diff --git a/package.json b/package.json index 44512967..e97639ab 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@supabase/supabase-js": "^1.22.5", "@xstate/graph": "^1.3.0", "@xstate/inspect": "^0.4.1", - "@xstate/react": "^1.5.1", + "@xstate/react": "^2.0.0", "date-fns": "^2.22.1", "elkjs": "^0.7.1", "framer-motion": "^4", @@ -30,7 +30,7 @@ "realms-shim": "^1.2.2", "web-vitals": "^1.0.1", "web-worker": "^1.0.0", - "xstate": "^4.26.0" + "xstate": "^4.29.0" }, "scripts": { "start": "concurrently \"npm:start:app\" \"npm:graphql:codegen\"", diff --git a/src/CanvasContainer.tsx b/src/CanvasContainer.tsx index a816a514..50fc2642 100644 --- a/src/CanvasContainer.tsx +++ b/src/CanvasContainer.tsx @@ -49,6 +49,7 @@ const dragModel = createModel( const dragMachine = dragModel.createMachine( { + tsTypes: {} as import("./CanvasContainer.typegen").Typegen0, preserveActionOrder: true, initial: 'checking_if_disabled', states: { @@ -282,7 +283,7 @@ export const CanvasContainer: React.FC<{ panModeEnabled: boolean }> = ({ const [state, send] = useMachine(dragMachine, { actions: { sendPanChange: actions.send( - (_, ev: any) => { + (_, ev) => { // we need to translate a pointer move to the viewbox move // and that is going into the opposite direction than the pointer return canvasModel.events.PAN(-ev.delta.x, -ev.delta.y); diff --git a/src/CanvasContainer.typegen.ts b/src/CanvasContainer.typegen.ts new file mode 100644 index 00000000..45715f09 --- /dev/null +++ b/src/CanvasContainer.typegen.ts @@ -0,0 +1,70 @@ +// This file was automatically generated. Edits will be overwritten + +export interface Typegen0 { + '@@xstate/typegen': true; + eventsCausingActions: { + sendPanChange: 'POINTER_MOVED_BY'; + enableTextSelection: string; + disableTextSelection: 'ENABLE_PANNING'; + }; + internalEvents: {}; + invokeSrcNameMap: { + invokeDetectLock: 'done.invoke.(machine).enabled.mode.lockable.released:invocation[0]'; + wheelPressListener: 'done.invoke.(machine).enabled.mode.lockable.released:invocation[1]'; + invokeDetectRelease: 'done.invoke.(machine).enabled.mode.lockable.locked:invocation[0]'; + }; + missingImplementations: { + actions: 'sendPanChange'; + services: never; + guards: 'isPanDisabled'; + delays: never; + }; + eventsCausingServices: { + invokeDetectLock: 'RELEASE' | 'DRAG_SESSION_STOPPED'; + wheelPressListener: 'RELEASE' | 'DRAG_SESSION_STOPPED'; + invokeDetectRelease: 'LOCK'; + }; + eventsCausingGuards: { + isPanDisabled: string; + }; + eventsCausingDelays: {}; + matchesStates: + | 'checking_if_disabled' + | 'permanently_disabled' + | 'enabled' + | 'enabled.mode' + | 'enabled.mode.lockable' + | 'enabled.mode.lockable.released' + | 'enabled.mode.lockable.locked' + | 'enabled.mode.lockable.wheelPressed' + | 'enabled.mode.pan' + | 'enabled.panning' + | 'enabled.panning.disabled' + | 'enabled.panning.enabled' + | 'enabled.panning.enabled.idle' + | 'enabled.panning.enabled.active' + | 'enabled.panning.enabled.active.grabbed' + | 'enabled.panning.enabled.active.dragging' + | 'enabled.panning.enabled.active.done' + | { + enabled?: + | 'mode' + | 'panning' + | { + mode?: + | 'lockable' + | 'pan' + | { lockable?: 'released' | 'locked' | 'wheelPressed' }; + panning?: + | 'disabled' + | 'enabled' + | { + enabled?: + | 'idle' + | 'active' + | { active?: 'grabbed' | 'dragging' | 'done' }; + }; + }; + }; + tags: never; +} diff --git a/src/EditorPanel.tsx b/src/EditorPanel.tsx index 4f6f1a19..e284d237 100644 --- a/src/EditorPanel.tsx +++ b/src/EditorPanel.tsx @@ -12,7 +12,14 @@ import { useActor, useMachine, useSelector } from '@xstate/react'; import { editor, Range } from 'monaco-editor'; import dynamic from 'next/dynamic'; import React from 'react'; -import { ActorRefFrom, assign, DoneInvokeEvent, send, spawn } from 'xstate'; +import { + ActorRefFrom, + assign, + DoneInvokeEvent, + send, + spawn, + StateNode, +} from 'xstate'; import { createModel } from 'xstate/lib/model'; import { useAuth } from './authContext'; import { CommandPalette } from './CommandPalette'; @@ -83,9 +90,9 @@ const editorPanelModel = createModel( notifRef: undefined! as ActorRefFrom, monacoRef: null as Monaco | null, standaloneEditorRef: null as editor.IStandaloneCodeEditor | null, - sourceRef: null as SourceMachineActorRef, + sourceRef: null as unknown as SourceMachineActorRef, mainFile: 'main.ts', - machines: null as AnyStateMachine[] | null, + machines: null as ReturnType | null, deltaDecorations: [] as string[], }, { @@ -107,6 +114,14 @@ const editorPanelModel = createModel( const editorPanelMachine = editorPanelModel.createMachine( { + tsTypes: {} as import("./EditorPanel.typegen").Typegen0, + schema: {} as { + services: { + parseMachines: { + data: ReturnType; + }; + }; + }, entry: [assign({ notifRef: () => spawn(notifMachine) })], initial: 'booting', states: { @@ -185,48 +200,10 @@ const editorPanelMachine = editorPanelModel.createMachine( compiling: { tags: ['visualizing'], invoke: { - src: async (ctx) => { - const monaco = ctx.monacoRef!; - const uri = monaco.Uri.parse(ctx.mainFile); - const tsWoker = await monaco.languages.typescript - .getTypeScriptWorker() - .then((worker) => worker(uri)); - - const syntaxErrors = await tsWoker.getSyntacticDiagnostics( - uri.toString(), - ); - - if (syntaxErrors.length > 0) { - const model = ctx.monacoRef?.editor.getModel(uri); - // Only report one error at a time - const error = syntaxErrors[0]; - - const start = model?.getPositionAt(error.start!); - const end = model?.getPositionAt(error.start! + error.length!); - const errorRange = new ctx.monacoRef!.Range( - start?.lineNumber!, - 0, // beginning of the line where error occured - end?.lineNumber!, - end?.column!, - ); - return Promise.reject( - new SyntaxError(error.messageText.toString(), errorRange), - ); - } - - const compiledSource = await tsWoker - .getEmitOutput(uri.toString()) - .then((result) => result.outputFiles[0].text); - - return parseMachines(compiledSource); - }, + src: 'parseMachines', onDone: { target: 'updating', - actions: [ - assign({ - machines: (_, e: any) => e.data, - }), - ], + actions: ['assignParsedMachinesToContext'], }, onError: [ { @@ -276,13 +253,15 @@ const editorPanelMachine = editorPanelModel.createMachine( guards: { isGist: (ctx) => ctx.sourceRef.getSnapshot()!.context.sourceProvider === 'gist', - isSyntaxError: (_, e: any) => e.data instanceof SyntaxError, + isSyntaxError: (_, e) => e.data instanceof SyntaxError, }, actions: { - broadcastError: send((_, e: any) => ({ + assignParsedMachinesToContext: assign({ + machines: (_, e) => e.data, + }), + broadcastError: send((_, e) => ({ type: 'EDITOR_ENCOUNTERED_ERROR', - title: e.data.title, - message: e.data.message, + message: (e.data as Error).message, })), addDecorations: assign({ deltaDecorations: (ctx, e) => { @@ -322,6 +301,41 @@ const editorPanelMachine = editorPanelModel.createMachine( editor?.revealLineInCenterIfOutsideViewport(range.startLineNumber); }, }, + services: { + parseMachines: async (ctx) => { + const monaco = ctx.monacoRef!; + const uri = monaco.Uri.parse(ctx.mainFile); + const tsWoker = await monaco.languages.typescript + .getTypeScriptWorker() + .then((worker) => worker(uri)); + + const syntaxErrors = await tsWoker.getSyntacticDiagnostics( + uri.toString(), + ); + + if (syntaxErrors.length > 0) { + const model = ctx.monacoRef?.editor.getModel(uri); + // Only report one error at a time + const error = syntaxErrors[0]; + + const start = model?.getPositionAt(error.start!); + const end = model?.getPositionAt(error.start! + error.length!); + const errorRange = new ctx.monacoRef!.Range( + start?.lineNumber!, + 0, // beginning of the line where error occured + end?.lineNumber!, + end?.column!, + ); + throw new SyntaxError(error.messageText.toString(), errorRange); + } + + const compiledSource = await tsWoker + .getEmitOutput(uri.toString()) + .then((result) => result.outputFiles[0].text); + + return parseMachines(compiledSource); + }, + }, }, ); @@ -373,7 +387,7 @@ export const EditorPanel: React.FC<{ onSave: () => void; onFork: () => void; onCreateNew: () => void; - onChange: (machine: AnyStateMachine[]) => void; + onChange: (machine: ReturnType) => void; onChangedCodeValue: (code: string) => void; }> = ({ onSave, onChange, onChangedCodeValue, onFork, onCreateNew }) => { const embed = useEmbed(); @@ -512,8 +526,6 @@ export const EditorPanel: React.FC<{ {sourceOwnershipStatus === 'user-owns-source' && !embed?.isEmbedded && (