@synopackageland/ui 0.6.0 → 0.7.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/README.md CHANGED
@@ -7,7 +7,7 @@ Import the components you need into your Vue application.
7
7
 
8
8
  For DSM window / full-page Portal / phone layouts, see
9
9
  [Responsive DSM application layout](docs/adaptive-layout.md). To generate UI
10
- as JSON (including with an AI and the `syno-ui` validate/render CLI), see
10
+ as JSON (including with an AI and the `syno-pkg-land-ui` validate/render CLI), see
11
11
  [Declarative DSM UI](docs/json-ui.md).
12
12
 
13
13
  Synology Package Land UI provides Vue components for Synology package applications,
package/dist/cli.js CHANGED
@@ -1,83 +1,90 @@
1
1
  #!/usr/bin/env node
2
- import { readFile as v, mkdtemp as E, mkdir as M, writeFile as D, rm as R } from "node:fs/promises";
2
+ import { readFile as S, mkdtemp as E, mkdir as M, writeFile as D, rm as R } from "node:fs/promises";
3
3
  import { createServer as F } from "node:http";
4
4
  import { tmpdir as H } from "node:os";
5
- import { join as w, win32 as U, posix as j } from "node:path";
5
+ import { join as g, win32 as U, posix as j } from "node:path";
6
6
  import { spawn as I } from "node:child_process";
7
- import { existsSync as g } from "node:fs";
7
+ import { existsSync as f } from "node:fs";
8
8
  import { setTimeout as L } from "node:timers/promises";
9
9
  import { synoJsonAiPrompt as T, synoJsonSchema as W, describeSynoJsonComponent as _, closestSynoJsonComponent as V, listSynoJsonComponents as B, validateSynoJsonSpec as G } from "./json.js";
10
10
  const K = [
11
11
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
12
12
  "/Applications/Chromium.app/Contents/MacOS/Chromium"
13
13
  ], X = ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"], z = ["ProgramFiles", "ProgramFiles(x86)", "LOCALAPPDATA"];
14
- function Q(e = process.env, t = process.platform, n = g) {
15
- return e.CHROME ? e.CHROME : (t === "darwin" ? K : t === "win32" ? z.flatMap((o) => e[o] ? [U.join(e[o], "Google", "Chrome", "Application", "chrome.exe")] : []) : (e.PATH ?? "").split(j.delimiter).filter(Boolean).flatMap((o) => X.map((r) => j.join(o, r)))).find(n);
14
+ function Q(e = process.env, t = process.platform, n = f) {
15
+ return e.CHROME ? e.CHROME : (t === "darwin" ? K : t === "win32" ? z.flatMap((r) => e[r] ? [U.join(e[r], "Google", "Chrome", "Application", "chrome.exe")] : []) : (e.PATH ?? "").split(j.delimiter).filter(Boolean).flatMap((r) => X.map((o) => j.join(r, o)))).find(n);
16
16
  }
17
- async function Y(e) {
18
- const t = Q();
19
- if (!t) throw new Error("Chrome or Chromium was not found; set CHROME to its executable path.");
20
- const n = I(t, [
17
+ function Y(e, t = process.env) {
18
+ return [
21
19
  "--headless=new",
22
20
  "--disable-gpu",
23
21
  "--hide-scrollbars",
24
22
  "--no-first-run",
23
+ // Repaint whole tiles: partial raster redraws only the invalidated rect, and
24
+ // the anti-aliased edges it touches can come out one colour level apart, so
25
+ // the same page could otherwise screenshot differently from run to run.
26
+ "--disable-partial-raster",
25
27
  // GitHub's Ubuntu AppArmor policy blocks the unprivileged Chromium sandbox.
26
28
  // Match Playwright's CI launch defaults for these repository-only fixtures.
27
- ...process.env.CI ? ["--no-sandbox", "--disable-dev-shm-usage"] : [],
29
+ ...t.CI ? ["--no-sandbox", "--disable-dev-shm-usage"] : [],
28
30
  "--remote-debugging-port=0",
29
31
  `--user-data-dir=${e}`,
30
32
  "about:blank"
31
- ], { stdio: ["ignore", "ignore", "pipe"] });
32
- let s, o = "";
33
+ ];
34
+ }
35
+ async function Z(e) {
36
+ const t = Q();
37
+ if (!t) throw new Error("Chrome or Chromium was not found; set CHROME to its executable path.");
38
+ const n = I(t, Y(e), { stdio: ["ignore", "ignore", "pipe"] });
39
+ let s, r = "";
33
40
  n.once("error", (a) => {
34
41
  s = a;
35
42
  }), n.stderr.on("data", (a) => {
36
- o = (o + a).slice(-4e3);
43
+ r = (r + a).slice(-4e3);
37
44
  });
38
- const r = w(e, "DevToolsActivePort");
39
- for (let a = 0; a < 100 && !g(r) && !s && n.exitCode === null; a++) await L(100);
40
- if (!g(r))
41
- throw n.kill(), new Error(`Chrome failed to start: ${s?.message ?? (o || "no debugging port within 10 seconds")}`);
42
- const [i] = (await v(r, "utf8")).split(`
45
+ const o = g(e, "DevToolsActivePort");
46
+ for (let a = 0; a < 100 && !f(o) && !s && n.exitCode === null; a++) await L(100);
47
+ if (!f(o))
48
+ throw n.kill(), new Error(`Chrome failed to start: ${s?.message ?? (r || "no debugging port within 10 seconds")}`);
49
+ const [i] = (await S(o, "utf8")).split(`
43
50
  `), c = await (await fetch(`http://127.0.0.1:${i}/json/new?about:blank`, { method: "PUT" })).json(), d = new WebSocket(c.webSocketDebuggerUrl);
44
- await new Promise((a, m) => {
45
- d.onopen = a, d.onerror = m;
51
+ await new Promise((a, u) => {
52
+ d.onopen = a, d.onerror = u;
46
53
  });
47
54
  let l = 1;
48
- const u = /* @__PURE__ */ new Map();
55
+ const m = /* @__PURE__ */ new Map();
49
56
  d.onmessage = (a) => {
50
- const m = JSON.parse(String(a.data));
51
- u.get(m.id)?.(m), u.delete(m.id);
57
+ const u = JSON.parse(String(a.data));
58
+ m.get(u.id)?.(u), m.delete(u.id);
52
59
  };
53
- const b = (a, m = {}) => new Promise((P, A) => {
60
+ const b = (a, u = {}) => new Promise((A, P) => {
54
61
  const O = l++;
55
- u.set(O, (h) => h.error ? A(new Error(h.error.message)) : P(h.result)), d.send(JSON.stringify({ id: O, method: a, params: m }));
62
+ m.set(O, (w) => w.error ? P(new Error(w.error.message)) : A(w.result)), d.send(JSON.stringify({ id: O, method: a, params: u }));
56
63
  });
57
64
  await b("Emulation.setDeviceMetricsOverride", { width: 1100, height: 720, deviceScaleFactor: 1, mobile: !1 });
58
- const k = new Promise((a) => n.once("exit", a));
59
- return { send: b, close: () => (d.close(), n.kill(), k) };
65
+ const N = new Promise((a) => n.once("exit", a));
66
+ return { send: b, close: () => (d.close(), n.kill(), N) };
60
67
  }
61
- const Z = `<!doctype html>
68
+ const q = `<!doctype html>
62
69
  <html><head><meta charset="utf-8"><link rel="stylesheet" href="/theme.css">
63
70
  <style>html, body, #app { margin: 0; height: 100%; }</style></head>
64
71
  <body><div id="app"></div><script type="module" src="/preview.js"><\/script></body></html>`;
65
- function q(e, t) {
72
+ function ee(e, t) {
66
73
  const n = {
67
74
  "/theme.css": [new URL("theme.css", e), "text/css"],
68
75
  "/preview.js": [new URL("json-preview/preview.js", e), "text/javascript"]
69
- }, s = F(async (o, r) => {
70
- const i = new URL(o.url ?? "/", "http://localhost").pathname;
76
+ }, s = F(async (r, o) => {
77
+ const i = new URL(r.url ?? "/", "http://localhost").pathname;
71
78
  try {
72
- const [c, d] = i === "/" ? [Z, "text/html"] : i === "/config.json" ? [t, "application/json"] : [await v(n[i][0]), n[i][1]];
73
- r.writeHead(200, { "content-type": d }).end(c);
79
+ const [c, d] = i === "/" ? [q, "text/html"] : i === "/config.json" ? [t, "application/json"] : [await S(n[i][0]), n[i][1]];
80
+ o.writeHead(200, { "content-type": d }).end(c);
74
81
  } catch {
75
- r.writeHead(404).end();
82
+ o.writeHead(404).end();
76
83
  }
77
84
  });
78
- return new Promise((o) => s.listen(0, "127.0.0.1", () => o(s)));
85
+ return new Promise((r) => s.listen(0, "127.0.0.1", () => r(s)));
79
86
  }
80
- async function ee(e, t) {
87
+ async function te(e, t) {
81
88
  await e.send("Page.navigate", { url: t });
82
89
  const { result: n, exceptionDetails: s } = await e.send(
83
90
  "Runtime.evaluate",
@@ -96,25 +103,25 @@ async function ee(e, t) {
96
103
  if (s) throw new Error(`Rendering failed: ${s.exception?.description ?? "unknown error"}`);
97
104
  return n.value;
98
105
  }
99
- function te(e) {
106
+ function ne(e) {
100
107
  return async (t) => {
101
- const n = await q(e, JSON.stringify({ spec: t.spec, state: t.state, messages: t.messages })), s = `http://127.0.0.1:${n.address().port}/`, o = await E(w(H(), "syno-ui-render-"));
102
- let r;
108
+ const n = await ee(e, JSON.stringify({ spec: t.spec, state: t.state, messages: t.messages })), s = `http://127.0.0.1:${n.address().port}/`, r = await E(g(H(), "syno-pkg-land-ui-render-"));
109
+ let o;
103
110
  try {
104
- r = await Y(o), await M(t.out, { recursive: !0 });
111
+ o = await Z(r), await M(t.out, { recursive: !0 });
105
112
  const i = [];
106
113
  for (const c of t.widths) {
107
- await r.send("Emulation.setDeviceMetricsOverride", { width: c, height: t.height, deviceScaleFactor: 1, mobile: !1 });
108
- const d = await ee(r, `${s}?width=${c}`), { data: l } = await r.send("Page.captureScreenshot", { format: "png" }), u = w(t.out, `${t.name}-${c}.png`);
109
- await D(u, Buffer.from(l, "base64")), i.push({ width: c, screenshot: u, issues: d });
114
+ await o.send("Emulation.setDeviceMetricsOverride", { width: c, height: t.height, deviceScaleFactor: 1, mobile: !1 });
115
+ const d = await te(o, `${s}?width=${c}`), { data: l } = await o.send("Page.captureScreenshot", { format: "png" }), m = g(t.out, `${t.name}-${c}.png`);
116
+ await D(m, Buffer.from(l, "base64")), i.push({ width: c, screenshot: m, issues: d });
110
117
  }
111
118
  return i;
112
119
  } finally {
113
- await r?.close(), n.close(), await R(o, { recursive: !0, force: !0, maxRetries: 12, retryDelay: 250 });
120
+ await o?.close(), n.close(), await R(r, { recursive: !0, force: !0, maxRetries: 12, retryDelay: 250 });
114
121
  }
115
122
  };
116
123
  }
117
- const S = `Usage: syno-ui <command> [options]
124
+ const p = `Usage: syno-pkg-land-ui <command> [options]
118
125
 
119
126
  Commands:
120
127
  catalog [--json] List the JSON UI components
@@ -129,139 +136,141 @@ Commands:
129
136
  --state <file.json> Host-provided state
130
137
  --messages <file.json> Message catalog (enables i18n checks)
131
138
  render <file> [options] Validate, screenshot and report layout overflow (needs Chrome, Node 22+)
132
- --out <dir> Screenshot directory (default syno-ui-render)
139
+ --out <dir> Screenshot directory (default syno-pkg-land-ui-render)
133
140
  --widths 390,640,960,1350 Viewport widths: phone, narrow window, DSM window, wide
134
141
  --height <px> Viewport height (default 800)
135
142
  --state, --messages As for validate; messages also back $computed t()
136
- `, ne = /* @__PURE__ */ new Set(["actions", "functions", "state", "messages", "out", "widths", "height", "wide"]);
137
- function se(e) {
143
+ `, se = /* @__PURE__ */ new Set(["actions", "functions", "state", "messages", "out", "widths", "height", "wide"]);
144
+ function re(e) {
138
145
  const t = [], n = {};
139
146
  for (let s = 0; s < e.length; s++) {
140
- const o = e[s];
141
- if (!o.startsWith("--")) {
142
- t.push(o);
147
+ const r = e[s];
148
+ if (!r.startsWith("--")) {
149
+ t.push(r);
143
150
  continue;
144
151
  }
145
- const [r, i] = o.slice(2).split("=", 2);
146
- n[r] = i ?? (ne.has(r) && e[s + 1] !== void 0 ? e[++s] : !0);
152
+ const [o, i] = r.slice(2).split("=", 2);
153
+ n[o] = i ?? (se.has(o) && e[s + 1] !== void 0 ? e[++s] : !0);
147
154
  }
148
155
  return { positional: t, flags: n };
149
156
  }
150
- const f = (e) => typeof e == "string" ? e.split(",").map((t) => t.trim()).filter(Boolean) : void 0;
157
+ const y = (e) => typeof e == "string" ? e.split(",").map((t) => t.trim()).filter(Boolean) : void 0;
151
158
  function oe(e) {
152
159
  return ` ${e.severity} ${e.code} ${e.path || "/"}: ${e.message}${e.hint ? `
153
160
  hint: ${e.hint}` : ""}
154
161
  `;
155
162
  }
156
- class p extends Error {
163
+ class h extends Error {
157
164
  }
158
165
  async function C(e, t) {
159
166
  try {
160
167
  return await e.readFile(t);
161
168
  } catch (n) {
162
- throw new p(`Cannot read ${t}: ${n instanceof Error ? n.message : String(n)}`);
169
+ throw new h(`Cannot read ${t}: ${n instanceof Error ? n.message : String(n)}`);
163
170
  }
164
171
  }
165
- async function y(e, t) {
172
+ async function $(e, t) {
166
173
  const n = await C(e, t);
167
174
  try {
168
175
  return JSON.parse(n);
169
176
  } catch (s) {
170
- throw new p(`${t} is not valid JSON: ${s instanceof Error ? s.message : String(s)}`);
177
+ throw new h(`${t} is not valid JSON: ${s instanceof Error ? s.message : String(s)}`);
171
178
  }
172
179
  }
173
- async function x(e, t, n) {
180
+ async function k(e, t, n) {
174
181
  const s = await C(e, t);
175
- let o;
182
+ let r;
176
183
  try {
177
- o = JSON.parse(s);
178
- } catch (r) {
179
- return { valid: !1, errors: [{ severity: "error", code: "invalid_json", path: "", message: r instanceof Error ? r.message : String(r), hint: "Return a single JSON object without comments or trailing commas." }], warnings: [] };
184
+ r = JSON.parse(s);
185
+ } catch (o) {
186
+ return { valid: !1, errors: [{ severity: "error", code: "invalid_json", path: "", message: o instanceof Error ? o.message : String(o), hint: "Return a single JSON object without comments or trailing commas." }], warnings: [] };
180
187
  }
181
- return G(o, n);
188
+ return G(r, n);
182
189
  }
183
- async function J(e, t) {
190
+ async function x(e, t) {
184
191
  return {
185
- actions: f(t.actions),
186
- functions: f(t.functions),
187
- state: typeof t.state == "string" ? await y(e, t.state) : void 0,
188
- messages: typeof t.messages == "string" ? await y(e, t.messages) : void 0
192
+ actions: y(t.actions),
193
+ functions: y(t.functions),
194
+ state: typeof t.state == "string" ? await $(e, t.state) : void 0,
195
+ messages: typeof t.messages == "string" ? await $(e, t.messages) : void 0
189
196
  };
190
197
  }
191
- const N = (e, t) => e.errors.length > 0 || !!t.strict && e.warnings.length > 0;
192
- function $(e) {
198
+ const J = (e, t) => e.errors.length > 0 || !!t.strict && e.warnings.length > 0;
199
+ function v(e) {
193
200
  return e.errors.length || e.warnings.length ? `${e.file}: ${e.errors.length} error(s), ${e.warnings.length} warning(s)
194
201
  ${[...e.errors, ...e.warnings].map(oe).join("")}` : `${e.file}: OK
195
202
  `;
196
203
  }
197
- async function re(e, { positional: t, flags: n }) {
204
+ async function ie(e, { positional: t, flags: n }) {
198
205
  if (!t.length)
199
- return e.stderr(S), 2;
200
- const s = await J(e, n), o = [];
201
- for (const r of t) o.push({ file: r, ...await x(e, r, s) });
202
- return e.stdout(n.json ? JSON.stringify(o, null, 2) + `
203
- ` : o.map($).join("")), o.some((r) => N(r, n)) ? 1 : 0;
206
+ return e.stderr(p), 2;
207
+ const s = await x(e, n), r = [];
208
+ for (const o of t) r.push({ file: o, ...await k(e, o, s) });
209
+ return e.stdout(n.json ? JSON.stringify(r, null, 2) + `
210
+ ` : r.map(v).join("")), r.some((o) => J(o, n)) ? 1 : 0;
204
211
  }
205
- const ie = [390, 640, 960, 1350];
206
- async function ae(e, { positional: [t], flags: n }) {
212
+ const ae = [390, 640, 960, 1350];
213
+ async function ce(e, { positional: [t], flags: n }) {
207
214
  if (!t)
208
- return e.stderr(S), 2;
215
+ return e.stderr(p), 2;
209
216
  if (!e.render)
210
217
  return e.stderr(`render needs a local Chrome or Chromium (set CHROME to its path) and Node 22 or newer.
211
218
  `), 2;
212
- const s = await J(e, n), o = { file: t, ...await x(e, t, s) };
213
- if (N(o, n))
214
- return e.stdout(n.json ? JSON.stringify({ file: t, validation: o, renders: [] }, null, 2) + `
215
- ` : $(o)), 1;
216
- const r = await e.render({
217
- spec: await y(e, t),
219
+ const s = await x(e, n), r = { file: t, ...await k(e, t, s) };
220
+ if (J(r, n))
221
+ return e.stdout(n.json ? JSON.stringify({ file: t, validation: r, renders: [] }, null, 2) + `
222
+ ` : v(r)), 1;
223
+ const o = await e.render({
224
+ spec: await $(e, t),
218
225
  state: s.state,
219
226
  messages: s.messages,
220
- widths: f(n.widths)?.map(Number) ?? ie,
227
+ widths: y(n.widths)?.map(Number) ?? ae,
221
228
  height: typeof n.height == "string" ? Number(n.height) : 800,
222
- out: typeof n.out == "string" ? n.out : "syno-ui-render",
229
+ out: typeof n.out == "string" ? n.out : "syno-pkg-land-ui-render",
223
230
  name: t.split(/[\\/]/).pop().replace(/\.json$/i, "")
224
231
  }).catch((i) => {
225
- throw new p(`render failed: ${i instanceof Error ? i.message : String(i)}`);
232
+ throw new h(`render failed: ${i instanceof Error ? i.message : String(i)}`);
226
233
  });
227
- if (n.json) e.stdout(JSON.stringify({ file: t, validation: o, renders: r }, null, 2) + `
234
+ if (n.json) e.stdout(JSON.stringify({ file: t, validation: r, renders: o }, null, 2) + `
228
235
  `);
229
236
  else {
230
- e.stdout(o.warnings.length ? $(o) : "");
231
- for (const { width: i, screenshot: c, issues: d } of r)
237
+ e.stdout(r.warnings.length ? v(r) : "");
238
+ for (const { width: i, screenshot: c, issues: d } of o)
232
239
  e.stdout(`${t} @${i}px: ${d.length ? `${d.length} layout issue(s)` : "OK"} ${c}
233
240
  ` + d.map((l) => ` ${l.kind} ${l.key ?? "(outside the spec)"}: ${l.detail}
234
241
  `).join(""));
235
242
  }
236
- return r.some((i) => i.issues.length) ? 1 : 0;
243
+ return o.some((i) => i.issues.length) ? 1 : 0;
237
244
  }
238
- async function ce(e, t) {
245
+ async function de(e, t) {
239
246
  try {
240
- return await de(se(e), t);
247
+ return await le(re(e), t);
241
248
  } catch (n) {
242
- if (!(n instanceof p)) throw n;
249
+ if (!(n instanceof h)) throw n;
243
250
  return t.stderr(`${n.message}
244
251
  `), 2;
245
252
  }
246
253
  }
247
- async function de(e, t) {
248
- const [n, ...s] = e.positional, o = e.flags.json === !0;
254
+ async function le(e, t) {
255
+ if (e.flags.help === !0)
256
+ return t.stdout(p), 0;
257
+ const [n, ...s] = e.positional, r = e.flags.json === !0;
249
258
  switch (n) {
250
259
  case "catalog": {
251
- const r = B();
252
- return t.stdout(o ? JSON.stringify({ components: r }, null, 2) + `
253
- ` : r.map((i) => `${i.name}${i.required.length ? ` (${i.required.join(", ")})` : ""}: ${i.description}
260
+ const o = B();
261
+ return t.stdout(r ? JSON.stringify({ components: o }, null, 2) + `
262
+ ` : o.map((i) => `${i.name}${i.required.length ? ` (${i.required.join(", ")})` : ""}: ${i.description}
254
263
  `).join("")), 0;
255
264
  }
256
265
  case "describe": {
257
- const r = s[0] ?? "", i = _(r);
266
+ const o = s[0] ?? "", i = _(o);
258
267
  if (!i) {
259
- const c = V(r);
260
- return t.stderr(`Unknown component ${JSON.stringify(r)}.${c ? ` Did you mean "${c}"?` : ""} Run "syno-ui catalog".
268
+ const c = V(o);
269
+ return t.stderr(`Unknown component ${JSON.stringify(o)}.${c ? ` Did you mean "${c}"?` : ""} Run "syno-pkg-land-ui catalog".
261
270
  `), 2;
262
271
  }
263
- return t.stdout(o ? JSON.stringify(i, null, 2) + `
264
- ` : le(i)), 0;
272
+ return t.stdout(r ? JSON.stringify(i, null, 2) + `
273
+ ` : ue(i)), 0;
265
274
  }
266
275
  case "schema":
267
276
  return t.stdout(JSON.stringify(W(), null, 2) + `
@@ -269,14 +278,14 @@ async function de(e, t) {
269
278
  case "prompt":
270
279
  return t.stdout(T()), 0;
271
280
  case "validate":
272
- return re(t, { positional: s, flags: e.flags });
281
+ return ie(t, { positional: s, flags: e.flags });
273
282
  case "render":
274
- return ae(t, { positional: s, flags: e.flags });
283
+ return ce(t, { positional: s, flags: e.flags });
275
284
  default:
276
- return t.stderr(S), 2;
285
+ return t.stderr(p), 2;
277
286
  }
278
287
  }
279
- function le(e) {
288
+ function ue(e) {
280
289
  const t = Object.entries(e.props).map(([n, s]) => ` ${n}${s.required ? "" : "?"}: ${JSON.stringify(s.schema)}
281
290
  `).join("");
282
291
  return `${e.name}: ${e.description}
@@ -287,10 +296,10 @@ events: ${e.events.join(", ") || "(none)"}
287
296
  example: ${JSON.stringify(e.example)}
288
297
  `;
289
298
  }
290
- process.exitCode = await ce(process.argv.slice(2), {
299
+ process.exitCode = await de(process.argv.slice(2), {
291
300
  stdout: (e) => process.stdout.write(e),
292
301
  stderr: (e) => process.stderr.write(e),
293
- readFile: (e) => v(e, "utf8"),
302
+ readFile: (e) => S(e, "utf8"),
294
303
  // The DevTools connection uses the WebSocket global of Node 22 and newer.
295
- render: typeof WebSocket == "function" ? te(import.meta.url) : void 0
304
+ render: typeof WebSocket == "function" ? ne(import.meta.url) : void 0
296
305
  });
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import * as _l from "vue";
2
2
  import { inject as et, computed as h, defineComponent as E, openBlock as f, createElementBlock as _, normalizeStyle as $e, unref as l, createElementVNode as K, normalizeClass as le, createBlock as q, createCommentVNode as N, renderSlot as k, useAttrs as Kr, provide as Qe, watch as ve, watchEffect as lt, onBeforeUpdate as Ju, onBeforeUnmount as Ge, mergeProps as ee, shallowRef as ar, shallowReadonly as Zt, toValue as Xe, getCurrentScope as wi, onScopeDispose as vn, customRef as Qu, effectScope as Ci, getCurrentInstance as gt, isRef as zt, reactive as ct, toRefs as Fe, ref as T, nextTick as Ae, onMounted as Te, Fragment as fe, toHandlerKey as ed, camelize as ki, onUpdated as Si, triggerRef as td, toRef as ad, onUnmounted as pt, h as wt, Comment as rd, cloneVNode as od, withCtx as x, createVNode as O, withKeys as je, withDirectives as xt, vShow as ca, withModifiers as Se, Teleport as Wr, normalizeProps as Ee, guardReactiveProps as De, markRaw as nd, renderList as ke, watchSyncEffect as ld, withMemo as mn, watchPostEffect as $i, mergeDefaults as qi, resolveDynamicComponent as Ke, toHandlers as Pi, createTextVNode as ye, toDisplayString as Y, toRaw as id, useId as Ct, useTemplateRef as Ue, vModelText as Ai, resolveComponent as hn, vModelSelect as _o, useSlots as ea, createSlots as sd, shallowReactive as ud } from "vue";
3
3
  import { Minus as zo, ChevronDown as ft, Plus as No, Check as Lt, X as St, ChevronRight as dt, CalendarDays as dd, ChevronLeft as yn, CircleAlert as gn, TriangleAlert as bn, CircleCheck as Ei, Info as Hr, Ellipsis as xl, ChevronsLeft as cd, ChevronsRight as fd, RefreshCw as Er, Filter as pd, Search as vd, Trash2 as md, ChevronUp as Ti, GripVertical as wl, FolderOpen as Di, Folder as hd, File as yd, UploadCloud as gd, FileText as bd, Scissors as _d, Copy as xd, Pencil as wd, CircleHelp as Cd, Lightbulb as kd, ChevronsUpDown as Sd, Package as $d, ExternalLink as qd, Menu as Pd, Download as Ad, ArrowUpRight as Ed } from "@lucide/vue";
4
4
  import { DateFormatter as Ft, createCalendar as Td, toCalendar as Cl, CalendarDateTime as Bi, CalendarDate as Oi, getLocalTimeZone as _n, ZonedDateTime as Dd, getDayOfWeek as xn, startOfMonth as Bd, endOfMonth as kl, today as Od, isEqualMonth as Sl, isSameMonth as Ii, isSameDay as Pa, isEqualDay as Ua, isToday as Id, parseDate as Rd } from "@internationalized/date";
5
- const Ri = /* @__PURE__ */ Symbol("syno-ui:locale");
5
+ const Ri = /* @__PURE__ */ Symbol("syno-pkg-land-ui:locale");
6
6
  function rr(e) {
7
7
  const a = et(Ri, null);
8
8
  return h(() => e() ?? a?.value ?? "en");
@@ -1313,7 +1313,7 @@ const Ld = ["type", "disabled", "data-disabled"], nt = /* @__PURE__ */ E({
1313
1313
  k(r.$slots, "default")
1314
1314
  ], 10, zd));
1315
1315
  }
1316
- }), Mi = /* @__PURE__ */ Symbol("syno-ui:form"), Cn = /* @__PURE__ */ Symbol("syno-ui:form-field");
1316
+ }), Mi = /* @__PURE__ */ Symbol("syno-pkg-land-ui:form"), Cn = /* @__PURE__ */ Symbol("syno-pkg-land-ui:form-field");
1317
1317
  function Nd() {
1318
1318
  return et(Mi, null);
1319
1319
  }
@@ -20644,7 +20644,7 @@ const m0 = ["disabled", "readonly", "placeholder", "onKeydown"], h0 = ["disabled
20644
20644
  ], 8, T0)) : N("", !0)
20645
20645
  ], 16));
20646
20646
  }
20647
- }), ku = /* @__PURE__ */ Symbol("syno-ui-toast-controller");
20647
+ }), ku = /* @__PURE__ */ Symbol("syno-pkg-land-ui-toast-controller");
20648
20648
  function _A() {
20649
20649
  const e = et(ku);
20650
20650
  if (!e)
@@ -25859,7 +25859,7 @@ const rS = { class: "mb-3 flex flex-wrap items-start justify-between gap-3" }, o
25859
25859
  ])
25860
25860
  ], 40, kS));
25861
25861
  }
25862
- }), Iu = /* @__PURE__ */ Symbol("syno-ui-notification-controller");
25862
+ }), Iu = /* @__PURE__ */ Symbol("syno-pkg-land-ui-notification-controller");
25863
25863
  function OS() {
25864
25864
  const e = et(Iu);
25865
25865
  if (!e)
@@ -27157,7 +27157,7 @@ const b$ = ["aria-rowcount", "aria-colcount"], _$ = { role: "row" }, x$ = ["aria
27157
27157
  _: 1
27158
27158
  }, 8, ["open", "title", "description", "width"]));
27159
27159
  }
27160
- }), Ru = /* @__PURE__ */ Symbol("syno-ui-message-box");
27160
+ }), Ru = /* @__PURE__ */ Symbol("syno-pkg-land-ui-message-box");
27161
27161
  function H$(e = (a, t) => wn("en", a, t)) {
27162
27162
  const a = T([]), t = /* @__PURE__ */ new Map();
27163
27163
  let r = 1;
@@ -27244,7 +27244,7 @@ const EE = /* @__PURE__ */ E({
27244
27244
  }, null, 8, ["kind", "title", "message", "phrase", "progress", "options", "onSettle"]))), 128))
27245
27245
  ], 64));
27246
27246
  }
27247
- }), Mu = /* @__PURE__ */ Symbol("syno-ui-wizard");
27247
+ }), Mu = /* @__PURE__ */ Symbol("syno-pkg-land-ui-wizard");
27248
27248
  function G$() {
27249
27249
  return et(Mu, null);
27250
27250
  }
@@ -28216,7 +28216,7 @@ const Y$ = ["aria-label"], X$ = {
28216
28216
  ])) : N("", !0)
28217
28217
  ], 64));
28218
28218
  }
28219
- }), Vu = /* @__PURE__ */ Symbol("syno-ui:config-manager"), cl = /* @__PURE__ */ Symbol("syno-ui:config-provider");
28219
+ }), Vu = /* @__PURE__ */ Symbol("syno-pkg-land-ui:config-manager"), cl = /* @__PURE__ */ Symbol("syno-pkg-land-ui:config-provider");
28220
28220
  function Da(e) {
28221
28221
  if (typeof e != "object" || e === null || Array.isArray(e)) return !1;
28222
28222
  const a = Object.getPrototypeOf(e);
@@ -28947,7 +28947,7 @@ const ZE = /* @__PURE__ */ E({
28947
28947
  ve(
28948
28948
  () => a.alignItem,
28949
28949
  (r) => {
28950
- r && console.warn("[syno-ui] Space: `alignItem` is deprecated, use `alignItems` instead.");
28950
+ r && console.warn("[syno-pkg-land-ui] Space: `alignItem` is deprecated, use `alignItems` instead.");
28951
28951
  },
28952
28952
  { immediate: !0 }
28953
28953
  );
@@ -905,6 +905,6 @@ export declare const synoJsonCatalog: import('@json-render/core').Catalog<{
905
905
  }>;
906
906
  export declare const synoJsonAiRules: string[];
907
907
  /** The self-check loop an AI follows after generating a spec. */
908
- export declare const synoJsonAiWorkflow = "## Self-check workflow\n\n1. List components with `npx syno-ui catalog`; read props with `npx syno-ui describe <Component>`.\n2. Write the spec as JSON: {\"root\", \"elements\", \"state\"}. Seed every state path you bind or read.\n3. Run `npx syno-ui validate ui.json --json` (add --actions, --functions, --state, --messages for host-provided names).\n Fix every error at its JSON Pointer `path` using its `hint`, then validate again until it passes.\n4. Run `npx syno-ui render ui.json --out shots` to screenshot phone, DSM window and wide widths and to\n report layout overflow; fix reported elements and repeat.\n5. Hand over the spec only when validate and render both exit 0.";
908
+ export declare const synoJsonAiWorkflow = "## Self-check workflow\n\nInstall `@synopackageland/ui` first. Without a local install, use\n`npx -y -p @synopackageland/ui syno-pkg-land-ui <command>` (pin the package version for reproducibility).\n\n1. List components with `npx --no syno-pkg-land-ui catalog`; read props with `npx --no syno-pkg-land-ui describe <Component>`.\n2. Write the spec as JSON: {\"root\", \"elements\", \"state\"}. Seed every state path you bind or read.\n3. Run `npx --no syno-pkg-land-ui validate ui.json --json` (add --actions, --functions, --state, --messages for host-provided names).\n Fix every error at its JSON Pointer `path` using its `hint`, then validate again until it passes.\n4. Run `npx --no syno-pkg-land-ui render ui.json --out shots` to screenshot phone, DSM window and wide widths and to\n report layout overflow; fix reported elements and repeat.\n5. Hand over the spec only when validate and render both exit 0.";
909
909
  /** The complete AI system prompt: catalog, rules and the self-check workflow. */
910
910
  export declare function synoJsonAiPrompt(): string;
@@ -175,10 +175,13 @@ RULES:
175
175
 
176
176
  ## Self-check workflow
177
177
 
178
- 1. List components with `npx syno-ui catalog`; read props with `npx syno-ui describe <Component>`.
178
+ Install `@synopackageland/ui` first. Without a local install, use
179
+ `npx -y -p @synopackageland/ui syno-pkg-land-ui <command>` (pin the package version for reproducibility).
180
+
181
+ 1. List components with `npx --no syno-pkg-land-ui catalog`; read props with `npx --no syno-pkg-land-ui describe <Component>`.
179
182
  2. Write the spec as JSON: {"root", "elements", "state"}. Seed every state path you bind or read.
180
- 3. Run `npx syno-ui validate ui.json --json` (add --actions, --functions, --state, --messages for host-provided names).
183
+ 3. Run `npx --no syno-pkg-land-ui validate ui.json --json` (add --actions, --functions, --state, --messages for host-provided names).
181
184
  Fix every error at its JSON Pointer `path` using its `hint`, then validate again until it passes.
182
- 4. Run `npx syno-ui render ui.json --out shots` to screenshot phone, DSM window and wide widths and to
185
+ 4. Run `npx --no syno-pkg-land-ui render ui.json --out shots` to screenshot phone, DSM window and wide widths and to
183
186
  report layout overflow; fix reported elements and repeat.
184
187
  5. Hand over the spec only when validate and render both exit 0.
@@ -11364,7 +11364,7 @@ function jS(e, t) {
11364
11364
  f ? await f[1](l, u, c) : console.warn(`Unknown action: ${a}`);
11365
11365
  } };
11366
11366
  }
11367
- const LS = /* @__PURE__ */ Symbol("syno-ui:locale");
11367
+ const LS = /* @__PURE__ */ Symbol("syno-pkg-land-ui:locale");
11368
11368
  function qS(e) {
11369
11369
  const t = Le(LS, null);
11370
11370
  return $(() => e() ?? t?.value ?? "en");
@@ -12644,7 +12644,7 @@ const KS = ["type", "disabled", "data-disabled"], HS = /* @__PURE__ */ V({
12644
12644
  B(r.$slots, "default")
12645
12645
  ], 10, KS));
12646
12646
  }
12647
- }), Cp = /* @__PURE__ */ Symbol("syno-ui:form"), vl = /* @__PURE__ */ Symbol("syno-ui:form-field");
12647
+ }), Cp = /* @__PURE__ */ Symbol("syno-pkg-land-ui:form"), vl = /* @__PURE__ */ Symbol("syno-pkg-land-ui:form-field");
12648
12648
  function JS() {
12649
12649
  return Le(Cp, null);
12650
12650
  }
@@ -20522,7 +20522,7 @@ const ys = /* @__PURE__ */ Symbol("syno-app-presentation"), X$ = ["data-ui-data-
20522
20522
  ])) : Q("", !0)
20523
20523
  ], 64));
20524
20524
  }
20525
- }), QE = /* @__PURE__ */ Symbol("syno-ui:config-manager"), eP = /* @__PURE__ */ Symbol("syno-ui:config-provider");
20525
+ }), QE = /* @__PURE__ */ Symbol("syno-pkg-land-ui:config-manager"), eP = /* @__PURE__ */ Symbol("syno-pkg-land-ui:config-provider");
20526
20526
  function Pi(e) {
20527
20527
  if (typeof e != "object" || e === null || Array.isArray(e)) return !1;
20528
20528
  const t = Object.getPrototypeOf(e);
package/dist/json.js CHANGED
@@ -3,7 +3,7 @@ import { z as gt } from "zod";
3
3
  import { defineCatalog as z } from "@json-render/core";
4
4
  import { createStateStore as yt, defineCatalog as wt } from "@json-render/core";
5
5
  import { defineRegistry as B, useBoundProp as b, JSONUIProvider as M, Renderer as W } from "@json-render/vue";
6
- import { ActionProvider as $t, JSONUIProvider as St, Renderer as jt, StateProvider as Ot, ValidationProvider as xt, VisibilityProvider as At, defineRegistry as Pt, useActions as Ct, useBoundProp as kt, useStateStore as Rt } from "@json-render/vue";
6
+ import { ActionProvider as $t, JSONUIProvider as St, Renderer as jt, StateProvider as Ot, ValidationProvider as xt, VisibilityProvider as At, defineRegistry as Pt, useActions as kt, useBoundProp as Ct, useStateStore as Rt } from "@json-render/vue";
7
7
  import { schema as H } from "@json-render/vue/schema";
8
8
  import { schema as It } from "@json-render/vue/schema";
9
9
  import { h as d, defineComponent as G, openBlock as X, createBlock as Y, unref as j, withCtx as Q, createVNode as Z } from "vue";
@@ -16,11 +16,11 @@ const V = o.lazy(() => o.object({
16
16
  disabled: o.boolean().optional(),
17
17
  selectable: o.boolean().optional(),
18
18
  children: o.array(V).optional()
19
- })), C = o.object({
19
+ })), k = o.object({
20
20
  label: o.string(),
21
21
  value: o.string(),
22
22
  disabled: o.boolean().optional()
23
- }), Pe = C.extend({
23
+ }), Pe = k.extend({
24
24
  description: o.string().optional()
25
25
  }), U = o.object({
26
26
  key: o.string(),
@@ -88,7 +88,7 @@ const V = o.lazy(() => o.object({
88
88
  Tabs: {
89
89
  props: o.object({
90
90
  value: o.string(),
91
- items: o.array(C),
91
+ items: o.array(k),
92
92
  ariaLabel: o.string().optional()
93
93
  }),
94
94
  events: ["change"],
@@ -141,7 +141,7 @@ const V = o.lazy(() => o.object({
141
141
  Select: {
142
142
  props: o.object({
143
143
  value: o.string().optional(),
144
- options: o.array(C),
144
+ options: o.array(k),
145
145
  placeholder: o.string().optional(),
146
146
  disabled: o.boolean().optional(),
147
147
  search: o.boolean().optional()
@@ -318,7 +318,7 @@ const V = o.lazy(() => o.object({
318
318
  }, A = z(H, {
319
319
  components: _,
320
320
  actions: {}
321
- }), Ce = [
321
+ }), ke = [
322
322
  "Prefer AppRoot > AppShell > Page as the normal DSM application hierarchy.",
323
323
  "Prefer SettingsSection and SettingsField for settings; prefer ResourceList/DataTable for ordinary scalar resource collections.",
324
324
  "Keep state in the spec and use $bindState/$bindItem for editable controls; do not invent JavaScript in props.",
@@ -327,11 +327,11 @@ const V = o.lazy(() => o.object({
327
327
  "AppShell adapts to its actual DSM window width: phone (<=600px), desktop window (601–1199px), and wide web/Portal (>=1200px). Never assume browser viewport width.",
328
328
  "For a phone-unfriendly custom view, use ResponsiveView slots: mobile alternative and default full view. Keep essential actions accessible on mobile.",
329
329
  "Use low-level Stack/Text only when a semantic component does not already express the layout."
330
- ], ke = '## Self-check workflow\n\n1. List components with `npx syno-ui catalog`; read props with `npx syno-ui describe <Component>`.\n2. Write the spec as JSON: {"root", "elements", "state"}. Seed every state path you bind or read.\n3. Run `npx syno-ui validate ui.json --json` (add --actions, --functions, --state, --messages for host-provided names).\n Fix every error at its JSON Pointer `path` using its `hint`, then validate again until it passes.\n4. Run `npx syno-ui render ui.json --out shots` to screenshot phone, DSM window and wide widths and to\n report layout overflow; fix reported elements and repeat.\n5. Hand over the spec only when validate and render both exit 0.';
330
+ ], Ce = '## Self-check workflow\n\nInstall `@synopackageland/ui` first. Without a local install, use\n`npx -y -p @synopackageland/ui syno-pkg-land-ui <command>` (pin the package version for reproducibility).\n\n1. List components with `npx --no syno-pkg-land-ui catalog`; read props with `npx --no syno-pkg-land-ui describe <Component>`.\n2. Write the spec as JSON: {"root", "elements", "state"}. Seed every state path you bind or read.\n3. Run `npx --no syno-pkg-land-ui validate ui.json --json` (add --actions, --functions, --state, --messages for host-provided names).\n Fix every error at its JSON Pointer `path` using its `hint`, then validate again until it passes.\n4. Run `npx --no syno-pkg-land-ui render ui.json --out shots` to screenshot phone, DSM window and wide widths and to\n report layout overflow; fix reported elements and repeat.\n5. Hand over the spec only when validate and render both exit 0.';
331
331
  function rt() {
332
- return `${A.prompt({ customRules: Ce })}
332
+ return `${A.prompt({ customRules: ke })}
333
333
 
334
- ${ke}
334
+ ${Ce}
335
335
  `;
336
336
  }
337
337
  const Re = {
@@ -344,13 +344,13 @@ const Re = {
344
344
  function Ue(e) {
345
345
  return { default: () => e };
346
346
  }
347
- function k(e) {
347
+ function C(e) {
348
348
  return e ? Ae(e) : void 0;
349
349
  }
350
350
  function T(e) {
351
351
  return {
352
352
  ...e,
353
- icon: k(e.icon),
353
+ icon: C(e.icon),
354
354
  children: e.children?.map(T)
355
355
  };
356
356
  }
@@ -409,7 +409,7 @@ const Le = {
409
409
  return d(t === "heading" ? "h2" : t === "mono" ? "code" : "span", { class: t === "muted" ? "text-[13px] text-[var(--ui-text-muted)]" : t === "caption" ? "text-[12px] text-[var(--ui-text-muted)]" : t === "heading" ? "text-[16px] font-semibold text-[var(--ui-text)]" : t === "mono" ? "font-mono text-[12px] text-[var(--ui-text)]" : "text-[13px] text-[var(--ui-text)]", "data-syno-ui": "" }, e.text);
410
410
  },
411
411
  Button: ({ props: e, emit: t }) => {
412
- const n = k(e.icon);
412
+ const n = C(e.icon);
413
413
  return d(be, {
414
414
  variant: e.variant,
415
415
  size: e.size,
@@ -538,7 +538,7 @@ const Le = {
538
538
  EmptyState: ({ props: e, slots: t }) => d(ie, {
539
539
  title: e.title,
540
540
  description: e.description,
541
- icon: k(e.icon)
541
+ icon: C(e.icon)
542
542
  }, t),
543
543
  Progress: ({ props: e }) => d(ne, e),
544
544
  Badge: ({ props: e }) => d(oe, { tone: e.tone }, Ue(e.text)),
@@ -669,7 +669,7 @@ function ct(e, t = {}) {
669
669
  path: w("type"),
670
670
  elementKey: p,
671
671
  message: `Unknown component type ${JSON.stringify(h)}.`,
672
- hint: g ? `Did you mean "${g}"? Run "syno-ui catalog" for every component.` : 'Run "syno-ui catalog" for every component, or register a host extension component.'
672
+ hint: g ? `Did you mean "${g}"? Run "syno-pkg-land-ui catalog" for every component.` : 'Run "syno-pkg-land-ui catalog" for every component, or register a host extension component.'
673
673
  });
674
674
  continue;
675
675
  }
@@ -708,7 +708,7 @@ function qe(e, t, n, s, i) {
708
708
  path: a(l),
709
709
  elementKey: t,
710
710
  message: `${n} has no prop "${l}".`,
711
- hint: h ? `Did you mean "${h}"? Run "syno-ui describe ${n}".` : v(`${n} props`, c)
711
+ hint: h ? `Did you mean "${h}"? Run "syno-pkg-land-ui describe ${n}".` : v(`${n} props`, c)
712
712
  });
713
713
  continue;
714
714
  }
@@ -725,7 +725,7 @@ function qe(e, t, n, s, i) {
725
725
  });
726
726
  }
727
727
  for (const l of c)
728
- !Object.hasOwn(i, l) && !K(r[l]) && e.error({ code: "missing_prop", path: a(l), elementKey: t, message: `${n} requires prop "${l}".`, hint: `Run "syno-ui describe ${n}" for its type.` });
728
+ !Object.hasOwn(i, l) && !K(r[l]) && e.error({ code: "missing_prop", path: a(l), elementKey: t, message: `${n} requires prop "${l}".`, hint: `Run "syno-pkg-land-ui describe ${n}" for its type.` });
729
729
  }
730
730
  function F(e, t) {
731
731
  const n = [];
@@ -1103,14 +1103,14 @@ export {
1103
1103
  It as jsonRenderVueSchema,
1104
1104
  dt as listSynoJsonComponents,
1105
1105
  rt as synoJsonAiPrompt,
1106
- Ce as synoJsonAiRules,
1106
+ ke as synoJsonAiRules,
1107
1107
  A as synoJsonCatalog,
1108
1108
  _ as synoJsonComponentDefinitions,
1109
1109
  D as synoJsonRegistry,
1110
1110
  Le as synoJsonRenderers,
1111
1111
  mt as synoJsonSchema,
1112
- Ct as useActions,
1113
- kt as useBoundProp,
1112
+ kt as useActions,
1113
+ Ct as useBoundProp,
1114
1114
  Rt as useStateStore,
1115
1115
  ct as validateSynoJsonSpec,
1116
1116
  gt as z
@@ -3,7 +3,7 @@
3
3
  "version": "2.10.5",
4
4
  "delivery": "bundled-patched-esm",
5
5
  "patchManifestSha256": "14f10159b60d48ac7a0686f58faf7a30e99898dc267b3c032cf2bdd6052dc52b",
6
- "bundleSha256": "56bdc1751e42657de0e1480fdd37b200166c5df1aa0498f2af4f30b66f7f0955",
6
+ "bundleSha256": "f91918f6eb4061e040da4ccc839184535e347f7eee4472622fbb2fc0a37f6849",
7
7
  "sourceHashes": {
8
8
  "shared/useBodyScrollLock.js": "657592f283cb6fd25454abd284a0024af21936cb56e05b762ff0d22b6a979d32",
9
9
  "DismissableLayer/DismissableLayer.js": "da05ca544d9822a8e2b71057e78c0e1ef59423aa3893534338fb58e1ecbd1a8d",
package/docs/json-ui.md CHANGED
@@ -107,8 +107,11 @@ state machines stay application-owned extensions.
107
107
 
108
108
  ## AI tools
109
109
 
110
- The package ships a `syno-ui` command (`npx @synopackageland/ui <command>`, or
111
- `npx syno-ui` once installed) and the same functions from the JSON entry:
110
+ The package ships a `syno-pkg-land-ui` command and the same functions from the JSON
111
+ entry. After `npm install @synopackageland/ui`, run
112
+ `npx --no syno-pkg-land-ui <command>`. To run it without installing locally, use
113
+ `npx -y -p @synopackageland/ui syno-pkg-land-ui <command>`; pin the package version
114
+ for reproducible automation. Since 0.7.0 the old `syno-ui` bin is removed.
112
115
 
113
116
  | Command | Purpose | Function |
114
117
  | --- | --- | --- |
@@ -125,7 +128,7 @@ references and cycles, state pointers, `$item` outside `repeat`, actions,
125
128
  `$computed` functions and i18n keys. Tell it what the host provides:
126
129
 
127
130
  ```bash
128
- syno-ui validate ui.json --json --actions save,refresh --functions formatBytes \
131
+ syno-pkg-land-ui validate ui.json --json --actions save,refresh --functions formatBytes \
129
132
  --state host-state.json --messages messages/en.json
130
133
  ```
131
134
 
@@ -138,12 +141,12 @@ key and warns about literal user-facing text.
138
141
 
139
142
  ### Workflow for an AI
140
143
 
141
- 1. Read `syno-ui prompt` once, then `syno-ui catalog` and `syno-ui describe` for
144
+ 1. Read `syno-pkg-land-ui prompt` once, then `syno-pkg-land-ui catalog` and `syno-pkg-land-ui describe` for
142
145
  the components you plan to use.
143
146
  2. Write `{ "root", "elements", "state" }`, seeding every state path you bind.
144
- 3. Run `syno-ui validate ui.json --json` with the host's actions, functions,
147
+ 3. Run `syno-pkg-land-ui validate ui.json --json` with the host's actions, functions,
145
148
  state and messages. Fix each error at its `path` using its `hint`; repeat.
146
- 4. Run `syno-ui render ui.json --out shots`, look at the screenshots, and fix
149
+ 4. Run `syno-pkg-land-ui render ui.json --out shots`, look at the screenshots, and fix
147
150
  every element named in an `outside_viewport` or `clipped_content` issue.
148
151
  5. Hand over the spec only when `validate` and `render` both exit 0.
149
152
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.6.0",
2
+ "version": "0.7.0",
3
3
  "description": "Shared Vue 3 UI component library with a DSM 7 inspired application theme",
4
4
  "license": "MIT",
5
5
  "type": "module",
@@ -16,6 +16,9 @@
16
16
  "docs/component-defaults-0.5.7.md",
17
17
  "docs/json-ui.md"
18
18
  ],
19
+ "bin": {
20
+ "syno-pkg-land-ui": "./dist/cli.js"
21
+ },
19
22
  "main": "./dist/index.js",
20
23
  "module": "./dist/index.js",
21
24
  "types": "./dist/index.d.ts",