@mindstudio-ai/local-model-tunnel 0.3.2 → 0.3.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/{chunk-V3RKCMCQ.js → chunk-PPP2FEIN.js} +42 -77
- package/dist/chunk-PPP2FEIN.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +1 -1
- package/dist/{tui-YFUZJIGF.js → tui-4ZB4JAV4.js} +309 -410
- package/dist/tui-4ZB4JAV4.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-V3RKCMCQ.js.map +0 -1
- package/dist/tui-YFUZJIGF.js.map +0 -1
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TunnelRunner,
|
|
3
3
|
detectAllProviderStatuses,
|
|
4
|
-
discoverAllModels,
|
|
5
4
|
discoverAllModelsWithParameters,
|
|
6
5
|
getApiKey,
|
|
7
6
|
getConfigPath,
|
|
8
7
|
getEnvironment,
|
|
9
|
-
getProviderStatuses,
|
|
10
8
|
getSyncedModels,
|
|
11
9
|
pollDeviceAuth,
|
|
12
10
|
requestDeviceAuth,
|
|
13
11
|
requestEvents,
|
|
14
12
|
setApiKey,
|
|
15
|
-
|
|
16
|
-
updateLocalModel,
|
|
13
|
+
syncModels,
|
|
17
14
|
verifyApiKey
|
|
18
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-PPP2FEIN.js";
|
|
19
16
|
|
|
20
17
|
// src/tui/index.tsx
|
|
21
18
|
import { render } from "ink";
|
|
22
19
|
import { execFileSync, execSync } from "child_process";
|
|
23
20
|
|
|
24
21
|
// src/tui/App.tsx
|
|
25
|
-
import { useEffect as
|
|
26
|
-
import { Box as
|
|
22
|
+
import { useEffect as useEffect10, useCallback as useCallback8, useState as useState10, useRef as useRef5 } from "react";
|
|
23
|
+
import { Box as Box7, useApp, useStdout as useStdout6 } from "ink";
|
|
27
24
|
|
|
28
25
|
// src/tui/components/Header.tsx
|
|
29
26
|
import os from "os";
|
|
@@ -32,7 +29,7 @@ import { createRequire } from "module";
|
|
|
32
29
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
33
30
|
var require2 = createRequire(import.meta.url);
|
|
34
31
|
var pkg = require2("../package.json");
|
|
35
|
-
var LogoString = `
|
|
32
|
+
var LogoString = ` .=+-. :++.
|
|
36
33
|
*@@@@@+ :%@@@@%:
|
|
37
34
|
.%@@@@@@#..@@@@@@@=
|
|
38
35
|
.*@@@@@@@--@@@@@@@#.**.
|
|
@@ -59,7 +56,8 @@ function Header({
|
|
|
59
56
|
connection,
|
|
60
57
|
environment,
|
|
61
58
|
configPath,
|
|
62
|
-
connectionError
|
|
59
|
+
connectionError,
|
|
60
|
+
compact
|
|
63
61
|
}) {
|
|
64
62
|
const { color: connectionColor, text: connectionText } = getConnectionDisplay(connection);
|
|
65
63
|
return /* @__PURE__ */ jsxs(
|
|
@@ -73,16 +71,20 @@ function Header({
|
|
|
73
71
|
paddingY: 1,
|
|
74
72
|
width: "100%",
|
|
75
73
|
children: [
|
|
76
|
-
/* @__PURE__ */ jsx(Box, { paddingLeft: 3, children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: LogoString }) }),
|
|
77
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, children: [
|
|
74
|
+
!compact && /* @__PURE__ */ jsx(Box, { paddingLeft: 3, children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: LogoString }) }),
|
|
75
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: compact ? 0 : 4, children: [
|
|
78
76
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
79
77
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "white", children: "MindStudio Local Tunnel" }),
|
|
78
|
+
compact && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
79
|
+
" v",
|
|
80
|
+
pkg.version
|
|
81
|
+
] }),
|
|
80
82
|
environment !== "prod" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
81
83
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
82
84
|
/* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "[LOCAL]" })
|
|
83
85
|
] })
|
|
84
86
|
] }),
|
|
85
|
-
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
87
|
+
!compact && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
86
88
|
"v",
|
|
87
89
|
pkg.version
|
|
88
90
|
] }),
|
|
@@ -103,9 +105,11 @@ function Header({
|
|
|
103
105
|
|
|
104
106
|
// src/tui/components/NavigationMenu.tsx
|
|
105
107
|
import { useState, useEffect } from "react";
|
|
106
|
-
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
108
|
+
import { Box as Box2, Text as Text2, useInput, useStdout } from "ink";
|
|
107
109
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
108
110
|
function NavigationMenu({ items, onSelect, title }) {
|
|
111
|
+
const { stdout } = useStdout();
|
|
112
|
+
const compact = (stdout?.rows ?? 24) < 40;
|
|
109
113
|
const getDefaultIndex = () => {
|
|
110
114
|
const backIdx = items.findIndex((i) => i.id === "back");
|
|
111
115
|
if (backIdx >= 0) return backIdx;
|
|
@@ -116,6 +120,7 @@ function NavigationMenu({ items, onSelect, title }) {
|
|
|
116
120
|
useEffect(() => {
|
|
117
121
|
setSelectedIndex(getDefaultIndex());
|
|
118
122
|
}, [items]);
|
|
123
|
+
const selectableItems = items.filter((i) => !i.isSeparator);
|
|
119
124
|
const findNextEnabled = (from, direction) => {
|
|
120
125
|
let idx = from;
|
|
121
126
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -134,9 +139,9 @@ function NavigationMenu({ items, onSelect, title }) {
|
|
|
134
139
|
}
|
|
135
140
|
return;
|
|
136
141
|
}
|
|
137
|
-
if (key.upArrow) {
|
|
142
|
+
if (key.upArrow || compact && key.leftArrow) {
|
|
138
143
|
setSelectedIndex((prev) => findNextEnabled(prev, -1));
|
|
139
|
-
} else if (key.downArrow) {
|
|
144
|
+
} else if (key.downArrow || compact && key.rightArrow) {
|
|
140
145
|
setSelectedIndex((prev) => findNextEnabled(prev, 1));
|
|
141
146
|
} else if (key.return) {
|
|
142
147
|
const item = items[selectedIndex];
|
|
@@ -145,6 +150,24 @@ function NavigationMenu({ items, onSelect, title }) {
|
|
|
145
150
|
}
|
|
146
151
|
}
|
|
147
152
|
});
|
|
153
|
+
const hasBack = items.some((i) => i.id === "back");
|
|
154
|
+
if (compact) {
|
|
155
|
+
const selectedItem = items[selectedIndex];
|
|
156
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", paddingX: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "gray", children: /* @__PURE__ */ jsx2(Box2, { height: 1, overflow: "hidden", gap: 1, children: items.map((item, index) => {
|
|
157
|
+
if (item.isSeparator) return null;
|
|
158
|
+
const isSelected = index === selectedIndex;
|
|
159
|
+
return /* @__PURE__ */ jsx2(
|
|
160
|
+
Text2,
|
|
161
|
+
{
|
|
162
|
+
color: item.disabled ? "gray" : isSelected ? "cyan" : "white",
|
|
163
|
+
bold: isSelected,
|
|
164
|
+
wrap: "truncate-end",
|
|
165
|
+
children: isSelected ? `\u276F ${item.label}` : ` ${item.label}`
|
|
166
|
+
},
|
|
167
|
+
item.id
|
|
168
|
+
);
|
|
169
|
+
}) }) });
|
|
170
|
+
}
|
|
148
171
|
const separatorExtraLines = items.filter((item, idx) => item.isSeparator && idx > 0).length;
|
|
149
172
|
const menuHeight = items.length + 4 + separatorExtraLines;
|
|
150
173
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, marginBottom: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "gray", children: [
|
|
@@ -156,14 +179,14 @@ function NavigationMenu({ items, onSelect, title }) {
|
|
|
156
179
|
const isSelected = index === selectedIndex;
|
|
157
180
|
const prefix = isSelected ? "\u276F" : " ";
|
|
158
181
|
if (item.disabled) {
|
|
159
|
-
return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
|
|
182
|
+
return /* @__PURE__ */ jsx2(Box2, { height: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
|
|
160
183
|
prefix,
|
|
161
184
|
" ",
|
|
162
185
|
item.label,
|
|
163
186
|
item.disabledReason ? ` (${item.disabledReason})` : ""
|
|
164
187
|
] }) }, item.id);
|
|
165
188
|
}
|
|
166
|
-
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
189
|
+
return /* @__PURE__ */ jsxs2(Box2, { height: 1, overflow: "hidden", children: [
|
|
167
190
|
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "cyan" : "white", bold: isSelected, wrap: "truncate-end", children: [
|
|
168
191
|
prefix,
|
|
169
192
|
" ",
|
|
@@ -175,7 +198,7 @@ function NavigationMenu({ items, onSelect, title }) {
|
|
|
175
198
|
] })
|
|
176
199
|
] }, item.id);
|
|
177
200
|
}) }),
|
|
178
|
-
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, height: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "gray", wrap: "truncate-end", children:
|
|
201
|
+
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, height: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "gray", wrap: "truncate-end", children: hasBack ? "Up/Down Navigate \u2022 Enter Select \u2022 q/Esc Back" : "Up/Down Navigate \u2022 Enter Select \u2022 q Quit" }) })
|
|
179
202
|
] });
|
|
180
203
|
}
|
|
181
204
|
|
|
@@ -216,47 +239,56 @@ function useConnection() {
|
|
|
216
239
|
};
|
|
217
240
|
}
|
|
218
241
|
|
|
219
|
-
// src/tui/hooks/
|
|
220
|
-
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2 } from "react";
|
|
221
|
-
function
|
|
242
|
+
// src/tui/hooks/useSetupProviders.ts
|
|
243
|
+
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2, useRef } from "react";
|
|
244
|
+
function useSetupProviders() {
|
|
222
245
|
const [providers, setProviders] = useState3([]);
|
|
223
246
|
const [loading, setLoading] = useState3(true);
|
|
247
|
+
const [refreshing, setRefreshing] = useState3(false);
|
|
248
|
+
const initialLoadDone = useRef(false);
|
|
224
249
|
const refresh = useCallback2(async () => {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
} finally {
|
|
230
|
-
setLoading(false);
|
|
250
|
+
if (!initialLoadDone.current) {
|
|
251
|
+
setLoading(true);
|
|
252
|
+
} else {
|
|
253
|
+
setRefreshing(true);
|
|
231
254
|
}
|
|
255
|
+
const statuses = await detectAllProviderStatuses();
|
|
256
|
+
setProviders(statuses);
|
|
257
|
+
initialLoadDone.current = true;
|
|
258
|
+
setLoading(false);
|
|
259
|
+
setRefreshing(false);
|
|
232
260
|
}, []);
|
|
233
261
|
useEffect3(() => {
|
|
234
262
|
refresh();
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}, [refresh, pollInterval]);
|
|
238
|
-
return {
|
|
239
|
-
providers,
|
|
240
|
-
loading,
|
|
241
|
-
refresh
|
|
242
|
-
};
|
|
263
|
+
}, [refresh]);
|
|
264
|
+
return { providers, loading, refreshing, refresh };
|
|
243
265
|
}
|
|
244
266
|
|
|
245
267
|
// src/tui/hooks/useModels.ts
|
|
246
|
-
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback3 } from "react";
|
|
268
|
+
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback3, useRef as useRef2 } from "react";
|
|
247
269
|
function useModels() {
|
|
248
270
|
const [models, setModels] = useState4([]);
|
|
249
271
|
const [warnings, setWarnings] = useState4([]);
|
|
250
272
|
const [loading, setLoading] = useState4(true);
|
|
273
|
+
const [refreshing, setRefreshing] = useState4(false);
|
|
274
|
+
const initialLoadDone = useRef2(false);
|
|
251
275
|
const refresh = useCallback3(async () => {
|
|
252
|
-
|
|
276
|
+
if (!initialLoadDone.current) {
|
|
277
|
+
setLoading(true);
|
|
278
|
+
} else {
|
|
279
|
+
setRefreshing(true);
|
|
280
|
+
}
|
|
253
281
|
try {
|
|
254
|
-
const discoveredModels = await
|
|
282
|
+
const discoveredModels = await discoverAllModelsWithParameters();
|
|
255
283
|
setModels(discoveredModels.filter((m) => !m.statusHint));
|
|
256
284
|
setWarnings(discoveredModels.filter((m) => !!m.statusHint));
|
|
285
|
+
initialLoadDone.current = true;
|
|
286
|
+
return discoveredModels;
|
|
257
287
|
} catch {
|
|
288
|
+
return [];
|
|
258
289
|
} finally {
|
|
259
290
|
setLoading(false);
|
|
291
|
+
setRefreshing(false);
|
|
260
292
|
}
|
|
261
293
|
}, []);
|
|
262
294
|
useEffect4(() => {
|
|
@@ -266,15 +298,16 @@ function useModels() {
|
|
|
266
298
|
models,
|
|
267
299
|
warnings,
|
|
268
300
|
loading,
|
|
301
|
+
refreshing,
|
|
269
302
|
refresh
|
|
270
303
|
};
|
|
271
304
|
}
|
|
272
305
|
|
|
273
306
|
// src/tui/hooks/useRequests.ts
|
|
274
|
-
import { useState as useState5, useEffect as useEffect5, useCallback as useCallback4, useRef } from "react";
|
|
307
|
+
import { useState as useState5, useEffect as useEffect5, useCallback as useCallback4, useRef as useRef3 } from "react";
|
|
275
308
|
function useRequests(maxHistory = 50) {
|
|
276
309
|
const [requests, setRequests] = useState5([]);
|
|
277
|
-
const requestsRef =
|
|
310
|
+
const requestsRef = useRef3(/* @__PURE__ */ new Map());
|
|
278
311
|
useEffect5(() => {
|
|
279
312
|
const interval = setInterval(() => {
|
|
280
313
|
setRequests((prev) => {
|
|
@@ -352,14 +385,17 @@ function useSyncedModels(connectionStatus) {
|
|
|
352
385
|
const [syncedNames, setSyncedNames] = useState6(
|
|
353
386
|
/* @__PURE__ */ new Set()
|
|
354
387
|
);
|
|
388
|
+
const [syncedModels, setSyncedModels] = useState6([]);
|
|
355
389
|
const refresh = useCallback5(async () => {
|
|
356
390
|
if (connectionStatus !== "connected") {
|
|
357
391
|
setSyncedNames(/* @__PURE__ */ new Set());
|
|
392
|
+
setSyncedModels([]);
|
|
358
393
|
return;
|
|
359
394
|
}
|
|
360
395
|
try {
|
|
361
396
|
const models = await getSyncedModels();
|
|
362
397
|
setSyncedNames(new Set(models.map((m) => m.name)));
|
|
398
|
+
setSyncedModels(models);
|
|
363
399
|
} catch {
|
|
364
400
|
}
|
|
365
401
|
}, [connectionStatus]);
|
|
@@ -368,17 +404,18 @@ function useSyncedModels(connectionStatus) {
|
|
|
368
404
|
}, [refresh]);
|
|
369
405
|
return {
|
|
370
406
|
syncedNames,
|
|
407
|
+
syncedModels,
|
|
371
408
|
refresh
|
|
372
409
|
};
|
|
373
410
|
}
|
|
374
411
|
|
|
375
412
|
// src/tui/pages/DashboardPage.tsx
|
|
376
413
|
import { useMemo } from "react";
|
|
377
|
-
import { Box as Box4, Text as Text4, useStdout as
|
|
414
|
+
import { Box as Box4, Text as Text4, useStdout as useStdout3 } from "ink";
|
|
378
415
|
import Spinner2 from "ink-spinner";
|
|
379
416
|
|
|
380
417
|
// src/tui/components/RequestLog.tsx
|
|
381
|
-
import { Box as Box3, Text as Text3, useStdout } from "ink";
|
|
418
|
+
import { Box as Box3, Text as Text3, useStdout as useStdout2 } from "ink";
|
|
382
419
|
import Spinner from "ink-spinner";
|
|
383
420
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
384
421
|
function formatTime(timestamp) {
|
|
@@ -498,7 +535,7 @@ function RequestItem({ request, width }) {
|
|
|
498
535
|
] }) });
|
|
499
536
|
}
|
|
500
537
|
function RequestLog({ requests, maxVisible = 8, hasModels = true }) {
|
|
501
|
-
const { stdout } =
|
|
538
|
+
const { stdout } = useStdout2();
|
|
502
539
|
const width = stdout?.columns ?? 80;
|
|
503
540
|
const activeRequests = requests.filter((r) => r.status === "processing");
|
|
504
541
|
const completedRequests = requests.filter((r) => r.status !== "processing");
|
|
@@ -543,23 +580,6 @@ function RequestLog({ requests, maxVisible = 8, hasModels = true }) {
|
|
|
543
580
|
);
|
|
544
581
|
}
|
|
545
582
|
|
|
546
|
-
// src/tui/hooks/useSetupProviders.ts
|
|
547
|
-
import { useState as useState7, useEffect as useEffect7, useCallback as useCallback6 } from "react";
|
|
548
|
-
function useSetupProviders() {
|
|
549
|
-
const [providers, setProviders] = useState7([]);
|
|
550
|
-
const [loading, setLoading] = useState7(true);
|
|
551
|
-
const refresh = useCallback6(async () => {
|
|
552
|
-
setLoading(true);
|
|
553
|
-
const statuses = await detectAllProviderStatuses();
|
|
554
|
-
setProviders(statuses);
|
|
555
|
-
setLoading(false);
|
|
556
|
-
}, []);
|
|
557
|
-
useEffect7(() => {
|
|
558
|
-
refresh();
|
|
559
|
-
}, [refresh]);
|
|
560
|
-
return { providers, loading, refresh };
|
|
561
|
-
}
|
|
562
|
-
|
|
563
583
|
// src/tui/pages/DashboardPage.tsx
|
|
564
584
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
565
585
|
function getWorkflowCount(model) {
|
|
@@ -583,12 +603,14 @@ function DashboardPage({
|
|
|
583
603
|
requests,
|
|
584
604
|
models,
|
|
585
605
|
modelWarnings = [],
|
|
606
|
+
providers,
|
|
607
|
+
providersLoading,
|
|
586
608
|
syncedNames,
|
|
587
609
|
modelsLoading,
|
|
610
|
+
syncStatus = "idle",
|
|
588
611
|
onNavigate
|
|
589
612
|
}) {
|
|
590
|
-
const { stdout } =
|
|
591
|
-
const { providers, loading: setupLoading } = useSetupProviders();
|
|
613
|
+
const { stdout } = useStdout3();
|
|
592
614
|
const installedProviders = providers.filter(({ status }) => status.installed);
|
|
593
615
|
const provNameWidth = Math.max(
|
|
594
616
|
...installedProviders.map((p) => p.provider.displayName.length),
|
|
@@ -599,17 +621,13 @@ function DashboardPage({
|
|
|
599
621
|
const unavailableSynced = [...syncedNames].filter(
|
|
600
622
|
(name) => !allModelNames.has(name)
|
|
601
623
|
);
|
|
624
|
+
const syncDescription = syncStatus === "syncing" ? "Syncing..." : syncStatus === "synced" ? "\u2713 Synced" : "Re-detect providers and sync models to MindStudio";
|
|
602
625
|
const menuItems = useMemo(() => {
|
|
603
626
|
return [
|
|
604
|
-
{
|
|
605
|
-
id: "register",
|
|
606
|
-
label: "Sync Models",
|
|
607
|
-
description: "Sync models with MindStudio Cloud"
|
|
608
|
-
},
|
|
609
627
|
{
|
|
610
628
|
id: "refresh",
|
|
611
|
-
label: "
|
|
612
|
-
description:
|
|
629
|
+
label: "Sync Models",
|
|
630
|
+
description: syncDescription
|
|
613
631
|
},
|
|
614
632
|
{
|
|
615
633
|
id: "setup",
|
|
@@ -627,21 +645,23 @@ function DashboardPage({
|
|
|
627
645
|
description: "Quit the application"
|
|
628
646
|
}
|
|
629
647
|
];
|
|
630
|
-
}, []);
|
|
648
|
+
}, [syncDescription]);
|
|
631
649
|
const termHeight = (stdout?.rows ?? 24) - 4;
|
|
632
|
-
const
|
|
633
|
-
const
|
|
650
|
+
const compactHeader = (stdout?.rows ?? 24) <= 45 || (stdout?.columns ?? 80) <= 90;
|
|
651
|
+
const headerLines = compactHeader ? 7 : 14;
|
|
652
|
+
const providerContentLines = providersLoading ? 1 : installedProviders.length === 0 ? 2 : installedProviders.length;
|
|
634
653
|
const providersLines = 3 + providerContentLines;
|
|
635
654
|
const modelContentLines = modelsLoading ? 1 : models.length === 0 && unavailableSynced.length === 0 && modelWarnings.length === 0 ? 2 : models.length + modelWarnings.length + (unavailableSynced.length > 0 ? 1 + unavailableSynced.length : 0);
|
|
636
655
|
const modelsLines = 3 + modelContentLines;
|
|
637
656
|
const requestLogOverhead = 3;
|
|
638
|
-
const
|
|
657
|
+
const compactMenu = termHeight + 4 < 40;
|
|
658
|
+
const menuLines = compactMenu ? 2 : menuItems.length + 6;
|
|
639
659
|
const usedLines = headerLines + providersLines + modelsLines + requestLogOverhead + menuLines;
|
|
640
660
|
const maxVisible = Math.max(3, termHeight - usedLines);
|
|
641
661
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", flexGrow: 1, children: [
|
|
642
662
|
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
643
663
|
/* @__PURE__ */ jsx4(Text4, { bold: true, color: "white", underline: true, children: "Providers" }),
|
|
644
|
-
|
|
664
|
+
providersLoading ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
645
665
|
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }) }),
|
|
646
666
|
/* @__PURE__ */ jsx4(Text4, { children: " Detecting providers..." })
|
|
647
667
|
] }) : installedProviders.length === 0 ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
@@ -681,7 +701,7 @@ function DashboardPage({
|
|
|
681
701
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: displayProvider }),
|
|
682
702
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: " - " }),
|
|
683
703
|
/* @__PURE__ */ jsx4(Text4, { color: cap.color, children: cap.label })
|
|
684
|
-
] }, model.name);
|
|
704
|
+
] }, `${model.provider}:${model.name}`);
|
|
685
705
|
}),
|
|
686
706
|
modelWarnings.map((warning) => {
|
|
687
707
|
const displayProvider = providers.find((p) => p.provider.name === warning.provider)?.provider.displayName ?? warning.provider;
|
|
@@ -692,10 +712,10 @@ function DashboardPage({
|
|
|
692
712
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: displayProvider }),
|
|
693
713
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: " - " }),
|
|
694
714
|
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: warning.statusHint })
|
|
695
|
-
] }, warning.name);
|
|
715
|
+
] }, `${warning.provider}:${warning.name}`);
|
|
696
716
|
}),
|
|
697
717
|
unavailableSynced.length > 0 && /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: models.length > 0 ? 1 : 0, children: [
|
|
698
|
-
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Synced but not
|
|
718
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Synced but provider not running:" }),
|
|
699
719
|
unavailableSynced.map((name) => /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
700
720
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: "\u25CB" }),
|
|
701
721
|
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: ` ${name}` })
|
|
@@ -715,206 +735,18 @@ function DashboardPage({
|
|
|
715
735
|
] });
|
|
716
736
|
}
|
|
717
737
|
|
|
718
|
-
// src/tui/pages/RegisterPage.tsx
|
|
719
|
-
import { useEffect as useEffect9 } from "react";
|
|
720
|
-
import { Box as Box5, Text as Text5 } from "ink";
|
|
721
|
-
import Spinner3 from "ink-spinner";
|
|
722
|
-
|
|
723
|
-
// src/tui/hooks/useRegister.ts
|
|
724
|
-
import { useState as useState8, useCallback as useCallback7, useRef as useRef2, useEffect as useEffect8 } from "react";
|
|
725
|
-
var MODEL_TYPE_MAP = {
|
|
726
|
-
text: "llm_chat",
|
|
727
|
-
image: "image_generation",
|
|
728
|
-
video: "video_generation"
|
|
729
|
-
};
|
|
730
|
-
function useSync() {
|
|
731
|
-
const [status, setStatus] = useState8("idle");
|
|
732
|
-
const [progress, setProgress] = useState8({
|
|
733
|
-
current: 0,
|
|
734
|
-
total: 0
|
|
735
|
-
});
|
|
736
|
-
const [syncedModels, setSyncedModels] = useState8(
|
|
737
|
-
[]
|
|
738
|
-
);
|
|
739
|
-
const [error, setError] = useState8(null);
|
|
740
|
-
const cancelledRef = useRef2(false);
|
|
741
|
-
useEffect8(() => {
|
|
742
|
-
return () => {
|
|
743
|
-
cancelledRef.current = true;
|
|
744
|
-
};
|
|
745
|
-
}, []);
|
|
746
|
-
const cancel = useCallback7(() => {
|
|
747
|
-
cancelledRef.current = true;
|
|
748
|
-
setStatus("idle");
|
|
749
|
-
}, []);
|
|
750
|
-
const startSync = useCallback7(() => {
|
|
751
|
-
cancelledRef.current = false;
|
|
752
|
-
setError(null);
|
|
753
|
-
setSyncedModels([]);
|
|
754
|
-
const run = async () => {
|
|
755
|
-
try {
|
|
756
|
-
setStatus("discovering");
|
|
757
|
-
const localModels = await discoverAllModelsWithParameters();
|
|
758
|
-
if (cancelledRef.current) return;
|
|
759
|
-
if (localModels.length === 0) {
|
|
760
|
-
setError("No local models found.");
|
|
761
|
-
setStatus("error");
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
const existingSynced = await getSyncedModels();
|
|
765
|
-
if (cancelledRef.current) return;
|
|
766
|
-
const remoteByName = new Map(
|
|
767
|
-
existingSynced.map((m) => [m.name, m.id])
|
|
768
|
-
);
|
|
769
|
-
setStatus("syncing");
|
|
770
|
-
setProgress({ current: 0, total: localModels.length });
|
|
771
|
-
for (let i = 0; i < localModels.length; i++) {
|
|
772
|
-
if (cancelledRef.current) return;
|
|
773
|
-
const model = localModels[i];
|
|
774
|
-
const modelType = MODEL_TYPE_MAP[model.capability];
|
|
775
|
-
const existingId = remoteByName.get(model.name);
|
|
776
|
-
if (existingId) {
|
|
777
|
-
await updateLocalModel({
|
|
778
|
-
modelId: existingId,
|
|
779
|
-
modelName: model.name,
|
|
780
|
-
provider: model.provider,
|
|
781
|
-
modelType,
|
|
782
|
-
parameters: model.parameters
|
|
783
|
-
});
|
|
784
|
-
} else {
|
|
785
|
-
await syncLocalModel({
|
|
786
|
-
modelName: model.name,
|
|
787
|
-
provider: model.provider,
|
|
788
|
-
modelType,
|
|
789
|
-
parameters: model.parameters
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
setProgress({ current: i + 1, total: localModels.length });
|
|
793
|
-
}
|
|
794
|
-
if (cancelledRef.current) return;
|
|
795
|
-
const finalModels = localModels.map((m) => ({
|
|
796
|
-
name: m.name,
|
|
797
|
-
provider: m.provider,
|
|
798
|
-
capability: m.capability,
|
|
799
|
-
isNew: !remoteByName.has(m.name)
|
|
800
|
-
}));
|
|
801
|
-
setSyncedModels(finalModels);
|
|
802
|
-
setStatus("done");
|
|
803
|
-
} catch (err) {
|
|
804
|
-
if (!cancelledRef.current) {
|
|
805
|
-
setError(err instanceof Error ? err.message : "Sync failed");
|
|
806
|
-
setStatus("error");
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
};
|
|
810
|
-
run();
|
|
811
|
-
}, []);
|
|
812
|
-
return {
|
|
813
|
-
status,
|
|
814
|
-
progress,
|
|
815
|
-
syncedModels,
|
|
816
|
-
error,
|
|
817
|
-
startSync,
|
|
818
|
-
cancel
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
// src/tui/pages/RegisterPage.tsx
|
|
823
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
824
|
-
function SyncPage() {
|
|
825
|
-
const { status, progress, syncedModels, error, startSync, cancel } = useSync();
|
|
826
|
-
useEffect9(() => {
|
|
827
|
-
startSync();
|
|
828
|
-
return () => cancel();
|
|
829
|
-
}, []);
|
|
830
|
-
if (status === "idle") {
|
|
831
|
-
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: "gray", children: "Starting model sync..." }) }) });
|
|
832
|
-
}
|
|
833
|
-
if (status === "error") {
|
|
834
|
-
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
|
|
835
|
-
"Sync failed: ",
|
|
836
|
-
error
|
|
837
|
-
] }) }) });
|
|
838
|
-
}
|
|
839
|
-
if (status === "discovering") {
|
|
840
|
-
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
841
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: /* @__PURE__ */ jsx5(Spinner3, { type: "dots" }) }),
|
|
842
|
-
/* @__PURE__ */ jsx5(Text5, { children: " Discovering local models..." })
|
|
843
|
-
] }) });
|
|
844
|
-
}
|
|
845
|
-
if (status === "syncing") {
|
|
846
|
-
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", marginTop: 1, paddingX: 1, children: /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
847
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: /* @__PURE__ */ jsx5(Spinner3, { type: "dots" }) }),
|
|
848
|
-
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
849
|
-
" ",
|
|
850
|
-
"Syncing ",
|
|
851
|
-
progress.current,
|
|
852
|
-
"/",
|
|
853
|
-
progress.total,
|
|
854
|
-
" models..."
|
|
855
|
-
] })
|
|
856
|
-
] }) });
|
|
857
|
-
}
|
|
858
|
-
const newModels = syncedModels.filter((m) => m.isNew);
|
|
859
|
-
const resyncedModels = syncedModels.filter((m) => !m.isNew);
|
|
860
|
-
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, paddingX: 1, children: [
|
|
861
|
-
newModels.length > 0 && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
862
|
-
/* @__PURE__ */ jsxs5(Text5, { color: "green", children: [
|
|
863
|
-
"Synced ",
|
|
864
|
-
newModels.length,
|
|
865
|
-
" new model",
|
|
866
|
-
newModels.length !== 1 ? "s" : "",
|
|
867
|
-
":"
|
|
868
|
-
] }),
|
|
869
|
-
newModels.map((m) => /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
870
|
-
/* @__PURE__ */ jsx5(Text5, { color: "green", children: " \u2713 " }),
|
|
871
|
-
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
872
|
-
m.name,
|
|
873
|
-
" "
|
|
874
|
-
] }),
|
|
875
|
-
/* @__PURE__ */ jsxs5(Text5, { color: "gray", children: [
|
|
876
|
-
"[",
|
|
877
|
-
m.provider,
|
|
878
|
-
"]"
|
|
879
|
-
] })
|
|
880
|
-
] }, m.name))
|
|
881
|
-
] }),
|
|
882
|
-
resyncedModels.length > 0 && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
883
|
-
/* @__PURE__ */ jsxs5(Text5, { color: "green", children: [
|
|
884
|
-
"Resynced ",
|
|
885
|
-
resyncedModels.length,
|
|
886
|
-
" existing model",
|
|
887
|
-
resyncedModels.length !== 1 ? "s" : "",
|
|
888
|
-
":"
|
|
889
|
-
] }),
|
|
890
|
-
resyncedModels.map((m) => /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
891
|
-
/* @__PURE__ */ jsx5(Text5, { color: "green", children: " \u2713 " }),
|
|
892
|
-
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
893
|
-
m.name,
|
|
894
|
-
" "
|
|
895
|
-
] }),
|
|
896
|
-
/* @__PURE__ */ jsxs5(Text5, { color: "gray", children: [
|
|
897
|
-
"[",
|
|
898
|
-
m.provider,
|
|
899
|
-
"]"
|
|
900
|
-
] })
|
|
901
|
-
] }, m.name))
|
|
902
|
-
] })
|
|
903
|
-
] });
|
|
904
|
-
}
|
|
905
|
-
|
|
906
738
|
// src/tui/pages/SetupPage.tsx
|
|
907
|
-
import { useState as
|
|
908
|
-
import { Box as
|
|
909
|
-
import
|
|
739
|
+
import { useState as useState7, useMemo as useMemo3, useEffect as useEffect7 } from "react";
|
|
740
|
+
import { Box as Box5, Text as Text6, useInput as useInput2, useStdout as useStdout5 } from "ink";
|
|
741
|
+
import Spinner3 from "ink-spinner";
|
|
910
742
|
|
|
911
743
|
// src/tui/components/MarkdownText.tsx
|
|
912
744
|
import { useMemo as useMemo2 } from "react";
|
|
913
|
-
import { Text as
|
|
745
|
+
import { Text as Text5, useStdout as useStdout4 } from "ink";
|
|
914
746
|
import chalk from "chalk";
|
|
915
747
|
import { marked } from "marked";
|
|
916
748
|
import { markedTerminal } from "marked-terminal";
|
|
917
|
-
import { jsx as
|
|
749
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
918
750
|
var codeStyle = chalk.cyan;
|
|
919
751
|
var identity = (s) => s;
|
|
920
752
|
function renderMarkdown(content, width) {
|
|
@@ -944,13 +776,13 @@ function renderMarkdown(content, width) {
|
|
|
944
776
|
}
|
|
945
777
|
|
|
946
778
|
// src/tui/pages/SetupPage.tsx
|
|
947
|
-
import { Fragment as Fragment2, jsx as
|
|
779
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
948
780
|
function ProviderDetailView({
|
|
949
781
|
provider,
|
|
950
782
|
onBack
|
|
951
783
|
}) {
|
|
952
|
-
const [scrollOffset, setScrollOffset] =
|
|
953
|
-
const { stdout } =
|
|
784
|
+
const [scrollOffset, setScrollOffset] = useState7(0);
|
|
785
|
+
const { stdout } = useStdout5();
|
|
954
786
|
const termHeight = (stdout?.rows ?? 24) - 4;
|
|
955
787
|
const headerHeight = 14;
|
|
956
788
|
const footerLines = 6;
|
|
@@ -988,21 +820,21 @@ function ProviderDetailView({
|
|
|
988
820
|
(_, i) => i >= thumbPos && i < thumbPos + thumbSize
|
|
989
821
|
);
|
|
990
822
|
}, [scrollOffset, maxScroll, viewHeight, renderedLines.length]);
|
|
991
|
-
return /* @__PURE__ */
|
|
992
|
-
/* @__PURE__ */
|
|
993
|
-
/* @__PURE__ */
|
|
994
|
-
|
|
823
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
824
|
+
/* @__PURE__ */ jsxs5(Box5, { height: viewHeight, children: [
|
|
825
|
+
/* @__PURE__ */ jsx6(
|
|
826
|
+
Box5,
|
|
995
827
|
{
|
|
996
828
|
flexDirection: "column",
|
|
997
829
|
paddingX: 1,
|
|
998
830
|
paddingY: 1,
|
|
999
831
|
flexGrow: 1,
|
|
1000
832
|
overflow: "hidden",
|
|
1001
|
-
children: /* @__PURE__ */
|
|
833
|
+
children: /* @__PURE__ */ jsx6(Text6, { children: visibleContent })
|
|
1002
834
|
}
|
|
1003
835
|
),
|
|
1004
|
-
scrollbar && /* @__PURE__ */
|
|
1005
|
-
|
|
836
|
+
scrollbar && /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", children: scrollbar.map((isThumb, i) => /* @__PURE__ */ jsx6(
|
|
837
|
+
Text6,
|
|
1006
838
|
{
|
|
1007
839
|
color: isThumb ? "cyan" : "gray",
|
|
1008
840
|
dimColor: !isThumb,
|
|
@@ -1011,8 +843,8 @@ function ProviderDetailView({
|
|
|
1011
843
|
i
|
|
1012
844
|
)) })
|
|
1013
845
|
] }),
|
|
1014
|
-
/* @__PURE__ */
|
|
1015
|
-
|
|
846
|
+
/* @__PURE__ */ jsxs5(
|
|
847
|
+
Box5,
|
|
1016
848
|
{
|
|
1017
849
|
flexDirection: "column",
|
|
1018
850
|
paddingX: 1,
|
|
@@ -1023,15 +855,15 @@ function ProviderDetailView({
|
|
|
1023
855
|
borderRight: false,
|
|
1024
856
|
borderColor: "gray",
|
|
1025
857
|
children: [
|
|
1026
|
-
/* @__PURE__ */
|
|
1027
|
-
/* @__PURE__ */
|
|
1028
|
-
/* @__PURE__ */
|
|
858
|
+
/* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "gray", children: "Actions" }) }),
|
|
859
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
860
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "cyan", bold: true, children: [
|
|
1029
861
|
"\u276F",
|
|
1030
862
|
" Back"
|
|
1031
863
|
] }),
|
|
1032
|
-
/* @__PURE__ */
|
|
864
|
+
/* @__PURE__ */ jsx6(Text6, { color: "gray", children: " - Return to providers" })
|
|
1033
865
|
] }),
|
|
1034
|
-
/* @__PURE__ */
|
|
866
|
+
/* @__PURE__ */ jsx6(Box5, { marginTop: 1, height: 1, children: /* @__PURE__ */ jsxs5(Text6, { color: "gray", wrap: "truncate-end", children: [
|
|
1035
867
|
"Up/Down Scroll ",
|
|
1036
868
|
"\u2022",
|
|
1037
869
|
" Enter/q/Esc Back",
|
|
@@ -1044,7 +876,7 @@ function ProviderDetailView({
|
|
|
1044
876
|
}
|
|
1045
877
|
function SetupPage({ onBack }) {
|
|
1046
878
|
const { providers, loading } = useSetupProviders();
|
|
1047
|
-
const [selectedProvider, setSelectedProvider] =
|
|
879
|
+
const [selectedProvider, setSelectedProvider] = useState7(null);
|
|
1048
880
|
const running = useMemo3(
|
|
1049
881
|
() => providers.filter((p) => p.status.running),
|
|
1050
882
|
[providers]
|
|
@@ -1063,8 +895,8 @@ function SetupPage({ onBack }) {
|
|
|
1063
895
|
);
|
|
1064
896
|
const totalItems = allProviders.length + 1;
|
|
1065
897
|
const backIndex = allProviders.length;
|
|
1066
|
-
const [cursorIndex, setCursorIndex] =
|
|
1067
|
-
|
|
898
|
+
const [cursorIndex, setCursorIndex] = useState7(backIndex);
|
|
899
|
+
useEffect7(() => {
|
|
1068
900
|
setCursorIndex(backIndex);
|
|
1069
901
|
}, [backIndex]);
|
|
1070
902
|
useInput2((input, key) => {
|
|
@@ -1088,7 +920,7 @@ function SetupPage({ onBack }) {
|
|
|
1088
920
|
if (selectedProvider) {
|
|
1089
921
|
const found = providers.find((p) => p.provider.name === selectedProvider);
|
|
1090
922
|
if (found) {
|
|
1091
|
-
return /* @__PURE__ */
|
|
923
|
+
return /* @__PURE__ */ jsx6(
|
|
1092
924
|
ProviderDetailView,
|
|
1093
925
|
{
|
|
1094
926
|
provider: found.provider,
|
|
@@ -1097,26 +929,26 @@ function SetupPage({ onBack }) {
|
|
|
1097
929
|
);
|
|
1098
930
|
}
|
|
1099
931
|
}
|
|
1100
|
-
return /* @__PURE__ */
|
|
1101
|
-
/* @__PURE__ */
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
loading ? /* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1105
|
-
/* @__PURE__ */
|
|
1106
|
-
] }) : /* @__PURE__ */
|
|
1107
|
-
running.length > 0 && /* @__PURE__ */
|
|
1108
|
-
/* @__PURE__ */
|
|
932
|
+
return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
933
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "white", underline: true, children: "Manage Providers" }),
|
|
934
|
+
/* @__PURE__ */ jsx6(Text6, { color: "gray", children: "Select a provider to view its setup guide." }),
|
|
935
|
+
loading ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
936
|
+
/* @__PURE__ */ jsx6(Text6, { color: "cyan", children: /* @__PURE__ */ jsx6(Spinner3, { type: "dots" }) }),
|
|
937
|
+
/* @__PURE__ */ jsx6(Text6, { children: " Detecting providers..." })
|
|
938
|
+
] }) : /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
939
|
+
running.length > 0 && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
940
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "green", children: "Running" }),
|
|
1109
941
|
running.map(({ provider }, i) => {
|
|
1110
942
|
const index = i;
|
|
1111
943
|
const isSelected = index === cursorIndex;
|
|
1112
|
-
return /* @__PURE__ */
|
|
1113
|
-
|
|
944
|
+
return /* @__PURE__ */ jsxs5(
|
|
945
|
+
Box5,
|
|
1114
946
|
{
|
|
1115
947
|
flexDirection: "column",
|
|
1116
948
|
marginTop: i > 0 ? 1 : 0,
|
|
1117
949
|
children: [
|
|
1118
|
-
/* @__PURE__ */
|
|
1119
|
-
|
|
950
|
+
/* @__PURE__ */ jsx6(Box5, { children: /* @__PURE__ */ jsxs5(
|
|
951
|
+
Text6,
|
|
1120
952
|
{
|
|
1121
953
|
color: isSelected ? "cyan" : "white",
|
|
1122
954
|
bold: isSelected,
|
|
@@ -1129,7 +961,7 @@ function SetupPage({ onBack }) {
|
|
|
1129
961
|
]
|
|
1130
962
|
}
|
|
1131
963
|
) }),
|
|
1132
|
-
/* @__PURE__ */
|
|
964
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "gray", wrap: "wrap", children: [
|
|
1133
965
|
" ",
|
|
1134
966
|
provider.description
|
|
1135
967
|
] })
|
|
@@ -1139,24 +971,24 @@ function SetupPage({ onBack }) {
|
|
|
1139
971
|
);
|
|
1140
972
|
})
|
|
1141
973
|
] }),
|
|
1142
|
-
installed.length > 0 && /* @__PURE__ */
|
|
1143
|
-
|
|
974
|
+
installed.length > 0 && /* @__PURE__ */ jsxs5(
|
|
975
|
+
Box5,
|
|
1144
976
|
{
|
|
1145
977
|
flexDirection: "column",
|
|
1146
978
|
marginTop: running.length > 0 ? 1 : 0,
|
|
1147
979
|
children: [
|
|
1148
|
-
/* @__PURE__ */
|
|
980
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "yellow", children: "Installed" }),
|
|
1149
981
|
installed.map(({ provider, status }, i) => {
|
|
1150
982
|
const index = running.length + i;
|
|
1151
983
|
const isSelected = index === cursorIndex;
|
|
1152
|
-
return /* @__PURE__ */
|
|
1153
|
-
|
|
984
|
+
return /* @__PURE__ */ jsxs5(
|
|
985
|
+
Box5,
|
|
1154
986
|
{
|
|
1155
987
|
flexDirection: "column",
|
|
1156
988
|
marginTop: i > 0 ? 1 : 0,
|
|
1157
989
|
children: [
|
|
1158
|
-
/* @__PURE__ */
|
|
1159
|
-
|
|
990
|
+
/* @__PURE__ */ jsx6(Box5, { children: /* @__PURE__ */ jsxs5(
|
|
991
|
+
Text6,
|
|
1160
992
|
{
|
|
1161
993
|
color: isSelected ? "cyan" : "white",
|
|
1162
994
|
bold: isSelected,
|
|
@@ -1169,7 +1001,7 @@ function SetupPage({ onBack }) {
|
|
|
1169
1001
|
]
|
|
1170
1002
|
}
|
|
1171
1003
|
) }),
|
|
1172
|
-
/* @__PURE__ */
|
|
1004
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "gray", wrap: "wrap", children: [
|
|
1173
1005
|
" ",
|
|
1174
1006
|
provider.description
|
|
1175
1007
|
] })
|
|
@@ -1181,24 +1013,24 @@ function SetupPage({ onBack }) {
|
|
|
1181
1013
|
]
|
|
1182
1014
|
}
|
|
1183
1015
|
),
|
|
1184
|
-
notInstalled.length > 0 && /* @__PURE__ */
|
|
1185
|
-
|
|
1016
|
+
notInstalled.length > 0 && /* @__PURE__ */ jsxs5(
|
|
1017
|
+
Box5,
|
|
1186
1018
|
{
|
|
1187
1019
|
flexDirection: "column",
|
|
1188
1020
|
marginTop: running.length > 0 || installed.length > 0 ? 1 : 0,
|
|
1189
1021
|
children: [
|
|
1190
|
-
/* @__PURE__ */
|
|
1022
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: "gray", children: "Not Installed" }),
|
|
1191
1023
|
notInstalled.map(({ provider }, i) => {
|
|
1192
1024
|
const index = running.length + installed.length + i;
|
|
1193
1025
|
const isSelected = index === cursorIndex;
|
|
1194
|
-
return /* @__PURE__ */
|
|
1195
|
-
|
|
1026
|
+
return /* @__PURE__ */ jsxs5(
|
|
1027
|
+
Box5,
|
|
1196
1028
|
{
|
|
1197
1029
|
flexDirection: "column",
|
|
1198
1030
|
marginTop: i > 0 ? 1 : 0,
|
|
1199
1031
|
children: [
|
|
1200
|
-
/* @__PURE__ */
|
|
1201
|
-
|
|
1032
|
+
/* @__PURE__ */ jsx6(Box5, { children: /* @__PURE__ */ jsxs5(
|
|
1033
|
+
Text6,
|
|
1202
1034
|
{
|
|
1203
1035
|
color: isSelected ? "cyan" : "white",
|
|
1204
1036
|
bold: isSelected,
|
|
@@ -1209,7 +1041,7 @@ function SetupPage({ onBack }) {
|
|
|
1209
1041
|
]
|
|
1210
1042
|
}
|
|
1211
1043
|
) }),
|
|
1212
|
-
/* @__PURE__ */
|
|
1044
|
+
/* @__PURE__ */ jsxs5(Text6, { color: "gray", wrap: "wrap", children: [
|
|
1213
1045
|
" ",
|
|
1214
1046
|
provider.description
|
|
1215
1047
|
] })
|
|
@@ -1221,8 +1053,8 @@ function SetupPage({ onBack }) {
|
|
|
1221
1053
|
]
|
|
1222
1054
|
}
|
|
1223
1055
|
),
|
|
1224
|
-
/* @__PURE__ */
|
|
1225
|
-
|
|
1056
|
+
/* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs5(
|
|
1057
|
+
Text6,
|
|
1226
1058
|
{
|
|
1227
1059
|
color: cursorIndex === backIndex ? "cyan" : "white",
|
|
1228
1060
|
bold: cursorIndex === backIndex,
|
|
@@ -1233,7 +1065,7 @@ function SetupPage({ onBack }) {
|
|
|
1233
1065
|
}
|
|
1234
1066
|
) })
|
|
1235
1067
|
] }),
|
|
1236
|
-
/* @__PURE__ */
|
|
1068
|
+
/* @__PURE__ */ jsx6(Box5, { marginTop: 1, children: /* @__PURE__ */ jsxs5(Text6, { color: "gray", children: [
|
|
1237
1069
|
"Up/Down Navigate ",
|
|
1238
1070
|
"\u2022",
|
|
1239
1071
|
" Enter Select ",
|
|
@@ -1244,29 +1076,29 @@ function SetupPage({ onBack }) {
|
|
|
1244
1076
|
}
|
|
1245
1077
|
|
|
1246
1078
|
// src/tui/pages/OnboardingPage.tsx
|
|
1247
|
-
import { useEffect as
|
|
1248
|
-
import { Box as
|
|
1249
|
-
import
|
|
1079
|
+
import { useEffect as useEffect9, useCallback as useCallback7, useState as useState9, useMemo as useMemo4 } from "react";
|
|
1080
|
+
import { Box as Box6, Text as Text7, useInput as useInput3 } from "ink";
|
|
1081
|
+
import Spinner4 from "ink-spinner";
|
|
1250
1082
|
import chalk2 from "chalk";
|
|
1251
1083
|
|
|
1252
1084
|
// src/tui/hooks/useAuth.ts
|
|
1253
|
-
import { useState as
|
|
1085
|
+
import { useState as useState8, useCallback as useCallback6, useRef as useRef4, useEffect as useEffect8 } from "react";
|
|
1254
1086
|
import open from "open";
|
|
1255
1087
|
var POLL_INTERVAL = 2e3;
|
|
1256
1088
|
var MAX_ATTEMPTS = 30;
|
|
1257
1089
|
function useAuth() {
|
|
1258
|
-
const [status, setStatus] =
|
|
1259
|
-
const [authUrl, setAuthUrl] =
|
|
1260
|
-
const [timeRemaining, setTimeRemaining] =
|
|
1261
|
-
const cancelledRef =
|
|
1262
|
-
const timerRef =
|
|
1263
|
-
|
|
1090
|
+
const [status, setStatus] = useState8("idle");
|
|
1091
|
+
const [authUrl, setAuthUrl] = useState8(null);
|
|
1092
|
+
const [timeRemaining, setTimeRemaining] = useState8(0);
|
|
1093
|
+
const cancelledRef = useRef4(false);
|
|
1094
|
+
const timerRef = useRef4(null);
|
|
1095
|
+
useEffect8(() => {
|
|
1264
1096
|
return () => {
|
|
1265
1097
|
cancelledRef.current = true;
|
|
1266
1098
|
if (timerRef.current) clearInterval(timerRef.current);
|
|
1267
1099
|
};
|
|
1268
1100
|
}, []);
|
|
1269
|
-
const cancel =
|
|
1101
|
+
const cancel = useCallback6(() => {
|
|
1270
1102
|
cancelledRef.current = true;
|
|
1271
1103
|
if (timerRef.current) {
|
|
1272
1104
|
clearInterval(timerRef.current);
|
|
@@ -1276,7 +1108,7 @@ function useAuth() {
|
|
|
1276
1108
|
setAuthUrl(null);
|
|
1277
1109
|
setTimeRemaining(0);
|
|
1278
1110
|
}, []);
|
|
1279
|
-
const startAuth =
|
|
1111
|
+
const startAuth = useCallback6(() => {
|
|
1280
1112
|
cancelledRef.current = false;
|
|
1281
1113
|
const run = async () => {
|
|
1282
1114
|
try {
|
|
@@ -1334,10 +1166,10 @@ function useAuth() {
|
|
|
1334
1166
|
}
|
|
1335
1167
|
|
|
1336
1168
|
// src/tui/pages/OnboardingPage.tsx
|
|
1337
|
-
import { Fragment as Fragment3, jsx as
|
|
1169
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1338
1170
|
var SHIMMER_SPEED = 35;
|
|
1339
1171
|
function useShimmerLogo() {
|
|
1340
|
-
const [frame, setFrame] =
|
|
1172
|
+
const [frame, setFrame] = useState9(0);
|
|
1341
1173
|
const lines = useMemo4(() => LogoString.split("\n"), []);
|
|
1342
1174
|
const totalChars = useMemo4(() => {
|
|
1343
1175
|
let count = 0;
|
|
@@ -1349,7 +1181,7 @@ function useShimmerLogo() {
|
|
|
1349
1181
|
return count;
|
|
1350
1182
|
}, [lines]);
|
|
1351
1183
|
const cycleLength = totalChars + 40;
|
|
1352
|
-
|
|
1184
|
+
useEffect9(() => {
|
|
1353
1185
|
const interval = setInterval(() => {
|
|
1354
1186
|
setFrame((f) => (f + 1) % cycleLength);
|
|
1355
1187
|
}, SHIMMER_SPEED);
|
|
@@ -1402,16 +1234,16 @@ function OnboardingPage({ onComplete }) {
|
|
|
1402
1234
|
cancel: cancelAuth
|
|
1403
1235
|
} = useAuth();
|
|
1404
1236
|
const shimmerLogo = useShimmerLogo();
|
|
1405
|
-
|
|
1237
|
+
useEffect9(() => {
|
|
1406
1238
|
if (authStatus === "success") {
|
|
1407
1239
|
const timer = setTimeout(() => onComplete(), 1500);
|
|
1408
1240
|
return () => clearTimeout(timer);
|
|
1409
1241
|
}
|
|
1410
1242
|
}, [authStatus, onComplete]);
|
|
1411
|
-
|
|
1243
|
+
useEffect9(() => {
|
|
1412
1244
|
return () => cancelAuth();
|
|
1413
1245
|
}, []);
|
|
1414
|
-
const handleAction =
|
|
1246
|
+
const handleAction = useCallback7(() => {
|
|
1415
1247
|
cancelAuth();
|
|
1416
1248
|
startAuth();
|
|
1417
1249
|
}, [cancelAuth, startAuth]);
|
|
@@ -1421,57 +1253,66 @@ function OnboardingPage({ onComplete }) {
|
|
|
1421
1253
|
handleAction();
|
|
1422
1254
|
}
|
|
1423
1255
|
});
|
|
1424
|
-
return /* @__PURE__ */
|
|
1425
|
-
/* @__PURE__ */
|
|
1426
|
-
/* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1428
|
-
/* @__PURE__ */
|
|
1429
|
-
/* @__PURE__ */
|
|
1430
|
-
authStatus === "idle" && /* @__PURE__ */
|
|
1431
|
-
/* @__PURE__ */
|
|
1432
|
-
/* @__PURE__ */
|
|
1256
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", flexGrow: 1, children: [
|
|
1257
|
+
/* @__PURE__ */ jsx7(Box6, { flexGrow: 1 }),
|
|
1258
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", alignItems: "center", children: [
|
|
1259
|
+
/* @__PURE__ */ jsx7(Text7, { children: shimmerLogo }),
|
|
1260
|
+
/* @__PURE__ */ jsx7(Box6, { flexDirection: "column", alignItems: "center", marginTop: 2, children: /* @__PURE__ */ jsx7(Text7, { bold: true, color: "white", children: "MindStudio Local Tunnel" }) }),
|
|
1261
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", alignItems: "center", children: [
|
|
1262
|
+
authStatus === "idle" && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1263
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: "Connect your MindStudio account to get started." }),
|
|
1264
|
+
/* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: "Press any key to Connect Account" }) })
|
|
1433
1265
|
] }),
|
|
1434
|
-
(authStatus === "expired" || authStatus === "timeout") && /* @__PURE__ */
|
|
1435
|
-
/* @__PURE__ */
|
|
1436
|
-
/* @__PURE__ */
|
|
1266
|
+
(authStatus === "expired" || authStatus === "timeout") && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1267
|
+
/* @__PURE__ */ jsx7(Text7, { color: "red", children: authStatus === "expired" ? "Authorization expired." : "Authorization timed out." }),
|
|
1268
|
+
/* @__PURE__ */ jsx7(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: "Press any key to Try Again" }) })
|
|
1437
1269
|
] }),
|
|
1438
|
-
authStatus === "waiting" && /* @__PURE__ */
|
|
1439
|
-
/* @__PURE__ */
|
|
1440
|
-
/* @__PURE__ */
|
|
1441
|
-
/* @__PURE__ */
|
|
1270
|
+
authStatus === "waiting" && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1271
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1272
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: /* @__PURE__ */ jsx7(Spinner4, { type: "dots" }) }),
|
|
1273
|
+
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
1442
1274
|
" ",
|
|
1443
1275
|
"Waiting for browser authorization... (",
|
|
1444
1276
|
timeRemaining,
|
|
1445
1277
|
"s remaining)"
|
|
1446
1278
|
] })
|
|
1447
1279
|
] }),
|
|
1448
|
-
authUrl && /* @__PURE__ */
|
|
1449
|
-
/* @__PURE__ */
|
|
1450
|
-
/* @__PURE__ */
|
|
1280
|
+
authUrl && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", alignItems: "center", marginTop: 1, children: [
|
|
1281
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: "If browser didn't open, visit:" }),
|
|
1282
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: authUrl })
|
|
1451
1283
|
] })
|
|
1452
1284
|
] }),
|
|
1453
|
-
authStatus === "success" && /* @__PURE__ */
|
|
1285
|
+
authStatus === "success" && /* @__PURE__ */ jsxs6(Text7, { color: "green", children: [
|
|
1454
1286
|
"\u2713",
|
|
1455
1287
|
" Authenticated!"
|
|
1456
1288
|
] })
|
|
1457
1289
|
] })
|
|
1458
1290
|
] }),
|
|
1459
|
-
/* @__PURE__ */
|
|
1291
|
+
/* @__PURE__ */ jsx7(Box6, { flexGrow: 1 })
|
|
1460
1292
|
] });
|
|
1461
1293
|
}
|
|
1462
1294
|
|
|
1463
1295
|
// src/tui/App.tsx
|
|
1464
|
-
import { Fragment as Fragment4, jsx as
|
|
1296
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1297
|
+
var MODEL_TYPE_MAP = {
|
|
1298
|
+
text: "llm_chat",
|
|
1299
|
+
image: "image_generation",
|
|
1300
|
+
video: "video_generation"
|
|
1301
|
+
};
|
|
1465
1302
|
function App({ runner }) {
|
|
1466
1303
|
const { exit } = useApp();
|
|
1467
|
-
const { stdout } =
|
|
1304
|
+
const { stdout } = useStdout6();
|
|
1468
1305
|
const {
|
|
1469
1306
|
status: connectionStatus,
|
|
1470
1307
|
environment,
|
|
1471
1308
|
error: connectionError,
|
|
1472
1309
|
retry: retryConnection
|
|
1473
1310
|
} = useConnection();
|
|
1474
|
-
const {
|
|
1311
|
+
const {
|
|
1312
|
+
providers,
|
|
1313
|
+
loading: providersLoading,
|
|
1314
|
+
refresh: refreshProviders
|
|
1315
|
+
} = useSetupProviders();
|
|
1475
1316
|
const {
|
|
1476
1317
|
models,
|
|
1477
1318
|
warnings: modelWarnings,
|
|
@@ -1479,47 +1320,86 @@ function App({ runner }) {
|
|
|
1479
1320
|
refresh: refreshModels
|
|
1480
1321
|
} = useModels();
|
|
1481
1322
|
const { requests } = useRequests();
|
|
1482
|
-
const {
|
|
1323
|
+
const {
|
|
1324
|
+
syncedNames,
|
|
1325
|
+
syncedModels,
|
|
1326
|
+
refresh: refreshSynced
|
|
1327
|
+
} = useSyncedModels(connectionStatus);
|
|
1483
1328
|
const shouldOnboard = getApiKey() === void 0;
|
|
1484
|
-
const [page, setPage] =
|
|
1329
|
+
const [page, setPage] = useState10(
|
|
1485
1330
|
shouldOnboard ? "onboarding" : "dashboard"
|
|
1486
1331
|
);
|
|
1487
|
-
|
|
1332
|
+
const [syncStatus, setSyncStatus] = useState10(
|
|
1333
|
+
"idle"
|
|
1334
|
+
);
|
|
1335
|
+
const syncTimerRef = useRef5();
|
|
1336
|
+
const lastSyncPayloadRef = useRef5("");
|
|
1337
|
+
useEffect10(() => {
|
|
1338
|
+
if (connectionStatus === "not_authenticated") {
|
|
1339
|
+
setPage("onboarding");
|
|
1340
|
+
}
|
|
1341
|
+
}, [connectionStatus]);
|
|
1342
|
+
useEffect10(() => {
|
|
1488
1343
|
if (page === "dashboard") {
|
|
1489
1344
|
refreshAll();
|
|
1490
1345
|
}
|
|
1491
1346
|
}, [page]);
|
|
1492
|
-
|
|
1493
|
-
if (connectionStatus === "connected" &&
|
|
1494
|
-
runner.start(
|
|
1347
|
+
useEffect10(() => {
|
|
1348
|
+
if (connectionStatus === "connected" && syncedModels.length > 0) {
|
|
1349
|
+
runner.start(syncedModels);
|
|
1495
1350
|
}
|
|
1496
|
-
}, [connectionStatus,
|
|
1497
|
-
|
|
1498
|
-
const refreshAll =
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1351
|
+
}, [connectionStatus, syncedModels, runner]);
|
|
1352
|
+
useEffect10(() => () => runner.stop(), [runner]);
|
|
1353
|
+
const refreshAll = useCallback8(
|
|
1354
|
+
async (silent = false) => {
|
|
1355
|
+
if (!silent) setSyncStatus("syncing");
|
|
1356
|
+
const [discoveredModels] = await Promise.all([
|
|
1357
|
+
refreshModels(),
|
|
1358
|
+
refreshProviders()
|
|
1359
|
+
]);
|
|
1360
|
+
const modelsToSync = discoveredModels.filter((m) => !m.statusHint).map((m) => ({
|
|
1361
|
+
name: m.name,
|
|
1362
|
+
provider: m.provider,
|
|
1363
|
+
type: MODEL_TYPE_MAP[m.capability] || "llm_chat",
|
|
1364
|
+
parameters: m.parameters
|
|
1365
|
+
}));
|
|
1366
|
+
const payload = JSON.stringify(modelsToSync);
|
|
1367
|
+
if (payload !== lastSyncPayloadRef.current && modelsToSync.length > 0) {
|
|
1368
|
+
try {
|
|
1369
|
+
await syncModels(modelsToSync);
|
|
1370
|
+
lastSyncPayloadRef.current = payload;
|
|
1371
|
+
} catch {
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
await refreshSynced();
|
|
1375
|
+
if (!silent) {
|
|
1376
|
+
setSyncStatus("synced");
|
|
1377
|
+
clearTimeout(syncTimerRef.current);
|
|
1378
|
+
syncTimerRef.current = setTimeout(() => setSyncStatus("idle"), 1500);
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
[refreshProviders, refreshModels, refreshSynced]
|
|
1382
|
+
);
|
|
1383
|
+
useEffect10(() => {
|
|
1384
|
+
if (connectionStatus !== "connected" || page !== "dashboard") return;
|
|
1385
|
+
const interval = setInterval(() => refreshAll(true), 1500);
|
|
1386
|
+
return () => clearInterval(interval);
|
|
1387
|
+
}, [connectionStatus, page, refreshAll]);
|
|
1388
|
+
const handleQuit = useCallback8(() => {
|
|
1506
1389
|
runner.stop();
|
|
1507
1390
|
exit();
|
|
1508
1391
|
}, [runner, exit]);
|
|
1509
|
-
const handleOnboardingComplete =
|
|
1392
|
+
const handleOnboardingComplete = useCallback8(() => {
|
|
1510
1393
|
retryConnection();
|
|
1511
1394
|
refreshAll();
|
|
1512
1395
|
setPage("dashboard");
|
|
1513
1396
|
}, [retryConnection, refreshAll]);
|
|
1514
|
-
const handleNavigate =
|
|
1397
|
+
const handleNavigate = useCallback8(
|
|
1515
1398
|
(id) => {
|
|
1516
1399
|
switch (id) {
|
|
1517
1400
|
case "auth":
|
|
1518
1401
|
setPage("onboarding");
|
|
1519
1402
|
break;
|
|
1520
|
-
case "register":
|
|
1521
|
-
setPage("sync");
|
|
1522
|
-
break;
|
|
1523
1403
|
case "setup":
|
|
1524
1404
|
setPage("setup");
|
|
1525
1405
|
break;
|
|
@@ -1531,12 +1411,12 @@ function App({ runner }) {
|
|
|
1531
1411
|
break;
|
|
1532
1412
|
}
|
|
1533
1413
|
},
|
|
1534
|
-
[
|
|
1414
|
+
[refreshAll, handleQuit]
|
|
1535
1415
|
);
|
|
1536
1416
|
const subpageMenuItems = [
|
|
1537
1417
|
{ id: "back", label: "Back", description: "Return to dashboard" }
|
|
1538
1418
|
];
|
|
1539
|
-
const handleSubpageNavigate =
|
|
1419
|
+
const handleSubpageNavigate = useCallback8(
|
|
1540
1420
|
(id) => {
|
|
1541
1421
|
if (id === "back") {
|
|
1542
1422
|
setPage("dashboard");
|
|
@@ -1546,32 +1426,51 @@ function App({ runner }) {
|
|
|
1546
1426
|
},
|
|
1547
1427
|
[handleNavigate]
|
|
1548
1428
|
);
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
|
|
1429
|
+
const [termSize, setTermSize] = useState10({
|
|
1430
|
+
rows: stdout?.rows ?? 24,
|
|
1431
|
+
columns: stdout?.columns ?? 80
|
|
1432
|
+
});
|
|
1433
|
+
useEffect10(() => {
|
|
1434
|
+
if (!stdout) return;
|
|
1435
|
+
const onResize = () => {
|
|
1436
|
+
stdout.write("\x1B[2J\x1B[H");
|
|
1437
|
+
setTermSize({ rows: stdout.rows, columns: stdout.columns });
|
|
1438
|
+
};
|
|
1439
|
+
stdout.on("resize", onResize);
|
|
1440
|
+
return () => {
|
|
1441
|
+
stdout.off("resize", onResize);
|
|
1442
|
+
};
|
|
1443
|
+
}, [stdout]);
|
|
1444
|
+
const termHeight = termSize.rows - 4;
|
|
1445
|
+
const compactHeader = termSize.rows <= 45 || termSize.columns <= 90;
|
|
1446
|
+
return /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", height: termHeight, overflow: "hidden", children: page === "onboarding" ? /* @__PURE__ */ jsx8(OnboardingPage, { onComplete: handleOnboardingComplete }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
1447
|
+
/* @__PURE__ */ jsx8(
|
|
1552
1448
|
Header,
|
|
1553
1449
|
{
|
|
1554
1450
|
connection: connectionStatus,
|
|
1555
1451
|
environment,
|
|
1556
1452
|
configPath: getConfigPath(),
|
|
1557
|
-
connectionError
|
|
1453
|
+
connectionError,
|
|
1454
|
+
compact: compactHeader
|
|
1558
1455
|
}
|
|
1559
1456
|
),
|
|
1560
|
-
page === "dashboard" && /* @__PURE__ */
|
|
1457
|
+
page === "dashboard" && /* @__PURE__ */ jsx8(
|
|
1561
1458
|
DashboardPage,
|
|
1562
1459
|
{
|
|
1563
1460
|
requests,
|
|
1564
1461
|
models,
|
|
1565
1462
|
modelWarnings,
|
|
1463
|
+
providers,
|
|
1464
|
+
providersLoading,
|
|
1566
1465
|
syncedNames,
|
|
1567
1466
|
modelsLoading,
|
|
1467
|
+
syncStatus,
|
|
1568
1468
|
onNavigate: handleNavigate
|
|
1569
1469
|
}
|
|
1570
1470
|
),
|
|
1571
|
-
page === "setup" && /* @__PURE__ */
|
|
1572
|
-
page
|
|
1573
|
-
page !== "dashboard" && page !== "setup" && /* @__PURE__ */
|
|
1574
|
-
page !== "dashboard" && page !== "setup" && /* @__PURE__ */ jsx9(
|
|
1471
|
+
page === "setup" && /* @__PURE__ */ jsx8(SetupPage, { onBack: () => setPage("dashboard") }),
|
|
1472
|
+
page !== "dashboard" && page !== "setup" && /* @__PURE__ */ jsx8(Box7, { flexGrow: 1 }),
|
|
1473
|
+
page !== "dashboard" && page !== "setup" && /* @__PURE__ */ jsx8(
|
|
1575
1474
|
NavigationMenu,
|
|
1576
1475
|
{
|
|
1577
1476
|
items: subpageMenuItems,
|
|
@@ -1621,8 +1520,8 @@ async function checkForUpdate() {
|
|
|
1621
1520
|
}
|
|
1622
1521
|
|
|
1623
1522
|
// src/tui/components/UpdatePrompt.tsx
|
|
1624
|
-
import { Box as
|
|
1625
|
-
import { jsx as
|
|
1523
|
+
import { Box as Box8, Text as Text8, useInput as useInput4 } from "ink";
|
|
1524
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1626
1525
|
function UpdatePrompt({
|
|
1627
1526
|
currentVersion,
|
|
1628
1527
|
latestVersion,
|
|
@@ -1635,10 +1534,10 @@ function UpdatePrompt({
|
|
|
1635
1534
|
onChoice(false);
|
|
1636
1535
|
}
|
|
1637
1536
|
});
|
|
1638
|
-
return /* @__PURE__ */
|
|
1639
|
-
/* @__PURE__ */
|
|
1640
|
-
/* @__PURE__ */
|
|
1641
|
-
/* @__PURE__ */
|
|
1537
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
|
|
1538
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1539
|
+
/* @__PURE__ */ jsx9(Text8, { color: "yellow", bold: true, children: "Update available:" }),
|
|
1540
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1642
1541
|
" ",
|
|
1643
1542
|
"v",
|
|
1644
1543
|
currentVersion,
|
|
@@ -1648,20 +1547,20 @@ function UpdatePrompt({
|
|
|
1648
1547
|
latestVersion
|
|
1649
1548
|
] })
|
|
1650
1549
|
] }),
|
|
1651
|
-
/* @__PURE__ */
|
|
1550
|
+
/* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1652
1551
|
"Press ",
|
|
1653
|
-
/* @__PURE__ */
|
|
1552
|
+
/* @__PURE__ */ jsx9(Text8, { bold: true, color: "cyan", children: "y" }),
|
|
1654
1553
|
" to update, any other key to skip"
|
|
1655
1554
|
] }) })
|
|
1656
1555
|
] });
|
|
1657
1556
|
}
|
|
1658
1557
|
|
|
1659
1558
|
// src/tui/index.tsx
|
|
1660
|
-
import { jsx as
|
|
1559
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1661
1560
|
async function promptForUpdate(currentVersion, latestVersion) {
|
|
1662
1561
|
return new Promise((resolve) => {
|
|
1663
1562
|
const { unmount } = render(
|
|
1664
|
-
/* @__PURE__ */
|
|
1563
|
+
/* @__PURE__ */ jsx10(
|
|
1665
1564
|
UpdatePrompt,
|
|
1666
1565
|
{
|
|
1667
1566
|
currentVersion,
|
|
@@ -1703,7 +1602,7 @@ async function startTUI() {
|
|
|
1703
1602
|
}
|
|
1704
1603
|
const runner = new TunnelRunner();
|
|
1705
1604
|
const { waitUntilExit } = render(
|
|
1706
|
-
/* @__PURE__ */
|
|
1605
|
+
/* @__PURE__ */ jsx10(App, { runner }),
|
|
1707
1606
|
{
|
|
1708
1607
|
exitOnCtrlC: true
|
|
1709
1608
|
}
|
|
@@ -1714,4 +1613,4 @@ async function startTUI() {
|
|
|
1714
1613
|
export {
|
|
1715
1614
|
startTUI
|
|
1716
1615
|
};
|
|
1717
|
-
//# sourceMappingURL=tui-
|
|
1616
|
+
//# sourceMappingURL=tui-4ZB4JAV4.js.map
|