Fix xterm clipping; move mic + Jump to Current into status bar
Build App / compute-version (pull_request) Successful in 3s
Build App / build-macos (pull_request) Successful in 2m19s
Build App / build-linux (pull_request) Successful in 5m3s
Build App / build-windows (pull_request) Successful in 7m34s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
Build App / compute-version (pull_request) Successful in 3s
Build App / build-macos (pull_request) Successful in 2m19s
Build App / build-linux (pull_request) Successful in 5m3s
Build App / build-windows (pull_request) Successful in 7m34s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
Terminal layout fixes for the xterm pane: - Stop the terminal grid from clipping its rightmost column / bottom row. The padding was on the element xterm mounts into, which the FitAddon measures; the grid overhang got clipped. Padding now lives on a wrapper and the xterm host fills it with no padding. - Move the STT mic from a floating bottom-left overlay into the status bar (far right). A single useSTT instance bound to the active session now lives in App; Ctrl+Shift+M routes through the store. - Move "Jump to Current" from a floating terminal overlay into the status bar. The active TerminalView surfaces its scroll state and scroll action via the store. - Tighten terminal padding (was 8/12/48/16) now that nothing floats over it, so the terminal claims as much area as possible. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,6 @@ import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import { useTerminal } from "../../hooks/useTerminal";
|
||||
import { useAppState } from "../../store/appState";
|
||||
import { useSTT } from "../../hooks/useSTT";
|
||||
import SttButton from "./SttButton";
|
||||
import { awsSsoRefresh } from "../../lib/tauri-commands";
|
||||
import { UrlDetector } from "../../lib/urlDetector";
|
||||
import UrlToast from "./UrlToast";
|
||||
@@ -29,10 +27,8 @@ export default function TerminalView({ sessionId, active }: Props) {
|
||||
const detectorRef = useRef<UrlDetector | null>(null);
|
||||
const { sendInput, pasteImage, resize, onOutput, onExit } = useTerminal();
|
||||
const setTerminalHasSelection = useAppState(s => s.setTerminalHasSelection);
|
||||
const sttEnabled = useAppState(s => s.appSettings?.stt?.enabled);
|
||||
const stt = useSTT(sessionId, sendInput);
|
||||
const sttToggleRef = useRef(stt.toggle);
|
||||
sttToggleRef.current = stt.toggle;
|
||||
const setTerminalAtBottom = useAppState(s => s.setTerminalAtBottom);
|
||||
const setScrollActiveToBottom = useAppState(s => s.setScrollActiveToBottom);
|
||||
|
||||
const ssoBufferRef = useRef("");
|
||||
const ssoTriggeredRef = useRef(false);
|
||||
@@ -111,9 +107,10 @@ export default function TerminalView({ sessionId, active }: Props) {
|
||||
}
|
||||
return false; // prevent xterm from processing this key
|
||||
}
|
||||
// Ctrl+Shift+M toggles speech-to-text recording
|
||||
// Ctrl+Shift+M toggles speech-to-text recording (mic lives in the status
|
||||
// bar, bound to the active session; trigger it via the store).
|
||||
if (event.type === "keydown" && event.ctrlKey && event.shiftKey && event.key === "M") {
|
||||
sttToggleRef.current();
|
||||
useAppState.getState().sttToggle();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -393,6 +390,14 @@ export default function TerminalView({ sessionId, active }: Props) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Surface this terminal's scroll state to the status bar's "Jump to Current"
|
||||
// control, but only while it's the active (visible) terminal.
|
||||
useEffect(() => {
|
||||
if (!active) return;
|
||||
setTerminalAtBottom(isAtBottom);
|
||||
setScrollActiveToBottom(handleScrollToBottom);
|
||||
}, [active, isAtBottom, handleScrollToBottom, setTerminalAtBottom, setScrollActiveToBottom]);
|
||||
|
||||
const writeSelection = useCallback((mode: "trimmed" | "raw") => {
|
||||
const term = termRef.current;
|
||||
if (!term) return;
|
||||
@@ -457,23 +462,19 @@ export default function TerminalView({ sessionId, active }: Props) {
|
||||
>
|
||||
{isAutoFollow ? "▼ Following" : "▽ Paused"}
|
||||
</button>
|
||||
{/* STT mic button - bottom left */}
|
||||
{sttEnabled && <SttButton state={stt.state} error={stt.error} onToggle={stt.toggle} onCancel={stt.cancelRecording} />}
|
||||
{/* Jump to Current - bottom right, when scrolled up */}
|
||||
{!isAtBottom && (
|
||||
<button
|
||||
onClick={handleScrollToBottom}
|
||||
className="absolute bottom-4 right-4 z-50 px-3 py-1.5 rounded-md text-xs font-medium bg-[#1f2937] text-[#58a6ff] border border-[#30363d] shadow-lg hover:bg-[#2d3748] transition-colors cursor-pointer"
|
||||
>
|
||||
Jump to Current ↓
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="w-full h-full"
|
||||
style={{ padding: "8px 12px 48px 16px" }}
|
||||
onContextMenu={handleContextMenu}
|
||||
/>
|
||||
{/* Padding lives on this wrapper, NOT on the xterm host element. xterm's
|
||||
FitAddon measures the host element it's mounted into; padding there
|
||||
causes the grid to overhang and clip the rightmost column / bottom
|
||||
row. The host below fills this wrapper's content box with no padding.
|
||||
Kept tight so the terminal claims as much area as possible; right side
|
||||
leaves a little room beside the scrollbar. */}
|
||||
<div className="w-full h-full" style={{ padding: "4px 8px 4px 8px" }}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="w-full h-full"
|
||||
onContextMenu={handleContextMenu}
|
||||
/>
|
||||
</div>
|
||||
{contextMenu && (
|
||||
<TerminalContextMenu
|
||||
x={contextMenu.x}
|
||||
|
||||
Reference in New Issue
Block a user