@polyxd/ds-kit 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICE CHANGED
@@ -135,6 +135,51 @@ Upstream: https://github.com/adobe/spectrum-tokens
135
135
 
136
136
  Vendored under `packages/ds-spectrum/scripts/sources/`, with the upstream licence file beside it.
137
137
 
138
+ ## Logos
139
+
140
+ Twelve packs ship their design system's official logo, unaltered, as `logo.svg`
141
+ (Bootstrap and Radix also ship the owner's white version as `logo-white.svg`),
142
+ downloaded from the owner's own site, repository or brand page. Where a design
143
+ system has no mark of its own (Polaris, Spectrum, Primer), the pack ships its
144
+ company's logo. Each is used only to identify the design system a pack is
145
+ modelled on, beside its name. The logos are trademarks of their owners, not
146
+ Polyxd's, and are not covered by Polyxd's licence:
147
+
148
+ - Material 3 (`@polyxd/ds-material3`) — Google LLC
149
+ - Carbon (`@polyxd/ds-carbon`) — IBM Corp.
150
+ - Fluent 2 (`@polyxd/ds-fluent`) — Microsoft Corporation
151
+ - Polaris (`@polyxd/ds-polaris`) — the Shopify logo, Shopify Inc.
152
+ - Spectrum 2 (`@polyxd/ds-spectrum`) — the Adobe logo, Adobe Inc.
153
+ - Primer (`@polyxd/ds-primer`) — the GitHub logo, GitHub, Inc.
154
+ - shadcn/ui (`@polyxd/ds-shadcn`) — shadcn
155
+ - Ant Design (`@polyxd/ds-antd`) — Ant Group
156
+ - Bootstrap (`@polyxd/ds-bootstrap`) — the Bootstrap team
157
+ - Chakra UI (`@polyxd/ds-chakra`) — Chakra Systems Inc.
158
+ - Mantine (`@polyxd/ds-mantine`) — Vitaly Rtishchev
159
+ - Radix UI (`@polyxd/ds-radix`) — WorkOS
160
+
161
+ Google, IBM, Microsoft, Shopify, Adobe and GitHub restrict the use of their
162
+ logos without permission; each pack's manifest (`logo`) and README record the
163
+ source and the owner's guidelines. GOV.UK's crown and logotype are protected and
164
+ may not be used by a service that is not on GOV.UK, so `@polyxd/ds-govuk` ships
165
+ no logo and is named in words only.
166
+
167
+ The twelve template packs' `logo.svg` files are Polyxd's own marks, drawn from
168
+ each template's tokens.
169
+
170
+ ## Template packs
171
+
172
+ Twelve packs are original Polyxd templates, not readings of any design system:
173
+ `@polyxd/ds-sketch`, `@polyxd/ds-wireframe`, `@polyxd/ds-editorial`,
174
+ `@polyxd/ds-brutalist`, `@polyxd/ds-glass`, `@polyxd/ds-terminal`,
175
+ `@polyxd/ds-pastel`, `@polyxd/ds-civic`, `@polyxd/ds-finance`,
176
+ `@polyxd/ds-health`, `@polyxd/ds-neon` and `@polyxd/ds-mono`. Every value in
177
+ them was chosen for the template, they vendor nothing, and they are Apache-2.0
178
+ like the rest of Polyxd. The typefaces some of them name (Caveat, Patrick Hand,
179
+ Fraunces, Nunito, JetBrains Mono, Space Grotesk) are under the SIL Open Font
180
+ License and are loaded from Google Fonts by the page that uses them, never
181
+ distributed here.
182
+
138
183
  ## Specification
139
184
 
140
185
  ### A2UI
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @polyxd/ds-kit
2
+
3
+ The `polyxd` command, and the shared parts of building a Polyxd design-system pack: reading CSS custom properties, converting values to DTCG, writing the tiers and the manifest.
4
+
5
+ ```sh
6
+ npx polyxd pack ./src/tokens.css # a pack from your tokens, plus a mapping to correct
7
+ npx polyxd check ./ds-acme/manifest.json # the pack against the semantic token contract
8
+ npx polyxd studio push ./ --to https://studio.polyxd.com/api/w/acme
9
+ npx polyxd dev ./screens # preview the documents in a folder as you edit them
10
+ ```
11
+
12
+ ## polyxd dev
13
+
14
+ A local preview for people who write Polyxd documents in their editor.
15
+
16
+ ```sh
17
+ npx polyxd dev [dir] [--port 4310] [--theme material3] [--data <file.json>] [--pack <manifest.json|theme.css>] [--verify] [--open]
18
+ ```
19
+
20
+ It watches `dir` (default: the current folder) for documents: a JSON file with `specVersion` and `components`, or an intent-shaped file with a `document` inside (the document and its `data` are used). A document without embedded data takes `<name>.data.json` beside it, else the file given with `--data`. Packs, registries, reports and other JSON are ignored.
21
+
22
+ The page at `http://localhost:4310/` lists the documents with a dot for the static check (green clean, amber warnings, red errors), renders the selected one with the real renderer, and lets you switch between all built-in packs (plus one of yours with `--pack`), light and dark, phone, tablet and desktop widths with a draggable edge, and density. Beside the surface: the static check's issues, run in the page with `@polyxd/spec/browser` against the data (a binding that reads nothing is a warning); the document JSON with a copy button; the data, and the data as edited in the surface; and a log of the actions the surface dispatches, with their context resolved. Save any file and the surface reloads in place, keeping your selection and controls.
23
+
24
+ `--verify` adds a button that runs the full verifier for the selected document (13 packs × light/dark × 390/1100 with axe and the layout check) and streams its output into the panel. It needs `@polyxd/verifier` installed (`npm install -D @polyxd/verifier playwright && npx playwright install chromium`).
25
+
26
+ The renderer comes from `@polyxd/react/preview`, a single script and stylesheet with React, the renderer, the validator and every theme, so nothing is built on your side. Everything else is Node: `http`, `fs.watch`, and server-sent events.
27
+
28
+ ## Library
29
+
30
+ ```ts
31
+ import { cssVars, resolveVars, length, duration, bezier, shadowLayers, writePack } from "@polyxd/ds-kit";
32
+ import { inferMapping } from "@polyxd/ds-kit/infer";
33
+ ```
34
+
35
+ `cssVars` reads every custom property under the selectors you choose, `resolveVars` follows `var()` references, the converters turn CSS values into DTCG dimensions, durations, easings and shadows, and `writePack` writes the tiers, the manifest and a package.json. `inferMapping` guesses which of your variables fill which contract tokens, from their names and, failing that, by measuring them; `polyxd pack` is built on it.
36
+
37
+ [Docs](https://polyxd.com/docs/your-design-system/) · [Polyxd](https://polyxd.com) · [Source](https://github.com/visualfart/polyxd)
@@ -0,0 +1,9 @@
1
+ export interface PackCss {
2
+ /** The value the surface's data-pxd-theme attribute takes. */
3
+ name: string;
4
+ css: string;
5
+ }
6
+ /** CSS declarations for one resolved token; composite typography expands into its parts. */
7
+ export declare function declarations(name: string, type: string, value: unknown): [string, string][];
8
+ /** A pack as theme CSS: from its manifest (compiled here) or from a stylesheet (served as is). */
9
+ export declare function packCss(path: string): Promise<PackCss>;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * `polyxd dev --pack`: a pack of your own in the preview's pack switcher, next to the built-in ones.
3
+ *
4
+ * Takes a manifest (what `polyxd pack` writes) and compiles its semantic tier to the CSS variables
5
+ * the renderer reads, the way @polyxd/react's theme build does; or an already compiled theme
6
+ * stylesheet, served as it is.
7
+ */
8
+ import { readFile } from "node:fs/promises";
9
+ import { basename } from "node:path";
10
+ import { loadContract, loadDesignSystem } from "@polyxd/spec";
11
+ const cssVar = (token) => `--pxd-${token.replace(/\./g, "-")}`;
12
+ const dim = (v) => (typeof v === "object" && v && "unit" in v ? `${v.value}${v.unit}` : String(v));
13
+ function color(v) {
14
+ if (typeof v === "string")
15
+ return v;
16
+ const c = v;
17
+ if (c.alpha !== undefined && c.alpha < 1 && c.colorSpace === "srgb") {
18
+ const [r, g, b] = c.components.map((x) => Math.round(x * 255));
19
+ return `rgb(${r} ${g} ${b} / ${c.alpha})`;
20
+ }
21
+ return c.hex ?? `color(${c.colorSpace} ${c.components.join(" ")})`;
22
+ }
23
+ const GENERIC = /^(serif|sans-serif|monospace|cursive|fantasy|system-ui|ui-sans-serif|ui-serif|ui-monospace|ui-rounded|math|emoji|fangsong|-apple-system|BlinkMacSystemFont)$/;
24
+ function family(v) {
25
+ const names = (Array.isArray(v) ? v : String(v).split(",")).map((f) => String(f).trim().replace(/^["']|["']$/g, ""));
26
+ const css = names.map((f) => (GENERIC.test(f) || /^[A-Za-z_][\w-]*$/.test(f) ? f : `"${f}"`));
27
+ if (!names.some((f) => /^(serif|sans-serif|monospace|system-ui|ui-sans-serif|ui-serif|ui-monospace)$/.test(f)))
28
+ css.push("system-ui", "sans-serif");
29
+ return css.join(", ");
30
+ }
31
+ /** CSS declarations for one resolved token; composite typography expands into its parts. */
32
+ export function declarations(name, type, value) {
33
+ const v = cssVar(name);
34
+ switch (type) {
35
+ case "color":
36
+ return [[v, color(value)]];
37
+ case "dimension":
38
+ case "duration":
39
+ return [[v, dim(value)]];
40
+ case "number":
41
+ return [[v, String(value)]];
42
+ case "cubicBezier":
43
+ return [[v, `cubic-bezier(${value.join(", ")})`]];
44
+ case "shadow": {
45
+ const one = (s) => `${s.inset ? "inset " : ""}${dim(s.offsetX)} ${dim(s.offsetY)} ${dim(s.blur)} ${dim(s.spread)} ${color(s.color)}`;
46
+ return [[v, Array.isArray(value) ? value.map(one).join(", ") : one(value)]];
47
+ }
48
+ case "fontFamily":
49
+ return [[v, family(value)]];
50
+ case "typography": {
51
+ const t = value;
52
+ return [
53
+ [`${v}-family`, family(t.fontFamily)],
54
+ [`${v}-size`, dim(t.fontSize)],
55
+ [`${v}-weight`, String(t.fontWeight)],
56
+ [`${v}-line-height`, dim(t.lineHeight)],
57
+ [`${v}-letter-spacing`, t.letterSpacing === undefined ? "normal" : dim(t.letterSpacing)],
58
+ ];
59
+ }
60
+ default:
61
+ return [[v, String(value)]];
62
+ }
63
+ }
64
+ /** A pack as theme CSS: from its manifest (compiled here) or from a stylesheet (served as is). */
65
+ export async function packCss(path) {
66
+ if (path.endsWith(".css")) {
67
+ const css = await readFile(path, "utf8");
68
+ const named = /data-pxd-theme=["']?([a-z0-9-]+)/i.exec(css);
69
+ return { name: named?.[1] ?? basename(path, ".css"), css };
70
+ }
71
+ const [{ manifest, modes }, contract] = await Promise.all([loadDesignSystem(path), loadContract()]);
72
+ const blocks = [`/* ${manifest.name}, compiled by polyxd dev from ${basename(path)} */`];
73
+ const missing = [];
74
+ for (const [mode, tokens] of modes) {
75
+ const lines = [];
76
+ for (const name of Object.keys(contract.tokens)) {
77
+ const t = tokens.get(name);
78
+ if (!t) {
79
+ missing.push(`${mode}: ${name}`);
80
+ continue;
81
+ }
82
+ for (const [k, val] of declarations(name, t.type, t.value))
83
+ lines.push(` ${k}: ${val};`);
84
+ }
85
+ for (const [k, v] of Object.entries(manifest.layout ?? {}))
86
+ lines.push(` --pxd-layout-${k}: ${v};`);
87
+ lines.push(` color-scheme: ${mode === "dark" ? "dark" : "light"};`);
88
+ const selector = modes.size === 1
89
+ ? `[data-pxd-theme="${manifest.name}"]`
90
+ : mode === manifest.defaultMode
91
+ ? `[data-pxd-theme="${manifest.name}"]:not([data-pxd-mode]),\n[data-pxd-theme="${manifest.name}"][data-pxd-mode="${mode}"]`
92
+ : `[data-pxd-theme="${manifest.name}"][data-pxd-mode="${mode}"]`;
93
+ blocks.push(`${selector} {\n${lines.join("\n")}\n}`);
94
+ }
95
+ // A pack still being drafted renders with what it has; the contract check is `polyxd check`'s job.
96
+ if (missing.length)
97
+ blocks.push(`/* ${missing.length} contract token(s) not in this pack: ${missing.slice(0, 8).join(", ")}${missing.length > 8 ? ", …" : ""} */`);
98
+ return { name: manifest.name, css: blocks.join("\n\n") + "\n" };
99
+ }
100
+ //# sourceMappingURL=dev-pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-pack.js","sourceRoot":"","sources":["../src/dev-pack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The `polyxd dev` page: plain HTML, CSS and JS shipped with the package, so a preview needs no
3
+ * build step on the author's side. Its chrome is in the system font; only the surface is in the
4
+ * pack's. Everything it knows about the folder comes from /api/documents and the event stream.
5
+ */
6
+ export interface PageOptions {
7
+ dir: string;
8
+ /** The pack selected on first open. */
9
+ theme: string;
10
+ /** A pack added with --pack, by name. */
11
+ pack?: string;
12
+ verify: boolean;
13
+ /** Whether the renderer bundle is installed. */
14
+ bundle: boolean;
15
+ themes: string[];
16
+ }
17
+ export declare function devPage(options: PageOptions): string;
@@ -0,0 +1,359 @@
1
+ export function devPage(options) {
2
+ return PAGE.replace("__OPTIONS__", JSON.stringify(options).replace(/</g, "\\u003c"));
3
+ }
4
+ const PAGE = `<!doctype html>
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <title>polyxd dev</title>
10
+ <link rel="stylesheet" href="/preview/polyxd.css">
11
+ <link rel="stylesheet" href="/pack.css" id="packcss">
12
+ <style>
13
+ :root { color-scheme: light; --bg: #f4f4f6; --panel: #ffffff; --line: #dcdce2; --ink: #1c1c22; --muted: #6a6a75; --accent: #3554d1; --ok: #2e9e5b; --warn: #d99a1b; --err: #d0392b; }
14
+ * { box-sizing: border-box; }
15
+ html, body { height: 100%; margin: 0; }
16
+ body { font: 13px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif; color: var(--ink); background: var(--bg); display: flex; flex-direction: column; }
17
+ button, select { font: inherit; color: inherit; }
18
+ code, pre { font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
19
+ .bar { display: flex; align-items: center; gap: 16px; padding: 8px 14px; background: var(--panel); border-bottom: 1px solid var(--line); flex-wrap: wrap; }
20
+ .bar h1 { font-size: 14px; margin: 0; font-weight: 600; }
21
+ .bar .dir { color: var(--muted); font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
22
+ .controls { display: flex; align-items: center; gap: 12px; margin-left: auto; flex-wrap: wrap; }
23
+ .controls label { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); }
24
+ select { padding: 4px 6px; border: 1px solid var(--line); border-radius: 6px; background: #fff; }
25
+ .seg { display: inline-flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
26
+ .seg button { border: 0; background: #fff; padding: 4px 10px; cursor: pointer; border-right: 1px solid var(--line); }
27
+ .seg button:last-child { border-right: 0; }
28
+ .seg button[aria-pressed="true"] { background: var(--ink); color: #fff; }
29
+ .status { color: var(--muted); min-width: 120px; }
30
+ .status.live::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--ok); margin-right: 6px; }
31
+ .status.off::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--err); margin-right: 6px; }
32
+ .layout { flex: 1; display: grid; grid-template-columns: 260px 1fr 400px; min-height: 0; }
33
+ .list { border-right: 1px solid var(--line); background: var(--panel); overflow: auto; padding: 8px 0; }
34
+ .list h2 { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 8px 14px 4px; font-weight: 600; }
35
+ .doc { display: grid; grid-template-columns: 12px 1fr; gap: 0 8px; width: 100%; text-align: left; border: 0; background: none; padding: 7px 14px; cursor: pointer; }
36
+ .doc:hover { background: #f0f0f4; }
37
+ .doc[aria-current="true"] { background: #e8ecfb; }
38
+ .doc .dot { width: 9px; height: 9px; border-radius: 50%; margin-top: 5px; background: var(--ok); }
39
+ .doc .dot.warn { background: var(--warn); }
40
+ .doc .dot.err { background: var(--err); }
41
+ .doc .t { font-weight: 500; }
42
+ .doc .m { color: var(--muted); font-size: 12px; font-family: ui-monospace, Menlo, monospace; }
43
+ .doc .k { color: var(--muted); font-size: 11px; }
44
+ .stage { overflow: auto; padding: 20px; display: flex; justify-content: center; align-items: flex-start; }
45
+ .frame { position: relative; flex: none; min-height: 60%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.08), 0 0 0 1px var(--line); border-radius: 6px; overflow: visible; }
46
+ .frame .grip { position: absolute; top: 0; right: -14px; width: 10px; height: 100%; cursor: ew-resize; border-radius: 4px; background: transparent; }
47
+ .frame .grip::after { content: ""; position: absolute; top: 50%; left: 3px; width: 4px; height: 36px; margin-top: -18px; border-radius: 2px; background: #c2c2cc; }
48
+ .frame .grip:hover::after, .frame .grip.on::after { background: var(--accent); }
49
+ #surface { min-height: 200px; }
50
+ .empty { padding: 40px; color: var(--muted); text-align: center; }
51
+ .panel { border-left: 1px solid var(--line); background: var(--panel); display: flex; flex-direction: column; min-height: 0; }
52
+ .tabs { display: flex; border-bottom: 1px solid var(--line); }
53
+ .tabs button { flex: 1; border: 0; background: none; padding: 9px 4px; cursor: pointer; color: var(--muted); border-bottom: 2px solid transparent; font-size: 12px; }
54
+ .tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
55
+ .tabs button b { font-weight: 600; }
56
+ .pane { display: none; overflow: auto; flex: 1; padding: 12px 14px; }
57
+ .pane.on { display: block; }
58
+ .pane pre { margin: 0; white-space: pre-wrap; word-break: break-word; background: #f6f6f8; border: 1px solid var(--line); border-radius: 6px; padding: 10px; }
59
+ .pane h3 { font-size: 12px; margin: 14px 0 6px; color: var(--muted); font-weight: 600; }
60
+ .pane h3:first-child { margin-top: 0; }
61
+ .note { color: var(--muted); margin: 0 0 10px; }
62
+ .toolrow { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
63
+ .btn { border: 1px solid var(--line); background: #fff; border-radius: 6px; padding: 4px 10px; cursor: pointer; }
64
+ .btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
65
+ .btn[disabled] { opacity: .5; cursor: default; }
66
+ .issue { display: grid; grid-template-columns: 52px 1fr; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--line); }
67
+ .issue .sev { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em; padding-top: 2px; }
68
+ .issue .sev.error { color: var(--err); }
69
+ .issue .sev.warning { color: var(--warn); }
70
+ .issue .at { font-family: ui-monospace, Menlo, monospace; font-size: 11px; color: var(--muted); }
71
+ .action { padding: 8px 0; border-bottom: 1px solid var(--line); }
72
+ .action .n { font-weight: 600; }
73
+ .action .s { color: var(--muted); font-size: 12px; }
74
+ .action pre { margin-top: 4px; }
75
+ .verify-out { font-size: 11px; max-height: none; }
76
+ .verify-out .err { color: var(--err); }
77
+ .warning-box { background: #fff7e6; border: 1px solid #f0d9a0; padding: 10px; border-radius: 6px; margin-bottom: 10px; }
78
+ @media (max-width: 1100px) { .layout { grid-template-columns: 220px 1fr; } .panel { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--line); max-height: 40vh; } }
79
+ </style>
80
+ </head>
81
+ <body>
82
+ <header class="bar">
83
+ <h1>polyxd dev</h1>
84
+ <span class="dir" id="dir"></span>
85
+ <div class="controls">
86
+ <label>Pack <select id="theme" aria-label="Design-system pack"></select></label>
87
+ <div class="seg" id="mode" role="group" aria-label="Mode"><button data-v="light">Light</button><button data-v="dark">Dark</button></div>
88
+ <div class="seg" id="width" role="group" aria-label="Width"><button data-v="390">Phone</button><button data-v="820">Tablet</button><button data-v="1100">Desktop</button></div>
89
+ <span id="widthpx" class="dir">390px</span>
90
+ <label>Density <select id="density" aria-label="Density"><option value="comfortable">comfortable</option><option value="compact">compact</option><option value="spacious">spacious</option></select></label>
91
+ <span class="status" id="status" aria-live="polite"></span>
92
+ </div>
93
+ </header>
94
+ <main class="layout">
95
+ <nav class="list" id="list" aria-label="Documents"></nav>
96
+ <section class="stage" id="stage">
97
+ <div class="frame" id="frame" style="width:390px">
98
+ <div id="surface"></div>
99
+ <div class="grip" id="grip" title="Drag to resize" role="separator" aria-orientation="vertical" aria-label="Resize the surface"></div>
100
+ </div>
101
+ </section>
102
+ <aside class="panel">
103
+ <div class="tabs" role="tablist" aria-label="Panel">
104
+ <button role="tab" data-tab="issues" id="tab-issues">Issues</button>
105
+ <button role="tab" data-tab="document">Document</button>
106
+ <button role="tab" data-tab="data">Data</button>
107
+ <button role="tab" data-tab="actions" id="tab-actions">Actions</button>
108
+ <button role="tab" data-tab="verify">Verify</button>
109
+ </div>
110
+ <div class="pane" id="pane-issues" role="tabpanel"></div>
111
+ <div class="pane" id="pane-document" role="tabpanel">
112
+ <div class="toolrow"><button class="btn" id="copy-doc">Copy JSON</button><span class="note" id="doc-note"></span></div>
113
+ <pre id="doc-json"></pre>
114
+ </div>
115
+ <div class="pane" id="pane-data" role="tabpanel"></div>
116
+ <div class="pane" id="pane-actions" role="tabpanel"></div>
117
+ <div class="pane" id="pane-verify" role="tabpanel"></div>
118
+ </aside>
119
+ </main>
120
+ <script src="/preview/polyxd.js"></script>
121
+ <script>
122
+ (function () {
123
+ var O = __OPTIONS__;
124
+ var P = window.Polyxd;
125
+ var $ = function (id) { return document.getElementById(id); };
126
+ var esc = function (s) { return String(s).replace(/[&<>"]/g, function (c) { return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]; }); };
127
+ var pretty = function (v) { return JSON.stringify(v, null, 2); };
128
+ var time = function () { return new Date().toLocaleTimeString("en-GB"); };
129
+
130
+ // ---- state, kept across reloads of this page; the event stream never reloads it ----
131
+ var KEY = "polyxd-dev:" + O.dir;
132
+ var state = { selected: null, theme: O.theme, mode: "light", width: 390, density: "comfortable", tab: "issues" };
133
+ try { Object.assign(state, JSON.parse(localStorage.getItem(KEY) || "{}")); } catch (e) {}
134
+ var save = function () { try { localStorage.setItem(KEY, JSON.stringify(state)); } catch (e) {} };
135
+
136
+ var docs = [], broken = [], current = null, handle = null, versions = {}, actions = [], liveData = null, verifyStream = null;
137
+ var themes = (P && P.themes) || O.themes;
138
+ if (O.pack && themes.indexOf(O.pack) < 0) themes = [O.pack].concat(themes);
139
+ if (themes.indexOf(state.theme) < 0) state.theme = O.theme;
140
+ if (!O.pack) $("packcss").remove();
141
+
142
+ // ---- controls ----
143
+ $("dir").textContent = O.dir;
144
+ var themeSel = $("theme");
145
+ themes.forEach(function (t) { var o = document.createElement("option"); o.value = t; o.textContent = t === O.pack ? t + " (--pack)" : t; themeSel.appendChild(o); });
146
+ themeSel.value = state.theme;
147
+ themeSel.onchange = function () { state.theme = themeSel.value; save(); render(); };
148
+ var dens = $("density");
149
+ dens.value = state.density;
150
+ dens.onchange = function () { state.density = dens.value; save(); render(); };
151
+ var seg = function (id, get, set) {
152
+ var el = $(id);
153
+ var paint = function () { Array.prototype.forEach.call(el.querySelectorAll("button"), function (b) { b.setAttribute("aria-pressed", String(b.dataset.v === String(get()))); }); };
154
+ el.onclick = function (e) { var b = e.target.closest("button"); if (!b) return; set(b.dataset.v); paint(); };
155
+ paint();
156
+ return paint;
157
+ };
158
+ var paintMode = seg("mode", function () { return state.mode; }, function (v) { state.mode = v; save(); render(); });
159
+ var paintWidth = seg("width", function () { return state.width; }, function (v) { setWidth(Number(v)); });
160
+ function setWidth(w) {
161
+ var max = Math.max(320, $("stage").clientWidth - 60);
162
+ state.width = Math.round(Math.min(max, Math.max(320, w)));
163
+ $("frame").style.width = state.width + "px";
164
+ $("widthpx").textContent = state.width + "px";
165
+ paintWidth();
166
+ save();
167
+ }
168
+ setWidth(state.width);
169
+ // The grip: drag the surface's edge to any width between phone and the stage.
170
+ (function () {
171
+ var grip = $("grip"), startX = 0, startW = 0;
172
+ var move = function (e) { setWidth(startW + (e.clientX - startX)); };
173
+ var up = function () { grip.classList.remove("on"); window.removeEventListener("pointermove", move); window.removeEventListener("pointerup", up); };
174
+ grip.addEventListener("pointerdown", function (e) { startX = e.clientX; startW = state.width; grip.classList.add("on"); window.addEventListener("pointermove", move); window.addEventListener("pointerup", up); e.preventDefault(); });
175
+ grip.addEventListener("keydown", function (e) { if (e.key === "ArrowLeft") setWidth(state.width - 20); if (e.key === "ArrowRight") setWidth(state.width + 20); });
176
+ grip.tabIndex = 0;
177
+ })();
178
+
179
+ // ---- tabs ----
180
+ var tabs = document.querySelectorAll(".tabs button");
181
+ var showTab = function (name) {
182
+ state.tab = name; save();
183
+ Array.prototype.forEach.call(tabs, function (b) { b.setAttribute("aria-selected", String(b.dataset.tab === name)); });
184
+ Array.prototype.forEach.call(document.querySelectorAll(".pane"), function (p) { p.classList.toggle("on", p.id === "pane-" + name); });
185
+ };
186
+ Array.prototype.forEach.call(tabs, function (b) { b.onclick = function () { showTab(b.dataset.tab); }; });
187
+ showTab(state.tab);
188
+
189
+ // ---- the document list ----
190
+ var dot = function (check) { return check.errors ? "err" : check.warnings ? "warn" : "ok"; };
191
+ function paintList() {
192
+ var list = $("list");
193
+ var html = "<h2>" + docs.length + " document" + (docs.length === 1 ? "" : "s") + "</h2>";
194
+ if (!docs.length) html += '<p class="note" style="padding:0 14px">No documents here yet. A document is a JSON file with <code>specVersion</code> and <code>components</code>, or an intent file with a <code>document</code> inside.</p>';
195
+ docs.forEach(function (d) {
196
+ var kind = [d.kind, d.origin, d.dataFrom === "none" ? "no data" : d.dataFrom === "embedded" ? null : "data: " + d.dataFrom].filter(Boolean).join(" · ");
197
+ var title = d.check.errors + " error(s), " + d.check.warnings + " warning(s)";
198
+ html += '<button class="doc" data-file="' + esc(d.file) + '" aria-current="' + (d.file === state.selected) + '" title="' + esc(title) + '">' +
199
+ '<span class="dot ' + dot(d.check) + '" aria-label="' + esc(title) + '"></span>' +
200
+ '<span><span class="t">' + esc(d.title) + '</span><br><span class="m">' + esc(d.id || d.file) + '</span><br><span class="k">' + esc(kind) + '</span></span></button>';
201
+ });
202
+ if (broken.length) {
203
+ html += "<h2>Did not parse</h2>";
204
+ broken.forEach(function (b) { html += '<div class="doc" title="' + esc(b.message) + '"><span class="dot err"></span><span><span class="m">' + esc(b.file) + '</span><br><span class="k">' + esc(b.message) + '</span></span></div>'; });
205
+ }
206
+ list.innerHTML = html;
207
+ Array.prototype.forEach.call(list.querySelectorAll("button.doc"), function (b) { b.onclick = function () { select(b.dataset.file); }; });
208
+ }
209
+
210
+ // ---- the selected document ----
211
+ function select(file) {
212
+ if (!docs.some(function (d) { return d.file === file; })) file = docs.length ? docs[0].file : null;
213
+ var changed = file !== state.selected;
214
+ state.selected = file; save();
215
+ paintList();
216
+ if (!file) { current = null; unmount(); $("surface").innerHTML = '<p class="empty">Nothing to render.</p>'; paintPanels(); return; }
217
+ if (changed) { actions = []; liveData = null; stopVerify(); }
218
+ fetch("/api/documents/" + encodeURIComponent(file).replace(/%2F/g, "/")).then(function (r) { return r.json(); }).then(function (d) {
219
+ if (d.error) return;
220
+ current = d;
221
+ versions[file] = (versions[file] || 0) + 1;
222
+ render();
223
+ paintPanels();
224
+ });
225
+ }
226
+ function unmount() { if (handle) { handle.unmount(); handle = null; } }
227
+ function render() {
228
+ if (!current) return;
229
+ if (!P) { $("surface").innerHTML = '<p class="empty">The renderer bundle isn\\u2019t installed, so nothing renders here: <code>npm install @polyxd/react</code>, then start again.</p>'; return; }
230
+ var file = current.file;
231
+ var props = {
232
+ key: file + ":" + versions[file],
233
+ document: current.document,
234
+ data: current.data,
235
+ theme: state.theme,
236
+ mode: state.mode,
237
+ density: state.density,
238
+ onAction: function (e) { actions.unshift({ at: time(), name: e.name, source: e.source, context: e.context }); paintActions(); },
239
+ onDismiss: function () { actions.unshift({ at: time(), name: "ui.dismiss", source: "(surface)", context: {} }); paintActions(); },
240
+ onDataChange: function (d) { liveData = d; if (state.tab === "data") paintData(); }
241
+ };
242
+ if (handle) handle.update(props);
243
+ else { $("surface").innerHTML = ""; handle = P.mount($("surface"), props); }
244
+ }
245
+
246
+ // ---- panels ----
247
+ function paintPanels() { paintIssues(); paintDocument(); paintData(); paintActions(); paintVerify(); }
248
+ function issuesOf(d) {
249
+ if (P && P.validateDocument) {
250
+ try {
251
+ var doc = d.data !== undefined ? Object.assign({}, d.document, { data: d.data }) : d.document;
252
+ return P.validateDocument(doc, { missingData: "warning" }).issues;
253
+ } catch (e) { return [{ severity: "error", at: "/", message: "validator failed: " + e.message }]; }
254
+ }
255
+ return d.issues || [];
256
+ }
257
+ function paintIssues() {
258
+ var pane = $("pane-issues"), tab = $("tab-issues");
259
+ if (!current) { pane.innerHTML = '<p class="note">Select a document.</p>'; tab.innerHTML = "Issues"; return; }
260
+ var issues = issuesOf(current);
261
+ var errors = issues.filter(function (i) { return i.severity === "error"; }).length;
262
+ var warnings = issues.length - errors;
263
+ tab.innerHTML = "Issues" + (issues.length ? " <b>" + issues.length + "</b>" : "");
264
+ var html = '<p class="note">Static check' + (P ? " (run in this page with @polyxd/spec/browser)" : "") + ": schema, structure and every binding against the data the surface shows. " +
265
+ (issues.length ? errors + " error" + (errors === 1 ? "" : "s") + ", " + warnings + " warning" + (warnings === 1 ? "" : "s") + "." : "Nothing to report.") + "</p>";
266
+ issues.forEach(function (i) {
267
+ html += '<div class="issue"><span class="sev ' + i.severity + '">' + i.severity + "</span><span>" + esc(i.message) + '<br><span class="at">' + esc(i.at) + (i.code ? " · " + esc(i.code) : "") + "</span></span></div>";
268
+ });
269
+ pane.innerHTML = html;
270
+ }
271
+ function paintDocument() {
272
+ if (!current) { $("doc-json").textContent = ""; $("doc-note").textContent = ""; return; }
273
+ $("doc-json").textContent = pretty(current.document);
274
+ $("doc-note").textContent = current.file + " · " + current.document.components.length + " components" + (current.kind === "intent" ? " · from the intent file's document" : "");
275
+ }
276
+ $("copy-doc").onclick = function () {
277
+ if (!current) return;
278
+ var b = $("copy-doc");
279
+ navigator.clipboard.writeText(pretty(current.document)).then(function () { b.textContent = "Copied"; setTimeout(function () { b.textContent = "Copy JSON"; }, 1200); }, function () { b.textContent = "Couldn\\u2019t copy"; });
280
+ };
281
+ function paintData() {
282
+ var pane = $("pane-data");
283
+ if (!current) { pane.innerHTML = '<p class="note">Select a document.</p>'; return; }
284
+ var from = { embedded: "the document\\u2019s own data", sibling: "the sibling .data.json file", "default": "--data", none: "nowhere: this document has no data" }[current.dataFrom];
285
+ var html = "<h3>Data from " + esc(from) + "</h3>";
286
+ html += current.data === undefined ? '<p class="note">Bindings will render blank. Put a <code>data</code> snapshot in the document, a <code>' + esc(current.file.replace(/\\.json$/, ".data.json")) + "</code> beside it, or pass <code>--data</code>.</p>" : "<pre>" + esc(pretty(current.data)) + "</pre>";
287
+ if (liveData) html += "<h3>As edited in the surface</h3><pre>" + esc(pretty(liveData)) + "</pre>";
288
+ pane.innerHTML = html;
289
+ }
290
+ function paintActions() {
291
+ var pane = $("pane-actions"), tab = $("tab-actions");
292
+ tab.innerHTML = "Actions" + (actions.length ? " <b>" + actions.length + "</b>" : "");
293
+ if (!actions.length) { pane.innerHTML = '<p class="note">Actions the surface dispatches show here with their context resolved against the data: press a button, choose a row, submit a form.</p>'; return; }
294
+ pane.innerHTML = '<div class="toolrow"><button class="btn" id="clear-actions">Clear</button></div>' + actions.map(function (a) {
295
+ return '<div class="action"><span class="n">' + esc(a.name) + '</span> <span class="s">from ' + esc(a.source) + " · " + a.at + "</span><pre>" + esc(pretty(a.context)) + "</pre></div>";
296
+ }).join("");
297
+ $("clear-actions").onclick = function () { actions = []; paintActions(); };
298
+ }
299
+
300
+ // ---- verify: the full verifier, streamed ----
301
+ function stopVerify() { if (verifyStream) { verifyStream.close(); verifyStream = null; } }
302
+ function paintVerify() {
303
+ var pane = $("pane-verify");
304
+ if (!O.verify) { pane.innerHTML = '<p class="note">Start with <code>--verify</code> to run the full verifier from here: every built-in pack, light and dark, phone and desktop, with axe and the layout check. It needs <code>@polyxd/verifier</code> installed.</p>'; return; }
305
+ pane.innerHTML = '<div class="toolrow"><button class="btn primary" id="run-verify"' + (current ? "" : " disabled") + ">Verify in " + O.themes.length + ' packs</button><span class="note" id="verify-note"></span></div><pre class="verify-out" id="verify-out"></pre>';
306
+ $("run-verify").onclick = runVerify;
307
+ }
308
+ function runVerify() {
309
+ if (!current) return;
310
+ stopVerify();
311
+ var out = $("verify-out"), btn = $("run-verify"), note = $("verify-note");
312
+ out.textContent = ""; btn.disabled = true; note.textContent = "Running\\u2026 (26 renders; a minute or so)";
313
+ verifyStream = new EventSource("/api/verify/" + encodeURIComponent(current.file).replace(/%2F/g, "/"));
314
+ verifyStream.addEventListener("line", function (e) {
315
+ var d = JSON.parse(e.data);
316
+ var span = document.createElement("span");
317
+ span.className = d.stream === "err" ? "err" : "";
318
+ span.textContent = d.text + "\\n";
319
+ out.appendChild(span);
320
+ out.scrollTop = out.scrollHeight;
321
+ });
322
+ verifyStream.addEventListener("done", function (e) {
323
+ var d = JSON.parse(e.data);
324
+ note.textContent = d.code === 0 ? "Done: no errors." : d.code === 1 ? "Done: errors found." : "Did not run.";
325
+ btn.disabled = false;
326
+ stopVerify();
327
+ });
328
+ verifyStream.onerror = function () { note.textContent = "Stream ended."; btn.disabled = false; stopVerify(); };
329
+ }
330
+
331
+ // ---- the folder, live ----
332
+ function apply(listing) {
333
+ docs = listing.documents; broken = listing.broken || [];
334
+ paintList();
335
+ }
336
+ function setStatus(text, cls) { var s = $("status"); s.textContent = text; s.className = "status " + (cls || ""); }
337
+ var events = new EventSource("/api/events");
338
+ events.addEventListener("hello", function (e) {
339
+ apply(JSON.parse(e.data));
340
+ setStatus("watching", "live");
341
+ if (!current || !docs.some(function (d) { return d.file === state.selected; })) select(state.selected);
342
+ });
343
+ events.addEventListener("change", function (e) {
344
+ var d = JSON.parse(e.data);
345
+ apply(d);
346
+ if (state.selected && d.removed.indexOf(state.selected) >= 0) { select(null); setStatus("removed " + time(), "live"); return; }
347
+ if (state.selected && d.changed.indexOf(state.selected) >= 0) { select(state.selected); setStatus("reloaded " + time(), "live"); }
348
+ else if (!state.selected && docs.length) select(docs[0].file);
349
+ else setStatus("watching", "live");
350
+ });
351
+ events.addEventListener("bye", function () { setStatus("server stopped", "off"); });
352
+ events.onerror = function () { setStatus("reconnecting\\u2026", "off"); };
353
+ window.addEventListener("resize", function () { setWidth(state.width); });
354
+ })();
355
+ </script>
356
+ </body>
357
+ </html>
358
+ `;
359
+ //# sourceMappingURL=dev-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-page.js","sourceRoot":"","sources":["../src/dev-page.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,OAAO,CAAC,OAAoB;IAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkWZ,CAAC"}