md-review-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/bin/md-review.js +217 -0
- package/bin/skill-manager.js +145 -0
- package/dist/assets/_baseUniq-DI2TZgiU.js +1 -0
- package/dist/assets/arc-0aOBgqln.js +1 -0
- package/dist/assets/architectureDiagram-VXUJARFQ-D1WnZX0i.js +36 -0
- package/dist/assets/blockDiagram-VD42YOAC-CMmHyk4v.js +122 -0
- package/dist/assets/c4Diagram-YG6GDRKO-CnjNpHKo.js +10 -0
- package/dist/assets/channel-DKOgBY_w.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-_1bE-T-E.js +1 -0
- package/dist/assets/chunk-55IACEB6-dlxfCuoj.js +1 -0
- package/dist/assets/chunk-B4BG7PRW-C42iQvc0.js +165 -0
- package/dist/assets/chunk-DI55MBZ5-B5Uv0h4o.js +220 -0
- package/dist/assets/chunk-FMBD7UC4-BdfjKS1C.js +15 -0
- package/dist/assets/chunk-QN33PNHL-bYBDKYcm.js +1 -0
- package/dist/assets/chunk-QZHKN3VN-2hdQ_fkV.js +1 -0
- package/dist/assets/chunk-TZMSLE5B-C8LiAShG.js +1 -0
- package/dist/assets/classDiagram-2ON5EDUG-A7wHDY1p.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-A7wHDY1p.js +1 -0
- package/dist/assets/clone-FuZyOQgB.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-DsxEv76Y.js +1 -0
- package/dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
- package/dist/assets/dagre-6UL2VRFP-CzhKzxyL.js +4 -0
- package/dist/assets/defaultLocale-C4B-KCzX.js +1 -0
- package/dist/assets/diagram-PSM6KHXK-DchJfjQS.js +24 -0
- package/dist/assets/diagram-QEK2KX5R-CxBYETfP.js +43 -0
- package/dist/assets/diagram-S2PKOQOG-U9S5ZOME.js +24 -0
- package/dist/assets/erDiagram-Q2GNP2WA-BqZKWv9l.js +60 -0
- package/dist/assets/flowDiagram-NV44I4VS-7E3VaRAM.js +162 -0
- package/dist/assets/ganttDiagram-JELNMOA3-C3giu5WC.js +267 -0
- package/dist/assets/gitGraphDiagram-NY62KEGX-09l3qi9Y.js +65 -0
- package/dist/assets/graph-CIHF1jxj.js +1 -0
- package/dist/assets/index-D__pdEdb.css +19 -0
- package/dist/assets/index-DonetEir.js +346 -0
- package/dist/assets/infoDiagram-WHAUD3N6-CxrQKkZ7.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/journeyDiagram-XKPGCS4Q-CafZCYAC.js +139 -0
- package/dist/assets/kanban-definition-3W4ZIXB7-BPnBXjFX.js +89 -0
- package/dist/assets/katex-Cu_Erd72.js +261 -0
- package/dist/assets/layout-CuN5D054.js +1 -0
- package/dist/assets/linear-CVBbq0yW.js +1 -0
- package/dist/assets/min-DqAzei1c.js +1 -0
- package/dist/assets/mindmap-definition-VGOIOE7T-C9JzG0Gk.js +68 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pieDiagram-ADFJNKIX-DPLAvqYj.js +30 -0
- package/dist/assets/quadrantDiagram-AYHSOK5B-CF0Op1tv.js +7 -0
- package/dist/assets/requirementDiagram-UZGBJVZJ-CTYaZjq6.js +64 -0
- package/dist/assets/sankeyDiagram-TZEHDZUN-CVsSH6ag.js +10 -0
- package/dist/assets/sequenceDiagram-WL72ISMW-_5LQ8ply.js +145 -0
- package/dist/assets/stateDiagram-FKZM4ZOC-lGntU0qp.js +1 -0
- package/dist/assets/stateDiagram-v2-4FDKWEC3-D4z3Ploi.js +1 -0
- package/dist/assets/timeline-definition-IT6M3QCI-B2Cv_EhF.js +61 -0
- package/dist/assets/treemap-KMMF4GRG-C7myvUeN.js +128 -0
- package/dist/assets/xychartDiagram-PRI3JC2R-BlM5iMNi.js +7 -0
- package/dist/index.html +13 -0
- package/package.json +105 -0
- package/server/app.js +239 -0
- package/server/comment-store.js +277 -0
- package/server/index.js +161 -0
- package/skills/markdown-review-loop/SKILL.md +187 -0
- package/skills/markdown-review-loop/VERSION +1 -0
- package/skills/markdown-review-loop/agents/openai.yaml +4 -0
- package/skills/markdown-review-loop/references/review-template.md +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ryo Matsukawa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# md-review-server
|
|
2
|
+
|
|
3
|
+
简体中文 | [日本語](./README-ja.md)
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
`md-review-server` 是一个本地 Markdown 可视化评审服务。它保留 `md-review` 的 Markdown 预览、选区评论、评论列表和文件树能力,并将评论存储迁移到 sidecar review 文件,同时提供本地 HTTP API,供 Codex、其他 agent 或脚本读取评论并回写处理状态。
|
|
8
|
+
|
|
9
|
+
## 功能
|
|
10
|
+
|
|
11
|
+
- 按原始结构预览 Markdown 和 MDX 文件
|
|
12
|
+
- 解析并展示 Frontmatter 元数据
|
|
13
|
+
- 对选中文本和指定行范围创建评论
|
|
14
|
+
- 编辑和删除已有评论
|
|
15
|
+
- 将评论持久化到 `.reviews/*.review.json`
|
|
16
|
+
- 通过 HTTP API 读取评论和更新处理状态
|
|
17
|
+
- 在目录模式中通过文件树选择 Markdown 文件
|
|
18
|
+
- 支持深色模式,跟随系统偏好
|
|
19
|
+
- 支持可调整、可折叠的评论侧边栏
|
|
20
|
+
- 点击评论行号跳转到对应内容
|
|
21
|
+
- Markdown 文件变更后通过 SSE 自动刷新
|
|
22
|
+
|
|
23
|
+
## 安装
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm install -g md-review-server
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
当前首轮交付以本地使用为主,也可以在仓库中直接运行:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pnpm install
|
|
33
|
+
pnpm build
|
|
34
|
+
node bin/md-review.js docs --port 3030
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 使用方式
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
md-review-server [options] # 浏览当前目录下的 Markdown 文件
|
|
41
|
+
md-review-server <file> [options] # 预览单个 Markdown 文件
|
|
42
|
+
md-review-server <directory> [options] # 浏览指定目录下的 Markdown 文件
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 参数
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
-p, --port <port> 服务端口,默认 3030
|
|
49
|
+
--host <host> 监听地址,默认 127.0.0.1
|
|
50
|
+
--review-dir <dir> review sidecar 目录,默认 .reviews
|
|
51
|
+
--active-file <file> 目录模式下初始选中的文件
|
|
52
|
+
--readonly 禁用评论写入 API
|
|
53
|
+
--no-open 不自动打开浏览器
|
|
54
|
+
skill <command> 安装、更新或检查内置 Codex skills
|
|
55
|
+
-h, --help 显示帮助信息
|
|
56
|
+
-v, --version 显示版本号
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 示例
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
md-review-server
|
|
63
|
+
md-review-server docs
|
|
64
|
+
md-review-server README.md
|
|
65
|
+
md-review-server docs/guide.mdx
|
|
66
|
+
md-review-server docs --active-file docs/guide.md --port 8080
|
|
67
|
+
md-review-server skill install
|
|
68
|
+
md-review-server skill update --force
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
默认只监听 `127.0.0.1`。如果使用 `--host 0.0.0.0`,服务会在启动时输出安全提示;MVP 不包含认证能力。
|
|
72
|
+
|
|
73
|
+
## 评论数据
|
|
74
|
+
|
|
75
|
+
评论由服务端写入 Markdown 所在 review 目录:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
docs/.reviews/guide.v2.review.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
review 文件使用 JSON 存储,核心字段包括:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"schemaVersion": 1,
|
|
86
|
+
"document": "guide.v2.md",
|
|
87
|
+
"comments": [
|
|
88
|
+
{
|
|
89
|
+
"id": "c001",
|
|
90
|
+
"file": "guide.v2.md",
|
|
91
|
+
"startLine": 12,
|
|
92
|
+
"endLine": 12,
|
|
93
|
+
"startOffset": 4,
|
|
94
|
+
"endOffset": 18,
|
|
95
|
+
"selectedText": "selected text",
|
|
96
|
+
"beforeText": "before",
|
|
97
|
+
"afterText": "after",
|
|
98
|
+
"comment": "需要补充说明",
|
|
99
|
+
"status": "open"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
支持的评论状态:
|
|
106
|
+
|
|
107
|
+
- `open`:待处理
|
|
108
|
+
- `resolved`:已处理
|
|
109
|
+
- `partially_resolved`:部分处理
|
|
110
|
+
- `unresolved`:无法处理,需记录原因
|
|
111
|
+
- `ignored`:明确跳过
|
|
112
|
+
|
|
113
|
+
## HTTP API
|
|
114
|
+
|
|
115
|
+
### 获取会话信息
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
curl http://127.0.0.1:3030/api/session
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 获取待处理评论
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
curl 'http://127.0.0.1:3030/api/comments?file=guide.v2.md&status=open'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 创建评论
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
curl -X POST 'http://127.0.0.1:3030/api/comments' \
|
|
131
|
+
-H 'Content-Type: application/json' \
|
|
132
|
+
-d '{
|
|
133
|
+
"file": "guide.v2.md",
|
|
134
|
+
"startLine": 12,
|
|
135
|
+
"endLine": 12,
|
|
136
|
+
"selectedText": "selected text",
|
|
137
|
+
"comment": "需要补充说明"
|
|
138
|
+
}'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 批量回写状态
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
curl -X PATCH 'http://127.0.0.1:3030/api/comments' \
|
|
145
|
+
-H 'Content-Type: application/json' \
|
|
146
|
+
-d '{
|
|
147
|
+
"updates": [
|
|
148
|
+
{
|
|
149
|
+
"id": "c001",
|
|
150
|
+
"file": "guide.v2.md",
|
|
151
|
+
"status": "resolved",
|
|
152
|
+
"targetFile": "guide.v3.md",
|
|
153
|
+
"resolution": "已补充说明。"
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Codex 评审循环
|
|
160
|
+
|
|
161
|
+
推荐使用目录模式启动 review server:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
md-review-server docs --port 3030 --active-file docs/guide.v2.md
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
典型流程:
|
|
168
|
+
|
|
169
|
+
1. Codex 生成一个版本化 Markdown 文件,例如 `guide.v2.md`
|
|
170
|
+
2. 用户在浏览器中选区并创建评论
|
|
171
|
+
3. 服务端将评论写入 `.reviews/*.review.json`
|
|
172
|
+
4. Codex 通过 `GET /api/comments?status=open` 获取待处理评论
|
|
173
|
+
5. Codex 生成下一版 Markdown,例如 `guide.v3.md`
|
|
174
|
+
6. Codex 通过批量 `PATCH /api/comments` 回写每条评论的处理状态
|
|
175
|
+
7. 用户在同一个 review server 中选择新版本继续评审
|
|
176
|
+
|
|
177
|
+
### 安装 Codex Skill
|
|
178
|
+
|
|
179
|
+
包内提供 `markdown-review-loop` skill,用于让 Codex 自动执行启动 review server、读取评论、生成下一版 Markdown 和回写状态的流程。
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
npx -y md-review-server@latest skill install
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
如果已经全局安装 `md-review-server`,也可以直接运行 `md-review-server skill install`。
|
|
186
|
+
|
|
187
|
+
安装后可通过 `$markdown-review-loop` 显式触发,例如:
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
使用 $markdown-review-loop 帮我启动这份 Markdown 的评审循环。
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
skill 依赖本机可运行 `md-review-server`。本地开发阶段可以先在仓库中执行 `npm link`,或使用发布后的 npm 包。
|
|
194
|
+
|
|
195
|
+
更新 skill:
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
npx -y md-review-server@latest skill update
|
|
199
|
+
md-review-server skill doctor
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## 评论管理
|
|
203
|
+
|
|
204
|
+
### 添加评论
|
|
205
|
+
|
|
206
|
+
1. 在 Markdown 预览区域选择文本
|
|
207
|
+
2. 点击出现的 `Comment` 按钮
|
|
208
|
+
3. 输入评论内容
|
|
209
|
+
4. 按 `Cmd/Ctrl+Enter` 或点击 `Submit`
|
|
210
|
+
|
|
211
|
+
### 编辑评论
|
|
212
|
+
|
|
213
|
+
1. 点击评论上的编辑按钮
|
|
214
|
+
2. 修改文本框中的内容
|
|
215
|
+
3. 按 `Cmd/Ctrl+Enter` 或点击 `Save`
|
|
216
|
+
4. 按 `Escape` 或点击 `Cancel` 放弃修改
|
|
217
|
+
|
|
218
|
+
### 快捷键
|
|
219
|
+
|
|
220
|
+
- `Cmd/Ctrl+Enter`:提交或保存评论
|
|
221
|
+
- `Escape`:取消编辑
|
|
222
|
+
- `Cmd+K`:目录模式中聚焦搜索框
|
|
223
|
+
|
|
224
|
+
## 本地开发
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
pnpm install
|
|
228
|
+
pnpm dev
|
|
229
|
+
pnpm test
|
|
230
|
+
pnpm build
|
|
231
|
+
pnpm lint
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## License
|
|
235
|
+
|
|
236
|
+
[MIT](./LICENSE)
|
package/bin/md-review.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { resolve, dirname, relative } from 'path';
|
|
5
|
+
import { existsSync, readFileSync, statSync } from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import mri from 'mri';
|
|
8
|
+
import { handleSkillCommand } from './skill-manager.js';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const packageRoot = resolve(__dirname, '..');
|
|
13
|
+
|
|
14
|
+
const pkg = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf-8'));
|
|
15
|
+
|
|
16
|
+
const SERVER_READY_MESSAGE = 'md-review server started';
|
|
17
|
+
const rawArgs = process.argv.slice(2);
|
|
18
|
+
|
|
19
|
+
// Port validation function
|
|
20
|
+
function validatePort(value, name) {
|
|
21
|
+
const port = parseInt(value, 10);
|
|
22
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
23
|
+
console.error(`Error: Invalid ${name}: ${value}. Must be between 1 and 65535`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
return port;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if file has markdown extension
|
|
30
|
+
function isMarkdownFile(filePath) {
|
|
31
|
+
return filePath.endsWith('.md') || filePath.endsWith('.markdown') || filePath.endsWith('.mdx');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Parse arguments
|
|
35
|
+
const args = mri(rawArgs, {
|
|
36
|
+
alias: {
|
|
37
|
+
p: 'port',
|
|
38
|
+
h: 'help',
|
|
39
|
+
v: 'version',
|
|
40
|
+
},
|
|
41
|
+
default: {
|
|
42
|
+
port: '3030',
|
|
43
|
+
host: '127.0.0.1',
|
|
44
|
+
'review-dir': '.reviews',
|
|
45
|
+
open: true,
|
|
46
|
+
readonly: false,
|
|
47
|
+
},
|
|
48
|
+
boolean: ['help', 'version', 'open', 'readonly'],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (args._[0] === 'skill') {
|
|
52
|
+
process.exit(handleSkillCommand({ packageRoot, argv: rawArgs.slice(1) }));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Help message
|
|
56
|
+
if (args.help) {
|
|
57
|
+
console.log(`
|
|
58
|
+
md-review-server - Review Markdown files with sidecar comments and HTTP APIs
|
|
59
|
+
|
|
60
|
+
Usage:
|
|
61
|
+
md-review-server [options] Browse markdown files in current directory
|
|
62
|
+
md-review-server <file> [options] Preview a specific Markdown file
|
|
63
|
+
md-review-server <directory> [options] Browse Markdown files in a directory
|
|
64
|
+
md-review-server skill <command> Install, update, or inspect bundled Codex skills
|
|
65
|
+
|
|
66
|
+
Options:
|
|
67
|
+
-p, --port <port> Server port (default: 3030)
|
|
68
|
+
--host <host> Server host (default: 127.0.0.1)
|
|
69
|
+
--review-dir <dir> Review sidecar directory (default: .reviews)
|
|
70
|
+
--active-file <file> Initial file to select in directory mode
|
|
71
|
+
--readonly Disable comment write APIs
|
|
72
|
+
--no-open Do not open browser automatically
|
|
73
|
+
-h, --help Show this help message
|
|
74
|
+
-v, --version Show version number
|
|
75
|
+
|
|
76
|
+
Examples:
|
|
77
|
+
md-review-server
|
|
78
|
+
md-review-server docs --active-file guide.v2.md
|
|
79
|
+
md-review-server README.md --port 8080
|
|
80
|
+
md-review-server skill install
|
|
81
|
+
md-review-server skill update --force
|
|
82
|
+
`);
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Version
|
|
87
|
+
if (args.version) {
|
|
88
|
+
console.log(pkg.version);
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const file = args._[0];
|
|
93
|
+
const port = validatePort(args.port, 'port');
|
|
94
|
+
const host = args.host;
|
|
95
|
+
const shouldOpen = args.open;
|
|
96
|
+
const activeFile = args['active-file'] || '';
|
|
97
|
+
|
|
98
|
+
// Set environment variables
|
|
99
|
+
process.env.API_PORT = port;
|
|
100
|
+
process.env.API_HOST = host;
|
|
101
|
+
process.env.REVIEW_DIR = args['review-dir'];
|
|
102
|
+
process.env.ACTIVE_FILE = activeFile;
|
|
103
|
+
process.env.READONLY = args.readonly ? 'true' : 'false';
|
|
104
|
+
|
|
105
|
+
if (host === '0.0.0.0') {
|
|
106
|
+
console.warn('Warning: md-review-server will listen on 0.0.0.0 without authentication.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// If file is specified, validate it
|
|
110
|
+
if (file) {
|
|
111
|
+
const filePath = resolve(file);
|
|
112
|
+
|
|
113
|
+
if (!existsSync(filePath)) {
|
|
114
|
+
console.error(`Error: File not found: ${filePath}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const stats = statSync(filePath);
|
|
119
|
+
|
|
120
|
+
if (stats.isDirectory()) {
|
|
121
|
+
// Dev mode with specified directory
|
|
122
|
+
process.env.BASE_DIR = filePath;
|
|
123
|
+
if (activeFile) {
|
|
124
|
+
const activePath = resolve(activeFile);
|
|
125
|
+
if (activePath.startsWith(filePath)) {
|
|
126
|
+
process.env.ACTIVE_FILE = relative(filePath, activePath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
console.log(`Directory: ${filePath}`);
|
|
130
|
+
} else {
|
|
131
|
+
// File mode
|
|
132
|
+
if (!isMarkdownFile(filePath)) {
|
|
133
|
+
console.error(`Error: File must have .md or .markdown extension: ${filePath}`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
process.env.MARKDOWN_FILE_PATH = filePath;
|
|
138
|
+
console.log(`File: ${filePath}`);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
// Dev mode - browse all markdown files
|
|
142
|
+
const baseDir = process.cwd();
|
|
143
|
+
process.env.BASE_DIR = baseDir;
|
|
144
|
+
if (activeFile) {
|
|
145
|
+
const activePath = resolve(activeFile);
|
|
146
|
+
if (activePath.startsWith(baseDir)) {
|
|
147
|
+
process.env.ACTIVE_FILE = relative(baseDir, activePath);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
console.log(`Directory: ${baseDir}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log('Starting md-review-server...');
|
|
154
|
+
console.log(` Port: ${port}`);
|
|
155
|
+
console.log(` Host: ${host}`);
|
|
156
|
+
console.log(` Review dir: ${args['review-dir']}`);
|
|
157
|
+
if (process.env.ACTIVE_FILE) {
|
|
158
|
+
console.log(` Active file: ${process.env.ACTIVE_FILE}`);
|
|
159
|
+
}
|
|
160
|
+
if (args.readonly) {
|
|
161
|
+
console.log(' Readonly: true');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Start server
|
|
165
|
+
const serverProcess = spawn('node', ['server/index.js'], {
|
|
166
|
+
cwd: packageRoot,
|
|
167
|
+
stdio: ['inherit', 'pipe', 'inherit'],
|
|
168
|
+
env: process.env,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
let serverReady = false;
|
|
172
|
+
let actualPort = port;
|
|
173
|
+
|
|
174
|
+
// Wait for server to be ready before opening browser
|
|
175
|
+
serverProcess.stdout.on('data', async (data) => {
|
|
176
|
+
process.stdout.write(data);
|
|
177
|
+
const output = data.toString();
|
|
178
|
+
|
|
179
|
+
// Extract actual port from "API Server running on http://HOST:XXXX"
|
|
180
|
+
const portMatch = output.match(/API Server running on http:\/\/[^:]+:(\d+)/);
|
|
181
|
+
if (portMatch) {
|
|
182
|
+
actualPort = parseInt(portMatch[1], 10);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!serverReady && output.includes(SERVER_READY_MESSAGE)) {
|
|
186
|
+
serverReady = true;
|
|
187
|
+
|
|
188
|
+
if (shouldOpen) {
|
|
189
|
+
const openModule = await import('open');
|
|
190
|
+
const browserHost = host === '0.0.0.0' ? '127.0.0.1' : host;
|
|
191
|
+
openModule.default(`http://${browserHost}:${actualPort}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Handle graceful shutdown
|
|
197
|
+
const shutdown = () => {
|
|
198
|
+
console.log('\nShutting down...');
|
|
199
|
+
serverProcess.kill('SIGINT');
|
|
200
|
+
process.exit(0);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
process.on('SIGINT', shutdown);
|
|
204
|
+
process.on('SIGTERM', shutdown);
|
|
205
|
+
|
|
206
|
+
// Handle server exit
|
|
207
|
+
serverProcess.on('exit', (code) => {
|
|
208
|
+
if (code !== 0 && code !== null) {
|
|
209
|
+
console.error(`Server exited with code ${code}`);
|
|
210
|
+
}
|
|
211
|
+
process.exit(code || 0);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
serverProcess.on('error', (err) => {
|
|
215
|
+
console.error('Failed to start server:', err.message);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cpSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from 'fs';
|
|
9
|
+
import { dirname, join, resolve } from 'path';
|
|
10
|
+
|
|
11
|
+
export const SKILL_NAME = 'markdown-review-loop';
|
|
12
|
+
|
|
13
|
+
export function getCodexHome(env = process.env) {
|
|
14
|
+
return env.CODEX_HOME || join(env.HOME || process.cwd(), '.codex');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getSkillPaths(packageRoot, env = process.env) {
|
|
18
|
+
const codexHome = getCodexHome(env);
|
|
19
|
+
return {
|
|
20
|
+
source: resolve(packageRoot, 'skills', SKILL_NAME),
|
|
21
|
+
target: resolve(codexHome, 'skills', SKILL_NAME),
|
|
22
|
+
codexHome,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function readVersion(skillDir) {
|
|
27
|
+
const versionPath = join(skillDir, 'VERSION');
|
|
28
|
+
if (!existsSync(versionPath)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return readFileSync(versionPath, 'utf-8').trim() || null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function copySkill(source, target) {
|
|
35
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
36
|
+
rmSync(target, { recursive: true, force: true });
|
|
37
|
+
cpSync(source, target, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function installOrUpdateSkill({
|
|
41
|
+
packageRoot,
|
|
42
|
+
env = process.env,
|
|
43
|
+
force = false,
|
|
44
|
+
quiet = false,
|
|
45
|
+
} = {}) {
|
|
46
|
+
const { source, target } = getSkillPaths(packageRoot, env);
|
|
47
|
+
if (!existsSync(source)) {
|
|
48
|
+
throw new Error(`Bundled skill not found: ${source}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const bundledVersion = readVersion(source);
|
|
52
|
+
if (!bundledVersion) {
|
|
53
|
+
throw new Error(`Bundled skill VERSION is missing: ${source}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const installedVersion = readVersion(target);
|
|
57
|
+
if (!force && installedVersion === bundledVersion) {
|
|
58
|
+
if (!quiet) {
|
|
59
|
+
console.log(`${SKILL_NAME} is already up to date (${bundledVersion}).`);
|
|
60
|
+
}
|
|
61
|
+
return { action: 'skipped', version: bundledVersion, target };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
copySkill(source, target);
|
|
65
|
+
writeFileSync(join(target, 'VERSION'), `${bundledVersion}\n`);
|
|
66
|
+
|
|
67
|
+
if (!quiet) {
|
|
68
|
+
const action = installedVersion ? 'Updated' : 'Installed';
|
|
69
|
+
console.log(`${action} ${SKILL_NAME} ${bundledVersion} to ${target}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
action: installedVersion ? 'updated' : 'installed',
|
|
74
|
+
version: bundledVersion,
|
|
75
|
+
previousVersion: installedVersion,
|
|
76
|
+
target,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getSkillStatus({ packageRoot, env = process.env } = {}) {
|
|
81
|
+
const { source, target } = getSkillPaths(packageRoot, env);
|
|
82
|
+
const bundledVersion = readVersion(source);
|
|
83
|
+
const installedVersion = readVersion(target);
|
|
84
|
+
const installed = existsSync(join(target, 'SKILL.md'));
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
source,
|
|
88
|
+
target,
|
|
89
|
+
bundledVersion,
|
|
90
|
+
installedVersion,
|
|
91
|
+
installed,
|
|
92
|
+
upToDate: Boolean(installed && bundledVersion && installedVersion === bundledVersion),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function printSkillStatus(status) {
|
|
97
|
+
console.log(`Skill: ${SKILL_NAME}`);
|
|
98
|
+
console.log(`Bundled: ${status.bundledVersion || 'missing'}`);
|
|
99
|
+
console.log(`Installed: ${status.installedVersion || 'missing'}`);
|
|
100
|
+
console.log(`Path: ${status.target}`);
|
|
101
|
+
console.log(`Status: ${status.upToDate ? 'up to date' : status.installed ? 'update available' : 'not installed'}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function printSkillHelp() {
|
|
105
|
+
console.log(`
|
|
106
|
+
Usage:
|
|
107
|
+
md-review-server skill install [--force] [--quiet]
|
|
108
|
+
md-review-server skill update [--force] [--quiet]
|
|
109
|
+
md-review-server skill doctor
|
|
110
|
+
|
|
111
|
+
Examples:
|
|
112
|
+
npx -y md-review-server@latest skill install
|
|
113
|
+
npx -y md-review-server@latest skill update --quiet
|
|
114
|
+
md-review-server skill doctor
|
|
115
|
+
`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function handleSkillCommand({
|
|
119
|
+
packageRoot,
|
|
120
|
+
argv,
|
|
121
|
+
env = process.env,
|
|
122
|
+
} = {}) {
|
|
123
|
+
const subcommand = argv[0] || 'doctor';
|
|
124
|
+
const force = argv.includes('--force');
|
|
125
|
+
const quiet = argv.includes('--quiet');
|
|
126
|
+
|
|
127
|
+
if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
|
|
128
|
+
printSkillHelp();
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (subcommand === 'install' || subcommand === 'update') {
|
|
133
|
+
installOrUpdateSkill({ packageRoot, env, force, quiet });
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (subcommand === 'doctor' || subcommand === 'status') {
|
|
138
|
+
printSkillStatus(getSkillStatus({ packageRoot, env }));
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.error(`Unknown skill command: ${subcommand}`);
|
|
143
|
+
console.error('Usage: md-review-server skill <install|update|doctor> [--force] [--quiet]');
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{aV as L,br as ln,aE as A,aT as P,bs as gn,bt as dn,aD as W,bu as hn,bv as z,bw as pn,bm as An,bx as m,aW as N,a$ as U,b2 as T,by as _n,aZ as on,bz as wn,bp as On,aF as V,bn as vn,bA as I}from"./index-DonetEir.js";var Pn="[object Symbol]";function x(n){return typeof n=="symbol"||L(n)&&ln(n)==Pn}function yn(n,r){for(var e=-1,i=n==null?0:n.length,f=Array(i);++e<i;)f[e]=r(n[e],e,n);return f}var B=P?P.prototype:void 0,K=B?B.toString:void 0;function k(n){if(typeof n=="string")return n;if(A(n))return yn(n,k)+"";if(x(n))return K?K.call(n):"";var r=n+"";return r=="0"&&1/n==-1/0?"-0":r}function En(){}function bn(n,r){for(var e=-1,i=n==null?0:n.length;++e<i&&r(n[e],e,n)!==!1;);return n}function cn(n,r,e,i){for(var f=n.length,t=e+-1;++t<f;)if(r(n[t],t,n))return t;return-1}function Tn(n){return n!==n}function Rn(n,r,e){for(var i=e-1,f=n.length;++i<f;)if(n[i]===r)return i;return-1}function In(n,r,e){return r===r?Rn(n,r,e):cn(n,Tn,e)}function Sn(n,r){var e=n==null?0:n.length;return!!e&&In(n,r,0)>-1}function M(n){return W(n)?gn(n):dn(n)}var Ln=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,xn=/^\w*$/;function $(n,r){if(A(n))return!1;var e=typeof n;return e=="number"||e=="symbol"||e=="boolean"||n==null||x(n)?!0:xn.test(n)||!Ln.test(n)||r!=null&&n in Object(r)}var Mn=500;function $n(n){var r=hn(n,function(i){return e.size===Mn&&e.clear(),i}),e=r.cache;return r}var Cn=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,Dn=/\\(\\)?/g,Fn=$n(function(n){var r=[];return n.charCodeAt(0)===46&&r.push(""),n.replace(Cn,function(e,i,f,t){r.push(f?t.replace(Dn,"$1"):i||e)}),r});function Gn(n){return n==null?"":k(n)}function j(n,r){return A(n)?n:$(n,r)?[n]:Fn(Gn(n))}function R(n){if(typeof n=="string"||x(n))return n;var r=n+"";return r=="0"&&1/n==-1/0?"-0":r}function nn(n,r){r=j(r,n);for(var e=0,i=r.length;n!=null&&e<i;)n=n[R(r[e++])];return e&&e==i?n:void 0}function mn(n,r,e){var i=n==null?void 0:nn(n,r);return i===void 0?e:i}function rn(n,r){for(var e=-1,i=r.length,f=n.length;++e<i;)n[f+e]=r[e];return n}var H=P?P.isConcatSpreadable:void 0;function Nn(n){return A(n)||z(n)||!!(H&&n&&n[H])}function Hr(n,r,e,i,f){var t=-1,s=n.length;for(e||(e=Nn),f||(f=[]);++t<s;){var u=n[t];e(u)?rn(f,u):i||(f[f.length]=u)}return f}function Un(n,r,e,i){var f=-1,t=n==null?0:n.length;for(i&&t&&(e=n[++f]);++f<t;)e=r(e,n[f],f,n);return e}function en(n,r){for(var e=-1,i=n==null?0:n.length,f=0,t=[];++e<i;){var s=n[e];r(s,e,n)&&(t[f++]=s)}return t}function Bn(){return[]}var Kn=Object.prototype,Hn=Kn.propertyIsEnumerable,Z=Object.getOwnPropertySymbols,Zn=Z?function(n){return n==null?[]:(n=Object(n),en(Z(n),function(r){return Hn.call(n,r)}))}:Bn;function qn(n,r,e){var i=r(n);return A(n)?i:rn(i,e(n))}function q(n){return qn(n,M,Zn)}var Yn="__lodash_hash_undefined__";function Xn(n){return this.__data__.set(n,Yn),this}function Jn(n){return this.__data__.has(n)}function y(n){var r=-1,e=n==null?0:n.length;for(this.__data__=new pn;++r<e;)this.add(n[r])}y.prototype.add=y.prototype.push=Xn;y.prototype.has=Jn;function Qn(n,r){for(var e=-1,i=n==null?0:n.length;++e<i;)if(r(n[e],e,n))return!0;return!1}function tn(n,r){return n.has(r)}var Wn=1,zn=2;function fn(n,r,e,i,f,t){var s=e&Wn,u=n.length,a=r.length;if(u!=a&&!(s&&a>u))return!1;var h=t.get(n),g=t.get(r);if(h&&g)return h==r&&g==n;var l=-1,d=!0,o=e&zn?new y:void 0;for(t.set(n,r),t.set(r,n);++l<u;){var p=n[l],_=r[l];if(i)var w=s?i(_,p,l,r,n,t):i(p,_,l,n,r,t);if(w!==void 0){if(w)continue;d=!1;break}if(o){if(!Qn(r,function(O,v){if(!tn(o,v)&&(p===O||f(p,O,e,i,t)))return o.push(v)})){d=!1;break}}else if(!(p===_||f(p,_,e,i,t))){d=!1;break}}return t.delete(n),t.delete(r),d}function Vn(n){var r=-1,e=Array(n.size);return n.forEach(function(i,f){e[++r]=[f,i]}),e}function C(n){var r=-1,e=Array(n.size);return n.forEach(function(i){e[++r]=i}),e}var kn=1,jn=2,nr="[object Boolean]",rr="[object Date]",er="[object Error]",ir="[object Map]",tr="[object Number]",fr="[object RegExp]",sr="[object Set]",ur="[object String]",ar="[object Symbol]",lr="[object ArrayBuffer]",gr="[object DataView]",Y=P?P.prototype:void 0,S=Y?Y.valueOf:void 0;function dr(n,r,e,i,f,t,s){switch(e){case gr:if(n.byteLength!=r.byteLength||n.byteOffset!=r.byteOffset)return!1;n=n.buffer,r=r.buffer;case lr:return!(n.byteLength!=r.byteLength||!t(new m(n),new m(r)));case nr:case rr:case tr:return An(+n,+r);case er:return n.name==r.name&&n.message==r.message;case fr:case ur:return n==r+"";case ir:var u=Vn;case sr:var a=i&kn;if(u||(u=C),n.size!=r.size&&!a)return!1;var h=s.get(n);if(h)return h==r;i|=jn,s.set(n,r);var g=fn(u(n),u(r),i,f,t,s);return s.delete(n),g;case ar:if(S)return S.call(n)==S.call(r)}return!1}var hr=1,pr=Object.prototype,Ar=pr.hasOwnProperty;function _r(n,r,e,i,f,t){var s=e&hr,u=q(n),a=u.length,h=q(r),g=h.length;if(a!=g&&!s)return!1;for(var l=a;l--;){var d=u[l];if(!(s?d in r:Ar.call(r,d)))return!1}var o=t.get(n),p=t.get(r);if(o&&p)return o==r&&p==n;var _=!0;t.set(n,r),t.set(r,n);for(var w=s;++l<a;){d=u[l];var O=n[d],v=r[d];if(i)var G=s?i(v,O,d,r,n,t):i(O,v,d,n,r,t);if(!(G===void 0?O===v||f(O,v,e,i,t):G)){_=!1;break}w||(w=d=="constructor")}if(_&&!w){var E=n.constructor,b=r.constructor;E!=b&&"constructor"in n&&"constructor"in r&&!(typeof E=="function"&&E instanceof E&&typeof b=="function"&&b instanceof b)&&(_=!1)}return t.delete(n),t.delete(r),_}var or=1,X="[object Arguments]",J="[object Array]",c="[object Object]",wr=Object.prototype,Q=wr.hasOwnProperty;function Or(n,r,e,i,f,t){var s=A(n),u=A(r),a=s?J:N(n),h=u?J:N(r);a=a==X?c:a,h=h==X?c:h;var g=a==c,l=h==c,d=a==h;if(d&&U(n)){if(!U(r))return!1;s=!0,g=!1}if(d&&!g)return t||(t=new T),s||_n(n)?fn(n,r,e,i,f,t):dr(n,r,a,e,i,f,t);if(!(e&or)){var o=g&&Q.call(n,"__wrapped__"),p=l&&Q.call(r,"__wrapped__");if(o||p){var _=o?n.value():n,w=p?r.value():r;return t||(t=new T),f(_,w,e,i,t)}}return d?(t||(t=new T),_r(n,r,e,i,f,t)):!1}function D(n,r,e,i,f){return n===r?!0:n==null||r==null||!L(n)&&!L(r)?n!==n&&r!==r:Or(n,r,e,i,D,f)}var vr=1,Pr=2;function yr(n,r,e,i){var f=e.length,t=f;if(n==null)return!t;for(n=Object(n);f--;){var s=e[f];if(s[2]?s[1]!==n[s[0]]:!(s[0]in n))return!1}for(;++f<t;){s=e[f];var u=s[0],a=n[u],h=s[1];if(s[2]){if(a===void 0&&!(u in n))return!1}else{var g=new T,l;if(!(l===void 0?D(h,a,vr|Pr,i,g):l))return!1}}return!0}function sn(n){return n===n&&!on(n)}function Er(n){for(var r=M(n),e=r.length;e--;){var i=r[e],f=n[i];r[e]=[i,f,sn(f)]}return r}function un(n,r){return function(e){return e==null?!1:e[n]===r&&(r!==void 0||n in Object(e))}}function br(n){var r=Er(n);return r.length==1&&r[0][2]?un(r[0][0],r[0][1]):function(e){return e===n||yr(e,n,r)}}function cr(n,r){return n!=null&&r in Object(n)}function Tr(n,r,e){r=j(r,n);for(var i=-1,f=r.length,t=!1;++i<f;){var s=R(r[i]);if(!(t=n!=null&&e(n,s)))break;n=n[s]}return t||++i!=f?t:(f=n==null?0:n.length,!!f&&wn(f)&&On(s,f)&&(A(n)||z(n)))}function Rr(n,r){return n!=null&&Tr(n,r,cr)}var Ir=1,Sr=2;function Lr(n,r){return $(n)&&sn(r)?un(R(n),r):function(e){var i=mn(e,n);return i===void 0&&i===r?Rr(e,n):D(r,i,Ir|Sr)}}function xr(n){return function(r){return r==null?void 0:r[n]}}function Mr(n){return function(r){return nn(r,n)}}function $r(n){return $(n)?xr(R(n)):Mr(n)}function an(n){return typeof n=="function"?n:n==null?V:typeof n=="object"?A(n)?Lr(n[0],n[1]):br(n):$r(n)}function Cr(n,r){return n&&vn(n,r,M)}function Dr(n,r){return function(e,i){if(e==null)return e;if(!W(e))return n(e,i);for(var f=e.length,t=-1,s=Object(e);++t<f&&i(s[t],t,s)!==!1;);return e}}var F=Dr(Cr);function Fr(n){return typeof n=="function"?n:V}function Zr(n,r){var e=A(n)?bn:F;return e(n,Fr(r))}function Gr(n,r){var e=[];return F(n,function(i,f,t){r(i,f,t)&&e.push(i)}),e}function qr(n,r){var e=A(n)?en:Gr;return e(n,an(r))}function mr(n,r,e,i,f){return f(n,function(t,s,u){e=i?(i=!1,t):r(e,t,s,u)}),e}function Yr(n,r,e){var i=A(n)?Un:mr,f=arguments.length<3;return i(n,an(r),e,f,F)}var Nr=1/0,Ur=I&&1/C(new I([,-0]))[1]==Nr?function(n){return new I(n)}:En,Br=200;function Xr(n,r,e){var i=-1,f=Sn,t=n.length,s=!0,u=[],a=u;if(t>=Br){var h=r?null:Ur(n);if(h)return C(h);s=!1,f=tn,a=new y}else a=r?[]:u;n:for(;++i<t;){var g=n[i],l=r?r(g):g;if(g=g!==0?g:0,s&&l===l){for(var d=a.length;d--;)if(a[d]===l)continue n;r&&a.push(l),u.push(g)}else f(a,l,e)||(a!==u&&a.push(l),u.push(g))}return u}export{F as a,Hr as b,yn as c,an as d,rn as e,qn as f,Zn as g,q as h,x as i,bn as j,M as k,Xr as l,qr as m,Zr as n,cn as o,Fr as p,Cr as q,Yr as r,Bn as s,Tr as t,j as u,R as v,nn as w,Rr as x,Gn as y};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as B,a9 as rn,aa as L,ab as o,ac as sn,ad as on,ae as fn}from"./index-DonetEir.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(fn(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,F=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<rn)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(on((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|