paintress 0.4.0 → 0.5.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
@@ -56,7 +56,7 @@ paintress move <file> <id> <parent> [--index n] reparent a node
56
56
  paintress batch <file> '<ops-json>' several operations at once
57
57
  paintress validate <file> check against the format spec
58
58
  paintress render <file> <out.html> write the canvas as HTML
59
- paintress shot <file> <out.png> [--scale n] render to PNG
59
+ paintress shot <file> <out.png> [--scale n] [--board id|name] render to PNG (one board or the whole canvas)
60
60
 
61
61
  paintress web [--port n] [--idle ms] [--daemon] start the viewer
62
62
  paintress ping | status | stop viewer control
@@ -78,6 +78,10 @@ paintress watch demo ./demo
78
78
 
79
79
  Canvas navigation: wheel zooms at the pointer, space / middle button / empty area drags to pan, `Ctrl/Cmd+0` returns to 100%, `Shift+1` fits the window. Files with more than one board get a board strip. Edits made through the CLI or MCP are highlighted in the viewer, and clicks assign short ids that the agent can read back with `paintress_marks`.
80
80
 
81
+ Project rows have an **×** on hover that unbinds the project (design files are untouched; an agent re-registers on its next start), and **Stop server** in the sidebar footer shuts the viewer process down — both ask for confirmation first.
82
+
83
+ When npm has a newer release than the running one, the version badge in the header grows a second line in rust red; clicking it opens the upgrade prompt (one copyable sentence) and the GitHub link. The check is client-optional on the daemon side: if npm is unreachable, the badge simply stays as it is.
84
+
81
85
  The toolbar's right side has two viewer-only controls. **Canvas background** switches Light / Gray / Dark for the design surface without touching the rest of the UI (click a stop or drag the knob). **Language** switches the interface between 中文 and EN; on first load it follows the browser, and anything that is not Chinese gets English. Both are per-browser and remembered locally. Registering a project, or dropping a new `.ptr` / `.pen` into a watched directory, updates every open viewer without a reload.
82
86
 
83
87
  ## MCP clients
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as isDaemonRunning, c as pidPath, d as sanitizeProjectName, f as socketPath, i as attachPersistent, l as resolveProjectDir, m as encodeLine, n as runStdioServer, o as sendRequest, p as LineDecoder, r as TOOLS, s as lockPath, u as safeJoin } from "./server-19PJh0Vn.js";
3
- import { Kt as VERSION, t as renderDocument, w as loadDoc } from "./render-YpsrjGNy.js";
2
+ import { a as isDaemonRunning, c as pidPath, d as sanitizeProjectName, f as socketPath, i as attachPersistent, l as resolveProjectDir, m as encodeLine, n as runStdioServer, o as sendRequest, p as LineDecoder, r as TOOLS, s as lockPath, u as safeJoin } from "./server-vPylVJZf.js";
3
+ import { T as loadDoc, qt as VERSION, t as renderDocument } from "./render-CzpO1cOe.js";
4
4
  import "./index.js";
5
5
  import { chmodSync, closeSync, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
6
  import { homedir } from "node:os";
@@ -21,7 +21,7 @@ const DESIGN_EXT = /* @__PURE__ */ new Set([".ptr", ".pen"]);
21
21
  var Registry = class {
22
22
  projects = /* @__PURE__ */ new Map();
23
23
  /** 注册(或复用)项目目录;同名不同目录视为冲突。depth 默认 3、上限 6 */
24
- watch(projectNameRaw, dir, depth) {
24
+ watch(projectNameRaw, dir, depth, opts = {}) {
25
25
  const name = sanitizeProjectName(projectNameRaw);
26
26
  const { root, rootReal } = resolveProjectDir(dir);
27
27
  const wanted = clampDepth(depth);
@@ -30,6 +30,7 @@ var Registry = class {
30
30
  if (existing.rootReal !== rootReal) throw new Error(`项目名 "${name}" 已被 ${existing.root} 占用,请换一个名字(--name)`);
31
31
  if (depth !== void 0) existing.depth = wanted;
32
32
  existing.lastAccess = Date.now();
33
+ if (opts.owner === "manual") existing.owner = "manual";
33
34
  return existing;
34
35
  }
35
36
  const entry = {
@@ -39,6 +40,7 @@ var Registry = class {
39
40
  depth: wanted,
40
41
  agents: /* @__PURE__ */ new Set(),
41
42
  browsers: /* @__PURE__ */ new Set(),
43
+ owner: opts.owner ?? "manual",
42
44
  lastAccess: Date.now(),
43
45
  createdAt: Date.now()
44
46
  };
@@ -339,6 +341,57 @@ var WatchManager = class {
339
341
  await Promise.all([...this.designs.values()].map((d) => d.close()));
340
342
  }
341
343
  };
344
+ // src/web/update-check.ts
345
+ const OK_TTL_MS = 216e5;
346
+ const FAIL_TTL_MS = 6e5;
347
+ const TIMEOUT_MS = 3e3;
348
+ const REGISTRY_URL = "https://registry.npmjs.org/paintress/latest";
349
+ /** 当前缓存是否已经过期(没有缓存也算过期) */
350
+ function shouldRefresh(state, now = Date.now()) {
351
+ if (state === null) return true;
352
+ const ttl = state.version === null ? FAIL_TTL_MS : OK_TTL_MS;
353
+ return now - state.checkedAt >= ttl;
354
+ }
355
+ /** 查一次 npm;任何失败(离线 / 超时 / 404 / 坏 JSON)都返回 null,不抛 */
356
+ async function fetchLatestVersion(fetchImpl = fetch, timeoutMs = TIMEOUT_MS) {
357
+ try {
358
+ const res = await fetchImpl(REGISTRY_URL, { signal: AbortSignal.timeout(timeoutMs) });
359
+ if (!res.ok) return null;
360
+ const body = await res.json();
361
+ return typeof body.version === "string" && body.version !== "" ? body.version : null;
362
+ } catch {
363
+ return null;
364
+ }
365
+ }
366
+ var VersionChecker = class {
367
+ fetcher;
368
+ state = null;
369
+ inFlight = null;
370
+ constructor(fetcher = () => fetchLatestVersion()) {
371
+ this.fetcher = fetcher;
372
+ }
373
+ /** 已完成探测的最新版号(没查到为 null) */
374
+ get latest() {
375
+ return this.state?.version ?? null;
376
+ }
377
+ /** 读当前值并在后台刷新(不阻塞调用方) */
378
+ peek() {
379
+ if (shouldRefresh(this.state) && this.inFlight === null) {
380
+ const task = this.refresh().finally(() => {
381
+ this.inFlight = null;
382
+ });
383
+ this.inFlight = task;
384
+ }
385
+ return this.latest;
386
+ }
387
+ async refresh() {
388
+ const version = await this.fetcher();
389
+ this.state = {
390
+ version,
391
+ checkedAt: Date.now()
392
+ };
393
+ }
394
+ };
342
395
  // src/web/daemon.ts
343
396
  const DEFAULT_PORT = 3316;
344
397
  const MIME = {
@@ -442,12 +495,21 @@ function layout(title, body) {
442
495
  function esc(s) {
443
496
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
444
497
  }
498
+ /**
499
+ * 破坏性写接口的 CSRF 门槛:要求自定义头 `x-paintress: 1`。
500
+ * 跨站 fetch 带自定义头会触发 CORS 预检,而 daemon 从不应答预检 —— 跳站页面发不出该请求。
501
+ * 不做 Origin 白名单:手机经隧道/反代访问时 Origin 是本机看不到的域名,白名单会误伤正常使用。
502
+ */
503
+ function isTrustedWrite(req) {
504
+ return req.headers["x-paintress"] === "1";
505
+ }
445
506
  async function startDaemon(opts = {}) {
446
507
  const releaseLock = acquireLock();
447
508
  const registry = new Registry();
448
509
  const marks = new MarksStore();
449
510
  const changes = new ChangesStore();
450
511
  const watchers = new WatchManager();
512
+ const versions = new VersionChecker();
451
513
  const startedAt = Date.now();
452
514
  const idleTimeoutMs = opts.idleTimeoutMs ?? 6e4;
453
515
  const idleCheckMs = opts.idleCheckMs ?? 15e3;
@@ -458,6 +520,10 @@ async function startDaemon(opts = {}) {
458
520
  * 项目列表变化时推 `projects` 事件,避免用户按 F5 才看到新项目。
459
521
  */
460
522
  const registryClients = /* @__PURE__ */ new Set();
523
+ /** 控制面长连接:shutdown 时必须主动结束,否则 server.close() 会一直等 agent attach */
524
+ const controlClients = /* @__PURE__ */ new Set();
525
+ /** 将被关闭的长连接数:agent attach + 设计稿 SSE + 项目列表 SSE */
526
+ const connectionCount = () => registryClients.size + registry.all().reduce((n, p) => n + p.agents.size + p.browsers.size, 0);
461
527
  const notifyRegistry = () => {
462
528
  const payload = JSON.stringify({
463
529
  projects: registry.all().length,
@@ -483,15 +549,59 @@ async function startDaemon(opts = {}) {
483
549
  }, 150);
484
550
  registryTimer.unref?.();
485
551
  };
552
+ /**
553
+ * agent 自动注册的项目退场:连接归零且无浏览器在看的宽限期后 unwatch。
554
+ * - 宽限期跨过 agent 崩溃重启的窗口(新进程会重新 watch/attach,定时器随即撤销)
555
+ * - 浏览器 SSE 开着时永不退场
556
+ * - 人手动 watch 的项目(owner=manual)不参与自动退场
557
+ */
558
+ const autoDetachMs = opts.autoDetachMs ?? 45e3;
559
+ const autoDetachTimers = /* @__PURE__ */ new Map();
560
+ function cancelAutoDetach(name) {
561
+ const timer = autoDetachTimers.get(name);
562
+ if (timer === void 0) return;
563
+ clearTimeout(timer);
564
+ autoDetachTimers.delete(name);
565
+ }
566
+ /** 移除项目并释放目录监听(与 unwatch 同一套收尾),返回是否真的移除了 */
567
+ function detachProject(entry, reason) {
568
+ cancelAutoDetach(entry.name);
569
+ if (!registry.remove(entry.name)) return false;
570
+ if (!registry.all().some((p) => p.rootReal === entry.rootReal)) watchers.unwatchDesigns(entry.rootReal);
571
+ if (!opts.quiet) process.stderr.write(`paintress web: 解绑项目 ${entry.name}(${reason})\n`);
572
+ notifyRegistry();
573
+ return true;
574
+ }
575
+ function maybeScheduleAutoDetach(name) {
576
+ if (closing) return;
577
+ const entry = registry.get(name);
578
+ if (entry === void 0 || entry.owner !== "agent" || !registry.isIdle(entry)) {
579
+ cancelAutoDetach(name);
580
+ return;
581
+ }
582
+ if (autoDetachTimers.has(name)) return;
583
+ const timer = setTimeout(() => {
584
+ autoDetachTimers.delete(name);
585
+ const current = registry.get(name);
586
+ if (current === void 0 || current.owner !== "agent" || !registry.isIdle(current)) return;
587
+ detachProject(current, "agent 已退出");
588
+ }, autoDetachMs);
589
+ timer.unref?.();
590
+ autoDetachTimers.set(name, timer);
591
+ }
486
592
  /** 控制面:一个连接可同时作为 agent 存活信号 */
487
593
  const handleClient = (socket) => {
594
+ controlClients.add(socket);
595
+ socket.on("close", () => controlClients.delete(socket));
488
596
  const decoder = new LineDecoder();
489
597
  let attachToken = null;
490
598
  const cleanup = () => {
491
599
  if (!attachToken) return;
492
- const entry = registry.get(attachToken.project);
600
+ const gone = attachToken.project;
601
+ const entry = registry.get(gone);
493
602
  if (entry) entry.agents.delete(attachToken.token);
494
603
  attachToken = null;
604
+ maybeScheduleAutoDetach(gone);
495
605
  };
496
606
  socket.on("close", cleanup);
497
607
  socket.on("error", cleanup);
@@ -531,7 +641,8 @@ async function startDaemon(opts = {}) {
531
641
  });
532
642
  break;
533
643
  case "watch": {
534
- const entry = registry.watch(req.project, req.dir, req.depth);
644
+ const entry = registry.watch(req.project, req.dir, req.depth, req.owner === "agent" ? { owner: "agent" } : {});
645
+ cancelAutoDetach(entry.name);
535
646
  watchers.watchDesigns(entry.rootReal, entry.depth, scheduleRegistryNotify);
536
647
  reply({
537
648
  ok: true,
@@ -543,20 +654,17 @@ async function startDaemon(opts = {}) {
543
654
  }
544
655
  case "unwatch": {
545
656
  const before = registry.get(req.project);
546
- const removed = registry.remove(req.project);
657
+ const removed = before === void 0 ? false : detachProject(before, "unwatch");
547
658
  reply({
548
659
  ok: true,
549
660
  unwatched: removed
550
661
  });
551
- if (removed) {
552
- if (before && !registry.all().some((p) => p.rootReal === before.rootReal)) watchers.unwatchDesigns(before.rootReal);
553
- notifyRegistry();
554
- }
555
662
  break;
556
663
  }
557
664
  case "attach": {
558
665
  const entry = registry.get(req.project);
559
666
  if (!entry) throw new Error(`项目未注册:${req.project}`);
667
+ cancelAutoDetach(req.project);
560
668
  const token = Symbol(`agent:${req.project}`);
561
669
  entry.agents.add(token);
562
670
  attachToken = {
@@ -669,10 +777,38 @@ async function startDaemon(opts = {}) {
669
777
  pid: process.pid,
670
778
  uptimeMs: Date.now() - startedAt,
671
779
  version: VERSION,
780
+ latest: opts.updateCheck === true ? versions.peek() : null,
781
+ connections: connectionCount(),
672
782
  projects: registry.all().map((p) => registry.info(p))
673
783
  });
674
784
  return;
675
785
  }
786
+ if (segments[1] === "version" && segments.length === 2) {
787
+ sendJson(200, {
788
+ ok: true,
789
+ pid: process.pid,
790
+ version: VERSION,
791
+ latest: opts.updateCheck === true ? versions.peek() : null,
792
+ connections: connectionCount()
793
+ });
794
+ return;
795
+ }
796
+ if (segments[1] === "projects" && segments.length === 3 && req.method === "DELETE") {
797
+ if (!isTrustedWrite(req)) {
798
+ sendJson(403, {
799
+ ok: false,
800
+ error: "缺少 x-paintress 头"
801
+ });
802
+ return;
803
+ }
804
+ const name = decodeURIComponent(segments[2] ?? "");
805
+ const entry = registry.get(name);
806
+ sendJson(200, {
807
+ ok: true,
808
+ unwatched: entry === void 0 ? false : detachProject(entry, "web 界面解绑")
809
+ });
810
+ return;
811
+ }
676
812
  if (segments[1] === "projects" && segments[3] === "files") {
677
813
  const name = decodeURIComponent(segments[2] ?? "");
678
814
  const entry = registry.get(name);
@@ -692,6 +828,24 @@ async function startDaemon(opts = {}) {
692
828
  });
693
829
  return;
694
830
  }
831
+ if (segments[1] === "shutdown" && req.method === "POST") {
832
+ if (!isTrustedWrite(req)) {
833
+ sendJson(403, {
834
+ ok: false,
835
+ error: "缺少 x-paintress 头"
836
+ });
837
+ return;
838
+ }
839
+ res.once("finish", () => {
840
+ setTimeout(() => void shutdown("web 界面关闭"), 0).unref?.();
841
+ });
842
+ sendJson(200, {
843
+ ok: true,
844
+ shutdown: true,
845
+ connections: connectionCount()
846
+ });
847
+ return;
848
+ }
695
849
  if (segments[1] === "changes") {
696
850
  const qProject = url.searchParams.get("project") ?? "";
697
851
  const qFile = url.searchParams.get("path") ?? "";
@@ -808,6 +962,7 @@ async function startDaemon(opts = {}) {
808
962
  res.write(": connected\n\n");
809
963
  const token = Symbol("browser");
810
964
  entry.browsers.add(token);
965
+ cancelAutoDetach(entry.name);
811
966
  const release = watchers.acquire(abs, () => {
812
967
  res.write(`event: change\ndata: ${JSON.stringify({
813
968
  path: relPath,
@@ -819,6 +974,7 @@ async function startDaemon(opts = {}) {
819
974
  clearInterval(keepAlive);
820
975
  release();
821
976
  entry.browsers.delete(token);
977
+ maybeScheduleAutoDetach(entry.name);
822
978
  };
823
979
  req.on("close", done);
824
980
  return;
@@ -957,13 +1113,19 @@ async function startDaemon(opts = {}) {
957
1113
  if (!opts.quiet) process.stderr.write(`paintress web: 退出(${reason})\n`);
958
1114
  clearInterval(idleTimer);
959
1115
  if (registryTimer !== null) clearTimeout(registryTimer);
1116
+ for (const timer of autoDetachTimers.values()) clearTimeout(timer);
1117
+ autoDetachTimers.clear();
960
1118
  for (const client of registryClients) try {
961
1119
  client.end();
962
1120
  } catch {}
963
1121
  registryClients.clear();
1122
+ for (const client of controlClients) client.end();
1123
+ controlClients.clear();
964
1124
  await watchers.closeAll();
965
1125
  await new Promise((resolve) => sockServer.close(() => resolve()));
966
- await new Promise((resolve) => httpServer.close(() => resolve()));
1126
+ const httpClosed = new Promise((resolve) => httpServer.close(() => resolve()));
1127
+ httpServer.closeAllConnections();
1128
+ await httpClosed;
967
1129
  releaseLock();
968
1130
  }
969
1131
  return {
@@ -1281,7 +1443,8 @@ const FLAG_KEYS = /* @__PURE__ */ new Set([
1281
1443
  "layout",
1282
1444
  "padding",
1283
1445
  "gap",
1284
- "force"
1446
+ "force",
1447
+ "board"
1285
1448
  ]);
1286
1449
  const BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
1287
1450
  "regex",
@@ -1538,7 +1701,8 @@ async function main(argv) {
1538
1701
  }
1539
1702
  const handle = await startDaemon({
1540
1703
  ...port !== void 0 ? { port } : {},
1541
- ...idle !== void 0 ? { idleTimeoutMs: idle } : {}
1704
+ ...idle !== void 0 ? { idleTimeoutMs: idle } : {},
1705
+ updateCheck: true
1542
1706
  });
1543
1707
  console.log(JSON.stringify({
1544
1708
  ok: true,
@@ -1646,14 +1810,18 @@ async function main(argv) {
1646
1810
  out: a1
1647
1811
  });
1648
1812
  break;
1649
- case "shot":
1813
+ case "shot": {
1650
1814
  if (!a1) throw new Error("缺少 <out.png> 参数");
1815
+ if (flags["board"] === true) throw new Error("--board 需要画板 id 或 name");
1816
+ const board = flagStr(flags, "board");
1651
1817
  out = await call("paintress_screenshot", {
1652
1818
  path: file,
1653
1819
  out: a1,
1654
- ...flagNum(flags, "scale") !== void 0 ? { scale: flagNum(flags, "scale") } : {}
1820
+ ...flagNum(flags, "scale") !== void 0 ? { scale: flagNum(flags, "scale") } : {},
1821
+ ...board !== void 0 ? { board } : {}
1655
1822
  });
1656
1823
  break;
1824
+ }
1657
1825
  default:
1658
1826
  console.error(`未知子命令:${cmd}`);
1659
1827
  return 2;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { Ft as Affine, rt as BackgroundBlurEffect, yt as BlendMode, it as BlurEffect, It as BoolOrVar, R as BrowserSchema, Lt as Color, Rt as ColorOrVar, zt as CornerRadius, bt as CrossAxis, A as DocumentObjectSchema, j as DocumentSchema, at as EffectEntry, ot as Effects, z as EllipseSchema, C as FileChangedError, st as FillEntry, xt as FillRule, ct as Fills, B as FrameSchema, lt as GradientFill, St as GradientType, ut as GraphicsBlock, V as GroupSchema, H as IconFontSchema, Ct as IconLibrary, U as IconSchema, dt as ImageFill, wt as ImageFit, W as KNOWN_NODE_TYPES, G as LayoutBlock, Tt as LayoutDir, Et as MainAxis, ft as MeshGradientFill, K as NodeBase, q as NodeSchema, J as NoteSchema, Bt as NumOrVar, M as PEN_DOC_VERSION, N as PTR_SPEC_VERSION, Vt as Padding, Y as PathSchema, X as PolygonSchema, Dt as PositionMode, P as PtrDocumentSchema, Z as RectangleSchema, Q as RefSchema, $ as ScriptSchema, pt as ShaderEffect, mt as ShaderFill, ht as ShadowEffect, Ot as ShadowKind, Ht as Sizing, gt as SolidFill, Ut as StringOrVar, kt as StrokeAlign, _t as StrokeBlock, At as StrokeCap, jt as StrokeJoin, vt as StrokeWidth, Mt as TextAlign, Nt as TextAlignVertical, Pt as TextGrowth, et as TextSchema, tt as UnknownNodeSchema, Wt as VARIABLE_NAME, F as VariableDef, Gt as VariableRef, I as XPaintress, L as X_PAINTRESS_KEY, h as batch, g as buildIndex, O as collectIds, s as commonToCss, n as computeCanvas, c as cornerRadiusToCss, l as effectsToCss, r as escapeAttr, i as escapeHtml, p as expandRefs, u as fillsToCss, k as genId, _ as get, o as getLucideIcon, v as insert, w as loadDoc, m as makeVarResolver, y as move, b as query, x as remove, t as renderDocument, a as renderTree, T as saveDoc, nt as schemaForType, E as serializeDoc, d as strokeToCss, f as textToCss, S as update, D as validateDoc } from "./render-YpsrjGNy.js";
1
+ export { It as Affine, it as BackgroundBlurEffect, bt as BlendMode, at as BlurEffect, Lt as BoolOrVar, z as BrowserSchema, Rt as Color, zt as ColorOrVar, Bt as CornerRadius, xt as CrossAxis, j as DocumentObjectSchema, M as DocumentSchema, ot as EffectEntry, st as Effects, B as EllipseSchema, w as FileChangedError, ct as FillEntry, St as FillRule, lt as Fills, V as FrameSchema, ut as GradientFill, Ct as GradientType, dt as GraphicsBlock, H as GroupSchema, U as IconFontSchema, wt as IconLibrary, W as IconSchema, ft as ImageFill, Tt as ImageFit, G as KNOWN_NODE_TYPES, K as LayoutBlock, Et as LayoutDir, Dt as MainAxis, pt as MeshGradientFill, q as NodeBase, J as NodeSchema, Y as NoteSchema, Vt as NumOrVar, N as PEN_DOC_VERSION, P as PTR_SPEC_VERSION, Ht as Padding, X as PathSchema, Z as PolygonSchema, Ot as PositionMode, F as PtrDocumentSchema, Q as RectangleSchema, $ as RefSchema, et as ScriptSchema, mt as ShaderEffect, ht as ShaderFill, gt as ShadowEffect, kt as ShadowKind, Ut as Sizing, _t as SolidFill, Wt as StringOrVar, At as StrokeAlign, vt as StrokeBlock, jt as StrokeCap, Mt as StrokeJoin, yt as StrokeWidth, Nt as TextAlign, Pt as TextAlignVertical, Ft as TextGrowth, tt as TextSchema, nt as UnknownNodeSchema, Gt as VARIABLE_NAME, I as VariableDef, Kt as VariableRef, L as XPaintress, R as X_PAINTRESS_KEY, g as batch, n as boardBoxes, _ as buildIndex, k as collectIds, c as commonToCss, r as computeCanvas, l as cornerRadiusToCss, u as effectsToCss, i as escapeAttr, a as escapeHtml, m as expandRefs, d as fillsToCss, A as genId, v as get, s as getLucideIcon, y as insert, T as loadDoc, h as makeVarResolver, b as move, x as query, S as remove, t as renderDocument, o as renderTree, E as saveDoc, rt as schemaForType, D as serializeDoc, f as strokeToCss, p as textToCss, C as update, O as validateDoc } from "./render-CzpO1cOe.js";
@@ -1 +1 @@
1
- export { t as createMcpServer, n as runStdioServer } from "../server-19PJh0Vn.js";
1
+ export { t as createMcpServer, n as runStdioServer } from "../server-vPylVJZf.js";
@@ -1606,6 +1606,23 @@ function computeCanvas(nodes, vars) {
1606
1606
  height: Math.max(1, Math.ceil(maxH))
1607
1607
  };
1608
1608
  }
1609
+ function boardBoxes(nodes, vars) {
1610
+ const out = [];
1611
+ for (const n of nodes) {
1612
+ if (!isRecord(n)) continue;
1613
+ const box = nodeBox(n, vars);
1614
+ const name = typeof n["name"] === "string" && n["name"] !== "" ? n["name"] : void 0;
1615
+ out.push({
1616
+ id: typeof n["id"] === "string" ? n["id"] : "",
1617
+ ...name !== void 0 ? { name } : {},
1618
+ x: box.x,
1619
+ y: box.y,
1620
+ w: box.w,
1621
+ h: box.h
1622
+ });
1623
+ }
1624
+ return out;
1625
+ }
1609
1626
  /** 渲染整棵(已展开的)树:顶层节点在画布上绝对定位,并带画布元数据 */
1610
1627
  function renderTree(nodes, vars, warnings) {
1611
1628
  return nodes.filter(isRecord).map((n) => renderNode(n, "none", vars, warnings, boardAttrs(n, vars))).join("");
@@ -1621,6 +1638,7 @@ function renderDocument(doc, opts = {}) {
1621
1638
  const vars = makeVarResolver(doc, warnings);
1622
1639
  const expanded = expandRefs(doc, warnings);
1623
1640
  const canvas = computeCanvas(expanded, vars);
1641
+ const boards = boardBoxes(expanded, vars);
1624
1642
  const title = opts.title ?? "paintress render";
1625
1643
  const body = renderTree(expanded, vars, warnings);
1626
1644
  return {
@@ -1645,7 +1663,8 @@ function renderDocument(doc, opts = {}) {
1645
1663
  ""
1646
1664
  ].join("\n"),
1647
1665
  warnings,
1648
- canvas
1666
+ canvas,
1667
+ boards
1649
1668
  };
1650
1669
  }
1651
- export { ScriptSchema as $, DocumentObjectSchema as A, StrokeCap as At, FrameSchema as B, NumOrVar as Bt, FileChangedError as C, IconLibrary as Ct, validateDoc as D, PositionMode as Dt, serializeDoc as E, MainAxis as Et, VariableDef as F, Affine as Ft, LayoutBlock as G, VariableRef as Gt, IconFontSchema as H, Sizing as Ht, XPaintress as I, BoolOrVar as It, NoteSchema as J, NodeBase as K, VERSION as Kt, X_PAINTRESS_KEY as L, Color as Lt, PEN_DOC_VERSION as M, TextAlign as Mt, PTR_SPEC_VERSION as N, TextAlignVertical as Nt, collectIds as O, ShadowKind as Ot, PtrDocumentSchema as P, TextGrowth as Pt, RefSchema as Q, BrowserSchema as R, ColorOrVar as Rt, update as S, GradientType as St, saveDoc as T, LayoutDir as Tt, IconSchema as U, StringOrVar as Ut, GroupSchema as V, Padding as Vt, KNOWN_NODE_TYPES as W, VARIABLE_NAME as Wt, PolygonSchema as X, PathSchema as Y, RectangleSchema as Z, get as _, StrokeBlock as _t, renderTree as a, EffectEntry as at, query as b, CrossAxis as bt, cornerRadiusToCss as c, Fills as ct, strokeToCss as d, ImageFill as dt, TextSchema as et, textToCss as f, MeshGradientFill as ft, buildIndex as g, SolidFill as gt, batch as h, ShadowEffect as ht, escapeHtml as i, BlurEffect as it, DocumentSchema as j, StrokeJoin as jt, genId as k, StrokeAlign as kt, effectsToCss as l, GradientFill as lt, makeVarResolver as m, ShaderFill as mt, computeCanvas as n, schemaForType as nt, getLucideIcon as o, Effects as ot, expandRefs as p, ShaderEffect as pt, NodeSchema as q, escapeAttr as r, BackgroundBlurEffect as rt, commonToCss as s, FillEntry as st, renderDocument as t, UnknownNodeSchema as tt, fillsToCss as u, GraphicsBlock as ut, insert as v, StrokeWidth as vt, loadDoc as w, ImageFit as wt, remove as x, FillRule as xt, move as y, BlendMode as yt, EllipseSchema as z, CornerRadius as zt };
1670
+ export { RefSchema as $, genId as A, StrokeAlign as At, EllipseSchema as B, CornerRadius as Bt, update as C, GradientType as Ct, serializeDoc as D, MainAxis as Dt, saveDoc as E, LayoutDir as Et, PtrDocumentSchema as F, TextGrowth as Ft, KNOWN_NODE_TYPES as G, VARIABLE_NAME as Gt, GroupSchema as H, Padding as Ht, VariableDef as I, Affine as It, NodeSchema as J, LayoutBlock as K, VariableRef as Kt, XPaintress as L, BoolOrVar as Lt, DocumentSchema as M, StrokeJoin as Mt, PEN_DOC_VERSION as N, TextAlign as Nt, validateDoc as O, PositionMode as Ot, PTR_SPEC_VERSION as P, TextAlignVertical as Pt, RectangleSchema as Q, X_PAINTRESS_KEY as R, Color as Rt, remove as S, FillRule as St, loadDoc as T, ImageFit as Tt, IconFontSchema as U, Sizing as Ut, FrameSchema as V, NumOrVar as Vt, IconSchema as W, StringOrVar as Wt, PathSchema as X, NoteSchema as Y, PolygonSchema as Z, buildIndex as _, SolidFill as _t, escapeHtml as a, BlurEffect as at, move as b, BlendMode as bt, commonToCss as c, FillEntry as ct, fillsToCss as d, GraphicsBlock as dt, ScriptSchema as et, strokeToCss as f, ImageFill as ft, batch as g, ShadowEffect as gt, makeVarResolver as h, ShaderFill as ht, escapeAttr as i, BackgroundBlurEffect as it, DocumentObjectSchema as j, StrokeCap as jt, collectIds as k, ShadowKind as kt, cornerRadiusToCss as l, Fills as lt, expandRefs as m, ShaderEffect as mt, boardBoxes as n, UnknownNodeSchema as nt, renderTree as o, EffectEntry as ot, textToCss as p, MeshGradientFill as pt, NodeBase as q, VERSION as qt, computeCanvas as r, schemaForType as rt, getLucideIcon as s, Effects as st, renderDocument as t, TextSchema as tt, effectsToCss as u, GradientFill as ut, get as v, StrokeBlock as vt, FileChangedError as w, IconLibrary as wt, query as x, CrossAxis as xt, insert as y, StrokeWidth as yt, BrowserSchema as z, ColorOrVar as zt };
@@ -1,4 +1,4 @@
1
- import { D as validateDoc, Kt as VERSION, M as PEN_DOC_VERSION, O as collectIds, S as update, T as saveDoc, _ as get, b as query, h as batch, j as DocumentSchema, k as genId, t as renderDocument, v as insert, w as loadDoc, x as remove, y as move } from "./render-YpsrjGNy.js";
1
+ import { A as genId, C as update, E as saveDoc, M as DocumentSchema, N as PEN_DOC_VERSION, O as validateDoc, S as remove, T as loadDoc, b as move, g as batch, k as collectIds, qt as VERSION, t as renderDocument, v as get, x as query, y as insert } from "./render-CzpO1cOe.js";
2
2
  import { existsSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { homedir, tmpdir } from "node:os";
4
4
  import { z } from "zod";
@@ -66,12 +66,51 @@ function findBrowser() {
66
66
  }
67
67
  return null;
68
68
  }
69
- /** 渲染并截图整张画布 */
69
+ function boardLabel(b) {
70
+ return b.name !== void 0 && b.name !== "" ? `${b.name}(id=${b.id})` : b.id;
71
+ }
72
+ /**
73
+ * 画板选择子:id 精确 → name 精确 → name/id 模糊(不区分大小写)。
74
+ * 模糊命中多个时报歧义并列出候选,不猜——猜错会让 AI 以为看到了另一页。
75
+ */
76
+ function selectBoard(boards, query) {
77
+ if (boards.length === 0) throw new Error("设计稿没有画板(顶层节点为空),无法按画板截图");
78
+ const q = query.trim();
79
+ if (q === "") throw new Error("board 参数不能为空");
80
+ const byId = boards.filter((b) => b.id === q);
81
+ if (byId.length === 1) return byId[0];
82
+ const byName = boards.filter((b) => b.name === q);
83
+ if (byName.length === 1) return byName[0];
84
+ const lower = q.toLowerCase();
85
+ const fuzzy = boards.filter((b) => b.id.toLowerCase().includes(lower) || (b.name ?? "").toLowerCase().includes(lower));
86
+ if (fuzzy.length === 1) return fuzzy[0];
87
+ const all = boards.map(boardLabel).join("、");
88
+ if (fuzzy.length === 0) throw new Error(`没有匹配「${q}」的画板;可选:${all}`);
89
+ throw new Error(`「${q}」命中 ${fuzzy.length} 个画板,请写更精确的名字或 id:${fuzzy.map(boardLabel).join("、")}`);
90
+ }
91
+ /** 渲染并截图整张画布;指定 board 时只裁该画板(走同一套渲染与浏览器) */
70
92
  async function screenshotPen(penPath, outPng, opts = {}) {
71
93
  const browserPath = findBrowser();
72
94
  if (!browserPath) throw new Error("未找到可用浏览器:安装 Chromium/Chrome,或设置 PTR_BROWSER 指向可执行文件");
73
95
  const loaded = loadDoc(penPath);
74
96
  const rendered = renderDocument(loaded.doc);
97
+ const target = opts.board !== void 0 ? selectBoard(rendered.boards, opts.board) : void 0;
98
+ if (target !== void 0 && (target.w <= 0 || target.h <= 0)) throw new Error(`画板 ${boardLabel(target)} 尺寸为 0(${target.w}×${target.h}),无法截图`);
99
+ const viewport = {
100
+ width: Math.max(rendered.canvas.width, target !== void 0 ? Math.ceil(target.x + target.w) : 0),
101
+ height: Math.max(rendered.canvas.height, target !== void 0 ? Math.ceil(target.y + target.h) : 0)
102
+ };
103
+ const clip = target !== void 0 ? {
104
+ x: target.x,
105
+ y: target.y,
106
+ width: Math.ceil(target.w),
107
+ height: Math.ceil(target.h)
108
+ } : {
109
+ x: 0,
110
+ y: 0,
111
+ width: rendered.canvas.width,
112
+ height: rendered.canvas.height
113
+ };
75
114
  const tmpDir = mkdtempSync(join(tmpdir(), "paintress-shot-"));
76
115
  const htmlPath = join(tmpDir, "page.html");
77
116
  writeFileSync(htmlPath, rendered.html, "utf8");
@@ -82,22 +121,14 @@ async function screenshotPen(penPath, outPng, opts = {}) {
82
121
  });
83
122
  try {
84
123
  const page = await browser.newPage({
85
- viewport: {
86
- width: rendered.canvas.width,
87
- height: rendered.canvas.height
88
- },
124
+ viewport,
89
125
  deviceScaleFactor: opts.scale ?? 2
90
126
  });
91
127
  await page.goto(`file://${htmlPath}`);
92
128
  await page.waitForTimeout(opts.settleMs ?? 120);
93
129
  await page.screenshot({
94
130
  path: outPng,
95
- clip: {
96
- x: 0,
97
- y: 0,
98
- width: rendered.canvas.width,
99
- height: rendered.canvas.height
100
- }
131
+ clip
101
132
  });
102
133
  } finally {
103
134
  await browser.close();
@@ -109,12 +140,21 @@ async function screenshotPen(penPath, outPng, opts = {}) {
109
140
  });
110
141
  }
111
142
  if (statSync(outPng).size === 0) throw new Error("截图产物为空文件");
112
- return {
143
+ const result = {
113
144
  out: outPng,
114
145
  canvas: rendered.canvas,
115
146
  warnings: rendered.warnings,
116
147
  browser: browserPath
117
148
  };
149
+ if (target !== void 0) result.board = {
150
+ id: target.id,
151
+ ...target.name !== void 0 ? { name: target.name } : {},
152
+ x: target.x,
153
+ y: target.y,
154
+ width: target.w,
155
+ height: target.h
156
+ };
157
+ return result;
118
158
  }
119
159
  // src/web/protocol.ts
120
160
  function encodeLine(msg) {
@@ -638,19 +678,25 @@ const TOOLS = {
638
678
  }
639
679
  },
640
680
  paintress_screenshot: {
641
- description: "渲染并截图为 PNG(默认 2x);需要本机 Chromium/Chrome 或 PTR_BROWSER 指向的可执行文件",
681
+ description: "渲染并截图为 PNG(默认 2x);需要本机 Chromium/Chrome 或 PTR_BROWSER 指向的可执行文件。指定 board 时只截该画板(顶层 frame,即“一页”),而不是整张画布",
642
682
  inputSchema: {
643
683
  path: z.string(),
644
684
  out: z.string().describe("输出 .png 路径"),
645
- scale: z.number().int().min(1).max(4).optional().describe("像素倍率,默认 2")
685
+ scale: z.number().int().min(1).max(4).optional().describe("像素倍率,默认 2"),
686
+ board: z.string().optional().describe("只截某个画板:id 或 name;模糊命中多个时会报错并列出候选")
646
687
  },
647
688
  run: async (a) => {
648
689
  if (!findBrowser()) throw new Error("未找到可用浏览器:安装 Chromium/Chrome,或设置 PTR_BROWSER");
649
- const res = await screenshotPen(reqStr(a, "path"), reqStr(a, "out"), { ...typeof a["scale"] === "number" ? { scale: a["scale"] } : {} });
690
+ const board = typeof a["board"] === "string" ? a["board"] : "";
691
+ const res = await screenshotPen(reqStr(a, "path"), reqStr(a, "out"), {
692
+ ...typeof a["scale"] === "number" ? { scale: a["scale"] } : {},
693
+ ...board !== "" ? { board } : {}
694
+ });
650
695
  return {
651
696
  ok: true,
652
697
  out: res.out,
653
698
  canvas: res.canvas,
699
+ ...res.board !== void 0 ? { board: res.board } : {},
654
700
  browser: res.browser,
655
701
  warnings: res.warnings
656
702
  };
@@ -717,7 +763,8 @@ async function autoRegister(cwd = process.cwd()) {
717
763
  const watched = await sendRequest({
718
764
  kind: "watch",
719
765
  project,
720
- dir: cwd
766
+ dir: cwd,
767
+ owner: "agent"
721
768
  }, 8e3);
722
769
  if (!watched.ok) throw new Error(watched.error);
723
770
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paintress",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Design-document operation layer for AI agents: read, query, edit, render",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,7 +37,7 @@ If either prints something, **do not install again**. Use the existing binary. `
37
37
  | `paintress_batch` | several operations in one call |
38
38
  | `paintress_validate` | check the document against the format spec |
39
39
  | `paintress_render` | write the canvas as a standalone HTML file |
40
- | `paintress_screenshot` | render to PNG (headless Chromium) |
40
+ | `paintress_screenshot` | render to PNG (headless Chromium); pass `board` (id or name) to capture one board instead of the whole canvas |
41
41
  | `paintress_marks` | read the user's click markers (`#1 → node id`) |
42
42
 
43
43
  ## Working rules
@@ -47,7 +47,7 @@ If either prints something, **do not install again**. Use the existing binary. `
47
47
  3. **`#N` means a marker.** When the user writes `#1`, call `paintress_marks` and map `#1` to a real node id. Markers are per viewer session.
48
48
  4. **Top-level nodes are boards.** Use `paintress_doc_info` to list them before guessing what "the second screen" means.
49
49
  5. **After a batch, validate.** `paintress_validate` catches structural mistakes before the user opens the file.
50
- 6. **Screenshots are for you.** `paintress_screenshot` is how you look at the design; the HTML viewer is for the human.
50
+ 6. **Screenshots are for you.** `paintress_screenshot` is how you look at the design; the HTML viewer is for the human. When a document has several boards, pass `board` to get just that page — an ambiguous name errors out and lists the candidates rather than guessing.
51
51
 
52
52
  ## Node fields worth knowing
53
53
 
@@ -0,0 +1 @@
1
+ :root{--bg:#f6f1ea;--panel:#fffcf8;--border:#e6ded2;--text:#1f2a37;--muted:#7a7166;--accent:#205080;--accent-soft:#20508014;--gold:#908030;--gold-soft:#90803073;--radius:8px}*{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%}body{color:var(--text);background:var(--bg);-webkit-font-smoothing:antialiased;font:14px/1.55 system-ui,-apple-system,Segoe UI,Noto Sans SC,sans-serif}.app{grid-template-columns:280px 1fr;height:100%;display:grid}.sidebar{background:var(--panel);border-right:1px solid var(--border);flex-direction:column;min-height:0;display:flex}.sidebar-head{border-bottom:1px solid var(--border);align-items:center;gap:8px;padding:16px 16px 12px;display:flex}.sidebar-head h1{letter-spacing:-.01em;color:var(--accent);font-size:15px;font-weight:650}.badge{color:var(--muted);background:var(--bg);border-radius:999px;padding:1px 6px;font-size:11px}.badge-stack{font:inherit;cursor:pointer;border:1px solid #0000;flex-direction:column;align-items:flex-start;gap:1px;font-size:11px;line-height:1.2;display:inline-flex}.badge-stack:hover{border-color:#b4402a59}.badge-new{color:#b4402a;font-weight:700}.prompt{background:var(--bg);border:1px solid var(--border);font:inherit;white-space:pre-wrap;word-break:break-word;-webkit-user-select:all;user-select:all;border-radius:6px;margin:10px 0 8px;padding:8px 10px;font-size:12px;line-height:1.6}a.ghost{text-decoration:none;display:inline-block}.section{min-height:0;padding:12px 8px 4px;overflow-y:auto}.section.files{border-top:1px solid var(--border);flex:1;padding-top:12px}.section h2{text-transform:uppercase;letter-spacing:.06em;color:var(--muted);justify-content:space-between;gap:8px;padding:0 8px 8px;font-size:11px;font-weight:600;display:flex}.hint{color:var(--muted);text-transform:none;letter-spacing:0;font-size:11px;font-weight:400}.list{list-style:none}.item{text-align:left;cursor:pointer;width:100%;font:inherit;color:inherit;background:0 0;border:0;border-radius:6px;flex-direction:column;gap:1px;padding:7px 8px;display:flex}.item:hover{background:var(--bg)}.item.active{background:var(--accent-soft);color:var(--accent)}.item-name{text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.item-meta{color:var(--muted);font-size:11px}.item.active .item-meta{color:var(--accent);opacity:.75}.empty{color:var(--muted);padding:4px 8px 12px;font-size:12px;line-height:1.7}.empty code{background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:1px 5px;font-size:11px}.sidebar-foot{border-top:1px solid var(--border);color:var(--muted);flex-wrap:wrap;align-items:center;gap:8px;padding:10px 16px;font-size:12px;display:flex}.toggle{cursor:pointer;align-items:center;gap:5px;display:inline-flex}.ghost{background:var(--bg);border:1px solid var(--border);font:inherit;color:var(--text);cursor:pointer;border-radius:6px;padding:3px 9px;font-size:12px}.ghost:hover{border-color:var(--accent);color:var(--accent)}.stage{background:var(--canvas-bg,var(--bg));flex-direction:column;min-width:0;min-height:0;display:flex}.toolbar{border-bottom:1px solid var(--border);background:var(--panel);justify-content:space-between;align-items:center;gap:12px;padding:8px 14px;display:flex}.toolbar code{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.preview{border:1px solid var(--border);background:var(--canvas-bg,#fff);border-radius:10px;flex:1;width:calc(100% - 24px);min-height:0;margin:12px;box-shadow:0 1px 2px #1f2a370d,0 8px 24px #1f2a370d}.stage[data-canvas-bg=dark] .preview{border-color:#ffffff1f;box-shadow:0 1px 2px #00000059,0 10px 30px #00000052}.placeholder{color:var(--muted);flex:1;place-items:center;display:grid}.error{border-radius:var(--radius);color:#b91c1c;white-space:pre-wrap;background:#fef2f2;border:1px solid #fecaca;margin:12px;padding:10px 12px;font-size:12px}.section.marks{border-top:1px solid var(--border);flex:0 auto;max-height:34vh}.mark-no{background:var(--accent-soft);min-width:20px;color:var(--accent);font-variant-numeric:tabular-nums;border-radius:4px;margin-right:4px;padding:0 4px;font-size:11px;display:inline-block}.item.active .mark-no{background:#fff}.ghost.tiny{text-transform:none;letter-spacing:0;padding:1px 6px;font-size:11px}.crumbs{flex-wrap:wrap;align-items:center;gap:3px;min-width:0;display:flex;overflow:hidden}.crumbs code{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.crumb{font:inherit;color:var(--muted);cursor:pointer;text-overflow:ellipsis;white-space:nowrap;background:0 0;border:0;border-radius:4px;max-width:220px;padding:2px 5px;font-size:12px;overflow:hidden}.crumb:hover{background:var(--bg);color:var(--text)}.crumb.active{color:var(--accent);background:var(--accent-soft)}.sep{color:var(--border);font-size:11px}.brand-mark{flex:none;width:auto;height:32px}.canvas-bg{-webkit-user-select:none;user-select:none;flex:none;align-items:center;gap:8px;display:inline-flex}.canvas-bg-label{letter-spacing:.04em;text-transform:uppercase;color:var(--muted);text-align:right;min-width:34px;font-size:11px;font-weight:500}.canvas-bg-track{width:84px;height:calc(var(--thumb) + var(--pad) * 2);cursor:pointer;touch-action:none;background:linear-gradient(#e7e0d5,#f3eee6 60%,#f7f2ea);border-radius:999px;position:relative;box-shadow:inset 0 1px 2px #1f2a373d,inset 0 -1px #ffffffe6,inset 0 0 0 1px #1f2a3712,0 1px #fff}.canvas-bg-track:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.canvas-bg-thumb{top:var(--pad);left:var(--pad);width:var(--thumb);height:var(--thumb);transform:translateX(calc(var(--pos) * var(--step)));background:linear-gradient(#fff,#f1ebe1);border-radius:50%;justify-content:center;align-items:center;transition:transform .17s cubic-bezier(.22,.61,.36,1);display:flex;position:absolute;box-shadow:0 1px 2px #1f2a374d,0 0 0 1px #1f2a3714,inset 0 1px #fff}.canvas-bg-track.dragging .canvas-bg-thumb{transition:none}.canvas-bg-dot{background:var(--dot,#fff);border-radius:50%;width:11px;height:11px;transition:background .17s;box-shadow:0 0 0 1px #1f2a3742,0 1px 1px #1f2a371f}.lang-switch{background:linear-gradient(#ece6dc,#f6f1e9);border-radius:999px;flex:none;align-items:center;gap:2px;margin-left:4px;padding:2px;display:inline-flex;box-shadow:inset 0 1px 2px #1f2a3729,inset 0 0 0 1px #1f2a370d}.lang-opt{font:inherit;color:var(--muted);cursor:pointer;background:0 0;border:0;border-radius:999px;padding:2px 7px;font-size:11px;line-height:16px}.lang-opt:hover{color:var(--text)}.lang-opt.active{color:var(--accent);background:linear-gradient(#fffdf9,#f3ede4);font-weight:600;box-shadow:0 1px 2px #1f2a372e,inset 0 1px #fff}.tool-sep{background:var(--border);flex:none;width:1px;height:18px;margin:0 2px}.tools{flex:none;align-items:center;gap:6px;display:flex}.zoom{font-variant-numeric:tabular-nums;text-align:right;min-width:40px}.boardbar{border-bottom:1px solid var(--border);background:var(--panel);align-items:center;gap:6px;padding:6px 14px;display:flex;overflow-x:auto}.board{background:var(--bg);border:1px solid var(--border);font:inherit;color:var(--text);cursor:pointer;border-radius:6px;flex:none;align-items:baseline;gap:6px;padding:3px 9px;font-size:12px;display:inline-flex}.board:hover{border-color:var(--accent);color:var(--accent)}.board.active{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}.board-size{color:var(--muted);font-variant-numeric:tabular-nums;font-size:10px}.board.active .board-size{color:var(--accent);opacity:.75}.row{align-items:center;display:flex;position:relative}.row .item{flex:1;min-width:0}.row-action{background:var(--panel);width:20px;height:20px;color:var(--muted);font:inherit;cursor:pointer;opacity:0;border:1px solid #0000;border-radius:5px;place-items:center;padding:0;font-size:13px;line-height:1;transition:opacity .12s;display:grid;position:absolute;top:50%;right:4px;transform:translateY(-50%)}.row:hover .row-action,.row-action:focus-visible{opacity:1}.row-action:hover{color:#a33;border-color:#aa333359}@media (hover:none){.row-action{opacity:1}}.modal-backdrop{z-index:50;background:#1f2a3747;place-items:center;display:grid;position:fixed;inset:0}.modal{background:var(--panel);border:1px solid var(--border);border-radius:10px;width:min(380px,100vw - 32px);padding:16px 18px 14px;box-shadow:0 12px 40px #1f2a372e}.modal h3{color:var(--accent);margin-bottom:6px;font-size:14px;font-weight:650}.modal-body{color:var(--muted);font-size:12.5px;line-height:1.65}.modal-actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.ghost.danger{color:#a33}.ghost.danger:hover,.ghost.danger:focus-visible{color:#a33;background:#aa33330f;border-color:#a33}
@@ -0,0 +1,8 @@
1
+ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e={context:void 0,registry:void 0,effects:void 0,done:!1,getContextId(){return t(this.context.count)},getNextContextId(){return t(this.context.count++)}};function t(t){let n=String(t),r=n.length-1;return e.context.id+(r?String.fromCharCode(96+r):``)+n}function n(t){e.context=t}var r=(e,t)=>e===t,i=Symbol(`solid-track`),a={equals:r},o=null,s=oe,c=1,l=2,u={owned:null,cleanups:null,context:null,owner:null},d=null,f=null,p=null,m=null,h=null,g=0;function _(e,t){let n=p,r=d,i=e.length===0,a=t===void 0?r:t,o=i?u:{owned:null,cleanups:null,context:a?a.context:null,owner:a},s=i?e:()=>e(v(o));d=o,p=null;try{return j(s,!0)}finally{p=n,d=r}}function v(e){return()=>C(()=>F(e))}function y(e,t){t=t?Object.assign({},a,t):a;let n={value:e,observers:null,observerSlots:null,comparator:t.equals||void 0};return[O.bind(n),e=>(typeof e==`function`&&(e=f&&f.running&&f.sources.has(n)?e(n.tValue):e(n.value)),re(n,e))]}function b(e,t,n){k(ae(e,t,!1,c))}function x(e,t,n){s=se;let r=ae(e,t,!1,c),i=ne&&D(ne);i&&(r.suspense=i),n&&n.render||(r.user=!0),h?h.push(r):k(r)}function S(e,t,n){n=n?Object.assign({},a,n):a;let r=ae(e,t,!0,0);return r.observers=null,r.observerSlots=null,r.comparator=n.equals||void 0,k(r),O.bind(r)}function C(e){if(p===null)return e();let t=p;p=null;try{return e()}finally{p=t}}function w(e,t,n){let r=Array.isArray(e),i,a=n&&n.defer;return n=>{let o;if(r){o=Array(e.length);for(let t=0;t<e.length;t++)o[t]=e[t]()}else o=e();if(a)return a=!1,n;let s=C(()=>t(o,i,n));return i=o,s}}function T(e){x(()=>C(e))}function E(e){return d===null||(d.cleanups===null?d.cleanups=[e]:d.cleanups.push(e)),e}var[ee,te]=y(!1);function D(e){let t;return d&&d.context&&(t=d.context[e.id])!==void 0?t:e.defaultValue}var ne;function O(){let e=f&&f.running;if(this.sources&&(e?this.tState:this.state)){if((e?this.tState:this.state)===c)k(this);else{let e=m;m=null,j(()=>N(this),!1),m=e}}if(p){let e=this.observers;if(!e||e[e.length-1]!==p){let t=e?e.length:0;p.sources?(p.sources.push(this),p.sourceSlots.push(t)):(p.sources=[this],p.sourceSlots=[t]),e?(e.push(p),this.observerSlots.push(p.sources.length-1)):(this.observers=[p],this.observerSlots=[p.sources.length-1])}}return e&&f.sources.has(this)?this.tValue:this.value}function re(e,t,n){let r=f&&f.running&&f.sources.has(e)?e.tValue:e.value;if(!e.comparator||!e.comparator(r,t)){if(f){let r=f.running;(r||!n&&f.sources.has(e))&&(f.sources.add(e),e.tValue=t),r||(e.value=t)}else e.value=t;e.observers&&e.observers.length&&j(()=>{for(let t=0;t<e.observers.length;t+=1){let n=e.observers[t],r=f&&f.running;r&&f.disposed.has(n)||((r?!n.tState:!n.state)&&(n.pure?m.push(n):h.push(n),n.observers&&P(n)),r?n.tState=c:n.state=c)}if(m.length>1e6)throw m=[],Error()},!1)}return t}function k(e){if(!e.fn)return;F(e);let t=g;ie(e,f&&f.running&&f.sources.has(e)?e.tValue:e.value,t),f&&!f.running&&f.sources.has(e)&&queueMicrotask(()=>{j(()=>{f&&(f.running=!0),p=d=e,ie(e,e.tValue,t),p=d=null},!1)})}function ie(e,t,n){let r,i=d,a=p;p=d=e;try{r=e.fn(t)}catch(t){return e.pure&&(f&&f.running?(e.tState=c,e.tOwned&&e.tOwned.forEach(F),e.tOwned=void 0):(e.state=c,e.owned&&e.owned.forEach(F),e.owned=null)),e.updatedAt=n+1,I(t)}finally{p=a,d=i}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&`observers`in e?re(e,r,!0):f&&f.running&&e.pure?(f.sources.has(e)||(e.value=r),f.sources.add(e),e.tValue=r):e.value=r,e.updatedAt=n)}function ae(e,t,n,r=c,i){let a={fn:e,state:r,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:t,owner:d,context:d?d.context:null,pure:n};return f&&f.running&&(a.state=0,a.tState=r),d===null||d!==u&&(f&&f.running&&d.pure?d.tOwned?d.tOwned.push(a):d.tOwned=[a]:d.owned?d.owned.push(a):d.owned=[a]),a}function A(e){let t=f&&f.running;if((t?e.tState:e.state)===0)return;if((t?e.tState:e.state)===l)return N(e);if(e.suspense&&C(e.suspense.inFallback))return e.suspense.effects.push(e);let n=[e];for(;(e=e.owner)&&(!e.updatedAt||e.updatedAt<g);){if(t&&f.disposed.has(e))return;(t?e.tState:e.state)&&n.push(e)}for(let r=n.length-1;r>=0;r--){if(e=n[r],t){let t=e,i=n[r+1];for(;(t=t.owner)&&t!==i;)if(f.disposed.has(t))return}if((t?e.tState:e.state)===c)k(e);else if((t?e.tState:e.state)===l){let t=m;m=null,j(()=>N(e,n[0]),!1),m=t}}}function j(e,t){if(m)return e();let n=!1;t||(m=[]),h?n=!0:h=[],g++;try{let t=e();return M(n),t}catch(e){n||(h=null),m=null,I(e)}}function M(e){if(m&&=(oe(m),null),e)return;let t;if(f){if(!f.promises.size&&!f.queue.size){let e=f.sources,n=f.disposed;h.push.apply(h,f.effects),t=f.resolve;for(let e of h)`tState`in e&&(e.state=e.tState),delete e.tState;f=null,j(()=>{for(let e of n)F(e);for(let t of e){if(t.value=t.tValue,t.owned)for(let e=0,n=t.owned.length;e<n;e++)F(t.owned[e]);t.tOwned&&(t.owned=t.tOwned),delete t.tValue,delete t.tOwned,t.tState=0}te(!1)},!1)}else if(f.running){f.running=!1,f.effects.push.apply(f.effects,h),h=null,te(!0);return}}let n=h;h=null,n.length&&j(()=>s(n),!1),t&&t()}function oe(e){for(let t=0;t<e.length;t++)A(e[t])}function se(t){let r,i=0;for(r=0;r<t.length;r++){let e=t[r];e.user?t[i++]=e:A(e)}if(e.context){if(e.count){e.effects||=[],e.effects.push(...t.slice(0,i));return}n()}for(e.effects&&(e.done||!e.count)&&(t=[...e.effects,...t],i+=e.effects.length,delete e.effects),r=0;r<i;r++)A(t[r])}function N(e,t){let n=f&&f.running;n?e.tState=0:e.state=0;for(let r=0;r<e.sources.length;r+=1){let i=e.sources[r];if(i.sources){let e=n?i.tState:i.state;e===c?i!==t&&(!i.updatedAt||i.updatedAt<g)&&A(i):e===l&&N(i,t)}}}function P(e){let t=f&&f.running;for(let n=0;n<e.observers.length;n+=1){let r=e.observers[n];(t?!r.tState:!r.state)&&(t?r.tState=l:r.state=l,r.pure?m.push(r):h.push(r),r.observers&&P(r))}}function F(e){let t;if(e.sources)for(;e.sources.length;){let t=e.sources.pop(),n=e.sourceSlots.pop(),r=t.observers;if(r&&r.length){let e=r.pop(),i=t.observerSlots.pop();n<r.length&&(e.sourceSlots[i]=n,r[n]=e,t.observerSlots[n]=i)}}if(e.tOwned){for(t=e.tOwned.length-1;t>=0;t--)F(e.tOwned[t]);delete e.tOwned}if(f&&f.running&&e.pure)ce(e,!0);else if(e.owned){for(t=e.owned.length-1;t>=0;t--)F(e.owned[t]);e.owned=null}if(e.cleanups){for(t=e.cleanups.length-1;t>=0;t--)e.cleanups[t]();e.cleanups=null}f&&f.running?e.tState=0:e.state=0}function ce(e,t){if(t||(e.tState=0,f.disposed.add(e)),e.owned)for(let t=0;t<e.owned.length;t++)ce(e.owned[t])}function le(e){return e instanceof Error?e:Error(typeof e==`string`?e:`Unknown error`,{cause:e})}function ue(e,t,n){try{for(let n of t)n(e)}catch(e){I(e,n&&n.owner||null)}}function I(e,t=d){let n=o&&t&&t.context&&t.context[o],r=le(e);if(!n)throw r;h?h.push({fn(){ue(r,n,t)},state:c}):ue(r,n,t)}var L=Symbol(`fallback`);function de(e){for(let t=0;t<e.length;t++)e[t]()}function R(e,t,n={}){let r=[],a=[],o=[],s=0,c=t.length>1?[]:null;return E(()=>de(o)),()=>{let l=e()||[],u=l.length,d,f;return l[i],C(()=>{let e,t,i,m,h,g,v,y,b;if(u===0)s!==0&&(de(o),o=[],r=[],a=[],s=0,c&&=[]),n.fallback&&(r=[L],a[0]=_(e=>(o[0]=e,n.fallback())),s=1);else if(s===0){for(a=Array(u),f=0;f<u;f++)r[f]=l[f],a[f]=_(p);s=u}else{for(i=Array(u),m=Array(u),c&&(h=Array(u)),g=0,v=Math.min(s,u);g<v&&r[g]===l[g];g++);for(v=s-1,y=u-1;v>=g&&y>=g&&r[v]===l[y];v--,y--)i[y]=a[v],m[y]=o[v],c&&(h[y]=c[v]);for(e=new Map,t=Array(y+1),f=y;f>=g;f--)b=l[f],d=e.get(b),t[f]=d===void 0?-1:d,e.set(b,f);for(d=g;d<=v;d++)b=r[d],f=e.get(b),f!==void 0&&f!==-1?(i[f]=a[d],m[f]=o[d],c&&(h[f]=c[d]),f=t[f],e.set(b,f)):o[d]();for(f=g;f<u;f++)f in i?(a[f]=i[f],o[f]=m[f],c&&(c[f]=h[f],c[f](f))):a[f]=_(p);a=a.slice(0,s=u),r=l.slice(0)}return a});function p(e){if(o[f]=e,c){let[e,n]=y(f);return c[f]=n,t(l[f],e)}return t(l[f])}}}function z(e,t){return C(()=>e(t||{}))}var B=e=>`Stale read from <${e}>.`;function fe(e){let t=`fallback`in e&&{fallback:()=>e.fallback};return S(R(()=>e.each,e.children,t||void 0))}function V(e){let t=e.keyed,n=S(()=>e.when,void 0,void 0),r=t?n:S(n,void 0,{equals:(e,t)=>!e==!t});return S(()=>{let i=r();if(i){let a=e.children;return typeof a==`function`&&a.length>0?C(()=>a(t?i:()=>{if(!C(r))throw B(`Show`);return n()})):a}return e.fallback},void 0,void 0)}var pe=e=>S(()=>e());function me(e,t,n){let r=n.length,i=t.length,a=r,o=0,s=0,c=t[i-1].nextSibling,l=null;for(;o<i||s<a;)if(t[o]===n[s])o++,s++;else{for(;t[i-1]===n[a-1];)i--,a--;if(i===o){let t=a<r?s?n[s-1].nextSibling:n[a-s]:c;for(;s<a;)e.insertBefore(n[s++],t)}else if(a===s)for(;o<i;)l&&l.has(t[o])||t[o].remove(),o++;else if(t[o]===n[a-1]&&n[s]===t[i-1]){let r=t[--i].nextSibling;e.insertBefore(n[s++],t[o++].nextSibling),e.insertBefore(n[--a],r),t[i]=n[a]}else{if(!l){l=new Map;let e=s;for(;e<a;)l.set(n[e],e++)}let r=l.get(t[o]);if(r!=null){if(s<r&&r<a){let c=o,u=1,d;for(;++c<i&&c<a&&(d=l.get(t[c]))!=null&&d===r+u;)u++;if(u>r-s){let i=t[o];for(;s<r;)e.insertBefore(n[s++],i)}else e.replaceChild(n[s++],t[o++])}else o++}else t[o++].remove()}}}var H=`_$DX_DELEGATE`;function he(e,t,n,r={}){let i;return _(r=>{i=r,t===document?e():q(t,e(),!n&&t.firstChild?null:void 0,n)},r.owner),()=>{i(),t.textContent=``}}function U(e,t,n,r){let i,a=()=>{let t=r?document.createElementNS(`http://www.w3.org/1998/Math/MathML`,`template`):document.createElement(`template`);return t.innerHTML=e,n?t.content.firstChild.firstChild:r?t.firstChild:t.content.firstChild},o=t?()=>C(()=>document.importNode(i||=a(),!0)):()=>(i||=a()).cloneNode(!0);return o.cloneNode=o,o}function W(e,t=window.document){let n=t[H]||(t[H]=new Set);for(let r=0,i=e.length;r<i;r++){let i=e[r];n.has(i)||(n.add(i),t.addEventListener(i,ve))}}function G(e,t,n){_e(e)||(n==null?e.removeAttribute(t):e.setAttribute(t,n))}function K(e,t,n){n==null?e.style.removeProperty(t):e.style.setProperty(t,n)}function ge(e,t,n){return C(()=>e(t,n))}function q(e,t,n,r){if(n!==void 0&&!r&&(r=[]),typeof t!=`function`)return J(e,t,r,n);b(r=>J(e,t(),r,n),r)}function _e(t){return!!e.context&&!e.done&&(!t||t.isConnected)}function ve(t){if(e.registry&&e.events&&e.events.find(([e,n])=>n===t))return;let n=t.target,r=`$$${t.type}`,i=t.target,a=t.currentTarget,o=e=>Object.defineProperty(t,"target",{configurable:!0,value:e}),s=()=>{let e=n[r];if(e&&!n.disabled){let i=n[`${r}Data`];if(i===void 0?e.call(n,t):e.call(n,i,t),t.cancelBubble)return}return n.host&&typeof n.host!=`string`&&!n.host._$host&&n.contains(t.target)&&o(n.host),!0},c=()=>{for(;s()&&(n=n._$host||n.parentNode||n.host););};Object.defineProperty(t,"currentTarget",{configurable:!0,get(){return n||document}}),e.registry&&!e.done&&(e.done=_$HY.done=!0);try{if(t.composedPath){let e=t.composedPath();o(e[0]);for(let t=0;t<e.length-2&&(n=e[t],s());t++){if(n._$host){n=n._$host,c();break}if(n.parentNode===a)break}}else c();o(i)}finally{t=n=null}}function J(e,t,n,r,i){let a=_e(e);if(a){!n&&(n=[...e.childNodes]);let t=[];for(let e=0;e<n.length;e++){let r=n[e];r.nodeType===8&&r.data.slice(0,2)===`!$`?r.remove():t.push(r)}n=t}for(;typeof n==`function`;)n=n();if(t===n)return n;let o=typeof t,s=r!==void 0;if(e=s&&n[0]&&n[0].parentNode||e,o===`string`||o===`number`){if(a||o===`number`&&(t=t.toString(),t===n))return n;if(s){let i=n[0];i&&i.nodeType===3?i.data!==t&&(i.data=t):i=document.createTextNode(t),n=Y(e,n,r,i)}else n=n!==``&&typeof n==`string`?e.firstChild.data=t:e.textContent=t}else if(t==null||o===`boolean`){if(a)return n;n=Y(e,n,r)}else if(o===`function`)return b(()=>{let i=t();for(;typeof i==`function`;)i=i();n=J(e,i,n,r)}),()=>n;else if(Array.isArray(t)){let o=[],c=n&&Array.isArray(n);if(ye(o,t,n,i))return b(()=>n=J(e,o,n,r,!0)),()=>n;if(a){if(!o.length)return n;if(r===void 0)return n=[...e.childNodes];let t=o[0];if(t.parentNode!==e)return n;let i=[t];for(;(t=t.nextSibling)!==r;)i.push(t);return n=i}if(o.length===0){if(n=Y(e,n,r),s)return n}else c?n.length===0?be(e,o,r):me(e,n,o):(n&&Y(e),be(e,o));n=o}else if(t.nodeType){if(a&&t.parentNode)return n=s?[t]:t;if(Array.isArray(n)){if(s)return n=Y(e,n,r,t);Y(e,n,null,t)}else n==null||n===``||!e.firstChild?e.appendChild(t):e.replaceChild(t,e.firstChild);n=t}return n}function ye(e,t,n,r){let i=!1;for(let a=0,o=t.length;a<o;a++){let o=t[a],s=n&&n[e.length],c;if(o!=null&&o!==!0&&o!==!1){if((c=typeof o)==`object`&&o.nodeType)e.push(o);else if(Array.isArray(o))i=ye(e,o,s)||i;else if(c===`function`){if(r){for(;typeof o==`function`;)o=o();i=ye(e,Array.isArray(o)?o:[o],Array.isArray(s)?s:[s])||i}else e.push(o),i=!0}else{let t=String(o);s&&s.nodeType===3&&s.data===t?e.push(s):e.push(document.createTextNode(t))}}}return i}function be(e,t,n=null){for(let r=0,i=t.length;r<i;r++)e.insertBefore(t[r],n)}function Y(e,t,n,r){if(n===void 0)return e.textContent=``;let i=r||document.createTextNode(``);if(t.length){let r=!1;for(let a=t.length-1;a>=0;a--){let o=t[a];if(i!==o){let t=o.parentNode===e;!r&&!a?t?e.replaceChild(i,o):e.insertBefore(i,n):t&&o.remove()}else r=!0}}else e.insertBefore(i,n);return[i]}async function X(e){let t=await fetch(e,{headers:{accept:`application/json`}});if(!t.ok){let e=await t.text().catch(()=>``);throw Error(`HTTP ${t.status}${e?`: ${e.slice(0,200)}`:``}`)}return await t.json()}function xe(){return X(`/api/projects`)}function Se(){return X(`/api/version`)}function Ce(e){return X(`/api/projects/${encodeURIComponent(e)}/files`)}function we(e,t){return`/render/${encodeURIComponent(e)}/${t.split(`/`).map(encodeURIComponent).join(`/`)}?embed=1`}function Te(e,t){return`/events?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`}function Ee(){return`/events`}function De(e){return X(`/api/marks?project=${encodeURIComponent(e)}`)}async function Oe(e){let t=await fetch(`/api/marks`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw Error(`HTTP ${t.status}`);let n=await t.json();if(!n.ok)throw Error(n.error??`添加标记失败`);return n.mark}async function ke(e){let t=await fetch(`/api/marks?project=${encodeURIComponent(e)}`,{method:`DELETE`});if(!t.ok)throw Error(`HTTP ${t.status}`);return(await t.json()).cleared}var Ae={"x-paintress":`1`};async function je(e){let t=await fetch(`/api/projects/${encodeURIComponent(e)}`,{method:`DELETE`,headers:Ae}),n=await t.json();if(!t.ok||!n.ok)throw Error(n.error??`HTTP ${t.status}`);return n.unwatched===!0}async function Me(){let e=await fetch(`/api/shutdown`,{method:`POST`,headers:Ae}),t=await e.json();if(!e.ok||!t.ok)throw Error(t.error??`HTTP ${e.status}`);return t.connections??0}function Ne(e,t){return X(`/api/changes?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`)}var Z=U(`<div class=modal-actions>`),Pe=U(`<div class=modal-backdrop><div class=modal role=dialog aria-modal=true><h3>`),Fe=U(`<p class=modal-body>`),Ie=U(`<button class=ghost>`),Le=U(`<button class="ghost danger">`);function Re(e){x(()=>{e.open&&queueMicrotask(()=>{(e.initialFocus?.()??document.querySelector(`.modal-actions button`))?.focus()})});let t=t=>{t.key===`Escape`&&(t.preventDefault(),e.onClose())};return z(V,{get when(){return e.open},get children(){var n=Pe(),r=n.firstChild,i=r.firstChild;return n.$$mousedown=t=>{t.target===t.currentTarget&&e.onClose()},r.$$keydown=t,q(i,()=>e.title),q(r,()=>e.children,null),q(r,z(V,{get when(){return e.actions},get children(){var t=Z();return q(t,()=>e.actions),t}}),null),b(()=>G(r,`aria-label`,e.title)),n}})}function ze(e){let t;return z(Re,{get open(){return e.open},get title(){return e.title},get onClose(){return e.onCancel},initialFocus:()=>t,get actions(){return[(()=>{var t=Ie();return t.$$click=()=>e.onCancel(),q(t,()=>e.cancelLabel),t})(),(()=>{var n=Le();return n.$$click=()=>e.onConfirm(),ge(e=>{t=e},n),q(n,()=>e.confirmLabel),n})()]},get children(){var t=Fe();return q(t,()=>e.body),t}})}W([`mousedown`,`keydown`,`click`]);function Be(e){let t=e.trim().replace(/^v/i,``),n=t.indexOf(`-`);return{nums:(n>=0?t.slice(0,n):t).split(`.`).map(e=>{let t=Number.parseInt(e,10);return Number.isFinite(t)?t:0}),prerelease:n>=0}}function Ve(e,t){let n=Be(e),r=Be(t),i=Math.max(n.nums.length,r.nums.length);for(let e=0;e<i;e+=1){let t=n.nums[e]??0,i=r.nums[e]??0;if(t!==i)return t>i?1:-1}return n.prerelease===r.prerelease?0:n.prerelease?-1:1}function He(e,t){return Ve(e,t)>0}var Ue=.1,We=.02;function Ge(e,t){return t===0||Math.abs(e)>=Math.abs(t)?null:t>0?-1:1}function Q(e,t=Ue){return Math.min(4,Math.max(t,e))}function Ke(e,t,n,r,i=Ue){let a=Q(e.z*t,i);if(a===e.z)return e;let o=a/e.z;return{z:a,tx:n-(n-e.tx)*o,ty:r-(r-e.ty)*o}}function qe(e,t,n,r=Ue){return Ke(e,Q(t,r)/e.z,n.width/2,n.height/2,r)}function Je(e,t,n=24){let r=Math.max(1,e.w),i=Math.max(1,e.h),a=Math.max(1,t.width-n*2),o=Math.max(1,t.height-n*2),s=Q(Math.min(a/r,o/i),We);return{z:s,tx:(t.width-r*s)/2-e.x*s,ty:(t.height-i*s)/2-e.y*s}}function Ye(e,t,n){return{z:e.z,tx:e.tx+t,ty:e.ty+n}}function Xe(e){return`translate(${e.tx}px, ${e.ty}px) scale(${e.z})`}function Ze(e){if(e===null||e===``)return null;let t;try{t=JSON.parse(e)}catch{return null}if(typeof t!=`object`||!t)return null;let{z:n,tx:r,ty:i}=t;return typeof n!=`number`||typeof r!=`number`||typeof i!=`number`||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(i)||n<=0?null:{z:Q(n,We),tx:r,ty:i}}var Qe=3,$e=.0015;function et({doc:e,onChange:t,onLevelStep:n}){let r=e.querySelector(`.ptr-canvas`);if(r===null)return null;let i=r;i.style.transformOrigin=`0 0`;let a={z:1,tx:0,ty:0},o=!1,s=!1,c=!1,l=null,u=()=>({width:e.documentElement.clientWidth||1,height:e.documentElement.clientHeight||1});function d(){i.style.transform=Xe(a),t?.(a)}function f(e){a=e,d()}function p(){i.style.transition=``}function m(e){i.style.transition=`transform 180ms ease-out`,f(e),setTimeout(p,220)}function h(t){e.documentElement.style.cursor=t}function g(e){if(e.preventDefault(),e.altKey&&n!==void 0){let t=Ge(e.deltaX,e.deltaY);t!==null&&n(t,{x:e.clientX,y:e.clientY});return}p();let t=1;e.deltaMode===1?t=16:e.deltaMode===2&&(t=u().height);let r=a.z<.1?We:Ue;f(Ke(a,Math.exp(-e.deltaY*t*$e),e.clientX,e.clientY,r))}function _(t){return t.button===1?!0:t.button===0?o?!0:t.target===i||t.target===e.body||t.target===e.documentElement:!1}function v(e){_(e)&&(e.preventDefault(),l={lastX:e.clientX,lastY:e.clientY,startX:e.clientX,startY:e.clientY},s=!1)}function y(t){if(!l)return;if(!s){if(Math.hypot(t.clientX-l.startX,t.clientY-l.startY)<Qe)return;s=!0,p(),e.body.style.userSelect=`none`,h(`grabbing`)}let n=t.clientX-l.lastX,r=t.clientY-l.lastY;l.lastX=t.clientX,l.lastY=t.clientY,f(Ye(a,n,r))}function b(){l&&(l=null,s&&(s=!1,c=!0,e.body.style.userSelect=``,h(o?`grab`:``)))}function x(e){o!==e&&(o=e,s||h(e?`grab`:``))}function S(e){if(e.key===` `&&!e.metaKey&&!e.ctrlKey&&!e.altKey){let t=e.target,n=t?.tagName??``;if(n===`INPUT`||n===`TEXTAREA`||t?.isContentEditable===!0)return;x(!0),e.preventDefault()}else T(e)&&e.preventDefault()}function C(e){e.key===` `&&x(!1)}function w(){x(!1),b()}function T(e){return(e.ctrlKey||e.metaKey)&&(e.key===`0`||e.key===`)`||e.code===`Digit0`||e.code===`Numpad0`)?(f(qe(a,1,u())),!0):e.shiftKey&&!e.ctrlKey&&!e.metaKey&&(e.key===`!`||e.code===`Digit1`)?(E(),!0):!1}function E(){f(Je({x:0,y:0,w:i.offsetWidth,h:i.offsetHeight},u()))}function ee(e){let t=u();m({z:a.z,tx:t.width/2-(e.x+e.w/2)*a.z,ty:t.height/2-(e.y+e.h/2)*a.z})}return e.addEventListener(`wheel`,g,{passive:!1}),e.addEventListener(`mousedown`,v,!0),e.addEventListener(`mousemove`,y,!0),e.addEventListener(`mouseup`,b,!0),e.addEventListener(`keydown`,S),e.addEventListener(`keyup`,C),e.defaultView?.addEventListener(`blur`,w),{get:()=>a,set:f,fitAll:E,centerOn:ee,focusBox:e=>m(Je(e,u())),zoomToPercent:e=>f(qe(a,e,u())),consumeDragClick:()=>{let e=c;return c=!1,e},handleKey:T,setSpace:x,destroy:()=>{e.removeEventListener(`wheel`,g),e.removeEventListener(`mousedown`,v,!0),e.removeEventListener(`mousemove`,y,!0),e.removeEventListener(`mouseup`,b,!0),e.removeEventListener(`keydown`,S),e.removeEventListener(`keyup`,C),e.defaultView?.removeEventListener(`blur`,w)}}}var tt=`[data-ptr-selected]{outline:2px solid #908030 !important;outline-offset:-2px;}
2
+ [data-ptr-hover]:not([data-ptr-selected]){outline:1px dashed rgba(144,128,48,.55) !important;outline-offset:-2px;}
3
+ [data-ptr-changed]{box-shadow:inset 0 0 0 2px rgba(144,128,48,1);animation:ptr-pulse 2s ease-in-out infinite;}
4
+ @keyframes ptr-pulse{0%,100%{box-shadow:inset 0 0 0 2px rgba(144,128,48,1)}50%{box-shadow:inset 0 0 0 2px rgba(144,128,48,.25)}}
5
+ @media (prefers-reduced-motion: reduce){[data-ptr-changed]{animation:none !important;}}
6
+ html{cursor:default;}
7
+ html,body{overflow:hidden;}
8
+ html,body{background:var(--ptr-canvas-bg,#ffffff);}`;function nt(e){let{clientX:t,rectLeft:n,rectWidth:r,pad:i,thumb:a,count:o}=e,s=r-i*2-a;if(s<=0||o<2)return null;let c=(t-n-i-a/2)/s;return Math.max(0,Math.min(o-1,c*(o-1)))}var rt=U(`<div class=canvas-bg><span class=canvas-bg-label></span><div class=canvas-bg-track role=slider tabindex=0 aria-valuemin=0 style=--pad:3px;--thumb:20px><div class=canvas-bg-thumb aria-hidden=true><span class=canvas-bg-dot>`),$=[`light`,`gray`,`dark`],it={light:`Light`,gray:`Gray`,dark:`Dark`},at={light:`#ffffff`,gray:`#d2cec7`,dark:`#242b36`},ot=`paintress:canvas-bg`;function st(e){return $.includes(e)}function ct(){try{let e=localStorage.getItem(ot);if(st(e))return e}catch{}return`light`}function lt(e){try{localStorage.setItem(ot,e)}catch{}}var ut=3,dt=20;function ft(e){let[t,n]=y(!1),[r,i]=y(null),[a,o]=y(29),s,c=()=>$.indexOf(e.value),l=()=>r()??c();T(()=>{let e=()=>{let e=s;if(!e)return;let t=e.getBoundingClientRect().width-6-dt;o(t/($.length-1))};if(e(),typeof ResizeObserver>`u`||!s)return;let t=new ResizeObserver(e);t.observe(s),E(()=>t.disconnect())});function u(e){let t=s;if(!t)return c();let n=t.getBoundingClientRect();return nt({clientX:e.clientX,rectLeft:n.left,rectWidth:n.width,pad:ut,thumb:dt,count:$.length})??c()}function d(t){let n=$[Math.round(t)];n!==void 0&&n!==e.value&&e.onChange(n)}function f(e){e.button===0&&(e.preventDefault(),e.currentTarget.setPointerCapture(e.pointerId),n(!0),i(u(e)))}function p(e){t()&&i(u(e))}function m(e){if(!t())return;let r=e.currentTarget;n(!1),i(null),d(u(e)),r.hasPointerCapture(e.pointerId)&&r.releasePointerCapture(e.pointerId)}function h(){n(!1),i(null)}function g(t){let n=t.key===`ArrowRight`||t.key===`ArrowUp`?1:t.key===`ArrowLeft`||t.key===`ArrowDown`?-1:0;if(n===0)return;t.preventDefault();let r=$[Math.max(0,Math.min($.length-1,c()+n))];r!==void 0&&e.onChange(r)}return(()=>{var n=rt(),r=n.firstChild,i=r.nextSibling,o=i.firstChild.firstChild;q(r,()=>it[e.value]),i.$$keydown=g,i.addEventListener(`lostpointercapture`,h),i.addEventListener(`pointercancel`,h),i.$$pointerup=m,i.$$pointermove=p,i.$$pointerdown=f;var u=s;return typeof u==`function`?ge(u,i):s=i,b(n=>{var r=!!t(),s=e.label,u=$.length-1,d=c(),f=it[e.value],p=e.label,m=`${a()}px`,h=String(l()),g=at[e.value];return r!==n.e&&i.classList.toggle(`dragging`,n.e=r),s!==n.t&&G(i,`aria-label`,n.t=s),u!==n.a&&G(i,`aria-valuemax`,n.a=u),d!==n.o&&G(i,`aria-valuenow`,n.o=d),f!==n.i&&G(i,`aria-valuetext`,n.i=f),p!==n.n&&G(i,`title`,n.n=p),m!==n.s&&K(i,`--step`,n.s=m),h!==n.h&&K(i,`--pos`,n.h=h),g!==n.r&&K(o,`--dot`,n.r=g),n},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0,n:void 0,s:void 0,h:void 0,r:void 0}),n})()}W([`pointerdown`,`pointermove`,`pointerup`,`keydown`]);var pt={zh:{projects:`项目`,noProjects:`还没有注册项目`,projectFiles:e=>`${e} 个设计稿`,designs:`设计稿`,depth:`depth`,noFiles:`没有 .ptr / .pen 文件`,marks:`标记`,clear:`清空`,liveReload:`实时刷新`,refreshList:`刷新列表`,pickProject:`从左侧选择项目`,pickFile:`从左侧选择一个设计稿`,fit:`适应`,fitTitle:`适应窗口(Shift+1)`,actualSize:`1:1`,actualSizeTitle:`100%(Ctrl/Cmd+0)`,rerender:`重新渲染`,allBoards:`全部`,canvasBackground:`画布背景`,language:`界面语言`,unwatch:`解绑`,unwatchTitle:`解绑项目`,unwatchBody:e=>`「${e}」将从项目列表中移除。设计稿文件不受影响;agent 下次启动会自动重新注册。`,closeServer:`关闭服务器`,closeServerTitle:`关闭 web 进程`,closeServerBody:(e,t)=>`将断开 ${t} 个长连接并停止进程 pid ${e}。之后可用 paintress web 重新启动。`,cancel:`取消`,serverClosed:`服务已关闭`,serverClosedHint:`可以关闭此页面;需要时重新启动 paintress web。`,updateAvailable:(e,t)=>`新版 v${e}(当前 v${t})`,updateTitle:`有新版本`,updatePrompt:`把 paintress 升级到最新版:npm install -g paintress@latest,完成后重启 MCP 会话。`,updateSteps:`把上面这句话交给你的 AI 即可;或自己在终端执行那条命令。`,updateRepo:`GitHub`,copy:`复制`,copied:`已复制`,close:`关闭`,versionTitle:`版本`},en:{projects:`Projects`,noProjects:`No projects registered yet`,projectFiles:e=>`${e} design${e===1?``:`s`}`,designs:`Designs`,depth:`depth`,noFiles:`No .ptr / .pen files`,marks:`Marks`,clear:`Clear`,liveReload:`Live reload`,refreshList:`Refresh`,pickProject:`Pick a project on the left`,pickFile:`Pick a design on the left`,fit:`Fit`,fitTitle:`Fit to window (Shift+1)`,actualSize:`1:1`,actualSizeTitle:`100% (Ctrl/Cmd+0)`,rerender:`Re-render`,allBoards:`All`,canvasBackground:`Canvas background`,language:`Language`,unwatch:`Unbind`,unwatchTitle:`Unbind project`,unwatchBody:e=>`“${e}” will be removed from the project list. Design files are untouched; the agent re-registers it on next start.`,closeServer:`Stop server`,closeServerTitle:`Stop web process`,closeServerBody:(e,t)=>`This drops ${t} live connection${t===1?``:`s`} and stops process pid ${e}. Restart later with paintress web.`,cancel:`Cancel`,serverClosed:`Server stopped`,serverClosedHint:`You can close this page; run paintress web to start it again.`,updateAvailable:(e,t)=>`v${e} available (running v${t})`,updateTitle:`Update available`,updatePrompt:`Upgrade paintress to the latest version: npm install -g paintress@latest, then restart the MCP session.`,updateSteps:`Hand that line to your AI, or run the command yourself in a terminal.`,updateRepo:`GitHub`,copy:`Copy`,copied:`Copied`,close:`Close`,versionTitle:`Version`}};function mt(e){return pt[e]}function ht(e){return(e?.languages?.[0]??e?.language??``).toLowerCase().startsWith(`zh`)?`zh`:`en`}var gt=`paintress:lang`;function _t(e){return e===`zh`||e===`en`}function vt(){try{let e=localStorage.getItem(gt);if(_t(e))return e}catch{}return ht(typeof navigator>`u`?void 0:navigator)}function yt(e){try{localStorage.setItem(gt,e)}catch{}}var bt=U(`<button class="badge badge-stack"><span>v</span><span class=badge-new>v`),xt=U(`<ul class=list>`),St=U(`<section class="section files"><h2> <span class=hint> `),Ct=U(`<section class="section marks"><h2><button class="ghost tiny"></button></h2><ul class=list>`),wt=U(`<span class=hint>pid `),Tt=U(`<div class=error>`),Et=U(`<code>/`),Dt=U(`<span class="hint zoom">%`),Ot=U(`<button class="ghost tiny">`),kt=U(`<button class=ghost>`),At=U(`<span class=tool-sep>`),jt=U(`<div class=boardbar><button class=board>`),Mt=U(`<iframe class=preview title="design preview"sandbox="allow-same-origin allow-scripts">`),Nt=U(`<div class=modal-backdrop><div class=modal role=status><h3></h3><p class=modal-body>`),Pt=U(`<p class=modal-body>`),Ft=U(`<pre class=prompt>`),It=U(`<div class=app><aside class=sidebar><header class=sidebar-head><img class=brand-mark src=/logo.svg alt><h1>paintress</h1></header><section class=section><h2></h2></section><footer class=sidebar-foot><label class=toggle><input type=checkbox></label><button class=ghost></button><button class=ghost></button></footer></aside><main class=stage><div class=toolbar><div class=crumbs></div><div class=tools><div class=lang-switch role=group><button class=lang-opt>中</button><button class=lang-opt>EN`),Lt=U(`<span class=badge>v`),Rt=U(`<p class=empty><br><code>paintress watch &lt;name&gt; &lt;dir&gt;`),zt=U(`<li class=row><button class=item><span class=item-name></span><span class=item-meta></span></button><button class=row-action>×`),Bt=U(`<p class=empty>`),Vt=U(`<li><button class=item><span class=item-name>`),Ht=U(`<li><button class=item><span class=item-name><b class=mark-no>#</b> </span><span class=item-meta>`),Ut=U(`<span class=sep>/`),Wt=U(`<button class=crumb>`),Gt=U(`<p>`),Kt=U(`<div class=placeholder>`),qt=U(`<button class=board><span class=board-size>×`),Jt=U(`<a class=ghost href=https://github.com/aliciai-dev/paintress target=_blank rel=noreferrer>`);function Yt(e){return typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(e):e.replace(/["\\]/g,`\\$&`)}function Xt(){let[e,t]=y([]),[n,r]=y(null),[i,a]=y([]),[o,s]=y(null),[c,l]=y([]),[u,d]=y(null),[f,p]=y([]),[m,h]=y(null),[g,_]=y({}),[v,S]=y(0),[C,ee]=y(!0),[te,D]=y(``),[ne,O]=y([]),[re,k]=y(null),[ie,ae]=y(1),[A,j]=y(vt()),[M,oe]=y(ct()),[se,N]=y(null),[P,F]=y(!1),[ce,le]=y(!1),[ue,I]=y(!1),L=()=>mt(A()),de,R,B=null,me;function H(){let e=new URLSearchParams,t=n(),r=o();t&&e.set(`project`,t),r&&e.set(`file`,r);let i=e.toString();history.replaceState(null,``,i?`?${i}`:location.pathname)}async function he(){try{let e=await xe();t(e.projects),_({pid:e.pid,version:e.version,latest:e.latest??null,connections:e.connections}),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}async function U(){try{let e=await Se();_({pid:e.pid,version:e.version,latest:e.latest??null,connections:e.connections})}catch{}}async function W(e){try{l((await De(e)).marks)}catch{l([])}}async function K(e){r(e),s(null),d(null),p([]);try{let t=await Ce(e);a(t.files),h(null)}catch(e){a([]),h(e instanceof Error?e.message:String(e))}W(e),H()}async function _e(){await he();let t=n();if(t!==null){if(!e().some(e=>e.name===t))r(null),s(null),a([]),d(null),p([]),H();else{try{let n=await Ce(t),r=i();a(n.files);let c=o();if(c===null){let i=n.files.filter(e=>!r.includes(e));i.length>0&&e().some(e=>e.name===t)&&ve(i[0])}else n.files.includes(c)||(s(null),d(null),p([]),H())}catch{}W(t)}}}function ve(e){s(e),d(null),p([]),H()}function J(){let e=g().latest??null,t=g().version??null;return e===null||t===null?null:He(e,t)?e:null}async function ye(){try{await navigator.clipboard.writeText(L().updatePrompt),I(!0),setTimeout(()=>I(!1),1500)}catch{}}function be(e){N({kind:`unwatch`,name:e})}function Y(){N({kind:`shutdown`}),U()}function X(){let e=se();return e===null?{title:``,body:``,confirm:``}:e.kind===`unwatch`?{title:L().unwatchTitle,body:L().unwatchBody(e.name),confirm:L().unwatch}:{title:L().closeServerTitle,body:L().closeServerBody(g().pid??0,g().connections??0),confirm:L().closeServer}}async function Ae(){let e=se();if(N(null),e!==null)try{e.kind===`unwatch`?(await je(e.name),await _e()):(await Me(),F(!0))}catch(e){h(e instanceof Error?e.message:String(e))}}T(()=>{let e=new EventSource(Ee());e.addEventListener(`projects`,()=>void _e()),E(()=>e.close());let t=setTimeout(()=>{P()||U()},4e3),n=setInterval(()=>{P()||U()},6e4);E(()=>{clearTimeout(t),clearInterval(n)});let r=e=>{let t=e.target;return t?.tagName===`INPUT`||t?.tagName===`TEXTAREA`||t?.isContentEditable===!0},i=e=>{r(e)||(e.key===` `&&!e.ctrlKey&&!e.metaKey&&!e.altKey?(B?.setSpace(!0),e.preventDefault()):B?.handleKey(e)===!0&&e.preventDefault())},a=e=>{e.key===` `&&B?.setSpace(!1)};window.addEventListener(`keydown`,i),window.addEventListener(`keyup`,a),E(()=>{window.removeEventListener(`keydown`,i),window.removeEventListener(`keyup`,a)});let o=new URLSearchParams(location.search);(async()=>{await he();let e=o.get(`project`);if(e){await K(e);let t=o.get(`file`);t&&ve(t)}})()});let Z=()=>de?.contentDocument??null;function Pe(){let e=Z();if(!e||e.getElementById(`ptr-style`))return;let t=e.createElement(`style`);t.id=`ptr-style`,t.textContent=tt,e.head.appendChild(t)}function Fe(){let e=Z();if(!e)return;for(let t of e.querySelectorAll(`[data-ptr-selected]`))t.removeAttribute(`data-ptr-selected`);let t=u();t&&e.querySelector(`[data-node-id="${Yt(t)}"]`)?.setAttribute(`data-ptr-selected`,``)}function Ie(e){return e.parentElement?.closest(`[data-node-id]`)??null}function Le(e){return e.querySelector(`:scope > [data-node-id]`)}function Be(e){if(B?.consumeDragClick()===!0){e.preventDefault(),e.stopPropagation();return}let t=e.target?.closest(`[data-node-id]`);t&&(e.preventDefault(),e.stopPropagation(),Qe(e.altKey?Ie(t)??t:t))}function Ve(e,t){let n=Z();if(!n)return;let r=u(),i=r===null?null:n.querySelector(`[data-node-id="${Yt(r)}"]`);if(i===null){let e=(n.elementFromPoint(t.x,t.y)?.closest(`[data-node-id]`))?.dataset.nodeId;e!==void 0&&$e(e);return}let a=(e===1?Ie(i):Le(i))?.dataset.nodeId;a!==void 0&&$e(a)}function Ue(e){let t=Z();if(t){for(let e of t.querySelectorAll(`[data-ptr-hover]`))e.removeAttribute(`data-ptr-hover`);(e.target?.closest(`[data-node-id]`))?.setAttribute(`data-ptr-hover`,``)}}async function We(){let e=n(),t=o();if(e&&t)try{let n=await Ne(e,t);if(!n.ok||n.ids.length===0)return;let r=Z();if(!r)return;for(let e of n.ids)r.querySelector(`[data-node-id="${Yt(e)}"]`)?.setAttribute(`data-ptr-changed`,``)}catch{}}function Ge(){let e=Z();return e?Array.from(e.querySelectorAll(`[data-board]`)).map(e=>{let t=e.dataset;return{id:t.nodeId??``,...t.boardName===void 0?{}:{name:t.boardName},x:Number(t.boardX??0),y:Number(t.boardY??0),w:Number(t.boardW??0),h:Number(t.boardH??0)}}):[]}function Q(){let e=n(),t=o();return e&&t?`paintress:viewport:${e}/${t}`:null}function Ke(e){let t=Q();t!==null&&(clearTimeout(me),me=setTimeout(()=>{try{localStorage.setItem(t,JSON.stringify(e))}catch{}},300))}function qe(){let e=Z();e&&(Pe(),rt(),B?.destroy(),B=et({doc:e,onChange:e=>{ae(e.z),Ke(e)},onLevelStep:Ve}),O(Ge()),k(null),Je(),e.addEventListener(`click`,Be,!0),e.addEventListener(`mousemove`,Ue,!0),Fe(),We())}function Je(){if(!B)return;let e=Q(),t=e===null?null:localStorage.getItem(e),n=t===null?null:Ze(t);n===null?B.fitAll():B.set(n)}function Ye(e){if(k(e),!B)return;if(e===null){B.fitAll();return}let t=ne().find(t=>t.id===e);t&&B.focusBox({x:t.x,y:t.y,w:t.w,h:t.h})}function Xe(e){let t=[],n=e;for(;n;){let e=n.closest(`[data-node-id]`);if(!e)break;let r=e;t.unshift({id:r.dataset.nodeId??``,type:r.dataset.nodeType??`?`,name:r.dataset.nodeName}),n=e.parentElement}return t}async function Qe(e){let t=e.dataset.nodeId??``;if(t===``)return;let r=Xe(e);d(t),p(r),Fe();let i=n();if(i)try{await Oe({project:i,nodeId:t,...e.dataset.nodeName===void 0?{}:{name:e.dataset.nodeName},...e.dataset.nodeType===void 0?{}:{type:e.dataset.nodeType},path:r.map(e=>e.name??e.type).join(` / `)}),l((await De(i)).marks),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}function $e(e){d(e),Fe();let t=Z(),n=t?.querySelector(`[data-node-id="${Yt(e)}"]`);if(!n||!t){p([]);return}p(Xe(n));let r=t.querySelector(`.ptr-canvas`);if(!r||!B){n.scrollIntoView({block:`center`});return}let i=n.getBoundingClientRect(),a=r.getBoundingClientRect(),o=B.get().z;B.centerOn({x:(i.left-a.left)/o,y:(i.top-a.top)/o,w:i.width/o,h:i.height/o})}async function nt(){let e=n();if(e)try{await ke(e),l([]),d(null),p([]),Fe()}catch(e){h(e instanceof Error?e.message:String(e))}}x(()=>{let e=A();document.documentElement.lang=e===`zh`?`zh-CN`:`en`,yt(e)});function rt(){if(R!==void 0){if(o()===null)R.style.removeProperty(`--canvas-bg`),R.removeAttribute(`data-canvas-bg`);else{let e=at[M()];R.style.setProperty(`--canvas-bg`,e),R.setAttribute(`data-canvas-bg`,M())}Z()?.documentElement.style.setProperty(`--ptr-canvas-bg`,at[M()])}}x(()=>{M(),lt(M()),rt()}),x(w([n,o,v],()=>{let e=n(),t=o();D(e&&t?`${we(e,t)}&r=${v()}`:``),rt()}));let $=null;return x(w([n,o,C],()=>{$?.close(),$=null;let e=n(),t=o();C()&&e&&t&&($=new EventSource(Te(e,t)),$.addEventListener(`change`,()=>S(e=>e+1)))})),E(()=>$?.close()),(()=>{var t=It(),r=t.firstChild,a=r.firstChild;a.firstChild.nextSibling;var s=a.nextSibling,l=s.firstChild,d=s.nextSibling,p=d.firstChild,h=p.firstChild,_=p.nextSibling,v=_.nextSibling,y=r.nextSibling,x=y.firstChild,w=x.firstChild,T=w.nextSibling,E=T.firstChild,D=E.firstChild,O=D.nextSibling;return q(a,z(V,{get when(){return g().version},get children(){return z(V,{get when(){return J()},get fallback(){return(()=>{var e=Lt();return e.firstChild,q(e,()=>g().version,null),b(()=>G(e,`title`,L().versionTitle)),e})()},get children(){var e=bt(),t=e.firstChild;t.firstChild;var n=t.nextSibling;return n.firstChild,e.$$click=()=>le(!0),q(t,()=>g().version,null),q(n,J,null),b(()=>G(e,`title`,L().updateTitle)),e}})}}),null),q(l,()=>L().projects),q(s,z(V,{get when(){return e().length>0},get fallback(){return(()=>{var e=Rt(),t=e.firstChild;return q(e,()=>L().noProjects,t),e})()},get children(){var t=xt();return q(t,z(fe,{get each(){return e()},children:e=>(()=>{var t=zt(),r=t.firstChild,i=r.firstChild,a=i.nextSibling,o=r.nextSibling;return r.$$click=()=>void K(e.name),q(i,()=>e.name),q(a,()=>L().projectFiles(e.fileCount)),o.$$click=()=>be(e.name),b(t=>{var i=n()===e.name,a=L().unwatch,s=`${L().unwatch} ${e.name}`;return i!==t.e&&r.classList.toggle(`active`,t.e=i),a!==t.t&&G(o,`title`,t.t=a),s!==t.a&&G(o,`aria-label`,t.a=s),t},{e:void 0,t:void 0,a:void 0}),t})()})),t}}),null),q(r,z(V,{get when(){return n()},get children(){var t=St(),r=t.firstChild,a=r.firstChild,s=a.nextSibling,c=s.firstChild;return q(r,()=>L().designs,a),q(s,()=>L().depth,c),q(s,()=>e().find(e=>e.name===n())?.depth??`-`,null),q(t,z(V,{get when(){return i().length>0},get fallback(){return(()=>{var e=Bt();return q(e,()=>L().noFiles),e})()},get children(){var e=xt();return q(e,z(fe,{get each(){return i()},children:e=>(()=>{var t=Vt(),n=t.firstChild,r=n.firstChild;return n.$$click=()=>ve(e),q(r,e),b(()=>n.classList.toggle(`active`,o()===e)),t})()})),e}}),null),t}}),d),q(r,z(V,{get when(){return c().length>0},get children(){var e=Ct(),t=e.firstChild,n=t.firstChild,r=t.nextSibling;return q(t,()=>L().marks,n),n.$$click=()=>void nt(),q(n,()=>L().clear),q(r,z(fe,{get each(){return c()},children:e=>(()=>{var t=Ht(),n=t.firstChild,r=n.firstChild,i=r.firstChild;i.firstChild,i.nextSibling;var a=r.nextSibling;return n.$$click=()=>$e(e.nodeId),q(i,()=>e.shortId,null),q(r,()=>e.name??e.nodeId,null),q(a,()=>e.path),b(()=>n.classList.toggle(`active`,u()===e.nodeId)),t})()})),e}}),d),h.addEventListener(`change`,e=>ee(e.currentTarget.checked)),q(p,()=>L().liveReload,null),_.$$click=()=>void he(),q(_,()=>L().refreshList),v.$$click=()=>Y(),q(v,()=>L().closeServer),q(d,z(V,{get when(){return g().pid},get children(){var e=wt();return e.firstChild,q(e,()=>g().pid,null),e}}),null),ge(e=>{R=e},y),q(y,z(V,{get when(){return m()},get children(){var e=Tt();return q(e,m),e}}),x),q(w,z(V,{get when(){return f().length===0},get fallback(){return z(fe,{get each(){return f()},children:(e,t)=>[z(V,{get when(){return t()>0},get children(){return Ut()}}),(()=>{var t=Wt();return t.$$click=()=>$e(e.id),q(t,()=>e.name??e.type),b(()=>t.classList.toggle(`active`,u()===e.id)),t})()]})},get children(){return z(V,{get when(){return pe(()=>n()!==null)()&&o()!==null},get children(){var e=Et(),t=e.firstChild;return q(e,n,t),q(e,o,null),e}})}})),q(T,z(V,{get when(){return pe(()=>n()!==null)()&&o()!==null},get children(){return[(()=>{var e=Dt(),t=e.firstChild;return q(e,()=>Math.round(ie()*100),t),e})(),(()=>{var e=Ot();return e.$$click=()=>B?.fitAll(),q(e,()=>L().fit),b(()=>G(e,`title`,L().fitTitle)),e})(),(()=>{var e=Ot();return e.$$click=()=>B?.zoomToPercent(1),q(e,()=>L().actualSize),b(()=>G(e,`title`,L().actualSizeTitle)),e})(),(()=>{var e=kt();return e.$$click=()=>S(e=>e+1),q(e,()=>L().rerender),e})(),At()]}}),E),q(T,z(ft,{get value(){return M()},onChange:oe,get label(){return L().canvasBackground}}),E),D.$$click=()=>j(`zh`),O.$$click=()=>j(`en`),q(y,z(V,{get when(){return pe(()=>n()!==null)()&&o()!==null},get fallback(){return(()=>{var e=Kt();return q(e,z(V,{get when(){return n()},get fallback(){return(()=>{var e=Gt();return q(e,()=>L().pickProject),e})()},get children(){var e=Gt();return q(e,()=>L().pickFile),e}})),e})()},get children(){return[z(V,{get when(){return ne().length>1},get children(){var e=jt(),t=e.firstChild;return t.$$click=()=>Ye(null),q(t,()=>L().allBoards),q(e,z(fe,{get each(){return ne()},children:e=>(()=>{var t=qt(),n=t.firstChild,r=n.firstChild;return t.$$click=()=>Ye(e.id),q(t,()=>e.name??`#${e.id}`,n),q(n,()=>Math.round(e.w),r),q(n,()=>Math.round(e.h),null),b(()=>t.classList.toggle(`active`,re()===e.id)),t})()}),null),b(()=>t.classList.toggle(`active`,re()===null)),e}}),(()=>{var e=Mt();return e.addEventListener(`load`,qe),ge(e=>{de=e},e),b(()=>G(e,`src`,te())),e})()]}}),null),q(t,z(ze,{get open(){return se()!==null},get title(){return X().title},get body(){return X().body},get confirmLabel(){return X().confirm},get cancelLabel(){return L().cancel},onConfirm:()=>void Ae(),onCancel:()=>N(null)}),null),q(t,z(V,{get when(){return P()},get children(){var e=Nt(),t=e.firstChild.firstChild,n=t.nextSibling;return q(t,()=>L().serverClosed),q(n,()=>L().serverClosedHint),e}}),null),q(t,z(Re,{get open(){return ce()},get title(){return L().updateTitle},onClose:()=>le(!1),get actions(){return[(()=>{var e=Jt();return q(e,()=>L().updateRepo),e})(),(()=>{var e=kt();return e.$$click=()=>le(!1),q(e,()=>L().close),e})()]},get children(){return[(()=>{var e=Pt();return q(e,()=>L().updateAvailable(J()??``,g().version??``)),e})(),(()=>{var e=Ft();return q(e,()=>L().updatePrompt),e})(),(()=>{var e=Ot();return e.$$click=()=>void ye(),q(e,(()=>{var e=pe(()=>!!ue());return()=>e()?L().copied:L().copy})()),e})(),(()=>{var e=Pt();return q(e,()=>L().updateSteps),e})()]}}),null),b(e=>{var t=L().language,n=L().language,r=A()===`zh`,i=A()===`zh`,a=A()===`en`,o=A()===`en`;return t!==e.e&&G(E,`aria-label`,e.e=t),n!==e.t&&G(E,`title`,e.t=n),r!==e.a&&D.classList.toggle(`active`,e.a=r),i!==e.o&&G(D,`aria-pressed`,e.o=i),a!==e.i&&O.classList.toggle(`active`,e.i=a),o!==e.n&&G(O,`aria-pressed`,e.n=o),e},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0,n:void 0}),b(()=>h.checked=C()),t})()}W([`click`]);var Zt=document.getElementById(`root`);if(!Zt)throw Error(`缺少 #root 容器`);he(()=>z(Xt,{}),Zt);
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>paintress</title>
7
- <script type="module" crossorigin src="/assets/index-om6XE6pt.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-BEKlFXv9.css">
7
+ <script type="module" crossorigin src="/assets/index-kPX0SDa0.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-Dyrrkh92.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -1 +0,0 @@
1
- :root{--bg:#f6f1ea;--panel:#fffcf8;--border:#e6ded2;--text:#1f2a37;--muted:#7a7166;--accent:#205080;--accent-soft:#20508014;--gold:#908030;--gold-soft:#90803073;--radius:8px}*{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%}body{color:var(--text);background:var(--bg);-webkit-font-smoothing:antialiased;font:14px/1.55 system-ui,-apple-system,Segoe UI,Noto Sans SC,sans-serif}.app{grid-template-columns:280px 1fr;height:100%;display:grid}.sidebar{background:var(--panel);border-right:1px solid var(--border);flex-direction:column;min-height:0;display:flex}.sidebar-head{border-bottom:1px solid var(--border);align-items:center;gap:8px;padding:16px 16px 12px;display:flex}.sidebar-head h1{letter-spacing:-.01em;color:var(--accent);font-size:15px;font-weight:650}.badge{color:var(--muted);background:var(--bg);border-radius:999px;padding:1px 6px;font-size:11px}.section{min-height:0;padding:12px 8px 4px;overflow-y:auto}.section.files{border-top:1px solid var(--border);flex:1;padding-top:12px}.section h2{text-transform:uppercase;letter-spacing:.06em;color:var(--muted);justify-content:space-between;gap:8px;padding:0 8px 8px;font-size:11px;font-weight:600;display:flex}.hint{color:var(--muted);text-transform:none;letter-spacing:0;font-size:11px;font-weight:400}.list{list-style:none}.item{text-align:left;cursor:pointer;width:100%;font:inherit;color:inherit;background:0 0;border:0;border-radius:6px;flex-direction:column;gap:1px;padding:7px 8px;display:flex}.item:hover{background:var(--bg)}.item.active{background:var(--accent-soft);color:var(--accent)}.item-name{text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.item-meta{color:var(--muted);font-size:11px}.item.active .item-meta{color:var(--accent);opacity:.75}.empty{color:var(--muted);padding:4px 8px 12px;font-size:12px;line-height:1.7}.empty code{background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:1px 5px;font-size:11px}.sidebar-foot{border-top:1px solid var(--border);color:var(--muted);flex-wrap:wrap;align-items:center;gap:8px;padding:10px 16px;font-size:12px;display:flex}.toggle{cursor:pointer;align-items:center;gap:5px;display:inline-flex}.ghost{background:var(--bg);border:1px solid var(--border);font:inherit;color:var(--text);cursor:pointer;border-radius:6px;padding:3px 9px;font-size:12px}.ghost:hover{border-color:var(--accent);color:var(--accent)}.stage{background:var(--canvas-bg,var(--bg));flex-direction:column;min-width:0;min-height:0;display:flex}.toolbar{border-bottom:1px solid var(--border);background:var(--panel);justify-content:space-between;align-items:center;gap:12px;padding:8px 14px;display:flex}.toolbar code{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.preview{border:1px solid var(--border);background:var(--canvas-bg,#fff);border-radius:10px;flex:1;width:calc(100% - 24px);min-height:0;margin:12px;box-shadow:0 1px 2px #1f2a370d,0 8px 24px #1f2a370d}.stage[data-canvas-bg=dark] .preview{border-color:#ffffff1f;box-shadow:0 1px 2px #00000059,0 10px 30px #00000052}.placeholder{color:var(--muted);flex:1;place-items:center;display:grid}.error{border-radius:var(--radius);color:#b91c1c;white-space:pre-wrap;background:#fef2f2;border:1px solid #fecaca;margin:12px;padding:10px 12px;font-size:12px}.section.marks{border-top:1px solid var(--border);flex:0 auto;max-height:34vh}.mark-no{background:var(--accent-soft);min-width:20px;color:var(--accent);font-variant-numeric:tabular-nums;border-radius:4px;margin-right:4px;padding:0 4px;font-size:11px;display:inline-block}.item.active .mark-no{background:#fff}.ghost.tiny{text-transform:none;letter-spacing:0;padding:1px 6px;font-size:11px}.crumbs{flex-wrap:wrap;align-items:center;gap:3px;min-width:0;display:flex;overflow:hidden}.crumbs code{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.crumb{font:inherit;color:var(--muted);cursor:pointer;text-overflow:ellipsis;white-space:nowrap;background:0 0;border:0;border-radius:4px;max-width:220px;padding:2px 5px;font-size:12px;overflow:hidden}.crumb:hover{background:var(--bg);color:var(--text)}.crumb.active{color:var(--accent);background:var(--accent-soft)}.sep{color:var(--border);font-size:11px}.brand-mark{flex:none;width:auto;height:32px}.canvas-bg{-webkit-user-select:none;user-select:none;flex:none;align-items:center;gap:8px;display:inline-flex}.canvas-bg-label{letter-spacing:.04em;text-transform:uppercase;color:var(--muted);text-align:right;min-width:34px;font-size:11px;font-weight:500}.canvas-bg-track{width:84px;height:calc(var(--thumb) + var(--pad) * 2);cursor:pointer;touch-action:none;background:linear-gradient(#e7e0d5,#f3eee6 60%,#f7f2ea);border-radius:999px;position:relative;box-shadow:inset 0 1px 2px #1f2a373d,inset 0 -1px #ffffffe6,inset 0 0 0 1px #1f2a3712,0 1px #fff}.canvas-bg-track:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.canvas-bg-thumb{top:var(--pad);left:var(--pad);width:var(--thumb);height:var(--thumb);transform:translateX(calc(var(--pos) * var(--step)));background:linear-gradient(#fff,#f1ebe1);border-radius:50%;justify-content:center;align-items:center;transition:transform .17s cubic-bezier(.22,.61,.36,1);display:flex;position:absolute;box-shadow:0 1px 2px #1f2a374d,0 0 0 1px #1f2a3714,inset 0 1px #fff}.canvas-bg-track.dragging .canvas-bg-thumb{transition:none}.canvas-bg-dot{background:var(--dot,#fff);border-radius:50%;width:11px;height:11px;transition:background .17s;box-shadow:0 0 0 1px #1f2a3742,0 1px 1px #1f2a371f}.lang-switch{background:linear-gradient(#ece6dc,#f6f1e9);border-radius:999px;flex:none;align-items:center;gap:2px;margin-left:4px;padding:2px;display:inline-flex;box-shadow:inset 0 1px 2px #1f2a3729,inset 0 0 0 1px #1f2a370d}.lang-opt{font:inherit;color:var(--muted);cursor:pointer;background:0 0;border:0;border-radius:999px;padding:2px 7px;font-size:11px;line-height:16px}.lang-opt:hover{color:var(--text)}.lang-opt.active{color:var(--accent);background:linear-gradient(#fffdf9,#f3ede4);font-weight:600;box-shadow:0 1px 2px #1f2a372e,inset 0 1px #fff}.tool-sep{background:var(--border);flex:none;width:1px;height:18px;margin:0 2px}.tools{flex:none;align-items:center;gap:6px;display:flex}.zoom{font-variant-numeric:tabular-nums;text-align:right;min-width:40px}.boardbar{border-bottom:1px solid var(--border);background:var(--panel);align-items:center;gap:6px;padding:6px 14px;display:flex;overflow-x:auto}.board{background:var(--bg);border:1px solid var(--border);font:inherit;color:var(--text);cursor:pointer;border-radius:6px;flex:none;align-items:baseline;gap:6px;padding:3px 9px;font-size:12px;display:inline-flex}.board:hover{border-color:var(--accent);color:var(--accent)}.board.active{background:var(--accent-soft);border-color:var(--accent);color:var(--accent)}.board-size{color:var(--muted);font-variant-numeric:tabular-nums;font-size:10px}.board.active .board-size{color:var(--accent);opacity:.75}
@@ -1,7 +0,0 @@
1
- (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e={context:void 0,registry:void 0,effects:void 0,done:!1,getContextId(){return t(this.context.count)},getNextContextId(){return t(this.context.count++)}};function t(t){let n=String(t),r=n.length-1;return e.context.id+(r?String.fromCharCode(96+r):``)+n}function n(t){e.context=t}var r=(e,t)=>e===t,i=Symbol(`solid-track`),a={equals:r},o=null,s=oe,c=1,l=2,u={owned:null,cleanups:null,context:null,owner:null},d=null,f=null,p=null,m=null,h=null,g=0;function _(e,t){let n=p,r=d,i=e.length===0,a=t===void 0?r:t,o=i?u:{owned:null,cleanups:null,context:a?a.context:null,owner:a},s=i?e:()=>e(v(o));d=o,p=null;try{return j(s,!0)}finally{p=n,d=r}}function v(e){return()=>C(()=>I(e))}function y(e,t){t=t?Object.assign({},a,t):a;let n={value:e,observers:null,observerSlots:null,comparator:t.equals||void 0};return[ne.bind(n),e=>(typeof e==`function`&&(e=f&&f.running&&f.sources.has(n)?e(n.tValue):e(n.value)),re(n,e))]}function b(e,t,n){k(ae(e,t,!1,c))}function x(e,t,n){s=N;let r=ae(e,t,!1,c),i=O&&D(O);i&&(r.suspense=i),n&&n.render||(r.user=!0),h?h.push(r):k(r)}function S(e,t,n){n=n?Object.assign({},a,n):a;let r=ae(e,t,!0,0);return r.observers=null,r.observerSlots=null,r.comparator=n.equals||void 0,k(r),ne.bind(r)}function C(e){if(p===null)return e();let t=p;p=null;try{return e()}finally{p=t}}function w(e,t,n){let r=Array.isArray(e),i,a=n&&n.defer;return n=>{let o;if(r){o=Array(e.length);for(let t=0;t<e.length;t++)o[t]=e[t]()}else o=e();if(a)return a=!1,n;let s=C(()=>t(o,i,n));return i=o,s}}function T(e){x(()=>C(e))}function E(e){return d===null||(d.cleanups===null?d.cleanups=[e]:d.cleanups.push(e)),e}var[ee,te]=y(!1);function D(e){let t;return d&&d.context&&(t=d.context[e.id])!==void 0?t:e.defaultValue}var O;function ne(){let e=f&&f.running;if(this.sources&&(e?this.tState:this.state)){if((e?this.tState:this.state)===c)k(this);else{let e=m;m=null,j(()=>P(this),!1),m=e}}if(p){let e=this.observers;if(!e||e[e.length-1]!==p){let t=e?e.length:0;p.sources?(p.sources.push(this),p.sourceSlots.push(t)):(p.sources=[this],p.sourceSlots=[t]),e?(e.push(p),this.observerSlots.push(p.sources.length-1)):(this.observers=[p],this.observerSlots=[p.sources.length-1])}}return e&&f.sources.has(this)?this.tValue:this.value}function re(e,t,n){let r=f&&f.running&&f.sources.has(e)?e.tValue:e.value;if(!e.comparator||!e.comparator(r,t)){if(f){let r=f.running;(r||!n&&f.sources.has(e))&&(f.sources.add(e),e.tValue=t),r||(e.value=t)}else e.value=t;e.observers&&e.observers.length&&j(()=>{for(let t=0;t<e.observers.length;t+=1){let n=e.observers[t],r=f&&f.running;r&&f.disposed.has(n)||((r?!n.tState:!n.state)&&(n.pure?m.push(n):h.push(n),n.observers&&F(n)),r?n.tState=c:n.state=c)}if(m.length>1e6)throw m=[],Error()},!1)}return t}function k(e){if(!e.fn)return;I(e);let t=g;ie(e,f&&f.running&&f.sources.has(e)?e.tValue:e.value,t),f&&!f.running&&f.sources.has(e)&&queueMicrotask(()=>{j(()=>{f&&(f.running=!0),p=d=e,ie(e,e.tValue,t),p=d=null},!1)})}function ie(e,t,n){let r,i=d,a=p;p=d=e;try{r=e.fn(t)}catch(t){return e.pure&&(f&&f.running?(e.tState=c,e.tOwned&&e.tOwned.forEach(I),e.tOwned=void 0):(e.state=c,e.owned&&e.owned.forEach(I),e.owned=null)),e.updatedAt=n+1,ue(t)}finally{p=a,d=i}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&`observers`in e?re(e,r,!0):f&&f.running&&e.pure?(f.sources.has(e)||(e.value=r),f.sources.add(e),e.tValue=r):e.value=r,e.updatedAt=n)}function ae(e,t,n,r=c,i){let a={fn:e,state:r,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:t,owner:d,context:d?d.context:null,pure:n};return f&&f.running&&(a.state=0,a.tState=r),d===null||d!==u&&(f&&f.running&&d.pure?d.tOwned?d.tOwned.push(a):d.tOwned=[a]:d.owned?d.owned.push(a):d.owned=[a]),a}function A(e){let t=f&&f.running;if((t?e.tState:e.state)===0)return;if((t?e.tState:e.state)===l)return P(e);if(e.suspense&&C(e.suspense.inFallback))return e.suspense.effects.push(e);let n=[e];for(;(e=e.owner)&&(!e.updatedAt||e.updatedAt<g);){if(t&&f.disposed.has(e))return;(t?e.tState:e.state)&&n.push(e)}for(let r=n.length-1;r>=0;r--){if(e=n[r],t){let t=e,i=n[r+1];for(;(t=t.owner)&&t!==i;)if(f.disposed.has(t))return}if((t?e.tState:e.state)===c)k(e);else if((t?e.tState:e.state)===l){let t=m;m=null,j(()=>P(e,n[0]),!1),m=t}}}function j(e,t){if(m)return e();let n=!1;t||(m=[]),h?n=!0:h=[],g++;try{let t=e();return M(n),t}catch(e){n||(h=null),m=null,ue(e)}}function M(e){if(m&&=(oe(m),null),e)return;let t;if(f){if(!f.promises.size&&!f.queue.size){let e=f.sources,n=f.disposed;h.push.apply(h,f.effects),t=f.resolve;for(let e of h)`tState`in e&&(e.state=e.tState),delete e.tState;f=null,j(()=>{for(let e of n)I(e);for(let t of e){if(t.value=t.tValue,t.owned)for(let e=0,n=t.owned.length;e<n;e++)I(t.owned[e]);t.tOwned&&(t.owned=t.tOwned),delete t.tValue,delete t.tOwned,t.tState=0}te(!1)},!1)}else if(f.running){f.running=!1,f.effects.push.apply(f.effects,h),h=null,te(!0);return}}let n=h;h=null,n.length&&j(()=>s(n),!1),t&&t()}function oe(e){for(let t=0;t<e.length;t++)A(e[t])}function N(t){let r,i=0;for(r=0;r<t.length;r++){let e=t[r];e.user?t[i++]=e:A(e)}if(e.context){if(e.count){e.effects||=[],e.effects.push(...t.slice(0,i));return}n()}for(e.effects&&(e.done||!e.count)&&(t=[...e.effects,...t],i+=e.effects.length,delete e.effects),r=0;r<i;r++)A(t[r])}function P(e,t){let n=f&&f.running;n?e.tState=0:e.state=0;for(let r=0;r<e.sources.length;r+=1){let i=e.sources[r];if(i.sources){let e=n?i.tState:i.state;e===c?i!==t&&(!i.updatedAt||i.updatedAt<g)&&A(i):e===l&&P(i,t)}}}function F(e){let t=f&&f.running;for(let n=0;n<e.observers.length;n+=1){let r=e.observers[n];(t?!r.tState:!r.state)&&(t?r.tState=l:r.state=l,r.pure?m.push(r):h.push(r),r.observers&&F(r))}}function I(e){let t;if(e.sources)for(;e.sources.length;){let t=e.sources.pop(),n=e.sourceSlots.pop(),r=t.observers;if(r&&r.length){let e=r.pop(),i=t.observerSlots.pop();n<r.length&&(e.sourceSlots[i]=n,r[n]=e,t.observerSlots[n]=i)}}if(e.tOwned){for(t=e.tOwned.length-1;t>=0;t--)I(e.tOwned[t]);delete e.tOwned}if(f&&f.running&&e.pure)se(e,!0);else if(e.owned){for(t=e.owned.length-1;t>=0;t--)I(e.owned[t]);e.owned=null}if(e.cleanups){for(t=e.cleanups.length-1;t>=0;t--)e.cleanups[t]();e.cleanups=null}f&&f.running?e.tState=0:e.state=0}function se(e,t){if(t||(e.tState=0,f.disposed.add(e)),e.owned)for(let t=0;t<e.owned.length;t++)se(e.owned[t])}function ce(e){return e instanceof Error?e:Error(typeof e==`string`?e:`Unknown error`,{cause:e})}function le(e,t,n){try{for(let n of t)n(e)}catch(e){ue(e,n&&n.owner||null)}}function ue(e,t=d){let n=o&&t&&t.context&&t.context[o],r=ce(e);if(!n)throw r;h?h.push({fn(){le(r,n,t)},state:c}):le(r,n,t)}var de=Symbol(`fallback`);function fe(e){for(let t=0;t<e.length;t++)e[t]()}function pe(e,t,n={}){let r=[],a=[],o=[],s=0,c=t.length>1?[]:null;return E(()=>fe(o)),()=>{let l=e()||[],u=l.length,d,f;return l[i],C(()=>{let e,t,i,m,h,g,v,y,b;if(u===0)s!==0&&(fe(o),o=[],r=[],a=[],s=0,c&&=[]),n.fallback&&(r=[de],a[0]=_(e=>(o[0]=e,n.fallback())),s=1);else if(s===0){for(a=Array(u),f=0;f<u;f++)r[f]=l[f],a[f]=_(p);s=u}else{for(i=Array(u),m=Array(u),c&&(h=Array(u)),g=0,v=Math.min(s,u);g<v&&r[g]===l[g];g++);for(v=s-1,y=u-1;v>=g&&y>=g&&r[v]===l[y];v--,y--)i[y]=a[v],m[y]=o[v],c&&(h[y]=c[v]);for(e=new Map,t=Array(y+1),f=y;f>=g;f--)b=l[f],d=e.get(b),t[f]=d===void 0?-1:d,e.set(b,f);for(d=g;d<=v;d++)b=r[d],f=e.get(b),f!==void 0&&f!==-1?(i[f]=a[d],m[f]=o[d],c&&(h[f]=c[d]),f=t[f],e.set(b,f)):o[d]();for(f=g;f<u;f++)f in i?(a[f]=i[f],o[f]=m[f],c&&(c[f]=h[f],c[f](f))):a[f]=_(p);a=a.slice(0,s=u),r=l.slice(0)}return a});function p(e){if(o[f]=e,c){let[e,n]=y(f);return c[f]=n,t(l[f],e)}return t(l[f])}}}function L(e,t){return C(()=>e(t||{}))}var R=e=>`Stale read from <${e}>.`;function z(e){let t=`fallback`in e&&{fallback:()=>e.fallback};return S(pe(()=>e.each,e.children,t||void 0))}function B(e){let t=e.keyed,n=S(()=>e.when,void 0,void 0),r=t?n:S(n,void 0,{equals:(e,t)=>!e==!t});return S(()=>{let i=r();if(i){let a=e.children;return typeof a==`function`&&a.length>0?C(()=>a(t?i:()=>{if(!C(r))throw R(`Show`);return n()})):a}return e.fallback},void 0,void 0)}var me=e=>S(()=>e());function he(e,t,n){let r=n.length,i=t.length,a=r,o=0,s=0,c=t[i-1].nextSibling,l=null;for(;o<i||s<a;)if(t[o]===n[s])o++,s++;else{for(;t[i-1]===n[a-1];)i--,a--;if(i===o){let t=a<r?s?n[s-1].nextSibling:n[a-s]:c;for(;s<a;)e.insertBefore(n[s++],t)}else if(a===s)for(;o<i;)l&&l.has(t[o])||t[o].remove(),o++;else if(t[o]===n[a-1]&&n[s]===t[i-1]){let r=t[--i].nextSibling;e.insertBefore(n[s++],t[o++].nextSibling),e.insertBefore(n[--a],r),t[i]=n[a]}else{if(!l){l=new Map;let e=s;for(;e<a;)l.set(n[e],e++)}let r=l.get(t[o]);if(r!=null){if(s<r&&r<a){let c=o,u=1,d;for(;++c<i&&c<a&&(d=l.get(t[c]))!=null&&d===r+u;)u++;if(u>r-s){let i=t[o];for(;s<r;)e.insertBefore(n[s++],i)}else e.replaceChild(n[s++],t[o++])}else o++}else t[o++].remove()}}}var V=`_$DX_DELEGATE`;function ge(e,t,n,r={}){let i;return _(r=>{i=r,t===document?e():W(t,e(),!n&&t.firstChild?null:void 0,n)},r.owner),()=>{i(),t.textContent=``}}function H(e,t,n,r){let i,a=()=>{let t=r?document.createElementNS(`http://www.w3.org/1998/Math/MathML`,`template`):document.createElement(`template`);return t.innerHTML=e,n?t.content.firstChild.firstChild:r?t.firstChild:t.content.firstChild},o=t?()=>C(()=>document.importNode(i||=a(),!0)):()=>(i||=a()).cloneNode(!0);return o.cloneNode=o,o}function _e(e,t=window.document){let n=t[V]||(t[V]=new Set);for(let r=0,i=e.length;r<i;r++){let i=e[r];n.has(i)||(n.add(i),t.addEventListener(i,xe))}}function U(e,t,n){be(e)||(n==null?e.removeAttribute(t):e.setAttribute(t,n))}function ve(e,t,n){n==null?e.style.removeProperty(t):e.style.setProperty(t,n)}function ye(e,t,n){return C(()=>e(t,n))}function W(e,t,n,r){if(n!==void 0&&!r&&(r=[]),typeof t!=`function`)return G(e,t,r,n);b(r=>G(e,t(),r,n),r)}function be(t){return!!e.context&&!e.done&&(!t||t.isConnected)}function xe(t){if(e.registry&&e.events&&e.events.find(([e,n])=>n===t))return;let n=t.target,r=`$$${t.type}`,i=t.target,a=t.currentTarget,o=e=>Object.defineProperty(t,"target",{configurable:!0,value:e}),s=()=>{let e=n[r];if(e&&!n.disabled){let i=n[`${r}Data`];if(i===void 0?e.call(n,t):e.call(n,i,t),t.cancelBubble)return}return n.host&&typeof n.host!=`string`&&!n.host._$host&&n.contains(t.target)&&o(n.host),!0},c=()=>{for(;s()&&(n=n._$host||n.parentNode||n.host););};Object.defineProperty(t,"currentTarget",{configurable:!0,get(){return n||document}}),e.registry&&!e.done&&(e.done=_$HY.done=!0);try{if(t.composedPath){let e=t.composedPath();o(e[0]);for(let t=0;t<e.length-2&&(n=e[t],s());t++){if(n._$host){n=n._$host,c();break}if(n.parentNode===a)break}}else c();o(i)}finally{t=n=null}}function G(e,t,n,r,i){let a=be(e);if(a){!n&&(n=[...e.childNodes]);let t=[];for(let e=0;e<n.length;e++){let r=n[e];r.nodeType===8&&r.data.slice(0,2)===`!$`?r.remove():t.push(r)}n=t}for(;typeof n==`function`;)n=n();if(t===n)return n;let o=typeof t,s=r!==void 0;if(e=s&&n[0]&&n[0].parentNode||e,o===`string`||o===`number`){if(a||o===`number`&&(t=t.toString(),t===n))return n;if(s){let i=n[0];i&&i.nodeType===3?i.data!==t&&(i.data=t):i=document.createTextNode(t),n=q(e,n,r,i)}else n=n!==``&&typeof n==`string`?e.firstChild.data=t:e.textContent=t}else if(t==null||o===`boolean`){if(a)return n;n=q(e,n,r)}else if(o===`function`)return b(()=>{let i=t();for(;typeof i==`function`;)i=i();n=G(e,i,n,r)}),()=>n;else if(Array.isArray(t)){let o=[],c=n&&Array.isArray(n);if(K(o,t,n,i))return b(()=>n=G(e,o,n,r,!0)),()=>n;if(a){if(!o.length)return n;if(r===void 0)return n=[...e.childNodes];let t=o[0];if(t.parentNode!==e)return n;let i=[t];for(;(t=t.nextSibling)!==r;)i.push(t);return n=i}if(o.length===0){if(n=q(e,n,r),s)return n}else c?n.length===0?Se(e,o,r):he(e,n,o):(n&&q(e),Se(e,o));n=o}else if(t.nodeType){if(a&&t.parentNode)return n=s?[t]:t;if(Array.isArray(n)){if(s)return n=q(e,n,r,t);q(e,n,null,t)}else n==null||n===``||!e.firstChild?e.appendChild(t):e.replaceChild(t,e.firstChild);n=t}return n}function K(e,t,n,r){let i=!1;for(let a=0,o=t.length;a<o;a++){let o=t[a],s=n&&n[e.length],c;if(o!=null&&o!==!0&&o!==!1){if((c=typeof o)==`object`&&o.nodeType)e.push(o);else if(Array.isArray(o))i=K(e,o,s)||i;else if(c===`function`){if(r){for(;typeof o==`function`;)o=o();i=K(e,Array.isArray(o)?o:[o],Array.isArray(s)?s:[s])||i}else e.push(o),i=!0}else{let t=String(o);s&&s.nodeType===3&&s.data===t?e.push(s):e.push(document.createTextNode(t))}}}return i}function Se(e,t,n=null){for(let r=0,i=t.length;r<i;r++)e.insertBefore(t[r],n)}function q(e,t,n,r){if(n===void 0)return e.textContent=``;let i=r||document.createTextNode(``);if(t.length){let r=!1;for(let a=t.length-1;a>=0;a--){let o=t[a];if(i!==o){let t=o.parentNode===e;!r&&!a?t?e.replaceChild(i,o):e.insertBefore(i,n):t&&o.remove()}else r=!0}}else e.insertBefore(i,n);return[i]}async function J(e){let t=await fetch(e,{headers:{accept:`application/json`}});if(!t.ok){let e=await t.text().catch(()=>``);throw Error(`HTTP ${t.status}${e?`: ${e.slice(0,200)}`:``}`)}return await t.json()}function Ce(){return J(`/api/projects`)}function we(e){return J(`/api/projects/${encodeURIComponent(e)}/files`)}function Te(e,t){return`/render/${encodeURIComponent(e)}/${t.split(`/`).map(encodeURIComponent).join(`/`)}?embed=1`}function Ee(e,t){return`/events?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`}function De(){return`/events`}function Oe(e){return J(`/api/marks?project=${encodeURIComponent(e)}`)}async function ke(e){let t=await fetch(`/api/marks`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e)});if(!t.ok)throw Error(`HTTP ${t.status}`);let n=await t.json();if(!n.ok)throw Error(n.error??`添加标记失败`);return n.mark}async function Ae(e){let t=await fetch(`/api/marks?project=${encodeURIComponent(e)}`,{method:`DELETE`});if(!t.ok)throw Error(`HTTP ${t.status}`);return(await t.json()).cleared}function je(e,t){return J(`/api/changes?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`)}var Y=.1,X=.02;function Me(e,t){return t===0||Math.abs(e)>=Math.abs(t)?null:t>0?-1:1}function Z(e,t=Y){return Math.min(4,Math.max(t,e))}function Ne(e,t,n,r,i=Y){let a=Z(e.z*t,i);if(a===e.z)return e;let o=a/e.z;return{z:a,tx:n-(n-e.tx)*o,ty:r-(r-e.ty)*o}}function Pe(e,t,n,r=Y){return Ne(e,Z(t,r)/e.z,n.width/2,n.height/2,r)}function Q(e,t,n=24){let r=Math.max(1,e.w),i=Math.max(1,e.h),a=Math.max(1,t.width-n*2),o=Math.max(1,t.height-n*2),s=Z(Math.min(a/r,o/i),X);return{z:s,tx:(t.width-r*s)/2-e.x*s,ty:(t.height-i*s)/2-e.y*s}}function Fe(e,t,n){return{z:e.z,tx:e.tx+t,ty:e.ty+n}}function Ie(e){return`translate(${e.tx}px, ${e.ty}px) scale(${e.z})`}function Le(e){if(e===null||e===``)return null;let t;try{t=JSON.parse(e)}catch{return null}if(typeof t!=`object`||!t)return null;let{z:n,tx:r,ty:i}=t;return typeof n!=`number`||typeof r!=`number`||typeof i!=`number`||!Number.isFinite(n)||!Number.isFinite(r)||!Number.isFinite(i)||n<=0?null:{z:Z(n,X),tx:r,ty:i}}var Re=3,ze=.0015;function Be({doc:e,onChange:t,onLevelStep:n}){let r=e.querySelector(`.ptr-canvas`);if(r===null)return null;let i=r;i.style.transformOrigin=`0 0`;let a={z:1,tx:0,ty:0},o=!1,s=!1,c=!1,l=null,u=()=>({width:e.documentElement.clientWidth||1,height:e.documentElement.clientHeight||1});function d(){i.style.transform=Ie(a),t?.(a)}function f(e){a=e,d()}function p(){i.style.transition=``}function m(e){i.style.transition=`transform 180ms ease-out`,f(e),setTimeout(p,220)}function h(t){e.documentElement.style.cursor=t}function g(e){if(e.preventDefault(),e.altKey&&n!==void 0){let t=Me(e.deltaX,e.deltaY);t!==null&&n(t,{x:e.clientX,y:e.clientY});return}p();let t=1;e.deltaMode===1?t=16:e.deltaMode===2&&(t=u().height);let r=a.z<.1?X:Y;f(Ne(a,Math.exp(-e.deltaY*t*ze),e.clientX,e.clientY,r))}function _(t){return t.button===1?!0:t.button===0?o?!0:t.target===i||t.target===e.body||t.target===e.documentElement:!1}function v(e){_(e)&&(e.preventDefault(),l={lastX:e.clientX,lastY:e.clientY,startX:e.clientX,startY:e.clientY},s=!1)}function y(t){if(!l)return;if(!s){if(Math.hypot(t.clientX-l.startX,t.clientY-l.startY)<Re)return;s=!0,p(),e.body.style.userSelect=`none`,h(`grabbing`)}let n=t.clientX-l.lastX,r=t.clientY-l.lastY;l.lastX=t.clientX,l.lastY=t.clientY,f(Fe(a,n,r))}function b(){l&&(l=null,s&&(s=!1,c=!0,e.body.style.userSelect=``,h(o?`grab`:``)))}function x(e){o!==e&&(o=e,s||h(e?`grab`:``))}function S(e){if(e.key===` `&&!e.metaKey&&!e.ctrlKey&&!e.altKey){let t=e.target,n=t?.tagName??``;if(n===`INPUT`||n===`TEXTAREA`||t?.isContentEditable===!0)return;x(!0),e.preventDefault()}else T(e)&&e.preventDefault()}function C(e){e.key===` `&&x(!1)}function w(){x(!1),b()}function T(e){return(e.ctrlKey||e.metaKey)&&(e.key===`0`||e.key===`)`||e.code===`Digit0`||e.code===`Numpad0`)?(f(Pe(a,1,u())),!0):e.shiftKey&&!e.ctrlKey&&!e.metaKey&&(e.key===`!`||e.code===`Digit1`)?(E(),!0):!1}function E(){f(Q({x:0,y:0,w:i.offsetWidth,h:i.offsetHeight},u()))}function ee(e){let t=u();m({z:a.z,tx:t.width/2-(e.x+e.w/2)*a.z,ty:t.height/2-(e.y+e.h/2)*a.z})}return e.addEventListener(`wheel`,g,{passive:!1}),e.addEventListener(`mousedown`,v,!0),e.addEventListener(`mousemove`,y,!0),e.addEventListener(`mouseup`,b,!0),e.addEventListener(`keydown`,S),e.addEventListener(`keyup`,C),e.defaultView?.addEventListener(`blur`,w),{get:()=>a,set:f,fitAll:E,centerOn:ee,focusBox:e=>m(Q(e,u())),zoomToPercent:e=>f(Pe(a,e,u())),consumeDragClick:()=>{let e=c;return c=!1,e},handleKey:T,setSpace:x,destroy:()=>{e.removeEventListener(`wheel`,g),e.removeEventListener(`mousedown`,v,!0),e.removeEventListener(`mousemove`,y,!0),e.removeEventListener(`mouseup`,b,!0),e.removeEventListener(`keydown`,S),e.removeEventListener(`keyup`,C),e.defaultView?.removeEventListener(`blur`,w)}}}function Ve(e){let{clientX:t,rectLeft:n,rectWidth:r,pad:i,thumb:a,count:o}=e,s=r-i*2-a;if(s<=0||o<2)return null;let c=(t-n-i-a/2)/s;return Math.max(0,Math.min(o-1,c*(o-1)))}var He=H(`<div class=canvas-bg><span class=canvas-bg-label></span><div class=canvas-bg-track role=slider tabindex=0 aria-valuemin=0 style=--pad:3px;--thumb:20px><div class=canvas-bg-thumb aria-hidden=true><span class=canvas-bg-dot>`),$=[`light`,`gray`,`dark`],Ue={light:`Light`,gray:`Gray`,dark:`Dark`},We={light:`#ffffff`,gray:`#d2cec7`,dark:`#242b36`},Ge=`paintress:canvas-bg`;function Ke(e){return $.includes(e)}function qe(){try{let e=localStorage.getItem(Ge);if(Ke(e))return e}catch{}return`light`}function Je(e){try{localStorage.setItem(Ge,e)}catch{}}var Ye=3,Xe=20;function Ze(e){let[t,n]=y(!1),[r,i]=y(null),[a,o]=y(29),s,c=()=>$.indexOf(e.value),l=()=>r()??c();T(()=>{let e=()=>{let e=s;if(!e)return;let t=e.getBoundingClientRect().width-6-Xe;o(t/($.length-1))};if(e(),typeof ResizeObserver>`u`||!s)return;let t=new ResizeObserver(e);t.observe(s),E(()=>t.disconnect())});function u(e){let t=s;if(!t)return c();let n=t.getBoundingClientRect();return Ve({clientX:e.clientX,rectLeft:n.left,rectWidth:n.width,pad:Ye,thumb:Xe,count:$.length})??c()}function d(t){let n=$[Math.round(t)];n!==void 0&&n!==e.value&&e.onChange(n)}function f(e){e.button===0&&(e.preventDefault(),e.currentTarget.setPointerCapture(e.pointerId),n(!0),i(u(e)))}function p(e){t()&&i(u(e))}function m(e){if(!t())return;let r=e.currentTarget;n(!1),i(null),d(u(e)),r.hasPointerCapture(e.pointerId)&&r.releasePointerCapture(e.pointerId)}function h(){n(!1),i(null)}function g(t){let n=t.key===`ArrowRight`||t.key===`ArrowUp`?1:t.key===`ArrowLeft`||t.key===`ArrowDown`?-1:0;if(n===0)return;t.preventDefault();let r=$[Math.max(0,Math.min($.length-1,c()+n))];r!==void 0&&e.onChange(r)}return(()=>{var n=He(),r=n.firstChild,i=r.nextSibling,o=i.firstChild.firstChild;W(r,()=>Ue[e.value]),i.$$keydown=g,i.addEventListener(`lostpointercapture`,h),i.addEventListener(`pointercancel`,h),i.$$pointerup=m,i.$$pointermove=p,i.$$pointerdown=f;var u=s;return typeof u==`function`?ye(u,i):s=i,b(n=>{var r=!!t(),s=e.label,u=$.length-1,d=c(),f=Ue[e.value],p=e.label,m=`${a()}px`,h=String(l()),g=We[e.value];return r!==n.e&&i.classList.toggle(`dragging`,n.e=r),s!==n.t&&U(i,`aria-label`,n.t=s),u!==n.a&&U(i,`aria-valuemax`,n.a=u),d!==n.o&&U(i,`aria-valuenow`,n.o=d),f!==n.i&&U(i,`aria-valuetext`,n.i=f),p!==n.n&&U(i,`title`,n.n=p),m!==n.s&&ve(i,`--step`,n.s=m),h!==n.h&&ve(i,`--pos`,n.h=h),g!==n.r&&ve(o,`--dot`,n.r=g),n},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0,n:void 0,s:void 0,h:void 0,r:void 0}),n})()}_e([`pointerdown`,`pointermove`,`pointerup`,`keydown`]);var Qe={zh:{projects:`项目`,noProjects:`还没有注册项目`,projectFiles:e=>`${e} 个设计稿`,designs:`设计稿`,depth:`depth`,noFiles:`没有 .ptr / .pen 文件`,marks:`标记`,clear:`清空`,liveReload:`实时刷新`,refreshList:`刷新列表`,pickProject:`从左侧选择项目`,pickFile:`从左侧选择一个设计稿`,fit:`适应`,fitTitle:`适应窗口(Shift+1)`,actualSize:`1:1`,actualSizeTitle:`100%(Ctrl/Cmd+0)`,rerender:`重新渲染`,allBoards:`全部`,canvasBackground:`画布背景`,language:`界面语言`},en:{projects:`Projects`,noProjects:`No projects registered yet`,projectFiles:e=>`${e} design${e===1?``:`s`}`,designs:`Designs`,depth:`depth`,noFiles:`No .ptr / .pen files`,marks:`Marks`,clear:`Clear`,liveReload:`Live reload`,refreshList:`Refresh`,pickProject:`Pick a project on the left`,pickFile:`Pick a design on the left`,fit:`Fit`,fitTitle:`Fit to window (Shift+1)`,actualSize:`1:1`,actualSizeTitle:`100% (Ctrl/Cmd+0)`,rerender:`Re-render`,allBoards:`All`,canvasBackground:`Canvas background`,language:`Language`}};function $e(e){return Qe[e]}function et(e){return(e?.languages?.[0]??e?.language??``).toLowerCase().startsWith(`zh`)?`zh`:`en`}var tt=`paintress:lang`;function nt(e){return e===`zh`||e===`en`}function rt(){try{let e=localStorage.getItem(tt);if(nt(e))return e}catch{}return et(typeof navigator>`u`?void 0:navigator)}function it(e){try{localStorage.setItem(tt,e)}catch{}}var at=H(`<span class=badge>v`),ot=H(`<ul class=list>`),st=H(`<section class="section files"><h2> <span class=hint> `),ct=H(`<section class="section marks"><h2><button class="ghost tiny"></button></h2><ul class=list>`),lt=H(`<span class=hint>pid `),ut=H(`<div class=error>`),dt=H(`<code>/`),ft=H(`<span class="hint zoom">%`),pt=H(`<button class="ghost tiny">`),mt=H(`<button class=ghost>`),ht=H(`<span class=tool-sep>`),gt=H(`<div class=boardbar><button class=board>`),_t=H(`<iframe class=preview title="design preview"sandbox="allow-same-origin allow-scripts">`),vt=H(`<div class=app><aside class=sidebar><header class=sidebar-head><img class=brand-mark src=/logo.svg alt><h1>paintress</h1></header><section class=section><h2></h2></section><footer class=sidebar-foot><label class=toggle><input type=checkbox></label><button class=ghost></button></footer></aside><main class=stage><div class=toolbar><div class=crumbs></div><div class=tools><div class=lang-switch role=group><button class=lang-opt>中</button><button class=lang-opt>EN`),yt=H(`<p class=empty><br><code>paintress watch &lt;name&gt; &lt;dir&gt;`),bt=H(`<li><button class=item><span class=item-name></span><span class=item-meta>`),xt=H(`<p class=empty>`),St=H(`<li><button class=item><span class=item-name>`),Ct=H(`<li><button class=item><span class=item-name><b class=mark-no>#</b> </span><span class=item-meta>`),wt=H(`<span class=sep>/`),Tt=H(`<button class=crumb>`),Et=H(`<p>`),Dt=H(`<div class=placeholder>`),Ot=H(`<button class=board><span class=board-size>×`);function kt(e){return typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(e):e.replace(/["\\]/g,`\\$&`)}function At(){let[e,t]=y([]),[n,r]=y(null),[i,a]=y([]),[o,s]=y(null),[c,l]=y([]),[u,d]=y(null),[f,p]=y([]),[m,h]=y(null),[g,_]=y({}),[v,S]=y(0),[C,ee]=y(!0),[te,D]=y(``),[O,ne]=y([]),[re,k]=y(null),[ie,ae]=y(1),[A,j]=y(rt()),[M,oe]=y(qe()),N=()=>$e(A()),P,F,I=null,se;function ce(){let e=new URLSearchParams,t=n(),r=o();t&&e.set(`project`,t),r&&e.set(`file`,r);let i=e.toString();history.replaceState(null,``,i?`?${i}`:location.pathname)}async function le(){try{let e=await Ce();t(e.projects),_({pid:e.pid,version:e.version}),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}async function ue(e){try{l((await Oe(e)).marks)}catch{l([])}}async function de(e){r(e),s(null),d(null),p([]);try{let t=await we(e);a(t.files),h(null)}catch(e){a([]),h(e instanceof Error?e.message:String(e))}ue(e),ce()}async function fe(){await le();let t=n();if(t!==null){if(!e().some(e=>e.name===t))r(null),s(null),a([]),d(null),p([]),ce();else{try{let n=await we(t),r=i();a(n.files);let c=o();if(c===null){let i=n.files.filter(e=>!r.includes(e));i.length>0&&e().some(e=>e.name===t)&&pe(i[0])}else n.files.includes(c)||(s(null),d(null),p([]),ce())}catch{}ue(t)}}}function pe(e){s(e),d(null),p([]),ce()}T(()=>{let e=new EventSource(De());e.addEventListener(`projects`,()=>void fe()),E(()=>e.close());let t=e=>{let t=e.target;return t?.tagName===`INPUT`||t?.tagName===`TEXTAREA`||t?.isContentEditable===!0},n=e=>{t(e)||(e.key===` `&&!e.ctrlKey&&!e.metaKey&&!e.altKey?(I?.setSpace(!0),e.preventDefault()):I?.handleKey(e)===!0&&e.preventDefault())},r=e=>{e.key===` `&&I?.setSpace(!1)};window.addEventListener(`keydown`,n),window.addEventListener(`keyup`,r),E(()=>{window.removeEventListener(`keydown`,n),window.removeEventListener(`keyup`,r)});let i=new URLSearchParams(location.search);(async()=>{await le();let e=i.get(`project`);if(e){await de(e);let t=i.get(`file`);t&&pe(t)}})()});let R=()=>P?.contentDocument??null;function he(){let e=R();if(!e||e.getElementById(`ptr-style`))return;let t=e.createElement(`style`);t.id=`ptr-style`,t.textContent=`[data-ptr-selected]{outline:2px solid #908030 !important;outline-offset:-2px;}
2
- [data-ptr-hover]:not([data-ptr-selected]){outline:1px dashed rgba(144,128,48,.55) !important;outline-offset:-2px;}
3
- [data-ptr-changed]{box-shadow:inset 0 0 0 2px #908030 !important;animation:ptr-pulse 1.4s ease-in-out 3;}
4
- @keyframes ptr-pulse{0%,100%{box-shadow:inset 0 0 0 2px #908030}50%{box-shadow:inset 0 0 0 2px rgba(144,128,48,.35)}}
5
- html{cursor:default;}
6
- html,body{overflow:hidden;}
7
- html,body{background:var(--ptr-canvas-bg,#ffffff);}`,e.head.appendChild(t)}function V(){let e=R();if(!e)return;for(let t of e.querySelectorAll(`[data-ptr-selected]`))t.removeAttribute(`data-ptr-selected`);let t=u();t&&e.querySelector(`[data-node-id="${kt(t)}"]`)?.setAttribute(`data-ptr-selected`,``)}function ge(e){return e.parentElement?.closest(`[data-node-id]`)??null}function H(e){return e.querySelector(`:scope > [data-node-id]`)}function _e(e){if(I?.consumeDragClick()===!0){e.preventDefault(),e.stopPropagation();return}let t=e.target?.closest(`[data-node-id]`);t&&(e.preventDefault(),e.stopPropagation(),Me(e.altKey?ge(t)??t:t))}function ve(e,t){let n=R();if(!n)return;let r=u(),i=r===null?null:n.querySelector(`[data-node-id="${kt(r)}"]`);if(i===null){let e=(n.elementFromPoint(t.x,t.y)?.closest(`[data-node-id]`))?.dataset.nodeId;e!==void 0&&Z(e);return}let a=(e===1?ge(i):H(i))?.dataset.nodeId;a!==void 0&&Z(a)}function be(e){let t=R();if(t){for(let e of t.querySelectorAll(`[data-ptr-hover]`))e.removeAttribute(`data-ptr-hover`);(e.target?.closest(`[data-node-id]`))?.setAttribute(`data-ptr-hover`,``)}}async function xe(){let e=n(),t=o();if(e&&t)try{let n=await je(e,t);if(!n.ok||n.ids.length===0)return;let r=R();if(!r)return;for(let e of n.ids)r.querySelector(`[data-node-id="${kt(e)}"]`)?.setAttribute(`data-ptr-changed`,``)}catch{}}function G(){let e=R();return e?Array.from(e.querySelectorAll(`[data-board]`)).map(e=>{let t=e.dataset;return{id:t.nodeId??``,...t.boardName===void 0?{}:{name:t.boardName},x:Number(t.boardX??0),y:Number(t.boardY??0),w:Number(t.boardW??0),h:Number(t.boardH??0)}}):[]}function K(){let e=n(),t=o();return e&&t?`paintress:viewport:${e}/${t}`:null}function Se(e){let t=K();t!==null&&(clearTimeout(se),se=setTimeout(()=>{try{localStorage.setItem(t,JSON.stringify(e))}catch{}},300))}function q(){let e=R();e&&(he(),Pe(),I?.destroy(),I=Be({doc:e,onChange:e=>{ae(e.z),Se(e)},onLevelStep:ve}),ne(G()),k(null),J(),e.addEventListener(`click`,_e,!0),e.addEventListener(`mousemove`,be,!0),V(),xe())}function J(){if(!I)return;let e=K(),t=e===null?null:localStorage.getItem(e),n=t===null?null:Le(t);n===null?I.fitAll():I.set(n)}function Y(e){if(k(e),!I)return;if(e===null){I.fitAll();return}let t=O().find(t=>t.id===e);t&&I.focusBox({x:t.x,y:t.y,w:t.w,h:t.h})}function X(e){let t=[],n=e;for(;n;){let e=n.closest(`[data-node-id]`);if(!e)break;let r=e;t.unshift({id:r.dataset.nodeId??``,type:r.dataset.nodeType??`?`,name:r.dataset.nodeName}),n=e.parentElement}return t}async function Me(e){let t=e.dataset.nodeId??``;if(t===``)return;let r=X(e);d(t),p(r),V();let i=n();if(i)try{await ke({project:i,nodeId:t,...e.dataset.nodeName===void 0?{}:{name:e.dataset.nodeName},...e.dataset.nodeType===void 0?{}:{type:e.dataset.nodeType},path:r.map(e=>e.name??e.type).join(` / `)}),l((await Oe(i)).marks),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}function Z(e){d(e),V();let t=R(),n=t?.querySelector(`[data-node-id="${kt(e)}"]`);if(!n||!t){p([]);return}p(X(n));let r=t.querySelector(`.ptr-canvas`);if(!r||!I){n.scrollIntoView({block:`center`});return}let i=n.getBoundingClientRect(),a=r.getBoundingClientRect(),o=I.get().z;I.centerOn({x:(i.left-a.left)/o,y:(i.top-a.top)/o,w:i.width/o,h:i.height/o})}async function Ne(){let e=n();if(e)try{await Ae(e),l([]),d(null),p([]),V()}catch(e){h(e instanceof Error?e.message:String(e))}}x(()=>{let e=A();document.documentElement.lang=e===`zh`?`zh-CN`:`en`,it(e)});function Pe(){if(F!==void 0){if(o()===null)F.style.removeProperty(`--canvas-bg`),F.removeAttribute(`data-canvas-bg`);else{let e=We[M()];F.style.setProperty(`--canvas-bg`,e),F.setAttribute(`data-canvas-bg`,M())}R()?.documentElement.style.setProperty(`--ptr-canvas-bg`,We[M()])}}x(()=>{M(),Je(M()),Pe()}),x(w([n,o,v],()=>{let e=n(),t=o();D(e&&t?`${Te(e,t)}&r=${v()}`:``),Pe()}));let Q=null;return x(w([n,o,C],()=>{Q?.close(),Q=null;let e=n(),t=o();C()&&e&&t&&(Q=new EventSource(Ee(e,t)),Q.addEventListener(`change`,()=>S(e=>e+1)))})),E(()=>Q?.close()),(()=>{var t=vt(),r=t.firstChild,a=r.firstChild;a.firstChild.nextSibling;var s=a.nextSibling,l=s.firstChild,d=s.nextSibling,p=d.firstChild,h=p.firstChild,_=p.nextSibling,v=r.nextSibling,y=v.firstChild,x=y.firstChild,w=x.nextSibling,T=w.firstChild,E=T.firstChild,D=E.nextSibling;return W(a,L(B,{get when(){return g().version},get children(){var e=at();return e.firstChild,W(e,()=>g().version,null),e}}),null),W(l,()=>N().projects),W(s,L(B,{get when(){return e().length>0},get fallback(){return(()=>{var e=yt(),t=e.firstChild;return W(e,()=>N().noProjects,t),e})()},get children(){var t=ot();return W(t,L(z,{get each(){return e()},children:e=>(()=>{var t=bt(),r=t.firstChild,i=r.firstChild,a=i.nextSibling;return r.$$click=()=>void de(e.name),W(i,()=>e.name),W(a,()=>N().projectFiles(e.fileCount)),b(()=>r.classList.toggle(`active`,n()===e.name)),t})()})),t}}),null),W(r,L(B,{get when(){return n()},get children(){var t=st(),r=t.firstChild,a=r.firstChild,s=a.nextSibling,c=s.firstChild;return W(r,()=>N().designs,a),W(s,()=>N().depth,c),W(s,()=>e().find(e=>e.name===n())?.depth??`-`,null),W(t,L(B,{get when(){return i().length>0},get fallback(){return(()=>{var e=xt();return W(e,()=>N().noFiles),e})()},get children(){var e=ot();return W(e,L(z,{get each(){return i()},children:e=>(()=>{var t=St(),n=t.firstChild,r=n.firstChild;return n.$$click=()=>pe(e),W(r,e),b(()=>n.classList.toggle(`active`,o()===e)),t})()})),e}}),null),t}}),d),W(r,L(B,{get when(){return c().length>0},get children(){var e=ct(),t=e.firstChild,n=t.firstChild,r=t.nextSibling;return W(t,()=>N().marks,n),n.$$click=()=>void Ne(),W(n,()=>N().clear),W(r,L(z,{get each(){return c()},children:e=>(()=>{var t=Ct(),n=t.firstChild,r=n.firstChild,i=r.firstChild;i.firstChild,i.nextSibling;var a=r.nextSibling;return n.$$click=()=>Z(e.nodeId),W(i,()=>e.shortId,null),W(r,()=>e.name??e.nodeId,null),W(a,()=>e.path),b(()=>n.classList.toggle(`active`,u()===e.nodeId)),t})()})),e}}),d),h.addEventListener(`change`,e=>ee(e.currentTarget.checked)),W(p,()=>N().liveReload,null),_.$$click=()=>void le(),W(_,()=>N().refreshList),W(d,L(B,{get when(){return g().pid},get children(){var e=lt();return e.firstChild,W(e,()=>g().pid,null),e}}),null),ye(e=>{F=e},v),W(v,L(B,{get when(){return m()},get children(){var e=ut();return W(e,m),e}}),y),W(x,L(B,{get when(){return f().length===0},get fallback(){return L(z,{get each(){return f()},children:(e,t)=>[L(B,{get when(){return t()>0},get children(){return wt()}}),(()=>{var t=Tt();return t.$$click=()=>Z(e.id),W(t,()=>e.name??e.type),b(()=>t.classList.toggle(`active`,u()===e.id)),t})()]})},get children(){return L(B,{get when(){return me(()=>n()!==null)()&&o()!==null},get children(){var e=dt(),t=e.firstChild;return W(e,n,t),W(e,o,null),e}})}})),W(w,L(B,{get when(){return me(()=>n()!==null)()&&o()!==null},get children(){return[(()=>{var e=ft(),t=e.firstChild;return W(e,()=>Math.round(ie()*100),t),e})(),(()=>{var e=pt();return e.$$click=()=>I?.fitAll(),W(e,()=>N().fit),b(()=>U(e,`title`,N().fitTitle)),e})(),(()=>{var e=pt();return e.$$click=()=>I?.zoomToPercent(1),W(e,()=>N().actualSize),b(()=>U(e,`title`,N().actualSizeTitle)),e})(),(()=>{var e=mt();return e.$$click=()=>S(e=>e+1),W(e,()=>N().rerender),e})(),ht()]}}),T),W(w,L(Ze,{get value(){return M()},onChange:oe,get label(){return N().canvasBackground}}),T),E.$$click=()=>j(`zh`),D.$$click=()=>j(`en`),W(v,L(B,{get when(){return me(()=>n()!==null)()&&o()!==null},get fallback(){return(()=>{var e=Dt();return W(e,L(B,{get when(){return n()},get fallback(){return(()=>{var e=Et();return W(e,()=>N().pickProject),e})()},get children(){var e=Et();return W(e,()=>N().pickFile),e}})),e})()},get children(){return[L(B,{get when(){return O().length>1},get children(){var e=gt(),t=e.firstChild;return t.$$click=()=>Y(null),W(t,()=>N().allBoards),W(e,L(z,{get each(){return O()},children:e=>(()=>{var t=Ot(),n=t.firstChild,r=n.firstChild;return t.$$click=()=>Y(e.id),W(t,()=>e.name??`#${e.id}`,n),W(n,()=>Math.round(e.w),r),W(n,()=>Math.round(e.h),null),b(()=>t.classList.toggle(`active`,re()===e.id)),t})()}),null),b(()=>t.classList.toggle(`active`,re()===null)),e}}),(()=>{var e=_t();return e.addEventListener(`load`,q),ye(e=>{P=e},e),b(()=>U(e,`src`,te())),e})()]}}),null),b(e=>{var t=N().language,n=N().language,r=A()===`zh`,i=A()===`zh`,a=A()===`en`,o=A()===`en`;return t!==e.e&&U(T,`aria-label`,e.e=t),n!==e.t&&U(T,`title`,e.t=n),r!==e.a&&E.classList.toggle(`active`,e.a=r),i!==e.o&&U(E,`aria-pressed`,e.o=i),a!==e.i&&D.classList.toggle(`active`,e.i=a),o!==e.n&&U(D,`aria-pressed`,e.n=o),e},{e:void 0,t:void 0,a:void 0,o:void 0,i:void 0,n:void 0}),b(()=>h.checked=C()),t})()}_e([`click`]);var jt=document.getElementById(`root`);if(!jt)throw Error(`缺少 #root 容器`);ge(()=>L(At,{}),jt);