diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DictationManager.ts | 5 | ||||
-rw-r--r-- | src/client/views/DictationButton.tsx | 57 | ||||
-rw-r--r-- | src/client/views/DictationOverlay.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx | 31 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 53 |
5 files changed, 101 insertions, 46 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 831afe538..897366757 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -71,7 +71,6 @@ export namespace DictationManager { let current: string | undefined; let sessionResults: string[] = []; - // eslint-disable-next-line new-cap const recognizer: Opt<SpeechRecognition> = webkitSpeechRecognition ? new webkitSpeechRecognition() : undefined; export type InterimResultHandler = (results: string) => void; @@ -257,7 +256,6 @@ export namespace DictationManager { if (entry) { let success = false; const { restrictTo } = entry; - // eslint-disable-next-line no-restricted-syntax for (const target of targets) { if (!restrictTo || validate(target, restrictTo)) { // eslint-disable-next-line no-await-in-loop @@ -268,7 +266,6 @@ export namespace DictationManager { return success; } - // eslint-disable-next-line no-restricted-syntax for (const depEntry of Dependent) { const regex = depEntry.expression; const matches = regex.exec(phrase); @@ -276,7 +273,6 @@ export namespace DictationManager { if (matches !== null) { let success = false; const { restrictTo } = depEntry; - // eslint-disable-next-line no-restricted-syntax for (const target of targets) { if (!restrictTo || validate(target, restrictTo)) { // eslint-disable-next-line no-await-in-loop @@ -307,7 +303,6 @@ export namespace DictationManager { }; const validate = (target: DocumentView, types: DocumentType[]) => { - // eslint-disable-next-line no-restricted-syntax for (const type of types) { if (tryCast(target, type)) { return true; diff --git a/src/client/views/DictationButton.tsx b/src/client/views/DictationButton.tsx new file mode 100644 index 000000000..0ce586df4 --- /dev/null +++ b/src/client/views/DictationButton.tsx @@ -0,0 +1,57 @@ +import { IconButton, Type } from '@dash/components'; +import { action, makeObservable, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { BiMicrophone } from 'react-icons/bi'; +import { DictationManager } from '../util/DictationManager'; +import { SnappingManager } from '../util/SnappingManager'; + +export interface DictationButtonProps { + setInput: (val: string) => void; + inputRef?: HTMLInputElement | null | undefined; +} +@observer +export class DictationButton extends React.Component<DictationButtonProps> { + @observable private _isRecording = false; + constructor(props: DictationButtonProps) { + super(props); + makeObservable(this); + } + + stopDictation = action(() => { + this._isRecording = false; + DictationManager.Controls.stop(); + }); + + render() { + return ( + <IconButton + type={Type.TERT} + color={this._isRecording ? '#2bcaff' : SnappingManager.userVariantColor} + tooltip="Record" + icon={<BiMicrophone size="16px" />} + onClick={action(() => { + if (!this._isRecording) { + this._isRecording = true; + DictationManager.Controls.listen({ + interimHandler: (value: string) => { + this.props.setInput(value); + if (this.props.inputRef) { + this.props.inputRef.focus(); + this.props.inputRef.scrollLeft = 1000000; + } + }, + continuous: { indefinite: false }, + }).then(results => { + if (results && [DictationManager.Controls.Infringed].includes(results)) { + DictationManager.Controls.stop(); + } + }); + } else { + this.stopDictation(); + } + })} + /> + ); + } +} diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx index e33049d3b..66831ec7f 100644 --- a/src/client/views/DictationOverlay.tsx +++ b/src/client/views/DictationOverlay.tsx @@ -14,7 +14,6 @@ export class DictationOverlay extends React.Component { @observable private _dictationDisplayState = false; @observable private _dictationListeningState: DictationManager.Controls.ListeningUIStatus = false; - // eslint-disable-next-line react/no-unused-class-component-methods public hasActiveModal = false; constructor(props: object) { diff --git a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx index 810e1793b..4453fe140 100644 --- a/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx +++ b/src/client/views/nodes/chatbot/chatboxcomponents/ChatBox.tsx @@ -22,17 +22,18 @@ import { CollectionViewType, DocumentType } from '../../../../documents/Document import { Docs, DocumentOptions } from '../../../../documents/Documents'; import { DocumentManager } from '../../../../util/DocumentManager'; import { LinkManager } from '../../../../util/LinkManager'; +import { DictationButton } from '../../../DictationButton'; import { ViewBoxAnnotatableComponent } from '../../../DocComponent'; import { DocumentView } from '../../DocumentView'; import { FieldView, FieldViewProps } from '../../FieldView'; import { PDFBox } from '../../PDFBox'; import { Agent } from '../agentsystem/Agent'; +import { supportedDocumentTypes } from '../tools/CreateDocumentTool'; import { ASSISTANT_ROLE, AssistantMessage, CHUNK_TYPE, Citation, ProcessingInfo, SimplifiedChunk, TEXT_TYPE } from '../types/types'; import { Vectorstore } from '../vectorstore/Vectorstore'; import './ChatBox.scss'; import MessageComponentBox from './MessageComponent'; import { ProgressBar } from './ProgressBar'; -import { supportedDocumentTypes } from '../tools/CreateDocumentTool'; dotenv.config(); @@ -64,6 +65,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { private vectorstore: Vectorstore; private agent: Agent; private messagesRef: React.RefObject<HTMLDivElement>; + private _textInputRef: HTMLInputElement | undefined | null; /** * Static method that returns the layout string for the field. @@ -73,6 +75,10 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { return FieldView.LayoutString(ChatBox, fieldKey); } + setChatInput = action((input: string) => { + this._inputValue = input; + }); + /** * Constructor initializes the component, sets up OpenAI, vector store, and agent instances, * and observes changes in the chat history to save the state in dataDoc. @@ -820,6 +826,7 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this._inputValue = question; }; + _dictation: DictationButton | null = null; /** * Renders the chat interface, including the message list, input field, and other UI elements. */ @@ -847,8 +854,19 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { </div> <form onSubmit={this.askGPT} className="chat-input"> - <input type="text" name="messageInput" autoComplete="off" placeholder="Type your message here..." value={this._inputValue} onChange={action(e => (this._inputValue = e.target.value))} disabled={this._isLoading} /> - <button className="submit-button" type="submit" disabled={this._isLoading || !this._inputValue.trim()}> + <input + ref={r => { + this._textInputRef = r; + }} + type="text" + name="messageInput" + autoComplete="off" + placeholder="Type your message here..." + value={this._inputValue} + onChange={action(e => (this._inputValue = e.target.value))} + disabled={this._isLoading} + /> + <button className="submit-button" onClick={() => this._dictation?.stopDictation()} type="submit" disabled={this._isLoading || !this._inputValue.trim()}> {this._isLoading ? ( <div className="spinner"></div> ) : ( @@ -858,6 +876,13 @@ export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { </svg> )} </button> + <DictationButton + ref={r => { + this._dictation = r; + }} + setInput={this.setChatInput} + inputRef={this._textInputRef} + /> </form> {/* Popup for citation */} {this._citationPopup.visible && ( diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3ce4dc6cb..b98a7f96e 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -7,7 +7,6 @@ import { IReactionDisposer, ObservableSet, action, computed, makeObservable, obs import { observer } from 'mobx-react'; import * as React from 'react'; import { AiOutlineSend } from 'react-icons/ai'; -import { BiMicrophone } from 'react-icons/bi'; import { FaArrowDown, FaArrowLeft, FaArrowRight, FaArrowUp } from 'react-icons/fa'; import ReactLoading from 'react-loading'; import ReactTextareaAutosize from 'react-textarea-autosize'; @@ -26,12 +25,12 @@ import { DocServer } from '../../../DocServer'; import { getSlideTransitionSuggestions, gptSlideProperties, gptTrailSlideCustomization } from '../../../apis/gpt/PresCustomization'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DictationManager } from '../../../util/DictationManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { SerializationHelper } from '../../../util/SerializationHelper'; import { SnappingManager } from '../../../util/SnappingManager'; import { UndoManager, undoBatch, undoable } from '../../../util/UndoManager'; +import { DictationButton } from '../../DictationButton'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { pinDataTypes as dataTypes } from '../../PinFuncs'; import { CollectionView } from '../../collections/CollectionView'; @@ -79,6 +78,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { _keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed. Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation. _unmounting = false; // flag that view is unmounting used to block RemFromMap from deleting things _presTimer: NodeJS.Timeout | undefined; + _animationDictation: DictationButton | null = null; + _slideDictation: DictationButton | null = null; // eslint-disable-next-line no-use-before-define @observable public static Instance: PresBox; @@ -108,7 +109,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @observable _chatActive: boolean = false; @observable _animationChat: string = ''; @observable _chatInput: string = ''; - @observable _isRecording: boolean = false; @observable _isLoading: boolean = false; @observable generatedAnimations: AnimationSettings[] = [ @@ -147,7 +147,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { setChatInput = action((input: string) => { this._chatInput = input; }); // prettier-ignore setAnimationChat = action((input: string) => { this._animationChat = input; }); // prettier-ignore setIsLoading = action((input?: boolean) => { this._isLoading = !!input; }); // prettier-ignore - setIsRecording = action((input: boolean) => { this._isRecording = input; }); // prettier-ignore setShowAIGalleryVisibilty = action((visible: boolean) => { this._showAIGallery = visible; }); // prettier-ignore setBezierControlPoints = action((newPoints: { p1: number[]; p2: number[] }) => { this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`); @@ -280,24 +279,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } }; - // Recording for GPT customization - recordDictation = () => { - this.setIsRecording(true); - this.setChatInput(''); - DictationManager.Controls.listen({ - interimHandler: this.setDictationContent, - continuous: { indefinite: false }, - }).then(results => { - if (results && [DictationManager.Controls.Infringed].includes(results)) { - DictationManager.Controls.stop(); - } - }); - }; - stopDictation = () => { - this.setIsRecording(false); - DictationManager.Controls.stop(); - }; - setDictationContent = (value: string) => this.setChatInput(value); customizeAnimations = action(() => { @@ -310,7 +291,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { customizeWithGPT = action((input: string) => { // const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect'; - this.setIsRecording(false); this.setIsLoading(true); const slideDefaults: { [key: string]: FieldResult } = { presentation_transition: 500, config_zoom: 1 }; const currSlideProperties = gptSlideProperties.reduce( @@ -1785,7 +1765,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this.setAnimationChat(e.target.value); }} onKeyDown={e => { - this.stopDictation(); + this._animationDictation?.stopDictation(); e.stopPropagation(); }} /> @@ -1799,6 +1779,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { color={SnappingManager.userVariantColor} onClick={this.customizeAnimations} /> + <DictationButton + ref={r => { + this._animationDictation = r; + }} + setInput={this.setAnimationChat} + /> </div> <div style={{ alignItems: 'center' }}> Click a box to use the effect. @@ -1878,22 +1864,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this.setChatInput(e.target.value); }} onKeyDown={e => { - this.stopDictation(); + this._slideDictation?.stopDictation(); e.stopPropagation(); }} /> - <IconButton - type={Type.TERT} - color={this._isRecording ? '#2bcaff' : SnappingManager.userVariantColor} - tooltip="Record" - icon={<BiMicrophone size="16px" />} - onClick={() => { - if (!this._isRecording) { - this.recordDictation(); - } else { - this.stopDictation(); - } + <DictationButton + ref={r => { + this._slideDictation = r; }} + setInput={this.setChatInput} /> </div> <Button @@ -1904,7 +1883,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { iconPlacement="right" color={SnappingManager.userVariantColor} onClick={() => { - this.stopDictation(); + this._slideDictation?.stopDictation(); this.customizeWithGPT(this._chatInput); }} /> |