Skip to content

Commit d03a4ff

Browse files
karkarlCopilot
andcommitted
Migrate design system components to components.jsonc
Colophon replaced the pseudocode-React components.jsx format with a structured, framework-agnostic components.jsonc element tree (rendered by a pure JSON->DOM interpreter, no React/Babel). Port the seed's components over faithfully: - Button, Field, Card, Badge, ChatBubble, ChatComposer, ChatThread, ExampleScreen translated to the element-tree schema at their rest state, keeping the ds-* classes, CSS-variable styling, WinUI-derived metrics, and SVG glyphs (send/mic/add/copy/chevron/ sparkle) intact. ComposerPicker factored out as a reusable component. - Interactive hover/press JS (not expressible in the static scene graph) is dropped; the preview shows the documented rest state. Validates clean (0 errors / 0 warnings); all prototypes.jsonc component references resolve. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent dc55060 commit d03a4ff

2 files changed

Lines changed: 305 additions & 402 deletions

File tree

.agents/design/components.jsonc

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
// components.jsonc — the component patterns your team has agreed on, as a structured
2+
// element tree (JSON + comments). One component = one decision already made.
3+
//
4+
// These render live in the Design System + Prototype canvases via a pure JSON→DOM
5+
// interpreter (no React, no build step). Style with the `ds-*` classes and CSS variables
6+
// the design system generates from design.json (var(--color-accent), var(--space-4),
7+
// var(--radius-md), var(--font-display), …). Copilot reads this file to match the house
8+
// style when it builds new UI.
9+
//
10+
// Schema:
11+
// { "meta": {…}, "components": [ { "name", "note"?, "props"?, "root" } ] }
12+
// node = element { "el": "div", "class"?, "attrs"?, "children"?: [node|string] }
13+
// | component { "component": "Button", "props"?: { … } } // ref by name
14+
// | string "literal" or "{prop}" // prop interpolation
15+
// "{prop}" interpolates a prop into class / attr values / text; "{{" and "}}" are
16+
// literal braces. SVG tags (svg/path/rect/…) render in the SVG namespace automatically.
17+
//
18+
// This is DESIGN INTENT for the canvas preview — not shipping code. To ship, port these
19+
// patterns into the app using the port targets in design.json `authority`: most surfaces
20+
// ship as native WinUI 3 / C# (port via github.com/microsoft/win-dev-skills), while the
21+
// chat surface (ChatBubble, ChatComposer, ChatThread) ships via Reactor
22+
// (github.com/microsoft/microsoft-ui-reactor). Don't ship this verbatim.
23+
{
24+
"meta": { "version": 1, "updatedBy": "copilot", "note": "Windows Fluent UI for OpenClaw — migrated from components.jsx." },
25+
"components": [
26+
{
27+
"name": "Button",
28+
"note": "Primary action. One per view — it's where the eye should land. Padding mirrors WinUI's default ButtonPadding (11,5,11,6 → 5px 11px 6px); MinWidth 100px keeps short labels (OK / Connect) from cramping and gives side-by-side buttons a consistent footprint.",
29+
"props": { "children": "Save changes", "variant": "primary" },
30+
"root": {
31+
"el": "button",
32+
"class": "ds-btn ds-btn-{variant}",
33+
"attrs": { "style": "padding:5px 11px 6px;min-width:100px" },
34+
"children": ["{children}"]
35+
}
36+
},
37+
{
38+
"name": "Field",
39+
"note": "Text input with a quiet label above it — the field is the subject. Input padding mirrors WinUI TextControlThemePadding (10,5,6,6 → 5px 6px 6px 10px) with MinHeight 32px, so a field lines up with a Button on the same row.",
40+
"props": { "label": "Gateway address", "placeholder": "127.0.0.1:18789" },
41+
"root": {
42+
"el": "label",
43+
"class": "ds-field",
44+
"children": [
45+
{ "el": "span", "class": "ds-field-label", "children": ["{label}"] },
46+
{
47+
"el": "input",
48+
"class": "ds-input",
49+
"attrs": { "placeholder": "{placeholder}", "style": "padding:5px 6px 6px 10px;min-height:32px" }
50+
}
51+
]
52+
}
53+
},
54+
{
55+
"name": "Card",
56+
"note": "Content card. Flat by default — a 1px hairline border, not a drop shadow. Padding mirrors the WinUI Gallery card (16,12 → 12px 16px) with the `md` radius and a `line`-colored border. Never nest a card inside a card.",
57+
"props": { "title": "Windows node", "body": "Paired · 6 capabilities exposed over MCP." },
58+
"root": {
59+
"el": "article",
60+
"class": "ds-card",
61+
"attrs": { "style": "padding:12px 16px" },
62+
"children": [
63+
{ "el": "h3", "class": "ds-card-title", "children": ["{title}"] },
64+
{ "el": "p", "class": "ds-card-body", "children": ["{body}"] },
65+
{ "el": "a", "class": "ds-link", "attrs": { "href": "#" }, "children": ["Open report →"] }
66+
]
67+
}
68+
},
69+
{
70+
"name": "Badge",
71+
"note": "Status pill. Uses semantic color tokens, never a raw hex.",
72+
"props": { "children": "Connected", "tone": "positive" },
73+
"root": { "el": "span", "class": "ds-badge ds-badge-{tone}", "children": ["{children}"] }
74+
},
75+
{
76+
"name": "ComposerPicker",
77+
"note": "Subtle inline picker — text label + chevron, no border until hover in the app. Used for the model and reasoning-effort selectors; reads as a quiet dropdown, not a button. (Preview shows the rest state.)",
78+
"props": { "label": "Model" },
79+
"root": {
80+
"el": "button",
81+
"attrs": {
82+
"type": "button",
83+
"style": "display:inline-flex;align-items:center;gap:var(--space-1);height:32px;padding:0 var(--space-2);border-radius:var(--radius-sm);border:1px solid transparent;background:transparent;color:var(--color-muted);font-family:var(--font-body);font-size:13px;line-height:18px;cursor:pointer;white-space:nowrap"
84+
},
85+
"children": [
86+
{ "el": "span", "children": ["{label}"] },
87+
{
88+
"el": "svg",
89+
"attrs": { "width": "12", "height": "12", "viewBox": "0 0 24 24", "fill": "none", "aria-hidden": "true", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" },
90+
"children": [ { "el": "path", "attrs": { "d": "M6 9l6 6 6-6" } } ]
91+
}
92+
]
93+
}
94+
},
95+
{
96+
"name": "ChatBubble",
97+
"note": "Assistant chat bubble: a 36×36 avatar (subtle fill, hairline border) to the LEFT of a bubble on a subtle surface with a hairline border. The `bubble` radius (16px) is friendlier than the 8/12px control radii; padding 12/16. Footer reads timestamp · usage · Copy — the same used/context (pct%) string ChatUsageFormatter produces. Mirrors OpenClawChatTimeline. (User messages ride the accent on the right in-app; this component previews the assistant variant.)",
98+
"props": {
99+
"time": "2:14 PM",
100+
"usage": "12.5K/200K (6%)",
101+
"children": "Paired to the gateway — 6 Windows node capabilities are live."
102+
},
103+
"root": {
104+
"el": "div",
105+
"attrs": { "style": "display:flex;align-items:flex-start;gap:var(--space-2)" },
106+
"children": [
107+
{
108+
"el": "div",
109+
"attrs": { "aria-hidden": "true", "style": "flex:0 0 auto;width:36px;height:36px;border-radius:var(--radius-pill);background:var(--color-surface);border:1px solid var(--color-line);color:var(--color-muted);display:inline-flex;align-items:center;justify-content:center" },
110+
"children": [
111+
{
112+
"el": "svg",
113+
"attrs": { "width": "18", "height": "18", "viewBox": "0 0 24 24", "fill": "currentColor", "aria-hidden": "true" },
114+
"children": [ { "el": "path", "attrs": { "d": "M12 2.5l1.7 5.1a4 4 0 0 0 2.7 2.7l5.1 1.7-5.1 1.7a4 4 0 0 0-2.7 2.7L12 21.5l-1.7-5.1a4 4 0 0 0-2.7-2.7L2.5 12l5.1-1.7a4 4 0 0 0 2.7-2.7L12 2.5z" } } ]
115+
}
116+
]
117+
},
118+
{
119+
"el": "div",
120+
"attrs": { "style": "display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-1)" },
121+
"children": [
122+
{
123+
"el": "div",
124+
"attrs": { "style": "max-width:min(80%, 520px);padding:var(--space-3) var(--space-4);border-radius:var(--radius-bubble);font-family:var(--font-body);font-size:14px;line-height:20px;background:var(--color-surface);color:var(--color-ink);border:1px solid var(--color-line)" },
125+
"children": ["{children}"]
126+
},
127+
{
128+
"el": "div",
129+
"attrs": { "style": "display:flex;align-items:center;gap:var(--space-2);padding:0 var(--space-2);font-family:var(--font-body);font-size:12px;line-height:16px;color:var(--color-muted)" },
130+
"children": [
131+
{ "el": "span", "children": ["{time}"] },
132+
{ "el": "span", "attrs": { "aria-hidden": "true" }, "children": ["·"] },
133+
{ "el": "span", "children": ["{usage}"] },
134+
{
135+
"el": "button",
136+
"attrs": { "type": "button", "title": "Copy", "aria-label": "Copy message", "style": "display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;border-radius:var(--radius-sm);background:transparent;color:var(--color-muted);opacity:0.65;cursor:pointer;transform:translateY(2px)" },
137+
"children": [
138+
{
139+
"el": "svg",
140+
"attrs": { "width": "14", "height": "14", "viewBox": "0 0 24 24", "fill": "none", "aria-hidden": "true", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" },
141+
"children": [
142+
{ "el": "rect", "attrs": { "x": "9", "y": "9", "width": "11", "height": "11", "rx": "2" } },
143+
{ "el": "path", "attrs": { "d": "M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1" } }
144+
]
145+
}
146+
]
147+
}
148+
]
149+
}
150+
]
151+
}
152+
]
153+
}
154+
},
155+
{
156+
"name": "ChatComposer",
157+
"note": "Chat input. A rounded surface (md radius / 8px) with a 1px hairline border wrapping a borderless, transparent, auto-growing textarea (min 56 → max 200) over a toolbar. Left cluster = Add, model picker, reasoning-effort picker; right cluster = mic + Send. Only Send spends the accent; every other control stays subtle so the toolbar reads as quiet chrome. (Preview shows the rest state.)",
158+
"props": {
159+
"placeholder": "Message OpenClaw…",
160+
"model": "Claude Opus 4.8",
161+
"effort": "High"
162+
},
163+
"root": {
164+
"el": "div",
165+
"attrs": { "style": "display:flex;flex-direction:column;gap:var(--space-2);padding:var(--space-2);border-radius:var(--radius-md);background:var(--color-surface);border:1px solid var(--color-line)" },
166+
"children": [
167+
{
168+
"el": "textarea",
169+
"attrs": { "rows": "2", "placeholder": "{placeholder}", "style": "min-height:56px;max-height:200px;resize:none;border:none;outline:none;background:transparent;color:var(--color-ink);font-family:var(--font-body);font-size:14px;line-height:20px;padding:var(--space-2)" }
170+
},
171+
{
172+
"el": "div",
173+
"attrs": { "style": "display:flex;align-items:center;justify-content:space-between;gap:var(--space-2)" },
174+
"children": [
175+
{
176+
"el": "div",
177+
"attrs": { "style": "display:flex;align-items:center;gap:var(--space-1);min-width:0" },
178+
"children": [
179+
{
180+
"el": "button",
181+
"attrs": { "type": "button", "title": "Add context", "aria-label": "Add context", "style": "display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border-radius:var(--radius-sm);cursor:pointer;border:1px solid transparent;background:transparent;color:var(--color-muted)" },
182+
"children": [
183+
{
184+
"el": "svg",
185+
"attrs": { "width": "18", "height": "18", "viewBox": "0 0 24 24", "fill": "none", "aria-hidden": "true", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" },
186+
"children": [ { "el": "path", "attrs": { "d": "M12 5v14M5 12h14" } } ]
187+
}
188+
]
189+
},
190+
{ "component": "ComposerPicker", "props": { "label": "{model}" } },
191+
{ "component": "ComposerPicker", "props": { "label": "{effort}" } }
192+
]
193+
},
194+
{
195+
"el": "div",
196+
"attrs": { "style": "display:flex;align-items:center;gap:var(--space-1)" },
197+
"children": [
198+
{
199+
"el": "button",
200+
"attrs": { "type": "button", "title": "Dictate", "aria-label": "Dictate", "style": "display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border-radius:var(--radius-sm);cursor:pointer;border:1px solid transparent;background:transparent;color:var(--color-muted)" },
201+
"children": [
202+
{
203+
"el": "svg",
204+
"attrs": { "width": "18", "height": "18", "viewBox": "0 0 24 24", "fill": "none", "aria-hidden": "true", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" },
205+
"children": [
206+
{ "el": "rect", "attrs": { "x": "9", "y": "3", "width": "6", "height": "11", "rx": "3" } },
207+
{ "el": "path", "attrs": { "d": "M5 11a7 7 0 0 0 14 0M12 18v3" } }
208+
]
209+
}
210+
]
211+
},
212+
{
213+
"el": "button",
214+
"attrs": { "type": "button", "title": "Send", "aria-label": "Send", "style": "display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border-radius:var(--radius-sm);cursor:pointer;border:none;background:var(--color-accent);color:var(--color-accentInk)" },
215+
"children": [
216+
{
217+
"el": "svg",
218+
"attrs": { "width": "18", "height": "18", "viewBox": "0 0 24 24", "fill": "none", "aria-hidden": "true", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" },
219+
"children": [ { "el": "path", "attrs": { "d": "M12 19V5M5 12l7-7 7 7" } } ]
220+
}
221+
]
222+
}
223+
]
224+
}
225+
]
226+
}
227+
]
228+
}
229+
},
230+
{
231+
"name": "ChatThread",
232+
"note": "A short thread: the \"does chat hang together\" check — a user turn (accent bubble on the right) and an assistant turn (with its Copy affordance + context-usage readout), then the composer.",
233+
"root": {
234+
"el": "div",
235+
"attrs": { "style": "display:flex;flex-direction:column;gap:var(--space-3);max-width:560px" },
236+
"children": [
237+
{
238+
"el": "div",
239+
"attrs": { "style": "display:flex;justify-content:flex-end" },
240+
"children": [
241+
{
242+
"el": "div",
243+
"attrs": { "style": "display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-1)" },
244+
"children": [
245+
{
246+
"el": "div",
247+
"attrs": { "style": "max-width:min(80%, 520px);padding:var(--space-3) var(--space-4);border-radius:var(--radius-bubble);font-family:var(--font-body);font-size:14px;line-height:20px;background:var(--color-accent);color:var(--color-accentInk);border:none" },
248+
"children": ["Can you list the Windows node capabilities?"]
249+
},
250+
{
251+
"el": "div",
252+
"attrs": { "style": "display:flex;align-items:center;gap:var(--space-2);padding:0 var(--space-2);font-family:var(--font-body);font-size:12px;line-height:16px;color:var(--color-muted)" },
253+
"children": [ { "el": "span", "children": ["2:14 PM"] } ]
254+
}
255+
]
256+
}
257+
]
258+
},
259+
{ "component": "ChatBubble", "props": { "time": "2:14 PM", "usage": "12.5K/200K (6%)", "children": "Paired to the gateway — 6 capabilities are live, including system.run and clipboard access." } },
260+
{ "component": "ChatComposer" }
261+
]
262+
}
263+
},
264+
{
265+
"name": "ExampleScreen",
266+
"note": "A representative screen composed from the pieces above — the \"does it hang together\" check for the whole system.",
267+
"root": {
268+
"el": "section",
269+
"class": "ds-screen",
270+
"children": [
271+
{
272+
"el": "header",
273+
"class": "ds-screen-head",
274+
"children": [
275+
{
276+
"el": "div",
277+
"children": [
278+
{ "el": "p", "class": "ds-eyebrow", "children": ["Connection"] },
279+
{ "el": "h2", "class": "ds-screen-title", "children": ["OpenClaw Windows Hub"] }
280+
]
281+
},
282+
{ "component": "Badge", "props": { "tone": "positive", "children": "Connected" } }
283+
]
284+
},
285+
{
286+
"el": "div",
287+
"class": "ds-grid",
288+
"children": [
289+
{ "component": "Card", "props": { "title": "Gateway", "body": "Paired to OpenClawGateway · last seen just now." } },
290+
{ "component": "Card", "props": { "title": "Windows node", "body": "6 capabilities exposed over MCP." } }
291+
]
292+
},
293+
{
294+
"el": "div",
295+
"class": "ds-row",
296+
"children": [
297+
{ "component": "Field", "props": { "label": "Gateway address", "placeholder": "127.0.0.1:18789" } },
298+
{ "component": "Button", "props": { "children": "Connect" } }
299+
]
300+
}
301+
]
302+
}
303+
}
304+
]
305+
}

0 commit comments

Comments
 (0)