Phosphor icons: resolve by name via a generated map (T-314)

Replace the 49 hand-maintained named consts with one generated
label→codepoint map (phosphor_glyphs.g.dart, 1512 glyphs from the glyph
table via tool/gen_phosphor_glyphs.dart). Feature code now references
glyphs by their exact kebab-case name — PhosphorIcons.byName('folder') —
with no raw codepoints; this also lets a Lua extension name an icon
without crossing the FFI boundary with a codepoint.

byName is total: an unknown name degrades to the `placeholder` box so the
bug is visible (it's a real error), while phosphor_glyphs_test asserts
every byName('...') literal in lib/ resolves — recovering the typo check a
const gave. Migrated the 89 call sites. Adds EmptyIconPainter for an
intentional blank that still reserves the icon box; ClideFilterBox gains
showIcon to keep the slot aligned when blank.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 12:46:51 +02:00
co-authored by Claude Opus 4.8
parent b8d75230e8
commit 3833a41a9e
38 changed files with 1779 additions and 163 deletions
+18 -18
View File
@@ -395,7 +395,7 @@ class _RightHatContent extends StatelessWidget {
return Row(children: [
_WinBtn(icon: const PhosphorIconPainter(0xe32a), onTap: wc.minimize, tokens: tokens),
_WinBtn(icon: const PhosphorIconPainter(0xe45e), onTap: wc.toggleMaximize, tokens: tokens),
_WinBtn(icon: PhosphorIcons.xMark, onTap: wc.close, tokens: tokens, isClose: true),
_WinBtn(icon: PhosphorIcons.byName('x'), onTap: wc.close, tokens: tokens, isClose: true),
]);
}
}
@@ -452,7 +452,7 @@ class _ProjectSwitcherButton extends StatelessWidget {
children: [
ClideText(label, fontSize: 12, color: hovered ? tokens.globalForeground : tokens.chromeForeground, fontFamily: clideMonoFamily),
const SizedBox(width: 4),
ClideIcon(PhosphorIcons.caretDown, size: 8, color: tokens.chromeForeground),
ClideIcon(PhosphorIcons.byName('caret-down'), size: 8, color: tokens.chromeForeground),
],
),
);
@@ -591,7 +591,7 @@ class _RecentProjectRow extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
child: Row(
children: [
ClideIcon(PhosphorIcons.folder, size: 14, color: tokens.globalTextMuted),
ClideIcon(PhosphorIcons.byName('folder'), size: 14, color: tokens.globalTextMuted),
const SizedBox(width: 8),
Expanded(
child: Column(
@@ -608,7 +608,7 @@ class _RecentProjectRow extends StatelessWidget {
muted: true, fontSize: 12, fontFamily: clideMonoFamily, maxLines: 1, overflow: TextOverflow.ellipsis),
),
ClideText(' · ', muted: true, fontSize: 12),
ClideIcon(PhosphorIcons.gitBranch, size: 10, color: tokens.globalTextMuted),
ClideIcon(PhosphorIcons.byName('git-branch'), size: 10, color: tokens.globalTextMuted),
const SizedBox(width: 3),
ClideText(project.branch!, muted: true, fontSize: 12, fontFamily: clideMonoFamily),
],
@@ -920,7 +920,7 @@ class _RevealedTab extends StatelessWidget {
tooltip: 'Close',
builder: (_, hovered, __) => Padding(
padding: const EdgeInsets.all(6),
child: ClideIcon(PhosphorIcons.xMark, size: 12, color: hovered ? tokens.globalForeground : tokens.globalTextMuted),
child: ClideIcon(PhosphorIcons.byName('x'), size: 12, color: hovered ? tokens.globalForeground : tokens.globalTextMuted),
),
),
),
@@ -1088,20 +1088,20 @@ class _BottomRail extends StatelessWidget {
if (t.icon is ClideIconPainter) return t.icon as ClideIconPainter;
if (slot == Slots.sidebar) {
return switch (t.id) {
'files.tree' => PhosphorIcons.folder,
'git.panel' => PhosphorIcons.gitBranch,
'pql.panel' => PhosphorIcons.magnifyingGlass,
'problems.panel' => PhosphorIcons.warningCircle,
'decisions.panel' => PhosphorIcons.lightbulb,
'tickets.panel' => PhosphorIcons.ticket,
_ => PhosphorIcons.circlesFour,
'files.tree' => PhosphorIcons.byName('folder'),
'git.panel' => PhosphorIcons.byName('git-branch'),
'pql.panel' => PhosphorIcons.byName('magnifying-glass'),
'problems.panel' => PhosphorIcons.byName('warning-circle'),
'decisions.panel' => PhosphorIcons.byName('lightbulb'),
'tickets.panel' => PhosphorIcons.byName('ticket'),
_ => PhosphorIcons.byName('circles-four'),
};
}
return switch (t.id) {
'markdown.viewer' => PhosphorIcons.eye,
'graph.view' => PhosphorIcons.graph,
'pql.backlinks' => PhosphorIcons.link,
_ => PhosphorIcons.circlesFour,
'markdown.viewer' => PhosphorIcons.byName('eye'),
'graph.view' => PhosphorIcons.byName('graph'),
'pql.backlinks' => PhosphorIcons.byName('link'),
_ => PhosphorIcons.byName('circles-four'),
};
}
}
@@ -1136,8 +1136,8 @@ class StatusbarCollapseToggle extends StatelessWidget {
// The chevron points the DIRECTION OF THE ACTION: collapsing tucks the pane
// toward its own edge, expanding brings it back toward the centre.
final icon = _isSidebar
? (collapsed ? PhosphorIcons.caretLineRight : PhosphorIcons.caretLineLeft)
: (collapsed ? PhosphorIcons.caretLineLeft : PhosphorIcons.caretLineRight);
? (collapsed ? PhosphorIcons.byName('caret-line-right') : PhosphorIcons.byName('caret-line-left'))
: (collapsed ? PhosphorIcons.byName('caret-line-left') : PhosphorIcons.byName('caret-line-right'));
final what = _isSidebar ? 'sidebar' : 'context panel';
return SizedBox(
width: 24,