From 36a5630c63c8e5e0190fe874465f2673c0c92c47 Mon Sep 17 00:00:00 2001 From: uncle6me-web Date: Thu, 6 Aug 2026 13:12:15 +0800 Subject: [PATCH] =?UTF-8?q?portal=20=E9=A1=AF=E7=A4=BA=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=99=A8=E7=89=88=E6=9C=AC=EF=BC=8B=E4=B8=8B=E8=BC=89=E7=B6=B2?= =?UTF-8?q?=E5=9D=80=E6=94=B9=E8=AE=80=E7=9C=9F=E7=9B=B8=E6=BA=90=EF=BC=88?= =?UTF-8?q?leo=2008-06=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit leo:「Portal 和 rag.arcrun.dev 應該顯示同步器的版本⋯⋯因為你說最新 0.18.5 連我都沒辦法確認,所以用戶到底是否最新版他自己也不知道」。 ## 病 release(雲端知識庫)與 daemon(桌面同步器)是兩條版本線。 版本卡只顯示前者;下載按鈕的檔名是寫死的固定別名 (DAEMON_MAC/DAEMON_WIN)⇒ 頁面上完全看不出「同步器是哪一版」。 ## 改 - 下載網址改向 /api/latest 取 daemon.downloads(真相源=bundles manifest), 取不到才退回原本的固定別名 ⇒ 按鈕永遠可用。 ⚠️ 沒有違背 fe0ee82 立的「不要每出一版就改 code」——網址是取來的, 一樣不必改 code;還順便解掉固定別名的兩個老問題: ① 別名指向 @main 會吃 CDN/ref 快取拿到舊檔(08-04 撞過) ② 檔名不帶版號 ⇒ 無從顯示「這是哪一版」=leo 這次抱怨的正題 - 版本卡下方加一行「最新的同步器版本是 X(你手上那支在同步器視窗左下角)」 +連到版本說明頁。取不到就**不顯示**,不編數字。 - 渲染改成「先用退路畫、取到真相源再重畫」,renderDaemonDownload 做成可重複呼叫 (清掉上一輪插入的節點,否則會疊出兩份「不是這個系統?」)。 ## 驗 · node --test os-split.test.mjs safejson.test.mjs → 8 passed, 0 failed · index.html 三個 script 區塊逐塊 node --check → 全部語法 OK · 未驗:線上畫面(要等 bundle 出貨後才有 daemon 欄位可讀)⇒ ◐ --- console-ui/public/portal/index.html | 99 +++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/console-ui/public/portal/index.html b/console-ui/public/portal/index.html index 11696ba..b0a51b8 100644 --- a/console-ui/public/portal/index.html +++ b/console-ui/public/portal/index.html @@ -802,8 +802,20 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { // 不再給 zip——zip 解開就是 .app,使用者很可能直接在「下載」資料夾雙擊啟動, // 而自更新會蓋錯位置(t184 Oscar 的病)。 // ⚠️ 用**固定檔名**(不帶版號),否則每出一版都要改這裡的 code。 + // + // 🔴 2026-08-06 升級(leo:「Portal 和 rag.arcrun.dev 應該顯示同步器的版本⋯⋯ + // **連我都沒辦法確認**,所以用戶到底是否最新版他自己也不知道」): + // 下面兩個常數**降級為退路**,正常情況改向 `/api/latest` 取 + // `daemon.version` 與 `daemon.downloads`(真相源=bundles 的 manifest)。 + // ⚠️ 這**沒有違背**上面那條「不要每出一版就改 code」——網址現在是**取來的**, + // 一樣不用改 code;而且順便解掉固定別名的兩個老問題: + // ① 別名指向 @main,會吃到 CDN/ref 快取拿到舊檔(08-04 撞過) + // ② 檔名不帶版號 ⇒ 頁面上無從顯示「這是哪一版」=leo 這次抱怨的正題 + // 取不到就退回這兩個固定檔名,按鈕不會變死連結。 var DAEMON_MAC = 'ArcrunRAG-mac.dmg'; var DAEMON_WIN = 'ArcrunRAG-win-unsigned.zip'; + // 由 /api/latest 填入(見下方 loadDaemonLatest);null=還沒取到或取不到。 + var DAEMON_LATEST = null; // Mac 那顆 > jsDelivr 單檔 20MB 上限(實測回 "File size exceeded...")→ 一律走 raw; // Windows 同走 raw 保持單一來源、少一個會壞的地方。 function daemonBase() { @@ -826,8 +838,11 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { var isMobile = /iPhone|iPad|iPod|Android|Mobile/i.test(ua); var isWin = !isMobile && /Windows NT/i.test(ua); var isMac = !isMobile && /Macintosh|Mac OS X/i.test(ua) && !/Windows/i.test(ua); - var mac = { os: 'mac', label: '下載 Mac 版', url: base + DAEMON_MAC }; - var win = { os: 'win', label: '下載 Windows 版', url: base + DAEMON_WIN }; + // 有取到真相源就用它的網址(帶版號、指向釘點 sha),否則退回固定別名。 + var dlm = (DAEMON_LATEST && DAEMON_LATEST.downloads && DAEMON_LATEST.downloads.mac) || (base + DAEMON_MAC); + var dlw = (DAEMON_LATEST && DAEMON_LATEST.downloads && DAEMON_LATEST.downloads.win) || (base + DAEMON_WIN); + var mac = { os: 'mac', label: '下載 Mac 版', url: dlm }; + var win = { os: 'win', label: '下載 Windows 版', url: dlw }; if (isWin) return { pick: win, other: mac, sure: true }; if (isMac) return { pick: mac, other: win, sure: true }; return { pick: null, other: null, sure: false, mac: mac, win: win }; @@ -839,31 +854,65 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return { return '(開啟後把 Arcrun 拖進「應用程式」,再從啟動台開啟;封測版未簽章,第一次請右鍵→打開)'; } // 07-27:設定頁的常駐入口(下載小幫手/換 AI 金鑰)——與一次性卡片同一組 API - (function () { + // 🔴 2026-08-06 改成「先用退路畫、取到真相源再重畫」: + // fetch 是非同步的,若等它回來才畫,網路慢時使用者會看到一顆沒有網址的按鈕。 + // ⇒ 先用固定別名畫出可用的按鈕,取到 /api/latest 後再重畫成帶版號的網址+版本行。 + // renderDaemonDownload 必須**可重複呼叫**(第二次要清掉第一次插進去的兄弟節點), + // 否則重畫會疊出兩份「不是這個系統?」。 + function renderDaemonDownload() { var dl = $('st-daemon-dl'); - if (dl) { - var d = daemonPick(); - if (d.sure) { - dl.setAttribute('href', d.pick.url); - dl.textContent = d.pick.label; - // 判對了也要留另一版的路(UA 會判錯) - var alt = document.createElement('span'); - alt.className = 'muted'; - alt.style.cssText = 'font-size:12.5px;margin-left:8px'; - alt.innerHTML = '不是這個系統?' + d.other.label + ''; - if (dl.parentNode) dl.parentNode.insertBefore(alt, dl.nextSibling); - } else { - // 判不出來=兩個都給,不預設 Mac - dl.setAttribute('href', d.mac.url); - dl.textContent = d.mac.label; - var both = document.createElement('span'); - both.className = 'muted'; - both.style.cssText = 'font-size:12.5px;margin-left:8px'; - both.innerHTML = '或 ' + d.win.label + ''; - if (dl.parentNode) dl.parentNode.insertBefore(both, dl.nextSibling); - } + if (!dl) return; + // 清掉上一輪插入的節點(用 id 標記,才不會誤刪別人的東西) + ['st-daemon-alt', 'st-daemon-ver'].forEach(function (id) { + var old = $(id); if (old && old.parentNode) old.parentNode.removeChild(old); + }); + var d = daemonPick(); + var alt = document.createElement('span'); + alt.id = 'st-daemon-alt'; + alt.className = 'muted'; + alt.style.cssText = 'font-size:12.5px;margin-left:8px'; + if (d.sure) { + dl.setAttribute('href', d.pick.url); + dl.textContent = d.pick.label; + // 判對了也要留另一版的路(UA 會判錯) + alt.innerHTML = '不是這個系統?' + d.other.label + ''; + } else { + // 判不出來=兩個都給,不預設 Mac + dl.setAttribute('href', d.mac.url); + dl.textContent = d.mac.label; + alt.innerHTML = '或 ' + d.win.label + ''; } - })(); + if (dl.parentNode) dl.parentNode.insertBefore(alt, dl.nextSibling); + + // 版本行:leo 要的「用戶看得出自己是不是最新版」。 + // 取不到就**不顯示**,不要編一個數字(同 landing 的原則:寧可空著也不說謊)。 + if (DAEMON_LATEST && DAEMON_LATEST.version) { + var ver = document.createElement('div'); + ver.id = 'st-daemon-ver'; + ver.className = 'muted'; + ver.style.cssText = 'font-size:12.5px;margin-top:6px'; + ver.innerHTML = '最新的同步器版本是 ' + esc(DAEMON_LATEST.version) + '' + + '(你手上那支的版本,在同步器視窗左下角)' + + ' · 這一版改了什麼'; + if (alt.parentNode) alt.parentNode.insertBefore(ver, alt.nextSibling); + } + } + + function loadDaemonLatest() { + // 不用 INSTALLER_ORIGIN:它宣告在本區塊之後(var 提升 ⇒ 這裡是 undefined)。 + fetch('https://install.arcrun.dev/api/latest') + .then(function (r) { return r.ok ? r.json() : null; }) + .then(function (j) { + if (j && j.daemon && j.daemon.version) { + DAEMON_LATEST = j.daemon; + renderDaemonDownload(); // 取到了才重畫 + } + }) + .catch(function () { /* 取不到就維持退路的固定別名,按鈕仍可用 */ }); + } + + renderDaemonDownload(); + loadDaemonLatest(); // t176(leo 08-03):AI 設定的前端邏輯整段移除。 // 雲端聊天走 Workers AI(免金鑰);地端萃取金鑰改由同步小幫手托盤「AI 設定…」自己設。