aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/util/Scripting.ts8
-rw-r--r--src/client/views/MainView.tsx3
-rw-r--r--src/client/views/nodes/ScriptingBox.scss33
-rw-r--r--src/client/views/nodes/ScriptingBox.tsx137
-rw-r--r--src/server/authentication/models/current_user_utils.ts1
6 files changed, 123 insertions, 63 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5231dbb8c..5eca71ca9 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -509,8 +509,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.PRES), initial, options);
}
- export function ScriptingDocument(url: string, options: DocumentOptions = {}) {
- return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), new YoutubeField(new URL(url)), options);
+ export function ScriptingDocument(options: DocumentOptions = {}) {
+ return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), "", options);
}
export function VideoDocument(url: string, options: DocumentOptions = {}) {
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index cf04c44ca..f97d91d10 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -89,9 +89,9 @@ const _scriptingGlobals: { [name: string]: any } = {};
let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
- const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error);
- if ((options.typecheck !== false && errors) || !script) {
- return { compiled: false, errors: diagnostics };
+ const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error);
+ if ((options.typecheck !== false && errors.length) || !script) {
+ return { compiled: false, errors };
}
const paramNames = Object.keys(scriptingGlobals);
@@ -201,7 +201,7 @@ export interface ScriptOptions {
capturedVariables?: { [name: string]: Field }; // list of captured variables
typecheck?: boolean; // should the compiler perform typechecking
editable?: boolean; // can the script edit Docs
- traverser?: TraverserParam;
+ traverser?: TraverserParam;
transformer?: Transformer; // does the editor display a text label by each document that can be used as a captured document reference
globals?: { [name: string]: any };
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7c9f47fe4..1edfc871f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
import { library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons';
+import { faTerminal, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, faThumbtack, faTree, faTv, faUndoAlt, faVideo } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -101,6 +101,7 @@ export class MainView extends React.Component {
}
}
+ library.add(faTerminal);
library.add(faFileAlt);
library.add(faStickyNote);
library.add(faFont);
diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss
index e69de29bb..f31c72bb2 100644
--- a/src/client/views/nodes/ScriptingBox.scss
+++ b/src/client/views/nodes/ScriptingBox.scss
@@ -0,0 +1,33 @@
+.scriptingBox-outerDiv {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ pointer-events: all;
+ background-color: rgb(241, 239, 235);
+ padding: 10px;
+ .scriptingBox-inputDiv {
+ display: flex;
+ flex-direction: column;
+ height: calc(100% - 30px);
+ .scriptingBox-errorMessage {
+ overflow: auto;
+ }
+ .scriptingBox-textArea {
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+ resize: none;
+ padding: 7px;
+ }
+ }
+
+ .scriptingBox-toolbar {
+ width: 100%;
+ height: 30px;
+ .scriptingBox-button {
+ width: 50%
+ }
+ }
+}
+
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index a2dd134ed..74d9c2e94 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,71 +1,96 @@
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { faEdit } from '@fortawesome/free-regular-svg-icons';
-import { computed } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc } from '../../../new_fields/Doc';
-import { documentSchema } from '../../../new_fields/documentSchemas';
+import { action, observable, computed } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { Opt } from "../../../new_fields/Doc";
+import { documentSchema } from "../../../new_fields/documentSchemas";
+import { createSchema, makeInterface } from "../../../new_fields/Schema";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { StrCast, ScriptCast } from "../../../new_fields/Types";
+import { returnTrue } from "../../../Utils";
+import { InteractionUtils } from "../../util/InteractionUtils";
import { CompileScript } from "../../util/Scripting";
-import { ScriptBox } from '../ScriptBox';
-import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
-import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, ScriptCast } from '../../../new_fields/Types';
-import { DocComponent } from '../DocComponent';
-import { FieldView, FieldViewProps } from './FieldView';
-import './ScriptingBox.scss';
-import { DocumentIconContainer } from './DocumentIcon';
-
-
-library.add(faEdit as any);
+import { DocAnnotatableComponent } from "../DocComponent";
+import { EditableView } from "../EditableView";
+import { FieldView, FieldViewProps } from "../nodes/FieldView";
+import "./ScriptingBox.scss";
const ScriptingSchema = createSchema({
- onClick: ScriptField,
- buttonParams: listSpec("string"),
- text: "string"
});
type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>;
const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema);
@observer
-export class ScriptingBox extends DocComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) {
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScriptingBox, fieldKey); }
+export class ScriptingBox extends DocAnnotatableComponent<FieldViewProps, ScriptingDocument>(ScriptingDocument) {
+ protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
+ public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScriptingBox, fieldStr); }
- @computed get dataDoc() {
- return this.props.DataDoc &&
- (this.Document.isTemplateForField || BoolCast(this.props.DataDoc.isTemplateForField) ||
- this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document);
+ @observable private _errorMessage: string = "";
+ @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-raw"]); }
+ set rawScript(value) { this.dataDoc[this.props.fieldKey + "-raw"] = value; }
+
+ @action
+ componentDidMount() {
+ this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript || this.rawScript;
}
- specificContextMenu = (e: React.MouseEvent): void => { }
+ @action
+ onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this.rawScript = e.target.value;
+ }
- render() {
- const script = ScriptCast(this.props.Document[this.props.fieldKey]);
- let originalText: string | undefined = undefined;
- if (script) {
- originalText = script.script.originalScript;
+ @action
+ onError = (error: any) => {
+ this._errorMessage = (error as any).map((e: any) => e.messageText).join(" ");
+ }
+
+ @action
+ onCompile = () => {
+ const result = CompileScript(this.rawScript, {});
+ this._errorMessage = "";
+ if (result.compiled) {
+ this._errorMessage = "";
+ this.dataDoc[this.props.fieldKey] = new ScriptField(result);
}
- return !(this.props.Document instanceof Doc) ? (null) :
- <ScriptBox initialText={originalText}
- setParams={() => { }}
- onCancel={() => { }}
- onSave={(text, onError) => {
- if (!text) {
- this.dataDoc[this.props.fieldKey] = undefined;
- } else {
- const script = CompileScript(text, {
- params: { this: Doc.name },
- typecheck: false,
- editable: true,
- transformer: DocumentIconContainer.getTransformer()
- });
- if (!script.compiled) {
- onError(script.errors.map(error => error.messageText).join("\n"));
- }
- else {
- this.dataDoc[this.props.fieldKey] = new ScriptField(script);
- }
- }
- }} showDocumentIcons />;
+ else {
+ this.dataDoc[this.props.fieldKey] = undefined;
+ this.onError(result.errors);
+ }
+ }
+
+ @action
+ onRun = () => {
+ this.onCompile();
+ ScriptCast(this.dataDoc[this.props.fieldKey])?.script.run({},
+ (err: any) => {
+ this._errorMessage = "";
+ this.onError(err);
+ });
+ }
+
+ render() {
+ let onFocus: Opt<() => void> = undefined, onBlur: Opt<() => void> = undefined;
+ const params = <EditableView
+ contents={""}
+ display={"block"}
+ maxHeight={72}
+ height={35}
+ fontSize={28}
+ GetValue={() => ""}
+ SetValue={returnTrue}
+ />;
+ return (
+ <div className="scriptingBox-outerDiv" onPointerDown={(e) => this.props.isSelected() && e.stopPropagation()} onWheel={(e) => this.props.isSelected() && e.stopPropagation()}>
+ <div className="scriptingBox-inputDiv" >
+ <textarea className="scriptingBox-textarea" placeholder="write your script here" onChange={this.onChange} value={this.rawScript} onFocus={onFocus} onBlur={onBlur} />
+ <div className="scriptingBox-errorMessage">{this._errorMessage}</div>
+ <div style={{ background: "beige" }} >{params}</div>
+ </div>
+ <div className="scriptingBox-toolbar">
+ <button className="scriptingBox-button" onPointerDown={e => { this.onCompile(); e.stopPropagation(); }}>Compile</button>
+ <button className="scriptingBox-button" onPointerDown={e => { this.onRun(); e.stopPropagation(); }}>Run</button>
+ </div>
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index fa1d49e4e..37b680765 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -80,6 +80,7 @@ export class CurrentUserUtils {
{ title: "record", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
{ title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
{ title: "presentation", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation },
+ { title: "script", icon: "terminal", ignoreClick: true, drag: 'Docs.Create.ScriptingDocument({ _width: 200, _height: 250 title: "untitled script" })' },
{ title: "import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
{ title: "mobile view", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
{ title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },