Refactor/926 state driven instance navigation#973
Open
MaanilVerma wants to merge 19 commits into
Open
Conversation
Lift the implicit "which window does this click target" decision out of
three scattered sites into one pure, table-driven function. Behavior-
identical (current matrix encoded); deltas land in Phase 3.
- shared/viewKind: ViewKind/Category/NavClass + navClass + viewKindFor,
the single classifier (remote folds into cloud for navigation)
- shared/navigation/navDecision: total O(1) transition table →
{window, verb, confirm, primaryLabel, secondary}; runs in main + renderer
- main: computeViewKind + currentView/currentCategory on the picker
snapshot; new openInstallInNewWindow primitive (focus-existing else
spawn a fresh chooser host) + IPC + preload bridge; deliverPickToEntry
extracted and shared with the swap path
- renderer: useInstanceNavState (facts) + useInstanceActions (dispatcher);
footer CTA + caret split-button driven by the decision; picker routes
the emitted NavDecision through the dispatcher
typecheck (node+web) + lint clean; 1971 tests pass.
…e 3a) - pickInstallFromPicker: Switch / Open in new window / Cancel via openSystemModalChoiceAsync; 'secondary' → openInstallInNewWindow (keeps A running) - decision table: instance|instance|stopped gains the new-window caret No flag. typecheck + lint clean; 1971 tests pass.
- 3 e2e specs (dashboard/instance/cloud): bridge → IPC → window for the navigation deltas (8 tests, lifecycle project) - record open-install-new-window IPC for assertions - handoff doc: test-coverage table (unit vs e2e) + why some cells are unit/manual-only
- openInstallInNewWindow fails closed (try/catch) + telemetry after the spawn guard so a failed spawn can't record a false success - pick/restart picker IPC wirings now .catch() unhandled rejections - dispatch aborts (no unhandled rejection) when a confirm dialog rejects - remove dead 'switch-3way' Confirm variant; strip rollout/history from source comments; relocate the swap-contract JSDoc - tests: dispatch reject + bridge-undefined, caret new-window coverage, unknown-category, e2e parent-undisturbed + reset symmetry typecheck + lint clean; 1976 unit tests pass.
…d? column The handoff doc's matrix had been condensed (dropped the deferred Stop/Restart secondaries, folded row-13 into 'source of truth'). Reproduce the original 17-row matrix verbatim with an Implemented? column; deviations (row 13 new-window) and deferrals (rows 8/15) called out in §1a.
Clicking Switch showed a wrong 'Restart?' dialog then closed the picker before main's 'Switch?' modal. Now the picker shows ONE in-drawer 3-way (Switch / Open in new window / Cancel) and stays open until the user commits, matching Restart. - useInstanceActions: switch routes through confirmSwitch; pickInstall(confirmed:true) so main skips its modal - pickInstallFromPicker + pick-install IPC + preload bridge take a confirmed flag - remove the now-dead Confirm type/field from the decision
…op in caret - leading icons on the primary CTA, caret items, and the More menu (Switch→Replace, Stop→CircleStop, Forget→EyeOff vs Uninstall→Trash2) - caret dropdown gains a 'Window options' heading + divider, fits content width; MoreMenu supports an optional heading + per-item icon (More menu unchanged otherwise) - reuse the existing synthetic Stop action in the caret next to Restart (own running local instance)
…ey-files + coverage
…rop 'Start (new window)') It spawns a window, not a swap — so the open-in-new-window label + icon are clearer and consistent with the caret. Removes the now-unused startNewWindow key.
…f → Restart - targetKind: remote folds to the cloud target rows (fixes Remote→Remote / Cloud→Remote dead 'Start' and Instance→Remote wrong 3-way) - add cloud|cloud|stopped (open-new) + running-elsewhere (focus) cells; reachability test bans no-op CTAs - cloud/remote self → Restart; allowDuplicate kept dormant/reserved - per-category CTA wording + icon: 'Open Remote' (server) vs 'Open Cloud'
- Three-way modal helper resolving the raw confirm/secondary/cancel action was lost in the main merge while its index.ts caller survived, breaking typecheck. - Restores the definition verbatim from 40837aa.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Refactors instance/window navigation (#926) from logic scattered across three sites into one pure, table-driven decision function. Given the current view (Dashboard | Instance | Cloud) and a clicked target,
decideNavigationreturns whether to switch in place, restart, focus, or open a new window — and drives the footer CTA label, a new caret split-button, and an in-drawer confirm from that single source. Ships the CTO matrix's behavior changes (Instance→Instance 3-way, cloud/instance/remote always-new-window) and adds Remote handling the matrix never specified.Changes
What
src/shared/navigation/navDecision.ts(new) —decideNavigation(NavInput): NavDecision, a totalReadonlyMaptransition table overViewKind × TargetKind × TargetRun. Pure, O(1), runs identically in renderer + main.src/shared/viewKind.ts(new) — singleViewKind/Category/NavClassvocabulary +navClass(foldsremote ⇒ cloud) +viewKindFor(one classifier shared bycomputeViewKindand the snapshot builder).src/renderer/src/composables/useInstanceNavState.ts(new) — read-model derivingNavInputfacts; reusesuseInstallCtaso the one-install-one-window invariant stays single-sourced. A remote target routes via the cloud cells (non-local URL backend).src/renderer/src/composables/useInstanceActions.ts(new) — single dispatcher routing a decision's verb onto the bridge, with cloud-capacity + local-kill gates; aborts (no unhandled rejection) if a confirm dialog rejects.src/main/index.ts— extractsdeliverPickToEntry(shared by swap + new-window), adds theopenInstallInNewWindowprimitive (focus-existing else spawn a fresh chooser host), and threads aconfirmedflag throughpickInstallFromPickerso the renderer's in-drawer confirm replaces main's system modal.src/main/popups/titlePopup.ts— adds thecomfy-titlepopup:open-install-new-windowIPC +currentView/currentCategoryon the picker snapshot; picker IPC wirings wrapped in.catch().src/renderer/src/components/settings/ComfyUISettingsContent.vue— footer CTA + caret split-button driven bydecideNavigation; leading icons on the CTA and both menus; per-category CTA wording/icon (Open CloudvsOpen Remote); reuses the existingstopaction in the caret next to Restart.src/renderer/src/views/comfyUISettings/MoreMenu.vue— optionalheading+ per-itemicon(the plain "More" menu is unchanged).docs/instance-navigation-926-handoff.md(new) — verbatim CTO matrix + Implemented? column, Remote rows (§1b), deviations (§1a), manual-verification + unit-vs-e2e coverage tables.Breaking
primary-actionemit payload changedboolean → NavDecisionbut is internal to the picker. CTA labels for unchanged cells are byte-identical; existing telemetry (instance.switched,instance.opened_new_window) is preserved.Review Focus
navDecision.ts) is the whole behavior surface — each matrix cell is one entry. Review it as data against the CTO matrix in the handoff doc. A reachability test bans any no-op CTA for a reachable (host, target, run) combo.allowDuplicateplumbing is kept dormant/reserved).useInstallContextMenuthinning) is a deferred follow-up. The 3-way confirm rendering and focus-existing-for-a-real-window are unit/manual-only (e2e can't spawn a live ComfyUI process) — see the coverage table.Testing
State-driven navigation splits into a pure decision (unit-testable exhaustively) and side-effecting wiring (e2e via real bridge → IPC → window): unit pins what the decision is for every cell; e2e pins that the wiring fires for the risky paths. Un-e2e-able cells are documented with their reason in the handoff doc.
navDecision.test.ts— every matrix cell + the fullViewKind × TargetKind × TargetRun × class × intentcross-product (totality), caret/new-window selection, and the no-dead-no-op-CTA reachability guard.useInstanceActions.test.ts— verb→bridge routing for all verbs; cloud-capacity + switch/restart gates; the 3-way (switch / open-new / cancel) outcomes; dispatch-reject and bridge-undefined guards; dormantallowDuplicatepassthrough.useInstanceNavState.test.ts/registry.test.ts— run-state derivation; remote routes like cloud (incl. Instance→Remote ≡ Instance→Cloud);computeViewKind(null/unknown category).e2e/nav-matrix-{dashboard,instance,cloud}.test.ts— 8 tests asserting via recorded IPC + liveBrowserWindowcounts: same-window launch, focus-existing, new-window spawn (parent undisturbed),allowDuplicateprimitive.pnpm run typecheck(node/web/e2e/integration) +pnpm run lintclean; 1983 unit tests pass; e2e--project=lifecyclegreen (pnpm run buildfirst — e2e runs the built bundle).Manual verification matrix
Remote rows (not in the original CTO matrix — Remote ≡ Cloud target)
Full matrix, deviations, and coverage:
docs/instance-navigation-926-handoff.md.Closes #926 closes #911