editprompt 1.6.0 → 1.8.1
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 +112 -9
- package/dist/index.js +470 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ A CLI tool that lets you write prompts for CLI tools using your favorite text ed
|
|
|
22
22
|
## ✨ Features
|
|
23
23
|
|
|
24
24
|
- 🖊️ **Editor Integration**: Use your preferred text editor to write prompts
|
|
25
|
-
- 🖥️ **Multiplexer Support**: Send prompts directly to tmux, WezTerm, or
|
|
25
|
+
- 🖥️ **Multiplexer Support**: Send prompts directly to tmux, WezTerm, Herdr, or Rex sessions
|
|
26
26
|
- 🖥️ **Universal Terminal Support**: Works with any terminal via clipboard - no multiplexer required
|
|
27
27
|
- 📤 **Send Without Closing**: Iterate on prompts without closing your editor
|
|
28
28
|
- 📋 **Quote Buffering**: Collect text selections and send them as quoted replies
|
|
@@ -187,6 +187,92 @@ Run the binding from a target pane. editprompt opens a regular bottom split for
|
|
|
187
187
|
|
|
188
188
|
Inside a Herdr pane, editprompt automatically selects the Herdr backend when `HERDR_SOCKET_PATH` and a pane ID are available. An explicit `--mux` option or `EDITPROMPT_MUX` value takes precedence, so the binding above keeps `--mux herdr` to make its intent clear.
|
|
189
189
|
|
|
190
|
+
### Rex Integration
|
|
191
|
+
|
|
192
|
+
[Rex](https://www.superlogical.com/rex/docs) is configured in Lua, and its configuration file can both run commands and build layouts, so the binding does the splitting and editprompt only opens the editor. Add this to `~/.config/rex/init.lua`:
|
|
193
|
+
|
|
194
|
+
```lua
|
|
195
|
+
-- Height of the editor pane, in rows. new_split takes a fraction, so it is
|
|
196
|
+
-- derived from the target pane's current height; a fixed fraction would make
|
|
197
|
+
-- the editor grow with the window. PANE_CHROME_ROWS is what a pane spends on
|
|
198
|
+
-- its header and border, which hold no text.
|
|
199
|
+
local EDITOR_ROWS = 12
|
|
200
|
+
local PANE_CHROME_ROWS = 3
|
|
201
|
+
|
|
202
|
+
local function editor_ratio(block_id)
|
|
203
|
+
local size = rex.block.call("com.superlogical.terminal", "size", { block_id = block_id })
|
|
204
|
+
local rows = size and size.rows
|
|
205
|
+
if not rows or rows <= 1 then
|
|
206
|
+
return 0.8
|
|
207
|
+
end
|
|
208
|
+
-- Never let the editor take more than half, for a pane too short to spare the
|
|
209
|
+
-- rows, and stay inside the fraction new_split accepts.
|
|
210
|
+
return math.max(0.5, math.min(0.9, 1 - (EDITOR_ROWS + PANE_CHROME_ROWS) / rows))
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
-- Start the editor in the directory the target pane last reported. A shell that
|
|
214
|
+
-- uses OSC 7, such as nushell, reports it as a file:// URL.
|
|
215
|
+
local function pane_cwd(block_id)
|
|
216
|
+
local result = rex.block.call("com.superlogical.terminal", "pwd", { block_id = block_id })
|
|
217
|
+
local pwd = result and result.pwd
|
|
218
|
+
if not pwd then
|
|
219
|
+
return nil
|
|
220
|
+
end
|
|
221
|
+
return (pwd:gsub("^file://[^/]*", ""))
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
rex.bind("alt+q", function(ctx)
|
|
225
|
+
-- Focus an editor pane that is already open, or, when pressed from the editor
|
|
226
|
+
-- pane, go back to the target pane.
|
|
227
|
+
local resumed, err = rex.exec({
|
|
228
|
+
"editprompt", "resume", "--mux", "rex", "--target-pane", ctx.block_id,
|
|
229
|
+
})
|
|
230
|
+
if not resumed then
|
|
231
|
+
-- The command never ran at all, which is not the same as having no editor
|
|
232
|
+
-- pane to go back to. Say so rather than opening a split that would fail
|
|
233
|
+
-- the same way.
|
|
234
|
+
rex.log("error", "editprompt could not be run: " .. tostring(err))
|
|
235
|
+
return true
|
|
236
|
+
end
|
|
237
|
+
if resumed.ok then
|
|
238
|
+
return true
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
rex.session.new_split{
|
|
242
|
+
anchor_id = ctx.block_id,
|
|
243
|
+
direction = "vertical",
|
|
244
|
+
side = "after",
|
|
245
|
+
ratio = editor_ratio(ctx.block_id),
|
|
246
|
+
focus = true,
|
|
247
|
+
layout = rex.layout.block{
|
|
248
|
+
flavor = "com.superlogical.terminal.shell",
|
|
249
|
+
options = {
|
|
250
|
+
command = {
|
|
251
|
+
"editprompt", "open", "--mux", "rex",
|
|
252
|
+
"--target-pane", ctx.block_id, "--editor", "nvim", "--always-copy",
|
|
253
|
+
},
|
|
254
|
+
cwd = pane_cwd(ctx.block_id),
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
return true
|
|
259
|
+
end)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Reload it with `rex config reload`; Rex does not watch the file for changes. `rex config check` reports mistakes without applying them.
|
|
263
|
+
|
|
264
|
+
`ratio` is the share the `before` part keeps, which here is the target pane, so a larger value leaves the editor less room. `direction = "vertical"` stacks the panes and `side = "after"` puts the new one below. `EDITOR_ROWS` is the knob worth tuning: it keeps the editor the same height whatever the window's size, the way `--pane-rows` does for Herdr.
|
|
265
|
+
|
|
266
|
+
**Turn off the app's "Autobalance Splits" setting.** With it on, Rex equalises every pane as soon as a block is created, which discards the `ratio` the binding asked for and leaves the editor half the window. Setting the border afterwards with `session.resize_pane` does not help either: the equalisation lands after it. `focus` defaults to false, so the binding asks for it explicitly. The pane closes once the editor exits, which is Rex's default for a terminal started with a `command`; it stays open after a quick failure instead, so a startup error remains readable.
|
|
267
|
+
|
|
268
|
+
editprompt locates the `rex` CLI through `REX_BIN_DIR`, which Rex sets in every terminal it opens, and falls back to the PATH. That matters because the login shell can rebuild PATH and drop the directory Rex added. On NixOS and nix-darwin it does, and the fix Rex documents for that runs in interactive shells only, which a terminal started from `init.lua` is not.
|
|
269
|
+
|
|
270
|
+
`rex.exec` runs in the Rex server's environment rather than a shell's, so give `editprompt` an absolute path there if the server's PATH does not include it.
|
|
271
|
+
|
|
272
|
+
Name the editor with `--editor`. A terminal Rex starts gets the login shell's environment, not the interactive one, so `$EDITOR` is whatever that environment sets rather than what your prompt uses — on nix-darwin, for instance, it is `nano`.
|
|
273
|
+
|
|
274
|
+
Inside a Rex pane, editprompt automatically selects the Rex backend when `REX_BLOCK` is set. Running tmux inside a Rex pane sets both, and Rex wins, so pass `--mux tmux` or set `EDITPROMPT_MUX` for that case.
|
|
275
|
+
|
|
190
276
|
### Editor Integration (Send Without Closing)
|
|
191
277
|
|
|
192
278
|
While editprompt is running, you can send content to the target pane or clipboard without closing the editor. This allows you to iterate quickly on your prompts.
|
|
@@ -205,7 +291,7 @@ editprompt input --auto-send -- "your content here"
|
|
|
205
291
|
editprompt input --auto-send --send-key "C-m" -- "your content here"
|
|
206
292
|
# Customize the key to send after content (tmux format example)
|
|
207
293
|
# WezTerm example: --send-key "\r"
|
|
208
|
-
# Herdr example: --send-key "enter"
|
|
294
|
+
# Herdr and Rex example: --send-key "enter"
|
|
209
295
|
```
|
|
210
296
|
|
|
211
297
|
This sends the content to the target pane (or clipboard) while keeping your editor open, so you can continue editing and send multiple times.
|
|
@@ -217,6 +303,7 @@ This sends the content to the target pane (or clipboard) while keeping your edit
|
|
|
217
303
|
- tmux format: `Enter` (default), `C-a`, etc.
|
|
218
304
|
- WezTerm format: `\r` (default), `\x01`, etc.
|
|
219
305
|
- Herdr format: `enter` (default), `ctrl+a`, etc.
|
|
306
|
+
- Rex format: `enter` (default), `ctrl+a`, etc.
|
|
220
307
|
|
|
221
308
|
#### Neovim Integration
|
|
222
309
|
|
|
@@ -236,7 +323,7 @@ editprompt press -- Up
|
|
|
236
323
|
editprompt press -- C-m # Enter
|
|
237
324
|
editprompt press -- C-c # Ctrl+C
|
|
238
325
|
|
|
239
|
-
# Herdr examples
|
|
326
|
+
# Herdr and Rex examples
|
|
240
327
|
editprompt press -- enter
|
|
241
328
|
editprompt press -- ctrl+c
|
|
242
329
|
|
|
@@ -252,12 +339,12 @@ editprompt press --delay 500 -- Tab
|
|
|
252
339
|
|
|
253
340
|
**Key notation** depends on your multiplexer:
|
|
254
341
|
|
|
255
|
-
| Key | tmux | WezTerm | Herdr |
|
|
256
|
-
| ------------- | ------------- | ------------------- | ------------- |
|
|
257
|
-
| Enter | `C-m` | `\r` | `enter` |
|
|
258
|
-
| Tab | `Tab` | `\t` | `tab` |
|
|
259
|
-
| Escape | `Escape` | `\x1b` | `esc` |
|
|
260
|
-
| Arrow Up/Down | `Up` / `Down` | `\x1b[A` / `\x1b[B` | `up` / `down` |
|
|
342
|
+
| Key | tmux | WezTerm | Herdr | Rex |
|
|
343
|
+
| ------------- | ------------- | ------------------- | ------------- | ------------- |
|
|
344
|
+
| Enter | `C-m` | `\r` | `enter` | `enter` |
|
|
345
|
+
| Tab | `Tab` | `\t` | `tab` | `tab` |
|
|
346
|
+
| Escape | `Escape` | `\x1b` | `esc` | `escape` |
|
|
347
|
+
| Arrow Up/Down | `Up` / `Down` | `\x1b[A` / `\x1b[B` | `up` / `down` | `up` / `down` |
|
|
261
348
|
|
|
262
349
|
### Quote Workflow Setup
|
|
263
350
|
|
|
@@ -327,6 +414,22 @@ editprompt collect --mux herdr --target-pane "$HERDR_PANE_ID" -- "selected text"
|
|
|
327
414
|
|
|
328
415
|
This can be called from a script or editor integration that has access to the selected text. Herdr quote buffers are stored in editprompt's configuration, associated with the target pane.
|
|
329
416
|
|
|
417
|
+
#### Collecting Quotes in Rex
|
|
418
|
+
|
|
419
|
+
Pass selected text as an argument together with the target block ID:
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
editprompt collect --mux rex --target-pane "$REX_BLOCK" -- "selected text"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
From `init.lua`, call it with `rex.exec`, which takes an argv table so the text needs no quoting:
|
|
426
|
+
|
|
427
|
+
```lua
|
|
428
|
+
rex.exec({ "editprompt", "collect", "--mux", "rex", "--target-pane", ctx.block_id, "--", text })
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
As with Herdr, Rex quote buffers live in editprompt's configuration, associated with the target pane.
|
|
432
|
+
|
|
330
433
|
#### Capturing Collected Quotes
|
|
331
434
|
|
|
332
435
|
Run this command from within your editor pane to retrieve all collected quotes:
|
package/dist/index.js
CHANGED
|
@@ -7,10 +7,10 @@ import { createConnection } from "node:net";
|
|
|
7
7
|
import Conf from "conf";
|
|
8
8
|
import { exec, spawn } from "node:child_process";
|
|
9
9
|
import { promisify } from "node:util";
|
|
10
|
+
import { join } from "node:path";
|
|
10
11
|
import clipboardy from "clipboardy";
|
|
11
12
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
12
13
|
import { tmpdir } from "node:os";
|
|
13
|
-
import { join } from "node:path";
|
|
14
14
|
|
|
15
15
|
//#region \0rolldown/runtime.js
|
|
16
16
|
var __defProp = Object.defineProperty;
|
|
@@ -90,7 +90,7 @@ function setupLogger(options = {}) {
|
|
|
90
90
|
|
|
91
91
|
//#endregion
|
|
92
92
|
//#region package.json
|
|
93
|
-
var version = "1.
|
|
93
|
+
var version = "1.8.1";
|
|
94
94
|
|
|
95
95
|
//#endregion
|
|
96
96
|
//#region src/config/constants.ts
|
|
@@ -102,6 +102,9 @@ const TMUX_SEND_CHUNK_BYTES = TMUX_IMSG_LIMIT_BYTES / 2;
|
|
|
102
102
|
const WEZTERM_SEND_CHUNK_BYTES = TMUX_SEND_CHUNK_BYTES;
|
|
103
103
|
const HERDR_REQUEST_LIMIT_BYTES = 1024 * 1024;
|
|
104
104
|
const HERDR_SEND_CHUNK_BYTES = HERDR_REQUEST_LIMIT_BYTES / 8;
|
|
105
|
+
const NIWATERM_SEND_CHUNK_BYTES = WEZTERM_SEND_CHUNK_BYTES;
|
|
106
|
+
const REX_CONTROL_LIMIT_BYTES = 64 * 1024;
|
|
107
|
+
const REX_SEND_CHUNK_BYTES = REX_CONTROL_LIMIT_BYTES / 8;
|
|
105
108
|
|
|
106
109
|
//#endregion
|
|
107
110
|
//#region src/utils/contentChunker.ts
|
|
@@ -139,28 +142,28 @@ const conf = new Conf({ projectName });
|
|
|
139
142
|
//#endregion
|
|
140
143
|
//#region src/modules/herdr.ts
|
|
141
144
|
var herdr_exports = /* @__PURE__ */ __exportAll({
|
|
142
|
-
appendToQuoteText: () => appendToQuoteText$
|
|
145
|
+
appendToQuoteText: () => appendToQuoteText$2,
|
|
143
146
|
calculateEditorSplitRatio: () => calculateEditorSplitRatio,
|
|
144
|
-
checkPaneExists: () => checkPaneExists$
|
|
145
|
-
clearEditorPaneId: () => clearEditorPaneId$
|
|
146
|
-
clearQuoteText: () => clearQuoteText$
|
|
147
|
+
checkPaneExists: () => checkPaneExists$4,
|
|
148
|
+
clearEditorPaneId: () => clearEditorPaneId$4,
|
|
149
|
+
clearQuoteText: () => clearQuoteText$2,
|
|
147
150
|
closePane: () => closePane,
|
|
148
|
-
focusPane: () => focusPane$
|
|
149
|
-
getCurrentPaneId: () => getCurrentPaneId$
|
|
150
|
-
getEditorPaneId: () => getEditorPaneId$
|
|
151
|
-
getQuoteText: () => getQuoteText$
|
|
151
|
+
focusPane: () => focusPane$4,
|
|
152
|
+
getCurrentPaneId: () => getCurrentPaneId$4,
|
|
153
|
+
getEditorPaneId: () => getEditorPaneId$4,
|
|
154
|
+
getQuoteText: () => getQuoteText$2,
|
|
152
155
|
getStorageNamespace: () => getStorageNamespace,
|
|
153
|
-
getTargetPaneIds: () => getTargetPaneIds$
|
|
156
|
+
getTargetPaneIds: () => getTargetPaneIds$4,
|
|
154
157
|
inputToHerdrPane: () => inputToHerdrPane,
|
|
155
|
-
isEditorPaneFromConf: () => isEditorPaneFromConf$
|
|
156
|
-
markAsEditorPane: () => markAsEditorPane$
|
|
157
|
-
resumeEditorPane: () => resumeEditorPane,
|
|
158
|
+
isEditorPaneFromConf: () => isEditorPaneFromConf$2,
|
|
159
|
+
markAsEditorPane: () => markAsEditorPane$4,
|
|
160
|
+
resumeEditorPane: () => resumeEditorPane$1,
|
|
158
161
|
runCommandInPane: () => runCommandInPane,
|
|
159
|
-
saveEditorPaneId: () => saveEditorPaneId$
|
|
162
|
+
saveEditorPaneId: () => saveEditorPaneId$4,
|
|
160
163
|
sendKeyToHerdrPane: () => sendKeyToHerdrPane,
|
|
161
164
|
splitEditorPane: () => splitEditorPane
|
|
162
165
|
});
|
|
163
|
-
const logger$
|
|
166
|
+
const logger$15 = getLogger(["editprompt", "herdr"]);
|
|
164
167
|
const HERDR_REQUEST_TIMEOUT_MS = 6e3;
|
|
165
168
|
let requestSequence = 0;
|
|
166
169
|
function getSocketPath() {
|
|
@@ -241,27 +244,27 @@ async function sendRequest(method, params) {
|
|
|
241
244
|
function getStorageNamespace() {
|
|
242
245
|
return `herdr.session_${createHash("sha256").update(getSocketPath()).digest("hex").slice(0, 16)}`;
|
|
243
246
|
}
|
|
244
|
-
function targetPaneKey(paneId) {
|
|
247
|
+
function targetPaneKey$1(paneId) {
|
|
245
248
|
return `${getStorageNamespace()}.targetPane.pane_${paneId}`;
|
|
246
249
|
}
|
|
247
|
-
function editorPaneKey(paneId) {
|
|
250
|
+
function editorPaneKey$1(paneId) {
|
|
248
251
|
return `${getStorageNamespace()}.editorPane.pane_${paneId}`;
|
|
249
252
|
}
|
|
250
|
-
async function getCurrentPaneId$
|
|
253
|
+
async function getCurrentPaneId$4() {
|
|
251
254
|
const paneId = process.env.HERDR_PANE_ID?.trim() || process.env.HERDR_ACTIVE_PANE_ID?.trim();
|
|
252
255
|
if (!paneId) throw new Error("HERDR_PANE_ID or HERDR_ACTIVE_PANE_ID is not set");
|
|
253
256
|
return paneId;
|
|
254
257
|
}
|
|
255
|
-
async function checkPaneExists$
|
|
258
|
+
async function checkPaneExists$4(paneId) {
|
|
256
259
|
try {
|
|
257
260
|
await sendRequest("pane.get", { pane_id: paneId });
|
|
258
261
|
return true;
|
|
259
262
|
} catch (error) {
|
|
260
|
-
logger$
|
|
263
|
+
logger$15.debug("checkPaneExists failed: {error}", { error });
|
|
261
264
|
return false;
|
|
262
265
|
}
|
|
263
266
|
}
|
|
264
|
-
async function focusPane$
|
|
267
|
+
async function focusPane$4(paneId) {
|
|
265
268
|
await sendRequest("pane.focus", { pane_id: paneId });
|
|
266
269
|
}
|
|
267
270
|
function calculateEditorSplitRatio(paneHeight, editorPaneRows) {
|
|
@@ -284,14 +287,14 @@ async function splitEditorPane(targetPaneId, editorPaneRows, cwd, env = {}) {
|
|
|
284
287
|
if (typeof editorPaneId !== "string" || editorPaneId === "") throw new Error("Herdr pane.split did not return the new pane ID");
|
|
285
288
|
return editorPaneId;
|
|
286
289
|
}
|
|
287
|
-
function quoteShellWord(value) {
|
|
290
|
+
function quoteShellWord$1(value) {
|
|
288
291
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
289
292
|
}
|
|
290
293
|
async function runCommandInPane(paneId, argv) {
|
|
291
294
|
if (argv.length === 0) throw new Error("Cannot run an empty command in a Herdr pane");
|
|
292
295
|
await sendRequest("pane.send_input", {
|
|
293
296
|
pane_id: paneId,
|
|
294
|
-
text: `exec ${argv.map(quoteShellWord).join(" ")}`,
|
|
297
|
+
text: `exec ${argv.map(quoteShellWord$1).join(" ")}`,
|
|
295
298
|
keys: ["Enter"]
|
|
296
299
|
});
|
|
297
300
|
}
|
|
@@ -304,7 +307,7 @@ async function inputToHerdrPane(paneId, content) {
|
|
|
304
307
|
pane_id: paneId,
|
|
305
308
|
text: chunk
|
|
306
309
|
});
|
|
307
|
-
logger$
|
|
310
|
+
logger$15.debug("Content sent to Herdr pane: {paneId} ({chunkCount} chunks)", {
|
|
308
311
|
paneId,
|
|
309
312
|
chunkCount: chunks.length
|
|
310
313
|
});
|
|
@@ -316,6 +319,264 @@ async function sendKeyToHerdrPane(paneId, key, delay = 1e3) {
|
|
|
316
319
|
keys: [key]
|
|
317
320
|
});
|
|
318
321
|
}
|
|
322
|
+
async function saveEditorPaneId$4(targetPaneId, editorPaneId) {
|
|
323
|
+
const data = conf.get(targetPaneKey$1(targetPaneId));
|
|
324
|
+
const existing = typeof data === "object" && data !== null ? data : {};
|
|
325
|
+
conf.set(targetPaneKey$1(targetPaneId), {
|
|
326
|
+
...existing,
|
|
327
|
+
editorPaneId
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
async function getEditorPaneId$4(targetPaneId) {
|
|
331
|
+
const data = conf.get(targetPaneKey$1(targetPaneId));
|
|
332
|
+
if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
|
|
333
|
+
return "";
|
|
334
|
+
}
|
|
335
|
+
async function clearEditorPaneId$4(targetPaneId, expectedEditorPaneId) {
|
|
336
|
+
const editorPaneId = await getEditorPaneId$4(targetPaneId);
|
|
337
|
+
if (expectedEditorPaneId && editorPaneId !== expectedEditorPaneId) {
|
|
338
|
+
conf.delete(editorPaneKey$1(expectedEditorPaneId));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const data = conf.get(targetPaneKey$1(targetPaneId));
|
|
342
|
+
if (typeof data === "object" && data !== null && "editorPaneId" in data) {
|
|
343
|
+
const { editorPaneId: _, ...remaining } = data;
|
|
344
|
+
if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey$1(targetPaneId));
|
|
345
|
+
else conf.set(targetPaneKey$1(targetPaneId), remaining);
|
|
346
|
+
}
|
|
347
|
+
if (editorPaneId) conf.delete(editorPaneKey$1(editorPaneId));
|
|
348
|
+
}
|
|
349
|
+
async function markAsEditorPane$4(editorPaneId, targetPaneIds) {
|
|
350
|
+
const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
|
|
351
|
+
conf.set(editorPaneKey$1(editorPaneId), { targetPaneIds: uniqueTargetPaneIds });
|
|
352
|
+
for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$4(targetPaneId, editorPaneId);
|
|
353
|
+
}
|
|
354
|
+
async function getTargetPaneIds$4(editorPaneId) {
|
|
355
|
+
const data = conf.get(editorPaneKey$1(editorPaneId));
|
|
356
|
+
if (typeof data === "object" && data !== null && "targetPaneIds" in data) {
|
|
357
|
+
const targetPaneIds = data.targetPaneIds;
|
|
358
|
+
if (Array.isArray(targetPaneIds)) return targetPaneIds.map((id) => String(id));
|
|
359
|
+
}
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
function isEditorPaneFromConf$2(paneId) {
|
|
363
|
+
return conf.has(editorPaneKey$1(paneId));
|
|
364
|
+
}
|
|
365
|
+
async function resumeEditorPane$1(targetPaneId) {
|
|
366
|
+
const currentPaneId = await getCurrentPaneId$4();
|
|
367
|
+
const editorPaneId = isEditorPaneFromConf$2(currentPaneId) ? currentPaneId : isEditorPaneFromConf$2(targetPaneId) ? targetPaneId : "";
|
|
368
|
+
if (editorPaneId) {
|
|
369
|
+
const targetPaneIds = await getTargetPaneIds$4(editorPaneId);
|
|
370
|
+
for (const paneId of targetPaneIds) if (await checkPaneExists$4(paneId)) {
|
|
371
|
+
await focusPane$4(paneId);
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
const registeredEditorPaneId = await getEditorPaneId$4(targetPaneId);
|
|
377
|
+
if (registeredEditorPaneId === "") return false;
|
|
378
|
+
if (!await checkPaneExists$4(registeredEditorPaneId)) {
|
|
379
|
+
await clearEditorPaneId$4(targetPaneId);
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
await focusPane$4(registeredEditorPaneId);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
async function appendToQuoteText$2(paneId, content) {
|
|
386
|
+
const data = conf.get(targetPaneKey$1(paneId));
|
|
387
|
+
const existing = typeof data === "object" && data !== null ? data : {};
|
|
388
|
+
const existingQuoteText = "quote_text" in existing ? String(existing.quote_text) : "";
|
|
389
|
+
const quoteText = existingQuoteText.trim() !== "" ? `${existingQuoteText}\n\n${content}` : content;
|
|
390
|
+
conf.set(targetPaneKey$1(paneId), {
|
|
391
|
+
...existing,
|
|
392
|
+
quote_text: quoteText
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
async function getQuoteText$2(paneId) {
|
|
396
|
+
const data = conf.get(targetPaneKey$1(paneId));
|
|
397
|
+
if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
|
|
398
|
+
return "";
|
|
399
|
+
}
|
|
400
|
+
async function clearQuoteText$2(paneId) {
|
|
401
|
+
const data = conf.get(targetPaneKey$1(paneId));
|
|
402
|
+
if (typeof data === "object" && data !== null && "quote_text" in data) {
|
|
403
|
+
const { quote_text: _, ...remaining } = data;
|
|
404
|
+
if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey$1(paneId));
|
|
405
|
+
else conf.set(targetPaneKey$1(paneId), remaining);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/modules/niwaterm.ts
|
|
411
|
+
const execAsync$2 = promisify(exec);
|
|
412
|
+
const logger$14 = getLogger(["editprompt", "niwaterm"]);
|
|
413
|
+
const VAR_EDITOR_PANE = "editprompt_editor_pane";
|
|
414
|
+
const VAR_IS_EDITOR = "editprompt_is_editor";
|
|
415
|
+
const VAR_TARGET_PANES = "editprompt_target_panes";
|
|
416
|
+
const VAR_QUOTE = "editprompt_quote";
|
|
417
|
+
function quoteShellWord(value) {
|
|
418
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
419
|
+
}
|
|
420
|
+
async function getCurrentPaneId$3() {
|
|
421
|
+
const tabId = process.env.NIWATERM_TAB_ID?.trim();
|
|
422
|
+
if (!tabId) throw new Error("NIWATERM_TAB_ID is not set");
|
|
423
|
+
return tabId;
|
|
424
|
+
}
|
|
425
|
+
async function getTabVar(tabId, key) {
|
|
426
|
+
try {
|
|
427
|
+
const { stdout } = await execAsync$2(`niwa tab var get -t ${quoteShellWord(tabId)} ${quoteShellWord(key)}`);
|
|
428
|
+
return stdout.trim();
|
|
429
|
+
} catch (error) {
|
|
430
|
+
logger$14.debug("getTabVar failed: {error}", { error });
|
|
431
|
+
return "";
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async function setTabVar(tabId, key, value) {
|
|
435
|
+
await execAsync$2(`niwa tab var set -t ${quoteShellWord(tabId)} ${quoteShellWord(key)} ${quoteShellWord(value)}`);
|
|
436
|
+
}
|
|
437
|
+
async function checkPaneExists$3(paneId) {
|
|
438
|
+
try {
|
|
439
|
+
await execAsync$2(`niwa tab var list -t ${quoteShellWord(paneId)}`);
|
|
440
|
+
return true;
|
|
441
|
+
} catch (error) {
|
|
442
|
+
logger$14.debug("checkPaneExists failed: {error}", { error });
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async function focusPane$3(paneId) {
|
|
447
|
+
await execAsync$2(`niwa tab focus -t ${quoteShellWord(paneId)}`);
|
|
448
|
+
}
|
|
449
|
+
async function saveEditorPaneId$3(targetPaneId, editorPaneId) {
|
|
450
|
+
await setTabVar(targetPaneId, VAR_EDITOR_PANE, editorPaneId);
|
|
451
|
+
}
|
|
452
|
+
async function clearEditorPaneId$3(targetPaneId) {
|
|
453
|
+
await setTabVar(targetPaneId, VAR_EDITOR_PANE, "");
|
|
454
|
+
}
|
|
455
|
+
async function getEditorPaneId$3(targetPaneId) {
|
|
456
|
+
return getTabVar(targetPaneId, VAR_EDITOR_PANE);
|
|
457
|
+
}
|
|
458
|
+
async function markAsEditorPane$3(editorPaneId, targetPaneIds) {
|
|
459
|
+
const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
|
|
460
|
+
await setTabVar(editorPaneId, VAR_IS_EDITOR, "1");
|
|
461
|
+
await setTabVar(editorPaneId, VAR_TARGET_PANES, uniqueTargetPaneIds.join(","));
|
|
462
|
+
for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$3(targetPaneId, editorPaneId);
|
|
463
|
+
}
|
|
464
|
+
async function getTargetPaneIds$3(editorPaneId) {
|
|
465
|
+
const value = await getTabVar(editorPaneId, VAR_TARGET_PANES);
|
|
466
|
+
if (value === "") return [];
|
|
467
|
+
return value.split(",").map((id) => id.trim());
|
|
468
|
+
}
|
|
469
|
+
async function isEditorPane$1(paneId) {
|
|
470
|
+
return await getTabVar(paneId, VAR_IS_EDITOR) === "1";
|
|
471
|
+
}
|
|
472
|
+
async function getQuoteVariableContent$1(paneId) {
|
|
473
|
+
return getTabVar(paneId, VAR_QUOTE);
|
|
474
|
+
}
|
|
475
|
+
async function appendToQuoteVariable$1(paneId, content) {
|
|
476
|
+
const existingContent = await getQuoteVariableContent$1(paneId);
|
|
477
|
+
await setTabVar(paneId, VAR_QUOTE, existingContent.trim() !== "" ? `${existingContent}\n${content}` : content);
|
|
478
|
+
}
|
|
479
|
+
async function clearQuoteVariable$1(paneId) {
|
|
480
|
+
await setTabVar(paneId, VAR_QUOTE, "");
|
|
481
|
+
}
|
|
482
|
+
async function sendKeyToNiwatermPane(paneId, key, delay = 1e3) {
|
|
483
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
484
|
+
await execAsync$2(`niwa tab send-keys -t ${quoteShellWord(paneId)} ${quoteShellWord(key)}`);
|
|
485
|
+
}
|
|
486
|
+
async function inputToNiwatermPane(paneId, content) {
|
|
487
|
+
const chunks = splitByByteSize(content, NIWATERM_SEND_CHUNK_BYTES);
|
|
488
|
+
for (const chunk of chunks) await execAsync$2(`niwa tab send-keys -t ${quoteShellWord(paneId)} ${quoteShellWord(chunk)}`);
|
|
489
|
+
logger$14.debug("Content sent to niwaterm pane: {paneId} ({chunks} chunk(s))", {
|
|
490
|
+
paneId,
|
|
491
|
+
chunks: chunks.length
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
//#endregion
|
|
496
|
+
//#region src/modules/rex.ts
|
|
497
|
+
const logger$13 = getLogger(["editprompt", "rex"]);
|
|
498
|
+
const REX_COMMAND = "rex";
|
|
499
|
+
const REX_GLOBAL_ARGS = ["--autostart=false"];
|
|
500
|
+
function rexCommand() {
|
|
501
|
+
const binDir = process.env.REX_BIN_DIR?.trim();
|
|
502
|
+
return binDir ? join(binDir, REX_COMMAND) : REX_COMMAND;
|
|
503
|
+
}
|
|
504
|
+
function targetPaneKey(paneId) {
|
|
505
|
+
return `rex.targetPane.pane_${paneId}`;
|
|
506
|
+
}
|
|
507
|
+
function editorPaneKey(paneId) {
|
|
508
|
+
return `rex.editorPane.pane_${paneId}`;
|
|
509
|
+
}
|
|
510
|
+
function rexArgs(args) {
|
|
511
|
+
return [...REX_GLOBAL_ARGS, ...args];
|
|
512
|
+
}
|
|
513
|
+
function runRex(args, input) {
|
|
514
|
+
const command = rexCommand();
|
|
515
|
+
const argv = rexArgs(args);
|
|
516
|
+
return new Promise((resolve, reject) => {
|
|
517
|
+
const child = spawn(command, argv, { stdio: [
|
|
518
|
+
input === void 0 ? "ignore" : "pipe",
|
|
519
|
+
"pipe",
|
|
520
|
+
"pipe"
|
|
521
|
+
] });
|
|
522
|
+
let stdout = "";
|
|
523
|
+
let stderr = "";
|
|
524
|
+
child.stdout?.setEncoding("utf8");
|
|
525
|
+
child.stdout?.on("data", (chunk) => {
|
|
526
|
+
stdout += chunk;
|
|
527
|
+
});
|
|
528
|
+
child.stderr?.setEncoding("utf8");
|
|
529
|
+
child.stderr?.on("data", (chunk) => {
|
|
530
|
+
stderr += chunk;
|
|
531
|
+
});
|
|
532
|
+
child.on("error", (error) => {
|
|
533
|
+
reject(/* @__PURE__ */ new Error(`Failed to run rex: ${error.message}`));
|
|
534
|
+
});
|
|
535
|
+
child.on("close", (code) => {
|
|
536
|
+
if (code === 0) {
|
|
537
|
+
resolve(stdout);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
reject(/* @__PURE__ */ new Error(`rex ${argv.join(" ")} exited with ${code}: ${stderr.trim()}`));
|
|
541
|
+
});
|
|
542
|
+
if (input !== void 0) {
|
|
543
|
+
if (!child.stdin) {
|
|
544
|
+
reject(/* @__PURE__ */ new Error("rex was started without a standard input to write to"));
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
child.stdin.on("error", (error) => {
|
|
548
|
+
reject(/* @__PURE__ */ new Error(`Failed to write to rex: ${error.message}`));
|
|
549
|
+
});
|
|
550
|
+
child.stdin.end(input);
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
async function getCurrentPaneId$2() {
|
|
555
|
+
const paneId = process.env.REX_BLOCK?.trim();
|
|
556
|
+
if (!paneId) throw new Error("REX_BLOCK is not set");
|
|
557
|
+
return paneId;
|
|
558
|
+
}
|
|
559
|
+
async function checkPaneExists$2(paneId) {
|
|
560
|
+
try {
|
|
561
|
+
await runRex([
|
|
562
|
+
"block",
|
|
563
|
+
"inspect",
|
|
564
|
+
paneId,
|
|
565
|
+
"--json"
|
|
566
|
+
]);
|
|
567
|
+
return true;
|
|
568
|
+
} catch (error) {
|
|
569
|
+
logger$13.debug("checkPaneExists failed: {error}", { error });
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
async function focusPane$2(paneId) {
|
|
574
|
+
await runRex([
|
|
575
|
+
"block",
|
|
576
|
+
"focus",
|
|
577
|
+
paneId
|
|
578
|
+
]);
|
|
579
|
+
}
|
|
319
580
|
async function saveEditorPaneId$2(targetPaneId, editorPaneId) {
|
|
320
581
|
const data = conf.get(targetPaneKey(targetPaneId));
|
|
321
582
|
const existing = typeof data === "object" && data !== null ? data : {};
|
|
@@ -329,12 +590,8 @@ async function getEditorPaneId$2(targetPaneId) {
|
|
|
329
590
|
if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
|
|
330
591
|
return "";
|
|
331
592
|
}
|
|
332
|
-
async function clearEditorPaneId$2(targetPaneId
|
|
593
|
+
async function clearEditorPaneId$2(targetPaneId) {
|
|
333
594
|
const editorPaneId = await getEditorPaneId$2(targetPaneId);
|
|
334
|
-
if (expectedEditorPaneId && editorPaneId !== expectedEditorPaneId) {
|
|
335
|
-
conf.delete(editorPaneKey(expectedEditorPaneId));
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
595
|
const data = conf.get(targetPaneKey(targetPaneId));
|
|
339
596
|
if (typeof data === "object" && data !== null && "editorPaneId" in data) {
|
|
340
597
|
const { editorPaneId: _, ...remaining } = data;
|
|
@@ -402,6 +659,27 @@ async function clearQuoteText$1(paneId) {
|
|
|
402
659
|
else conf.set(targetPaneKey(paneId), remaining);
|
|
403
660
|
}
|
|
404
661
|
}
|
|
662
|
+
async function inputToRexPane(paneId, content) {
|
|
663
|
+
const chunks = splitByByteSize(content, REX_SEND_CHUNK_BYTES);
|
|
664
|
+
for (const chunk of chunks) await runRex([
|
|
665
|
+
"send",
|
|
666
|
+
"-b",
|
|
667
|
+
paneId
|
|
668
|
+
], chunk);
|
|
669
|
+
logger$13.debug("Content sent to Rex pane: {paneId} ({chunks} chunk(s))", {
|
|
670
|
+
paneId,
|
|
671
|
+
chunks: chunks.length
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
async function sendKeyToRexPane(paneId, key, delay = 1e3) {
|
|
675
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
676
|
+
await runRex([
|
|
677
|
+
"send-key",
|
|
678
|
+
"-b",
|
|
679
|
+
paneId,
|
|
680
|
+
key
|
|
681
|
+
]);
|
|
682
|
+
}
|
|
405
683
|
|
|
406
684
|
//#endregion
|
|
407
685
|
//#region src/modules/tmux.ts
|
|
@@ -768,10 +1046,12 @@ function processQuoteText(text, options) {
|
|
|
768
1046
|
const SUPPORTED_MUXES = [
|
|
769
1047
|
"tmux",
|
|
770
1048
|
"wezterm",
|
|
771
|
-
"herdr"
|
|
1049
|
+
"herdr",
|
|
1050
|
+
"niwaterm",
|
|
1051
|
+
"rex"
|
|
772
1052
|
];
|
|
773
1053
|
function isMuxType(value) {
|
|
774
|
-
return value === "tmux" || value === "wezterm" || value === "herdr";
|
|
1054
|
+
return value === "tmux" || value === "wezterm" || value === "herdr" || value === "niwaterm" || value === "rex";
|
|
775
1055
|
}
|
|
776
1056
|
function hasValue(value) {
|
|
777
1057
|
return typeof value === "string" && value.trim() !== "";
|
|
@@ -779,8 +1059,14 @@ function hasValue(value) {
|
|
|
779
1059
|
function isHerdrEnvironment(env = process.env) {
|
|
780
1060
|
return hasValue(env.HERDR_SOCKET_PATH) && (hasValue(env.HERDR_PANE_ID) || hasValue(env.HERDR_ACTIVE_PANE_ID));
|
|
781
1061
|
}
|
|
1062
|
+
function isNiwatermEnvironment(env = process.env) {
|
|
1063
|
+
return hasValue(env.NIWATERM_TAB_ID);
|
|
1064
|
+
}
|
|
1065
|
+
function isRexEnvironment(env = process.env) {
|
|
1066
|
+
return hasValue(env.REX_BLOCK);
|
|
1067
|
+
}
|
|
782
1068
|
function resolveMux(explicitMux, env = process.env) {
|
|
783
|
-
const muxValue = explicitMux || env.EDITPROMPT_MUX || (isHerdrEnvironment(env) ? "herdr" : "tmux");
|
|
1069
|
+
const muxValue = explicitMux || env.EDITPROMPT_MUX || (isHerdrEnvironment(env) ? "herdr" : isNiwatermEnvironment(env) ? "niwaterm" : isRexEnvironment(env) ? "rex" : "tmux");
|
|
784
1070
|
if (!isMuxType(muxValue)) {
|
|
785
1071
|
const displayValue = typeof muxValue === "string" ? muxValue : `<${typeof muxValue}>`;
|
|
786
1072
|
throw new Error(`Invalid multiplexer type '${displayValue}'. Supported values: ${SUPPORTED_MUXES.join(", ")}`);
|
|
@@ -793,7 +1079,7 @@ function resolveMux(explicitMux, env = process.env) {
|
|
|
793
1079
|
const logger$10 = getLogger(["editprompt"]);
|
|
794
1080
|
const ARG_MUX = {
|
|
795
1081
|
short: "m",
|
|
796
|
-
description: "Multiplexer type (tmux, wezterm, or
|
|
1082
|
+
description: "Multiplexer type (tmux, wezterm, herdr, niwaterm, or rex; auto-detects Herdr, niwaterm and Rex, otherwise tmux)",
|
|
797
1083
|
type: "string"
|
|
798
1084
|
};
|
|
799
1085
|
const ARG_TARGET_PANE_SINGLE = {
|
|
@@ -905,7 +1191,9 @@ async function runCollectMode(mux, targetPaneId, rawContent, outputs = ["buffer"
|
|
|
905
1191
|
const processedText = processQuoteText(selection, { withQuote });
|
|
906
1192
|
for (const output of outputs) if (output === "buffer") if (mux === "tmux") await appendToQuoteVariable(targetPaneId, processedText);
|
|
907
1193
|
else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
|
|
908
|
-
else await
|
|
1194
|
+
else if (mux === "niwaterm") await appendToQuoteVariable$1(targetPaneId, processedText);
|
|
1195
|
+
else if (mux === "rex") await appendToQuoteText$1(targetPaneId, processedText);
|
|
1196
|
+
else await appendToQuoteText$2(targetPaneId, processedText);
|
|
909
1197
|
else if (output === "stdout") process.stdout.write(processedText);
|
|
910
1198
|
} catch (error) {
|
|
911
1199
|
logger$9.error(`${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -935,7 +1223,7 @@ const collectCommand = define({
|
|
|
935
1223
|
const outputs = normalizeCollectOutputs(ctx.values.output);
|
|
936
1224
|
const withQuote = !ctx.values["no-quote"];
|
|
937
1225
|
let rawContent;
|
|
938
|
-
if (mux === "wezterm" || mux === "herdr") {
|
|
1226
|
+
if (mux === "wezterm" || mux === "herdr" || mux === "niwaterm" || mux === "rex") {
|
|
939
1227
|
rawContent = extractRawContent(ctx.rest, ctx.positionals);
|
|
940
1228
|
if (rawContent === void 0) {
|
|
941
1229
|
logger$9.error(`Text content is required for collect mode with ${mux}. Use: editprompt collect --mux ${mux} --target-pane <id> -- "<text>"`);
|
|
@@ -974,9 +1262,15 @@ async function runDumpMode() {
|
|
|
974
1262
|
} else if (config.mux === "wezterm") {
|
|
975
1263
|
currentPaneId = await getCurrentPaneId();
|
|
976
1264
|
isEditor = isEditorPaneFromConf(currentPaneId);
|
|
977
|
-
} else {
|
|
1265
|
+
} else if (config.mux === "niwaterm") {
|
|
1266
|
+
currentPaneId = await getCurrentPaneId$3();
|
|
1267
|
+
isEditor = await isEditorPane$1(currentPaneId);
|
|
1268
|
+
} else if (config.mux === "rex") {
|
|
978
1269
|
currentPaneId = await getCurrentPaneId$2();
|
|
979
1270
|
isEditor = isEditorPaneFromConf$1(currentPaneId);
|
|
1271
|
+
} else {
|
|
1272
|
+
currentPaneId = await getCurrentPaneId$4();
|
|
1273
|
+
isEditor = isEditorPaneFromConf$2(currentPaneId);
|
|
980
1274
|
}
|
|
981
1275
|
if (!isEditor) {
|
|
982
1276
|
logger$8.error("Current pane is not an editor pane");
|
|
@@ -985,7 +1279,9 @@ async function runDumpMode() {
|
|
|
985
1279
|
let targetPanes;
|
|
986
1280
|
if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
|
|
987
1281
|
else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
|
|
988
|
-
else targetPanes = await getTargetPaneIds$
|
|
1282
|
+
else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
|
|
1283
|
+
else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
|
|
1284
|
+
else targetPanes = await getTargetPaneIds$4(currentPaneId);
|
|
989
1285
|
if (targetPanes.length === 0) {
|
|
990
1286
|
logger$8.error("No target panes registered for this editor pane");
|
|
991
1287
|
process.exit(1);
|
|
@@ -999,9 +1295,15 @@ async function runDumpMode() {
|
|
|
999
1295
|
} else if (config.mux === "wezterm") {
|
|
1000
1296
|
content = await getQuoteText(targetPane);
|
|
1001
1297
|
await clearQuoteText(targetPane);
|
|
1002
|
-
} else {
|
|
1298
|
+
} else if (config.mux === "niwaterm") {
|
|
1299
|
+
content = await getQuoteVariableContent$1(targetPane);
|
|
1300
|
+
await clearQuoteVariable$1(targetPane);
|
|
1301
|
+
} else if (config.mux === "rex") {
|
|
1003
1302
|
content = await getQuoteText$1(targetPane);
|
|
1004
1303
|
await clearQuoteText$1(targetPane);
|
|
1304
|
+
} else {
|
|
1305
|
+
content = await getQuoteText$2(targetPane);
|
|
1306
|
+
await clearQuoteText$2(targetPane);
|
|
1005
1307
|
}
|
|
1006
1308
|
if (content.trim() !== "") quoteContents.push(content);
|
|
1007
1309
|
}
|
|
@@ -1048,13 +1350,17 @@ async function copyToClipboard(content) {
|
|
|
1048
1350
|
async function inputContentToPane(content, mux, targetPaneId) {
|
|
1049
1351
|
if (mux === "wezterm") await inputToWeztermPane(targetPaneId, content);
|
|
1050
1352
|
else if (mux === "herdr") await inputToHerdrPane(targetPaneId, content);
|
|
1353
|
+
else if (mux === "niwaterm") await inputToNiwatermPane(targetPaneId, content);
|
|
1354
|
+
else if (mux === "rex") await inputToRexPane(targetPaneId, content);
|
|
1051
1355
|
else await inputToTmuxPane(targetPaneId, content);
|
|
1052
1356
|
}
|
|
1053
1357
|
async function focusFirstSuccessPane(mux, targetPanes, failedPanes) {
|
|
1054
1358
|
const firstSuccessPane = targetPanes.find((p) => !failedPanes.includes(p));
|
|
1055
1359
|
if (firstSuccessPane) if (mux === "tmux") await focusPane$1(firstSuccessPane);
|
|
1056
1360
|
else if (mux === "wezterm") await focusPane(firstSuccessPane);
|
|
1057
|
-
else await focusPane$
|
|
1361
|
+
else if (mux === "niwaterm") await focusPane$3(firstSuccessPane);
|
|
1362
|
+
else if (mux === "rex") await focusPane$2(firstSuccessPane);
|
|
1363
|
+
else await focusPane$4(firstSuccessPane);
|
|
1058
1364
|
}
|
|
1059
1365
|
async function handleContentDelivery(content, mux, targetPanes) {
|
|
1060
1366
|
if (!content) return {
|
|
@@ -1131,9 +1437,15 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
|
|
|
1131
1437
|
} else if (config.mux === "wezterm") {
|
|
1132
1438
|
currentPaneId = await getCurrentPaneId();
|
|
1133
1439
|
isEditor = isEditorPaneFromConf(currentPaneId);
|
|
1134
|
-
} else {
|
|
1440
|
+
} else if (config.mux === "niwaterm") {
|
|
1441
|
+
currentPaneId = await getCurrentPaneId$3();
|
|
1442
|
+
isEditor = await isEditorPane$1(currentPaneId);
|
|
1443
|
+
} else if (config.mux === "rex") {
|
|
1135
1444
|
currentPaneId = await getCurrentPaneId$2();
|
|
1136
1445
|
isEditor = isEditorPaneFromConf$1(currentPaneId);
|
|
1446
|
+
} else {
|
|
1447
|
+
currentPaneId = await getCurrentPaneId$4();
|
|
1448
|
+
isEditor = isEditorPaneFromConf$2(currentPaneId);
|
|
1137
1449
|
}
|
|
1138
1450
|
if (!isEditor) {
|
|
1139
1451
|
logger$6.error("Current pane is not an editor pane");
|
|
@@ -1142,13 +1454,15 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
|
|
|
1142
1454
|
let targetPanes;
|
|
1143
1455
|
if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
|
|
1144
1456
|
else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
|
|
1145
|
-
else targetPanes = await getTargetPaneIds$
|
|
1457
|
+
else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
|
|
1458
|
+
else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
|
|
1459
|
+
else targetPanes = await getTargetPaneIds$4(currentPaneId);
|
|
1146
1460
|
if (targetPanes.length === 0) {
|
|
1147
1461
|
logger$6.error("No target panes registered for this editor pane");
|
|
1148
1462
|
process.exit(1);
|
|
1149
1463
|
}
|
|
1150
1464
|
if (autoSend) {
|
|
1151
|
-
const key = sendKey || (config.mux === "wezterm" ? "\\r" : config.mux === "herdr" ? "enter" : "C-m");
|
|
1465
|
+
const key = sendKey || (config.mux === "wezterm" ? "\\r" : config.mux === "herdr" || config.mux === "rex" ? "enter" : "C-m");
|
|
1152
1466
|
const delay = /\.(png|webp|avif|jpe?g|gif)\b/i.test(content) ? sendKeyDelay ?? 1e3 : 200;
|
|
1153
1467
|
let successCount = 0;
|
|
1154
1468
|
for (const targetPane of targetPanes) try {
|
|
@@ -1158,6 +1472,12 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
|
|
|
1158
1472
|
} else if (config.mux === "herdr") {
|
|
1159
1473
|
await inputToHerdrPane(targetPane, content);
|
|
1160
1474
|
await sendKeyToHerdrPane(targetPane, key, delay);
|
|
1475
|
+
} else if (config.mux === "niwaterm") {
|
|
1476
|
+
await inputToNiwatermPane(targetPane, content);
|
|
1477
|
+
await sendKeyToNiwatermPane(targetPane, key, delay);
|
|
1478
|
+
} else if (config.mux === "rex") {
|
|
1479
|
+
await inputToRexPane(targetPane, content);
|
|
1480
|
+
await sendKeyToRexPane(targetPane, key, delay);
|
|
1161
1481
|
} else {
|
|
1162
1482
|
await inputToTmuxPane(targetPane, content);
|
|
1163
1483
|
await sendKeyToTmuxPane(targetPane, key, delay);
|
|
@@ -1325,8 +1645,16 @@ async function runOpenEditorMode(options) {
|
|
|
1325
1645
|
await markAsEditorPane(currentPaneId, options.targetPanes);
|
|
1326
1646
|
} catch {}
|
|
1327
1647
|
else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
|
|
1328
|
-
const currentPaneId = await getCurrentPaneId$
|
|
1648
|
+
const currentPaneId = await getCurrentPaneId$4();
|
|
1329
1649
|
herdrEditorPaneId = currentPaneId;
|
|
1650
|
+
await markAsEditorPane$4(currentPaneId, options.targetPanes);
|
|
1651
|
+
} catch {}
|
|
1652
|
+
else if (options.targetPanes.length > 0 && options.mux === "niwaterm") try {
|
|
1653
|
+
const currentPaneId = await getCurrentPaneId$3();
|
|
1654
|
+
await markAsEditorPane$3(currentPaneId, options.targetPanes);
|
|
1655
|
+
} catch {}
|
|
1656
|
+
else if (options.targetPanes.length > 0 && options.mux === "rex") try {
|
|
1657
|
+
const currentPaneId = await getCurrentPaneId$2();
|
|
1330
1658
|
await markAsEditorPane$2(currentPaneId, options.targetPanes);
|
|
1331
1659
|
} catch {}
|
|
1332
1660
|
try {
|
|
@@ -1363,7 +1691,13 @@ async function runOpenEditorMode(options) {
|
|
|
1363
1691
|
for (const targetPane of options.targetPanes) await clearEditorPaneId(targetPane);
|
|
1364
1692
|
} catch {}
|
|
1365
1693
|
else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
|
|
1366
|
-
for (const targetPane of options.targetPanes) await clearEditorPaneId$
|
|
1694
|
+
for (const targetPane of options.targetPanes) await clearEditorPaneId$4(targetPane, herdrEditorPaneId);
|
|
1695
|
+
} catch {}
|
|
1696
|
+
else if (options.targetPanes.length > 0 && options.mux === "niwaterm") try {
|
|
1697
|
+
for (const targetPane of options.targetPanes) await clearEditorPaneId$3(targetPane);
|
|
1698
|
+
} catch {}
|
|
1699
|
+
else if (options.targetPanes.length > 0 && options.mux === "rex") try {
|
|
1700
|
+
for (const targetPane of options.targetPanes) await clearEditorPaneId$2(targetPane);
|
|
1367
1701
|
} catch {}
|
|
1368
1702
|
}
|
|
1369
1703
|
}
|
|
@@ -1419,6 +1753,18 @@ async function runRegisterMode(options) {
|
|
|
1419
1753
|
process.exit(1);
|
|
1420
1754
|
}
|
|
1421
1755
|
} else if (options.mux === "herdr") {
|
|
1756
|
+
editorPaneId = await getCurrentPaneId$4();
|
|
1757
|
+
if (!isEditorPaneFromConf$2(editorPaneId)) {
|
|
1758
|
+
logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
|
|
1759
|
+
process.exit(1);
|
|
1760
|
+
}
|
|
1761
|
+
} else if (options.mux === "niwaterm") {
|
|
1762
|
+
editorPaneId = await getCurrentPaneId$3();
|
|
1763
|
+
if (!await isEditorPane$1(editorPaneId)) {
|
|
1764
|
+
logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
|
|
1765
|
+
process.exit(1);
|
|
1766
|
+
}
|
|
1767
|
+
} else if (options.mux === "rex") {
|
|
1422
1768
|
editorPaneId = await getCurrentPaneId$2();
|
|
1423
1769
|
if (!isEditorPaneFromConf$1(editorPaneId)) {
|
|
1424
1770
|
logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
|
|
@@ -1432,11 +1778,15 @@ async function runRegisterMode(options) {
|
|
|
1432
1778
|
let existingPanes = [];
|
|
1433
1779
|
if (options.mux === "tmux") existingPanes = await getTargetPaneIds$1(editorPaneId);
|
|
1434
1780
|
else if (options.mux === "wezterm") existingPanes = await getTargetPaneIds(editorPaneId);
|
|
1435
|
-
else if (options.mux === "herdr") existingPanes = await getTargetPaneIds$
|
|
1781
|
+
else if (options.mux === "herdr") existingPanes = await getTargetPaneIds$4(editorPaneId);
|
|
1782
|
+
else if (options.mux === "niwaterm") existingPanes = await getTargetPaneIds$3(editorPaneId);
|
|
1783
|
+
else if (options.mux === "rex") existingPanes = await getTargetPaneIds$2(editorPaneId);
|
|
1436
1784
|
const mergedTargetPanes = [...new Set([...existingPanes, ...options.targetPanes])];
|
|
1437
1785
|
if (options.mux === "tmux") await markAsEditorPane$1(editorPaneId, mergedTargetPanes);
|
|
1438
1786
|
else if (options.mux === "wezterm") await markAsEditorPane(editorPaneId, mergedTargetPanes);
|
|
1439
|
-
else if (options.mux === "herdr") await markAsEditorPane$
|
|
1787
|
+
else if (options.mux === "herdr") await markAsEditorPane$4(editorPaneId, mergedTargetPanes);
|
|
1788
|
+
else if (options.mux === "niwaterm") await markAsEditorPane$3(editorPaneId, mergedTargetPanes);
|
|
1789
|
+
else if (options.mux === "rex") await markAsEditorPane$2(editorPaneId, mergedTargetPanes);
|
|
1440
1790
|
logger$4.info(`Editor pane ${editorPaneId} registered with target panes: ${mergedTargetPanes.join(", ")}`);
|
|
1441
1791
|
} catch (error) {
|
|
1442
1792
|
logger$4.error(`${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -1477,11 +1827,61 @@ const registerCommand = define({
|
|
|
1477
1827
|
const logger$3 = getLogger(["editprompt", "resume"]);
|
|
1478
1828
|
async function runResumeMode(targetPane, mux) {
|
|
1479
1829
|
if (mux === "herdr") try {
|
|
1480
|
-
process.exit(await resumeEditorPane(targetPane) ? 0 : 1);
|
|
1830
|
+
process.exit(await resumeEditorPane$1(targetPane) ? 0 : 1);
|
|
1481
1831
|
} catch (error) {
|
|
1482
1832
|
logger$3.debug("Herdr resume failed: {error}", { error });
|
|
1483
1833
|
process.exit(1);
|
|
1484
1834
|
}
|
|
1835
|
+
if (mux === "rex") try {
|
|
1836
|
+
process.exit(await resumeEditorPane(targetPane) ? 0 : 1);
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
logger$3.debug("Rex resume failed: {error}", { error });
|
|
1839
|
+
process.exit(1);
|
|
1840
|
+
}
|
|
1841
|
+
if (mux === "niwaterm") {
|
|
1842
|
+
const currentPaneId = await getCurrentPaneId$3();
|
|
1843
|
+
if (await isEditorPane$1(currentPaneId)) {
|
|
1844
|
+
logger$3.debug("Current pane is an editor pane");
|
|
1845
|
+
const originalTargetPaneIds = await getTargetPaneIds$3(currentPaneId);
|
|
1846
|
+
if (originalTargetPaneIds.length === 0) {
|
|
1847
|
+
logger$3.debug("No target pane IDs found for editor pane");
|
|
1848
|
+
process.exit(1);
|
|
1849
|
+
}
|
|
1850
|
+
let focused = false;
|
|
1851
|
+
for (const paneId of originalTargetPaneIds) if (await checkPaneExists$3(paneId)) {
|
|
1852
|
+
await focusPane$3(paneId);
|
|
1853
|
+
focused = true;
|
|
1854
|
+
break;
|
|
1855
|
+
}
|
|
1856
|
+
if (!focused) {
|
|
1857
|
+
logger$3.debug("All target panes do not exist");
|
|
1858
|
+
process.exit(1);
|
|
1859
|
+
}
|
|
1860
|
+
process.exit(0);
|
|
1861
|
+
}
|
|
1862
|
+
logger$3.debug("Current pane is not an editor pane");
|
|
1863
|
+
const editorPaneId = await getEditorPaneId$3(targetPane);
|
|
1864
|
+
logger$3.debug("niwaterm editorPaneId: {editorPaneId}", { editorPaneId });
|
|
1865
|
+
if (editorPaneId === "") {
|
|
1866
|
+
logger$3.debug("Editor pane ID not found");
|
|
1867
|
+
process.exit(1);
|
|
1868
|
+
}
|
|
1869
|
+
if (!await checkPaneExists$3(editorPaneId)) {
|
|
1870
|
+
logger$3.debug("Editor pane does not exist");
|
|
1871
|
+
await clearEditorPaneId$3(targetPane);
|
|
1872
|
+
process.exit(1);
|
|
1873
|
+
}
|
|
1874
|
+
try {
|
|
1875
|
+
await focusPane$3(editorPaneId);
|
|
1876
|
+
process.exit(0);
|
|
1877
|
+
} catch (error) {
|
|
1878
|
+
logger$3.debug("Can't focus editorPaneId: {editorPaneId}, error: {error}", {
|
|
1879
|
+
editorPaneId,
|
|
1880
|
+
error
|
|
1881
|
+
});
|
|
1882
|
+
process.exit(1);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1485
1885
|
if (mux === "wezterm") {
|
|
1486
1886
|
const currentPaneId = await getCurrentPaneId();
|
|
1487
1887
|
if (isEditorPaneFromConf(currentPaneId)) {
|
|
@@ -1585,9 +1985,15 @@ async function runPressMode(key, delay = 0) {
|
|
|
1585
1985
|
} else if (config.mux === "wezterm") {
|
|
1586
1986
|
currentPaneId = await getCurrentPaneId();
|
|
1587
1987
|
isEditor = isEditorPaneFromConf(currentPaneId);
|
|
1588
|
-
} else {
|
|
1988
|
+
} else if (config.mux === "niwaterm") {
|
|
1989
|
+
currentPaneId = await getCurrentPaneId$3();
|
|
1990
|
+
isEditor = await isEditorPane$1(currentPaneId);
|
|
1991
|
+
} else if (config.mux === "rex") {
|
|
1589
1992
|
currentPaneId = await getCurrentPaneId$2();
|
|
1590
1993
|
isEditor = isEditorPaneFromConf$1(currentPaneId);
|
|
1994
|
+
} else {
|
|
1995
|
+
currentPaneId = await getCurrentPaneId$4();
|
|
1996
|
+
isEditor = isEditorPaneFromConf$2(currentPaneId);
|
|
1591
1997
|
}
|
|
1592
1998
|
if (!isEditor) {
|
|
1593
1999
|
logger$2.error("Current pane is not an editor pane");
|
|
@@ -1596,7 +2002,9 @@ async function runPressMode(key, delay = 0) {
|
|
|
1596
2002
|
let targetPanes;
|
|
1597
2003
|
if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
|
|
1598
2004
|
else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
|
|
1599
|
-
else targetPanes = await getTargetPaneIds$
|
|
2005
|
+
else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
|
|
2006
|
+
else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
|
|
2007
|
+
else targetPanes = await getTargetPaneIds$4(currentPaneId);
|
|
1600
2008
|
if (targetPanes.length === 0) {
|
|
1601
2009
|
logger$2.error("No target panes registered for this editor pane");
|
|
1602
2010
|
process.exit(1);
|
|
@@ -1605,6 +2013,8 @@ async function runPressMode(key, delay = 0) {
|
|
|
1605
2013
|
for (const targetPane of targetPanes) try {
|
|
1606
2014
|
if (config.mux === "wezterm") await sendKeyToWeztermPane(targetPane, key, delay);
|
|
1607
2015
|
else if (config.mux === "herdr") await sendKeyToHerdrPane(targetPane, key, delay);
|
|
2016
|
+
else if (config.mux === "niwaterm") await sendKeyToNiwatermPane(targetPane, key, delay);
|
|
2017
|
+
else if (config.mux === "rex") await sendKeyToRexPane(targetPane, key, delay);
|
|
1608
2018
|
else await sendKeyToTmuxPane(targetPane, key, delay);
|
|
1609
2019
|
successCount++;
|
|
1610
2020
|
} catch (error) {
|
|
@@ -1698,9 +2108,15 @@ async function getTargetPaneForStash() {
|
|
|
1698
2108
|
} else if (config.mux === "wezterm") {
|
|
1699
2109
|
currentPaneId = await getCurrentPaneId();
|
|
1700
2110
|
isEditor = isEditorPaneFromConf(currentPaneId);
|
|
1701
|
-
} else {
|
|
2111
|
+
} else if (config.mux === "niwaterm") {
|
|
2112
|
+
currentPaneId = await getCurrentPaneId$3();
|
|
2113
|
+
isEditor = await isEditorPane$1(currentPaneId);
|
|
2114
|
+
} else if (config.mux === "rex") {
|
|
1702
2115
|
currentPaneId = await getCurrentPaneId$2();
|
|
1703
2116
|
isEditor = isEditorPaneFromConf$1(currentPaneId);
|
|
2117
|
+
} else {
|
|
2118
|
+
currentPaneId = await getCurrentPaneId$4();
|
|
2119
|
+
isEditor = isEditorPaneFromConf$2(currentPaneId);
|
|
1704
2120
|
}
|
|
1705
2121
|
if (!isEditor) {
|
|
1706
2122
|
logger$1.error("Current pane is not an editor pane");
|
|
@@ -1709,7 +2125,9 @@ async function getTargetPaneForStash() {
|
|
|
1709
2125
|
let targetPanes;
|
|
1710
2126
|
if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
|
|
1711
2127
|
else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
|
|
1712
|
-
else targetPanes = await getTargetPaneIds$
|
|
2128
|
+
else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
|
|
2129
|
+
else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
|
|
2130
|
+
else targetPanes = await getTargetPaneIds$4(currentPaneId);
|
|
1713
2131
|
if (targetPanes.length === 0) {
|
|
1714
2132
|
logger$1.error("No target panes registered for this editor pane");
|
|
1715
2133
|
process.exit(1);
|