chore(sync): derive icon-map.json from the shared icon keys, and guard it - #378
Merged
Conversation
…ard it Verifies and lands the icon-map content from the closed PR #67, rebuilt rather than imported. The map is now **derived**. b24ui and nuxt/ui define the same semantic icon keys — `src/runtime/dictionary/icons.ts` here, `src/theme/icons.ts` upstream — so for each of the 37 keys both sides carry, the entry is (upstream's lucide name -> b24ui's component). Nothing is chosen by eye. That turned up three wrong values in #67, none of which would have failed an import because each resolves to a real icon: - `i-lucide-rotate-cw` where upstream's `reload` is `i-lucide-rotate-ccw` - `i-lucide-circle-check` for `copyCheck`, whose lucide name is `i-lucide-copy-check` (`circle-check` is upstream's `success`, a key we do not have) - `i-lucide-refresh-cw`, which no upstream key uses and seven derivable pairs it had missed: `drag`, `panelClose`, `panelOpen`, `star`, `stop`, `copyCheck`, `reload`. The five entries #67 dropped (`activity`, `arrow-up-to-line`, `house`, `settings`, `user`) are kept — they match no key on either side, which is exactly the hardcoded-literal case this table exists for. #67's five corrections to the committed values are confirmed: `check`, `chevronDown`, `chevronUp`, `minus` and `x` all pointed at glyphs this library does not render for those roles. `test/utils/icon-map.spec.ts` holds the result: every value must resolve in the installed `@bitrix24/b24icons-vue`, must come from the dictionary (or a five-entry documented exception list), and must not pick the wrong group for a name the dictionary already places — 233 icon names exist in more than one group and 16 of those are in this map, so `solid/RefreshIcon` for `outline/RefreshIcon` is a live failure mode, not a hypothetical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JS8ypVfQSFzYVZzkTHhURb
This was referenced Aug 12, 2026
Closed
Review follow-up on this branch. Three of the findings were substantive. **The recorded justification was false.** The changelog said the five corrected values pointed at "glyphs the library does not render for those roles". It does render them: `Checkbox.vue` renders `main/CheckIcon` and `actions/Minus20Icon`, `Badge.vue` renders `actions/Cross20Icon`, `Button.vue` renders `outline/ChevronDownSIcon`. Roughly half the icon paths under `src/` are hardcoded in components that never read the dictionary. The corrections stand — the map must agree with the dictionary — but the reason is restated, and the underlying inconsistency is now #380. **The guard did not check the derivation.** Pointing `i-lucide-check` at any other icon the dictionary genuinely uses passed every assertion. There is now an explicit `i-lucide-* -> semantic key` table, and each derived row must equal what that key resolves to; a row disappearing fails too. **The map was missing the one literal it exists for.** `i-lucide-terminal` is the only `i-lucide-*` string upstream hardcodes under `src/`, and `prose/CodeIcon.vue` has answered it all along. The old dictionary-only allowlist would have rejected it, so the guard's basis widens to any icon used anywhere in `src/`. Also: `i-lucide-circle-x` gains a row (our `caution` is commented "this for error"); the `$schema-note` no longer contradicts itself about `circle-check`; duplicate JSON keys, which the import silently collapses, are now caught; stray values report their key; a redundant cast is gone; and both the note and the changelog now say the guard catches wrong rows, not stale ones. Not changed: the 37-pair / 43x39 counts. Three reviewers read the installed `@nuxt/ui@4.8.2` — pulled in transitively by `nuxtseo-layer-devtools`, older than the sync cursor — and concluded `star` was fabricated. Upstream at `3dbca02` has it. That trap is now written down. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JS8ypVfQSFzYVZzkTHhURb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Verifies and lands the icon-map content from the closed #67 — rebuilt rather than imported, per the follow-up flagged in #377. Revised after review; see the last section for what changed and why.
The map is derived, not chosen
b24ui and nuxt/ui define the same semantic icon keys:
src/runtime/dictionary/icons.tshere,src/theme/icons.tsupstream. For every key both sides carry, the row follows mechanically — (upstream's lucide name → whatever our dictionary maps that key to). 43 upstream keys × 39 of ours = 37 derived rows, no judgement involved.Every value in both the old map and #67's proposal resolves to a real icon in
@bitrix24/b24icons-vue@2.0.7, so "does it exist" settles nothing. The dictionary does.Three wrong values in #67
None would have failed an import — each resolves:
i-lucide-rotate-cw→ RefreshIconi-lucide-rotate-ccwreloadis counter-clockwisei-lucide-circle-check→ CircleCheckIconi-lucide-copy-checkcopyCheck's lucide namei-lucide-refresh-cw→ Refresh6IconPlus seven derivable pairs it missed:
drag(grip-vertical),panelClose,panelOpen,star,stop(square),copyCheck,reload.The one row that mattered most was in neither map
i-lucide-terminalis the onlyi-lucide-*literal upstream hardcodes undersrc/(src/theme/prose/code-icon.ts) — the exact case this table exists for — andprose/CodeIcon.vuehas answered it withfile-type/TerminalIconall along. Neither the old map nor #67 had it.i-lucide-circle-x(upstream'serror) also gains a row: our dictionary commentscaution: AlertIconwith// this for error, so it was mappable, not unmappable.Final map: 46 rows — 37 derived, 9 judgement calls, each named in the file's own
$schema-note.#67's five corrections are confirmed — but not for the reason first given
The old map disagreed with the dictionary on
check,chevronDown,chevronUp,minusandx. Correcting them is right. The justification in the first draft of this PR was wrong, and is corrected:i-lucide-checkmain/CheckIconoutline/CheckLIconCheckbox.vue:62i-lucide-minusactions/Minus20Iconactions/Minus30IconCheckbox.vue:61i-lucide-xactions/Cross20Iconoutline/CrossMIconBadge.vue:64i-lucide-chevron-downoutline/ChevronDownSIconoutline/ChevronDownLIconButton.vue:101Those are not glyphs the library never renders — they are glyphs it renders from components that bypass the dictionary. Roughly half the
@bitrix24/b24icons-vue/…paths undersrc/are hardcoded that way. Split out as #380.The guard —
test/utils/icon-map.spec.tsThe table rots silently: nothing imports it, nothing renders it, and a wrong row surfaces only as the wrong glyph in whatever gets ported next.
i-lucide-* → keytable, re-resolved through the dictionary on every run.@bitrix24/b24icons-vue.src/actually uses, or one of five documented legacy entries — deliberately not narrowed to the dictionary, which would rejectterminal.i-lucide-*shape, and non-trivial-parse checks.Proven by mutation rather than assumed. The defect that escaped the first version — swapping
i-lucide-checkfor another icon the dictionary genuinely uses — now fails by name:and so do a deleted row, a nonexistent path, an unused icon, and a right-name/wrong-group value.
PORTING.md§1 gains the icons note: most icon diffs are 1:1 because the keys are shared, so
icons.<key>ports unchanged — with the caveat that the dictionary is the declared authority, not the only one, so check the component too (#380). It also states plainly that the guard catches wrong rows, not stale ones: nothing here notices if upstream renames a default, so re-verify when a port touches an icon key.No
.sync/log/or ledger entry — this is not a port of an upstream commit, same as #343, #346, #351 and #377.Review round
Five specialist reviews plus
/code-review. Substantive findings, all fixed here: the false "does not render" claim; the guard not checking the derivation; the missingterminalrow and the too-narrow allowlist that would have rejected it; the self-contradicting$schema-note;errorlisted as unmappable. Smaller ones: stray values now report their key, duplicate keys are caught, a bundled assertion is split, a redundant cast dropped.Security review: no meaningful impact —
require.resolvelocates without loading, and neither.sync/nortest/ships in the package.Follow-ups split out rather than folded in: #380 (components bypass the dictionary — needs a design call, changes what users see) and #381 (the icons integration docs are stale against
icons.ts, which asks to keep them in sync).Verify (
CI=true)lint·typecheck·test·build— all green. Tests 6218 passed | 6 skipped across 266 files.