@churivibhav/reqex 0.1.1 → 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 +1321 -287
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,93 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
#!/usr/bin/env node
|
|
3
2
|
|
|
4
3
|
// src/cli.ts
|
|
5
|
-
import
|
|
6
|
-
import
|
|
4
|
+
import path7 from "path";
|
|
5
|
+
import process3 from "process";
|
|
6
|
+
import { ZR_MOD_CTRL, ZR_MOD_SHIFT } from "@rezi-ui/core/keybindings";
|
|
7
7
|
import { createNodeApp } from "@rezi-ui/node";
|
|
8
8
|
|
|
9
|
-
// src/config/
|
|
9
|
+
// src/config/config.ts
|
|
10
10
|
import fs from "fs";
|
|
11
|
+
import path2 from "path";
|
|
12
|
+
|
|
13
|
+
// src/config/paths.ts
|
|
11
14
|
import os from "os";
|
|
12
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";
|
|
13
91
|
var VSCODE_DEFAULTS = {
|
|
14
92
|
F5: "request.send",
|
|
15
93
|
"ctrl+enter": "request.send",
|
|
@@ -31,13 +109,14 @@ var VSCODE_DEFAULTS = {
|
|
|
31
109
|
F11: "pane.zoom",
|
|
32
110
|
z: "pane.zoom",
|
|
33
111
|
F1: "help.show",
|
|
34
|
-
"?": "help.show",
|
|
35
112
|
"ctrl+/": "keybindings.show",
|
|
36
113
|
escape: "overlay.close",
|
|
37
114
|
"ctrl+q": "app.quit",
|
|
38
115
|
"ctrl+x": "request.cancel",
|
|
39
116
|
"ctrl+shift+c": "response.copy",
|
|
40
117
|
"ctrl+f": "response.search",
|
|
118
|
+
"ctrl+[": "response.jsonFoldToggle",
|
|
119
|
+
"ctrl+]": "response.jsonUnfoldAll",
|
|
41
120
|
"ctrl+tab": "response.tab.next",
|
|
42
121
|
"ctrl+shift+tab": "response.tab.prev"
|
|
43
122
|
};
|
|
@@ -72,6 +151,8 @@ var COMMAND_LABELS = {
|
|
|
72
151
|
"response.tab.prev": "Previous response tab",
|
|
73
152
|
"response.copy": "Copy response",
|
|
74
153
|
"response.search": "Search response",
|
|
154
|
+
"response.jsonFoldToggle": "Fold/unfold JSON node",
|
|
155
|
+
"response.jsonUnfoldAll": "Unfold all JSON",
|
|
75
156
|
"editor.searchNext": "Find next in editor"
|
|
76
157
|
};
|
|
77
158
|
var CHORD_PART_LABELS = {
|
|
@@ -117,38 +198,21 @@ function buildKeybindingsViewLines(bindings, maxLines) {
|
|
|
117
198
|
}
|
|
118
199
|
return rows;
|
|
119
200
|
}
|
|
120
|
-
function
|
|
121
|
-
if (process.env.REQEX_CONFIG_DIR) {
|
|
122
|
-
return process.env.REQEX_CONFIG_DIR;
|
|
123
|
-
}
|
|
124
|
-
if (process.platform === "win32") {
|
|
125
|
-
const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
126
|
-
return path.join(appData, "reqex");
|
|
127
|
-
}
|
|
128
|
-
if (process.platform === "darwin") {
|
|
129
|
-
return path.join(os.homedir(), "Library", "Application Support", "reqex");
|
|
130
|
-
}
|
|
131
|
-
const xdg = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
|
|
132
|
-
return path.join(xdg, "reqex");
|
|
133
|
-
}
|
|
134
|
-
function getProjectConfigDir(workspaceRoot) {
|
|
135
|
-
return path.join(workspaceRoot, ".reqex");
|
|
136
|
-
}
|
|
137
|
-
function readJsonIfExists(filePath) {
|
|
201
|
+
function readJsonIfExists2(filePath) {
|
|
138
202
|
try {
|
|
139
|
-
if (!
|
|
203
|
+
if (!fs2.existsSync(filePath)) {
|
|
140
204
|
return null;
|
|
141
205
|
}
|
|
142
|
-
const raw =
|
|
206
|
+
const raw = fs2.readFileSync(filePath, "utf8");
|
|
143
207
|
return JSON.parse(raw);
|
|
144
208
|
} catch {
|
|
145
209
|
return null;
|
|
146
210
|
}
|
|
147
211
|
}
|
|
148
212
|
function loadKeybindings(workspaceRoot) {
|
|
149
|
-
const userConfig =
|
|
150
|
-
const projectConfig =
|
|
151
|
-
|
|
213
|
+
const userConfig = readJsonIfExists2(path3.join(getConfigDir(), "keybindings.json"));
|
|
214
|
+
const projectConfig = readJsonIfExists2(
|
|
215
|
+
path3.join(getProjectConfigDir(workspaceRoot), "keybindings.json")
|
|
152
216
|
);
|
|
153
217
|
const preset = projectConfig?.preset ?? userConfig?.preset ?? "vscode";
|
|
154
218
|
const defaults = preset === "vim" ? VIM_DEFAULTS : VSCODE_DEFAULTS;
|
|
@@ -165,10 +229,10 @@ function watchKeybindings(workspaceRoot, onChange) {
|
|
|
165
229
|
const dirs = [getConfigDir(), getProjectConfigDir(workspaceRoot)];
|
|
166
230
|
const watchers = dirs.map((dir) => {
|
|
167
231
|
try {
|
|
168
|
-
|
|
232
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
169
233
|
} catch {
|
|
170
234
|
}
|
|
171
|
-
return
|
|
235
|
+
return fs2.watch(dir, { persistent: false }, () => onChange());
|
|
172
236
|
});
|
|
173
237
|
return () => {
|
|
174
238
|
for (const watcher of watchers) {
|
|
@@ -176,34 +240,166 @@ function watchKeybindings(workspaceRoot, onChange) {
|
|
|
176
240
|
}
|
|
177
241
|
};
|
|
178
242
|
}
|
|
179
|
-
var
|
|
180
|
-
editor: ["
|
|
181
|
-
response: ["
|
|
182
|
-
files: ["
|
|
183
|
-
overlay: ["
|
|
184
|
-
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"
|
|
278
|
+
};
|
|
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": []
|
|
185
307
|
};
|
|
186
|
-
function
|
|
187
|
-
|
|
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;
|
|
188
330
|
if (context.overlay !== "none") {
|
|
189
|
-
|
|
331
|
+
contextual = FOOTER_COMMANDS.overlay;
|
|
190
332
|
} else if (context.sending) {
|
|
191
|
-
|
|
333
|
+
contextual = FOOTER_COMMANDS.sending;
|
|
192
334
|
} else {
|
|
193
|
-
|
|
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
|
+
);
|
|
194
347
|
}
|
|
348
|
+
if (!context.hasFoldedJson) {
|
|
349
|
+
return commands.filter((command) => command !== "response.jsonUnfoldAll");
|
|
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
|
+
});
|
|
195
374
|
const leftBudget = 48;
|
|
196
375
|
const maxWidth = Math.max(20, context.viewportWidth - leftBudget);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const help = pinned.find((item) => item.command === "help.show");
|
|
392
|
+
if (help && selected[selected.length - 1]?.command !== "help.show") {
|
|
393
|
+
append(help);
|
|
200
394
|
}
|
|
201
|
-
return
|
|
395
|
+
return selected.filter(Boolean);
|
|
202
396
|
}
|
|
203
397
|
var HELP_HINT_LINES = [
|
|
204
398
|
"F5 Send request under cursor",
|
|
205
399
|
"Tab / Shift+Tab Cycle panes",
|
|
206
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",
|
|
207
403
|
"Ctrl+E Environment switcher",
|
|
208
404
|
"F2 / Ctrl+Shift+P Command palette",
|
|
209
405
|
"Ctrl+Shift+C Copy response tab",
|
|
@@ -271,33 +467,91 @@ function resolveRegionAtLine(regions, line) {
|
|
|
271
467
|
return best;
|
|
272
468
|
});
|
|
273
469
|
}
|
|
470
|
+
function resolveActiveRegion(parsedFile, cursorLine) {
|
|
471
|
+
if (!parsedFile) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
return resolveRegionAtLine(parsedFile.regions, cursorLine);
|
|
475
|
+
}
|
|
274
476
|
var HTTP_METHOD_PREFIX = /^\s*(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|CONNECT|TRACE|GRAPHQL)\b/u;
|
|
275
|
-
function
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
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;
|
|
284
485
|
}
|
|
285
|
-
const isActive = region.id === activeRegionId;
|
|
286
|
-
const line = fileLines[region.startLine] ?? "";
|
|
287
|
-
const markerCol = markerColumnAfterMethod(line);
|
|
288
|
-
markers.push({
|
|
289
|
-
line: region.startLine,
|
|
290
|
-
startColumn: markerCol,
|
|
291
|
-
endColumn: markerCol + 1,
|
|
292
|
-
severity: isActive ? "hint" : "info",
|
|
293
|
-
message: `${region.method ?? "REQ"} ${region.name}`
|
|
294
|
-
});
|
|
295
486
|
}
|
|
296
|
-
return
|
|
487
|
+
return firstRegion.startLine;
|
|
297
488
|
}
|
|
298
489
|
|
|
299
490
|
// src/engine/store.ts
|
|
300
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
|
|
301
555
|
var store = new httpyacStoreModule.HttpFileStore();
|
|
302
556
|
var versions = /* @__PURE__ */ new Map();
|
|
303
557
|
function toRegion(region) {
|
|
@@ -322,7 +576,9 @@ function getParseVersion(filePath) {
|
|
|
322
576
|
}
|
|
323
577
|
async function parseFile(filePath, getText, workingDir, version) {
|
|
324
578
|
const parseVersion = version ?? getParseVersion(filePath);
|
|
579
|
+
const config = await loadEnvironmentConfig(filePath, workingDir);
|
|
325
580
|
const httpFile = await store.getOrCreate(filePath, getText, parseVersion, {
|
|
581
|
+
config,
|
|
326
582
|
workingDir
|
|
327
583
|
});
|
|
328
584
|
const regions = httpFile.httpRegions.map(toRegion);
|
|
@@ -404,11 +660,13 @@ async function sendRegion(options) {
|
|
|
404
660
|
const logResponse = async (response) => {
|
|
405
661
|
capturedResponse = response;
|
|
406
662
|
};
|
|
663
|
+
const config = await loadEnvironmentConfig(options.filePath, options.workingDir);
|
|
407
664
|
try {
|
|
408
665
|
await send({
|
|
409
666
|
httpFile,
|
|
410
667
|
httpRegion,
|
|
411
668
|
activeEnvironment: options.activeEnvironment,
|
|
669
|
+
config,
|
|
412
670
|
variables: options.variables,
|
|
413
671
|
logResponse
|
|
414
672
|
});
|
|
@@ -433,26 +691,28 @@ async function sendRegion(options) {
|
|
|
433
691
|
};
|
|
434
692
|
}
|
|
435
693
|
}
|
|
436
|
-
async function listEnvironments(filePath) {
|
|
694
|
+
async function listEnvironments(filePath, workingDir) {
|
|
437
695
|
const httpFile = getHttpFile(filePath);
|
|
438
696
|
if (!httpFile) {
|
|
439
697
|
return [];
|
|
440
698
|
}
|
|
441
|
-
|
|
699
|
+
const config = await loadEnvironmentConfig(filePath, workingDir);
|
|
700
|
+
return getEnvironments({ httpFile, config });
|
|
442
701
|
}
|
|
443
|
-
async function listVariables(filePath, activeEnvironment) {
|
|
702
|
+
async function listVariables(filePath, workingDir, activeEnvironment) {
|
|
444
703
|
const httpFile = getHttpFile(filePath);
|
|
445
704
|
if (!httpFile) {
|
|
446
705
|
return {};
|
|
447
706
|
}
|
|
448
|
-
|
|
707
|
+
const config = await loadEnvironmentConfig(filePath, workingDir);
|
|
708
|
+
return getVariables({ httpFile, activeEnvironment, config });
|
|
449
709
|
}
|
|
450
710
|
|
|
451
711
|
// src/keymap/dispatcher.ts
|
|
452
712
|
function buildBindingMap(bindings, execute) {
|
|
453
713
|
const map = {};
|
|
454
714
|
for (const [key, command] of Object.entries(bindings)) {
|
|
455
|
-
map[key] = () => execute(command);
|
|
715
|
+
map[key] = (ctx) => execute(command, ctx);
|
|
456
716
|
}
|
|
457
717
|
return map;
|
|
458
718
|
}
|
|
@@ -465,6 +725,8 @@ function commandFromPaletteId(id) {
|
|
|
465
725
|
"palette.commands": "palette.commands",
|
|
466
726
|
"help.show": "help.show",
|
|
467
727
|
"keybindings.show": "keybindings.show",
|
|
728
|
+
"response.jsonFoldToggle": "response.jsonFoldToggle",
|
|
729
|
+
"response.jsonUnfoldAll": "response.jsonUnfoldAll",
|
|
468
730
|
"pane.zoom": "pane.zoom",
|
|
469
731
|
"sidebar.toggle": "sidebar.toggle"
|
|
470
732
|
};
|
|
@@ -476,6 +738,8 @@ var COMMAND_ITEMS = [
|
|
|
476
738
|
{ id: "file.save", label: "Save File", description: "Write editor to disk", shortcut: "Ctrl+S" },
|
|
477
739
|
{ id: "env.switcher", label: "Switch Environment", description: "Choose active environment", shortcut: "Ctrl+E" },
|
|
478
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+]" },
|
|
479
743
|
{ id: "pane.zoom", label: "Zoom Pane", description: "Zoom focused pane", shortcut: "F11" },
|
|
480
744
|
{ id: "help.show", label: "Help", description: "Show quick help", shortcut: "F1" },
|
|
481
745
|
{ id: "keybindings.show", label: "Keybindings", description: "Show all keybindings", shortcut: "Ctrl+/" }
|
|
@@ -488,7 +752,7 @@ import { EventEmitter } from "events";
|
|
|
488
752
|
|
|
489
753
|
// src/workspace/discovery.ts
|
|
490
754
|
import { readdir, stat } from "fs/promises";
|
|
491
|
-
import
|
|
755
|
+
import path5 from "path";
|
|
492
756
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
493
757
|
".git",
|
|
494
758
|
"node_modules",
|
|
@@ -503,7 +767,7 @@ var FILE_KINDS = [
|
|
|
503
767
|
{ ext: ".env.json", kind: "env-json" }
|
|
504
768
|
];
|
|
505
769
|
function classifyFile(filePath) {
|
|
506
|
-
const base =
|
|
770
|
+
const base = path5.basename(filePath);
|
|
507
771
|
for (const { ext, kind } of FILE_KINDS) {
|
|
508
772
|
if (base === ext || base.endsWith(ext)) {
|
|
509
773
|
return kind;
|
|
@@ -532,7 +796,7 @@ async function discoverDirectory(rootDir, currentDir) {
|
|
|
532
796
|
continue;
|
|
533
797
|
}
|
|
534
798
|
}
|
|
535
|
-
const fullPath =
|
|
799
|
+
const fullPath = path5.join(currentDir, entry);
|
|
536
800
|
let entryStat;
|
|
537
801
|
try {
|
|
538
802
|
entryStat = await stat(fullPath);
|
|
@@ -623,17 +887,17 @@ var Workspace = class extends EventEmitter {
|
|
|
623
887
|
],
|
|
624
888
|
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 }
|
|
625
889
|
});
|
|
626
|
-
const handle = async (type,
|
|
890
|
+
const handle = async (type, path8) => {
|
|
627
891
|
try {
|
|
628
892
|
this.tree = await discoverFileTree(this.rootDir);
|
|
629
|
-
this.emitChange({ type, path:
|
|
893
|
+
this.emitChange({ type, path: path8 });
|
|
630
894
|
} catch (error) {
|
|
631
895
|
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
632
896
|
}
|
|
633
897
|
};
|
|
634
|
-
this.watcher.on("add", (
|
|
635
|
-
this.watcher.on("change", (
|
|
636
|
-
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));
|
|
637
901
|
this.watcher.on("error", (error) => {
|
|
638
902
|
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
639
903
|
});
|
|
@@ -663,12 +927,12 @@ function createInitialState(workspaceRoot) {
|
|
|
663
927
|
dirty: false,
|
|
664
928
|
parseVersion: 0,
|
|
665
929
|
parsedFile: null,
|
|
666
|
-
activeRegion: null,
|
|
667
930
|
responseEditor: {
|
|
668
931
|
scrollTop: 0,
|
|
669
932
|
scrollLeft: 0,
|
|
670
933
|
cursor: { line: 0, column: 0 },
|
|
671
|
-
selection: null
|
|
934
|
+
selection: null,
|
|
935
|
+
foldedJsonPaths: []
|
|
672
936
|
},
|
|
673
937
|
resultGeneration: 0,
|
|
674
938
|
editor: {
|
|
@@ -704,7 +968,9 @@ function createInitialState(workspaceRoot) {
|
|
|
704
968
|
},
|
|
705
969
|
settings: {
|
|
706
970
|
keymapPreset: "vscode",
|
|
707
|
-
keybindings: {}
|
|
971
|
+
keybindings: {},
|
|
972
|
+
theme: "auto",
|
|
973
|
+
themeMode: "dark"
|
|
708
974
|
}
|
|
709
975
|
};
|
|
710
976
|
}
|
|
@@ -761,7 +1027,196 @@ function createSendController() {
|
|
|
761
1027
|
};
|
|
762
1028
|
}
|
|
763
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
|
+
|
|
764
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
|
+
}
|
|
765
1220
|
function createCommandContext(deps) {
|
|
766
1221
|
setPromptHandler(async (request) => {
|
|
767
1222
|
return new Promise((resolve) => {
|
|
@@ -792,27 +1247,28 @@ function createCommandContext(deps) {
|
|
|
792
1247
|
ui: { ...state.ui, pendingPrompt: null }
|
|
793
1248
|
}));
|
|
794
1249
|
};
|
|
795
|
-
const openFile = async (
|
|
796
|
-
const content = await deps.workspace.readFile(
|
|
1250
|
+
const openFile = async (path8) => {
|
|
1251
|
+
const content = await deps.workspace.readFile(path8);
|
|
797
1252
|
const lines = linesFromContent(content);
|
|
798
|
-
const parsed = await parseFile(
|
|
799
|
-
const environments = await listEnvironments(
|
|
800
|
-
const variables = await listVariables(
|
|
801
|
-
|
|
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;
|
|
802
1259
|
deps.update((state) => ({
|
|
803
1260
|
...state,
|
|
804
|
-
selectedFilePath:
|
|
1261
|
+
selectedFilePath: path8,
|
|
805
1262
|
fileContent: content,
|
|
806
1263
|
fileLines: lines,
|
|
807
1264
|
dirty: false,
|
|
808
1265
|
parseVersion: parsed.version,
|
|
809
1266
|
parsedFile: parsed,
|
|
810
|
-
activeRegion,
|
|
811
1267
|
editor: {
|
|
812
1268
|
...state.editor,
|
|
813
|
-
cursor: { line:
|
|
1269
|
+
cursor: { line: initialLine, column: 0 },
|
|
814
1270
|
selection: null,
|
|
815
|
-
scrollTop:
|
|
1271
|
+
scrollTop: initialLine
|
|
816
1272
|
},
|
|
817
1273
|
request: {
|
|
818
1274
|
...state.request,
|
|
@@ -846,22 +1302,37 @@ function createCommandContext(deps) {
|
|
|
846
1302
|
deps.workspace.rootDir,
|
|
847
1303
|
version
|
|
848
1304
|
);
|
|
849
|
-
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);
|
|
850
1305
|
deps.update((s) => ({
|
|
851
1306
|
...s,
|
|
852
1307
|
fileContent: content,
|
|
853
1308
|
dirty: false,
|
|
854
1309
|
parsedFile: parsed,
|
|
855
|
-
activeRegion: activeRegion ?? null,
|
|
856
1310
|
ui: { ...s.ui, statusMessage: "Saved" }
|
|
857
1311
|
}));
|
|
858
1312
|
};
|
|
859
1313
|
const runSend = async () => {
|
|
860
1314
|
const state = deps.getState();
|
|
861
|
-
if (!state.selectedFilePath
|
|
1315
|
+
if (!state.selectedFilePath) {
|
|
862
1316
|
return;
|
|
863
1317
|
}
|
|
864
|
-
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);
|
|
865
1336
|
if (!region) {
|
|
866
1337
|
deps.update((s) => ({
|
|
867
1338
|
...s,
|
|
@@ -873,13 +1344,13 @@ function createCommandContext(deps) {
|
|
|
873
1344
|
const gen = sendController.beginSend();
|
|
874
1345
|
deps.update((s) => ({
|
|
875
1346
|
...s,
|
|
876
|
-
activeRegion: region,
|
|
877
1347
|
request: { ...s.request, sending: true, error: null, result: null },
|
|
878
1348
|
responseEditor: {
|
|
879
1349
|
scrollTop: 0,
|
|
880
1350
|
scrollLeft: 0,
|
|
881
1351
|
cursor: { line: 0, column: 0 },
|
|
882
|
-
selection: null
|
|
1352
|
+
selection: null,
|
|
1353
|
+
foldedJsonPaths: []
|
|
883
1354
|
},
|
|
884
1355
|
resultGeneration: s.resultGeneration + 1,
|
|
885
1356
|
ui: { ...s.ui, focusPane: "response", responseTab: "pretty" }
|
|
@@ -895,9 +1366,11 @@ function createCommandContext(deps) {
|
|
|
895
1366
|
if (!sendController.isCurrent(gen)) {
|
|
896
1367
|
return;
|
|
897
1368
|
}
|
|
898
|
-
const variables = await listVariables(
|
|
899
|
-
|
|
900
|
-
|
|
1369
|
+
const variables = await listVariables(
|
|
1370
|
+
state.selectedFilePath,
|
|
1371
|
+
deps.workspace.rootDir,
|
|
1372
|
+
[...deps.getState().request.activeEnvironment]
|
|
1373
|
+
);
|
|
901
1374
|
if (!sendController.isCurrent(gen)) {
|
|
902
1375
|
return;
|
|
903
1376
|
}
|
|
@@ -970,9 +1443,9 @@ function createCommandContext(deps) {
|
|
|
970
1443
|
ui: {
|
|
971
1444
|
...s.ui,
|
|
972
1445
|
overlay: s.ui.overlay === "env" ? "none" : "env",
|
|
973
|
-
envSelectedIndex:
|
|
974
|
-
|
|
975
|
-
s.request.
|
|
1446
|
+
envSelectedIndex: envSelectedIndexFromActive(
|
|
1447
|
+
s.request.environments,
|
|
1448
|
+
s.request.activeEnvironment
|
|
976
1449
|
)
|
|
977
1450
|
}
|
|
978
1451
|
}));
|
|
@@ -1003,10 +1476,13 @@ function createCommandContext(deps) {
|
|
|
1003
1476
|
}
|
|
1004
1477
|
break;
|
|
1005
1478
|
case "env.apply": {
|
|
1006
|
-
const envName =
|
|
1479
|
+
const envName = envNameFromSelectedIndex(
|
|
1480
|
+
state.request.environments,
|
|
1481
|
+
state.ui.envSelectedIndex
|
|
1482
|
+
);
|
|
1007
1483
|
void (async () => {
|
|
1008
1484
|
const activeEnvironment = envName ? [envName] : [];
|
|
1009
|
-
const variables = state.selectedFilePath ? await listVariables(state.selectedFilePath, activeEnvironment) : {};
|
|
1485
|
+
const variables = state.selectedFilePath ? await listVariables(state.selectedFilePath, deps.workspace.rootDir, activeEnvironment) : {};
|
|
1010
1486
|
deps.update((s) => ({
|
|
1011
1487
|
...s,
|
|
1012
1488
|
request: { ...s.request, activeEnvironment, variables },
|
|
@@ -1096,6 +1572,46 @@ function createCommandContext(deps) {
|
|
|
1096
1572
|
ui: { ...s.ui, focusPane: "response", responseTab: "pretty" }
|
|
1097
1573
|
}));
|
|
1098
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;
|
|
1099
1615
|
case "editor.searchNext":
|
|
1100
1616
|
break;
|
|
1101
1617
|
default:
|
|
@@ -1118,103 +1634,240 @@ function createCommandContext(deps) {
|
|
|
1118
1634
|
return context;
|
|
1119
1635
|
}
|
|
1120
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
|
+
|
|
1121
1755
|
// src/ui/app-view.ts
|
|
1122
|
-
import
|
|
1756
|
+
import path6 from "path";
|
|
1123
1757
|
import {
|
|
1124
|
-
rgb,
|
|
1125
1758
|
ui
|
|
1126
1759
|
} from "@rezi-ui/core";
|
|
1127
1760
|
|
|
1128
|
-
// src/
|
|
1129
|
-
var
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
"
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
}
|
|
1153
|
-
return tokens;
|
|
1154
|
-
}
|
|
1155
|
-
if (/^\s*#/u.test(line)) {
|
|
1156
|
-
return [{ text: line, kind: "comment" }];
|
|
1157
|
-
}
|
|
1158
|
-
if (/^\s*\/\//u.test(line)) {
|
|
1159
|
-
return [{ text: line, kind: "comment" }];
|
|
1160
|
-
}
|
|
1161
|
-
if (/^\s*@/u.test(line)) {
|
|
1162
|
-
return [{ text: line, kind: "type" }];
|
|
1163
|
-
}
|
|
1164
|
-
const headerMatch = /^\s*([!#$%&'*+\-.^_`|~0-9A-Za-z]+)(\s*:\s*)(.*)$/u.exec(line);
|
|
1165
|
-
if (headerMatch) {
|
|
1166
|
-
tokens.push({ text: headerMatch[1] ?? "", kind: "function" });
|
|
1167
|
-
tokens.push({ text: headerMatch[2] ?? "", kind: "operator" });
|
|
1168
|
-
tokens.push({ text: headerMatch[3] ?? "", kind: "string" });
|
|
1169
|
-
return tokens;
|
|
1170
|
-
}
|
|
1171
|
-
const words = line.split(/(\s+)/u);
|
|
1172
|
-
for (const word of words) {
|
|
1173
|
-
if (KEYWORDS.has(word)) {
|
|
1174
|
-
tokens.push({ text: word, kind: "keyword" });
|
|
1175
|
-
} else if (word.length > 0) {
|
|
1176
|
-
tokens.push({ text: word, kind: "plain" });
|
|
1177
|
-
}
|
|
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";
|
|
1178
1785
|
}
|
|
1179
|
-
return tokens.length > 0 ? tokens : [{ text: line, kind: "plain" }];
|
|
1180
1786
|
}
|
|
1181
|
-
function
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
return
|
|
1185
|
-
}
|
|
1186
|
-
try {
|
|
1187
|
-
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
1188
|
-
} catch {
|
|
1189
|
-
return text;
|
|
1190
|
-
}
|
|
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
|
+
});
|
|
1191
1792
|
}
|
|
1192
|
-
function
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
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;
|
|
1198
1812
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
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;
|
|
1201
1825
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1826
|
+
return {
|
|
1827
|
+
anchor: editorCursorFromSource(selection.anchor),
|
|
1828
|
+
active: editorCursorFromSource(selection.active)
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
function sourceSelectionFromEditor(selection) {
|
|
1832
|
+
if (!selection) {
|
|
1833
|
+
return null;
|
|
1204
1834
|
}
|
|
1205
|
-
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
|
+
};
|
|
1206
1856
|
}
|
|
1207
1857
|
|
|
1208
1858
|
// src/ui/app-view.ts
|
|
1209
|
-
function paneStyle(focused) {
|
|
1210
|
-
return
|
|
1859
|
+
function paneStyle(colors, focused) {
|
|
1860
|
+
return {
|
|
1861
|
+
bg: colors.bgElevated,
|
|
1862
|
+
...focused ? { fg: colors.paneFocused, bold: true } : { fg: colors.paneMuted }
|
|
1863
|
+
};
|
|
1211
1864
|
}
|
|
1212
|
-
function renderFileTree(state, deps) {
|
|
1865
|
+
function renderFileTree(state, deps, colors) {
|
|
1213
1866
|
return ui.panel(
|
|
1214
1867
|
{
|
|
1215
1868
|
id: "pane-files",
|
|
1216
1869
|
title: state.ui.focusPane === "files" ? "\u25CF Files" : "Files",
|
|
1217
|
-
style: paneStyle(state.ui.focusPane === "files")
|
|
1870
|
+
style: paneStyle(colors, state.ui.focusPane === "files")
|
|
1218
1871
|
},
|
|
1219
1872
|
[
|
|
1220
1873
|
ui.tree({
|
|
@@ -1229,7 +1882,7 @@ function renderFileTree(state, deps) {
|
|
|
1229
1882
|
onPress: (node) => deps.onTreePress(node),
|
|
1230
1883
|
renderNode: (node, _depth, nodeState) => ui.row({ gap: 1 }, [
|
|
1231
1884
|
ui.text(nodeState.selected ? `\u25B8 ${node.name}` : node.name, {
|
|
1232
|
-
style: nodeState.selected ? { fg:
|
|
1885
|
+
style: nodeState.selected ? { fg: colors.selected, bold: true } : state.dirty && node.path === state.selectedFilePath ? { fg: colors.dirty } : void 0
|
|
1233
1886
|
})
|
|
1234
1887
|
]),
|
|
1235
1888
|
flex: 1,
|
|
@@ -1238,38 +1891,32 @@ function renderFileTree(state, deps) {
|
|
|
1238
1891
|
]
|
|
1239
1892
|
);
|
|
1240
1893
|
}
|
|
1241
|
-
function renderEditor(state, deps, readOnly) {
|
|
1242
|
-
const activeRegion =
|
|
1894
|
+
function renderEditor(state, deps, readOnly, colors) {
|
|
1895
|
+
const activeRegion = resolveActiveRegion(state.parsedFile, state.editor.cursor.line);
|
|
1243
1896
|
const titleParts = [
|
|
1244
1897
|
state.ui.focusPane === "editor" ? "\u25CF Editor" : "Editor",
|
|
1245
1898
|
state.selectedFilePath ? state.selectedFilePath.split("/").pop() : "No file",
|
|
1246
1899
|
state.dirty ? " \u25CF" : "",
|
|
1247
1900
|
activeRegion ? ` | ${activeRegion.method ?? "?"} ${activeRegion.name}` : ""
|
|
1248
1901
|
];
|
|
1249
|
-
const diagnostics = state.parsedFile ? buildRegionDiagnostics(
|
|
1250
|
-
state.parsedFile.regions,
|
|
1251
|
-
activeRegion?.id ?? null,
|
|
1252
|
-
state.fileLines
|
|
1253
|
-
) : [];
|
|
1254
1902
|
return ui.panel(
|
|
1255
1903
|
{
|
|
1256
1904
|
id: "pane-editor",
|
|
1257
1905
|
title: titleParts.join(""),
|
|
1258
|
-
style: paneStyle(state.ui.focusPane === "editor")
|
|
1906
|
+
style: paneStyle(colors, state.ui.focusPane === "editor")
|
|
1259
1907
|
},
|
|
1260
1908
|
[
|
|
1261
1909
|
ui.codeEditor({
|
|
1262
1910
|
id: "editor",
|
|
1263
|
-
lines: state.fileLines,
|
|
1264
|
-
cursor: state.editor.cursor,
|
|
1265
|
-
selection: state.editor.selection,
|
|
1911
|
+
lines: prefixEditorLines(state.fileLines, activeRegion),
|
|
1912
|
+
cursor: editorCursorFromSource(state.editor.cursor),
|
|
1913
|
+
selection: editorSelectionFromSource(state.editor.selection),
|
|
1266
1914
|
scrollTop: state.editor.scrollTop,
|
|
1267
1915
|
scrollLeft: state.editor.scrollLeft,
|
|
1268
1916
|
readOnly,
|
|
1269
1917
|
lineNumbers: true,
|
|
1270
1918
|
syntaxLanguage: "plain",
|
|
1271
|
-
tokenizeLine:
|
|
1272
|
-
diagnostics,
|
|
1919
|
+
tokenizeLine: createRegionAwareTokenizer(activeRegion),
|
|
1273
1920
|
onChange: (lines, cursor) => deps.onEditorChange(lines, cursor),
|
|
1274
1921
|
onSelectionChange: deps.onEditorSelection,
|
|
1275
1922
|
onScroll: deps.onEditorScroll,
|
|
@@ -1296,7 +1943,7 @@ function responseCursorProps(state, deps) {
|
|
|
1296
1943
|
onSelectionChange: deps.onResponseSelection
|
|
1297
1944
|
};
|
|
1298
1945
|
}
|
|
1299
|
-
function renderResponseBody(state, deps) {
|
|
1946
|
+
function renderResponseBody(state, deps, colors) {
|
|
1300
1947
|
const result = state.request.result;
|
|
1301
1948
|
const scroll = responseScrollProps(state, deps);
|
|
1302
1949
|
const cursor = responseCursorProps(state, deps);
|
|
@@ -1339,7 +1986,7 @@ function renderResponseBody(state, deps) {
|
|
|
1339
1986
|
case "variables":
|
|
1340
1987
|
return ui.codeEditor({
|
|
1341
1988
|
id: `response-vars-${gen}`,
|
|
1342
|
-
lines:
|
|
1989
|
+
lines: JSON.stringify(state.request.variables, null, 2).split("\n"),
|
|
1343
1990
|
readOnly: true,
|
|
1344
1991
|
syntaxLanguage: "json",
|
|
1345
1992
|
...cursor,
|
|
@@ -1351,16 +1998,20 @@ function renderResponseBody(state, deps) {
|
|
|
1351
1998
|
...result.testResults.map(
|
|
1352
1999
|
(test) => ui.text(`${test.status === "SUCCESS" ? "\u2713" : "\u2717"} ${test.message}`, {
|
|
1353
2000
|
style: {
|
|
1354
|
-
fg: test.status === "SUCCESS" ?
|
|
2001
|
+
fg: test.status === "SUCCESS" ? colors.success : test.status === "SKIPPED" ? colors.warning : colors.error
|
|
1355
2002
|
}
|
|
1356
2003
|
})
|
|
1357
2004
|
)
|
|
1358
2005
|
]);
|
|
1359
2006
|
case "pretty":
|
|
1360
|
-
default:
|
|
2007
|
+
default: {
|
|
2008
|
+
const prettyView = buildFoldableJsonView(
|
|
2009
|
+
prettyJsonIfPossible(result.prettyBody || result.body),
|
|
2010
|
+
state.responseEditor.foldedJsonPaths
|
|
2011
|
+
);
|
|
1361
2012
|
return ui.codeEditor({
|
|
1362
2013
|
id: `response-pretty-${gen}`,
|
|
1363
|
-
lines:
|
|
2014
|
+
lines: prettyView.lines,
|
|
1364
2015
|
readOnly: true,
|
|
1365
2016
|
lineNumbers: true,
|
|
1366
2017
|
syntaxLanguage: "json",
|
|
@@ -1369,9 +2020,10 @@ function renderResponseBody(state, deps) {
|
|
|
1369
2020
|
...scroll,
|
|
1370
2021
|
flex: 1
|
|
1371
2022
|
});
|
|
2023
|
+
}
|
|
1372
2024
|
}
|
|
1373
2025
|
}
|
|
1374
|
-
function renderResponse(state, deps) {
|
|
2026
|
+
function renderResponse(state, deps, colors) {
|
|
1375
2027
|
const result = state.request.result;
|
|
1376
2028
|
const statusLine = state.request.sending || !result ? null : `${result.protocol ?? "HTTP"} ${result.statusCode ?? "?"} ${result.statusMessage ?? ""} \xB7 ${result.durationMs ?? "?"} ms`;
|
|
1377
2029
|
const tabItems = [
|
|
@@ -1385,12 +2037,15 @@ function renderResponse(state, deps) {
|
|
|
1385
2037
|
{
|
|
1386
2038
|
id: "pane-response",
|
|
1387
2039
|
title: state.ui.focusPane === "response" ? "\u25CF Response" : "Response",
|
|
1388
|
-
style: paneStyle(state.ui.focusPane === "response")
|
|
2040
|
+
style: paneStyle(colors, state.ui.focusPane === "response")
|
|
1389
2041
|
},
|
|
1390
2042
|
[
|
|
1391
2043
|
ui.row({ gap: 2 }, [
|
|
1392
2044
|
state.request.sending ? ui.spinner({ label: "Sending request\u2026" }) : ui.text(statusLine ?? "Ready", {
|
|
1393
|
-
style: {
|
|
2045
|
+
style: {
|
|
2046
|
+
fg: statusColorForTone(colors, statusTone(result?.statusCode)),
|
|
2047
|
+
bold: true
|
|
2048
|
+
}
|
|
1394
2049
|
}),
|
|
1395
2050
|
result?.error && !state.request.sending ? ui.badge(result.error, { variant: "error" }) : null
|
|
1396
2051
|
]),
|
|
@@ -1402,43 +2057,42 @@ function renderResponse(state, deps) {
|
|
|
1402
2057
|
disabled: state.request.sending,
|
|
1403
2058
|
onPress: () => deps.onResponseTab(tab.key)
|
|
1404
2059
|
})
|
|
1405
|
-
)
|
|
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
|
|
1406
2073
|
]),
|
|
1407
|
-
renderResponseBody(state, deps)
|
|
2074
|
+
renderResponseBody(state, deps, colors)
|
|
1408
2075
|
]
|
|
1409
2076
|
);
|
|
1410
2077
|
}
|
|
1411
|
-
function
|
|
1412
|
-
const tone = statusTone(code);
|
|
1413
|
-
switch (tone) {
|
|
1414
|
-
case "green":
|
|
1415
|
-
return [120, 220, 140];
|
|
1416
|
-
case "yellow":
|
|
1417
|
-
return [240, 200, 100];
|
|
1418
|
-
case "red":
|
|
1419
|
-
return [240, 120, 120];
|
|
1420
|
-
default:
|
|
1421
|
-
return [140, 200, 240];
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
function renderMainLayout(state, deps) {
|
|
2078
|
+
function renderMainLayout(state, deps, colors) {
|
|
1425
2079
|
const layoutMode = resolveLayoutMode(state.ui.viewportWidth);
|
|
1426
2080
|
const zoom = state.ui.zoomPane;
|
|
1427
2081
|
if (zoom) {
|
|
1428
|
-
if (zoom === "files") return renderFileTree(state, deps);
|
|
1429
|
-
if (zoom === "editor") return renderEditor(state, deps, false);
|
|
1430
|
-
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);
|
|
1431
2085
|
}
|
|
1432
2086
|
if (layoutMode === "stacked") {
|
|
1433
|
-
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);
|
|
1434
2088
|
return pane;
|
|
1435
2089
|
}
|
|
1436
2090
|
if (layoutMode === "sidebar-overlay") {
|
|
1437
2091
|
return ui.row({ gap: 1, flex: 1 }, [
|
|
1438
|
-
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,
|
|
1439
2093
|
ui.column({ gap: 1, flex: 1 }, [
|
|
1440
|
-
renderEditor(state, deps, false),
|
|
1441
|
-
renderResponse(state, deps)
|
|
2094
|
+
renderEditor(state, deps, false, colors),
|
|
2095
|
+
renderResponse(state, deps, colors)
|
|
1442
2096
|
])
|
|
1443
2097
|
]);
|
|
1444
2098
|
}
|
|
@@ -1452,38 +2106,53 @@ function renderMainLayout(state, deps) {
|
|
|
1452
2106
|
onChange: deps.onSplitChange
|
|
1453
2107
|
},
|
|
1454
2108
|
[
|
|
1455
|
-
renderFileTree(state, deps),
|
|
1456
|
-
renderEditor(state, deps, false),
|
|
1457
|
-
renderResponse(state, deps)
|
|
2109
|
+
renderFileTree(state, deps, colors),
|
|
2110
|
+
renderEditor(state, deps, false, colors),
|
|
2111
|
+
renderResponse(state, deps, colors)
|
|
1458
2112
|
]
|
|
1459
2113
|
)
|
|
1460
2114
|
]);
|
|
1461
2115
|
}
|
|
1462
|
-
function renderFooter(state) {
|
|
2116
|
+
function renderFooter(state, deps, colors) {
|
|
1463
2117
|
const env = state.request.activeEnvironment.length > 0 ? state.request.activeEnvironment.join(",") : "none";
|
|
1464
|
-
const dirName =
|
|
2118
|
+
const dirName = path6.basename(state.workspaceRoot);
|
|
1465
2119
|
const branch = state.ui.gitBranch ?? "\u2014";
|
|
1466
|
-
const fileName = state.selectedFilePath ?
|
|
1467
|
-
const hints =
|
|
2120
|
+
const fileName = state.selectedFilePath ? path6.basename(state.selectedFilePath) : null;
|
|
2121
|
+
const hints = footerHintItems({
|
|
1468
2122
|
focusPane: state.ui.focusPane,
|
|
1469
2123
|
overlay: state.ui.overlay,
|
|
1470
2124
|
viewportWidth: state.ui.viewportWidth,
|
|
1471
|
-
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
|
|
1472
2130
|
});
|
|
1473
2131
|
return ui.statusBar({
|
|
1474
2132
|
id: "status-bar",
|
|
2133
|
+
style: { bg: colors.bgSubtle, fg: colors.fgPrimary },
|
|
1475
2134
|
left: [
|
|
1476
|
-
ui.text(dirName, { style: { fg:
|
|
1477
|
-
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 } }),
|
|
1478
2137
|
fileName ? ui.text(` | ${fileName}`) : null,
|
|
1479
|
-
state.dirty ? ui.text(" \u25CF", { style: { fg:
|
|
1480
|
-
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
|
+
}),
|
|
1481
2144
|
state.ui.statusMessage ? ui.text(` | ${state.ui.statusMessage}`) : null
|
|
1482
2145
|
].filter(Boolean),
|
|
1483
|
-
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
|
+
)
|
|
1484
2153
|
});
|
|
1485
2154
|
}
|
|
1486
|
-
function renderOverlayContent(state, deps) {
|
|
2155
|
+
function renderOverlayContent(state, deps, colors) {
|
|
1487
2156
|
switch (state.ui.overlay) {
|
|
1488
2157
|
case "env":
|
|
1489
2158
|
return ui.modal({
|
|
@@ -1491,12 +2160,18 @@ function renderOverlayContent(state, deps) {
|
|
|
1491
2160
|
title: "Environment",
|
|
1492
2161
|
content: ui.column({ gap: 1 }, [
|
|
1493
2162
|
ui.text("Select environment (Enter to apply, Esc to close)"),
|
|
1494
|
-
ui.
|
|
1495
|
-
|
|
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
|
|
1496
2168
|
}),
|
|
1497
2169
|
...state.request.environments.map(
|
|
1498
|
-
(env, index) => ui.
|
|
1499
|
-
|
|
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
|
|
1500
2175
|
})
|
|
1501
2176
|
)
|
|
1502
2177
|
]),
|
|
@@ -1562,9 +2237,10 @@ function renderOverlayContent(state, deps) {
|
|
|
1562
2237
|
}
|
|
1563
2238
|
}
|
|
1564
2239
|
function renderApp(state, deps) {
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1567
|
-
|
|
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)
|
|
1568
2244
|
]);
|
|
1569
2245
|
if (state.ui.overlay === "none") {
|
|
1570
2246
|
return base;
|
|
@@ -1577,7 +2253,7 @@ function renderApp(state, deps) {
|
|
|
1577
2253
|
backdrop: "dim",
|
|
1578
2254
|
closeOnEscape: true,
|
|
1579
2255
|
onClose: deps.onOverlayClose,
|
|
1580
|
-
content: renderOverlayContent(state, deps)
|
|
2256
|
+
content: renderOverlayContent(state, deps, colors)
|
|
1581
2257
|
})
|
|
1582
2258
|
]);
|
|
1583
2259
|
}
|
|
@@ -1592,6 +2268,35 @@ function focusPaneId(pane) {
|
|
|
1592
2268
|
}
|
|
1593
2269
|
}
|
|
1594
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
|
+
|
|
1595
2300
|
// src/utils/clipboard.ts
|
|
1596
2301
|
import clipboard from "clipboardy";
|
|
1597
2302
|
async function copyToClipboard(text) {
|
|
@@ -1605,12 +2310,11 @@ async function copyToClipboard(text) {
|
|
|
1605
2310
|
return false;
|
|
1606
2311
|
}
|
|
1607
2312
|
}
|
|
1608
|
-
function
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
}
|
|
2313
|
+
async function readFromClipboard() {
|
|
2314
|
+
try {
|
|
2315
|
+
return await clipboard.read();
|
|
2316
|
+
} catch {
|
|
2317
|
+
return null;
|
|
1614
2318
|
}
|
|
1615
2319
|
}
|
|
1616
2320
|
|
|
@@ -1632,17 +2336,229 @@ async function getGitBranch(root) {
|
|
|
1632
2336
|
}
|
|
1633
2337
|
}
|
|
1634
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
|
+
|
|
1635
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
|
+
}
|
|
1636
2545
|
async function main() {
|
|
1637
2546
|
initEngineProviders();
|
|
1638
|
-
const workspaceRoot =
|
|
2547
|
+
const workspaceRoot = path7.resolve(process3.argv[2] ?? process3.cwd());
|
|
1639
2548
|
const workspace = new Workspace(workspaceRoot);
|
|
1640
2549
|
const tree = await workspace.open();
|
|
2550
|
+
const config = loadConfig(workspaceRoot);
|
|
2551
|
+
const initialThemeMode = await resolveThemeMode(config.theme);
|
|
1641
2552
|
let currentState = createInitialState(workspaceRoot);
|
|
1642
2553
|
currentState = {
|
|
1643
2554
|
...currentState,
|
|
1644
2555
|
fileTree: tree,
|
|
1645
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
|
+
},
|
|
1646
2562
|
ui: {
|
|
1647
2563
|
...currentState.ui,
|
|
1648
2564
|
gitBranch: await getGitBranch(workspaceRoot)
|
|
@@ -1663,19 +2579,24 @@ async function main() {
|
|
|
1663
2579
|
}
|
|
1664
2580
|
const loaded = loadKeybindings(workspaceRoot);
|
|
1665
2581
|
app.keys({
|
|
1666
|
-
...buildBindingMap(loaded.bindings, (command) => {
|
|
2582
|
+
...buildBindingMap(loaded.bindings, (command, ctx) => {
|
|
2583
|
+
currentState = ctx.state;
|
|
1667
2584
|
if (command === "response.copy") {
|
|
1668
2585
|
void handleCopy(app, currentState);
|
|
1669
2586
|
}
|
|
1670
2587
|
bus.execute(command);
|
|
1671
2588
|
}),
|
|
1672
2589
|
enter: {
|
|
1673
|
-
handler: () =>
|
|
2590
|
+
handler: (ctx) => {
|
|
2591
|
+
currentState = ctx.state;
|
|
2592
|
+
bus.execute("env.apply");
|
|
2593
|
+
},
|
|
1674
2594
|
when: (ctx) => ctx.state.ui.overlay === "env",
|
|
1675
2595
|
description: "Apply selected environment"
|
|
1676
2596
|
},
|
|
1677
2597
|
up: {
|
|
1678
|
-
handler: () => {
|
|
2598
|
+
handler: (ctx) => {
|
|
2599
|
+
currentState = ctx.state;
|
|
1679
2600
|
if (currentState.ui.overlay === "env") {
|
|
1680
2601
|
bus.execute("env.selectPrev");
|
|
1681
2602
|
}
|
|
@@ -1683,7 +2604,8 @@ async function main() {
|
|
|
1683
2604
|
when: (ctx) => ctx.state.ui.overlay === "env" || ctx.state.ui.overlay === "commandPalette"
|
|
1684
2605
|
},
|
|
1685
2606
|
down: {
|
|
1686
|
-
handler: () => {
|
|
2607
|
+
handler: (ctx) => {
|
|
2608
|
+
currentState = ctx.state;
|
|
1687
2609
|
if (currentState.ui.overlay === "env") {
|
|
1688
2610
|
bus.execute("env.selectNext");
|
|
1689
2611
|
}
|
|
@@ -1694,11 +2616,33 @@ async function main() {
|
|
|
1694
2616
|
app.update((state) => ({
|
|
1695
2617
|
...state,
|
|
1696
2618
|
settings: {
|
|
2619
|
+
...state.settings,
|
|
1697
2620
|
keymapPreset: loaded.preset,
|
|
1698
2621
|
keybindings: loaded.bindings
|
|
1699
2622
|
}
|
|
1700
2623
|
}));
|
|
1701
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
|
+
};
|
|
1702
2646
|
bus = createCommandContext({
|
|
1703
2647
|
workspace,
|
|
1704
2648
|
getState: () => currentState,
|
|
@@ -1709,16 +2653,27 @@ async function main() {
|
|
|
1709
2653
|
});
|
|
1710
2654
|
},
|
|
1711
2655
|
quit: () => {
|
|
1712
|
-
void workspace.close().finally(() => {
|
|
1713
|
-
app?.stop()
|
|
2656
|
+
void workspace.close().finally(async () => {
|
|
2657
|
+
await app?.stop();
|
|
2658
|
+
await app?.dispose();
|
|
2659
|
+
process3.exit(0);
|
|
1714
2660
|
});
|
|
1715
2661
|
},
|
|
1716
2662
|
reloadKeybindings
|
|
1717
2663
|
});
|
|
1718
|
-
app = createNodeApp({ initialState: currentState });
|
|
2664
|
+
app = createNodeApp({ initialState: currentState, theme: themeForMode(initialThemeMode) });
|
|
1719
2665
|
reloadKeybindings();
|
|
1720
|
-
|
|
1721
|
-
|
|
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);
|
|
1722
2677
|
workspace.on("change", () => {
|
|
1723
2678
|
void bus.refreshWorkspace();
|
|
1724
2679
|
void refreshGitBranch();
|
|
@@ -1727,29 +2682,27 @@ async function main() {
|
|
|
1727
2682
|
(state) => renderApp(state, {
|
|
1728
2683
|
onEditorChange: (lines, cursor) => {
|
|
1729
2684
|
app?.update((prev) => {
|
|
1730
|
-
const
|
|
2685
|
+
const fileLines = stripEditorLines(lines);
|
|
2686
|
+
const sourceCursor = sourceCursorFromEditor(cursor);
|
|
1731
2687
|
return {
|
|
1732
2688
|
...prev,
|
|
1733
|
-
fileLines
|
|
1734
|
-
dirty: contentFromLines(
|
|
1735
|
-
activeRegion,
|
|
2689
|
+
fileLines,
|
|
2690
|
+
dirty: contentFromLines(fileLines) !== prev.fileContent,
|
|
1736
2691
|
ui: { ...prev.ui, focusPane: "editor" },
|
|
1737
|
-
editor: { ...prev.editor, cursor }
|
|
2692
|
+
editor: { ...prev.editor, cursor: sourceCursor }
|
|
1738
2693
|
};
|
|
1739
2694
|
});
|
|
1740
2695
|
},
|
|
1741
2696
|
onEditorSelection: (selection) => {
|
|
1742
2697
|
app?.update((prev) => {
|
|
1743
|
-
const
|
|
1744
|
-
const activeRegion = prev.parsedFile ? resolveRegionAtLine(prev.parsedFile.regions, cursorLine) : null;
|
|
2698
|
+
const sourceSelection = sourceSelectionFromEditor(selection);
|
|
1745
2699
|
return {
|
|
1746
2700
|
...prev,
|
|
1747
|
-
activeRegion,
|
|
1748
2701
|
ui: { ...prev.ui, focusPane: "editor" },
|
|
1749
2702
|
editor: {
|
|
1750
2703
|
...prev.editor,
|
|
1751
|
-
selection,
|
|
1752
|
-
cursor:
|
|
2704
|
+
selection: sourceSelection,
|
|
2705
|
+
cursor: sourceSelection?.active ?? prev.editor.cursor
|
|
1753
2706
|
}
|
|
1754
2707
|
};
|
|
1755
2708
|
});
|
|
@@ -1813,6 +2766,12 @@ async function main() {
|
|
|
1813
2766
|
responseEditor: { ...prev.responseEditor, cursor }
|
|
1814
2767
|
}));
|
|
1815
2768
|
},
|
|
2769
|
+
onResponseJsonFoldToggle: () => {
|
|
2770
|
+
bus?.execute("response.jsonFoldToggle");
|
|
2771
|
+
},
|
|
2772
|
+
onResponseJsonUnfoldAll: () => {
|
|
2773
|
+
bus?.execute("response.jsonUnfoldAll");
|
|
2774
|
+
},
|
|
1816
2775
|
onSplitChange: (sizes) => {
|
|
1817
2776
|
if (sizes.length === 3) {
|
|
1818
2777
|
app?.update((prev) => ({
|
|
@@ -1850,26 +2809,34 @@ async function main() {
|
|
|
1850
2809
|
bus?.execute("overlay.close");
|
|
1851
2810
|
},
|
|
1852
2811
|
onEnvSelect: (index) => {
|
|
1853
|
-
app?.update((prev) =>
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
2812
|
+
app?.update((prev) => {
|
|
2813
|
+
currentState = {
|
|
2814
|
+
...prev,
|
|
2815
|
+
ui: { ...prev.ui, envSelectedIndex: index }
|
|
2816
|
+
};
|
|
2817
|
+
return currentState;
|
|
2818
|
+
});
|
|
2819
|
+
bus?.execute("env.apply");
|
|
1857
2820
|
},
|
|
1858
2821
|
onResponseSearch: (query) => {
|
|
1859
2822
|
app?.update((prev) => ({
|
|
1860
2823
|
...prev,
|
|
1861
2824
|
editor: { ...prev.editor, searchQuery: query }
|
|
1862
2825
|
}));
|
|
1863
|
-
}
|
|
2826
|
+
},
|
|
2827
|
+
onCommand: executeCommand
|
|
1864
2828
|
})
|
|
1865
2829
|
);
|
|
1866
|
-
app.onEvent((event) =>
|
|
2830
|
+
app.onEvent((event) => {
|
|
2831
|
+
void handleUiEvent(event, app);
|
|
2832
|
+
});
|
|
1867
2833
|
const files = flattenFiles(tree).filter((node) => node.kind === "file");
|
|
1868
2834
|
if (files[0]) {
|
|
1869
2835
|
await bus.openFile(files[0].path);
|
|
1870
2836
|
}
|
|
1871
2837
|
await app.run();
|
|
1872
|
-
|
|
2838
|
+
stopKeybindingWatch();
|
|
2839
|
+
stopConfigWatch();
|
|
1873
2840
|
}
|
|
1874
2841
|
async function handleCopy(app, state) {
|
|
1875
2842
|
const result = state.request.result;
|
|
@@ -1888,7 +2855,38 @@ async function handleCopy(app, state) {
|
|
|
1888
2855
|
ui: { ...prev.ui, statusMessage: ok ? "Copied" : "Copy failed" }
|
|
1889
2856
|
}));
|
|
1890
2857
|
}
|
|
1891
|
-
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) {
|
|
1892
2890
|
if (event.kind === "engine" && event.event.kind === "resize") {
|
|
1893
2891
|
const resize = event.event;
|
|
1894
2892
|
app.update((prev) => ({
|
|
@@ -1903,10 +2901,46 @@ function handleUiEvent(event, app) {
|
|
|
1903
2901
|
}));
|
|
1904
2902
|
return;
|
|
1905
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
|
+
}
|
|
1906
2908
|
if (event.kind !== "engine" || event.event.kind !== "mouse") {
|
|
1907
2909
|
return;
|
|
1908
2910
|
}
|
|
1909
|
-
|
|
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
|
+
}
|
|
1910
2944
|
const panes = [
|
|
1911
2945
|
{ id: "pane-files", pane: "files" },
|
|
1912
2946
|
{ id: "pane-editor", pane: "editor" },
|
|
@@ -1917,7 +2951,7 @@ function handleUiEvent(event, app) {
|
|
|
1917
2951
|
if (!rect) {
|
|
1918
2952
|
continue;
|
|
1919
2953
|
}
|
|
1920
|
-
if (x
|
|
2954
|
+
if (isPointInRect(x, y, rect)) {
|
|
1921
2955
|
app.update((prev) => ({
|
|
1922
2956
|
...prev,
|
|
1923
2957
|
ui: { ...prev.ui, focusPane: entry.pane }
|
|
@@ -1928,7 +2962,7 @@ function handleUiEvent(event, app) {
|
|
|
1928
2962
|
}
|
|
1929
2963
|
void main().catch((error) => {
|
|
1930
2964
|
console.error(error);
|
|
1931
|
-
|
|
2965
|
+
process3.exit(1);
|
|
1932
2966
|
});
|
|
1933
2967
|
export {
|
|
1934
2968
|
focusPaneId
|