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:
@@ -564,7 +564,7 @@ class _ClaudeComposerState extends State<ClaudeComposer> {
|
||||
onTap: () => _removeAttachment(a),
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: ClideIcon(PhosphorIcons.xMark, size: 12, color: theme.globalTextMuted),
|
||||
child: ClideIcon(PhosphorIcons.byName('x'), size: 12, color: theme.globalTextMuted),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -580,6 +580,6 @@ class _ClaudeComposerState extends State<ClaudeComposer> {
|
||||
if (a.isImage) {
|
||||
return ImageThumbnail(path: a.path, size: 44);
|
||||
}
|
||||
return ClideIcon(PhosphorIcons.fileText, size: 18, color: theme.globalTextMuted);
|
||||
return ClideIcon(PhosphorIcons.byName('file-text'), size: 18, color: theme.globalTextMuted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -902,14 +902,14 @@ class _AgentRosterRowState extends State<_AgentRosterRow> {
|
||||
children: [
|
||||
// Show / hide
|
||||
_IconButton(
|
||||
painter: isVisible ? PhosphorIcons.eye : PhosphorIcons.eyeSlash,
|
||||
painter: isVisible ? PhosphorIcons.byName('eye') : PhosphorIcons.byName('eye-slash'),
|
||||
tooltip: isVisible ? 'Hide pane' : 'Show pane',
|
||||
color: tokens.globalTextMuted,
|
||||
onTap: () => isVisible ? widget.orchestrator!.hide(managed.id) : widget.orchestrator!.show(managed.id),
|
||||
),
|
||||
// Mute / unmute
|
||||
_IconButton(
|
||||
painter: isMuted ? PhosphorIcons.eyeSlash : PhosphorIcons.eye,
|
||||
painter: isMuted ? PhosphorIcons.byName('eye-slash') : PhosphorIcons.byName('eye'),
|
||||
// NOTE: We use eye/eyeSlash as stand-ins until a dedicated speaker
|
||||
// icon is added to PhosphorIcons (no speaker codepoint yet).
|
||||
// The semantic tooltip still says mute/unmute so AT users are clear.
|
||||
@@ -919,21 +919,21 @@ class _AgentRosterRowState extends State<_AgentRosterRow> {
|
||||
),
|
||||
// Inject message
|
||||
_IconButton(
|
||||
painter: PhosphorIcons.chatCircle,
|
||||
painter: PhosphorIcons.byName('chat-circle'),
|
||||
tooltip: 'Inject message',
|
||||
color: isInjecting ? tokens.globalFocus : tokens.globalTextMuted,
|
||||
onTap: () => widget.onToggleInject(widget.member.name),
|
||||
),
|
||||
// Fork session (T-172): branch into a new pane without touching the original.
|
||||
_IconButton(
|
||||
painter: PhosphorIcons.gitBranch,
|
||||
painter: PhosphorIcons.byName('git-branch'),
|
||||
tooltip: 'Fork session',
|
||||
color: tokens.globalTextMuted,
|
||||
onTap: () => widget.onFork(widget.member.name),
|
||||
),
|
||||
// Close session
|
||||
_IconButton(
|
||||
painter: PhosphorIcons.xMark,
|
||||
painter: PhosphorIcons.byName('x'),
|
||||
tooltip: 'Close session',
|
||||
color: tokens.globalTextMuted,
|
||||
onTap: () => widget.onClose(widget.member.name),
|
||||
@@ -958,7 +958,7 @@ class _AgentRosterRowState extends State<_AgentRosterRow> {
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
_IconButton(
|
||||
painter: PhosphorIcons.xMark,
|
||||
painter: PhosphorIcons.byName('x'),
|
||||
tooltip: 'Cancel',
|
||||
color: tokens.globalTextMuted,
|
||||
onTap: () => widget.onToggleInject(widget.member.name),
|
||||
@@ -1186,7 +1186,7 @@ class _TaskRow extends StatelessWidget {
|
||||
// Reassign: cycle to the next roster member.
|
||||
if (broker != null && broker!.members.length > 1)
|
||||
_IconButton(
|
||||
painter: PhosphorIcons.arrowClockwise,
|
||||
painter: PhosphorIcons.byName('arrow-clockwise'),
|
||||
tooltip: 'Reassign task',
|
||||
color: tokens.globalTextMuted,
|
||||
onTap: () => _reassign(context),
|
||||
|
||||
@@ -288,7 +288,7 @@ class _ConversationCardState extends State<ConversationCard> {
|
||||
builder: (_, hovered, pressed) => Padding(
|
||||
padding: const EdgeInsets.only(right: 6),
|
||||
child: ClideIcon(
|
||||
_collapsed ? PhosphorIcons.caretRight : PhosphorIcons.caretDown,
|
||||
_collapsed ? PhosphorIcons.byName('caret-right') : PhosphorIcons.byName('caret-down'),
|
||||
size: 12,
|
||||
color: tokens.globalTextMuted,
|
||||
),
|
||||
|
||||
@@ -535,7 +535,7 @@ class _ConversationTurn extends StatelessWidget {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ClideIcon(PhosphorIcons.image, size: 16, color: tokens.globalTextMuted),
|
||||
ClideIcon(PhosphorIcons.byName('image'), size: 16, color: tokens.globalTextMuted),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: ClideText('could not load $path', fontSize: clideFontMeta, color: tokens.globalTextMuted, maxLines: 1),
|
||||
|
||||
@@ -296,7 +296,7 @@ class ClaudeExtension extends ClideExtension {
|
||||
id: 'claude.meta',
|
||||
slot: Slots.sidebar,
|
||||
title: 'Activity',
|
||||
icon: PhosphorIcons.robot,
|
||||
icon: PhosphorIcons.byName('robot'),
|
||||
priority: 60,
|
||||
build: (_) => const ClaudeMetaSidebar(),
|
||||
),
|
||||
|
||||
@@ -36,7 +36,7 @@ Widget _placeholder(BuildContext context, double size) {
|
||||
height: size,
|
||||
color: t.panelBackground,
|
||||
alignment: Alignment.center,
|
||||
child: ClideIcon(PhosphorIcons.image, size: size * 0.45, color: t.globalTextMuted),
|
||||
child: ClideIcon(PhosphorIcons.byName('image'), size: size * 0.45, color: t.globalTextMuted),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@ import 'package:flutter/widgets.dart';
|
||||
ClideIconPainter permissionModeIcon(String mode) {
|
||||
switch (mode) {
|
||||
case 'acceptEdits':
|
||||
return PhosphorIcons.pencilSimple;
|
||||
return PhosphorIcons.byName('pencil-simple');
|
||||
case 'plan':
|
||||
return PhosphorIcons.listChecks;
|
||||
return PhosphorIcons.byName('list-checks');
|
||||
case 'bypassPermissions':
|
||||
return PhosphorIcons.shieldWarning;
|
||||
return PhosphorIcons.byName('shield-warning');
|
||||
default:
|
||||
return PhosphorIcons.shieldCheck;
|
||||
return PhosphorIcons.byName('shield-check');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ class _TeamChatSidebarState extends State<TeamChatSidebar> {
|
||||
builder: (ctx, hovered, _) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 1),
|
||||
child: ClideIcon(
|
||||
PhosphorIcons.arrowsOutSimple,
|
||||
PhosphorIcons.byName('arrows-out-simple'),
|
||||
size: 10,
|
||||
color: hovered ? tokens.globalForeground : tokens.globalTextMuted,
|
||||
),
|
||||
@@ -383,7 +383,7 @@ class _TeamChatPaneState extends State<TeamChatPane> {
|
||||
),
|
||||
child: _interrupt
|
||||
? Center(
|
||||
child: ClideIcon(PhosphorIcons.check, size: 9, color: tokens.globalFocus),
|
||||
child: ClideIcon(PhosphorIcons.byName('check'), size: 9, color: tokens.globalFocus),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user