Skip to content

refactor(dashboard): pass lucide icons as values instead of name strings - #649

Merged
SantiagoDePolonia merged 2 commits into
mainfrom
refactor/icon-props
Aug 4, 2026
Merged

refactor(dashboard): pass lucide icons as values instead of name strings#649
SantiagoDePolonia merged 2 commits into
mainfrom
refactor/icon-props

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Description

Follow-up to #648, which introduced a curated iconRegistry mapping kebab-case names to lucide icons so the bundler could tree-shake them.

That registry has a failure mode: an icon name with no registry entry resolves to an empty node array and renders a blank SVG, with nothing in the build, svelte-check, or the test suite noticing. Review of #648 caught exactly that — EmptyState's default icon = "inbox" was never registered, because a prop default isn't a call site any name-scan looks at.

This removes the indirection instead of patching it. Callers import the icon and pass it directly:

import { Pencil } from "lucide";
<Icon icon={Pencil} />

icons.js is deleted — there is no list to keep in sync, and tree-shaking now follows the imports structurally instead of depending on an enumerated map staying complete. Config-driven icons (sidebar nav, theme toggle, confirm dialogs, budget periods, workflow chart nodes) hold the icon itself rather than a name string.

A guard test, because the toolchain is quieter than expected

I assumed a bad icon would now be a build error. It isn't — I checked: with a deliberately typo'd import { Inboxx } from "lucide", vite build succeeds with zero warnings (rolldown does not error on missing named exports) and svelte-check reports 0 problems (jsconfig.json sets checkJs: false). A typo would still have shipped as a blank SVG.

So tests/icons.test.js scans every import { … } from "lucide" in the source tree and resolves each name against the real package, asserting it exists and is a non-empty node array. Unlike the registry it replaces, it needs no maintenance — it discovers imports rather than being told about them. Verified it fails on the typo above with lucide has no export "Inboxx" (imported by lib/components/atoms/EmptyState.svelte), and it runs in the existing pre-commit hook and CI.

User-visible impact

None. Same icons, same rendering. Bundle size is unchanged at ~799 KB (the same icons reach the output either way) — this buys correctness and removes a maintenance burden, not bytes.

Testing

  • vite build clean, svelte-check 0 errors/0 warnings (314 files), 436/436 tests (435 + the new guard).
  • Rendered the dashboard in headless Chrome and audited every SVG in the DOM: 23 icons, 0 with no drawable child, covering static imports, config-driven icons (sidebar/theme), and prop pass-through.
  • Separately verified the $state path: confirm.svelte.js holds its icon inside a $state object, so Svelte deeply proxies the lucide array before Icon sees it. Mounted that exact shape in a browser and confirmed it renders (3 drawable children, correct SVG namespace).
  • Confirmed the guard test fails on a typo'd icon name, and passes once restored.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Enhancements
    • Updated dashboard icons across navigation, dialogs, forms, tables, settings, and action buttons for more consistent rendering.
    • Preserved existing actions, layouts, and workflows while standardizing icon presentation.
    • Expanded consistent icon coverage across budgets, models, providers, rate limits, audit logs, usage, and workflows.
  • Tests
    • Added automated checks to verify dashboard icons use valid, drawable definitions.

Follow-up to #648. Icon took a kebab-case name and resolved it through a
curated registry, so an unregistered name rendered a blank SVG instead of
failing — exactly the EmptyState "inbox" bug found in review there.

Callers now import the icon and pass it (<Icon icon={Pencil} />), which
deletes the registry: there is no list to keep in sync, and tree-shaking
follows the imports structurally rather than from an enumerated map.
Config-driven icons (sidebar nav, theme toggle, confirm dialogs, budget
periods) hold the icon itself instead of a name.

The bundler does not warn on missing named exports and jsconfig runs with
checkJs:false, so a typo would still slip through silently. A guard test
resolves every lucide import in the source tree against the package,
failing with the offending file name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 4, 2026 14:56

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The dashboard migrates icon rendering from string-based registry names to imported Lucide icon data passed through Icon’s icon prop. Consumers, navigation, helpers, workflow nodes, and confirmation state use the new API. Tests validate icon exports and drawable data.

Changes

Dashboard icon migration

Layer / File(s) Summary
Shared icon contract
web/dashboard/src/lib/components/atoms/Icon.svelte, web/dashboard/src/lib/components/atoms/*, web/dashboard/src/lib/components/organisms/*, web/dashboard/src/lib/stores/*
Icon now renders direct Lucide data arrays. Shared components, navigation, theme controls, and confirmation dialogs pass imported icon components.
Dashboard page consumers
web/dashboard/src/pages/audit-logs/*, web/dashboard/src/pages/auth-keys/*, web/dashboard/src/pages/budgets/*, web/dashboard/src/pages/guardrails/*, web/dashboard/src/pages/mcp-servers/*, web/dashboard/src/pages/models/*
Dashboard page controls pass imported Lucide components through the icon prop. Budget and editor state values also use component references.
Operational pages and workflows
web/dashboard/src/pages/overview/*, web/dashboard/src/pages/providers-config/*, web/dashboard/src/pages/rate-limits/*, web/dashboard/src/pages/settings/*, web/dashboard/src/pages/usage/*, web/dashboard/src/pages/workflows/*
Status, form, action, confirmation, usage, and workflow icons use the component-based API.
Icon contract validation
web/dashboard/tests/icons.test.js, web/dashboard/tests/budgets.test.js
Tests validate Lucide imports, drawable icon arrays, non-string icon bindings, and budget-period icon mappings.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: copilot

Poem

A rabbit brings Lucide near,
Direct icon shapes now appear.
Names leave the dashboard code,
Shared props carry the render load.
Tests check each drawable ear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main refactor from Lucide icon name strings to direct icon values.
Description check ✅ Passed The description explains the motivation, implementation, user impact, testing, and known toolchain behavior in sufficient detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/icon-props

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

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/dashboard/src/lib/components/organisms/EditorDialog.svelte`:
- Line 46: Update all remaining callers of EditorDialog's submitIcon prop to
pass Lucide icon data instead of strings. In AuthKeyEditor.svelte,
VirtualModelEditor.svelte, WorkflowEditor.svelte, and other editor components
(AuthKeyLabelsEditor, MCP, Tenant, Rate-limit, Policy editors), replace the
string values "plus", "save", "check", and "delete" with their corresponding
imported Lucide icons (Plus, Save, Check, Trash respectively). Import each
needed Lucide icon at the top of each file and update the submitIcon assignments
to use the imported icon data in the same format that Save is now used in
EditorDialog.svelte.

In `@web/dashboard/tests/icons.test.js`:
- Around line 31-36: Update importedIcons() to parse aliased named imports by
extracting the exported Lucide name before the “as” clause, so lucide[name] uses
Pencil rather than the local alias EditIcon. Preserve existing handling for
non-aliased imports and apply the same normalization in the related logic at
lines 48–49.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0db7c7e1-8556-4f56-90fa-96f3d8c8f394

📥 Commits

Reviewing files that changed from the base of the PR and between adbbe0e and 33b3577.

⛔ Files ignored due to path filters (3)
  • internal/admin/dashboard/static/dist/assets/index-BMBBtagk.js is excluded by !**/dist/**
  • internal/admin/dashboard/static/dist/assets/index-DzO__mgM.js is excluded by !**/dist/**
  • internal/admin/dashboard/static/dist/index.html is excluded by !**/dist/**
📒 Files selected for processing (57)
  • web/dashboard/src/lib/components/atoms/CopyButton.svelte
  • web/dashboard/src/lib/components/atoms/DialogCloseButton.svelte
  • web/dashboard/src/lib/components/atoms/EmptyState.svelte
  • web/dashboard/src/lib/components/atoms/Icon.svelte
  • web/dashboard/src/lib/components/atoms/TableActionButton.svelte
  • web/dashboard/src/lib/components/atoms/icons.js
  • web/dashboard/src/lib/components/molecules/FilterInput.svelte
  • web/dashboard/src/lib/components/organisms/AuthDialog.svelte
  • web/dashboard/src/lib/components/organisms/EditorDialog.svelte
  • web/dashboard/src/lib/components/organisms/Sidebar.svelte
  • web/dashboard/src/lib/components/organisms/ThemeToggle.svelte
  • web/dashboard/src/lib/components/organisms/TypedConfirmationDialog.svelte
  • web/dashboard/src/lib/components/organisms/navigation.js
  • web/dashboard/src/lib/stores/confirm.svelte.js
  • web/dashboard/src/pages/audit-logs/AuditEntrySummary.svelte
  • web/dashboard/src/pages/audit-logs/AuditFilters.svelte
  • web/dashboard/src/pages/audit-logs/AuditPaneTabs.svelte
  • web/dashboard/src/pages/auth-keys/AuthKeyList.svelte
  • web/dashboard/src/pages/auth-keys/AuthKeysPage.svelte
  • web/dashboard/src/pages/budgets/BudgetEditor.svelte
  • web/dashboard/src/pages/budgets/BudgetList.svelte
  • web/dashboard/src/pages/budgets/BudgetsPage.svelte
  • web/dashboard/src/pages/budgets/budgets.svelte.js
  • web/dashboard/src/pages/guardrails/GuardrailList.svelte
  • web/dashboard/src/pages/mcp-servers/McpServerEditor.svelte
  • web/dashboard/src/pages/mcp-servers/McpServerList.svelte
  • web/dashboard/src/pages/mcp-servers/McpServersPage.svelte
  • web/dashboard/src/pages/models/FailoverDrafts.svelte
  • web/dashboard/src/pages/models/FailoverEditor.svelte
  • web/dashboard/src/pages/models/ModelGlobalActions.svelte
  • web/dashboard/src/pages/models/ModelRow.svelte
  • web/dashboard/src/pages/models/ModelTable.svelte
  • web/dashboard/src/pages/models/ModelsPage.svelte
  • web/dashboard/src/pages/models/PricingOverrideEditor.svelte
  • web/dashboard/src/pages/models/VirtualModelEditor.svelte
  • web/dashboard/src/pages/models/VmTargetRow.svelte
  • web/dashboard/src/pages/models/failover.svelte.js
  • web/dashboard/src/pages/overview/ProviderStatusCard.svelte
  • web/dashboard/src/pages/providers-config/ProviderCredentialField.svelte
  • web/dashboard/src/pages/providers-config/ProviderCredentialList.svelte
  • web/dashboard/src/pages/providers-config/ProvidersConfigPage.svelte
  • web/dashboard/src/pages/providers-config/providersConfig.svelte.js
  • web/dashboard/src/pages/rate-limits/RateLimitInspector.svelte
  • web/dashboard/src/pages/rate-limits/RateLimitList.svelte
  • web/dashboard/src/pages/rate-limits/RateLimitsPage.svelte
  • web/dashboard/src/pages/settings/BudgetResetSettings.svelte
  • web/dashboard/src/pages/settings/BudgetSettings.svelte
  • web/dashboard/src/pages/settings/FailoverSettings.svelte
  • web/dashboard/src/pages/settings/PricingRecalculation.svelte
  • web/dashboard/src/pages/settings/RuntimeRefresh.svelte
  • web/dashboard/src/pages/settings/TaggingSettings.svelte
  • web/dashboard/src/pages/usage/UsageLog.svelte
  • web/dashboard/src/pages/workflows/WorkflowCard.svelte
  • web/dashboard/src/pages/workflows/WorkflowChart.svelte
  • web/dashboard/src/pages/workflows/WorkflowIdBadge.svelte
  • web/dashboard/src/pages/workflows/WorkflowsPage.svelte
  • web/dashboard/tests/icons.test.js
💤 Files with no reviewable changes (1)
  • web/dashboard/src/lib/components/atoms/icons.js

submitLabel = "Save",
submittingLabel = "Saving...",
submitIcon = "save",
submitIcon = Save,

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 '<EditorDialog\b|submitIcon\s*=' \
  web/dashboard/src --glob '*.svelte' --glob '*.js'

Repository: ENTERPILOT/GoModel

Length of output: 17093


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== EditorDialog relevant source =="
sed -n '1,180p' web/dashboard/src/lib/components/organisms/EditorDialog.svelte

echo
echo "== Icon component definitions/usages =="
fd -i 'Icon\.svelte$|Icon.*\.svelte' web/dashboard/src --exec sh -c 'echo "--- $1"; sed -n "1,220p" "$1"' sh {}

Repository: ENTERPILOT/GoModel

Length of output: 5589


Update the remaining submitIcon callers to use Lucide icon data.

Icon.svelte expects Lucide icon data ([tag, attrs, children]), so callers currently passing "plus", "save", "check", or "delete" render an empty SVG node. Import the matching Lucide icons and pass them to EditorDialog on these call sites:
web/dashboard/src/pages/auth-keys/AuthKeyEditor.svelte:20,
web/dashboard/src/pages/models/VirtualModelEditor.svelte:34,
web/dashboard/src/pages/workflows/WorkflowEditor.svelte:21, and the AuthKeyLabelsEditor/MCP/Tenant/Rate-limit/Policy editors where they may also use string icons.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/dashboard/src/lib/components/organisms/EditorDialog.svelte` at line 46,
Update all remaining callers of EditorDialog's submitIcon prop to pass Lucide
icon data instead of strings. In AuthKeyEditor.svelte,
VirtualModelEditor.svelte, WorkflowEditor.svelte, and other editor components
(AuthKeyLabelsEditor, MCP, Tenant, Rate-limit, Policy editors), replace the
string values "plus", "save", "check", and "delete" with their corresponding
imported Lucide icons (Plus, Save, Check, Trash respectively). Import each
needed Lucide icon at the top of each file and update the submitIcon assignments
to use the imported icon data in the same format that Save is now used in
EditorDialog.svelte.

Comment on lines +31 to +36
for (const match of source.matchAll(/import\s*\{([^}]*)\}\s*from\s*"lucide";/g)) {
for (const raw of match[1].split(",")) {
const name = raw.trim();
if (!name) continue;
if (!found.has(name)) found.set(name, []);
found.get(name).push(file.slice(SRC.length + 1));

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
fd -a 'icons.test.js' . || true

echo "== file excerpt =="
if [ -f web/dashboard/tests/icons.test.js ]; then
  nl -ba web/dashboard/tests/icons.test.js | sed -n '1,100p'
fi

echo "== lint/import config references =="
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' "eslint-plugin-import|eslint-config-prettier|single quotes|quote|jsx-a11y|svelte/linter|linter|import/.source|import source" . || true

echo "== changed stats and target diff =="
git diff --stat || true
git diff -- web/dashboard/tests/icons.test.js || true

echo "== deterministic regex behavior probe =="
python3 - <<'PY'
import re
pattern = re.compile(r'import\s*\{([^}]*)\}\s*from\s*"lucide";(?=\s|$)')
samples = ['import { Pencil } from "lucide";', "import { Pencil } from 'lucide';", 'import { Pencil } from "lucide"', 'import { Pencil as EditIcon } from "lucide";']
for s in samples:
    m = pattern.search(s)
    print(repr(s), "=>", ["[" + ", ".join(f'{name.strip()}: src="A"' for name in (m.group(1) or "").split(",")) + "]" if m else None])
PY

Repository: ENTERPILOT/GoModel

Length of output: 286


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file excerpt =="
if [ -f web/dashboard/tests/icons.test.js ]; then
  cat -n web/dashboard/tests/icons.test.js | sed -n '1,100p'
fi

echo "== changed stats and target diff =="
git diff --stat || true
git diff -- web/dashboard/tests/icons.test.js || true

echo "== lint/import config references =="
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' "eslint-plugin-import|eslint-config-prettier|single quotes|quote|jsx-a11y|svelte/linter|linter|import/.source|import source" . || true

echo "== deterministic regex behavior probe =="
python3 - <<'PY'
import re

# Current scan uses /import\s*\{([^}]*)\}\s*from\s*"lucide";/g.
# This regex stops after from "lucide"; but also runs without lookahead in MatchAll context.
pattern = re.compile(r'import\s*\{([^}]*)\}\s*from\s*"lucide";')
samples = [
    'import { Pencil } from "lucide";',
    "import { Pencil } from 'lucide';",
    'import { Pencil } from "lucide"',
    'import { Pencil as EditIcon } from "lucide";',
    'import { Pencil as EditIcon, ArrowLeft } from "lucide";',
]
for s in samples:
    m = pattern.search(s)
    if m:
        names = [name.strip() for name in m.group(1).split(",")]
    else:
        names = None
    print(repr(s), "=> matched:", bool(m), "names:", names)
PY

Repository: ENTERPILOT/GoModel

Length of output: 10687


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== lock and package manifest files =="
git ls-files | rg '(^|/)(package(-lock)?\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?)$' || true

echo "== lucide dependency declarations =="
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' '"lucide("' package.json web/dashboard/package.json pnpm-lock.yaml package-lock.json yarn.lock bun.lock bun.lockb 2>/dev/null || true

echo "== source lucide imports =="
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'import\s*\{[^}]*\}\s*from\s+["'\'']lucide["'\'']' web/dashboard/src packages 2>/dev/null || true

echo "== ESLint and Svelte config files =="
git ls-files | rg '(^|/)(eslint|eslint-plugin-import|svelte.config|svelte-check|jsconfig|tsconfig|prettier)(\.(js|cjs|mjs|json|yml|yaml))?$|(\.eslintrc|eslint\.config|\.lintstagedrc|svelte\.config|jsconfig|tsconfig).*\.(js|cjs|mjs|json|yml|yaml)$' || true

echo "== all non-build files with explicit quote rules =="
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!web/dashboard/tests/icons.test.js' "quote|single|jsx-quotes|import source|import from" \
  .eslintrc.js .eslintrc.cjs .eslintrc.json .eslintrc.yml .eslintrc.yaml svelte.config.* eslint.config.* jsconfig.* web/dashboard/eslint.config.* web/dashboard/.eslintrc.* 2>/dev/null || true

Repository: ENTERPILOT/GoModel

Length of output: 5799


Handle aliased Lucide imports in importedIcons().

The scan matches the double-quoted form, but import { Pencil as EditIcon } from "lucide"; records the export as Pencil as EditIcon, so the comment’s stated contract is not enforced through lucide[name] for valid alias syntax. Strip the local alias before the export lookup. Also applies to lines 48-49.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/dashboard/tests/icons.test.js` around lines 31 - 36, Update
importedIcons() to parse aliased named imports by extracting the exported Lucide
name before the “as” clause, so lucide[name] uses Pencil rather than the local
alias EditIcon. Preserve existing handling for non-aliased imports and apply the
same normalization in the related logic at lines 48–49.

@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown

Confidence Score: 4/5

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex executed the Playwright capture script to reproduce the posted P1 finding and produced a runtime output trace.
  • T-Rex used the budget-period rendering harness and loaded the harness page to exercise the UI path related to the finding.
  • T-Rex reviewed the original capture invocation output to confirm the exact inputs used for the reproduction.
  • T-Rex validated that the observed Playwright runtime output matched the expected reproduction criteria and aligned with the posted finding.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "refactor(dashboard): pass lucide icons a..." | Re-trigger Greptile

<div class="budget-row-period">
<span class="budget-period-label {budgetPeriodClass(item)}">
<Icon name={budgetPeriodIcon(item)} class="budget-period-icon" />
<Icon icon={budgetPeriodIcon(item)} class="budget-period-icon" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Budget period icons use the removed string contract

budgetPeriodIcon(item) still returns legacy strings such as "clock" and "calendar", but Icon now iterates its icon prop as a Lucide SVG node array. Rendering a budget-period row therefore throws from Icon.svelte before the budget list mounts. Return imported Lucide icon values for each period instead of strings.

Artifacts

Playwright capture script

  • Review-authored Chromium capture script that loads the focused budget-icon harness and inspects rendered period icon SVG children.

Budget-period rendering harness

  • Review-authored Svelte harness that reproduces BudgetList.svelte line 66 for hourly, daily, weekly, monthly, and custom period rows alongside valid lucide controls.

Budget-period harness page

  • Review-authored Vite entry page that mounts the focused budget-period rendering harness for Chromium.

Observed Playwright runtime output

  • Captured command output showing the Icon.svelte TypeError and confirming that no harness main element mounted.

Original capture invocation output

  • Captured output from the original review-authored capture script invocation, recording its working directory and nonzero result when rendering failed.

View artifacts

T-Rex Ran code and verified through T-Rex

Review follow-up. Three EditorDialog callers passed submitIcon as a name
string, and budgetPeriodIcon() still returned names — both render a blank
SVG now that Icon takes the icon itself. The codemod only rewrote <Icon>
tags and icon: config literals, so neither shape was touched.

Adds two guards: one asserts no icon binding is assigned a name string,
one asserts budgetPeriodIcon returns a drawable icon. Both were confirmed
to fail on the exact bugs above.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 4, 2026 15:34

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.


for (const file of sourceFiles(SRC)) {
const source = readFileSync(file, "utf8");
for (const [match, prop, value] of source.matchAll(binding)) {

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/dashboard/tests/icons.test.js`:
- Around line 67-76: Update the icon-binding check around the binding loop and
produced-value extraction so direct quoted string expressions such as
icon={"inbox"} and single-quoted equivalents are inspected and rejected, while
preserving ternary branch handling. Ensure the extraction does not discard
non-ternary string values before the existing icon-name validation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 47b950c7-8003-4009-b27e-463e3250f021

📥 Commits

Reviewing files that changed from the base of the PR and between 33b3577 and a63773a.

⛔ Files ignored due to path filters (2)
  • internal/admin/dashboard/static/dist/assets/index-G3uz-AWc.js is excluded by !**/dist/**
  • internal/admin/dashboard/static/dist/index.html is excluded by !**/dist/**
📒 Files selected for processing (6)
  • web/dashboard/src/pages/auth-keys/AuthKeyEditor.svelte
  • web/dashboard/src/pages/budgets/budgets-helpers.js
  • web/dashboard/src/pages/models/VirtualModelEditor.svelte
  • web/dashboard/src/pages/workflows/WorkflowEditor.svelte
  • web/dashboard/tests/budgets.test.js
  • web/dashboard/tests/icons.test.js

Comment on lines +67 to +76
const binding = /\b(icon|submitIcon)\s*(?:=|:)\s*(\{[^}]*\}|"[^"]*")/g;
const offenders = [];

for (const file of sourceFiles(SRC)) {
const source = readFileSync(file, "utf8");
for (const [match, prop, value] of source.matchAll(binding)) {
const produced = value.startsWith("{")
? value.slice(1, -1).split(/\?|:/).slice(1).join(" ") // ternary branches only
: value;
if (!/"[a-z][a-z0-9-]*"/.test(produced)) continue;

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Inspect direct string expressions in the icon-binding check.

At Line 74, split(/\?|:/).slice(1) returns an empty string for icon={"inbox"} because the expression has no ternary operator. The test therefore permits a string-valued icon binding. The same gap affects single-quoted strings.

Proposed fix
-  const binding = /\b(icon|submitIcon)\s*(?:=|:)\s*(\{[^}]*\}|"[^"]*")/g;
+  const binding =
+    /\b(icon|submitIcon)\s*(?:=|:)\s*(\{[^}]*\}|"[^"]*"|'[^']*')/g;
...
-      const produced = value.startsWith("{")
-        ? value.slice(1, -1).split(/\?|:/).slice(1).join(" ") // ternary branches only
-        : value;
-      if (!/"[a-z][a-z0-9-]*"/.test(produced)) continue;
+      const expression = value.startsWith("{")
+        ? value.slice(1, -1)
+        : value;
+      const produced = expression.includes("?")
+        ? expression.split("?").slice(1).join(" ")
+        : expression;
+      if (!/(["'])[a-z][a-z0-9-]*\1/.test(produced)) continue;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const binding = /\b(icon|submitIcon)\s*(?:=|:)\s*(\{[^}]*\}|"[^"]*")/g;
const offenders = [];
for (const file of sourceFiles(SRC)) {
const source = readFileSync(file, "utf8");
for (const [match, prop, value] of source.matchAll(binding)) {
const produced = value.startsWith("{")
? value.slice(1, -1).split(/\?|:/).slice(1).join(" ") // ternary branches only
: value;
if (!/"[a-z][a-z0-9-]*"/.test(produced)) continue;
const binding =
/\b(icon|submitIcon)\s*(?:=|:)\s*(\{[^}]*\}|"[^"]*"|'[^']*')/g;
const offenders = [];
for (const file of sourceFiles(SRC)) {
const source = readFileSync(file, "utf8");
for (const [match, prop, value] of source.matchAll(binding)) {
const expression = value.startsWith("{")
? value.slice(1, -1)
: value;
const produced = expression.includes("?")
? expression.split("?").slice(1).join(" ")
: expression;
if (!/(["'])[a-z][a-z0-9-]*\1/.test(produced)) continue;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/dashboard/tests/icons.test.js` around lines 67 - 76, Update the
icon-binding check around the binding loop and produced-value extraction so
direct quoted string expressions such as icon={"inbox"} and single-quoted
equivalents are inspected and rejected, while preserving ternary branch
handling. Ensure the extraction does not discard non-ternary string values
before the existing icon-name validation.

@SantiagoDePolonia
SantiagoDePolonia merged commit a37ec24 into main Aug 4, 2026
20 checks passed
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.

3 participants