@valyrianjs/terminal 0.2.1 → 0.2.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.
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +12 -14
- package/dist/ansi.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +4 -0
- package/dist/events.js.map +1 -1
- package/dist/frame-style.d.ts +7 -0
- package/dist/frame-style.d.ts.map +1 -0
- package/dist/frame-style.js +27 -0
- package/dist/frame-style.js.map +1 -0
- package/dist/layout.d.ts +5 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +53 -23
- package/dist/layout.js.map +1 -1
- package/dist/mouse.d.ts.map +1 -1
- package/dist/mouse.js +8 -1
- package/dist/mouse.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +87 -48
- package/dist/render.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +2 -0
- package/dist/session.js.map +1 -1
- package/dist/text.d.ts +7 -0
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +114 -0
- package/dist/text.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/api-reference.md +6 -3
- package/docs/cookbook.md +1 -1
- package/docs/interaction-model.md +5 -5
- package/docs/primitive-gallery.md +4 -4
- package/examples/basic.tsx +22 -0
- package/examples/cli.tsx +55 -0
- package/examples/demo.tsx +98 -0
- package/examples/docs/background-fill.tsx +107 -0
- package/examples/docs/component-composition.tsx +140 -0
- package/examples/docs/cursor.tsx +121 -0
- package/examples/docs/employees-list.tsx +138 -0
- package/examples/docs/hello.tsx +98 -0
- package/examples/docs/interactive-note.tsx +111 -0
- package/examples/docs/module-api-dashboard.tsx +307 -0
- package/examples/docs/module-flux-store.tsx +181 -0
- package/examples/docs/module-form-workflow.tsx +339 -0
- package/examples/docs/module-forms.tsx +218 -0
- package/examples/docs/module-money.tsx +175 -0
- package/examples/docs/module-native-store.tsx +188 -0
- package/examples/docs/module-pulses.tsx +142 -0
- package/examples/docs/module-query.tsx +209 -0
- package/examples/docs/module-request.tsx +194 -0
- package/examples/docs/module-state-workbench.tsx +283 -0
- package/examples/docs/module-tasks.tsx +223 -0
- package/examples/docs/module-translate.tsx +194 -0
- package/examples/docs/module-utils.tsx +168 -0
- package/examples/docs/module-valyrian-core.tsx +159 -0
- package/examples/docs/pizza-builder.tsx +463 -0
- package/examples/docs/primitive-activity-console.tsx +113 -0
- package/examples/docs/primitive-command-panel.tsx +186 -0
- package/examples/docs/primitive-data-explorer.tsx +155 -0
- package/examples/docs/primitive-input-workbench.tsx +128 -0
- package/examples/docs/primitive-layout-shell.tsx +115 -0
- package/examples/docs/responsive-split.tsx +186 -0
- package/examples/docs/style-system.tsx +209 -0
- package/examples/docs/theme-colors.tsx +225 -0
- package/examples/docs/virtualized-list-workbench.tsx +232 -0
- package/examples/opencode-dogfood-app.tsx +215 -0
- package/examples/opencode-dogfood-lifecycle.tsx +194 -0
- package/examples/opencode-dogfood.tsx +11 -0
- package/llms-full.txt +16 -13
- package/package.json +3 -2
- package/src/ansi.ts +12 -14
- package/src/events.ts +2 -0
- package/src/frame-style.ts +36 -0
- package/src/layout.ts +57 -24
- package/src/mouse.ts +10 -1
- package/src/render.ts +92 -48
- package/src/session.ts +2 -0
- package/src/text.ts +148 -0
- package/src/types.ts +3 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Fixed,
|
|
3
|
+
Pane,
|
|
4
|
+
Screen,
|
|
5
|
+
Split,
|
|
6
|
+
Text,
|
|
7
|
+
mountTerminal
|
|
8
|
+
} from "@valyrianjs/terminal";
|
|
9
|
+
import type {
|
|
10
|
+
TerminalMountOptions,
|
|
11
|
+
TerminalSession
|
|
12
|
+
} from "@valyrianjs/terminal";
|
|
13
|
+
|
|
14
|
+
interface ResponsiveSplitState {
|
|
15
|
+
running: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ResponsiveSplitDemo {
|
|
19
|
+
session: TerminalSession;
|
|
20
|
+
dispatchKey(key: string): string;
|
|
21
|
+
output(): string;
|
|
22
|
+
ansiOutput(): string;
|
|
23
|
+
isRunning(): boolean;
|
|
24
|
+
destroy(): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function shouldRunSnapshot() {
|
|
28
|
+
return (
|
|
29
|
+
process.argv.includes("--snapshot") ||
|
|
30
|
+
process.env.VALYRIAN_TERMINAL_EXAMPLE_SNAPSHOT === "1" ||
|
|
31
|
+
!process.stdin.isTTY
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function App({
|
|
36
|
+
getSize
|
|
37
|
+
}: {
|
|
38
|
+
getSize?: () => { cols: number; rows: number } | undefined;
|
|
39
|
+
}) {
|
|
40
|
+
const terminalWidth = getSize?.()?.cols;
|
|
41
|
+
const layout = "3 columns";
|
|
42
|
+
const sizes = "25% / 1fr / 2fr";
|
|
43
|
+
const widthLabel = terminalWidth ? String(terminalWidth) : "auto";
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Screen title="Workspace">
|
|
47
|
+
<Fixed position="top" size={3}>
|
|
48
|
+
<Text>{`Terminal width: ${widthLabel}`}</Text>
|
|
49
|
+
<Text>{`Layout: ${layout}`}</Text>
|
|
50
|
+
<Text>{`sizes: ${sizes}`}</Text>
|
|
51
|
+
</Fixed>
|
|
52
|
+
<Split
|
|
53
|
+
gap={1}
|
|
54
|
+
sizes={["25%", "1fr", "2fr"]}
|
|
55
|
+
breakpoints={[
|
|
56
|
+
{
|
|
57
|
+
maxCols: 72,
|
|
58
|
+
direction: "column",
|
|
59
|
+
sizes: ["1fr", "1fr", "1fr"],
|
|
60
|
+
gap: 0
|
|
61
|
+
}
|
|
62
|
+
]}
|
|
63
|
+
>
|
|
64
|
+
<Pane
|
|
65
|
+
style={{
|
|
66
|
+
background: "#111827",
|
|
67
|
+
border: {
|
|
68
|
+
top: true,
|
|
69
|
+
right: true,
|
|
70
|
+
bottom: true,
|
|
71
|
+
left: true,
|
|
72
|
+
style: "solid",
|
|
73
|
+
color: "#2563eb"
|
|
74
|
+
}
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
<Text>Inbox</Text>
|
|
78
|
+
<Text>3 unread</Text>
|
|
79
|
+
</Pane>
|
|
80
|
+
<Pane
|
|
81
|
+
style={{
|
|
82
|
+
background: "#172554",
|
|
83
|
+
border: {
|
|
84
|
+
top: true,
|
|
85
|
+
right: true,
|
|
86
|
+
bottom: true,
|
|
87
|
+
left: true,
|
|
88
|
+
style: "solid",
|
|
89
|
+
color: "#60a5fa"
|
|
90
|
+
}
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<Text>Preview</Text>
|
|
94
|
+
<Text>Welcome thread</Text>
|
|
95
|
+
</Pane>
|
|
96
|
+
<Pane
|
|
97
|
+
style={{
|
|
98
|
+
background: "#1f2937",
|
|
99
|
+
border: {
|
|
100
|
+
top: true,
|
|
101
|
+
right: true,
|
|
102
|
+
bottom: true,
|
|
103
|
+
left: true,
|
|
104
|
+
style: "solid",
|
|
105
|
+
color: "#a78bfa"
|
|
106
|
+
}
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
<Text>Details</Text>
|
|
110
|
+
<Text>Owner: team</Text>
|
|
111
|
+
</Pane>
|
|
112
|
+
</Split>
|
|
113
|
+
<Fixed position="bottom" size={1}>
|
|
114
|
+
<Text>Resize the terminal to change layout | Ctrl+C: quit</Text>
|
|
115
|
+
</Fixed>
|
|
116
|
+
</Screen>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function createResponsiveSplitDemo(
|
|
121
|
+
options: TerminalMountOptions = {}
|
|
122
|
+
): ResponsiveSplitDemo {
|
|
123
|
+
let session: TerminalSession;
|
|
124
|
+
const state = {
|
|
125
|
+
running: true
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
function quit() {
|
|
129
|
+
state.running = false;
|
|
130
|
+
session.destroy();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
session = mountTerminal(<App getSize={() => session?.size()} />, {
|
|
134
|
+
...options,
|
|
135
|
+
keymap: {
|
|
136
|
+
...options.keymap,
|
|
137
|
+
bindings: [
|
|
138
|
+
...(options.keymap?.bindings || []),
|
|
139
|
+
{ key: "CTRL_C", command: { id: "quit" }, scope: "global" }
|
|
140
|
+
],
|
|
141
|
+
onCommand(command, context) {
|
|
142
|
+
if (command.id === "quit") {
|
|
143
|
+
quit();
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return options.keymap?.onCommand?.(command, context);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
session.update();
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
session,
|
|
154
|
+
dispatchKey(key: string) {
|
|
155
|
+
return session.dispatchKey(key);
|
|
156
|
+
},
|
|
157
|
+
output() {
|
|
158
|
+
return session.output();
|
|
159
|
+
},
|
|
160
|
+
ansiOutput() {
|
|
161
|
+
return session.ansiOutput();
|
|
162
|
+
},
|
|
163
|
+
isRunning() {
|
|
164
|
+
return state.running;
|
|
165
|
+
},
|
|
166
|
+
destroy() {
|
|
167
|
+
state.running = false;
|
|
168
|
+
session.destroy();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (import.meta.main) {
|
|
174
|
+
if (shouldRunSnapshot()) {
|
|
175
|
+
const demo = createResponsiveSplitDemo({
|
|
176
|
+
runtime: "headless",
|
|
177
|
+
cols: 40,
|
|
178
|
+
rows: 16
|
|
179
|
+
});
|
|
180
|
+
process.stdout.write(demo.output());
|
|
181
|
+
process.stdout.write("\n");
|
|
182
|
+
demo.destroy();
|
|
183
|
+
} else {
|
|
184
|
+
createResponsiveSplitDemo();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { Button, Input, Pane, Screen, Text, mountTerminal } from "@valyrianjs/terminal";
|
|
2
|
+
import type { TerminalMountOptions, TerminalSession, TerminalVisualState } from "@valyrianjs/terminal";
|
|
3
|
+
|
|
4
|
+
type BuildStatus = "Ready" | "Building" | "Built";
|
|
5
|
+
|
|
6
|
+
interface StyleSystemDemoState {
|
|
7
|
+
status: BuildStatus;
|
|
8
|
+
warnings: boolean;
|
|
9
|
+
note: string;
|
|
10
|
+
running: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface StyleSystemDemo {
|
|
14
|
+
session: TerminalSession;
|
|
15
|
+
dispatchKey(key: string): string;
|
|
16
|
+
output(): string;
|
|
17
|
+
ansiOutput(): string;
|
|
18
|
+
isRunning(): boolean;
|
|
19
|
+
destroy(): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const theme = {
|
|
23
|
+
styles: {
|
|
24
|
+
title: {
|
|
25
|
+
app: { color: "#88c0d0" }
|
|
26
|
+
},
|
|
27
|
+
button: {
|
|
28
|
+
primary: {
|
|
29
|
+
color: "#ffffff",
|
|
30
|
+
background: "#5e81ac",
|
|
31
|
+
padding: { left: 1, right: 1 },
|
|
32
|
+
focus: { color: "#eceff4", background: "#81a1c1" }
|
|
33
|
+
},
|
|
34
|
+
loading: { color: "#88c0d0" },
|
|
35
|
+
success: { color: "#a3be8c" }
|
|
36
|
+
},
|
|
37
|
+
panel: {
|
|
38
|
+
queue: {
|
|
39
|
+
border: { top: true, right: true, bottom: true, left: true, style: "solid", color: "#4b5563" },
|
|
40
|
+
padding: { left: 1, right: 1 },
|
|
41
|
+
dropTarget: { border: { top: true, right: true, bottom: true, left: true, style: "solid", color: "#8fbcbb" } }
|
|
42
|
+
},
|
|
43
|
+
cta: {
|
|
44
|
+
color: "#eceff4",
|
|
45
|
+
background: "#1f2328",
|
|
46
|
+
padding: { left: 2, right: 2 }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
input: {
|
|
50
|
+
readonly: { color: "#777777" },
|
|
51
|
+
placeholder: { color: "#777777" }
|
|
52
|
+
},
|
|
53
|
+
state: {
|
|
54
|
+
success: { color: "#a3be8c" },
|
|
55
|
+
warning: { color: "#ebcb8b" },
|
|
56
|
+
loading: { color: "#88c0d0" },
|
|
57
|
+
readonly: { color: "#777777" },
|
|
58
|
+
expanded: { color: "#b48ead" },
|
|
59
|
+
dropTarget: { color: "#8fbcbb" },
|
|
60
|
+
muted: { color: "#6b7280" }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function createInitialState(): StyleSystemDemoState {
|
|
66
|
+
return {
|
|
67
|
+
status: "Ready",
|
|
68
|
+
warnings: true,
|
|
69
|
+
note: "local-preview-2026-05-29",
|
|
70
|
+
running: true
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function nextBuildStatus(status: BuildStatus): BuildStatus {
|
|
75
|
+
if (status === "Ready") {
|
|
76
|
+
return "Building";
|
|
77
|
+
}
|
|
78
|
+
if (status === "Building") {
|
|
79
|
+
return "Built";
|
|
80
|
+
}
|
|
81
|
+
return "Building";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function App({ state }: { state: StyleSystemDemoState }) {
|
|
85
|
+
const warningText = state.warnings ? "2 warnings" : "No warnings";
|
|
86
|
+
const statusState: TerminalVisualState = state.status === "Built"
|
|
87
|
+
? "success"
|
|
88
|
+
: state.status === "Building"
|
|
89
|
+
? "loading"
|
|
90
|
+
: state.warnings
|
|
91
|
+
? "warning"
|
|
92
|
+
: "success";
|
|
93
|
+
const buildButtonState: TerminalVisualState | undefined = state.status === "Built" ? "success" : state.status === "Building" ? "loading" : undefined;
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<Screen title="Valyrian Preview Console">
|
|
97
|
+
<Text style="title.app">Valyrian Preview Console</Text>
|
|
98
|
+
<Text>Project valyrianjs-terminal</Text>
|
|
99
|
+
<Text>Target local preview</Text>
|
|
100
|
+
<Text state={statusState} styles={{ warning: "state.warning", success: "state.success", loading: "state.loading" }}>
|
|
101
|
+
{`Status ${state.status} · ${warningText}`}
|
|
102
|
+
</Text>
|
|
103
|
+
|
|
104
|
+
<Pane style="panel.queue" state="dropTarget" styles={{ dropTarget: "panel.queue.dropTarget" }}>
|
|
105
|
+
<Text>Build queue</Text>
|
|
106
|
+
<Text state="loading" styles={{ loading: "state.loading" }}>> bundle-assets loading</Text>
|
|
107
|
+
<Text state="readonly" styles={{ readonly: "state.readonly" }}> config-check readonly</Text>
|
|
108
|
+
<Text state="expanded" styles={{ expanded: "state.expanded" }}> preview-server expanded</Text>
|
|
109
|
+
<Text state="dropTarget" styles={{ dropTarget: "state.dropTarget" }}> cache-warmup drop target</Text>
|
|
110
|
+
</Pane>
|
|
111
|
+
|
|
112
|
+
<Text>Preview note</Text>
|
|
113
|
+
<Input
|
|
114
|
+
id="note"
|
|
115
|
+
value={state.note}
|
|
116
|
+
state="readonly"
|
|
117
|
+
styles={{ readonly: "input.readonly", placeholder: "input.placeholder" }}
|
|
118
|
+
/>
|
|
119
|
+
|
|
120
|
+
<Pane width={24} style="panel.cta">
|
|
121
|
+
<Button
|
|
122
|
+
id="build"
|
|
123
|
+
style="button.primary"
|
|
124
|
+
state={buildButtonState}
|
|
125
|
+
styles={{ focus: "button.primary.focus", loading: "button.loading", success: "button.success" }}
|
|
126
|
+
onpress={() => {
|
|
127
|
+
state.status = nextBuildStatus(state.status);
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
BUILD PREVIEW
|
|
131
|
+
</Button>
|
|
132
|
+
</Pane>
|
|
133
|
+
|
|
134
|
+
<Text state="muted" styles={{ muted: "state.muted" }}>Tab: focus Enter: build W: warnings Ctrl+C: quit</Text>
|
|
135
|
+
</Screen>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function createStyleSystemDemo(options: TerminalMountOptions = {}): StyleSystemDemo {
|
|
140
|
+
const state = createInitialState();
|
|
141
|
+
let session: TerminalSession;
|
|
142
|
+
|
|
143
|
+
function quit() {
|
|
144
|
+
state.running = false;
|
|
145
|
+
session.destroy();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
session = mountTerminal(<App state={state} />, {
|
|
149
|
+
theme,
|
|
150
|
+
...options,
|
|
151
|
+
keymap: {
|
|
152
|
+
...options.keymap,
|
|
153
|
+
bindings: [
|
|
154
|
+
...(options.keymap?.bindings || []),
|
|
155
|
+
{ key: "w", command: { id: "warnings.toggle" }, scope: "global" },
|
|
156
|
+
{ key: "W", command: { id: "warnings.toggle" }, scope: "global" },
|
|
157
|
+
{ key: "CTRL_C", command: { id: "quit" }, scope: "global" }
|
|
158
|
+
],
|
|
159
|
+
onCommand(command, context) {
|
|
160
|
+
if (command.id === "warnings.toggle") {
|
|
161
|
+
state.warnings = !state.warnings;
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
if (command.id === "quit") {
|
|
165
|
+
quit();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return options.keymap?.onCommand?.(command, context);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
session.focus("build");
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
session,
|
|
177
|
+
dispatchKey(key: string) {
|
|
178
|
+
return session.dispatchKey(key);
|
|
179
|
+
},
|
|
180
|
+
output() {
|
|
181
|
+
return session.output();
|
|
182
|
+
},
|
|
183
|
+
ansiOutput() {
|
|
184
|
+
return session.ansiOutput();
|
|
185
|
+
},
|
|
186
|
+
isRunning() {
|
|
187
|
+
return state.running;
|
|
188
|
+
},
|
|
189
|
+
destroy() {
|
|
190
|
+
state.running = false;
|
|
191
|
+
session.destroy();
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function shouldRunSnapshot() {
|
|
197
|
+
return process.argv.includes("--snapshot") || process.env.VALYRIAN_TERMINAL_EXAMPLE_SNAPSHOT === "1" || !process.stdin.isTTY;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (import.meta.main) {
|
|
201
|
+
if (shouldRunSnapshot()) {
|
|
202
|
+
const demo = createStyleSystemDemo({ runtime: "headless", cols: 56, rows: 18 });
|
|
203
|
+
process.stdout.write(demo.output());
|
|
204
|
+
process.stdout.write("\n");
|
|
205
|
+
demo.destroy();
|
|
206
|
+
} else {
|
|
207
|
+
createStyleSystemDemo();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { Screen, Text, mountTerminal } from "@valyrianjs/terminal";
|
|
2
|
+
import type {
|
|
3
|
+
TerminalMountOptions,
|
|
4
|
+
TerminalSession
|
|
5
|
+
} from "@valyrianjs/terminal";
|
|
6
|
+
|
|
7
|
+
interface ThemeColorsState {
|
|
8
|
+
palette: "day" | "night";
|
|
9
|
+
changedTo: "day" | "night" | "";
|
|
10
|
+
running: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ThemeColorsDemo {
|
|
14
|
+
session: TerminalSession;
|
|
15
|
+
dispatchKey(key: string): string;
|
|
16
|
+
output(): string;
|
|
17
|
+
ansiOutput(): string;
|
|
18
|
+
isRunning(): boolean;
|
|
19
|
+
destroy(): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const paletteNames = {
|
|
23
|
+
day: "Day",
|
|
24
|
+
night: "Night"
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
const swatchStyles = {
|
|
28
|
+
day: {
|
|
29
|
+
primary: "swatch.day.primary",
|
|
30
|
+
success: "swatch.day.success",
|
|
31
|
+
warning: "swatch.day.warning",
|
|
32
|
+
danger: "swatch.day.danger",
|
|
33
|
+
muted: "swatch.day.muted"
|
|
34
|
+
},
|
|
35
|
+
night: {
|
|
36
|
+
primary: "swatch.night.primary",
|
|
37
|
+
success: "swatch.night.success",
|
|
38
|
+
warning: "swatch.night.warning",
|
|
39
|
+
danger: "swatch.night.danger",
|
|
40
|
+
muted: "swatch.night.muted"
|
|
41
|
+
}
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
const theme = {
|
|
45
|
+
styles: {
|
|
46
|
+
swatch: {
|
|
47
|
+
day: {
|
|
48
|
+
primary: {
|
|
49
|
+
color: "#ffffff",
|
|
50
|
+
background: "#2563eb",
|
|
51
|
+
padding: { left: 1, right: 1 }
|
|
52
|
+
},
|
|
53
|
+
success: {
|
|
54
|
+
color: "#052e16",
|
|
55
|
+
background: "#86efac",
|
|
56
|
+
padding: { left: 1, right: 1 }
|
|
57
|
+
},
|
|
58
|
+
warning: {
|
|
59
|
+
color: "#451a03",
|
|
60
|
+
background: "#fbbf24",
|
|
61
|
+
padding: { left: 1, right: 1 }
|
|
62
|
+
},
|
|
63
|
+
danger: {
|
|
64
|
+
color: "#ffffff",
|
|
65
|
+
background: "#dc2626",
|
|
66
|
+
padding: { left: 1, right: 1 }
|
|
67
|
+
},
|
|
68
|
+
muted: {
|
|
69
|
+
color: "#111827",
|
|
70
|
+
background: "#d1d5db",
|
|
71
|
+
padding: { left: 1, right: 1 }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
night: {
|
|
75
|
+
primary: {
|
|
76
|
+
color: "#dbeafe",
|
|
77
|
+
background: "#1d4ed8",
|
|
78
|
+
padding: { left: 1, right: 1 }
|
|
79
|
+
},
|
|
80
|
+
success: {
|
|
81
|
+
color: "#dcfce7",
|
|
82
|
+
background: "#15803d",
|
|
83
|
+
padding: { left: 1, right: 1 }
|
|
84
|
+
},
|
|
85
|
+
warning: {
|
|
86
|
+
color: "#fef3c7",
|
|
87
|
+
background: "#b45309",
|
|
88
|
+
padding: { left: 1, right: 1 }
|
|
89
|
+
},
|
|
90
|
+
danger: {
|
|
91
|
+
color: "#fee2e2",
|
|
92
|
+
background: "#b91c1c",
|
|
93
|
+
padding: { left: 1, right: 1 }
|
|
94
|
+
},
|
|
95
|
+
muted: {
|
|
96
|
+
color: "#e5e7eb",
|
|
97
|
+
background: "#374151",
|
|
98
|
+
padding: { left: 1, right: 1 }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
function createInitialState(): ThemeColorsState {
|
|
106
|
+
return { palette: "day", changedTo: "", running: true };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function shouldRunSnapshot() {
|
|
110
|
+
return (
|
|
111
|
+
process.argv.includes("--snapshot") ||
|
|
112
|
+
process.env.VALYRIAN_TERMINAL_EXAMPLE_SNAPSHOT === "1" ||
|
|
113
|
+
!process.stdin.isTTY
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function App({ state }: { state: ThemeColorsState }) {
|
|
118
|
+
const paletteLabel = paletteNames[state.palette];
|
|
119
|
+
const swatches = swatchStyles[state.palette];
|
|
120
|
+
const feedback = state.changedTo
|
|
121
|
+
? `Changed to ${paletteNames[state.changedTo]} palette`
|
|
122
|
+
: "Tab/1/2 changes the palette";
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<Screen title="Theme tokens">
|
|
126
|
+
<Text>{`Palette: ${paletteLabel}`}</Text>
|
|
127
|
+
<Text>{feedback}</Text>
|
|
128
|
+
<Text style={swatches.primary}>Primary App action</Text>
|
|
129
|
+
<Text style={swatches.success}>Success Completed</Text>
|
|
130
|
+
<Text style={swatches.warning}>Warning Needs review</Text>
|
|
131
|
+
<Text style={swatches.danger}>Danger Destructive</Text>
|
|
132
|
+
<Text style={swatches.muted}>Muted Secondary text</Text>
|
|
133
|
+
<Text styles={{ muted: swatches.muted }} state="muted">
|
|
134
|
+
Tab/1/2 palette | Ctrl+C: quit
|
|
135
|
+
</Text>
|
|
136
|
+
</Screen>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function createThemeColorsDemo(
|
|
141
|
+
options: TerminalMountOptions = {}
|
|
142
|
+
): ThemeColorsDemo {
|
|
143
|
+
const state = createInitialState();
|
|
144
|
+
let session: TerminalSession;
|
|
145
|
+
|
|
146
|
+
function quit() {
|
|
147
|
+
state.running = false;
|
|
148
|
+
session.destroy();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function setPalette(palette: ThemeColorsState["palette"]) {
|
|
152
|
+
state.palette = palette;
|
|
153
|
+
state.changedTo = palette;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function switchPalette() {
|
|
157
|
+
setPalette(state.palette === "day" ? "night" : "day");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
session = mountTerminal(<App state={state} />, {
|
|
161
|
+
theme,
|
|
162
|
+
...options,
|
|
163
|
+
keymap: {
|
|
164
|
+
...options.keymap,
|
|
165
|
+
bindings: [
|
|
166
|
+
...(options.keymap?.bindings || []),
|
|
167
|
+
{ key: "Tab", command: { id: "palette.switch" }, scope: "global" },
|
|
168
|
+
{ key: "TAB", command: { id: "palette.switch" }, scope: "global" },
|
|
169
|
+
{ key: "1", command: { id: "palette.day" }, scope: "global" },
|
|
170
|
+
{ key: "2", command: { id: "palette.night" }, scope: "global" },
|
|
171
|
+
{ key: "CTRL_C", command: { id: "quit" }, scope: "global" }
|
|
172
|
+
],
|
|
173
|
+
onCommand(command, context) {
|
|
174
|
+
if (command.id === "palette.switch") {
|
|
175
|
+
switchPalette();
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
if (command.id === "palette.day") {
|
|
179
|
+
setPalette("day");
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
if (command.id === "palette.night") {
|
|
183
|
+
setPalette("night");
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
if (command.id === "quit") {
|
|
187
|
+
quit();
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return options.keymap?.onCommand?.(command, context);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
session,
|
|
197
|
+
dispatchKey(key: string) {
|
|
198
|
+
return session.dispatchKey(key);
|
|
199
|
+
},
|
|
200
|
+
output() {
|
|
201
|
+
return session.output();
|
|
202
|
+
},
|
|
203
|
+
ansiOutput() {
|
|
204
|
+
return session.ansiOutput();
|
|
205
|
+
},
|
|
206
|
+
isRunning() {
|
|
207
|
+
return state.running;
|
|
208
|
+
},
|
|
209
|
+
destroy() {
|
|
210
|
+
state.running = false;
|
|
211
|
+
session.destroy();
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (import.meta.main) {
|
|
217
|
+
if (shouldRunSnapshot()) {
|
|
218
|
+
const demo = createThemeColorsDemo({ runtime: "headless", cols: 64, rows: 10 });
|
|
219
|
+
process.stdout.write(demo.output());
|
|
220
|
+
process.stdout.write("\n");
|
|
221
|
+
demo.destroy();
|
|
222
|
+
} else {
|
|
223
|
+
createThemeColorsDemo();
|
|
224
|
+
}
|
|
225
|
+
}
|