paintress 0.1.1 → 0.2.1
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 +153 -77
- package/README.zh-CN.md +203 -0
- package/assets/icon-512-wordmark.webp +0 -0
- package/assets/icon-512.webp +0 -0
- package/assets/logo.svg +47 -0
- package/dist/cli.js +2 -3
- package/dist/index.js +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/{render-Bv6Wlw9M.js → render-CbYe9rII.js} +20 -2
- package/dist/{server-DTPbfSgY.js → server-C4TC8Bw3.js} +2 -2
- package/package.json +13 -3
- package/skills/paintress/SKILL.md +53 -0
- package/ui/dist/assets/{index-D1dPL6Y0.js → index-CMZ78plm.js} +5 -5
- package/ui/dist/assets/index-lyiZ9W-B.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/logo.svg +47 -0
- package/ui/dist/assets/index-D--yd1D2.css +0 -1
package/README.md
CHANGED
|
@@ -1,127 +1,203 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="assets/icon-512-wordmark.webp" width="200" alt="paintress">
|
|
3
|
+
<p>Design-document operation layer for AI agents</p>
|
|
4
|
+
<p><a href="README.zh-CN.md">中文</a></p>
|
|
5
|
+
</div>
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
**To install: paste this repository's URL into your agent and ask it to install paintress.** Agents that need a precise procedure will find one at the [end of this file](#installation-guide-for-ai-agents).
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
## What it is
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
paintress reads and writes structured design documents — JSON files with a documented schema (`.pen`, and its own `.ptr` for anything it writes). It renders them to HTML, serves a local viewer, and exposes the same operations as MCP tools and as a CLI.
|
|
8
12
|
|
|
9
|
-
-
|
|
10
|
-
- **渲染**:设计稿 → HTML/CSS,由浏览器排版,输出可直接使用的页面
|
|
11
|
-
- **查看**:本地服务,文件变更自动刷新
|
|
12
|
-
- **标记**:点击节点生成短号,便于在对话中指代
|
|
13
|
+
It is not an editor. There is no canvas, no drag-and-drop, no property panel. A human looks at the design in a browser and clicks nodes to point at them; the agent does the reading and writing.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
| Part | Purpose |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| MCP tools | query, read, update, insert, delete, move, batch, validate, render, screenshot |
|
|
18
|
+
| CLI | the same operations plus the viewer process, for use without an agent |
|
|
19
|
+
| Viewer | local web server: renders a document, highlights changes, hands back clicked nodes as `#1`, `#2`, … |
|
|
20
|
+
| Screenshot | PNG of a whole canvas, for agents that can look at images |
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
Node.js 22 or newer.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
17
27
|
|
|
18
28
|
```bash
|
|
19
|
-
npm install -g paintress
|
|
20
|
-
npx paintress --help # 或临时运行
|
|
29
|
+
npm install -g paintress
|
|
21
30
|
```
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
Then register it with the agent(s) you use:
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
```bash
|
|
35
|
+
paintress doctor # what is installed and which clients already have the entry
|
|
36
|
+
paintress install # write the MCP entry into every detected client config
|
|
37
|
+
paintress install pi cursor # or only the ones you name
|
|
38
|
+
```
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
`install` merges a single entry, preserves everything else in the file, and writes a `*.bak-<timestamp>` backup before touching it. Running it twice changes nothing.
|
|
41
|
+
|
|
42
|
+
## CLI
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
paintress mcp <file>? start the stdio MCP server
|
|
46
|
+
paintress doc_info <file> document summary
|
|
47
|
+
paintress query <file> [--type t] [--name n|--regex] find nodes
|
|
48
|
+
paintress get <file> <id> [--depth n] read a node subtree
|
|
49
|
+
paintress update <file> <id> '<patch-json>' shallow-merge attributes
|
|
50
|
+
paintress insert <file> <parent> '<node-json>' insert a child node
|
|
51
|
+
paintress delete <file> <id> remove a node
|
|
52
|
+
paintress move <file> <id> <parent> [--index n] reparent a node
|
|
53
|
+
paintress batch <file> '<ops-json>' several operations at once
|
|
54
|
+
paintress validate <file> check against the format spec
|
|
55
|
+
paintress render <file> <out.html> write the canvas as HTML
|
|
56
|
+
paintress shot <file> <out.png> [--scale n] render to PNG
|
|
57
|
+
|
|
58
|
+
paintress web [--port n] [--idle ms] [--daemon] start the viewer
|
|
59
|
+
paintress ping | status | stop viewer control
|
|
60
|
+
paintress watch <name> <dir> | unwatch <name> register a directory to browse
|
|
61
|
+
paintress attach <name> keep the viewer alive while an agent works
|
|
62
|
+
|
|
63
|
+
paintress install [client...] [--dry-run] write MCP config entries
|
|
64
|
+
paintress doctor show client configuration state
|
|
65
|
+
paintress mcp-config <client> print the config snippet for one client
|
|
66
|
+
```
|
|
28
67
|
|
|
29
|
-
|
|
68
|
+
## Viewer
|
|
30
69
|
|
|
31
70
|
```bash
|
|
32
|
-
paintress
|
|
33
|
-
paintress
|
|
34
|
-
|
|
35
|
-
paintress install pi cursor # 只写指定客户端
|
|
71
|
+
paintress web --daemon --idle 3600000
|
|
72
|
+
paintress watch demo ./demo
|
|
73
|
+
# open http://localhost:3316/?project=demo&file=hello.ptr
|
|
36
74
|
```
|
|
37
75
|
|
|
38
|
-
|
|
76
|
+
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`.
|
|
39
77
|
|
|
40
|
-
|
|
78
|
+
## MCP clients
|
|
79
|
+
|
|
80
|
+
`paintress install` writes these; the same entries can be added by hand.
|
|
81
|
+
|
|
82
|
+
| Client | Config file | Top-level key |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| Claude Desktop | `~/.config/Claude/claude_desktop_config.json` | `mcpServers` |
|
|
85
|
+
| Cursor | `~/.cursor/mcp.json` | `mcpServers` |
|
|
86
|
+
| VS Code | `~/.config/Code/User/mcp.json` | `servers` (with `"type": "stdio"`) |
|
|
87
|
+
| Codex | `~/.codex/config.toml` | `[mcp_servers.paintress]` |
|
|
88
|
+
| pi | `~/.pi/agent/mcp.json` | `mcpServers` |
|
|
41
89
|
|
|
42
90
|
```bash
|
|
43
|
-
paintress mcp-config
|
|
44
|
-
paintress mcp-config codex # 输出 TOML 片段
|
|
91
|
+
paintress mcp-config cursor # print the exact snippet instead of editing files
|
|
45
92
|
```
|
|
46
93
|
|
|
47
|
-
|
|
94
|
+
## Skill
|
|
48
95
|
|
|
49
|
-
|
|
96
|
+
`skills/paintress/SKILL.md` is a short instruction file for agents: when to reach for these tools, how `#1` markers map to node ids, and which tool to use for what. Copy it into your agent's skill directory if it supports one.
|
|
50
97
|
|
|
51
|
-
|
|
98
|
+
## Documentation
|
|
52
99
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
```
|
|
100
|
+
- `specs/PLAN.md` — roadmap and open questions
|
|
101
|
+
- `format/spec.yaml` — the document format spec (the `.pen` profile is frozen; `.ptr` evolves)
|
|
102
|
+
- `.agents/memory/ARCHITECTURE.md` — design decisions and their rationale
|
|
103
|
+
- `.agents/implementations/` — per-change records with measurements
|
|
60
104
|
|
|
61
|
-
|
|
105
|
+
## License
|
|
62
106
|
|
|
63
|
-
|
|
107
|
+
MIT
|
|
64
108
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Installation guide for AI agents
|
|
112
|
+
|
|
113
|
+
You are an agent asked to install paintress on this machine. Work through the steps in order and report what you found, what you reused, and what you changed.
|
|
114
|
+
|
|
115
|
+
**Detection comes first.** Several agents can share one machine. A previous agent may already have installed paintress globally and registered it for itself. Installing a second copy is a bug, not thoroughness.
|
|
72
116
|
|
|
73
|
-
###
|
|
117
|
+
### 0. Detect
|
|
74
118
|
|
|
75
|
-
|
|
119
|
+
| Check | Command | What it tells you |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| Binary on PATH | `command -v paintress` | a usable install already exists |
|
|
122
|
+
| Global npm package | `npm ls -g --depth=0 paintress` | installed globally via npm |
|
|
123
|
+
| Project-local | `npm ls --depth=0 paintress` | installed inside the current project |
|
|
124
|
+
| Client configs | `paintress doctor` | which agents already have an entry, and its state |
|
|
76
125
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
126
|
+
Rules:
|
|
127
|
+
|
|
128
|
+
- **Any hit means reuse.** Do not install again; go to step 2.
|
|
129
|
+
- 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
|
+
- Only when every check is empty do you install (step 1).
|
|
131
|
+
|
|
132
|
+
### 1. Install (only if step 0 found nothing)
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm install -g paintress
|
|
83
136
|
```
|
|
84
137
|
|
|
85
|
-
|
|
138
|
+
If global installs are not permitted in this environment:
|
|
86
139
|
|
|
87
|
-
|
|
140
|
+
```bash
|
|
141
|
+
npm install paintress # project-local; call it through npx paintress ...
|
|
142
|
+
npx -y paintress@latest ... # no install at all; slower on first run
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Prefer the global install. It is what makes the entry in every client config reusable by the next agent.
|
|
146
|
+
|
|
147
|
+
### 2. Register the MCP server for the agent you are running in
|
|
88
148
|
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
149
|
+
```bash
|
|
150
|
+
paintress doctor # list detected clients and their state
|
|
151
|
+
paintress install --dry-run # preview the writes
|
|
152
|
+
paintress install <client-id> # write only the client you are running in
|
|
93
153
|
```
|
|
94
154
|
|
|
95
|
-
|
|
155
|
+
Client ids: `claude`, `cursor`, `vscode`, `codex`, `pi`. Use `paintress install` without arguments to cover every detected client.
|
|
156
|
+
|
|
157
|
+
What the write does:
|
|
158
|
+
|
|
159
|
+
- adds or refreshes one entry named `paintress` (`mcpServers.paintress`, or `servers.paintress` for VS Code, or `[mcp_servers.paintress]` for Codex);
|
|
160
|
+
- leaves every other key, server and comment structure in the file alone;
|
|
161
|
+
- writes `*.bak-<timestamp>` next to the file before replacing it;
|
|
162
|
+
- is idempotent: an identical entry reports `noop` and nothing is written;
|
|
163
|
+
- uses the bare command `paintress` when it resolves on PATH, otherwise the absolute path of the binary.
|
|
164
|
+
|
|
165
|
+
If the agent you run in is not in that list, print the closest snippet and adapt it:
|
|
96
166
|
|
|
97
|
-
|
|
167
|
+
```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
|
|
171
|
+
```
|
|
98
172
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
173
|
+
### 3. Install the skill (recommended)
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
cp "$(npm root -g)/paintress/skills/paintress/SKILL.md" <your-skill-dir>/paintress/SKILL.md
|
|
105
177
|
```
|
|
106
178
|
|
|
107
|
-
|
|
179
|
+
Use the skill directory your harness actually reads (`~/.pi/agent/skills/`, `~/.claude/skills/`, `.cursor/skills/`, …). If your harness has no skill mechanism, load the file into context once and keep the tool list.
|
|
108
180
|
|
|
109
|
-
|
|
181
|
+
### 4. Verify
|
|
110
182
|
|
|
111
183
|
```bash
|
|
112
|
-
paintress
|
|
113
|
-
|
|
184
|
+
paintress doctor # entry present, command resolvable
|
|
185
|
+
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}\n' | paintress mcp
|
|
114
186
|
```
|
|
115
187
|
|
|
116
|
-
|
|
188
|
+
The second command must answer with a `result` containing `"serverInfo": {"name": "paintress", …}`. After a session restart, `paintress_doc_info` on any `.pen` file is the end-to-end check.
|
|
117
189
|
|
|
118
|
-
|
|
190
|
+
### 5. Multi-agent rules
|
|
119
191
|
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
192
|
+
- One global install serves every agent on the machine. Do not create per-agent copies.
|
|
193
|
+
- Each agent adds **its own** config entry. Never rewrite another agent's file, and never remove entries you did not add.
|
|
194
|
+
- If `doctor` reports `noop`, another agent already registered it correctly — stop there and say so.
|
|
195
|
+
- Report the outcome as: reused / installed / registered (which client) / skipped (why).
|
|
124
196
|
|
|
125
|
-
|
|
197
|
+
### 6. Uninstall
|
|
126
198
|
|
|
127
|
-
|
|
199
|
+
```bash
|
|
200
|
+
npm uninstall -g paintress
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Then delete the `paintress` entry from each client config, or restore the newest `*.bak-<timestamp>` next to it.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="assets/icon-512-wordmark.webp" width="200" alt="paintress">
|
|
3
|
+
<p>面向 AI agent 的设计稿操作层</p>
|
|
4
|
+
<p><a href="README.md">English</a></p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
**安装方式:把本仓库地址贴给您的 agent,让它装 paintress。** 需要精确流程的 agent 见[文末的安装手册](#给-ai-agent-的安装手册)。
|
|
8
|
+
|
|
9
|
+
## 这是什么
|
|
10
|
+
|
|
11
|
+
paintress 读写结构化的设计稿——带公开 schema 的 JSON 文件(`.pen`,以及它自己写出的 `.ptr`)。它把设计稿渲染成 HTML,起一个本地查看器,并把同一套操作同时暴露为 MCP 工具和 CLI。
|
|
12
|
+
|
|
13
|
+
它不是编辑器:没有画布、没有拖拽、没有属性面板。人在浏览器里看设计稿并点击节点指认目标,agent 负责读写。
|
|
14
|
+
|
|
15
|
+
| 组成 | 作用 |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| MCP 工具 | 查询、读取、更新、插入、删除、移动、批量、校验、渲染、截图 |
|
|
18
|
+
| CLI | 同样的操作,外加查看器进程;不接 agent 也能用 |
|
|
19
|
+
| 查看器 | 本地 web 服务:渲染设计稿、高亮变更、把点击的节点变成 `#1`、`#2`… |
|
|
20
|
+
| 截图 | 整张画布的 PNG,给能看图的 agent 用 |
|
|
21
|
+
|
|
22
|
+
## 环境要求
|
|
23
|
+
|
|
24
|
+
Node.js 22 及以上。
|
|
25
|
+
|
|
26
|
+
## 安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g paintress
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
然后把 MCP 条目写进您使用的客户端:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
paintress doctor # 查看已装情况、哪些客户端已有条目
|
|
36
|
+
paintress install # 写入所有检测到的客户端配置
|
|
37
|
+
paintress install pi cursor # 或只写指定的客户端
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`install` 只合并 `paintress` 一条,文件里其他内容保持原样,改动前写 `*.bak-<时间戳>` 备份;重复执行不会改变任何东西。
|
|
41
|
+
|
|
42
|
+
## CLI
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
paintress mcp <file>? 启动 stdio MCP server
|
|
46
|
+
paintress doc_info <file> 文档摘要
|
|
47
|
+
paintress query <file> [--type t] [--name n|--regex] 条件查询节点
|
|
48
|
+
paintress get <file> <id> [--depth n] 读取节点子树
|
|
49
|
+
paintress update <file> <id> '<patch-json>' 浅合并更新属性
|
|
50
|
+
paintress insert <file> <parent> '<node-json>' 插入子节点
|
|
51
|
+
paintress delete <file> <id> 删除节点
|
|
52
|
+
paintress move <file> <id> <parent> [--index n] 移动节点
|
|
53
|
+
paintress batch <file> '<ops-json>' 一次执行多个操作
|
|
54
|
+
paintress validate <file> 按格式规格校验
|
|
55
|
+
paintress render <file> <out.html> 渲染为 HTML
|
|
56
|
+
paintress shot <file> <out.png> [--scale n] 渲染为 PNG
|
|
57
|
+
|
|
58
|
+
paintress web [--port n] [--idle ms] [--daemon] 启动查看器
|
|
59
|
+
paintress ping | status | stop 查看器控制
|
|
60
|
+
paintress watch <name> <dir> | unwatch <name> 注册/移除要浏览的目录
|
|
61
|
+
paintress attach <name> agent 工作期间保持查看器存活
|
|
62
|
+
|
|
63
|
+
paintress install [client...] [--dry-run] 写入 MCP 配置条目
|
|
64
|
+
paintress doctor 查看各客户端配置状态
|
|
65
|
+
paintress mcp-config <client> 打印单个客户端的配置片段
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 查看器
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
paintress web --daemon --idle 3600000
|
|
72
|
+
paintress watch demo ./demo
|
|
73
|
+
# 打开 http://localhost:3316/?project=demo&file=hello.ptr
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
画布操作:滚轮以指针为锚点缩放,空格 / 中键 / 空白处拖拽平移,`Ctrl/Cmd+0` 回到 100%,`Shift+1` 适应窗口。多画板文件会显示画板条。通过 CLI 或 MCP 做的修改会在查看器里高亮,点击会让节点获得短号,agent 用 `paintress_marks` 读回。
|
|
77
|
+
|
|
78
|
+
## MCP 客户端
|
|
79
|
+
|
|
80
|
+
`paintress install` 会写入下面这些位置,也可以手动添加。
|
|
81
|
+
|
|
82
|
+
| 客户端 | 配置文件 | 顶层键 |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| Claude Desktop | `~/.config/Claude/claude_desktop_config.json` | `mcpServers` |
|
|
85
|
+
| Cursor | `~/.cursor/mcp.json` | `mcpServers` |
|
|
86
|
+
| VS Code | `~/.config/Code/User/mcp.json` | `servers`(条目需 `"type": "stdio"`) |
|
|
87
|
+
| Codex | `~/.codex/config.toml` | `[mcp_servers.paintress]` |
|
|
88
|
+
| pi | `~/.pi/agent/mcp.json` | `mcpServers` |
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
paintress mcp-config cursor # 只打印片段,不改文件
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Skill
|
|
95
|
+
|
|
96
|
+
`skills/paintress/SKILL.md` 是给 agent 的简短说明:什么时候用这些工具、`#1` 怎么映射到节点 id、哪个工具做什么。支持 skill 机制的客户端把它复制进对应目录即可。
|
|
97
|
+
|
|
98
|
+
## 文档
|
|
99
|
+
|
|
100
|
+
- `specs/PLAN.md` —— 路线与未决问题
|
|
101
|
+
- `format/spec.yaml` —— 文件格式规格(`.pen` 层冻结,`.ptr` 层演进)
|
|
102
|
+
- `.agents/memory/ARCHITECTURE.md` —— 设计决策与理由
|
|
103
|
+
- `.agents/implementations/` —— 每次变更的记录与实测数据
|
|
104
|
+
|
|
105
|
+
## 许可
|
|
106
|
+
|
|
107
|
+
MIT
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 给 AI agent 的安装手册
|
|
112
|
+
|
|
113
|
+
您是被要求在本机安装 paintress 的 agent。请按顺序执行,并汇报:发现了什么、复用了什么、改动了什么。
|
|
114
|
+
|
|
115
|
+
**先检测。** 一台机器上可能有多个 agent,之前的 agent 可能已经全局安装过 paintress 并给自己注册过。再装一份是错误,不是周全。
|
|
116
|
+
|
|
117
|
+
### 0. 检测
|
|
118
|
+
|
|
119
|
+
| 检查项 | 命令 | 含义 |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| PATH 上是否已有 | `command -v paintress` | 已有可用安装 |
|
|
122
|
+
| 是否全局安装 | `npm ls -g --depth=0 paintress` | 通过 npm 装在全局 |
|
|
123
|
+
| 是否项目内安装 | `npm ls --depth=0 paintress` | 装在当前项目里 |
|
|
124
|
+
| 客户端配置 | `paintress doctor` | 哪些 agent 已有条目、状态如何 |
|
|
125
|
+
|
|
126
|
+
规则:
|
|
127
|
+
|
|
128
|
+
- **命中任意一项就复用**,不要重装,直接进入第 2 步。
|
|
129
|
+
- 向用户报告解析到的路径与版本(查看器运行时 `paintress ping` 会打印版本;包版本用 `npm ls -g --depth=0 paintress`)。
|
|
130
|
+
- 只有在全部检查都为空时,才执行第 1 步。
|
|
131
|
+
|
|
132
|
+
### 1. 安装(仅在检测全空时)
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm install -g paintress
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
如果环境不允许全局安装:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
npm install paintress # 项目内安装,之后用 npx paintress ...
|
|
142
|
+
npx -y paintress@latest ... # 完全不安装,首次运行较慢
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
优先全局安装:只有全局安装,下一个 agent 才能直接复用。
|
|
146
|
+
|
|
147
|
+
### 2. 给「当前所在的 agent」注册 MCP
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
paintress doctor # 列出检测到的客户端与状态
|
|
151
|
+
paintress install --dry-run # 预览将要写入的内容
|
|
152
|
+
paintress install <client-id> # 只写当前所在的客户端
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
客户端 id:`claude`、`cursor`、`vscode`、`codex`、`pi`。不带参数执行 `paintress install` 会覆盖所有检测到的客户端。
|
|
156
|
+
|
|
157
|
+
写入行为:
|
|
158
|
+
|
|
159
|
+
- 新增或更新名为 `paintress` 的一条(`mcpServers.paintress`;VS Code 为 `servers.paintress`;Codex 为 `[mcp_servers.paintress]`);
|
|
160
|
+
- 文件里其他键、其他 server、既有排版一律不动;
|
|
161
|
+
- 替换前在同目录写 `*.bak-<时间戳>` 备份;
|
|
162
|
+
- 幂等:条目已一致时返回 `noop`,不写磁盘;
|
|
163
|
+
- 当 `paintress` 能在 PATH 上解析时写裸命令,否则写可执行文件的绝对路径。
|
|
164
|
+
|
|
165
|
+
如果当前 agent 不在列表里,打印最接近的片段再自行适配:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
paintress mcp-config cursor # JSON,顶层 mcpServers
|
|
169
|
+
paintress mcp-config vscode # JSON,顶层 servers + type: stdio
|
|
170
|
+
paintress mcp-config codex # TOML 表
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 3. 安装 skill(建议)
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
cp "$(npm root -g)/paintress/skills/paintress/SKILL.md" <技能目录>/paintress/SKILL.md
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
使用您的 harness 真正读取的技能目录(`~/.pi/agent/skills/`、`~/.claude/skills/`、`.cursor/skills/` 等)。如果 harness 没有 skill 机制,把该文件读入上下文一次,并记住工具清单。
|
|
180
|
+
|
|
181
|
+
### 4. 验证
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
paintress doctor # 条目存在、命令可解析
|
|
185
|
+
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}\n' | paintress mcp
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
第二条命令应当返回包含 `"serverInfo": {"name": "paintress", …}` 的 `result`。会话重启后,对任意 `.pen` 文件调用 `paintress_doc_info` 是一次端到端验证。
|
|
189
|
+
|
|
190
|
+
### 5. 多 agent 规则
|
|
191
|
+
|
|
192
|
+
- 一份全局安装服务本机所有 agent,不要为每个 agent 复制一份。
|
|
193
|
+
- 每个 agent 只添加**自己**的配置条目;不要重写其他 agent 的文件,也不要删除不是自己添加的条目。
|
|
194
|
+
- 如果 `doctor` 显示 `noop`,说明已有人正确注册过——就此停下并说明情况。
|
|
195
|
+
- 汇报格式:复用 / 新装 / 注册(哪个客户端)/ 跳过(原因)。
|
|
196
|
+
|
|
197
|
+
### 6. 卸载
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
npm uninstall -g paintress
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
然后从各客户端配置里删掉 `paintress` 条目,或恢复同目录下最新的 `*.bak-<时间戳>`。
|
|
Binary file
|
|
Binary file
|
package/assets/logo.svg
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
width="540"
|
|
4
|
+
height="942"
|
|
5
|
+
viewBox="0 0 540 942"
|
|
6
|
+
role="img"
|
|
7
|
+
aria-label="paintress"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
sodipodi:docname="logo-v2.svg"
|
|
11
|
+
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
|
12
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
13
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
16
|
+
<defs
|
|
17
|
+
id="defs1" />
|
|
18
|
+
<sodipodi:namedview
|
|
19
|
+
id="namedview1"
|
|
20
|
+
pagecolor="#ffffff"
|
|
21
|
+
bordercolor="#000000"
|
|
22
|
+
borderopacity="0.25"
|
|
23
|
+
inkscape:showpageshadow="2"
|
|
24
|
+
inkscape:pageopacity="0.0"
|
|
25
|
+
inkscape:pagecheckerboard="0"
|
|
26
|
+
inkscape:deskcolor="#d1d1d1"
|
|
27
|
+
inkscape:zoom="0.73036093"
|
|
28
|
+
inkscape:cx="186.2093"
|
|
29
|
+
inkscape:cy="335.45058"
|
|
30
|
+
inkscape:window-width="2048"
|
|
31
|
+
inkscape:window-height="1130"
|
|
32
|
+
inkscape:window-x="2451"
|
|
33
|
+
inkscape:window-y="-6"
|
|
34
|
+
inkscape:window-maximized="1"
|
|
35
|
+
inkscape:current-layer="svg1" />
|
|
36
|
+
<title
|
|
37
|
+
id="title1">paintress</title>
|
|
38
|
+
<path
|
|
39
|
+
d="m 197,705 c -8.26,9.44 -8.26,9.44 -11.5,15 -0.19,0.31 -0.37,0.63 -0.56,0.95 -6.83,11.6 -11.92,23.92 -14.94,37.05 -0.12,0.49 -0.23,0.98 -0.35,1.48 -7.26,31.72 2.27,62.42 16.35,90.52 h 2 c -0.06,-0.52 -0.12,-1.04 -0.18,-1.57 -3.18,-27.37 -1.65,-51.92 15.93,-74.55 7.05,-8.67 15.37,-15.67 25.25,-20.88 -0.29,0.35 -0.58,0.7 -0.88,1.06 -21.52,26.11 -31.93,55.49 -28.92,89.48 1.98,19.12 9.45,35.88 18.8,52.46 h 3 c -0.07,-0.48 -0.14,-0.97 -0.21,-1.47 -0.67,-4.85 -1.03,-9.63 -1.04,-14.53 v -1.09 c 0.07,-25.16 10.74,-46.44 28.25,-63.91 0.34,-0.34 0.67,-0.68 1.02,-1.03 0.66,-0.66 1.32,-1.31 1.98,-1.97 v -2 c 0.29,-0.13 0.29,-0.13 1.77,-0.8 2.31,-1.16 4.03,-2.66 5.88,-4.44 0.34,-0.32 0.68,-0.63 1.02,-0.96 1.1,-1.03 2.18,-2.07 3.27,-3.11 0.76,-0.72 1.52,-1.45 2.27,-2.17 2.27,-2.16 4.53,-4.34 6.79,-6.52 0.17,-0.16 0.17,-0.16 1.01,-0.97 C 286.1,778.4 286.1,778.4 291,771 c -6.34,17.73 -16.66,32.22 -28.15,46.89 -16.46,21.04 -31.06,42.93 -31.1,70.61 v 1.33 c 0.04,16.47 4.61,33.21 15.25,46.17 0.98,0.83 1.87,1.39 3,2 0.02,-0.19 0.02,-0.19 0.15,-1.17 2.45,-18.03 9.3,-33.78 20.85,-47.83 0.36,-0.44 0.71,-0.87 1.08,-1.32 2.51,-3.02 5.18,-5.87 7.92,-8.68 0.27,-0.28 0.55,-0.56 0.83,-0.85 2.23,-2.29 4.48,-4.57 6.73,-6.84 0.16,-0.15 0.16,-0.15 0.94,-0.94 3.39,-3.41 6.84,-6.71 10.48,-9.85 4.1,-3.54 8,-7.26 11.83,-11.08 l 1.04,-1.04 C 331.59,828.6 346.77,804 351,776 c 0.03,-0.2 0.03,-0.2 0.21,-1.24 C 354.78,752.34 348.58,728.23 337,709 l -3,-4 z"
|
|
40
|
+
style="fill:#908030;fill-rule:evenodd;fill-opacity:1"
|
|
41
|
+
id="path1-8" />
|
|
42
|
+
<path
|
|
43
|
+
d="m 278.25,4.75 c -0.54,0.04 -1.07,0.09 -1.62,0.13 -32.98,2.74 -65.41,11.83 -94.6,27.56 -1.09,0.59 -2.18,1.17 -3.27,1.76 -9.58,5.13 -18.92,10.46 -27.76,16.8 -0.33,0.24 -0.67,0.48 -1.01,0.72 -19.41,13.9 -37.87,28.97 -55.51,45.05 -1.41,1.28 -2.83,2.53 -4.26,3.77 -4.6,3.96 -9,8.08 -13.27,12.38 -1.01,1.02 -2.02,2.02 -3.04,3.03 -4.51,4.51 -8.78,9.18 -12.91,14.05 -0.26,0.31 -0.52,0.62 -0.79,0.94 C 51.53,141.19 43.49,151.85 36,163 c -0.36,0.54 -0.73,1.08 -1.1,1.64 C 15.55,193.82 -1.65,230.99 5.25,266.8 9.67,288.42 20.73,307.65 34,325 c 0.34,0.45 0.68,0.91 1.04,1.37 4.36,5.74 9.12,11.09 14.03,16.37 2.45,2.63 4.83,5.29 7.12,8.07 2.24,2.69 4.71,4.97 7.38,7.23 2.11,1.79 4.14,3.62 6.13,5.54 13.36,12.86 28.46,23.82 44.3,33.42 0.58,0.35 1.15,0.7 1.73,1.05 5.19,3.16 10.41,6.27 15.66,9.35 28.21,16.59 53.61,34.5 62.54,67.99 1.54,6.13 2.75,12.29 3.07,18.61 0.03,0.46 0.05,0.93 0.08,1.4 0.68,13.34 -1.85,26.5 -4.08,39.6 1.85,-0.43 2.8,-0.76 4.04,-2.24 0.31,-0.52 0.62,-1.04 0.94,-1.58 0.17,-0.28 0.35,-0.57 0.53,-0.87 0.57,-0.95 1.12,-1.91 1.68,-2.87 0.39,-0.68 0.79,-1.35 1.19,-2.03 4.24,-7.28 8.03,-14.78 11.62,-22.41 0.14,-0.3 0.14,-0.3 0.85,-1.79 4.41,-9.46 7.6,-19.11 10.15,-29.21 0.08,-0.33 0.17,-0.66 0.26,-0.99 6.3,-24.54 5.51,-55.54 -7.26,-78.01 -4.49,-7.28 -11.39,-15.32 -19.92,-17.75 -6.41,-1.5 -12.87,-1.49 -19.36,-0.55 -0.6,0.09 -1.19,0.18 -1.8,0.26 -7.7,1.21 -15.19,3.27 -22.7,5.29 -3.09,0.83 -6.18,1.65 -9.27,2.46 -1.06,0.28 -2.12,0.57 -3.18,0.86 -10.28,2.74 -20.91,4.34 -30.77,-0.57 -3.76,-2.39 -4.96,-5.81 -6,-10 -0.28,-2.27 -0.36,-4.53 -0.44,-6.81 -0.4,-7.22 -1.09,-13.77 -6.18,-19.19 -1.931839,-2.97898 -2.97,-5.84 -3.21,-9.89 -0.561751,-9.12845 -0.405349,-4.8236 -3.1,-23.27 -0.12,-3.08 -1.22,-5.4 -3.07,-7.84 -2.19,-1.66 -4.54,-2.86 -7,-4.06 -7.85,-3.87 -7.85,-3.87 -9.62,-7.53 -0.89,-3.3 -0.51,-6.41 1.16,-9.37 1.41,-2.15 3,-4.12 4.64,-6.09 1.51,-1.84 2.98,-3.71 4.44,-5.59 0.3,-0.37 0.59,-0.74 0.89,-1.12 8.66,-11.08 15.69,-23.33 15.68,-37.68 v -1.85 c -0.05,-4.27 -0.5,-8.37 -1.19,-12.59 -4.35,-27.27 3.54,-56.47 19.58,-78.85 2.05,-2.82 4.2,-5.57 6.42,-8.27 0.03,0.22 0.03,0.22 0.2,1.32 4.32,28.44 16.11,51.38 36.06,71.91 0.76,0.79 1.51,1.58 2.27,2.38 7.9,8.31 16.26,15.56 25.47,22.39 0.37,0.27 0.74,0.55 1.11,0.83 4.85,3.6 9.71,7.18 14.58,10.74 13.79,10.11 26.89,20.68 39.31,32.43 0.37,0.34 0.73,0.69 1.11,1.04 5.52,5.18 10.26,10.99 14.89,16.96 0.39,0.5 0.78,1.01 1.18,1.53 22.65,29.58 36.75,65.17 38.82,102.47 0.02,0.28 0.02,0.28 0.1,1.68 2.35,45.29 -22.98,81.38 -49.23,115.57 -4.25,5.54 -8.44,11.12 -12.61,16.72 -1.3,1.74 -2.6,3.49 -3.91,5.22 -33.66,44.8 -33.66,44.8 -42.35,70.81 -0.18,0.52 -0.35,1.04 -0.53,1.57 -3.43,10.53 -2.77,21.5 -2.47,32.43 l 1,1 h 155 c 2.23,-6.7 2.23,-6.7 2.79,-10.04 0.13,-0.74 0.27,-1.48 0.4,-2.23 0.07,-0.38 0.14,-0.76 0.21,-1.16 4.16,-22.24 14.72,-40.98 28.6,-58.57 0.68,-0.87 1.35,-1.74 2.03,-2.61 4.41,-5.64 9.11,-10.97 13.94,-16.25 2.37,-2.61 4.68,-5.27 6.99,-7.94 0.97,-1.12 1.95,-2.22 2.93,-3.33 C 422.55,515.4 441.2,488.4 451,457 c 0.17,-0.54 0.34,-1.07 0.51,-1.62 8.54,-27.92 4.9,-58.56 -8.05,-84.44 -0.46,-0.94 -0.91,-1.88 -1.36,-2.82 -11.64,-24.3 -31.52,-46.39 -52.62,-62.94 -1.27,-1.01 -2.52,-2.05 -3.76,-3.1 -16.39,-13.88 -35.02,-25.85 -53.67,-36.48 -1.17,-0.67 -2.35,-1.35 -3.53,-2.03 -11.51,-6.64 -23.14,-13.02 -34.95,-19.11 -7.64,-3.95 -15.19,-8.03 -22.73,-12.18 -4.03,-2.21 -8.06,-4.42 -12.15,-6.51 C 234.47,213.38 212.24,198.14 192,180 c -0.36,-0.32 -0.73,-0.64 -1.1,-0.98 -12.8,-11.46 -22.69,-27.28 -30.34,-42.52 -0.17,-0.33 -0.33,-0.67 -0.51,-1.01 -2.03,-4.06 -3.75,-8.13 -5.05,-12.49 0.26,0.37 0.51,0.73 0.78,1.11 27.73,39.61 64.59,62.16 136.67,91.89 32.87,13.2 65.64,27.22 94.55,48 0.34,0.25 0.69,0.49 1.04,0.74 18.15,13 35.43,27.37 48.96,45.26 0.22,0.29 0.45,0.59 0.67,0.89 25.44,33.61 39.25,71.89 33.72,114.45 -0.57,3.94 -1.42,7.8 -2.39,11.66 -0.1,0.39 -0.19,0.78 -0.29,1.18 -3.25,12.86 -7.87,24.93 -13.71,36.82 -0.16,0.32 -0.32,0.65 -0.48,0.98 -11.57,23.61 -27.36,44.24 -44.52,64.02 5.28,0 9.9,-6.86 13.59,-10.54 l 4.92,-4.89 c 13.59,-13.51 13.59,-13.51 18.83,-19.78 1.05,-1.25 2.14,-2.48 3.22,-3.71 33.2,-38.29 57.35,-83.87 82.75,-188.96 0.04,-0.3 0.04,-0.3 0.25,-1.85 1.92,-14.6 2.68,-29.12 2.69,-43.83 v -1.21 C 536.28,216.18 525.54,167.08 481,96 474.35027,82.502457 457.163,66.047388 442.4,54.389598 429.97756,44.580059 423.21007,38.671449 403.0602,29.91592 382.91033,21.160391 355.82,11.19 331,7 330.62,6.94 330.24,6.87 329.84,6.8 312.74,3.95 295.51,3.36 278.25,4.75 Z M 190,655 c 1.15,10.37 1.15,10.37 2.16,15.5 0.1,0.56 0.21,1.12 0.32,1.69 0.22,1.16 0.45,2.33 0.68,3.49 l 1.02,5.34 c 0.22,1.14 0.44,2.28 0.66,3.41 0.1,0.53 0.21,1.06 0.31,1.61 0.05,0.25 0.05,0.25 0.29,1.49 0.09,0.43 0.17,0.86 0.26,1.3 C 196,690 196,690 197,692 h 137 c 2.56,-12.29 5.09,-24.59 7,-37 z"
|
|
44
|
+
style="fill:#205080;fill-opacity:1;fill-rule:evenodd"
|
|
45
|
+
id="path1"
|
|
46
|
+
sodipodi:nodetypes="ccccccccccccccccccccccccccccccsccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccszcccccccccccccccc" />
|
|
47
|
+
</svg>
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
+
import { Wt as VERSION, t as renderDocument, w as loadDoc } from "./render-CbYe9rII.js";
|
|
4
4
|
import "./index.js";
|
|
5
5
|
import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
@@ -272,7 +272,6 @@ var WatchManager = class {
|
|
|
272
272
|
}
|
|
273
273
|
};
|
|
274
274
|
// src/web/daemon.ts
|
|
275
|
-
const VERSION = "0.1.0";
|
|
276
275
|
const DEFAULT_PORT = 3316;
|
|
277
276
|
const MIME = {
|
|
278
277
|
".html": "text/html; charset=utf-8",
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Nt as Affine, tt as BackgroundBlurEffect, _t as BlendMode, nt as BlurEffect, Pt as BoolOrVar, L as BrowserSchema, Ft as Color, It as ColorOrVar, Lt as CornerRadius, vt as CrossAxis, A as DocumentObjectSchema, j as DocumentSchema, rt as EffectEntry, it as Effects, R as EllipseSchema, C as FileChangedError, at as FillEntry, yt as FillRule, ot as Fills, z as FrameSchema, st as GradientFill, bt as GradientType, ct as GraphicsBlock, B as GroupSchema, xt as IconLibrary, V as IconSchema, lt as ImageFill, St as ImageFit, H as KNOWN_NODE_TYPES, U as LayoutBlock, Ct as LayoutDir, wt as MainAxis, ut as MeshGradientFill, W as NodeBase, G as NodeSchema, K as NoteSchema, Rt as NumOrVar, M as PTR_SPEC_VERSION, zt as Padding, q as PathSchema, J as PolygonSchema, Tt as PositionMode, N as PtrDocumentSchema, Y as RectangleSchema, X as RefSchema, Z as ScriptSchema, dt as ShaderEffect, ft as ShaderFill, pt as ShadowEffect, Et as ShadowKind, Bt as Sizing, mt as SolidFill, Vt as StringOrVar, Dt as StrokeAlign, ht as StrokeBlock, Ot as StrokeCap, kt as StrokeJoin, gt as StrokeWidth, At as TextAlign, jt as TextAlignVertical, Mt as TextGrowth, Q as TextSchema, $ as UnknownNodeSchema, Ht as VARIABLE_NAME, P as VariableDef, Ut as VariableRef, F as XPaintress, I 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, et as schemaForType, E as serializeDoc, d as strokeToCss, f as textToCss, S as update, D as validateDoc } from "./render-
|
|
1
|
+
export { Nt as Affine, tt as BackgroundBlurEffect, _t as BlendMode, nt as BlurEffect, Pt as BoolOrVar, L as BrowserSchema, Ft as Color, It as ColorOrVar, Lt as CornerRadius, vt as CrossAxis, A as DocumentObjectSchema, j as DocumentSchema, rt as EffectEntry, it as Effects, R as EllipseSchema, C as FileChangedError, at as FillEntry, yt as FillRule, ot as Fills, z as FrameSchema, st as GradientFill, bt as GradientType, ct as GraphicsBlock, B as GroupSchema, xt as IconLibrary, V as IconSchema, lt as ImageFill, St as ImageFit, H as KNOWN_NODE_TYPES, U as LayoutBlock, Ct as LayoutDir, wt as MainAxis, ut as MeshGradientFill, W as NodeBase, G as NodeSchema, K as NoteSchema, Rt as NumOrVar, M as PTR_SPEC_VERSION, zt as Padding, q as PathSchema, J as PolygonSchema, Tt as PositionMode, N as PtrDocumentSchema, Y as RectangleSchema, X as RefSchema, Z as ScriptSchema, dt as ShaderEffect, ft as ShaderFill, pt as ShadowEffect, Et as ShadowKind, Bt as Sizing, mt as SolidFill, Vt as StringOrVar, Dt as StrokeAlign, ht as StrokeBlock, Ot as StrokeCap, kt as StrokeJoin, gt as StrokeWidth, At as TextAlign, jt as TextAlignVertical, Mt as TextGrowth, Q as TextSchema, $ as UnknownNodeSchema, Ht as VARIABLE_NAME, P as VariableDef, Ut as VariableRef, F as XPaintress, I 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, et as schemaForType, E as serializeDoc, d as strokeToCss, f as textToCss, S as update, D as validateDoc } from "./render-CbYe9rII.js";
|
package/dist/mcp/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as createMcpServer, n as runStdioServer } from "../server-
|
|
1
|
+
export { t as createMcpServer, n as runStdioServer } from "../server-C4TC8Bw3.js";
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { copyFileSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { basename, dirname, join } from "node:path";
|
|
6
|
+
// src/shared/version.ts
|
|
7
|
+
/**
|
|
8
|
+
* 包版本:运行时从 package.json 读取,避免在多处硬编码后忘记同步。
|
|
9
|
+
*
|
|
10
|
+
* 路径要覆盖两种布局:
|
|
11
|
+
* - 构建产物 `dist/cli.js`、`dist/index.js` → `../package.json`
|
|
12
|
+
* - 构建产物 `dist/mcp/server.js` 与源码 `src/shared/version.ts` → `../../package.json`
|
|
13
|
+
*/
|
|
14
|
+
function readPackageVersion() {
|
|
15
|
+
for (const rel of ["../package.json", "../../package.json"]) try {
|
|
16
|
+
const raw = readFileSync(fileURLToPath(new URL(rel, import.meta.url)), "utf8");
|
|
17
|
+
const version = JSON.parse(raw).version;
|
|
18
|
+
if (typeof version === "string" && version !== "") return version;
|
|
19
|
+
} catch {}
|
|
20
|
+
return "0.0.0";
|
|
21
|
+
}
|
|
22
|
+
const VERSION = readPackageVersion();
|
|
5
23
|
// src/schema/scalars.ts
|
|
6
24
|
/** 规格来源:format/spec.yaml §scalars(线键名,v0.2.0) */
|
|
7
25
|
/** 变量名形如 $accent、$gap-md(必须字母或下划线开头,避免把 $99 这类价格文本当成变量) */
|
|
@@ -628,7 +646,7 @@ function validateDoc(input) {
|
|
|
628
646
|
};
|
|
629
647
|
}
|
|
630
648
|
// src/doc/io.ts
|
|
631
|
-
const GENERATOR =
|
|
649
|
+
const GENERATOR = `paintress/${VERSION}`;
|
|
632
650
|
var FileChangedError = class extends Error {
|
|
633
651
|
path;
|
|
634
652
|
constructor(path) {
|
|
@@ -1592,4 +1610,4 @@ function renderDocument(doc, opts = {}) {
|
|
|
1592
1610
|
canvas
|
|
1593
1611
|
};
|
|
1594
1612
|
}
|
|
1595
|
-
export { UnknownNodeSchema as $, DocumentObjectSchema as A, TextAlign as At, GroupSchema as B, Sizing as Bt, FileChangedError as C, LayoutDir as Ct, validateDoc as D, StrokeAlign as Dt, serializeDoc as E, ShadowKind as Et, XPaintress as F, Color as Ft, NodeSchema as G, KNOWN_NODE_TYPES as H, VARIABLE_NAME as Ht, X_PAINTRESS_KEY as I, ColorOrVar as It, PolygonSchema as J, NoteSchema as K, BrowserSchema as L, CornerRadius as Lt, PTR_SPEC_VERSION as M, TextGrowth as Mt, PtrDocumentSchema as N, Affine as Nt, collectIds as O, StrokeCap as Ot, VariableDef as P, BoolOrVar as Pt, TextSchema as Q, EllipseSchema as R, NumOrVar as Rt, update as S, ImageFit as St, saveDoc as T, PositionMode as Tt, LayoutBlock as U, VariableRef as Ut, IconSchema as V, StringOrVar as Vt, NodeBase as W, RefSchema as X, RectangleSchema as Y, ScriptSchema as Z, get as _, BlendMode as _t, renderTree as a, FillEntry as at, query as b, GradientType as bt, cornerRadiusToCss as c, GraphicsBlock as ct, strokeToCss as d, ShaderEffect as dt, schemaForType as et, textToCss as f, ShaderFill as ft, buildIndex as g, StrokeWidth as gt, batch as h, StrokeBlock as ht, escapeHtml as i, Effects as it, DocumentSchema as j, TextAlignVertical as jt, genId as k, StrokeJoin as kt, effectsToCss as l, ImageFill as lt, makeVarResolver as m, SolidFill as mt, computeCanvas as n, BlurEffect as nt, getLucideIcon as o, Fills as ot, expandRefs as p, ShadowEffect as pt, PathSchema as q, escapeAttr as r, EffectEntry as rt, commonToCss as s, GradientFill as st, renderDocument as t, BackgroundBlurEffect as tt, fillsToCss as u, MeshGradientFill as ut, insert as v, CrossAxis as vt, loadDoc as w, MainAxis as wt, remove as x, IconLibrary as xt, move as y, FillRule as yt, FrameSchema as z, Padding as zt };
|
|
1613
|
+
export { UnknownNodeSchema as $, DocumentObjectSchema as A, TextAlign as At, GroupSchema as B, Sizing as Bt, FileChangedError as C, LayoutDir as Ct, validateDoc as D, StrokeAlign as Dt, serializeDoc as E, ShadowKind as Et, XPaintress as F, Color as Ft, NodeSchema as G, KNOWN_NODE_TYPES as H, VARIABLE_NAME as Ht, X_PAINTRESS_KEY as I, ColorOrVar as It, PolygonSchema as J, NoteSchema as K, BrowserSchema as L, CornerRadius as Lt, PTR_SPEC_VERSION as M, TextGrowth as Mt, PtrDocumentSchema as N, Affine as Nt, collectIds as O, StrokeCap as Ot, VariableDef as P, BoolOrVar as Pt, TextSchema as Q, EllipseSchema as R, NumOrVar as Rt, update as S, ImageFit as St, saveDoc as T, PositionMode as Tt, LayoutBlock as U, VariableRef as Ut, IconSchema as V, StringOrVar as Vt, NodeBase as W, VERSION as Wt, RefSchema as X, RectangleSchema as Y, ScriptSchema as Z, get as _, BlendMode as _t, renderTree as a, FillEntry as at, query as b, GradientType as bt, cornerRadiusToCss as c, GraphicsBlock as ct, strokeToCss as d, ShaderEffect as dt, schemaForType as et, textToCss as f, ShaderFill as ft, buildIndex as g, StrokeWidth as gt, batch as h, StrokeBlock as ht, escapeHtml as i, Effects as it, DocumentSchema as j, TextAlignVertical as jt, genId as k, StrokeJoin as kt, effectsToCss as l, ImageFill as lt, makeVarResolver as m, SolidFill as mt, computeCanvas as n, BlurEffect as nt, getLucideIcon as o, Fills as ot, expandRefs as p, ShadowEffect as pt, PathSchema as q, escapeAttr as r, EffectEntry as rt, commonToCss as s, GradientFill as st, renderDocument as t, BackgroundBlurEffect as tt, fillsToCss as u, MeshGradientFill as ut, insert as v, CrossAxis as vt, loadDoc as w, MainAxis as wt, remove as x, IconLibrary as xt, move as y, FillRule as yt, FrameSchema as z, Padding as zt };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as validateDoc, O as collectIds, S as update, T as saveDoc, _ as get, b as query, h as batch, t as renderDocument, v as insert, w as loadDoc, x as remove, y as move } from "./render-
|
|
1
|
+
import { D as validateDoc, O as collectIds, S as update, T as saveDoc, Wt as VERSION, _ as get, b as query, h as batch, t as renderDocument, v as insert, w as loadDoc, x as remove, y as move } from "./render-CbYe9rII.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";
|
|
@@ -634,7 +634,7 @@ async function autoRegister(cwd = process.cwd()) {
|
|
|
634
634
|
}
|
|
635
635
|
// src/mcp/server.ts
|
|
636
636
|
const SERVER_NAME = "paintress";
|
|
637
|
-
const SERVER_VERSION =
|
|
637
|
+
const SERVER_VERSION = VERSION;
|
|
638
638
|
/** 构建 MCP server:把共享工具注册到 stdio 协议上 */
|
|
639
639
|
function createMcpServer() {
|
|
640
640
|
const server = new McpServer({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paintress",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Design-document operation layer for AI agents: read, query, edit, render",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
|
-
"ui/dist"
|
|
29
|
+
"ui/dist",
|
|
30
|
+
"skills",
|
|
31
|
+
"assets"
|
|
30
32
|
],
|
|
31
33
|
"scripts": {
|
|
32
34
|
"build": "tsdown && pnpm --dir ui build",
|
|
@@ -52,5 +54,13 @@
|
|
|
52
54
|
"tsdown": "^0.12.0",
|
|
53
55
|
"typescript": "^5.7.0",
|
|
54
56
|
"vitest": "^3.0.0"
|
|
55
|
-
}
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"mcp",
|
|
60
|
+
"model-context-protocol",
|
|
61
|
+
"ai-agents",
|
|
62
|
+
"design",
|
|
63
|
+
"renderer",
|
|
64
|
+
"cli"
|
|
65
|
+
]
|
|
56
66
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: paintress
|
|
3
|
+
description: Read, query, edit, render and screenshot .pen/.ptr design documents through MCP tools, and drive the local viewer. Use when the user refers to a design file, a node in it, a board screenshot, or shorthand ids like #1.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# paintress
|
|
7
|
+
|
|
8
|
+
paintress is a design-document operation layer: JSON design files (`.pen`, and paintress's own `.ptr`) in, MCP tools and a CLI out. It does not draw anything itself — it reads and writes the file, and renders it to HTML for a browser.
|
|
9
|
+
|
|
10
|
+
## Check before installing
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
command -v paintress # already on PATH?
|
|
14
|
+
npm ls -g --depth=0 paintress # installed globally?
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
If either prints something, **do not install again**. Use the existing binary. `paintress doctor` shows which agents already have the MCP entry and whether it is current.
|
|
18
|
+
|
|
19
|
+
## Tools
|
|
20
|
+
|
|
21
|
+
| Tool | What it does |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `paintress_doc_info` | document summary: version, format flavor, node counts, variables, top-level nodes |
|
|
24
|
+
| `paintress_query` | find nodes by type / name (exact or regex) / id / reusable |
|
|
25
|
+
| `paintress_get` | fetch one node's subtree; `depth` controls how far down |
|
|
26
|
+
| `paintress_update` | shallow-merge attributes on one node (`id` and `type` are immutable) |
|
|
27
|
+
| `paintress_insert` | insert a child node; returns the generated `id` |
|
|
28
|
+
| `paintress_delete` | remove a node |
|
|
29
|
+
| `paintress_move` | reparent a node, optional index |
|
|
30
|
+
| `paintress_batch` | several operations in one call |
|
|
31
|
+
| `paintress_validate` | check the document against the format spec |
|
|
32
|
+
| `paintress_render` | write the canvas as a standalone HTML file |
|
|
33
|
+
| `paintress_screenshot` | render to PNG (headless Chromium) |
|
|
34
|
+
| `paintress_marks` | read the user's click markers (`#1 → node id`) |
|
|
35
|
+
|
|
36
|
+
## Working rules
|
|
37
|
+
|
|
38
|
+
1. **Query before you edit.** Node ids come from the file; never invent them.
|
|
39
|
+
2. **`#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.
|
|
40
|
+
3. **Top-level nodes are boards.** Use `paintress_doc_info` to list them before guessing what "the second screen" means.
|
|
41
|
+
4. **After a batch, validate.** `paintress_validate` catches structural mistakes before the user opens the file.
|
|
42
|
+
5. **Screenshots are for you.** `paintress_screenshot` is how you look at the design; the HTML viewer is for the human.
|
|
43
|
+
|
|
44
|
+
## Viewer
|
|
45
|
+
|
|
46
|
+
The human-facing viewer is a separate process:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
paintress web --daemon # http://localhost:3316
|
|
50
|
+
paintress watch <name> <dir> # register a directory
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Ask the user to open the URL and click the nodes they mean; then read `paintress_marks` to learn which ids those clicks became.
|
|
@@ -1,6 +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=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><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 <name> <dir>`),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 #2563eb !important;outline-offset:-2px;}
|
|
2
|
-
[data-ptr-hover]:not([data-ptr-selected]){outline:1px dashed
|
|
3
|
-
[data-ptr-changed]{box-shadow:inset 0 0 0 2px #
|
|
4
|
-
@keyframes ptr-pulse{0%,100%{box-shadow:inset 0 0 0 2px #
|
|
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 <name> <dir>`),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
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;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);
|
|
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);
|
|
@@ -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;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(--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:#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}.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:22px;height:22px}.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}
|
package/ui/dist/index.html
CHANGED
|
@@ -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-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-CMZ78plm.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-lyiZ9W-B.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/ui/dist/logo.svg
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
width="540"
|
|
4
|
+
height="942"
|
|
5
|
+
viewBox="0 0 540 942"
|
|
6
|
+
role="img"
|
|
7
|
+
aria-label="paintress"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg1"
|
|
10
|
+
sodipodi:docname="logo-v2.svg"
|
|
11
|
+
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
|
12
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
13
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
16
|
+
<defs
|
|
17
|
+
id="defs1" />
|
|
18
|
+
<sodipodi:namedview
|
|
19
|
+
id="namedview1"
|
|
20
|
+
pagecolor="#ffffff"
|
|
21
|
+
bordercolor="#000000"
|
|
22
|
+
borderopacity="0.25"
|
|
23
|
+
inkscape:showpageshadow="2"
|
|
24
|
+
inkscape:pageopacity="0.0"
|
|
25
|
+
inkscape:pagecheckerboard="0"
|
|
26
|
+
inkscape:deskcolor="#d1d1d1"
|
|
27
|
+
inkscape:zoom="0.73036093"
|
|
28
|
+
inkscape:cx="186.2093"
|
|
29
|
+
inkscape:cy="335.45058"
|
|
30
|
+
inkscape:window-width="2048"
|
|
31
|
+
inkscape:window-height="1130"
|
|
32
|
+
inkscape:window-x="2451"
|
|
33
|
+
inkscape:window-y="-6"
|
|
34
|
+
inkscape:window-maximized="1"
|
|
35
|
+
inkscape:current-layer="svg1" />
|
|
36
|
+
<title
|
|
37
|
+
id="title1">paintress</title>
|
|
38
|
+
<path
|
|
39
|
+
d="m 197,705 c -8.26,9.44 -8.26,9.44 -11.5,15 -0.19,0.31 -0.37,0.63 -0.56,0.95 -6.83,11.6 -11.92,23.92 -14.94,37.05 -0.12,0.49 -0.23,0.98 -0.35,1.48 -7.26,31.72 2.27,62.42 16.35,90.52 h 2 c -0.06,-0.52 -0.12,-1.04 -0.18,-1.57 -3.18,-27.37 -1.65,-51.92 15.93,-74.55 7.05,-8.67 15.37,-15.67 25.25,-20.88 -0.29,0.35 -0.58,0.7 -0.88,1.06 -21.52,26.11 -31.93,55.49 -28.92,89.48 1.98,19.12 9.45,35.88 18.8,52.46 h 3 c -0.07,-0.48 -0.14,-0.97 -0.21,-1.47 -0.67,-4.85 -1.03,-9.63 -1.04,-14.53 v -1.09 c 0.07,-25.16 10.74,-46.44 28.25,-63.91 0.34,-0.34 0.67,-0.68 1.02,-1.03 0.66,-0.66 1.32,-1.31 1.98,-1.97 v -2 c 0.29,-0.13 0.29,-0.13 1.77,-0.8 2.31,-1.16 4.03,-2.66 5.88,-4.44 0.34,-0.32 0.68,-0.63 1.02,-0.96 1.1,-1.03 2.18,-2.07 3.27,-3.11 0.76,-0.72 1.52,-1.45 2.27,-2.17 2.27,-2.16 4.53,-4.34 6.79,-6.52 0.17,-0.16 0.17,-0.16 1.01,-0.97 C 286.1,778.4 286.1,778.4 291,771 c -6.34,17.73 -16.66,32.22 -28.15,46.89 -16.46,21.04 -31.06,42.93 -31.1,70.61 v 1.33 c 0.04,16.47 4.61,33.21 15.25,46.17 0.98,0.83 1.87,1.39 3,2 0.02,-0.19 0.02,-0.19 0.15,-1.17 2.45,-18.03 9.3,-33.78 20.85,-47.83 0.36,-0.44 0.71,-0.87 1.08,-1.32 2.51,-3.02 5.18,-5.87 7.92,-8.68 0.27,-0.28 0.55,-0.56 0.83,-0.85 2.23,-2.29 4.48,-4.57 6.73,-6.84 0.16,-0.15 0.16,-0.15 0.94,-0.94 3.39,-3.41 6.84,-6.71 10.48,-9.85 4.1,-3.54 8,-7.26 11.83,-11.08 l 1.04,-1.04 C 331.59,828.6 346.77,804 351,776 c 0.03,-0.2 0.03,-0.2 0.21,-1.24 C 354.78,752.34 348.58,728.23 337,709 l -3,-4 z"
|
|
40
|
+
style="fill:#908030;fill-rule:evenodd;fill-opacity:1"
|
|
41
|
+
id="path1-8" />
|
|
42
|
+
<path
|
|
43
|
+
d="m 278.25,4.75 c -0.54,0.04 -1.07,0.09 -1.62,0.13 -32.98,2.74 -65.41,11.83 -94.6,27.56 -1.09,0.59 -2.18,1.17 -3.27,1.76 -9.58,5.13 -18.92,10.46 -27.76,16.8 -0.33,0.24 -0.67,0.48 -1.01,0.72 -19.41,13.9 -37.87,28.97 -55.51,45.05 -1.41,1.28 -2.83,2.53 -4.26,3.77 -4.6,3.96 -9,8.08 -13.27,12.38 -1.01,1.02 -2.02,2.02 -3.04,3.03 -4.51,4.51 -8.78,9.18 -12.91,14.05 -0.26,0.31 -0.52,0.62 -0.79,0.94 C 51.53,141.19 43.49,151.85 36,163 c -0.36,0.54 -0.73,1.08 -1.1,1.64 C 15.55,193.82 -1.65,230.99 5.25,266.8 9.67,288.42 20.73,307.65 34,325 c 0.34,0.45 0.68,0.91 1.04,1.37 4.36,5.74 9.12,11.09 14.03,16.37 2.45,2.63 4.83,5.29 7.12,8.07 2.24,2.69 4.71,4.97 7.38,7.23 2.11,1.79 4.14,3.62 6.13,5.54 13.36,12.86 28.46,23.82 44.3,33.42 0.58,0.35 1.15,0.7 1.73,1.05 5.19,3.16 10.41,6.27 15.66,9.35 28.21,16.59 53.61,34.5 62.54,67.99 1.54,6.13 2.75,12.29 3.07,18.61 0.03,0.46 0.05,0.93 0.08,1.4 0.68,13.34 -1.85,26.5 -4.08,39.6 1.85,-0.43 2.8,-0.76 4.04,-2.24 0.31,-0.52 0.62,-1.04 0.94,-1.58 0.17,-0.28 0.35,-0.57 0.53,-0.87 0.57,-0.95 1.12,-1.91 1.68,-2.87 0.39,-0.68 0.79,-1.35 1.19,-2.03 4.24,-7.28 8.03,-14.78 11.62,-22.41 0.14,-0.3 0.14,-0.3 0.85,-1.79 4.41,-9.46 7.6,-19.11 10.15,-29.21 0.08,-0.33 0.17,-0.66 0.26,-0.99 6.3,-24.54 5.51,-55.54 -7.26,-78.01 -4.49,-7.28 -11.39,-15.32 -19.92,-17.75 -6.41,-1.5 -12.87,-1.49 -19.36,-0.55 -0.6,0.09 -1.19,0.18 -1.8,0.26 -7.7,1.21 -15.19,3.27 -22.7,5.29 -3.09,0.83 -6.18,1.65 -9.27,2.46 -1.06,0.28 -2.12,0.57 -3.18,0.86 -10.28,2.74 -20.91,4.34 -30.77,-0.57 -3.76,-2.39 -4.96,-5.81 -6,-10 -0.28,-2.27 -0.36,-4.53 -0.44,-6.81 -0.4,-7.22 -1.09,-13.77 -6.18,-19.19 -1.931839,-2.97898 -2.97,-5.84 -3.21,-9.89 -0.561751,-9.12845 -0.405349,-4.8236 -3.1,-23.27 -0.12,-3.08 -1.22,-5.4 -3.07,-7.84 -2.19,-1.66 -4.54,-2.86 -7,-4.06 -7.85,-3.87 -7.85,-3.87 -9.62,-7.53 -0.89,-3.3 -0.51,-6.41 1.16,-9.37 1.41,-2.15 3,-4.12 4.64,-6.09 1.51,-1.84 2.98,-3.71 4.44,-5.59 0.3,-0.37 0.59,-0.74 0.89,-1.12 8.66,-11.08 15.69,-23.33 15.68,-37.68 v -1.85 c -0.05,-4.27 -0.5,-8.37 -1.19,-12.59 -4.35,-27.27 3.54,-56.47 19.58,-78.85 2.05,-2.82 4.2,-5.57 6.42,-8.27 0.03,0.22 0.03,0.22 0.2,1.32 4.32,28.44 16.11,51.38 36.06,71.91 0.76,0.79 1.51,1.58 2.27,2.38 7.9,8.31 16.26,15.56 25.47,22.39 0.37,0.27 0.74,0.55 1.11,0.83 4.85,3.6 9.71,7.18 14.58,10.74 13.79,10.11 26.89,20.68 39.31,32.43 0.37,0.34 0.73,0.69 1.11,1.04 5.52,5.18 10.26,10.99 14.89,16.96 0.39,0.5 0.78,1.01 1.18,1.53 22.65,29.58 36.75,65.17 38.82,102.47 0.02,0.28 0.02,0.28 0.1,1.68 2.35,45.29 -22.98,81.38 -49.23,115.57 -4.25,5.54 -8.44,11.12 -12.61,16.72 -1.3,1.74 -2.6,3.49 -3.91,5.22 -33.66,44.8 -33.66,44.8 -42.35,70.81 -0.18,0.52 -0.35,1.04 -0.53,1.57 -3.43,10.53 -2.77,21.5 -2.47,32.43 l 1,1 h 155 c 2.23,-6.7 2.23,-6.7 2.79,-10.04 0.13,-0.74 0.27,-1.48 0.4,-2.23 0.07,-0.38 0.14,-0.76 0.21,-1.16 4.16,-22.24 14.72,-40.98 28.6,-58.57 0.68,-0.87 1.35,-1.74 2.03,-2.61 4.41,-5.64 9.11,-10.97 13.94,-16.25 2.37,-2.61 4.68,-5.27 6.99,-7.94 0.97,-1.12 1.95,-2.22 2.93,-3.33 C 422.55,515.4 441.2,488.4 451,457 c 0.17,-0.54 0.34,-1.07 0.51,-1.62 8.54,-27.92 4.9,-58.56 -8.05,-84.44 -0.46,-0.94 -0.91,-1.88 -1.36,-2.82 -11.64,-24.3 -31.52,-46.39 -52.62,-62.94 -1.27,-1.01 -2.52,-2.05 -3.76,-3.1 -16.39,-13.88 -35.02,-25.85 -53.67,-36.48 -1.17,-0.67 -2.35,-1.35 -3.53,-2.03 -11.51,-6.64 -23.14,-13.02 -34.95,-19.11 -7.64,-3.95 -15.19,-8.03 -22.73,-12.18 -4.03,-2.21 -8.06,-4.42 -12.15,-6.51 C 234.47,213.38 212.24,198.14 192,180 c -0.36,-0.32 -0.73,-0.64 -1.1,-0.98 -12.8,-11.46 -22.69,-27.28 -30.34,-42.52 -0.17,-0.33 -0.33,-0.67 -0.51,-1.01 -2.03,-4.06 -3.75,-8.13 -5.05,-12.49 0.26,0.37 0.51,0.73 0.78,1.11 27.73,39.61 64.59,62.16 136.67,91.89 32.87,13.2 65.64,27.22 94.55,48 0.34,0.25 0.69,0.49 1.04,0.74 18.15,13 35.43,27.37 48.96,45.26 0.22,0.29 0.45,0.59 0.67,0.89 25.44,33.61 39.25,71.89 33.72,114.45 -0.57,3.94 -1.42,7.8 -2.39,11.66 -0.1,0.39 -0.19,0.78 -0.29,1.18 -3.25,12.86 -7.87,24.93 -13.71,36.82 -0.16,0.32 -0.32,0.65 -0.48,0.98 -11.57,23.61 -27.36,44.24 -44.52,64.02 5.28,0 9.9,-6.86 13.59,-10.54 l 4.92,-4.89 c 13.59,-13.51 13.59,-13.51 18.83,-19.78 1.05,-1.25 2.14,-2.48 3.22,-3.71 33.2,-38.29 57.35,-83.87 82.75,-188.96 0.04,-0.3 0.04,-0.3 0.25,-1.85 1.92,-14.6 2.68,-29.12 2.69,-43.83 v -1.21 C 536.28,216.18 525.54,167.08 481,96 474.35027,82.502457 457.163,66.047388 442.4,54.389598 429.97756,44.580059 423.21007,38.671449 403.0602,29.91592 382.91033,21.160391 355.82,11.19 331,7 330.62,6.94 330.24,6.87 329.84,6.8 312.74,3.95 295.51,3.36 278.25,4.75 Z M 190,655 c 1.15,10.37 1.15,10.37 2.16,15.5 0.1,0.56 0.21,1.12 0.32,1.69 0.22,1.16 0.45,2.33 0.68,3.49 l 1.02,5.34 c 0.22,1.14 0.44,2.28 0.66,3.41 0.1,0.53 0.21,1.06 0.31,1.61 0.05,0.25 0.05,0.25 0.29,1.49 0.09,0.43 0.17,0.86 0.26,1.3 C 196,690 196,690 197,692 h 137 c 2.56,-12.29 5.09,-24.59 7,-37 z"
|
|
44
|
+
style="fill:#205080;fill-opacity:1;fill-rule:evenodd"
|
|
45
|
+
id="path1"
|
|
46
|
+
sodipodi:nodetypes="ccccccccccccccccccccccccccccccsccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccszcccccccccccccccc" />
|
|
47
|
+
</svg>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--bg:#f8fafc;--panel:#fff;--border:#e2e8f0;--text:#1e293b;--muted:#64748b;--accent:#2563eb;--accent-soft:#eff6ff;--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:baseline;gap:8px;padding:16px 16px 12px;display:flex}.sidebar-head h1{letter-spacing:-.01em;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{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{background:#fff;border:0;flex:1;width:100%}.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}.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}
|