diff --git a/modules/terminal_manager.py b/modules/terminal_manager.py index d33fb0b..811c62a 100644 --- a/modules/terminal_manager.py +++ b/modules/terminal_manager.py @@ -416,16 +416,22 @@ class TerminalManager: } terminal = self.terminals[target_session] - output = terminal.get_output(last_n_lines) + snapshot = terminal.get_snapshot( + last_n_lines or self.default_snapshot_lines, + self.max_snapshot_chars + ) + output = snapshot.get("output") if snapshot.get("success") else terminal.get_output(last_n_lines) return { "success": True, "session": target_session, "output": output, - "is_interactive": terminal.is_interactive, - "last_command": terminal.last_command, - "seconds_since_last_output": terminal._seconds_since_last_output(), - "echo_loop_detected": terminal.echo_loop_detected + "is_interactive": snapshot.get("is_interactive", terminal.is_interactive), + "last_command": snapshot.get("last_command", terminal.last_command), + "seconds_since_last_output": snapshot.get("seconds_since_last_output", terminal._seconds_since_last_output()), + "echo_loop_detected": snapshot.get("echo_loop_detected", terminal.echo_loop_detected), + "lines_returned": snapshot.get("lines_returned"), + "truncated": snapshot.get("truncated", False) } def get_terminal_snapshot( diff --git a/static/index.html b/static/index.html index ae7d0f8..9f39156 100644 --- a/static/index.html +++ b/static/index.html @@ -616,6 +616,7 @@ +
diff --git a/static/terminal.html b/static/terminal.html index f0a31f0..f93a1c5 100644 --- a/static/terminal.html +++ b/static/terminal.html @@ -14,31 +14,36 @@ padding: 0; box-sizing: border-box; } - + :root { + --claude-bg: #eeece2; + --claude-panel: rgba(255, 255, 255, 0.92); + --claude-border: rgba(118, 103, 84, 0.25); + --claude-text: #3d3929; + --claude-text-secondary: #7f7766; + --claude-accent: #da7756; + --claude-muted: rgba(121, 109, 94, 0.45); + } body { - font-family: 'Consolas', 'Monaco', 'Courier New', monospace; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: #fff; + font-family: 'Iowan Old Style', ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + background: var(--claude-bg); + color: var(--claude-text); height: 100vh; display: flex; flex-direction: column; + overflow: hidden; } - - /* 头部区域 */ .header { - background: rgba(0, 0, 0, 0.3); - padding: 15px 20px; - backdrop-filter: blur(10px); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + background: var(--claude-bg); + padding: 15px 24px; + border-bottom: 1px solid var(--claude-border); + box-shadow: 0 10px 20px rgba(61, 57, 41, 0.08); } - .header h1 { font-size: 24px; display: flex; align-items: center; gap: 10px; } - .status-indicator { width: 12px; height: 12px; @@ -46,109 +51,87 @@ background: #4ade80; animation: pulse 2s infinite; } - @keyframes pulse { - 0% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.7); } + 0% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.5); } 70% { box-shadow: 0 0 0 10px rgba(74, 222, 128, 0); } 100% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0); } } - .status-indicator.disconnected { background: #ef4444; animation: none; } - - /* 会话标签栏 */ .session-tabs { - background: rgba(0, 0, 0, 0.2); - padding: 10px 20px; + background: var(--claude-bg); + padding: 12px 24px; display: flex; gap: 10px; overflow-x: auto; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 1px solid var(--claude-border); + box-shadow: inset 0 -1px rgba(61, 57, 41, 0.05); } - .tab { padding: 8px 16px; - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 8px; + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(118, 103, 84, 0.25); + border-radius: 10px; cursor: pointer; - transition: all 0.3s; + transition: all 0.25s ease; display: flex; align-items: center; gap: 8px; white-space: nowrap; + color: var(--claude-text); } - .tab:hover { - background: rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.95); + box-shadow: 0 6px 16px rgba(61, 57, 41, 0.12); transform: translateY(-2px); } - .tab.active { - background: #007acc; - border-color: #007acc; - box-shadow: 0 4px 12px rgba(0, 122, 204, 0.3); + background: var(--claude-accent); + border-color: var(--claude-accent); + color: #fffaf0; + box-shadow: 0 10px 24px rgba(218, 119, 86, 0.26); } - .tab-icon { font-size: 14px; } - - .tab-close { - margin-left: 8px; - cursor: pointer; - opacity: 0.6; - transition: opacity 0.2s; - } - - .tab-close:hover { - opacity: 1; - } - - /* 主要内容区域 */ .main-content { flex: 1; display: flex; - padding: 20px; - gap: 20px; + padding: 22px; + gap: 22px; overflow: hidden; } - - /* 终端容器 */ .terminal-wrapper { flex: 1; - background: #1e1e1e; - border-radius: 12px; + background: #ffffff; + border-radius: 20px; overflow: hidden; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); + box-shadow: 0 24px 48px rgba(61, 57, 41, 0.16); display: flex; flex-direction: column; + border: 1px solid var(--claude-border); } - .terminal-header { - background: #2d2d2d; - padding: 10px 15px; + background: rgba(255, 255, 255, 0.86); + padding: 12px 18px; display: flex; justify-content: space-between; align-items: center; - border-bottom: 1px solid #444; + border-bottom: 1px solid var(--claude-border); } - .terminal-title { display: flex; align-items: center; gap: 10px; font-size: 14px; - color: #888; + color: var(--claude-text-secondary); } - .terminal-controls { display: flex; gap: 8px; } - .control-btn { width: 12px; height: 12px; @@ -156,129 +139,129 @@ cursor: pointer; transition: opacity 0.2s; } - .control-btn:hover { opacity: 0.8; } - .control-btn.close { background: #ff5f56; } .control-btn.minimize { background: #ffbd2e; } .control-btn.maximize { background: #27c93f; } - + .terminal-body { + flex: 1; + padding: 0 28px 0 32px; + background: #ffffff; + display: flex; + align-items: stretch; + justify-content: stretch; + } #terminal { flex: 1; - padding: 10px; - padding-bottom: 30px; /* 增加底部内边距 */ - overflow-y: auto; /* 确保可以滚动 */ + height: 100%; + margin: 0 28px 0 36px; + background: #ffffff; + color: var(--claude-text); + overflow: hidden; + } + #terminal .xterm-viewport { + overflow-x: hidden !important; + padding-bottom: 0; + } + #terminal .xterm-scroll-area { + margin-bottom: 32px; + } + #terminal .xterm-rows { + padding-bottom: 20px; } - - /* 侧边栏信息 */ .sidebar { width: 300px; - background: rgba(0, 0, 0, 0.2); - border-radius: 12px; + background: var(--claude-panel); + border-radius: 18px; padding: 20px; - backdrop-filter: blur(10px); + border: 1px solid var(--claude-border); + box-shadow: 0 18px 40px rgba(61, 57, 41, 0.1); overflow-y: auto; } - .info-section { margin-bottom: 20px; } - .info-section h3 { font-size: 14px; margin-bottom: 10px; - color: rgba(255, 255, 255, 0.7); - text-transform: uppercase; - letter-spacing: 1px; + color: var(--claude-text-secondary); + letter-spacing: 0.05em; } - .info-item { display: flex; justify-content: space-between; padding: 8px 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 1px solid rgba(118, 103, 84, 0.15); font-size: 14px; } - .info-label { - color: rgba(255, 255, 255, 0.6); + color: var(--claude-text-secondary); } - .info-value { - color: #fff; - font-weight: 500; + color: var(--claude-text); + font-weight: 600; } - - /* 命令历史 */ .command-history { - max-height: 200px; + max-height: 210px; overflow-y: auto; - background: rgba(0, 0, 0, 0.2); - border-radius: 8px; - padding: 10px; + background: rgba(255, 255, 255, 0.78); + border-radius: 12px; + padding: 12px; + border: 1px solid rgba(118, 103, 84, 0.2); } - .command-item { - padding: 5px; - margin: 2px 0; - font-family: monospace; + padding: 6px; + margin: 3px 0; + font-family: inherit; font-size: 12px; - color: #4ade80; - border-left: 2px solid #4ade80; + color: var(--claude-accent); + border-left: 3px solid var(--claude-accent); padding-left: 10px; + background: rgba(218, 119, 86, 0.08); + border-radius: 6px; } - - /* 底部状态栏 */ .status-bar { - background: rgba(0, 0, 0, 0.3); - padding: 10px 20px; + background: var(--claude-panel); + padding: 12px 24px; display: flex; justify-content: space-between; align-items: center; - font-size: 12px; - border-top: 1px solid rgba(255, 255, 255, 0.1); + font-size: 13px; + border-top: 1px solid var(--claude-border); + box-shadow: 0 -8px 18px rgba(61, 57, 41, 0.08); } - .status-left { display: flex; gap: 20px; } - .status-item { display: flex; align-items: center; - gap: 5px; + gap: 6px; + color: var(--claude-text-secondary); } - - /* 响应式设计 */ @media (max-width: 768px) { .main-content { flex-direction: column; } - .sidebar { width: 100%; } } - - /* 加载动画 */ .loader { width: 40px; height: 40px; - border: 3px solid rgba(255, 255, 255, 0.3); - border-top-color: #fff; + border: 3px solid rgba(61, 57, 41, 0.15); + border-top-color: var(--claude-accent); border-radius: 50%; animation: spin 1s linear infinite; margin: 20px auto; } - @keyframes spin { to { transform: rotate(360deg); } } - - /* 提示信息 */ .tooltip { position: absolute; background: rgba(0, 0, 0, 0.8); @@ -325,7 +308,9 @@ - +