mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 07:35:24 +00:00
fix: session log player scrolling off screen horizontally
This commit is contained in:
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"
|
||||||
|
|||||||
Reference in new issue
Block a user