@papyrus-sdk/ui-react 0.2.7 → 0.2.9
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/README.md +15 -0
- package/base.css +92 -38
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +909 -274
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +907 -272
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,15 +17,15 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
18
|
|
|
19
19
|
// index.ts
|
|
20
|
-
var
|
|
21
|
-
__export(
|
|
20
|
+
var ui_react_exports = {};
|
|
21
|
+
__export(ui_react_exports, {
|
|
22
22
|
PageRenderer: () => PageRenderer_default,
|
|
23
23
|
SidebarLeft: () => SidebarLeft_default,
|
|
24
24
|
SidebarRight: () => SidebarRight_default,
|
|
25
25
|
Topbar: () => Topbar_default,
|
|
26
26
|
Viewer: () => Viewer_default
|
|
27
27
|
});
|
|
28
|
-
module.exports = __toCommonJS(
|
|
28
|
+
module.exports = __toCommonJS(ui_react_exports);
|
|
29
29
|
|
|
30
30
|
// components/Topbar.tsx
|
|
31
31
|
var import_react = require("react");
|
|
@@ -57,16 +57,31 @@ var Topbar = ({
|
|
|
57
57
|
triggerScrollToPage
|
|
58
58
|
} = (0, import_core.useViewerStore)();
|
|
59
59
|
const fileInputRef = (0, import_react.useRef)(null);
|
|
60
|
+
const zoomTimerRef = (0, import_react.useRef)(null);
|
|
61
|
+
const pendingZoomRef = (0, import_react.useRef)(null);
|
|
60
62
|
const [pageInput, setPageInput] = (0, import_react.useState)(currentPage.toString());
|
|
63
|
+
const pageDigits = Math.max(2, String(pageCount || 1).length);
|
|
61
64
|
const [showPageThemes, setShowPageThemes] = (0, import_react.useState)(false);
|
|
62
65
|
const isDark = uiTheme === "dark";
|
|
63
66
|
(0, import_react.useEffect)(() => {
|
|
64
67
|
setPageInput(currentPage.toString());
|
|
65
68
|
}, [currentPage]);
|
|
69
|
+
(0, import_react.useEffect)(() => () => {
|
|
70
|
+
if (zoomTimerRef.current) clearTimeout(zoomTimerRef.current);
|
|
71
|
+
}, []);
|
|
66
72
|
const handleZoom = (delta) => {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
const baseZoom = pendingZoomRef.current ?? zoom;
|
|
74
|
+
const nextZoom = Math.max(0.2, Math.min(5, baseZoom + delta));
|
|
75
|
+
pendingZoomRef.current = nextZoom;
|
|
76
|
+
if (zoomTimerRef.current) return;
|
|
77
|
+
zoomTimerRef.current = setTimeout(() => {
|
|
78
|
+
zoomTimerRef.current = null;
|
|
79
|
+
const targetZoom = pendingZoomRef.current;
|
|
80
|
+
pendingZoomRef.current = null;
|
|
81
|
+
if (targetZoom == null) return;
|
|
82
|
+
engine.setZoom(targetZoom);
|
|
83
|
+
setDocumentState({ zoom: targetZoom });
|
|
84
|
+
}, 80);
|
|
70
85
|
};
|
|
71
86
|
const handlePageChange = (page) => {
|
|
72
87
|
if (pageCount <= 0) return;
|
|
@@ -81,95 +96,103 @@ var Topbar = ({
|
|
|
81
96
|
{ id: "dark", name: "Invertido", color: "bg-gray-800" },
|
|
82
97
|
{ id: "high-contrast", name: "Contraste", color: "bg-black" }
|
|
83
98
|
];
|
|
84
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"input",
|
|
98
|
-
{
|
|
99
|
-
type: "text",
|
|
100
|
-
className: "w-10 text-center bg-transparent focus:outline-none font-bold text-sm",
|
|
101
|
-
value: pageInput,
|
|
102
|
-
onChange: (e) => setPageInput(e.target.value),
|
|
103
|
-
onKeyDown: (e) => e.key === "Enter" && handlePageChange(parseInt(pageInput)),
|
|
104
|
-
onBlur: () => handlePageChange(parseInt(pageInput))
|
|
105
|
-
}
|
|
106
|
-
),
|
|
107
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "opacity-40 px-1", children: "/" }),
|
|
108
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "opacity-80 text-sm", children: pageCount > 0 ? pageCount : "\u2014" }),
|
|
109
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => handlePageChange(currentPage + 1), className: "p-1.5 rounded transition-all hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) }) })
|
|
110
|
-
] }),
|
|
111
|
-
showZoomControls && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `flex items-center rounded-lg p-1 border ${isDark ? "bg-[#2a2a2a] border-[#444]" : "bg-gray-50 border-gray-200"}`, children: [
|
|
112
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => handleZoom(-0.1), className: "p-1.5 rounded hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) }) }),
|
|
113
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "px-3 text-xs font-bold min-w-[50px] text-center", children: [
|
|
114
|
-
Math.round(zoom * 100),
|
|
115
|
-
"%"
|
|
99
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
100
|
+
"div",
|
|
101
|
+
{
|
|
102
|
+
"data-papyrus-theme": uiTheme,
|
|
103
|
+
className: `papyrus-topbar papyrus-theme 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"}`,
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-3", children: [
|
|
106
|
+
showSidebarLeftToggle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: toggleSidebarLeft, className: `p-2 rounded-md ${isDark ? "hover:bg-white/10" : "hover:bg-gray-100"}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }) }),
|
|
107
|
+
showBrand && (brand ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-bold text-lg tracking-tight", style: { color: accentColor }, children: [
|
|
108
|
+
"Papyrus",
|
|
109
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: isDark ? "text-white" : "text-gray-900", children: "Core" })
|
|
110
|
+
] })),
|
|
111
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `text-sm font-semibold truncate max-w-[220px] ${isDark ? "text-gray-200" : "text-gray-700"}`, title: typeof title === "string" ? title : void 0, children: title })
|
|
116
112
|
] }),
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
children:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
113
|
+
(showPageControls || showZoomControls) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-4", children: [
|
|
114
|
+
showPageControls && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `papyrus-control flex items-center rounded-lg p-1 space-x-1 border ${isDark ? "bg-[#2a2a2a] border-[#444]" : "bg-gray-50 border-gray-200"}`, children: [
|
|
115
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => handlePageChange(currentPage - 1), className: "p-1.5 rounded transition-all hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }),
|
|
116
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
117
|
+
"input",
|
|
118
|
+
{
|
|
119
|
+
type: "text",
|
|
120
|
+
className: "papyrus-input text-center bg-transparent focus:outline-none font-bold text-sm shrink-0",
|
|
121
|
+
style: { width: `${pageDigits + 1.75}ch` },
|
|
122
|
+
value: pageInput,
|
|
123
|
+
onChange: (e) => setPageInput(e.target.value),
|
|
124
|
+
onKeyDown: (e) => e.key === "Enter" && handlePageChange(parseInt(pageInput)),
|
|
125
|
+
onBlur: () => handlePageChange(parseInt(pageInput))
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "opacity-40 px-1", children: "/" }),
|
|
129
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "opacity-80 text-sm", children: pageCount > 0 ? pageCount : "\u2014" }),
|
|
130
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => handlePageChange(currentPage + 1), className: "p-1.5 rounded transition-all hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) }) })
|
|
131
|
+
] }),
|
|
132
|
+
showZoomControls && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `papyrus-control flex items-center rounded-lg p-1 border ${isDark ? "bg-[#2a2a2a] border-[#444]" : "bg-gray-50 border-gray-200"}`, children: [
|
|
133
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => handleZoom(-0.1), className: "p-1.5 rounded hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) }) }),
|
|
134
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "px-3 text-xs font-bold min-w-[50px] text-center", children: [
|
|
135
|
+
Math.round(zoom * 100),
|
|
136
|
+
"%"
|
|
137
|
+
] }),
|
|
138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => handleZoom(0.1), className: "p-1.5 rounded hover:brightness-110", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" }) }) })
|
|
139
|
+
] })
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center space-x-3", children: [
|
|
142
|
+
showPageThemeSelector && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative", children: [
|
|
143
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
144
|
+
"button",
|
|
145
|
+
{
|
|
146
|
+
onClick: () => setShowPageThemes(!showPageThemes),
|
|
147
|
+
className: `papyrus-control 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"}`,
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `w-3 h-3 rounded-full border ${themes.find((t) => t.id === pageTheme)?.color}` }),
|
|
150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "TEMA" })
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
showPageThemes && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `papyrus-popover 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__ */ (0, import_jsx_runtime.jsxs)(
|
|
155
|
+
"button",
|
|
156
|
+
{
|
|
157
|
+
onClick: () => {
|
|
158
|
+
setDocumentState({ pageTheme: t.id });
|
|
159
|
+
setShowPageThemes(false);
|
|
160
|
+
},
|
|
161
|
+
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"}`,
|
|
162
|
+
style: pageTheme === t.id ? { backgroundColor: accentColor } : void 0,
|
|
163
|
+
children: [
|
|
164
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: `w-3 h-3 rounded-full border ${t.color}` }),
|
|
165
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t.name })
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
t.id
|
|
169
|
+
)) })
|
|
170
|
+
] }),
|
|
171
|
+
showUIToggle && /* @__PURE__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" }) }) }),
|
|
172
|
+
showUpload && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
173
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
174
|
+
"button",
|
|
175
|
+
{
|
|
176
|
+
onClick: () => fileInputRef.current?.click(),
|
|
177
|
+
className: "px-4 py-1.5 text-white rounded-md text-sm font-bold shadow-md active:scale-95",
|
|
178
|
+
style: { backgroundColor: accentColor },
|
|
179
|
+
children: "UPLOAD"
|
|
180
|
+
}
|
|
181
|
+
),
|
|
182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "file", ref: fileInputRef, className: "hidden", accept: ".pdf,.epub,.txt", onChange: async (e) => {
|
|
183
|
+
const file = e.target.files?.[0];
|
|
184
|
+
if (file) {
|
|
185
|
+
setDocumentState({ isLoaded: false });
|
|
186
|
+
await engine.load(file);
|
|
187
|
+
setDocumentState({ isLoaded: true, pageCount: engine.getPageCount(), currentPage: 1, outline: await engine.getOutline() });
|
|
188
|
+
}
|
|
189
|
+
} })
|
|
190
|
+
] }),
|
|
191
|
+
showSearch && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => toggleSidebarRight("search"), className: `p-2 rounded-md ${isDark ? "hover:bg-white/10" : "hover:bg-gray-100"}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) })
|
|
192
|
+
] })
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
);
|
|
173
196
|
};
|
|
174
197
|
var Topbar_default = Topbar;
|
|
175
198
|
|
|
@@ -187,22 +210,44 @@ var withAlpha = (hex, alpha) => {
|
|
|
187
210
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
188
211
|
};
|
|
189
212
|
var Thumbnail = ({ engine, pageIndex, active, isDark, accentColor, onClick }) => {
|
|
213
|
+
const wrapperRef = (0, import_react2.useRef)(null);
|
|
190
214
|
const canvasRef = (0, import_react2.useRef)(null);
|
|
191
215
|
const htmlRef = (0, import_react2.useRef)(null);
|
|
192
216
|
const accentSoft = withAlpha(accentColor, 0.12);
|
|
193
217
|
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
218
|
+
const [isVisible, setIsVisible] = (0, import_react2.useState)(false);
|
|
194
219
|
(0, import_react2.useEffect)(() => {
|
|
195
|
-
|
|
220
|
+
const target = wrapperRef.current;
|
|
221
|
+
if (!target) return;
|
|
222
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
223
|
+
setIsVisible(true);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const root = target.closest(".custom-scrollbar");
|
|
227
|
+
const observer = new IntersectionObserver((entries) => {
|
|
228
|
+
entries.forEach((entry) => {
|
|
229
|
+
if (entry.isIntersecting) {
|
|
230
|
+
setIsVisible(true);
|
|
231
|
+
observer.disconnect();
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}, { root: root ?? null, rootMargin: "200px" });
|
|
235
|
+
observer.observe(target);
|
|
236
|
+
return () => observer.disconnect();
|
|
237
|
+
}, []);
|
|
238
|
+
(0, import_react2.useEffect)(() => {
|
|
239
|
+
if (renderTargetType === "element" || !isVisible) return;
|
|
196
240
|
const target = canvasRef.current;
|
|
197
241
|
if (target) {
|
|
198
242
|
engine.renderPage(pageIndex, target, 0.15).catch((err) => {
|
|
199
243
|
console.error("[Papyrus] Thumbnail render failed:", err);
|
|
200
244
|
});
|
|
201
245
|
}
|
|
202
|
-
}, [engine, pageIndex, renderTargetType]);
|
|
246
|
+
}, [engine, pageIndex, renderTargetType, isVisible]);
|
|
203
247
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
204
248
|
"div",
|
|
205
249
|
{
|
|
250
|
+
ref: wrapperRef,
|
|
206
251
|
onClick,
|
|
207
252
|
className: `p-3 cursor-pointer transition-all rounded-lg border-2 ${active ? "shadow-sm" : "border-transparent"}`,
|
|
208
253
|
style: active ? { borderColor: accentColor, backgroundColor: accentSoft } : void 0,
|
|
@@ -300,39 +345,46 @@ var SidebarLeft = ({ engine }) => {
|
|
|
300
345
|
} = (0, import_core2.useViewerStore)();
|
|
301
346
|
const isDark = uiTheme === "dark";
|
|
302
347
|
if (!sidebarLeftOpen) return null;
|
|
303
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
348
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
349
|
+
"div",
|
|
350
|
+
{
|
|
351
|
+
"data-papyrus-theme": uiTheme,
|
|
352
|
+
className: `papyrus-sidebar-left papyrus-theme 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"}`,
|
|
353
|
+
children: [
|
|
354
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `p-4 border-b flex flex-col space-y-4 ${isDark ? "border-[#3a3a3a]" : "border-gray-100"}`, children: [
|
|
355
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
356
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: `text-sm font-bold uppercase tracking-widest ${isDark ? "text-gray-100" : "text-gray-800"}`, children: sidebarLeftTab === "thumbnails" ? "Thumbnails" : "Sum\xE1rio" }),
|
|
357
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => setDocumentState({ sidebarLeftOpen: false }), className: "text-gray-400 hover:text-gray-600", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
|
|
358
|
+
] }),
|
|
359
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-1", children: [
|
|
360
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
361
|
+
"button",
|
|
362
|
+
{
|
|
363
|
+
onClick: () => setSidebarLeftTab("thumbnails"),
|
|
364
|
+
className: `p-2 rounded-md ${sidebarLeftTab === "thumbnails" ? isDark ? "bg-white/10 text-white" : "bg-white shadow-sm border border-gray-200" : "text-gray-400"}`,
|
|
365
|
+
style: sidebarLeftTab === "thumbnails" && !isDark ? { color: accentColor } : void 0,
|
|
366
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }) })
|
|
367
|
+
}
|
|
368
|
+
),
|
|
369
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
370
|
+
"button",
|
|
371
|
+
{
|
|
372
|
+
onClick: () => setSidebarLeftTab("summary"),
|
|
373
|
+
className: `p-2 rounded-md ${sidebarLeftTab === "summary" ? isDark ? "bg-white/10 text-white" : "bg-white shadow-sm border border-gray-200" : "text-gray-400"}`,
|
|
374
|
+
style: sidebarLeftTab === "summary" && !isDark ? { color: accentColor } : void 0,
|
|
375
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h7" }) })
|
|
376
|
+
}
|
|
377
|
+
)
|
|
378
|
+
] })
|
|
379
|
+
] }),
|
|
380
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-1 overflow-y-auto custom-scrollbar p-3", children: sidebarLeftTab === "thumbnails" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-1", children: Array.from({ length: pageCount }).map((_, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Thumbnail, { engine, pageIndex: idx, isDark, accentColor, active: currentPage === idx + 1, onClick: () => {
|
|
381
|
+
engine.goToPage(idx + 1);
|
|
382
|
+
setDocumentState({ currentPage: idx + 1 });
|
|
383
|
+
triggerScrollToPage(idx);
|
|
384
|
+
} }, idx)) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-col space-y-0.5", children: outline.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(OutlineNode, { item, engine, isDark, accentColor }, i)) }) })
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
);
|
|
336
388
|
};
|
|
337
389
|
var SidebarLeft_default = SidebarLeft;
|
|
338
390
|
|
|
@@ -368,6 +420,7 @@ var SidebarRight = ({ engine }) => {
|
|
|
368
420
|
const searchService = new import_core3.SearchService(engine);
|
|
369
421
|
const isDark = uiTheme === "dark";
|
|
370
422
|
const accentSoft = withAlpha2(accentColor, 0.12);
|
|
423
|
+
const resultsCount = searchResults.length;
|
|
371
424
|
const handleSearch = async (e) => {
|
|
372
425
|
e.preventDefault();
|
|
373
426
|
if (!query.trim()) {
|
|
@@ -380,114 +433,124 @@ var SidebarRight = ({ engine }) => {
|
|
|
380
433
|
setIsSearching(false);
|
|
381
434
|
};
|
|
382
435
|
if (!sidebarRightOpen) return null;
|
|
383
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
410
|
-
"input",
|
|
411
|
-
{
|
|
412
|
-
type: "text",
|
|
413
|
-
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"}`,
|
|
414
|
-
placeholder: "O que voc\xEA procura?",
|
|
415
|
-
value: query,
|
|
416
|
-
onChange: (e) => setQuery(e.target.value)
|
|
417
|
-
}
|
|
418
|
-
),
|
|
419
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", className: "absolute right-3 top-2.5 text-gray-400 transition-colors", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) })
|
|
420
|
-
] }),
|
|
421
|
-
isSearching && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 space-y-3", children: [
|
|
422
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-6 h-6 border-2 border-t-transparent rounded-full animate-spin", style: { borderColor: accentColor } }),
|
|
423
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[10px] font-bold text-gray-500 uppercase", children: "Varrendo documento..." })
|
|
424
|
-
] }),
|
|
425
|
-
!isSearching && searchResults.map((res, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
426
|
-
"div",
|
|
427
|
-
{
|
|
428
|
-
onClick: () => {
|
|
429
|
-
setDocumentState({ activeSearchIndex: idx });
|
|
430
|
-
triggerScrollToPage(res.pageIndex);
|
|
431
|
-
},
|
|
432
|
-
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"}`,
|
|
433
|
-
style: idx === activeSearchIndex ? { borderColor: accentColor, backgroundColor: accentSoft } : void 0,
|
|
434
|
-
children: [
|
|
435
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
|
|
436
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
437
|
-
"span",
|
|
438
|
-
{
|
|
439
|
-
className: `text-[10px] font-black uppercase tracking-tighter ${idx === activeSearchIndex ? "" : "text-gray-400"}`,
|
|
440
|
-
style: idx === activeSearchIndex ? { color: accentColor } : void 0,
|
|
441
|
-
children: [
|
|
442
|
-
"P\xC1GINA ",
|
|
443
|
-
res.pageIndex + 1
|
|
444
|
-
]
|
|
445
|
-
}
|
|
446
|
-
),
|
|
447
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
448
|
-
"svg",
|
|
449
|
-
{
|
|
450
|
-
className: `w-3 h-3 transition-transform ${idx === activeSearchIndex ? "" : "text-gray-300"}`,
|
|
451
|
-
style: idx === activeSearchIndex ? { color: accentColor } : void 0,
|
|
452
|
-
fill: "none",
|
|
453
|
-
stroke: "currentColor",
|
|
454
|
-
viewBox: "0 0 24 24",
|
|
455
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 3, d: "M9 5l7 7-7 7" })
|
|
456
|
-
}
|
|
457
|
-
)
|
|
458
|
-
] }),
|
|
459
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: `text-[11px] font-medium leading-relaxed italic ${isDark ? "text-gray-400" : "text-gray-600"}`, children: [
|
|
460
|
-
"...",
|
|
461
|
-
res.text,
|
|
462
|
-
"..."
|
|
463
|
-
] })
|
|
464
|
-
]
|
|
465
|
-
},
|
|
466
|
-
idx
|
|
467
|
-
))
|
|
468
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-3", children: [
|
|
469
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "text-[9px] font-black text-gray-400 uppercase tracking-[0.2em] mb-6 flex items-center", children: [
|
|
470
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "WORKSET" }),
|
|
471
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1 h-px bg-current ml-3 opacity-10" })
|
|
472
|
-
] }),
|
|
473
|
-
annotations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "text-center py-20", children: [
|
|
474
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-12 h-12 bg-gray-500/10 rounded-full flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "w-6 h-6 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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" }) }) }),
|
|
475
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[10px] font-bold text-gray-400 uppercase tracking-widest", children: "Sem anota\xE7\xF5es" })
|
|
476
|
-
] }) : annotations.map((ann) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("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: [
|
|
477
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between mb-3", children: [
|
|
478
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
479
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-2.5 h-2.5 rounded-full", style: { backgroundColor: ann.color } }),
|
|
480
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "text-[10px] font-black", style: { color: accentColor }, children: [
|
|
481
|
-
"P",
|
|
482
|
-
ann.pageIndex + 1
|
|
483
|
-
] })
|
|
436
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
437
|
+
"div",
|
|
438
|
+
{
|
|
439
|
+
"data-papyrus-theme": uiTheme,
|
|
440
|
+
className: `papyrus-sidebar-right papyrus-theme 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"}`,
|
|
441
|
+
children: [
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `p-4 border-b flex items-center justify-between shrink-0 ${isDark ? "border-[#333]" : "border-gray-100"}`, children: [
|
|
443
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex space-x-6", children: [
|
|
444
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
445
|
+
"button",
|
|
446
|
+
{
|
|
447
|
+
onClick: () => toggleSidebarRight("search"),
|
|
448
|
+
className: `text-[10px] font-black uppercase tracking-widest pb-1 transition-all ${sidebarRightTab === "search" ? "border-b-2" : "text-gray-400"}`,
|
|
449
|
+
style: sidebarRightTab === "search" ? { color: accentColor, borderColor: accentColor } : void 0,
|
|
450
|
+
children: "Busca"
|
|
451
|
+
}
|
|
452
|
+
),
|
|
453
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
454
|
+
"button",
|
|
455
|
+
{
|
|
456
|
+
onClick: () => toggleSidebarRight("annotations"),
|
|
457
|
+
className: `text-[10px] font-black uppercase tracking-widest pb-1 transition-all ${sidebarRightTab === "annotations" ? "border-b-2" : "text-gray-400"}`,
|
|
458
|
+
style: sidebarRightTab === "annotations" ? { color: accentColor, borderColor: accentColor } : void 0,
|
|
459
|
+
children: "Notas"
|
|
460
|
+
}
|
|
461
|
+
)
|
|
484
462
|
] }),
|
|
485
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("
|
|
463
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: () => toggleSidebarRight(), className: "papyrus-unstyled-button text-gray-400 hover:text-red-500 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
|
|
486
464
|
] }),
|
|
487
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
465
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1 overflow-y-auto p-4 custom-scrollbar bg-opacity-50", children: sidebarRightTab === "search" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-4", children: [
|
|
466
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSearch, className: "relative mb-6", children: [
|
|
467
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
468
|
+
"input",
|
|
469
|
+
{
|
|
470
|
+
type: "text",
|
|
471
|
+
className: `papyrus-input 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"}`,
|
|
472
|
+
placeholder: "O que voc\xEA procura?",
|
|
473
|
+
value: query,
|
|
474
|
+
onChange: (e) => setQuery(e.target.value)
|
|
475
|
+
}
|
|
476
|
+
),
|
|
477
|
+
resultsCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "absolute right-9 top-2.5 text-[10px] font-bold text-gray-400", children: resultsCount }),
|
|
478
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", className: "papyrus-unstyled-button absolute right-3 top-2.5 text-gray-400 transition-colors", style: { color: accentColor }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }) })
|
|
479
|
+
] }),
|
|
480
|
+
isSearching && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 space-y-3", children: [
|
|
481
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-6 h-6 border-2 border-t-transparent rounded-full animate-spin", style: { borderColor: accentColor } }),
|
|
482
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[10px] font-bold text-gray-500 uppercase", children: "Varrendo documento..." })
|
|
483
|
+
] }),
|
|
484
|
+
!isSearching && searchResults.map((res, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
485
|
+
"div",
|
|
486
|
+
{
|
|
487
|
+
onClick: () => {
|
|
488
|
+
const page = res.pageIndex + 1;
|
|
489
|
+
engine.goToPage(page);
|
|
490
|
+
setDocumentState({ activeSearchIndex: idx, currentPage: page });
|
|
491
|
+
triggerScrollToPage(res.pageIndex);
|
|
492
|
+
},
|
|
493
|
+
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"}`,
|
|
494
|
+
style: idx === activeSearchIndex ? { borderColor: accentColor, backgroundColor: accentSoft } : void 0,
|
|
495
|
+
children: [
|
|
496
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
|
|
497
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
498
|
+
"span",
|
|
499
|
+
{
|
|
500
|
+
className: `text-[10px] font-black uppercase tracking-tighter ${idx === activeSearchIndex ? "" : "text-gray-400"}`,
|
|
501
|
+
style: idx === activeSearchIndex ? { color: accentColor } : void 0,
|
|
502
|
+
children: [
|
|
503
|
+
"P\xC1GINA ",
|
|
504
|
+
res.pageIndex + 1
|
|
505
|
+
]
|
|
506
|
+
}
|
|
507
|
+
),
|
|
508
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
509
|
+
"svg",
|
|
510
|
+
{
|
|
511
|
+
className: `w-3 h-3 transition-transform ${idx === activeSearchIndex ? "" : "text-gray-300"}`,
|
|
512
|
+
style: idx === activeSearchIndex ? { color: accentColor } : void 0,
|
|
513
|
+
fill: "none",
|
|
514
|
+
stroke: "currentColor",
|
|
515
|
+
viewBox: "0 0 24 24",
|
|
516
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 3, d: "M9 5l7 7-7 7" })
|
|
517
|
+
}
|
|
518
|
+
)
|
|
519
|
+
] }),
|
|
520
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: `text-[11px] font-medium leading-relaxed italic ${isDark ? "text-gray-400" : "text-gray-600"}`, children: [
|
|
521
|
+
"...",
|
|
522
|
+
res.text,
|
|
523
|
+
"..."
|
|
524
|
+
] })
|
|
525
|
+
]
|
|
526
|
+
},
|
|
527
|
+
idx
|
|
528
|
+
))
|
|
529
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-3", children: [
|
|
530
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "text-[9px] font-black text-gray-400 uppercase tracking-[0.2em] mb-6 flex items-center", children: [
|
|
531
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "WORKSET" }),
|
|
532
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1 h-px bg-current ml-3 opacity-10" })
|
|
533
|
+
] }),
|
|
534
|
+
annotations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "text-center py-20", children: [
|
|
535
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-12 h-12 bg-gray-500/10 rounded-full flex items-center justify-center mx-auto mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "w-6 h-6 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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" }) }) }),
|
|
536
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[10px] font-bold text-gray-400 uppercase tracking-widest", children: "Sem anota\xE7\xF5es" })
|
|
537
|
+
] }) : annotations.map((ann) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("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: [
|
|
538
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between mb-3", children: [
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center space-x-2", children: [
|
|
540
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-2.5 h-2.5 rounded-full", style: { backgroundColor: ann.color } }),
|
|
541
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "text-[10px] font-black", style: { color: accentColor }, children: [
|
|
542
|
+
"P",
|
|
543
|
+
ann.pageIndex + 1
|
|
544
|
+
] })
|
|
545
|
+
] }),
|
|
546
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[9px] text-gray-400 font-bold", children: new Date(ann.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) })
|
|
547
|
+
] }),
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: `text-[11px] font-bold uppercase tracking-tight ${isDark ? "text-gray-200" : "text-gray-700"}`, children: ann.type })
|
|
549
|
+
] }, ann.id))
|
|
550
|
+
] }) })
|
|
551
|
+
]
|
|
552
|
+
}
|
|
553
|
+
);
|
|
491
554
|
};
|
|
492
555
|
var SidebarRight_default = SidebarRight;
|
|
493
556
|
|
|
@@ -500,7 +563,7 @@ var import_react4 = require("react");
|
|
|
500
563
|
var import_core4 = require("@papyrus-sdk/core");
|
|
501
564
|
var import_types = require("@papyrus-sdk/types");
|
|
502
565
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
503
|
-
var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
566
|
+
var PageRenderer = ({ engine, pageIndex, availableWidth, onMeasuredSize }) => {
|
|
504
567
|
const containerRef = (0, import_react4.useRef)(null);
|
|
505
568
|
const canvasRef = (0, import_react4.useRef)(null);
|
|
506
569
|
const htmlLayerRef = (0, import_react4.useRef)(null);
|
|
@@ -510,6 +573,10 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
510
573
|
const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
|
|
511
574
|
const [startPos, setStartPos] = (0, import_react4.useState)({ x: 0, y: 0 });
|
|
512
575
|
const [currentRect, setCurrentRect] = (0, import_react4.useState)({ x: 0, y: 0, w: 0, h: 0 });
|
|
576
|
+
const [textLayerVersion, setTextLayerVersion] = (0, import_react4.useState)(0);
|
|
577
|
+
const [selectionMenu, setSelectionMenu] = (0, import_react4.useState)(null);
|
|
578
|
+
const [isInkDrawing, setIsInkDrawing] = (0, import_react4.useState)(false);
|
|
579
|
+
const [inkPoints, setInkPoints] = (0, import_react4.useState)([]);
|
|
513
580
|
const {
|
|
514
581
|
zoom,
|
|
515
582
|
rotation,
|
|
@@ -522,10 +589,20 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
522
589
|
removeAnnotation,
|
|
523
590
|
selectedAnnotationId,
|
|
524
591
|
setSelectedAnnotation,
|
|
525
|
-
accentColor
|
|
592
|
+
accentColor,
|
|
593
|
+
annotationColor,
|
|
594
|
+
searchQuery,
|
|
595
|
+
searchResults,
|
|
596
|
+
activeSearchIndex
|
|
526
597
|
} = (0, import_core4.useViewerStore)();
|
|
527
598
|
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
528
599
|
const isElementRender = renderTargetType === "element";
|
|
600
|
+
const textMarkupTools = /* @__PURE__ */ new Set(["highlight", "underline", "squiggly", "strikeout"]);
|
|
601
|
+
const canSelectText = activeTool === "select" || textMarkupTools.has(activeTool);
|
|
602
|
+
const hasSearchHits = (0, import_react4.useMemo)(
|
|
603
|
+
() => Boolean(searchQuery?.trim()) && searchResults.some((res) => res.pageIndex === pageIndex),
|
|
604
|
+
[searchQuery, searchResults, pageIndex]
|
|
605
|
+
);
|
|
529
606
|
(0, import_react4.useEffect)(() => {
|
|
530
607
|
let active = true;
|
|
531
608
|
const loadSize = async () => {
|
|
@@ -558,8 +635,14 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
558
635
|
};
|
|
559
636
|
}, [pageSize, zoom, fitScale]);
|
|
560
637
|
(0, import_react4.useEffect)(() => {
|
|
561
|
-
if (
|
|
562
|
-
|
|
638
|
+
if (!displaySize || !onMeasuredSize) return;
|
|
639
|
+
onMeasuredSize(pageIndex, {
|
|
640
|
+
width: Math.round(displaySize.width),
|
|
641
|
+
height: Math.round(displaySize.height)
|
|
642
|
+
});
|
|
643
|
+
}, [displaySize, onMeasuredSize, pageIndex]);
|
|
644
|
+
(0, import_react4.useEffect)(() => {
|
|
645
|
+
if (scrollToPageSignal === pageIndex) {
|
|
563
646
|
setDocumentState({ scrollToPageSignal: null });
|
|
564
647
|
}
|
|
565
648
|
}, [scrollToPageSignal, pageIndex, setDocumentState]);
|
|
@@ -571,10 +654,15 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
571
654
|
setLoading(true);
|
|
572
655
|
try {
|
|
573
656
|
const RENDER_SCALE = 2;
|
|
574
|
-
const
|
|
575
|
-
|
|
657
|
+
const canvasRenderScale = isElementRender ? 1 : RENDER_SCALE * fitScale;
|
|
658
|
+
const textRenderScale = isElementRender ? 1 : fitScale;
|
|
659
|
+
if (!isElementRender && canvasRef.current && displaySize) {
|
|
660
|
+
canvasRef.current.style.width = `${displaySize.width}px`;
|
|
661
|
+
canvasRef.current.style.height = `${displaySize.height}px`;
|
|
662
|
+
}
|
|
663
|
+
await engine.renderPage(pageIndex, renderTarget, canvasRenderScale);
|
|
576
664
|
if (!isElementRender && !pageSize && canvasRef.current) {
|
|
577
|
-
const denom =
|
|
665
|
+
const denom = canvasRenderScale * Math.max(zoom, 0.01);
|
|
578
666
|
if (denom > 0) {
|
|
579
667
|
setPageSize({
|
|
580
668
|
width: canvasRef.current.width / denom,
|
|
@@ -585,7 +673,7 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
585
673
|
if (!active || !textLayerRef.current) return;
|
|
586
674
|
if (!isElementRender) {
|
|
587
675
|
textLayerRef.current.innerHTML = "";
|
|
588
|
-
await engine.renderTextLayer(pageIndex, textLayerRef.current,
|
|
676
|
+
await engine.renderTextLayer(pageIndex, textLayerRef.current, textRenderScale);
|
|
589
677
|
}
|
|
590
678
|
if (!active || !textLayerRef.current) return;
|
|
591
679
|
if (!isElementRender && displaySize) {
|
|
@@ -596,6 +684,7 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
596
684
|
textLayerRef.current.style.width = `${displaySize.width}px`;
|
|
597
685
|
textLayerRef.current.style.height = `${displaySize.height}px`;
|
|
598
686
|
}
|
|
687
|
+
setTextLayerVersion((v) => v + 1);
|
|
599
688
|
} catch (err) {
|
|
600
689
|
if (!active) return;
|
|
601
690
|
console.error("[Papyrus] Falha na renderiza\xE7\xE3o:", err);
|
|
@@ -608,8 +697,68 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
608
697
|
active = false;
|
|
609
698
|
};
|
|
610
699
|
}, [engine, pageIndex, zoom, rotation, isElementRender, fitScale, displaySize, pageSize]);
|
|
700
|
+
(0, import_react4.useEffect)(() => {
|
|
701
|
+
if (isElementRender) return;
|
|
702
|
+
const layer = textLayerRef.current;
|
|
703
|
+
if (!layer) return;
|
|
704
|
+
const query = searchQuery?.trim().toLowerCase();
|
|
705
|
+
const existingMarks = Array.from(layer.querySelectorAll("mark.papyrus-search-hit"));
|
|
706
|
+
existingMarks.forEach((mark) => {
|
|
707
|
+
const parent = mark.parentNode;
|
|
708
|
+
if (!parent) return;
|
|
709
|
+
while (mark.firstChild) parent.insertBefore(mark.firstChild, mark);
|
|
710
|
+
parent.removeChild(mark);
|
|
711
|
+
parent.normalize();
|
|
712
|
+
});
|
|
713
|
+
if (!query || !hasSearchHits) return;
|
|
714
|
+
const nodes = [];
|
|
715
|
+
const walker = document.createTreeWalker(layer, NodeFilter.SHOW_TEXT, {
|
|
716
|
+
acceptNode(node) {
|
|
717
|
+
const text = node.nodeValue ?? "";
|
|
718
|
+
if (!text.trim()) return NodeFilter.FILTER_REJECT;
|
|
719
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
while (walker.nextNode()) {
|
|
723
|
+
nodes.push(walker.currentNode);
|
|
724
|
+
}
|
|
725
|
+
nodes.forEach((textNode) => {
|
|
726
|
+
const text = textNode.nodeValue ?? "";
|
|
727
|
+
const lower = text.toLowerCase();
|
|
728
|
+
if (!lower.includes(query)) return;
|
|
729
|
+
const fragment = document.createDocumentFragment();
|
|
730
|
+
let cursor = 0;
|
|
731
|
+
let index = lower.indexOf(query, cursor);
|
|
732
|
+
while (index !== -1) {
|
|
733
|
+
if (index > cursor) {
|
|
734
|
+
fragment.appendChild(document.createTextNode(text.slice(cursor, index)));
|
|
735
|
+
}
|
|
736
|
+
const mark = document.createElement("mark");
|
|
737
|
+
mark.className = "papyrus-search-hit";
|
|
738
|
+
mark.textContent = text.slice(index, index + query.length);
|
|
739
|
+
fragment.appendChild(mark);
|
|
740
|
+
cursor = index + query.length;
|
|
741
|
+
index = lower.indexOf(query, cursor);
|
|
742
|
+
}
|
|
743
|
+
if (cursor < text.length) {
|
|
744
|
+
fragment.appendChild(document.createTextNode(text.slice(cursor)));
|
|
745
|
+
}
|
|
746
|
+
const parent = textNode.parentNode;
|
|
747
|
+
if (parent) parent.replaceChild(fragment, textNode);
|
|
748
|
+
});
|
|
749
|
+
}, [searchQuery, hasSearchHits, pageIndex, isElementRender, activeSearchIndex, textLayerVersion]);
|
|
611
750
|
const handleMouseDown = (e) => {
|
|
612
|
-
|
|
751
|
+
setSelectionMenu(null);
|
|
752
|
+
if (activeTool === "ink") {
|
|
753
|
+
const rect2 = containerRef.current?.getBoundingClientRect();
|
|
754
|
+
if (!rect2) return;
|
|
755
|
+
const x2 = (e.clientX - rect2.left) / rect2.width;
|
|
756
|
+
const y2 = (e.clientY - rect2.top) / rect2.height;
|
|
757
|
+
setIsInkDrawing(true);
|
|
758
|
+
setInkPoints([{ x: x2, y: y2 }]);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (canSelectText) return;
|
|
613
762
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
614
763
|
if (!rect) return;
|
|
615
764
|
setIsDragging(true);
|
|
@@ -619,6 +768,14 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
619
768
|
setCurrentRect({ x, y, w: 0, h: 0 });
|
|
620
769
|
};
|
|
621
770
|
const handleMouseMove = (e) => {
|
|
771
|
+
if (isInkDrawing) {
|
|
772
|
+
const rect2 = containerRef.current?.getBoundingClientRect();
|
|
773
|
+
if (!rect2) return;
|
|
774
|
+
const x = (e.clientX - rect2.left) / rect2.width;
|
|
775
|
+
const y = (e.clientY - rect2.top) / rect2.height;
|
|
776
|
+
setInkPoints((prev) => [...prev, { x, y }]);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
622
779
|
if (!isDragging) return;
|
|
623
780
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
624
781
|
if (!rect) return;
|
|
@@ -632,6 +789,115 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
632
789
|
});
|
|
633
790
|
};
|
|
634
791
|
const handleMouseUp = (e) => {
|
|
792
|
+
if (isInkDrawing) {
|
|
793
|
+
setIsInkDrawing(false);
|
|
794
|
+
if (inkPoints.length > 1) {
|
|
795
|
+
const xs = inkPoints.map((p) => p.x);
|
|
796
|
+
const ys = inkPoints.map((p) => p.y);
|
|
797
|
+
const minX = Math.min(...xs);
|
|
798
|
+
const maxX = Math.max(...xs);
|
|
799
|
+
const minY = Math.min(...ys);
|
|
800
|
+
const maxY = Math.max(...ys);
|
|
801
|
+
const width = Math.max(maxX - minX, 5e-4);
|
|
802
|
+
const height = Math.max(maxY - minY, 5e-4);
|
|
803
|
+
const path = inkPoints.map((p) => ({
|
|
804
|
+
x: Math.max(0, Math.min(1, p.x)),
|
|
805
|
+
y: Math.max(0, Math.min(1, p.y))
|
|
806
|
+
}));
|
|
807
|
+
addAnnotation({
|
|
808
|
+
id: Math.random().toString(36).substr(2, 9),
|
|
809
|
+
pageIndex,
|
|
810
|
+
type: "ink",
|
|
811
|
+
rect: { x: minX, y: minY, width, height },
|
|
812
|
+
path,
|
|
813
|
+
color: annotationColor,
|
|
814
|
+
createdAt: Date.now()
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
setInkPoints([]);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
const selection = window.getSelection();
|
|
821
|
+
const selectionText = selection?.toString().trim() ?? "";
|
|
822
|
+
if (selectionText && textLayerRef.current && containerRef.current && selection && selection.rangeCount > 0) {
|
|
823
|
+
const range = selection.getRangeAt(0);
|
|
824
|
+
if (textLayerRef.current.contains(range.commonAncestorContainer)) {
|
|
825
|
+
const containerRect = containerRef.current.getBoundingClientRect();
|
|
826
|
+
const clientRects = Array.from(range.getClientRects());
|
|
827
|
+
const rects = clientRects.filter((r) => r.width > 1 && r.height > 1).map((r) => {
|
|
828
|
+
const x = (r.left - containerRect.left) / containerRect.width;
|
|
829
|
+
const y = (r.top - containerRect.top) / containerRect.height;
|
|
830
|
+
const width = r.width / containerRect.width;
|
|
831
|
+
const height = r.height / containerRect.height;
|
|
832
|
+
return {
|
|
833
|
+
x: Math.max(0, Math.min(1, x)),
|
|
834
|
+
y: Math.max(0, Math.min(1, y)),
|
|
835
|
+
width: Math.max(0, Math.min(1, width)),
|
|
836
|
+
height: Math.max(0, Math.min(1, height))
|
|
837
|
+
};
|
|
838
|
+
});
|
|
839
|
+
const uniqueRects = rects.filter((rect, index, list) => {
|
|
840
|
+
const key = `${Math.round(rect.x * 1e4)}-${Math.round(rect.y * 1e4)}-${Math.round(rect.width * 1e4)}-${Math.round(rect.height * 1e4)}`;
|
|
841
|
+
return list.findIndex((r) => `${Math.round(r.x * 1e4)}-${Math.round(r.y * 1e4)}-${Math.round(r.width * 1e4)}-${Math.round(r.height * 1e4)}` === key) === index;
|
|
842
|
+
});
|
|
843
|
+
const mergedRects = uniqueRects.reduce((acc, rect) => {
|
|
844
|
+
const target = acc.find((r) => {
|
|
845
|
+
const closeY = Math.abs(r.y - rect.y) < 2e-3 && Math.abs(r.height - rect.height) < 2e-3;
|
|
846
|
+
const overlaps = rect.x <= r.x + r.width + 2e-3 && rect.x + rect.width >= r.x - 2e-3;
|
|
847
|
+
return closeY && overlaps;
|
|
848
|
+
});
|
|
849
|
+
if (!target) {
|
|
850
|
+
acc.push({ ...rect });
|
|
851
|
+
return acc;
|
|
852
|
+
}
|
|
853
|
+
const left = Math.min(target.x, rect.x);
|
|
854
|
+
const right = Math.max(target.x + target.width, rect.x + rect.width);
|
|
855
|
+
target.x = left;
|
|
856
|
+
target.width = right - left;
|
|
857
|
+
return acc;
|
|
858
|
+
}, []);
|
|
859
|
+
if (mergedRects.length) {
|
|
860
|
+
const xs = mergedRects.map((r) => r.x);
|
|
861
|
+
const ys = mergedRects.map((r) => r.y);
|
|
862
|
+
const xe = mergedRects.map((r) => r.x + r.width);
|
|
863
|
+
const ye = mergedRects.map((r) => r.y + r.height);
|
|
864
|
+
const rect = {
|
|
865
|
+
x: Math.min(...xs),
|
|
866
|
+
y: Math.min(...ys),
|
|
867
|
+
width: Math.max(...xe) - Math.min(...xs),
|
|
868
|
+
height: Math.max(...ye) - Math.min(...ys)
|
|
869
|
+
};
|
|
870
|
+
if (textMarkupTools.has(activeTool)) {
|
|
871
|
+
addAnnotation({
|
|
872
|
+
id: Math.random().toString(36).substr(2, 9),
|
|
873
|
+
pageIndex,
|
|
874
|
+
type: activeTool,
|
|
875
|
+
rect,
|
|
876
|
+
rects: mergedRects,
|
|
877
|
+
color: annotationColor,
|
|
878
|
+
content: selectionText,
|
|
879
|
+
createdAt: Date.now()
|
|
880
|
+
});
|
|
881
|
+
selection.removeAllRanges();
|
|
882
|
+
setSelectionMenu(null);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
if (activeTool === "select") {
|
|
886
|
+
const anchorX = (rect.x + rect.width) * containerRect.width;
|
|
887
|
+
const anchorY = rect.y * containerRect.height;
|
|
888
|
+
setSelectionMenu({
|
|
889
|
+
rects: mergedRects,
|
|
890
|
+
rect,
|
|
891
|
+
text: selectionText,
|
|
892
|
+
anchor: {
|
|
893
|
+
x: Math.max(12, Math.min(containerRect.width - 12, anchorX)),
|
|
894
|
+
y: Math.max(12, anchorY - 32)
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
635
901
|
if (isDragging) {
|
|
636
902
|
setIsDragging(false);
|
|
637
903
|
if (currentRect.w > 5 && currentRect.h > 5) {
|
|
@@ -647,7 +913,7 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
647
913
|
width: currentRect.w / rect.width,
|
|
648
914
|
height: currentRect.h / rect.height
|
|
649
915
|
},
|
|
650
|
-
color: activeTool === "highlight" ?
|
|
916
|
+
color: activeTool === "highlight" ? annotationColor : accentColor,
|
|
651
917
|
content: activeTool === "text" || activeTool === "comment" ? "" : void 0,
|
|
652
918
|
createdAt: Date.now()
|
|
653
919
|
});
|
|
@@ -657,7 +923,6 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
657
923
|
return;
|
|
658
924
|
}
|
|
659
925
|
if (activeTool === "select") {
|
|
660
|
-
const selection = window.getSelection();
|
|
661
926
|
const selectedText = selection?.toString().trim();
|
|
662
927
|
if (selectedText) {
|
|
663
928
|
import_core4.papyrusEvents.emit(import_types.PapyrusEventType.TEXT_SELECTED, {
|
|
@@ -683,7 +948,7 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
683
948
|
"div",
|
|
684
949
|
{
|
|
685
950
|
ref: containerRef,
|
|
686
|
-
className: `relative inline-block shadow-2xl bg-white mb-10 transition-all ${
|
|
951
|
+
className: `relative inline-block shadow-2xl bg-white mb-10 transition-all ${canSelectText ? "" : "no-select cursor-crosshair"}`,
|
|
687
952
|
style: { scrollMarginTop: "20px", minHeight: "100px" },
|
|
688
953
|
onMouseDown: handleMouseDown,
|
|
689
954
|
onMouseMove: handleMouseMove,
|
|
@@ -712,7 +977,7 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
712
977
|
ref: textLayerRef,
|
|
713
978
|
className: "textLayer",
|
|
714
979
|
style: {
|
|
715
|
-
pointerEvents: isElementRender ? "none" :
|
|
980
|
+
pointerEvents: isElementRender ? "none" : canSelectText ? "auto" : "none",
|
|
716
981
|
display: isElementRender ? "none" : "block"
|
|
717
982
|
}
|
|
718
983
|
}
|
|
@@ -722,8 +987,9 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
722
987
|
{
|
|
723
988
|
className: "absolute border-2 z-[40] pointer-events-none",
|
|
724
989
|
style: {
|
|
725
|
-
borderColor: accentColor,
|
|
726
|
-
backgroundColor: `${accentColor}33`,
|
|
990
|
+
borderColor: activeTool === "highlight" ? annotationColor : accentColor,
|
|
991
|
+
backgroundColor: activeTool === "highlight" ? `${annotationColor}66` : `${accentColor}33`,
|
|
992
|
+
mixBlendMode: activeTool === "highlight" ? "multiply" : void 0,
|
|
727
993
|
left: currentRect.x,
|
|
728
994
|
top: currentRect.y,
|
|
729
995
|
width: currentRect.w,
|
|
@@ -731,6 +997,60 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
731
997
|
}
|
|
732
998
|
}
|
|
733
999
|
),
|
|
1000
|
+
isInkDrawing && inkPoints.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1001
|
+
"svg",
|
|
1002
|
+
{
|
|
1003
|
+
className: "absolute inset-0 pointer-events-none z-[45]",
|
|
1004
|
+
viewBox: "0 0 1 1",
|
|
1005
|
+
preserveAspectRatio: "none",
|
|
1006
|
+
style: { width: "100%", height: "100%" },
|
|
1007
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1008
|
+
"path",
|
|
1009
|
+
{
|
|
1010
|
+
d: inkPoints.map((p, idx) => `${idx === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" "),
|
|
1011
|
+
fill: "none",
|
|
1012
|
+
stroke: annotationColor,
|
|
1013
|
+
strokeWidth: 8e-3,
|
|
1014
|
+
strokeLinecap: "round",
|
|
1015
|
+
strokeLinejoin: "round"
|
|
1016
|
+
}
|
|
1017
|
+
)
|
|
1018
|
+
}
|
|
1019
|
+
),
|
|
1020
|
+
selectionMenu && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1021
|
+
"div",
|
|
1022
|
+
{
|
|
1023
|
+
className: "absolute z-[60] flex items-center gap-1 rounded-full border px-2 py-1 shadow-xl bg-white/95 backdrop-blur-md text-gray-700",
|
|
1024
|
+
style: { left: selectionMenu.anchor.x, top: selectionMenu.anchor.y },
|
|
1025
|
+
children: [
|
|
1026
|
+
{ id: "highlight", label: "Marcar" },
|
|
1027
|
+
{ id: "underline", label: "Sublinhar" },
|
|
1028
|
+
{ id: "squiggly", label: "Onda" },
|
|
1029
|
+
{ id: "strikeout", label: "Risco" }
|
|
1030
|
+
].map((action) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1031
|
+
"button",
|
|
1032
|
+
{
|
|
1033
|
+
className: "text-[10px] font-bold px-2 py-1 rounded-full hover:bg-gray-100",
|
|
1034
|
+
onClick: () => {
|
|
1035
|
+
addAnnotation({
|
|
1036
|
+
id: Math.random().toString(36).substr(2, 9),
|
|
1037
|
+
pageIndex,
|
|
1038
|
+
type: action.id,
|
|
1039
|
+
rect: selectionMenu.rect,
|
|
1040
|
+
rects: selectionMenu.rects,
|
|
1041
|
+
content: selectionMenu.text,
|
|
1042
|
+
color: annotationColor,
|
|
1043
|
+
createdAt: Date.now()
|
|
1044
|
+
});
|
|
1045
|
+
window.getSelection()?.removeAllRanges();
|
|
1046
|
+
setSelectionMenu(null);
|
|
1047
|
+
},
|
|
1048
|
+
children: action.label
|
|
1049
|
+
},
|
|
1050
|
+
action.id
|
|
1051
|
+
))
|
|
1052
|
+
}
|
|
1053
|
+
),
|
|
734
1054
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute inset-0 pointer-events-none z-20", children: annotations.filter((a) => a.pageIndex === pageIndex).map((ann) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
735
1055
|
AnnotationItem,
|
|
736
1056
|
{
|
|
@@ -748,6 +1068,101 @@ var PageRenderer = ({ engine, pageIndex, availableWidth }) => {
|
|
|
748
1068
|
};
|
|
749
1069
|
var AnnotationItem = ({ ann, isSelected, accentColor, onDelete, onSelect }) => {
|
|
750
1070
|
const isText = ann.type === "text" || ann.type === "comment";
|
|
1071
|
+
const isHighlight = ann.type === "highlight";
|
|
1072
|
+
const isMarkup = ann.type === "highlight" || ann.type === "underline" || ann.type === "squiggly" || ann.type === "strikeout";
|
|
1073
|
+
const rects = ann.rects && ann.rects.length > 0 ? ann.rects : [ann.rect];
|
|
1074
|
+
const isInk = ann.type === "ink" && ann.path && ann.path.length > 1;
|
|
1075
|
+
const renderMarkupRects = () => {
|
|
1076
|
+
if (!isMarkup) return null;
|
|
1077
|
+
return rects.map((r, idx) => {
|
|
1078
|
+
const left = ann.rect.width ? (r.x - ann.rect.x) / ann.rect.width * 100 : 0;
|
|
1079
|
+
const top = ann.rect.height ? (r.y - ann.rect.y) / ann.rect.height * 100 : 0;
|
|
1080
|
+
const width = ann.rect.width ? r.width / ann.rect.width * 100 : 100;
|
|
1081
|
+
const height = ann.rect.height ? r.height / ann.rect.height * 100 : 100;
|
|
1082
|
+
if (ann.type === "highlight") {
|
|
1083
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1084
|
+
"div",
|
|
1085
|
+
{
|
|
1086
|
+
className: "absolute rounded-sm",
|
|
1087
|
+
style: {
|
|
1088
|
+
left: `${left}%`,
|
|
1089
|
+
top: `${top}%`,
|
|
1090
|
+
width: `${width}%`,
|
|
1091
|
+
height: `${height}%`,
|
|
1092
|
+
backgroundColor: `${ann.color}88`,
|
|
1093
|
+
mixBlendMode: "multiply"
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
idx
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
const lineStyle = {
|
|
1100
|
+
left: `${left}%`,
|
|
1101
|
+
width: `${width}%`
|
|
1102
|
+
};
|
|
1103
|
+
if (ann.type === "underline") {
|
|
1104
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1105
|
+
"div",
|
|
1106
|
+
{
|
|
1107
|
+
className: "absolute",
|
|
1108
|
+
style: {
|
|
1109
|
+
...lineStyle,
|
|
1110
|
+
top: `calc(${top}% + ${height}% - 2px)`,
|
|
1111
|
+
height: "2px",
|
|
1112
|
+
backgroundColor: ann.color
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
idx
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
if (ann.type === "strikeout") {
|
|
1119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1120
|
+
"div",
|
|
1121
|
+
{
|
|
1122
|
+
className: "absolute",
|
|
1123
|
+
style: {
|
|
1124
|
+
...lineStyle,
|
|
1125
|
+
top: `calc(${top}% + ${height * 0.5}% - 1px)`,
|
|
1126
|
+
height: "2px",
|
|
1127
|
+
backgroundColor: ann.color
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
idx
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
if (ann.type === "squiggly") {
|
|
1134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1135
|
+
"div",
|
|
1136
|
+
{
|
|
1137
|
+
className: "absolute",
|
|
1138
|
+
style: {
|
|
1139
|
+
...lineStyle,
|
|
1140
|
+
top: `calc(${top}% + ${height}% - 4px)`,
|
|
1141
|
+
height: "4px",
|
|
1142
|
+
backgroundImage: `linear-gradient(135deg, transparent 75%, ${ann.color} 0), linear-gradient(225deg, transparent 75%, ${ann.color} 0)`,
|
|
1143
|
+
backgroundSize: "6px 6px",
|
|
1144
|
+
backgroundPosition: "0 0, 3px 3px"
|
|
1145
|
+
}
|
|
1146
|
+
},
|
|
1147
|
+
idx
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
return null;
|
|
1151
|
+
});
|
|
1152
|
+
};
|
|
1153
|
+
const renderInk = () => {
|
|
1154
|
+
if (!isInk || !ann.path) return null;
|
|
1155
|
+
const d = ann.path.map((p, idx) => `${idx === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
|
|
1156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1157
|
+
"svg",
|
|
1158
|
+
{
|
|
1159
|
+
className: "absolute inset-0",
|
|
1160
|
+
viewBox: `${ann.rect.x} ${ann.rect.y} ${ann.rect.width} ${ann.rect.height}`,
|
|
1161
|
+
preserveAspectRatio: "none",
|
|
1162
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d, fill: "none", stroke: ann.color, strokeWidth: 8e-3, strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1163
|
+
}
|
|
1164
|
+
);
|
|
1165
|
+
};
|
|
751
1166
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
752
1167
|
"div",
|
|
753
1168
|
{
|
|
@@ -757,8 +1172,9 @@ var AnnotationItem = ({ ann, isSelected, accentColor, onDelete, onSelect }) => {
|
|
|
757
1172
|
top: `${ann.rect.y * 100}%`,
|
|
758
1173
|
width: `${ann.rect.width * 100}%`,
|
|
759
1174
|
height: `${ann.rect.height * 100}%`,
|
|
760
|
-
backgroundColor:
|
|
761
|
-
|
|
1175
|
+
backgroundColor: !isMarkup && isHighlight ? `${ann.color}88` : "transparent",
|
|
1176
|
+
mixBlendMode: !isMarkup && isHighlight ? "multiply" : void 0,
|
|
1177
|
+
borderBottom: ann.type === "strikeout" && !isMarkup ? `2px solid ${ann.color}` : "none",
|
|
762
1178
|
outline: isSelected ? `2px solid ${accentColor}` : void 0
|
|
763
1179
|
},
|
|
764
1180
|
onClick: (e) => {
|
|
@@ -766,6 +1182,8 @@ var AnnotationItem = ({ ann, isSelected, accentColor, onDelete, onSelect }) => {
|
|
|
766
1182
|
onSelect();
|
|
767
1183
|
},
|
|
768
1184
|
children: [
|
|
1185
|
+
renderMarkupRects(),
|
|
1186
|
+
renderInk(),
|
|
769
1187
|
isText && isSelected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)(
|
|
770
1188
|
"textarea",
|
|
771
1189
|
{
|
|
@@ -795,14 +1213,36 @@ var PageRenderer_default = PageRenderer;
|
|
|
795
1213
|
|
|
796
1214
|
// components/Viewer.tsx
|
|
797
1215
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1216
|
+
var BASE_OVERSCAN = 6;
|
|
798
1217
|
var Viewer = ({ engine }) => {
|
|
799
|
-
const {
|
|
1218
|
+
const { pageCount, currentPage, zoom, activeTool, uiTheme, scrollToPageSignal, setDocumentState, accentColor, annotationColor, setAnnotationColor } = (0, import_core5.useViewerStore)();
|
|
800
1219
|
const isDark = uiTheme === "dark";
|
|
801
1220
|
const viewerRef = (0, import_react5.useRef)(null);
|
|
1221
|
+
const colorPickerRef = (0, import_react5.useRef)(null);
|
|
1222
|
+
const pageRefs = (0, import_react5.useRef)([]);
|
|
1223
|
+
const intersectionRatiosRef = (0, import_react5.useRef)({});
|
|
1224
|
+
const frameRef = (0, import_react5.useRef)(null);
|
|
1225
|
+
const jumpRef = (0, import_react5.useRef)(false);
|
|
1226
|
+
const jumpTimerRef = (0, import_react5.useRef)(null);
|
|
802
1227
|
const [availableWidth, setAvailableWidth] = (0, import_react5.useState)(null);
|
|
1228
|
+
const [basePageSize, setBasePageSize] = (0, import_react5.useState)(null);
|
|
1229
|
+
const [pageSizes, setPageSizes] = (0, import_react5.useState)({});
|
|
1230
|
+
const [colorPickerOpen, setColorPickerOpen] = (0, import_react5.useState)(false);
|
|
803
1231
|
const isCompact = availableWidth !== null && availableWidth < 820;
|
|
804
1232
|
const paddingY = isCompact ? "py-10" : "py-16";
|
|
805
|
-
const toolDockPosition = isCompact ? "bottom-
|
|
1233
|
+
const toolDockPosition = isCompact ? "bottom-4" : "bottom-8";
|
|
1234
|
+
const colorPalette = ["#fbbf24", "#f97316", "#ef4444", "#22c55e", "#06b6d4", "#3b82f6", "#8b5cf6", "#111827"];
|
|
1235
|
+
(0, import_react5.useEffect)(() => {
|
|
1236
|
+
if (!colorPickerOpen) return;
|
|
1237
|
+
const handleClick = (event) => {
|
|
1238
|
+
if (!colorPickerRef.current) return;
|
|
1239
|
+
if (!colorPickerRef.current.contains(event.target)) {
|
|
1240
|
+
setColorPickerOpen(false);
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
document.addEventListener("mousedown", handleClick);
|
|
1244
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
1245
|
+
}, [colorPickerOpen]);
|
|
806
1246
|
(0, import_react5.useEffect)(() => {
|
|
807
1247
|
const element = viewerRef.current;
|
|
808
1248
|
if (!element) return;
|
|
@@ -819,44 +1259,239 @@ var Viewer = ({ engine }) => {
|
|
|
819
1259
|
observer.observe(element);
|
|
820
1260
|
return () => observer.disconnect();
|
|
821
1261
|
}, []);
|
|
1262
|
+
(0, import_react5.useEffect)(() => {
|
|
1263
|
+
let active = true;
|
|
1264
|
+
if (!pageCount) return;
|
|
1265
|
+
const loadBaseSize = async () => {
|
|
1266
|
+
try {
|
|
1267
|
+
const size = await engine.getPageDimensions(0);
|
|
1268
|
+
if (!active || !size.width || !size.height) return;
|
|
1269
|
+
setBasePageSize(size);
|
|
1270
|
+
} catch {
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
loadBaseSize();
|
|
1274
|
+
return () => {
|
|
1275
|
+
active = false;
|
|
1276
|
+
};
|
|
1277
|
+
}, [engine, pageCount]);
|
|
1278
|
+
(0, import_react5.useEffect)(() => {
|
|
1279
|
+
if (scrollToPageSignal == null) return;
|
|
1280
|
+
const root = viewerRef.current;
|
|
1281
|
+
const target = pageRefs.current[scrollToPageSignal];
|
|
1282
|
+
if (root) {
|
|
1283
|
+
setDocumentState({ currentPage: scrollToPageSignal + 1 });
|
|
1284
|
+
jumpRef.current = true;
|
|
1285
|
+
if (jumpTimerRef.current) clearTimeout(jumpTimerRef.current);
|
|
1286
|
+
const previousBehavior = root.style.scrollBehavior;
|
|
1287
|
+
root.style.scrollBehavior = "auto";
|
|
1288
|
+
let targetTop = null;
|
|
1289
|
+
if (target) {
|
|
1290
|
+
targetTop = target.offsetTop;
|
|
1291
|
+
} else if (basePageSize && availableWidth) {
|
|
1292
|
+
const fitScale = Math.min(1, Math.max(0, availableWidth - 48) / basePageSize.width);
|
|
1293
|
+
const estimatedPageHeight = basePageSize.height * fitScale * zoom + 64;
|
|
1294
|
+
targetTop = Math.max(0, estimatedPageHeight * scrollToPageSignal);
|
|
1295
|
+
} else if (pageCount > 1) {
|
|
1296
|
+
const maxScroll = Math.max(0, root.scrollHeight - root.clientHeight);
|
|
1297
|
+
const ratio = scrollToPageSignal / Math.max(1, pageCount - 1);
|
|
1298
|
+
targetTop = Math.max(0, maxScroll * ratio);
|
|
1299
|
+
}
|
|
1300
|
+
if (targetTop != null) {
|
|
1301
|
+
root.scrollTop = Math.max(0, targetTop - 12);
|
|
1302
|
+
}
|
|
1303
|
+
requestAnimationFrame(() => {
|
|
1304
|
+
root.style.scrollBehavior = previousBehavior;
|
|
1305
|
+
});
|
|
1306
|
+
jumpTimerRef.current = setTimeout(() => {
|
|
1307
|
+
jumpRef.current = false;
|
|
1308
|
+
}, 250);
|
|
1309
|
+
}
|
|
1310
|
+
setDocumentState({ scrollToPageSignal: null });
|
|
1311
|
+
}, [scrollToPageSignal, setDocumentState, basePageSize, availableWidth, zoom, pageCount]);
|
|
1312
|
+
(0, import_react5.useEffect)(() => {
|
|
1313
|
+
setPageSizes({});
|
|
1314
|
+
}, [zoom]);
|
|
822
1315
|
(0, import_react5.useEffect)(() => {
|
|
823
1316
|
const root = viewerRef.current;
|
|
824
1317
|
if (!root) return;
|
|
1318
|
+
const flushCurrentPage = () => {
|
|
1319
|
+
if (jumpRef.current) return;
|
|
1320
|
+
const ratios = intersectionRatiosRef.current;
|
|
1321
|
+
const entries = Object.entries(ratios).filter(([, ratio]) => ratio > 0);
|
|
1322
|
+
if (!entries.length) return;
|
|
1323
|
+
const currentIndex = currentPage - 1;
|
|
1324
|
+
const currentRatio = ratios[currentIndex] ?? 0;
|
|
1325
|
+
const [bestIndexText, bestRatio] = entries.reduce(
|
|
1326
|
+
(best, candidate) => Number(candidate[1]) > Number(best[1]) ? candidate : best
|
|
1327
|
+
);
|
|
1328
|
+
const bestIndex = Number(bestIndexText);
|
|
1329
|
+
if (!Number.isFinite(bestIndex)) return;
|
|
1330
|
+
const bestPage = bestIndex + 1;
|
|
1331
|
+
const shouldSwitch = bestPage !== currentPage && (currentRatio <= 0 || bestRatio >= currentRatio + 0.1 || bestRatio >= 0.75);
|
|
1332
|
+
if (shouldSwitch) setDocumentState({ currentPage: bestPage });
|
|
1333
|
+
};
|
|
825
1334
|
const observer = new IntersectionObserver((entries) => {
|
|
826
1335
|
entries.forEach((entry) => {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1336
|
+
const pageIndex = parseInt(entry.target.getAttribute("data-page-index") || "0");
|
|
1337
|
+
if (!Number.isFinite(pageIndex)) return;
|
|
1338
|
+
intersectionRatiosRef.current[pageIndex] = entry.isIntersecting ? entry.intersectionRatio : 0;
|
|
1339
|
+
});
|
|
1340
|
+
if (frameRef.current != null) cancelAnimationFrame(frameRef.current);
|
|
1341
|
+
frameRef.current = requestAnimationFrame(() => {
|
|
1342
|
+
frameRef.current = null;
|
|
1343
|
+
flushCurrentPage();
|
|
831
1344
|
});
|
|
832
|
-
}, { root, threshold: 0.5 });
|
|
1345
|
+
}, { root, threshold: [0.25, 0.5, 0.75] });
|
|
833
1346
|
const pageElements = root.querySelectorAll(".page-container");
|
|
834
1347
|
pageElements.forEach((el) => observer.observe(el));
|
|
835
1348
|
return () => {
|
|
1349
|
+
if (frameRef.current != null) {
|
|
1350
|
+
cancelAnimationFrame(frameRef.current);
|
|
1351
|
+
frameRef.current = null;
|
|
1352
|
+
}
|
|
836
1353
|
pageElements.forEach((el) => observer.unobserve(el));
|
|
837
1354
|
observer.disconnect();
|
|
838
1355
|
};
|
|
839
1356
|
}, [pageCount, setDocumentState, currentPage]);
|
|
1357
|
+
const virtualOverscan = zoom > 1.35 ? 4 : BASE_OVERSCAN;
|
|
1358
|
+
const virtualAnchor = currentPage - 1;
|
|
1359
|
+
const virtualStart = Math.max(0, virtualAnchor - virtualOverscan);
|
|
1360
|
+
const virtualEnd = Math.min(pageCount - 1, virtualAnchor + virtualOverscan);
|
|
1361
|
+
const fallbackSize = (0, import_react5.useMemo)(() => {
|
|
1362
|
+
if (basePageSize && availableWidth) {
|
|
1363
|
+
const fitScale = Math.min(1, Math.max(0, availableWidth - 48) / basePageSize.width);
|
|
1364
|
+
return {
|
|
1365
|
+
width: Math.round(basePageSize.width * fitScale * zoom),
|
|
1366
|
+
height: Math.round(basePageSize.height * fitScale * zoom)
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
const base = availableWidth ? Math.max(680, availableWidth * 1.3) : 1100;
|
|
1370
|
+
return {
|
|
1371
|
+
width: Math.round((availableWidth ?? 860) - 48),
|
|
1372
|
+
height: Math.round(base * zoom)
|
|
1373
|
+
};
|
|
1374
|
+
}, [basePageSize, availableWidth, zoom]);
|
|
1375
|
+
const averagePageHeight = (0, import_react5.useMemo)(() => {
|
|
1376
|
+
const heights = Object.values(pageSizes).map((size) => size.height);
|
|
1377
|
+
if (!heights.length) return availableWidth ? Math.max(680, availableWidth * 1.3) : 1100;
|
|
1378
|
+
return Math.round(heights.reduce((sum, h) => sum + h, 0) / heights.length);
|
|
1379
|
+
}, [pageSizes, availableWidth]);
|
|
840
1380
|
const pages = Array.from({ length: pageCount }).map((_, i) => i);
|
|
1381
|
+
const handlePageMeasured = (pageIndex, size) => {
|
|
1382
|
+
setPageSizes((prev) => {
|
|
1383
|
+
const current = prev[pageIndex];
|
|
1384
|
+
if (current && current.width === size.width && current.height === size.height) return prev;
|
|
1385
|
+
return { ...prev, [pageIndex]: size };
|
|
1386
|
+
});
|
|
1387
|
+
};
|
|
841
1388
|
const tools = [
|
|
842
1389
|
{ id: "select", name: "Select", icon: "M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5" },
|
|
843
|
-
{ id: "highlight", name: "
|
|
844
|
-
{ id: "
|
|
1390
|
+
{ id: "highlight", name: "Highlight", 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" },
|
|
1391
|
+
{ id: "underline", name: "Underline", icon: "M6 3v6a6 6 0 0012 0V3M4 21h16" },
|
|
1392
|
+
{ id: "squiggly", name: "Squiggly", icon: "M3 17c2-4 4-4 6 0s4 4 6 0 4-4 6 0" },
|
|
1393
|
+
{ id: "strikeout", name: "Strike", icon: "M4 12h16M8 6h8M8 18h8" },
|
|
1394
|
+
{ id: "ink", name: "Freehand", icon: "M4 19c4-6 7-9 10-9 3 0 5 2 6 5" },
|
|
845
1395
|
{ 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" }
|
|
846
1396
|
];
|
|
847
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1397
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1398
|
+
"div",
|
|
1399
|
+
{
|
|
1400
|
+
ref: viewerRef,
|
|
1401
|
+
"data-papyrus-theme": uiTheme,
|
|
1402
|
+
className: `papyrus-viewer papyrus-theme min-w-0 w-full flex-1 overflow-y-scroll overflow-x-hidden flex flex-col items-center ${paddingY} relative custom-scrollbar scroll-smooth ${isDark ? "bg-[#121212]" : "bg-[#e9ecef]"}`,
|
|
1403
|
+
children: [
|
|
1404
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex flex-col items-center gap-6 w-full min-w-0", children: pages.map((idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1405
|
+
"div",
|
|
1406
|
+
{
|
|
1407
|
+
ref: (element) => {
|
|
1408
|
+
pageRefs.current[idx] = element;
|
|
1409
|
+
},
|
|
1410
|
+
"data-page-index": idx,
|
|
1411
|
+
className: "page-container",
|
|
1412
|
+
children: idx >= virtualStart && idx <= virtualEnd ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1413
|
+
PageRenderer_default,
|
|
1414
|
+
{
|
|
1415
|
+
engine,
|
|
1416
|
+
pageIndex: idx,
|
|
1417
|
+
availableWidth: availableWidth ?? void 0,
|
|
1418
|
+
onMeasuredSize: handlePageMeasured
|
|
1419
|
+
}
|
|
1420
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1421
|
+
"div",
|
|
1422
|
+
{
|
|
1423
|
+
className: `inline-block mb-10 shadow-2xl border ${isDark ? "bg-[#0f0f0f] border-[#2b2b2b]" : "bg-white border-gray-200"}`,
|
|
1424
|
+
style: {
|
|
1425
|
+
width: pageSizes[idx]?.width ?? fallbackSize.width,
|
|
1426
|
+
height: pageSizes[idx]?.height ?? Math.max(fallbackSize.height, averagePageHeight)
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
)
|
|
1430
|
+
},
|
|
1431
|
+
idx
|
|
1432
|
+
)) }),
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `papyrus-tool-dock sticky ${toolDockPosition} w-full flex justify-center pointer-events-none z-[70]`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `pointer-events-auto shadow-2xl rounded-2xl p-2 flex items-center border z-[80] ${isDark ? "bg-[#2a2a2a]/90 border-[#3a3a3a] backdrop-blur-xl" : "bg-white/95 border-gray-100 backdrop-blur-md"}`, children: [
|
|
1434
|
+
tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1435
|
+
"button",
|
|
1436
|
+
{
|
|
1437
|
+
title: tool.name,
|
|
1438
|
+
"aria-label": tool.name,
|
|
1439
|
+
onClick: () => setDocumentState({ activeTool: tool.id }),
|
|
1440
|
+
className: `w-10 h-10 rounded-xl flex items-center justify-center transition-all ${activeTool === tool.id ? "text-white shadow-lg" : "text-gray-400"}`,
|
|
1441
|
+
style: activeTool === tool.id ? { backgroundColor: accentColor } : void 0,
|
|
1442
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: tool.icon }) })
|
|
1443
|
+
},
|
|
1444
|
+
tool.id
|
|
1445
|
+
)),
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-px h-7 mx-2 bg-white/10" }),
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: colorPickerRef, className: "relative", children: [
|
|
1448
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1449
|
+
"button",
|
|
1450
|
+
{
|
|
1451
|
+
title: "Cor do marcador",
|
|
1452
|
+
"aria-label": "Cor do marcador",
|
|
1453
|
+
onClick: () => setColorPickerOpen((prev) => !prev),
|
|
1454
|
+
className: "w-9 h-9 rounded-full flex items-center justify-center border transition-all cursor-pointer relative",
|
|
1455
|
+
style: { borderColor: annotationColor },
|
|
1456
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-5 h-5 rounded-full", style: { backgroundColor: annotationColor } })
|
|
1457
|
+
}
|
|
1458
|
+
),
|
|
1459
|
+
colorPickerOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `absolute bottom-full left-1/2 -translate-x-1/2 mb-3 w-48 rounded-xl border p-3 shadow-2xl overflow-hidden ${isDark ? "bg-[#1f1f1f] border-[#333]" : "bg-white border-gray-200"}`, children: [
|
|
1460
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "grid grid-cols-4 gap-2 mb-3", children: colorPalette.map((color) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1461
|
+
"button",
|
|
1462
|
+
{
|
|
1463
|
+
onClick: () => {
|
|
1464
|
+
setAnnotationColor(color);
|
|
1465
|
+
setColorPickerOpen(false);
|
|
1466
|
+
},
|
|
1467
|
+
className: "w-7 h-7 rounded-full border transition-all",
|
|
1468
|
+
style: { backgroundColor: color, borderColor: color === annotationColor ? "#fff" : "transparent" }
|
|
1469
|
+
},
|
|
1470
|
+
color
|
|
1471
|
+
)) }),
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 w-full", children: [
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[10px] uppercase tracking-widest text-gray-400 shrink-0", children: "Hex" }),
|
|
1474
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1475
|
+
"input",
|
|
1476
|
+
{
|
|
1477
|
+
type: "text",
|
|
1478
|
+
value: annotationColor.toUpperCase(),
|
|
1479
|
+
onChange: (e) => {
|
|
1480
|
+
const next = e.target.value.trim();
|
|
1481
|
+
if (next.startsWith("#") && (next.length === 4 || next.length === 7)) {
|
|
1482
|
+
setAnnotationColor(next);
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
className: `flex-1 min-w-0 w-full text-xs rounded-md px-2 py-1 border ${isDark ? "bg-[#2a2a2a] border-[#444] text-white" : "bg-gray-100 border-gray-200 text-gray-700"}`
|
|
1486
|
+
}
|
|
1487
|
+
)
|
|
1488
|
+
] })
|
|
1489
|
+
] })
|
|
1490
|
+
] })
|
|
1491
|
+
] }) })
|
|
1492
|
+
]
|
|
1493
|
+
}
|
|
1494
|
+
);
|
|
860
1495
|
};
|
|
861
1496
|
var Viewer_default = Viewer;
|
|
862
1497
|
// Annotate the CommonJS export names for ESM import in node:
|