Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions frontend/app/knowledge/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useIsCloudBrand } from "@/contexts/brand-context";
import { useKnowledgeIngestFocus } from "@/hooks/useKnowledgeIngestFocus";
import { getConnectorDescriptor } from "@/lib/connectors/registry";
import {
buildKnowledgeTableRows,
Expand Down Expand Up @@ -453,17 +454,13 @@ function SearchPage() {

const gridRows = fileResults;
const gridRef = useRef<AgGridReact>(null);
const {
gridRowsSelectionKey,
onKnowledgeGridReady,
onKnowledgeRowDataUpdated,
} = useKnowledgeIngestFocus(gridRef, gridRows, taskFiles);

// Re-run only when row identity/status changes, not on every list poll reference.
const gridRowsSelectionKey = useMemo(
() =>
gridRows
.map(
(row) => `${getKnowledgeFileIdentity(row)}:${row.status ?? "active"}`,
)
.join("\0"),
[gridRows],
);

useEffect(() => {
const api = gridRef.current?.api;
Expand Down Expand Up @@ -1011,6 +1008,8 @@ function SearchPage() {
}
isRowSelectable={(params) => isDeletableKnowledgeRow(params.data)}
domLayout="normal"
onGridReady={onKnowledgeGridReady}
onRowDataUpdated={onKnowledgeRowDataUpdated}
onSelectionChanged={onSelectionChanged}
pagination={pagination}
paginationPageSize={paginationPageSize}
Expand Down Expand Up @@ -1045,6 +1044,8 @@ function SearchPage() {
}
isRowSelectable={(params) => isDeletableKnowledgeRow(params.data)}
domLayout="normal"
onGridReady={onKnowledgeGridReady}
onRowDataUpdated={onKnowledgeRowDataUpdated}
onSelectionChanged={onSelectionChanged}
pagination={pagination}
paginationPageSize={paginationPageSize}
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/upload/[provider]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { useTask } from "@/contexts/task-context";
import { useSessionIngestSettings } from "@/hooks/useSessionIngestSettings";
import { getConnectorDescriptor } from "@/lib/connectors/registry";
import { queueKnowledgeIngestFocusForCloudFiles } from "@/lib/knowledge-grid-pagination";

// CloudFile interface is now imported from the unified cloud picker

Expand Down Expand Up @@ -91,6 +92,7 @@ export default function UploadProviderPage() {
files: CloudFile[],
replaceDuplicates: boolean,
) => {
queueKnowledgeIngestFocusForCloudFiles(files, replaceDuplicates);
syncMutation.mutate(
{
connectorType: connector.type,
Expand Down
6 changes: 6 additions & 0 deletions frontend/components/AgGrid/registerAgGridModules.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
CellStyleModule,
ClientSideRowModelApiModule,
ClientSideRowModelModule,
ColumnApiModule,
ColumnAutoSizeModule,
Expand All @@ -9,7 +10,9 @@ import {
ModuleRegistry,
PaginationModule,
QuickFilterModule,
RowApiModule,
RowSelectionModule,
ScrollApiModule,
TextFilterModule,
ValidationModule,
} from "ag-grid-community";
Expand All @@ -24,6 +27,9 @@ ModuleRegistry.registerModules([
CellStyleModule,
QuickFilterModule,
ClientSideRowModelModule,
ClientSideRowModelApiModule,
RowApiModule,
ScrollApiModule,
TextFilterModule,
DateFilterModule,
EventApiModule,
Expand Down
2 changes: 2 additions & 0 deletions frontend/components/knowledge-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
getConnectorDescriptor,
getConnectorDescriptors,
} from "@/lib/connectors/registry";
import { dispatchKnowledgeIngestFocus } from "@/lib/knowledge-grid-pagination";
import {
duplicateCheck,
uploadFiles,
Expand Down Expand Up @@ -335,6 +336,7 @@ export function KnowledgeDropdown() {
setFileUploading(true);

try {
dispatchKnowledgeIngestFocus(file.name, replace);
await uploadFileUtil(file, replace);
refetchTasks();
} catch (error) {
Expand Down
3 changes: 3 additions & 0 deletions frontend/contexts/task-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export function TaskProvider({ children }: { children: React.ReactNode }) {
},
[],
);

const openTaskDialog = useCallback((taskId: string) => {
setTaskDialogTaskId(taskId);
setIsTaskDialogOpen(true);
Expand Down Expand Up @@ -587,6 +588,8 @@ export function TaskProvider({ children }: { children: React.ReactNode }) {
!isTerminalFailedTask(previousTask) &&
isTerminalFailedTask(currentTask)
) {
clearTaskConnectorType(currentTask.task_id);

if (!isOnboardingActive) {
selectTask(currentTask.task_id);
setIsMenuOpen(true);
Expand Down
152 changes: 152 additions & 0 deletions frontend/hooks/useKnowledgeIngestFocus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"use client";

import type { AgGridReact } from "ag-grid-react";
import { type RefObject, useCallback, useEffect, useMemo, useRef } from "react";
import type { File } from "@/app/api/queries/useGetSearchQuery";
import type { TaskFile } from "@/contexts/task-context";
import {
buildGridRowsSelectionKey,
collectNewIngestFocusIdentities,
collectProcessingFocusIdentities,
consumePersistedKnowledgeIngestFocus,
focusPendingIngestRows,
type IngestFocusMode,
inferIngestFocusMode,
ingestFocusModeFromReplace,
KNOWLEDGE_INGEST_FOCUS_EVENT,
} from "@/lib/knowledge-grid-pagination";

export function useKnowledgeIngestFocus(
gridRef: RefObject<AgGridReact<File> | null>,
gridRows: File[],
taskFiles: TaskFile[],
) {
const paginationSnapshotRef = useRef({
initialized: false,
taskFiles: [] as TaskFile[],
gridRows: [] as File[],
});

const pendingFocusRef = useRef({
identities: new Set<string>(),
modes: new Map<string, IngestFocusMode>(),
});

const tryFocusPendingIngestRows = useCallback(
(rows: File[]) => {
const run = () => {
const api = gridRef.current?.api;
if (!api) {
return;
}
const pending = pendingFocusRef.current;
const resolved = focusPendingIngestRows(
api,
pending.identities,
rows,
pending.modes,
);
for (const identity of resolved) {
pending.identities.delete(identity);
pending.modes.delete(identity);
}
};
requestAnimationFrame(() => requestAnimationFrame(run));
},
[gridRef],
);

const queueIngestFocusIdentities = useCallback(
(identities: string[], mode?: IngestFocusMode, rows: File[] = gridRows) => {
if (identities.length === 0) {
return;
}
const pending = pendingFocusRef.current;
for (const identity of identities) {
pending.identities.add(identity);
pending.modes.set(
identity,
mode ??
pending.modes.get(identity) ??
inferIngestFocusMode(identity, rows),
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
tryFocusPendingIngestRows(rows);
},
[gridRows, tryFocusPendingIngestRows],
);

useEffect(() => {
const stored = consumePersistedKnowledgeIngestFocus();
for (const target of stored) {
queueIngestFocusIdentities(
[target.filename],
ingestFocusModeFromReplace(target.replace),
);
}
}, [queueIngestFocusIdentities]);

useEffect(() => {
const handler = (event: Event) => {
const detail = (
event as CustomEvent<{ filename: string; replace: boolean }>
).detail;
if (!detail?.filename) {
return;
}
queueIngestFocusIdentities(
[detail.filename],
ingestFocusModeFromReplace(detail.replace),
);
};
window.addEventListener(KNOWLEDGE_INGEST_FOCUS_EVENT, handler);
return () =>
window.removeEventListener(KNOWLEDGE_INGEST_FOCUS_EVENT, handler);
}, [queueIngestFocusIdentities]);

useEffect(() => {
const snapshot = paginationSnapshotRef.current;
if (!snapshot.initialized) {
snapshot.taskFiles = taskFiles;
snapshot.gridRows = gridRows;
snapshot.initialized = true;
return;
}

const fromTasks = collectNewIngestFocusIdentities(
snapshot.taskFiles,
taskFiles,
);
const fromGrid = collectProcessingFocusIdentities(
snapshot.gridRows,
gridRows,
);
snapshot.taskFiles = taskFiles;
snapshot.gridRows = gridRows;

queueIngestFocusIdentities([...new Set([...fromTasks, ...fromGrid])]);
}, [taskFiles, gridRows, queueIngestFocusIdentities]);

const gridRowsSelectionKey = useMemo(
() => buildGridRowsSelectionKey(gridRows),
[gridRows],
);

useEffect(() => {
tryFocusPendingIngestRows(gridRows);
}, [gridRows, gridRowsSelectionKey, tryFocusPendingIngestRows]);

const onKnowledgeGridReady = useCallback(() => {
tryFocusPendingIngestRows(gridRows);
}, [gridRows, tryFocusPendingIngestRows]);

const onKnowledgeRowDataUpdated = useCallback(() => {
tryFocusPendingIngestRows(gridRows);
}, [gridRows, tryFocusPendingIngestRows]);

return {
gridRowsSelectionKey,
onKnowledgeGridReady,
onKnowledgeRowDataUpdated,
};
}
Loading
Loading