Skip to content

feat(portal,server): custom blocks authoring, canvas integration and queued execution - #259

Merged
07prajwal2000 merged 2 commits into
Fluxify-rest:mainfrom
07prajwal2000:feature/custom-blocks-input-params
Aug 17, 2026
Merged

feat(portal,server): custom blocks authoring, canvas integration and queued execution#259
07prajwal2000 merged 2 commits into
Fluxify-rest:mainfrom
07prajwal2000:feature/custom-blocks-input-params

Conversation

@07prajwal2000

Copy link
Copy Markdown
Collaborator

Closes #258.

Why

Custom blocks worked end to end on the server, but the portal exposed a thin slice: a card grid, a three-field create modal, and an "Open canvas" link. The panel that renders a placed block's settings already read inputParams — nothing could write it, so every custom block on a route was unconfigurable. Authoring a block also gave no way to reach it: it never appeared in the block picker.

What

Authoring

  • Input params editor and icon picker, wired into a four-step create page (mirroring the route create flow) and a settings modal, so a block can declare its input contract and be edited after creation. name stays immutable and the UI says why.
  • List page reworked: a grid of the real canvas node, search, an empty state, and a click straight into the block's canvas.

Canvas

  • Custom blocks load from the DB into the block picker under their own category. Plugin-sourced blocks land in "Built-in", so the shipped set has somewhere to go; empty categories are hidden.
  • A placed block renders its own label, description and icon on the node and in the settings panel — the panel titles with the label and shows the identifier underneath, since that is what flows reference.
  • On a custom block's own canvas, its integration_selector params are injected into the integration picker. Picking one writes param:<name>; the concrete integration is chosen by the caller, so no test/open action is offered for it. Route canvases are untouched — the behaviour is gated on the custom-block route's blockId.

Engine

  • params is bound alongside input in a custom block's JS. Configuration is params.<name> at any depth; input is always the previous block's output. Previously the first block saw config as input.foo and the caller's value at input.input, and the meaning changed further down the graph.

  • Nine db/log emitters take their connection through node.value, so a param: integration resolves at call time rather than being baked in.

  • New queued invoke mode: durable background execution. async is a floating promise on the request worker — it dies with the worker, competes with traffic, and cannot retry. queued publishes to a JetStream work queue and another worker picks it up.

    One stream for all background work (fluxify.jobs.<projectId>.<kind>), kind-routed through a handler registry, so crons, workflows and scheduled jobs reuse the same stream, consumer and worker rather than each adding a mostly-idle stream. Transport (acks, redelivery, concurrency) lives where NATS is; handlers live where user code is, connected over the existing supervisor↔execution IPC. Tunable via JOBS_CONCURRENCY, JOBS_ACK_WAIT_MS, JOBS_MAX_DELIVER, JOBS_RETRY_DELAY_MS.

Fixes

  • The compiler never registered custom blocks into its own library, so any route calling one failed with No codegen for block type. It now registers its own output and resolves dependencies before compiling, and unregisters on rename or delete — a rename previously left the old name resolving to a stale body forever.
  • Recursion is refused. A block cannot call itself, directly or through a chain of other custom blocks: checked on every canvas save against the project's call graph (the error names the chain), and reflected in the picker, which offers the block disabled with the reason.
  • Handle shape rules no longer depend on stylesheet order — rendering a block preview outside the canvas used to flip the cascade and turn inbound handles into ovals.

Notes

  • apps/portal only; no hardcoded colors (semantic tokens throughout); shared components reused.
  • Migration: custom blocks whose JS reads a config param as input.foo need params.foo. Nothing warns — the value silently becomes undefined. Cheaper to land now than after people build on it.
  • Queued mode needs the compiled worker; the legacy interpreted path never honoured invoke at all.

Testing

turbo run lint clean across all 10 packages. packages/blocks 145 pass, apps/server 355 pass, apps/portal 82 pass.

🤖 Generated with Claude Code

07prajwal2000 and others added 2 commits August 17, 2026 19:47
…queued execution

Closes Fluxify-rest#258.

Portal
- Input params editor and icon picker, wired into a 4-step create page and a
  settings modal, so a custom block can finally declare its input contract.
- List page reworked: grid of the real canvas node, search, empty state, and
  a click straight into the block's canvas.
- Custom blocks now load from the DB into the block picker under their own
  category (plugin-sourced ones get "Built-in", ready for the shipped set), and
  a placed block renders its own label, description and icon on the canvas and
  in the settings panel.
- A custom block's `integration_selector` params are injected into the
  integration picker on its own canvas: picking one writes `param:<name>` and
  the real integration is chosen by the caller, so no test/open action is shown.

Engine
- `params` is bound alongside `input` in a custom block's JS: configuration is
  `params.<name>` at any depth, `input` is always the previous block's output.
- Nine db/log emitters take their connection through `node.value`, so a
  `param:` integration resolves at call time.
- New `queued` invoke mode: durable background execution over a single shared
  JetStream work queue (`fluxify.jobs.<projectId>.<kind>`), kind-routed so
  crons, workflows and schedules can reuse the same stream and worker.

Fixes
- The compiler never registered custom blocks into its own library, so any
  route calling one failed with "No codegen for block type". It now registers
  its own output and resolves dependencies before compiling, and unregisters
  on rename or delete.
- Recursion is refused: a block cannot call itself, directly or through a
  chain, checked on every canvas save and reflected in the picker.
- Handle shape rules no longer depend on stylesheet order.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One custom block, two input params, three routes that configure it
differently — signing, lenient verification, and strict verification that
ends on the block's own response block.

The harness gained what custom blocks need: fixtures declare the blocks they
call in `uses`, and the runner registers them before compiling the route, the
same order the real compiler works in. Custom blocks live in `blocks/` in the
shape the portal saves them, input contract included.

Also adds `engine: "none"` for graphs that touch no database, so a pure-JS
fixture no longer starts a Postgres container.

The block's secret is hardcoded — custom block params cannot reference app
config yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@07prajwal2000
07prajwal2000 added this pull request to the merge queue Aug 17, 2026
Merged via the queue into Fluxify-rest:main with commit 95e6cff Aug 17, 2026
12 checks passed
@07prajwal2000
07prajwal2000 deleted the feature/custom-blocks-input-params branch August 17, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom blocks portal rework: input params authoring, icons, editing and a real canvas

1 participant