@mindstudio-ai/local-model-tunnel 0.4.0 → 0.4.2

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.
@@ -20,19 +20,21 @@ import {
20
20
  setUserId,
21
21
  syncModels,
22
22
  verifyApiKey
23
- } from "./chunk-44NXQXRB.js";
23
+ } from "./chunk-KLOTDVWL.js";
24
24
 
25
25
  // src/tui/index.tsx
26
26
  import { render } from "ink";
27
27
  import { execFileSync, execSync as execSync2 } from "child_process";
28
28
 
29
29
  // src/tui/App.tsx
30
- import { useEffect as useEffect15, useCallback as useCallback10, useState as useState15, useRef as useRef9 } from "react";
30
+ import { useEffect as useEffect16, useCallback as useCallback10, useState as useState16, useRef as useRef9 } from "react";
31
31
  import { Box as Box10, useApp, useStdout as useStdout7 } from "ink";
32
32
 
33
33
  // src/tui/components/Header.tsx
34
+ import { useState, useEffect, useMemo } from "react";
34
35
  import os from "os";
35
36
  import { Box, Text } from "ink";
37
+ import chalk from "chalk";
36
38
  import { createRequire } from "module";
37
39
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
38
40
  var require2 = createRequire(import.meta.url);
@@ -46,6 +48,52 @@ var LogoString = ` .=+-. :++.
46
48
  =@@@@@@@-.@@@@@@@#.-@@@@@@+
47
49
  :@@@@@@: +@@@@@#. .@@@@@@:
48
50
  .++: .-*-. .++:`;
51
+ function useActiveShimmerLogo(active) {
52
+ const [frame, setFrame] = useState(0);
53
+ const lines = useMemo(() => LogoString.split("\n"), []);
54
+ const totalChars = useMemo(() => {
55
+ let count = 0;
56
+ for (const line of lines) {
57
+ for (const ch of line) {
58
+ if (ch !== " " && ch !== " ") count++;
59
+ }
60
+ }
61
+ return count;
62
+ }, [lines]);
63
+ useEffect(() => {
64
+ if (!active) return;
65
+ const interval = setInterval(() => {
66
+ setFrame((f) => f + 1);
67
+ }, 25);
68
+ return () => clearInterval(interval);
69
+ }, [active]);
70
+ return useMemo(() => {
71
+ if (!active) return null;
72
+ const waveLength = 20;
73
+ let charIdx = 0;
74
+ return lines.map((line) => {
75
+ let result = "";
76
+ for (let i = 0; i < line.length; i++) {
77
+ const ch = line[i];
78
+ if (ch === " " || ch === " ") {
79
+ result += ch;
80
+ continue;
81
+ }
82
+ const phase = (charIdx - frame * 0.5) / waveLength * Math.PI * 2;
83
+ const brightness = 0.65 + 0.35 * Math.sin(phase);
84
+ if (brightness >= 0.85) {
85
+ result += chalk.cyanBright(ch);
86
+ } else if (brightness >= 0.65) {
87
+ result += chalk.cyan(ch);
88
+ } else {
89
+ result += chalk.rgb(0, 140, 160)(ch);
90
+ }
91
+ charIdx++;
92
+ }
93
+ return result;
94
+ }).join("\n");
95
+ }, [active, frame, lines, totalChars]);
96
+ }
49
97
  var getConnectionDisplay = (status) => {
50
98
  switch (status) {
51
99
  case "connected":
@@ -65,9 +113,11 @@ function Header({
65
113
  environment,
66
114
  configPath,
67
115
  connectionError,
68
- compact
116
+ compact,
117
+ hasActiveRequest
69
118
  }) {
70
119
  const { color: connectionColor, text: connectionText } = getConnectionDisplay(connection);
120
+ const shimmerLogo = useActiveShimmerLogo(!compact && !!hasActiveRequest);
71
121
  return /* @__PURE__ */ jsxs(
72
122
  Box,
73
123
  {
@@ -79,7 +129,7 @@ function Header({
79
129
  paddingY: 1,
80
130
  width: "100%",
81
131
  children: [
82
- !compact && /* @__PURE__ */ jsx(Box, { paddingLeft: 3, children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: LogoString }) }),
132
+ !compact && /* @__PURE__ */ jsx(Box, { paddingLeft: 3, children: shimmerLogo ? /* @__PURE__ */ jsx(Text, { children: shimmerLogo }) : /* @__PURE__ */ jsx(Text, { color: "cyan", children: LogoString }) }),
83
133
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: compact ? 0 : 4, children: [
84
134
  /* @__PURE__ */ jsxs(Box, { children: [
85
135
  /* @__PURE__ */ jsx(Text, { bold: true, color: "white", children: "MindStudio Local Tunnel" }),
@@ -92,10 +142,6 @@ function Header({
92
142
  /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: "[LOCAL]" })
93
143
  ] })
94
144
  ] }),
95
- !compact && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
96
- "v",
97
- pkg.version
98
- ] }),
99
145
  /* @__PURE__ */ jsxs(Text, { color: connectionColor, children: [
100
146
  "\u25CF ",
101
147
  connectionText
@@ -104,6 +150,10 @@ function Header({
104
150
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
105
151
  "Config: ",
106
152
  configPath.replace(os.homedir(), "~")
153
+ ] }),
154
+ !compact && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
155
+ "v",
156
+ pkg.version
107
157
  ] })
108
158
  ] })
109
159
  ]
@@ -112,18 +162,22 @@ function Header({
112
162
  }
113
163
 
114
164
  // src/tui/components/NavigationMenu.tsx
115
- import { useState, useEffect } from "react";
165
+ import { useState as useState2, useEffect as useEffect2 } from "react";
116
166
  import { Box as Box2, Text as Text2, useInput, useStdout } from "ink";
117
167
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
118
- function NavigationMenu({ items, onSelect, title }) {
168
+ function NavigationMenu({
169
+ items,
170
+ onSelect,
171
+ title
172
+ }) {
119
173
  const { stdout } = useStdout();
120
174
  const compact = (stdout?.rows ?? 24) < 40;
121
175
  const getDefaultIndex = () => {
122
176
  const firstIdx = items.findIndex((i) => !i.disabled && !i.isSeparator);
123
177
  return firstIdx >= 0 ? firstIdx : 0;
124
178
  };
125
- const [selectedIndex, setSelectedIndex] = useState(getDefaultIndex);
126
- useEffect(() => {
179
+ const [selectedIndex, setSelectedIndex] = useState2(getDefaultIndex);
180
+ useEffect2(() => {
127
181
  setSelectedIndex(getDefaultIndex());
128
182
  }, [items]);
129
183
  const selectableItems = items.filter((i) => !i.isSeparator);
@@ -159,60 +213,98 @@ function NavigationMenu({ items, onSelect, title }) {
159
213
  const hasBack = items.some((i) => i.id === "back");
160
214
  if (compact) {
161
215
  const selectedItem = items[selectedIndex];
162
- 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) => {
163
- if (item.isSeparator) return null;
164
- const isSelected = index === selectedIndex;
165
- return /* @__PURE__ */ jsx2(
166
- Text2,
167
- {
168
- color: item.disabled ? "gray" : isSelected ? "cyan" : "white",
169
- bold: isSelected,
170
- wrap: "truncate-end",
171
- children: isSelected ? `\u276F ${item.label}` : ` ${item.label}`
172
- },
173
- item.id
174
- );
175
- }) }) });
216
+ return /* @__PURE__ */ jsx2(
217
+ Box2,
218
+ {
219
+ flexDirection: "column",
220
+ paddingX: 1,
221
+ borderStyle: "single",
222
+ borderTop: true,
223
+ borderBottom: false,
224
+ borderLeft: false,
225
+ borderRight: false,
226
+ borderColor: "gray",
227
+ children: /* @__PURE__ */ jsx2(Box2, { height: 1, overflow: "hidden", gap: 1, children: items.map((item, index) => {
228
+ if (item.isSeparator) return null;
229
+ const isSelected = index === selectedIndex;
230
+ return /* @__PURE__ */ jsx2(
231
+ Text2,
232
+ {
233
+ color: item.disabled ? "gray" : isSelected ? "cyan" : "white",
234
+ bold: isSelected,
235
+ wrap: "truncate-end",
236
+ children: isSelected ? `\u276F ${item.label}` : ` ${item.label}`
237
+ },
238
+ item.id
239
+ );
240
+ }) })
241
+ }
242
+ );
176
243
  }
177
- const separatorExtraLines = items.filter((item, idx) => item.isSeparator && idx > 0).length;
244
+ const separatorExtraLines = items.filter(
245
+ (item, idx) => item.isSeparator && idx > 0
246
+ ).length;
178
247
  const menuHeight = items.length + 4 + separatorExtraLines;
179
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, marginBottom: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: "gray", children: [
180
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "gray", children: title ?? "Actions" }) }),
181
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: items.map((item, index) => {
182
- if (item.isSeparator) {
183
- return /* @__PURE__ */ jsx2(Box2, { marginTop: index > 0 ? 1 : 0, children: item.label ? /* @__PURE__ */ jsx2(Text2, { bold: true, color: item.color ?? "gray", wrap: "truncate-end", children: item.label }) : null }, item.id);
184
- }
185
- const isSelected = index === selectedIndex;
186
- const prefix = isSelected ? "\u276F" : " ";
187
- if (item.disabled) {
188
- return /* @__PURE__ */ jsx2(Box2, { height: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
189
- prefix,
190
- " ",
191
- item.label,
192
- item.disabledReason ? ` (${item.disabledReason})` : ""
193
- ] }) }, item.id);
194
- }
195
- return /* @__PURE__ */ jsxs2(Box2, { height: 1, overflow: "hidden", children: [
196
- /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "cyan" : "white", bold: isSelected, wrap: "truncate-end", children: [
197
- prefix,
198
- " ",
199
- item.label
200
- ] }),
201
- isSelected && /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
202
- " - ",
203
- item.description
204
- ] })
205
- ] }, item.id);
206
- }) }),
207
- /* @__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" }) })
208
- ] });
248
+ return /* @__PURE__ */ jsxs2(
249
+ Box2,
250
+ {
251
+ flexDirection: "column",
252
+ paddingX: 1,
253
+ marginBottom: 1,
254
+ borderStyle: "single",
255
+ borderTop: true,
256
+ borderBottom: false,
257
+ borderLeft: false,
258
+ borderRight: false,
259
+ borderColor: "gray",
260
+ children: [
261
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "gray", children: title ?? "Actions" }) }),
262
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: items.map((item, index) => {
263
+ if (item.isSeparator) {
264
+ return /* @__PURE__ */ jsx2(Box2, { marginTop: index > 0 ? 1 : 0, children: item.label ? /* @__PURE__ */ jsx2(Text2, { bold: true, color: item.color ?? "gray", wrap: "truncate-end", children: item.label }) : null }, item.id);
265
+ }
266
+ const isSelected = index === selectedIndex;
267
+ const prefix = isSelected ? "\u276F" : " ";
268
+ if (item.disabled) {
269
+ return /* @__PURE__ */ jsx2(Box2, { height: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
270
+ prefix,
271
+ " ",
272
+ item.label,
273
+ item.disabledReason ? ` (${item.disabledReason})` : ""
274
+ ] }) }, item.id);
275
+ }
276
+ return /* @__PURE__ */ jsxs2(Box2, { height: 1, overflow: "hidden", children: [
277
+ /* @__PURE__ */ jsxs2(
278
+ Text2,
279
+ {
280
+ color: isSelected ? "cyan" : "white",
281
+ bold: isSelected,
282
+ wrap: "truncate-end",
283
+ children: [
284
+ prefix,
285
+ " ",
286
+ item.label
287
+ ]
288
+ }
289
+ ),
290
+ isSelected && /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
291
+ " ",
292
+ "- ",
293
+ item.description
294
+ ] })
295
+ ] }, item.id);
296
+ }) }),
297
+ /* @__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" }) })
298
+ ]
299
+ }
300
+ );
209
301
  }
210
302
 
211
303
  // src/tui/hooks/useConnection.ts
212
- import { useState as useState2, useEffect as useEffect2, useCallback } from "react";
304
+ import { useState as useState3, useEffect as useEffect3, useCallback } from "react";
213
305
  function useConnection() {
214
- const [status, setStatus] = useState2("connecting");
215
- const [error, setError] = useState2(null);
306
+ const [status, setStatus] = useState3("connecting");
307
+ const [error, setError] = useState3(null);
216
308
  const environment = getEnvironment();
217
309
  const connect = useCallback(async () => {
218
310
  setStatus("connecting");
@@ -235,7 +327,7 @@ function useConnection() {
235
327
  setError(err instanceof Error ? err.message : "Connection failed");
236
328
  }
237
329
  }, []);
238
- useEffect2(() => {
330
+ useEffect3(() => {
239
331
  connect();
240
332
  }, [connect]);
241
333
  return {
@@ -246,14 +338,65 @@ function useConnection() {
246
338
  };
247
339
  }
248
340
 
249
- // src/tui/models/hooks/useSetupProviders.ts
250
- import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2, useRef } from "react";
251
- function useSetupProviders() {
252
- const [providers, setProviders] = useState3([]);
253
- const [loading, setLoading] = useState3(true);
254
- const [refreshing, setRefreshing] = useState3(false);
341
+ // src/tui/interfaces/hooks/useEditorSessions.ts
342
+ import { useState as useState4, useEffect as useEffect4, useCallback as useCallback2, useRef } from "react";
343
+ function useEditorSessions() {
344
+ const [sessions, setSessions] = useState4([]);
345
+ const [loading, setLoading] = useState4(true);
346
+ const [error, setError] = useState4(null);
347
+ const [refreshStatus, setRefreshStatus] = useState4("idle");
255
348
  const initialLoadDone = useRef(false);
349
+ const timerRef = useRef();
350
+ const pollRef = useRef();
256
351
  const refresh = useCallback2(async () => {
352
+ if (!initialLoadDone.current) {
353
+ setLoading(true);
354
+ } else {
355
+ setRefreshStatus("refreshing");
356
+ }
357
+ setError(null);
358
+ try {
359
+ const data = await getEditorSessions();
360
+ setSessions(data);
361
+ initialLoadDone.current = true;
362
+ setRefreshStatus("refreshed");
363
+ clearTimeout(timerRef.current);
364
+ timerRef.current = setTimeout(() => setRefreshStatus("idle"), 1500);
365
+ } catch (err) {
366
+ setError(err instanceof Error ? err.message : "Failed to fetch sessions");
367
+ setRefreshStatus("idle");
368
+ } finally {
369
+ setLoading(false);
370
+ }
371
+ }, []);
372
+ useEffect4(() => {
373
+ refresh();
374
+ }, [refresh]);
375
+ useEffect4(() => {
376
+ pollRef.current = setInterval(async () => {
377
+ if (!initialLoadDone.current) return;
378
+ try {
379
+ const data = await getEditorSessions();
380
+ setSessions(data);
381
+ } catch {
382
+ }
383
+ }, 5e3);
384
+ return () => clearInterval(pollRef.current);
385
+ }, []);
386
+ useEffect4(() => {
387
+ return () => clearTimeout(timerRef.current);
388
+ }, []);
389
+ return { sessions, loading, error, refreshStatus, refresh };
390
+ }
391
+
392
+ // src/tui/models/hooks/useSetupProviders.ts
393
+ import { useState as useState5, useEffect as useEffect5, useCallback as useCallback3, useRef as useRef2 } from "react";
394
+ function useSetupProviders() {
395
+ const [providers, setProviders] = useState5([]);
396
+ const [loading, setLoading] = useState5(true);
397
+ const [refreshing, setRefreshing] = useState5(false);
398
+ const initialLoadDone = useRef2(false);
399
+ const refresh = useCallback3(async () => {
257
400
  if (!initialLoadDone.current) {
258
401
  setLoading(true);
259
402
  } else {
@@ -265,21 +408,21 @@ function useSetupProviders() {
265
408
  setLoading(false);
266
409
  setRefreshing(false);
267
410
  }, []);
268
- useEffect3(() => {
411
+ useEffect5(() => {
269
412
  refresh();
270
413
  }, [refresh]);
271
414
  return { providers, loading, refreshing, refresh };
272
415
  }
273
416
 
274
417
  // src/tui/models/hooks/useModels.ts
275
- import { useState as useState4, useEffect as useEffect4, useCallback as useCallback3, useRef as useRef2 } from "react";
418
+ import { useState as useState6, useEffect as useEffect6, useCallback as useCallback4, useRef as useRef3 } from "react";
276
419
  function useModels() {
277
- const [models, setModels] = useState4([]);
278
- const [warnings, setWarnings] = useState4([]);
279
- const [loading, setLoading] = useState4(true);
280
- const [refreshing, setRefreshing] = useState4(false);
281
- const initialLoadDone = useRef2(false);
282
- const refresh = useCallback3(async () => {
420
+ const [models, setModels] = useState6([]);
421
+ const [warnings, setWarnings] = useState6([]);
422
+ const [loading, setLoading] = useState6(true);
423
+ const [refreshing, setRefreshing] = useState6(false);
424
+ const initialLoadDone = useRef3(false);
425
+ const refresh = useCallback4(async () => {
283
426
  if (!initialLoadDone.current) {
284
427
  setLoading(true);
285
428
  } else {
@@ -298,7 +441,7 @@ function useModels() {
298
441
  setRefreshing(false);
299
442
  }
300
443
  }, []);
301
- useEffect4(() => {
444
+ useEffect6(() => {
302
445
  refresh();
303
446
  }, [refresh]);
304
447
  return {
@@ -311,11 +454,11 @@ function useModels() {
311
454
  }
312
455
 
313
456
  // src/tui/models/hooks/useRequests.ts
314
- import { useState as useState5, useEffect as useEffect5, useCallback as useCallback4, useRef as useRef3 } from "react";
457
+ import { useState as useState7, useEffect as useEffect7, useCallback as useCallback5, useRef as useRef4 } from "react";
315
458
  function useRequests(maxHistory = 50) {
316
- const [requests, setRequests] = useState5([]);
317
- const requestsRef = useRef3(/* @__PURE__ */ new Map());
318
- useEffect5(() => {
459
+ const [requests, setRequests] = useState7([]);
460
+ const requestsRef = useRef4(/* @__PURE__ */ new Map());
461
+ useEffect7(() => {
319
462
  const interval = setInterval(() => {
320
463
  setRequests((prev) => {
321
464
  const hasActive = prev.some((r) => r.status === "processing");
@@ -324,7 +467,7 @@ function useRequests(maxHistory = 50) {
324
467
  }, 1e3);
325
468
  return () => clearInterval(interval);
326
469
  }, []);
327
- useEffect5(() => {
470
+ useEffect7(() => {
328
471
  const unsubStart = requestEvents.onStart((event) => {
329
472
  const entry = {
330
473
  id: event.id,
@@ -343,7 +486,9 @@ function useRequests(maxHistory = 50) {
343
486
  ...existing,
344
487
  ...event.content !== void 0 && { content: event.content },
345
488
  ...event.step !== void 0 && { step: event.step },
346
- ...event.totalSteps !== void 0 && { totalSteps: event.totalSteps }
489
+ ...event.totalSteps !== void 0 && {
490
+ totalSteps: event.totalSteps
491
+ }
347
492
  };
348
493
  requestsRef.current.set(event.id, updated);
349
494
  setRequests(
@@ -375,7 +520,7 @@ function useRequests(maxHistory = 50) {
375
520
  };
376
521
  }, [maxHistory]);
377
522
  const activeCount = requests.filter((r) => r.status === "processing").length;
378
- const clear = useCallback4(() => {
523
+ const clear = useCallback5(() => {
379
524
  requestsRef.current.clear();
380
525
  setRequests([]);
381
526
  }, []);
@@ -387,13 +532,11 @@ function useRequests(maxHistory = 50) {
387
532
  }
388
533
 
389
534
  // src/tui/models/hooks/useRegisteredModels.ts
390
- import { useState as useState6, useEffect as useEffect6, useCallback as useCallback5 } from "react";
535
+ import { useState as useState8, useEffect as useEffect8, useCallback as useCallback6 } from "react";
391
536
  function useSyncedModels(connectionStatus) {
392
- const [syncedNames, setSyncedNames] = useState6(
393
- /* @__PURE__ */ new Set()
394
- );
395
- const [syncedModels, setSyncedModels] = useState6([]);
396
- const refresh = useCallback5(async () => {
537
+ const [syncedNames, setSyncedNames] = useState8(/* @__PURE__ */ new Set());
538
+ const [syncedModels, setSyncedModels] = useState8([]);
539
+ const refresh = useCallback6(async () => {
397
540
  if (connectionStatus !== "connected") {
398
541
  setSyncedNames(/* @__PURE__ */ new Set());
399
542
  setSyncedModels([]);
@@ -406,7 +549,7 @@ function useSyncedModels(connectionStatus) {
406
549
  } catch {
407
550
  }
408
551
  }, [connectionStatus]);
409
- useEffect6(() => {
552
+ useEffect8(() => {
410
553
  refresh();
411
554
  }, [refresh]);
412
555
  return {
@@ -417,7 +560,7 @@ function useSyncedModels(connectionStatus) {
417
560
  }
418
561
 
419
562
  // src/tui/models/pages/DashboardPage.tsx
420
- import { useMemo } from "react";
563
+ import { useMemo as useMemo2 } from "react";
421
564
  import { Box as Box4, Text as Text4, useStdout as useStdout3 } from "ink";
422
565
  import Spinner2 from "ink-spinner";
423
566
 
@@ -455,7 +598,10 @@ function snippetLine(content, maxWidth) {
455
598
  if (flat.length <= maxWidth) return flat;
456
599
  return "\u2026" + flat.slice(-(maxWidth - 1));
457
600
  }
458
- function RequestItem({ request, width }) {
601
+ function RequestItem({
602
+ request,
603
+ width
604
+ }) {
459
605
  const time = formatTime(request.startTime);
460
606
  const typeLabel = getRequestTypeLabel(request.requestType);
461
607
  const snippetIndent = " ";
@@ -470,13 +616,13 @@ function RequestItem({ request, width }) {
470
616
  /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
471
617
  " ",
472
618
  time,
473
- " "
619
+ " "
474
620
  ] }),
475
621
  /* @__PURE__ */ jsx3(Text3, { color: "white", children: request.modelId }),
476
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " " }),
622
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " " }),
477
623
  /* @__PURE__ */ jsx3(Text3, { color: typeLabel.color, children: typeLabel.label }),
478
624
  /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
479
- " ",
625
+ " ",
480
626
  formatDuration(elapsed),
481
627
  "..."
482
628
  ] })
@@ -508,13 +654,13 @@ function RequestItem({ request, width }) {
508
654
  /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
509
655
  " ",
510
656
  time,
511
- " "
657
+ " "
512
658
  ] }),
513
659
  /* @__PURE__ */ jsx3(Text3, { color: "white", children: request.modelId }),
514
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " " }),
660
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " " }),
515
661
  /* @__PURE__ */ jsx3(Text3, { color: typeLabel.color, children: typeLabel.label }),
516
662
  /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
517
- " ",
663
+ " ",
518
664
  duration,
519
665
  resultInfo
520
666
  ] })
@@ -530,18 +676,22 @@ function RequestItem({ request, width }) {
530
676
  /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
531
677
  " ",
532
678
  time,
533
- " "
679
+ " "
534
680
  ] }),
535
681
  /* @__PURE__ */ jsx3(Text3, { color: "white", children: request.modelId }),
536
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " " }),
682
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " " }),
537
683
  /* @__PURE__ */ jsx3(Text3, { color: typeLabel.color, children: typeLabel.label }),
538
684
  /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
539
- " ",
685
+ " ",
540
686
  request.error || "Failed"
541
687
  ] })
542
688
  ] }) });
543
689
  }
544
- function RequestLog({ requests, maxVisible = 8, hasModels = true }) {
690
+ function RequestLog({
691
+ requests,
692
+ maxVisible = 8,
693
+ hasModels = true
694
+ }) {
545
695
  const { stdout } = useStdout2();
546
696
  const width = stdout?.columns ?? 80;
547
697
  const activeRequests = requests.filter((r) => r.status === "processing");
@@ -615,6 +765,8 @@ function DashboardPage({
615
765
  syncedNames,
616
766
  modelsLoading,
617
767
  syncStatus = "idle",
768
+ editorSessions,
769
+ editorsLoading,
618
770
  onNavigate
619
771
  }) {
620
772
  const { stdout } = useStdout3();
@@ -629,11 +781,11 @@ function DashboardPage({
629
781
  (name) => !allModelNames.has(name)
630
782
  );
631
783
  const syncDescription = syncStatus === "syncing" ? "Syncing..." : syncStatus === "synced" ? "\u2713 Synced" : "Re-detect providers and sync models to MindStudio";
632
- const menuItems = useMemo(() => {
784
+ const menuItems = useMemo2(() => {
633
785
  return [
634
786
  {
635
787
  id: "interfaces",
636
- label: "Connect to IDE",
788
+ label: "Connect to Agent",
637
789
  description: "Connect your local editor to a MindStudio interface or script"
638
790
  },
639
791
  {
@@ -663,16 +815,25 @@ function DashboardPage({
663
815
  const headerLines = compactHeader ? 7 : 14;
664
816
  const providerContentLines = providersLoading ? 1 : installedProviders.length === 0 ? 2 : installedProviders.length;
665
817
  const providersLines = 3 + providerContentLines;
818
+ const editorsContentLines = editorsLoading ? 1 : editorSessions.length === 0 ? 1 : editorSessions.length;
819
+ const editorsLines = 3 + editorsContentLines;
666
820
  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);
667
821
  const modelsLines = 3 + modelContentLines;
668
822
  const requestLogOverhead = 3;
669
823
  const compactMenu = termHeight + 4 < 40;
670
824
  const menuLines = compactMenu ? 2 : menuItems.length + 6;
671
- const usedLines = headerLines + providersLines + modelsLines + requestLogOverhead + menuLines;
825
+ const usedLines = headerLines + providersLines + editorsLines + modelsLines + requestLogOverhead + menuLines;
672
826
  const maxVisible = Math.max(3, termHeight - usedLines);
673
827
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", flexGrow: 1, children: [
674
828
  /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
675
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "white", underline: true, children: "Providers" }),
829
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "white", underline: true, children: "Open Agents" }),
830
+ editorsLoading ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
831
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }) }),
832
+ /* @__PURE__ */ jsx4(Text4, { children: " Loading editor sessions..." })
833
+ ] }) : editorSessions.length === 0 ? /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { color: "gray", children: "Agents you are editing in the MindStudio IDE will appear here." }) }) : /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginTop: 1, children: editorSessions.map((session) => /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsx4(Text4, { color: "white", children: session.appName }) }, session.appId)) })
834
+ ] }),
835
+ /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
836
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "white", underline: true, children: "AI Model Providers" }),
676
837
  providersLoading ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
677
838
  /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }) }),
678
839
  /* @__PURE__ */ jsx4(Text4, { children: " Detecting providers..." })
@@ -691,7 +852,7 @@ function DashboardPage({
691
852
  }) })
692
853
  ] }),
693
854
  /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
694
- /* @__PURE__ */ jsx4(Text4, { bold: true, color: "white", underline: true, children: "Models" }),
855
+ /* @__PURE__ */ jsx4(Text4, { bold: true, color: "white", underline: true, children: "Local Models" }),
695
856
  modelsLoading ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
696
857
  /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }) }),
697
858
  /* @__PURE__ */ jsx4(Text4, { children: " Discovering models..." })
@@ -748,18 +909,18 @@ function DashboardPage({
748
909
  }
749
910
 
750
911
  // src/tui/models/pages/SetupPage.tsx
751
- import { useState as useState7, useMemo as useMemo3, useEffect as useEffect7 } from "react";
912
+ import { useState as useState9, useMemo as useMemo4, useEffect as useEffect9 } from "react";
752
913
  import { Box as Box5, Text as Text6, useInput as useInput2, useStdout as useStdout5 } from "ink";
753
914
  import Spinner3 from "ink-spinner";
754
915
 
755
916
  // src/tui/components/MarkdownText.tsx
756
- import { useMemo as useMemo2 } from "react";
917
+ import { useMemo as useMemo3 } from "react";
757
918
  import { Text as Text5, useStdout as useStdout4 } from "ink";
758
- import chalk from "chalk";
919
+ import chalk2 from "chalk";
759
920
  import { marked } from "marked";
760
921
  import { markedTerminal } from "marked-terminal";
761
922
  import { jsx as jsx5 } from "react/jsx-runtime";
762
- var codeStyle = chalk.cyan;
923
+ var codeStyle = chalk2.cyan;
763
924
  var identity = (s) => s;
764
925
  function renderMarkdown(content, width) {
765
926
  marked.use(
@@ -793,7 +954,7 @@ function ProviderDetailView({
793
954
  provider,
794
955
  onBack
795
956
  }) {
796
- const [scrollOffset, setScrollOffset] = useState7(0);
957
+ const [scrollOffset, setScrollOffset] = useState9(0);
797
958
  const { stdout } = useStdout5();
798
959
  const termHeight = (stdout?.rows ?? 24) - 4;
799
960
  const headerHeight = 14;
@@ -801,7 +962,7 @@ function ProviderDetailView({
801
962
  const contentPadding = 2;
802
963
  const viewHeight = termHeight - headerHeight - footerLines - contentPadding;
803
964
  const contentWidth = (stdout?.columns ?? 80) - 4;
804
- const renderedLines = useMemo3(() => {
965
+ const renderedLines = useMemo4(() => {
805
966
  const rendered = renderMarkdown(provider.readme, contentWidth);
806
967
  return rendered.split("\n");
807
968
  }, [provider.readme, contentWidth]);
@@ -818,7 +979,7 @@ function ProviderDetailView({
818
979
  }
819
980
  });
820
981
  const visibleContent = renderedLines.slice(scrollOffset, scrollOffset + viewHeight).join("\n");
821
- const scrollbar = useMemo3(() => {
982
+ const scrollbar = useMemo4(() => {
822
983
  if (maxScroll === 0) return null;
823
984
  const thumbSize = Math.max(
824
985
  1,
@@ -888,27 +1049,27 @@ function ProviderDetailView({
888
1049
  }
889
1050
  function SetupPage({ onBack }) {
890
1051
  const { providers, loading } = useSetupProviders();
891
- const [selectedProvider, setSelectedProvider] = useState7(null);
892
- const running = useMemo3(
1052
+ const [selectedProvider, setSelectedProvider] = useState9(null);
1053
+ const running = useMemo4(
893
1054
  () => providers.filter((p) => p.status.running),
894
1055
  [providers]
895
1056
  );
896
- const installed = useMemo3(
1057
+ const installed = useMemo4(
897
1058
  () => providers.filter((p) => p.status.installed && !p.status.running),
898
1059
  [providers]
899
1060
  );
900
- const notInstalled = useMemo3(
1061
+ const notInstalled = useMemo4(
901
1062
  () => providers.filter((p) => !p.status.installed),
902
1063
  [providers]
903
1064
  );
904
- const allProviders = useMemo3(
1065
+ const allProviders = useMemo4(
905
1066
  () => [...running, ...installed, ...notInstalled],
906
1067
  [running, installed, notInstalled]
907
1068
  );
908
1069
  const totalItems = allProviders.length + 1;
909
1070
  const backIndex = allProviders.length;
910
- const [cursorIndex, setCursorIndex] = useState7(0);
911
- useEffect7(() => {
1071
+ const [cursorIndex, setCursorIndex] = useState9(0);
1072
+ useEffect9(() => {
912
1073
  setCursorIndex(0);
913
1074
  }, [allProviders.length]);
914
1075
  useInput2((input, key) => {
@@ -1088,51 +1249,11 @@ function SetupPage({ onBack }) {
1088
1249
  }
1089
1250
 
1090
1251
  // src/tui/interfaces/pages/InterfacesPage.tsx
1091
- import { useState as useState12, useMemo as useMemo5, useEffect as useEffect12, useRef as useRef7 } from "react";
1252
+ import { useState as useState13, useMemo as useMemo6, useEffect as useEffect13, useRef as useRef7 } from "react";
1092
1253
  import { Box as Box8, Text as Text9, useInput as useInput5 } from "ink";
1093
- import Spinner5 from "ink-spinner";
1094
-
1095
- // src/tui/interfaces/hooks/useEditorSessions.ts
1096
- import { useState as useState8, useEffect as useEffect8, useCallback as useCallback6, useRef as useRef4 } from "react";
1097
- function useEditorSessions() {
1098
- const [sessions, setSessions] = useState8([]);
1099
- const [loading, setLoading] = useState8(true);
1100
- const [error, setError] = useState8(null);
1101
- const [refreshStatus, setRefreshStatus] = useState8("idle");
1102
- const initialLoadDone = useRef4(false);
1103
- const timerRef = useRef4();
1104
- const refresh = useCallback6(async () => {
1105
- if (!initialLoadDone.current) {
1106
- setLoading(true);
1107
- } else {
1108
- setRefreshStatus("refreshing");
1109
- }
1110
- setError(null);
1111
- try {
1112
- const data = await getEditorSessions();
1113
- setSessions(data);
1114
- initialLoadDone.current = true;
1115
- setRefreshStatus("refreshed");
1116
- clearTimeout(timerRef.current);
1117
- timerRef.current = setTimeout(() => setRefreshStatus("idle"), 1500);
1118
- } catch (err) {
1119
- setError(err instanceof Error ? err.message : "Failed to fetch sessions");
1120
- setRefreshStatus("idle");
1121
- } finally {
1122
- setLoading(false);
1123
- }
1124
- }, []);
1125
- useEffect8(() => {
1126
- refresh();
1127
- }, [refresh]);
1128
- useEffect8(() => {
1129
- return () => clearTimeout(timerRef.current);
1130
- }, []);
1131
- return { sessions, loading, error, refreshStatus, refresh };
1132
- }
1133
1254
 
1134
1255
  // src/tui/interfaces/hooks/useLocalInterface.ts
1135
- import { useState as useState9, useCallback as useCallback7, useRef as useRef5, useEffect as useEffect9 } from "react";
1256
+ import { useState as useState10, useCallback as useCallback7, useRef as useRef5, useEffect as useEffect10 } from "react";
1136
1257
  import { spawn } from "child_process";
1137
1258
  import crypto from "crypto";
1138
1259
  import fs from "fs";
@@ -1148,7 +1269,7 @@ function useLocalInterface({
1148
1269
  sessionId
1149
1270
  }) {
1150
1271
  const key = `${appId}:${stepId}`;
1151
- const [hasLocalCopy, setHasLocalCopy] = useState9(() => {
1272
+ const [hasLocalCopy, setHasLocalCopy] = useState10(() => {
1152
1273
  const existing = getLocalInterfacePath(key);
1153
1274
  if (!existing) return false;
1154
1275
  try {
@@ -1157,13 +1278,13 @@ function useLocalInterface({
1157
1278
  return false;
1158
1279
  }
1159
1280
  });
1160
- const [phase, setPhase] = useState9("idle");
1161
- const [outputLines, setOutputLines] = useState9([]);
1162
- const [errorMessage, setErrorMessage] = useState9(null);
1281
+ const [phase, setPhase] = useState10("idle");
1282
+ const [outputLines, setOutputLines] = useState10([]);
1283
+ const [errorMessage, setErrorMessage] = useState10(null);
1163
1284
  const processRef = useRef5(null);
1164
1285
  const mountedRef = useRef5(true);
1165
1286
  const stoppedRef = useRef5(false);
1166
- useEffect9(() => {
1287
+ useEffect10(() => {
1167
1288
  mountedRef.current = true;
1168
1289
  return () => {
1169
1290
  mountedRef.current = false;
@@ -1218,16 +1339,21 @@ function useLocalInterface({
1218
1339
  const env = {};
1219
1340
  if (getEnvironment() === "local") {
1220
1341
  env.MINDSTUDIO_API_URL = getApiBaseUrl();
1342
+ env.MINDSTUDIO_WS_URL = "ws://localhost:8888";
1221
1343
  }
1222
- if (mode === "script") {
1223
- env.MINDSTUDIO_API_KEY = getApiKey() ?? "";
1224
- return {
1225
- args: ["run", "dev:local", "--", "--app", appId, "--workflow", workflowId, "--step", stepId],
1226
- env
1227
- };
1228
- }
1344
+ env.MINDSTUDIO_API_KEY = getApiKey() ?? "";
1229
1345
  return {
1230
- args: ["run", "dev:local", "--", sessionId ?? ""],
1346
+ args: [
1347
+ "run",
1348
+ "dev:local",
1349
+ "--",
1350
+ "--app",
1351
+ appId,
1352
+ "--workflow",
1353
+ workflowId,
1354
+ "--step",
1355
+ stepId
1356
+ ],
1231
1357
  env
1232
1358
  };
1233
1359
  };
@@ -1290,14 +1416,21 @@ function useLocalInterface({
1290
1416
  } catch (err) {
1291
1417
  if (mountedRef.current) {
1292
1418
  setPhase("error");
1293
- setErrorMessage(
1294
- err instanceof Error ? err.message : "Unknown error"
1295
- );
1419
+ setErrorMessage(err instanceof Error ? err.message : "Unknown error");
1296
1420
  }
1297
1421
  }
1298
1422
  };
1299
1423
  run();
1300
- }, [key, mode, appId, stepId, workflowId, sessionId, appendOutput, runCommand]);
1424
+ }, [
1425
+ key,
1426
+ mode,
1427
+ appId,
1428
+ stepId,
1429
+ workflowId,
1430
+ sessionId,
1431
+ appendOutput,
1432
+ runCommand
1433
+ ]);
1301
1434
  const stop = useCallback7(() => {
1302
1435
  stoppedRef.current = true;
1303
1436
  if (processRef.current) {
@@ -1324,9 +1457,7 @@ function useLocalInterface({
1324
1457
  setHasLocalCopy(false);
1325
1458
  appendOutput("Deleted successfully.");
1326
1459
  } catch (err) {
1327
- setErrorMessage(
1328
- err instanceof Error ? err.message : "Failed to delete"
1329
- );
1460
+ setErrorMessage(err instanceof Error ? err.message : "Failed to delete");
1330
1461
  }
1331
1462
  setPhase("idle");
1332
1463
  }, [key, appendOutput]);
@@ -1343,7 +1474,7 @@ function useLocalInterface({
1343
1474
  }
1344
1475
 
1345
1476
  // src/tui/interfaces/pages/InterfaceSessionView.tsx
1346
- import { useState as useState10, useEffect as useEffect10 } from "react";
1477
+ import { useState as useState11, useEffect as useEffect11 } from "react";
1347
1478
  import { Box as Box6, Text as Text7, useInput as useInput3 } from "ink";
1348
1479
  import os2 from "os";
1349
1480
  import open from "open";
@@ -1364,8 +1495,8 @@ function InterfaceSessionView({
1364
1495
  menuItems.push({ id: "delete", label: "Delete Local Copy" });
1365
1496
  }
1366
1497
  menuItems.push({ id: "back", label: "Back" });
1367
- const [cursorIndex, setCursorIndex] = useState10(0);
1368
- useEffect10(() => {
1498
+ const [cursorIndex, setCursorIndex] = useState11(0);
1499
+ useEffect11(() => {
1369
1500
  setCursorIndex(0);
1370
1501
  }, [hasLocalCopy]);
1371
1502
  useInput3((input, key) => {
@@ -1447,7 +1578,7 @@ function InterfaceSessionView({
1447
1578
  }
1448
1579
 
1449
1580
  // src/tui/interfaces/pages/InterfaceRunningView.tsx
1450
- import { useState as useState11, useMemo as useMemo4, useRef as useRef6, useEffect as useEffect11 } from "react";
1581
+ import { useState as useState12, useMemo as useMemo5, useRef as useRef6, useEffect as useEffect12 } from "react";
1451
1582
  import { Box as Box7, Text as Text8, useInput as useInput4, useStdout as useStdout6 } from "ink";
1452
1583
  import { execSync } from "child_process";
1453
1584
  import os3 from "os";
@@ -1473,13 +1604,21 @@ function getPhaseLabel(phase) {
1473
1604
  case "cloning":
1474
1605
  return { text: "Cloning scaffold...", color: "cyan", showSpinner: true };
1475
1606
  case "installing":
1476
- return { text: "Installing dependencies...", color: "cyan", showSpinner: true };
1607
+ return {
1608
+ text: "Installing dependencies...",
1609
+ color: "cyan",
1610
+ showSpinner: true
1611
+ };
1477
1612
  case "running":
1478
1613
  return { text: "Dev server running", color: "green", showSpinner: true };
1479
1614
  case "error":
1480
1615
  return { text: "Error", color: "red", showSpinner: false };
1481
1616
  case "deleting":
1482
- return { text: "Deleting local copy...", color: "yellow", showSpinner: true };
1617
+ return {
1618
+ text: "Deleting local copy...",
1619
+ color: "yellow",
1620
+ showSpinner: true
1621
+ };
1483
1622
  default:
1484
1623
  return { text: "Idle", color: "gray", showSpinner: false };
1485
1624
  }
@@ -1497,20 +1636,28 @@ function InterfaceRunningView({
1497
1636
  const termHeight = (stdout?.rows ?? 24) - 4;
1498
1637
  const isActive = phase === "cloning" || phase === "installing" || phase === "running" || phase === "deleting";
1499
1638
  const displayPath = localPath?.replace(os3.homedir(), "~");
1500
- const menuItems = useMemo4(() => {
1639
+ const menuItems = useMemo5(() => {
1501
1640
  const items = [];
1502
1641
  if (isActive && displayPath) {
1503
- items.push({ id: "claude", label: "Copy Claude Code Command", copyValue: `cd ${displayPath} && claude` });
1504
- items.push({ id: "codex", label: "Copy Codex Command", copyValue: `cd ${displayPath} && codex` });
1642
+ items.push({
1643
+ id: "claude",
1644
+ label: "Copy Claude Code Command",
1645
+ copyValue: `cd ${displayPath} && claude`
1646
+ });
1647
+ items.push({
1648
+ id: "codex",
1649
+ label: "Copy Codex Command",
1650
+ copyValue: `cd ${displayPath} && codex`
1651
+ });
1505
1652
  items.push({ id: "reveal", label: "Open Folder" });
1506
1653
  }
1507
1654
  items.push({ id: "action", label: isActive ? "Stop" : "Back" });
1508
1655
  return items;
1509
1656
  }, [isActive, displayPath]);
1510
- const [cursorIndex, setCursorIndex] = useState11(0);
1511
- const [copiedId, setCopiedId] = useState11(null);
1657
+ const [cursorIndex, setCursorIndex] = useState12(0);
1658
+ const [copiedId, setCopiedId] = useState12(null);
1512
1659
  const copiedTimerRef = useRef6();
1513
- useEffect11(() => {
1660
+ useEffect12(() => {
1514
1661
  return () => clearTimeout(copiedTimerRef.current);
1515
1662
  }, []);
1516
1663
  const headerHeight = 14;
@@ -1590,14 +1737,15 @@ function InterfaceRunningView({
1590
1737
  "\u2022",
1591
1738
  " Enter Select ",
1592
1739
  "\u2022",
1593
- " q/Esc ",
1740
+ " q/Esc",
1741
+ " ",
1594
1742
  isActive ? "Stop" : "Back"
1595
1743
  ] }) })
1596
1744
  ] }) });
1597
1745
  }
1598
1746
 
1599
1747
  // src/tui/interfaces/pages/InterfacesPage.tsx
1600
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1748
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1601
1749
  function getSessionStatus(item) {
1602
1750
  if (item.kind === "script") return "running";
1603
1751
  const status = item.step.spaEditorSession?.status;
@@ -1640,10 +1788,7 @@ function formatCount(interfaces, scripts) {
1640
1788
  }
1641
1789
  return parts.join(", ");
1642
1790
  }
1643
- function OfflineView({
1644
- item,
1645
- onBack
1646
- }) {
1791
+ function OfflineView({ item, onBack }) {
1647
1792
  useInput5((input, key) => {
1648
1793
  if (input === "q" || key.escape || key.return) {
1649
1794
  onBack();
@@ -1673,8 +1818,8 @@ function AgentListView({
1673
1818
  const refreshIndex = sessions.length;
1674
1819
  const backIndex = sessions.length + 1;
1675
1820
  const totalItems = sessions.length + 2;
1676
- const [cursorIndex, setCursorIndex] = useState12(0);
1677
- useEffect12(() => {
1821
+ const [cursorIndex, setCursorIndex] = useState13(0);
1822
+ useEffect13(() => {
1678
1823
  setCursorIndex(sessions.length > 0 ? 0 : backIndex);
1679
1824
  }, [sessions.length, backIndex]);
1680
1825
  useInput5((input, key) => {
@@ -1698,35 +1843,48 @@ function AgentListView({
1698
1843
  });
1699
1844
  return /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
1700
1845
  /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", underline: true, children: "Choose an Agent" }),
1701
- sessions.length === 0 ? /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
1702
- /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "No active editor sessions." }),
1703
- /* @__PURE__ */ jsx9(Text9, { color: "gray", children: "Open an app in MindStudio's editor to see sessions here." })
1704
- ] }) : /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", marginTop: 1, children: sessions.map((session, i) => {
1846
+ /* @__PURE__ */ jsx9(Text9, { color: "gray", children: "Don't see your agent? Make sure it's open in the MindStudio editor." }),
1847
+ sessions.length === 0 ? /* @__PURE__ */ jsx9(Box8, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "No active editor sessions." }) }) : /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", marginTop: 1, children: sessions.map((session, i) => {
1705
1848
  const isSelected = i === cursorIndex;
1706
1849
  const stats = formatCount(
1707
1850
  session.customInterfaceSteps.length,
1708
1851
  session.scriptSteps.length
1709
1852
  );
1710
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: i > 0 ? 1 : 0, children: [
1711
- /* @__PURE__ */ jsxs8(Box8, { children: [
1712
- /* @__PURE__ */ jsxs8(Text9, { color: isSelected ? "cyan" : "white", bold: isSelected, children: [
1713
- isSelected ? "\u276F" : " ",
1714
- " ",
1715
- session.appName
1716
- ] }),
1717
- isSelected && /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " - Connect to this Agent" })
1718
- ] }),
1719
- /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
1720
- " ",
1721
- "https://app.mindstudio.ai/agents/",
1722
- session.appId,
1723
- "/edit"
1724
- ] }),
1725
- /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
1726
- " ",
1727
- stats
1728
- ] })
1729
- ] }, session.appId);
1853
+ return /* @__PURE__ */ jsxs8(
1854
+ Box8,
1855
+ {
1856
+ flexDirection: "column",
1857
+ marginTop: i > 0 ? 1 : 0,
1858
+ children: [
1859
+ /* @__PURE__ */ jsxs8(Box8, { children: [
1860
+ /* @__PURE__ */ jsxs8(
1861
+ Text9,
1862
+ {
1863
+ color: isSelected ? "cyan" : "white",
1864
+ bold: isSelected,
1865
+ children: [
1866
+ isSelected ? "\u276F" : " ",
1867
+ " ",
1868
+ session.appName
1869
+ ]
1870
+ }
1871
+ ),
1872
+ isSelected && /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " - Connect to this Agent" })
1873
+ ] }),
1874
+ /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
1875
+ " ",
1876
+ "https://app.mindstudio.ai/agents/",
1877
+ session.appId,
1878
+ "/edit"
1879
+ ] }),
1880
+ stats !== "" && /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
1881
+ " ",
1882
+ stats
1883
+ ] })
1884
+ ]
1885
+ },
1886
+ session.appId
1887
+ );
1730
1888
  }) }),
1731
1889
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
1732
1890
  /* @__PURE__ */ jsxs8(Box8, { children: [
@@ -1774,11 +1932,8 @@ function AgentDetailView({
1774
1932
  onRefresh,
1775
1933
  refreshStatus
1776
1934
  }) {
1777
- const [offlineItem, setOfflineItem] = useState12(null);
1778
- useEffect12(() => {
1779
- onRefresh();
1780
- }, []);
1781
- const interfaces = useMemo5(
1935
+ const [offlineItem, setOfflineItem] = useState13(null);
1936
+ const interfaces = useMemo6(
1782
1937
  () => session.customInterfaceSteps.map((step) => ({
1783
1938
  kind: "interface",
1784
1939
  appId: session.appId,
@@ -1787,7 +1942,7 @@ function AgentDetailView({
1787
1942
  })),
1788
1943
  [session]
1789
1944
  );
1790
- const scripts = useMemo5(
1945
+ const scripts = useMemo6(
1791
1946
  () => session.scriptSteps.map((step) => ({
1792
1947
  kind: "script",
1793
1948
  appId: session.appId,
@@ -1796,15 +1951,15 @@ function AgentDetailView({
1796
1951
  })),
1797
1952
  [session]
1798
1953
  );
1799
- const allItems = useMemo5(
1954
+ const allItems = useMemo6(
1800
1955
  () => [...interfaces, ...scripts],
1801
1956
  [interfaces, scripts]
1802
1957
  );
1803
1958
  const refreshIndex = allItems.length;
1804
1959
  const backIndex = allItems.length + 1;
1805
1960
  const totalItems = allItems.length + 2;
1806
- const [cursorIndex, setCursorIndex] = useState12(0);
1807
- useEffect12(() => {
1961
+ const [cursorIndex, setCursorIndex] = useState13(0);
1962
+ useEffect13(() => {
1808
1963
  setCursorIndex(0);
1809
1964
  }, [allItems.length]);
1810
1965
  useInput5((input, key) => {
@@ -1840,48 +1995,83 @@ function AgentDetailView({
1840
1995
  const scriptsOffset = interfaces.length;
1841
1996
  return /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
1842
1997
  /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", underline: true, children: session.appName }),
1998
+ /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
1999
+ "https://app.mindstudio.ai/agents/",
2000
+ session.appId,
2001
+ "/edit"
2002
+ ] }),
1843
2003
  /* @__PURE__ */ jsx9(Text9, { color: "gray", children: "Select an interface or script to connect your local editor." }),
1844
2004
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
1845
- interfaces.length > 0 && /* @__PURE__ */ jsxs8(Fragment4, { children: [
1846
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", underline: true, children: "Interfaces" }),
1847
- /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", marginTop: 1, children: interfaces.map((item, i) => {
1848
- const isSelected = i === cursorIndex;
1849
- const status = getSessionStatus(item);
1850
- const statusLabel = getStatusLabel(status);
1851
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: i > 0 ? 1 : 0, children: [
1852
- /* @__PURE__ */ jsxs8(Text9, { color: isSelected ? "cyan" : "white", bold: isSelected, children: [
1853
- isSelected ? "\u276F" : " ",
1854
- " ",
1855
- item.step.workflowName,
1856
- " - ",
1857
- item.step.displayName
1858
- ] }),
1859
- /* @__PURE__ */ jsx9(Box8, { children: /* @__PURE__ */ jsxs8(Text9, { color: statusLabel.color, children: [
1860
- " ",
1861
- statusLabel.text
1862
- ] }) })
1863
- ] }, `${item.step.workflowId}:${item.step.stepId}`);
1864
- }) })
1865
- ] }),
1866
- scripts.length > 0 && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: interfaces.length > 0 ? 1 : 0, children: [
1867
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", underline: true, children: "Scripts" }),
1868
- /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", marginTop: 1, children: scripts.map((item, i) => {
2005
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", children: "Interfaces" }),
2006
+ interfaces.length > 0 ? /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", marginTop: 1, children: interfaces.map((item, i) => {
2007
+ const isSelected = i === cursorIndex;
2008
+ const status = getSessionStatus(item);
2009
+ const statusLabel = getStatusLabel(status);
2010
+ return /* @__PURE__ */ jsxs8(
2011
+ Box8,
2012
+ {
2013
+ flexDirection: "column",
2014
+ marginTop: i > 0 ? 1 : 0,
2015
+ children: [
2016
+ /* @__PURE__ */ jsxs8(
2017
+ Text9,
2018
+ {
2019
+ color: isSelected ? "cyan" : "white",
2020
+ bold: isSelected,
2021
+ children: [
2022
+ isSelected ? "\u276F" : " ",
2023
+ " ",
2024
+ item.step.workflowName,
2025
+ " -",
2026
+ " ",
2027
+ item.step.displayName
2028
+ ]
2029
+ }
2030
+ ),
2031
+ /* @__PURE__ */ jsx9(Box8, { children: /* @__PURE__ */ jsxs8(Text9, { color: statusLabel.color, children: [
2032
+ " ",
2033
+ statusLabel.text
2034
+ ] }) })
2035
+ ]
2036
+ },
2037
+ `${item.step.workflowId}:${item.step.stepId}`
2038
+ );
2039
+ }) }) : /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " No interfaces in this agent." }),
2040
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
2041
+ /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", children: "Scripts" }),
2042
+ scripts.length > 0 ? /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", marginTop: 1, children: scripts.map((item, i) => {
1869
2043
  const index = scriptsOffset + i;
1870
2044
  const isSelected = index === cursorIndex;
1871
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: i > 0 ? 1 : 0, children: [
1872
- /* @__PURE__ */ jsxs8(Text9, { color: isSelected ? "cyan" : "white", bold: isSelected, children: [
1873
- isSelected ? "\u276F" : " ",
1874
- " ",
1875
- item.step.workflowName,
1876
- " - ",
1877
- item.step.displayName
1878
- ] }),
1879
- /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
1880
- " ",
1881
- item.step.entryFile
1882
- ] })
1883
- ] }, `${item.step.workflowId}:${item.step.stepId}`);
1884
- }) })
2045
+ return /* @__PURE__ */ jsxs8(
2046
+ Box8,
2047
+ {
2048
+ flexDirection: "column",
2049
+ marginTop: i > 0 ? 1 : 0,
2050
+ children: [
2051
+ /* @__PURE__ */ jsxs8(
2052
+ Text9,
2053
+ {
2054
+ color: isSelected ? "cyan" : "white",
2055
+ bold: isSelected,
2056
+ children: [
2057
+ isSelected ? "\u276F" : " ",
2058
+ " ",
2059
+ item.step.workflowName,
2060
+ " -",
2061
+ " ",
2062
+ item.step.displayName
2063
+ ]
2064
+ }
2065
+ ),
2066
+ /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
2067
+ " ",
2068
+ item.step.entryFile
2069
+ ] })
2070
+ ]
2071
+ },
2072
+ `${item.step.workflowId}:${item.step.stepId}`
2073
+ );
2074
+ }) }) : /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " No scripts in this agent." })
1885
2075
  ] }),
1886
2076
  /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
1887
2077
  /* @__PURE__ */ jsxs8(Box8, { children: [
@@ -1952,7 +2142,7 @@ function LocalDevView({
1952
2142
  const name = `${item.step.workflowName} - ${item.step.displayName}`;
1953
2143
  const isActive = phase === "cloning" || phase === "installing" || phase === "running" || phase === "deleting";
1954
2144
  const wasActiveRef = useRef7(false);
1955
- useEffect12(() => {
2145
+ useEffect13(() => {
1956
2146
  if (isActive) {
1957
2147
  wasActiveRef.current = true;
1958
2148
  } else if (wasActiveRef.current && phase === "idle") {
@@ -1986,33 +2176,16 @@ function LocalDevView({
1986
2176
  }
1987
2177
  );
1988
2178
  }
1989
- function InterfacesPage({ onBack }) {
1990
- const { sessions, loading, error, refreshStatus, refresh } = useEditorSessions();
1991
- const [selectedAppId, setSelectedAppId] = useState12(null);
1992
- const [selectedItem, setSelectedItem] = useState12(null);
1993
- if (loading) {
1994
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", flexGrow: 1, paddingX: 1, marginTop: 1, children: [
1995
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", underline: true, children: "Choose an Agent" }),
1996
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, children: [
1997
- /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: /* @__PURE__ */ jsx9(Spinner5, { type: "dots" }) }),
1998
- /* @__PURE__ */ jsx9(Text9, { children: " Loading editor sessions..." })
1999
- ] })
2000
- ] });
2001
- }
2002
- if (error) {
2003
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", flexGrow: 1, paddingX: 1, marginTop: 1, children: [
2004
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: "white", underline: true, children: "Choose an Agent" }),
2005
- /* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "red", children: error }) })
2006
- ] });
2007
- }
2179
+ function InterfacesPage({
2180
+ onBack,
2181
+ sessions,
2182
+ refreshStatus,
2183
+ refresh
2184
+ }) {
2185
+ const [selectedAppId, setSelectedAppId] = useState13(null);
2186
+ const [selectedItem, setSelectedItem] = useState13(null);
2008
2187
  if (selectedItem) {
2009
- return /* @__PURE__ */ jsx9(
2010
- LocalDevView,
2011
- {
2012
- item: selectedItem,
2013
- onBack: () => setSelectedItem(null)
2014
- }
2015
- );
2188
+ return /* @__PURE__ */ jsx9(LocalDevView, { item: selectedItem, onBack: () => setSelectedItem(null) });
2016
2189
  }
2017
2190
  if (selectedAppId) {
2018
2191
  const session = sessions.find((s) => s.appId === selectedAppId);
@@ -2044,23 +2217,23 @@ function InterfacesPage({ onBack }) {
2044
2217
  }
2045
2218
 
2046
2219
  // src/tui/pages/OnboardingPage.tsx
2047
- import { useEffect as useEffect14, useCallback as useCallback9, useState as useState14, useMemo as useMemo6 } from "react";
2220
+ import { useEffect as useEffect15, useCallback as useCallback9, useState as useState15, useMemo as useMemo7 } from "react";
2048
2221
  import { Box as Box9, Text as Text10, useInput as useInput6 } from "ink";
2049
- import Spinner6 from "ink-spinner";
2050
- import chalk2 from "chalk";
2222
+ import Spinner5 from "ink-spinner";
2223
+ import chalk3 from "chalk";
2051
2224
 
2052
2225
  // src/tui/hooks/useAuth.ts
2053
- import { useState as useState13, useCallback as useCallback8, useRef as useRef8, useEffect as useEffect13 } from "react";
2226
+ import { useState as useState14, useCallback as useCallback8, useRef as useRef8, useEffect as useEffect14 } from "react";
2054
2227
  import open3 from "open";
2055
2228
  var POLL_INTERVAL = 2e3;
2056
2229
  var MAX_ATTEMPTS = 30;
2057
2230
  function useAuth() {
2058
- const [status, setStatus] = useState13("idle");
2059
- const [authUrl, setAuthUrl] = useState13(null);
2060
- const [timeRemaining, setTimeRemaining] = useState13(0);
2231
+ const [status, setStatus] = useState14("idle");
2232
+ const [authUrl, setAuthUrl] = useState14(null);
2233
+ const [timeRemaining, setTimeRemaining] = useState14(0);
2061
2234
  const cancelledRef = useRef8(false);
2062
2235
  const timerRef = useRef8(null);
2063
- useEffect13(() => {
2236
+ useEffect14(() => {
2064
2237
  return () => {
2065
2238
  cancelledRef.current = true;
2066
2239
  if (timerRef.current) clearInterval(timerRef.current);
@@ -2137,12 +2310,12 @@ function useAuth() {
2137
2310
  }
2138
2311
 
2139
2312
  // src/tui/pages/OnboardingPage.tsx
2140
- import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2313
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2141
2314
  var SHIMMER_SPEED = 35;
2142
2315
  function useShimmerLogo() {
2143
- const [frame, setFrame] = useState14(0);
2144
- const lines = useMemo6(() => LogoString.split("\n"), []);
2145
- const totalChars = useMemo6(() => {
2316
+ const [frame, setFrame] = useState15(0);
2317
+ const lines = useMemo7(() => LogoString.split("\n"), []);
2318
+ const totalChars = useMemo7(() => {
2146
2319
  let count = 0;
2147
2320
  for (const line of lines) {
2148
2321
  for (const ch of line) {
@@ -2152,13 +2325,13 @@ function useShimmerLogo() {
2152
2325
  return count;
2153
2326
  }, [lines]);
2154
2327
  const cycleLength = totalChars + 40;
2155
- useEffect14(() => {
2328
+ useEffect15(() => {
2156
2329
  const interval = setInterval(() => {
2157
2330
  setFrame((f) => (f + 1) % cycleLength);
2158
2331
  }, SHIMMER_SPEED);
2159
2332
  return () => clearInterval(interval);
2160
2333
  }, [cycleLength]);
2161
- return useMemo6(() => {
2334
+ return useMemo7(() => {
2162
2335
  const sweepPos = frame;
2163
2336
  const holdEnd = totalChars + 20;
2164
2337
  let charIdx = 0;
@@ -2182,13 +2355,13 @@ function useShimmerLogo() {
2182
2355
  brightness = Math.max(0.1, 1 - fadeProgress);
2183
2356
  }
2184
2357
  if (brightness >= 0.9) {
2185
- result += chalk2.cyanBright.bold(ch);
2358
+ result += chalk3.cyanBright.bold(ch);
2186
2359
  } else if (brightness >= 0.6) {
2187
- result += chalk2.cyan(ch);
2360
+ result += chalk3.cyan(ch);
2188
2361
  } else if (brightness >= 0.3) {
2189
- result += chalk2.rgb(0, 100, 120)(ch);
2362
+ result += chalk3.rgb(0, 100, 120)(ch);
2190
2363
  } else {
2191
- result += chalk2.rgb(0, 50, 60)(ch);
2364
+ result += chalk3.rgb(0, 50, 60)(ch);
2192
2365
  }
2193
2366
  charIdx++;
2194
2367
  }
@@ -2205,13 +2378,13 @@ function OnboardingPage({ onComplete }) {
2205
2378
  cancel: cancelAuth
2206
2379
  } = useAuth();
2207
2380
  const shimmerLogo = useShimmerLogo();
2208
- useEffect14(() => {
2381
+ useEffect15(() => {
2209
2382
  if (authStatus === "success") {
2210
2383
  const timer = setTimeout(() => onComplete(), 1500);
2211
2384
  return () => clearTimeout(timer);
2212
2385
  }
2213
2386
  }, [authStatus, onComplete]);
2214
- useEffect14(() => {
2387
+ useEffect15(() => {
2215
2388
  return () => cancelAuth();
2216
2389
  }, []);
2217
2390
  const handleAction = useCallback9(() => {
@@ -2230,17 +2403,17 @@ function OnboardingPage({ onComplete }) {
2230
2403
  /* @__PURE__ */ jsx10(Text10, { children: shimmerLogo }),
2231
2404
  /* @__PURE__ */ jsx10(Box9, { flexDirection: "column", alignItems: "center", marginTop: 2, children: /* @__PURE__ */ jsx10(Text10, { bold: true, color: "white", children: "MindStudio Local Tunnel" }) }),
2232
2405
  /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", alignItems: "center", children: [
2233
- authStatus === "idle" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2406
+ authStatus === "idle" && /* @__PURE__ */ jsxs9(Fragment4, { children: [
2234
2407
  /* @__PURE__ */ jsx10(Text10, { color: "gray", children: "Connect your MindStudio account to get started." }),
2235
2408
  /* @__PURE__ */ jsx10(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: "Press any key to Connect Account" }) })
2236
2409
  ] }),
2237
- (authStatus === "expired" || authStatus === "timeout") && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2410
+ (authStatus === "expired" || authStatus === "timeout") && /* @__PURE__ */ jsxs9(Fragment4, { children: [
2238
2411
  /* @__PURE__ */ jsx10(Text10, { color: "red", children: authStatus === "expired" ? "Authorization expired." : "Authorization timed out." }),
2239
2412
  /* @__PURE__ */ jsx10(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: "Press any key to Try Again" }) })
2240
2413
  ] }),
2241
- authStatus === "waiting" && /* @__PURE__ */ jsxs9(Fragment5, { children: [
2414
+ authStatus === "waiting" && /* @__PURE__ */ jsxs9(Fragment4, { children: [
2242
2415
  /* @__PURE__ */ jsxs9(Box9, { children: [
2243
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: /* @__PURE__ */ jsx10(Spinner6, { type: "dots" }) }),
2416
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: /* @__PURE__ */ jsx10(Spinner5, { type: "dots" }) }),
2244
2417
  /* @__PURE__ */ jsxs9(Text10, { children: [
2245
2418
  " ",
2246
2419
  "Waiting for browser authorization... (",
@@ -2264,7 +2437,7 @@ function OnboardingPage({ onComplete }) {
2264
2437
  }
2265
2438
 
2266
2439
  // src/tui/App.tsx
2267
- import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2440
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2268
2441
  var MODEL_TYPE_MAP = {
2269
2442
  text: "llm_chat",
2270
2443
  image: "image_generation",
@@ -2279,6 +2452,7 @@ function App({ runner }) {
2279
2452
  error: connectionError,
2280
2453
  retry: retryConnection
2281
2454
  } = useConnection();
2455
+ const editorSessions = useEditorSessions();
2282
2456
  const {
2283
2457
  providers,
2284
2458
  loading: providersLoading,
@@ -2290,37 +2464,37 @@ function App({ runner }) {
2290
2464
  loading: modelsLoading,
2291
2465
  refresh: refreshModels
2292
2466
  } = useModels();
2293
- const { requests } = useRequests();
2467
+ const { requests, activeCount: activeRequestCount } = useRequests();
2294
2468
  const {
2295
2469
  syncedNames,
2296
2470
  syncedModels,
2297
2471
  refresh: refreshSynced
2298
2472
  } = useSyncedModels(connectionStatus);
2299
2473
  const shouldOnboard = getApiKey() === void 0 || getUserId() === void 0;
2300
- const [page, setPage] = useState15(
2474
+ const [page, setPage] = useState16(
2301
2475
  shouldOnboard ? "onboarding" : "dashboard"
2302
2476
  );
2303
- const [syncStatus, setSyncStatus] = useState15(
2477
+ const [syncStatus, setSyncStatus] = useState16(
2304
2478
  "idle"
2305
2479
  );
2306
- const syncTimerRef = useRef9();
2480
+ const syncTimerRef = useRef9(void 0);
2307
2481
  const lastSyncPayloadRef = useRef9("");
2308
- useEffect15(() => {
2482
+ useEffect16(() => {
2309
2483
  if (connectionStatus === "not_authenticated") {
2310
2484
  setPage("onboarding");
2311
2485
  }
2312
2486
  }, [connectionStatus]);
2313
- useEffect15(() => {
2487
+ useEffect16(() => {
2314
2488
  if (page === "dashboard") {
2315
2489
  refreshAll();
2316
2490
  }
2317
2491
  }, [page]);
2318
- useEffect15(() => {
2492
+ useEffect16(() => {
2319
2493
  if (connectionStatus === "connected" && syncedModels.length > 0) {
2320
2494
  runner.start(syncedModels);
2321
2495
  }
2322
2496
  }, [connectionStatus, syncedModels, runner]);
2323
- useEffect15(() => () => runner.stop(), [runner]);
2497
+ useEffect16(() => () => runner.stop(), [runner]);
2324
2498
  const refreshAll = useCallback10(
2325
2499
  async (silent = false) => {
2326
2500
  if (!silent) setSyncStatus("syncing");
@@ -2351,7 +2525,7 @@ function App({ runner }) {
2351
2525
  },
2352
2526
  [refreshProviders, refreshModels, refreshSynced]
2353
2527
  );
2354
- useEffect15(() => {
2528
+ useEffect16(() => {
2355
2529
  if (connectionStatus !== "connected" || page !== "dashboard") return;
2356
2530
  const interval = setInterval(() => refreshAll(true), 1500);
2357
2531
  return () => clearInterval(interval);
@@ -2400,11 +2574,11 @@ function App({ runner }) {
2400
2574
  },
2401
2575
  [handleNavigate]
2402
2576
  );
2403
- const [termSize, setTermSize] = useState15({
2577
+ const [termSize, setTermSize] = useState16({
2404
2578
  rows: stdout?.rows ?? 24,
2405
2579
  columns: stdout?.columns ?? 80
2406
2580
  });
2407
- useEffect15(() => {
2581
+ useEffect16(() => {
2408
2582
  if (!stdout) return;
2409
2583
  const onResize = () => {
2410
2584
  stdout.write("\x1B[2J\x1B[H");
@@ -2417,7 +2591,7 @@ function App({ runner }) {
2417
2591
  }, [stdout]);
2418
2592
  const termHeight = termSize.rows - 4;
2419
2593
  const compactHeader = termSize.rows <= 45 || termSize.columns <= 90;
2420
- return /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", height: termHeight, overflow: "hidden", children: page === "onboarding" ? /* @__PURE__ */ jsx11(OnboardingPage, { onComplete: handleOnboardingComplete }) : /* @__PURE__ */ jsxs10(Fragment6, { children: [
2594
+ return /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", height: termHeight, overflow: "hidden", children: page === "onboarding" ? /* @__PURE__ */ jsx11(OnboardingPage, { onComplete: handleOnboardingComplete }) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
2421
2595
  /* @__PURE__ */ jsx11(
2422
2596
  Header,
2423
2597
  {
@@ -2425,7 +2599,8 @@ function App({ runner }) {
2425
2599
  environment,
2426
2600
  configPath: getConfigPath(),
2427
2601
  connectionError,
2428
- compact: compactHeader
2602
+ compact: compactHeader,
2603
+ hasActiveRequest: activeRequestCount > 0
2429
2604
  }
2430
2605
  ),
2431
2606
  page === "dashboard" && /* @__PURE__ */ jsx11(
@@ -2439,6 +2614,8 @@ function App({ runner }) {
2439
2614
  syncedNames,
2440
2615
  modelsLoading,
2441
2616
  syncStatus,
2617
+ editorSessions: editorSessions.sessions,
2618
+ editorsLoading: editorSessions.loading,
2442
2619
  onNavigate: handleNavigate
2443
2620
  }
2444
2621
  ),
@@ -2446,7 +2623,10 @@ function App({ runner }) {
2446
2623
  page === "interfaces" && /* @__PURE__ */ jsx11(
2447
2624
  InterfacesPage,
2448
2625
  {
2449
- onBack: () => setPage("dashboard")
2626
+ onBack: () => setPage("dashboard"),
2627
+ sessions: editorSessions.sessions,
2628
+ refreshStatus: editorSessions.refreshStatus,
2629
+ refresh: editorSessions.refresh
2450
2630
  }
2451
2631
  ),
2452
2632
  page !== "dashboard" && page !== "setup" && page !== "interfaces" && /* @__PURE__ */ jsx11(Box10, { flexGrow: 1 }),
@@ -2500,38 +2680,99 @@ async function checkForUpdate() {
2500
2680
  }
2501
2681
 
2502
2682
  // src/tui/components/UpdatePrompt.tsx
2503
- import { Box as Box11, Text as Text11, useInput as useInput7 } from "ink";
2683
+ import { useState as useState17, useEffect as useEffect17, useMemo as useMemo8 } from "react";
2684
+ import { Box as Box11, Text as Text11, useInput as useInput7, useStdout as useStdout8 } from "ink";
2685
+ import chalk4 from "chalk";
2504
2686
  import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2687
+ var SHIMMER_SPEED2 = 35;
2688
+ function useShimmerLogo2() {
2689
+ const [frame, setFrame] = useState17(0);
2690
+ const lines = useMemo8(() => LogoString.split("\n"), []);
2691
+ const totalChars = useMemo8(() => {
2692
+ let count = 0;
2693
+ for (const line of lines) {
2694
+ for (const ch of line) {
2695
+ if (ch !== " " && ch !== " ") count++;
2696
+ }
2697
+ }
2698
+ return count;
2699
+ }, [lines]);
2700
+ const cycleLength = totalChars + 40;
2701
+ useEffect17(() => {
2702
+ const interval = setInterval(() => {
2703
+ setFrame((f) => (f + 1) % cycleLength);
2704
+ }, SHIMMER_SPEED2);
2705
+ return () => clearInterval(interval);
2706
+ }, [cycleLength]);
2707
+ return useMemo8(() => {
2708
+ const sweepPos = frame;
2709
+ const holdEnd = totalChars + 20;
2710
+ let charIdx = 0;
2711
+ return lines.map((line) => {
2712
+ let result = "";
2713
+ for (let i = 0; i < line.length; i++) {
2714
+ const ch = line[i];
2715
+ if (ch === " " || ch === " ") {
2716
+ result += ch;
2717
+ continue;
2718
+ }
2719
+ let brightness;
2720
+ if (sweepPos <= totalChars) {
2721
+ const lag = charIdx;
2722
+ const t = sweepPos - lag;
2723
+ brightness = t <= 0 ? 0.1 : Math.min(1, t / 8);
2724
+ } else if (sweepPos <= holdEnd) {
2725
+ brightness = 1;
2726
+ } else {
2727
+ const fadeProgress = (sweepPos - holdEnd) / (cycleLength - holdEnd);
2728
+ brightness = Math.max(0.1, 1 - fadeProgress);
2729
+ }
2730
+ if (brightness >= 0.9) {
2731
+ result += chalk4.cyanBright.bold(ch);
2732
+ } else if (brightness >= 0.6) {
2733
+ result += chalk4.cyan(ch);
2734
+ } else if (brightness >= 0.3) {
2735
+ result += chalk4.rgb(0, 100, 120)(ch);
2736
+ } else {
2737
+ result += chalk4.rgb(0, 50, 60)(ch);
2738
+ }
2739
+ charIdx++;
2740
+ }
2741
+ return result;
2742
+ }).join("\n");
2743
+ }, [frame, lines, totalChars, cycleLength]);
2744
+ }
2505
2745
  function UpdatePrompt({
2506
2746
  currentVersion,
2507
2747
  latestVersion,
2508
2748
  onChoice
2509
2749
  }) {
2510
- useInput7((input) => {
2511
- if (input.toLowerCase() === "y") {
2512
- onChoice(true);
2513
- } else {
2514
- onChoice(false);
2515
- }
2750
+ const { stdout } = useStdout8();
2751
+ const shimmerLogo = useShimmerLogo2();
2752
+ useInput7(() => {
2753
+ onChoice(true);
2516
2754
  });
2517
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingY: 1, paddingX: 2, children: [
2518
- /* @__PURE__ */ jsxs11(Text11, { children: [
2519
- /* @__PURE__ */ jsx12(Text11, { color: "yellow", bold: true, children: "Update available:" }),
2520
- /* @__PURE__ */ jsxs11(Text11, { children: [
2521
- " ",
2522
- "v",
2523
- currentVersion,
2524
- " ",
2525
- "\u2192",
2526
- " v",
2527
- latestVersion
2755
+ const termHeight = (stdout?.rows ?? 24) - 4;
2756
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", height: termHeight, children: [
2757
+ /* @__PURE__ */ jsx12(Box11, { flexGrow: 1 }),
2758
+ /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", alignItems: "center", children: [
2759
+ /* @__PURE__ */ jsx12(Text11, { children: shimmerLogo }),
2760
+ /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", alignItems: "center", marginTop: 2, children: /* @__PURE__ */ jsx12(Text11, { bold: true, color: "white", children: "MindStudio Local Tunnel" }) }),
2761
+ /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", alignItems: "center", children: [
2762
+ /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
2763
+ "Update required ",
2764
+ "\u2022",
2765
+ " v",
2766
+ currentVersion,
2767
+ " ",
2768
+ "\u2192",
2769
+ " v",
2770
+ latestVersion
2771
+ ] }),
2772
+ /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text11, { color: "cyan", bold: true, children: "Press any key to update" }) })
2528
2773
  ] })
2529
2774
  ] }),
2530
- /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { children: [
2531
- "Press ",
2532
- /* @__PURE__ */ jsx12(Text11, { bold: true, color: "cyan", children: "y" }),
2533
- " to update, any other key to skip"
2534
- ] }) })
2775
+ /* @__PURE__ */ jsx12(Box11, { flexGrow: 1 })
2535
2776
  ] });
2536
2777
  }
2537
2778
 
@@ -2581,16 +2822,13 @@ async function startTUI() {
2581
2822
  console.clear();
2582
2823
  }
2583
2824
  const runner = new TunnelRunner();
2584
- const { waitUntilExit } = render(
2585
- /* @__PURE__ */ jsx13(App, { runner }),
2586
- {
2587
- exitOnCtrlC: true
2588
- }
2589
- );
2825
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx13(App, { runner }), {
2826
+ exitOnCtrlC: true
2827
+ });
2590
2828
  await waitUntilExit();
2591
2829
  runner.stop();
2592
2830
  }
2593
2831
  export {
2594
2832
  startTUI
2595
2833
  };
2596
- //# sourceMappingURL=tui-BW6XKMWK.js.map
2834
+ //# sourceMappingURL=tui-OZLGNBGD.js.map