paintress 0.2.2 → 0.3.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
@@ -42,8 +42,8 @@ paintress install pi cursor # or only the ones you name
42
42
  ## CLI
43
43
 
44
44
  ```text
45
- paintress mcp <file>? start the stdio MCP server
46
- paintress doc_info <file> document summary
45
+ paintress mcp start the stdio MCP server
46
+ paintress info <file> document summary
47
47
  paintress query <file> [--type t] [--name n|--regex] find nodes
48
48
  paintress get <file> <id> [--depth n] read a node subtree
49
49
  paintress update <file> <id> '<patch-json>' shallow-merge attributes
@@ -84,8 +84,10 @@ Canvas navigation: wheel zooms at the pointer, space / middle button / empty are
84
84
  | Claude Desktop | `~/.config/Claude/claude_desktop_config.json` | `mcpServers` |
85
85
  | Cursor | `~/.cursor/mcp.json` | `mcpServers` |
86
86
  | VS Code | `~/.config/Code/User/mcp.json` | `servers` (with `"type": "stdio"`) |
87
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` |
87
88
  | Codex | `~/.codex/config.toml` | `[mcp_servers.paintress]` |
88
89
  | pi | `~/.pi/agent/mcp.json` | `mcpServers` |
90
+ | OpenCode | `~/.config/opencode/opencode.json` | `mcp.servers` (current) or `mcp` (older; `command` is an array) |
89
91
 
90
92
  ```bash
91
93
  paintress mcp-config cursor # print the exact snippet instead of editing files
@@ -129,6 +131,12 @@ Rules:
129
131
  - Report the resolved path and version (`paintress ping` prints the daemon version when the viewer runs; `npm ls -g --depth=0 paintress` prints the package version).
130
132
  - Only when every check is empty do you install (step 1).
131
133
 
134
+ If the install succeeded but `command -v paintress` still finds nothing (some version managers need a new shell), call it by full path — do not install a second copy:
135
+
136
+ ```bash
137
+ "$(npm prefix -g)/bin/paintress" doctor
138
+ ```
139
+
132
140
  ### 1. Install (only if step 0 found nothing)
133
141
 
134
142
  ```bash
@@ -152,7 +160,9 @@ paintress install --dry-run # preview the writes
152
160
  paintress install <client-id> # write only the client you are running in
153
161
  ```
154
162
 
155
- Client ids: `claude`, `cursor`, `vscode`, `codex`, `pi`. Use `paintress install` without arguments to cover every detected client.
163
+ Client ids: `claude`, `cursor`, `vscode`, `windsurf`, `codex`, `pi`, `opencode`. Use `paintress install` without arguments to cover every detected client.
164
+
165
+ OpenCode has two config layouts in the wild. `install` follows whatever the existing file already uses and falls back to the current `mcp.servers` shape for a new file; force it with `--opencode-layout legacy` or `--opencode-layout v2`.
156
166
 
157
167
  What the write does:
158
168
 
@@ -162,17 +172,27 @@ What the write does:
162
172
  - is idempotent: an identical entry reports `noop` and nothing is written;
163
173
  - uses the bare command `paintress` when it resolves on PATH, otherwise the absolute path of the binary.
164
174
 
165
- If the agent you run in is not in that list, print the closest snippet and adapt it:
175
+ ### If your client is not in that list
176
+
177
+ The entry is small and nearly the same everywhere, so adapt it by hand:
178
+
179
+ 1. Find where the client keeps its MCP servers — its own docs, an `/mcp` command, or a "configure MCP servers" menu item.
180
+ 2. Match the shape. Most clients use a `mcpServers` object holding `command` + `args`; VS Code uses `servers` with `"type": "stdio"`; OpenCode puts a single `command` **array** under `mcp.servers` (older builds: under `mcp`) with `"type": "local"`; Codex uses a TOML table.
181
+ 3. Print the closest snippet and edit it:
166
182
 
167
183
  ```bash
168
- paintress mcp-config cursor # JSON with mcpServers
169
- paintress mcp-config vscode # JSON with servers + type: stdio
170
- paintress mcp-config codex # TOML table
184
+ paintress mcp-config cursor # JSON with mcpServers
185
+ paintress mcp-config vscode # JSON with servers + type: stdio
186
+ paintress mcp-config opencode # JSON with mcp.servers + command array
187
+ paintress mcp-config codex # TOML table
171
188
  ```
172
189
 
190
+ To make a client first-class (so `doctor` and `install` cover it), add one spec to `src/integrations/clients.ts` — `id`, `label`, `format`, `rootPath`, optional `typeValue` / `commandForm` / `extraFields`, and `configPath(home, platform, env)` — plus one test in `test/integrations.spec.ts`. No other file needs to change.
191
+
173
192
  ### 3. Install the skill (recommended)
174
193
 
175
194
  ```bash
195
+ mkdir -p <your-skill-dir>/paintress
176
196
  cp "$(npm root -g)/paintress/skills/paintress/SKILL.md" <your-skill-dir>/paintress/SKILL.md
177
197
  ```
178
198
 
package/README.zh-CN.md CHANGED
@@ -42,8 +42,8 @@ paintress install pi cursor # 或只写指定的客户端
42
42
  ## CLI
43
43
 
44
44
  ```text
45
- paintress mcp <file>? 启动 stdio MCP server
46
- paintress doc_info <file> 文档摘要
45
+ paintress mcp 启动 stdio MCP server
46
+ paintress info <file> 文档摘要
47
47
  paintress query <file> [--type t] [--name n|--regex] 条件查询节点
48
48
  paintress get <file> <id> [--depth n] 读取节点子树
49
49
  paintress update <file> <id> '<patch-json>' 浅合并更新属性
@@ -84,8 +84,10 @@ paintress watch demo ./demo
84
84
  | Claude Desktop | `~/.config/Claude/claude_desktop_config.json` | `mcpServers` |
85
85
  | Cursor | `~/.cursor/mcp.json` | `mcpServers` |
86
86
  | VS Code | `~/.config/Code/User/mcp.json` | `servers`(条目需 `"type": "stdio"`) |
87
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` |
87
88
  | Codex | `~/.codex/config.toml` | `[mcp_servers.paintress]` |
88
89
  | pi | `~/.pi/agent/mcp.json` | `mcpServers` |
90
+ | OpenCode | `~/.config/opencode/opencode.json` | `mcp.servers`(当前版)或 `mcp`(旧版;`command` 是数组) |
89
91
 
90
92
  ```bash
91
93
  paintress mcp-config cursor # 只打印片段,不改文件
@@ -129,6 +131,12 @@ MIT
129
131
  - 向用户报告解析到的路径与版本(查看器运行时 `paintress ping` 会打印版本;包版本用 `npm ls -g --depth=0 paintress`)。
130
132
  - 只有在全部检查都为空时,才执行第 1 步。
131
133
 
134
+ 如果安装成功但 `command -v paintress` 仍然找不到(某些版本管理器需要新开 shell),用完整路径调用,**不要因此再装一份**:
135
+
136
+ ```bash
137
+ "$(npm prefix -g)/bin/paintress" doctor
138
+ ```
139
+
132
140
  ### 1. 安装(仅在检测全空时)
133
141
 
134
142
  ```bash
@@ -152,7 +160,9 @@ paintress install --dry-run # 预览将要写入的内容
152
160
  paintress install <client-id> # 只写当前所在的客户端
153
161
  ```
154
162
 
155
- 客户端 id:`claude`、`cursor`、`vscode`、`codex`、`pi`。不带参数执行 `paintress install` 会覆盖所有检测到的客户端。
163
+ 客户端 id:`claude`、`cursor`、`vscode`、`windsurf`、`codex`、`pi`、`opencode`。不带参数执行 `paintress install` 会覆盖所有检测到的客户端。
164
+
165
+ OpenCode 现在有两套配置布局:`install` 会跟随文件里既有的布局,新文件按官方当前的 `mcp.servers`;需要强制时用 `--opencode-layout legacy` 或 `--opencode-layout v2`。
156
166
 
157
167
  写入行为:
158
168
 
@@ -162,17 +172,27 @@ paintress install <client-id> # 只写当前所在的客户端
162
172
  - 幂等:条目已一致时返回 `noop`,不写磁盘;
163
173
  - 当 `paintress` 能在 PATH 上解析时写裸命令,否则写可执行文件的绝对路径。
164
174
 
165
- 如果当前 agent 不在列表里,打印最接近的片段再自行适配:
175
+ ### 如果当前 agent 不在列表里
176
+
177
+ 条目很小,各家格式几乎一致,直接照抄改写即可:
178
+
179
+ 1. 先找到该客户端放 MCP server 的位置——查它自己的文档、`/mcp` 命令,或「配置 MCP 服务器」菜单项。
180
+ 2. 对照格式:多数是 `mcpServers` 对象,里面 `command` + `args`;VS Code 是 `servers` 加 `"type": "stdio"`;OpenCode 的 `command` 是**数组**,放在 `mcp.servers` 下(旧版放 `mcp` 下)并带 `"type": "local"`;Codex 是 TOML 表。
181
+ 3. 打印最接近的片段再改:
166
182
 
167
183
  ```bash
168
- paintress mcp-config cursor # JSON,顶层 mcpServers
169
- paintress mcp-config vscode # JSON,顶层 servers + type: stdio
170
- paintress mcp-config codex # TOML 表
184
+ paintress mcp-config cursor # JSON,顶层 mcpServers
185
+ paintress mcp-config vscode # JSON,顶层 servers + type: stdio
186
+ paintress mcp-config opencode # JSON,mcp.servers + command 数组
187
+ paintress mcp-config codex # TOML 表
171
188
  ```
172
189
 
190
+ 想让某个客户端成为内置支持(`doctor`、`install` 都能覆盖),只需在 `src/integrations/clients.ts` 里加一条配置:`id`、`label`、`format`、`rootPath`,以及可选的 `typeValue` / `commandForm` / `extraFields` 和 `configPath(home, platform, env)`,再在 `test/integrations.spec.ts` 加一个测试。其他文件都不用动。
191
+
173
192
  ### 3. 安装 skill(建议)
174
193
 
175
194
  ```bash
195
+ mkdir -p <技能目录>/paintress
176
196
  cp "$(npm root -g)/paintress/skills/paintress/SKILL.md" <技能目录>/paintress/SKILL.md
177
197
  ```
178
198
 
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
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-C4TC8Bw3.js";
3
3
  import { Wt as VERSION, t as renderDocument, w as loadDoc } from "./render-CbYe9rII.js";
4
4
  import "./index.js";
5
- import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
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";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { dirname, extname, join, relative, resolve } from "node:path";
@@ -845,14 +845,21 @@ async function startDaemon(opts = {}) {
845
845
  };
846
846
  }
847
847
  // src/integrations/clients.ts
848
+ /**
849
+ * 多客户端接入(阶段 5):把「paintress mcp」这条 stdio 命令写进各家 agent 的配置。
850
+ *
851
+ * 设计要点:
852
+ * - 只认「已知客户端 + 已知路径」,不做全盘扫描;
853
+ * - 生成条目与写入分离:解析/生成是纯函数(可单测),落盘只在 apply 阶段发生;
854
+ * - 路径由 home / platform / env 注入,测试不依赖真实文件系统。
855
+ */
848
856
  const posixHome = (home) => home.replace(/\/+$/, "");
849
857
  const CLIENTS = [
850
858
  {
851
859
  id: "claude",
852
860
  label: "Claude Desktop",
853
861
  format: "json",
854
- rootKey: "mcpServers",
855
- needsType: false,
862
+ rootPath: ["mcpServers"],
856
863
  configPath: (home, platform, env) => {
857
864
  if (platform === "darwin") return `${posixHome(home)}/Library/Application Support/Claude/claude_desktop_config.json`;
858
865
  if (platform === "win32") return `${env["APPDATA"] ?? posixHome(home)}/Claude/claude_desktop_config.json`;
@@ -863,43 +870,68 @@ const CLIENTS = [
863
870
  id: "cursor",
864
871
  label: "Cursor",
865
872
  format: "json",
866
- rootKey: "mcpServers",
867
- needsType: false,
873
+ rootPath: ["mcpServers"],
868
874
  configPath: (home) => `${posixHome(home)}/.cursor/mcp.json`
869
875
  },
870
876
  {
871
877
  id: "vscode",
872
878
  label: "VS Code",
873
879
  format: "json",
874
- rootKey: "servers",
875
- needsType: true,
880
+ rootPath: ["servers"],
881
+ typeValue: "stdio",
876
882
  configPath: (home, platform, env) => {
877
883
  if (platform === "darwin") return `${posixHome(home)}/Library/Application Support/Code/User/mcp.json`;
878
884
  if (platform === "win32") return `${env["APPDATA"] ?? posixHome(home)}/Code/User/mcp.json`;
879
885
  return `${posixHome(home)}/.config/Code/User/mcp.json`;
880
886
  }
881
887
  },
888
+ {
889
+ id: "windsurf",
890
+ label: "Windsurf",
891
+ format: "json",
892
+ rootPath: ["mcpServers"],
893
+ configPath: (home, platform, env) => {
894
+ return `${platform === "win32" ? env["USERPROFILE"] ?? posixHome(home) : posixHome(home)}/.codeium/windsurf/mcp_config.json`;
895
+ }
896
+ },
882
897
  {
883
898
  id: "codex",
884
899
  label: "Codex",
885
900
  format: "toml",
886
- rootKey: "mcp_servers",
887
- needsType: false,
901
+ rootPath: ["mcp_servers"],
888
902
  configPath: (home) => `${posixHome(home)}/.codex/config.toml`
889
903
  },
890
904
  {
891
905
  id: "pi",
892
906
  label: "pi",
893
907
  format: "json",
894
- rootKey: "mcpServers",
895
- needsType: false,
908
+ rootPath: ["mcpServers"],
896
909
  configPath: (home) => `${posixHome(home)}/.pi/agent/mcp.json`
910
+ },
911
+ {
912
+ id: "opencode",
913
+ label: "OpenCode",
914
+ format: "json",
915
+ rootPath: ["mcp", "servers"],
916
+ typeValue: "local",
917
+ commandForm: "command-array",
918
+ configPath: (home, _platform, env) => {
919
+ return `${env["XDG_CONFIG_HOME"] ?? `${posixHome(home)}/.config`}/opencode/opencode.json`;
920
+ }
897
921
  }
898
922
  ];
899
923
  function findClient(id) {
900
924
  return CLIENTS.find((c) => c.id === id);
901
925
  }
902
926
  /**
927
+ * 客户端是否已在本机出现过:配置目录存在即视为已安装/已使用。
928
+ * 不依赖可执行文件是否在 PATH(GUI 客户端通常不在 PATH),也不扫全盘。
929
+ */
930
+ function isClientDetected(spec, home, platform, env, exists) {
931
+ const path = spec.configPath(home, platform, env);
932
+ return exists(path) || exists(dirname(path));
933
+ }
934
+ /**
903
935
  * 决定写进配置的启动命令。
904
936
  *
905
937
  * - 若 `paintress` 已经在 PATH 上指向当前可执行文件(全局安装的常见情形)→ 用裸命令,
@@ -937,33 +969,84 @@ function resolveLaunchCommand(opts) {
937
969
  args: ["mcp"]
938
970
  };
939
971
  }
940
- /** 单个客户端的配置条目 */
941
- function buildEntry(spec, launch) {
942
- const base = {
943
- command: launch.command,
944
- args: launch.args
945
- };
946
- return spec.needsType ? {
947
- type: "stdio",
948
- ...base
949
- } : base;
972
+ /** 单个客户端的配置条目;OpenCode 旧布局还需要 enabled: true */
973
+ function buildEntry(spec, launch, layout = "auto") {
974
+ const entry = {};
975
+ if (spec.typeValue !== void 0) entry["type"] = spec.typeValue;
976
+ if (spec.commandForm === "command-array") entry["command"] = [launch.command, ...launch.args];
977
+ else {
978
+ entry["command"] = launch.command;
979
+ entry["args"] = launch.args;
980
+ }
981
+ if (spec.extraFields !== void 0) Object.assign(entry, spec.extraFields);
982
+ if (spec.id === "opencode" && layout === "legacy") entry["enabled"] = true;
983
+ return entry;
984
+ }
985
+ function isRecord$1(value) {
986
+ return typeof value === "object" && value !== null && !Array.isArray(value);
987
+ }
988
+ /**
989
+ * 解析 server 表在配置里的路径。
990
+ *
991
+ * OpenCode 有两套布局:V2 把 server 放在 `mcp.servers` 下,旧布局直接放在 `mcp` 下。
992
+ * 已有文件按实际内容判断(避免把条目写到当前版本不读的位置),新文件用官方当前的 V2。
993
+ */
994
+ function resolveRootPath(spec, existingRoot, layout = "auto") {
995
+ if (spec.id !== "opencode") return spec.rootPath;
996
+ if (layout === "legacy") return ["mcp"];
997
+ if (layout === "v2") return ["mcp", "servers"];
998
+ const mcp = existingRoot?.["mcp"];
999
+ if (isRecord$1(mcp)) {
1000
+ if (isRecord$1(mcp["servers"])) return ["mcp", "servers"];
1001
+ if (Object.values(mcp).some((v) => isRecord$1(v) && (typeof v["type"] === "string" || "command" in v))) return ["mcp"];
1002
+ }
1003
+ return ["mcp", "servers"];
950
1004
  }
951
1005
  /** Codex 用的 TOML 片段(改 TOML 风险高,只输出不写入) */
952
1006
  function tomlSnippet(launch, name = "paintress") {
953
- const args = launch.args.map((a) => `"${a}"`).join(", ");
954
- return `[mcp_servers.${name}]\ncommand = "${launch.command}"\nargs = [${args}]\n`;
1007
+ const args = launch.args.map((a) => JSON.stringify(a)).join(", ");
1008
+ return `[mcp_servers.${name}]\ncommand = ${JSON.stringify(launch.command)}\nargs = [${args}]\n`;
955
1009
  }
956
- /** JSON 客户端用的片段(含顶层键) */
1010
+ /** JSON 客户端用的片段(含嵌套路径,OpenCode 按默认布局) */
957
1011
  function jsonSnippet(spec, launch, name = "paintress") {
958
- return JSON.stringify({ [spec.rootKey]: { [name]: buildEntry(spec, launch) } }, null, 2);
1012
+ const path = resolveRootPath(spec, null);
1013
+ const entry = buildEntry(spec, launch, path.length === 1 ? "legacy" : "v2");
1014
+ let cursor = { [name]: entry };
1015
+ for (const key of [...path].reverse()) cursor = { [key]: cursor };
1016
+ return JSON.stringify(cursor, null, 2);
959
1017
  }
960
1018
  // src/integrations/install.ts
961
1019
  /**
962
1020
  * 配置写入:解析 → 合并 → 备份 → 原子落盘。
963
1021
  * 只动 `rootKey` 下名为 `paintress` 的那一条,其余内容与顺序保持不变(JSON 重新序列化)。
964
1022
  */
1023
+ function isRecord(value) {
1024
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1025
+ }
1026
+ /** 按路径取出 server 表(不存在或类型不对时返回空对象) */
1027
+ function readTable(root, path) {
1028
+ let cursor = root;
1029
+ for (const key of path) {
1030
+ if (!isRecord(cursor)) return {};
1031
+ cursor = cursor[key];
1032
+ }
1033
+ return isRecord(cursor) ? { ...cursor } : {};
1034
+ }
1035
+ /** 按路径写回 server 表,沿途缺失的层级补建,其余键原样保留 */
1036
+ function writeTable(root, path, table) {
1037
+ let cursor = root;
1038
+ path.forEach((key, index) => {
1039
+ if (index === path.length - 1) {
1040
+ cursor[key] = table;
1041
+ return;
1042
+ }
1043
+ const next = isRecord(cursor[key]) ? { ...cursor[key] } : {};
1044
+ cursor[key] = next;
1045
+ cursor = next;
1046
+ });
1047
+ }
965
1048
  /** 纯函数:把 paintress 条目并入现有配置文本 */
966
- function planJsonMerge(spec, path, existing, launch) {
1049
+ function planJsonMerge(spec, path, existing, launch, options = {}) {
967
1050
  const base = {
968
1051
  spec,
969
1052
  path,
@@ -972,14 +1055,14 @@ function planJsonMerge(spec, path, existing, launch) {
972
1055
  detail: ""
973
1056
  };
974
1057
  if (spec.format === "toml") {
975
- const snippet = `[mcp_servers.paintress]\ncommand = "${launch.command}"\nargs = [${launch.args.map((a) => `"${a}"`).join(", ")}]\n`;
1058
+ const snippet = tomlSnippet(launch);
976
1059
  if (existing === null) return {
977
1060
  ...base,
978
1061
  status: "create",
979
1062
  next: snippet,
980
1063
  detail: "将创建配置文件并写入条目"
981
1064
  };
982
- if (existing.includes("[mcp_servers.paintress]")) return {
1065
+ if (/^\s*\[\s*mcp_servers\s*\.\s*(?:paintress|"paintress"|'paintress')\s*\]\s*$/m.test(existing)) return {
983
1066
  ...base,
984
1067
  status: "noop",
985
1068
  detail: "条目已存在(TOML 不自动改写)"
@@ -1008,9 +1091,10 @@ function planJsonMerge(spec, path, existing, launch) {
1008
1091
  detail: "配置文件不是合法 JSON,未改动"
1009
1092
  };
1010
1093
  }
1011
- const servers = root[spec.rootKey];
1012
- const table = typeof servers === "object" && servers !== null && !Array.isArray(servers) ? { ...servers } : {};
1013
- const entry = buildEntry(spec, launch);
1094
+ const rootPath = resolveRootPath(spec, root, options.opencodeLayout ?? "auto");
1095
+ const layout = spec.id === "opencode" && rootPath.length === 1 ? "legacy" : "v2";
1096
+ const table = readTable(root, rootPath);
1097
+ const entry = buildEntry(spec, launch, layout);
1014
1098
  const current = table["paintress"];
1015
1099
  if (JSON.stringify(current) === JSON.stringify(entry)) return {
1016
1100
  ...base,
@@ -1020,7 +1104,7 @@ function planJsonMerge(spec, path, existing, launch) {
1020
1104
  };
1021
1105
  const existed = current !== void 0;
1022
1106
  table["paintress"] = entry;
1023
- root[spec.rootKey] = table;
1107
+ writeTable(root, rootPath, table);
1024
1108
  return {
1025
1109
  ...base,
1026
1110
  status: existed ? "update" : existing === null ? "create" : "update",
@@ -1039,6 +1123,7 @@ function applyPlan(path, content, dryRun) {
1039
1123
  }
1040
1124
  const tmp = join(dirname(path), `.paintress-tmp-${process.pid}`);
1041
1125
  writeFileSync(tmp, content, "utf8");
1126
+ if (existsSync(path)) chmodSync(tmp, statSync(path).mode & 511);
1042
1127
  renameSync(tmp, path);
1043
1128
  return { backup };
1044
1129
  }
@@ -1059,6 +1144,13 @@ const FLAG_KEYS = /* @__PURE__ */ new Set([
1059
1144
  "port",
1060
1145
  "idle",
1061
1146
  "daemon",
1147
+ "dry-run",
1148
+ "opencode-layout"
1149
+ ]);
1150
+ const BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
1151
+ "regex",
1152
+ "reusable",
1153
+ "daemon",
1062
1154
  "dry-run"
1063
1155
  ]);
1064
1156
  /** 纯函数:argv → 命令 + 位置参数 + 标志(可单测) */
@@ -1073,7 +1165,7 @@ function parseArgv(argv) {
1073
1165
  const key = tok.slice(2);
1074
1166
  if (!FLAG_KEYS.has(key)) return null;
1075
1167
  const next = rest[i + 1];
1076
- if (key === "regex" || key === "reusable") flags[key] = true;
1168
+ if (BOOLEAN_FLAGS.has(key)) flags[key] = true;
1077
1169
  else if (typeof next === "string" && !next.startsWith("--")) {
1078
1170
  flags[key] = next;
1079
1171
  i++;
@@ -1199,22 +1291,29 @@ async function main(argv) {
1199
1291
  if (cmd === "install" || cmd === "doctor") {
1200
1292
  const home = homedir();
1201
1293
  const launch = currentLaunchCommand();
1202
- const ids = positionals.length > 0 ? positionals : CLIENTS.map((c) => c.id);
1294
+ const detected = (spec) => isClientDetected(spec, home, process.platform, process.env, existsSync);
1295
+ let ids;
1296
+ if (positionals.length > 0) ids = positionals;
1297
+ else if (cmd === "doctor") ids = CLIENTS.map((c) => c.id);
1298
+ else ids = CLIENTS.filter(detected).map((c) => c.id);
1203
1299
  const unknown = ids.filter((id) => findClient(id) === void 0);
1204
1300
  if (unknown.length > 0) throw new Error(`未知客户端:${unknown.join(", ")}(可选:${CLIENTS.map((c) => c.id).join(" / ")})`);
1205
1301
  const dryRun = cmd === "doctor" || flags["dry-run"] === true;
1302
+ const layoutFlag = flagStr(flags, "opencode-layout") ?? "auto";
1303
+ if (layoutFlag !== "auto" && layoutFlag !== "legacy" && layoutFlag !== "v2") throw new Error(`--opencode-layout 只接受 auto / legacy / v2,收到:${layoutFlag}`);
1206
1304
  const results = [];
1207
1305
  for (const id of ids) {
1208
1306
  const spec = findClient(id);
1209
1307
  if (spec === void 0) continue;
1210
1308
  const path = spec.configPath(home, process.platform, process.env);
1211
1309
  const existing = readConfig(path);
1212
- const plan = planJsonMerge(spec, path, existing, launch);
1310
+ const plan = planJsonMerge(spec, path, existing, launch, { opencodeLayout: layoutFlag });
1213
1311
  if (cmd === "doctor") {
1214
1312
  results.push({
1215
1313
  client: spec.id,
1216
1314
  label: spec.label,
1217
1315
  path,
1316
+ detected: detected(spec),
1218
1317
  exists: existing !== null,
1219
1318
  status: plan.status,
1220
1319
  detail: plan.detail
@@ -1404,11 +1503,10 @@ async function main(argv) {
1404
1503
  return 1;
1405
1504
  }
1406
1505
  }
1407
- function isDirectExecution() {
1408
- const entry = process.argv[1];
1506
+ function isDirectExecution(entry = process.argv[1], moduleUrl = import.meta.url) {
1409
1507
  if (entry === void 0) return false;
1410
1508
  try {
1411
- return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));
1509
+ return realpathSync(entry) === realpathSync(fileURLToPath(moduleUrl));
1412
1510
  } catch {
1413
1511
  return false;
1414
1512
  }
@@ -1416,4 +1514,4 @@ function isDirectExecution() {
1416
1514
  if (isDirectExecution()) main(process.argv.slice(2)).then((code) => {
1417
1515
  if (code !== 0) process.exitCode = code;
1418
1516
  });
1419
- export { main, parseArgv };
1517
+ export { isDirectExecution, main, parseArgv };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paintress",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Design-document operation layer for AI agents: read, query, edit, render",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,6 @@
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=N,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 M(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[re.bind(n),e=>(typeof e==`function`&&(e=f&&f.running&&f.sources.has(n)?e(n.tValue):e(n.value)),O(n,e))]}function b(e,t,n){k(A(e,t,!1,c))}function x(e,t,n){s=oe;let r=A(e,t,!1,c),i=D&&ne(D);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=A(e,t,!0,0);return r.observers=null,r.observerSlots=null,r.comparator=n.equals||void 0,k(r),re.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 ee(e){x(()=>C(e))}function T(e){return d===null||(d.cleanups===null?d.cleanups=[e]:d.cleanups.push(e)),e}var[te,E]=y(!1);function ne(e){let t;return d&&d.context&&(t=d.context[e.id])!==void 0?t:e.defaultValue}var D;function re(){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,M(()=>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 O(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&&M(()=>{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(()=>{M(()=>{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,z(t)}finally{p=a,d=i}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&`observers`in e?O(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 A(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 j(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,M(()=>P(e,n[0]),!1),m=t}}}function M(e,t){if(m)return e();let n=!1;t||(m=[]),h?n=!0:h=[],g++;try{let t=e();return ae(n),t}catch(e){n||(h=null),m=null,z(e)}}function ae(e){if(m&&=(N(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,M(()=>{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}E(!1)},!1)}else if(f.running){f.running=!1,f.effects.push.apply(f.effects,h),h=null,E(!0);return}}let n=h;h=null,n.length&&M(()=>s(n),!1),t&&t()}function N(e){for(let t=0;t<e.length;t++)j(e[t])}function oe(t){let r,i=0;for(r=0;r<t.length;r++){let e=t[r];e.user?t[i++]=e:j(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++)j(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)&&j(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)L(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 L(e,t){if(t||(e.tState=0,f.disposed.add(e)),e.owned)for(let t=0;t<e.owned.length;t++)L(e.owned[t])}function se(e){return e instanceof Error?e:Error(typeof e==`string`?e:`Unknown error`,{cause:e})}function R(e,t,n){try{for(let n of t)n(e)}catch(e){z(e,n&&n.owner||null)}}function z(e,t=d){let n=o&&t&&t.context&&t.context[o],r=se(e);if(!n)throw r;h?h.push({fn(){R(r,n,t)},state:c}):R(r,n,t)}var ce=Symbol(`fallback`);function B(e){for(let t=0;t<e.length;t++)e[t]()}function le(e,t,n={}){let r=[],a=[],o=[],s=0,c=t.length>1?[]:null;return T(()=>B(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&&(B(o),o=[],r=[],a=[],s=0,c&&=[]),n.fallback&&(r=[ce],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 V(e,t){return C(()=>e(t||{}))}var ue=e=>`Stale read from <${e}>.`;function H(e){let t=`fallback`in e&&{fallback:()=>e.fallback};return S(le(()=>e.each,e.children,t||void 0))}function U(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 ue(`Show`);return n()})):a}return e.fallback},void 0,void 0)}var de=e=>S(()=>e());function fe(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 pe=`_$DX_DELEGATE`;function me(e,t,n,r={}){let i;return _(r=>{i=r,t===document?e():G(t,e(),!n&&t.firstChild?null:void 0,n)},r.owner),()=>{i(),t.textContent=``}}function W(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 he(e,t=window.document){let n=t[pe]||(t[pe]=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,ye))}}function ge(e,t,n){ve(e)||(n==null?e.removeAttribute(t):e.setAttribute(t,n))}function _e(e,t,n){return C(()=>e(t,n))}function G(e,t,n,r){if(n!==void 0&&!r&&(r=[]),typeof t!=`function`)return K(e,t,r,n);b(r=>K(e,t(),r,n),r)}function ve(t){return!!e.context&&!e.done&&(!t||t.isConnected)}function ye(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 K(e,t,n,r,i){let a=ve(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=K(e,i,n,r)}),()=>n;else if(Array.isArray(t)){let o=[],c=n&&Array.isArray(n);if(q(o,t,n,i))return b(()=>n=K(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?J(e,o,r):fe(e,n,o):(n&&Y(e),J(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 q(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=q(e,o,s)||i;else if(c===`function`){if(r){for(;typeof o==`function`;)o=o();i=q(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 J(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 be(){return X(`/api/projects`)}function xe(e){return X(`/api/projects/${encodeURIComponent(e)}/files`)}function Se(e,t){return`/render/${encodeURIComponent(e)}/${t.split(`/`).map(encodeURIComponent).join(`/`)}?embed=1`}function Ce(e,t){return`/events?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`}function we(e){return X(`/api/marks?project=${encodeURIComponent(e)}`)}async function Te(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 Ee(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 De(e,t){return X(`/api/changes?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`)}var Z=.1,Oe=.02;function ke(e,t){return t===0||Math.abs(e)>=Math.abs(t)?null:t>0?-1:1}function Q(e,t=Z){return Math.min(4,Math.max(t,e))}function Ae(e,t,n,r,i=Z){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 je(e,t,n,r=Z){return Ae(e,Q(t,r)/e.z,n.width/2,n.height/2,r)}function Me(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),Oe);return{z:s,tx:(t.width-r*s)/2-e.x*s,ty:(t.height-i*s)/2-e.y*s}}function Ne(e,t,n){return{z:e.z,tx:e.tx+t,ty:e.ty+n}}function Pe(e){return`translate(${e.tx}px, ${e.ty}px) scale(${e.z})`}function Fe(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,Oe),tx:r,ty:i}}var Ie=3,Le=.0015;function Re({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=Pe(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=ke(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?Oe:Z;f(Ae(a,Math.exp(-e.deltaY*t*Le),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)<Ie)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(Ne(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 ee(e)&&e.preventDefault()}function C(e){e.key===` `&&x(!1)}function w(){x(!1),b()}function ee(e){return(e.ctrlKey||e.metaKey)&&(e.key===`0`||e.key===`)`||e.code===`Digit0`||e.code===`Numpad0`)?(f(je(a,1,u())),!0):e.shiftKey&&!e.ctrlKey&&!e.metaKey&&(e.key===`!`||e.code===`Digit1`)?(T(),!0):!1}function T(){f(Me({x:0,y:0,w:i.offsetWidth,h:i.offsetHeight},u()))}function te(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:T,centerOn:te,focusBox:e=>m(Me(e,u())),zoomToPercent:e=>f(je(a,e,u())),consumeDragClick:()=>{let e=c;return c=!1,e},handleKey:ee,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 ze=W(`<span class=badge>v`),Be=W(`<ul class=list>`),Ve=W(`<section class="section files"><h2>设计稿 <span class=hint>depth `),He=W(`<section class="section marks"><h2>标记<button class="ghost tiny">清空</button></h2><ul class=list>`),Ue=W(`<span class=hint>pid `),We=W(`<div class=error>`),Ge=W(`<code>/`),Ke=W(`<div class=toolbar><div class=crumbs></div><div class=tools><span class="hint zoom">%</span><button class="ghost tiny"title=适应窗口(Shift+1)>适应</button><button class="ghost tiny"title=100%(Ctrl/Cmd+0)>1:1</button><button class=ghost>重新渲染`),qe=W(`<div class=boardbar><button class=board>全部`),Je=W(`<iframe class=preview title="design preview"sandbox="allow-same-origin allow-scripts">`),Ye=W(`<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>`),Xe=W(`<p class=empty>还没有注册项目<br><code>paintress watch &lt;name&gt; &lt;dir&gt;`),Ze=W(`<li><button class=item><span class=item-name></span><span class=item-meta> 个设计稿`),Qe=W(`<p class=empty>没有 .ptr / .pen 文件`),$e=W(`<li><button class=item><span class=item-name>`),et=W(`<li><button class=item><span class=item-name><b class=mark-no>#</b> </span><span class=item-meta>`),tt=W(`<p>从左侧选择一个设计稿`),nt=W(`<div class=placeholder>`),rt=W(`<p>从左侧选择项目`),it=W(`<span class=sep>/`),at=W(`<button class=crumb>`),ot=W(`<button class=board><span class=board-size>×`);function $(e){return typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(e):e.replace(/["\\]/g,`\\$&`)}function st(){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,te]=y(!0),[E,ne]=y(``),[D,re]=y([]),[O,k]=y(null),[ie,A]=y(1),j,M=null,ae;function N(){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 oe(){try{let e=await be();t(e.projects),_({pid:e.pid,version:e.version}),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}async function P(e){try{l((await we(e)).marks)}catch{l([])}}async function F(e){r(e),s(null),d(null),p([]);try{let t=await xe(e);a(t.files),h(null)}catch(e){a([]),h(e instanceof Error?e.message:String(e))}P(e),N()}function I(e){s(e),d(null),p([]),N()}ee(()=>{let e=e=>{let t=e.target;return t?.tagName===`INPUT`||t?.tagName===`TEXTAREA`||t?.isContentEditable===!0},t=t=>{e(t)||(t.key===` `&&!t.ctrlKey&&!t.metaKey&&!t.altKey?(M?.setSpace(!0),t.preventDefault()):M?.handleKey(t)===!0&&t.preventDefault())},n=e=>{e.key===` `&&M?.setSpace(!1)};window.addEventListener(`keydown`,t),window.addEventListener(`keyup`,n),T(()=>{window.removeEventListener(`keydown`,t),window.removeEventListener(`keyup`,n)});let r=new URLSearchParams(location.search);(async()=>{await oe();let e=r.get(`project`);if(e){await F(e);let t=r.get(`file`);t&&I(t)}})()});let L=()=>j?.contentDocument??null;function se(){let e=L();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;}`,e.head.appendChild(t)}function R(){let e=L();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="${$(t)}"]`)?.setAttribute(`data-ptr-selected`,``)}function z(e){return e.parentElement?.closest(`[data-node-id]`)??null}function ce(e){return e.querySelector(`:scope > [data-node-id]`)}function B(e){if(M?.consumeDragClick()===!0){e.preventDefault(),e.stopPropagation();return}let t=e.target?.closest(`[data-node-id]`);t&&(e.preventDefault(),e.stopPropagation(),q(e.altKey?z(t)??t:t))}function le(e,t){let n=L();if(!n)return;let r=u(),i=r===null?null:n.querySelector(`[data-node-id="${$(r)}"]`);if(i===null){let e=(n.elementFromPoint(t.x,t.y)?.closest(`[data-node-id]`))?.dataset.nodeId;e!==void 0&&J(e);return}let a=(e===1?z(i):ce(i))?.dataset.nodeId;a!==void 0&&J(a)}function ue(e){let t=L();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 fe(){let e=n(),t=o();if(e&&t)try{let n=await De(e,t);if(!n.ok||n.ids.length===0)return;let r=L();if(!r)return;for(let e of n.ids)r.querySelector(`[data-node-id="${$(e)}"]`)?.setAttribute(`data-ptr-changed`,``)}catch{}}function pe(){let e=L();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 me(){let e=n(),t=o();return e&&t?`paintress:viewport:${e}/${t}`:null}function W(e){let t=me();t!==null&&(clearTimeout(ae),ae=setTimeout(()=>{try{localStorage.setItem(t,JSON.stringify(e))}catch{}},300))}function he(){let e=L();e&&(se(),M?.destroy(),M=Re({doc:e,onChange:e=>{A(e.z),W(e)},onLevelStep:le}),re(pe()),k(null),ve(),e.addEventListener(`click`,B,!0),e.addEventListener(`mousemove`,ue,!0),R(),fe())}function ve(){if(!M)return;let e=me(),t=e===null?null:localStorage.getItem(e),n=t===null?null:Fe(t);n===null?M.fitAll():M.set(n)}function ye(e){if(k(e),!M)return;if(e===null){M.fitAll();return}let t=D().find(t=>t.id===e);t&&M.focusBox({x:t.x,y:t.y,w:t.w,h:t.h})}function K(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 q(e){let t=e.dataset.nodeId??``;if(t===``)return;let r=K(e);d(t),p(r),R();let i=n();if(i)try{await Te({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 we(i)).marks),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}function J(e){d(e),R();let t=L(),n=t?.querySelector(`[data-node-id="${$(e)}"]`);if(!n||!t){p([]);return}p(K(n));let r=t.querySelector(`.ptr-canvas`);if(!r||!M){n.scrollIntoView({block:`center`});return}let i=n.getBoundingClientRect(),a=r.getBoundingClientRect(),o=M.get().z;M.centerOn({x:(i.left-a.left)/o,y:(i.top-a.top)/o,w:i.width/o,h:i.height/o})}async function Y(){let e=n();if(e)try{await Ee(e),l([]),d(null),p([]),R()}catch(e){h(e instanceof Error?e.message:String(e))}}x(w([n,o,v],()=>{let e=n(),t=o();ne(e&&t?`${Se(e,t)}&r=${v()}`:``)}));let X=null;return x(w([n,o,C],()=>{X?.close(),X=null;let e=n(),t=o();C()&&e&&t&&(X=new EventSource(Ce(e,t)),X.addEventListener(`change`,()=>S(e=>e+1)))})),T(()=>X?.close()),(()=>{var t=Ye(),r=t.firstChild,a=r.firstChild;a.firstChild.nextSibling;var s=a.nextSibling;s.firstChild;var l=s.nextSibling,d=l.firstChild,p=d.firstChild,h=d.nextSibling,_=r.nextSibling;return G(a,V(U,{get when(){return g().version},get children(){var e=ze();return e.firstChild,G(e,()=>g().version,null),e}}),null),G(s,V(U,{get when(){return e().length>0},get fallback(){return Xe()},get children(){var t=Be();return G(t,V(H,{get each(){return e()},children:e=>(()=>{var t=Ze(),r=t.firstChild,i=r.firstChild,a=i.nextSibling,o=a.firstChild;return r.$$click=()=>void F(e.name),G(i,()=>e.name),G(a,()=>e.fileCount,o),b(()=>r.classList.toggle(`active`,n()===e.name)),t})()})),t}}),null),G(r,V(U,{get when(){return n()},get children(){var t=Ve(),r=t.firstChild.firstChild.nextSibling;return r.firstChild,G(r,()=>e().find(e=>e.name===n())?.depth??`-`,null),G(t,V(U,{get when(){return i().length>0},get fallback(){return Qe()},get children(){var e=Be();return G(e,V(H,{get each(){return i()},children:e=>(()=>{var t=$e(),n=t.firstChild,r=n.firstChild;return n.$$click=()=>I(e),G(r,e),b(()=>n.classList.toggle(`active`,o()===e)),t})()})),e}}),null),t}}),l),G(r,V(U,{get when(){return c().length>0},get children(){var e=He(),t=e.firstChild,n=t.firstChild.nextSibling,r=t.nextSibling;return n.$$click=()=>void Y(),G(r,V(H,{get each(){return c()},children:e=>(()=>{var t=et(),n=t.firstChild,r=n.firstChild,i=r.firstChild;i.firstChild,i.nextSibling;var a=r.nextSibling;return n.$$click=()=>J(e.nodeId),G(i,()=>e.shortId,null),G(r,()=>e.name??e.nodeId,null),G(a,()=>e.path),b(()=>n.classList.toggle(`active`,u()===e.nodeId)),t})()})),e}}),l),p.addEventListener(`change`,e=>te(e.currentTarget.checked)),h.$$click=()=>void oe(),G(l,V(U,{get when(){return g().pid},get children(){var e=Ue();return e.firstChild,G(e,()=>g().pid,null),e}}),null),G(_,V(U,{get when(){return m()},get children(){var e=We();return G(e,m),e}}),null),G(_,V(U,{get when(){return de(()=>n()!==null)()&&o()!==null},get fallback(){return(()=>{var e=nt();return G(e,V(U,{get when(){return n()},get fallback(){return rt()},get children(){return tt()}})),e})()},get children(){return[(()=>{var e=Ke(),t=e.firstChild,r=t.nextSibling.firstChild,i=r.firstChild,a=r.nextSibling,s=a.nextSibling,c=s.nextSibling;return G(t,V(U,{get when(){return f().length===0},get fallback(){return V(H,{get each(){return f()},children:(e,t)=>[V(U,{get when(){return t()>0},get children(){return it()}}),(()=>{var t=at();return t.$$click=()=>J(e.id),G(t,()=>e.name??e.type),b(()=>t.classList.toggle(`active`,u()===e.id)),t})()]})},get children(){var e=Ge(),t=e.firstChild;return G(e,n,t),G(e,o,null),e}})),G(r,()=>Math.round(ie()*100),i),a.$$click=()=>M?.fitAll(),s.$$click=()=>M?.zoomToPercent(1),c.$$click=()=>S(e=>e+1),e})(),V(U,{get when(){return D().length>1},get children(){var e=qe(),t=e.firstChild;return t.$$click=()=>ye(null),G(e,V(H,{get each(){return D()},children:e=>(()=>{var t=ot(),n=t.firstChild,r=n.firstChild;return t.$$click=()=>ye(e.id),G(t,()=>e.name??`#${e.id}`,n),G(n,()=>Math.round(e.w),r),G(n,()=>Math.round(e.h),null),b(()=>t.classList.toggle(`active`,O()===e.id)),t})()}),null),b(()=>t.classList.toggle(`active`,O()===null)),e}}),(()=>{var e=Je();return e.addEventListener(`load`,he),_e(e=>{j=e},e),b(()=>ge(e,`src`,E())),e})()]}}),null),b(()=>p.checked=C()),t})()}he([`click`]);var ct=document.getElementById(`root`);if(!ct)throw Error(`缺少 #root 容器`);me(()=>V(st,{}),ct);
@@ -4,7 +4,7 @@
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-CMZ78plm.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-WQnLS4sD.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-lyiZ9W-B.css">
9
9
  </head>
10
10
  <body>
@@ -1,6 +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=M,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[ie.bind(n),e=>(typeof e==`function`&&(e=f&&f.running&&f.sources.has(n)?e(n.tValue):e(n.value)),D(n,e))]}function b(e,t,n){O(k(e,t,!1,c))}function x(e,t,n){s=se;let r=k(e,t,!1,c),i=E&&re(E);i&&(r.suspense=i),n&&n.render||(r.user=!0),h?h.push(r):O(r)}function S(e,t,n){n=n?Object.assign({},a,n):a;let r=k(e,t,!0,0);return r.observers=null,r.observerSlots=null,r.comparator=n.equals||void 0,O(r),ie.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 ee(e){x(()=>C(e))}function T(e){return d===null||(d.cleanups===null?d.cleanups=[e]:d.cleanups.push(e)),e}var[te,ne]=y(!1);function re(e){let t;return d&&d.context&&(t=d.context[e.id])!==void 0?t:e.defaultValue}var E;function ie(){let e=f&&f.running;if(this.sources&&(e?this.tState:this.state)){if((e?this.tState:this.state)===c)O(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 D(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 O(e){if(!e.fn)return;F(e);let t=g;ae(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,ae(e,e.tValue,t),p=d=null},!1)})}function ae(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,R(t)}finally{p=a,d=i}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&`observers`in e?D(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 k(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)O(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 oe(n),t}catch(e){n||(h=null),m=null,R(e)}}function oe(e){if(m&&=(M(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}ne(!1)},!1)}else if(f.running){f.running=!1,f.effects.push.apply(f.effects,h),h=null,ne(!0);return}}let n=h;h=null,n.length&&j(()=>s(n),!1),t&&t()}function M(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)I(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 I(e,t){if(t||(e.tState=0,f.disposed.add(e)),e.owned)for(let t=0;t<e.owned.length;t++)I(e.owned[t])}function ce(e){return e instanceof Error?e:Error(typeof e==`string`?e:`Unknown error`,{cause:e})}function L(e,t,n){try{for(let n of t)n(e)}catch(e){R(e,n&&n.owner||null)}}function R(e,t=d){let n=o&&t&&t.context&&t.context[o],r=ce(e);if(!n)throw r;h?h.push({fn(){L(r,n,t)},state:c}):L(r,n,t)}var le=Symbol(`fallback`);function z(e){for(let t=0;t<e.length;t++)e[t]()}function ue(e,t,n={}){let r=[],a=[],o=[],s=0,c=t.length>1?[]:null;return T(()=>z(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&&(z(o),o=[],r=[],a=[],s=0,c&&=[]),n.fallback&&(r=[le],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 B(e,t){return C(()=>e(t||{}))}var de=e=>`Stale read from <${e}>.`;function V(e){let t=`fallback`in e&&{fallback:()=>e.fallback};return S(ue(()=>e.each,e.children,t||void 0))}function H(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 de(`Show`);return n()})):a}return e.fallback},void 0,void 0)}var fe=e=>S(()=>e());function pe(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 me=`_$DX_DELEGATE`;function U(e,t,n,r={}){let i;return _(r=>{i=r,t===document?e():G(t,e(),!n&&t.firstChild?null:void 0,n)},r.owner),()=>{i(),t.textContent=``}}function W(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 he(e,t=window.document){let n=t[me]||(t[me]=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 ge(e,t,n){K(e)||(n==null?e.removeAttribute(t):e.setAttribute(t,n))}function _e(e,t,n){return C(()=>e(t,n))}function G(e,t,n,r){if(n!==void 0&&!r&&(r=[]),typeof t!=`function`)return q(e,t,r,n);b(r=>q(e,t(),r,n),r)}function K(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 q(e,t,n,r,i){let a=K(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=X(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=X(e,n,r)}else if(o===`function`)return b(()=>{let i=t();for(;typeof i==`function`;)i=i();n=q(e,i,n,r)}),()=>n;else if(Array.isArray(t)){let o=[],c=n&&Array.isArray(n);if(J(o,t,n,i))return b(()=>n=q(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=X(e,n,r),s)return n}else c?n.length===0?Y(e,o,r):pe(e,n,o):(n&&X(e),Y(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=X(e,n,r,t);X(e,n,null,t)}else n==null||n===``||!e.firstChild?e.appendChild(t):e.replaceChild(t,e.firstChild);n=t}return n}function J(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=J(e,o,s)||i;else if(c===`function`){if(r){for(;typeof o==`function`;)o=o();i=J(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 Y(e,t,n=null){for(let r=0,i=t.length;r<i;r++)e.insertBefore(t[r],n)}function X(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 Z(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 ye(){return Z(`/api/projects`)}function be(e){return Z(`/api/projects/${encodeURIComponent(e)}/files`)}function xe(e,t){return`/render/${encodeURIComponent(e)}/${t.split(`/`).map(encodeURIComponent).join(`/`)}?embed=1`}function Se(e,t){return`/events?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`}function Ce(e){return Z(`/api/marks?project=${encodeURIComponent(e)}`)}async function we(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 Te(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 Ee(e,t){return Z(`/api/changes?project=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`)}var De=.1,Oe=.02;function Q(e,t=De){return Math.min(4,Math.max(t,e))}function ke(e,t,n,r,i=De){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 Ae(e,t,n,r=De){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),Oe);return{z:s,tx:(t.width-r*s)/2-e.x*s,ty:(t.height-i*s)/2-e.y*s}}function Me(e,t,n){return{z:e.z,tx:e.tx+t,ty:e.ty+n}}function Ne(e){return`translate(${e.tx}px, ${e.ty}px) scale(${e.z})`}function Pe(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,Oe),tx:r,ty:i}}var Fe=3,Ie=.0015;function Le({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=Ne(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){n(e.deltaY>0?-1:1,{x:e.clientX,y:e.clientY});return}p();let t=1;e.deltaMode===1?t=16:e.deltaMode===2&&(t=u().height),f(ke(a,Math.exp(-e.deltaY*t*Ie),e.clientX,e.clientY))}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)<Fe)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(Me(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 ee(e)&&e.preventDefault()}function C(e){e.key===` `&&x(!1)}function w(){x(!1),b()}function ee(e){return(e.ctrlKey||e.metaKey)&&(e.key===`0`||e.key===`)`||e.code===`Digit0`||e.code===`Numpad0`)?(f(Ae(a,1,u())),!0):e.shiftKey&&!e.ctrlKey&&!e.metaKey&&(e.key===`!`||e.code===`Digit1`)?(T(),!0):!1}function T(){f(je({x:0,y:0,w:i.offsetWidth,h:i.offsetHeight},u()))}function te(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:T,centerOn:te,focusBox:e=>m(je(e,u())),zoomToPercent:e=>f(Ae(a,e,u())),consumeDragClick:()=>{let e=c;return c=!1,e},handleKey:ee,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 Re=W(`<span class=badge>v`),ze=W(`<ul class=list>`),Be=W(`<section class="section files"><h2>设计稿 <span class=hint>depth `),Ve=W(`<section class="section marks"><h2>标记<button class="ghost tiny">清空</button></h2><ul class=list>`),He=W(`<span class=hint>pid `),Ue=W(`<div class=error>`),We=W(`<code>/`),Ge=W(`<div class=toolbar><div class=crumbs></div><div class=tools><span class="hint zoom">%</span><button class="ghost tiny"title=适应窗口(Shift+1)>适应</button><button class="ghost tiny"title=100%(Ctrl/Cmd+0)>1:1</button><button class=ghost>重新渲染`),Ke=W(`<div class=boardbar><button class=board>全部`),qe=W(`<iframe class=preview title="design preview"sandbox="allow-same-origin allow-scripts">`),Je=W(`<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>`),Ye=W(`<p class=empty>还没有注册项目<br><code>paintress watch &lt;name&gt; &lt;dir&gt;`),Xe=W(`<li><button class=item><span class=item-name></span><span class=item-meta> 个设计稿`),Ze=W(`<p class=empty>没有 .ptr / .pen 文件`),Qe=W(`<li><button class=item><span class=item-name>`),$e=W(`<li><button class=item><span class=item-name><b class=mark-no>#</b> </span><span class=item-meta>`),et=W(`<p>从左侧选择一个设计稿`),tt=W(`<div class=placeholder>`),nt=W(`<p>从左侧选择项目`),rt=W(`<span class=sep>/`),it=W(`<button class=crumb>`),at=W(`<button class=board><span class=board-size>×`);function $(e){return typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(e):e.replace(/["\\]/g,`\\$&`)}function ot(){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,te]=y(!0),[ne,re]=y(``),[E,ie]=y([]),[D,O]=y(null),[ae,k]=y(1),A,j=null,oe;function M(){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 se(){try{let e=await ye();t(e.projects),_({pid:e.pid,version:e.version}),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}async function N(e){try{l((await Ce(e)).marks)}catch{l([])}}async function P(e){r(e),s(null),d(null),p([]);try{let t=await be(e);a(t.files),h(null)}catch(e){a([]),h(e instanceof Error?e.message:String(e))}N(e),M()}function F(e){s(e),d(null),p([]),M()}ee(()=>{let e=e=>{let t=e.target;return t?.tagName===`INPUT`||t?.tagName===`TEXTAREA`||t?.isContentEditable===!0},t=t=>{e(t)||(t.key===` `&&!t.ctrlKey&&!t.metaKey&&!t.altKey?(j?.setSpace(!0),t.preventDefault()):j?.handleKey(t)===!0&&t.preventDefault())},n=e=>{e.key===` `&&j?.setSpace(!1)};window.addEventListener(`keydown`,t),window.addEventListener(`keyup`,n),T(()=>{window.removeEventListener(`keydown`,t),window.removeEventListener(`keyup`,n)});let r=new URLSearchParams(location.search);(async()=>{await se();let e=r.get(`project`);if(e){await P(e);let t=r.get(`file`);t&&F(t)}})()});let I=()=>A?.contentDocument??null;function ce(){let e=I();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;}`,e.head.appendChild(t)}function L(){let e=I();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="${$(t)}"]`)?.setAttribute(`data-ptr-selected`,``)}function R(e){return e.parentElement?.closest(`[data-node-id]`)??null}function le(e){return e.querySelector(`:scope > [data-node-id]`)}function z(e){if(j?.consumeDragClick()===!0){e.preventDefault(),e.stopPropagation();return}let t=e.target?.closest(`[data-node-id]`);t&&(e.preventDefault(),e.stopPropagation(),J(e.altKey?R(t)??t:t))}function ue(e,t){let n=I();if(!n)return;let r=u(),i=r===null?null:n.querySelector(`[data-node-id="${$(r)}"]`);if(i===null){let e=(n.elementFromPoint(t.x,t.y)?.closest(`[data-node-id]`))?.dataset.nodeId;e!==void 0&&Y(e);return}let a=(e===1?R(i):le(i))?.dataset.nodeId;a!==void 0&&Y(a)}function de(e){let t=I();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 pe(){let e=n(),t=o();if(e&&t)try{let n=await Ee(e,t);if(!n.ok||n.ids.length===0)return;let r=I();if(!r)return;for(let e of n.ids)r.querySelector(`[data-node-id="${$(e)}"]`)?.setAttribute(`data-ptr-changed`,``)}catch{}}function me(){let e=I();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 U(){let e=n(),t=o();return e&&t?`paintress:viewport:${e}/${t}`:null}function W(e){let t=U();t!==null&&(clearTimeout(oe),oe=setTimeout(()=>{try{localStorage.setItem(t,JSON.stringify(e))}catch{}},300))}function he(){let e=I();e&&(ce(),j?.destroy(),j=Le({doc:e,onChange:e=>{k(e.z),W(e)},onLevelStep:ue}),ie(me()),O(null),K(),e.addEventListener(`click`,z,!0),e.addEventListener(`mousemove`,de,!0),L(),pe())}function K(){if(!j)return;let e=U(),t=e===null?null:localStorage.getItem(e),n=t===null?null:Pe(t);n===null?j.fitAll():j.set(n)}function ve(e){if(O(e),!j)return;if(e===null){j.fitAll();return}let t=E().find(t=>t.id===e);t&&j.focusBox({x:t.x,y:t.y,w:t.w,h:t.h})}function q(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 J(e){let t=e.dataset.nodeId??``;if(t===``)return;let r=q(e);d(t),p(r),L();let i=n();if(i)try{await we({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 Ce(i)).marks),h(null)}catch(e){h(e instanceof Error?e.message:String(e))}}function Y(e){d(e),L();let t=I(),n=t?.querySelector(`[data-node-id="${$(e)}"]`);if(!n||!t){p([]);return}p(q(n));let r=t.querySelector(`.ptr-canvas`);if(!r||!j){n.scrollIntoView({block:`center`});return}let i=n.getBoundingClientRect(),a=r.getBoundingClientRect(),o=j.get().z;j.centerOn({x:(i.left-a.left)/o,y:(i.top-a.top)/o,w:i.width/o,h:i.height/o})}async function X(){let e=n();if(e)try{await Te(e),l([]),d(null),p([]),L()}catch(e){h(e instanceof Error?e.message:String(e))}}x(w([n,o,v],()=>{let e=n(),t=o();re(e&&t?`${xe(e,t)}&r=${v()}`:``)}));let Z=null;return x(w([n,o,C],()=>{Z?.close(),Z=null;let e=n(),t=o();C()&&e&&t&&(Z=new EventSource(Se(e,t)),Z.addEventListener(`change`,()=>S(e=>e+1)))})),T(()=>Z?.close()),(()=>{var t=Je(),r=t.firstChild,a=r.firstChild;a.firstChild.nextSibling;var s=a.nextSibling;s.firstChild;var l=s.nextSibling,d=l.firstChild,p=d.firstChild,h=d.nextSibling,_=r.nextSibling;return G(a,B(H,{get when(){return g().version},get children(){var e=Re();return e.firstChild,G(e,()=>g().version,null),e}}),null),G(s,B(H,{get when(){return e().length>0},get fallback(){return Ye()},get children(){var t=ze();return G(t,B(V,{get each(){return e()},children:e=>(()=>{var t=Xe(),r=t.firstChild,i=r.firstChild,a=i.nextSibling,o=a.firstChild;return r.$$click=()=>void P(e.name),G(i,()=>e.name),G(a,()=>e.fileCount,o),b(()=>r.classList.toggle(`active`,n()===e.name)),t})()})),t}}),null),G(r,B(H,{get when(){return n()},get children(){var t=Be(),r=t.firstChild.firstChild.nextSibling;return r.firstChild,G(r,()=>e().find(e=>e.name===n())?.depth??`-`,null),G(t,B(H,{get when(){return i().length>0},get fallback(){return Ze()},get children(){var e=ze();return G(e,B(V,{get each(){return i()},children:e=>(()=>{var t=Qe(),n=t.firstChild,r=n.firstChild;return n.$$click=()=>F(e),G(r,e),b(()=>n.classList.toggle(`active`,o()===e)),t})()})),e}}),null),t}}),l),G(r,B(H,{get when(){return c().length>0},get children(){var e=Ve(),t=e.firstChild,n=t.firstChild.nextSibling,r=t.nextSibling;return n.$$click=()=>void X(),G(r,B(V,{get each(){return c()},children:e=>(()=>{var t=$e(),n=t.firstChild,r=n.firstChild,i=r.firstChild;i.firstChild,i.nextSibling;var a=r.nextSibling;return n.$$click=()=>Y(e.nodeId),G(i,()=>e.shortId,null),G(r,()=>e.name??e.nodeId,null),G(a,()=>e.path),b(()=>n.classList.toggle(`active`,u()===e.nodeId)),t})()})),e}}),l),p.addEventListener(`change`,e=>te(e.currentTarget.checked)),h.$$click=()=>void se(),G(l,B(H,{get when(){return g().pid},get children(){var e=He();return e.firstChild,G(e,()=>g().pid,null),e}}),null),G(_,B(H,{get when(){return m()},get children(){var e=Ue();return G(e,m),e}}),null),G(_,B(H,{get when(){return fe(()=>n()!==null)()&&o()!==null},get fallback(){return(()=>{var e=tt();return G(e,B(H,{get when(){return n()},get fallback(){return nt()},get children(){return et()}})),e})()},get children(){return[(()=>{var e=Ge(),t=e.firstChild,r=t.nextSibling.firstChild,i=r.firstChild,a=r.nextSibling,s=a.nextSibling,c=s.nextSibling;return G(t,B(H,{get when(){return f().length===0},get fallback(){return B(V,{get each(){return f()},children:(e,t)=>[B(H,{get when(){return t()>0},get children(){return rt()}}),(()=>{var t=it();return t.$$click=()=>Y(e.id),G(t,()=>e.name??e.type),b(()=>t.classList.toggle(`active`,u()===e.id)),t})()]})},get children(){var e=We(),t=e.firstChild;return G(e,n,t),G(e,o,null),e}})),G(r,()=>Math.round(ae()*100),i),a.$$click=()=>j?.fitAll(),s.$$click=()=>j?.zoomToPercent(1),c.$$click=()=>S(e=>e+1),e})(),B(H,{get when(){return E().length>1},get children(){var e=Ke(),t=e.firstChild;return t.$$click=()=>ve(null),G(e,B(V,{get each(){return E()},children:e=>(()=>{var t=at(),n=t.firstChild,r=n.firstChild;return t.$$click=()=>ve(e.id),G(t,()=>e.name??`#${e.id}`,n),G(n,()=>Math.round(e.w),r),G(n,()=>Math.round(e.h),null),b(()=>t.classList.toggle(`active`,D()===e.id)),t})()}),null),b(()=>t.classList.toggle(`active`,D()===null)),e}}),(()=>{var e=qe();return e.addEventListener(`load`,he),_e(e=>{A=e},e),b(()=>ge(e,`src`,ne())),e})()]}}),null),b(()=>p.checked=C()),t})()}he([`click`]);var st=document.getElementById(`root`);if(!st)throw Error(`缺少 #root 容器`);U(()=>B(ot,{}),st);