From 86c666427ff8b9d516450a150af641570e00f2d2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 1 Jul 2025 13:17:40 -0400 Subject: reverted chat send to use dash component Button, and dictation to use Toggle. Reverted Dropdown to use trigger of CLICK (hover doesn't work well). got rid of currentuserutil button references in infoUI & replaced with info-specific button descriptions. fixed up a bunch of lint/typing errors --- src/client/views/DictationButton.tsx | 33 +- .../CollectionFreeFormInfoState.tsx | 53 +- .../CollectionFreeFormInfoUI.tsx | 536 +++++++++++---------- src/client/views/nodes/ImageBox.tsx | 1 - .../nodes/chatbot/chatboxcomponents/ChatBox.scss | 1 - .../nodes/chatbot/chatboxcomponents/ChatBox.tsx | 53 +- .../nodes/chatbot/tools/DocumentMetadataTool.ts | 15 +- .../nodes/chatbot/utils/AgentDocumentManager.ts | 86 ++-- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 27 +- 9 files changed, 425 insertions(+), 380 deletions(-) (limited to 'src') diff --git a/src/client/views/DictationButton.tsx b/src/client/views/DictationButton.tsx index fc3165f67..882e857c5 100644 --- a/src/client/views/DictationButton.tsx +++ b/src/client/views/DictationButton.tsx @@ -1,9 +1,10 @@ -import { makeObservable, observable, action } from 'mobx'; +import { Toggle, ToggleType } from '@dash/components'; +import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import './DictationButton.scss'; import { DictationManager } from '../util/DictationManager'; import { SnappingManager } from '../util/SnappingManager'; +import './DictationButton.scss'; export interface DictationButtonProps { setInput: (val: string) => void; @@ -26,9 +27,21 @@ export class DictationButton extends React.Component { render() { return ( - + })} + /> ); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index 8ed3e8e30..48cab9c7b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -3,16 +3,24 @@ import { IReactionDisposer, action, makeObservable, observable, reaction } from import { observer } from 'mobx-react'; import * as React from 'react'; import { SettingsManager } from '../../../util/SettingsManager'; +import { ButtonType } from '../../nodes/FontIconBox/FontIconBox'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import './CollectionFreeFormView.scss'; -import { Button } from '../../../util/CurrentUserUtils'; +export interface InfoButton { + targetState?: infoState; + // DocumentOptions fields a button can set + title?: string; + toolTip?: string; + btnType?: ButtonType; + // fields that do not correspond to DocumentOption fields + scripts?: { script?: string; onClick?: string; onDoubleClick?: string }; +} /** * An Fsa Arc. The first array element is a test condition function that will be observed. * The second array element is a function that will be invoked when the first test function * returns a truthy value */ -// eslint-disable-next-line no-use-before-define export type infoArc = [() => unknown, (res?: unknown) => infoState]; export const StateMessage = Symbol('StateMessage'); @@ -22,19 +30,13 @@ export const StateMessageButton = Symbol('StateMessageButton'); export class infoState { [StateMessage]: string = ''; [StateMessageGIF]?: string = ''; - [StateMessageButton]?: Button[]; + [StateMessageButton]?: InfoButton[]; [StateEntryFunc]?: () => unknown; [key: string]: infoArc; - constructor( - message: string, - arcs: { [key: string]: infoArc }, - messageGif?: string, - buttons?: Button[], - entryFunc?: () => unknown - ) { + constructor(message: string, arcs?: { [key: string]: infoArc }, messageGif?: string, buttons?: InfoButton[], entryFunc?: () => unknown) { this[StateMessage] = message; - Object.assign(this, arcs); + Object.assign(this, arcs ?? {}); this[StateMessageGIF] = messageGif; this[StateEntryFunc] = entryFunc; this[StateMessageButton] = buttons; @@ -53,9 +55,9 @@ export class infoState { */ export function InfoState( msg: string, // - arcs: { [key: string]: infoArc }, + arcs?: { [key: string]: infoArc }, gif?: string, - button?: Button[], + button?: InfoButton[], entryFunc?: () => unknown ) { return new infoState(msg, arcs, gif, button, entryFunc); @@ -63,7 +65,7 @@ export function InfoState( export interface CollectionFreeFormInfoStateProps { infoState: infoState; - next: (state: infoState) => unknown; // Ensure it's properly defined + next: (state: infoState) => unknown; // Ensure it's properly defined close: () => void; } @@ -114,8 +116,8 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent

@@ -129,11 +131,11 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent

- +
state message gif
- + {/* Render the buttons for skipping */}
{buttons?.map((button, index) => ( @@ -142,23 +144,16 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent { - console.log("Attempting transition to:", button.targetState); + console.log('Attempting transition to:', button.targetState); this.props.next(button.targetState as infoState); // ✅ Use the prop instead - })}> + })}> {button.title} ))}
- +
- this.props.close())} - /> + this.props.close())} />
); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index efc22f523..147c900be 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -1,17 +1,13 @@ -import { makeObservable, observable, runInAction } from 'mobx'; +import { action, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc } from '../../../../fields/Doc'; -import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { CollectionFreeFormInfoState, infoState } from './CollectionFreeFormInfoState'; -import { Button } from '../../../util/CurrentUserUtils'; -import { InfoState } from './CollectionFreeFormInfoState'; -import { DocButtonState } from '../../nodes/DocumentLinksButton'; -import { InkTool } from '../../../../fields/InkField'; -import { DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { CollectionFreeFormView } from '.'; -import { DocListCast } from '../../../../fields/Doc'; +import { Doc, DocListCast } from '../../../../fields/Doc'; +import { InkTool } from '../../../../fields/InkField'; +import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { ButtonType } from '../../nodes/FontIconBox/FontIconBox'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; +import { CollectionFreeFormInfoState, InfoButton, infoState, InfoState } from './CollectionFreeFormInfoState'; export interface CollectionFreeFormInfoUIProps { Document: Doc; @@ -22,28 +18,19 @@ export interface CollectionFreeFormInfoUIProps { @observer export class CollectionFreeFormInfoUI extends ObservableReactComponent { - _originalBackground: string | undefined; - private tutorialStates: { [key: string]: infoState } = {}; + private _originalBackground: string | undefined; + private _tutorialStates: { [key: string]: infoState } = {}; public static Init() { - CollectionFreeFormView.SetInfoUICreator( - (doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => - - ); + CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => ); } constructor(props: CollectionFreeFormInfoUIProps) { super(props); makeObservable(this); - this.tutorialStates = {}; // Initialize an empty object + this._tutorialStates = {}; // Initialize an empty object this.currState = this.setupStates(); // Call setupStates() here } - @observable _currState: infoState | undefined = undefined; @observable _nextState: infoState | undefined = undefined; // Track next state @@ -51,52 +38,47 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent { - this._currState = val; - }); + runInAction(() => (this._currState = val)); } - componentWillUnmount(): void { + componentWillUnmount() { this._props.Document.backgroundColor = this._originalBackground; } - skipToState = (newState: infoState) => { - runInAction(() => { - if (!this._currState) { - this._currState = newState; // Assign directly if undefined - } else { - this._currState = newState; - } - }); - }; + skipToState = action((newState: infoState) => (this._currState = newState)); createNextButton = (newState: ReturnType) => { return { - title: "Next", - toolTip: "Next", + title: 'Next', + toolTip: 'Next', btnType: ButtonType.ClickButton, scripts: { - onClick: `this.skipToState(${newState})` + onClick: `this.skipToState(${newState})`, }, - targetState: newState + targetState: newState, }; }; setupStates = () => { let docCounter = this._props.childDocs().length; - let lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] + let lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1]; let linkCounter = Doc.Links(lastDocCreated)?.length; - let presentationCounter = DocListCast(Doc.ActivePresentation?.data).length + let presentationCounter = DocListCast(Doc.ActivePresentation?.data).length; this._originalBackground = this._props.Document.backgroundColor as string; - - this.tutorialStates.multipleDocs = InfoState("Let's create a new link! Click the link icon on one document and connect it to another.", { - // eslint-disable-next-line no-use-before-define - linkStarted: [() => DocumentLinksButton.StartLink, () => { - linkCounter = Doc.Links(lastDocCreated).length - // eslint-disable-next-line no-use-before-define - return startedLink}], + + this._tutorialStates.multipleDocs = InfoState( + "Let's create a new link! Click the link icon on one document and connect it to another.", + { + linkStarted: [ + () => DocumentLinksButton.StartLink, + () => { + linkCounter = Doc.Links(lastDocCreated).length; + // eslint-disable-next-line no-use-before-define + return startedLink; + }, + ], // docCreated: [() => this._props.childDocs().length > docCounter, () => { // docCounter += 1 // lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] @@ -106,238 +88,306 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent DocListCast(Doc.ActivePresentation?.data).length > presentationCounter, () => { - presentationCounter++ - // eslint-disable-next-line no-use-before-define - return pinnedDoc}], + this._tutorialStates.presentDocs = InfoState( + "Select a document then click the 'pin' button in the top left to create your presentation.", + { + docPinned: [ + () => DocListCast(Doc.ActivePresentation?.data).length > presentationCounter, + () => { + presentationCounter++; + // eslint-disable-next-line no-use-before-define + return pinnedDoc; + }, + ], }, - 'pin-explanation.gif'); - - this.tutorialStates.nestedCollections = InfoState("Want to learn how to create a nested collection? Click the : button and add a 'collection' doc", { - // eslint-disable-next-line no-use-before-define - docCreated: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] - // eslint-disable-next-line no-use-before-define - return marqueeSelection}] - }, - 'dash-nested-collection.gif') - - - this.tutorialStates.makePresentation = InfoState("Add a new document to create a presentation!", { - docCreated: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] - // eslint-disable-next-line no-use-before-define - return this.tutorialStates.presentDocs}], - }); - - const skipToLinksButton: Button = { - title: "Links Tutorial", - toolTip: "Skip", - btnType: ButtonType.ClickButton, - scripts: { - onClick: "this.skipToState(this.tutorialStates.multipleDocs)" + 'pin-explanation.gif' + ); + + this._tutorialStates.nestedCollections = InfoState( + "Want to learn how to create a nested collection? Click the : button and add a 'collection' doc", + { + docCreated: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1]; + // eslint-disable-next-line no-use-before-define + return marqueeSelection; + }, + ], }, - targetState: this.tutorialStates.multipleDocs - }; + 'dash-nested-collection.gif' + ); + + this._tutorialStates.makePresentation = InfoState('Add a new document to create a presentation!', { + docCreated: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1]; + return this._tutorialStates.presentDocs; + }, + ], + }); - const skipToPinsButton: Button = { - title: "Pins Tutorial", - toolTip: "Skip", + const skipToLinksButton: InfoButton = { + title: 'Links Tutorial', + toolTip: 'Skip', btnType: ButtonType.ClickButton, scripts: { - onClick: "this.skipToState(this.tutorialStates.makePresentation)" + onClick: 'this.skipToState(this.tutorialStates.multipleDocs)', }, - targetState: this.tutorialStates.makePresentation + targetState: this._tutorialStates.multipleDocs, }; - const skipToPresentationButton: Button = { - title: "Collections Tutorial", - toolTip: "Skip", + const skipToPinsButton: InfoButton = { + title: 'Pins Tutorial', + toolTip: 'Skip', btnType: ButtonType.ClickButton, scripts: { - onClick: "this.skipToState(this.tutorialStates.nestedCollections)" + onClick: 'this.skipToState(this.tutorialStates.makePresentation)', }, - targetState: this.tutorialStates.nestedCollections + targetState: this._tutorialStates.makePresentation, }; - const ending = InfoState("If you have any more questions, feel free to ask Dash's AI Bot!") + // const skipToPresentationButton: Button = { + // title: "Collections Tutorial", + // toolTip: "Skip", + // btnType: ButtonType.ClickButton, + // scripts: { + // onClick: "this.skipToState(this.tutorialStates.nestedCollections)" + // }, + // targetState: this.tutorialStates.nestedCollections + // }; + + const ending = InfoState("If you have any more questions, feel free to ask Dash's AI Bot!"); // Traditional tutorial - const completed = InfoState( - "Eager to learn more? Click the ? icon in the top right corner to read our full documentation.", - { docRemoved: [() => this._props.childDocs().length === 1, () => this.tutorialStates.start] }, - 'documentation.png', - ); + const completed = InfoState('Eager to learn more? Click the ? icon in the top right corner to read our full documentation.', { docRemoved: [() => this._props.childDocs().length === 1, () => this._tutorialStates.start] }, 'documentation.png'); const penMode = InfoState("You're in pen mode! Click and drag to draw your first masterpiece, then click the Ink button once you're done.", { activePen: [() => Doc.ActiveTool !== InkTool.Ink, () => completed], }); - + const briefArtisticFeature = InfoState("Finally, want to explore the art feature of Dash? Click the 'Ink' button on the hotbar then click the pen button.", { penModeActivated: [() => Doc.ActiveTool === InkTool.Ink, () => penMode], }); - - const activatePresentation = InfoState("Lastly, click the linked node and start the presentation!", { - presentation: [() => Doc.ActivePresentation?.presentation_status === "auto", () => briefArtisticFeature], + + const activatePresentation = InfoState('Lastly, click the linked node and start the presentation!', { + presentation: [() => Doc.ActivePresentation?.presentation_status === 'auto', () => briefArtisticFeature], }); - - const deletePresentation = InfoState("Cool! Click 'setOnClick to follow primary link' for your non-presentation doc and try deleting the presentation.", { - docRemoved: [() => this._props.childDocs().length < docCounter, () => { - docCounter -= 1 - return activatePresentation}], - }, - 'onclick-node.gif'); - - const trailedPresentation = InfoState("Try linking your presentation to the last doc you created (now highlighted).", { - linkAdd: [() => Doc.Links(lastDocCreated)?.length > linkCounter, () => { - linkCounter += 1 - return deletePresentation - }], - docAdded: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - // Last doc that is not the presentation - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 2] - linkCounter = Doc.Links(lastDocCreated)?.length - return deletePresentation}] - }, - 'link-presentation.gif'); - - const pinnedPresentation = InfoState("Want to see something cool? Zoom out, click the trail button on the presentation, and drag it inside the canvas.", { - docAdded: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - // Last doc that is not the presentation - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 2] - Doc.HighlightDoc(lastDocCreated) - linkCounter = Doc.Links(lastDocCreated)?.length - return trailedPresentation}], - }, - 'dash-trail-explanation.gif'); - - const pinnedDoc2 = InfoState("You pinned another doc. Press autoplay to the right to show your presentation!", { - autoPresentation: [() => Doc.ActivePresentation?.presentation_status === "auto", () => pinnedPresentation], + + const deletePresentation = InfoState( + "Cool! Click 'setOnClick to follow primary link' for your non-presentation doc and try deleting the presentation.", + { + docRemoved: [ + () => this._props.childDocs().length < docCounter, + () => { + docCounter -= 1; + return activatePresentation; + }, + ], + }, + 'onclick-node.gif' + ); + + const trailedPresentation = InfoState( + 'Try linking your presentation to the last doc you created (now highlighted).', + { + linkAdd: [ + () => Doc.Links(lastDocCreated)?.length > linkCounter, + () => { + linkCounter += 1; + return deletePresentation; + }, + ], + docAdded: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + // Last doc that is not the presentation + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 2]; + linkCounter = Doc.Links(lastDocCreated)?.length; + return deletePresentation; + }, + ], + }, + 'link-presentation.gif' + ); + + const pinnedPresentation = InfoState( + 'Want to see something cool? Zoom out, click the trail button on the presentation, and drag it inside the canvas.', + { + docAdded: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + // Last doc that is not the presentation + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 2]; + Doc.HighlightDoc(lastDocCreated); + linkCounter = Doc.Links(lastDocCreated)?.length; + return trailedPresentation; + }, + ], + }, + 'dash-trail-explanation.gif' + ); + + const pinnedDoc2 = InfoState('You pinned another doc. Press autoplay to the right to show your presentation!', { + autoPresentation: [() => Doc.ActivePresentation?.presentation_status === 'auto', () => pinnedPresentation], }); - const pinnedDoc = InfoState("You just pinned your doc. Pin another doc to add to the presentation!", { - // eslint-disable-next-line no-use-before-define - addedDoc: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] - return pinnedDoc}], - docPinned: [() => DocListCast(Doc.ActivePresentation?.data).length > presentationCounter, () => { - presentationCounter++ - // eslint-disable-next-line no-use-before-define - return pinnedDoc2}], + const pinnedDoc = InfoState('You just pinned your doc. Pin another doc to add to the presentation!', { + addedDoc: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1]; + return pinnedDoc; + }, + ], + docPinned: [ + () => DocListCast(Doc.ActivePresentation?.data).length > presentationCounter, + () => { + presentationCounter++; + return pinnedDoc2; + }, + ], }); - - const editLink = InfoState("Want to make your link visible? Click 'show link'.", { - docCreated: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] - return this.tutorialStates.makePresentation}], - }, - 'show-link.gif'); - - const madeLink = InfoState("You made your first link! You can view your links by selecting the blue dot.", { - linkViewed: [() => DocButtonState.Instance.LinkEditorDocView, () => { - docCounter = this._props.childDocs().length - return editLink}], - }, - 'dash-following-link.gif'); - - const startedLink = InfoState("Now click the highlighted link icon on your other document.", { - linkAdd: [() => Doc.Links(lastDocCreated)?.length > linkCounter, () => { - linkCounter += 1 - return madeLink - }] - }, - 'dash-create-link-board.gif'); - - this.tutorialStates.movedDoc = InfoState("Great moves! Try creating a second document.", { - docCreated: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] - return this.tutorialStates.multipleDocs}], - }, - 'dash-colon-menu.gif'); // prettier-ignore - - this.tutorialStates.start = InfoState("Welcome to Dash! Click anywhere and begin typing ':' to create your first document.", { - docCreated: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] - return this.tutorialStates.movedDoc}] - }, - undefined, - [skipToLinksButton, skipToPinsButton]) - + + const editLink = InfoState( + "Want to make your link visible? Click 'show link'.", + { + docCreated: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1]; + return this._tutorialStates.makePresentation; + }, + ], + }, + 'show-link.gif' + ); + + const madeLink = InfoState( + 'You made your first link! You can view your links by selecting the blue dot.', + { + linkViewed: [ + () => DocButtonState.Instance.LinkEditorDocView, + () => { + docCounter = this._props.childDocs().length; + return editLink; + }, + ], + }, + 'dash-following-link.gif' + ); + + const startedLink = InfoState( + 'Now click the highlighted link icon on your other document.', + { + linkAdd: [ + () => Doc.Links(lastDocCreated)?.length > linkCounter, + () => { + linkCounter += 1; + return madeLink; + }, + ], + }, + 'dash-create-link-board.gif' + ); + + this._tutorialStates.movedDoc = InfoState( + "Great moves! Try creating a second document.", + { + docCreated: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1 + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] + return this._tutorialStates.multipleDocs + } + ], + }, + 'dash-colon-menu.gif'); // prettier-ignore + + this._tutorialStates.start = InfoState( + "Welcome to Dash! Click anywhere and begin typing ':' to create your first document.", + { + docCreated: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1]; + return this._tutorialStates.movedDoc; + }, + ], + }, + undefined, + [skipToLinksButton, skipToPinsButton] + ); + // Information on created nested collections - const createdMarquee = InfoState("Next, right click and drag a square to create the collection", { - // eslint-disable-next-line no-use-before-define - marqueeMade: [() => this._props.childDocs().length < docCounter, () => { - docCounter -= 1 - return ending}], - }, - 'dash-create-collection-marquee.gif') - - const marqueeSelection = InfoState("Want an easier way to make a collection of docs? First add two docs you want to make a collection of", { - marqueeMade: [() => this._props.childDocs().length > docCounter, () => { - docCounter += 1 - lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] - return createdMarquee}] - }) + const createdMarquee = InfoState( + 'Next, right click and drag a square to create the collection', + { + marqueeMade: [ + () => this._props.childDocs().length < docCounter, + () => { + docCounter -= 1; + return ending; + }, + ], + }, + 'dash-create-collection-marquee.gif' + ); + + const marqueeSelection = InfoState('Want an easier way to make a collection of docs? First add two docs you want to make a collection of', { + marqueeMade: [ + () => this._props.childDocs().length > docCounter, + () => { + docCounter += 1; + lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1]; + return createdMarquee; + }, + ], + }); // Explanation of importing - const easierImport = InfoState("Or, for easier access, you can drag any of the accepted file types from your computer or a webpage and drop it into your dashboard. This includes images, videos, audio, pdfs, and more!", { - }, - 'dash-', - [this.createNextButton(ending)]) - - this.tutorialStates.importFile = InfoState("Want to learn how to import a file? Import using the import menu on the left hand side", { - }, - 'dash-import.gif', - [this.createNextButton(easierImport)]) + const easierImport = InfoState('Or, for easier access, you can drag any of the accepted file types from your computer or a webpage and drop it into your dashboard. This includes images, videos, audio, pdfs, and more!', {}, 'dash-', [ + this.createNextButton(ending), + ]); + + this._tutorialStates.importFile = InfoState('Want to learn how to import a file? Import using the import menu on the left hand side', {}, 'dash-import.gif', [this.createNextButton(easierImport)]); // Editing documents // Accessed by right-clicking anywhere on the target document or selecting the three bars menu at the bottom of the document chrome - const extraContentsOfDoc = InfoState("Lastly, all documents also have a context-sensitive toolbar. The toolbar contents vary depending on the document type.", { - }, - 'context-toolbar.png', - [this.createNextButton(ending)]) - - const contentsofDoc = InfoState("You can access the context of a doc through right-clicking anywhere on the target document or selecting the three bars menu at the bottom of the document chrome", { - }, - 'dash-context-menu.gif', - [this.createNextButton(extraContentsOfDoc)]) - - const propertiesofDoc = InfoState("You can also access the properties of a doc through the double arrows in the top right or the single arrow on the right edge of the screen", { - }, - 'dash-properties-pane.gif', - [this.createNextButton(contentsofDoc)]) - - this.tutorialStates.editingDocuments = InfoState("Want to learn how to edit a document? Either left or right click the document", { - }, - 'document-chrome.png', - [this.createNextButton(propertiesofDoc)]) - return this.tutorialStates.start - }; + const extraContentsOfDoc = InfoState('Lastly, all documents also have a context-sensitive toolbar. The toolbar contents vary depending on the document type.', {}, 'context-toolbar.png', [this.createNextButton(ending)]); + + const contentsofDoc = InfoState('You can access the context of a doc through right-clicking anywhere on the target document or selecting the three bars menu at the bottom of the document chrome', {}, 'dash-context-menu.gif', [ + this.createNextButton(extraContentsOfDoc), + ]); + const propertiesofDoc = InfoState('You can also access the properties of a doc through the double arrows in the top right or the single arrow on the right edge of the screen', {}, 'dash-properties-pane.gif', [ + this.createNextButton(contentsofDoc), + ]); + + this._tutorialStates.editingDocuments = InfoState('Want to learn how to edit a document? Either left or right click the document', {}, 'document-chrome.png', [this.createNextButton(propertiesofDoc)]); + return this._tutorialStates.start; + }; render() { - if (!this.currState) return null; - - return ( - ); } -} \ No newline at end of file +} 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() { })(); 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() { } 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() { 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() { // 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() { } // 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() { 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() { 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() { 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() { disabled={this._isLoading} /> - +