UNPKG

vde-layout

Version:

Terminal multiplexer layout management tool for VDE (Vibe Coding Development Environment)

174 lines 7.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LayoutEngine = void 0; const executor_1 = require("../tmux/executor"); const commands_1 = require("../tmux/commands"); const ratio_1 = require("../utils/ratio"); class LayoutEngine { executor; commandGenerator; constructor(options = {}) { if (options.executor && "verifyTmuxEnvironment" in options.executor) { this.executor = options.executor; } else if (options.executor) { const executorOptions = { verbose: options.verbose, dryRun: options.dryRun, executor: options.executor, }; this.executor = new executor_1.TmuxExecutor(executorOptions); } else { const executorOptions = { verbose: options.verbose, dryRun: options.dryRun, }; this.executor = new executor_1.TmuxExecutor(executorOptions); } this.commandGenerator = options.commandGenerator || new commands_1.TmuxCommandGenerator(); } async createLayout(preset) { await this.executor.verifyTmuxEnvironment(); const windowName = preset.name || "vde-layout"; await this.executor.execute(this.commandGenerator.newWindow(windowName)); const initialPaneId = await this.getCurrentPaneId(); let paneInfos; if (preset.layout) { paneInfos = await this.createLayoutRecursive(preset.layout, initialPaneId); } else { const terminalPane = { name: preset.name, command: preset.command, }; paneInfos = [ { pane: terminalPane, paneId: initialPaneId, }, ]; } for (const paneInfo of paneInfos) { await this.applyPaneOptions(paneInfo); } const focusedPane = paneInfos.find((info) => { const terminalPane = info.pane; return terminalPane.focus === true; }); if (focusedPane !== undefined) { await this.executor.execute(this.commandGenerator.selectPane(focusedPane.paneId)); } } async createLayoutRecursive(layout, parentPaneId, _isFirst = true) { const isLayout = (l) => { return (typeof l === "object" && l !== null && "type" in l && "panes" in l && "ratio" in l && (l.type === "horizontal" || l.type === "vertical")); }; if (!isLayout(layout)) { return [ { pane: layout, paneId: parentPaneId, }, ]; } const paneInfos = []; const currentPaneId = parentPaneId; let paneIndex = 0; if (parentPaneId && parentPaneId.startsWith("%")) { paneIndex = parseInt(parentPaneId.slice(1), 10) || 0; } const normalizedRatio = (0, ratio_1.normalizeRatio)(layout.ratio ?? []); const paneIds = [parentPaneId]; for (let i = 1; i < layout.panes.length; i++) { let percentage; if (i === 1) { const firstPaneRatio = normalizedRatio[0]; const totalRatio = normalizedRatio.reduce((sum, r) => sum + r, 0); const remainingRatiosSum = totalRatio - firstPaneRatio; percentage = Math.round((remainingRatiosSum / totalRatio) * 100); } else { const currentSpaceRatios = normalizedRatio.slice(i - 1); const currentSpaceTotal = currentSpaceRatios.reduce((sum, r) => sum + r, 0); const newPaneRatios = normalizedRatio.slice(i); const newPaneTotal = newPaneRatios.reduce((sum, r) => sum + r, 0); percentage = Math.round((newPaneTotal / currentSpaceTotal) * 100); } const beforePaneIds = await this.getAllPaneIds(); const targetPaneId = i === 1 ? currentPaneId : paneIds[paneIds.length - 1]; await this.executor.execute(this.commandGenerator.splitWindow(layout.type, targetPaneId, percentage)); const afterPaneIds = await this.getAllPaneIds(); const newPaneId = afterPaneIds.find((id) => !beforePaneIds.includes(id)); if (newPaneId === undefined) { const fallbackPaneId = `%${paneIndex + i}`; paneIds.push(fallbackPaneId); } else { paneIds.push(newPaneId); } paneIndex++; } for (let i = 0; i < layout.panes.length; i++) { const pane = layout.panes[i]; const paneId = paneIds[i]; const childPaneInfos = await this.createLayoutRecursive(pane, paneId, false); for (const childInfo of childPaneInfos) { if (childInfo.paneId && childInfo.paneId.startsWith("%")) { const childIndex = parseInt(childInfo.paneId.slice(1), 10); if (!isNaN(childIndex) && childIndex > paneIndex) { paneIndex = childIndex; } } } paneInfos.push(...childPaneInfos); } return paneInfos; } async applyPaneOptions(paneInfo) { const pane = paneInfo.pane; const paneId = paneInfo.paneId; if (pane.cwd !== undefined) { await this.executor.execute(this.commandGenerator.changeDirectory(paneId, pane.cwd)); } if (pane.env !== undefined) { const envCommands = this.commandGenerator.setEnvironment(paneId, pane.env); for (const command of envCommands) { await this.executor.execute(command); } } if (pane.name !== undefined) { await this.executor.execute(this.commandGenerator.setPaneTitle(paneId, pane.name)); } if (pane.delay !== undefined && pane.delay > 0) { await new Promise((resolve) => setTimeout(resolve, pane.delay)); } if (pane.command !== undefined) { await this.executor.execute(this.commandGenerator.sendKeys(paneId, pane.command)); } } async getCurrentPaneId() { const result = await this.executor.execute(["display-message", "-p", "#{pane_id}"]); if (!result || typeof result !== "string") { return "%0"; } return result.trim() || "%0"; } async getAllPaneIds() { const result = await this.executor.execute(["list-panes", "-F", "#{pane_id}"]); if (!result || typeof result !== "string") { return ["%0"]; } return result .trim() .split("\n") .filter((id) => id.trim() !== ""); } } exports.LayoutEngine = LayoutEngine; //# sourceMappingURL=engine.js.map