81 lines
682 KiB
JavaScript
81 lines
682 KiB
JavaScript
// arcrun-rag-ui — 用戶實例的 GUI(console-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-01(arcrun-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 // 台北時間 helper(lib/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) => ({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[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-01(arcrun-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 <!-- t36(leo 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 // 台北時間 helper(lib/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 { '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[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*>\\s?/.test(line)) { closeList(); out.push('<blockquote>' + mdInline(line.replace(/^\\s*>\\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 // 隱藏頁/不存在頁 guard:hash 不在 VIEWS → 改寫成 HOME(hashchange 會再進來一次正常渲染)\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/map(cypher 代轉 KBDB 基本盤,kbdb-proxy.ts——同 /kbdb/search 慣例)。\n // 兩段式渲染:先用全館列表(top_entities=名字 top3)立即出卡片,再逐庫拉 /kbdb/map/:library\n // 補 degree/bridges/commit_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 收斂:圖查詢改打 KBDB,neighbors 是 { 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 // ── 零件 / recipes(v0 搜尋台保留區)──\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#9:todo+inbox 二源、三欄 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 entries(Logseq 萃取,ingest 灌入中)+ inbox entries(Telegram)。<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-check(console session 保護的小端點——瀏覽器沒有 KBDB\n // token,PATCH 由 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#25:portal admin 密碼救援——只吃 console owner session(S.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/#82,leo 2026-08-24 拍板)\n// 跑法:node console-ui/public/portal/app-mount.test.mjs\n//\n// 這支守的是**協定的形狀**,不是長相——長相要用瀏覽器量(見 #152 的驗收留言)。\n// 之所以要有它:這一輪的病根是「出口與樣式被做成個案」——筆記 App 有返回鍵、總圖沒有;\n// CIS 表複製進 shadow 了、但 App 根本沒接上。個案會悄悄再長出來,所以把\n// 「不准再有個案」這件事寫成可執行的檢查。\nimport fs from 'node:fs';\n\nconst html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname, 'utf8');\nlet pass = 0, fail = 0;\nconst chk = (label, cond, extra = '') => {\n if (cond) { console.log('PASS:', label); pass++; }\n else { console.log('FAIL:', label, extra); fail++; }\n};\n\n// ── ① 出口:每一個非首頁的 view 都必須有 .pagehead ────────────────────────────\n// ensureBackControls() 是往 `#v-<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+4(skipped 的 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 root(leo 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 #17181A/Paper #FDFCFB/Canvas #F2F1ED/Muted #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.on,console 同修)── */\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 用 --amber/Relation\n 當 logo 色——CIS:Relation 是「唯一強調色」不是品牌色,logo 上色是一句謊話),\n 靠兩張圖(-ink/-paper-on-ink)依 data-theme 切換,不用 filter 避免色偏。\n 08-01 二次修正:官方原始 PNG(1840x560)畫布只有 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#152(leo 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#152(leo 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#152(leo 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#44:leo「要看到從 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% 是 22–24px),\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#180,leo 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#180,leo 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/macOS/Android 各長一套,\n 同一份畫面在不同機器上不一致,設計稿無法被保證;\n ② emoji 吃不到 CIS 的 --ink/--amber ⇒ 切深色模式時它不會跟著變色;\n ③ 本 repo 的 CIS 是紙感墨字(見 system-dev/wiki/cis-web-style.md)。\n 尺寸/筆畫全部照 leo 核准的設計稿(Main.dc.html/Launcher-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 為什麼不是 markmap/jsTree/treejs/@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 sprite(Arcrun#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.html/Launcher-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<!-- 首次設定(t49,leo 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#152:OS 級四段導覽——取代原本的單層清單。已安裝的 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-suite:upload 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 零件/Recipe/Credential 現在活在另一套 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/零件/Recipe/Credential</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-02(leo:「不顯示的話用戶不知道要不要更新」+「發現落後就按一下開啟 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 <!-- D62(leo 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-09(arcrun-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\">接上你的 AI(MCP)</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 <!-- t176(leo 08-03):AI 設定整塊移除。\n 雲端聊天問答走 Workers AI(用戶自己 CF 帳號內建,**免金鑰、裝好就能用**);\n 地端萃取用哪把金鑰改由「同步小幫手」自己設定(托盤選單「AI 設定…」)。\n 為什麼移除:雲端的 extractor 設定是全租戶共用一把 KV,任一處設了 claude\n 會讓所有沒裝 Claude Code 的機器萃取全滅,而這裡的 Claude 勾選框又恆為\n disabled(daemon 從未回報 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-08(t213,InkStoneCo 總管交辦)發現這顆按鈕在**封測者的瀏覽器**裡執行,\n 跟他電腦上的 daemon 是兩個獨立行程,構不到本機資料(檔案總量/失敗分類/\n daemon 版本)——完整版改在 arcrun-app(同步小幫手)「版本與更新」頁本機端匯出\n (打新端點 GET /portal/daemon/diagnostics)。當時按鈕先保留當退路,文案改成\n 誠實講清楚自己只有一半、導去完整版。\n\n 2026-08-09(leo 拍板拿掉):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 <!-- 管理(P4,admin-only:nav 只有 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 // 台北時間 helper(lib/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 { '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[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 再上格式,防 XSS;console 同款)──\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*>\\s?/.test(line)) { closeList(); out.push('<blockquote>' + mdInline(line.replace(/^\\s*>\\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 顯示 helpers(source/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-t21,2026-08-05 分支整理救回):\n // 新 ingest 鏈(rag_ingest_card/rag_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-07:entry_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 系統 v0(K5):'app' 是**唯一**代表「隨便哪個已安裝 App」的 view——\n // 具體是哪一個由 S.appId(route 的 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-24(Arcrun#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#152,leo 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-status(configured 布林,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 // t53(leo 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-X/tab-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 // t161(leo 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-27,leo:「客戶是用 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 // t176(leo 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:複製任意網址到剪貼簿的共用 helper(08-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-09(arcrun-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#81(leo 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#81(leo 08-12):第二項「啟用 AI 問答(貼 Google AI 金鑰)」整條刪除。\n // 為什麼不是「一個沒用的欄位」而已:它長在**安裝完成清單裡而且是勾選項**\n // ⇒ 用戶會以為不做這步就沒裝完,而它要人離開流程、去第三方網站申請帳號、\n // 把金鑰貼進表單——整條安裝路徑上最重的一個動作,而且是白做的。\n // 真相:雲端問答走 t181(08-04)改好的 Workers AI(`env.AI` binding,免金鑰)——\n // /portal/data/chat → tenant 的 rag_chat workflow → `workers_ai_chat` recipe\n // (api-recipe-seeds.ts:150,endpoint `@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 // t54:config.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#44;inkstone/Arcrun#170 改寫成「只畫命中的那條路徑」)──\n // leo 原話(Arcrun#44):「在 AI 搜尋格子應看到我輸入的內容從 graph 找到幾個資料夾、\n // 從資料夾的摘要讀到的內容⋯⋯會看到他的參照原文位置檔名,而不是簡單向量搜尋的結果。」\n //\n // 🔴 leo 原話(inkstone/Arcrun#170,2026-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 // (見 adminLibsTreeData/folderKidsData)。它們仍是兩支獨立的資料組法,\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 registry/jsDelivr/原始碼,不是憑印象):\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.17(leo 附的範例之一)\n // 形狀對(就是檔案總管),但:\n // ❌ `dependencies: { jquery: \"^3.5.0\" }`(npm registry 實查)——這頁沒有 jQuery,\n // 為了一棵樹要吞 jQuery 87KB + jstree.min.js 139KB + 主題 CSS 27KB\n // + 兩張雪碧圖 + throbber.gif(jsDelivr 實查檔案大小)= 5 個檔、約 255KB。\n // ❌ 它的節點是**單行錨點**(`.jstree-anchor` 固定行高、nowrap),而我們的節點\n // 要掛多段異質 HTML;要嘛改寫它的主題,要嘛放棄 (a)。\n //\n // ③ TreeJS(m-thalmann/treejs,leo 附的範例之二)\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 沒有存 hostname/machine 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#180,leo 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 的 padding/font-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 沒有 hostname/machine id/device 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 只代查再作答;答案附 sources(mode+page),點 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-wrap+esc,不走 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#100:leo 看到\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 // 固定種子 PRNG(mulberry32):確定性佈局\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 系統 v0:Portal 泛用 loader(K5)──\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 // ── Icon(Arcrun#152):描邊式 inline SVG,不用 emoji ─────────────────────\n // 為什麼換掉 emoji:① emoji 是各作業系統自帶的彩色點陣字形,Windows/macOS/\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 // 遇到不認得的 icon(App 宣告裡自己填的 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#152,leo 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/#82,leo 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/upload;Gitea 位置與 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 尾段即 base64(unicode 安全,免手刻 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-retention,role=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 // 🔴 這裡只組**資料**,一行畫面都不畫——畫法只有 renderTree/treeNodeHtml 一份。\n // 第四層(子資料夾/檔案)的資料由 folderTreeData() 出,也就是入口②:\n // 兩個入口的資料在這裡接成一棵連續的樹,而不是各畫各的兩棵。\n //\n // 🔴 機器那一層現在**沒有資料**(collector 全 repo 沒有 hostname/machine 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#180(leo 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#44,leo 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#180(2026-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#180,leo 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_id,entries 標 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_version)vs 最新版(安裝器 /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-05:Mac 一律給 DMG(拖進 Applications 的標準安裝畫面),不再給 zip\n // ——zip 解開就是一個裸 .app,使用者會直接在「下載」資料夾雙擊執行,自更新會蓋錯位置。\n chk('Mac 給 dmg(不是 zip)', d.sure && d.pick.url.endsWith('ArcrunRAG-mac.dmg'));\n chk('Mac 另一版是 Windows', d.other && d.other.url.endsWith('win-unsigned.zip'));\n chk('Mac 話術提 右鍵打開', api.daemonHint('mac').includes('右鍵'));\n }\n if (name==='iPhone' || name==='Linux') {\n // iPhone 含 \"Mac OS X\" 但不是桌機 Mac;Linux 兩者皆非 → 都該落在「不確定=兩個都給」\n if (name==='Linux') chk('Linux 判不出來→兩個都給', d.sure===false);\n if (name==='iPhone') chk('iPhone 不該被判成 Mac(手機→兩個都給)', d.sure===false, 'sure='+d.sure);\n }\n}\n// 舊 key 相容\nconst fn2 = new Function('navigator','window', src + '; return daemonBase();');\nconsole.log('\\n[相容] daemonDownload 舊 key →', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}}));\nchk('舊 key 推得出目錄', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}})==='https://x.dev/d/');\nchk('daemonBase 新 key 優先', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonBase:'https://y.dev/z'}})==='https://y.dev/z/');\nconsole.log(`\\n=== ${pass} passed, ${fail} failed ===`);\nprocess.exit(fail?1:0);\n"},"/portal/retrieval-tree.test.mjs":{"type":"application/javascript; charset=utf-8","b64":false,"data":"// 檢索過程樹(Arcrun#44 第二輪,leo 2026-08-18「希望以樹狀圖來展示」)的資料組裝測試。\n// 只測**樹的資料**,不測它怎麼被畫出來(那在 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#170(2026-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 之前每個節點塞 2–3 行(<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#144,leo 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 裡的引號要跳脫,不能把屬性打斷', /"|"|'/.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 ≈ 759px,1080p 放得下',\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#180(leo 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#180(leo 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+0=6)',\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 指向同帳號的 cypher(WORKER_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-ui:WORKER_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');
|