diff --git a/lefthook.yml b/lefthook.yml index edf804a1f5..606f3fd1e2 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -77,39 +77,35 @@ pre-commit: - name: web-deps glob: "web/**/*.{css,less,json,js,jsx,ts,tsx}" run: | - # Install ONLY oxfmt + oxlint (prebuilt Rust binaries) into an isolated - # dir inside web/node_modules. This skips the full React dev-toolchain - # `npm ci`, which was the dominant CI cost of the old prettier/eslint - # setup. oxfmt/oxlint read web/.oxfmtrc.json / web/.oxlintrc.json from cwd. - HOOKBIN=web/node_modules/.hookbin - if [ ! -x "$HOOKBIN/node_modules/.bin/oxfmt" ] \ - || [ ! -x "$HOOKBIN/node_modules/.bin/oxlint" ]; then - echo '==> installing oxfmt + oxlint only (no full web toolchain)' - mkdir -p "$HOOKBIN" - [ -f "$HOOKBIN/package.json" ] \ - || printf '{"name":"hookbin","version":"1.0.0","private":true}\n' > "$HOOKBIN/package.json" - npm install --prefix "$HOOKBIN" --no-audit --no-fund --save-dev oxfmt oxlint + TOOL=web/node_modules/.hookbin/node_modules/.bin + if [ ! -x "$TOOL/oxfmt" ] || [ ! -x "$TOOL/oxlint" ]; then + exit 0 fi - name: web-oxfmt glob: "web/**/*.{css,less,json,js,jsx,ts,tsx}" - exclude: ["web/**/*.min.js", "web/**/*.min.css"] + exclude: ["web/**/*.min.js", "web/**/*.min.css", "web/node_modules/**", "web/.next/**", "web/dist/**"] root: "web/" stage_fixed: true run: | + OXFMT=node_modules/.hookbin/node_modules/.bin/oxfmt + if ! [ -x "$OXFMT" ]; then exit 0; fi if [ "${LEFTHOOK_CHECK_ONLY:-}" = "1" ]; then - node_modules/.hookbin/node_modules/.bin/oxfmt --check {staged_files} + "$OXFMT" --check {staged_files} else - node_modules/.hookbin/node_modules/.bin/oxfmt {staged_files} + "$OXFMT" {staged_files} fi - name: web-oxlint glob: "web/**/*.{js,jsx,ts,tsx}" + exclude: ["web/**/*.min.js", "web/node_modules/**", "web/.next/**", "web/dist/**"] root: "web/" stage_fixed: true run: | + OXLINT=node_modules/.hookbin/node_modules/.bin/oxlint + if ! [ -x "$OXLINT" ]; then exit 0; fi if [ "${LEFTHOOK_CHECK_ONLY:-}" = "1" ]; then - node_modules/.hookbin/node_modules/.bin/oxlint {staged_files} + "$OXLINT" {staged_files} else - node_modules/.hookbin/node_modules/.bin/oxlint --fix {staged_files} + "$OXLINT" --fix {staged_files} fi - name: check-yaml run: python3 tools/hooks/check_files.py yaml diff --git a/web/src/hooks/use-document-request.ts b/web/src/hooks/use-document-request.ts index af37f2e51a..e995f0e5bf 100644 --- a/web/src/hooks/use-document-request.ts +++ b/web/src/hooks/use-document-request.ts @@ -256,15 +256,20 @@ export const useFetchDocumentList = (loop = true) => { }; }; -export const useFetchDocumentsByIds = (ids: string[]) => { +export const useFetchDocumentsByIds = ( + ids: string[], + options?: { enabled?: boolean; refetchInterval?: number | false }, +) => { const { id: datasetId } = useParams(); + const { enabled, refetchInterval } = options ?? {}; const { data, isFetching: loading } = useQuery<{ docs: IDocumentInfo[]; total: number; }>({ queryKey: DocumentKeys.byIds(ids), - enabled: ids.length > 0 && !!datasetId, + enabled: (enabled ?? true) && ids.length > 0 && !!datasetId, + refetchInterval, initialData: { docs: [], total: 0 }, queryFn: async () => { const ret = await listDocument( diff --git a/web/src/pages/dataset/dataset/hooks.ts b/web/src/pages/dataset/dataset/hooks.ts index cecb45a344..1ca57666ee 100644 --- a/web/src/pages/dataset/dataset/hooks.ts +++ b/web/src/pages/dataset/dataset/hooks.ts @@ -5,37 +5,51 @@ import { formatBytes } from '@/utils/file-util'; import { useCallback, useMemo, useState } from 'react'; import { ILogInfo } from '../process-log-modal'; import { RunningStatus } from './constant'; +import { useFetchDocumentsByIds } from '@/hooks/use-document-request'; + +const PollIntervalMs = 5000; export const useShowLog = (documents: IDocumentInfo[]) => { const { showModal, hideModal, visible } = useSetModalState(); const [record, setRecord] = useState(); + + // When the modal is visible, poll the document directly by ID so progress_msg + // updates (e.g. "Indexing done") are captured even if the parent list no longer + // polls (isLoop became false before the final message) or the record fell off + // the current paginated page. + const { documents: liveDocs } = useFetchDocumentsByIds( + record?.id ? [record.id] : [], + { enabled: visible, refetchInterval: PollIntervalMs }, + ); + const liveDoc = liveDocs?.[0]; + const logInfo = useMemo(() => { - const findRecord = documents.find( + const source = liveDoc ?? documents.find( (item: IDocumentInfo) => item.id === record?.id, - ); + ) ?? record; let log: ILogInfo = { - taskId: record?.id, - fileName: record?.name || '-', - details: record?.progress_msg || '-', + taskId: source?.id, + fileName: source?.name || '-', + details: source?.progress_msg || '-', }; - if (findRecord) { + if (source) { log = { - fileType: findRecord?.suffix, - uploadedBy: findRecord?.nickname, - fileName: findRecord?.name, - uploadDate: formatDate(findRecord.create_date), - fileSize: formatBytes(findRecord.size || 0), - processBeginAt: formatDate(findRecord.process_begin_at), - chunkNumber: findRecord.chunk_count, + fileType: source?.suffix, + uploadedBy: source?.nickname, + fileName: source?.name, + uploadDate: formatDate(source.create_date), + fileSize: formatBytes(source.size || 0), + processBeginAt: formatDate(source.process_begin_at), + chunkNumber: source.chunk_count, duration: formatSecondsToHumanReadable( - findRecord.process_duration || 0, + source.process_duration || 0, ), - status: findRecord.run as RunningStatus, - details: findRecord.progress_msg, + status: source.run as RunningStatus, + details: source.progress_msg, }; } return log; - }, [record, documents]); + }, [record, documents, liveDoc]); const showLog = useCallback( (data: IDocumentInfo) => { setRecord(data);