| 1 | import { FileIcon, FolderIcon } from "lucide-react"; |
| 2 | import { memo, useInsertionEffect, useMemo } from "react"; |
| 3 | |
| 4 | import { ensurePierreIconSprite, resolvePierreIconForEntry } from "../../pierre-icons"; |
| 5 | import { cn } from "~/lib/utils"; |
| 6 | |
| 7 | const 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 | |
| 62 | export 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 | }); |