This commit is contained in:
decolua 2026-04-06 17:32:44 +07:00
parent 7db4b9834e
commit 307be3b63d
18 changed files with 406 additions and 179 deletions

View file

@ -4,15 +4,19 @@ import { useState, useMemo, useEffect } from "react";
import PropTypes from "prop-types";
import Modal from "./Modal";
import { getModelsByProviderId, PROVIDER_ID_TO_ALIAS } from "@/shared/constants/models";
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_TIER_PROVIDERS, isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
// Provider order: OAuth first, then Free Tier, then API Key (matches dashboard/providers)
const PROVIDER_ORDER = [
...Object.keys(OAUTH_PROVIDERS),
...Object.keys(FREE_PROVIDERS),
...Object.keys(FREE_TIER_PROVIDERS),
...Object.keys(APIKEY_PROVIDERS),
];
// Providers that need no auth — always show in model selector
const NO_AUTH_PROVIDER_IDS = Object.keys(FREE_PROVIDERS).filter(id => FREE_PROVIDERS[id].noAuth);
export default function ModelSelectModal({
isOpen,
onClose,
@ -58,7 +62,7 @@ export default function ModelSelectModal({
if (isOpen) fetchProviderNodes();
}, [isOpen]);
const allProviders = useMemo(() => ({ ...OAUTH_PROVIDERS, ...FREE_TIER_PROVIDERS, ...APIKEY_PROVIDERS }), []);
const allProviders = useMemo(() => ({ ...OAUTH_PROVIDERS, ...FREE_PROVIDERS, ...FREE_TIER_PROVIDERS, ...APIKEY_PROVIDERS }), []);
// Group models by provider with priority order
const groupedModels = useMemo(() => {
@ -70,6 +74,7 @@ export default function ModelSelectModal({
// Only show connected providers (including both standard and custom)
const providerIdsToShow = new Set([
...activeConnectionIds, // Only connected providers
...NO_AUTH_PROVIDER_IDS, // No-auth providers always visible
]);
// Sort by PROVIDER_ORDER

View file

@ -23,11 +23,11 @@ const navItems = [
const debugItems = [
{ href: "/dashboard/console-log", label: "Console Log", icon: "terminal" },
{ href: "/dashboard/translator", label: "Translator", icon: "translate" },
];
const systemItems = [
{ href: "/dashboard/proxy-pools", label: "Proxy Pools", icon: "lan" },
{ href: "/dashboard/profile", label: "Settings", icon: "settings" },
];
export default function Sidebar({ onClose }) {
@ -171,52 +171,6 @@ export default function Sidebar({ onClose }) {
</div>
)}
{/* Debug section */}
<div className="pt-4 mt-2">
<p className="px-4 text-xs font-semibold text-text-muted/60 uppercase tracking-wider mb-2">
Debug
</p>
{enableTranslator && (
<Link
href="/dashboard/translator"
onClick={onClose}
className={cn(
"flex items-center gap-3 px-4 py-2 rounded-lg transition-all group",
isActive("/dashboard/translator")
? "bg-primary/10 text-primary"
: "text-text-muted hover:bg-surface/50 hover:text-text-main"
)}
>
<span className={cn("material-symbols-outlined text-[18px]", isActive("/dashboard/translator") ? "fill-1" : "group-hover:text-primary transition-colors")}>
translate
</span>
<span className="text-sm font-medium">Translator</span>
</Link>
)}
{debugItems.map((item) => (
<Link
key={item.href}
href={item.href}
onClick={onClose}
className={cn(
"flex items-center gap-3 px-4 py-2 rounded-lg transition-all group",
isActive(item.href)
? "bg-primary/10 text-primary"
: "text-text-muted hover:bg-surface/50 hover:text-text-main"
)}
>
<span
className={cn(
"material-symbols-outlined text-[18px]",
isActive(item.href) ? "fill-1" : "group-hover:text-primary transition-colors"
)}
>
{item.icon}
</span>
<span className="text-sm font-medium">{item.label}</span>
</Link>
))}
</div>
{/* System section */}
<div className="pt-4 mt-2">
@ -246,23 +200,61 @@ export default function Sidebar({ onClose }) {
<span className="text-sm font-medium">{item.label}</span>
</Link>
))}
{/* Debug items (inside System section, before Settings) */}
{debugItems.map((item) => {
const show = item.href !== "/dashboard/translator" || enableTranslator;
return show ? (
<Link
key={item.href}
href={item.href}
onClick={onClose}
className={cn(
"flex items-center gap-3 px-4 py-2 rounded-lg transition-all group",
isActive(item.href)
? "bg-primary/10 text-primary"
: "text-text-muted hover:bg-surface/50 hover:text-text-main"
)}
>
<span
className={cn(
"material-symbols-outlined text-[18px]",
isActive(item.href) ? "fill-1" : "group-hover:text-primary transition-colors"
)}
>
{item.icon}
</span>
<span className="text-sm font-medium">{item.label}</span>
</Link>
) : null;
})}
{/* Settings */}
<Link
href="/dashboard/profile"
onClick={onClose}
className={cn(
"flex items-center gap-3 px-4 py-2 rounded-lg transition-all group",
isActive("/dashboard/profile")
? "bg-primary/10 text-primary"
: "text-text-muted hover:bg-surface/50 hover:text-text-main"
)}
>
<span
className={cn(
"material-symbols-outlined text-[18px]",
isActive("/dashboard/profile") ? "fill-1" : "group-hover:text-primary transition-colors"
)}
>
settings
</span>
<span className="text-sm font-medium">Settings</span>
</Link>
</div>
</nav>
{/* Footer section */}
<div className="p-3 border-t border-black/5 dark:border-white/5">
{/* Info message */}
<div className="flex items-start gap-2 p-2 rounded-lg bg-surface/50 mb-2">
<div className="flex items-center justify-center size-6 rounded-md bg-blue-500/10 text-blue-500 shrink-0 mt-0.5">
<span className="material-symbols-outlined text-[14px]">info</span>
</div>
<div className="flex flex-col">
<span className="text-xs font-medium text-text-main leading-relaxed">
Service is running in terminal. You can close this web page. Shutdown will stop the service.
</span>
</div>
</div>
{/* Shutdown button */}
<Button
variant="outline"