Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
317 commits
Select commit Hold shift + click to select a range
b91ea44
style(webv2): polish workflow editor controls
joshistoast Jun 15, 2026
f370652
fix(webv2): improve starter model selection styling
joshistoast Jun 15, 2026
2c6e459
fix(webv2): use chakra package button in more places
joshistoast Jun 15, 2026
7d3bb4e
style(webv2): no relative parent imports
joshistoast Jun 15, 2026
7986ad2
style(webv2): add lint + formatting rules
joshistoast Jun 15, 2026
2bc83f8
feat(workbench): support batched workflow runs
joshistoast Jun 15, 2026
c7b8ba6
fix(workbench): harden image context menu targets
joshistoast Jun 15, 2026
de50c6a
feat(workflow): improve editor connections and validation
joshistoast Jun 15, 2026
27a3d84
chore(frontend): enforce exhaustive deps linting
joshistoast Jun 15, 2026
1913ddb
feat(workbench): improve queue lifecycle controls
joshistoast Jun 15, 2026
785c212
feat(workbench): show progress images in gallery and preview
joshistoast Jun 15, 2026
f77387f
refactor(workbench): simplify image context menu lifecycle
joshistoast Jun 15, 2026
e628fae
feat(webv2): organize workbench components
joshistoast Jun 15, 2026
4540593
feat(webv2): add model taxonomy helpers
joshistoast Jun 17, 2026
c25b337
feat(webv2): support expanded generation policies
joshistoast Jun 17, 2026
464c162
feat(webv2): add generate model component controls
joshistoast Jun 17, 2026
bfe2a70
feat(webv2): improve model picker interactions
joshistoast Jun 17, 2026
672ae7f
fix(webv2): validate invocations against loaded models
joshistoast Jun 17, 2026
f37f9d9
feat(webv2): improve gallery refresh and image recall
joshistoast Jun 17, 2026
ce83d78
feat(webv2): improve workflow validation and editor controls
joshistoast Jun 17, 2026
4440612
feat(webv2): improve model management views
joshistoast Jun 17, 2026
618d729
fix(webv2): improve API validation error messages
joshistoast Jun 17, 2026
5000102
style(webv2): standardize workbench chrome recipes
joshistoast Jun 17, 2026
1e4341a
feat(webv2): add workbench widget runtime api
joshistoast Jun 18, 2026
7d06547
feat(webv2): add workbench widget placement commands
joshistoast Jun 18, 2026
d1739c5
perf(webv2): narrow workbench shell widget subscriptions
joshistoast Jun 18, 2026
4dcf1bc
feat(webv2): normalize workbench widget manifests
joshistoast Jun 18, 2026
1978599
fix(api): sanitize stale queue image results
joshistoast Jun 19, 2026
389551e
fix(api): secure manager endpoints
joshistoast Jun 19, 2026
811c8d3
feat(webv2): add launchpad managers
joshistoast Jun 19, 2026
d3ec7f9
perf(webv2): virtualize workflow node picker
joshistoast Jun 20, 2026
837c3f1
style(webv2): unify workflow node chrome
joshistoast Jun 20, 2026
ec6ecf1
feat(webv2): add workflow field reset controls
joshistoast Jun 20, 2026
04f6c9e
fix(webv2): preserve model default state
joshistoast Jun 20, 2026
b294cab
fix(webv2): stabilize tooltip-wrapped menu triggers
joshistoast Jun 20, 2026
37aac1a
fix(webv2): constrain gallery image board drops
joshistoast Jun 20, 2026
c393b59
style(webv2): polish workbench controls
joshistoast Jun 20, 2026
48775eb
refactor(webv2): remove layout actions widget
joshistoast Jun 20, 2026
38e827e
style(webv2): refine status bar widgets
joshistoast Jun 20, 2026
49d571b
chore(webv2): update frontend dependencies
joshistoast Jun 20, 2026
2bcbd45
feat(webv2): add configurable workbench hotkeys
joshistoast Jun 20, 2026
9f04435
refactor(webv2): extract image recall helpers
joshistoast Jun 20, 2026
e87da4d
feat(webv2): mount singleton widget hosts
joshistoast Jun 20, 2026
60abc3b
refactor(webv2): remove history controls widget
joshistoast Jun 20, 2026
36fe681
fix(webv2): respect reduced motion during first paint
joshistoast Jun 20, 2026
c4cc9bc
style(webv2): polish menus and workflow controls
joshistoast Jun 20, 2026
5fa86c8
feat(webv2): add color compensation to generate widget
joshistoast Jun 20, 2026
036fca3
fix(webv2): generation policiies lint
joshistoast Jun 20, 2026
971950f
feat(webv2): add loading screen, organize brand mark component usage
joshistoast Jun 20, 2026
a0d639d
fix(webv2): generate settings collapsible height
joshistoast Jun 20, 2026
ff698a8
feat(webv2): add resizable textarea component
joshistoast Jun 20, 2026
e133a89
feat(webv2): toggle negative prompt, remember textarea height
joshistoast Jun 20, 2026
d701cbc
feat(webv2): add some prompt actions
joshistoast Jun 20, 2026
70b921f
feat(webv2): add prompt AST, keybind weighting and experimental syntax
joshistoast Jun 20, 2026
5fdbaf0
feat(webv2): update model picker and base identity palettes
joshistoast Jun 20, 2026
a667a13
feat(webv2): better layout presets
joshistoast Jun 21, 2026
784625d
feat(webv2): add prompt trigger to neg, trigger on '<'
joshistoast Jun 21, 2026
ea5c016
feat(webv2): state selection overhaul
joshistoast Jun 26, 2026
125c067
chore(webv2): upgrade deps, apply new linting and style formatting
joshistoast Jun 26, 2026
2c9f62d
feat(queue): retain denoise image preview while final processes
joshistoast Jun 26, 2026
bfefc0d
fix(gallery): prevent repeated total update dispatches
joshistoast Jun 26, 2026
693cbb3
feat(workbench): rename graph invocation source ids
joshistoast Jun 26, 2026
73b5a9e
feat(queue): show project queue progress in tabs
joshistoast Jun 26, 2026
15e147c
style(workbench): polish widget rail slot styling
joshistoast Jun 26, 2026
3660e0f
fix(generate): default missing batch count
joshistoast Jun 26, 2026
a103845
feat(model-cache): report clear results and current usage
joshistoast Jun 28, 2026
979f964
feat(queue): support project-scoped queue queries
joshistoast Jun 28, 2026
6ca1872
feat(workbench): tag queue submissions with project origin
joshistoast Jun 28, 2026
eccc112
fix(workbench): preserve live progress images while streaming
joshistoast Jun 28, 2026
9de16cc
feat(queue): add server-backed queue widget
joshistoast Jun 28, 2026
eeb0094
Merge remote-tracking branch 'v7/main' into v7
joshistoast Jun 28, 2026
611fee4
fix(api): formatting
joshistoast Jun 28, 2026
dc02852
fix(ci): sort service imports
joshistoast Jun 28, 2026
a885fc5
fix(ci): format project records tests
joshistoast Jun 28, 2026
39cb546
fix(ci): update generated api schema
joshistoast Jun 28, 2026
fa536d3
fix(ci): regenerate api types with locked deps
joshistoast Jun 28, 2026
83b7c70
fix(tests): update stale router and queue test doubles
joshistoast Jun 28, 2026
0b0793e
perf(workflows): index workflow graph lookups
joshistoast Jun 29, 2026
dc87fe9
perf(workflows): optimize large workflow rendering
joshistoast Jun 29, 2026
d84db2e
fix(workbench): harden persistence for large projects
joshistoast Jun 29, 2026
64f219c
feat(workbench): show fallback for lazy widgets
joshistoast Jun 29, 2026
6bd123e
feat(router): use workbnch splash screen
joshistoast Jun 29, 2026
c0a7419
feat(workbench): add project-scoped diagnostics logger
joshistoast Jun 29, 2026
7bab0c2
feat(workbench): expose diagnostics developer settings
joshistoast Jun 29, 2026
d9c6a72
feat(workbench): route errors through diagnostics
joshistoast Jun 29, 2026
d90b53c
feat(workbench): add diagnostic metadata to error producers
joshistoast Jun 29, 2026
25a3716
feat(workbench): record performance timings in diagnostics
joshistoast Jun 29, 2026
a6b35ea
feat(workbench): render diagnostics entries in diagnostics widget
joshistoast Jun 29, 2026
3cd31f4
Merge pull request #2 from invoke-ai/v7
joshistoast Jun 29, 2026
3d9b0fd
Merge branch 'invoke-ai:main' into main
joshistoast Jun 29, 2026
71ad72c
fix(gallery): progress placement, formatting
joshistoast Jun 29, 2026
2dd44c1
Merge pull request #4 from invoke-ai/fix/gallery-grid-progress-placement
joshistoast Jun 29, 2026
601c504
feat(i18n): implement i18n
joshistoast Jul 1, 2026
7a13294
Merge pull request #5 from invoke-ai/feat/basic-i18n
joshistoast Jul 1, 2026
da62d88
Merge remote-tracking branch 'upstream/main'
joshistoast Jul 1, 2026
1a8d7d9
feat(generate): reference images, ui tweaks
joshistoast Jul 2, 2026
b7af45d
Merge pull request #6 from invoke-ai/feat/generate-reference-images
joshistoast Jul 2, 2026
b166e1f
Merge remote-tracking branch 'upstream/main'
joshistoast Jul 2, 2026
571b8be
feat(canvas): new canvas
joshistoast Jul 9, 2026
1de0204
fix(canvas): inpainted queue items don't get fully regenerated
joshistoast Jul 9, 2026
7074302
fix(canvas): inpaint outputs stage more than one result
joshistoast Jul 9, 2026
8d96d85
fix(canvas): qol updates
joshistoast Jul 9, 2026
271fe1a
fix(canvas): order staging slots by queue execution
joshistoast Jul 9, 2026
f6d3456
feat(canvas): preview queued staging progress
joshistoast Jul 9, 2026
35df523
feat(canvas): add progress staging auto-switch
joshistoast Jul 9, 2026
67b6892
refactor(canvas): unify floating canvas controls
joshistoast Jul 9, 2026
fde10b7
style(layers): polish layer list interactions
joshistoast Jul 9, 2026
5513501
feat(canvas): add layer row context menus
joshistoast Jul 9, 2026
fa59a9f
feat(canvas): more qol updates
joshistoast Jul 9, 2026
beeb2f0
feat(canvas): layer context actions registry
joshistoast Jul 9, 2026
fbceff5
feat(canvas): add shared layer pixel export primitive
joshistoast Jul 9, 2026
fa36f6f
feat(canvas): add baked layer export primitive
joshistoast Jul 9, 2026
709ea52
feat(canvas): add 'save layer to assets'
joshistoast Jul 9, 2026
a3e2383
feat(canvas): add copy layer to clipboard
joshistoast Jul 9, 2026
f174719
feat(canvas): add crop layer to bbox
joshistoast Jul 9, 2026
0d5add5
feat(canvas): add copy layer to raster
joshistoast Jul 9, 2026
63d4ba4
feat(canvas): add layer copy and conversion parity
joshistoast Jul 9, 2026
2213173
feat(canvas): add boolean raster operations
joshistoast Jul 9, 2026
5c0fbda
feat(canvas): extract masked canvas areas
joshistoast Jul 9, 2026
4d4993f
feat(canvas): add control filter menu action
joshistoast Jul 9, 2026
2a9ce2b
fix(canvas): preserve live layer action state
joshistoast Jul 9, 2026
07fec99
refactor(canvas): organize layer context menu like legacy
joshistoast Jul 9, 2026
eb232eb
feat(canvas): add wavy denoising strength track
joshistoast Jul 9, 2026
50c0f7f
feat(canvas): clear selected masks with shift c
joshistoast Jul 9, 2026
a9d6ccd
fix(canvas): preserve cleared mask persistence
joshistoast Jul 9, 2026
3fb8248
fix(canvas): match legacy denoising wave track
joshistoast Jul 9, 2026
1187d7f
fix(canvas): move denoising wave beside label
joshistoast Jul 9, 2026
1bf4910
feat(canvas): move blend mode before opacity
joshistoast Jul 9, 2026
bb85170
fix(canvas): improve curves editor visibility
joshistoast Jul 9, 2026
47afab0
refactor(canvas): complete layer action registry
joshistoast Jul 9, 2026
dfe719b
fix(canvas): harden layer raster operations
joshistoast Jul 9, 2026
2c3d4f0
feat(canvas): add raster filter actions
joshistoast Jul 10, 2026
f061653
feat(canvas): create masks with segment anything
joshistoast Jul 10, 2026
74ce5fe
feat(canvas): run workflows from layers
joshistoast Jul 10, 2026
0a17730
fix(canvas): make raster structural history failure-atomic
joshistoast Jul 10, 2026
bd1cc55
fix(canvas): anchor new session menu
joshistoast Jul 10, 2026
3dd9d52
fix(layers): stop space from activating row drag
joshistoast Jul 10, 2026
e3595d3
test(layers): remove brittle dnd wiring assertions
joshistoast Jul 10, 2026
800396d
fix(canvas): restore curves plot structure
joshistoast Jul 10, 2026
99fea91
test(canvas): cover curves pointer cancellation
joshistoast Jul 10, 2026
b660ea7
test(canvas): verify curves drag completion
joshistoast Jul 10, 2026
f2bc351
fix(canvas): make layer thumbnails render independently
joshistoast Jul 10, 2026
1aee9b3
fix(canvas): cancel stale thumbnail rasterization
joshistoast Jul 10, 2026
1712d11
fix(canvas): release thumbnail bitmap resources
joshistoast Jul 10, 2026
5ec7d3c
fix(canvas): clean up unpublished thumbnail bitmaps
joshistoast Jul 10, 2026
220df23
fix(canvas): release deferred shared thumbnail bitmaps
joshistoast Jul 10, 2026
f3ced7b
feat(canvas): render faithful layer thumbnails
joshistoast Jul 10, 2026
d04b027
fix(canvas): bound thumbnail effect rendering
joshistoast Jul 10, 2026
507fbca
feat(canvas): add guarded canvas operation sessions
joshistoast Jul 10, 2026
2cee031
fix(canvas): harden canvas operation lifecycle
joshistoast Jul 10, 2026
6c55a44
fix(canvas): require synchronous operation commits
joshistoast Jul 10, 2026
6f74a44
feat(canvas): support interactive segment anything sessions
joshistoast Jul 11, 2026
51555d8
fix(canvas): harden interactive sam processing
joshistoast Jul 11, 2026
d4ce951
fix(canvas): finalize sam session lifecycle
joshistoast Jul 11, 2026
0a97585
fix(canvas): isolate sam session subscribers
joshistoast Jul 11, 2026
7ba03d8
fix(canvas): clear stale sam session errors
joshistoast Jul 11, 2026
16a5ebb
fix(canvas): allow sam retry after invalidation
joshistoast Jul 11, 2026
53c9460
fix(canvas): guard sam export preparation
joshistoast Jul 11, 2026
bd0a5b3
feat(canvas): add select object canvas tool
joshistoast Jul 11, 2026
335564f
fix(canvas): harden select object tool lifecycle
joshistoast Jul 11, 2026
d5aed49
feat(canvas): move select object into canvas controls
joshistoast Jul 11, 2026
743e31e
fix(canvas): guard select object commits
joshistoast Jul 11, 2026
62c48e2
fix(canvas): preserve replacement sam operations
joshistoast Jul 11, 2026
b58197e
feat(canvas): move layer filtering into canvas controls
joshistoast Jul 11, 2026
ab66f48
chore: remove committed implementation plan
joshistoast Jul 11, 2026
b49c2e0
fix(canvas): harden canvas filter operations
joshistoast Jul 11, 2026
8e5ca43
feat(canvas): move layer workflows into canvas controls
joshistoast Jul 11, 2026
6f4482f
Revert "feat(canvas): move layer workflows into canvas controls"
joshistoast Jul 11, 2026
2e7e927
fix(canvas): close canvas operation edit races
joshistoast Jul 11, 2026
a8000a6
feat(canvas): add legacy control preprocessors
joshistoast Jul 11, 2026
4630941
fix(canvas): match legacy filter parameter behavior
joshistoast Jul 11, 2026
7a58a5d
fix(canvas): align control adapter defaults
joshistoast Jul 11, 2026
077f7a8
feat(canvas): support z-image controls
joshistoast Jul 11, 2026
ad68879
fix(canvas): normalize z-image control state
joshistoast Jul 11, 2026
80a26b3
fix(canvas): guard malformed canvas snapshots
joshistoast Jul 11, 2026
f1632a9
fix(canvas): validate persisted snapshot layers
joshistoast Jul 11, 2026
ace7ce0
fix(canvas): preserve filter geometry and keyboard reorder
joshistoast Jul 11, 2026
5c53985
fix(canvas): keep operation controls visible
joshistoast Jul 11, 2026
756d523
fix(canvas): enforce operation lock boundaries
joshistoast Jul 11, 2026
6c423b7
fix(canvas): interrupt locked canvas operations
joshistoast Jul 11, 2026
04dad82
fix(canvas): segment canvas bbox composite
joshistoast Jul 11, 2026
2285735
fix(canvas): guard sam composite exports
joshistoast Jul 11, 2026
4edf679
fix(canvas): harden sam processing feedback
joshistoast Jul 12, 2026
ee831db
fix(canvas): validate sam preview output
joshistoast Jul 12, 2026
b495512
fix(canvas): reconcile sam utility results
joshistoast Jul 12, 2026
11cbe77
fix(canvas): contain utility queue seams
joshistoast Jul 12, 2026
cdb2f92
feat(canvas): redesign canvas operation panels
joshistoast Jul 12, 2026
5d19324
fix(canvas): make operation panels responsive
joshistoast Jul 12, 2026
ca14137
fix(canvas): close layer properties on operations
joshistoast Jul 12, 2026
3b58222
fix(canvas): close operation integration gaps
joshistoast Jul 12, 2026
d91ee66
fix(canvas): reset and reuse sam sources
joshistoast Jul 12, 2026
2cbeba9
fix(sam): update sam2 post-processing
joshistoast Jul 12, 2026
25aac2f
fix(canvas): preserve utility error details
joshistoast Jul 12, 2026
c78911f
fix(canvas): compact select object controls
joshistoast Jul 12, 2026
37001ef
fix(canvas): match legacy select object layout
joshistoast Jul 12, 2026
0cc9923
fix(canvas): preserve compact panel behavior
joshistoast Jul 12, 2026
e4e215a
fix(canvas): align queue event error types
joshistoast Jul 12, 2026
89138bc
feat(canvas): redesign SAM tool options
joshistoast Jul 12, 2026
44a5879
fix(canvas): SAM behavior matches legacy
joshistoast Jul 13, 2026
859c805
fix(canvas): define layer filter launch policy
joshistoast Jul 13, 2026
08398d9
fix(canvas): handle layer filter launch eligibility
joshistoast Jul 13, 2026
3af37f1
test(canvas): cover layer filter launch interactions
joshistoast Jul 13, 2026
8b1fc7f
feat(canvas): define control pixel edit targets
joshistoast Jul 13, 2026
b7c20e3
feat(canvas): add layer snapshot history
joshistoast Jul 13, 2026
9730be3
feat(canvas): paint directly on control layers
joshistoast Jul 13, 2026
5bce614
fix(canvas): make control paint commits failure atomic
joshistoast Jul 13, 2026
4055000
feat(canvas): edit control layers through selections
joshistoast Jul 13, 2026
80aa6d8
fix(canvas): skip no-op control selection edits
joshistoast Jul 13, 2026
e398b06
fix(canvas): make control selection rollback idempotent
joshistoast Jul 13, 2026
ae065f9
fix(canvas): make control edit rollback exact
joshistoast Jul 14, 2026
2fd03e2
fix(canvas): keep SAM results out of gallery
joshistoast Jul 14, 2026
8756a6d
feat(canvas): auto-process filter drafts in session
joshistoast Jul 14, 2026
931269b
feat(canvas): expose filter auto-process on the engine
joshistoast Jul 14, 2026
7901ff2
test(canvas): add autoProcess to filter session fixture
joshistoast Jul 14, 2026
350f805
refactor(canvas): extract shared operation status slot
joshistoast Jul 14, 2026
ead4b2f
feat(canvas): compact filter controls for the operation bar
joshistoast Jul 14, 2026
e4f5c00
feat(canvas): slim the filter operation bar
joshistoast Jul 14, 2026
606675c
refactor(canvas): remove unused canvas operation panel
joshistoast Jul 14, 2026
6a10600
fix(canvas): clear pending filter auto-run on interrupt
joshistoast Jul 14, 2026
bb60f4d
style(canvas): flatten filter bar to a single row
joshistoast Jul 14, 2026
45e2367
test(canvas): pin auto-process in-flight guard
joshistoast Jul 14, 2026
b1f59d3
fix(canvas): fully dispose filter session after commit fallback
joshistoast Jul 14, 2026
1096b9c
refactor(canvas): let the engine own filter commit teardown
joshistoast Jul 14, 2026
a57d7bb
perf(canvas): refactor canvas engine
joshistoast Jul 14, 2026
d4d9774
feat(canvas): merge visible legacy parity
joshistoast Jul 14, 2026
48698e5
feat(canvas): add reusable raster composite export
joshistoast Jul 16, 2026
cd8e8d0
feat(canvas): add gallery save service
joshistoast Jul 16, 2026
0502607
feat(canvas): add save-to-gallery context menu
joshistoast Jul 16, 2026
8d34539
fix(canvas): harden gallery context saves
joshistoast Jul 16, 2026
61f1f88
fix(canvas): isolate surface context menu boundary
joshistoast Jul 16, 2026
b4a623f
refactor(canvas): support atomic batch layer insertion
joshistoast Jul 16, 2026
81ca358
feat(canvas): add typed gallery image imports
joshistoast Jul 16, 2026
7286774
feat(gallery): send images to typed canvas layers
joshistoast Jul 16, 2026
97e7501
fix(gallery): harden canvas import feedback
joshistoast Jul 16, 2026
0f1c832
fix(dnd): route gallery drags to canvas targets
joshistoast Jul 16, 2026
d2ebe67
test(dnd): cover source-region collision routing
joshistoast Jul 16, 2026
3c93f5d
feat(canvas): add legacy gallery image drop zones
joshistoast Jul 16, 2026
c16cc57
refactor(canvas): share compositor plan types
joshistoast Jul 16, 2026
7d5110a
fix(canvas): color picker component mounting
joshistoast Jul 16, 2026
2f9fff9
fix(canvas): minor ui tweaks
joshistoast Jul 16, 2026
4867f50
fix(canvas): color picker mount test
joshistoast Jul 16, 2026
866289b
docs: design canvas context menu groups
joshistoast Jul 16, 2026
90dbb9b
fix(canvas): restore legacy context menu groups
joshistoast Jul 16, 2026
391e804
feat(canvas): small menus as default
joshistoast Jul 16, 2026
a008d0b
feat(canvas): harden engine ownership and raster transactions
joshistoast Jul 16, 2026
545db32
test(canvas): add real-browser acceptance coverage
joshistoast Jul 16, 2026
4d86917
docs(canvas): document engine ownership invariants
joshistoast Jul 16, 2026
f20d433
fix(ci): playwright and linux cpu tests
joshistoast Jul 16, 2026
c9517b9
Merge pull request #9 from invoke-ai/feat/canvas
joshistoast Jul 16, 2026
4315169
Merge upstream/main into main
joshistoast Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 6 additions & 1 deletion .github/actions/install-frontend-deps/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
name: install frontend dependencies
description: Installs frontend dependencies with pnpm, with caching
inputs:
working-directory:
description: Directory to install dependencies in
required: false
default: invokeai/frontend/web
runs:
using: 'composite'
steps:
Expand Down Expand Up @@ -30,4 +35,4 @@ runs:
- name: install frontend dependencies
run: pnpm install --prefer-frozen-lockfile
shell: bash
working-directory: invokeai/frontend/web
working-directory: ${{ inputs.working-directory }}
41 changes: 37 additions & 4 deletions .github/workflows/frontend-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ on:
type: boolean
default: true

defaults:
run:
working-directory: invokeai/frontend/web

jobs:
frontend-checks:
runs-on: ubuntu-latest
timeout-minutes: 10 # expected run time: <2 min
defaults:
run:
working-directory: invokeai/frontend/web
steps:
- uses: actions/checkout@v6

Expand Down Expand Up @@ -94,3 +93,37 @@ jobs:
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm lint:knip'
shell: bash

frontend-webv2-checks:
runs-on: ubuntu-latest
timeout-minutes: 10 # expected run time: <2 min
defaults:
run:
working-directory: invokeai/frontend/webv2
steps:
- uses: actions/checkout@v6

- name: check for changed webv2 files
if: ${{ inputs.always_run != true }}
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
webv2:
- 'invokeai/frontend/webv2/**'

- name: install dependencies
if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }}
uses: ./.github/actions/install-frontend-deps
with:
working-directory: invokeai/frontend/webv2

- name: lint
if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm run lint'
shell: bash

- name: build
if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm run build'
shell: bash
47 changes: 43 additions & 4 deletions .github/workflows/frontend-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ on:
type: boolean
default: true

defaults:
run:
working-directory: invokeai/frontend/web

jobs:
frontend-tests:
runs-on: ubuntu-latest
timeout-minutes: 10 # expected run time: <2 min
defaults:
run:
working-directory: invokeai/frontend/web
steps:
- uses: actions/checkout@v6

Expand All @@ -63,3 +62,43 @@ jobs:
if: ${{ steps.changed-files.outputs.frontend_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm test:no-watch'
shell: bash

frontend-webv2-tests:
runs-on: ubuntu-latest
timeout-minutes: 10 # expected run time: <2 min
defaults:
run:
working-directory: invokeai/frontend/webv2
steps:
- uses: actions/checkout@v6

- name: check for changed webv2 files
if: ${{ inputs.always_run != true }}
id: changed-files
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96
with:
files_yaml: |
webv2:
- '.github/workflows/frontend-tests.yml'
- 'invokeai/frontend/webv2/**'

- name: install dependencies
if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }}
uses: ./.github/actions/install-frontend-deps
with:
working-directory: invokeai/frontend/webv2

- name: vitest
if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm test'
shell: bash

- name: install Chromium
if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm exec playwright install chromium'
shell: bash

- name: vitest browser
if: ${{ steps.changed-files.outputs.webv2_any_changed == 'true' || inputs.always_run == true }}
run: 'pnpm test:browser'
shell: bash
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ env/
venv/
ENV/

# Generated stuff
invokeai.yaml
invokeai.example.yaml

/models/
/databases/
/configs/
/nodes/
/outputs/

# Spyder project settings
.spyderproject
.spyproject
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Canvas Context Menu Groups Design

## Goal

Restore the legacy canvas context-menu organization with labeled layer and canvas groups while keeping Delete as the final action.

## Design

- Canvas-surface context menus use Chakra `Menu.ItemGroup` and `Menu.ItemGroupLabel` primitives with the established uppercase, subtle label styling.
- A context menu opened over a layer labels the layer-scoped actions with the singular layer type: Raster Layer, Control Layer, Inpaint Mask, or Regional Guidance.
- Canvas-wide actions are labeled Canvas. Save to Gallery remains in this canvas group and appears before the terminal Delete action.
- Delete remains a separated standalone danger action at the bottom so no non-destructive action appears beneath it.
- A context menu opened over empty canvas space shows only the Canvas group.
- Layer-panel kebab and row context menus remain unchanged because the legacy panel menus were ungrouped.

## Boundaries

- Reuse existing translations for Canvas and singular layer types.
- Preserve all existing handlers, disabled states, submenu behavior, and menu positioning.
- Add no state, effects, dependencies, or action-registry coupling.

## Verification

- Add regression coverage for the surface-menu group model and render order.
- Verify each layer type resolves to the matching legacy label.
- Verify the optional Canvas group precedes the terminal danger section and panel menus remain ungrouped when labels are omitted.
- Run the focused context-menu tests, frontend typecheck, formatting check, and full frontend test suite.
3 changes: 3 additions & 0 deletions invokeai/app/api/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from invokeai.app.services.names.names_default import SimpleNameService
from invokeai.app.services.object_serializer.object_serializer_disk import ObjectSerializerDisk
from invokeai.app.services.object_serializer.object_serializer_forward_cache import ObjectSerializerForwardCache
from invokeai.app.services.project_records.project_records_sqlite import ProjectRecordsSqlite
from invokeai.app.services.session_processor.session_processor_default import (
DefaultSessionProcessor,
DefaultSessionRunner,
Expand Down Expand Up @@ -189,6 +190,7 @@ def initialize(
style_preset_image_files = StylePresetImageFileStorageDisk(style_presets_folder / "images")
workflow_thumbnails = WorkflowThumbnailFileStorageDisk(workflow_thumbnails_folder)
client_state_persistence = ClientStatePersistenceSqlite(db=db)
project_records = ProjectRecordsSqlite(db=db)
users = UserService(db=db)

services = InvocationServices(
Expand Down Expand Up @@ -223,6 +225,7 @@ def initialize(
style_preset_image_files=style_preset_image_files,
workflow_thumbnails=workflow_thumbnails,
client_state_persistence=client_state_persistence,
project_records=project_records,
users=users,
)

Expand Down
4 changes: 4 additions & 0 deletions invokeai/app/api/routers/_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def assert_image_owner(image_name: str, current_user: CurrentUserOrDefault) -> N
"""
if current_user.is_admin:
return
if not ApiDependencies.invoker.services.image_records.exists(image_name):
raise HTTPException(status_code=404, detail="Image not found")
owner = ApiDependencies.invoker.services.image_records.get_user_id(image_name)
if owner is not None and owner == current_user.user_id:
return
Expand Down Expand Up @@ -50,6 +52,8 @@ def assert_image_read_access(image_name: str, current_user: CurrentUserOrDefault
"""
if current_user.is_admin:
return
if not ApiDependencies.invoker.services.image_records.exists(image_name):
raise HTTPException(status_code=404, detail="Image not found")

owner = ApiDependencies.invoker.services.image_records.get_user_id(image_name)
if owner is not None and owner == current_user.user_id:
Expand Down
23 changes: 21 additions & 2 deletions invokeai/app/api/routers/app_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
load_external_api_keys,
)
from invokeai.app.services.external_generation.external_generation_common import ExternalProviderStatus
from invokeai.app.services.external_generation.startup import sync_configured_external_starter_models
from invokeai.app.services.invocation_cache.invocation_cache_common import InvocationCacheStatus
from invokeai.app.services.model_records.model_records_base import UnknownModelException
from invokeai.backend.image_util.infill_methods.patchmatch import PatchMatch
Expand Down Expand Up @@ -178,7 +179,7 @@ async def update_runtime_config(
status_code=200,
response_model=list[ExternalProviderStatusModel],
)
async def get_external_provider_statuses() -> list[ExternalProviderStatusModel]:
async def get_external_provider_statuses(_: AdminUserOrDefault) -> list[ExternalProviderStatusModel]:
statuses = ApiDependencies.invoker.services.external_generation.get_provider_statuses()
return [status_to_model(status) for status in statuses.values()]

Expand All @@ -189,7 +190,7 @@ async def get_external_provider_statuses() -> list[ExternalProviderStatusModel]:
status_code=200,
response_model=list[ExternalProviderConfigModel],
)
async def get_external_provider_configs() -> list[ExternalProviderConfigModel]:
async def get_external_provider_configs(_: AdminUserOrDefault) -> list[ExternalProviderConfigModel]:
config = get_config()
return [_build_external_provider_config(provider_id, config) for provider_id in EXTERNAL_PROVIDER_FIELDS]

Expand Down Expand Up @@ -219,9 +220,14 @@ async def set_external_provider_config(
raise HTTPException(status_code=400, detail="No external provider config fields provided")

api_key_removed = update.api_key is not None and updates.get(api_key_field) is None
api_key_set = update.api_key is not None and updates.get(api_key_field) is not None
_apply_external_provider_update(updates)
if api_key_removed:
_remove_external_models_for_provider(provider_id)
elif api_key_set:
# Configuring a key should make the provider's models usable without a
# restart; queue its external starter models the same way startup does.
_sync_external_starter_models_for_provider(provider_id)
return _build_external_provider_config(provider_id, get_config())


Expand Down Expand Up @@ -316,6 +322,19 @@ def _build_external_provider_config(provider_id: str, config: InvokeAIAppConfig)
)


def _sync_external_starter_models_for_provider(provider_id: str) -> None:
invoker = ApiDependencies.invoker
try:
sync_configured_external_starter_models(
configured_provider_ids={provider_id},
model_manager=invoker.services.model_manager,
logger=invoker.services.logger,
)
except Exception as error:
# Queuing installs must never fail the config save; surface and move on.
invoker.services.logger.warning(f"Failed queueing external starter models for '{provider_id}': {error}")


def _remove_external_models_for_provider(provider_id: str) -> None:
model_manager = ApiDependencies.invoker.services.model_manager
external_models = model_manager.store.search_by_attr(
Expand Down
36 changes: 26 additions & 10 deletions invokeai/app/api/routers/custom_nodes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""FastAPI routes for custom node management."""

import asyncio
import json
import shutil
import subprocess
import sys
import traceback
from importlib.util import module_from_spec, spec_from_file_location
Expand All @@ -28,6 +28,7 @@
# were imported by that pack. Used on uninstall to delete only pack-imported workflows
# — deleting by tag alone is unsafe because users can edit tags on their own workflows.
PACK_MANIFEST_FILENAME = ".invokeai_pack_manifest.json"
GIT_CLONE_TIMEOUT_SECONDS = 120


class NodePackInfo(BaseModel):
Expand Down Expand Up @@ -126,6 +127,26 @@ def _get_installed_packs() -> list[NodePackInfo]:
return packs


async def _clone_node_pack(source: str, target_dir: Path) -> tuple[int, str]:
process = await asyncio.create_subprocess_exec(
"git",
"clone",
source,
str(target_dir),
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)

try:
_stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=GIT_CLONE_TIMEOUT_SECONDS)
except asyncio.TimeoutError:
process.kill()
await process.communicate()
raise

return process.returncode or 0, stderr.decode(errors="replace").strip()


@custom_nodes_router.get(
"/",
operation_id="list_custom_node_packs",
Expand Down Expand Up @@ -172,21 +193,16 @@ async def install_custom_node_pack(

try:
# Clone the repository
result = subprocess.run(
["git", "clone", source, str(target_dir)],
capture_output=True,
text=True,
timeout=120,
)
returncode, stderr = await _clone_node_pack(source, target_dir)

if result.returncode != 0:
if returncode != 0:
# Clean up on failure
if target_dir.exists():
shutil.rmtree(target_dir)
return InstallNodePackResponse(
name=pack_name,
success=False,
message=f"Git clone failed: {result.stderr.strip()}",
message=f"Git clone failed: {stderr}",
)

# Detect dependency manifests but do NOT install them automatically.
Expand Down Expand Up @@ -232,7 +248,7 @@ async def install_custom_node_pack(
dependency_file=dependency_file,
)

except subprocess.TimeoutExpired:
except asyncio.TimeoutError:
if target_dir.exists():
shutil.rmtree(target_dir)
return InstallNodePackResponse(
Expand Down
Loading
Loading