Record the Tabler pack choice, icon conventions, and the per-file glyph-to-icon mapping for the unicode-to-solid-icons migration.
10 KiB
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, 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 <svg> |
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) andTbFill*(filled). Always useTbOutline*.
Conventions
import { TbOutlineFolderOpen, TbOutlineX } from "solid-icons/tb";
<TbOutlineFolderOpen size={16} class="text-gray-400" />
size?: string | numberdefaults to"1em";colordefaults tocurrentColor. 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 aflex/inline-flexcontainer don't need it. - Fixed-size buttons get an explicit
size(16forw-7 h-7/w-8 h-8,14forw-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 frontmattericon: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 toTbOutlineFile.CompletionItem.icon(src/components/journal/command-parser.ts) — an optionalIconTypesrendered byCompletionsDropdown.tsxbefore 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 | <Dynamic component={docIcon(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 <Dynamic component={icon} />
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.mdnow says "点击骰子图标"). Remaining emoji insrc/doc-entries/*.mdare illustrative — coloured status dots (🟢🟡🔴⚪injournal-gm.md) — not UI icons. - CLI console / MCP output (
✅ ❌ 📦insrc/cli/**) keeps its emoji: it is terminal text, not rendered UI. command-completions.tsCompletionItemnow carries an optionalicon; any future completion source can use it.