react-os-shell 0.1.18 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ export { Preview as default, setPdfPreview } from './chunk-C3AD7QK2.js';
2
+ import './chunk-WIJ45SYD.js';
3
+ import './chunk-AKZTZLKP.js';
4
+ import './chunk-RFTLYCSF.js';
5
+ //# sourceMappingURL=Preview-EWKE64O5.js.map
6
+ //# sourceMappingURL=Preview-EWKE64O5.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-5GRB2ADJ.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"Preview-EWKE64O5.js"}
@@ -3,14 +3,24 @@ import * as react from 'react';
3
3
  import { W as WindowRegistry } from '../types-BKoa7nhP.js';
4
4
 
5
5
  interface PdfPreviewData {
6
- /** Object URL or remote URL of the PDF. Blob URLs are revoked when the window unmounts. */
7
- url: string;
6
+ /** Object URL or remote URL of the PDF. Blob URLs are revoked when the window unmounts.
7
+ * Leave blank when staging a `converting: true` placeholder; call `setPdfPreview` again
8
+ * with the resolved URL once conversion finishes. */
9
+ url?: string;
8
10
  /** Display name (and download filename). */
9
11
  filename: string;
12
+ /** Renderer to use. Defaults to `'pdf'`. `'dxf'` requires the consumer to
13
+ * have `dxf-viewer` installed (it's an optional peer dep). `'image'`
14
+ * renders an `<img>` for raster screenshots / photos. */
15
+ kind?: 'pdf' | 'dxf' | 'image';
10
16
  /** Optional download handler — replaces the built-in "save URL as filename" if supplied. */
11
17
  onDownload?: () => void;
12
18
  /** Optional email handler — only shown when supplied. */
13
19
  onEmail?: () => void;
20
+ /** Show a progress placeholder while the consumer fetches/converts the file. */
21
+ converting?: boolean;
22
+ /** Headline shown on the converting placeholder (e.g. "CONVERTING DWG FILE"). */
23
+ convertingMessage?: string;
14
24
  }
15
25
  /** Stage a PDF for the next Preview window mount, or swap into an open one. */
16
26
  declare function setPdfPreview(data: PdfPreviewData): void;
@@ -1,5 +1,7 @@
1
- export { setPdfPreview } from '../chunk-KLAHLSYK.js';
1
+ export { setPdfPreview } from '../chunk-C3AD7QK2.js';
2
2
  import '../chunk-WIJ45SYD.js';
3
+ import '../chunk-AKZTZLKP.js';
4
+ import '../chunk-RFTLYCSF.js';
3
5
  import { lazy } from 'react';
4
6
 
5
7
  var Calculator = lazy(() => import('../Calculator-ZZMTB7Y7.js'));
@@ -17,7 +19,7 @@ var Minesweeper = lazy(() => import('../Minesweeper-KAOD327F.js'));
17
19
  var Email = lazy(() => import('../Email-UCNJ53MV.js'));
18
20
  var GeminiChat = lazy(() => import('../GeminiChat-BXLBJFT4.js'));
19
21
  var Calendar = lazy(() => import('../Calendar-RQVSPJAJ.js'));
20
- var Preview = lazy(() => import('../Preview-5GRB2ADJ.js'));
22
+ var Preview = lazy(() => import('../Preview-EWKE64O5.js'));
21
23
  var utilityApps = {
22
24
  "/calculator": { component: Calculator, label: "Calculator", size: "sm", allowPinOnTop: true, utility: true, widget: true, autoHeight: true, dimensions: [280, 420] },
23
25
  "/spreadsheet": { component: Spreadsheet, label: "Spreadsheets", size: "2xl", compact: true, multiInstance: true },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/apps/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Bundled apps — pre-built window registry entries for the 16 apps that ship\n * with `react-os-shell`. Consumers compose them into their own registry via\n * `createWindowRegistry`:\n *\n * import { createWindowRegistry } from 'react-os-shell';\n * import { bundledApps } from 'react-os-shell/apps';\n * import { erpEntities } from './shell-config';\n *\n * const windows = createWindowRegistry(bundledApps, erpEntities);\n *\n * Subsets are also exported (`utilityApps`, `gameApps`, `googleApps`) so a\n * consumer can pick-and-choose without importing every component.\n *\n * NOTE: 4 apps require consumer-supplied persistence (Calendar / Notepad /\n * WorldClock for stored preferences, Minesweeper for leaderboard). They're\n * exported individually but absent from `bundledApps` — wire the prefs\n * provider to opt them in.\n */\nimport { lazy } from 'react';\nimport type { WindowRegistry } from '../windowRegistry/types';\n\n// ── Utility apps ──\nconst Calculator = lazy(() => import('./Calculator'));\nconst Spreadsheet = lazy(() => import('./Spreadsheet'));\nconst Weather = lazy(() => import('./Weather'));\nconst CurrencyConverter = lazy(() => import('./CurrencyConverter'));\nconst PomodoroTimer = lazy(() => import('./PomodoroTimer'));\nconst Notepad = lazy(() => import('./Notepad'));\n\n// ── Games ──\nconst Chess = lazy(() => import('./Chess'));\nconst Checkers = lazy(() => import('./Checkers'));\nconst Sudoku = lazy(() => import('./Sudoku'));\nconst Tetris = lazy(() => import('./Tetris'));\nconst Game2048 = lazy(() => import('./Game2048'));\nconst Minesweeper = lazy(() => import('./Minesweeper'));\n\n// ── Google apps ──\nconst Email = lazy(() => import('./Email'));\nconst GeminiChat = lazy(() => import('./GeminiChat'));\nconst Calendar = lazy(() => import('./Calendar'));\n\n// ── Document apps ──\nconst Preview = lazy(() => import('./Preview'));\n\nexport const utilityApps: WindowRegistry = {\n '/calculator': { component: Calculator, label: 'Calculator', size: 'sm', allowPinOnTop: true, utility: true, widget: true, autoHeight: true, dimensions: [280, 420] },\n '/spreadsheet': { component: Spreadsheet, label: 'Spreadsheets', size: '2xl', compact: true, multiInstance: true },\n '/notepad': { component: Notepad, label: 'Notepad', size: 'lg', allowPinOnTop: true },\n '/weather': { component: Weather, label: 'Weather', size: 'sm', utility: true, widget: true, autoHeight: true, dimensions: [320, 400] },\n '/currency': { component: CurrencyConverter, label: 'Currency Converter', size: 'sm', utility: true, widget: true, autoHeight: true, dimensions: [320, 480] },\n '/pomodoro': { component: PomodoroTimer, label: 'Pomodoro Timer', size: 'sm', utility: true, widget: true, autoHeight: true, dimensions: [320, 420] },\n};\n\nexport const gameApps: WindowRegistry = {\n '/chess': { component: Chess, label: 'Chess', size: 'lg', compact: true },\n '/checkers': { component: Checkers, label: 'Checkers', size: 'lg', compact: true },\n '/sudoku': { component: Sudoku, label: 'Sudoku', size: 'sm', compact: true, dimensions: [360, 535] },\n '/tetris': { component: Tetris, label: 'Tetris', size: 'md', compact: true, dimensions: [452, 618] },\n '/2048': { component: Game2048, label: '2048', size: 'sm', compact: true },\n '/minesweeper': { component: Minesweeper, label: 'Minesweeper', size: 'sm', compact: true },\n};\n\nexport const googleApps: WindowRegistry = {\n '/email': { component: Email, label: 'Email', size: '2xl' },\n '/gemini': { component: GeminiChat, label: 'Gemini AI', size: 'lg' },\n '/calendar': { component: Calendar, label: 'Calendar', size: 'xl' },\n};\n\nexport const documentApps: WindowRegistry = {\n '/preview': { component: Preview, label: 'Preview', size: '2xl', multiInstance: true },\n};\n\nexport const bundledApps: WindowRegistry = {\n ...utilityApps,\n ...gameApps,\n ...googleApps,\n ...documentApps,\n};\n\nexport {\n Calculator,\n Spreadsheet,\n Notepad,\n Weather,\n CurrencyConverter,\n PomodoroTimer,\n Chess,\n Checkers,\n Sudoku,\n Tetris,\n Game2048,\n Minesweeper,\n Email,\n GeminiChat,\n Calendar,\n Preview,\n};\n\nexport { setPdfPreview } from './Preview';\nexport type { PdfPreviewData } from './Preview';\n"]}
1
+ {"version":3,"sources":["../../src/apps/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Bundled apps — pre-built window registry entries for the 16 apps that ship\n * with `react-os-shell`. Consumers compose them into their own registry via\n * `createWindowRegistry`:\n *\n * import { createWindowRegistry } from 'react-os-shell';\n * import { bundledApps } from 'react-os-shell/apps';\n * import { erpEntities } from './shell-config';\n *\n * const windows = createWindowRegistry(bundledApps, erpEntities);\n *\n * Subsets are also exported (`utilityApps`, `gameApps`, `googleApps`) so a\n * consumer can pick-and-choose without importing every component.\n *\n * NOTE: 4 apps require consumer-supplied persistence (Calendar / Notepad /\n * WorldClock for stored preferences, Minesweeper for leaderboard). They're\n * exported individually but absent from `bundledApps` — wire the prefs\n * provider to opt them in.\n */\nimport { lazy } from 'react';\nimport type { WindowRegistry } from '../windowRegistry/types';\n\n// ── Utility apps ──\nconst Calculator = lazy(() => import('./Calculator'));\nconst Spreadsheet = lazy(() => import('./Spreadsheet'));\nconst Weather = lazy(() => import('./Weather'));\nconst CurrencyConverter = lazy(() => import('./CurrencyConverter'));\nconst PomodoroTimer = lazy(() => import('./PomodoroTimer'));\nconst Notepad = lazy(() => import('./Notepad'));\n\n// ── Games ──\nconst Chess = lazy(() => import('./Chess'));\nconst Checkers = lazy(() => import('./Checkers'));\nconst Sudoku = lazy(() => import('./Sudoku'));\nconst Tetris = lazy(() => import('./Tetris'));\nconst Game2048 = lazy(() => import('./Game2048'));\nconst Minesweeper = lazy(() => import('./Minesweeper'));\n\n// ── Google apps ──\nconst Email = lazy(() => import('./Email'));\nconst GeminiChat = lazy(() => import('./GeminiChat'));\nconst Calendar = lazy(() => import('./Calendar'));\n\n// ── Document apps ──\nconst Preview = lazy(() => import('./Preview'));\n\nexport const utilityApps: WindowRegistry = {\n '/calculator': { component: Calculator, label: 'Calculator', size: 'sm', allowPinOnTop: true, utility: true, widget: true, autoHeight: true, dimensions: [280, 420] },\n '/spreadsheet': { component: Spreadsheet, label: 'Spreadsheets', size: '2xl', compact: true, multiInstance: true },\n '/notepad': { component: Notepad, label: 'Notepad', size: 'lg', allowPinOnTop: true },\n '/weather': { component: Weather, label: 'Weather', size: 'sm', utility: true, widget: true, autoHeight: true, dimensions: [320, 400] },\n '/currency': { component: CurrencyConverter, label: 'Currency Converter', size: 'sm', utility: true, widget: true, autoHeight: true, dimensions: [320, 480] },\n '/pomodoro': { component: PomodoroTimer, label: 'Pomodoro Timer', size: 'sm', utility: true, widget: true, autoHeight: true, dimensions: [320, 420] },\n};\n\nexport const gameApps: WindowRegistry = {\n '/chess': { component: Chess, label: 'Chess', size: 'lg', compact: true },\n '/checkers': { component: Checkers, label: 'Checkers', size: 'lg', compact: true },\n '/sudoku': { component: Sudoku, label: 'Sudoku', size: 'sm', compact: true, dimensions: [360, 535] },\n '/tetris': { component: Tetris, label: 'Tetris', size: 'md', compact: true, dimensions: [452, 618] },\n '/2048': { component: Game2048, label: '2048', size: 'sm', compact: true },\n '/minesweeper': { component: Minesweeper, label: 'Minesweeper', size: 'sm', compact: true },\n};\n\nexport const googleApps: WindowRegistry = {\n '/email': { component: Email, label: 'Email', size: '2xl' },\n '/gemini': { component: GeminiChat, label: 'Gemini AI', size: 'lg' },\n '/calendar': { component: Calendar, label: 'Calendar', size: 'xl' },\n};\n\nexport const documentApps: WindowRegistry = {\n '/preview': { component: Preview, label: 'Preview', size: '2xl', multiInstance: true },\n};\n\nexport const bundledApps: WindowRegistry = {\n ...utilityApps,\n ...gameApps,\n ...googleApps,\n ...documentApps,\n};\n\nexport {\n Calculator,\n Spreadsheet,\n Notepad,\n Weather,\n CurrencyConverter,\n PomodoroTimer,\n Chess,\n Checkers,\n Sudoku,\n Tetris,\n Game2048,\n Minesweeper,\n Email,\n GeminiChat,\n Calendar,\n Preview,\n};\n\nexport { setPdfPreview } from './Preview';\nexport type { PdfPreviewData } from './Preview';\n"]}
@@ -0,0 +1,444 @@
1
+ import { toast_default } from './chunk-WIJ45SYD.js';
2
+ import { WindowTitle } from './chunk-AKZTZLKP.js';
3
+ import { useState, useEffect, useRef } from 'react';
4
+ import * as pdfjsLib from 'pdfjs-dist';
5
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
+
7
+ var TITLE_DISPLAY_MAX = 24;
8
+ function truncateForTitle(s) {
9
+ return s.length > TITLE_DISPLAY_MAX ? `${s.slice(0, TITLE_DISPLAY_MAX - 1)}\u2026` : s;
10
+ }
11
+ if (typeof window !== "undefined" && !pdfjsLib.GlobalWorkerOptions.workerSrc) {
12
+ pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version}/build/pdf.worker.min.mjs`;
13
+ }
14
+ var EVENT_NAME = "react-os-shell:pdf-preview";
15
+ var pendingData = null;
16
+ function setPdfPreview(data) {
17
+ pendingData = data;
18
+ if (typeof window !== "undefined") {
19
+ window.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: data }));
20
+ }
21
+ }
22
+ function Preview() {
23
+ const [data, setData] = useState(() => {
24
+ const d = pendingData;
25
+ pendingData = null;
26
+ return d;
27
+ });
28
+ useEffect(() => {
29
+ const handler = (e) => {
30
+ const next = e.detail;
31
+ setData((prev) => {
32
+ if (prev?.url && prev.url !== next.url && prev.url.startsWith("blob:")) {
33
+ URL.revokeObjectURL(prev.url);
34
+ }
35
+ return next;
36
+ });
37
+ };
38
+ window.addEventListener(EVENT_NAME, handler);
39
+ return () => window.removeEventListener(EVENT_NAME, handler);
40
+ }, []);
41
+ useEffect(() => () => {
42
+ if (data?.url?.startsWith("blob:")) URL.revokeObjectURL(data.url);
43
+ }, []);
44
+ const titleName = data?.filename ? truncateForTitle(data.filename) : "Untitled";
45
+ const fileRef = useRef(null);
46
+ const [isDragging, setIsDragging] = useState(false);
47
+ const handlePick = () => fileRef.current?.click();
48
+ const ingestFile = (file) => {
49
+ const url = URL.createObjectURL(file);
50
+ const ext = (file.name.split(".").pop() || "").toLowerCase();
51
+ const kind = ext === "pdf" ? "pdf" : ext === "dxf" ? "dxf" : ["jpg", "jpeg", "png", "gif", "webp", "svg", "avif", "bmp"].includes(ext) ? "image" : void 0;
52
+ if (!kind) {
53
+ URL.revokeObjectURL(url);
54
+ if (ext === "dwg") toast_default.error("DWG files need server-side conversion. Convert to PDF or DXF first.");
55
+ else toast_default.error(`Unsupported file type: .${ext || "unknown"}`);
56
+ return;
57
+ }
58
+ setPdfPreview({ url, filename: file.name, kind });
59
+ };
60
+ const handleFile = (e) => {
61
+ const file = e.target.files?.[0];
62
+ if (file) ingestFile(file);
63
+ if (fileRef.current) fileRef.current.value = "";
64
+ };
65
+ const handleDrop = (e) => {
66
+ e.preventDefault();
67
+ setIsDragging(false);
68
+ const file = e.dataTransfer.files?.[0];
69
+ if (file) ingestFile(file);
70
+ };
71
+ const Toolbar = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0", children: [
72
+ /* @__PURE__ */ jsx(
73
+ "input",
74
+ {
75
+ ref: fileRef,
76
+ type: "file",
77
+ accept: ".pdf,.dxf,.jpg,.jpeg,.png,.gif,.webp,.svg,.avif,.bmp",
78
+ onChange: handleFile,
79
+ className: "hidden"
80
+ }
81
+ ),
82
+ /* @__PURE__ */ jsxs(
83
+ "button",
84
+ {
85
+ onClick: handlePick,
86
+ className: "text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors flex items-center gap-1",
87
+ children: [
88
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" }) }),
89
+ "Open"
90
+ ]
91
+ }
92
+ ),
93
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400 ml-1", children: "PDF \xB7 DXF \xB7 Images" }),
94
+ data?.filename && /* @__PURE__ */ jsxs(Fragment, { children: [
95
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300 mx-1" }),
96
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700 truncate max-w-[200px]", title: data.filename, children: data.filename })
97
+ ] })
98
+ ] });
99
+ let body;
100
+ if (!data) {
101
+ body = /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center text-gray-500 text-sm gap-3 p-8 text-center", children: [
102
+ /* @__PURE__ */ jsx("svg", { className: "h-12 w-12 text-gray-300", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" }) }),
103
+ /* @__PURE__ */ jsxs("p", { className: "font-medium text-gray-700", children: [
104
+ "Drop a file here, or click ",
105
+ /* @__PURE__ */ jsx("button", { onClick: handlePick, className: "text-blue-600 hover:underline", children: "Open" }),
106
+ "."
107
+ ] }),
108
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 max-w-sm", children: [
109
+ /* @__PURE__ */ jsx("p", { className: "font-semibold uppercase tracking-wide text-[10px] text-gray-400 mb-1", children: "Supported formats" }),
110
+ /* @__PURE__ */ jsxs("ul", { className: "space-y-0.5", children: [
111
+ /* @__PURE__ */ jsxs("li", { children: [
112
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-gray-700", children: ".pdf" }),
113
+ " \u2014 multi-page document viewer"
114
+ ] }),
115
+ /* @__PURE__ */ jsxs("li", { children: [
116
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-gray-700", children: ".dxf" }),
117
+ " \u2014 vector CAD drawings (requires the optional ",
118
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: "dxf-viewer" }),
119
+ " peer dep)"
120
+ ] }),
121
+ /* @__PURE__ */ jsxs("li", { children: [
122
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-gray-700", children: ".jpg .jpeg .png .gif .webp .svg .avif .bmp" }),
123
+ " \u2014 raster images"
124
+ ] })
125
+ ] }),
126
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-[11px] text-gray-400 italic", children: "DWG files need to be converted to PDF or DXF first (server-side)." })
127
+ ] })
128
+ ] });
129
+ } else if (data.converting || !data.url) {
130
+ body = /* @__PURE__ */ jsx(ConvertingPanel, { filename: data.filename, message: data.convertingMessage });
131
+ } else if (data.kind === "dxf") {
132
+ body = /* @__PURE__ */ jsx(DxfPanel, { url: data.url, filename: data.filename, onDownload: data.onDownload, onEmail: data.onEmail }, data.url);
133
+ } else if (data.kind === "image") {
134
+ body = /* @__PURE__ */ jsx(ImagePanel, { url: data.url, filename: data.filename, onDownload: data.onDownload, onEmail: data.onEmail }, data.url);
135
+ } else {
136
+ body = /* @__PURE__ */ jsx(PdfPanel, { url: data.url, filename: data.filename, onDownload: data.onDownload, onEmail: data.onEmail }, data.url);
137
+ }
138
+ return /* @__PURE__ */ jsxs(
139
+ "div",
140
+ {
141
+ className: "relative flex flex-col h-full",
142
+ onDragOver: (e) => {
143
+ e.preventDefault();
144
+ if (!isDragging) setIsDragging(true);
145
+ },
146
+ onDragLeave: (e) => {
147
+ if (e.currentTarget === e.target) setIsDragging(false);
148
+ },
149
+ onDrop: handleDrop,
150
+ children: [
151
+ /* @__PURE__ */ jsx(WindowTitle, { title: `${titleName} - Preview` }),
152
+ Toolbar,
153
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0", children: body }),
154
+ isDragging && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20", children: /* @__PURE__ */ jsx("div", { className: "px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg", children: "Drop to open" }) })
155
+ ]
156
+ }
157
+ );
158
+ }
159
+ function ConvertingPanel({ filename, message }) {
160
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full bg-gray-100 gap-4 px-8", children: [
161
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
162
+ /* @__PURE__ */ jsxs("svg", { className: "h-12 w-12 text-blue-500 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
163
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: "0.2" }),
164
+ /* @__PURE__ */ jsx("path", { d: "M22 12a10 10 0 0 1-10 10", strokeLinecap: "round" })
165
+ ] }),
166
+ /* @__PURE__ */ jsx("div", { className: "text-base font-semibold tracking-wide text-gray-700 uppercase", children: message || "Converting file" }),
167
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-400 truncate max-w-md", children: filename })
168
+ ] }),
169
+ /* @__PURE__ */ jsx("div", { className: "w-72 h-1.5 rounded-full bg-gray-200 overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 bg-blue-500 rounded-full animate-pulse", style: { animation: "preview-bar 1.4s ease-in-out infinite" } }) }),
170
+ /* @__PURE__ */ jsx("style", { children: `@keyframes preview-bar { 0% { transform: translateX(-110%); } 100% { transform: translateX(310%); } }` })
171
+ ] });
172
+ }
173
+ function PdfPanel({ url, filename, onDownload, onEmail }) {
174
+ const canvasRef = useRef(null);
175
+ const containerRef = useRef(null);
176
+ const [pdf, setPdf] = useState(null);
177
+ const [page, setPage] = useState(1);
178
+ const [totalPages, setTotalPages] = useState(0);
179
+ const [scale, setScale] = useState(1.5);
180
+ const [loading, setLoading] = useState(true);
181
+ useEffect(() => {
182
+ let cancelled = false;
183
+ setLoading(true);
184
+ pdfjsLib.getDocument(url).promise.then((doc) => {
185
+ if (cancelled) return;
186
+ setPdf(doc);
187
+ setTotalPages(doc.numPages);
188
+ setLoading(false);
189
+ }).catch(() => {
190
+ if (!cancelled) {
191
+ toast_default.error("Failed to load PDF");
192
+ setLoading(false);
193
+ }
194
+ });
195
+ return () => {
196
+ cancelled = true;
197
+ };
198
+ }, [url]);
199
+ useEffect(() => {
200
+ if (!pdf || !containerRef.current) return;
201
+ pdf.getPage(1).then((p) => {
202
+ const containerW = containerRef.current?.clientWidth || 800;
203
+ const viewport = p.getViewport({ scale: 1 });
204
+ const fitScale = (containerW - 40) / viewport.width;
205
+ setScale(Math.min(Math.max(fitScale, 0.5), 3));
206
+ });
207
+ }, [pdf]);
208
+ useEffect(() => {
209
+ if (!pdf || !canvasRef.current) return;
210
+ let cancelled = false;
211
+ pdf.getPage(page).then((p) => {
212
+ if (cancelled || !canvasRef.current) return;
213
+ const viewport = p.getViewport({ scale });
214
+ const canvas = canvasRef.current;
215
+ canvas.width = viewport.width;
216
+ canvas.height = viewport.height;
217
+ const ctx = canvas.getContext("2d");
218
+ p.render({ canvas, canvasContext: ctx, viewport }).promise.catch(() => {
219
+ });
220
+ });
221
+ return () => {
222
+ cancelled = true;
223
+ };
224
+ }, [pdf, page, scale]);
225
+ const handlePrint = () => {
226
+ if (!pdf) return;
227
+ const win = window.open("", "_blank");
228
+ if (!win) {
229
+ toast_default.error("Allow popups to print");
230
+ return;
231
+ }
232
+ const promises = [];
233
+ for (let i = 1; i <= totalPages; i++) {
234
+ promises.push(pdf.getPage(i).then((p) => {
235
+ const vp = p.getViewport({ scale: 2 });
236
+ const c = document.createElement("canvas");
237
+ c.width = vp.width;
238
+ c.height = vp.height;
239
+ return p.render({ canvas: c, canvasContext: c.getContext("2d"), viewport: vp }).promise.then(() => c.toDataURL());
240
+ }));
241
+ }
242
+ Promise.all(promises).then((images) => {
243
+ win.document.write(`<html><head><title>${filename}</title><style>@media print{body{margin:0}img{width:100%;page-break-after:always}}</style></head><body>`);
244
+ win.document.write(images.map((src) => `<img src="${src}"/>`).join(""));
245
+ win.document.write("</body></html>");
246
+ win.document.close();
247
+ setTimeout(() => {
248
+ win.print();
249
+ win.close();
250
+ }, 300);
251
+ });
252
+ };
253
+ const handleDefaultDownload = () => {
254
+ const a = document.createElement("a");
255
+ a.href = url;
256
+ a.download = filename;
257
+ a.click();
258
+ };
259
+ const fitWidth = () => {
260
+ if (!pdf || !containerRef.current) return;
261
+ pdf.getPage(page).then((p) => {
262
+ const containerW = containerRef.current?.clientWidth || 800;
263
+ const viewport = p.getViewport({ scale: 1 });
264
+ setScale(Math.min(Math.max((containerW - 40) / viewport.width, 0.5), 3));
265
+ });
266
+ };
267
+ const btn = "px-2 py-1 rounded hover:bg-gray-200 transition-colors text-gray-600 flex items-center gap-1";
268
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
269
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0 text-xs", children: [
270
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
271
+ /* @__PURE__ */ jsx("button", { onClick: () => setPage((p) => Math.max(1, p - 1)), disabled: page <= 1, className: "px-2 py-1 rounded hover:bg-gray-200 disabled:opacity-30", children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.75 19.5L8.25 12l7.5-7.5" }) }) }),
272
+ /* @__PURE__ */ jsxs("span", { className: "text-gray-600 font-medium tabular-nums", children: [
273
+ page,
274
+ " / ",
275
+ totalPages
276
+ ] }),
277
+ /* @__PURE__ */ jsx("button", { onClick: () => setPage((p) => Math.min(totalPages, p + 1)), disabled: page >= totalPages, className: "px-2 py-1 rounded hover:bg-gray-200 disabled:opacity-30", children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) }) })
278
+ ] }),
279
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
280
+ /* @__PURE__ */ jsx("button", { onClick: () => setScale((s) => Math.max(0.3, Math.round((s - 0.25) * 100) / 100)), className: btn, children: "\u2212" }),
281
+ /* @__PURE__ */ jsxs("span", { className: "text-gray-500 w-12 text-center tabular-nums", children: [
282
+ Math.round(scale * 100),
283
+ "%"
284
+ ] }),
285
+ /* @__PURE__ */ jsx("button", { onClick: () => setScale((s) => Math.min(4, Math.round((s + 0.25) * 100) / 100)), className: btn, children: "+" }),
286
+ /* @__PURE__ */ jsx("button", { onClick: fitWidth, className: btn, children: "Fit" })
287
+ ] }),
288
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
289
+ /* @__PURE__ */ jsxs("button", { onClick: handlePrint, className: btn, children: [
290
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0110.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0l.229 2.523a1.125 1.125 0 01-1.12 1.227H7.231c-.662 0-1.18-.568-1.12-1.227L6.34 18m11.318 0h1.091A2.25 2.25 0 0021 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 00-1.913-.247M6.34 18H5.25A2.25 2.25 0 013 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 011.913-.247m10.5 0a48.536 48.536 0 00-10.5 0m10.5 0V3.375c0-.621-.504-1.125-1.125-1.125h-8.25c-.621 0-1.125.504-1.125 1.125v3.659M18 10.5h.008v.008H18V10.5zm-3 0h.008v.008H15V10.5z" }) }),
291
+ "Print"
292
+ ] }),
293
+ /* @__PURE__ */ jsxs("button", { onClick: onDownload ?? handleDefaultDownload, className: btn, children: [
294
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" }) }),
295
+ "Download"
296
+ ] }),
297
+ onEmail && /* @__PURE__ */ jsxs("button", { onClick: onEmail, className: btn, children: [
298
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" }) }),
299
+ "Email"
300
+ ] })
301
+ ] })
302
+ ] }),
303
+ /* @__PURE__ */ jsx("div", { ref: containerRef, className: "flex-1 overflow-auto bg-gray-100 flex justify-center p-4", children: loading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-20 text-gray-400 text-sm", children: "Loading PDF..." }) : /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "shadow-lg rounded" }) })
304
+ ] });
305
+ }
306
+ function DxfPanel({ url, filename, onDownload, onEmail }) {
307
+ const containerRef = useRef(null);
308
+ const viewerRef = useRef(null);
309
+ const [loading, setLoading] = useState(true);
310
+ const [error, setError] = useState(null);
311
+ useEffect(() => {
312
+ let cancelled = false;
313
+ let viewer = null;
314
+ setLoading(true);
315
+ setError(null);
316
+ (async () => {
317
+ let DxfViewer;
318
+ try {
319
+ const mod = await import('dxf-viewer');
320
+ DxfViewer = mod.DxfViewer;
321
+ } catch (e) {
322
+ if (!cancelled) {
323
+ setError("dxf-viewer is not installed in this app.");
324
+ setLoading(false);
325
+ }
326
+ return;
327
+ }
328
+ if (cancelled || !containerRef.current) return;
329
+ try {
330
+ viewer = new DxfViewer(containerRef.current, {
331
+ clearColor: { r: 1, g: 1, b: 1, alpha: 1 },
332
+ autoResize: true,
333
+ colorCorrection: true
334
+ });
335
+ viewerRef.current = viewer;
336
+ await viewer.Load({ url, fonts: [], workerFactory: null });
337
+ if (cancelled) return;
338
+ setLoading(false);
339
+ } catch (e) {
340
+ if (!cancelled) {
341
+ setError(e?.message || "Failed to render DXF.");
342
+ setLoading(false);
343
+ }
344
+ }
345
+ })();
346
+ return () => {
347
+ cancelled = true;
348
+ try {
349
+ viewer?.Destroy?.();
350
+ } catch {
351
+ }
352
+ viewerRef.current = null;
353
+ };
354
+ }, [url]);
355
+ const handleDefaultDownload = () => {
356
+ const a = document.createElement("a");
357
+ a.href = url;
358
+ a.download = filename;
359
+ a.click();
360
+ };
361
+ const handleResetView = () => {
362
+ try {
363
+ viewerRef.current?.FitView?.();
364
+ } catch {
365
+ }
366
+ };
367
+ const btn = "px-2 py-1 rounded hover:bg-gray-200 transition-colors text-gray-600 flex items-center gap-1";
368
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
369
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0 text-xs", children: [
370
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
371
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: "DXF" }),
372
+ /* @__PURE__ */ jsx("span", { className: "text-gray-400 truncate max-w-xs", children: filename })
373
+ ] }),
374
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
375
+ /* @__PURE__ */ jsx("button", { onClick: handleResetView, className: btn, title: "Fit drawing to view", children: "Fit" }),
376
+ /* @__PURE__ */ jsxs("button", { onClick: onDownload ?? handleDefaultDownload, className: btn, children: [
377
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" }) }),
378
+ "Download"
379
+ ] }),
380
+ onEmail && /* @__PURE__ */ jsxs("button", { onClick: onEmail, className: btn, children: [
381
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" }) }),
382
+ "Email"
383
+ ] })
384
+ ] })
385
+ ] }),
386
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 bg-white", children: [
387
+ /* @__PURE__ */ jsx("div", { ref: containerRef, className: "absolute inset-0" }),
388
+ loading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-white/80 text-sm text-gray-500", children: "Loading drawing\u2026" }),
389
+ error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-sm text-red-600 px-6 text-center", children: error })
390
+ ] })
391
+ ] });
392
+ }
393
+ function ImagePanel({ url, filename, onDownload, onEmail }) {
394
+ const [zoom, setZoom] = useState(1);
395
+ const [error, setError] = useState(false);
396
+ const handleDefaultDownload = () => {
397
+ const a = document.createElement("a");
398
+ a.href = url;
399
+ a.download = filename;
400
+ a.click();
401
+ };
402
+ const btn = "px-2 py-1 rounded hover:bg-gray-200 transition-colors text-gray-600 flex items-center gap-1";
403
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
404
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0 text-xs", children: [
405
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
406
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: "Image" }),
407
+ /* @__PURE__ */ jsx("span", { className: "text-gray-400 truncate max-w-xs", children: filename })
408
+ ] }),
409
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
410
+ /* @__PURE__ */ jsx("button", { onClick: () => setZoom((z) => Math.max(0.1, Math.round((z - 0.25) * 100) / 100)), className: btn, children: "\u2212" }),
411
+ /* @__PURE__ */ jsxs("span", { className: "text-gray-500 w-12 text-center tabular-nums", children: [
412
+ Math.round(zoom * 100),
413
+ "%"
414
+ ] }),
415
+ /* @__PURE__ */ jsx("button", { onClick: () => setZoom((z) => Math.min(8, Math.round((z + 0.25) * 100) / 100)), className: btn, children: "+" }),
416
+ /* @__PURE__ */ jsx("button", { onClick: () => setZoom(1), className: btn, children: "1:1" })
417
+ ] }),
418
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
419
+ /* @__PURE__ */ jsxs("button", { onClick: onDownload ?? handleDefaultDownload, className: btn, children: [
420
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" }) }),
421
+ "Download"
422
+ ] }),
423
+ onEmail && /* @__PURE__ */ jsxs("button", { onClick: onEmail, className: btn, children: [
424
+ /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" }) }),
425
+ "Email"
426
+ ] })
427
+ ] })
428
+ ] }),
429
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto bg-gray-100 flex items-center justify-center p-4", children: error ? /* @__PURE__ */ jsx("div", { className: "text-sm text-red-600", children: "Failed to load image." }) : /* @__PURE__ */ jsx(
430
+ "img",
431
+ {
432
+ src: url,
433
+ alt: filename,
434
+ onError: () => setError(true),
435
+ style: { transform: `scale(${zoom})`, transformOrigin: "center center", transition: "transform 120ms ease" },
436
+ className: "max-w-full max-h-full shadow-lg rounded bg-white"
437
+ }
438
+ ) })
439
+ ] });
440
+ }
441
+
442
+ export { Preview, setPdfPreview };
443
+ //# sourceMappingURL=chunk-C3AD7QK2.js.map
444
+ //# sourceMappingURL=chunk-C3AD7QK2.js.map