@raingor/pi-web-switch 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +4 -4
- package/index.html +2 -2
- package/package.json +5 -3
- package/public/manifest.webmanifest +2 -2
- package/public/sw.js +51 -51
- package/server/pi-reader.ts +326 -19
- package/src/App.tsx +2 -0
- package/src/components/dashboard/DashboardPage.tsx +341 -27
- package/src/components/layout/AppShell.tsx +45 -13
- package/src/components/layout/Sidebar.tsx +78 -74
- package/src/components/providers/ProvidersModelsPage.tsx +125 -131
- package/src/components/sessions/MemoryPage.tsx +32 -12
- package/src/components/sessions/SessionsPage.tsx +18 -4
- package/src/components/settings/SettingsPage.tsx +6 -1
- package/src/components/speedtest/ModelSpeedTestPage.tsx +429 -0
- package/src/components/ui/EmptyState.tsx +7 -6
- package/src/components/ui/Modal.tsx +33 -25
- package/src/components/ui/StatCard.tsx +10 -13
- package/src/data/builtin-providers.test.ts +109 -0
- package/src/data/builtin-providers.ts +67 -44
- package/src/data/model-catalog.test.ts +122 -0
- package/src/data/model-catalog.ts +697 -478
- package/src/index.css +624 -210
- package/src/lib/translations/en.ts +88 -9
- package/src/lib/translations/ja.ts +88 -9
- package/src/lib/translations/zh-CN.ts +88 -9
- package/src/lib/translations/zh-TW.ts +87 -9
- package/src/main.tsx +99 -22
- package/vite.config.ts +62 -137
- package/src/data/mock-config.ts +0 -247
- package/src/data/mock-usage.ts +0 -151
|
@@ -7,106 +7,110 @@ import {
|
|
|
7
7
|
Plug,
|
|
8
8
|
Users,
|
|
9
9
|
Globe,
|
|
10
|
+
ChevronDown,
|
|
11
|
+
X,
|
|
12
|
+
Orbit,
|
|
13
|
+
Gauge,
|
|
10
14
|
} from "lucide-react";
|
|
11
15
|
import { cn } from "@/lib/utils";
|
|
12
16
|
import { useTranslation, LANGUAGES } from "@/lib/i18n";
|
|
13
17
|
import { useState } from "react";
|
|
14
18
|
|
|
15
19
|
const navItems = [
|
|
16
|
-
{ to: "/", icon: LayoutDashboard, key: "nav.dashboard" },
|
|
17
|
-
{ to: "/sessions", icon: History, key: "nav.sessions" },
|
|
18
|
-
{ to: "/memory", icon: Brain, key: "nav.memory" },
|
|
19
|
-
{ to: "/providers", icon: Plug, key: "nav.providers_models" },
|
|
20
|
-
{ to: "/subagents", icon: Users, key: "nav.subagents" },
|
|
21
|
-
{ to: "/settings", icon: Settings, key: "nav.settings" },
|
|
20
|
+
{ to: "/", icon: LayoutDashboard, key: "nav.dashboard", code: "01" },
|
|
21
|
+
{ to: "/sessions", icon: History, key: "nav.sessions", code: "02" },
|
|
22
|
+
{ to: "/memory", icon: Brain, key: "nav.memory", code: "03" },
|
|
23
|
+
{ to: "/providers", icon: Plug, key: "nav.providers_models", code: "04" },
|
|
24
|
+
{ to: "/subagents", icon: Users, key: "nav.subagents", code: "05" },
|
|
25
|
+
{ to: "/settings", icon: Settings, key: "nav.settings", code: "06" },
|
|
26
|
+
{ to: "/speed-test", icon: Gauge, key: "nav.speed_test", code: "07" },
|
|
22
27
|
];
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
interface SidebarProps {
|
|
30
|
+
mobileOpen?: boolean;
|
|
31
|
+
onClose?: () => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function Sidebar({ mobileOpen = false, onClose }: SidebarProps) {
|
|
25
35
|
const { t, lang, setLang } = useTranslation();
|
|
26
36
|
const [langOpen, setLangOpen] = useState(false);
|
|
27
37
|
|
|
28
38
|
return (
|
|
29
|
-
<aside
|
|
30
|
-
className="
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
>
|
|
41
|
-
<img src="/pi.svg" alt="pi-switch" className="h-9 w-9 rounded-lg" />
|
|
42
|
-
<div>
|
|
43
|
-
<h1 className="text-base font-semibold" style={{ color: "var(--page-text)" }}>pi-switch</h1>
|
|
44
|
-
<p className="text-xs" style={{ color: "var(--subtle-text)" }}>{t("app.subtitle")}</p>
|
|
39
|
+
<aside className={cn("command-sidebar", mobileOpen && "is-open")}>
|
|
40
|
+
<div className="sidebar-edge" aria-hidden="true" />
|
|
41
|
+
|
|
42
|
+
<div className="brand-block">
|
|
43
|
+
<div className="brand-mark">
|
|
44
|
+
<img src="/pi.svg" alt="pi-switch" />
|
|
45
|
+
<span className="brand-orbit" aria-hidden="true" />
|
|
46
|
+
</div>
|
|
47
|
+
<div className="min-w-0">
|
|
48
|
+
<div className="brand-name">pi-switch</div>
|
|
49
|
+
<div className="brand-subtitle">{t("app.subtitle")}</div>
|
|
45
50
|
</div>
|
|
51
|
+
<button className="sidebar-close" aria-label="Close navigation" onClick={onClose}>
|
|
52
|
+
<X className="h-4 w-4" />
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div className="sidebar-system-label">
|
|
57
|
+
<span>CONTROL MATRIX</span>
|
|
58
|
+
<span className="sidebar-label-line" />
|
|
46
59
|
</div>
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
{navItems.map(({ to, icon: Icon, key }) => (
|
|
61
|
+
<nav className="command-nav">
|
|
62
|
+
{navItems.map(({ to, icon: Icon, key, code }) => (
|
|
51
63
|
<NavLink
|
|
52
64
|
key={to}
|
|
53
65
|
to={to}
|
|
54
66
|
end={to === "/"}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors",
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
style={({ isActive }) => ({
|
|
61
|
-
backgroundColor: isActive ? "var(--sidebar-active-bg)" : "transparent",
|
|
62
|
-
color: isActive ? "var(--sidebar-active-text)" : "var(--sidebar-text)",
|
|
63
|
-
})}
|
|
67
|
+
onClick={onClose}
|
|
68
|
+
className={({ isActive }) => cn("command-nav-item", isActive && "is-active")}
|
|
64
69
|
>
|
|
65
|
-
<
|
|
66
|
-
|
|
70
|
+
<span className="nav-code">{code}</span>
|
|
71
|
+
<span className="nav-icon"><Icon className="h-4 w-4" /></span>
|
|
72
|
+
<span className="nav-label">{t(key)}</span>
|
|
73
|
+
<span className="nav-signal" aria-hidden="true" />
|
|
67
74
|
</NavLink>
|
|
68
75
|
))}
|
|
69
76
|
</nav>
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
>
|
|
78
|
-
<Globe className="h-4 w-4" />
|
|
79
|
-
<span className="flex-1 text-left">{LANGUAGES.find((l) => l.code === lang)?.nativeLabel || "English"}</span>
|
|
80
|
-
</button>
|
|
81
|
-
{langOpen && (
|
|
82
|
-
<div className="mt-1 space-y-0.5 px-1">
|
|
83
|
-
{LANGUAGES.map((l) => (
|
|
84
|
-
<button
|
|
85
|
-
key={l.code}
|
|
86
|
-
onClick={() => { setLang(l.code); setLangOpen(false); }}
|
|
87
|
-
className={cn(
|
|
88
|
-
"flex w-full items-center gap-3 rounded-lg px-3 py-1.5 text-xs font-medium transition-colors",
|
|
89
|
-
lang === l.code ? "bg-blue-600/10 text-blue-400" : ""
|
|
90
|
-
)}
|
|
91
|
-
style={{
|
|
92
|
-
color: lang === l.code ? "var(--sidebar-active-text)" : "var(--sidebar-text)",
|
|
93
|
-
backgroundColor: lang === l.code ? "var(--sidebar-active-bg)" : "transparent",
|
|
94
|
-
}}
|
|
95
|
-
>
|
|
96
|
-
{l.nativeLabel}
|
|
97
|
-
</button>
|
|
98
|
-
))}
|
|
78
|
+
<div className="sidebar-footer">
|
|
79
|
+
<div className="node-status">
|
|
80
|
+
<div className="node-status-icon"><Orbit className="h-4 w-4" /></div>
|
|
81
|
+
<div>
|
|
82
|
+
<span className="node-status-label">LOCAL NODE</span>
|
|
83
|
+
<span className="node-status-value"><i /> SYSTEM ONLINE</span>
|
|
99
84
|
</div>
|
|
100
|
-
|
|
101
|
-
</div>
|
|
85
|
+
</div>
|
|
102
86
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
87
|
+
<div className="language-panel">
|
|
88
|
+
<button onClick={() => setLangOpen(!langOpen)} className="language-trigger">
|
|
89
|
+
<Globe className="h-4 w-4" />
|
|
90
|
+
<span>{LANGUAGES.find((l) => l.code === lang)?.nativeLabel || "English"}</span>
|
|
91
|
+
<ChevronDown className={cn("ml-auto h-3.5 w-3.5 transition-transform", langOpen && "rotate-180")} />
|
|
92
|
+
</button>
|
|
93
|
+
{langOpen && (
|
|
94
|
+
<div className="language-menu">
|
|
95
|
+
{LANGUAGES.map((l) => (
|
|
96
|
+
<button
|
|
97
|
+
key={l.code}
|
|
98
|
+
onClick={() => { setLang(l.code); setLangOpen(false); }}
|
|
99
|
+
className={cn("language-option", lang === l.code && "is-selected")}
|
|
100
|
+
>
|
|
101
|
+
<span>{l.nativeLabel}</span>
|
|
102
|
+
{lang === l.code && <i />}
|
|
103
|
+
</button>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div className="version-strip">
|
|
110
|
+
<span>{t("app.version")}</span>
|
|
111
|
+
<span>BUILD // STABLE</span>
|
|
112
|
+
</div>
|
|
109
113
|
</div>
|
|
110
114
|
</aside>
|
|
111
115
|
);
|
|
112
|
-
}
|
|
116
|
+
}
|