Skip to content

feat(canvas): type-first control layers with background detection and derived models - #10002

Open
joshistoast wants to merge 24 commits into
mainfrom
feat/control-layers-ux
Open

joshistoast wants to merge 24 commits into
mainfrom
feat/control-layers-ux

Conversation

@joshistoast

@joshistoast joshistoast commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A new control layer used to be an empty shell with a model guessed by name, a Filter button that stayed disabled until the layer had pixels, and no help choosing a ControlNet or the preprocessor that goes with it.

Control layers are now type-first. The Properties panel has fixed rows (Image, Type, Detection, Model, Weight, Steps, Priority) whose values change but whose positions never move. The user says what the image should control (Pose, Depth, Edges, Soft edges, Line art, Sketch, Color, Tile); the app picks the detector and resolves the model (explicit pick, remembered pick for the base and type, the only dedicated model, a Union model that covers the type, or asks), runs detection in the background, keeps the original beside the detected map so detection can be re-run, and explains each choice in one caption line.

  • Entry points: Layers "+" adds an empty control layer and opens its Properties; gallery images get "Use as Control" (fills the selected empty control layer, else creates one); raster layers get "Use as control layer"; a control image dropped on the canvas fills an empty layer.
  • Layer rows read "Type · Detector · weight", spin while a detector runs, and warn on issues. Invoke shows the issue count and jumps to the first offending layer.
  • The canvas shows a drop hint over an empty control layer, a status chip while detection runs, and an Original / Map / Overlay view switch for a detected layer.
  • Empty or untyped control layers only warn; Invoke stays enabled and they contribute nothing. Only a content-bearing layer that cannot resolve a model, or whose map is stale, blocks.
  • FLUX Union ControlNets now receive instantx_control_mode from the control type.
  • The adapter-kind select is gone: the kind is derived from the resolved model and adapter.kind stays persisted.
  • Arrow keys on a focused radio group or slider no longer resolve to gallery navigation, so segmented controls work by keyboard.

The source button reads "Frame" (not "Canvas") to match the bbox rename in #9961.

Related Issues / Discussions

Overlaps #9961 (canvas v7 feedback) in wording; both touch the control layer panel and the layer menu. main is merged in (schema v5 is shared with the noise adjustment from #9933).

QA Instructions

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

  • pnpm check:release: passed on the branch after merging main (lint and architecture checks; 11,336 unit tests; 3,403 browser tests; mock-backend fixtures; architecture build and browser performance gates, 49 and 47 checks; project-file journeys; accessibility journeys including the new workbench-canvas-control-layer journey).
  • Performance baselines were hand-edited for the new eager modules (owner sets) and for the byte growth of the merged locale and editor chunks. No timing baseline changed.
  • Mock-backend walk (pnpm dev:mock, POST /__models {"set":"control-sdxl"} and {"set":"control-flux"}): empty layer rows and hint card; Type menu hints (model counts, "via Union", "Not installed"); Depth with two candidates asks and remembers the pick; Reset returns focus to the trigger; T2I via Edges dims Priority; Use as Control fills then creates; Invoke "· N issues" jumps to the layer; FLUX Depth resolves to the Control LoRA; keyboard operation of the view switch with gallery focus held; detection failure reverts the view switch with a notice.
  • Not run: the real-backend walk (upload → Depth → spinner → map replaces pixels → undo; detector settings re-run; Replace › Frame; Draw; Invoke carrying the resolved adapter and FLUX instantx_control_mode; Original / Overlay rendering on screen). The mock backend cannot run detectors or decode its fixture images, so the running state, the detected map and the Original / Overlay rendering are covered by coordinator, compositor and engine tests only.
Screenshots (mock backend)

New control layer: fixed rows, "Set by type", drop hint on the canvas, row "Control Layer 1"

01-empty-control-layer Type menu on SDXL: model counts per type, "Not installed" for types without a model 02-sdxl-type-hints Depth with two candidates: "Choose a depth model" and the caption naming both; row renamed "Depth 1" 03-depth-choose-model Model menu grouped: Made for depth, Union, Purpose not set 04-model-menu-groups After a pick: "your pick for depth" with Reset 05-depth-picked-reset Pose: "your only pose model" 06-pose-only-model Edges resolves to the T2I-Adapter; Priority is disabled 07-edges-t2i-priority SD 1.5 main with a drawn Edges layer: "No control models" with the starter-models link; Invoke counts 2 issues 08-sd15-no-control-models Invoke tooltip naming the offending layer 09-invoke-issues-tooltip Type menu on FLUX dev: "via Union" for the types the Union ControlNet covers 10-flux-type-hints FLUX Depth resolves to the Control LoRA; the detector failed on the mock backend, so the Detection row shows the failure with Run again 11-flux-depth-control-lora FLUX Line art: "None for FLUX" with Find one 12-flux-lineart-none

Review

Resolved: a transform bake now rebases the kept original and holds its asset refs; undo never restarts detection; stale detection results are dropped on layer removal, input change and document replace; untyped layers with a model keep their legacy summary; the control view ends on both locks and on stroke start, reports failed decodes distinctly, and skips hidden layers; a per-frame allocation in the compositor was removed; the retained original is tracked as detached pixels under the raster budget.

Limits: SDXL "union" models (xinsir) are offered as explicit picks only until the backend exposes union control types; a remembered pick applies to every AUTO layer of the same base and type; the backend recall path still understands only legacy *_image_processor ids, which the frontend classifier accepts alongside node ids; a superseded decode keeps its reservation until it settles, so two quick view switches near the budget limit can refuse once.

Compatibility / Rollout

Canvas documents adopt schema v5 per document when a control layer carries controlType, detection, original or originalTransform (a v4 build would strip these keys and lose the original on its next autosave). v5 is shared with the noise adjustment from main; the version requirement is one function over both reasons. Documents without them stay at their current version; nothing steps down. Legacy filtered control layers keep working as map-only layers until the image is replaced. WorkbenchPreferences gains controlModelMemory with a normalizer. The graph compiler input is unchanged except the optional InstantX mode on FLUX ControlNet nodes.

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)

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Adds controlType, detection, original and originalTransform to the control layer contract with load-time repair rules and a pure derivation module; a document carrying them writes canvas schema 5.
One control layer factory remains, creating untyped layers with no default model.
Classifies installed control models by purpose and resolves a typed layer's model at read time: explicit pick, remembered pick per base and type, the only dedicated model, a covering union, else a readiness reason.
Adds the remembered-pick preference, the new validation reasons, and the FLUX InstantX control mode on union graphs.
…ngine

ControlDerivationController publishes owner-less guarded patch and map commits that pair in history, so one undo reverts an intent and its map together.
Materialize, transform, crop, generated and filter results rebase or detach the kept original; uploads can be flushed per layer.
An intent-driven coordinator debounces per layer, runs the detector graph on the kept original, retries commits refused by a modal operation, and exposes per-layer status.
It never starts from document changes, so undo and redo never re-run detection.
Owner sets gain the derivation, taxonomy, controller and coordinator modules; editor route bytes are re-recorded by hand (+1.3 %), timings and paths unchanged.
Select renders a group whose label is null under a separator, PropertyControlRow accepts a label id for aria-labelledby, and LayerFilterControls can render short enums as a segmented control.
Opt-in control-sdxl and control-flux sets carry purposeful names and both preprocessor id forms; the model PATCH mock now applies the change.
…layers

Filter runs on raster layers only and control maps come from detection; the filter recommendation, the control-source filter branches and the control Filter menu item are removed.
One fixed Properties panel: Image, Type, Detection with inline settings, Model with a caption that explains the pick, Weight, Steps and Priority. The image comes from an upload, the frame or drawing; choosing a type names the layer and picks are remembered per main model.
Owner sets gain the panel and source-action modules and drop the filter recommendation; locale and layers-widget bytes are re-recorded by hand, timings and paths unchanged.
Issues no longer carry a suggested adapter kind now that the kind is derived
from the resolved model; the sequence and attention helpers nothing called
are removed.
Layers "+" adds an untyped control layer and opens its Properties. Gallery
images gain "Use as Control", which fills the selected empty control layer or
creates one and reveals it; raster layers offer "Use as control layer" at the
top level. A control image dropped on the canvas fills an empty layer too.
The Invoke button shows the issue count, and activating it while blocked
reveals the first offending layer's Properties.
The row icon spins while a detector runs and warns on every issue code; the
summary reads "Type · Detector · weight", or says what is missing.
A selected empty control layer shows a drop hint over the canvas, hidden
while painting; detection progress is announced by a status chip at the tool
strip, so the detection row keeps only the failure reason on its retry.
Adds the control layer accessibility journey.
The layer-properties reveal modules boot with the editor so Invoke and the
image menu can open a layer's Properties; the baselines record them and the
canvas overlays.
A display-only control layer view decodes the pre-detection original under
the raster budget and draws it in place of the map, or beneath the map at
half opacity, through the layer's transform. The view never persists or
enters history and ends on any edit, stroke, lock, selection or document
change.
A ready control layer offers the view switch in the status chip slot; the
choice shows at once, follows the engine, and reports a failed or refused
original with a notice.
Plain arrow, Home and End presses on a radio group or slider no longer
resolve to gallery navigation, so segmented controls can be operated by
keyboard.
The browser baseline lists the new engine controller in the editor owner
sets.
@github-actions github-actions Bot added the frontend PRs that change frontend files label Oct 23, 2026
Locale and editor chunks grew by the merged control layer and noise adjustment code; the workflow activation chunk shrank.
The editor-canvas activation chunk grew with the merged adjustment and control layer code; the launchpad script bytes shrank.

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

frontend PRs that change frontend files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants