@papyrus-sdk/ui-react 0.1.0

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.
package/dist/index.mjs ADDED
@@ -0,0 +1,696 @@
1
+ // components/Topbar.tsx
2
+ import { useRef, useState, useEffect } from "react";
3
+ import { useViewerStore } from "@papyrus-sdk/core";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ var Topbar = ({ engine }) => {
6
+ const {
7
+ currentPage,
8
+ pageCount,
9
+ zoom,
10
+ uiTheme,
11
+ pageTheme,
12
+ setDocumentState,
13
+ accentColor,
14
+ toggleSidebarLeft,
15
+ toggleSidebarRight,
16
+ triggerScrollToPage
17
+ } = useViewerStore();
18
+ const fileInputRef = useRef(null);
19
+ const [pageInput, setPageInput] = useState(currentPage.toString());
20
+ const [showPageThemes, setShowPageThemes] = useState(false);
21
+ const isDark = uiTheme === "dark";
22
+ useEffect(() => {
23
+ setPageInput(currentPage.toString());
24
+ }, [currentPage]);
25
+ const handleZoom = (delta) => {
26
+ const newZoom = Math.max(0.2, Math.min(5, zoom + delta));
27
+ engine.setZoom(newZoom);
28
+ setDocumentState({ zoom: newZoom });
29
+ };
30
+ const handlePageChange = (page) => {
31
+ const p = Math.max(1, Math.min(pageCount, isNaN(page) ? 1 : page));
32
+ engine.goToPage(p);
33
+ setDocumentState({ currentPage: p });
34
+ triggerScrollToPage(p - 1);
35
+ };
36
+ const themes = [
37
+ { id: "normal", name: "Original", color: "bg-white" },
38
+ { id: "sepia", name: "S\xE9pia", color: "bg-[#f4ecd8]" },
39
+ { id: "dark", name: "Invertido", color: "bg-gray-800" },
40
+ { id: "high-contrast", name: "Contraste", color: "bg-black" }
41
+ ];
42
+ return /* @__PURE__ */ jsxs("div", { className: `h-14 border-b flex items-center justify-between px-4 z-50 transition-colors duration-200 ${isDark ? "bg-[#1a1a1a] border-[#333] text-white" : "bg-white border-gray-200 text-gray-800"}`, children: [
43
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
44
+ /* @__PURE__ */ jsx("button", { onClick: toggleSidebarLeft, className: `p-2 rounded-md ${isDark ? "hover:bg-white/10" : "hover:bg-gray-100"}`, children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }) }),
45
+ /* @__PURE__ */ jsxs("span", { className: "font-bold text-lg tracking-tight", style: { color: accentColor }, children: [
46
+ "Papyrus",
47
+ /* @__PURE__ */ jsx("span", { className: isDark ? "text-white" : "text-gray-900", children: "Core" })
48
+ ] })
49
+ ] }),
50
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4", children: [
51
+ /* @__PURE__ */ jsxs("div", { className: `flex items-center rounded-lg p-1 space-x-1 border ${isDark ? "bg-[#2a2a2a] border-[#444]" : "bg-gray-50 border-gray-200"}`, children: [
52
+ /* @__PURE__ */ jsx("button", { onClick: () => handlePageChange(currentPage - 1), className: "p-1.5 rounded transition-all hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }),
53
+ /* @__PURE__ */ jsx(
54
+ "input",
55
+ {
56
+ type: "text",
57
+ className: "w-10 text-center bg-transparent focus:outline-none font-bold text-sm",
58
+ value: pageInput,
59
+ onChange: (e) => setPageInput(e.target.value),
60
+ onKeyDown: (e) => e.key === "Enter" && handlePageChange(parseInt(pageInput)),
61
+ onBlur: () => handlePageChange(parseInt(pageInput))
62
+ }
63
+ ),
64
+ /* @__PURE__ */ jsx("span", { className: "opacity-40 px-1", children: "/" }),
65
+ /* @__PURE__ */ jsx("span", { className: "opacity-80 text-sm", children: pageCount }),
66
+ /* @__PURE__ */ jsx("button", { onClick: () => handlePageChange(currentPage + 1), className: "p-1.5 rounded transition-all hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) }) })
67
+ ] }),
68
+ /* @__PURE__ */ jsxs("div", { className: `flex items-center rounded-lg p-1 border ${isDark ? "bg-[#2a2a2a] border-[#444]" : "bg-gray-50 border-gray-200"}`, children: [
69
+ /* @__PURE__ */ jsx("button", { onClick: () => handleZoom(-0.1), className: "p-1.5 rounded hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) }) }),
70
+ /* @__PURE__ */ jsxs("span", { className: "px-3 text-xs font-bold min-w-[50px] text-center", children: [
71
+ Math.round(zoom * 100),
72
+ "%"
73
+ ] }),
74
+ /* @__PURE__ */ jsx("button", { onClick: () => handleZoom(0.1), className: "p-1.5 rounded hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) }) })
75
+ ] })
76
+ ] }),
77
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
78
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
79
+ /* @__PURE__ */ jsxs(
80
+ "button",
81
+ {
82
+ onClick: () => setShowPageThemes(!showPageThemes),
83
+ className: `flex items-center space-x-2 px-3 py-1.5 rounded-md text-xs font-bold border transition-all ${isDark ? "bg-[#2a2a2a] border-[#444]" : "bg-gray-50 border-gray-200"}`,
84
+ children: [
85
+ /* @__PURE__ */ jsx("div", { className: `w-3 h-3 rounded-full border ${themes.find((t) => t.id === pageTheme)?.color}` }),
86
+ /* @__PURE__ */ jsx("span", { children: "TEMA" })
87
+ ]
88
+ }
89
+ ),
90
+ showPageThemes && /* @__PURE__ */ jsx("div", { className: `absolute top-full right-0 mt-2 w-48 rounded-lg shadow-xl border p-2 z-[60] ${isDark ? "bg-[#2a2a2a] border-[#444]" : "bg-white border-gray-200"}`, children: themes.map((t) => /* @__PURE__ */ jsxs(
91
+ "button",
92
+ {
93
+ onClick: () => {
94
+ setDocumentState({ pageTheme: t.id });
95
+ setShowPageThemes(false);
96
+ },
97
+ className: `w-full flex items-center space-x-3 px-3 py-2 rounded-md text-sm ${pageTheme === t.id ? "text-white" : isDark ? "hover:bg-white/10 text-gray-300" : "hover:bg-gray-50 text-gray-700"}`,
98
+ style: pageTheme === t.id ? { backgroundColor: accentColor } : void 0,
99
+ children: [
100
+ /* @__PURE__ */ jsx("div", { className: `w-3 h-3 rounded-full border ${t.color}` }),
101
+ /* @__PURE__ */ jsx("span", { children: t.name })
102
+ ]
103
+ },
104
+ t.id
105
+ )) })
106
+ ] }),
107
+ /* @__PURE__ */ jsx("button", { onClick: () => setDocumentState({ uiTheme: isDark ? "light" : "dark" }), className: `p-2 rounded-full ${isDark ? "bg-yellow-500/10 text-yellow-500" : "bg-gray-100 text-gray-500"}`, children: isDark ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { d: "M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414z" }) }) : /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { d: "M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" }) }) }),
108
+ /* @__PURE__ */ jsx(
109
+ "button",
110
+ {
111
+ onClick: () => fileInputRef.current?.click(),
112
+ className: "px-4 py-1.5 text-white rounded-md text-sm font-bold shadow-md active:scale-95",
113
+ style: { backgroundColor: accentColor },
114
+ children: "UPLOAD"
115
+ }
116
+ ),
117
+ /* @__PURE__ */ jsx("input", { type: "file", ref: fileInputRef, className: "hidden", accept: ".pdf", onChange: async (e) => {
118
+ const file = e.target.files?.[0];
119
+ if (file) {
120
+ setDocumentState({ isLoaded: false });
121
+ await engine.load(file);
122
+ setDocumentState({ isLoaded: true, pageCount: engine.getPageCount(), currentPage: 1, outline: await engine.getOutline() });
123
+ }
124
+ } }),
125
+ /* @__PURE__ */ jsx("button", { onClick: () => toggleSidebarRight("search"), className: `p-2 rounded-md ${isDark ? "hover:bg-white/10" : "hover:bg-gray-100"}`, children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) })
126
+ ] })
127
+ ] });
128
+ };
129
+ var Topbar_default = Topbar;
130
+
131
+ // components/SidebarLeft.tsx
132
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
133
+ import { useViewerStore as useViewerStore2 } from "@papyrus-sdk/core";
134
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
135
+ var withAlpha = (hex, alpha) => {
136
+ const normalized = hex.replace("#", "").trim();
137
+ const value = normalized.length === 3 ? normalized.split("").map((c) => c + c).join("") : normalized;
138
+ if (value.length !== 6) return hex;
139
+ const r = parseInt(value.slice(0, 2), 16);
140
+ const g = parseInt(value.slice(2, 4), 16);
141
+ const b = parseInt(value.slice(4, 6), 16);
142
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
143
+ };
144
+ var Thumbnail = ({ engine, pageIndex, active, isDark, accentColor, onClick }) => {
145
+ const canvasRef = useRef2(null);
146
+ const accentSoft = withAlpha(accentColor, 0.12);
147
+ useEffect2(() => {
148
+ if (canvasRef.current) {
149
+ engine.renderPage(pageIndex, canvasRef.current, 0.15);
150
+ }
151
+ }, [engine, pageIndex]);
152
+ return /* @__PURE__ */ jsx2(
153
+ "div",
154
+ {
155
+ onClick,
156
+ className: `p-3 cursor-pointer transition-all rounded-lg border-2 ${active ? "shadow-sm" : "border-transparent"}`,
157
+ style: active ? { borderColor: accentColor, backgroundColor: accentSoft } : void 0,
158
+ children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center", children: [
159
+ /* @__PURE__ */ jsx2("div", { className: `shadow-lg rounded overflow-hidden mb-2 border ${isDark ? "border-[#333]" : "border-gray-200"}`, children: /* @__PURE__ */ jsx2("canvas", { ref: canvasRef, className: "max-w-full h-auto bg-white" }) }),
160
+ /* @__PURE__ */ jsx2("span", { className: `text-[11px] font-bold ${active ? "" : isDark ? "text-gray-500" : "text-gray-400"}`, style: active ? { color: accentColor } : void 0, children: pageIndex + 1 })
161
+ ] })
162
+ }
163
+ );
164
+ };
165
+ var OutlineNode = ({ item, engine, isDark, accentColor, depth = 0 }) => {
166
+ const { triggerScrollToPage, outlineSearchQuery } = useViewerStore2();
167
+ const [expanded, setExpanded] = useState2(true);
168
+ const accentSoft = withAlpha(accentColor, 0.2);
169
+ const matchesSearch = outlineSearchQuery === "" || item.title.toLowerCase().includes(outlineSearchQuery.toLowerCase());
170
+ const hasMatchingChildren = item.children?.some((child) => child.title.toLowerCase().includes(outlineSearchQuery.toLowerCase()));
171
+ if (!matchesSearch && !hasMatchingChildren && outlineSearchQuery !== "") return null;
172
+ const handleClick = () => {
173
+ if (item.pageIndex >= 0) {
174
+ engine.goToPage(item.pageIndex + 1);
175
+ triggerScrollToPage(item.pageIndex);
176
+ }
177
+ };
178
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col", children: [
179
+ /* @__PURE__ */ jsxs2(
180
+ "div",
181
+ {
182
+ className: `flex items-center py-1.5 px-3 rounded-md transition-colors group ${item.pageIndex >= 0 ? "cursor-pointer" : "cursor-default"} ${isDark ? "hover:bg-white/10" : "hover:bg-gray-100"}`,
183
+ style: { paddingLeft: `${depth * 14 + 8}px` },
184
+ onClick: handleClick,
185
+ children: [
186
+ item.children && item.children.length > 0 ? /* @__PURE__ */ jsx2(
187
+ "button",
188
+ {
189
+ className: `mr-1 text-gray-400 transition-transform p-1`,
190
+ style: { color: accentColor, transform: expanded ? "rotate(90deg)" : "rotate(0deg)" },
191
+ onClick: (e) => {
192
+ e.stopPropagation();
193
+ setExpanded(!expanded);
194
+ },
195
+ children: /* @__PURE__ */ jsx2("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 3, d: "M9 5l7 7-7 7" }) })
196
+ }
197
+ ) : /* @__PURE__ */ jsx2("div", { className: "w-5" }),
198
+ /* @__PURE__ */ jsx2(
199
+ "span",
200
+ {
201
+ className: `text-[13px] leading-tight font-medium truncate ${isDark ? "text-gray-200" : "text-gray-700"}`,
202
+ style: matchesSearch && outlineSearchQuery ? { backgroundColor: accentSoft, color: accentColor } : void 0,
203
+ children: item.title
204
+ }
205
+ )
206
+ ]
207
+ }
208
+ ),
209
+ expanded && item.children && item.children.length > 0 && /* @__PURE__ */ jsx2("div", { className: "flex flex-col", children: item.children.map((child, i) => /* @__PURE__ */ jsx2(OutlineNode, { item: child, engine, isDark, accentColor, depth: depth + 1 }, i)) })
210
+ ] });
211
+ };
212
+ var SidebarLeft = ({ engine }) => {
213
+ const {
214
+ pageCount,
215
+ currentPage,
216
+ setDocumentState,
217
+ sidebarLeftOpen,
218
+ uiTheme,
219
+ triggerScrollToPage,
220
+ sidebarLeftTab,
221
+ setSidebarLeftTab,
222
+ outline,
223
+ outlineSearchQuery,
224
+ setOutlineSearch,
225
+ accentColor
226
+ } = useViewerStore2();
227
+ const isDark = uiTheme === "dark";
228
+ if (!sidebarLeftOpen) return null;
229
+ return /* @__PURE__ */ jsxs2("div", { className: `w-72 border-r flex flex-col h-full shrink-0 overflow-hidden transition-colors duration-200 ${isDark ? "bg-[#2a2a2a] border-[#3a3a3a]" : "bg-[#fcfcfc] border-gray-200"}`, children: [
230
+ /* @__PURE__ */ jsxs2("div", { className: `p-4 border-b flex flex-col space-y-4 ${isDark ? "border-[#3a3a3a]" : "border-gray-100"}`, children: [
231
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between", children: [
232
+ /* @__PURE__ */ jsx2("h3", { className: `text-sm font-bold uppercase tracking-widest ${isDark ? "text-gray-100" : "text-gray-800"}`, children: sidebarLeftTab === "thumbnails" ? "Thumbnails" : "Sum\xE1rio" }),
233
+ /* @__PURE__ */ jsx2("button", { onClick: () => setDocumentState({ sidebarLeftOpen: false }), className: "text-gray-400 hover:text-gray-600", children: /* @__PURE__ */ jsx2("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
234
+ ] }),
235
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-1", children: [
236
+ /* @__PURE__ */ jsx2(
237
+ "button",
238
+ {
239
+ onClick: () => setSidebarLeftTab("thumbnails"),
240
+ className: `p-2 rounded-md ${sidebarLeftTab === "thumbnails" ? isDark ? "bg-white/10 text-white" : "bg-white shadow-sm border border-gray-200" : "text-gray-400"}`,
241
+ style: sidebarLeftTab === "thumbnails" && !isDark ? { color: accentColor } : void 0,
242
+ children: /* @__PURE__ */ jsx2("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2 2V6z" }) })
243
+ }
244
+ ),
245
+ /* @__PURE__ */ jsx2(
246
+ "button",
247
+ {
248
+ onClick: () => setSidebarLeftTab("summary"),
249
+ className: `p-2 rounded-md ${sidebarLeftTab === "summary" ? isDark ? "bg-white/10 text-white" : "bg-white shadow-sm border border-gray-200" : "text-gray-400"}`,
250
+ style: sidebarLeftTab === "summary" && !isDark ? { color: accentColor } : void 0,
251
+ children: /* @__PURE__ */ jsx2("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h7" }) })
252
+ }
253
+ )
254
+ ] })
255
+ ] }),
256
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-3", children: sidebarLeftTab === "thumbnails" ? /* @__PURE__ */ jsx2("div", { className: "space-y-1", children: Array.from({ length: pageCount }).map((_, idx) => /* @__PURE__ */ jsx2(Thumbnail, { engine, pageIndex: idx, isDark, accentColor, active: currentPage === idx + 1, onClick: () => {
257
+ engine.goToPage(idx + 1);
258
+ setDocumentState({ currentPage: idx + 1 });
259
+ triggerScrollToPage(idx);
260
+ } }, idx)) }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col space-y-0.5", children: outline.map((item, i) => /* @__PURE__ */ jsx2(OutlineNode, { item, engine, isDark, accentColor }, i)) }) })
261
+ ] });
262
+ };
263
+ var SidebarLeft_default = SidebarLeft;
264
+
265
+ // components/SidebarRight.tsx
266
+ import { useState as useState3 } from "react";
267
+ import { useViewerStore as useViewerStore3, SearchService } from "@papyrus-sdk/core";
268
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
269
+ var withAlpha2 = (hex, alpha) => {
270
+ const normalized = hex.replace("#", "").trim();
271
+ const value = normalized.length === 3 ? normalized.split("").map((c) => c + c).join("") : normalized;
272
+ if (value.length !== 6) return hex;
273
+ const r = parseInt(value.slice(0, 2), 16);
274
+ const g = parseInt(value.slice(2, 4), 16);
275
+ const b = parseInt(value.slice(4, 6), 16);
276
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
277
+ };
278
+ var SidebarRight = ({ engine }) => {
279
+ const {
280
+ sidebarRightOpen,
281
+ sidebarRightTab,
282
+ toggleSidebarRight,
283
+ searchResults,
284
+ activeSearchIndex,
285
+ uiTheme,
286
+ setSearch,
287
+ setDocumentState,
288
+ triggerScrollToPage,
289
+ annotations,
290
+ accentColor
291
+ } = useViewerStore3();
292
+ const [query, setQuery] = useState3("");
293
+ const [isSearching, setIsSearching] = useState3(false);
294
+ const searchService = new SearchService(engine);
295
+ const isDark = uiTheme === "dark";
296
+ const accentSoft = withAlpha2(accentColor, 0.12);
297
+ const handleSearch = async (e) => {
298
+ e.preventDefault();
299
+ if (!query.trim()) {
300
+ setSearch("", []);
301
+ return;
302
+ }
303
+ setIsSearching(true);
304
+ const results = await searchService.search(query);
305
+ setSearch(query, results);
306
+ setIsSearching(false);
307
+ };
308
+ if (!sidebarRightOpen) return null;
309
+ return /* @__PURE__ */ jsxs3("div", { className: `w-80 border-l flex flex-col h-full shrink-0 transition-colors duration-200 shadow-2xl z-40 ${isDark ? "bg-[#1a1a1a] border-[#333]" : "bg-white border-gray-200"}`, children: [
310
+ /* @__PURE__ */ jsxs3("div", { className: `p-4 border-b flex items-center justify-between shrink-0 ${isDark ? "border-[#333]" : "border-gray-100"}`, children: [
311
+ /* @__PURE__ */ jsxs3("div", { className: "flex space-x-6", children: [
312
+ /* @__PURE__ */ jsx3(
313
+ "button",
314
+ {
315
+ onClick: () => toggleSidebarRight("search"),
316
+ className: `text-[10px] font-black uppercase tracking-widest pb-1 transition-all ${sidebarRightTab === "search" ? "border-b-2" : "text-gray-400"}`,
317
+ style: sidebarRightTab === "search" ? { color: accentColor, borderColor: accentColor } : void 0,
318
+ children: "Busca"
319
+ }
320
+ ),
321
+ /* @__PURE__ */ jsx3(
322
+ "button",
323
+ {
324
+ onClick: () => toggleSidebarRight("annotations"),
325
+ className: `text-[10px] font-black uppercase tracking-widest pb-1 transition-all ${sidebarRightTab === "annotations" ? "border-b-2" : "text-gray-400"}`,
326
+ style: sidebarRightTab === "annotations" ? { color: accentColor, borderColor: accentColor } : void 0,
327
+ children: "Notas"
328
+ }
329
+ )
330
+ ] }),
331
+ /* @__PURE__ */ jsx3("button", { onClick: () => toggleSidebarRight(), className: "text-gray-400 hover:text-red-500 transition-colors", children: /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
332
+ ] }),
333
+ /* @__PURE__ */ jsx3("div", { className: "flex-1 overflow-y-auto p-4 custom-scrollbar bg-opacity-50", children: sidebarRightTab === "search" ? /* @__PURE__ */ jsxs3("div", { className: "space-y-4", children: [
334
+ /* @__PURE__ */ jsxs3("form", { onSubmit: handleSearch, className: "relative mb-6", children: [
335
+ /* @__PURE__ */ jsx3(
336
+ "input",
337
+ {
338
+ type: "text",
339
+ className: `w-full rounded-lg px-4 py-2.5 text-xs outline-none border transition-all shadow-inner font-medium ${isDark ? "bg-[#2a2a2a] text-white border-[#444] focus:border-blue-500" : "bg-gray-100 border-gray-200 focus:bg-white focus:border-blue-400"}`,
340
+ placeholder: "O que voc\xEA procura?",
341
+ value: query,
342
+ onChange: (e) => setQuery(e.target.value)
343
+ }
344
+ ),
345
+ /* @__PURE__ */ jsx3("button", { type: "submit", className: "absolute right-3 top-2.5 text-gray-400 transition-colors", style: { color: accentColor }, children: /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) })
346
+ ] }),
347
+ isSearching && /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center py-12 space-y-3", children: [
348
+ /* @__PURE__ */ jsx3("div", { className: "w-6 h-6 border-2 border-t-transparent rounded-full animate-spin", style: { borderColor: accentColor } }),
349
+ /* @__PURE__ */ jsx3("span", { className: "text-[10px] font-bold text-gray-500 uppercase", children: "Varrendo documento..." })
350
+ ] }),
351
+ !isSearching && searchResults.map((res, idx) => /* @__PURE__ */ jsxs3(
352
+ "div",
353
+ {
354
+ onClick: () => {
355
+ setDocumentState({ activeSearchIndex: idx });
356
+ triggerScrollToPage(res.pageIndex);
357
+ },
358
+ className: `p-4 rounded-xl border-2 cursor-pointer transition-all group hover:scale-[1.02] ${idx === activeSearchIndex ? "shadow-lg" : isDark ? "border-[#333] hover:border-[#555] bg-[#222]" : "border-gray-50 hover:border-gray-200 bg-gray-50/50 hover:bg-white"}`,
359
+ style: idx === activeSearchIndex ? { borderColor: accentColor, backgroundColor: accentSoft } : void 0,
360
+ children: [
361
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-2", children: [
362
+ /* @__PURE__ */ jsxs3(
363
+ "span",
364
+ {
365
+ className: `text-[10px] font-black uppercase tracking-tighter ${idx === activeSearchIndex ? "" : "text-gray-400"}`,
366
+ style: idx === activeSearchIndex ? { color: accentColor } : void 0,
367
+ children: [
368
+ "P\xC1GINA ",
369
+ res.pageIndex + 1
370
+ ]
371
+ }
372
+ ),
373
+ /* @__PURE__ */ jsx3(
374
+ "svg",
375
+ {
376
+ className: `w-3 h-3 transition-transform ${idx === activeSearchIndex ? "" : "text-gray-300"}`,
377
+ style: idx === activeSearchIndex ? { color: accentColor } : void 0,
378
+ fill: "none",
379
+ stroke: "currentColor",
380
+ viewBox: "0 0 24 24",
381
+ children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 3, d: "M9 5l7 7-7 7" })
382
+ }
383
+ )
384
+ ] }),
385
+ /* @__PURE__ */ jsxs3("p", { className: `text-[11px] font-medium leading-relaxed italic ${isDark ? "text-gray-400" : "text-gray-600"}`, children: [
386
+ "...",
387
+ res.text,
388
+ "..."
389
+ ] })
390
+ ]
391
+ },
392
+ idx
393
+ ))
394
+ ] }) : /* @__PURE__ */ jsxs3("div", { className: "space-y-3", children: [
395
+ /* @__PURE__ */ jsxs3("div", { className: "text-[9px] font-black text-gray-400 uppercase tracking-[0.2em] mb-6 flex items-center", children: [
396
+ /* @__PURE__ */ jsx3("span", { children: "WORKSET" }),
397
+ /* @__PURE__ */ jsx3("div", { className: "flex-1 h-px bg-current ml-3 opacity-10" })
398
+ ] }),
399
+ annotations.length === 0 ? /* @__PURE__ */ jsxs3("div", { className: "text-center py-20", children: [
400
+ /* @__PURE__ */ jsx3("div", { className: "w-12 h-12 bg-gray-500/10 rounded-full flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ jsx3("svg", { className: "w-6 h-6 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) }) }),
401
+ /* @__PURE__ */ jsx3("p", { className: "text-[10px] font-bold text-gray-400 uppercase tracking-widest", children: "Sem anota\xE7\xF5es" })
402
+ ] }) : annotations.map((ann) => /* @__PURE__ */ jsxs3("div", { className: `p-4 rounded-xl border group transition-all cursor-pointer ${isDark ? "bg-[#222] border-[#333] hover:border-[#444]" : "bg-white border-gray-100 shadow-sm hover:shadow-md"}`, children: [
403
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-3", children: [
404
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center space-x-2", children: [
405
+ /* @__PURE__ */ jsx3("div", { className: "w-2.5 h-2.5 rounded-full", style: { backgroundColor: ann.color } }),
406
+ /* @__PURE__ */ jsxs3("span", { className: "text-[10px] font-black", style: { color: accentColor }, children: [
407
+ "P",
408
+ ann.pageIndex + 1
409
+ ] })
410
+ ] }),
411
+ /* @__PURE__ */ jsx3("span", { className: "text-[9px] text-gray-400 font-bold", children: new Date(ann.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) })
412
+ ] }),
413
+ /* @__PURE__ */ jsx3("p", { className: `text-[11px] font-bold uppercase tracking-tight ${isDark ? "text-gray-200" : "text-gray-700"}`, children: ann.type })
414
+ ] }, ann.id))
415
+ ] }) })
416
+ ] });
417
+ };
418
+ var SidebarRight_default = SidebarRight;
419
+
420
+ // components/Viewer.tsx
421
+ import { useEffect as useEffect4, useRef as useRef4 } from "react";
422
+ import { useViewerStore as useViewerStore5 } from "@papyrus-sdk/core";
423
+
424
+ // components/PageRenderer.tsx
425
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
426
+ import { useViewerStore as useViewerStore4, papyrusEvents } from "@papyrus-sdk/core";
427
+ import { PapyrusEventType } from "@papyrus-sdk/types";
428
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
429
+ var PageRenderer = ({ engine, pageIndex }) => {
430
+ const containerRef = useRef3(null);
431
+ const canvasRef = useRef3(null);
432
+ const textLayerRef = useRef3(null);
433
+ const [loading, setLoading] = useState4(true);
434
+ const [isDragging, setIsDragging] = useState4(false);
435
+ const [startPos, setStartPos] = useState4({ x: 0, y: 0 });
436
+ const [currentRect, setCurrentRect] = useState4({ x: 0, y: 0, w: 0, h: 0 });
437
+ const {
438
+ zoom,
439
+ rotation,
440
+ pageTheme,
441
+ scrollToPageSignal,
442
+ setDocumentState,
443
+ annotations,
444
+ addAnnotation,
445
+ activeTool,
446
+ removeAnnotation,
447
+ selectedAnnotationId,
448
+ setSelectedAnnotation,
449
+ accentColor
450
+ } = useViewerStore4();
451
+ useEffect3(() => {
452
+ if (scrollToPageSignal === pageIndex && containerRef.current) {
453
+ containerRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
454
+ setDocumentState({ scrollToPageSignal: null });
455
+ }
456
+ }, [scrollToPageSignal, pageIndex, setDocumentState]);
457
+ useEffect3(() => {
458
+ let active = true;
459
+ const render = async () => {
460
+ if (!canvasRef.current || !textLayerRef.current) return;
461
+ setLoading(true);
462
+ try {
463
+ const RENDER_SCALE = 2;
464
+ await engine.renderPage(pageIndex, canvasRef.current, RENDER_SCALE);
465
+ textLayerRef.current.innerHTML = "";
466
+ await engine.renderTextLayer(pageIndex, textLayerRef.current, RENDER_SCALE);
467
+ } catch (err) {
468
+ console.error("[Papyrus] Falha na renderiza\xE7\xE3o:", err);
469
+ } finally {
470
+ if (active) setLoading(false);
471
+ }
472
+ };
473
+ render();
474
+ return () => {
475
+ active = false;
476
+ };
477
+ }, [engine, pageIndex, zoom, rotation]);
478
+ const handleMouseDown = (e) => {
479
+ if (activeTool === "select") return;
480
+ const rect = containerRef.current?.getBoundingClientRect();
481
+ if (!rect) return;
482
+ setIsDragging(true);
483
+ const x = e.clientX - rect.left;
484
+ const y = e.clientY - rect.top;
485
+ setStartPos({ x, y });
486
+ setCurrentRect({ x, y, w: 0, h: 0 });
487
+ };
488
+ const handleMouseMove = (e) => {
489
+ if (!isDragging) return;
490
+ const rect = containerRef.current?.getBoundingClientRect();
491
+ if (!rect) return;
492
+ const currentX = e.clientX - rect.left;
493
+ const currentY = e.clientY - rect.top;
494
+ setCurrentRect({
495
+ x: Math.min(startPos.x, currentX),
496
+ y: Math.min(startPos.y, currentY),
497
+ w: Math.abs(currentX - startPos.x),
498
+ h: Math.abs(currentY - startPos.y)
499
+ });
500
+ };
501
+ const handleMouseUp = (e) => {
502
+ if (isDragging) {
503
+ setIsDragging(false);
504
+ if (currentRect.w > 5 && currentRect.h > 5) {
505
+ const rect = containerRef.current?.getBoundingClientRect();
506
+ if (rect) {
507
+ addAnnotation({
508
+ id: Math.random().toString(36).substr(2, 9),
509
+ pageIndex,
510
+ type: activeTool,
511
+ rect: {
512
+ x: currentRect.x / rect.width,
513
+ y: currentRect.y / rect.height,
514
+ width: currentRect.w / rect.width,
515
+ height: currentRect.h / rect.height
516
+ },
517
+ color: activeTool === "highlight" ? "#fbbf24" : activeTool === "strikeout" ? "#ef4444" : accentColor,
518
+ content: activeTool === "text" || activeTool === "comment" ? "" : void 0,
519
+ createdAt: Date.now()
520
+ });
521
+ }
522
+ }
523
+ setCurrentRect({ x: 0, y: 0, w: 0, h: 0 });
524
+ return;
525
+ }
526
+ if (activeTool === "select") {
527
+ const selection = window.getSelection();
528
+ const selectedText = selection?.toString().trim();
529
+ if (selectedText) {
530
+ papyrusEvents.emit(PapyrusEventType.TEXT_SELECTED, {
531
+ text: selectedText,
532
+ pageIndex
533
+ });
534
+ }
535
+ }
536
+ };
537
+ const getPageFilter = () => {
538
+ switch (pageTheme) {
539
+ case "sepia":
540
+ return "sepia(0.6) contrast(1.1) brightness(0.95)";
541
+ case "dark":
542
+ return "invert(0.9) hue-rotate(180deg) brightness(1.1)";
543
+ case "high-contrast":
544
+ return "contrast(2) grayscale(1)";
545
+ default:
546
+ return "none";
547
+ }
548
+ };
549
+ return /* @__PURE__ */ jsxs4(
550
+ "div",
551
+ {
552
+ ref: containerRef,
553
+ className: `relative inline-block shadow-2xl bg-white mb-10 transition-all ${activeTool !== "select" ? "no-select cursor-crosshair" : ""}`,
554
+ style: { scrollMarginTop: "20px", minHeight: "100px" },
555
+ onMouseDown: handleMouseDown,
556
+ onMouseMove: handleMouseMove,
557
+ onMouseUp: handleMouseUp,
558
+ children: [
559
+ loading && /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 bg-gray-50 flex items-center justify-center z-10 animate-pulse", children: /* @__PURE__ */ jsx4("span", { className: "text-[10px] font-black text-gray-400 uppercase tracking-widest", children: "Sincronizando..." }) }),
560
+ /* @__PURE__ */ jsx4("canvas", { ref: canvasRef, style: { filter: getPageFilter() }, className: "block" }),
561
+ /* @__PURE__ */ jsx4(
562
+ "div",
563
+ {
564
+ ref: textLayerRef,
565
+ className: "textLayer",
566
+ style: { pointerEvents: activeTool === "select" ? "auto" : "none" }
567
+ }
568
+ ),
569
+ isDragging && /* @__PURE__ */ jsx4(
570
+ "div",
571
+ {
572
+ className: "absolute border-2 z-[40] pointer-events-none",
573
+ style: {
574
+ borderColor: accentColor,
575
+ backgroundColor: `${accentColor}33`,
576
+ left: currentRect.x,
577
+ top: currentRect.y,
578
+ width: currentRect.w,
579
+ height: currentRect.h
580
+ }
581
+ }
582
+ ),
583
+ /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 pointer-events-none z-20", children: annotations.filter((a) => a.pageIndex === pageIndex).map((ann) => /* @__PURE__ */ jsx4(
584
+ AnnotationItem,
585
+ {
586
+ ann,
587
+ isSelected: selectedAnnotationId === ann.id,
588
+ accentColor,
589
+ onDelete: () => removeAnnotation(ann.id),
590
+ onSelect: () => setSelectedAnnotation(ann.id)
591
+ },
592
+ ann.id
593
+ )) })
594
+ ]
595
+ }
596
+ );
597
+ };
598
+ var AnnotationItem = ({ ann, isSelected, accentColor, onDelete, onSelect }) => {
599
+ const isText = ann.type === "text" || ann.type === "comment";
600
+ return /* @__PURE__ */ jsxs4(
601
+ "div",
602
+ {
603
+ className: `absolute pointer-events-auto transition-all ${isSelected ? "shadow-xl z-30" : "z-10"}`,
604
+ style: {
605
+ left: `${ann.rect.x * 100}%`,
606
+ top: `${ann.rect.y * 100}%`,
607
+ width: `${ann.rect.width * 100}%`,
608
+ height: `${ann.rect.height * 100}%`,
609
+ backgroundColor: ann.type === "highlight" ? `${ann.color}77` : "transparent",
610
+ borderBottom: ann.type === "strikeout" ? `2px solid ${ann.color}` : "none",
611
+ outline: isSelected ? `2px solid ${accentColor}` : void 0
612
+ },
613
+ onClick: (e) => {
614
+ e.stopPropagation();
615
+ onSelect();
616
+ },
617
+ children: [
618
+ isText && isSelected && /* @__PURE__ */ jsx4("div", { className: "absolute top-full mt-2 w-64 bg-white shadow-2xl rounded-xl p-4 border border-gray-100 z-50", children: /* @__PURE__ */ jsx4(
619
+ "textarea",
620
+ {
621
+ className: "w-full bg-transparent border-none focus:ring-0 p-0 text-gray-800 text-xs font-medium",
622
+ placeholder: "Escreva sua nota...",
623
+ rows: 3,
624
+ defaultValue: ann.content || "",
625
+ autoFocus: true
626
+ }
627
+ ) }),
628
+ isSelected && /* @__PURE__ */ jsx4(
629
+ "button",
630
+ {
631
+ onClick: (e) => {
632
+ e.stopPropagation();
633
+ onDelete();
634
+ },
635
+ className: "absolute -top-3 -right-3 bg-red-500 text-white rounded-full p-1 shadow-lg hover:bg-red-600",
636
+ children: /* @__PURE__ */ jsx4("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 3, d: "M6 18L18 6M6 6l12 12" }) })
637
+ }
638
+ )
639
+ ]
640
+ }
641
+ );
642
+ };
643
+ var PageRenderer_default = PageRenderer;
644
+
645
+ // components/Viewer.tsx
646
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
647
+ var Viewer = ({ engine }) => {
648
+ const { viewMode, pageCount, currentPage, activeTool, uiTheme, setDocumentState, accentColor } = useViewerStore5();
649
+ const isDark = uiTheme === "dark";
650
+ const viewerRef = useRef4(null);
651
+ useEffect4(() => {
652
+ const observer = new IntersectionObserver((entries) => {
653
+ entries.forEach((entry) => {
654
+ if (entry.isIntersecting) {
655
+ const pageIndex = parseInt(entry.target.getAttribute("data-page-index") || "0");
656
+ if (pageIndex + 1 !== currentPage) setDocumentState({ currentPage: pageIndex + 1 });
657
+ }
658
+ });
659
+ }, { root: viewerRef.current, threshold: 0.5 });
660
+ const pageElements = viewerRef.current?.querySelectorAll(".page-container");
661
+ pageElements?.forEach((el) => observer.observe(el));
662
+ return () => {
663
+ pageElements?.forEach((el) => observer.unobserve(el));
664
+ observer.disconnect();
665
+ };
666
+ }, [pageCount, setDocumentState, currentPage]);
667
+ const pages = Array.from({ length: pageCount }).map((_, i) => i);
668
+ const tools = [
669
+ { id: "select", name: "Select", icon: "M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5" },
670
+ { id: "highlight", name: "Marker", icon: "M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" },
671
+ { id: "strikeout", name: "Strike", icon: "M13 10V3L4 14h7v7l9-11h-7z" },
672
+ { id: "comment", name: "Note", icon: "M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" }
673
+ ];
674
+ return /* @__PURE__ */ jsxs5("div", { ref: viewerRef, className: `flex-1 overflow-auto flex flex-col items-center py-16 relative custom-scrollbar scroll-smooth ${isDark ? "bg-[#121212]" : "bg-[#e9ecef]"}`, children: [
675
+ /* @__PURE__ */ jsx5("div", { className: "flex flex-col items-center gap-6 w-full", children: pages.map((idx) => /* @__PURE__ */ jsx5("div", { "data-page-index": idx, className: "page-container", children: /* @__PURE__ */ jsx5(PageRenderer_default, { engine, pageIndex: idx }) }, idx)) }),
676
+ /* @__PURE__ */ jsx5("div", { className: `fixed bottom-10 left-1/2 -translate-x-1/2 shadow-2xl rounded-2xl p-2 flex border z-50 ${isDark ? "bg-[#2a2a2a]/90 border-[#3a3a3a] backdrop-blur-xl" : "bg-white/95 border-gray-100 backdrop-blur-md"}`, children: tools.map((tool) => /* @__PURE__ */ jsx5(
677
+ "button",
678
+ {
679
+ onClick: () => setDocumentState({ activeTool: tool.id }),
680
+ className: `w-10 h-10 rounded-xl flex items-center justify-center transition-all ${activeTool === tool.id ? "text-white shadow-lg" : "text-gray-400"}`,
681
+ style: activeTool === tool.id ? { backgroundColor: accentColor } : void 0,
682
+ children: /* @__PURE__ */ jsx5("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: tool.icon }) })
683
+ },
684
+ tool.id
685
+ )) })
686
+ ] });
687
+ };
688
+ var Viewer_default = Viewer;
689
+ export {
690
+ PageRenderer_default as PageRenderer,
691
+ SidebarLeft_default as SidebarLeft,
692
+ SidebarRight_default as SidebarRight,
693
+ Topbar_default as Topbar,
694
+ Viewer_default as Viewer
695
+ };
696
+ //# sourceMappingURL=index.mjs.map