mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
feat(ui): display branch name in terminal header with git icon
- Move branch name display from tab name to terminal header - Show full branch name (no truncation) with GitBranch icon - Display branch name for both 'new tab' and 'split' modes - Persist openTerminalMode setting to server and include in import/export - Update settings dropdown to simplified "New Tab" label
This commit is contained in:
@@ -181,7 +181,7 @@ export function TerminalSection() {
|
|||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="newTab">New Tab (named after branch)</SelectItem>
|
<SelectItem value="newTab">New Tab</SelectItem>
|
||||||
<SelectItem value="split">Split Current Tab</SelectItem>
|
<SelectItem value="split">Split Current Tab</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -596,22 +596,27 @@ export function TerminalView() {
|
|||||||
pendingTerminalCreatedRef.current = true;
|
pendingTerminalCreatedRef.current = true;
|
||||||
|
|
||||||
if (openMode === 'newTab') {
|
if (openMode === 'newTab') {
|
||||||
// Create a new tab named after the branch
|
// Create a new tab with default naming
|
||||||
const newTabId = addTerminalTab(pending.branchName);
|
const newTabId = addTerminalTab();
|
||||||
|
|
||||||
// Set the tab's layout to the new terminal
|
// Set the tab's layout to the new terminal with branch name for display in header
|
||||||
useAppStore
|
useAppStore
|
||||||
.getState()
|
.getState()
|
||||||
.setTerminalTabLayout(
|
.setTerminalTabLayout(
|
||||||
newTabId,
|
newTabId,
|
||||||
{ type: 'terminal', sessionId: data.data.id, size: 100 },
|
{
|
||||||
|
type: 'terminal',
|
||||||
|
sessionId: data.data.id,
|
||||||
|
size: 100,
|
||||||
|
branchName: pending.branchName,
|
||||||
|
},
|
||||||
data.data.id
|
data.data.id
|
||||||
);
|
);
|
||||||
toast.success(`Opened terminal for ${pending.branchName}`);
|
toast.success(`Opened terminal for ${pending.branchName}`);
|
||||||
} else {
|
} else {
|
||||||
// Split mode: add to current tab layout
|
// Split mode: add to current tab layout with branch name
|
||||||
addTerminalToLayout(data.data.id);
|
addTerminalToLayout(data.data.id, 'horizontal', undefined, pending.branchName);
|
||||||
toast.success(`Opened terminal in ${pending.cwd.split('/').pop() || pending.cwd}`);
|
toast.success(`Opened terminal for ${pending.branchName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark this session as new for running initial command
|
// Mark this session as new for running initial command
|
||||||
@@ -789,6 +794,7 @@ export function TerminalView() {
|
|||||||
sessionId,
|
sessionId,
|
||||||
size: persisted.size,
|
size: persisted.size,
|
||||||
fontSize: persisted.fontSize,
|
fontSize: persisted.fontSize,
|
||||||
|
branchName: persisted.branchName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1347,6 +1353,7 @@ export function TerminalView() {
|
|||||||
onCommandRan={() => handleCommandRan(content.sessionId)}
|
onCommandRan={() => handleCommandRan(content.sessionId)}
|
||||||
isMaximized={terminalState.maximizedSessionId === content.sessionId}
|
isMaximized={terminalState.maximizedSessionId === content.sessionId}
|
||||||
onToggleMaximize={() => toggleTerminalMaximized(content.sessionId)}
|
onToggleMaximize={() => toggleTerminalMaximized(content.sessionId)}
|
||||||
|
branchName={content.branchName}
|
||||||
/>
|
/>
|
||||||
</TerminalErrorBoundary>
|
</TerminalErrorBoundary>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
Maximize2,
|
Maximize2,
|
||||||
Minimize2,
|
Minimize2,
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
|
GitBranch,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Spinner } from '@/components/ui/spinner';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
@@ -94,6 +95,7 @@ interface TerminalPanelProps {
|
|||||||
onCommandRan?: () => void; // Callback when the initial command has been sent
|
onCommandRan?: () => void; // Callback when the initial command has been sent
|
||||||
isMaximized?: boolean;
|
isMaximized?: boolean;
|
||||||
onToggleMaximize?: () => void;
|
onToggleMaximize?: () => void;
|
||||||
|
branchName?: string; // Branch name to display in header (from "Open in Terminal" action)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type for xterm Terminal - we'll use any since we're dynamically importing
|
// Type for xterm Terminal - we'll use any since we're dynamically importing
|
||||||
@@ -124,6 +126,7 @@ export function TerminalPanel({
|
|||||||
onCommandRan,
|
onCommandRan,
|
||||||
isMaximized = false,
|
isMaximized = false,
|
||||||
onToggleMaximize,
|
onToggleMaximize,
|
||||||
|
branchName,
|
||||||
}: TerminalPanelProps) {
|
}: TerminalPanelProps) {
|
||||||
const terminalRef = useRef<HTMLDivElement>(null);
|
const terminalRef = useRef<HTMLDivElement>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -1776,6 +1779,13 @@ export function TerminalPanel({
|
|||||||
<div className="flex items-center gap-1.5 flex-1 min-w-0">
|
<div className="flex items-center gap-1.5 flex-1 min-w-0">
|
||||||
<Terminal className="h-3 w-3 shrink-0 text-muted-foreground" />
|
<Terminal className="h-3 w-3 shrink-0 text-muted-foreground" />
|
||||||
<span className="text-xs truncate text-foreground">{shellName}</span>
|
<span className="text-xs truncate text-foreground">{shellName}</span>
|
||||||
|
{/* Branch name indicator - show when terminal was opened from worktree */}
|
||||||
|
{branchName && (
|
||||||
|
<span className="flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded bg-brand-500/10 text-brand-500 shrink-0">
|
||||||
|
<GitBranch className="h-2.5 w-2.5 shrink-0" />
|
||||||
|
<span>{branchName}</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{/* Font size indicator - only show when not default */}
|
{/* Font size indicator - only show when not default */}
|
||||||
{fontSize !== DEFAULT_FONT_SIZE && (
|
{fontSize !== DEFAULT_FONT_SIZE && (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const SETTINGS_FIELDS_TO_SYNC = [
|
|||||||
'fontFamilySans',
|
'fontFamilySans',
|
||||||
'fontFamilyMono',
|
'fontFamilyMono',
|
||||||
'terminalFontFamily', // Maps to terminalState.fontFamily
|
'terminalFontFamily', // Maps to terminalState.fontFamily
|
||||||
|
'openTerminalMode', // Maps to terminalState.openTerminalMode
|
||||||
'sidebarOpen',
|
'sidebarOpen',
|
||||||
'chatHistoryOpen',
|
'chatHistoryOpen',
|
||||||
'maxConcurrency',
|
'maxConcurrency',
|
||||||
@@ -101,6 +102,9 @@ function getSettingsFieldValue(
|
|||||||
if (field === 'terminalFontFamily') {
|
if (field === 'terminalFontFamily') {
|
||||||
return appState.terminalState.fontFamily;
|
return appState.terminalState.fontFamily;
|
||||||
}
|
}
|
||||||
|
if (field === 'openTerminalMode') {
|
||||||
|
return appState.terminalState.openTerminalMode;
|
||||||
|
}
|
||||||
return appState[field as keyof typeof appState];
|
return appState[field as keyof typeof appState];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +132,9 @@ function hasSettingsFieldChanged(
|
|||||||
if (field === 'terminalFontFamily') {
|
if (field === 'terminalFontFamily') {
|
||||||
return newState.terminalState.fontFamily !== prevState.terminalState.fontFamily;
|
return newState.terminalState.fontFamily !== prevState.terminalState.fontFamily;
|
||||||
}
|
}
|
||||||
|
if (field === 'openTerminalMode') {
|
||||||
|
return newState.terminalState.openTerminalMode !== prevState.terminalState.openTerminalMode;
|
||||||
|
}
|
||||||
const key = field as keyof typeof newState;
|
const key = field as keyof typeof newState;
|
||||||
return newState[key] !== prevState[key];
|
return newState[key] !== prevState[key];
|
||||||
}
|
}
|
||||||
@@ -571,11 +578,16 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
|
|||||||
worktreePanelCollapsed: serverSettings.worktreePanelCollapsed ?? false,
|
worktreePanelCollapsed: serverSettings.worktreePanelCollapsed ?? false,
|
||||||
lastProjectDir: serverSettings.lastProjectDir ?? '',
|
lastProjectDir: serverSettings.lastProjectDir ?? '',
|
||||||
recentFolders: serverSettings.recentFolders ?? [],
|
recentFolders: serverSettings.recentFolders ?? [],
|
||||||
// Terminal font (nested in terminalState)
|
// Terminal settings (nested in terminalState)
|
||||||
...(serverSettings.terminalFontFamily && {
|
...((serverSettings.terminalFontFamily || serverSettings.openTerminalMode) && {
|
||||||
terminalState: {
|
terminalState: {
|
||||||
...currentAppState.terminalState,
|
...currentAppState.terminalState,
|
||||||
fontFamily: serverSettings.terminalFontFamily,
|
...(serverSettings.terminalFontFamily && {
|
||||||
|
fontFamily: serverSettings.terminalFontFamily,
|
||||||
|
}),
|
||||||
|
...(serverSettings.openTerminalMode && {
|
||||||
|
openTerminalMode: serverSettings.openTerminalMode,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1805,8 +1805,6 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
this.post('/api/worktree/switch-branch', { worktreePath, branchName }),
|
this.post('/api/worktree/switch-branch', { worktreePath, branchName }),
|
||||||
openInEditor: (worktreePath: string, editorCommand?: string) =>
|
openInEditor: (worktreePath: string, editorCommand?: string) =>
|
||||||
this.post('/api/worktree/open-in-editor', { worktreePath, editorCommand }),
|
this.post('/api/worktree/open-in-editor', { worktreePath, editorCommand }),
|
||||||
openInTerminal: (worktreePath: string) =>
|
|
||||||
this.post('/api/worktree/open-in-terminal', { worktreePath }),
|
|
||||||
getDefaultEditor: () => this.get('/api/worktree/default-editor'),
|
getDefaultEditor: () => this.get('/api/worktree/default-editor'),
|
||||||
getAvailableEditors: () => this.get('/api/worktree/available-editors'),
|
getAvailableEditors: () => this.get('/api/worktree/available-editors'),
|
||||||
refreshEditors: () => this.post('/api/worktree/refresh-editors', {}),
|
refreshEditors: () => this.post('/api/worktree/refresh-editors', {}),
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ export interface ProjectAnalysis {
|
|||||||
|
|
||||||
// Terminal panel layout types (recursive for splits)
|
// Terminal panel layout types (recursive for splits)
|
||||||
export type TerminalPanelContent =
|
export type TerminalPanelContent =
|
||||||
| { type: 'terminal'; sessionId: string; size?: number; fontSize?: number }
|
| { type: 'terminal'; sessionId: string; size?: number; fontSize?: number; branchName?: string }
|
||||||
| {
|
| {
|
||||||
type: 'split';
|
type: 'split';
|
||||||
id: string; // Stable ID for React key stability
|
id: string; // Stable ID for React key stability
|
||||||
@@ -538,7 +538,7 @@ export interface TerminalState {
|
|||||||
// Persisted terminal layout - now includes sessionIds for reconnection
|
// Persisted terminal layout - now includes sessionIds for reconnection
|
||||||
// Used to restore terminal layout structure when switching projects
|
// Used to restore terminal layout structure when switching projects
|
||||||
export type PersistedTerminalPanel =
|
export type PersistedTerminalPanel =
|
||||||
| { type: 'terminal'; size?: number; fontSize?: number; sessionId?: string }
|
| { type: 'terminal'; size?: number; fontSize?: number; sessionId?: string; branchName?: string }
|
||||||
| {
|
| {
|
||||||
type: 'split';
|
type: 'split';
|
||||||
id?: string; // Optional for backwards compatibility with older persisted layouts
|
id?: string; // Optional for backwards compatibility with older persisted layouts
|
||||||
@@ -576,6 +576,7 @@ export interface PersistedTerminalSettings {
|
|||||||
scrollbackLines: number;
|
scrollbackLines: number;
|
||||||
lineHeight: number;
|
lineHeight: number;
|
||||||
maxSessions: number;
|
maxSessions: number;
|
||||||
|
openTerminalMode: 'newTab' | 'split';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** State for worktree init script execution */
|
/** State for worktree init script execution */
|
||||||
@@ -1217,7 +1218,8 @@ export interface AppActions {
|
|||||||
addTerminalToLayout: (
|
addTerminalToLayout: (
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
direction?: 'horizontal' | 'vertical',
|
direction?: 'horizontal' | 'vertical',
|
||||||
targetSessionId?: string
|
targetSessionId?: string,
|
||||||
|
branchName?: string
|
||||||
) => void;
|
) => void;
|
||||||
removeTerminalFromLayout: (sessionId: string) => void;
|
removeTerminalFromLayout: (sessionId: string) => void;
|
||||||
swapTerminals: (sessionId1: string, sessionId2: string) => void;
|
swapTerminals: (sessionId1: string, sessionId2: string) => void;
|
||||||
@@ -2676,12 +2678,13 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addTerminalToLayout: (sessionId, direction = 'horizontal', targetSessionId) => {
|
addTerminalToLayout: (sessionId, direction = 'horizontal', targetSessionId, branchName) => {
|
||||||
const current = get().terminalState;
|
const current = get().terminalState;
|
||||||
const newTerminal: TerminalPanelContent = {
|
const newTerminal: TerminalPanelContent = {
|
||||||
type: 'terminal',
|
type: 'terminal',
|
||||||
sessionId,
|
sessionId,
|
||||||
size: 50,
|
size: 50,
|
||||||
|
branchName,
|
||||||
};
|
};
|
||||||
|
|
||||||
// If no tabs, create first tab
|
// If no tabs, create first tab
|
||||||
@@ -2694,7 +2697,7 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
{
|
{
|
||||||
id: newTabId,
|
id: newTabId,
|
||||||
name: 'Terminal 1',
|
name: 'Terminal 1',
|
||||||
layout: { type: 'terminal', sessionId, size: 100 },
|
layout: { type: 'terminal', sessionId, size: 100, branchName },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
activeTabId: newTabId,
|
activeTabId: newTabId,
|
||||||
@@ -3396,6 +3399,7 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
size: panel.size,
|
size: panel.size,
|
||||||
fontSize: panel.fontSize,
|
fontSize: panel.fontSize,
|
||||||
sessionId: panel.sessionId, // Preserve for reconnection
|
sessionId: panel.sessionId, // Preserve for reconnection
|
||||||
|
branchName: panel.branchName, // Preserve branch name for display
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -475,6 +475,10 @@ export interface GlobalSettings {
|
|||||||
/** Terminal font family (undefined = use default Menlo/Monaco) */
|
/** Terminal font family (undefined = use default Menlo/Monaco) */
|
||||||
terminalFontFamily?: string;
|
terminalFontFamily?: string;
|
||||||
|
|
||||||
|
// Terminal Configuration
|
||||||
|
/** How to open terminals from "Open in Terminal" worktree action */
|
||||||
|
openTerminalMode?: 'newTab' | 'split';
|
||||||
|
|
||||||
// UI State Preferences
|
// UI State Preferences
|
||||||
/** Whether sidebar is currently open */
|
/** Whether sidebar is currently open */
|
||||||
sidebarOpen: boolean;
|
sidebarOpen: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user