Skip to content

Extend Lua plugin API to expose quadraui primitives #146

Description

@JDonaghy

Tracking issue. Not a current priority. Prerequisite for #147 — bundled Postman-like extension.

What

Give vimcode Lua plugins the ability to declare rich UI using `quadraui` primitives (`Form`, `TreeView`, `TreeTable`, `ListView`, `TextDisplay`, `TextEditor`, `Dialog`, `Palette`, etc.) and receive events routed back to plugin callbacks.

Today, vimcode's Lua API (`vimcode.*`) is limited to annotations, set_line, commands, and keymaps. Plugins cannot declare forms, tables, or interactive panels. This blocks anything beyond lightweight scripts.

Why

  • Enables a Postman-like HTTP client extension (see sibling issue)
  • Enables plugin-authored tool panels (SQL runner, API tester, log filter, etc.) without touching vimcode's Rust code
  • Converts vimcode into a general-purpose plugin-extensible keyboard-first shell, the same way VSCode's extension ecosystem works

Rough API shape (illustrative, not final)

```lua
-- Register a panel
vimcode.ui.register_panel("my-ext", {
title = "My Panel",
icon = "",
render = function(ctx)
return {
type = "form",
id = "main-form",
fields = {
{ type = "text", id = "url", label = "URL" },
{ type = "dropdown", id = "method", options = {"GET","POST"} },
{ type = "button", id = "send", label = "Send" },
},
}
end,
on_event = function(ctx, event)
if event.widget_id == "send" and event.kind == "ButtonClicked" then
-- do the thing
end
end,
})
```

Design invariants to preserve when building quadraui

These are properties the quadraui design must maintain so this feature is implementable later without breaking changes:

  1. `WidgetId` must be owned/allocatable (e.g. `String` or `Cow`), not `&'static str`. Plugins generate IDs at runtime.
  2. Events are dispatched as plain data, not Rust closures. Backend emits `UiEvent`; app (vimcode) routes to plugin by widget ID. No closures crossing the Rust/Lua boundary.
  3. Primitive data structs must be serde-compatible (Serialize + Deserialize). Lua tables convert to JSON and then to Rust structs.
  4. `WidgetId` namespace support. Apps can reserve an ID prefix per plugin so plugin widget IDs don't collide (e.g., `"plugin:my-ext:send"`).
  5. No global event handlers. All widgets have an ID; all events reference it. No "on_change" magic that doesn't carry the widget.
  6. Primitives don't borrow from app state. They own their data (or accept `&'a` with a clear lifetime). Plugins can pass owned data without borrow-checker puzzles.

These are already the design's intent per `docs/UI_CRATE_DESIGN.md`. This issue documents them explicitly to prevent drift.

Additional vimcode-side work required

Beyond quadraui, vimcode needs:

  • `vimcode.http` API for async HTTP (uses `reqwest` internally; callbacks as plugin events) — needed for Postman-alike and generally useful
  • JSON parse/stringify in Lua (a Rust crate wrapped, or bundle a Lua JSON lib)
  • Plugin-scoped storage (persistent key-value for plugin state)
  • Panel registration currently exists (`PanelRegistration`) — extend it to accept a render function producing a quadraui tree

Non-goals

  • FFI / direct Rust access for plugins
  • Letting plugins define new primitives (they compose existing ones)
  • WebAssembly plugin runtime (Lua stays primary; WASM is a separate future topic)

Dependencies

  • quadraui primitives stabilized and using the design invariants above
  • Ideally after quadraui 1.0 publishes; could be v1.x work

Priority

Low until a concrete plugin needs it (Postman extension, SQL client extension, etc.).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    coordTracked by coord-tui pipelinefeatureNew featureinfrastructureBuild, CI, distributionstatus:readyRefined and ready to enter the work pipeline

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions