diff --git a/vscode/src/panel/KanbanPanel.ts b/vscode/src/panel/KanbanPanel.ts index 3b7fca0..a82f95b 100644 --- a/vscode/src/panel/KanbanPanel.ts +++ b/vscode/src/panel/KanbanPanel.ts @@ -932,6 +932,7 @@ export class KanbanWebviewPanel { worktreePreRemoveScript: s.worktreePreRemoveScript, implTabPreCreateScript: s.implTabPreCreateScript, implTabPostCloseScript: s.implTabPostCloseScript, + splitNewTerminals: s.splitNewTerminals, conflictResolutionProfilePath: s.conflictResolutionProfilePath, commitProfilePath: await getEffectiveCommitProfilePath(this.context), prDiffSummaryProfilePath: await getEffectivePrDiffSummaryProfilePath(this.context), @@ -1010,6 +1011,7 @@ export class KanbanWebviewPanel { worktreePreRemoveScript: s.worktreePreRemoveScript, implTabPreCreateScript: s.implTabPreCreateScript, implTabPostCloseScript: s.implTabPostCloseScript, + splitNewTerminals: s.splitNewTerminals, conflictResolutionProfilePath: s.conflictResolutionProfilePath, commitProfilePath: await getEffectiveCommitProfilePath(this.context), prDiffSummaryProfilePath: await getEffectivePrDiffSummaryProfilePath(this.context), diff --git a/vscode/src/panel/handlers/settings.ts b/vscode/src/panel/handlers/settings.ts index 9aa24ef..5040aeb 100644 --- a/vscode/src/panel/handlers/settings.ts +++ b/vscode/src/panel/handlers/settings.ts @@ -38,6 +38,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: { worktreePreRemoveScript: string implTabPreCreateScript: string implTabPostCloseScript: string + splitNewTerminals: boolean conflictResolutionProfilePath: string commitProfilePath: string prDiffSummaryProfilePath: string @@ -116,6 +117,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: { worktreePreRemoveScript: trimmedPreRemove, implTabPreCreateScript: trimmedImplPre, implTabPostCloseScript: trimmedImplPost, + splitNewTerminals: payload.splitNewTerminals, conflictResolutionProfilePath: trimmedConflictProfile, commitProfilePath: trimmedCommitProfile, prDiffSummaryProfilePath: trimmedPrDiffSummaryProfile, @@ -144,6 +146,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: { worktreePreRemoveScript: trimmedPreRemove, implTabPreCreateScript: trimmedImplPre, implTabPostCloseScript: trimmedImplPost, + splitNewTerminals: payload.splitNewTerminals, conflictResolutionProfilePath: trimmedConflictProfile, commitProfilePath: trimmedCommitProfile, prDiffSummaryProfilePath: trimmedPrDiffSummaryProfile, @@ -249,6 +252,7 @@ export async function handleEditSettingsRequest(panel: KanbanWebviewPanel): Prom worktreePreRemoveScript: s.worktreePreRemoveScript, implTabPreCreateScript: s.implTabPreCreateScript, implTabPostCloseScript: s.implTabPostCloseScript, + splitNewTerminals: s.splitNewTerminals, conflictResolutionProfilePath: s.conflictResolutionProfilePath, commitProfilePath, prDiffSummaryProfilePath, diff --git a/vscode/src/panel/handlers/terminals.test.ts b/vscode/src/panel/handlers/terminals.test.ts new file mode 100644 index 0000000..d1522cf --- /dev/null +++ b/vscode/src/panel/handlers/terminals.test.ts @@ -0,0 +1,11 @@ +import { describe, expect, it } from 'vitest' +import { terminalEditorColumn } from '../terminalColumn' + +describe('terminalEditorColumn', () => { + it('分屏 → 右侧第 2 栏', () => { + expect(terminalEditorColumn(true)).toBe(2) + }) + it('不分屏 → 当前栏(只加标签)', () => { + expect(terminalEditorColumn(false)).toBe(-1) + }) +}) diff --git a/vscode/src/panel/handlers/terminals.ts b/vscode/src/panel/handlers/terminals.ts index 88fcbdf..2a74786 100644 --- a/vscode/src/panel/handlers/terminals.ts +++ b/vscode/src/panel/handlers/terminals.ts @@ -4,16 +4,14 @@ import type { } from 'vscode' import type { Issue } from '../../gitea/types' import type { KanbanWebviewPanel } from '../KanbanPanel' -import { ViewColumn, window } from 'vscode' +import { window } from 'vscode' import { logger } from '../../logging/logger' +import { getSettings } from '../../settings/store' +import { terminalEditorColumn } from '../terminalColumn' export function resolveTerminalLocation(panel: KanbanWebviewPanel | null, preserveFocus: boolean): TerminalEditorLocationOptions { - void panel - // Pin all plugin-managed terminals to editor group 2 (right side of the - // kanban panel in column 1). VS Code creates the group on demand if it - // doesn't exist yet, and stacks new terminals as tabs in that group when - // it does — exactly what we want, no manual scan of existing tabs needed. - return { viewColumn: ViewColumn.Two, preserveFocus } + const split = panel ? getSettings(panel.context).splitNewTerminals : true + return { viewColumn: terminalEditorColumn(split), preserveFocus } } /** diff --git a/vscode/src/panel/messages.ts b/vscode/src/panel/messages.ts index 1f9cdf5..8b57807 100644 --- a/vscode/src/panel/messages.ts +++ b/vscode/src/panel/messages.ts @@ -84,6 +84,7 @@ export type ExtensionToWebview worktreePreRemoveScript: string implTabPreCreateScript: string implTabPostCloseScript: string + splitNewTerminals: boolean conflictResolutionProfilePath: string commitProfilePath: string prDiffSummaryProfilePath: string @@ -162,6 +163,7 @@ export type WebviewToExtension worktreePreRemoveScript: string implTabPreCreateScript: string implTabPostCloseScript: string + splitNewTerminals: boolean conflictResolutionProfilePath: string commitProfilePath: string prDiffSummaryProfilePath: string diff --git a/vscode/src/panel/terminalColumn.ts b/vscode/src/panel/terminalColumn.ts new file mode 100644 index 0000000..45ac4b5 --- /dev/null +++ b/vscode/src/panel/terminalColumn.ts @@ -0,0 +1,7 @@ +/** + * 会话终端落在编辑区哪一栏。 + * 数值对齐 VS Code ViewColumn:2 = Two(右侧分屏),-1 = Active(当前栏只加标签)。 + */ +export function terminalEditorColumn(split: boolean): 2 | -1 { + return split ? 2 : -1 +} diff --git a/vscode/src/settings/store.ts b/vscode/src/settings/store.ts index 734468f..5f08099 100644 --- a/vscode/src/settings/store.ts +++ b/vscode/src/settings/store.ts @@ -159,6 +159,10 @@ export interface Settings { * Same env vars as the worktree hooks. */ implTabPostCloseScript: string + /** + * 新开会话终端是否分到右侧一栏。关掉则在当前编辑组加标签,适合小屏。 + */ + splitNewTerminals: boolean /** * YouTrack instance base URL (e.g. `https://ziwuxian.youtrack.cloud`). Empty * string means "YouTrack source disabled" — the loader returns no issues. @@ -264,6 +268,7 @@ function defaults(ctx: ExtensionContext): Settings { worktreePreRemoveScript: '', implTabPreCreateScript: '', implTabPostCloseScript: '', + splitNewTerminals: true, youtrackBaseUrl: '', youtrackProjectShortName: '', youtrackCloseCommand: '', @@ -350,6 +355,9 @@ export function getSettings(ctx: ExtensionContext): Settings { const implTabPostCloseScript = typeof stored.implTabPostCloseScript === 'string' ? stored.implTabPostCloseScript : base.implTabPostCloseScript + const splitNewTerminals = typeof stored.splitNewTerminals === 'boolean' + ? stored.splitNewTerminals + : base.splitNewTerminals // YouTrack config: '' is meaningful (= "source disabled" / "auto-detect close // command"), so don't coerce. Non-string legacy values collapse to ''. const youtrackBaseUrl = typeof stored.youtrackBaseUrl === 'string' @@ -405,6 +413,7 @@ export function getSettings(ctx: ExtensionContext): Settings { worktreePreRemoveScript, implTabPreCreateScript, implTabPostCloseScript, + splitNewTerminals, youtrackBaseUrl, youtrackProjectShortName, youtrackCloseCommand, diff --git a/vscode/webview-ui/src/App.tsx b/vscode/webview-ui/src/App.tsx index d18ad65..a0315c2 100644 --- a/vscode/webview-ui/src/App.tsx +++ b/vscode/webview-ui/src/App.tsx @@ -534,6 +534,7 @@ export function App() { initialWorktreePreRemoveScript={settings?.worktreePreRemoveScript ?? ''} initialImplTabPreCreateScript={settings?.implTabPreCreateScript ?? ''} initialImplTabPostCloseScript={settings?.implTabPostCloseScript ?? ''} + initialSplitNewTerminals={settings?.splitNewTerminals ?? true} initialConflictResolutionProfilePath={settings?.conflictResolutionProfilePath ?? ''} initialCommitProfilePath={settings?.commitProfilePath ?? ''} initialPrDiffSummaryProfilePath={settings?.prDiffSummaryProfilePath ?? ''} diff --git a/vscode/webview-ui/src/components/SettingsModal.tsx b/vscode/webview-ui/src/components/SettingsModal.tsx index 0a31e08..737af49 100644 --- a/vscode/webview-ui/src/components/SettingsModal.tsx +++ b/vscode/webview-ui/src/components/SettingsModal.tsx @@ -36,6 +36,7 @@ interface SubmitValues { worktreePreRemoveScript: string implTabPreCreateScript: string implTabPostCloseScript: string + splitNewTerminals: boolean conflictResolutionProfilePath: string commitProfilePath: string prDiffSummaryProfilePath: string @@ -85,6 +86,7 @@ export interface SettingsModalProps { initialWorktreePreRemoveScript: string initialImplTabPreCreateScript: string initialImplTabPostCloseScript: string + initialSplitNewTerminals: boolean initialConflictResolutionProfilePath: string initialCommitProfilePath: string initialPrDiffSummaryProfilePath: string @@ -170,6 +172,7 @@ export function SettingsModal({ initialWorktreePreRemoveScript, initialImplTabPreCreateScript, initialImplTabPostCloseScript, + initialSplitNewTerminals, initialConflictResolutionProfilePath, initialCommitProfilePath, initialPrDiffSummaryProfilePath, @@ -207,6 +210,7 @@ export function SettingsModal({ const [worktreePreRemoveScript, setWorktreePreRemoveScript] = useState(initialWorktreePreRemoveScript) const [implTabPreCreateScript, setImplTabPreCreateScript] = useState(initialImplTabPreCreateScript) const [implTabPostCloseScript, setImplTabPostCloseScript] = useState(initialImplTabPostCloseScript) + const [splitNewTerminals, setSplitNewTerminals] = useState(initialSplitNewTerminals) const [conflictResolutionProfilePath, setConflictResolutionProfilePath] = useState(initialConflictResolutionProfilePath) const [commitProfilePath, setCommitProfilePath] = useState(initialCommitProfilePath) const [prDiffSummaryProfilePath, setPrDiffSummaryProfilePath] = useState(initialPrDiffSummaryProfilePath) @@ -246,6 +250,7 @@ export function SettingsModal({ setWorktreePreRemoveScript(initialWorktreePreRemoveScript) setImplTabPreCreateScript(initialImplTabPreCreateScript) setImplTabPostCloseScript(initialImplTabPostCloseScript) + setSplitNewTerminals(initialSplitNewTerminals) setConflictResolutionProfilePath(initialConflictResolutionProfilePath) setCommitProfilePath(initialCommitProfilePath) setPrDiffSummaryProfilePath(initialPrDiffSummaryProfilePath) @@ -336,6 +341,7 @@ export function SettingsModal({ worktreePreRemoveScript: worktreePreRemoveScript.trim(), implTabPreCreateScript: implTabPreCreateScript.trim(), implTabPostCloseScript: implTabPostCloseScript.trim(), + splitNewTerminals, // 冲突解决 profile:空串 = 用 DEFAULT_PROFILE_PATH conflictResolutionProfilePath: conflictResolutionProfilePath.trim(), // 提交代码 profile:空串 = 未配置 → 提交严格失败 @@ -910,6 +916,19 @@ export function SettingsModal({ {activeGroup === 'hooks' && ( <> + + +