Skip to content

feat: migrate auth forms to vee-validate#9042

Draft
DrJKL wants to merge 7 commits intomainfrom
drjkl/vee-validate
Draft

feat: migrate auth forms to vee-validate#9042
DrJKL wants to merge 7 commits intomainfrom
drjkl/vee-validate

Conversation

@DrJKL
Copy link
Contributor

@DrJKL DrJKL commented Feb 21, 2026

Summary

Migrate auth-related forms from @primevue/forms to vee-validate + @vee-validate/zod, add reusable shadcn-style form primitives, and remove legacy PrimeVue form dependencies.

Changes

  • What:
    • Added src/components/ui/form/* primitives (FormField, FormItem, FormControl, FormLabel, FormDescription, FormMessage) with field/item context helpers.
    • Migrated form handling in:
      • src/components/dialog/content/signin/SignInForm.vue
      • src/components/dialog/content/signin/SignUpForm.vue
      • src/components/dialog/content/signin/ApiKeyForm.vue
      • src/components/dialog/content/signin/PasswordFields.vue
      • src/components/dialog/content/UpdatePasswordContent.vue
      • src/platform/cloud/onboarding/components/CloudSignInForm.vue
    • Updated impacted tests:
      • src/components/dialog/content/signin/SignInForm.test.ts
      • src/components/dialog/content/signin/ApiKeyForm.test.ts
    • Removed @primevue/forms and @primeuix/forms from dependencies.
  • Dependencies: Added vee-validate and @vee-validate/zod; removed @primevue/forms and @primeuix/forms.

Review Focus

  • Behavior parity for validation and submit/disable states across migrated auth forms.
  • VeeValidate field bindings with PrimeVue input components (InputText/Password), especially model/update wiring and error display.
  • Accessibility attributes and context wiring in new src/components/ui/form/* primitives.

Screenshots (if applicable)

N/A (no visual design changes; structural/form-state migration only).

┆Issue is synchronized with this Notion page by Unito

DrJKL and others added 2 commits February 20, 2026 19:10
- replace @primevue/forms usage with vee-validate + @vee-validate/zod

- add shared ui form primitives for field/item/control/message wiring

- update auth form tests and remove legacy forms dependencies

Amp-Thread-ID: https://ampcode.com/threads/T-019c7e06-730d-729e-8602-1bc1dffe4801
Co-authored-by: Amp <amp@ampcode.com>
- remove stale @primeuix/forms knip ignore dependency

- use FormDescription in password field requirements block

Amp-Thread-ID: https://ampcode.com/threads/T-019c7e06-730d-729e-8602-1bc1dffe4801
Co-authored-by: Amp <amp@ampcode.com>
@github-actions
Copy link

github-actions bot commented Feb 21, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 02/21/2026, 08:08:00 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Feb 21, 2026

Playwright: ✅ 526 passed, 0 failed · 4 flaky

📊 Browser Reports
  • chromium: View Report (✅ 513 / ❌ 0 / ⚠️ 4 / ⏭️ 10)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 10 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Migrates form validation from PrimeVue Forms to vee-validate, adds a small form UI component library (FormItem/Field/Label/Control/Message/Description + composables), tightens Zod schemas to require string-keyed records, updates dependency configuration (remove PrimeVue forms, add vee-validate), and applies type-safety refinements across the codebase.

Changes

Cohort / File(s) Summary
Dependency & PNPM config
knip.config.ts, package.json, pnpm-workspace.yaml
Removed PrimeVue form packages, added vee-validate, upgraded Zod-related packages, and added PNPM packageExtensions/peerDependencyRules entries.
New Form UI library & composables
src/components/ui/form/FormControl.vue, src/components/ui/form/FormDescription.vue, src/components/ui/form/FormField.vue, src/components/ui/form/FormItem.vue, src/components/ui/form/FormLabel.vue, src/components/ui/form/FormMessage.vue, src/components/ui/form/injectionKeys.ts, src/components/ui/form/useFormField.ts
Introduced accessible form primitives and injection keys plus useFormField composable to integrate vee-validate fields with ARIA ids and error messaging.
Forms migrated to vee-validate
src/components/dialog/content/UpdatePasswordContent.vue, src/components/dialog/content/signin/ApiKeyForm.vue, src/components/dialog/content/signin/PasswordFields.vue, src/components/dialog/content/signin/SignInForm.vue, src/components/dialog/content/signin/SignUpForm.vue, src/platform/cloud/onboarding/components/CloudSignInForm.vue
Replaced PrimeVue Form usage with native <form> + useForm/FormField patterns, rewired validation/submission flows to vee-validate (handleSubmit/validate), and updated templates to use new form primitives.
Form-related tests updated
src/components/dialog/content/signin/ApiKeyForm.test.ts, src/components/dialog/content/signin/SignInForm.test.ts
Removed global PrimeVue Form/Button registrations, converted tests to DOM-driven interactions, and adapted assertions to the new validation/submission flows.
Schema key-typing changes
src/platform/assets/schemas/assetSchema.ts, src/platform/tasks/services/taskService.ts, src/schemas/apiSchema.ts, src/schemas/colorPaletteSchema.ts, src/schemas/nodeDef/nodeDefSchemaV2.ts, src/schemas/nodeDefSchema.ts
Tightened various Zod records to z.record(z.string(), ...) or tuple types, affecting inferred public types and enforcing string keys.
Workflow schema refactor & tests
src/platform/workflow/validation/schemas/workflowSchema.ts, src/platform/workflow/validation/schemas/workflowSchema.test.ts
Introduced a forward-declared recursive subgraph schema, updated SubgraphDefinition type, and replaced commented @ts-expect-error checks in tests with runtime null guards.
Error handling adjustments
src/platform/assets/utils/createAssetWidget.ts, src/platform/assets/utils/createModelNodeFromAsset.ts
Log Zod v4 error details via .issues instead of .errors.
Type annotation and minor fixes
src/composables/useBrowserTabTitle.ts, src/scripts/app.ts, src/scripts/changeTracker.ts, src/stores/executionStore.ts, src/stores/subgraphStore.ts, src/utils/nodeDefUtil.ts
Added explicit callback typings, adjusted casts and type predicates, and refined internal type assertions; no runtime behavior changes.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Form as SignInForm / Form UI
    participant Validator as vee-validate useForm
    participant Field as FormField
    participant Composable as useFormField
    participant Handler as handleSubmit
    participant Auth as authActions

    User->>Form: enter credentials
    Form->>Validator: initialize useForm(schema, values)
    Form->>Field: render FormField(name)
    Field->>Composable: inject field name & item id
    Composable-->>Field: provide errors/meta/ids

    User->>Form: submit
    Form->>Handler: handleSubmit(onSubmit)
    Handler->>Validator: validate()
    Validator->>Field: validate field values
    Field->>Composable: request error state
    Composable-->>Field: error messages

    alt validation success
        Handler->>Auth: authActions.signIn(values)
        Auth-->>Handler: response
        Handler->>Form: emit submit / navigate
    else validation failure
        Handler->>Form: display FormMessage errors
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Hopping through fields with a curious twitch,

I swapped old forms for vee-validate's stitch.
Strings guard the records, errors sing in line,
New components nest, ARIA marked fine.
A rabbit's small cheer — type-safe and spry! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: migrate auth forms to vee-validate' directly summarizes the main change: migrating authentication forms from PrimeVue to vee-validate.
Description check ✅ Passed The description provides a clear Summary, detailed Changes section with What/Dependencies, and Review Focus areas. All required template sections are present and well-filled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch drjkl/vee-validate

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Feb 21, 2026

📦 Bundle: 4.38 MB gzip 🔴 +18.2 kB

Details

Summary

  • Raw size: 20.6 MB baseline 20.5 MB — 🔴 +75.4 kB
  • Gzip: 4.38 MB baseline 4.36 MB — 🔴 +18.2 kB
  • Brotli: 3.38 MB baseline 3.37 MB — 🔴 +16.2 kB
  • Bundles: 220 current • 220 baseline • 138 added / 138 removed

Category Glance
Vendor & Third-Party 🔴 +71.2 kB (8.93 MB) · User & Accounts 🔴 +4.82 kB (20.8 kB) · Data & Services 🟢 -2.26 kB (2.5 MB) · Other 🔴 +1.06 kB (7.6 MB) · Views & Navigation 🔴 +580 B (69.3 kB) · Graph Workspace 🔴 +13 B (926 kB) · + 5 more

App Entry Points — 21.5 kB (baseline 21.5 kB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-Bzw8E2Yc.js (new) 21.5 kB 🔴 +21.5 kB 🔴 +7.01 kB 🔴 +6.09 kB
assets/index-gvF9m8xJ.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -7 kB 🟢 -6.08 kB

Status: 1 added / 1 removed

Graph Workspace — 926 kB (baseline 926 kB) • 🔴 +13 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-C34Hih0T.js (new) 926 kB 🔴 +926 kB 🔴 +199 kB 🔴 +151 kB
assets/GraphView-Cs5pj1uW.js (removed) 926 kB 🟢 -926 kB 🟢 -199 kB 🟢 -151 kB

Status: 1 added / 1 removed

Views & Navigation — 69.3 kB (baseline 68.8 kB) • 🔴 +580 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-gURyuTan.js (removed) 15.5 kB 🟢 -15.5 kB 🟢 -3.32 kB 🟢 -2.81 kB
assets/CloudSurveyView-BiYbFsqq.js (new) 15.5 kB 🔴 +15.5 kB 🔴 +3.32 kB 🔴 +2.83 kB
assets/CloudLoginView-DcA-5pc4.js (new) 10.7 kB 🔴 +10.7 kB 🔴 +3.09 kB 🔴 +2.72 kB
assets/CloudLoginView-CCxo9TuX.js (removed) 10 kB 🟢 -10 kB 🟢 -2.93 kB 🟢 -2.57 kB
assets/UserCheckView-DBzVM7Uq.js (new) 8.41 kB 🔴 +8.41 kB 🔴 +2.23 kB 🔴 +1.94 kB
assets/UserCheckView-DLpdrXb-.js (removed) 8.41 kB 🟢 -8.41 kB 🟢 -2.23 kB 🟢 -1.94 kB
assets/CloudSignupView-B0B_hfCb.js (removed) 7.41 kB 🟢 -7.41 kB 🟢 -2.32 kB 🟢 -2.03 kB
assets/CloudSignupView-i5HGFaK6.js (new) 7.37 kB 🔴 +7.37 kB 🔴 +2.3 kB 🔴 +2.01 kB
assets/CloudLayoutView-CcPuh9ch.js (removed) 6.43 kB 🟢 -6.43 kB 🟢 -2.1 kB 🟢 -1.84 kB
assets/CloudLayoutView-CG2TR7Zc.js (new) 6.43 kB 🔴 +6.43 kB 🔴 +2.1 kB 🔴 +1.83 kB
assets/CloudForgotPasswordView-7eETBUbj.js (removed) 5.56 kB 🟢 -5.56 kB 🟢 -1.94 kB 🟢 -1.71 kB
assets/CloudForgotPasswordView-CeMjo5jW.js (new) 5.56 kB 🔴 +5.56 kB 🔴 +1.93 kB 🔴 +1.75 kB
assets/CloudAuthTimeoutView-B-3Qx2AM.js (new) 4.91 kB 🔴 +4.91 kB 🔴 +1.77 kB 🔴 +1.56 kB
assets/CloudAuthTimeoutView-C6wLCNy4.js (removed) 4.91 kB 🟢 -4.91 kB 🟢 -1.77 kB 🟢 -1.55 kB
assets/CloudSubscriptionRedirectView-BF0hJAQz.js (new) 4.71 kB 🔴 +4.71 kB 🔴 +1.78 kB 🔴 +1.58 kB
assets/CloudSubscriptionRedirectView-Db1OJP05.js (removed) 4.71 kB 🟢 -4.71 kB 🟢 -1.78 kB 🟢 -1.57 kB
assets/UserSelectView-B3ieorDR.js (removed) 4.5 kB 🟢 -4.5 kB 🟢 -1.64 kB 🟢 -1.46 kB
assets/UserSelectView-yMkW_X9X.js (new) 4.5 kB 🔴 +4.5 kB 🔴 +1.64 kB 🔴 +1.47 kB
assets/layout-BbDp11iH.js (removed) 296 B 🟢 -296 B 🟢 -224 B 🟢 -211 B
assets/layout-uv7UEPw_.js (new) 296 B 🔴 +296 B 🔴 +224 B 🔴 +187 B
assets/CloudSorryContactSupportView-BFdRDCGB.js 1.02 kB 1.02 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 10 added / 10 removed

Panels & Settings — 432 kB (baseline 432 kB) • 🟢 -1 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SecretsPanel-5ONW-qYd.js (new) 21.5 kB 🔴 +21.5 kB 🔴 +5.3 kB 🔴 +4.64 kB
assets/SecretsPanel-DJJ8pmJD.js (removed) 21.5 kB 🟢 -21.5 kB 🟢 -5.31 kB 🟢 -4.65 kB
assets/LegacyCreditsPanel-BPWIA1S3.js (removed) 20.6 kB 🟢 -20.6 kB 🟢 -5.57 kB 🟢 -4.89 kB
assets/LegacyCreditsPanel-CE9ZPhVg.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +5.57 kB 🔴 +4.9 kB
assets/SubscriptionPanel-BCl_llZp.js (new) 18.6 kB 🔴 +18.6 kB 🔴 +4.7 kB 🔴 +4.15 kB
assets/SubscriptionPanel-DjXbm3TJ.js (removed) 18.6 kB 🟢 -18.6 kB 🟢 -4.71 kB 🟢 -4.16 kB
assets/KeybindingPanel-BQMMoFpF.js (new) 12.3 kB 🔴 +12.3 kB 🔴 +3.57 kB 🔴 +3.18 kB
assets/KeybindingPanel-BYvzBFSd.js (removed) 12.3 kB 🟢 -12.3 kB 🟢 -3.58 kB 🟢 -3.18 kB
assets/AboutPanel-_xgGgRZv.js (removed) 9.79 kB 🟢 -9.79 kB 🟢 -2.73 kB 🟢 -2.45 kB
assets/AboutPanel-bioFUN8f.js (new) 9.79 kB 🔴 +9.79 kB 🔴 +2.73 kB 🔴 +2.46 kB
assets/ExtensionPanel-C3fc9D_1.js (removed) 9.38 kB 🟢 -9.38 kB 🟢 -2.65 kB 🟢 -2.36 kB
assets/ExtensionPanel-MId-rEty.js (new) 9.38 kB 🔴 +9.38 kB 🔴 +2.65 kB 🔴 +2.37 kB
assets/ServerConfigPanel-CYZSGsMb.js (new) 6.44 kB 🔴 +6.44 kB 🔴 +2.13 kB 🔴 +1.94 kB
assets/ServerConfigPanel-cz_zypKO.js (removed) 6.44 kB 🟢 -6.44 kB 🟢 -2.13 kB 🟢 -1.9 kB
assets/UserPanel-BawZFzvA.js (new) 6.16 kB 🔴 +6.16 kB 🔴 +1.99 kB 🔴 +1.75 kB
assets/UserPanel-DVUuRhJ2.js (removed) 6.16 kB 🟢 -6.16 kB 🟢 -1.99 kB 🟢 -1.74 kB
assets/cloudRemoteConfig-CujCwX6s.js (new) 1.44 kB 🔴 +1.44 kB 🔴 +707 B 🔴 +606 B
assets/cloudRemoteConfig-D2sNqkF8.js (removed) 1.44 kB 🟢 -1.44 kB 🟢 -711 B 🟢 -608 B
assets/refreshRemoteConfig-BpCQIMHx.js (removed) 1.14 kB 🟢 -1.14 kB 🟢 -520 B 🟢 -474 B
assets/refreshRemoteConfig-DZRoUTCL.js (new) 1.14 kB 🔴 +1.14 kB 🔴 +520 B 🔴 +456 B
assets/config-xzJccvL6.js 996 B 996 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B-d9Otjz.js 28.4 kB 28.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-B-NSRtAS.js 27.4 kB 27.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BkxYToz5.js 29.6 kB 29.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C-IPGWus.js 38.1 kB 38.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CDM4whDV.js 27.6 kB 27.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CpfxiDnY.js 24.2 kB 24.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DbUzP7aR.js 32 kB 32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Df-nqQec.js 28.4 kB 28.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DjceRQX8.js 33.7 kB 33.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Dl79FKCL.js 23.6 kB 23.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-VC7Ihh9Z.js 30.2 kB 30.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 10 added / 10 removed

User & Accounts — 20.8 kB (baseline 16 kB) • 🔴 +4.82 kB

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/PasswordFields-D4GHqoGu.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.21 kB 🔴 +1.95 kB
assets/PasswordFields-oQHcbQgH.js (removed) 4.51 kB 🟢 -4.51 kB 🟢 -1.35 kB 🟢 -1.2 kB
assets/auth-3-lLPyt7.js (new) 3.4 kB 🔴 +3.4 kB 🔴 +1.18 kB 🔴 +987 B
assets/auth-DlkVcddr.js (removed) 3.4 kB 🟢 -3.4 kB 🟢 -1.18 kB 🟢 -991 B
assets/SignUpForm-7DUTaZuo.js (new) 3.29 kB 🔴 +3.29 kB 🔴 +1.29 kB 🔴 +1.15 kB
assets/SignUpForm-D4VYOAbi.js (removed) 3.01 kB 🟢 -3.01 kB 🟢 -1.23 kB 🟢 -1.1 kB
assets/UpdatePasswordContent-DPbiehP_.js (new) 2.51 kB 🔴 +2.51 kB 🔴 +1.14 kB 🔴 +990 B
assets/UpdatePasswordContent-CC7mPCvK.js (removed) 2.37 kB 🟢 -2.37 kB 🟢 -1.07 kB 🟢 -940 B
assets/firebaseAuthStore-7_tH3it3.js (new) 790 B 🔴 +790 B 🔴 +389 B 🔴 +347 B
assets/firebaseAuthStore-DneQf_ZN.js (removed) 790 B 🟢 -790 B 🟢 -387 B 🟢 -349 B
assets/auth-7LplpSoI.js (removed) 357 B 🟢 -357 B 🟢 -223 B 🟢 -194 B
assets/auth-CL6Y77df.js (new) 357 B 🔴 +357 B 🔴 +223 B 🔴 +189 B
assets/WorkspaceProfilePic-GWdeygOb.js 1.57 kB 1.57 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

Editors & Dialogs — 738 B (baseline 738 B) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useSubscriptionDialog-BaHBRU6T.js (removed) 738 B 🟢 -738 B 🟢 -379 B 🟢 -325 B
assets/useSubscriptionDialog-lCnm9U68.js (new) 738 B 🔴 +738 B 🔴 +377 B 🔴 +328 B

Status: 1 added / 1 removed

UI Components — 43.3 kB (baseline 43.2 kB) • 🔴 +1 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useTerminalTabs-DiLCHEBU.js (new) 9.85 kB 🔴 +9.85 kB 🔴 +3.4 kB 🔴 +2.99 kB
assets/useTerminalTabs-RRICypTt.js (removed) 9.85 kB 🟢 -9.85 kB 🟢 -3.4 kB 🟢 -3 kB
assets/ComfyQueueButton-DJCW9Bgx.js (removed) 8.02 kB 🟢 -8.02 kB 🟢 -2.49 kB 🟢 -2.22 kB
assets/ComfyQueueButton-YHXyqYtm.js (new) 8.02 kB 🔴 +8.02 kB 🔴 +2.49 kB 🔴 +2.22 kB
assets/TopbarBadge-5MSug0ax.js (removed) 7.45 kB 🟢 -7.45 kB 🟢 -1.82 kB 🟢 -1.6 kB
assets/TopbarBadge-BueVqged.js (new) 7.45 kB 🔴 +7.45 kB 🔴 +1.81 kB 🔴 +1.6 kB
assets/ScrubableNumberInput-BVLYtZ53.js (removed) 5.96 kB 🟢 -5.96 kB 🟢 -2.07 kB 🟢 -1.83 kB
assets/ScrubableNumberInput-Cq5emqIY.js (new) 5.96 kB 🔴 +5.96 kB 🔴 +2.07 kB 🔴 +1.83 kB
assets/Button-CNInF-wG.js (removed) 2.98 kB 🟢 -2.98 kB 🟢 -1.21 kB 🟢 -1.07 kB
assets/Button-DdiKd4B0.js (new) 2.98 kB 🔴 +2.98 kB 🔴 +1.2 kB 🔴 +1.07 kB
assets/SubscribeButton-B4_VRO6w.js (removed) 2.35 kB 🟢 -2.35 kB 🟢 -1.03 kB 🟢 -889 B
assets/SubscribeButton-YVvOcgsP.js (new) 2.35 kB 🔴 +2.35 kB 🔴 +1.02 kB 🔴 +891 B
assets/WidgetButton-Bhm3dR3R.js (new) 1.84 kB 🔴 +1.84 kB 🔴 +875 B 🔴 +768 B
assets/WidgetButton-DCQMAWoZ.js (removed) 1.84 kB 🟢 -1.84 kB 🟢 -877 B 🟢 -769 B
assets/cloudFeedbackTopbarButton-BSemPcen.js (removed) 1.6 kB 🟢 -1.6 kB 🟢 -857 B 🟢 -755 B
assets/cloudFeedbackTopbarButton-DakYOBNM.js (new) 1.6 kB 🔴 +1.6 kB 🔴 +855 B 🔴 +765 B
assets/CloudBadge-CbUjJL6g.js (new) 1.24 kB 🔴 +1.24 kB 🔴 +605 B 🔴 +526 B
assets/CloudBadge-DBWhxYZS.js (removed) 1.24 kB 🟢 -1.24 kB 🟢 -608 B 🟢 -529 B
assets/UserAvatar-DRIP2fXu.js (removed) 1.17 kB 🟢 -1.17 kB 🟢 -619 B 🟢 -557 B
assets/UserAvatar-W7Tfo4UY.js (new) 1.17 kB 🔴 +1.17 kB 🔴 +618 B 🔴 +529 B
assets/ComfyQueueButton-BCrM-rcW.js (new) 795 B 🔴 +795 B 🔴 +392 B 🔴 +348 B
assets/ComfyQueueButton-eSgSJGjQ.js (removed) 795 B 🟢 -795 B 🟢 -394 B 🟢 -349 B

Status: 11 added / 11 removed

Data & Services — 2.5 MB (baseline 2.5 MB) • 🟢 -2.26 kB

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/dialogService-DODKWN4M.js (removed) 1.72 MB 🟢 -1.72 MB 🟢 -385 kB 🟢 -292 kB
assets/dialogService-BoRLr7-l.js (new) 1.72 MB 🔴 +1.72 MB 🔴 +385 kB 🔴 +292 kB
assets/api-B-ja5ITy.js (removed) 651 kB 🟢 -651 kB 🟢 -147 kB 🟢 -117 kB
assets/api-BI2uQ6uL.js (new) 650 kB 🔴 +650 kB 🔴 +147 kB 🔴 +117 kB
assets/load3dService-Dwgjl5uw.js (removed) 91 kB 🟢 -91 kB 🟢 -19 kB 🟢 -16.4 kB
assets/load3dService-VYy6qCpm.js (new) 91 kB 🔴 +91 kB 🔴 +19 kB 🔴 +16.4 kB
assets/systemStatsStore-CyjwIsLi.js (new) 12.3 kB 🔴 +12.3 kB 🔴 +4.31 kB 🔴 +3.79 kB
assets/systemStatsStore-s1r8sxuR.js (removed) 12.3 kB 🟢 -12.3 kB 🟢 -4.31 kB 🟢 -3.78 kB
assets/releaseStore-Cxn8pAOq.js (removed) 7.96 kB 🟢 -7.96 kB 🟢 -2.22 kB 🟢 -1.95 kB
assets/releaseStore-uSN2zYf0.js (new) 7.96 kB 🔴 +7.96 kB 🔴 +2.22 kB 🔴 +1.95 kB
assets/keybindingService-B1egYbX7.js (new) 6.52 kB 🔴 +6.52 kB 🔴 +1.71 kB 🔴 +1.47 kB
assets/keybindingService-C1LbDrXs.js (removed) 6.52 kB 🟢 -6.52 kB 🟢 -1.71 kB 🟢 -1.48 kB
assets/bootstrapStore-B0NjENeE.js (new) 2.08 kB 🔴 +2.08 kB 🔴 +873 B 🔴 +790 B
assets/bootstrapStore-DAaOEPmX.js (removed) 2.08 kB 🟢 -2.08 kB 🟢 -878 B 🟢 -797 B
assets/userStore-B14ZTRKp.js (new) 1.85 kB 🔴 +1.85 kB 🔴 +720 B 🔴 +634 B
assets/userStore-BW9CsYpL.js (removed) 1.85 kB 🟢 -1.85 kB 🟢 -720 B 🟢 -677 B
assets/audioService-B9EJ6tVH.js (new) 1.73 kB 🔴 +1.73 kB 🔴 +850 B 🔴 +725 B
assets/audioService-DQGcAlgR.js (removed) 1.73 kB 🟢 -1.73 kB 🟢 -847 B 🟢 -727 B
assets/releaseStore-BBZnsaRD.js (new) 762 B 🔴 +762 B 🔴 +384 B 🔴 +340 B
assets/releaseStore-DcnIMLgC.js (removed) 762 B 🟢 -762 B 🟢 -382 B 🟢 -347 B
assets/settingStore-DfwLYJdW.js (removed) 746 B 🟢 -746 B 🟢 -383 B 🟢 -341 B
assets/settingStore-DvRTiZxI.js (new) 746 B 🔴 +746 B 🔴 +386 B 🔴 +342 B
assets/workflowDraftStore-D3BqtyrZ.js (new) 738 B 🔴 +738 B 🔴 +378 B 🔴 +331 B
assets/workflowDraftStore-DStdRuEc.js (removed) 738 B 🟢 -738 B 🟢 -376 B 🟢 -334 B
assets/dialogService-Bjdvu9_Q.js (removed) 727 B 🟢 -727 B 🟢 -367 B 🟢 -327 B
assets/dialogService-BzM_IB7w.js (new) 727 B 🔴 +727 B 🔴 +366 B 🔴 +329 B
assets/serverConfigStore-CQEF-KM1.js 2.32 kB 2.32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 13 added / 13 removed

Utilities & Hooks — 57.7 kB (baseline 57.7 kB) • 🔴 +1 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useLoad3d-CiaQrguF.js (removed) 14.6 kB 🟢 -14.6 kB 🟢 -3.63 kB 🟢 -3.2 kB
assets/useLoad3d-DfA9MrcI.js (new) 14.6 kB 🔴 +14.6 kB 🔴 +3.63 kB 🔴 +3.21 kB
assets/useLoad3dViewer-CkyqrV9m.js (removed) 14.1 kB 🟢 -14.1 kB 🟢 -3.15 kB 🟢 -2.8 kB
assets/useLoad3dViewer-DVVjVIE6.js (new) 14.1 kB 🔴 +14.1 kB 🔴 +3.15 kB 🔴 +2.8 kB
assets/colorUtil-CWslCnGw.js (new) 7 kB 🔴 +7 kB 🔴 +2.14 kB 🔴 +1.9 kB
assets/colorUtil-BGddE1gK.js (removed) 7 kB 🟢 -7 kB 🟢 -2.14 kB 🟢 -1.9 kB
assets/useFeatureFlags-CHgOC1uH.js (new) 3.52 kB 🔴 +3.52 kB 🔴 +1.09 kB 🔴 +930 B
assets/useFeatureFlags-DUVGEQjP.js (removed) 3.52 kB 🟢 -3.52 kB 🟢 -1.09 kB 🟢 -930 B
assets/useWorkspaceUI-BLq-pbBV.js (removed) 3 kB 🟢 -3 kB 🟢 -824 B 🟢 -702 B
assets/useWorkspaceUI-D5MhoM4a.js (new) 3 kB 🔴 +3 kB 🔴 +821 B 🔴 +738 B
assets/useSubscriptionCredits-BHksUA9Q.js (new) 2.75 kB 🔴 +2.75 kB 🔴 +1.04 kB 🔴 +901 B
assets/useSubscriptionCredits-DqpZVNvc.js (removed) 2.75 kB 🟢 -2.75 kB 🟢 -1.04 kB 🟢 -901 B
assets/subscriptionCheckoutUtil-C-xwpsUK.js (new) 2.53 kB 🔴 +2.53 kB 🔴 +1.06 kB 🔴 +954 B
assets/subscriptionCheckoutUtil-Ddc6m3UG.js (removed) 2.53 kB 🟢 -2.53 kB 🟢 -1.06 kB 🟢 -930 B
assets/useExternalLink-B5vQfjBP.js (removed) 1.66 kB 🟢 -1.66 kB 🟢 -772 B 🟢 -677 B
assets/useExternalLink-jZEuhXSm.js (new) 1.66 kB 🔴 +1.66 kB 🔴 +774 B 🔴 +677 B
assets/useErrorHandling-Bes46n47.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -630 B 🟢 -536 B
assets/useErrorHandling-BsOgFZTc.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +629 B 🔴 +534 B
assets/useWorkspaceSwitch-BcRcg_2M.js (removed) 1.25 kB 🟢 -1.25 kB 🟢 -546 B 🟢 -483 B
assets/useWorkspaceSwitch-WyLg2Gm6.js (new) 1.25 kB 🔴 +1.25 kB 🔴 +545 B 🔴 +488 B
assets/useLoad3d-bqlO6gV5.js (removed) 861 B 🟢 -861 B 🟢 -425 B 🟢 -378 B
assets/useLoad3d-D3OKJrcJ.js (new) 861 B 🔴 +861 B 🔴 +424 B 🔴 +378 B
assets/audioUtils-B6sd8UR9.js (new) 858 B 🔴 +858 B 🔴 +499 B 🔴 +402 B
assets/audioUtils-CPT1AS4v.js (removed) 858 B 🟢 -858 B 🟢 -502 B 🟢 -403 B
assets/useLoad3dViewer-DECHoS-l.js (new) 840 B 🔴 +840 B 🔴 +408 B 🔴 +366 B
assets/useLoad3dViewer-DmauZhL9.js (removed) 840 B 🟢 -840 B 🟢 -410 B 🟢 -367 B
assets/useCurrentUser-BpqF40t2.js (removed) 724 B 🟢 -724 B 🟢 -370 B 🟢 -356 B
assets/useCurrentUser-CtetmA7g.js (new) 724 B 🔴 +724 B 🔴 +372 B 🔴 +330 B
assets/_plugin-vue_export-helper-CY4XIWDa.js 315 B 315 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/envUtil-BQSmRN2Q.js 466 B 466 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/markdownRendererUtil-BzorCTYU.js 1.56 kB 1.56 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/SkeletonUtils-DzR_k0-v.js 133 B 133 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 14 added / 14 removed

Vendor & Third-Party — 8.93 MB (baseline 8.86 MB) • 🔴 +71.2 kB

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-primevue-CR72kPwu.js (removed) 1.73 MB 🟢 -1.73 MB 🟢 -311 kB 🟢 -190 kB
assets/vendor-primevue-E-O_PuMn.js (new) 1.69 MB 🔴 +1.69 MB 🔴 +304 kB 🔴 +184 kB
assets/vendor-other-mgw4gctM.js (new) 1.6 MB 🔴 +1.6 MB 🔴 +336 kB 🔴 +268 kB
assets/vendor-other-DWggOyQ3.js (removed) 1.52 MB 🟢 -1.52 MB 🟢 -318 kB 🟢 -253 kB
assets/vendor-reka-ui-B1BWVj71.js (new) 379 kB 🔴 +379 kB 🔴 +73.1 kB 🔴 +58.4 kB
assets/vendor-reka-ui-CWMIYdD2.js (removed) 379 kB 🟢 -379 kB 🟢 -73.1 kB 🟢 -58.4 kB
assets/vendor-zod-CVP8qhyq.js (new) 139 kB 🔴 +139 kB 🔴 +26.6 kB 🔴 +22.5 kB
assets/vendor-zod-DcCUUPIi.js (removed) 109 kB 🟢 -109 kB 🟢 -19.6 kB 🟢 -16.7 kB
assets/vendor-axios-C4mPrLmU.js 70.3 kB 70.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-chart-DURtBH6a.js 399 kB 399 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-firebase-BvMr43CG.js 836 kB 836 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-i18n-CJliEbVY.js 131 kB 131 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-markdown-DDKkCsp-.js 145 kB 145 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-sentry-SQwstEKc.js 182 kB 182 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-v5hUZrto.js 1.8 MB 1.8 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-Bi_34iZD.js 625 kB 625 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-core-CmHHRvL9.js 311 kB 311 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vueuse-B4hGe0IQ.js 113 kB 113 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BNvt5AdL.js 374 kB 374 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-yjs-CP_4YO8u.js 143 kB 143 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 4 added / 4 removed

Other — 7.6 MB (baseline 7.6 MB) • 🔴 +1.06 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/i18n-B5Ky-3zQ.js (removed) 517 kB 🟢 -517 kB 🟢 -99.2 kB 🟢 -77.2 kB
assets/i18n-Cp2wyOJ1.js (new) 517 kB 🔴 +517 kB 🔴 +99.2 kB 🔴 +77.3 kB
assets/core-BgZNyD_p.js (removed) 72.8 kB 🟢 -72.8 kB 🟢 -18.8 kB 🟢 -16.1 kB
assets/core-DFt2KKSt.js (new) 72.8 kB 🔴 +72.8 kB 🔴 +18.8 kB 🔴 +16.1 kB
assets/groupNode-B7fLEPUd.js (removed) 71.8 kB 🟢 -71.8 kB 🟢 -17.7 kB 🟢 -15.6 kB
assets/groupNode-BiZ8_Hcz.js (new) 71.8 kB 🔴 +71.8 kB 🔴 +17.7 kB 🔴 +15.5 kB
assets/WidgetSelect-D9v2dsjq.js (removed) 57.6 kB 🟢 -57.6 kB 🟢 -12.2 kB 🟢 -10.5 kB
assets/WidgetSelect-DEF2MAUt.js (new) 57.6 kB 🔴 +57.6 kB 🔴 +12.2 kB 🔴 +10.5 kB
assets/SubscriptionRequiredDialogContentWorkspace-Ck1IZl6S.js (new) 45.8 kB 🔴 +45.8 kB 🔴 +8.56 kB 🔴 +7.4 kB
assets/SubscriptionRequiredDialogContentWorkspace-D01B6DNP.js (removed) 45.8 kB 🟢 -45.8 kB 🟢 -8.56 kB 🟢 -7.42 kB
assets/Load3DControls-BbjSfdUJ.js (new) 30.9 kB 🔴 +30.9 kB 🔴 +5.34 kB 🔴 +4.65 kB
assets/Load3DControls-DlpIKrO1.js (removed) 30.9 kB 🟢 -30.9 kB 🟢 -5.34 kB 🟢 -4.64 kB
assets/WorkspacePanelContent-CE2TbJuD.js (new) 29.3 kB 🔴 +29.3 kB 🔴 +6.15 kB 🔴 +5.39 kB
assets/WorkspacePanelContent-G-zqlsWQ.js (removed) 29.3 kB 🟢 -29.3 kB 🟢 -6.15 kB 🟢 -5.4 kB
assets/SubscriptionRequiredDialogContent-D0Vh9pLg.js (removed) 26.2 kB 🟢 -26.2 kB 🟢 -6.57 kB 🟢 -5.79 kB
assets/SubscriptionRequiredDialogContent-DfE2bEUn.js (new) 26.2 kB 🔴 +26.2 kB 🔴 +6.57 kB 🔴 +5.79 kB
assets/Load3dViewerContent-1GSqOVc4.js (new) 23 kB 🔴 +23 kB 🔴 +5.18 kB 🔴 +4.5 kB
assets/Load3dViewerContent-C-ULoZK4.js (removed) 23 kB 🟢 -23 kB 🟢 -5.19 kB 🟢 -4.5 kB
assets/WidgetImageCrop-BHBNuEtA.js (new) 22.1 kB 🔴 +22.1 kB 🔴 +5.51 kB 🔴 +4.85 kB
assets/WidgetImageCrop-DNlv2iON.js (removed) 22.1 kB 🟢 -22.1 kB 🟢 -5.51 kB 🟢 -4.86 kB
assets/SubscriptionPanelContentWorkspace-BbBEJzYZ.js (new) 21.6 kB 🔴 +21.6 kB 🔴 +5.02 kB 🔴 +4.43 kB
assets/SubscriptionPanelContentWorkspace-DHxlxP4j.js (removed) 21.6 kB 🟢 -21.6 kB 🟢 -5.02 kB 🟢 -4.43 kB
assets/SignInContent-B5Hjicfl.js (new) 20 kB 🔴 +20 kB 🔴 +5.05 kB 🔴 +4.45 kB
assets/CurrentUserPopoverWorkspace-9zKwJ5nE.js (new) 19.8 kB 🔴 +19.8 kB 🔴 +4.86 kB 🔴 +4.33 kB
assets/CurrentUserPopoverWorkspace-DXRqFnm7.js (removed) 19.8 kB 🟢 -19.8 kB 🟢 -4.87 kB 🟢 -4.33 kB
assets/SignInContent-Ctobj-19.js (removed) 18.9 kB 🟢 -18.9 kB 🟢 -4.79 kB 🟢 -4.2 kB
assets/WidgetInputNumber-_9cdeLN9.js (removed) 18.6 kB 🟢 -18.6 kB 🟢 -4.71 kB 🟢 -4.18 kB
assets/WidgetInputNumber-BfUwGhFr.js (new) 18.6 kB 🔴 +18.6 kB 🔴 +4.71 kB 🔴 +4.19 kB
assets/WidgetRecordAudio-1FW899jS.js (removed) 17.3 kB 🟢 -17.3 kB 🟢 -4.95 kB 🟢 -4.42 kB
assets/WidgetRecordAudio-DXonZ7kp.js (new) 17.3 kB 🔴 +17.3 kB 🔴 +4.94 kB 🔴 +4.42 kB
assets/Load3D-CMeyMfus.js (new) 16.2 kB 🔴 +16.2 kB 🔴 +4.03 kB 🔴 +3.52 kB
assets/Load3D-qIpzcNhM.js (removed) 16.2 kB 🟢 -16.2 kB 🟢 -4.03 kB 🟢 -3.51 kB
assets/load3d-BsG68Zgj.js (new) 14.7 kB 🔴 +14.7 kB 🔴 +4.19 kB 🔴 +3.64 kB
assets/load3d-CBo_gxZs.js (removed) 14.7 kB 🟢 -14.7 kB 🟢 -4.19 kB 🟢 -3.64 kB
assets/LazyImage-Aa6aLK1I.js (removed) 12.3 kB 🟢 -12.3 kB 🟢 -3.81 kB 🟢 -3.35 kB
assets/LazyImage-DyiN6Wa4.js (new) 12.3 kB 🔴 +12.3 kB 🔴 +3.81 kB 🔴 +3.35 kB
assets/AudioPreviewPlayer-BMX2yIo4.js (removed) 10.9 kB 🟢 -10.9 kB 🟢 -3.2 kB 🟢 -2.85 kB
assets/AudioPreviewPlayer-D5YskUr-.js (new) 10.9 kB 🔴 +10.9 kB 🔴 +3.2 kB 🔴 +2.87 kB
assets/changeTracker-B9SIi5XR.js (new) 9.38 kB 🔴 +9.38 kB 🔴 +2.89 kB 🔴 +2.54 kB
assets/changeTracker-DTHRE7sQ.js (removed) 9.38 kB 🟢 -9.38 kB 🟢 -2.9 kB 🟢 -2.55 kB
assets/nodeTemplates-owf4TRnv.js (removed) 9.3 kB 🟢 -9.3 kB 🟢 -3.26 kB 🟢 -2.86 kB
assets/nodeTemplates-W9JNNBZE.js (new) 9.3 kB 🔴 +9.3 kB 🔴 +3.26 kB 🔴 +2.87 kB
assets/SelectValue-BPetVzj6.js (new) 8.94 kB 🔴 +8.94 kB 🔴 +2.27 kB 🔴 +2 kB
assets/SelectValue-C6nCOyTG.js (removed) 8.94 kB 🟢 -8.94 kB 🟢 -2.27 kB 🟢 -2 kB
assets/InviteMemberDialogContent-CfSUOhZQ.js (removed) 7.38 kB 🟢 -7.38 kB 🟢 -2.3 kB 🟢 -2 kB
assets/InviteMemberDialogContent-DRjuB72O.js (new) 7.38 kB 🔴 +7.38 kB 🔴 +2.29 kB 🔴 +2 kB
assets/WidgetToggleSwitch-Bbm1fk7b.js (new) 6.8 kB 🔴 +6.8 kB 🔴 +2.19 kB 🔴 +1.93 kB
assets/WidgetToggleSwitch-CriLM9xx.js (removed) 6.8 kB 🟢 -6.8 kB 🟢 -2.19 kB 🟢 -1.94 kB
assets/Load3DConfiguration-BAh8D-e9.js (removed) 6.27 kB 🟢 -6.27 kB 🟢 -1.92 kB 🟢 -1.68 kB
assets/Load3DConfiguration-CgrObHct.js (new) 6.27 kB 🔴 +6.27 kB 🔴 +1.91 kB 🔴 +1.68 kB
assets/CreateWorkspaceDialogContent-BCkCknHq.js (new) 5.53 kB 🔴 +5.53 kB 🔴 +1.99 kB 🔴 +1.74 kB
assets/CreateWorkspaceDialogContent-BoCnU3oY.js (removed) 5.53 kB 🟢 -5.53 kB 🟢 -1.99 kB 🟢 -1.74 kB
assets/EditWorkspaceDialogContent-BED_I-Q6.js (removed) 5.33 kB 🟢 -5.33 kB 🟢 -1.95 kB 🟢 -1.7 kB
assets/EditWorkspaceDialogContent-Jg_jkai8.js (new) 5.33 kB 🔴 +5.33 kB 🔴 +1.95 kB 🔴 +1.7 kB
assets/ValueControlPopover-B6ebiary.js (removed) 4.92 kB 🟢 -4.92 kB 🟢 -1.76 kB 🟢 -1.58 kB
assets/ValueControlPopover-IUIenQYm.js (new) 4.92 kB 🔴 +4.92 kB 🔴 +1.76 kB 🔴 +1.58 kB
assets/Preview3d-BlbDvSGy.js (removed) 4.82 kB 🟢 -4.82 kB 🟢 -1.57 kB 🟢 -1.37 kB
assets/Preview3d-CPGThjbf.js (new) 4.82 kB 🔴 +4.82 kB 🔴 +1.57 kB 🔴 +1.37 kB
assets/CancelSubscriptionDialogContent-6zB0-2ZY.js (removed) 4.79 kB 🟢 -4.79 kB 🟢 -1.78 kB 🟢 -1.57 kB
assets/CancelSubscriptionDialogContent-7-7kiJmr.js (new) 4.79 kB 🔴 +4.79 kB 🔴 +1.78 kB 🔴 +1.57 kB
assets/AnimationControls-Cav283Qb.js (removed) 4.61 kB 🟢 -4.61 kB 🟢 -1.6 kB 🟢 -1.41 kB
assets/AnimationControls-yi3oJiNr.js (new) 4.61 kB 🔴 +4.61 kB 🔴 +1.6 kB 🔴 +1.4 kB
assets/DeleteWorkspaceDialogContent-B8AxayCJ.js (new) 4.23 kB 🔴 +4.23 kB 🔴 +1.63 kB 🔴 +1.42 kB
assets/DeleteWorkspaceDialogContent-BAE7-E4h.js (removed) 4.23 kB 🟢 -4.23 kB 🟢 -1.64 kB 🟢 -1.43 kB
assets/WidgetWithControl-C2FbkCSt.js (removed) 4.07 kB 🟢 -4.07 kB 🟢 -1.75 kB 🟢 -1.57 kB
assets/WidgetWithControl-C5f4RAZ9.js (new) 4.07 kB 🔴 +4.07 kB 🔴 +1.75 kB 🔴 +1.57 kB
assets/LeaveWorkspaceDialogContent-C0UsM0FR.js (new) 4.06 kB 🔴 +4.06 kB 🔴 +1.58 kB 🔴 +1.38 kB
assets/LeaveWorkspaceDialogContent-Di7kNekF.js (removed) 4.06 kB 🟢 -4.06 kB 🟢 -1.58 kB 🟢 -1.39 kB
assets/RemoveMemberDialogContent-BmEgJnMo.js (removed) 4.04 kB 🟢 -4.04 kB 🟢 -1.53 kB 🟢 -1.34 kB
assets/RemoveMemberDialogContent-CffWe97d.js (new) 4.04 kB 🔴 +4.04 kB 🔴 +1.53 kB 🔴 +1.33 kB
assets/RevokeInviteDialogContent-_hQwXJ1g.js (removed) 3.96 kB 🟢 -3.96 kB 🟢 -1.55 kB 🟢 -1.36 kB
assets/RevokeInviteDialogContent-Kbag0xA3.js (new) 3.96 kB 🔴 +3.96 kB 🔴 +1.54 kB 🔴 +1.36 kB
assets/InviteMemberUpsellDialogContent-5UFzQM_e.js (removed) 3.83 kB 🟢 -3.83 kB 🟢 -1.4 kB 🟢 -1.24 kB
assets/InviteMemberUpsellDialogContent-D1s-kVQh.js (new) 3.83 kB 🔴 +3.83 kB 🔴 +1.4 kB 🔴 +1.23 kB
assets/Popover-CU8WUCW0.js (new) 3.65 kB 🔴 +3.65 kB 🔴 +1.44 kB 🔴 +1.27 kB
assets/Popover-D-qxBfh0.js (removed) 3.65 kB 🟢 -3.65 kB 🟢 -1.44 kB 🟢 -1.27 kB
assets/WidgetGalleria-Dv_XIwSb.js (removed) 3.61 kB 🟢 -3.61 kB 🟢 -1.4 kB 🟢 -1.25 kB
assets/WidgetGalleria-OYk7iIMP.js (new) 3.61 kB 🔴 +3.61 kB 🔴 +1.4 kB 🔴 +1.25 kB
assets/Slider-C3ME8F9R.js (new) 3.52 kB 🔴 +3.52 kB 🔴 +1.36 kB 🔴 +1.19 kB
assets/Slider-C93lIxy9.js (removed) 3.52 kB 🟢 -3.52 kB 🟢 -1.36 kB 🟢 -1.19 kB
assets/saveMesh-LvkJa_m_.js (removed) 3.38 kB 🟢 -3.38 kB 🟢 -1.46 kB 🟢 -1.3 kB
assets/saveMesh-My10F03j.js (new) 3.38 kB 🔴 +3.38 kB 🔴 +1.46 kB 🔴 +1.29 kB
assets/WidgetBoundingBox-BnN1Eo6h.js (removed) 3.19 kB 🟢 -3.19 kB 🟢 -895 B 🟢 -778 B
assets/WidgetBoundingBox-rZWa1Nys.js (new) 3.19 kB 🔴 +3.19 kB 🔴 +895 B 🔴 +779 B
assets/WidgetTextarea-DKDKaQ2N.js (removed) 3.13 kB 🟢 -3.13 kB 🟢 -1.29 kB 🟢 -1.14 kB
assets/WidgetTextarea-DT64xza3.js (new) 3.13 kB 🔴 +3.13 kB 🔴 +1.29 kB 🔴 +1.13 kB
assets/cloudSessionCookie-LEPy8pXp.js (removed) 3.1 kB 🟢 -3.1 kB 🟢 -1.09 kB 🟢 -954 B
assets/cloudSessionCookie-xu3--riK.js (new) 3.1 kB 🔴 +3.1 kB 🔴 +1.09 kB 🔴 +978 B
assets/WidgetMarkdown-7LlFg5-F.js (new) 2.93 kB 🔴 +2.93 kB 🔴 +1.23 kB 🔴 +1.07 kB
assets/WidgetMarkdown-D-IgUMWN.js (removed) 2.93 kB 🟢 -2.93 kB 🟢 -1.23 kB 🟢 -1.07 kB
assets/GlobalToast-B5cXDUO3.js (removed) 2.91 kB 🟢 -2.91 kB 🟢 -1.22 kB 🟢 -1.04 kB
assets/GlobalToast-Di2vm-Vf.js (new) 2.91 kB 🔴 +2.91 kB 🔴 +1.21 kB 🔴 +1.06 kB
assets/WidgetColorPicker-Dfmb72eI.js (removed) 2.9 kB 🟢 -2.9 kB 🟢 -1.23 kB 🟢 -1.1 kB
assets/WidgetColorPicker-Dkn8rsGj.js (new) 2.9 kB 🔴 +2.9 kB 🔴 +1.23 kB 🔴 +1.1 kB
assets/ApiNodesSignInContent-BCirXXBX.js (removed) 2.69 kB 🟢 -2.69 kB 🟢 -1.06 kB 🟢 -924 B
assets/ApiNodesSignInContent-tzvqE_Yj.js (new) 2.69 kB 🔴 +2.69 kB 🔴 +1.05 kB 🔴 +920 B
assets/WidgetChart-BAGFL1Oy.js (new) 2.21 kB 🔴 +2.21 kB 🔴 +953 B 🔴 +825 B
assets/WidgetChart-M9_HgbZg.js (removed) 2.21 kB 🟢 -2.21 kB 🟢 -954 B 🟢 -824 B
assets/SubscribeToRun-Bdlcff0s.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -1.01 kB 🟢 -866 B
assets/SubscribeToRun-O65TvB6i.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +1.01 kB 🔴 +881 B
assets/WidgetLayoutField-BYYhFbtc.js (removed) 1.95 kB 🟢 -1.95 kB 🟢 -877 B 🟢 -766 B
assets/WidgetLayoutField-DZWTzyAZ.js (new) 1.95 kB 🔴 +1.95 kB 🔴 +879 B 🔴 +766 B
assets/WidgetInputText-B0pyKoS_.js (removed) 1.86 kB 🟢 -1.86 kB 🟢 -875 B 🟢 -797 B
assets/WidgetInputText-Dr4QlXbz.js (new) 1.86 kB 🔴 +1.86 kB 🔴 +873 B 🔴 +803 B
assets/CloudRunButtonWrapper-2D7Ydnc2.js (new) 1.68 kB 🔴 +1.68 kB 🔴 +784 B 🔴 +712 B
assets/CloudRunButtonWrapper-D6rKsmq_.js (removed) 1.68 kB 🟢 -1.68 kB 🟢 -784 B 🟢 -717 B
assets/signInSchema-BnzFDhSh.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +566 B 🔴 +523 B
assets/signInSchema-rOdql-Uv.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -563 B 🟢 -513 B
assets/cloudBadges-CtT-AcnZ.js (new) 1.37 kB 🔴 +1.37 kB 🔴 +707 B 🔴 +604 B
assets/cloudBadges-Rgjd4sEl.js (removed) 1.37 kB 🟢 -1.37 kB 🟢 -706 B 🟢 -616 B
assets/cloudSubscription-C66Mn_46.js (new) 1.33 kB 🔴 +1.33 kB 🔴 +656 B 🔴 +569 B
assets/cloudSubscription-CJvE2flf.js (removed) 1.33 kB 🟢 -1.33 kB 🟢 -659 B 🟢 -565 B
assets/Load3D-BTBqbtVB.js (new) 1.07 kB 🔴 +1.07 kB 🔴 +499 B 🔴 +443 B
assets/Load3D-D2CrChdP.js (removed) 1.07 kB 🟢 -1.07 kB 🟢 -497 B 🟢 -443 B
assets/nightlyBadges-BRUI0MGI.js (new) 1 kB 🔴 +1 kB 🔴 +533 B 🔴 +474 B
assets/nightlyBadges-DzBYYq4w.js (removed) 1 kB 🟢 -1 kB 🟢 -534 B 🟢 -476 B
assets/Load3dViewerContent-Cfl1QgQB.js (removed) 995 B 🟢 -995 B 🟢 -468 B 🟢 -411 B
assets/Load3dViewerContent-DZOLIO-C.js (new) 995 B 🔴 +995 B 🔴 +468 B 🔴 +415 B
assets/SubscriptionPanelContentWorkspace-Ch4DHQTL.js (removed) 932 B 🟢 -932 B 🟢 -440 B 🟢 -382 B
assets/SubscriptionPanelContentWorkspace-DsMqsjwT.js (new) 932 B 🔴 +932 B 🔴 +437 B 🔴 +379 B
assets/graphHasMissingNodes-BL8_pio5.js (removed) 761 B 🟢 -761 B 🟢 -371 B 🟢 -350 B
assets/graphHasMissingNodes-C42X4yvY.js (new) 761 B 🔴 +761 B 🔴 +374 B 🔴 +330 B
assets/changeTracker-CPpxVMgt.js (removed) 759 B 🟢 -759 B 🟢 -380 B 🟢 -338 B
assets/changeTracker-DV7aAs2B.js (new) 759 B 🔴 +759 B 🔴 +383 B 🔴 +338 B
assets/WidgetLegacy-C6OncUid.js (removed) 747 B 🟢 -747 B 🟢 -381 B 🟢 -358 B
assets/WidgetLegacy-CzXq6fZQ.js (new) 747 B 🔴 +747 B 🔴 +384 B 🔴 +337 B
assets/WidgetInputNumber-Dzfxgz-D.js (removed) 469 B 🟢 -469 B 🟢 -263 B 🟢 -227 B
assets/WidgetInputNumber-g7svZDeO.js (new) 469 B 🔴 +469 B 🔴 +263 B 🔴 +228 B
assets/WidgetBoundingBox-BNApKt6O.js (new) 283 B 🔴 +283 B 🔴 +184 B 🔴 +166 B
assets/WidgetBoundingBox-DGkCPuUx.js (removed) 283 B 🟢 -283 B 🟢 -187 B 🟢 -161 B
assets/src-BBLtS5nq.js (new) 251 B 🔴 +251 B 🔴 +210 B 🔴 +181 B
assets/src-BM32AeDI.js (removed) 251 B 🟢 -251 B 🟢 -212 B 🟢 -183 B
assets/i18n-0q9rSAFc.js (removed) 199 B 🟢 -199 B 🟢 -162 B 🟢 -139 B
assets/i18n-CN_aStPc.js (new) 199 B 🔴 +199 B 🔴 +160 B 🔴 +138 B
assets/auto-CkuZicVk.js 1.7 kB 1.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/BaseViewTemplate-ndtvsvUb.js 1.78 kB 1.78 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/comfy-logo-single-BOy4dLVz.js 198 B 198 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/ComfyOrgHeader-lEigKlcw.js 910 B 910 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Bby2febn.js 17.5 kB 17.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BKmFL2QC.js 15.2 kB 15.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BNpTn9uz.js 16.1 kB 16.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C7Z8bnzO.js 18.8 kB 18.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CD5IDg_4.js 17.5 kB 17.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CPUZB452.js 16.1 kB 16.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-Dk2oeM9s.js 16.7 kB 16.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DMj08GVE.js 16.1 kB 16.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DYSEOs6b.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-M6XaeehJ.js 15.1 kB 15.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-xeygSHEJ.js 16.9 kB 16.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/constants-CSuZkFI0.js 579 B 579 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-4BcAaOTc.js 139 kB 139 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bg5cayOv.js 143 kB 143 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BXCMUxAI.js 194 kB 194 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-C-ubKMvD.js 167 kB 167 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Ccv6mb-Q.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Ck1YAwgP.js 138 kB 138 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CTcttQfN.js 173 kB 173 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CvTCILBi.js 141 kB 141 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CYzqtgdm.js 146 kB 146 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DyPfngEQ.js 122 kB 122 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-m0CBxVe8.js 123 kB 123 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/Media3DTop-CmGGqSiN.js 1.82 kB 1.82 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-zM5lg836.js 1.43 kB 1.43 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-ehyhGKmX.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-CKKXQJZR.js 2.23 kB 2.23 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs--6_mYlRO.js 391 kB 391 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-3u4v-kS1.js 440 kB 440 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BH8mhXzJ.js 392 kB 392 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BmowbuJi.js 356 kB 356 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BnBVG5qa.js 395 kB 395 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BqRtcdE-.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CVGJ6uZG.js 441 kB 441 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DlXVzxIC.js 388 kB 388 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DNAHMp4N.js 383 kB 383 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DuCwYOrl.js 360 kB 360 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-V_dqZ5BH.js 480 kB 480 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/previousFullPath-BF8DBOKp.js 665 B 665 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/rolldown-runtime-DLICfi3-.js 1.97 kB 1.97 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/telemetry-zZf2dHJ2.js 226 B 226 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/types-DT3N7am7.js 204 B 204 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widget-DTUjK0ZE.js 445 B 445 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-CQpRvH6q.js 3.1 kB 3.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BmHUlE-K.js 1.1 kB 1.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetTypes-BtuQMzwn.js 393 B 393 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 67 added / 67 removed

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/dialog/content/signin/SignUpForm.vue (1)

26-34: ⚠️ Potential issue | 🟠 Major

Guard against duplicate submits while loading is true.

When loading is true, the button disappears but the <form> can still submit via Enter key. This can re-trigger emit('submit', …) while a request is in flight.

🛡️ Suggested guard
 const onSubmit = useThrottleFn(
   handleSubmit((submittedValues) => {
+    if (loading.value) return
     emit('submit', submittedValues)
   }),
   1_500
 )

Also applies to: 79-82

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/SignUpForm.vue` around lines 26 - 34,
The form currently still submits via Enter while loading because the template
only hides the button; add a guard at the start of the component's submit
handler(s) to return early when the reactive loading flag is true (e.g., in the
method that calls emit('submit', …) — check functions like onSubmit/handleSubmit
and any other submit handlers around the other occurrence at lines 79-82) so
duplicate emits are prevented; optionally also add `@submit.prevent` on the <form>
handler to ensure keyboard submits go through the guarded handler only.
🧹 Nitpick comments (2)
src/components/ui/form/useFormField.ts (1)

9-35: Prefer a function declaration for this composable to match the repo's convention.

The codebase predominantly uses export function useXxx() pattern for composables (e.g., useWorkspaceBilling, useWorkflowValidation, useAssetSelection). This aligns with the guideline: Do not use function expressions if it's possible to use function declarations instead.

♻️ Proposed fix
-export const useFormField = () => {
+export function useFormField() {
   const fieldName = inject(FORM_FIELD_NAME_INJECTION_KEY)
   const itemId = inject(FORM_ITEM_ID_INJECTION_KEY)

   if (!fieldName || !itemId) {
     throw new Error('useFormField must be used within FormField and FormItem')
   }

   const errorMessage = useFieldError(fieldName)
   const formItemId = `${itemId}-form-item`
   const formDescriptionId = `${itemId}-form-item-description`
   const formMessageId = `${itemId}-form-item-message`
   const describedBy = computed(() =>
     errorMessage.value
       ? `${formDescriptionId} ${formMessageId}`
       : formDescriptionId
   )

   return {
     errorMessage,
     formDescriptionId,
     formItemId,
     formMessageId,
     describedBy,
     name: fieldName
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/form/useFormField.ts` around lines 9 - 35, Change the
composable from a function expression to a function declaration: replace the
exported constant declaration "export const useFormField = () => { ... }" with a
named function declaration "export function useFormField() { ... }" while
preserving all internal logic (injections FORM_FIELD_NAME_INJECTION_KEY and
FORM_ITEM_ID_INJECTION_KEY, the error throw, useFieldError(fieldName), the
computed describedBy, and the returned properties formDescriptionId, formItemId,
formMessageId, errorMessage, describedBy, name). Ensure the function name
remains useFormField and exports the function as before.
src/components/dialog/content/signin/SignUpForm.vue (1)

6-16: Prefer $t in the template and drop useI18n when unused in script.

Line 6Line 16 use t() only in the template. The repo preference is to use $t in template-only cases to avoid unnecessary imports and setup work.
Based on learnings: “In Vue single-file components where the i18n t function is only used within the template, prefer using the built-in $t in the template instead of importing useI18n…”

🔧 Proposed refactor
-        <FormLabel class="mb-2 text-base font-medium opacity-80">
-          {{ t('auth.signup.emailLabel') }}
-        </FormLabel>
+        <FormLabel class="mb-2 text-base font-medium opacity-80">
+          {{ $t('auth.signup.emailLabel') }}
+        </FormLabel>
...
-            :placeholder="t('auth.signup.emailPlaceholder')"
+            :placeholder="$t('auth.signup.emailPlaceholder')"
...
-      {{ t('auth.signup.signUpButton') }}
+      {{ $t('auth.signup.signUpButton') }}

- import { useI18n } from 'vue-i18n'
...
- const { t } = useI18n()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/SignUpForm.vue` around lines 6 - 16, The
template is using the composition API i18n helper t(...) but since i18n is only
used in the template you should switch to the built-in $t and remove the unused
useI18n setup/import: replace occurrences like t('auth.signup.emailLabel') and
t('auth.signup.emailPlaceholder') with $t('...') in the template (e.g., inside
FormLabel and InputText) and delete any useI18n import/const call from the
script section so there are no unused imports or setup code remaining.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pnpm-workspace.yaml`:
- Line 38: The repo's `@vee-validate/zod` (referenced as '@vee-validate/zod')
requires zod ^3.24.0 but the workspace currently constrains zod to ^3.23.8;
update the zod version constraint to ^3.24.0 (or newer) wherever zod is declared
(ensure you update all occurrences mentioned alongside '@vee-validate/zod'), or
alternatively downgrade/pin '@vee-validate/zod' to a version compatible with zod
^3.23.8; adjust the package dependency entries for zod and/or `@vee-validate/zod`
so the peer dependency requirement is satisfied.

In `@src/components/dialog/content/signin/PasswordFields.vue`:
- Around line 93-122: In PasswordFields.vue the computed passwordChecks accesses
values.password directly which can be undefined; update the passwordChecks
computed to coerce or guard values.password (e.g. const pwd = values.password ??
'' or String(values.password)) and then use pwd.length and run the regex tests
against pwd (replace occurrences of values.password.length and
/.../.test(values.password) with pwd.length and /.../.test(pwd)) so undefined
passwords no longer throw; target the passwordChecks computed constant to apply
this fix.

In `@src/components/dialog/content/signin/SignInForm.vue`:
- Around line 37-45: The clickable <span> is not keyboard-accessible; replace it
with the shared Button (or an anchor/button element) so the "Forgot password"
control is semantic and focusable: in SignInForm.vue change the span to use the
shared Button component (or a <button>) and wire its click to
handleForgotPassword(values.email, !errors.email), keep the same
classes/conditional 'text-link-disabled' logic via :class or props, and ensure
it has appropriate type="button" and aria-disabled when values.email is empty or
errors.email is present so keyboard and screen-reader users can activate or
understand the state.

In `@src/components/ui/form/FormControl.vue`:
- Around line 10-23: The component only extracts the class prop and drops all
other incoming attributes; update the props destructuring to capture rest props
(e.g., const { class: className, ...rest } = defineProps< { class?:
HTMLAttributes['class']; [key: string]: unknown } >()) and then forward them
onto the Slot wrapper (add v-bind="rest" or :="rest" to the Slot element) while
keeping the existing :id="formItemId", :aria-describedby="describedBy",
:aria-invalid="Boolean(errorMessage)" and :class="cn(className)"; this ensures
data-* and custom aria-* props passed to FormControl.vue are forwarded to the
underlying Slot.

In `@src/components/ui/form/FormDescription.vue`:
- Around line 15-17: The paragraph in FormDescription.vue uses the wrong design
token: update the class string in the template (the <p> element that binds
:id="formDescriptionId" and uses cn(..., className)) to replace "text-muted"
with "text-muted-foreground" so it follows the design-token guideline; ensure
you only change that token and keep the existing cn(...) usage and props
(formDescriptionId, className) intact.

In `@src/components/ui/form/index.ts`:
- Around line 1-6: Remove the barrel export file (index.ts) that re-exports
FormControl, FormDescription, FormField, FormItem, FormLabel, and FormMessage;
instead delete this file and update all consumers (notably SignInForm.vue and
PasswordFields.vue) to import the specific components directly from their .vue
files (for example import FormField from './FormField.vue'), replacing any
imports that reference the barrel with direct file paths for each exported
symbol.

---

Outside diff comments:
In `@src/components/dialog/content/signin/SignUpForm.vue`:
- Around line 26-34: The form currently still submits via Enter while loading
because the template only hides the button; add a guard at the start of the
component's submit handler(s) to return early when the reactive loading flag is
true (e.g., in the method that calls emit('submit', …) — check functions like
onSubmit/handleSubmit and any other submit handlers around the other occurrence
at lines 79-82) so duplicate emits are prevented; optionally also add
`@submit.prevent` on the <form> handler to ensure keyboard submits go through the
guarded handler only.

---

Nitpick comments:
In `@src/components/dialog/content/signin/SignUpForm.vue`:
- Around line 6-16: The template is using the composition API i18n helper t(...)
but since i18n is only used in the template you should switch to the built-in $t
and remove the unused useI18n setup/import: replace occurrences like
t('auth.signup.emailLabel') and t('auth.signup.emailPlaceholder') with $t('...')
in the template (e.g., inside FormLabel and InputText) and delete any useI18n
import/const call from the script section so there are no unused imports or
setup code remaining.

In `@src/components/ui/form/useFormField.ts`:
- Around line 9-35: Change the composable from a function expression to a
function declaration: replace the exported constant declaration "export const
useFormField = () => { ... }" with a named function declaration "export function
useFormField() { ... }" while preserving all internal logic (injections
FORM_FIELD_NAME_INJECTION_KEY and FORM_ITEM_ID_INJECTION_KEY, the error throw,
useFieldError(fieldName), the computed describedBy, and the returned properties
formDescriptionId, formItemId, formMessageId, errorMessage, describedBy, name).
Ensure the function name remains useFormField and exports the function as
before.

'@types/node': ^24.1.0
'@types/semver': ^7.7.0
'@types/three': ^0.169.0
'@vee-validate/zod': ^4.15.1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check `@vee-validate/zod` 4.15.1 peerDependencies
curl -s https://registry.npmjs.org/@vee-validate/zod/4.15.1 | jq '.peerDependencies'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 92


Upgrade Zod to satisfy @vee-validate/zod 4.15.1's peer dependency.

@vee-validate/zod 4.15.1 requires zod ^3.24.0, but the repository currently uses zod ^3.23.8. Either upgrade zod to ^3.24.0 or newer, or use a compatible @vee-validate/zod version.

Also applies to: line 95

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pnpm-workspace.yaml` at line 38, The repo's `@vee-validate/zod` (referenced as
'@vee-validate/zod') requires zod ^3.24.0 but the workspace currently constrains
zod to ^3.23.8; update the zod version constraint to ^3.24.0 (or newer) wherever
zod is declared (ensure you update all occurrences mentioned alongside
'@vee-validate/zod'), or alternatively downgrade/pin '@vee-validate/zod' to a
version compatible with zod ^3.23.8; adjust the package dependency entries for
zod and/or `@vee-validate/zod` so the peer dependency requirement is satisfied.

Comment on lines +10 to +23
const { class: className } = defineProps<{
class?: HTMLAttributes['class']
}>()

const { describedBy, errorMessage, formItemId } = useFormField()
</script>

<template>
<Slot
:id="formItemId"
:aria-describedby="describedBy"
:aria-invalid="Boolean(errorMessage)"
:class="cn(className)"
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine FormControl.vue
find . -name "FormControl.vue" -type f

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 110


🏁 Script executed:

# Check the actual content of FormControl.vue
cat -n src/components/ui/form/FormControl.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 784


🏁 Script executed:

# Check for useForwardProps usage patterns in UI components
rg -n "useForwardProps" src/components/ui

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2243


🏁 Script executed:

# Look for other Reka UI Slot wrapper patterns
rg -n "from 'reka-ui'" src/components/ui | head -20

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2125


🏁 Script executed:

# Check if there's anything special about Slot component in reka-ui documentation or other similar patterns
rg -n "Slot" src/components/ui -A 5 | head -40

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2429


🏁 Script executed:

# Look at similar simple wrapper patterns (e.g., form-related components)
find src/components/ui/form -name "*.vue" -type f | xargs -I {} sh -c 'echo "=== {} ===" && head -15 {}'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 2290


🏁 Script executed:

# Check what Slot component from reka-ui actually supports (look at its usage in similar contexts)
rg -n "Slot" src/components/ui -B 2 -A 10 | grep -A 10 "from 'reka-ui'"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 795


🏁 Script executed:

# Examine FormDescription, FormLabel, FormMessage to see if they're simple divs or also Reka UI wrappers
cat -n src/components/ui/form/FormDescription.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 623


🏁 Script executed:

# Check if there are any tests or usage examples of FormControl that might shed light on what attrs might be passed
rg -n "FormControl" src --type vue -A 5 | head -50

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 97


🏁 Script executed:

# Let's also check the Reka UI Slot component documentation or any type hints
rg -n "SlotProps\|Slot.*Props" src/components/ui -A 3

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Let me check if there are any examples showing how Slot is typically used in reka-ui context
rg -n "Slot" src -B 3 -A 3 --type-list | head -60

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 459


🏁 Script executed:

# Check node_modules or package.json to understand reka-ui version
cat package.json | grep -A 2 -B 2 reka-ui

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 203


🏁 Script executed:

# Check if Slot from reka-ui has any special docs or if similar primitive wrappers exist
rg -n "import.*Slot.*from.*reka-ui" src -A 20 | head -60

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1388


🏁 Script executed:

# Let's check ToggleGroupItem as a reference - it uses useForwardProps with a Reka UI component
cat -n src/components/ui/toggle-group/ToggleGroupItem.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1608


🏁 Script executed:

# Check if there are any consumers of FormControl that might pass additional attributes
rg -n "FormControl" src -B 2 -A 8 | grep -v "test" | head -80

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 6602


🏁 Script executed:

# Look at SelectValue and SelectSeparator (simple Slot-like primitives) to see if they also might benefit from rest props
cat -n src/components/ui/select/SelectValue.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 406


Forward rest props through the Slot wrapper.

As a Reka UI wrapper, this component should forward non-class props so consumers can pass through attributes (e.g., data-*, custom aria-* values) to the underlying Slot.

♻️ Proposed fix
-import { Slot } from 'reka-ui'
+import { Slot, useForwardProps } from 'reka-ui'

-const { class: className } = defineProps<{
+const { class: className, ...restProps } = defineProps<{
   class?: HTMLAttributes['class']
 }>()
+const forwardedProps = useForwardProps(restProps)

 <Slot
+  v-bind="forwardedProps"
   :id="formItemId"
   :aria-describedby="describedBy"
   :aria-invalid="Boolean(errorMessage)"
   :class="cn(className)"
 >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/form/FormControl.vue` around lines 10 - 23, The component
only extracts the class prop and drops all other incoming attributes; update the
props destructuring to capture rest props (e.g., const { class: className,
...rest } = defineProps< { class?: HTMLAttributes['class']; [key: string]:
unknown } >()) and then forward them onto the Slot wrapper (add v-bind="rest" or
:="rest" to the Slot element) while keeping the existing :id="formItemId",
:aria-describedby="describedBy", :aria-invalid="Boolean(errorMessage)" and
:class="cn(className)"; this ensures data-* and custom aria-* props passed to
FormControl.vue are forwarded to the underlying Slot.

Comment on lines +1 to +6
export { default as FormControl } from './FormControl.vue'
export { default as FormDescription } from './FormDescription.vue'
export { default as FormField } from './FormField.vue'
export { default as FormItem } from './FormItem.vue'
export { default as FormLabel } from './FormLabel.vue'
export { default as FormMessage } from './FormMessage.vue'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid adding a barrel file under src/.

The repo disallows barrel exports in index.ts. Please remove this file and update consumers to import each component directly (e.g., from ./FormField.vue). This will also affect imports in SignInForm.vue and PasswordFields.vue.

As per coding guidelines: **/index.ts: Don't use barrel files (e.g., /some/package/index.ts to re-export within /src).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/form/index.ts` around lines 1 - 6, Remove the barrel export
file (index.ts) that re-exports FormControl, FormDescription, FormField,
FormItem, FormLabel, and FormMessage; instead delete this file and update all
consumers (notably SignInForm.vue and PasswordFields.vue) to import the specific
components directly from their .vue files (for example import FormField from
'./FormField.vue'), replacing any imports that reference the barrel with direct
file paths for each exported symbol.

@DrJKL DrJKL requested a review from viva-jinyi February 21, 2026 04:17
DrJKL and others added 2 commits February 20, 2026 21:41
- Remove barrel file (ui/form/index.ts), use direct imports

- Use text-muted-foreground design token in FormDescription

- Replace span with Button for keyboard-accessible forgot password

- Use function declaration for useFormField composable

- Guard against undefined values.password in PasswordFields

Amp-Thread-ID: https://ampcode.com/threads/T-019c7ebc-b163-72d9-a20e-ed38717b9cbb
Co-authored-by: Amp <amp@ampcode.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/scripts/changeTracker.ts (1)

431-446: Avoid in-place sorting during graph diff.

graph.nodes.sort(...) mutates the workflow state; graphDiff is called during debug logging and can still reorder nodes. Prefer a non-mutating copy to keep state stable.

♻️ Suggested change (non-mutating sort)
-        nodes: graph.nodes.sort((a: ComfyNode, b: ComfyNode) => {
+        nodes: [...graph.nodes].sort((a: ComfyNode, b: ComfyNode) => {
           if (typeof a.id === 'number' && typeof b.id === 'number') {
             return a.id - b.id
           }
           return 0
         })

As per coding guidelines, “Avoid mutable state; prefer immutability and assignment at point of declaration.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/scripts/changeTracker.ts` around lines 431 - 446, The function
sortGraphNodes currently calls graph.nodes.sort(...) which mutates the original
workflow; change it to perform a non-mutating sort by creating a copy of the
nodes array (e.g. using [...graph.nodes] or graph.nodes.slice()) and sorting
that copy, then return the new graph object with the sorted copy assigned to the
nodes property (preserving links, floatingLinks, reroutes, groups, extra,
definitions, subgraphs). Target the sortGraphNodes function and the nodes array
usage to ensure graphDiff/debug logging no longer reorders the original graph.
src/utils/veeValidateZod.ts (1)

4-23: Consider using function declaration for pure helper.

Per repository conventions, prefer function declarations over function expressions for pure functions.

♻️ Proposed refactor
-const buildTypedSchemaErrors = (issues: z.ZodIssue[]): TypedSchemaError[] => {
+function buildTypedSchemaErrors(issues: z.ZodIssue[]): TypedSchemaError[] {
   const groupedErrors = new Map<string, TypedSchemaError>()

   for (const issue of issues) {
     const path = issue.path.length > 0 ? issue.path.join('.') : ''
     const existingError = groupedErrors.get(path)

     if (existingError) {
       existingError.errors.push(issue.message)
       continue
     }

     groupedErrors.set(path, {
       path: path || undefined,
       errors: [issue.message]
     })
   }

   return [...groupedErrors.values()]
 }

Based on learnings: "Prefer pure function declarations over function expressions."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/veeValidateZod.ts` around lines 4 - 23, Replace the arrow function
expression buildTypedSchemaErrors with a function declaration while keeping its
signature and body intact; specifically, change "const buildTypedSchemaErrors =
(issues: z.ZodIssue[]): TypedSchemaError[] => { ... }" to "function
buildTypedSchemaErrors(issues: z.ZodIssue[]): TypedSchemaError[] { ... }",
preserving the internal logic (groupedErrors Map, path computation, merging
messages) and the TypedSchemaError and z.ZodIssue types so behavior and types
remain unchanged.
src/components/dialog/content/signin/ApiKeyForm.vue (1)

127-138: Prefer a function declaration for onSubmit.

The handler doesn’t rely on closure semantics, so a declaration keeps it aligned with the repo’s style.

♻️ Suggested change
-const onSubmit = async (event: Event) => {
+async function onSubmit(event: Event) {
   event.preventDefault()
   const { valid, values: submittedValues } = await validate()
   if (!valid) {
     return
   }

   if (submittedValues?.apiKey) {
     await apiKeyStore.storeApiKey(submittedValues.apiKey)
     emit('success')
   }
-}
+}
As per coding guidelines: Do not use function expressions if it's possible to use function declarations instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/ApiKeyForm.vue` around lines 127 - 138,
Convert the onSubmit arrow function into an async function declaration named
onSubmit to match project style: replace the const onSubmit = async (event:
Event) => { ... } with async function onSubmit(event: Event) { ... } while
preserving the existing logic that calls validate(), checks valid, uses
submittedValues?.apiKey, calls apiKeyStore.storeApiKey(submittedValues.apiKey),
and emits 'success'; ensure any references to onSubmit (e.g. form `@submit` or
template bindings) continue to point to the same identifier.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/platform/workflow/validation/schemas/workflowSchema.ts`:
- Around line 348-352: The Zod generic usage for zSubgraphDefinition is using
Zod v4-style two-type parameters; update the declaration for Zod v3
compatibility by removing the second generic parameter (i.e., use
z.ZodType<SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>> so the Def defaults)
or alternatively replace the second generic with the proper ZodTypeDef type if
you intentionally need a Def; adjust the declaration for zSubgraphDefinition and
any other occurrences referencing
SubgraphDefinitionBase/ComfyWorkflow1BaseOutput to match Zod v3 generics.

In `@src/schemas/apiSchema.ts`:
- Line 313: The z.enum(LinkMarkerShape) call fails because LinkMarkerShape is a
numeric TS enum; update the schema at 'Comfy.Graph.LinkMarkers' to accept
numeric values by replacing z.enum(LinkMarkerShape) with a numeric union such as
z.union([z.literal(0), z.literal(1), z.literal(2)]) (or alternatively convert
LinkMarkerShape to a string enum and keep z.enum), ensuring the schema uses the
same numeric values as the LinkMarkerShape enum.

---

Duplicate comments:
In `@pnpm-workspace.yaml`:
- Around line 94-111: Confirm that the bumped Zod version (^4.3.6) is supported
by the adapter and utilities by inspecting their peerDependencies for Zod 4:
check `@vee-validate/zod`, zod-to-json-schema, and zod-validation-error (and any
other packages referencing Zod) and if they do not declare compatibility with
Zod 4, either pin Zod to a compatible 3.x version or upgrade/lock those packages
to versions that declare Zod 4 support (or add a resolutions/overrides entry) so
the dependency graph consistently uses a Zod-4-compatible set.

In `@src/components/dialog/content/signin/SignInForm.vue`:
- Around line 37-45: Replace the non-semantic <span> with a real <button
type="button"> in the SignInForm.vue block so the forgot-password control is
keyboard accessible: bind :disabled="!values.email || Boolean(errors.email)"
instead of toggling classes only, keep the click handler
`@click`="handleForgotPassword(values.email, !errors.email)", add aria-disabled
when disabled, preserve the text via {{ t('auth.login.forgotPassword') }}, and
ensure the button does not submit the form (type="button") and styling/classes
are adjusted to match the current visual states.

---

Nitpick comments:
In `@src/components/dialog/content/signin/ApiKeyForm.vue`:
- Around line 127-138: Convert the onSubmit arrow function into an async
function declaration named onSubmit to match project style: replace the const
onSubmit = async (event: Event) => { ... } with async function onSubmit(event:
Event) { ... } while preserving the existing logic that calls validate(), checks
valid, uses submittedValues?.apiKey, calls
apiKeyStore.storeApiKey(submittedValues.apiKey), and emits 'success'; ensure any
references to onSubmit (e.g. form `@submit` or template bindings) continue to
point to the same identifier.

In `@src/scripts/changeTracker.ts`:
- Around line 431-446: The function sortGraphNodes currently calls
graph.nodes.sort(...) which mutates the original workflow; change it to perform
a non-mutating sort by creating a copy of the nodes array (e.g. using
[...graph.nodes] or graph.nodes.slice()) and sorting that copy, then return the
new graph object with the sorted copy assigned to the nodes property (preserving
links, floatingLinks, reroutes, groups, extra, definitions, subgraphs). Target
the sortGraphNodes function and the nodes array usage to ensure graphDiff/debug
logging no longer reorders the original graph.

In `@src/utils/veeValidateZod.ts`:
- Around line 4-23: Replace the arrow function expression buildTypedSchemaErrors
with a function declaration while keeping its signature and body intact;
specifically, change "const buildTypedSchemaErrors = (issues: z.ZodIssue[]):
TypedSchemaError[] => { ... }" to "function buildTypedSchemaErrors(issues:
z.ZodIssue[]): TypedSchemaError[] { ... }", preserving the internal logic
(groupedErrors Map, path computation, merging messages) and the TypedSchemaError
and z.ZodIssue types so behavior and types remain unchanged.

Comment on lines +348 to +352
// eslint-disable-next-line prefer-const -- Forward declaration required for recursive schema.
let zSubgraphDefinition: z.ZodType<
SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>,
SubgraphDefinitionBase<ComfyWorkflow1BaseInput>
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check installed Zod version and ZodType definition
rg -n '"zod"' package.json 2>/dev/null || echo "No package.json found with zod"

# Look for ZodType definition in the codebase or node_modules
if [ -f "package.json" ]; then
  cat package.json | grep -A 5 -B 5 '"zod"' || true
fi

# Check node_modules for Zod type definitions
if [ -d "node_modules/zod" ]; then
  echo "=== Zod types.d.ts ZodType definition ==="
  head -n 100 node_modules/zod/lib/types.d.ts 2>/dev/null | grep -A 20 "class ZodType" || true
fi

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 756


🏁 Script executed:

#!/bin/bash
# Look for the specific file mentioned in the review
cat -n src/platform/workflow/validation/schemas/workflowSchema.ts | sed -n '345,355p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 465


🏁 Script executed:

#!/bin/bash
# Search for how ZodType is used elsewhere in the codebase
rg "z\.ZodType<" src/ -A 2 | head -n 40

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 717


🌐 Web query:

Zod v3 ZodType generic parameters definition

💡 Result:

In Zod v3, the base schema type is:

class ZodType<
  Output = any,
  Def extends ZodTypeDef = ZodTypeDef,
  Input = Output
> { /* ... */ }

[1]

What the generic parameters mean:

  • Output: the type you get after parsing (i.e., z.output<typeof schema> / z.infer<typeof schema>). [1]
  • Def: the internal “definition” type that describes the schema (a subtype of ZodTypeDef). Most users leave this as the default. [1]
  • Input: the type accepted as input to parsing (i.e., z.input<typeof schema>). Defaults to Output, but differs when you use things like .transform() / ZodEffects. [1], [2]

Example where InputOutput (because of a transform), using ZodType<Output, Def, Input> explicitly: [2]

const schema: z.ZodType<ILocal, z.ZodTypeDef, IRemote> =
  z.object({ b: z.number() }).transform(({ b }) => ({ a: b }));

Sources: [1] Zod v3 README on npm (shows the ZodType<Output, Def, Input> definition), [2] Stack Overflow example using ZodType<..., z.ZodTypeDef, ...> with differing input/output.


🌐 Web query:

Zod v4 ZodType generic parameters definition

💡 Result:

In Zod v4 (the "zod/v4" classic API), ZodType is defined with (up to) three generic parameters:

export interface ZodType<
  out Output = unknown,
  out Input = unknown,
  out Internals extends core.$ZodTypeInternals<Output, Input> =
    core.$ZodTypeInternals<Output, Input>
> extends core.$ZodType<Output, Input, Internals> { /* ... */ }

[1]

Key v4 change vs v3: the old middle generic Def extends ZodTypeDef was removed, and Input now defaults to unknown (not Output). [2]

Related: the underlying core base type is $ZodType<Output = unknown, Input = unknown>. [3]

Sources: [1] [2] [3]


Correct the ZodType generic parameters for Zod v3 compatibility.

The code uses z.ZodType<Output, Input> syntax (Zod v4), but the project is on Zod v3 (^3.23.8). In Zod v3, the second generic parameter is Def extends ZodTypeDef (internal definition), not Input. Either:

  1. Use z.ZodType<SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>> (omit the second parameter to let it default), or
  2. Upgrade to Zod v4 if migration is planned.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/workflow/validation/schemas/workflowSchema.ts` around lines 348
- 352, The Zod generic usage for zSubgraphDefinition is using Zod v4-style
two-type parameters; update the declaration for Zod v3 compatibility by removing
the second generic parameter (i.e., use
z.ZodType<SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>> so the Def defaults)
or alternatively replace the second generic with the proper ZodTypeDef type if
you intentionally need a Def; adjust the declaration for zSubgraphDefinition and
any other occurrences referencing
SubgraphDefinitionBase/ComfyWorkflow1BaseOutput to match Zod v3 generics.

'Comfy.Graph.DeduplicateSubgraphNodeIds': z.boolean(),
'Comfy.Graph.LiveSelection': z.boolean(),
'Comfy.Graph.LinkMarkers': z.nativeEnum(LinkMarkerShape),
'Comfy.Graph.LinkMarkers': z.enum(LinkMarkerShape),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find package.json to check Zod version
fd package.json -x cat {} \; | head -100

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 4222


🏁 Script executed:

# Find LinkMarkerShape definition
rg -n "LinkMarkerShape\s*=" --type=ts -A3 | head -50

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 496


🏁 Script executed:

# Check the actual code at src/schemas/apiSchema.ts line 313 with context
cat -n src/schemas/apiSchema.ts | sed -n '305,320p'

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 926


🏁 Script executed:

# Verify Zod import and usage in apiSchema.ts
head -50 src/schemas/apiSchema.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1710


🏁 Script executed:

# Find LinkMarkerShape definition in litegraph.ts
rg -n "enum LinkMarkerShape|LinkMarkerShape\s*=" src/lib/litegraph/src/litegraph.ts -A10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 52


🏁 Script executed:

# Check if it's exported and the exact format
cat -n src/lib/litegraph/src/litegraph.ts | grep -A15 -B5 "LinkMarkerShape"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 819


🏁 Script executed:

# Find Zod version from pnpm-lock.yaml or catalog
rg "zod" pnpm-lock.yaml -A2 | head -30

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 890


🏁 Script executed:

# Also check for package catalog
find . -name "pnpm-workspace.yaml" -o -name ".npmrc" -o -name "package-lock.json" | xargs cat 2>/dev/null | head -50

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1416


🏁 Script executed:

# Check root level catalog definition
cat -n .npmrc catalog.json pnpm-workspace.yaml 2>/dev/null | head -100

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 3296


🏁 Script executed:

# Find and check the LinkMarkerShape definition in globalEnums
find . -name "globalEnums*" -type f | head -10

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 114


🏁 Script executed:

# Search for LinkMarkerShape in types directory
rg -n "LinkMarkerShape" src/lib/litegraph/src/types/ -A10 -B2

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 912


🏁 Script executed:

# Get the full definition from globalEnums
cat -n src/lib/litegraph/src/types/globalEnums.ts | grep -A20 "LinkMarkerShape"

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 574


Fix z.enum(LinkMarkerShape) to work with numeric enum values.

The project uses Zod 4.3.6, where z.enum() accepts only string literals or string enums, but LinkMarkerShape is a numeric TypeScript enum (0, 1, 2). Use z.union([z.literal(0), z.literal(1), z.literal(2)]) or convert LinkMarkerShape to a string enum instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/schemas/apiSchema.ts` at line 313, The z.enum(LinkMarkerShape) call fails
because LinkMarkerShape is a numeric TS enum; update the schema at
'Comfy.Graph.LinkMarkers' to accept numeric values by replacing
z.enum(LinkMarkerShape) with a numeric union such as z.union([z.literal(0),
z.literal(1), z.literal(2)]) (or alternatively convert LinkMarkerShape to a
string enum and keep z.enum), ensuring the schema uses the same numeric values
as the LinkMarkerShape enum.

- Upgrade vee-validate from v4.15.1 to v5.0.0-beta.0 (Standard Schema)

- Remove custom toTypedSchema adapter (veeValidateZod.ts)

- Pass Zod schemas directly to validationSchema

- Fix SignInForm tests: use text-based selectors, remove try/catch anti-pattern

Amp-Thread-ID: https://ampcode.com/threads/T-019c7ec7-2a88-7165-9db4-ef656336407b
Co-authored-by: Amp <amp@ampcode.com>
@socket-security
Copy link

socket-security bot commented Feb 21, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpicocolors@​1.1.11001007380100
Addedmarkdown-table@​3.0.410010010080100
Addedpretty-bytes@​7.1.010010010082100
Addeduuid@​11.1.010010010083100

View full report

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
src/components/dialog/content/signin/ApiKeyForm.vue (2)

34-43: Inconsistent field binding pattern.

This form uses v-model="apiKey" + v-bind="apiKeyAttrs" while SignInForm and SignUpForm use FormField with componentField. Both work, but using FormField would provide consistency across auth forms.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/ApiKeyForm.vue` around lines 34 - 43,
The ApiKeyForm uses InputText with v-model="apiKey" and v-bind="apiKeyAttrs"
which is inconsistent with SignInForm/SignUpForm; refactor the API key input to
use the shared FormField pattern instead: replace the InputText usage with
FormField configured with componentField set to the InputText component, pass
apiKeyAttrs via the FormField props (name="apiKey", :componentField="InputText",
v-bind for attributes like autocomplete/placeholder/type/class, and :invalid
bound to Boolean(errors.apiKey)) so the field wiring, validation and event
handling match SignInForm/SignUpForm (update references to apiKey, apiKeyAttrs
and InputText accordingly).

126-137: Redundant null check after validation.

Similar to SignInForm, the submittedValues?.apiKey check is redundant when valid is true. Consider using handleSubmit for consistency with other forms in this PR:

♻️ Suggested simplification
-const onSubmit = async (event: Event) => {
-  event.preventDefault()
-  const { valid, values: submittedValues } = await validate()
-  if (!valid) {
-    return
-  }
-
-  if (submittedValues?.apiKey) {
-    await apiKeyStore.storeApiKey(submittedValues.apiKey)
-    emit('success')
-  }
-}
+const { defineField, errors, handleSubmit } = useForm({
+  initialValues: {
+    apiKey: ''
+  },
+  validationSchema: apiKeySchema
+})
+
+const onSubmit = handleSubmit(async (submittedValues) => {
+  await apiKeyStore.storeApiKey(submittedValues.apiKey)
+  emit('success')
+})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/ApiKeyForm.vue` around lines 126 - 137,
The onSubmit handler performs an unnecessary null-check
(submittedValues?.apiKey) after validate() returns valid; update onSubmit to
assume submittedValues is present when valid and either directly use
submittedValues.apiKey (no optional chaining) or replace the manual validate
flow with the shared handleSubmit helper used by other forms for consistency;
ensure you still call apiKeyStore.storeApiKey(submittedValues.apiKey) and
emit('success') when validation passes (refer to onSubmit, validate,
handleSubmit, apiKeyStore.storeApiKey, and emit).
src/platform/cloud/onboarding/components/CloudSignInForm.vue (1)

118-132: !important usage violates coding guidelines.

The style block uses !important extensively. As per coding guidelines: "Never use !important or the ! important prefix for Tailwind classes; instead find and correct interfering existing !important classes."

Consider using PrimeVue's pt (passthrough) prop system to style these inputs without !important, or create a dedicated input variant that doesn't require style overrides.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/platform/cloud/onboarding/components/CloudSignInForm.vue` around lines
118 - 132, The scoped style block in CloudSignInForm.vue uses multiple
!important overrides on selectors (.p-inputtext, .p-password input,
.p-checkbox-checked .p-checkbox-box); remove the !important rules and replace
them by using PrimeVue passthrough (pt) props on the input components or by
creating a dedicated input/checkbox variant CSS class applied to the component,
e.g., update the components where .p-inputtext / .p-password / .p-checkbox-box
are used to supply pt attributes or a custom class and move the styling into
that variant (without !important) so it doesn't conflict with existing PrimeVue
styles.
src/components/dialog/content/signin/SignInForm.vue (1)

119-128: Redundant field checks after validation.

Since signInSchema validates that email and password are required strings, the submittedValues?.email && submittedValues.password check is redundant when valid is true. Consider using handleSubmit (like in CloudSignInForm.vue) which guarantees typed values when the callback fires:

♻️ Suggested simplification
-const onSubmit = useThrottleFn(async (event: Event) => {
-  event.preventDefault()
-  const { valid, values: submittedValues } = await validate()
-  if (valid && submittedValues?.email && submittedValues.password) {
-    emit('submit', {
-      email: submittedValues.email,
-      password: submittedValues.password
-    })
-  }
-}, 1_500)
+const onSubmit = useThrottleFn(
+  handleSubmit((submittedValues) => {
+    emit('submit', submittedValues)
+  }),
+  1_500
+)

This also removes the need to destructure validate from useForm and manually call event.preventDefault(), since handleSubmit handles that automatically.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/SignInForm.vue` around lines 119 - 128,
The onSubmit function is redundantly checking submittedValues?.email &&
submittedValues.password after validate; switch to using handleSubmit from
useForm (as in CloudSignInForm.vue) so the callback receives typed, guaranteed
values and you can remove the manual validate call, preventDefault, and the
extra null checks; wrap handleSubmit's callback with useThrottleFn (keep the
existing onSubmit name), remove destructuring of validate, and rely on
signInSchema to ensure email/password exist before emitting the 'submit' event.
src/components/dialog/content/signin/SignInForm.test.ts (1)

87-100: Avoid non‑null assertion to keep failures readable.

If the button isn’t found, the ! can throw a cryptic error. Consider an explicit guard so test failures are clearer.

♻️ Suggested tweak
 function findForgotPasswordButton(wrapper: VueWrapper<ComponentInstance>) {
-  return wrapper
-    .findAll('button[type="button"]')
-    .find((btn) =>
-      btn.text().includes(enMessages.auth.login.forgotPassword)
-    )!
+  const button = wrapper
+    .findAll('button[type="button"]')
+    .find((btn) =>
+      btn.text().includes(enMessages.auth.login.forgotPassword)
+    )
+  if (!button) {
+    throw new Error('Forgot Password button not found')
+  }
+  return button
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/SignInForm.test.ts` around lines 87 -
100, The test currently uses a non-null assertion in findForgotPasswordButton
which can produce cryptic crashes; update findForgotPasswordButton (and the
'shows disabled style when email is empty' test) to avoid '!' by returning an
optional (or null) and add an explicit guard/assertion before accessing methods:
locate the helper function findForgotPasswordButton and change it to return
undefined/null when not found, then in the test assert the button exists with a
clear message (e.g., expect(forgotBtn).toBeTruthy()/toBeDefined() with a message
or throw an AssertionError) before calling forgotBtn.classes() so failures are
readable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/dialog/content/signin/SignInForm.test.ts`:
- Around line 151-185: Remove the ProgressSpinner stub and stop relying on the
module-level mockLoading flag; instead set loading per-test via a hoisted mock
or a per-test factory and assert with findComponent(ProgressSpinner). Update the
first test to not register a ProgressSpinner stub in the mountComponent call,
set the mocked loading state locally (use vi.hoisted() or create a mountFactory
that accepts { loading }), mount via mountComponent/mountFactory, await
nextTick(), then assert wrapper.findComponent(ProgressSpinner).exists() is true
and the submit button is absent; mirror the same per-test local setup for the
non-loading case where mockLoading is false.

---

Nitpick comments:
In `@src/components/dialog/content/signin/ApiKeyForm.vue`:
- Around line 34-43: The ApiKeyForm uses InputText with v-model="apiKey" and
v-bind="apiKeyAttrs" which is inconsistent with SignInForm/SignUpForm; refactor
the API key input to use the shared FormField pattern instead: replace the
InputText usage with FormField configured with componentField set to the
InputText component, pass apiKeyAttrs via the FormField props (name="apiKey",
:componentField="InputText", v-bind for attributes like
autocomplete/placeholder/type/class, and :invalid bound to
Boolean(errors.apiKey)) so the field wiring, validation and event handling match
SignInForm/SignUpForm (update references to apiKey, apiKeyAttrs and InputText
accordingly).
- Around line 126-137: The onSubmit handler performs an unnecessary null-check
(submittedValues?.apiKey) after validate() returns valid; update onSubmit to
assume submittedValues is present when valid and either directly use
submittedValues.apiKey (no optional chaining) or replace the manual validate
flow with the shared handleSubmit helper used by other forms for consistency;
ensure you still call apiKeyStore.storeApiKey(submittedValues.apiKey) and
emit('success') when validation passes (refer to onSubmit, validate,
handleSubmit, apiKeyStore.storeApiKey, and emit).

In `@src/components/dialog/content/signin/SignInForm.test.ts`:
- Around line 87-100: The test currently uses a non-null assertion in
findForgotPasswordButton which can produce cryptic crashes; update
findForgotPasswordButton (and the 'shows disabled style when email is empty'
test) to avoid '!' by returning an optional (or null) and add an explicit
guard/assertion before accessing methods: locate the helper function
findForgotPasswordButton and change it to return undefined/null when not found,
then in the test assert the button exists with a clear message (e.g.,
expect(forgotBtn).toBeTruthy()/toBeDefined() with a message or throw an
AssertionError) before calling forgotBtn.classes() so failures are readable.

In `@src/components/dialog/content/signin/SignInForm.vue`:
- Around line 119-128: The onSubmit function is redundantly checking
submittedValues?.email && submittedValues.password after validate; switch to
using handleSubmit from useForm (as in CloudSignInForm.vue) so the callback
receives typed, guaranteed values and you can remove the manual validate call,
preventDefault, and the extra null checks; wrap handleSubmit's callback with
useThrottleFn (keep the existing onSubmit name), remove destructuring of
validate, and rely on signInSchema to ensure email/password exist before
emitting the 'submit' event.

In `@src/platform/cloud/onboarding/components/CloudSignInForm.vue`:
- Around line 118-132: The scoped style block in CloudSignInForm.vue uses
multiple !important overrides on selectors (.p-inputtext, .p-password input,
.p-checkbox-checked .p-checkbox-box); remove the !important rules and replace
them by using PrimeVue passthrough (pt) props on the input components or by
creating a dedicated input/checkbox variant CSS class applied to the component,
e.g., update the components where .p-inputtext / .p-password / .p-checkbox-box
are used to supply pt attributes or a custom class and move the styling into
that variant (without !important) so it doesn't conflict with existing PrimeVue
styles.

Comment on lines 151 to 185
describe('Loading State', () => {
it('shows spinner when loading', async () => {
mockLoading = true
const wrapper = mountComponent(
{},
{
global: {
plugins: [
PrimeVue,
createI18n({
legacy: false,
locale: 'en',
messages: { en: enMessages }
}),
ToastService
],
stubs: {
ProgressSpinner: { template: '<div data-testid="spinner" />' }
}
}
}
)
await nextTick()

try {
const wrapper = mountComponent()
await nextTick()

expect(wrapper.findComponent(ProgressSpinner).exists()).toBe(true)
expect(wrapper.findComponent(Button).exists()).toBe(false)
} catch (error) {
// Fallback test - check HTML content if component rendering fails
mockLoading = true
const wrapper = mountComponent()
expect(wrapper.html()).toContain('p-progressspinner')
expect(wrapper.html()).not.toContain('<button')
}
expect(wrapper.find('[data-testid="spinner"]').exists()).toBe(true)
expect(wrapper.find('button[type="submit"]').exists()).toBe(false)
})

it('shows button when not loading', () => {
it('shows submit button when not loading', () => {
mockLoading = false

const wrapper = mountComponent()

expect(wrapper.findComponent(ProgressSpinner).exists()).toBe(false)
expect(wrapper.findComponent(Button).exists()).toBe(true)
expect(wrapper.find('button[type="submit"]').exists()).toBe(true)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd -t f "SignInForm.test.ts" --exec cat -n {} \;

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 9460


Avoid stubbing ProgressSpinner and use global mutable state in this test.

Stubbing ProgressSpinner (a PrimeVue primitive) and asserting via data-testid reduces test fidelity. The codebase already demonstrates the correct pattern at line 183—use findComponent(ProgressSpinner) instead. Additionally, toggling module-level mockLoading risks cross-test pollution. Isolate loading state per test using vi.hoisted() or a per-test factory to ensure test independence.

Example refactor
- let mockLoading = false
- vi.mock('@/stores/firebaseAuthStore', () => ({
-   useFirebaseAuthStore: vi.fn(() => ({
-     get loading() {
-       return mockLoading
-     }
-   }))
- }))

+ const { mockLoading } = vi.hoisted(() => ({
+   mockLoading: { value: false }
+ }))
+ 
+ vi.mock('@/stores/firebaseAuthStore', () => ({
+   useFirebaseAuthStore: vi.fn(() => ({
+     get loading() {
+       return mockLoading.value
+     }
+   }))
+ }))

For the spinner test:

- const wrapper = mountComponent(
-   {},
-   {
-     global: {
-       plugins: [...],
-       stubs: {
-         ProgressSpinner: { template: '<div data-testid="spinner" />' }
-       }
-     }
-   }
- )
- await nextTick()
- 
- expect(wrapper.find('[data-testid="spinner"]').exists()).toBe(true)
+ mockLoading.value = true
+ const wrapper = mountComponent()
+ await nextTick()
+ 
+ expect(wrapper.findComponent(ProgressSpinner).exists()).toBe(true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/dialog/content/signin/SignInForm.test.ts` around lines 151 -
185, Remove the ProgressSpinner stub and stop relying on the module-level
mockLoading flag; instead set loading per-test via a hoisted mock or a per-test
factory and assert with findComponent(ProgressSpinner). Update the first test to
not register a ProgressSpinner stub in the mountComponent call, set the mocked
loading state locally (use vi.hoisted() or create a mountFactory that accepts {
loading }), mount via mountComponent/mountFactory, await nextTick(), then assert
wrapper.findComponent(ProgressSpinner).exists() is true and the submit button is
absent; mirror the same per-test local setup for the non-loading case where
mockLoading is false.

- Remove per-test ProgressSpinner stub; stub globally in mountComponent

- Use findComponent(ProgressSpinner) consistently in loading tests

- Replace non-null assertion with throwing guard in findForgotPasswordButton

Amp-Thread-ID: https://ampcode.com/threads/T-019c7f35-4320-75dc-be40-98526259f2ae
Co-authored-by: Amp <amp@ampcode.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/components/dialog/content/signin/SignInForm.test.ts`:
- Around line 154-169: Tests currently rely on a module-level mutable variable
mockLoading which can leak state between tests; change the setup so each test
gets its own isolated loading value by replacing the module-level mockLoading
with a per-test factory or a vi.hoisted() ref and update mountComponent to
accept a loading argument (or read the hoisted ref) so tests set loading
explicitly before mount; update tests referencing mockLoading to call
mountComponent({ loading: true/false }) or set the hoisted ref within each test,
and keep references to ProgressSpinner and mountComponent to locate where the
change is needed.
- Around line 67-81: The test currently stubs the ProgressSpinner (stubs: {
ProgressSpinner: true }) which lowers fidelity; update the mount setup in
mountComponent for SignInForm to register the real ProgressSpinner instead of
stubbing it by removing the stub entry and adding ProgressSpinner to the mount
global components (or import ProgressSpinner from PrimeVue and include it in
global.components) while keeping existing plugins (PrimeVue, i18n, ToastService)
so the real primitive component is available during tests.

Copy link
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we split the PR or stack it?

@DrJKL
Copy link
Contributor Author

DrJKL commented Feb 23, 2026

Could we split the PR or stack it?

That was the plan, just wanted to make sure it all worked first.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

Note

Docstrings generation - SUCCESS
Generated docstrings and committed to branch drjkl/vee-validate (commit: b42c7878b8a3f962c77ede3cc6bb746e0a3847ef)

Docstrings generation was requested by @DrJKL.

The following files were modified:

* `src/components/ui/form/useFormField.ts`
* `src/platform/assets/utils/createAssetWidget.ts`
* `src/platform/workflow/validation/schemas/workflowSchema.ts`
* `src/scripts/changeTracker.ts`
* `src/stores/subgraphStore.ts`

These files were kept as they were:
* `src/platform/assets/utils/createModelNodeFromAsset.ts`

These files were ignored:
* `src/components/dialog/content/signin/ApiKeyForm.test.ts`
* `src/components/dialog/content/signin/SignInForm.test.ts`
* `src/platform/workflow/validation/schemas/workflowSchema.test.ts`

These file types are not supported:
* `package.json`
* `pnpm-workspace.yaml`
* `src/components/dialog/content/UpdatePasswordContent.vue`
* `src/components/dialog/content/signin/ApiKeyForm.vue`
* `src/components/dialog/content/signin/PasswordFields.vue`
* `src/components/dialog/content/signin/SignInForm.vue`
* `src/components/dialog/content/signin/SignUpForm.vue`
* `src/components/ui/form/FormControl.vue`
* `src/components/ui/form/FormDescription.vue`
* `src/components/ui/form/FormField.vue`
* `src/components/ui/form/FormItem.vue`
* `src/components/ui/form/FormLabel.vue`
* `src/components/ui/form/FormMessage.vue`
* `src/platform/cloud/onboarding/components/CloudSignInForm.vue`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants