@imenam/mcp-documentor 1.0.91 → 1.0.92
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 +2 -2
- package/dist/src/gui-worker.js +52 -68
- package/dist/src/index.js +8 -1
- package/gui/dist/assets/{arc-VljQAUvA.js → arc-BbedxJ3W.js} +1 -1
- package/gui/dist/assets/{architectureDiagram-3BPJPVTR-lbkjtvmN.js → architectureDiagram-3BPJPVTR-DF3IYwLZ.js} +1 -1
- package/gui/dist/assets/{blockDiagram-GPEHLZMM-G4A-rV4c.js → blockDiagram-GPEHLZMM-CgILcS8Q.js} +1 -1
- package/gui/dist/assets/{c4Diagram-AAUBKEIU-Dq3aMw8v.js → c4Diagram-AAUBKEIU-ByGBdJcU.js} +1 -1
- package/gui/dist/assets/channel-CIJsZK_a.js +1 -0
- package/gui/dist/assets/{chunk-2J33WTMH-VSzyIJHo.js → chunk-2J33WTMH-DIk0ZeJ6.js} +1 -1
- package/gui/dist/assets/{chunk-4BX2VUAB-DER41q8w.js → chunk-4BX2VUAB-fF78mC-G.js} +1 -1
- package/gui/dist/assets/{chunk-55IACEB6-BlOGGRk4.js → chunk-55IACEB6-BzoqbAWn.js} +1 -1
- package/gui/dist/assets/{chunk-727SXJPM-Dv3Ay2KV.js → chunk-727SXJPM-B0BlALdx.js} +1 -1
- package/gui/dist/assets/{chunk-AQP2D5EJ-BycHHbwh.js → chunk-AQP2D5EJ-DlKhB6LK.js} +1 -1
- package/gui/dist/assets/{chunk-FMBD7UC4-DBoCiqpY.js → chunk-FMBD7UC4-DBTqXHP1.js} +1 -1
- package/gui/dist/assets/{chunk-ND2GUHAM-DGKypjhb.js → chunk-ND2GUHAM-BmMICe1L.js} +1 -1
- package/gui/dist/assets/{chunk-QZHKN3VN-B8Gn_mBo.js → chunk-QZHKN3VN-DyEBqCzf.js} +1 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-LzEGnqT7.js +1 -0
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-LzEGnqT7.js +1 -0
- package/gui/dist/assets/{cose-bilkent-S5V4N54A-DyamTuYj.js → cose-bilkent-S5V4N54A-DMkNiOVz.js} +1 -1
- package/gui/dist/assets/{dagre-BM42HDAG-BKC5D2WN.js → dagre-BM42HDAG-CEGX5APR.js} +1 -1
- package/gui/dist/assets/{diagram-2AECGRRQ-BV2lBpwH.js → diagram-2AECGRRQ-DHo40rfh.js} +1 -1
- package/gui/dist/assets/{diagram-5GNKFQAL-DGwbAj4W.js → diagram-5GNKFQAL-CPbyX-Jj.js} +1 -1
- package/gui/dist/assets/{diagram-KO2AKTUF-CZAde8Sg.js → diagram-KO2AKTUF-B-LnaXtr.js} +1 -1
- package/gui/dist/assets/{diagram-LMA3HP47-fQfo0z7i.js → diagram-LMA3HP47-gjnm6GxN.js} +1 -1
- package/gui/dist/assets/{diagram-OG6HWLK6-BlD3VAFM.js → diagram-OG6HWLK6-B_qc7WBs.js} +1 -1
- package/gui/dist/assets/{erDiagram-TEJ5UH35-DPUEmQfe.js → erDiagram-TEJ5UH35-B7jlXuub.js} +1 -1
- package/gui/dist/assets/{flowDiagram-I6XJVG4X-p2HfkBjW.js → flowDiagram-I6XJVG4X-BjWyZsCs.js} +1 -1
- package/gui/dist/assets/{ganttDiagram-6RSMTGT7-CWfFoy4g.js → ganttDiagram-6RSMTGT7-CGdvXL_7.js} +1 -1
- package/gui/dist/assets/{gitGraphDiagram-PVQCEYII-DkTKzmes.js → gitGraphDiagram-PVQCEYII-TxUcHrcr.js} +1 -1
- package/gui/dist/assets/{index-BJ4WCWig.css → index-ARDnpfWR.css} +1 -1
- package/gui/dist/assets/{index-C8LVeAsA.js → index-CrG0yj-K.js} +93 -96
- package/gui/dist/assets/{infoDiagram-5YYISTIA-CXHTqZoU.js → infoDiagram-5YYISTIA-BElIHd-R.js} +1 -1
- package/gui/dist/assets/{ishikawaDiagram-YF4QCWOH-DZ61Cc8a.js → ishikawaDiagram-YF4QCWOH-De4Ds8bG.js} +1 -1
- package/gui/dist/assets/{journeyDiagram-JHISSGLW-qYtEmFuB.js → journeyDiagram-JHISSGLW-Cjpgxilg.js} +1 -1
- package/gui/dist/assets/{kanban-definition-UN3LZRKU-Bs-W313s.js → kanban-definition-UN3LZRKU-D8Y_Af2d.js} +1 -1
- package/gui/dist/assets/{linear-CU6J91M5.js → linear-BB2LhUqA.js} +1 -1
- package/gui/dist/assets/{mindmap-definition-RKZ34NQL-CvBqAH4c.js → mindmap-definition-RKZ34NQL-Cq_fBarD.js} +1 -1
- package/gui/dist/assets/{pieDiagram-4H26LBE5-BD2UcQce.js → pieDiagram-4H26LBE5-LGkTEx-6.js} +1 -1
- package/gui/dist/assets/{quadrantDiagram-W4KKPZXB-DEuynlP9.js → quadrantDiagram-W4KKPZXB-mgy_9RRW.js} +1 -1
- package/gui/dist/assets/{requirementDiagram-4Y6WPE33-DPxfzOF3.js → requirementDiagram-4Y6WPE33-B_d8OEc3.js} +1 -1
- package/gui/dist/assets/{sankeyDiagram-5OEKKPKP-C70_cePR.js → sankeyDiagram-5OEKKPKP-BgBLM80u.js} +1 -1
- package/gui/dist/assets/{sequenceDiagram-3UESZ5HK-FDb_1B1W.js → sequenceDiagram-3UESZ5HK-B9GkqtWb.js} +1 -1
- package/gui/dist/assets/{stateDiagram-AJRCARHV-BH1bq2it.js → stateDiagram-AJRCARHV-UoRM2vqX.js} +1 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-B7m-qaHs.js +1 -0
- package/gui/dist/assets/{timeline-definition-PNZ67QCA-DNUx0JIp.js → timeline-definition-PNZ67QCA-Dyx58rRr.js} +1 -1
- package/gui/dist/assets/{vennDiagram-CIIHVFJN-DEAfzbjb.js → vennDiagram-CIIHVFJN-IypthVr7.js} +1 -1
- package/gui/dist/assets/{wardley-L42UT6IY-_REPWPb-.js → wardley-L42UT6IY-DkLzWKUw.js} +1 -1
- package/gui/dist/assets/{wardleyDiagram-YWT4CUSO-CZFLXFE3.js → wardleyDiagram-YWT4CUSO-98VafNiZ.js} +1 -1
- package/gui/dist/assets/{xychartDiagram-2RQKCTM6-P2Nhif6M.js → xychartDiagram-2RQKCTM6-dZj2dDaC.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/gui/dist/assets/channel-DFvG2cZZ.js +0 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-B8xEoAMI.js +0 -1
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-B8xEoAMI.js +0 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-BfpDu1Qi.js +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Connect it to Cursor, Claude Desktop, or any MCP-compatible agent to let your AI
|
|
|
12
12
|
- **Track a branch, not a checkout** — point a project at a GitHub branch from its settings in the GUI and freshness is measured against *published* code: switching branches or editing without committing no longer makes docs look stale, and a server with no copy of the codebase (an MCP gateway) can still verify them
|
|
13
13
|
- **Semantic search** — find relevant docs by meaning (requires an OpenAI API key)
|
|
14
14
|
- **Multi-project support** — organize docs into separate projects; every MCP tool names the project it targets, and the GUI's project selector is a per-browser view preference
|
|
15
|
-
- **GUI** — built-in web interface, either behind the proxy (`PROXY_URL`) or served directly on a local port (`APP_PORT`); with neither, the GUI worker is not started. Navigation runs from an icon rail on the left: the current project's badge (click to switch projects or create one), then Documentation, Search (`Ctrl/⌘ + K`, current or all projects), Outdated, Recent, Export bundles and Logs, with Sync and Settings at the bottom. Each destination fills the panel next to the rail; the panel's hide button, or a second click on the active icon, folds it away. Drag the panel's right edge to resize it (double-click resets the width); the browser remembers the width. The details panel on the right of a document (`Ctrl/⌘ + \`) is open by default from 1280 px wide; showing or hiding it by hand is remembered across reloads until the window crosses that width. Badges show the outdated docs, pending commits and the day's failed GitHub calls
|
|
15
|
+
- **GUI** — built-in web interface, either behind the proxy (`PROXY_URL`) or served directly on a local port (`APP_PORT`); with neither, the GUI worker is not started. Navigation runs from an icon rail on the left: the current project's badge (click to switch projects or create one), then Documentation, Search (`Ctrl/⌘ + K`, current or all projects), Outdated, Recent, Export bundles and Logs, with Sync and Settings at the bottom. Each destination fills the panel next to the rail; the panel's hide button, or a second click on the active icon, folds it away. Drag the panel's right edge to resize it (double-click resets the width); the browser remembers the width. The details panel on the right of a document (`Ctrl/⌘ + \`) is open by default from 1280 px wide; showing or hiding it by hand is remembered across reloads until the window crosses that width. Badges show the outdated docs, pending commits and the day's failed GitHub calls. Each open tab polls `GET /api/changes` every 3 seconds and every request is answered immediately: no connection stays open, so many tabs can share the proxy's origin, and changes made by the agent show up within 3 seconds. After a GUI worker restart, or when it has fallen more than 200 changes behind, the tab reloads the server state (`GET /api/initial-state`) and the list of the project it shows
|
|
16
16
|
- **Proxy-aware** — integrates with the [proxy-setup](https://github.com/Imena/proxy-setup) for GUI exposure
|
|
17
17
|
- **Links & structure** — `check_links`, `get_doc_tree`, `move_doc`, and `git_diff_doc` for maintenance workflows
|
|
18
18
|
- **Image support** — upload images to a project's assets via `upload_image` (local file path) or directly from the GUI editor; images are stored per-project and rendered inline in the viewer and in PDF exports
|
|
@@ -320,7 +320,7 @@ Everything else is content-addressed: a commit's tree is cached by its sha for t
|
|
|
320
320
|
|
|
321
321
|
By default, data lives under **`.docs-data`** next to the resolved package root (the folder that contains `package.json` for the running install). Set **`MCP_DATA_DIR`** to use another directory. Contents include projects, SQLite/embeddings, and logs under `.docs-data/logs`. This tree is **not** part of the npm package; it is created at runtime.
|
|
322
322
|
|
|
323
|
-
There is no server-side "active project": every MCP tool call names its project, and the GUI's selector only changes what that browser displays (kept in the URL and in `localStorage`). A legacy `currentProject` left in `config.json` is dropped on first read. GitHub sync settings live in environment variables, not on disk.
|
|
323
|
+
There is no server-side "active project": every MCP tool call names its project, and the GUI's selector only changes what that browser displays (kept in the URL and in `localStorage`). The open and closed folders of each project's navigation tree are also kept in `localStorage`, so a reload restores them. A legacy `currentProject` left in `config.json` is dropped on first read. GitHub sync settings live in environment variables, not on disk.
|
|
324
324
|
|
|
325
325
|
## License
|
|
326
326
|
|
package/dist/src/gui-worker.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { serve } from "@hono/node-server";
|
|
2
2
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
3
3
|
import { Hono } from "hono";
|
|
4
|
-
import { streamSSE } from "hono/streaming";
|
|
5
4
|
import { GUI_LISTEN_HOST, ProxyClient, honoGuiGuard } from "@imenam/mcp-gui-interface";
|
|
6
5
|
import { IPCMessageSchema } from "../shared/schemas/doc.schema.js";
|
|
7
6
|
import { isReadOnly } from "../shared/read-only.js";
|
|
@@ -9,6 +8,7 @@ import path from "path";
|
|
|
9
8
|
import fs from "fs";
|
|
10
9
|
import { execFile, spawn } from "child_process";
|
|
11
10
|
import { Readable } from "stream";
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
12
|
import { promisify } from "util";
|
|
13
13
|
const execFileAsync = promisify(execFile);
|
|
14
14
|
import { fileURLToPath } from "url";
|
|
@@ -129,7 +129,6 @@ let guiPublicUrl = '';
|
|
|
129
129
|
const app = new Hono();
|
|
130
130
|
app.use("*", honoGuiGuard());
|
|
131
131
|
let proxyClient = null;
|
|
132
|
-
const sseControllers = new Set();
|
|
133
132
|
const pendingRequests = new Map();
|
|
134
133
|
function safeSend(message) {
|
|
135
134
|
if (process.send && process.connected) {
|
|
@@ -171,6 +170,27 @@ function ipcRequest(type, data, timeout = 5000) {
|
|
|
171
170
|
}
|
|
172
171
|
});
|
|
173
172
|
}
|
|
173
|
+
/** Message types the master pushes to every GUI, journaled for `/api/changes`. */
|
|
174
|
+
const BROADCAST_TYPES = [
|
|
175
|
+
"INITIAL_STATE",
|
|
176
|
+
"CONFIG_UPDATED",
|
|
177
|
+
"BUNDLES_UPDATED",
|
|
178
|
+
"PROJECTS_CHANGED",
|
|
179
|
+
"DOCS_CHANGED",
|
|
180
|
+
"CODE_SOURCES_CHANGED",
|
|
181
|
+
];
|
|
182
|
+
/** Number of pushed messages kept for clients catching up. */
|
|
183
|
+
const CHANGE_JOURNAL_SIZE = 200;
|
|
184
|
+
/** Identifies this worker run: a client holding another epoch has missed everything since. */
|
|
185
|
+
const workerEpoch = randomUUID();
|
|
186
|
+
let changeSeq = 0;
|
|
187
|
+
const changeJournal = [];
|
|
188
|
+
function recordChange(message) {
|
|
189
|
+
changeSeq += 1;
|
|
190
|
+
changeJournal.push({ seq: changeSeq, message });
|
|
191
|
+
if (changeJournal.length > CHANGE_JOURNAL_SIZE)
|
|
192
|
+
changeJournal.shift();
|
|
193
|
+
}
|
|
174
194
|
function setupIpcErrorHandlers() {
|
|
175
195
|
process.on("error", (err) => {
|
|
176
196
|
if (err.code === "ERR_IPC_CHANNEL_CLOSED")
|
|
@@ -179,7 +199,7 @@ function setupIpcErrorHandlers() {
|
|
|
179
199
|
});
|
|
180
200
|
process.on("disconnect", () => {
|
|
181
201
|
logger.warn("IPC disconnected from MCP master (parent closed the channel or process exited). " +
|
|
182
|
-
"GUI worker will exit
|
|
202
|
+
"GUI worker will exit.");
|
|
183
203
|
process.exit(0);
|
|
184
204
|
});
|
|
185
205
|
process.on("message", (msg) => {
|
|
@@ -199,24 +219,8 @@ function setupIpcErrorHandlers() {
|
|
|
199
219
|
});
|
|
200
220
|
return;
|
|
201
221
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"CONFIG_UPDATED",
|
|
205
|
-
"BUNDLES_UPDATED",
|
|
206
|
-
"PROJECTS_CHANGED",
|
|
207
|
-
"DOCS_CHANGED",
|
|
208
|
-
"CODE_SOURCES_CHANGED",
|
|
209
|
-
];
|
|
210
|
-
if (msg && BROADCAST_TYPES.includes(msg.type)) {
|
|
211
|
-
for (const controller of sseControllers) {
|
|
212
|
-
try {
|
|
213
|
-
controller.writeSSE({ data: JSON.stringify(msg), event: "message" });
|
|
214
|
-
}
|
|
215
|
-
catch {
|
|
216
|
-
sseControllers.delete(controller);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
222
|
+
if (msg && BROADCAST_TYPES.includes(msg.type))
|
|
223
|
+
recordChange(msg);
|
|
220
224
|
});
|
|
221
225
|
}
|
|
222
226
|
// --- HTTP Routes ---
|
|
@@ -251,54 +255,34 @@ app.get("/proxy/health", (c) => {
|
|
|
251
255
|
timestamp: new Date().toISOString(),
|
|
252
256
|
});
|
|
253
257
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
catch (err) {
|
|
276
|
-
logger.warn(`SSE /api/events: initial writeSSE failed (id=${clientId}): ${err?.message ?? err}`);
|
|
277
|
-
sseControllers.delete(stream);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
while (true) {
|
|
281
|
-
await stream.sleep(30000);
|
|
282
|
-
try {
|
|
283
|
-
await stream.writeSSE({ data: "ping", event: "ping" });
|
|
284
|
-
}
|
|
285
|
-
catch (err) {
|
|
286
|
-
logger.warn(`SSE /api/events: keepalive ping failed (id=${clientId}): ${err?.message ?? err}`);
|
|
287
|
-
sseControllers.delete(stream);
|
|
288
|
-
break;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
258
|
+
// Answers immediately with the pushed messages after `since`, or `reset: true` when the
|
|
259
|
+
// client must reload the whole state: no cursor, another worker run, or messages dropped.
|
|
260
|
+
app.get("/api/changes", (c) => {
|
|
261
|
+
const since = c.req.query("since");
|
|
262
|
+
const epoch = c.req.query("epoch");
|
|
263
|
+
if (since !== undefined && !/^\d+$/.test(since)) {
|
|
264
|
+
return c.json({ error: `since must be a non-negative integer, got "${since}"` }, 400);
|
|
265
|
+
}
|
|
266
|
+
const reset = { epoch: workerEpoch, seq: changeSeq, reset: true };
|
|
267
|
+
if (since === undefined || epoch !== workerEpoch)
|
|
268
|
+
return c.json(reset);
|
|
269
|
+
const from = Number(since);
|
|
270
|
+
const oldestKept = changeJournal.length > 0 ? changeJournal[0].seq : changeSeq + 1;
|
|
271
|
+
if (from > changeSeq || from < oldestKept - 1)
|
|
272
|
+
return c.json(reset);
|
|
273
|
+
return c.json({
|
|
274
|
+
epoch: workerEpoch,
|
|
275
|
+
seq: changeSeq,
|
|
276
|
+
reset: false,
|
|
277
|
+
events: changeJournal.filter((entry) => entry.seq > from).map((entry) => entry.message),
|
|
291
278
|
});
|
|
292
279
|
});
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
logger.warn(`Client diagnostics: invalid JSON body (${e?.message ?? e})`);
|
|
300
|
-
}
|
|
301
|
-
return c.json({ ok: true });
|
|
280
|
+
// Server-side state a client loads on start and after each reset.
|
|
281
|
+
app.get("/api/initial-state", async (c) => {
|
|
282
|
+
const result = await ipcRequest("GET_INITIAL_STATE");
|
|
283
|
+
if (result.error)
|
|
284
|
+
return c.json({ error: result.error }, 500);
|
|
285
|
+
return c.json(result.data);
|
|
302
286
|
});
|
|
303
287
|
app.get("/api/configurations", async (c) => {
|
|
304
288
|
const configRes = await ipcRequest("GET_CONFIG");
|
|
@@ -700,7 +684,7 @@ app.get("/api/export-zip/:id", async (c) => {
|
|
|
700
684
|
docId = ref;
|
|
701
685
|
}
|
|
702
686
|
else {
|
|
703
|
-
zip.file(`${ref}.md`, `# [Unqualified doc reference: ${ref} — no project given]
|
|
687
|
+
zip.file(`${ref}.md`, `# [Unqualified doc reference: ${ref} — no project given]
|
|
704
688
|
`);
|
|
705
689
|
continue;
|
|
706
690
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -120,7 +120,6 @@ let guiLauncher;
|
|
|
120
120
|
let guiStatus = "disabled";
|
|
121
121
|
let guiUrl = null;
|
|
122
122
|
let guiStartWaiter = null;
|
|
123
|
-
const sseControllers = new Set();
|
|
124
123
|
function broadcastToGUI(message) {
|
|
125
124
|
const child = guiLauncher.getProcess();
|
|
126
125
|
if (child?.connected) {
|
|
@@ -359,6 +358,14 @@ async function handleGetInitialState(msg) {
|
|
|
359
358
|
}
|
|
360
359
|
catch (err) {
|
|
361
360
|
logger.error(`Failed to get initial state: ${err.message}`);
|
|
361
|
+
if (msg.correlationId) {
|
|
362
|
+
broadcastToGUI({
|
|
363
|
+
type: "INITIAL_STATE",
|
|
364
|
+
correlationId: msg.correlationId,
|
|
365
|
+
error: err.message,
|
|
366
|
+
timestamp: new Date().toISOString(),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
362
369
|
}
|
|
363
370
|
}
|
|
364
371
|
async function handleGetConfig(msg) {
|
|
@@ -1 +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 rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-
|
|
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 rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-CrG0yj-K.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};
|