diff options
Diffstat (limited to 'src/client/views/nodes')
5 files changed, 75 insertions, 81 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 78bacdcac..fb2346bd1 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -49,7 +49,6 @@ import { gptImageLabel } from '../../apis/gpt/GPT'; const DefaultPath = '/assets/unknown-file-icon-hi.png'; export class ImageEditorData { - // eslint-disable-next-line no-use-before-define private static _instance: ImageEditorData; private static get imageData() { return (ImageEditorData._instance ?? new ImageEditorData()).imageData; } // prettier-ignore @observable imageData: { rootDoc: Doc | undefined; open: boolean; source: string; addDoc: Opt<(doc: Doc | Doc[], annotationKey?: string) => boolean> } = observable({ rootDoc: undefined, open: false, source: '', addDoc: undefined }); diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss index 4a916e86c..0bacc70c2 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.scss @@ -183,7 +183,6 @@ $font-size-xlarge: 18px; box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.05); position: relative; align-items: center; - gap: 12px; z-index: 5; transition: padding 0.2s ease; diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx index 19459b025..636b77b38 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx @@ -7,10 +7,12 @@ * with support for follow-up questions and citation management. */ +import { Button, Size, Type } from '@dash/components'; import { ObservableSet, action, computed, makeObservable, observable, observe, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import OpenAI, { ClientOptions } from 'openai'; import * as React from 'react'; +import { AiOutlineSend } from 'react-icons/ai'; import { v4 as uuidv4 } from 'uuid'; import { ClientUtils, OmitKeys } from '../../../../../ClientUtils'; import { Doc, DocListCast, Opt } from '../../../../../fields/Doc'; @@ -18,7 +20,9 @@ import { DocData, DocViews } from '../../../../../fields/DocSymbols'; import { Id } from '../../../../../fields/FieldSymbols'; import { RichTextField } from '../../../../../fields/RichTextField'; import { ScriptField } from '../../../../../fields/ScriptField'; -import { CsvCast, DocCast, PDFCast, RTFCast, StrCast, VideoCast, AudioCast } from '../../../../../fields/Types'; +import { AudioCast, CsvCast, DocCast, NumCast, PDFCast, RTFCast, StrCast, VideoCast } from '../../../../../fields/Types'; +import { Upload } from '../../../../../server/SharedMediaTypes'; +import { DocServer } from '../../../../DocServer'; import { DocUtils } from '../../../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../../../documents/DocumentTypes'; import { Docs, DocumentOptions } from '../../../../documents/Documents'; @@ -26,23 +30,23 @@ import { DocumentManager } from '../../../../util/DocumentManager'; import { ImageUtils } from '../../../../util/Import & Export/ImageUtils'; import { LinkManager } from '../../../../util/LinkManager'; import { CompileError, CompileScript } from '../../../../util/Scripting'; +import { SnappingManager } from '../../../../util/SnappingManager'; import { DictationButton } from '../../../DictationButton'; import { ViewBoxAnnotatableComponent } from '../../../DocComponent'; import { AudioBox } from '../../AudioBox'; import { DocumentView, DocumentViewInternal } from '../../DocumentView'; import { FieldView, FieldViewProps } from '../../FieldView'; +import { OpenWhere } from '../../OpenWhere'; import { PDFBox } from '../../PDFBox'; import { ScriptingBox } from '../../ScriptingBox'; import { VideoBox } from '../../VideoBox'; import { Agent } from '../agentsystem/Agent'; import { supportedDocTypes } from '../types/tool_types'; import { ASSISTANT_ROLE, AssistantMessage, CHUNK_TYPE, Citation, ProcessingInfo, SimplifiedChunk, TEXT_TYPE } from '../types/types'; +import { AgentDocumentManager } from '../utils/AgentDocumentManager'; import { Vectorstore } from '../vectorstore/Vectorstore'; import './ChatBox.scss'; import MessageComponentBox from './MessageComponent'; -import { OpenWhere } from '../../OpenWhere'; -import { Upload } from '../../../../../server/SharedMediaTypes'; -import { AgentDocumentManager } from '../utils/AgentDocumentManager'; export type parsedDocData = { doc_type: string; @@ -555,8 +559,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { })(); if (ndoc) { - ndoc.x = NumCast((options.x as number) ?? 0) + (insideCol ? 0 : NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc.width)) + 100; - ndoc.y = NumCast(options.y as number) + (insideCol ? 0 : NumCast(this.layoutDoc.y)); + ndoc.x = ((options.x as number) ?? 0) + (insideCol ? 0 : NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc.width)) + 100; + ndoc.y = ((options.y as number) ?? 0) + (insideCol ? 0 : NumCast(this.layoutDoc.y)); } return ndoc; }; @@ -655,8 +659,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } else { console.warn(`Chunk not found for chunk ID: ${chunkId}`); } - return; - } + return; + } console.log(`Found chunk in document:`, foundChunk); @@ -665,7 +669,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const directMatchSegmentStart = this.getDirectMatchingSegmentStart(doc, citation.direct_text || '', foundChunk.indexes || []); if (directMatchSegmentStart) { await this.goToMediaTimestamp(doc, directMatchSegmentStart, foundChunk.chunkType); - } else { + } else { console.error('No direct matching segment found for the citation.'); } } else if (foundChunk.chunkType === CHUNK_TYPE.TABLE || foundChunk.chunkType === CHUNK_TYPE.IMAGE) { @@ -710,7 +714,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { // If specific indexes are provided, filter segments by those indexes if (indexesOfSegments && indexesOfSegments.length > 0) { - segments = original_segments.filter((segment: any) => indexesOfSegments.includes(segment.index)); + segments = original_segments.filter(segment => indexesOfSegments.includes(segment.index)); } // If no segments match the indexes, use all segments @@ -719,7 +723,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } // First try to find an exact match - const exactMatch = segments.find((segment: any) => segment.text && segment.text.includes(citationText)); + const exactMatch = segments.find(segment => segment.text && segment.text.includes(citationText)); if (exactMatch) { return exactMatch.start; @@ -828,7 +832,8 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { existingDoc._width = x2 - x1; existingDoc._height = y2 - y1; } - const highlightDoc = existingDoc ?? this.createImageCitationHighlight(x1, y1, x2, y2, citation, annotationKey, doc); + // const highlightDoc = + existingDoc ?? this.createImageCitationHighlight(x1, y1, x2, y2, citation, annotationKey, doc); //doc.layout_scroll = y1; doc._layout_curPage = foundChunk.startPage + 1; @@ -922,7 +927,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { console.error(`Maximum verification attempts (${attempt}) reached for document ${doc.id}`); // Last resort: force re-creation of the document view - if (isPDF) { + if (isPDF) { console.log('Forcing document recreation as last resort'); DocumentManager.Instance.showDocument(doc, { willZoomCentered: true, @@ -950,7 +955,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { return; } - this.processPDFDocumentView(doc, isPDF, citation, foundChunk); + this.processPDFDocumentView(doc, isPDF, citation, foundChunk); } catch (error) { console.error(`Error on verification attempt ${attempt}:`, error); if (attempt < 5) { @@ -1454,16 +1459,16 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { disabled={this._isLoading} /> </div> - <button className="submit-button" onClick={() => this._dictation?.stopDictation()} type="submit" disabled={this._isLoading || !this._inputValue.trim()}> - {this._isLoading ? ( - <div className="spinner"></div> - ) : ( - <svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"> - <line x1="22" y1="2" x2="11" y2="13"></line> - <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon> - </svg> - )} - </button> + <Button + // className="submit-button" + onClick={() => this._dictation?.stopDictation()} + type={Type.PRIM} + tooltip="Send to AI" + color={SnappingManager.userVariantColor} + inactive={this._isLoading || !this._inputValue.trim()} + icon={<AiOutlineSend />} + size={Size.LARGE} + /> <DictationButton ref={this.setDictationRef} setInput={this.setChatInput} inputRef={this._textInputRef} /> </form> {/* Popup for citation */} diff --git a/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts b/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts index a55f901e1..da4a4ae29 100644 --- a/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts +++ b/src/client/views/nodes/chatbot/tools/DocumentMetadataTool.ts @@ -1,16 +1,7 @@ -import { Doc, FieldType } from '../../../../../fields/Doc'; -import { DocData } from '../../../../../fields/DocSymbols'; +import { Parameter, ParametersType, supportedDocTypes, ToolInfo } from '../types/tool_types'; import { Observation } from '../types/types'; -import { ParametersType, ToolInfo, Parameter } from '../types/tool_types'; -import { BaseTool } from './BaseTool'; -import { DocumentOptions } from '../../../../documents/Documents'; -import { CollectionFreeFormDocumentView } from '../../../nodes/CollectionFreeFormDocumentView'; -import { v4 as uuidv4 } from 'uuid'; -import { LinkManager } from '../../../../util/LinkManager'; -import { DocCast, StrCast } from '../../../../../fields/Types'; -import { supportedDocTypes } from '../types/tool_types'; -import { parsedDoc } from '../chatboxcomponents/ChatBox'; import { AgentDocumentManager } from '../utils/AgentDocumentManager'; +import { BaseTool } from './BaseTool'; // Define the parameters for the DocumentMetadataTool const parameterDefinitions: ReadonlyArray<Parameter> = [ @@ -598,7 +589,7 @@ export class DocumentMetadataTool extends BaseTool<DocumentMetadataToolParamsTyp message: string; fieldName?: string; originalFieldName?: string; - newValue?: any; + newValue?: string | number | boolean | object; warning?: string; }[] = []; diff --git a/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts b/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts index 3c8b49f33..dcb708450 100644 --- a/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts +++ b/src/client/views/nodes/chatbot/utils/AgentDocumentManager.ts @@ -1,7 +1,5 @@ import { action, computed, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; -import { v4 as uuidv4 } from 'uuid'; -import { Doc, StrListCast } from '../../../../../fields/Doc'; +import { Doc, FieldResult, StrListCast } from '../../../../../fields/Doc'; import { DocData } from '../../../../../fields/DocSymbols'; import { Id } from '../../../../../fields/FieldSymbols'; import { List } from '../../../../../fields/List'; @@ -30,7 +28,7 @@ export class AgentDocumentManager { @observable private documentsById: ObservableMap<string, AgentDocument>; private chatBox: ChatBox; private chatBoxDocument: Doc | null = null; - private fieldMetadata: Record<string, any> = {}; + private fieldMetadata: Record<string, any> = {}; // bcz: CHANGE any to a proper type! @observable private simplifiedChunks: ObservableMap<string, SimplifiedChunk>; /** @@ -103,6 +101,7 @@ export class AgentDocumentManager { for (const [fieldName, fieldInfo] of documentOptionsEntries) { // Extract field information const fieldData: Record<string, any> = { + // bcz: CHANGE any to a proper type! name: fieldName, withoutUnderscore: fieldName.startsWith('_') ? fieldName.substring(1) : fieldName, description: '', @@ -223,6 +222,7 @@ export class AgentDocumentManager { const dataDoc = agentDoc.dataDoc; const metadata: Record<string, any> = { + // bcz: CHANGE any to a proper type! id: layoutDoc[Id] || dataDoc[Id] || '', title: layoutDoc.title || '', type: layoutDoc.type || '', @@ -235,7 +235,7 @@ export class AgentDocumentManager { // Process all known field definitions Object.keys(this.fieldMetadata).forEach(fieldName => { - const fieldDef = this.fieldMetadata[fieldName]; + // const fieldDef = this.fieldMetadata[fieldName]; const strippedName = fieldName.startsWith('_') ? fieldName.substring(1) : fieldName; // Check if field exists on layout document @@ -307,7 +307,7 @@ export class AgentDocumentManager { * @param value The field value to format * @returns A JSON-friendly representation of the field value */ - private formatFieldValue(value: any): any { + private formatFieldValue(value: FieldResult | undefined) { if (value === undefined || value === null) { return null; } @@ -330,12 +330,12 @@ export class AgentDocumentManager { if (rtfObj.doc && rtfObj.doc.content) { // Recursively extract text from the content let plainText = ''; - const extractText = (node: any) => { + const extractText = (node: { text: string; content?: unknown[] }) => { if (node.text) { plainText += node.text; } if (node.content && Array.isArray(node.content)) { - node.content.forEach((child: any) => extractText(child)); + node.content.forEach(child => extractText(child as { text: string; content?: unknown[] })); } }; @@ -351,7 +351,7 @@ export class AgentDocumentManager { }; } } - } catch (e) { + } catch { // If parsing fails, just treat as a regular string } } @@ -366,7 +366,7 @@ export class AgentDocumentManager { try { // Try to convert to JSON string return JSON.stringify(value); - } catch (e) { + } catch { return '[Complex Object]'; } } @@ -381,26 +381,24 @@ export class AgentDocumentManager { * @param fieldValue The string value to convert * @returns The converted value with the appropriate type */ - private convertFieldValue(fieldName: string, fieldValue: any): any { + private convertFieldValue(fieldName: string, fieldValueIn: string | number | boolean): FieldResult | undefined { // If fieldValue is already a number or boolean, we don't need to convert it from string - if (typeof fieldValue === 'number' || typeof fieldValue === 'boolean') { - return fieldValue; + if (typeof fieldValueIn === 'number' || typeof fieldValueIn === 'boolean') { + return fieldValueIn; } // If fieldValue is a string "true" or "false", convert to boolean - if (typeof fieldValue === 'string') { - if (fieldValue.toLowerCase() === 'true') { + if (typeof fieldValueIn === 'string') { + if (fieldValueIn.toLowerCase() === 'true') { return true; } - if (fieldValue.toLowerCase() === 'false') { + if (fieldValueIn.toLowerCase() === 'false') { return false; } } - // If fieldValue is not a string (and not a number or boolean), convert it to string - if (typeof fieldValue !== 'string') { - fieldValue = String(fieldValue); - } + // coerce fieldvValue to a string + const fieldValue = typeof fieldValueIn !== 'string' ? String(fieldValueIn) : fieldValueIn; // Special handling for text field - convert to proper RichTextField format if (fieldName === 'text') { @@ -408,7 +406,7 @@ export class AgentDocumentManager { // Check if it's already a valid JSON RichTextField JSON.parse(fieldValue); return fieldValue; - } catch (e) { + } catch { // It's a plain text string, so convert it to RichTextField format const rtf = { doc: { @@ -462,21 +460,21 @@ export class AgentDocumentManager { // Try to convert to date (stored as number timestamp) try { return new Date(fieldValue).getTime(); - } catch (e) { + } catch { return fieldValue; } } else if (fieldType.includes('list') || fieldType.includes('array')) { // Try to parse as JSON array try { - return JSON.parse(fieldValue); - } catch (e) { + return JSON.parse(fieldValue) as FieldResult; // bcz: this needs to be typed properly. Dash fields can't accept a generic 'objext' + } catch { return fieldValue; } } else if (fieldType === 'json' || fieldType === 'object') { // Try to parse as JSON object try { - return JSON.parse(fieldValue); - } catch (e) { + return JSON.parse(fieldValue) as FieldResult; // bcz: this needs to be typed properly. Dash fields can't accept a generic 'objext' + } catch { return fieldValue; } } @@ -492,6 +490,7 @@ export class AgentDocumentManager { public getAllFieldMetadata() { // Start with our already populated fieldMetadata from the DocumentOptions class const result: Record<string, any> = { + // bcz: CHANGE any to a proper type! fieldCount: Object.keys(this.fieldMetadata).length, fields: {}, fieldsByType: { @@ -526,6 +525,7 @@ export class AgentDocumentManager { // Create structured field metadata const fieldData: Record<string, any> = { + // bcz: CHANGE any to a proper type! name: fieldName, displayName: strippedName, description: fieldInfo.description || '', @@ -618,12 +618,12 @@ export class AgentDocumentManager { message: string; fieldName?: string; originalFieldName?: string; - newValue?: any; + newValue?: string | number | boolean | object; warning?: string; } { // Normalize field name (handle with/without underscore) let normalizedFieldName = fieldName.startsWith('_') ? fieldName : fieldName; - const strippedFieldName = fieldName.startsWith('_') ? fieldName.substring(1) : fieldName; + // const strippedFieldName = fieldName.startsWith('_') ? fieldName.substring(1) : fieldName; // Handle common field name aliases (width → _width, height → _height) // Many document fields use '_' prefix for layout properties @@ -690,7 +690,7 @@ export class AgentDocumentManager { } // Set the field value on the target document - targetDoc[normalizedFieldName] = convertedValue; + targetDoc[normalizedFieldName] = convertedValue; // bcz: converteValue needs to be typed properly. Dash fields can't accept a generic 'objext' return { success: true, @@ -712,19 +712,19 @@ export class AgentDocumentManager { * @param documentId Optional ID of a specific document to get metadata for * @returns Document metadata or metadata for all documents */ - public getDocumentMetadata(documentId?: string): any { + public getDocumentMetadata(documentId?: string) { if (documentId) { console.log(`Returning document metadata for docID, ${documentId}:`, this.extractDocumentMetadata(documentId)); return this.extractDocumentMetadata(documentId); } else { // Get metadata for all documents - const documentsMetadata: Record<string, Record<string, any>> = {}; - for (const documentId of this.documentsById.keys()) { - const metadata = this.extractDocumentMetadata(documentId); + const documentsMetadata: Record<string, Record<string, any>> = {}; // bcz: CHANGE any to a proper type! + for (const docid of this.documentsById.keys()) { + const metadata = this.extractDocumentMetadata(docid); if (metadata) { - documentsMetadata[documentId] = metadata; + documentsMetadata[docid] = metadata; } else { - console.warn(`No metadata found for document with ID: ${documentId}`); + console.warn(`No metadata found for document with ID: ${docid}`); } } return { @@ -842,7 +842,7 @@ export class AgentDocumentManager { * @returns The ID of the created document */ - public async createDocInDash(docType: string, data: string, options?: any): Promise<string> { + public async createDocInDash(docType: string, data: string, options?: DocumentOptions): Promise<string> { // Validate doc_type if (!this.isValidDocType(docType)) { throw new Error(`Invalid document type: ${docType}`); @@ -1054,13 +1054,13 @@ export class AgentDocumentManager { endPage: chunk.metadata.end_page, location: chunk.metadata.location, } as SimplifiedChunk); - } else if (docType === 'csv') { + } else if (docType === 'csv' && 'row_start' in chunk.metadata && 'row_end' in chunk.metadata && 'col_start' in chunk.metadata && 'col_end' in chunk.metadata) { simplifiedChunks.push({ ...baseChunk, - rowStart: (chunk.metadata as any).row_start, - rowEnd: (chunk.metadata as any).row_end, - colStart: (chunk.metadata as any).col_start, - colEnd: (chunk.metadata as any).col_end, + rowStart: chunk.metadata.row_start, + rowEnd: chunk.metadata.row_end, + colStart: chunk.metadata.col_start, + colEnd: chunk.metadata.col_end, } as SimplifiedChunk); } else { // Default for other document types @@ -1077,7 +1077,7 @@ export class AgentDocumentManager { * @returns The simplified chunk if found, undefined otherwise */ @action - public getSimplifiedChunkById(chunkId: string): any | undefined { + public getSimplifiedChunkById(chunkId: string) { return { foundChunk: this.simplifiedChunks.get(chunkId), doc: this.getDocument(this.simplifiedChunks.get(chunkId)?.doc_id || chunkId), dataDoc: this.getDataDocument(this.simplifiedChunks.get(chunkId)?.doc_id || chunkId) }; } @@ -1098,7 +1098,7 @@ export class AgentDocumentManager { * @param doc The document containing original media segments * @returns Array of media segments or empty array if none exist */ - public getOriginalSegments(doc: Doc): any[] { + public getOriginalSegments(doc: Doc): { text: string; index: string; start: number }[] { if (!doc || !doc.original_segments) { return []; } |
