272 lines
18 KiB
Markdown
272 lines
18 KiB
Markdown
---
|
|
phase: quick-261005-qvk
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- admin/src/app/icons.ts
|
|
- admin/tests/app/icons.test.ts
|
|
- modules/boardwalk/dist/
|
|
autonomous: true
|
|
requirements: [QUICK-261005-qvk]
|
|
|
|
estimate:
|
|
tokens: 50000
|
|
raw_tokens: 50000
|
|
tasks: 2
|
|
confidence: low
|
|
|
|
must_haves:
|
|
truths:
|
|
- "iconFor() still prefers a lucide registry name, then a Winter icon-* alias, then Square; empty, null, undefined, unknown names, and inherited Object keys (constructor, toString, __proto__) still return Square"
|
|
- "BM Studies nav names render lucide icons, not Square: icon-graduation-cap, icon-book, icon-bookmark, icon-calendar, icon-clock-o, icon-flag, icon-file-text-o, icon-tags"
|
|
- "Quizzes names icon-question-circle and icon-list, and the documented Winter name icon-pencil, resolve to lucide components rather than Square"
|
|
- "Every winterIcons alias points at a key that exists in the lucide icons map; named imports stay named (no namespace import of @lucide/vue); no Font Awesome package and no new icon library"
|
|
- "admin/tests/app/icons.test.ts covers the BM set, Quizzes names, icon-pencil, a handful of other Winter aliases from the closed table, and the existing fallback cases; npm --prefix admin test -- tests/app/icons.test.ts and npm --prefix admin run typecheck pass"
|
|
- "modules/boardwalk/dist matches a fresh admin Vite build (scripts/check-admin-dist.sh); HEAD is one code commit of icons.ts, icons.test.ts and the dist tree, with no co-author or session trailer and no .planning files"
|
|
artifacts:
|
|
- path: "admin/src/app/icons.ts"
|
|
provides: "Lucide registry plus Winter icon-* aliases used by PluginRail, SectionPanel, SectionFlyout and SettingsIndexView"
|
|
contains: "icon-graduation-cap"
|
|
- path: "admin/tests/app/icons.test.ts"
|
|
provides: "iconFor unit coverage for BM, Quizzes, icon-pencil and unknown fallback"
|
|
contains: "icon-graduation-cap"
|
|
- path: "modules/boardwalk/dist/"
|
|
provides: "Embedded SPA build that includes the new named lucide icons"
|
|
contains: "index.html"
|
|
key_links:
|
|
- from: "pact.NavigationItem.Icon / SettingsItem.Icon (Winter icon-* string from the admin API)"
|
|
to: "iconFor() in PluginRail, SectionPanel, SectionFlyout, SettingsIndexView"
|
|
via: "winterIcons alias then icons[lucideName], else fallbackIcon Square"
|
|
pattern: "own\\(winterIcons"
|
|
- from: "named @lucide/vue imports in icons.ts"
|
|
to: "icons Record keys (kebab-case lucide names)"
|
|
via: "tree-shaken named imports only; comment in icons.ts already forbids a namespace import"
|
|
pattern: "from '@lucide/vue'"
|
|
---
|
|
|
|
<objective>
|
|
Make Winter-style Font Awesome class names on plugin navigation and settings actually render in the SummerCMS admin SPA. Plugins such as jz.bm send names like icon-graduation-cap and icon-book; the SPA never loads Font Awesome, so unknown names become empty Square glyphs. Expand the lucide registry and the Winter alias map in admin/src/app/icons.ts so BM Studies, Quizzes, the documented icon-pencil examples, and a closed set of common Winter backend names resolve to existing @lucide/vue 1.17.0 exports. Keep Square for truly unknown names. Do not change plugin YAML or Go in other repositories.
|
|
|
|
Purpose: the documented Winter icon-* API on pact.NavigationItem.Icon and SettingsItem.Icon is the contract ported plugins already use; the framework SPA must honour it.
|
|
Output: updated icons.ts and icons.test.ts, a rebuilt modules/boardwalk/dist, one code commit.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@~/.codex/gsd-core/workflows/execute-plan.md
|
|
@~/.codex/gsd-core/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/STATE.md
|
|
@CLAUDE.md
|
|
@admin/src/app/icons.ts
|
|
@admin/tests/app/icons.test.ts
|
|
@admin/package.json
|
|
@docs/backend/admin-spa.md
|
|
@docs/backend/admin-controllers.md
|
|
@docs/setup/porting-a-plugin.md
|
|
@scripts/check-admin-dist.sh
|
|
|
|
Phase 10 D-11: navigation icons are lucide names in a curated registry; a small Winter icon-* map lets ported plugins work unchanged; unknown names get a neutral fallback. The map today is only icon-archive, icon-circle, icon-list-ul, icon-tags, icon-user, icon-search, icon-cog, icon-users. iconFor trims the name, looks up icons, then winterIcons then icons again, else Square. own() uses Object.hasOwn so constructor/toString never count.
|
|
|
|
Call sites already wired (do not change them): PluginRail.vue, SectionPanel.vue, SectionFlyout.vue, SettingsIndexView.vue all pass the server icon string through iconFor().
|
|
|
|
Package: @lucide/vue 1.17.0 (not lucide-vue-next). Named imports only. Confirmed exports in admin/node_modules/@lucide/vue/dist/lucide-vue.d.ts: Book, Bookmark, Calendar, Clock, Flag, FileText, GraduationCap, Pencil, CircleQuestionMark, File, Folder, Copy, Files, Globe, Mail, House, Database, Info, Leaf, Lock, Key, Link, Star, Heart, Bell, Inbox, Download, Upload, Funnel, Wrench, WandSparkles, LifeBuoy, Network, MessageCircle, TriangleAlert, Box, Briefcase, Building, Languages, Gauge, MapPin, Phone, Camera, Play, Pause, ShoppingCart, CreditCard, Table, LayoutGrid, RefreshCw, ExternalLink, CircleCheck, ListOrdered, ClipboardList, Trophy, Paperclip, Code, Cloud, ChartBar. CircleHelp and HelpCircle are absent in this pin; icon-question-circle maps to CircleQuestionMark (key circle-question-mark). Filter is absent; icon-filter maps to Funnel (key funnel). List, Tags, Image, Plus, Minus, Check, X, Settings, User, Users, Archive, Circle, Puzzle, Trash2, LogOut already sit in icons.
|
|
|
|
Docs (admin-controllers.md, users-and-permissions.md, settings.md, porting-a-plugin.md, docs/examples/blog/plugin.go) show Icon: "icon-pencil" as Winter examples. They do not claim those names already render and they do not load Font Awesome. Leave those pages unchanged; mapping icon-pencil makes the examples true. Do not add an icon catalogue.
|
|
|
|
BM Studies names from the task: group icon-graduation-cap, Courses icon-book, Readings icon-bookmark, Meetings icon-calendar, Sessions icon-clock-o, Deadlines icon-flag, Materials icon-file-text-o, Tags icon-tags. Quizzes: icon-question-circle, icon-list.
|
|
|
|
Closed Winter alias table (Winter name -> lucide key already in, or added to, icons). Keep every alias already present.
|
|
|
|
BM and documented:
|
|
icon-graduation-cap -> graduation-cap
|
|
icon-book -> book
|
|
icon-bookmark -> bookmark
|
|
icon-calendar -> calendar
|
|
icon-clock-o -> clock
|
|
icon-clock -> clock
|
|
icon-flag -> flag
|
|
icon-file-text-o -> file-text
|
|
icon-file-text -> file-text
|
|
icon-tags -> tags (exists)
|
|
icon-pencil -> pencil
|
|
icon-edit -> pencil
|
|
icon-question-circle -> circle-question-mark
|
|
icon-question -> circle-question-mark
|
|
icon-list -> list (exists)
|
|
|
|
Already mapped, keep: icon-archive, icon-circle, icon-list-ul, icon-user, icon-search, icon-cog, icon-users.
|
|
|
|
Common Winter backend (core / RainLab / typical plugin nav and settings):
|
|
icon-home -> house
|
|
icon-leaf -> leaf
|
|
icon-magic -> wand-sparkles
|
|
icon-life-ring -> life-buoy
|
|
icon-sitemap -> network
|
|
icon-copy -> copy
|
|
icon-files-o -> files
|
|
icon-envelope -> mail
|
|
icon-envelope-o -> mail
|
|
icon-globe -> globe
|
|
icon-database -> database
|
|
icon-info-circle -> info
|
|
icon-exclamation-triangle -> triangle-alert
|
|
icon-warning -> triangle-alert
|
|
icon-lock -> lock
|
|
icon-key -> key
|
|
icon-star -> star
|
|
icon-heart -> heart
|
|
icon-comment -> message-circle
|
|
icon-comments -> message-circle
|
|
icon-comments-o -> message-circle
|
|
icon-folder -> folder
|
|
icon-folder-o -> folder
|
|
icon-file -> file
|
|
icon-file-o -> file
|
|
icon-picture-o -> image
|
|
icon-photo -> image
|
|
icon-plus -> plus
|
|
icon-minus -> minus
|
|
icon-check -> check
|
|
icon-check-circle -> circle-check
|
|
icon-times -> x
|
|
icon-trash-o -> trash-2
|
|
icon-trash -> trash-2
|
|
icon-sign-out -> log-out
|
|
icon-cogs -> settings
|
|
icon-group -> users
|
|
icon-list-ol -> list-ordered
|
|
icon-list-alt -> clipboard-list
|
|
icon-dashboard -> gauge
|
|
icon-tachometer -> gauge
|
|
icon-th -> layout-grid
|
|
icon-th-large -> layout-grid
|
|
icon-table -> table
|
|
icon-bar-chart -> chart-bar
|
|
icon-wrench -> wrench
|
|
icon-cube -> box
|
|
icon-building -> building
|
|
icon-briefcase -> briefcase
|
|
icon-language -> languages
|
|
icon-download -> download
|
|
icon-upload -> upload
|
|
icon-refresh -> refresh-cw
|
|
icon-filter -> funnel
|
|
icon-link -> link
|
|
icon-code -> code
|
|
icon-cloud -> cloud
|
|
icon-bell -> bell
|
|
icon-inbox -> inbox
|
|
icon-paperclip -> paperclip
|
|
icon-external-link -> external-link
|
|
icon-shopping-cart -> shopping-cart
|
|
icon-credit-card -> credit-card
|
|
icon-map-marker -> map-pin
|
|
icon-phone -> phone
|
|
icon-camera -> camera
|
|
icon-play -> play
|
|
icon-pause -> pause
|
|
icon-trophy -> trophy
|
|
|
|
Do not add Font Awesome, lucide-vue-next, or any new npm package. Do not change plugin navigation in other repos. Do not commit .planning artifacts. Rebuild dist: hashed files under modules/boardwalk/dist/assets/ will change; commit the whole dist tree with the source.
|
|
|
|
Existing tests in admin/tests/app/icons.test.ts: lucide names disc-3/tags, aliases icon-archive/icon-cog, Square fallback including constructor/toString/__proto__, and a loop that every winterIcons value exists in icons. Extend that file; keep those cases.
|
|
|
|
Verify command that already works in this repo: npm --prefix admin test (vitest run) and npm --prefix admin run typecheck. After a SPA source change, npm --prefix admin run build then scripts/check-admin-dist.sh.
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="tracer" tdd="true">
|
|
<name>Task 1: Tracer - iconFor maps BM Winter names and icon-pencil onto lucide components</name>
|
|
<files>admin/src/app/icons.ts, admin/tests/app/icons.test.ts</files>
|
|
<read_first>admin/src/app/icons.ts, admin/tests/app/icons.test.ts, admin/node_modules/@lucide/vue/dist/lucide-vue.d.ts (confirm Book, Bookmark, Calendar, Clock, Flag, FileText, GraduationCap, Pencil exist as declare const)</read_first>
|
|
<behavior>
|
|
- iconFor('icon-graduation-cap') returns GraduationCap, not Square
|
|
- iconFor('icon-book') returns Book; iconFor('icon-bookmark') Bookmark; iconFor('icon-calendar') Calendar; iconFor('icon-clock-o') Clock; iconFor('icon-flag') Flag; iconFor('icon-file-text-o') FileText; iconFor('icon-tags') Tags; iconFor('icon-pencil') Pencil
|
|
- lucide keys work too: iconFor('book') returns Book, iconFor(' graduation-cap ') returns GraduationCap
|
|
- Existing cases still pass: icon-archive, icon-cog, unknown-icon-name, icon-unknown, empty/null/undefined, constructor, toString, __proto__ all unchanged
|
|
- Every winterIcons value is a key in icons
|
|
</behavior>
|
|
<action>
|
|
In admin/tests/app/icons.test.ts, extend the Winter-alias case (and add lucide-name cases as needed) so the BM names and icon-pencil assert the matching @lucide/vue components. Import those components from @lucide/vue the same way the file already imports Archive, Disc3, Settings, Square, Tags. Run the file and confirm the new assertions fail because the aliases and registry keys are missing.
|
|
|
|
Then in admin/src/app/icons.ts add named imports Book, Bookmark, Calendar, Clock, Flag, FileText, GraduationCap, Pencil from @lucide/vue (keep the existing named-import list; do not switch to a namespace import). Register kebab-case keys book, bookmark, calendar, clock, flag, file-text, graduation-cap, pencil on icons. Add winterIcons aliases from the BM-and-documented block in context (icon-graduation-cap, icon-book, icon-bookmark, icon-calendar, icon-clock-o, icon-clock, icon-flag, icon-file-text-o, icon-file-text, icon-pencil, icon-edit; icon-tags already exists). Do not change iconFor, own, or fallbackIcon. Do not add a new dependency. Do not edit Vue shell components; they already call iconFor.
|
|
|
|
Leave Quizzes and the rest of the common Winter table for Task 2. Do not rebuild dist yet. Do not commit yet.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && npm --prefix admin test -- tests/app/icons.test.ts</automated>
|
|
</verify>
|
|
<done>iconFor resolves every BM Studies Winter name and icon-pencil to the matching lucide component; unknown names still return Square; the existing constructor/toString fallback cases still pass; nothing committed.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Map Quizzes and the closed Winter backend alias table, rebuild the embedded SPA, commit once</name>
|
|
<files>admin/src/app/icons.ts, admin/tests/app/icons.test.ts, modules/boardwalk/dist/</files>
|
|
<read_first>admin/src/app/icons.ts, admin/tests/app/icons.test.ts, scripts/check-admin-dist.sh, docs/backend/admin-spa.md (confirm it does not claim icon names already render; do not edit docs)</read_first>
|
|
<behavior>
|
|
- iconFor('icon-question-circle') returns CircleQuestionMark; iconFor('icon-list') returns List
|
|
- iconFor('icon-home') returns House; iconFor('icon-magic') WandSparkles; iconFor('icon-life-ring') LifeBuoy; iconFor('icon-sitemap') Network; iconFor('icon-envelope-o') Mail; iconFor('icon-copy') Copy
|
|
- Every alias in the closed table in context is present in winterIcons and resolves through iconFor to a defined lucide component
|
|
- iconFor('icon-unknown') and iconFor('constructor') still return Square
|
|
- typecheck passes; a fresh Vite build matches modules/boardwalk/dist
|
|
</behavior>
|
|
<action>
|
|
Add the remaining named @lucide/vue imports from the closed table in context (CircleQuestionMark, File, Folder, Copy, Files, Globe, Mail, House, Database, Info, Leaf, Lock, Key, Link, Star, Heart, Bell, Inbox, Download, Upload, Funnel, Wrench, WandSparkles, LifeBuoy, Network, MessageCircle, TriangleAlert, Box, Briefcase, Building, Languages, Gauge, MapPin, Phone, Camera, Play, Pause, ShoppingCart, CreditCard, Table, LayoutGrid, RefreshCw, ExternalLink, CircleCheck, ListOrdered, ClipboardList, Trophy, Paperclip, Code, Cloud, ChartBar). Register each as its lucide kebab-case key on icons (circle-question-mark, file, folder, copy, files, globe, mail, house, database, info, leaf, lock, key, link, star, heart, bell, inbox, download, upload, funnel, wrench, wand-sparkles, life-buoy, network, message-circle, triangle-alert, box, briefcase, building, languages, gauge, map-pin, phone, camera, play, pause, shopping-cart, credit-card, table, layout-grid, refresh-cw, external-link, circle-check, list-ordered, clipboard-list, trophy, paperclip, code, cloud, chart-bar). Skip keys already in the map (list, image, plus, minus, check, x, settings, users, puzzle, trash-2, log-out, user-plus, circle-alert, tags, archive, circle, search, user).
|
|
|
|
Add every remaining winterIcons alias from the closed table. Do not invent names outside that table. Do not map a Winter name onto a lucide export that is not in the declare-const list above (in particular not CircleHelp). Keep Square as the fallback. Keep named imports only.
|
|
|
|
Extend admin/tests/app/icons.test.ts: keep the Task 1 BM/pencil cases; add Quizzes icon-question-circle and icon-list; add at least icon-home, icon-magic, icon-life-ring, icon-sitemap, icon-envelope-o, icon-copy, icon-leaf, icon-cogs, icon-edit; keep the loop over Object.entries(winterIcons); keep the unknown/empty/inherited fallback cases.
|
|
|
|
Do not edit docs/ or plugin navigation in other repos. Do not add Font Awesome or any package to admin/package.json.
|
|
|
|
Then from the repository root run npm --prefix admin run typecheck, npm --prefix admin test -- tests/app/icons.test.ts, npm --prefix admin run build (writes modules/boardwalk/dist), and scripts/check-admin-dist.sh. If dist hashes change, stage the new assets and any removed old hashed files.
|
|
|
|
Make ONE commit containing only admin/src/app/icons.ts, admin/tests/app/icons.test.ts, and modules/boardwalk/dist/ (index.html plus assets). Message: fix(admin): map Winter icon-* names onto lucide for plugin navigation. No co-author line and no session attribution trailer. Do not stage .planning files or other dirty files.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && npm --prefix admin run typecheck && npm --prefix admin test -- tests/app/icons.test.ts && scripts/check-admin-dist.sh && bash -c 'set -e; msg=$(git log -1 --format=%B); files=$(git show --name-only --format= HEAD); ! grep -qi -e co-authored-by -e claude-session <<< "$msg"; echo "$files" | grep -q "admin/src/app/icons.ts"; echo "$files" | grep -q "admin/tests/app/icons.test.ts"; echo "$files" | grep -q "modules/boardwalk/dist/"'</automated>
|
|
</verify>
|
|
<done>Quizzes and the closed Winter table resolve through iconFor to lucide components; unknown names remain Square; typecheck, icons tests and check-admin-dist.sh pass; HEAD is a single code commit of icons source, tests and the dist tree, with no co-author or session trailer and no planning files.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| admin API navigation/settings JSON -> SPA iconFor | Plugin-supplied icon strings are untrusted labels; they must never become arbitrary JS object members or extra network loads |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|----------|-------------|-----------------|
|
|
| T-qvk-01 | Tampering / elevation via prototype keys | iconFor / own() | medium | mitigate | Keep Object.hasOwn lookup; tests continue to assert constructor, toString and __proto__ return Square |
|
|
| T-qvk-02 | Information disclosure | lucide named imports | low | mitigate | Named imports only so the bundle does not include the entire @lucide/vue set; no Font Awesome webfont fetch |
|
|
| T-qvk-SC | Tampering | npm/pip/cargo/go installs | low | accept | No new package; only additional named exports from the already-pinned @lucide/vue 1.17.0 |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
- npm --prefix admin test -- tests/app/icons.test.ts passes, including BM names, Quizzes names, icon-pencil, inherited-key fallback, and the winterIcons-to-icons loop.
|
|
- npm --prefix admin run typecheck passes.
|
|
- scripts/check-admin-dist.sh reports the committed dist matches a fresh Vite build.
|
|
- git show HEAD lists only icons.ts, icons.test.ts and modules/boardwalk/dist/; the message has no co-author or session trailer.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- BM Studies side-menu Winter names and Quizzes icon-question-circle / icon-list render lucide glyphs instead of empty squares once the binary embeds the new dist.
|
|
- Documented icon-pencil examples become true without editing docs.
|
|
- Unknown Winter or lucide names still show Square.
|
|
- One framework commit; no plugin-repo changes.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/quick/261005-qvk-fix-icons-sm-bm-plugin-tried-to-apply-ic/261005-qvk-SUMMARY.md` when done
|
|
</output>
|