The new container gate test could fail on a cold-cache first run (onChooseTeam
observed 0 calls) due to a userEvent/happy-dom event-dispatch warm-up race; the
gate source is synchronous and correct. Wrap the positive assertions in
vi.waitFor so the emit lands deterministically.
A team plan can't be subscribed in place from a personal workspace, so a
personal user choosing a team plan in the unified pricing table is routed to
the Team Workspaces create/switch dialog instead of advancing into team
checkout. Gate on isInPersonalWorkspace; team workspaces keep the normal flow.
Port of the FE-768 Plan & Credits redesign onto the integration base,
using canManageSubscriptionLifecycle for the original-owner gate.
- ... menu per role: original owner = Edit / Cancel plan / Delete;
promoted owner = Edit / Leave; member = Leave; personal = Edit, plus
Cancel plan + Delete once on a paid plan.
- Cancel/Delete are creator-or-personal-owner only; the creator can't
leave; Delete is locked while a team subscription is active.
- Render the menu via the design-system DropdownMenu (dividers, danger
Delete) to match Figma; cancelled state shows Manage billing +
Reactivate plan; personal Free shows Subscribe, both with the menu.
- give the email input a persistent tertiary-background fill so it reads as a
field at rest, not only on focus (Figma 3084-6116)
- add a showSubmit prop to InviteMembersForm and expose submit/canSubmit/loading
so the success screen places "Send invites" in the footer next to Close,
not glued under the input (Figma 3698-10316)
A Stripe-backed subscribe (needs_payment_method / pending_payment) only fired
the billing op poll; on completion billingOperationStore.handleSuccess closed
the pricing dialog and opened settings, so "You're all set" never showed. Team
subscribes were also gated out of the success step by a selectedTierKey-only
guard.
- await the billing op in the async checkout branches and advance to the
success step instead of tearing the dialog down
- leave the subscription dialog open in handleSuccess (only top-ups close +
surface settings); keep the success toast
- render the success step for team checkouts, sourcing the name, price and
credits from the selected slider stop
- drive the team success invite block off the flat MAX_WORKSPACE_MEMBERS cap
A cancelled-but-still-active personal subscription renders both "Add credits" and "Resubscribe" in the credits row. With a wide (7-digit) balance the second button overflowed the fixed-width w-80 popover. Make the popover width fluid (w-fit, clamped min-w-80..max-w-96) so it stays 320px in the common single-action case and only grows (to ~370px) to fit both buttons.
Adds a @cloud e2e that asserts the Resubscribe button stays within the popover bounds.
Members upsell 'Upgrade to Team' was re-wired to showSubscriptionDialog()
during the FE-768 members merge, dropping the FE-934 team-tab fix on this
integration branch. Restore showTeamPlans() -> show({ planMode: 'team' }).
Route all same-origin /api, websocket, and bootstrap /api/features calls to https://pr-4393.testenvs.comfy.org so the labeled preview build exercises that ephemeral backend. Throwaway, scoped to this DO NOT MERGE integration PR.
Renders an inline invite block in the team variant of the "You're all set" success card so a buyer can invite teammates right after a team-plan upgrade.
- New shared InviteMembersForm.vue (chips/TagsInput multi-email form)
- Team upgrades only; seats capped via useBillingContext.getMaxSeats
- workspace_invite_sent telemetry + success-card i18n
- Storybook story + mocks
Stacked on FE-934 #12975 (success-card host).
(cherry picked from commit af8a7a1f41)
Add a /?pricing=1 (and /?pricing=team / =personal) deep link that opens the
pricing table on app load, gated to the original owner via
canManageSubscriptionLifecycle. Members and promoted owners are a silent
no-op (param stripped, app loads normally). Mirrors the existing preserved-
query URL-loader pattern; survives the login redirect. Eligible opens emit
the subscription modal_opened telemetry with a new deep_link reason.
(cherry picked from commit 0708c01f79)
The watch now gates ensureMembersLoaded on activeWorkspace.type === 'team'
so personal/undefined workspaces never invoke the store loader. This keeps
consumer composables that mock the store minimally (useSubscriptionDialog)
working, while the store method stays the idempotent safe loader.
(cherry picked from commit dde38af722)
Address review: own the team members-preload coordination in the store
instead of the composable. ensureMembersLoaded no-ops for personal /
already-loaded workspaces, dedupes in-flight calls, and logs failures so a
later call retries. Inline the redundant isCurrentUserOriginalOwner computed.
(cherry picked from commit 8278c9d874)
The FE-934 single-plan-confirm cherry-pick renamed the stories' plan
fixtures (creatorPlan -> creatorMonthly/creatorAnnual); the FE-965
TeamSuccessWithInvite story still referenced the removed creatorPlan.
The confirm CTA used the `secondary` variant (charcoal-600), nearly
indistinguishable from the dialog surface. Add a `tertiary-background`
design-system token (charcoal-400 per the DES spec) and a matching Button
variant, and use it for the confirm CTAs. The success check pointed at the
undefined `success-foreground` token (no color); switch it to the defined
`success-background` (#00cd72).
(cherry picked from commit 5b113faa20)
Rewrite the plan-change confirm to the single-plan layout from the design
mockup: immediate upgrades show prorated line items with upfront (yearly)
or monthly credit refill; scheduled downgrades show "Starts {date}", $0
due today, and an "After that" block. Add the "Billed monthly" note to the
new-subscription confirm and Storybook stories covering each variation.
(cherry picked from commit d61ab982c5)
The members upsell banner read 'Upgrade to Team' regardless of prior subscription. Branch the copy on a lapsed team plan (subscription_status 'canceled' or 'ended') so cancelled/ended workspaces see 'Reactivate Team' / 'To add more teammates, reactivate your plan.' instead, while never-subscribed workspaces keep the upgrade copy.
- useTeamPlan: hasLapsedTeamPlan (team-only, canceled|ended)
- MemberUpsellBanner: reactivate prop branches banner + CTA copy
- i18n: upsellBannerReactivate, reactivateTeam
- tests + Storybook stories (Upgrade/Reactivate/BothStates)
(cherry picked from commit 4b4c3ccf92)
Render the pricing table with v-show instead of v-if so the plan mode,
billing cycle, and credit-stop selection survive a round trip to the
confirm step and back. Backspace on the confirm step mirrors the back
arrow (ignored while an editable element is focused).
Yearly headline now shows the monthly-equivalent price with a
"{total} Billed yearly" subtitle and "Each year credits refill to" (x12);
monthly is unchanged. Pass the selected billing cycle to the team confirm
so the subtitle renders, drop the redundant header credits/month line, and
reword "Starting today" to "Starts today".
Resolve DropdownMenu z-index conflict with FE-770 (5a7ff04b01): FE-768's
dynamic contentStyle (lift past the current top modal) supersedes FE-770's
static z-3000 bump, so keep the static base at z-1700 per FE-768's design.
The in-app harness was a preview-only stand-in while the team-workspaces
backend was unavailable. The real backend now serves workspaces, billing
status, plans and the team-workspaces flag, so the mock is no longer needed.
The unified pricing dialog hugged its content, so switching For Personal / For Teams resized it (personal ~1108px, team grew to 95vw, with differing heights). Pin the pricing step's content root to a fixed width (1280) and a min-height floor so both plan scopes render at the same size; the compact confirm/success steps still shrink to their content.
The body-portaled menu kept a static z-1700, so it lost to dialogs that
join @primeuix's auto-incrementing 'modal' counter (z-1701+) and rendered
behind them (e.g. the workspace members more-options menu inside Settings).
Lift the open menu past the current top overlay when one exists; otherwise
keep the static z-index. Covered by a behavioral z-index test.
Await initialize() before flipping the flag so the test exercises true
reinitialization from an initialized legacy context rather than first-time
workspace activation. Still red without the type watch (verified locally).
The init watch only tracked the active workspace id, so when the resolved
billing type flipped after setup (e.g. team-workspaces flag resolving from
authenticated remote config), the active backend swapped without running its
initialize(), leaving subscription/balance stale or empty.
Watch the resolved type alongside the workspace id and reinitialize on either.
Non preview, `canvasOnly` widgets like `control_after_generate` could be
displayed in the subgraph editor even though promoting them would have
no visual or functional effect when in vue mode.
In vue mode, these entries are now hidden from the list of candidate
items for promotion to reduce confusion.
## Summary
Account preconditions (sign-in / subscription / credits) on running a
workflow now open their modal directly and stay out of the error panel +
error count — previously `subscription_required` fell through to a red
"1 ERROR — Subscription required to queue workflows" banner. This covers
**both** paths: the `execution_error` websocket event and the `POST
/prompt` 402 queue paywall (`{ type: "PAYMENT_REQUIRED", message:
"Subscription required to queue workflows" }`), which is the exact
payload reported in #12840.
## Changes
- **What**: `execution_error` is classified by a pure
`accountPreconditionRouting` resolver (precedence sign-in > subscription
> credits) and routed to the existing modal via
`useAccountPreconditionDialog`; `executionStore` returns early for
preconditions so they never populate `lastExecutionError` /
`lastPromptError` / `lastNodeErrors` → fully excluded from the panel and
`totalErrorCount`. Runtime credit error at a node → credits modal (out
of panel; can name the node).
- **Queue paywall**: the `queuePrompt` catch resolves the same
precondition from the `POST /prompt` 402 response and opens the modal,
short-circuiting before `lastPromptError`, so the queue paywall stays
out of the panel too. The runtime matcher learns the `"Subscription
required to queue workflows"` message.
- **Breaking**: none.
## Before / After
Free-tier queue paywall (`POST /prompt` → 402) on a cloud build:
**Before** — raw `Subscription required to queue workflows` surfaced in
the error panel (no actionable upgrade):
<img width="1600" height="873" alt="before-error-panel"
src="https://github.com/user-attachments/assets/1b76b742-16bf-47e3-9245-17e35f8f1e70"
/>
**After** — clean subscription modal opens; nothing in the error panel
or error count:
<img width="1600" height="873" alt="after-subscription-modal"
src="https://github.com/user-attachments/assets/13d238cb-20bf-4795-a530-5abcf9968dc7"
/>
## Review Focus
- **Routing-only — the run button is intentionally untouched.** The
original AC#3 ("no Subscribe-to-Run button") is superseded by the FE-978
run-lock decision (pre-emptive role-aware lock, Figma 3253-18671).
Complements #12786 (FE-978 run-lock); disjoint file sets.
- Tests: `accountPreconditionRouting` / `useAccountPreconditionDialog` /
`executionStore` — each precondition routes to its modal and is excluded
from the panel/count; precedence resolves on co-occurrence. Plus
Playwright `browser_tests/tests/subscriptionPaywallError.spec.ts` — the
queue paywall (402) stays out of the error panel, with a control
asserting ordinary queue errors still surface. typecheck / oxlint /
eslint / stylelint / oxfmt / knip clean.
Fixes FE-878
Fixes#12840
## Summary
Adds `@comfyorg/comfyui-desktop-bridge-types` as a workspace package in
the frontend monorepo and changes the frontend app dependency to
`workspace:*`.
Adds a dedicated `Publish Desktop Bridge Types` workflow for publishing
`packages/comfyui-desktop-bridge-types` by its own package version,
without coupling it to the generated `@comfyorg/comfyui-frontend-types`
release. The generated frontend types package still emits a concrete
`@comfyorg/comfyui-desktop-bridge-types@0.1.2` dependency instead of
leaking workspace/catalog protocol references.
The Desktop2 missing-model path uses `window.__comfyDesktop2.isRemote()`
when available, but falls back to the legacy
`window.__comfyDesktop2Remote` marker so frontend rollout stays
compatible with older Desktop builds.
Paired Desktop PR: https://github.com/Comfy-Org/Comfy-Desktop/pull/1112
## Summary
Remove unused `export` keyword from `ExportFormatOption` interface. The
interface is only used internally in `constants.ts` and is not imported
elsewhere.
Fixes knip "unused exported types" error.
Co-authored-by: Connor Byrne <c.byrne@comfy.org>