()
| 2802 | }, 220); |
| 2803 | |
| 2804 | async function loadFileIcons() { |
| 2805 | // 0) Best-effort auto-scan (localhost/dev only): parse directory listings to discover icons |
| 2806 | // This allows new files dropped into icons/ to appear without manually editing manifests. |
| 2807 | // It only works when served by a server that exposes directory listings (e.g. `py -m http.server`). |
| 2808 | if (ICON_AUTO_SCAN_ENABLED && (location.protocol === "http:" || location.protocol === "https:")) { |
| 2809 | const auto = await tryLoadIconsFromDirectoryListing(); |
| 2810 | if (auto && auto.length) { |
| 2811 | FILE_ICONS = auto; |
| 2812 | return; |
| 2813 | } |
| 2814 | } |
| 2815 | |
| 2816 | // Prefer JSON on GitHub Pages to avoid relying on cached injected scripts. |
| 2817 | const isGitHubPages = /github\.io$/i.test(location.hostname || ""); |
| 2818 | if (isGitHubPages) { |
| 2819 | try { |
| 2820 | const res = await fetch(`icons/manifest.json?v=${Date.now()}`, { cache: "no-store" }); |
| 2821 | if (!res.ok) throw new Error("manifest not found"); |
| 2822 | const manifest = await res.json(); |
| 2823 | const files = Array.isArray(manifest.files) ? manifest.files : []; |
| 2824 | FILE_ICONS = files.map((file) => { |
| 2825 | const name = normalizeIconNameFromFile(file); |
| 2826 | const size = inferSizeFromPath(file); |
| 2827 | return { |
| 2828 | file, |
| 2829 | name, |
| 2830 | tags: [...inferTagsFromName(name), ...(size?.tag ? [size.tag] : [])], |
| 2831 | url: buildIconUrl(file) |
| 2832 | }; |
| 2833 | }); |
| 2834 | return; |
| 2835 | } catch { |
| 2836 | // fall through to injected manifest as a last resort |
| 2837 | } |
| 2838 | } |
| 2839 | |
| 2840 | // 1) Prefer JS-injected manifest (works on file://) |
| 2841 | try { |
| 2842 | const injected = window.DISPLAYKIT_ICON_MANIFEST; |
| 2843 | if (injected && Array.isArray(injected.files) && injected.files.length) { |
| 2844 | const files = injected.files; |
| 2845 | FILE_ICONS = files.map((file) => { |
| 2846 | const name = normalizeIconNameFromFile(file); |
| 2847 | const size = inferSizeFromPath(file); |
| 2848 | return { |
| 2849 | file, |
| 2850 | name, |
| 2851 | tags: [...inferTagsFromName(name), ...(size?.tag ? [size.tag] : [])], |
| 2852 | url: buildIconUrl(file) |
| 2853 | }; |
| 2854 | }); |
| 2855 | return; |
| 2856 | } |
| 2857 | } catch { |
| 2858 | // ignore |
| 2859 | } |
| 2860 | |
| 2861 | // 2) Fallback to JSON manifest (works on http/https) |
no test coverage detected