statelyai / xstate-tools

Public monorepo for XState tooling
183 stars 40 forks source link

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

Open eponymous301 opened 1 year ago

eponymous301 commented 1 year ago

Current challenges with editing machine layouts:

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": {}
  }
}
Andarist commented 1 year ago

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.

eponymous301 commented 1 year ago

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.