Comment by whacked_new

1 month ago

The tradeoffs are very shallow, mainly I just wanted the maximum chance of seeing something working ASAP.

Staying vanilla reduces dependencies, which also makes testing easier during iteration. I forgot which agent (CC or codex) I used for the bulk of the code generation, but some times I manually do some one-off dialog and those get unbalanced parens.

In the agent I ask it to do `emacs -q -nw -l ...` to iterate, so it starts a bare emacs instance. This seems to have worked well when adding [Submit and Close] and [Cancel] buttons, as well as "move the cursor into the first text input widget when inserting a new array item" (the default action is to keep the cursor on the [INS] widget after inserting the list item).

The next consideration is just that I am less confident in the agent's ability to just ingest a .el library and use it correctly vs something more popular like python. Maybe it can, I just wanted results fast and not have to dig too deep myself. I had to go in and do some (setq debug-on-error t) toggles a few times while it was figuring out the json schema load into alist/hashmap differences and I didn't want to spend more time doing plumbing.

But as you probably can imagine, dynamic inline forms immediate gives us state issues, so I asked the agent to create a buffer-local alist (or hashmap?) to track the states of the form input, so it can be cleaned up on close. It's a bit unreliable now. If vui.el already has a solution I'll switch over next.

Thanks for the detailed response! This is really helpful feedback.

Looking at your gist, I think the code actually illustrates why I built vui.el in the first place. The schema→widget mapping logic is genuinely interesting work, but a significant chunk of the code (~400 lines) is dedicated to the inline form lifecycle: jsf--inline-forms registry, marker tracking, resync passes, advice on widget-before-change, overlay cleanup, etc. That's exactly the plumbing vui.el handles automatically.

With vui.el, your json-schema-form could potentially be just the schema translation + a component wrapper:

    (vui-defcomponent json-schema-form (schema on-submit)
      :state ((values (schema-defaults schema))
              (errors nil))
      :render
      (vui-vstack
       (render-schema-fields schema values
         :on-change (lambda (path val)
                      (vui-set-state :values (set-at-path values path val))))
       (vui-button "Submit"
         :on-click (lambda ()
                     (let ((errs (validate-schema schema values)))
                       (if errs
                           (vui-set-state :errors errs)
                         (funcall on-submit values)))))))

State tracking, cleanup on close, multiple forms per buffer - all handled by the framework. Your validation logic and schema mapping would be the same, just without the infrastructure code.

On the emacs -q -nw workflow: it works, but you might find eldev + buttercup tests even better for AI-assisted iteration. The agent can run eldev test after changes and self-correct on failures. Byte-compilation catches issues too. Claude Code handles eldev well out of the box.

Anyway, not trying to hard-sell vui.el - your approach clearly works and the schema-form idea is cool. But if you do hit more state/cleanup headaches, might be worth a look. Happy to help if you want to try porting the schema logic over.

  • I played with vui.el's hello world code earlier today, and it's a great showcase!

    Is there a simple way to achieve in-buffer forms? The jsf-- stuff was instructed to make the widget form read-only except for the interactive widget components (took a few iterations to work), while keeping the rest of the buffer editable. The demos seem to all mount to a new buffer. Though I could also feed vui.el into an agent and ask :-)

    Thanks for the tips. I guess I can add the deps to --eval "(load 'eldev)" in manual testing.

    edit: wow, I just saw your implementation at https://gist.github.com/d12frosted/51edb747167c0fa8fcce40fed...

    this is awesome!

    • Glad you like it! I was curious to see the comparison myself, so I asked Claude Code to reimplement it with vui.el - wanted concrete numbers and code rather than just claims. The 60% reduction was a pleasant surprise.

      Regarding in-buffer forms: I'd love to understand your use case better. When you say "injecting" forms into a buffer while keeping the rest editable - what's the actual workflow you're building? A few scenarios come to mind:

      1. Inline editing in org/markdown - e.g., a structured data block that becomes a form when you enter it? 2. Mixed content - documentation with embedded interactive widgets? 3. Progressive disclosure - expand a section into a form, collapse back when done?

      Right now vui.el mounts to a dedicated buffer, so it doesn't support inline injection. But depending on the use case, it might not be too complex to add - or there might be a different approach that works better. Would be interested to hear more about what you're trying to build.

      (And yes, feeding vui.el to an agent works surprisingly well - that's exactly how the gist was created!)

      3 replies →