fix(tmux): thread agent_pane_min_width config through pane management
The agent_pane_min_width config value was accepted in the schema and passed as CapacityConfig.agentPaneWidth but never actually used — the underscore-prefixed _config parameter in decideSpawnActions was unused, and all split/capacity calculations used the hardcoded MIN_PANE_WIDTH. Now decideSpawnActions, canSplitPane, isSplittableAtCount, findMinimalEvictions, and calculateCapacity all accept and use the configured minimum pane width, falling back to the default (52) when not provided. Closes #1781
This commit is contained in:
28
bun.lock
28
bun.lock
@@ -28,13 +28,13 @@
|
||||
"typescript": "^5.7.3",
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"oh-my-opencode-darwin-arm64": "3.5.2",
|
||||
"oh-my-opencode-darwin-x64": "3.5.2",
|
||||
"oh-my-opencode-linux-arm64": "3.5.2",
|
||||
"oh-my-opencode-linux-arm64-musl": "3.5.2",
|
||||
"oh-my-opencode-linux-x64": "3.5.2",
|
||||
"oh-my-opencode-linux-x64-musl": "3.5.2",
|
||||
"oh-my-opencode-windows-x64": "3.5.2",
|
||||
"oh-my-opencode-darwin-arm64": "3.5.3",
|
||||
"oh-my-opencode-darwin-x64": "3.5.3",
|
||||
"oh-my-opencode-linux-arm64": "3.5.3",
|
||||
"oh-my-opencode-linux-arm64-musl": "3.5.3",
|
||||
"oh-my-opencode-linux-x64": "3.5.3",
|
||||
"oh-my-opencode-linux-x64-musl": "3.5.3",
|
||||
"oh-my-opencode-windows-x64": "3.5.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -226,19 +226,19 @@
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.5.2", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-oIS3lB2F9/N+3mF5wCKk6/EPVSz516XWN+mNdquSSeddw+xqMxGdhKY6K/XeYbHJzeN2Z8IOikNEJ6psR2/a8g=="],
|
||||
"oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.5.3", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-Dq0+PC2dyAqG7c3DUnQmdOkKbKmOsRHwoqgLCQNKN1lTRllF8zbWqp5B+LGKxSPxPqJIPS3mKt+wIR2KvkYJVw=="],
|
||||
|
||||
"oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.5.2", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-OAdXo4ZCCYO4kRWtnyz3tdmaGYPUB3WcXimXAxp+/sEZxAnh7n1RQkpLn6UxWX4AIAdRT9dfrOfRic6VoCYv2g=="],
|
||||
"oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.5.3", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-Ke45Bv/ygZm3YUSUumIyk647KZ2PFzw30tH597cOpG8MDPGbNVBCM6EKFezcukUPT+gPFVpE1IiGzEkn4JmgZA=="],
|
||||
|
||||
"oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.5.2", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-5XXNMFhp1VsyrGNRBoXcOyoaUeVkbrWkBRPDGZfpiq+kRXH3aaSWdR5G7Pl/TadOQv9Bl8/8YaxsuHRTFT1aXw=="],
|
||||
"oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.5.3", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-aP5S3DngUhFkNeqYM33Ge6zccCWLzB/O3FLXLFXy/Iws03N8xugw72pnMK6lUbIia9QQBKK7IZBoYm9C79pZ3g=="],
|
||||
|
||||
"oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.5.2", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-/woIpqvEI85MgJvEVnz4g5FBLeiQNK7srRsueIFPBmtTahh42HFleCDaIltOl/ndjsE5nCHacQVJHkC9W9/F3Q=="],
|
||||
"oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.5.3", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-UiD/hVKYZQyX4D5N5SnZT4M5Z/B2SDtJWBW4MibpYSAcPKNCEBKi/5E4hOPxAtTfFGR8tIXFmYZdQJDkVfvluw=="],
|
||||
|
||||
"oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.5.2", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-vTL2A+6zzGhi+m7sC8peLDq5OAp2dRR0UEb4RbZAOHtlEruF7qFEmcK3ccWxwc3+Z3G/ITfwn5VNa72ZS4pNTg=="],
|
||||
"oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.5.3", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-L9kqwzElGkaQ8pgtv1ZjcHARw9LPaU4UEVjzauByTMi+/5Js/PTsNXBggxSRzZfQ8/MNBPSCiA4K10Kc0YjjvA=="],
|
||||
|
||||
"oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.5.2", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-bOAA55snLsK2QB00IkQy8le0Oqh/GJ7pxEHtm1oUezlQrW/nX5SS/hJ7dPHMmOd9FoiqnqyqWZxNkLmFoG463A=="],
|
||||
"oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.5.3", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-Z0fVVih/b2dbNeb9DK9oca5dNYCZyPySBRtxRhDXod5d7fJNgIPrvUoEd3SNfkRGORyFB3hGBZ6nqQ6N8+8DEA=="],
|
||||
|
||||
"oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.5.2", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-fnHiAPYglw3unPckmQBoCT6+VqjSWCE3S3J551mRo0ZFrxuEP2ZKyHZeFMMOtKwDepCvmKgd1W040+KmuVUXOA=="],
|
||||
"oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.5.3", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-ocWPjRs2sJgN02PJnEIYtqdMVDex1YhEj1FzAU5XIicfzQbgxLh9nz1yhHZzfqGJq69QStU6ofpc5kQpfX1LMg=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
|
||||
@@ -351,4 +351,47 @@ describe("calculateCapacity", () => {
|
||||
expect(capacity.rows).toBe(4)
|
||||
expect(capacity.total).toBe(12)
|
||||
})
|
||||
|
||||
it("#given a smaller minPaneWidth #when calculating capacity #then fits more columns", () => {
|
||||
//#given
|
||||
const smallMinWidth = 30
|
||||
|
||||
//#when
|
||||
const defaultCapacity = calculateCapacity(212, 44)
|
||||
const customCapacity = calculateCapacity(212, 44, smallMinWidth)
|
||||
|
||||
//#then
|
||||
expect(customCapacity.cols).toBeGreaterThanOrEqual(defaultCapacity.cols)
|
||||
})
|
||||
})
|
||||
|
||||
describe("decideSpawnActions with custom agentPaneWidth", () => {
|
||||
const createWindowState = (
|
||||
windowWidth: number,
|
||||
windowHeight: number,
|
||||
agentPanes: Array<{ paneId: string; width: number; height: number; left: number; top: number }> = []
|
||||
): WindowState => ({
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
mainPane: { paneId: "%0", width: Math.floor(windowWidth / 2), height: windowHeight, left: 0, top: 0, title: "main", isActive: true },
|
||||
agentPanes: agentPanes.map((p, i) => ({
|
||||
...p,
|
||||
title: `agent-${i}`,
|
||||
isActive: false,
|
||||
})),
|
||||
})
|
||||
|
||||
it("#given a smaller agentPaneWidth #when window would be too small for default #then spawns with custom config", () => {
|
||||
//#given
|
||||
const smallConfig: CapacityConfig = { mainPaneMinWidth: 120, agentPaneWidth: 25 }
|
||||
const state = createWindowState(100, 30)
|
||||
|
||||
//#when
|
||||
const defaultResult = decideSpawnActions(state, "ses1", "test", { mainPaneMinWidth: 120, agentPaneWidth: 52 }, [])
|
||||
const customResult = decideSpawnActions(state, "ses1", "test", smallConfig, [])
|
||||
|
||||
//#then
|
||||
expect(defaultResult.canSpawn).toBe(false)
|
||||
expect(customResult.canSpawn).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { MIN_PANE_HEIGHT, MIN_PANE_WIDTH } from "./types"
|
||||
import type { TmuxPaneInfo } from "./types"
|
||||
import {
|
||||
DIVIDER_SIZE,
|
||||
MAIN_PANE_RATIO,
|
||||
MAX_GRID_SIZE,
|
||||
} from "./tmux-grid-constants"
|
||||
import { MIN_PANE_HEIGHT, MIN_PANE_WIDTH } from "./types"
|
||||
|
||||
export interface GridCapacity {
|
||||
cols: number
|
||||
@@ -27,6 +27,7 @@ export interface GridPlan {
|
||||
export function calculateCapacity(
|
||||
windowWidth: number,
|
||||
windowHeight: number,
|
||||
minPaneWidth: number = MIN_PANE_WIDTH,
|
||||
): GridCapacity {
|
||||
const availableWidth = Math.floor(windowWidth * (1 - MAIN_PANE_RATIO))
|
||||
const cols = Math.min(
|
||||
@@ -34,7 +35,7 @@ export function calculateCapacity(
|
||||
Math.max(
|
||||
0,
|
||||
Math.floor(
|
||||
(availableWidth + DIVIDER_SIZE) / (MIN_PANE_WIDTH + DIVIDER_SIZE),
|
||||
(availableWidth + DIVIDER_SIZE) / (minPaneWidth + DIVIDER_SIZE),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MIN_PANE_HEIGHT, MIN_PANE_WIDTH } from "./types"
|
||||
import type { SplitDirection, TmuxPaneInfo } from "./types"
|
||||
import {
|
||||
DIVIDER_SIZE,
|
||||
@@ -7,6 +8,10 @@ import {
|
||||
MIN_SPLIT_WIDTH,
|
||||
} from "./tmux-grid-constants"
|
||||
|
||||
function minSplitWidthFor(minPaneWidth: number): number {
|
||||
return 2 * minPaneWidth + DIVIDER_SIZE
|
||||
}
|
||||
|
||||
export function getColumnCount(paneCount: number): number {
|
||||
if (paneCount <= 0) return 1
|
||||
return Math.min(MAX_COLS, Math.max(1, Math.ceil(paneCount / MAX_ROWS)))
|
||||
@@ -21,26 +26,32 @@ export function getColumnWidth(agentAreaWidth: number, paneCount: number): numbe
|
||||
export function isSplittableAtCount(
|
||||
agentAreaWidth: number,
|
||||
paneCount: number,
|
||||
minPaneWidth: number = MIN_PANE_WIDTH,
|
||||
): boolean {
|
||||
const columnWidth = getColumnWidth(agentAreaWidth, paneCount)
|
||||
return columnWidth >= MIN_SPLIT_WIDTH
|
||||
return columnWidth >= minSplitWidthFor(minPaneWidth)
|
||||
}
|
||||
|
||||
export function findMinimalEvictions(
|
||||
agentAreaWidth: number,
|
||||
currentCount: number,
|
||||
minPaneWidth: number = MIN_PANE_WIDTH,
|
||||
): number | null {
|
||||
for (let k = 1; k <= currentCount; k++) {
|
||||
if (isSplittableAtCount(agentAreaWidth, currentCount - k)) {
|
||||
if (isSplittableAtCount(agentAreaWidth, currentCount - k, minPaneWidth)) {
|
||||
return k
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function canSplitPane(pane: TmuxPaneInfo, direction: SplitDirection): boolean {
|
||||
export function canSplitPane(
|
||||
pane: TmuxPaneInfo,
|
||||
direction: SplitDirection,
|
||||
minPaneWidth: number = MIN_PANE_WIDTH,
|
||||
): boolean {
|
||||
if (direction === "-h") {
|
||||
return pane.width >= MIN_SPLIT_WIDTH
|
||||
return pane.width >= minSplitWidthFor(minPaneWidth)
|
||||
}
|
||||
return pane.height >= MIN_SPLIT_HEIGHT
|
||||
}
|
||||
|
||||
@@ -13,23 +13,23 @@ import {
|
||||
} from "./pane-split-availability"
|
||||
import { findSpawnTarget } from "./spawn-target-finder"
|
||||
import { findOldestAgentPane, type SessionMapping } from "./oldest-agent-pane"
|
||||
import { MIN_PANE_WIDTH } from "./types"
|
||||
|
||||
export function decideSpawnActions(
|
||||
state: WindowState,
|
||||
sessionId: string,
|
||||
description: string,
|
||||
_config: CapacityConfig,
|
||||
config: CapacityConfig,
|
||||
sessionMappings: SessionMapping[],
|
||||
): SpawnDecision {
|
||||
if (!state.mainPane) {
|
||||
return { canSpawn: false, actions: [], reason: "no main pane found" }
|
||||
}
|
||||
|
||||
const minPaneWidth = config.agentPaneWidth
|
||||
const agentAreaWidth = Math.floor(state.windowWidth * (1 - MAIN_PANE_RATIO))
|
||||
const currentCount = state.agentPanes.length
|
||||
|
||||
if (agentAreaWidth < MIN_PANE_WIDTH) {
|
||||
if (agentAreaWidth < minPaneWidth) {
|
||||
return {
|
||||
canSpawn: false,
|
||||
actions: [],
|
||||
@@ -44,7 +44,7 @@ export function decideSpawnActions(
|
||||
|
||||
if (currentCount === 0) {
|
||||
const virtualMainPane: TmuxPaneInfo = { ...state.mainPane, width: state.windowWidth }
|
||||
if (canSplitPane(virtualMainPane, "-h")) {
|
||||
if (canSplitPane(virtualMainPane, "-h", minPaneWidth)) {
|
||||
return {
|
||||
canSpawn: true,
|
||||
actions: [
|
||||
@@ -61,7 +61,7 @@ export function decideSpawnActions(
|
||||
return { canSpawn: false, actions: [], reason: "mainPane too small to split" }
|
||||
}
|
||||
|
||||
if (isSplittableAtCount(agentAreaWidth, currentCount)) {
|
||||
if (isSplittableAtCount(agentAreaWidth, currentCount, minPaneWidth)) {
|
||||
const spawnTarget = findSpawnTarget(state)
|
||||
if (spawnTarget) {
|
||||
return {
|
||||
@@ -79,7 +79,7 @@ export function decideSpawnActions(
|
||||
}
|
||||
}
|
||||
|
||||
const minEvictions = findMinimalEvictions(agentAreaWidth, currentCount)
|
||||
const minEvictions = findMinimalEvictions(agentAreaWidth, currentCount, minPaneWidth)
|
||||
if (minEvictions === 1 && oldestPane) {
|
||||
return {
|
||||
canSpawn: true,
|
||||
|
||||
Reference in New Issue
Block a user