From 33379808919f918dd9d731e473771c0d2123f2ad Mon Sep 17 00:00:00 2001 From: ship Date: Wed, 26 Aug 2026 08:00:50 +0800 Subject: [PATCH] =?UTF-8?q?ship=201.4.54=EF=BC=9AArcrun@d3019a52cb18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- arcrun-cypher-executor/worker.mjs | 1617 ++++++++++++++++++++++++++++- arcrun-kbdb/worker.mjs | 225 +++- manifest.json | 56 +- tier2/ui/index.js | 4 +- 5 files changed, 1834 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 2bef3ae..6ab204d 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Served via jsDelivr; fetched automatically during install — you never need to - `arcrun-wait/` — **arcrun-wait**(首裝) - `daemon/` — 桌面 App(Mac/Windows)安裝檔 -Built from `Arcrun@db33cbe907dd` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.53,built 2026-08-24)。 +Built from `Arcrun@d3019a52cb18` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.54,built 2026-08-26)。 ⚠️ 這份檔案由出貨管線每次自動重寫(`installer/scripts/render-bundles-readme.mjs`)—— 不要手動改這裡列的零件清單——它是算出來的:公庫=Arcrun 這一版編了什麼, diff --git a/arcrun-cypher-executor/worker.mjs b/arcrun-cypher-executor/worker.mjs index ab3ba98..d2b3b9e 100644 --- a/arcrun-cypher-executor/worker.mjs +++ b/arcrun-cypher-executor/worker.mjs @@ -2436,7 +2436,7 @@ var init_recipes = __esm({ updated_at: now2 }; await installRecipeRecord(c.env.RECIPES, recipe); - const kbdbBase2 = (c.env.KBDB_BASE_URL ?? "https://kbdb.finally.click").replace(/\/$/, ""); + const kbdbBase3 = (c.env.KBDB_BASE_URL ?? "https://kbdb.finally.click").replace(/\/$/, ""); const evidence = { content: canonicalId, entry_type: "recipe_submission", @@ -2449,12 +2449,12 @@ var init_recipes = __esm({ submitted_at: now2 }) }; - const kbdbHeaders = { "Content-Type": "application/json" }; - if (c.env.KBDB_INTERNAL_TOKEN) kbdbHeaders["Authorization"] = `Bearer ${c.env.KBDB_INTERNAL_TOKEN}`; + const kbdbHeaders2 = { "Content-Type": "application/json" }; + if (c.env.KBDB_INTERNAL_TOKEN) kbdbHeaders2["Authorization"] = `Bearer ${c.env.KBDB_INTERNAL_TOKEN}`; c.executionCtx.waitUntil( - fetch(`${kbdbBase2}/entries`, { + fetch(`${kbdbBase3}/entries`, { method: "POST", - headers: kbdbHeaders, + headers: kbdbHeaders2, body: JSON.stringify(evidence) }).catch(() => void 0) ); @@ -9372,7 +9372,35 @@ async function mutateAuthStore(env, fn, tokenOverride) { } // cypher-executor/src/routes/health.ts +init_kbdb_proxy(); var healthRouter = new Hono2(); +var DATA_LAYER_CACHE_MS = 1e4; +var dataLayerCache = null; +async function dataLayerStatus(env) { + const now2 = Date.now(); + if (dataLayerCache && now2 - dataLayerCache.at < DATA_LAYER_CACHE_MS) return dataLayerCache.block; + let block; + try { + const { base, headers } = kbdbBase(env); + const res = await fetch(`${base}/health`, { headers, signal: AbortSignal.timeout(5e3) }); + const body = await res.json().catch(() => null); + block = body?.data_layer ?? { + ok: false, + reason: "kbdb_health_unreadable", + detail: `HTTP ${res.status}`, + summary: "\u9019\u53F0\u7684 kbdb \u9084\u6C92\u6709\u8CC7\u6599\u5C64\u4E16\u4EE3\u63A2\u91DD\uFF08\u6BD4 Arcrun#159 \u7684\u4FEE\u6CD5\u820A\uFF09\u2014\u2014\u72C0\u614B\u672A\u77E5\u3002" + }; + } catch (e) { + block = { + ok: false, + reason: "kbdb_unreachable", + detail: e instanceof Error ? e.message : String(e), + summary: "\u6253\u4E0D\u5230 kbdb\u2014\u2014\u8CC7\u6599\u5C64\u72C0\u614B\u672A\u77E5\uFF08\u672A\u77E5\u4E0D\u7B49\u65BC\u5065\u5EB7\uFF09\u3002" + }; + } + dataLayerCache = { at: now2, block }; + return block; +} function authStoreStatus(env) { const legacy = readAuthStore(env); return { @@ -9380,11 +9408,14 @@ function authStoreStatus(env) { portal_users: { home: "kbdb", writable: true, legacy_secrets_present: legacy.users.length > 0 } }; } -healthRouter.get("/health", (c) => { +healthRouter.get("/health", async (c) => { const bundleVersion = c.env.ARCRUN_BUNDLE_VERSION; const bundleCommit = c.env.ARCRUN_BUNDLE_COMMIT; + const dataLayer = await dataLayerStatus(c.env); return c.json({ ok: true, + status: dataLayer.ok ? "ok" : "degraded", + data_layer: dataLayer, ...bundleVersion ? { bundle_version: bundleVersion } : {}, ...bundleCommit ? { bundle_commit: bundleCommit } : {}, auth_store: authStoreStatus(c.env), @@ -14818,7 +14849,7 @@ async function cachedGiteaSprint(env, nowMs, waitUntil, fetcher = fetchGiteaSpri consoleDashboardRouter.get("/console/dashboard-data", async (c) => { const tenant2 = knowledgeOwner(c.env); const now2 = Date.now(); - const { base: kbdbUrl, headers: kbdbHeaders } = kbdbBase(c.env); + const { base: kbdbUrl, headers: kbdbHeaders2 } = kbdbBase(c.env); const graphUrl = graphBase(c.env); const [ beatEntries, @@ -14839,8 +14870,8 @@ consoleDashboardRouter.get("/console/dashboard-data", async (c) => { fetchEntries(c.env, tenant2, "dash_wait", 100), fetchEntries(c.env, tenant2, "inbox", 200), cachedGiteaSprint(c.env, now2, (p) => c.executionCtx.waitUntil(p)), - fetchJson(`${kbdbUrl}/health`, kbdbHeaders), - fetchJson(`${kbdbUrl}/embed/backfill/status`, kbdbHeaders), + fetchJson(`${kbdbUrl}/health`, kbdbHeaders2), + fetchJson(`${kbdbUrl}/embed/backfill/status`, kbdbHeaders2), // graph-plugin 只拿來判「圖服務活著沒」(燈號)——數字不從這裡拿,見 fetchTripletTotal。 // headers 一定要帶:plugin 的 /triplets 前綴掛 Bearer 閘,漏帶=永遠 401=永遠假紅燈(#100)。 fetchJson( @@ -15048,6 +15079,464 @@ init_webhook_handlers(); // cypher-executor/src/lib/app-system.ts init_kbdb_proxy(); init_webhook_handlers(); + +// cypher-executor/src/lib/asset-keys.ts +var ID_PREFIX = "arcrun"; +function classifyAssetKey(key) { + if (key.startsWith("idx:") || key.startsWith("cron-idx:")) return null; + if (key.startsWith("auth_recipe:")) { + const service = key.slice("auth_recipe:".length); + if (!service) return null; + return { + entry_type: "auth_recipe", + entry_id: `${ID_PREFIX}:auth_recipe:${service}`, + owner_id: null, + page_name: service, + kv_key: key + }; + } + if (key.startsWith("prompt_recipe:")) { + const name = key.slice("prompt_recipe:".length); + if (!name) return null; + return { + entry_type: "prompt_recipe", + entry_id: `${ID_PREFIX}:prompt_recipe:${name}`, + owner_id: null, + page_name: name, + kv_key: key + }; + } + if (key.startsWith("recipe:")) { + const id = key.slice("recipe:".length); + if (!id) return null; + return { + entry_type: "api_recipe", + entry_id: `${ID_PREFIX}:recipe:${id}`, + owner_id: null, + page_name: id, + kv_key: key + }; + } + const appAt = key.indexOf(":app:"); + if (appAt > 0) { + const owner = key.slice(0, appAt); + const id = key.slice(appAt + ":app:".length); + if (!owner || !id) return null; + return { + entry_type: "app_install", + entry_id: `${ID_PREFIX}:app:${owner}:${id}`, + owner_id: owner, + page_name: id, + kv_key: key + }; + } + const wfAt = key.indexOf(":wf:"); + if (wfAt > 0) { + const owner = key.slice(0, wfAt); + const name = key.slice(wfAt + ":wf:".length); + if (!owner || !name) return null; + return { + entry_type: "workflow_def", + entry_id: `${ID_PREFIX}:wf:${owner}:${name}`, + owner_id: owner, + page_name: name, + kv_key: key + }; + } + return null; +} +function assetKvKey(entryType, ownerId, pageName) { + switch (entryType) { + case "workflow_def": + return `${ownerId ?? ""}:wf:${pageName}`; + case "app_install": + return `${ownerId ?? ""}:app:${pageName}`; + case "api_recipe": + return `recipe:${pageName}`; + case "auth_recipe": + return `auth_recipe:${pageName}`; + case "prompt_recipe": + return `prompt_recipe:${pageName}`; + } +} +function classifyListPrefix(prefix) { + if (!prefix) return null; + if (prefix.startsWith("idx:") || prefix.startsWith("cron-idx:")) return null; + if (prefix === "auth_recipe:") return { entry_type: "auth_recipe" }; + if (prefix === "prompt_recipe:") return { entry_type: "prompt_recipe" }; + if (prefix === "recipe:") return { entry_type: "api_recipe" }; + if (prefix.endsWith(":wf:")) { + const owner = prefix.slice(0, -":wf:".length); + if (owner) return { entry_type: "workflow_def", owner_id: owner }; + } + if (prefix.endsWith(":app:")) { + const owner = prefix.slice(0, -":app:".length); + if (owner) return { entry_type: "app_install", owner_id: owner }; + } + return null; +} + +// cypher-executor/src/lib/durable-store.ts +function kbdbBase2(env) { + return (env.KBDB_BASE_URL ?? "https://arcrun-kbdb.uncle6-me.workers.dev").replace(/\/$/, ""); +} +function kbdbHeaders(env) { + const h = { "Content-Type": "application/json" }; + if (env.KBDB_INTERNAL_TOKEN) h["Authorization"] = `Bearer ${env.KBDB_INTERNAL_TOKEN}`; + return h; +} +var KbdbUnavailableError = class extends Error { + constructor(op, detail) { + super( + `\u8CC7\u7522\u7121\u6CD5\u5BEB\u5165 KBDB\uFF08${op}\uFF09\uFF1A${detail}\u3002\u672C\u6B21\u64CD\u4F5C\u5DF2\u4E2D\u6B62\u4E14\u672A\u5BEB\u5165\u4EFB\u4F55\u4E00\u908A\u2014\u2014\u9019\u662F\u523B\u610F\u7684\uFF1A\u5BE7\u53EF\u8B93\u4F60\u73FE\u5728\u770B\u5230\u5931\u6557\uFF0C\u4E5F\u4E0D\u8981\u5BEB\u9032\u53EA\u6703\u88AB\u4E0B\u6B21\u66F4\u65B0\u63DB\u6389\u7684 KV\u3001\u4E8B\u5F8C\u624D\u767C\u73FE\u6771\u897F\u4E0D\u898B\u4E86\uFF08Leo/Arcrun#16\u3001#17\uFF09\u3002` + ); + this.name = "KbdbUnavailableError"; + } +}; +function assetContent(type, def, pageName) { + const d = def ?? {}; + const pick = (...keys) => { + for (const k of keys) { + const v = d[k]; + if (typeof v === "string" && v.trim()) return v.trim(); + } + return ""; + }; + switch (type) { + case "workflow_def": + return pick("description") || pageName; + case "api_recipe": + return pick("description", "display_name", "canonical_id") || pageName; + case "auth_recipe": + return pick("description", "display_name", "service") || pageName; + case "prompt_recipe": + return pick("description", "name") || pageName; + case "app_install": + return pick("name") || pageName; + } +} +function entryToKvValue(entry) { + if (!entry?.metadata_json) return null; + try { + const env = JSON.parse(entry.metadata_json); + if (!env || env.arcrun_asset !== true) return null; + if (typeof env.definition_raw === "string") return env.definition_raw; + if (env.definition === void 0) return null; + return JSON.stringify(env.definition); + } catch { + return null; + } +} +var DurableKv = class { + constructor(kv, env) { + this.kv = kv; + this.env = env; + } + kv; + env; + rehydratedRecipeIdx = false; + rehydratedCronIdx = false; + /** + * 拿回底層那顆真正的 KV。**只有遷移/盤點會用到**(routes/storage.ts): + * 那兩支的工作正是「比較 KV 那邊有什麼、KBDB 這邊有什麼」, + * 若透過包裝去問,list 會被導去 KBDB,就永遠比不出差異、也搬不動舊資料。 + * 一般業務程式碼不該碰這支——碰了就等於繞過本卷的全部保護。 + */ + get rawKv() { + return this.kv; + } + // ── KBDB 存取原語 ────────────────────────────────────────────────────── + async kbdbGetEntry(entryId) { + const res = await fetch(`${kbdbBase2(this.env)}/entries/${encodeURIComponent(entryId)}`, { + headers: kbdbHeaders(this.env) + }); + if (res.status === 404) return null; + if (!res.ok) return null; + const json = await res.json().catch(() => null); + return json?.entry ?? null; + } + async kbdbListEntries(entryType, ownerId) { + const params = new URLSearchParams({ entry_type: entryType, limit: "1000" }); + if (ownerId) params.set("owner_id", ownerId); + const res = await fetch(`${kbdbBase2(this.env)}/entries?${params.toString()}`, { + headers: kbdbHeaders(this.env) + }); + if (!res.ok) throw new KbdbUnavailableError("list", `HTTP ${res.status}`); + const json = await res.json().catch(() => null); + return json?.entries ?? []; + } + async kbdbPutEntry(ref, envelope) { + const content = assetContent(ref.entry_type, envelope.definition, ref.page_name); + const res = await fetch(`${kbdbBase2(this.env)}/entries/${encodeURIComponent(ref.entry_id)}`, { + method: "PUT", + headers: kbdbHeaders(this.env), + body: JSON.stringify({ + entry_type: ref.entry_type, + owner_id: ref.owner_id, + page_name: ref.page_name, + content, + // 刻意**不**標 embed:true:工作流的語意搜尋走既有的 entry_type='workflow' 那一列 + //(workflow-discovery 方案 C 的雙寫),這裡標了會變成同一支工作流嵌兩份向量。 + metadata_json: JSON.stringify(envelope) + }) + }); + if (!res.ok) throw new KbdbUnavailableError("put", `HTTP ${res.status} @ ${ref.entry_id}`); + } + async kbdbDeleteEntry(entryId) { + const res = await fetch(`${kbdbBase2(this.env)}/entries/${encodeURIComponent(entryId)}`, { + method: "DELETE", + headers: kbdbHeaders(this.env) + }); + if (!res.ok && res.status !== 404) throw new KbdbUnavailableError("delete", `HTTP ${res.status} @ ${entryId}`); + } + // ── 衍生索引重建(讀不到就重算,不進 KBDB)──────────────────────────── + /** + * 從 KBDB 的 api_recipe 列重建 recipe 反查索引: + * idx:{hash_id} → canonical_id + * idx:canonical:{canonical} → [uuid, ...] + * idx:installed:{canonical} → uuid + * + * installed 的還原順序:先看資產自己標的 installed 旗標(正常路徑,寫入時就記下了, + * 見 put() 對 `idx:installed:` 的處理);同一個 canonical 沒有任何一版標記時 + *(=遷移之前就存在的舊資料),退而取 updated_at 最新的那一版——因為 + * installRecipeRecord 的語意本來就是「最後寫入的那版即為安裝版」。 + * 這是還原不是猜測,但仍是**退路**,故在此寫明白。 + */ + async rehydrateRecipeIndices() { + if (this.rehydratedRecipeIdx) return; + this.rehydratedRecipeIdx = true; + const entries = await this.kbdbListEntries("api_recipe"); + const byCanonical = /* @__PURE__ */ new Map(); + const writes = []; + for (const e of entries) { + const raw2 = entryToKvValue(e); + if (!raw2) continue; + let def; + try { + def = JSON.parse(raw2); + } catch { + continue; + } + if (!def.canonical_id) continue; + if (def.hash_id) writes.push(this.kv.put(`idx:${def.hash_id}`, def.canonical_id)); + if (!def.uuid) continue; + let installed = false; + try { + installed = JSON.parse(e.metadata_json ?? "{}").installed === true; + } catch { + } + const list = byCanonical.get(def.canonical_id) ?? []; + list.push({ uuid: def.uuid, installed, updated_at: e.updated_at ?? 0 }); + byCanonical.set(def.canonical_id, list); + } + for (const [canonical, versions] of byCanonical) { + writes.push(this.kv.put(`idx:canonical:${canonical}`, JSON.stringify(versions.map((v) => v.uuid)))); + const chosen = versions.find((v) => v.installed) ?? versions.reduce((a, b) => b.updated_at > a.updated_at ? b : a); + writes.push(this.kv.put(`idx:installed:${canonical}`, chosen.uuid)); + } + await Promise.all(writes); + } + /** 從 KBDB 的 workflow_def 列重建 cron 索引(單一 key,見 lib/cron-index.ts)。 */ + async rehydrateCronIndex() { + if (this.rehydratedCronIdx) return; + this.rehydratedCronIdx = true; + const entries = await this.kbdbListEntries("workflow_def"); + const index = {}; + for (const e of entries) { + const raw2 = entryToKvValue(e); + if (!raw2) continue; + let def; + try { + def = JSON.parse(raw2); + } catch { + continue; + } + if (!def.cron_expr || !e.owner_id || !e.page_name) continue; + index[cronEntryKey(e.owner_id, e.page_name)] = def.cron_expr; + } + await this.kv.put(CRON_INDEX_KEY, JSON.stringify(index)); + } + // ── KVNamespace 介面 ─────────────────────────────────────────────────── + async get(key, type) { + const t0 = typeof type === "string" ? type : type?.type; + if (t0 === "arrayBuffer" || t0 === "stream") return this.kv.get(key, t0); + const asText = (raw2) => { + if (raw2 === null) return null; + const t = typeof type === "string" ? type : type?.type; + if (t === "json") { + try { + return JSON.parse(raw2); + } catch { + return null; + } + } + return raw2; + }; + const ref = classifyAssetKey(key); + if (!ref) { + const raw2 = await this.kv.get(key, "text"); + if (raw2 !== null) return asText(raw2); + if (key.startsWith("idx:")) { + await this.rehydrateRecipeIndices().catch(() => { + }); + return asText(await this.kv.get(key, "text")); + } + if (key === CRON_INDEX_KEY) { + await this.rehydrateCronIndex().catch(() => { + }); + return asText(await this.kv.get(key, "text")); + } + return asText(raw2); + } + const cached = await this.kv.get(key, "text"); + if (cached !== null) return asText(cached); + const fromKbdb = entryToKvValue(await this.kbdbGetEntry(ref.entry_id)); + if (fromKbdb === null) return asText(null); + await this.kv.put(key, fromKbdb).catch(() => { + }); + return asText(fromKbdb); + } + async put(key, value, options) { + if (options?.expirationTtl || options?.expiration || typeof value !== "string") { + return this.kv.put(key, value, options); + } + if (key.startsWith("idx:installed:")) { + await this.kv.put(key, value, options); + await this.markInstalledVersion(key.slice("idx:installed:".length), value).catch(() => { + }); + return; + } + const ref = classifyAssetKey(key); + if (!ref) return this.kv.put(key, value, options); + let definition; + let definitionRaw; + try { + definition = JSON.parse(value); + } catch { + definitionRaw = value; + } + const previous = ref.entry_type === "api_recipe" ? await this.readEnvelope(ref) : null; + await this.kbdbPutEntry(ref, { + arcrun_asset: true, + kv_key: key, + ...definitionRaw !== void 0 ? { definition_raw: definitionRaw } : { definition }, + ...previous?.installed ? { installed: true } : {} + }); + await this.kv.put(key, value, options); + } + async delete(key) { + const ref = classifyAssetKey(key); + if (ref) await this.kbdbDeleteEntry(ref.entry_id); + await this.kv.delete(key); + } + async list(options) { + const target = classifyListPrefix(options?.prefix ?? void 0); + if (!target) return this.kv.list(options); + const entries = await this.kbdbListEntries(target.entry_type, target.owner_id); + const keys = []; + const warm = []; + for (const e of entries) { + if (!e.page_name) continue; + if (target.entry_type === "workflow_def" && !e.owner_id) continue; + const name = assetKvKey(target.entry_type, e.owner_id ?? null, e.page_name); + keys.push({ name }); + const raw2 = entryToKvValue(e); + if (raw2 !== null) warm.push(this.kv.put(name, raw2).catch(() => { + })); + } + await Promise.all(warm); + if (target.entry_type === "app_install") { + const seen = new Set(keys.map((k) => k.name)); + const fromKv = await this.kv.list({ prefix: options?.prefix ?? void 0 }); + const heal = []; + for (const k of fromKv.keys) { + if (seen.has(k.name)) continue; + keys.push({ name: k.name }); + heal.push( + (async () => { + const raw2 = await this.kv.get(k.name, "text"); + if (raw2 === null) return; + const ref = classifyAssetKey(k.name); + if (!ref) return; + let definition; + let definitionRaw; + try { + definition = JSON.parse(raw2); + } catch { + definitionRaw = raw2; + } + await this.kbdbPutEntry(ref, { + arcrun_asset: true, + kv_key: k.name, + ...definitionRaw !== void 0 ? { definition_raw: definitionRaw } : { definition } + }); + })().catch(() => { + }) + ); + } + await Promise.all(heal); + } + return { keys, list_complete: true, cacheStatus: null }; + } + /** KVNamespace 介面補齊(本 worker 沒有呼叫端在用,原樣轉發,不做資產處理)。 */ + getWithMetadata(key, type) { + return this.kv.getWithMetadata(key, type); + } + // ── 內部小工具 ───────────────────────────────────────────────────────── + async readEnvelope(ref) { + const entry = await this.kbdbGetEntry(ref.entry_id); + if (!entry?.metadata_json) return null; + try { + const env = JSON.parse(entry.metadata_json); + return env?.arcrun_asset === true ? env : null; + } catch { + return null; + } + } + /** 把「這個 canonical 目前裝的是哪一版」記進該版 recipe 的信封(同 canonical 的其他版清掉旗標)。 */ + async markInstalledVersion(canonicalId, uuid) { + const entries = await this.kbdbListEntries("api_recipe"); + const jobs = []; + for (const e of entries) { + const raw2 = entryToKvValue(e); + if (!raw2) continue; + let def; + try { + def = JSON.parse(raw2); + } catch { + continue; + } + if (def.canonical_id !== canonicalId || !def.uuid) continue; + const shouldBeInstalled = def.uuid === uuid; + let envelope; + try { + envelope = JSON.parse(e.metadata_json ?? "{}"); + } catch { + continue; + } + if (envelope.installed === true === shouldBeInstalled) continue; + envelope.installed = shouldBeInstalled; + jobs.push( + fetch(`${kbdbBase2(this.env)}/entries/${encodeURIComponent(e.id)}`, { + method: "PATCH", + headers: kbdbHeaders(this.env), + body: JSON.stringify({ metadata_json: JSON.stringify(envelope) }) + }) + ); + } + await Promise.all(jobs); + } +}; + +// cypher-executor/src/lib/app-system.ts +function appStore(env) { + return new DurableKv(env.WEBHOOKS, env); +} +function kbdbBehindHint(e) { + const msg = e instanceof Error ? `${e.name}: ${e.message}` : String(e); + if (!msg.includes("KbdbUnavailable")) return null; + return "\u9019\u53F0\u5BE6\u4F8B\u7684\u77E5\u8B58\u5EAB\uFF08KBDB\uFF09\u6C92\u6709\u56DE\u61C9\uFF0C\u6216\u5B83\u7684\u7248\u672C\u6BD4\u76EE\u524D\u7684\u7A0B\u5F0F\u78BC\u820A\u2014\u2014\u5B89\u88DD\u9700\u8981\u77E5\u8B58\u5EAB\u63D0\u4F9B\u300C\u540C\u4E00\u4EFD\u8CC7\u7522\u6C38\u9060\u540C\u4E00\u5217\u300D\u7684\u5BEB\u5165\u80FD\u529B\u3002\u8ACB\u7BA1\u7406\u8005\u628A\u77E5\u8B58\u5EAB\u4E00\u8D77\u66F4\u65B0\u5230\u540C\u4E00\u7248\uFF1B\u5728\u90A3\u4E4B\u524D**\u5DF2\u7D93\u88DD\u597D\u7684 App \u4E0D\u53D7\u5F71\u97FF**\uFF0C\u7167\u5E38\u53EF\u7528\u3002"; +} var APP_UI_STYLES = ["inherit", "own"]; var DEFAULT_APP_UI_STYLE = "inherit"; var ID_RE = /^[a-z][a-z0-9_-]{0,63}$/; @@ -15119,6 +15608,17 @@ function validateAppDeclaration(raw2) { } if (d.actions !== void 0 && (!Array.isArray(d.actions) || d.actions.some((a) => typeof a !== "string"))) { errors.push("actions \u5FC5\u9808\u662F\u5B57\u4E32\u9663\u5217"); + } else if (Array.isArray(d.actions) && Array.isArray(d.workflows)) { + const wfNames = new Set( + d.workflows.map((w) => w && typeof w === "object" ? w.name : void 0).filter((n) => typeof n === "string") + ); + for (const a of d.actions) { + if (!wfNames.has(a)) { + errors.push( + `actions \u88E1\u7684\u300C${a}\u300D\u627E\u4E0D\u5230\u540C\u540D\u7684 workflow\uFF08\u52D5\u4F5C\u540D\u5C31\u662F\u5DE5\u4F5C\u6D41\u672C\u5730\u540D\uFF09\u3002\u9019\u4EFD\u5BA3\u544A\u7684\u5DE5\u4F5C\u6D41\u662F\uFF1A${[...wfNames].join("\uFF0F") || "\uFF08\u4E00\u500B\u90FD\u6C92\u6709\uFF09"}` + ); + } + } } return { ok: errors.length === 0, errors }; } @@ -15143,6 +15643,26 @@ function applyDeclarationDefaults(raw2) { keeps_data: raw2.remove?.keeps_data ?? true }; } +var APP_PLACEHOLDERS = ["__NAMESPACE__", "__CYPHER_BASE__"]; +function resolveDeclarationPlaceholders(value, coords) { + const base = coords.cypherBase.replace(/\/+$/, ""); + const swap = (s) => s.split("__NAMESPACE__").join(coords.namespace).split("__CYPHER_BASE__").join(base); + const walk = (v) => { + if (typeof v === "string") return swap(v); + if (Array.isArray(v)) return v.map(walk); + if (v && typeof v === "object") { + const out = {}; + for (const [k, val] of Object.entries(v)) out[swap(k)] = walk(val); + return out; + } + return v; + }; + return walk(value); +} +function remainingPlaceholders(value) { + const text = JSON.stringify(value) ?? ""; + return APP_PLACEHOLDERS.filter((p) => text.includes(p)); +} async function computeContentHash(raw2) { const canonical = canonicalize(raw2); const data = new TextEncoder().encode(canonical); @@ -15164,7 +15684,7 @@ function workflowKvKey(tenant2, wfKey) { return `${tenant2}:wf:${wfKey}`; } async function getInstalledApp(env, tenant2, id) { - const raw2 = await env.WEBHOOKS.get(appKey(tenant2, id), "text"); + const raw2 = await appStore(env).get(appKey(tenant2, id), "text"); if (!raw2) return null; try { return JSON.parse(raw2); @@ -15174,10 +15694,10 @@ async function getInstalledApp(env, tenant2, id) { } async function listInstalledApps(env, tenant2) { const prefix = `${tenant2}:app:`; - const list = await env.WEBHOOKS.list({ prefix }); + const list = await appStore(env).list({ prefix }); const apps = await Promise.all( list.keys.map(async (k) => { - const raw2 = await env.WEBHOOKS.get(k.name, "text"); + const raw2 = await appStore(env).get(k.name, "text"); if (!raw2) return null; try { return JSON.parse(raw2); @@ -15201,36 +15721,83 @@ async function ensureTemplate(env, name, slots, description) { body: JSON.stringify({ name, slots, description }) }); } -async function installApp(env, tenant2, rawDecl) { - const validation = validateAppDeclaration(rawDecl); - if (!validation.ok) return { ok: false, errors: validation.errors }; - const decl = applyDeclarationDefaults(rawDecl); - const contentHash = await computeContentHash(rawDecl); +async function installApp(env, tenant2, rawDecl, opts = {}) { + let decl0 = rawDecl; + if (opts.coords) { + try { + decl0 = resolveDeclarationPlaceholders(rawDecl, opts.coords); + } catch (e) { + return { + ok: false, + step: "\u88DC\u4E0A\u9019\u53F0\u5BE6\u4F8B\u7684\u4F4D\u5740", + errors: [e instanceof Error ? e.message : String(e)], + hint: "\u9019\u662F\u7CFB\u7D71\u5167\u90E8\u7684\u554F\u984C\uFF0C\u4E0D\u662F\u4F60\u505A\u932F\u4E86\u3002\u628A\u9019\u53E5\u8A71\u56DE\u5831\u7D66\u7BA1\u7406\u8005\u5373\u53EF\u3002" + }; + } + } + const left = remainingPlaceholders(decl0); + if (left.length > 0) { + return { + ok: false, + step: "\u88DC\u4E0A\u9019\u53F0\u5BE6\u4F8B\u7684\u4F4D\u5740", + errors: [`\u9019\u4EFD\u5BA3\u544A\u88E1\u9084\u6709\u6C92\u586B\u7684\u4F4D\u7F6E\uFF1A${left.join("\u3001")}`], + hint: "\u9019\u500B App \u7684\u4F5C\u8005\u7559\u4E86\u8981\u7531\u5BE6\u4F8B\u586B\u7684\u6B04\u4F4D\uFF0C\u4F46\u9019\u53F0\u5BE6\u4F8B\u6C92\u586B\u5F97\u8D77\u4F86\u3002\u8ACB\u56DE\u5831\u7D66\u7BA1\u7406\u8005\u3002" + }; + } + const validation = validateAppDeclaration(decl0); + if (!validation.ok) { + return { + ok: false, + step: "\u6AA2\u67E5\u9019\u500B App \u7684\u5BA3\u544A", + errors: validation.errors, + hint: "\u9019\u662F App \u4F5C\u8005\u90A3\u908A\u8981\u4FEE\u7684\uFF0C\u91CD\u8A66\u4E0D\u6703\u8B8A\u597D\u3002\u628A\u4E0A\u9762\u9019\u5E7E\u884C\u539F\u6587\u56DE\u5831\u7D66\u9019\u500B App \u7684\u4F5C\u8005\u3002" + }; + } + const decl = applyDeclarationDefaults(decl0); + const contentHash = await computeContentHash(decl0); const existing = await getInstalledApp(env, tenant2, decl.id); if (existing && existing.content_hash === contentHash && existing.status === "active") { return { ok: true, changed: false, app: existing }; } - for (const dt of decl.data) { - await ensureTemplate(env, dt.name, dt.slots, dt.description); + try { + for (const dt of decl.data) { + await ensureTemplate(env, dt.name, dt.slots, dt.description); + } + } catch (e) { + return { + ok: false, + step: "\u6E96\u5099\u8CC7\u6599\u578B\u5225", + errors: [e instanceof Error ? e.message : String(e)], + hint: "\u9019\u53F0\u5BE6\u4F8B\u7684\u77E5\u8B58\u5EAB\u670D\u52D9\u6C92\u6709\u56DE\u61C9\u3002\u7A0D\u7B49\u4E00\u5206\u9418\u518D\u6309\u4E00\u6B21\u5B89\u88DD\uFF1B\u4E00\u76F4\u5931\u6557\u5C31\u628A\u9019\u53E5\u8A71\u56DE\u5831\u7D66\u7BA1\u7406\u8005\u3002" + }; } const workflowRefs = []; - for (const wf of decl.workflows) { - const wfKey = `${decl.id}__${wf.name}`; - await env.WEBHOOKS.put( - workflowKvKey(tenant2, wfKey), - JSON.stringify({ - graph: wf.graph, - description: wf.description || `${decl.name}: ${wf.name}`, - created_at: existing?.installed_at ?? (/* @__PURE__ */ new Date()).toISOString() - }) - ); - workflowRefs.push({ name: wf.name, wf_key: wfKey, description: wf.description || "" }); + try { + for (const wf of decl.workflows) { + const wfKey = `${decl.id}__${wf.name}`; + await appStore(env).put( + workflowKvKey(tenant2, wfKey), + JSON.stringify({ + graph: wf.graph, + description: wf.description || `${decl.name}: ${wf.name}`, + created_at: existing?.installed_at ?? (/* @__PURE__ */ new Date()).toISOString() + }) + ); + workflowRefs.push({ name: wf.name, wf_key: wfKey, description: wf.description || "" }); + } + } catch (e) { + return { + ok: false, + step: "\u5B89\u88DD\u5DE5\u4F5C\u6D41", + errors: [e instanceof Error ? e.message : String(e)], + hint: kbdbBehindHint(e) ?? `\u5DF2\u7D93\u88DD\u4E0A ${workflowRefs.length}\uFF0F${decl.workflows.length} \u689D\u3002\u518D\u6309\u4E00\u6B21\u5B89\u88DD\u6703\u5F9E\u982D\u8986\u5BEB\u4E00\u904D\uFF1B\u4E00\u76F4\u5931\u6557\u5C31\u628A\u9019\u53E5\u8A71\u56DE\u5831\u7D66\u7BA1\u7406\u8005\u3002` + }; } if (existing) { const keepKeys = new Set(workflowRefs.map((w) => w.wf_key)); for (const prev of existing.workflows) { if (!keepKeys.has(prev.wf_key)) { - await env.WEBHOOKS.delete(workflowKvKey(tenant2, prev.wf_key)); + await appStore(env).delete(workflowKvKey(tenant2, prev.wf_key)); } } } @@ -15252,16 +15819,32 @@ async function installApp(env, tenant2, rawDecl) { updated_at: (/* @__PURE__ */ new Date()).toISOString(), status: "active" }; - await env.WEBHOOKS.put(appKey(tenant2, decl.id), JSON.stringify(record)); + try { + await appStore(env).put(appKey(tenant2, decl.id), JSON.stringify(record)); + } catch (e) { + return { + ok: false, + step: "\u5BEB\u5165\u5B89\u88DD\u7D00\u9304", + errors: [e instanceof Error ? e.message : String(e)], + hint: kbdbBehindHint(e) ?? "\u5DE5\u4F5C\u6D41\u5DF2\u7D93\u88DD\u597D\u4E86\uFF0C\u53EA\u5DEE\u6700\u5F8C\u9019\u4E00\u7B46\u7D00\u9304\u2014\u2014\u518D\u6309\u4E00\u6B21\u5B89\u88DD\u5C31\u6703\u88DC\u4E0A\u3002" + }; + } return { ok: true, changed: true, app: record }; } async function uninstallApp(env, tenant2, id) { const existing = await getInstalledApp(env, tenant2, id); if (!existing) return { ok: false, error: "\u9019\u500B App \u6C92\u6709\u5B89\u88DD\u7D00\u9304" }; - for (const wf of existing.workflows) { - await env.WEBHOOKS.delete(workflowKvKey(tenant2, wf.wf_key)); + try { + for (const wf of existing.workflows) { + await appStore(env).delete(workflowKvKey(tenant2, wf.wf_key)); + } + await appStore(env).delete(appKey(tenant2, id)); + } catch (e) { + return { + ok: false, + error: kbdbBehindHint(e) ?? `\u79FB\u9664\u5931\u6557\uFF1A${e instanceof Error ? e.message : String(e)}` + }; } - await env.WEBHOOKS.delete(appKey(tenant2, id)); return { ok: true }; } async function runAppAction(env, tenant2, appId, action, payload, ctx) { @@ -15272,7 +15855,7 @@ async function runAppAction(env, tenant2, appId, action, payload, ctx) { } const wfRef = app2.workflows.find((w) => w.name === action); if (!wfRef) return { ok: false, status: 500, error: "\u52D5\u4F5C\u5C0D\u61C9\u7684\u5DE5\u4F5C\u6D41\u907A\u5931\uFF08\u5B89\u88DD\u614B\u640D\u6BC0\uFF09" }; - const raw2 = await env.WEBHOOKS.get(workflowKvKey(tenant2, wfRef.wf_key), "text"); + const raw2 = await appStore(env).get(workflowKvKey(tenant2, wfRef.wf_key), "text"); if (!raw2) return { ok: false, status: 500, error: "\u5DE5\u4F5C\u6D41\u8CC7\u6599\u907A\u5931\uFF08\u5B89\u88DD\u614B\u640D\u6BC0\uFF09" }; let graph; try { @@ -15307,6 +15890,899 @@ function detailApp(app2) { }; } +// cypher-executor/src/lib/app-catalog/notes.json +var notes_default = { + id: "notes", + name: "\u7B46\u8A18", + icon: "\u{1F4DD}", + workflows: [ + { + name: "create_note", + description: "\u6536\u4E00\u6BB5 markdown \u5167\u5BB9\uFF08\u53EF\u9078\u65E5\u671F\uFF09\uFF0C\u5BEB\u5165\u4E00\u7B46 note record\uFF0C\u56DE\u50B3\u6574\u7406\u904E\u7684\u4E7E\u6DE8\u6B04\u4F4D \uFF08\u4E0D\u662F KBDB \u539F\u59CB\u56DE\u61C9\u2014\u2014\u90A3\u500B\u8981\u547C\u53EB\u7AEF\u81EA\u5DF1\u525D body \u5B57\u4E32\uFF0C\u4E0D\u8A72\u8B93\u524D\u7AEF\u505A\u9019\u4EF6\u4E8B\uFF09\u3002\n", + graph: { + id: "notes_create", + name: "notes_create", + nodes: [ + { + id: "input", + type: "Input", + label: "input" + }, + { + id: "prep", + type: "Component", + label: "prep", + componentId: "code", + data: { + code: `// \u{1F534} \u771F\u5BE6\u8E29\u904E\u7684\u5751\uFF08\u672C\u6A5F\u7528 matrix/arcrun \u7684 GraphExecutor \u771F\u8DD1\u904E\u624D\u767C\u73FE\uFF0C\u4E0D\u662F\u731C\u7684\uFF09\uFF1A +// {{input.xxx}} \u5728 xxx \u9019\u500B key \u771F\u7684\u4E0D\u5B58\u5728\u6642\uFF0CGraphExecutor \u7684 interpolateString +// \u6703\u56DE\u300C\u539F\u6A23\u4FDD\u7559\u9019\u500B {{...}} \u5B57\u9762\u5B57\u4E32\u300D\uFF0C\u4E0D\u662F undefined\u3001\u4E5F\u4E0D\u662F\u7A7A\u5B57\u4E32 +// \uFF08matrix/arcrun/cypher-executor/src/graph-executor.ts interpolateString \u7684\u65E2\u6709\u8A2D\u8A08\uFF1A +// \u300C\u627E\u4E0D\u5230\u5C31\u4FDD\u7559\u5B57\u9762\u5B57\u4E32\u300D\uFF0C\u662F\u5F15\u64CE\u6545\u610F\u7684\u884C\u70BA\uFF0C\u4E0D\u662F bug\uFF09\u3002 +// \u21D2 \u547C\u53EB\u7AEF\u6F0F\u5E36 date \u6642\uFF0C\u9019\u88E1\u6536\u5230\u7684 input.date \u4E0D\u662F undefined\uFF0C\u662F\u5B57\u4E32 "{{input.date}}" +// \u2014\u2014\u82E5\u53EA\u5224\u65B7 == null || === ''\uFF0C\u9019\u500B\u5B57\u9762\u5B57\u4E32\u6703\u88AB\u8AA4\u5224\u6210\u300C\u6709\u7D66\u503C\u300D\uFF0C +// \u7136\u5F8C\u5728 YYYY-MM-DD \u683C\u5F0F\u6AA2\u67E5\u90A3\u95DC\u5931\u6557\uFF0C\u8B93\u300C\u4E0D\u5E36 date \u5C31\u7528\u4ECA\u5929\u300D\u6574\u500B\u5931\u6548\u3002 +// \u4FEE\u6CD5\uFF1A\u591A\u5224\u4E00\u7A2E\u300C\u770B\u8D77\u4F86\u50CF\u6C92\u89E3\u958B\u7684\u6A21\u677F\u300D\u7684\u5F62\u72C0\uFF0C\u8996\u540C\u6C92\u7D66\u3002 +function provided(v) { + if (typeof v !== 'string') return false; + var t = v.trim(); + return t !== '' && !/^\\{\\{[\\s\\S]*\\}\\}$/.test(t); +} +const content = provided(input.content) ? String(input.content).trim() : ''; +if (!content) return { success: false, error: 'content \u4E0D\u53EF\u70BA\u7A7A\uFF08\u7B46\u8A18\u81F3\u5C11\u8981\u6709\u5167\u5BB9\uFF09' }; +const now = new Date(); +const rawDate = provided(input.date) ? String(input.date).trim() : now.toISOString().slice(0, 10); +if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(rawDate)) { + return { success: false, error: 'date \u5FC5\u9808\u662F YYYY-MM-DD \u683C\u5F0F\uFF08\u6536\u5230\uFF1A' + rawDate + '\uFF09' }; +} +return { success: true, content: content, date: rawDate, created_at: now.toISOString() }; +`, + input: { + content: "{{input.content}}", + date: "{{input.date}}" + }, + limits: { + timeout_ms: 1e3, + max_output_bytes: 8192 + } + } + }, + { + id: "write_note", + type: "Component", + label: "write_note", + componentId: "http_request", + data: { + method: "POST", + url: "__CYPHER_BASE__/kbdb/records", + headers: { + "Content-Type": "application/json", + "X-Arcrun-API-Key": "__NAMESPACE__" + }, + body_json: { + template: "note", + values: { + date: "{{prep.data.date}}", + content: "{{prep.data.content}}", + created_at: "{{prep.data.created_at}}" + } + } + } + }, + { + id: "extract_note", + type: "Component", + label: "extract_note", + componentId: "code", + data: { + code: "function parse(b) { if (typeof b === 'string') { try { return JSON.parse(b); } catch (e) { return null; } } return b; }\nconst body = parse(input.body);\nif (!body || body.success !== true || !body.record) {\n return { success: false, error: (body && body.error) || 'KBDB \u5BEB\u5165\u6C92\u6709\u56DE\u53EF\u7528\u7684 record' };\n}\nconst v = body.record.values || {};\nreturn {\n success: true,\n record_id: body.record.record_id,\n date: v.date || '',\n content: v.content || '',\n created_at: v.created_at || '',\n};\n", + input: { + body: "{{write_note.data.body}}" + }, + limits: { + timeout_ms: 1e3, + max_output_bytes: 65536 + } + } + } + ], + edges: [ + { + from: "input", + to: "prep", + type: "ON_SUCCESS" + }, + { + from: "prep", + to: "write_note", + type: "ON_SUCCESS" + }, + { + from: "write_note", + to: "extract_note", + type: "ON_SUCCESS" + } + ] + } + }, + { + name: "list_notes", + description: "\u53D6\u56DE\u6CB3\u9053\uFF1A\u9810\u8A2D\u5168\u90E8\uFF08\u4F9D created_at \u65B0\u5230\u820A\uFF09\uFF0C\u5E36 date \u53EA\u56DE\u90A3\u4E00\u5929\u7684\u7B46\u8A18\u3002\n", + graph: { + id: "notes_list", + name: "notes_list", + nodes: [ + { + id: "input", + type: "Input", + label: "input" + }, + { + id: "fetch_notes", + type: "Component", + label: "fetch_notes", + componentId: "http_request", + data: { + method: "GET", + url: "__CYPHER_BASE__/kbdb/records/by-template/note?limit=500", + headers: { + Accept: "application/json", + "X-Arcrun-API-Key": "__NAMESPACE__" + } + } + }, + { + id: "filter_sort", + type: "Component", + label: "filter_sort", + componentId: "code", + data: { + code: "// \u{1F534} \u771F\u5BE6\u8E29\u904E\u7684\u5751\uFF08\u672C\u6A5F\u7528 matrix/arcrun \u7684 GraphExecutor \u771F\u8DD1\u904E\u624D\u767C\u73FE\u2014\u2014\n// \u4E0D\u52A0\u9019\u6BB5\u9632\u8B77\u6642\uFF0C\u300C\u4E0D\u5E36 date\uFF1D\u67E5\u5168\u90E8\u300D\u9019\u500B\u6700\u5E38\u7528\u7684\u9810\u8A2D\u8DEF\u5F91\u6703\u975C\u9ED8\u56DE\u50B3 0 \u7B46\uFF0C\n// \u8A73\u7D30\u6210\u56E0\u898B notes-create.yaml \u7684 prep \u7BC0\u9EDE\u540C\u6B3E\u8A3B\u89E3\uFF0C\u9019\u88E1\u4E0D\u91CD\u8907\u8CBC\u4E00\u6B21\uFF09\u3002\nfunction provided(v) {\n if (typeof v !== 'string') return false;\n var t = v.trim();\n return t !== '' && !/^\\{\\{[\\s\\S]*\\}\\}$/.test(t);\n}\nfunction parse(b) { if (typeof b === 'string') { try { return JSON.parse(b); } catch (e) { return null; } } return b; }\nconst body = parse(input.body) || {};\nconst records = Array.isArray(body.records) ? body.records : [];\nconst wantDate = provided(input.date) ? String(input.date).trim() : '';\nlet notes = records.map(function (r) {\n const v = r.values || {};\n return {\n record_id: r.record_id,\n date: v.date || '',\n content: v.content || '',\n created_at: v.created_at || ''\n };\n});\nif (wantDate) notes = notes.filter(function (n) { return n.date === wantDate; });\nnotes.sort(function (a, b) { return (b.created_at || '').localeCompare(a.created_at || ''); });\nreturn { success: true, notes: notes, count: notes.length, date: wantDate || null };\n", + input: { + body: "{{fetch_notes.data.body}}", + date: "{{input.date}}" + }, + limits: { + timeout_ms: 3e3, + max_output_bytes: 4194304 + } + } + } + ], + edges: [ + { + from: "input", + to: "fetch_notes", + type: "ON_SUCCESS" + }, + { + from: "fetch_notes", + to: "filter_sort", + type: "ON_SUCCESS" + } + ] + } + } + ], + ui: { + html: ` + + + + +\u7B46\u8A18 + + + + +
+ + +
+

\u7B46\u8A18

+
+ +
+ + +
+
+
+
+ +
+ + \u5C0F\u65E5\u66C6 + + + \u25BE +
+ + +
+
\u8F09\u5165\u4E2D\u2026
+
+
+ +\n\n\n\n\n\n
\n
\n
Arcrun 駕駛艙
\n
\n
\n \n
\n
\n
\n
\n
\n
載入中
\n
\n
\n
\n
\n
\n
今日完成
\n
/
\n
\n
\n
\n
收件匣未處理
\n
\n
來自 Telegram
\n
\n
\n
\n
等你的事
\n
載入中…
\n
\n
\n
今日路線
\n \n
本週
\n \n
系統狀況live 健康信號
\n
載入中…
\n
每 60 秒自動刷新
\n 進入完整控制台 ›\n
\n\n\n\n"},"/console/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"\n\n\n\n\nArcrun Console\n\n\n\n\n\n\n\n\n
\n
\n
\n
Arcrun
\n
私人系統・僅供擁有者進入
\n
\n
\n \n \n \n
\n
\n
這是一個人的智慧總部。
若你不是擁有者,這裡沒有你要找的東西。
\n \n \n
\n
\n\n\n
\n
\n
\n
Arcrun
\n
首次設定
\n
系統偵測到尚未設定擁有者帳號。
設定一組 Email 與密碼,之後只有你能進入。
\n
\n
\n \n \n \n \n
\n
\n \n
\n
\n\n\n
\n
\n
Arcrun
\n \n
總庫搜尋
\n
工作流
\n \n
設定
\n
☾ 切深色
\n
\n
\n
\n\n \n
\n
Arcrun 控制台
\n
\n
\n
\n
\n
\n
載入中
\n
\n
\n
\n
\n
\n
今日完成
\n
/
\n
\n
\n
\n
收件匣未處理
\n
\n
來自 Telegram
\n
\n
\n
\n
等你的事
\n
載入中…
\n
\n
\n
\n
\n
\n 今日路線狀態即時同步\n
\n
  • 載入中…
\n
本週
\n
    \n
    每 60 秒自動刷新
    \n
    \n
    \n
    \n\n \n
    \n
    總庫搜尋
    \n
    \n
    \n
    \n 藏書地圖\n
    \n
    地圖載入中…
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n \n
    \n
    \n \n 知識卡片\n
    \n
    \n
    載入中…
    \n
    \n
    關聯視圖
    \n
    \n
    \n
    \n
    \n
    \n\n \n
    \n
    工作流
    \n \n
    載入中…
    \n
    \n
    零件與 Recipes(框架資源)
    \n
    \n \n \n
    \n
    \n
    \n
    \n\n \n
    \n
    憑證管理
    \n
    \n 🛡 保險箱原則:系統只保存目錄與加密後的值,任何頁面都看不到密文內容。只能整筆替換或刪除。\n
    \n
    \n
    新增憑證
    \n
    \n \n \n \n \n
    \n
    \n
    \n
    載入中…
    \n
    \n\n \n
    \n
    分流台全部待辦・80/15/5
    \n
    \n
    載入中…
    \n
    \n\n \n
    \n
    設定
    \n
    \n
    \n
    \n
    \n
    深色模式
    \n
    預設淺色(紙感)。切換立即生效,選擇記在這台裝置(localStorage)。
    \n
    \n
    \n
    \n
    \n
    \n \n
    語意搜尋
    \n
    狀態偵測中…
    \n
    \n
    \n
    \n
    MCP token 有效期(TTL)
    \n
    讀取中…
    \n
    \n 誠實佔位:此頁還不能改這個值——可調功能=Arcrun#19(設定頁改→存 KBDB→發 token 時讀)。實作前要調整請在部署端 env MCP_TOKEN_TTL(mcp worker)設定。\n
    \n
    \n
    \n
    更換帳號密碼
    \n
    需輸入舊密碼驗證身分
    \n
    \n \n \n \n \n
    \n
    \n
    \n \n
    \n
    Portal 帳號密碼救援
    \n
    忘記某個 Portal(RAG 搜尋頁)帳號的密碼,包含你自己那組管理員帳號——不需要先登進 Portal。輸入該帳號的 Email,會產生一組新密碼,只顯示這一次,請立刻抄下並拿去 Portal 登入頁使用。
    \n
    \n \n \n
    \n
    \n
    \n
    \n
    系統資訊
    \n
    載入中…
    \n
    \n \n
    \n
    \n\n
    \n
    \n\n\n
    \n \n
    搜尋
    \n
    工作流
    \n \n
    設定
    \n
    \n\n
    \n\n\n\n\n"},"/favicon.ico":{"type":"image/x-icon","b64":true,"data":"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"},"/favicon.svg":{"type":"image/svg+xml","b64":false,"data":"arcrun icon"},"/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"\n\n\n\nArcrun RAG\n\n\n\n\n\n\n\n

    正在前往搜尋頁… 沒有自動跳轉請點這裡

    \n\n\n"},"/portal/app-mount.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// App 掛載協定 v0.2 的結構自測(Arcrun#152/#82,leo 2026-08-24 拍板)\n// 跑法:node console-ui/public/portal/app-mount.test.mjs\n//\n// 這支守的是**協定的形狀**,不是長相——長相要用瀏覽器量(見 #152 的驗收留言)。\n// 之所以要有它:這一輪的病根是「出口與樣式被做成個案」——筆記 App 有返回鍵、總圖沒有;\n// CIS 表複製進 shadow 了、但 App 根本沒接上。個案會悄悄再長出來,所以把\n// 「不准再有個案」這件事寫成可執行的檢查。\nimport fs from 'node:fs';\n\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\nlet pass = 0, fail = 0;\nconst chk = (label, cond, extra = '') => {\n if (cond) { console.log('PASS:', label); pass++; }\n else { console.log('FAIL:', label, extra); fail++; }\n};\n\n// ── ① 出口:每一個非首頁的 view 都必須有 .pagehead ────────────────────────────\n// ensureBackControls() 是往 `#v- .pagehead` 裡塞返回鍵的。少了那一列 = 那個 view\n// 悄悄沒有出口,而且沒有人會發現(leo 就是這樣卡在總圖)。\nconst viewsLine = html.match(/var VIEWS = \\[(.*?)\\];/);\nif (!viewsLine) throw new Error('抽不到 VIEWS 清單');\nconst VIEWS = viewsLine[1].split(',').map((s) => s.trim().replace(/^'|'$/g, ''));\nconst HOME = (html.match(/var HOME = '([^']+)'/) || [])[1];\nchk('抽得到 VIEWS 與 HOME', VIEWS.length > 0 && !!HOME, `VIEWS=${VIEWS.length} HOME=${HOME}`);\n\nfor (const v of VIEWS) {\n if (v === HOME) continue;\n // 抓 `
    \">` 之後、下一個 view 之前那一段,看有沒有 pagehead\n const at = html.indexOf(`id=\"v-${v}\"`);\n if (at < 0) { chk(`view ${v} 存在於 HTML`, false); continue; }\n const nextView = html.indexOf('id=\"v-', at + 10);\n const block = html.slice(at, nextView < 0 ? at + 4000 : nextView);\n chk(`非首頁 view「${v}」有 .pagehead(返回鍵才掛得上)`, /class=\"pagehead\"/.test(block));\n}\n\n// ── ② 不准再有「某一頁自己的返回鍵」 ─────────────────────────────────────────\nchk('沒有殘留的個案返回鍵(app-back-btn 已撤)', !html.includes('app-back-btn'));\nchk('返回鍵只由 ensureBackControls() 產生(全檔只有一處 createElement 出 data-portal-back)',\n (html.match(/setAttribute\\('data-portal-back'/g) || []).length === 1);\nchk('返回鍵是頁首第一格(insertBefore firstChild)', /insertBefore\\(b, head\\.firstChild\\)/.test(html));\nchk('返回鍵一律回 HOME', /data-portal-back\\]'\\)\\) nav\\(HOME\\)/.test(html));\n\n// ── ③ 樣式:兩種模式與層序 ──────────────────────────────────────────────────\nchk('層序=arcrun-base < app < arcrun(全局在最上面才蓋得過 App)',\n html.includes('@layer arcrun-base, app, arcrun;'));\nchk('沒宣告=跟隨全局(預設 inherit,不是維持 App 原樣)',\n /var inherit = style !== 'own';/.test(html));\nchk('App 自己的 style 會被包進 app 層', /'@layer app\\{' \\+ st\\.textContent \\+ '\\}'/.test(html));\nchk('全局色票/CIS 都進 arcrun 層', (html.match(/'@layer arcrun\\{'/g) || []).length === 2);\nchk('樣式模式從 App 宣告來(不是 Portal 猜的)', /mountAppUi\\(host, app\\.ui_html, app\\.ui_style\\)/.test(html));\n\n// ── ④ 畫布是全局決定的(兩種模式都一樣)────────────────────────────────────\n// leo:「全局要給它多大的畫布也是全局決定的⋯⋯應該整個右側邊欄都是它的」\nchk('App 頁不吃 .page 的寬度上限與左右留白', /#v-app\\.on \\{[^}]*max-width: none;[^}]*padding: 0;/.test(html));\nchk('shadow host 由**外部文件**決定尺寸(App 在 shadow 裡改不動)',\n /#app-ui-mount \\{[^}]*width: 100%/.test(html));\n\n// ── ⑤ 隔離沒有被拆掉(這一輪的紅線)──────────────────────────────────────\nchk('App 仍然掛進 Shadow DOM', /host\\.attachShadow\\(\\{ mode: 'open' \\}\\)/.test(html));\n\nconsole.log(`\\n${fail === 0 ? '✅' : '❌'} pass=${pass} fail=${fail}`);\nprocess.exit(fail === 0 ? 0 : 1);\n"},"/portal/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"\n\n\n\n\nArcrun Portal\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n
    \n
    \n
    \n
    \"arc>run\" width=\"986\" height=\"176\">\"arc>run\" width=\"986\" height=\"176\">
    \n
    知識入口・Portal
    \n
    \n
    \n \n \n \n
    \n
    \n \n \n
    \n \n \n
    \n
    \n \n
    \n
    \n\n\n
    \n
    \n
    \n
    設定新密碼
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n\n\n
    \n
    \n
    \n
    \"arc>run\" width=\"986\" height=\"176\">\"arc>run\" width=\"986\" height=\"176\">
    \n
    歡迎!先建立你的帳號
    \n
    \n
    \n \n \n \n \n
    \n \n \n
    \n
    這組帳密就是之後登入知識庫用的,只需要設定這一次。
    \n
    \n
    \n\n\n
    \n
    \n
    \"arcrun\"\"arcrun\"
    \n \n
    App 界面
    \n
    App 市集
    \n
    AR 設定
    \n
    App 開發
    \n
    ☾ 切深色
    \n
    \n
    \n
    \n\n \n
    \n
    App 界面
    \n
    載入中…
    \n
    \n\n \n
    \n
    搜尋
    \n
    \n
    \n \n \n
    \n
    \n \n \n \n
    \n \n
    \n
    AI 問答・答案附出處,可回搜尋驗證
    \n
    \n \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n\n \n
    \n
    總圖
    \n
    \n
    \n
    \n 點節點可跳到該實體的圖譜搜尋。這張圖由知識庫的三元組即時算出——同一份地圖的文字版\n (給 AI 注入用的總庫目錄)。\n
    \n
    \n
    \n\n \n
    \n
    \n \n 知識卡片\n
    \n
    載入中…
    \n
    \n\n \n
    \n
    App 市集瀏覽、安裝、或上傳你自己做的 App
    \n
    \n
    \n
    \n
    \n
    上傳你的 App
    \n
    打包成一份宣告,讓其他人也能安裝——尚未接後端,先佔位
    \n
    \n
    \n
    已安裝
    \n
    載入中…
    \n
    可安裝
    \n
    \n
    尚未接市集後端
    \n
    目前還沒有「瀏覽別人分享的 App」這條路,之後開通後會列在這裡——現在這裡不放假資料。
    \n
    \n
    \n
    \n\n \n
    \n
    上傳Markdown / 純文字
    \n
    \n 上傳的文件會進入知識庫收件管線:約 1 分鐘後可在搜尋頁找到;AI 整理後會以 wiki 卡形式出現。支援 .md / .txt(一律以 .md 收件)。\n
    \n
    \n
    把 .md / .txt 檔案拖放到這裡
    \n
    \n \n \n
    \n
    \n
    \n\n \n \n
    \n
    App 開發Workflows/零件/Recipe/Credential
    \n
    \n
    \n
    \n
    \n
    叫 AI 幫你做一個 App
    \n
    用一句話描述你要什麼——尚未接後端,先佔位
    \n
    \n
    \n
    工作環境
    \n
    \n
    \n Workflows\n
    \n
    \n 資料上傳\n
    \n
    零件尚未接進 Portal
    \n
    Recipe尚未接進 Portal
    \n
    Credential尚未接進 Portal
    \n
    \n
    \n
    \n\n
    \n
    工作流唯讀・系統狀態
    \n
    \n 此頁只顯示系統裡的工作流與最近執行狀態,不能觸發執行(觸發屬系統擁有者權限)。\n
    \n
    載入中…
    \n
    \n\n \n
    \n
    \n \n App\n \n
    \n
    載入中…
    \n
    \n\n \n
    \n
    設定
    \n \n
    \n
    帳號
    \n
    管理
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n 版本\n \n
    \n
    查詢中…
    \n
    \n \n
    \n
    \n
    \n
    我的帳號
    \n
    載入中…
    \n
    \n
    \n
    \n
    \n
    深色模式
    \n
    預設淺色(紙感)。切換立即生效,選擇記在這台裝置。
    \n
    \n
    \n
    \n
    \n \n
    \n
    更改密碼
    \n
    需輸入舊密碼驗證身分;新密碼至少 8 碼
    \n
    \n \n \n \n \n
    \n
    \n
    \n \n
    \n \n
    \n 你的知識庫網址(小幫手連線用)\n \n \n
    \n
    同步小幫手
    \n
    把你電腦上的資料夾變成知識庫。換電腦或重裝時可以再下載一次。
    \n
    \n 下載 Mac 版\n
    封測版未簽章,第一次請右鍵→打開。裝好第一次開啟時,貼上這個網址+你的帳號密碼就連上了。
    \n
    \n
    \n \n
    \n
    \n 你的 MCP 網址(給你的 AI 連線用)\n \n \n
    \n
    接上你的 AI(MCP)
    \n
    把上面這串網址貼到 Claude、ChatGPT 等 AI 的「新增自訂連接器」欄位,就能讓你的 AI 直接查這個知識庫。
    \n
    \n \n
    \n
    AI 設定
    \n
    \n 這裡不需要任何設定。聊天問答用的是你自己 Cloudflare 帳號內建的 AI,裝好就能直接問。
    \n 文件整理成知識卡的部分,請在同步小幫手(電腦上的托盤圖示)的「AI 設定…」填一把 Gemini API Key。\n
    \n
    \n \n
    \n
    疑難排解
    \n
    要回報問題,請到你電腦上的 Arcrun(同步小幫手)「版本與更新」頁——那裡的「疑難排解」按一下就能匯出完整診斷檔給我們(只有統計數字,不含你的任何文件內容)。
    \n
    \n \n
    \n
    \n\n \n
    \n
    管理帳號與知識庫授權
    \n
    \n
    帳號
    \n
    管理
    \n
    \n\n
    執行紀錄保留期
    \n
    \n
    保留天數
    \n
    執行紀錄是稽核資料,超過保留天數會被每日自動清除;預設 90 天(3 個月),也可設為「不刪除」(企業稽核用途)。
    \n
    \n \n \n \n
    \n
    \n
    \n
    \n\n
    帳號管理
    \n
    \n
    新增同仁帳號
    \n
    建立後會產生一組一次性密碼——只顯示這一次,請當場轉交同仁(同仁可在「設定」自行改密)。
    \n
    \n \n \n \n \n
    \n
    \n
    \n
    \n
    載入中…
    \n\n
    庫目錄管理
    \n
    \n 你的知識庫網址(小幫手連線用)\n \n \n
    \n
    \n \n 裝好同步小幫手並選好要看守的資料夾之後,每個資料夾會自動成為一個「庫」出現在下面——不需要人工新增。下面還是空的,代表小幫手還沒裝好或還沒選資料夾。\n
    \n
    載入中…
    \n
    \n\n
    \n
    \n\n\n
    \n
    App 界面
    \n
    App 市集
    \n
    AR 設定
    \n
    App 開發
    \n
    \n\n
    \n\n\n\n"},"/portal/os-split.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"import fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname,'utf8');\n// 抽出 daemonPick 相關函式(從 DAEMON_BASE_DEFAULT 到 daemonHint 結尾)\n//\n// 🔴 2026-08-05:結尾標記本來寫死 daemonHint 的**整句文案**,於是同日改 Mac 提示語\n// (zip→DMG 的步驟不同)就讓這支自測直接炸「抽不到函式區塊」,而且沒人發現。\n// ⇒ 改成錨定「函式結束」這個結構,不再綁文案——文案本來就會改,測試不該為此壞掉。\nconst start = html.indexOf('var DAEMON_BASE_DEFAULT');\nconst hintAt = html.indexOf('function daemonHint', start);\nconst endMark = '\\n }';\nconst end = hintAt < 0 ? -1 : html.indexOf(endMark, hintAt) + endMark.length;\nif (start < 0 || hintAt < 0 || end < start) throw new Error('抽不到函式區塊');\nconst src = html.slice(start, end);\n\nconst cases = [\n ['Windows', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36'],\n ['Mac', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15'],\n ['iPhone', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Safari/604.1'],\n ['Linux', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120 Safari/537.36'],\n];\nlet pass=0, fail=0;\nconst chk=(l,c,extra='')=>{ if(c){console.log('PASS:',l);pass++;} else {console.log('FAIL:',l,extra);fail++;} };\n\nfor (const [name, ua] of cases) {\n const fn = new Function('navigator','window', src + '; return {daemonPick:daemonPick, daemonHint:daemonHint, daemonBase:daemonBase};');\n const api = fn({userAgent: ua}, {});\n const d = api.daemonPick();\n const label = d.sure ? d.pick.label : '(兩個都給)';\n const url = d.sure ? d.pick.url : d.mac.url + ' + ' + d.win.url;\n console.log(`\\n[${name}] sure=${d.sure} → ${label}`);\n console.log(` url: ${url}`);\n if (name==='Windows') {\n chk('Windows 給 win zip', d.sure && d.pick.url.endsWith('ArcrunRAG-win-unsigned.zip'), d.pick&&d.pick.url);\n chk('Windows 另一版是 Mac', d.other && d.other.url.endsWith('ArcrunRAG-mac.dmg'));\n chk('Windows 話術提 藍色視窗', api.daemonHint('win').includes('仍要執行'));\n }\n if (name==='Mac') {\n // 2026-08-05:Mac 一律給 DMG(拖進 Applications 的標準安裝畫面),不再給 zip\n // ——zip 解開就是一個裸 .app,使用者會直接在「下載」資料夾雙擊執行,自更新會蓋錯位置。\n chk('Mac 給 dmg(不是 zip)', d.sure && d.pick.url.endsWith('ArcrunRAG-mac.dmg'));\n chk('Mac 另一版是 Windows', d.other && d.other.url.endsWith('win-unsigned.zip'));\n chk('Mac 話術提 右鍵打開', api.daemonHint('mac').includes('右鍵'));\n }\n if (name==='iPhone' || name==='Linux') {\n // iPhone 含 \"Mac OS X\" 但不是桌機 Mac;Linux 兩者皆非 → 都該落在「不確定=兩個都給」\n if (name==='Linux') chk('Linux 判不出來→兩個都給', d.sure===false);\n if (name==='iPhone') chk('iPhone 不該被判成 Mac(手機→兩個都給)', d.sure===false, 'sure='+d.sure);\n }\n}\n// 舊 key 相容\nconst fn2 = new Function('navigator','window', src + '; return daemonBase();');\nconsole.log('\\n[相容] daemonDownload 舊 key →', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}}));\nchk('舊 key 推得出目錄', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}})==='https://x.dev/d/');\nchk('daemonBase 新 key 優先', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonBase:'https://y.dev/z'}})==='https://y.dev/z/');\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail?1:0);\n"},"/portal/retrieval-tree.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// 檢索過程樹(Arcrun#44 第二輪,leo 2026-08-18「希望以樹狀圖來展示」)的資料組裝測試。\n// 只測**樹的資料**,不測 markmap 怎麼畫——畫圖是現成函式庫的事(D97),\n// 我們自己要為之負責的是「層級對不對、有沒有把讀過的東西吞掉」。\n// 跑法:node console-ui/public/portal/retrieval-tree.test.mjs\nimport fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\n\nconst grab = (name) => {\n const i = html.indexOf(`function ${name}(`);\n if (i < 0) throw new Error(`找不到 ${name}`);\n let d = 0, j = html.indexOf('{', i);\n for (let k = j; k < html.length; k++) { if (html[k] === '{') d++; if (html[k] === '}') { d--; if (!d) return html.slice(i, k + 1); } }\n throw new Error('括號不平衡');\n};\nconst src = ['esc', 'srcLocalPath', 'machineLabelMap', 'srcCrumbs', 'retrievalTreeData', 'visibleLeaves', 'treeListHtml'].map(grab).join('\\n');\nconst fn = new Function(src + '\\nvar SEARCH_ROUTE_LABEL = { graph: \"圖譜命中子庫\", index: \"索引退回選庫\", all: \"全庫讀取\" };'\n + '\\nreturn { srcCrumbs, machineLabelMap, retrievalTreeData, visibleLeaves, treeListHtml };')();\n\nlet pass = 0, fail = 0;\nconst t = (l, c, e = '') => { c ? (console.log('PASS:', l), pass++) : (console.log('FAIL:', l, e), fail++); };\nconst txt = (n) => String(n.content).replace(/<[^>]*>/g, '');\n\n// 2026-08-18 從 youlin stage 實跑 /portal/data/chat 抓下來的真回應形狀(narrative 真的是空字串)\nconst REAL = {\n route: 'graph', vector_used: false, libraries_considered: 8,\n libraries: [{ library: 'testkb', score: 1.69, via: 'graph', matched_entities: ['Arcrun124驗證卡'] }],\n indexes_read: [\n { library: 'general', narrative: '', selected: false, top_entities: [], triplet_count: 0, entry_count: 100 },\n { library: 'testkb', narrative: '', selected: true, top_entities: ['Arcrun124驗證卡'], triplet_count: 4, entry_count: 9 },\n { library: 'kb', narrative: '', selected: false, top_entities: ['knowledge-base'], triplet_count: 7, entry_count: 16 }\n ],\n pages_read: [{ page: 'Arcrun124驗證卡', library: 'testkb', chars: 231, source: 'kb://test/arcrun-124-verify.md#0' }]\n};\n\n// ① 層級=leo 畫的那張:搜尋詞 → 命中子庫 → 索引敘事 → 原文片段\nconst tree = fn.retrievalTreeData('Arcrun124驗證卡是什麼?', REAL);\nt('第 1 層是搜尋詞', txt(tree).indexOf('Arcrun124驗證卡是什麼?') === 0, txt(tree));\nconst lib = tree.children[0];\nt('第 2 層是命中子庫(帶分數與命中實體)', /testkb/.test(txt(lib)) && /1\\.69/.test(txt(lib)) && /命中實體/.test(txt(lib)), txt(lib));\nconst idx = lib.children[0];\nt('第 3 層是該子庫的索引敘事', /三元組/.test(txt(idx)) && /筆條目/.test(txt(idx)), txt(idx));\nconst page = idx.children[0];\nt('第 4 層是原文片段(掛在索引敘事底下)', /Arcrun124驗證卡/.test(txt(page)) && /231 字/.test(txt(page)), txt(page));\nt('原文片段帶資料夾麵包屑', /test › arcrun-124-verify\\.md/.test(txt(page)), txt(page));\n\n// ② narrative 是空字串時不假裝有敘事(線上真資料就是這樣)\nt('沒有敘事就說沒有,不留空白', /索引沒有敘事/.test(txt(idx)), txt(idx));\n\n// ③ 沒被選用的子庫不可以被吞掉,但要收合起來不擠掉主線\nconst rest = tree.children[tree.children.length - 1];\nt('沒讀原文的子庫收在一個節點底下', /考慮過但沒讀原文(2 個子庫)/.test(txt(rest)), txt(rest));\nt('那個節點預設收合', rest.payload && rest.payload.fold === 1, JSON.stringify(rest.payload));\nt('主線只有命中的子庫', tree.children.length === 2, `實得 ${tree.children.length} 條分支`);\n\n// ④ 讀了原文、但沒出現在 libraries[] 的子庫(route=all)也要長出來\nconst allRoute = fn.retrievalTreeData('問句', {\n route: 'all', libraries: [], indexes_read: [],\n pages_read: [{ page: 'P', library: 'kb', chars: 10, source: 'kb://a/b/c.md#2' }]\n});\nt('route=all 時仍有子庫分支', allRoute.children.length === 1 && /kb/.test(txt(allRoute.children[0])), txt(allRoute.children[0]));\nt('沒有索引敘事時不生假的中間層', /^P/.test(txt(allRoute.children[0].children[0])), txt(allRoute.children[0].children[0]));\n\n// ⑤ 完全沒資料時不要生一棵空樹\nconst empty = fn.retrievalTreeData('問句', { route: 'all', libraries: [], indexes_read: [], pages_read: [] });\nt('沒有任何子庫時說明原因', /全庫作答|沒有回報/.test(txt(empty.children[0])), txt(empty.children[0]));\n\n// ⑥ 麵包屑:資料夾可以有很多層,全部拆開;機器那一層現在是「沒有」\nconst c = fn.srcCrumbs('kb://RFP/2026/design.md#3', 'kb');\nt('多層資料夾全部拆開', JSON.stringify(c.dirs) === '[\"RFP\",\"2026\"]', JSON.stringify(c.dirs));\nt('檔名拆得出來', c.file === 'design.md', c.file);\nt('片段編號拆得出來', c.frag === '3', c.frag);\nt('子庫帶得進來', c.library === 'kb', c.library);\nt('機器那一層是 null(資料裡真的沒有)', c.machine === null, String(c.machine));\nt('相對路徑原樣留著(複製給用戶去本機找)', c.path === 'RFP/2026/design.md', c.path);\n\nconst g = fn.srcCrumbs('gitea:Leo/InkStoneCo@wiki/x.md#1', 'kb');\nt('非 kb:// 來源不硬拆成假麵包屑', g.path === '' && g.file === '' && g.dirs.length === 0, JSON.stringify(g));\n\n// ⑦ markmap 載不進來時的退路:同一份樹資料畫成巢狀清單\nconst list = fn.treeListHtml(tree);\nt('退路是巢狀清單', /
      /.test(list) && (list.match(/
        = 3, String((list.match(/
          n.content).join(' ');\nt('樹上兩個片段標的是同一個機器名', (both.match(/教育部 Leo 的 Mac/g) || []).length === 2, both.slice(0, 200));\n\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail ? 1 : 0);\n"},"/portal/safejson.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"import fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname,'utf8');\n\n// 抽出 safeJson 與 friendlyErr 求值\nconst grab = (name) => {\n const i = html.indexOf(`function ${name}(`);\n if (i < 0) throw new Error(`找不到 ${name}`);\n let d=0, j=html.indexOf('{', i);\n for (let k=j;k{c?(console.log('PASS:',l),pass++):(console.log('FAIL:',l,e),fail++)};\n\n// ① safeJson:非 JSON 不可拋例外(同事撞到的 404 HTML 頁)\nconst html404 = '404 Not Found';\nawait fn.safeJson({ text: () => Promise.resolve(html404) })\n .then(d => t('404 HTML → 回空物件不拋錯', typeof d === 'object' && d !== null))\n .catch(e => t('404 HTML → 不該拋錯', false, e.message));\n\nawait fn.safeJson({ text: () => Promise.resolve('') })\n .then(d => t('空回應 → 回空物件', JSON.stringify(d)==='{}'))\n .catch(() => t('空回應 → 不該拋錯', false));\n\nawait fn.safeJson({ text: () => Promise.resolve('{\"error\":\"帳號或密碼不對\"}') })\n .then(d => t('正常 JSON 仍要解析得出來', d.error === '帳號或密碼不對'), )\n .catch(() => t('正常 JSON 不該拋錯', false));\n\n// ② friendlyErr:不可把技術訊息噴給使用者\nconst leak = fn.friendlyErr(new Error('Unexpected non-whitespace character after JSON at position 4'));\nt('JSON 錯誤 → 不外洩原文', !/JSON|position/i.test(leak), `實得: ${leak}`);\nt('JSON 錯誤 → 說人話', /伺服器回應異常/.test(leak), `實得: ${leak}`);\n\nconst net = fn.friendlyErr(new Error('Failed to fetch'));\nt('網路錯誤 → 既有訊息保留', /連線中斷/.test(net), `實得: ${net}`);\n\nconst ours = fn.friendlyErr(new Error('帳號或密碼不對——用你在知識庫網站設定的那組'));\nt('我們自己的中文訊息 → 原樣顯示', /帳號或密碼不對/.test(ours), `實得: ${ours}`);\n\nconst stack = fn.friendlyErr(new Error('TypeError: Cannot read properties of undefined'));\nt('英文技術訊息 → 收斂不外洩', !/TypeError|undefined/.test(stack), `實得: ${stack}`);\n\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail?1:0);\n"},"/portal/tree-links.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// 樹狀圖的**線型**測試(InkStoneCo#44 第三輪,leo 2026-08-18:\n// 「線型可以改成類似 terminal tree 那樣往右、往下展開的 L 型折線,線的色彩不要花花綠綠」)。\n//\n// 為什麼這個要測,而 markmap 的佈局不測(D97「畫圖是現成函式庫的事」):\n// 折線的座標換算是**我們自己寫的那一段**——markmap 交出曲線 `d`,我們把它改寫成 L 型。\n// 換算錯了畫面只會「線歪掉」,不會報錯,靠眼睛看很容易漏。\n// 跑法:node console-ui/public/portal/tree-links.test.mjs\nimport fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\n\nconst grab = (name) => {\n const i = html.indexOf(`function ${name}(`);\n if (i < 0) throw new Error(`找不到 ${name}`);\n let d = 0;\n for (let k = html.indexOf('{', i); k < html.length; k++) { if (html[k] === '{') d++; if (html[k] === '}') { d--; if (!d) return html.slice(i, k + 1); } }\n throw new Error('括號不平衡');\n};\nconst fn = new Function(['elbowPath', 'elbowFromCurve'].map(grab).join('\\n')\n + '\\nreturn { elbowPath, elbowFromCurve };')();\n\nlet pass = 0, fail = 0;\nconst t = (l, c, e = '') => { c ? (console.log('PASS:', l), pass++) : (console.log('FAIL:', l, e), fail++); };\n\n// ① 折線只由 M/L 組成——沒有任何曲線指令。這同時是「已經改過了」的判斷依據,\n// 所以它壞掉不只是變醜,是會無限重寫。\nconst down = fn.elbowPath(100, 50, 152, 120);\nt('往下的連線是 L 型折線,只有 M/L', /^M[\\d.,-]+(L[\\d.,-]+){3}$/.test(down), down);\nt('折線不含任何曲線指令(C/S/Q/A)', !/[CSQAcsqa]/.test(down), down);\n\n// ② 轉折點:先往右 12px、再垂直、最後水平接進子節點 ⇒ 兩個直角\nt('起點是父節點右緣', down.startsWith('M100,50'), down);\nt('第一段水平:往右 12px 到幹線', down.indexOf('L112,50') > 0, down);\nt('第二段垂直:幹線 x 不變、y 換到子節點那列', down.indexOf('L112,120') > 0, down);\nt('第三段水平:接進子節點左緣', down.endsWith('L152,120'), down);\n\n// ③ 同一個父節點的所有子節點共用同一條垂直幹線(起點相同 ⇒ 轉折 x 相同)\nconst up = fn.elbowPath(100, 50, 152, -20);\nt('往上的子節點用同一條幹線 x=112', up.indexOf('L112,50') > 0 && up.indexOf('L112,-20') > 0, up);\n\n// ④ 父子同一列時不要硬折一個看不見的直角,畫一條直線就好\nconst flat = fn.elbowPath(100, 50, 152, 50);\nt('同一列=一條直線(兩段)', flat === 'M100,50L152,50', flat);\n\n// ⑤ 框很窄/間距被壓縮時,幹線不可以跑到子節點右邊(會變成往回折的怪線)\nconst tight = fn.elbowPath(100, 50, 104, 120);\nt('間距 < 12 時幹線收回到子節點左緣', tight.indexOf('L104,50L104,120') > 0, tight);\n\n// ⑥ 吃得下 markmap 真的吐出來的 bumpX 字串(d3 linkHorizontal + bumpX 的格式)\nconst real = fn.elbowFromCurve('M100,50C126,50,126,120,152,120');\nt('bumpX 曲線 → 折線(只取頭尾兩點,控制點丟掉)', real === 'M100,50L112,50L112,120L152,120', real);\n\n// ⑦ 已經是折線的就不要再動——不然 observer 改寫→再觸發→再改寫,會轉不停\nt('已經是折線 ⇒ 回 null(不重寫)', fn.elbowFromCurve(real) === null, String(fn.elbowFromCurve(real)));\nt('空字串 ⇒ 回 null', fn.elbowFromCurve('') === null);\nt('認不得的 d ⇒ 回 null,不亂畫', fn.elbowFromCurve('C') === null);\n\n// ⑧ 小數座標(markmap 的佈局會給小數)不要吐出一長串尾數\nconst frac = fn.elbowPath(100.123456, 50.987654, 152.5, 120.25);\nt('座標四捨五入到小數兩位', frac === 'M100.12,50.99L112.12,50.99L112.12,120.25L152.5,120.25', frac);\n\nconsole.log(`\\n${fail ? '❌' : '✅'} ${pass} passed, ${fail} failed`);\nprocess.exit(fail ? 1 : 0);\n"},"/portal/vendor/markmap-view.min.js":{"type":"application/javascript; charset=utf-8","b64":false,"data":"(()=>{var ki=\"npm2url/dist/index.cjs\",Si={jsdelivr:t=>`https://cdn.jsdelivr.net/npm/${t}`,unpkg:t=>`https://unpkg.com/${t}`};async function Ei(t,e){let r=await fetch(t,{signal:e});if(!r.ok)throw r;await r.text()}var Ce=class{constructor(){this.providers={...Si},this.provider=\"jsdelivr\"}async getFastestProvider(e=5e3,r=ki){let n=new AbortController,i=0;try{return await new Promise((o,a)=>{Promise.all(Object.entries(this.providers).map(async([s,l])=>{try{await Ei(l(r),n.signal),o(s)}catch{}})).then(()=>a(new Error(\"All providers failed\"))),i=setTimeout(a,e,new Error(\"Timed out\"))})}finally{n.abort(),clearTimeout(i)}}async findFastestProvider(e,r){return this.provider=await this.getFastestProvider(e,r),this.provider}setProvider(e,r){r?this.providers[e]=r:delete this.providers[e]}getFullUrl(e,r=this.provider){if(e.includes(\"://\"))return e;let n=this.providers[r];if(!n)throw new Error(`Provider ${r} not found`);return n(e)}},Gs=new Ce,Vt=class{constructor(){this.listeners=[]}tap(e){return this.listeners.push(e),()=>this.revoke(e)}revoke(e){let r=this.listeners.indexOf(e);r>=0&&this.listeners.splice(r,1)}revokeAll(){this.listeners.splice(0)}call(...e){for(let r of this.listeners)r(...e)}};var Ci=Math.random().toString(36).slice(2,8),sr=0;function ur(){return sr+=1,`mm-${Ci}-${sr}`}function kt(){}function St(t,e){let r=(n,i)=>e(n,()=>{var o;return(o=n.children)==null?void 0:o.map(a=>r(a,n))},i);return r(t)}function fr(t,...e){let r=(t||\"\").split(\" \").filter(Boolean);return e.forEach(n=>{n&&r.indexOf(n)<0&&r.push(n)}),r.join(\" \")}function Ai(t){let e={};return function(...n){let i=`${n[0]}`,o=e[i];return o||(o={value:t(...n)},e[i]=o),o.value}}function cr(t,e){let r={timer:0};function n(){r.timer&&(window.clearTimeout(r.timer),r.timer=0)}function i(){n(),r.args&&(r.result=t(...r.args))}return function(...a){return n(),r.args=a,r.timer=window.setTimeout(i,e),r.result}}var hr=1,pr=2,zi=\"http://www.w3.org/2000/svg\",Ee=\"http://www.w3.org/1999/xlink\",$i={show:Ee,actuate:Ee,href:Ee},Ti=t=>typeof t==\"string\"||typeof t==\"number\",Oi=t=>(t==null?void 0:t.vtype)===hr,Mi=t=>(t==null?void 0:t.vtype)===pr;function Ii(t,e,...r){return e=Object.assign({},e,{children:r.length===1?r[0]:r}),Ri(t,e)}function Ri(t,e){let r;if(typeof t==\"string\")r=hr;else if(typeof t==\"function\")r=pr;else throw new Error(\"Invalid VNode type\");return{vtype:r,type:t,props:e}}function Ni(t){return t.children}var Pi={isSvg:!1};function lr(t,e){Array.isArray(e)||(e=[e]),e=e.filter(Boolean),e.length&&t.append(...e)}function Bi(t,e,r){for(let n in e)if(!(n===\"key\"||n===\"children\"||n===\"ref\"))if(n===\"dangerouslySetInnerHTML\")t.innerHTML=e[n].__html;else if(n===\"innerHTML\"||n===\"textContent\"||n===\"innerText\"||n===\"value\"&&[\"textarea\",\"select\"].includes(t.tagName)){let i=e[n];i!=null&&(t[n]=i)}else n.startsWith(\"on\")?t[n.toLowerCase()]=e[n]:Di(t,n,e[n],r.isSvg)}var Xi={className:\"class\",labelFor:\"for\"};function Di(t,e,r,n){if(e=Xi[e]||e,r===!0)t.setAttribute(e,\"\");else if(r===!1)t.removeAttribute(e);else{let i=n?$i[e]:void 0;i!==void 0?t.setAttributeNS(i,e,r):t.setAttribute(e,r)}}function qi(t){return t.reduce((e,r)=>e.concat(r),[])}function Ae(t,e){return Array.isArray(t)?qi(t.map(r=>Ae(r,e))):ze(t,e)}function ze(t,e=Pi){if(t==null||typeof t==\"boolean\")return null;if(t instanceof Node)return t;if(Mi(t)){let{type:r,props:n}=t;if(r===Ni){let o=document.createDocumentFragment();if(n.children){let a=Ae(n.children,e);lr(o,a)}return o}let i=r(n);return ze(i,e)}if(Ti(t))return document.createTextNode(`${t}`);if(Oi(t)){let r,{type:n,props:i}=t;if(!e.isSvg&&n===\"svg\"&&(e=Object.assign({},e,{isSvg:!0})),e.isSvg?r=document.createElementNS(zi,n):r=document.createElement(n),Bi(r,i,e),i.children){let a=e;e.isSvg&&n===\"foreignObject\"&&(a=Object.assign({},a,{isSvg:!1}));let s=Ae(i.children,a);s!=null&&lr(r,s)}let{ref:o}=i;return typeof o==\"function\"&&o(r),r}throw new Error(\"mount: Invalid Vnode!\")}function Li(t){return ze(t)}function Yi(...t){return Li(Ii(...t))}var Ws=Ai(t=>{document.head.append(Yi(\"link\",{rel:\"preload\",as:\"script\",href:t}))});var mt=class extends Map{constructor(e,r=Vi){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:r}}),e!=null)for(let[n,i]of e)this.set(n,i)}get(e){return super.get(dr(this,e))}has(e){return super.has(dr(this,e))}set(e,r){return super.set(Fi(this,e),r)}delete(e){return super.delete(Hi(this,e))}};function dr({_intern:t,_key:e},r){let n=e(r);return t.has(n)?t.get(n):r}function Fi({_intern:t,_key:e},r){let n=e(r);return t.has(n)?t.get(n):(t.set(n,r),r)}function Hi({_intern:t,_key:e},r){let n=e(r);return t.has(n)&&(r=t.get(n),t.delete(n)),r}function Vi(t){return t!==null&&typeof t==\"object\"?t.valueOf():t}function Et(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}function Ct(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}function Gt(t,e){let r,n=-1,i=-1;if(e===void 0)for(let o of t)++i,o!=null&&(r>o||r===void 0&&o>=o)&&(r=o,n=i);else for(let o of t)(o=e(o,++i,t))!=null&&(r>o||r===void 0&&o>=o)&&(r=o,n=i);return n}var Gi={value:()=>{}};function xr(){for(var t=0,e=arguments.length,r={},n;t=0&&(n=r.slice(i+1),r=r.slice(0,i)),r&&!e.hasOwnProperty(r))throw new Error(\"unknown type: \"+r);return{type:r,name:n}})}Wt.prototype=xr.prototype={constructor:Wt,on:function(t,e){var r=this._,n=Wi(t+\"\",r),i,o=-1,a=n.length;if(arguments.length<2){for(;++o0)for(var r=new Array(i),n=0,i,o;n=0&&(e=t.slice(0,r))!==\"xmlns\"&&(t=t.slice(r+1)),$e.hasOwnProperty(e)?{space:$e[e],local:t}:t}function Ui(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===Kt&&e.documentElement.namespaceURI===Kt?e.createElement(t):e.createElementNS(r,t)}}function Qi(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Ut(t){var e=Q(t);return(e.local?Qi:Ui)(e)}function Zi(){}function lt(t){return t==null?Zi:function(){return this.querySelector(t)}}function gr(t){typeof t!=\"function\"&&(t=lt(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i=O&&(O=$+1);!(I=A[O])&&++O<_;);M._next=I||null}}return a=new T(a,n),a._enter=s,a._exit=l,a}function uo(t){return typeof t==\"object\"&&\"length\"in t?t:Array.from(t)}function Er(){return new T(this._exit||this._groups.map(Zt),this._parents)}function Cr(t,e,r){var n=this.enter(),i=this,o=this.exit();return typeof t==\"function\"?(n=t(n),n&&(n=n.selection())):n=n.append(t+\"\"),e!=null&&(i=e(i),i&&(i=i.selection())),r==null?o.remove():r(o),n&&i?n.merge(i).order():i}function Ar(t){for(var e=t.selection?t.selection():t,r=this._groups,n=e._groups,i=r.length,o=n.length,a=Math.min(i,o),s=new Array(i),l=0;l=0;)(a=n[i])&&(o&&a.compareDocumentPosition(o)^4&&o.parentNode.insertBefore(a,o),o=a);return this}function $r(t){t||(t=fo);function e(p,d){return p&&d?t(p.__data__,d.__data__):!p-!d}for(var r=this._groups,n=r.length,i=new Array(n),o=0;oe?1:t>=e?0:NaN}function Tr(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function Or(){return Array.from(this)}function Mr(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?yo:typeof e==\"function\"?wo:vo)(t,e,r==null?\"\":r)):et(this.node(),t)}function et(t,e){return t.style.getPropertyValue(e)||Jt(t).getComputedStyle(t,null).getPropertyValue(e)}function _o(t){return function(){delete this[t]}}function bo(t,e){return function(){this[t]=e}}function ko(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function Xr(t,e){return arguments.length>1?this.each((e==null?_o:typeof e==\"function\"?ko:bo)(t,e)):this.node()[t]}function Dr(t){return t.trim().split(/^|\\s+/)}function Oe(t){return t.classList||new qr(t)}function qr(t){this._node=t,this._names=Dr(t.getAttribute(\"class\")||\"\")}qr.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute(\"class\",this._names.join(\" \")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute(\"class\",this._names.join(\" \")))},contains:function(t){return this._names.indexOf(t)>=0}};function Lr(t,e){for(var r=Oe(t),n=-1,i=e.length;++n=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function Lo(t){return function(){var e=this.__on;if(e){for(var r=0,n=-1,i=e.length,o;r>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):r===8?re(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):r===4?re(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=Wo.exec(t))?new D(e[1],e[2],e[3],1):(e=Ko.exec(t))?new D(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=Uo.exec(t))?re(e[1],e[2],e[3],e[4]):(e=Qo.exec(t))?re(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=Zo.exec(t))?hn(e[1],e[2]/100,e[3]/100,1):(e=Jo.exec(t))?hn(e[1],e[2]/100,e[3]/100,e[4]):an.hasOwnProperty(t)?un(an[t]):t===\"transparent\"?new D(NaN,NaN,NaN,0):null}function un(t){return new D(t>>16&255,t>>8&255,t&255,1)}function re(t,e,r,n){return n<=0&&(t=e=r=NaN),new D(t,e,r,n)}function ea(t){return t instanceof It||(t=rt(t)),t?(t=t.rgb(),new D(t.r,t.g,t.b,t.opacity)):new D}function gt(t,e,r,n){return arguments.length===1?ea(t):new D(t,e,r,n==null?1:n)}function D(t,e,r,n){this.r=+t,this.g=+e,this.b=+r,this.opacity=+n}ee(D,gt,Ne(It,{brighter(t){return t=t==null?ie:Math.pow(ie,t),new D(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?Ot:Math.pow(Ot,t),new D(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new D(ft(this.r),ft(this.g),ft(this.b),oe(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:fn,formatHex:fn,formatHex8:ra,formatRgb:cn,toString:cn}));function fn(){return`#${ut(this.r)}${ut(this.g)}${ut(this.b)}`}function ra(){return`#${ut(this.r)}${ut(this.g)}${ut(this.b)}${ut((isNaN(this.opacity)?1:this.opacity)*255)}`}function cn(){let t=oe(this.opacity);return`${t===1?\"rgb(\":\"rgba(\"}${ft(this.r)}, ${ft(this.g)}, ${ft(this.b)}${t===1?\")\":`, ${t})`}`}function oe(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function ft(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function ut(t){return t=ft(t),(t<16?\"0\":\"\")+t.toString(16)}function hn(t,e,r,n){return n<=0?t=e=r=NaN:r<=0||r>=1?t=e=NaN:e<=0&&(t=NaN),new G(t,e,r,n)}function dn(t){if(t instanceof G)return new G(t.h,t.s,t.l,t.opacity);if(t instanceof It||(t=rt(t)),!t)return new G;if(t instanceof G)return t;t=t.rgb();var e=t.r/255,r=t.g/255,n=t.b/255,i=Math.min(e,r,n),o=Math.max(e,r,n),a=NaN,s=o-i,l=(o+i)/2;return s?(e===o?a=(r-n)/s+(r0&&l<1?0:a,new G(a,s,l,t.opacity)}function mn(t,e,r,n){return arguments.length===1?dn(t):new G(t,e,r,n==null?1:n)}function G(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}ee(G,mn,Ne(It,{brighter(t){return t=t==null?ie:Math.pow(ie,t),new G(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?Ot:Math.pow(Ot,t),new G(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*e,i=2*r-n;return new D(Pe(t>=240?t-240:t+120,i,n),Pe(t,i,n),Pe(t<120?t+240:t-120,i,n),this.opacity)},clamp(){return new G(pn(this.h),ne(this.s),ne(this.l),oe(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){let t=oe(this.opacity);return`${t===1?\"hsl(\":\"hsla(\"}${pn(this.h)}, ${ne(this.s)*100}%, ${ne(this.l)*100}%${t===1?\")\":`, ${t})`}`}}));function pn(t){return t=(t||0)%360,t<0?t+360:t}function ne(t){return Math.max(0,Math.min(1,t||0))}function Pe(t,e,r){return(t<60?e+(r-e)*t/60:t<180?r:t<240?e+(r-e)*(240-t)/60:e)*255}function Be(t,e,r,n,i){var o=t*t,a=o*t;return((1-3*t+3*o-a)*e+(4-6*o+3*a)*r+(1+3*t+3*o-3*a)*n+a*i)/6}function xn(t){var e=t.length-1;return function(r){var n=r<=0?r=0:r>=1?(r=1,e-1):Math.floor(r*e),i=t[n],o=t[n+1],a=n>0?t[n-1]:2*i-o,s=n()=>t;function na(t,e){return function(r){return t+r*e}}function ia(t,e,r){return t=Math.pow(t,r),e=Math.pow(e,r)-t,r=1/r,function(n){return Math.pow(t+n*e,r)}}function yn(t){return(t=+t)==1?ae:function(e,r){return r-e?ia(e,r,t):Xe(isNaN(e)?r:e)}}function ae(t,e){var r=e-t;return r?na(t,r):Xe(isNaN(t)?e:t)}var se=(function t(e){var r=yn(e);function n(i,o){var a=r((i=gt(i)).r,(o=gt(o)).r),s=r(i.g,o.g),l=r(i.b,o.b),u=ae(i.opacity,o.opacity);return function(f){return i.r=a(f),i.g=s(f),i.b=l(f),i.opacity=u(f),i+\"\"}}return n.gamma=t,n})(1);function vn(t){return function(e){var r=e.length,n=new Array(r),i=new Array(r),o=new Array(r),a,s;for(a=0;ar&&(o=e.slice(r,o),s[a]?s[a]+=o:s[++a]=o),(n=n[0])===(i=i[0])?s[a]?s[a]+=i:s[++a]=i:(s[++a]=null,l.push({i:a,x:Y(n,i)})),r=De.lastIndex;return r180?f+=360:f-u>180&&(u+=360),d.push({i:p.push(i(p)+\"rotate(\",null,n)-2,x:Y(u,f)})):f&&p.push(i(p)+\"rotate(\"+f+n)}function s(u,f,p,d){u!==f?d.push({i:p.push(i(p)+\"skewX(\",null,n)-2,x:Y(u,f)}):f&&p.push(i(p)+\"skewX(\"+f+n)}function l(u,f,p,d,x,_){if(u!==p||f!==d){var b=x.push(i(x)+\"scale(\",null,\",\",null,\")\");_.push({i:b-4,x:Y(u,p)},{i:b-2,x:Y(f,d)})}else(p!==1||d!==1)&&x.push(i(x)+\"scale(\"+p+\",\"+d+\")\")}return function(u,f){var p=[],d=[];return u=t(u),f=t(f),o(u.translateX,u.translateY,f.translateX,f.translateY,p,d),a(u.rotate,f.rotate,p,d),s(u.skewX,f.skewX,p,d),l(u.scaleX,u.scaleY,f.scaleX,f.scaleY,p,d),u=f=null,function(x){for(var _=-1,b=d.length,A;++_=0&&t._call.call(void 0,e),t=t._next;--yt}function En(){ct=(ce=Bt.now())+he,yt=Nt=0;try{zn()}finally{yt=0,da(),ct=0}}function pa(){var t=Bt.now(),e=t-ce;e>Cn&&(he-=e,ce=t)}function da(){for(var t,e=fe,r,n=1/0;e;)e._call?(n>e._time&&(n=e._time),t=e,e=e._next):(r=e._next,e._next=null,e=t?t._next=r:fe=r);Pt=t,Ge(n)}function Ge(t){if(!yt){Nt&&(Nt=clearTimeout(Nt));var e=t-ct;e>24?(t<1/0&&(Nt=setTimeout(En,t-Bt.now()-he)),Rt&&(Rt=clearInterval(Rt))):(Rt||(ce=Bt.now(),Rt=setInterval(pa,Cn)),yt=1,An(En))}}function de(t,e,r){var n=new Xt;return e=e==null?0:+e,n.restart(i=>{n.stop(),t(i+e)},e,r),n}var ma=At(\"start\",\"end\",\"cancel\",\"interrupt\"),xa=[],On=0,$n=1,xe=2,me=3,Tn=4,ge=5,qt=6;function nt(t,e,r,n,i,o){var a=t.__transition;if(!a)t.__transition={};else if(r in a)return;ga(t,r,{name:e,index:n,group:i,on:ma,tween:xa,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:On})}function Lt(t,e){var r=R(t,e);if(r.state>On)throw new Error(\"too late; already scheduled\");return r}function P(t,e){var r=R(t,e);if(r.state>me)throw new Error(\"too late; already running\");return r}function R(t,e){var r=t.__transition;if(!r||!(r=r[e]))throw new Error(\"transition not found\");return r}function ga(t,e,r){var n=t.__transition,i;n[e]=r,r.timer=pe(o,0,r.time);function o(u){r.state=$n,r.timer.restart(a,r.delay,r.time),r.delay<=u&&a(u-r.delay)}function a(u){var f,p,d,x;if(r.state!==$n)return l();for(f in n)if(x=n[f],x.name===r.name){if(x.state===me)return de(a);x.state===Tn?(x.state=qt,x.timer.stop(),x.on.call(\"interrupt\",t,t.__data__,x.index,x.group),delete n[f]):+fxe&&n.state=0&&(e=e.slice(0,r)),!e||e===\"start\"})}function Ba(t,e,r){var n,i,o=Pa(e)?Lt:P;return function(){var a=o(this,t),s=a.on;s!==n&&(i=(n=s).copy()).on(e,r),a.on=i}}function Yn(t,e){var r=this._id;return arguments.length<2?R(this.node(),r).on.on(t):this.each(Ba(r,t,e))}function Xa(t){return function(){var e=this.parentNode;for(var r in this.__transition)if(+r!==t)return;e&&e.removeChild(this)}}function Fn(){return this.on(\"end.remove\",Xa(this._id))}function Hn(t){var e=this._name,r=this._id;typeof t!=\"function\"&&(t=lt(t));for(var n=this._groups,i=n.length,o=new Array(i),a=0;a=0))throw new Error(`invalid digits: ${t}`);if(e>15)return ni;let r=10**e;return function(n){this._+=n[0];for(let i=1,o=n.length;iht)if(!(Math.abs(p*l-u*f)>ht)||!o)this._append`L${this._x1=e},${this._y1=r}`;else{let x=n-a,_=i-s,b=l*l+u*u,A=x*x+_*_,g=Math.sqrt(b),$=Math.sqrt(d),O=o*Math.tan((Ke-Math.acos((b+d-A)/(2*g*$)))/2),M=O/$,I=O/g;Math.abs(M-1)>ht&&this._append`L${e+M*f},${r+M*p}`,this._append`A${o},${o},0,0,${+(p*x>f*_)},${this._x1=e+I*l},${this._y1=r+I*u}`}}arc(e,r,n,i,o,a){if(e=+e,r=+r,n=+n,a=!!a,n<0)throw new Error(`negative radius: ${n}`);let s=n*Math.cos(i),l=n*Math.sin(i),u=e+s,f=r+l,p=1^a,d=a?i-o:o-i;this._x1===null?this._append`M${u},${f}`:(Math.abs(this._x1-u)>ht||Math.abs(this._y1-f)>ht)&&this._append`L${u},${f}`,n&&(d<0&&(d=d%Ue+Ue),d>ts?this._append`A${n},${n},0,1,${p},${e-s},${r-l}A${n},${n},0,1,${p},${this._x1=u},${this._y1=f}`:d>ht&&this._append`A${n},${n},0,${+(d>=Ke)},${p},${this._x1=e+n*Math.cos(o)},${this._y1=r+n*Math.sin(o)}`)}rect(e,r,n,i){this._append`M${this._x0=this._x1=+e},${this._y0=this._y1=+r}h${n=+n}v${+i}h${-n}Z`}toString(){return this._}};function ii(){return new pt}ii.prototype=pt.prototype;function oi(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}var Qe=Symbol(\"implicit\");function wt(){var t=new mt,e=[],r=[],n=Qe;function i(o){let a=t.get(o);if(a===void 0){if(n!==Qe)return n;t.set(o,a=e.push(o)-1)}return r[a%r.length]}return i.domain=function(o){if(!arguments.length)return e.slice();e=[],t=new mt;for(let a of o)t.has(a)||t.set(a,e.push(a)-1);return i},i.range=function(o){return arguments.length?(r=Array.from(o),i):r.slice()},i.unknown=function(o){return arguments.length?(n=o,i):n},i.copy=function(){return wt(e,r).unknown(n)},oi.apply(i,arguments),i}function ai(t){for(var e=t.length/6|0,r=new Array(e),n=0;n=0))throw new RangeError(`invalid digits: ${r}`);e=n}return t},()=>new pt(e)}var li=Array.prototype.slice;function ui(t){return t[0]}function fi(t){return t[1]}var je=class{constructor(e,r){this._context=e,this._x=r}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(e,r){switch(e=+e,r=+r,this._point){case 0:{this._point=1,this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+e)/2,this._y0,this._x0,r,e,r):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+r)/2,e,this._y0,e,r);break}}this._x0=e,this._y0=r}};function ci(t){return new je(t,!0)}function rs(t){return t.source}function ns(t){return t.target}function hi(t){let e=rs,r=ns,n=ui,i=fi,o=null,a=null,s=si(l);function l(){let u,f=li.call(arguments),p=e.apply(this,f),d=r.apply(this,f);if(o==null&&(a=t(u=s())),a.lineStart(),f[0]=p,a.point(+n.apply(this,f),+i.apply(this,f)),f[0]=d,a.point(+n.apply(this,f),+i.apply(this,f)),a.lineEnd(),u)return a=null,u+\"\"||null}return l.source=function(u){return arguments.length?(e=u,l):e},l.target=function(u){return arguments.length?(r=u,l):r},l.x=function(u){return arguments.length?(n=typeof u==\"function\"?u:Je(+u),l):n},l.y=function(u){return arguments.length?(i=typeof u==\"function\"?u:Je(+u),l):i},l.context=function(u){return arguments.length?(u==null?o=a=null:a=t(o=u),l):o},l}function tr(){return hi(ci)}var Yt=t=>()=>t;function er(t,{sourceEvent:e,target:r,transform:n,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},transform:{value:n,enumerable:!0,configurable:!0},_:{value:i}})}function W(t,e,r){this.k=t,this.x=e,this.y=r}W.prototype={constructor:W,scale:function(t){return t===1?this:new W(this.k*t,this.x,this.y)},translate:function(t,e){return t===0&e===0?this:new W(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return\"translate(\"+this.x+\",\"+this.y+\") scale(\"+this.k+\")\"}};var dt=new W(1,0,0);tt.prototype=W.prototype;function tt(t){for(;!t.__zoom;)if(!(t=t.parentNode))return dt;return t.__zoom}function _e(t){t.stopImmediatePropagation()}function _t(t){t.preventDefault(),t.stopImmediatePropagation()}function is(t){return(!t.ctrlKey||t.type===\"wheel\")&&!t.button}function os(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t,t.hasAttribute(\"viewBox\")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]):[[0,0],[t.clientWidth,t.clientHeight]]}function pi(){return this.__zoom||dt}function as(t){return-t.deltaY*(t.deltaMode===1?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function ss(){return navigator.maxTouchPoints||\"ontouchstart\"in this}function ls(t,e,r){var n=t.invertX(e[0][0])-r[0][0],i=t.invertX(e[1][0])-r[1][0],o=t.invertY(e[0][1])-r[0][1],a=t.invertY(e[1][1])-r[1][1];return t.translate(i>n?(n+i)/2:Math.min(0,n)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}function rr(){var t=is,e=os,r=ls,n=as,i=ss,o=[0,1/0],a=[[-1/0,-1/0],[1/0,1/0]],s=250,l=Ve,u=At(\"start\",\"zoom\",\"end\"),f,p,d,x=500,_=150,b=0,A=10;function g(h){h.property(\"__zoom\",pi).on(\"wheel.zoom\",ot,{passive:!1}).on(\"mousedown.zoom\",at).on(\"dblclick.zoom\",st).filter(i).on(\"touchstart.zoom\",Ft).on(\"touchmove.zoom\",Ht).on(\"touchend.zoom touchcancel.zoom\",bt).style(\"-webkit-tap-highlight-color\",\"rgba(0,0,0,0)\")}g.transform=function(h,y,m,w){var k=h.selection?h.selection():h;k.property(\"__zoom\",pi),h!==k?I(h,y,m,w):k.interrupt().each(function(){X(this,arguments).event(w).start().zoom(null,typeof y==\"function\"?y.apply(this,arguments):y).end()})},g.scaleBy=function(h,y,m,w){g.scaleTo(h,function(){var k=this.__zoom.k,S=typeof y==\"function\"?y.apply(this,arguments):y;return k*S},m,w)},g.scaleTo=function(h,y,m,w){g.transform(h,function(){var k=e.apply(this,arguments),S=this.__zoom,E=m==null?M(k):typeof m==\"function\"?m.apply(this,arguments):m,z=S.invert(E),c=typeof y==\"function\"?y.apply(this,arguments):y;return r(O($(S,c),E,z),k,a)},m,w)},g.translateBy=function(h,y,m,w){g.transform(h,function(){return r(this.__zoom.translate(typeof y==\"function\"?y.apply(this,arguments):y,typeof m==\"function\"?m.apply(this,arguments):m),e.apply(this,arguments),a)},null,w)},g.translateTo=function(h,y,m,w,k){g.transform(h,function(){var S=e.apply(this,arguments),E=this.__zoom,z=w==null?M(S):typeof w==\"function\"?w.apply(this,arguments):w;return r(dt.translate(z[0],z[1]).scale(E.k).translate(typeof y==\"function\"?-y.apply(this,arguments):-y,typeof m==\"function\"?-m.apply(this,arguments):-m),S,a)},w,k)};function $(h,y){return y=Math.max(o[0],Math.min(o[1],y)),y===h.k?h:new W(y,h.x,h.y)}function O(h,y,m){var w=y[0]-m[0]*h.k,k=y[1]-m[1]*h.k;return w===h.x&&k===h.y?h:new W(h.k,w,k)}function M(h){return[(+h[0][0]+ +h[1][0])/2,(+h[0][1]+ +h[1][1])/2]}function I(h,y,m,w){h.on(\"start.zoom\",function(){X(this,arguments).event(w).start()}).on(\"interrupt.zoom end.zoom\",function(){X(this,arguments).event(w).end()}).tween(\"zoom\",function(){var k=this,S=arguments,E=X(k,S).event(w),z=e.apply(k,S),c=m==null?M(z):typeof m==\"function\"?m.apply(k,S):m,v=Math.max(z[1][0]-z[0][0],z[1][1]-z[0][1]),C=k.__zoom,N=typeof y==\"function\"?y.apply(k,S):y,H=l(C.invert(c).concat(v/C.k),N.invert(c).concat(v/N.k));return function(V){if(V===1)V=N;else{var U=H(V),Se=v/U[2];V=new W(Se,c[0]-U[0]*Se,c[1]-U[1]*Se)}E.zoom(null,V)}})}function X(h,y,m){return!m&&h.__zooming||new F(h,y)}function F(h,y){this.that=h,this.args=y,this.active=0,this.sourceEvent=null,this.extent=e.apply(h,y),this.taps=0}F.prototype={event:function(h){return h&&(this.sourceEvent=h),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit(\"start\")),this},zoom:function(h,y){return this.mouse&&h!==\"mouse\"&&(this.mouse[1]=y.invert(this.mouse[0])),this.touch0&&h!==\"touch\"&&(this.touch0[1]=y.invert(this.touch0[0])),this.touch1&&h!==\"touch\"&&(this.touch1[1]=y.invert(this.touch1[0])),this.that.__zoom=y,this.emit(\"zoom\"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit(\"end\")),this},emit:function(h){var y=L(this.that).datum();u.call(h,this.that,new er(h,{sourceEvent:this.sourceEvent,target:g,type:h,transform:this.that.__zoom,dispatch:u}),y)}};function ot(h,...y){if(!t.apply(this,arguments))return;var m=X(this,y).event(h),w=this.__zoom,k=Math.max(o[0],Math.min(o[1],w.k*Math.pow(2,n.apply(this,arguments)))),S=J(h);if(m.wheel)(m.mouse[0][0]!==S[0]||m.mouse[0][1]!==S[1])&&(m.mouse[1]=w.invert(m.mouse[0]=S)),clearTimeout(m.wheel);else{if(w.k===k)return;m.mouse=[S,w.invert(S)],it(this),m.start()}_t(h),m.wheel=setTimeout(E,_),m.zoom(\"mouse\",r(O($(w,k),m.mouse[0],m.mouse[1]),m.extent,a));function E(){m.wheel=null,m.end()}}function at(h,...y){if(d||!t.apply(this,arguments))return;var m=h.currentTarget,w=X(this,y,!0).event(h),k=L(h.view).on(\"mousemove.zoom\",c,!0).on(\"mouseup.zoom\",v,!0),S=J(h,m),E=h.clientX,z=h.clientY;Ie(h.view),_e(h),w.mouse=[S,this.__zoom.invert(S)],it(this),w.start();function c(C){if(_t(C),!w.moved){var N=C.clientX-E,H=C.clientY-z;w.moved=N*N+H*H>b}w.event(C).zoom(\"mouse\",r(O(w.that.__zoom,w.mouse[0]=J(C,m),w.mouse[1]),w.extent,a))}function v(C){k.on(\"mousemove.zoom mouseup.zoom\",null),Re(C.view,w.moved),_t(C),w.event(C).end()}}function st(h,...y){if(t.apply(this,arguments)){var m=this.__zoom,w=J(h.changedTouches?h.changedTouches[0]:h,this),k=m.invert(w),S=m.k*(h.shiftKey?.5:2),E=r(O($(m,S),w,k),e.apply(this,y),a);_t(h),s>0?L(this).transition().duration(s).call(I,E,w,h):L(this).call(g.transform,E,w,h)}}function Ft(h,...y){if(t.apply(this,arguments)){var m=h.touches,w=m.length,k=X(this,y,h.changedTouches.length===w).event(h),S,E,z,c;for(_e(h),E=0;En=>t+e/r**n.state.depth,gi={autoFit:!1,duration:500,embedGlobalCSS:!0,fitRatio:.95,maxInitialScale:2,scrollForPan:mi,initialExpandLevel:-1,zoom:!0,pan:!0,toggleRecursively:!1,color:t=>{var e;return us(`${((e=t.state)==null?void 0:e.path)||\"\"}`)},lineWidth:xi(),maxWidth:0,nodeMinHeight:16,paddingX:8,spacingHorizontal:80,spacingVertical:5};function yi(t){let e={},r={...t},{color:n,colorFreezeLevel:i,lineWidth:o}=r;if((n==null?void 0:n.length)===1){let a=n[0];e.color=()=>a}else if(n!=null&&n.length){let a=wt(n);e.color=s=>a(`${s.state.path}`)}if(i){let a=e.color||gi.color;e.color=s=>(s={...s,state:{...s.state,path:s.state.path.split(\".\").slice(0,i).join(\".\")}},a(s))}if(o){let a=Array.isArray(o)?o:[o,0,1];e.lineWidth=xi(...a)}return[\"duration\",\"fitRatio\",\"initialExpandLevel\",\"maxInitialScale\",\"maxWidth\",\"nodeMinHeight\",\"paddingX\",\"spacingHorizontal\",\"spacingVertical\"].forEach(a=>{let s=r[a];typeof s==\"number\"&&(e[a]=s)}),[\"zoom\",\"pan\"].forEach(a=>{let s=r[a];s!=null&&(e[a]=!!s)}),e}function fs(t){let e=0;for(let r=0;r>>0).toString(36)}function q(t){if(typeof t==\"string\"){let r=t;t=n=>n.matches(r)}let e=t;return function(){let r=Array.from(this.childNodes);return e&&(r=r.filter(n=>e(n))),r}}function cs(t){var e=0,r=t.children,n=r&&r.length;if(!n)e=1;else for(;--n>=0;)e+=r[n].value;t.value=e}function hs(){return this.eachAfter(cs)}function ps(t){var e=this,r,n=[e],i,o,a;do for(r=n.reverse(),n=[];e=r.pop();)if(t(e),i=e.children,i)for(o=0,a=i.length;o=0;--i)r.push(n[i]);return this}function ms(t){for(var e=this,r=[e],n=[],i,o,a;e=r.pop();)if(n.push(e),i=e.children,i)for(o=0,a=i.length;o=0;)r+=n[i].value;e.value=r})}function gs(t){return this.eachBefore(function(e){e.children&&e.children.sort(t)})}function ys(t){for(var e=this,r=vs(e,t),n=[e];e!==r;)e=e.parent,n.push(e);for(var i=n.length;t!==r;)n.splice(i,0,t),t=t.parent;return n}function vs(t,e){if(t===e)return t;var r=t.ancestors(),n=e.ancestors(),i=null;for(t=r.pop(),e=n.pop();t===e;)i=t,t=r.pop(),e=n.pop();return i}function ws(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e}function _s(){var t=[];return this.each(function(e){t.push(e)}),t}function bs(){var t=[];return this.eachBefore(function(e){e.children||t.push(e)}),t}function ks(){var t=this,e=[];return t.each(function(r){r!==t&&e.push({source:r.parent,target:r})}),e}function ar(t,e){var r=new be(t),n=+t.value&&(r.value=t.value),i,o=[r],a,s,l,u;for(e==null&&(e=Es);i=o.pop();)if(n&&(i.value=+i.data.value),(s=e(i.data))&&(u=s.length))for(i.children=new Array(u),l=u-1;l>=0;--l)o.push(a=i.children[l]=new be(s[l])),a.parent=i,a.depth=i.depth+1;return r.eachBefore(As)}function Ss(){return ar(this).eachBefore(Cs)}function Es(t){return t.children}function Cs(t){t.data=t.data.data}function As(t){var e=0;do t.height=e;while((t=t.parent)&&t.height<++e)}function be(t){this.data=t,this.depth=this.height=0,this.parent=null}be.prototype=ar.prototype={constructor:be,count:hs,each:ps,eachAfter:ms,eachBefore:ds,sum:xs,sort:gs,path:ys,ancestors:ws,descendants:_s,leaves:bs,links:ks,copy:Ss};var zs=\"2.1.2\",$s={version:zs},{version:Ts}=$s,Os=Object.freeze({children:t=>t.children,nodeSize:t=>t.data.size,spacing:0});function wi(t){let e=Object.assign({},Os,t);function r(s){let l=e[s];return typeof l==\"function\"?l:()=>l}function n(s){let l=a(o(),s,u=>u.children);return l.update(),l.data}function i(){let s=r(\"nodeSize\"),l=r(\"spacing\");return class vi extends ar.prototype.constructor{constructor(f){super(f)}copy(){let f=a(this.constructor,this,p=>p.children);return f.each(p=>p.data=p.data.data),f}get size(){return s(this)}spacing(f){return l(this,f)}get nodes(){return this.descendants()}get xSize(){return this.size[0]}get ySize(){return this.size[1]}get top(){return this.y}get bottom(){return this.y+this.ySize}get left(){return this.x-this.xSize/2}get right(){return this.x+this.xSize/2}get root(){let f=this.ancestors();return f[f.length-1]}get numChildren(){return this.hasChildren?this.children.length:0}get hasChildren(){return!this.noChildren}get noChildren(){return this.children===null}get firstChild(){return this.hasChildren?this.children[0]:null}get lastChild(){return this.hasChildren?this.children[this.numChildren-1]:null}get extents(){return(this.children||[]).reduce((f,p)=>vi.maxExtents(f,p.extents),this.nodeExtents)}get nodeExtents(){return{top:this.top,bottom:this.bottom,left:this.left,right:this.right}}static maxExtents(f,p){return{top:Math.min(f.top,p.top),bottom:Math.max(f.bottom,p.bottom),left:Math.min(f.left,p.left),right:Math.max(f.right,p.right)}}}}function o(){let s=i(),l=r(\"nodeSize\"),u=r(\"spacing\");return class extends s{constructor(f){super(f),Object.assign(this,{x:0,y:0,relX:0,prelim:0,shift:0,change:0,lExt:this,lExtRelX:0,lThr:null,rExt:this,rExtRelX:0,rThr:null})}get size(){return l(this.data)}spacing(f){return u(this.data,f.data)}get x(){return this.data.x}set x(f){this.data.x=f}get y(){return this.data.y}set y(f){this.data.y=f}update(){return _i(this),bi(this),this}}}function a(s,l,u){let f=(p,d)=>{let x=new s(p);Object.assign(x,{parent:d,depth:d===null?0:d.depth+1,height:0,length:1});let _=u(p)||[];return x.children=_.length===0?null:_.map(b=>f(b,x)),x.children&&Object.assign(x,x.children.reduce((b,A)=>({height:Math.max(b.height,A.height+1),length:b.length+A.length}),x)),x};return f(l,null)}return Object.assign(n,{nodeSize(s){return arguments.length?(e.nodeSize=s,n):e.nodeSize},spacing(s){return arguments.length?(e.spacing=s,n):e.spacing},children(s){return arguments.length?(e.children=s,n):e.children},hierarchy(s,l){let u=typeof l>\"u\"?e.children:l;return a(i(),s,u)},dump(s){let l=r(\"nodeSize\"),u=f=>p=>{let d=f+\" \",x=f+\" \",{x:_,y:b}=p,A=l(p),g=p.children||[],$=g.length===0?\" \":`,${d}children: [${x}${g.map(u(x)).join(x)}${d}],${f}`;return`{ size: [${A.join(\", \")}],${d}x: ${_}, y: ${b}${$}},`};return u(`\n`)(s)}}),n}wi.version=Ts;var _i=(t,e=0)=>(t.y=e,(t.children||[]).reduce((r,n)=>{let[i,o]=r;_i(n,t.y+t.ySize);let a=(i===0?n.lExt:n.rExt).bottom;i!==0&&Is(t,i,o);let s=Ls(a,i,o);return[i+1,s]},[0,null]),Ms(t),qs(t),t),bi=(t,e,r)=>{typeof e>\"u\"&&(e=-t.relX-t.prelim,r=0);let n=e+t.relX;return t.relX=n+t.prelim-r,t.prelim=0,t.x=r+t.relX,(t.children||[]).forEach(i=>bi(i,n,t.x)),t},Ms=t=>{(t.children||[]).reduce((e,r)=>{let[n,i]=e,o=n+r.shift,a=i+o+r.change;return r.relX+=a,[o,a]},[0,0])},Is=(t,e,r)=>{let n=t.children[e-1],i=t.children[e],o=n,a=n.relX,s=i,l=i.relX,u=!0;for(;o&&s;){o.bottom>r.lowY&&(r=r.next);let f=a+o.prelim-(l+s.prelim)+o.xSize/2+s.xSize/2+o.spacing(s);(f>0||f<0&&u)&&(l+=f,Rs(i,f),Ns(t,e,r.index,f)),u=!1;let p=o.bottom,d=s.bottom;p<=d&&(o=Bs(o),o&&(a+=o.relX)),p>=d&&(s=Ps(s),s&&(l+=s.relX))}!o&&s?Xs(t,e,s,l):o&&!s&&Ds(t,e,o,a)},Rs=(t,e)=>{t.relX+=e,t.lExtRelX+=e,t.rExtRelX+=e},Ns=(t,e,r,n)=>{let i=t.children[e],o=e-r;if(o>1){let a=n/o;t.children[r+1].shift+=a,i.shift-=a,i.change-=n-a}},Ps=t=>t.hasChildren?t.firstChild:t.lThr,Bs=t=>t.hasChildren?t.lastChild:t.rThr,Xs=(t,e,r,n)=>{let i=t.firstChild,o=i.lExt,a=t.children[e];o.lThr=r;let s=n-r.relX-i.lExtRelX;o.relX+=s,o.prelim-=s,i.lExt=a.lExt,i.lExtRelX=a.lExtRelX},Ds=(t,e,r,n)=>{let i=t.children[e],o=i.rExt,a=t.children[e-1];o.rThr=r;let s=n-r.relX-i.rExtRelX;o.relX+=s,o.prelim-=s,i.rExt=a.rExt,i.rExtRelX=a.rExtRelX},qs=t=>{if(t.hasChildren){let e=t.firstChild,r=t.lastChild,n=(e.prelim+e.relX-e.xSize/2+r.relX+r.prelim+r.xSize/2)/2;Object.assign(t,{prelim:n,lExt:e.lExt,lExtRelX:e.lExtRelX,rExt:r.rExt,rExtRelX:r.rExtRelX})}},Ls=(t,e,r)=>{for(;r!==null&&t>=r.lowY;)r=r.next;return{lowY:t,index:e,next:r}},Ys=\".markmap{--markmap-max-width: 9999px;--markmap-a-color: #0097e6;--markmap-a-hover-color: #00a8ff;--markmap-code-bg: #f0f0f0;--markmap-code-color: #555;--markmap-highlight-bg: #ffeaa7;--markmap-table-border: 1px solid currentColor;--markmap-font: 300 16px/20px sans-serif;--markmap-circle-open-bg: #fff;--markmap-text-color: #333;--markmap-highlight-node-bg: #ff02;font:var(--markmap-font);color:var(--markmap-text-color)}.markmap-link{fill:none}.markmap-node>circle{cursor:pointer}.markmap-foreign{display:inline-block}.markmap-foreign p{margin:0}.markmap-foreign a{color:var(--markmap-a-color)}.markmap-foreign a:hover{color:var(--markmap-a-hover-color)}.markmap-foreign code{padding:.25em;font-size:calc(1em - 2px);color:var(--markmap-code-color);background-color:var(--markmap-code-bg);border-radius:2px}.markmap-foreign pre{margin:0}.markmap-foreign pre>code{display:block}.markmap-foreign del{text-decoration:line-through}.markmap-foreign em{font-style:italic}.markmap-foreign strong{font-weight:700}.markmap-foreign mark{background:var(--markmap-highlight-bg)}.markmap-foreign table,.markmap-foreign th,.markmap-foreign td{border-collapse:collapse;border:var(--markmap-table-border)}.markmap-foreign img{display:inline-block}.markmap-foreign svg{fill:currentColor}.markmap-foreign>div{width:var(--markmap-max-width);text-align:left}.markmap-foreign>div>div{display:inline-block}.markmap-highlight rect{fill:var(--markmap-highlight-node-bg)}.markmap-dark .markmap{--markmap-code-bg: #1a1b26;--markmap-code-color: #ddd;--markmap-circle-open-bg: #444;--markmap-text-color: #eee}\";var nr=\"g.markmap-node\",Fs=\"path.markmap-link\",Hs=\"g.markmap-highlight\",ir=tr();function di(t,e){let r=Gt(t,e);return t[r]}function or(t){t.stopPropagation()}var Vs=new Vt,ke=class t{constructor(e,r){this.options={...gi},this._disposeList=[],this.handleZoom=n=>{let{transform:i}=n;this.g.attr(\"transform\",i)},this.handlePan=n=>{n.preventDefault();let i=tt(this.svg.node()),o=i.translate(-n.deltaX/i.k,-n.deltaY/i.k);this.svg.call(this.zoom.transform,o)},this.handleClick=(n,i)=>{let o=this.options.toggleRecursively;(mi?n.metaKey:n.ctrlKey)&&(o=!o),this.toggleNode(i,o)},this.ensureView=this.ensureVisible,this.svg=e.datum?e:L(e),this.styleNode=this.svg.append(\"style\"),this.zoom=rr().filter(n=>this.options.scrollForPan&&n.type===\"wheel\"?n.ctrlKey&&!n.button:(!n.ctrlKey||n.type===\"wheel\")&&!n.button).on(\"zoom\",this.handleZoom),this.setOptions(r),this.state={id:this.options.id||this.svg.attr(\"id\")||ur(),rect:{x1:0,y1:0,x2:0,y2:0}},this.g=this.svg.append(\"g\"),this.g.append(\"g\").attr(\"class\",\"markmap-highlight\"),this._observer=new ResizeObserver(cr(()=>{this.renderData()},100)),this._disposeList.push(Vs.tap(()=>{this.setData()}),()=>this._observer.disconnect())}getStyleContent(){let{style:e}=this.options,{id:r}=this.state,n=typeof e==\"function\"?e(r):\"\";return[this.options.embedGlobalCSS&&Ys,n].filter(Boolean).join(`\n`)}updateStyle(){this.svg.attr(\"class\",fr(this.svg.attr(\"class\"),\"markmap\",this.state.id));let e=this.getStyleContent();this.styleNode.text(e)}async toggleNode(e,r=!1){var n,i;let o=(n=e.payload)!=null&&n.fold?0:1;r?St(e,(a,s)=>{a.payload={...a.payload,fold:o},s()}):e.payload={...e.payload,fold:(i=e.payload)!=null&&i.fold?0:1},await this.renderData(e)}_initializeData(e){let r=0,{color:n,initialExpandLevel:i}=this.options,o=0,a=0;return St(e,(s,l,u)=>{var f,p,d,x;a+=1,s.children=(f=s.children)==null?void 0:f.map(b=>({...b})),r+=1,s.state={...s.state,depth:a,id:r,rect:{x:0,y:0,width:0,height:0},size:[0,0]},s.state.key=[(p=u==null?void 0:u.state)==null?void 0:p.id,s.state.id].filter(Boolean).join(\".\")+fs(s.content),s.state.path=[(d=u==null?void 0:u.state)==null?void 0:d.path,s.state.id].filter(Boolean).join(\".\"),n(s);let _=((x=s.payload)==null?void 0:x.fold)===2;_?o+=1:(o||i>=0&&s.state.depth>=i)&&(s.payload={...s.payload,fold:1}),l(),_&&(o-=1),a-=1}),e}_relayout(){if(!this.state.data)return;this.g.selectAll(q(nr)).selectAll(q(\"foreignObject\")).each(function(l){var u;let f=(u=this.firstChild)==null?void 0:u.firstChild,p=[f.scrollWidth,f.scrollHeight];l.state.size=p});let{lineWidth:e,paddingX:r,spacingHorizontal:n,spacingVertical:i}=this.options,o=wi({}).children(l=>{var u;if(!((u=l.payload)!=null&&u.fold))return l.children}).nodeSize(l=>{let[u,f]=l.data.state.size;return[f,u+(u?r*2:0)+n]}).spacing((l,u)=>(l.parent===u.parent?i:i*2)+e(l.data)),a=o.hierarchy(this.state.data);o(a);let s=a.descendants();s.forEach(l=>{let u=l.data;u.state.rect={x:l.y,y:l.x-l.xSize/2,width:l.ySize-n,height:l.xSize}}),this.state.rect={x1:Ct(s,l=>l.data.state.rect.x)||0,y1:Ct(s,l=>l.data.state.rect.y)||0,x2:Et(s,l=>l.data.state.rect.x+l.data.state.rect.width)||0,y2:Et(s,l=>l.data.state.rect.y+l.data.state.rect.height)||0}}setOptions(e){this.options={...this.options,...e},this.options.zoom?this.svg.call(this.zoom):this.svg.on(\".zoom\",null),this.options.pan?this.svg.on(\"wheel\",this.handlePan):this.svg.on(\"wheel\",null)}async setData(e,r){r&&this.setOptions(r),e&&(this.state.data=this._initializeData(e)),this.state.data&&(this.updateStyle(),await this.renderData())}async setHighlight(e){this.state.highlight=e||void 0,await this.renderData()}_getHighlightRect(e){let r=this.svg.node(),n=4/tt(r).k,i={...e.state.rect};return i.x-=n,i.y-=n,i.width+=2*n,i.height+=2*n,i}async renderData(e){let{paddingX:r,autoFit:n,color:i,maxWidth:o,lineWidth:a}=this.options,s=this.state.data;if(!s)return;let l={},u={},f=[];St(s,(c,v,C)=>{var N;(N=c.payload)!=null&&N.fold||v(),l[c.state.id]=c,C&&(u[c.state.id]=C.state.id),f.push(c)});let p={},d={},x=c=>{!c||p[c.state.id]||St(c,(v,C)=>{p[v.state.id]=c.state.id,C()})},_=c=>d[p[c.state.id]]||s.state.rect,b=c=>(l[p[c.state.id]]||s).state.rect;d[s.state.id]=s.state.rect,e&&x(e);let{highlight:A}=this.state;A&&!l[A.state.id]&&(A=void 0);let g=this.g.selectAll(q(Hs)).selectAll(q(\"rect\")).data(A?[this._getHighlightRect(A)]:[]).join(\"rect\").attr(\"x\",c=>c.x).attr(\"y\",c=>c.y).attr(\"width\",c=>c.width).attr(\"height\",c=>c.height),$=this.g.selectAll(q(nr)).each(c=>{d[c.state.id]=c.state.rect}).data(f,c=>c.state.key),O=$.enter().append(\"g\").attr(\"data-depth\",c=>c.state.depth).attr(\"data-path\",c=>c.state.path).each(c=>{x(l[u[c.state.id]])}),M=$.exit().each(c=>{x(l[u[c.state.id]])}),I=$.merge(O).attr(\"class\",c=>{var v;return[\"markmap-node\",((v=c.payload)==null?void 0:v.fold)&&\"markmap-fold\"].filter(Boolean).join(\" \")}),X=I.selectAll(q(\"line\")).data(c=>[c],c=>c.state.key),F=X.enter().append(\"line\").attr(\"stroke\",c=>i(c)).attr(\"stroke-width\",0),ot=X.merge(F),at=I.selectAll(q(\"circle\")).data(c=>{var v;return(v=c.children)!=null&&v.length?[c]:[]},c=>c.state.key),st=at.enter().append(\"circle\").attr(\"stroke-width\",0).attr(\"r\",0).on(\"click\",(c,v)=>this.handleClick(c,v)).on(\"mousedown\",or).merge(at).attr(\"stroke\",c=>i(c)).attr(\"fill\",c=>{var v;return(v=c.payload)!=null&&v.fold&&c.children?i(c):\"var(--markmap-circle-open-bg)\"}),Ft=this._observer,Ht=I.selectAll(q(\"foreignObject\")).data(c=>[c],c=>c.state.key),bt=Ht.enter().append(\"foreignObject\").attr(\"class\",\"markmap-foreign\").attr(\"x\",r).attr(\"y\",0).style(\"opacity\",0).on(\"mousedown\",or).on(\"dblclick\",or);bt.append(\"xhtml:div\").append(\"xhtml:div\").html(c=>c.content).attr(\"xmlns\",\"http://www.w3.org/1999/xhtml\"),bt.each(function(){var c;let v=(c=this.firstChild)==null?void 0:c.firstChild;Ft.observe(v)});let h=M.selectAll(q(\"foreignObject\"));h.each(function(){var c;let v=(c=this.firstChild)==null?void 0:c.firstChild;Ft.unobserve(v)});let y=bt.merge(Ht),m=f.flatMap(c=>{var v;return(v=c.payload)!=null&&v.fold?[]:c.children.map(C=>({source:c,target:C}))}),w=this.g.selectAll(q(Fs)).data(m,c=>c.target.state.key),k=w.exit(),S=w.enter().insert(\"path\",\"g\").attr(\"class\",\"markmap-link\").attr(\"data-depth\",c=>c.target.state.depth).attr(\"data-path\",c=>c.target.state.path).attr(\"d\",c=>{let v=_(c.target),C=[v.x+v.width,v.y+v.height];return ir({source:C,target:C})}).attr(\"stroke-width\",0).merge(w);this.svg.style(\"--markmap-max-width\",o?`${o}px`:null),await new Promise(requestAnimationFrame),this._relayout(),g=g.data(A?[this._getHighlightRect(A)]:[]).join(\"rect\"),this.transition(g).attr(\"x\",c=>c.x).attr(\"y\",c=>c.y).attr(\"width\",c=>c.width).attr(\"height\",c=>c.height),O.attr(\"transform\",c=>{let v=_(c);return`translate(${v.x+v.width-c.state.rect.width},${v.y+v.height-c.state.rect.height})`}),this.transition(M).attr(\"transform\",c=>{let v=b(c),C=v.x+v.width-c.state.rect.width,N=v.y+v.height-c.state.rect.height;return`translate(${C},${N})`}).remove(),this.transition(I).attr(\"transform\",c=>`translate(${c.state.rect.x},${c.state.rect.y})`);let E=M.selectAll(q(\"line\"));this.transition(E).attr(\"x1\",c=>c.state.rect.width).attr(\"stroke-width\",0),F.attr(\"x1\",c=>c.state.rect.width).attr(\"x2\",c=>c.state.rect.width),ot.attr(\"y1\",c=>c.state.rect.height+a(c)/2).attr(\"y2\",c=>c.state.rect.height+a(c)/2),this.transition(ot).attr(\"x1\",-1).attr(\"x2\",c=>c.state.rect.width+2).attr(\"stroke\",c=>i(c)).attr(\"stroke-width\",a);let z=M.selectAll(q(\"circle\"));this.transition(z).attr(\"r\",0).attr(\"stroke-width\",0),st.attr(\"cx\",c=>c.state.rect.width).attr(\"cy\",c=>c.state.rect.height+a(c)/2),this.transition(st).attr(\"r\",6).attr(\"stroke-width\",\"1.5\"),this.transition(h).style(\"opacity\",0),y.attr(\"width\",c=>Math.max(0,c.state.rect.width-r*2)).attr(\"height\",c=>c.state.rect.height),this.transition(y).style(\"opacity\",1),this.transition(k).attr(\"d\",c=>{let v=b(c.target),C=[v.x+v.width,v.y+v.height+a(c.target)/2];return ir({source:C,target:C})}).attr(\"stroke-width\",0).remove(),this.transition(S).attr(\"stroke\",c=>i(c.target)).attr(\"stroke-width\",c=>a(c.target)).attr(\"d\",c=>{let v=c.source,C=c.target,N=[v.state.rect.x+v.state.rect.width,v.state.rect.y+v.state.rect.height+a(v)/2],H=[C.state.rect.x,C.state.rect.y+C.state.rect.height+a(C)/2];return ir({source:N,target:H})}),n&&this.fit()}transition(e){let{duration:r}=this.options;return e.transition().duration(r)}async fit(e=this.options.maxInitialScale){let r=this.svg.node(),{width:n,height:i}=r.getBoundingClientRect(),{fitRatio:o}=this.options,{x1:a,y1:s,x2:l,y2:u}=this.state.rect,f=l-a,p=u-s,d=Math.min(n/f*o,i/p*o,e),x=dt.translate((n-f*d)/2-a*d,(i-p*d)/2-s*d).scale(d);return this.transition(this.svg).call(this.zoom.transform,x).end().catch(kt)}findElement(e){let r;return this.g.selectAll(q(nr)).each(function(n){n===e&&(r={data:n,g:this})}),r}async ensureVisible(e,r){var n;let i=(n=this.findElement(e))==null?void 0:n.data;if(!i)return;let o=this.svg.node(),a=o.getBoundingClientRect(),s=tt(o),[l,u]=[i.state.rect.x,i.state.rect.x+i.state.rect.width+2].map(g=>g*s.k+s.x),[f,p]=[i.state.rect.y,i.state.rect.y+i.state.rect.height].map(g=>g*s.k+s.y),d={left:0,right:0,top:0,bottom:0,...r},x=[d.left-l,a.width-d.right-u],_=[d.top-f,a.height-d.bottom-p],b=x[0]*x[1]>0?di(x,Math.abs)/s.k:0,A=_[0]*_[1]>0?di(_,Math.abs)/s.k:0;if(b||A){let g=s.translate(b,A);return this.transition(this.svg).call(this.zoom.transform,g).end().catch(kt)}}async centerNode(e,r){var n;let i=(n=this.findElement(e))==null?void 0:n.data;if(!i)return;let o=this.svg.node(),a=o.getBoundingClientRect(),s=tt(o),l=(i.state.rect.x+i.state.rect.width/2)*s.k+s.x,u=(i.state.rect.y+i.state.rect.height/2)*s.k+s.y,f={left:0,right:0,top:0,bottom:0,...r},p=(f.left+a.width-f.right)/2,d=(f.top+a.height-f.bottom)/2,x=(p-l)/s.k,_=(d-u)/s.k;if(x||_){let b=s.translate(x,_);return this.transition(this.svg).call(this.zoom.transform,b).end().catch(kt)}}async rescale(e){let r=this.svg.node(),{width:n,height:i}=r.getBoundingClientRect(),o=n/2,a=i/2,s=tt(r),l=s.translate((o-s.x)*(1-e)/s.k,(a-s.y)*(1-e)/s.k).scale(e);return this.transition(this.svg).call(this.zoom.transform,l).end().catch(kt)}destroy(){this.svg.on(\".zoom\",null),this.svg.html(null),this._disposeList.forEach(e=>{e()})}static create(e,r,n=null){let i=new t(e,r);return n&&i.setData(n).then(()=>{i.fit()}),i}};window.markmap={Markmap:ke,deriveOptions:yi};})();\n/*! Bundled license information:\n\nmarkmap-common/dist/index.mjs:\n (*! @gera2ld/jsx-dom v2.2.2 | ISC License *)\n*/\n"}}; -const UI_FINGERPRINT = "8a5624143cd007282fc94abfa3391d6a90cf4f2626765ed7d6d137dbd187714d"; +const FILES = {"/apple-touch-icon.png":{"type":"image/png","b64":true,"data":"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"},"/console/dashboard/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"\n\n\n\n\nArcrun 駕駛艙\n\n\n\n\n\n\n
          \n
          \n
          Arcrun 駕駛艙
          \n
          \n
          \n \n
          \n
          \n
          \n
          \n
          \n
          載入中
          \n
          \n
          \n
          \n
          \n
          \n
          今日完成
          \n
          /
          \n
          \n
          \n
          \n
          收件匣未處理
          \n
          \n
          來自 Telegram
          \n
          \n
          \n
          \n
          等你的事
          \n
          載入中…
          \n
          \n
          \n
          今日路線
          \n
          • 載入中…
          \n
          本週
          \n
            \n
            系統狀況live 健康信號
            \n
            載入中…
            \n
            每 60 秒自動刷新
            \n 進入完整控制台 ›\n
            \n\n\n\n"},"/console/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"\n\n\n\n\nArcrun Console\n\n\n\n\n\n\n\n\n
            \n
            \n
            \n
            Arcrun
            \n
            私人系統・僅供擁有者進入
            \n
            \n
            \n \n \n \n
            \n
            \n
            這是一個人的智慧總部。
            若你不是擁有者,這裡沒有你要找的東西。
            \n \n \n
            \n
            \n\n\n
            \n
            \n
            \n
            Arcrun
            \n
            首次設定
            \n
            系統偵測到尚未設定擁有者帳號。
            設定一組 Email 與密碼,之後只有你能進入。
            \n
            \n
            \n \n \n \n \n
            \n
            \n \n
            \n
            \n\n\n
            \n
            \n
            Arcrun
            \n \n
            總庫搜尋
            \n
            工作流
            \n \n
            設定
            \n
            ☾ 切深色
            \n
            \n
            \n
            \n\n \n
            \n
            Arcrun 控制台
            \n
            \n
            \n
            \n
            \n
            \n
            載入中
            \n
            \n
            \n
            \n
            \n
            \n
            今日完成
            \n
            /
            \n
            \n
            \n
            \n
            收件匣未處理
            \n
            \n
            來自 Telegram
            \n
            \n
            \n
            \n
            等你的事
            \n
            載入中…
            \n
            \n
            \n
            \n
            \n
            \n 今日路線狀態即時同步\n
            \n
            • 載入中…
            \n
            本週
            \n
              \n
              每 60 秒自動刷新
              \n
              \n
              \n
              \n\n \n
              \n
              總庫搜尋
              \n
              \n
              \n
              \n 藏書地圖\n
              \n
              地圖載入中…
              \n
              \n
              \n
              \n \n \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n\n \n
              \n
              \n \n 知識卡片\n
              \n
              \n
              載入中…
              \n
              \n
              關聯視圖
              \n
              \n
              \n
              \n
              \n
              \n\n \n
              \n
              工作流
              \n \n
              載入中…
              \n
              \n
              零件與 Recipes(框架資源)
              \n
              \n \n \n
              \n
              \n
              \n
              \n\n \n
              \n
              憑證管理
              \n
              \n 🛡 保險箱原則:系統只保存目錄與加密後的值,任何頁面都看不到密文內容。只能整筆替換或刪除。\n
              \n
              \n
              新增憑證
              \n
              \n \n \n \n \n
              \n
              \n
              \n
              載入中…
              \n
              \n\n \n
              \n
              分流台全部待辦・80/15/5
              \n
              \n
              載入中…
              \n
              \n\n \n
              \n
              設定
              \n
              \n
              \n
              \n
              \n
              深色模式
              \n
              預設淺色(紙感)。切換立即生效,選擇記在這台裝置(localStorage)。
              \n
              \n
              \n
              \n
              \n
              \n \n
              語意搜尋
              \n
              狀態偵測中…
              \n
              \n
              \n
              \n
              MCP token 有效期(TTL)
              \n
              讀取中…
              \n
              \n 誠實佔位:此頁還不能改這個值——可調功能=Arcrun#19(設定頁改→存 KBDB→發 token 時讀)。實作前要調整請在部署端 env MCP_TOKEN_TTL(mcp worker)設定。\n
              \n
              \n
              \n
              更換帳號密碼
              \n
              需輸入舊密碼驗證身分
              \n
              \n \n \n \n \n
              \n
              \n
              \n \n
              \n
              Portal 帳號密碼救援
              \n
              忘記某個 Portal(RAG 搜尋頁)帳號的密碼,包含你自己那組管理員帳號——不需要先登進 Portal。輸入該帳號的 Email,會產生一組新密碼,只顯示這一次,請立刻抄下並拿去 Portal 登入頁使用。
              \n
              \n \n \n
              \n
              \n
              \n
              \n
              系統資訊
              \n
              載入中…
              \n
              \n \n
              \n
              \n\n
              \n
              \n\n\n
              \n \n
              搜尋
              \n
              工作流
              \n \n
              設定
              \n
              \n\n
              \n\n\n\n\n"},"/favicon.ico":{"type":"image/x-icon","b64":true,"data":"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"},"/favicon.svg":{"type":"image/svg+xml","b64":false,"data":"arcrun icon"},"/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"\n\n\n\nArcrun RAG\n\n\n\n\n\n\n\n

              正在前往搜尋頁… 沒有自動跳轉請點這裡

              \n\n\n"},"/portal/app-mount.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// App 掛載協定 v0.2 的結構自測(Arcrun#152/#82,leo 2026-08-24 拍板)\n// 跑法:node console-ui/public/portal/app-mount.test.mjs\n//\n// 這支守的是**協定的形狀**,不是長相——長相要用瀏覽器量(見 #152 的驗收留言)。\n// 之所以要有它:這一輪的病根是「出口與樣式被做成個案」——筆記 App 有返回鍵、總圖沒有;\n// CIS 表複製進 shadow 了、但 App 根本沒接上。個案會悄悄再長出來,所以把\n// 「不准再有個案」這件事寫成可執行的檢查。\nimport fs from 'node:fs';\n\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\nlet pass = 0, fail = 0;\nconst chk = (label, cond, extra = '') => {\n if (cond) { console.log('PASS:', label); pass++; }\n else { console.log('FAIL:', label, extra); fail++; }\n};\n\n// ── ① 出口:每一個非首頁的 view 都必須有 .pagehead ────────────────────────────\n// ensureBackControls() 是往 `#v- .pagehead` 裡塞返回鍵的。少了那一列 = 那個 view\n// 悄悄沒有出口,而且沒有人會發現(leo 就是這樣卡在總圖)。\nconst viewsLine = html.match(/var VIEWS = \\[(.*?)\\];/);\nif (!viewsLine) throw new Error('抽不到 VIEWS 清單');\nconst VIEWS = viewsLine[1].split(',').map((s) => s.trim().replace(/^'|'$/g, ''));\nconst HOME = (html.match(/var HOME = '([^']+)'/) || [])[1];\nchk('抽得到 VIEWS 與 HOME', VIEWS.length > 0 && !!HOME, `VIEWS=${VIEWS.length} HOME=${HOME}`);\n\nfor (const v of VIEWS) {\n if (v === HOME) continue;\n // 抓 `
              \">` 之後、下一個 view 之前那一段,看有沒有 pagehead\n const at = html.indexOf(`id=\"v-${v}\"`);\n if (at < 0) { chk(`view ${v} 存在於 HTML`, false); continue; }\n const nextView = html.indexOf('id=\"v-', at + 10);\n const block = html.slice(at, nextView < 0 ? at + 4000 : nextView);\n chk(`非首頁 view「${v}」有 .pagehead(返回鍵才掛得上)`, /class=\"pagehead\"/.test(block));\n}\n\n// ── ② 不准再有「某一頁自己的返回鍵」 ─────────────────────────────────────────\nchk('沒有殘留的個案返回鍵(app-back-btn 已撤)', !html.includes('app-back-btn'));\nchk('返回鍵只由 ensureBackControls() 產生(全檔只有一處 createElement 出 data-portal-back)',\n (html.match(/setAttribute\\('data-portal-back'/g) || []).length === 1);\nchk('返回鍵是頁首第一格(insertBefore firstChild)', /insertBefore\\(b, head\\.firstChild\\)/.test(html));\nchk('返回鍵一律回 HOME', /data-portal-back\\]'\\)\\) nav\\(HOME\\)/.test(html));\n\n// ── ③ 樣式:兩種模式與層序 ──────────────────────────────────────────────────\nchk('層序=arcrun-base < app < arcrun(全局在最上面才蓋得過 App)',\n html.includes('@layer arcrun-base, app, arcrun;'));\nchk('沒宣告=跟隨全局(預設 inherit,不是維持 App 原樣)',\n /var inherit = style !== 'own';/.test(html));\nchk('App 自己的 style 會被包進 app 層', /'@layer app\\{' \\+ st\\.textContent \\+ '\\}'/.test(html));\nchk('全局色票/CIS 都進 arcrun 層', (html.match(/'@layer arcrun\\{'/g) || []).length === 2);\nchk('樣式模式從 App 宣告來(不是 Portal 猜的)', /mountAppUi\\(host, app\\.ui_html, app\\.ui_style\\)/.test(html));\n\n// ── ④ 畫布是全局決定的(兩種模式都一樣)────────────────────────────────────\n// leo:「全局要給它多大的畫布也是全局決定的⋯⋯應該整個右側邊欄都是它的」\nchk('App 頁不吃 .page 的寬度上限與左右留白', /#v-app\\.on \\{[^}]*max-width: none;[^}]*padding: 0;/.test(html));\nchk('shadow host 由**外部文件**決定尺寸(App 在 shadow 裡改不動)',\n /#app-ui-mount \\{[^}]*width: 100%/.test(html));\n\n// ── ⑤ 隔離沒有被拆掉(這一輪的紅線)──────────────────────────────────────\nchk('App 仍然掛進 Shadow DOM', /host\\.attachShadow\\(\\{ mode: 'open' \\}\\)/.test(html));\n\nconsole.log(`\\n${fail === 0 ? '✅' : '❌'} pass=${pass} fail=${fail}`);\nprocess.exit(fail === 0 ? 0 : 1);\n"},"/portal/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"\n\n\n\n\nArcrun Portal\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n\n\n
              \n
              \n
              \n
              \"arc>run\" width=\"986\" height=\"176\">\"arc>run\" width=\"986\" height=\"176\">
              \n
              知識入口・Portal
              \n
              \n
              \n \n \n \n
              \n
              \n \n \n
              \n \n \n
              \n
              \n \n
              \n
              \n\n\n
              \n
              \n
              \n
              設定新密碼
              \n
              \n
              \n
              \n \n
              \n
              \n
              \n\n\n
              \n
              \n
              \n
              \"arc>run\" width=\"986\" height=\"176\">\"arc>run\" width=\"986\" height=\"176\">
              \n
              歡迎!先建立你的帳號
              \n
              \n
              \n \n \n \n \n
              \n \n \n
              \n
              這組帳密就是之後登入知識庫用的,只需要設定這一次。
              \n
              \n
              \n\n\n
              \n
              \n
              \"arcrun\"\"arcrun\"
              \n \n
              App 界面
              \n
              App 市集
              \n
              AR 設定
              \n
              App 開發
              \n
              ☾ 切深色
              \n
              \n
              \n
              \n\n \n
              \n
              App 界面
              \n
              載入中…
              \n
              \n\n \n
              \n
              搜尋
              \n
              \n
              \n \n \n
              \n
              \n \n \n \n
              \n \n
              \n
              AI 問答・答案附出處,可回搜尋驗證
              \n
              \n \n \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n
              \n\n \n
              \n
              總圖
              \n
              \n
              \n
              \n 點節點可跳到該實體的圖譜搜尋。這張圖由知識庫的三元組即時算出——同一份地圖的文字版\n (給 AI 注入用的總庫目錄)。\n
              \n
              \n
              \n\n \n
              \n
              \n \n 知識卡片\n
              \n
              載入中…
              \n
              \n\n \n
              \n
              App 市集瀏覽、安裝、或上傳你自己做的 App
              \n
              \n
              \n
              \n
              \n
              上傳你的 App
              \n
              打包成一份宣告,讓其他人也能安裝——尚未接後端,先佔位
              \n
              \n
              \n
              已安裝
              \n
              載入中…
              \n
              可安裝
              \n \n
              載入中…
              \n
              \n
              \n\n \n
              \n
              上傳Markdown / 純文字
              \n
              \n 上傳的文件會進入知識庫收件管線:約 1 分鐘後可在搜尋頁找到;AI 整理後會以 wiki 卡形式出現。支援 .md / .txt(一律以 .md 收件)。\n
              \n
              \n
              把 .md / .txt 檔案拖放到這裡
              \n
              \n \n \n
              \n
              \n
              \n\n \n \n
              \n
              App 開發Workflows/零件/Recipe/Credential
              \n
              \n
              \n
              \n
              \n
              叫 AI 幫你做一個 App
              \n
              用一句話描述你要什麼——尚未接後端,先佔位
              \n
              \n
              \n
              工作環境
              \n
              \n
              \n Workflows\n
              \n
              \n 資料上傳\n
              \n
              零件尚未接進 Portal
              \n
              Recipe尚未接進 Portal
              \n
              Credential尚未接進 Portal
              \n
              \n
              \n
              \n\n
              \n
              工作流唯讀・系統狀態
              \n
              \n 此頁只顯示系統裡的工作流與最近執行狀態,不能觸發執行(觸發屬系統擁有者權限)。\n
              \n
              載入中…
              \n
              \n\n \n
              \n
              \n \n App\n \n
              \n
              載入中…
              \n
              \n\n \n
              \n
              設定
              \n \n
              \n
              帳號
              \n
              管理
              \n
              \n
              \n \n
              \n
              \n
              \n
              \n 版本\n \n
              \n
              查詢中…
              \n
              \n \n
              \n
              \n
              \n
              我的帳號
              \n
              載入中…
              \n
              \n
              \n
              \n
              \n
              深色模式
              \n
              預設淺色(紙感)。切換立即生效,選擇記在這台裝置。
              \n
              \n
              \n
              \n
              \n \n
              \n
              更改密碼
              \n
              需輸入舊密碼驗證身分;新密碼至少 8 碼
              \n
              \n \n \n \n \n
              \n
              \n
              \n \n
              \n \n
              \n 你的知識庫網址(小幫手連線用)\n \n \n
              \n
              同步小幫手
              \n
              把你電腦上的資料夾變成知識庫。換電腦或重裝時可以再下載一次。
              \n
              \n 下載 Mac 版\n
              封測版未簽章,第一次請右鍵→打開。裝好第一次開啟時,貼上這個網址+你的帳號密碼就連上了。
              \n
              \n
              \n \n
              \n
              \n 你的 MCP 網址(給你的 AI 連線用)\n \n \n
              \n
              接上你的 AI(MCP)
              \n
              把上面這串網址貼到 Claude、ChatGPT 等 AI 的「新增自訂連接器」欄位,就能讓你的 AI 直接查這個知識庫。
              \n
              \n \n
              \n
              AI 設定
              \n
              \n 這裡不需要任何設定。聊天問答用的是你自己 Cloudflare 帳號內建的 AI,裝好就能直接問。
              \n 文件整理成知識卡的部分,請在同步小幫手(電腦上的托盤圖示)的「AI 設定…」填一把 Gemini API Key。\n
              \n
              \n \n
              \n
              疑難排解
              \n
              要回報問題,請到你電腦上的 Arcrun(同步小幫手)「版本與更新」頁——那裡的「疑難排解」按一下就能匯出完整診斷檔給我們(只有統計數字,不含你的任何文件內容)。
              \n
              \n \n
              \n
              \n\n \n
              \n
              管理帳號與知識庫授權
              \n
              \n
              帳號
              \n
              管理
              \n
              \n\n
              執行紀錄保留期
              \n
              \n
              保留天數
              \n
              執行紀錄是稽核資料,超過保留天數會被每日自動清除;預設 90 天(3 個月),也可設為「不刪除」(企業稽核用途)。
              \n
              \n \n \n \n
              \n
              \n
              \n
              \n\n
              帳號管理
              \n
              \n
              新增同仁帳號
              \n
              建立後會產生一組一次性密碼——只顯示這一次,請當場轉交同仁(同仁可在「設定」自行改密)。
              \n
              \n \n \n \n \n
              \n
              \n
              \n
              \n
              載入中…
              \n\n
              庫目錄管理
              \n
              \n 你的知識庫網址(小幫手連線用)\n \n \n
              \n
              \n \n 裝好同步小幫手並選好要看守的資料夾之後,每個資料夾會自動成為一個「庫」出現在下面——不需要人工新增。下面還是空的,代表小幫手還沒裝好或還沒選資料夾。\n
              \n
              載入中…
              \n
              \n\n
              \n
              \n\n\n
              \n
              App 界面
              \n
              App 市集
              \n
              AR 設定
              \n
              App 開發
              \n
              \n\n
              \n\n\n\n"},"/portal/os-split.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"import fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname,'utf8');\n// 抽出 daemonPick 相關函式(從 DAEMON_BASE_DEFAULT 到 daemonHint 結尾)\n//\n// 🔴 2026-08-05:結尾標記本來寫死 daemonHint 的**整句文案**,於是同日改 Mac 提示語\n// (zip→DMG 的步驟不同)就讓這支自測直接炸「抽不到函式區塊」,而且沒人發現。\n// ⇒ 改成錨定「函式結束」這個結構,不再綁文案——文案本來就會改,測試不該為此壞掉。\nconst start = html.indexOf('var DAEMON_BASE_DEFAULT');\nconst hintAt = html.indexOf('function daemonHint', start);\nconst endMark = '\\n }';\nconst end = hintAt < 0 ? -1 : html.indexOf(endMark, hintAt) + endMark.length;\nif (start < 0 || hintAt < 0 || end < start) throw new Error('抽不到函式區塊');\nconst src = html.slice(start, end);\n\nconst cases = [\n ['Windows', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36'],\n ['Mac', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15'],\n ['iPhone', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Safari/604.1'],\n ['Linux', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120 Safari/537.36'],\n];\nlet pass=0, fail=0;\nconst chk=(l,c,extra='')=>{ if(c){console.log('PASS:',l);pass++;} else {console.log('FAIL:',l,extra);fail++;} };\n\nfor (const [name, ua] of cases) {\n const fn = new Function('navigator','window', src + '; return {daemonPick:daemonPick, daemonHint:daemonHint, daemonBase:daemonBase};');\n const api = fn({userAgent: ua}, {});\n const d = api.daemonPick();\n const label = d.sure ? d.pick.label : '(兩個都給)';\n const url = d.sure ? d.pick.url : d.mac.url + ' + ' + d.win.url;\n console.log(`\\n[${name}] sure=${d.sure} → ${label}`);\n console.log(` url: ${url}`);\n if (name==='Windows') {\n chk('Windows 給 win zip', d.sure && d.pick.url.endsWith('ArcrunRAG-win-unsigned.zip'), d.pick&&d.pick.url);\n chk('Windows 另一版是 Mac', d.other && d.other.url.endsWith('ArcrunRAG-mac.dmg'));\n chk('Windows 話術提 藍色視窗', api.daemonHint('win').includes('仍要執行'));\n }\n if (name==='Mac') {\n // 2026-08-05:Mac 一律給 DMG(拖進 Applications 的標準安裝畫面),不再給 zip\n // ——zip 解開就是一個裸 .app,使用者會直接在「下載」資料夾雙擊執行,自更新會蓋錯位置。\n chk('Mac 給 dmg(不是 zip)', d.sure && d.pick.url.endsWith('ArcrunRAG-mac.dmg'));\n chk('Mac 另一版是 Windows', d.other && d.other.url.endsWith('win-unsigned.zip'));\n chk('Mac 話術提 右鍵打開', api.daemonHint('mac').includes('右鍵'));\n }\n if (name==='iPhone' || name==='Linux') {\n // iPhone 含 \"Mac OS X\" 但不是桌機 Mac;Linux 兩者皆非 → 都該落在「不確定=兩個都給」\n if (name==='Linux') chk('Linux 判不出來→兩個都給', d.sure===false);\n if (name==='iPhone') chk('iPhone 不該被判成 Mac(手機→兩個都給)', d.sure===false, 'sure='+d.sure);\n }\n}\n// 舊 key 相容\nconst fn2 = new Function('navigator','window', src + '; return daemonBase();');\nconsole.log('\\n[相容] daemonDownload 舊 key →', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}}));\nchk('舊 key 推得出目錄', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}})==='https://x.dev/d/');\nchk('daemonBase 新 key 優先', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonBase:'https://y.dev/z'}})==='https://y.dev/z/');\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail?1:0);\n"},"/portal/retrieval-tree.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// 檢索過程樹(Arcrun#44 第二輪,leo 2026-08-18「希望以樹狀圖來展示」)的資料組裝測試。\n// 只測**樹的資料**,不測 markmap 怎麼畫——畫圖是現成函式庫的事(D97),\n// 我們自己要為之負責的是「層級對不對、有沒有把讀過的東西吞掉」。\n// 跑法:node console-ui/public/portal/retrieval-tree.test.mjs\nimport fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\n\nconst grab = (name) => {\n const i = html.indexOf(`function ${name}(`);\n if (i < 0) throw new Error(`找不到 ${name}`);\n let d = 0, j = html.indexOf('{', i);\n for (let k = j; k < html.length; k++) { if (html[k] === '{') d++; if (html[k] === '}') { d--; if (!d) return html.slice(i, k + 1); } }\n throw new Error('括號不平衡');\n};\nconst src = ['esc', 'srcLocalPath', 'machineLabelMap', 'srcCrumbs', 'retrievalTreeData', 'visibleLeaves', 'treeListHtml'].map(grab).join('\\n');\nconst fn = new Function(src + '\\nvar SEARCH_ROUTE_LABEL = { graph: \"圖譜命中子庫\", index: \"索引退回選庫\", all: \"全庫讀取\" };'\n + '\\nreturn { srcCrumbs, machineLabelMap, retrievalTreeData, visibleLeaves, treeListHtml };')();\n\nlet pass = 0, fail = 0;\nconst t = (l, c, e = '') => { c ? (console.log('PASS:', l), pass++) : (console.log('FAIL:', l, e), fail++); };\nconst txt = (n) => String(n.content).replace(/<[^>]*>/g, '');\n\n// 2026-08-18 從 youlin stage 實跑 /portal/data/chat 抓下來的真回應形狀(narrative 真的是空字串)\nconst REAL = {\n route: 'graph', vector_used: false, libraries_considered: 8,\n libraries: [{ library: 'testkb', score: 1.69, via: 'graph', matched_entities: ['Arcrun124驗證卡'] }],\n indexes_read: [\n { library: 'general', narrative: '', selected: false, top_entities: [], triplet_count: 0, entry_count: 100 },\n { library: 'testkb', narrative: '', selected: true, top_entities: ['Arcrun124驗證卡'], triplet_count: 4, entry_count: 9 },\n { library: 'kb', narrative: '', selected: false, top_entities: ['knowledge-base'], triplet_count: 7, entry_count: 16 }\n ],\n pages_read: [{ page: 'Arcrun124驗證卡', library: 'testkb', chars: 231, source: 'kb://test/arcrun-124-verify.md#0' }]\n};\n\n// ① 層級=leo 畫的那張:搜尋詞 → 命中子庫 → 索引敘事 → 原文片段\nconst tree = fn.retrievalTreeData('Arcrun124驗證卡是什麼?', REAL);\nt('第 1 層是搜尋詞', txt(tree).indexOf('Arcrun124驗證卡是什麼?') === 0, txt(tree));\nconst lib = tree.children[0];\nt('第 2 層是命中子庫(帶分數與命中實體)', /testkb/.test(txt(lib)) && /1\\.69/.test(txt(lib)) && /命中實體/.test(txt(lib)), txt(lib));\nconst idx = lib.children[0];\nt('第 3 層是該子庫的索引敘事', /三元組/.test(txt(idx)) && /筆條目/.test(txt(idx)), txt(idx));\nconst page = idx.children[0];\nt('第 4 層是原文片段(掛在索引敘事底下)', /Arcrun124驗證卡/.test(txt(page)) && /231 字/.test(txt(page)), txt(page));\nt('原文片段帶資料夾麵包屑', /test › arcrun-124-verify\\.md/.test(txt(page)), txt(page));\n\n// ② narrative 是空字串時不假裝有敘事(線上真資料就是這樣)\nt('沒有敘事就說沒有,不留空白', /索引沒有敘事/.test(txt(idx)), txt(idx));\n\n// ③ 沒被選用的子庫不可以被吞掉,但要收合起來不擠掉主線\nconst rest = tree.children[tree.children.length - 1];\nt('沒讀原文的子庫收在一個節點底下', /考慮過但沒讀原文(2 個子庫)/.test(txt(rest)), txt(rest));\nt('那個節點預設收合', rest.payload && rest.payload.fold === 1, JSON.stringify(rest.payload));\nt('主線只有命中的子庫', tree.children.length === 2, `實得 ${tree.children.length} 條分支`);\n\n// ④ 讀了原文、但沒出現在 libraries[] 的子庫(route=all)也要長出來\nconst allRoute = fn.retrievalTreeData('問句', {\n route: 'all', libraries: [], indexes_read: [],\n pages_read: [{ page: 'P', library: 'kb', chars: 10, source: 'kb://a/b/c.md#2' }]\n});\nt('route=all 時仍有子庫分支', allRoute.children.length === 1 && /kb/.test(txt(allRoute.children[0])), txt(allRoute.children[0]));\nt('沒有索引敘事時不生假的中間層', /^P/.test(txt(allRoute.children[0].children[0])), txt(allRoute.children[0].children[0]));\n\n// ⑤ 完全沒資料時不要生一棵空樹\nconst empty = fn.retrievalTreeData('問句', { route: 'all', libraries: [], indexes_read: [], pages_read: [] });\nt('沒有任何子庫時說明原因', /全庫作答|沒有回報/.test(txt(empty.children[0])), txt(empty.children[0]));\n\n// ⑥ 麵包屑:資料夾可以有很多層,全部拆開;機器那一層現在是「沒有」\nconst c = fn.srcCrumbs('kb://RFP/2026/design.md#3', 'kb');\nt('多層資料夾全部拆開', JSON.stringify(c.dirs) === '[\"RFP\",\"2026\"]', JSON.stringify(c.dirs));\nt('檔名拆得出來', c.file === 'design.md', c.file);\nt('片段編號拆得出來', c.frag === '3', c.frag);\nt('子庫帶得進來', c.library === 'kb', c.library);\nt('機器那一層是 null(資料裡真的沒有)', c.machine === null, String(c.machine));\nt('相對路徑原樣留著(複製給用戶去本機找)', c.path === 'RFP/2026/design.md', c.path);\n\nconst g = fn.srcCrumbs('gitea:Leo/InkStoneCo@wiki/x.md#1', 'kb');\nt('非 kb:// 來源不硬拆成假麵包屑', g.path === '' && g.file === '' && g.dirs.length === 0, JSON.stringify(g));\n\n// ⑦ markmap 載不進來時的退路:同一份樹資料畫成巢狀清單\nconst list = fn.treeListHtml(tree);\nt('退路是巢狀清單', /
                /.test(list) && (list.match(/
                  = 3, String((list.match(/
                    n.content).join(' ');\nt('樹上兩個片段標的是同一個機器名', (both.match(/教育部 Leo 的 Mac/g) || []).length === 2, both.slice(0, 200));\n\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail ? 1 : 0);\n"},"/portal/safejson.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"import fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname,'utf8');\n\n// 抽出 safeJson 與 friendlyErr 求值\nconst grab = (name) => {\n const i = html.indexOf(`function ${name}(`);\n if (i < 0) throw new Error(`找不到 ${name}`);\n let d=0, j=html.indexOf('{', i);\n for (let k=j;k{c?(console.log('PASS:',l),pass++):(console.log('FAIL:',l,e),fail++)};\n\n// ① safeJson:非 JSON 不可拋例外(同事撞到的 404 HTML 頁)\nconst html404 = '404 Not Found';\nawait fn.safeJson({ text: () => Promise.resolve(html404) })\n .then(d => t('404 HTML → 回空物件不拋錯', typeof d === 'object' && d !== null))\n .catch(e => t('404 HTML → 不該拋錯', false, e.message));\n\nawait fn.safeJson({ text: () => Promise.resolve('') })\n .then(d => t('空回應 → 回空物件', JSON.stringify(d)==='{}'))\n .catch(() => t('空回應 → 不該拋錯', false));\n\nawait fn.safeJson({ text: () => Promise.resolve('{\"error\":\"帳號或密碼不對\"}') })\n .then(d => t('正常 JSON 仍要解析得出來', d.error === '帳號或密碼不對'), )\n .catch(() => t('正常 JSON 不該拋錯', false));\n\n// ② friendlyErr:不可把技術訊息噴給使用者\nconst leak = fn.friendlyErr(new Error('Unexpected non-whitespace character after JSON at position 4'));\nt('JSON 錯誤 → 不外洩原文', !/JSON|position/i.test(leak), `實得: ${leak}`);\nt('JSON 錯誤 → 說人話', /伺服器回應異常/.test(leak), `實得: ${leak}`);\n\nconst net = fn.friendlyErr(new Error('Failed to fetch'));\nt('網路錯誤 → 既有訊息保留', /連線中斷/.test(net), `實得: ${net}`);\n\nconst ours = fn.friendlyErr(new Error('帳號或密碼不對——用你在知識庫網站設定的那組'));\nt('我們自己的中文訊息 → 原樣顯示', /帳號或密碼不對/.test(ours), `實得: ${ours}`);\n\nconst stack = fn.friendlyErr(new Error('TypeError: Cannot read properties of undefined'));\nt('英文技術訊息 → 收斂不外洩', !/TypeError|undefined/.test(stack), `實得: ${stack}`);\n\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail?1:0);\n"},"/portal/tree-links.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// 樹狀圖的**線型**測試(InkStoneCo#44 第三輪,leo 2026-08-18:\n// 「線型可以改成類似 terminal tree 那樣往右、往下展開的 L 型折線,線的色彩不要花花綠綠」)。\n//\n// 為什麼這個要測,而 markmap 的佈局不測(D97「畫圖是現成函式庫的事」):\n// 折線的座標換算是**我們自己寫的那一段**——markmap 交出曲線 `d`,我們把它改寫成 L 型。\n// 換算錯了畫面只會「線歪掉」,不會報錯,靠眼睛看很容易漏。\n// 跑法:node console-ui/public/portal/tree-links.test.mjs\nimport fs from 'node:fs';\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\n\nconst grab = (name) => {\n const i = html.indexOf(`function ${name}(`);\n if (i < 0) throw new Error(`找不到 ${name}`);\n let d = 0;\n for (let k = html.indexOf('{', i); k < html.length; k++) { if (html[k] === '{') d++; if (html[k] === '}') { d--; if (!d) return html.slice(i, k + 1); } }\n throw new Error('括號不平衡');\n};\nconst fn = new Function(['elbowPath', 'elbowFromCurve'].map(grab).join('\\n')\n + '\\nreturn { elbowPath, elbowFromCurve };')();\n\nlet pass = 0, fail = 0;\nconst t = (l, c, e = '') => { c ? (console.log('PASS:', l), pass++) : (console.log('FAIL:', l, e), fail++); };\n\n// ① 折線只由 M/L 組成——沒有任何曲線指令。這同時是「已經改過了」的判斷依據,\n// 所以它壞掉不只是變醜,是會無限重寫。\nconst down = fn.elbowPath(100, 50, 152, 120);\nt('往下的連線是 L 型折線,只有 M/L', /^M[\\d.,-]+(L[\\d.,-]+){3}$/.test(down), down);\nt('折線不含任何曲線指令(C/S/Q/A)', !/[CSQAcsqa]/.test(down), down);\n\n// ② 轉折點:先往右 12px、再垂直、最後水平接進子節點 ⇒ 兩個直角\nt('起點是父節點右緣', down.startsWith('M100,50'), down);\nt('第一段水平:往右 12px 到幹線', down.indexOf('L112,50') > 0, down);\nt('第二段垂直:幹線 x 不變、y 換到子節點那列', down.indexOf('L112,120') > 0, down);\nt('第三段水平:接進子節點左緣', down.endsWith('L152,120'), down);\n\n// ③ 同一個父節點的所有子節點共用同一條垂直幹線(起點相同 ⇒ 轉折 x 相同)\nconst up = fn.elbowPath(100, 50, 152, -20);\nt('往上的子節點用同一條幹線 x=112', up.indexOf('L112,50') > 0 && up.indexOf('L112,-20') > 0, up);\n\n// ④ 父子同一列時不要硬折一個看不見的直角,畫一條直線就好\nconst flat = fn.elbowPath(100, 50, 152, 50);\nt('同一列=一條直線(兩段)', flat === 'M100,50L152,50', flat);\n\n// ⑤ 框很窄/間距被壓縮時,幹線不可以跑到子節點右邊(會變成往回折的怪線)\nconst tight = fn.elbowPath(100, 50, 104, 120);\nt('間距 < 12 時幹線收回到子節點左緣', tight.indexOf('L104,50L104,120') > 0, tight);\n\n// ⑥ 吃得下 markmap 真的吐出來的 bumpX 字串(d3 linkHorizontal + bumpX 的格式)\nconst real = fn.elbowFromCurve('M100,50C126,50,126,120,152,120');\nt('bumpX 曲線 → 折線(只取頭尾兩點,控制點丟掉)', real === 'M100,50L112,50L112,120L152,120', real);\n\n// ⑦ 已經是折線的就不要再動——不然 observer 改寫→再觸發→再改寫,會轉不停\nt('已經是折線 ⇒ 回 null(不重寫)', fn.elbowFromCurve(real) === null, String(fn.elbowFromCurve(real)));\nt('空字串 ⇒ 回 null', fn.elbowFromCurve('') === null);\nt('認不得的 d ⇒ 回 null,不亂畫', fn.elbowFromCurve('C') === null);\n\n// ⑧ 小數座標(markmap 的佈局會給小數)不要吐出一長串尾數\nconst frac = fn.elbowPath(100.123456, 50.987654, 152.5, 120.25);\nt('座標四捨五入到小數兩位', frac === 'M100.12,50.99L112.12,50.99L112.12,120.25L152.5,120.25', frac);\n\nconsole.log(`\\n${fail ? '❌' : '✅'} ${pass} passed, ${fail} failed`);\nprocess.exit(fail ? 1 : 0);\n"},"/portal/vendor/markmap-view.min.js":{"type":"application/javascript; charset=utf-8","b64":false,"data":"(()=>{var ki=\"npm2url/dist/index.cjs\",Si={jsdelivr:t=>`https://cdn.jsdelivr.net/npm/${t}`,unpkg:t=>`https://unpkg.com/${t}`};async function Ei(t,e){let r=await fetch(t,{signal:e});if(!r.ok)throw r;await r.text()}var Ce=class{constructor(){this.providers={...Si},this.provider=\"jsdelivr\"}async getFastestProvider(e=5e3,r=ki){let n=new AbortController,i=0;try{return await new Promise((o,a)=>{Promise.all(Object.entries(this.providers).map(async([s,l])=>{try{await Ei(l(r),n.signal),o(s)}catch{}})).then(()=>a(new Error(\"All providers failed\"))),i=setTimeout(a,e,new Error(\"Timed out\"))})}finally{n.abort(),clearTimeout(i)}}async findFastestProvider(e,r){return this.provider=await this.getFastestProvider(e,r),this.provider}setProvider(e,r){r?this.providers[e]=r:delete this.providers[e]}getFullUrl(e,r=this.provider){if(e.includes(\"://\"))return e;let n=this.providers[r];if(!n)throw new Error(`Provider ${r} not found`);return n(e)}},Gs=new Ce,Vt=class{constructor(){this.listeners=[]}tap(e){return this.listeners.push(e),()=>this.revoke(e)}revoke(e){let r=this.listeners.indexOf(e);r>=0&&this.listeners.splice(r,1)}revokeAll(){this.listeners.splice(0)}call(...e){for(let r of this.listeners)r(...e)}};var Ci=Math.random().toString(36).slice(2,8),sr=0;function ur(){return sr+=1,`mm-${Ci}-${sr}`}function kt(){}function St(t,e){let r=(n,i)=>e(n,()=>{var o;return(o=n.children)==null?void 0:o.map(a=>r(a,n))},i);return r(t)}function fr(t,...e){let r=(t||\"\").split(\" \").filter(Boolean);return e.forEach(n=>{n&&r.indexOf(n)<0&&r.push(n)}),r.join(\" \")}function Ai(t){let e={};return function(...n){let i=`${n[0]}`,o=e[i];return o||(o={value:t(...n)},e[i]=o),o.value}}function cr(t,e){let r={timer:0};function n(){r.timer&&(window.clearTimeout(r.timer),r.timer=0)}function i(){n(),r.args&&(r.result=t(...r.args))}return function(...a){return n(),r.args=a,r.timer=window.setTimeout(i,e),r.result}}var hr=1,pr=2,zi=\"http://www.w3.org/2000/svg\",Ee=\"http://www.w3.org/1999/xlink\",$i={show:Ee,actuate:Ee,href:Ee},Ti=t=>typeof t==\"string\"||typeof t==\"number\",Oi=t=>(t==null?void 0:t.vtype)===hr,Mi=t=>(t==null?void 0:t.vtype)===pr;function Ii(t,e,...r){return e=Object.assign({},e,{children:r.length===1?r[0]:r}),Ri(t,e)}function Ri(t,e){let r;if(typeof t==\"string\")r=hr;else if(typeof t==\"function\")r=pr;else throw new Error(\"Invalid VNode type\");return{vtype:r,type:t,props:e}}function Ni(t){return t.children}var Pi={isSvg:!1};function lr(t,e){Array.isArray(e)||(e=[e]),e=e.filter(Boolean),e.length&&t.append(...e)}function Bi(t,e,r){for(let n in e)if(!(n===\"key\"||n===\"children\"||n===\"ref\"))if(n===\"dangerouslySetInnerHTML\")t.innerHTML=e[n].__html;else if(n===\"innerHTML\"||n===\"textContent\"||n===\"innerText\"||n===\"value\"&&[\"textarea\",\"select\"].includes(t.tagName)){let i=e[n];i!=null&&(t[n]=i)}else n.startsWith(\"on\")?t[n.toLowerCase()]=e[n]:Di(t,n,e[n],r.isSvg)}var Xi={className:\"class\",labelFor:\"for\"};function Di(t,e,r,n){if(e=Xi[e]||e,r===!0)t.setAttribute(e,\"\");else if(r===!1)t.removeAttribute(e);else{let i=n?$i[e]:void 0;i!==void 0?t.setAttributeNS(i,e,r):t.setAttribute(e,r)}}function qi(t){return t.reduce((e,r)=>e.concat(r),[])}function Ae(t,e){return Array.isArray(t)?qi(t.map(r=>Ae(r,e))):ze(t,e)}function ze(t,e=Pi){if(t==null||typeof t==\"boolean\")return null;if(t instanceof Node)return t;if(Mi(t)){let{type:r,props:n}=t;if(r===Ni){let o=document.createDocumentFragment();if(n.children){let a=Ae(n.children,e);lr(o,a)}return o}let i=r(n);return ze(i,e)}if(Ti(t))return document.createTextNode(`${t}`);if(Oi(t)){let r,{type:n,props:i}=t;if(!e.isSvg&&n===\"svg\"&&(e=Object.assign({},e,{isSvg:!0})),e.isSvg?r=document.createElementNS(zi,n):r=document.createElement(n),Bi(r,i,e),i.children){let a=e;e.isSvg&&n===\"foreignObject\"&&(a=Object.assign({},a,{isSvg:!1}));let s=Ae(i.children,a);s!=null&&lr(r,s)}let{ref:o}=i;return typeof o==\"function\"&&o(r),r}throw new Error(\"mount: Invalid Vnode!\")}function Li(t){return ze(t)}function Yi(...t){return Li(Ii(...t))}var Ws=Ai(t=>{document.head.append(Yi(\"link\",{rel:\"preload\",as:\"script\",href:t}))});var mt=class extends Map{constructor(e,r=Vi){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:r}}),e!=null)for(let[n,i]of e)this.set(n,i)}get(e){return super.get(dr(this,e))}has(e){return super.has(dr(this,e))}set(e,r){return super.set(Fi(this,e),r)}delete(e){return super.delete(Hi(this,e))}};function dr({_intern:t,_key:e},r){let n=e(r);return t.has(n)?t.get(n):r}function Fi({_intern:t,_key:e},r){let n=e(r);return t.has(n)?t.get(n):(t.set(n,r),r)}function Hi({_intern:t,_key:e},r){let n=e(r);return t.has(n)&&(r=t.get(n),t.delete(n)),r}function Vi(t){return t!==null&&typeof t==\"object\"?t.valueOf():t}function Et(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}function Ct(t,e){let r;if(e===void 0)for(let n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}function Gt(t,e){let r,n=-1,i=-1;if(e===void 0)for(let o of t)++i,o!=null&&(r>o||r===void 0&&o>=o)&&(r=o,n=i);else for(let o of t)(o=e(o,++i,t))!=null&&(r>o||r===void 0&&o>=o)&&(r=o,n=i);return n}var Gi={value:()=>{}};function xr(){for(var t=0,e=arguments.length,r={},n;t=0&&(n=r.slice(i+1),r=r.slice(0,i)),r&&!e.hasOwnProperty(r))throw new Error(\"unknown type: \"+r);return{type:r,name:n}})}Wt.prototype=xr.prototype={constructor:Wt,on:function(t,e){var r=this._,n=Wi(t+\"\",r),i,o=-1,a=n.length;if(arguments.length<2){for(;++o0)for(var r=new Array(i),n=0,i,o;n=0&&(e=t.slice(0,r))!==\"xmlns\"&&(t=t.slice(r+1)),$e.hasOwnProperty(e)?{space:$e[e],local:t}:t}function Ui(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===Kt&&e.documentElement.namespaceURI===Kt?e.createElement(t):e.createElementNS(r,t)}}function Qi(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Ut(t){var e=Q(t);return(e.local?Qi:Ui)(e)}function Zi(){}function lt(t){return t==null?Zi:function(){return this.querySelector(t)}}function gr(t){typeof t!=\"function\"&&(t=lt(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i=O&&(O=$+1);!(I=A[O])&&++O<_;);M._next=I||null}}return a=new T(a,n),a._enter=s,a._exit=l,a}function uo(t){return typeof t==\"object\"&&\"length\"in t?t:Array.from(t)}function Er(){return new T(this._exit||this._groups.map(Zt),this._parents)}function Cr(t,e,r){var n=this.enter(),i=this,o=this.exit();return typeof t==\"function\"?(n=t(n),n&&(n=n.selection())):n=n.append(t+\"\"),e!=null&&(i=e(i),i&&(i=i.selection())),r==null?o.remove():r(o),n&&i?n.merge(i).order():i}function Ar(t){for(var e=t.selection?t.selection():t,r=this._groups,n=e._groups,i=r.length,o=n.length,a=Math.min(i,o),s=new Array(i),l=0;l=0;)(a=n[i])&&(o&&a.compareDocumentPosition(o)^4&&o.parentNode.insertBefore(a,o),o=a);return this}function $r(t){t||(t=fo);function e(p,d){return p&&d?t(p.__data__,d.__data__):!p-!d}for(var r=this._groups,n=r.length,i=new Array(n),o=0;oe?1:t>=e?0:NaN}function Tr(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function Or(){return Array.from(this)}function Mr(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?yo:typeof e==\"function\"?wo:vo)(t,e,r==null?\"\":r)):et(this.node(),t)}function et(t,e){return t.style.getPropertyValue(e)||Jt(t).getComputedStyle(t,null).getPropertyValue(e)}function _o(t){return function(){delete this[t]}}function bo(t,e){return function(){this[t]=e}}function ko(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function Xr(t,e){return arguments.length>1?this.each((e==null?_o:typeof e==\"function\"?ko:bo)(t,e)):this.node()[t]}function Dr(t){return t.trim().split(/^|\\s+/)}function Oe(t){return t.classList||new qr(t)}function qr(t){this._node=t,this._names=Dr(t.getAttribute(\"class\")||\"\")}qr.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute(\"class\",this._names.join(\" \")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute(\"class\",this._names.join(\" \")))},contains:function(t){return this._names.indexOf(t)>=0}};function Lr(t,e){for(var r=Oe(t),n=-1,i=e.length;++n=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function Lo(t){return function(){var e=this.__on;if(e){for(var r=0,n=-1,i=e.length,o;r>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):r===8?re(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):r===4?re(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=Wo.exec(t))?new D(e[1],e[2],e[3],1):(e=Ko.exec(t))?new D(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=Uo.exec(t))?re(e[1],e[2],e[3],e[4]):(e=Qo.exec(t))?re(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=Zo.exec(t))?hn(e[1],e[2]/100,e[3]/100,1):(e=Jo.exec(t))?hn(e[1],e[2]/100,e[3]/100,e[4]):an.hasOwnProperty(t)?un(an[t]):t===\"transparent\"?new D(NaN,NaN,NaN,0):null}function un(t){return new D(t>>16&255,t>>8&255,t&255,1)}function re(t,e,r,n){return n<=0&&(t=e=r=NaN),new D(t,e,r,n)}function ea(t){return t instanceof It||(t=rt(t)),t?(t=t.rgb(),new D(t.r,t.g,t.b,t.opacity)):new D}function gt(t,e,r,n){return arguments.length===1?ea(t):new D(t,e,r,n==null?1:n)}function D(t,e,r,n){this.r=+t,this.g=+e,this.b=+r,this.opacity=+n}ee(D,gt,Ne(It,{brighter(t){return t=t==null?ie:Math.pow(ie,t),new D(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?Ot:Math.pow(Ot,t),new D(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new D(ft(this.r),ft(this.g),ft(this.b),oe(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:fn,formatHex:fn,formatHex8:ra,formatRgb:cn,toString:cn}));function fn(){return`#${ut(this.r)}${ut(this.g)}${ut(this.b)}`}function ra(){return`#${ut(this.r)}${ut(this.g)}${ut(this.b)}${ut((isNaN(this.opacity)?1:this.opacity)*255)}`}function cn(){let t=oe(this.opacity);return`${t===1?\"rgb(\":\"rgba(\"}${ft(this.r)}, ${ft(this.g)}, ${ft(this.b)}${t===1?\")\":`, ${t})`}`}function oe(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function ft(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function ut(t){return t=ft(t),(t<16?\"0\":\"\")+t.toString(16)}function hn(t,e,r,n){return n<=0?t=e=r=NaN:r<=0||r>=1?t=e=NaN:e<=0&&(t=NaN),new G(t,e,r,n)}function dn(t){if(t instanceof G)return new G(t.h,t.s,t.l,t.opacity);if(t instanceof It||(t=rt(t)),!t)return new G;if(t instanceof G)return t;t=t.rgb();var e=t.r/255,r=t.g/255,n=t.b/255,i=Math.min(e,r,n),o=Math.max(e,r,n),a=NaN,s=o-i,l=(o+i)/2;return s?(e===o?a=(r-n)/s+(r0&&l<1?0:a,new G(a,s,l,t.opacity)}function mn(t,e,r,n){return arguments.length===1?dn(t):new G(t,e,r,n==null?1:n)}function G(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}ee(G,mn,Ne(It,{brighter(t){return t=t==null?ie:Math.pow(ie,t),new G(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?Ot:Math.pow(Ot,t),new G(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*e,i=2*r-n;return new D(Pe(t>=240?t-240:t+120,i,n),Pe(t,i,n),Pe(t<120?t+240:t-120,i,n),this.opacity)},clamp(){return new G(pn(this.h),ne(this.s),ne(this.l),oe(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){let t=oe(this.opacity);return`${t===1?\"hsl(\":\"hsla(\"}${pn(this.h)}, ${ne(this.s)*100}%, ${ne(this.l)*100}%${t===1?\")\":`, ${t})`}`}}));function pn(t){return t=(t||0)%360,t<0?t+360:t}function ne(t){return Math.max(0,Math.min(1,t||0))}function Pe(t,e,r){return(t<60?e+(r-e)*t/60:t<180?r:t<240?e+(r-e)*(240-t)/60:e)*255}function Be(t,e,r,n,i){var o=t*t,a=o*t;return((1-3*t+3*o-a)*e+(4-6*o+3*a)*r+(1+3*t+3*o-3*a)*n+a*i)/6}function xn(t){var e=t.length-1;return function(r){var n=r<=0?r=0:r>=1?(r=1,e-1):Math.floor(r*e),i=t[n],o=t[n+1],a=n>0?t[n-1]:2*i-o,s=n()=>t;function na(t,e){return function(r){return t+r*e}}function ia(t,e,r){return t=Math.pow(t,r),e=Math.pow(e,r)-t,r=1/r,function(n){return Math.pow(t+n*e,r)}}function yn(t){return(t=+t)==1?ae:function(e,r){return r-e?ia(e,r,t):Xe(isNaN(e)?r:e)}}function ae(t,e){var r=e-t;return r?na(t,r):Xe(isNaN(t)?e:t)}var se=(function t(e){var r=yn(e);function n(i,o){var a=r((i=gt(i)).r,(o=gt(o)).r),s=r(i.g,o.g),l=r(i.b,o.b),u=ae(i.opacity,o.opacity);return function(f){return i.r=a(f),i.g=s(f),i.b=l(f),i.opacity=u(f),i+\"\"}}return n.gamma=t,n})(1);function vn(t){return function(e){var r=e.length,n=new Array(r),i=new Array(r),o=new Array(r),a,s;for(a=0;ar&&(o=e.slice(r,o),s[a]?s[a]+=o:s[++a]=o),(n=n[0])===(i=i[0])?s[a]?s[a]+=i:s[++a]=i:(s[++a]=null,l.push({i:a,x:Y(n,i)})),r=De.lastIndex;return r180?f+=360:f-u>180&&(u+=360),d.push({i:p.push(i(p)+\"rotate(\",null,n)-2,x:Y(u,f)})):f&&p.push(i(p)+\"rotate(\"+f+n)}function s(u,f,p,d){u!==f?d.push({i:p.push(i(p)+\"skewX(\",null,n)-2,x:Y(u,f)}):f&&p.push(i(p)+\"skewX(\"+f+n)}function l(u,f,p,d,x,_){if(u!==p||f!==d){var b=x.push(i(x)+\"scale(\",null,\",\",null,\")\");_.push({i:b-4,x:Y(u,p)},{i:b-2,x:Y(f,d)})}else(p!==1||d!==1)&&x.push(i(x)+\"scale(\"+p+\",\"+d+\")\")}return function(u,f){var p=[],d=[];return u=t(u),f=t(f),o(u.translateX,u.translateY,f.translateX,f.translateY,p,d),a(u.rotate,f.rotate,p,d),s(u.skewX,f.skewX,p,d),l(u.scaleX,u.scaleY,f.scaleX,f.scaleY,p,d),u=f=null,function(x){for(var _=-1,b=d.length,A;++_=0&&t._call.call(void 0,e),t=t._next;--yt}function En(){ct=(ce=Bt.now())+he,yt=Nt=0;try{zn()}finally{yt=0,da(),ct=0}}function pa(){var t=Bt.now(),e=t-ce;e>Cn&&(he-=e,ce=t)}function da(){for(var t,e=fe,r,n=1/0;e;)e._call?(n>e._time&&(n=e._time),t=e,e=e._next):(r=e._next,e._next=null,e=t?t._next=r:fe=r);Pt=t,Ge(n)}function Ge(t){if(!yt){Nt&&(Nt=clearTimeout(Nt));var e=t-ct;e>24?(t<1/0&&(Nt=setTimeout(En,t-Bt.now()-he)),Rt&&(Rt=clearInterval(Rt))):(Rt||(ce=Bt.now(),Rt=setInterval(pa,Cn)),yt=1,An(En))}}function de(t,e,r){var n=new Xt;return e=e==null?0:+e,n.restart(i=>{n.stop(),t(i+e)},e,r),n}var ma=At(\"start\",\"end\",\"cancel\",\"interrupt\"),xa=[],On=0,$n=1,xe=2,me=3,Tn=4,ge=5,qt=6;function nt(t,e,r,n,i,o){var a=t.__transition;if(!a)t.__transition={};else if(r in a)return;ga(t,r,{name:e,index:n,group:i,on:ma,tween:xa,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:On})}function Lt(t,e){var r=R(t,e);if(r.state>On)throw new Error(\"too late; already scheduled\");return r}function P(t,e){var r=R(t,e);if(r.state>me)throw new Error(\"too late; already running\");return r}function R(t,e){var r=t.__transition;if(!r||!(r=r[e]))throw new Error(\"transition not found\");return r}function ga(t,e,r){var n=t.__transition,i;n[e]=r,r.timer=pe(o,0,r.time);function o(u){r.state=$n,r.timer.restart(a,r.delay,r.time),r.delay<=u&&a(u-r.delay)}function a(u){var f,p,d,x;if(r.state!==$n)return l();for(f in n)if(x=n[f],x.name===r.name){if(x.state===me)return de(a);x.state===Tn?(x.state=qt,x.timer.stop(),x.on.call(\"interrupt\",t,t.__data__,x.index,x.group),delete n[f]):+fxe&&n.state=0&&(e=e.slice(0,r)),!e||e===\"start\"})}function Ba(t,e,r){var n,i,o=Pa(e)?Lt:P;return function(){var a=o(this,t),s=a.on;s!==n&&(i=(n=s).copy()).on(e,r),a.on=i}}function Yn(t,e){var r=this._id;return arguments.length<2?R(this.node(),r).on.on(t):this.each(Ba(r,t,e))}function Xa(t){return function(){var e=this.parentNode;for(var r in this.__transition)if(+r!==t)return;e&&e.removeChild(this)}}function Fn(){return this.on(\"end.remove\",Xa(this._id))}function Hn(t){var e=this._name,r=this._id;typeof t!=\"function\"&&(t=lt(t));for(var n=this._groups,i=n.length,o=new Array(i),a=0;a=0))throw new Error(`invalid digits: ${t}`);if(e>15)return ni;let r=10**e;return function(n){this._+=n[0];for(let i=1,o=n.length;iht)if(!(Math.abs(p*l-u*f)>ht)||!o)this._append`L${this._x1=e},${this._y1=r}`;else{let x=n-a,_=i-s,b=l*l+u*u,A=x*x+_*_,g=Math.sqrt(b),$=Math.sqrt(d),O=o*Math.tan((Ke-Math.acos((b+d-A)/(2*g*$)))/2),M=O/$,I=O/g;Math.abs(M-1)>ht&&this._append`L${e+M*f},${r+M*p}`,this._append`A${o},${o},0,0,${+(p*x>f*_)},${this._x1=e+I*l},${this._y1=r+I*u}`}}arc(e,r,n,i,o,a){if(e=+e,r=+r,n=+n,a=!!a,n<0)throw new Error(`negative radius: ${n}`);let s=n*Math.cos(i),l=n*Math.sin(i),u=e+s,f=r+l,p=1^a,d=a?i-o:o-i;this._x1===null?this._append`M${u},${f}`:(Math.abs(this._x1-u)>ht||Math.abs(this._y1-f)>ht)&&this._append`L${u},${f}`,n&&(d<0&&(d=d%Ue+Ue),d>ts?this._append`A${n},${n},0,1,${p},${e-s},${r-l}A${n},${n},0,1,${p},${this._x1=u},${this._y1=f}`:d>ht&&this._append`A${n},${n},0,${+(d>=Ke)},${p},${this._x1=e+n*Math.cos(o)},${this._y1=r+n*Math.sin(o)}`)}rect(e,r,n,i){this._append`M${this._x0=this._x1=+e},${this._y0=this._y1=+r}h${n=+n}v${+i}h${-n}Z`}toString(){return this._}};function ii(){return new pt}ii.prototype=pt.prototype;function oi(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}var Qe=Symbol(\"implicit\");function wt(){var t=new mt,e=[],r=[],n=Qe;function i(o){let a=t.get(o);if(a===void 0){if(n!==Qe)return n;t.set(o,a=e.push(o)-1)}return r[a%r.length]}return i.domain=function(o){if(!arguments.length)return e.slice();e=[],t=new mt;for(let a of o)t.has(a)||t.set(a,e.push(a)-1);return i},i.range=function(o){return arguments.length?(r=Array.from(o),i):r.slice()},i.unknown=function(o){return arguments.length?(n=o,i):n},i.copy=function(){return wt(e,r).unknown(n)},oi.apply(i,arguments),i}function ai(t){for(var e=t.length/6|0,r=new Array(e),n=0;n=0))throw new RangeError(`invalid digits: ${r}`);e=n}return t},()=>new pt(e)}var li=Array.prototype.slice;function ui(t){return t[0]}function fi(t){return t[1]}var je=class{constructor(e,r){this._context=e,this._x=r}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(e,r){switch(e=+e,r=+r,this._point){case 0:{this._point=1,this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+e)/2,this._y0,this._x0,r,e,r):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+r)/2,e,this._y0,e,r);break}}this._x0=e,this._y0=r}};function ci(t){return new je(t,!0)}function rs(t){return t.source}function ns(t){return t.target}function hi(t){let e=rs,r=ns,n=ui,i=fi,o=null,a=null,s=si(l);function l(){let u,f=li.call(arguments),p=e.apply(this,f),d=r.apply(this,f);if(o==null&&(a=t(u=s())),a.lineStart(),f[0]=p,a.point(+n.apply(this,f),+i.apply(this,f)),f[0]=d,a.point(+n.apply(this,f),+i.apply(this,f)),a.lineEnd(),u)return a=null,u+\"\"||null}return l.source=function(u){return arguments.length?(e=u,l):e},l.target=function(u){return arguments.length?(r=u,l):r},l.x=function(u){return arguments.length?(n=typeof u==\"function\"?u:Je(+u),l):n},l.y=function(u){return arguments.length?(i=typeof u==\"function\"?u:Je(+u),l):i},l.context=function(u){return arguments.length?(u==null?o=a=null:a=t(o=u),l):o},l}function tr(){return hi(ci)}var Yt=t=>()=>t;function er(t,{sourceEvent:e,target:r,transform:n,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},transform:{value:n,enumerable:!0,configurable:!0},_:{value:i}})}function W(t,e,r){this.k=t,this.x=e,this.y=r}W.prototype={constructor:W,scale:function(t){return t===1?this:new W(this.k*t,this.x,this.y)},translate:function(t,e){return t===0&e===0?this:new W(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return\"translate(\"+this.x+\",\"+this.y+\") scale(\"+this.k+\")\"}};var dt=new W(1,0,0);tt.prototype=W.prototype;function tt(t){for(;!t.__zoom;)if(!(t=t.parentNode))return dt;return t.__zoom}function _e(t){t.stopImmediatePropagation()}function _t(t){t.preventDefault(),t.stopImmediatePropagation()}function is(t){return(!t.ctrlKey||t.type===\"wheel\")&&!t.button}function os(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t,t.hasAttribute(\"viewBox\")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]):[[0,0],[t.clientWidth,t.clientHeight]]}function pi(){return this.__zoom||dt}function as(t){return-t.deltaY*(t.deltaMode===1?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function ss(){return navigator.maxTouchPoints||\"ontouchstart\"in this}function ls(t,e,r){var n=t.invertX(e[0][0])-r[0][0],i=t.invertX(e[1][0])-r[1][0],o=t.invertY(e[0][1])-r[0][1],a=t.invertY(e[1][1])-r[1][1];return t.translate(i>n?(n+i)/2:Math.min(0,n)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}function rr(){var t=is,e=os,r=ls,n=as,i=ss,o=[0,1/0],a=[[-1/0,-1/0],[1/0,1/0]],s=250,l=Ve,u=At(\"start\",\"zoom\",\"end\"),f,p,d,x=500,_=150,b=0,A=10;function g(h){h.property(\"__zoom\",pi).on(\"wheel.zoom\",ot,{passive:!1}).on(\"mousedown.zoom\",at).on(\"dblclick.zoom\",st).filter(i).on(\"touchstart.zoom\",Ft).on(\"touchmove.zoom\",Ht).on(\"touchend.zoom touchcancel.zoom\",bt).style(\"-webkit-tap-highlight-color\",\"rgba(0,0,0,0)\")}g.transform=function(h,y,m,w){var k=h.selection?h.selection():h;k.property(\"__zoom\",pi),h!==k?I(h,y,m,w):k.interrupt().each(function(){X(this,arguments).event(w).start().zoom(null,typeof y==\"function\"?y.apply(this,arguments):y).end()})},g.scaleBy=function(h,y,m,w){g.scaleTo(h,function(){var k=this.__zoom.k,S=typeof y==\"function\"?y.apply(this,arguments):y;return k*S},m,w)},g.scaleTo=function(h,y,m,w){g.transform(h,function(){var k=e.apply(this,arguments),S=this.__zoom,E=m==null?M(k):typeof m==\"function\"?m.apply(this,arguments):m,z=S.invert(E),c=typeof y==\"function\"?y.apply(this,arguments):y;return r(O($(S,c),E,z),k,a)},m,w)},g.translateBy=function(h,y,m,w){g.transform(h,function(){return r(this.__zoom.translate(typeof y==\"function\"?y.apply(this,arguments):y,typeof m==\"function\"?m.apply(this,arguments):m),e.apply(this,arguments),a)},null,w)},g.translateTo=function(h,y,m,w,k){g.transform(h,function(){var S=e.apply(this,arguments),E=this.__zoom,z=w==null?M(S):typeof w==\"function\"?w.apply(this,arguments):w;return r(dt.translate(z[0],z[1]).scale(E.k).translate(typeof y==\"function\"?-y.apply(this,arguments):-y,typeof m==\"function\"?-m.apply(this,arguments):-m),S,a)},w,k)};function $(h,y){return y=Math.max(o[0],Math.min(o[1],y)),y===h.k?h:new W(y,h.x,h.y)}function O(h,y,m){var w=y[0]-m[0]*h.k,k=y[1]-m[1]*h.k;return w===h.x&&k===h.y?h:new W(h.k,w,k)}function M(h){return[(+h[0][0]+ +h[1][0])/2,(+h[0][1]+ +h[1][1])/2]}function I(h,y,m,w){h.on(\"start.zoom\",function(){X(this,arguments).event(w).start()}).on(\"interrupt.zoom end.zoom\",function(){X(this,arguments).event(w).end()}).tween(\"zoom\",function(){var k=this,S=arguments,E=X(k,S).event(w),z=e.apply(k,S),c=m==null?M(z):typeof m==\"function\"?m.apply(k,S):m,v=Math.max(z[1][0]-z[0][0],z[1][1]-z[0][1]),C=k.__zoom,N=typeof y==\"function\"?y.apply(k,S):y,H=l(C.invert(c).concat(v/C.k),N.invert(c).concat(v/N.k));return function(V){if(V===1)V=N;else{var U=H(V),Se=v/U[2];V=new W(Se,c[0]-U[0]*Se,c[1]-U[1]*Se)}E.zoom(null,V)}})}function X(h,y,m){return!m&&h.__zooming||new F(h,y)}function F(h,y){this.that=h,this.args=y,this.active=0,this.sourceEvent=null,this.extent=e.apply(h,y),this.taps=0}F.prototype={event:function(h){return h&&(this.sourceEvent=h),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit(\"start\")),this},zoom:function(h,y){return this.mouse&&h!==\"mouse\"&&(this.mouse[1]=y.invert(this.mouse[0])),this.touch0&&h!==\"touch\"&&(this.touch0[1]=y.invert(this.touch0[0])),this.touch1&&h!==\"touch\"&&(this.touch1[1]=y.invert(this.touch1[0])),this.that.__zoom=y,this.emit(\"zoom\"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit(\"end\")),this},emit:function(h){var y=L(this.that).datum();u.call(h,this.that,new er(h,{sourceEvent:this.sourceEvent,target:g,type:h,transform:this.that.__zoom,dispatch:u}),y)}};function ot(h,...y){if(!t.apply(this,arguments))return;var m=X(this,y).event(h),w=this.__zoom,k=Math.max(o[0],Math.min(o[1],w.k*Math.pow(2,n.apply(this,arguments)))),S=J(h);if(m.wheel)(m.mouse[0][0]!==S[0]||m.mouse[0][1]!==S[1])&&(m.mouse[1]=w.invert(m.mouse[0]=S)),clearTimeout(m.wheel);else{if(w.k===k)return;m.mouse=[S,w.invert(S)],it(this),m.start()}_t(h),m.wheel=setTimeout(E,_),m.zoom(\"mouse\",r(O($(w,k),m.mouse[0],m.mouse[1]),m.extent,a));function E(){m.wheel=null,m.end()}}function at(h,...y){if(d||!t.apply(this,arguments))return;var m=h.currentTarget,w=X(this,y,!0).event(h),k=L(h.view).on(\"mousemove.zoom\",c,!0).on(\"mouseup.zoom\",v,!0),S=J(h,m),E=h.clientX,z=h.clientY;Ie(h.view),_e(h),w.mouse=[S,this.__zoom.invert(S)],it(this),w.start();function c(C){if(_t(C),!w.moved){var N=C.clientX-E,H=C.clientY-z;w.moved=N*N+H*H>b}w.event(C).zoom(\"mouse\",r(O(w.that.__zoom,w.mouse[0]=J(C,m),w.mouse[1]),w.extent,a))}function v(C){k.on(\"mousemove.zoom mouseup.zoom\",null),Re(C.view,w.moved),_t(C),w.event(C).end()}}function st(h,...y){if(t.apply(this,arguments)){var m=this.__zoom,w=J(h.changedTouches?h.changedTouches[0]:h,this),k=m.invert(w),S=m.k*(h.shiftKey?.5:2),E=r(O($(m,S),w,k),e.apply(this,y),a);_t(h),s>0?L(this).transition().duration(s).call(I,E,w,h):L(this).call(g.transform,E,w,h)}}function Ft(h,...y){if(t.apply(this,arguments)){var m=h.touches,w=m.length,k=X(this,y,h.changedTouches.length===w).event(h),S,E,z,c;for(_e(h),E=0;En=>t+e/r**n.state.depth,gi={autoFit:!1,duration:500,embedGlobalCSS:!0,fitRatio:.95,maxInitialScale:2,scrollForPan:mi,initialExpandLevel:-1,zoom:!0,pan:!0,toggleRecursively:!1,color:t=>{var e;return us(`${((e=t.state)==null?void 0:e.path)||\"\"}`)},lineWidth:xi(),maxWidth:0,nodeMinHeight:16,paddingX:8,spacingHorizontal:80,spacingVertical:5};function yi(t){let e={},r={...t},{color:n,colorFreezeLevel:i,lineWidth:o}=r;if((n==null?void 0:n.length)===1){let a=n[0];e.color=()=>a}else if(n!=null&&n.length){let a=wt(n);e.color=s=>a(`${s.state.path}`)}if(i){let a=e.color||gi.color;e.color=s=>(s={...s,state:{...s.state,path:s.state.path.split(\".\").slice(0,i).join(\".\")}},a(s))}if(o){let a=Array.isArray(o)?o:[o,0,1];e.lineWidth=xi(...a)}return[\"duration\",\"fitRatio\",\"initialExpandLevel\",\"maxInitialScale\",\"maxWidth\",\"nodeMinHeight\",\"paddingX\",\"spacingHorizontal\",\"spacingVertical\"].forEach(a=>{let s=r[a];typeof s==\"number\"&&(e[a]=s)}),[\"zoom\",\"pan\"].forEach(a=>{let s=r[a];s!=null&&(e[a]=!!s)}),e}function fs(t){let e=0;for(let r=0;r>>0).toString(36)}function q(t){if(typeof t==\"string\"){let r=t;t=n=>n.matches(r)}let e=t;return function(){let r=Array.from(this.childNodes);return e&&(r=r.filter(n=>e(n))),r}}function cs(t){var e=0,r=t.children,n=r&&r.length;if(!n)e=1;else for(;--n>=0;)e+=r[n].value;t.value=e}function hs(){return this.eachAfter(cs)}function ps(t){var e=this,r,n=[e],i,o,a;do for(r=n.reverse(),n=[];e=r.pop();)if(t(e),i=e.children,i)for(o=0,a=i.length;o=0;--i)r.push(n[i]);return this}function ms(t){for(var e=this,r=[e],n=[],i,o,a;e=r.pop();)if(n.push(e),i=e.children,i)for(o=0,a=i.length;o=0;)r+=n[i].value;e.value=r})}function gs(t){return this.eachBefore(function(e){e.children&&e.children.sort(t)})}function ys(t){for(var e=this,r=vs(e,t),n=[e];e!==r;)e=e.parent,n.push(e);for(var i=n.length;t!==r;)n.splice(i,0,t),t=t.parent;return n}function vs(t,e){if(t===e)return t;var r=t.ancestors(),n=e.ancestors(),i=null;for(t=r.pop(),e=n.pop();t===e;)i=t,t=r.pop(),e=n.pop();return i}function ws(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e}function _s(){var t=[];return this.each(function(e){t.push(e)}),t}function bs(){var t=[];return this.eachBefore(function(e){e.children||t.push(e)}),t}function ks(){var t=this,e=[];return t.each(function(r){r!==t&&e.push({source:r.parent,target:r})}),e}function ar(t,e){var r=new be(t),n=+t.value&&(r.value=t.value),i,o=[r],a,s,l,u;for(e==null&&(e=Es);i=o.pop();)if(n&&(i.value=+i.data.value),(s=e(i.data))&&(u=s.length))for(i.children=new Array(u),l=u-1;l>=0;--l)o.push(a=i.children[l]=new be(s[l])),a.parent=i,a.depth=i.depth+1;return r.eachBefore(As)}function Ss(){return ar(this).eachBefore(Cs)}function Es(t){return t.children}function Cs(t){t.data=t.data.data}function As(t){var e=0;do t.height=e;while((t=t.parent)&&t.height<++e)}function be(t){this.data=t,this.depth=this.height=0,this.parent=null}be.prototype=ar.prototype={constructor:be,count:hs,each:ps,eachAfter:ms,eachBefore:ds,sum:xs,sort:gs,path:ys,ancestors:ws,descendants:_s,leaves:bs,links:ks,copy:Ss};var zs=\"2.1.2\",$s={version:zs},{version:Ts}=$s,Os=Object.freeze({children:t=>t.children,nodeSize:t=>t.data.size,spacing:0});function wi(t){let e=Object.assign({},Os,t);function r(s){let l=e[s];return typeof l==\"function\"?l:()=>l}function n(s){let l=a(o(),s,u=>u.children);return l.update(),l.data}function i(){let s=r(\"nodeSize\"),l=r(\"spacing\");return class vi extends ar.prototype.constructor{constructor(f){super(f)}copy(){let f=a(this.constructor,this,p=>p.children);return f.each(p=>p.data=p.data.data),f}get size(){return s(this)}spacing(f){return l(this,f)}get nodes(){return this.descendants()}get xSize(){return this.size[0]}get ySize(){return this.size[1]}get top(){return this.y}get bottom(){return this.y+this.ySize}get left(){return this.x-this.xSize/2}get right(){return this.x+this.xSize/2}get root(){let f=this.ancestors();return f[f.length-1]}get numChildren(){return this.hasChildren?this.children.length:0}get hasChildren(){return!this.noChildren}get noChildren(){return this.children===null}get firstChild(){return this.hasChildren?this.children[0]:null}get lastChild(){return this.hasChildren?this.children[this.numChildren-1]:null}get extents(){return(this.children||[]).reduce((f,p)=>vi.maxExtents(f,p.extents),this.nodeExtents)}get nodeExtents(){return{top:this.top,bottom:this.bottom,left:this.left,right:this.right}}static maxExtents(f,p){return{top:Math.min(f.top,p.top),bottom:Math.max(f.bottom,p.bottom),left:Math.min(f.left,p.left),right:Math.max(f.right,p.right)}}}}function o(){let s=i(),l=r(\"nodeSize\"),u=r(\"spacing\");return class extends s{constructor(f){super(f),Object.assign(this,{x:0,y:0,relX:0,prelim:0,shift:0,change:0,lExt:this,lExtRelX:0,lThr:null,rExt:this,rExtRelX:0,rThr:null})}get size(){return l(this.data)}spacing(f){return u(this.data,f.data)}get x(){return this.data.x}set x(f){this.data.x=f}get y(){return this.data.y}set y(f){this.data.y=f}update(){return _i(this),bi(this),this}}}function a(s,l,u){let f=(p,d)=>{let x=new s(p);Object.assign(x,{parent:d,depth:d===null?0:d.depth+1,height:0,length:1});let _=u(p)||[];return x.children=_.length===0?null:_.map(b=>f(b,x)),x.children&&Object.assign(x,x.children.reduce((b,A)=>({height:Math.max(b.height,A.height+1),length:b.length+A.length}),x)),x};return f(l,null)}return Object.assign(n,{nodeSize(s){return arguments.length?(e.nodeSize=s,n):e.nodeSize},spacing(s){return arguments.length?(e.spacing=s,n):e.spacing},children(s){return arguments.length?(e.children=s,n):e.children},hierarchy(s,l){let u=typeof l>\"u\"?e.children:l;return a(i(),s,u)},dump(s){let l=r(\"nodeSize\"),u=f=>p=>{let d=f+\" \",x=f+\" \",{x:_,y:b}=p,A=l(p),g=p.children||[],$=g.length===0?\" \":`,${d}children: [${x}${g.map(u(x)).join(x)}${d}],${f}`;return`{ size: [${A.join(\", \")}],${d}x: ${_}, y: ${b}${$}},`};return u(`\n`)(s)}}),n}wi.version=Ts;var _i=(t,e=0)=>(t.y=e,(t.children||[]).reduce((r,n)=>{let[i,o]=r;_i(n,t.y+t.ySize);let a=(i===0?n.lExt:n.rExt).bottom;i!==0&&Is(t,i,o);let s=Ls(a,i,o);return[i+1,s]},[0,null]),Ms(t),qs(t),t),bi=(t,e,r)=>{typeof e>\"u\"&&(e=-t.relX-t.prelim,r=0);let n=e+t.relX;return t.relX=n+t.prelim-r,t.prelim=0,t.x=r+t.relX,(t.children||[]).forEach(i=>bi(i,n,t.x)),t},Ms=t=>{(t.children||[]).reduce((e,r)=>{let[n,i]=e,o=n+r.shift,a=i+o+r.change;return r.relX+=a,[o,a]},[0,0])},Is=(t,e,r)=>{let n=t.children[e-1],i=t.children[e],o=n,a=n.relX,s=i,l=i.relX,u=!0;for(;o&&s;){o.bottom>r.lowY&&(r=r.next);let f=a+o.prelim-(l+s.prelim)+o.xSize/2+s.xSize/2+o.spacing(s);(f>0||f<0&&u)&&(l+=f,Rs(i,f),Ns(t,e,r.index,f)),u=!1;let p=o.bottom,d=s.bottom;p<=d&&(o=Bs(o),o&&(a+=o.relX)),p>=d&&(s=Ps(s),s&&(l+=s.relX))}!o&&s?Xs(t,e,s,l):o&&!s&&Ds(t,e,o,a)},Rs=(t,e)=>{t.relX+=e,t.lExtRelX+=e,t.rExtRelX+=e},Ns=(t,e,r,n)=>{let i=t.children[e],o=e-r;if(o>1){let a=n/o;t.children[r+1].shift+=a,i.shift-=a,i.change-=n-a}},Ps=t=>t.hasChildren?t.firstChild:t.lThr,Bs=t=>t.hasChildren?t.lastChild:t.rThr,Xs=(t,e,r,n)=>{let i=t.firstChild,o=i.lExt,a=t.children[e];o.lThr=r;let s=n-r.relX-i.lExtRelX;o.relX+=s,o.prelim-=s,i.lExt=a.lExt,i.lExtRelX=a.lExtRelX},Ds=(t,e,r,n)=>{let i=t.children[e],o=i.rExt,a=t.children[e-1];o.rThr=r;let s=n-r.relX-i.rExtRelX;o.relX+=s,o.prelim-=s,i.rExt=a.rExt,i.rExtRelX=a.rExtRelX},qs=t=>{if(t.hasChildren){let e=t.firstChild,r=t.lastChild,n=(e.prelim+e.relX-e.xSize/2+r.relX+r.prelim+r.xSize/2)/2;Object.assign(t,{prelim:n,lExt:e.lExt,lExtRelX:e.lExtRelX,rExt:r.rExt,rExtRelX:r.rExtRelX})}},Ls=(t,e,r)=>{for(;r!==null&&t>=r.lowY;)r=r.next;return{lowY:t,index:e,next:r}},Ys=\".markmap{--markmap-max-width: 9999px;--markmap-a-color: #0097e6;--markmap-a-hover-color: #00a8ff;--markmap-code-bg: #f0f0f0;--markmap-code-color: #555;--markmap-highlight-bg: #ffeaa7;--markmap-table-border: 1px solid currentColor;--markmap-font: 300 16px/20px sans-serif;--markmap-circle-open-bg: #fff;--markmap-text-color: #333;--markmap-highlight-node-bg: #ff02;font:var(--markmap-font);color:var(--markmap-text-color)}.markmap-link{fill:none}.markmap-node>circle{cursor:pointer}.markmap-foreign{display:inline-block}.markmap-foreign p{margin:0}.markmap-foreign a{color:var(--markmap-a-color)}.markmap-foreign a:hover{color:var(--markmap-a-hover-color)}.markmap-foreign code{padding:.25em;font-size:calc(1em - 2px);color:var(--markmap-code-color);background-color:var(--markmap-code-bg);border-radius:2px}.markmap-foreign pre{margin:0}.markmap-foreign pre>code{display:block}.markmap-foreign del{text-decoration:line-through}.markmap-foreign em{font-style:italic}.markmap-foreign strong{font-weight:700}.markmap-foreign mark{background:var(--markmap-highlight-bg)}.markmap-foreign table,.markmap-foreign th,.markmap-foreign td{border-collapse:collapse;border:var(--markmap-table-border)}.markmap-foreign img{display:inline-block}.markmap-foreign svg{fill:currentColor}.markmap-foreign>div{width:var(--markmap-max-width);text-align:left}.markmap-foreign>div>div{display:inline-block}.markmap-highlight rect{fill:var(--markmap-highlight-node-bg)}.markmap-dark .markmap{--markmap-code-bg: #1a1b26;--markmap-code-color: #ddd;--markmap-circle-open-bg: #444;--markmap-text-color: #eee}\";var nr=\"g.markmap-node\",Fs=\"path.markmap-link\",Hs=\"g.markmap-highlight\",ir=tr();function di(t,e){let r=Gt(t,e);return t[r]}function or(t){t.stopPropagation()}var Vs=new Vt,ke=class t{constructor(e,r){this.options={...gi},this._disposeList=[],this.handleZoom=n=>{let{transform:i}=n;this.g.attr(\"transform\",i)},this.handlePan=n=>{n.preventDefault();let i=tt(this.svg.node()),o=i.translate(-n.deltaX/i.k,-n.deltaY/i.k);this.svg.call(this.zoom.transform,o)},this.handleClick=(n,i)=>{let o=this.options.toggleRecursively;(mi?n.metaKey:n.ctrlKey)&&(o=!o),this.toggleNode(i,o)},this.ensureView=this.ensureVisible,this.svg=e.datum?e:L(e),this.styleNode=this.svg.append(\"style\"),this.zoom=rr().filter(n=>this.options.scrollForPan&&n.type===\"wheel\"?n.ctrlKey&&!n.button:(!n.ctrlKey||n.type===\"wheel\")&&!n.button).on(\"zoom\",this.handleZoom),this.setOptions(r),this.state={id:this.options.id||this.svg.attr(\"id\")||ur(),rect:{x1:0,y1:0,x2:0,y2:0}},this.g=this.svg.append(\"g\"),this.g.append(\"g\").attr(\"class\",\"markmap-highlight\"),this._observer=new ResizeObserver(cr(()=>{this.renderData()},100)),this._disposeList.push(Vs.tap(()=>{this.setData()}),()=>this._observer.disconnect())}getStyleContent(){let{style:e}=this.options,{id:r}=this.state,n=typeof e==\"function\"?e(r):\"\";return[this.options.embedGlobalCSS&&Ys,n].filter(Boolean).join(`\n`)}updateStyle(){this.svg.attr(\"class\",fr(this.svg.attr(\"class\"),\"markmap\",this.state.id));let e=this.getStyleContent();this.styleNode.text(e)}async toggleNode(e,r=!1){var n,i;let o=(n=e.payload)!=null&&n.fold?0:1;r?St(e,(a,s)=>{a.payload={...a.payload,fold:o},s()}):e.payload={...e.payload,fold:(i=e.payload)!=null&&i.fold?0:1},await this.renderData(e)}_initializeData(e){let r=0,{color:n,initialExpandLevel:i}=this.options,o=0,a=0;return St(e,(s,l,u)=>{var f,p,d,x;a+=1,s.children=(f=s.children)==null?void 0:f.map(b=>({...b})),r+=1,s.state={...s.state,depth:a,id:r,rect:{x:0,y:0,width:0,height:0},size:[0,0]},s.state.key=[(p=u==null?void 0:u.state)==null?void 0:p.id,s.state.id].filter(Boolean).join(\".\")+fs(s.content),s.state.path=[(d=u==null?void 0:u.state)==null?void 0:d.path,s.state.id].filter(Boolean).join(\".\"),n(s);let _=((x=s.payload)==null?void 0:x.fold)===2;_?o+=1:(o||i>=0&&s.state.depth>=i)&&(s.payload={...s.payload,fold:1}),l(),_&&(o-=1),a-=1}),e}_relayout(){if(!this.state.data)return;this.g.selectAll(q(nr)).selectAll(q(\"foreignObject\")).each(function(l){var u;let f=(u=this.firstChild)==null?void 0:u.firstChild,p=[f.scrollWidth,f.scrollHeight];l.state.size=p});let{lineWidth:e,paddingX:r,spacingHorizontal:n,spacingVertical:i}=this.options,o=wi({}).children(l=>{var u;if(!((u=l.payload)!=null&&u.fold))return l.children}).nodeSize(l=>{let[u,f]=l.data.state.size;return[f,u+(u?r*2:0)+n]}).spacing((l,u)=>(l.parent===u.parent?i:i*2)+e(l.data)),a=o.hierarchy(this.state.data);o(a);let s=a.descendants();s.forEach(l=>{let u=l.data;u.state.rect={x:l.y,y:l.x-l.xSize/2,width:l.ySize-n,height:l.xSize}}),this.state.rect={x1:Ct(s,l=>l.data.state.rect.x)||0,y1:Ct(s,l=>l.data.state.rect.y)||0,x2:Et(s,l=>l.data.state.rect.x+l.data.state.rect.width)||0,y2:Et(s,l=>l.data.state.rect.y+l.data.state.rect.height)||0}}setOptions(e){this.options={...this.options,...e},this.options.zoom?this.svg.call(this.zoom):this.svg.on(\".zoom\",null),this.options.pan?this.svg.on(\"wheel\",this.handlePan):this.svg.on(\"wheel\",null)}async setData(e,r){r&&this.setOptions(r),e&&(this.state.data=this._initializeData(e)),this.state.data&&(this.updateStyle(),await this.renderData())}async setHighlight(e){this.state.highlight=e||void 0,await this.renderData()}_getHighlightRect(e){let r=this.svg.node(),n=4/tt(r).k,i={...e.state.rect};return i.x-=n,i.y-=n,i.width+=2*n,i.height+=2*n,i}async renderData(e){let{paddingX:r,autoFit:n,color:i,maxWidth:o,lineWidth:a}=this.options,s=this.state.data;if(!s)return;let l={},u={},f=[];St(s,(c,v,C)=>{var N;(N=c.payload)!=null&&N.fold||v(),l[c.state.id]=c,C&&(u[c.state.id]=C.state.id),f.push(c)});let p={},d={},x=c=>{!c||p[c.state.id]||St(c,(v,C)=>{p[v.state.id]=c.state.id,C()})},_=c=>d[p[c.state.id]]||s.state.rect,b=c=>(l[p[c.state.id]]||s).state.rect;d[s.state.id]=s.state.rect,e&&x(e);let{highlight:A}=this.state;A&&!l[A.state.id]&&(A=void 0);let g=this.g.selectAll(q(Hs)).selectAll(q(\"rect\")).data(A?[this._getHighlightRect(A)]:[]).join(\"rect\").attr(\"x\",c=>c.x).attr(\"y\",c=>c.y).attr(\"width\",c=>c.width).attr(\"height\",c=>c.height),$=this.g.selectAll(q(nr)).each(c=>{d[c.state.id]=c.state.rect}).data(f,c=>c.state.key),O=$.enter().append(\"g\").attr(\"data-depth\",c=>c.state.depth).attr(\"data-path\",c=>c.state.path).each(c=>{x(l[u[c.state.id]])}),M=$.exit().each(c=>{x(l[u[c.state.id]])}),I=$.merge(O).attr(\"class\",c=>{var v;return[\"markmap-node\",((v=c.payload)==null?void 0:v.fold)&&\"markmap-fold\"].filter(Boolean).join(\" \")}),X=I.selectAll(q(\"line\")).data(c=>[c],c=>c.state.key),F=X.enter().append(\"line\").attr(\"stroke\",c=>i(c)).attr(\"stroke-width\",0),ot=X.merge(F),at=I.selectAll(q(\"circle\")).data(c=>{var v;return(v=c.children)!=null&&v.length?[c]:[]},c=>c.state.key),st=at.enter().append(\"circle\").attr(\"stroke-width\",0).attr(\"r\",0).on(\"click\",(c,v)=>this.handleClick(c,v)).on(\"mousedown\",or).merge(at).attr(\"stroke\",c=>i(c)).attr(\"fill\",c=>{var v;return(v=c.payload)!=null&&v.fold&&c.children?i(c):\"var(--markmap-circle-open-bg)\"}),Ft=this._observer,Ht=I.selectAll(q(\"foreignObject\")).data(c=>[c],c=>c.state.key),bt=Ht.enter().append(\"foreignObject\").attr(\"class\",\"markmap-foreign\").attr(\"x\",r).attr(\"y\",0).style(\"opacity\",0).on(\"mousedown\",or).on(\"dblclick\",or);bt.append(\"xhtml:div\").append(\"xhtml:div\").html(c=>c.content).attr(\"xmlns\",\"http://www.w3.org/1999/xhtml\"),bt.each(function(){var c;let v=(c=this.firstChild)==null?void 0:c.firstChild;Ft.observe(v)});let h=M.selectAll(q(\"foreignObject\"));h.each(function(){var c;let v=(c=this.firstChild)==null?void 0:c.firstChild;Ft.unobserve(v)});let y=bt.merge(Ht),m=f.flatMap(c=>{var v;return(v=c.payload)!=null&&v.fold?[]:c.children.map(C=>({source:c,target:C}))}),w=this.g.selectAll(q(Fs)).data(m,c=>c.target.state.key),k=w.exit(),S=w.enter().insert(\"path\",\"g\").attr(\"class\",\"markmap-link\").attr(\"data-depth\",c=>c.target.state.depth).attr(\"data-path\",c=>c.target.state.path).attr(\"d\",c=>{let v=_(c.target),C=[v.x+v.width,v.y+v.height];return ir({source:C,target:C})}).attr(\"stroke-width\",0).merge(w);this.svg.style(\"--markmap-max-width\",o?`${o}px`:null),await new Promise(requestAnimationFrame),this._relayout(),g=g.data(A?[this._getHighlightRect(A)]:[]).join(\"rect\"),this.transition(g).attr(\"x\",c=>c.x).attr(\"y\",c=>c.y).attr(\"width\",c=>c.width).attr(\"height\",c=>c.height),O.attr(\"transform\",c=>{let v=_(c);return`translate(${v.x+v.width-c.state.rect.width},${v.y+v.height-c.state.rect.height})`}),this.transition(M).attr(\"transform\",c=>{let v=b(c),C=v.x+v.width-c.state.rect.width,N=v.y+v.height-c.state.rect.height;return`translate(${C},${N})`}).remove(),this.transition(I).attr(\"transform\",c=>`translate(${c.state.rect.x},${c.state.rect.y})`);let E=M.selectAll(q(\"line\"));this.transition(E).attr(\"x1\",c=>c.state.rect.width).attr(\"stroke-width\",0),F.attr(\"x1\",c=>c.state.rect.width).attr(\"x2\",c=>c.state.rect.width),ot.attr(\"y1\",c=>c.state.rect.height+a(c)/2).attr(\"y2\",c=>c.state.rect.height+a(c)/2),this.transition(ot).attr(\"x1\",-1).attr(\"x2\",c=>c.state.rect.width+2).attr(\"stroke\",c=>i(c)).attr(\"stroke-width\",a);let z=M.selectAll(q(\"circle\"));this.transition(z).attr(\"r\",0).attr(\"stroke-width\",0),st.attr(\"cx\",c=>c.state.rect.width).attr(\"cy\",c=>c.state.rect.height+a(c)/2),this.transition(st).attr(\"r\",6).attr(\"stroke-width\",\"1.5\"),this.transition(h).style(\"opacity\",0),y.attr(\"width\",c=>Math.max(0,c.state.rect.width-r*2)).attr(\"height\",c=>c.state.rect.height),this.transition(y).style(\"opacity\",1),this.transition(k).attr(\"d\",c=>{let v=b(c.target),C=[v.x+v.width,v.y+v.height+a(c.target)/2];return ir({source:C,target:C})}).attr(\"stroke-width\",0).remove(),this.transition(S).attr(\"stroke\",c=>i(c.target)).attr(\"stroke-width\",c=>a(c.target)).attr(\"d\",c=>{let v=c.source,C=c.target,N=[v.state.rect.x+v.state.rect.width,v.state.rect.y+v.state.rect.height+a(v)/2],H=[C.state.rect.x,C.state.rect.y+C.state.rect.height+a(C)/2];return ir({source:N,target:H})}),n&&this.fit()}transition(e){let{duration:r}=this.options;return e.transition().duration(r)}async fit(e=this.options.maxInitialScale){let r=this.svg.node(),{width:n,height:i}=r.getBoundingClientRect(),{fitRatio:o}=this.options,{x1:a,y1:s,x2:l,y2:u}=this.state.rect,f=l-a,p=u-s,d=Math.min(n/f*o,i/p*o,e),x=dt.translate((n-f*d)/2-a*d,(i-p*d)/2-s*d).scale(d);return this.transition(this.svg).call(this.zoom.transform,x).end().catch(kt)}findElement(e){let r;return this.g.selectAll(q(nr)).each(function(n){n===e&&(r={data:n,g:this})}),r}async ensureVisible(e,r){var n;let i=(n=this.findElement(e))==null?void 0:n.data;if(!i)return;let o=this.svg.node(),a=o.getBoundingClientRect(),s=tt(o),[l,u]=[i.state.rect.x,i.state.rect.x+i.state.rect.width+2].map(g=>g*s.k+s.x),[f,p]=[i.state.rect.y,i.state.rect.y+i.state.rect.height].map(g=>g*s.k+s.y),d={left:0,right:0,top:0,bottom:0,...r},x=[d.left-l,a.width-d.right-u],_=[d.top-f,a.height-d.bottom-p],b=x[0]*x[1]>0?di(x,Math.abs)/s.k:0,A=_[0]*_[1]>0?di(_,Math.abs)/s.k:0;if(b||A){let g=s.translate(b,A);return this.transition(this.svg).call(this.zoom.transform,g).end().catch(kt)}}async centerNode(e,r){var n;let i=(n=this.findElement(e))==null?void 0:n.data;if(!i)return;let o=this.svg.node(),a=o.getBoundingClientRect(),s=tt(o),l=(i.state.rect.x+i.state.rect.width/2)*s.k+s.x,u=(i.state.rect.y+i.state.rect.height/2)*s.k+s.y,f={left:0,right:0,top:0,bottom:0,...r},p=(f.left+a.width-f.right)/2,d=(f.top+a.height-f.bottom)/2,x=(p-l)/s.k,_=(d-u)/s.k;if(x||_){let b=s.translate(x,_);return this.transition(this.svg).call(this.zoom.transform,b).end().catch(kt)}}async rescale(e){let r=this.svg.node(),{width:n,height:i}=r.getBoundingClientRect(),o=n/2,a=i/2,s=tt(r),l=s.translate((o-s.x)*(1-e)/s.k,(a-s.y)*(1-e)/s.k).scale(e);return this.transition(this.svg).call(this.zoom.transform,l).end().catch(kt)}destroy(){this.svg.on(\".zoom\",null),this.svg.html(null),this._disposeList.forEach(e=>{e()})}static create(e,r,n=null){let i=new t(e,r);return n&&i.setData(n).then(()=>{i.fit()}),i}};window.markmap={Markmap:ke,deriveOptions:yi};})();\n/*! Bundled license information:\n\nmarkmap-common/dist/index.mjs:\n (*! @gera2ld/jsx-dom v2.2.2 | ISC License *)\n*/\n"}}; +const UI_FINGERPRINT = "2d331d41223782ff26dd5e5f1150cde9e6a686dd949c4a314a54eda3887e92a9"; function b64ToBytes(b64) { const bin = atob(b64); const a = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) a[i] = bin.charCodeAt(i); return a; } export default { async fetch(request, env) {