Replace the ad-hoc "workspace" abstraction with a focused, URL-driven
"sidebar view" registry that implements the modern Vercel / Cloudflare
drill-in pattern: clicking a top-level entry (e.g. System Settings)
swaps the sidebar to a contextual workspace, with a `← Back to
Dashboard` affordance, instead of stacking sub-navigation in the root.
Architecture
------------
- types.ts
+ SidebarView — declarative nested view config
(id, pathPattern, parent, getNavGroups)
+ SidebarViewParent — back-navigation descriptor
+ ResolvedSidebarView — { key, view, navGroups } returned by hook
+ SidebarData — slimmed to { navGroups } only
- Workspace — removed (logo/plan never rendered)
- lib/sidebar-view-registry.ts (new, replaces workspace-registry.ts)
+ SIDEBAR_VIEWS array — single source of truth for nested views
+ resolveSidebarView(pathname)
+ getNavGroupsForPath(pathname, t) — back-compat helper for the
command palette
- config/system-settings.config.ts
Refactored to export a single SYSTEM_SETTINGS_VIEW (SidebarView)
with parent `/dashboard/overview` + label `Back to Dashboard`.
- components/sidebar-view-header.tsx (new)
Renders only the back affordance (chevron + label). Uses the
default SidebarMenuButton size so its typography matches the
nav items below; collapses gracefully into icon mode via the
existing tooltip behavior. The redundant "title + icon" row was
removed — workspace context is already carried by the nav groups.
- hooks/use-sidebar-view.ts (new)
Encapsulates view resolution and root-nav filtering:
· matched view → returns its nav groups verbatim (route-level
beforeLoad guards already enforce access);
· no match → returns root nav groups, narrowed by user
role (admin gate) and useSidebarConfig
(admin × user sidebar_modules overlay).
- components/app-sidebar.tsx
Now a thin presentation layer: reads { key, view, navGroups }
from useSidebarView() and orchestrates the view transition via
AnimatePresence + MOTION_VARIANTS.sidebarSlide (respects
prefers-reduced-motion). No logic, no role checks, no path
matching — those live in the hook.
- components/command-menu.tsx
Switched to the new getNavGroupsForPath() API; behavior preserved.
Cleanup
-------
- Deleted layout/context/workspace-context.tsx (zero consumers).
- Deleted layout/lib/workspace-registry.ts and its
workspace-registry.example.ts companion (over-abstracted: name/id
metadata, isInWorkspace / getAllWorkspaces / WORKSPACE_IDS were
registered but never read).
- Removed `workspaces` field from useSidebarData (never consumed
after the top-switcher was dropped).
- Dropped WorkspaceProvider from authenticated-layout.tsx.
- Trimmed dead `Manage and configure` translation key from all six
locale files and from static-keys.ts.
i18n
----
Added the `Back to Dashboard` key to en, zh, fr, ja, ru, vi, and
registered it in static-keys.ts under "Sidebar views".
Verification
------------
- bun run typecheck: passes
- Lint: no new warnings/errors on the touched files
- Adding a new drill-in workspace now only requires registering a
SidebarView in SIDEBAR_VIEWS — no changes to AppSidebar required.
Users reported that the API key edit drawer's "Save changes" button
becomes unresponsive after the drawer has been open / idle for a
while: no loading state, no request, no error. Reopening the drawer
restores it because a fresh DOM is created.
The button lived in `SheetFooter` (a portaled Base UI Sheet) and was
linked to the form via the HTML `form='api-key-form'` attribute. Once
the portal/DOM relationship goes stale, the click no longer triggers
the form's submit event, hence the silent failure.
Defensive fix: drop the cross-DOM `form` linkage and call
`form.handleSubmit(onSubmit)` directly via `onClick`. The native
submit path (Enter key, original `<form onSubmit>`) is preserved.
Closes#4858
The faceted filter popover used a fixed width of 200px, which clipped
long option labels (e.g. user-defined channel group names) and forced
the truncated text to be unreadable without leaving a way to see the
full value.
- Switch PopoverContent from `w-[200px]` to
`min-w-[200px] max-w-[360px]` so short option lists keep their
current footprint while long labels can expand up to 360px before
the existing truncate kicks in.
- Add `title={t(option.label)}` on the truncated label span so users
can still hover to see the full text on extreme cases.
Closes#4905
The new UI's sign-up form sent the invite code under key `aff`, but
the backend `Register` controller binds it to `User.AffCode` whose
JSON tag is `aff_code` (see model/user.go). Result: every invited
sign-up landed with `inviter_id = 0`, breaking the affiliate flow.
Rename only the request payload field so it matches the backend
contract. URL query parameter (`/sign-up?aff=...`), localStorage key
and OAuth state continue to use `aff` and are unchanged.
Closes#4945
* fix: unify color system for api-info, add slate to SemanticColor
Signed-off-by: Micah-Zheng <102610064+Micah-Zheng@users.noreply.github.com>
* fix: use direct Tailwind color classes in colorToBgClass for accurate color display
Signed-off-by: Micah-Zheng <102610064+Micah-Zheng@users.noreply.github.com>
---------
Signed-off-by: Micah-Zheng <102610064+Micah-Zheng@users.noreply.github.com>
Selected preset buttons looked identical to unselected in dark mode: the
override classes `border-foreground bg-foreground/5` carry no `dark:`
variant, while the Button `outline` variant base contains
`dark:border-input dark:bg-input/30`. tailwind-merge keeps both (different
variants → no conflict), and in dark mode CSS specificity makes
`.dark .border-input` win over `.border-foreground`, so the override is
silently overridden and the bright-border/tinted-bg selection state never
applies.
Add explicit `dark:border-foreground dark:bg-foreground/10` to the
override so tailwind-merge resolves the dark-variant conflict in favor
of the override and the selected state is clearly distinguishable on
both light and dark backgrounds.
Co-authored-by: xinnyu <xinnyu@users.noreply.github.com>
* fix(auth): localize reset password confirmation
Wrap reset confirmation page copy in frontend i18n calls and add matching locale entries so the page no longer mixes translated labels with hardcoded English copy.
* fix(auth): use semantic reset i18n keys
/api/status never returned `register_enabled` or `password_register_enabled`,
so the sign-in page had no way to react when an admin disabled registration.
The "Sign up" link was only gated on `self_use_mode_enabled`, which is a
separate and unrelated concept (single-user vs. multi-user deployment).
Result: toggling "Registration Enabled" in admin settings had no visible
effect on the login page — users could still see the sign-up link even when
registration was disabled, and could not see it even when it was enabled
(if the system happened to be in self-use mode from initial setup).
Fix:
- Add `register_enabled` and `password_register_enabled` to GetStatus()
- Gate the "Sign up" link on `register_enabled !== false` in addition to
the existing `!self_use_mode_enabled` check
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The "Send reset email" button was missing type="submit", preventing
form submission when clicked. All other auth forms (sign-in, sign-up,
OTP) already have this attribute set correctly.
Closes#4793
* chore(dev): add local setup state reset target
- add a reset-setup make target to clear setup records, root users, and related options.
- support both docker dev PostgreSQL and local SQLite development databases.
- restart the docker dev backend so setup status is recalculated after reset.
* fix(chat): prevent preset menu text overflow
- add truncation layout for chat preset names to keep long labels inside the sidebar menu.
- prevent loading and external-link icons from shrinking in constrained menu rows.
* fix(i18n): translate dashboard granularity options
- call t() for granularity option labels in dashboard system settings.
- keep localized text consistent between the select trigger and dropdown items.
* chore(dev): add backend dev service rebuild target
- add a dev-api-rebuild make target to rebuild and start the docker backend service.
- reuse DEV_COMPOSE_FILE and DEV_BACKEND_SERVICE variables to avoid repeated compose config literals.
* fix(i18n): align interface language option labels
- add shared interface language options to keep display names consistent.
- reuse the shared options in the header switcher and profile preferences.
- normalize language codes so zh-CN and zh_CN resolve to Simplified Chinese.
* fix(i18n): add missing frontend translation keys
- route channel key prompts, form validation messages, and channel fallback text through i18n.
- add missing translations across six locales for channels, rankings, billing, and logs.
- update i18n sync reports so literal t() keys are present in the base locale.
- Add singleSelect to status/role filters in API keys, users, and redemptions tables (#4880)
- Fix affiliate link 404 by changing /register to /sign-up (#4893)
- Open FetchModelsDialog in channel creation mode via customFetcher prop (#4817)
- Add TruncatedText component with tooltip for long channel names, token names, and usernames (#4877)
- Elevate forgot-password link z-index to prevent label click interception (#4898)
When proxying through another new-api instance, the upstream
X-Oneapi-Request-Id was overwriting the local one in client responses.
This adds a new `upstream_request_id` field to the logs table, captures
the upstream ID during relay, and filters it from being copied back to
the client. Frontend gains search/filter and detail display support.
* fix: theme-aware payment paths, auto-group validation, route guards, perf group filtering
- Add common.ThemeAwarePath to generate correct redirect URLs based on
active theme (default vs classic), replacing hardcoded /console/* paths
in 7 controllers and service/quota.go (#4765)
- Validate auto-group availability against getUserGroups before defaulting
form values; playground falls back to 'default' group when 'auto' is
unavailable (#4796, #4799)
- Enforce HeaderNavModules settings in rankings route (frontend + backend
API) and SidebarModulesAdmin in playground route to block direct URL
access when features are disabled (#4704, #4512)
- Filter perf_metrics API response to only include currently configured
groups, hiding stale data from deleted groups (#4790)
- Preserve query params (pay=success/fail) in /console/topup → /wallet
frontend redirect
* fix: update hero section text and localization strings for clarity
fix(ui): add onSelect compat wrapper for DropdownMenuItem
Bridges Base UI DropdownMenu with Radix-style onSelect so existing consumers work without migration.
fix(web/default): use getUserGroups for ratio display to respect GroupGroupRatio
Unifies admin/user ratio display so API key list matches the actual billing ratio.
Add a Bun script to apply and normalize AGPL copyright headers across the default frontend source files.
The script keeps headers idempotent, upgrades existing headers to the 2023-2026 QuantumNous range, and is exposed through `bun run copyright` for future maintenance.
Add a shared `performance-metrics` feature module for perf metric APIs, DTOs, and formatting, then surface global 24h model performance on the dashboard with cards and a top-model table.
Reuse the shared metrics module from pricing model details, remove duplicated perf API/formatting code from pricing, and add localized labels for the new dashboard performance UI.