Skip to content

feat(hub-ui): render explicit SVG mask icons - #382

Open
dvcolomban wants to merge 6 commits into
devframes:mainfrom
dvcolomban:dvcol/feat-inline-icons
Open

feat(hub-ui): render explicit SVG mask icons#382
dvcolomban wants to merge 6 commits into
devframes:mainfrom
dvcolomban:dvcol/feat-inline-icons

Conversation

@dvcolomban

@dvcolomban dvcolomban commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Background (Why)

Locally bundled monochrome SVG icons need to follow the dock's foreground color, including selected states. SVGs rendered through an image element cannot inherit that color.

Changes (What)

Accept mask:<image-url> in the existing string icon API. Render it as an alpha mask using currentColor, resolve relative dock URLs against the supplying hub, and preserve mask strings through terminal session metadata. Ordinary images, Iconify names and dock theme pairs keep their existing behavior.

Verification (Testing)

Existing Vitest tests cover mask URL resolution, theme pairs and terminal metadata serialization. After cleanup, 108 scoped tests, affected lint and type checks pass. Builds and all 288 affected tests passed during the earlier implementation validation.

Browser validation before removing the temporary harness covered data URLs, ordinary images, selected/dimmed states, launchers and the command palette with Iconify blocked, across embedded dock and standalone SPA modes and both color schemes. The screenshots remain separate from the source changes.

Local masks in dark mode with Iconify blocked

All light/dark, embedded and command-palette screenshots.

Copilot AI lite review requested due to automatic review settings September 10, 2026 17:11
@coldtea-pr-lens

coldtea-pr-lens Bot commented Sep 10, 2026

Copy link
Copy Markdown

◈ PR Lens

🟢 +0 new · 🟠 ~4 changed · 🔴 -0 removed · 2 flows · 7 files · commit 593ca84


Architecture

Architecture diagram for devframes/devframe at 593ca84

4 components touched across 4 lanes.

Open the interactive canvas


Inside the changed components — 2 views

Component view — Hub dock icon resolution

Client runtime URL resolution and hub UI Vue mask rendering.

Architecture view of Component view — Hub dock icon resolution in devframes/devframe

Component view — Terminals mask icon rendering

Node session icon normalization and Svelte client CSS alpha masking.

Architecture view of Component view — Terminals mask icon rendering in devframes/devframe

Data flow

Data flow diagram for devframes/devframe at 593ca84

Resolving and rendering dock mask icons · Aggregating and rendering terminal mask icons

Open the interactive canvas


The other flows — 1 sequence

Aggregating and rendering terminal mask icons

Sequence diagram of Aggregating and rendering terminal mask icons in devframes/devframe

View

  • Architecture lens
  • Data flow lens
  • Expand every detail

Tip

Untick Architecture lens or Data flow lens under View to hide a diagram, or tick Expand every detail to open every section. The comment redraws in a few seconds.

🪧 More tips
  • Run npx skills add coldteadotai/pr-lens, then tell your coding agent: "Diagram the change you just made with PR Lens and attach it to the pull request."
  • Run npx @coldtea/pr-lens-cli analyze --base origin/main on a branch, then npx @coldtea/pr-lens-cli render .pr-lens/graph.json. Same lenses, your own model key, before the pull request exists.
  • Click the link under each diagram to open it on a canvas you can zoom, pan and step through.
  • The diagrams are links. Click one to open it on the canvas, then press W or click play to walk through the change.
  • Open a diagram on the canvas, then press W or click play to walk through the change one step at a time.
  • The CLI's render reads .github/pr-lens.yml and applies your renames, exclusions and lane pins at draw time.
  • Set github.comment.collapsed: true in .github/pr-lens.yml to fold the comment behind one View architecture and data flow row. Drawing still runs on every push.
  • Add .github/workflows/pr-lens.yml with coldteadotai/pr-lens/packages/action@v0 and your model provider's key as its api-key to run PR Lens from your own CI. Any /chat/completions endpoint works.
  • Push a commit and the comment redraws for the new head. A slow older run never overwrites a newer one.
  • Switch GitHub to dark mode and the diagrams follow. The moving dots are this pull request's data in motion.

Thanks for using PR Lens! It's built by Coldtea, free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

@dvcolomban is attempting to deploy a commit to the NuxtLabs Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are blocking packaging/security issues (subpath export types for ./icons, and inline SVG allowing <image>-based external loads) plus a brittle deep cross-workspace component import in the inspect UI.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR extends icon support across the devframe + hub ecosystem so dock entries, commands, and terminal sessions can carry inline icon sources (raw SVG strings, imported Iconify icon data, or theme pairs), reducing reliance on remote Iconify fetches and enabling currentColor inheritance for inline SVG rendering.

Changes:

  • Introduces DevframeIcon / DevframeIconSource and propagates them through devframe definitions, hub types, commands, and terminal session metadata/RPC schemas.
  • Adds a browser helper entrypoint @devframes/hub-ui/icons plus hub-ui icon rendering updates (inline SVG sanitization, finite bundled Phosphor subset generation).
  • Updates tests, examples, docs, build tasks, and public API snapshots to reflect the new icon shapes and offline behavior.
File summaries
File Description
turbo.json Adds a hub-ui icon generation task and wires it into typecheck/build outputs.
tsconfig.base.json Adds a TS path alias for @devframes/hub-ui/icons.
pnpm-workspace.yaml Adds catalog entries for Iconify utils/types and jsdom.
pnpm-lock.yaml Locks new dependencies (@iconify/utils, @iconify/types, jsdom) and transitive graph changes.
package.json Ensures hub-ui icon generation runs during postinstall.
alias.ts Adds runtime/source alias for @devframes/hub-ui/icons.
packages/devframe/src/types/index.ts Re-exports the new icons type module.
packages/devframe/src/types/icons.ts Defines DevframeIconSource/DevframeIcon using @iconify/types.
packages/devframe/src/types/devframe.ts Switches DevframeDefinition.icon and dock defaults to DevframeIcon.
packages/devframe/package.json Adds @iconify/types dependency for type resolution.
packages/hub/src/types/docks.ts Updates dock icon typing to DevframeIcon and re-exports DevframeIconSource.
packages/hub/src/types/commands.ts Updates command icon docs to match expanded icon source forms.
packages/hub/src/client/dock-resources.ts Preserves non-string icon sources while still resolving URL strings.
packages/hub/src/client/dock-resources.test.ts Adds coverage for preserving transported icon objects and mixed theme pairs.
packages/hub-ui/package.json Exposes ./icons entry and adds a build step for generated builtin icon data.
packages/hub-ui/tsdown.config.ts Builds both node entry and browser icon helpers; bundles iconify/dompurify deps.
packages/hub-ui/vitest.config.ts Adds a dedicated vitest project config for hub-ui.
packages/hub-ui/scripts/build-icons.ts Generates a finite builtin Phosphor icon subset for offline dock defaults.
packages/hub-ui/src/client/utils/icons.ts Adds icon resolution helpers (variant selection, image detection, inline SVG sanitization).
packages/hub-ui/src/client/utils/icons.test.ts Adds jsdom tests for conversion/sanitization/offline builtin behavior and source switching.
packages/hub-ui/src/client/components/icons/IconifyIcon.vue Switches to inline SVG rendering via the new icon helpers.
packages/hub-ui/src/client/components/dock/DockIcon.vue Accepts DevframeDockEntryIcon and forwards sources to IconifyIcon for rendering.
packages/hub-ui/src/client/components/dock/DockIcon.stories.ts Adds stories for raw SVG and imported Iconify data rendering.
packages/hub-ui/src/client/state/dock-settings.ts Removes legacy command-icon collapsing helper (icons now carried through).
packages/hub-ui/src/client/state/context.ts Projects dock entry icons into commands without collapsing theme/object forms.
plugins/terminals/src/node/types.ts Updates terminal preset/session icon types to DevframeIcon.
plugins/terminals/src/node/rpc/schemas.ts Expands terminal RPC schemas to accept object/iconify/raw SVG icon sources.
plugins/terminals/src/node/manager.ts Preserves hub-provided icon sources rather than normalizing to UnoCSS classes.
plugins/terminals/test/_utils.ts Updates fake hub terminal icon typing to DevframeIcon.
plugins/terminals/test/terminals.test.ts Adds transport test for theme/raw/imported icons; updates expectations for preserved icon names.
plugins/terminals/app/client/SessionIcon.svelte Adds a renderer for inline SVG/imported data/image URLs (and existing class icons) in the terminals UI.
plugins/terminals/app/client/App.svelte Uses SessionIcon to render session/preset icons across the UI.
plugins/terminals/package.json Adds hub-ui workspace dependency for consuming the icon helpers.
plugins/inspect/src/node/types.ts Updates inspect command icon typing/documentation to DevframeIcon.
plugins/inspect/src/node/rpc/functions/_hub-commands.ts Updates hub command-like icon typing to DevframeIcon.
plugins/inspect/src/node/index.ts Updates inspect devframe options icon typing to DevframeIcon.
plugins/inspect/app/components/CommandRow.vue Switches command row icon rendering to a dock icon component.
plugins/messages/src/node/index.ts Updates messages devframe options icon typing to DevframeIcon.
plugins/og/src/node/index.ts Updates og devframe options icon typing to DevframeIcon.
plugins/data-inspector/src/node/index.ts Updates data-inspector devframe options icon typing to DevframeIcon.
plugins/assets/src/node/index.ts Updates assets devframe options icon typing to DevframeIcon.
plugins/a11y/src/node/index.ts Updates a11y devframe options icon typing to DevframeIcon.
plugins/code-server/test/_utils.ts Updates fake hub session icon typing to DevframeIcon.
plugins/code-server/src/node/supervisor.ts Updates hub terminal bridge typing to accept DevframeIcon icons.
examples/custom-hub-vite/src/client/main.ts Updates example dock icon painting to work with non-string icon sources.
examples/custom-hub-next/src/client/app/page.tsx Changes dock icon cache keying to account for object icon sources.
design/dock-icon.ts Switches design helper to use the new icon helpers instead of direct Iconify fetch.
docs/content/1.guide/16.hub.md Documents inline icon support and the @devframes/hub-ui/icons helpers for custom renderers.
tests/snapshots/tsnapi/devframe/types.snapshot.d.ts Snapshot updates to include exported DevframeIcon/DevframeIconSource.
tests/snapshots/tsnapi/devframe/index.snapshot.d.ts Snapshot updates for DevframeDefinition.icon and dock defaults using DevframeIcon.
tests/snapshots/tsnapi/@devframes/plugin-terminals/types.snapshot.d.ts Snapshot updates for terminal icon types using DevframeIcon.
tests/snapshots/tsnapi/@devframes/plugin-terminals/rpc.snapshot.d.ts Snapshot updates for expanded terminal RPC icon schema types.
tests/snapshots/tsnapi/@devframes/plugin-terminals/index.snapshot.d.ts Snapshot updates for terminal icon types using DevframeIcon.
tests/snapshots/tsnapi/@devframes/plugin-og/index.snapshot.d.ts Snapshot updates for og devframe option icon typing.
tests/snapshots/tsnapi/@devframes/plugin-messages/index.snapshot.d.ts Snapshot updates for messages devframe option icon typing.
tests/snapshots/tsnapi/@devframes/plugin-data-inspector/index.snapshot.d.ts Snapshot updates for data-inspector devframe option icon typing.
tests/snapshots/tsnapi/@devframes/plugin-assets/index.snapshot.d.ts Snapshot updates for assets devframe option icon typing.
tests/snapshots/tsnapi/@devframes/plugin-a11y/index.snapshot.d.ts Snapshot updates for a11y devframe option icon typing.
tests/snapshots/tsnapi/@devframes/hub/types.snapshot.d.ts Snapshot updates to export DevframeIconSource from hub types.
tests/snapshots/tsnapi/@devframes/hub/index.snapshot.d.ts Snapshot updates for dock entry icon typing and exports.
tests/snapshots/tsnapi/@devframes/hub-ui/icons.snapshot.d.ts Adds public API snapshot for @devframes/hub-ui/icons types.
tests/snapshots/tsnapi/@devframes/hub-ui/icons.snapshot.js Adds public API snapshot for @devframes/hub-ui/icons runtime exports.
Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file
  • Files reviewed: 48/62 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/hub-ui/src/client/utils/icons.ts Outdated
Comment thread packages/hub-ui/package.json
Comment thread plugins/inspect/app/components/CommandRow.vue
Copilot AI review requested due to automatic review settings September 10, 2026 17:24
@dvcolomban
dvcolomban marked this pull request as draft September 10, 2026 17:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The inspect devframe UI imports a hub-ui SFC via a repo-relative path (bypassing declared dependencies), and this coupling should be resolved before approval.

Review details

Files not reviewed (1)

  • pnpm-lock.yaml: Generated file

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

packages/hub-ui/src/client/utils/icons.ts:23

  • This browser helper module instantiates DOMPurify at import time and later relies on DOM globals. If it’s ever imported in a non-DOM context (SSR bundlers or accidental node-side import), it can crash during module evaluation. Consider guarding DOMPurify initialization and failing fast inside getIconSvg() when DOM APIs are unavailable so the module remains safe to import.
    storybook/src/client/icons.ts:4
  • This Storybook helper reaches into packages/hub-ui/src/... directly. Since @devframes/hub-ui/icons is now an exported entry, importing from it keeps Storybook aligned with the public API and avoids coupling to the monorepo file layout.

plugins/inspect/app/components/CommandRow.vue:6

  • This component imports a Vue SFC from another workspace via a repo-relative path, which bypasses the package dependency graph (the inspect devframe doesn’t declare any dependency on @devframes/hub-ui) and makes the build fragile to repo layout changes. Prefer rendering icons locally using the public @devframes/hub-ui/icons helpers (or add an explicit dependency and import from an exported entry point).
  • Files reviewed: 50/64 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@dvcolomban
dvcolomban force-pushed the dvcol/feat-inline-icons branch from affd084 to 02b909e Compare September 10, 2026 18:08
@dvcolomban dvcolomban changed the title feat(icons): support imported inline SVG data feat(hub-ui): render explicit SVG mask icons Sep 10, 2026
@dvcolomban
dvcolomban force-pushed the dvcol/feat-inline-icons branch from 02b909e to 13e57cf Compare September 10, 2026 19:34
@dvcolomban
dvcolomban marked this pull request as ready for review September 10, 2026 19:39
Copilot AI review requested due to automatic review settings September 10, 2026 19:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Mask URL handling should trim/guard the value consistently to avoid whitespace/empty-value edge cases that can break resolution or produce unexpected URLs.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment thread packages/hub-ui/src/client/components/icons/IconifyIcon.vue Outdated
Comment thread packages/hub/src/client/dock-resources.ts
Comment thread plugins/terminals/app/client/App.svelte Outdated
Copilot AI review requested due to automatic review settings September 10, 2026 19:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Empty mask: values currently render as url("") in CSS masks, which can cause unintended fetches and should be treated as mask: none (and the docs inline code formatting needs correction).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

docs/content/8.references/4.node-api.md:33

  • The inline code sample uses doubled backticks (`...`), which will render literal backtick characters around the string and is confusing for readers. Use a normal inline code span for the string expression.
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread packages/hub-ui/src/client/components/icons/IconifyIcon.vue
Comment thread plugins/terminals/app/client/App.svelte
Copilot AI review requested due to automatic review settings September 10, 2026 19:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new mask icon renderers introduce inconsistent accessibility attributes (e.g., missing alt="" / aria-hidden) that should be made consistent before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

packages/hub-ui/src/client/components/icons/IconifyIcon.vue:45

  • IconifyIcon now renders mask: icons with aria-hidden, but the other branches (Iconify SVG injection and <img>) still lack aria-hidden and the <img> lacks alt. Since these dock icons are decorative (buttons already have aria-label / adjacent text), add aria-hidden="true" consistently and set alt="" on the image branch to avoid screen readers announcing the URL.
  }
  catch {
    // A failed icon fetch (offline / flaky CDN) should degrade to a blank icon,
    // not throw out of the async effect and crash the surrounding panel.
    iconifyLoaded.value = undefined
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread plugins/terminals/app/client/App.svelte
Copilot AI review requested due to automatic review settings September 10, 2026 20:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation is cohesive across resolver, renderers, and terminal metadata, and the added tests cover the key new resolution and transport behaviors.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 10, 2026 20:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new hub-ui icon rendering branch introduces accessibility regressions/inconsistencies (decorative SVG/img not consistently hidden from assistive tech) and a small URL-resolution consistency issue for protocol-relative mask URLs.

Review details

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

packages/hub-ui/src/client/components/icons/IconifyIcon.vue:60

  • In the Iconify SVG branch, the icon is decorative (dock buttons already provide an aria-label), but this wrapper div isn’t marked aria-hidden. That can cause redundant/verbose screen-reader output when the injected SVG includes accessible content. Mark this branch aria-hidden="true" like the mask branch.

This issue also appears on line 61 of the same file.
packages/hub/src/client/dock-resources.ts:34

  • resolveIconUrl preserves protocol-relative URLs (//...) for non-mask icons, but mask://... currently gets normalized via new URL(...) into an explicit scheme. Preserving //... for masks as well keeps behavior consistent and avoids unnecessarily hardcoding a scheme in serialized metadata.

packages/hub-ui/src/client/components/icons/IconifyIcon.vue:65

  • The <img> fallback is also decorative in the dock (the parent button provides an aria-label), but it currently has no alt and isn’t aria-hidden. Add alt="" (and optionally aria-hidden="true") to prevent screen readers from announcing the URL/filename.
  <img
    v-else :src="icon"
    class="w-full h-full m-auto"
    draggable="false"
  >
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants