1- /**
2- * Unified Icon Resolver
3- *
4- * Provides a single interface for resolving file/folder icons through
5- * a theme adapter, regardless of whether the active theme comes from
6- * FSS metadata or a VS Code icon theme JSON.
7- */
8-
9- import { iconThemeVersionAtom } from "@/atoms" ;
101import { useBridge } from "@/features/bridge/useBridge" ;
112import { atom , useAtomValue , useSetAtom } from "jotai" ;
12- import { useCallback , useMemo } from "react" ;
3+ import { useCallback } from "react" ;
134import {
145 FssIconThemeAdapter ,
156 NoneIconThemeAdapter ,
@@ -23,58 +14,80 @@ import {
2314import { DEFAULT_ICONS } from "./defaultIcons" ;
2415import { useIconAssetStore } from "./iconCache" ;
2516
26- const activeIconThemeAdapterAtom = atom < IconThemeAdapter > ( new NoneIconThemeAdapter ( ) ) ;
17+ type ActiveIconThemeState = {
18+ adapter : IconThemeAdapter ;
19+ kind : "dark" | "light" ;
20+ type : IconThemeType ;
21+ } ;
22+
23+ const activeIconThemeStateAtom = atom < ActiveIconThemeState > ( {
24+ adapter : new NoneIconThemeAdapter ( ) ,
25+ kind : "dark" ,
26+ type : "none" ,
27+ } ) ;
2728
2829export function useSetIconTheme ( ) {
2930 const bridge = useBridge ( ) ;
30- const fssTheme = useMemo ( ( ) => new FssIconThemeAdapter ( ) , [ ] ) ;
31- const vscodeTheme = useMemo ( ( ) => new VSCodeIconThemeAdapter ( bridge ) , [ bridge ] ) ;
32- const noneTheme = useMemo ( ( ) => new NoneIconThemeAdapter ( ) , [ ] ) ;
33- const setActiveTheme = useSetAtom ( activeIconThemeAdapterAtom ) ;
34- const bumpThemeVersion = useSetAtom ( iconThemeVersionAtom ) ;
31+ const setActiveThemeState = useSetAtom ( activeIconThemeStateAtom ) ;
3532
3633 return {
3734 setIconTheme : async ( type : IconThemeType , path ?: string ) : Promise < void > => {
38- noneTheme . clear ( ) ;
39- fssTheme . clear ( ) ;
40- vscodeTheme . clear ( ) ;
41-
4235 if ( type === "vscode" && path ) {
36+ const adapter = new VSCodeIconThemeAdapter ( bridge ) ;
4337 try {
44- await vscodeTheme . load ( path ) ;
45- setActiveTheme ( vscodeTheme ) ;
46- bumpThemeVersion ( ( v ) => v + 1 ) ;
38+ await adapter . load ( path ) ;
39+ setActiveThemeState ( ( current ) => {
40+ current . adapter . clear ( ) ;
41+ adapter . setThemeKind ?.( current . kind ) ;
42+ return {
43+ adapter,
44+ kind : current . kind ,
45+ type : "vscode" ,
46+ } ;
47+ } ) ;
4748 } catch {
48- setActiveTheme ( noneTheme ) ;
49- bumpThemeVersion ( ( v ) => v + 1 ) ;
49+ setActiveThemeState ( ( current ) => {
50+ current . adapter . clear ( ) ;
51+ return {
52+ adapter : new NoneIconThemeAdapter ( ) ,
53+ kind : current . kind ,
54+ type : "none" ,
55+ } ;
56+ } ) ;
5057 }
5158 return ;
5259 }
5360
54- const nextTheme = type === "fss" ? fssTheme : noneTheme ;
55- setActiveTheme ( nextTheme ) ;
56- bumpThemeVersion ( ( v ) => v + 1 ) ;
61+ setActiveThemeState ( ( current ) => {
62+ current . adapter . clear ( ) ;
63+ const adapter : IconThemeAdapter = type === "fss" ? new FssIconThemeAdapter ( ) : new NoneIconThemeAdapter ( ) ;
64+ adapter . setThemeKind ?.( current . kind ) ;
65+ return {
66+ adapter,
67+ kind : current . kind ,
68+ type,
69+ } ;
70+ } ) ;
5771 } ,
5872 } ;
5973}
6074
6175export function useSetIconThemeKind ( ) {
62- const activeTheme = useAtomValue ( activeIconThemeAdapterAtom ) ;
63- const bumpThemeVersion = useSetAtom ( iconThemeVersionAtom ) ;
76+ const setActiveThemeState = useSetAtom ( activeIconThemeStateAtom ) ;
6477 return {
6578 setIconThemeKind : ( kind : "dark" | "light" ) : void => {
66- activeTheme . setThemeKind ?.( kind ) ;
67- bumpThemeVersion ( ( v ) => v + 1 ) ;
79+ setActiveThemeState ( ( current ) => {
80+ current . adapter . setThemeKind ?.( kind ) ;
81+ return {
82+ ...current ,
83+ } ;
84+ } ) ;
6885 } ,
6986 } ;
7087}
7188
7289export function useIconThemeType ( ) : IconThemeType {
73- return useAtomValue ( activeIconThemeAdapterAtom ) . kind ;
74- }
75-
76- export function useIconThemeVersion ( ) : number {
77- return useAtomValue ( iconThemeVersionAtom ) ;
90+ return useAtomValue ( activeIconThemeStateAtom ) . type ;
7891}
7992
8093export interface ResolvedIcon {
@@ -90,7 +103,7 @@ export interface ResolvedIcon {
90103 * Returns either an image icon or a font icon plus fallback metadata.
91104 */
92105export function useResolveIcon ( ) {
93- const activeTheme = useAtomValue ( activeIconThemeAdapterAtom ) ;
106+ const { adapter : activeTheme } = useAtomValue ( activeIconThemeStateAtom ) ;
94107 const iconAssets = useIconAssetStore ( ) ;
95108
96109 return useCallback (
@@ -143,7 +156,7 @@ export function useResolveIcon() {
143156 * Load any image assets and ensure any font families are ready.
144157 */
145158export function useLoadIconsForPaths ( ) {
146- const activeTheme = useAtomValue ( activeIconThemeAdapterAtom ) ;
159+ const { adapter : activeTheme } = useAtomValue ( activeIconThemeStateAtom ) ;
147160 const iconAssets = useIconAssetStore ( ) ;
148161 return useCallback (
149162 async ( icons : ResolvedThemeIcon [ ] ) : Promise < void > => {
0 commit comments