consolidate theme-ui into ui-design skill (T-85)

Replaces the single-file theme-ui skill with a routed ui-design skill
backed by four references:

- theme.md      — token system, identity rule, palette layers, type
- surface.md    — token selection per surface (chrome, panels, tabs,
                  buttons, status, overlays)
- geometry.md   — control spacing/alignment principles distilled from
                  the MultitabPane work: uniform inner spacing rule,
                  no double-edge padding, two-column control pattern,
                  perceived mass over measured pixels
- icons.md      — Phosphor icons + clide-owned painters

SKILL.md routes to the right reference and holds the universal rules.
The trigger description widens to cover spacing/alignment questions
in addition to token selection.

geometry.md references T-86 (codify spacing constants); the doc uses
literal pixel values until those constants land.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-06 15:38:19 +02:00
co-authored by Claude
parent 71b21e1974
commit 29ac9b3caf
8 changed files with 543 additions and 212 deletions
-209
View File
@@ -1,209 +0,0 @@
---
name: theme-ui
description: >-
Token selection guide for clide UI development. Use when building or
modifying widgets, panels, pane chrome, status indicators, icons, or
any visual surface. Ensures correct background, border, text, and hover
tokens are applied per surface type. Triggers on: new widget code,
theme-related changes, "which token", "what color", color/background
questions, visual inconsistency fixes, new panel/pane/view development,
adding or looking up Phosphor icons, icon codepoints.
---
# Theme-UI — token selection for clide surfaces
All colors come from `SurfaceTokens` via `ClideTheme.of(context).surface`.
Never hardcode colors. Never use Material/Cupertino color constants.
## Token selection by surface
Pick tokens based on **where** the widget lives, not what it does.
### Chrome (hat bar, status bar, sidebar, context panel, spines, drag handles)
```
background → chromeBackground
text → chromeForeground
border → chromeBorder (1px)
active text → globalForeground
```
### Side panels (sidebar, context panel)
```
background → chromeBackground (both sides — they're chrome frame)
text → sidebarForeground
hover → sidebarItemHover
selected → sidebarItemSelected
section head → sidebarSectionHeader (muted, used for "START", "FILES", etc.)
```
Padding: 2px on outer edges, 0px on divider edge.
### Center column (workspace, Claude pane, editor)
```
background → panelBackground
text → globalForeground
```
No padding — content fills edge to edge.
### Pane headers (`ClidePaneChrome`)
```
background → panelHeader
text (title) → panelHeaderForeground
text (sub) → globalTextMuted
```
### List items (decisions, tickets, file rows, backlinks)
```
background → (none / transparent)
hover bg → listItemHoverBackground
selected bg → listItemSelectedBackground
text → listItemForeground / sidebarForeground (in sidebar)
selected txt → listItemSelectedForeground
```
In sidebar context, use `sidebarItemHover` not `listItemHoverBackground`.
### Buttons
```
normal → buttonBackground / buttonForeground / buttonBorder
hover → buttonHoverBackground
active → buttonActiveBackground
primary → buttonActiveBackground bg + globalBackground text
subtle → listItemBackground / listItemHoverBackground (no border)
```
### Dividers and separators
```
line → dividerColor (always, everywhere)
drag handle → 8px hit area, 1px visible line, panel bg fill
hover line → panelActiveBorder
```
### Status indicators
```
success/ok → statusSuccess (green: done, added, connected)
warning → statusWarning (amber: question, modified, missing)
error → statusError (red: deleted, rejected, cancelled)
info → statusInfo (blue: in_progress, modified)
```
Map semantic states, not visual styles:
- `done` / `added` / `ok``statusSuccess`
- `in_progress` / `modified``statusInfo`
- `question` / `warning``statusWarning`
- `cancelled` / `deleted` / `error``statusError`
### Overlays (dialogs, palette, tooltips)
```
dialog bg → modalSurfaceBackground
dialog border→ modalSurfaceBorder
backdrop → modalOverlayBackground
tooltip → tooltipBackground / tooltipForeground / tooltipBorder
dropdown → dropdownBackground / dropdownForeground / dropdownBorder
```
## Typography
Three constants — never hardcode sizes or families.
```
family UI → inherited from DefaultTextStyle (JosefinSans Light 300)
family mono → clideMonoFamily (JetBrainsMono)
body size → clideFontBody (15)
caption size → clideFontCaption (14) — status bar, section headers, git info
mono size → clideFontMono (14) — terminal, code, paths, IDs
```
Use `ClideText` for themed text. Set `muted: true` for secondary text
(resolves to `globalTextMuted`). Set `fontFamily: clideMonoFamily` for
code/paths/IDs. Don't set fontFamily for UI text — it inherits.
## Token identity rule
Every visual surface gets its own named token. Never borrow a token from
another surface just because they happen to resolve to the same color.
**Wrong:** `sidebarBackground` for the hat bar (the hat isn't a sidebar).
**Right:** Create `chromeBackground` that resolves to the same palette key.
When two surfaces share a color:
1. **If they're the same conceptual surface** (sidebar + context panel are
both "side panels") → one shared token set is fine.
2. **If they're different surfaces that happen to match** (hat bar + sidebar
+ status bar are all "chrome frame") → create a shared primitive in the
palette/semantic layer (e.g. `bgChrome`) and give each surface its own
token that maps to that primitive. This lets themes diverge them later.
The palette layer has these depth primitives:
- `bg` (`#20202C`) — outermost root, behind everything
- `bgSunken` (`#1A1A24`) — chrome frame: sidebar, hat, statusbar
- `surface` (`#242838`) — elevated: pane headers, active tabs
- `surfaceHi` (`#2C3046`) — interactive: hover states, selections
Chrome tokens (`chromeBackground`/`chromeForeground`/`chromeBorder`) are
the shared root for all frame surfaces. They resolve to `bgSunken` /
`textDim` / `border` in the palette. Themes can override them to diverge
hat from sidebar from status bar if desired.
## Extension-owned domain colors
Extensions that need domain-specific color coding (ticket types, decision
types, priority levels) should NOT add tokens to `SurfaceTokens`. Instead:
1. Create a color map class in the extension (e.g. `TicketTypeColors`)
2. Ship dark and light presets, auto-selected via `ClideTheme.of(context).dark`
3. Store user overrides under `ext.<id>.colors` in settings
4. Reference: `lib/builtin/tickets/src/ticket_colors.dart`
This keeps the core token surface lean and lets each extension own its
palette. The pattern scales to any extension needing domain colors.
## Icons — Phosphor Icons
The app bundles Phosphor Icons (v2.0.8, MIT) as TTF fonts at
`assets/fonts/phosphor/` (regular, bold, fill weights).
**Codepoint reference:** `assets/fonts/phosphor/codepoints.csv`
full mapping of all 1512 icon codepoints to kebab-case and PascalCase
names. Read this file to look up any icon by name or codepoint.
**Adding an icon:** find the codepoint in `codepoints.csv`, then add
a `static const` entry to `PhosphorIcons` in
`lib/widgets/src/icons/phosphor.dart`:
```dart
static const arrowClockwise = PhosphorIconPainter(0xe036);
```
Only add icons we actually use — don't bulk-import the full set.
**Using an icon:** `ClideIcon(PhosphorIcons.arrowClockwise, size: 13)`
or as a `TabContribution` icon field: `icon: PhosphorIcons.lightbulb`.
**Bold weight:** pass `family: 'Phosphor-Bold'` to `PhosphorIconPainter`.
Fill weight: `family: 'Phosphor-Fill'`.
## Anti-patterns
- Borrowing another surface's token (`sidebarBackground` for hat bar)
- `globalBackground` for panel fill → use `panelBackground`
- `listItemHoverBackground` in sidebar → use `sidebarItemHover`
- Hardcoded `Color(0xFF...)` → use a token
- `fontSize: 14` → use `clideFontCaption` or `clideFontMono`
- `fontFamily: 'JetBrainsMono'` → use `clideMonoFamily`
## Reference
Full token list: `lib/kernel/src/theme/tokens.dart`
Resolver fallbacks: `lib/kernel/src/theme/resolver.dart`
Theme YAML example: `lib/kernel/src/theme/themes/clide.yaml`
+51
View File
@@ -0,0 +1,51 @@
---
name: ui-design
description: >-
Visual design guide for clide UI development — covers theme tokens,
surface-specific token selection, control geometry/spacing/alignment,
and Phosphor icons. Use when building or modifying widgets, panels,
pane chrome, status indicators, tabs, list items, dialogs, or any
visual surface. Triggers on: new widget code, theme-related changes,
"which token", "what color", color/background questions, visual
inconsistency fixes, "alignment off", "spacing", "padding", control
geometry questions, new panel/pane/view development, adding or
looking up Phosphor icons, icon codepoints.
---
# UI design — clide visual surface guide
This skill bundles four concerns that all surface in widget work:
| Concern | Reference | When to read |
|---------|-----------|--------------|
| Token system, palette, typography | [`references/theme.md`](references/theme.md) | Designing or extending the theme pipeline; deciding whether to add a new token |
| Token selection per surface | [`references/surface.md`](references/surface.md) | Building a new widget or modifying an existing one — "which token does this need" |
| Spacing, alignment, control layout | [`references/geometry.md`](references/geometry.md) | Building tab strips, list items, buttons, anything where icons sit next to text or padded edges |
| Phosphor icon usage and codepoints | [`references/icons.md`](references/icons.md) | Adding or referencing an icon |
Read the reference that matches the question. They cross-reference each
other where relevant; you don't need to read all four.
## Universal rules
These apply across every reference and every surface:
- All colors come from `SurfaceTokens` via `ClideTheme.of(context).surface`.
Never hardcode `Color(0xFF...)`.
- Never use `Material*` or `Cupertino*` widgets or color constants — clide
is `WidgetsApp` only (D-7).
- Use `ClideText` for themed text; never bare `Text` in production widgets.
- Typography: `clideFontMono` for code/paths/IDs, `clideFontCaption` for
status/section headers, body inherits from `DefaultTextStyle`.
## Anti-patterns (cross-cutting)
- Borrowing another surface's token (`sidebarBackground` for hat bar) — give
each surface its own token even if they share a palette key. See `theme.md`.
- Hardcoded hex colors → use a token. See `surface.md` for which one.
- `fontSize: 14` literal → use `clideFontCaption` or `clideFontMono`.
- `fontFamily: 'JetBrainsMono'` literal → use `clideMonoFamily`.
- Stacking edge padding on a padded parent + a padded child action → see
`geometry.md` "no double edge padding".
- Eyeballing pixel margins instead of working back from the constraint —
the math matters; see `geometry.md` "uniform inner spacing".
@@ -0,0 +1,170 @@
# Geometry — spacing, alignment, control layout
Principles for placing icons, buttons, and text inside controls.
Apply when building tab strips, list items, buttons with affordances,
or anything where actions sit next to content.
> Numeric values in this doc will move to `ClideSpacing` constants
> (`lib/widgets/src/spacing.dart`) — see T-86. Until then, the
> constants used in the codebase: `12` (text inset), `6` (icon
> breathing), `8` (standard gap), `4` (tight gap), `16` (icon hit
> area), `28` (button / row height).
## Uniform inner spacing rule
Icons inside control surfaces should have **equal margin on every
constrained side**. The "constrained sides" are top, bottom, and the
side opposite to where content flows in.
The remaining side — where the text or other content sits — gets a
larger, content-appropriate breathing room.
Example: tab close button (16×16 inside a 28-tall tab):
```
top : 6 ┐
bottom : 6 ├─ uniform: (28 16) / 2 = 6
right : 6 ┘
left : 8 ── content gap (separates from title text)
```
The visual effect: the close button looks like a deliberate
affordance with a calm, consistent border, not a glyph stuffed into
the corner.
## No double-edge padding
When a fixed-size action (icon button, close ×) sits at the edge of
a padded parent, the parent's padding on that edge should **not stack**
with the action's own internal margin. Pick one place to hold the
breathing room.
Wrong:
```dart
Container(
padding: EdgeInsets.symmetric(horizontal: 12), // tab pad: 12 right
child: Row(children: [
Expanded(child: title),
SizedBox(width: 8), // gap: 8
Container(width: 28, alignment: Center, // close: 6 internal margin
child: Icon(close, size: 16)),
]),
)
// Visible margin from icon right to outer right = 12 + 6 = 18px → too much
```
Right:
```dart
Container(
padding: EdgeInsets.only(left: 12, right: 6), // pad matches icon margin
child: Row(children: [
Expanded(child: title),
SizedBox(width: 8),
Container(width: 16, height: 16, alignment: Center, // hit target = icon size
child: Icon(close, size: 10)),
]),
)
// Visible margin = 6 (parent right pad) ≈ 6 (top/bottom auto) → uniform
```
## Two-column control pattern
For tab-shaped or row-shaped controls with a primary content area and
a secondary action:
```dart
Row(children: [
Expanded(child: <content>), // takes remainder
if (action != null) ...[
SizedBox(width: 8), // standard gap
<fixed-size action>, // shrinks to content
],
])
```
- **Left column**: `Expanded`, holds the primary content (title,
label, description). Aligned to the start of its space by default.
- **Right column**: fixed natural width, holds the action (close,
status, indicator). Sized to the icon, not to artificial padding.
The parent container's padding sits flush against both columns (see
"no double-edge padding").
## Match perceived mass, not measured pixels
Glyphs vary in visual weight. A bold `+` looks heavier than a thin
`×` at the same point size. When eyeballing alignment, trust the
optical center over the geometric center.
In practice: if two icons measure to the same margin but one *looks*
crowded, give the heavier glyph slightly more breathing room and
trim the lighter one. For clide, this came up with the `×` close
glyph vs the `+` add glyph — both at 14pt, but `+` reads as denser
and is left in its 28-wide button without further padding, while
`×` sits in a 16×16 hit area with 6px symmetric margin.
## Strip / row should fill the parent
Tab strips, status bars, and divider rows should span the full
parent width, not size to their content. Without this, the strip
looks like it floats inside the pane.
```dart
Column(
crossAxisAlignment: CrossAxisAlignment.stretch, // <-- this
children: [
_TabStrip(...),
Expanded(child: _body(...)),
],
)
```
Without `stretch`, Column gives loose width constraints and a
`Container(height: tabHeight)` child sizes to its child's natural
width — the strip ends mid-pane.
## Anchor strips with a divider
Add a 1px bottom border (`dividerColor`) to tab strips and any
header strip that sits above content. Without it, the strip looks
disconnected from the body and the perceived alignment slips.
```dart
Container(
height: 28,
decoration: BoxDecoration(
color: tokens.tabBarBackground,
border: Border(bottom: BorderSide(color: tokens.dividerColor)),
),
child: ...,
)
```
## Anti-patterns
- Centering a glyph inside a "hover background" that's larger than
the natural icon size, then surrounding the whole thing with a
padded parent — the icon ends up far inside the visible edge.
- Hardcoded `padding: EdgeInsets.symmetric(horizontal: 12)` on every
control regardless of whether the right edge has an action — see
"no double-edge padding".
- Tab strip inside `Column` without `crossAxisAlignment.stretch`
the strip ends mid-pane.
- `mainAxisSize.min` on the tab strip's outer Row when you actually
want it to fill parent width — only use `min` for pill-shaped
controls that should hug their content.
- Eyeballing alignment without working back from a target margin in
pixels. The math matters; see "uniform inner spacing".
## Testing alignment
When iterating on a control's spacing:
1. State the target margin (e.g. "6px around the close icon, all
sides except left").
2. Map every contributing source: parent padding, gap SizedBoxes,
container alignment offsets, icon-to-container size differences.
3. Sum them. Adjust until they hit the target.
4. Verify visually — perceived mass may justify a 12px tweak.
@@ -0,0 +1,89 @@
# Icons — Phosphor + clide-owned painters
## Phosphor Icons
The app bundles Phosphor Icons (v2.0.8, MIT) as TTF fonts at
`assets/fonts/phosphor/` (regular, bold, fill weights).
**Codepoint reference:** `assets/fonts/phosphor/codepoints.csv` — full
mapping of all 1512 icon codepoints to kebab-case and PascalCase
names. Read this file to look up any icon by name or codepoint.
### Adding an icon
Find the codepoint in `codepoints.csv`, then add a `static const`
entry to `PhosphorIcons` in `lib/widgets/src/icons/phosphor.dart`:
```dart
static const arrowClockwise = PhosphorIconPainter(0xe036);
```
Only add icons we actually use — don't bulk-import the full set.
### Using an icon
```dart
ClideIcon(PhosphorIcons.arrowClockwise, size: 13)
```
Or as a `TabContribution` icon field: `icon: PhosphorIcons.lightbulb`.
**Bold weight:** pass `family: 'Phosphor-Bold'` to `PhosphorIconPainter`.
**Fill weight:** `family: 'Phosphor-Fill'`.
## clide-owned painters
Some shapes are simple enough to paint directly without an icon
font. Hand-rolled `ClideIconPainter` subclasses live under
`lib/widgets/src/icons/`:
- `CheckIcon`, `ChevronIcon`, `CloseIcon` (`x.dart`)
- `DotIcon`, `FolderIcon`, `GearIcon`
- `GitBranchIcon`, `PlugIcon`, `SearchIcon`
- `TerminalIcon`, `WarningIcon`
Use these for tiny, theme-aware glyphs (close ×, dropdown chevrons,
status dots) where pulling in the Phosphor font weight would be
overkill or where the visual needs to match the theme's stroke
weight conventions.
Pattern for a new painter:
```dart
class FoobarIcon extends ClideIconPainter {
const FoobarIcon();
@override
void paint(Canvas canvas, Color color) {
final p = Paint()
..color = color
..strokeWidth = 0.10
..strokeCap = StrokeCap.round;
// Coordinates are 0..1 (the painter is given a unit square).
canvas.drawLine(const Offset(0.2, 0.2), const Offset(0.8, 0.8), p);
}
}
```
## Sizing
Icon sizes used in clide (subject to consolidation under
`ClideSpacing` — see T-86):
- `10` — micro: close × inside a tab
- `13` — caption-row icons (sidebar, status bar)
- `14` — standard inline icons (icon rail)
- `16` — small icon hit-target outer container
- `18``20` — emphatic / standalone icons
Pass `size:` to `ClideIcon`; the painter receives a unit-square
canvas regardless. Color defaults to `globalForeground`; pass
explicit `color:` for muted/active variants.
## Anti-patterns
- Importing all of Phosphor — only declare codepoints we use.
- Hand-painting a glyph that already exists in Phosphor at the right
weight — use the font.
- Hardcoded `Color` on icons — pass through the surface tokens
(`globalForeground`, `globalTextMuted`, `panelActiveBorder`, etc.).
@@ -0,0 +1,120 @@
# Surface — token selection per surface type
Pick tokens based on **where** the widget lives, not what it does.
## Chrome (hat bar, status bar, sidebar, context panel, spines, drag handles)
```
background → chromeBackground
text → chromeForeground
border → chromeBorder (1px)
active text → globalForeground
```
## Side panels (sidebar, context panel)
```
background → chromeBackground (both sides — they're chrome frame)
text → sidebarForeground
hover → sidebarItemHover
selected → sidebarItemSelected
section head → sidebarSectionHeader (muted, used for "START", "FILES", etc.)
```
Padding: 2px on outer edges, 0px on divider edge.
## Center column (workspace, Claude pane, editor)
```
background → panelBackground
text → globalForeground
```
No padding — content fills edge to edge.
## Pane headers (`ClidePaneChrome`)
```
background → panelHeader
text (title) → panelHeaderForeground
text (sub) → globalTextMuted
```
## Tabs (`MultitabPane`, `ClideTabBar`)
```
strip bg → tabBarBackground
strip border → bottom: dividerColor (anchors strip to body)
active fg → tabActiveForeground
inactive fg → tabInactiveForeground
active bg → panelHeader (elevated chrome)
inactive bg → tabBarBackground (blends with strip)
active border→ panelActiveBorder (top accent, 1.5px)
side border → panelBorder
```
For control geometry inside tabs (close button placement, padding,
two-column title+action layout) see [`geometry.md`](geometry.md).
## List items (decisions, tickets, file rows, backlinks)
```
background → (none / transparent)
hover bg → listItemHoverBackground
selected bg → listItemSelectedBackground
text → listItemForeground / sidebarForeground (in sidebar)
selected txt → listItemSelectedForeground
```
In sidebar context, use `sidebarItemHover` not `listItemHoverBackground`.
## Buttons
```
normal → buttonBackground / buttonForeground / buttonBorder
hover → buttonHoverBackground
active → buttonActiveBackground
primary → buttonActiveBackground bg + globalBackground text
subtle → listItemBackground / listItemHoverBackground (no border)
```
## Dividers and separators
```
line → dividerColor (always, everywhere)
drag handle → 8px hit area, 1px visible line, panel bg fill
hover line → panelActiveBorder
```
## Status indicators
```
success/ok → statusSuccess (green: done, added, connected)
warning → statusWarning (amber: question, modified, missing)
error → statusError (red: deleted, rejected, cancelled)
info → statusInfo (blue: in_progress, modified)
```
Map semantic states, not visual styles:
- `done` / `added` / `ok``statusSuccess`
- `in_progress` / `modified``statusInfo`
- `question` / `warning``statusWarning`
- `cancelled` / `deleted` / `error``statusError`
## Overlays (dialogs, palette, tooltips)
```
dialog bg → modalSurfaceBackground
dialog border→ modalSurfaceBorder
backdrop → modalOverlayBackground
tooltip → tooltipBackground / tooltipForeground / tooltipBorder
dropdown → dropdownBackground / dropdownForeground / dropdownBorder
```
## Anti-patterns
- `globalBackground` for panel fill → use `panelBackground`
- `listItemHoverBackground` in sidebar → use `sidebarItemHover`
- Tab active bg = `panelBackground` → use `panelHeader` (elevated chrome)
- Tab active border = `globalFocus` → use `panelActiveBorder`
@@ -0,0 +1,70 @@
# Theme — token system, palette, typography
## Token identity rule
Every visual surface gets its own named token. Never borrow a token from
another surface just because they happen to resolve to the same color.
**Wrong:** `sidebarBackground` for the hat bar (the hat isn't a sidebar).
**Right:** Create `chromeBackground` that resolves to the same palette key.
When two surfaces share a color:
1. **Same conceptual surface** (sidebar + context panel are both "side
panels") → one shared token set is fine.
2. **Different surfaces that happen to match** (hat bar + sidebar +
status bar are all "chrome frame") → create a shared primitive in the
palette/semantic layer (e.g. `bgChrome`) and give each surface its
own token that maps to that primitive. This lets themes diverge them
later without breaking widgets.
## Palette depth primitives
The palette layer has these depth primitives (defined in each theme YAML):
- `bg` (`#20202C`) — outermost root, behind everything
- `bgSunken` (`#1A1A24`) — chrome frame: sidebar, hat, statusbar
- `surface` (`#242838`) — elevated: pane headers, active tabs
- `surfaceHi` (`#2C3046`) — interactive: hover states, selections
Chrome tokens (`chromeBackground` / `chromeForeground` / `chromeBorder`)
are the shared root for all frame surfaces. They resolve to `bgSunken` /
`textDim` / `border` in the palette. Themes can override them to diverge
hat from sidebar from status bar if desired.
## Typography
Three constants — never hardcode sizes or families:
```
family UI → inherited from DefaultTextStyle (JosefinSans Light 300)
family mono → clideMonoFamily (JetBrainsMono)
body size → clideFontBody (15)
caption size → clideFontCaption (14) — status bar, section headers, git info
mono size → clideFontMono (14) — terminal, code, paths, IDs
```
Use `ClideText` for themed text. Set `muted: true` for secondary text
(resolves to `globalTextMuted`). Set `fontFamily: clideMonoFamily` for
code/paths/IDs. Don't set fontFamily for UI text — it inherits.
## Extension-owned domain colors
Extensions that need domain-specific color coding (ticket types, decision
types, priority levels) should NOT add tokens to `SurfaceTokens`. Instead:
1. Create a color map class in the extension (e.g. `TicketTypeColors`).
2. Ship dark and light presets, auto-selected via
`ClideTheme.of(context).dark`.
3. Store user overrides under `ext.<id>.colors` in settings.
4. Reference: `lib/builtin/tickets/src/ticket_colors.dart`.
This keeps the core token surface lean and lets each extension own its
palette. The pattern scales to any extension needing domain colors.
## Where to look in the codebase
- Full token list: `lib/kernel/src/theme/tokens.dart`
- Resolver fallbacks: `lib/kernel/src/theme/resolver.dart`
- Theme YAML example: `lib/kernel/src/theme/themes/clide.yaml`
- Decision: D-43 (handoff), D-44 (four bundled themes), D-45 (syntax tokens)
+35 -3
View File
@@ -1,5 +1,5 @@
{
"exported_at": "2026-05-06T13:21:08Z",
"exported_at": "2026-05-06T13:38:19Z",
"decisions": [
{
"id": "D-1",
@@ -2073,11 +2073,11 @@
"type": "task",
"parent_id": "T-3",
"title": "secondary Claude pane UI wiring",
"status": "in_progress",
"status": "done",
"priority": "medium",
"decision_ref": "D-41",
"created_at": "2026-04-22 14:08:40",
"updated_at": "2026-05-06 12:20:58"
"updated_at": "2026-05-06 13:21:15"
},
{
"id": "T-25",
@@ -2691,6 +2691,24 @@
"priority": "medium",
"created_at": "2026-05-06 10:16:02",
"updated_at": "2026-05-06 12:17:27"
},
{
"id": "T-85",
"type": "task",
"title": "consolidate theme-ui into ui-design skill with theme/surface/geometry/icons references",
"status": "in_progress",
"priority": "medium",
"created_at": "2026-05-06 13:28:58",
"updated_at": "2026-05-06 13:29:05"
},
{
"id": "T-86",
"type": "task",
"title": "codify UI spacing constants (gaps, paddings, control sizes)",
"status": "backlog",
"priority": "medium",
"created_at": "2026-05-06 13:28:58",
"updated_at": "2026-05-06 13:28:58"
}
],
"ticket_deps": null,
@@ -4065,6 +4083,20 @@
"old_value": "ready",
"new_value": "in_progress",
"changed_at": "2026-05-06 12:20:58"
},
{
"ticket_id": "T-24",
"field": "status",
"old_value": "in_progress",
"new_value": "done",
"changed_at": "2026-05-06 13:21:15"
},
{
"ticket_id": "T-85",
"field": "status",
"old_value": "backlog",
"new_value": "in_progress",
"changed_at": "2026-05-06 13:29:05"
}
]
}
+8
View File
@@ -28,6 +28,14 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
type, supports pinned/non-closeable tabs (primary), drag-reorder,
close × on hover, and an optional `+` add button. Used by the
Claude pane to render primary + secondaries.
- `MultitabPane.keepAlive` mode — when set, all entry bodies stay
mounted via IndexedStack so switching tabs preserves their state
(PTY connections, scroll position, etc.).
### Changed
- Claude pane uses `MultitabPane` for primary + secondaries — drops
~100 lines of bespoke tab-strip code, gains drag-to-reorder.
### Changed