diff --git a/.agents/design/components.jsonc b/.agents/design/components.jsonc new file mode 100644 index 000000000..3b0d3fb55 --- /dev/null +++ b/.agents/design/components.jsonc @@ -0,0 +1,305 @@ +// components.jsonc — the component patterns your team has agreed on, as a structured +// element tree (JSON + comments). One component = one decision already made. +// +// These render live in the Design System + Prototype canvases via a pure JSON→DOM +// interpreter (no React, no build step). Style with the `ds-*` classes and CSS variables +// the design system generates from design.json (var(--color-accent), var(--space-4), +// var(--radius-md), var(--font-display), …). Copilot reads this file to match the house +// style when it builds new UI. +// +// Schema: +// { "meta": {…}, "components": [ { "name", "note"?, "props"?, "root" } ] } +// node = element { "el": "div", "class"?, "attrs"?, "children"?: [node|string] } +// | component { "component": "Button", "props"?: { … } } // ref by name +// | string "literal" or "{prop}" // prop interpolation +// "{prop}" interpolates a prop into class / attr values / text; "{{" and "}}" are +// literal braces. SVG tags (svg/path/rect/…) render in the SVG namespace automatically. +// +// This is DESIGN INTENT for the canvas preview — not shipping code. To ship, port these +// patterns into the app using the port targets in design.json `authority`: most surfaces +// ship as native WinUI 3 / C# (port via github.com/microsoft/win-dev-skills), while the +// chat surface (ChatBubble, ChatComposer, ChatThread) ships via Reactor +// (github.com/microsoft/microsoft-ui-reactor). Don't ship this verbatim. +{ + "meta": { "version": 1, "updatedBy": "copilot", "note": "Windows Fluent UI for OpenClaw — migrated from components.jsx." }, + "components": [ + { + "name": "Button", + "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.", + "props": { "children": "Save changes", "variant": "primary" }, + "root": { + "el": "button", + "class": "ds-btn ds-btn-{variant}", + "attrs": { "style": "padding:5px 11px 6px;min-width:100px" }, + "children": ["{children}"] + } + }, + { + "name": "Field", + "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.", + "props": { "label": "Gateway address", "placeholder": "127.0.0.1:18789" }, + "root": { + "el": "label", + "class": "ds-field", + "children": [ + { "el": "span", "class": "ds-field-label", "children": ["{label}"] }, + { + "el": "input", + "class": "ds-input", + "attrs": { "placeholder": "{placeholder}", "style": "padding:5px 6px 6px 10px;min-height:32px" } + } + ] + } + }, + { + "name": "Card", + "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.", + "props": { "title": "Windows node", "body": "Paired · 6 capabilities exposed over MCP." }, + "root": { + "el": "article", + "class": "ds-card", + "attrs": { "style": "padding:12px 16px" }, + "children": [ + { "el": "h3", "class": "ds-card-title", "children": ["{title}"] }, + { "el": "p", "class": "ds-card-body", "children": ["{body}"] }, + { "el": "a", "class": "ds-link", "attrs": { "href": "#" }, "children": ["Open report →"] } + ] + } + }, + { + "name": "Badge", + "note": "Status pill. Uses semantic color tokens, never a raw hex.", + "props": { "children": "Connected", "tone": "positive" }, + "root": { "el": "span", "class": "ds-badge ds-badge-{tone}", "children": ["{children}"] } + }, + { + "name": "ComposerPicker", + "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.)", + "props": { "label": "Model" }, + "root": { + "el": "button", + "attrs": { + "type": "button", + "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" + }, + "children": [ + { "el": "span", "children": ["{label}"] }, + { + "el": "svg", + "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" }, + "children": [ { "el": "path", "attrs": { "d": "M6 9l6 6 6-6" } } ] + } + ] + } + }, + { + "name": "ChatBubble", + "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.)", + "props": { + "time": "2:14 PM", + "usage": "12.5K/200K (6%)", + "children": "Paired to the gateway — 6 Windows node capabilities are live." + }, + "root": { + "el": "div", + "attrs": { "style": "display:flex;align-items:flex-start;gap:var(--space-2)" }, + "children": [ + { + "el": "div", + "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" }, + "children": [ + { + "el": "svg", + "attrs": { "width": "18", "height": "18", "viewBox": "0 0 24 24", "fill": "currentColor", "aria-hidden": "true" }, + "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" } } ] + } + ] + }, + { + "el": "div", + "attrs": { "style": "display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-1)" }, + "children": [ + { + "el": "div", + "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)" }, + "children": ["{children}"] + }, + { + "el": "div", + "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)" }, + "children": [ + { "el": "span", "children": ["{time}"] }, + { "el": "span", "attrs": { "aria-hidden": "true" }, "children": ["·"] }, + { "el": "span", "children": ["{usage}"] }, + { + "el": "button", + "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)" }, + "children": [ + { + "el": "svg", + "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" }, + "children": [ + { "el": "rect", "attrs": { "x": "9", "y": "9", "width": "11", "height": "11", "rx": "2" } }, + { "el": "path", "attrs": { "d": "M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1" } } + ] + } + ] + } + ] + } + ] + } + ] + } + }, + { + "name": "ChatComposer", + "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.)", + "props": { + "placeholder": "Message OpenClaw…", + "model": "Claude Opus 4.8", + "effort": "High" + }, + "root": { + "el": "div", + "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)" }, + "children": [ + { + "el": "textarea", + "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)" } + }, + { + "el": "div", + "attrs": { "style": "display:flex;align-items:center;justify-content:space-between;gap:var(--space-2)" }, + "children": [ + { + "el": "div", + "attrs": { "style": "display:flex;align-items:center;gap:var(--space-1);min-width:0" }, + "children": [ + { + "el": "button", + "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)" }, + "children": [ + { + "el": "svg", + "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" }, + "children": [ { "el": "path", "attrs": { "d": "M12 5v14M5 12h14" } } ] + } + ] + }, + { "component": "ComposerPicker", "props": { "label": "{model}" } }, + { "component": "ComposerPicker", "props": { "label": "{effort}" } } + ] + }, + { + "el": "div", + "attrs": { "style": "display:flex;align-items:center;gap:var(--space-1)" }, + "children": [ + { + "el": "button", + "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)" }, + "children": [ + { + "el": "svg", + "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" }, + "children": [ + { "el": "rect", "attrs": { "x": "9", "y": "3", "width": "6", "height": "11", "rx": "3" } }, + { "el": "path", "attrs": { "d": "M5 11a7 7 0 0 0 14 0M12 18v3" } } + ] + } + ] + }, + { + "el": "button", + "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)" }, + "children": [ + { + "el": "svg", + "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" }, + "children": [ { "el": "path", "attrs": { "d": "M12 19V5M5 12l7-7 7 7" } } ] + } + ] + } + ] + } + ] + } + ] + } + }, + { + "name": "ChatThread", + "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.", + "root": { + "el": "div", + "attrs": { "style": "display:flex;flex-direction:column;gap:var(--space-3);max-width:560px" }, + "children": [ + { + "el": "div", + "attrs": { "style": "display:flex;justify-content:flex-end" }, + "children": [ + { + "el": "div", + "attrs": { "style": "display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-1)" }, + "children": [ + { + "el": "div", + "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" }, + "children": ["Can you list the Windows node capabilities?"] + }, + { + "el": "div", + "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)" }, + "children": [ { "el": "span", "children": ["2:14 PM"] } ] + } + ] + } + ] + }, + { "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." } }, + { "component": "ChatComposer" } + ] + } + }, + { + "name": "ExampleScreen", + "note": "A representative screen composed from the pieces above — the \"does it hang together\" check for the whole system.", + "root": { + "el": "section", + "class": "ds-screen", + "children": [ + { + "el": "header", + "class": "ds-screen-head", + "children": [ + { + "el": "div", + "children": [ + { "el": "p", "class": "ds-eyebrow", "children": ["Connection"] }, + { "el": "h2", "class": "ds-screen-title", "children": ["OpenClaw Windows Hub"] } + ] + }, + { "component": "Badge", "props": { "tone": "positive", "children": "Connected" } } + ] + }, + { + "el": "div", + "class": "ds-grid", + "children": [ + { "component": "Card", "props": { "title": "Gateway", "body": "Paired to OpenClawGateway · last seen just now." } }, + { "component": "Card", "props": { "title": "Windows node", "body": "6 capabilities exposed over MCP." } } + ] + }, + { + "el": "div", + "class": "ds-row", + "children": [ + { "component": "Field", "props": { "label": "Gateway address", "placeholder": "127.0.0.1:18789" } }, + { "component": "Button", "props": { "children": "Connect" } } + ] + } + ] + } + } + ] +} diff --git a/.agents/design/design.json b/.agents/design/design.json new file mode 100644 index 000000000..76551ef29 --- /dev/null +++ b/.agents/design/design.json @@ -0,0 +1,114 @@ +{ + "$schema": "https://agents.design/schema/v1", + "meta": { + "version": 1, + "updatedBy": "xaml+manual", + "note": "These tokens are the source of truth for DESIGN (brand, color, type, spacing, principles) and are framework-agnostic — components.jsx shows design intent for the canvas preview, not shipping code. They were captured by manual inspection of the repo's WinUI 3 / XAML and C# surfaces (the CSS/JSX scanner finds no files in a XAML repo) and map to Windows Fluent system resources (SystemAccentColor, Mica, Segoe UI Variable). To ship, port the design into native code via the port targets in `authority`. Edit in the Design System canvas or directly." + }, + "authority": { + "designSource": "self", + "owner": "OpenClaw Windows (openclaw/openclaw-windows-node)", + "syncProcess": "Tokens are captured by manual inspection of the app's WinUI 3 XAML/C# Fluent resources; re-inspect and update this file when the app's ThemeResource mappings or SystemAccentColor usage change.", + "port": { + "authoritySource": "Native WinUI 3 / C# (Windows Fluent)", + "syncSource": "https://github.com/microsoft/win-dev-skills", + "helperAgent": "win-dev-skills" + }, + "portOverrides": [ + { + "area": "chat", + "components": ["ChatBubble", "ChatComposer", "ChatThread"], + "authoritySource": "Reactor (Microsoft.UI.Reactor)", + "syncSource": "https://github.com/microsoft/microsoft-ui-reactor", + "helperAgent": "" + } + ] + }, + "brand": { + "name": "Windows Fluent UI for OpenClaw", + "tagline": "A calm native Windows companion for your AI assistant.", + "description": "Windows Fluent UI for OpenClaw is the design system for OpenClaw's native Windows companion suite — a WinUI 3 system-tray app that connects your PC to OpenClaw, the AI-powered personal assistant. The app pairs your PC to an OpenClaw gateway, exposes allowlisted Windows node capabilities over MCP, and guides first-run pairing through a setup wizard. Its interface follows Windows Fluent design conventions — Mica surfaces, the user's system accent, and Segoe UI Variable type — so it feels like a quiet, native extension of the OS rather than a bolted-on dashboard.", + "surface": "product", + "voice": "Clear, respectful, and native to Windows. Explain pairing, permissions, and security plainly. State what will happen and why; never alarmist, never hype.", + "personality": ["native", "trustworthy", "calm", "precise"], + "antiReferences": [ + "custom-skinned controls that fight the OS theme", + "neon gradients and glassmorphism overload", + "ignoring the user's system accent or light/dark preference", + "cramming a web dashboard into a desktop window", + "hard-coded hex that breaks in dark or high-contrast mode" + ] + }, + "colors": [ + { "name": "ink", "value": "#1b1b1b", "resource": "TextFillColorPrimaryBrush", "themes": { "dark": "#ffffff", "highContrast": "#ffffff" }, "usage": "Primary text, headings. Fluent TextFillColorPrimary on light." }, + { "name": "paper", "value": "#f3f3f3", "resource": "ApplicationPageBackgroundThemeBrush", "themes": { "dark": "#202020", "highContrast": "#000000" }, "usage": "App / page background. Mica base / ApplicationPageBackground (light)." }, + { "name": "surface", "value": "#fbfbfb", "resource": "CardBackgroundFillColorDefaultBrush", "themes": { "dark": "#2b2b2b", "highContrast": "#000000" }, "usage": "Cards, flyouts, inputs. Fluent CardBackgroundFillColorDefault." }, + { "name": "muted", "value": "#5d5d5d", "resource": "TextFillColorSecondaryBrush", "themes": { "dark": "#c8c8c8", "highContrast": "#ffffff" }, "usage": "Secondary text, captions. Fluent TextFillColorSecondary/Tertiary." }, + { "name": "line", "value": "#e5e5e5", "resource": "CardStrokeColorDefaultBrush", "themes": { "dark": "#303030", "highContrast": "#ffffff" }, "usage": "Borders, dividers. Fluent CardStrokeColorDefault / DividerStroke." }, + { "name": "accent", "value": "#0067c0", "resource": "AccentFillColorDefaultBrush", "themes": { "dark": "#4cc2ff", "highContrast": "#ffff00" }, "usage": "Primary actions, links, selection, focus. Follows SystemAccentColor (default AccentDark1 on light)." }, + { "name": "accentInk", "value": "#ffffff", "resource": "TextOnAccentFillColorPrimaryBrush", "themes": { "dark": "#000000", "highContrast": "#000000" }, "usage": "Text/glyphs on accent fills." }, + { "name": "positive", "value": "#0f7b0f", "resource": "SystemFillColorSuccessBrush", "themes": { "dark": "#6ccb5f", "highContrast": "#3ff23f" }, "usage": "Success / connected states. Fluent SystemFillColorSuccess." }, + { "name": "warning", "value": "#9d5d00", "resource": "SystemFillColorCautionBrush", "themes": { "dark": "#fcd116", "highContrast": "#ffb000" }, "usage": "Warnings / attention. Fluent SystemFillColorCaution." }, + { "name": "critical", "value": "#c42b1c", "resource": "SystemFillColorCriticalBrush", "themes": { "dark": "#ff99a4", "highContrast": "#ff5449" }, "usage": "Errors, disconnected, destructive. Fluent SystemFillColorCritical." }, + { "name": "subtleHover", "value": "rgba(0,0,0,0.037)", "resource": "SubtleFillColorSecondaryBrush", "themes": { "dark": "rgba(255,255,255,0.0605)", "highContrast": "transparent" }, "usage": "Hover fill for subtle/transparent controls (icon buttons, inline pickers). Fluent SubtleFillColorSecondary (light #09000000)." }, + { "name": "subtlePressed", "value": "rgba(0,0,0,0.024)", "resource": "SubtleFillColorTertiaryBrush", "themes": { "dark": "rgba(255,255,255,0.0419)", "highContrast": "transparent" }, "usage": "Pressed fill for subtle/transparent controls. Fluent SubtleFillColorTertiary (light #06000000)." } + ], + "typography": { + "display": { + "family": "\"Segoe UI Variable Display\", \"Segoe UI Variable\", \"Segoe UI\", system-ui, sans-serif", + "weights": [400, 600], + "usage": "Page and section titles. Segoe UI Variable Display optical size." + }, + "body": { + "family": "\"Segoe UI Variable Text\", \"Segoe UI Variable\", \"Segoe UI\", system-ui, sans-serif", + "weights": [400, 600], + "usage": "Body copy, UI labels, buttons. Segoe UI Variable Text optical size." + }, + "mono": { + "family": "\"Cascadia Mono\", \"Cascadia Code\", Consolas, \"SFMono-Regular\", monospace", + "weights": [400], + "usage": "Commands, endpoints, tokens, logs, timestamps. Used heavily across setup and diagnostics surfaces." + }, + "scale": [ + { "name": "display", "size": "40px", "lineHeight": "50px", "weight": 600, "role": "display", "tracking": "-0.01em" }, + { "name": "title", "size": "28px", "lineHeight": "36px", "weight": 600, "role": "display" }, + { "name": "heading", "size": "20px", "lineHeight": "28px", "weight": 600, "role": "body" }, + { "name": "body", "size": "14px", "lineHeight": "20px", "weight": 400, "role": "body" }, + { "name": "small", "size": "13px", "lineHeight": "18px", "weight": 400, "role": "body" }, + { "name": "caption", "size": "12px", "lineHeight": "16px", "weight": 400, "role": "body" } + ] + }, + "spacing": { + "unit": 4, + "scale": [ + { "name": "1", "value": "4px" }, + { "name": "2", "value": "8px" }, + { "name": "3", "value": "12px" }, + { "name": "4", "value": "16px" }, + { "name": "5", "value": "24px" }, + { "name": "6", "value": "32px" }, + { "name": "7", "value": "48px" }, + { "name": "8", "value": "64px" } + ] + }, + "radii": [ + { "name": "sm", "value": "4px", "usage": "Buttons, inputs, small controls. Fluent ControlCornerRadius." }, + { "name": "md", "value": "8px", "usage": "Cards, dialogs, flyouts. Fluent OverlayCornerRadius." }, + { "name": "lg", "value": "12px", "usage": "Large panels and grouped surfaces." }, + { "name": "bubble", "value": "16px", "usage": "Chat message bubbles — intentionally friendlier than control radii. Mirrors OpenClawChatTimeline CornerRadius 16." }, + { "name": "pill", "value": "999px", "usage": "Status pills, badges, toggles." } + ], + "shadows": [ + { "name": "sm", "value": "0 1px 2px rgba(0,0,0,0.10)" }, + { "name": "md", "value": "0 4px 16px rgba(0,0,0,0.12)" }, + { "name": "lg", "value": "0 12px 40px rgba(0,0,0,0.16)" } + ], + "principles": [ + "Defer to Windows. Theme off SystemAccentColor and Fluent resources so the app follows the user's accent, light/dark, and high-contrast choices.", + "One accent, and it means \"act here\". Selection, focus, and primary actions share the system accent; don't spend it on decoration.", + "Hierarchy over decoration — size, weight, and space do the work before color does.", + "State must be legible: connected, pairing, and error states read clearly via positive/warning/critical, never color alone.", + "No hard-coded hex in UI. Use tokens/theme resources so dark and high-contrast modes stay correct.", + "Motion is a cue, not a garnish: 120-200ms ease-out, no bounce." + ] +} diff --git a/.agents/design/principles.md b/.agents/design/principles.md new file mode 100644 index 000000000..5baaa5cd3 --- /dev/null +++ b/.agents/design/principles.md @@ -0,0 +1,55 @@ +# Design principles — Windows Fluent UI for OpenClaw + +> This is the prose companion to `design.json`. Designers write here; developers +> and Copilot read here. Keep it short enough that people actually read it. +> This is the design system for the **OpenClaw Windows Hub** app. + +## Design leads, native code ships +These files are the source of truth for **design** — tokens, component intent, and +principles — and are framework-agnostic. The React in `components.jsx` is design +intent for the canvas preview, not shipping code. How a design becomes shipping code +is recorded in `design.json` under `authority`: + +- **Default:** surfaces ship as **native WinUI 3 / C#** (Windows Fluent). Port the + design into XAML/C# using [win-dev-skills](https://github.com/microsoft/win-dev-skills). +- **Chat surface** (`ChatBubble`, `ChatComposer`, `ChatThread`): ships via + **[Reactor](https://github.com/microsoft/microsoft-ui-reactor)**, the React-style + framework for this app's chat. (Reactor has no dedicated porting agent yet — WIP.) + +When you need a value the design doesn't cover, add it here first, then port it into +the native/Reactor implementation via the matching source above — don't hard-code a +one-off in feature code. + +## The one-line brief +A calm native Windows companion for your AI assistant. It should feel like a +quiet, trustworthy extension of Windows — Fluent surfaces, the user's own +accent color — not a web dashboard wearing a desktop window. + +## Voice +- Clear, respectful, and native to Windows. Say what will happen and why. +- Explain pairing, permissions, and security plainly — never alarmist, never hype. +- No hype words ("supercharge", "seamless", "revolutionary", "effortless"). + +## Information hierarchy +1. **One subject per screen.** Decide what the screen is *for* and let that element win. +2. **Size and weight before color.** Establish the hierarchy in grayscale first; color is the last 10%. +3. **One accent.** `accent` = "act here" (selection, focus, primary action). If everything is accented, nothing is. +4. **Generous vertical rhythm.** Prefer the `space-5`/`space-6` steps between groups; let content breathe. + +## Do +- Defer to Windows: theme off `SystemAccentColor` and Fluent resources so the app + follows the user's accent, light/dark, and high-contrast settings. +- Use Segoe UI Variable for text and Cascadia Mono for commands, endpoints, tokens, and logs. +- Make connection state legible: connected/pairing/error read via `positive`/`warning`/`critical`. +- Keep motion to 120–200ms ease-out. Animate to explain, not to impress. + +## Don't +- Don't hard-code hex in UI — it breaks in dark and high-contrast modes. Use tokens/theme resources. +- Don't put gray text on a colored background. +- Don't nest cards inside cards, or wrap every block in a card. +- Don't reach for bounce/elastic easing; it reads as dated and AI-generated. +- Don't fight the OS theme with custom-skinned controls or glassmorphism overload. + +## Anti-references +If a mock could be mistaken for a neon-gradient SaaS landing page, or ignores the +user's system accent and light/dark preference — start over.