diff options
Diffstat (limited to 'src/client/util/DictationManager.ts')
-rw-r--r-- | src/client/util/DictationManager.ts | 144 |
1 files changed, 142 insertions, 2 deletions
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index b58bdb6c7..80efe12cd 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -1,3 +1,15 @@ +import { string } from "prop-types"; +import { observable, action, autorun } from "mobx"; +import { SelectionManager } from "./SelectionManager"; +import { DocumentView } from "../views/nodes/DocumentView"; +import { UndoManager } from "./UndoManager"; +import * as converter from "words-to-numbers"; +import { Doc } from "../../new_fields/Doc"; +import { List } from "../../new_fields/List"; +import { Docs } from "../documents/Documents"; +import { CollectionViewType } from "../views/collections/CollectionBaseView"; +import { MainView } from "../views/MainView"; + namespace CORE { export interface IWindow extends Window { webkitSpeechRecognition: any; @@ -5,11 +17,14 @@ namespace CORE { } const { webkitSpeechRecognition }: CORE.IWindow = window as CORE.IWindow; +export type IndependentAction = (target: DocumentView) => any | Promise<any>; +export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise<any>; +export type RegexEntry = { key: RegExp, value: DependentAction }; export default class DictationManager { public static Instance = new DictationManager(); - private isListening = false; private recognizer: any; + private isListening = false; constructor() { this.recognizer = new webkitSpeechRecognition(); @@ -19,8 +34,12 @@ export default class DictationManager { finish = (handler: any, data: any) => { handler(data); - this.isListening = false; + this.stop(); + } + + stop = () => { this.recognizer.stop(); + this.isListening = false; } listen = () => { @@ -33,7 +52,128 @@ export default class DictationManager { this.recognizer.onresult = (e: any) => this.finish(resolve, e.results[0][0].transcript); this.recognizer.onerror = (e: any) => this.finish(reject, e); }); + } + + private sanitize = (title: string) => { + return title.replace("...", "").toLowerCase().trim(); + } + + public registerStatic = (keys: Array<string>, action: IndependentAction, overwrite = false) => { + let success = true; + keys.forEach(key => { + key = this.sanitize(key); + let existing = RegisteredCommands.Independent.get(key); + if (!existing || overwrite) { + RegisteredCommands.Independent.set(key, action); + } else { + success = false; + } + }); + return success; + } + + public interpretNumber = (number: string) => { + let initial = parseInt(number); + if (!isNaN(initial)) { + return initial; + } + let converted = converter.wordsToNumbers(number, { fuzzy: true }); + if (converted === null) { + return NaN; + } + return typeof converted === "string" ? parseInt(converted) : converted; + } + + public registerDynamic = (dynamicKey: RegExp, action: DependentAction) => { + RegisteredCommands.Dependent.push({ + key: dynamicKey, + value: action + }); + } + + public execute = async (phrase: string) => { + let target = SelectionManager.SelectedDocuments()[0]; + if (!target) { + return; + } + let batch = UndoManager.StartBatch("Dictation Action"); + phrase = this.sanitize(phrase); + + let independentAction = RegisteredCommands.Independent.get(phrase); + if (independentAction) { + await independentAction(target); + return true; + } + let success = false; + for (let entry of RegisteredCommands.Dependent) { + let regex = entry.key; + let dependentAction = entry.value; + let matches = regex.exec(phrase); + regex.lastIndex = 0; + if (matches !== null) { + await dependentAction(target, matches); + success = true; + break; + } + } + batch.end(); + + return success; } +} + +export namespace RegisteredCommands { + + export const Independent = new Map<string, IndependentAction>([ + + ["clear", (target: DocumentView) => { + Doc.GetProto(target.props.Document).data = new List(); + }], + + ["open fields", (target: DocumentView) => { + let kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 }); + target.props.addDocTab(kvp, target.dataDoc, "onRight"); + }] + + ]); + + export const Dependent = new Array<RegexEntry>( + + { + key: /create (\w+) documents of type (image|nested collection)/g, + value: (target: DocumentView, matches: RegExpExecArray) => { + let count = DictationManager.Instance.interpretNumber(matches[1]); + let what = matches[2]; + if (!("viewType" in target.props.Document)) { + return; + } + let dataDoc = Doc.GetProto(target.props.Document); + let fieldKey = "data"; + for (let i = 0; i < count; i++) { + let created: Doc | undefined; + switch (what) { + case "image": + created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"); + break; + case "nested collection": + created = Docs.Create.FreeformDocument([], {}); + break; + } + created && Doc.AddDocToList(dataDoc, fieldKey, created); + } + } + }, + + { + key: /view as (freeform|stacking|masonry|schema|tree)/g, + value: (target: DocumentView, matches: RegExpExecArray) => { + let mode = CollectionViewType.ValueOf(matches[1]); + mode && (target.props.Document.viewType = mode); + } + } + + ); + }
\ No newline at end of file |