1import { FileIcon, FolderIcon } from "lucide-react";
2import { memo, useInsertionEffect, useMemo } from "react";
3
4import { ensurePierreIconSprite, resolvePierreIconForEntry } from "../../pierre-icons";
5import { cn } from "~/lib/utils";
6
7const ICON_COLORS: Record<string, readonly [light: string, dark: string]> = {
8 astro: ["#a631be", "#d568ea"],
9 babel: ["#d5a910", "#ffd452"],
10 bash: ["#199f43", "#5ecc71"],
11 biome: ["#1a85d4", "#69b1ff"],
12 bootstrap: ["#693acf", "#9d6afb"],
13 browserslist: ["#d5a910", "#ffd452"],
14 bun: ["#594c5b", "#79697b"],
15 c: ["#1a85d4", "#69b1ff"],
16 claude: ["#d47628", "#ffa359"],
17 cpp: ["#1a85d4", "#69b1ff"],
18 css: ["#693acf", "#9d6afb"],
19 database: ["#a631be", "#d568ea"],
20 default: ["#84848a", "#adadb1"],
21 docker: ["#1a85d4", "#69b1ff"],
22 eslint: ["#693acf", "#9d6afb"],
23 git: ["#ff8c5b", "#d5512f"],
24 go: ["#1ca1c7", "#68cdf2"],
25 graphql: ["#d32a61", "#ff678d"],
26 html: ["#d47628", "#ffa359"],
27 image: ["#d32a61", "#ff678d"],
28 javascript: ["#d5a910", "#ffd452"],
29 json: ["#d47628", "#ffa359"],
30 markdown: ["#199f43", "#5ecc71"],
31 mcp: ["#17a5af", "#64d1db"],
32 nextjs: ["#84848a", "#adadb1"],
33 npm: ["#d52c36", "#ff6762"],
34 oxc: ["#1ca1c7", "#68cdf2"],
35 postcss: ["#d52c36", "#ff6762"],
36 prettier: ["#17a5af", "#64d1db"],
37 python: ["#1a85d4", "#69b1ff"],
38 react: ["#1ca1c7", "#68cdf2"],
39 ruby: ["#d52c36", "#ff6762"],
40 rust: ["#d47628", "#ffa359"],
41 sass: ["#d32a61", "#ff678d"],
42 stylelint: ["#84848a", "#adadb1"],
43 svelte: ["#d52c36", "#ff6762"],
44 svg: ["#d47628", "#ffa359"],
45 svgo: ["#199f43", "#5ecc71"],
46 swift: ["#d47628", "#ffa359"],
47 table: ["#17a5af", "#64d1db"],
48 tailwind: ["#1ca1c7", "#68cdf2"],
49 terraform: ["#693acf", "#9d6afb"],
50 text: ["#84848a", "#adadb1"],
51 typescript: ["#1a85d4", "#69b1ff"],
52 vite: ["#a631be", "#d568ea"],
53 vscode: ["#1a85d4", "#69b1ff"],
54 vue: ["#199f43", "#5ecc71"],
55 wasm: ["#693acf", "#9d6afb"],
56 webpack: ["#1a85d4", "#69b1ff"],
57 yml: ["#d52c36", "#ff6762"],
58 zig: ["#d47628", "#ffa359"],
59 zip: ["#d47628", "#ffa359"],
60};
61
62export const PierreEntryIcon = memo(function PierreEntryIcon(props: {
63 pathValue: string;
64 kind: "file" | "directory";
65 mimeType?: string | undefined;
66 theme: "light" | "dark";
67 className?: string;
68}) {
69 useInsertionEffect(ensurePierreIconSprite, []);
70 const icon = useMemo(
71 () => resolvePierreIconForEntry(props.pathValue, props.kind, props.mimeType),
72 [props.kind, props.pathValue, props.mimeType],
73 );
74
75 if (!icon) {
76 return props.kind === "directory" ? (
77 <FolderIcon className={cn("size-4 text-icon-muted", props.className)} />
78 ) : (
79 <FileIcon className={cn("size-4 text-icon-muted", props.className)} />
80 );
81 }
82
83 const colors = ICON_COLORS[icon.token ?? "default"] ?? ICON_COLORS.default;
84 return (
85 <svg
86 aria-hidden="true"
87 data-pierre-icon={icon.name}
88 data-icon-token={icon.token}
89 className={cn("size-4 shrink-0", props.className)}
90 style={{ color: colors?.[props.theme === "light" ? 0 : 1] }}
91 viewBox="0 0 16 16"
92 >
93 <use href={`#${icon.name}`} />
94 </svg>
95 );
96});