fix: session log player scrolling off screen horizontally

This commit is contained in:
LukeGus committed 2026-10-01 13:14:00 -05:00
1 parent deb86d11b6
commit 744510520d
2 files changed
+33 -12

No files matched your search

@@ -517,7 +517,9 @@ export function SessionLogsPanel() {
</div> </div>
{/* Log content */} {/* Log content */}
<div className="flex-1 min-h-0 overflow-y-auto"> <div
className={`flex-1 min-h-0 min-w-0 ${viewBlob ? "overflow-hidden" : "overflow-y-auto overflow-x-hidden"}`}
>
{viewLoading ? ( {viewLoading ? (
<div className="flex items-center justify-center py-16"> <div className="flex items-center justify-center py-16">
<Loader2 className="size-4 animate-spin text-muted-foreground" /> <Loader2 className="size-4 animate-spin text-muted-foreground" />
@@ -2,7 +2,6 @@ import { useTranslation } from "@termix/plugin-sdk/frontend";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { Pause, Play } from "lucide-react"; import { Pause, Play } from "lucide-react";
import { Terminal } from "@xterm/xterm"; import { Terminal } from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import Guacamole from "guacamole-common-js"; import Guacamole from "guacamole-common-js";
import "@xterm/xterm/css/xterm.css"; import "@xterm/xterm/css/xterm.css";
import { parseAsciicast, type Asciicast } from "./asciicast"; import { parseAsciicast, type Asciicast } from "./asciicast";
@@ -144,6 +143,7 @@ function PlaybackControls({
function AsciicastPlayer({ blob }: { blob: Blob }) { function AsciicastPlayer({ blob }: { blob: Blob }) {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const screenRef = useRef<HTMLDivElement>(null);
const terminalRef = useRef<Terminal | null>(null); const terminalRef = useRef<Terminal | null>(null);
const recordingRef = useRef<Asciicast | null>(null); const recordingRef = useRef<Asciicast | null>(null);
const positionRef = useRef(0); const positionRef = useRef(0);
@@ -178,7 +178,9 @@ function AsciicastPlayer({ blob }: { blob: Blob }) {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!containerRef.current) return; const container = containerRef.current;
const screen = screenRef.current;
if (!container || !screen) return;
const terminal = new Terminal({ const terminal = new Terminal({
cursorBlink: false, cursorBlink: false,
disableStdin: true, disableStdin: true,
@@ -186,13 +188,24 @@ function AsciicastPlayer({ blob }: { blob: Blob }) {
fontSize: 12, fontSize: 12,
theme: { background: "#09090b" }, theme: { background: "#09090b" },
}); });
const fitAddon = new FitAddon(); terminal.open(screen);
terminal.loadAddon(fitAddon);
terminal.open(containerRef.current);
terminalRef.current = terminal; terminalRef.current = terminal;
const observer = new ResizeObserver(() => fitAddon.fit()); // The recording keeps its own size, scaled down to fit the panel.
observer.observe(containerRef.current); const fit = () => {
fitAddon.fit(); const width = screen.offsetWidth;
const height = screen.offsetHeight;
if (!width || !height) return;
const scale = Math.min(
1,
container.clientWidth / width,
container.clientHeight / height,
);
screen.style.transform = `scale(${scale})`;
};
const observer = new ResizeObserver(fit);
observer.observe(container);
observer.observe(screen);
const resizeListener = terminal.onResize(() => requestAnimationFrame(fit));
blob blob
.text() .text()
@@ -207,6 +220,7 @@ function AsciicastPlayer({ blob }: { blob: Blob }) {
return () => { return () => {
observer.disconnect(); observer.disconnect();
resizeListener.dispose();
terminal.dispose(); terminal.dispose();
terminalRef.current = null; terminalRef.current = null;
}; };
@@ -232,8 +246,13 @@ function AsciicastPlayer({ blob }: { blob: Blob }) {
if (error) return <div className="p-4 text-xs text-destructive">{error}</div>; if (error) return <div className="p-4 text-xs text-destructive">{error}</div>;
return ( return (
<div className="flex h-full min-h-0 flex-col"> <div className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden">
<div ref={containerRef} className="min-h-0 flex-1 bg-[#09090b] p-2" /> <div
ref={containerRef}
className="min-h-0 min-w-0 flex-1 overflow-hidden bg-[#09090b] p-2"
>
<div ref={screenRef} className="w-max origin-top-left" />
</div>
<PlaybackControls <PlaybackControls
playing={playing} playing={playing}
position={position} position={position}
@@ -314,7 +333,7 @@ function GuacamolePlayer({ blob }: { blob: Blob }) {
if (error) return <div className="p-4 text-xs text-destructive">{error}</div>; if (error) return <div className="p-4 text-xs text-destructive">{error}</div>;
return ( return (
<div className="flex h-full min-h-0 flex-col"> <div className="flex h-full min-h-0 min-w-0 flex-col overflow-hidden">
<div <div
ref={containerRef} ref={containerRef}
className="min-h-0 flex-1 overflow-hidden bg-black" className="min-h-0 flex-1 overflow-hidden bg-black"