mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-21 18:55:16 +02:00
Merge remote-tracking branch 'upstream/dev' into feat/ui-revamp
This commit is contained in:
commit
481bb406b6
757 changed files with 70989 additions and 4089 deletions
|
|
@ -1,82 +0,0 @@
|
|||
import { atom } from "jotai";
|
||||
import { rightPanelCollapsedAtom, rightPanelTabAtom } from "@/atoms/layout/right-panel.atom";
|
||||
|
||||
export interface ExtraField {
|
||||
label: string;
|
||||
key: string;
|
||||
value: string;
|
||||
type: "text" | "email" | "emails" | "datetime-local" | "textarea";
|
||||
}
|
||||
|
||||
interface HitlEditPanelState {
|
||||
isOpen: boolean;
|
||||
title: string;
|
||||
content: string;
|
||||
toolName: string;
|
||||
contentFormat?: "markdown" | "html";
|
||||
extraFields?: ExtraField[];
|
||||
onSave:
|
||||
| ((title: string, content: string, extraFieldValues?: Record<string, string>) => void)
|
||||
| null;
|
||||
onClose: (() => void) | null;
|
||||
}
|
||||
|
||||
const initialState: HitlEditPanelState = {
|
||||
isOpen: false,
|
||||
title: "",
|
||||
content: "",
|
||||
toolName: "",
|
||||
contentFormat: undefined,
|
||||
extraFields: undefined,
|
||||
onSave: null,
|
||||
onClose: null,
|
||||
};
|
||||
|
||||
export const hitlEditPanelAtom = atom<HitlEditPanelState>(initialState);
|
||||
|
||||
const preHitlCollapsedAtom = atom<boolean | null>(null);
|
||||
|
||||
export const openHitlEditPanelAtom = atom(
|
||||
null,
|
||||
(
|
||||
get,
|
||||
set,
|
||||
payload: {
|
||||
title: string;
|
||||
content: string;
|
||||
toolName: string;
|
||||
contentFormat?: "markdown" | "html";
|
||||
extraFields?: ExtraField[];
|
||||
onSave: (title: string, content: string, extraFieldValues?: Record<string, string>) => void;
|
||||
onClose?: () => void;
|
||||
}
|
||||
) => {
|
||||
if (!get(hitlEditPanelAtom).isOpen) {
|
||||
set(preHitlCollapsedAtom, get(rightPanelCollapsedAtom));
|
||||
}
|
||||
set(hitlEditPanelAtom, {
|
||||
isOpen: true,
|
||||
title: payload.title,
|
||||
content: payload.content,
|
||||
toolName: payload.toolName,
|
||||
contentFormat: payload.contentFormat,
|
||||
extraFields: payload.extraFields,
|
||||
onSave: payload.onSave,
|
||||
onClose: payload.onClose ?? null,
|
||||
});
|
||||
set(rightPanelTabAtom, "hitl-edit");
|
||||
set(rightPanelCollapsedAtom, false);
|
||||
}
|
||||
);
|
||||
|
||||
export const closeHitlEditPanelAtom = atom(null, (get, set) => {
|
||||
const current = get(hitlEditPanelAtom);
|
||||
current.onClose?.();
|
||||
set(hitlEditPanelAtom, initialState);
|
||||
set(rightPanelTabAtom, "sources");
|
||||
const prev = get(preHitlCollapsedAtom);
|
||||
if (prev !== null) {
|
||||
set(rightPanelCollapsedAtom, prev);
|
||||
set(preHitlCollapsedAtom, null);
|
||||
}
|
||||
});
|
||||
|
|
@ -4,45 +4,108 @@ import { atom } from "jotai";
|
|||
import type { Document } from "@/contracts/types/document.types";
|
||||
|
||||
/**
|
||||
* Atom to store the full document objects mentioned via @-mention chips
|
||||
* in the current chat composer. This persists across component remounts.
|
||||
* Sentinel ``document_type`` used for folder mention chips so the
|
||||
* dedup key (`kind:document_type:id`) never collides a document with a
|
||||
* folder that happens to share an integer id.
|
||||
*/
|
||||
export const mentionedDocumentsAtom = atom<Pick<Document, "id" | "title" | "document_type">[]>([]);
|
||||
export const FOLDER_MENTION_DOCUMENT_TYPE = "FOLDER";
|
||||
|
||||
/**
|
||||
* Derived read-only atom that maps deduplicated mentioned docs
|
||||
* into backend payload fields.
|
||||
*/
|
||||
export const mentionedDocumentIdsAtom = atom((get) => {
|
||||
const allDocs = get(mentionedDocumentsAtom);
|
||||
const seen = new Set<string>();
|
||||
const deduped = allDocs.filter((d) => {
|
||||
const key = `${d.document_type}:${d.id}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
return {
|
||||
surfsense_doc_ids: deduped
|
||||
.filter((doc) => doc.document_type === "SURFSENSE_DOCS")
|
||||
.map((doc) => doc.id),
|
||||
document_ids: deduped
|
||||
.filter((doc) => doc.document_type !== "SURFSENSE_DOCS")
|
||||
.map((doc) => doc.id),
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Simplified document info for display purposes
|
||||
* Display metadata for a single ``@``-mention chip.
|
||||
*
|
||||
* The ``kind`` discriminator identifies whether the chip is a
|
||||
* knowledge-base document or a knowledge-base folder. Folders carry
|
||||
* the sentinel ``document_type === FOLDER_MENTION_DOCUMENT_TYPE`` so
|
||||
* the editor, picker, and persisted ``mentioned-documents`` content
|
||||
* part all stay aligned with the backend Pydantic schema.
|
||||
*/
|
||||
export interface MentionedDocumentInfo {
|
||||
id: number;
|
||||
title: string;
|
||||
document_type: string;
|
||||
kind: "doc" | "folder";
|
||||
}
|
||||
|
||||
/**
|
||||
* Atom to store mentioned documents per message ID.
|
||||
* Backwards-compatible doc-only chip shape for legacy callers that
|
||||
* haven't migrated to the discriminated union yet. Keep narrow so
|
||||
* accidental new callers fail typecheck and route through the
|
||||
* discriminated type instead.
|
||||
*/
|
||||
type LegacyDocMention = Pick<Document, "id" | "title" | "document_type">;
|
||||
|
||||
/**
|
||||
* Normalize an arbitrary chip-like input into the discriminated
|
||||
* ``MentionedDocumentInfo`` shape. Existing call sites that only have
|
||||
* ``{id, title, document_type}`` flow through here so they don't have
|
||||
* to thread ``kind`` everywhere — the helper defaults to ``"doc"`` and
|
||||
* rewrites the document type for folders.
|
||||
*/
|
||||
export function toMentionedDocumentInfo(
|
||||
input: LegacyDocMention | MentionedDocumentInfo
|
||||
): MentionedDocumentInfo {
|
||||
if ("kind" in input && (input.kind === "doc" || input.kind === "folder")) {
|
||||
return input;
|
||||
}
|
||||
return {
|
||||
id: input.id,
|
||||
title: input.title,
|
||||
document_type: input.document_type,
|
||||
kind: "doc",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a folder-mention chip from a folder row (id + name).
|
||||
*/
|
||||
export function makeFolderMention(input: { id: number; name: string }): MentionedDocumentInfo {
|
||||
return {
|
||||
id: input.id,
|
||||
title: input.name,
|
||||
document_type: FOLDER_MENTION_DOCUMENT_TYPE,
|
||||
kind: "folder",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Atom to store the full mention objects (documents + folders) attached
|
||||
* via @-mention chips in the current chat composer. Persists across
|
||||
* component remounts.
|
||||
*/
|
||||
export const mentionedDocumentsAtom = atom<MentionedDocumentInfo[]>([]);
|
||||
|
||||
/**
|
||||
* Derived read-only atom that maps deduplicated mention chips into
|
||||
* backend payload fields. Doc chips split by ``document_type`` exactly
|
||||
* like before; folder chips are projected into a separate
|
||||
* ``folder_ids`` bucket so the route can forward
|
||||
* ``mentioned_folder_ids`` to the agent without the priority middleware
|
||||
* conflating them with hybrid-search ids.
|
||||
*/
|
||||
export const mentionedDocumentIdsAtom = atom((get) => {
|
||||
const allMentions = get(mentionedDocumentsAtom);
|
||||
const seen = new Set<string>();
|
||||
const deduped = allMentions.filter((m) => {
|
||||
const key = `${m.kind}:${m.document_type}:${m.id}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
const docs = deduped.filter((m) => m.kind === "doc");
|
||||
const folders = deduped.filter((m) => m.kind === "folder");
|
||||
return {
|
||||
surfsense_doc_ids: docs
|
||||
.filter((doc) => doc.document_type === "SURFSENSE_DOCS")
|
||||
.map((doc) => doc.id),
|
||||
document_ids: docs
|
||||
.filter((doc) => doc.document_type !== "SURFSENSE_DOCS")
|
||||
.map((doc) => doc.id),
|
||||
folder_ids: folders.map((f) => f.id),
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Atom to store mentioned chips per message ID.
|
||||
* This allows displaying which documents were mentioned with each user message.
|
||||
*/
|
||||
export const messageDocumentsMapAtom = atom<Record<string, MentionedDocumentInfo[]>>({});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue