文件
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

72 行
2.2 KiB
TypeScript

import * as React from 'react';
import { useEffect, useState } from 'react';
import { _ } from '@joplin/lib/locale';
import EmbeddingIndexer from '@joplin/lib/services/ai/EmbeddingIndexer';
import { IndexStatus } from '@joplin/lib/services/ai/types';
// Live status panel under the "Enable AI features" toggle. Polls
// EmbeddingIndexer.getStatus() while the AI section is visible.
const POLL_INTERVAL_MS = 2000;
const modelStatusLabel = (s: IndexStatus['modelDownloadStatus']) => {
switch (s) {
case 'not-started': return _('Not started');
case 'downloading': return _('Downloading…');
case 'downloaded': return _('Downloaded');
case 'unavailable': return _('Unavailable on this platform');
}
};
const indexerStateLabel = (s: IndexStatus['indexerState']) => {
switch (s) {
case 'idle': return _('Idle');
case 'running': return _('Indexing…');
case 'ai-disabled': return _('AI is disabled');
case 'index-disabled': return _('Indexing is disabled');
case 'vector-search-unavailable': return _('Vector search is not available on this platform');
}
};
const AiIndexStatus = () => {
const [status, setStatus] = useState<IndexStatus | null>(null);
useEffect(() => {
let cancelled = false;
let timer: ReturnType<typeof setTimeout> | null = null;
// Chained setTimeout, not setInterval — keeps polls from stacking up
// when getStatus() is slow (e.g. competing with the model load).
const tick = async () => {
try {
const s = await EmbeddingIndexer.instance().getStatus();
if (!cancelled) setStatus(s);
} catch {
// Swallow: this panel is decorative.
}
if (!cancelled) timer = setTimeout(() => void tick(), POLL_INTERVAL_MS);
};
void tick();
return () => {
cancelled = true;
if (timer) clearTimeout(timer);
};
}, []);
if (!status) return null;
return (
<div className='ai-index-status'>
<h3 className='title'>{_('Embedding index')}</h3>
<dl className='rows'>
<dt>{_('Model:')}</dt>
<dd>{modelStatusLabel(status.modelDownloadStatus)}</dd>
<dt>{_('Indexer:')}</dt>
<dd>{indexerStateLabel(status.indexerState)}</dd>
<dt>{_('Indexed notes:')}</dt>
<dd>{`${status.notesIndexed} / ${status.totalNotes}`}</dd>
</dl>
</div>
);
};
export default AiIndexStatus;