zuejs 0.1.0
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/LICENSE +21 -0
- package/README.md +15 -0
- package/package.json +48 -0
- package/src/compute.ts +255 -0
- package/src/index.ts +3 -0
- package/src/ui.ts +215 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 proplayer919
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zuejs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Terminal math editor for context-based symbolic and numeric computations.",
|
|
5
|
+
"module": "src/index.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"private": false,
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"author": "proplayer919",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"math",
|
|
12
|
+
"terminal",
|
|
13
|
+
"bun",
|
|
14
|
+
"cli",
|
|
15
|
+
"symbolic"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/proplayer919/zue.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/proplayer919/zue/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/proplayer919/zue#readme",
|
|
25
|
+
"files": [
|
|
26
|
+
"src",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"start": "bun src/index.ts",
|
|
35
|
+
"dev": "bun --watch src/index.ts",
|
|
36
|
+
"check": "tsc --noEmit",
|
|
37
|
+
"prepublishOnly": "npm run check"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/blessed": "^0.1.27",
|
|
41
|
+
"@types/bun": "latest",
|
|
42
|
+
"typescript": "^5"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"blessed": "^0.1.81",
|
|
46
|
+
"mathjs": "^15.1.1"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/compute.ts
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { evaluate, parse } from "mathjs";
|
|
2
|
+
|
|
3
|
+
const QUERY_PATTERN = /^(.+)\s*=\s*\?$/;
|
|
4
|
+
const FUNCTION_DEFINITION_PATTERN = /^([A-Za-z_]\w*)\s*\(([^)]*)\)\s*=\s*(.+)$/;
|
|
5
|
+
|
|
6
|
+
type FunctionDefinition = {
|
|
7
|
+
params: string[];
|
|
8
|
+
body: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function formatValue(value: unknown): string {
|
|
12
|
+
if (typeof value === "string") {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof value === "number") {
|
|
17
|
+
return Number.isInteger(value)
|
|
18
|
+
? value.toString()
|
|
19
|
+
: value.toFixed(10).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return JSON.stringify(value);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function splitTopLevelArgs(input: string): string[] {
|
|
26
|
+
const args: string[] = [];
|
|
27
|
+
let current = "";
|
|
28
|
+
let depth = 0;
|
|
29
|
+
|
|
30
|
+
for (const char of input) {
|
|
31
|
+
|
|
32
|
+
if (char === "(") {
|
|
33
|
+
depth += 1;
|
|
34
|
+
} else if (char === ")") {
|
|
35
|
+
depth -= 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (char === "," && depth === 0) {
|
|
39
|
+
args.push(current.trim());
|
|
40
|
+
current = "";
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
current += char;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (current.trim().length > 0) {
|
|
48
|
+
args.push(current.trim());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return args;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getKnownSymbols(term: string, scope: Record<string, unknown>): Array<[string, unknown]> {
|
|
55
|
+
const node = parse(term);
|
|
56
|
+
const symbols = new Set<string>();
|
|
57
|
+
|
|
58
|
+
node.traverse((child) => {
|
|
59
|
+
if (child.type === "SymbolNode" && "name" in child && typeof child.name === "string") {
|
|
60
|
+
symbols.add(child.name);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return [...symbols]
|
|
65
|
+
.filter((name) => Object.hasOwn(scope, name))
|
|
66
|
+
.map((name) => [name, scope[name]])
|
|
67
|
+
.filter((entry): entry is [string, unknown] => entry[1] !== undefined);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildFunctionWorkingSteps(
|
|
71
|
+
term: string,
|
|
72
|
+
scope: Record<string, unknown>,
|
|
73
|
+
functions: Map<string, FunctionDefinition>
|
|
74
|
+
): string[] {
|
|
75
|
+
const directCall = /^([A-Za-z_]\w*)\s*\((.*)\)$/.exec(term);
|
|
76
|
+
if (!directCall) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const fnName = (directCall[1] ?? "").trim();
|
|
81
|
+
const argsRaw = (directCall[2] ?? "").trim();
|
|
82
|
+
const definition = functions.get(fnName);
|
|
83
|
+
if (!definition) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const args = argsRaw.length === 0 ? [] : splitTopLevelArgs(argsRaw);
|
|
88
|
+
if (args.length !== definition.params.length) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let substituted = definition.body;
|
|
93
|
+
const bindings: string[] = [];
|
|
94
|
+
|
|
95
|
+
for (let index = 0; index < definition.params.length; index += 1) {
|
|
96
|
+
const param = definition.params[index] ?? "";
|
|
97
|
+
const arg = args[index] ?? "";
|
|
98
|
+
bindings.push(`${param} = ${arg}`);
|
|
99
|
+
substituted = substituted.replaceAll(new RegExp(String.raw`\b${param}\b`, "g"), `(${arg})`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const steps: string[] = [];
|
|
103
|
+
steps.push(
|
|
104
|
+
` 2) Function rule: ${fnName}(${definition.params.join(", ")}) = ${definition.body}`,
|
|
105
|
+
` 3) Substitute arguments: ${bindings.join(", ")}`,
|
|
106
|
+
` 4) Expanded form: ${substituted}`
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const expandedValue = evaluate(substituted, scope);
|
|
111
|
+
steps.push(` 5) Evaluate expanded form: ${formatValue(expandedValue)}`);
|
|
112
|
+
} catch {
|
|
113
|
+
// If expanded form can't be directly evaluated, keep earlier steps.
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return steps;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function buildGenericWorkingSteps(term: string, scope: Record<string, unknown>): string[] {
|
|
120
|
+
const known = getKnownSymbols(term, scope);
|
|
121
|
+
if (known.length === 0) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const substitutions = known
|
|
126
|
+
.map(([name, value]) => `${name} = ${formatValue(value)}`)
|
|
127
|
+
.join(", ");
|
|
128
|
+
|
|
129
|
+
let substituted = term;
|
|
130
|
+
for (const [name, value] of known) {
|
|
131
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
132
|
+
substituted = substituted.replaceAll(new RegExp(String.raw`\b${name}\b`, "g"), `(${formatValue(value)})`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return [` 2) Substitute known values: ${substitutions}`, ` 3) Evaluate: ${substituted}`];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function computeInput(text: string): string {
|
|
140
|
+
const lines = text.split(/\r?\n/);
|
|
141
|
+
const scope: Record<string, unknown> = {};
|
|
142
|
+
const functions = new Map<string, FunctionDefinition>();
|
|
143
|
+
const contextLogs: string[] = [];
|
|
144
|
+
const resultLogs: string[] = [];
|
|
145
|
+
let contextCount = 0;
|
|
146
|
+
let queryCount = 0;
|
|
147
|
+
|
|
148
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
149
|
+
const raw = lines[index] ?? "";
|
|
150
|
+
const line = raw.trim();
|
|
151
|
+
const lineNo = index + 1;
|
|
152
|
+
|
|
153
|
+
if (line.length === 0 || line.startsWith("#") || line.startsWith("//")) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const queryMatch = new RegExp(QUERY_PATTERN).exec(line);
|
|
158
|
+
if (queryMatch) {
|
|
159
|
+
queryCount += 1;
|
|
160
|
+
const term = (queryMatch[1] ?? "").trim();
|
|
161
|
+
|
|
162
|
+
if (!term) {
|
|
163
|
+
resultLogs.push(`[ERR] Line ${lineNo}: empty query`, " Use format: term = ?", "");
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const functionWorking = buildFunctionWorkingSteps(term, scope, functions);
|
|
169
|
+
const genericWorking = functionWorking.length > 0 ? [] : buildGenericWorkingSteps(term, scope);
|
|
170
|
+
const value = evaluate(term, scope);
|
|
171
|
+
let resultStepNumber = "2";
|
|
172
|
+
if (genericWorking.length > 0) {
|
|
173
|
+
resultStepNumber = "4";
|
|
174
|
+
}
|
|
175
|
+
if (functionWorking.length > 0) {
|
|
176
|
+
resultStepNumber = "6";
|
|
177
|
+
}
|
|
178
|
+
resultLogs.push(
|
|
179
|
+
`[OK] Line ${lineNo}: ${term} = ?`,
|
|
180
|
+
" 1) Read term using the current context",
|
|
181
|
+
...functionWorking,
|
|
182
|
+
...genericWorking,
|
|
183
|
+
` ${resultStepNumber}) Result: ${formatValue(value)}`,
|
|
184
|
+
""
|
|
185
|
+
);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
resultLogs.push(
|
|
188
|
+
`[ERR] Line ${lineNo}: ${term} = ?`,
|
|
189
|
+
` ${error instanceof Error ? error.message : "Unable to evaluate query"}`,
|
|
190
|
+
""
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
contextCount += 1;
|
|
198
|
+
try {
|
|
199
|
+
const functionMatch = new RegExp(FUNCTION_DEFINITION_PATTERN).exec(line);
|
|
200
|
+
if (functionMatch) {
|
|
201
|
+
const name = (functionMatch[1] ?? "").trim();
|
|
202
|
+
const paramsRaw = (functionMatch[2] ?? "").trim();
|
|
203
|
+
const body = (functionMatch[3] ?? "").trim();
|
|
204
|
+
const params = paramsRaw
|
|
205
|
+
.split(",")
|
|
206
|
+
.map((item) => item.trim())
|
|
207
|
+
.filter((item) => item.length > 0);
|
|
208
|
+
|
|
209
|
+
functions.set(name, {
|
|
210
|
+
params,
|
|
211
|
+
body,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
evaluate(line, scope);
|
|
216
|
+
contextLogs.push(`- L${lineNo}: ${line}`);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
resultLogs.push(
|
|
219
|
+
`[ERR] Line ${lineNo}: ${line}`,
|
|
220
|
+
` ${error instanceof Error ? error.message : "Invalid context statement"}`,
|
|
221
|
+
""
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (contextCount === 0 && queryCount === 0) {
|
|
227
|
+
return [
|
|
228
|
+
"No input to compute.",
|
|
229
|
+
"",
|
|
230
|
+
"Use one shared context:",
|
|
231
|
+
" a = 8",
|
|
232
|
+
" f(x) = x^2 + a",
|
|
233
|
+
" f(3) = ?",
|
|
234
|
+
].join("\n");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const output: string[] = [];
|
|
238
|
+
output.push(`Context statements: ${contextCount}`, `Queries: ${queryCount}`);
|
|
239
|
+
|
|
240
|
+
if (contextLogs.length > 0) {
|
|
241
|
+
output.push("", "Context", ...contextLogs);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (queryCount === 0) {
|
|
245
|
+
output.push("", "No query lines found.", "Add one or more lines like: term = ?");
|
|
246
|
+
|
|
247
|
+
if (resultLogs.length > 0) {
|
|
248
|
+
output.push("", "Errors", ...resultLogs);
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
output.push("", "Results", ...resultLogs);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return output.join("\n").trimEnd();
|
|
255
|
+
}
|
package/src/index.ts
ADDED
package/src/ui.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import blessed from "blessed";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { computeInput } from "./compute";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_EDITOR_VALUE = "a = 8\nb = 3 * a\nf(x) = x^2 + b\na + b = ?\nf(4) = ?";
|
|
6
|
+
|
|
7
|
+
export function runTerminalApp(): void {
|
|
8
|
+
const shouldManageFlowControl = process.platform !== "win32";
|
|
9
|
+
if (shouldManageFlowControl) {
|
|
10
|
+
spawnSync("stty", ["-ixon"], { stdio: "inherit" });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const screen = blessed.screen({
|
|
14
|
+
smartCSR: true,
|
|
15
|
+
title: "Zue Math Editor",
|
|
16
|
+
fullUnicode: true,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const editor = blessed.textarea({
|
|
20
|
+
parent: screen,
|
|
21
|
+
label: " Math Editor ",
|
|
22
|
+
top: 0,
|
|
23
|
+
left: 0,
|
|
24
|
+
width: "100%",
|
|
25
|
+
height: "100%-3",
|
|
26
|
+
border: "line",
|
|
27
|
+
scrollbar: {
|
|
28
|
+
ch: " ",
|
|
29
|
+
},
|
|
30
|
+
inputOnFocus: true,
|
|
31
|
+
mouse: true,
|
|
32
|
+
keys: true,
|
|
33
|
+
vi: false,
|
|
34
|
+
padding: {
|
|
35
|
+
left: 1,
|
|
36
|
+
right: 1,
|
|
37
|
+
},
|
|
38
|
+
style: {
|
|
39
|
+
border: {
|
|
40
|
+
fg: "cyan",
|
|
41
|
+
},
|
|
42
|
+
focus: {
|
|
43
|
+
border: {
|
|
44
|
+
fg: "green",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
value: DEFAULT_EDITOR_VALUE,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
(editor as unknown as { readEditor: (_callback?: unknown) => void }).readEditor =
|
|
52
|
+
(_callback?: unknown): void => {
|
|
53
|
+
// no-op
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const results = blessed.box({
|
|
57
|
+
parent: screen,
|
|
58
|
+
label: " Results ",
|
|
59
|
+
top: 0,
|
|
60
|
+
left: 0,
|
|
61
|
+
width: "100%",
|
|
62
|
+
height: "100%-3",
|
|
63
|
+
border: "line",
|
|
64
|
+
scrollable: true,
|
|
65
|
+
alwaysScroll: true,
|
|
66
|
+
hidden: true,
|
|
67
|
+
mouse: true,
|
|
68
|
+
keys: false,
|
|
69
|
+
tags: false,
|
|
70
|
+
scrollbar: {
|
|
71
|
+
ch: " ",
|
|
72
|
+
},
|
|
73
|
+
padding: {
|
|
74
|
+
left: 1,
|
|
75
|
+
right: 1,
|
|
76
|
+
},
|
|
77
|
+
style: {
|
|
78
|
+
border: {
|
|
79
|
+
fg: "magenta",
|
|
80
|
+
},
|
|
81
|
+
focus: {
|
|
82
|
+
border: {
|
|
83
|
+
fg: "green",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const status = blessed.box({
|
|
90
|
+
parent: screen,
|
|
91
|
+
bottom: 2,
|
|
92
|
+
left: 0,
|
|
93
|
+
width: "100%",
|
|
94
|
+
height: 1,
|
|
95
|
+
tags: false,
|
|
96
|
+
style: {
|
|
97
|
+
fg: "black",
|
|
98
|
+
bg: "white",
|
|
99
|
+
},
|
|
100
|
+
content: " Mode: Editor | Define context, then compute term = ? with Ctrl+E ",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
blessed.box({
|
|
104
|
+
parent: screen,
|
|
105
|
+
bottom: 0,
|
|
106
|
+
left: 0,
|
|
107
|
+
width: "100%",
|
|
108
|
+
height: 2,
|
|
109
|
+
tags: false,
|
|
110
|
+
style: {
|
|
111
|
+
fg: "black",
|
|
112
|
+
bg: "green",
|
|
113
|
+
},
|
|
114
|
+
content:
|
|
115
|
+
" ^E Compute ^R Results ^T Editor ^↑/↓ Scroll Results ^Q Quit\n Use shared context: defs/functions, then term = ? on one or more lines ",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
let mode: "editor" | "results" = "editor";
|
|
119
|
+
|
|
120
|
+
const quitApp = (): void => {
|
|
121
|
+
if (shouldManageFlowControl) {
|
|
122
|
+
spawnSync("stty", ["ixon"], { stdio: "inherit" });
|
|
123
|
+
}
|
|
124
|
+
screen.destroy();
|
|
125
|
+
process.exit(0);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const setStatus = (message: string): void => {
|
|
129
|
+
status.setContent(` ${message} `);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const showEditor = (): void => {
|
|
133
|
+
mode = "editor";
|
|
134
|
+
results.hide();
|
|
135
|
+
editor.show();
|
|
136
|
+
editor.focus();
|
|
137
|
+
setStatus("Mode: Editor | Define context + use term = ?, then Ctrl+E");
|
|
138
|
+
screen.render();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const showResults = (): void => {
|
|
142
|
+
mode = "results";
|
|
143
|
+
editor.hide();
|
|
144
|
+
results.show();
|
|
145
|
+
results.focus();
|
|
146
|
+
setStatus("Mode: Results | Scroll with Up/Down");
|
|
147
|
+
screen.render();
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const computeAndShowResults = (): void => {
|
|
151
|
+
const input = editor.getValue();
|
|
152
|
+
const output = computeInput(input);
|
|
153
|
+
results.setContent(output);
|
|
154
|
+
results.setScroll(0);
|
|
155
|
+
showResults();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
screen.key(["C-e"], () => {
|
|
159
|
+
computeAndShowResults();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
editor.key(["C-e"], () => {
|
|
163
|
+
computeAndShowResults();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
screen.key(["C-r"], () => {
|
|
167
|
+
showResults();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
screen.key(["C-t"], () => {
|
|
171
|
+
showEditor();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
screen.key(["up"], () => {
|
|
175
|
+
if (mode === "results") {
|
|
176
|
+
results.scroll(-1);
|
|
177
|
+
screen.render();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
screen.key(["down"], () => {
|
|
182
|
+
if (mode === "results") {
|
|
183
|
+
results.scroll(1);
|
|
184
|
+
screen.render();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
screen.key(["pageup"], () => {
|
|
189
|
+
if (mode === "results") {
|
|
190
|
+
results.scroll(-10);
|
|
191
|
+
screen.render();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
screen.key(["pagedown"], () => {
|
|
196
|
+
if (mode === "results") {
|
|
197
|
+
results.scroll(10);
|
|
198
|
+
screen.render();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const quitKeys = ["C-q", "C-c", "escape"];
|
|
203
|
+
screen.key(quitKeys, quitApp);
|
|
204
|
+
editor.key(quitKeys, quitApp);
|
|
205
|
+
results.key(quitKeys, quitApp);
|
|
206
|
+
|
|
207
|
+
process.once("exit", () => {
|
|
208
|
+
if (shouldManageFlowControl) {
|
|
209
|
+
spawnSync("stty", ["ixon"], { stdio: "inherit" });
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
showEditor();
|
|
214
|
+
screen.render();
|
|
215
|
+
}
|