From 49bed7d2797012f1678114ebc571d69d3ffb5d23 Mon Sep 17 00:00:00 2001 From: hypercross Date: Sun, 13 Sep 2026 14:01:10 +0800 Subject: [PATCH] docs: add icon migration reference Record the Tabler pack choice, icon conventions, and the per-file glyph-to-icon mapping for the unicode-to-solid-icons migration. --- docs/icon-migration.md | 191 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 docs/icon-migration.md diff --git a/docs/icon-migration.md b/docs/icon-migration.md new file mode 100644 index 0000000..fa271e9 --- /dev/null +++ b/docs/icon-migration.md @@ -0,0 +1,191 @@ +# Icon migration — unicode/emoji → `solid-icons` + +**Status: implemented.** This document is both the audit and the record of the +migration. It lists the pack/conventions used and the per-file mapping. + +## Goal + +Replace ad-hoc unicode symbols and emoji in **UI chrome** with real SVG icon +components from [`solid-icons`](https://solid-icons.vercel.app), so icons are +consistent, themeable (`currentColor`, `size`), and render identically across +platforms (emoji fonts vary wildly between OSes). + +## Scope + +**In scope — UI chrome** (`src/components/**`, `src/App.tsx`, doc-entry +frontmatter, `RevealManager` inline SVGs, the editor-panel align PNGs). + +**Out of scope — left as-is:** + +| Thing | Why | +|---|---| +| `:[icon-name]` content directive (`src/markdown/icon.ts`, `assets/`) | Game-content icons resolved from user `assets/` dirs via CSS `background-image`. Not UI chrome. | +| CLI console output (`✅ ❌ 📦` in `src/cli/**`) | Terminal output, not a browser. `solid-icons` is a SolidJS/browser library. | +| Emoji in `README.md`, `docs/*.md` prose | Documentation text, not rendered UI. | +| Dimension text `×` in `PltPreview` / `PrintPreviewHeader` | A multiplication sign in "100mm × 50mm", not an icon. | +| `content/**` sample content | User-authored data. | +| `PltPreview.tsx` / `PrintPreview.tsx` `` | Rendered card/page artwork, not icons. | + +## Pack + +**Tabler** (`solid-icons/tb`). `solid-icons@1.2.0` does **not** ship Lucide; the +available packs are `ai, bi, bs, cg, fa, fi, hi, im, io, md, oc, ri, si, tb, ti, +vs, wi`. Tabler was chosen because it is stroke-based, uses `currentColor`, and +covers everything needed (dice, ruler, grip, alignment, arrows, …). + +> Exports are prefixed `TbOutline*` (outline) and `TbFill*` (filled). Always use +> `TbOutline*`. + +## Conventions + +```tsx +import { TbOutlineFolderOpen, TbOutlineX } from "solid-icons/tb"; + + +``` + +- `size?: string | number` defaults to `"1em"`; `color` defaults to + `currentColor`. Icons therefore inherit the surrounding font-size and text + color, so existing Tailwind text-color/size classes keep working. +- **Inline alignment:** an inline SVG sits on the text baseline, so icons placed + inline next to text get `class="inline-block align-[-0.125em]"`. Icons inside + a `flex`/`inline-flex` container don't need it. +- **Fixed-size buttons** get an explicit `size` (`16` for `w-7 h-7`/`w-8 h-8`, + `14` for `w-5 h-5`/`w-6 h-6`). +- Text labels next to a glyph (e.g. `📥 导出 PDF`) are preserved; only the glyph + is replaced. + +## Shared modules + +- **`src/components/doc-icons.ts`** — maps doc-entry frontmatter `icon:` keys + (e.g. `dice`, `table`) to Tabler components. Frontmatter is YAML, so it can't + hold a component; it stores a semantic key instead. `docIcon(key)` falls back + to `TbOutlineFile`. +- **`CompletionItem.icon`** (`src/components/journal/command-parser.ts`) — an + optional `IconTypes` rendered by `CompletionsDropdown.tsx` before the label, + so completion entries carry real icons instead of emoji in the label string. + +## Inventory + +### Shell / navigation + +| File | Glyph | Purpose | Icon | +|---|---|---|---| +| `src/App.tsx` | `☰` | mobile menu | `TbOutlineMenu2` | +| `src/App.tsx` | `📋` | journal panel toggle | `TbOutlineClipboardList` | +| `src/App.tsx` | `?` | docs/help | `TbOutlineHelpCircle` | +| `src/components/Sidebar.tsx` | `📂` | content source | `TbOutlineFolderOpen` | +| `src/components/Sidebar.tsx` | `✕` | close sidebar | `TbOutlineX` | +| `src/components/FileTree.tsx` | `📂` / `📁` | folder open / closed | `TbOutlineFolderOpen` / `TbOutlineFolder` | +| `src/components/FileTree.tsx` | `📄` | file | `TbOutlineFile` | +| `src/components/FileTree.tsx` | `▶` | expand chevron | `TbOutlineChevronRight` | +| `src/components/DocDialog.tsx` | `▾` | dropdown caret | `TbOutlineChevronDown` | +| `src/components/DocDialog.tsx` | `📖` | directives doc set | `TbOutlineBook2` | +| `src/components/DocDialog.tsx` | `✓` | selected doc set | `TbOutlineCheck` | +| `src/components/DocDialog.tsx` | `📋` | journal doc set | `TbOutlineClipboardList` | +| `src/components/DocDialog.tsx` | `✕` | close dialog | `TbOutlineX` | +| `src/components/DocDialog.tsx` | entry `icon:` | doc entry icon | `` | + +### md-* components + +| File | Glyph | Purpose | Icon | +|---|---|---|---| +| `src/components/md-dice.tsx` | `🎲` | dice reset | `TbOutlineDice5` | +| `src/components/md-table.tsx` | `🎲` | random row | `TbOutlineDice5` | +| `src/components/md-pins.tsx` | `📋` | copy pin coords | `TbOutlineCopy` | +| `src/components/md-yarn-spinner.tsx` | `🔄` | restart | `TbOutlineRotate` | +| `src/components/md-yarn-spinner.tsx` | `⏩` | advance | `TbOutlinePlayerTrackNext` | +| `src/components/md-yarn-spinner.tsx` | `→` | option bullet | `TbOutlineArrowRight` | + +### Journal + +| File | Glyph | Purpose | Icon | +|---|---|---|---| +| `src/components/journal/ConnectDialog.tsx` | `🗑` | delete session | `TbOutlineTrash` | +| `src/components/journal/CreateSessionDialog.tsx` | `✕` | close | `TbOutlineX` | +| `src/components/journal/DeleteSessionDialog.tsx` | `✕` | close | `TbOutlineX` | +| `src/components/journal/ErrorPopup.tsx` | `✕` | close | `TbOutlineX` | +| `src/components/journal/InviteDialog.tsx` | `✕` | close | `TbOutlineX` | +| `src/components/journal/JournalHeader.tsx` | `←` | back to session list | `TbOutlineArrowLeft` | +| `src/components/journal/JournalHeader.tsx` | `▸` | hide panel | `TbOutlineLayoutSidebarRightCollapse` | +| `src/components/journal/SessionDropdown.tsx` | `▾` | dropdown caret | `TbOutlineChevronDown` | +| `src/components/journal/SessionDropdown.tsx` | `✓` | active session | `TbOutlineCheck` | +| `src/components/journal/SessionDropdown.tsx` | `🗑` | delete session | `TbOutlineTrash` | +| `src/components/journal/StreamMessage.tsx` | `×` | revert message | `TbOutlineArrowBackUp` | +| `src/components/journal/types/intent.tsx` | `🙋` | intent | `TbOutlineHandStop` | +| `src/components/journal/types/intent.tsx` | `✅` | verdict | `TbOutlineCircleCheck` | +| `src/components/journal/types/link.tsx` | `📄` | link | `TbOutlineFileText` | +| `src/components/journal/types/roll.tsx` | `🎲` | roll | `TbOutlineDice5` | +| `src/components/journal/types/roll.tsx` | `→` | notation → total | `TbOutlineArrowRight` | +| `src/components/journal/types/var.tsx` | `🔢` | variable | `TbOutlineHash` | +| `src/components/journal/command-completions.ts` | `🎲` | dice completion | `TbOutlineDice5` (via `item.icon`) | +| `src/components/journal/command-completions.ts` | `✨` | spark completion | `TbOutlineSparkles` (via `item.icon`) | + +### md-deck + +| File | Glyph | Purpose | Icon | +|---|---|---|---| +| `src/components/md-deck/DeckHeader.tsx` | `✓` / `✏️` | editing toggle | `TbOutlineCheck` / `TbOutlinePencil` | +| `src/components/md-deck/DeckHeader.tsx` | `📥` | export PDF | `TbOutlineDownload` | +| `src/components/md-deck/DeckHeader.tsx` | `📋` | copy code | `TbOutlineCopy` | +| `src/components/md-deck/PltPreview.tsx` | `📥` | download PLT | `TbOutlineDownload` | +| `src/components/md-deck/PrintPreviewFooter.tsx` | `❌` | export error | `TbOutlineCircleX` | +| `src/components/md-deck/PrintPreviewFooter.tsx` | `✕` | close | `TbOutlineX` | +| `src/components/md-deck/PrintPreviewHeader.tsx` | `➕` | add deck | `TbOutlinePlus` | +| `src/components/md-deck/PrintPreviewHeader.tsx` | `📐` | PLT preview | `TbOutlineRuler` | +| `src/components/md-deck/PrintPreviewHeader.tsx` | `📥` | export PDF | `TbOutlineDownload` | +| `src/components/md-deck/CardList.tsx` | `‹` | previous page | `TbOutlineChevronLeft` | +| `src/components/md-deck/CardList.tsx` | `›` | next page | `TbOutlineChevronRight` | +| `src/components/md-deck/editor-panel/LayerRow.tsx` | `⠿` | drag handle | `TbOutlineGripVertical` | +| `src/components/md-deck/editor-panel/LayerRow.tsx` | `👁` / `👁‍🗨` | visible / hidden | `TbOutlineEye` / `TbOutlineEyeOff` | +| `src/components/md-deck/editor-panel/LayerRow.tsx` | `↑ → ↓ ←` | orientation | `TbOutlineArrowUp` / `TbOutlineArrowRight` / `TbOutlineArrowDown` / `TbOutlineArrowLeft` | +| `src/components/md-deck/editor-panel/LayerRow.tsx` | `↖ ↑ ↗ ↙ ↓ ↘` | alignment | `TbOutlineArrowUpLeft` / `TbOutlineArrowUp` / `TbOutlineArrowUpRight` / `TbOutlineArrowDownLeft` / `TbOutlineArrowDown` / `TbOutlineArrowDownRight` | +| `src/components/md-deck/editor-panel/LayerRow.tsx` | `✕` | delete layer | `TbOutlineTrash` | +| `src/components/md-deck/editor-panel/LayerRow.tsx` | `align-*.png` | align presets | `TbOutlineAlignLeft` / `TbOutlineAlignCenter` / `TbOutlineAlignRight` | + +The three `align-*.png` assets were deleted; the `icons/` directory is gone. + +### `RevealManager.tsx` (formerly inline SVG strings) + +| Former | Purpose | Icon | +|---|---|---| +| `LINK_SVG` | floating `/link` action | `TbOutlineLink` | +| `SPARK_SVG` | floating `/roll` spark action | `TbOutlineSparkles` | + +`FloatingButton.svg: string` (rendered via `innerHTML`) became +`FloatingButton.icon: IconTypes`, rendered as `` +children. + +### `src/doc-entries/*.md` frontmatter `icon:` + +| File / tag | Key | +|---|---| +| `journal-gm.md` | `gm` | +| `journal-link.md` | `link` | +| `journal-player.md` | `player` | +| `journal-roll.md` | `roll` | +| `journal-set.md` | `set` | +| `journal-spark.md` | `spark` | +| `md-bg.md` | `background` | +| `md-border.md` | `border` | +| `md-deck.md` | `deck` | +| `md-dice.md` | `dice` | +| `md-embed.md` | `embed` | +| `md-font.md` | `font` | +| `md-link.md` | `link` | +| `md-pins.md` | `pins` | +| `md-table.md` | `table` | +| `md-yarn-spinner.md` | `yarn` | + +Keys are resolved through `docIcon()` in `src/components/doc-icons.ts`. + +## Follow-ups / not migrated (by design) + +- Doc-entry **body prose** that referenced a replaced button glyph was reworded + to name the button (e.g. `md-dice.md` now says "点击骰子图标"). Remaining emoji + in `src/doc-entries/*.md` are illustrative — coloured status dots + (`🟢🟡🔴⚪` in `journal-gm.md`) — not UI icons. +- CLI console / MCP output (`✅ ❌ 📦` in `src/cli/**`) keeps its emoji: it is + terminal text, not rendered UI. +- `command-completions.ts` `CompletionItem` now carries an optional `icon`; any + future completion source can use it.