@churivibhav/reqex 0.1.2 → 0.1.3
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/README.md +26 -3
- package/dist/cli.js +1322 -287
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,93 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import path7 from "path";
|
|
5
|
+
import process3 from "process";
|
|
6
|
+
import { ZR_MOD_CTRL, ZR_MOD_SHIFT } from "@rezi-ui/core/keybindings";
|
|
6
7
|
import { createNodeApp } from "@rezi-ui/node";
|
|
7
8
|
|
|
8
|
-
// src/config/
|
|
9
|
+
// src/config/config.ts
|
|
9
10
|
import fs from "fs";
|
|
11
|
+
import path2 from "path";
|
|
12
|
+
|
|
13
|
+
// src/config/paths.ts
|
|
10
14
|
import os from "os";
|
|
11
15
|
import path from "path";
|
|
16
|
+
function getConfigDir() {
|
|
17
|
+
if (process.env.REQEX_CONFIG_DIR) {
|
|
18
|
+
return process.env.REQEX_CONFIG_DIR;
|
|
19
|
+
}
|
|
20
|
+
if (process.platform === "win32") {
|
|
21
|
+
const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
22
|
+
return path.join(appData, "reqex");
|
|
23
|
+
}
|
|
24
|
+
if (process.platform === "darwin") {
|
|
25
|
+
return path.join(os.homedir(), "Library", "Application Support", "reqex");
|
|
26
|
+
}
|
|
27
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
|
|
28
|
+
return path.join(xdg, "reqex");
|
|
29
|
+
}
|
|
30
|
+
function getProjectConfigDir(workspaceRoot) {
|
|
31
|
+
return path.join(workspaceRoot, ".reqex");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/config/config.ts
|
|
35
|
+
var DEFAULT_THEME = "auto";
|
|
36
|
+
function readJsonIfExists(filePath) {
|
|
37
|
+
try {
|
|
38
|
+
if (!fs.existsSync(filePath)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
42
|
+
return JSON.parse(raw);
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function parseThemePreference(value) {
|
|
48
|
+
if (value === "auto" || value === "light" || value === "dark") {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function loadConfig(workspaceRoot) {
|
|
54
|
+
const userConfig = readJsonIfExists(path2.join(getConfigDir(), "config.json"));
|
|
55
|
+
const projectConfig = readJsonIfExists(
|
|
56
|
+
path2.join(getProjectConfigDir(workspaceRoot), "config.json")
|
|
57
|
+
);
|
|
58
|
+
const envTheme = parseThemePreference(process.env.REQEX_THEME);
|
|
59
|
+
const fileTheme = projectConfig?.theme ?? userConfig?.theme ?? DEFAULT_THEME;
|
|
60
|
+
return {
|
|
61
|
+
theme: envTheme ?? fileTheme
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function watchConfig(workspaceRoot, onChange) {
|
|
65
|
+
const files = [
|
|
66
|
+
path2.join(getConfigDir(), "config.json"),
|
|
67
|
+
path2.join(getProjectConfigDir(workspaceRoot), "config.json")
|
|
68
|
+
];
|
|
69
|
+
const watchers = files.map((filePath) => {
|
|
70
|
+
const dir = path2.dirname(filePath);
|
|
71
|
+
try {
|
|
72
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
return fs.watch(dir, { persistent: false }, (_event, filename) => {
|
|
76
|
+
if (filename === "config.json") {
|
|
77
|
+
onChange();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
return () => {
|
|
82
|
+
for (const watcher of watchers) {
|
|
83
|
+
watcher.close();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/config/keybindings.ts
|
|
89
|
+
import fs2 from "fs";
|
|
90
|
+
import path3 from "path";
|
|
12
91
|
var VSCODE_DEFAULTS = {
|
|
13
92
|
F5: "request.send",
|
|
14
93
|
"ctrl+enter": "request.send",
|
|
@@ -30,13 +109,14 @@ var VSCODE_DEFAULTS = {
|
|
|
30
109
|
F11: "pane.zoom",
|
|
31
110
|
z: "pane.zoom",
|
|
32
111
|
F1: "help.show",
|
|
33
|
-
"?": "help.show",
|
|
34
112
|
"ctrl+/": "keybindings.show",
|
|
35
113
|
escape: "overlay.close",
|
|
36
114
|
"ctrl+q": "app.quit",
|
|
37
115
|
"ctrl+x": "request.cancel",
|
|
38
116
|
"ctrl+shift+c": "response.copy",
|
|
39
117
|
"ctrl+f": "response.search",
|
|
118
|
+
"ctrl+[": "response.jsonFoldToggle",
|
|
119
|
+
"ctrl+]": "response.jsonUnfoldAll",
|
|
40
120
|
"ctrl+tab": "response.tab.next",
|
|
41
121
|
"ctrl+shift+tab": "response.tab.prev"
|
|
42
122
|
};
|
|
@@ -71,6 +151,8 @@ var COMMAND_LABELS = {
|
|
|
71
151
|
"response.tab.prev": "Previous response tab",
|
|
72
152
|
"response.copy": "Copy response",
|
|
73
153
|
"response.search": "Search response",
|
|
154
|
+
"response.jsonFoldToggle": "Fold/unfold JSON node",
|
|
155
|
+
"response.jsonUnfoldAll": "Unfold all JSON",
|
|
74
156
|
"editor.searchNext": "Find next in editor"
|
|
75
157
|
};
|
|
76
158
|
var CHORD_PART_LABELS = {
|
|
@@ -116,38 +198,21 @@ function buildKeybindingsViewLines(bindings, maxLines) {
|
|
|
116
198
|
}
|
|
117
199
|
return rows;
|
|
118
200
|
}
|
|
119
|
-
function
|
|
120
|
-
if (process.env.REQEX_CONFIG_DIR) {
|
|
121
|
-
return process.env.REQEX_CONFIG_DIR;
|
|
122
|
-
}
|
|
123
|
-
if (process.platform === "win32") {
|
|
124
|
-
const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
125
|
-
return path.join(appData, "reqex");
|
|
126
|
-
}
|
|
127
|
-
if (process.platform === "darwin") {
|
|
128
|
-
return path.join(os.homedir(), "Library", "Application Support", "reqex");
|
|
129
|
-
}
|
|
130
|
-
const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
|
|
131
|
-
return path.join(xdg, "reqex");
|
|
132
|
-
}
|
|
133
|
-
function getProjectConfigDir(workspaceRoot) {
|
|
134
|
-
return path.join(workspaceRoot, ".reqex");
|
|
135
|
-
}
|
|
136
|
-
function readJsonIfExists(filePath) {
|
|
201
|
+
function readJsonIfExists2(filePath) {
|
|
137
202
|
try {
|
|
138
|
-
if (!
|
|
203
|
+
if (!fs2.existsSync(filePath)) {
|
|
139
204
|
return null;
|
|
140
205
|
}
|
|
141
|
-
const raw =
|
|
206
|
+
const raw = fs2.readFileSync(filePath, "utf8");
|
|
142
207
|
return JSON.parse(raw);
|
|
143
208
|
} catch {
|
|
144
209
|
return null;
|
|
145
210
|
}
|
|
146
211
|
}
|
|
147
212
|
function loadKeybindings(workspaceRoot) {
|
|
148
|
-
const userConfig =
|
|
149
|
-
const projectConfig =
|
|
150
|
-
|
|
213
|
+
const userConfig = readJsonIfExists2(path3.join(getConfigDir(), "keybindings.json"));
|
|
214
|
+
const projectConfig = readJsonIfExists2(
|
|
215
|
+
path3.join(getProjectConfigDir(workspaceRoot), "keybindings.json")
|
|
151
216
|
);
|
|
152
217
|
const preset = projectConfig?.preset ?? userConfig?.preset ?? "vscode";
|
|
153
218
|
const defaults = preset === "vim" ? VIM_DEFAULTS : VSCODE_DEFAULTS;
|
|
@@ -164,10 +229,10 @@ function watchKeybindings(workspaceRoot, onChange) {
|
|
|
164
229
|
const dirs = [getConfigDir(), getProjectConfigDir(workspaceRoot)];
|
|
165
230
|
const watchers = dirs.map((dir) => {
|
|
166
231
|
try {
|
|
167
|
-
|
|
232
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
168
233
|
} catch {
|
|
169
234
|
}
|
|
170
|
-
return
|
|
235
|
+
return fs2.watch(dir, { persistent: false }, () => onChange());
|
|
171
236
|
});
|
|
172
237
|
return () => {
|
|
173
238
|
for (const watcher of watchers) {
|
|
@@ -175,34 +240,166 @@ function watchKeybindings(workspaceRoot, onChange) {
|
|
|
175
240
|
}
|
|
176
241
|
};
|
|
177
242
|
}
|
|
178
|
-
var
|
|
179
|
-
editor: ["
|
|
180
|
-
response: ["
|
|
181
|
-
files: ["
|
|
182
|
-
overlay: ["
|
|
183
|
-
sending: ["
|
|
243
|
+
var FOOTER_COMMANDS = {
|
|
244
|
+
editor: ["file.save", "palette.commands"],
|
|
245
|
+
response: ["response.copy", "response.jsonFoldToggle", "response.jsonUnfoldAll"],
|
|
246
|
+
files: ["palette.files", "pane.focusNext", "palette.commands"],
|
|
247
|
+
overlay: ["overlay.close"],
|
|
248
|
+
sending: ["pane.focusNext", "app.quit"]
|
|
249
|
+
};
|
|
250
|
+
var FOOTER_LABELS = {
|
|
251
|
+
"request.send": "Send",
|
|
252
|
+
"request.cancel": "Cancel",
|
|
253
|
+
"pane.focusNext": "Panes",
|
|
254
|
+
"pane.focusPrev": "Prev pane",
|
|
255
|
+
"pane.focusFiles": "Files pane",
|
|
256
|
+
"pane.focusEditor": "Editor pane",
|
|
257
|
+
"pane.focusResponse": "Response pane",
|
|
258
|
+
"sidebar.toggle": "Sidebar",
|
|
259
|
+
"file.save": "Save",
|
|
260
|
+
"env.switcher": "Env",
|
|
261
|
+
"env.selectNext": "Next env",
|
|
262
|
+
"env.selectPrev": "Prev env",
|
|
263
|
+
"env.apply": "Select",
|
|
264
|
+
"overlay.close": "Close",
|
|
265
|
+
"app.quit": "Quit",
|
|
266
|
+
"palette.commands": "Palette",
|
|
267
|
+
"palette.files": "Files",
|
|
268
|
+
"help.show": "Help",
|
|
269
|
+
"keybindings.show": "Keys",
|
|
270
|
+
"pane.zoom": "Zoom",
|
|
271
|
+
"response.tab.next": "Next tab",
|
|
272
|
+
"response.tab.prev": "Prev tab",
|
|
273
|
+
"response.copy": "Copy",
|
|
274
|
+
"response.search": "Search",
|
|
275
|
+
"response.jsonFoldToggle": "Fold",
|
|
276
|
+
"response.jsonUnfoldAll": "Unfold",
|
|
277
|
+
"editor.searchNext": "Find next"
|
|
184
278
|
};
|
|
185
|
-
|
|
186
|
-
|
|
279
|
+
var PREFERRED_FOOTER_KEYS = {
|
|
280
|
+
"request.send": ["F5", "ctrl+enter", "alt+enter"],
|
|
281
|
+
"request.cancel": ["ctrl+x"],
|
|
282
|
+
"pane.focusNext": ["tab"],
|
|
283
|
+
"pane.focusPrev": ["shift+tab"],
|
|
284
|
+
"pane.focusFiles": ["ctrl+1", "alt+1"],
|
|
285
|
+
"pane.focusEditor": ["ctrl+2", "alt+2"],
|
|
286
|
+
"pane.focusResponse": ["ctrl+3", "alt+3"],
|
|
287
|
+
"sidebar.toggle": ["ctrl+b"],
|
|
288
|
+
"file.save": ["ctrl+s"],
|
|
289
|
+
"env.switcher": ["ctrl+e"],
|
|
290
|
+
"env.selectNext": ["down"],
|
|
291
|
+
"env.selectPrev": ["up"],
|
|
292
|
+
"env.apply": ["enter"],
|
|
293
|
+
"overlay.close": ["escape"],
|
|
294
|
+
"app.quit": ["ctrl+q"],
|
|
295
|
+
"palette.commands": ["F2", "ctrl+shift+p"],
|
|
296
|
+
"palette.files": ["ctrl+p"],
|
|
297
|
+
"help.show": ["F1"],
|
|
298
|
+
"keybindings.show": ["ctrl+/"],
|
|
299
|
+
"pane.zoom": ["F11", "z"],
|
|
300
|
+
"response.tab.next": ["ctrl+tab"],
|
|
301
|
+
"response.tab.prev": ["ctrl+shift+tab"],
|
|
302
|
+
"response.copy": ["ctrl+shift+c"],
|
|
303
|
+
"response.search": ["ctrl+f"],
|
|
304
|
+
"response.jsonFoldToggle": ["ctrl+["],
|
|
305
|
+
"response.jsonUnfoldAll": ["ctrl+]"],
|
|
306
|
+
"editor.searchNext": []
|
|
307
|
+
};
|
|
308
|
+
function commandKey(bindings, command) {
|
|
309
|
+
const keys = Object.entries(bindings).filter(([, boundCommand]) => boundCommand === command).map(([key]) => key);
|
|
310
|
+
if (command === "help.show" && keys.length === 0) {
|
|
311
|
+
return "F1";
|
|
312
|
+
}
|
|
313
|
+
if (keys.length === 0) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
const preferred = PREFERRED_FOOTER_KEYS[command] ?? [];
|
|
317
|
+
keys.sort((a, b) => {
|
|
318
|
+
const aPreferred = preferred.indexOf(a);
|
|
319
|
+
const bPreferred = preferred.indexOf(b);
|
|
320
|
+
if (aPreferred !== -1 || bPreferred !== -1) {
|
|
321
|
+
return (aPreferred === -1 ? Number.MAX_SAFE_INTEGER : aPreferred) - (bPreferred === -1 ? Number.MAX_SAFE_INTEGER : bPreferred);
|
|
322
|
+
}
|
|
323
|
+
return formatKeyChord(a).length - formatKeyChord(b).length || a.localeCompare(b);
|
|
324
|
+
});
|
|
325
|
+
return keys[0] ?? null;
|
|
326
|
+
}
|
|
327
|
+
function footerCommandList(context) {
|
|
328
|
+
const action = context.sending ? "request.cancel" : "request.send";
|
|
329
|
+
let contextual;
|
|
187
330
|
if (context.overlay !== "none") {
|
|
188
|
-
|
|
331
|
+
contextual = FOOTER_COMMANDS.overlay;
|
|
189
332
|
} else if (context.sending) {
|
|
190
|
-
|
|
333
|
+
contextual = FOOTER_COMMANDS.sending;
|
|
191
334
|
} else {
|
|
192
|
-
|
|
335
|
+
contextual = FOOTER_COMMANDS[context.focusPane] ?? FOOTER_COMMANDS.editor;
|
|
336
|
+
}
|
|
337
|
+
const commands = [action, ...contextual];
|
|
338
|
+
if (!context.hasResponse) {
|
|
339
|
+
return commands.filter(
|
|
340
|
+
(command) => command !== "response.copy" && command !== "response.jsonFoldToggle" && command !== "response.jsonUnfoldAll"
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
if (context.responseTab !== "pretty") {
|
|
344
|
+
return commands.filter(
|
|
345
|
+
(command) => command !== "response.jsonFoldToggle" && command !== "response.jsonUnfoldAll"
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (!context.hasFoldedJson) {
|
|
349
|
+
return commands.filter((command) => command !== "response.jsonUnfoldAll");
|
|
193
350
|
}
|
|
351
|
+
return commands;
|
|
352
|
+
}
|
|
353
|
+
function footerHintItems(context) {
|
|
354
|
+
const bindings = context.bindings ?? {};
|
|
355
|
+
const seen = /* @__PURE__ */ new Set();
|
|
356
|
+
const commands = [...footerCommandList(context), "help.show"].filter((command) => {
|
|
357
|
+
if (command === "help.show") {
|
|
358
|
+
seen.delete(command);
|
|
359
|
+
}
|
|
360
|
+
if (seen.has(command)) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
seen.add(command);
|
|
364
|
+
return true;
|
|
365
|
+
});
|
|
366
|
+
const items = commands.flatMap((command) => {
|
|
367
|
+
const key = commandKey(bindings, command);
|
|
368
|
+
if (!key) {
|
|
369
|
+
return [];
|
|
370
|
+
}
|
|
371
|
+
const formattedKey = command === "help.show" ? "F1" : formatKeyChord(key);
|
|
372
|
+
return [{ command, key: formattedKey, label: `${formattedKey} ${FOOTER_LABELS[command]}` }];
|
|
373
|
+
});
|
|
194
374
|
const leftBudget = 48;
|
|
195
375
|
const maxWidth = Math.max(20, context.viewportWidth - leftBudget);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
376
|
+
const pinned = items.filter(
|
|
377
|
+
(item) => item.command === "request.send" || item.command === "request.cancel" || item.command === "help.show"
|
|
378
|
+
);
|
|
379
|
+
const middle = items.filter((item) => !pinned.includes(item));
|
|
380
|
+
const selected = [];
|
|
381
|
+
const append = (item) => {
|
|
382
|
+
selected.push(item);
|
|
383
|
+
};
|
|
384
|
+
append(pinned[0] ?? items[0]);
|
|
385
|
+
for (const item of middle) {
|
|
386
|
+
const candidate = [...selected, item, pinned[pinned.length - 1]].filter(Boolean);
|
|
387
|
+
if (candidate.map((entry) => entry.label).join(" \xB7 ").length <= maxWidth) {
|
|
388
|
+
append(item);
|
|
389
|
+
}
|
|
199
390
|
}
|
|
200
|
-
|
|
391
|
+
const help = pinned.find((item) => item.command === "help.show");
|
|
392
|
+
if (help && selected[selected.length - 1]?.command !== "help.show") {
|
|
393
|
+
append(help);
|
|
394
|
+
}
|
|
395
|
+
return selected.filter(Boolean);
|
|
201
396
|
}
|
|
202
397
|
var HELP_HINT_LINES = [
|
|
203
398
|
"F5 Send request under cursor",
|
|
204
399
|
"Tab / Shift+Tab Cycle panes",
|
|
205
400
|
"Ctrl+S Save file",
|
|
401
|
+
"Mouse click Place editor cursor \xB7 Shift+click Select",
|
|
402
|
+
"Ctrl+A/C/X/V Select all, copy, cut, paste in editor",
|
|
206
403
|
"Ctrl+E Environment switcher",
|
|
207
404
|
"F2 / Ctrl+Shift+P Command palette",
|
|
208
405
|
"Ctrl+Shift+C Copy response tab",
|
|
@@ -270,33 +467,91 @@ function resolveRegionAtLine(regions, line) {
|
|
|
270
467
|
return best;
|
|
271
468
|
});
|
|
272
469
|
}
|
|
470
|
+
function resolveActiveRegion(parsedFile, cursorLine) {
|
|
471
|
+
if (!parsedFile) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
return resolveRegionAtLine(parsedFile.regions, cursorLine);
|
|
475
|
+
}
|
|
273
476
|
var HTTP_METHOD_PREFIX = /^\s*(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|CONNECT|TRACE|GRAPHQL)\b/u;
|
|
274
|
-
function
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
continue;
|
|
477
|
+
function firstRequestLine(regions, fileLines) {
|
|
478
|
+
const firstRegion = regions.find((region) => region.hasRequest && !region.isGlobal);
|
|
479
|
+
if (!firstRegion) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
for (let line = firstRegion.startLine; line <= firstRegion.endLine; line++) {
|
|
483
|
+
if (HTTP_METHOD_PREFIX.test(fileLines[line] ?? "")) {
|
|
484
|
+
return line;
|
|
283
485
|
}
|
|
284
|
-
const isActive = region.id === activeRegionId;
|
|
285
|
-
const line = fileLines[region.startLine] ?? "";
|
|
286
|
-
const markerCol = markerColumnAfterMethod(line);
|
|
287
|
-
markers.push({
|
|
288
|
-
line: region.startLine,
|
|
289
|
-
startColumn: markerCol,
|
|
290
|
-
endColumn: markerCol + 1,
|
|
291
|
-
severity: isActive ? "hint" : "info",
|
|
292
|
-
message: `${region.method ?? "REQ"} ${region.name}`
|
|
293
|
-
});
|
|
294
486
|
}
|
|
295
|
-
return
|
|
487
|
+
return firstRegion.startLine;
|
|
296
488
|
}
|
|
297
489
|
|
|
298
490
|
// src/engine/store.ts
|
|
299
491
|
import { store as httpyacStoreModule } from "httpyac";
|
|
492
|
+
|
|
493
|
+
// src/engine/env-config.ts
|
|
494
|
+
import fs3 from "fs/promises";
|
|
495
|
+
import path4 from "path";
|
|
496
|
+
var ENV_FILE_NAMES = [
|
|
497
|
+
".env.json",
|
|
498
|
+
"http-client.env.json",
|
|
499
|
+
"http-client.private.env.json"
|
|
500
|
+
];
|
|
501
|
+
function isRecord(value) {
|
|
502
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
503
|
+
}
|
|
504
|
+
function mergeVariables(target, source) {
|
|
505
|
+
for (const [envName, variables] of Object.entries(source)) {
|
|
506
|
+
if (!isRecord(variables)) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
target[envName] = {
|
|
510
|
+
...target[envName] ?? {},
|
|
511
|
+
...variables
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function directoriesToSearch(filePath, workingDir) {
|
|
516
|
+
const root = path4.resolve(workingDir);
|
|
517
|
+
const dirs = [];
|
|
518
|
+
let current = path4.dirname(path4.resolve(filePath));
|
|
519
|
+
while (current.startsWith(root)) {
|
|
520
|
+
dirs.unshift(current);
|
|
521
|
+
const parent = path4.dirname(current);
|
|
522
|
+
if (parent === current) {
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
current = parent;
|
|
526
|
+
}
|
|
527
|
+
return dirs.length > 0 ? dirs : [path4.dirname(path4.resolve(filePath))];
|
|
528
|
+
}
|
|
529
|
+
async function readEnvFile(filePath) {
|
|
530
|
+
try {
|
|
531
|
+
const raw = await fs3.readFile(filePath, "utf8");
|
|
532
|
+
const parsed = JSON.parse(raw);
|
|
533
|
+
return isRecord(parsed) ? parsed : null;
|
|
534
|
+
} catch (error) {
|
|
535
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
async function loadEnvironmentConfig(filePath, workingDir) {
|
|
542
|
+
const environments = {};
|
|
543
|
+
for (const dir of directoriesToSearch(filePath, workingDir)) {
|
|
544
|
+
for (const fileName of ENV_FILE_NAMES) {
|
|
545
|
+
const parsed = await readEnvFile(path4.join(dir, fileName));
|
|
546
|
+
if (parsed) {
|
|
547
|
+
mergeVariables(environments, parsed);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return Object.keys(environments).length > 0 ? { environments } : {};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/engine/store.ts
|
|
300
555
|
var store = new httpyacStoreModule.HttpFileStore();
|
|
301
556
|
var versions = /* @__PURE__ */ new Map();
|
|
302
557
|
function toRegion(region) {
|
|
@@ -321,7 +576,9 @@ function getParseVersion(filePath) {
|
|
|
321
576
|
}
|
|
322
577
|
async function parseFile(filePath, getText, workingDir, version) {
|
|
323
578
|
const parseVersion = version ?? getParseVersion(filePath);
|
|
579
|
+
const config = await loadEnvironmentConfig(filePath, workingDir);
|
|
324
580
|
const httpFile = await store.getOrCreate(filePath, getText, parseVersion, {
|
|
581
|
+
config,
|
|
325
582
|
workingDir
|
|
326
583
|
});
|
|
327
584
|
const regions = httpFile.httpRegions.map(toRegion);
|
|
@@ -403,11 +660,13 @@ async function sendRegion(options) {
|
|
|
403
660
|
const logResponse = async (response) => {
|
|
404
661
|
capturedResponse = response;
|
|
405
662
|
};
|
|
663
|
+
const config = await loadEnvironmentConfig(options.filePath, options.workingDir);
|
|
406
664
|
try {
|
|
407
665
|
await send({
|
|
408
666
|
httpFile,
|
|
409
667
|
httpRegion,
|
|
410
668
|
activeEnvironment: options.activeEnvironment,
|
|
669
|
+
config,
|
|
411
670
|
variables: options.variables,
|
|
412
671
|
logResponse
|
|
413
672
|
});
|
|
@@ -432,26 +691,28 @@ async function sendRegion(options) {
|
|
|
432
691
|
};
|
|
433
692
|
}
|
|
434
693
|
}
|
|
435
|
-
async function listEnvironments(filePath) {
|
|
694
|
+
async function listEnvironments(filePath, workingDir) {
|
|
436
695
|
const httpFile = getHttpFile(filePath);
|
|
437
696
|
if (!httpFile) {
|
|
438
697
|
return [];
|
|
439
698
|
}
|
|
440
|
-
|
|
699
|
+
const config = await loadEnvironmentConfig(filePath, workingDir);
|
|
700
|
+
return getEnvironments({ httpFile, config });
|
|
441
701
|
}
|
|
442
|
-
async function listVariables(filePath, activeEnvironment) {
|
|
702
|
+
async function listVariables(filePath, workingDir, activeEnvironment) {
|
|
443
703
|
const httpFile = getHttpFile(filePath);
|
|
444
704
|
if (!httpFile) {
|
|
445
705
|
return {};
|
|
446
706
|
}
|
|
447
|
-
|
|
707
|
+
const config = await loadEnvironmentConfig(filePath, workingDir);
|
|
708
|
+
return getVariables({ httpFile, activeEnvironment, config });
|
|
448
709
|
}
|
|
449
710
|
|
|
450
711
|
// src/keymap/dispatcher.ts
|
|
451
712
|
function buildBindingMap(bindings, execute) {
|
|
452
713
|
const map = {};
|
|
453
714
|
for (const [key, command] of Object.entries(bindings)) {
|
|
454
|
-
map[key] = () => execute(command);
|
|
715
|
+
map[key] = (ctx) => execute(command, ctx);
|
|
455
716
|
}
|
|
456
717
|
return map;
|
|
457
718
|
}
|
|
@@ -464,6 +725,8 @@ function commandFromPaletteId(id) {
|
|
|
464
725
|
"palette.commands": "palette.commands",
|
|
465
726
|
"help.show": "help.show",
|
|
466
727
|
"keybindings.show": "keybindings.show",
|
|
728
|
+
"response.jsonFoldToggle": "response.jsonFoldToggle",
|
|
729
|
+
"response.jsonUnfoldAll": "response.jsonUnfoldAll",
|
|
467
730
|
"pane.zoom": "pane.zoom",
|
|
468
731
|
"sidebar.toggle": "sidebar.toggle"
|
|
469
732
|
};
|
|
@@ -475,6 +738,8 @@ var COMMAND_ITEMS = [
|
|
|
475
738
|
{ id: "file.save", label: "Save File", description: "Write editor to disk", shortcut: "Ctrl+S" },
|
|
476
739
|
{ id: "env.switcher", label: "Switch Environment", description: "Choose active environment", shortcut: "Ctrl+E" },
|
|
477
740
|
{ id: "sidebar.toggle", label: "Toggle Sidebar", description: "Show/hide file tree", shortcut: "Ctrl+B" },
|
|
741
|
+
{ id: "response.jsonFoldToggle", label: "Fold/Unfold JSON", description: "Toggle JSON node in pretty response", shortcut: "Ctrl+[" },
|
|
742
|
+
{ id: "response.jsonUnfoldAll", label: "Unfold All JSON", description: "Expand folded pretty response JSON", shortcut: "Ctrl+]" },
|
|
478
743
|
{ id: "pane.zoom", label: "Zoom Pane", description: "Zoom focused pane", shortcut: "F11" },
|
|
479
744
|
{ id: "help.show", label: "Help", description: "Show quick help", shortcut: "F1" },
|
|
480
745
|
{ id: "keybindings.show", label: "Keybindings", description: "Show all keybindings", shortcut: "Ctrl+/" }
|
|
@@ -487,7 +752,7 @@ import { EventEmitter } from "events";
|
|
|
487
752
|
|
|
488
753
|
// src/workspace/discovery.ts
|
|
489
754
|
import { readdir, stat } from "fs/promises";
|
|
490
|
-
import
|
|
755
|
+
import path5 from "path";
|
|
491
756
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
492
757
|
".git",
|
|
493
758
|
"node_modules",
|
|
@@ -502,7 +767,7 @@ var FILE_KINDS = [
|
|
|
502
767
|
{ ext: ".env.json", kind: "env-json" }
|
|
503
768
|
];
|
|
504
769
|
function classifyFile(filePath) {
|
|
505
|
-
const base =
|
|
770
|
+
const base = path5.basename(filePath);
|
|
506
771
|
for (const { ext, kind } of FILE_KINDS) {
|
|
507
772
|
if (base === ext || base.endsWith(ext)) {
|
|
508
773
|
return kind;
|
|
@@ -531,7 +796,7 @@ async function discoverDirectory(rootDir, currentDir) {
|
|
|
531
796
|
continue;
|
|
532
797
|
}
|
|
533
798
|
}
|
|
534
|
-
const fullPath =
|
|
799
|
+
const fullPath = path5.join(currentDir, entry);
|
|
535
800
|
let entryStat;
|
|
536
801
|
try {
|
|
537
802
|
entryStat = await stat(fullPath);
|
|
@@ -622,17 +887,17 @@ var Workspace = class extends EventEmitter {
|
|
|
622
887
|
],
|
|
623
888
|
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 }
|
|
624
889
|
});
|
|
625
|
-
const handle = async (type,
|
|
890
|
+
const handle = async (type, path8) => {
|
|
626
891
|
try {
|
|
627
892
|
this.tree = await discoverFileTree(this.rootDir);
|
|
628
|
-
this.emitChange({ type, path:
|
|
893
|
+
this.emitChange({ type, path: path8 });
|
|
629
894
|
} catch (error) {
|
|
630
895
|
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
631
896
|
}
|
|
632
897
|
};
|
|
633
|
-
this.watcher.on("add", (
|
|
634
|
-
this.watcher.on("change", (
|
|
635
|
-
this.watcher.on("unlink", (
|
|
898
|
+
this.watcher.on("add", (path8) => void handle("add", path8));
|
|
899
|
+
this.watcher.on("change", (path8) => void handle("change", path8));
|
|
900
|
+
this.watcher.on("unlink", (path8) => void handle("unlink", path8));
|
|
636
901
|
this.watcher.on("error", (error) => {
|
|
637
902
|
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
638
903
|
});
|
|
@@ -662,12 +927,12 @@ function createInitialState(workspaceRoot) {
|
|
|
662
927
|
dirty: false,
|
|
663
928
|
parseVersion: 0,
|
|
664
929
|
parsedFile: null,
|
|
665
|
-
activeRegion: null,
|
|
666
930
|
responseEditor: {
|
|
667
931
|
scrollTop: 0,
|
|
668
932
|
scrollLeft: 0,
|
|
669
933
|
cursor: { line: 0, column: 0 },
|
|
670
|
-
selection: null
|
|
934
|
+
selection: null,
|
|
935
|
+
foldedJsonPaths: []
|
|
671
936
|
},
|
|
672
937
|
resultGeneration: 0,
|
|
673
938
|
editor: {
|
|
@@ -703,7 +968,9 @@ function createInitialState(workspaceRoot) {
|
|
|
703
968
|
},
|
|
704
969
|
settings: {
|
|
705
970
|
keymapPreset: "vscode",
|
|
706
|
-
keybindings: {}
|
|
971
|
+
keybindings: {},
|
|
972
|
+
theme: "auto",
|
|
973
|
+
themeMode: "dark"
|
|
707
974
|
}
|
|
708
975
|
};
|
|
709
976
|
}
|
|
@@ -760,7 +1027,196 @@ function createSendController() {
|
|
|
760
1027
|
};
|
|
761
1028
|
}
|
|
762
1029
|
|
|
1030
|
+
// src/utils/http-syntax.ts
|
|
1031
|
+
var KEYWORDS = /* @__PURE__ */ new Set([
|
|
1032
|
+
"GET",
|
|
1033
|
+
"POST",
|
|
1034
|
+
"PUT",
|
|
1035
|
+
"PATCH",
|
|
1036
|
+
"DELETE",
|
|
1037
|
+
"HEAD",
|
|
1038
|
+
"OPTIONS",
|
|
1039
|
+
"CONNECT",
|
|
1040
|
+
"TRACE",
|
|
1041
|
+
"GRAPHQL"
|
|
1042
|
+
]);
|
|
1043
|
+
function tokenizeHttpLine(line, _context) {
|
|
1044
|
+
const tokens = [];
|
|
1045
|
+
const methodMatch = /^\s*(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|CONNECT|TRACE|GRAPHQL)\b/u.exec(
|
|
1046
|
+
line
|
|
1047
|
+
);
|
|
1048
|
+
if (methodMatch) {
|
|
1049
|
+
const method = methodMatch[1] ?? "";
|
|
1050
|
+
tokens.push({ text: method, kind: "keyword" });
|
|
1051
|
+
const rest = line.slice(methodMatch[0].length);
|
|
1052
|
+
if (rest.length > 0) {
|
|
1053
|
+
tokens.push({ text: rest, kind: "string" });
|
|
1054
|
+
}
|
|
1055
|
+
return tokens;
|
|
1056
|
+
}
|
|
1057
|
+
if (/^\s*#/u.test(line)) {
|
|
1058
|
+
return [{ text: line, kind: "comment" }];
|
|
1059
|
+
}
|
|
1060
|
+
if (/^\s*\/\//u.test(line)) {
|
|
1061
|
+
return [{ text: line, kind: "comment" }];
|
|
1062
|
+
}
|
|
1063
|
+
if (/^\s*@/u.test(line)) {
|
|
1064
|
+
return [{ text: line, kind: "type" }];
|
|
1065
|
+
}
|
|
1066
|
+
const headerMatch = /^\s*([!#$%&'*+\-.^_`|~0-9A-Za-z]+)(\s*:\s*)(.*)$/u.exec(line);
|
|
1067
|
+
if (headerMatch) {
|
|
1068
|
+
tokens.push({ text: headerMatch[1] ?? "", kind: "function" });
|
|
1069
|
+
tokens.push({ text: headerMatch[2] ?? "", kind: "operator" });
|
|
1070
|
+
tokens.push({ text: headerMatch[3] ?? "", kind: "string" });
|
|
1071
|
+
return tokens;
|
|
1072
|
+
}
|
|
1073
|
+
const words = line.split(/(\s+)/u);
|
|
1074
|
+
for (const word of words) {
|
|
1075
|
+
if (KEYWORDS.has(word)) {
|
|
1076
|
+
tokens.push({ text: word, kind: "keyword" });
|
|
1077
|
+
} else if (word.length > 0) {
|
|
1078
|
+
tokens.push({ text: word, kind: "plain" });
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return tokens.length > 0 ? tokens : [{ text: line, kind: "plain" }];
|
|
1082
|
+
}
|
|
1083
|
+
function prettyJsonIfPossible(text) {
|
|
1084
|
+
const trimmed = text.trim();
|
|
1085
|
+
if (!trimmed) {
|
|
1086
|
+
return text;
|
|
1087
|
+
}
|
|
1088
|
+
try {
|
|
1089
|
+
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
1090
|
+
} catch {
|
|
1091
|
+
return text;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function statusTone(statusCode) {
|
|
1095
|
+
if (!statusCode) {
|
|
1096
|
+
return "cyan";
|
|
1097
|
+
}
|
|
1098
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
1099
|
+
return "green";
|
|
1100
|
+
}
|
|
1101
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
1102
|
+
return "yellow";
|
|
1103
|
+
}
|
|
1104
|
+
if (statusCode >= 500) {
|
|
1105
|
+
return "red";
|
|
1106
|
+
}
|
|
1107
|
+
return "cyan";
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/utils/json-folding.ts
|
|
1111
|
+
var INDENT = 2;
|
|
1112
|
+
function escapeJsonPointerSegment(segment) {
|
|
1113
|
+
return segment.replace(/~/gu, "~0").replace(/\//gu, "~1");
|
|
1114
|
+
}
|
|
1115
|
+
function isRecord2(value) {
|
|
1116
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1117
|
+
}
|
|
1118
|
+
function primitiveToJson(value) {
|
|
1119
|
+
return JSON.stringify(value);
|
|
1120
|
+
}
|
|
1121
|
+
function summarizeFolded(value) {
|
|
1122
|
+
if (Array.isArray(value)) {
|
|
1123
|
+
return `${value.length} ${value.length === 1 ? "item" : "items"}`;
|
|
1124
|
+
}
|
|
1125
|
+
if (isRecord2(value)) {
|
|
1126
|
+
const count = Object.keys(value).length;
|
|
1127
|
+
return `${count} ${count === 1 ? "property" : "properties"}`;
|
|
1128
|
+
}
|
|
1129
|
+
return "";
|
|
1130
|
+
}
|
|
1131
|
+
function renderValue(args) {
|
|
1132
|
+
const { value, path: path8, parentPath, key, indent, isLast, foldedPaths, lines, lineToFoldPath } = args;
|
|
1133
|
+
const leading = " ".repeat(indent);
|
|
1134
|
+
const keyPrefix = key === void 0 ? "" : `${JSON.stringify(key)}: `;
|
|
1135
|
+
const comma = isLast ? "" : ",";
|
|
1136
|
+
if (Array.isArray(value) || isRecord2(value)) {
|
|
1137
|
+
const isArray = Array.isArray(value);
|
|
1138
|
+
const open = isArray ? "[" : "{";
|
|
1139
|
+
const close = isArray ? "]" : "}";
|
|
1140
|
+
const entries = isArray ? value.map((item, index) => [String(index), item]) : Object.entries(value);
|
|
1141
|
+
if (entries.length === 0) {
|
|
1142
|
+
lines.push(`${leading}${keyPrefix}${open}${close}${comma}`);
|
|
1143
|
+
lineToFoldPath.push(parentPath);
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
if (foldedPaths.has(path8)) {
|
|
1147
|
+
lines.push(`${leading}${keyPrefix}${open} ... ${summarizeFolded(value)} ${close}${comma}`);
|
|
1148
|
+
lineToFoldPath.push(path8);
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
lines.push(`${leading}${keyPrefix}${open}`);
|
|
1152
|
+
lineToFoldPath.push(path8);
|
|
1153
|
+
entries.forEach(([entryKey, entryValue], index) => {
|
|
1154
|
+
const childPath = `${path8}/${escapeJsonPointerSegment(entryKey)}`;
|
|
1155
|
+
renderValue({
|
|
1156
|
+
value: entryValue,
|
|
1157
|
+
path: childPath,
|
|
1158
|
+
parentPath: path8,
|
|
1159
|
+
key: isArray ? void 0 : entryKey,
|
|
1160
|
+
indent: indent + INDENT,
|
|
1161
|
+
isLast: index === entries.length - 1,
|
|
1162
|
+
foldedPaths,
|
|
1163
|
+
lines,
|
|
1164
|
+
lineToFoldPath
|
|
1165
|
+
});
|
|
1166
|
+
});
|
|
1167
|
+
lines.push(`${leading}${close}${comma}`);
|
|
1168
|
+
lineToFoldPath.push(path8);
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
lines.push(`${leading}${keyPrefix}${primitiveToJson(value)}${comma}`);
|
|
1172
|
+
lineToFoldPath.push(parentPath);
|
|
1173
|
+
}
|
|
1174
|
+
function buildFoldableJsonView(text, foldedPaths) {
|
|
1175
|
+
try {
|
|
1176
|
+
const value = JSON.parse(text.trim());
|
|
1177
|
+
const lines = [];
|
|
1178
|
+
const lineToFoldPath = [];
|
|
1179
|
+
renderValue({
|
|
1180
|
+
value,
|
|
1181
|
+
path: "",
|
|
1182
|
+
parentPath: null,
|
|
1183
|
+
indent: 0,
|
|
1184
|
+
isLast: true,
|
|
1185
|
+
foldedPaths: new Set(foldedPaths),
|
|
1186
|
+
lines,
|
|
1187
|
+
lineToFoldPath
|
|
1188
|
+
});
|
|
1189
|
+
return { lines, lineToFoldPath };
|
|
1190
|
+
} catch {
|
|
1191
|
+
return { lines: text.split("\n"), lineToFoldPath: text.split("\n").map(() => null) };
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function toggleJsonFoldAtLine(text, foldedPaths, line) {
|
|
1195
|
+
const view = buildFoldableJsonView(text, foldedPaths);
|
|
1196
|
+
const path8 = view.lineToFoldPath[line] ?? null;
|
|
1197
|
+
if (path8 === null) {
|
|
1198
|
+
return foldedPaths;
|
|
1199
|
+
}
|
|
1200
|
+
const next = new Set(foldedPaths);
|
|
1201
|
+
if (next.has(path8)) {
|
|
1202
|
+
next.delete(path8);
|
|
1203
|
+
} else {
|
|
1204
|
+
next.add(path8);
|
|
1205
|
+
}
|
|
1206
|
+
return [...next].sort();
|
|
1207
|
+
}
|
|
1208
|
+
|
|
763
1209
|
// src/state/commands.ts
|
|
1210
|
+
function envNameFromSelectedIndex(environments, selectedIndex) {
|
|
1211
|
+
return selectedIndex === 0 ? void 0 : environments[selectedIndex - 1];
|
|
1212
|
+
}
|
|
1213
|
+
function envSelectedIndexFromActive(environments, activeEnvironment) {
|
|
1214
|
+
if (activeEnvironment.length === 0) {
|
|
1215
|
+
return 0;
|
|
1216
|
+
}
|
|
1217
|
+
const idx = environments.indexOf(activeEnvironment[0]);
|
|
1218
|
+
return idx === -1 ? 0 : idx + 1;
|
|
1219
|
+
}
|
|
764
1220
|
function createCommandContext(deps) {
|
|
765
1221
|
setPromptHandler(async (request) => {
|
|
766
1222
|
return new Promise((resolve) => {
|
|
@@ -791,27 +1247,28 @@ function createCommandContext(deps) {
|
|
|
791
1247
|
ui: { ...state.ui, pendingPrompt: null }
|
|
792
1248
|
}));
|
|
793
1249
|
};
|
|
794
|
-
const openFile = async (
|
|
795
|
-
const content = await deps.workspace.readFile(
|
|
1250
|
+
const openFile = async (path8) => {
|
|
1251
|
+
const content = await deps.workspace.readFile(path8);
|
|
796
1252
|
const lines = linesFromContent(content);
|
|
797
|
-
const parsed = await parseFile(
|
|
798
|
-
const environments = await listEnvironments(
|
|
799
|
-
const variables = await listVariables(
|
|
800
|
-
|
|
1253
|
+
const parsed = await parseFile(path8, async () => content, deps.workspace.rootDir);
|
|
1254
|
+
const environments = await listEnvironments(path8, deps.workspace.rootDir);
|
|
1255
|
+
const variables = await listVariables(path8, deps.workspace.rootDir, [
|
|
1256
|
+
...deps.getState().request.activeEnvironment
|
|
1257
|
+
]);
|
|
1258
|
+
const initialLine = firstRequestLine(parsed.regions, lines) ?? 0;
|
|
801
1259
|
deps.update((state) => ({
|
|
802
1260
|
...state,
|
|
803
|
-
selectedFilePath:
|
|
1261
|
+
selectedFilePath: path8,
|
|
804
1262
|
fileContent: content,
|
|
805
1263
|
fileLines: lines,
|
|
806
1264
|
dirty: false,
|
|
807
1265
|
parseVersion: parsed.version,
|
|
808
1266
|
parsedFile: parsed,
|
|
809
|
-
activeRegion,
|
|
810
1267
|
editor: {
|
|
811
1268
|
...state.editor,
|
|
812
|
-
cursor: { line:
|
|
1269
|
+
cursor: { line: initialLine, column: 0 },
|
|
813
1270
|
selection: null,
|
|
814
|
-
scrollTop:
|
|
1271
|
+
scrollTop: initialLine
|
|
815
1272
|
},
|
|
816
1273
|
request: {
|
|
817
1274
|
...state.request,
|
|
@@ -845,22 +1302,37 @@ function createCommandContext(deps) {
|
|
|
845
1302
|
deps.workspace.rootDir,
|
|
846
1303
|
version
|
|
847
1304
|
);
|
|
848
|
-
const activeRegion = state.activeRegion ? parsed.regions.find((region) => region.id === state.activeRegion?.id) ?? resolveRegionAtLine(parsed.regions, state.editor.cursor.line) : resolveRegionAtLine(parsed.regions, state.editor.cursor.line);
|
|
849
1305
|
deps.update((s) => ({
|
|
850
1306
|
...s,
|
|
851
1307
|
fileContent: content,
|
|
852
1308
|
dirty: false,
|
|
853
1309
|
parsedFile: parsed,
|
|
854
|
-
activeRegion: activeRegion ?? null,
|
|
855
1310
|
ui: { ...s.ui, statusMessage: "Saved" }
|
|
856
1311
|
}));
|
|
857
1312
|
};
|
|
858
1313
|
const runSend = async () => {
|
|
859
1314
|
const state = deps.getState();
|
|
860
|
-
if (!state.selectedFilePath
|
|
1315
|
+
if (!state.selectedFilePath) {
|
|
861
1316
|
return;
|
|
862
1317
|
}
|
|
863
|
-
const
|
|
1318
|
+
const cursorLine = state.editor.cursor.line;
|
|
1319
|
+
const content = contentFromLines(state.fileLines);
|
|
1320
|
+
let parsedFile = state.parsedFile;
|
|
1321
|
+
if (!parsedFile || state.dirty) {
|
|
1322
|
+
const version = bumpParseVersion(state.selectedFilePath);
|
|
1323
|
+
parsedFile = await parseFile(
|
|
1324
|
+
state.selectedFilePath,
|
|
1325
|
+
async () => content,
|
|
1326
|
+
deps.workspace.rootDir,
|
|
1327
|
+
version
|
|
1328
|
+
);
|
|
1329
|
+
deps.update((s) => ({
|
|
1330
|
+
...s,
|
|
1331
|
+
parsedFile,
|
|
1332
|
+
parseVersion: parsedFile.version
|
|
1333
|
+
}));
|
|
1334
|
+
}
|
|
1335
|
+
const region = resolveActiveRegion(parsedFile, cursorLine);
|
|
864
1336
|
if (!region) {
|
|
865
1337
|
deps.update((s) => ({
|
|
866
1338
|
...s,
|
|
@@ -872,13 +1344,13 @@ function createCommandContext(deps) {
|
|
|
872
1344
|
const gen = sendController.beginSend();
|
|
873
1345
|
deps.update((s) => ({
|
|
874
1346
|
...s,
|
|
875
|
-
activeRegion: region,
|
|
876
1347
|
request: { ...s.request, sending: true, error: null, result: null },
|
|
877
1348
|
responseEditor: {
|
|
878
1349
|
scrollTop: 0,
|
|
879
1350
|
scrollLeft: 0,
|
|
880
1351
|
cursor: { line: 0, column: 0 },
|
|
881
|
-
selection: null
|
|
1352
|
+
selection: null,
|
|
1353
|
+
foldedJsonPaths: []
|
|
882
1354
|
},
|
|
883
1355
|
resultGeneration: s.resultGeneration + 1,
|
|
884
1356
|
ui: { ...s.ui, focusPane: "response", responseTab: "pretty" }
|
|
@@ -894,10 +1366,12 @@ function createCommandContext(deps) {
|
|
|
894
1366
|
if (!sendController.isCurrent(gen)) {
|
|
895
1367
|
return;
|
|
896
1368
|
}
|
|
897
|
-
const variables = await listVariables(
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1369
|
+
const variables = await listVariables(
|
|
1370
|
+
state.selectedFilePath,
|
|
1371
|
+
deps.workspace.rootDir,
|
|
1372
|
+
[...deps.getState().request.activeEnvironment]
|
|
1373
|
+
);
|
|
1374
|
+
if (!sendController.isCurrent(gen)) {
|
|
901
1375
|
return;
|
|
902
1376
|
}
|
|
903
1377
|
deps.update((s) => ({
|
|
@@ -969,9 +1443,9 @@ function createCommandContext(deps) {
|
|
|
969
1443
|
ui: {
|
|
970
1444
|
...s.ui,
|
|
971
1445
|
overlay: s.ui.overlay === "env" ? "none" : "env",
|
|
972
|
-
envSelectedIndex:
|
|
973
|
-
|
|
974
|
-
s.request.
|
|
1446
|
+
envSelectedIndex: envSelectedIndexFromActive(
|
|
1447
|
+
s.request.environments,
|
|
1448
|
+
s.request.activeEnvironment
|
|
975
1449
|
)
|
|
976
1450
|
}
|
|
977
1451
|
}));
|
|
@@ -1002,10 +1476,13 @@ function createCommandContext(deps) {
|
|
|
1002
1476
|
}
|
|
1003
1477
|
break;
|
|
1004
1478
|
case "env.apply": {
|
|
1005
|
-
const envName =
|
|
1479
|
+
const envName = envNameFromSelectedIndex(
|
|
1480
|
+
state.request.environments,
|
|
1481
|
+
state.ui.envSelectedIndex
|
|
1482
|
+
);
|
|
1006
1483
|
void (async () => {
|
|
1007
1484
|
const activeEnvironment = envName ? [envName] : [];
|
|
1008
|
-
const variables = state.selectedFilePath ? await listVariables(state.selectedFilePath, activeEnvironment) : {};
|
|
1485
|
+
const variables = state.selectedFilePath ? await listVariables(state.selectedFilePath, deps.workspace.rootDir, activeEnvironment) : {};
|
|
1009
1486
|
deps.update((s) => ({
|
|
1010
1487
|
...s,
|
|
1011
1488
|
request: { ...s.request, activeEnvironment, variables },
|
|
@@ -1095,6 +1572,46 @@ function createCommandContext(deps) {
|
|
|
1095
1572
|
ui: { ...s.ui, focusPane: "response", responseTab: "pretty" }
|
|
1096
1573
|
}));
|
|
1097
1574
|
break;
|
|
1575
|
+
case "response.jsonFoldToggle": {
|
|
1576
|
+
const result = state.request.result;
|
|
1577
|
+
if (!result || state.ui.responseTab !== "pretty") {
|
|
1578
|
+
break;
|
|
1579
|
+
}
|
|
1580
|
+
const text = prettyJsonIfPossible(result.prettyBody || result.body);
|
|
1581
|
+
const foldedJsonPaths = toggleJsonFoldAtLine(
|
|
1582
|
+
text,
|
|
1583
|
+
state.responseEditor.foldedJsonPaths,
|
|
1584
|
+
state.responseEditor.cursor.line
|
|
1585
|
+
);
|
|
1586
|
+
if (foldedJsonPaths === state.responseEditor.foldedJsonPaths) {
|
|
1587
|
+
deps.update((s) => ({
|
|
1588
|
+
...s,
|
|
1589
|
+
ui: { ...s.ui, statusMessage: "No foldable JSON node" }
|
|
1590
|
+
}));
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1593
|
+
const visibleLineCount = buildFoldableJsonView(text, foldedJsonPaths).lines.length;
|
|
1594
|
+
deps.update((s) => ({
|
|
1595
|
+
...s,
|
|
1596
|
+
responseEditor: {
|
|
1597
|
+
...s.responseEditor,
|
|
1598
|
+
foldedJsonPaths,
|
|
1599
|
+
cursor: {
|
|
1600
|
+
...s.responseEditor.cursor,
|
|
1601
|
+
line: Math.min(s.responseEditor.cursor.line, Math.max(0, visibleLineCount - 1))
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
ui: { ...s.ui, focusPane: "response", statusMessage: "JSON fold toggled" }
|
|
1605
|
+
}));
|
|
1606
|
+
break;
|
|
1607
|
+
}
|
|
1608
|
+
case "response.jsonUnfoldAll":
|
|
1609
|
+
deps.update((s) => ({
|
|
1610
|
+
...s,
|
|
1611
|
+
responseEditor: { ...s.responseEditor, foldedJsonPaths: [] },
|
|
1612
|
+
ui: { ...s.ui, focusPane: "response", statusMessage: "JSON unfolded" }
|
|
1613
|
+
}));
|
|
1614
|
+
break;
|
|
1098
1615
|
case "editor.searchNext":
|
|
1099
1616
|
break;
|
|
1100
1617
|
default:
|
|
@@ -1117,103 +1634,240 @@ function createCommandContext(deps) {
|
|
|
1117
1634
|
return context;
|
|
1118
1635
|
}
|
|
1119
1636
|
|
|
1637
|
+
// src/state/editor-edit.ts
|
|
1638
|
+
function clamp(value, min, max) {
|
|
1639
|
+
return Math.max(min, Math.min(max, value));
|
|
1640
|
+
}
|
|
1641
|
+
function clampCursor(lines, cursor) {
|
|
1642
|
+
const lineCount = Math.max(1, lines.length);
|
|
1643
|
+
const line = clamp(cursor.line, 0, lineCount - 1);
|
|
1644
|
+
const text = lines[line] ?? "";
|
|
1645
|
+
return { line, column: clamp(cursor.column, 0, text.length) };
|
|
1646
|
+
}
|
|
1647
|
+
function normalizeSelection(selection) {
|
|
1648
|
+
const { anchor, active } = selection;
|
|
1649
|
+
if (anchor.line < active.line || anchor.line === active.line && anchor.column <= active.column) {
|
|
1650
|
+
return [anchor, active];
|
|
1651
|
+
}
|
|
1652
|
+
return [active, anchor];
|
|
1653
|
+
}
|
|
1654
|
+
function deleteSelection(lines, selection) {
|
|
1655
|
+
const nextLines = [...lines];
|
|
1656
|
+
if (nextLines.length === 0) {
|
|
1657
|
+
nextLines.push("");
|
|
1658
|
+
}
|
|
1659
|
+
const [rawStart, rawEnd] = normalizeSelection(selection);
|
|
1660
|
+
const start = clampCursor(nextLines, rawStart);
|
|
1661
|
+
const end = clampCursor(nextLines, rawEnd);
|
|
1662
|
+
const startLine = nextLines[start.line] ?? "";
|
|
1663
|
+
const endLine = nextLines[end.line] ?? "";
|
|
1664
|
+
if (start.line === end.line) {
|
|
1665
|
+
nextLines[start.line] = startLine.slice(0, start.column) + startLine.slice(end.column);
|
|
1666
|
+
return { lines: nextLines, cursor: start };
|
|
1667
|
+
}
|
|
1668
|
+
nextLines.splice(
|
|
1669
|
+
start.line,
|
|
1670
|
+
end.line - start.line + 1,
|
|
1671
|
+
startLine.slice(0, start.column) + endLine.slice(end.column)
|
|
1672
|
+
);
|
|
1673
|
+
return { lines: nextLines, cursor: start };
|
|
1674
|
+
}
|
|
1675
|
+
function insertText(lines, cursor, text) {
|
|
1676
|
+
const nextLines = [...lines];
|
|
1677
|
+
if (nextLines.length === 0) {
|
|
1678
|
+
nextLines.push("");
|
|
1679
|
+
}
|
|
1680
|
+
const safeCursor = clampCursor(nextLines, cursor);
|
|
1681
|
+
const currentLine = nextLines[safeCursor.line] ?? "";
|
|
1682
|
+
const before = currentLine.slice(0, safeCursor.column);
|
|
1683
|
+
const after = currentLine.slice(safeCursor.column);
|
|
1684
|
+
const insertLines = text.replace(/\r\n?/gu, "\n").split("\n");
|
|
1685
|
+
if (insertLines.length === 1) {
|
|
1686
|
+
const inserted = insertLines[0] ?? "";
|
|
1687
|
+
nextLines[safeCursor.line] = before + inserted + after;
|
|
1688
|
+
return {
|
|
1689
|
+
lines: nextLines,
|
|
1690
|
+
cursor: { line: safeCursor.line, column: safeCursor.column + inserted.length }
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
const first = before + (insertLines[0] ?? "");
|
|
1694
|
+
const lastInsert = insertLines[insertLines.length - 1] ?? "";
|
|
1695
|
+
const last = lastInsert + after;
|
|
1696
|
+
nextLines.splice(safeCursor.line, 1, first, ...insertLines.slice(1, -1), last);
|
|
1697
|
+
return {
|
|
1698
|
+
lines: nextLines,
|
|
1699
|
+
cursor: {
|
|
1700
|
+
line: safeCursor.line + insertLines.length - 1,
|
|
1701
|
+
column: lastInsert.length
|
|
1702
|
+
}
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
function pasteIntoEditor(args) {
|
|
1706
|
+
const base = args.selection ? deleteSelection(args.lines, args.selection) : { lines: args.lines, cursor: args.cursor };
|
|
1707
|
+
const next = insertText(base.lines, base.cursor, args.text);
|
|
1708
|
+
return { ...next, selection: null };
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// src/ui/theme-colors.ts
|
|
1712
|
+
import {
|
|
1713
|
+
darkTheme,
|
|
1714
|
+
lightTheme,
|
|
1715
|
+
resolveColorToken
|
|
1716
|
+
} from "@rezi-ui/core";
|
|
1717
|
+
function token(theme, path8) {
|
|
1718
|
+
return resolveColorToken(theme, path8) ?? 0;
|
|
1719
|
+
}
|
|
1720
|
+
function themeForMode(mode) {
|
|
1721
|
+
return mode === "light" ? lightTheme : darkTheme;
|
|
1722
|
+
}
|
|
1723
|
+
function colorsFor(theme) {
|
|
1724
|
+
return {
|
|
1725
|
+
bgBase: token(theme, "bg.base"),
|
|
1726
|
+
bgElevated: token(theme, "bg.elevated"),
|
|
1727
|
+
bgSubtle: token(theme, "bg.subtle"),
|
|
1728
|
+
fgPrimary: token(theme, "fg.primary"),
|
|
1729
|
+
paneFocused: token(theme, "accent.secondary"),
|
|
1730
|
+
paneMuted: token(theme, "fg.muted"),
|
|
1731
|
+
selected: token(theme, "accent.primary"),
|
|
1732
|
+
dirty: token(theme, "warning"),
|
|
1733
|
+
success: token(theme, "success"),
|
|
1734
|
+
warning: token(theme, "warning"),
|
|
1735
|
+
error: token(theme, "error"),
|
|
1736
|
+
info: token(theme, "info")
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function colorsForMode(mode) {
|
|
1740
|
+
return colorsFor(themeForMode(mode));
|
|
1741
|
+
}
|
|
1742
|
+
function statusColorForTone(colors, tone) {
|
|
1743
|
+
switch (tone) {
|
|
1744
|
+
case "green":
|
|
1745
|
+
return colors.success;
|
|
1746
|
+
case "yellow":
|
|
1747
|
+
return colors.warning;
|
|
1748
|
+
case "red":
|
|
1749
|
+
return colors.error;
|
|
1750
|
+
default:
|
|
1751
|
+
return colors.info;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1120
1755
|
// src/ui/app-view.ts
|
|
1121
|
-
import
|
|
1756
|
+
import path6 from "path";
|
|
1122
1757
|
import {
|
|
1123
|
-
rgb,
|
|
1124
1758
|
ui
|
|
1125
1759
|
} from "@rezi-ui/core";
|
|
1126
1760
|
|
|
1127
|
-
// src/
|
|
1128
|
-
var
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
"
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
}
|
|
1152
|
-
return tokens;
|
|
1153
|
-
}
|
|
1154
|
-
if (/^\s*#/u.test(line)) {
|
|
1155
|
-
return [{ text: line, kind: "comment" }];
|
|
1156
|
-
}
|
|
1157
|
-
if (/^\s*\/\//u.test(line)) {
|
|
1158
|
-
return [{ text: line, kind: "comment" }];
|
|
1159
|
-
}
|
|
1160
|
-
if (/^\s*@/u.test(line)) {
|
|
1161
|
-
return [{ text: line, kind: "type" }];
|
|
1162
|
-
}
|
|
1163
|
-
const headerMatch = /^\s*([!#$%&'*+\-.^_`|~0-9A-Za-z]+)(\s*:\s*)(.*)$/u.exec(line);
|
|
1164
|
-
if (headerMatch) {
|
|
1165
|
-
tokens.push({ text: headerMatch[1] ?? "", kind: "function" });
|
|
1166
|
-
tokens.push({ text: headerMatch[2] ?? "", kind: "operator" });
|
|
1167
|
-
tokens.push({ text: headerMatch[3] ?? "", kind: "string" });
|
|
1168
|
-
return tokens;
|
|
1169
|
-
}
|
|
1170
|
-
const words = line.split(/(\s+)/u);
|
|
1171
|
-
for (const word of words) {
|
|
1172
|
-
if (KEYWORDS.has(word)) {
|
|
1173
|
-
tokens.push({ text: word, kind: "keyword" });
|
|
1174
|
-
} else if (word.length > 0) {
|
|
1175
|
-
tokens.push({ text: word, kind: "plain" });
|
|
1176
|
-
}
|
|
1761
|
+
// src/ui/editor-gutter.ts
|
|
1762
|
+
var EDITOR_GUTTER_WIDTH = 1;
|
|
1763
|
+
var ACTIVE_GUTTER = "\u258E";
|
|
1764
|
+
var INACTIVE_GUTTER = " ";
|
|
1765
|
+
function clamp2(value, min, max) {
|
|
1766
|
+
return Math.max(min, Math.min(max, value));
|
|
1767
|
+
}
|
|
1768
|
+
function gutterTokenKind(method, highlighted) {
|
|
1769
|
+
if (!highlighted) {
|
|
1770
|
+
return "plain";
|
|
1771
|
+
}
|
|
1772
|
+
switch ((method ?? "GET").toUpperCase()) {
|
|
1773
|
+
case "GET":
|
|
1774
|
+
case "HEAD":
|
|
1775
|
+
case "OPTIONS":
|
|
1776
|
+
return "string";
|
|
1777
|
+
case "POST":
|
|
1778
|
+
case "PUT":
|
|
1779
|
+
case "PATCH":
|
|
1780
|
+
return "type";
|
|
1781
|
+
case "DELETE":
|
|
1782
|
+
return "operator";
|
|
1783
|
+
default:
|
|
1784
|
+
return "keyword";
|
|
1177
1785
|
}
|
|
1178
|
-
return tokens.length > 0 ? tokens : [{ text: line, kind: "plain" }];
|
|
1179
1786
|
}
|
|
1180
|
-
function
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
return
|
|
1184
|
-
}
|
|
1185
|
-
try {
|
|
1186
|
-
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
1187
|
-
} catch {
|
|
1188
|
-
return text;
|
|
1189
|
-
}
|
|
1787
|
+
function prefixEditorLines(lines, activeRegion) {
|
|
1788
|
+
return lines.map((line, index) => {
|
|
1789
|
+
const highlighted = activeRegion !== null && index >= activeRegion.startLine && index <= activeRegion.endLine;
|
|
1790
|
+
return `${highlighted ? ACTIVE_GUTTER : INACTIVE_GUTTER}${line}`;
|
|
1791
|
+
});
|
|
1190
1792
|
}
|
|
1191
|
-
function
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1793
|
+
function stripEditorLines(lines) {
|
|
1794
|
+
return lines.map((line) => line.slice(EDITOR_GUTTER_WIDTH));
|
|
1795
|
+
}
|
|
1796
|
+
function editorCursorFromSource(cursor) {
|
|
1797
|
+
return {
|
|
1798
|
+
line: cursor.line,
|
|
1799
|
+
column: cursor.column + EDITOR_GUTTER_WIDTH
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function sourceCursorFromEditor(cursor) {
|
|
1803
|
+
return {
|
|
1804
|
+
line: cursor.line,
|
|
1805
|
+
column: Math.max(0, cursor.column - EDITOR_GUTTER_WIDTH)
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
function sourceCursorFromEditorPoint(args) {
|
|
1809
|
+
const { x, y, rect, lines, scrollTop, scrollLeft } = args;
|
|
1810
|
+
if (rect.w <= 0 || rect.h <= 0 || x < rect.x || x >= rect.x + rect.w || y < rect.y || y >= rect.y + rect.h) {
|
|
1811
|
+
return null;
|
|
1197
1812
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1813
|
+
const lineCount = Math.max(1, lines.length);
|
|
1814
|
+
const lineNumberWidth2 = args.lineNumbers === false ? 0 : String(lineCount).length + 1;
|
|
1815
|
+
const line = clamp2(scrollTop + (y - rect.y), 0, lineCount - 1);
|
|
1816
|
+
const lineText = lines[line] ?? "";
|
|
1817
|
+
const localTextColumn = x - rect.x - lineNumberWidth2;
|
|
1818
|
+
const editorColumn = Math.max(0, scrollLeft + localTextColumn);
|
|
1819
|
+
const sourceColumn = clamp2(editorColumn - EDITOR_GUTTER_WIDTH, 0, lineText.length);
|
|
1820
|
+
return { line, column: sourceColumn };
|
|
1821
|
+
}
|
|
1822
|
+
function editorSelectionFromSource(selection) {
|
|
1823
|
+
if (!selection) {
|
|
1824
|
+
return null;
|
|
1200
1825
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1826
|
+
return {
|
|
1827
|
+
anchor: editorCursorFromSource(selection.anchor),
|
|
1828
|
+
active: editorCursorFromSource(selection.active)
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
function sourceSelectionFromEditor(selection) {
|
|
1832
|
+
if (!selection) {
|
|
1833
|
+
return null;
|
|
1203
1834
|
}
|
|
1204
|
-
return
|
|
1835
|
+
return {
|
|
1836
|
+
anchor: sourceCursorFromEditor(selection.anchor),
|
|
1837
|
+
active: sourceCursorFromEditor(selection.active)
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
function createRegionAwareTokenizer(activeRegion) {
|
|
1841
|
+
return (line, context) => {
|
|
1842
|
+
const gutter = line[0] ?? INACTIVE_GUTTER;
|
|
1843
|
+
const body = line.slice(EDITOR_GUTTER_WIDTH);
|
|
1844
|
+
const highlighted = activeRegion !== null && context.lineNumber >= activeRegion.startLine && context.lineNumber <= activeRegion.endLine;
|
|
1845
|
+
const tokens = [
|
|
1846
|
+
{
|
|
1847
|
+
text: gutter,
|
|
1848
|
+
kind: gutterTokenKind(activeRegion?.method, highlighted && gutter !== INACTIVE_GUTTER)
|
|
1849
|
+
}
|
|
1850
|
+
];
|
|
1851
|
+
if (body.length > 0) {
|
|
1852
|
+
tokens.push(...tokenizeHttpLine(body, context));
|
|
1853
|
+
}
|
|
1854
|
+
return tokens;
|
|
1855
|
+
};
|
|
1205
1856
|
}
|
|
1206
1857
|
|
|
1207
1858
|
// src/ui/app-view.ts
|
|
1208
|
-
function paneStyle(focused) {
|
|
1209
|
-
return
|
|
1859
|
+
function paneStyle(colors, focused) {
|
|
1860
|
+
return {
|
|
1861
|
+
bg: colors.bgElevated,
|
|
1862
|
+
...focused ? { fg: colors.paneFocused, bold: true } : { fg: colors.paneMuted }
|
|
1863
|
+
};
|
|
1210
1864
|
}
|
|
1211
|
-
function renderFileTree(state, deps) {
|
|
1865
|
+
function renderFileTree(state, deps, colors) {
|
|
1212
1866
|
return ui.panel(
|
|
1213
1867
|
{
|
|
1214
1868
|
id: "pane-files",
|
|
1215
1869
|
title: state.ui.focusPane === "files" ? "\u25CF Files" : "Files",
|
|
1216
|
-
style: paneStyle(state.ui.focusPane === "files")
|
|
1870
|
+
style: paneStyle(colors, state.ui.focusPane === "files")
|
|
1217
1871
|
},
|
|
1218
1872
|
[
|
|
1219
1873
|
ui.tree({
|
|
@@ -1228,7 +1882,7 @@ function renderFileTree(state, deps) {
|
|
|
1228
1882
|
onPress: (node) => deps.onTreePress(node),
|
|
1229
1883
|
renderNode: (node, _depth, nodeState) => ui.row({ gap: 1 }, [
|
|
1230
1884
|
ui.text(nodeState.selected ? `\u25B8 ${node.name}` : node.name, {
|
|
1231
|
-
style: nodeState.selected ? { fg:
|
|
1885
|
+
style: nodeState.selected ? { fg: colors.selected, bold: true } : state.dirty && node.path === state.selectedFilePath ? { fg: colors.dirty } : void 0
|
|
1232
1886
|
})
|
|
1233
1887
|
]),
|
|
1234
1888
|
flex: 1,
|
|
@@ -1237,38 +1891,32 @@ function renderFileTree(state, deps) {
|
|
|
1237
1891
|
]
|
|
1238
1892
|
);
|
|
1239
1893
|
}
|
|
1240
|
-
function renderEditor(state, deps, readOnly) {
|
|
1241
|
-
const activeRegion =
|
|
1894
|
+
function renderEditor(state, deps, readOnly, colors) {
|
|
1895
|
+
const activeRegion = resolveActiveRegion(state.parsedFile, state.editor.cursor.line);
|
|
1242
1896
|
const titleParts = [
|
|
1243
1897
|
state.ui.focusPane === "editor" ? "\u25CF Editor" : "Editor",
|
|
1244
1898
|
state.selectedFilePath ? state.selectedFilePath.split("/").pop() : "No file",
|
|
1245
1899
|
state.dirty ? " \u25CF" : "",
|
|
1246
1900
|
activeRegion ? ` | ${activeRegion.method ?? "?"} ${activeRegion.name}` : ""
|
|
1247
1901
|
];
|
|
1248
|
-
const diagnostics = state.parsedFile ? buildRegionDiagnostics(
|
|
1249
|
-
state.parsedFile.regions,
|
|
1250
|
-
activeRegion?.id ?? null,
|
|
1251
|
-
state.fileLines
|
|
1252
|
-
) : [];
|
|
1253
1902
|
return ui.panel(
|
|
1254
1903
|
{
|
|
1255
1904
|
id: "pane-editor",
|
|
1256
1905
|
title: titleParts.join(""),
|
|
1257
|
-
style: paneStyle(state.ui.focusPane === "editor")
|
|
1906
|
+
style: paneStyle(colors, state.ui.focusPane === "editor")
|
|
1258
1907
|
},
|
|
1259
1908
|
[
|
|
1260
1909
|
ui.codeEditor({
|
|
1261
1910
|
id: "editor",
|
|
1262
|
-
lines: state.fileLines,
|
|
1263
|
-
cursor: state.editor.cursor,
|
|
1264
|
-
selection: state.editor.selection,
|
|
1911
|
+
lines: prefixEditorLines(state.fileLines, activeRegion),
|
|
1912
|
+
cursor: editorCursorFromSource(state.editor.cursor),
|
|
1913
|
+
selection: editorSelectionFromSource(state.editor.selection),
|
|
1265
1914
|
scrollTop: state.editor.scrollTop,
|
|
1266
1915
|
scrollLeft: state.editor.scrollLeft,
|
|
1267
1916
|
readOnly,
|
|
1268
1917
|
lineNumbers: true,
|
|
1269
1918
|
syntaxLanguage: "plain",
|
|
1270
|
-
tokenizeLine:
|
|
1271
|
-
diagnostics,
|
|
1919
|
+
tokenizeLine: createRegionAwareTokenizer(activeRegion),
|
|
1272
1920
|
onChange: (lines, cursor) => deps.onEditorChange(lines, cursor),
|
|
1273
1921
|
onSelectionChange: deps.onEditorSelection,
|
|
1274
1922
|
onScroll: deps.onEditorScroll,
|
|
@@ -1295,7 +1943,7 @@ function responseCursorProps(state, deps) {
|
|
|
1295
1943
|
onSelectionChange: deps.onResponseSelection
|
|
1296
1944
|
};
|
|
1297
1945
|
}
|
|
1298
|
-
function renderResponseBody(state, deps) {
|
|
1946
|
+
function renderResponseBody(state, deps, colors) {
|
|
1299
1947
|
const result = state.request.result;
|
|
1300
1948
|
const scroll = responseScrollProps(state, deps);
|
|
1301
1949
|
const cursor = responseCursorProps(state, deps);
|
|
@@ -1338,7 +1986,7 @@ function renderResponseBody(state, deps) {
|
|
|
1338
1986
|
case "variables":
|
|
1339
1987
|
return ui.codeEditor({
|
|
1340
1988
|
id: `response-vars-${gen}`,
|
|
1341
|
-
lines:
|
|
1989
|
+
lines: JSON.stringify(state.request.variables, null, 2).split("\n"),
|
|
1342
1990
|
readOnly: true,
|
|
1343
1991
|
syntaxLanguage: "json",
|
|
1344
1992
|
...cursor,
|
|
@@ -1350,16 +1998,20 @@ function renderResponseBody(state, deps) {
|
|
|
1350
1998
|
...result.testResults.map(
|
|
1351
1999
|
(test) => ui.text(`${test.status === "SUCCESS" ? "\u2713" : "\u2717"} ${test.message}`, {
|
|
1352
2000
|
style: {
|
|
1353
|
-
fg: test.status === "SUCCESS" ?
|
|
2001
|
+
fg: test.status === "SUCCESS" ? colors.success : test.status === "SKIPPED" ? colors.warning : colors.error
|
|
1354
2002
|
}
|
|
1355
2003
|
})
|
|
1356
2004
|
)
|
|
1357
2005
|
]);
|
|
1358
2006
|
case "pretty":
|
|
1359
|
-
default:
|
|
2007
|
+
default: {
|
|
2008
|
+
const prettyView = buildFoldableJsonView(
|
|
2009
|
+
prettyJsonIfPossible(result.prettyBody || result.body),
|
|
2010
|
+
state.responseEditor.foldedJsonPaths
|
|
2011
|
+
);
|
|
1360
2012
|
return ui.codeEditor({
|
|
1361
2013
|
id: `response-pretty-${gen}`,
|
|
1362
|
-
lines:
|
|
2014
|
+
lines: prettyView.lines,
|
|
1363
2015
|
readOnly: true,
|
|
1364
2016
|
lineNumbers: true,
|
|
1365
2017
|
syntaxLanguage: "json",
|
|
@@ -1368,9 +2020,10 @@ function renderResponseBody(state, deps) {
|
|
|
1368
2020
|
...scroll,
|
|
1369
2021
|
flex: 1
|
|
1370
2022
|
});
|
|
2023
|
+
}
|
|
1371
2024
|
}
|
|
1372
2025
|
}
|
|
1373
|
-
function renderResponse(state, deps) {
|
|
2026
|
+
function renderResponse(state, deps, colors) {
|
|
1374
2027
|
const result = state.request.result;
|
|
1375
2028
|
const statusLine = state.request.sending || !result ? null : `${result.protocol ?? "HTTP"} ${result.statusCode ?? "?"} ${result.statusMessage ?? ""} \xB7 ${result.durationMs ?? "?"} ms`;
|
|
1376
2029
|
const tabItems = [
|
|
@@ -1384,12 +2037,15 @@ function renderResponse(state, deps) {
|
|
|
1384
2037
|
{
|
|
1385
2038
|
id: "pane-response",
|
|
1386
2039
|
title: state.ui.focusPane === "response" ? "\u25CF Response" : "Response",
|
|
1387
|
-
style: paneStyle(state.ui.focusPane === "response")
|
|
2040
|
+
style: paneStyle(colors, state.ui.focusPane === "response")
|
|
1388
2041
|
},
|
|
1389
2042
|
[
|
|
1390
2043
|
ui.row({ gap: 2 }, [
|
|
1391
2044
|
state.request.sending ? ui.spinner({ label: "Sending request\u2026" }) : ui.text(statusLine ?? "Ready", {
|
|
1392
|
-
style: {
|
|
2045
|
+
style: {
|
|
2046
|
+
fg: statusColorForTone(colors, statusTone(result?.statusCode)),
|
|
2047
|
+
bold: true
|
|
2048
|
+
}
|
|
1393
2049
|
}),
|
|
1394
2050
|
result?.error && !state.request.sending ? ui.badge(result.error, { variant: "error" }) : null
|
|
1395
2051
|
]),
|
|
@@ -1401,43 +2057,42 @@ function renderResponse(state, deps) {
|
|
|
1401
2057
|
disabled: state.request.sending,
|
|
1402
2058
|
onPress: () => deps.onResponseTab(tab.key)
|
|
1403
2059
|
})
|
|
1404
|
-
)
|
|
2060
|
+
),
|
|
2061
|
+
state.ui.responseTab === "pretty" && result ? ui.button({
|
|
2062
|
+
id: "response-json-fold",
|
|
2063
|
+
label: "Fold/Unfold",
|
|
2064
|
+
disabled: state.request.sending,
|
|
2065
|
+
onPress: deps.onResponseJsonFoldToggle
|
|
2066
|
+
}) : null,
|
|
2067
|
+
state.ui.responseTab === "pretty" && state.responseEditor.foldedJsonPaths.length > 0 ? ui.button({
|
|
2068
|
+
id: "response-json-unfold-all",
|
|
2069
|
+
label: "Unfold all",
|
|
2070
|
+
disabled: state.request.sending,
|
|
2071
|
+
onPress: deps.onResponseJsonUnfoldAll
|
|
2072
|
+
}) : null
|
|
1405
2073
|
]),
|
|
1406
|
-
renderResponseBody(state, deps)
|
|
2074
|
+
renderResponseBody(state, deps, colors)
|
|
1407
2075
|
]
|
|
1408
2076
|
);
|
|
1409
2077
|
}
|
|
1410
|
-
function
|
|
1411
|
-
const tone = statusTone(code);
|
|
1412
|
-
switch (tone) {
|
|
1413
|
-
case "green":
|
|
1414
|
-
return [120, 220, 140];
|
|
1415
|
-
case "yellow":
|
|
1416
|
-
return [240, 200, 100];
|
|
1417
|
-
case "red":
|
|
1418
|
-
return [240, 120, 120];
|
|
1419
|
-
default:
|
|
1420
|
-
return [140, 200, 240];
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
function renderMainLayout(state, deps) {
|
|
2078
|
+
function renderMainLayout(state, deps, colors) {
|
|
1424
2079
|
const layoutMode = resolveLayoutMode(state.ui.viewportWidth);
|
|
1425
2080
|
const zoom = state.ui.zoomPane;
|
|
1426
2081
|
if (zoom) {
|
|
1427
|
-
if (zoom === "files") return renderFileTree(state, deps);
|
|
1428
|
-
if (zoom === "editor") return renderEditor(state, deps, false);
|
|
1429
|
-
return renderResponse(state, deps);
|
|
2082
|
+
if (zoom === "files") return renderFileTree(state, deps, colors);
|
|
2083
|
+
if (zoom === "editor") return renderEditor(state, deps, false, colors);
|
|
2084
|
+
return renderResponse(state, deps, colors);
|
|
1430
2085
|
}
|
|
1431
2086
|
if (layoutMode === "stacked") {
|
|
1432
|
-
const pane = state.ui.focusPane === "files" ? renderFileTree(state, deps) : state.ui.focusPane === "response" ? renderResponse(state, deps) : renderEditor(state, deps, false);
|
|
2087
|
+
const pane = state.ui.focusPane === "files" ? renderFileTree(state, deps, colors) : state.ui.focusPane === "response" ? renderResponse(state, deps, colors) : renderEditor(state, deps, false, colors);
|
|
1433
2088
|
return pane;
|
|
1434
2089
|
}
|
|
1435
2090
|
if (layoutMode === "sidebar-overlay") {
|
|
1436
2091
|
return ui.row({ gap: 1, flex: 1 }, [
|
|
1437
|
-
state.ui.sidebarVisible ? ui.box({ width: 28, flex: 0 }, [renderFileTree(state, deps)]) : null,
|
|
2092
|
+
state.ui.sidebarVisible ? ui.box({ width: 28, flex: 0 }, [renderFileTree(state, deps, colors)]) : null,
|
|
1438
2093
|
ui.column({ gap: 1, flex: 1 }, [
|
|
1439
|
-
renderEditor(state, deps, false),
|
|
1440
|
-
renderResponse(state, deps)
|
|
2094
|
+
renderEditor(state, deps, false, colors),
|
|
2095
|
+
renderResponse(state, deps, colors)
|
|
1441
2096
|
])
|
|
1442
2097
|
]);
|
|
1443
2098
|
}
|
|
@@ -1451,38 +2106,53 @@ function renderMainLayout(state, deps) {
|
|
|
1451
2106
|
onChange: deps.onSplitChange
|
|
1452
2107
|
},
|
|
1453
2108
|
[
|
|
1454
|
-
renderFileTree(state, deps),
|
|
1455
|
-
renderEditor(state, deps, false),
|
|
1456
|
-
renderResponse(state, deps)
|
|
2109
|
+
renderFileTree(state, deps, colors),
|
|
2110
|
+
renderEditor(state, deps, false, colors),
|
|
2111
|
+
renderResponse(state, deps, colors)
|
|
1457
2112
|
]
|
|
1458
2113
|
)
|
|
1459
2114
|
]);
|
|
1460
2115
|
}
|
|
1461
|
-
function renderFooter(state) {
|
|
2116
|
+
function renderFooter(state, deps, colors) {
|
|
1462
2117
|
const env = state.request.activeEnvironment.length > 0 ? state.request.activeEnvironment.join(",") : "none";
|
|
1463
|
-
const dirName =
|
|
2118
|
+
const dirName = path6.basename(state.workspaceRoot);
|
|
1464
2119
|
const branch = state.ui.gitBranch ?? "\u2014";
|
|
1465
|
-
const fileName = state.selectedFilePath ?
|
|
1466
|
-
const hints =
|
|
2120
|
+
const fileName = state.selectedFilePath ? path6.basename(state.selectedFilePath) : null;
|
|
2121
|
+
const hints = footerHintItems({
|
|
1467
2122
|
focusPane: state.ui.focusPane,
|
|
1468
2123
|
overlay: state.ui.overlay,
|
|
1469
2124
|
viewportWidth: state.ui.viewportWidth,
|
|
1470
|
-
sending: state.request.sending
|
|
2125
|
+
sending: state.request.sending,
|
|
2126
|
+
bindings: state.settings.keybindings,
|
|
2127
|
+
responseTab: state.ui.responseTab,
|
|
2128
|
+
hasResponse: Boolean(state.request.result),
|
|
2129
|
+
hasFoldedJson: state.responseEditor.foldedJsonPaths.length > 0
|
|
1471
2130
|
});
|
|
1472
2131
|
return ui.statusBar({
|
|
1473
2132
|
id: "status-bar",
|
|
2133
|
+
style: { bg: colors.bgSubtle, fg: colors.fgPrimary },
|
|
1474
2134
|
left: [
|
|
1475
|
-
ui.text(dirName, { style: { fg:
|
|
1476
|
-
ui.text(` \u2387 ${branch}`, { style: { fg:
|
|
2135
|
+
ui.text(dirName, { style: { fg: colors.paneFocused, bold: true } }),
|
|
2136
|
+
ui.text(` \u2387 ${branch}`, { style: { fg: colors.success } }),
|
|
1477
2137
|
fileName ? ui.text(` | ${fileName}`) : null,
|
|
1478
|
-
state.dirty ? ui.text(" \u25CF", { style: { fg:
|
|
1479
|
-
ui.
|
|
2138
|
+
state.dirty ? ui.text(" \u25CF", { style: { fg: colors.dirty } }) : null,
|
|
2139
|
+
ui.button({
|
|
2140
|
+
id: "status-env.switcher",
|
|
2141
|
+
label: ` ^E env: ${env}`,
|
|
2142
|
+
onPress: () => deps.onCommand("env.switcher")
|
|
2143
|
+
}),
|
|
1480
2144
|
state.ui.statusMessage ? ui.text(` | ${state.ui.statusMessage}`) : null
|
|
1481
2145
|
].filter(Boolean),
|
|
1482
|
-
right:
|
|
2146
|
+
right: hints.map(
|
|
2147
|
+
(hint) => ui.button({
|
|
2148
|
+
id: `status-${hint.command}`,
|
|
2149
|
+
label: hint.label,
|
|
2150
|
+
onPress: () => deps.onCommand(hint.command)
|
|
2151
|
+
})
|
|
2152
|
+
)
|
|
1483
2153
|
});
|
|
1484
2154
|
}
|
|
1485
|
-
function renderOverlayContent(state, deps) {
|
|
2155
|
+
function renderOverlayContent(state, deps, colors) {
|
|
1486
2156
|
switch (state.ui.overlay) {
|
|
1487
2157
|
case "env":
|
|
1488
2158
|
return ui.modal({
|
|
@@ -1490,12 +2160,18 @@ function renderOverlayContent(state, deps) {
|
|
|
1490
2160
|
title: "Environment",
|
|
1491
2161
|
content: ui.column({ gap: 1 }, [
|
|
1492
2162
|
ui.text("Select environment (Enter to apply, Esc to close)"),
|
|
1493
|
-
ui.
|
|
1494
|
-
|
|
2163
|
+
ui.button({
|
|
2164
|
+
id: "env-option-none",
|
|
2165
|
+
label: "(none)",
|
|
2166
|
+
onPress: () => deps.onEnvSelect(0),
|
|
2167
|
+
style: state.ui.envSelectedIndex === 0 ? { fg: colors.selected, bold: true } : void 0
|
|
1495
2168
|
}),
|
|
1496
2169
|
...state.request.environments.map(
|
|
1497
|
-
(env, index) => ui.
|
|
1498
|
-
|
|
2170
|
+
(env, index) => ui.button({
|
|
2171
|
+
id: `env-option-${index}`,
|
|
2172
|
+
label: env,
|
|
2173
|
+
onPress: () => deps.onEnvSelect(index + 1),
|
|
2174
|
+
style: index + 1 === state.ui.envSelectedIndex ? { fg: colors.selected, bold: true } : void 0
|
|
1499
2175
|
})
|
|
1500
2176
|
)
|
|
1501
2177
|
]),
|
|
@@ -1561,9 +2237,10 @@ function renderOverlayContent(state, deps) {
|
|
|
1561
2237
|
}
|
|
1562
2238
|
}
|
|
1563
2239
|
function renderApp(state, deps) {
|
|
1564
|
-
const
|
|
1565
|
-
|
|
1566
|
-
|
|
2240
|
+
const colors = colorsForMode(state.settings.themeMode);
|
|
2241
|
+
const base = ui.column({ gap: 1, flex: 1, style: { bg: colors.bgBase } }, [
|
|
2242
|
+
renderMainLayout(state, deps, colors),
|
|
2243
|
+
renderFooter(state, deps, colors)
|
|
1567
2244
|
]);
|
|
1568
2245
|
if (state.ui.overlay === "none") {
|
|
1569
2246
|
return base;
|
|
@@ -1576,7 +2253,7 @@ function renderApp(state, deps) {
|
|
|
1576
2253
|
backdrop: "dim",
|
|
1577
2254
|
closeOnEscape: true,
|
|
1578
2255
|
onClose: deps.onOverlayClose,
|
|
1579
|
-
content: renderOverlayContent(state, deps)
|
|
2256
|
+
content: renderOverlayContent(state, deps, colors)
|
|
1580
2257
|
})
|
|
1581
2258
|
]);
|
|
1582
2259
|
}
|
|
@@ -1591,6 +2268,35 @@ function focusPaneId(pane) {
|
|
|
1591
2268
|
}
|
|
1592
2269
|
}
|
|
1593
2270
|
|
|
2271
|
+
// src/ui/scroll.ts
|
|
2272
|
+
import { routeWheel } from "@rezi-ui/core";
|
|
2273
|
+
function lineNumberWidth(lines, lineNumbers) {
|
|
2274
|
+
if (!lineNumbers) {
|
|
2275
|
+
return 0;
|
|
2276
|
+
}
|
|
2277
|
+
return String(Math.max(1, lines.length)).length + 1;
|
|
2278
|
+
}
|
|
2279
|
+
function maxLineWidth(lines) {
|
|
2280
|
+
return lines.reduce((max, line) => Math.max(max, line.length), 0);
|
|
2281
|
+
}
|
|
2282
|
+
function resolveWheelScroll(event, state, lines, viewport) {
|
|
2283
|
+
const routed = routeWheel(event, {
|
|
2284
|
+
scrollX: state.scrollLeft,
|
|
2285
|
+
scrollY: state.scrollTop,
|
|
2286
|
+
contentWidth: maxLineWidth(lines),
|
|
2287
|
+
contentHeight: Math.max(1, lines.length),
|
|
2288
|
+
viewportWidth: Math.max(0, viewport.width - lineNumberWidth(lines, viewport.lineNumbers ?? true)),
|
|
2289
|
+
viewportHeight: Math.max(0, viewport.height)
|
|
2290
|
+
});
|
|
2291
|
+
if (routed.nextScrollX === void 0 && routed.nextScrollY === void 0) {
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2294
|
+
return {
|
|
2295
|
+
scrollTop: routed.nextScrollY ?? state.scrollTop,
|
|
2296
|
+
scrollLeft: routed.nextScrollX ?? state.scrollLeft
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
|
|
1594
2300
|
// src/utils/clipboard.ts
|
|
1595
2301
|
import clipboard from "clipboardy";
|
|
1596
2302
|
async function copyToClipboard(text) {
|
|
@@ -1604,12 +2310,11 @@ async function copyToClipboard(text) {
|
|
|
1604
2310
|
return false;
|
|
1605
2311
|
}
|
|
1606
2312
|
}
|
|
1607
|
-
function
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
}
|
|
2313
|
+
async function readFromClipboard() {
|
|
2314
|
+
try {
|
|
2315
|
+
return await clipboard.read();
|
|
2316
|
+
} catch {
|
|
2317
|
+
return null;
|
|
1613
2318
|
}
|
|
1614
2319
|
}
|
|
1615
2320
|
|
|
@@ -1631,17 +2336,229 @@ async function getGitBranch(root) {
|
|
|
1631
2336
|
}
|
|
1632
2337
|
}
|
|
1633
2338
|
|
|
2339
|
+
// src/utils/terminal-theme.ts
|
|
2340
|
+
import process2 from "process";
|
|
2341
|
+
import { Readable } from "stream";
|
|
2342
|
+
var OSC_11_BACKGROUND_QUERY = "\x1B]11;?\x07";
|
|
2343
|
+
function parseHexComponent(value) {
|
|
2344
|
+
const parsed = Number.parseInt(value, 16);
|
|
2345
|
+
if (Number.isNaN(parsed)) {
|
|
2346
|
+
return null;
|
|
2347
|
+
}
|
|
2348
|
+
if (value.length <= 2) {
|
|
2349
|
+
return parsed;
|
|
2350
|
+
}
|
|
2351
|
+
return Math.round(parsed * 255 / 65535);
|
|
2352
|
+
}
|
|
2353
|
+
function parseOsc11BackgroundColor(sequence) {
|
|
2354
|
+
const rgbMatch = /\x1b\]11;rgb:([0-9a-f]{1,4})\/([0-9a-f]{1,4})\/([0-9a-f]{1,4})(?:\x07|\x1b\\)/iu.exec(
|
|
2355
|
+
sequence
|
|
2356
|
+
);
|
|
2357
|
+
if (rgbMatch) {
|
|
2358
|
+
const red = parseHexComponent(rgbMatch[1] ?? "");
|
|
2359
|
+
const green = parseHexComponent(rgbMatch[2] ?? "");
|
|
2360
|
+
const blue = parseHexComponent(rgbMatch[3] ?? "");
|
|
2361
|
+
if (red === null || green === null || blue === null) {
|
|
2362
|
+
return null;
|
|
2363
|
+
}
|
|
2364
|
+
return { red, green, blue };
|
|
2365
|
+
}
|
|
2366
|
+
const hexMatch = /\x1b\]11;#([0-9a-f]{6})(?:\x07|\x1b\\)/iu.exec(sequence);
|
|
2367
|
+
if (hexMatch) {
|
|
2368
|
+
const hex = hexMatch[1] ?? "";
|
|
2369
|
+
const red = parseHexComponent(hex.slice(0, 2));
|
|
2370
|
+
const green = parseHexComponent(hex.slice(2, 4));
|
|
2371
|
+
const blue = parseHexComponent(hex.slice(4, 6));
|
|
2372
|
+
if (red === null || green === null || blue === null) {
|
|
2373
|
+
return null;
|
|
2374
|
+
}
|
|
2375
|
+
return { red, green, blue };
|
|
2376
|
+
}
|
|
2377
|
+
return null;
|
|
2378
|
+
}
|
|
2379
|
+
function themeModeForBackgroundColor({ red, green, blue }) {
|
|
2380
|
+
const linear = [red, green, blue].map((component) => {
|
|
2381
|
+
const normalized = component / 255;
|
|
2382
|
+
return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
|
|
2383
|
+
});
|
|
2384
|
+
const luminance = 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
|
|
2385
|
+
return luminance > 0.5 ? "light" : "dark";
|
|
2386
|
+
}
|
|
2387
|
+
function themeModeFromColorFgbg() {
|
|
2388
|
+
const colorFgbg = process2.env.COLORFGBG;
|
|
2389
|
+
if (!colorFgbg) {
|
|
2390
|
+
return null;
|
|
2391
|
+
}
|
|
2392
|
+
const parts = colorFgbg.split(";");
|
|
2393
|
+
const background = Number.parseInt(parts[parts.length - 1] ?? "", 10);
|
|
2394
|
+
if (Number.isNaN(background)) {
|
|
2395
|
+
return null;
|
|
2396
|
+
}
|
|
2397
|
+
return background > 7 ? "light" : "dark";
|
|
2398
|
+
}
|
|
2399
|
+
async function detectTerminalThemeMode(options = {}) {
|
|
2400
|
+
const input = options.input ?? process2.stdin;
|
|
2401
|
+
const output = options.output ?? process2.stdout;
|
|
2402
|
+
const timeoutMs = options.timeoutMs ?? 150;
|
|
2403
|
+
if (!("isTTY" in input && input.isTTY) || !("isTTY" in output && output.isTTY)) {
|
|
2404
|
+
return null;
|
|
2405
|
+
}
|
|
2406
|
+
const wasRaw = "isRaw" in input ? input.isRaw : void 0;
|
|
2407
|
+
let settled = false;
|
|
2408
|
+
let buffer = "";
|
|
2409
|
+
return await new Promise((resolve) => {
|
|
2410
|
+
const drainInput = () => {
|
|
2411
|
+
if (!(input instanceof Readable)) {
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
while (input.readableLength > 0) {
|
|
2415
|
+
input.read();
|
|
2416
|
+
}
|
|
2417
|
+
input.pause();
|
|
2418
|
+
};
|
|
2419
|
+
const cleanup = () => {
|
|
2420
|
+
if (settled) {
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
settled = true;
|
|
2424
|
+
clearTimeout(timer);
|
|
2425
|
+
input.removeListener("data", onData);
|
|
2426
|
+
if (wasRaw !== void 0 && "setRawMode" in input) {
|
|
2427
|
+
input.setRawMode?.(wasRaw);
|
|
2428
|
+
}
|
|
2429
|
+
drainInput();
|
|
2430
|
+
};
|
|
2431
|
+
const finish = (mode) => {
|
|
2432
|
+
cleanup();
|
|
2433
|
+
resolve(mode);
|
|
2434
|
+
};
|
|
2435
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
2436
|
+
const onData = (chunk) => {
|
|
2437
|
+
buffer += Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk;
|
|
2438
|
+
const color = parseOsc11BackgroundColor(buffer);
|
|
2439
|
+
if (color) {
|
|
2440
|
+
finish(themeModeForBackgroundColor(color));
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
input.setRawMode?.(true);
|
|
2444
|
+
if (input instanceof Readable) {
|
|
2445
|
+
input.resume();
|
|
2446
|
+
}
|
|
2447
|
+
input.on("data", onData);
|
|
2448
|
+
output.write(OSC_11_BACKGROUND_QUERY);
|
|
2449
|
+
});
|
|
2450
|
+
}
|
|
2451
|
+
async function resolveThemeMode(preference, options = {}) {
|
|
2452
|
+
const { allowProbe = true, fallbackMode = "dark" } = options;
|
|
2453
|
+
if (preference === "light") {
|
|
2454
|
+
return "light";
|
|
2455
|
+
}
|
|
2456
|
+
if (preference === "dark") {
|
|
2457
|
+
return "dark";
|
|
2458
|
+
}
|
|
2459
|
+
if (allowProbe) {
|
|
2460
|
+
const detected = await detectTerminalThemeMode();
|
|
2461
|
+
if (detected) {
|
|
2462
|
+
return detected;
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
return themeModeFromColorFgbg() ?? fallbackMode;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
1634
2468
|
// src/cli.ts
|
|
2469
|
+
function isPointInRect(x, y, rect) {
|
|
2470
|
+
return x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h;
|
|
2471
|
+
}
|
|
2472
|
+
function responseEditorId(state) {
|
|
2473
|
+
switch (state.ui.responseTab) {
|
|
2474
|
+
case "pretty":
|
|
2475
|
+
return `response-pretty-${state.resultGeneration}`;
|
|
2476
|
+
case "raw":
|
|
2477
|
+
return `response-raw-${state.resultGeneration}`;
|
|
2478
|
+
case "variables":
|
|
2479
|
+
return `response-vars-${state.resultGeneration}`;
|
|
2480
|
+
case "headers":
|
|
2481
|
+
case "tests":
|
|
2482
|
+
return null;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
function responseEditorLines(state) {
|
|
2486
|
+
const result = state.request.result;
|
|
2487
|
+
if (!result) {
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
switch (state.ui.responseTab) {
|
|
2491
|
+
case "pretty":
|
|
2492
|
+
return buildFoldableJsonView(
|
|
2493
|
+
prettyJsonIfPossible(result.prettyBody || result.body),
|
|
2494
|
+
state.responseEditor.foldedJsonPaths
|
|
2495
|
+
).lines;
|
|
2496
|
+
case "raw":
|
|
2497
|
+
return result.body ? result.body.split("\n") : [""];
|
|
2498
|
+
case "variables":
|
|
2499
|
+
return JSON.stringify(state.request.variables, null, 2).split("\n");
|
|
2500
|
+
case "headers":
|
|
2501
|
+
case "tests":
|
|
2502
|
+
return null;
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
function handleWheelEvent(event, app) {
|
|
2506
|
+
if (event.kind !== "mouse" || event.mouseKind !== 5) {
|
|
2507
|
+
return false;
|
|
2508
|
+
}
|
|
2509
|
+
let handled = false;
|
|
2510
|
+
app.update((prev) => {
|
|
2511
|
+
const editorRect = app.measureElement("editor");
|
|
2512
|
+
if (editorRect && isPointInRect(event.x, event.y, editorRect)) {
|
|
2513
|
+
handled = true;
|
|
2514
|
+
const next = resolveWheelScroll(
|
|
2515
|
+
event,
|
|
2516
|
+
prev.editor,
|
|
2517
|
+
prefixEditorLines(prev.fileLines, null),
|
|
2518
|
+
{ width: editorRect.w, height: editorRect.h }
|
|
2519
|
+
);
|
|
2520
|
+
return {
|
|
2521
|
+
...prev,
|
|
2522
|
+
ui: { ...prev.ui, focusPane: "editor" },
|
|
2523
|
+
editor: next ? { ...prev.editor, scrollTop: next.scrollTop, scrollLeft: next.scrollLeft } : prev.editor
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
const id = responseEditorId(prev);
|
|
2527
|
+
const responseRect = id ? app.measureElement(id) : null;
|
|
2528
|
+
if (responseRect && isPointInRect(event.x, event.y, responseRect)) {
|
|
2529
|
+
handled = true;
|
|
2530
|
+
const lines = responseEditorLines(prev);
|
|
2531
|
+
const next = lines ? resolveWheelScroll(event, prev.responseEditor, lines, {
|
|
2532
|
+
width: responseRect.w,
|
|
2533
|
+
height: responseRect.h
|
|
2534
|
+
}) : null;
|
|
2535
|
+
return {
|
|
2536
|
+
...prev,
|
|
2537
|
+
ui: { ...prev.ui, focusPane: "response" },
|
|
2538
|
+
responseEditor: next ? { ...prev.responseEditor, scrollTop: next.scrollTop, scrollLeft: next.scrollLeft } : prev.responseEditor
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
return prev;
|
|
2542
|
+
});
|
|
2543
|
+
return handled;
|
|
2544
|
+
}
|
|
1635
2545
|
async function main() {
|
|
1636
2546
|
initEngineProviders();
|
|
1637
|
-
const workspaceRoot =
|
|
2547
|
+
const workspaceRoot = path7.resolve(process3.argv[2] ?? process3.cwd());
|
|
1638
2548
|
const workspace = new Workspace(workspaceRoot);
|
|
1639
2549
|
const tree = await workspace.open();
|
|
2550
|
+
const config = loadConfig(workspaceRoot);
|
|
2551
|
+
const initialThemeMode = await resolveThemeMode(config.theme);
|
|
1640
2552
|
let currentState = createInitialState(workspaceRoot);
|
|
1641
2553
|
currentState = {
|
|
1642
2554
|
...currentState,
|
|
1643
2555
|
fileTree: tree,
|
|
1644
2556
|
expandedPaths: tree.filter((n) => n.kind === "directory").map((n) => n.path),
|
|
2557
|
+
settings: {
|
|
2558
|
+
...currentState.settings,
|
|
2559
|
+
theme: config.theme,
|
|
2560
|
+
themeMode: initialThemeMode
|
|
2561
|
+
},
|
|
1645
2562
|
ui: {
|
|
1646
2563
|
...currentState.ui,
|
|
1647
2564
|
gitBranch: await getGitBranch(workspaceRoot)
|
|
@@ -1662,19 +2579,24 @@ async function main() {
|
|
|
1662
2579
|
}
|
|
1663
2580
|
const loaded = loadKeybindings(workspaceRoot);
|
|
1664
2581
|
app.keys({
|
|
1665
|
-
...buildBindingMap(loaded.bindings, (command) => {
|
|
2582
|
+
...buildBindingMap(loaded.bindings, (command, ctx) => {
|
|
2583
|
+
currentState = ctx.state;
|
|
1666
2584
|
if (command === "response.copy") {
|
|
1667
2585
|
void handleCopy(app, currentState);
|
|
1668
2586
|
}
|
|
1669
2587
|
bus.execute(command);
|
|
1670
2588
|
}),
|
|
1671
2589
|
enter: {
|
|
1672
|
-
handler: () =>
|
|
2590
|
+
handler: (ctx) => {
|
|
2591
|
+
currentState = ctx.state;
|
|
2592
|
+
bus.execute("env.apply");
|
|
2593
|
+
},
|
|
1673
2594
|
when: (ctx) => ctx.state.ui.overlay === "env",
|
|
1674
2595
|
description: "Apply selected environment"
|
|
1675
2596
|
},
|
|
1676
2597
|
up: {
|
|
1677
|
-
handler: () => {
|
|
2598
|
+
handler: (ctx) => {
|
|
2599
|
+
currentState = ctx.state;
|
|
1678
2600
|
if (currentState.ui.overlay === "env") {
|
|
1679
2601
|
bus.execute("env.selectPrev");
|
|
1680
2602
|
}
|
|
@@ -1682,7 +2604,8 @@ async function main() {
|
|
|
1682
2604
|
when: (ctx) => ctx.state.ui.overlay === "env" || ctx.state.ui.overlay === "commandPalette"
|
|
1683
2605
|
},
|
|
1684
2606
|
down: {
|
|
1685
|
-
handler: () => {
|
|
2607
|
+
handler: (ctx) => {
|
|
2608
|
+
currentState = ctx.state;
|
|
1686
2609
|
if (currentState.ui.overlay === "env") {
|
|
1687
2610
|
bus.execute("env.selectNext");
|
|
1688
2611
|
}
|
|
@@ -1693,11 +2616,33 @@ async function main() {
|
|
|
1693
2616
|
app.update((state) => ({
|
|
1694
2617
|
...state,
|
|
1695
2618
|
settings: {
|
|
2619
|
+
...state.settings,
|
|
1696
2620
|
keymapPreset: loaded.preset,
|
|
1697
2621
|
keybindings: loaded.bindings
|
|
1698
2622
|
}
|
|
1699
2623
|
}));
|
|
1700
2624
|
};
|
|
2625
|
+
const applyTheme = async (preference) => {
|
|
2626
|
+
if (!app) {
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
const themeMode = await resolveThemeMode(preference, {
|
|
2630
|
+
allowProbe: false,
|
|
2631
|
+
fallbackMode: currentState.settings.themeMode
|
|
2632
|
+
});
|
|
2633
|
+
app.setTheme(themeForMode(themeMode));
|
|
2634
|
+
app.update((state) => ({
|
|
2635
|
+
...state,
|
|
2636
|
+
settings: {
|
|
2637
|
+
...state.settings,
|
|
2638
|
+
theme: preference,
|
|
2639
|
+
themeMode
|
|
2640
|
+
}
|
|
2641
|
+
}));
|
|
2642
|
+
};
|
|
2643
|
+
const reloadConfig = () => {
|
|
2644
|
+
void applyTheme(loadConfig(workspaceRoot).theme);
|
|
2645
|
+
};
|
|
1701
2646
|
bus = createCommandContext({
|
|
1702
2647
|
workspace,
|
|
1703
2648
|
getState: () => currentState,
|
|
@@ -1708,16 +2653,27 @@ async function main() {
|
|
|
1708
2653
|
});
|
|
1709
2654
|
},
|
|
1710
2655
|
quit: () => {
|
|
1711
|
-
void workspace.close().finally(() => {
|
|
1712
|
-
app?.stop()
|
|
2656
|
+
void workspace.close().finally(async () => {
|
|
2657
|
+
await app?.stop();
|
|
2658
|
+
await app?.dispose();
|
|
2659
|
+
process3.exit(0);
|
|
1713
2660
|
});
|
|
1714
2661
|
},
|
|
1715
2662
|
reloadKeybindings
|
|
1716
2663
|
});
|
|
1717
|
-
app = createNodeApp({ initialState: currentState });
|
|
2664
|
+
app = createNodeApp({ initialState: currentState, theme: themeForMode(initialThemeMode) });
|
|
1718
2665
|
reloadKeybindings();
|
|
1719
|
-
|
|
1720
|
-
|
|
2666
|
+
const executeCommand = (command) => {
|
|
2667
|
+
if (!bus || !app) {
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
if (command === "response.copy") {
|
|
2671
|
+
void handleCopy(app, currentState);
|
|
2672
|
+
}
|
|
2673
|
+
bus.execute(command);
|
|
2674
|
+
};
|
|
2675
|
+
const stopKeybindingWatch = watchKeybindings(workspaceRoot, reloadKeybindings);
|
|
2676
|
+
const stopConfigWatch = watchConfig(workspaceRoot, reloadConfig);
|
|
1721
2677
|
workspace.on("change", () => {
|
|
1722
2678
|
void bus.refreshWorkspace();
|
|
1723
2679
|
void refreshGitBranch();
|
|
@@ -1726,29 +2682,27 @@ async function main() {
|
|
|
1726
2682
|
(state) => renderApp(state, {
|
|
1727
2683
|
onEditorChange: (lines, cursor) => {
|
|
1728
2684
|
app?.update((prev) => {
|
|
1729
|
-
const
|
|
2685
|
+
const fileLines = stripEditorLines(lines);
|
|
2686
|
+
const sourceCursor = sourceCursorFromEditor(cursor);
|
|
1730
2687
|
return {
|
|
1731
2688
|
...prev,
|
|
1732
|
-
fileLines
|
|
1733
|
-
dirty: contentFromLines(
|
|
1734
|
-
activeRegion,
|
|
2689
|
+
fileLines,
|
|
2690
|
+
dirty: contentFromLines(fileLines) !== prev.fileContent,
|
|
1735
2691
|
ui: { ...prev.ui, focusPane: "editor" },
|
|
1736
|
-
editor: { ...prev.editor, cursor }
|
|
2692
|
+
editor: { ...prev.editor, cursor: sourceCursor }
|
|
1737
2693
|
};
|
|
1738
2694
|
});
|
|
1739
2695
|
},
|
|
1740
2696
|
onEditorSelection: (selection) => {
|
|
1741
2697
|
app?.update((prev) => {
|
|
1742
|
-
const
|
|
1743
|
-
const activeRegion = prev.parsedFile ? resolveRegionAtLine(prev.parsedFile.regions, cursorLine) : null;
|
|
2698
|
+
const sourceSelection = sourceSelectionFromEditor(selection);
|
|
1744
2699
|
return {
|
|
1745
2700
|
...prev,
|
|
1746
|
-
activeRegion,
|
|
1747
2701
|
ui: { ...prev.ui, focusPane: "editor" },
|
|
1748
2702
|
editor: {
|
|
1749
2703
|
...prev.editor,
|
|
1750
|
-
selection,
|
|
1751
|
-
cursor:
|
|
2704
|
+
selection: sourceSelection,
|
|
2705
|
+
cursor: sourceSelection?.active ?? prev.editor.cursor
|
|
1752
2706
|
}
|
|
1753
2707
|
};
|
|
1754
2708
|
});
|
|
@@ -1812,6 +2766,12 @@ async function main() {
|
|
|
1812
2766
|
responseEditor: { ...prev.responseEditor, cursor }
|
|
1813
2767
|
}));
|
|
1814
2768
|
},
|
|
2769
|
+
onResponseJsonFoldToggle: () => {
|
|
2770
|
+
bus?.execute("response.jsonFoldToggle");
|
|
2771
|
+
},
|
|
2772
|
+
onResponseJsonUnfoldAll: () => {
|
|
2773
|
+
bus?.execute("response.jsonUnfoldAll");
|
|
2774
|
+
},
|
|
1815
2775
|
onSplitChange: (sizes) => {
|
|
1816
2776
|
if (sizes.length === 3) {
|
|
1817
2777
|
app?.update((prev) => ({
|
|
@@ -1849,26 +2809,34 @@ async function main() {
|
|
|
1849
2809
|
bus?.execute("overlay.close");
|
|
1850
2810
|
},
|
|
1851
2811
|
onEnvSelect: (index) => {
|
|
1852
|
-
app?.update((prev) =>
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
2812
|
+
app?.update((prev) => {
|
|
2813
|
+
currentState = {
|
|
2814
|
+
...prev,
|
|
2815
|
+
ui: { ...prev.ui, envSelectedIndex: index }
|
|
2816
|
+
};
|
|
2817
|
+
return currentState;
|
|
2818
|
+
});
|
|
2819
|
+
bus?.execute("env.apply");
|
|
1856
2820
|
},
|
|
1857
2821
|
onResponseSearch: (query) => {
|
|
1858
2822
|
app?.update((prev) => ({
|
|
1859
2823
|
...prev,
|
|
1860
2824
|
editor: { ...prev.editor, searchQuery: query }
|
|
1861
2825
|
}));
|
|
1862
|
-
}
|
|
2826
|
+
},
|
|
2827
|
+
onCommand: executeCommand
|
|
1863
2828
|
})
|
|
1864
2829
|
);
|
|
1865
|
-
app.onEvent((event) =>
|
|
2830
|
+
app.onEvent((event) => {
|
|
2831
|
+
void handleUiEvent(event, app);
|
|
2832
|
+
});
|
|
1866
2833
|
const files = flattenFiles(tree).filter((node) => node.kind === "file");
|
|
1867
2834
|
if (files[0]) {
|
|
1868
2835
|
await bus.openFile(files[0].path);
|
|
1869
2836
|
}
|
|
1870
2837
|
await app.run();
|
|
1871
|
-
|
|
2838
|
+
stopKeybindingWatch();
|
|
2839
|
+
stopConfigWatch();
|
|
1872
2840
|
}
|
|
1873
2841
|
async function handleCopy(app, state) {
|
|
1874
2842
|
const result = state.request.result;
|
|
@@ -1887,7 +2855,38 @@ async function handleCopy(app, state) {
|
|
|
1887
2855
|
ui: { ...prev.ui, statusMessage: ok ? "Copied" : "Copy failed" }
|
|
1888
2856
|
}));
|
|
1889
2857
|
}
|
|
1890
|
-
function
|
|
2858
|
+
async function handlePaste(app) {
|
|
2859
|
+
const text = await readFromClipboard();
|
|
2860
|
+
app.update((prev) => {
|
|
2861
|
+
if (prev.ui.overlay !== "none" || prev.ui.focusPane !== "editor") {
|
|
2862
|
+
return prev;
|
|
2863
|
+
}
|
|
2864
|
+
if (!text) {
|
|
2865
|
+
return {
|
|
2866
|
+
...prev,
|
|
2867
|
+
ui: { ...prev.ui, statusMessage: text === "" ? "Clipboard empty" : "Paste failed" }
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
const next = pasteIntoEditor({
|
|
2871
|
+
lines: prev.fileLines,
|
|
2872
|
+
cursor: prev.editor.cursor,
|
|
2873
|
+
selection: prev.editor.selection,
|
|
2874
|
+
text
|
|
2875
|
+
});
|
|
2876
|
+
return {
|
|
2877
|
+
...prev,
|
|
2878
|
+
fileLines: next.lines,
|
|
2879
|
+
dirty: contentFromLines(next.lines) !== prev.fileContent,
|
|
2880
|
+
editor: {
|
|
2881
|
+
...prev.editor,
|
|
2882
|
+
cursor: next.cursor,
|
|
2883
|
+
selection: next.selection
|
|
2884
|
+
},
|
|
2885
|
+
ui: { ...prev.ui, focusPane: "editor", statusMessage: "Pasted" }
|
|
2886
|
+
};
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
async function handleUiEvent(event, app) {
|
|
1891
2890
|
if (event.kind === "engine" && event.event.kind === "resize") {
|
|
1892
2891
|
const resize = event.event;
|
|
1893
2892
|
app.update((prev) => ({
|
|
@@ -1902,10 +2901,46 @@ function handleUiEvent(event, app) {
|
|
|
1902
2901
|
}));
|
|
1903
2902
|
return;
|
|
1904
2903
|
}
|
|
2904
|
+
if (event.kind === "engine" && event.event.kind === "key" && event.event.action === "down" && (event.event.mods & ZR_MOD_CTRL) !== 0 && event.event.key === 86) {
|
|
2905
|
+
await handlePaste(app);
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
1905
2908
|
if (event.kind !== "engine" || event.event.kind !== "mouse") {
|
|
1906
2909
|
return;
|
|
1907
2910
|
}
|
|
1908
|
-
|
|
2911
|
+
if (handleWheelEvent(event.event, app)) {
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
const { x, y, mouseKind, mods } = event.event;
|
|
2915
|
+
if (mouseKind === 3) {
|
|
2916
|
+
const editorRect = app.measureElement("editor");
|
|
2917
|
+
if (editorRect && isPointInRect(x, y, editorRect)) {
|
|
2918
|
+
app.update((prev) => {
|
|
2919
|
+
const cursor = sourceCursorFromEditorPoint({
|
|
2920
|
+
x,
|
|
2921
|
+
y,
|
|
2922
|
+
rect: editorRect,
|
|
2923
|
+
lines: prev.fileLines,
|
|
2924
|
+
scrollTop: prev.editor.scrollTop,
|
|
2925
|
+
scrollLeft: prev.editor.scrollLeft
|
|
2926
|
+
});
|
|
2927
|
+
if (!cursor) {
|
|
2928
|
+
return prev;
|
|
2929
|
+
}
|
|
2930
|
+
const extendSelection = (mods & ZR_MOD_SHIFT) !== 0;
|
|
2931
|
+
return {
|
|
2932
|
+
...prev,
|
|
2933
|
+
ui: { ...prev.ui, focusPane: "editor" },
|
|
2934
|
+
editor: {
|
|
2935
|
+
...prev.editor,
|
|
2936
|
+
cursor,
|
|
2937
|
+
selection: extendSelection ? { anchor: prev.editor.cursor, active: cursor } : null
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
});
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
1909
2944
|
const panes = [
|
|
1910
2945
|
{ id: "pane-files", pane: "files" },
|
|
1911
2946
|
{ id: "pane-editor", pane: "editor" },
|
|
@@ -1916,7 +2951,7 @@ function handleUiEvent(event, app) {
|
|
|
1916
2951
|
if (!rect) {
|
|
1917
2952
|
continue;
|
|
1918
2953
|
}
|
|
1919
|
-
if (x
|
|
2954
|
+
if (isPointInRect(x, y, rect)) {
|
|
1920
2955
|
app.update((prev) => ({
|
|
1921
2956
|
...prev,
|
|
1922
2957
|
ui: { ...prev.ui, focusPane: entry.pane }
|
|
@@ -1927,7 +2962,7 @@ function handleUiEvent(event, app) {
|
|
|
1927
2962
|
}
|
|
1928
2963
|
void main().catch((error) => {
|
|
1929
2964
|
console.error(error);
|
|
1930
|
-
|
|
2965
|
+
process3.exit(1);
|
|
1931
2966
|
});
|
|
1932
2967
|
export {
|
|
1933
2968
|
focusPaneId
|