Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Configuration is stored in the platform-specific config directory:
| `agent` | string | `""` | Pass `--agent <name>` to Copilot CLI |
| `model` | string | `""` | Pass `--model <name>` to Copilot CLI |
| `launch_mode` | string | `"tab"` | How to open sessions: `in-place`, `tab`, `window`, `pane` |
| `pane_direction` | string | `"auto"` | Split direction for pane mode: `auto`, `right`, `down`, `left`, `up` |
| `pane_direction` | string | `"auto"` | Split direction for pane mode: `auto`, `right`, `down`, `left`, `up` (see note below) |
| `custom_command` | string | `""` | Custom launch command (`{sessionId}` is replaced) |
| `excluded_dirs` | array | `[]` | Directory paths to hide from session list |
| `theme` | string | `"auto"` | Color scheme: `auto` or a named scheme |
Expand All @@ -262,6 +262,20 @@ Configuration is stored in the platform-specific config directory:
| `hiddenSessions` | array | `[]` | Session IDs hidden from the main list |
| `favoriteSessions` | array | `[]` | Session IDs starred as favorites |

#### Pane Direction Semantics

When `launch_mode` is `"pane"`, the `pane_direction` value maps to Windows Terminal's `-H` / `-V` split-pane flags:

| Direction | WT Flag | Meaning |
|-----------|---------|---------|
| `down` | `-H` | Horizontal split — divider runs horizontally, new pane below |
| `up` | `-H` | Horizontal split — WT controls actual placement (closest available) |
| `right` | `-V` | Vertical split — divider runs vertically, new pane to the right |
| `left` | `-V` | Vertical split — WT controls actual placement (closest available) |
| `auto` | *(none)* | Windows Terminal decides automatically |

> **Note:** `-H` and `-V` control split *orientation* only (the direction the divider runs). Windows Terminal decides actual pane placement based on available space.

### Example config.json

```json
Expand Down
51 changes: 51 additions & 0 deletions internal/platform/coverage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,57 @@ func TestLaunchStyleConstants(t *testing.T) {
}
}

// ---------------------------------------------------------------------------
// appendWTPaneDirFlags
// ---------------------------------------------------------------------------

func TestAppendWTPaneDirFlags(t *testing.T) {
tests := []struct {
name string
dir string
want []string
}{
{"down", "down", []string{"-H"}},
{"up", "up", []string{"-H"}},
{"right", "right", []string{"-V"}},
{"left", "left", []string{"-V"}},
{"auto", "auto", nil},
{"empty", "", nil},
{"unknown", "diagonal", nil},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
base := []string{"-w", "0", "split-pane"}
got := appendWTPaneDirFlags(base, tc.dir)

// base should never be modified in place.
extra := got[len(base):]
if tc.want == nil {
if len(extra) != 0 {
t.Errorf("dir=%q: got extra args %v, want none", tc.dir, extra)
}
return
}
if len(extra) != len(tc.want) {
t.Fatalf("dir=%q: got %v extra args, want %v", tc.dir, extra, tc.want)
}
for i, w := range tc.want {
if extra[i] != w {
t.Errorf("dir=%q: extra[%d]=%q, want %q", tc.dir, i, extra[i], w)
}
}
})
}
}

func TestAppendWTPaneDirFlags_PreservesExistingArgs(t *testing.T) {
base := []string{"existing", "args"}
got := appendWTPaneDirFlags(base, "down")
if len(got) != 3 || got[0] != "existing" || got[1] != "args" || got[2] != "-H" {
t.Errorf("got %v, want [existing args -H]", got)
}
}

// ---------------------------------------------------------------------------
// TerminalInfo and ShellInfo
// ---------------------------------------------------------------------------
Expand Down
36 changes: 30 additions & 6 deletions internal/platform/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,34 @@ func defaultWindowsShell() ShellInfo {
return ShellInfo{Name: "Command Prompt", Path: p}
}

// appendWTPaneDirFlags translates a dispatch pane direction string into
// the correct wt.exe split-pane flags.
//
// Windows Terminal's -H and -V flags control the split *orientation* (the
// direction the divider runs), not which side the new pane appears on.
// WT itself decides actual pane placement based on available space.
//
// Mapping:
//
// "down" → -H horizontal split — divider runs horizontally, new pane below
// "up" → -H horizontal split — WT picks closest available placement
// "right" → -V vertical split — divider runs vertically, new pane to the right
// "left" → -V vertical split — WT picks closest available placement
// "auto" → (no flag) WT default behavior
// "" → (no flag) WT default behavior
// unknown → (no flag) WT default behavior
func appendWTPaneDirFlags(args []string, dir string) []string {
switch dir {
case "down", "up":
return append(args, "-H")
case "right", "left":
return append(args, "-V")
default:
// "auto" or empty — let Windows Terminal choose.
return args
}
}

func launchWindowsSession(shell ShellInfo, resumeCmd string, terminal string, cwd string, launchStyle string, paneDirection string) error {
// Use Windows Terminal when configured (or defaulted by LaunchSession).
if terminal == termWindowsTerminal {
Expand All @@ -549,9 +577,7 @@ func launchWindowsSession(shell ShellInfo, resumeCmd string, terminal string, cw
case LaunchStylePane:
// Open a split pane in the current tab.
args = append(args, "-w", "0", "split-pane")
if paneDirection != "" && paneDirection != "auto" {
args = append(args, "--direction", paneDirection)
}
args = appendWTPaneDirFlags(args, paneDirection)
default:
// Open a new tab in the most recently used window.
// Without -w 0, wt.exe opens a new window by default.
Expand Down Expand Up @@ -879,9 +905,7 @@ func buildWSLWTArgs(shell ShellInfo, resumeCmd, winCwd, distro, launchStyle, pan
args = append(args, "-w", "new", "new-tab")
case LaunchStylePane:
args = append(args, "-w", "0", "split-pane")
if paneDirection != "" && paneDirection != "auto" {
args = append(args, "--direction", paneDirection)
}
args = appendWTPaneDirFlags(args, paneDirection)
default:
args = append(args, "-w", "0", "new-tab")
}
Expand Down
7 changes: 5 additions & 2 deletions internal/platform/wsl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,11 @@ func TestBuildWSLWTArgs_LaunchStyles(t *testing.T) {
{"explicit_tab", LaunchStyleTab, "", []string{"-w", "0", "new-tab"}},
{"window", LaunchStyleWindow, "", []string{"-w", "new", "new-tab"}},
{"pane_auto", LaunchStylePane, "auto", []string{"-w", "0", "split-pane"}},
{"pane_right", LaunchStylePane, "right", []string{"-w", "0", "split-pane", "--direction", "right"}},
{"pane_down", LaunchStylePane, "down", []string{"-w", "0", "split-pane", "--direction", "down"}},
{"pane_right", LaunchStylePane, "right", []string{"-w", "0", "split-pane", "-V"}},
{"pane_down", LaunchStylePane, "down", []string{"-w", "0", "split-pane", "-H"}},
{"pane_left", LaunchStylePane, "left", []string{"-w", "0", "split-pane", "-V"}},
{"pane_up", LaunchStylePane, "up", []string{"-w", "0", "split-pane", "-H"}},
{"pane_empty_dir", LaunchStylePane, "", []string{"-w", "0", "split-pane"}},
}

for _, tc := range tests {
Expand Down
25 changes: 24 additions & 1 deletion web/src/pages/config.astro
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import ConfigTable from '../components/ConfigTable.astro';
{ key: 'agent', type: 'string', default: '""', description: 'Pass --agent <name> to Copilot CLI.' },
{ key: 'model', type: 'string', default: '""', description: 'Pass --model <name> to Copilot CLI.' },
{ key: 'launch_mode', type: 'string', default: '"tab"', description: 'How to open sessions: in-place, tab, window, pane.' },
{ key: 'pane_direction', type: 'string', default: '"auto"', description: 'Split direction for pane mode: auto, right, down, left, up.' },
{ key: 'pane_direction', type: 'string', default: '"auto"', description: 'Split direction for pane mode: auto, right, down, left, up (see Pane Direction Semantics below).' },
{ key: 'custom_command', type: 'string', default: '""', description: 'Custom launch command. {sessionId} is replaced with the session ID.' },
{ key: 'excluded_dirs', type: 'array', default: '[]', description: 'Directory paths to hide from the session list.' },
{ key: 'favoriteSessions', type: 'array', default: '[]', description: 'Session IDs starred as favorites.' },
Expand Down Expand Up @@ -113,6 +113,29 @@ import ConfigTable from '../components/ConfigTable.astro';
<pre><code>"custom_command": "my-tool resume {'{sessionId}'}"</code></pre>
</div>
</section>

<section class="section">
<div class="container">
<h2>Pane Direction Semantics</h2>
<p>
When <code>launch_mode</code> is <code>"pane"</code>, the
<code>pane_direction</code> value maps to Windows Terminal's
<code>-H</code> / <code>-V</code> split-pane flags:
</p>
<ul>
<li><code>down</code> → <code>-H</code> — horizontal split (divider runs horizontally, new pane below)</li>
<li><code>up</code> → <code>-H</code> — horizontal split (WT controls actual placement; closest available)</li>
<li><code>right</code> → <code>-V</code> — vertical split (divider runs vertically, new pane to the right)</li>
<li><code>left</code> → <code>-V</code> — vertical split (WT controls actual placement; closest available)</li>
<li><code>auto</code> / empty — no flag; Windows Terminal decides automatically</li>
</ul>
<p>
<strong>Note:</strong> <code>-H</code> and <code>-V</code> control
split <em>orientation</em> only (the direction the divider runs).
Windows Terminal decides actual pane placement based on available space.
</p>
</div>
</section>
</Base>

<style>
Expand Down
19 changes: 18 additions & 1 deletion web/src/pages/features.astro
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,28 @@ import Screenshot from '../components/Screenshot.astro';
<ul>
<li><kbd>Ctrl</kbd>+double-click forces a new window</li>
<li><kbd>Shift</kbd>+double-click forces a new tab</li>
<li>Pane mode supports directional splits: auto, right, down, left, up</li>
<li>Pane mode supports directional splits: auto, right, down, left, up (see pane direction semantics below)</li>
<li>Windows: uses <code>wt</code> commands for Windows Terminal</li>
<li>macOS: supports Terminal.app and iTerm2</li>
<li>Linux: detects and delegates to the active terminal emulator</li>
</ul>
<h3>Pane Direction Semantics</h3>
<p>
The <code>pane_direction</code> config value maps to Windows Terminal's
<code>-H</code> / <code>-V</code> split-pane flags:
</p>
<ul>
<li><code>down</code> → <code>-H</code> — horizontal split (divider runs horizontally, new pane below)</li>
<li><code>up</code> → <code>-H</code> — horizontal split (WT controls actual placement)</li>
<li><code>right</code> → <code>-V</code> — vertical split (divider runs vertically, new pane right)</li>
<li><code>left</code> → <code>-V</code> — vertical split (WT controls actual placement)</li>
<li><code>auto</code> / empty — no flag; Windows Terminal decides automatically</li>
</ul>
<p>
<strong>Note:</strong> <code>-H</code> / <code>-V</code> control split
<em>orientation</em> only. Windows Terminal decides actual pane placement
based on available space.
</p>
</div>
<div class="feature-text">
<p class="text-only-note">
Expand Down
Loading