statelyai / xstate-tools

Public monorepo for XState tooling

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature request: VSCode extension / online editor - store layout info as a field in each compound state (and use in inspector)

eponymous301 opened this issue · comments

Current challenges with editing machine layouts:

  • The @xstate-layout comment created by VSCode extension cannot be used by @xstate/inspect nor copied into editor in Stately Studio
  • Layout work done online using Stately Studio cannot be exported and used by VSCode extension
  • Copying a machine definition and pasting into a state of a larger machine loses layout info for the inner machine

If a new 'layout' string were added to each compound node, recording layout info for children (but not grandchildren) this would help solve above, e.g.:

{
  "id": "Machine Name",
  "initial": "First State",
  "layout": "N4IgpgJg5mDOIC5QBg5mDOIC5QBcDDOIC5QBcD2FUGIAqB5A4v....",
  "states": {
    "First State": {
      "initial": "new state 1",
      "layout": "DICiA+gMK4CiA+gMK4CCiA+gM4CCiA+gMK4CyACq....",
      "states": {
        "new state 1": {
          "on": {
            "start": {
              "target": "new state 2"
            }
          }
        },
        "new state 2": {
          "type": "final"
        }
      },
      "onDone": {
        "target": "Second State"
      }
    },
    "Second State": {}
  }
}

The @xstate-layout comment created by VSCode extension cannot be used by @xstate/inspect

The default view used there doesn't understand this layout information anyway.

nor copied into editor in Stately Studio

This sounds like a problem that could be fixed.

If a new 'layout' string were added to each compound node, recording layout info for children (but not grandchildren) this would help solve above, e.g.:

I was thinking about writing this information in JSDoc comments at the state levels. This potentially would address your problem - although we'd have to make sure that the encoded information is using relative offsets and not absolute ones. It wouldn't quite address your concern with this information being available at runtime - I think though that most people don't want this at runtime since it bloats the bundlesize.

I think though that most people don't want this at runtime since it bloats the bundlesize.

Hmm, I don't have a simple solution for bundle size concerns (I have only cursory knowledge of the build/bundle process). I am guessing that it would be possible to add some kind of preprocessor or minifier plugin for the build to strip out the field, but I do acknowledge that makes more dev work and adds complications.

It would be quite nice though to be able to do the layout work once and have it available everywhere. I assume that having the info available at runtime would be a pre-requisite to make it possible for @xstate/inspect to use the same layout.

I haven't looked at the @xstate/inspect source code yet, is there is a way to pass layout info into it if the run-time environment can provide it?

I was thinking about writing this information in JSDoc comments at the state levels.

I like JSDoc very much for annotating functions. I think state machine definitions are more like data than source code declarations though, and comments are kind of brittle for annotating data (and don't survive trips through JSON).

I also wind up copying and pasting between machines during development, and also setting up machine sections as constants and then manipulating, e.g.:

const dragDropMachine = {
  context: {...},
  states: {...},
  initial: "..."
}

const dragDropMachineOptions = {
  actions: {...},
  services: {...}
}
...

const drawLineMachine = {
   context: {...},
   states: {
       ...
       DrawingLine : {
            description: "dragging and dropping line 2nd point",
            meta: {
                submachine: "dragDropMachine"
            },
            onDone: { target: "SavingLine" }
      }
   },
   initial: "..."
}

const drawLineMachineOptions = {
  actions: {...},
  services: {...}
}

...
const {machineDef, machineOpts} = injectSubmachines(drawLineMachine, drawLineMachineOptions)

const myMachine = createMachine(machineDef)

const {state, send, service} = useMachine(myMachine, machineOpts)

The injectSubmachines() function prepares a merged version of the main machine definition and its options, namespacing the keys and references inside submachines (lifting submachine contexts up to main context in the process)

Side note - it would be nice also to be able to have cursor within a chunk of code like the definition of dragDropMachine above and invoke designer plugin (even though createMachine( is not present) - but if layout info traveled with machine definition it would always be possible to copy/paste to online editor, do layout there, and paste back into source.

In any case, I have been delighted and impressed with xstate - it has been very helpful in letting me extract app logic and express it in a declarative way. Thank you and please excuse my inevitable 'yes but could you please add X?' pestering.