daemon: 首次啟動改成兩步引導精靈(Gitea #23)

舊版 onboarding() 只有兩行字+兩顆按鈕,沒解釋 Arcrun 是什麼,也沒有
「我做到哪了」的感覺;『我還沒有知識庫』點下去就丟去外部網址,
回來以後要自己想起來要按哪顆鈕才能連線。

改成兩步小精靈,每步都有步驟點(1/2):
① 認識 Arcrun:三句大白話講清楚在做什麼,不用任何內部詞
② 連上或申請知識庫:『已經有了』/『還沒有』並排兩張卡,
   『還沒有』那張當場講清楚『回來這裡按哪顆鈕』,不是丟出去就沒事

連線邏輯(Connect/showConnect)完全沒動,接的是既有後端;
連線成功後落回首頁,首頁本來就有狀態時間軸+自動種好的範例資料夾
(default_library.go 的 P4),使用者不必再多做一步就看得到
『丟檔案 → 知識卡』整條路跑起來。

只動前端(main.js+style.css),零 Go 改動。check-cis.sh 全過;
用 mock window.go.main.App 在瀏覽器截圖驗過兩步畫面(截圖見 issue #23 留言)。
This commit is contained in:
2026-08-09 15:03:23 +08:00
parent 63bea13f21
commit e7a72d74e1
2 changed files with 91 additions and 8 deletions
+55 -8
View File
@@ -29,6 +29,7 @@ $('themeBtn').onclick = () =>
let state = null;
let page = 'home'; // 'home' | 'ai' | 'update' | 'lib:<idx>'
let updateInfo = null;
let obStep = 1; // 首次啟動精靈目前在第幾步(issue #23,見 onboarding()
// ── 覆蓋層(只給必須打斷的確認)──
function openSheet(html, wire) { $('sheet').innerHTML = html; $('overlay').classList.add('on'); if (wire) wire(); }
@@ -345,17 +346,61 @@ function cardDiagnostics() {
</div>`;
}
// ── 第一次打開的引導(issue #23,從 #18 拆出來)──
//
// 🔴 leo 的驗法:「拿一個從沒裝過的狀態實際走一次:不看文件、不問人,
// 能不能自己完成第一次設定並看到第一個成果。」
// 舊版只有兩顆按鈕+兩行字,沒有解釋「這是什麼」,也沒有「我做到哪了」的感覺
// ——這裡改成兩步的小精靈,每步都回答「這是什麼/我該做什麼/我做到哪了」:
// ① 認識 Arcrun:用大白話講清楚在做什麼,不用任何內部詞(collectornamespace
// 萃取…),只用「資料夾」「知識卡」「知識庫」這些使用者本來就懂或一看就懂的詞
// ② 連上或申請知識庫:兩條路並排,「還沒有」那條**當場**講清楚回來要做什麼
// (不是丟出去一個外部網址就沒事,那正是舊版讓人卡住的地方)
// 連線成功後會直接落回首頁——首頁本來就有「狀態時間軸」+自動種好的範例資料夾
// (見 default_library.go 的 P4),使用者不必再多做一步就能看到「丟檔案 → 知識卡」
// 這條路真的跑起來,那就是「第一個成果」。
function onboarding() {
const obDots = `
<div class="obdots">
<span class="obdot ${obStep === 1 ? 'on' : 'done'}">${obStep === 1 ? '1' : '✓'}</span>
<span class="obline"></span>
<span class="obdot ${obStep === 2 ? 'on' : ''}">2</span>
</div>`;
if (obStep === 2) {
return `
<div class="empty ob">
${obDots}
<div class="obcap">第 2 步・共 2 步・連上你的知識庫</div>
<div class="t">你已經有知識庫了嗎?</div>
<div class="d">知識庫是存放你「知識卡」的地方——就像信箱之於信件,之後打開它的網址就能搜尋、AI 也能直接查。</div>
<div class="obchoice">
<div class="obcard">
<div class="obh">已經有了</div>
<div class="d">手上有網址、帳號、密碼(邀請你的人會給你)。</div>
<button class="primary" id="obConnect">連上知識庫</button>
</div>
<div class="obcard">
<div class="obh">還沒有</div>
<div class="d">免費申請一個,幾分鐘完成。<br/>填完會拿到網址、帳號、密碼——<b>回到這裡</b>,按左邊「連上知識庫」貼上去就完成。</div>
<button id="obInstall">免費申請一個</button>
</div>
</div>
<button class="ghost" id="obBack" style="margin-top:16px"> 上一步</button>
</div>`;
}
return `
<div class="empty">
<div class="t">先連上你的知識庫</div>
<div class="d">
Arcrun 會看守你指定的資料夾,<br/>
自動把文件整理成 AI 查得到的知識卡。<br/>
還沒有知識庫?先到 install.arcrun.dev 裝一個(免費)。
<div class="empty ob">
${obDots}
<div class="obcap">第 1 步・共 2 步・認識 Arcrun</div>
<div class="t">歡迎使用 Arcrun</div>
<div class="d obintro">
<p>① 你指定一個資料夾,Arcrun 會在背景幫你看著它。</p>
<p>② 資料夾裡新增或修改的檔案,會被自動整理成一張張「知識卡」。</p>
<p>③ 之後不管在哪台電腦、哪個裝置,打開你的知識庫網站,或讓你的 AI 助理直接問,都找得到。</p>
</div>
<button class="primary" id="obConnect">新增知識庫帳號</button>
<button id="obInstall">我還沒有知識庫</button>
<button class="primary" id="obNext">開始設定</button>
</div>`;
}
@@ -381,6 +426,8 @@ function wire() {
on('hLogs', () => go.OpenLogFolder());
on('hAcct', showConnect); on('obConnect', showConnect);
on('obInstall', () => go.OpenURL('https://install.arcrun.dev/'));
on('obNext', () => { obStep = 2; renderPage(); });
on('obBack', () => { obStep = 1; renderPage(); });
on('aiSave', saveAI);
on('uCheck', checkUpdate); on('uDownload', downloadUpdate); on('uApply', applyUpdate);
on('uDiag', exportDiagnostics);
+36
View File
@@ -149,6 +149,42 @@ button.ghost:hover { color: var(--err); background: transparent; }
.empty .t { font-size: 17px; font-weight: 600; margin-bottom: 10px; }
.empty .d { font-size: 14px; color: rgba(var(--ink-rgb), .55); line-height: 1.9; margin-bottom: 20px; }
/* ── 首次啟動精靈(issue #23):兩步,讓人隨時知道「我做到哪了」──
步驟點沿用首頁「狀態時間軸」同一套配色邏輯(done=--ok、on=--amber),
不另外發明一組顏色。 */
.ob { max-width: 520px; margin: 0 auto; padding-top: 36px; }
.obdots {
display: flex; align-items: center; justify-content: center;
gap: 8px; margin-bottom: 18px;
}
.obdot {
width: 26px; height: 26px; border-radius: 50%; flex: none;
display: flex; align-items: center; justify-content: center;
font-size: 12.5px; font-weight: 600; font-family: ui-monospace, Menlo, monospace;
background: rgba(var(--ink-rgb), .1); color: rgba(var(--ink-rgb), .45);
}
.obdot.on { background: var(--amber); color: var(--knob); }
.obdot.done { background: var(--ok); color: var(--knob); }
.obline { width: 40px; height: 2px; background: rgba(var(--ink-rgb), .12); }
.obcap {
font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
color: rgba(var(--ink-rgb), .4); margin-bottom: 14px;
}
.obintro { text-align: left; max-width: 420px; margin: 0 auto 24px; }
.obintro p { margin: 0 0 10px; }
.obchoice {
display: flex; gap: 14px; margin-top: 22px; text-align: left;
flex-wrap: wrap; justify-content: center;
}
.obcard {
flex: 1 1 200px; max-width: 240px;
background: var(--well); border-radius: 12px; padding: 16px 18px;
display: flex; flex-direction: column; gap: 8px;
}
.obcard .obh { font-size: 14.5px; font-weight: 600; }
.obcard .d { margin-bottom: 4px; }
.obcard button { margin-top: auto; }
label.radio { display: flex; align-items: flex-start; gap: 10px; margin: 12px 0; font-size: 14px; cursor: pointer; }
label.radio input { margin-top: 3px; }
input[type=text], input[type=password] {