Files
2026-08-28 09:15:20 +08:00

81 lines
682 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// arcrun-rag-ui — 用戶實例的 GUIconsole-ui/public 內嵌;由 Arcrun scripts/build-ui-worker.mjs 產生,勿手改)
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":"<!doctype html>\n<html lang=\"zh-Hant\">\n<head>\n<meta charset=\"utf-8\"/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Arcrun 駕駛艙</title>\n<script>\n // 主題預載(防閃色):預設淺色(leo 2026-07-04 明示),與 /console 共用同一 localStorage key\n document.documentElement.setAttribute('data-theme', (function () {\n try { return localStorage.getItem('arcrun_console_theme') === 'dark' ? 'dark' : 'light'; } catch (e) { return 'light'; }\n })());\n</script>\n<style>\n /* Mira Console 定稿視覺(紙感「2a」,Mira Style Guide 2026-07-04):\n 紙紋底 repeating-linear-gradient、明體標題級聯、琥珀強調、呼吸狀態球嵌單字。\n 2026-07-04 二輪:CSS custom properties 兩份色板——預設淺色(宣紙米白+墨字),深色=原定稿暖黑不動。 */\n * { box-sizing: border-box; }\n :root {\n --paper-a: #f4eddc; --paper-b: #f1e9d6;\n --ink: #2f2a20; --ink-rgb: 30,24,14;\n --amber: #8a5f1e; --amber-rgb: 138,95,30;\n --ok: #1d7a48; --ok-rgb: 29,122,72;\n --err: #b03a26; --err-rgb: 176,58,38;\n --track: rgba(30,24,14,.12);\n }\n :root[data-theme=\"dark\"] {\n --paper-a: #191410; --paper-b: #1b1611;\n --ink: #ede4d3; --ink-rgb: 237,228,211;\n --amber: #e8b45a; --amber-rgb: 232,180,90;\n --ok: #7fe0a8; --ok-rgb: 63,190,120;\n --err: #e58575; --err-rgb: 217,95,76;\n --track: rgba(255,255,255,.08);\n }\n html, body { margin: 0; background: repeating-linear-gradient(0deg,var(--paper-a) 0px,var(--paper-a) 3px,var(--paper-b) 3px,var(--paper-b) 4px); color: var(--ink);\n font-family: -apple-system, \"PingFang TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 16px; -webkit-font-smoothing: antialiased; }\n .serif { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; }\n main { max-width: 560px; margin: 0 auto; padding: 0 20px 40px; }\n .pagehead { padding: 22px 2px 14px; border-bottom: 2px solid rgba(var(--amber-rgb),.4); display: flex; justify-content: space-between; align-items: baseline; }\n .pagehead .title { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 23px; letter-spacing: .2em; }\n .pagehead .title small { font-size: 14px; letter-spacing: .3em; color: rgba(var(--ink-rgb),.5); }\n .pagehead .date { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 14px; color: rgba(var(--ink-rgb),.55); }\n .orb-row { display: flex; align-items: center; gap: 20px; padding: 26px 2px 20px; }\n .orb { width: 84px; height: 84px; border-radius: 50%; flex: none; display: grid; place-items: center; }\n .orb span { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 30px; font-weight: 600; color: rgba(10,20,14,.85); text-shadow: 0 1px 0 rgba(255,255,255,.25); }\n .orb-title { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 23px; font-weight: 600; }\n .orb-sub { margin-top: 5px; font-size: 15px; color: rgba(var(--ink-rgb),.6); line-height: 1.55; }\n @keyframes breatheGreen { 0%,100% { box-shadow: 0 0 24px 6px rgba(var(--ok-rgb),.35); } 50% { box-shadow: 0 0 42px 14px rgba(var(--ok-rgb),.55); } }\n @keyframes breatheAmber { 0%,100% { box-shadow: 0 0 24px 6px rgba(var(--amber-rgb),.35); } 50% { box-shadow: 0 0 42px 14px rgba(var(--amber-rgb),.6); } }\n @keyframes breatheRed { 0%,100% { box-shadow: 0 0 24px 6px rgba(var(--err-rgb),.4); } 50% { box-shadow: 0 0 44px 16px rgba(var(--err-rgb),.65); } }\n .bricks { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n .brick { padding: 16px; border-radius: 12px; }\n .brick.amber { background: rgba(var(--amber-rgb),.07); border: 1px solid rgba(var(--amber-rgb),.22); }\n .brick.plain { background: rgba(var(--ink-rgb),.04); border: 1px solid rgba(var(--ink-rgb),.14); }\n .brick .lbl { font-size: 13.5px; color: rgba(var(--ink-rgb),.55); margin-bottom: 6px; }\n .brick .num { font-family: ui-monospace, Menlo, monospace; font-size: 26px; color: var(--amber); }\n .brick .num small { font-size: 15px; color: rgba(var(--ink-rgb),.5); }\n .bar { margin-top: 10px; height: 6px; border-radius: 3px; background: var(--track); }\n .bar > i { display: block; height: 100%; border-radius: 3px; background: linear-gradient(90deg,#b98330,#e8b45a); transition: width .6s; }\n .wait-box { margin-top: 14px; padding: 20px; border-radius: 12px; border: 1px dashed rgba(var(--ok-rgb),.3); background: rgba(var(--ok-rgb),.05); }\n .wait-box.has { border-color: rgba(var(--amber-rgb),.45); background: rgba(var(--amber-rgb),.05); }\n .wait-head { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 16px; letter-spacing: .2em; color: rgba(var(--ink-rgb),.6); margin-bottom: 10px; text-align: center; }\n .wait-none { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 20px; color: var(--ok); letter-spacing: .08em; text-align: center; }\n .wait-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; margin-top: 8px; border-radius: 10px; background: rgba(var(--amber-rgb),.1); border: 1px solid rgba(var(--amber-rgb),.3); font-size: 16px; line-height: 1.5; }\n .wait-item .dm { color: var(--amber); font-size: 17px; flex: none; }\n .wait-meta { margin-top: 10px; text-align: center; font-size: 12.5px; color: rgba(var(--ink-rgb),.45); line-height: 1.7; }\n .wait-meta .warn { color: var(--err); }\n .subhead { display: flex; justify-content: space-between; align-items: baseline; margin: 24px 0 10px; }\n .subhead .t { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 16px; letter-spacing: .2em; color: rgba(var(--ink-rgb),.6); }\n .subhead .m { font-size: 13px; color: rgba(var(--ink-rgb),.4); }\n ul.route { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }\n ul.route li { display: flex; align-items: flex-start; gap: 12px; padding: 13px 16px; border-radius: 11px; background: rgba(var(--ink-rgb),.045); border: 1px solid transparent; font-size: 16px; line-height: 1.4; }\n ul.route li.doing { background: rgba(var(--amber-rgb),.09); border-color: rgba(var(--amber-rgb),.3); }\n ul.route li .ic { flex: none; font-size: 15px; margin-top: 2px; }\n ul.route li.done { color: rgba(var(--ink-rgb),.65); }\n ul.route li.done .ic { color: var(--ok); }\n ul.route li.doing .ic { color: var(--amber); }\n ul.route li.todo { color: rgba(var(--ink-rgb),.6); }\n ul.route li.todo .ic { color: rgba(var(--ink-rgb),.35); }\n ul.route li.blocked .ic { color: var(--err); }\n ul.route.faded li { opacity: .55; }\n .sys { margin-top: 6px; display: flex; flex-direction: column; gap: 6px; }\n .sys .row { display: flex; justify-content: space-between; align-items: baseline; padding: 10px 14px; border-radius: 10px; background: rgba(var(--ink-rgb),.04); border: 1px solid rgba(var(--ink-rgb),.12); font-size: 14.5px; }\n .sys .row .k { color: rgba(var(--ink-rgb),.6); }\n .sys .row .v { font-family: ui-monospace, Menlo, monospace; font-size: 14px; }\n .sys .ok { color: var(--ok); }\n .sys .bad { color: var(--err); }\n .sys .off { color: rgba(var(--ink-rgb),.5); }\n .muted { color: rgba(var(--ink-rgb),.45); font-size: 14px; }\n .err { color: var(--err); font-size: 14px; }\n .stamp { margin: 16px 0 8px; text-align: center; font-size: 12.5px; color: rgba(var(--ink-rgb),.35); line-height: 1.8; }\n .enter { display: block; text-align: center; font-size: 13.5px; color: rgba(var(--amber-rgb),.75); text-decoration: none; margin-top: 6px; }\n .theme-btn { flex: none; margin-left: 12px; width: 34px; height: 34px; border-radius: 50%; border: 1px solid rgba(var(--ink-rgb),.25); background: none; color: rgba(var(--ink-rgb),.65); font-size: 16px; cursor: pointer; line-height: 1; align-self: center; }\n</style>\n<script src=\"/config.js\"></script>\n<script>\n// 2026-08-01arcrun-rag#10 同族):拔掉寫死中央位址的 fallback。\n// apiBase 由 worker 動態產生的 /config.js 注入;缺它就讓它明顯壞掉,\n// **不要靜默把請求(可能含金鑰)送去中央實例**。\nwindow.ARCRUN_API_BASE = (window.ARCRUN_CONFIG && window.ARCRUN_CONFIG.apiBase) || \"\";\n</script>\n</head>\n<body>\n<main>\n <div class=\"pagehead\">\n <div class=\"title serif\">Arcrun<small> 駕駛艙</small></div>\n <div style=\"display:flex;align-items:baseline\">\n <div class=\"date serif\" id=\"date-str\"></div>\n <button class=\"theme-btn\" id=\"theme-btn\" title=\"切換深/淺色\">☾</button>\n </div>\n </div>\n <div class=\"orb-row\">\n <div class=\"orb\" id=\"orb\" style=\"background:radial-gradient(circle at 36% 30%,#8fe8b4,#3fbe78 55%,#22754a 100%)\"><span id=\"orb-char\">…</span></div>\n <div>\n <div class=\"orb-title\" id=\"orb-title\">載入中</div>\n <div class=\"orb-sub\" id=\"orb-sub\"></div>\n </div>\n </div>\n <div class=\"bricks\">\n <div class=\"brick amber\">\n <div class=\"lbl\">今日完成</div>\n <div class=\"num\"><span id=\"done-n\"></span><small> / <span id=\"total-n\"></span> 件</small></div>\n <div class=\"bar\"><i id=\"bar-fill\" style=\"width:0%\"></i></div>\n </div>\n <div class=\"brick plain\">\n <div class=\"lbl\">收件匣未處理</div>\n <div class=\"num\"><span id=\"inbox-n\"></span><small> 條</small></div>\n <div class=\"lbl\" style=\"margin:10px 0 0\">來自 Telegram</div>\n </div>\n </div>\n <div class=\"wait-box\" id=\"wait-box\">\n <div class=\"wait-head\">等你的事</div>\n <div id=\"wait-body\" class=\"wait-none\">載入中…</div>\n <div class=\"wait-meta\" id=\"wait-meta\"></div>\n </div>\n <div class=\"subhead\"><span class=\"t\">今日路線</span><span class=\"m\" id=\"route-m\"></span></div>\n <ul class=\"route\" id=\"today-list\"><li class=\"todo\"><span class=\"ic\">○</span>載入中…</li></ul>\n <div class=\"subhead\" id=\"week-head\" style=\"display:none\"><span class=\"t\">本週</span></div>\n <ul class=\"route\" id=\"week-list\"></ul>\n <div class=\"subhead\"><span class=\"t\">系統狀況</span><span class=\"m\">live 健康信號</span></div>\n <div class=\"sys\" id=\"sys-list\"><div class=\"row\"><span class=\"k\">載入中…</span></div></div>\n <div class=\"stamp\" id=\"stamp\">每 60 秒自動刷新</div>\n <a class=\"enter\" href=\"/console\">進入完整控制台 </a>\n</main>\n<script>\n(function () {\n var API_BASE = window.ARCRUN_API_BASE || '';\n // 台北時間 helperlib/taipei-time.ts 注入,與 server 判定同一套——顯示不隨看的裝置時區漂移)\n var TAIPEI_OFFSET_MS = 28800000; // UTC+8,台北無 DST\nfunction tpePad(n) { n = String(n); return n.length < 2 ? '0' + n : n; }\nfunction taipeiDayKey(ms) { return new Date(ms + TAIPEI_OFFSET_MS).toISOString().slice(0, 10); }\nfunction taipeiDateStr(ms) { return taipeiDayKey(ms); }\nfunction taipeiDateTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return taipeiDayKey(ms) + ' ' + tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()); }\nfunction taipeiTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()) + ':' + tpePad(d.getUTCSeconds()); }\nfunction taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { month: d.getUTCMonth() + 1, day: d.getUTCDate() }; }\n const $ = (id) => document.getElementById(id);\n const LIGHT = {\n green: { ch: '安', title: '系統運轉中', grad: 'radial-gradient(circle at 36% 30%,#8fe8b4,#3fbe78 55%,#22754a 100%)', anim: 'breatheGreen' },\n yellow: { ch: '趕', title: '落後趕工中', grad: 'radial-gradient(circle at 36% 30%,#f2d194,#e8b45a 55%,#8a5f1e 100%)', anim: 'breatheAmber' },\n red: { ch: '滯', title: '卡住或斷訊', grad: 'radial-gradient(circle at 36% 30%,#f0a094,#d95f4c 55%,#7e2c20 100%)', anim: 'breatheRed' }\n };\n const ICONS = { done: '✓', doing: '◐', todo: '○', blocked: '●' };\n function esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n }\n function taskLine(t) {\n const cls = ICONS[t.status] ? t.status : 'blocked';\n const ic = ICONS[t.status] || '●';\n return '<li class=\"' + cls + '\"><span class=\"ic\">' + ic + '</span><span>' + esc(t.title) + '</span></li>';\n }\n function humanAge(m) {\n if (m == null || m < 0) return '時間不明';\n if (m < 60) return m + ' 分鐘前';\n if (m < 2880) return Math.round(m / 60) + ' 小時前';\n return Math.round(m / 1440) + ' 天前';\n }\n const CNUM = ['零','一','二','三','四','五','六','七','八','九','十'];\n function cnDay(n) { return n <= 10 ? CNUM[n] : (n < 20 ? '十' + (n % 10 ? CNUM[n % 10] : '') : CNUM[Math.floor(n / 10)] + '十' + (n % 10 ? CNUM[n % 10] : '')); }\n // 頁首日期=台北日(原本用瀏覽器本地時區,換裝置會漂)\n const nowTpe = taipeiMonthDay(Date.now());\n $('date-str').textContent = CNUM[nowTpe.month] + '月' + cnDay(nowTpe.day) + '日';\n // 深/淺切換(與 /console 共用 arcrun_console_theme;預設淺色)\n function syncThemeBtn() { $('theme-btn').textContent = document.documentElement.getAttribute('data-theme') === 'dark' ? '☀' : '☾'; }\n $('theme-btn').addEventListener('click', () => {\n const next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';\n document.documentElement.setAttribute('data-theme', next);\n try { localStorage.setItem('arcrun_console_theme', next); } catch (e) { /* 私密模式忽略 */ }\n syncThemeBtn();\n });\n syncThemeBtn();\n // fetch 失敗(斷網)的裸訊息 → 友善誠實文案;60 秒定時器常駐,網路恢復自動刷回\n function friendlyErr(e) {\n const m = e && e.message ? String(e.message) : String(e);\n return /failed to fetch|load failed|networkerror|network request failed/i.test(m) ? '連線中斷' : m;\n }\n function sysRow(k, v, cls) {\n return '<div class=\"row\"><span class=\"k\">' + esc(k) + '</span><span class=\"v ' + cls + '\">' + esc(v) + '</span></div>';\n }\n async function load() {\n try {\n const res = await fetch(API_BASE + '/console/dashboard-data');\n if (!res.ok) throw new Error('HTTP ' + res.status);\n const d = await res.json();\n const cfg = LIGHT[d.light] || LIGHT.green;\n const orb = $('orb');\n orb.style.background = cfg.grad;\n orb.style.animation = cfg.anim + ' 3.4s ease-in-out infinite';\n $('orb-char').textContent = cfg.ch;\n $('orb-title').textContent = cfg.title;\n $('orb-sub').textContent = (d.last_beat\n ? d.last_beat.actor + '・' + d.last_beat.ago_minutes + ' 分鐘前' + (d.last_beat.note ? '・' + d.last_beat.note : '')\n : '尚無心跳資料') + (d.light !== 'green' && d.light_reason ? '' + d.light_reason + '' : '');\n const done = d.today_done || 0, total = d.today_total || 0;\n $('done-n').textContent = done; $('total-n').textContent = total;\n $('bar-fill').style.width = (total ? Math.round((done / total) * 100) : 0) + '%';\n $('inbox-n').textContent = d.inbox_new || 0;\n // ── 等你的事:來源 + 維護時間攤開講,stale 一定警示 ──\n const wb = $('wait-box'), body = $('wait-body'), wmeta = $('wait-meta');\n const wm = d.waiting_meta || {};\n if (d.waiting && d.waiting.length) {\n wb.classList.add('has');\n body.className = '';\n body.innerHTML = d.waiting.map((w) =>\n '<div class=\"wait-item\"><span class=\"dm\">' + (w.urgency ? esc(w.urgency) : '◆') + '</span><span>' +\n (w.id ? '<b>#' + esc(w.id) + '</b> ' : '') + esc(w.title) + '</span></div>').join('');\n } else {\n wb.classList.remove('has');\n body.className = 'wait-none';\n body.textContent = wm.source === 'none' ? '(管線未接)' : '無,你不用做任何事';\n }\n let metaTxt = '';\n if (wm.source === 'gitea_sprint') {\n metaTxt = '來源:sprint 等leo清單(' + esc((wm.sprint_files || []).join('、')) + ')・清單維護於 ' + humanAge(wm.updated_ago_minutes);\n if (wm.stale) metaTxt += '<br><span class=\"warn\">⚠ 清單超過 2 天沒維護,可能過時</span>';\n } else if (wm.source === 'kbdb_dash_wait') {\n metaTxt = '<span class=\"warn\">⚠ ' + esc(wm.note || 'dash_wait 殘資料') + '・上次寫入 ' + humanAge(wm.updated_ago_minutes) + ',可能過時</span>';\n } else {\n metaTxt = '<span class=\"warn\">管線未接:Gitea sprint 清單與 dash_wait 皆無資料</span>';\n }\n wmeta.innerHTML = metaTxt;\n // ── 今日路線:sprint 任務板優先(來源攤開講);dash_task fallback 沿舊誠實降級 ──\n const rm = d.route_meta || {};\n const today = (d.tasks || []).filter((t) => t.scope === 'today');\n const week = (d.tasks || []).filter((t) => t.scope === 'week');\n if (rm.source === 'gitea_sprint_board') {\n $('route-m').textContent = '來源 sprint 任務板・更新於 ' + humanAge(rm.updated_ago_minutes);\n $('today-list').className = 'route';\n const staleHead = rm.is_today ? '' :\n '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">⚠ 今日任務板未更新(最後 ' + humanAge(rm.updated_ago_minutes) + ')——以下是板上現況</span></li>';\n $('today-list').innerHTML = staleHead + (today.length\n ? today.map(taskLine).join('')\n : '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">任務板上沒有可解析的事項</span></li>');\n } else if (rm.is_today) {\n $('route-m').textContent = '更新於 ' + humanAge(rm.updated_ago_minutes);\n $('today-list').className = 'route';\n $('today-list').innerHTML = today.length ? today.map(taskLine).join('') : '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">今日無排定項目</span></li>';\n } else if (today.length) {\n $('route-m').textContent = '最後路線・' + humanAge(rm.updated_ago_minutes) + '寫入';\n $('today-list').className = 'route faded';\n $('today-list').innerHTML =\n '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">今日尚無路線寫入——以下是 ' + humanAge(rm.updated_ago_minutes) +\n '的殘留路線(sprint 任務板→dashboard 投影管線未接,等leo清單#15 裁決中)</span></li>' + today.map(taskLine).join('');\n } else {\n $('route-m').textContent = '';\n $('today-list').className = 'route';\n $('today-list').innerHTML = '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">無資料——dash_task 管線未接</span></li>';\n }\n $('week-head').style.display = week.length ? '' : 'none';\n $('week-list').innerHTML = week.map(taskLine).join('');\n // ── 系統狀況 + 總庫規模(全 live,讀不到就標讀不到)──\n const sys = d.system || {}, kb = d.kb || {};\n const rows = [];\n rows.push(sysRow('KBDB 基本盤', sys.kbdb_ok ? '● 正常' : '● 打不通', sys.kbdb_ok ? 'ok' : 'bad'));\n if (sys.embed) {\n rows.push(sys.embed.enabled\n ? sysRow('語意嵌入', '● 啟用(已嵌 ' + (sys.embed.embedded ?? '?') + '・待嵌 ' + (sys.embed.pending ?? '?') + '', 'ok')\n : sysRow('語意嵌入', '○ 停用(已嵌 ' + (sys.embed.embedded ?? '?') + '・待嵌 ' + (sys.embed.pending ?? '?') + '', 'bad'));\n } else {\n rows.push(sysRow('語意嵌入', '狀態讀不到', 'off'));\n }\n // Arcrun#100:「服務活著嗎」與「庫裡有幾條」拆兩列。混一列時,圖服務打不通會把\n // 「其實有 1854 條」整個吞掉,畫面看起來就像知識庫是空的。數字讀不到寫「讀不到」,不寫 0。\n var gOk = !!(sys.graph && sys.graph.ok);\n var tri = sys.graph && sys.graph.triplets != null ? sys.graph.triplets : null;\n rows.push(sysRow('知識圖譜服務', gOk ? '● 正常' : '● 打不通', gOk ? 'ok' : 'bad'));\n rows.push(sysRow('三元組(關聯)', tri == null ? '讀不到' : tri.toLocaleString() + ' 條', tri == null ? 'off' : ''));\n rows.push(sysRow('工作流', sys.workflow_total == null ? '讀不到' : sys.workflow_total + ' 條', sys.workflow_total == null ? 'off' : ''));\n // 精耕層 wiki 卡(leo 2026-07-07 裁:14-E 遺產總數 deprecated 不再顯示,只顯示真的新的;\n // 三元組/已嵌入 已各有一列)\n rows.push(sysRow('精耕層 wiki 卡', kb.wiki_card_total == null ? '讀不到' : kb.wiki_card_total + ' 張', kb.wiki_card_total == null ? 'off' : ''));\n $('sys-list').innerHTML = rows.join('');\n $('stamp').innerHTML = '每 60 秒自動刷新・上次 ' + esc(taipeiTimeStr(Date.parse(d.generated_at))) + '(台北)<br>此頁不含機敏內容,免登入';\n } catch (e) {\n $('orb-char').textContent = '';\n $('orb-title').textContent = '讀不到狀態';\n $('orb-sub').innerHTML = '<span class=\"err\">' + esc(friendlyErr(e)) + '・每 60 秒自動重試</span>';\n }\n }\n load();\n setInterval(load, 60000);\n})();\n</script>\n</body>\n</html>\n"},"/console/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"<!doctype html>\n<html lang=\"zh-Hant\">\n<head>\n<meta charset=\"utf-8\"/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Arcrun Console</title>\n<script>\n // 主題預載(防閃色):預設淺色(leo 2026-07-04 明示),選擇存 localStorage\n document.documentElement.setAttribute('data-theme', (function () {\n try { return localStorage.getItem('arcrun_console_theme') === 'dark' ? 'dark' : 'light'; } catch (e) { return 'light'; }\n })());\n</script>\n<style>\n * { box-sizing: border-box; }\n /* ── 主題色板(一套結構兩份色值):預設淺色=宣紙米白紙紋+墨色文字;深色=定稿紙感暖黑 2a 原值不動 ── */\n :root {\n --paper-a: #f4eddc; --paper-b: #f1e9d6; /* 宣紙米白紙紋 */\n --ink: #2f2a20; --ink-rgb: 30,24,14; /* 墨色文字 */\n --amber: #8a5f1e; --amber-rgb: 138,95,30; /* 琥珀沿用、調深到淺底可讀 */\n --ok: #1d7a48; --ok-rgb: 29,122,72; --ok-soft: #1d6b40;\n --err: #b03a26; --err-rgb: 176,58,38;\n --well: rgba(30,24,14,.06); --well2: rgba(30,24,14,.08);\n --bar-bg: #efe7d3; --track: rgba(30,24,14,.12); --knob: #ffffff;\n }\n :root[data-theme=\"dark\"] {\n --paper-a: #191410; --paper-b: #1b1611;\n --ink: #ede4d3; --ink-rgb: 237,228,211;\n --amber: #e8b45a; --amber-rgb: 232,180,90;\n --ok: #7fe0a8; --ok-rgb: 63,190,120; --ok-soft: #a8e8c4;\n --err: #e58575; --err-rgb: 217,95,76;\n --well: rgba(0,0,0,.25); --well2: rgba(0,0,0,.3);\n --bar-bg: #17120e; --track: rgba(255,255,255,.08); --knob: #ede4d3;\n }\n html, body { margin: 0; background: repeating-linear-gradient(0deg,var(--paper-a) 0px,var(--paper-a) 3px,var(--paper-b) 3px,var(--paper-b) 4px); color: var(--ink);\n font-family: -apple-system, \"PingFang TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 16px; -webkit-font-smoothing: antialiased; }\n input, textarea, button { font-family: inherit; }\n ::placeholder { color: rgba(var(--ink-rgb),.35); }\n input:focus, textarea:focus { outline: 2px solid rgba(var(--amber-rgb),.5); outline-offset: 1px; }\n a { color: var(--amber); }\n .serif { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; }\n .mono { font-family: ui-monospace, Menlo, monospace; }\n .muted { color: rgba(var(--ink-rgb),.55); }\n .dim { color: rgba(var(--ink-rgb),.4); }\n .err { color: var(--err); }\n .ok { color: var(--ok); }\n @keyframes breatheGreen { 0%,100% { box-shadow: 0 0 24px 6px rgba(var(--ok-rgb),.35); } 50% { box-shadow: 0 0 42px 14px rgba(var(--ok-rgb),.55); } }\n @keyframes breatheAmber { 0%,100% { box-shadow: 0 0 24px 6px rgba(var(--amber-rgb),.35); } 50% { box-shadow: 0 0 42px 14px rgba(var(--amber-rgb),.6); } }\n @keyframes breatheRed { 0%,100% { box-shadow: 0 0 24px 6px rgba(var(--err-rgb),.4); } 50% { box-shadow: 0 0 44px 16px rgba(var(--err-rgb),.65); } }\n\n /* ── auth 全屏 ── */\n .authwrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }\n .authbox { width: 100%; max-width: 400px; display: flex; flex-direction: column; gap: 24px; text-align: center; }\n .brand { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 44px; letter-spacing: .24em; color: var(--amber); }\n .brand-sub { margin-top: 10px; font-size: 15px; color: rgba(var(--ink-rgb),.55); letter-spacing: .1em; }\n\n /* ── 版面骨架 ── */\n #shell { display: none; min-height: 100vh; }\n #shell.on { display: flex; }\n #sidenav { display: none; }\n main { flex: 1; min-width: 0; padding-bottom: 96px; }\n .page { max-width: 980px; margin: 0 auto; padding: 0 20px 40px; }\n .page.narrow { max-width: 760px; }\n .page.wide { max-width: 1080px; }\n .pagehead { padding: 22px 2px 14px; border-bottom: 2px solid rgba(var(--amber-rgb),.4); display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }\n .pagehead .t { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 23px; letter-spacing: .2em; white-space: nowrap; }\n .pagehead .m { font-size: 13.5px; color: rgba(var(--ink-rgb),.45); }\n #tabbar { position: fixed; left: 0; right: 0; bottom: 0; background: var(--bar-bg); border-top: 2px solid rgba(var(--amber-rgb),.4); display: none; z-index: 50; padding-bottom: env(safe-area-inset-bottom); }\n #tabbar.on { display: flex; }\n #tabbar .tab { flex: 1; text-align: center; padding: 15px 0 13px; font-size: 14.5px; cursor: pointer; min-height: 44px; color: rgba(var(--ink-rgb),.5); border-top: 2px solid transparent; margin-top: -2px; }\n #tabbar .tab.on { color: var(--amber); font-weight: 600; border-top-color: var(--amber); }\n @media (min-width: 1024px) {\n #tabbar.on { display: none; }\n main { padding-bottom: 20px; }\n #sidenav { display: flex; width: 216px; flex: none; position: sticky; top: 0; height: 100vh; border-right: 2px solid rgba(var(--amber-rgb),.35); padding: 28px 0; flex-direction: column; gap: 4px; }\n #sidenav .logo { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 26px; letter-spacing: .22em; color: var(--amber); padding: 0 24px 20px; }\n #sidenav .nav { padding: 13px 24px; font-size: 16px; cursor: pointer; letter-spacing: .08em; color: rgba(var(--ink-rgb),.6); border-right: 2px solid transparent; }\n #sidenav .nav.on { color: var(--amber); background: rgba(var(--amber-rgb),.08); border-right-color: var(--amber); font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-weight: 600; }\n #sidenav .foot { margin-top: auto; padding: 0 24px; font-size: 12.5px; color: rgba(var(--ink-rgb),.35); line-height: 1.7; }\n }\n .view { display: none; }\n .view.on { display: block; }\n /* 登入/首次設定=flex 置中(.view.on 的 display:block 會蓋掉 .authwrap 的 flex → 內容靠左,leo 實測回饋修正) */\n .authwrap.view.on { display: flex; }\n\n /* ── 元件 ── */\n .btn { padding: 13px; font-size: 16px; font-weight: 600; border-radius: 10px; border: none; background: linear-gradient(90deg,#b98330,#e8b45a); color: #241804; cursor: pointer; }\n .btn:disabled { opacity: .45; cursor: default; }\n .btn2 { padding: 11px 16px; font-size: 15px; border-radius: 10px; border: 1px solid rgba(var(--amber-rgb),.6); background: none; color: var(--amber); cursor: pointer; }\n .btn3 { padding: 11px 16px; font-size: 15px; border-radius: 10px; border: 1px solid rgba(var(--ink-rgb),.25); background: none; color: rgba(var(--ink-rgb),.7); cursor: pointer; }\n .btn-danger { padding: 12px 18px; font-size: 15px; border-radius: 10px; border: 1px solid rgba(var(--err-rgb),.5); background: none; color: var(--err); cursor: pointer; }\n .btn-danger:disabled { opacity: .4; cursor: default; }\n .txt { width: 100%; padding: 13px 15px; font-size: 16px; border-radius: 10px; border: 1px solid rgba(var(--ink-rgb),.2); background: rgba(var(--ink-rgb),.05); color: var(--ink); }\n textarea.txt { resize: vertical; font-family: ui-monospace, Menlo, monospace; font-size: 14.5px; }\n .panel { padding: 18px; border-radius: 13px; background: rgba(var(--ink-rgb),.04); border: 1px solid rgba(var(--ink-rgb),.12); }\n .card-item { border-radius: 13px; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.1); }\n .card-item.hl { border-color: rgba(var(--amber-rgb),.3); }\n .tag { display: inline-block; padding: 3px 9px; border-radius: 5px; background: rgba(var(--amber-rgb),.12); color: var(--amber); font-size: 12.5px; }\n .tag.green { background: rgba(var(--ok-rgb),.12); color: var(--ok); }\n .chip { flex: none; padding: 9px 16px; font-size: 14.5px; border-radius: 999px; cursor: pointer; border: 1px solid rgba(var(--ink-rgb),.18); background: none; color: rgba(var(--ink-rgb),.6); }\n .chip.on { border-color: rgba(var(--amber-rgb),.6); background: rgba(var(--amber-rgb),.14); color: var(--amber); font-weight: 600; }\n .hscroll { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; }\n .subhead { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 16px; letter-spacing: .2em; color: rgba(var(--ink-rgb),.6); }\n .honest { padding: 22px; border-radius: 13px; border: 1px dashed rgba(var(--amber-rgb),.4); background: rgba(var(--amber-rgb),.06); text-align: center; }\n .honest .h { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 19px; color: var(--amber); margin-bottom: 8px; }\n .honest .b { font-size: 15px; color: rgba(var(--ink-rgb),.6); line-height: 1.7; }\n #toast { position: fixed; left: 50%; bottom: 110px; transform: translateX(-50%); z-index: 99; padding: 13px 22px; border-radius: 11px; background: #e8b45a; color: #241804; font-size: 15.5px; font-weight: 600; box-shadow: 0 6px 24px rgba(0,0,0,.5); white-space: nowrap; display: none; }\n\n /* ── 駕駛艙 ── */\n .orb-row { display: flex; align-items: center; gap: 20px; padding: 26px 2px 20px; }\n .orb { width: 84px; height: 84px; border-radius: 50%; flex: none; display: grid; place-items: center; }\n .orb span { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 30px; font-weight: 600; color: rgba(10,20,14,.85); text-shadow: 0 1px 0 rgba(255,255,255,.25); }\n .orb-title { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 23px; font-weight: 600; }\n .orb-sub { margin-top: 5px; font-size: 15px; color: rgba(var(--ink-rgb),.6); line-height: 1.55; }\n .cockgrid { display: grid; grid-template-columns: 1fr; gap: 0 40px; }\n @media (min-width: 1024px) { .cockgrid { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); } }\n .bricks { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n .brick { padding: 16px; border-radius: 12px; }\n .brick.amber { background: rgba(var(--amber-rgb),.07); border: 1px solid rgba(var(--amber-rgb),.22); }\n .brick.plain { background: rgba(var(--ink-rgb),.04); border: 1px solid rgba(var(--ink-rgb),.14); cursor: pointer; }\n .brick .lbl { font-size: 13.5px; color: rgba(var(--ink-rgb),.55); margin-bottom: 6px; }\n .brick .num { font-family: ui-monospace, Menlo, monospace; font-size: 26px; color: var(--amber); }\n .brick .num small { font-size: 15px; color: rgba(var(--ink-rgb),.5); }\n .bar { margin-top: 10px; height: 6px; border-radius: 3px; background: var(--track); }\n .bar > i { display: block; height: 100%; border-radius: 3px; background: linear-gradient(90deg,#b98330,#e8b45a); transition: width .6s; }\n .wait-box { margin-top: 14px; padding: 20px; border-radius: 12px; border: 1px dashed rgba(var(--ok-rgb),.3); background: rgba(var(--ok-rgb),.05); }\n .wait-box.has { border-color: rgba(var(--amber-rgb),.45); background: rgba(var(--amber-rgb),.05); }\n .wait-head { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 16px; letter-spacing: .2em; color: rgba(var(--ink-rgb),.6); margin-bottom: 10px; text-align: center; }\n .wait-none { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 20px; color: var(--ok); letter-spacing: .08em; text-align: center; }\n .wait-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; margin-top: 8px; border-radius: 10px; background: rgba(var(--amber-rgb),.1); border: 1px solid rgba(var(--amber-rgb),.3); font-size: 16px; line-height: 1.5; }\n .wait-item .dm { color: var(--amber); font-size: 17px; flex: none; }\n ul.route { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }\n ul.route li { display: flex; align-items: flex-start; gap: 12px; padding: 13px 16px; border-radius: 11px; background: rgba(var(--ink-rgb),.045); border: 1px solid transparent; font-size: 16px; line-height: 1.4; }\n ul.route li.doing { background: rgba(var(--amber-rgb),.09); border-color: rgba(var(--amber-rgb),.3); }\n ul.route li .ic { flex: none; font-size: 15px; margin-top: 2px; }\n ul.route li.done { color: rgba(var(--ink-rgb),.65); } ul.route li.done .ic { color: var(--ok); }\n ul.route li.doing .ic { color: var(--amber); }\n ul.route li.todo { color: rgba(var(--ink-rgb),.6); } ul.route li.todo .ic { color: rgba(var(--ink-rgb),.35); }\n ul.route li.blocked .ic { color: var(--err); }\n\n /* ── 搜尋 ── */\n .searchrow { display: flex; gap: 10px; align-items: stretch; }\n .searchrow input { flex: 1; min-width: 0; padding: 16px 18px; font-size: 18px; border-radius: 13px; border: 1.5px solid rgba(var(--amber-rgb),.35); background: rgba(var(--ink-rgb),.05); color: var(--ink); }\n .sembtn { flex: none; padding: 0 20px; font-size: 16px; border-radius: 13px; cursor: pointer; letter-spacing: .1em; border: 1.5px solid rgba(var(--ink-rgb),.2); background: none; color: rgba(var(--ink-rgb),.55); }\n .sembtn.on { border-color: var(--amber); background: rgba(var(--amber-rgb),.15); color: var(--amber); font-weight: 600; }\n .kgrid { display: grid; grid-template-columns: 1fr; gap: 12px; padding-bottom: 8px; }\n @media (min-width: 700px) { .kgrid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); } }\n .kcard { padding: 18px; border-radius: 13px; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.1); cursor: pointer; display: flex; flex-direction: column; gap: 9px; }\n .kcard:hover { border-color: rgba(var(--amber-rgb),.45); background: rgba(var(--amber-rgb),.06); }\n .kcard .kt { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 18.5px; font-weight: 600; line-height: 1.45; word-break: break-word; }\n .kcard .ks { font-size: 15px; color: rgba(var(--ink-rgb),.65); line-height: 1.65; word-break: break-word; }\n .kcard .km { display: flex; gap: 10px; align-items: center; margin-top: auto; font-size: 13px; flex-wrap: wrap; }\n\n /* ── 卡片詳頁 ── */\n .cardwrap { display: flex; flex-wrap: wrap; gap: 32px; padding: 24px 0 40px; }\n .cardmain { flex: 1 1 540px; min-width: 0; }\n .cardside { flex: 1 1 360px; min-width: 0; }\n .cardtitle { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 28px; font-weight: 600; line-height: 1.4; word-break: break-word; }\n .md { margin-top: 18px; }\n .md p { font-size: 16.5px; line-height: 1.85; color: rgba(var(--ink-rgb),.82); margin: 8px 0; word-break: break-word; }\n .md h1, .md h2, .md h3, .md h4 { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-weight: 600; color: var(--amber); margin: 18px 0 6px; line-height: 1.4; }\n .md h1 { font-size: 22px; } .md h2 { font-size: 20px; } .md h3 { font-size: 18px; } .md h4 { font-size: 17px; }\n .md ul, .md ol { margin: 8px 0; padding-left: 24px; }\n .md li { font-size: 16.5px; line-height: 1.8; color: rgba(var(--ink-rgb),.82); margin: 4px 0; word-break: break-word; }\n .md blockquote { margin: 10px 0; padding: 12px 18px; border-left: 3px solid rgba(var(--amber-rgb),.5); font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 17px; line-height: 1.8; color: rgba(var(--ink-rgb),.7); }\n .md code { font-family: ui-monospace, Menlo, monospace; font-size: 14.5px; background: var(--well2); border-radius: 4px; padding: 1px 6px; word-break: break-all; }\n .md pre { background: var(--well2); border: 1px solid rgba(var(--ink-rgb),.1); border-radius: 10px; padding: 14px; overflow-x: auto; }\n .md pre code { background: none; padding: 0; }\n .srcline { margin-top: 24px; display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 11px; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.12); }\n .graphbox { position: relative; height: 340px; border-radius: 14px; background: var(--well); border: 1px solid rgba(var(--ink-rgb),.1); overflow: hidden; }\n .gnode { position: absolute; transform: translate(-50%,-50%); max-width: 96px; padding: 8px 11px; border-radius: 999px; text-align: center; font-size: 12.5px; line-height: 1.3; cursor: pointer; word-break: break-word; background: rgba(var(--ok-rgb),.1); border: 1px solid rgba(var(--ok-rgb),.35); color: var(--ok-soft); }\n .gcenter { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 104px; height: 104px; border-radius: 50%; background: radial-gradient(circle at 36% 30%,rgba(242,209,148,.95),rgba(232,180,90,.9) 55%,rgba(138,95,30,.95)); display: grid; place-items: center; text-align: center; padding: 8px; box-shadow: 0 0 30px rgba(var(--amber-rgb),.3); }\n .gcenter span { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 13.5px; font-weight: 600; line-height: 1.35; color: #241804; word-break: break-word; }\n .nbrow { display: flex; align-items: center; gap: 11px; padding: 12px 14px; border-radius: 10px; cursor: pointer; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.1); font-size: 15.5px; }\n\n /* ── 工作流 / 憑證 / 分流台 清單 ── */\n .listcol { display: flex; flex-direction: column; gap: 10px; padding: 16px 0 8px; }\n .rowhead { display: flex; align-items: center; gap: 12px; padding: 16px 18px; cursor: pointer; }\n .rowbody { padding: 0 18px 18px; border-top: 1px solid rgba(var(--ink-rgb),.1); }\n .pinbtn { flex: none; width: 34px; height: 34px; border-radius: 8px; border: none; cursor: pointer; font-size: 17px; background: rgba(var(--ink-rgb),.06); color: rgba(var(--ink-rgb),.4); }\n .pinbtn.on { background: rgba(var(--amber-rgb),.15); color: var(--amber); }\n .urlbox { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-radius: 10px; background: var(--well); border: 1px solid rgba(var(--ink-rgb),.1); min-width: 0; }\n .urlbox .u { font-family: ui-monospace, Menlo, monospace; font-size: 13.5px; color: var(--amber); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }\n .avatar { width: 38px; height: 38px; border-radius: 10px; background: rgba(var(--amber-rgb),.1); border: 1px solid rgba(var(--amber-rgb),.25); display: grid; place-items: center; font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 17px; color: var(--amber); flex: none; }\n .maskline { display: flex; align-items: center; gap: 10px; padding: 14px; border-radius: 10px; background: var(--well); border: 1px solid rgba(var(--ink-rgb),.1); }\n /* 分流台(Arcrun#9):三欄 80/15/5 手機直排;欄色點 ai=綠 collab=琥珀 leo=紅 */\n .tri-sec { margin-top: 24px; }\n .tri-sechead { display: flex; align-items: baseline; gap: 9px; }\n .tri-sechead .dot { flex: none; width: 9px; height: 9px; border-radius: 50%; align-self: center; }\n .tri-sec.ai .dot { background: var(--ok); } .tri-sec.collab .dot { background: var(--amber); } .tri-sec.leo .dot { background: var(--err); }\n .tri-sechead .nm { font-family: 'Songti TC','LiSong Pro',PMingLiU,serif; font-size: 17px; letter-spacing: .14em; }\n .tri-sechead .pc { font-size: 12.5px; color: rgba(var(--ink-rgb),.4); }\n .tri-sechead .ct { margin-left: auto; font-family: ui-monospace, Menlo, monospace; font-size: 14px; color: rgba(var(--ink-rgb),.55); }\n .tri-item { padding: 14px 16px; border-radius: 12px; margin-top: 9px; }\n .tri-item.new { background: rgba(var(--amber-rgb),.07); border: 1px solid rgba(var(--amber-rgb),.28); }\n .tri-sec.leo .tri-item.new { background: rgba(var(--err-rgb),.06); border-color: rgba(var(--err-rgb),.3); }\n .tri-item.done { background: rgba(var(--ink-rgb),.04); border: 1px solid rgba(var(--ink-rgb),.1); opacity: .7; }\n .tri-item.done .tx { text-decoration: line-through; color: rgba(var(--ink-rgb),.6); }\n .tri-item .tx { font-size: 16.5px; line-height: 1.6; word-break: break-word; }\n .tri-item .mt { margin-top: 9px; display: flex; gap: 7px; align-items: center; flex-wrap: wrap; font-size: 12.5px; color: rgba(var(--ink-rgb),.45); }\n .tri-item .row { display: flex; gap: 12px; align-items: flex-start; }\n .tri-item .row .tx { flex: 1; min-width: 0; }\n /* 勾掉鈕:≥44px 圓形(手機好按),綠框空心=待勾;按下去由後端回寫才算數(誠實,不先假裝成功) */\n .tri-check { flex: none; width: 44px; height: 44px; border-radius: 50%; cursor: pointer; font-size: 19px; line-height: 1;\n border: 1.5px solid rgba(var(--ok-rgb),.55); background: rgba(var(--ok-rgb),.06); color: var(--ok); }\n .tri-check:active { background: rgba(var(--ok-rgb),.22); }\n .tri-check:disabled { opacity: .45; cursor: default; }\n /* 還原鈕(誤勾救濟,只出現在「顯示已完成」清單):小顆但仍 44px 高好按 */\n .tri-restore { flex: none; min-height: 32px; padding: 5px 14px; border-radius: 999px; cursor: pointer; font-size: 13px;\n border: 1px solid rgba(var(--amber-rgb),.5); background: none; color: var(--amber); }\n .tri-restore:disabled { opacity: .45; cursor: default; }\n .tag.red { background: rgba(var(--err-rgb),.12); color: var(--err); }\n .tag.dim { background: rgba(var(--ink-rgb),.08); color: rgba(var(--ink-rgb),.55); }\n\n /* ── 設定 ── */\n .setcol { display: flex; flex-direction: column; gap: 14px; padding: 20px 0 40px; }\n .switch { flex: none; margin-left: auto; width: 56px; height: 32px; border-radius: 999px; padding: 3px; background: rgba(var(--ink-rgb),.15); transition: background .25s; }\n .switch.on { background: rgba(var(--ok-rgb),.5); }\n .switch i { display: block; width: 26px; height: 26px; border-radius: 50%; background: var(--knob); box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: transform .25s; }\n .switch.on i { transform: translateX(24px); }\n .kvline { display: flex; justify-content: space-between; gap: 12px; font-size: 15px; margin: 5px 0; }\n</style>\n<script src=\"/config.js\"></script>\n<script>\n// 2026-08-01arcrun-rag#10 同族):拔掉寫死中央位址的 fallback。\n// apiBase 由 worker 動態產生的 /config.js 注入;缺它就讓它明顯壞掉,\n// **不要靜默把請求(可能含金鑰)送去中央實例**。\nwindow.ARCRUN_API_BASE = (window.ARCRUN_CONFIG && window.ARCRUN_CONFIG.apiBase) || \"\";\n</script>\n</head>\n<body>\n\n<!-- 登入 -->\n<div class=\"authwrap view\" id=\"v-login\">\n <div class=\"authbox\">\n <div>\n <div class=\"brand\">Arcrun</div>\n <div class=\"brand-sub\">私人系統・僅供擁有者進入</div>\n </div>\n <div style=\"display:flex;flex-direction:column;gap:12px;text-align:left\">\n <input type=\"email\" id=\"login-email\" class=\"txt\" placeholder=\"Email\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <input type=\"password\" id=\"login-password\" class=\"txt\" placeholder=\"密碼\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <button class=\"btn\" id=\"login-submit\" style=\"margin-top:6px;padding:15px;font-size:17px;letter-spacing:.2em\">進入</button>\n <div id=\"login-status\" class=\"err\" style=\"font-size:14px;min-height:1.2em\"></div>\n </div>\n <div style=\"font-size:13.5px;color:rgba(var(--ink-rgb),.4);line-height:1.7\">這是一個人的智慧總部。<br>若你不是擁有者,這裡沒有你要找的東西。</div>\n \n <button class=\"btn3 themelabel\" data-themetoggle style=\"align-self:center;padding:8px 16px;font-size:13.5px;border-radius:999px\">☾ 切深色</button>\n </div>\n</div>\n\n<!-- 首次設定 -->\n<div class=\"authwrap view\" id=\"v-setup\">\n <div class=\"authbox\" style=\"max-width:440px;text-align:left\">\n <div style=\"text-align:center\">\n <div class=\"brand\" style=\"font-size:38px\">Arcrun</div>\n <div class=\"serif\" style=\"margin-top:12px;font-size:21px\">首次設定</div>\n <div style=\"margin-top:8px;font-size:15px;color:rgba(var(--ink-rgb),.55);line-height:1.7\">系統偵測到尚未設定擁有者帳號。<br>設定一組 Email 與密碼,之後只有你能進入。</div>\n </div>\n <div style=\"display:flex;flex-direction:column;gap:12px\">\n <input type=\"email\" id=\"setup-email\" class=\"txt\" placeholder=\"你的 Email\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <input type=\"password\" id=\"setup-password\" class=\"txt\" placeholder=\"設定密碼(至少 8 碼)\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <input type=\"password\" id=\"setup-password2\" class=\"txt\" placeholder=\"再輸入一次密碼\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <button class=\"btn\" id=\"setup-submit\" style=\"margin-top:6px;padding:15px;font-size:17px;letter-spacing:.12em\">完成設定並進入</button>\n <div id=\"setup-status\" class=\"err\" style=\"font-size:14px;min-height:1.2em\"></div>\n </div>\n <button class=\"btn3 themelabel\" data-themetoggle style=\"align-self:center;padding:8px 16px;font-size:13.5px;border-radius:999px\">☾ 切深色</button>\n </div>\n</div>\n\n<!-- 主應用 -->\n<div id=\"shell\">\n <div id=\"sidenav\">\n <div class=\"logo\">Arcrun</div>\n \n <div class=\"nav\" data-nav=\"search\">總庫搜尋</div>\n <div class=\"nav\" data-nav=\"workflows\">工作流</div>\n \n <div class=\"nav\" data-nav=\"settings\">設定</div>\n <div class=\"nav themelabel\" data-themetoggle style=\"margin-top:8px;font-size:14.5px\">☾ 切深色</div>\n <div class=\"foot\" id=\"side-foot\"></div>\n </div>\n <main>\n\n <!-- 駕駛艙 -->\n <div class=\"view page\" id=\"v-cockpit\">\n <div class=\"pagehead\"><span class=\"t\">Arcrun<span style=\"font-size:14px;letter-spacing:.3em;color:rgba(var(--ink-rgb),.5)\"> 控制台</span></span><span class=\"m serif\" id=\"ck-date\"></span></div>\n <div class=\"cockgrid\">\n <div>\n <div class=\"orb-row\">\n <div class=\"orb\" id=\"ck-orb\" style=\"background:radial-gradient(circle at 36% 30%,#8fe8b4,#3fbe78 55%,#22754a 100%)\"><span id=\"ck-char\">…</span></div>\n <div>\n <div class=\"orb-title\" id=\"ck-title\">載入中</div>\n <div class=\"orb-sub\" id=\"ck-sub\"></div>\n </div>\n </div>\n <div class=\"bricks\">\n <div class=\"brick amber\">\n <div class=\"lbl\">今日完成</div>\n <div class=\"num\"><span id=\"ck-done\"></span><small> / <span id=\"ck-total\"></span> 件</small></div>\n <div class=\"bar\"><i id=\"ck-bar\" style=\"width:0%\"></i></div>\n </div>\n <div class=\"brick plain\" data-nav=\"inbox\">\n <div class=\"lbl\">收件匣未處理</div>\n <div class=\"num\"><span id=\"ck-inbox\"></span><small> 條</small></div>\n <div class=\"lbl\" style=\"margin:10px 0 0\">來自 Telegram</div>\n </div>\n </div>\n <div class=\"wait-box\" id=\"ck-waitbox\">\n <div class=\"wait-head\">等你的事</div>\n <div id=\"ck-wait\" class=\"wait-none\">載入中…</div>\n <div id=\"ck-waitmeta\" style=\"margin-top:10px;text-align:center;font-size:12.5px;color:rgba(var(--ink-rgb),.45);line-height:1.7\"></div>\n </div>\n </div>\n <div>\n <div style=\"display:flex;justify-content:space-between;align-items:baseline;margin:24px 0 10px\">\n <span class=\"subhead\">今日路線</span><span id=\"ck-route-src\" style=\"font-size:13px;color:rgba(var(--ink-rgb),.4)\">狀態即時同步</span>\n </div>\n <ul class=\"route\" id=\"ck-today\"><li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">載入中…</span></li></ul>\n <div class=\"subhead\" id=\"ck-weekhead\" style=\"display:none;margin:18px 0 10px\">本週</div>\n <ul class=\"route\" id=\"ck-week\"></ul>\n <div style=\"margin:16px 0 8px;text-align:center;font-size:12.5px;color:rgba(var(--ink-rgb),.35);line-height:1.8\" id=\"ck-stamp\">每 60 秒自動刷新</div>\n </div>\n </div>\n </div>\n\n <!-- 總庫搜尋(library-map SDD M5/R4:搜尋框上方=藏書地圖——先看全館有哪些庫,點庫卡片帶 library filter 進庫搜尋) -->\n <div class=\"view page wide\" id=\"v-search\">\n <div class=\"pagehead\"><span class=\"t\">總庫搜尋</span><span class=\"m mono\" id=\"se-scale\"></span></div>\n <div style=\"padding-top:20px\">\n <div id=\"lm-section\" style=\"margin-bottom:24px\">\n <div style=\"display:flex;justify-content:space-between;align-items:baseline;margin-bottom:10px\">\n <span class=\"subhead\">藏書地圖</span><span id=\"lm-meta\" style=\"font-size:13px;color:rgba(var(--ink-rgb),.4)\"></span>\n </div>\n <div class=\"kgrid\" id=\"lm-cards\"><div class=\"muted\" style=\"font-size:14px\">地圖載入中…</div></div>\n <div id=\"lm-bridges\"></div>\n </div>\n <div class=\"searchrow\">\n <input id=\"se-q\" placeholder=\"搜尋知識…\" autocomplete=\"off\">\n <button class=\"sembtn\" id=\"se-sem\">語意</button>\n </div>\n <div id=\"se-libbar\"></div>\n <div class=\"hscroll\" id=\"se-chips\" style=\"margin-top:12px\"></div>\n <div id=\"se-banner\"></div>\n <div id=\"se-count\" style=\"margin:20px 0 12px;font-size:14px;color:rgba(var(--ink-rgb),.5)\"></div>\n <div class=\"kgrid\" id=\"se-results\"></div>\n </div>\n </div>\n\n <!-- 卡片詳頁 -->\n <div class=\"view page wide\" id=\"v-card\">\n <div class=\"pagehead\" style=\"justify-content:flex-start;gap:14px\">\n <button class=\"btn2\" style=\"border:none;padding:0;font-size:16px\" data-nav=\"search\"> 搜尋</button>\n <span class=\"serif\" style=\"font-size:17px;letter-spacing:.14em;color:rgba(var(--ink-rgb),.55)\">知識卡片</span>\n </div>\n <div class=\"cardwrap\">\n <div class=\"cardmain\" id=\"cd-main\"><div class=\"muted\" style=\"padding:24px 0\">載入中…</div></div>\n <div class=\"cardside\">\n <div class=\"subhead\" style=\"margin:6px 0 12px;display:block\">關聯視圖</div>\n <div id=\"cd-graph\"></div>\n <div id=\"cd-neighbors\" style=\"margin-top:16px;display:flex;flex-direction:column;gap:7px\"></div>\n </div>\n </div>\n </div>\n\n <!-- 工作流 -->\n <div class=\"view page narrow\" id=\"v-workflows\">\n <div class=\"pagehead\"><span class=\"t\">工作流</span><span class=\"m\" id=\"wf-count\"></span></div>\n <input id=\"wf-q\" class=\"txt\" placeholder=\"搜尋工作流…\" style=\"margin-top:18px\" autocomplete=\"off\">\n <div class=\"listcol\" id=\"wf-list\"><div class=\"muted\">載入中…</div></div>\n <div style=\"margin-top:26px\">\n <div class=\"subhead\" style=\"display:block;margin-bottom:10px\">零件與 Recipes(框架資源)</div>\n <div class=\"searchrow\">\n <input id=\"cr-q\" class=\"txt\" placeholder=\"如「telegram send」「gsheets append」\" style=\"font-size:15px;padding:12px 14px;border-radius:11px;border-width:1px\">\n <button class=\"btn2\" id=\"cr-go\" style=\"flex:none\">搜</button>\n </div>\n <div id=\"cr-out\" style=\"margin-top:10px\"></div>\n </div>\n </div>\n\n <!-- 憑證管理 -->\n <div class=\"view page narrow\" id=\"v-creds\">\n <div class=\"pagehead\"><span class=\"t\">憑證管理</span><button class=\"btn2\" id=\"cred-addbtn\" style=\"padding:9px 16px;font-size:14.5px\"> 新增憑證</button></div>\n <div style=\"margin-top:16px;padding:13px 16px;border-radius:11px;background:rgba(var(--ok-rgb),.05);border:1px solid rgba(var(--ok-rgb),.2);font-size:14px;line-height:1.7;color:rgba(var(--ink-rgb),.65)\">\n <span class=\"ok\">🛡 保險箱原則</span>:系統只保存目錄與加密後的值,<b style=\"color:var(--ink)\">任何頁面都看不到密文內容</b>。只能整筆替換或刪除。\n </div>\n <div id=\"cred-form\" style=\"display:none;margin-top:14px\" class=\"panel\">\n <div class=\"serif\" style=\"font-size:17px;color:var(--amber);margin-bottom:10px\">新增憑證</div>\n <div style=\"display:flex;flex-direction:column;gap:10px\">\n <input id=\"cred-name\" class=\"txt\" placeholder=\"名稱(英數與底線,例:openai_main_key\">\n <input id=\"cred-service\" class=\"txt\" placeholder=\"服務(例:OpenAI\">\n <textarea id=\"cred-secret\" class=\"txt\" placeholder=\"密文值(保存後不再顯示)\" rows=\"2\"></textarea>\n <button class=\"btn\" id=\"cred-save\">加密保存</button>\n <div id=\"cred-form-status\" style=\"font-size:14px;min-height:1.2em\"></div>\n </div>\n </div>\n <div class=\"listcol\" id=\"cred-list\"><div class=\"muted\">載入中…</div></div>\n </div>\n\n <!-- 分流台(原收件匣,Arcrun#9 改裝=全部待辦分流台) -->\n <div class=\"view page narrow\" id=\"v-inbox\">\n <div class=\"pagehead\"><span class=\"t\">分流台</span><span class=\"m\">全部待辦・80/15/5</span></div>\n <div class=\"hscroll\" id=\"tg-chips\" style=\"margin-top:14px\"></div>\n <div id=\"tg-out\" style=\"padding-top:2px\"><div class=\"muted\" style=\"padding:14px 0\">載入中…</div></div>\n </div>\n\n <!-- 設定 -->\n <div class=\"view page narrow\" id=\"v-settings\">\n <div class=\"pagehead\"><span class=\"t\">設定</span></div>\n <div class=\"setcol\">\n <div class=\"panel\">\n <div style=\"display:flex;align-items:center;gap:14px\">\n <div style=\"min-width:0\">\n <div style=\"font-size:17px;font-weight:600\">深色模式</div>\n <div style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">預設淺色(紙感)。切換立即生效,選擇記在這台裝置(localStorage)。</div>\n </div>\n <div class=\"switch\" id=\"st-theme-switch\" data-themetoggle style=\"cursor:pointer\"><i></i></div>\n </div>\n </div>\n <div class=\"panel\">\n <!-- t36leo 2026-07-25 定案:語意搜尋預設開啟、不做開關):\n 這裡原本是一個「長得像開關、其實不能按」的唯讀狀態燈(title 自承「不能遠端改」),\n 旁邊還教用戶去部署端改 config.yaml 跑 CLI——對一鍵安裝進來的用戶那是天書,\n 而且安裝器現在裝機時就把語意索引開好了,那段指示已經不成立。\n 改成單純的狀態列:能用就說能用,不能用就說原因,不擺一個按不動的開關讓人誤會。 -->\n <div style=\"font-size:17px;font-weight:600\">語意搜尋</div>\n <div id=\"st-vec\" style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">狀態偵測中…</div>\n <div id=\"st-vec-hint\" style=\"margin-top:12px;padding:12px 14px;border-radius:10px;background:rgba(var(--amber-rgb),.07);border:1px dashed rgba(var(--amber-rgb),.35);font-size:14px;line-height:1.7;color:rgba(var(--ink-rgb),.65);display:none\"></div>\n </div>\n <div class=\"panel\">\n <div style=\"font-size:17px;font-weight:600\">MCP token 有效期(TTL</div>\n <div id=\"st-mcp-ttl\" style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">讀取中…</div>\n <div style=\"margin-top:12px;padding:12px 14px;border-radius:10px;background:rgba(var(--amber-rgb),.07);border:1px dashed rgba(var(--amber-rgb),.35);font-size:14px;line-height:1.7;color:rgba(var(--ink-rgb),.65)\">\n 誠實佔位:此頁<b style=\"color:var(--amber)\">還不能改</b>這個值——可調功能=Arcrun#19(設定頁改→存 KBDB→發 token 時讀)。實作前要調整請在部署端 env <code style=\"font-size:12.5px\">MCP_TOKEN_TTL</code>mcp worker)設定。\n </div>\n </div>\n <div class=\"panel\">\n <div style=\"font-size:17px;font-weight:600\">更換帳號密碼</div>\n <div style=\"margin-top:4px;font-size:14px;color:rgba(var(--ink-rgb),.55)\">需輸入舊密碼驗證身分</div>\n <div style=\"margin-top:14px;display:flex;flex-direction:column;gap:10px\">\n <input type=\"password\" id=\"st-pw-old\" class=\"txt\" placeholder=\"舊密碼\">\n <input type=\"email\" id=\"st-pw-email\" class=\"txt\" placeholder=\"Email(可沿用或換新)\">\n <input type=\"password\" id=\"st-pw-new\" class=\"txt\" placeholder=\"新密碼(至少 8 碼)\">\n <button class=\"btn\" id=\"st-pw-save\">更新密碼</button>\n <div id=\"st-pw-status\" style=\"font-size:14px;min-height:1.2em\"></div>\n </div>\n </div>\n \n <div class=\"panel\">\n <div style=\"font-size:17px;font-weight:600\">Portal 帳號密碼救援</div>\n <div style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">忘記某個 Portal(RAG 搜尋頁)帳號的密碼,包含你自己那組管理員帳號——不需要先登進 Portal。輸入該帳號的 Email,會產生一組新密碼,只顯示這一次,請立刻抄下並拿去 Portal 登入頁使用。</div>\n <div style=\"margin-top:14px;display:flex;flex-direction:column;gap:10px\">\n <input type=\"email\" id=\"st-portal-recover-email\" class=\"txt\" placeholder=\"Portal 帳號 Email\">\n <button class=\"btn\" id=\"st-portal-recover-btn\">產生新密碼</button>\n <div id=\"st-portal-recover-status\" style=\"font-size:14px;min-height:1.2em\"></div>\n </div>\n </div>\n <div class=\"panel\">\n <div style=\"font-size:17px;font-weight:600;margin-bottom:12px\">系統資訊</div>\n <div id=\"st-info\"><div class=\"muted\">載入中…</div></div>\n </div>\n <button class=\"btn3\" id=\"st-logout\" style=\"padding:14px;font-size:16px;border-radius:11px\">登出</button>\n </div>\n </div>\n\n </main>\n</div>\n\n<!-- 手機底部導覽 -->\n<div id=\"tabbar\">\n \n <div class=\"tab\" data-nav=\"search\">搜尋</div>\n <div class=\"tab\" data-nav=\"workflows\">工作流</div>\n \n <div class=\"tab\" data-nav=\"settings\">設定</div>\n</div>\n\n<div id=\"toast\"></div>\n\n<script>\n(function () {\n var API_BASE = window.ARCRUN_API_BASE || '';\n 'use strict';\n // 台北時間 helperlib/taipei-time.ts 注入,與駕駛艙 #36 台北日判定同一套)——\n // console 所有日期/時間顯示固定 Asia/Taipei,不隨看的裝置時區漂移\n var TAIPEI_OFFSET_MS = 28800000; // UTC+8,台北無 DST\nfunction tpePad(n) { n = String(n); return n.length < 2 ? '0' + n : n; }\nfunction taipeiDayKey(ms) { return new Date(ms + TAIPEI_OFFSET_MS).toISOString().slice(0, 10); }\nfunction taipeiDateStr(ms) { return taipeiDayKey(ms); }\nfunction taipeiDateTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return taipeiDayKey(ms) + ' ' + tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()); }\nfunction taipeiTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()) + ':' + tpePad(d.getUTCSeconds()); }\nfunction taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { month: d.getUTCMonth() + 1, day: d.getUTCDate() }; }\n var REGISTRY_BASE = \"https://registry.arcrun.dev\";\n var $ = function (id) { return document.getElementById(id); };\n\n // ── 狀態 ──\n var S = {\n token: localStorage.getItem('arcrun_console_session') || '',\n tenant: '',\n chip: '', // entry_type filter'' = 全部)\n library: '', // library filter'' = 全館;藏書地圖點庫卡片設定,M5/R4\n semantic: false,\n cardId: '',\n graphCenter: '',\n wfCache: null,\n pins: (function () { try { return JSON.parse(localStorage.getItem('arcrun_console_pins') || '{}'); } catch (e) { return {}; } })()\n };\n\n function esc(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c];\n });\n }\n function toast(msg) {\n var el = $('toast');\n el.textContent = msg;\n el.style.display = 'block';\n clearTimeout(toast._t);\n toast._t = setTimeout(function () { el.style.display = 'none'; }, 2000);\n }\n function apiHeaders() { return { 'X-Arcrun-API-Key': S.tenant }; }\n\n // fetch 失敗(斷網)的裸 TypeError 訊息(Failed to fetch / Load failed)→ 友善誠實文案\n function friendlyErr(e) {\n var m = e && e.message ? String(e.message) : String(e);\n if (/failed to fetch|load failed|networkerror|network request failed/i.test(m)) return '連線中斷——請檢查網路後重試';\n return m;\n }\n\n // ── 主題(深/淺):預設淺色,選擇存 localStorage(head 預載腳本已先套用防閃色)──\n function getTheme() { return document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'; }\n function setTheme(t) {\n document.documentElement.setAttribute('data-theme', t);\n try { localStorage.setItem('arcrun_console_theme', t); } catch (e) { /* 私密模式存不了就只影響本次 */ }\n syncThemeUI();\n }\n function syncThemeUI() {\n var dark = getTheme() === 'dark';\n document.querySelectorAll('.themelabel').forEach(function (el) { el.textContent = dark ? '☀ 切淺色' : '☾ 切深色'; });\n var sw = $('st-theme-switch');\n if (sw) sw.classList.toggle('on', dark);\n }\n document.addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-themetoggle]');\n if (t) setTheme(getTheme() === 'dark' ? 'light' : 'dark');\n });\n syncThemeUI();\n\n // created_at 容錯:epoch 秒 / 毫秒 / ISO / 'YYYY-MM-DD HH:MM:SS'\n function parseAtMs(v) {\n if (v == null || v === '') return null;\n if (typeof v === 'number') return v < 1e12 ? v * 1000 : v;\n if (/^\\d+$/.test(v)) { var n = Number(v); return n < 1e12 ? n * 1000 : n; }\n var s = /T/.test(v) ? v : String(v).replace(' ', 'T') + 'Z';\n var ms = Date.parse(s);\n return isNaN(ms) ? null : ms;\n }\n // 顯示一律台北時間(bug 修正:原本用瀏覽器本地 getFullYear/getHours——顯示結果\n // 隨看的裝置時區漂移,且與後端「台北日」判定不同套;憑證頁 7/6 22:57(台北)建立的\n // credential 在非 +8 裝置上會顯示成另一天)\n function fmtDate(v) {\n var ms = parseAtMs(v);\n return ms == null ? '' : taipeiDateStr(ms);\n }\n function fmtDateTime(v) {\n var ms = parseAtMs(v);\n return ms == null ? '' : taipeiDateTimeStr(ms);\n }\n\n // ── 極簡 Markdown 渲染(先全 escape 再上格式,防 XSS)──\n function mdInline(s) {\n return s\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<b>$1</b>')\n .replace(/(^|[^\\\\])\\x60([^\\x60]+)\\x60/g, '$1<code>$2</code>');\n }\n function mdRender(src) {\n var lines = String(src || '').split(/\\r?\\n/);\n var out = [], inCode = false, listMode = '';\n function closeList() { if (listMode) { out.push(listMode === 'ul' ? '</ul>' : '</ol>'); listMode = ''; } }\n for (var i = 0; i < lines.length; i++) {\n var raw = lines[i];\n if (/^\\s*\\x60\\x60\\x60/.test(raw)) {\n closeList();\n if (inCode) { out.push('</code></pre>'); inCode = false; }\n else { out.push('<pre><code>'); inCode = true; }\n continue;\n }\n if (inCode) { out.push(esc(raw) + '\\n'); continue; }\n var line = esc(raw);\n var h = raw.match(/^(#{1,4})\\s+(.*)$/);\n if (h) { closeList(); out.push('<h' + h[1].length + '>' + mdInline(esc(h[2])) + '</h' + h[1].length + '>'); continue; }\n if (/^\\s*&gt;\\s?/.test(line)) { closeList(); out.push('<blockquote>' + mdInline(line.replace(/^\\s*&gt;\\s?/, '')) + '</blockquote>'); continue; }\n var li = raw.match(/^\\s*[-*+]\\s+(.*)$/);\n if (li) { if (listMode !== 'ul') { closeList(); out.push('<ul>'); listMode = 'ul'; } out.push('<li>' + mdInline(esc(li[1])) + '</li>'); continue; }\n var oli = raw.match(/^\\s*\\d+[.)]\\s+(.*)$/);\n if (oli) { if (listMode !== 'ol') { closeList(); out.push('<ol>'); listMode = 'ol'; } out.push('<li>' + mdInline(esc(oli[1])) + '</li>'); continue; }\n if (/^\\s*$/.test(raw)) { closeList(); continue; }\n closeList();\n out.push('<p>' + mdInline(line) + '</p>');\n }\n closeList();\n if (inCode) out.push('</code></pre>');\n return out.join('');\n }\n\n // ── 路由 ──\n // VIEWS / HOME 由 server 依 CONSOLE_PROFILE 注入(full=全套 7 頁落地駕駛艙;rag=4 頁落地搜尋)。\n // 不在 VIEWS 裡的 hash(含被 profile 裁掉的頁)一律導回 HOME——手打 #/cockpit 也進不去。\n var VIEWS = [\"search\",\"card\",\"workflows\",\"settings\"];\n var HOME = \"search\";\n function currentRoute() {\n var h = location.hash.replace(/^#\\/?/, '');\n var seg = h.split('/');\n return { raw: seg[0] || '', view: VIEWS.indexOf(seg[0]) >= 0 ? seg[0] : HOME, arg: seg.slice(1).join('/') };\n }\n function nav(view, arg) {\n location.hash = '#/' + view + (arg ? '/' + encodeURIComponent(arg) : '');\n }\n function showView(name) {\n VIEWS.forEach(function (v) { $('v-' + v).classList.toggle('on', v === name); });\n var activeKey = name === 'card' ? 'search' : name;\n document.querySelectorAll('#sidenav .nav, #tabbar .tab').forEach(function (el) {\n el.classList.toggle('on', el.getAttribute('data-nav') === activeKey);\n });\n window.scrollTo(0, 0);\n }\n var LOADERS = { cockpit: loadCockpit, search: initSearchOnce, card: loadCard, workflows: loadWorkflows, creds: loadCreds, inbox: loadTriage, settings: loadSettings };\n function route() {\n var r = currentRoute();\n // 隱藏頁/不存在頁 guardhash 不在 VIEWS → 改寫成 HOMEhashchange 會再進來一次正常渲染)\n if (r.raw && r.raw !== r.view) { location.hash = '#/' + r.view; return; }\n if (r.view === 'card' && r.arg) S.cardId = decodeURIComponent(r.arg);\n showView(r.view);\n if (LOADERS[r.view]) LOADERS[r.view]();\n }\n window.addEventListener('hashchange', route);\n document.addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-nav]');\n if (t) nav(t.getAttribute('data-nav'));\n });\n\n // ── 認證 ──\n function showAuth(which) {\n $('v-login').classList.toggle('on', which === 'login');\n $('v-setup').classList.toggle('on', which === 'setup');\n $('shell').classList.remove('on');\n $('tabbar').classList.remove('on');\n }\n function showApp() {\n $('v-login').classList.remove('on');\n $('v-setup').classList.remove('on');\n $('shell').classList.add('on');\n $('tabbar').classList.add('on');\n $('side-foot').innerHTML = '租戶 ' + esc(S.tenant) + '<br>' + esc(location.host);\n if (!location.hash) location.hash = '#/' + HOME;\n route();\n }\n function init() {\n if (S.token) {\n fetch(API_BASE + '/console/session', { headers: { Authorization: 'Bearer ' + S.token } })\n .then(function (r) { return r.ok ? r.json() : Promise.reject(new Error('session 失效')); })\n .then(function (d) { S.tenant = d.tenant || ''; showApp(); })\n .catch(function () {\n S.token = ''; localStorage.removeItem('arcrun_console_session');\n checkConfigured();\n });\n } else {\n checkConfigured();\n }\n }\n function checkConfigured() {\n fetch(API_BASE + '/console/auth-status')\n .then(function (r) { return r.json(); })\n .then(function (d) { showAuth(d.configured ? 'login' : 'setup'); })\n .catch(function () { showAuth('login'); });\n }\n function handleAuthResponse(d) {\n S.token = d.session_token;\n localStorage.setItem('arcrun_console_session', S.token);\n S.tenant = d.tenant || '';\n showApp();\n }\n $('login-submit').addEventListener('click', function () {\n var email = $('login-email').value.trim(), password = $('login-password').value;\n $('login-status').textContent = '';\n fetch(API_BASE + '/console/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, password: password }) })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) { if (!x.ok) { $('login-status').textContent = x.d.error || '登入失敗'; return; } handleAuthResponse(x.d); })\n .catch(function (e) { $('login-status').textContent = '請求失敗:' + friendlyErr(e); });\n });\n $('login-password').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') $('login-submit').click(); });\n $('setup-submit').addEventListener('click', function () {\n var email = $('setup-email').value.trim(), pw = $('setup-password').value, pw2 = $('setup-password2').value;\n if (pw !== pw2) { $('setup-status').textContent = '兩次密碼不一致'; return; }\n $('setup-status').textContent = '';\n fetch(API_BASE + '/console/setup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, password: pw }) })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) { if (!x.ok) { $('setup-status').textContent = x.d.error || '設定失敗'; return; } handleAuthResponse(x.d); })\n .catch(function (e) { $('setup-status').textContent = '請求失敗:' + friendlyErr(e); });\n });\n\n // ── 駕駛艙 ──\n var LIGHT = {\n green: { ch: '安', title: '系統運轉中', grad: 'radial-gradient(circle at 36% 30%,#8fe8b4,#3fbe78 55%,#22754a 100%)', anim: 'breatheGreen' },\n yellow: { ch: '趕', title: '落後趕工中', grad: 'radial-gradient(circle at 36% 30%,#f2d194,#e8b45a 55%,#8a5f1e 100%)', anim: 'breatheAmber' },\n red: { ch: '滯', title: '卡住或斷訊', grad: 'radial-gradient(circle at 36% 30%,#f0a094,#d95f4c 55%,#7e2c20 100%)', anim: 'breatheRed' }\n };\n var TASK_ICONS = { done: '✓', doing: '◐', todo: '○', blocked: '●' };\n var CNUM = ['零','一','二','三','四','五','六','七','八','九','十'];\n function cnDay(n) { return n <= 10 ? CNUM[n] : (n < 20 ? '十' + (n % 10 ? CNUM[n % 10] : '') : CNUM[Math.floor(n / 10)] + '十' + (n % 10 ? CNUM[n % 10] : '')); }\n function taskLine(t) {\n var cls = TASK_ICONS[t.status] ? t.status : 'blocked';\n return '<li class=\"' + cls + '\"><span class=\"ic\">' + (TASK_ICONS[t.status] || '●') + '</span><span>' + esc(t.title) + '</span></li>';\n }\n function ckAge(m) {\n if (m == null || m < 0) return '時間不明';\n if (m < 60) return m + ' 分鐘前';\n if (m < 2880) return Math.round(m / 60) + ' 小時前';\n return Math.round(m / 1440) + ' 天前';\n }\n function loadCockpit() {\n var nowTpe = taipeiMonthDay(Date.now()); // 頁首日期=台北日,不吃裝置時區\n $('ck-date').textContent = CNUM[nowTpe.month] + '月' + cnDay(nowTpe.day) + '日';\n fetch(API_BASE + '/console/dashboard-data')\n .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })\n .then(function (d) {\n var cfg = LIGHT[d.light] || LIGHT.green;\n var orb = $('ck-orb');\n orb.style.background = cfg.grad;\n orb.style.animation = cfg.anim + ' 3.4s ease-in-out infinite';\n $('ck-char').textContent = cfg.ch;\n $('ck-title').textContent = cfg.title;\n $('ck-sub').textContent = (d.last_beat\n ? d.last_beat.actor + '・' + d.last_beat.ago_minutes + ' 分鐘前' + (d.last_beat.note ? '・' + d.last_beat.note : '')\n : '尚無心跳資料') + (d.light !== 'green' && d.light_reason ? '' + d.light_reason + '' : '');\n var done = d.today_done || 0, total = d.today_total || 0;\n $('ck-done').textContent = done; $('ck-total').textContent = total;\n $('ck-bar').style.width = (total ? Math.round(done / total * 100) : 0) + '%';\n $('ck-inbox').textContent = d.inbox_new || 0;\n // 等你的事:來源與維護時間攤開講(fix/console-dashboard-live-data)——\n // Gitea sprint 等leo清單是活資料源;fallback dash_wait 一定標 stale,不假裝是今天的\n var wb = $('ck-waitbox'), body = $('ck-wait'), wmeta = $('ck-waitmeta');\n var wm = d.waiting_meta || {};\n if (d.waiting && d.waiting.length) {\n wb.classList.add('has'); body.className = '';\n body.innerHTML = d.waiting.map(function (w) {\n return '<div class=\"wait-item\"><span class=\"dm\">' + (w.urgency ? esc(w.urgency) : '◆') + '</span><span>' +\n (w.id ? '<b>#' + esc(w.id) + '</b> ' : '') + esc(w.title) + '</span></div>';\n }).join('');\n } else {\n wb.classList.remove('has'); body.className = 'wait-none';\n body.textContent = wm.source === 'none' ? '(管線未接)' : '無,你不用做任何事';\n }\n if (wm.source === 'gitea_sprint') {\n wmeta.innerHTML = '來源:sprint 等leo清單・維護於 ' + ckAge(wm.updated_ago_minutes) +\n (wm.stale ? '<br><span class=\"err\">⚠ 清單超過 2 天沒維護,可能過時</span>' : '');\n } else if (wm.source === 'kbdb_dash_wait') {\n wmeta.innerHTML = '<span class=\"err\">⚠ ' + esc(wm.note || 'dash_wait 殘資料') + '・上次寫入 ' + ckAge(wm.updated_ago_minutes) + ',可能過時</span>';\n } else if (wm.source === 'none') {\n wmeta.innerHTML = '<span class=\"err\">管線未接:Gitea sprint 清單與 dash_wait 皆無資料</span>';\n } else {\n wmeta.textContent = '';\n }\n // 今日路線:sprint 任務板優先(來源與更新時間攤開講);dash_task fallback 沿舊誠實降級\n var rm = d.route_meta || {};\n var today = (d.tasks || []).filter(function (t) { return t.scope === 'today'; });\n var week = (d.tasks || []).filter(function (t) { return t.scope === 'week'; });\n if (rm.source === 'gitea_sprint_board') {\n $('ck-route-src').textContent = '來源 sprint 任務板・更新於 ' + ckAge(rm.updated_ago_minutes);\n var staleHead = rm.is_today ? '' :\n '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">⚠ 今日任務板未更新(最後 ' + ckAge(rm.updated_ago_minutes) + ')——以下是板上現況</span></li>';\n $('ck-today').innerHTML = staleHead + (today.length\n ? today.map(taskLine).join('')\n : '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">任務板上沒有可解析的事項</span></li>');\n } else if (rm.is_today || !today.length) {\n $('ck-route-src').textContent = '來源 dash_task';\n $('ck-today').innerHTML = today.length ? today.map(taskLine).join('') : '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">今日無排定項目——dash_task 今天沒有寫入</span></li>';\n } else {\n $('ck-route-src').textContent = '來源 dash_task';\n $('ck-today').innerHTML = '<li class=\"todo\"><span class=\"ic\">○</span><span class=\"muted\">今日尚無路線寫入——以下是 ' + ckAge(rm.updated_ago_minutes) + '的殘留路線</span></li>' + today.map(taskLine).join('');\n }\n $('ck-weekhead').style.display = week.length ? '' : 'none';\n $('ck-week').innerHTML = week.map(taskLine).join('');\n $('ck-stamp').textContent = '每 60 秒自動刷新・上次 ' + taipeiTimeStr(Date.parse(d.generated_at)) + '(台北)';\n })\n .catch(function (e) {\n $('ck-char').textContent = '';\n $('ck-title').textContent = '讀不到狀態';\n $('ck-sub').innerHTML = '<span class=\"err\">' + esc(friendlyErr(e)) + '・每 60 秒自動重試</span>';\n });\n }\n // 60 秒定時器無條件常駐:斷網期間照跑,網路恢復後下一輪自動刷新回正常畫面\n setInterval(function () { if (currentRoute().view === 'cockpit' && $('shell').classList.contains('on')) loadCockpit(); }, 60000);\n\n // ── 總庫搜尋 ──\n // chips = KBDB 真能篩的 entry_type(誠實:不編造「電腦筆記/手機筆記」等 KBDB 沒有的分類軸)\n var CHIPS = [\n { label: '全部', v: '' },\n { label: '筆記 note', v: 'note' },\n { label: '區塊 block', v: 'block' },\n { label: '數值 value', v: 'value' },\n { label: '工作流 workflow', v: 'workflow' }\n ];\n var searchInited = false;\n function initSearchOnce() {\n if (searchInited) { return; }\n searchInited = true;\n renderChips();\n loadScale();\n loadLibraryMap();\n }\n\n // ── 藏書地圖(library-map SDD M5/R4)──\n // 資料源:GET /kbdb/mapcypher 代轉 KBDB 基本盤,kbdb-proxy.ts——同 /kbdb/search 慣例)。\n // 兩段式渲染:先用全館列表(top_entities=名字 top3)立即出卡片,再逐庫拉 /kbdb/map/:library\n // 補 degreebridgescommit_hash(庫數少;詳圖拉不到就維持名字版——加分項不擋渲染)。\n // 空陣列/錯誤=誠實空狀態,只佔地圖區塊,不擋下方搜尋。\n var LM = { libs: null, details: {} }; // 地圖快取:點卡片重繪「搜尋中」標記時不用重打 API\n function lmHonest(head, body) {\n $('lm-cards').innerHTML = '<div class=\"honest\" style=\"grid-column:1/-1\"><div class=\"h\">' + esc(head) + '</div><div class=\"b\">' + body + '</div></div>';\n $('lm-bridges').innerHTML = '';\n $('lm-meta').textContent = '';\n }\n function lmEntityLine(ents) {\n // ents:詳圖版 [{name, degree}] 或全館列表版 ['name']——兩形都渲染,degree 有才顯示\n return (ents || []).slice(0, 5).map(function (t) {\n var name = typeof t === 'string' ? t : (t && t.name) || '';\n var deg = t && typeof t === 'object' && typeof t.degree === 'number' ? t.degree : null;\n return '<span class=\"tag green\">' + esc(name) + (deg != null ? '<span style=\"opacity:.65\">・' + deg + '</span>' : '') + '</span>';\n }).join(' ');\n }\n function renderLmCards(libs, details) {\n $('lm-meta').textContent = libs.length + ' 個庫・點卡片進該庫搜尋';\n $('lm-cards').innerHTML = libs.map(function (l) {\n var d = details[l.library];\n var ents = d && d.top_entities && d.top_entities.length ? d.top_entities : l.top_entities;\n // 更新時間感:commit_hash(詳圖有才顯示,短碼)+ updated_at(epoch 秒,台北時間)\n var stamp = (d && d.commit_hash ? '<span class=\"mono\">' + esc(String(d.commit_hash).slice(0, 7)) + '</span>・' : '') +\n '更新 ' + esc(fmtDateTime(l.updated_at) || '—');\n return '<div class=\"kcard\" data-lib=\"' + esc(l.library) + '\">' +\n '<div class=\"kt\">' + esc(l.library) + (S.library === l.library ? ' <span class=\"tag\" style=\"font-size:11.5px;vertical-align:middle\">搜尋中</span>' : '') + '</div>' +\n '<div class=\"ks\">' + (l.narrative ? esc(l.narrative) : '<span class=\"dim\">(此庫尚無 narrative——recompute 時可帶入)</span>') + '</div>' +\n (ents && ents.length ? '<div style=\"display:flex;gap:6px;flex-wrap:wrap\">' + lmEntityLine(ents) + '</div>' : '') +\n // Arcrun#87 三次收尾(2026-08-13):triplet_count=0 不等於這庫沒有知識——entries 有標庫、\n // 只是三元組萃取沒對它跑過(kb 以外幾乎全庫皆此況)。entry_count>0 時附一句,別讓看板\n // 的「0 三元組」被讀成「這庫是空的」(同 MCP 端 kbdb_map.ts 的 entryOnlyHint 同一件事)。\n '<div class=\"km\"><span class=\"mono\" style=\"color:var(--amber)\">' + (Number(l.triplet_count) || 0) + ' 三元組' +\n (!(Number(l.triplet_count) || 0) && (Number(l.entry_count) || 0) > 0\n ? '<span class=\"dim\">・' + (Number(l.entry_count) || 0) + ' 筆原始內容(尚未萃取)</span>' : '') + '</span>' +\n '<span class=\"dim\" style=\"margin-left:auto;font-size:12.5px\">' + stamp + '</span></div></div>';\n }).join('');\n }\n function renderLmBridges(details) {\n // 跨庫橋:詳圖 bridges[{entity, libraries[]}],多庫詳圖去重後列一小區;沒資料就整區不出\n var seen = {}, rows = [];\n Object.keys(details).forEach(function (k) {\n (details[k].bridges || []).forEach(function (b) {\n if (!b || !b.entity || seen[b.entity]) return;\n seen[b.entity] = true;\n rows.push(b);\n });\n });\n if (!rows.length) { $('lm-bridges').innerHTML = ''; return; }\n $('lm-bridges').innerHTML = '<div style=\"margin-top:14px;padding:14px 16px;border-radius:12px;background:rgba(var(--ok-rgb),.05);border:1px solid rgba(var(--ok-rgb),.2)\">' +\n '<div style=\"font-size:13px;letter-spacing:.14em;color:rgba(var(--ink-rgb),.5);margin-bottom:8px\">跨庫橋(同一 entity 出現在多個庫)</div>' +\n '<div style=\"display:flex;gap:8px;flex-wrap:wrap\">' + rows.slice(0, 12).map(function (b) {\n return '<span class=\"tag green\">' + esc(b.entity) + '</span><span class=\"dim\" style=\"font-size:12.5px;align-self:center\">' + esc((b.libraries || []).join(' ↔ ')) + '</span>';\n }).join('<span style=\"width:6px\"></span>') + '</div></div>';\n }\n function loadLibraryMap() {\n fetch(API_BASE + '/kbdb/map', { headers: apiHeaders() })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (!x.ok || x.d.success === false) {\n lmHonest('讀不到藏書地圖', esc(x.d.error || ('HTTP ' + x.status)) + '<br>不影響下方搜尋,可直接搜全庫。');\n return;\n }\n var libs = x.d.libraries || [];\n if (!libs.length) {\n lmHonest('還沒有藏書地圖', '這個租戶目前沒有任何三元組資料(地圖是查詢時即時核對重算的,不是要人手動 backfill——資料一進來下次載入就會出現)。<br>不影響下方搜尋,可直接搜全庫。');\n return;\n }\n LM.libs = libs; LM.details = {};\n renderLmCards(libs, {});\n Promise.allSettled(libs.map(function (l) {\n return fetch(API_BASE + '/kbdb/map/' + encodeURIComponent(l.library), { headers: apiHeaders() })\n .then(function (r) { return r.ok ? r.json() : null; });\n })).then(function (rs) {\n var details = {};\n rs.forEach(function (r) {\n if (r.status === 'fulfilled' && r.value && r.value.map && r.value.map.library) details[r.value.map.library] = r.value.map;\n });\n LM.details = details;\n renderLmCards(libs, details);\n renderLmBridges(details);\n });\n })\n .catch(function (e) { lmHonest('地圖服務不可達', esc(friendlyErr(e)) + '<br>不影響下方搜尋,可直接搜全庫。'); });\n }\n // 點庫卡片=設 library filter/kbdb/search 既有 library 參數,多值逗號分隔——此處單庫)進庫搜尋\n $('lm-cards').addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-lib]');\n if (!t) return;\n S.library = t.getAttribute('data-lib');\n renderLibBar();\n if (LM.libs) renderLmCards(LM.libs, LM.details);\n $('se-q').focus();\n if ($('se-q').value.trim()) doSearch();\n toast('已鎖定庫「' + S.library + '」——輸入關鍵字搜這個庫');\n });\n function renderLibBar() {\n $('se-libbar').innerHTML = S.library\n ? '<div style=\"margin-top:10px;display:flex;align-items:center;gap:8px;font-size:14px\">' +\n '<span class=\"tag\">庫 ' + esc(S.library) + '</span>' +\n '<button class=\"chip\" id=\"se-libclear\" style=\"padding:5px 12px;font-size:13px\">✕ 清除庫篩選(搜全館)</button></div>'\n : '';\n var clear = document.getElementById('se-libclear');\n if (clear) clear.addEventListener('click', function () {\n S.library = '';\n renderLibBar();\n if (LM.libs) renderLmCards(LM.libs, LM.details);\n if ($('se-q').value.trim()) doSearch();\n });\n }\n function renderChips() {\n $('se-chips').innerHTML = CHIPS.map(function (c, i) {\n return '<button class=\"chip' + (S.chip === c.v ? ' on' : '') + '\" data-chip=\"' + i + '\">' + esc(c.label) + '</button>';\n }).join('');\n }\n $('se-chips').addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-chip]');\n if (!t) return;\n S.chip = CHIPS[Number(t.getAttribute('data-chip'))].v;\n renderChips();\n if ($('se-q').value.trim()) doSearch();\n });\n // 頭部統計=精耕層 live 數字(leo 2026-07-07 裁:45.8 萬 14-E 搬遷遺產已 deprecated、\n // 之後要刪——不再拿遺產總數撐場面,只顯示真的新的)。搜尋本身仍搜全庫,資料不藏。\n function loadScale() {\n fetch(API_BASE + '/console/kb-scale-data')\n .then(function (r) { return r.ok ? r.json() : null; })\n .then(function (d) {\n // #100:讀不到就明說讀不到(原本靜默 return,會把上一輪的舊數字留在畫面上)\n if (!d) { $('se-scale').textContent = '精耕層 讀不到(規模統計讀取失敗,不影響搜尋)'; return; }\n var n = function (v) { return v == null ? '讀不到' : v.toLocaleString(); };\n var parts = ['wiki 卡 ' + n(d.wiki_card_total), '三元組 ' + n(d.triplets_total), '已嵌入 ' + n(d.embedded)];\n var latest = d.wiki_card_latest_ago_minutes;\n $('se-scale').textContent = '精耕層 ' + parts.join('・') +\n (latest != null && latest >= 0 ? '・最近寫入 ' + ckAge(latest) : '');\n })\n .catch(function () { $('se-scale').textContent = '精耕層 讀不到(規模統計讀取失敗,不影響搜尋)'; });\n }\n $('se-sem').addEventListener('click', function () {\n S.semantic = !S.semantic;\n $('se-sem').classList.toggle('on', S.semantic);\n if ($('se-q').value.trim()) doSearch();\n });\n $('se-q').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') doSearch(); });\n function entryTitle(e) {\n if (e.page_name) return e.page_name;\n var first = String(e.content || '').split(/\\r?\\n/).find(function (l) { return l.trim(); }) || '';\n first = first.replace(/^#+\\s*/, '').replace(/^[-*>]\\s*/, '').trim();\n if (first.length > 60) first = first.slice(0, 60) + '…';\n return first || '(無標題・' + (e.entry_type || 'entry') + '';\n }\n function entrySnippet(e) {\n var lines = String(e.content || '').split(/\\r?\\n/).filter(function (l) { return l.trim(); });\n var body = lines.slice(1).join(' ').replace(/\\s+/g, ' ').trim();\n if (!body) body = lines.join(' ');\n if (body.length > 140) body = body.slice(0, 140) + '…';\n return body;\n }\n function doSearch() {\n var q = $('se-q').value.trim();\n if (!q) { $('se-count').textContent = '請輸入查詢字'; return; }\n $('se-banner').innerHTML = '';\n $('se-count').textContent = '查詢中…';\n $('se-results').innerHTML = '';\n var url = API_BASE + '/kbdb/search?q=' + encodeURIComponent(q) + '&mode=' + (S.semantic ? 'semantic' : 'keyword');\n if (S.chip) url += '&entry_type=' + encodeURIComponent(S.chip);\n // 藏書地圖進庫(M5/R4):library filter/kbdb/search 既有參數(portal-auth P1 順延項),不新造\n if (S.library) url += '&library=' + encodeURIComponent(S.library);\n fetch(url, { headers: apiHeaders() })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (!x.ok) { $('se-count').innerHTML = '<span class=\"err\">' + esc(x.d.error || ('查詢失敗(HTTP ' + x.status + '')) + '</span>'; return; }\n var d = x.d;\n if (S.semantic && d.mode === 'keyword') {\n // 2026-08-09 leo:語意搜尋是安裝即提供的功能,降級=故障,不說「尚未啟用」。\n $('se-banner').innerHTML = '<div class=\"honest\" style=\"margin-top:18px\"><div class=\"h\">語意搜尋目前故障</div><div class=\"b\">' + esc(d.capability_hint || '語意搜尋目前故障(實例缺 Vectorize/AI 設定),以下先給關鍵字結果,不假裝是語意結果。') + '<br>維運資訊:' + esc(d.admin_hint || '(此版本後端未回報細節)') + '</div></div>';\n }\n var entries = d.entries || [];\n $('se-count').textContent = '命中 ' + entries.length + ' 筆・模式 ' + (d.mode || 'keyword') +\n '・搜尋範圍=' + (S.library ? '庫「' + S.library + '」' : '全庫(含 14-E 遺產)');\n if (!entries.length) {\n $('se-results').innerHTML = '<div class=\"muted\" style=\"padding:30px 10px;text-align:center;grid-column:1/-1\">找不到「' + esc(q) + '」——換個關鍵字試試。</div>';\n return;\n }\n $('se-results').innerHTML = entries.map(function (e) {\n // 精耕層(wiki_card)綠標突顯;其餘照 entry_type 原樣(不誤標——block/value/note\n // 大宗是 14-E 遺產,但 entry_type 本身不足以斷言,僅精耕層正向標記)\n var typeTag = e.entry_type === 'wiki_card'\n ? '<span class=\"tag green\">wiki_card・精耕</span>'\n : '<span class=\"tag\">' + esc(e.entry_type || 'entry') + '</span>';\n return '<div class=\"kcard\" data-card=\"' + esc(e.id) + '\">' +\n '<div class=\"kt\">' + esc(entryTitle(e)) + '</div>' +\n '<div class=\"ks\">' + esc(entrySnippet(e)) + '</div>' +\n '<div class=\"km\">' + typeTag +\n (e.source ? '<span class=\"dim\">' + esc(e.source) + '</span>' : '') +\n '<span class=\"dim mono\" style=\"margin-left:auto\">' + esc(fmtDate(e.created_at)) + '</span></div></div>';\n }).join('');\n })\n .catch(function (e) { $('se-count').innerHTML = '<span class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</span>'; });\n }\n $('se-results').addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-card]');\n if (t) nav('card', t.getAttribute('data-card'));\n });\n\n // ── 卡片詳頁 + 關聯 ──\n function loadCard() {\n if (!S.cardId) { $('cd-main').innerHTML = '<div class=\"muted\" style=\"padding:24px 0\">沒有指定卡片——從「總庫搜尋」點一張進來。</div>'; renderGraphEmpty('尚未載入卡片'); return; }\n $('cd-main').innerHTML = '<div class=\"muted\" style=\"padding:24px 0\">載入中…</div>';\n fetch(API_BASE + '/kbdb/entries/' + encodeURIComponent(S.cardId), { headers: apiHeaders() })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n if (!x.ok || !x.d.entry) { $('cd-main').innerHTML = '<div class=\"err\" style=\"padding:24px 0\">' + esc(x.d.error || '讀不到這張卡片') + '</div>'; renderGraphEmpty('讀不到卡片'); return; }\n var e = x.d.entry;\n var title = entryTitle(e);\n var html = '<div class=\"cardtitle\">' + esc(title) + '</div>' +\n '<div style=\"display:flex;gap:10px;align-items:center;margin-top:12px;font-size:13.5px;flex-wrap:wrap\">' +\n '<span class=\"tag\">' + esc(e.entry_type || 'entry') + '</span>' +\n (e.page_name ? '<span class=\"tag green\">' + esc(e.page_name) + '</span>' : '') +\n '<span class=\"muted mono\">' + esc(fmtDateTime(e.created_at)) + '</span></div>' +\n '<div class=\"md\">' + mdRender(e.content || '(此條目沒有內文)') + '</div>' +\n (e.source\n ? '<div class=\"srcline\"><span style=\"font-size:13px;letter-spacing:.14em;color:rgba(var(--ink-rgb),.5);flex:none\">來源回溯</span><span class=\"mono\" style=\"font-size:14px;color:var(--amber);word-break:break-all\">' + esc(e.source) + '</span></div>'\n : '');\n $('cd-main').innerHTML = html;\n var nodeName = e.page_name || title;\n S.graphCenter = nodeName;\n loadNeighbors(nodeName);\n })\n .catch(function (er) { $('cd-main').innerHTML = '<div class=\"err\" style=\"padding:24px 0\">請求失敗:' + esc(friendlyErr(er)) + '</div>'; renderGraphEmpty('請求失敗'); });\n }\n function renderGraphEmpty(msg) {\n $('cd-graph').innerHTML = '<div class=\"graphbox\" style=\"display:grid;place-items:center;height:160px\"><span class=\"muted\" style=\"font-size:14px;padding:0 20px;text-align:center\">' + esc(msg) + '</span></div>';\n $('cd-neighbors').innerHTML = '';\n }\n function loadNeighbors(name) {\n renderGraphEmpty('查詢關聯中…');\n fetch(API_BASE + '/kbdb/graph/neighbors/' + encodeURIComponent(name), { headers: apiHeaders() })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (!x.ok) { renderGraphEmpty(x.d.error || ('關聯查詢失敗(HTTP ' + x.status + '')); return; }\n var neighbors = x.d.neighbors || [];\n var edges = x.d.edges || [];\n // Arcrun#168 收斂:圖查詢改打 KBDBneighbors 是 { node, predicate, from, depth } 物件\n //(舊的 plugin 版回字串)。不轉成名字會渲染成 [object Object]——與 portal 同一套慣例。\n var names = neighbors.map(function (nb) {\n if (typeof nb === 'string') return nb;\n return (nb && (nb.node || nb.name)) || '';\n }).filter(Boolean);\n if (!names.length) { renderGraphEmpty('尚無關聯資料——這張卡還沒有 triplet 連進知識圖譜。'); return; }\n renderGraph(name, names.slice(0, 10));\n $('cd-neighbors').innerHTML = edges.slice(0, 12).map(function (t) {\n var other = t.subject === name ? t.object : t.subject;\n return '<div class=\"nbrow\" data-gnode=\"' + esc(other) + '\">' +\n '<span class=\"tag green\" style=\"flex:none\">' + esc(t.predicate || '關聯') + '</span>' +\n '<span style=\"word-break:break-word\">' + esc(other) + '</span>' +\n '<span style=\"margin-left:auto;color:rgba(var(--amber-rgb),.6);flex:none\"></span></div>';\n }).join('');\n })\n .catch(function (e) { renderGraphEmpty('關聯服務不可達:' + friendlyErr(e)); });\n }\n function renderGraph(center, nodes) {\n var n = nodes.length || 1;\n var lines = '', chips = '';\n for (var i = 0; i < n; i++) {\n var a = (-90 + i * (360 / n)) * Math.PI / 180;\n var x = 50 + 39 * Math.cos(a), y = 50 + 37 * Math.sin(a);\n lines += '<line x1=\"50\" y1=\"50\" x2=\"' + x.toFixed(2) + '\" y2=\"' + y.toFixed(2) + '\" stroke=\"rgba(var(--ok-rgb),.3)\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" stroke-dasharray=\"4 4\"/>';\n chips += '<div class=\"gnode\" style=\"left:' + x.toFixed(2) + '%;top:' + y.toFixed(2) + '%\" data-gnode=\"' + esc(nodes[i]) + '\">' + esc(nodes[i]) + '</div>';\n }\n var shortCenter = center.length > 16 ? center.slice(0, 16) + '…' : center;\n $('cd-graph').innerHTML = '<div class=\"graphbox\">' +\n '<svg viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\" style=\"position:absolute;inset:0;width:100%;height:100%\">' + lines + '</svg>' +\n '<div class=\"gcenter\"><span>' + esc(shortCenter) + '</span></div>' + chips +\n '<div style=\"position:absolute;right:12px;bottom:10px;font-size:11.5px;color:rgba(var(--ink-rgb),.35)\">╌ 知識圖譜鄰居(點節點展開)</div></div>';\n }\n document.addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-gnode]');\n if (!t) return;\n var name = t.getAttribute('data-gnode');\n S.graphCenter = name;\n loadNeighbors(name);\n });\n\n // ── 工作流 ──\n function loadWorkflows() {\n fetch(API_BASE + '/webhooks/named', { headers: apiHeaders() })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n if (!x.ok) { $('wf-list').innerHTML = '<div class=\"err\">' + esc(x.d.error || '讀取失敗') + '</div>'; return; }\n S.wfCache = x.d.workflows || [];\n $('wf-count').textContent = S.wfCache.length + ' 條自動化';\n renderWorkflows();\n })\n .catch(function (e) { $('wf-list').innerHTML = '<div class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</div>'; });\n }\n $('wf-q').addEventListener('input', function () { renderWorkflows(); });\n function renderWorkflows(openName) {\n var list = S.wfCache || [];\n var q = $('wf-q').value.trim().toLowerCase();\n if (q) list = list.filter(function (w) { return (w.name + ' ' + (w.description || '')).toLowerCase().indexOf(q) >= 0; });\n list = list.slice().sort(function (a, b) { return (S.pins[b.name] ? 1 : 0) - (S.pins[a.name] ? 1 : 0); });\n if (!list.length) {\n $('wf-list').innerHTML = '<div class=\"honest\"><div class=\"h\">' + (q ? '沒有符合的工作流' : '還沒有任何工作流') + '</div><div class=\"b\">' + (q ? '換個關鍵字試試。' : '用 acr push 部署第一條 workflow 後,這裡就會出現。') + '</div></div>';\n return;\n }\n $('wf-list').innerHTML = list.map(function (w) {\n var pinned = !!S.pins[w.name];\n var open = w.name === openName;\n return '<div class=\"card-item' + (pinned ? ' hl' : '') + '\" data-wf=\"' + esc(w.name) + '\">' +\n '<div class=\"rowhead\" data-wftoggle=\"' + esc(w.name) + '\">' +\n '<button class=\"pinbtn' + (pinned ? ' on' : '') + '\" data-wfpin=\"' + esc(w.name) + '\" title=\"釘選\">' + (pinned ? '★' : '☆') + '</button>' +\n '<div style=\"min-width:0\"><div style=\"font-size:17px;font-weight:600;line-height:1.4;word-break:break-word\">' + esc(w.name) + '</div>' +\n '<div style=\"margin-top:3px;font-size:13.5px;color:rgba(var(--ink-rgb),.5)\">' + esc(w.cron_expr ? '排程 ' + w.cron_expr : '事件觸發') + (w.created_at ? '・建立 ' + esc(fmtDate(w.created_at)) : '') + '</div></div>' +\n '<span style=\"margin-left:auto;color:rgba(var(--amber-rgb),.6);flex:none\">' + (open ? '▾' : '') + '</span></div>' +\n (open ? renderWfBody(w) : '') + '</div>';\n }).join('');\n if (openName) fetchWfRuns(openName);\n }\n function renderWfBody(w) {\n return '<div class=\"rowbody\">' +\n '<div style=\"margin-top:14px;font-size:15.5px;line-height:1.7;color:rgba(var(--ink-rgb),.7)\">' + (w.description ? esc(w.description) : '<span class=\"dim\">(未填描述——re-deploy 時補一句「能做什麼」它才搜得到)</span>') + '</div>' +\n '<div class=\"urlbox\" style=\"margin-top:14px\"><span style=\"font-size:12.5px;letter-spacing:.1em;color:rgba(var(--ink-rgb),.45);flex:none\">觸發網址</span>' +\n '<span class=\"u\">' + esc(w.webhook_url || '') + '</span>' +\n '<button class=\"btn2\" style=\"margin-left:auto;flex:none;padding:7px 13px;font-size:13.5px\" data-wfcopy=\"' + esc(w.webhook_url || '') + '\">複製</button></div>' +\n '<div style=\"margin-top:14px;font-size:13px;letter-spacing:.14em;color:rgba(var(--ink-rgb),.5)\">最近執行</div>' +\n '<div id=\"wf-runs\" style=\"margin-top:8px;display:flex;flex-direction:column;gap:6px\"><span class=\"dim\" style=\"font-size:14px\">載入中…</span></div>' +\n '<button class=\"btn\" style=\"margin-top:16px;width:100%\" data-wfrun=\"' + esc(w.name) + '\">手動觸發</button>' +\n '<div id=\"wf-runmsg\" style=\"margin-top:8px;font-size:14px;min-height:1.2em\"></div></div>';\n }\n var wfOpen = '';\n $('wf-list').addEventListener('click', function (ev) {\n var pin = ev.target.closest('[data-wfpin]');\n if (pin) {\n var name = pin.getAttribute('data-wfpin');\n if (S.pins[name]) delete S.pins[name]; else S.pins[name] = true;\n localStorage.setItem('arcrun_console_pins', JSON.stringify(S.pins));\n renderWorkflows(wfOpen);\n ev.stopPropagation();\n return;\n }\n var copy = ev.target.closest('[data-wfcopy]');\n if (copy) {\n var url = copy.getAttribute('data-wfcopy');\n (navigator.clipboard ? navigator.clipboard.writeText(url) : Promise.reject()).then(\n function () { toast('已複製觸發網址'); },\n function () { toast('複製失敗——手動選取網址'); }\n );\n ev.stopPropagation();\n return;\n }\n var run = ev.target.closest('[data-wfrun]');\n if (run) { triggerWf(run.getAttribute('data-wfrun'), run); ev.stopPropagation(); return; }\n var tog = ev.target.closest('[data-wftoggle]');\n if (tog) {\n var n = tog.getAttribute('data-wftoggle');\n wfOpen = wfOpen === n ? '' : n;\n renderWorkflows(wfOpen);\n }\n });\n function fetchWfRuns(name) {\n var box = document.getElementById('wf-runs');\n if (!box) return;\n fetch(API_BASE + '/workflows/' + encodeURIComponent(name) + '/executions?limit=3', { headers: apiHeaders() })\n .then(function (r) { return r.json(); })\n .then(function (d) {\n var runs = (d.data && d.data.executions) || [];\n if (!runs.length) { box.innerHTML = '<span class=\"dim\" style=\"font-size:14px\">尚無執行紀錄</span>'; return; }\n box.innerHTML = runs.map(function (r) {\n var okRun = r.verdict === 'success';\n return '<div style=\"display:flex;gap:10px;align-items:baseline;font-size:14.5px\">' +\n '<span style=\"flex:none;font-size:10px;color:' + (okRun ? 'var(--ok)' : 'var(--err)') + '\">●</span>' +\n '<span style=\"color:rgba(var(--ink-rgb),.7)\">' + (okRun ? '成功' : '失敗') + (typeof r.duration_ms === 'number' ? '・' + r.duration_ms + 'ms' : '') + (r.message && !okRun ? '・' + esc(String(r.message).slice(0, 60)) : '') + '</span>' +\n '<span class=\"dim mono\" style=\"margin-left:auto;font-size:12.5px\">' + esc(r.recorded_at ? fmtDateTime(r.recorded_at) : (r.timestamp ? fmtDateTime(Number(r.timestamp)) : '')) + '</span></div>';\n }).join('');\n })\n .catch(function () { box.innerHTML = '<span class=\"dim\" style=\"font-size:14px\">執行紀錄讀取失敗</span>'; });\n }\n function triggerWf(name, btn) {\n btn.disabled = true; btn.textContent = '執行中…';\n var msg = document.getElementById('wf-runmsg');\n fetch(API_BASE + '/webhooks/named/' + encodeURIComponent(name) + '/trigger', {\n method: 'POST',\n headers: Object.assign({ 'Content-Type': 'application/json' }, apiHeaders()),\n body: '{}'\n })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n btn.disabled = false; btn.textContent = '手動觸發';\n if (x.ok && x.d.success !== false) {\n if (msg) msg.innerHTML = '<span class=\"ok\">✓ 執行成功' + (typeof x.d.duration_ms === 'number' ? '・' + x.d.duration_ms + 'ms' : '') + '</span>';\n toast('「' + name + '」執行完成');\n } else {\n if (msg) msg.innerHTML = '<span class=\"err\">✕ ' + esc(x.d.error || '執行失敗') + '</span>';\n }\n fetchWfRuns(name);\n })\n .catch(function (e) {\n btn.disabled = false; btn.textContent = '手動觸發';\n if (msg) msg.innerHTML = '<span class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</span>';\n });\n }\n\n // ── 零件 / recipesv0 搜尋台保留區)──\n $('cr-go').addEventListener('click', doCompSearch);\n $('cr-q').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') doCompSearch(); });\n function doCompSearch() {\n var q = $('cr-q').value.trim();\n if (!q) return;\n var out = $('cr-out');\n out.innerHTML = '<div class=\"muted\">查詢中…</div>';\n Promise.allSettled([\n fetch(REGISTRY_BASE + '/components/search?q=' + encodeURIComponent(q)).then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); }),\n fetch(API_BASE + '/public-recipes?q=' + encodeURIComponent(q)).then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n ]).then(function (rs) {\n var html = '';\n var comp = rs[0];\n html += '<div class=\"dim\" style=\"font-size:12.5px;letter-spacing:.1em;margin:10px 0 6px\">COMPONENTS</div>';\n if (comp.status === 'fulfilled' && comp.value.ok) {\n var results = (comp.value.d.data && comp.value.d.data.results) || [];\n html += results.length\n ? results.map(function (c) { return '<div class=\"card-item\" style=\"padding:10px 14px;margin-bottom:6px;font-size:14px\"><span class=\"tag\">' + esc(c.canonical_id || c.id) + '</span> <span class=\"muted\">' + esc(c.description || c.name || '') + '</span></div>'; }).join('')\n : '<div class=\"muted\" style=\"font-size:14px\">沒查到符合的 component</div>';\n } else {\n html += '<div class=\"err\" style=\"font-size:14px\">components 查詢失敗(零件庫不可達)</div>';\n }\n var rec = rs[1];\n html += '<div class=\"dim\" style=\"font-size:12.5px;letter-spacing:.1em;margin:12px 0 6px\">RECIPES</div>';\n if (rec.status === 'fulfilled' && rec.value.ok) {\n var d = rec.value.d;\n if (d.found === false) {\n html += '<div class=\"muted\" style=\"font-size:14px\">' + esc(d.hint || '沒查到符合的 recipe') + '</div>';\n } else {\n var recipes = d.recipes || [];\n html += recipes.length\n ? recipes.map(function (r) { return '<div class=\"card-item\" style=\"padding:10px 14px;margin-bottom:6px;font-size:14px\"><span class=\"tag\">' + esc(r.canonical_id) + '</span> <span class=\"muted\">' + esc(r.description || r.display_name || '') + '</span></div>'; }).join('')\n : '<div class=\"muted\" style=\"font-size:14px\">沒查到符合的 recipe</div>';\n }\n } else {\n html += '<div class=\"err\" style=\"font-size:14px\">recipes 查詢失敗</div>';\n }\n out.innerHTML = html;\n });\n }\n\n // ── 憑證管理 ──\n $('cred-addbtn').addEventListener('click', function () {\n var f = $('cred-form');\n f.style.display = f.style.display === 'none' ? '' : 'none';\n });\n $('cred-save').addEventListener('click', function () {\n var name = $('cred-name').value.trim(), service = $('cred-service').value.trim(), value = $('cred-secret').value;\n var st = $('cred-form-status');\n if (!name || !value) { st.innerHTML = '<span class=\"err\">名稱與密文值必填</span>'; return; }\n if (!/^\\w+$/.test(name)) { st.innerHTML = '<span class=\"err\">名稱只能包含英文字母、數字和底線</span>'; return; }\n st.textContent = '保存中…';\n fetch(API_BASE + '/credentials', {\n method: 'POST',\n headers: Object.assign({ 'Content-Type': 'application/json' }, apiHeaders()),\n body: JSON.stringify({ name: name, value: value, service: service || undefined })\n })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n if (!x.ok || x.d.success === false) { st.innerHTML = '<span class=\"err\">' + esc(x.d.error || '保存失敗') + '</span>'; return; }\n st.textContent = '';\n $('cred-name').value = ''; $('cred-service').value = ''; $('cred-secret').value = '';\n $('cred-form').style.display = 'none';\n toast('已加密保存');\n loadCreds();\n })\n .catch(function (e) { st.innerHTML = '<span class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</span>'; });\n });\n var credOpen = '';\n var credCache = [];\n function loadCreds() {\n fetch(API_BASE + '/credentials/catalog', { headers: apiHeaders() })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n if (!x.ok || x.d.success === false) {\n $('cred-list').innerHTML = '<div class=\"honest\"><div class=\"h\">目錄讀取失敗</div><div class=\"b\">' + esc(x.d.error || '未知錯誤') + '</div></div>';\n return;\n }\n credCache = x.d.credentials || [];\n renderCreds();\n })\n .catch(function (e) { $('cred-list').innerHTML = '<div class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</div>'; });\n }\n function renderCreds() {\n if (!credCache.length) {\n $('cred-list').innerHTML = '<div class=\"honest\"><div class=\"h\">還沒有任何憑證</div><div class=\"b\">按「+ 新增憑證」加入第一筆。目錄只存名稱與服務,密文進 Workers Secrets、不可回看。</div></div>';\n return;\n }\n $('cred-list').innerHTML = credCache.map(function (c) {\n var open = credOpen === c.name;\n var initial = (c.service || c.name || '?').slice(0, 1).toUpperCase();\n return '<div class=\"card-item' + (open ? ' hl' : '') + '\">' +\n '<div class=\"rowhead\" data-credtoggle=\"' + esc(c.name) + '\">' +\n '<span class=\"avatar\">' + esc(initial) + '</span>' +\n '<div style=\"min-width:0\"><div style=\"font-size:16.5px;font-weight:600;word-break:break-all\">' + esc(c.name) + '</div>' +\n '<div style=\"margin-top:2px;font-size:13.5px;color:rgba(var(--ink-rgb),.5)\">' + esc(c.service || '未填服務') + '・建立於 ' + esc(fmtDateTime(c.created_at) || '—') + '(台北)</div></div>' +\n '<div style=\"margin-left:auto;text-align:right;font-size:12.5px;color:rgba(var(--ink-rgb),.4);flex:none\">最後使用<br>' + esc(c.last_used_at ? fmtDateTime(c.last_used_at) : '尚未記錄') + '</div></div>' +\n (open\n ? '<div class=\"rowbody\">' +\n '<div class=\"maskline\" style=\"margin-top:14px\"><span class=\"mono\" style=\"font-size:16px;letter-spacing:.3em;color:rgba(var(--ink-rgb),.4)\">••••••••••••••••</span><span style=\"margin-left:auto;font-size:12.5px;color:rgba(var(--ink-rgb),.4)\">已加密・不可回看</span></div>' +\n '<textarea class=\"txt\" id=\"cred-newval\" placeholder=\"貼上新值以整筆替換…\" rows=\"2\" style=\"margin-top:10px\"></textarea>' +\n '<div style=\"display:flex;gap:10px;margin-top:10px;flex-wrap:wrap\">' +\n '<button class=\"btn\" style=\"flex:1;min-width:140px\" data-credreplace=\"' + esc(c.name) + '\">整筆替換</button>' +\n '<button class=\"btn-danger\" disabled title=\"刪除端點尚未接上新目錄(T9 範圍)\">刪除・即將開通</button></div>' +\n '<div id=\"cred-msg\" style=\"margin-top:8px;font-size:14px;min-height:1.2em\"></div></div>'\n : '') +\n '</div>';\n }).join('');\n }\n $('cred-list').addEventListener('click', function (ev) {\n var rep = ev.target.closest('[data-credreplace]');\n if (rep) {\n var name = rep.getAttribute('data-credreplace');\n var ta = document.getElementById('cred-newval');\n var msg = document.getElementById('cred-msg');\n var val = ta ? ta.value : '';\n if (!val.trim()) { if (msg) msg.innerHTML = '<span class=\"err\">先貼上新值</span>'; return; }\n rep.disabled = true;\n var cur = credCache.find(function (c) { return c.name === name; });\n fetch(API_BASE + '/credentials/' + encodeURIComponent(name), {\n method: 'PUT',\n headers: Object.assign({ 'Content-Type': 'application/json' }, apiHeaders()),\n body: JSON.stringify({ value: val, service: cur && cur.service ? cur.service : undefined })\n })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n rep.disabled = false;\n if (!x.ok || x.d.success === false) { if (msg) msg.innerHTML = '<span class=\"err\">' + esc(x.d.error || '替換失敗') + '</span>'; return; }\n credOpen = '';\n toast('「' + name + '」已整筆替換');\n loadCreds();\n })\n .catch(function (e) { rep.disabled = false; if (msg) msg.innerHTML = '<span class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</span>'; });\n return;\n }\n var tog = ev.target.closest('[data-credtoggle]');\n if (tog) {\n var n = tog.getAttribute('data-credtoggle');\n credOpen = credOpen === n ? '' : n;\n renderCreds();\n }\n });\n\n // ── 分流台(原收件匣,Arcrun#9todoinbox 二源、三欄 80/15/5、per-project chips)──\n // 頁面只渲染 /console/triage-data 的結果(分欄/計數判定在後端純函式);chips 切換與\n // 「顯示已完成」是同一包資料的前端過濾,零額外請求(leo 手機看,省來回)。\n var TG = { data: null, project: '', showDone: false };\n var TG_TIERS = [\n { key: 'ai', nm: 'AI 會幫我搞定', pc: '80%', empty: '這欄空的——還沒有分給 AI 的待辦' },\n { key: 'collab', nm: '協作・AI 做完你接手', pc: '15%', empty: '這欄空的——沒有要你配合的事' },\n { key: 'leo', nm: '非我不可', pc: '5%', empty: '這欄空的——沒有非你不可的事' }\n ];\n function triItem(m) {\n var done = m.status === 'done';\n var meta = '';\n if (m.marker) meta += '<span class=\"tag dim\">' + esc(m.marker) + '</span>';\n meta += '<span class=\"tag\">' + esc(m.project || '未分項') + '</span>';\n if (m.source) meta += '<span>' + esc(m.source) + '</span>';\n if (m.unclassified && m.origin === 'todo') meta += '<span class=\"tag red\">未分流</span>';\n // 誤勾救濟:done 清單裡給「還原」(status 回 new + 移除 checked_via/checked_at,端點同一個)\n if (done) meta += '<button class=\"tri-restore\" data-trirestore=\"' + esc(m.id) + '\">還原</button>';\n meta += '<span class=\"mono\" style=\"margin-left:auto\">' + esc(fmtDateTime(m.at)) + '</span>';\n // 勾掉=雙向銷案的 console 端終局(checked_via:console,萃取端絕不復活);\n // 後端回寫成功才改本地狀態移出三欄(done 預設隱藏),不樂觀假裝\n var body = done\n ? '<div class=\"tx\">' + esc(m.text) + '</div>'\n : '<div class=\"row\"><div class=\"tx\">' + esc(m.text) + '</div><button class=\"tri-check\" data-tricheck=\"' + esc(m.id) + '\" title=\"勾掉(銷案)\">✓</button></div>';\n return '<div class=\"tri-item ' + (done ? 'done' : 'new') + '\">' + body + '<div class=\"mt\">' + meta + '</div></div>';\n }\n function triChip(label, val, on) {\n return '<button class=\"chip' + (on ? ' on' : '') + '\" data-triproj=\"' + esc(val) + '\">' + esc(label) + '</button>';\n }\n function renderTriage() {\n var d = TG.data || {};\n var items = d.items || [];\n var out = $('tg-out');\n // per-project chips:全部+各 project+(有未分項才出)未分項\n var chips = triChip('全部', '', TG.project === '');\n (d.projects || []).forEach(function (p) { chips += triChip(p, p, TG.project === p); });\n if (items.some(function (i) { return !i.project; })) chips += triChip('未分項', '__none__', TG.project === '__none__');\n $('tg-chips').innerHTML = items.length ? chips : '';\n if (!items.length) {\n out.innerHTML = '<div class=\"honest\" style=\"margin-top:14px\"><div class=\"h\">分流台是空的</div><div class=\"b\">資料源=KBDB todo entriesLogseq 萃取,ingest 灌入中)+ inbox entriesTelegram)。<br>寫入方尚未灌資料時這裡誠實顯示空。</div></div>';\n return;\n }\n var vis = items.filter(function (i) {\n return TG.project === '' || (TG.project === '__none__' ? !i.project : i.project === TG.project);\n });\n var doneN = vis.filter(function (i) { return i.status === 'done'; }).length;\n var html = '';\n TG_TIERS.forEach(function (t) {\n var news = vis.filter(function (i) { return i.tier === t.key && i.status !== 'done'; });\n var dones = TG.showDone ? vis.filter(function (i) { return i.tier === t.key && i.status === 'done'; }) : [];\n html += '<div class=\"tri-sec ' + t.key + '\"><div class=\"tri-sechead\"><span class=\"dot\"></span><span class=\"nm serif\">' + t.nm + '</span><span class=\"pc\">' + t.pc + '</span><span class=\"ct\">' + news.length + ' 件</span></div>';\n if (!news.length && !dones.length) html += '<div class=\"muted\" style=\"font-size:14px;padding:10px 2px 0\">' + t.empty + '</div>';\n html += news.map(triItem).join('') + dones.map(triItem).join('');\n html += '</div>';\n });\n html += '<div style=\"margin:24px 0 20px;text-align:center\"><button class=\"btn3\" id=\"tg-donetoggle\" style=\"padding:9px 18px;font-size:13.5px;border-radius:999px\">' +\n (TG.showDone ? '隱藏已完成' : '顯示已完成(' + doneN + '') + '</button></div>';\n out.innerHTML = html;\n $('tg-donetoggle').addEventListener('click', function () { TG.showDone = !TG.showDone; renderTriage(); });\n }\n $('tg-chips').addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-triproj]');\n if (!t) return;\n TG.project = t.getAttribute('data-triproj');\n renderTriage();\n });\n // 勾掉/還原:POST /console/triage-checkconsole session 保護的小端點——瀏覽器沒有 KBDB\n // tokenPATCH 由 server 端做,先 GET 原 entry 整串回寫防蓋掉別的欄位)。成功才動本地\n // 狀態重繪(該筆立即移出/回到三欄),失敗誠實 toast、按鈕解鎖可重試。\n function triageAct(id, action, btn) {\n btn.disabled = true;\n fetch(API_BASE + '/console/triage-check', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + S.token },\n body: JSON.stringify({ entry_id: id, action: action })\n })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (!x.ok) { btn.disabled = false; toast(x.d.error || ('失敗(HTTP ' + x.status + '')); return; }\n var items = (TG.data && TG.data.items) || [];\n for (var i = 0; i < items.length; i++) {\n if (items[i].id === id) items[i].status = (action === 'restore' ? 'new' : 'done');\n }\n renderTriage();\n toast(action === 'restore' ? '已還原' : '已勾掉');\n })\n .catch(function (e) { btn.disabled = false; toast('請求失敗:' + friendlyErr(e)); });\n }\n $('tg-out').addEventListener('click', function (ev) {\n var chk = ev.target.closest('[data-tricheck]');\n if (chk) { triageAct(chk.getAttribute('data-tricheck'), 'check', chk); return; }\n var rst = ev.target.closest('[data-trirestore]');\n if (rst) triageAct(rst.getAttribute('data-trirestore'), 'restore', rst);\n });\n function loadTriage() {\n var out = $('tg-out');\n out.innerHTML = '<div class=\"muted\" style=\"padding:14px 0\">載入中…</div>';\n fetch(API_BASE + '/console/triage-data', { headers: { Authorization: 'Bearer ' + S.token } })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (!x.ok) {\n out.innerHTML = '<div class=\"honest\" style=\"margin-top:14px\"><div class=\"h\">讀不到分流台</div><div class=\"b\">' + esc(x.d.error || ('HTTP ' + x.status)) + '</div></div>';\n return;\n }\n TG.data = x.d;\n // 記著的 project 篩選若已不存在(資料變了)→ 退回全部,別讓頁面空得不明不白\n if (TG.project && TG.project !== '__none__' && (x.d.projects || []).indexOf(TG.project) < 0) TG.project = '';\n renderTriage();\n })\n .catch(function (e) { out.innerHTML = '<div class=\"err\" style=\"padding:14px 0\">請求失敗:' + esc(friendlyErr(e)) + '</div>'; });\n }\n\n // ── 設定 ──\n function loadSettings() {\n // vectorize 狀態:探測既有 /kbdb/search 的 mode / capability_hint(無新端點,誠實讀降級訊號)\n fetch(API_BASE + '/kbdb/search?q=mira&mode=semantic', { headers: apiHeaders() })\n .then(function (r) { return r.json(); })\n .then(function (d) {\n // t36:狀態照實顯示(live 探測 mode,不是讀設定值)。啟用時不再顯示任何操作指示——\n // 沒有東西要用戶操作;未啟用才給一句人話與下一步。\n // 2026-08-09 leo:語意搜尋是安裝即提供的功能——探測到降級=這台實例壞了,\n // 照實標「故障」,不說「尚未啟用」(那會把 bug 說成沒提供的功能)。\n var on = d.mode === 'semantic';\n $('st-vec').textContent = on\n ? '● 正常——搜尋頁切到「語意」就能用意思找資料。'\n : '○ 故障——語意搜尋是內建功能,這台實例現在少了它(系統端問題,不是操作問題)。';\n var hint = $('st-vec-hint');\n if (on) {\n hint.style.display = 'none';\n } else {\n hint.style.display = '';\n hint.innerHTML = '修復方式:重新跑一次安裝流程(用原本的 Cloudflare 帳號),會把缺的語意索引設定補回來;已建好的資料不會重來。'\n + (d.admin_hint ? '<br>維運資訊:' + esc(d.admin_hint) : '');\n }\n })\n .catch(function () {\n $('st-vec').textContent = '狀態偵測失敗(知識庫服務目前連不上)';\n var hint = $('st-vec-hint');\n hint.style.display = '';\n hint.textContent = '這通常是暫時的,稍後重新整理這一頁再看。';\n });\n // MCP token TTL(誠實佔位:只顯示目前生效值,不假裝能改)\n fetch(API_BASE + '/console/settings-data')\n .then(function (r) { return r.ok ? r.json() : null; })\n .then(function (d) {\n if (!d) { $('st-mcp-ttl').textContent = '讀取失敗'; return; }\n var days = Math.round(d.mcp_token_ttl_seconds / 86400 * 10) / 10;\n $('st-mcp-ttl').textContent = '目前生效:' + d.mcp_token_ttl_seconds.toLocaleString() + ' 秒(約 ' + days + ' 天)・' +\n (d.mcp_token_ttl_source === 'env' ? '來自部署端 env MCP_TOKEN_TTL' : '未設 env,用預設值 30 天');\n })\n .catch(function () { $('st-mcp-ttl').textContent = '讀取失敗'; });\n // 系統資訊:worker 自報 + 精耕層規模(14-E 遺產總數不再顯示,leo 2026-07-07 裁)\n Promise.allSettled([\n fetch(API_BASE + '/').then(function (r) { return r.json(); }),\n fetch(API_BASE + '/console/kb-scale-data').then(function (r) { return r.ok ? r.json() : null; })\n ]).then(function (rs) {\n var svc = rs[0].status === 'fulfilled' ? rs[0].value : {};\n var kb = rs[1].status === 'fulfilled' ? rs[1].value : null;\n var n = function (v) { return v == null ? '讀不到' : v.toLocaleString(); };\n var rows = '';\n rows += '<div class=\"kvline\"><span class=\"muted\">服務</span><span class=\"mono\" style=\"font-size:14px\">' + esc(svc.service || 'arcrun-cypher-executor') + '</span></div>';\n rows += '<div class=\"kvline\"><span class=\"muted\">版本</span><span class=\"mono\" style=\"color:var(--amber)\">' + esc(svc.version || '—') + '</span></div>';\n rows += '<div class=\"kvline\"><span class=\"muted\">部署位置</span><span class=\"mono\" style=\"font-size:14px\">' + esc(location.host) + '</span></div>';\n rows += '<div class=\"kvline\"><span class=\"muted\">租戶</span><span class=\"mono\">' + esc(S.tenant) + '</span></div>';\n rows += '<div class=\"kvline\"><span class=\"muted\">精耕層</span><span class=\"mono\" style=\"color:var(--amber)\">' + (kb ? 'wiki 卡 ' + n(kb.wiki_card_total) + '・三元組 ' + n(kb.triplets_total) + '・已嵌入 ' + n(kb.embedded) : '讀取失敗') + '</span></div>';\n $('st-info').innerHTML = rows;\n });\n }\n $('st-pw-save').addEventListener('click', function () {\n var oldPw = $('st-pw-old').value, email = $('st-pw-email').value.trim(), newPw = $('st-pw-new').value;\n var st = $('st-pw-status');\n if (!oldPw) { st.innerHTML = '<span class=\"err\">請先輸入舊密碼</span>'; return; }\n if (!email) { st.innerHTML = '<span class=\"err\">請輸入 Email(沿用原本的也要填)</span>'; return; }\n if (!newPw || newPw.length < 8) { st.innerHTML = '<span class=\"err\">新密碼至少 8 碼</span>'; return; }\n st.textContent = '更新中…';\n fetch(API_BASE + '/console/setup/reset', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ current_password: oldPw, email: email, password: newPw })\n })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n if (!x.ok) { st.innerHTML = '<span class=\"err\">' + esc(x.d.error || '更新失敗') + '</span>'; return; }\n st.innerHTML = '<span class=\"ok\">密碼已更新,下次登入用新帳密</span>';\n $('st-pw-old').value = ''; $('st-pw-email').value = ''; $('st-pw-new').value = '';\n toast('密碼已更新');\n })\n .catch(function (e) { st.innerHTML = '<span class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</span>'; });\n });\n // arcrun-rag#25portal admin 密碼救援——只吃 console owner sessionS.token,本頁登入用的\n // 那把),不吃 portal session,所以就算忘記 portal 密碼、進不去 portal 也走得通。\n $('st-portal-recover-btn').addEventListener('click', function () {\n var email = $('st-portal-recover-email').value.trim();\n var st = $('st-portal-recover-status');\n if (!email) { st.innerHTML = '<span class=\"err\">請輸入 Email</span>'; return; }\n st.textContent = '處理中…';\n fetch(API_BASE + '/portal/admin/recover-password', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + S.token },\n body: JSON.stringify({ email: email })\n })\n .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n if (!x.ok) { st.innerHTML = '<span class=\"err\">' + esc(x.d.error || '失敗') + '</span>'; return; }\n st.innerHTML = '<span class=\"ok\">新密碼:<code style=\"font-size:15px;user-select:all\">' + esc(x.d.password) + '</code>(只顯示這一次,請立刻抄下)</span>';\n $('st-portal-recover-email').value = '';\n toast('新密碼已產生,請立刻抄下');\n })\n .catch(function (e) { st.innerHTML = '<span class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</span>'; });\n });\n // t36:原本這裡綁在那顆假開關上(點了只會 toast 一段 CLI 指示)。開關已移除,\n // 這個 handler 也必須一起拿掉——留著會讓 $('st-vec-switch') 回 null、addEventListener\n // 當場拋錯,把後面所有綁定(含登出)一起打斷。\n $('st-logout').addEventListener('click', function () {\n var t = S.token;\n if (t) fetch(API_BASE + '/console/logout', { method: 'POST', headers: { Authorization: 'Bearer ' + t } }).catch(function () {});\n S.token = ''; S.tenant = '';\n localStorage.removeItem('arcrun_console_session');\n location.hash = '';\n checkConfigured();\n });\n\n init();\n})();\n</script>\n</body>\n</html>\n"},"/favicon.ico":{"type":"image/x-icon","b64":true,"data":"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"},"/favicon.svg":{"type":"image/svg+xml","b64":false,"data":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\" role=\"img\" aria-label=\"arcrun icon\"><title>arcrun icon</title><rect width=\"1024\" height=\"1024\" fill=\"#17181A\"/><path fill=\"#FDFCFB\" fill-rule=\"nonzero\" d=\"M463.01,612.91 L436.06,612.91 L436.06,485.41 L435.86,477.78 L435.27,470.46 L434.28,463.44 L432.89,456.73 L431.11,450.31 L428.93,444.20 L426.36,438.39 L423.39,432.88 L420.02,427.68 L416.26,422.78 L412.10,418.18 L407.55,413.88 L402.62,409.91 L397.31,406.28 L391.65,402.99 L385.61,400.06 L379.21,397.47 L372.44,395.22 L365.30,393.32 L357.79,391.76 L349.92,390.55 L341.68,389.69 L333.08,389.17 L324.10,389.00 L317.39,389.10 L310.90,389.40 L304.63,389.89 L298.59,390.58 L292.77,391.47 L287.17,392.56 L281.80,393.85 L276.65,395.33 L271.72,397.02 L267.02,398.90 L262.53,400.98 L258.28,403.25 L254.20,405.69 L250.26,408.26 L246.45,410.95 L242.78,413.76 L239.25,416.71 L235.86,419.77 L232.60,422.97 L229.48,426.29 L226.50,429.74 L223.65,433.31 L220.94,437.01 L218.37,440.83 L257.76,476.08 L259.43,473.77 L261.16,471.52 L262.96,469.32 L264.81,467.18 L266.73,465.09 L268.71,463.05 L270.75,461.07 L272.85,459.15 L275.01,457.27 L277.23,455.45 L279.51,453.69 L281.86,451.98 L284.30,450.36 L286.86,448.89 L289.55,447.55 L292.37,446.36 L295.31,445.31 L298.38,444.39 L301.58,443.62 L304.90,442.99 L308.34,442.50 L311.91,442.15 L315.61,441.94 L319.44,441.87 L323.74,441.95 L327.85,442.21 L331.75,442.65 L335.45,443.25 L338.95,444.03 L342.24,444.98 L345.34,446.10 L348.23,447.40 L350.93,448.87 L353.42,450.51 L355.71,452.32 L357.79,454.31 L359.70,456.45 L361.44,458.74 L363.01,461.18 L364.42,463.75 L365.66,466.47 L366.73,469.34 L367.64,472.35 L368.39,475.50 L368.97,478.80 L369.38,482.24 L369.63,485.82 L369.71,489.55 L369.71,509.25 L323.58,509.25 L314.52,509.39 L305.80,509.82 L297.44,510.53 L289.43,511.52 L281.78,512.80 L274.47,514.37 L267.52,516.22 L260.93,518.35 L254.68,520.77 L248.79,523.47 L243.25,526.45 L238.06,529.72 L233.26,533.28 L228.88,537.14 L224.91,541.30 L221.36,545.76 L218.23,550.52 L215.52,555.57 L213.22,560.93 L211.34,566.58 L209.88,572.53 L208.84,578.78 L208.21,585.33 L208.00,592.18 L208.15,598.13 L208.62,603.87 L209.39,609.41 L210.48,614.75 L211.87,619.89 L213.57,624.83 L215.58,629.57 L217.91,634.11 L220.54,638.44 L223.48,642.57 L226.73,646.50 L230.29,650.23 L234.14,653.71 L238.25,656.88 L242.63,659.75 L247.28,662.32 L252.19,664.59 L257.37,666.56 L262.82,668.22 L268.53,669.58 L274.51,670.64 L280.75,671.40 L287.26,671.85 L294.04,672.00 L299.07,671.91 L303.96,671.63 L308.72,671.17 L313.33,670.53 L317.81,669.71 L322.16,668.70 L326.37,667.50 L330.44,666.13 L334.37,664.57 L338.17,662.82 L341.83,660.89 L345.35,658.78 L348.71,656.49 L351.88,654.01 L354.85,651.35 L357.62,648.50 L360.20,645.47 L362.59,642.26 L364.78,638.87 L366.78,635.29 L368.58,631.52 L370.19,627.58 L371.60,623.45 L372.82,619.13 L375.93,619.13 L376.52,622.61 L377.24,625.98 L378.09,629.22 L379.07,632.35 L380.19,635.36 L381.44,638.24 L382.83,641.01 L384.34,643.67 L385.99,646.20 L387.78,648.61 L389.69,650.91 L391.74,653.08 L393.92,655.11 L396.23,656.96 L398.66,658.64 L401.22,660.14 L403.90,661.46 L406.71,662.61 L409.64,663.58 L412.71,664.37 L415.89,664.99 L419.21,665.43 L422.65,665.69 L426.21,665.78 L463.01,665.78 L463.01,612.91 Z M475.77,630.42 L546.23,713.58 L762.31,530.50 L546.23,347.42 L475.77,430.58 L593.69,530.50 L475.77,630.42 Z M667.77,630.42 L738.23,713.58 L954.31,530.50 L738.23,347.42 L667.77,430.58 L785.69,530.50 L667.77,630.42 Z\"/></svg>"},"/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"<!doctype html>\n<html lang=\"zh-Hant\">\n<head>\n<meta charset=\"utf-8\">\n<title>Arcrun RAG</title>\n<!--\n 根目錄直接導向搜尋 Portal。\n\n 為什麼不做「選擇介面」的導覽頁(2026-07-21 leo 實際撞到):\n 這份 UI 部署出去的網址是給**使用者**的入口(個人站 mira.uncle6.me,\n 以及自架用戶自己的網址),進站就是要能用——多一層選擇=多一個困惑點,\n (2026-08-08 更正:原註解寫「這個網域=rag-demo.arcrun.dev 是客戶測試入口」,\n 那是 uncle6 帳號那個已廢的 demo 站,leo 已定案不再拿它當範例;\n 註解留著會把下一個人導向錯的環境,故改寫。理由本身仍然成立。)\n 而且會讓客戶看到 Admin Console 這個維運介面(不該對客戶露出)。\n\n 維運者要進 console 直接打 /console/ 即可。\n-->\n<meta http-equiv=\"refresh\" content=\"0; url=/portal/\">\n<link rel=\"canonical\" href=\"/portal/\">\n<script>location.replace('/portal/');</script>\n<style>\n body{margin:0;min-height:100vh;display:grid;place-items:center;\n background:#faf8f5;color:#6b635a;\n font:15px/1.7 system-ui,-apple-system,\"Noto Sans TC\",sans-serif}\n @media(prefers-color-scheme:dark){body{background:#1a1816;color:#9a9186}}\n</style>\n</head>\n<body>\n <p>正在前往搜尋頁… <a href=\"/portal/\">沒有自動跳轉請點這裡</a></p>\n</body>\n</html>\n"},"/portal/app-mount.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// App 掛載協定 v0.2 的結構自測(Arcrun#152#82leo 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-<name> .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 // 抓 `<div ... id=\"v-<name>\">` 之後、下一個 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/folder-tree.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// 資料夾樹的畫面計算(InkStoneCo#44)——守 `gapWhy` 與 `rollupTree` 之間的**介面契約**。\n//\n// 🔴 這支測試存在的唯一理由,是一個 2026-08-17 真的送到瀏覽器才被抓到的 bug\n//\n// `rollupTree()` 疊出來的合計物件用短鍵名 { total, synced, pending, unsupported, excluded }\n// 而 `gapWhy()` 當時讀的是節點的長鍵名 { total_files, unsupported_files, … }\n//\n// ⇒ 三個判斷全部拿到 undefined ⇒ **那行「為什麼沒上傳」永遠是空字串**。\n//\n// 症狀有多難發現:畫面完全正常——樹畫得出來、每個數字都對、也沒有任何 console 錯誤,\n// **只是那句解釋安靜地不見了**。而那句解釋正是 leo 要這個畫面回答的唯一那件事:\n// 「不上傳通常是不支援,比如程式碼、不支援的格式。」\n// 單元測試(雲端那半)全綠、`curl` 也看不出來(它不執行 JS)。\n//\n// ⇒ 所以這裡測的不是「gapWhy 會不會算數」,而是**它跟呼叫端講不講同一種話**。\n// 以後誰改了任一邊的鍵名,這支就會紅。\n\nimport fs from 'node:fs';\n\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\n\n// 抽出 gapWhy + rollupTree 兩支(錨定「函式結束」這個結構,不綁文案——\n// 同 os-split.test.mjs 2026-08-05 的教訓:綁文案會讓改字就炸)。\nconst start = html.indexOf('function gapWhy');\nconst rollupAt = html.indexOf('function rollupTree', start);\nconst endMark = 'return { sums: sums, kids: kids };';\nconst end = rollupAt < 0 ? -1 : html.indexOf(endMark, rollupAt) + endMark.length + '\\n }'.length;\nif (start < 0 || rollupAt < 0 || end < start) throw new Error('抽不到函式區塊');\nconst src = html.slice(start, end);\n\nconst api = new Function(src + '; return { gapWhy: gapWhy, rollupTree: rollupTree };')();\n\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// 小幫手真的會送上來的形狀(長鍵名),刻意混著支援/不支援/整棵被剪掉的目錄。\nconst nodes = [\n { path: '', name: 'kb', parent: '-', depth: 0, total_files: 2, synced_files: 1, pending_files: 1, unsupported_files: 0, excluded_files: 0 },\n { path: 'docs', name: 'docs', parent: '', depth: 1, total_files: 5, synced_files: 2, pending_files: 1, unsupported_files: 2, excluded_files: 0 },\n { path: 'docs/img', name: 'img', parent: 'docs', depth: 2, total_files: 4, synced_files: 0, pending_files: 0, unsupported_files: 4, excluded_files: 0 },\n { path: 'src', name: 'src', parent: '', depth: 1, total_files: 4, synced_files: 0, pending_files: 0, unsupported_files: 0, excluded_files: 4 },\n { path: 'node_modules', name: 'node_modules', parent: '', depth: 1, total_files: 0, synced_files: 0, pending_files: 0, unsupported_files: 0, excluded_files: 0, skipped: true, skip_reason: '這是工具產生的' },\n];\n\nconst r = api.rollupTree(nodes);\n\n// ① 子樹合計:根要把 docs/img/src 全部疊進來(skipped 的那棵不算——沒走進去就是不知道)\nconst root = r.sums[''];\nchk('根的合計=2+5+4+4skipped 的 node_modules 不計)', root.total === 15, JSON.stringify(root));\nchk('根的已同步=1+2', root.synced === 3, JSON.stringify(root));\n\n// ② 🔴 本檔的重點:gapWhy 吃得下 rollupTree 吐出來的東西\nconst why = api.gapWhy(root);\nchk('gapWhy(合計) 不是空字串(鍵名對得上)', why.length > 0, JSON.stringify(why));\nchk('gapWhy 講得出不支援的份數', why.includes('6 份'), why); // 2(docs) + 4(img)\nchk('gapWhy 講得出不收的份數', why.includes('4 份'), why); // src\nchk('gapWhy 講得出處理中的份數', why.includes('2 份'), why); // 根 1 + docs 1\n\n// ③ 差額必須解釋得了(leo 的規格:兩個數字不相等是正常的,但差額要說得出來)\nconst gap = root.total - root.synced;\nchk('差額 == 不支援 + 不收 + 處理中', gap === root.unsupported + root.excluded + root.pending,\n `gap=${gap} unsup=${root.unsupported} excl=${root.excluded} pend=${root.pending}`);\n\n// ④ 完全同步的資料夾不該多嘴\nchk('沒有差額時 gapWhy 回空字串',\n api.gapWhy({ total: 3, synced: 3, pending: 0, unsupported: 0, excluded: 0 }) === '');\n\nconsole.log(`\\n${pass} passed, ${fail} failed`);\nprocess.exit(fail ? 1 : 0);\n"},"/portal/index.html":{"type":"text/html; charset=utf-8","b64":false,"data":"<!doctype html>\n<html lang=\"zh-Hant\">\n<head>\n<meta charset=\"utf-8\"/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Arcrun Portal</title>\n<link rel=\"icon\" href=\"/favicon.svg\" type=\"image/svg+xml\">\n<link rel=\"alternate icon\" href=\"/favicon.ico\" sizes=\"16x16 32x32 48x48\">\n<link rel=\"apple-touch-icon\" href=\"/apple-touch-icon.png\">\n<script>\n // 主題預載(防閃色):預設淺色,選擇存 localStorage(與 console 同設計語言、不同 key\n document.documentElement.setAttribute('data-theme', (function () {\n try { return localStorage.getItem('arcrun_portal_theme') === 'dark' ? 'dark' : 'light'; } catch (e) { return 'light'; }\n })());\n</script>\n<!-- id=\"portal-cis\"Arcrun#152——App 掛進 Shadow DOM 之後,這份表會被複製一份進\n 每個 App 的 shadow rootleo 2026-08-24:「App 應該先採用整體的 styles」)。\n 要複製就得指得到它 ⇒ 這個 id 是契約的一部分,改名前先看 mountAppUi()。 -->\n<style id=\"portal-cis\">\n * { box-sizing: border-box; }\n /* ── 主題色板(2026-08-01 CIS 第三輪:leo 親驗「Portal 裡的色彩都沒改」——\n 舊「宣紙米白紙紋+墨字+琥珀金」整套換成 Arcrun CIS token。\n Ink #17181APaper #FDFCFBCanvas #F2F1EDMuted #9A978F\n Relation #B04A2F(唯一強調色,≤5% 螢幕)/Relation-dark #D9784F(暗色底用)。\n ── */\n :root {\n --paper-a: #FDFCFB; --paper-b: #F2F1ED;\n --ink: #17181A; --ink-rgb: 23,24,26;\n --amber: #B04A2F; --amber-rgb: 176,74,47;\n --ok: #1d7a48; --ok-rgb: 29,122,72; --ok-soft: #1d6b40;\n --err: #b03a26; --err-rgb: 176,58,38;\n --well: rgba(23,24,26,.06); --well2: rgba(23,24,26,.08);\n --bar-bg: #F2F1ED; --track: rgba(23,24,26,.12); --knob: #FDFCFB;\n }\n :root[data-theme=\"dark\"] {\n --paper-a: #1b1c1e; --paper-b: #1e1f21;\n --ink: #FDFCFB; --ink-rgb: 253,252,251;\n --amber: #D9784F; --amber-rgb: 217,120,79;\n --ok: #7fe0a8; --ok-rgb: 63,190,120; --ok-soft: #a8e8c4;\n --err: #e58575; --err-rgb: 217,95,76;\n --well: rgba(0,0,0,.25); --well2: rgba(0,0,0,.3);\n --bar-bg: #17181A; --track: rgba(255,255,255,.08); --knob: #FDFCFB;\n }\n html, body { margin: 0; background: repeating-linear-gradient(0deg,var(--paper-a) 0px,var(--paper-a) 3px,var(--paper-b) 3px,var(--paper-b) 4px); color: var(--ink);\n font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 16px; -webkit-font-smoothing: antialiased; }\n input, textarea, button { font-family: inherit; }\n ::placeholder { color: rgba(var(--ink-rgb),.35); }\n input:focus { outline: 2px solid rgba(var(--amber-rgb),.5); outline-offset: 1px; }\n a { color: var(--amber); }\n .serif { font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; }\n .mono { font-family: ui-monospace, Menlo, monospace; }\n .muted { color: rgba(var(--ink-rgb),.55); }\n .dim { color: rgba(var(--ink-rgb),.4); }\n .err { color: var(--err); }\n .ok { color: var(--ok); }\n\n /* ── auth 全屏(.view.on 的 block 會蓋 flex → 補 .authwrap.view.onconsole 同修)── */\n .authwrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }\n .authbox { width: 100%; max-width: 400px; display: flex; flex-direction: column; gap: 24px; text-align: center; }\n /* 08-01 緊急修正:登入頁品牌 lockup 改用官方 PNG(自產 SVG outline 字腔缺失作廢,\n 見 arcrun-cis/README.md 說明)。mark 不上色(NEVER 用 --amberRelation\n 當 logo 色——CISRelation 是「唯一強調色」不是品牌色,logo 上色是一句謊話),\n 靠兩張圖(-ink-paper-on-ink)依 data-theme 切換,不用 filter 避免色偏。\n 08-01 二次修正:官方原始 PNG1840x560)畫布只有 32% 高度是實際字形(上下大留白),\n height:44px 時實際字高僅約 14px,看起來過小。改用裁淨版(493x88,長寬比 5.604:1,\n 四邊已裁到字腔邊緣無留白),此時 height:44px=實際字高 44px。 */\n .brand { display: flex; justify-content: center; }\n .brand img { height: clamp(32px, 8.5vw, 48px); width: auto; display: block; max-width: 100%; }\n .brand .wm-paper { display: none; }\n :root[data-theme=\"dark\"] .brand .wm-ink { display: none; }\n :root[data-theme=\"dark\"] .brand .wm-paper { display: block; }\n .brand-sub { margin-top: 10px; font-size: 15px; color: rgba(var(--ink-rgb),.55); letter-spacing: .1em; }\n\n /* ── 版面骨架 ── */\n #shell { display: none; min-height: 100vh; }\n #shell.on { display: flex; }\n #sidenav { display: none; }\n main { flex: 1; min-width: 0; padding-bottom: 96px; }\n .page { max-width: 980px; margin: 0 auto; padding: 0 20px 40px; }\n .page.narrow { max-width: 760px; }\n /* Arcrun#152leo 2026-08-24 實測第三件):「App 頁的 responsiveness 有問題」。\n 病灶在這一列:flex 不換行 `.t` 是 `white-space:nowrap`min-content =整串長度、\n 且 flex item 預設 `min-width:auto` ⇒ 縮不下去)⇒ 窄寬度時 `.t` 吃光整條線,\n `.m` 被壓到自己的 min-content(中文=一個字寬)⇒ 變成一行一個字的窄柱並溢出畫面右緣。\n 啟動器頁最慘,因為它的 `.t` 是「晚安,<使用者名稱>」——長度由使用者名字決定。\n 實測(375px 視窗、名字 youlin.hsieh.dev):頁首高 219px、`.m` 寬 38px 高 171px、\n 右緣 394px > 視窗寬。\n 修法兩件,都在這一列,不動任何 JS:\n ① `flex-wrap: wrap` ⇒ 擠不下時 `.m` 整段掉到第二行(拿到整條寬度),不再被壓成窄柱;\n ② `.t` 在窄螢幕解除 nowrap `min-width: 0` ⇒ 名字再長也是換行,不會頂破畫面。\n 桌面寬度兩件都不觸發(兩段本來就排得下),所以維持原本的左右對齊一行。 */\n .pagehead { padding: 22px 2px 14px; border-bottom: 2px solid rgba(var(--amber-rgb),.4); display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 2px 10px; }\n .pagehead .t { font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 23px; letter-spacing: .2em; white-space: nowrap; min-width: 0; }\n .pagehead .m { font-size: 13.5px; color: rgba(var(--ink-rgb),.45); min-width: 0; }\n @media (max-width: 700px) { .pagehead .t { white-space: normal; overflow-wrap: anywhere; } }\n /* Arcrun#152leo 2026-08-24:「所有的 App 都要顯示,不然他要怎麼回 home?」):\n 出口是**協定層**的東西——凡不是首頁的 view,頁首第一格一律是同一顆「← 返回」,\n 由 ensureBackControls() 統一塞(見該函式)。這裡只負責它的長相與位置:\n `.t { margin-right:auto }` 讓標題緊跟在返回鍵右邊、說明 `.m` 仍然靠右,\n 所以**首頁(沒有返回鍵)的排版跟以前一模一樣**。 */\n .pagehead .t { margin-right: auto; }\n .backbtn { flex: none; padding: 7px 12px; font-size: 13.5px; align-self: center; }\n /* Arcrun#152leo 2026-08-24 拍板):「全局要給它多大的畫布也是全局決定的⋯⋯\n 它呈現白底的一個黑框表示那是系統給它的範圍?**應該整個右側邊欄都是它的**」。\n ⇒ App 的畫布=整個右側內容區:`.page` 的 980px 上限與左右留白在這一頁全部拿掉,\n 只有頁首那一列自己補回留白(頁首是 Portal 的東西,不是 App 的畫布)。\n 🔴 兩種樣式模式(inherit/own)都吃這一段——畫布不是 App 能決定的事。 */\n #v-app.on { display: flex; flex-direction: column; max-width: none; padding: 0; min-height: calc(100vh - 96px); }\n /* 22px 不是 20px`.page` 的 20px `.pagehead` 自己的 2px。返回鍵要跟其他頁**同一個\n x 座標**(實測過的:其他頁 22、這頁若寫 20 就會差 2px)——「位置固定」是量出來的。 */\n #v-app > .pagehead { padding-left: 22px; padding-right: 22px; }\n #app-body { flex: 1; display: flex; flex-direction: column; min-width: 0; }\n /* 沒有自帶畫面的 App 走系統預設畫面(工作流清單),那是 Portal 自己的內容 ⇒ 補回\n `.page` 原本的左右留白。只有 App 的畫布(`#app-ui-mount`)才是滿版的。 */\n #app-body > :not(#app-ui-mount) { margin-left: 20px; margin-right: 20px; }\n /* shadow host:寬度吃滿、高度撐滿剩下的地方。App 自己在 shadow 裡寫什麼都改不了這一段\n (外部文件的規則對 host 元素勝過 shadow 內的 `:host` 規則)⇒ 畫布真的由全局說了算。 */\n #app-ui-mount { flex: 1; display: block; width: 100%; min-width: 0; }\n @media (min-width: 1024px) { #v-app.on { min-height: calc(100vh - 20px); } }\n #tabbar { position: fixed; left: 0; right: 0; bottom: 0; background: var(--bar-bg); border-top: 2px solid rgba(var(--amber-rgb),.4); display: none; z-index: 50; padding-bottom: env(safe-area-inset-bottom); }\n #tabbar.on { display: flex; }\n #tabbar .tab { flex: 1; text-align: center; padding: 15px 0 13px; font-size: 14.5px; cursor: pointer; min-height: 44px; color: rgba(var(--ink-rgb),.5); border-top: 2px solid transparent; margin-top: -2px; }\n #tabbar .tab.on { color: var(--amber); font-weight: 600; border-top-color: var(--amber); }\n #tabbar .tab.hide { display: none; }\n @media (min-width: 1024px) {\n #tabbar.on { display: none; }\n main { padding-bottom: 20px; }\n #sidenav { display: flex; width: 216px; flex: none; position: sticky; top: 0; height: 100vh; border-right: 2px solid rgba(var(--amber-rgb),.35); padding: 28px 0; flex-direction: column; gap: 4px; }\n #sidenav .logo { display: flex; color: var(--ink); padding: 0 32px 20px; }\n #sidenav .logo img { height: clamp(22px, 5vw, 28px); width: auto; display: block; max-width: 100%; }\n #sidenav .logo .wm-paper { display: none; }\n :root[data-theme=\"dark\"] #sidenav .logo .wm-ink { display: none; }\n :root[data-theme=\"dark\"] #sidenav .logo .wm-paper { display: block; }\n #sidenav .nav { padding: 13px 24px; font-size: 16px; cursor: pointer; letter-spacing: .08em; color: rgba(var(--ink-rgb),.6); border-right: 2px solid transparent; }\n #sidenav .nav.on { color: var(--amber); background: rgba(var(--amber-rgb),.08); border-right-color: var(--amber); font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-weight: 600; }\n #sidenav .nav.hide { display: none; }\n #sidenav .foot { margin-top: auto; padding: 0 24px; font-size: 12.5px; color: rgba(var(--ink-rgb),.35); line-height: 1.7; }\n }\n .view { display: none; }\n .view.on { display: block; }\n .authwrap.view.on { display: flex; }\n\n /* ── 元件 ── */\n .btn { padding: 13px; font-size: 16px; font-weight: 600; border-radius: 10px; border: none; background: var(--amber); color: #FDFCFB; cursor: pointer; }\n .btn:disabled { opacity: .45; cursor: default; }\n .btn3 { padding: 11px 16px; font-size: 15px; border-radius: 10px; border: 1px solid rgba(var(--ink-rgb),.25); background: none; color: rgba(var(--ink-rgb),.7); cursor: pointer; }\n .txt { width: 100%; padding: 13px 15px; font-size: 16px; border-radius: 10px; border: 1px solid rgba(var(--ink-rgb),.2); background: rgba(var(--ink-rgb),.05); color: var(--ink); }\n .panel { padding: 18px; border-radius: 13px; background: rgba(var(--ink-rgb),.04); border: 1px solid rgba(var(--ink-rgb),.12); }\n .tag { display: inline-block; padding: 3px 9px; border-radius: 5px; background: rgba(var(--amber-rgb),.12); color: var(--amber); font-size: 12.5px; }\n .tag.green { background: rgba(var(--ok-rgb),.12); color: var(--ok); }\n .tag.dim { background: rgba(var(--ink-rgb),.08); color: rgba(var(--ink-rgb),.55); }\n .honest { padding: 22px; border-radius: 13px; border: 1px dashed rgba(var(--amber-rgb),.4); background: rgba(var(--amber-rgb),.06); text-align: center; }\n .honest .h { font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 19px; color: var(--amber); margin-bottom: 8px; }\n .honest .b { font-size: 15px; color: rgba(var(--ink-rgb),.6); line-height: 1.7; }\n #toast { position: fixed; left: 50%; bottom: 110px; transform: translateX(-50%); z-index: 99; padding: 13px 22px; border-radius: 11px; background: var(--amber); color: #FDFCFB; font-size: 15.5px; font-weight: 600; box-shadow: 0 6px 24px rgba(0,0,0,.5); white-space: nowrap; display: none; }\n\n /* ── AI 檢索過程(Arcrun#44leo「要看到從 graph 找到幾個子庫、讀到的 index 敘事、\n 引用的原文位置——不是簡單向量搜尋的結果」)。一眼掃完優先:路徑徽章+非向量徽章\n 常駐可見,細節(命中子庫/索引敘事/原文片段)用原生 <details> 收合,想細看再展開。 */\n .tracehead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 14px; }\n .trace { margin-top: 10px; border-radius: 13px; border: 1px solid rgba(var(--ink-rgb),.12); background: rgba(var(--ink-rgb),.03); }\n .trace > summary { cursor: pointer; padding: 10px 14px; font-size: 13.5px; color: rgba(var(--ink-rgb),.6); user-select: none; }\n .trace-body { padding: 2px 14px 14px; border-top: 1px solid rgba(var(--ink-rgb),.08); margin-top: 2px; }\n /* ── 🌲 檔案總管式的樹(`.ftree`)───────────────────────────────────────────────\n leo 2026-08-19(驗 1.4.49 後打回 markmap):\n 「我的需求是**向右向下**,類似 terminal 的 tree,一列一列向下往後退縮,\n 緊湊但可點擊展開,**模擬 Windows 的檔案總管的 tree**⋯⋯上下間距很大、\n 展開字很小⋯⋯用戶要擁有觸控板,否則很難閱讀。」\n ⇒ 一列 = 一個節點,往下排、往右縮排;沒有畫布、沒有縮放、沒有平移。\n 摺疊引擎=瀏覽器原生的 <details>/<summary>(見 renderTree 的技術選型註解)。\n\n 🔴 密度是這個畫面的規格,不是品味:一列 ≈ 23px(Windows 檔案總管 100% 是 2224px),\n 字級 13.5px(≈ Explorer 的 12pt)。34 列(8 子庫 × 17 片原文的真實形狀)≈ 790px,\n 1080p 桌機不捲動看得完。改這幾個數字前先回去算那筆帳。 */\n .ftree { margin-top: 8px; padding: 7px 8px; border-radius: 11px; background: var(--well);\n border: 1px solid rgba(var(--ink-rgb),.1);\n font: 400 13.5px/1.35 -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif;\n color: var(--ink); }\n /* 縮排靠巢狀(不是靠算 padding),所以層數沒有上限;那條 border-left 就是\n 檔案總管的「導引線」,一路指回父資料夾。 */\n .ftree .ft-kids { margin-left: 8px; padding-left: 10px; border-left: 1px solid rgba(var(--ink-rgb),.14); }\n .ftree .ft-row { display: flex; align-items: center; gap: 5px; padding: 2.5px 5px; border-radius: 5px;\n white-space: nowrap; overflow: hidden; }\n .ftree .ft-row:hover { background: rgba(var(--ink-rgb),.06); }\n /* summary 預設帶一個系統三角形,且是 list-item——兩邊都要關掉才輪得到我們自己的箭頭 */\n .ftree summary.ft-row { cursor: pointer; list-style: none; user-select: none; }\n .ftree summary.ft-row::-webkit-details-marker { display: none; }\n .ftree summary.ft-row::marker { content: \"\"; }\n .ftree .ft-tw { flex: none; width: 12px; text-align: center; font-size: 9px;\n color: rgba(var(--ink-rgb),.45); transition: transform .12s ease; }\n .ftree details[open] > summary > .ft-tw { transform: rotate(90deg); }\n /* 葉子沒有箭頭,但要佔掉同樣的寬度,不然同一層的名字會左右錯開 */\n .ftree .ft-leaf > .ft-tw { visibility: hidden; }\n /* 一列放不下就用「…」截斷(=檔案總管對長檔名的做法),全文掛在 title 上,滑鼠停著就看得到 */\n /* flex-grow 是 0:名字後面若掛著操作鈕(.ft-tail),鈕要**貼著名字**,不要被推到\n 1920px 寬的另一端——那等於叫使用者把滑鼠橫越整個螢幕。縮的時候照樣截斷(flex-shrink 1)。 */\n .ftree .ft-lbl { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n /* 行尾那一格(庫目錄的「移除」鈕)——名字被截斷時它不能跟著消失,所以放在 .ft-lbl 外面。\n 它長在 <summary> 裡,點它會連帶把那一列摺疊起來 ⇒ 事件處理端要 preventDefault(見 #v-admin 的委派)。 */\n .ftree .ft-tail { flex: none; display: flex; align-items: center; gap: 6px; margin-left: 8px; }\n /* 🔴 這顆鈕的高度不准把一列撐高:一列的內容區是 13.5px × 1.35 ≈ 18.2px\n 所以 line-height 16 上下框線 1px 18px,剛好塞得下(不然 23px 的密度就破功了)。 */\n .ftree .ft-tail .btn3 { box-sizing: border-box; padding: 0 8px; font-size: 11.5px; line-height: 16px;\n border-radius: 6px; border: 1px solid rgba(var(--err-rgb),.4);\n background: rgba(var(--err-rgb),.05); color: rgba(var(--err-rgb),.85); }\n .ftree .ft-sep { color: rgba(var(--ink-rgb),.28); padding: 0 1px; }\n .ftree .tt-sub { color: rgba(var(--ink-rgb),.55); font-size: 12px; }\n .ftree .tt-path { font-family: ui-monospace, Menlo, monospace; color: rgba(var(--amber-rgb),.85); font-size: 11.5px; }\n .ftree .tt-dim { color: rgba(var(--ink-rgb),.45); }\n /* 異質節點的樣式語彙(D97:三個入口共用同一份 render,節點種類靠這幾個 class 分)。\n 同步庫總覽的四層長這樣:總庫 → 機器 → 資料夾 → 子資料夾/檔案,\n 機器與資料夾必須一眼分得開,所以給不同顏色與前綴圖示。 */\n .ftree .tt-machine { color: var(--amber); font-weight: 600; }\n .ftree .tt-machine::before { content: \"🖥 \"; }\n .ftree .tt-folder::before { content: \"📁 \"; }\n .ftree .tt-file::before { content: \"📄 \"; }\n /* 拿不到值時的節點:明說「未知」,不要拿別的東西頂替(機器那層現在就是這個狀態) */\n .ftree .tt-unknown { color: rgba(var(--err-rgb),.75); border-bottom: 1px dashed rgba(var(--err-rgb),.5); }\n .ftree .tt-count { color: rgba(var(--ink-rgb),.5); font-size: 12px; }\n /* 「份文件」三個字換成圖示(leo 2026-08-28:「GUI 是國際通用,圖形指示,不限語文」)。\n 一棵樹裡每一列都寫一次「份文件」=同一個詞重複幾十次,而 📄 不必翻譯。\n 🔴 前綴掛在 .tt-docs 上、不掛 .tt-count:同一列還有別種數字(資料夾數、\n 還沒回報數),它們各有各的圖示,不該一律被加上 📄。 */\n .ftree .tt-docs::before { content: \"📄 \"; }\n /* 「這一層的檔案這次沒收」=一個記號,不是一句話(inkstone/Arcrun#180leo 2026-08-28\n 「用一個 emoticon 就好了,這段太囉嗦」——同一句理由在一棵樹裡重複了 8 次)。\n 虛線底+cursor:help 就是「滑過去有東西」的把手;理由住在那一格彈出視窗裡。 */\n .ftree .tt-skip { color: rgba(var(--ink-rgb),.42); font-size: 12px; cursor: help;\n border-bottom: 1px dotted rgba(var(--ink-rgb),.3); }\n /* ── 膠囊 滑過去才出現的那一格(inkstone/Arcrun#180leo 2026-08-28)──────\n leo 原話:「每個資料夾的說明又長又囉嗦,**只要符號加數字**⋯⋯\n **只要「文件」即可,其他的可以放在一個 moseover 彈出視窗裡**」。\n 🔴 「收起來」不等於「刪掉」:判定語仍在彈出視窗裡(arcrun-rag#136 那條線要拿它\n 來救被跳過的資料夾),只是不再佔滿每一列。 */\n .ftree .tt-pill { display: inline-block; padding: 0 6px; border-radius: 999px; font-size: 11px;\n line-height: 16px; background: rgba(var(--ink-rgb),.08); color: rgba(var(--ink-rgb),.7); }\n .ftree .tt-pill.is-type { background: rgba(var(--amber-rgb),.12); color: rgba(var(--amber-rgb),.95); }\n .ftree .ft-hint { color: rgba(var(--ink-rgb),.3); font-size: 11px; }\n /* 🔴 position:fixed 是必要的,不是隨手選的:.ft-row 與 .ft-lbl 都是 overflow:hidden\n (名字太長要截斷成「…」),彈出視窗長在裡面會被切掉 ⇒ 掛在 <body> 底下、\n 由 JS 依照那一列的位置擺(見 ftPopShow)。 */\n /* 🔴 底色用 --paper-a(不透明的紙色),**不是 --well**--well 是半透明的疊色\n `rgba(23,24,26,.06)`),貼在頁面上很好看,但浮在別的字上面就會透出下面那些字\n ——2026-08-28 實測截圖就是這樣,整個彈出視窗看起來像壞掉。 */\n .ft-pop { position: fixed; z-index: 90; max-width: 380px; padding: 9px 11px; border-radius: 10px;\n background: var(--paper-a); border: 1px solid rgba(var(--ink-rgb),.18);\n box-shadow: 0 6px 20px rgba(var(--ink-rgb),.16); pointer-events: none;\n font: 400 12.5px/1.55 -apple-system, \"PingFang TC\", \"Noto Sans TC\", system-ui, sans-serif;\n color: var(--ink); white-space: normal; }\n .ft-pop .pop-t { font-weight: 600; font-size: 13px; }\n .ft-pop .pop-n { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 5px; }\n .ft-pop .pop-why { margin-top: 6px; color: rgba(var(--ink-rgb),.6); font-size: 12px; }\n .ft-pop .pop-path { margin-top: 4px; font-family: ui-monospace, Menlo, monospace; font-size: 11px;\n color: rgba(var(--amber-rgb),.85); word-break: break-all; }\n .ft-pop .tt-pill { display: inline-block; padding: 0 7px; border-radius: 999px; font-size: 11.5px;\n line-height: 17px; background: rgba(var(--ink-rgb),.08); color: rgba(var(--ink-rgb),.72); }\n .ft-pop .tt-pill.is-type { background: rgba(var(--amber-rgb),.12); color: rgba(var(--amber-rgb),.95); }\n .ft-pop .tt-pill.is-warn { background: rgba(var(--err-rgb),.1); color: rgba(var(--err-rgb),.85); }\n .ftree .ft-foot { padding: 7px 5px 1px; font-size: 12.5px; color: rgba(var(--ink-rgb),.45); line-height: 1.6;\n white-space: normal; }\n /* 出處麵包屑(leo 2026-08-18:「應該是讓他可以去本機資料夾找到原文⋯⋯帶上原機器的編號、\n 資料夾名稱、檔名的麵包屑⋯⋯不用再開搜尋框了」)*/\n .crumbrow { padding: 11px 13px; border-radius: 11px; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.1); }\n .crumbs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 14.5px; }\n .crumbs .sep { color: rgba(var(--ink-rgb),.3); }\n .crumbs .crumb { color: rgba(var(--ink-rgb),.7); }\n .crumbs .crumb.file { font-weight: 600; color: var(--ink); word-break: break-all; }\n .crumbs .crumb.unknown { color: rgba(var(--err-rgb),.75); border-bottom: 1px dashed rgba(var(--err-rgb),.5); cursor: help; }\n .crumbfoot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 7px; }\n .crumbfoot .p { font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; color: rgba(var(--ink-rgb),.55); word-break: break-all; }\n .btn3 { flex: none; padding: 5px 11px; font-size: 12.5px; border-radius: 8px; cursor: pointer; border: 1px solid rgba(var(--amber-rgb),.45); background: rgba(var(--amber-rgb),.08); color: var(--amber); }\n\n /* ── 搜尋 ── */\n .searchrow { display: flex; gap: 10px; align-items: stretch; }\n .searchrow input { flex: 1; min-width: 0; padding: 16px 18px; font-size: 18px; border-radius: 13px; border: 1.5px solid rgba(var(--amber-rgb),.35); background: rgba(var(--ink-rgb),.05); color: var(--ink); }\n .modebar { display: flex; gap: 8px; margin-top: 12px; }\n .modebtn { flex: none; padding: 9px 18px; font-size: 14.5px; border-radius: 999px; cursor: pointer; border: 1px solid rgba(var(--ink-rgb),.18); background: none; color: rgba(var(--ink-rgb),.6); }\n .modebtn.on { border-color: rgba(var(--amber-rgb),.6); background: rgba(var(--amber-rgb),.14); color: var(--amber); font-weight: 600; }\n .modebtn.hide { display: none; }\n /* 🔴 2026-08-10 實測撞到:`.hide` 一直**只有上面那三條有 scope 的規則**#tabbar .tab / #sidenav .nav / .modebtn),\n 所以任何其他元素掛上 class=\"hide\" 都**完全沒有被藏起來**——看起來有藏、其實沒藏。\n D62 的「忘記密碼」欄位與「現有密碼」欄位就是這樣在瀏覽器上原形畢露的。\n 補一條通用規則;帶 !important 是因為要蓋過 inline 的 display#forgot-box 就有)。\n 既有那三種用法的意圖與本規則一致(都是「藏起來」),加這條不改變它們的行為。 */\n .hide { display: none !important; }\n .kgrid { display: grid; grid-template-columns: 1fr; gap: 12px; padding-bottom: 8px; }\n @media (min-width: 700px) { .kgrid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); } }\n .kcard { padding: 18px; border-radius: 13px; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.1); cursor: pointer; display: flex; flex-direction: column; gap: 9px; }\n .kcard:hover { border-color: rgba(var(--amber-rgb),.45); background: rgba(var(--amber-rgb),.06); }\n .kcard .kt { font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 18.5px; font-weight: 600; line-height: 1.45; word-break: break-word; }\n .kcard .ks { font-size: 15px; color: rgba(var(--ink-rgb),.65); line-height: 1.65; word-break: break-word; }\n .kcard .km { display: flex; gap: 10px; align-items: center; margin-top: auto; font-size: 13px; flex-wrap: wrap; }\n\n /* ── 卡片詳頁 ── */\n .cardtitle { font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 28px; font-weight: 600; line-height: 1.4; word-break: break-word; }\n .md { margin-top: 18px; }\n .md p { font-size: 16.5px; line-height: 1.85; color: rgba(var(--ink-rgb),.82); margin: 8px 0; word-break: break-word; }\n .md h1, .md h2, .md h3, .md h4 { font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-weight: 600; color: var(--amber); margin: 18px 0 6px; line-height: 1.4; }\n .md h1 { font-size: 22px; } .md h2 { font-size: 20px; } .md h3 { font-size: 18px; } .md h4 { font-size: 17px; }\n .md ul, .md ol { margin: 8px 0; padding-left: 24px; }\n .md li { font-size: 16.5px; line-height: 1.8; color: rgba(var(--ink-rgb),.82); margin: 4px 0; word-break: break-word; }\n .md blockquote { margin: 10px 0; padding: 12px 18px; border-left: 3px solid rgba(var(--amber-rgb),.5); font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 17px; line-height: 1.8; color: rgba(var(--ink-rgb),.7); }\n .md code { font-family: ui-monospace, Menlo, monospace; font-size: 14.5px; background: var(--well2); border-radius: 4px; padding: 1px 6px; word-break: break-all; }\n .md pre { background: var(--well2); border: 1px solid rgba(var(--ink-rgb),.1); border-radius: 10px; padding: 14px; overflow-x: auto; }\n .md pre code { background: none; padding: 0; }\n .srcline { margin-top: 24px; display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 11px; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.12); }\n\n /* ── graph 模式(console 關聯視圖同款)── */\n .graphbox { position: relative; height: 340px; border-radius: 14px; background: var(--well); border: 1px solid rgba(var(--ink-rgb),.1); overflow: hidden; }\n .gnode { position: absolute; transform: translate(-50%,-50%); max-width: 96px; padding: 8px 11px; border-radius: 999px; text-align: center; font-size: 12.5px; line-height: 1.3; cursor: pointer; word-break: break-word; background: rgba(var(--ok-rgb),.1); border: 1px solid rgba(var(--ok-rgb),.35); color: var(--ok-soft); }\n .gcenter { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: 104px; height: 104px; border-radius: 50%; background: radial-gradient(circle at 36% 30%,rgba(217,120,79,.95),rgba(176,74,47,.9) 55%,rgba(120,45,25,.95)); display: grid; place-items: center; text-align: center; padding: 8px; box-shadow: 0 0 30px rgba(var(--amber-rgb),.3); }\n .gcenter span { font-family: -apple-system,\"PingFang TC\",\"Microsoft JhengHei\",system-ui,sans-serif; font-size: 13.5px; font-weight: 600; line-height: 1.35; color: #FDFCFB; word-break: break-word; }\n .nbrow { display: flex; align-items: center; gap: 11px; padding: 12px 14px; border-radius: 10px; cursor: pointer; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.1); font-size: 15.5px; }\n /* 總圖(Arcrun#39 GUI 切片):沿用紙感語言——green 章印節點、amber 高度數節點 */\n .mapbox { position: relative; border-radius: 14px; background: var(--well); border: 1px solid rgba(var(--ink-rgb),.1); overflow: hidden; }\n .mapbox svg { display: block; width: 100%; height: auto; }\n .mapnode { cursor: pointer; }\n .mapnode circle { fill: rgba(var(--ok-rgb),.12); stroke: rgba(var(--ok-rgb),.45); stroke-width: 1.4; transition: stroke-width .15s; }\n .mapnode.hub circle { fill: rgba(var(--amber-rgb),.2); stroke: rgba(var(--amber-rgb),.65); }\n .mapnode:hover circle { stroke-width: 3; }\n .mapnode text { font-size: 12.5px; fill: var(--ink); pointer-events: none; }\n .mapedge { stroke: rgba(var(--ink-rgb),.18); stroke-width: 1; stroke-dasharray: 4 4; }\n\n /* ── 管理頁(P4)── */\n .sechead { font-family: -apple-system, \"IBM Plex Sans\", \"PingFang TC\", \"Noto Sans TC\", \"Microsoft JhengHei\", system-ui, sans-serif; font-size: 19px; letter-spacing: .14em; color: var(--amber); margin: 26px 0 12px; padding-bottom: 8px; border-bottom: 1px solid rgba(var(--amber-rgb),.3); }\n .formrow { display: flex; gap: 10px; flex-wrap: wrap; }\n .formrow .txt { flex: 1; min-width: 180px; }\n .formrow select { padding: 12px 14px; font-size: 15.5px; border-radius: 10px; border: 1px solid rgba(var(--ink-rgb),.2); background: rgba(var(--ink-rgb),.05); color: var(--ink); }\n .btnrow { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }\n .btn2 { padding: 9px 14px; font-size: 14px; border-radius: 9px; border: 1px solid rgba(var(--amber-rgb),.45); background: rgba(var(--amber-rgb),.08); color: var(--amber); cursor: pointer; }\n .btn2.warn { border-color: rgba(var(--err-rgb),.45); background: rgba(var(--err-rgb),.06); color: var(--err); }\n .btn2:disabled { opacity: .45; cursor: default; }\n .libgrid { display: flex; gap: 8px 14px; flex-wrap: wrap; margin-top: 10px; }\n .libgrid label { display: flex; align-items: center; gap: 7px; font-size: 14.5px; cursor: pointer; padding: 6px 10px; border-radius: 8px; background: rgba(var(--ink-rgb),.05); border: 1px solid rgba(var(--ink-rgb),.12); }\n .libgrid input[type=checkbox] { accent-color: var(--amber); width: 16px; height: 16px; }\n .otpbox { margin-top: 14px; padding: 18px; border-radius: 13px; border: 2px solid rgba(var(--amber-rgb),.55); background: rgba(var(--amber-rgb),.08); }\n .otpbox .pw { font-family: ui-monospace, Menlo, monospace; font-size: 22px; letter-spacing: .12em; color: var(--amber); word-break: break-all; user-select: all; margin: 10px 0; }\n\n /* ── 清單 / 設定 ── */\n .listcol { display: flex; flex-direction: column; gap: 10px; padding: 16px 0 8px; }\n .card-item { border-radius: 13px; background: rgba(var(--ink-rgb),.045); border: 1px solid rgba(var(--ink-rgb),.1); padding: 16px 18px; }\n .setcol { display: flex; flex-direction: column; gap: 14px; padding: 20px 0 40px; }\n .switch { flex: none; margin-left: auto; width: 56px; height: 32px; border-radius: 999px; padding: 3px; background: rgba(var(--ink-rgb),.15); transition: background .25s; }\n .switch.on { background: rgba(var(--ok-rgb),.5); }\n .switch i { display: block; width: 26px; height: 26px; border-radius: 50%; background: var(--knob); box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: transform .25s; }\n .switch.on i { transform: translateX(24px); }\n .kvline { display: flex; justify-content: space-between; gap: 12px; font-size: 15px; margin: 5px 0; }\n\n /* ── App 啟動器外殼(Arcrun#152):#sidenav#tabbar 從「單層清單」升級成\n App 界面/App 市集/AR 設定/App 開發 四段 OS 級導覽。既有頁面(search/map/\n upload/workflows/admin/settings)原地不動——只在頁內加一列 subnav 或收進對應\n 頁簽的清單;沿用既有 #sidenav/#tabbar 開關與既有 700px/1024px 斷點,不另立系統。\n\n Icon 一律**描邊式 inline SVG**`stroke=\"currentColor\"`),不用 emoji。三個理由:\n ① emoji 是各作業系統自帶的彩色點陣字形,Windows/macOSAndroid 各長一套,\n 同一份畫面在不同機器上不一致,設計稿無法被保證;\n ② emoji 吃不到 CIS 的 --ink--amber ⇒ 切深色模式時它不會跟著變色;\n ③ 本 repo 的 CIS 是紙感墨字(見 system-dev/wiki/cis-web-style.md)。\n 尺寸/筆畫全部照 leo 核准的設計稿(Main.dc.htmlLauncher-Desktop.dc.html):\n 導覽 19/21px・stroke-width 1.9,磚塊 28px・stroke-width 1.8。\n ⚠️ 不引入任何 icon 函式庫/CDN——Portal 要內嵌成單檔 worker 出貨。 */\n .nav.osnav, .tab.ostab { display: flex; align-items: center; gap: 12px; }\n .tab.ostab { flex-direction: column; gap: 3px; }\n .osnav .os-ic, .ostab .os-ic { flex: none; display: flex; line-height: 0; }\n /* 顏色不寫死:SVG 用 currentColor,色彩由 #sidenav .nav#tabbar .tab(含 .on 的\n var(--amber))決定 ⇒ 深淺色模式與選中狀態都自動跟著 CIS token 走。 */\n .osnav .os-ic svg { display: block; width: 19px; height: 19px; stroke-width: 1.9; }\n .ostab .os-ic svg { display: block; width: 21px; height: 21px; stroke-width: 1.9; }\n .subnav { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0 4px; }\n .subchip { padding: 8px 16px; border-radius: 999px; font-size: 14px; cursor: pointer; border: 1px solid rgba(var(--ink-rgb),.18); color: rgba(var(--ink-rgb),.6); }\n .subchip.on { border-color: rgba(var(--amber-rgb),.6); background: rgba(var(--amber-rgb),.14); color: var(--amber); font-weight: 600; }\n .launchgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px 12px; padding: 22px 2px 8px; }\n @media (min-width: 700px) { .launchgrid { grid-template-columns: repeat(5, 1fr); gap: 30px 18px; } }\n .apptile { display: flex; flex-direction: column; align-items: center; gap: 8px; cursor: pointer; }\n .apptile .ic { width: 64px; height: 64px; border-radius: 17px; display: flex; align-items: center; justify-content: center; background: var(--well); border: 1px solid rgba(var(--ink-rgb),.1); color: var(--ink); }\n .apptile .ic svg { display: block; width: 28px; height: 28px; stroke-width: 1.8; }\n /* 已安裝 App 的磚塊:底是琥珀漸層,故 icon 用紙色。#FDFCFB 是本檔既有的\n 「紙色壓在琥珀上」慣例(同 .btn),兩個主題下底色都是琥珀,故不需換色。 */\n .apptile.accent .ic { background: linear-gradient(155deg, var(--amber), #7A331F); border: none; box-shadow: 0 6px 16px rgba(var(--amber-rgb),.28); color: #FDFCFB; }\n .apptile.add .ic { background: transparent; border: 1.5px dashed rgba(var(--ink-rgb),.22); color: rgba(var(--ink-rgb),.55); }\n .apptile.add .ic svg { width: 24px; height: 24px; }\n .apptile .lbl { font-size: 12.5px; text-align: center; line-height: 1.3; word-break: break-word; }\n .apptile-ic-sm { flex: none; width: 40px; height: 40px; border-radius: 11px; display: flex; align-items: center; justify-content: center; background: var(--well); color: var(--ink); }\n .apptile-ic-sm svg { display: block; width: 20px; height: 20px; stroke-width: 1.8; }\n .promorow { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 13px; border: 1.5px dashed rgba(var(--amber-rgb),.4); background: rgba(var(--amber-rgb),.06); cursor: pointer; }\n .promorow .ic { flex: none; width: 38px; height: 38px; border-radius: 10px; background: var(--amber); color: #FDFCFB; display: flex; align-items: center; justify-content: center; font-size: 17px; }\n</style>\n<script src=\"/config.js\"></script>\n<!-- 🌲 樹狀圖沒有外部函式庫:摺疊引擎=瀏覽器原生的 <details>/<summary>。\n 為什麼不是 markmapjsTreetreejs@widgetjs/tree,論證寫在 renderTree() 的註解裡\n Arcrun#144 完工判準第 4 條)。順帶:這頁因此回到**零第三方前端依賴**。 -->\n<script>\n// apiBase 由安裝器注入的 config.js 提供(實查用戶實例:\n// window.ARCRUN_CONFIG = { apiBase: \"https://arcrun-cypher-executor.<subdomain>.workers.dev\" })。\n// 🔴 2026-08-01 拔掉舊的 `|| \"https://cypher.arcrun.dev\"` fallback\n// 那是「靜默打到別人家」的未爆彈——config.js 一旦沒載入/被擋/改名,\n// 前端會安靜地把請求(含**用戶金鑰**)送去中央實例,而不是明顯壞掉。\n// **寧可明顯失敗,不要靜默錯置。**\nwindow.ARCRUN_API_BASE = (window.ARCRUN_CONFIG && window.ARCRUN_CONFIG.apiBase) || \"\";\nif (!window.ARCRUN_API_BASE) {\n document.addEventListener('DOMContentLoaded', function () {\n var b = document.createElement('div');\n b.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:99999;background:#b4462f;color:#fff;padding:12px 16px;font:14px/1.5 system-ui;text-align:center';\n b.textContent = '設定檔沒載入(config.js),這個頁面連不到你的服務。請重新整理;若持續發生,請重跑一次安裝。';\n document.body.appendChild(b);\n });\n}\n</script>\n</head>\n<body>\n\n<!-- ══ Icon spriteArcrun#152)══════════════════════════════════════════════\n 描邊式 inline SVG,一份 <symbol> 定義、靠 <use> 在各處引用 ⇒ 桌面側欄與手機\n tabbar 引的是同一份,兩處不可能長歪(emoji 時代是兩份字面 emoji 各自維護)。\n\n ⚠️ 每個 symbol 都**刻意不寫 stroke-width**——由 CSS 設在外層 <svg> 上再繼承進來,\n 這樣同一顆 icon 能在導覽(1.9)與磚塊(1.8)用不同筆畫,照設計稿。\n stroke=\"currentColor\" ⇒ 顏色一律由 CSS 的 color 決定(CIS token),不寫死。\n\n ⚠️ 不是函式庫、沒有 CDN、沒有外部檔案:Portal 要內嵌成單檔 worker 出貨。 -->\n<svg width=\"0\" height=\"0\" aria-hidden=\"true\" focusable=\"false\" style=\"position:absolute;overflow:hidden\">\n <defs>\n <!-- 四頁簽(Main.dc.htmlLauncher-Desktop.dc.html 原樣) -->\n <symbol id=\"i-apps\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3.5\" y=\"3.5\" width=\"7\" height=\"7\" rx=\"1.6\"/><rect x=\"13.5\" y=\"3.5\" width=\"7\" height=\"7\" rx=\"1.6\"/>\n <rect x=\"3.5\" y=\"13.5\" width=\"7\" height=\"7\" rx=\"1.6\"/><rect x=\"13.5\" y=\"13.5\" width=\"7\" height=\"7\" rx=\"1.6\"/>\n </symbol>\n <symbol id=\"i-market\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M4 8 5.5 4h13L20 8\"/><path d=\"M4 8v11a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V8\"/><path d=\"M9 12a3 3 0 0 0 6 0\"/>\n </symbol>\n <!-- 設定=滑桿。用 Launcher-Desktop 那版(Main 那版尾巴多一段 M22 18h0 零長度線段,\n 圓端點會多畫出一個小圓點)⇒ 統一取沒有雜點的,桌面與手機才真的一致。 -->\n <symbol id=\"i-settings\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M4 6h10M18 6h2M4 12h2M10 12h10M4 18h14\"/>\n <circle cx=\"16\" cy=\"6\" r=\"2\"/><circle cx=\"7\" cy=\"12\" r=\"2\"/><circle cx=\"18\" cy=\"18\" r=\"2\"/>\n </symbol>\n <symbol id=\"i-dev\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m8 8-4 4 4 4M16 8l4 4-4 4M13.5 5l-3 14\"/>\n </symbol>\n\n <!-- 內建磚塊(設計稿原樣) -->\n <symbol id=\"i-search\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"10.5\" cy=\"10.5\" r=\"6.5\"/><path d=\"m20 20-4.8-4.8\"/>\n </symbol>\n <symbol id=\"i-graph\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"5\" r=\"2.2\"/><circle cx=\"5\" cy=\"17\" r=\"2.2\"/><circle cx=\"19\" cy=\"17\" r=\"2.2\"/>\n <path d=\"M12 7.2 6.3 15.2M12 7.2l5.7 8M7.2 17h9.6\"/>\n </symbol>\n <symbol id=\"i-plus\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14M5 12h14\"/>\n </symbol>\n\n <!-- App 磚塊字形集:一對一取代 app-system.ts 的 ICON_SET 那 12 顆 emoji\n (順序必須與它一致,見 APP_GLYPHS)。 -->\n <symbol id=\"i-doc\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M6 3h7l5 5v12a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z\"/><path d=\"M13 3v5h5\"/>\n </symbol>\n <symbol id=\"i-note\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M17 3a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z\"/><path d=\"M14.5 5.5 18.5 9.5\"/>\n </symbol>\n <symbol id=\"i-layers\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m12 3 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 13 9 5 9-5\"/>\n </symbol>\n <symbol id=\"i-pin\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M8.5 3h7l-1 6 3.5 3v2H6v-2l3.5-3-1-6Z\"/><path d=\"M12 14v7\"/>\n </symbol>\n <symbol id=\"i-wrench\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M15.5 3.4a5.2 5.2 0 0 0-6.4 6.4l-5.4 5.4a2 2 0 0 0 2.8 2.8l5.4-5.4a5.2 5.2 0 0 0 6.4-6.4l-3 3-2.8-.4-.4-2.8 3-3Z\"/>\n </symbol>\n <symbol id=\"i-box\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3.5 7.5 12 3l8.5 4.5v9L12 21l-8.5-4.5v-9Z\"/><path d=\"M3.5 7.5 12 12l8.5-4.5M12 12v9\"/>\n </symbol>\n <symbol id=\"i-folder\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6.5A1.5 1.5 0 0 1 4.5 5h4l2 2.5h7A1.5 1.5 0 0 1 19 9v8.5a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 3 17.5v-11Z\"/>\n </symbol>\n <symbol id=\"i-check\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"m8 12.2 2.8 2.8L16 9.8\"/>\n </symbol>\n <symbol id=\"i-bulb\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 3a6 6 0 0 0-3.5 10.9V16h7v-2.1A6 6 0 0 0 12 3Z\"/><path d=\"M9.8 19h4.4M10.5 21.5h3\"/>\n </symbol>\n <symbol id=\"i-bell\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8.5a6 6 0 1 0-12 0c0 5.5-2 7-2 7h16s-2-1.5-2-7Z\"/><path d=\"M10.2 19a2.2 2.2 0 0 0 3.6 0\"/>\n </symbol>\n <symbol id=\"i-chart\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M4 4v16h16\"/><path d=\"M8 20v-6.5M12.5 20V8.5M17 20v-4\"/>\n </symbol>\n <symbol id=\"i-compass\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"m15.6 8.4-2.1 5.1-5.1 2.1 2.1-5.1 5.1-2.1Z\"/>\n </symbol>\n </defs>\n</svg>\n\n<!-- 登入(未登入只見這個殼;本頁不含任何資料)-->\n<div class=\"authwrap view\" id=\"v-login\">\n <div class=\"authbox\">\n <div>\n <div class=\"brand\" role=\"img\" aria-label=\"Arcrun\"><img class=\"wm-ink\" src=\"data:image/png;base64,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\" alt=\"arc>>run\" width=\"986\" height=\"176\"><img class=\"wm-paper\" src=\"data:image/png;base64,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\" alt=\"arc>>run\" width=\"986\" height=\"176\"></div>\n <div class=\"brand-sub\">知識入口・Portal</div>\n </div>\n <div style=\"display:flex;flex-direction:column;gap:12px;text-align:left\">\n <input type=\"email\" id=\"login-email\" class=\"txt\" placeholder=\"Email\" autocomplete=\"username\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <input type=\"password\" id=\"login-password\" class=\"txt\" placeholder=\"密碼\" autocomplete=\"current-password\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <button class=\"btn\" id=\"login-submit\" style=\"margin-top:6px;padding:15px;font-size:17px;letter-spacing:.2em\">登入</button>\n <div id=\"login-status\" class=\"err\" style=\"font-size:14px;min-height:1.2em\"></div>\n </div>\n <!-- 🔴 D50/D62:這裡原本寫「忘記密碼請聯絡管理員重設」+一條「用管理主控台密碼救援自己」\n 的 console 連結。**兩條都是已否決的做法**,不准寫回來:\n ① leo:「我要是記得密碼,還來找忘記密碼幹嘛?」——console 登入頁一樣要密碼\n ② console 與 portal 是安裝時**同一組帳密**寫進兩個地方(D50 補刀)\n ⇒ console 不是第二道閘,是同一把鎖的第二個鑰匙孔,往它補洞不會多出任何一條路\n ③ 唯一管理員忘記密碼時「聯絡管理員」=叫他聯絡他自己\n leo 2026-08-10:「**是對 portal 不是對 console**,這樣 youlin 雖然忘記,\n 我還是可以去 portal 忘記密碼。」⇒ 出口就在這裡。 -->\n <div style=\"font-size:13.5px;color:rgba(var(--ink-rgb),.4);line-height:1.7\">\n 忘記密碼了?<a href=\"#\" id=\"login-forgot\" style=\"color:var(--amber)\">寄一條「修改密碼」連結給我</a>\n </div>\n <div id=\"forgot-box\" class=\"hide\" style=\"display:flex;flex-direction:column;gap:10px;margin-top:4px\">\n <input type=\"email\" id=\"forgot-email\" class=\"txt\" placeholder=\"你註冊時用的 Email\" autocomplete=\"username\">\n <button class=\"btn\" id=\"forgot-submit\">寄出連結</button>\n <div id=\"forgot-status\" style=\"font-size:13.5px;min-height:1.2em;line-height:1.6\"></div>\n </div>\n <button class=\"btn3 themelabel\" data-themetoggle style=\"align-self:center;padding:8px 16px;font-size:13.5px;border-radius:999px\">☾ 切深色</button>\n </div>\n</div>\n\n<!-- 修改密碼(D62):忘記密碼點信裡的連結進來的落點。\n 🔴 這個殼**刻意沒有自己的密碼欄位**——真正的表單是設定頁那一份 #pw-form,\n 進到這個模式時被 JS 原封不動搬進 #reset-slot。D62:「同一個畫面、同一條寫入路徑,\n 差別只有一格:現有密碼是要填、還是被連結豁免。」複製一份長得像的就會漂移。 -->\n<div class=\"authwrap view\" id=\"v-reset\">\n <div class=\"authbox\">\n <div>\n <div style=\"font-size:22px;font-weight:600;margin-bottom:6px\">設定新密碼</div>\n <div id=\"reset-who\" style=\"font-size:14px;color:rgba(var(--ink-rgb),.55);line-height:1.7;margin-bottom:14px\"></div>\n <div id=\"reset-slot\"></div>\n <div id=\"reset-error\" class=\"err\" style=\"font-size:14px;line-height:1.7\"></div>\n <div style=\"margin-top:16px;font-size:13.5px\"><a href=\"#/\" id=\"reset-back\" style=\"color:var(--amber)\">回登入頁</a></div>\n </div>\n </div>\n</div>\n\n<!-- 首次設定(t49leo 07-25:一鍵安裝的用戶打開專屬網址,第一件事就是在這裡建帳號,\n 不再需要回安裝器那頁。auth-status 說 configured:false 才會出現。)-->\n<div class=\"authwrap view\" id=\"v-firstsetup\">\n <div class=\"authbox\">\n <div>\n <div class=\"brand\" role=\"img\" aria-label=\"Arcrun\"><img class=\"wm-ink\" src=\"data:image/png;base64,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\" alt=\"arc>>run\" width=\"986\" height=\"176\"><img class=\"wm-paper\" src=\"data:image/png;base64,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\" alt=\"arc>>run\" width=\"986\" height=\"176\"></div>\n <div class=\"brand-sub\">歡迎!先建立你的帳號</div>\n </div>\n <div style=\"display:flex;flex-direction:column;gap:12px;text-align:left\">\n <input type=\"email\" id=\"fs-email\" class=\"txt\" placeholder=\"你的 Email\" autocomplete=\"username\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <input type=\"password\" id=\"fs-password\" class=\"txt\" placeholder=\"設定密碼(至少 8 碼)\" autocomplete=\"new-password\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <input type=\"password\" id=\"fs-password2\" class=\"txt\" placeholder=\"再輸入一次密碼\" autocomplete=\"new-password\" style=\"padding:15px 16px;font-size:17px;border-radius:11px\">\n <button class=\"btn\" id=\"fs-submit\" style=\"margin-top:6px;padding:15px;font-size:17px;letter-spacing:.12em\">建立帳號並進入</button>\n <div id=\"fs-status\" class=\"err\" style=\"font-size:14px;min-height:1.2em\"></div>\n <!-- leo 07-25 實撞:已設定過(409)時這頁是死路——必須有回登入的路 -->\n <button class=\"btn3\" id=\"fs-tologin\" style=\"padding:9px 16px;font-size:14px;border-radius:999px;align-self:center;cursor:pointer\">已經有帳號?改用登入</button>\n </div>\n <div style=\"font-size:13.5px;color:rgba(var(--ink-rgb),.4);line-height:1.7\">這組帳密就是之後登入知識庫用的,只需要設定這一次。</div>\n </div>\n</div>\n\n<!-- 主應用 -->\n<div id=\"shell\">\n <div id=\"sidenav\">\n <div class=\"logo\" role=\"img\" aria-label=\"Arcrun\"><img class=\"wm-ink\" src=\"data:image/png;base64,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\" width=\"986\" height=\"176\" alt=\"arcrun\"><img class=\"wm-paper\" src=\"data:image/png;base64,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\" width=\"986\" height=\"176\" alt=\"arcrun\"></div>\n <!-- Arcrun#152OS 級四段導覽——取代原本的單層清單。已安裝的 App 不再各自佔一格\n 側欄項目,改以「App 界面」頁簽裡的九宮格+「App 市集」頁簽裡的清單呈現\n renderAppsNav()K5 泛用 loader 沿用不變,只是渲染目標從側欄換成這兩處)。 -->\n <div class=\"nav osnav\" data-ostab=\"apps\"><span class=\"os-ic\"><svg><use href=\"#i-apps\"/></svg></span><span>App 界面</span></div>\n <div class=\"nav osnav\" data-ostab=\"market\"><span class=\"os-ic\"><svg><use href=\"#i-market\"/></svg></span><span>App 市集</span></div>\n <div class=\"nav osnav\" data-ostab=\"settings\"><span class=\"os-ic\"><svg><use href=\"#i-settings\"/></svg></span><span>AR 設定</span></div>\n <div class=\"nav osnav\" data-ostab=\"dev\"><span class=\"os-ic\"><svg><use href=\"#i-dev\"/></svg></span><span>App 開發</span></div>\n <div class=\"nav themelabel\" data-themetoggle style=\"margin-top:8px;font-size:14.5px\">☾ 切深色</div>\n <div class=\"foot\" id=\"side-foot\"></div>\n </div>\n <main>\n\n <!-- App 界面(Arcrun#152,落地頁):九宮格啟動器——RAG 搜尋/知識總圖/已安裝 App\n 都以有 icon、點開就用的磚塊呈現,磚塊清單由 renderAppsNav()K5 泛用 loader\n 動態產生,這裡本身 0 硬編清單。 -->\n <div class=\"view page\" id=\"v-launcher\">\n <div class=\"pagehead\"><span class=\"t\" id=\"lh-greet\">App 界面</span><span class=\"m\" id=\"lh-sub\"></span></div>\n <div class=\"launchgrid\" id=\"launch-grid\"><div class=\"muted\" style=\"padding:6px 4px\">載入中…</div></div>\n </div>\n\n <!-- 搜尋(落地頁)-->\n <div class=\"view page\" id=\"v-search\">\n <div class=\"pagehead\"><span class=\"t\">搜尋</span><span class=\"m\" id=\"se-scope\"></span></div>\n <div style=\"padding-top:20px\">\n <div class=\"searchrow\">\n <input id=\"se-q\" placeholder=\"搜尋知識…\" autocomplete=\"off\">\n <button class=\"btn\" id=\"se-go\" style=\"flex:none;padding:0 22px;letter-spacing:.2em\">搜</button>\n </div>\n <div class=\"modebar\">\n <button class=\"modebtn on\" data-mode=\"keyword\">關鍵字</button>\n <button class=\"modebtn\" data-mode=\"semantic\">語意</button>\n <button class=\"modebtn hide\" id=\"mode-graph\" data-mode=\"graph\">圖譜</button>\n </div>\n <!-- AI 問答(portal-demo-suite)。設計哲學:AI 檢索=用戶手動搜尋同一套——\n /portal/data/chat 與 /portal/data/search 走同一個 session 閘、同一個租戶資料面,\n AI 只是代查再作答,不多開任何權限。此實例未安裝問答 workflow 時 server 誠實 404。-->\n <div style=\"margin-top:14px;padding:14px;border-radius:13px;background:rgba(var(--amber-rgb),.05);border:1px solid rgba(var(--amber-rgb),.25)\">\n <div style=\"font-size:13.5px;letter-spacing:.1em;color:rgba(var(--ink-rgb),.5);margin-bottom:9px\">AI 問答・答案附出處,可回搜尋驗證</div>\n <div class=\"searchrow\">\n <input id=\"ai-q\" placeholder=\"用一句話問知識庫…\" autocomplete=\"off\" style=\"padding:12px 15px;font-size:16px\">\n <button class=\"btn\" id=\"ai-go\" style=\"flex:none;padding:0 20px\">問 AI</button>\n </div>\n <div id=\"ai-out\"></div>\n </div>\n <div id=\"se-banner\"></div>\n <div id=\"se-count\" style=\"margin:20px 0 12px;font-size:14px;color:rgba(var(--ink-rgb),.5)\"></div>\n <div class=\"kgrid\" id=\"se-results\"></div>\n <div id=\"se-graphout\" style=\"display:none\">\n <div id=\"gr-box\"></div>\n <div id=\"gr-edges\" style=\"margin-top:16px;display:flex;flex-direction:column;gap:7px\"></div>\n </div>\n </div>\n </div>\n\n <!-- 總圖(Arcrun#39 藏書地圖 GUI 切片;graph 粗閘後可見):全庫三元組一張網。\n 力導向佈局內聯手刻(demo 量級 ≤ 數百節點),不引外部套件(頁面自足原則)。-->\n <div class=\"view page\" id=\"v-map\">\n <div class=\"pagehead\"><span class=\"t\">總圖</span><span class=\"m\" id=\"map-meta\"></span></div>\n <div style=\"padding-top:20px\">\n <div id=\"map-box\"></div>\n <div style=\"margin-top:12px;font-size:13.5px;color:rgba(var(--ink-rgb),.5)\">\n 點節點可跳到該實體的圖譜搜尋。這張圖由知識庫的三元組即時算出——同一份地圖的文字版\n (給 AI 注入用的總庫目錄)<span id=\"map-md-link\"></span>。\n </div>\n </div>\n </div>\n\n <!-- 卡片詳頁 -->\n <div class=\"view page\" id=\"v-card\">\n <div class=\"pagehead\" style=\"justify-content:flex-start;gap:14px\">\n <button class=\"btn3\" style=\"border:none;padding:0;font-size:16px;color:var(--amber)\" data-nav=\"search\"> 搜尋</button>\n <span class=\"serif\" style=\"font-size:17px;letter-spacing:.14em;color:rgba(var(--ink-rgb),.55)\">知識卡片</span>\n </div>\n <div id=\"cd-main\" style=\"padding:24px 0 40px\"><div class=\"muted\">載入中…</div></div>\n </div>\n\n <!-- App 市集(Arcrun#152):已安裝清單沿用 renderAppsNav() 同一份資料(K2 單一真相源);\n 「可安裝」讀 /portal/data/apps/catalog(隨引擎出貨的目錄),按一下就裝。\n 「上傳你的 App」=第三方發佈,那是另一份協定(誰託管/誰審/怎麼簽章),\n 刻意維持佔位,不假裝能用。 -->\n <div class=\"view page narrow\" id=\"v-market\">\n <div class=\"pagehead\"><span class=\"t\">App 市集</span><span class=\"m\">瀏覽、安裝、或上傳你自己做的 App</span></div>\n <div style=\"padding-top:16px\">\n <div class=\"promorow\" id=\"market-upload-banner\">\n <div class=\"ic\">⬆</div>\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-size:14.5px;font-weight:600\">上傳你的 App</div>\n <div style=\"font-size:12.5px;color:rgba(var(--ink-rgb),.55);margin-top:2px\">打包成一份宣告,讓其他人也能安裝——<b style=\"color:var(--ink)\">尚未接後端</b>,先佔位</div>\n </div>\n </div>\n <div class=\"sechead\" style=\"margin-top:22px\">已安裝</div>\n <div class=\"listcol\" id=\"market-installed\"><div class=\"muted\">載入中…</div></div>\n <div class=\"sechead\">可安裝</div>\n <!-- 🔴 底部留白不是排版偏好,是為了讓「安裝/移除」按得到:\n 「還差 1 步」那張安裝清單卡是 `position:fixed; right:20px; bottom:20px; z-index:60`\n 而本頁每一列的動作鍵都靠右 ⇒ 最後幾列的按鈕**整顆被蓋在那張卡下面,點不到**\n 2026-08-25 youlin 實撞:滑鼠按在按鈕上,elementFromPoint 回的是 setup-checklist)。\n 留一段空白 ⇒ 清單永遠捲得上來、按得到。卡片本身不動(它是別條線的東西)。 -->\n <div class=\"listcol\" id=\"market-catalog\" style=\"padding-bottom:240px\"><div class=\"muted\">載入中…</div></div>\n </div>\n </div>\n\n <!-- 上傳(portal-demo-suiteupload bindings 齊全才顯示 nav——/portal/session 的\n upload_enabled;藏只是 UX,真閘在 /portal/data/upload 路由層 404-->\n <div class=\"view page narrow\" id=\"v-upload\">\n <div class=\"pagehead\"><span class=\"t\">上傳</span><span class=\"m\">Markdown / 純文字</span></div>\n <div style=\"margin-top:16px;padding:13px 16px;border-radius:11px;background:rgba(var(--amber-rgb),.06);border:1px dashed rgba(var(--amber-rgb),.35);font-size:14px;line-height:1.7;color:rgba(var(--ink-rgb),.65)\">\n 上傳的文件會進入知識庫收件管線:<b style=\"color:var(--ink)\">約 1 分鐘後可在搜尋頁找到</b>;AI 整理後會以 wiki 卡形式出現。支援 .md / .txt(一律以 .md 收件)。\n </div>\n <div id=\"up-drop\" style=\"margin-top:16px;padding:44px 20px;border-radius:13px;border:2px dashed rgba(var(--amber-rgb),.4);background:rgba(var(--amber-rgb),.04);text-align:center;cursor:pointer\">\n <div style=\"font-size:16px;color:rgba(var(--ink-rgb),.7)\">把 .md / .txt 檔案拖放到這裡</div>\n <div class=\"muted\" style=\"margin-top:8px;font-size:14px\">或</div>\n <button class=\"btn2\" id=\"up-pick\" style=\"margin-top:10px;padding:11px 22px;font-size:15px\">選擇檔案(可多選)</button>\n <input type=\"file\" id=\"up-file\" accept=\".md,.txt\" multiple style=\"display:none\">\n </div>\n <div class=\"listcol\" id=\"up-list\"></div>\n </div>\n\n <!-- 工作流(D-8:預設 admin 才看得到;唯讀,不開 trigger-->\n <!-- App 開發(Arcrun#152):工作環境入口。Workflows/上傳資料是既有功能,收攏成清單;\n 零件/RecipeCredential 現在活在另一套 console owner session(不同認證模型,\n design §四已裁「不新造」),Portal 這裡老實標「尚未接進 Portal」,不假裝能點。\n 「叫 AI 幫你做一個 App」同理——沒有後端,先佔位不假裝能用。 -->\n <div class=\"view page narrow\" id=\"v-dev\">\n <div class=\"pagehead\"><span class=\"t\">App 開發</span><span class=\"m\">Workflows/零件/RecipeCredential</span></div>\n <div style=\"padding-top:16px\">\n <div class=\"promorow\" id=\"dev-ai-banner\">\n <div class=\"ic\">✦</div>\n <div style=\"flex:1;min-width:0\">\n <div style=\"font-size:15px;font-weight:600\">叫 AI 幫你做一個 App</div>\n <div style=\"font-size:13px;color:rgba(var(--ink-rgb),.55);margin-top:2px\">用一句話描述你要什麼——<b style=\"color:var(--ink)\">尚未接後端</b>,先佔位</div>\n </div>\n </div>\n <div class=\"sechead\" style=\"margin-top:20px\">工作環境</div>\n <div class=\"listcol\">\n <div class=\"card-item hide\" id=\"nav-workflows\" data-nav=\"workflows\" style=\"cursor:pointer;display:flex;align-items:center;justify-content:space-between\">\n <span style=\"font-weight:600\">Workflows</span><span class=\"dim\"></span>\n </div>\n <div class=\"card-item hide\" id=\"nav-upload\" data-nav=\"upload\" style=\"cursor:pointer;display:flex;align-items:center;justify-content:space-between\">\n <span style=\"font-weight:600\">資料上傳</span><span class=\"dim\"></span>\n </div>\n <div class=\"card-item\" style=\"opacity:.6\"><div style=\"display:flex;align-items:center;justify-content:space-between\"><span>零件</span><span class=\"tag dim\">尚未接進 Portal</span></div></div>\n <div class=\"card-item\" style=\"opacity:.6\"><div style=\"display:flex;align-items:center;justify-content:space-between\"><span>Recipe</span><span class=\"tag dim\">尚未接進 Portal</span></div></div>\n <div class=\"card-item\" style=\"opacity:.6\"><div style=\"display:flex;align-items:center;justify-content:space-between\"><span>Credential</span><span class=\"tag dim\">尚未接進 Portal</span></div></div>\n </div>\n </div>\n </div>\n\n <div class=\"view page narrow\" id=\"v-workflows\">\n <div class=\"pagehead\"><span class=\"t\">工作流</span><span class=\"m\">唯讀・系統狀態</span></div>\n <div style=\"margin-top:16px;padding:13px 16px;border-radius:11px;background:rgba(var(--amber-rgb),.06);border:1px dashed rgba(var(--amber-rgb),.35);font-size:14px;line-height:1.7;color:rgba(var(--ink-rgb),.65)\">\n 此頁只顯示系統裡的工作流與最近執行狀態,<b style=\"color:var(--ink)\">不能觸發執行</b>(觸發屬系統擁有者權限)。\n </div>\n <div class=\"listcol\" id=\"wf-list\"><div class=\"muted\">載入中…</div></div>\n </div>\n\n <!-- Leo/Arcrun#82 App 系統 v0(K5):泛用畫面容器——所有已安裝的 App 共用這一個\n view,內容由 loadApp()/renderAppView() 依 App id 動態換掉。 -->\n <div class=\"view page\" id=\"v-app\">\n <div class=\"pagehead\">\n <!-- Arcrun#152:返回鍵**不寫在這裡**。leo 2026-08-24 第二輪:「只有筆記有 nav bar,\n 所有的 App 都要顯示,不然他要怎麼回 home?」——第一輪只給 App 頁補了一顆,\n 而九宮格點出去的還有搜尋/總圖/卡片等內建 view,它們一顆都沒有。\n ⇒ 出口改成協定層的東西,由 ensureBackControls() 對**每一個非首頁 view** 統一塞,\n 新增 view 不必記得補。這裡刻意留空,別再手寫第二顆。 -->\n <span class=\"t\" id=\"app-title\">App</span>\n <button class=\"btn3 hide\" id=\"app-remove-btn\" style=\"margin-left:auto;color:var(--err,#c0392b)\">移除這個 App</button>\n </div>\n <div id=\"app-body\"><div class=\"muted\">載入中…</div></div>\n </div>\n\n <!-- 設定 -->\n <div class=\"view page narrow\" id=\"v-settings\">\n <div class=\"pagehead\"><span class=\"t\">設定</span></div>\n <!-- AR 設定頁簽的次層導覽(Arcrun#152):帳號設定/管理原地不動,這裡只加一列切換。\n 「管理」chip 沿用既有 id=\"nav-admin\" 的權限隱藏邏輯(showApp() 依 role 切 hide)。 -->\n <div class=\"subnav\">\n <div class=\"subchip on\" data-nav=\"settings\">帳號</div>\n <div class=\"subchip hide\" id=\"nav-admin\" data-nav=\"admin\">管理</div>\n </div>\n <div class=\"setcol\">\n <!-- 🆕 2026-08-02leo:「不顯示的話用戶不知道要不要更新」+「發現落後就按一下開啟 install」):\n 版本卡放設定頁第一張——用戶想「我是不是舊的」時第一眼就看到。\n 落後時直接給一顆帶 email 的更新按鈕(安裝器 t154 對既有實例免辨識碼),\n 不要求用戶理解版本號、找網址、翻辨識碼。 -->\n <div class=\"panel\" id=\"st-ver-panel\">\n <div style=\"display:flex;align-items:center;gap:12px;flex-wrap:wrap\">\n <div style=\"min-width:0;flex:1\">\n <div style=\"font-size:17px;font-weight:600;display:flex;align-items:center;gap:8px\">\n 版本\n <span id=\"st-ver-dot\" style=\"display:none;width:9px;height:9px;border-radius:50%;background:#C2492E\" title=\"有新版本\"></span>\n </div>\n <div id=\"st-ver-line\" style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">查詢中…</div>\n </div>\n <button class=\"btn\" id=\"st-ver-update\" style=\"display:none\">立即更新</button>\n </div>\n </div>\n <div class=\"panel\">\n <div style=\"font-size:17px;font-weight:600;margin-bottom:10px\">我的帳號</div>\n <div id=\"st-me\"><div class=\"muted\">載入中…</div></div>\n </div>\n <div class=\"panel\">\n <div style=\"display:flex;align-items:center;gap:14px\">\n <div style=\"min-width:0\">\n <div style=\"font-size:17px;font-weight:600\">深色模式</div>\n <div style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">預設淺色(紙感)。切換立即生效,選擇記在這台裝置。</div>\n </div>\n <div class=\"switch\" id=\"st-theme-switch\" data-themetoggle style=\"cursor:pointer\"><i></i></div>\n </div>\n </div>\n <!-- D62leo 2026-08-10):「改密碼與忘記密碼**這兩個機制其實是一個機制,可以簡化**。」\n ⇒ 這個表單是**整個站唯一一份**改密碼的表單。忘記密碼點連結進來時,\n 它會被原封不動搬到 #v-reset 那個殼裡(不是複製一份長得像的)——\n 差別只有「現有密碼」那一格顯不顯示。同一個畫面、同一條寫入路徑。 -->\n <div class=\"panel\" id=\"st-pw-panel\">\n <div style=\"font-size:17px;font-weight:600\" id=\"pw-title\">更改密碼</div>\n <div style=\"margin-top:4px;font-size:14px;color:rgba(var(--ink-rgb),.55)\" id=\"pw-sub\">需輸入舊密碼驗證身分;新密碼至少 8 碼</div>\n <div id=\"pw-form\" style=\"margin-top:14px;display:flex;flex-direction:column;gap:10px\">\n <input type=\"password\" id=\"st-pw-old\" class=\"txt\" placeholder=\"舊密碼\" autocomplete=\"current-password\">\n <input type=\"password\" id=\"st-pw-new\" class=\"txt\" placeholder=\"新密碼(至少 8 碼)\" autocomplete=\"new-password\">\n <input type=\"password\" id=\"st-pw-new2\" class=\"txt\" placeholder=\"再輸入一次新密碼\" autocomplete=\"new-password\">\n <button class=\"btn\" id=\"st-pw-save\">更新密碼</button>\n <div id=\"st-pw-status\" style=\"font-size:14px;min-height:1.2em\"></div>\n </div>\n </div>\n <!-- 07-27 leo:金鑰與下載原本只在一次性的「還差 N 步」卡片裡,按了「稍後再說」\n 或裝完就再也找不到。換金鑰/換電腦重新下載都需要常駐入口,故搬進設定頁。 -->\n <div class=\"panel\">\n <!-- t87 07-28 leo:乾淨網址(只有 origin,不含 /portal/# 後綴),供小幫手連線用 -->\n <div style=\"display:flex;align-items:center;gap:8px;margin-bottom:14px;font-size:13.5px\">\n <span style=\"color:rgba(var(--ink-rgb),.6);white-space:nowrap\">你的知識庫網址(小幫手連線用)</span>\n <code id=\"st-origin-url\" style=\"flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--ink)\"></code>\n <button class=\"btn3\" id=\"st-copy-url\" style=\"padding:5px 12px;font-size:13px;white-space:nowrap;flex:none\">複製</button>\n </div>\n <div style=\"font-size:17px;font-weight:600\">同步小幫手</div>\n <div style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">把你電腦上的資料夾變成知識庫。換電腦或重裝時可以再下載一次。</div>\n <div style=\"margin-top:12px\">\n <a class=\"btn3\" id=\"st-daemon-dl\" href=\"#\" style=\"display:inline-block;padding:11px 18px;border-radius:10px;text-decoration:none\">下載 Mac 版</a>\n <div style=\"margin-top:8px;font-size:13px;line-height:1.7;color:rgba(var(--ink-rgb),.5)\">封測版未簽章,第一次請右鍵→打開。裝好第一次開啟時,貼上這個網址+你的帳號密碼就連上了。</div>\n </div>\n </div>\n <!-- 08-09arcrun-rag#7,封測者原話「說明叫我把 MCP 加進 claude.ai connector,但我找不到網址」):\n 文件一直寫「登入 portal 設定頁直接複製」,但畫面上從沒真的顯示過這串網址——用戶照著文件的\n 指示走到這裡,只會撲空。這裡補上:MCP 網址跟知識庫網址(apiBase)是同一顆自架帳號的\n workers.dev 子網域,只是 worker 名字從 arcrun-cypher-executor 換成 arcrun-mcp\n (CLI 部署當時就是這樣組出這兩個網址的,見 cli/src/lib/deploy.ts:386-392)——\n 純前端字串轉換,不需要後端新端點、不需要安裝器多寫一份設定。 -->\n <div class=\"panel\">\n <div style=\"display:flex;align-items:center;gap:8px;margin-bottom:14px;font-size:13.5px\">\n <span style=\"color:rgba(var(--ink-rgb),.6);white-space:nowrap\">你的 MCP 網址(給你的 AI 連線用)</span>\n <code id=\"st-mcp-url\" style=\"flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--ink)\">—</code>\n <button class=\"btn3\" id=\"st-copy-mcp-url\" style=\"padding:5px 12px;font-size:13px;white-space:nowrap;flex:none\">複製</button>\n </div>\n <div style=\"font-size:17px;font-weight:600\">接上你的 AIMCP</div>\n <div style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">把上面這串網址貼到 Claude、ChatGPT 等 AI 的「新增自訂連接器」欄位,就能讓你的 AI 直接查這個知識庫。</div>\n </div>\n <!-- t176leo 08-03):AI 設定整塊移除。\n 雲端聊天問答走 Workers AI(用戶自己 CF 帳號內建,**免金鑰、裝好就能用**);\n 地端萃取用哪把金鑰改由「同步小幫手」自己設定(托盤選單「AI 設定…」)。\n 為什麼移除:雲端的 extractor 設定是全租戶共用一把 KV,任一處設了 claude\n 會讓所有沒裝 Claude Code 的機器萃取全滅,而這裡的 Claude 勾選框又恆為\n disableddaemon 從未回報 has_claude)⇒ 用戶自己解不開(08-03 封測實證)。\n leo:「搞不清楚設定 AI 是設雲端還是地端」——設定只留一處,就不會再搞混。 -->\n <div class=\"panel\">\n <div style=\"font-size:17px;font-weight:600\">AI 設定</div>\n <div style=\"margin-top:12px;font-size:13.5px;line-height:1.7;color:rgba(var(--ink-rgb),.55);background:rgba(var(--ink-rgb),.03);border-radius:9px;padding:11px 13px\">\n <b style=\"color:var(--ink)\">這裡不需要任何設定。</b>聊天問答用的是你自己 Cloudflare 帳號內建的 AI,裝好就能直接問。<br>\n 文件整理成知識卡的部分,請在<b style=\"color:var(--ink)\">同步小幫手</b>(電腦上的托盤圖示)的「AI 設定…」填一把 Gemini API Key。\n </div>\n </div>\n <!-- 檢修孔演進史:\n 2026-08-07 leo 直接指令「一顆按鈕在設定裡,按鈕下載一個檔案,把檔案發給我」\n →「疑難排解」面板+#st-diag-export 按鈕誕生,打 GET /portal/data/diagnostics。\n\n 2026-08-08t213InkStoneCo 總管交辦)發現這顆按鈕在**封測者的瀏覽器**裡執行,\n 跟他電腦上的 daemon 是兩個獨立行程,構不到本機資料(檔案總量/失敗分類/\n daemon 版本)——完整版改在 arcrun-app(同步小幫手)「版本與更新」頁本機端匯出\n (打新端點 GET /portal/daemon/diagnostics)。當時按鈕先保留當退路,文案改成\n 誠實講清楚自己只有一半、導去完整版。\n\n 2026-08-09leo 拍板拿掉):leo 08-08「雲端那個要刪掉?不刪用戶搞不清楚要去\n 哪裏下載」,封測者已被通知去更新到有地端匯出的版本後,08-09 追認「通知完畢\n 可以刪除」。⇒ 按鈕與 #st-diag-export/#st-diag-status 一併移除,面板改成純文字\n 指路(同步小幫手才是唯一還按得到、也答得出完整診斷的地方)。\n GET /portal/data/diagnostics 端點本身留著未刪(無害、未被任何 UI 呼叫,\n 純粹清路標,不動後端)。 -->\n <div class=\"panel\">\n <div style=\"font-size:17px;font-weight:600\">疑難排解</div>\n <div style=\"margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)\">要回報問題,請到你電腦上的 <b style=\"color:var(--ink)\">Arcrun</b>(同步小幫手)「版本與更新」頁——那裡的「疑難排解」按一下就能匯出完整診斷檔給我們(只有統計數字,不含你的任何文件內容)。</div>\n </div>\n <button class=\"btn3\" id=\"st-logout\" style=\"padding:14px;font-size:16px;border-radius:11px\">登出</button>\n </div>\n </div>\n\n <!-- 管理(P4admin-onlynav 只有 admin 看得到;真閘在 /portal/admin/* role 閘 403-->\n <div class=\"view page\" id=\"v-admin\">\n <div class=\"pagehead\"><span class=\"t\">管理</span><span class=\"m\">帳號與知識庫授權</span></div>\n <div class=\"subnav\">\n <div class=\"subchip\" data-nav=\"settings\">帳號</div>\n <div class=\"subchip on\" data-nav=\"admin\">管理</div>\n </div>\n\n <div class=\"sechead\">執行紀錄保留期</div>\n <div class=\"panel\">\n <div style=\"font-size:16px;font-weight:600;margin-bottom:4px\">保留天數</div>\n <div style=\"font-size:13.5px;color:rgba(var(--ink-rgb),.55);margin-bottom:12px\">執行紀錄是稽核資料,超過保留天數會被每日自動清除;預設 90 天(3 個月),也可設為「不刪除」(企業稽核用途)。</div>\n <div class=\"formrow\">\n <input type=\"number\" id=\"ad-ret-days\" class=\"txt\" min=\"1\" step=\"1\" placeholder=\"天數(例:90\">\n <label style=\"display:flex;align-items:center;gap:7px;white-space:nowrap;font-size:15px;padding:0 4px\">\n <input type=\"checkbox\" id=\"ad-ret-never\"> 不刪除\n </label>\n <button class=\"btn\" id=\"ad-ret-save\" style=\"flex:none;padding:0 20px\">儲存</button>\n </div>\n <div id=\"ad-ret-status\" class=\"err\" style=\"font-size:14px;min-height:1.2em;margin-top:8px\"></div>\n <div id=\"ad-ret-current\" class=\"muted\" style=\"font-size:13px;margin-top:2px\"></div>\n </div>\n\n <div class=\"sechead\">帳號管理</div>\n <div class=\"panel\">\n <div style=\"font-size:16px;font-weight:600;margin-bottom:4px\">新增同仁帳號</div>\n <div style=\"font-size:13.5px;color:rgba(var(--ink-rgb),.55);margin-bottom:12px\">建立後會產生一組一次性密碼——只顯示這一次,請當場轉交同仁(同仁可在「設定」自行改密)。</div>\n <div class=\"formrow\">\n <input type=\"email\" id=\"ad-nu-email\" class=\"txt\" placeholder=\"Email\" autocomplete=\"off\">\n <input type=\"text\" id=\"ad-nu-name\" class=\"txt\" placeholder=\"顯示名(可留空=用 Email\" autocomplete=\"off\">\n <select id=\"ad-nu-role\"><option value=\"user\">一般用戶</option><option value=\"admin\">管理員</option></select>\n <button class=\"btn\" id=\"ad-nu-create\" style=\"flex:none;padding:0 20px\">建立</button>\n </div>\n <div id=\"ad-nu-status\" class=\"err\" style=\"font-size:14px;min-height:1.2em;margin-top:8px\"></div>\n </div>\n <div id=\"ad-otp\"></div>\n <div class=\"listcol\" id=\"ad-users\"><div class=\"muted\">載入中…</div></div>\n\n <div class=\"sechead\">庫目錄管理</div>\n <div style=\"display:flex;align-items:center;gap:8px;margin-bottom:10px;font-size:13.5px\">\n <span style=\"color:rgba(var(--ink-rgb),.6);white-space:nowrap\">你的知識庫網址(小幫手連線用)</span>\n <code id=\"ad-origin-url\" style=\"flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;color:var(--ink)\"></code>\n <button class=\"btn3\" id=\"ad-copy-url\" style=\"padding:5px 12px;font-size:13px;white-space:nowrap;flex:none\">複製</button>\n </div>\n <div style=\"margin-bottom:12px;padding:12px 15px;border-radius:11px;background:rgba(var(--amber-rgb),.06);border:1px dashed rgba(var(--amber-rgb),.35);font-size:13.5px;line-height:1.7;color:rgba(var(--ink-rgb),.65)\">\n <!-- 07-27 leo:拿掉「登記新庫」——庫由「同步小幫手」看守資料夾時自動蓋章產生,\n 人工登記只會製造對不上的空庫。這裡只做「已存在的庫」的管理。 -->\n 裝好<b style=\"color:var(--ink)\">同步小幫手</b>並選好要看守的資料夾之後,每個資料夾會自動成為一個「庫」出現在下面——<b style=\"color:var(--ink)\">不需要人工新增</b>。下面還是空的,代表小幫手還沒裝好或還沒選資料夾。\n </div>\n <div class=\"listcol\" id=\"ad-libs\"><div class=\"muted\">載入中…</div></div>\n </div>\n\n </main>\n</div>\n\n<!-- 手機底部導覽 -->\n<div id=\"tabbar\">\n <div class=\"tab ostab\" data-ostab=\"apps\"><span class=\"os-ic\"><svg><use href=\"#i-apps\"/></svg></span><span>App 界面</span></div>\n <div class=\"tab ostab\" data-ostab=\"market\"><span class=\"os-ic\"><svg><use href=\"#i-market\"/></svg></span><span>App 市集</span></div>\n <div class=\"tab ostab\" data-ostab=\"settings\"><span class=\"os-ic\"><svg><use href=\"#i-settings\"/></svg></span><span>AR 設定</span></div>\n <div class=\"tab ostab\" data-ostab=\"dev\"><span class=\"os-ic\"><svg><use href=\"#i-dev\"/></svg></span><span>App 開發</span></div>\n</div>\n\n<div id=\"toast\"></div>\n\n<script>\n(function () {\n var API_BASE = window.ARCRUN_API_BASE || '';\n 'use strict';\n // 台北時間 helperlib/taipei-time.ts 注入,console 同套——顯示不隨裝置時區漂移)\n var TAIPEI_OFFSET_MS = 28800000; // UTC+8,台北無 DST\nfunction tpePad(n) { n = String(n); return n.length < 2 ? '0' + n : n; }\nfunction taipeiDayKey(ms) { return new Date(ms + TAIPEI_OFFSET_MS).toISOString().slice(0, 10); }\nfunction taipeiDateStr(ms) { return taipeiDayKey(ms); }\nfunction taipeiDateTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return taipeiDayKey(ms) + ' ' + tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()); }\nfunction taipeiTimeStr(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return tpePad(d.getUTCHours()) + ':' + tpePad(d.getUTCMinutes()) + ':' + tpePad(d.getUTCSeconds()); }\nfunction taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { month: d.getUTCMonth() + 1, day: d.getUTCDate() }; }\n var $ = function (id) { return document.getElementById(id); };\n\n // ── 狀態:只有 session token**沒有租戶字串、沒有 API key**design §3.3)──\n var S = {\n token: localStorage.getItem('arcrun_portal_session') || '',\n profile: null, // /portal/session 回應(display_name/role/libraries/graph_allowed/workflows_visible\n mode: 'keyword',\n cardId: '',\n appId: '' // Leo/Arcrun#82:目前打開的 App id#/app/{id} 路由用)\n };\n\n function esc(s) {\n return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c];\n });\n }\n function toast(msg) {\n var el = $('toast');\n el.textContent = msg;\n el.style.display = 'block';\n clearTimeout(toast._t);\n toast._t = setTimeout(function () { el.style.display = 'none'; }, 2000);\n }\n // safeJson:把回應當 JSON 解析,**解析不了就回空物件而不是拋例外**(t75 ①,2026-07-28)。\n //\n // 為什麼需要:原本各處都直接 r.json(),但伺服器不一定回 JSON——404 頁、Cloudflare 錯誤頁、\n // 反向代理的 HTML 都是純文字。JSON.parse 一爆,錯誤就沿著 .catch 走到 friendlyErr\n // **技術訊息原文被噴到畫面上**。leo 同事實測看到的就是:\n // 「Unexpected non-whitespace character after JSON at position 4」\n // 真正的原因其實是 `/portal/admin/chat-key` 回 404(實例的 cypher 是舊版沒這端點),\n // 但使用者只看得到一句看不懂的英文 ⇒ 無從判斷是自己打錯還是系統壞了。\n function safeJson(r) {\n return r.text().then(function (t) {\n if (!t) return {};\n try { return JSON.parse(t); } catch (e) { return {}; }\n });\n }\n\n // friendlyErr:給使用者看的訊息,**不外洩技術細節**(t75 ①)。\n // 原本最後一行是 `return m` =把任何例外訊息原樣顯示,包含 JSON parse 錯誤、\n // stack 片段這種對使用者毫無意義、只會嚇到人的東西。\n function friendlyErr(e) {\n var m = e && e.message ? String(e.message) : String(e);\n if (/failed to fetch|load failed|networkerror|network request failed/i.test(m)) return '連線中斷——請檢查網路後重試';\n // JSON 解析類=伺服器回了非預期內容(多半是 404/代理錯誤頁),對使用者說人話。\n if (/json|unexpected token|unexpected non-whitespace/i.test(m)) return '伺服器回應異常,請稍後再試一次(若持續發生請回報)';\n // 其餘:只在含中文(=我們自己寫的訊息)時原樣顯示;純英文技術訊息一律收斂。\n if (/[\\u4e00-\\u9fff]/.test(m)) return m;\n return '操作失敗,請稍後再試一次';\n }\n function authHeaders() { return S.token ? { 'Authorization': 'Bearer ' + S.token } : {}; }\n\n // ── 主題(console 同款,key 獨立)──\n function getTheme() { return document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'; }\n function setTheme(t) {\n document.documentElement.setAttribute('data-theme', t);\n try { localStorage.setItem('arcrun_portal_theme', t); } catch (e) { /* 私密模式存不了 */ }\n syncThemeUI();\n }\n function syncThemeUI() {\n var dark = getTheme() === 'dark';\n document.querySelectorAll('.themelabel').forEach(function (el) { el.textContent = dark ? '☀ 切淺色' : '☾ 切深色'; });\n var sw = $('st-theme-switch');\n if (sw) sw.classList.toggle('on', dark);\n }\n document.addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-themetoggle]');\n if (t) setTheme(getTheme() === 'dark' ? 'light' : 'dark');\n });\n syncThemeUI();\n\n // created_at 容錯(epoch 秒/毫秒/ISO),顯示一律台北時間(console 同款)\n function parseAtMs(v) {\n if (v == null || v === '') return null;\n if (typeof v === 'number') return v < 1e12 ? v * 1000 : v;\n if (/^\\d+$/.test(v)) { var n = Number(v); return n < 1e12 ? n * 1000 : n; }\n var s = /T/.test(v) ? v : String(v).replace(' ', 'T') + 'Z';\n var ms = Date.parse(s);\n return isNaN(ms) ? null : ms;\n }\n function fmtDate(v) { var ms = parseAtMs(v); return ms == null ? '' : taipeiDateStr(ms); }\n function fmtDateTime(v) { var ms = parseAtMs(v); return ms == null ? '' : taipeiDateTimeStr(ms); }\n\n // ── 極簡 Markdown 渲染(先全 escape 再上格式,防 XSSconsole 同款)──\n function mdInline(s) {\n return s\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<b>$1</b>')\n .replace(/(^|[^\\\\])\\x60([^\\x60]+)\\x60/g, '$1<code>$2</code>');\n }\n function mdRender(src) {\n var lines = String(src || '').split(/\\r?\\n/);\n var out = [], inCode = false, listMode = '';\n function closeList() { if (listMode) { out.push(listMode === 'ul' ? '</ul>' : '</ol>'); listMode = ''; } }\n for (var i = 0; i < lines.length; i++) {\n var raw = lines[i];\n if (/^\\s*\\x60\\x60\\x60/.test(raw)) {\n closeList();\n if (inCode) { out.push('</code></pre>'); inCode = false; }\n else { out.push('<pre><code>'); inCode = true; }\n continue;\n }\n if (inCode) { out.push(esc(raw) + '\\n'); continue; }\n var line = esc(raw);\n var h = raw.match(/^(#{1,4})\\s+(.*)$/);\n if (h) { closeList(); out.push('<h' + h[1].length + '>' + mdInline(esc(h[2])) + '</h' + h[1].length + '>'); continue; }\n if (/^\\s*&gt;\\s?/.test(line)) { closeList(); out.push('<blockquote>' + mdInline(line.replace(/^\\s*&gt;\\s?/, '')) + '</blockquote>'); continue; }\n var li = raw.match(/^\\s*[-*+]\\s+(.*)$/);\n if (li) { if (listMode !== 'ul') { closeList(); out.push('<ul>'); listMode = 'ul'; } out.push('<li>' + mdInline(esc(li[1])) + '</li>'); continue; }\n var oli = raw.match(/^\\s*\\d+[.)]\\s+(.*)$/);\n if (oli) { if (listMode !== 'ol') { closeList(); out.push('<ol>'); listMode = 'ol'; } out.push('<li>' + mdInline(esc(oli[1])) + '</li>'); continue; }\n if (/^\\s*$/.test(raw)) { closeList(); continue; }\n closeList();\n out.push('<p>' + mdInline(line) + '</p>');\n }\n closeList();\n if (inCode) out.push('</code></pre>');\n return out.join('');\n }\n\n // ── entry 顯示 helperssource/library 從 metadata_json 取——server 已 enforce 過權限)──\n function entryMeta(e) {\n try { var m = JSON.parse(e.metadata_json || 'null'); return m && typeof m === 'object' ? m : {}; } catch (er) { return {}; }\n }\n function entrySource(e) { var m = entryMeta(e); return typeof m.source === 'string' ? m.source : ''; }\n // 來源回溯超連結:PORTAL_SOURCE_WEB_BASE 有設且 source 是 gitea:// 才給 href\n // 其餘回空字串=維持純文字(行為與未設時一字不變)。chunk 錨點(#n)捨棄。\n var SOURCE_WEB_BASE = \"\";\n function encSrcPath(p) { return p.split('/').map(encodeURIComponent).join('/'); }\n // 🔴 t21(移植自已刪分支 fix/portal-source-scheme-t212026-08-05 分支整理救回):\n // 新 ingest 鏈(rag_ingest_cardrag_ingest_direct)寫的 metadata.source 是 **kb://<path>**\n // 但這裡原本只認 gitea:// ⇒ 收卡上雲後溯源連結一律失效(B4 部好溯源後一經重灌即再斷鏈)。\n // 三種 scheme 各自的正解:\n // gitea://<path> (舊 rag_ingest v2)→ {base}/<path>,行為一字不變\n // gitea:<org/repo>@<path> km-wiki-ingest 實形)→ {base}/<org/repo>/src/branch/main/<path>\n // kb://<path> (新 ingest 鏈實形)→ **雲端沒有對應網頁,回空字串=維持純文字,不造死鏈**\n function srcHref(src) {\n src = String(src);\n if (!SOURCE_WEB_BASE) return '';\n var base = SOURCE_WEB_BASE.replace(/\\/+$/, '');\n if (src.indexOf('gitea://') === 0) {\n return base + '/' + encSrcPath(src.slice(8).replace(/#\\d+$/, ''));\n }\n if (src.indexOf('gitea:') === 0) {\n var rest = src.slice(6).replace(/#[^#]*$/, '');\n var at = rest.indexOf('@');\n if (at <= 0 || at >= rest.length - 1) return '';\n return base + '/' + encSrcPath(rest.slice(0, at)) + '/src/branch/main/' + encSrcPath(rest.slice(at + 1));\n }\n return '';\n }\n // kb:// 來源的本地相對路徑(去 scheme、去錨點);非 kb:// 回空字串。\n function srcLocalPath(src) {\n src = String(src);\n if (src.indexOf('kb://') !== 0) return '';\n return src.slice(5).replace(/#[^#]*$/, '');\n }\n function entryLib(e) { var m = entryMeta(e); return (typeof m.library === 'string' && m.library) ? m.library : 'general'; }\n // 內部型別 → 人話標籤(2026-08-07entry_type 原始值如 wiki_card / block / execution_log\n // 是資料庫內部分類,不是用戶該懂的詞——尤其 wiki_card 直接違背上傳頁自己講的「AI 整理後\n // 會以 wiki 卡形式出現」,若卡片上貼的標籤是英文 snake_case「wiki_card」,等於自打嘴巴。\n // 未知型別一律落地成中性的「筆記」,不吐原始英文字串給使用者。\n var ENTRY_TYPE_LABEL = {\n wiki_card: '知識卡', block: '知識卡', value: '記錄', workflow: '工作流',\n execution_log: '執行紀錄', todo: '待辦', inbox: '收件', user_template: '範本',\n recipe_submission: '投稿', agent_feedback: '回饋'\n };\n function entryTypeLabel(t) { return ENTRY_TYPE_LABEL[t] || '筆記'; }\n // 搜尋模式的原始值(keyword/semantic)是 API 參數,不是用戶詞彙——一律轉中文再顯示。\n var SEARCH_MODE_LABEL = { keyword: '關鍵字', semantic: '語意', graph: '圖譜' };\n function searchModeLabel(m) { return SEARCH_MODE_LABEL[m] || m; }\n function entryTitle(e) {\n if (e.page_name) return e.page_name;\n var first = String(e.content || '').split(/\\r?\\n/).find(function (l) { return l.trim(); }) || '';\n first = first.replace(/^#+\\s*/, '').replace(/^[-*>]\\s*/, '').trim();\n if (first.length > 60) first = first.slice(0, 60) + '…';\n return first || '(無標題・' + entryTypeLabel(e.entry_type) + '';\n }\n function entrySnippet(e) {\n var lines = String(e.content || '').split(/\\r?\\n/).filter(function (l) { return l.trim(); });\n var body = lines.slice(1).join(' ').replace(/\\s+/g, ' ').trim();\n if (!body) body = lines.join(' ');\n if (body.length > 140) body = body.slice(0, 140) + '…';\n return body;\n }\n\n // ── 路由 ──\n // Leo/Arcrun#82 App 系統 v0K5):'app' 是**唯一**代表「隨便哪個已安裝 App」的 view——\n // 具體是哪一個由 S.appIdroute 的 arg)決定,同 'card' 用 S.cardId 的既有模式。\n // 這樣往後裝第 N 個 App 不必再幫它加一個新的 view/route,本檔維持 0 行改動(K5 驗收)。\n var VIEWS = ['launcher', 'search', 'map', 'card', 'market', 'upload', 'dev', 'workflows', 'admin', 'app', 'settings'];\n var HOME = 'launcher';\n // Arcrun#152:四頁簽 OS 級導覽——每個 view 歸屬哪一個頁簽(用來高亮 #sidenav/#tabbar\n // 的四個 data-ostab 按鈕),以及每個頁簽按鈕點下去要落在哪個 view(其餘子頁一律靠\n // 頁內的磚塊/subnav chip 進入,不需要每個子頁都在頁簽列出現)。\n var OS_TAB_OF_VIEW = {\n launcher: 'apps', search: 'apps', map: 'apps', card: 'apps', app: 'apps',\n market: 'market',\n settings: 'settings', admin: 'settings',\n dev: 'dev', workflows: 'dev', upload: 'dev',\n };\n var OS_TAB_HOME = { apps: 'launcher', market: 'market', settings: 'settings', dev: 'dev' };\n function allowedViews() {\n // 工作流/管理/上傳頁按 session 能力顯示(真閘在 server/portal/data/workflows 403/404、\n // /portal/admin/* role 閘 403、/portal/data/upload 未啟用 404——前端藏只是 UX,curl 直打也繞不過)\n return VIEWS.filter(function (v) {\n if (v === 'workflows') return !!(S.profile && S.profile.workflows_visible);\n if (v === 'admin') return !!(S.profile && S.profile.role === 'admin');\n if (v === 'upload') return !!(S.profile && S.profile.upload_enabled);\n return true;\n });\n }\n function currentRoute() {\n var h = location.hash.replace(/^#\\/?/, '');\n var seg = h.split('/');\n // App 頁的網址是 #/app/{id}——id 是動態的,不在 allowedViews() 的固定清單裡,\n // 所以在一般判斷之前先攔下來(同 'card' 用 arg 帶 cardId 的既有模式)。\n if (seg[0] === 'app') return { raw: 'app', view: 'app', arg: seg.slice(1).join('/') };\n var ok = allowedViews();\n return { raw: seg[0] || '', view: ok.indexOf(seg[0]) >= 0 ? seg[0] : HOME, arg: seg.slice(1).join('/') };\n }\n function nav(view, arg) {\n location.hash = '#/' + view + (arg ? '/' + encodeURIComponent(arg) : '');\n }\n function showView(name) {\n VIEWS.forEach(function (v) { $('v-' + v).classList.toggle('on', v === name); });\n // Arcrun#152:離開 App 頁就把 App 的畫面整個卸下來(見 unmountAppUi 的說明)。\n // `name === 'app'` 時不動——renderAppsNav() 會為了補高亮再叫一次 showView('app')\n // 那一次刻意「不重新整理內容」。\n if (name !== 'app') { try { unmountAppUi(); } catch (e) { /* 卸不掉不擋切頁 */ } }\n // 'card' 借用 'search' 的高亮(既有慣例);'app' 借用動態塞進去的\n // `data-nav=\"app:{id}\"` 那一格——同一時刻只會有一個 App 頁在看,不需要每個 App\n // 各自維護一個 view 容器(同一份 v-app,內容由 renderAppView 換掉)。\n // 🔴 2026-08-24Arcrun#152):選取器從 `#sidenav .nav, #tabbar .tab` 放寬成全文件\n // `[data-nav]`——subnav chip(設定/管理)與 App 開發頁的清單卡片現在活在 `main` 內容\n // 裡,不再只活在側欄/tabbar 兩個容器裡,範圍要跟著放寬,否則這些新位置永遠不會高亮。\n var activeKey = name === 'card' ? 'search' : (name === 'app' ? ('app:' + S.appId) : name);\n document.querySelectorAll('[data-nav]').forEach(function (el) {\n el.classList.toggle('on', el.getAttribute('data-nav') === activeKey);\n });\n var osTab = OS_TAB_OF_VIEW[name] || 'apps';\n document.querySelectorAll('[data-ostab]').forEach(function (el) {\n el.classList.toggle('on', el.getAttribute('data-ostab') === osTab);\n });\n window.scrollTo(0, 0);\n }\n var LOADERS = {\n card: loadCard, map: loadMap, workflows: loadWorkflows, admin: loadAdmin, app: loadApp, settings: loadSettings,\n launcher: renderAppsNav, market: renderAppsNav,\n };\n function route() {\n // 🔴 D62`#/reset?token=…` **不是站內的一個 view**,是「還沒登入的人拿著連結進來」。\n // 少了這一段,下面那行正規化會把不認得的 `reset` 換成 HOME ⇒ **連 token 一起丟掉**,\n // 使用者點信裡的連結會落在搜尋頁、而且再也回不去(連結一次有效,已經耗掉了)。\n // 實測(2026-08-10 stage,瀏覽器):網址被改寫成 `#/search`、修改密碼畫面根本沒出現。\n var rt = resetTokenFromUrl();\n if (rt) { showReset(rt); return; }\n var r = currentRoute();\n if (r.raw && r.raw !== r.view) { location.hash = '#/' + r.view; return; }\n if (r.view === 'card' && r.arg) S.cardId = decodeURIComponent(r.arg);\n if (r.view === 'app' && r.arg) S.appId = decodeURIComponent(r.arg);\n showView(r.view);\n if (LOADERS[r.view]) LOADERS[r.view]();\n }\n window.addEventListener('hashchange', route);\n document.addEventListener('click', function (ev) {\n // OS 頁簽按鈕(#sidenav/#tabbar 的四個 data-ostab):點下去落在該頁簽的落地頁。\n var ot = ev.target.closest('[data-ostab]');\n if (ot) { nav(OS_TAB_HOME[ot.getAttribute('data-ostab')] || 'launcher'); return; }\n var t = ev.target.closest('[data-nav]');\n if (!t) return;\n var dn = t.getAttribute('data-nav');\n // App 磚塊/清單項目用 \"app:{id}\" 帶身分(K5:動態塞進來的,不是固定 view 清單裡的一員)。\n if (dn && dn.indexOf('app:') === 0) { nav('app', dn.slice(4)); return; }\n nav(dn);\n });\n // ── 出口(Arcrun#152leo 2026-08-24 第二輪)────────────────────────────────\n // 「**只有筆記有 nav bar,所有的 App 都要顯示,不然他要怎麼回 home?**」\n //\n // 第一輪的修法只在 App 頁(`#v-app`)手寫了一顆返回鍵——而使用者從九宮格點出去的\n // **不只是 App**:搜尋、知識總圖、卡片、市集⋯⋯全都是「我從首頁點開的東西」,\n // 而它們一顆出口都沒有。leo 就是打開總圖之後卡在那裡。\n // 🔴 病根不是「漏了總圖」,是**出口被做成個案**。所以這裡改成一條規則:\n // **凡不是首頁的 view,頁首第一格一律是同一顆「← 返回」**,由這支統一塞。\n // 新增 view 只要有 `.pagehead` 就自動有出口,不必有人記得補——\n // 「下一個人不需要知道任何事就會做對」跟樣式那件事是同一個判準。\n function ensureBackControls() {\n VIEWS.forEach(function (v) {\n if (v === HOME) return; // 首頁自己就是目的地\n var head = document.querySelector('#v-' + v + ' .pagehead');\n if (!head || head.querySelector('[data-portal-back]')) return; // 冪等,重複呼叫不會長出第二顆\n var b = document.createElement('button');\n b.className = 'btn3 backbtn';\n b.setAttribute('data-portal-back', v);\n b.setAttribute('title', '回首頁(App 界面)');\n b.textContent = '← 返回';\n head.insertBefore(b, head.firstChild); // 位置固定=永遠是頁首第一格\n });\n }\n ensureBackControls();\n // 走 nav() ⇒ 同時留一筆瀏覽器歷史,手機的系統「上一頁」也有效。\n document.addEventListener('click', function (ev) {\n if (ev.target.closest('[data-portal-back]')) nav(HOME);\n });\n\n // Arcrun#152:「上傳你的 App」「叫 AI 幫你做一個 App」兩個佔位入口——刻意不掛\n // data-nav(沒有目的地可去),點下去老實說「尚未接後端」,不假裝能用。\n ['market-upload-banner', 'dev-ai-banner'].forEach(function (id) {\n var el = $(id);\n if (el) el.addEventListener('click', function () { toast('尚未接後端,先佔位'); });\n });\n\n // ── 認證流 ──\n function showAuth() {\n $('v-firstsetup').classList.remove('on');\n $('v-login').classList.add('on');\n $('shell').classList.remove('on');\n $('tabbar').classList.remove('on');\n // t49:全新實例(還沒有任何帳號)→ 換顯示「首次設定」,不讓用戶對著登入殼發呆。\n // 探測走 console/auth-statusconfigured 布林,console 首設同一顆;失敗就保持登入殼=誠實降級)。\n fetch(API_BASE + '/console/auth-status')\n .then(function (r) { return safeJson(r); })\n .then(function (d) {\n if (d && d.configured === false) {\n $('v-login').classList.remove('on');\n $('v-firstsetup').classList.add('on');\n }\n })\n .catch(function () { /* 探測不到就維持登入殼 */ });\n }\n function showApp() {\n $('v-login').classList.remove('on');\n $('v-firstsetup').classList.remove('on');\n $('shell').classList.add('on');\n $('tabbar').classList.add('on');\n // t53leo 07-25:「進站要做的事——給金鑰、下載 daemon——不然這個安裝沒完成」):\n // 常駐「完成安裝」清單卡,三件做完才消失(localStorage 記進度;admin 才看得到——\n // 金鑰與 daemon 設定是裝機者的事,同事帳號不顯示)。\n try { renderSetupChecklist(); } catch (e) { /* 清單卡失敗不擋主流程 */ }\n var p = S.profile || {};\n $('side-foot').innerHTML = esc(p.display_name || '') + '<br>' + esc(location.host);\n // Arcrun#152:原本每個能力在側欄與 tabbar 各佔一格(nav-Xtab-X 各一份要同步 hide),\n // 現在子頁的清單卡片/subnav chip 活在 `main` 內容裡,桌機/手機共用同一份 DOM\n // (由既有的 700px/1024px CSS 斷點決定要不要顯示 #sidenav/#tabbar,不是各自複製一份)\n // ⇒ 每個能力只剩一顆元素要切 hide,tab-* 那份不再存在。\n $('nav-workflows').classList.toggle('hide', !p.workflows_visible);\n // 上傳:一般用戶可見,但只在實例啟用上傳(bindings 齊全)時顯示(未啟用的實例零影響)\n $('nav-upload').classList.toggle('hide', !p.upload_enabled);\n $('nav-admin').classList.toggle('hide', p.role !== 'admin');\n $('mode-graph').classList.toggle('hide', !p.graph_allowed);\n var libs = p.libraries || [];\n $('se-scope').textContent = libs.indexOf('*') >= 0 ? '範圍:全部知識庫' : ('範圍:' + libs.join('・'));\n // Leo/Arcrun#82 App 系統 v0(K5):清單一律從這裡動態撈,不再有第二份硬編清單。\n // Arcrun#152:渲染目標從側欄/tabbar 換成「App 界面」磚塊格與「App 市集」已安裝清單。\n try { renderAppsNav(); } catch (e) { /* 清單讀不到不擋主流程(同 setup checklist 慣例) */ }\n if (!location.hash) location.hash = '#/' + HOME;\n route();\n }\n // t161leo 07-31 實撞:「實際上根本沒連上任何庫,清單空白」):session 過期時\n // 舊行為=靜默清 token 跳回登入頁,**畫面不說任何原因** ⇒ 用戶以為「庫不見了/系統壞了」。\n // 友善=前端:踢回登入頁時一定要說「為什麼」,用戶才知道下一步做什麼(重新登入即可,資料都在)。\n function dropSession(reason) {\n S.token = '';\n S.profile = null;\n try { localStorage.removeItem('arcrun_portal_session'); } catch (e) { /* noop */ }\n showAuth();\n var msg = reason || '你的登入已過期,請重新登入(你的資料都還在,不會遺失)。';\n try {\n var el = $('login-status');\n if (el) el.textContent = msg;\n } catch (e) { /* 登入殼還沒渲染就算了 */ }\n }\n function boot() {\n // D62:信裡的「修改密碼」連結最優先——這個人**就是進不去才收到這封信的**,\n // 不可以因為瀏覽器裡剛好還留著一個舊 token 就把他丟去別的畫面。\n var rt = (typeof resetTokenFromUrl === 'function') ? resetTokenFromUrl() : '';\n if (rt) { showReset(rt); return; }\n if (!S.token) { showAuth(); return; }\n fetch(API_BASE + '/portal/session', { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n // 🔴 arcrun-rag#66**只有 401 才算「你被登出了」**。\n // 舊版是 `if (!x.ok) dropSession()` 任何非 2xx 都清掉 token——\n // 包含改完密碼後那幾十秒的 503(認證 secret 正在鋪開)與 502(KBDB 暫時不可達)。\n // 那正是 leo 08-10「改完密碼、重新整理就回不去」的最後一哩:\n // 後端就算不刪 KV,前端自己把鑰匙丟了,結果一樣。\n if (x.status === 401) { dropSession(); return; }\n if (!x.ok) {\n // 暫時性故障:留著 session,告訴他這是暫時的、下一步做什麼。\n showAuth();\n $('login-status').textContent = (x.d && x.d.code === 'auth_store_propagating')\n ? '認證資料正在更新中(通常幾十秒),請稍候重新整理——你並沒有被登出。'\n : '服務暫時不可用,請稍後重新整理(你的登入沒有失效)。';\n return;\n }\n S.profile = x.d;\n showApp();\n })\n .catch(function () {\n // 斷網時不清 session(誠實:連不上 ≠ session 失效),顯示登入殼並提示\n showAuth();\n $('login-status').textContent = '連線中斷——請檢查網路後重新整理';\n });\n }\n $('login-submit').addEventListener('click', doLogin);\n $('login-password').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') doLogin(); });\n function doLogin() {\n var email = $('login-email').value.trim();\n var password = $('login-password').value;\n if (!email || !password) { $('login-status').textContent = '請輸入 Email 與密碼'; return; }\n $('login-submit').disabled = true;\n $('login-status').textContent = '';\n fetch(API_BASE + '/portal/login', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: email, password: password })\n })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n $('login-submit').disabled = false;\n if (!x.ok) { $('login-status').textContent = x.d.error || '登入失敗'; return; }\n S.token = x.d.session_token;\n try { localStorage.setItem('arcrun_portal_session', S.token); } catch (e) { /* 私密模式只活本次 */ }\n $('login-password').value = '';\n boot();\n })\n .catch(function (e) { $('login-submit').disabled = false; $('login-status').textContent = friendlyErr(e); });\n }\n // t72 OS 分流(2026-07-27leo:「客戶是用 windows 的」)——在此之前四處寫死「下載 Mac 版」,\n // Windows 客戶按下去會拿到 Mac 的 .app=按了連到錯的東西(leo 判準:那不算友善)。\n // 判不出 OS 時**兩個都給**,不替用戶猜;且無論判成哪個,頁面都留「不是這個系統?」的另一版連結\n // ——UA 會判錯,判錯時用戶要有路可走(施工圖 §3 注意事項 1、3)。\n var DAEMON_BASE_DEFAULT = 'https://raw.githubusercontent.com/youlinhsieh/arcrun-rag-bundles/main/daemon/';\n // 🔴 2026-08-05 leo:「Mac 強調要包裝成 application + dmg 的格式,拖進去就會放到\n // application,**解決之前直接在下載資料夾啟動造成更新問題**」\n // ⇒ Mac 一律給 **DMG**(開啟後是「把 Arcrun 拖進 Applications」的標準畫面),\n // 不再給 zip——zip 解開就是 .app,使用者很可能直接在「下載」資料夾雙擊啟動,\n // 而自更新會蓋錯位置(t184 Oscar 的病)。\n // ⚠️ 用**固定檔名**(不帶版號),否則每出一版都要改這裡的 code。\n //\n // 🔴 2026-08-06 升級(leo:「Portal 和 rag.arcrun.dev 應該顯示同步器的版本⋯⋯\n // **連我都沒辦法確認**,所以用戶到底是否最新版他自己也不知道」):\n // 下面兩個常數**降級為退路**,正常情況改向 `/api/latest` 取\n // `daemon.version` 與 `daemon.downloads`(真相源=bundles 的 manifest)。\n // ⚠️ 這**沒有違背**上面那條「不要每出一版就改 code」——網址現在是**取來的**,\n // 一樣不用改 code;而且順便解掉固定別名的兩個老問題:\n // ① 別名指向 @main,會吃到 CDN/ref 快取拿到舊檔(08-04 撞過)\n // ② 檔名不帶版號 ⇒ 頁面上無從顯示「這是哪一版」=leo 這次抱怨的正題\n // 取不到就退回這兩個固定檔名,按鈕不會變死連結。\n var DAEMON_MAC = 'ArcrunRAG-mac.dmg';\n var DAEMON_WIN = 'ArcrunRAG-win-unsigned.zip';\n // 由 /api/latest 填入(見下方 loadDaemonLatest);null=還沒取到或取不到。\n var DAEMON_LATEST = null;\n // Mac 那顆 > jsDelivr 單檔 20MB 上限(實測回 \"File size exceeded...\")→ 一律走 raw\n // Windows 同走 raw 保持單一來源、少一個會壞的地方。\n function daemonBase() {\n var cfg = (window.ARCRUN_CONFIG || {});\n if (cfg.daemonBase) return String(cfg.daemonBase).replace(/\\/?$/, '/');\n // 舊 key 相容(施工圖 §3 注意事項 2):daemonDownload 是「單一檔案網址」,\n // 有設就尊重它當 Mac 版網址,並由它推出目錄給 Windows 版用。\n if (cfg.daemonDownload) {\n var s = String(cfg.daemonDownload);\n return s.slice(0, s.lastIndexOf('/') + 1) || DAEMON_BASE_DEFAULT;\n }\n return DAEMON_BASE_DEFAULT;\n }\n function daemonPick() {\n var base = daemonBase();\n var ua = navigator.userAgent || '';\n // 手機/平板先排除:iOS 的 UA 含 \"Mac OS X\",不排除會把 iPhone 判成 Mac,\n // 讓手機用戶下載一個裝不起來的桌面 app(測到才發現,2026-07-27)。\n // 手機=判不出桌面 OS → 落到「兩個都給」,用戶回桌機再選。\n var isMobile = /iPhone|iPad|iPod|Android|Mobile/i.test(ua);\n var isWin = !isMobile && /Windows NT/i.test(ua);\n var isMac = !isMobile && /Macintosh|Mac OS X/i.test(ua) && !/Windows/i.test(ua);\n // 有取到真相源就用它的網址(帶版號、指向釘點 sha),否則退回固定別名。\n var dlm = (DAEMON_LATEST && DAEMON_LATEST.downloads && DAEMON_LATEST.downloads.mac) || (base + DAEMON_MAC);\n var dlw = (DAEMON_LATEST && DAEMON_LATEST.downloads && DAEMON_LATEST.downloads.win) || (base + DAEMON_WIN);\n var mac = { os: 'mac', label: '下載 Mac 版', url: dlm };\n var win = { os: 'win', label: '下載 Windows 版', url: dlw };\n if (isWin) return { pick: win, other: mac, sure: true };\n if (isMac) return { pick: mac, other: win, sure: true };\n return { pick: null, other: null, sure: false, mac: mac, win: win };\n }\n // 封測期第一次開啟的擋關提示(未簽章)——Mac/Windows 攔法不同,話術也不同。\n function daemonHint(os) {\n if (os === 'win') return '(封測版未簽章,Windows 第一次會跳藍色視窗擋下來——點「更多資訊」→「仍要執行」就好)';\n // DMG 版的步驟與 zip 不同:先拖進「應用程式」再從那裡開,才不會在下載資料夾啟動。\n return '(開啟後把 Arcrun 拖進「應用程式」,再從啟動台開啟;封測版未簽章,第一次請右鍵→打開)';\n }\n // 07-27:設定頁的常駐入口(下載小幫手/換 AI 金鑰)——與一次性卡片同一組 API\n // 🔴 2026-08-06 改成「先用退路畫、取到真相源再重畫」:\n // fetch 是非同步的,若等它回來才畫,網路慢時使用者會看到一顆沒有網址的按鈕。\n // ⇒ 先用固定別名畫出可用的按鈕,取到 /api/latest 後再重畫成帶版號的網址+版本行。\n // renderDaemonDownload 必須**可重複呼叫**(第二次要清掉第一次插進去的兄弟節點),\n // 否則重畫會疊出兩份「不是這個系統?」。\n function renderDaemonDownload() {\n var dl = $('st-daemon-dl');\n if (!dl) return;\n // 清掉上一輪插入的節點(用 id 標記,才不會誤刪別人的東西)\n ['st-daemon-alt', 'st-daemon-ver'].forEach(function (id) {\n var old = $(id); if (old && old.parentNode) old.parentNode.removeChild(old);\n });\n var d = daemonPick();\n var alt = document.createElement('span');\n alt.id = 'st-daemon-alt';\n alt.className = 'muted';\n alt.style.cssText = 'font-size:12.5px;margin-left:8px';\n if (d.sure) {\n dl.setAttribute('href', d.pick.url);\n dl.textContent = d.pick.label;\n // 判對了也要留另一版的路(UA 會判錯)\n alt.innerHTML = '不是這個系統?<a href=\"' + d.other.url + '\">' + d.other.label + '</a>';\n } else {\n // 判不出來=兩個都給,不預設 Mac\n dl.setAttribute('href', d.mac.url);\n dl.textContent = d.mac.label;\n alt.innerHTML = '或 <a href=\"' + d.win.url + '\">' + d.win.label + '</a>';\n }\n if (dl.parentNode) dl.parentNode.insertBefore(alt, dl.nextSibling);\n\n // 版本行:leo 要的「用戶看得出自己是不是最新版」。\n // 取不到就**不顯示**,不要編一個數字(同 landing 的原則:寧可空著也不說謊)。\n if (DAEMON_LATEST && DAEMON_LATEST.version) {\n var ver = document.createElement('div');\n ver.id = 'st-daemon-ver';\n ver.className = 'muted';\n ver.style.cssText = 'font-size:12.5px;margin-top:6px';\n ver.innerHTML = '最新的同步器版本是 <strong>' + esc(DAEMON_LATEST.version) + '</strong>'\n + '(你手上那支的版本,在同步器視窗左下角)'\n + ' · <a href=\"https://rag.arcrun.dev/docs/help/changelog/\" target=\"_blank\" rel=\"noopener\">這一版改了什麼</a>';\n if (alt.parentNode) alt.parentNode.insertBefore(ver, alt.nextSibling);\n }\n }\n\n function loadDaemonLatest() {\n // 不用 INSTALLER_ORIGIN:它宣告在本區塊之後(var 提升 ⇒ 這裡是 undefined)。\n fetch('https://install.arcrun.dev/api/latest')\n .then(function (r) { return r.ok ? r.json() : null; })\n .then(function (j) {\n if (j && j.daemon && j.daemon.version) {\n DAEMON_LATEST = j.daemon;\n renderDaemonDownload(); // 取到了才重畫\n }\n })\n .catch(function () { /* 取不到就維持退路的固定別名,按鈕仍可用 */ });\n }\n\n renderDaemonDownload();\n loadDaemonLatest();\n\n // t176leo 08-03):AI 設定的前端邏輯整段移除。\n // 雲端聊天走 Workers AI(免金鑰);地端萃取金鑰改由同步小幫手托盤「AI 設定…」自己設。\n // 原本這裡有 Claude 勾選框的 enable/disable 邏輯——它依賴 daemon 回報 has_claude\n // 而 daemon 從未實作該回報 ⇒ 勾選框恆為 disabled(leo 實測「hover 變可點但點擊沒反應」)。\n\n $('st-logout').addEventListener('click', function () {\n fetch(API_BASE + '/portal/logout', { method: 'POST', headers: authHeaders() }).catch(function () { /* 盡力而為 */ });\n dropSession();\n });\n\n // 檢修孔前端已於 2026-08-09 移除(t213,leo 拍板「通知完畢可以刪除」)——\n // #st-diag-export 元素不再存在,匯出診斷檔改在 arcrun-app(同步小幫手)\n // 「版本與更新」頁本機端做。GET /portal/data/diagnostics 端點本身未刪(無害、\n // 已無任何 UI 呼叫),只是這裡不再掛按鈕去打它。\n\n // t87 07-28 leo:複製任意網址到剪貼簿的共用 helper08-09 從 copyOriginUrl 拆出\n // copyText,讓 MCP 網址這種「不是 location.origin」的字串也能共用同一套按鈕行為)。\n function copyText(btn, url) {\n var orig = btn.textContent;\n if (!navigator.clipboard || !navigator.clipboard.writeText) {\n alert('請手動選取並複製:' + url);\n return;\n }\n navigator.clipboard.writeText(url).then(function () {\n btn.textContent = '已複製';\n setTimeout(function () { btn.textContent = orig; }, 1500);\n }).catch(function () {\n alert('請手動選取並複製:' + url);\n });\n }\n function copyOriginUrl(btn) { copyText(btn, location.origin); }\n (function () {\n ['st-origin-url', 'ad-origin-url'].forEach(function (id) {\n var el = $(id); if (el) el.textContent = location.origin;\n });\n ['st-copy-url', 'ad-copy-url'].forEach(function (id) {\n var el = $(id); if (el) el.addEventListener('click', function () { copyOriginUrl(this); });\n });\n })();\n\n // 08-09arcrun-rag#7):MCP 網址=apiBase 換一個 worker 名字(arcrun-cypher-executor →\n // arcrun-mcp),同一顆自架帳號的 workers.dev 子網域(deploy.ts:386-392 部署時就是這樣組的)。\n // 不是這個形狀(例如官方多租戶自訂網域)就誠實留空,不亂猜一個貼上去會連錯的網址。\n function mcpUrlFromApiBase() {\n try {\n var u = new URL(window.ARCRUN_API_BASE);\n if (u.hostname.indexOf('arcrun-cypher-executor.') === 0) {\n u.hostname = u.hostname.replace('arcrun-cypher-executor.', 'arcrun-mcp.');\n return u.origin + '/mcp';\n }\n } catch (e) { /* apiBase 空值或格式不符時不猜 */ }\n return '';\n }\n (function () {\n var mcpUrl = mcpUrlFromApiBase();\n var el = $('st-mcp-url');\n var btn = $('st-copy-mcp-url');\n if (el) el.textContent = mcpUrl || '(尚未偵測到,請確認安裝已完成)';\n if (btn) {\n if (mcpUrl) {\n btn.addEventListener('click', function () { copyText(this, mcpUrl); });\n } else {\n btn.disabled = true;\n }\n }\n })();\n\n // ── t53 完成安裝清單(進站必見,做完才消失)───────────────────────────────\n // 件數演進:t53 三件 → t54 兩件(設定檔改由小幫手憑帳密自取)\n // → arcrun-rag#81 一件(AI 問答改走 Workers AI,不再要用戶自備金鑰)。\n function setupSteps() {\n try { return JSON.parse(localStorage.getItem('arcrun_setup_steps') || '{}'); } catch (e) { return {}; }\n }\n function markStep(k) {\n var s = setupSteps(); s[k] = 1;\n try { localStorage.setItem('arcrun_setup_steps', JSON.stringify(s)); } catch (e) { /* noop */ }\n renderSetupChecklist();\n }\n function renderSetupChecklist() {\n var old = document.getElementById('setup-checklist');\n if (old) old.remove();\n var p = S.profile || {};\n if (p.role !== 'admin') return;\n var s = setupSteps();\n // arcrun-rag#81leo 08-12:「已經改用 workers AI,刪掉」):只剩「下載小幫手」一件。\n // 舊的 s.key(貼 Google AI 金鑰)已整條移除,見下方 innerHTML 處的說明。\n // 相容:舊瀏覽器 localStorage 裡殘留的 s.key 不再被讀 —— 沒設過 key 的人也不會被卡住。\n if (s.daemon) return; // t54 起設定改由小幫手輸入帳密自取,不再下載檔案\n var cfg = window.ARCRUN_CONFIG || {};\n var dpick = daemonPick(); // t72 OS 分流(同一組判定,見上面 daemonPick\n var daemonUrl = dpick.sure ? dpick.pick.url : dpick.mac.url;\n var row = function (done, id, html) {\n return '<div style=\"display:flex;gap:10px;align-items:baseline;margin-top:10px\">'\n + '<span>' + (done ? '✅' : '⬜') + '</span><div style=\"flex:1\">' + html + '</div></div>';\n };\n var el = document.createElement('div');\n el.id = 'setup-checklist';\n el.style.cssText = 'position:fixed;right:20px;bottom:20px;z-index:60;max-width:400px;width:calc(100% - 40px);padding:18px 20px;border-radius:14px;background:rgba(var(--amber-rgb),.10);border:1px solid rgba(var(--amber-rgb),.45);backdrop-filter:blur(8px);font-size:14px;line-height:1.65';\n // 🔴 arcrun-rag#81leo 08-12):第二項「啟用 AI 問答(貼 Google AI 金鑰)」整條刪除。\n // 為什麼不是「一個沒用的欄位」而已:它長在**安裝完成清單裡而且是勾選項**\n // ⇒ 用戶會以為不做這步就沒裝完,而它要人離開流程、去第三方網站申請帳號、\n // 把金鑰貼進表單——整條安裝路徑上最重的一個動作,而且是白做的。\n // 真相:雲端問答走 t18108-04)改好的 Workers AI`env.AI` binding,免金鑰)——\n // /portal/data/chat → tenant 的 rag_chat workflow → `workers_ai_chat` recipe\n // api-recipe-seeds.ts:150endpoint `@cf/meta/llama-4-scout-17b-16e-instruct`)。\n // 這裡貼的金鑰是打 POST /portal/admin/ai 存一筆雲端 gemini_api_key credential\n // **現行問答鏈路一個地方都沒有讀它**。\n // 後端 route 本身不動(同 08-09 拿掉檢修孔按鈕的處置:端點無害、已無任何 UI 呼叫,\n // 純粹清路標);已經存過金鑰的人那筆 credential 也原封不動,不做刪除。\n // ⚠️ 別把這個跟設定頁「AI 設定」面板講的地端萃取金鑰搞混——那把是填在\n // **同步小幫手**托盤裡的,從來不經過這個清單。\n el.innerHTML = '<b>還差 1 步,安裝就真的完成了</b>'\n + row(s.daemon, 'daemon',\n '<b>下載同步小幫手</b>(把資料夾變成知識庫)<br>'\n + (daemonUrl\n // t72:按鈕字樣+擋關話術都跟著 OS 走;並且一律附「另一個系統」的連結\n //(UA 判錯時用戶要有路走;判不出來時=兩個平等並列,不預設 Mac)\n ? '<a href=\"' + esc(daemonUrl) + '\" id=\"sc-daemon\">'\n + esc(dpick.sure ? dpick.pick.label : dpick.mac.label) + '</a>'\n + '<span class=\"muted\" style=\"font-size:12.5px\">'\n + esc(daemonHint(dpick.sure ? dpick.pick.os : 'mac')) + '</span>'\n + '<div class=\"muted\" style=\"font-size:12.5px\">'\n + (dpick.sure\n ? '不是這個系統?<a href=\"' + esc(dpick.other.url) + '\">' + esc(dpick.other.label) + '</a>'\n : '或 <a href=\"' + esc(dpick.win.url) + '\">' + esc(dpick.win.label) + '</a>')\n + '</div>'\n // 誠實降級(leo 判準:按了連到錯的機制=不友善):沒有可用下載點時\n // 不放死連結,說明現況並讓這步可手動打勾,不卡住清單。\n : '<span class=\"muted\" style=\"font-size:13px\">封測期由我們直接寄給你安裝檔;'\n + '收到後回來按 <a href=\"#\" id=\"sc-daemon\">我已經裝好了</a>。</span>')\n // t54(leo:「最好的就是把它的帳密直接輸入」):設定不再是一個要下載的檔案——\n // 小幫手第一次開啟會問網址+帳密,自己去換設定。\n + '<div class=\"muted\" style=\"font-size:12.5px;margin-top:4px\">裝好第一次開啟時,貼上這個網址+你的帳號密碼就連上了,不用下載設定檔。</div>')\n + '<div style=\"margin-top:10px;text-align:right\"><button id=\"sc-later\" style=\"border:none;background:none;color:inherit;cursor:pointer;font-size:12.5px;text-decoration:underline;opacity:.65\">稍後再說</button></div>';\n document.body.appendChild(el);\n var dl = document.getElementById('sc-daemon');\n if (dl) dl.addEventListener('click', function () { markStep('daemon'); });\n // t54config.json 下載鈕已移除(設定由小幫手憑帳密自取)\n // arcrun-rag#81:金鑰輸入框的送出邏輯(POST /portal/admin/ai)一併移除——\n // 只藏畫面留著那條路,等於這個要求還在,只是變得更難發現。\n var later = document.getElementById('sc-later');\n if (later) later.addEventListener('click', function () { el.remove(); }); // 只藏本次,下次進站再提醒\n }\n\n // ── t49 首次設定(leo 07-25):console/setup → portal/admin/bootstrap → 預設庫登記 → portal/login\n // 四發全是既有端點,一顆按鈕做完;用戶感受=「建一組帳密就進來了」。\n $('fs-submit').addEventListener('click', doFirstSetup);\n $('fs-password2').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') doFirstSetup(); });\n $('fs-tologin').addEventListener('click', function () {\n $('v-firstsetup').classList.remove('on');\n $('v-login').classList.add('on');\n });\n function doFirstSetup() {\n var email = $('fs-email').value.trim();\n var pw = $('fs-password').value;\n var st = $('fs-status');\n if (!email || pw.length < 8) { st.textContent = '請填 Email,密碼至少 8 碼'; return; }\n if (pw !== $('fs-password2').value) { st.textContent = '兩次密碼不一樣'; return; }\n $('fs-submit').disabled = true; st.textContent = '';\n var post = function (path, body, hdrs) {\n return fetch(API_BASE + path, {\n method: 'POST',\n headers: Object.assign({ 'Content-Type': 'application/json' }, hdrs || {}),\n body: JSON.stringify(body)\n }).then(function (r) { return r.json().catch(function () { return {}; }).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); });\n };\n post('/console/setup', { email: email, password: pw })\n .then(function (x) {\n // 已設定過(409)=這實例其實有人建過了——自動帶去登入,別把用戶困在死路(leo 實撞)\n if (x.status === 409) {\n $('fs-submit').disabled = false;\n $('v-firstsetup').classList.remove('on');\n $('v-login').classList.add('on');\n $('login-email').value = email;\n $('login-status').textContent = '這個知識庫已經設定過帳號了,直接登入即可。';\n throw { handled: true };\n }\n if (!x.ok || !x.d.session_token) throw new Error(x.d.error || '設定沒有成功,請再試一次');\n var ownerTok = x.d.session_token;\n return post('/portal/admin/bootstrap', { email: email, password: pw, display_name: email.split('@')[0] }, { Authorization: 'Bearer ' + ownerTok })\n .then(function (b) {\n // 409=已 bootstrap 過(冪等視為就緒);其餘失敗誠實丟出\n if (!b.ok && b.status !== 409) throw new Error(b.d.error || '初始化沒有成功,請再試一次');\n return post('/portal/login', { email: email, password: pw });\n });\n })\n .then(function (l) {\n if (!l.ok || !l.d.session_token) throw new Error(l.d.error || '帳號建好了,但自動登入沒成功——請用剛設定的帳密登入');\n S.token = l.d.session_token;\n try { localStorage.setItem('arcrun_portal_session', S.token); } catch (e) { /* noop */ }\n })\n .then(function () {\n $('fs-submit').disabled = false;\n try { localStorage.setItem('arcrun_onboarding', '1'); } catch (e) { /* noop */ }\n boot();\n })\n .catch(function (e) {\n if (e && e.handled) return; // 409 已自動切登入,不再顯示錯誤\n $('fs-submit').disabled = false;\n st.textContent = (e && e.message) || friendlyErr(e);\n });\n }\n\n // 任何 data 請求收到 401 → session 失效 → 回登入殼\n //\n // 🔴 arcrun-rag#66 的前端那一半:後端在「認證資料正在鋪開」時改回 503\n // `auth_store_propagating`(不再回 401、也不再刪 KV 那筆 session)。\n // 這裡**必須跟著只認 401**——若前端把任何錯誤都當登出,後端不刪也沒用:\n // 使用者手上的 token 會被自己的瀏覽器丟掉,症狀跟被踢出去一模一樣。\n // (503 由各呼叫點自己顯示錯誤訊息,session 原封不動。)\n function guard401(status) {\n if (status === 401) { dropSession(); return true; }\n return false;\n }\n\n // ── 搜尋 ──\n document.querySelectorAll('.modebtn').forEach(function (b) {\n b.addEventListener('click', function () {\n S.mode = b.getAttribute('data-mode');\n document.querySelectorAll('.modebtn').forEach(function (x) { x.classList.toggle('on', x === b); });\n if ($('se-q').value.trim()) doSearch();\n });\n });\n $('se-go').addEventListener('click', doSearch);\n $('se-q').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') doSearch(); });\n function doSearch() {\n var q = $('se-q').value.trim();\n if (!q) { $('se-count').textContent = '請輸入查詢字'; return; }\n $('se-banner').innerHTML = '';\n if (S.mode === 'graph') { doGraphSearch(q); return; }\n $('se-graphout').style.display = 'none';\n $('se-results').style.display = '';\n $('se-count').textContent = '查詢中…';\n $('se-results').innerHTML = '';\n var url = API_BASE + '/portal/data/search?q=' + encodeURIComponent(q) + '&mode=' + (S.mode === 'semantic' ? 'semantic' : 'keyword');\n fetch(url, { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { $('se-count').innerHTML = '<span class=\"err\">' + esc(x.d.error || ('查詢失敗(HTTP ' + x.status + '')) + '</span>'; return; }\n var d = x.d;\n if (S.mode === 'semantic' && d.mode === 'keyword') {\n // 🔴 2026-08-09 leo:「語義搜尋已經確定是一安裝就提供的功能⋯⋯我沒有不開通這個\n // 功能,是壞了,沒有人會把 bug 美化成沒提供沒開通。」\n // 走到這裡=這台實例的語意搜尋壞了(缺 binding 或向量化失敗),照實說是故障、\n // 是我們的問題,不要求使用者做任何事。文案優先用後端 capability_hint(已是人話,\n // 能分「暫時故障/部署故障」),舊版後端沒有就用底下的通用故障文案。\n var bhint = (d.capability_hint && !/開通|尚未啟用/.test(d.capability_hint))\n ? d.capability_hint\n : '語意搜尋目前故障,先用關鍵字幫你找了下面的結果。這是我們系統的問題,不是你的操作問題,你不需要做任何事,我們會修好它。';\n $('se-banner').innerHTML = '<div class=\"honest\" style=\"margin-top:18px\"><div class=\"h\">語意搜尋目前故障</div><div class=\"b\">' + esc(bhint) + '</div></div>';\n }\n var entries = d.entries || [];\n $('se-count').textContent = '命中 ' + entries.length + ' 筆・模式 ' + searchModeLabel(d.mode || 'keyword') + (d.note ? '・' + d.note : '');\n if (!entries.length) {\n // 空結果不一律怪查詢字:語意模式的空結果,後端 capability_hint 會分\n // 「真的沒命中(換字)」與「索引故障/還沒有資料(不是用戶的問題)」,照實顯示。\n // 降級(mode=keyword)時故障說明已在上方橫幅,這裡不重複。\n var emptyMsg = (d.capability_hint && d.mode === 'semantic')\n ? esc(d.capability_hint)\n : '找不到「' + esc(q) + '」——換個關鍵字試試。';\n $('se-results').innerHTML = '<div class=\"muted\" style=\"padding:30px 10px;text-align:center;grid-column:1/-1\">' + emptyMsg + '</div>';\n return;\n }\n $('se-results').innerHTML = entries.map(function (e) {\n var src = entrySource(e);\n return '<div class=\"kcard\" data-card=\"' + esc(e.id) + '\">' +\n '<div class=\"kt\">' + esc(entryTitle(e)) + '</div>' +\n '<div class=\"ks\">' + esc(entrySnippet(e)) + '</div>' +\n '<div class=\"km\">' +\n '<span class=\"tag\">' + esc(entryTypeLabel(e.entry_type)) + '</span>' +\n '<span class=\"tag green\">' + esc(entryLib(e)) + '</span>' +\n (src ? '<span class=\"dim\" style=\"word-break:break-all\">' + esc(src) + '</span>' : '') +\n '<span class=\"dim mono\" style=\"margin-left:auto\">' + esc(fmtDate(e.created_at)) + '</span></div></div>';\n }).join('');\n })\n .catch(function (e) { $('se-count').innerHTML = '<span class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</span>'; });\n }\n $('se-results').addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-card]');\n if (t) nav('card', t.getAttribute('data-card'));\n });\n\n // ── AI 檢索過程(Arcrun#44inkstone/Arcrun#170 改寫成「只畫命中的那條路徑」)──\n // leo 原話(Arcrun#44):「在 AI 搜尋格子應看到我輸入的內容從 graph 找到幾個資料夾、\n // 從資料夾的摘要讀到的內容⋯⋯會看到他的參照原文位置檔名,而不是簡單向量搜尋的結果。」\n //\n // 🔴 leo 原話(inkstone/Arcrun#1702026-08-27):「這張圖的目的是告訴他相關文件放哪裡」\n // ⇒ 這張圖**只該在真的有命中的時候出現**,而且只畫命中的那條路徑;\n // 「索引沒有敘事」「重點實體」「三元組」「子庫」「片段 #N」這類內部詞,\n // 使用者看不懂、也不是在跟他溝通——這裡全部換成他自己資料夾裡的說法。\n //\n // 後端契約(workflows/rag-chat.local.yaml 的 finalize 節點,同一次 #170 修好):\n // retrieval 只在**這次答案真的引用了至少一筆原文**時才會非 null,\n // 而且已經收斂到「真的貢獻了被引用內容」的資料夾/頁面——不會再出現\n // 「讀過但沒用上」的陪葬清單。前端這裡不必、也不該再自己過濾一次。\n function retrievalTraceHtml(retrieval) {\n if (!retrieval || typeof retrieval !== 'object') return '';\n var route = retrieval.route || '';\n var libs = Array.isArray(retrieval.libraries) ? retrieval.libraries : [];\n var idxs = Array.isArray(retrieval.indexes_read) ? retrieval.indexes_read : [];\n var pages = Array.isArray(retrieval.pages_read) ? retrieval.pages_read : [];\n\n // 只有一句話,告訴使用者「這個答案是怎麼被找到的」——不談內部路由名詞,\n // 只談對他有意義的事:是不是靠他打的關鍵字直接命中。\n var routeTxt = route === 'graph'\n ? '根據你打的關鍵字找到的'\n : '從你的資料夾內容裡找到的';\n var head = '<div class=\"tracehead\"><span class=\"tag green\">' + esc(routeTxt) + '</span></div>';\n\n if (!libs.length && !idxs.length && !pages.length) return head;\n\n // 🌳 2026-08-18 leo:「希望以樹狀圖來展示⋯⋯就像現有的 graph search 一樣 render」。\n // 樹的**資料**在 retrievalTreeData()(唯一一份),畫法在 renderTree()(也唯一一份)。\n return head + '<details class=\"trace\" open><summary>看看是從哪裡找到的</summary>' +\n '<div class=\"trace-body\"><div id=\"tracetree\"></div></div></details>';\n }\n\n // 檢索過程 → 一棵樹(renderTree 吃的節點:{ content: HTML 字串, children: [], payload? })。\n // 形狀:搜尋詞 ├ 命中的資料夾 ├ 資料夾摘要 └ 讀入的原文段落。\n // #170 之後這裡收到的 r 已經是後端濾過的「真出處」,不再需要自己判斷哪些是陪葬。\n function retrievalTreeData(question, r) {\n var libs = Array.isArray(r.libraries) ? r.libraries : [];\n var idxs = Array.isArray(r.indexes_read) ? r.indexes_read : [];\n var pages = Array.isArray(r.pages_read) ? r.pages_read : [];\n var pagesByLib = {}, idxByLib = {}, mounted = {};\n var labels = machineLabelMap(pages);\n pages.forEach(function (p) { var k = p.library || ''; (pagesByLib[k] = pagesByLib[k] || []).push(p); });\n idxs.forEach(function (ix) { var k = ix.library || ''; (idxByLib[k] = idxByLib[k] || []).push(ix); });\n\n function node(content, children, fold) {\n var n = { content: content, children: children || [] };\n if (fold) n.payload = { fold: 1 };\n return n;\n }\n function idxNode(ix) {\n var ents = Array.isArray(ix.top_entities) ? ix.top_entities : [];\n var counts = [];\n if (typeof ix.entry_count === 'number') counts.push(ix.entry_count + ' 筆內容');\n // narrative 常常是空字串(還沒補寫摘要)——不假裝有,改把這個資料夾主要談什麼\n // (top_entities)擺出來,讓這一層仍然說得出讀到了什麼;兩者都沒有就整行不顯示。\n var main = ix.narrative\n ? esc(String(ix.narrative).slice(0, 160)) + (String(ix.narrative).length > 160 ? '…' : '')\n : (ents.length ? '<span class=\"tt-sub\">主要談:' + esc(ents.slice(0, 3).join('、')) + '</span>' : '');\n return main +\n (counts.length ? '<span class=\"ft-sep\">·</span><span class=\"tt-sub\">' + esc(counts.join('・')) + '</span>' : '');\n }\n function pageNode(p) {\n var c = srcCrumbs(p.source, p.library, p, labels);\n var trail = c.dirs.concat(c.file ? [c.file] : []).join(' ');\n var frag = c.frag && /^\\d+$/.test(c.frag) ? ('第 ' + (parseInt(c.frag, 10) + 1) + ' 段') : '';\n return node('<b class=\"tt-file\">' + esc(p.page || '(無頁名)') + '</b>' +\n (typeof p.chars === 'number' ? ' <span class=\"tt-sub\">' + esc(p.chars) + ' 字</span>' : '') +\n (c.machineLabel\n ? '<span class=\"ft-sep\">·</span><span class=\"tt-machine\" title=\"' + esc('比對鍵:' + c.machine) + '\">' + esc(c.machineLabel) + '</span>'\n : '<span class=\"ft-sep\">·</span><span class=\"tt-machine tt-unknown\">未知來源</span>') +\n (trail ? '<span class=\"ft-sep\">·</span><span class=\"tt-path\">' + esc(trail) + (frag ? '' + esc(frag) + '' : '') + '</span>' : ''), []);\n }\n function libBranch(name, meta) {\n var mine = idxByLib[name] || [];\n var myPages = (pagesByLib[name] || []).map(pageNode);\n var kids;\n if (mine.length) {\n // 原文段落掛在該資料夾的摘要底下;同一資料夾有多筆摘要時掛第一筆,其餘平列。\n kids = mine.map(function (ix, i) { return node(idxNode(ix), i === 0 ? myPages : []); });\n } else {\n // 沒有摘要就不要生一個假的中間層——原文段落直接掛在資料夾底下。\n kids = myPages;\n }\n var ents = Array.isArray(meta.matched_entities) ? meta.matched_entities : [];\n return node('<b class=\"tt-folder\">' + esc(name || '(未命名資料夾)') + '</b>' +\n (ents.length ? '<span class=\"ft-sep\">·</span><span class=\"tt-sub\">找到關鍵字:' + esc(ents.slice(0, 3).join('、')) + '</span>' : ''), kids);\n }\n\n var branches = [];\n libs.forEach(function (l) { var n = l.library || ''; mounted[n] = 1; branches.push(libBranch(n, l)); });\n // 有讀原文、但沒出現在 libraries[] 的資料夾(route=all 這條路徑會這樣)也要長出來,不能吞掉。\n Object.keys(pagesByLib).forEach(function (n) {\n if (mounted[n]) return;\n mounted[n] = 1;\n branches.push(libBranch(n, {}));\n });\n // 防禦性 fallback:正常情況下呼叫端已保證有命中才會走到這裡(retrieval 非 null\n // 時後端保證至少有一筆真出處),但這裡不假設呼叫端永遠守規矩——給一句誠實的\n // 說明而不是生一棵空樹或整段回 null(回 null 會讓下游 renderTree 拿到 null.children\n // 而炸掉,2026-08-27 總管複驗實測抓到的 TypeError 就是這裡)。\n if (!branches.length) {\n branches.push(node('<span class=\"tt-dim\">' +\n (r.route === 'all' ? '沒有資料夾命中,已讀取全部資料夾作答。' : '這次沒有回報任何資料夾。') +\n '</span>', []));\n }\n return node('<b>' + esc(question || '(無搜尋詞)') + '</b>', branches);\n }\n\n // 🌲 renderTree — **這個軟體裡唯一的樹狀渲染入口**(D97;佈局於 2026-08-19 被 leo 打回重選)。\n // 餵它的資料形狀不變:{ content: HTML 字串, children: [], payload?: { fold: 1 } }。\n //\n // 服務三個入口(同一份 render,三種資料組法):\n // ① 同步庫總覽(四層):總庫 → 機器 → 資料夾 → 子資料夾/檔案 ── adminLibsTreeData()\n // ② 單一資料夾樹:資料夾 → 子資料夾(節點右邊掛「已同步/總共」)── folderTreeData()\n // ③ 檢索過程樹:搜尋詞 → 子庫 → 索引敘事 → 原文片段 ── retrievalTreeData()\n // ①②在畫面上接成**一棵連續的樹**:②就是①的第四層,點開某個庫時才去抓\n // (見 adminLibsTreeDatafolderKidsData)。它們仍是兩支獨立的資料組法,\n // 但畫法只有這一份——這正是判準第 1 條要的「三個入口共用同一份 render」。\n // 三者的差別只在**資料怎麼組**,畫法沒有第二套。深度沒有上限(巢狀 <details>),\n // 節點內容是 HTML ⇒ 機器/資料夾/檔案/未知長得不一樣(.tt-machine.tt-folder\n // .tt-file.tt-unknown)。\n //\n // ──────────────────────────────────────────────────────────────────────────\n // 🔴 技術選型(Arcrun#144 完工判準第 4 條。leo:「你去找合適的技術,\n // **不准許把我貼給你什麼就用**,技術選型要思考符合需求」)\n //\n // 需求(leo 2026-08-19 原話):「一列一列向下往後退縮,緊湊但可點擊展開,\n // 模擬 Windows 的檔案總管的 tree」+「用滑鼠輕易操作,不會佔用大面積」。\n // D97 的紅線沒被推翻:用現成的、不自造輪胎、要能互動摺疊——被推翻的只是\n // 「現成的=markmap」。\n //\n // 這棵樹的**硬約束**(三個入口共用,少一個就不能用):\n // (a) 節點內容是任意 HTML(要分機器/資料夾/檔案/未知,還要掛徽章與計數)\n // (b) 摺疊要是現成的互動,不是我們自己寫的\n // (c) 零第三方依賴可離線(這頁由 worker 內嵌供檔,企業內網也要打得開)\n // (d) 沒有建置步驟(整個 portal 是一份手寫 HTML,沒有 bundler\n //\n // 候選與實測(2026-08-26 查 npm registryjsDelivr/原始碼,不是憑印象):\n //\n // ① markmap-view 0.18.12(現行,leo 打回)\n // ❌ 它是**心智圖**佈局(放射/左右分佈),不是檔案總管佈局。1.4.49 已證明\n // 把線改成 L 型也不會變成後者——形狀是它的核心,不是選項。\n // ❌ 附帶成本:78KB vendored、SVG 畫布要縮放平移(leo:「用戶要擁有觸控板」)、\n // 我們還得靠 MutationObserver 去改寫它的 `d`elbowifyLinks)——\n // **為了得到不是它設計目標的東西而改寫它的內部輸出,本身就是選錯的訊號**。\n //\n // ② jsTree 3.3.17leo 附的範例之一)\n // 形狀對(就是檔案總管),但:\n // ❌ `dependencies: { jquery: \"^3.5.0\" }`npm registry 實查)——這頁沒有 jQuery,\n // 為了一棵樹要吞 jQuery 87KB jstree.min.js 139KB 主題 CSS 27KB\n // 兩張雪碧圖 throbber.gifjsDelivr 實查檔案大小)= 5 個檔、約 255KB。\n // ❌ 它的節點是**單行錨點**`.jstree-anchor` 固定行高、nowrap),而我們的節點\n // 要掛多段異質 HTML;要嘛改寫它的主題,要嘛放棄 (a)。\n //\n // ③ TreeJSm-thalmann/treejsleo 附的範例之二)\n // ✅ 目標對(README 原文就是「display a TreeView like in the windows filebrowser」)、零依賴。\n // ❌ 節點只吃 `userobject` 的 `toString()` ⇒ **純文字**,違反 (a)。\n // ❌ 沒有維護訊號(無版本/無更新時間),沒有 npm 上的對應套件。\n //\n // ④ @widgetjs/tree 1.8.3(零依賴、9.6KB,本輪另外找的)\n // ❌ 讀它的 bundle 原始碼:`document.createTextNode(e.text)` ⇒ **純文字**,違反 (a)。\n // ❌ 而且每個節點強制長一個 `treejs-checkbox``selectMode: \"checkbox\"` 是預設)——\n // 那是**選取**元件,不是展示樹。\n //\n // ⑤ Shoelace `<sl-tree>` / `<sl-tree-item>`web component\n // ✅ 形狀對、節點吃 slot(任意 HTML)、鍵盤可用。\n // ❌ `dependencies` 有 lit 7 個(npm 實查);ESM 模組圖要現代 bundler 或 CDN\n // 違反 (c)(d)。摺疊箭頭在 shadow DOM 裡,要 `::part` 才改得動樣式。\n //\n // ⑥ 🏆 **瀏覽器原生 `<details>` / `<summary>`**(本次選用)\n // ✅ (a) `<summary>` 裡放任意 HTML(b) **摺疊是瀏覽器實作的**,不是我們寫的\n // ——D97「用現成的、不自造輪胎」在這裡被滿足得最徹底:現成到它是平台本身;\n // (c) 0 bytes、離線可用;(d) 無建置。\n // ✅ 滑鼠點一下展開/收合,鍵盤 Enter/Space 也行,還自帶 disclosure 的無障礙語意。\n // ✅ 這頁本來就在用它(`<details class=\"trace\">` 那個「檢視檢索過程」)⇒ 一致。\n // ⚠️ 誠實標明我們自己寫的是什麼:**只有把樹資料攤成巢狀 <details> 的那幾行字串拼接,\n // 外加縮排/導引線的 CSS。佈局引擎與摺疊行為都不是我們的。**\n // 「自造輪胎」指的是自己寫展開收合與版面計算——那個沒有發生。\n // ⚠️ 代價(要講出來):沒有動畫、沒有拖拉、沒有鍵盤上下鍵巡覽(`role=\"tree\"` 那套)。\n // 三者都不在 leo 的需求裡(他要的是「用滑鼠輕易操作」)。真要 tree 的鍵盤語意,\n // 再回頭評 ⑤。\n // ──────────────────────────────────────────────────────────────────────────\n //\n // 🔴 拿不到值的那一層要顯示成「未知」,不准拿別的東西頂替。\n // 現況:機器那一層在資料上是空的(collector 沒有存 hostnamemachine id),\n // 所以檢索過程的出處麵包屑標「機器:未記錄」;同步庫總覽同理,\n // 資料補上之後那個節點會自然長出真名,畫面這端不需要再改。\n function renderTree(box, data, footHtml) {\n if (!box) return false;\n box.innerHTML = treeHtml(data, footHtml);\n return true;\n }\n // 樹 → HTML 字串(`renderTree` 與任何想把樹嵌進更大一塊 HTML 的呼叫端共用這支)。\n // `data` 可以是一個節點,也可以是節點陣列(資料夾樹會有多個根)。\n function treeHtml(data, footHtml) {\n var roots = Object.prototype.toString.call(data) === '[object Array]' ? data : [data];\n return '<div class=\"ftree\">' + treeKidsHtml(roots)\n + (footHtml ? '<div class=\"ft-foot\">' + footHtml + '</div>' : '') + '</div>';\n }\n // 一批節點 → 「某一層的內容」。給**延後載入子樹**的呼叫端用:同步庫總覽點開某個庫\n // 才去抓它的資料夾結構,抓回來只換那一層的 `.ft-kids`,不重畫整棵樹(重畫會把使用者\n // 已經展開/收合的其他支全部彈回預設)。畫法仍是同一支 treeNodeHtml,沒有第二套。\n // 🔴 正規化只在這裡做一次(deep),treeNodeHtml 往下遞迴時**不再正規化**——\n // 兩邊都做會讓每深一層就多正規化一次整棵子樹,深樹會炸。\n function treeKidsHtml(nodes) {\n var out = '';\n for (var i = 0; i < (nodes || []).length; i++) out += treeNodeHtml(normalizeTreeNode(nodes[i]));\n return out;\n }\n // 一個節點 → 一列(有小孩就是可摺疊的 <details>,沒有就是一列 div)。\n // 縮排靠**巢狀**.ft-kids 的 padding-left),不是靠 depth 算 padding:\n // 這樣層數沒有上限,導引線也自然接得起來。\n function treeNodeHtml(n) {\n // payload.pop=這一列「滑過去才出現」的那一格(HTML 字串,呼叫端自己跳脫)。\n // 🔴 有 pop 就不掛原生 title:兩個 tooltip 會同時冒出來互相打架,\n // 而且原生 title 只吃純文字(膠囊會被壓成一長串字)——那正是本票要拿掉的東西。\n var pop = (n.payload && n.payload.pop) ? ' data-pop=\"' + esc(n.payload.pop) + '\"' : titleAttr(n.content);\n var label = '<span class=\"ft-tw\">▶</span><span class=\"ft-lbl\"' + pop + '>' + n.content + '</span>'\n // tail=行尾那一格(庫目錄的「移除」鈕就住這裡)。它**不參與截斷**:名字太長時\n // 被「…」吃掉的是名字,不是操作鈕——檔案總管也是這樣,欄位再擠右邊那格都還在。\n + (n.tail ? '<span class=\"ft-tail\">' + n.tail + '</span>' : '');\n // payload.attrs=掛在這一列容器上的額外屬性(例:同步庫總覽用 data-libtree 標出\n // 「這一支的小孩要等點開才去抓」)。呼叫端自己負責跳脫,內容與 content 同一個信任層級。\n var attrs = (n.payload && n.payload.attrs) ? ' ' + n.payload.attrs : '';\n if (!n.children.length) return '<div class=\"ft-row ft-leaf\"' + attrs + '>' + label + '</div>';\n // payload.fold=這一支預設收起來(例:「考慮過但沒讀原文」的那 7 個子庫,\n // 它們確實被讀過不隱瞞,但不該把真正命中的那條路徑擠掉)\n var open = (n.payload && n.payload.fold) ? '' : ' open';\n var kids = '';\n for (var i = 0; i < n.children.length; i++) kids += treeNodeHtml(n.children[i]);\n return '<details class=\"ft-n\"' + open + attrs + '><summary class=\"ft-row\">' + label + '</summary>'\n + '<div class=\"ft-kids\">' + kids + '</div></details>';\n }\n // 一列放不下就截斷(見 .ft-lbl 的 text-overflow),所以全文要掛在 title 上——\n // 這正是檔案總管對長檔名的做法。節點內容是 HTML,先剝掉標籤再當純文字塞回屬性。\n function titleAttr(html) {\n var txt = String(html || '').replace(/<[^>]*>/g, '').replace(/\\s+/g, ' ').trim();\n return txt ? ' title=\"' + esc(txt) + '\"' : '';\n }\n\n // ── 滑過去才出現的那一格(inkstone/Arcrun#180leo 2026-08-28)──────────────\n //\n // leo:「每個資料夾的說明又長又囉嗦⋯⋯**只要「文件」即可,其他的可以放在一個\n // moseover 彈出視窗裡**」。他截圖裡十列講的是同兩句判定語,每列被吃掉一半。\n //\n // 🔴 為什麼不是 CSS `:hover` 的子元素:`.ft-row``.ft-lbl` 都是 overflow:hidden\n // (長名字要截斷成「…」),彈出視窗長在裡面會被切掉。所以只有一顆、掛在 <body>\n // 底下、position:fixed,由這裡依那一列的位置擺——與 `.ft-tail` 那顆鈕同一個道理:\n // 會被截斷影響的東西,就不要放進會被截斷的容器。\n // 🔴 只有一顆共用的元素(不是每列生一個):一棵樹可能上百列,每列掛一顆\n // 等於把 DOM 撐大一倍,而同時間只看得到一顆。\n var ftPop = null, ftPopTimer = null;\n function ftPopHide() {\n if (ftPopTimer) { clearTimeout(ftPopTimer); ftPopTimer = null; }\n if (ftPop) ftPop.style.display = 'none';\n }\n function ftPopShow(el) {\n var html = el.getAttribute('data-pop');\n if (!html) return;\n if (!ftPop) {\n ftPop = document.createElement('div');\n ftPop.className = 'ft-pop';\n document.body.appendChild(ftPop);\n }\n ftPop.innerHTML = html;\n ftPop.style.display = 'block';\n // 先擺再量(要拿到真實高度才知道下面塞不塞得下)\n var r = el.getBoundingClientRect(), p = ftPop.getBoundingClientRect();\n var left = Math.max(8, Math.min(r.left, window.innerWidth - p.width - 8));\n var top = r.bottom + 6;\n if (top + p.height > window.innerHeight - 8) top = Math.max(8, r.top - p.height - 6);\n ftPop.style.left = left + 'px';\n ftPop.style.top = top + 'px';\n }\n // 委派:樹會被整棵重畫(點開某個庫、移除某個庫),每次重綁事件遲早會漏綁一處。\n document.addEventListener('mouseover', function (ev) {\n var t = ev.target;\n var hit = (t && t.closest) ? t.closest('[data-pop]') : null;\n if (!hit) { ftPopHide(); return; }\n if (ftPopTimer) clearTimeout(ftPopTimer);\n // 150ms 的延遲=滑鼠橫越一棵樹時不會沿路炸出一串彈窗(tooltip 的通例)\n ftPopTimer = setTimeout(function () { ftPopShow(hit); }, 150);\n });\n window.addEventListener('scroll', ftPopHide, true);\n window.addEventListener('resize', ftPopHide);\n function mountRetrievalTree(question, retrieval) {\n var box = $('tracetree');\n if (!box || !retrieval || typeof retrieval !== 'object') return;\n var data;\n try { data = retrievalTreeData(question, retrieval); }\n catch (e) { box.innerHTML = '<div class=\"dim\" style=\"font-size:13.5px;padding:10px\">檢索過程畫不出來:' + esc(friendlyErr(e)) + '</div>'; return; }\n // data 為 null=防禦性 fallback(正常情況下呼叫端已保證有命中才會走到這裡);\n // 沒有東西可畫就把外層 <details> 藏起來,不留一棵空樹。\n if (!data) { var det = box.closest('details'); if (det) det.style.display = 'none'; return; }\n renderTree(box, data);\n }\n function normalizeTreeNode(n) {\n var kids = (n && n.children) || [];\n var out = { content: (n && n.content) || '', children: [] };\n if (n && n.payload) out.payload = n.payload;\n if (n && n.tail) out.tail = n.tail; // 行尾那一格(操作鈕)——不參與截斷,見 treeNodeHtml\n for (var i = 0; i < kids.length; i++) out.children.push(normalizeTreeNode(kids[i]));\n return out;\n }\n // 這棵樹**一開始**會佔幾列(收合的那一支只算它自己那一列)。\n // 🔴 這不是裝飾用的統計:Arcrun#144 判準第 2 條是「1080p 桌機不捲動看得完\n // 8 子庫 × 17 片原文」,而「看得完」= 列數 × 一列 23px 要塞得進畫面。\n // 改 .ft-row 的 paddingfont-size 之前,先用這支把列數乘回去算那筆帳。\n function visibleRows(n) {\n if (!n.children || !n.children.length) return 1;\n if (n.payload && n.payload.fold) return 1;\n var s = 1; // 自己也佔一列\n for (var i = 0; i < n.children.length; i++) s += visibleRows(n.children[i]);\n return s;\n }\n\n // kb:// 來源 → 麵包屑(資料夾可以有很多層,全部拆開)。\n // leo 2026-08-18:「應該是讓他可以去本機資料夾找到原文,那應該帶上原機器的編號、\n // 資料夾名稱、檔名的麵包屑。如果是要讓他快速 locate 原文,不用再開搜尋框了。」\n // 🔴 machine 這一層現在**永遠是 null**:資料上傳時根本沒有記錄來源機器\n // collector 全 repo 沒有 hostnamemachine iddevice id),\n // 所以畫面上誠實標「未記錄」,不掰一個看起來像機器編號的字串。\n // 同一台機器(machine 比對鍵相同)在一次回答裡只該有**一個**顯示名。\n // 實況(youlin stage 2026-08-18 實測):同一把 `youlinhsieh@Leo-MBA` 有兩種 label——\n // 改名前灌的那筆 label 就等於 ID 本身,改名後那筆是「教育部 Leo 的 Mac」。\n // 照單全收會在畫面上長成**兩台機器**,而它們是同一台。\n // 判準:label 與比對鍵不同 = 有人真的取過名字 ⇒ 用它;都沒取過就退回顯示 ID。\n // (比對鍵永遠掛在 title 上,所以「它到底是哪一台」查得到。)\n function machineLabelMap(items) {\n var map = {};\n (items || []).forEach(function (it) {\n var k = it && it.machine ? String(it.machine) : '';\n if (!k) return;\n var l = it && it.machine_label ? String(it.machine_label) : '';\n if (!map[k] || (map[k] === k && l && l !== k)) map[k] = l || k;\n });\n return map;\n }\n function srcCrumbs(source, library, src, labels) {\n // machine =比對鍵(穩定,不隨改名變);machine_label =顯示名(使用者可改)。\n // 🔴 判準是「有沒有值」,不是「有沒有這個欄位」:舊資料**根本沒有這兩格**\n // (資料端刻意不 backfill、不猜),空字串也一律當沒有 ⇒ 顯示「未知來源」。\n var mk = (src && src.machine) ? String(src.machine) : '';\n var ml = (src && src.machine_label) ? String(src.machine_label) : '';\n if (mk && labels && labels[mk]) ml = labels[mk]; // 同一台機器全篇同一個名字\n var out = { machine: mk || null, machineLabel: ml || mk || null,\n library: library || '', dirs: [], file: '', frag: '', path: '', raw: String(source || '') };\n var p = srcLocalPath(source); // kb:// → 去 scheme、去錨點的相對路徑\n if (!p) return out;\n out.path = p;\n var m = /#([^#]*)$/.exec(String(source));\n if (m) out.frag = m[1];\n var seg = p.split('/').filter(Boolean);\n out.file = seg.pop() || '';\n out.dirs = seg;\n return out;\n }\n\n // ── AI 問答(portal-demo-suite)──\n // 設計哲學:AI 檢索=用戶手動搜尋同一套——同 session token、同 /portal/data/* enforce 面,\n // AI 只代查再作答;答案附 sourcesmodepage),點 page 帶回搜尋框讓用戶自己驗證。\n $('ai-go').addEventListener('click', doAsk);\n $('ai-q').addEventListener('keydown', function (ev) { if (ev.key === 'Enter') doAsk(); });\n function doAsk() {\n var q = $('ai-q').value.trim();\n if (!q) { toast('請先輸入問題'); return; }\n $('ai-go').disabled = true;\n $('ai-out').innerHTML = '<div class=\"muted\" style=\"margin-top:12px\">AI 查閱知識庫中…</div>';\n fetch(API_BASE + '/portal/data/chat?question=' + encodeURIComponent(q), { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n $('ai-go').disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) {\n // 404=此實例未安裝問答 workflow(server 誠實回報,前端不假裝有 AI\n $('ai-out').innerHTML = '<div class=\"honest\" style=\"margin-top:12px\"><div class=\"h\">問不到</div><div class=\"b\">' + esc(x.d.error || ('問答失敗(HTTP ' + x.status + '')) + '</div></div>';\n return;\n }\n var srcs = x.d.sources || [];\n // answer 純文字渲染、保留換行(pre-wrapesc,不走 markdown、防 XSS\n var html = '<div class=\"panel\" style=\"margin-top:12px\">' +\n '<div style=\"white-space:pre-wrap;font-size:16px;line-height:1.85;word-break:break-word\">' + esc(x.d.answer || 'AI 沒有給出答案)') + '</div>';\n // 檢索過程(retrieval 整段可能不存在——舊實例的 workflow 不回它,見上方函式註解)\n html += retrievalTraceHtml(x.d.retrieval);\n if (srcs.length) {\n // 出處=「去你自己的資料夾把原檔找出來」的地址,不是「再搜一次」的入口\n // leo 2026-08-18:「如果是要讓他快速 locate 原文,不用再開搜尋框了。」)\n html += '<div style=\"margin-top:14px;font-size:13px;letter-spacing:.1em;color:rgba(var(--ink-rgb),.5)\">出處(在你的資料夾裡的位置)</div>' +\n '<div style=\"margin-top:7px;display:flex;flex-direction:column;gap:6px\">' +\n (function () { var lb = machineLabelMap(srcs); return srcs.map(function (s) { return srcCrumbRowHtml(s, lb); }).join(''); })() + '</div>';\n } else {\n // inkstone/Arcrun#170:沒找到不是句點——「只說沒找到,並告訴他可以怎麼換句話問」。\n // 不附一堆無關檔案,但也不要讓使用者卡在「不知道下一步」。\n html += '<div class=\"muted\" style=\"margin-top:10px;font-size:13.5px\">' +\n '換個說法再問一次,或用更具體的關鍵字(例如人名、檔名裡出現過的詞)試試看。' +\n '</div>';\n }\n html += '</div>';\n $('ai-out').innerHTML = html;\n mountRetrievalTree(q, x.d.retrieval);\n })\n .catch(function (e) { $('ai-go').disabled = false; $('ai-out').innerHTML = '<div class=\"err\" style=\"margin-top:12px\">請求失敗:' + esc(friendlyErr(e)) + '</div>'; });\n }\n // 一列出處=一條「機器 › 子庫 › 資料夾(可多層)› 檔名 › 片段」的麵包屑。\n // 資料夾那幾層一直都在(kb:// 後面就是相對路徑,多層的),只是以前整條當一個字串印出來;\n // 機器那一層則是**真的沒有被存下來**,畫面上標「未記錄」而不是湊一個。\n function srcCrumbRowHtml(s, labels) {\n var page = (s && (s.page || s.page_name)) || '';\n var src = (s && s.source) || '';\n var mode = (s && s.mode) || '';\n var isMode = !!SEARCH_MODE_LABEL[mode]; // keyword/semantic/graph=檢索模式\n var c = srcCrumbs(src, isMode ? '' : mode, s, labels); // 其餘情況下 mode 放的是子庫名\n var href = src ? srcHref(src) : '';\n var head = '<div class=\"crumbs\">' +\n (c.machineLabel\n // 顯示名是使用者可改的,比對鍵才是穩定的那個 ⇒ 真值掛在 title 上,兩個都看得到\n ? '<span class=\"crumb\" title=\"' + esc('這台機器的比對鍵:' + (c.machine || '')) + '\">' + esc(c.machineLabel) + '</span>'\n : '<span class=\"crumb unknown\" title=\"這筆資料沒有記錄來源機器(舊資料在收檔時還沒有這一格),所以看不出原稿在誰的電腦上。\">未知來源</span>');\n if (c.library) head += '<span class=\"sep\"></span><span class=\"crumb\">' + esc(c.library) + '</span>';\n c.dirs.forEach(function (d) { head += '<span class=\"sep\"></span><span class=\"crumb\">' + esc(d) + '</span>'; });\n if (c.file) head += '<span class=\"sep\"></span><span class=\"crumb file\">' + esc(c.file) + '</span>';\n if (!c.path) head += '<span class=\"sep\"></span><span class=\"crumb file\">' + esc(page || '(無頁名)') + '</span>';\n // inkstone/Arcrun#170:「片段 #N」是內部詞,對使用者而言只是「這篇文件的第幾段」。\n if (c.frag) head += '<span class=\"tag dim\">' + esc(/^\\d+$/.test(c.frag) ? ('第 ' + (parseInt(c.frag, 10) + 1) + ' 段') : ('第 ' + c.frag + ' 段')) + '</span>';\n if (isMode) head += '<span class=\"tag\">' + esc(searchModeLabel(mode)) + '</span>';\n head += '</div>';\n var foot = '<div class=\"crumbfoot\">';\n if (c.path) {\n foot += '<button class=\"btn3\" data-copypath=\"' + esc(c.path) + '\">複製路徑</button>' +\n '<span class=\"p\">' + esc(c.path) + '</span>';\n } else if (src) {\n // 非 kb:// 的來源(例如 gitea:)沒有本機路徑可給——照實印原字串,不假裝拆得出資料夾\n foot += '<span class=\"p\">' + esc(src) + '</span>';\n }\n if (c.path && page) foot += '<span class=\"p tt-dim\">頁:' + esc(page) + '</span>';\n if (href) foot += '<a href=\"' + esc(href) + '\" target=\"_blank\" rel=\"noopener\" style=\"color:var(--amber);font-size:12.5px\">來源 ↗</a>';\n foot += '</div>';\n return '<div class=\"crumbrow\">' + head + foot + '</div>';\n }\n $('ai-out').addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-copypath]');\n if (!t) return;\n var p = t.getAttribute('data-copypath');\n if (!p) return;\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(p).then(function () { toast('已複製路徑:' + p); },\n function () { toast('複製失敗,請手動選取路徑'); });\n } else {\n toast('這個瀏覽器不給複製,請手動選取路徑');\n }\n });\n\n // ── graph 模式(D-4 粗閘:無權者按鈕根本不顯示;就算 curl 直打 API 也是 403)──\n function doGraphSearch(name) {\n $('se-results').style.display = 'none';\n $('se-graphout').style.display = '';\n $('se-count').textContent = '查詢關聯中…';\n renderGraphEmpty('查詢關聯中…');\n fetch(API_BASE + '/portal/data/graph/neighbors/' + encodeURIComponent(name), { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { $('se-count').innerHTML = '<span class=\"err\">' + esc(x.d.error || ('關聯查詢失敗(HTTP ' + x.status + '')) + '</span>'; renderGraphEmpty(x.d.error || '關聯查詢失敗'); return; }\n var neighbors = x.d.neighbors || [];\n var edges = x.d.edges || [];\n // 形狀相容(Arcrun#57 家族):plugin 版 neighbors 是字串、workflow 版是\n // { node, predicate, from, depth } 物件——不正規化會渲染成 [object Object]。\n var names = neighbors.map(function (nb) {\n if (typeof nb === 'string') return nb;\n return (nb && (nb.node || nb.name)) || '';\n }).filter(Boolean);\n // workflow 版 edges 恆空,但 neighbor 物件本身就是一條邊(from --predicate--> node)→ 導出。\n if (!edges.length) {\n edges = neighbors.filter(function (nb) { return nb && typeof nb === 'object' && nb.predicate; })\n .map(function (nb) { return { subject: nb.from || name, predicate: nb.predicate, object: nb.node }; });\n }\n $('se-count').textContent = '節點「' + name + '」・鄰居 ' + names.length + '・關聯 ' + edges.length;\n if (!names.length) { renderGraphEmpty('尚無關聯資料——這個名字還沒有連進知識圖譜。'); return; }\n renderGraph(name, names.slice(0, 10));\n $('gr-edges').innerHTML = edges.slice(0, 12).map(function (t) {\n // 大小寫不敏感比對(graph 查詢已正規化:搜 rag 會命中節點 RAG\n var other = String(t.subject).toLowerCase() === String(name).toLowerCase() ? t.object : t.subject;\n return '<div class=\"nbrow\" data-gnode=\"' + esc(other) + '\">' +\n '<span class=\"tag green\" style=\"flex:none\">' + esc(t.predicate || '關聯') + '</span>' +\n '<span style=\"word-break:break-word\">' + esc(other) + '</span>' +\n '<span style=\"margin-left:auto;color:rgba(var(--amber-rgb),.6);flex:none\"></span></div>';\n }).join('');\n })\n .catch(function (e) { $('se-count').innerHTML = '<span class=\"err\">關聯服務不可達:' + esc(friendlyErr(e)) + '</span>'; renderGraphEmpty('關聯服務不可達'); });\n }\n function renderGraphEmpty(msg) {\n $('gr-box').innerHTML = '<div class=\"graphbox\" style=\"display:grid;place-items:center;height:160px\"><span class=\"muted\" style=\"font-size:14px;padding:0 20px;text-align:center\">' + esc(msg) + '</span></div>';\n $('gr-edges').innerHTML = '';\n }\n function renderGraph(center, nodes) {\n var n = nodes.length || 1;\n var lines = '', chips = '';\n for (var i = 0; i < n; i++) {\n var a = (-90 + i * (360 / n)) * Math.PI / 180;\n var x = 50 + 39 * Math.cos(a), y = 50 + 37 * Math.sin(a);\n lines += '<line x1=\"50\" y1=\"50\" x2=\"' + x.toFixed(2) + '\" y2=\"' + y.toFixed(2) + '\" stroke=\"rgba(var(--ok-rgb),.3)\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\" stroke-dasharray=\"4 4\"/>';\n chips += '<div class=\"gnode\" style=\"left:' + x.toFixed(2) + '%;top:' + y.toFixed(2) + '%\" data-gnode=\"' + esc(nodes[i]) + '\">' + esc(nodes[i]) + '</div>';\n }\n var shortCenter = center.length > 16 ? center.slice(0, 16) + '…' : center;\n $('gr-box').innerHTML = '<div class=\"graphbox\">' +\n '<svg viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\" style=\"position:absolute;inset:0;width:100%;height:100%\">' + lines + '</svg>' +\n '<div class=\"gcenter\"><span>' + esc(shortCenter) + '</span></div>' + chips +\n '<div style=\"position:absolute;right:12px;bottom:10px;font-size:11.5px;color:rgba(var(--ink-rgb),.35)\">╌ 知識圖譜鄰居(點節點展開)</div></div>';\n }\n document.addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-gnode]');\n if (!t) return;\n var name = t.getAttribute('data-gnode');\n $('se-q').value = name;\n doGraphSearch(name);\n });\n\n // ── 卡片詳頁 ──\n function loadCard() {\n if (!S.cardId) { $('cd-main').innerHTML = '<div class=\"muted\">沒有指定卡片——從「搜尋」點一張進來。</div>'; return; }\n $('cd-main').innerHTML = '<div class=\"muted\">載入中…</div>';\n fetch(API_BASE + '/portal/data/entries/' + encodeURIComponent(S.cardId), { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok || !x.d.entry) { $('cd-main').innerHTML = '<div class=\"err\">' + esc(x.d.error || '讀不到這張卡片') + '</div>'; return; }\n var e = x.d.entry;\n var src = entrySource(e);\n $('cd-main').innerHTML = '<div class=\"cardtitle\">' + esc(entryTitle(e)) + '</div>' +\n '<div style=\"display:flex;gap:10px;align-items:center;margin-top:12px;font-size:13.5px;flex-wrap:wrap\">' +\n '<span class=\"tag\">' + esc(entryTypeLabel(e.entry_type)) + '</span>' +\n '<span class=\"tag green\">' + esc(entryLib(e)) + '</span>' +\n (e.page_name ? '<span class=\"tag dim\">' + esc(e.page_name) + '</span>' : '') +\n '<span class=\"muted mono\">' + esc(fmtDateTime(e.created_at)) + '</span></div>' +\n '<div class=\"md\">' + mdRender(e.content || '(此條目沒有內文)') + '</div>' +\n (src\n ? (function () {\n var href = srcHref(src);\n var body = href\n ? '<a class=\"mono\" style=\"font-size:14px;color:var(--amber);word-break:break-all\" href=\"' + esc(href) + '\" target=\"_blank\" rel=\"noopener\">' + esc(src) + ' ↗</a>'\n : '<span class=\"mono\" style=\"font-size:14px;color:var(--amber);word-break:break-all\">' + esc(src) + '</span>';\n return '<div class=\"srcline\"><span style=\"font-size:13px;letter-spacing:.14em;color:rgba(var(--ink-rgb),.5);flex:none\">來源回溯</span>' + body + '</div>';\n })()\n : '');\n })\n .catch(function (er) { $('cd-main').innerHTML = '<div class=\"err\">請求失敗:' + esc(friendlyErr(er)) + '</div>'; });\n }\n\n // ── 總圖(Arcrun#39 藏書地圖 GUI 切片)──\n // 全庫三元組一張網:/portal/data/graph/overview → 手刻力導向佈局(demo 量級,零外部套件)。\n // 佈局用固定種子的 PRNG=同一庫每次長一樣(客戶重整不會跳位)。\n function jumpToGraph(name) {\n S.mode = 'graph';\n document.querySelectorAll('.modebtn').forEach(function (x) { x.classList.toggle('on', x.getAttribute('data-mode') === 'graph'); });\n nav('search');\n $('se-q').value = name;\n doGraphSearch(name);\n }\n // 讀不到就明說「讀不到」——標題列**絕不**留著 0 或舊數字(Arcrun#100leo 看到\n // 「0 個實體・0 條關聯」以為要去上傳文件,其實庫裡有 1854 條,只是這支讀失敗了)。\n function mapUnavailable(html) {\n $('map-meta').textContent = '讀不到';\n $('map-box').innerHTML = '<div class=\"err\" style=\"padding:30px 10px\">' + html + '</div>';\n }\n function loadMap() {\n $('map-meta').textContent = '';\n $('map-box').innerHTML = '<div class=\"muted\" style=\"padding:30px 10px\">載入總圖中…</div>';\n $('map-md-link').innerHTML = SOURCE_WEB_BASE\n ? '<a href=\"' + esc(SOURCE_WEB_BASE + '/system-dev/wiki/00-MAP.md') + '\" target=\"_blank\" rel=\"noopener\" style=\"color:var(--amber)\">00-MAP.md ↗</a>'\n : '存在知識庫的 00-MAP.md';\n fetch(API_BASE + '/portal/data/graph/overview', { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { mapUnavailable(esc(x.d.error || ('總圖載入失敗(HTTP ' + x.status + ''))); return; }\n // Arcrun#100:「0」只准在後端確認過真的是 0 的時候出現。\n // 形狀不對 → 讀不到(不是空庫);nodes 為空但 empty_confirmed 不成立 → 讀不到。\n if (!Array.isArray(x.d.nodes) || !Array.isArray(x.d.edges)) {\n mapUnavailable('總圖回應格式不對——沒有拿到關聯資料。這不代表知識庫是空的。');\n return;\n }\n var nodes = x.d.nodes, edges = x.d.edges;\n var total = typeof x.d.triplets_total === 'number' ? x.d.triplets_total : null;\n if (!nodes.length && x.d.empty_confirmed !== true) {\n mapUnavailable(x.d.empty_reason === 'scope_mismatch'\n ? '讀不到你這個帳號的關聯資料——知識庫裡有三元組'\n + (total ? '(本帳號範圍算到 ' + total.toLocaleString() + ' 條)' : '')\n + ',但這張圖一條都抽不出來。<br>'\n + '<b>這不是「還沒有關聯」,不用去上傳文件</b>;比較像資料的歸屬範圍對不上,請通知管理員。'\n : '讀不到知識庫的關聯資料,無法確認庫裡有沒有關聯。<br>'\n + '<b>這不是「還沒有關聯」,不用去上傳文件</b>——是這次讀取失敗,請稍後重整或通知管理員。');\n return;\n }\n $('map-meta').textContent = nodes.length + ' 個實體・' + edges.length + ' 條關聯'\n + (total !== null && x.d.truncated ? '(全庫共 ' + total.toLocaleString() + ' 條,已達單次上限)' : x.d.truncated ? '・已達上限截斷' : '');\n if (!nodes.length) { $('map-box').innerHTML = '<div class=\"muted\" style=\"padding:30px 10px\">知識庫還沒有任何關聯——上傳文件後 AI 會自動織網。</div>'; return; }\n renderMap(nodes, edges);\n })\n .catch(function (e) { mapUnavailable('請求失敗:' + esc(friendlyErr(e))); });\n }\n function renderMap(nodes, edges) {\n var N = nodes.length;\n var idx = {};\n nodes.forEach(function (n, i) { idx[n.name] = i; });\n var el = [];\n edges.forEach(function (e) {\n var a = idx[e.subject], b = idx[e.object];\n if (a == null || b == null || a === b) return;\n el.push([a, b, e.predicate || '']);\n });\n // 固定種子 PRNGmulberry32):確定性佈局\n var seed = 42;\n function rnd() {\n seed = (seed + 0x6D2B79F5) | 0;\n var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n }\n var W = 1000, H = Math.max(480, Math.min(760, 200 + N * 14));\n var px = [], py = [];\n for (var i = 0; i < N; i++) {\n var ang = 2 * Math.PI * i / N;\n var rad = Math.min(W, H) * (0.22 + 0.2 * rnd());\n px.push(W / 2 + rad * Math.cos(ang));\n py.push(H / 2 + rad * Math.sin(ang) * 0.75);\n }\n // 力導向:斥力(全對)+彈簧(邊)+向心,260 輪退火\n var K = Math.max(75, Math.sqrt(W * H / Math.max(N, 1)) * 0.9);\n for (var it = 0; it < 260; it++) {\n var cool = Math.max(0.06, 1 - it / 260);\n var fx = [], fy = [];\n for (var a1 = 0; a1 < N; a1++) { fx.push(0); fy.push(0); }\n for (var a2 = 0; a2 < N; a2++) {\n for (var b2 = a2 + 1; b2 < N; b2++) {\n var dx = px[a2] - px[b2], dy = py[a2] - py[b2];\n var d2 = dx * dx + dy * dy + 0.01;\n var f = Math.min(28, (K * K) / d2 * 2.2);\n var d = Math.sqrt(d2);\n fx[a2] += dx / d * f; fy[a2] += dy / d * f;\n fx[b2] -= dx / d * f; fy[b2] -= dy / d * f;\n }\n }\n el.forEach(function (e) {\n var dx = px[e[0]] - px[e[1]], dy = py[e[0]] - py[e[1]];\n var d = Math.sqrt(dx * dx + dy * dy) + 0.01;\n var f = (d - K) * 0.045;\n fx[e[0]] -= dx / d * f; fy[e[0]] -= dy / d * f;\n fx[e[1]] += dx / d * f; fy[e[1]] += dy / d * f;\n });\n for (var m = 0; m < N; m++) {\n fx[m] += (W / 2 - px[m]) * 0.012;\n fy[m] += (H / 2 - py[m]) * 0.012;\n px[m] += Math.max(-14, Math.min(14, fx[m])) * cool;\n py[m] += Math.max(-14, Math.min(14, fy[m])) * cool;\n }\n }\n // 視窗貼合\n var minX = 1e9, maxX = -1e9, minY = 1e9, maxY = -1e9;\n for (var q = 0; q < N; q++) {\n minX = Math.min(minX, px[q]); maxX = Math.max(maxX, px[q]);\n minY = Math.min(minY, py[q]); maxY = Math.max(maxY, py[q]);\n }\n var PAD = 70;\n var vb = (minX - PAD) + ' ' + (minY - PAD) + ' ' + (maxX - minX + PAD * 2) + ' ' + (maxY - minY + PAD * 2);\n var maxDeg = 1;\n nodes.forEach(function (n) { maxDeg = Math.max(maxDeg, n.degree || 1); });\n var svg = '<svg viewBox=\"' + vb + '\" preserveAspectRatio=\"xMidYMid meet\">';\n el.forEach(function (e) {\n svg += '<line class=\"mapedge\" x1=\"' + px[e[0]].toFixed(1) + '\" y1=\"' + py[e[0]].toFixed(1) +\n '\" x2=\"' + px[e[1]].toFixed(1) + '\" y2=\"' + py[e[1]].toFixed(1) + '\"><title>' +\n esc(nodes[e[0]].name + ' —' + (e[2] || '關聯') + '→ ' + nodes[e[1]].name) + '</title></line>';\n });\n nodes.forEach(function (n, i2) {\n var deg = n.degree || 1;\n var r = 9 + Math.sqrt(deg) * 5;\n var hub = deg >= Math.max(3, maxDeg * 0.6) ? ' hub' : '';\n svg += '<g class=\"mapnode' + hub + '\" data-mapnode=\"' + esc(n.name) + '\">' +\n '<circle cx=\"' + px[i2].toFixed(1) + '\" cy=\"' + py[i2].toFixed(1) + '\" r=\"' + r.toFixed(1) + '\"><title>' + esc(n.name + '・' + deg + ' 條關聯') + '</title></circle>' +\n '<text x=\"' + px[i2].toFixed(1) + '\" y=\"' + (py[i2] + r + 15).toFixed(1) + '\" text-anchor=\"middle\">' + esc(n.name.length > 14 ? n.name.slice(0, 14) + '…' : n.name) + '</text></g>';\n });\n svg += '</svg>';\n $('map-box').innerHTML = '<div class=\"mapbox\">' + svg +\n '<div style=\"position:absolute;right:12px;bottom:10px;font-size:11.5px;color:rgba(var(--ink-rgb),.35)\">╌ 知識庫總圖(點節點看鄰居)</div></div>';\n }\n document.addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-mapnode]');\n if (!t) return;\n jumpToGraph(t.getAttribute('data-mapnode'));\n });\n\n // ── 工作流(唯讀)──\n function loadWorkflows() {\n $('wf-list').innerHTML = '<div class=\"muted\">載入中…</div>';\n fetch(API_BASE + '/portal/data/workflows', { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { $('wf-list').innerHTML = '<div class=\"err\">' + esc(x.d.error || ('讀取失敗(HTTP ' + x.status + '')) + '</div>'; return; }\n var wfs = x.d.workflows || [];\n if (!wfs.length) { $('wf-list').innerHTML = '<div class=\"muted\" style=\"padding:20px 4px\">目前沒有任何工作流。</div>'; return; }\n $('wf-list').innerHTML = wfs.map(function (w) {\n var lastHtml = '<span class=\"dim\">尚無執行紀錄</span>';\n if (w.last_execution) {\n var v = w.last_execution.verdict || '';\n var cls = v === 'success' ? 'ok' : (v === 'failed' ? 'err' : 'muted');\n lastHtml = '<span class=\"' + cls + '\">' + esc(v || '(無 verdict') + '</span> <span class=\"dim mono\">' + esc(fmtDateTime(Number(w.last_execution.timestamp))) + '</span>';\n }\n return '<div class=\"card-item\">' +\n '<div style=\"display:flex;align-items:baseline;gap:10px;flex-wrap:wrap\">' +\n '<span class=\"serif\" style=\"font-size:17px;font-weight:600\">' + esc(w.name) + '</span>' +\n (w.cron_expr ? '<span class=\"tag dim mono\">' + esc(w.cron_expr) + '</span>' : '') +\n '<span style=\"margin-left:auto;font-size:13.5px\">' + lastHtml + '</span></div>' +\n (w.description ? '<div style=\"margin-top:8px;font-size:14.5px;line-height:1.65;color:rgba(var(--ink-rgb),.65)\">' + esc(w.description) + '</div>' : '<div class=\"dim\" style=\"margin-top:8px;font-size:13.5px\">(沒有描述)</div>') +\n '</div>';\n }).join('');\n })\n .catch(function (e) { $('wf-list').innerHTML = '<div class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</div>'; });\n }\n\n // ── Leo/Arcrun#82 App 系統 v0Portal 泛用 loaderK5)──\n //\n // 這是本卷唯一改動 Portal 原始碼的地方,且只改這一次——此後每裝一個新 App,\n // 「App 界面」磚塊格與「App 市集」已安裝清單自動多一格、點進去有畫面,這個檔案\n // 不需要再被碰(K5 驗收)。安裝/卸載走 /apps/install、DELETE /portal/data/apps/:id\n // 這裡只讀已安裝清單、渲染畫面、把使用者的動作轉成一支泛用端點的呼叫\n // (K6:前端永遠拿不到任何金鑰)。\n //\n // Arcrun#152:渲染目標從舊的側欄/tabbar 插槽(nav-apps-slot/tab-apps-slot,已隨四頁簽\n // 改版移除)換成 renderLaunchGrid()renderMarketInstalled()——同一份 fetch 結果餵給\n // 兩處呈現(K2 單一真相源:清單只讀一次,不是兩份各自 fetch)。\n\n function greetingText() {\n var h = new Date().getHours();\n var g = h < 5 ? '夜深了' : h < 12 ? '早安' : h < 18 ? '午安' : '晚安';\n var name = (S.profile && S.profile.display_name) || '';\n return name ? (g + '' + name) : g;\n }\n\n // ── IconArcrun#152):描邊式 inline SVG,不用 emoji ─────────────────────\n // 為什麼換掉 emoji:① emoji 是各作業系統自帶的彩色點陣字形,WindowsmacOS\n // Android 各長一套 ⇒ 設計稿保證不了;② 它吃不到 CIS 的 --ink--amber ⇒\n // 切深色模式時不會跟著變色。字形本體是 <body> 開頭那份 <symbol> sprite\n // 這裡只負責「挑哪一顆」。\n\n // 後端 app-system.ts 的 generateIcon() 會依 App 名字決定性發一顆 emoji,清單是\n // ICON_SET = ['📄','🗒️','🧩','📌','🔧','📦','🗂️','✅','💡','🔔','📊','🧭']。\n // 下面一對一換成同樣 12 顆描邊字形,**順序必須跟它一致**,同一個 App 才不會換臉。\n var APP_GLYPHS = ['doc', 'note', 'layers', 'pin', 'wrench', 'box', 'folder', 'check', 'bulb', 'bell', 'chart', 'compass'];\n var EMOJI_GLYPH = {\n '📄': 'doc', '🗒️': 'note', '🗒': 'note', '📝': 'note', '🧩': 'layers', '📌': 'pin',\n '🔧': 'wrench', '🛠️': 'wrench', '📦': 'box', '🗂️': 'folder', '🗂': 'folder', '📁': 'folder',\n '✅': 'check', '💡': 'bulb', '🔔': 'bell', '📊': 'chart', '📈': 'chart',\n '🧭': 'compass', '🔍': 'search', '🕸️': 'graph', '🏠': 'apps'\n };\n\n // 遇到不認得的 iconApp 宣告裡自己填的 emoji)就用跟後端同一條雜湊挑一顆——\n // 至少保證「同一個 App 每次都是同一顆」,不會每次重整就換臉。\n function appGlyph(icon, name) {\n if (icon && EMOJI_GLYPH[icon]) return EMOJI_GLYPH[icon];\n var seed = String(icon || name || '');\n var h = 0;\n for (var i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;\n return APP_GLYPHS[h % APP_GLYPHS.length];\n }\n\n // 白名單:只吐 sprite 裡真的有的 id ⇒ 這段 innerHTML 不可能被外部字串汙染\n // (後端來的 a.icon 一律先經過 appGlyph() 收斂成這張表裡的值)。\n var GLYPHS = APP_GLYPHS.concat(['apps', 'market', 'settings', 'dev', 'search', 'graph', 'plus']);\n function iconSvg(id) {\n return '<svg aria-hidden=\"true\" focusable=\"false\"><use href=\"#i-' + (GLYPHS.indexOf(id) >= 0 ? id : 'doc') + '\"/></svg>';\n }\n\n // 「App 界面」九宮格:知識庫搜尋/知識總圖是內建能力(本來就不是 app-system 安裝出來的\n // 那份清單),其餘磚塊來自已安裝 App;「加裝 App」磚塊固定放最後,導去「App 市集」。\n function renderLaunchGrid(apps) {\n var grid = $('launch-grid');\n if (!grid) return;\n var tiles = [{ gl: 'search', label: '知識庫搜尋', dn: 'search' }];\n if (S.profile && S.profile.graph_allowed) tiles.push({ gl: 'graph', label: '知識總圖', dn: 'map' });\n apps.forEach(function (a) {\n tiles.push({ gl: appGlyph(a.icon, a.name || a.id), label: a.name || a.id, dn: 'app:' + a.id, accent: true });\n });\n grid.innerHTML = tiles.map(function (t) {\n return '<div class=\"apptile' + (t.accent ? ' accent' : '') + '\" data-nav=\"' + esc(t.dn) + '\" title=\"' + esc(t.label) + '\">' +\n '<div class=\"ic\">' + iconSvg(t.gl) + '</div><div class=\"lbl\">' + esc(t.label) + '</div></div>';\n }).join('') + '<div class=\"apptile add\" data-nav=\"market\" title=\"App 市集\"><div class=\"ic\">' + iconSvg('plus') + '</div><div class=\"lbl muted\">加裝 App</div></div>';\n var g = $('lh-greet'); if (g) g.textContent = greetingText();\n var sub = $('lh-sub'); if (sub) sub.textContent = tiles.length + ' 個 App 可用・在「App 市集」找更多';\n }\n\n // 「App 市集」已安裝清單:知識庫搜尋(+知識總圖,若開放)標「內建」,其餘是真正\n // 走 /apps/install 裝進來的 App(K2:同一份資料,不是市集自己另存一份清單)。\n function renderMarketInstalled(apps) {\n var list = $('market-installed');\n if (!list) return;\n var items = [{ dn: 'search', name: '知識庫搜尋', gl: 'search', builtin: true, desc: 'RAG 語意搜尋,附出處與檢索過程' }];\n if (S.profile && S.profile.graph_allowed) items.push({ dn: 'map', name: '知識總圖', gl: 'graph', builtin: true, desc: '全庫三元組關聯圖' });\n apps.forEach(function (a) {\n items.push({ dn: 'app:' + a.id, name: a.name || a.id, gl: appGlyph(a.icon, a.name || a.id), builtin: false, desc: a.has_ui ? '自帶畫面的 App' : '工作流型 App(系統預設畫面)' });\n });\n var canManage = !!(S.profile && S.profile.role === 'admin');\n list.innerHTML = items.map(function (it) {\n // 卸載鍵只給裝上去的 App(內建能力沒得卸),且只給 admin——與後端\n // DELETE /portal/data/apps/:id 同一道閘。非 admin 看不到鍵,不是看到一顆按了才失敗的鍵。\n var removeBtn = (!it.builtin && canManage)\n ? '<button class=\"btn2 warn mk-remove\" data-app=\"' + esc(it.dn.slice(4)) + '\" data-name=\"' + esc(it.name) + '\">移除</button>'\n : '';\n return '<div class=\"card-item\" data-nav=\"' + esc(it.dn) + '\" style=\"cursor:pointer;display:flex;gap:14px;align-items:center\">' +\n '<div class=\"apptile-ic-sm\">' + iconSvg(it.gl) + '</div>' +\n '<div style=\"flex:1;min-width:0\">' +\n '<div style=\"display:flex;justify-content:space-between;gap:8px;align-items:baseline\"><span style=\"font-weight:600\">' + esc(it.name) + '</span><span class=\"tag green\">已安裝</span></div>' +\n '<div class=\"dim\" style=\"font-size:13px;margin-top:2px\">' + esc(it.desc) + (it.builtin ? ' · 內建' : '') + '</div>' +\n '</div>' + removeBtn + '</div>';\n }).join('');\n // stopPropagation:這顆鍵住在一張整張可點的卡片裡,不擋的話按「移除」會先被\n // 卡片的 data-nav 接走,變成「點了移除卻跳進那個 App」。\n list.querySelectorAll('.mk-remove').forEach(function (btn) {\n btn.addEventListener('click', function (ev) {\n ev.stopPropagation();\n removeAppFromMarket(btn.getAttribute('data-app'), btn.getAttribute('data-name'), btn);\n });\n });\n }\n\n // ── 「可安裝」清單(Arcrun#152)─────────────────────────────────────────────\n //\n // leo 2026-08-25:「**應該要讓每個人都可以顯示,按一下安裝**」。\n // 在這之前,portal 這一格寫的是「尚未接市集後端」,而真正裝一個 App 的路是\n // clone repo → 跑 node → 手動代換兩個佔位符 → curl POST 帶 API key。\n //\n // 清單來源=`/portal/data/apps/catalog`(隨引擎出貨的目錄,見 lib/app-catalog.ts)。\n // 🔴 前端只拿得到 id/name/summary/version/author/installed——**宣告本體、實例座標、\n // 任何金鑰都不會下發**(K6 同一條線)。\n function renderMarketCatalog(entries, canInstall) {\n var box = $('market-catalog');\n if (!box) return;\n if (!entries || entries.length === 0) {\n box.innerHTML = '<div class=\"honest\"><div class=\"h\">這一版沒有收錄可安裝的 App</div>' +\n '<div class=\"b\">市集內容跟著你這台實例的版本走。更新到新版本之後,這裡會多出東西。</div></div>';\n return;\n }\n box.innerHTML = entries.map(function (e) {\n var btn = e.installed\n ? '<button class=\"btn2 mk-install\" disabled data-app=\"' + esc(e.id) + '\">已安裝</button>'\n : (canInstall\n ? '<button class=\"btn2 mk-install\" data-app=\"' + esc(e.id) + '\" data-name=\"' + esc(e.name) + '\">安裝</button>'\n : '<button class=\"btn2\" disabled title=\"安裝 App 會影響這台實例的所有人,所以要管理員來裝\">要管理員才能裝</button>');\n return '<div class=\"card-item\" style=\"display:flex;gap:14px;align-items:center\">' +\n '<div class=\"apptile-ic-sm\">' + iconSvg(appGlyph(e.icon, e.name || e.id)) + '</div>' +\n '<div style=\"flex:1;min-width:0\">' +\n '<div style=\"display:flex;gap:8px;align-items:baseline;flex-wrap:wrap\"><span style=\"font-weight:600\">' + esc(e.name) + '</span>' +\n '<span class=\"tag dim mono\">v' + esc(e.version) + '</span><span class=\"dim\" style=\"font-size:12.5px\">' + esc(e.author) + '</span></div>' +\n '<div class=\"dim\" style=\"font-size:13px;margin-top:3px\">' + esc(e.summary) + '</div>' +\n '<div class=\"mk-msg\" style=\"font-size:13px;margin-top:8px;line-height:1.65\"></div>' +\n '</div>' + btn + '</div>';\n }).join('');\n box.querySelectorAll('.mk-install:not([disabled])').forEach(function (btn) {\n btn.addEventListener('click', function () {\n installAppFromMarket(btn.getAttribute('data-app'), btn.getAttribute('data-name'), btn);\n });\n });\n }\n\n /**\n * 把後端回的失敗攤成三句人話:**哪一步、為什麼、下一步做什麼**。\n * leo 的驗收條件之一——靜默失敗或丟一個錯誤代碼都算沒過。\n */\n function installFailureText(status, body) {\n var step = (body && body.step) || '';\n var why = (body && body.error) || ('伺服器回了 HTTP ' + status + ',沒有說明原因');\n var next = (body && body.hint) || '再按一次試試;一直失敗就把這段訊息回報給管理者。';\n return (step ? '卡在「' + step + '」:' : '') + why + '\\n下一步:' + next;\n }\n\n function installAppFromMarket(id, name, btn) {\n var card = btn.closest('.card-item');\n var msg = card ? card.querySelector('.mk-msg') : null;\n var original = btn.textContent;\n btn.disabled = true;\n btn.textContent = '安裝中…';\n if (msg) { msg.className = 'mk-msg dim'; msg.textContent = '正在把「' + name + '」裝到這台實例上…'; }\n fetch(API_BASE + '/portal/data/apps/catalog/' + encodeURIComponent(id) + '/install', {\n method: 'POST', headers: authHeaders(),\n })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) {\n btn.disabled = false;\n btn.textContent = original;\n if (msg) { msg.className = 'mk-msg err'; msg.style.whiteSpace = 'pre-line'; msg.textContent = '安裝沒有完成。' + installFailureText(x.status, x.d); }\n return;\n }\n btn.textContent = '已安裝';\n if (msg) { msg.className = 'mk-msg'; msg.style.whiteSpace = 'normal'; msg.innerHTML = '<span class=\"tag green\">裝好了</span> 回「App 界面」就看得到它。'; }\n toast('「' + name + '」已安裝');\n renderAppsNav(); // 九宮格與兩份清單一起重畫(同一份資料,不是各自 fetch)\n })\n .catch(function (e) {\n btn.disabled = false;\n btn.textContent = original;\n if (msg) { msg.className = 'mk-msg err'; msg.style.whiteSpace = 'pre-line'; msg.textContent = '安裝沒有完成。卡在「送出安裝要求」:' + friendlyErr(e) + '\\n下一步:確認網路,再按一次安裝。'; }\n });\n }\n\n /**\n * 兩段式確認:第一下把按鈕變成「再按一次確認」,第二下才真的做。\n *\n * 🔴 為什麼不用瀏覽器內建的 `confirm()`(原本就是那樣寫的,2026-08-25 實測撞到):\n * 有些內嵌/受管的瀏覽器環境**直接讓 confirm() 回 false 且不顯示任何東西**\n * ⇒ 使用者按了「移除」,畫面一動也不動,也沒有任何錯誤 ⇒ **靜默失敗**,\n * 而那正是本票明講不准出現的狀態。兩段式確認是同一道防呆,但它畫在頁面上,\n * 不依賴宿主環境願不願意開對話框。\n * 逾時自動復原(免得一顆「確認移除」永遠留在畫面上等著被誤觸)。窗口 30 秒——\n * 實測 6 秒太短:讀完提示再移到按鈕就過期了,那會變成「按兩次都只是重新武裝」,\n * 而使用者看到的是「按了兩下還是沒反應」——又是一種靜默失敗。\n */\n function armRemoveButton(btn, name, idleLabel) {\n btn.dataset.armed = '1';\n btn.dataset.idleLabel = idleLabel;\n btn.textContent = '確認移除';\n toast('再按一次「確認移除」:「' + name + '」的工作流會被撤掉,資料型別與資料本身保留。');\n clearTimeout(Number(btn.dataset.armTimer || 0));\n btn.dataset.armTimer = String(setTimeout(function () {\n btn.dataset.armed = '';\n btn.textContent = btn.dataset.idleLabel || idleLabel;\n }, 30000));\n }\n function disarmRemoveButton(btn) {\n clearTimeout(Number(btn.dataset.armTimer || 0));\n btn.dataset.armed = '';\n }\n\n function removeAppFromMarket(id, name, btn) {\n if (btn.dataset.armed !== '1') { armRemoveButton(btn, name, '移除'); return; }\n disarmRemoveButton(btn);\n btn.disabled = true;\n btn.textContent = '移除中…';\n fetch(API_BASE + '/portal/data/apps/' + encodeURIComponent(id), { method: 'DELETE', headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { btn.disabled = false; btn.textContent = '移除'; toast((x.d && x.d.error) || '移除失敗'); return; }\n toast('「' + name + '」已移除');\n renderAppsNav();\n })\n .catch(function (e) { btn.disabled = false; btn.textContent = '移除'; toast('請求失敗:' + friendlyErr(e)); });\n }\n\n // 讀不到就不顯示,不擋主流程(壞了不致命,同 setup checklist 慣例)。\n function renderAppsNav() {\n fetch(API_BASE + '/portal/data/apps', { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) return;\n var apps = (x.d && x.d.apps) || [];\n renderLaunchGrid(apps);\n renderMarketInstalled(apps);\n renderMarketCatalogFromServer(apps);\n // 非同步補回來的清單可能晚於 showView() 跑完——若使用者當下正停在某個 App 頁,\n // 重新套一次 highlight(不重新整理內容,只補樣式)。\n var v = currentRoute().view;\n if (v === 'app' || v === 'launcher' || v === 'market') showView(v);\n })\n .catch(function () { /* 讀不到清單=這個實例可能還沒裝任何 App,不是錯誤 */ });\n }\n\n // 目錄與已安裝清單是兩支端點(一個是「這版有哪些貨」,一個是「這台裝了什麼」)——\n // 但畫面上是同一頁的上下兩段,所以一起重畫,不讓使用者看到半新半舊。\n // 讀不到目錄就老實說讀不到(不是畫成「沒有可安裝的 App」——那是 #100 同一條)。\n function renderMarketCatalogFromServer(installedApps) {\n if (!$('market-catalog')) return;\n // 🔴「裝了沒」只認**一份**已安裝清單——就是上面那支 /portal/data/apps 的結果(K2)。\n // 目錄端點自己也會回一個 installed 旗標(給別的呼叫端用),但這裡不採信它:\n // 兩支端點是兩次獨立的 KV 讀取,而 KV 是最終一致的\n // ⇒ 剛按完「移除」的那一瞬間,兩邊可能一邊已更新、一邊還是舊的\n // ⇒ 畫面就會出現「已安裝清單裡沒有它,可安裝那格卻還寫著已安裝」。\n // 實際撞到過(2026-08-25 youlin)。同一份資料只讀一次,這種矛盾就不存在。\n var installedIds = {};\n (installedApps || []).forEach(function (a) { installedIds[a.id] = true; });\n fetch(API_BASE + '/portal/data/apps/catalog', { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) {\n $('market-catalog').innerHTML = '<div class=\"honest\"><div class=\"h\">讀不到可安裝清單</div>' +\n '<div class=\"b\">' + esc((x.d && x.d.error) || ('伺服器回了 HTTP ' + x.status)) +\n '<br>這不表示沒有 App 可裝,是這次沒問到。重新整理頁面再試一次。</div></div>';\n return;\n }\n var entries = ((x.d && x.d.apps) || []).map(function (e) {\n return {\n id: e.id, name: e.name, icon: e.icon, summary: e.summary,\n version: e.version, author: e.author,\n installed: !!installedIds[e.id], // ← 只認上面那一份,不認端點自己回的旗標\n };\n });\n renderMarketCatalog(entries, !!(x.d && x.d.can_install));\n })\n .catch(function (e) {\n $('market-catalog').innerHTML = '<div class=\"honest\"><div class=\"h\">讀不到可安裝清單</div>' +\n '<div class=\"b\">' + esc(friendlyErr(e)) + '<br>確認網路之後重新整理頁面。</div></div>';\n });\n }\n\n // 泛用動作端點(K6):App 的畫面/系統預設畫面都透過這支呼叫後端,前端沒有第二條路。\n function callAppAction(appId, action, payload) {\n return fetch(API_BASE + '/portal/data/apps/' + encodeURIComponent(appId) + '/action', {\n method: 'POST',\n headers: Object.assign({ 'Content-Type': 'application/json' }, authHeaders()),\n body: JSON.stringify({ action: action, payload: payload || {} }),\n }).then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); });\n }\n\n function loadApp() {\n var id = S.appId;\n var body = $('app-body');\n if (!id) { body.innerHTML = '<div class=\"err\">沒有指定 App</div>'; return; }\n $('app-title').textContent = id;\n $('app-remove-btn').classList.add('hide');\n body.innerHTML = '<div class=\"muted\">載入中…</div>';\n fetch(API_BASE + '/portal/data/apps/' + encodeURIComponent(id), { headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { body.innerHTML = '<div class=\"err\">' + esc((x.d && x.d.error) || '讀取失敗') + '</div>'; return; }\n renderAppView(x.d);\n })\n .catch(function (e) { body.innerHTML = '<div class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</div>'; });\n }\n\n // ── App 畫面的樣式隔離(Arcrun#152leo 2026-08-24 在 youlin stage 實測撞到)────────\n //\n // 舊做法是 `mount.innerHTML = app.ui_html`App 的 HTML **連同它的 `<style>`** 被貼進\n // Portal 這份文件裡。而 App 的畫面通常本來就是一份完整的 HTML 檔,帶著\n // :root { --bg: #0e0f12; --text: #eceef1; ... }\n // html, body { background: var(--bg); color: var(--text); }\n // 這種**針對整份文件**寫的規則 ⇒ 一點開 App,整個 Portal 就被它接管。\n // 實測(本機以 youlin 上那份真的「筆記」App 重現):`body` 的背景由紙紋變成\n // rgb(14,15,18)、文字色被翻成 rgb(236,238,241) ⇒ 側欄各分頁**按得到但看不見**。\n // 而且 `<style>` 會一直留在 `#v-app` 裡(切頁只是 toggle `.on` class,不是移除節點)\n // ⇒ **切回其他頁面畫面還是黑的**,只能重新整理才救得回來。\n //\n // 🔴 這不是「那個 App 的 CSS 寫壞了」,是協定允許任何 App 弄壞整個 Portal\n // ⇒ 未來市集上任何第三方 App 都能做到同一件事。所以修在 loader(K5 唯一那次改動),\n // 不是修在某個 App 身上。\n //\n // 修法=把 App 掛進 **Shadow DOM**shadow 內的樣式規則對外部文件不生效,這是瀏覽器\n // 層的保證,不靠「App 作者夠小心」也不靠我們的字串改寫夠周全。\n //\n // 🔴 **這一段只解掉了 leo 原則的後半(「不要影響整體」)。**\n // 當時還宣稱「把 CIS 樣式表複製一份進 shadow root ⇒ App 就跟著整體走」——\n // **那句話是錯的,leo 08-24 打開筆記 App 就抓到了**:那份副本裡的是\n // `--ink`/`--amber`,而 App 引用的是它自己 `:root` 裡的 `--accent`/`--bg`\n // 兩套沒有交集 ⇒ 複製進去對它一點作用都沒有。前半(「先採用整體的 styles」)\n // 整個沒做。真正的通道在下面「掛載協定 v0.2」那一段(cascade layers)。\n //\n // ⚠️ 誠實標明這個修法**不涵蓋**什麼:Shadow DOM 只隔離樣式與 DOM 查詢,App 的\n // `<script>` 仍然跑在 Portal 的 JS 環境裡(拿得到 `window``localStorage`)。\n // 要連腳本一起隔離得走 iframe sandbox postMessage,那是協定層的下一步(見 #82)。\n\n // ── 掛載協定 v0.2:兩種樣式模式(Arcrun#152#82leo 2026-08-24 拍板)──────────\n //\n // leo 逐字:「**App 應該可以選跟隨全局,或是堅持用自己的,但現在看起來是沒有連上全局\n // 的通道。如果用自己的,全局要給它多大的畫布也是全局決定的⋯⋯應該整個右側邊欄都是它的。**」\n //\n // 上一輪(Shadow DOM 隔離)只做到「App 弄不壞外面」。leo 08-24 打開筆記 App 之後\n // 連說四句——不吃全局的 style/按鈕藍色黑色/寬度字體不對——**每一句都是同一件事**:\n // 那條「跟隨全局」的通道從來沒有存在過。Portal 有 `--ink`/`--amber` 一套 token\n // 筆記 App 有 `--text`/`--panel`/`--accent` 另一套,**兩套沒有任何交集**\n // ⇒ 它不是被覆蓋,是從來沒接上。CIS 樣式表複製進 shadow 也沒用,App 引用的是自己那組。\n //\n // inherit**預設**)= 跟隨全局:配色、明暗主題、字體、畫布都由 Portal 決定,\n // App 只管自己的版面(間距、格線、它自己的 class)。\n // own = 堅持用自己的:App 自己畫,Portal 只保證它弄不壞外面。\n //\n // 🔴 **畫布大小兩種模式都由全局決定**——那是 `#app-ui-mount` 的 CSS(外部文件的規則\n // 對 host 元素勝過 shadow 內的 `:host`),App 在 shadow 裡寫什麼都改不動。\n //\n // 「跟隨全局」怎麼機械地成立(不是靠 App 作者自律)= **CSS Cascade Layers**\n // @layer arcrun-base, app, arcrun; ← 後宣告的層贏,且**與 specificity 無關**\n // · `arcrun-base`(最低):裸元素的預設版面(padding/圓角/大小)——App 可以蓋掉\n // · `app`(中):App 自己的規則——版面歸它\n // · `arcrun`(最高):Portal 的 CIS 樣式表 + 全局色票/字體——**配色歸 Portal**\n // 同一層內仍照 specificity ⇒ App 想要不一樣的按鈕,用 Portal 自己的 class\n // `.btn` / `.btn3`,它們在最高層且 specificity 較高)**而不是自己的顏色**。\n // 這就是那條「連上全局的通道」:它有名字、可查、而且不做也會自動吃到。\n var APP_DOC_SELECTORS = { ':root': 1, 'html': 1, 'body': 1 };\n\n // 走訪一份樣式表的每一條規則(含 @media / @layer / @supports 內部)。\n // 由後往前跑,因為 `inherit` 模式要邊走邊 deleteRule。\n function walkStyleRules(parent, fn) {\n var rules = parent.cssRules;\n if (!rules) return;\n for (var i = rules.length - 1; i >= 0; i--) {\n var r = rules[i];\n if (typeof r.selectorText === 'string') fn(parent, i, r);\n if (r.cssRules && r.cssRules.length) walkStyleRules(r, fn);\n }\n }\n\n // App 的 `<style>` 幾乎都是針對整份文件寫的(`:root` / `html` / `body`)。這兩種模式\n // 對它們的處置**刻意不同**,而那個差別就是「跟不跟隨全局」的實體:\n //\n // own → 改指向 `:host`(=App 自己那個容器)⇒ App 照它原本設計的樣子呈現,\n // 只是被關在自己的盒子裡。(這是上一輪就有的行為,一個字沒改。)\n // inherit → **整條丟掉**。文件層的長相在這個模式下屬於 Portal,不屬於 App\n // 丟掉之後 App 那組 `--accent`/`--bg` 不存在了,全局的 `--ink`/`--amber`\n // 沿繼承鏈穿過 shadow 邊界進來 ⇒ 連明暗主題切換都自動跟著走。\n // ⚠️ 誠實標明代價:App 若把 `--gap: 12px` 這種**非配色**的變數也寫在 `:root`\n // 在 inherit 模式下會一起消失。要留住它們的 App 宣告 `own`。\n // 兩種模式都只動「純文件層」那幾條;`body .card {…}` 這種後代選擇器一律改寫成\n // `:host .card`(那是 App 的版面,兩種模式都該留著)。\n function applyAppStyleMode(sheet, inherit) {\n walkStyleRules(sheet, function (parent, index, r) {\n var parts = r.selectorText.split(',');\n var kept = [];\n for (var i = 0; i < parts.length; i++) {\n var p = parts[i].trim();\n if (APP_DOC_SELECTORS[p]) { if (!inherit) kept.push(':host'); continue; }\n kept.push(p.replace(/^(?::root|html|body)(?=[\\s>+~])/, ':host'));\n }\n if (!kept.length) { try { parent.deleteRule(index); } catch (e) { /* 刪不掉不致命 */ } return; }\n var next = kept.join(', ');\n // 改寫失敗不致命(shadow 邊界已經把外洩擋住了),跳過即可。\n if (next !== r.selectorText) { try { r.selectorText = next; } catch (e) { /* noop */ } }\n });\n }\n\n // 最低層:裸元素的預設版面。**只放版面、不放顏色**——顏色在最高層,這樣 App 才「改得動\n // 自己的版面、改不動整體的配色」。沒有任何樣式的 App 放進來也長得像個 Arcrun 畫面。\n var APP_BASE_CSS = [\n ':host{display:block;width:100%;box-sizing:border-box;padding:20px}',\n '*{box-sizing:border-box}',\n 'button{padding:11px 16px;border-radius:10px;border:1px solid transparent;cursor:pointer}',\n 'input,textarea,select{padding:12px 14px;border-radius:10px;border:1px solid transparent;max-width:100%}',\n 'textarea{width:100%}',\n 'h1{font-size:23px;letter-spacing:.06em}h2{font-size:19px}h3{font-size:17px}',\n 'h1,h2,h3,h4,h5,h6{margin:1.1em 0 .5em;font-weight:600}',\n 'table{border-collapse:collapse;width:100%}th,td{padding:8px 10px;text-align:left}',\n 'pre{overflow:auto;padding:12px;border-radius:10px}',\n 'img,svg,video,canvas{max-width:100%}',\n ].join('');\n\n // 最高層:**這一層就是「全局」**。配色、明暗、字體、行高全在這裡,App 蓋不掉\n // (層比 specificity 大)。所有值一律用 CIS token ⇒ 切深色/淺色時整個 App 跟著變。\n var APP_INHERIT_CSS = [\n ':host{background:transparent;color:var(--ink);',\n 'font-family:-apple-system,\"IBM Plex Sans\",\"PingFang TC\",\"Noto Sans TC\",\"Microsoft JhengHei\",system-ui,sans-serif;',\n 'font-size:16px;line-height:1.6;-webkit-font-smoothing:antialiased}',\n 'button{background:var(--amber);color:#FDFCFB;border-color:transparent;font-family:inherit;font-size:15px;font-weight:600}',\n 'button:disabled{opacity:.45;cursor:default}',\n 'input,textarea,select{background:rgba(var(--ink-rgb),.05);color:var(--ink);',\n 'border-color:rgba(var(--ink-rgb),.2);font-family:inherit;font-size:16px}',\n '::placeholder{color:rgba(var(--ink-rgb),.35)}',\n 'input:focus,textarea:focus,select:focus{outline:2px solid rgba(var(--amber-rgb),.5);outline-offset:1px}',\n 'a{color:var(--amber)}',\n 'h1,h2,h3,h4,h5,h6{color:var(--ink);font-family:inherit}',\n 'hr{border:none;border-top:1px solid rgba(var(--ink-rgb),.12)}',\n 'th,td{border-bottom:1px solid rgba(var(--ink-rgb),.12);color:var(--ink)}',\n 'code,pre,kbd,samp{font-family:ui-monospace,Menlo,monospace;background:rgba(var(--ink-rgb),.06);color:var(--ink)}',\n ].join('');\n\n function styleEl(css) {\n var s = document.createElement('style');\n s.textContent = css;\n return s;\n }\n\n // App 的 script 幾乎都用 `document.getElementById` 抓自己的節點,而那些節點現在住在\n // shadow 裡——真 `document` 找不到它們(也不該找得到,那正是隔離的意思)。\n // 這個替身只把三支查詢方法改指向 App 自己的 shadow root,其餘一律轉給真 document。\n function appDocumentFor(root) {\n if (typeof Proxy !== 'function') return document; // 極舊瀏覽器:退回原行為,不擋人用\n return new Proxy(document, {\n get: function (target, key) {\n if (key === 'getElementById') return function (id) { return root.getElementById(id); };\n if (key === 'querySelector') return function (sel) { return root.querySelector(sel); };\n if (key === 'querySelectorAll') return function (sel) { return root.querySelectorAll(sel); };\n var v = target[key];\n return typeof v === 'function' ? v.bind(target) : v;\n },\n set: function (target, key, value) { target[key] = value; return true; },\n });\n }\n\n function mountAppUi(host, html, style) {\n var inherit = style !== 'own'; // 沒宣告=跟隨全局(舊 App 也一併被修好,見 detailApp\n var root = host.attachShadow({ mode: 'open' });\n var cis = $('portal-cis');\n // ① 層序先宣告——`@layer` 的順序由**第一次出現**決定,所以這一行必須在任何規則之前。\n // 後面三疊(arcrun-base / app / arcrun)都指名進層,誰蓋得掉誰就不再看 specificity。\n if (inherit) root.appendChild(styleEl('@layer arcrun-base, app, arcrun;'));\n // ② 最低層:裸元素的預設版面(App 蓋得掉)。\n root.appendChild(styleEl(inherit ? '@layer arcrun-base{' + APP_BASE_CSS + '}' : ':host{display:block;width:100%}'));\n if (inherit && cis) {\n // Portal 的 CIS 樣式表放**最高層**App 用 `.btn3``.muted``.panel` 這些既有 class\n // 時,它們的 specificity 比我下面那組裸元素規則高 ⇒ App 表達得出「這顆是次要按鈕」。\n root.appendChild(styleEl('@layer arcrun{' + cis.textContent + '}'));\n }\n if (inherit) {\n // 最高層的第二疊=全局色票/字體本身。放在 CIS 之後(同一層),所以 `.btn3` 這種\n // class 規則靠 specificity 仍然贏過我這組裸元素規則 ⇒ App 表達得出樣式意圖。\n root.appendChild(styleEl('@layer arcrun{' + APP_INHERIT_CSS + '}'));\n } else if (cis) {\n // own 模式:整體樣式一樣給(App 想用 `.btn3` 就用得到),但不設層 ⇒ App 自己的\n // 規則排在後面,照常蓋得過去。「堅持用自己的」就是這個意思。\n root.appendChild(cis.cloneNode(true));\n }\n // ③ App 自己的畫面。用 <template> 解析:`<!doctype>/<html>/<head>/<body>` 會被丟掉、\n // 內容留下,而且這一步**不會**執行內嵌 `<script>`(跟 innerHTML 同樣的特性)。\n var tpl = document.createElement('template');\n tpl.innerHTML = html;\n var styles = Array.prototype.slice.call(tpl.content.querySelectorAll('style'));\n var scripts = Array.prototype.slice.call(tpl.content.querySelectorAll('script'));\n // inherit 模式把 App 的每一份 <style> 包進 `app` 層。包在**掛進 shadow 之前**做,\n // 這樣它一被解析就已經在層裡,不會有「先以無層身分套用一幀」的閃色。\n if (inherit) styles.forEach(function (st) { st.textContent = '@layer app{' + st.textContent + '}'; });\n root.appendChild(tpl.content);\n // ④ 文件層選擇器的處置=兩種模式的實體差別(見 applyAppStyleMode 的說明)。\n // 只動 App 自己帶進來的 style;上面那份 CIS 副本不碰——它的 html/body 規則在\n // shadow 裡本來就不命中,改寫反而會把 Portal 的紙紋畫進 App 的盒子。\n styles.forEach(function (st) { if (st.sheet) applyAppStyleMode(st.sheet, inherit); });\n return { root: root, scripts: scripts };\n }\n\n function runAppScripts(root, scripts) {\n var appDocument = appDocumentFor(root);\n scripts.forEach(function (old) {\n if (old.src) {\n // 外部 script 換不掉它看到的 `document`(那是瀏覽器決定的)⇒ 照舊丟進文件執行。\n // 誠實標明:這種 App 的 script 抓不到自己 shadow 裡的畫面。v0 的 App 都是單檔內嵌。\n var s = document.createElement('script');\n s.src = old.src;\n old.parentNode.replaceChild(s, old);\n return;\n }\n try {\n (new Function('document', old.textContent)).call(window, appDocument);\n } catch (e) {\n // App 自己壞掉不該把 Portal 一起帶走——記進 console,畫面留著讓人看得出哪裡壞。\n console.error('[arcrun-app] App 的 script 執行失敗:', e);\n }\n });\n }\n\n // 離開 App 頁時把整個 shadow host 丟掉。Shadow DOM 已經擋住樣式外洩,這一步是為了\n // 不讓上一個 App 的畫面、計時器、監聽器留在文件裡(切頁只 toggle class,不會自己清)。\n function unmountAppUi() {\n var body = $('app-body');\n if (body) body.innerHTML = '';\n }\n\n function renderAppView(app) {\n var body = $('app-body');\n $('app-title').textContent = esc(app.icon || '') + ' ' + esc(app.name || app.id);\n if (app.has_ui && app.ui_html) {\n // 缺口①:畫面落地在本實例自己的資料層,執行期由 Portal 動態載入——不是編進出貨的 UI bundle。\n body.innerHTML = '';\n var host = document.createElement('div');\n host.id = 'app-ui-mount';\n body.appendChild(host);\n // 暴露給 App 自己畫面用的唯一通道(K6):只能呼叫白名單動作,拿不到任何金鑰/租戶字串。\n // 🔴 順序:**在跑 App 的 script 之前**就備妥——App 才不必為了「Portal 何時賦值」\n // 寫 setTimeout 之類的時序自保(舊版是先跑 script 才賦值)。\n window.arcrunApp = window.arcrunApp || {};\n window.arcrunApp.action = function (action, payload) { return callAppAction(app.id, action, payload); };\n // 掛載協定 v0.2:樣式模式由 App 的宣告決定(沒宣告=跟隨全局)。\n var mounted = mountAppUi(host, app.ui_html, app.ui_style);\n runAppScripts(mounted.root, mounted.scripts);\n } else {\n // K3/§2.1 系統預設畫面:沒有畫面的 App 一樣要有東西可點——這條工作流是什麼、\n // 上次跑得怎樣、要不要現在跑一次。\n var wfs = app.workflows || [];\n if (!wfs.length) {\n body.innerHTML = '<div class=\"muted\" style=\"padding:20px 4px\">這個 App 還沒有任何工作流。</div>';\n } else {\n body.innerHTML = wfs.map(function (w) {\n return '<div class=\"card-item\">' +\n '<div style=\"display:flex;align-items:baseline;gap:10px;flex-wrap:wrap\">' +\n '<span class=\"serif\" style=\"font-size:17px;font-weight:600\">' + esc(w.name) + '</span>' +\n '<button class=\"btn3\" style=\"margin-left:auto\" data-run-action=\"' + esc(w.name) + '\">現在執行</button></div>' +\n (w.description ? '<div style=\"margin-top:8px;font-size:14.5px;color:rgba(var(--ink-rgb),.65)\">' + esc(w.description) + '</div>' : '<div class=\"dim\" style=\"margin-top:8px;font-size:13.5px\">(沒有描述)</div>') +\n '<div class=\"dim mono\" data-run-status style=\"margin-top:6px;font-size:13px\"></div>' +\n '</div>';\n }).join('');\n Array.prototype.forEach.call(body.querySelectorAll('[data-run-action]'), function (btn) {\n btn.addEventListener('click', function () {\n var action = btn.getAttribute('data-run-action');\n var statusEl = btn.closest('.card-item').querySelector('[data-run-status]');\n // 這個動作是否在白名單內由 server 決定(前端只是顯示,藏不藏都繞不過)。\n statusEl.textContent = '執行中…';\n btn.disabled = true;\n callAppAction(app.id, action, {}).then(function (x) {\n btn.disabled = false;\n if (guard401(x.status)) return;\n if (x.ok && x.d && x.d.ok) {\n statusEl.textContent = '完成:' + JSON.stringify(x.d.result || {}).slice(0, 160);\n } else {\n statusEl.textContent = '失敗:' + ((x.d && x.d.error) || ('HTTP ' + x.status));\n }\n }).catch(function (e) {\n btn.disabled = false;\n statusEl.textContent = '請求失敗:' + friendlyErr(e);\n });\n });\n });\n }\n }\n // 卸載(design §四③:「刪 icon 就是刪 App」):admin-only,與 /portal/data/workflows 同一道閘。\n var canRemove = !!(S.profile && S.profile.role === 'admin');\n var removeBtn = $('app-remove-btn');\n removeBtn.classList.toggle('hide', !canRemove);\n // 兩段式確認,理由同 armRemoveButton():內建 confirm() 在某些內嵌瀏覽器會靜默回 false,\n // 那會讓「按了移除但什麼都沒發生」成為常態——本票明講不准有靜默失敗。\n removeBtn.onclick = function () {\n if (removeBtn.dataset.armed !== '1') { armRemoveButton(removeBtn, app.name || app.id, '移除這個 App'); return; }\n disarmRemoveButton(removeBtn);\n fetch(API_BASE + '/portal/data/apps/' + encodeURIComponent(app.id), { method: 'DELETE', headers: authHeaders() })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { toast((x.d && x.d.error) || '移除失敗'); return; }\n toast('已移除');\n nav('launcher'); // Arcrun#152:回「App 界面」(原本回 search,四頁簽改版後首頁是啟動器)\n renderAppsNav(); // App 界面磚塊格/App 市集清單重新整理——這一格立刻消失,不用整頁重新整理\n })\n .catch(function (e) { toast('請求失敗:' + friendlyErr(e)); });\n };\n }\n\n // ── 上傳頁(portal-demo-suite)──\n // 前端只讀檔轉 base64 逐檔 POST /portal/data/uploadGitea 位置與 token 全在 server 側,\n // 這裡看不到任何 repo/token 字串。逐檔獨立狀態列(一檔失敗不拖累其他檔)。\n var UP_OK_MSG = '已收件,約 1 分鐘後可在搜尋頁找到;AI 整理後會以 wiki 卡形式出現。';\n function b64FromDataUrl(dataUrl) {\n var i = String(dataUrl).indexOf(',');\n return i >= 0 ? String(dataUrl).slice(i + 1) : '';\n }\n function uploadStatusRow(name) {\n var row = document.createElement('div');\n row.className = 'card-item';\n row.innerHTML = '<div style=\"display:flex;align-items:baseline;gap:10px;flex-wrap:wrap\">' +\n '<span class=\"mono\" style=\"font-size:14.5px;word-break:break-all\">' + esc(name) + '</span>' +\n '<span class=\"up-st muted\" style=\"margin-left:auto;font-size:14px;flex:none\">讀取中…</span></div>' +\n '<div class=\"up-msg\" style=\"margin-top:6px;font-size:13.5px;line-height:1.65;color:rgba(var(--ink-rgb),.6)\"></div>';\n $('up-list').prepend(row);\n return row;\n }\n function setRowStatus(row, ok, statusText, msg) {\n var st = row.querySelector('.up-st');\n st.textContent = statusText;\n st.className = 'up-st ' + (ok === true ? 'ok' : (ok === false ? 'err' : 'muted'));\n row.querySelector('.up-msg').textContent = msg || '';\n }\n function uploadOneFile(file) {\n var row = uploadStatusRow(file.name);\n if (!/\\.(md|txt)$/i.test(file.name)) {\n setRowStatus(row, false, '已略過', '只收 .md / .txt 檔。');\n return;\n }\n var reader = new FileReader();\n reader.onerror = function () { setRowStatus(row, false, '讀檔失敗', '瀏覽器讀不到這個檔案。'); };\n reader.onload = function () {\n setRowStatus(row, null, '上傳中…', '');\n fetch(API_BASE + '/portal/data/upload', {\n method: 'POST',\n headers: Object.assign({ 'Content-Type': 'application/json' }, authHeaders()),\n body: JSON.stringify({ filename: file.name, content_b64: b64FromDataUrl(reader.result) })\n })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { setRowStatus(row, false, '失敗', x.d.error || ('HTTP ' + x.status)); return; }\n setRowStatus(row, true, '已收件', UP_OK_MSG + (x.d.path ? '' + x.d.path + '' : ''));\n })\n .catch(function (e) { setRowStatus(row, false, '失敗', friendlyErr(e)); });\n };\n reader.readAsDataURL(file); // dataURL 尾段即 base64unicode 安全,免手刻 btoa 編碼)\n }\n function handleUploadFiles(files) {\n for (var i = 0; i < files.length; i++) uploadOneFile(files[i]);\n }\n $('up-pick').addEventListener('click', function (ev) { ev.stopPropagation(); $('up-file').click(); });\n $('up-drop').addEventListener('click', function () { $('up-file').click(); });\n $('up-file').addEventListener('change', function () { handleUploadFiles(this.files); this.value = ''; });\n ['dragover', 'dragenter'].forEach(function (t) {\n $('up-drop').addEventListener(t, function (ev) { ev.preventDefault(); ev.currentTarget.style.background = 'rgba(var(--amber-rgb),.12)'; });\n });\n ['dragleave', 'drop'].forEach(function (t) {\n $('up-drop').addEventListener(t, function (ev) { ev.preventDefault(); ev.currentTarget.style.background = 'rgba(var(--amber-rgb),.04)'; });\n });\n $('up-drop').addEventListener('drop', function (ev) {\n if (ev.dataTransfer && ev.dataTransfer.files && ev.dataTransfer.files.length) handleUploadFiles(ev.dataTransfer.files);\n });\n\n // ── 管理頁(P4:帳號管理+庫目錄管理。全走 /portal/admin/*,前端零業務邏輯;\n // 一般用戶 nav 不顯示,且 server role 閘 403——藏只是 UX,真閘在後端)──\n var adminLibs = []; // 庫目錄快取(渲染每帳號的庫勾選用)\n\n function adminApi(method, path, body) {\n path = API_BASE + path;\n var opt = { method: method, headers: Object.assign({ 'Content-Type': 'application/json' }, authHeaders()) };\n if (body !== undefined) opt.body = JSON.stringify(body);\n return fetch(path, opt).then(function (r) {\n return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; });\n });\n }\n\n // 執行紀錄保留期(P7):GET/PUT /portal/admin/execution-log-retentionrole=admin 閘(server 端)。\n // retention_days: number=自訂天數;null=「不刪除」(企業稽核,leo 08-07:「我願意花很多錢\n // 保存,不要刪除」);未設定過的租戶也會回一個值(KBDB 端退回預設 90 天)。\n function loadRetention() {\n $('ad-ret-current').textContent = '載入中…';\n adminApi('GET', '/portal/admin/execution-log-retention')\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { $('ad-ret-current').textContent = ''; $('ad-ret-status').textContent = x.d.error || ('讀取失敗(HTTP ' + x.status + ''); return; }\n var days = x.d.retention_days;\n var def = x.d.default_days || 90;\n $('ad-ret-never').checked = (days === null);\n $('ad-ret-days').value = (days === null) ? '' : (days != null ? days : def);\n $('ad-ret-days').disabled = (days === null);\n $('ad-ret-current').textContent = (days === null)\n ? '目前設定:不刪除(企業稽核)'\n : '目前設定:保留 ' + (days != null ? days : def) + ' 天' + (days == null ? '(沿用預設值,尚未自訂)' : '');\n })\n .catch(function (e) { $('ad-ret-current').textContent = ''; $('ad-ret-status').textContent = friendlyErr(e); });\n }\n $('ad-ret-never').addEventListener('change', function () {\n $('ad-ret-days').disabled = this.checked;\n });\n $('ad-ret-save').addEventListener('click', function () {\n var st = $('ad-ret-status');\n st.textContent = '';\n var never = $('ad-ret-never').checked;\n var body;\n if (never) {\n body = { retention_days: null };\n } else {\n var n = parseInt($('ad-ret-days').value, 10);\n if (!n || n <= 0) { st.textContent = '請輸入大於 0 的天數,或勾「不刪除」'; return; }\n body = { retention_days: n };\n }\n $('ad-ret-save').disabled = true;\n adminApi('PUT', '/portal/admin/execution-log-retention', body)\n .then(function (x) {\n $('ad-ret-save').disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) { st.textContent = x.d.error || ('儲存失敗(HTTP ' + x.status + ''); return; }\n toast('保留期已更新');\n loadRetention();\n })\n .catch(function (e) { $('ad-ret-save').disabled = false; st.textContent = friendlyErr(e); });\n });\n\n function loadAdmin() {\n $('ad-users').innerHTML = '<div class=\"muted\">載入中…</div>';\n $('ad-libs').innerHTML = '<div class=\"muted\">載入中…</div>';\n loadRetention();\n adminApi('GET', '/portal/admin/libraries')\n .then(function (x) {\n if (guard401(x.status)) return;\n if (!x.ok) { $('ad-libs').innerHTML = '<div class=\"err\">' + esc(x.d.error || ('讀取失敗(HTTP ' + x.status + '')) + '</div>'; return; }\n adminLibs = x.d.libraries || [];\n renderAdminLibs();\n return adminApi('GET', '/portal/admin/users').then(function (y) {\n if (guard401(y.status)) return;\n if (!y.ok) { $('ad-users').innerHTML = '<div class=\"err\">' + esc(y.d.error || ('讀取失敗(HTTP ' + y.status + '')) + '</div>'; return; }\n renderAdminUsers(y.d.users || []);\n });\n })\n .catch(function (e) {\n $('ad-users').innerHTML = '<div class=\"err\">請求失敗:' + esc(friendlyErr(e)) + '</div>';\n $('ad-libs').innerHTML = '';\n });\n }\n\n // 帳號可勾選的庫=庫目錄啟用中的庫 ∪ general ∪ 該帳號已有的庫(目錄外的舊授權不憑空消失)\n function checkableLibs(userLibs) {\n var names = ['general'];\n adminLibs.forEach(function (l) { if (l.status !== 'disabled' && l.name && names.indexOf(l.name) < 0) names.push(l.name); });\n (userLibs || []).forEach(function (n) { if (n !== '*' && names.indexOf(n) < 0) names.push(n); });\n return names;\n }\n function libDisplay(name) {\n for (var i = 0; i < adminLibs.length; i++) { if (adminLibs[i].name === name) return adminLibs[i].display_name || name; }\n return name;\n }\n\n function renderAdminUsers(users) {\n if (!users.length) { $('ad-users').innerHTML = '<div class=\"muted\" style=\"padding:16px 4px\">還沒有任何同仁帳號——用上面的表單新增第一個。</div>'; return; }\n $('ad-users').innerHTML = users.map(function (u) {\n var star = (u.libraries || []).indexOf('*') >= 0;\n var disabled = u.status === 'disabled';\n var libChips = star\n ? '<span class=\"tag green\">全部知識庫</span>'\n : ((u.libraries || []).length\n ? (u.libraries || []).map(function (n) { return '<span class=\"tag green\">' + esc(libDisplay(n)) + '</span>'; }).join(' ')\n : '<span class=\"tag dim\">尚未授權任何庫</span>');\n var boxes = '<label><input type=\"checkbox\" data-star data-uid=\"' + esc(u.record_id) + '\"' + (star ? ' checked' : '') + '> 全部知識庫(*</label>' +\n checkableLibs(u.libraries).map(function (n) {\n var on = !star && (u.libraries || []).indexOf(n) >= 0;\n return '<label><input type=\"checkbox\" data-lib=\"' + esc(n) + '\" data-uid=\"' + esc(u.record_id) + '\"' + (on ? ' checked' : '') + (star ? ' disabled' : '') + '> ' + esc(libDisplay(n)) + ' <span class=\"dim mono\" style=\"font-size:12px\">' + esc(n) + '</span></label>';\n }).join('');\n return '<div class=\"card-item\"' + (disabled ? ' style=\"opacity:.6\"' : '') + '>' +\n '<div style=\"display:flex;align-items:baseline;gap:10px;flex-wrap:wrap\">' +\n '<span class=\"serif\" style=\"font-size:17px;font-weight:600\">' + esc(u.display_name || u.email) + '</span>' +\n '<span class=\"muted\" style=\"font-size:14px;word-break:break-all\">' + esc(u.email) + '</span>' +\n '<span class=\"tag' + (u.role === 'admin' ? '' : ' dim') + '\">' + (u.role === 'admin' ? '管理員' : '一般用戶') + '</span>' +\n '<span class=\"tag ' + (disabled ? 'dim' : 'green') + '\">' + (disabled ? '已停用' : '啟用中') + '</span></div>' +\n '<div style=\"margin-top:9px;display:flex;gap:6px;flex-wrap:wrap;align-items:center;font-size:13px\"><span class=\"muted\" style=\"flex:none\">可查庫</span>' + libChips + '</div>' +\n '<div class=\"btnrow\">' +\n '<button class=\"btn2\" data-act=\"edit-libs\" data-uid=\"' + esc(u.record_id) + '\">編輯可查庫</button>' +\n '<button class=\"btn2\" data-act=\"reset-pw\" data-uid=\"' + esc(u.record_id) + '\" data-email=\"' + esc(u.email) + '\">重設密碼</button>' +\n (disabled\n ? '<button class=\"btn2\" data-act=\"set-status\" data-next=\"active\" data-uid=\"' + esc(u.record_id) + '\">啟用</button>'\n : '<button class=\"btn2 warn\" data-act=\"set-status\" data-next=\"disabled\" data-uid=\"' + esc(u.record_id) + '\" data-email=\"' + esc(u.email) + '\">停用</button>') +\n '</div>' +\n '<div id=\"libed-' + esc(u.record_id) + '\" style=\"display:none\">' +\n '<div class=\"libgrid\">' + boxes + '</div>' +\n '<div class=\"btnrow\"><button class=\"btn2\" data-act=\"save-libs\" data-uid=\"' + esc(u.record_id) + '\">儲存庫權限</button>' +\n '<span class=\"dim\" style=\"font-size:13px;align-self:center\">勾「全部知識庫」=不限庫;否則至少勾一個。儲存後立即生效。</span></div></div>' +\n '</div>';\n }).join('');\n }\n\n // ── ① 同步庫總覽(Arcrun#144 判準第 1 條的第三個入口)─────────────────────────\n //\n // leo 2026-08-19:「**除了搜尋,庫目錄管理也要改成這樣**,因為這就是檔案夾,\n // 模擬他常用的檔案夾。」\n // ⇒ 這一頁**不再是一疊卡片**,而是跟檢索過程樹、單一資料夾樹**同一棵**檔案總管式的樹:\n // 總庫 → 機器 → 資料夾(庫)→ 子資料夾/檔案。四層一路縮排下去,點得開收得起。\n //\n // 🔴 這裡只組**資料**,一行畫面都不畫——畫法只有 renderTreetreeNodeHtml 一份。\n // 第四層(子資料夾/檔案)的資料由 folderTreeData() 出,也就是入口②:\n // 兩個入口的資料在這裡接成一棵連續的樹,而不是各畫各的兩棵。\n //\n // 🔴 機器那一層現在**沒有資料**collector 全 repo 沒有 hostnamemachine id),\n // 所以誠實長成一個「未知來源」節點,不拿 root 路徑或帳號名去頂替。\n // 資料補上之後這一層會自然分裂成好幾台,畫面這端不需要再改。\n function adminLibsTreeData(libs) {\n var synced = 0, total = 0, unreported = 0;\n var libNodes = (libs || []).map(function (l) {\n var s = l.folder_tree;\n if (s) { synced += s.synced_files || 0; total += s.total_files || 0; } else { unreported++; }\n return libTreeNode(l);\n });\n // 🔴 leo 2026-08-28:「GUI 是國際通用,圖形指示,不限語文⋯⋯任何出現文字都要謹慎,\n // 文字表示『我的設計不夠好所以要靠文字來補』」⇒ 這一列以前是三句話\n // (「N 個資料夾・已同步 X / 共 Y 份文件・M 個還沒回報結構」),現在是三組圖示+數字,\n // 每一組是什麼**滑過去才講**(下面那個 pop)。資訊一件都沒少,只是不再佔版面。\n var head = '<b>總庫</b>'\n + '<span class=\"ft-sep\">·</span><span class=\"tt-count\">🗂 ' + libNodes.length + '</span>'\n + (total > 0 ? '<span class=\"ft-sep\">·</span><span class=\"tt-count tt-docs\">' + synced + ' / ' + total + '</span>' : '')\n + (unreported > 0 ? '<span class=\"ft-sep\">·</span><span class=\"tt-count\">❔ ' + unreported + '</span>' : '')\n + '<span class=\"ft-sep\">·</span><span class=\"ft-hint\">ⓘ</span>';\n var headPop = '<div class=\"pop-n\">'\n + '<span class=\"tt-pill\">🗂 ' + libNodes.length + ' 個資料夾</span>'\n + (total > 0 ? '<span class=\"tt-pill\">📄 ' + synced + ' / ' + total + ' 已同步/總共</span>' : '')\n + (unreported > 0 ? '<span class=\"tt-pill\">❔ ' + unreported + ' 個還沒回報結構</span>' : '')\n + '</div>';\n // 機器層:拿不到值 ⇒ 明說未知(D97「不准拿別的東西頂替」)\n var machine = {\n content: '<span class=\"tt-machine tt-unknown\">未知來源</span>'\n + '<span class=\"ft-sep\">·</span><span class=\"ft-hint\">ⓘ</span>',\n payload: { pop: '<div class=\"pop-t\">🖥 未知來源</div>'\n + '<div class=\"pop-why\">同步小幫手還沒回報是哪一台機器。</div>' },\n children: libNodes\n };\n return { content: head, payload: { pop: headPop }, children: [machine] };\n }\n\n // 收檔策略 → 一顆膠囊上的短語(leo 2026-08-28 管它叫 `types`:「判定形式」)。\n // 🔴 這裡**不做判斷**,只是把地端已經判好的 mode 翻成人看得懂的兩三個字\n // (判準活在 collector/ingestplan.go 的 PlanIngest,畫面不准有第二份)。\n function libTypeLabel(mode) {\n if (mode === 'all') return '一般資料夾';\n if (mode === 'curated-wiki') return '專案・讀 wiki';\n if (mode === 'docs-only') return '專案・讀文件';\n return mode ? String(mode) : '';\n }\n\n // 一個庫 → 第三層的那一列(+行尾的「移除」)。\n //\n // 🔴 inkstone/Arcrun#180leo 2026-08-28):**一列只留「路徑 文件 X/Y」。**\n // 原話:「每個資料夾的說明又長又囉嗦,只要符號加數字⋯⋯**只要「文件」即可,\n // 其他的可以放在一個 moseover 彈出視窗裡**」。他的截圖裡十列講的是同兩句判定語\n // (「這是一般資料夾或筆記庫(⋯)」/「這是一個開發專案(⋯)」),每列被吃掉一半。\n //\n // 🔴 **「收起來」不等於「刪掉」**:判定語、知識卡數、關聯數、根路徑全都還在,\n // 只是搬進滑過去才出現的那一格(arcrun-rag#136 要靠判定語救回被跳過的資料夾)。\n // 例外是「異常」——停用/小幫手沒在同步這兩件,留在列上但**縮成一個記號**:\n // 它們正是本票標題那半(「一眼分得出哪些還在同步、哪些是歷史殘留」),\n // 藏進彈出視窗就等於要使用者逐列滑過去才發現哪個庫是殘留的。\n function libTreeNode(l) {\n var name = l.name || '';\n var s = l.folder_tree;\n var shown = l.display_name || name;\n var label = '<span class=\"tt-folder\">' + esc(shown) + '</span>';\n // 顯示名與庫鍵一樣時不重複寫兩遍(leo 截圖:`kb-youlin · kb-youlin`\n if (name && name !== shown) label += '<span class=\"ft-sep\">·</span><span class=\"tt-path\">' + esc(name) + '</span>';\n if (s) {\n var full = s.total_files > 0 && s.synced_files === s.total_files;\n label += '<span class=\"ft-sep\">·</span><span class=\"tt-count tt-docs\"' + (full ? ' style=\"color:var(--ink)\"' : '') + '>'\n + s.synced_files + ' / ' + s.total_files + '</span>';\n }\n // 異常=記號,不是句子(全文在彈出視窗裡)\n if (l.status === 'disabled') label += '<span class=\"ft-sep\">·</span><span class=\"tt-unknown\">⏻</span>';\n if (l.daemon_watching === false) label += '<span class=\"ft-sep\">·</span><span class=\"tt-skip\">⏸</span>';\n label += '<span class=\"ft-sep\">·</span><span class=\"ft-hint\">ⓘ</span>';\n\n // ── 滑過去才出現的那一格:符號+數字+短語(紅線:不要把長句搬進來繼續長)──\n var pills = [];\n if (s) pills.push('<span class=\"tt-pill\">📄 ' + s.synced_files + ' / ' + s.total_files + '</span>');\n if (l.card_count > 0) pills.push('<span class=\"tt-pill\">🗂 ' + l.card_count + '</span>');\n if (l.triplet_count > 0) pills.push('<span class=\"tt-pill\">🔗 ' + l.triplet_count + '</span>');\n var types = s ? libTypeLabel(s.mode) : '';\n if (types) pills.push('<span class=\"tt-pill is-type\">' + esc(types) + '</span>');\n if (l.status === 'disabled') pills.push('<span class=\"tt-pill is-warn\">⏻ 已停用</span>');\n if (l.daemon_watching === false) pills.push('<span class=\"tt-pill is-warn\">⏸ 小幫手沒在同步</span>');\n if (!pills.length) pills.push('<span class=\"tt-pill\">還沒有內容</span>');\n var pop = '<div class=\"pop-t\">📁 ' + esc(shown) + '</div>'\n + '<div class=\"pop-n\">' + pills.join('') + '</div>'\n + (s && s.reason ? '<div class=\"pop-why\">' + esc(s.reason) + '</div>' : '')\n + (!l.auto && l.description ? '<div class=\"pop-why\">' + esc(l.description) + '</div>' : '')\n + (s && s.root ? '<div class=\"pop-path\">' + esc(s.root) + '</div>' : '');\n\n // 「移除」兩個字 → 垃圾桶(leo 2026-08-28 的判準:先找圖形)。\n // 🔴 敢圖示化是因為**兩條路徑都有二次確認**(confirm/要求打出庫名,見 #v-admin 的委派),\n // 誤按不會直接掉東西;title 仍留著給讀螢幕器與滑鼠停留。\n var tail = l.auto\n ? '<button class=\"btn3\" title=\"移除\" data-act=\"lib-remove-auto\" data-name=\"' + esc(name) + '\">🗑</button>'\n : '<button class=\"btn3\" title=\"移除\" data-act=\"lib-remove\" data-id=\"' + esc(l.record_id) + '\" data-name=\"' + esc(l.display_name || name) + '\">🗑</button>';\n\n // 第四層。小幫手沒報過結構 ⇒ 講人話,而且不掛延後載入的標記(沒有東西可以抓)。\n if (!s) {\n return { content: label, tail: tail, payload: { fold: 1, pop: pop },\n children: [{ content: '<span class=\"tt-dim\">同步小幫手還沒回報這個資料夾的結構(更新小幫手後會出現)</span>' }] };\n }\n return { content: label, tail: tail,\n // 展開狀態由使用者決定,重畫時記得住(移除某個庫之後整棵會重畫)\n payload: { fold: treeOpen[name] ? 0 : 1, pop: pop, attrs: 'data-libtree=\"' + esc(name) + '\"' },\n children: folderKidsData(name) };\n }\n\n // 第四層的資料:還沒抓=一列「點開才去拿」的佔位(真的點開時才發請求)。\n function folderKidsData(name) {\n var tree = folderTrees[name];\n if (tree === undefined) return [{ content: '<span class=\"tt-dim\">載入中…</span>' }];\n if (tree === null) return [{ content: '<span class=\"tt-dim\">同步小幫手還沒回報這個資料夾的結構。</span>' }];\n if (!(tree.nodes || []).length) {\n return [{ content: '<span class=\"tt-dim\">這個資料夾目前沒有任何內容(指定了就會在這裡,等你放東西進去)。</span>' }];\n }\n var roots = folderTreeData(tree); // ← 入口②:同一支 folderTreeData,同一支畫法\n // 🔴 第三層那一列**就是**這個被看守的資料夾本身,而入口②的根節點也是它\n // ⇒ 直接接上去會連著兩列同名同數字,看起來像壞掉。攤掉那個根、接它的小孩,\n // 才真的是規格說的四層(總庫 → 機器 → 資料夾 → 子資料夾/檔案)。\n // 只有一個根、而且它底下真的有東西時才攤——被整棵剪掉(skipped)的根要留著,\n // 因為那一列講的是「為什麼沒走進去」,攤掉就把理由弄不見了。\n // 🔴 這裡曾經多掛一列「⊘ = 這一層的檔案這次沒收:<理由>(N 個資料夾)」當圖例。\n // 2026-08-28 拿掉了:**用一句話解釋一個圖形,就是那個圖形沒做好**\n // (leo:「文字表示我的設計不夠好所以要靠文字來補」)。\n // 理由沒有消失——它掛在每一格的 ⊘/ⓘ 上,滑過去就看得到。\n return (roots.length === 1 && roots[0].children.length) ? roots[0].children : roots;\n }\n\n function renderAdminLibs() {\n var box = $('ad-libs');\n if (!adminLibs.length) { box.innerHTML = '<div class=\"muted\" style=\"padding:16px 4px\">同步小幫手還沒送來任何庫。裝好小幫手並選好資料夾後,庫會自動出現在這裡。</div>'; return; }\n // 這兩行以前永遠攤在樹底下。它們沒有被刪掉,是被收進一個 ⓘ 裡(leo 2026-08-28 第 3 步):\n // 真的只能用字 ⇒ 收進滑過去才出現的地方,不佔預設版面。\n var foot = '<span class=\"ft-hint\" data-pop=\"'\n + esc('<div class=\"pop-why\">數字是「已同步 / 這個資料夾底下總共」。兩者不相等是正常的'\n + '——差額是還讀不了的格式、不在收檔範圍的檔案(程式碼等),或還在處理中。</div>')\n + '\">ⓘ</span>';\n renderTree(box, adminLibsTreeData(adminLibs), foot);\n }\n\n // ── 資料夾結構(InkStoneCo#44leo 2026-08-17)────────────────────────────\n //\n // leo 的原話就是規格:「tree 在 portal 上應該顯示該資料夾的 tree,並顯示該資料夾\n // 每個子資料夾的『同步文件數/總文件數』,不上傳通常是不支援,比如程式碼、不支援的格式。」\n //\n // 🔴 **這裡一個判準都不自己發明**:兩個數字(分子分母)與「為什麼沒收」的分類\n // 全部由同步小幫手算好送上來(判準活在 collector/scan.go 那趟 WalkDir)。\n // 畫面只做兩件事:把節點串成樹、把子樹的數字加起來。\n // 若有一天要在這裡寫「.py 算不算支援」之類的判斷 —— 停手,那是第二份實作。\n var folderTrees = {}; // library → 樹(展開時才抓;null=抓過但小幫手還沒報)\n var treeOpen = {}; // library → 是否展開(重畫整棵時記得住使用者展開了哪幾支)\n var folderTreeLoading = {}; // library → 這一支正在抓(避免連點兩下發兩次請求)\n\n // 差額必須解釋得了:總數 − 已同步 = 不支援 + 不收 + 處理中。\n // 這行文案的存在理由就是 leo 那句「不上傳通常是不支援」——畫面要自己回答,不必問人。\n // 🔴 參數是 rollupTree() 疊出來的**子樹合計**(鍵名 total/synced/pending/unsupported/\n // excluded),**不是**小幫手送上來的節點(那個是 total_files/unsupported_files…)。\n // 2026-08-17 實撞:這裡原本讀 `n.unsupported_files`,而呼叫端傳的是合計物件\n // ⇒ 三個判斷全部拿到 undefined ⇒ 這行字**永遠是空的**。\n // 症狀特別惡劣:畫面看起來很正常(數字都在、也對),只是「為什麼沒上傳」那句\n // 安靜地不見了——而那正是 leo 要這個畫面回答的**唯一那件事**\n // (「不上傳通常是不支援,比如程式碼、不支援的格式」)。\n // ⇒ 這種 bug 只有真的用瀏覽器看畫面才抓得到,單元測試與 curl 都會放它過。\n function gapWhy(s) {\n var parts = [];\n if (s.unsupported > 0) parts.push(s.unsupported + ' 份格式還讀不了');\n if (s.excluded > 0) parts.push(s.excluded + ' 份不在收檔範圍(程式碼等)');\n if (s.pending > 0) parts.push(s.pending + ' 份處理中');\n return parts.join('・');\n }\n\n // 子樹合計:小幫手送的每個數字都只算「這一層直接放的檔」(存兩套遲早對不起來),\n // 所以要顯示「這個資料夾底下總共」就得在這裡疊一次。\n //\n // 🔴 inkstone/Arcrun#1802026-08-28):**skipped 的節點現在照樣要疊進來。**\n // 以前這裡寫 `if (!n.skipped)`,理由是「沒走進去就是不知道裡面有幾個檔」——\n // 在**當時**是對的,因為那一版的 skipped = 整棵被剪掉、數字全是 0 而且是編的。\n // #180 之後 collector 的收檔範圍已從剪枝搬到 CollectsDirWhy**走訪照走、數字照算**\n // skipped 只表示「這一層的檔這次不收」。⇒ 那個 if 現在丟掉的是**量出來的真數字**。\n // 實測(youlinhsieh-test1`~/.arcrun-rag/folder-trees.json`):\n // 全部節點相加 收 18 / 共 37\n // 跳過 skipped 之後 收 18 / 共 19 ← 差的 18 份全在 skipped 的那 9 個資料夾裡\n // 真正沒走進去的那種(node_modules 等)數字本來就是 0,加 0 不影響分母;\n // **它們的差別在「畫面該不該寫數字」,不在「要不要疊」**(見 folderTreeData)。\n function rollupTree(nodes) {\n var byPath = {}, kids = {};\n nodes.forEach(function (n) { byPath[n.path] = n; (kids[n.parent] = kids[n.parent] || []).push(n); });\n var sums = {};\n function walk(n) {\n var acc = {\n total: n.total_files || 0, synced: n.synced_files || 0, pending: n.pending_files || 0,\n unsupported: n.unsupported_files || 0, excluded: n.excluded_files || 0\n };\n (kids[n.path] || []).forEach(function (c) {\n var s = walk(c);\n acc.total += s.total; acc.synced += s.synced; acc.pending += s.pending;\n acc.unsupported += s.unsupported; acc.excluded += s.excluded;\n });\n sums[n.path] = acc;\n return acc;\n }\n // 根=parent 是 '-'collector/foldertree.go 刻意用它,才分得出「我是根」與「父親是根」)\n (kids['-'] || []).forEach(walk);\n // 孤兒節點(父親不在這份清單裡,例如樹被截斷)也要走一次,否則它整棵不會被算到\n nodes.forEach(function (n) { if (sums[n.path] === undefined && !byPath[n.parent]) walk(n); });\n return { sums: sums, kids: kids };\n }\n\n // 資料夾樹 → renderTree 吃的節點(Arcrun#144 判準第 1 條:三個入口共用同一份 render)。\n // 🔴 這裡只組**資料**,一行畫面都不畫——畫法只有 renderTree 一份。\n // 2026-08-19 之前這支自己 emit 一堆 div,於是它有縮排卻**點不開**\n // (判準第 3 條要的展開/收合,是換到共用 render 之後才拿到的)。\n // 回傳節點陣列(這份清單可以有多個根)。\n function folderTreeData(tree) {\n var nodes = (tree && tree.nodes) || [];\n var r = rollupTree(nodes);\n function build(n) {\n var s = r.sums[n.path] || { total: 0, synced: 0, pending: 0, unsupported: 0, excluded: 0 };\n var label = '<span class=\"tt-folder\">' + esc(n.name || '(未命名)') + '</span>';\n // ── 這一層沒收的記號(inkstone/Arcrun#180leo 2026-08-28)──────────────\n // 以前這裡寫「整個資料夾未收 · <一整句理由>」,而同一句理由在一棵樹裡會重複 8 次\n // ⇒ leo:「用一個 emoticon 就好了,這段太囉嗦」。\n // 現在:一個 ⊘,理由搬進滑過去才出現的那一格;有哪幾種理由彙整在樹腳那一列。\n if (n.skipped) label += '<span class=\"ft-sep\">·</span><span class=\"tt-skip\">⊘</span>';\n // ── 數字:**量得出來才寫**(票上第 3 條:數字不能說謊)───────────────────\n // skipped 有兩種,差別就在這裡:\n // ① 走進去了、這一層的檔不收(範本、非本次收檔範圍)⇒ 分母是真的 ⇒ 照寫\n // 實測 `scripts` 底下真的有 1 個檔 ⇒ 要顯示 `0 / 1`,寫 0/0 才是說謊。\n // ② 真的沒走進去(node_modules 這類)⇒ 分母不知道 ⇒ **一個數字都不寫**,\n // 只留 ⊘。這正是舊註解「不准顯示 0/0」要守的那件事。\n // 判準:子樹合計 total > 0 就是量出來的;沒被 skip 的節點照舊一律顯示(0/0 是實話)。\n if (!n.skipped || s.total > 0) {\n var full = s.total > 0 && s.synced === s.total;\n label += '<span class=\"ft-sep\">·</span><span class=\"tt-count tt-docs\"' + (full ? ' style=\"color:var(--ink)\"' : '') + '>'\n + s.synced + ' / ' + s.total + '</span>';\n }\n // ── 差額的解釋搬進滑過去那一格(leo 2026-08-28:「一列只留文件 X/Y」)──────\n // 它還在(leo 的規格「差額要說得出來」沒有被推翻),只是不再佔滿每一列:\n // 一棵樹裡十列講同一句「N 份不在收檔範圍(程式碼等)」,正是本票要拿掉的東西。\n var why = gapWhy(s);\n var pills = [];\n if (!n.skipped || s.total > 0) pills.push('<span class=\"tt-pill\">📄 ' + s.synced + ' / ' + s.total + '</span>');\n if (s.unsupported > 0) pills.push('<span class=\"tt-pill\">⚠ 讀不了 ' + s.unsupported + '</span>');\n // skipped 的節點不再多一顆「⊘ 不收 N」:它與下面那顆「⊘ 這一層沒收」講同一件事,\n // 而分母已經寫在 📄 那顆上(實測截圖裡兩顆並排,看起來像系統在重複自己)。\n if (s.excluded > 0 && !n.skipped) pills.push('<span class=\"tt-pill\">⊘ 不收 ' + s.excluded + '</span>');\n if (s.pending > 0) pills.push('<span class=\"tt-pill\">⏳ 處理中 ' + s.pending + '</span>');\n if (n.skipped) pills.push('<span class=\"tt-pill is-type\">⊘ 這一層沒收</span>');\n var pop = '<div class=\"pop-t\">📁 ' + esc(n.name || '(未命名)') + '</div>'\n + (pills.length ? '<div class=\"pop-n\">' + pills.join('') + '</div>' : '')\n + (n.skipped ? '<div class=\"pop-why\">' + esc(n.skip_reason || '(小幫手沒說明理由)') + '</div>' : '')\n + (!n.skipped && why ? '<div class=\"pop-why\">' + esc(why) + '</div>' : '');\n // ⊘ 自己就是「滑過去有東西」的把手(虛線底、cursor:help),旁邊再掛一個 ⓘ\n // 等於同一件事說兩次 ⇒ 只有沒有 ⊘ 的列才需要那個提示。\n if (why && !n.skipped) label += '<span class=\"ft-sep\">·</span><span class=\"ft-hint\">ⓘ</span>';\n var kids = (r.kids[n.path] || []).slice().sort(function (a, b) { return a.path < b.path ? -1 : 1; }).map(build);\n return { content: label, payload: { pop: pop }, children: kids };\n }\n var roots = (r.kids['-'] || []).map(build);\n // 父親不在清單裡的孤兒(樹被截斷時會有)——照樣列出來,缺角要看得見,不要偷偷藏起來\n var seen = {};\n nodes.forEach(function (n) { seen[n.path] = true; });\n nodes.forEach(function (n) { if (n.parent !== '-' && !seen[n.parent]) roots.push(build(n)); });\n return roots;\n }\n\n // 某個庫那一列底下的「小孩」容器(=第四層要填進去的地方)。\n // 只認**直接**的 .ft-kids,不然會抓到孫子那層。\n function libKidsBox(name) {\n var d = $('ad-libs') && $('ad-libs').querySelector('details[data-libtree=\"' + cssQuote(name) + '\"]');\n if (!d) return null;\n for (var i = 0; i < d.children.length; i++) {\n if (d.children[i].className === 'ft-kids') return d.children[i];\n }\n return null;\n }\n function cssQuote(s) { return String(s).replace(/[\"\\\\]/g, '\\\\$&'); }\n\n // 把第四層填進去。🔴 只換那一層,不重畫整棵樹——重畫會把使用者已經展開/收合的\n // 其他支全部彈回預設(而且會讓剛點開的那一下看起來像沒反應)。\n function fillLibKids(name, html) {\n var box = libKidsBox(name);\n if (box) box.innerHTML = html;\n }\n function mountFolderTreeKids(name) {\n var tree = folderTrees[name];\n var foot = '';\n if (tree && tree.truncated) {\n foot = '<div class=\"ft-foot\">資料夾太多,只顯示前 ' + (tree.nodes || []).length\n + ' 個(實際有 ' + tree.total_nodes + ' 個)。</div>';\n }\n fillLibKids(name, treeKidsHtml(folderKidsData(name)) + foot);\n }\n\n // 點開某個庫才去抓它的資料夾結構(列表端點只帶摘要,整棵樹一個庫可能上百個節點)。\n // 掛在 `toggle` 事件上——它**不冒泡**,所以要用捕獲階段。\n $('ad-libs').addEventListener('toggle', function (ev) {\n var d = ev.target;\n if (!d || !d.hasAttribute || !d.hasAttribute('data-libtree')) return;\n var name = d.getAttribute('data-libtree') || '';\n treeOpen[name] = d.open;\n if (!d.open || folderTrees[name] !== undefined || folderTreeLoading[name]) return;\n folderTreeLoading[name] = true;\n adminApi('GET', '/portal/admin/folder-tree?library=' + encodeURIComponent(name))\n .then(function (x) {\n folderTreeLoading[name] = false;\n if (guard401(x.status)) return;\n if (!x.ok) {\n fillLibKids(name, '<div class=\"ft-row ft-leaf\"><span class=\"ft-tw\"></span><span class=\"ft-lbl tt-unknown\">'\n + esc(x.d.error || ('讀取失敗(HTTP ' + x.status + '')) + '</span></div>');\n return;\n }\n folderTrees[name] = x.d.tree || null;\n mountFolderTreeKids(name);\n })\n .catch(function (e) {\n folderTreeLoading[name] = false;\n fillLibKids(name, '<div class=\"ft-row ft-leaf\"><span class=\"ft-tw\"></span><span class=\"ft-lbl tt-unknown\">'\n + esc(friendlyErr(e)) + '</span></div>');\n });\n }, true);\n\n // 一次性密碼顯示(server 不留明碼——關掉就真的沒了,誠實跟 admin 說清楚)\n function showOtp(email, password) {\n $('ad-otp').innerHTML = '<div class=\"otpbox\">' +\n '<div style=\"font-size:15px;font-weight:600\">' + esc(email) + ' 的一次性密碼</div>' +\n '<div class=\"pw\">' + esc(password) + '</div>' +\n '<div style=\"font-size:13.5px;line-height:1.7;color:rgba(var(--ink-rgb),.65)\">只顯示這一次——關閉後再也調不出來(系統只存雜湊)。請當場轉交同仁,並提醒登入後到「設定」改成自己的密碼。</div>' +\n '<div class=\"btnrow\"><button class=\"btn2\" id=\"ad-otp-close\">我已轉交,關閉</button></div></div>';\n $('ad-otp').scrollIntoView({ block: 'nearest' });\n }\n\n // 新增帳號\n $('ad-nu-create').addEventListener('click', function () {\n var email = $('ad-nu-email').value.trim();\n var name = $('ad-nu-name').value.trim();\n var role = $('ad-nu-role').value === 'admin' ? 'admin' : 'user';\n var st = $('ad-nu-status');\n if (!email) { st.textContent = '請填 Email'; return; }\n st.textContent = '';\n $('ad-nu-create').disabled = true;\n var body = { email: email, role: role };\n if (name) body.display_name = name;\n adminApi('POST', '/portal/admin/users', body)\n .then(function (x) {\n $('ad-nu-create').disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) { st.textContent = x.d.error || ('建立失敗(HTTP ' + x.status + ''); return; }\n $('ad-nu-email').value = ''; $('ad-nu-name').value = ''; $('ad-nu-role').value = 'user';\n if (x.d.generated_password) showOtp(email, x.d.generated_password);\n toast('帳號已建立');\n loadAdmin();\n })\n .catch(function (e) { $('ad-nu-create').disabled = false; st.textContent = friendlyErr(e); });\n });\n\n\n // 「全部知識庫」勾選 → 其餘庫勾選框連動停用(存的就是 [\"*\"],個別勾選無意義)\n document.addEventListener('change', function (ev) {\n var t = ev.target;\n if (!t || !t.hasAttribute || !t.hasAttribute('data-star')) return;\n var uid = t.getAttribute('data-uid');\n document.querySelectorAll('#libed-' + uid + ' input[data-lib]').forEach(function (cb) {\n cb.disabled = t.checked;\n if (t.checked) cb.checked = false;\n });\n });\n\n // 管理頁動作(事件委派)\n $('v-admin').addEventListener('click', function (ev) {\n var t = ev.target.closest('[data-act]');\n if (!t) return;\n // 操作鈕長在樹的 <summary> 裡(庫目錄的「移除」):點它的預設行為是把那一列摺疊起來。\n // 🔴 擋掉那個預設行為,否則按「移除」會順手把資料夾結構收起來——看起來像是按錯了。\n if (t.closest('summary')) ev.preventDefault();\n var act = t.getAttribute('data-act');\n var uid = t.getAttribute('data-uid');\n\n if (act === 'edit-libs') {\n var ed = $('libed-' + uid);\n ed.style.display = ed.style.display === 'none' ? '' : 'none';\n return;\n }\n if (act === 'save-libs') {\n var star = document.querySelector('#libed-' + uid + ' input[data-star]');\n var libs;\n if (star && star.checked) { libs = ['*']; }\n else {\n libs = [];\n document.querySelectorAll('#libed-' + uid + ' input[data-lib]').forEach(function (cb) {\n if (cb.checked) libs.push(cb.getAttribute('data-lib'));\n });\n if (!libs.length) { toast('至少勾一個庫,或勾「全部知識庫」'); return; }\n }\n t.disabled = true;\n adminApi('PATCH', '/portal/admin/users/' + encodeURIComponent(uid), { libraries: libs })\n .then(function (x) {\n t.disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) { toast(x.d.error || ('儲存失敗(HTTP ' + x.status + '')); return; }\n toast('庫權限已更新,立即生效');\n loadAdmin();\n })\n .catch(function (e) { t.disabled = false; toast(friendlyErr(e)); });\n return;\n }\n if (act === 'set-status') {\n var next = t.getAttribute('data-next');\n if (next === 'disabled' && !confirm('確定停用 ' + (t.getAttribute('data-email') || '這個帳號') + '?停用後其登入與既有 session 立即失效。')) return;\n t.disabled = true;\n adminApi('PATCH', '/portal/admin/users/' + encodeURIComponent(uid), { status: next })\n .then(function (x) {\n t.disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) { toast(x.d.error || ('更新失敗(HTTP ' + x.status + '')); return; } // 含最後一個 admin 的 409 訊息\n toast(next === 'disabled' ? '已停用' : '已啟用');\n loadAdmin();\n })\n .catch(function (e) { t.disabled = false; toast(friendlyErr(e)); });\n return;\n }\n if (act === 'reset-pw') {\n var email = t.getAttribute('data-email') || '';\n if (!confirm('確定重設 ' + email + ' 的密碼?舊密碼將立即失效,會產生一組新的一次性密碼。')) return;\n t.disabled = true;\n adminApi('POST', '/portal/admin/users/' + encodeURIComponent(uid) + '/reset-password')\n .then(function (x) {\n t.disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) { toast(x.d.error || ('重設失敗(HTTP ' + x.status + '')); return; }\n showOtp(email, x.d.password);\n toast('密碼已重設');\n })\n .catch(function (e) { t.disabled = false; toast(friendlyErr(e)); });\n return;\n }\n // 展開/收起資料夾結構已不走按鈕——那一列就是 <summary>,點它本身就會展開\n // (Arcrun#144:三個入口共用同一份 render,摺疊交給瀏覽器原生的 <details>)。\n // 移除已登記庫(有 record_id\n if (act === 'lib-remove') {\n var libId = t.getAttribute('data-id') || '';\n var libName = t.getAttribute('data-name') || libId;\n if (!confirm('把「' + libName + '」從目錄移除?\\n\\n資料不會刪除,重新同步時會再出現。')) return;\n t.disabled = true;\n adminApi('DELETE', '/portal/admin/libraries/' + encodeURIComponent(libId))\n .then(function (x) {\n t.disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) { toast(x.d.error || ('移除失敗(HTTP ' + x.status + '')); return; }\n adminLibs = adminLibs.filter(function (l) { return l.record_id !== libId; });\n renderAdminLibs();\n toast('已從目錄移除');\n })\n .catch(function (e) { t.disabled = false; toast(friendlyErr(e)); });\n return;\n }\n // 移除 auto 庫(無 record_identries 標 deprecated\n if (act === 'lib-remove-auto') {\n var autoName = t.getAttribute('data-name') || '';\n var input = window.prompt('移除「' + autoName + '」會讓它的內容不再被搜尋到(資料保留可還原)。\\n\\n請輸入庫名確認:');\n if (input === null) return; // 取消\n if (input.trim() !== autoName) { toast('庫名輸入不符,取消移除'); return; }\n t.disabled = true;\n adminApi('DELETE', '/portal/admin/libraries/by-name/' + encodeURIComponent(autoName), { confirm: autoName })\n .then(function (x) {\n t.disabled = false;\n if (guard401(x.status)) return;\n if (!x.ok) { toast(x.d.error || ('移除失敗(HTTP ' + x.status + '')); return; }\n adminLibs = adminLibs.filter(function (l) { return l.name !== autoName; });\n renderAdminLibs();\n toast('已移除(' + (x.d.deprecated_count || 0) + ' 筆標為不可搜)');\n })\n .catch(function (e) { t.disabled = false; toast(friendlyErr(e)); });\n return;\n }\n });\n // 一次性密碼關閉鈕(otpbox 動態生成,掛在容器上)\n $('ad-otp').addEventListener('click', function (ev) {\n if (ev.target && ev.target.id === 'ad-otp-close') $('ad-otp').innerHTML = '';\n });\n\n // ── 版本檢查(2026-08-02)──────────────────────────────────────────────\n // leo 的兩個要求:\n // ②「不顯示的話用戶不知道要不要更新」→ 一定要有目前版本\n // ③「白癡化,發現落後就按一下開啟 install 直接帶它的 email 和辨識碼」\n // → 落後才出現按鈕,且自動帶 email;辨識碼不必帶(安裝器 t154 對既有實例免碼)\n //\n // 比法:自己的版本(cypher /health 的 bundle_versionvs 最新版(安裝器 /api/latest)。\n // 兩邊都是 semver(例 1.4.2),用數字逐段比,不用字串比('1.4.10' < '1.4.9' 會出錯)。\n var INSTALLER_ORIGIN = 'https://install.arcrun.dev';\n\n // Arcrun#106:版號後面可以帶 build metadata`1.4.41+d61`、`1.4.41+a1b2c3d`)——\n // 那是 semver 規格裡「比大小時要忽略」的那一段。舊寫法拿整串去比對正規式,\n // 一律落到「較舊版本」(youlin 實例就是這樣,明明有版號卻顯示不出來)。\n // 這裡只取前面的 `x.y.z` 當比較用的核心,顯示仍顯示完整原字串。\n function semverCore(v) {\n var m = String(v || '').match(/^(\\d+\\.\\d+\\.\\d+)/);\n return m ? m[1] : '';\n }\n\n function cmpSemver(a, b) {\n var x = String(a || '').split('.').map(Number);\n var y = String(b || '').split('.').map(Number);\n for (var i = 0; i < 3; i++) {\n var d = (x[i] || 0) - (y[i] || 0);\n if (d) return d < 0 ? -1 : 1;\n }\n return 0;\n }\n\n function loadVersion() {\n var line = $('st-ver-line');\n var dot = $('st-ver-dot');\n var btn = $('st-ver-update');\n if (!line) return;\n\n // #106:順便把 bundle_commit 帶回來(有注入才有)——版號是頻道編號,commit 才是「真的部了哪份碼」。\n var mineCommit = '';\n var mineP = fetch(window.ARCRUN_API_BASE + '/health', { cache: 'no-store' })\n .then(function (r) { return r.ok ? r.json() : null; })\n .then(function (j) {\n mineCommit = (j && j.bundle_commit) || '';\n return (j && j.bundle_version) || '';\n })\n .catch(function () { return ''; });\n var latestP = fetch(INSTALLER_ORIGIN + '/api/latest')\n .then(function (r) { return r.ok ? r.json() : null; })\n .then(function (j) { return (j && j.release) || ''; })\n .catch(function () { return ''; });\n\n Promise.all([mineP, latestP]).then(function (res) {\n var mine = res[0], latest = res[1];\n\n if (!mine) { line.textContent = '無法讀取目前版本(知識庫服務可能正在啟動)'; return; }\n // 舊實例的 bundle_version 是舊格式(2026-07-31+8e83589),比不了 semver。\n // 這種情況一律當成「落後」——因為新版才會寫 semver 進來。\n // #106`1.4.41+<commit>` 這種帶 build metadata 的**是** semver,取核心比即可。\n var mineCore = semverCore(mine);\n var mineIsSemver = !!mineCore;\n // commit 是輔助資訊(有才顯示):版號說「哪一版」,commit 說「真的是哪份碼」。\n var commitNote = mineCommit ? ' <span class=\"muted\">commit ' + esc(String(mineCommit).slice(0, 7)) + '</span>' : '';\n\n if (!latest) {\n line.innerHTML = '目前版本 <strong>' + esc(mine) + '</strong>(暫時查不到最新版,稍後再試)' + commitNote;\n return;\n }\n var behind = !mineIsSemver || cmpSemver(mineCore, latest) < 0;\n if (!behind) {\n line.innerHTML = '目前版本 <strong>' + esc(mine) + '</strong> 已是最新版' + commitNote;\n dot.style.display = 'none';\n btn.style.display = 'none';\n return;\n }\n line.innerHTML = '目前版本 <strong>' + esc(mineIsSemver ? mine : '較舊版本') + '</strong>'\n + ' → 最新版 <strong>' + esc(latest) + '</strong><br>'\n + '<span style=\"color:#C2492E\">有新版可以更新。按「立即更新」會開啟安裝頁,用你原本的 Cloudflare 帳號重新安裝一次即可,知識庫內容不會動到。</span>';\n dot.style.display = 'inline-block';\n btn.style.display = '';\n btn.onclick = function () {\n // 帶 email 讓安裝頁預填;既有實例更新免辨識碼(安裝器 t154)\n var email = (S.profile && S.profile.email) || '';\n var url = INSTALLER_ORIGIN + '/' + (email ? '?email=' + encodeURIComponent(email) : '');\n window.open(url, '_blank', 'noopener');\n };\n });\n }\n\n // ── 設定 ──\n function loadSettings() {\n var p = S.profile;\n if (!p) { $('st-me').innerHTML = '<div class=\"muted\">載入中…</div>'; return; }\n if (window._loadAiConfig) window._loadAiConfig();\n loadVersion();\n var libs = p.libraries || [];\n var libHtml = libs.indexOf('*') >= 0\n ? '<span class=\"tag green\">全部知識庫</span>'\n : (libs.length ? libs.map(function (l) { return '<span class=\"tag green\">' + esc(l) + '</span>'; }).join(' ') : '<span class=\"tag dim\">尚未授權任何庫</span>');\n $('st-me').innerHTML =\n '<div class=\"kvline\"><span class=\"muted\">名稱</span><span>' + esc(p.display_name || '') + '</span></div>' +\n '<div class=\"kvline\"><span class=\"muted\">角色</span><span>' + esc(p.role === 'admin' ? '管理員' : '一般用戶') + '</span></div>' +\n '<div class=\"kvline\" style=\"align-items:flex-start\"><span class=\"muted\" style=\"flex:none\">可查庫</span><span style=\"text-align:right;display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end\">' + libHtml + '</span></div>';\n }\n // ══════════ D62:改密碼與忘記密碼=同一個機制 ══════════\n //\n // leo 2026-08-10:「這兩個機制其實是一個機制,可以簡化。」\n // 修改密碼:輸入**現有的** → 輸入新的 → 覆蓋\n // 忘記密碼:收到**「修改密碼」連結** → **不輸入現有密碼(忽略)** → 輸入新的 → 覆蓋\n // ⇒ 底下只有**一個表單元素**(#pw-form)與**一支送出函式**,差別只有「現有密碼」那一格\n // 顯不顯示、以及帶 current 還是帶 reset_token。兩份長得像的東西必然漂移\n // (arcrun-rag#40「同一個事實兩份」)。\n // 🔴 不做一次性密碼(leo:「不要發一次性密碼太麻煩」)。\n\n var S_RESET_TOKEN = ''; // 非空=現在是「忘記密碼」模式\n\n /** 把那一份唯一的表單搬到指定容器,並決定「現有密碼」那一格出不出現。 */\n function mountPasswordForm(host, needCurrent) {\n var form = $('pw-form');\n if (form && form.parentNode !== host) host.appendChild(form);\n $('st-pw-old').classList.toggle('hide', !needCurrent);\n $('st-pw-save').textContent = needCurrent ? '更新密碼' : '設定新密碼';\n $('st-pw-old').value = ''; $('st-pw-new').value = ''; $('st-pw-new2').value = '';\n $('st-pw-status').textContent = '';\n }\n\n /** 送出:兩種模式共用。帶 reset_token 就走連結那一格,否則走現有密碼那一格。 */\n function submitPasswordChange() {\n var needCurrent = !S_RESET_TOKEN;\n var oldPw = $('st-pw-old').value;\n var newPw = $('st-pw-new').value;\n var newPw2 = $('st-pw-new2').value;\n var st = $('st-pw-status');\n st.className = 'err';\n if (needCurrent && !oldPw) { st.textContent = '請填現在的密碼'; return; }\n if (!newPw) { st.textContent = '請填新密碼'; return; }\n if (newPw.length < 8) { st.textContent = '新密碼至少 8 碼'; return; }\n if (newPw !== newPw2) { st.textContent = '兩次輸入的新密碼不一致'; return; }\n var body = { 'new': newPw };\n if (S_RESET_TOKEN) body.reset_token = S_RESET_TOKEN; else body.current = oldPw;\n $('st-pw-save').disabled = true;\n st.textContent = '處理中…';\n fetch(API_BASE + '/portal/password/change', {\n method: 'POST',\n headers: Object.assign({ 'Content-Type': 'application/json' }, needCurrent ? authHeaders() : {}),\n body: JSON.stringify(body)\n })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n $('st-pw-save').disabled = false;\n // 忘記密碼模式沒有 session,401 不代表「你被登出」——別誤踢\n if (needCurrent && guard401(x.status)) return;\n if (!x.ok) { st.textContent = (x.d && x.d.error) || '更新失敗'; return; }\n st.className = 'ok';\n $('st-pw-old').value = ''; $('st-pw-new').value = ''; $('st-pw-new2').value = '';\n if (S_RESET_TOKEN) {\n st.textContent = '密碼已設定好了,正在帶你回登入頁…';\n var who = (x.d && x.d.email) || '';\n S_RESET_TOKEN = '';\n setTimeout(function () {\n history.replaceState(null, '', location.pathname);\n location.hash = '';\n $('v-reset').classList.remove('on');\n mountPasswordForm($('st-pw-panel'), true);\n showAuth();\n if (who) $('login-email').value = who;\n $('login-status').className = 'ok';\n $('login-status').textContent = '新密碼已生效,請用它登入。';\n }, 1200);\n } else {\n st.textContent = '密碼已更新(你不會被登出,可以繼續操作)';\n toast('密碼已更新');\n }\n })\n .catch(function (e) { $('st-pw-save').disabled = false; st.textContent = friendlyErr(e); });\n }\n $('st-pw-save').addEventListener('click', submitPasswordChange);\n\n // ── 登入頁的「忘記密碼」入口(**在 portal,不是 console**leo 2026-08-10)──\n $('login-forgot').addEventListener('click', function (ev) {\n ev.preventDefault();\n $('forgot-box').classList.remove('hide');\n $('forgot-email').value = $('login-email').value.trim();\n $('forgot-email').focus();\n });\n $('forgot-submit').addEventListener('click', function () {\n var email = $('forgot-email').value.trim();\n var st = $('forgot-status');\n st.className = 'err';\n if (!email) { st.textContent = '請填 Email'; return; }\n $('forgot-submit').disabled = true;\n st.textContent = '寄送中…';\n fetch(API_BASE + '/portal/password/forgot', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: email })\n })\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })\n .then(function (x) {\n $('forgot-submit').disabled = false;\n if (!x.ok) { st.textContent = (x.d && x.d.error) || '寄送失敗'; return; }\n st.className = 'ok';\n st.textContent = (x.d && x.d.message) || '如果這個 email 有帳號,連結已經寄出了。';\n })\n .catch(function (e) { $('forgot-submit').disabled = false; st.textContent = friendlyErr(e); });\n });\n\n /** 點信裡的連結進來:`…/portal/#/reset?token=…`cypher 的 /portal/password/reset-link 導過來的)。 */\n function resetTokenFromUrl() {\n var m = String(location.hash || '').match(/[?&]token=([^&]+)/);\n if (m) return decodeURIComponent(m[1]);\n m = String(location.search || '').match(/[?&]token=([^&]+)/);\n return m ? decodeURIComponent(m[1]) : '';\n }\n function showReset(token) {\n $('v-login').classList.remove('on');\n $('v-firstsetup').classList.remove('on');\n $('shell').classList.remove('on');\n $('tabbar').classList.remove('on');\n $('v-reset').classList.add('on');\n $('reset-error').textContent = '';\n // 先問一次「這張票還有效嗎」——連結是一次有效、30 分鐘過期的,\n // 過期就要當場說清楚,不要讓人填完密碼才被打回票(#49:別把系統狀態講成用戶的錯)。\n fetch(API_BASE + '/portal/password/reset?token=' + encodeURIComponent(token))\n .then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, d: d }; }); })\n .then(function (x) {\n if (!x.ok || !x.d || x.d.valid !== true) {\n $('reset-who').textContent = '';\n $('reset-error').textContent = (x.d && x.d.error) || '這條連結已經失效了。請回登入頁重新按一次「忘記密碼」。';\n return;\n }\n S_RESET_TOKEN = token;\n $('reset-who').innerHTML = '你正在為 <strong>' + esc(x.d.email) + '</strong> 設定新密碼。<br>這條連結只能用一次,<strong>不需要輸入現在的密碼</strong>。';\n mountPasswordForm($('reset-slot'), false); // ← 差別只有這一格\n })\n .catch(function (e) { $('reset-error').textContent = friendlyErr(e); });\n }\n $('reset-back').addEventListener('click', function (ev) {\n ev.preventDefault();\n S_RESET_TOKEN = '';\n history.replaceState(null, '', location.pathname);\n location.hash = '';\n $('v-reset').classList.remove('on');\n mountPasswordForm($('st-pw-panel'), true);\n showAuth();\n });\n\n boot();\n})();\n</script>\n</body>\n</html>"},"/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-05Mac 一律給 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// 只測**樹的資料**,不測它怎麼被畫出來(那在 tree-render.test.mjs),\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', 'normalizeTreeNode', 'visibleRows'].map(grab).join('\\n');\nconst fn = new Function(src + '\\nvar SEARCH_ROUTE_LABEL = { graph: \"圖譜命中子庫\", index: \"索引退回選庫\", all: \"全庫讀取\" };'\n + '\\nreturn { srcCrumbs, machineLabelMap, retrievalTreeData, normalizeTreeNode, visibleRows };')();\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// 🔴 inkstone/Arcrun#1702026-08-27)改掉這裡好幾條斷言的期望值——\n// 不是測試鬆綁,是行為本身照票的要求換了:\n// 「子庫」「三元組」「索引沒有敘事」是內部詞,low-code 使用者看不懂\n// (leo 原話:「這是在跟誰溝通?」),全部換成使用者看得懂的說法;\n// 而「考慮過但沒讀原文」的收合桶本身也被拿掉——leo:「這張圖的目的是\n// 告訴他相關文件放哪裡……只該在真的有命中的時候出現,而且只畫命中的\n// 那條路徑」,收合桶再怎麼收合,都還是陪葬在畫面上的無關資訊。\n// (總管 2026-08-27 複驗指令:逐條判斷哪些是刻意改掉、哪些是改壞了,\n// 刻意改的要在這裡寫明原因,不准整支刪掉或跳過。)\n\n// ① 層級=leo 畫的那張:搜尋詞 → 命中資料夾 → 資料夾摘要 → 原文片段\nconst tree = fn.retrievalTreeData('Arcrun124驗證卡是什麼?', REAL);\nt('第 1 層是搜尋詞', txt(tree).indexOf('Arcrun124驗證卡是什麼?') === 0, txt(tree));\nconst lib = tree.children[0];\n// 「命中實體」→「找到關鍵字」(#170:使用者看得懂的字,意思不變)。\n// 🔴 同一次順便拿掉了原始相似度分數(`score 1.69`)與內部路由詞(`via: graph`)——\n// 兩個都是「對使用者沒有意義的數字/內部詞」,不服務「相關文件放哪裡」這個目的,\n// 分數沒有刻度、使用者不知道 1.69 是好是壞;拿掉比留著噪音更誠實。\nt('第 2 層是命中資料夾(帶找到的關鍵字,不帶原始分數/內部路由詞)',\n /testkb/.test(txt(lib)) && /找到關鍵字/.test(txt(lib)) && !/1\\.69/.test(txt(lib)) && !/\\bgraph\\b/.test(txt(lib)), txt(lib));\nconst idx = lib.children[0];\n// 「三元組」整個拿掉(使用者看不懂、對他沒意義),「筆條目」→「筆內容」(跟 assemble 的\n// prompt 用語一致,見 workflows/rag-chat.local.yaml 的 catalog 組字)\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 是空字串時不假裝有敘事——但也不能講「索引沒有敘事」這種內部詞\n// (使用者看不懂「索引」「敘事」是什麼),改把這個資料夾主要談什麼(top_entities)\n// 擺出來,讓這一層仍然說得出讀到了什麼(REAL 的 testkb 剛好兩者都是這個情況:\n// narrative 空、top_entities 有)\nt('narrative 空時用「主要談」代替,不講內部詞「索引沒有敘事」', /主要談:Arcrun124驗證卡/.test(txt(idx)) && !/索引沒有敘事/.test(txt(idx)), txt(idx));\n// narrative 與 top_entities 都沒有時,這一層真的沒有東西可說——整行不顯示,\n// 不留一個「(索引沒有敘事)」式的空殼占位字(general 那筆兩者皆空,但 general\n// 沒被選用,不會出現在樹上;這裡直接測 idxNode 沒被匯出,改用等價的資料夾建構驗證)\nconst bothEmpty = fn.retrievalTreeData('問句', {\n route: 'graph',\n libraries: [{ library: 'blanklib', score: 1, via: 'graph', matched_entities: [] }],\n indexes_read: [{ library: 'blanklib', narrative: '', selected: true, top_entities: [], entry_count: 3 }],\n pages_read: [{ page: 'Q', library: 'blanklib', chars: 5, source: 'kb://q.md#0' }]\n});\nconst blankIdx = bothEmpty.children[0].children[0];\nt('narrative 與主要談都沒有時,那一行不留內部詞占位字', !/主要談|索引沒有敘事/.test(txt(blankIdx)) && /筆內容/.test(txt(blankIdx)), txt(blankIdx));\n\n// ③ 🔴 拿掉「沒被選用的資料夾收在收合節點」這整條行為(#170):\n// leo 的判準是「只畫命中的那條路徑」,收合起來還是陪葬在畫面上——\n// 使用者要的是「相關文件放哪裡」,不是「這次系統瞄過哪些資料夾」。\n// 新判準:沒被選用、沒讀到原文的資料夾**完全不出現**在樹上。\nt('沒被選用的資料夾完全不出現(不生收合桶,也不用任何形式露出)', !/general|(?<!test)kb(?!db)/.test(txt(tree)), txt(tree));\nt('主線只有命中的資料夾,沒有陪葬的其他分支', tree.children.length === 1, `實得 ${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// ⑤ 完全沒資料時不要生一棵空樹,也不准回 null(2026-08-27 總管複驗實測抓到:\n// 回 null 會讓 renderTree 對 null.children 解構炸掉——TypeError,這是本輪修的\n// 另一個病;防禦性 fallback 一律回一個合法節點,不是把「沒東西可畫」變成\n// 一個特殊值讓呼叫端自己判斷)\nconst empty = fn.retrievalTreeData('問句', { route: 'all', libraries: [], indexes_read: [], pages_read: [] });\nt('沒有任何資料夾時說明原因,且回傳合法節點(不是 null', empty && /資料夾作答|沒有回報/.test(txt(empty.children[0])), empty ? txt(empty.children[0]) : 'got null');\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// ⑦ 節點內容是**單行**Arcrun#144 判準第 3 條:字級行距接近作業系統檔案總管)。\n// 2026-08-19 之前每個節點塞 23 行(<br> 換行),那正是 leo 說的「上下間距很大」。\n// 一列放不下的部分由 CSS 截斷、全文掛 title,所以資料這端不准再自己換行。\nconst everyContent = [tree, lib, idx, page].map(n => String(n.content)).join('');\nt('節點內容不含換行標籤(一個節點=一列)', !/<br\\s*\\/?>/i.test(everyContent),\n (everyContent.match(/<br[^>]*>/gi) || []).join(' '));\nt('同一列裡的多段資訊用分隔點接起來', /<span class=\"ft-sep\">/.test(String(page.content)), String(page.content));\n\n// ⑧ 判準第 2 條的算式:列數 × 一列 23px 要塞得進 1080p(收合的那支只算它自己一列)\n// #170 拿掉了「考慮過但沒讀原文」那個收合桶,這棵樹裡不再有任何 fold=1 的節點——\n// 但 visibleRows 的收合行為是共用機制(同一份 renderTree 服務三個入口,見檔頭註解),\n// 用一個手刻的 fixture 驗證它沒有被連帶改壞,不依賴這棵樹恰好長出一個折疊節點。\nconst foldedFixture = { content: 'x', payload: { fold: 1 },\n children: [{ content: 'a', children: [] }, { content: 'b', children: [] }] };\nt('收合的節點只算一列(就算底下藏著子節點)', fn.visibleRows(foldedFixture) === 1, String(fn.visibleRows(foldedFixture)));\n// 新樹形=根(1) → 資料夾(1) → 摘要(1) → 原文片段(1),沒有陪葬分支,共 4 列\n// (改前是 5:多算一列給已拿掉的「考慮過但沒讀原文」收合桶)\nt('整棵樹的列數=展開狀態下看得到幾列', fn.visibleRows(fn.normalizeTreeNode(tree)) === 4,\n String(fn.visibleRows(fn.normalizeTreeNode(tree))));\nt('單一節點也算一列(不會回 0', fn.visibleRows({ content: 'x', children: [] }) === 1);\n\n// ⑨ 機器那一層(inkstone/mira#6 補上的兩格:machine=比對鍵、machine_label=顯示名)\nconst withM = fn.srcCrumbs('kb://RFP/daemon-真機驗收.md#0', 'mira6-watch',\n { machine: 'youlinhsieh@Leo-MBA', machine_label: '教育部 Leo 的 Mac' });\nt('有機器就拿顯示名', withM.machineLabel === '教育部 Leo 的 Mac', String(withM.machineLabel));\nt('比對鍵原樣留著(樹上分支要用它當 key', withM.machine === 'youlinhsieh@Leo-MBA', String(withM.machine));\nt('機器名沒有混進路徑', withM.path === 'RFP/daemon-真機驗收.md' && withM.dirs.join('/') === 'RFP', withM.path);\n\nconst onlyKey = fn.srcCrumbs('kb://a/b.md#0', 'lib', { machine: 'u@Host' });\nt('只有比對鍵沒有顯示名 → 顯示名退回比對鍵', onlyKey.machineLabel === 'u@Host', String(onlyKey.machineLabel));\n\nconst noM = fn.srcCrumbs('kb://a/b.md#0', 'lib', {});\nt('舊資料沒有這兩格 → machine 是 null(畫面顯示未知來源)', noM.machine === null && noM.machineLabel === null, JSON.stringify(noM.machine));\nconst emptyM = fn.srcCrumbs('kb://a/b.md#0', 'lib', { machine: '', machine_label: '' });\nt('空字串一律當沒有,不顯示成空白機器', emptyM.machine === null && emptyM.machineLabel === null, JSON.stringify(emptyM));\n\n// 同一個相對路徑來自兩台機器 → 樹上要是兩支,不可以被併成一支\nconst twoMachines = fn.retrievalTreeData('問句', { route: 'graph',\n libraries: [{ library: 'mira6-watch', score: 2.1, via: 'graph', matched_entities: ['x'] }],\n indexes_read: [{ library: 'mira6-watch', narrative: '', selected: true, triplet_count: 1, entry_count: 2 }],\n pages_read: [\n { page: 'P', library: 'mira6-watch', chars: 10, source: 'kb://RFP/same.md#0', machine: 'u@Leo-MBA', machine_label: 'Leo 的 Mac' },\n { page: 'P', library: 'mira6-watch', chars: 10, source: 'kb://RFP/same.md#0', machine: 'u@Leo-iMac', machine_label: 'Leo 的 iMac' } ] });\nconst pageNodes = twoMachines.children[0].children[0].children;\nt('同路徑不同機器=兩支,不被併掉', pageNodes.length === 2, String(pageNodes.length));\nt('兩支各自標出自己的機器',\n /Leo 的 Mac/.test(pageNodes[0].content) && /Leo 的 iMac/.test(pageNodes[1].content),\n txt(pageNodes[0]) + ' ' + txt(pageNodes[1]));\nconst noMachinePage = fn.retrievalTreeData('問句', { route: 'graph', libraries: [], indexes_read: [],\n pages_read: [{ page: 'P', library: 'kb', chars: 1, source: 'kb://a.md#0' }] });\nt('沒有機器的片段標「未知來源」,不留空', /未知來源/.test(noMachinePage.children[0].children[0].content));\n\n// ⑩ 同一台機器只能有一個顯示名(youlin stage 2026-08-18 實測到的真實情況:\n// 同一把 youlinhsieh@Leo-MBA,改名前那筆 label 就是 ID 本身,改名後是「教育部 Leo 的 Mac」)\nconst REALPAIR = [\n { page: '資料夾總覽:mira6-watch', library: 'mira6-watch', chars: 40, source: 'kb://x.md#0',\n machine: 'youlinhsieh@Leo-MBA', machine_label: 'youlinhsieh@Leo-MBA' },\n { page: 'daemon-真機驗收', library: 'mira6-watch', chars: 93, source: 'kb://RFP/daemon.md#0',\n machine: 'youlinhsieh@Leo-MBA', machine_label: '教育部 Leo 的 Mac' }\n];\nconst lb = fn.machineLabelMap(REALPAIR);\nt('有人取過名字就全篇用那個名字', lb['youlinhsieh@Leo-MBA'] === '教育部 Leo 的 Mac', JSON.stringify(lb));\nt('舊那筆也跟著顯示成新名字(同一台不會長成兩台)',\n fn.srcCrumbs('kb://x.md#0', 'mira6-watch', REALPAIR[0], lb).machineLabel === '教育部 Leo 的 Mac');\nt('比對鍵不受顯示名影響', fn.srcCrumbs('kb://x.md#0', 'mira6-watch', REALPAIR[0], lb).machine === 'youlinhsieh@Leo-MBA');\nt('誰都沒取過名字 → 退回顯示 ID', fn.machineLabelMap([{ machine: 'u@H', machine_label: 'u@H' }])['u@H'] === 'u@H');\nt('兩台不同機器不會被併成一個名字',\n Object.keys(fn.machineLabelMap([{ machine: 'a@X', machine_label: 'X 機' }, { machine: 'b@Y', machine_label: 'Y 機' }])).length === 2);\n\nconst treeReal = fn.retrievalTreeData('問句', { route: 'graph',\n libraries: [{ library: 'mira6-watch', score: 2.1, via: 'graph', matched_entities: ['x'] }],\n indexes_read: [{ library: 'mira6-watch', narrative: '', selected: true, triplet_count: 1, entry_count: 2 }],\n pages_read: REALPAIR });\nconst both = treeReal.children[0].children[0].children.map(n => 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<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(grab('safeJson') + '\\n' + grab('friendlyErr') + '\\nreturn {safeJson, friendlyErr};')();\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// ① safeJson:非 JSON 不可拋例外(同事撞到的 404 HTML 頁)\nconst html404 = '<!DOCTYPE html><html><body>404 Not Found</body></html>';\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-render.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// 樹的**畫法**測試(Arcrun#144leo 2026-08-19 打回 markmap 之後)。\n//\n// 取代舊的 tree-links.test.mjs——那支測的是「把 markmap 的貝茲曲線改寫成 L 型折線」,\n// 而那整段程式碼連同 markmap 一起被移除了(形狀錯的不是線,是佈局;見 index.html\n// renderTree() 的技術選型註解)。\n//\n// 這支守的是**我們自己寫的那一段**:把樹資料攤成巢狀 <details> 的字串拼接。\n// 摺疊行為本身是瀏覽器的,不測;縮排是 CSS 的,不測。\n// 跑法:node console-ui/public/portal/tree-render.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(\n ['esc', 'treeHtml', 'treeKidsHtml', 'treeNodeHtml', 'titleAttr', 'normalizeTreeNode', 'visibleRows',\n 'gapWhy', 'rollupTree', 'folderTreeData', 'libTypeLabel', 'adminLibsTreeData', 'libTreeNode',\n 'folderKidsData'].map(grab).join('\\n')\n // 同步庫總覽的第四層是「點開才抓」的,資料放在這兩張表裡(畫面端的快取)。\n // 測試自己當那份快取,就能把「還沒抓/抓過但沒有/抓到了」三種狀態都走一遍。\n + '\\nvar folderTrees = {}, treeOpen = {};'\n + '\\nreturn { treeHtml, treeKidsHtml, treeNodeHtml, titleAttr, normalizeTreeNode, visibleRows,'\n + ' adminLibsTreeData, folderTreeData,'\n + ' setFolderTree: function (n, t) { folderTrees[n] = t; },'\n + ' setOpen: function (n, v) { treeOpen[n] = v; } };')();\n\nlet pass = 0, fail = 0;\nconst t = (l, c, e = '') => { c ? (console.log('PASS:', l), pass++) : (console.log('FAIL:', l, e), fail++); };\n\nconst leaf = (c) => ({ content: c, children: [] });\nconst TREE = {\n content: '<b>問句</b>', children: [\n { content: '<b class=\"tt-folder\">kb</b>', children: [leaf('<b class=\"tt-file\">頁 A</b>'), leaf('<b class=\"tt-file\">頁 B</b>')] },\n { content: '<span class=\"tt-dim\">考慮過但沒讀原文</span>', payload: { fold: 1 }, children: [leaf('other')] }\n ]\n};\n\n// ① 有小孩=可摺疊的 <details>;沒小孩=一列,不生假的可展開節點\nconst out = fn.treeHtml(TREE);\nt('外層是 .ftree', out.startsWith('<div class=\"ftree\">'), out.slice(0, 40));\nt('有小孩的節點是 <details>', (out.match(/<details class=\"ft-n\"/g) || []).length === 3,\n String((out.match(/<details class=\"ft-n\"/g) || []).length));\nt('葉子不是 <details>,是一列 .ft-leaf', (out.match(/ft-row ft-leaf/g) || []).length === 3,\n String((out.match(/ft-row ft-leaf/g) || []).length));\nt('可摺疊的節點把標題放在 <summary>(點得到的就是這一列)', /<summary class=\"ft-row\">/.test(out));\n\n// ② 縮排靠巢狀,不靠算 padding ⇒ 深度沒有上限\nt('每一層小孩包在 .ft-kids 裡', (out.match(/<div class=\"ft-kids\">/g) || []).length === 3,\n String((out.match(/<div class=\"ft-kids\">/g) || []).length));\nlet deep = leaf('底'); for (let i = 0; i < 12; i++) deep = { content: 'L' + i, children: [deep] };\nt('12 層照樣攤得開(沒有寫死的深度上限)',\n (fn.treeHtml(deep).match(/<div class=\"ft-kids\">/g) || []).length === 12);\n\n// ③ payload.fold ⇒ 預設收起來(那一支不能被展開,也不能被吞掉)\nconst folded = out.slice(out.indexOf('考慮過') - 200, out.indexOf('考慮過'));\nt('fold 的那一支沒有 open 屬性', /<details class=\"ft-n\"><summary/.test(out), '');\nt('沒 fold 的節點預設展開', /<details class=\"ft-n\" open>/.test(out), '');\nt('收起來的那支內容仍在 DOM 裡(不隱瞞,只是收合)', /other/.test(out));\n\n// ④ 節點內容是 HTML(三個入口的異質節點靠這個分)——不可以被跳脫掉\nt('節點的 HTML 原樣留著', /<b class=\"tt-folder\">kb<\\/b>/.test(out) && /<b class=\"tt-file\">頁 A<\\/b>/.test(out));\n\n// ⑤ 一列放不下會截斷 ⇒ 全文必須掛在 title 上(=檔案總管對長檔名的做法)\nt('title 是剝掉標籤的純文字', fn.titleAttr('<b>甲</b><span class=\"ft-sep\">·</span>乙') === ' title=\"甲·乙\"',\n fn.titleAttr('<b>甲</b><span class=\"ft-sep\">·</span>乙'));\nt('title 裡的引號要跳脫,不能把屬性打斷', /&quot;|&#34;|&#39;/.test(fn.titleAttr('<b>say \"hi\"</b>')) || !/\"hi\"/.test(fn.titleAttr('<b>say \"hi\"</b>')),\n fn.titleAttr('<b>say \"hi\"</b>'));\nt('沒有文字就不掛空的 title', fn.titleAttr('<span></span>') === '');\n\n// ⑥ 多個根(資料夾樹會有)要全部畫出來,不是只畫第一個\nconst multi = fn.treeHtml([leaf('根一'), leaf('根二')]);\nt('陣列=多個根,全部畫出來', /根一/.test(multi) && /根二/.test(multi));\n\n// ⑦ 註腳(資料夾樹的「數字是已同步/總共」那段)掛得上去,且只在有內容時出現\nt('有註腳就畫在樹的後面', /<div class=\"ft-foot\">說明<\\/div><\\/div>$/.test(fn.treeHtml(leaf('x'), '說明')));\nt('沒註腳就不生空的區塊', !/ft-foot/.test(fn.treeHtml(leaf('x'))));\n\n// ⑧ 呼叫端漏寫 children 不會炸(葉子寫成 { content } 是最常見的手滑)\nt('缺 children 的節點補得回來', fn.normalizeTreeNode({ content: 'x' }).children.length === 0);\nt('缺 children 也畫得出來', /ft-leaf/.test(fn.treeHtml({ content: 'x' })));\n\n// ⑨ 🔴 判準第 2 條的算式:列數 × 23px 要塞得進 1080p。\n// 這支測的是「列數算得對」——收合的那支只算它自己一列。\nt('列數=展開狀態下看得到的列', fn.visibleRows(fn.normalizeTreeNode(TREE)) === 5,\n String(fn.visibleRows(fn.normalizeTreeNode(TREE)))); // 根 + kb + 2 頁 + 收合的那支\nconst wide = { content: 'r', children: [] };\nfor (let i = 0; i < 8; i++) wide.children.push({ content: 'lib' + i, children: [{ content: 'idx', children: Array.from({ length: 2 }, () => leaf('p')) }] });\nt('8 子庫 × (索引+2 片原文) 33 列,×23px ≈ 759px1080p 放得下',\n fn.visibleRows(fn.normalizeTreeNode(wide)) === 33 && fn.visibleRows(fn.normalizeTreeNode(wide)) * 23 < 900,\n String(fn.visibleRows(fn.normalizeTreeNode(wide))));\n\n// ⑩ 行尾那一格(庫目錄的「移除」鈕)——名字被截斷時它不能跟著消失,\n// 所以它必須在 .ft-lbl **外面**,而且不出現在 title 裡(title 是「這一列叫什麼」)。\nconst withTail = fn.treeHtml({ content: '很長的名字', tail: '<button data-act=\"x\">移除</button>', children: [] });\nt('tail 畫在 .ft-lbl 外面(不參與截斷)', /<\\/span><span class=\"ft-tail\"><button/.test(withTail), withTail);\nt('沒有 tail 就不生空的那一格', !/ft-tail/.test(fn.treeHtml(leaf('x'))));\nt('tail 的內容不會被塞進 title', !/移除/.test((withTail.match(/title=\"[^\"]*\"/) || [''])[0]), withTail);\nt('normalizeTreeNode 會把 tail 帶下去', fn.normalizeTreeNode({ content: 'a', tail: 'B' }).tail === 'B');\n\n// ⑪ payload.attrs=掛在這一列容器上的額外屬性(延後載入的標記靠它認人)\nt('attrs 掛在 <details> 上', /<details class=\"ft-n\" open data-libtree=\"kb\">/.test(\n fn.treeHtml({ content: 'a', payload: { attrs: 'data-libtree=\"kb\"' }, children: [leaf('b')] })));\nt('葉子也吃得到 attrs', /<div class=\"ft-row ft-leaf\" data-x=\"1\">/.test(\n fn.treeHtml({ content: 'a', payload: { attrs: 'data-x=\"1\"' }, children: [] })));\n\n// ⑫ treeKidsHtml=「只畫某一層」,給點開才抓的第四層用(不重畫整棵樹)\nconst kids = fn.treeKidsHtml([leaf('甲'), { content: '乙', children: [leaf('丙')] }]);\nt('treeKidsHtml 不包 .ftree 外殼(它是要塞進既有那一層的)', !/class=\"ftree\"/.test(kids), kids);\nt('treeKidsHtml 畫的是同一種列', /ft-row ft-leaf/.test(kids) && /<details class=\"ft-n\"/.test(kids));\n\n// ⑬ 🔴 判準第 1 條的第三個入口:同步庫總覽=四層,且機器那層沒資料時要說「未知」\nconst LIBS = [\n { name: 'kb', display_name: '公司知識庫', card_count: 12, triplet_count: 3,\n folder_tree: { root: '/Users/leo/KB', synced_files: 9, total_files: 11 } },\n { name: 'rfp', display_name: '客戶 RFP', auto: true, card_count: 0, triplet_count: 0,\n folder_tree: { root: '/Users/leo/RFP', synced_files: 4, total_files: 4 } },\n { name: 'old', display_name: '舊庫', status: 'disabled' } // 小幫手沒報過結構\n];\nconst ov = fn.adminLibsTreeData(LIBS);\nt('第一層是總庫', /<b>總庫<\\/b>/.test(ov.content), ov.content);\n// 🔴 inkstone/Arcrun#180leo 2026-08-28「GUI 是圖形指示,不限語文」):\n// 這一列以前是三句話,現在是三組圖示+數字,句子收進滑過去那一格。\n// **兩邊都要測**:只測列上會讓「把說明整個弄丟」也是綠的。\nt('總庫那列把三個庫與份數加總起來(圖示+數字,不是句子)',\n /🗂 3</.test(ov.content) && /tt-docs\">13 \\/ 15</.test(ov.content), ov.content);\nt('沒回報結構的庫要被數出來,不是靜靜消失', /❔ 1</.test(ov.content), ov.content);\nt('那三個數字各是什麼,滑過去講得出來(收起來≠刪掉)',\n /3 個資料夾/.test(ov.payload.pop) && /已同步/總共/.test(ov.payload.pop) && /還沒回報結構/.test(ov.payload.pop),\n ov.payload.pop);\nt('機器那一層的說明也收進滑過去那一格', /還沒回報是哪一台機器/.test(ov.children[0].payload.pop),\n ov.children[0].payload.pop);\nt('第二層是機器,而且現在沒有資料 ⇒ 明說未知,不拿路徑或帳號頂替',\n ov.children.length === 1 && /tt-machine tt-unknown\">未知來源/.test(ov.children[0].content), ov.children[0].content);\nconst libNodes = ov.children[0].children;\nt('第三層是三個庫', libNodes.length === 3);\nt('庫那一列掛得到「移除」鈕(在 tail,不會被名字擠掉)', /data-act=\"lib-remove\"/.test(libNodes[0].tail), libNodes[0].tail);\nt('auto 庫走的是另一支移除(沒有 record_id', /data-act=\"lib-remove-auto\"/.test(libNodes[1].tail), libNodes[1].tail);\n// 🔴 inkstone/Arcrun#180:異常留在列上,但縮成一個記號;全文在滑過去那一格。\n// 「停用」與「小幫手沒在同步」正是本票標題那半(一眼分得出還在同步/歷史殘留),\n// 所以不准整個搬進彈出視窗——搬進去就要逐列滑過才發現哪個庫是殘留的。\nt('停用的庫在列上看得出來(記號,不是句子)', /tt-unknown\">⏻</.test(libNodes[2].content), libNodes[2].content);\nt('停用的全文還在(收起來≠刪掉)', /已停用/.test(libNodes[2].payload.pop), libNodes[2].payload.pop);\nt('沒回報結構的庫不掛延後載入的標記(沒有東西可抓)',\n !(libNodes[2].payload && libNodes[2].payload.attrs), JSON.stringify(libNodes[2].payload));\nt('有結構的庫掛得到延後載入的標記', /data-libtree=\"kb\"/.test(libNodes[0].payload.attrs), libNodes[0].payload.attrs);\nt('庫預設收起來(不然一進頁面就展開幾百列)', libNodes[0].payload.fold === 1);\n\n// ⑭ 第四層=入口②的 folderTreeData,接在庫底下(不是另畫一棵)\nfn.setFolderTree('kb', { nodes: [\n { path: '', name: 'KB', parent: '-', depth: 0, total_files: 2, synced_files: 1, pending_files: 1, unsupported_files: 0, excluded_files: 0 },\n { path: 'docs', name: 'docs', parent: '', depth: 1, total_files: 3, synced_files: 1, pending_files: 0, unsupported_files: 2, excluded_files: 0 }\n] });\nfn.setOpen('kb', true);\nconst ov2 = fn.adminLibsTreeData(LIBS).children[0].children[0];\nt('點開過的庫,重畫時記得它是開的', ov2.payload.fold === 0, String(ov2.payload.fold));\n// 🔴 入口②的根節點就是第三層那個庫本身 ⇒ 要攤掉,不然連著兩列同名同數字\nt('第四層直接是子資料夾,不重複庫自己那一列',\n ov2.children.length === 1 && /tt-folder\">docs</.test(ov2.children[0].content), JSON.stringify(ov2.children[0] || {}).slice(0, 120));\nt('第四層仍帶著「為什麼沒收」那句(gapWhy 的契約沒有斷)',\n /格式還讀不了/.test(fn.treeHtml(ov2.children)), fn.treeHtml(ov2.children).slice(0, 300));\nt('整棵總覽是同一支 treeHtml 畫出來的(沒有第二套外殼)',\n fn.treeHtml(fn.adminLibsTreeData(LIBS)).startsWith('<div class=\"ftree\">'));\n\n// ⑮ 判準第 2 條:預設狀態(全部收起來)1080p 一定看得完\nfn.setOpen('kb', false); // 回到「還沒有人點開任何一支」的初始狀態\nt('總覽預設列數=總庫 1 + 機器 1 + 庫 N,不會一進來就爆版',\n fn.visibleRows(fn.normalizeTreeNode(fn.adminLibsTreeData(LIBS))) === 2 + LIBS.length,\n String(fn.visibleRows(fn.normalizeTreeNode(fn.adminLibsTreeData(LIBS)))));\n\n// ⑯ inkstone/Arcrun#180leo 2026-08-28):庫那一列只留「路徑 + 文件 X/Y」\n// 他原話:「只要符號加數字⋯⋯只要「文件」即可,其他的可以放在一個 moseover 彈出視窗裡」。\n// 他截圖裡每一列被判定語吃掉一半,而十列講的是同兩句話。\nconst PROJ = [{ name: 'pms', display_name: 'pms', auto: true, card_count: 16, triplet_count: 77,\n folder_tree: { root: '/Users/leo/pms', mode: 'docs-only', synced_files: 4, total_files: 36,\n reason: '這是一個開發專案(我看到 package.json、pnpm-workspace.yaml),這次只讀文件區' } }];\nconst projRow = fn.adminLibsTreeData(PROJ).children[0].children[0];\nt('庫那一列不再有判定語', !/開發專案/.test(projRow.content), projRow.content);\nt('庫那一列不再有知識卡/關聯的字', !/知識卡/.test(projRow.content) && !/條關聯/.test(projRow.content), projRow.content);\nt('庫那一列不再有根路徑', !/Users\\/leo/.test(projRow.content), projRow.content);\nt('庫那一列留著份數(圖示+數字,不寫「份文件」)',\n /tt-docs[^>]*>4 \\/ 36</.test(projRow.content) && !/份文件/.test(projRow.content), projRow.content);\nt('顯示名與庫鍵一樣時不重複寫兩遍', (projRow.content.match(/pms/g) || []).length === 1, projRow.content);\n// 滑過去那一格:四種數字都在,判定形式是膠囊\nt('滑過去看得到知識卡與關聯', /🗂 16</.test(projRow.payload.pop) && /🔗 77</.test(projRow.payload.pop), projRow.payload.pop);\nt('types 是膠囊,不是一整句話', /tt-pill is-type\">專案・讀文件</.test(projRow.payload.pop), projRow.payload.pop);\nt('完整判定語仍找得到(arcrun-rag#136 要靠它救回資料夾)', /pnpm-workspace/.test(projRow.payload.pop), projRow.payload.pop);\nt('根路徑仍找得到', /Users\\/leo\\/pms/.test(projRow.payload.pop), projRow.payload.pop);\n// 🔴 有 pop 的那一列不准再掛原生 title:兩個 tooltip 會同時冒出來\nt('有彈出視窗的列不掛原生 title(不然兩個 tooltip 打架)',\n !/ title=\"/.test(fn.treeHtml({ content: 'x', payload: { pop: '<b>y</b>' }, children: [] })),\n fn.treeHtml({ content: 'x', payload: { pop: '<b>y</b>' }, children: [] }));\n\n// ⑰ inkstone/Arcrun#180:被跳過的資料夾=一個記號,不是一句重複 8 次的話\n// leo 看到的原況:同一句「整個資料夾未收·這是安裝範本鋪下來的空白樣板,你還沒在上面\n// 寫過東西」在一棵樹裡出現 8 次;而 `scripts` 底下**真的有 1 個檔**,顯示 0/0 是說謊。\nconst SKIPTREE = { nodes: [\n { path: '', name: 'root', parent: '-', depth: 0, total_files: 2, synced_files: 2, pending_files: 0, unsupported_files: 0, excluded_files: 0 },\n // ① 走進去了、數字量得到、只是這一層不收\n { path: 'scripts', name: 'scripts', parent: '', depth: 1, total_files: 1, synced_files: 0, pending_files: 0, unsupported_files: 0, excluded_files: 1, skipped: true, skip_reason: '這是安裝範本時鋪下來的空白樣板' },\n { path: 'docs', name: 'docs', parent: '', depth: 1, total_files: 3, synced_files: 0, pending_files: 0, unsupported_files: 0, excluded_files: 3, skipped: true, skip_reason: '這是安裝範本時鋪下來的空白樣板' },\n // ② 真的沒走進去:數字是 0 而且不是事實\n { path: 'node_modules', name: 'node_modules', parent: '', depth: 1, total_files: 0, synced_files: 0, pending_files: 0, unsupported_files: 0, excluded_files: 0, skipped: true, skip_reason: '這是工具產生的' }\n] };\nconst sk = fn.folderTreeData(SKIPTREE)[0].children;\nconst byName = (n) => sk.find((k) => new RegExp('tt-folder\">' + n + '<').test(k.content));\nconst rowsHtml = sk.map((k) => k.content).join('');\nt('被跳過的資料夾在列上只剩一個記號', /tt-skip\">⊘</.test(byName('scripts').content), byName('scripts').content);\nt('那句理由不再出現在任何一列上(一棵樹裡它本來重複 8 次)',\n !/空白樣板/.test(rowsHtml) && !/整個資料夾未收/.test(rowsHtml), rowsHtml);\nt('理由還在,收在滑過去那一格(收起來≠刪掉)', /空白樣板/.test(byName('scripts').payload.pop), byName('scripts').payload.pop);\n// 🔴 數字不能說謊:走得進去的那種要寫真數字,沒走進去的那種一個數字都不准寫\nt('走得進去、量得到 ⇒ 照寫真數字(scripts 底下真的有 1 個檔,不是 0/0',\n /tt-docs[^>]*>0 \\/ 1</.test(byName('scripts').content), byName('scripts').content);\nt('真的沒走進去 ⇒ 不寫數字,只留記號(0/0 會是我們自己編的)',\n !/tt-count/.test(byName('node_modules').content), byName('node_modules').content);\n// 🔴 skipped 的真數字要疊進祖先(#180 之前 rollupTree 把它們整個丟掉 ⇒ 表頭與逐格對不起來)\nt('祖先的合計把 skipped 那幾層的真數字算進去(2+1+3+06',\n /tt-docs[^>]*>2 \\/ 6</.test(fn.folderTreeData(SKIPTREE)[0].content), fn.folderTreeData(SKIPTREE)[0].content);\n\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail ? 1 : 0);\n"}};
const UI_FINGERPRINT = "58dc6b8efc198d4412a4f9055e3801314a9a4fba886aa72637fe6cc99613f528";
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) {
const url = new URL(request.url);
let p = url.pathname;
// 🔴 UI 自報版本(安裝器據此判斷要不要重推,取代「猜 favicon 特徵」)。
// 比照 cypher 的 /health。舊世代的 UI 沒有這條路由 ⇒ SPA fallback 回 HTML
// ⇒ 安裝器解析 JSON 失敗=判定為舊版=重推。**天然向後相容,不需額外世代判斷。**
// bundle_version 由安裝器以 ARCRUN_BUNDLE_VERSION 注入(cypher /health 同一個值);
// 沒注入就回空字串,安裝器那端把空值一律當「舊的」處理。
if (p === '/__version') {
// 🔴 2026-08-08:多報 worker_subdomain / config_ok 兩格。
// 這條路由是**安裝器唯一會去問實例的地方**probeInstanceStale),
// 以前它只報版本 ⇒「版本對、但 apiBase 空」的實例在安裝器眼中是「已是最新」
// ⇒ 永遠不會被重推=壞掉的實例自己好不了。報了之後,安裝器把
// config_ok:false 一律當成 stale ⇒ **下一次安裝/更新就自動治好它**。
return new Response(JSON.stringify({
ok: true,
ui_fingerprint: UI_FINGERPRINT,
bundle_version: env.ARCRUN_BUNDLE_VERSION || '',
worker_subdomain: env.WORKER_SUBDOMAIN || '',
config_ok: Boolean(env.WORKER_SUBDOMAIN),
}), {
headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' },
});
}
// config.js 動態產生:apiBase 指向同帳號的 cypherWORKER_SUBDOMAIN 由安裝器注入)
//
// 🔴 2026-08-08 事故(leo 的 youlin 實例整頁不能用,頂端紅字「設定檔沒載入」):
// 有人用 wrangler deploy 手動補這顆 worker ⇒ 繞過安裝器的變數注入
// ⇒ WORKER_SUBDOMAIN 空 ⇒ 這裡**安靜地**退成 apiBase:""(而且回 HTTP 200!)
// ⇒ 前端連不到自己的後端,但**部署是「成功」的**:worker 活著、curl 200、
// /__version 版本也對——只有真人打開瀏覽器才看得出壞了。
// 正解不是「退一個合理的預設值」(沒有合理預設,猜錯更慘),
// 是**大聲失敗**:回 500+專用標頭,讓機器檢查一眼看穿。
// 瀏覽器不執行非 2xx 的 <script>,所以用戶端行為與今天一致
// (portal 照樣顯示「設定檔沒載入」紅字),差別是**機器也看得見了**。
// no-store:這頁被邊緣快取過就會「修好了還是壞的」(08-08 實撞,害人誤判修復失敗)。
if (p === '/config.js') {
const sub = env.WORKER_SUBDOMAIN || '';
if (!sub) {
return new Response(CONFIG_MISSING_BODY, {
status: 500,
headers: {
'content-type': TYPES_JS,
'cache-control': 'no-store',
'x-arcrun-config-error': 'WORKER_SUBDOMAIN-missing',
},
});
}
const apiBase = `https://arcrun-cypher-executor.${sub}.workers.dev`;
return new Response(`window.ARCRUN_CONFIG = { apiBase: ${JSON.stringify(apiBase)} };`, {
headers: { 'content-type': TYPES_JS, 'cache-control': 'no-store' },
});
}
if (p.endsWith('/')) p += 'index.html';
let f = FILES[p] || FILES[p + '/index.html'] || FILES[p + '.html'];
if (!f && !p.includes('.')) f = FILES[p + '/index.html'];
if (!f) { p = '/portal/index.html'; f = FILES[p]; } // SPA fallback→portal
if (!f) return new Response('not found', { status: 404 });
const body = f.b64 ? b64ToBytes(f.data) : f.data;
return new Response(body, { headers: { 'content-type': f.type, 'cache-control': 'no-cache' } });
},
};
const TYPES_JS = 'application/javascript; charset=utf-8';
const CONFIG_MISSING_BODY = [
'/* ============================================================',
' arcrun-rag-uiWORKER_SUBDOMAIN 這個變數沒有被注入。',
' 算不出 apiBase = 前端不知道自己的後端在哪 = 整個 portal 不能用。',
'',
' 成因幾乎一定是:這顆 worker 被手動 wrangler deploy 部署,繞過了注入。',
' 修法(擇一):',
' 1. 重跑一次安裝器(它會注入,且會把這顆判定為 stale 而重推)',
' 2. node installer/scripts/deploy-ui.mjs --subdomain <你的 workers.dev 子網域>',
' ============================================================ */',
'throw new Error("arcrun-rag-ui: WORKER_SUBDOMAIN 未注入,apiBase 無法產生");',
].join('\n');