@synopackageland/ui 0.6.1 → 0.8.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,
@@ -136,7 +136,7 @@ import { AppRoot, ToastProvider } from '@synopackageland/ui'
136
136
  - makes the app root the containing block of fixed overlays, so dialogs,
137
137
  sheets and toasts stay inside the window.
138
138
 
139
- `AppRoot` renders `<div data-ui-app data-syno-ui>` and routes every overlay
139
+ `AppRoot` renders `<div data-ui-app data-syno-pkg-land-ui>` and routes every overlay
140
140
  portal (Dialog, Sheet, Toast, Popover, Select, DropdownMenu, ContextMenu,
141
141
  Tooltip) into itself, not `document.body`, where the scoped tokens
142
142
  and reset would not reach them. Each `AppRoot` teleports into its own element;
package/dist/cli.js CHANGED
@@ -1,17 +1,17 @@
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) {
14
+ function Q(e = process.env, t = process.platform, n = f) {
15
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
17
  function Y(e, t = process.env) {
@@ -42,28 +42,28 @@ async function Z(e) {
42
42
  }), n.stderr.on("data", (a) => {
43
43
  r = (r + a).slice(-4e3);
44
44
  });
45
- const o = w(e, "DevToolsActivePort");
46
- for (let a = 0; a < 100 && !g(o) && !s && n.exitCode === null; a++) await L(100);
47
- if (!g(o))
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
48
  throw n.kill(), new Error(`Chrome failed to start: ${s?.message ?? (r || "no debugging port within 10 seconds")}`);
49
- const [i] = (await v(o, "utf8")).split(`
49
+ const [i] = (await S(o, "utf8")).split(`
50
50
  `), c = await (await fetch(`http://127.0.0.1:${i}/json/new?about:blank`, { method: "PUT" })).json(), d = new WebSocket(c.webSocketDebuggerUrl);
51
- await new Promise((a, m) => {
52
- d.onopen = a, d.onerror = m;
51
+ await new Promise((a, u) => {
52
+ d.onopen = a, d.onerror = u;
53
53
  });
54
54
  let l = 1;
55
- const u = /* @__PURE__ */ new Map();
55
+ const m = /* @__PURE__ */ new Map();
56
56
  d.onmessage = (a) => {
57
- const m = JSON.parse(String(a.data));
58
- 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);
59
59
  };
60
- const b = (a, m = {}) => new Promise((A, P) => {
60
+ const b = (a, u = {}) => new Promise((A, P) => {
61
61
  const O = l++;
62
- u.set(O, (h) => h.error ? P(new Error(h.error.message)) : A(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 }));
63
63
  });
64
64
  await b("Emulation.setDeviceMetricsOverride", { width: 1100, height: 720, deviceScaleFactor: 1, mobile: !1 });
65
- const k = new Promise((a) => n.once("exit", a));
66
- 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) };
67
67
  }
68
68
  const q = `<!doctype html>
69
69
  <html><head><meta charset="utf-8"><link rel="stylesheet" href="/theme.css">
@@ -76,7 +76,7 @@ function ee(e, t) {
76
76
  }, s = F(async (r, o) => {
77
77
  const i = new URL(r.url ?? "/", "http://localhost").pathname;
78
78
  try {
79
- const [c, d] = i === "/" ? [q, "text/html"] : i === "/config.json" ? [t, "application/json"] : [await v(n[i][0]), n[i][1]];
79
+ const [c, d] = i === "/" ? [q, "text/html"] : i === "/config.json" ? [t, "application/json"] : [await S(n[i][0]), n[i][1]];
80
80
  o.writeHead(200, { "content-type": d }).end(c);
81
81
  } catch {
82
82
  o.writeHead(404).end();
@@ -105,15 +105,15 @@ async function te(e, t) {
105
105
  }
106
106
  function ne(e) {
107
107
  return async (t) => {
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(w(H(), "syno-ui-render-"));
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
109
  let o;
110
110
  try {
111
111
  o = await Z(r), await M(t.out, { recursive: !0 });
112
112
  const i = [];
113
113
  for (const c of t.widths) {
114
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" }), u = w(t.out, `${t.name}-${c}.png`);
116
- await D(u, Buffer.from(l, "base64")), i.push({ width: c, screenshot: u, issues: d });
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 });
117
117
  }
118
118
  return i;
119
119
  } finally {
@@ -121,7 +121,7 @@ function ne(e) {
121
121
  }
122
122
  };
123
123
  }
124
- const S = `Usage: syno-ui <command> [options]
124
+ const p = `Usage: syno-pkg-land-ui <command> [options]
125
125
 
126
126
  Commands:
127
127
  catalog [--json] List the JSON UI components
@@ -136,7 +136,7 @@ Commands:
136
136
  --state <file.json> Host-provided state
137
137
  --messages <file.json> Message catalog (enables i18n checks)
138
138
  render <file> [options] Validate, screenshot and report layout overflow (needs Chrome, Node 22+)
139
- --out <dir> Screenshot directory (default syno-ui-render)
139
+ --out <dir> Screenshot directory (default syno-pkg-land-ui-render)
140
140
  --widths 390,640,960,1350 Viewport widths: phone, narrow window, DSM window, wide
141
141
  --height <px> Viewport height (default 800)
142
142
  --state, --messages As for validate; messages also back $computed t()
@@ -154,30 +154,30 @@ function re(e) {
154
154
  }
155
155
  return { positional: t, flags: n };
156
156
  }
157
- 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;
158
158
  function oe(e) {
159
159
  return ` ${e.severity} ${e.code} ${e.path || "/"}: ${e.message}${e.hint ? `
160
160
  hint: ${e.hint}` : ""}
161
161
  `;
162
162
  }
163
- class p extends Error {
163
+ class h extends Error {
164
164
  }
165
165
  async function C(e, t) {
166
166
  try {
167
167
  return await e.readFile(t);
168
168
  } catch (n) {
169
- 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)}`);
170
170
  }
171
171
  }
172
- async function y(e, t) {
172
+ async function $(e, t) {
173
173
  const n = await C(e, t);
174
174
  try {
175
175
  return JSON.parse(n);
176
176
  } catch (s) {
177
- 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)}`);
178
178
  }
179
179
  }
180
- async function x(e, t, n) {
180
+ async function k(e, t, n) {
181
181
  const s = await C(e, t);
182
182
  let r;
183
183
  try {
@@ -187,54 +187,54 @@ async function x(e, t, n) {
187
187
  }
188
188
  return G(r, n);
189
189
  }
190
- async function J(e, t) {
190
+ async function x(e, t) {
191
191
  return {
192
- actions: f(t.actions),
193
- functions: f(t.functions),
194
- state: typeof t.state == "string" ? await y(e, t.state) : void 0,
195
- 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
196
196
  };
197
197
  }
198
- const N = (e, t) => e.errors.length > 0 || !!t.strict && e.warnings.length > 0;
199
- function $(e) {
198
+ const J = (e, t) => e.errors.length > 0 || !!t.strict && e.warnings.length > 0;
199
+ function v(e) {
200
200
  return e.errors.length || e.warnings.length ? `${e.file}: ${e.errors.length} error(s), ${e.warnings.length} warning(s)
201
201
  ${[...e.errors, ...e.warnings].map(oe).join("")}` : `${e.file}: OK
202
202
  `;
203
203
  }
204
204
  async function ie(e, { positional: t, flags: n }) {
205
205
  if (!t.length)
206
- return e.stderr(S), 2;
207
- const s = await J(e, n), r = [];
208
- for (const o of t) r.push({ file: o, ...await x(e, o, s) });
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
209
  return e.stdout(n.json ? JSON.stringify(r, null, 2) + `
210
- ` : r.map($).join("")), r.some((o) => N(o, n)) ? 1 : 0;
210
+ ` : r.map(v).join("")), r.some((o) => J(o, n)) ? 1 : 0;
211
211
  }
212
212
  const ae = [390, 640, 960, 1350];
213
213
  async function ce(e, { positional: [t], flags: n }) {
214
214
  if (!t)
215
- return e.stderr(S), 2;
215
+ return e.stderr(p), 2;
216
216
  if (!e.render)
217
217
  return e.stderr(`render needs a local Chrome or Chromium (set CHROME to its path) and Node 22 or newer.
218
218
  `), 2;
219
- const s = await J(e, n), r = { file: t, ...await x(e, t, s) };
220
- if (N(r, n))
219
+ const s = await x(e, n), r = { file: t, ...await k(e, t, s) };
220
+ if (J(r, n))
221
221
  return e.stdout(n.json ? JSON.stringify({ file: t, validation: r, renders: [] }, null, 2) + `
222
- ` : $(r)), 1;
222
+ ` : v(r)), 1;
223
223
  const o = await e.render({
224
- spec: await y(e, t),
224
+ spec: await $(e, t),
225
225
  state: s.state,
226
226
  messages: s.messages,
227
- widths: f(n.widths)?.map(Number) ?? ae,
227
+ widths: y(n.widths)?.map(Number) ?? ae,
228
228
  height: typeof n.height == "string" ? Number(n.height) : 800,
229
- out: typeof n.out == "string" ? n.out : "syno-ui-render",
229
+ out: typeof n.out == "string" ? n.out : "syno-pkg-land-ui-render",
230
230
  name: t.split(/[\\/]/).pop().replace(/\.json$/i, "")
231
231
  }).catch((i) => {
232
- 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)}`);
233
233
  });
234
234
  if (n.json) e.stdout(JSON.stringify({ file: t, validation: r, renders: o }, null, 2) + `
235
235
  `);
236
236
  else {
237
- e.stdout(r.warnings.length ? $(r) : "");
237
+ e.stdout(r.warnings.length ? v(r) : "");
238
238
  for (const { width: i, screenshot: c, issues: d } of o)
239
239
  e.stdout(`${t} @${i}px: ${d.length ? `${d.length} layout issue(s)` : "OK"} ${c}
240
240
  ` + d.map((l) => ` ${l.kind} ${l.key ?? "(outside the spec)"}: ${l.detail}
@@ -246,12 +246,14 @@ async function de(e, t) {
246
246
  try {
247
247
  return await le(re(e), t);
248
248
  } catch (n) {
249
- if (!(n instanceof p)) throw n;
249
+ if (!(n instanceof h)) throw n;
250
250
  return t.stderr(`${n.message}
251
251
  `), 2;
252
252
  }
253
253
  }
254
254
  async function le(e, t) {
255
+ if (e.flags.help === !0)
256
+ return t.stdout(p), 0;
255
257
  const [n, ...s] = e.positional, r = e.flags.json === !0;
256
258
  switch (n) {
257
259
  case "catalog": {
@@ -264,11 +266,11 @@ async function le(e, t) {
264
266
  const o = s[0] ?? "", i = _(o);
265
267
  if (!i) {
266
268
  const c = V(o);
267
- return t.stderr(`Unknown component ${JSON.stringify(o)}.${c ? ` Did you mean "${c}"?` : ""} Run "syno-ui catalog".
269
+ return t.stderr(`Unknown component ${JSON.stringify(o)}.${c ? ` Did you mean "${c}"?` : ""} Run "syno-pkg-land-ui catalog".
268
270
  `), 2;
269
271
  }
270
272
  return t.stdout(r ? JSON.stringify(i, null, 2) + `
271
- ` : me(i)), 0;
273
+ ` : ue(i)), 0;
272
274
  }
273
275
  case "schema":
274
276
  return t.stdout(JSON.stringify(W(), null, 2) + `
@@ -280,10 +282,10 @@ async function le(e, t) {
280
282
  case "render":
281
283
  return ce(t, { positional: s, flags: e.flags });
282
284
  default:
283
- return t.stderr(S), 2;
285
+ return t.stderr(p), 2;
284
286
  }
285
287
  }
286
- function me(e) {
288
+ function ue(e) {
287
289
  const t = Object.entries(e.props).map(([n, s]) => ` ${n}${s.required ? "" : "?"}: ${JSON.stringify(s.schema)}
288
290
  `).join("");
289
291
  return `${e.name}: ${e.description}
@@ -297,7 +299,7 @@ example: ${JSON.stringify(e.example)}
297
299
  process.exitCode = await de(process.argv.slice(2), {
298
300
  stdout: (e) => process.stdout.write(e),
299
301
  stderr: (e) => process.stderr.write(e),
300
- readFile: (e) => v(e, "utf8"),
302
+ readFile: (e) => S(e, "utf8"),
301
303
  // The DevTools connection uses the WebSocket global of Node 22 and newer.
302
304
  render: typeof WebSocket == "function" ? ne(import.meta.url) : void 0
303
305
  });