From 978e1b04210a9f7d3aa93823dabcadf1d5d82a90 Mon Sep 17 00:00:00 2001 From: "R.D." Date: Sun, 29 Mar 2026 01:53:39 -0400 Subject: [PATCH] Add full sidebar visibility controls --- docs/USER_GUIDE.md | 3 +- .../settings/components/AppearanceTab.tsx | 110 ++++++++++- src/domain/types.ts | 1 + src/i18n/messages/en.json | 8 + src/i18n/messages/zh-CN.json | 8 + src/lib/db/settings.ts | 1 + src/shared/components/Sidebar.tsx | 171 +++++++----------- src/shared/constants/sidebarVisibility.ts | 144 +++++++++++++++ src/shared/validation/schemas.ts | 2 + src/shared/validation/settingsSchemas.ts | 2 + src/types/settings.ts | 4 + 11 files changed, 344 insertions(+), 110 deletions(-) create mode 100644 src/shared/constants/sidebarVisibility.ts diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 2a2a5562fc3a..f2582cf2886d 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -783,10 +783,11 @@ curl -X POST http://localhost:20128/api/db-backups/import \ ### Settings Dashboard -The settings page is organized into 5 tabs for easy navigation: +The settings page is organized into 6 tabs for easy navigation: | Tab | Contents | | -------------- | ---------------------------------------------------------------------------------------------- | +| **General** | System storage tools, appearance settings, theme controls, and per-item sidebar visibility | | **Security** | Login/Password settings, IP Access Control, API auth for `/models`, and Provider Blocking | | **Routing** | Global routing strategy (6 options), wildcard model aliases, fallback chains, combo defaults | | **Resilience** | Provider profiles, editable rate limits, circuit breaker status, policies & locked identifiers | diff --git a/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx b/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx index abe5a8320484..5afb23043729 100644 --- a/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx @@ -6,17 +6,35 @@ import { useTheme } from "@/shared/hooks/useTheme"; import useThemeStore, { COLOR_THEMES } from "@/store/themeStore"; import { cn } from "@/shared/utils/cn"; import { useTranslations } from "next-intl"; +import { + HIDDEN_SIDEBAR_ITEMS_SETTING_KEY, + SIDEBAR_SECTIONS, + SIDEBAR_SETTINGS_UPDATED_EVENT, + normalizeHiddenSidebarItems, + type HideableSidebarItemId, +} from "@/shared/constants/sidebarVisibility"; export default function AppearanceTab() { const { theme, setTheme, isDark } = useTheme(); const { colorTheme, customColor, setColorTheme, setCustomColorTheme } = useThemeStore(); const t = useTranslations("settings"); + const tSidebar = useTranslations("sidebar"); const [settings, setSettings] = useState>({}); const [loading, setLoading] = useState(true); const [customThemeColor, setCustomThemeColor] = useState(customColor || "#3b82f6"); - const isValidHex = /^#([0-9a-fA-F]{6})$/.test(customThemeColor.startsWith("#") ? customThemeColor : `#${customThemeColor}`); + const isValidHex = /^#([0-9a-fA-F]{6})$/.test( + customThemeColor.startsWith("#") ? customThemeColor : `#${customThemeColor}` + ); + const hiddenSidebarItems = normalizeHiddenSidebarItems( + settings[HIDDEN_SIDEBAR_ITEMS_SETTING_KEY] + ); + const hiddenSidebarSet = new Set(hiddenSidebarItems); + + const getSettingsLabel = (key: string, fallback: string) => + typeof t.has === "function" && t.has(key) ? t(key) : fallback; + const getSidebarLabel = (key: string, fallback: string) => + typeof tSidebar.has === "function" && tSidebar.has(key) ? tSidebar(key) : fallback; - // Subscribe to store changes (e.g. from another tab) via Zustand external subscription useEffect(() => { const unsubscribe = useThemeStore.subscribe((state) => { if (state.customColor && state.customColor !== customThemeColor) { @@ -25,6 +43,7 @@ export default function AppearanceTab() { }); return unsubscribe; }, []); // eslint-disable-line react-hooks/exhaustive-deps + const themeOptionLabels: Record = { light: t("themeLight"), dark: t("themeDark"), @@ -40,7 +59,12 @@ export default function AppearanceTab() { return res.json(); }) .then((data) => { - setSettings(data); + setSettings({ + ...data, + [HIDDEN_SIDEBAR_ITEMS_SETTING_KEY]: normalizeHiddenSidebarItems( + data[HIDDEN_SIDEBAR_ITEMS_SETTING_KEY] + ), + }); setLoading(false); }) .catch(() => setLoading(false)); @@ -54,7 +78,18 @@ export default function AppearanceTab() { body: JSON.stringify({ [key]: value }), }); if (res.ok) { - setSettings((prev) => ({ ...prev, [key]: value })); + setSettings((prev) => ({ + ...prev, + [key]: + key === HIDDEN_SIDEBAR_ITEMS_SETTING_KEY ? normalizeHiddenSidebarItems(value) : value, + })); + if (typeof window !== "undefined") { + window.dispatchEvent( + new CustomEvent(SIDEBAR_SETTINGS_UPDATED_EVENT, { + detail: { [key]: value }, + }) + ); + } } } catch (err) { console.error(`Failed to update ${key}:`, err); @@ -71,6 +106,20 @@ export default function AppearanceTab() { { id: "cyan", color: COLOR_THEMES.cyan, label: t("themeCyan") }, ]; + const sidebarSections = SIDEBAR_SECTIONS.map((section) => ({ + ...section, + title: getSidebarLabel(section.titleKey, section.titleFallback), + items: section.items.map((item) => ({ ...item, label: tSidebar(item.i18nKey) })), + })); + + const toggleSidebarItem = (itemId: HideableSidebarItemId) => { + const nextHiddenItems = hiddenSidebarSet.has(itemId) + ? hiddenSidebarItems.filter((id) => id !== itemId) + : [...hiddenSidebarItems, itemId]; + + updateSetting(HIDDEN_SIDEBAR_ITEMS_SETTING_KEY, nextHiddenItems); + }; + return (
@@ -164,10 +213,61 @@ export default function AppearanceTab() { maxLength={7} className={`flex-1 h-10 px-3 rounded-lg bg-surface border text-sm text-text-main focus:outline-none ${isValidHex ? "border-border focus:border-primary" : "border-red-400 focus:border-red-500"}`} /> - +
+
+
+

+ {getSettingsLabel("sidebarVisibility", "Hide sidebar items")} +

+

+ {getSettingsLabel( + "sidebarVisibilityDesc", + "Hide any sidebar navigation entry to reduce visual clutter without disabling any features" + )} +

+
+ +
+ {sidebarSections.map((section) => ( +
+
+

+ {section.title} +

+
+ +
+ {section.items.map((item) => ( +
+

{item.label}

+ toggleSidebarItem(item.id)} + disabled={loading} + /> +
+ ))} +
+
+ ))} +
+ +

+ {getSettingsLabel( + "sidebarVisibilityHint", + "Any sidebar section is hidden automatically when all of its entries are hidden" + )} +

+
+
diff --git a/src/domain/types.ts b/src/domain/types.ts index e3b942bb1d15..ef516fa4065d 100644 --- a/src/domain/types.ts +++ b/src/domain/types.ts @@ -101,6 +101,7 @@ * @property {string} [instanceName] - Instance display name * @property {string} [corsOrigins] - Allowed CORS origins * @property {number} [logRetentionDays] - Log retention in days + * @property {string[]} [hiddenSidebarItems] - Sidebar entry ids hidden for visual decluttering */ /** diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index c5b3d7920704..dc9105da856f 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -170,6 +170,11 @@ "debug": "Debug", "system": "System", "help": "Help", + "primarySection": "Main", + "cliSection": "CLI", + "debugSection": "Debug", + "systemSection": "System", + "helpSection": "Help", "serverDisconnected": "Server Disconnected", "serverDisconnectedMsg": "The proxy server has been stopped or is restarting.", "expandSidebar": "Expand sidebar", @@ -1724,6 +1729,9 @@ "themeLight": "Light", "themeDark": "Dark", "themeSystem": "System", + "sidebarVisibility": "Hide sidebar items", + "sidebarVisibilityDesc": "Hide any sidebar navigation entry to reduce visual clutter without disabling any features", + "sidebarVisibilityHint": "Any sidebar section is hidden automatically when all of its entries are hidden", "hideHealthLogs": "Hide Health Check Logs", "hideHealthLogsDesc": "When ON, suppress [HealthCheck] messages in server console", "themeAccent": "Theme color", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 44ea4b62cb81..400802ea98e0 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -170,6 +170,11 @@ "debug": "调试", "system": "系统", "help": "帮助", + "primarySection": "主导航", + "cliSection": "CLI", + "debugSection": "调试", + "systemSection": "系统", + "helpSection": "帮助", "serverDisconnected": "服务器已断开连接", "serverDisconnectedMsg": "代理服务器已停止,或正在重启。", "expandSidebar": "展开侧边栏", @@ -1718,6 +1723,9 @@ "themeLight": "浅色", "themeDark": "深色", "themeSystem": "系统", + "sidebarVisibility": "隐藏侧边栏项目", + "sidebarVisibilityDesc": "可以隐藏任意侧边栏导航项,以减少视觉负担,但不会禁用任何功能", + "sidebarVisibilityHint": "当某个侧边栏分组中的所有项目都被隐藏时,该分组会自动隐藏", "hideHealthLogs": "隐藏健康检查日志", "hideHealthLogsDesc": "开启后,将抑制服务器控制台中的 [HealthCheck] 消息", "themeAccent": "主题颜色", diff --git a/src/lib/db/settings.ts b/src/lib/db/settings.ts index e5ac9f6553c0..4822d08dc630 100644 --- a/src/lib/db/settings.ts +++ b/src/lib/db/settings.ts @@ -45,6 +45,7 @@ export async function getSettings() { cloudEnabled: false, stickyRoundRobinLimit: 3, requireLogin: true, + hiddenSidebarItems: [], }; for (const row of rows) { const record = toRecord(row); diff --git a/src/shared/components/Sidebar.tsx b/src/shared/components/Sidebar.tsx index 24165899dc72..fb77a3017a1e 100644 --- a/src/shared/components/Sidebar.tsx +++ b/src/shared/components/Sidebar.tsx @@ -11,46 +11,12 @@ import Button from "./Button"; import { ConfirmModal } from "./Modal"; import CloudSyncStatus from "./CloudSyncStatus"; import { useTranslations } from "next-intl"; -// Nav items use i18n keys resolved inside the component -const navItemDefs = [ - { href: "/dashboard", i18nKey: "home", icon: "home", exact: true }, - { href: "/dashboard/endpoint", i18nKey: "endpoints", icon: "api" }, - { href: "/dashboard/api-manager", i18nKey: "apiManager", icon: "vpn_key" }, - { href: "/dashboard/providers", i18nKey: "providers", icon: "dns" }, - { href: "/dashboard/combos", i18nKey: "combos", icon: "layers" }, - { href: "/dashboard/costs", i18nKey: "costs", icon: "account_balance_wallet" }, - { href: "/dashboard/analytics", i18nKey: "analytics", icon: "analytics" }, - { href: "/dashboard/limits", i18nKey: "limits", icon: "tune" }, - { href: "/dashboard/cache", i18nKey: "cache", icon: "cached" }, -]; - -const cliItemDefs = [ - { href: "/dashboard/cli-tools", i18nKey: "cliToolsShort", icon: "terminal" }, - { href: "/dashboard/agents", i18nKey: "agents", icon: "smart_toy" }, -]; - -const debugItemDefs = [ - { href: "/dashboard/translator", i18nKey: "translator", icon: "translate" }, - { href: "/dashboard/playground", i18nKey: "playground", icon: "science" }, - { href: "/dashboard/media", i18nKey: "media", icon: "auto_awesome" }, - { href: "/dashboard/search-tools", i18nKey: "searchTools", icon: "manage_search" }, -]; - -const systemItemDefs = [ - { href: "/dashboard/health", i18nKey: "health", icon: "health_and_safety" }, - { href: "/dashboard/logs", i18nKey: "logs", icon: "description" }, - { href: "/dashboard/settings", i18nKey: "settings", icon: "settings" }, -]; - -const helpItemDefs = [ - { href: "/docs", i18nKey: "docs", icon: "menu_book" }, - { - href: "https://github.com/diegosouzapw/OmniRoute/issues", - i18nKey: "issues", - icon: "bug_report", - external: true, - }, -]; +import { + HIDDEN_SIDEBAR_ITEMS_SETTING_KEY, + SIDEBAR_SETTINGS_UPDATED_EVENT, + SIDEBAR_SECTIONS, + normalizeHiddenSidebarItems, +} from "@/shared/constants/sidebarVisibility"; export default function Sidebar({ onClose, @@ -70,13 +36,41 @@ export default function Sidebar({ const [isRestarting, setIsRestarting] = useState(false); const [isDisconnected, setIsDisconnected] = useState(false); const [showDebug, setShowDebug] = useState(false); + const [hiddenSidebarItems, setHiddenSidebarItems] = useState([]); - // Check if debug mode is enabled useEffect(() => { + const applySettings = (data) => { + setShowDebug(data?.enableRequestLogs === true); + setHiddenSidebarItems(normalizeHiddenSidebarItems(data?.[HIDDEN_SIDEBAR_ITEMS_SETTING_KEY])); + }; + fetch("/api/settings") .then((res) => res.json()) - .then((data) => setShowDebug(data?.enableRequestLogs === true)) + .then((data) => applySettings(data)) .catch(() => {}); + + const handleSettingsUpdated = (event: Event) => { + const detail = (event as CustomEvent>).detail || {}; + + if ("enableRequestLogs" in detail) { + setShowDebug(detail.enableRequestLogs === true); + } + + if (HIDDEN_SIDEBAR_ITEMS_SETTING_KEY in detail) { + setHiddenSidebarItems( + normalizeHiddenSidebarItems(detail[HIDDEN_SIDEBAR_ITEMS_SETTING_KEY]) + ); + } + }; + + window.addEventListener(SIDEBAR_SETTINGS_UPDATED_EVENT, handleSettingsUpdated as EventListener); + + return () => { + window.removeEventListener( + SIDEBAR_SETTINGS_UPDATED_EVENT, + handleSettingsUpdated as EventListener + ); + }; }, []); const isActive = (href, exact) => { @@ -107,20 +101,27 @@ export default function Sidebar({ } setIsRestarting(false); setShowRestartModal(false); - // Show reconnecting state, then try to reload after a delay setIsDisconnected(true); setTimeout(() => { globalThis.location.reload(); }, 3000); }; - // Resolve i18n keys → labels - const resolveItems = (defs) => defs.map((d) => ({ ...d, label: t(d.i18nKey) })); - const navItems = resolveItems(navItemDefs); - const cliItems = resolveItems(cliItemDefs); - const debugItems = resolveItems(debugItemDefs); - const systemItems = resolveItems(systemItemDefs); - const helpItems = resolveItems(helpItemDefs); + const getSidebarLabel = (key: string, fallback: string) => + typeof t.has === "function" && t.has(key) ? t(key) : fallback; + + const hiddenSidebarSet = new Set(hiddenSidebarItems); + const visibleSections = SIDEBAR_SECTIONS.filter( + (section) => section.visibility !== "debug" || showDebug + ) + .map((section) => ({ + ...section, + title: getSidebarLabel(section.titleKey, section.titleFallback), + items: section.items + .map((item) => ({ ...item, label: t(item.i18nKey) })) + .filter((item) => !hiddenSidebarSet.has(item.id)), + })) + .filter((section) => section.items.length > 0); const renderNavLink = (item) => { const active = !item.external && isActive(item.href, item.exact); @@ -179,14 +180,12 @@ export default function Sidebar({ collapsed ? "w-16" : "w-72" )} > - {/* Skip to content link */} Skip to content - {/* Traffic lights + collapse toggle */}
- {/* Logo */}
- {/* Navigation */} - {/* Cloud sync status indicator */} - {/* Footer — Shutdown + Restart */}
- {/* Shutdown Confirmation Modal */} setShowShutdownModal(false)} @@ -342,7 +307,6 @@ export default function Sidebar({ loading={isShuttingDown} /> - {/* Restart Confirmation Modal */} setShowRestartModal(false)} @@ -355,7 +319,6 @@ export default function Sidebar({ loading={isRestarting} /> - {/* Disconnected Overlay */} {isDisconnected && (
diff --git a/src/shared/constants/sidebarVisibility.ts b/src/shared/constants/sidebarVisibility.ts new file mode 100644 index 000000000000..fa73e228d748 --- /dev/null +++ b/src/shared/constants/sidebarVisibility.ts @@ -0,0 +1,144 @@ +export const HIDEABLE_SIDEBAR_ITEM_IDS = [ + "home", + "endpoints", + "api-manager", + "providers", + "combos", + "costs", + "analytics", + "limits", + "cache", + "cli-tools", + "agents", + "translator", + "playground", + "media", + "search-tools", + "health", + "logs", + "settings", + "docs", + "issues", +] as const; + +export type HideableSidebarItemId = (typeof HIDEABLE_SIDEBAR_ITEM_IDS)[number]; +export type SidebarSectionId = "primary" | "cli" | "debug" | "system" | "help"; + +export interface SidebarItemDefinition { + id: HideableSidebarItemId; + href: string; + i18nKey: string; + icon: string; + exact?: boolean; + external?: boolean; +} + +export interface SidebarSectionDefinition { + id: SidebarSectionId; + titleKey: string; + titleFallback: string; + items: readonly SidebarItemDefinition[]; + showTitleInSidebar?: boolean; + visibility?: "always" | "debug"; +} + +const PRIMARY_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [ + { id: "home", href: "/dashboard", i18nKey: "home", icon: "home", exact: true }, + { id: "endpoints", href: "/dashboard/endpoint", i18nKey: "endpoints", icon: "api" }, + { id: "api-manager", href: "/dashboard/api-manager", i18nKey: "apiManager", icon: "vpn_key" }, + { id: "providers", href: "/dashboard/providers", i18nKey: "providers", icon: "dns" }, + { id: "combos", href: "/dashboard/combos", i18nKey: "combos", icon: "layers" }, + { id: "costs", href: "/dashboard/costs", i18nKey: "costs", icon: "account_balance_wallet" }, + { id: "analytics", href: "/dashboard/analytics", i18nKey: "analytics", icon: "analytics" }, + { id: "limits", href: "/dashboard/limits", i18nKey: "limits", icon: "tune" }, + { id: "cache", href: "/dashboard/cache", i18nKey: "cache", icon: "cached" }, +]; + +const CLI_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [ + { id: "cli-tools", href: "/dashboard/cli-tools", i18nKey: "cliToolsShort", icon: "terminal" }, + { id: "agents", href: "/dashboard/agents", i18nKey: "agents", icon: "smart_toy" }, +]; + +const DEBUG_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [ + { id: "translator", href: "/dashboard/translator", i18nKey: "translator", icon: "translate" }, + { id: "playground", href: "/dashboard/playground", i18nKey: "playground", icon: "science" }, + { id: "media", href: "/dashboard/media", i18nKey: "media", icon: "auto_awesome" }, + { + id: "search-tools", + href: "/dashboard/search-tools", + i18nKey: "searchTools", + icon: "manage_search", + }, +]; + +const SYSTEM_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [ + { id: "health", href: "/dashboard/health", i18nKey: "health", icon: "health_and_safety" }, + { id: "logs", href: "/dashboard/logs", i18nKey: "logs", icon: "description" }, + { id: "settings", href: "/dashboard/settings", i18nKey: "settings", icon: "settings" }, +]; + +const HELP_SIDEBAR_ITEMS: readonly SidebarItemDefinition[] = [ + { id: "docs", href: "/docs", i18nKey: "docs", icon: "menu_book", external: true }, + { + id: "issues", + href: "https://github.com/diegosouzapw/OmniRoute/issues", + i18nKey: "issues", + icon: "bug_report", + external: true, + }, +]; + +export const SIDEBAR_SECTIONS: readonly SidebarSectionDefinition[] = [ + { + id: "primary", + titleKey: "primarySection", + titleFallback: "Main", + items: PRIMARY_SIDEBAR_ITEMS, + showTitleInSidebar: false, + }, + { + id: "cli", + titleKey: "cliSection", + titleFallback: "CLI", + items: CLI_SIDEBAR_ITEMS, + }, + { + id: "debug", + titleKey: "debugSection", + titleFallback: "Debug", + items: DEBUG_SIDEBAR_ITEMS, + visibility: "debug", + }, + { + id: "system", + titleKey: "systemSection", + titleFallback: "System", + items: SYSTEM_SIDEBAR_ITEMS, + }, + { + id: "help", + titleKey: "helpSection", + titleFallback: "Help", + items: HELP_SIDEBAR_ITEMS, + }, +] as const; + +export const HIDDEN_SIDEBAR_ITEMS_SETTING_KEY = "hiddenSidebarItems"; +export const SIDEBAR_SETTINGS_UPDATED_EVENT = "omniroute:settings-updated"; + +export function normalizeHiddenSidebarItems(value: unknown): HideableSidebarItemId[] { + if (!Array.isArray(value)) return []; + + const hiddenItems = new Set(); + + for (const item of value) { + if ( + typeof item === "string" && + HIDEABLE_SIDEBAR_ITEM_IDS.includes(item as HideableSidebarItemId) + ) { + hiddenItems.add(item as HideableSidebarItemId); + } + } + + return HIDEABLE_SIDEBAR_ITEM_IDS.filter((item) => hiddenItems.has(item)); +} diff --git a/src/shared/validation/schemas.ts b/src/shared/validation/schemas.ts index 0c32f398c69d..6af6e4a2956f 100644 --- a/src/shared/validation/schemas.ts +++ b/src/shared/validation/schemas.ts @@ -1,4 +1,5 @@ import { z } from "zod"; +import { HIDEABLE_SIDEBAR_ITEM_IDS } from "@/shared/constants/sidebarVisibility"; import { isForbiddenUpstreamHeaderName } from "@/shared/constants/upstreamHeaders"; function isHttpUrl(value: string): boolean { @@ -159,6 +160,7 @@ export const updateSettingsSchema = z.object({ requireAuthForModels: z.boolean().optional(), blockedProviders: z.array(z.string().max(100)).optional(), hideHealthCheckLogs: z.boolean().optional(), + hiddenSidebarItems: z.array(z.enum(HIDEABLE_SIDEBAR_ITEM_IDS)).optional(), // Routing settings (#134) fallbackStrategy: z .enum([ diff --git a/src/shared/validation/settingsSchemas.ts b/src/shared/validation/settingsSchemas.ts index 33092bb7f70a..ec3392f7927c 100644 --- a/src/shared/validation/settingsSchemas.ts +++ b/src/shared/validation/settingsSchemas.ts @@ -6,6 +6,7 @@ * at runtime (see: https://github.com/vercel/next.js/issues/12557). */ import { z } from "zod"; +import { HIDEABLE_SIDEBAR_ITEM_IDS } from "@/shared/constants/sidebarVisibility"; export const updateSettingsSchema = z.object({ newPassword: z.string().min(1).max(200).optional(), @@ -25,6 +26,7 @@ export const updateSettingsSchema = z.object({ requireAuthForModels: z.boolean().optional(), blockedProviders: z.array(z.string().max(100)).optional(), hideHealthCheckLogs: z.boolean().optional(), + hiddenSidebarItems: z.array(z.enum(HIDEABLE_SIDEBAR_ITEM_IDS)).optional(), // Routing settings (#134) fallbackStrategy: z .enum(["fill-first", "round-robin", "p2c", "random", "least-used", "cost-optimized"]) diff --git a/src/types/settings.ts b/src/types/settings.ts index 188405672a99..15f84d3a233a 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -1,3 +1,5 @@ +import type { HideableSidebarItemId } from "@/shared/constants/sidebarVisibility"; + /** * Application settings stored in SQLite key-value pairs. */ @@ -14,6 +16,8 @@ export interface Settings { | "strict-random"; stickyRoundRobinLimit: number; jwtSecret?: string; + hideHealthCheckLogs?: boolean; + hiddenSidebarItems?: HideableSidebarItemId[]; } export interface ComboDefaults {