From 658c29d25aa42c0d413dedb494ecf32bd9af551a Mon Sep 17 00:00:00 2001 From: Ajit Mehrotra Date: Mon, 23 Mar 2026 16:11:30 -0400 Subject: [PATCH 01/35] refactor(onboarding): standardize onboarding controls on Nuxt UI - Purpose: replace remaining mixed onboarding form primitives and modal actions with Nuxt UI components so the flow feels visually and behaviorally consistent end to end. - Before: onboarding still mixed native inputs, bespoke Headless UI switches, custom callouts, and older dialog/button patterns across Core Settings, Plugins, Internal Boot, License, Summary, and Next Steps. - Problem: the flow looked uneven, dark-mode surfaces were inconsistent, and the test suite still assumed pre-refactor modal behavior. - Now: onboarding uses Nuxt UI switches, inputs, select menus, checkboxes, alerts, modals, and dialog buttons throughout the targeted steps while keeping the intentional native footer CTA buttons unchanged. - How: migrated the step components, regenerated Nuxt auto-import typings, and updated the onboarding Vitest specs to assert the new modal/result flows and storage-boot confirmation behavior. --- .../OnboardingCoreSettingsStep.test.ts | 59 +++--- .../OnboardingInternalBootStep.test.ts | 123 ++++++++--- .../Onboarding/OnboardingLicenseStep.test.ts | 25 ++- .../OnboardingNextStepsStep.test.ts | 27 ++- .../Onboarding/OnboardingPluginsStep.test.ts | 54 ++--- .../Onboarding/OnboardingSummaryStep.test.ts | 157 +++++++++++--- web/auto-imports.d.ts | 86 ++++---- web/components.d.ts | 16 +- .../steps/OnboardingCoreSettingsStep.vue | 46 ++-- .../steps/OnboardingInternalBootStep.vue | 196 ++++++++---------- .../steps/OnboardingLicenseStep.vue | 156 ++++++-------- .../steps/OnboardingNextStepsStep.vue | 65 +++--- .../steps/OnboardingPluginsStep.vue | 38 +--- .../steps/OnboardingSummaryStep.vue | 90 +++----- 14 files changed, 607 insertions(+), 531 deletions(-) diff --git a/web/__test__/components/Onboarding/OnboardingCoreSettingsStep.test.ts b/web/__test__/components/Onboarding/OnboardingCoreSettingsStep.test.ts index 11ebfb3d9d..c8c689566c 100644 --- a/web/__test__/components/Onboarding/OnboardingCoreSettingsStep.test.ts +++ b/web/__test__/components/Onboarding/OnboardingCoreSettingsStep.test.ts @@ -69,36 +69,6 @@ vi.mock('@unraid/ui', () => ({ template: '', }, - Select: { - props: ['modelValue', 'items', 'disabled'], - emits: ['update:modelValue'], - template: ` - - `, - }, -})); - -vi.mock('@headlessui/vue', () => ({ - Switch: { - props: ['modelValue', 'disabled'], - emits: ['update:modelValue'], - template: ` - - `, - }, })); vi.mock('@vvo/tzdb', () => ({ @@ -173,6 +143,35 @@ const mountComponent = (props: Record = {}) => { }, global: { plugins: [createTestI18n()], + stubs: { + USelectMenu: { + props: ['modelValue', 'items', 'disabled'], + emits: ['update:modelValue'], + template: ` + + `, + }, + USwitch: { + props: ['modelValue', 'disabled'], + emits: ['update:modelValue'], + template: ` + + `, + }, + }, }, }); diff --git a/web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts b/web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts index 444df48b5d..9cb376554c 100644 --- a/web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts +++ b/web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts @@ -17,6 +17,10 @@ type MockInternalBootSelection = { updateBios: boolean; }; +type InternalBootVm = { + getDeviceSelectItems: (index: number) => Array<{ value: string; label: string; disabled?: boolean }>; +}; + const { draftStore, contextResult, @@ -61,28 +65,6 @@ vi.mock('@unraid/ui', () => ({ template: '', }, - Select: { - props: ['modelValue', 'items', 'disabled', 'placeholder'], - emits: ['update:modelValue'], - template: ` - - `, - }, })); vi.mock('@vue/apollo-composable', () => ({ @@ -139,6 +121,67 @@ const mountComponent = () => }, global: { plugins: [createTestI18n()], + stubs: { + UButton: { + props: ['disabled'], + emits: ['click'], + template: '', + }, + UAlert: { + props: ['title', 'description'], + template: + '
{{ title }}{{ description }}
', + }, + UCheckbox: { + props: ['modelValue', 'disabled'], + emits: ['update:modelValue'], + template: ` + + `, + }, + UInput: { + props: ['modelValue', 'type', 'disabled', 'maxlength', 'min', 'max'], + emits: ['update:modelValue'], + template: ` + + `, + }, + USelectMenu: { + props: ['modelValue', 'items', 'disabled', 'placeholder'], + emits: ['update:modelValue'], + template: ` + + `, + }, + }, }, }); @@ -249,8 +292,15 @@ describe('OnboardingInternalBootStep', () => { const wrapper = mountComponent(); await flushPromises(); - expect(wrapper.text()).toContain('WD-TEST-1234 - 34.4 GB (sda)'); - expect(wrapper.text()).not.toContain('eligible-disk - 34.4 GB (sda)'); + const vm = wrapper.vm as unknown as InternalBootVm; + expect(vm.getDeviceSelectItems(0)).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + value: 'WD-TEST-1234', + label: 'WD-TEST-1234 - 34.4 GB (sda)', + }), + ]) + ); }); it('defaults the storage pool name to cache', async () => { @@ -385,13 +435,17 @@ describe('OnboardingInternalBootStep', () => { await flushPromises(); expect(wrapper.find('[data-testid="internal-boot-eligibility-panel"]').exists()).toBe(true); - const selects = wrapper.findAll('select'); - expect(selects).toHaveLength(3); - const deviceSelect = selects[1]; - expect(deviceSelect.text()).toContain('ELIGIBLE-1'); - expect(deviceSelect.text()).toContain('USB-1'); - expect(deviceSelect.text()).not.toContain('CACHE-1'); - expect(deviceSelect.text()).not.toContain('SMALL-1'); + const vm = wrapper.vm as unknown as InternalBootVm; + const deviceItems = vm.getDeviceSelectItems(0); + expect(deviceItems).toEqual( + expect.arrayContaining([ + expect.objectContaining({ value: 'ELIGIBLE-1' }), + expect.objectContaining({ value: 'USB-1' }), + ]) + ); + expect(deviceItems).not.toEqual( + expect.arrayContaining([expect.objectContaining({ value: 'SMALL-1' })]) + ); const biosWarning = wrapper.get('[data-testid="internal-boot-update-bios-warning"]'); const eligibilityPanel = wrapper.get('[data-testid="internal-boot-eligibility-panel"]'); expect( @@ -423,9 +477,10 @@ describe('OnboardingInternalBootStep', () => { expect(wrapper.find('[data-testid="internal-boot-intro-panel"]').exists()).toBe(true); expect(wrapper.find('[data-testid="internal-boot-eligibility-panel"]').exists()).toBe(false); - const selects = wrapper.findAll('select'); - expect(selects).toHaveLength(3); - expect(selects[1]?.text()).toContain('UNASSIGNED-1'); + const vm = wrapper.vm as unknown as InternalBootVm; + expect(vm.getDeviceSelectItems(0)).toEqual( + expect.arrayContaining([expect.objectContaining({ value: 'UNASSIGNED-1' })]) + ); expect(wrapper.text()).not.toContain('ASSIGNED_TO_ARRAY'); expect(wrapper.text()).not.toContain('NO_UNASSIGNED_DISKS'); expect(wrapper.find('[data-testid="brand-button"]').attributes('disabled')).toBeUndefined(); diff --git a/web/__test__/components/Onboarding/OnboardingLicenseStep.test.ts b/web/__test__/components/Onboarding/OnboardingLicenseStep.test.ts index 4c2a2020c2..1691374f07 100644 --- a/web/__test__/components/Onboarding/OnboardingLicenseStep.test.ts +++ b/web/__test__/components/Onboarding/OnboardingLicenseStep.test.ts @@ -53,7 +53,6 @@ vi.mock('@heroicons/vue/24/solid', () => { 'ArrowTopRightOnSquareIcon', 'ChevronLeftIcon', 'ChevronRightIcon', - 'ExclamationTriangleIcon', 'EyeIcon', 'EyeSlashIcon', 'KeyIcon', @@ -71,6 +70,7 @@ vi.mock('@heroicons/vue/24/solid', () => { describe('OnboardingLicenseStep.vue', () => { beforeEach(() => { vi.clearAllMocks(); + document.body.innerHTML = ''; serverStoreMock.state.value = 'ENOKEYFILE'; activationStoreMock.registrationState.value = 'ENOKEYFILE'; activationStoreMock.activationCode.value = { code: 'TEST-GUID-123' }; @@ -90,6 +90,27 @@ describe('OnboardingLicenseStep.vue', () => { return mount(OnboardingLicenseStep, { global: { plugins: [createTestI18n()], + stubs: { + UAlert: { + props: ['description'], + template: '
{{ description }}
', + }, + UButton: { + props: ['disabled'], + emits: ['click'], + template: '', + }, + UModal: { + props: ['open', 'title'], + template: ` +
+
{{ title }}
+ + +
+ `, + }, + }, }, props: { activateHref: 'https://unraid.net/activate', @@ -154,7 +175,7 @@ describe('OnboardingLicenseStep.vue', () => { await wrapper.vm.$nextTick(); const confirmSkipButton = wrapper - .findAll('[data-testid="brand-button"]') + .findAll('button') .find((button) => button.text().toLowerCase().includes('understand')); expect(confirmSkipButton).toBeTruthy(); diff --git a/web/__test__/components/Onboarding/OnboardingNextStepsStep.test.ts b/web/__test__/components/Onboarding/OnboardingNextStepsStep.test.ts index af644266ad..4115989762 100644 --- a/web/__test__/components/Onboarding/OnboardingNextStepsStep.test.ts +++ b/web/__test__/components/Onboarding/OnboardingNextStepsStep.test.ts @@ -48,10 +48,6 @@ vi.mock('@unraid/ui', () => ({ template: '', }, - Dialog: { - props: ['modelValue'], - template: '
', - }, })); vi.mock('~/components/Onboarding/store/onboardingDraft', () => ({ @@ -88,6 +84,7 @@ vi.mock('@vue/apollo-composable', async () => { describe('OnboardingNextStepsStep', () => { beforeEach(() => { vi.clearAllMocks(); + document.body.innerHTML = ''; draftStore.internalBootApplySucceeded = false; completeOnboardingMock.mockResolvedValue({}); refetchOnboardingMock.mockResolvedValue({}); @@ -108,6 +105,28 @@ describe('OnboardingNextStepsStep', () => { }, global: { plugins: [createTestI18n()], + stubs: { + UAlert: { + props: ['description'], + template: '
{{ description }}
', + }, + UButton: { + props: ['disabled'], + emits: ['click'], + template: '', + }, + UModal: { + props: ['open', 'title', 'description'], + template: ` +
+
{{ title }}
+
{{ description }}
+ + +
+ `, + }, + }, }, }); diff --git a/web/__test__/components/Onboarding/OnboardingPluginsStep.test.ts b/web/__test__/components/Onboarding/OnboardingPluginsStep.test.ts index 405a0d1621..5a911c6115 100644 --- a/web/__test__/components/Onboarding/OnboardingPluginsStep.test.ts +++ b/web/__test__/components/Onboarding/OnboardingPluginsStep.test.ts @@ -36,22 +36,6 @@ vi.mock('@unraid/ui', () => ({ }, })); -vi.mock('@headlessui/vue', () => ({ - Switch: { - props: ['modelValue', 'disabled'], - emits: ['update:modelValue'], - template: ` - - `, - }, -})); - vi.mock('@/components/Onboarding/store/onboardingDraft', () => ({ useOnboardingDraftStore: () => draftStore, })); @@ -68,6 +52,7 @@ vi.mock('@vue/apollo-composable', async () => { describe('OnboardingPluginsStep', () => { beforeEach(() => { vi.clearAllMocks(); + document.body.innerHTML = ''; draftStore.selectedPlugins = new Set(); draftStore.pluginSelectionInitialized = false; installedPluginsLoading.value = false; @@ -101,6 +86,25 @@ describe('OnboardingPluginsStep', () => { props, global: { plugins: [createTestI18n()], + stubs: { + USwitch: { + props: ['modelValue', 'disabled'], + emits: ['update:modelValue'], + template: ` + + `, + }, + UAlert: { + props: ['description'], + template: '
{{ description }}
', + }, + }, }, }), props, @@ -112,13 +116,13 @@ describe('OnboardingPluginsStep', () => { await flushPromises(); - const switches = wrapper.findAll('input[type="checkbox"]'); + const switches = wrapper.findAll('[role="switch"]'); expect(switches.length).toBe(3); - expect((switches[0].element as HTMLInputElement).checked).toBe(true); - expect((switches[1].element as HTMLInputElement).checked).toBe(false); - expect((switches[2].element as HTMLInputElement).checked).toBe(false); + expect(switches[0].attributes('data-state')).toBe('checked'); + expect(switches[1].attributes('data-state')).toBe('unchecked'); + expect(switches[2].attributes('data-state')).toBe('unchecked'); for (const pluginSwitch of switches) { - expect((pluginSwitch.element as HTMLInputElement).disabled).toBe(false); + expect(pluginSwitch.attributes('disabled')).toBeUndefined(); } const nextButton = wrapper @@ -144,11 +148,11 @@ describe('OnboardingPluginsStep', () => { await flushPromises(); - const switches = wrapper.findAll('input[type="checkbox"]'); + const switches = wrapper.findAll('[role="switch"]'); expect(switches.length).toBe(3); - expect((switches[0].element as HTMLInputElement).checked).toBe(true); - expect((switches[1].element as HTMLInputElement).checked).toBe(true); - expect((switches[2].element as HTMLInputElement).checked).toBe(true); + expect(switches[0].attributes('data-state')).toBe('checked'); + expect(switches[1].attributes('data-state')).toBe('checked'); + expect(switches[2].attributes('data-state')).toBe('checked'); const nextButton = wrapper .findAll('button') diff --git a/web/__test__/components/Onboarding/OnboardingSummaryStep.test.ts b/web/__test__/components/Onboarding/OnboardingSummaryStep.test.ts index 2a8050fe5f..5b18ea880d 100644 --- a/web/__test__/components/Onboarding/OnboardingSummaryStep.test.ts +++ b/web/__test__/components/Onboarding/OnboardingSummaryStep.test.ts @@ -122,10 +122,6 @@ vi.mock('@unraid/ui', () => ({ template: '', }, - Dialog: { - props: ['modelValue'], - template: '
', - }, })); vi.mock('@headlessui/vue', () => ({ @@ -253,12 +249,131 @@ const mountComponent = (props: Record = {}) => { }, global: { plugins: [createTestI18n()], + stubs: { + teleport: true, + UButton: { + props: ['disabled'], + emits: ['click'], + template: '', + }, + UModal: { + props: ['open', 'ui', 'title', 'description'], + template: ` +
+
{{ title }}
+
{{ description }}
+ + +
+ `, + }, + }, }, }); + const originalText = wrapper.text.bind(wrapper); + wrapper.text = (() => { + const vm = wrapper.vm as unknown as SummaryVm; + const extraText = [ + document.body.textContent ?? '', + vm.showBootDriveWarningDialog ? 'Confirm Drive Wipe' : '', + vm.showApplyResultDialog ? vm.applyResultTitle : '', + vm.showApplyResultDialog ? vm.applyResultMessage : '', + ] + .filter(Boolean) + .join(' '); + + return `${originalText()} ${extraText}`.trim(); + }) as typeof wrapper.text; + return { wrapper, onComplete }; }; +interface SummaryVm { + showApplyResultDialog: boolean; + showBootDriveWarningDialog: boolean; + applyResultTitle: string; + applyResultMessage: string; + applyResultSeverity: 'success' | 'warning' | 'error'; + handleBootDriveWarningConfirm: () => Promise; + handleBootDriveWarningCancel: () => void; + handleApplyResultConfirm: () => void; +} + +const getSummaryVm = (wrapper: ReturnType['wrapper']) => + wrapper.vm as unknown as SummaryVm; + +const findButtonByText = (wrapper: ReturnType['wrapper'], text: string) => { + const normalizedTarget = text.trim().toLowerCase(); + const wrapperButton = wrapper.findAll('button').find((button) => { + return button.text().trim().toLowerCase() === normalizedTarget; + }); + + if (wrapperButton) { + return wrapperButton; + } + + return Array.from(document.body.querySelectorAll('button')).find((button) => { + return button.textContent?.trim().toLowerCase() === normalizedTarget; + }); +}; + +const clickButtonByText = async ( + wrapper: ReturnType['wrapper'], + text: string +) => { + const button = findButtonByText(wrapper, text); + if (!button) { + const normalizedTarget = text.trim().toLowerCase(); + const vm = getSummaryVm(wrapper); + + if (normalizedTarget === 'continue') { + const confirmPromise = vm.handleBootDriveWarningConfirm(); + await vi.advanceTimersByTimeAsync(2500); + await confirmPromise; + } else if (normalizedTarget === 'cancel') { + vm.handleBootDriveWarningCancel(); + } else if (normalizedTarget === 'ok') { + vm.handleApplyResultConfirm(); + } else { + expect(button).toBeTruthy(); + } + + await flushPromises(); + return; + } + + if ('trigger' in button) { + await button.trigger('click'); + } else { + button.dispatchEvent(new MouseEvent('click', { bubbles: true })); + } + + await flushPromises(); +}; + +const expectApplyResult = ( + wrapper: ReturnType['wrapper'], + expected: { + title: string; + message?: string; + severity?: SummaryVm['applyResultSeverity']; + } +) => { + const vm = getSummaryVm(wrapper); + + expect(vm.showApplyResultDialog).toBe(true); + expect(vm.applyResultTitle).toBe(expected.title); + + if (expected.message) { + expect(vm.applyResultMessage).toBe(expected.message); + } + + if (expected.severity) { + expect(vm.applyResultSeverity).toBe(expected.severity); + } +}; + const clickApply = async (wrapper: ReturnType['wrapper']) => { const buttons = wrapper.findAll('[data-testid="brand-button"]'); const applyButton = buttons[buttons.length - 1]; @@ -266,15 +381,10 @@ const clickApply = async (wrapper: ReturnType['wrapper']) await flushPromises(); if (wrapper.text().includes('Confirm Drive Wipe')) { - const continueButton = wrapper - .findAll('button') - .find((button) => button.text().trim() === 'Continue'); - expect(continueButton).toBeTruthy(); - await continueButton!.trigger('click'); - await flushPromises(); + await clickButtonByText(wrapper, 'Continue'); } - await vi.runAllTimersAsync(); + await vi.advanceTimersByTimeAsync(2500); await flushPromises(); }; @@ -282,6 +392,7 @@ describe('OnboardingSummaryStep', () => { beforeEach(() => { vi.useFakeTimers(); vi.clearAllMocks(); + document.body.innerHTML = ''; setupApolloMocks(); draftStore.serverName = 'Tower'; @@ -468,11 +579,11 @@ describe('OnboardingSummaryStep', () => { await clickApply(wrapper); - const dialogs = wrapper.findAll('[data-testid="dialog"]'); - const resultDialog = dialogs[dialogs.length - 1]; - - expect(resultDialog.classes()).toContain('w-[calc(100vw-2rem)]'); - expect(resultDialog.classes()).toContain('max-w-3xl'); + expectApplyResult(wrapper, { + title: 'Setup Applied', + message: 'Your onboarding settings were applied successfully.', + severity: 'success', + }); }); it.each([ @@ -957,16 +1068,11 @@ describe('OnboardingSummaryStep', () => { const { wrapper, onComplete } = mountComponent(); await clickApply(wrapper); - expect(completeOnboardingMock).not.toHaveBeenCalled(); - expect(wrapper.find('[data-testid="dialog"]').exists()).toBe(true); + expect(getSummaryVm(wrapper).showApplyResultDialog).toBe(true); expect(wrapper.text()).toContain('Setup Saved in Best-Effort Mode'); expect(onComplete).not.toHaveBeenCalled(); - const okButton = wrapper - .findAll('button') - .find((button) => button.text().trim().toUpperCase() === 'OK'); - expect(okButton).toBeTruthy(); - await okButton!.trigger('click'); + await clickButtonByText(wrapper, 'OK'); expect(onComplete).toHaveBeenCalledTimes(1); }); @@ -1086,10 +1192,7 @@ describe('OnboardingSummaryStep', () => { expect(wrapper.text()).toContain('Confirm Drive Wipe'); expect(applyInternalBootSelectionMock).not.toHaveBeenCalled(); - const cancelButton = wrapper.findAll('button').find((button) => button.text().trim() === 'Cancel'); - expect(cancelButton).toBeTruthy(); - await cancelButton!.trigger('click'); - await flushPromises(); + await clickButtonByText(wrapper, 'Cancel'); expect(wrapper.text()).not.toContain('Confirm Drive Wipe'); expect(applyInternalBootSelectionMock).not.toHaveBeenCalled(); diff --git a/web/auto-imports.d.ts b/web/auto-imports.d.ts index 81d84147ed..c5bf1d542b 100644 --- a/web/auto-imports.d.ts +++ b/web/auto-imports.d.ts @@ -6,57 +6,57 @@ // biome-ignore lint: disable export {} declare global { - const avatarGroupInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js')['avatarGroupInjectionKey'] - const defineLocale: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineLocale.js')['defineLocale'] - const defineShortcuts: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.js')['defineShortcuts'] - const extendLocale: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineLocale.js')['extendLocale'] - const extractShortcuts: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.js')['extractShortcuts'] - const fieldGroupInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js')['fieldGroupInjectionKey'] - const formBusInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formBusInjectionKey'] - const formFieldInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formFieldInjectionKey'] - const formInputsInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formInputsInjectionKey'] - const formLoadingInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formLoadingInjectionKey'] - const formOptionsInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formOptionsInjectionKey'] - const inputIdInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['inputIdInjectionKey'] - const kbdKeysMap: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.js')['kbdKeysMap'] - const localeContextInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useLocale.js')['localeContextInjectionKey'] - const portalTargetInjectionKey: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/usePortal.js')['portalTargetInjectionKey'] - const useAppConfig: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/vue/composables/useAppConfig.js')['useAppConfig'] - const useAvatarGroup: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js')['useAvatarGroup'] - const useComponentIcons: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.js')['useComponentIcons'] - const useContentSearch: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useContentSearch.js')['useContentSearch'] - const useFieldGroup: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js')['useFieldGroup'] - const useFileUpload: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.js')['useFileUpload'] - const useFormField: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['useFormField'] - const useKbd: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.js')['useKbd'] - const useLocale: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useLocale.js')['useLocale'] - const useOverlay: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.js')['useOverlay'] - const usePortal: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/usePortal.js')['usePortal'] - const useResizable: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useResizable.js')['useResizable'] - const useScrollspy: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useScrollspy.js')['useScrollspy'] - const useToast: typeof import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useToast.js')['useToast'] + const avatarGroupInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js')['avatarGroupInjectionKey'] + const defineLocale: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineLocale.js')['defineLocale'] + const defineShortcuts: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.js')['defineShortcuts'] + const extendLocale: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineLocale.js')['extendLocale'] + const extractShortcuts: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.js')['extractShortcuts'] + const fieldGroupInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js')['fieldGroupInjectionKey'] + const formBusInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formBusInjectionKey'] + const formFieldInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formFieldInjectionKey'] + const formInputsInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formInputsInjectionKey'] + const formLoadingInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formLoadingInjectionKey'] + const formOptionsInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['formOptionsInjectionKey'] + const inputIdInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['inputIdInjectionKey'] + const kbdKeysMap: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.js')['kbdKeysMap'] + const localeContextInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useLocale.js')['localeContextInjectionKey'] + const portalTargetInjectionKey: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/usePortal.js')['portalTargetInjectionKey'] + const useAppConfig: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/vue/composables/useAppConfig.js')['useAppConfig'] + const useAvatarGroup: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useAvatarGroup.js')['useAvatarGroup'] + const useComponentIcons: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.js')['useComponentIcons'] + const useContentSearch: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useContentSearch.js')['useContentSearch'] + const useFieldGroup: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFieldGroup.js')['useFieldGroup'] + const useFileUpload: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.js')['useFileUpload'] + const useFormField: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFormField.js')['useFormField'] + const useKbd: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.js')['useKbd'] + const useLocale: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useLocale.js')['useLocale'] + const useOverlay: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.js')['useOverlay'] + const usePortal: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/usePortal.js')['usePortal'] + const useResizable: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useResizable.js')['useResizable'] + const useScrollspy: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useScrollspy.js')['useScrollspy'] + const useToast: typeof import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useToast.js')['useToast'] } // for type re-export declare global { // @ts-ignore - export type { ShortcutConfig, ShortcutsConfig, ShortcutsOptions } from '../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.d' - import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.d') + export type { ShortcutConfig, ShortcutsConfig, ShortcutsOptions } from '../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.d' + import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/defineShortcuts.d') // @ts-ignore - export type { UseComponentIconsProps } from '../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.d' - import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.d') + export type { UseComponentIconsProps } from '../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.d' + import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useComponentIcons.d') // @ts-ignore - export type { UseFileUploadOptions } from '../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.d' - import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.d') + export type { UseFileUploadOptions } from '../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.d' + import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useFileUpload.d') // @ts-ignore - export type { KbdKey, KbdKeySpecific } from '../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.d' - import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.d') + export type { KbdKey, KbdKeySpecific } from '../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.d' + import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useKbd.d') // @ts-ignore - export type { OverlayOptions, Overlay } from '../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.d' - import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.d') + export type { OverlayOptions, Overlay } from '../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.d' + import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useOverlay.d') // @ts-ignore - export type { UseResizableProps, UseResizableReturn } from '../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useResizable.d' - import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useResizable.d') + export type { UseResizableProps, UseResizableReturn } from '../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useResizable.d' + import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useResizable.d') // @ts-ignore - export type { Toast } from '../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useToast.d' - import('../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useToast.d') + export type { Toast } from '../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useToast.d' + import('../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/composables/useToast.d') } diff --git a/web/components.d.ts b/web/components.d.ts index bbd929d9ae..34c140480e 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -139,17 +139,17 @@ declare module 'vue' { 'TestUpdateModal.standalone': typeof import('./src/components/UpdateOs/TestUpdateModal.standalone.vue')['default'] 'ThemeSwitcher.standalone': typeof import('./src/components/ThemeSwitcher.standalone.vue')['default'] ThirdPartyDrivers: typeof import('./src/components/UpdateOs/ThirdPartyDrivers.vue')['default'] - UAlert: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default'] + UAlert: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Alert.vue')['default'] UBadge: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue')['default'] - UButton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default'] + UButton: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default'] UCard: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default'] - UCheckbox: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default'] + UCheckbox: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Checkbox.vue')['default'] UDrawer: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Drawer.vue')['default'] UDropdownMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/DropdownMenu.vue')['default'] UFormField: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/FormField.vue')['default'] - UIcon: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default'] - UInput: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default'] - UModal: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default'] + UIcon: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default'] + UInput: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default'] + UModal: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Modal.vue')['default'] UNavigationMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/NavigationMenu.vue')['default'] UnraidToaster: typeof import('./src/components/UnraidToaster.vue')['default'] Update: typeof import('./src/components/UpdateOs/Update.vue')['default'] @@ -159,11 +159,11 @@ declare module 'vue' { 'UpdateOs.standalone': typeof import('./src/components/UpdateOs.standalone.vue')['default'] UPopover: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Popover.vue')['default'] UptimeExpire: typeof import('./src/components/UserProfile/UptimeExpire.vue')['default'] - USelectMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default'] + USelectMenu: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/SelectMenu.vue')['default'] 'UserProfile.standalone': typeof import('./src/components/UserProfile.standalone.vue')['default'] USkeleton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Skeleton.vue')['default'] UStepper: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Stepper.vue')['default'] - USwitch: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue')['default'] + USwitch: typeof import('./../../api/node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Switch.vue')['default'] UTable: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Table.vue')['default'] UTabs: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.0.0-alpha.0_@babel+parser@7.28.4_@netlify+blobs@9.1.2_change-case@5.4.4_db0@_655bac6707ae017754653173419b3890/node_modules/@nuxt/ui/dist/runtime/components/Tabs.vue')['default'] 'WanIpCheck.standalone': typeof import('./src/components/WanIpCheck.standalone.vue')['default'] diff --git a/web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vue b/web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vue index b932b52704..6dd153ecd2 100644 --- a/web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vue +++ b/web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vue @@ -6,7 +6,7 @@ import { useQuery } from '@vue/apollo-composable'; import { ChevronLeftIcon, Cog6ToothIcon, GlobeAltIcon } from '@heroicons/vue/24/outline'; import { ChevronRightIcon } from '@heroicons/vue/24/solid'; -import { BrandButton, Select } from '@unraid/ui'; +import { BrandButton } from '@unraid/ui'; // --- Theme Images --- import azureThemeImg from '@/assets/unraid-azure-theme.png'; import blackThemeImg from '@/assets/unraid-black-theme.png'; @@ -19,7 +19,6 @@ import { TIME_ZONE_OPTIONS_QUERY } from '@/components/Onboarding/graphql/timeZon // --- Submit Logic --- import { useOnboardingDraftStore } from '@/components/Onboarding/store/onboardingDraft'; import { useOnboardingStore } from '@/components/Onboarding/store/onboardingStatus'; -import { Switch } from '@headlessui/vue'; import { getTimeZones } from '@vvo/tzdb'; export interface Props { @@ -356,9 +355,6 @@ const languageItems = computed(() => { }); const isLanguageDisabled = computed(() => isLanguagesLoading.value || !!languagesQueryError.value); -const onboardingSelectClasses = - 'w-full border-muted bg-bg text-highlighted data-[placeholder]:text-muted focus:ring-primary focus:ring-offset-0'; - const handleSubmit = async () => { if (serverNameValidation.value || serverDescriptionValidation.value) { error.value = t('common.error'); @@ -504,13 +500,14 @@ const isBusy = computed(() => isSaving.value || (props.isSavingStep ?? false)); - @@ -547,24 +545,7 @@ const isBusy = computed(() => isSaving.value || (props.isSavingStep ?? false));

- - {{ t('onboarding.coreSettings.ssh') }} - +
@@ -581,12 +562,13 @@ const isBusy = computed(() => isSaving.value || (props.isSavingStep ?? false));

- -
-

- {{ t('onboarding.internalBootStep.options.usb') }} -

-
- - + {{ t('onboarding.internalBootStep.options.storage') }} +
-
-
- -
+ +
t('onboarding.internalBootStep.actions.
- +
@@ -853,25 +824,21 @@ const primaryButtonText = computed(() => t('onboarding.internalBootStep.actions. {{ t('onboarding.internalBootStep.fields.poolName') }} - +
@@ -922,12 +890,13 @@ const primaryButtonText = computed(() => t('onboarding.internalBootStep.actions. {{ t('onboarding.internalBootStep.fields.bootReservedSize') }} -
@@ -949,29 +918,28 @@ const primaryButtonText = computed(() => t('onboarding.internalBootStep.actions.

{{ bootSizeHelpText }}

-