t193 三修:每庫一頁+狀態時間軸+預設淺色(leo 六點回饋)

## ① logo 尺寸不對導致白邊
真兇:我寫死 height:22px **沒給 width:auto** ⇒ 容器一壓就變形、露出底板邊緣。
修:照 portal 側欄同一條 clamp(22px,5vw,28px) + width:auto + max-width:100%,
    並依 CIS「clear space = one chevron height」在四邊留白。
實測:logo 底板與品牌區落差 **0**(原本是明顯方塊)。

## ② 深色跟網頁不同/「它有淺色佈景?」
是——**portal 預設淺色**,深色由使用者切換並存 localStorage。
我卻硬跟系統走 ⇒ 兩邊當然不同。
修:改成與 portal 同一套(預設淺色+切換鈕+localStorage),
    按鈕規格也逐字對齊 portal 的 .btn/.btn3(padding/font-size/radius/border)。
實測淺色:品牌區(253,252,251)=Paper、側欄(242,241,237)=Canvas,與 CIS 一致。

## ③ 「開啟知識庫網頁」開到錯的庫
真兇:它在首頁、寫死取 accounts[0] ⇒ 有兩個庫必然開錯。
修:移到**各庫頁的上方**,開的就是那個庫。

## ④⑤ 30 個資料夾放不下/「加入資料夾」會加到哪個帳號?
修:**每個知識庫一個獨立分頁**(側邊欄動態列出,附資料夾數)。
    「加入資料夾」在庫頁裡,**作用對象就是那個庫**,不會加錯。

## ⑥ 首頁要顯示 status(看守/發現變化/萃取/上傳)
修:首頁改成**狀態時間軸**四步,進行中那步的圓點會呼吸。
    誠實邊界:collector 目前回報「整輪」而非逐檔階段,
    所以同步中時後三步一起標進行中,**不假裝有更細的進度**。

兩支機械閘全過(check-cis.sh 14 項/check-render.sh 深淺色皆驗)。
style.css 仍是 0 個 hex(顏色全走 var)。
This commit is contained in:
2026-08-04 23:35:49 +08:00
parent 3d13e5a54e
commit 751ee74f19
4 changed files with 224 additions and 100 deletions
+45
View File
@@ -138,6 +138,50 @@ type UIState struct {
Engine string `json:"engine"` // "workers-ai" | "gemma"
GeminiKey string `json:"geminiKey"` // 只回遮罩,不回真值
ExtractedOK int `json:"extractedOK"` // 首頁「已整理幾份」
Steps []Step `json:"steps"` // 首頁狀態時間軸(leo #6
}
// Step 是首頁狀態時間軸的一格。
// 🔴 leo 08-04:「首頁要顯示的應該是 status,如果**看守、發現變化、萃取、上傳…
// 不同 status 在哪裡顯示**?」
// ⇒ 把一輪同步拆成四步,讓使用者看得到「現在走到哪」,而不是只有一句「看守中」。
type Step struct {
Title string `json:"title"`
Meta string `json:"meta,omitempty"`
State string `json:"state"` // "done"(已完成)|"now"(進行中)|""(還沒輪到)
}
// buildSteps 依 status.json 與訊號檔推出四步的狀態。
// 誠實邊界:collector 目前回報的是「整輪」而非逐檔階段,所以同步中時
// 「發現變化→萃取→上傳」一起標進行中;不假裝有更細的進度。
func buildSteps(s syncStatus, syncing bool) []Step {
watch := Step{Title: "看守資料夾", Meta: "有變動就自動開始", State: "done"}
if syncing {
return []Step{
watch,
{Title: "發現變化", State: "done"},
{Title: "用 AI 整理成知識卡", Meta: "進行中", State: "now"},
{Title: "上傳到你的知識庫", State: ""},
}
}
done := ""
if s.ExtractedOK > 0 || s.LastSync != "" {
done = "done"
}
meta := ""
if s.ExtractedOK > 0 {
meta = fmt.Sprintf("上一輪 %d 份", s.ExtractedOK)
}
up := Step{Title: "上傳到你的知識庫", Meta: meta, State: done}
if s.ExtractFailed > 0 {
up.Meta = fmt.Sprintf("%s · ⚠ %d 份失敗", meta, s.ExtractFailed)
}
return []Step{
watch,
{Title: "發現變化", Meta: "等待中", State: done},
{Title: "用 AI 整理成知識卡", State: done},
up,
}
}
// GetState 是前端每秒拉一次的單一入口。
@@ -164,6 +208,7 @@ func (a *App) GetState() UIState {
sync := loadSyncStatus()
st.ExtractedOK = sync.ExtractedOK
st.Syncing, st.StatusBig, st.StatusSub = describeStatus(sync)
st.Steps = buildSteps(sync, st.Syncing)
return st
}