@raingor/pi-web-switch 0.4.2 → 0.4.4
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 +18 -0
- package/index.html +2 -2
- package/package.json +7 -38
- package/public/apple-touch-icon.png +0 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/manifest.webmanifest +2 -2
- package/public/pi.svg +41 -6
- package/public/sw.js +51 -51
- package/server/pi-reader.ts +353 -237
- package/src/App.tsx +2 -0
- package/src/components/dashboard/DashboardPage.tsx +341 -56
- package/src/components/layout/AppShell.tsx +45 -13
- package/src/components/layout/Sidebar.tsx +78 -85
- package/src/components/providers/ProvidersModelsPage.tsx +125 -131
- package/src/components/sessions/MemoryPage.tsx +34 -13
- package/src/components/sessions/SessionsPage.tsx +20 -40
- 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 -14
- package/src/lib/translations/ja.ts +88 -14
- package/src/lib/translations/zh-CN.ts +88 -14
- package/src/lib/translations/zh-TW.ts +87 -14
- package/src/main.tsx +97 -44
- package/src/types/index.ts +0 -1
- package/vite.config.ts +65 -164
- package/dist-electron/main/main.cjs +0 -2453
- package/src/data/mock-config.ts +0 -247
- package/src/data/mock-usage.ts +0 -151
|
@@ -7,117 +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
|
-
<div
|
|
41
|
-
className="flex items-center gap-3 border-b px-6 pt-12 pb-5"
|
|
42
|
-
style={{
|
|
43
|
-
borderColor: "var(--sidebar-border)",
|
|
44
|
-
userSelect: "none",
|
|
45
|
-
WebkitUserSelect: "none",
|
|
46
|
-
// Electron-only: lets the user drag the frameless (hiddenInset) window
|
|
47
|
-
// from the sidebar header. TypeScript doesn't know the non-standard
|
|
48
|
-
// -webkit-app-region property, so cast it explicitly.
|
|
49
|
-
...({ WebkitAppRegion: "drag" } as React.CSSProperties),
|
|
50
|
-
}}
|
|
51
|
-
>
|
|
52
|
-
<img src="/pi.svg" alt="pi-switch" className="h-9 w-9 rounded-lg" />
|
|
53
|
-
<div>
|
|
54
|
-
<h1 className="text-base font-semibold" style={{ color: "var(--page-text)" }}>pi-switch</h1>
|
|
55
|
-
<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>
|
|
56
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" />
|
|
57
59
|
</div>
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
{navItems.map(({ to, icon: Icon, key }) => (
|
|
61
|
+
<nav className="command-nav">
|
|
62
|
+
{navItems.map(({ to, icon: Icon, key, code }) => (
|
|
62
63
|
<NavLink
|
|
63
64
|
key={to}
|
|
64
65
|
to={to}
|
|
65
66
|
end={to === "/"}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors",
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
style={({ isActive }) => ({
|
|
72
|
-
backgroundColor: isActive ? "var(--sidebar-active-bg)" : "transparent",
|
|
73
|
-
color: isActive ? "var(--sidebar-active-text)" : "var(--sidebar-text)",
|
|
74
|
-
})}
|
|
67
|
+
onClick={onClose}
|
|
68
|
+
className={({ isActive }) => cn("command-nav-item", isActive && "is-active")}
|
|
75
69
|
>
|
|
76
|
-
<
|
|
77
|
-
|
|
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" />
|
|
78
74
|
</NavLink>
|
|
79
75
|
))}
|
|
80
76
|
</nav>
|
|
81
77
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
>
|
|
89
|
-
<Globe className="h-4 w-4" />
|
|
90
|
-
<span className="flex-1 text-left">{LANGUAGES.find((l) => l.code === lang)?.nativeLabel || "English"}</span>
|
|
91
|
-
</button>
|
|
92
|
-
{langOpen && (
|
|
93
|
-
<div className="mt-1 space-y-0.5 px-1">
|
|
94
|
-
{LANGUAGES.map((l) => (
|
|
95
|
-
<button
|
|
96
|
-
key={l.code}
|
|
97
|
-
onClick={() => { setLang(l.code); setLangOpen(false); }}
|
|
98
|
-
className={cn(
|
|
99
|
-
"flex w-full items-center gap-3 rounded-lg px-3 py-1.5 text-xs font-medium transition-colors",
|
|
100
|
-
lang === l.code ? "bg-blue-600/10 text-blue-400" : ""
|
|
101
|
-
)}
|
|
102
|
-
style={{
|
|
103
|
-
color: lang === l.code ? "var(--sidebar-active-text)" : "var(--sidebar-text)",
|
|
104
|
-
backgroundColor: lang === l.code ? "var(--sidebar-active-bg)" : "transparent",
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
107
|
-
{l.nativeLabel}
|
|
108
|
-
</button>
|
|
109
|
-
))}
|
|
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>
|
|
110
84
|
</div>
|
|
111
|
-
|
|
112
|
-
</div>
|
|
85
|
+
</div>
|
|
113
86
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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>
|
|
120
113
|
</div>
|
|
121
114
|
</aside>
|
|
122
115
|
);
|
|
123
|
-
}
|
|
116
|
+
}
|