From 39b35d46a06ef3bc4077d1def001562bd3ba752d Mon Sep 17 00:00:00 2001 From: Jimbb Date: Fri, 23 Oct 2026 11:40:38 +0800 Subject: [PATCH] feat(frontend): add reactive localization and refine admin layouts (#501) * feat(frontend): add English and Traditional Chinese locales * fix(frontend): capitalize the English login key label * fix(frontend): capitalize standalone English labels * feat(frontend): add reactive localization and refine admin layouts Replace DOM text translation with domain-based Vue I18n resources for English, Simplified Chinese, and Traditional Chinese. Localize shared controls, notifications, charts, and key usage while refining table alignment, menus, and compact labels. Expose semantic usage-window and trend-metric identifiers from the API. Pin the UI and workbench dependencies to published feature commits and preserve the original font defaults. Co-authored-by: Codex --------- Co-authored-by: Jimbb <14995924+jimbb@users.noreply.github.com> Co-authored-by: Zyy Co-authored-by: Codex --- .../src/admin/accounts/presenter.rs | 15 +- .../gateway-api/src/admin/accounts/wire.rs | 12 +- .../src/admin/observability/presenter.rs | 18 +- .../src/admin/observability/wire.rs | 17 +- .../gateway-api/tests/admin/accounts/mod.rs | 6 +- .../tests/admin/observability/response.rs | 66 ++ docs/api.md | 22 +- docs/architecture.md | 3 +- docs/theme.md | 18 +- frontend/eslint.config.ts | 22 + frontend/package.json | 5 +- frontend/pnpm-lock.yaml | 879 +++++++++++++++++- frontend/pnpm-workspace.yaml | 1 + frontend/src/App.vue | 12 +- frontend/src/api/modules/accounts.ts | 2 +- frontend/src/api/modules/dashboard.ts | 7 +- frontend/src/api/modules/key-usage.ts | 13 - frontend/src/api/request/plugins/error.ts | 59 +- frontend/src/api/request/plugins/feedback.ts | 2 +- frontend/src/api/request/plugins/raw.ts | 7 +- frontend/src/api/request/plugins/retry.ts | 3 +- frontend/src/api/request/plugins/session.ts | 9 +- .../components/AccountGroupCheckboxGrid.vue | 8 +- frontend/src/components/AccountGroupMarks.vue | 6 +- frontend/src/components/ApiKeyConfigModal.vue | 22 +- frontend/src/components/AppAboutModal.vue | 112 --- frontend/src/components/AppLanguageMenu.vue | 66 ++ frontend/src/components/LastUsedAtCell.vue | 12 +- frontend/src/components/ProviderFilter.vue | 15 +- frontend/src/components/ProviderIconGroup.vue | 6 +- .../RequestLocationDetectButton.vue | 9 +- .../src/components/RequestLocationFields.vue | 21 +- .../account/account-usage-window/index.vue | 16 +- .../account/account-usage-window/presenter.ts | 52 +- frontend/src/components/charts/tooltip.ts | 2 +- .../client-profile/ClientProfileEditor.vue | 17 +- .../ClientProfilePresetFields.vue | 45 +- .../ClientProfilePreviewPanel.vue | 18 +- .../ProviderRequestProfilesEditor.vue | 10 +- .../client-profile/XaiClientProfileEditor.vue | 35 +- frontend/src/components/schema-form/index.vue | 17 +- .../usage/HealthTimelinePointPopover.vue | 22 +- .../usage/RequestHealthTimelineCard.vue | 10 +- .../src/components/usage/UsageBillingCell.vue | 42 +- .../components/usage/UsageDetailPopover.vue | 2 +- .../src/components/usage/UsageLatencyCell.vue | 12 +- .../src/components/usage/UsageModelCell.vue | 4 +- .../components/usage/UsagePerformanceCell.vue | 6 +- .../usage/UsageReasoningEffortCell.vue | 12 +- .../usage/UsageRecordStatusFilter.vue | 18 +- .../components/usage/UsageRecordsTable.vue | 11 +- .../src/components/usage/UsageRouteCell.vue | 8 +- .../src/components/usage/UsageTokenCell.vue | 18 +- .../src/components/usage/shared/columns.ts | 40 +- .../src/components/usage/shared/health.ts | 32 +- .../src/components/usage/shared/presenter.ts | 45 +- frontend/src/composables/useAsyncAction.ts | 7 +- frontend/src/composables/useCopyText.ts | 7 +- frontend/src/composables/useLocale.ts | 13 + frontend/src/i18n/index.ts | 14 + frontend/src/i18n/locale.ts | 52 ++ frontend/src/i18n/locales/en/accounts.json | 620 ++++++++++++ frontend/src/i18n/locales/en/appearance.json | 222 +++++ frontend/src/i18n/locales/en/auth.json | 21 + frontend/src/i18n/locales/en/common.json | 140 +++ frontend/src/i18n/locales/en/dashboard.json | 111 +++ frontend/src/i18n/locales/en/groups.json | 78 ++ frontend/src/i18n/locales/en/index.ts | 29 + frontend/src/i18n/locales/en/keys.json | 110 +++ frontend/src/i18n/locales/en/navigation.json | 29 + frontend/src/i18n/locales/en/plugins.json | 533 +++++++++++ frontend/src/i18n/locales/en/proxies.json | 165 ++++ frontend/src/i18n/locales/en/settings.json | 648 +++++++++++++ .../src/i18n/locales/en/systemUpdate.json | 109 +++ frontend/src/i18n/locales/en/usage.json | 348 +++++++ .../src/i18n/locales/zh-Hans/accounts.json | 620 ++++++++++++ .../src/i18n/locales/zh-Hans/appearance.json | 222 +++++ frontend/src/i18n/locales/zh-Hans/auth.json | 21 + frontend/src/i18n/locales/zh-Hans/common.json | 140 +++ .../src/i18n/locales/zh-Hans/dashboard.json | 111 +++ frontend/src/i18n/locales/zh-Hans/groups.json | 78 ++ frontend/src/i18n/locales/zh-Hans/index.ts | 29 + frontend/src/i18n/locales/zh-Hans/keys.json | 110 +++ .../src/i18n/locales/zh-Hans/navigation.json | 29 + .../src/i18n/locales/zh-Hans/plugins.json | 533 +++++++++++ .../src/i18n/locales/zh-Hans/proxies.json | 165 ++++ .../src/i18n/locales/zh-Hans/settings.json | 648 +++++++++++++ .../i18n/locales/zh-Hans/systemUpdate.json | 109 +++ frontend/src/i18n/locales/zh-Hans/usage.json | 348 +++++++ .../src/i18n/locales/zh-Hant/accounts.json | 620 ++++++++++++ .../src/i18n/locales/zh-Hant/appearance.json | 222 +++++ frontend/src/i18n/locales/zh-Hant/auth.json | 21 + frontend/src/i18n/locales/zh-Hant/common.json | 140 +++ .../src/i18n/locales/zh-Hant/dashboard.json | 111 +++ frontend/src/i18n/locales/zh-Hant/groups.json | 78 ++ frontend/src/i18n/locales/zh-Hant/index.ts | 29 + frontend/src/i18n/locales/zh-Hant/keys.json | 110 +++ .../src/i18n/locales/zh-Hant/navigation.json | 29 + .../src/i18n/locales/zh-Hant/plugins.json | 533 +++++++++++ .../src/i18n/locales/zh-Hant/proxies.json | 165 ++++ .../src/i18n/locales/zh-Hant/settings.json | 648 +++++++++++++ .../i18n/locales/zh-Hant/systemUpdate.json | 109 +++ frontend/src/i18n/locales/zh-Hant/usage.json | 348 +++++++ frontend/src/i18n/messages.ts | 12 + frontend/src/i18n/ui.ts | 8 + frontend/src/i18n/vue-i18n.d.ts | 5 + frontend/src/layout/components/AppSidebar.vue | 65 +- .../components/FloatingSidebarToggle.vue | 27 +- .../components/system-update-modal/index.vue | 85 +- .../system-update-modal/presenter.ts | 52 +- frontend/src/layout/index.vue | 14 +- frontend/src/main.ts | 4 +- frontend/src/router/index.ts | 3 +- frontend/src/stores/modules/system-update.ts | 23 +- frontend/src/utils/data.ts | 8 +- frontend/src/utils/format.ts | 27 +- frontend/src/utils/location.ts | 3 +- frontend/src/utils/notification.ts | 4 + frontend/src/utils/operation.ts | 3 +- frontend/src/utils/providers.ts | 3 +- .../components/AccountApiKeyFields.vue | 29 +- .../components/AccountBatchEditModal.vue | 12 +- .../components/AccountCapacityIndicator.vue | 10 +- .../components/AccountConcurrencyModal.vue | 9 +- .../components/AccountConcurrencyTable.vue | 20 +- .../components/AccountConnectionTestModal.vue | 41 +- .../accounts/components/AccountEditModal.vue | 33 +- .../accounts/components/AccountFilters.vue | 30 +- .../components/AccountModelAccessField.vue | 31 +- .../components/AccountNotesPopover.vue | 8 +- .../components/AccountOverviewCards.vue | 22 +- .../accounts/components/AccountProxyField.vue | 17 +- .../components/AccountSettingsFields.vue | 22 +- .../components/AccountTableActions.vue | 23 +- .../accounts/components/AccountUsagePanel.vue | 62 +- .../AccountImportFields.vue | 9 +- .../AccountOAuthFields.vue | 20 +- .../AccountProviderChooser.vue | 15 +- .../AccountSetupFields.vue | 11 +- .../components/account-create-modal/index.vue | 21 +- .../account-create-modal/presenter.ts | 33 +- .../account-import-tasks/TaskDetail.vue | 36 +- .../components/account-import-tasks/index.vue | 28 +- .../account-import-tasks/presenter.ts | 36 +- .../ActivityInsights.vue | 26 +- .../account-profile-modal/Metrics.vue | 27 +- .../account-profile-modal/ProfileHero.vue | 8 +- .../account-profile-modal/Skeleton.vue | 6 +- .../account-profile-modal/Subscription.vue | 28 +- .../account-profile-modal/TokenActivity.vue | 26 +- .../account-profile-modal/activity.ts | 5 +- .../account-profile-modal/index.vue | 25 +- .../ForecastCapacity.vue | 23 +- .../account-quota-forecast-modal/Skeleton.vue | 6 +- .../account-quota-forecast-modal/index.vue | 46 +- .../account-quota-panel/Credits.vue | 20 +- .../components/account-quota-panel/Entry.vue | 27 +- .../ResetCreditAutomation.vue | 41 +- .../account-quota-panel/ResetCredits.vue | 62 +- .../account-quota-panel/UsageLimits.vue | 18 +- .../components/account-quota-panel/index.vue | 20 +- .../account-quota-summary-cell/Entry.vue | 28 +- .../WindowGroup.vue | 4 +- .../account-quota-summary-cell/index.vue | 24 +- .../components/account-status-badge/index.vue | 16 +- .../account-status-badge/presenter.ts | 78 +- .../composables/useAccountAuthorization.ts | 9 +- .../composables/useAccountBatchEditor.ts | 11 +- .../composables/useAccountConnectionTest.ts | 102 +- .../accounts/composables/useAccountEditor.ts | 47 +- .../composables/useAccountImportTasks.ts | 3 +- .../composables/useAccountMutations.ts | 41 +- .../composables/useAccountOnboarding.ts | 23 +- .../composables/useAccountQuotaForecast.ts | 5 +- .../composables/useAccountResetCredits.ts | 21 +- frontend/src/views/accounts/constants.ts | 80 +- frontend/src/views/accounts/index.vue | 46 +- .../src/views/accounts/utils/accountCreate.ts | 7 +- .../src/views/accounts/utils/accountImport.ts | 17 +- .../src/views/accounts/utils/modelAccess.ts | 9 +- .../views/accounts/utils/schedulingForm.ts | 6 +- .../views/accounts/utils/upstreamApiKey.ts | 18 +- .../components/AccountOverviewCard.vue | 74 +- .../components/DashboardAccountIdentity.vue | 12 +- .../dashboard/components/DashboardContent.vue | 22 +- .../components/DashboardHeartbeat.vue | 8 +- .../views/dashboard/components/MetricCard.vue | 8 +- .../dashboard/components/RequestTrendCard.vue | 29 +- .../dashboard/components/UsageRecordCard.vue | 18 +- .../dashboard/components/WireProfileCard.vue | 58 +- .../dashboard/composables/useDashboard.ts | 13 +- .../composables/useRequestTrendChart.ts | 159 ++-- frontend/src/views/dashboard/presenter.ts | 108 +-- .../groups/components/AccountGroupActions.vue | 13 +- .../groups/components/AccountGroupFilters.vue | 16 +- .../components/AccountGroupFormModal.vue | 52 +- .../components/AccountGroupMetricsCell.vue | 26 +- .../groups/composables/useAccountGroups.ts | 24 +- frontend/src/views/groups/constants.ts | 32 +- frontend/src/views/groups/index.vue | 54 +- .../key-usage/components/KeyUsageBudget.vue | 27 +- .../key-usage/components/KeyUsageHeader.vue | 33 +- .../key-usage/components/KeyUsageRecords.vue | 15 +- .../key-usage/components/KeyUsageSkeleton.vue | 26 +- .../key-usage/components/KeyUsageSummary.vue | 19 +- .../key-usage/components/KeyUsageTrend.vue | 22 +- .../key-usage/composables/useKeyConfig.ts | 3 +- frontend/src/views/key-usage/index.vue | 43 +- frontend/src/views/key-usage/utils/format.ts | 7 +- frontend/src/views/key-usage/utils/metrics.ts | 18 +- .../views/keys/components/ApiKeyActions.vue | 18 +- .../keys/components/ApiKeyBudgetCell.vue | 28 +- .../components/ApiKeyBudgetResetModal.vue | 45 +- .../keys/components/ApiKeyCreatedModal.vue | 22 +- .../views/keys/components/ApiKeyFilters.vue | 12 +- .../views/keys/components/ApiKeyFormModal.vue | 62 +- .../keys/components/ApiKeyPrefixCell.vue | 6 +- .../views/keys/components/ApiKeyScopeCell.vue | 6 +- .../keys/components/ApiKeyStatusBadge.vue | 6 +- .../views/keys/composables/useApiKeyEditor.ts | 21 +- .../keys/composables/useApiKeyMutations.ts | 9 +- .../views/keys/composables/useApiKeyUse.ts | 7 +- frontend/src/views/keys/constants.ts | 28 +- frontend/src/views/keys/index.vue | 50 +- .../src/views/login/components/LoginPanel.vue | 46 +- .../login/components/LoginThemeToggle.vue | 6 +- frontend/src/views/login/index.vue | 18 +- .../plugins/components/PluginAssetPicker.vue | 11 +- .../components/PluginBindingEditor.vue | 36 +- .../components/PluginCapabilityTags.vue | 8 +- .../plugins/components/PluginCatalogPanel.vue | 48 +- .../components/PluginConfigurationFields.vue | 32 +- .../components/PluginConfigurationModal.vue | 43 +- .../components/PluginConfigurationSummary.vue | 34 +- .../plugins/components/PluginDetailModal.vue | 48 +- .../PluginDownloadAuthentication.vue | 58 +- .../plugins/components/PluginEnableModal.vue | 24 +- .../components/PluginExtensionsPanel.vue | 14 +- .../PluginFrontendAuthenticationEditor.vue | 83 +- .../plugins/components/PluginInstallModal.vue | 107 ++- .../components/PluginLegacyConfigurations.vue | 19 +- .../plugins/components/PluginManagement.vue | 90 +- .../components/PluginManagementFrame.vue | 8 +- .../views/plugins/components/PluginPage.vue | 22 +- .../plugins/components/PluginPageLoading.vue | 6 +- .../components/PluginResourcePicker.vue | 28 +- .../components/PluginRollbackModal.vue | 26 +- .../plugins/components/PluginScopeInput.vue | 7 +- .../components/PluginSourceProxyField.vue | 14 +- .../plugins/components/PluginStatusNotice.vue | 17 +- .../components/PluginUpdateCheckModal.vue | 40 +- .../plugins/components/PluginVersionCard.vue | 24 +- .../components/PluginVersionsPanel.vue | 10 +- .../components/SourceCredentialPicker.vue | 9 +- .../composables/usePluginArtifactActions.ts | 7 +- .../composables/usePluginCredentials.ts | 9 +- .../composables/usePluginFrameSession.ts | 65 +- .../composables/usePluginInstallation.ts | 29 +- .../plugins/composables/usePluginInstances.ts | 21 +- .../composables/usePluginManagement.ts | 7 +- .../plugins/composables/usePluginUninstall.ts | 17 +- .../composables/usePluginUpdateCheck.ts | 7 +- .../plugins/composables/usePluginVersions.ts | 19 +- frontend/src/views/plugins/constants.ts | 76 +- .../plugins/management/bridge-runtime.ts | 62 +- .../src/views/plugins/management/frame.ts | 81 +- frontend/src/views/plugins/utils/actions.ts | 2 +- frontend/src/views/plugins/utils/model.ts | 17 +- .../proxies/components/ProxyAccountsModal.vue | 32 +- .../views/proxies/components/ProxyActions.vue | 14 +- .../components/ProxyBatchCreateFields.vue | 37 +- .../proxies/components/ProxyBatchResults.vue | 31 +- .../proxies/components/ProxyFormModal.vue | 62 +- .../views/proxies/components/ProxyRegion.vue | 21 +- .../views/proxies/composables/useProxies.ts | 39 +- .../proxies/composables/useProxyAccounts.ts | 5 +- .../composables/useProxyBatchCreate.ts | 11 +- .../proxies/composables/useProxyBatchTest.ts | 5 +- .../composables/useProxyBatchTesting.ts | 13 +- frontend/src/views/proxies/constants.ts | 24 +- frontend/src/views/proxies/index.vue | 46 +- frontend/src/views/proxies/utils/batch.ts | 44 +- frontend/src/views/proxies/utils/location.ts | 6 +- .../components/AccountAffinityCard.vue | 35 +- .../components/AccountAutoFreezeCard.vue | 52 +- .../settings/components/AccountWarmupCard.vue | 44 +- .../settings/components/AdminApiKeyCard.vue | 28 +- .../settings/components/AdminPasswordCard.vue | 12 +- .../components/ChangePasswordModal.vue | 35 +- .../settings/components/ClientProfileCard.vue | 8 +- .../components/ConcurrencyPolicyCard.vue | 50 +- .../settings/components/ModelAliasesCard.vue | 22 +- .../components/RequestHistoryCard.vue | 12 +- .../components/RequestLocationCard.vue | 10 +- .../settings/components/ResetCreditCard.vue | 14 +- .../components/ResponseBodyLimitCard.vue | 10 +- .../components/RotationStrategyCard.vue | 20 +- .../components/SettingsAccessSection.vue | 14 +- .../components/SmartSchedulingModal.vue | 46 +- .../settings/components/TokenRefreshCard.vue | 14 +- .../components/backup/BackupRecordsCard.vue | 57 +- .../components/backup/BackupScheduleCard.vue | 42 +- .../components/backup/BackupStatusBadge.vue | 22 +- .../components/backup/BackupStorageCard.vue | 78 +- .../components/backup/R2GuideModal.vue | 58 +- .../components/ClientCliInstallPanel.vue | 28 +- .../components/ClientDesktopInstallPanel.vue | 20 +- .../ClientDesktopOfflineDownloads.vue | 40 +- .../components/ClientInstallCommandList.vue | 8 +- .../components/ClientVersionGuideModal.vue | 14 +- .../components/ClientVersionPolicyCard.vue | 24 +- .../components/privacy/PolicyPreview.vue | 33 +- .../components/privacy/PrivacyPolicyCard.vue | 29 +- .../components/privacy/RuleEditor.vue | 48 +- .../components/privacy/RulesTable.vue | 32 +- .../components/privacy/StepFields.vue | 58 +- .../settings/components/privacy/StepList.vue | 42 +- .../settings/components/privacy/presets.ts | 58 +- .../settings/composables/useAdminApiKey.ts | 11 +- .../settings/composables/useBackupRecords.ts | 11 +- .../settings/composables/useBackupSettings.ts | 11 +- .../settings/composables/useSettingsForm.ts | 55 +- frontend/src/views/settings/constants.ts | 23 +- frontend/src/views/settings/index.vue | 34 +- .../settings/pricing/PricingBatchModal.vue | 20 +- .../views/settings/pricing/PricingEditor.vue | 56 +- .../settings/pricing/PricingRateFields.vue | 11 +- .../settings/pricing/PricingSyncModal.vue | 30 +- .../views/settings/pricing/PricingTable.vue | 42 +- .../views/settings/pricing/PricingToolbar.vue | 36 +- .../views/settings/pricing/PricingUnit.vue | 12 +- frontend/src/views/settings/pricing/index.vue | 12 +- frontend/src/views/settings/pricing/model.ts | 36 +- .../src/views/settings/pricing/usePricing.ts | 9 +- .../components/AppearanceSettingsSection.vue | 45 +- .../theme/components/ThemeColorTokenField.vue | 10 +- .../components/ThemeComponentPreview.vue | 232 ++--- .../components/ThemeComponentTokenPanel.vue | 132 +-- .../components/ThemeDashboardPreview.vue | 12 +- .../theme/components/ThemeEditorPreview.vue | 26 +- .../components/ThemeGlobalTokenPanel.vue | 98 +- .../components/ThemeNumberTokenField.vue | 8 +- .../theme/components/ThemePreviewCanvas.vue | 18 +- .../theme/components/ThemeTextTokenField.vue | 6 +- .../src/views/theme/fixtures/dashboard.ts | 48 +- .../components/DiagnosticsDownloadButton.vue | 5 +- .../views/usage/components/UsageCostCard.vue | 69 +- .../usage/components/UsageDiagnosticCard.vue | 64 +- .../components/UsageErrorDetailModal.vue | 75 +- .../views/usage/components/UsageFilters.vue | 25 +- .../usage/components/UsageHealthCard.vue | 66 +- .../usage/components/UsageInsightsGrid.vue | 8 +- .../usage/components/UsagePerformanceCard.vue | 59 +- .../components/UsageRecordDetailModal.vue | 159 ++-- .../usage/components/UsageSummaryCards.vue | 32 +- frontend/src/views/usage/index.vue | 24 +- frontend/src/views/usage/utils/chart.ts | 9 +- frontend/src/views/usage/utils/detail.ts | 11 +- frontend/tsconfig.node.json | 3 +- frontend/tsconfig.source.json | 3 + frontend/vite.config.ts | 9 +- modules/plugins | 2 +- modules/ui | 2 +- 363 files changed, 15479 insertions(+), 3795 deletions(-) delete mode 100644 frontend/src/components/AppAboutModal.vue create mode 100644 frontend/src/components/AppLanguageMenu.vue create mode 100644 frontend/src/composables/useLocale.ts create mode 100644 frontend/src/i18n/index.ts create mode 100644 frontend/src/i18n/locale.ts create mode 100644 frontend/src/i18n/locales/en/accounts.json create mode 100644 frontend/src/i18n/locales/en/appearance.json create mode 100644 frontend/src/i18n/locales/en/auth.json create mode 100644 frontend/src/i18n/locales/en/common.json create mode 100644 frontend/src/i18n/locales/en/dashboard.json create mode 100644 frontend/src/i18n/locales/en/groups.json create mode 100644 frontend/src/i18n/locales/en/index.ts create mode 100644 frontend/src/i18n/locales/en/keys.json create mode 100644 frontend/src/i18n/locales/en/navigation.json create mode 100644 frontend/src/i18n/locales/en/plugins.json create mode 100644 frontend/src/i18n/locales/en/proxies.json create mode 100644 frontend/src/i18n/locales/en/settings.json create mode 100644 frontend/src/i18n/locales/en/systemUpdate.json create mode 100644 frontend/src/i18n/locales/en/usage.json create mode 100644 frontend/src/i18n/locales/zh-Hans/accounts.json create mode 100644 frontend/src/i18n/locales/zh-Hans/appearance.json create mode 100644 frontend/src/i18n/locales/zh-Hans/auth.json create mode 100644 frontend/src/i18n/locales/zh-Hans/common.json create mode 100644 frontend/src/i18n/locales/zh-Hans/dashboard.json create mode 100644 frontend/src/i18n/locales/zh-Hans/groups.json create mode 100644 frontend/src/i18n/locales/zh-Hans/index.ts create mode 100644 frontend/src/i18n/locales/zh-Hans/keys.json create mode 100644 frontend/src/i18n/locales/zh-Hans/navigation.json create mode 100644 frontend/src/i18n/locales/zh-Hans/plugins.json create mode 100644 frontend/src/i18n/locales/zh-Hans/proxies.json create mode 100644 frontend/src/i18n/locales/zh-Hans/settings.json create mode 100644 frontend/src/i18n/locales/zh-Hans/systemUpdate.json create mode 100644 frontend/src/i18n/locales/zh-Hans/usage.json create mode 100644 frontend/src/i18n/locales/zh-Hant/accounts.json create mode 100644 frontend/src/i18n/locales/zh-Hant/appearance.json create mode 100644 frontend/src/i18n/locales/zh-Hant/auth.json create mode 100644 frontend/src/i18n/locales/zh-Hant/common.json create mode 100644 frontend/src/i18n/locales/zh-Hant/dashboard.json create mode 100644 frontend/src/i18n/locales/zh-Hant/groups.json create mode 100644 frontend/src/i18n/locales/zh-Hant/index.ts create mode 100644 frontend/src/i18n/locales/zh-Hant/keys.json create mode 100644 frontend/src/i18n/locales/zh-Hant/navigation.json create mode 100644 frontend/src/i18n/locales/zh-Hant/plugins.json create mode 100644 frontend/src/i18n/locales/zh-Hant/proxies.json create mode 100644 frontend/src/i18n/locales/zh-Hant/settings.json create mode 100644 frontend/src/i18n/locales/zh-Hant/systemUpdate.json create mode 100644 frontend/src/i18n/locales/zh-Hant/usage.json create mode 100644 frontend/src/i18n/messages.ts create mode 100644 frontend/src/i18n/ui.ts create mode 100644 frontend/src/i18n/vue-i18n.d.ts create mode 100644 frontend/src/utils/notification.ts diff --git a/backend/crates/gateway-api/src/admin/accounts/presenter.rs b/backend/crates/gateway-api/src/admin/accounts/presenter.rs index 8a6bd2bc8..869fa5c14 100644 --- a/backend/crates/gateway-api/src/admin/accounts/presenter.rs +++ b/backend/crates/gateway-api/src/admin/accounts/presenter.rs @@ -84,7 +84,7 @@ pub(super) fn account_view( let usage_period = quota.usage_window().map(|(_, period)| period); let mut usage = account_usage_view(usage, usage_period, now, time); if account.authentication_kind == "api_key" { - usage.window_label_display = "通用额度".to_owned(); + usage.window_kind = AccountUsageWindowKind::General; } let (quota, refresh_token_expires_at) = account_quota_view(quota, cooldown, now, time); AccountView { @@ -344,12 +344,11 @@ pub(super) fn account_usage_view( "known" }; AccountUsageView { - window_label_display: match period { - Some(AccountUsagePeriod::Weekly) => "周额度窗口", - Some(AccountUsagePeriod::Monthly) => "月额度窗口", - None => "周/月额度窗口", - } - .to_owned(), + window_kind: match period { + Some(AccountUsagePeriod::Weekly) => AccountUsageWindowKind::Weekly, + Some(AccountUsagePeriod::Monthly) => AccountUsageWindowKind::Monthly, + None => AccountUsageWindowKind::Quota, + }, request_count: Some(usage.request_count), request_count_display: format_number(usage.request_count), input_tokens: usage.input_tokens, @@ -470,7 +469,7 @@ pub(super) fn display_optional_tokens(value: Option) -> String { pub(super) fn empty_account_usage() -> AccountUsageView { AccountUsageView { - window_label_display: "周/月额度窗口".to_owned(), + window_kind: AccountUsageWindowKind::Quota, request_count: None, request_count_display: "—".to_owned(), input_tokens: None, diff --git a/backend/crates/gateway-api/src/admin/accounts/wire.rs b/backend/crates/gateway-api/src/admin/accounts/wire.rs index 10c8b12f8..49accbee1 100644 --- a/backend/crates/gateway-api/src/admin/accounts/wire.rs +++ b/backend/crates/gateway-api/src/admin/accounts/wire.rs @@ -425,11 +425,21 @@ pub struct AccountQuotaWindowView { pub reset_at_display: String, } +/// 账号用量窗口的语义标识,展示名称由管理端本地化 +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum AccountUsageWindowKind { + General, + Weekly, + Monthly, + Quota, +} + /// 账号观测用量 #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct AccountUsageView { - pub window_label_display: String, + pub window_kind: AccountUsageWindowKind, pub request_count: Option, pub request_count_display: String, pub input_tokens: Option, diff --git a/backend/crates/gateway-api/src/admin/observability/presenter.rs b/backend/crates/gateway-api/src/admin/observability/presenter.rs index 8d5d813af..5da2fa0ee 100644 --- a/backend/crates/gateway-api/src/admin/observability/presenter.rs +++ b/backend/crates/gateway-api/src/admin/observability/presenter.rs @@ -735,34 +735,34 @@ pub(crate) fn trend_summary_view( match kind { TrendKind::Usage => vec![ TrendSummaryView { - label: "输入".to_owned(), + metric: TrendMetric::InputTokens, value: format_compact_number(summary.input_tokens), ratio: None, }, TrendSummaryView { - label: "输出".to_owned(), + metric: TrendMetric::OutputTokens, value: format_compact_number(summary.output_tokens), ratio: None, }, TrendSummaryView { - label: "缓存".to_owned(), + metric: TrendMetric::CachedInputTokens, value: format_compact_number(summary.cached_input_tokens), ratio: None, }, ], TrendKind::Latency => vec![ TrendSummaryView { - label: "首字 P95".to_owned(), + metric: TrendMetric::FirstTokenP95Ms, value: display_duration_f64(summary.peak_first_token_p95_ms), ratio: None, }, TrendSummaryView { - label: "总耗时 P95".to_owned(), + metric: TrendMetric::LatencyP95Ms, value: display_duration_f64(summary.peak_latency_p95_ms), ratio: None, }, TrendSummaryView { - label: "速率 P50".to_owned(), + metric: TrendMetric::OutputThroughputP50, value: summary .minimum_output_throughput_p50 .map_or_else(|| "—".to_owned(), |value| format!("{value} tok/s")), @@ -771,19 +771,19 @@ pub(crate) fn trend_summary_view( ], TrendKind::Errors => vec![ TrendSummaryView { - label: "错误数".to_owned(), + metric: TrendMetric::Errors, value: format_compact_number(summary.service_failure_count), ratio: None, }, TrendSummaryView { - label: "成功率".to_owned(), + metric: TrendMetric::SuccessRate, value: "—".to_owned(), ratio: summary .success_rate .map(|value| format!("{:.1}%", value * 100.0)), }, TrendSummaryView { - label: "总请求".to_owned(), + metric: TrendMetric::Requests, value: format_compact_number(summary.request_count), ratio: None, }, diff --git a/backend/crates/gateway-api/src/admin/observability/wire.rs b/backend/crates/gateway-api/src/admin/observability/wire.rs index 829465fcd..de76f2c66 100644 --- a/backend/crates/gateway-api/src/admin/observability/wire.rs +++ b/backend/crates/gateway-api/src/admin/observability/wire.rs @@ -323,10 +323,25 @@ pub struct TrendPointView { pub capacity_utilization: Option, } +/// 趋势指标的稳定身份,展示名称由管理端本地化 +#[derive(Debug, Clone, Copy, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum TrendMetric { + InputTokens, + OutputTokens, + CachedInputTokens, + FirstTokenP95Ms, + LatencyP95Ms, + OutputThroughputP50, + Errors, + SuccessRate, + Requests, +} + /// Dashboard 趋势摘要 #[derive(Debug, Serialize)] pub struct TrendSummaryView { - pub label: String, + pub metric: TrendMetric, pub value: String, pub ratio: Option, } diff --git a/backend/crates/gateway-api/tests/admin/accounts/mod.rs b/backend/crates/gateway-api/tests/admin/accounts/mod.rs index b633ecda6..92a1cc79c 100644 --- a/backend/crates/gateway-api/tests/admin/accounts/mod.rs +++ b/backend/crates/gateway-api/tests/admin/accounts/mod.rs @@ -476,13 +476,13 @@ fn single_update_should_reject_oversized_notes_and_control_characters() { } mod response { - use gateway_api::admin::accounts::AccountUsageView; + use gateway_api::admin::accounts::{AccountUsageView, AccountUsageWindowKind}; use gateway_api::admin::presenter::format_decimal_currency; #[test] fn account_usage_view_should_keep_unobserved_numbers_null() { let view = AccountUsageView { - window_label_display: "周/月额度窗口".to_owned(), + window_kind: AccountUsageWindowKind::Quota, request_count: None, request_count_display: "-".to_owned(), input_tokens: None, @@ -516,7 +516,7 @@ mod response { models: Vec::new(), }; let value = serde_json::to_value(view).expect("serialize account usage"); - assert_eq!(value["windowLabelDisplay"], "周/月额度窗口"); + assert_eq!(value["windowKind"], "quota"); assert!(value["inputTokens"].is_null()); assert!(value["totalTokens"].is_null()); assert!(value["reasoningOutputTokens"].is_null()); diff --git a/backend/crates/gateway-api/tests/admin/observability/response.rs b/backend/crates/gateway-api/tests/admin/observability/response.rs index c4343041c..40bb3987e 100644 --- a/backend/crates/gateway-api/tests/admin/observability/response.rs +++ b/backend/crates/gateway-api/tests/admin/observability/response.rs @@ -1464,3 +1464,69 @@ async fn usage_status_query_accepts_three_modes_and_rejects_unknown_values() { } } } + +#[tokio::test] +async fn dashboard_summary_should_identify_metrics_without_localized_labels() { + use axum::{ + body::{Body, to_bytes}, + http::{Request, StatusCode, header}, + }; + use chrono::Duration; + use gateway_admin::model::observability::{DashboardObservation, TimeRange}; + use gateway_api::admin::observability; + use tower::ServiceExt as _; + + use crate::admin::{AdminTestFixture, AdminTestState}; + + let fixture = AdminTestFixture::new().await; + fixture.auth.insert_session("valid-session"); + let now = Utc::now(); + *fixture.dashboard_observation.lock().unwrap() = Some(DashboardObservation { + yesterday: Default::default(), + analytics: Default::default(), + runtime_slots: None, + range: TimeRange::new(now - Duration::hours(1), now).unwrap(), + totals: Default::default(), + provider_accounts: Default::default(), + trend: Vec::new(), + account_usage: Vec::new(), + recent_requests: Vec::new(), + }); + + for (kind, metrics) in [ + ( + "usage", + ["inputTokens", "outputTokens", "cachedInputTokens"], + ), + ( + "latency", + ["firstTokenP95Ms", "latencyP95Ms", "outputThroughputP50"], + ), + ("errors", ["errors", "successRate", "requests"]), + ] { + let response = observability::router::() + .with_state(fixture.state()) + .oneshot( + Request::builder() + .uri(format!("/api/admin/dashboard/summary?kind={kind}")) + .header(header::COOKIE, "cpr_session=valid-session") + .header(header::ACCEPT_LANGUAGE, "en") + .header("x-request-id", format!("req_trend_metrics_{kind}")) + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + let status = response.status(); + let body = to_bytes(response.into_body(), 1024 * 1024).await.unwrap(); + let value: serde_json::Value = serde_json::from_slice(&body).unwrap(); + assert_eq!(status, StatusCode::OK, "{kind}: {value}"); + let summary = value["data"]["trend"]["summary"].as_array().unwrap(); + assert_eq!(summary.len(), metrics.len()); + for (item, metric) in summary.iter().zip(metrics) { + assert_eq!(item["metric"], metric); + assert!(item.get("label").is_none()); + assert!(item["value"].is_string()); + } + } +} diff --git a/docs/api.md b/docs/api.md index 46b5d9492..5dedb37f3 100644 --- a/docs/api.md +++ b/docs/api.md @@ -158,6 +158,8 @@ Codex PAT 验证服务不可用和身份响应无效分别返回 `50301`、`5020 `YYYY-MM-DD HH:mm:ss`,短时间、相对时间和图表标签按对应视图返回;缺失时间的展示字段为 `null` 或占位文本。 调用者直接显示文本,使用原始时间点进行排序、比较和输入,不把展示文本转回请求时间 +账号、模型用量和 Key 列表的最近使用时间由前端根据原始 RFC3339 时间点,通过 VueUse `useTimeAgoIntl` 按界面语言显示短相对时间,并随时间更新;悬停的完整日期仍使用后端 `lastUsedAtFullDisplay`,不使用浏览器本地时区重算 + 账号冷却返回 RFC3339 `rateLimitedUntil` 和 `rateLimitRecoveryDisplay`,后者包含剩余时长、 到期时间或等待恢复探测的提示,随账号数据刷新;前端不解析展示文本或自行计算恢复提示 @@ -1034,7 +1036,7 @@ API Key 账号与 OAuth 共用现有 Responses、Images 和 standalone Search 包括 Responses Lite 和原生压缩协议的透传,实际支持情况由上游决定;不提供独立 compact 路由。 模型目录、连接测试和本地用量统计可用;OAuth 刷新/重新授权、ChatGPT 额度、个人资料、订阅和重置卡不适用。 客户端的 `image_generation` 和 `X-OpenAI-Actor-Authorization` 声明不改变账号的实际能力。 -账号列表和详情的 API Key `usage` 汇总该账号创建后仍保留的本地请求记录,`windowLabelDisplay` 为 `通用额度`; +账号列表和详情的 API Key `usage` 汇总该账号创建后仍保留的本地请求记录,`usage.windowKind` 为 `general`,管理端按当前语言显示通用额度; 日志清理会影响累计范围,不代表上游余额。OAuth 账号仍按实际周/月额度窗口统计 批量导入 AT / RT 使用 `accounts` JSON 数组,最多 200 项,不接受纯文本 token 列表。例如: @@ -1165,10 +1167,10 @@ OAuth start 使用: - 正常 Responses 请求会解析上游响应的 rate-limit headers,合并进同一 quota 快照并同步状态。Free、 K12 等套餐共用该状态机;套餐只参与账号展示和按套餐隔离的模型目录 cache,不存在 K12 专属额度路径 - 账号详情的 Token 统计、模型排行和列表 Token 汇总优先使用账号级周额度窗口,无可统计的周窗口时 - 使用月额度窗口;`usage.windowLabelDisplay` 随选中的窗口返回“周额度窗口”或“月额度窗口”。查询边界 + 使用月额度窗口;`usage.windowKind` 随选中的窗口返回 `weekly` 或 `monthly`。查询边界 严格为 `[resetAt - windowSeconds, resetAt)`,不是自然周/月或最近 7/30 天;额度刷新若返回了更早的 - 重置时间,会按新边界重新聚合。没有边界完整、可归属到账号的周/月窗口时显示无数据,标签为 - “周/月额度窗口”,不回退到 5 小时、日窗口或历史累计。 + 重置时间,会按新边界重新聚合。没有边界完整、可归属到账号的周/月窗口时显示无数据,`usage.windowKind` 为 `quota` + (周/月额度窗口),不回退到 5 小时、日窗口或历史累计。 金额原值保持完整精度,USD 展示值 小于 1 美元时最多保留四位小数,其余保留两位 @@ -1989,6 +1991,18 @@ errorCode, errorMessage, startedAt, completedAt, expiresAt, createdAt, updatedAt | `GET` | `/api/admin/operations/errors` | 运维错误分页列表 | | `GET` | `/api/admin/diagnostics/export` | 按 `requestId` 或 `errorId` 下载诊断 ZIP 附件 | +### Dashboard 趋势指标 + +趋势摘要 `summary[]` 返回稳定的 `metric`、展示数值 `value` 和可空的 `ratio`,指标名称由前端本地化 + +| kind | metric | +| --- | --- | +| `usage` | `inputTokens`、`outputTokens`、`cachedInputTokens` | +| `latency` | `firstTokenP95Ms`、`latencyP95Ms`、`outputThroughputP50` | +| `errors` | `errors`、`successRate`、`requests` | + +图表颜色、系列身份和选中状态使用 `metric`,不根据译文或数组位置推断指标 + ### Dashboard 容量与账号用量 Dashboard 的 `capacityInfo` 统计普通容量,不含独立审批池;`maxConcurrentPerAccount` 为默认账号并发上限,`0` 表示不限制。 diff --git a/docs/architecture.md b/docs/architecture.md index c70b4b3a5..330d52990 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -91,7 +91,7 @@ Pinia 持久化、登录、路由和业务请求由各应用持有 部署时区由 Host 配置拥有,组合根把不可变的 `DeploymentTimeZone` 传给各模块。 Core 的 `time` 只提供时区与日历边界换算;Admin/Provider 拥有查询范围与调度规则,Store 绑定显式 UTC 边界并维护事务。 -API 的时间 presenter 负责页面及事件展示文本,前端消费投影,不解释时区或格式化日期。 +API 的时间 presenter 负责部署时区下的日期及事件展示文本,前端不重新解释时区或格式化绝对日期;最近使用的相对时间本地化边界见 [API 页面时间](api.md#页面时间合同)。 持久化、协议传输、排序与 TTL 使用 UTC 时间点或持续时长,不依赖进程、浏览器及数据库会话的本地时区。 请求位置中的时区属于 Provider 请求身份,与部署日历规则独立。配置操作见[部署时区](../deploy/README.md#部署时区) @@ -342,6 +342,7 @@ Client Key 费用账本独立累计各次 attempt 的实际费用,不能因请 | `presenter.ts`、页面 `utils/` | 纯展示投影或该页面拥有的业务转换,不启动请求 | | `components/usage/`、`components/account/` | 跨页面复用的用量、健康时间线、套餐和额度窗口展示 | | `composables/` | 请求取消与过期结果隔离、分页、选择、异步动作、目录加载等共享机制 | +| `i18n/` | Vue I18n 实例、三语资源和应用语言策略;从全局 Composer 派生 UI 库语言,不翻译用户数据或远端自由文本 | | `utils/` | 按用途分开的数据解析、数字与时长格式化、位置校验、客户端配置、插件导航及颜色工具 | JSON、原始字节与流式请求共用会话恢复和身份代际校验,身份变化后的迟到结果按取消处理。 diff --git a/docs/theme.md b/docs/theme.md index 048fdff7e..51729120b 100644 --- a/docs/theme.md +++ b/docs/theme.md @@ -45,7 +45,7 @@ - **文案不使用句号和分号**:自行编写的标题、标签、按钮、表单帮助、空状态、提示和错误文案不使用 `。`、句末 `.`、`;` 或 `;`,优先写成短语或简短分句 需要表达多个要点时,按内容关系拆成字段、短行或列表,不把长段落机械替换成逗号串 这项约定针对界面文案,URL、版本号、小数、代码、原始日志和用户输入保留原样 -- **说明只补必要信息**:标题、字段值和操作已能表达的内容不再重复,不同时用帮助文字、状态行和说明卡解释同一事实 +- **页面直显以简洁为主**:标题、标签、按钮使用简短明确的名称或动作,说明只补必要信息,不重复标题、字段值和操作已能表达的内容,不同时用多种说明形式解释同一事实 页面保留当前决策必需的短提示,详细规则、背景原因、示例和低频帮助优先收进已有 Popover 或就近的帮助入口,不把实现细节写成用户教程 重要说明可以在 Popover 中展开,但影响当前选择的关键限制、错误原因和不可逆后果需在操作处保留简明提示,不能全部藏起来 - **按内容选择提示方式**:Tooltip 用于短名称或单句补充,Popover 用于需要阅读、多段说明或含链接的内容,复用项目现有组件 @@ -62,10 +62,26 @@ | 页面头部 | `@codex-proxy/ui` 的 `ZPageHeader`、[系统概览](../frontend/src/views/dashboard/components/DashboardContent.vue) | 简短标题、必要的统计范围或状态、就近操作 | | 内容与指标 | `@codex-proxy/ui` 的 `ZCard`、[用量概览卡片](../frontend/src/views/usage/components/UsageSummaryCards.vue) | 可选说明、紧凑摘要、数值与辅助信息的主次 | | 表单帮助 | [API Key 账号字段](../frontend/src/views/accounts/components/AccountApiKeyFields.vue) | 直接命名字段,在对应位置提供短提示和示例 | +| 详细规则 | [自动审批并发帮助](../frontend/src/views/settings/components/ConcurrencyPolicyCard.vue) | 标签旁帮助图标打开 Popover,页面保留短提示,浮层补充规则和示例 | | 账号与用量展示 | [账号额度窗口](../frontend/src/components/account/account-usage-window/index.vue)、[共用用量表格](../frontend/src/components/usage/UsageRecordsTable.vue) | 复用后端展示字段和共享 presenter,管理端与 Key 页保持同一展示口径 | 页面查询、表单状态与纯展示逻辑的归属见 [前端模块职责](architecture.md#34-前端模块职责) +## 语言与字体 + +应用使用 Vue I18n Composition API,语言标识为 `en`、`zh-Hans`、`zh-Hant`。资源按领域存放在 `frontend/src/i18n/locales/`,使用 `领域.功能.用途` 的语义 key,例如 `accounts.connectionTest.description`,三种语言保持相同 key 和具名参数 + +- 标签、帮助、ARIA、通知和图表显式取消息;列定义、选项和展示投影使用响应式计算,不把翻译结果存进业务数据 +- 完整句子使用具名参数,含组件的消息使用 `I18nT`;数量使用 Vue I18n 复数规则,不拆词拼接 +- 专业缩写在三种语言中保留原样及规范大小写,例如 `TTFT`、`RPM`、`TPM`、`API`、`HTTP`、`WS`、`RC`,直接显示时不翻译或展开,必要的全称和解释放入帮助提示 +- 英文使用句首大写和美式拼写,统一 Account、Proxy、Usage、Rate limit、API key、TTFT 等术语,协议标识和用户值保留原样 +- 繁体中文只转换简体原文的字形,保持措辞与语序,不替换地区用词,例如 `数据 → 數據`、`设置 → 設置` +- 只翻译应用拥有的文本,具体后端错误、上游诊断、日志和独立插件内容保留原文;固定错误码在请求层映射,业务分类码优先保留后端公开的具体原因,无原因时使用本地兜底文案 +- 侧边栏语言图标展开就地选项,选择后切换并收起;品牌区固定显示 `Codex Proxy`、`Rust build` 和版本号 +- UI 库默认文案由 `ZConfigProvider` 提供,命令式通知通过 `utils/notification.ts` 绑定同一个语言来源,组件约定见 [UI 国际化](../modules/ui/playground/guide/i18n.md) + +正文默认使用 Inter Variable,中文及缺失字形按系统无衬线字体栈回退。代码使用 JetBrains Mono Variable,中文由系统字体补齐,不随语言切换额外修改字重。默认值由 UI 库持有,应用负责加载字体包,图表读取同一字体 Token + ## 架构概览 ```mermaid diff --git a/frontend/eslint.config.ts b/frontend/eslint.config.ts index 81677acfc..5c4feca34 100644 --- a/frontend/eslint.config.ts +++ b/frontend/eslint.config.ts @@ -1,6 +1,7 @@ // @env node import type { ESLint, Rule } from 'eslint' import antfu from '@antfu/eslint-config' +import vueI18n from '@intlify/eslint-plugin-vue-i18n' type LocatedRuleNode = Rule.Node & { loc: NonNullable @@ -99,4 +100,25 @@ export default antfu( 'vue/mustache-interpolation-spacing': ['error', 'always'], }, }, + ...vueI18n.configs.base, + { + settings: { + 'vue-i18n': { + localeDir: { + pattern: './src/i18n/locales/*/*.json', + localeKey: 'path', + localePattern: /\/locales\/(?[^/]+)\//, + }, + messageSyntaxVersion: '^11.0.0', + }, + }, + rules: { + '@intlify/vue-i18n/no-missing-keys': 'error', + '@intlify/vue-i18n/no-missing-keys-in-other-locales': 'error', + '@intlify/vue-i18n/no-duplicate-keys-in-locale': 'error', + '@intlify/vue-i18n/valid-message-syntax': 'error', + '@intlify/vue-i18n/valid-plural-forms': 'error', + '@intlify/vue-i18n/no-unused-keys': ['error', { extensions: ['.ts', '.vue'] }], + }, + }, ) diff --git a/frontend/package.json b/frontend/package.json index ade696aaa..cdb7cb6e2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@boxicons/vue": "1.0.1", - "@codex-proxy/ui": "github:zyycn/codex-proxy-ui#v0.5.0", + "@codex-proxy/ui": "github:zyycn/codex-proxy-ui#acd8578f82a91dc5d6da70b68c3bd354a1dde061", "@fontsource-variable/inter": "^5.3.0", "@fontsource-variable/jetbrains-mono": "^5.3.0", "@lucide/vue": "^1.59.0", @@ -34,10 +34,13 @@ "semver": "^7.8.5", "sortablejs": "^1.15.7", "vue": "^3.5.44", + "vue-i18n": "11.4.16", "vue-router": "^5.4.0" }, "devDependencies": { "@antfu/eslint-config": "^9.5.1", + "@intlify/eslint-plugin-vue-i18n": "4.5.1", + "@intlify/unplugin-vue-i18n": "11.2.7", "@tailwindcss/vite": "^4.3.3", "@types/css-tree": "^3.2.0", "@types/node": "^26.6.5", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index d065b079a..899fbca8f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -195,8 +195,8 @@ importers: specifier: 1.0.1 version: 1.0.1(vue@3.5.44(typescript@6.0.3)) '@codex-proxy/ui': - specifier: github:zyycn/codex-proxy-ui#v0.5.0 - version: https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/5eac879635ab4c15cfeea71d3d65fbf467780bbc(axios@1.20.0(debug@4.4.3))(change-case@5.4.4)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.44(typescript@6.0.3)) + specifier: github:zyycn/codex-proxy-ui#acd8578f82a91dc5d6da70b68c3bd354a1dde061 + version: https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/acd8578f82a91dc5d6da70b68c3bd354a1dde061(axios@1.20.0(debug@4.4.3))(change-case@5.4.4)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.44(typescript@6.0.3)) '@fontsource-variable/inter': specifier: ^5.3.0 version: 5.3.0 @@ -245,6 +245,9 @@ importers: vue: specifier: ^3.5.44 version: 3.5.44(typescript@6.0.3) + vue-i18n: + specifier: 11.4.16 + version: 11.4.16(vue@3.5.44(typescript@6.0.3)) vue-router: specifier: ^5.4.0 version: 5.4.0(@vue/compiler-sfc@3.5.44)(pinia@4.1.0(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.44(typescript@6.0.3)))(rolldown@1.2.8)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.44(typescript@6.0.3)) @@ -252,6 +255,12 @@ importers: '@antfu/eslint-config': specifier: ^9.5.1 version: 9.5.1(@typescript-eslint/typescript-estree@8.69.0(typescript@6.0.3))(@typescript-eslint/utils@8.69.0(eslint@10.13.0(jiti@2.7.0))(typescript@6.0.3))(@vue/compiler-sfc@3.5.44)(eslint-plugin-format@2.0.1(eslint@10.13.0(jiti@2.7.0)))(eslint-plugin-vuejs-accessibility@2.6.2(eslint@10.13.0(jiti@2.7.0))(globals@17.12.0))(eslint@10.13.0(jiti@2.7.0))(typescript@6.0.3) + '@intlify/eslint-plugin-vue-i18n': + specifier: 4.5.1 + version: 4.5.1(eslint@10.13.0(jiti@2.7.0))(jsonc-eslint-parser@3.3.0)(vue-eslint-parser@10.4.1(eslint@10.13.0(jiti@2.7.0)))(yaml-eslint-parser@2.1.0) + '@intlify/unplugin-vue-i18n': + specifier: 11.2.7 + version: 11.2.7(typescript@6.0.3)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue-i18n@11.4.16(vue@3.5.44(typescript@6.0.3)))(vue@3.5.44(typescript@6.0.3)) '@tailwindcss/vite': specifier: ^4.3.3 version: 4.3.3(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0)) @@ -403,6 +412,10 @@ packages: engines: {node: ^22.18.0 || >=24.11.0} hasBin: true + '@babel/runtime@7.29.10': + resolution: {integrity: sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} @@ -430,8 +443,8 @@ packages: resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} engines: {node: '>= 20.12.0'} - '@codex-proxy/ui@https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/5eac879635ab4c15cfeea71d3d65fbf467780bbc': - resolution: {gitHosted: true, integrity: sha512-/n01jJCEKGxCzwkHkpBFUOP3Z+MeV5ufA/14qnrHJ3k9pm9cdb083Nhvd4KaIP9PftHXbCOISUzY9D9EzTZWBA==, tarball: https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/5eac879635ab4c15cfeea71d3d65fbf467780bbc} + '@codex-proxy/ui@https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/acd8578f82a91dc5d6da70b68c3bd354a1dde061': + resolution: {gitHosted: true, integrity: sha512-Zme/PI94AL8qt3mQ2YsYMlYWAtqQhqZDxDq7s/R1uE/lEt9ZCcpLkG+o14UmMbal9fLTUMMjAjudOCUUhevk3A==, tarball: https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/acd8578f82a91dc5d6da70b68c3bd354a1dde061} version: 0.5.0 engines: {node: '>=24'} peerDependencies: @@ -473,6 +486,162 @@ packages: resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} engines: {node: '>=10'} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-plugin-eslint-comments@4.7.2': resolution: {integrity: sha512-LF03qURSwEWm2dz5wtdDCzNk+7Opl0X7q6I3undsaIuNsEiNvRV3BCtqu14Q/6Pzg1tBj44LcxpW2EpSLZStZw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -518,6 +687,10 @@ packages: resolution: {integrity: sha512-cg0ohyrAG3swyGqt8t1K/OK97DqBw/ftDvlvyY1fmEst5B40UOmsimwLENq74z2dyw5CDM+3zJIW+CV2nFNDdA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/eslintrc@3.3.7': + resolution: {integrity: sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/markdown@8.0.3': resolution: {integrity: sha512-rBTSSShrq7e4O+PWfeE4azH4/CWPNrC+VGxBXiW00o3vYVJnznsZiDayj3KC9JztIMVRZxZHn2nrDIUau/4j7A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -556,6 +729,79 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@intlify/bundle-utils@11.2.7': + resolution: {integrity: sha512-N1u1ZG553SkFxiQ3AEx2Fc7QuGxATjPCHDveHkSP93Imgz8VhHJUZZHEA9pat6oPZC/VcYbP0953EBtPWd+LEA==} + engines: {node: '>= 22.13'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/core-base@11.4.16': + resolution: {integrity: sha512-xB8nMdMRLNx7b0zPvrHy0+N9uq9Vfi1aUxHqWgn2e2/TbjCKy8HfoFpnUXXxVP8I0p1ccd6LFSC3XsFjZ3vweA==} + engines: {node: '>= 22'} + + '@intlify/devtools-types@11.4.16': + resolution: {integrity: sha512-Bl7arICsKgwcSEQuZJun3swenN4jwSpg35lCpijFznYbW9GvkfH4wLxcI5+gz7d/EHqu66UT9CZDIdnQdgMAGg==} + engines: {node: '>= 22'} + + '@intlify/eslint-plugin-vue-i18n@4.5.1': + resolution: {integrity: sha512-iAX/zbg48uF8BvjfJKGKUOCdxntcJsNsh5iXePh9ivMDu+AdzfN7EHsdKyohxXTKcc8HaU1bW/SW++BQ+C0HUQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + eslint: ^8.0.0 || ^9.0.0-0 || ^10.0.0 + jsonc-eslint-parser: ^2.3.0 || ^3.0.0 + vue-eslint-parser: ^10.0.0 + yaml-eslint-parser: ^1.2.2 || ^2.0.0 + + '@intlify/message-compiler@11.4.16': + resolution: {integrity: sha512-p4lDWmBAsi3ja+PuEfPRavYyvqkYn/Upxz7UpnyI9LgIIy8XawcGIDzN+k99Z+99etO3HmDXGeTxnJIJGgSZ+g==} + engines: {node: '>= 22'} + + '@intlify/shared@10.0.8': + resolution: {integrity: sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==} + engines: {node: '>= 16'} + + '@intlify/shared@11.4.16': + resolution: {integrity: sha512-xehmkbEgbbNh/V+d8r3hFup0ViaN29YTJSAfRr28C54rxjyZEO0RNQKqW7VVajj16gLdglotNjiq+sIVNtHGzw==} + engines: {node: '>= 22'} + + '@intlify/unplugin-vue-i18n@11.2.7': + resolution: {integrity: sha512-S+kF2s8tve3GZ93C6Y9K7HtS2NGQrXkbUPO0g7e7Fz2xf1kjurk4cnu7G3hsfy9GPckRSGtUYrgB+54/fRgHUA==} + engines: {node: '>= 22.13'} + peerDependencies: + petite-vue-i18n: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vite: + optional: true + vue-i18n: + optional: true + + '@intlify/vue-i18n-extensions@8.0.0': + resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==} + engines: {node: '>= 18'} + peerDependencies: + vue: ^3.0.0 + vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 + peerDependenciesMeta: + vue: + optional: true + vue-i18n: + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -718,6 +964,10 @@ packages: cpu: [x64] os: [win32] + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@pkgr/core@0.3.6': resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -917,6 +1167,15 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@sindresorhus/base62@1.0.0': resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} engines: {node: '>=18'} @@ -1183,6 +1442,9 @@ packages: '@vue/compiler-ssr@3.5.44': resolution: {integrity: sha512-xr5gb1JVHlfXJwSlRz1FeP6uxoazdzHuMTVLE7up+4Vrqxk219CvxLPxD9NXBt47yvZW7+p4DXfyp4O1fkByIA==} + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + '@vue/devtools-api@8.2.1': resolution: {integrity: sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==} @@ -1300,6 +1562,22 @@ packages: alien-signals@3.2.1: resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.4.0: + resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + ansis@4.3.1: resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} engines: {node: '>=14'} @@ -1308,6 +1586,9 @@ packages: resolution: {integrity: sha512-sqRsNQBwbKLRX0jV5Cu5uzmtflf892n4Vukz7T659ebL4pz3mpOqCMU7lxMoBTFwnp10E3YB5ZcyHM41W5bcDA==} engines: {node: '>=18'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -1330,6 +1611,9 @@ packages: axios@1.20.0: resolution: {integrity: sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -1345,6 +1629,12 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} + + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} + brace-expansion@5.0.12: resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} @@ -1369,6 +1659,10 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} @@ -1392,6 +1686,13 @@ packages: circle-flags@2.8.3: resolution: {integrity: sha512-62gm4tY7evXzNdLP+nFOzxEtCagbY4nSnrPNn/yOWFAvApvOWCAvyqL+NeLN+nfhGOGc4L+oSgEheqM3XE5w7g==} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1408,6 +1709,9 @@ packages: resolution: {integrity: sha512-rKZTGo4fzKYna8UcL0isTg5wkBNla7bxTypLwZQXjIdi++IdP1OJ41rI5Mti3/jltkPujbu4i9LIARYA+zpotQ==} engines: {node: '>= 12.0.0'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -1489,12 +1793,21 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + echarts@6.1.0: resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==} electron-to-chromium@1.5.422: resolution: {integrity: sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.1: resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} engines: {node: '>=14'} @@ -1503,6 +1816,10 @@ packages: resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} engines: {node: '>=10.13.0'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -1530,6 +1847,11 @@ packages: es-toolkit@1.52.0: resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1548,6 +1870,12 @@ packages: peerDependencies: eslint: '>=6.0.0' + eslint-compat-utils@0.6.5: + resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + eslint-config-flat-gitignore@2.4.0: resolution: {integrity: sha512-JhYC+V7qEDiCDsbJcVP8fxNc62fk4+i91YLP/YvmVHlkaQ8Xo99olYN8MO5COdxl7onGlsrfZVONe5NYsZP1UA==} peerDependencies: @@ -1744,6 +2072,10 @@ packages: resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -1828,6 +2160,10 @@ packages: debug: optional: true + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + form-data@4.0.6: resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} @@ -1866,6 +2202,15 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + globals@15.15.0: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} @@ -1931,6 +2276,14 @@ packages: resolution: {integrity: sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==} engines: {node: '>= 4'} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-fresh@4.0.1: + resolution: {integrity: sha512-/XFeXYgkuWgbtqPC7qPKvciXTvhtz7WMNQKQsAM1Kz+j0I5OWJDrybfiya6QtHF9oEvPkLwTHmC9+4OR/MmfIA==} + engines: {node: '>=22.15'} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1947,6 +2300,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1955,13 +2312,23 @@ packages: resolution: {integrity: sha512-NhOds0mDx9lJu+1lBRO0xbwFo5nobA7GCk/0e5xjr6+6XugX985+0OyGX35BNrTkPAsdLcIKg02HUQJOK8D8kw==} engines: {node: '>=18'} + is-language-code@3.1.0: + resolution: {integrity: sha512-zJdQ3QTeLye+iphMeK3wks+vXSRFKh68/Pnlw7aOfApFSEIOhYa8P9vwwa6QrImNNBMJTiL1PpYF0f4BxDuEgA==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + jsdoc-type-pratt-parser@9.0.1: resolution: {integrity: sha512-1LJ/negRKJxH0+ulOsJYEpUgzIs1DAOz9QctuS4In2LW/Ro3l8C4ej5al+5FU2hBGEOAY3R3SCnP6x8226JMIw==} engines: {node: ^22.22.2 || >=24.15.0} @@ -1981,6 +2348,15 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.2: + resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + jsonc-eslint-parser@3.3.0: resolution: {integrity: sha512-hYTGkHGNRZnXOFZ1urhINADoqDrGfpy53cjw+dxk84QE0pUDujQzeUeamNs6Mz44/TKD49z2x6/GVSu4ZrtA+Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -2155,6 +2531,9 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + magic-string-ast@1.0.3: resolution: {integrity: sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==} engines: {node: '>=20.19.0'} @@ -2328,6 +2707,17 @@ packages: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} @@ -2396,9 +2786,16 @@ packages: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@1.8.0: resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + parse-gitignore@2.0.0: resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} engines: {node: '>=14'} @@ -2409,6 +2806,9 @@ packages: parse-statements@1.0.11: resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -2420,6 +2820,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -2530,6 +2934,10 @@ packages: resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} engines: {node: '>=18'} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -2563,6 +2971,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -2582,10 +2994,30 @@ packages: spdx-license-ids@3.0.23: resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-indent@4.1.1: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + super-regex@1.1.0: resolution: {integrity: sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==} engines: {node: '>=18'} @@ -2672,6 +3104,10 @@ packages: resolution: {integrity: sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==} engines: {node: '>=20.19.0'} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + unplugin@3.4.0: resolution: {integrity: sha512-9skdIFlCsPdFV7wUfZxNsFInlW+7nJmGu2gkTu0OUhF56aXGsHab9x52/QhdJ4lC7ZDPWTxbiC4ANqVlsuaW3w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2776,6 +3212,12 @@ packages: peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + vue-i18n@11.4.16: + resolution: {integrity: sha512-Kv9tp6iTmaAeOwop7hkAo28N1IBsbSVpnxyE26b6gqc4Q2E9Ke3ZHHA3oqqwS6/cYLT1henZU4etTaLQxrK98Q==} + engines: {node: '>= 22'} + peerDependencies: + vue: ^3.0.0 + vue-router@5.4.0: resolution: {integrity: sha512-pFvcYcxinDHjZUlf7AzFBrT8r8Xqvv4uMHoJnjc+EqPawXQvHTkF8S4tfSmQLpriVHUGfkaKlI7Et0KRjTMTdw==} peerDependencies: @@ -2823,10 +3265,22 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} + yaml-eslint-parser@1.3.2: + resolution: {integrity: sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==} + engines: {node: ^14.17.0 || >=16.0.0} + yaml-eslint-parser@2.1.0: resolution: {integrity: sha512-1zo9KRfp6vIAXBEhWAz09ex3hUwh3T+/6dGbGteHvNseA0Uk4FgQnPES55klZ6cDzSy9FpgKS0D/CoLUvCCj4w==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -2928,6 +3382,8 @@ snapshots: dependencies: '@babel/types': 8.0.6 + '@babel/runtime@7.29.10': {} + '@babel/types@7.29.8': dependencies: '@babel/helper-string-parser': 7.29.7 @@ -2966,7 +3422,7 @@ snapshots: fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@codex-proxy/ui@https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/5eac879635ab4c15cfeea71d3d65fbf467780bbc(axios@1.20.0(debug@4.4.3))(change-case@5.4.4)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.44(typescript@6.0.3))': + '@codex-proxy/ui@https://codeload.github.com/zyycn/codex-proxy-ui/tar.gz/acd8578f82a91dc5d6da70b68c3bd354a1dde061(axios@1.20.0(debug@4.4.3))(change-case@5.4.4)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.44(typescript@6.0.3))': dependencies: '@ant-design/colors': 8.0.1 '@ant-design/fast-color': 3.0.1 @@ -3027,6 +3483,84 @@ snapshots: '@es-joy/resolve.exports@1.2.0': {} + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + '@eslint-community/eslint-plugin-eslint-comments@4.7.2(eslint@10.13.0(jiti@2.7.0))': dependencies: escape-string-regexp: 4.0.0 @@ -3071,6 +3605,20 @@ snapshots: mdn-data: 2.34.0 source-map-js: 1.2.2 + '@eslint/eslintrc@3.3.7': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.2 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + '@eslint/markdown@8.0.3': dependencies: '@eslint/core': 1.2.1 @@ -3114,6 +3662,102 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@intlify/bundle-utils@11.2.7(vue-i18n@11.4.16(vue@3.5.44(typescript@6.0.3)))': + dependencies: + '@intlify/message-compiler': 11.4.16 + '@intlify/shared': 11.4.16 + acorn: 8.18.0 + esbuild: 0.25.12 + jsonc-eslint-parser: 2.4.2 + source-map-js: 1.2.2 + yaml-eslint-parser: 1.3.2 + optionalDependencies: + vue-i18n: 11.4.16(vue@3.5.44(typescript@6.0.3)) + + '@intlify/core-base@11.4.16': + dependencies: + '@intlify/devtools-types': 11.4.16 + '@intlify/message-compiler': 11.4.16 + '@intlify/shared': 11.4.16 + + '@intlify/devtools-types@11.4.16': + dependencies: + '@intlify/core-base': 11.4.16 + '@intlify/shared': 11.4.16 + + '@intlify/eslint-plugin-vue-i18n@4.5.1(eslint@10.13.0(jiti@2.7.0))(jsonc-eslint-parser@3.3.0)(vue-eslint-parser@10.4.1(eslint@10.13.0(jiti@2.7.0)))(yaml-eslint-parser@2.1.0)': + dependencies: + '@eslint/eslintrc': 3.3.7 + '@intlify/core-base': 11.4.16 + '@intlify/message-compiler': 11.4.16 + debug: 4.4.3 + eslint: 10.13.0(jiti@2.7.0) + eslint-compat-utils: 0.6.5(eslint@10.13.0(jiti@2.7.0)) + glob: 10.5.0 + globals: 17.12.0 + ignore: 7.0.9 + import-fresh: 4.0.1 + is-language-code: 3.1.0 + js-yaml: 4.3.2 + json5: 2.2.3 + jsonc-eslint-parser: 3.3.0 + parse5: 7.3.0 + semver: 7.8.5 + synckit: 0.11.13 + vue-eslint-parser: 10.4.1(eslint@10.13.0(jiti@2.7.0)) + yaml-eslint-parser: 2.1.0 + transitivePeerDependencies: + - supports-color + + '@intlify/message-compiler@11.4.16': + dependencies: + '@intlify/shared': 11.4.16 + source-map-js: 1.2.2 + + '@intlify/shared@10.0.8': {} + + '@intlify/shared@11.4.16': {} + + '@intlify/unplugin-vue-i18n@11.2.7(typescript@6.0.3)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue-i18n@11.4.16(vue@3.5.44(typescript@6.0.3)))(vue@3.5.44(typescript@6.0.3))': + dependencies: + '@intlify/bundle-utils': 11.2.7(vue-i18n@11.4.16(vue@3.5.44(typescript@6.0.3))) + '@intlify/shared': 11.4.16 + '@intlify/vue-i18n-extensions': 8.0.0(vue-i18n@11.4.16(vue@3.5.44(typescript@6.0.3)))(vue@3.5.44(typescript@6.0.3)) + '@rollup/pluginutils': 5.4.0 + '@typescript-eslint/scope-manager': 8.69.0 + '@typescript-eslint/typescript-estree': 8.69.0(typescript@6.0.3) + debug: 4.4.3 + pathe: 2.0.3 + picocolors: 1.1.1 + tinyglobby: 0.2.17 + unplugin: 2.3.11 + vue: 3.5.44(typescript@6.0.3) + optionalDependencies: + vite: 8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0) + vue-i18n: 11.4.16(vue@3.5.44(typescript@6.0.3)) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + + '@intlify/vue-i18n-extensions@8.0.0(vue-i18n@11.4.16(vue@3.5.44(typescript@6.0.3)))(vue@3.5.44(typescript@6.0.3))': + dependencies: + '@babel/parser': 7.29.9 + '@intlify/shared': 10.0.8 + '@vue/compiler-dom': 3.5.44 + optionalDependencies: + vue: 3.5.44(typescript@6.0.3) + vue-i18n: 11.4.16(vue@3.5.44(typescript@6.0.3)) + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -3209,6 +3853,9 @@ snapshots: '@oxfmt/binding-win32-x64-msvc@0.35.0': optional: true + '@pkgjs/parseargs@0.11.0': + optional: true + '@pkgr/core@0.3.6': {} '@rolldown/binding-android-arm-eabi@1.2.14': @@ -3303,6 +3950,12 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@rollup/pluginutils@5.4.0': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.7 + '@sindresorhus/base62@1.0.0': {} '@stylistic/eslint-plugin@5.10.0(eslint@10.13.0(jiti@2.7.0))': @@ -3584,6 +4237,8 @@ snapshots: '@vue/compiler-dom': 3.5.44 '@vue/shared': 3.5.44 + '@vue/devtools-api@6.6.4': {} + '@vue/devtools-api@8.2.1': dependencies: '@vue/devtools-kit': 8.2.1 @@ -3680,10 +4335,22 @@ snapshots: alien-signals@3.2.1: {} + ansi-regex@5.0.1: {} + + ansi-regex@6.4.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + ansis@4.3.1: {} are-docs-informative@0.1.1: {} + argparse@2.0.1: {} + aria-query@5.3.2: {} ast-kit@2.2.0: @@ -3715,6 +4382,8 @@ snapshots: - debug - supports-color + balanced-match@1.0.2: {} + balanced-match@4.0.4: {} baseline-browser-mapping@2.11.21: {} @@ -3723,6 +4392,15 @@ snapshots: boolbase@1.0.0: {} + brace-expansion@1.1.21: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.1.7: + dependencies: + balanced-match: 1.0.2 + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -3752,6 +4430,8 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 + callsites@3.1.0: {} + caniuse-lite@1.0.30001810: {} ccount@2.0.1: {} @@ -3768,6 +4448,12 @@ snapshots: circle-flags@2.8.3: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -3778,6 +4464,8 @@ snapshots: comment-parser@1.4.8: {} + concat-map@0.0.1: {} + confbox@0.1.8: {} confbox@0.3.1: {} @@ -3841,6 +4529,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + echarts@6.1.0: dependencies: tslib: 2.3.0 @@ -3848,6 +4538,10 @@ snapshots: electron-to-chromium@1.5.422: {} + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + empathic@2.0.1: {} enhanced-resolve@5.24.5: @@ -3855,6 +4549,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + entities@6.0.1: {} + entities@7.0.1: {} entities@8.0.0: {} @@ -3876,6 +4572,35 @@ snapshots: es-toolkit@1.52.0: {} + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -3887,6 +4612,11 @@ snapshots: eslint: 10.13.0(jiti@2.7.0) semver: 7.8.5 + eslint-compat-utils@0.6.5(eslint@10.13.0(jiti@2.7.0)): + dependencies: + eslint: 10.13.0(jiti@2.7.0) + semver: 7.8.5 + eslint-config-flat-gitignore@2.4.0(eslint@10.13.0(jiti@2.7.0)): dependencies: '@eslint/compat': 2.1.1(eslint@10.13.0(jiti@2.7.0)) @@ -4178,6 +4908,12 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 5.0.1 + espree@9.6.1: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 3.4.3 + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -4247,6 +4983,11 @@ snapshots: optionalDependencies: debug: 4.4.3 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + form-data@4.0.6: dependencies: asynckit: 0.4.0 @@ -4292,6 +5033,17 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + globals@14.0.0: {} + globals@15.15.0: {} globals@17.12.0: {} @@ -4341,6 +5093,13 @@ snapshots: ignore@7.0.9: {} + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-fresh@4.0.1: {} + imurmurhash@0.1.4: {} indent-string@5.0.0: {} @@ -4351,6 +5110,8 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -4360,10 +5121,24 @@ snapshots: identifier-regex: 1.1.0 super-regex: 1.1.0 + is-language-code@3.1.0: + dependencies: + '@babel/runtime': 7.29.10 + isexe@2.0.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jiti@2.7.0: {} + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + jsdoc-type-pratt-parser@9.0.1: dependencies: '@types/estree': 1.0.9 @@ -4379,6 +5154,15 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.2: + dependencies: + acorn: 8.18.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.8.5 + jsonc-eslint-parser@3.3.0: dependencies: acorn: 8.18.0 @@ -4508,6 +5292,8 @@ snapshots: longest-streak@3.1.0: {} + lru-cache@10.4.3: {} + magic-string-ast@1.0.3: dependencies: magic-string: 0.30.21 @@ -4879,6 +5665,16 @@ snapshots: dependencies: brace-expansion: 5.0.12 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.21 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.7 + + minipass@7.1.3: {} + mlly@1.8.2: dependencies: acorn: 8.18.0 @@ -4959,8 +5755,14 @@ snapshots: p-timeout@6.1.4: {} + package-json-from-dist@1.0.1: {} + package-manager-detector@1.8.0: {} + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parse-gitignore@2.0.0: {} parse-imports-exports@0.2.4: @@ -4969,12 +5771,21 @@ snapshots: parse-statements@1.0.11: {} + parse5@7.3.0: + dependencies: + entities: 6.0.1 + path-browserify@1.0.1: {} path-exists@4.0.0: {} path-key@3.1.1: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + pathe@2.0.3: {} perfect-debounce@2.1.0: {} @@ -5063,6 +5874,8 @@ snapshots: reserved-identifiers@1.2.0: {} + resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} rolldown@1.2.14: @@ -5124,6 +5937,8 @@ snapshots: shebang-regex@3.0.0: {} + signal-exit@4.1.0: {} + sisteransi@1.0.5: {} sortablejs@1.15.7: {} @@ -5139,8 +5954,30 @@ snapshots: spdx-license-ids@3.0.23: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.4.0 + strip-indent@4.1.1: {} + strip-json-comments@3.1.1: {} + super-regex@1.1.0: dependencies: function-timeout: 1.0.2 @@ -5224,6 +6061,13 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.7 + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.18.0 + picomatch: 4.0.7 + webpack-virtual-modules: 0.6.2 + unplugin@3.4.0(rolldown@1.2.8)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0)): dependencies: '@jridgewell/remapping': 2.3.5 @@ -5274,6 +6118,14 @@ snapshots: transitivePeerDependencies: - supports-color + vue-i18n@11.4.16(vue@3.5.44(typescript@6.0.3)): + dependencies: + '@intlify/core-base': 11.4.16 + '@intlify/devtools-types': 11.4.16 + '@intlify/shared': 11.4.16 + '@vue/devtools-api': 6.6.4 + vue: 3.5.44(typescript@6.0.3) + vue-router@5.4.0(@vue/compiler-sfc@3.5.44)(pinia@4.1.0(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.44(typescript@6.0.3)))(rolldown@1.2.8)(vite@8.3.4(@types/node@26.6.5)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.44(typescript@6.0.3)): dependencies: '@vue-macros/common': 3.1.4(vue@3.5.44(typescript@6.0.3)) @@ -5334,8 +6186,25 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + xml-name-validator@5.0.0: {} + yaml-eslint-parser@1.3.2: + dependencies: + eslint-visitor-keys: 3.4.3 + yaml: 2.9.0 + yaml-eslint-parser@2.1.0: dependencies: eslint-visitor-keys: 5.0.1 diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml index 4f2737b20..f99ced53a 100644 --- a/frontend/pnpm-workspace.yaml +++ b/frontend/pnpm-workspace.yaml @@ -3,3 +3,4 @@ autoInstallPeers: false # Git 依赖通过 prepack 生成产物;信任自有 UI 仓库,实际提交仍由锁文件固定。 allowBuilds: '@codex-proxy/ui@git+https://github.com/zyycn/codex-proxy-ui.git': true + esbuild@0.25.12: true diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a78fb6fd3..c145fca49 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,7 +1,17 @@ diff --git a/frontend/src/api/modules/accounts.ts b/frontend/src/api/modules/accounts.ts index 89fb72ee6..50b65a13e 100644 --- a/frontend/src/api/modules/accounts.ts +++ b/frontend/src/api/modules/accounts.ts @@ -87,7 +87,7 @@ export interface AccountModelUsage { } export interface AccountUsage { - windowLabelDisplay: string + windowKind: 'general' | 'weekly' | 'monthly' | 'quota' requestCount: number | null requestCountDisplay: string inputTokens: number | null diff --git a/frontend/src/api/modules/dashboard.ts b/frontend/src/api/modules/dashboard.ts index 94db2a3a4..76f356297 100644 --- a/frontend/src/api/modules/dashboard.ts +++ b/frontend/src/api/modules/dashboard.ts @@ -42,8 +42,13 @@ export interface DashboardTrendPoint { capacityUtilization: number | null } +export type DashboardTrendMetric + = 'inputTokens' | 'outputTokens' | 'cachedInputTokens' + | 'firstTokenP95Ms' | 'latencyP95Ms' | 'outputThroughputP50' + | 'errors' | 'successRate' | 'requests' + export interface DashboardTrendSummaryItem { - label: string + metric: DashboardTrendMetric value: string ratio: string | null } diff --git a/frontend/src/api/modules/key-usage.ts b/frontend/src/api/modules/key-usage.ts index 75c9c502b..f9402799b 100644 --- a/frontend/src/api/modules/key-usage.ts +++ b/frontend/src/api/modules/key-usage.ts @@ -9,11 +9,6 @@ export interface KeyUsageConfig { plaintextKey: string } -export interface KeyUsageVersion { - version: string - gitSha: string -} - export interface KeyUsageMetrics { requests: number inputTokens: number @@ -109,14 +104,6 @@ export function getKeyUsageOverview(data: KeyUsageQuery, options: RequestOptions }) } -export function getKeyUsageVersion(options: RequestOptions = {}) { - return request({ - url: '/api/key-usage/version', - method: 'GET', - ...options, - }) -} - export function getKeyUsageConfig(options: RequestOptions = {}) { return request({ url: '/api/key-usage/config', diff --git a/frontend/src/api/request/plugins/error.ts b/frontend/src/api/request/plugins/error.ts index 3de6c8a9e..4e8d1f03f 100644 --- a/frontend/src/api/request/plugins/error.ts +++ b/frontend/src/api/request/plugins/error.ts @@ -1,6 +1,7 @@ import type { AxiosError, AxiosResponse, AxiosResponseHeaders, RawAxiosResponseHeaders } from 'axios' import type { RequestContext, RequestNext } from './index' import { isAxiosError } from 'axios' +import { t } from '@/i18n' export type ApiErrorKind = 'api' | 'timeout' | 'network' | 'http' | 'cancelled' @@ -45,16 +46,16 @@ export function normalizeApiError(error: AxiosError) { const requestId = responseHeader(error.response?.headers, 'x-request-id') if (error.code === 'ERR_CANCELED') { - return new ApiError('请求已取消', status, undefined, requestId, 'cancelled') + return new ApiError(t('common.errors.canceled'), status, undefined, requestId, 'cancelled') } const apiError = error.response && normalizeApiResponseError(error.response) if (apiError) return apiError if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT' || error.code === 'ERR_TIMEOUT') { - return new ApiError('请求超时,请稍后重试', status, undefined, requestId, 'timeout') + return new ApiError(t('common.errors.timeout'), status, undefined, requestId, 'timeout') } if (!error.response) { - return new ApiError('网络连接失败,请检查网络后重试', 0, undefined, requestId, 'network') + return new ApiError(t('common.errors.network'), 0, undefined, requestId, 'network') } return new ApiError(httpFallback(status), status, undefined, requestId, 'http') } @@ -63,8 +64,9 @@ export function normalizeApiResponseError(response: AxiosResponse) { const envelope = adminErrorEnvelope(response.data) if (!envelope || envelope.code === 200) return undefined + const message = adminErrorMessage(envelope) || httpFallback(response.status >= 400 ? response.status : 0) return new ApiError( - envelope.message || httpFallback(response.status >= 400 ? response.status : 0), + message, response.status, envelope.code, responseHeader(response.headers, 'x-request-id'), @@ -82,6 +84,25 @@ function adminErrorEnvelope(value: unknown): AdminErrorEnvelope | null { return { code: record.code, message } } +// 固定错误码使用本地文案,业务分类码保留后端已公开的具体原因 +function adminErrorMessage(error: AdminErrorEnvelope): string { + switch (error.code) { + case 40000: return t('common.errors.malformedJson') + case 40002: return t('common.errors.invalidTimeRange') + case 40101: return t('common.errors.signInRequired') + case 40102: return t('common.errors.invalidCredentials') + case 40103: return t('common.errors.invalidAdminApiKey') + case 40301: return t('common.errors.forbidden') + case 42901: return t('common.errors.tooManyLoginAttempts') + case 42902: return error.message || t('common.errors.rateLimited') + case 50001: return t('common.errors.internal') + case 50201: return error.message || t('common.errors.upstreamFailure') + case 50202: return error.message || t('common.errors.upstreamResultUnknown') + case 50301: return error.message || t('common.errors.unavailable') + default: return error.message + } +} + function responseHeader( headers: RawAxiosResponseHeaders | AxiosResponseHeaders | undefined, name: string, @@ -95,35 +116,35 @@ function responseHeader( function httpFallback(status: number) { switch (status) { case 400: - return '请求参数不合法' + return t('common.errors.invalidParameters') case 401: - return '需要登录' + return t('common.errors.signInRequired') case 403: - return '没有权限执行此操作' + return t('common.errors.forbidden') case 404: - return '请求的接口不存在' + return t('common.errors.notFound') case 408: - return '请求超时,请稍后重试' + return t('common.errors.timeout') case 409: - return '资源状态冲突,请刷新后重试' + return t('common.errors.conflict') case 413: - return '请求内容过大' + return t('common.errors.payloadTooLarge') case 415: - return '请求格式不受支持' + return t('common.errors.unsupportedFormat') case 422: - return '请求内容不合法' + return t('common.errors.invalidContent') case 429: - return '请求过于频繁,请稍后重试' + return t('common.errors.rateLimited') case 500: - return '服务内部错误' + return t('common.errors.internal') case 502: - return '上游服务请求失败' + return t('common.errors.upstreamFailure') case 503: - return '服务暂不可用,请稍后重试' + return t('common.errors.unavailable') case 504: - return '上游服务响应超时' + return t('common.errors.upstreamTimeout') default: - return status > 0 ? `请求失败(HTTP ${status})` : '请求失败' + return status > 0 ? t('common.errors.http', { status }) : t('common.errors.request') } } diff --git a/frontend/src/api/request/plugins/feedback.ts b/frontend/src/api/request/plugins/feedback.ts index 2e96e3ccf..1fdfb82d9 100644 --- a/frontend/src/api/request/plugins/feedback.ts +++ b/frontend/src/api/request/plugins/feedback.ts @@ -1,5 +1,5 @@ import type { RequestContext, RequestNext } from './index' -import { ZNotification } from '@codex-proxy/ui' +import { notification as ZNotification } from '@/utils/notification' import { ApiError, isSessionRequired } from './error' // 共享刷新 Promise 的同一个失败只提示一次,弱引用不保留已结束请求 diff --git a/frontend/src/api/request/plugins/raw.ts b/frontend/src/api/request/plugins/raw.ts index 5d59a6037..07fdbf95b 100644 --- a/frontend/src/api/request/plugins/raw.ts +++ b/frontend/src/api/request/plugins/raw.ts @@ -1,4 +1,5 @@ import type { RequestContext, RequestNext } from './index' +import { t } from '@/i18n' import { ApiError, normalizeApiResponseError } from './error' export interface RawResponseMeta { @@ -50,7 +51,7 @@ export async function rawPlugin(context: RequestContext, next: RequestNext) { } const body = response.data as ArrayBuffer if (body.byteLength > options.maximumBytes) - throw new ApiError('响应内容超过大小限制', 502, undefined, rawHeader(response.headers, 'x-request-id'), 'http') + throw new ApiError(t('common.errors.responseTooLarge'), 502, undefined, rawHeader(response.headers, 'x-request-id'), 'http') return { ...response, data: { @@ -69,7 +70,7 @@ export async function rawPlugin(context: RequestContext, next: RequestNext) { if (normalized) throw normalized throw new ApiError( - response.status >= 400 ? `请求失败(HTTP ${response.status})` : '服务返回了不受支持的响应', + response.status >= 400 ? t('common.errors.http', { status: response.status }) : t('common.errors.unsupportedResponse'), response.status, undefined, rawHeader(response.headers, 'x-request-id'), @@ -90,7 +91,7 @@ async function readErrorBody(body: ReadableStream | null, maximumByt break length += value.byteLength if (length > maximumBytes) - throw new ApiError('响应内容超过大小限制', 502, undefined, undefined, 'http') + throw new ApiError(t('common.errors.responseTooLarge'), 502, undefined, undefined, 'http') chunks.push(value) } } diff --git a/frontend/src/api/request/plugins/retry.ts b/frontend/src/api/request/plugins/retry.ts index c3c4c0384..ead0a820c 100644 --- a/frontend/src/api/request/plugins/retry.ts +++ b/frontend/src/api/request/plugins/retry.ts @@ -1,4 +1,5 @@ import type { RequestContext, RequestNext } from './index' +import { t } from '@/i18n' import { ApiError, isTransientApiError } from './error' export async function retryPlugin(context: RequestContext, next: RequestNext) { @@ -6,7 +7,7 @@ export async function retryPlugin(context: RequestContext, next: RequestNext) { const retry = config.retry ?? ['GET', 'HEAD'].includes((config.method ?? 'GET').toUpperCase()) for (let attempt = 0; ; attempt += 1) { if (!context.isCurrent()) - throw new ApiError('请求已取消', 0, undefined, undefined, 'cancelled') + throw new ApiError(t('common.errors.canceled'), 0, undefined, undefined, 'cancelled') try { return await next() } diff --git a/frontend/src/api/request/plugins/session.ts b/frontend/src/api/request/plugins/session.ts index eed0b87e6..0c53bb85a 100644 --- a/frontend/src/api/request/plugins/session.ts +++ b/frontend/src/api/request/plugins/session.ts @@ -1,6 +1,7 @@ import type { RequestConfig } from '../index' import type { RequestContext, RequestNext } from './index' -import { ZNotification } from '@codex-proxy/ui' +import { t } from '@/i18n' +import { notification as ZNotification } from '@/utils/notification' import { ApiError, isSessionRequired } from './error' // 会话失效是后端业务事实,登录凭据错误和 403 不清除已有会话 @@ -65,15 +66,15 @@ export async function sessionPlugin(context: RequestContext, send: RequestNext) const isCurrent = context.isCurrent context.isCurrent = () => isCurrent() && generation === sessionGeneration if (!config.skipSessionRecovery && sessionRecovery && !await sessionRecovery) - return expireSession(new ApiError('登录已失效,请重新登录', 401, SESSION_REQUIRED), config, generation) + return expireSession(new ApiError(t('common.errors.sessionExpired'), 401, SESSION_REQUIRED), config, generation) if (config.signal?.aborted || generation !== sessionGeneration) - throw new ApiError('请求已取消', 0, undefined, undefined, 'cancelled') + throw new ApiError(t('common.errors.canceled'), 0, undefined, undefined, 'cancelled') const sendCurrent: RequestNext = async () => { const response = await send() if (context.isCurrent()) return response // 身份已切换时丢弃迟到结果,流式传输由请求层终止 - throw new ApiError('请求已取消', 0, undefined, undefined, 'cancelled') + throw new ApiError(t('common.errors.canceled'), 0, undefined, undefined, 'cancelled') } const recovered = successfulRecoveries try { diff --git a/frontend/src/components/AccountGroupCheckboxGrid.vue b/frontend/src/components/AccountGroupCheckboxGrid.vue index 72d0e5a0c..c62488f8f 100644 --- a/frontend/src/components/AccountGroupCheckboxGrid.vue +++ b/frontend/src/components/AccountGroupCheckboxGrid.vue @@ -3,6 +3,8 @@ import type { AccountGroupRef } from '@/api' import { ZCheckbox } from '@codex-proxy/ui' +import { useI18n } from 'vue-i18n' + withDefaults( defineProps<{ groups: AccountGroupRef[] @@ -15,6 +17,8 @@ withDefaults( }, ) +const { t } = useI18n({ useScope: 'global' }) + const selectedGroupIds = defineModel({ required: true }) function updateGroup(groupId: string, selected: boolean) { @@ -42,10 +46,10 @@ function updateGroup(groupId: string, selected: boolean) { @update:model-value="updateGroup(group.id, $event)" /> - 已禁用 + {{ t('common.status.disabled') }}

- {{ loading ? '正在加载分组...' : '暂无分组,请先在分组管理中创建' }} + {{ loading ? t('groups.selector.loading') : t('groups.selector.empty') }}

diff --git a/frontend/src/components/AccountGroupMarks.vue b/frontend/src/components/AccountGroupMarks.vue index dfc78a1f4..2597886e4 100644 --- a/frontend/src/components/AccountGroupMarks.vue +++ b/frontend/src/components/AccountGroupMarks.vue @@ -1,13 +1,17 @@ diff --git a/frontend/src/components/ApiKeyConfigModal.vue b/frontend/src/components/ApiKeyConfigModal.vue index e0f2b31f9..97c3c82a4 100644 --- a/frontend/src/components/ApiKeyConfigModal.vue +++ b/frontend/src/components/ApiKeyConfigModal.vue @@ -2,22 +2,26 @@ import { ZButton, ZDialog, ZIconButton, ZScrollbar, ZSegmented, ZSwitch } from '@codex-proxy/ui' import { Apple, Copy, Monitor, Upload } from '@lucide/vue' + import { computed, shallowRef } from 'vue' +import { useI18n } from 'vue-i18n' import { buildCodexCcSwitchImportDeeplink, buildCodexConfig, CODEX_WEBSOCKET_ENABLED_BY_DEFAULT } from '@/utils/client' -const props = withDefaults(defineProps<{ +const props = defineProps<{ title?: string apiKey: { name?: string key?: string } | null apiBaseUrl: string -}>(), { title: '使用密钥' }) +}>() const emit = defineEmits<{ copy: [text: string] }>() +const { t } = useI18n({ useScope: 'global' }) + const open = defineModel({ default: false }) const activePlatform = shallowRef('unix') @@ -54,16 +58,16 @@ function importToCcs() {