@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,168 @@
|
|
|
1
|
+
import { get, pick, set } from "valyrian.js/utils";
|
|
2
|
+
import { Pane, Screen, Text, mountTerminal } from "@valyrianjs/terminal";
|
|
3
|
+
import type { TerminalMountOptions, TerminalSession } from "@valyrianjs/terminal";
|
|
4
|
+
|
|
5
|
+
interface ModuleDemo {
|
|
6
|
+
session: TerminalSession;
|
|
7
|
+
dispatchKey(key: string): string;
|
|
8
|
+
output(): string;
|
|
9
|
+
ansiOutput(): string;
|
|
10
|
+
isRunning(): boolean;
|
|
11
|
+
destroy(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type AccountPayload = {
|
|
15
|
+
account: { name: string; plan: string; region?: string; owner: { name: string; email: string } };
|
|
16
|
+
usage: { seats: number; projects: number; lastLogin: string };
|
|
17
|
+
billing: { status: string; renewal: string };
|
|
18
|
+
};
|
|
19
|
+
type InspectorState = { selected: number; filter: "all" | "account"; record: AccountPayload };
|
|
20
|
+
type FieldRow = { group: string; label: string; value: string };
|
|
21
|
+
type AccountSummaryKey = "name" | "plan" | "region";
|
|
22
|
+
|
|
23
|
+
const PANEL_STYLE = { color: "#f8fafc", background: "#111827", padding: { left: 1, right: 1 } };
|
|
24
|
+
const FOOTER_STYLE = { color: "#dbeafe", background: "#1e293b" };
|
|
25
|
+
|
|
26
|
+
function shouldRunSnapshot() {
|
|
27
|
+
return process.argv.includes("--snapshot") || process.env.VALYRIAN_TERMINAL_EXAMPLE_SNAPSHOT === "1" || !process.stdin.isTTY;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createRecord(): AccountPayload {
|
|
31
|
+
const record: AccountPayload = {
|
|
32
|
+
account: { name: "Northwind", plan: "Team", owner: { name: "Ada Lovelace", email: "ada@example.test" } },
|
|
33
|
+
usage: { seats: 12, projects: 4, lastLogin: "2026-05-29" },
|
|
34
|
+
billing: { status: "current", renewal: "2026-06-15" }
|
|
35
|
+
};
|
|
36
|
+
set(record, "account.region", "MX");
|
|
37
|
+
return record;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildRows(record: AccountPayload, filter: InspectorState["filter"]): FieldRow[] {
|
|
41
|
+
const safeAccount = pick<AccountPayload["account"], AccountSummaryKey>(record.account, ["name", "plan", "region"]);
|
|
42
|
+
const rows = [
|
|
43
|
+
{ group: "account", label: "Account", value: String(get(safeAccount, "name", "unknown")) },
|
|
44
|
+
{ group: "account", label: "Plan", value: String(get(safeAccount, "plan", "none")) },
|
|
45
|
+
{ group: "account", label: "Region", value: String(get(safeAccount, "region", "unknown")) },
|
|
46
|
+
{ group: "owner", label: "Owner", value: String(get(record, "account.owner.name", "unknown")) },
|
|
47
|
+
{ group: "usage", label: "Seats", value: String(get(record, "usage.seats", 0)) },
|
|
48
|
+
{ group: "usage", label: "Projects", value: String(get(record, "usage.projects", 0)) },
|
|
49
|
+
{ group: "billing", label: "Billing", value: String(get(record, "billing.status", "unknown")) }
|
|
50
|
+
];
|
|
51
|
+
return filter === "account" ? rows.filter((row) => row.group === "account") : rows;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function App({ state }: { state: InspectorState }) {
|
|
55
|
+
const rows = buildRows(state.record, state.filter);
|
|
56
|
+
const selected = rows[state.selected] || rows[0];
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Screen title="Account Data Inspector">
|
|
60
|
+
<Text style={{ color: "#ffffff", background: "#020617" }}>Account Data Inspector</Text>
|
|
61
|
+
<Pane style={PANEL_STYLE}>
|
|
62
|
+
<Text>Account Data Inspector</Text>
|
|
63
|
+
<Text>{`Filter: ${state.filter === "account" ? "account fields" : "all groups"}`}</Text>
|
|
64
|
+
{rows.map((row, index) => (
|
|
65
|
+
<Text>{`${index === state.selected ? ">" : " "} [${row.group}] ${row.label}: ${row.value}`}</Text>
|
|
66
|
+
))}
|
|
67
|
+
<Text>{`Selected field: ${selected.label}`}</Text>
|
|
68
|
+
</Pane>
|
|
69
|
+
<Text style={FOOTER_STYLE}>J/K select field F filter group R reset Ctrl+C: quit</Text>
|
|
70
|
+
</Screen>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createModuleUtilsDemo(options: TerminalMountOptions = {}): ModuleDemo {
|
|
75
|
+
const state: InspectorState = { selected: 0, filter: "all", record: createRecord() };
|
|
76
|
+
let running = true;
|
|
77
|
+
let session: TerminalSession;
|
|
78
|
+
|
|
79
|
+
function quit() {
|
|
80
|
+
running = false;
|
|
81
|
+
session.destroy();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function clampSelection() {
|
|
85
|
+
const count = buildRows(state.record, state.filter).length;
|
|
86
|
+
state.selected = Math.min(state.selected, Math.max(0, count - 1));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
session = mountTerminal(<App state={state} />, {
|
|
90
|
+
...options,
|
|
91
|
+
cols: options.cols ?? 92,
|
|
92
|
+
rows: options.rows ?? 20,
|
|
93
|
+
keymap: {
|
|
94
|
+
...options.keymap,
|
|
95
|
+
bindings: [
|
|
96
|
+
...(options.keymap?.bindings || []),
|
|
97
|
+
{ key: "j", command: { id: "inspector.next" }, scope: "global" },
|
|
98
|
+
{ key: "J", command: { id: "inspector.next" }, scope: "global" },
|
|
99
|
+
{ key: "k", command: { id: "inspector.previous" }, scope: "global" },
|
|
100
|
+
{ key: "K", command: { id: "inspector.previous" }, scope: "global" },
|
|
101
|
+
{ key: "f", command: { id: "inspector.filter" }, scope: "global" },
|
|
102
|
+
{ key: "F", command: { id: "inspector.filter" }, scope: "global" },
|
|
103
|
+
{ key: "r", command: { id: "inspector.reset" }, scope: "global" },
|
|
104
|
+
{ key: "R", command: { id: "inspector.reset" }, scope: "global" },
|
|
105
|
+
{ key: "CTRL_C", command: { id: "quit" }, scope: "global" }
|
|
106
|
+
],
|
|
107
|
+
onCommand(command, context) {
|
|
108
|
+
if (command.id === "inspector.next") {
|
|
109
|
+
const count = buildRows(state.record, state.filter).length;
|
|
110
|
+
state.selected = (state.selected + 1) % count;
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
if (command.id === "inspector.previous") {
|
|
114
|
+
const count = buildRows(state.record, state.filter).length;
|
|
115
|
+
state.selected = (state.selected - 1 + count) % count;
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (command.id === "inspector.filter") {
|
|
119
|
+
state.filter = state.filter === "all" ? "account" : "all";
|
|
120
|
+
clampSelection();
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
if (command.id === "inspector.reset") {
|
|
124
|
+
state.selected = 0;
|
|
125
|
+
state.filter = "all";
|
|
126
|
+
state.record = createRecord();
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
if (command.id === "quit") {
|
|
130
|
+
quit();
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return options.keymap?.onCommand?.(command, context);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
session,
|
|
140
|
+
dispatchKey(key: string) {
|
|
141
|
+
return session.dispatchKey(key);
|
|
142
|
+
},
|
|
143
|
+
output() {
|
|
144
|
+
return session.output();
|
|
145
|
+
},
|
|
146
|
+
ansiOutput() {
|
|
147
|
+
return session.ansiOutput();
|
|
148
|
+
},
|
|
149
|
+
isRunning() {
|
|
150
|
+
return running;
|
|
151
|
+
},
|
|
152
|
+
destroy() {
|
|
153
|
+
running = false;
|
|
154
|
+
session.destroy();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (import.meta.main) {
|
|
160
|
+
if (shouldRunSnapshot()) {
|
|
161
|
+
const demo = createModuleUtilsDemo({ runtime: "headless", cols: 92, rows: 20 });
|
|
162
|
+
process.stdout.write(demo.output());
|
|
163
|
+
process.stdout.write("\n");
|
|
164
|
+
demo.destroy();
|
|
165
|
+
} else {
|
|
166
|
+
createModuleUtilsDemo();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { v } from "valyrian.js";
|
|
2
|
+
import { Pane, Screen, Text, mountTerminal } from "@valyrianjs/terminal";
|
|
3
|
+
import type { TerminalMountOptions, TerminalSession } from "@valyrianjs/terminal";
|
|
4
|
+
|
|
5
|
+
interface ModuleDemo {
|
|
6
|
+
session: TerminalSession;
|
|
7
|
+
dispatchKey(key: string): string;
|
|
8
|
+
output(): string;
|
|
9
|
+
ansiOutput(): string;
|
|
10
|
+
isRunning(): boolean;
|
|
11
|
+
destroy(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type Command = {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
result: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type CommandPaletteState = {
|
|
21
|
+
selected: number;
|
|
22
|
+
result: string;
|
|
23
|
+
history: string[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const COMMANDS: Command[] = [
|
|
27
|
+
{ id: "open-incidents", name: "Open incident board", result: "Incident board opened" },
|
|
28
|
+
{ id: "assign-oncall", name: "Assign on-call engineer", result: "On-call engineer assigned" },
|
|
29
|
+
{ id: "publish-summary", name: "Publish shift summary", result: "Shift summary published" }
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const INITIAL_RESULT = "Choose a command";
|
|
33
|
+
const PANEL_STYLE = { color: "#f8fafc", background: "#111827", padding: { left: 1, right: 1 } };
|
|
34
|
+
const FOOTER_STYLE = { color: "#dbeafe", background: "#1e293b" };
|
|
35
|
+
|
|
36
|
+
function shouldRunSnapshot() {
|
|
37
|
+
return process.argv.includes("--snapshot") || process.env.VALYRIAN_TERMINAL_EXAMPLE_SNAPSHOT === "1" || !process.stdin.isTTY;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function clampSelection(index: number) {
|
|
41
|
+
return (index + COMMANDS.length) % COMMANDS.length;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const manualNode = v(Text, null, "Manual vnode: quick actions are composed with TSX rows");
|
|
45
|
+
|
|
46
|
+
export function App({ state }: { state: CommandPaletteState }) {
|
|
47
|
+
return (
|
|
48
|
+
<Screen title="Terminal Command Palette">
|
|
49
|
+
<Text style={{ color: "#ffffff", background: "#020617" }}>Terminal Command Palette</Text>
|
|
50
|
+
<Pane style={PANEL_STYLE}>
|
|
51
|
+
<Text>Command Palette</Text>
|
|
52
|
+
{manualNode}
|
|
53
|
+
{COMMANDS.map((command, index) => (
|
|
54
|
+
<Text>{`${index === state.selected ? ">" : " "} ${command.name}`}</Text>
|
|
55
|
+
))}
|
|
56
|
+
<Text>{`Result: ${state.result}`}</Text>
|
|
57
|
+
<Text>{`History: ${state.history.length ? state.history.join(" -> ") : "none"}`}</Text>
|
|
58
|
+
</Pane>
|
|
59
|
+
<Text style={FOOTER_STYLE}>J/K move Enter run command R reset Ctrl+C: quit</Text>
|
|
60
|
+
</Screen>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function createModuleValyrianCoreDemo(options: TerminalMountOptions = {}): ModuleDemo {
|
|
65
|
+
const state: CommandPaletteState = { selected: 0, result: INITIAL_RESULT, history: [] };
|
|
66
|
+
let running = true;
|
|
67
|
+
let session: TerminalSession;
|
|
68
|
+
|
|
69
|
+
function quit() {
|
|
70
|
+
running = false;
|
|
71
|
+
session.destroy();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function runSelectedCommand() {
|
|
75
|
+
const command = COMMANDS[state.selected];
|
|
76
|
+
state.result = command.result;
|
|
77
|
+
state.history = [...state.history.slice(-2), command.name];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function reset() {
|
|
81
|
+
state.selected = 0;
|
|
82
|
+
state.result = INITIAL_RESULT;
|
|
83
|
+
state.history = [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
session = mountTerminal(<App state={state} />, {
|
|
87
|
+
...options,
|
|
88
|
+
cols: options.cols ?? 92,
|
|
89
|
+
rows: options.rows ?? 18,
|
|
90
|
+
keymap: {
|
|
91
|
+
...options.keymap,
|
|
92
|
+
bindings: [
|
|
93
|
+
...(options.keymap?.bindings || []),
|
|
94
|
+
{ key: "j", command: { id: "palette.next" }, scope: "global" },
|
|
95
|
+
{ key: "J", command: { id: "palette.next" }, scope: "global" },
|
|
96
|
+
{ key: "k", command: { id: "palette.previous" }, scope: "global" },
|
|
97
|
+
{ key: "K", command: { id: "palette.previous" }, scope: "global" },
|
|
98
|
+
{ key: "ENTER", command: { id: "palette.run" }, scope: "global" },
|
|
99
|
+
{ key: "r", command: { id: "palette.reset" }, scope: "global" },
|
|
100
|
+
{ key: "R", command: { id: "palette.reset" }, scope: "global" },
|
|
101
|
+
{ key: "CTRL_C", command: { id: "quit" }, scope: "global" }
|
|
102
|
+
],
|
|
103
|
+
onCommand(command, context) {
|
|
104
|
+
if (command.id === "palette.next") {
|
|
105
|
+
state.selected = clampSelection(state.selected + 1);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (command.id === "palette.previous") {
|
|
109
|
+
state.selected = clampSelection(state.selected - 1);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
if (command.id === "palette.run") {
|
|
113
|
+
runSelectedCommand();
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (command.id === "palette.reset") {
|
|
117
|
+
reset();
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (command.id === "quit") {
|
|
121
|
+
quit();
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
return options.keymap?.onCommand?.(command, context);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
session,
|
|
131
|
+
dispatchKey(key: string) {
|
|
132
|
+
return session.dispatchKey(key);
|
|
133
|
+
},
|
|
134
|
+
output() {
|
|
135
|
+
return session.output();
|
|
136
|
+
},
|
|
137
|
+
ansiOutput() {
|
|
138
|
+
return session.ansiOutput();
|
|
139
|
+
},
|
|
140
|
+
isRunning() {
|
|
141
|
+
return running;
|
|
142
|
+
},
|
|
143
|
+
destroy() {
|
|
144
|
+
running = false;
|
|
145
|
+
session.destroy();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (import.meta.main) {
|
|
151
|
+
if (shouldRunSnapshot()) {
|
|
152
|
+
const demo = createModuleValyrianCoreDemo({ runtime: "headless", cols: 92, rows: 18 });
|
|
153
|
+
process.stdout.write(demo.output());
|
|
154
|
+
process.stdout.write("\n");
|
|
155
|
+
demo.destroy();
|
|
156
|
+
} else {
|
|
157
|
+
createModuleValyrianCoreDemo();
|
|
158
|
+
}
|
|
159
|
+
}
|