vde-layout 0.0.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/dist/index.js +187 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,6 +112,8 @@ layout:
|
|
|
112
112
|
focus: true # optional; only one pane should be true
|
|
113
113
|
delay: 500 # optional; wait (ms) before running command
|
|
114
114
|
title: "Server" # optional; tmux pane title
|
|
115
|
+
ephemeral: true # optional; close pane after command completes
|
|
116
|
+
closeOnError: false # optional; if ephemeral, close on error (default: false)
|
|
115
117
|
- type: vertical # nested split
|
|
116
118
|
ratio: [1, 1]
|
|
117
119
|
panes:
|
|
@@ -119,6 +121,71 @@ layout:
|
|
|
119
121
|
- name: "shell"
|
|
120
122
|
```
|
|
121
123
|
|
|
124
|
+
### Template Tokens
|
|
125
|
+
You can reference dynamically-assigned pane IDs within pane commands using template tokens. These tokens are resolved after the layout finishes splitting panes but before commands execute:
|
|
126
|
+
|
|
127
|
+
- **`{{this_pane}}`** – References the current pane receiving the command
|
|
128
|
+
- **`{{focus_pane}}`** – References the pane that will receive focus
|
|
129
|
+
- **`{{pane_id:<name>}}`** – References a specific pane by its name
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
```yaml
|
|
133
|
+
presets:
|
|
134
|
+
cross-pane-demo:
|
|
135
|
+
name: Cross Pane Coordination
|
|
136
|
+
layout:
|
|
137
|
+
type: vertical
|
|
138
|
+
ratio: [2, 1]
|
|
139
|
+
panes:
|
|
140
|
+
- name: editor
|
|
141
|
+
command: 'echo "Editor pane ID: {{this_pane}}"'
|
|
142
|
+
focus: true
|
|
143
|
+
- name: terminal
|
|
144
|
+
command: 'echo "I can reference the editor pane: {{pane_id:editor}}"'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Common use cases:**
|
|
148
|
+
- Send commands to other panes: `tmux send-keys -t {{pane_id:editor}} "npm test" Enter`
|
|
149
|
+
- Display pane information for debugging: `echo "Current: {{this_pane}}, Focus: {{focus_pane}}"`
|
|
150
|
+
- Coordinate tasks across multiple panes within your preset configuration
|
|
151
|
+
|
|
152
|
+
### Ephemeral Panes
|
|
153
|
+
Ephemeral panes automatically close after their command completes. This is useful for one-time tasks like builds, tests, or initialization scripts.
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
panes:
|
|
157
|
+
- name: build
|
|
158
|
+
command: npm run build
|
|
159
|
+
ephemeral: true # Pane closes when command finishes
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Error handling:**
|
|
163
|
+
- By default, ephemeral panes remain open if the command fails, allowing you to inspect errors
|
|
164
|
+
- Set `closeOnError: true` to close the pane regardless of success or failure
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
panes:
|
|
168
|
+
- name: quick-test
|
|
169
|
+
command: npm test
|
|
170
|
+
ephemeral: true
|
|
171
|
+
closeOnError: false # Default: stays open on error
|
|
172
|
+
|
|
173
|
+
- name: build-and-exit
|
|
174
|
+
command: npm run build
|
|
175
|
+
ephemeral: true
|
|
176
|
+
closeOnError: true # Closes even if build fails
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Combining with template tokens:**
|
|
180
|
+
```yaml
|
|
181
|
+
panes:
|
|
182
|
+
- name: editor
|
|
183
|
+
command: nvim
|
|
184
|
+
- name: test-runner
|
|
185
|
+
command: 'tmux send-keys -t {{pane_id:editor}} ":!npm test" Enter'
|
|
186
|
+
ephemeral: true # Run once and close
|
|
187
|
+
```
|
|
188
|
+
|
|
122
189
|
### Ratio Normalization
|
|
123
190
|
Ratios can be any set of positive integers. vde-layout normalizes them to percentages:
|
|
124
191
|
- `[1, 1]` → `[50, 50]`
|
package/dist/index.js
CHANGED
|
@@ -120,7 +120,9 @@ const TerminalPaneSchema = z.object({
|
|
|
120
120
|
env: z.record(z.string()).optional(),
|
|
121
121
|
delay: z.number().int().positive().optional(),
|
|
122
122
|
title: z.string().optional(),
|
|
123
|
-
focus: z.boolean().optional()
|
|
123
|
+
focus: z.boolean().optional(),
|
|
124
|
+
ephemeral: z.boolean().optional(),
|
|
125
|
+
closeOnError: z.boolean().optional()
|
|
124
126
|
}).strict();
|
|
125
127
|
const SplitPaneSchema = z.lazy(() => z.object({
|
|
126
128
|
type: z.enum(["horizontal", "vertical"]),
|
|
@@ -730,6 +732,70 @@ const isFunctionalCoreError = (value) => {
|
|
|
730
732
|
return (candidate.kind === "compile" || candidate.kind === "plan" || candidate.kind === "emit" || candidate.kind === "execution") && typeof candidate.code === "string" && typeof candidate.message === "string";
|
|
731
733
|
};
|
|
732
734
|
|
|
735
|
+
//#endregion
|
|
736
|
+
//#region src/utils/template-tokens.ts
|
|
737
|
+
const TemplateTokenErrorImpl = function TemplateTokenError$1(message, tokenType, availablePanes) {
|
|
738
|
+
const error = new Error(message);
|
|
739
|
+
Object.setPrototypeOf(error, TemplateTokenErrorImpl.prototype);
|
|
740
|
+
error.name = "TemplateTokenError";
|
|
741
|
+
error.tokenType = tokenType;
|
|
742
|
+
error.availablePanes = availablePanes;
|
|
743
|
+
return error;
|
|
744
|
+
};
|
|
745
|
+
TemplateTokenErrorImpl.prototype = Object.create(Error.prototype);
|
|
746
|
+
TemplateTokenErrorImpl.prototype.constructor = TemplateTokenErrorImpl;
|
|
747
|
+
const TemplateTokenError = TemplateTokenErrorImpl;
|
|
748
|
+
/**
|
|
749
|
+
* Replaces template tokens in a command string with actual pane IDs.
|
|
750
|
+
*
|
|
751
|
+
* Uses a single-pass regex replacement to avoid nested token issues.
|
|
752
|
+
* All tokens are replaced in a single pass, preventing already-replaced
|
|
753
|
+
* values from being re-processed.
|
|
754
|
+
*
|
|
755
|
+
* @param input - Object containing the command and pane ID mappings
|
|
756
|
+
* @returns The command string with all template tokens replaced
|
|
757
|
+
* @throws TemplateTokenError if a referenced pane name is not found in the mapping
|
|
758
|
+
*/
|
|
759
|
+
const replaceTemplateTokens = ({ command, currentPaneRealId, focusPaneRealId, nameToRealIdMap }) => {
|
|
760
|
+
return command.replace(/\{\{(this_pane|focus_pane|pane_id:([^}]+))\}\}/g, (match, tokenContent, paneName) => {
|
|
761
|
+
if (tokenContent === "this_pane") return currentPaneRealId;
|
|
762
|
+
if (tokenContent === "focus_pane") return focusPaneRealId;
|
|
763
|
+
if (tokenContent.startsWith("pane_id:") && paneName !== void 0) {
|
|
764
|
+
const trimmedName = paneName.trim();
|
|
765
|
+
const paneId = nameToRealIdMap.get(trimmedName);
|
|
766
|
+
if (paneId === void 0) throw new TemplateTokenError(`Pane name "${trimmedName}" not found. Available panes: ${Array.from(nameToRealIdMap.keys()).join(", ")}`, "pane_id", Array.from(nameToRealIdMap.keys()));
|
|
767
|
+
return paneId;
|
|
768
|
+
}
|
|
769
|
+
return match;
|
|
770
|
+
});
|
|
771
|
+
};
|
|
772
|
+
/**
|
|
773
|
+
* Builds a mapping from pane names to real pane IDs.
|
|
774
|
+
*
|
|
775
|
+
* **Duplicate Name Handling:**
|
|
776
|
+
* If multiple panes share the same name, the last one in the terminals
|
|
777
|
+
* array wins. This follows the iteration order of the layout tree.
|
|
778
|
+
* It's recommended to use unique names for panes to avoid ambiguity
|
|
779
|
+
* in template token references.
|
|
780
|
+
*
|
|
781
|
+
* **Virtual to Real ID Resolution:**
|
|
782
|
+
* Only panes with successfully resolved real IDs (present in paneMap)
|
|
783
|
+
* are included in the resulting map. This ensures that template tokens
|
|
784
|
+
* only reference panes that have been properly created.
|
|
785
|
+
*
|
|
786
|
+
* @param terminals - Array of emitted terminals from the layout plan
|
|
787
|
+
* @param paneMap - Map from virtual pane IDs to real pane IDs (backend-specific)
|
|
788
|
+
* @returns A map from pane names to real pane IDs
|
|
789
|
+
*/
|
|
790
|
+
const buildNameToRealIdMap = (terminals, paneMap) => {
|
|
791
|
+
const nameToRealIdMap = /* @__PURE__ */ new Map();
|
|
792
|
+
for (const terminal of terminals) {
|
|
793
|
+
const realId = paneMap.get(terminal.virtualPaneId);
|
|
794
|
+
if (realId !== void 0) nameToRealIdMap.set(terminal.name, realId);
|
|
795
|
+
}
|
|
796
|
+
return nameToRealIdMap;
|
|
797
|
+
};
|
|
798
|
+
|
|
733
799
|
//#endregion
|
|
734
800
|
//#region src/executor/plan-runner.ts
|
|
735
801
|
const DOUBLE_QUOTE = "\"";
|
|
@@ -812,7 +878,8 @@ const executePlan = async ({ emission, executor, windowName, windowMode, onConfi
|
|
|
812
878
|
await executeTerminalCommands({
|
|
813
879
|
terminals: emission.terminals,
|
|
814
880
|
executor,
|
|
815
|
-
paneMap
|
|
881
|
+
paneMap,
|
|
882
|
+
focusPaneVirtualId: emission.summary.focusPaneId
|
|
816
883
|
});
|
|
817
884
|
const finalRealFocus = resolvePaneId(paneMap, emission.summary.focusPaneId);
|
|
818
885
|
if (typeof finalRealFocus === "string" && finalRealFocus.length > 0) await executeCommand(executor, [
|
|
@@ -868,7 +935,16 @@ const executeFocusStep = async ({ step, executor, paneMap }) => {
|
|
|
868
935
|
details: { command }
|
|
869
936
|
});
|
|
870
937
|
};
|
|
871
|
-
const executeTerminalCommands = async ({ terminals, executor, paneMap }) => {
|
|
938
|
+
const executeTerminalCommands = async ({ terminals, executor, paneMap, focusPaneVirtualId }) => {
|
|
939
|
+
const nameToRealIdMap = buildNameToRealIdMap(terminals, paneMap);
|
|
940
|
+
if (!paneMap.has(focusPaneVirtualId)) raiseExecutionError("UNKNOWN_PANE", {
|
|
941
|
+
message: `Unknown focus pane: ${focusPaneVirtualId}`,
|
|
942
|
+
path: focusPaneVirtualId
|
|
943
|
+
});
|
|
944
|
+
const focusPaneRealId = ensureNonEmpty(resolvePaneId(paneMap, focusPaneVirtualId), () => raiseExecutionError("UNKNOWN_PANE", {
|
|
945
|
+
message: `Unknown focus pane: ${focusPaneVirtualId}`,
|
|
946
|
+
path: focusPaneVirtualId
|
|
947
|
+
}));
|
|
872
948
|
for (const terminal of terminals) {
|
|
873
949
|
const realPaneId = ensureNonEmpty(resolvePaneId(paneMap, terminal.virtualPaneId), () => raiseExecutionError("UNKNOWN_PANE", {
|
|
874
950
|
message: `Unknown terminal pane: ${terminal.virtualPaneId}`,
|
|
@@ -903,18 +979,44 @@ const executeTerminalCommands = async ({ terminals, executor, paneMap }) => {
|
|
|
903
979
|
path: terminal.virtualPaneId
|
|
904
980
|
});
|
|
905
981
|
}
|
|
906
|
-
if (typeof terminal.command === "string" && terminal.command.length > 0)
|
|
907
|
-
"
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
982
|
+
if (typeof terminal.command === "string" && terminal.command.length > 0) {
|
|
983
|
+
const focusPaneRealIdForCommand = terminal.command.includes("{{focus_pane}}") ? focusPaneRealId : "";
|
|
984
|
+
let commandWithTokensReplaced;
|
|
985
|
+
try {
|
|
986
|
+
commandWithTokensReplaced = replaceTemplateTokens({
|
|
987
|
+
command: terminal.command,
|
|
988
|
+
currentPaneRealId: realPaneId,
|
|
989
|
+
focusPaneRealId: focusPaneRealIdForCommand,
|
|
990
|
+
nameToRealIdMap
|
|
991
|
+
});
|
|
992
|
+
} catch (error) {
|
|
993
|
+
if (error instanceof TemplateTokenError) throw createFunctionalError("execution", {
|
|
994
|
+
code: "TEMPLATE_TOKEN_ERROR",
|
|
995
|
+
message: `Template token resolution failed for pane ${terminal.virtualPaneId}: ${error.message}`,
|
|
996
|
+
path: terminal.virtualPaneId,
|
|
997
|
+
details: {
|
|
998
|
+
command: terminal.command,
|
|
999
|
+
tokenType: error.tokenType,
|
|
1000
|
+
availablePanes: error.availablePanes
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
throw error;
|
|
1004
|
+
}
|
|
1005
|
+
if (terminal.ephemeral === true) if (terminal.closeOnError === true) commandWithTokensReplaced = `${commandWithTokensReplaced}; exit`;
|
|
1006
|
+
else commandWithTokensReplaced = `${commandWithTokensReplaced}; [ $? -eq 0 ] && exit`;
|
|
1007
|
+
await executeCommand(executor, [
|
|
1008
|
+
"send-keys",
|
|
1009
|
+
"-t",
|
|
1010
|
+
realPaneId,
|
|
1011
|
+
commandWithTokensReplaced,
|
|
1012
|
+
"Enter"
|
|
1013
|
+
], {
|
|
1014
|
+
code: ErrorCodes.TMUX_COMMAND_FAILED,
|
|
1015
|
+
message: `Failed to execute command for pane ${terminal.virtualPaneId}`,
|
|
1016
|
+
path: terminal.virtualPaneId,
|
|
1017
|
+
details: { command: terminal.command }
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
918
1020
|
}
|
|
919
1021
|
};
|
|
920
1022
|
const executeCommand = async (executor, command, context) => {
|
|
@@ -1398,20 +1500,22 @@ const buildDryRunSteps = (emission) => {
|
|
|
1398
1500
|
return steps;
|
|
1399
1501
|
};
|
|
1400
1502
|
const resolveCurrentWindow = async (context) => {
|
|
1401
|
-
const
|
|
1503
|
+
const preferredPaneId = typeof context.preferredPaneId === "string" && context.preferredPaneId.length > 0 ? context.preferredPaneId : void 0;
|
|
1504
|
+
const preferredWindowId = preferredPaneId !== void 0 ? findWindowContainingPane(context.list, preferredPaneId) : void 0;
|
|
1505
|
+
const activeWindow = (preferredWindowId !== void 0 ? context.list.windows.find((window) => window.windowId === preferredWindowId) : void 0) ?? context.list.windows.find((window) => window.isActive) ?? context.list.windows[0];
|
|
1402
1506
|
if (!activeWindow) throw createFunctionalError("execution", {
|
|
1403
1507
|
code: ErrorCodes.TERMINAL_COMMAND_FAILED,
|
|
1404
1508
|
message: "No active wezterm window detected",
|
|
1405
1509
|
details: { hint: "Launch wezterm and ensure a window is focused, or run with --new-window." }
|
|
1406
1510
|
});
|
|
1407
|
-
const activeTab = activeWindow.tabs.find((tab) => tab.isActive) ?? activeWindow.tabs[0];
|
|
1511
|
+
const activeTab = (preferredPaneId !== void 0 ? activeWindow.tabs.find((tab) => tab.panes.some((pane) => pane.paneId === preferredPaneId)) : void 0) ?? activeWindow.tabs.find((tab) => tab.isActive) ?? activeWindow.tabs[0];
|
|
1408
1512
|
if (!activeTab) throw createFunctionalError("execution", {
|
|
1409
1513
|
code: ErrorCodes.TERMINAL_COMMAND_FAILED,
|
|
1410
1514
|
message: "No active wezterm tab detected",
|
|
1411
1515
|
path: activeWindow.windowId,
|
|
1412
1516
|
details: { hint: "Ensure a wezterm tab is focused before using --current-window." }
|
|
1413
1517
|
});
|
|
1414
|
-
const activePane = activeTab.panes.find((pane) => pane.isActive) ?? activeTab.panes[0];
|
|
1518
|
+
const activePane = (preferredPaneId !== void 0 ? activeTab.panes.find((pane) => pane.paneId === preferredPaneId) : void 0) ?? activeTab.panes.find((pane) => pane.isActive) ?? activeTab.panes[0];
|
|
1415
1519
|
if (!activePane) throw createFunctionalError("execution", {
|
|
1416
1520
|
code: ErrorCodes.TERMINAL_COMMAND_FAILED,
|
|
1417
1521
|
message: "No active wezterm pane detected",
|
|
@@ -1485,7 +1589,8 @@ const waitForPaneRegistration = async ({ paneId, listWindows, windowHint }) => {
|
|
|
1485
1589
|
}
|
|
1486
1590
|
});
|
|
1487
1591
|
};
|
|
1488
|
-
const resolveInitialPane = async ({ windowMode, prompt, dryRun, listWindows, runCommand, logCommand, initialCwd, workspaceHint, initialList }) => {
|
|
1592
|
+
const resolveInitialPane = async ({ windowMode, prompt, dryRun, listWindows, runCommand, logCommand, initialCwd, workspaceHint, initialList, preferredPaneId }) => {
|
|
1593
|
+
const preferredPaneIdValue = typeof preferredPaneId === "string" && preferredPaneId.length > 0 ? preferredPaneId : void 0;
|
|
1489
1594
|
if (windowMode === "current-window") {
|
|
1490
1595
|
const snapshot = initialList ?? await listWindows();
|
|
1491
1596
|
const scoped = filterWindowsByWorkspace(snapshot, workspaceHint);
|
|
@@ -1493,12 +1598,14 @@ const resolveInitialPane = async ({ windowMode, prompt, dryRun, listWindows, run
|
|
|
1493
1598
|
list: scoped,
|
|
1494
1599
|
prompt,
|
|
1495
1600
|
dryRun,
|
|
1496
|
-
logCommand
|
|
1601
|
+
logCommand,
|
|
1602
|
+
preferredPaneId: preferredPaneIdValue
|
|
1497
1603
|
});
|
|
1498
1604
|
}
|
|
1499
1605
|
const existingSnapshot = initialList ?? await listWindows();
|
|
1500
1606
|
const scopedExisting = filterWindowsByWorkspace(existingSnapshot, workspaceHint);
|
|
1501
|
-
const
|
|
1607
|
+
const scopedPreferredWindowId = preferredPaneIdValue !== void 0 ? findWindowContainingPane(scopedExisting, preferredPaneIdValue) : void 0;
|
|
1608
|
+
const activeWindow = (scopedPreferredWindowId !== void 0 ? scopedExisting.windows.find((window) => window.windowId === scopedPreferredWindowId) : void 0) ?? findActiveWindow(scopedExisting);
|
|
1502
1609
|
if (activeWindow) {
|
|
1503
1610
|
const args$1 = [
|
|
1504
1611
|
"spawn",
|
|
@@ -1618,7 +1725,14 @@ const sendTextToPane = async ({ paneId, text, runCommand, context }) => {
|
|
|
1618
1725
|
appendCarriageReturn(text)
|
|
1619
1726
|
], context);
|
|
1620
1727
|
};
|
|
1621
|
-
const applyTerminalCommands = async ({ terminals, paneMap, runCommand }) => {
|
|
1728
|
+
const applyTerminalCommands = async ({ terminals, paneMap, runCommand, focusPaneVirtualId }) => {
|
|
1729
|
+
const nameToRealIdMap = buildNameToRealIdMap(terminals, paneMap);
|
|
1730
|
+
if (!paneMap.has(focusPaneVirtualId)) throw createFunctionalError("execution", {
|
|
1731
|
+
code: ErrorCodes.INVALID_PANE,
|
|
1732
|
+
message: `Unknown focus pane: ${focusPaneVirtualId}`,
|
|
1733
|
+
path: focusPaneVirtualId
|
|
1734
|
+
});
|
|
1735
|
+
const focusPaneRealId = resolveRealPaneId(paneMap, focusPaneVirtualId, { stepId: focusPaneVirtualId });
|
|
1622
1736
|
for (const terminal of terminals) {
|
|
1623
1737
|
const realPaneId = resolveRealPaneId(paneMap, terminal.virtualPaneId, { stepId: terminal.virtualPaneId });
|
|
1624
1738
|
if (typeof terminal.cwd === "string" && terminal.cwd.length > 0) {
|
|
@@ -1646,16 +1760,42 @@ const applyTerminalCommands = async ({ terminals, paneMap, runCommand }) => {
|
|
|
1646
1760
|
}
|
|
1647
1761
|
});
|
|
1648
1762
|
}
|
|
1649
|
-
if (typeof terminal.command === "string" && terminal.command.length > 0)
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1763
|
+
if (typeof terminal.command === "string" && terminal.command.length > 0) {
|
|
1764
|
+
const focusPaneRealIdForCommand = terminal.command.includes("{{focus_pane}}") ? focusPaneRealId : "";
|
|
1765
|
+
let commandWithTokensReplaced;
|
|
1766
|
+
try {
|
|
1767
|
+
commandWithTokensReplaced = replaceTemplateTokens({
|
|
1768
|
+
command: terminal.command,
|
|
1769
|
+
currentPaneRealId: realPaneId,
|
|
1770
|
+
focusPaneRealId: focusPaneRealIdForCommand,
|
|
1771
|
+
nameToRealIdMap
|
|
1772
|
+
});
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
if (error instanceof TemplateTokenError) throw createFunctionalError("execution", {
|
|
1775
|
+
code: "TEMPLATE_TOKEN_ERROR",
|
|
1776
|
+
message: `Template token resolution failed for pane ${terminal.virtualPaneId}: ${error.message}`,
|
|
1777
|
+
path: terminal.virtualPaneId,
|
|
1778
|
+
details: {
|
|
1779
|
+
command: terminal.command,
|
|
1780
|
+
tokenType: error.tokenType,
|
|
1781
|
+
availablePanes: error.availablePanes
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
throw error;
|
|
1657
1785
|
}
|
|
1658
|
-
|
|
1786
|
+
if (terminal.ephemeral === true) if (terminal.closeOnError === true) commandWithTokensReplaced = `${commandWithTokensReplaced}; exit`;
|
|
1787
|
+
else commandWithTokensReplaced = `${commandWithTokensReplaced}; [ $? -eq 0 ] && exit`;
|
|
1788
|
+
await sendTextToPane({
|
|
1789
|
+
paneId: realPaneId,
|
|
1790
|
+
text: commandWithTokensReplaced,
|
|
1791
|
+
runCommand,
|
|
1792
|
+
context: {
|
|
1793
|
+
message: `Failed to execute command for pane ${terminal.virtualPaneId}`,
|
|
1794
|
+
path: terminal.virtualPaneId,
|
|
1795
|
+
details: { command: terminal.command }
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1659
1799
|
}
|
|
1660
1800
|
};
|
|
1661
1801
|
const applySplitStep = async ({ step, paneMap, windowId, runCommand, listWindows, logPaneMapping }) => {
|
|
@@ -1744,7 +1884,8 @@ const createWeztermBackend = (context) => {
|
|
|
1744
1884
|
logCommand,
|
|
1745
1885
|
initialCwd,
|
|
1746
1886
|
workspaceHint,
|
|
1747
|
-
initialList: cachedInitialList
|
|
1887
|
+
initialList: cachedInitialList,
|
|
1888
|
+
preferredPaneId: context.paneId
|
|
1748
1889
|
});
|
|
1749
1890
|
registerPaneWithAncestors(paneMap, initialVirtualPaneId, initialPaneId);
|
|
1750
1891
|
logPaneMapping(initialVirtualPaneId, initialPaneId);
|
|
@@ -1770,7 +1911,8 @@ const createWeztermBackend = (context) => {
|
|
|
1770
1911
|
await applyTerminalCommands({
|
|
1771
1912
|
terminals: emission.terminals,
|
|
1772
1913
|
paneMap,
|
|
1773
|
-
runCommand
|
|
1914
|
+
runCommand,
|
|
1915
|
+
focusPaneVirtualId: emission.summary.focusPaneId
|
|
1774
1916
|
});
|
|
1775
1917
|
const focusVirtual = emission.summary.focusPaneId;
|
|
1776
1918
|
const focusPaneId = typeof focusVirtual === "string" ? paneMap.get(focusVirtual) : void 0;
|
|
@@ -2035,6 +2177,8 @@ const parseTerminalPane = (node) => {
|
|
|
2035
2177
|
const command = typeof node.command === "string" ? node.command : void 0;
|
|
2036
2178
|
const cwd = typeof node.cwd === "string" ? node.cwd : void 0;
|
|
2037
2179
|
const focus = node.focus === true ? true : void 0;
|
|
2180
|
+
const ephemeral = node.ephemeral === true ? true : void 0;
|
|
2181
|
+
const closeOnError = node.closeOnError === true ? true : void 0;
|
|
2038
2182
|
const env = normalizeEnv(node.env);
|
|
2039
2183
|
const options = collectOptions(node, new Set([
|
|
2040
2184
|
"name",
|
|
@@ -2042,6 +2186,8 @@ const parseTerminalPane = (node) => {
|
|
|
2042
2186
|
"cwd",
|
|
2043
2187
|
"env",
|
|
2044
2188
|
"focus",
|
|
2189
|
+
"ephemeral",
|
|
2190
|
+
"closeOnError",
|
|
2045
2191
|
"options",
|
|
2046
2192
|
"title",
|
|
2047
2193
|
"delay"
|
|
@@ -2053,6 +2199,8 @@ const parseTerminalPane = (node) => {
|
|
|
2053
2199
|
cwd,
|
|
2054
2200
|
env,
|
|
2055
2201
|
focus,
|
|
2202
|
+
ephemeral,
|
|
2203
|
+
closeOnError,
|
|
2056
2204
|
options
|
|
2057
2205
|
};
|
|
2058
2206
|
};
|
|
@@ -2173,7 +2321,9 @@ const createTerminalNode = ({ id, terminal, focusOverride }) => {
|
|
|
2173
2321
|
cwd: terminal.cwd,
|
|
2174
2322
|
env: terminal.env,
|
|
2175
2323
|
options: terminal.options,
|
|
2176
|
-
focus: focusOverride === true ? true : terminal.focus === true
|
|
2324
|
+
focus: focusOverride === true ? true : terminal.focus === true,
|
|
2325
|
+
ephemeral: terminal.ephemeral,
|
|
2326
|
+
closeOnError: terminal.closeOnError
|
|
2177
2327
|
};
|
|
2178
2328
|
};
|
|
2179
2329
|
const ensureFocus = (node, focusPaneId) => {
|
|
@@ -2277,7 +2427,9 @@ const collectTerminals = (node) => {
|
|
|
2277
2427
|
cwd: node.cwd,
|
|
2278
2428
|
env: node.env,
|
|
2279
2429
|
focus: node.focus,
|
|
2280
|
-
name: node.name
|
|
2430
|
+
name: node.name,
|
|
2431
|
+
ephemeral: node.ephemeral,
|
|
2432
|
+
closeOnError: node.closeOnError
|
|
2281
2433
|
}];
|
|
2282
2434
|
return node.panes.flatMap((pane) => collectTerminals(pane));
|
|
2283
2435
|
};
|