@@ -9,7 +9,7 @@ import type { DevframeJsonRenderSpec } from '@devframes/json-render'
99import type { DevframeJsonRenderDockEntry } from '@devframes/json-render/hub'
1010import { connectDevframe , createDevframeClientHost , FRAME_NAV_CHANNEL } from '@devframes/hub/client'
1111import { createJsonRenderDockRenderer } from '@devframes/json-render-ui'
12- import { iconClass } from './icons'
12+ import { dockIconSvg } from './icons'
1313import 'virtual:uno.css'
1414import '@antfu/design/styles.css'
1515
@@ -40,13 +40,48 @@ function renderList<T>(host: HTMLElement, items: readonly T[], render: (item: T)
4040 host . innerHTML = items . map ( render ) . join ( '' )
4141}
4242
43- /** Render a dock icon, falling back to the title's initial when unknown. */
43+ // Session-lifetime cache of resolved dock-icon SVGs, keyed by the icon id
44+ // (e.g. `ph:git-branch-duotone`) — `dockIconSvg` itself caches per fetch, this
45+ // just lets a re-render (e.g. a badge update) paint an already-resolved icon
46+ // synchronously instead of flashing the fallback initial again.
47+ const dockIconCache = new Map < string , string | null > ( )
48+
49+ function dockIconKey ( icon : DevframeDockEntry [ 'icon' ] ) : string | undefined {
50+ return typeof icon === 'string' ? icon : icon ?. light
51+ }
52+
53+ /** Render a dock icon's placeholder markup: a resolved SVG if cached, else the title's initial. */
4454function dockIcon ( entry : DevframeDockEntry ) : string {
45- const cls = iconClass ( entry . icon )
46- if ( cls )
47- return `<span class="${ cls } shrink-0 text-lg"></span>`
55+ const key = entry . icon ? dockIconKey ( entry . icon ) : undefined
56+ const cached = key ? dockIconCache . get ( key ) : undefined
57+ if ( cached )
58+ return `<span class="h-5 w-5 shrink-0 text-lg" data-dock-icon="${ entry . id } ">${ cached } </span>`
4859 const initial = ( entry . title ?. [ 0 ] ?? '?' ) . toUpperCase ( )
49- return `<span class="grid h-5 w-5 shrink-0 place-items-center rounded bg-active text-[0.7rem] font-bold">${ initial } </span>`
60+ return `<span class="grid h-5 w-5 shrink-0 place-items-center rounded bg-active text-[0.7rem] font-bold" data-dock-icon="${ entry . id } ">${ initial } </span>`
61+ }
62+
63+ /**
64+ * Resolve (and cache) each unresolved dock's icon SVG, then patch just that
65+ * dock's `[data-dock-icon]` element in place — no full re-render, since the
66+ * fetch is async and the dock list may already be showing by the time it
67+ * settles. A dock with no icon or an unparsable/failed fetch keeps its
68+ * fallback initial.
69+ */
70+ async function hydrateDockIcons ( list : readonly DevframeDockEntry [ ] ) : Promise < void > {
71+ await Promise . all ( list . map ( async ( entry ) => {
72+ const key = entry . icon ? dockIconKey ( entry . icon ) : undefined
73+ if ( ! key || dockIconCache . has ( key ) )
74+ return
75+ const svg = await dockIconSvg ( entry . icon ) ?? null
76+ dockIconCache . set ( key , svg )
77+ if ( ! svg )
78+ return
79+ const el = docksEl . querySelector < HTMLElement > ( `[data-dock-icon="${ entry . id } "]` )
80+ if ( el ) {
81+ el . className = 'h-5 w-5 shrink-0 text-lg'
82+ el . innerHTML = svg
83+ }
84+ } ) )
5085}
5186
5287function isIframeDock ( d : DevframeDockEntry ) : d is DevframeDockEntry & { type : 'iframe' , url : string } {
@@ -315,6 +350,7 @@ async function main() {
315350
316351 renderList ( docksEl , list , d =>
317352 `<li><button type="button" data-dock-id="${ d . id } " class="relative inline-flex items-center gap-1.5 max-w-52 px-2 py-1 rounded-md border border-transparent text-sm op-fade select-none cursor-pointer transition hover:op100 hover:bg-active w-full! max-w-none! gap-2.5!${ d . id === docksCtx . selectedId ? ' op100! bg-active border-base! color-base' : '' } " title="${ d . title } ">${ dockIcon ( d ) } <span class="truncate">${ d . title } </span>${ d . badge ? `<span class="ml-auto shrink-0 rounded bg-active px1 py0.5 text-[0.6rem] font-mono color-base">${ d . badge } </span>` : '' } </button></li>` )
353+ void hydrateDockIcons ( list )
318354
319355 void showSelection ( list )
320356 }
0 commit comments