mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-11-28 21:20:25 +08:00
the primary purpose of this PR is to fix a showstopper bug in the CodeEditor component by setting "path" to a stable UUID. the bug was that it started as empty string, so it created a shared model between all of the codeeditor components. now each will get their own monaco model. also took this opportunity to do more more preview view refactoring, splitting up code, and more tailwind migrations.
89 lines
3.5 KiB
TypeScript
89 lines
3.5 KiB
TypeScript
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { Button } from "@/app/element/button";
|
|
import { CenteredDiv } from "@/app/element/quickelems";
|
|
import { getWebServerEndpoint } from "@/util/endpoints";
|
|
import { formatRemoteUri } from "@/util/waveutil";
|
|
import { useAtomValue } from "jotai";
|
|
import { TransformComponent, TransformWrapper, useControls } from "react-zoom-pan-pinch";
|
|
import type { SpecializedViewProps } from "./preview";
|
|
|
|
function ImageZoomControls() {
|
|
const { zoomIn, zoomOut, resetTransform } = useControls();
|
|
|
|
return (
|
|
<div className="absolute flex flex-row z-[2] top-0 right-0 p-[5px] gap-1">
|
|
<Button onClick={() => zoomIn()} title="Zoom In" className="py-1 px-[5px]">
|
|
<i className="fa-sharp fa-plus" />
|
|
</Button>
|
|
<Button onClick={() => zoomOut()} title="Zoom Out" className="py-1 px-[5px]">
|
|
<i className="fa-sharp fa-minus" />
|
|
</Button>
|
|
<Button onClick={() => resetTransform()} title="Reset Zoom" className="py-1 px-[5px]">
|
|
<i className="fa-sharp fa-rotate-left" />
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function StreamingImagePreview({ url }: { url: string }) {
|
|
return (
|
|
<div className="flex flex-row h-full overflow-hidden items-center justify-center relative">
|
|
<TransformWrapper initialScale={1} centerOnInit pinch={{ step: 10 }}>
|
|
{({ zoomIn, zoomOut, resetTransform, ...rest }) => (
|
|
<>
|
|
<ImageZoomControls />
|
|
<TransformComponent wrapperClass="!h-full !w-full">
|
|
<img src={url} className="z-[1]" />
|
|
</TransformComponent>
|
|
</>
|
|
)}
|
|
</TransformWrapper>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function StreamingPreview({ model }: SpecializedViewProps) {
|
|
const conn = useAtomValue(model.connection);
|
|
const fileInfo = useAtomValue(model.statFile);
|
|
const filePath = fileInfo.path;
|
|
const remotePath = formatRemoteUri(filePath, conn);
|
|
const usp = new URLSearchParams();
|
|
usp.set("path", remotePath);
|
|
if (conn != null) {
|
|
usp.set("connection", conn);
|
|
}
|
|
const streamingUrl = `${getWebServerEndpoint()}/wave/stream-file?${usp.toString()}`;
|
|
if (fileInfo.mimetype === "application/pdf") {
|
|
return (
|
|
<div className="flex flex-row h-full overflow-hidden items-center justify-center p-[5px]">
|
|
<iframe src={streamingUrl} width="100%" height="100%" name="pdfview" />
|
|
</div>
|
|
);
|
|
}
|
|
if (fileInfo.mimetype.startsWith("video/")) {
|
|
return (
|
|
<div className="flex flex-row h-full overflow-hidden items-center justify-center">
|
|
<video controls className="w-full h-full p-[10px] object-contain">
|
|
<source src={streamingUrl} />
|
|
</video>
|
|
</div>
|
|
);
|
|
}
|
|
if (fileInfo.mimetype.startsWith("audio/")) {
|
|
return (
|
|
<div className="flex flex-row h-full overflow-hidden items-center justify-center">
|
|
<audio controls className="w-full h-full p-[10px] object-contain">
|
|
<source src={streamingUrl} />
|
|
</audio>
|
|
</div>
|
|
);
|
|
}
|
|
if (fileInfo.mimetype.startsWith("image/")) {
|
|
return <StreamingImagePreview url={streamingUrl} />;
|
|
}
|
|
return <CenteredDiv>Preview Not Supported</CenteredDiv>;
|
|
}
|
|
|
|
export { StreamingPreview };
|