137
src/features/tmux-subagent/grid-planning.ts
Normal file
137
src/features/tmux-subagent/grid-planning.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { MIN_PANE_HEIGHT, MIN_PANE_WIDTH } from "./types"
|
||||
import type { CapacityConfig, TmuxPaneInfo } from "./types"
|
||||
import {
|
||||
DIVIDER_SIZE,
|
||||
MAX_GRID_SIZE,
|
||||
computeAgentAreaWidth,
|
||||
} from "./tmux-grid-constants"
|
||||
|
||||
export interface GridCapacity {
|
||||
cols: number
|
||||
rows: number
|
||||
total: number
|
||||
}
|
||||
|
||||
export interface GridSlot {
|
||||
row: number
|
||||
col: number
|
||||
}
|
||||
|
||||
export interface GridPlan {
|
||||
cols: number
|
||||
rows: number
|
||||
slotWidth: number
|
||||
slotHeight: number
|
||||
}
|
||||
|
||||
type CapacityOptions = CapacityConfig | number | undefined
|
||||
|
||||
function resolveMinPaneWidth(options?: CapacityOptions): number {
|
||||
if (typeof options === "number") {
|
||||
return Math.max(1, options)
|
||||
}
|
||||
if (options && typeof options.agentPaneWidth === "number") {
|
||||
return Math.max(1, options.agentPaneWidth)
|
||||
}
|
||||
return MIN_PANE_WIDTH
|
||||
}
|
||||
|
||||
function resolveAgentAreaWidth(windowWidth: number, options?: CapacityOptions): number {
|
||||
if (typeof options === "number") {
|
||||
return computeAgentAreaWidth(windowWidth)
|
||||
}
|
||||
return computeAgentAreaWidth(windowWidth, options)
|
||||
}
|
||||
|
||||
export function calculateCapacity(
|
||||
windowWidth: number,
|
||||
windowHeight: number,
|
||||
options?: CapacityOptions,
|
||||
mainPaneWidth?: number,
|
||||
): GridCapacity {
|
||||
const availableWidth =
|
||||
typeof mainPaneWidth === "number"
|
||||
? Math.max(0, windowWidth - mainPaneWidth - DIVIDER_SIZE)
|
||||
: resolveAgentAreaWidth(windowWidth, options)
|
||||
const minPaneWidth = resolveMinPaneWidth(options)
|
||||
const cols = Math.min(
|
||||
MAX_GRID_SIZE,
|
||||
Math.max(
|
||||
0,
|
||||
Math.floor(
|
||||
(availableWidth + DIVIDER_SIZE) / (minPaneWidth + DIVIDER_SIZE),
|
||||
),
|
||||
),
|
||||
)
|
||||
const rows = Math.min(
|
||||
MAX_GRID_SIZE,
|
||||
Math.max(
|
||||
0,
|
||||
Math.floor(
|
||||
(windowHeight + DIVIDER_SIZE) / (MIN_PANE_HEIGHT + DIVIDER_SIZE),
|
||||
),
|
||||
),
|
||||
)
|
||||
return { cols, rows, total: cols * rows }
|
||||
}
|
||||
|
||||
export function computeGridPlan(
|
||||
windowWidth: number,
|
||||
windowHeight: number,
|
||||
paneCount: number,
|
||||
options?: CapacityOptions,
|
||||
mainPaneWidth?: number,
|
||||
): GridPlan {
|
||||
const capacity = calculateCapacity(windowWidth, windowHeight, options, mainPaneWidth)
|
||||
const { cols: maxCols, rows: maxRows } = capacity
|
||||
|
||||
if (maxCols === 0 || maxRows === 0 || paneCount === 0) {
|
||||
return { cols: 1, rows: 1, slotWidth: 0, slotHeight: 0 }
|
||||
}
|
||||
|
||||
let bestCols = 1
|
||||
let bestRows = 1
|
||||
let bestArea = Number.POSITIVE_INFINITY
|
||||
|
||||
for (let rows = 1; rows <= maxRows; rows++) {
|
||||
for (let cols = 1; cols <= maxCols; cols++) {
|
||||
if (cols * rows < paneCount) continue
|
||||
const area = cols * rows
|
||||
if (area < bestArea || (area === bestArea && rows < bestRows)) {
|
||||
bestCols = cols
|
||||
bestRows = rows
|
||||
bestArea = area
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const availableWidth =
|
||||
typeof mainPaneWidth === "number"
|
||||
? Math.max(0, windowWidth - mainPaneWidth - DIVIDER_SIZE)
|
||||
: resolveAgentAreaWidth(windowWidth, options)
|
||||
const slotWidth = Math.floor(availableWidth / bestCols)
|
||||
const slotHeight = Math.floor(windowHeight / bestRows)
|
||||
|
||||
return { cols: bestCols, rows: bestRows, slotWidth, slotHeight }
|
||||
}
|
||||
|
||||
export function mapPaneToSlot(
|
||||
pane: TmuxPaneInfo,
|
||||
plan: GridPlan,
|
||||
mainPaneWidth: number,
|
||||
): GridSlot {
|
||||
const rightAreaX = mainPaneWidth
|
||||
const relativeX = Math.max(0, pane.left - rightAreaX)
|
||||
const relativeY = pane.top
|
||||
|
||||
const col =
|
||||
plan.slotWidth > 0
|
||||
? Math.min(plan.cols - 1, Math.floor(relativeX / plan.slotWidth))
|
||||
: 0
|
||||
const row =
|
||||
plan.slotHeight > 0
|
||||
? Math.min(plan.rows - 1, Math.floor(relativeY / plan.slotHeight))
|
||||
: 0
|
||||
|
||||
return { row, col }
|
||||
}
|
||||
Reference in New Issue
Block a user