Skip to content

feat(workflows): redesign the workflow library, previews and editor nodes - #10222

Open
joshistoast wants to merge 29 commits into
mainfrom
feat/workflow-library-redesign
Open

joshistoast wants to merge 29 commits into
mainfrom
feat/workflow-library-redesign

Conversation

@joshistoast

@joshistoast joshistoast commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Finding a workflow in a growing library meant scrolling a tabbed grid with one fixed sort per tab, no way to filter the project's own workflows, and no sign of whether a workflow would run. Judging one before opening it meant a second dialog that drew the compiled backend graph at an unreadable size. This redesigns the library, replaces that preview, and brings the editor's nodes and header in line with it.

Workflow library

  • Places and tags. The tabs become a left rail: In this project, Recent, My library, Templates and Shared with me, each with a count, plus the tags in use with counts and any-of filtering. Search, sort and tags work the same on every place, including This project.
  • Sort by last run. last_run_at is a library order key, with a direction menu on every key. A completed run of a project workflow linked to a template marks that template as run. Sort, direction, layout and card size persist per account; search and tags reset when the dialog closes.
  • Grid or list. Smaller cards (up to six columns) with node-position silhouettes when there is no thumbnail, a readiness dot (ready, models to install, node packs missing) and hover quick actions; or a list built on the platform List with the same facts, a date column for the current order and the same actions per row.
  • Detail rail. A flush full-height rail with the preview, name, author and version, facts, the inputs the workflow asks for, the author's notes, requirements naming the missing node packs, copies in this project, and for project workflows the linked template, its revision and update actions. Below 56rem the rails collapse into a places menu, a Tags popover and a summary sheet.
  • Menus. Right-click, Shift+F10 or a card's or row's More button open the actions menu for that workflow without changing the selection. Workflow menus use the standard menu item, without per-item descriptions.

Preview in place

  • In the library. Preview (or Space on a card or row) replaces the list with a stage instead of stacking a dialog; the detail rail and Open stay where they are, and a stepper at the top of the rail moves through the current results. Escape returns to the list as it was.
  • Nodes, Inputs, Graph. Nodes draws the saved workflow, not the compiled graph: editor titles, a summary line per node, markers for inputs you set, models you lack and the result, value-only helper nodes folded into the inputs they feed (Show all restores them), a card beside a selected node, find, zoom and a minimap when the graph does not fit. Inputs shows the author's form read-only with its saved values. Graph shows the compiled backend graph JSON, or why it cannot compile yet.
  • View graph on widgets. The widget action and the publication review use the same view in a dialog sized like the library, with run facts and notices in a side rail. Its footer is the top bar's Invoke control bound to the widget the dialog was opened on, so it shows the same issue list and fixes; Edit as workflow sits beside it for Generate and Canvas.

Editor

  • Nodes mirror the legacy editor's behaviour in the current look. Node state is derived in one place: invalid and failed nodes share a red outline and header, the selection ring takes the node's tone and keyboard focus is a separate ring. The header has a smaller title and ghost status and info buttons whose panels say what is wrong in plain words; progress is one ring in the status button. Rows show their actions on hover or keyboard focus, mark in-form fields on the label and invalid fields on the control, and controls sit in a recessed fill. Collapsed nodes show one merged port per side with counted edges; selecting a node emphasises its edges; the minimap tints invalid, failed and running nodes; ports have a larger grab area and line up with their labels' letters. The Current Image node follows the gallery selection and its image can be dragged.
  • Workflow switcher. The workflow name in the editor header opens a menu of the project's workflows, with New workflow and Workflow library beneath it.
  • Workflow menu. The widget's own items are grouped under This workflow and Import and export, with an Export submenu that includes Download as PNG. The shared Graph group is unchanged.
  • Smaller changes. "Paste with connections" is now "Smart paste". Closing a dialog opened from another dialog no longer closes the one beneath it. Exported workflow images draw node borders in the theme's border colour.

Screenshots are from the mock backend, so names and dates are fixture data.

My library, grid, with the detail rail

My library, grid, with the detail rail

Right-click opens the menu for that card; the selection and rail stay put

Right-click opens the menu for that card; the selection and rail stay put

List layout

List layout

View menu: layout, card size, thumbnail display

View menu: layout, card size, thumbnail display

In this project, list, with the linked-template rail

In this project, list, with the linked-template rail

Narrow window: places menu, Tags button, summary sheet

Narrow window: places menu, Tags button, summary sheet

Short window: the sheet expanded over the cards

Short window: the sheet expanded over the cards

Light theme, list

Light theme, list

The screenshots above predate the preview, node and header changes: the rail now also shows the author, inputs and notes, and its button reads Preview. Screenshots of the preview, View graph, the refreshed nodes and the switcher are still to be added.

Related Issues / Discussions

Addresses #10168: sort keys and direction, last-run ordering, multi-tag filtering, search and sort on This project, and the persistence rules. Not included, and left for follow-ups: the readiness and callable filters (they need a server-side requirements index to be correct beyond the loaded window), pinned workflows (needs a backend flag) and drag reordering in This project.

QA Instructions

From invokeai/frontend/webv2 under Node 22 / pnpm 10:

pnpm lint
pnpm test
pnpm test:browser              # 1 pre-existing failure, see below
pnpm test:performance:architecture
pnpm test:project-files:browser
pnpm test:accessibility:browser # stops at a pre-existing failure, see below
  • Lint, type check, unit tests (13,219), the architecture inventory, both performance gates and the project-files journey pass. The browser suite passes (4,805 of 4,806) except src/platform/ui/ConfirmDialog.browser.test.tsx > reconciles a confirmation closed while its Activity is hidden, and the accessibility run stops at the hidden-maintenance journey with the same symptom (an alert dialog not hidden while its Activity is hidden), so the journeys after it do not run in sequence. Both reproduce on the base commit in this environment.
  • Backend: pytest tests/app/services/workflow_records passes; Ruff clean.
  • Docs: the docs site checks were not run (the docs toolchain needs a newer Node than this environment had). Two workflow docs pages and the editor tour diagram are updated; the introduction page's screenshots still show the previous node rows.
  • Manual, library: open the library from the editor; switch places and check tags; change the sort key and direction; switch Grid and List from the view menu and reopen the dialog to see the choice kept; select, double-click, right-click and use More on cards and rows; narrow the window below 56rem and open the summary sheet; check In this project shows the linked template and its update actions.
  • Manual, preview: press Preview or Space on a workflow; step with the rail's arrows; switch Nodes, Inputs and Graph and check the tabs do not move; select a node, follow a From/To chip, press Escape twice (card, then back to the list); use Show all and Simplify. On a widget, open View graph: check the run facts in the rail, the Invoke control's issue list when the route is blocked, and that Escape closes the issue card before the dialog.
  • Manual, editor: leave a required field empty and check the red outline, the field mark and the status panel's wording; select the invalid node; hover and Tab into a row to see its actions; add a field to the form and check the label; collapse two connected nodes, click the merged line twice and delete it, then undo; select a node and check the other edges dim; export a PNG with collapsed nodes and with a node selected; switch workflows from the header and create one there; use Export in the widget menu.
  • Performance: scrolling the 1,000-workflow fixture on a production build went from about 30 long tasks in four seconds (first iteration of the new cards) to zero in both layouts (grid 16.7 ms mean frame, list about 20 ms). The library dialog loads on first open, so every editor route ships about 72 KB less script at the same request counts. The preview dialog and its parts are no longer in the workflow editor's startup: that route keeps 67 startup requests. The node work grows the workflow editor's largest on-demand chunk from 210,962 to 220,580 bytes after moving the status and info panels into their own chunk; the browser baseline records that figure and the new modules in each route's file list, with no other byte, request or timing figure changed. Selecting every edge in a 1,000-node, 1,600-edge graph costs 0.02 ms with nothing collapsed and 0.33 ms with every third node collapsed.

Review

Findings resolved during review, library: a stored name sort flipping to Z–A on upgrade (a missing direction now takes the key's natural default); place totals being probed on every mount; the last-run touch not refreshing the store; project search ignoring tags; the browse filter not resetting on close; background refreshes while the dialog was closed (now held by an in-use lease); the selected card staying in a loading state after its page was evicted; keyboard focus landing on list placeholders; hover quick actions shifting rows; a stale kept position highlighting the wrong row after a reorder; and a centre click on a small card landing on the hidden quick-action strip.

Preview: preview mode surviving a library close that bypassed its own controls; a node card for a node that left the document crashing the stage; a stale find count; a late step selecting after the preview closed; stage controls painting over the node card; the editor loading the preview dialog at startup through a shared barrel; a starter-models fetch triggered by opening View graph; and the footer's Invoke advertising a shortcut that is suppressed while the dialog is open.

Editor: exported images dropping edges between collapsed nodes and dimming edges when a node was selected; a merged edge being reconnectable or losing its selection on a second click, so only part of it moved or deleted; mass edge selection being quadratic; a failed load of an on-demand panel or of the switcher's list crashing the editor chrome (both now fall back and retry); the switcher's actions shifting as its list arrived, so a quick click could land on another row; the launchpad's Model Manager reaching the editor runtime through the switcher's scrolling list; and split node titles losing the space at their split point.

Remaining limits: the platform Scrollable rewrites four inherited custom properties on every scroll event, which restyles every descendant; the grid and the List pin them locally. Recent in a multi-user install lists globally opened bundled templates. Tag filtering uses the server's substring match. On very tall windows showing more than about 44 list rows, the farthest rows stay placeholders because the browse window holds 60 positions. A preview graph that does not fit a narrow stage opens at its readable zoom with a minimap. The publication review still opens the preview dialog over its own dialog. The Current Image node has no previous/next buttons: nothing outside the Gallery and Preview widgets can step the gallery selection yet. Whether outputs should share rows with inputs, as in the legacy editor, is undecided; rows keep today's order. Ports are centred on a label's lowercase letters, so all-capital labels sit up to two pixels off. The first time the switcher opens in a page session its rows redraw once when the themed scroll container arrives. Light themes and Firefox/Safari were not checked by eye for the node changes.

Compatibility / Rollout

  • Preferences: workflowLibrarySortDirection, workflowLibraryCardSize and workflowLibraryLayout are new keys with defaults; unknown or missing values normalise to the defaults, and the previous workflowLibrarySort values remain valid. The preview, node and menu changes add no persisted state; collapsing nodes never rewrites a workflow's edges.
  • API: WorkflowRecordOrderBy gains last_run_at; openapi.json and schema.ts are regenerated. No schema migration: the column already exists and is written only through the existing last_run_at endpoint.
  • Performance baselines: performance/architecture-baseline.json and performance/browser-baseline.json are edited by hand for the file-list changes and the one chunk figure above, keeping their recorded timings.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

Add last_run_at as a library sort key and regenerate the API contracts. A completed run of a project
workflow linked to a non-bundled template now marks that template as run, through a wrapper around the
queue history routing that bumps once per completion, owner-scoped and fenced. The mock backend honours
the new order key, has_been_opened and the last_run_at touch.
Replace the library tabs with a places rail (In this project, Recent, My library, Templates, Shared with me)
and multi-select tag checkboxes with counts. One toolbar on every place: search, a sort menu with direction,
a view menu with card size and Fill/Fit, and New workflow on In this project. Sort, direction and card size
are account preferences; search and tags reset when the dialog closes. Recent lists opened workflows by
last opened; This project filters and sorts client-side. The browse store keys cached views by the whole
filter, probes place totals once per opening, refreshes order-touched views only while the dialog is in
use, and keeps the selected workflow's list copy enriched when its page is reloaded.
…d quick actions

Cards pack at 150px (up to six columns) or 220px in the large size. A workflow without a thumbnail shows a
silhouette drawn from its node positions. Each card carries a readiness dot: ready, models to install, or
node packs missing. Hovering a card offers Open, Preview and More; keyboard paths are unchanged. The grid
dispatches card events from data attributes, memoises cards, renders nothing while scrolling within a row,
and pins the scroll area's inherited custom properties so scrolling no longer restyles every card. Focus
survives a column-count change.
The library rail leads with the preview, name and Open, then facts (base, nodes, callable), description,
tags, requirements with missing node packs named, copies in this project, updated and created. The project
rail names the linked template and its revision, flags when the template is newer, and offers Update from
template and Update library template. Below 56rem the rails give way to a summary sheet whose details
expand over the grid and restore scroll and focus on collapse.
The library dialog no longer ships in the editor startup bundle: the widget chrome loads it lazily on the
first opening, preloads it when the library button is hovered or focused, and keeps it mounted afterwards.
Two shared module groups absorb the chunks the split exposed. Every editor route loads about 72 KB less
script at the same request counts; both performance baselines are re-recorded to keep the saving.
Update the editor reference for places, tags, the sort and view menus, readiness dots, the linked-template
actions and the narrow layout.
…y layout

The wide-layout rule set the start border and then reset it with the border-width shorthand in the same declaration, so the detail rail met the cards with no divider. Set each side's width explicitly.
Open returns to the pinned footer as the full-width primary action under Preview graph, beside More, in both the library and project rails. A picker's commit action belongs in a fixed, large footer slot rather than a small button competing with the name. The narrow sheet's summary bar keeps its own Open and More, so the footer pair stays wide-only.
List gains onRangeChange, the virtualizer's visible row range reported only when it changes, for
server-windowed lists that load pages as they scroll, and pins the scroll area's inherited overflow custom
properties on its row container so a scroll no longer restyles every row. ListItem gains id, on the primary
button, so an external menu or dialog can name the row as its trigger for focus return, and controls, applied
as aria-controls while the button is expanded, so a dialog's focus trap admits a menu the row opened.
The view menu's first group picks Grid or List; the choice is an account preference, and the card size and
thumbnail display options apply to the grid only. The list is the platform List at comfortable density: a
small thumbnail or silhouette, the name with the Public and Active badges, the base badge, readiness dot and
the same fact text as the cards, a date column for the current order, and Open, Preview and More, shown on
hover and focus at a constant width. Click selects, double-click opens, right-click, Shift+F10 and More
anchor the same menu as the cards. Rows of a windowed place are keyed by position: a placeholder is a busy row
labelled for screen readers that becomes the entry when its page arrives, so focus and the tab stop survive
End, Home and a layout switch. Range loading, visible demand and the per-filter scroll anchor are shared with
the grid through one demand module, and the readiness and fact derivation lives in one helper for both.
In the wide layout the chips row had no grid area and was auto-placed along the dialog's bottom edge, lifting
the body whenever a tag was checked. The narrow Tags button already counts the checked tags and the result
line names them, so the row goes rather than being hidden per layout.
The hidden quick-action strip still took pointer events while transparent, so a press that began on the
card could end on Open and select nothing, and once the pointer had rested on a small card its centre lay
under the revealed Open. The strip now ignores the pointer until it is shown and sits over the top of the
image, clear of the centre; the in-project marker moves to the image's bottom-left so a long label never
covers it.
A returning grid waited for its ResizeObserver's first report to learn its width, and Chromium can hold that
report back past the frame, leaving the grid on placeholders with no range requested. The width is now read
from the viewport before the first paint; the observer still tracks later changes.
Preview and More sit on an opaque panel fill so they read over any thumbnail, and that fill replaced the
outline recipe's hover. They now lay the recipe's tint over the panel on hover, and More while its menu is
open.
Right-click, Shift+F10 and the More button on a card or list row open the actions menu for that workflow and
leave the selection and the detail rail alone; the rail's and the sheet's More still act on the selection.
The action set and its rename and delete dialogs move into one hook used by the rail and by a context-menu
host, acting by id and revision with write access captured when a dialog opens. Deleting another workflow
keeps the selection; the menu closes when its target leaves the listing; a press on one menu's trigger
while another is open swaps them in one press; one pending Duplicate per workflow across both surfaces.
@github-actions github-actions Bot added python PRs that change python files services PRs that change app services frontend PRs that change frontend files python-tests PRs that change python tests docs PRs that change docs labels Nov 10, 2026
A modal traps focus, so focus leaving it is never a dismissal; under StrictMode a closing child dialog returned
focus into itself and closed the dialog that opened it. Escape, close controls and outside presses still dismiss.
A pure preview model built from a workflow document (titles, markers, folded helper inputs, layered layout),
and the node view, inputs view and stage parts that draw it. No surface uses them yet.
The invocation state and submission accept a source to lock the route to, and the Invoke button can show that
route as fixed, report jumps to a setting, and render at control size. The top bar's own instance is unchanged.
…ew graph

The library's Preview replaces its list with a stage (Nodes, Inputs and Graph tabs, find, stepper in the rail)
instead of a stacked dialog. View graph and the publication review use the same view, with the top bar's Invoke
control bound to the previewed source and run facts in a side rail. The old list, side panel and Open-as menu go;
performance baselines drop their sources.
Card, row and rail menus for library and project workflows drop the per-item description and the wider minimum
width that made room for it.
… colour

The export clone referenced theme variables by names that do not exist for flat dotted tokens, so node borders
came out in the text colour.
…l is missing

The inputs view used a border token the theme does not define, so the border fell back to the text colour.
…essed control

Under a zoomed canvas the text area's bottom edge lands inside a device pixel and the masked fade let a slice of
text through. Inside a host marked as a control well the fade is an overlay that reaches the outer edge and
redraws the border; elsewhere the mask is unchanged.
Node state is derived in one place: invalid and failed nodes share a red outline and header, the selection ring
takes the node's tone, and keyboard focus is a separate ring. The header gets a smaller title and ghost status
and info buttons with panels; rows show their actions on hover or focus, mark in-form fields on the label and
invalid fields on the control, and controls sit in a recessed well. Collapsed nodes show one merged port per side
with counted edges, a selected node's edges are emphasised, the minimap tints invalid and running nodes, and the
Current Image node follows the gallery selection and can be dragged. The editor chunk's recorded size and file
list in the performance baseline are updated for this.
…ailed load

A lazily imported module renders behind an error boundary with one fallback for loading and failure, retries on
the next mount, and renders directly once loaded.
Labels are trimmed to the body of their text so a port lines up with the letters, not the line box; ports and
edge anchors do not move. Rename inputs use the size of the text they replace, and the node panels load through
the shared on-demand helper.
…the workflow menu

The workflow name opens a menu of the project's workflows with New workflow and Workflow library beneath it. The
widget menu groups its own items under This workflow and Import and export, with an Export submenu that includes
Download as PNG; the export flow is shared with the toolbar and loads on first use. The performance baseline lists
the two modules that now load with every editor route.
@joshistoast joshistoast changed the title feat(workflows): redesign the workflow library feat(workflows): redesign the workflow library, previews and editor nodes Nov 10, 2026
@joshistoast
joshistoast marked this pull request as ready for review November 10, 2026 12:14
…-redesign

# Conflicts:
#	invokeai/frontend/webv2/scripts/run-project-file-journey.mjs

This branch has not been deployed

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

Labels

docs PRs that change docs frontend PRs that change frontend files python PRs that change python files python-tests PRs that change python tests services PRs that change app services

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant