pilotswarm-cli 0.1.13 → 0.1.14
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/package.json +1 -1
- package/src/app.js +22 -0
- package/src/index.js +67 -0
- package/src/node-sdk-transport.js +41 -0
- package/src/platform.js +53 -30
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -241,6 +241,7 @@ export function PilotSwarmTuiApp({ controller, platform, onRequestExit }) {
|
|
|
241
241
|
const isCtrlA = matchesCtrlKey("a", "\u0001");
|
|
242
242
|
const isShiftN = input === "N" || (key.shift && key.name === "n");
|
|
243
243
|
const isShiftD = input === "D" || (key.shift && key.name === "d");
|
|
244
|
+
const isShiftT = !key.ctrl && !key.meta && !key.alt && (input === "T" || (key.shift && key.name === "t"));
|
|
244
245
|
const isAltBackspace = key.meta && (key.backspace || key.delete || key.name === "backspace" || key.name === "delete");
|
|
245
246
|
const isAltLeftWord = key.meta && (key.leftArrow || key.name === "left" || input === "b" || input === "B");
|
|
246
247
|
const isAltRightWord = key.meta && (key.rightArrow || key.name === "right" || input === "f" || input === "F");
|
|
@@ -275,6 +276,10 @@ export function PilotSwarmTuiApp({ controller, platform, onRequestExit }) {
|
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
if (modal) {
|
|
279
|
+
if (isShiftT && modal.type === "themePicker") {
|
|
280
|
+
controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
278
283
|
if (modal.type === "renameSession" || modal.type === "artifactUpload") {
|
|
279
284
|
if (key.escape) {
|
|
280
285
|
controller.handleCommand(UI_COMMANDS.CLOSE_MODAL).catch(() => {});
|
|
@@ -342,6 +347,11 @@ export function PilotSwarmTuiApp({ controller, platform, onRequestExit }) {
|
|
|
342
347
|
return;
|
|
343
348
|
}
|
|
344
349
|
|
|
350
|
+
if (focus !== "prompt" && isShiftT) {
|
|
351
|
+
controller.handleCommand(UI_COMMANDS.OPEN_THEME_PICKER).catch(() => {});
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
345
355
|
if (key.tab && key.shift) {
|
|
346
356
|
controller.handleCommand(UI_COMMANDS.FOCUS_PREV).catch(() => {});
|
|
347
357
|
return;
|
|
@@ -387,6 +397,18 @@ export function PilotSwarmTuiApp({ controller, platform, onRequestExit }) {
|
|
|
387
397
|
controller.handleCommand(UI_COMMANDS.OPEN_FILES_FILTER).catch(() => {});
|
|
388
398
|
return;
|
|
389
399
|
}
|
|
400
|
+
if (focus === "inspector" && inspectorTab === "history" && input === "f") {
|
|
401
|
+
controller.handleCommand(UI_COMMANDS.OPEN_HISTORY_FORMAT).catch(() => {});
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (focus === "inspector" && inspectorTab === "history" && input === "r") {
|
|
405
|
+
controller.handleCommand(UI_COMMANDS.REFRESH_EXECUTION_HISTORY).catch(() => {});
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (focus === "inspector" && inspectorTab === "history" && input === "a") {
|
|
409
|
+
controller.handleCommand(UI_COMMANDS.EXPORT_EXECUTION_HISTORY).catch(() => {});
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
390
412
|
if (focus === "inspector" && inspectorTab === "files" && input === "v") {
|
|
391
413
|
controller.handleCommand(UI_COMMANDS.TOGGLE_FILE_PREVIEW_FULLSCREEN).catch(() => {});
|
|
392
414
|
return;
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
3
5
|
import { createRequire } from "node:module";
|
|
4
6
|
import { render } from "ink";
|
|
5
7
|
import {
|
|
@@ -14,6 +16,26 @@ import { NodeSdkTransport } from "./node-sdk-transport.js";
|
|
|
14
16
|
|
|
15
17
|
const require = createRequire(import.meta.url);
|
|
16
18
|
|
|
19
|
+
const CONFIG_DIR = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "pilotswarm");
|
|
20
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
21
|
+
|
|
22
|
+
function readConfig() {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function writeConfig(patch) {
|
|
31
|
+
try {
|
|
32
|
+
const existing = readConfig();
|
|
33
|
+
const merged = { ...existing, ...patch };
|
|
34
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
35
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
|
|
17
39
|
function setupTuiHostRuntime() {
|
|
18
40
|
const logFile = "/tmp/duroxide-tui.log";
|
|
19
41
|
const originalConsole = {
|
|
@@ -90,9 +112,11 @@ export async function startTuiApp(config) {
|
|
|
90
112
|
store: config.store,
|
|
91
113
|
mode: config.mode,
|
|
92
114
|
});
|
|
115
|
+
const userConfig = readConfig();
|
|
93
116
|
const store = createStore(appReducer, createInitialState({
|
|
94
117
|
mode: config.mode,
|
|
95
118
|
branding: config.branding,
|
|
119
|
+
themeId: userConfig.themeId,
|
|
96
120
|
}));
|
|
97
121
|
const controller = new PilotSwarmUiController({ store, transport });
|
|
98
122
|
let tuiApp;
|
|
@@ -144,6 +168,49 @@ export async function startTuiApp(config) {
|
|
|
144
168
|
exitOnCtrlC: false,
|
|
145
169
|
});
|
|
146
170
|
|
|
171
|
+
// Listen for portal theme-change OSC sequences on stdin:
|
|
172
|
+
// \x1b]777;theme;<themeId>\x07
|
|
173
|
+
let oscBuffer = "";
|
|
174
|
+
const OSC_PREFIX = "\x1b]777;theme;";
|
|
175
|
+
const OSC_SUFFIX = "\x07";
|
|
176
|
+
const stdinThemeHandler = (data) => {
|
|
177
|
+
const str = typeof data === "string" ? data : data.toString("utf8");
|
|
178
|
+
oscBuffer += str;
|
|
179
|
+
while (oscBuffer.includes(OSC_PREFIX) && oscBuffer.includes(OSC_SUFFIX)) {
|
|
180
|
+
const start = oscBuffer.indexOf(OSC_PREFIX);
|
|
181
|
+
const end = oscBuffer.indexOf(OSC_SUFFIX, start);
|
|
182
|
+
if (end < 0) break;
|
|
183
|
+
const themeId = oscBuffer.slice(start + OSC_PREFIX.length, end);
|
|
184
|
+
oscBuffer = oscBuffer.slice(end + OSC_SUFFIX.length);
|
|
185
|
+
if (themeId) {
|
|
186
|
+
store.dispatch({ type: "ui/theme", themeId });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Prevent buffer from growing unbounded
|
|
190
|
+
if (oscBuffer.length > 1024) oscBuffer = oscBuffer.slice(-256);
|
|
191
|
+
};
|
|
192
|
+
process.stdin.on("data", stdinThemeHandler);
|
|
193
|
+
|
|
194
|
+
// Sync viewport on terminal resize (SIGWINCH)
|
|
195
|
+
const syncViewport = () => {
|
|
196
|
+
controller.setViewport({
|
|
197
|
+
width: process.stdout.columns || 120,
|
|
198
|
+
height: process.stdout.rows || 40,
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
syncViewport();
|
|
202
|
+
process.stdout.on("resize", syncViewport);
|
|
203
|
+
|
|
204
|
+
// Persist theme changes to config file
|
|
205
|
+
let lastPersistedThemeId = store.getState().ui.themeId;
|
|
206
|
+
store.subscribe(() => {
|
|
207
|
+
const currentThemeId = store.getState().ui.themeId;
|
|
208
|
+
if (currentThemeId && currentThemeId !== lastPersistedThemeId) {
|
|
209
|
+
lastPersistedThemeId = currentThemeId;
|
|
210
|
+
writeConfig({ themeId: currentThemeId });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
147
214
|
try {
|
|
148
215
|
await exitPromise;
|
|
149
216
|
} finally {
|
|
@@ -335,6 +335,10 @@ export class NodeSdkTransport {
|
|
|
335
335
|
return this.mgmt.getOrchestrationStats(sessionId);
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
async getExecutionHistory(sessionId, executionId) {
|
|
339
|
+
return this.mgmt.getExecutionHistory(sessionId, executionId);
|
|
340
|
+
}
|
|
341
|
+
|
|
338
342
|
async createSession({ model } = {}) {
|
|
339
343
|
const effectiveModel = model || this.mgmt.getDefaultModel();
|
|
340
344
|
const session = await this.client.createSession(effectiveModel ? { model: effectiveModel } : undefined);
|
|
@@ -495,6 +499,43 @@ export class NodeSdkTransport {
|
|
|
495
499
|
};
|
|
496
500
|
}
|
|
497
501
|
|
|
502
|
+
async exportExecutionHistory(sessionId) {
|
|
503
|
+
if (!this.artifactStore) {
|
|
504
|
+
throw new Error("Artifact store is not available for this transport.");
|
|
505
|
+
}
|
|
506
|
+
const shortId = String(sessionId || "").slice(0, 8);
|
|
507
|
+
const [history, stats] = await Promise.all([
|
|
508
|
+
this.mgmt.getExecutionHistory(sessionId),
|
|
509
|
+
this.mgmt.getOrchestrationStats(sessionId),
|
|
510
|
+
]);
|
|
511
|
+
const sessionInfo = await this.mgmt.getSession(sessionId).catch(() => null);
|
|
512
|
+
const exportData = {
|
|
513
|
+
exportedAt: new Date().toISOString(),
|
|
514
|
+
sessionId,
|
|
515
|
+
title: sessionInfo?.title || null,
|
|
516
|
+
agentId: sessionInfo?.agentId || null,
|
|
517
|
+
model: sessionInfo?.model || null,
|
|
518
|
+
orchestrationStats: stats || null,
|
|
519
|
+
eventCount: history?.length || 0,
|
|
520
|
+
events: (history || []).map((e) => {
|
|
521
|
+
const evt = { ...e };
|
|
522
|
+
if (evt.data) {
|
|
523
|
+
try { evt.data = JSON.parse(evt.data); } catch { /* keep raw */ }
|
|
524
|
+
}
|
|
525
|
+
return evt;
|
|
526
|
+
}),
|
|
527
|
+
};
|
|
528
|
+
const filename = `execution-history-${shortId}-${Date.now()}.json`;
|
|
529
|
+
const content = JSON.stringify(exportData, null, 2);
|
|
530
|
+
await this.artifactStore.uploadArtifact(sessionId, filename, content, guessArtifactContentType(filename));
|
|
531
|
+
return {
|
|
532
|
+
sessionId,
|
|
533
|
+
filename,
|
|
534
|
+
artifactLink: `artifact://${sessionId}/${filename}`,
|
|
535
|
+
sizeBytes: Buffer.byteLength(content, "utf8"),
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
498
539
|
async openPathInDefaultApp(targetPath) {
|
|
499
540
|
const resolvedPath = path.resolve(expandUserPath(targetPath));
|
|
500
541
|
if (!resolvedPath) {
|
package/src/platform.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
|
-
import { parseTerminalMarkupRuns } from "pilotswarm-ui-core";
|
|
4
|
+
import { DEFAULT_THEME_ID, getTheme, parseTerminalMarkupRuns } from "pilotswarm-ui-core";
|
|
5
5
|
|
|
6
6
|
const MAX_PROMPT_INPUT_ROWS = 3;
|
|
7
|
-
const SELECTION_BACKGROUND = "
|
|
8
|
-
const SELECTION_FOREGROUND = "
|
|
7
|
+
const SELECTION_BACKGROUND = "selectionBackground";
|
|
8
|
+
const SELECTION_FOREGROUND = "selectionForeground";
|
|
9
9
|
|
|
10
10
|
function clampValue(value, min, max) {
|
|
11
11
|
return Math.max(min, Math.min(max, value));
|
|
@@ -25,8 +25,23 @@ const tuiPlatformRuntime = {
|
|
|
25
25
|
paneRegistry: new Map(),
|
|
26
26
|
selection: createEmptySelection(),
|
|
27
27
|
renderInvalidator: null,
|
|
28
|
+
themeId: DEFAULT_THEME_ID,
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
function getCurrentTheme() {
|
|
32
|
+
return getTheme(tuiPlatformRuntime.themeId) || getTheme(DEFAULT_THEME_ID);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveColorToken(color) {
|
|
36
|
+
if (!color) return undefined;
|
|
37
|
+
const theme = getCurrentTheme();
|
|
38
|
+
return theme?.tui?.[color] || color;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isDimColorToken(color) {
|
|
42
|
+
return color === "gray";
|
|
43
|
+
}
|
|
44
|
+
|
|
30
45
|
function trimText(value, width) {
|
|
31
46
|
if (width <= 0) return "";
|
|
32
47
|
const text = String(value || "");
|
|
@@ -230,11 +245,11 @@ function flattenTitleText(title) {
|
|
|
230
245
|
function renderInlineRuns(runs, keyPrefix = "run") {
|
|
231
246
|
return runs.map((run, index) => React.createElement(Text, {
|
|
232
247
|
key: `${keyPrefix}:${index}`,
|
|
233
|
-
color: run.color
|
|
234
|
-
backgroundColor: run.backgroundColor
|
|
248
|
+
color: resolveColorToken(run.color),
|
|
249
|
+
backgroundColor: resolveColorToken(run.backgroundColor),
|
|
235
250
|
bold: Boolean(run.bold),
|
|
236
251
|
underline: Boolean(run.underline),
|
|
237
|
-
dimColor: run.color
|
|
252
|
+
dimColor: isDimColorToken(run.color),
|
|
238
253
|
}, run.text || ""));
|
|
239
254
|
}
|
|
240
255
|
|
|
@@ -339,15 +354,15 @@ function renderPanelRow(line, rowKey, contentWidth, borderColor, scrollIndicator
|
|
|
339
354
|
const selectedRuns = applySelectionToRuns(lineToRuns(line, contentWidth), normalizedSelection);
|
|
340
355
|
|
|
341
356
|
return React.createElement(Box, { key: `row:${rowKey}`, flexDirection: "row" },
|
|
342
|
-
React.createElement(Text, { color: borderColor }, "│ "),
|
|
343
|
-
React.createElement(Box, { width: contentWidth, backgroundColor: fillColor
|
|
357
|
+
React.createElement(Text, { color: resolveColorToken(borderColor) }, "│ "),
|
|
358
|
+
React.createElement(Box, { width: contentWidth, backgroundColor: resolveColorToken(fillColor) },
|
|
344
359
|
!line
|
|
345
360
|
? React.createElement(Text, null, " ".repeat(contentWidth))
|
|
346
361
|
: selectedRuns.length > 0
|
|
347
362
|
? renderInlineRuns(selectedRuns, `inline:${rowKey}`)
|
|
348
363
|
: React.createElement(Text, null, "")),
|
|
349
|
-
React.createElement(Text, { color: scrollIndicator ? "gray" : undefined, dimColor: Boolean(scrollIndicator) }, scrollChar),
|
|
350
|
-
React.createElement(Text, { color: borderColor }, "│"));
|
|
364
|
+
React.createElement(Text, { color: scrollIndicator ? resolveColorToken("gray") : undefined, dimColor: Boolean(scrollIndicator) }, scrollChar),
|
|
365
|
+
React.createElement(Text, { color: resolveColorToken(borderColor) }, "│"));
|
|
351
366
|
}
|
|
352
367
|
|
|
353
368
|
function renderBorderTop(title, color, width) {
|
|
@@ -356,14 +371,14 @@ function renderBorderTop(title, color, width) {
|
|
|
356
371
|
const fill = Math.max(0, safeWidth - titleRunLength(safeTitleRuns) - 5);
|
|
357
372
|
|
|
358
373
|
return React.createElement(Box, null,
|
|
359
|
-
React.createElement(Text, { color }, "╭─ "),
|
|
374
|
+
React.createElement(Text, { color: resolveColorToken(color) }, "╭─ "),
|
|
360
375
|
renderInlineRuns(safeTitleRuns, "title"),
|
|
361
|
-
React.createElement(Text, { color }, ` ${"─".repeat(fill)}╮`));
|
|
376
|
+
React.createElement(Text, { color: resolveColorToken(color) }, ` ${"─".repeat(fill)}╮`));
|
|
362
377
|
}
|
|
363
378
|
|
|
364
379
|
function renderBorderBottom(color, width) {
|
|
365
380
|
const safeWidth = Math.max(8, Number(width) || 40);
|
|
366
|
-
return React.createElement(Text, { color }, `╰${"─".repeat(Math.max(0, safeWidth - 2))}╯`);
|
|
381
|
+
return React.createElement(Text, { color: resolveColorToken(color) }, `╰${"─".repeat(Math.max(0, safeWidth - 2))}╯`);
|
|
367
382
|
}
|
|
368
383
|
|
|
369
384
|
function compareSelectionPoints(left, right) {
|
|
@@ -527,10 +542,10 @@ function linesToElements(lines) {
|
|
|
527
542
|
}
|
|
528
543
|
return React.createElement(Text, {
|
|
529
544
|
key: `text:${index}`,
|
|
530
|
-
color: line.color
|
|
531
|
-
backgroundColor: line.backgroundColor
|
|
545
|
+
color: resolveColorToken(line.color),
|
|
546
|
+
backgroundColor: resolveColorToken(line.backgroundColor),
|
|
532
547
|
bold: Boolean(line.bold),
|
|
533
|
-
dimColor: line.color
|
|
548
|
+
dimColor: isDimColorToken(line.color),
|
|
534
549
|
}, line.text || "");
|
|
535
550
|
});
|
|
536
551
|
}
|
|
@@ -540,6 +555,7 @@ function Root({ children }) {
|
|
|
540
555
|
flexDirection: "column",
|
|
541
556
|
height: process.stdout.rows || 40,
|
|
542
557
|
width: process.stdout.columns || 120,
|
|
558
|
+
backgroundColor: resolveColorToken("background"),
|
|
543
559
|
}, children);
|
|
544
560
|
}
|
|
545
561
|
|
|
@@ -554,13 +570,13 @@ function Column({ children, ...props }) {
|
|
|
554
570
|
function Header({ title, subtitle }) {
|
|
555
571
|
return React.createElement(Box, {
|
|
556
572
|
borderStyle: "round",
|
|
557
|
-
borderColor: "cyan",
|
|
573
|
+
borderColor: resolveColorToken("cyan"),
|
|
558
574
|
paddingX: 1,
|
|
559
575
|
marginBottom: 1,
|
|
560
576
|
justifyContent: "space-between",
|
|
561
577
|
},
|
|
562
|
-
React.createElement(Text, { bold: true, color: "cyan" }, title),
|
|
563
|
-
React.createElement(Text, { color: "gray" }, subtitle || ""));
|
|
578
|
+
React.createElement(Text, { bold: true, color: resolveColorToken("cyan") }, title),
|
|
579
|
+
React.createElement(Text, { color: resolveColorToken("gray"), dimColor: true }, subtitle || ""));
|
|
564
580
|
}
|
|
565
581
|
|
|
566
582
|
function Panel({
|
|
@@ -660,10 +676,10 @@ function Panel({
|
|
|
660
676
|
: React.createElement(Box, {
|
|
661
677
|
flexDirection: "column",
|
|
662
678
|
borderStyle: "round",
|
|
663
|
-
borderColor: borderColor,
|
|
679
|
+
borderColor: resolveColorToken(borderColor),
|
|
664
680
|
paddingX: 1,
|
|
665
681
|
flexGrow: 1,
|
|
666
|
-
backgroundColor: fillColor
|
|
682
|
+
backgroundColor: resolveColorToken(fillColor),
|
|
667
683
|
}, children),
|
|
668
684
|
renderBorderBottom(borderColor, safeWidth));
|
|
669
685
|
}
|
|
@@ -717,11 +733,11 @@ function renderPromptRow(lineText, cursorColumn, { color, showCursor, keyPrefix,
|
|
|
717
733
|
showCursor
|
|
718
734
|
? cursorChar
|
|
719
735
|
? React.createElement(Text, {
|
|
720
|
-
color: "
|
|
721
|
-
backgroundColor: "
|
|
736
|
+
color: resolveColorToken("promptCursorForeground"),
|
|
737
|
+
backgroundColor: resolveColorToken("promptCursorBackground"),
|
|
722
738
|
dimColor,
|
|
723
739
|
}, cursorChar)
|
|
724
|
-
: React.createElement(Text, { color: "
|
|
740
|
+
: React.createElement(Text, { color: resolveColorToken("promptCursorBackground") }, "█")
|
|
725
741
|
: null,
|
|
726
742
|
after ? React.createElement(Text, { color, dimColor }, after) : null,
|
|
727
743
|
);
|
|
@@ -732,7 +748,7 @@ function Input({ label, value, focused, placeholder, rows = 1, cursorIndex = 0 }
|
|
|
732
748
|
const isEmpty = safeValue.length === 0;
|
|
733
749
|
const safeRows = clampValue(Number(rows) || 1, 1, MAX_PROMPT_INPUT_ROWS);
|
|
734
750
|
const labelPrefix = React.createElement(Text, {
|
|
735
|
-
color: focused ? "red" : "green",
|
|
751
|
+
color: resolveColorToken(focused ? "red" : "green"),
|
|
736
752
|
bold: true,
|
|
737
753
|
}, `${label}: `);
|
|
738
754
|
const cursorPosition = getPromptCursorPosition(safeValue, cursorIndex);
|
|
@@ -756,7 +772,7 @@ function Input({ label, value, focused, placeholder, rows = 1, cursorIndex = 0 }
|
|
|
756
772
|
isEmpty
|
|
757
773
|
? [
|
|
758
774
|
renderPromptRow(placeholder || "Type a message and press Enter", focused ? 0 : null, {
|
|
759
|
-
color: "gray",
|
|
775
|
+
color: resolveColorToken("gray"),
|
|
760
776
|
dimColor: true,
|
|
761
777
|
showCursor: Boolean(focused),
|
|
762
778
|
keyPrefix: "prompt-line:0",
|
|
@@ -768,7 +784,7 @@ function Input({ label, value, focused, placeholder, rows = 1, cursorIndex = 0 }
|
|
|
768
784
|
}, React.createElement(Text, null, ""))),
|
|
769
785
|
]
|
|
770
786
|
: displayLines.map((line, index) => renderPromptRow(line, focused && visibleCursorLine === index ? cursorPosition.column : null, {
|
|
771
|
-
color: "white",
|
|
787
|
+
color: resolveColorToken("white"),
|
|
772
788
|
showCursor: Boolean(focused && visibleCursorLine === index),
|
|
773
789
|
keyPrefix: `prompt-line:${index}`,
|
|
774
790
|
prefix: index === 0 ? labelPrefix : null,
|
|
@@ -779,12 +795,12 @@ function Input({ label, value, focused, placeholder, rows = 1, cursorIndex = 0 }
|
|
|
779
795
|
function StatusLine({ left, right }) {
|
|
780
796
|
return React.createElement(Box, {
|
|
781
797
|
borderStyle: "round",
|
|
782
|
-
borderColor: "gray",
|
|
798
|
+
borderColor: resolveColorToken("gray"),
|
|
783
799
|
paddingX: 1,
|
|
784
800
|
justifyContent: "space-between",
|
|
785
801
|
},
|
|
786
|
-
React.createElement(Text, { color: "white" }, left || ""),
|
|
787
|
-
React.createElement(Text, { color: "gray", dimColor: true }, right || ""));
|
|
802
|
+
React.createElement(Text, { color: resolveColorToken("white") }, left || ""),
|
|
803
|
+
React.createElement(Text, { color: resolveColorToken("gray"), dimColor: true }, right || ""));
|
|
788
804
|
}
|
|
789
805
|
|
|
790
806
|
function Overlay({ children }) {
|
|
@@ -807,6 +823,7 @@ export function createTuiPlatform() {
|
|
|
807
823
|
tuiPlatformRuntime.paneRegistry.clear();
|
|
808
824
|
tuiPlatformRuntime.selection = createEmptySelection();
|
|
809
825
|
tuiPlatformRuntime.renderInvalidator = null;
|
|
826
|
+
tuiPlatformRuntime.themeId = DEFAULT_THEME_ID;
|
|
810
827
|
|
|
811
828
|
return {
|
|
812
829
|
Root,
|
|
@@ -818,6 +835,12 @@ export function createTuiPlatform() {
|
|
|
818
835
|
Lines,
|
|
819
836
|
Input,
|
|
820
837
|
StatusLine,
|
|
838
|
+
setTheme(themeId) {
|
|
839
|
+
const nextTheme = getTheme(themeId) || getTheme(DEFAULT_THEME_ID);
|
|
840
|
+
if (!nextTheme || nextTheme.id === tuiPlatformRuntime.themeId) return;
|
|
841
|
+
tuiPlatformRuntime.themeId = nextTheme.id;
|
|
842
|
+
requestTuiRender();
|
|
843
|
+
},
|
|
821
844
|
setRenderInvalidator(fn) {
|
|
822
845
|
tuiPlatformRuntime.renderInvalidator = typeof fn === "function" ? fn : null;
|
|
823
846
|
},
|