diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/DictationManager.ts | 60 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 65 |
3 files changed, 103 insertions, 25 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 488a146bf..781e5e465 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -45,7 +45,7 @@ export namespace DictationManager { export namespace Controls { - const infringe = "unable to process: dictation manager still involved in previous session"; + export const Infringed = "unable to process: dictation manager still involved in previous session"; const intraSession = ". "; const interSession = " ... "; @@ -69,30 +69,32 @@ export namespace DictationManager { delimiters: DelimiterArgs; interimHandler: InterimResultHandler; tryExecute: boolean; + terminators: string[]; } export const listen = async (options?: Partial<ListeningOptions>) => { let results: string | undefined; - let main = MainView.Instance; + // let main = MainView.Instance; - main.dictationOverlayVisible = true; - main.isListening = { interim: false }; + // main.dictationOverlayVisible = true; + // main.isListening = { interim: false }; try { results = await listenImpl(options); if (results) { Utils.CopyText(results); - main.isListening = false; - let execute = options && options.tryExecute; - main.dictatedPhrase = execute ? results.toLowerCase() : results; - main.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; + // main.isListening = false; + // let execute = options && options.tryExecute; + // main.dictatedPhrase = execute ? results.toLowerCase() : results; + // main.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; + options && options.tryExecute && await DictationManager.Commands.execute(results); } } catch (e) { - main.isListening = false; - main.dictatedPhrase = results = `dictation error: ${"error" in e ? e.error : "unknown error"}`; - main.dictationSuccess = false; + // main.isListening = false; + // main.dictatedPhrase = results = `dictation error: ${"error" in e ? e.error : "unknown error"}`; + // main.dictationSuccess = false; } finally { - main.initiateDictationFade(); + // main.initiateDictationFade(); } return results; @@ -100,7 +102,7 @@ export namespace DictationManager { const listenImpl = (options?: Partial<ListeningOptions>) => { if (isListening) { - return infringe; + return Infringed; } isListening = true; @@ -128,6 +130,12 @@ export namespace DictationManager { recognizer.onresult = (e: SpeechRecognitionEvent) => { current = synthesize(e, intra); + let matchedTerminator: string | undefined; + if (options && options.terminators && (matchedTerminator = options.terminators.find(end => current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false))) { + current = matchedTerminator; + recognizer.abort(); + return complete(); + } handler && handler(current); isManuallyStopped && complete(); }; @@ -163,13 +171,13 @@ export namespace DictationManager { } isManuallyStopped = true; salvageSession ? recognizer.stop() : recognizer.abort(); - let main = MainView.Instance; - if (main.dictationOverlayVisible) { - main.cancelDictationFade(); - main.dictationOverlayVisible = false; - main.dictationSuccess = undefined; - setTimeout(() => main.dictatedPhrase = placeholder, 500); - } + // let main = MainView.Instance; + // if (main.dictationOverlayVisible) { + // main.cancelDictationFade(); + // main.dictationOverlayVisible = false; + // main.dictationSuccess = undefined; + // setTimeout(() => main.dictatedPhrase = placeholder, 500); + // } }; const synthesize = (e: SpeechRecognitionEvent, delimiter?: string) => { @@ -301,11 +309,16 @@ export namespace DictationManager { } }], - ["create bulleted note", { + ["new outline", { action: (target: DocumentView) => { let newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" }); + newBox.autoHeight = true; let proto = newBox.proto!; - let proseMirrorState = '"{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":""}]}]}]}]},"selection":{"type":"text","anchor":1,"head":1}}"'; + proto.page = -1; + let prompt = "Press alt + r to start dictating here..."; + let head = 3; + let anchor = head + prompt.length; + let proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`; proto.data = new RichTextField(proseMirrorState); proto.backgroundColor = "#eeffff"; target.props.addDocTab(newBox, proto, "onRight"); @@ -323,6 +336,9 @@ export namespace DictationManager { let what = matches[2]; let dataDoc = Doc.GetProto(target.props.Document); let fieldKey = "data"; + if (isNaN(count)) { + return; + } for (let i = 0; i < count; i++) { let created: Doc | undefined; switch (what) { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 891fd7847..cc8e57af9 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -730,7 +730,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> onPointerEnter={e => e.ctrlKey && runInAction(() => this.openHover = true)} onPointerLeave={() => runInAction(() => this.openHover = false)} onClick={e => { - if (e.ctrlKey) { + if (e.altKey) { + e.preventDefault(); window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`); } else { this.clearPullColor(); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 3f3360eff..0c39a57ff 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -6,7 +6,7 @@ import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; import { Fragment, Node, Node as ProsNode, NodeType, Slice } from "prosemirror-model"; -import { EditorState, Plugin, Transaction } from "prosemirror-state"; +import { EditorState, Plugin, Transaction, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc"; @@ -35,6 +35,8 @@ import React = require("react"); import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils'; import { DocumentDecorations } from '../DocumentDecorations'; import { MainOverlayTextBox } from '../MainOverlayTextBox'; +import { DictationManager } from '../../util/DictationManager'; +import { ReplaceStep } from 'prosemirror-transform'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -180,6 +182,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const marks = tx.storedMarks; if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); } } + this._applyingChange = true; const fieldkey = "preview"; if (this.extensionDoc) this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n"); @@ -265,6 +268,62 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } + recordKeyHandler = (e: KeyboardEvent) => { + if (this.props.Document !== SelectionManager.SelectedDocuments()[0].props.Document) { + return; + } + e.stopPropagation(); + e.preventDefault(); + e.key === "R" && this.recordBullet(); + } + + recordBullet = async () => { + let completedCue = "end session"; + let results = await DictationManager.Controls.listen({ + interimHandler: this.setCurrentBulletContent, + continuous: { indefinite: false }, + // terminators: [completedCue, "bullet", "next"] + }); + if (results && [DictationManager.Controls.Infringed, completedCue].includes(results)) { + DictationManager.Controls.stop(); + return; + } + this.nextBullet(this._editorView!.state.selection.to); + setTimeout(this.recordBullet, 2000); + } + + setCurrentBulletContent = (value: string) => { + if (this._editorView) { + let state = this._editorView.state; + let from = state.selection.from; + let to = state.selection.to; + this._editorView.dispatch(state.tr.insertText(value, from, to)); + state = this._editorView.state; + let updated = TextSelection.create(state.doc, from, from + value.length); + this._editorView.dispatch(state.tr.setSelection(updated)); + } + } + + nextBullet = (pos: number) => { + if (this._editorView) { + let frag = Fragment.fromArray(this.newListItems(2)); + let slice = new Slice(frag, 2, 2); + let state = this._editorView.state; + this._editorView.dispatch(state.tr.step(new ReplaceStep(pos, pos, slice))); + pos += 4; + state = this._editorView.state; + this._editorView.dispatch(state.tr.setSelection(TextSelection.create(this._editorView.state.doc, pos, pos))); + } + } + + private newListItems = (count: number) => { + let listItems: any[] = []; + for (let i = 0; i < count; i++) { + listItems.push(schema.nodes.list_item.create(null, schema.nodes.paragraph.create(null))); + } + return listItems; + } + componentDidMount() { const config = { schema, @@ -316,7 +375,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe updatedState.selection = { type: "text", anchor: end, head: end }; this._editorView.updateState(EditorState.fromJSON(config, updatedState)); } - this.tryUpdateHeight(); } } ); @@ -658,6 +716,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @action onFocused = (e: React.FocusEvent): void => { + document.removeEventListener("keypress", this.recordKeyHandler); + document.addEventListener("keypress", this.recordKeyHandler); if (!this.props.isOverlay) { FormattedTextBox.InputBoxOverlay = this; } else { @@ -707,6 +767,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe }); } onBlur = (e: any) => { + document.removeEventListener("keypress", this.recordKeyHandler); if (this._undoTyping) { this._undoTyping.end(); this._undoTyping = undefined; |