// Arcrun 桌面 App — 前端(t193)
//
// 全部畫面都在**同一個視窗**裡:狀態、清單、設定都用內嵌覆蓋層,
// 不再像 fyne 版那樣「每個功能開一個小小的 popup」(leo 08-04 點破)。
import './style.css';
const go = window.go.main.App;
const $ = (id) => document.getElementById(id);
// 跟隨系統深淺色——CIS 兩套色票都定義好了,這裡只負責切 data-theme
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const applyTheme = () =>
document.documentElement.setAttribute('data-theme', mq.matches ? 'dark' : 'light');
mq.addEventListener('change', applyTheme);
applyTheme();
// ── 覆蓋層(取代 popup 視窗)──
function openSheet(html, wire) {
$('sheet').innerHTML = html;
$('overlay').classList.add('on');
if (wire) wire();
}
function closeSheet() { $('overlay').classList.remove('on'); }
$('overlay').addEventListener('click', (e) => { if (e.target.id === 'overlay') closeSheet(); });
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeSheet(); });
const esc = (s) => String(s).replace(/[&<>"]/g, (c) =>
({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
// ── 主畫面渲染 ──
let state = null;
function render(s) {
state = s;
$('ver').textContent = s.version ? '版本 ' + s.version : '';
$('statusBig').textContent = s.statusBig;
$('statusBig').classList.toggle('syncing', !!s.syncing);
$('statusSub').textContent = s.statusSub;
const list = $('list');
if (!s.accounts || s.accounts.length === 0) {
// 空狀態=onboarding,不是空白面板
list.innerHTML = `
先連上你的知識庫
Arcrun 會看守你指定的資料夾,
自動把文件整理成 AI 查得到的知識卡。
還沒有知識庫?先到 install.arcrun.dev 裝一個(免費)。
`;
$('obConnect').onclick = showConnect;
$('obInstall').onclick = () => go.OpenURL('https://install.arcrun.dev/');
return;
}
list.innerHTML = s.accounts.map((a) => `
${esc(a.name)}${esc(a.host)}
${(a.folders || []).map((f) => `
${esc(f.path)}
自動同步中
`).join('')
|| `還沒有資料夾——按下面的「加入資料夾」
`}
`).join('');
list.querySelectorAll('[data-rm]').forEach((b) => {
b.onclick = () => confirmRemove(Number(b.dataset.acc), b.dataset.rm);
});
}
async function tick() {
try { render(await go.GetState()); } catch (e) { /* 後端還沒起來,下一輪再試 */ }
}
// ── 動作 ──
$('btnSync').onclick = async () => { await go.SyncNow(); tick(); };
$('btnAddFolder').onclick = async () => {
if (!state || !state.accounts.length) { showConnect(); return; }
const p = await go.PickFolder(); // 系統原生面板(macOS powerbox 會授權)
if (!p) return;
await go.AddFolder(0, p);
tick();
};
function confirmRemove(accIdx, path) {
openSheet(`
移除這個資料夾?
「${esc(path)}」不再自動同步。
已經上傳的知識卡不會被刪除。
`, () => {
$('c1').onclick = closeSheet;
$('c2').onclick = async () => { await go.RemoveFolder(accIdx, path); closeSheet(); tick(); };
});
}
function showConnect() {
openSheet(`
連上你的知識庫
貼上你的知識庫網址,再輸入你在網站上設定的帳號密碼。
`, () => {
$('c1').onclick = closeSheet;
$('c2').onclick = async () => {
const err = $('err');
try {
await go.Connect($('u').value.trim(), $('e').value.trim(), $('p').value);
closeSheet(); tick();
} catch (ex) {
err.textContent = String(ex); err.style.display = 'block';
}
};
});
}
$('btnAddAccount').onclick = showConnect;
$('btnAI').onclick = () => {
const gem = state && state.engine === 'gemma';
openSheet(`
用哪個 AI 幫你整理文件?
`, () => {
$('c1').onclick = closeSheet;
$('c2').onclick = async () => {
const useGemini = document.querySelector('input[name=ai]:checked').value === 'gemini';
const err = $('err');
try {
await go.SetAI(useGemini, $('k').value.trim());
closeSheet(); tick();
} catch (ex) { err.textContent = String(ex); err.style.display = 'block'; }
};
});
};
$('btnUpdate').onclick = () => go.OpenURL('https://rag.arcrun.dev/docs/use/update/');
tick();
setInterval(tick, 1000); // 每秒刷新 ⇒ 同步中看得到在動