移植 t21 溯源 scheme 相容:kb:// 不造死鏈(救自已刪分支)

## 這條與 t195 直接相關
新 ingest 鏈(rag_ingest_card/rag_ingest_direct,就是我今天在修的收卡鏈)
寫的 metadata.source 是 **kb://<path>**,但 main 的 portal 溯源只認 gitea://
⇒ 卡片上傳成功後,**溯源連結一律失效**。

原修補在 fix/portal-source-scheme-t21(07-24),一個月沒併回 main。
該分支的檔案 console-ui/src/portal-ui.ts 已被重構搬走(UI 搬 CF Pages),
⇒ 不能直接 merge,**移植邏輯到現行的 console-ui/public/portal/index.html**。

## 三種 scheme 的正解(照原分支設計)
· gitea://<path>          舊 rag_ingest v2 → {base}/<path>,行為一字不變(不回歸)
· gitea:<org/repo>@<path> km-wiki-ingest 實形 → {base}/<org/repo>/src/branch/main/<path>
· kb://<path>             新 ingest 鏈實形 → **回空字串=維持純文字,不造死鏈**
  (雲端沒有對應網頁;另給 srcLocalPath() 取本地相對路徑顯示)

## 實測四情境(抽出函式跑 node)
gitea://docs/a b.md#3        → https://…/base/docs/a%20b.md           錨點捨棄+編碼
gitea:Leo/kb@notes/x.md#seg01 → https://…/base/Leo/kb/src/branch/main/notes/x.md 
kb://wiki/cards/policy.md    →                                      不造死鏈
srcLocalPath(kb://…#seg02)   → wiki/cards/policy.md                  

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
uncle6me-web
2026-08-05 13:38:19 +08:00
parent 5783420fcf
commit 3d21bf0725
+27 -3
View File
@@ -605,10 +605,34 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
// 來源回溯超連結:PORTAL_SOURCE_WEB_BASE 有設且 source 是 gitea:// 才給 href
// 其餘回空字串=維持純文字(行為與未設時一字不變)。chunk 錨點(#n)捨棄。
var SOURCE_WEB_BASE = "";
function encSrcPath(p) { return p.split('/').map(encodeURIComponent).join('/'); }
// 🔴 t21(移植自已刪分支 fix/portal-source-scheme-t212026-08-05 分支整理救回):
// 新 ingest 鏈(rag_ingest_cardrag_ingest_direct)寫的 metadata.source 是 **kb://<path>**
// 但這裡原本只認 gitea:// ⇒ 收卡上雲後溯源連結一律失效(B4 部好溯源後一經重灌即再斷鏈)。
// 三種 scheme 各自的正解:
// gitea://<path> (舊 rag_ingest v2)→ {base}/<path>,行為一字不變
// gitea:<org/repo>@<path> km-wiki-ingest 實形)→ {base}/<org/repo>/src/branch/main/<path>
// kb://<path> (新 ingest 鏈實形)→ **雲端沒有對應網頁,回空字串=維持純文字,不造死鏈**
function srcHref(src) {
if (!SOURCE_WEB_BASE || String(src).indexOf('gitea://') !== 0) return '';
var p = String(src).slice(8).replace(/#\d+$/, '');
return SOURCE_WEB_BASE.replace(/\/+$/, '') + '/' + p.split('/').map(encodeURIComponent).join('/');
src = String(src);
if (!SOURCE_WEB_BASE) return '';
var base = SOURCE_WEB_BASE.replace(/\/+$/, '');
if (src.indexOf('gitea://') === 0) {
return base + '/' + encSrcPath(src.slice(8).replace(/#\d+$/, ''));
}
if (src.indexOf('gitea:') === 0) {
var rest = src.slice(6).replace(/#[^#]*$/, '');
var at = rest.indexOf('@');
if (at <= 0 || at >= rest.length - 1) return '';
return base + '/' + encSrcPath(rest.slice(0, at)) + '/src/branch/main/' + encSrcPath(rest.slice(at + 1));
}
return '';
}
// kb:// 來源的本地相對路徑(去 scheme、去錨點);非 kb:// 回空字串。
function srcLocalPath(src) {
src = String(src);
if (src.indexOf('kb://') !== 0) return '';
return src.slice(5).replace(/#[^#]*$/, '');
}
function entryLib(e) { var m = entryMeta(e); return (typeof m.library === 'string' && m.library) ? m.library : 'general'; }
function entryTitle(e) {