mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 22:55:24 +00:00
feat: convert file manager into a plugin
This commit is contained in:
1 parent
b76da9ab52
commit
c85c1fdb86
259 files changed
+54155
-16791
No files matched your search
@@ -0,0 +1,151 @@
|
||||
import React, { useState } from "react";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { AlertCircle, Download } from "lucide-react";
|
||||
import { Button } from "@termix/plugin-sdk/ui";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// No workerSrc: the termix-plugin CLI's esbuild step has no asset-copy
|
||||
// pipeline for a worker file the way core's Vite build does with `?url`, so
|
||||
// pdfjs falls back to parsing on the main thread instead. Fine for the
|
||||
// config/log-sized files this preview usually opens; a real worker needs the
|
||||
// CLI to gain static-asset output first (see FINISH-LIST.md).
|
||||
|
||||
interface PdfPreviewProps {
|
||||
content: string;
|
||||
onDownload?: () => void;
|
||||
onMediaDimensionsChange?: (dimensions: {
|
||||
width: number;
|
||||
height: number;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export function PdfPreview({
|
||||
content,
|
||||
onDownload,
|
||||
onMediaDimensionsChange,
|
||||
}: PdfPreviewProps) {
|
||||
const { t } = useTranslation();
|
||||
const [numPages, setNumPages] = useState<number | null>(null);
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
const [pdfScale, setPdfScale] = useState(1.2);
|
||||
const [pdfError, setPdfError] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-background">
|
||||
<div className="flex-shrink-0 bg-muted/30 border-b border-border p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPageNumber(Math.max(1, pageNumber - 1))}
|
||||
disabled={pageNumber <= 1}
|
||||
>
|
||||
{t("fileManager.previous")}
|
||||
</Button>
|
||||
<span className="text-sm text-foreground px-3 py-1 bg-background border border-border">
|
||||
{t("fileManager.pageXOfY", {
|
||||
current: pageNumber,
|
||||
total: numPages || 0,
|
||||
})}
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
setPageNumber(Math.min(numPages || 1, pageNumber + 1))
|
||||
}
|
||||
disabled={!numPages || pageNumber >= numPages}
|
||||
>
|
||||
{t("fileManager.next")}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPdfScale(Math.max(0.5, pdfScale - 0.2))}
|
||||
>
|
||||
{t("fileManager.zoomOut")}
|
||||
</Button>
|
||||
<span className="text-sm text-foreground px-3 py-1 bg-background border border-border min-w-[80px] text-center">
|
||||
{Math.round(pdfScale * 100)}%
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPdfScale(Math.min(3.0, pdfScale + 0.2))}
|
||||
>
|
||||
{t("fileManager.zoomIn")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-auto thin-scrollbar p-6 bg-surface">
|
||||
<div className="flex justify-center">
|
||||
{pdfError ? (
|
||||
<div className="text-center text-muted-foreground p-8">
|
||||
<AlertCircle className="w-16 h-16 mx-auto mb-4 text-muted-foreground/50" />
|
||||
<h3 className="text-lg font-medium mb-2">
|
||||
{t("fileManager.cannotLoadPdf")}
|
||||
</h3>
|
||||
<p className="text-sm mb-4">{t("fileManager.pdfLoadError")}</p>
|
||||
{onDownload && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onDownload}
|
||||
className="flex items-center gap-2 mx-auto"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
{t("fileManager.download")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Document
|
||||
file={`data:application/pdf;base64,${content}`}
|
||||
onLoadSuccess={({ numPages }) => {
|
||||
setNumPages(numPages);
|
||||
setPdfError(false);
|
||||
|
||||
onMediaDimensionsChange?.({
|
||||
width: 800,
|
||||
height: 600,
|
||||
});
|
||||
}}
|
||||
onLoadError={(error) => {
|
||||
console.error("PDF load error:", error);
|
||||
setPdfError(true);
|
||||
}}
|
||||
loading={
|
||||
<div className="text-center p-8">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-2"></div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("fileManager.loadingPdf")}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Page
|
||||
pageNumber={pageNumber}
|
||||
scale={pdfScale}
|
||||
className="shadow-lg"
|
||||
loading={
|
||||
<div className="text-center p-4">
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-500 mx-auto mb-2"></div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("fileManager.loadingPage")}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</Document>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user