|
| 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