fix(mira): wikilink autocomplete 5s cache + 開下拉時 refetch
leo 反饋:30s TTL 太久,wiki_synthesis 後台跑出新 entity,autocomplete 撈不到。 - TTL 30s → 5s - WikilinkAutocomplete 在 matchInfo 從 null → 有值時主動 invalidate refetch - 順手把 yaml-parser 對 FOREACH iterator relation 命名變體(「對每個 X」/「FOREACH X」)放行,graph-builder 早就支援,validate 卻擋掉 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -216,17 +216,21 @@ async function fetchAllEntityNames(apiKey: string): Promise<Set<string>> {
|
||||
}
|
||||
}
|
||||
|
||||
// Cache:每個 session 只 fetch 一次 entity list(autocomplete 用)
|
||||
// Cache:debounce 短 TTL(5s),同時 autocomplete 開啟時主動 refetch
|
||||
// 30s 太久:wiki_synthesis 後台跑出新 entity(如 Claude 自己命名的「李飛飛的視界之旅」)autocomplete 撈不到
|
||||
let cachedEntityNames: Set<string> | null = null;
|
||||
let cachedEntityFetchedAt = 0;
|
||||
async function getEntityNamesCached(apiKey: string): Promise<Set<string>> {
|
||||
const now = Date.now();
|
||||
// 30s TTL — leo 持續寫貼文時新建的 entity 也能很快被 autocomplete 看到
|
||||
if (cachedEntityNames && now - cachedEntityFetchedAt < 30_000) return cachedEntityNames;
|
||||
if (cachedEntityNames && now - cachedEntityFetchedAt < 5_000) return cachedEntityNames;
|
||||
cachedEntityNames = await fetchAllEntityNames(apiKey);
|
||||
cachedEntityFetchedAt = now;
|
||||
return cachedEntityNames;
|
||||
}
|
||||
function invalidateEntityCache() {
|
||||
cachedEntityNames = null;
|
||||
cachedEntityFetchedAt = 0;
|
||||
}
|
||||
|
||||
// 簡單 type 推測:《X》 → 書;http(s)://X → URL;其他 → 概念
|
||||
function guessEntityType(name: string): 'book' | 'url' | 'concept' {
|
||||
@@ -664,10 +668,17 @@ function WikilinkAutocomplete({
|
||||
const [matchInfo, setMatchInfo] = useState<{ start: number; query: string } | null>(null);
|
||||
const [selectedIdx, setSelectedIdx] = useState(0);
|
||||
|
||||
// 載入 entity 清單(cached 30s)
|
||||
// 載入 entity 清單(5s cache + 每次 `[[` 開啟時 refetch 保持新鮮)
|
||||
useEffect(() => {
|
||||
getEntityNamesCached(apiKey).then(set => setEntities(Array.from(set).sort()));
|
||||
}, [apiKey]);
|
||||
// matchInfo 從 null → 有值 = 剛打開 autocomplete → 重抓
|
||||
useEffect(() => {
|
||||
if (!matchInfo) return;
|
||||
invalidateEntityCache();
|
||||
getEntityNamesCached(apiKey).then(set => setEntities(Array.from(set).sort()));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [matchInfo !== null, apiKey]);
|
||||
|
||||
// 監聽 textarea 變化 / cursor 移動 → 重算 matchInfo
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user