@raingor/pi-web-switch 0.2.0 → 0.3.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/index.html +5 -1
- package/package.json +19 -6
- package/promote/dashboard.png +0 -0
- 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 +31 -0
- package/public/sw.js +81 -0
- package/src/App.tsx +0 -4
- package/src/components/dashboard/DashboardPage.tsx +30 -17
- package/src/components/layout/Sidebar.tsx +0 -4
- package/src/main.tsx +10 -1
- package/src/store/config-store.ts +5 -1
package/index.html
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<title>pi-switch — pi Configuration Manager</title>
|
|
7
7
|
<link rel="icon" type="image/svg+xml" href="/pi.svg" />
|
|
8
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
9
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
10
|
+
<meta name="theme-color" content="#2563eb" />
|
|
11
|
+
<meta name="description" content="Web UI for pi coding agent — configuration management, session browser, and usage dashboard" />
|
|
8
12
|
</head>
|
|
9
13
|
<body class="bg-gray-950 text-gray-100 antialiased">
|
|
10
14
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raingor/pi-web-switch",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "pi-package/index.ts",
|
|
6
7
|
"description": "Web UI for pi coding agent — live configuration management, session browser, and memory viewer",
|
|
7
|
-
"keywords": [
|
|
8
|
+
"keywords": [
|
|
9
|
+
"pi-package",
|
|
10
|
+
"pi",
|
|
11
|
+
"pi-coding-agent",
|
|
12
|
+
"dashboard",
|
|
13
|
+
"configuration",
|
|
14
|
+
"web-ui"
|
|
15
|
+
],
|
|
8
16
|
"author": "Raingor",
|
|
9
17
|
"license": "MIT",
|
|
10
18
|
"repository": {
|
|
@@ -21,8 +29,12 @@
|
|
|
21
29
|
"preview": "vite preview"
|
|
22
30
|
},
|
|
23
31
|
"pi": {
|
|
24
|
-
"extensions": [
|
|
25
|
-
|
|
32
|
+
"extensions": [
|
|
33
|
+
"./pi-package/index.ts"
|
|
34
|
+
],
|
|
35
|
+
"skills": [
|
|
36
|
+
"./pi-package/skills"
|
|
37
|
+
]
|
|
26
38
|
},
|
|
27
39
|
"dependencies": {
|
|
28
40
|
"lucide-react": "^0.487.0",
|
|
@@ -39,6 +51,7 @@
|
|
|
39
51
|
"@vitejs/plugin-react": "^4.4.1",
|
|
40
52
|
"tailwindcss": "^4.1.4",
|
|
41
53
|
"typescript": "~5.8.3",
|
|
42
|
-
"vite": "^6.3.2"
|
|
54
|
+
"vite": "^6.3.2",
|
|
55
|
+
"vite-plugin-pwa": "^1.3.0"
|
|
43
56
|
}
|
|
44
|
-
}
|
|
57
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-web-switch",
|
|
3
|
+
"short_name": "pi-switch",
|
|
4
|
+
"description": "Web UI for pi coding agent — configuration management, session browser, and usage dashboard",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"scope": "/",
|
|
7
|
+
"display": "standalone",
|
|
8
|
+
"orientation": "any",
|
|
9
|
+
"background_color": "#0a0a0a",
|
|
10
|
+
"theme_color": "#2563eb",
|
|
11
|
+
"icons": [
|
|
12
|
+
{
|
|
13
|
+
"src": "/icon-192.png",
|
|
14
|
+
"sizes": "192x192",
|
|
15
|
+
"type": "image/png",
|
|
16
|
+
"purpose": "any"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"src": "/icon-512.png",
|
|
20
|
+
"sizes": "512x512",
|
|
21
|
+
"type": "image/png",
|
|
22
|
+
"purpose": "any"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"src": "/icon-512.png",
|
|
26
|
+
"sizes": "512x512",
|
|
27
|
+
"type": "image/png",
|
|
28
|
+
"purpose": "maskable"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
package/public/sw.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// ─── pi-web-switch Service Worker ───────────────────────
|
|
2
|
+
// Cache-first for static assets, network-first for API calls.
|
|
3
|
+
// Bump CACHE_VERSION to invalidate old caches on deploy.
|
|
4
|
+
|
|
5
|
+
const CACHE_VERSION = "pi-web-switch-v1";
|
|
6
|
+
const STATIC_CACHE = `${CACHE_VERSION}-static`;
|
|
7
|
+
const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`;
|
|
8
|
+
|
|
9
|
+
const PRECACHE_URLS = [
|
|
10
|
+
"/",
|
|
11
|
+
"/index.html",
|
|
12
|
+
"/manifest.webmanifest",
|
|
13
|
+
"/icon-192.png",
|
|
14
|
+
"/icon-512.png",
|
|
15
|
+
"/apple-touch-icon.png",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// ─── Install: precache core assets ─────────────────────
|
|
19
|
+
self.addEventListener("install", (event) => {
|
|
20
|
+
event.waitUntil(
|
|
21
|
+
caches
|
|
22
|
+
.open(STATIC_CACHE)
|
|
23
|
+
.then((cache) => cache.addAll(PRECACHE_URLS))
|
|
24
|
+
.then(() => self.skipWaiting())
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ─── Activate: clean old caches ────────────────────────
|
|
29
|
+
self.addEventListener("activate", (event) => {
|
|
30
|
+
event.waitUntil(
|
|
31
|
+
caches
|
|
32
|
+
.keys()
|
|
33
|
+
.then((keys) =>
|
|
34
|
+
Promise.all(
|
|
35
|
+
keys
|
|
36
|
+
.filter((k) => !k.startsWith(CACHE_VERSION))
|
|
37
|
+
.map((k) => caches.delete(k))
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
.then(() => self.clients.claim())
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ─── Fetch: strategy by request type ───────────────────
|
|
45
|
+
self.addEventListener("fetch", (event) => {
|
|
46
|
+
const { request } = event;
|
|
47
|
+
|
|
48
|
+
// Only handle GET; ignore cross-origin and chrome-extension.
|
|
49
|
+
if (request.method !== "GET") return;
|
|
50
|
+
const url = new URL(request.url);
|
|
51
|
+
if (url.origin !== self.location.origin) return;
|
|
52
|
+
|
|
53
|
+
// API calls: network-first, fallback to cache.
|
|
54
|
+
if (url.pathname.startsWith("/api/")) {
|
|
55
|
+
event.respondWith(
|
|
56
|
+
fetch(request)
|
|
57
|
+
.then((response) => {
|
|
58
|
+
const copy = response.clone();
|
|
59
|
+
caches.open(RUNTIME_CACHE).then((cache) => cache.put(request, copy));
|
|
60
|
+
return response;
|
|
61
|
+
})
|
|
62
|
+
.catch(() => caches.match(request))
|
|
63
|
+
);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Static assets: cache-first, then network (and cache the result).
|
|
68
|
+
event.respondWith(
|
|
69
|
+
caches.match(request).then((cached) => {
|
|
70
|
+
if (cached) return cached;
|
|
71
|
+
return fetch(request).then((response) => {
|
|
72
|
+
if (!response || response.status !== 200 || response.type !== "basic") {
|
|
73
|
+
return response;
|
|
74
|
+
}
|
|
75
|
+
const copy = response.clone();
|
|
76
|
+
caches.open(RUNTIME_CACHE).then((cache) => cache.put(request, copy));
|
|
77
|
+
return response;
|
|
78
|
+
});
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
});
|
package/src/App.tsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
|
2
2
|
import { AppShell } from "@/components/layout/AppShell";
|
|
3
3
|
import { DashboardPage } from "@/components/dashboard/DashboardPage";
|
|
4
|
-
import { ModelsPage } from "@/components/models/ModelsPage";
|
|
5
|
-
import { ProvidersPage } from "@/components/providers/ProvidersPage";
|
|
6
4
|
import { SessionsPage } from "@/components/sessions/SessionsPage";
|
|
7
5
|
import { MemoryPage } from "@/components/sessions/MemoryPage";
|
|
8
6
|
import { SettingsPage } from "@/components/settings/SettingsPage";
|
|
@@ -13,8 +11,6 @@ export default function App() {
|
|
|
13
11
|
<Routes>
|
|
14
12
|
<Route element={<AppShell />}>
|
|
15
13
|
<Route path="/" element={<DashboardPage />} />
|
|
16
|
-
<Route path="/models" element={<ModelsPage />} />
|
|
17
|
-
<Route path="/providers" element={<ProvidersPage />} />
|
|
18
14
|
<Route path="/sessions" element={<SessionsPage />} />
|
|
19
15
|
<Route path="/memory" element={<MemoryPage />} />
|
|
20
16
|
<Route path="/settings" element={<SettingsPage />} />
|
|
@@ -99,7 +99,20 @@ const CHART_LABELS: Record<string, string> = {
|
|
|
99
99
|
|
|
100
100
|
// ─── Helpers ────────────────────────────────────────────
|
|
101
101
|
|
|
102
|
-
function formatTokensShort(n: number): string {
|
|
102
|
+
function formatTokensShort(n: number, lang: string = "en"): string {
|
|
103
|
+
// 中文:亿 / 万
|
|
104
|
+
if (lang.startsWith("zh")) {
|
|
105
|
+
if (n >= 100_000_000) return `${(n / 100_000_000).toFixed(2)}亿`;
|
|
106
|
+
if (n >= 10_000) return `${(n / 10_000).toFixed(1)}万`;
|
|
107
|
+
return n.toLocaleString("zh-CN");
|
|
108
|
+
}
|
|
109
|
+
// 日文:億 / 万
|
|
110
|
+
if (lang.startsWith("ja")) {
|
|
111
|
+
if (n >= 100_000_000) return `${(n / 100_000_000).toFixed(2)}億`;
|
|
112
|
+
if (n >= 10_000) return `${(n / 10_000).toFixed(1)}万`;
|
|
113
|
+
return n.toLocaleString("ja-JP");
|
|
114
|
+
}
|
|
115
|
+
// 英文:M / K
|
|
103
116
|
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
104
117
|
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
|
|
105
118
|
return n.toString();
|
|
@@ -183,7 +196,7 @@ function StatCard({
|
|
|
183
196
|
|
|
184
197
|
// ─── Breakdown Row ──────────────────────────────────────
|
|
185
198
|
|
|
186
|
-
function BreakdownRow({ label, value, total, color }: { label: string; value: number; total: number; color: string }) {
|
|
199
|
+
function BreakdownRow({ label, value, total, color, lang = "en" }: { label: string; value: number; total: number; color: string; lang?: string }) {
|
|
187
200
|
const pct = total > 0 ? (value / total) * 100 : 0;
|
|
188
201
|
return (
|
|
189
202
|
<div className="flex items-center justify-between py-1.5">
|
|
@@ -192,7 +205,7 @@ function BreakdownRow({ label, value, total, color }: { label: string; value: nu
|
|
|
192
205
|
<span className="text-xs" style={{ color: "var(--muted-text)" }}>{label}</span>
|
|
193
206
|
</div>
|
|
194
207
|
<div className="flex items-center gap-2">
|
|
195
|
-
<span className="text-xs font-medium" style={{ color: "var(--page-text)" }}>{formatTokensShort(value)}</span>
|
|
208
|
+
<span className="text-xs font-medium" style={{ color: "var(--page-text)" }}>{formatTokensShort(value, lang)}</span>
|
|
196
209
|
<span className="text-xs" style={{ color: "var(--subtle-text)" }}>({pct.toFixed(1)}%)</span>
|
|
197
210
|
</div>
|
|
198
211
|
</div>
|
|
@@ -202,7 +215,7 @@ function BreakdownRow({ label, value, total, color }: { label: string; value: nu
|
|
|
202
215
|
// ─── Main Component ─────────────────────────────────────
|
|
203
216
|
|
|
204
217
|
export function DashboardPage() {
|
|
205
|
-
const { t } = useTranslation();
|
|
218
|
+
const { t, lang } = useTranslation();
|
|
206
219
|
const { currency, toggle: toggleCurrency } = useCurrency();
|
|
207
220
|
const { initialized } = useConfigStore();
|
|
208
221
|
const [range, setRange] = useState<RangeKey>("today");
|
|
@@ -389,13 +402,13 @@ export function DashboardPage() {
|
|
|
389
402
|
title={t("dashboard.total_tokens")}
|
|
390
403
|
value={data.totalTokens.toLocaleString("en-US")}
|
|
391
404
|
icon={<Activity className="h-4 w-4" style={{ color: "#3b82f6" }} />}
|
|
392
|
-
subtitle={`≈ ${formatTokensShort(data.totalTokens)}`}
|
|
405
|
+
subtitle={`≈ ${formatTokensShort(data.totalTokens, lang)}`}
|
|
393
406
|
>
|
|
394
407
|
<div className="mt-3 space-y-0.5 border-t pt-3" style={{ borderColor: "var(--card-border)" }}>
|
|
395
|
-
<BreakdownRow label={t("dashboard.input")} value={data.totalInput} total={data.totalTokens} color="#3b82f6" />
|
|
396
|
-
<BreakdownRow label={t("dashboard.output")} value={data.totalOutput} total={data.totalTokens} color="#10b981" />
|
|
397
|
-
<BreakdownRow label={t("dashboard.cache_create")} value={data.totalCacheWrite} total={data.totalTokens} color="#f59e0b" />
|
|
398
|
-
<BreakdownRow label={t("dashboard.cache_hit")} value={data.totalCacheRead} total={data.totalTokens} color="#8b5cf6" />
|
|
408
|
+
<BreakdownRow label={t("dashboard.input")} value={data.totalInput} total={data.totalTokens} color="#3b82f6" lang={lang} />
|
|
409
|
+
<BreakdownRow label={t("dashboard.output")} value={data.totalOutput} total={data.totalTokens} color="#10b981" lang={lang} />
|
|
410
|
+
<BreakdownRow label={t("dashboard.cache_create")} value={data.totalCacheWrite} total={data.totalTokens} color="#f59e0b" lang={lang} />
|
|
411
|
+
<BreakdownRow label={t("dashboard.cache_hit")} value={data.totalCacheRead} total={data.totalTokens} color="#8b5cf6" lang={lang} />
|
|
399
412
|
</div>
|
|
400
413
|
</StatCard>
|
|
401
414
|
|
|
@@ -509,8 +522,8 @@ export function DashboardPage() {
|
|
|
509
522
|
<td className="px-4 py-2.5 whitespace-nowrap" style={{ color: "var(--page-text)" }}>{r.timestamp}</td>
|
|
510
523
|
<td className="px-4 py-2.5" style={{ color: "var(--page-text)" }}>{r.providerId}</td>
|
|
511
524
|
<td className="px-4 py-2.5" style={{ color: "var(--page-text)" }}>{r.modelId}</td>
|
|
512
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(r.input)}</td>
|
|
513
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(r.output)}</td>
|
|
525
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(r.input, lang)}</td>
|
|
526
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(r.output, lang)}</td>
|
|
514
527
|
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{currency === "CNY" ? `¥${(r.cost * USD_TO_CNY).toFixed(4)}` : formatCostShort(r.cost)}</td>
|
|
515
528
|
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{r.requests}</td>
|
|
516
529
|
<td className="px-4 py-2.5 text-center">
|
|
@@ -545,9 +558,9 @@ export function DashboardPage() {
|
|
|
545
558
|
data.providerStats.map((p, i) => (
|
|
546
559
|
<tr key={i} className="border-b" style={{ borderColor: "var(--card-border)" }}>
|
|
547
560
|
<td className="px-4 py-2.5 font-medium" style={{ color: "var(--page-text)" }}>{p.providerId}</td>
|
|
548
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(p.totalTokens)}</td>
|
|
549
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(p.totalInput)}</td>
|
|
550
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(p.totalOutput)}</td>
|
|
561
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(p.totalTokens, lang)}</td>
|
|
562
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(p.totalInput, lang)}</td>
|
|
563
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(p.totalOutput, lang)}</td>
|
|
551
564
|
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{currency === "CNY" ? `¥${(p.totalCost * USD_TO_CNY).toFixed(4)}` : formatCostShort(p.totalCost)}</td>
|
|
552
565
|
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{p.totalRequests}</td>
|
|
553
566
|
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{p.modelCount}</td>
|
|
@@ -579,9 +592,9 @@ export function DashboardPage() {
|
|
|
579
592
|
<tr key={i} className="border-b" style={{ borderColor: "var(--card-border)" }}>
|
|
580
593
|
<td className="px-4 py-2.5 font-medium" style={{ color: "var(--page-text)" }}>{m.modelId}</td>
|
|
581
594
|
<td className="px-4 py-2.5" style={{ color: "var(--page-text)" }}>{m.providerId}</td>
|
|
582
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(m.totalTokens)}</td>
|
|
583
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(m.totalInput)}</td>
|
|
584
|
-
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(m.totalOutput)}</td>
|
|
595
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(m.totalTokens, lang)}</td>
|
|
596
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(m.totalInput, lang)}</td>
|
|
597
|
+
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{formatTokensShort(m.totalOutput, lang)}</td>
|
|
585
598
|
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{currency === "CNY" ? `¥${(m.totalCost * USD_TO_CNY).toFixed(4)}` : formatCostShort(m.totalCost)}</td>
|
|
586
599
|
<td className="px-4 py-2.5 text-right font-mono" style={{ color: "var(--page-text)" }}>{m.totalRequests}</td>
|
|
587
600
|
</tr>
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { NavLink } from "react-router-dom";
|
|
2
2
|
import {
|
|
3
3
|
LayoutDashboard,
|
|
4
|
-
Box,
|
|
5
|
-
Plug,
|
|
6
4
|
Settings,
|
|
7
5
|
Pi,
|
|
8
6
|
History,
|
|
@@ -15,8 +13,6 @@ import { useState } from "react";
|
|
|
15
13
|
|
|
16
14
|
const navItems = [
|
|
17
15
|
{ to: "/", icon: LayoutDashboard, key: "nav.dashboard" },
|
|
18
|
-
{ to: "/models", icon: Box, key: "nav.models" },
|
|
19
|
-
{ to: "/providers", icon: Plug, key: "nav.providers" },
|
|
20
16
|
{ to: "/sessions", icon: History, key: "nav.sessions" },
|
|
21
17
|
{ to: "/memory", icon: Brain, key: "nav.memory" },
|
|
22
18
|
{ to: "/settings", icon: Settings, key: "nav.settings" },
|
package/src/main.tsx
CHANGED
|
@@ -103,4 +103,13 @@ createRoot(document.getElementById("root")!).render(
|
|
|
103
103
|
<StrictMode>
|
|
104
104
|
<Root />
|
|
105
105
|
</StrictMode>
|
|
106
|
-
);
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// ─── PWA: register service worker ──────────────────────
|
|
109
|
+
if ("serviceWorker" in navigator) {
|
|
110
|
+
window.addEventListener("load", () => {
|
|
111
|
+
navigator.serviceWorker.register("/sw.js").catch((err) => {
|
|
112
|
+
console.warn("SW registration failed:", err);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
@@ -421,7 +421,11 @@ export const useConfigStore = create<ConfigState>((set, get) => ({
|
|
|
421
421
|
if (ok) {
|
|
422
422
|
set({ modelsJson: updated });
|
|
423
423
|
const { auth } = get();
|
|
424
|
-
|
|
424
|
+
const newAllProviders = mergeProviders(auth ?? {}, updated);
|
|
425
|
+
const newAllModels = newAllProviders.flatMap((p) =>
|
|
426
|
+
p.models.map((m) => ({ ...m, providerId: p.id, providerName: p.name }))
|
|
427
|
+
);
|
|
428
|
+
set({ allProviders: newAllProviders, allModels: newAllModels });
|
|
425
429
|
}
|
|
426
430
|
},
|
|
427
431
|
|