@@ -63,7 +63,18 @@ import { TAIPEI_CLIENT_JS } from '../lib/taipei-time';
export const consoleRouter = new Hono < { Bindings : Bindings } > ( ) ;
function renderConsoleHtml ( registryBase : string , brand : string ) : string {
function renderConsoleHtml ( registryBase : string , brand : string , profile : string ) : string {
// Console profile( config 裁剪,非 auth——#24/#25 多人 Portal 是另一回事):
// full(未設即此)=全套,Mira 實例現行為一字不變。
// rag=企業產品樣:只留 總庫搜尋/工作流/設定(card 是搜尋的詳頁,跟著留),落地=搜尋頁。
// 隱藏頁的 view div 仍在 DOM( display:none、路由 guard 導回,永遠點不進)——
// 因 creds/inbox 有 top-level addEventListener 綁定,抽掉 div 會讓整包 JS 啟動即炸,
// 純渲染層裁剪以「導不進去」為準,不為了抽 DOM 把 null-guard 灑滿全檔。
const rag = profile === 'rag' ;
const views = rag
? [ 'search' , 'card' , 'workflows' , 'settings' ]
: [ 'cockpit' , 'search' , 'card' , 'workflows' , 'creds' , 'inbox' , 'settings' ] ;
const home = rag ? 'search' : 'cockpit' ;
return ` <!doctype html>
<html lang="zh-Hant">
<head>
@@ -302,7 +313,7 @@ function renderConsoleHtml(registryBase: string, brand: string): string {
<div id="login-status" class="err" style="font-size:14px;min-height:1.2em"></div>
</div>
<div style="font-size:13.5px;color:rgba(var(--ink-rgb),.4);line-height:1.7">這是一個人的智慧總部。<br>若你不是擁有者,這裡沒有你要找的東西。</div>
<a href="/console/dashboard" style="font-size:13.5px;color:rgba(var(--amber-rgb),.7)">免登入看駕駛艙 › </a>
${ rag ? '' : ' <a href="/console/dashboard" style="font-size:13.5px;color:rgba(var(--amber-rgb),.7)">免登入看駕駛艙 › </a>' }
<button class="btn3 themelabel" data-themetoggle style="align-self:center;padding:8px 16px;font-size:13.5px;border-radius:999px">☾ 切深色</button>
</div>
</div>
@@ -330,11 +341,11 @@ function renderConsoleHtml(registryBase: string, brand: string): string {
<div id="shell">
<div id="sidenav">
<div class="logo"> ${ brand } </div>
<div class="nav" data-nav="cockpit">駕駛艙</div>
${ rag ? '' : ' <div class="nav" data-nav="cockpit">駕駛艙</div>' }
<div class="nav" data-nav="search">總庫搜尋</div>
<div class="nav" data-nav="workflows">工作流</div>
<div class="nav" data-nav="creds">憑證管理</div>
<div class="nav" data-nav="inbox">分流台</div>
${ rag ? '' : ` <div class="nav" data-nav="creds">憑證管理</div>
<div class="nav" data-nav="inbox">分流台</div> ` }
<div class="nav" data-nav="settings">設定</div>
<div class="nav themelabel" data-themetoggle style="margin-top:8px;font-size:14.5px">☾ 切深色</div>
<div class="foot" id="side-foot"></div>
@@ -498,13 +509,13 @@ function renderConsoleHtml(registryBase: string, brand: string): string {
<div id="st-pw-status" style="font-size:14px;min-height:1.2em"></div>
</div>
</div>
<div class="panel" style="display:flex;align-items:center;gap:12px;cursor:pointer" data-nav="creds">
${ rag ? '' : ` <div class="panel" style="display:flex;align-items:center;gap:12px;cursor:pointer" data-nav="creds">
<div>
<div style="font-size:17px;font-weight:600">憑證管理</div>
<div style="margin-top:4px;font-size:14px;color:rgba(var(--ink-rgb),.55)">只保存目錄與加密值,不可回看</div>
</div>
<span style="margin-left:auto;color:rgba(var(--amber-rgb),.6);font-size:18px">› </span>
</div>
</div> ` }
<div class="panel">
<div style="font-size:17px;font-weight:600;margin-bottom:12px">系統資訊</div>
<div id="st-info"><div class="muted">載入中…</div></div>
@@ -518,10 +529,10 @@ function renderConsoleHtml(registryBase: string, brand: string): string {
<!-- 手機底部導覽 -->
<div id="tabbar">
<div class="tab" data-nav="cockpit">駕駛艙</div>
${ rag ? '' : ' <div class="tab" data-nav="cockpit">駕駛艙</div>' }
<div class="tab" data-nav="search">搜尋</div>
<div class="tab" data-nav="workflows">工作流</div>
<div class="tab" data-nav="inbox">分流台</div>
${ rag ? '' : ' <div class="tab" data-nav="inbox">分流台</div>' }
<div class="tab" data-nav="settings">設定</div>
</div>
@@ -646,11 +657,14 @@ function renderConsoleHtml(registryBase: string, brand: string): string {
}
// ── 路由 ──
var VIEWS = ['cockpit', 'search', 'card', 'workflows', 'creds', 'inbox', 'settings'];
// VIEWS / HOME 由 server 依 CONSOLE_PROFILE 注入(full=全套 7 頁落地駕駛艙;rag=4 頁落地搜尋)。
// 不在 VIEWS 裡的 hash(含被 profile 裁掉的頁)一律導回 HOME——手打 #/cockpit 也進不去。
var VIEWS = ${ JSON . stringify ( views ) } ;
var HOME = ${ JSON . stringify ( home ) } ;
function currentRoute() {
var h = location.hash.replace(/^# \\ /?/, '');
var seg = h.split('/');
return { view: VIEWS.indexOf(seg[0]) >= 0 ? seg[0] : 'cockpit' , arg: seg.slice(1).join('/') };
return { raw: seg[0] || '', view: VIEWS.indexOf(seg[0]) >= 0 ? seg[0] : HOME , arg: seg.slice(1).join('/') };
}
function nav(view, arg) {
location.hash = '#/' + view + (arg ? '/' + encodeURIComponent(arg) : '');
@@ -666,6 +680,8 @@ function renderConsoleHtml(registryBase: string, brand: string): string {
var LOADERS = { cockpit: loadCockpit, search: initSearchOnce, card: loadCard, workflows: loadWorkflows, creds: loadCreds, inbox: loadTriage, settings: loadSettings };
function route() {
var r = currentRoute();
// 隱藏頁/不存在頁 guard: hash 不在 VIEWS → 改寫成 HOME( hashchange 會再進來一次正常渲染)
if (r.raw && r.raw !== r.view) { location.hash = '#/' + r.view; return; }
if (r.view === 'card' && r.arg) S.cardId = decodeURIComponent(r.arg);
showView(r.view);
if (LOADERS[r.view]) LOADERS[r.view]();
@@ -689,7 +705,7 @@ function renderConsoleHtml(registryBase: string, brand: string): string {
$ ('shell').classList.add('on');
$ ('tabbar').classList.add('on');
$ ('side-foot').innerHTML = '租戶 ' + esc(S.tenant) + '<br>' + esc(location.host);
if (!location.hash) location.hash = '#/cockpit ';
if (!location.hash) location.hash = '#/' + HOME ;
route();
}
function init() {
@@ -1476,5 +1492,6 @@ consoleRouter.get('/console', (c) => {
? ` https://arcrun-registry. ${ subdomain } .workers.dev `
: 'https://registry.arcrun.dev' ;
// 品牌字樣(Arcrun#21):引擎預設 Arcrun,實例可用 CONSOLE_BRAND 覆蓋(如 "Arcrun RAG")
return c . html ( renderConsoleHtml ( registryBase , c . env . CONSOLE_BRAND || 'Arcrun' ) ) ;
// 頁面組成(console-profile-trim):CONSOLE_PROFILE 未設=full 全套;"rag"=企業產品樣(搜尋落地)
return c . html ( renderConsoleHtml ( registryBase , c . env . CONSOLE_BRAND || 'Arcrun' , c . env . CONSOLE_PROFILE || 'full' ) ) ;
} ) ;