Skip to content
This repository has been archived by the owner on Feb 25, 2024. It is now read-only.

Refactored to use XState typegen #341

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "https://pkg.csb.dev/statelyai/xstate/commit/35740f50/@xstate/react",
"date-fns": "^2.22.1",
"elkjs": "^0.7.1",
"framer-motion": "^4",
Expand All @@ -30,7 +30,7 @@
"realms-shim": "^1.2.2",
"web-vitals": "^1.0.1",
"web-worker": "^1.0.0",
"xstate": "^4.26.0"
"xstate": "https://pkg.csb.dev/statelyai/xstate/commit/35740f50/xstate"
},
"scripts": {
"start": "concurrently \"npm:start:app\" \"npm:graphql:codegen\"",
Expand Down
3 changes: 2 additions & 1 deletion src/CanvasContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const dragModel = createModel(

const dragMachine = dragModel.createMachine(
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsTypes: {} as import("./CanvasContainer.typegen").Typegen0,
preserveActionOrder: true,
initial: 'checking_if_disabled',
states: {
Expand Down Expand Up @@ -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);
Expand Down
70 changes: 70 additions & 0 deletions src/CanvasContainer.typegen.ts
Original file line number Diff line number Diff line change
@@ -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;
}
112 changes: 62 additions & 50 deletions src/EditorPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -83,9 +90,9 @@ const editorPanelModel = createModel(
notifRef: undefined! as ActorRefFrom<typeof notifMachine>,
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<typeof parseMachines> | null,
deltaDecorations: [] as string[],
},
{
Expand All @@ -107,6 +114,14 @@ const editorPanelModel = createModel(

const editorPanelMachine = editorPanelModel.createMachine(
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsTypes: {} as import("./EditorPanel.typegen").Typegen0,
schema: {} as {
services: {
parseMachines: {
data: ReturnType<typeof parseMachines>;
};
};
},
entry: [assign({ notifRef: () => spawn(notifMachine) })],
initial: 'booting',
states: {
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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);
},
},
},
);

Expand Down Expand Up @@ -373,7 +387,7 @@ export const EditorPanel: React.FC<{
onSave: () => void;
onFork: () => void;
onCreateNew: () => void;
onChange: (machine: AnyStateMachine[]) => void;
onChange: (machine: ReturnType<typeof parseMachines>) => void;
onChangedCodeValue: (code: string) => void;
}> = ({ onSave, onChange, onChangedCodeValue, onFork, onCreateNew }) => {
const embed = useEmbed();
Expand Down Expand Up @@ -512,8 +526,6 @@ export const EditorPanel: React.FC<{
{sourceOwnershipStatus === 'user-owns-source' &&
!embed?.isEmbedded && (
<Button
disabled={sourceState.hasTag('forking')}
isLoading={sourceState.hasTag('forking')}
onClick={() => {
onFork();
}}
Expand Down
58 changes: 58 additions & 0 deletions src/EditorPanel.typegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// This file was automatically generated. Edits will be overwritten

export interface Typegen0 {
'@@xstate/typegen': true;
eventsCausingActions: {
onChangedCodeValue: 'EDITOR_CHANGED_VALUE';
clearDecorations: 'EDITOR_CHANGED_VALUE';
onChange: 'UPDATE_MACHINE_PRESSED';
broadcastError:
| 'error.platform.(machine).booting.fixing_gist_imports:invocation[0]'
| 'error.platform.(machine).compiling:invocation[0]';
assignParsedMachinesToContext: 'done.invoke.(machine).compiling:invocation[0]';
addDecorations: 'error.platform.(machine).compiling:invocation[0]';
scrollToLineWithError: 'error.platform.(machine).compiling:invocation[0]';
};
internalEvents: {
'error.platform.(machine).booting.fixing_gist_imports:invocation[0]': {
type: 'error.platform.(machine).booting.fixing_gist_imports:invocation[0]';
data: unknown;
};
'error.platform.(machine).compiling:invocation[0]': {
type: 'error.platform.(machine).compiling:invocation[0]';
data: unknown;
};
'done.invoke.(machine).compiling:invocation[0]': {
type: 'done.invoke.(machine).compiling:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
};
invokeSrcNameMap: {
parseMachines: 'done.invoke.(machine).compiling:invocation[0]';
};
missingImplementations: {
actions: 'onChangedCodeValue' | 'onChange';
services: never;
guards: never;
delays: never;
};
eventsCausingServices: {
parseMachines: 'COMPILE' | 'done.state.(machine).booting';
};
eventsCausingGuards: {
isGist: 'EDITOR_READY';
isSyntaxError: 'error.platform.(machine).compiling:invocation[0]';
};
eventsCausingDelays: {};
matchesStates:
| 'booting'
| 'booting.waiting_for_monaco'
| 'booting.fixing_gist_imports'
| 'booting.done'
| 'active'
| 'updating'
| 'compiling'
| { booting?: 'waiting_for_monaco' | 'fixing_gist_imports' | 'done' };
tags: 'visualizing';
}
11 changes: 5 additions & 6 deletions src/EmbedPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ const extractFormData = (form: HTMLFormElement): ParsedEmbed => {
/**
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox
*/
const getEmbedCodeFromUrl = (embedUrl: string) => `<iframe src="${embedUrl}" sandbox="allow-same-origin allow-scripts"></iframe>`;
const getEmbedCodeFromUrl = (embedUrl: string) =>
`<iframe src="${embedUrl}" sandbox="allow-same-origin allow-scripts"></iframe>`;

const embedPreviewModel = createModel(
{
Expand Down Expand Up @@ -91,6 +92,7 @@ const embedPreviewModel = createModel(
);

const embedPreviewMachine = embedPreviewModel.createMachine({
tsTypes: {} as import("./EmbedPreview.typegen").Typegen0,
id: 'preview',
type: 'parallel',
preserveActionOrder: false, // TODO: remove this after we figured why this makes a bug
Expand All @@ -101,11 +103,7 @@ const embedPreviewMachine = embedPreviewModel.createMachine({
ready: {
entry: [
'makeEmbedUrlAndCode',
pure((ctx: ContextFrom<typeof embedPreviewModel>) => {
if (!ctx.loaded) {
return { type: 'makePreviewUrl' };
}
}),
'makePreviewUrl',
'updateEmbedCopy',
send('PREVIEW'),
],
Expand Down Expand Up @@ -220,6 +218,7 @@ const EmbedPreviewContent: React.FC = () => {
};
}),
makePreviewUrl: assign((ctx) => {
if (!ctx.loaded) return {};
const url = makeEmbedUrl(
router.query.sourceFileId as string,
window.location.origin,
Expand Down
Loading