diff --git a/cmd/arcrun-app/app.go b/cmd/arcrun-app/app.go
index a2d1e9f..8406d6a 100644
--- a/cmd/arcrun-app/app.go
+++ b/cmd/arcrun-app/app.go
@@ -135,8 +135,9 @@ type UIState struct {
StatusSub string `json:"statusSub"`
Syncing bool `json:"syncing"`
Accounts []UIAccount `json:"accounts"`
- Engine string `json:"engine"` // "workers-ai" | "gemma"
- GeminiKey string `json:"geminiKey"` // 只回遮罩,不回真值
+ Engine string `json:"engine"` // "workers-ai" | "gemma"
+ GeminiKey string `json:"geminiKey"` // 只回遮罩,不回真值
+ ExtractedOK int `json:"extractedOK"` // 首頁「已整理幾份」
}
// GetState 是前端每秒拉一次的單一入口。
@@ -161,6 +162,7 @@ func (a *App) GetState() UIState {
}
sync := loadSyncStatus()
+ st.ExtractedOK = sync.ExtractedOK
st.Syncing, st.StatusBig, st.StatusSub = describeStatus(sync)
return st
}
diff --git a/cmd/arcrun-app/check-cis.sh b/cmd/arcrun-app/check-cis.sh
index 4153468..de4318e 100755
--- a/cmd/arcrun-app/check-cis.sh
+++ b/cmd/arcrun-app/check-cis.sh
@@ -6,7 +6,9 @@
# ⇒ 交貨前先跑這支。不過就不准交。
set -uo pipefail
cd "$(dirname "$0")"
-CSS=frontend/src/style.css
+# 色票/字體/紋理在共用底層;版面在 style.css ⇒ 兩份都要看
+CSS=frontend/src/arcrun-cis.css
+LAYOUT=frontend/src/style.css
HTML=frontend/index.html
FAIL=0
ok(){ printf " ✅ %s\n" "$1"; }
@@ -21,8 +23,12 @@ for t in "#FDFCFB:Paper" "#F2F1ED:Canvas" "#17181A:Ink" "#B04A2F:Relation" "#D97
done
# ② 不准自創顏色:抓不在白名單內的 hex
+# 版面檔**一個 hex 都不准有**(顏色一律 var(--...))
+STRAY_LAYOUT=$(grep -oiE '#[0-9a-f]{6}' "$LAYOUT" || true)
+[ -z "$STRAY_LAYOUT" ] && ok "版面檔沒有寫死顏色" || ng "版面檔出現 hex:$STRAY_LAYOUT"
+
STRAY=$(grep -oiE '#[0-9a-f]{6}' "$CSS" | tr 'a-f' 'A-F' | sort -u \
- | grep -vE '#(FDFCFB|F2F1ED|17181A|B04A2F|D9784F|1D7A48|7FE0A8|B03A26|E58575|1B1C1E|1E1F21)' || true)
+ | grep -vE '#(FDFCFB|F2F1ED|17181A|B04A2F|D9784F|1D7A48|7FE0A8|B03A26|E58575|1B1C1E|1E1F21|1D6B40|A8E8C4)' || true)
[ -z "$STRAY" ] && ok "沒有自創顏色" || ng "出現非 CIS 色:$(echo $STRAY | tr '\n' ' ')"
# ③ Logo 必須真的放進去(leo 點名的那條)
diff --git a/cmd/arcrun-app/check-render.sh b/cmd/arcrun-app/check-render.sh
new file mode 100755
index 0000000..c0d4079
--- /dev/null
+++ b/cmd/arcrun-app/check-render.sh
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+# check-render.sh — **看得見的驗收**:用 headless Chrome 真的渲染一次並量像素(t193)
+#
+# 🔴 leo 2026-08-04:「既然是 Web,你做的時候**無法檢視**?例如用 chrome dev tool mcp?」
+# ⇒ 對,可以。以後改完 UI 就跑這支,不要再拿沒看過的畫面交件。
+#
+# 它驗兩件肉眼容易漏、但機器一量就知道的事:
+# ① lockup 底板與品牌區背景是否接合(paper-on-ink.png 自帶不透明 Ink 底板,
+# 背景不對就會出現一個突兀方塊——leo 截圖裡看到的就是它)
+# ② 深色模式的規則有沒有真的進到 build(曾發生 dist 是舊的、我卻對著舊圖除錯)
+set -uo pipefail
+cd "$(dirname "$0")"
+CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
+[ -x "$CHROME" ] || { echo "❌ 找不到 Chrome,跳過渲染檢查"; exit 0; }
+[ -d frontend/dist ] || { echo "❌ 沒有 frontend/dist——先 wails build"; exit 1; }
+
+W=$(mktemp -d); trap 'rm -rf "$W"; kill %1 2>/dev/null' EXIT
+cp -R frontend/dist/* "$W/"
+cat > "$W/mock.js" <<'JS'
+window.go={main:{App:{GetState:async()=>({version:'v0',statusBig:'看守中',statusSub:'',
+ syncing:false,engine:'workers-ai',geminiKey:'',extractedOK:0,accounts:[]}),
+ SyncNow:async()=>{},PickFolder:async()=>'',AddFolder:async()=>{},RemoveFolder:async()=>{},
+ SetAI:async()=>{},OpenURL:()=>{},Connect:async()=>{},CheckUpdate:async()=>({})}}};
+JS
+python3 - "$W" <<'PY'
+import re,sys
+p=sys.argv[1]+'/index.html'; s=open(p,encoding='utf-8').read()
+s=s.replace('src="/assets/','src="./assets/').replace('href="/assets/','href="./assets/')
+s=s.replace('\n
diff --git a/cmd/arcrun-app/frontend/src/arcrun-cis.css b/cmd/arcrun-app/frontend/src/arcrun-cis.css
new file mode 100644
index 0000000..04bf2bc
--- /dev/null
+++ b/cmd/arcrun-app/frontend/src/arcrun-cis.css
@@ -0,0 +1,33 @@
+/* arcrun-cis.css — Arcrun 全站共用樣式底層(唯一真相源)
+ *
+ * 🔴 leo 2026-08-04:「不會這裡又寫了一個新的 CSS?前面剛剛才說過**不要創造新的**?」
+ *
+ * 病根:portal 的樣式**內嵌在 HTML 裡**(單一