终端数据保留
Some checks failed
Build Linux / Build Linux x86_64 (push) Has been cancelled
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Linux os:ubuntu-latest rust-target:x86_64-unknown-linux-gnu]) (push) Has been cancelled
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:Windows os:windows-latest rust-target:x86_64-pc-windows-msvc]) (push) Has been cancelled
Build Test / Build Test (${{ matrix.platform.name }}) (map[name:macOS os:macos-latest rust-target:x86_64-apple-darwin]) (push) Has been cancelled
Build Test / Build Test Summary (push) Has been cancelled

This commit is contained in:
2025-08-15 01:34:15 +08:00
parent 830f6e42a8
commit 452c2ccf9b
3 changed files with 182 additions and 60 deletions

View File

@@ -1889,8 +1889,8 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
visible: true, visible: true,
content: ( content: (
<MainContentArea isEditing={layout.activeView === 'editor'}> <MainContentArea isEditing={layout.activeView === 'editor'}>
{layout.activeView === 'terminal' ? ( {/* 终端始终渲染,通过显示/隐藏控制 */}
// 终端视图 <div className={cn("absolute inset-0", layout.activeView === 'terminal' ? 'block' : 'hidden')}>
<Terminal <Terminal
onClose={closeTerminal} onClose={closeTerminal}
isMaximized={layout.isTerminalMaximized} isMaximized={layout.isTerminalMaximized}
@@ -1898,7 +1898,11 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
projectPath={projectPath} projectPath={projectPath}
className="h-full w-full" className="h-full w-full"
/> />
) : layout.activeView === 'editor' && layout.editingFile ? ( </div>
{/* 其他视图 */}
<div className={cn("h-full w-full", layout.activeView === 'terminal' ? 'hidden' : 'block')}>
{layout.activeView === 'editor' && layout.editingFile ? (
// 文件编辑器视图 // 文件编辑器视图
<FileEditorEnhanced <FileEditorEnhanced
filePath={layout.editingFile} filePath={layout.editingFile}
@@ -2114,6 +2118,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
} }
/> />
)} )}
</div>
</MainContentArea> </MainContentArea>
) )
}, },

View File

@@ -32,6 +32,7 @@ export const Terminal: React.FC<TerminalProps> = ({
const [isConnected, setIsConnected] = useState(false); const [isConnected, setIsConnected] = useState(false);
const [sessionId, setSessionId] = useState<string | null>(null); const [sessionId, setSessionId] = useState<string | null>(null);
const [terminalSize, setTerminalSize] = useState({ cols: 80, rows: 24 }); const [terminalSize, setTerminalSize] = useState({ cols: 80, rows: 24 });
const [containerWidth, setContainerWidth] = useState(0);
// 计算终端应该有的尺寸 // 计算终端应该有的尺寸
const calculateOptimalSize = useCallback(() => { const calculateOptimalSize = useCallback(() => {
@@ -46,17 +47,26 @@ export const Terminal: React.FC<TerminalProps> = ({
const lineHeight = fontSize * 1.2; // 行高 const lineHeight = fontSize * 1.2; // 行高
// 计算能容纳的最大列数和行数 // 计算能容纳的最大列数和行数
// 减去一些像素避免滚动条 const availableWidth = rect.width - 2;
const cols = Math.max(80, Math.floor((rect.width - 2) / charWidth)); const availableHeight = rect.height - 2;
const rows = Math.max(24, Math.floor((rect.height - 2) / lineHeight));
console.log('[Terminal] Calculated size:', { const cols = Math.max(80, Math.floor(availableWidth / charWidth));
const rows = Math.max(24, Math.floor(availableHeight / lineHeight));
// 计算实际使用的宽度
const usedWidth = cols * charWidth;
const unusedWidth = availableWidth - usedWidth;
const percentUsed = ((usedWidth / availableWidth) * 100).toFixed(1);
console.log('[Terminal] Size calculation:', {
containerWidth: rect.width, containerWidth: rect.width,
containerHeight: rect.height, availableWidth,
cols,
rows,
charWidth, charWidth,
lineHeight cols,
usedWidth,
unusedWidth,
percentUsed: `${percentUsed}%`,
message: unusedWidth > 10 ? `还有 ${unusedWidth.toFixed(1)}px 未使用` : '宽度使用正常'
}); });
return { cols, rows }; return { cols, rows };
@@ -66,29 +76,73 @@ export const Terminal: React.FC<TerminalProps> = ({
const resizeTerminal = useCallback(() => { const resizeTerminal = useCallback(() => {
if (!xtermRef.current || !terminalRef.current) return; if (!xtermRef.current || !terminalRef.current) return;
const newSize = calculateOptimalSize(); // 先尝试获取实际的字符尺寸
let actualCharWidth = 8.4; // 默认值
let actualLineHeight = 16.8; // 默认值
try {
const core = (xtermRef.current as any)._core;
if (core && core._renderService && core._renderService.dimensions) {
const dims = core._renderService.dimensions;
if (dims.actualCellWidth) actualCharWidth = dims.actualCellWidth;
if (dims.actualCellHeight) actualLineHeight = dims.actualCellHeight;
console.log('[Terminal] Using actual char dimensions:', {
actualCharWidth,
actualLineHeight
});
}
} catch (e) {
// 使用默认值
}
// 使用实际字符尺寸计算新的列数和行数
const rect = terminalRef.current.getBoundingClientRect();
const availableWidth = rect.width - 2;
const availableHeight = rect.height - 2;
// 更新容器宽度显示
setContainerWidth(rect.width);
const newCols = Math.max(80, Math.floor(availableWidth / actualCharWidth));
const newRows = Math.max(24, Math.floor(availableHeight / actualLineHeight));
// 计算宽度使用情况
const usedWidth = newCols * actualCharWidth;
const unusedWidth = availableWidth - usedWidth;
const percentUsed = ((usedWidth / availableWidth) * 100).toFixed(1);
// 只有当尺寸真的改变时才调整 // 只有当尺寸真的改变时才调整
if (newSize.cols !== terminalSize.cols || newSize.rows !== terminalSize.rows) { if (newCols !== terminalSize.cols || newRows !== terminalSize.rows) {
console.log('[Terminal] Resizing from', terminalSize, 'to', newSize); console.log('[Terminal] Resizing:', {
from: terminalSize,
to: { cols: newCols, rows: newRows },
containerWidth: rect.width,
availableWidth,
usedWidth,
unusedWidth,
percentUsed: `${percentUsed}%`
});
setTerminalSize(newSize); setTerminalSize({ cols: newCols, rows: newRows });
xtermRef.current.resize(newSize.cols, newSize.rows); xtermRef.current.resize(newCols, newRows);
// 更新后端 // 更新后端
if (sessionId) { if (sessionId) {
api.resizeTerminal(sessionId, newSize.cols, newSize.rows).catch(console.error); api.resizeTerminal(sessionId, newCols, newRows).catch(console.error);
} }
// 强制刷新渲染 // 强制刷新渲染
if ((xtermRef.current as any)._core) { try {
const core = (xtermRef.current as any)._core; const core = (xtermRef.current as any)._core;
if (core._renderService) { if (core && core._renderService && core._renderService._onIntersectionChange) {
core._renderService.onResize(newSize.cols, newSize.rows); core._renderService._onIntersectionChange({ intersectionRatio: 1 });
}
} catch (e) {
// 忽略错误,某些版本的 xterm 可能没有这个方法
} }
} }
} }, [terminalSize, sessionId]);
}, [calculateOptimalSize, terminalSize, sessionId]);
// 防抖的resize处理 // 防抖的resize处理
const handleResize = useCallback(() => { const handleResize = useCallback(() => {
@@ -180,10 +234,47 @@ export const Terminal: React.FC<TerminalProps> = ({
// 保存引用 // 保存引用
xtermRef.current = xterm; xtermRef.current = xterm;
// 延迟一下确保渲染完成,然后调整尺寸 // 延迟一下确保渲染完成,然后获取实际字符尺寸并调整
setTimeout(() => { setTimeout(() => {
if (xtermRef.current && terminalRef.current) {
// 尝试获取实际的字符尺寸
try {
const core = (xtermRef.current as any)._core;
if (core && core._renderService && core._renderService.dimensions) {
const dims = core._renderService.dimensions;
const actualCharWidth = dims.actualCellWidth || dims.scaledCellWidth;
const actualLineHeight = dims.actualCellHeight || dims.scaledCellHeight;
if (actualCharWidth && actualLineHeight) {
console.log('[Terminal] Actual character dimensions:', {
charWidth: actualCharWidth,
lineHeight: actualLineHeight
});
// 使用实际尺寸重新计算
const rect = terminalRef.current.getBoundingClientRect();
const availableWidth = rect.width - 2;
const newCols = Math.floor(availableWidth / actualCharWidth);
console.log('[Terminal] Recalculating with actual dimensions:', {
availableWidth,
actualCharWidth,
newCols,
currentCols: xtermRef.current.cols
});
if (newCols > xtermRef.current.cols) {
xtermRef.current.resize(newCols, xtermRef.current.rows);
setTerminalSize({ cols: newCols, rows: xtermRef.current.rows });
}
}
}
} catch (e) {
console.warn('[Terminal] Could not get actual char dimensions:', e);
}
}
resizeTerminal(); resizeTerminal();
}, 100); }, 150);
// 创建终端会话 // 创建终端会话
const newSessionId = await api.createTerminalSession(projectPath || process.cwd()); const newSessionId = await api.createTerminalSession(projectPath || process.cwd());
@@ -259,7 +350,15 @@ export const Terminal: React.FC<TerminalProps> = ({
useEffect(() => { useEffect(() => {
if (!terminalRef.current) return; if (!terminalRef.current) return;
const resizeObserver = new ResizeObserver(() => { // 初始化时立即获取容器宽度
const rect = terminalRef.current.getBoundingClientRect();
setContainerWidth(rect.width);
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width } = entry.contentRect;
setContainerWidth(width);
}
handleResize(); handleResize();
}); });
@@ -301,6 +400,9 @@ export const Terminal: React.FC<TerminalProps> = ({
<span className="text-xs text-gray-400"> <span className="text-xs text-gray-400">
{terminalSize.cols}×{terminalSize.rows} {terminalSize.cols}×{terminalSize.rows}
</span> </span>
<span className="text-xs text-yellow-400">
: {containerWidth.toFixed(0)}px
</span>
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">

View File

@@ -13,6 +13,8 @@ interface LayoutState {
editingFile: string | null; // 新增:正在编辑的文件 editingFile: string | null; // 新增:正在编辑的文件
previewUrl: string | null; // 新增预览URL previewUrl: string | null; // 新增预览URL
isTerminalMaximized: boolean; // 新增:终端是否最大化 isTerminalMaximized: boolean; // 新增:终端是否最大化
isTerminalOpen: boolean; // 新增:终端是否打开(保持会话)
previousView: 'chat' | 'editor' | 'preview' | null; // 新增:记录终端打开前的视图
} }
interface LayoutBreakpoints { interface LayoutBreakpoints {
@@ -37,6 +39,8 @@ const DEFAULT_LAYOUT: LayoutState = {
editingFile: null, editingFile: null,
previewUrl: null, previewUrl: null,
isTerminalMaximized: false, // 默认终端不最大化 isTerminalMaximized: false, // 默认终端不最大化
isTerminalOpen: false, // 默认终端关闭
previousView: null, // 默认无前一个视图
}; };
const STORAGE_KEY = 'claudia_layout_preferences'; const STORAGE_KEY = 'claudia_layout_preferences';
@@ -301,21 +305,32 @@ export function useLayoutManager(projectPath?: string) {
}); });
}, [saveLayout]); }, [saveLayout]);
// 打开终端 // 打开/切换终端
const openTerminal = useCallback(() => { const openTerminal = useCallback(() => {
if (layout.activeView === 'terminal') {
// 如果终端已经显示,收起它(恢复之前的视图)
saveLayout({
activeView: layout.previousView || 'chat',
previousView: null,
});
} else {
// 显示终端,记住当前视图
const prev = layout.activeView as 'chat' | 'editor' | 'preview';
saveLayout({ saveLayout({
activeView: 'terminal', activeView: 'terminal',
editingFile: null, isTerminalOpen: true,
previewUrl: null, previousView: prev,
}); });
}, [saveLayout]); }
}, [layout.activeView, layout.previousView, saveLayout]);
// 关闭终端 // 关闭终端(恢复之前的视图)
const closeTerminal = useCallback(() => { const closeTerminal = useCallback(() => {
saveLayout({ saveLayout({
activeView: 'chat', activeView: layout.previousView || 'chat',
previousView: null,
}); });
}, [saveLayout]); }, [layout.previousView, saveLayout]);
// 切换终端最大化状态 // 切换终端最大化状态
const toggleTerminalMaximize = useCallback(() => { const toggleTerminalMaximize = useCallback(() => {