Skip to content

Commit fbbe29e

Browse files
committed
chore(design-sync): sync HeartbeatToolCall into the design system
Enroll the HeartbeatToolCall tool card and push it to the Mux Design System claude.ai/design project (now 34 components): - ds-barrel.ts: export HeartbeatToolCall (gen-barrel-confirmed). - config.json: titleMap "Heartbeat" -> "HeartbeatToolCall"; overrides cardMode "column" (the CustomMessageWrapping story renders at a pinned 375px, wider than a grid cell); "Timeouts": null to drop the WorkflowRun timeout sub-story. - gen-barrel.mjs: add "Timeouts" to EXCLUDE_HEAVY (a segment exclusion) so the heavy WorkflowRun component (shiki/mermaid/recharts, over the 5 MB cap) stays out of the bundle — its ".../Timeouts" title segment slipped the "WorkflowRun" seg exclusion. Segment (not path) exclusion so the generated titleMap also nulls "Timeouts" and stays consistent with cfg.titleMap (a path exclusion would drift from config). - previews/HeartbeatToolCall.tsx: hand-authored isolated preview, one cell per story (all 10 graded match against the reference Storybook). - NOTES.md: re-sync risks for the above, plus the accepted Ubuntu Mono fallback font. Change-Id: Iaa6a1c73ddcef47c2cff85c7a0d3d95bac633a94 Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 472435a commit fbbe29e

5 files changed

Lines changed: 246 additions & 1 deletion

File tree

.design-sync/NOTES.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,24 @@ needed; the cap is per-file 5 MB on both `_ds_bundle.js` and each `_preview/*.js
161161
changes, revisit the `tsconfig.ds.json` stubs.
162162
- **Per-file 5 MB cap** applies to BOTH `_ds_bundle.js` and each `_preview/*.js`.
163163
Bundle ~4.7 MB and previews ~3.3 MB leave thin headroom — adding components or
164-
providers needs a fresh size measurement.
164+
providers needs a fresh size measurement. (HeartbeatToolCall added: bundle 4.69 MB,
165+
its preview 3.21 MB — still fits, headroom now ~0.3 MB.)
166+
- **HeartbeatToolCall (tool card):** owned preview has one cell per story (10, named to
167+
match the story exports so `compare` pairs them — the stories import `meta.tsx` → the
168+
whole app, so the preview is hand-authored like the other cards). `cfg.overrides`
169+
uses `cardMode: "column"` because the `CustomMessageWrapping` story renders at a pinned
170+
375px (narrower than a grid cell → `[GRID_OVERFLOW] wide`); column keeps all stories
171+
full-width. All 10 stories grade `match`.
172+
- **WorkflowRun/Timeouts exclusion:** `WorkflowRunToolCall` is EXCLUDE_HEAVY via its
173+
`WorkflowRun` segment, but `WorkflowRunToolCall.timeout.stories.tsx` titles to
174+
`…/WorkflowRun/Timeouts` (segment `Timeouts`), which slips that exclusion and would pull
175+
the heavy component (shiki/mermaid/recharts) into the bundle over cap. Fixed by adding
176+
`"Timeouts"` to `gen-barrel.mjs` EXCLUDE_HEAVY (a **segment** exclusion, not a path one, so
177+
the generated titleMap also nulls `Timeouts` and stays consistent with `cfg.titleMap "Timeouts": null`
178+
— a path exclusion would drift). If WorkflowRun stories are retitled again, re-check this.
179+
- **`[FONT_MISSING]` "Ubuntu Mono":** pre-existing fallback in the `globals.css` monospace
180+
stack (`… "Geist Mono", "Ubuntu Mono", "Consolas" …`). Geist Mono ships and wins, so
181+
nothing renders in Ubuntu Mono — accepted (the unused fallback needs no @font-face).
165182
- **Accepted `close` grades (6 stories — isolated-preview limits, NOT defects):**
166183
the isolated single-component preview legitimately diverges from a story that is
167184
a gallery, a full-app scenario, a play-expanded interaction, or a mid-stream

.design-sync/config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"Messages": null,
3030
"Task": null,
3131
"WorkflowRun": null,
32+
"Timeouts": null,
3233
"ProposePlan": null,
3334
"Media": null,
3435
"AttachFile": null,
@@ -70,6 +71,7 @@
7071
"Compaction": "CompactingMessageContent",
7172
"ContextUsageIndicator": "ContextUsageIndicatorButton",
7273
"Goal": "GetGoalToolCall",
74+
"Heartbeat": "HeartbeatToolCall",
7375
"HookOutput": "HookOutputDisplay",
7476
"Init": "InitMessage",
7577
"Streaming": "StreamingBarrierView",
@@ -163,6 +165,10 @@
163165
"GetGoalToolCall": {
164166
"skip": []
165167
},
168+
"HeartbeatToolCall": {
169+
"skip": [],
170+
"cardMode": "column"
171+
},
166172
"InitMessage": {
167173
"skip": []
168174
},

.design-sync/ds-barrel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export { Dialog } from "@/browser/components/Dialog/Dialog";
1717
export { GeneralSection } from "@/browser/features/Settings/Sections/GeneralSection";
1818
export { GetGoalToolCall } from "@/browser/features/Tools/GetGoalToolCall";
1919
export { GoalTab } from "@/browser/features/RightSidebar/GoalTab";
20+
export { HeartbeatToolCall } from "@/browser/features/Tools/HeartbeatToolCall";
2021
export { HookOutputDisplay } from "@/browser/features/Tools/Shared/HookOutputDisplay";
2122
export { InitMessage } from "@/browser/features/Messages/InitMessage";
2223
export { Input } from "@/browser/components/Input/Input";

.design-sync/gen-barrel.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ const EXCLUDE_HEAVY = [
4646
"Messages",
4747
"Task",
4848
"WorkflowRun",
49+
// WorkflowRunToolCall's timeout stories title to ".../WorkflowRun/Timeouts" (segment
50+
// "Timeouts"); exclude that segment too so the heavy component stays out — and so the
51+
// generated titleMap nulls "Timeouts" (a path exclusion would not, drifting from config).
52+
"Timeouts",
4953
"ProposePlan",
5054
"Media",
5155
"AttachFile",
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import * as React from "react";
2+
import { MuxPreviewShell } from "../preview-harness";
3+
import { HeartbeatToolCall } from "@/browser/features/Tools/HeartbeatToolCall";
4+
5+
// Isolated previews of the heartbeat tool-call card — one cell per story in
6+
// HeartbeatToolCall.stories.tsx, named to match the story exports so compare pairs them,
7+
// rendered with the same inline args/result/status. Hand-authored (not generated) because
8+
// the stories import meta.tsx → the whole app graph, which is over the bundle cap (see
9+
// .design-sync/NOTES.md "UI primitives / previews").
10+
11+
const TASK_PROMPT =
12+
"Check the CI run for the auth refactor. If it's green, open the PR; if it's red, " +
13+
"summarize the first failure and stop.";
14+
const DEFAULT_BODY =
15+
"Check in on the current state of this workspace — review any pending work, check for stale " +
16+
"context, and determine if any action is needed. If everything looks good, briefly confirm the " +
17+
"workspace status.";
18+
19+
// Mirrors the stories' decorator chain (theme + tooltip provider, dark background, max-w-2xl).
20+
// `width` reproduces a story-level narrow wrapper (CustomMessageWrapping renders at 375px).
21+
const Shell = (props: { width?: string; children: React.ReactNode }) => (
22+
<MuxPreviewShell>
23+
<div className="bg-background p-6">
24+
<div className="w-full max-w-2xl">
25+
{props.width ? <div style={{ width: props.width }}>{props.children}</div> : props.children}
26+
</div>
27+
</div>
28+
</MuxPreviewShell>
29+
);
30+
31+
export const ScheduledEnabled = () => (
32+
<Shell>
33+
<HeartbeatToolCall
34+
args={{
35+
action: "set",
36+
enabled: true,
37+
intervalMs: 30 * 60_000,
38+
contextMode: "normal",
39+
message: TASK_PROMPT,
40+
}}
41+
status="completed"
42+
defaultExpanded
43+
result={{
44+
success: true,
45+
action: "set",
46+
configured: true,
47+
settings: {
48+
enabled: true,
49+
intervalMs: 30 * 60_000,
50+
contextMode: "normal",
51+
message: TASK_PROMPT,
52+
},
53+
summary: "Heartbeat is enabled for this workspace at 30 minutes.",
54+
}}
55+
/>
56+
</Shell>
57+
);
58+
59+
export const CustomMessageWrapping = () => (
60+
<Shell width="375px">
61+
<HeartbeatToolCall
62+
args={{ action: "set", enabled: true, intervalMs: 1_800_000 }}
63+
status="completed"
64+
defaultExpanded
65+
result={{
66+
success: true,
67+
action: "set",
68+
configured: true,
69+
settings: {
70+
enabled: true,
71+
intervalMs: 1_800_000,
72+
contextMode: "normal",
73+
message:
74+
"Poll the deploy and report status.\n" +
75+
"Logs: https://ci.example.com/runs/0123456789abcdef0123456789abcdef/jobs/deploy-prod-us-east-1/raw?download=true\n" +
76+
"If it failed, summarize the first error and stop.",
77+
},
78+
summary: "Heartbeat is enabled for this workspace at 30 minutes.",
79+
}}
80+
/>
81+
</Shell>
82+
);
83+
84+
export const LongCadenceCompact = () => (
85+
<Shell>
86+
<HeartbeatToolCall
87+
args={{ action: "set", enabled: true, intervalMs: 2 * 3_600_000, contextMode: "compact" }}
88+
status="completed"
89+
defaultExpanded
90+
result={{
91+
success: true,
92+
action: "set",
93+
configured: true,
94+
settings: { enabled: true, intervalMs: 2 * 3_600_000, contextMode: "compact" },
95+
summary: "Heartbeat is enabled for this workspace at 2 hours.",
96+
}}
97+
/>
98+
</Shell>
99+
);
100+
101+
export const ReadReset = () => (
102+
<Shell>
103+
<HeartbeatToolCall
104+
args={{ action: "get" }}
105+
status="completed"
106+
defaultExpanded
107+
result={{
108+
success: true,
109+
action: "get",
110+
configured: true,
111+
settings: {
112+
enabled: true,
113+
intervalMs: 3_600_000,
114+
contextMode: "reset",
115+
message: DEFAULT_BODY,
116+
},
117+
summary: "Heartbeat is enabled for this workspace at 1 hour.",
118+
}}
119+
/>
120+
</Shell>
121+
);
122+
123+
export const Paused = () => (
124+
<Shell>
125+
<HeartbeatToolCall
126+
args={{ action: "set", enabled: false }}
127+
status="completed"
128+
defaultExpanded
129+
result={{
130+
success: true,
131+
action: "set",
132+
configured: true,
133+
settings: {
134+
enabled: false,
135+
intervalMs: 30 * 60_000,
136+
contextMode: "normal",
137+
message: DEFAULT_BODY,
138+
},
139+
summary: "Heartbeat is disabled for this workspace at 30 minutes.",
140+
}}
141+
/>
142+
</Shell>
143+
);
144+
145+
export const ReadNotConfigured = () => (
146+
<Shell>
147+
<HeartbeatToolCall
148+
args={{ action: "get" }}
149+
status="completed"
150+
defaultExpanded
151+
result={{
152+
success: true,
153+
action: "get",
154+
configured: false,
155+
settings: null,
156+
summary: "No heartbeat settings are configured for this workspace.",
157+
}}
158+
/>
159+
</Shell>
160+
);
161+
162+
export const Cleared = () => (
163+
<Shell>
164+
<HeartbeatToolCall
165+
args={{ action: "unset" }}
166+
status="completed"
167+
defaultExpanded
168+
result={{
169+
success: true,
170+
action: "unset",
171+
configured: false,
172+
settings: null,
173+
summary: "Heartbeat settings removed for this workspace.",
174+
}}
175+
/>
176+
</Shell>
177+
);
178+
179+
export const Executing = () => (
180+
<Shell>
181+
<HeartbeatToolCall
182+
args={{ action: "set", enabled: true, intervalMs: 30 * 60_000 }}
183+
status="executing"
184+
defaultExpanded
185+
/>
186+
</Shell>
187+
);
188+
189+
export const ErrorResult = () => (
190+
<Shell>
191+
<HeartbeatToolCall
192+
args={{ action: "set", enabled: true, intervalMs: 30 * 60_000 }}
193+
status="failed"
194+
defaultExpanded
195+
result={{
196+
success: false,
197+
error: "Failed to update heartbeat settings: workspace configuration is unavailable.",
198+
}}
199+
/>
200+
</Shell>
201+
);
202+
203+
export const Interrupted = () => (
204+
<Shell>
205+
<HeartbeatToolCall
206+
args={{
207+
action: "set",
208+
enabled: true,
209+
intervalMs: 2 * 3_600_000,
210+
contextMode: "compact",
211+
message: "Watch the long-running migration and report when it finishes.",
212+
}}
213+
status="interrupted"
214+
defaultExpanded
215+
/>
216+
</Shell>
217+
);

0 commit comments

Comments
 (0)