create-rezi 0.1.0-alpha.45 → 0.1.0-alpha.48
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 +2 -2
- package/templates/starship/src/main.ts +232 -20
- package/templates/starship/src/screens/bridge.ts +1 -1
- package/templates/starship/src/screens/cargo.ts +6 -2
- package/templates/starship/src/screens/comms.ts +6 -2
- package/templates/starship/src/screens/crew.ts +200 -113
- package/templates/starship/src/screens/engineering.ts +30 -14
- package/templates/starship/src/screens/settings.ts +3 -2
- package/templates/starship/src/theme.ts +163 -143
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-rezi",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.48",
|
|
4
4
|
"description": "Scaffold a Rezi terminal UI app.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://rezitui.dev",
|
|
@@ -28,6 +28,6 @@
|
|
|
28
28
|
"bun": ">=1.3.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@rezi-ui/testkit": "0.1.0-alpha.
|
|
31
|
+
"@rezi-ui/testkit": "0.1.0-alpha.48"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type DebugController,
|
|
3
|
+
type DebugRecord,
|
|
4
|
+
categoriesToMask,
|
|
5
|
+
createDebugController,
|
|
6
|
+
} from "@rezi-ui/core";
|
|
2
7
|
import { createNodeApp } from "@rezi-ui/node";
|
|
3
8
|
import { debugSnapshot } from "./helpers/debug.js";
|
|
4
9
|
import { resolveStarshipCommand } from "./helpers/keybindings.js";
|
|
@@ -14,10 +19,21 @@ import type { RouteDeps, RouteId, StarshipAction, StarshipState } from "./types.
|
|
|
14
19
|
const UI_FPS_CAP = 30;
|
|
15
20
|
const TICK_MS = 800;
|
|
16
21
|
const TOAST_PRUNE_MS = 3000;
|
|
22
|
+
const DEBUG_TRACE_ENABLED = process.env.REZI_STARSHIP_DEBUG_TRACE === "1";
|
|
23
|
+
const EXECUTION_MODE: "inline" | "worker" =
|
|
24
|
+
process.env.REZI_STARSHIP_EXECUTION_MODE === "worker" ? "worker" : "inline";
|
|
25
|
+
|
|
26
|
+
function clampViewportAxis(value: number | undefined, fallback: number): number {
|
|
27
|
+
const safeFallback = Math.max(1, Math.trunc(fallback));
|
|
28
|
+
if (!Number.isFinite(value)) return safeFallback;
|
|
29
|
+
const raw = Math.trunc(value ?? safeFallback);
|
|
30
|
+
if (raw <= 0) return safeFallback;
|
|
31
|
+
return raw;
|
|
32
|
+
}
|
|
17
33
|
|
|
18
34
|
const initialState = createInitialStateWithViewport(Date.now(), {
|
|
19
|
-
cols: process.stdout.columns
|
|
20
|
-
rows: process.stdout.rows
|
|
35
|
+
cols: clampViewportAxis(process.stdout.columns, 120),
|
|
36
|
+
rows: clampViewportAxis(process.stdout.rows, 40),
|
|
21
37
|
});
|
|
22
38
|
const enableHsr = process.argv.includes("--hsr") || process.env.REZI_HSR === "1";
|
|
23
39
|
const hasInteractiveTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
@@ -41,6 +57,15 @@ let app!: ReturnType<typeof createNodeApp<StarshipState>>;
|
|
|
41
57
|
let stopping = false;
|
|
42
58
|
let tickTimer: ReturnType<typeof setInterval> | null = null;
|
|
43
59
|
let toastTimer: ReturnType<typeof setInterval> | null = null;
|
|
60
|
+
let debugTraceTimer: ReturnType<typeof setInterval> | null = null;
|
|
61
|
+
let debugTraceInFlight = false;
|
|
62
|
+
let debugController: DebugController | null = null;
|
|
63
|
+
let debugLastRecordId = 0n;
|
|
64
|
+
let stopCode = 0;
|
|
65
|
+
let stopResolve: (() => void) | null = null;
|
|
66
|
+
const stopPromise = new Promise<void>((resolve) => {
|
|
67
|
+
stopResolve = resolve;
|
|
68
|
+
});
|
|
44
69
|
let lastViewport = {
|
|
45
70
|
cols: initialState.viewportCols,
|
|
46
71
|
rows: initialState.viewportRows,
|
|
@@ -68,20 +93,22 @@ function dispatch(action: StarshipAction): void {
|
|
|
68
93
|
}
|
|
69
94
|
|
|
70
95
|
function syncViewport(cols: number, rows: number): void {
|
|
71
|
-
|
|
72
|
-
|
|
96
|
+
const safeCols = clampViewportAxis(cols, lastViewport.cols);
|
|
97
|
+
const safeRows = clampViewportAxis(rows, lastViewport.rows);
|
|
98
|
+
if (safeCols === lastViewport.cols && safeRows === lastViewport.rows) return;
|
|
99
|
+
lastViewport = { cols: safeCols, rows: safeRows };
|
|
73
100
|
debugSnapshot("runtime.viewport", {
|
|
74
|
-
cols,
|
|
75
|
-
rows,
|
|
101
|
+
cols: safeCols,
|
|
102
|
+
rows: safeRows,
|
|
76
103
|
route: currentRouteId(),
|
|
77
104
|
});
|
|
78
|
-
dispatch({ type: "set-viewport", cols, rows });
|
|
105
|
+
dispatch({ type: "set-viewport", cols: safeCols, rows: safeRows });
|
|
79
106
|
}
|
|
80
107
|
|
|
81
108
|
function syncViewportFromStdout(): void {
|
|
82
109
|
if (!process.stdout.isTTY) return;
|
|
83
|
-
const cols = process.stdout.columns
|
|
84
|
-
const rows = process.stdout.rows
|
|
110
|
+
const cols = clampViewportAxis(process.stdout.columns, lastViewport.cols);
|
|
111
|
+
const rows = clampViewportAxis(process.stdout.rows, lastViewport.rows);
|
|
85
112
|
syncViewport(cols, rows);
|
|
86
113
|
}
|
|
87
114
|
|
|
@@ -96,6 +123,17 @@ function currentRouteId(): RouteId {
|
|
|
96
123
|
return "bridge";
|
|
97
124
|
}
|
|
98
125
|
|
|
126
|
+
const frameAuditGlobal = globalThis as {
|
|
127
|
+
__reziFrameAuditContext?: () => Readonly<Record<string, unknown>>;
|
|
128
|
+
};
|
|
129
|
+
frameAuditGlobal.__reziFrameAuditContext = () =>
|
|
130
|
+
Object.freeze({
|
|
131
|
+
route: currentRouteId(),
|
|
132
|
+
viewportCols: lastViewport.cols,
|
|
133
|
+
viewportRows: lastViewport.rows,
|
|
134
|
+
executionMode: EXECUTION_MODE,
|
|
135
|
+
});
|
|
136
|
+
|
|
99
137
|
function navigate(routeId: RouteId): void {
|
|
100
138
|
const router = app.router;
|
|
101
139
|
if (!router) return;
|
|
@@ -126,6 +164,7 @@ function buildRoutes(factory: CreateRoutesFn) {
|
|
|
126
164
|
async function stopApp(code = 0): Promise<void> {
|
|
127
165
|
if (stopping) return;
|
|
128
166
|
stopping = true;
|
|
167
|
+
stopCode = code;
|
|
129
168
|
|
|
130
169
|
if (tickTimer) {
|
|
131
170
|
clearInterval(tickTimer);
|
|
@@ -137,18 +176,28 @@ async function stopApp(code = 0): Promise<void> {
|
|
|
137
176
|
toastTimer = null;
|
|
138
177
|
}
|
|
139
178
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
179
|
+
if (debugTraceTimer) {
|
|
180
|
+
clearInterval(debugTraceTimer);
|
|
181
|
+
debugTraceTimer = null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (debugController) {
|
|
185
|
+
try {
|
|
186
|
+
await debugController.disable();
|
|
187
|
+
} catch {
|
|
188
|
+
// Ignore debug shutdown races.
|
|
189
|
+
}
|
|
190
|
+
debugController = null;
|
|
144
191
|
}
|
|
145
192
|
|
|
146
193
|
try {
|
|
147
|
-
app.
|
|
194
|
+
await app.stop();
|
|
148
195
|
} catch {
|
|
149
|
-
// Ignore
|
|
196
|
+
// Ignore stop races.
|
|
150
197
|
}
|
|
151
|
-
|
|
198
|
+
frameAuditGlobal.__reziFrameAuditContext = undefined;
|
|
199
|
+
stopResolve?.();
|
|
200
|
+
stopResolve = null;
|
|
152
201
|
}
|
|
153
202
|
|
|
154
203
|
function applyCommand(command: ReturnType<typeof resolveStarshipCommand>): void {
|
|
@@ -464,13 +513,172 @@ function bindKeys(): void {
|
|
|
464
513
|
app.keys(bindingMap);
|
|
465
514
|
}
|
|
466
515
|
|
|
516
|
+
function snapshotDebugRecord(record: DebugRecord): void {
|
|
517
|
+
const { header } = record;
|
|
518
|
+
if (header.category === "frame") {
|
|
519
|
+
if (
|
|
520
|
+
record.payload &&
|
|
521
|
+
typeof record.payload === "object" &&
|
|
522
|
+
"drawlistBytes" in record.payload &&
|
|
523
|
+
"drawlistCmds" in record.payload
|
|
524
|
+
) {
|
|
525
|
+
debugSnapshot("runtime.debug.frame", {
|
|
526
|
+
recordId: header.recordId.toString(),
|
|
527
|
+
frameId: header.frameId.toString(),
|
|
528
|
+
route: currentRouteId(),
|
|
529
|
+
drawlistBytes: record.payload.drawlistBytes,
|
|
530
|
+
drawlistCmds: record.payload.drawlistCmds,
|
|
531
|
+
diffBytesEmitted:
|
|
532
|
+
"diffBytesEmitted" in record.payload ? record.payload.diffBytesEmitted : null,
|
|
533
|
+
dirtyLines: "dirtyLines" in record.payload ? record.payload.dirtyLines : null,
|
|
534
|
+
dirtyCells: "dirtyCells" in record.payload ? record.payload.dirtyCells : null,
|
|
535
|
+
damageRects: "damageRects" in record.payload ? record.payload.damageRects : null,
|
|
536
|
+
usDrawlist: "usDrawlist" in record.payload ? record.payload.usDrawlist : null,
|
|
537
|
+
usDiff: "usDiff" in record.payload ? record.payload.usDiff : null,
|
|
538
|
+
usWrite: "usWrite" in record.payload ? record.payload.usWrite : null,
|
|
539
|
+
});
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
debugSnapshot("runtime.debug.frame.raw", {
|
|
544
|
+
recordId: header.recordId.toString(),
|
|
545
|
+
frameId: header.frameId.toString(),
|
|
546
|
+
code: header.code,
|
|
547
|
+
severity: header.severity,
|
|
548
|
+
payloadSize: header.payloadSize,
|
|
549
|
+
route: currentRouteId(),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (header.category === "drawlist") {
|
|
554
|
+
if (
|
|
555
|
+
record.payload &&
|
|
556
|
+
typeof record.payload === "object" &&
|
|
557
|
+
"totalBytes" in record.payload &&
|
|
558
|
+
"cmdCount" in record.payload
|
|
559
|
+
) {
|
|
560
|
+
debugSnapshot("runtime.debug.drawlist", {
|
|
561
|
+
recordId: header.recordId.toString(),
|
|
562
|
+
frameId: header.frameId.toString(),
|
|
563
|
+
code: header.code,
|
|
564
|
+
severity: header.severity,
|
|
565
|
+
route: currentRouteId(),
|
|
566
|
+
totalBytes: record.payload.totalBytes,
|
|
567
|
+
cmdCount: record.payload.cmdCount,
|
|
568
|
+
validationResult:
|
|
569
|
+
"validationResult" in record.payload ? record.payload.validationResult : null,
|
|
570
|
+
executionResult:
|
|
571
|
+
"executionResult" in record.payload ? record.payload.executionResult : null,
|
|
572
|
+
clipStackMaxDepth:
|
|
573
|
+
"clipStackMaxDepth" in record.payload ? record.payload.clipStackMaxDepth : null,
|
|
574
|
+
textRuns: "textRuns" in record.payload ? record.payload.textRuns : null,
|
|
575
|
+
fillRects: "fillRects" in record.payload ? record.payload.fillRects : null,
|
|
576
|
+
});
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (
|
|
581
|
+
record.payload &&
|
|
582
|
+
typeof record.payload === "object" &&
|
|
583
|
+
"kind" in record.payload &&
|
|
584
|
+
record.payload.kind === "drawlistBytes" &&
|
|
585
|
+
"bytes" in record.payload
|
|
586
|
+
) {
|
|
587
|
+
debugSnapshot("runtime.debug.drawlistBytes", {
|
|
588
|
+
recordId: header.recordId.toString(),
|
|
589
|
+
frameId: header.frameId.toString(),
|
|
590
|
+
code: header.code,
|
|
591
|
+
severity: header.severity,
|
|
592
|
+
route: currentRouteId(),
|
|
593
|
+
bytes: record.payload.bytes.byteLength,
|
|
594
|
+
});
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
debugSnapshot("runtime.debug.drawlist.raw", {
|
|
599
|
+
recordId: header.recordId.toString(),
|
|
600
|
+
frameId: header.frameId.toString(),
|
|
601
|
+
code: header.code,
|
|
602
|
+
severity: header.severity,
|
|
603
|
+
payloadSize: header.payloadSize,
|
|
604
|
+
route: currentRouteId(),
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
async function setupDebugTrace(): Promise<void> {
|
|
610
|
+
if (!DEBUG_TRACE_ENABLED) return;
|
|
611
|
+
try {
|
|
612
|
+
debugController = createDebugController({
|
|
613
|
+
backend: app.backend.debug,
|
|
614
|
+
terminalCapsProvider: () => app.backend.getCaps(),
|
|
615
|
+
maxFrames: 512,
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
await debugController.enable({
|
|
619
|
+
minSeverity: "trace",
|
|
620
|
+
categoryMask: categoriesToMask(["frame", "drawlist", "error"]),
|
|
621
|
+
captureRawEvents: false,
|
|
622
|
+
captureDrawlistBytes: false,
|
|
623
|
+
});
|
|
624
|
+
await debugController.reset();
|
|
625
|
+
} catch (error) {
|
|
626
|
+
debugSnapshot("runtime.debug.enable.error", {
|
|
627
|
+
message: error instanceof Error ? error.message : String(error),
|
|
628
|
+
route: currentRouteId(),
|
|
629
|
+
});
|
|
630
|
+
if (debugController) {
|
|
631
|
+
try {
|
|
632
|
+
await debugController.disable();
|
|
633
|
+
} catch {
|
|
634
|
+
// Ignore debug shutdown races.
|
|
635
|
+
}
|
|
636
|
+
debugController = null;
|
|
637
|
+
}
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
debugLastRecordId = 0n;
|
|
642
|
+
|
|
643
|
+
debugSnapshot("runtime.debug.enable", {
|
|
644
|
+
route: currentRouteId(),
|
|
645
|
+
viewportCols: lastViewport.cols,
|
|
646
|
+
viewportRows: lastViewport.rows,
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
const pump = async () => {
|
|
650
|
+
if (!debugController || debugTraceInFlight) return;
|
|
651
|
+
debugTraceInFlight = true;
|
|
652
|
+
try {
|
|
653
|
+
const records = await debugController.query({ maxRecords: 256 });
|
|
654
|
+
if (records.length === 0) return;
|
|
655
|
+
for (const record of records) {
|
|
656
|
+
if (record.header.recordId <= debugLastRecordId) continue;
|
|
657
|
+
debugLastRecordId = record.header.recordId;
|
|
658
|
+
snapshotDebugRecord(record);
|
|
659
|
+
}
|
|
660
|
+
} catch (error) {
|
|
661
|
+
debugSnapshot("runtime.debug.query.error", {
|
|
662
|
+
message: error instanceof Error ? error.message : String(error),
|
|
663
|
+
route: currentRouteId(),
|
|
664
|
+
});
|
|
665
|
+
} finally {
|
|
666
|
+
debugTraceInFlight = false;
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
debugTraceTimer = setInterval(() => {
|
|
671
|
+
void pump();
|
|
672
|
+
}, 250);
|
|
673
|
+
}
|
|
674
|
+
|
|
467
675
|
const routes = buildRoutes(createStarshipRoutes);
|
|
468
676
|
|
|
469
677
|
debugSnapshot("runtime.app.create", {
|
|
470
678
|
routeCount: routes.length,
|
|
471
679
|
initialRoute: "bridge",
|
|
472
680
|
fpsCap: UI_FPS_CAP,
|
|
473
|
-
executionMode:
|
|
681
|
+
executionMode: EXECUTION_MODE,
|
|
474
682
|
});
|
|
475
683
|
|
|
476
684
|
app = createNodeApp({
|
|
@@ -479,7 +687,7 @@ app = createNodeApp({
|
|
|
479
687
|
initialRoute: "bridge",
|
|
480
688
|
config: {
|
|
481
689
|
fpsCap: UI_FPS_CAP,
|
|
482
|
-
executionMode:
|
|
690
|
+
executionMode: EXECUTION_MODE,
|
|
483
691
|
},
|
|
484
692
|
theme: themeSpec(initialState.themeName).theme,
|
|
485
693
|
...(enableHsr
|
|
@@ -545,5 +753,9 @@ debugSnapshot("runtime.app.start", {
|
|
|
545
753
|
viewportRows: lastViewport.rows,
|
|
546
754
|
});
|
|
547
755
|
|
|
756
|
+
await setupDebugTrace();
|
|
548
757
|
await app.start();
|
|
549
|
-
await
|
|
758
|
+
await stopPromise;
|
|
759
|
+
if (stopCode !== 0) {
|
|
760
|
+
process.exitCode = stopCode;
|
|
761
|
+
}
|
|
@@ -554,7 +554,7 @@ export function renderBridgeScreen(
|
|
|
554
554
|
title: "Bridge Overview",
|
|
555
555
|
context,
|
|
556
556
|
deps,
|
|
557
|
-
body: ui.column({ gap: SPACE.sm, width: "100%" }, [
|
|
557
|
+
body: ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
558
558
|
BridgeCommandDeck({ key: "bridge-command-deck", state, dispatch: deps.dispatch }),
|
|
559
559
|
]),
|
|
560
560
|
});
|
|
@@ -30,8 +30,12 @@ export function renderCargoScreen(
|
|
|
30
30
|
title: "Cargo Hold",
|
|
31
31
|
context,
|
|
32
32
|
deps,
|
|
33
|
-
body: ui.column({ gap: SPACE.sm, width: "100%" }, [
|
|
34
|
-
CargoDeck({
|
|
33
|
+
body: ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
34
|
+
CargoDeck({
|
|
35
|
+
key: "cargo-deck",
|
|
36
|
+
state: context.state,
|
|
37
|
+
dispatch: deps.dispatch,
|
|
38
|
+
}),
|
|
35
39
|
]),
|
|
36
40
|
});
|
|
37
41
|
}
|
|
@@ -462,8 +462,12 @@ export function renderCommsScreen(
|
|
|
462
462
|
title: "Communications",
|
|
463
463
|
context,
|
|
464
464
|
deps,
|
|
465
|
-
body: ui.column({ gap: SPACE.sm, width: "100%" }, [
|
|
466
|
-
CommsDeck({
|
|
465
|
+
body: ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
466
|
+
CommsDeck({
|
|
467
|
+
key: "comms-deck",
|
|
468
|
+
state: context.state,
|
|
469
|
+
dispatch: deps.dispatch,
|
|
470
|
+
}),
|
|
467
471
|
]),
|
|
468
472
|
});
|
|
469
473
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type RouteRenderContext,
|
|
8
8
|
type VNode,
|
|
9
9
|
} from "@rezi-ui/core";
|
|
10
|
+
import { debugSnapshot } from "../helpers/debug.js";
|
|
10
11
|
import { resolveLayout } from "../helpers/layout.js";
|
|
11
12
|
import { crewCounts, departmentLabel, rankBadge, statusBadge } from "../helpers/formatters.js";
|
|
12
13
|
import { selectedCrew, visibleCrew } from "../helpers/state.js";
|
|
@@ -193,7 +194,7 @@ const CrewDeck = defineWidget<CrewDeckProps>((props, ctx): VNode => {
|
|
|
193
194
|
...tableSkin(tokens),
|
|
194
195
|
});
|
|
195
196
|
|
|
196
|
-
const detailPanel = ui.column({ gap: SPACE.sm }, [
|
|
197
|
+
const detailPanel = ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
197
198
|
maybe(selected, (member) =>
|
|
198
199
|
surfacePanel(
|
|
199
200
|
tokens,
|
|
@@ -300,8 +301,18 @@ const CrewDeck = defineWidget<CrewDeckProps>((props, ctx): VNode => {
|
|
|
300
301
|
),
|
|
301
302
|
]);
|
|
302
303
|
|
|
303
|
-
const manifestBlock = ui.column({ gap: SPACE.sm }, [
|
|
304
|
-
|
|
304
|
+
const manifestBlock = ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
305
|
+
ui.box(
|
|
306
|
+
{
|
|
307
|
+
border: "none",
|
|
308
|
+
p: 0,
|
|
309
|
+
width: "100%",
|
|
310
|
+
flex: 1,
|
|
311
|
+
minHeight: 10,
|
|
312
|
+
overflow: "hidden",
|
|
313
|
+
},
|
|
314
|
+
[surfacePanel(tokens, "Crew Manifest", [table])],
|
|
315
|
+
),
|
|
305
316
|
ui.pagination({
|
|
306
317
|
id: ctx.id("crew-pagination"),
|
|
307
318
|
page,
|
|
@@ -313,126 +324,185 @@ const CrewDeck = defineWidget<CrewDeckProps>((props, ctx): VNode => {
|
|
|
313
324
|
|
|
314
325
|
const deckLayout = layout.wide
|
|
315
326
|
? showDetailPane
|
|
316
|
-
? ui.
|
|
317
|
-
id: ctx.id("crew-master-detail"),
|
|
318
|
-
masterWidth: layout.crewMasterWidth,
|
|
319
|
-
master: manifestBlock,
|
|
320
|
-
detail: detailPanel,
|
|
321
|
-
})
|
|
322
|
-
: manifestBlock
|
|
323
|
-
: showDetailPane
|
|
324
|
-
? ui.column({ gap: SPACE.sm }, [manifestBlock, detailPanel])
|
|
325
|
-
: manifestBlock;
|
|
326
|
-
|
|
327
|
-
const operationsPanel = surfacePanel(
|
|
328
|
-
tokens,
|
|
329
|
-
"Crew Operations",
|
|
330
|
-
[
|
|
331
|
-
sectionHeader(tokens, "Manifest Controls", "Consistent staffing and assignment flow"),
|
|
332
|
-
ui.row({ gap: SPACE.md, wrap: !layout.wide, items: "start" }, [
|
|
333
|
-
ui.box(
|
|
327
|
+
? ui.row(
|
|
334
328
|
{
|
|
335
|
-
|
|
336
|
-
p: 0,
|
|
329
|
+
id: ctx.id("crew-master-detail"),
|
|
337
330
|
gap: SPACE.sm,
|
|
338
|
-
|
|
331
|
+
width: "100%",
|
|
332
|
+
height: "100%",
|
|
333
|
+
items: "stretch",
|
|
334
|
+
wrap: false,
|
|
339
335
|
},
|
|
340
336
|
[
|
|
341
|
-
ui.
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
id: ctx.id("crew-new-assignment"),
|
|
362
|
-
label: "New Assignment",
|
|
363
|
-
intent: "primary",
|
|
364
|
-
onPress: () => props.dispatch({ type: "toggle-crew-editor" }),
|
|
365
|
-
}),
|
|
366
|
-
ui.button({
|
|
367
|
-
id: ctx.id("crew-edit-selected"),
|
|
368
|
-
label: "Edit Selected",
|
|
369
|
-
intent: "secondary",
|
|
370
|
-
onPress: () => props.dispatch({ type: "toggle-crew-editor" }),
|
|
371
|
-
}),
|
|
372
|
-
]),
|
|
337
|
+
ui.box(
|
|
338
|
+
{
|
|
339
|
+
border: "none",
|
|
340
|
+
p: 0,
|
|
341
|
+
width: layout.crewMasterWidth,
|
|
342
|
+
height: "100%",
|
|
343
|
+
overflow: "hidden",
|
|
344
|
+
},
|
|
345
|
+
[manifestBlock],
|
|
346
|
+
),
|
|
347
|
+
ui.box(
|
|
348
|
+
{
|
|
349
|
+
border: "none",
|
|
350
|
+
p: 0,
|
|
351
|
+
flex: 1,
|
|
352
|
+
height: "100%",
|
|
353
|
+
overflow: "hidden",
|
|
354
|
+
},
|
|
355
|
+
[detailPanel],
|
|
356
|
+
),
|
|
373
357
|
],
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
358
|
+
)
|
|
359
|
+
: manifestBlock
|
|
360
|
+
: showDetailPane
|
|
361
|
+
? ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
362
|
+
ui.box(
|
|
363
|
+
{ border: "none", p: 0, width: "100%", flex: 1, minHeight: 10, overflow: "hidden" },
|
|
364
|
+
[manifestBlock],
|
|
365
|
+
),
|
|
366
|
+
ui.box(
|
|
367
|
+
{ border: "none", p: 0, width: "100%", flex: 1, minHeight: 10, overflow: "hidden" },
|
|
368
|
+
[detailPanel],
|
|
369
|
+
),
|
|
370
|
+
])
|
|
371
|
+
: manifestBlock;
|
|
372
|
+
|
|
373
|
+
const operationsPanelMaxHeight = Math.max(12, Math.min(22, Math.floor(layout.height * 0.34)));
|
|
374
|
+
debugSnapshot("crew.render", {
|
|
375
|
+
viewportCols: props.state.viewportCols,
|
|
376
|
+
viewportRows: props.state.viewportRows,
|
|
377
|
+
visibleCount: visible.length,
|
|
378
|
+
sortedCount: sorted.length,
|
|
379
|
+
page,
|
|
380
|
+
totalPages,
|
|
381
|
+
pageDataCount: pageData.length,
|
|
382
|
+
showDetailPane,
|
|
383
|
+
operationsPanelMaxHeight,
|
|
384
|
+
editingCrew: props.state.editingCrew,
|
|
385
|
+
});
|
|
386
|
+
const operationsPanel = ui.box(
|
|
387
|
+
{
|
|
388
|
+
border: "none",
|
|
389
|
+
p: 0,
|
|
390
|
+
width: "100%",
|
|
391
|
+
height: operationsPanelMaxHeight,
|
|
392
|
+
overflow: "scroll",
|
|
393
|
+
},
|
|
394
|
+
[
|
|
395
|
+
surfacePanel(
|
|
396
|
+
tokens,
|
|
397
|
+
"Crew Operations",
|
|
398
|
+
[
|
|
399
|
+
sectionHeader(tokens, "Manifest Controls", "Consistent staffing and assignment flow"),
|
|
400
|
+
ui.row({ gap: SPACE.md, wrap: !layout.wide, items: "start" }, [
|
|
401
|
+
ui.box(
|
|
402
|
+
{
|
|
403
|
+
border: "none",
|
|
404
|
+
p: 0,
|
|
405
|
+
gap: SPACE.sm,
|
|
406
|
+
...(layout.wide ? { flex: 2 } : {}),
|
|
407
|
+
},
|
|
408
|
+
[
|
|
409
|
+
ui.row({ gap: SPACE.sm, wrap: true }, [
|
|
410
|
+
ui.badge(`Total ${counts.total}`, { variant: "info" }),
|
|
411
|
+
ui.badge(`Active ${counts.active}`, { variant: "success" }),
|
|
412
|
+
ui.badge(`Away ${counts.away}`, { variant: "warning" }),
|
|
413
|
+
ui.badge(`Injured ${counts.injured}`, { variant: "error" }),
|
|
414
|
+
]),
|
|
415
|
+
ui.form([
|
|
416
|
+
ui.field({
|
|
417
|
+
label: "Search Crew",
|
|
418
|
+
hint: "Filter by name, rank, or department",
|
|
419
|
+
children: ui.input({
|
|
420
|
+
id: ctx.id("crew-search"),
|
|
421
|
+
value: props.state.crewSearchQuery,
|
|
422
|
+
placeholder: "Type to filter",
|
|
423
|
+
onInput: (value) => props.dispatch({ type: "set-crew-search", query: value }),
|
|
424
|
+
}),
|
|
425
|
+
}),
|
|
426
|
+
]),
|
|
427
|
+
ui.actions([
|
|
428
|
+
ui.button({
|
|
429
|
+
id: ctx.id("crew-new-assignment"),
|
|
430
|
+
label: "New Assignment",
|
|
431
|
+
intent: "primary",
|
|
432
|
+
onPress: () => props.dispatch({ type: "toggle-crew-editor" }),
|
|
433
|
+
}),
|
|
434
|
+
ui.button({
|
|
435
|
+
id: ctx.id("crew-edit-selected"),
|
|
436
|
+
label: "Edit Selected",
|
|
437
|
+
intent: "secondary",
|
|
438
|
+
onPress: () => props.dispatch({ type: "toggle-crew-editor" }),
|
|
439
|
+
}),
|
|
440
|
+
]),
|
|
441
|
+
],
|
|
442
|
+
),
|
|
443
|
+
...(layout.wide
|
|
444
|
+
? [
|
|
445
|
+
ui.box(
|
|
446
|
+
{
|
|
447
|
+
border: "none",
|
|
448
|
+
p: 0,
|
|
449
|
+
flex: 1,
|
|
450
|
+
},
|
|
387
451
|
[
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
452
|
+
surfacePanel(
|
|
453
|
+
tokens,
|
|
454
|
+
"Crew Snapshot",
|
|
455
|
+
[
|
|
456
|
+
selected
|
|
457
|
+
? ui.column({ gap: SPACE.xs }, [
|
|
458
|
+
ui.text(selected.name, { variant: "label" }),
|
|
459
|
+
ui.row({ gap: SPACE.xs, wrap: true }, [
|
|
460
|
+
ui.badge(rankBadge(selected.rank).text, {
|
|
461
|
+
variant: rankBadge(selected.rank).variant,
|
|
462
|
+
}),
|
|
463
|
+
ui.badge(statusBadge(selected.status).text, {
|
|
464
|
+
variant: statusBadge(selected.status).variant,
|
|
465
|
+
}),
|
|
466
|
+
ui.tag(departmentLabel(selected.department), { variant: "info" }),
|
|
467
|
+
]),
|
|
468
|
+
])
|
|
469
|
+
: ui.text("No crew selected", {
|
|
470
|
+
variant: "caption",
|
|
471
|
+
style: { fg: tokens.text.muted, dim: true },
|
|
394
472
|
}),
|
|
395
|
-
|
|
396
|
-
|
|
473
|
+
ui.divider(),
|
|
474
|
+
ui.row({ gap: SPACE.xs, wrap: true }, [
|
|
475
|
+
ui.badge(`Visible ${sorted.length}`, { variant: "info" }),
|
|
476
|
+
ui.badge(`Page ${page}/${totalPages}`, { variant: "default" }),
|
|
477
|
+
]),
|
|
478
|
+
staffingError
|
|
479
|
+
? ui.callout("Critical staffing below minimum.", {
|
|
480
|
+
title: "Guardrail",
|
|
481
|
+
variant: "warning",
|
|
482
|
+
})
|
|
483
|
+
: ui.callout("Critical staffing thresholds healthy.", {
|
|
484
|
+
title: "Guardrail",
|
|
485
|
+
variant: "success",
|
|
397
486
|
}),
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
ui.divider(),
|
|
406
|
-
ui.row({ gap: SPACE.xs, wrap: true }, [
|
|
407
|
-
ui.badge(`Visible ${sorted.length}`, { variant: "info" }),
|
|
408
|
-
ui.badge(`Page ${page}/${totalPages}`, { variant: "default" }),
|
|
409
|
-
]),
|
|
410
|
-
staffingError
|
|
411
|
-
? ui.callout("Critical staffing below minimum.", {
|
|
412
|
-
title: "Guardrail",
|
|
413
|
-
variant: "warning",
|
|
414
|
-
})
|
|
415
|
-
: ui.callout("Critical staffing thresholds healthy.", {
|
|
416
|
-
title: "Guardrail",
|
|
417
|
-
variant: "success",
|
|
418
|
-
}),
|
|
487
|
+
],
|
|
488
|
+
{
|
|
489
|
+
tone: "inset",
|
|
490
|
+
p: SPACE.sm,
|
|
491
|
+
gap: SPACE.sm,
|
|
492
|
+
},
|
|
493
|
+
),
|
|
419
494
|
],
|
|
420
|
-
{
|
|
421
|
-
tone: "inset",
|
|
422
|
-
p: SPACE.sm,
|
|
423
|
-
gap: SPACE.sm,
|
|
424
|
-
},
|
|
425
495
|
),
|
|
426
|
-
]
|
|
427
|
-
),
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
496
|
+
]
|
|
497
|
+
: []),
|
|
498
|
+
]),
|
|
499
|
+
],
|
|
500
|
+
{ tone: "base" },
|
|
501
|
+
),
|
|
431
502
|
],
|
|
432
|
-
{ tone: "base" },
|
|
433
503
|
);
|
|
434
504
|
|
|
435
|
-
return ui.column({ gap: SPACE.md, width: "100%" }, [
|
|
505
|
+
return ui.column({ gap: SPACE.md, width: "100%", height: "100%" }, [
|
|
436
506
|
operationsPanel,
|
|
437
507
|
show(
|
|
438
508
|
asyncCrew.loading,
|
|
@@ -443,7 +513,20 @@ const CrewDeck = defineWidget<CrewDeckProps>((props, ctx): VNode => {
|
|
|
443
513
|
ui.skeleton(44, { variant: "text" }),
|
|
444
514
|
], { tone: "inset" }),
|
|
445
515
|
),
|
|
446
|
-
show(
|
|
516
|
+
show(
|
|
517
|
+
!asyncCrew.loading,
|
|
518
|
+
ui.box(
|
|
519
|
+
{
|
|
520
|
+
border: "none",
|
|
521
|
+
p: 0,
|
|
522
|
+
width: "100%",
|
|
523
|
+
flex: 1,
|
|
524
|
+
minHeight: 12,
|
|
525
|
+
overflow: "hidden",
|
|
526
|
+
},
|
|
527
|
+
[deckLayout],
|
|
528
|
+
),
|
|
529
|
+
),
|
|
447
530
|
]);
|
|
448
531
|
});
|
|
449
532
|
|
|
@@ -452,8 +535,12 @@ export function renderCrewScreen(context: RouteRenderContext<StarshipState>, dep
|
|
|
452
535
|
title: "Crew Manifest",
|
|
453
536
|
context,
|
|
454
537
|
deps,
|
|
455
|
-
body: ui.column({ gap: SPACE.sm, width: "100%" }, [
|
|
456
|
-
CrewDeck({
|
|
538
|
+
body: ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
539
|
+
CrewDeck({
|
|
540
|
+
key: "crew-deck",
|
|
541
|
+
state: context.state,
|
|
542
|
+
dispatch: deps.dispatch,
|
|
543
|
+
}),
|
|
457
544
|
]),
|
|
458
545
|
});
|
|
459
546
|
}
|
|
@@ -371,16 +371,27 @@ const EngineeringDeck = defineWidget<EngineeringDeckProps>((props, ctx): VNode =
|
|
|
371
371
|
}),
|
|
372
372
|
]);
|
|
373
373
|
|
|
374
|
-
const leftPane =
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
374
|
+
const leftPane = showSecondaryPanels
|
|
375
|
+
? ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
376
|
+
ui.box({ border: "none", p: 0, width: "100%", flex: 3, minHeight: 12 }, [reactorPanel]),
|
|
377
|
+
ui.box({ border: "none", p: 0, width: "100%", flex: 2, minHeight: 10, overflow: "hidden" }, [
|
|
378
|
+
treePanel,
|
|
379
|
+
]),
|
|
380
|
+
])
|
|
381
|
+
: ui.column({ gap: SPACE.sm, width: "100%" }, [reactorPanel]);
|
|
382
|
+
const rightPane = showSecondaryPanels
|
|
383
|
+
? ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
384
|
+
ui.box({ border: "none", p: 0, width: "100%", flex: 3, minHeight: 12, overflow: "hidden" }, [
|
|
385
|
+
powerPanel,
|
|
386
|
+
]),
|
|
387
|
+
ui.box({ border: "none", p: 0, width: "100%", flex: 2, minHeight: 10 }, [thermalPanel]),
|
|
388
|
+
ui.box({ border: "none", p: 0, width: "100%", flex: 2, minHeight: 10, overflow: "hidden" }, [
|
|
389
|
+
diagnosticsPanel,
|
|
390
|
+
]),
|
|
391
|
+
])
|
|
392
|
+
: ui.column({ gap: SPACE.sm, width: "100%" }, [powerPanel]);
|
|
382
393
|
|
|
383
|
-
const
|
|
394
|
+
const responsiveDeckMinHeight = Math.max(
|
|
384
395
|
16,
|
|
385
396
|
contentRows - (showControlsSummary ? 12 : 10) - (showSecondaryPanels ? 0 : 2),
|
|
386
397
|
);
|
|
@@ -395,7 +406,8 @@ const EngineeringDeck = defineWidget<EngineeringDeckProps>((props, ctx): VNode =
|
|
|
395
406
|
border: "none",
|
|
396
407
|
p: 0,
|
|
397
408
|
width: "100%",
|
|
398
|
-
|
|
409
|
+
flex: 1,
|
|
410
|
+
minHeight: responsiveDeckMinHeight,
|
|
399
411
|
overflow: "scroll",
|
|
400
412
|
},
|
|
401
413
|
[responsiveDeckBody],
|
|
@@ -491,7 +503,7 @@ const EngineeringDeck = defineWidget<EngineeringDeckProps>((props, ctx): VNode =
|
|
|
491
503
|
includeResponsiveDeck: renderMode === "full",
|
|
492
504
|
responsiveDeckMode: useWideRow ? "row" : "column",
|
|
493
505
|
forceStackViaEnv,
|
|
494
|
-
|
|
506
|
+
responsiveDeckMinHeight,
|
|
495
507
|
});
|
|
496
508
|
|
|
497
509
|
if (veryCompactHeight) {
|
|
@@ -502,7 +514,7 @@ const EngineeringDeck = defineWidget<EngineeringDeckProps>((props, ctx): VNode =
|
|
|
502
514
|
return ui.column({ gap: SPACE.sm, width: "100%" }, [controlsPanel, reactorPanel]);
|
|
503
515
|
}
|
|
504
516
|
|
|
505
|
-
return ui.column({ gap: SPACE.sm, width: "100%" }, [
|
|
517
|
+
return ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
506
518
|
controlsRegion,
|
|
507
519
|
responsiveDeck,
|
|
508
520
|
]);
|
|
@@ -516,8 +528,12 @@ export function renderEngineeringScreen(
|
|
|
516
528
|
title: "Engineering Deck",
|
|
517
529
|
context,
|
|
518
530
|
deps,
|
|
519
|
-
body: ui.column({ gap: SPACE.sm, width: "100%" }, [
|
|
520
|
-
EngineeringDeck({
|
|
531
|
+
body: ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
532
|
+
EngineeringDeck({
|
|
533
|
+
key: "engineering-deck",
|
|
534
|
+
state: context.state,
|
|
535
|
+
dispatch: deps.dispatch,
|
|
536
|
+
}),
|
|
521
537
|
]),
|
|
522
538
|
});
|
|
523
539
|
}
|
|
@@ -209,7 +209,7 @@ function settingsRightRail(state: StarshipState, deps: RouteDeps): VNode {
|
|
|
209
209
|
subtitle: activeTheme.label,
|
|
210
210
|
actions: [ui.badge("Preview", { variant: "info" })],
|
|
211
211
|
}),
|
|
212
|
-
body: ui.column({ gap: SPACE.xs }, [
|
|
212
|
+
body: ui.column({ gap: SPACE.xs, width: "100%", height: "100%" }, [
|
|
213
213
|
ui.breadcrumb({
|
|
214
214
|
items: [{ label: "Bridge" }, { label: "Settings" }, { label: "Theme Preview" }],
|
|
215
215
|
}),
|
|
@@ -277,8 +277,9 @@ export function renderSettingsScreen(
|
|
|
277
277
|
title: "Ship Settings",
|
|
278
278
|
context,
|
|
279
279
|
deps,
|
|
280
|
-
body: ui.column({ gap: SPACE.sm, width: "100%" }, [
|
|
280
|
+
body: ui.column({ gap: SPACE.sm, width: "100%", height: "100%" }, [
|
|
281
281
|
SettingsDeck({
|
|
282
|
+
key: "settings-deck",
|
|
282
283
|
state: context.state,
|
|
283
284
|
dispatch: deps.dispatch,
|
|
284
285
|
}),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type BadgeVariant,
|
|
3
|
-
type
|
|
3
|
+
type Rgb24,
|
|
4
4
|
type TextStyle,
|
|
5
5
|
type ThemeDefinition,
|
|
6
|
-
darkTheme,
|
|
7
6
|
draculaTheme,
|
|
8
7
|
extendTheme,
|
|
9
8
|
nordTheme,
|
|
9
|
+
rgb,
|
|
10
10
|
} from "@rezi-ui/core";
|
|
11
11
|
import type { AlertLevel, ThemeName } from "./types.js";
|
|
12
12
|
|
|
@@ -42,52 +42,52 @@ const DAY_SHIFT_THEME = extendTheme(nordTheme, {
|
|
|
42
42
|
focusIndicator: {
|
|
43
43
|
bold: true,
|
|
44
44
|
underline: false,
|
|
45
|
-
focusRingColor:
|
|
45
|
+
focusRingColor: rgb(118, 208, 255),
|
|
46
46
|
},
|
|
47
47
|
colors: {
|
|
48
48
|
bg: {
|
|
49
|
-
base:
|
|
50
|
-
elevated:
|
|
51
|
-
overlay:
|
|
52
|
-
subtle:
|
|
49
|
+
base: rgb(29, 42, 58),
|
|
50
|
+
elevated: rgb(40, 57, 76),
|
|
51
|
+
overlay: rgb(52, 72, 94),
|
|
52
|
+
subtle: rgb(34, 49, 67),
|
|
53
53
|
},
|
|
54
54
|
fg: {
|
|
55
|
-
primary:
|
|
56
|
-
secondary:
|
|
57
|
-
muted:
|
|
58
|
-
inverse:
|
|
55
|
+
primary: rgb(236, 243, 255),
|
|
56
|
+
secondary: rgb(190, 217, 242),
|
|
57
|
+
muted: rgb(108, 138, 168),
|
|
58
|
+
inverse: rgb(20, 30, 42),
|
|
59
59
|
},
|
|
60
60
|
border: {
|
|
61
|
-
subtle:
|
|
62
|
-
default:
|
|
63
|
-
strong:
|
|
61
|
+
subtle: rgb(74, 98, 126),
|
|
62
|
+
default: rgb(104, 136, 168),
|
|
63
|
+
strong: rgb(137, 171, 206),
|
|
64
64
|
},
|
|
65
65
|
accent: {
|
|
66
|
-
primary:
|
|
67
|
-
secondary:
|
|
68
|
-
tertiary:
|
|
66
|
+
primary: rgb(106, 195, 255),
|
|
67
|
+
secondary: rgb(129, 217, 255),
|
|
68
|
+
tertiary: rgb(180, 231, 164),
|
|
69
69
|
},
|
|
70
|
-
info:
|
|
71
|
-
success:
|
|
72
|
-
warning:
|
|
73
|
-
error:
|
|
70
|
+
info: rgb(118, 208, 255),
|
|
71
|
+
success: rgb(166, 228, 149),
|
|
72
|
+
warning: rgb(255, 211, 131),
|
|
73
|
+
error: rgb(239, 118, 132),
|
|
74
74
|
selected: {
|
|
75
|
-
bg:
|
|
76
|
-
fg:
|
|
75
|
+
bg: rgb(68, 105, 139),
|
|
76
|
+
fg: rgb(236, 243, 255),
|
|
77
77
|
},
|
|
78
78
|
disabled: {
|
|
79
|
-
fg:
|
|
80
|
-
bg:
|
|
79
|
+
fg: rgb(95, 121, 149),
|
|
80
|
+
bg: rgb(39, 55, 72),
|
|
81
81
|
},
|
|
82
82
|
diagnostic: {
|
|
83
|
-
error:
|
|
84
|
-
warning:
|
|
85
|
-
info:
|
|
86
|
-
hint:
|
|
83
|
+
error: rgb(239, 118, 132),
|
|
84
|
+
warning: rgb(255, 211, 131),
|
|
85
|
+
info: rgb(118, 208, 255),
|
|
86
|
+
hint: rgb(149, 187, 228),
|
|
87
87
|
},
|
|
88
88
|
focus: {
|
|
89
|
-
ring:
|
|
90
|
-
bg:
|
|
89
|
+
ring: rgb(118, 208, 255),
|
|
90
|
+
bg: rgb(63, 96, 126),
|
|
91
91
|
},
|
|
92
92
|
},
|
|
93
93
|
});
|
|
@@ -105,52 +105,52 @@ const NIGHT_SHIFT_THEME = extendTheme(draculaTheme, {
|
|
|
105
105
|
focusIndicator: {
|
|
106
106
|
bold: true,
|
|
107
107
|
underline: false,
|
|
108
|
-
focusRingColor:
|
|
108
|
+
focusRingColor: rgb(176, 133, 255),
|
|
109
109
|
},
|
|
110
110
|
colors: {
|
|
111
111
|
bg: {
|
|
112
|
-
base:
|
|
113
|
-
elevated:
|
|
114
|
-
overlay:
|
|
115
|
-
subtle:
|
|
112
|
+
base: rgb(19, 22, 33),
|
|
113
|
+
elevated: rgb(28, 31, 46),
|
|
114
|
+
overlay: rgb(37, 41, 60),
|
|
115
|
+
subtle: rgb(24, 27, 40),
|
|
116
116
|
},
|
|
117
117
|
fg: {
|
|
118
|
-
primary:
|
|
119
|
-
secondary:
|
|
120
|
-
muted:
|
|
121
|
-
inverse:
|
|
118
|
+
primary: rgb(244, 246, 252),
|
|
119
|
+
secondary: rgb(202, 185, 252),
|
|
120
|
+
muted: rgb(131, 146, 186),
|
|
121
|
+
inverse: rgb(19, 22, 33),
|
|
122
122
|
},
|
|
123
123
|
accent: {
|
|
124
|
-
primary:
|
|
125
|
-
secondary:
|
|
126
|
-
tertiary:
|
|
124
|
+
primary: rgb(176, 133, 255),
|
|
125
|
+
secondary: rgb(129, 235, 255),
|
|
126
|
+
tertiary: rgb(119, 255, 196),
|
|
127
127
|
},
|
|
128
|
-
info:
|
|
129
|
-
success:
|
|
130
|
-
warning:
|
|
131
|
-
error:
|
|
128
|
+
info: rgb(129, 235, 255),
|
|
129
|
+
success: rgb(110, 249, 174),
|
|
130
|
+
warning: rgb(255, 207, 124),
|
|
131
|
+
error: rgb(255, 118, 132),
|
|
132
132
|
selected: {
|
|
133
|
-
bg:
|
|
134
|
-
fg:
|
|
133
|
+
bg: rgb(68, 76, 112),
|
|
134
|
+
fg: rgb(244, 246, 252),
|
|
135
135
|
},
|
|
136
136
|
disabled: {
|
|
137
|
-
fg:
|
|
138
|
-
bg:
|
|
137
|
+
fg: rgb(99, 111, 146),
|
|
138
|
+
bg: rgb(28, 31, 46),
|
|
139
139
|
},
|
|
140
140
|
diagnostic: {
|
|
141
|
-
error:
|
|
142
|
-
warning:
|
|
143
|
-
info:
|
|
144
|
-
hint:
|
|
141
|
+
error: rgb(255, 118, 132),
|
|
142
|
+
warning: rgb(255, 207, 124),
|
|
143
|
+
info: rgb(129, 235, 255),
|
|
144
|
+
hint: rgb(206, 158, 255),
|
|
145
145
|
},
|
|
146
146
|
focus: {
|
|
147
|
-
ring:
|
|
148
|
-
bg:
|
|
147
|
+
ring: rgb(176, 133, 255),
|
|
148
|
+
bg: rgb(64, 57, 96),
|
|
149
149
|
},
|
|
150
150
|
border: {
|
|
151
|
-
subtle:
|
|
152
|
-
default:
|
|
153
|
-
strong:
|
|
151
|
+
subtle: rgb(43, 49, 71),
|
|
152
|
+
default: rgb(72, 81, 116),
|
|
153
|
+
strong: rgb(104, 115, 156),
|
|
154
154
|
},
|
|
155
155
|
},
|
|
156
156
|
});
|
|
@@ -168,52 +168,52 @@ const RED_ALERT_THEME = extendTheme(draculaTheme, {
|
|
|
168
168
|
focusIndicator: {
|
|
169
169
|
bold: true,
|
|
170
170
|
underline: false,
|
|
171
|
-
focusRingColor:
|
|
171
|
+
focusRingColor: rgb(255, 112, 112),
|
|
172
172
|
},
|
|
173
173
|
colors: {
|
|
174
174
|
bg: {
|
|
175
|
-
base:
|
|
176
|
-
elevated:
|
|
177
|
-
overlay:
|
|
178
|
-
subtle:
|
|
175
|
+
base: rgb(24, 12, 19),
|
|
176
|
+
elevated: rgb(34, 15, 24),
|
|
177
|
+
overlay: rgb(46, 21, 32),
|
|
178
|
+
subtle: rgb(29, 13, 22),
|
|
179
179
|
},
|
|
180
180
|
fg: {
|
|
181
|
-
primary:
|
|
182
|
-
secondary:
|
|
183
|
-
muted:
|
|
184
|
-
inverse:
|
|
181
|
+
primary: rgb(255, 238, 242),
|
|
182
|
+
secondary: rgb(244, 190, 205),
|
|
183
|
+
muted: rgb(170, 122, 139),
|
|
184
|
+
inverse: rgb(24, 12, 19),
|
|
185
185
|
},
|
|
186
186
|
accent: {
|
|
187
|
-
primary:
|
|
188
|
-
secondary:
|
|
189
|
-
tertiary:
|
|
187
|
+
primary: rgb(255, 114, 144),
|
|
188
|
+
secondary: rgb(255, 182, 120),
|
|
189
|
+
tertiary: rgb(255, 220, 146),
|
|
190
190
|
},
|
|
191
|
-
success:
|
|
192
|
-
warning:
|
|
193
|
-
error:
|
|
194
|
-
info:
|
|
191
|
+
success: rgb(134, 247, 176),
|
|
192
|
+
warning: rgb(255, 181, 112),
|
|
193
|
+
error: rgb(255, 93, 117),
|
|
194
|
+
info: rgb(255, 141, 153),
|
|
195
195
|
selected: {
|
|
196
|
-
bg:
|
|
197
|
-
fg:
|
|
196
|
+
bg: rgb(82, 34, 52),
|
|
197
|
+
fg: rgb(255, 238, 242),
|
|
198
198
|
},
|
|
199
199
|
disabled: {
|
|
200
|
-
fg:
|
|
201
|
-
bg:
|
|
200
|
+
fg: rgb(142, 96, 112),
|
|
201
|
+
bg: rgb(34, 15, 24),
|
|
202
202
|
},
|
|
203
203
|
diagnostic: {
|
|
204
|
-
error:
|
|
205
|
-
warning:
|
|
206
|
-
info:
|
|
207
|
-
hint:
|
|
204
|
+
error: rgb(255, 93, 117),
|
|
205
|
+
warning: rgb(255, 181, 112),
|
|
206
|
+
info: rgb(255, 141, 153),
|
|
207
|
+
hint: rgb(255, 203, 133),
|
|
208
208
|
},
|
|
209
209
|
focus: {
|
|
210
|
-
ring:
|
|
211
|
-
bg:
|
|
210
|
+
ring: rgb(255, 112, 112),
|
|
211
|
+
bg: rgb(76, 35, 50),
|
|
212
212
|
},
|
|
213
213
|
border: {
|
|
214
|
-
subtle:
|
|
215
|
-
default:
|
|
216
|
-
strong:
|
|
214
|
+
subtle: rgb(86, 45, 61),
|
|
215
|
+
default: rgb(124, 65, 86),
|
|
216
|
+
strong: rgb(172, 86, 112),
|
|
217
217
|
},
|
|
218
218
|
},
|
|
219
219
|
});
|
|
@@ -244,18 +244,38 @@ function clampChannel(value: number): number {
|
|
|
244
244
|
return Math.max(0, Math.min(255, Math.round(value)));
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
type ColorInput = Rgb24;
|
|
248
|
+
|
|
249
|
+
function packRgb(value: Rgb24): Rgb24 {
|
|
250
|
+
return (Math.round(value) >>> 0) & 0x00ff_ffff;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function rgbChannel(value: Rgb24, shift: 0 | 8 | 16): number {
|
|
254
|
+
return (value >>> shift) & 0xff;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function unpackRgb(value: ColorInput): Readonly<{ r: number; g: number; b: number }> {
|
|
258
|
+
return Object.freeze({
|
|
259
|
+
r: rgbChannel(value, 16),
|
|
260
|
+
g: rgbChannel(value, 8),
|
|
261
|
+
b: rgbChannel(value, 0),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function blend(a: ColorInput, b: ColorInput, weight: number): Rgb24 {
|
|
248
266
|
const safe = Math.max(0, Math.min(1, weight));
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
267
|
+
const left = unpackRgb(a);
|
|
268
|
+
const right = unpackRgb(b);
|
|
269
|
+
return (
|
|
270
|
+
((clampChannel(left.r + (right.r - left.r) * safe) & 0xff) << 16) |
|
|
271
|
+
((clampChannel(left.g + (right.g - left.g) * safe) & 0xff) << 8) |
|
|
272
|
+
(clampChannel(left.b + (right.b - left.b) * safe) & 0xff)
|
|
273
|
+
);
|
|
254
274
|
}
|
|
255
275
|
|
|
256
|
-
export function toHex(color:
|
|
276
|
+
export function toHex(color: Rgb24): string {
|
|
257
277
|
const channel = (value: number) => clampChannel(value).toString(16).padStart(2, "0");
|
|
258
|
-
return `#${channel(color
|
|
278
|
+
return `#${channel(rgbChannel(color, 16))}${channel(rgbChannel(color, 8))}${channel(rgbChannel(color, 0))}`;
|
|
259
279
|
}
|
|
260
280
|
|
|
261
281
|
export function cycleThemeName(current: ThemeName): ThemeName {
|
|
@@ -285,52 +305,52 @@ export type StarshipStyles = Readonly<{
|
|
|
285
305
|
|
|
286
306
|
export type StarshipThemeTokens = Readonly<{
|
|
287
307
|
bg: Readonly<{
|
|
288
|
-
app:
|
|
308
|
+
app: Rgb24;
|
|
289
309
|
panel: Readonly<{
|
|
290
|
-
base:
|
|
291
|
-
inset:
|
|
292
|
-
elevated:
|
|
310
|
+
base: Rgb24;
|
|
311
|
+
inset: Rgb24;
|
|
312
|
+
elevated: Rgb24;
|
|
293
313
|
}>;
|
|
294
|
-
modal:
|
|
314
|
+
modal: Rgb24;
|
|
295
315
|
}>;
|
|
296
316
|
border: Readonly<{
|
|
297
|
-
default:
|
|
298
|
-
muted:
|
|
299
|
-
focus:
|
|
300
|
-
danger:
|
|
317
|
+
default: Rgb24;
|
|
318
|
+
muted: Rgb24;
|
|
319
|
+
focus: Rgb24;
|
|
320
|
+
danger: Rgb24;
|
|
301
321
|
}>;
|
|
302
322
|
text: Readonly<{
|
|
303
|
-
primary:
|
|
304
|
-
muted:
|
|
305
|
-
dim:
|
|
323
|
+
primary: Rgb24;
|
|
324
|
+
muted: Rgb24;
|
|
325
|
+
dim: Rgb24;
|
|
306
326
|
}>;
|
|
307
327
|
accent: Readonly<{
|
|
308
|
-
info:
|
|
309
|
-
success:
|
|
310
|
-
warn:
|
|
311
|
-
danger:
|
|
312
|
-
brand:
|
|
328
|
+
info: Rgb24;
|
|
329
|
+
success: Rgb24;
|
|
330
|
+
warn: Rgb24;
|
|
331
|
+
danger: Rgb24;
|
|
332
|
+
brand: Rgb24;
|
|
313
333
|
}>;
|
|
314
334
|
state: Readonly<{
|
|
315
|
-
selectedBg:
|
|
316
|
-
selectedText:
|
|
317
|
-
hoverBg:
|
|
318
|
-
focusRing:
|
|
335
|
+
selectedBg: Rgb24;
|
|
336
|
+
selectedText: Rgb24;
|
|
337
|
+
hoverBg: Rgb24;
|
|
338
|
+
focusRing: Rgb24;
|
|
319
339
|
}>;
|
|
320
340
|
progress: Readonly<{
|
|
321
|
-
track:
|
|
322
|
-
fill:
|
|
341
|
+
track: Rgb24;
|
|
342
|
+
fill: Rgb24;
|
|
323
343
|
}>;
|
|
324
344
|
table: Readonly<{
|
|
325
|
-
headerBg:
|
|
326
|
-
rowAltBg:
|
|
327
|
-
rowHoverBg:
|
|
328
|
-
rowSelectedBg:
|
|
345
|
+
headerBg: Rgb24;
|
|
346
|
+
rowAltBg: Rgb24;
|
|
347
|
+
rowHoverBg: Rgb24;
|
|
348
|
+
rowSelectedBg: Rgb24;
|
|
329
349
|
}>;
|
|
330
350
|
log: Readonly<{
|
|
331
|
-
info:
|
|
332
|
-
warn:
|
|
333
|
-
error:
|
|
351
|
+
info: Rgb24;
|
|
352
|
+
warn: Rgb24;
|
|
353
|
+
error: Rgb24;
|
|
334
354
|
}>;
|
|
335
355
|
}>;
|
|
336
356
|
|
|
@@ -384,37 +404,37 @@ export function themeTokens(themeName: ThemeName): StarshipThemeTokens {
|
|
|
384
404
|
: blend(colors.accent.primary, colors.accent.secondary, mode === "day" ? 0.18 : 0.1);
|
|
385
405
|
return Object.freeze({
|
|
386
406
|
bg: Object.freeze({
|
|
387
|
-
app: colors.bg.base,
|
|
407
|
+
app: packRgb(colors.bg.base),
|
|
388
408
|
panel: Object.freeze({
|
|
389
409
|
base: panelBase,
|
|
390
410
|
inset: panelInset,
|
|
391
411
|
elevated: panelElevated,
|
|
392
412
|
}),
|
|
393
|
-
modal: colors.bg.overlay,
|
|
413
|
+
modal: packRgb(colors.bg.overlay),
|
|
394
414
|
}),
|
|
395
415
|
border: Object.freeze({
|
|
396
|
-
default: colors.border.default,
|
|
397
|
-
muted: colors.border.subtle,
|
|
398
|
-
focus: mode === "alert" ? colors.error : colors.focus.ring,
|
|
399
|
-
danger: colors.error,
|
|
416
|
+
default: packRgb(colors.border.default),
|
|
417
|
+
muted: packRgb(colors.border.subtle),
|
|
418
|
+
focus: mode === "alert" ? packRgb(colors.error) : packRgb(colors.focus.ring),
|
|
419
|
+
danger: packRgb(colors.error),
|
|
400
420
|
}),
|
|
401
421
|
text: Object.freeze({
|
|
402
|
-
primary: colors.fg.primary,
|
|
403
|
-
muted: colors.fg.secondary,
|
|
404
|
-
dim: colors.fg.muted,
|
|
422
|
+
primary: packRgb(colors.fg.primary),
|
|
423
|
+
muted: packRgb(colors.fg.secondary),
|
|
424
|
+
dim: packRgb(colors.fg.muted),
|
|
405
425
|
}),
|
|
406
426
|
accent: Object.freeze({
|
|
407
|
-
info: colors.info,
|
|
408
|
-
success: colors.success,
|
|
409
|
-
warn: colors.warning,
|
|
410
|
-
danger: colors.error,
|
|
411
|
-
brand: colors.accent.primary,
|
|
427
|
+
info: packRgb(colors.info),
|
|
428
|
+
success: packRgb(colors.success),
|
|
429
|
+
warn: packRgb(colors.warning),
|
|
430
|
+
danger: packRgb(colors.error),
|
|
431
|
+
brand: packRgb(colors.accent.primary),
|
|
412
432
|
}),
|
|
413
433
|
state: Object.freeze({
|
|
414
434
|
selectedBg,
|
|
415
|
-
selectedText: colors.selected.fg,
|
|
435
|
+
selectedText: packRgb(colors.selected.fg),
|
|
416
436
|
hoverBg,
|
|
417
|
-
focusRing: mode === "alert" ? colors.error : colors.focus.ring,
|
|
437
|
+
focusRing: mode === "alert" ? packRgb(colors.error) : packRgb(colors.focus.ring),
|
|
418
438
|
}),
|
|
419
439
|
progress: Object.freeze({
|
|
420
440
|
track: blend(
|