@papyrus-sdk/ui-react 0.2.21 → 0.2.23
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/base.css +67 -3
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +529 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +529 -64
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { useViewerStore } from "@papyrus-sdk/core";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
var MOBILE_LANDSCAPE_MAX_HEIGHT_PX = 500;
|
|
7
|
+
var MOBILE_VIEWPORT_QUERY = `(max-width: 639px), (orientation: landscape) and (max-height: ${MOBILE_LANDSCAPE_MAX_HEIGHT_PX}px)`;
|
|
6
8
|
var Topbar = ({
|
|
7
9
|
engine,
|
|
8
10
|
showBrand = false,
|
|
@@ -41,6 +43,8 @@ var Topbar = ({
|
|
|
41
43
|
const [isMobileViewport, setIsMobileViewport] = useState(false);
|
|
42
44
|
const pageDigits = Math.max(2, String(pageCount || 1).length);
|
|
43
45
|
const isDark = uiTheme === "dark";
|
|
46
|
+
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
47
|
+
const isSingleViewportMode = renderTargetType === "element" || renderTargetType === "webview";
|
|
44
48
|
const canUseDOM = typeof document !== "undefined";
|
|
45
49
|
const hasMobileMenu = showZoomControls || showPageThemeSelector || showUIToggle || showUpload;
|
|
46
50
|
useEffect(() => {
|
|
@@ -57,7 +61,7 @@ var Topbar = ({
|
|
|
57
61
|
}, [hasMobileMenu]);
|
|
58
62
|
useEffect(() => {
|
|
59
63
|
if (!canUseDOM || typeof window.matchMedia !== "function") return;
|
|
60
|
-
const mediaQuery = window.matchMedia(
|
|
64
|
+
const mediaQuery = window.matchMedia(MOBILE_VIEWPORT_QUERY);
|
|
61
65
|
const updateViewport = () => setIsMobileViewport(mediaQuery.matches);
|
|
62
66
|
updateViewport();
|
|
63
67
|
if (typeof mediaQuery.addEventListener === "function") {
|
|
@@ -122,6 +126,10 @@ var Topbar = ({
|
|
|
122
126
|
if (pageCount <= 0) return;
|
|
123
127
|
const nextPage = Math.max(1, Math.min(pageCount, isNaN(page) ? 1 : page));
|
|
124
128
|
engine.goToPage(nextPage);
|
|
129
|
+
if (isSingleViewportMode) {
|
|
130
|
+
setDocumentState({ currentPage: nextPage, scrollToPageSignal: null });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
125
133
|
triggerScrollToPage(nextPage - 1);
|
|
126
134
|
};
|
|
127
135
|
const handleFileUpload = async (event) => {
|
|
@@ -757,12 +765,20 @@ var withAlpha = (hex, alpha) => {
|
|
|
757
765
|
const b = parseInt(value.slice(4, 6), 16);
|
|
758
766
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
759
767
|
};
|
|
768
|
+
var isEpubDebugEnabled = () => {
|
|
769
|
+
try {
|
|
770
|
+
return Boolean(globalThis?.__PAPYRUS_EPUB_DEBUG__);
|
|
771
|
+
} catch {
|
|
772
|
+
return false;
|
|
773
|
+
}
|
|
774
|
+
};
|
|
760
775
|
var Thumbnail = ({ engine, pageIndex, active, isDark, accentColor, onClick }) => {
|
|
761
776
|
const wrapperRef = useRef2(null);
|
|
762
777
|
const canvasRef = useRef2(null);
|
|
763
778
|
const htmlRef = useRef2(null);
|
|
764
779
|
const accentSoft = withAlpha(accentColor, 0.12);
|
|
765
780
|
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
781
|
+
const isElementRender = renderTargetType === "element";
|
|
766
782
|
const [isVisible, setIsVisible] = useState2(false);
|
|
767
783
|
useEffect2(() => {
|
|
768
784
|
const target = wrapperRef.current;
|
|
@@ -787,14 +803,14 @@ var Thumbnail = ({ engine, pageIndex, active, isDark, accentColor, onClick }) =>
|
|
|
787
803
|
return () => observer.disconnect();
|
|
788
804
|
}, []);
|
|
789
805
|
useEffect2(() => {
|
|
790
|
-
if (
|
|
791
|
-
const target = canvasRef.current;
|
|
806
|
+
if (!isVisible || isElementRender) return;
|
|
807
|
+
const target = renderTargetType === "element" ? htmlRef.current : canvasRef.current;
|
|
792
808
|
if (target) {
|
|
793
809
|
engine.renderPage(pageIndex, target, 0.15).catch((err) => {
|
|
794
810
|
console.error("[Papyrus] Thumbnail render failed:", err);
|
|
795
811
|
});
|
|
796
812
|
}
|
|
797
|
-
}, [engine, pageIndex, renderTargetType, isVisible]);
|
|
813
|
+
}, [engine, pageIndex, renderTargetType, isVisible, isElementRender]);
|
|
798
814
|
return /* @__PURE__ */ jsx2(
|
|
799
815
|
"div",
|
|
800
816
|
{
|
|
@@ -808,13 +824,20 @@ var Thumbnail = ({ engine, pageIndex, active, isDark, accentColor, onClick }) =>
|
|
|
808
824
|
{
|
|
809
825
|
className: `shadow-lg rounded overflow-hidden mb-2 border ${isDark ? "border-[#333]" : "border-gray-200"}`,
|
|
810
826
|
children: [
|
|
827
|
+
/* @__PURE__ */ jsx2(
|
|
828
|
+
"div",
|
|
829
|
+
{
|
|
830
|
+
className: `w-[90px] h-[120px] items-center justify-center text-[10px] font-black tracking-wider ${isElementRender ? "flex" : "hidden"} ${isDark ? "bg-[#1f1f1f] text-gray-300" : "bg-gray-100 text-gray-500"}`,
|
|
831
|
+
children: "CAP"
|
|
832
|
+
}
|
|
833
|
+
),
|
|
811
834
|
/* @__PURE__ */ jsx2(
|
|
812
835
|
"canvas",
|
|
813
836
|
{
|
|
814
837
|
ref: canvasRef,
|
|
815
838
|
className: "max-w-full h-auto bg-white",
|
|
816
839
|
style: {
|
|
817
|
-
display:
|
|
840
|
+
display: isElementRender ? "none" : "block"
|
|
818
841
|
}
|
|
819
842
|
}
|
|
820
843
|
),
|
|
@@ -826,10 +849,9 @@ var Thumbnail = ({ engine, pageIndex, active, isDark, accentColor, onClick }) =>
|
|
|
826
849
|
style: {
|
|
827
850
|
width: 90,
|
|
828
851
|
height: 120,
|
|
829
|
-
display:
|
|
852
|
+
display: "none",
|
|
830
853
|
overflow: "hidden"
|
|
831
|
-
}
|
|
832
|
-
children: renderTargetType === "element" && /* @__PURE__ */ jsx2("div", { className: "w-full h-full flex items-center justify-center text-[10px] font-semibold text-gray-500", children: "HTML" })
|
|
854
|
+
}
|
|
833
855
|
}
|
|
834
856
|
)
|
|
835
857
|
]
|
|
@@ -848,9 +870,11 @@ var Thumbnail = ({ engine, pageIndex, active, isDark, accentColor, onClick }) =>
|
|
|
848
870
|
);
|
|
849
871
|
};
|
|
850
872
|
var OutlineNode = ({ item, engine, isDark, accentColor, depth = 0 }) => {
|
|
851
|
-
const { triggerScrollToPage, outlineSearchQuery } = useViewerStore2();
|
|
873
|
+
const { triggerScrollToPage, outlineSearchQuery, setDocumentState } = useViewerStore2();
|
|
852
874
|
const [expanded, setExpanded] = useState2(true);
|
|
853
875
|
const accentSoft = withAlpha(accentColor, 0.2);
|
|
876
|
+
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
877
|
+
const isSingleViewportMode = renderTargetType === "element" || renderTargetType === "webview";
|
|
854
878
|
const matchesSearch = outlineSearchQuery === "" || item.title.toLowerCase().includes(outlineSearchQuery.toLowerCase());
|
|
855
879
|
const hasMatchingChildren = item.children?.some(
|
|
856
880
|
(child) => child.title.toLowerCase().includes(outlineSearchQuery.toLowerCase())
|
|
@@ -858,10 +882,53 @@ var OutlineNode = ({ item, engine, isDark, accentColor, depth = 0 }) => {
|
|
|
858
882
|
if (!matchesSearch && !hasMatchingChildren && outlineSearchQuery !== "")
|
|
859
883
|
return null;
|
|
860
884
|
const handleClick = () => {
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
885
|
+
void (async () => {
|
|
886
|
+
if (item.pageIndex < 0 && !item.dest) return;
|
|
887
|
+
let targetPageIndex = item.pageIndex;
|
|
888
|
+
let navigatedByDestination = false;
|
|
889
|
+
const destinationEngine = engine;
|
|
890
|
+
if (isSingleViewportMode && item.dest && typeof destinationEngine.goToDestination === "function") {
|
|
891
|
+
try {
|
|
892
|
+
if (isEpubDebugEnabled()) {
|
|
893
|
+
console.log("[EPUBUI] toc-click", {
|
|
894
|
+
title: item.title,
|
|
895
|
+
dest: item.dest,
|
|
896
|
+
pageIndex: item.pageIndex
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
const resolved = await destinationEngine.goToDestination(item.dest);
|
|
900
|
+
if (isEpubDebugEnabled()) {
|
|
901
|
+
console.log("[EPUBUI] toc-resolved", {
|
|
902
|
+
title: item.title,
|
|
903
|
+
resolved
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
if (resolved != null) targetPageIndex = resolved;
|
|
907
|
+
navigatedByDestination = true;
|
|
908
|
+
} catch {
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (item.dest && (!navigatedByDestination || targetPageIndex < 0)) {
|
|
912
|
+
try {
|
|
913
|
+
const resolved = await engine.getPageIndex(item.dest);
|
|
914
|
+
if (resolved != null) targetPageIndex = resolved;
|
|
915
|
+
} catch {
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (navigatedByDestination && isSingleViewportMode) {
|
|
919
|
+
const page2 = targetPageIndex >= 0 ? targetPageIndex + 1 : engine.getCurrentPage();
|
|
920
|
+
setDocumentState({ currentPage: page2, scrollToPageSignal: null });
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
if (targetPageIndex < 0) return;
|
|
924
|
+
const page = targetPageIndex + 1;
|
|
925
|
+
engine.goToPage(page);
|
|
926
|
+
if (isSingleViewportMode) {
|
|
927
|
+
setDocumentState({ currentPage: page, scrollToPageSignal: null });
|
|
928
|
+
} else {
|
|
929
|
+
triggerScrollToPage(targetPageIndex);
|
|
930
|
+
}
|
|
931
|
+
})();
|
|
865
932
|
};
|
|
866
933
|
return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col", children: [
|
|
867
934
|
/* @__PURE__ */ jsxs2(
|
|
@@ -943,6 +1010,24 @@ var SidebarLeft = ({ engine, style }) => {
|
|
|
943
1010
|
accentColor
|
|
944
1011
|
} = useViewerStore2();
|
|
945
1012
|
const isDark = uiTheme === "dark";
|
|
1013
|
+
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
1014
|
+
const prefersSummaryByDefault = renderTargetType === "element" || renderTargetType === "webview";
|
|
1015
|
+
const autoSummaryKeyRef = useRef2(null);
|
|
1016
|
+
useEffect2(() => {
|
|
1017
|
+
if (!prefersSummaryByDefault) return;
|
|
1018
|
+
if (sidebarLeftTab !== "thumbnails") return;
|
|
1019
|
+
if (pageCount <= 0) return;
|
|
1020
|
+
const docKey = `${pageCount}:${outline.length}`;
|
|
1021
|
+
if (autoSummaryKeyRef.current === docKey) return;
|
|
1022
|
+
autoSummaryKeyRef.current = docKey;
|
|
1023
|
+
setSidebarLeftTab("summary");
|
|
1024
|
+
}, [
|
|
1025
|
+
prefersSummaryByDefault,
|
|
1026
|
+
sidebarLeftTab,
|
|
1027
|
+
pageCount,
|
|
1028
|
+
outline.length,
|
|
1029
|
+
setSidebarLeftTab
|
|
1030
|
+
]);
|
|
946
1031
|
if (!sidebarLeftOpen) return null;
|
|
947
1032
|
return /* @__PURE__ */ jsxs2(
|
|
948
1033
|
"div",
|
|
@@ -1056,8 +1141,16 @@ var SidebarLeft = ({ engine, style }) => {
|
|
|
1056
1141
|
accentColor,
|
|
1057
1142
|
active: currentPage === idx + 1,
|
|
1058
1143
|
onClick: () => {
|
|
1059
|
-
|
|
1060
|
-
|
|
1144
|
+
const page = idx + 1;
|
|
1145
|
+
engine.goToPage(page);
|
|
1146
|
+
if (prefersSummaryByDefault) {
|
|
1147
|
+
setDocumentState({
|
|
1148
|
+
currentPage: page,
|
|
1149
|
+
scrollToPageSignal: null
|
|
1150
|
+
});
|
|
1151
|
+
} else {
|
|
1152
|
+
triggerScrollToPage(idx);
|
|
1153
|
+
}
|
|
1061
1154
|
}
|
|
1062
1155
|
},
|
|
1063
1156
|
idx
|
|
@@ -1109,10 +1202,14 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1109
1202
|
} = useViewerStore3();
|
|
1110
1203
|
const [query, setQuery] = useState3("");
|
|
1111
1204
|
const [isSearching, setIsSearching] = useState3(false);
|
|
1112
|
-
const [contentDrafts, setContentDrafts] = useState3(
|
|
1205
|
+
const [contentDrafts, setContentDrafts] = useState3(
|
|
1206
|
+
{}
|
|
1207
|
+
);
|
|
1113
1208
|
const [replyDrafts, setReplyDrafts] = useState3({});
|
|
1114
1209
|
const searchService = new SearchService(engine);
|
|
1115
1210
|
const isDark = uiTheme === "dark";
|
|
1211
|
+
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
1212
|
+
const isSingleViewportMode = renderTargetType === "element" || renderTargetType === "webview";
|
|
1116
1213
|
const accentSoft = withAlpha2(accentColor, 0.12);
|
|
1117
1214
|
const resultsCount = searchResults.length;
|
|
1118
1215
|
const handleSearch = async (e) => {
|
|
@@ -1129,9 +1226,13 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1129
1226
|
const jumpToAnnotation = (annotation) => {
|
|
1130
1227
|
const page = annotation.pageIndex + 1;
|
|
1131
1228
|
engine.goToPage(page);
|
|
1132
|
-
|
|
1229
|
+
if (isSingleViewportMode) {
|
|
1230
|
+
setDocumentState({ currentPage: page, scrollToPageSignal: null });
|
|
1231
|
+
} else {
|
|
1232
|
+
setDocumentState({ currentPage: page });
|
|
1233
|
+
triggerScrollToPage(annotation.pageIndex);
|
|
1234
|
+
}
|
|
1133
1235
|
setSelectedAnnotation(annotation.id);
|
|
1134
|
-
triggerScrollToPage(annotation.pageIndex);
|
|
1135
1236
|
};
|
|
1136
1237
|
const getContentDraft = (annotation) => {
|
|
1137
1238
|
if (Object.prototype.hasOwnProperty.call(contentDrafts, annotation.id)) {
|
|
@@ -1278,11 +1379,19 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1278
1379
|
onClick: () => {
|
|
1279
1380
|
const page = res.pageIndex + 1;
|
|
1280
1381
|
engine.goToPage(page);
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1382
|
+
if (isSingleViewportMode) {
|
|
1383
|
+
setDocumentState({
|
|
1384
|
+
activeSearchIndex: idx,
|
|
1385
|
+
currentPage: page,
|
|
1386
|
+
scrollToPageSignal: null
|
|
1387
|
+
});
|
|
1388
|
+
} else {
|
|
1389
|
+
setDocumentState({
|
|
1390
|
+
activeSearchIndex: idx,
|
|
1391
|
+
currentPage: page
|
|
1392
|
+
});
|
|
1393
|
+
triggerScrollToPage(res.pageIndex);
|
|
1394
|
+
}
|
|
1286
1395
|
},
|
|
1287
1396
|
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"}`,
|
|
1288
1397
|
style: idx === activeSearchIndex ? {
|
|
@@ -1369,7 +1478,9 @@ var SidebarRight = ({ engine, style }) => {
|
|
|
1369
1478
|
const replies = ann.replies ?? [];
|
|
1370
1479
|
const contentDraft = getContentDraft(ann);
|
|
1371
1480
|
const replyDraft = getReplyDraft(ann.id);
|
|
1372
|
-
const hasExistingContent = Boolean(
|
|
1481
|
+
const hasExistingContent = Boolean(
|
|
1482
|
+
(ann.content ?? "").trim()
|
|
1483
|
+
);
|
|
1373
1484
|
return /* @__PURE__ */ jsxs3(
|
|
1374
1485
|
"div",
|
|
1375
1486
|
{
|
|
@@ -1531,6 +1642,7 @@ var PageRenderer = ({
|
|
|
1531
1642
|
engine,
|
|
1532
1643
|
pageIndex,
|
|
1533
1644
|
availableWidth,
|
|
1645
|
+
availableHeight,
|
|
1534
1646
|
onMeasuredSize
|
|
1535
1647
|
}) => {
|
|
1536
1648
|
const containerRef = useRef3(null);
|
|
@@ -1572,6 +1684,11 @@ var PageRenderer = ({
|
|
|
1572
1684
|
} = useViewerStore4();
|
|
1573
1685
|
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
1574
1686
|
const isElementRender = renderTargetType === "element";
|
|
1687
|
+
const isLandscape = typeof availableWidth === "number" && typeof availableHeight === "number" && availableWidth > availableHeight;
|
|
1688
|
+
const isLandscapeShort = isLandscape && typeof availableHeight === "number" && availableHeight <= 500;
|
|
1689
|
+
const isMobileElementViewport = isElementRender && typeof availableWidth === "number" && (availableWidth <= 768 || isLandscapeShort);
|
|
1690
|
+
const renderZoomDependency = isElementRender ? 1 : zoom;
|
|
1691
|
+
const renderRotationDependency = isElementRender ? 0 : rotation;
|
|
1575
1692
|
const textMarkupTools = /* @__PURE__ */ new Set([
|
|
1576
1693
|
"highlight",
|
|
1577
1694
|
"underline",
|
|
@@ -1601,6 +1718,10 @@ var PageRenderer = ({
|
|
|
1601
1718
|
},
|
|
1602
1719
|
[]
|
|
1603
1720
|
);
|
|
1721
|
+
useEffect3(() => {
|
|
1722
|
+
if (!isElementRender) return;
|
|
1723
|
+
setPageSize(null);
|
|
1724
|
+
}, [isElementRender, pageIndex]);
|
|
1604
1725
|
useEffect3(() => {
|
|
1605
1726
|
let active = true;
|
|
1606
1727
|
const loadSize = async () => {
|
|
@@ -1619,12 +1740,13 @@ var PageRenderer = ({
|
|
|
1619
1740
|
};
|
|
1620
1741
|
}, [engine, pageIndex]);
|
|
1621
1742
|
const fitScale = useMemo2(() => {
|
|
1743
|
+
if (isElementRender && isMobileElementViewport) return 1;
|
|
1622
1744
|
if (!availableWidth || !pageSize?.width) return 1;
|
|
1623
1745
|
const targetWidth = Math.max(0, availableWidth - 48);
|
|
1624
1746
|
if (!targetWidth) return 1;
|
|
1625
1747
|
const rawScale = Math.min(1, targetWidth / pageSize.width);
|
|
1626
1748
|
return Math.round(rawScale * SCALE_PRECISION) / SCALE_PRECISION;
|
|
1627
|
-
}, [availableWidth, pageSize]);
|
|
1749
|
+
}, [isElementRender, isMobileElementViewport, availableWidth, pageSize]);
|
|
1628
1750
|
const displaySize = useMemo2(() => {
|
|
1629
1751
|
if (!pageSize) return null;
|
|
1630
1752
|
const scale = zoom * fitScale;
|
|
@@ -1660,6 +1782,15 @@ var PageRenderer = ({
|
|
|
1660
1782
|
canvasRef.current.style.height = `${displaySize.height}px`;
|
|
1661
1783
|
}
|
|
1662
1784
|
await engine.renderPage(pageIndex, renderTarget, canvasRenderScale);
|
|
1785
|
+
const measuredSize = await engine.getPageDimensions(pageIndex);
|
|
1786
|
+
if (measuredSize.width > 0 && measuredSize.height > 0 && active) {
|
|
1787
|
+
setPageSize((prev) => {
|
|
1788
|
+
if (prev && prev.width === measuredSize.width && prev.height === measuredSize.height) {
|
|
1789
|
+
return prev;
|
|
1790
|
+
}
|
|
1791
|
+
return measuredSize;
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1663
1794
|
if (!isElementRender && !pageSize && canvasRef.current) {
|
|
1664
1795
|
const denom = canvasRenderScale * Math.max(zoom, 0.01);
|
|
1665
1796
|
if (denom > 0) {
|
|
@@ -1702,13 +1833,24 @@ var PageRenderer = ({
|
|
|
1702
1833
|
}, [
|
|
1703
1834
|
engine,
|
|
1704
1835
|
pageIndex,
|
|
1705
|
-
zoom,
|
|
1706
|
-
rotation,
|
|
1707
1836
|
isElementRender,
|
|
1837
|
+
availableWidth,
|
|
1708
1838
|
fitScale,
|
|
1709
1839
|
displaySize,
|
|
1710
|
-
pageSize
|
|
1840
|
+
pageSize,
|
|
1841
|
+
renderZoomDependency,
|
|
1842
|
+
renderRotationDependency
|
|
1711
1843
|
]);
|
|
1844
|
+
useEffect3(() => {
|
|
1845
|
+
if (!isElementRender || pageSize) return;
|
|
1846
|
+
const target = htmlLayerRef.current;
|
|
1847
|
+
if (!target) return;
|
|
1848
|
+
const measuredWidth = target.clientWidth || target.scrollWidth || 0;
|
|
1849
|
+
const measuredHeight = target.clientHeight || target.scrollHeight || 0;
|
|
1850
|
+
if (measuredWidth > 0 && measuredHeight > 0) {
|
|
1851
|
+
setPageSize({ width: measuredWidth, height: measuredHeight });
|
|
1852
|
+
}
|
|
1853
|
+
}, [isElementRender, pageSize, textLayerVersion]);
|
|
1712
1854
|
useEffect3(() => {
|
|
1713
1855
|
if (isElementRender) return;
|
|
1714
1856
|
const layer = textLayerRef.current;
|
|
@@ -1770,8 +1912,12 @@ var PageRenderer = ({
|
|
|
1770
1912
|
activeSearchIndex,
|
|
1771
1913
|
textLayerVersion
|
|
1772
1914
|
]);
|
|
1773
|
-
const
|
|
1774
|
-
const
|
|
1915
|
+
const getTouchPoint = (event) => {
|
|
1916
|
+
const touch = event.touches[0] ?? event.changedTouches[0];
|
|
1917
|
+
if (!touch) return null;
|
|
1918
|
+
return { x: touch.clientX, y: touch.clientY };
|
|
1919
|
+
};
|
|
1920
|
+
const handlePointerDown = (clientX, clientY, target) => {
|
|
1775
1921
|
const clickedInsideAnnotation = Boolean(
|
|
1776
1922
|
target?.closest("[data-papyrus-annotation-id]")
|
|
1777
1923
|
);
|
|
@@ -1785,8 +1931,8 @@ var PageRenderer = ({
|
|
|
1785
1931
|
if (activeTool === "ink") {
|
|
1786
1932
|
const rect2 = containerRef.current?.getBoundingClientRect();
|
|
1787
1933
|
if (!rect2) return;
|
|
1788
|
-
const x2 = (
|
|
1789
|
-
const y2 = (
|
|
1934
|
+
const x2 = (clientX - rect2.left) / rect2.width;
|
|
1935
|
+
const y2 = (clientY - rect2.top) / rect2.height;
|
|
1790
1936
|
setIsInkDrawing(true);
|
|
1791
1937
|
setInkPoints([{ x: x2, y: y2 }]);
|
|
1792
1938
|
return;
|
|
@@ -1795,25 +1941,25 @@ var PageRenderer = ({
|
|
|
1795
1941
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
1796
1942
|
if (!rect) return;
|
|
1797
1943
|
setIsDragging(true);
|
|
1798
|
-
const x =
|
|
1799
|
-
const y =
|
|
1944
|
+
const x = clientX - rect.left;
|
|
1945
|
+
const y = clientY - rect.top;
|
|
1800
1946
|
setStartPos({ x, y });
|
|
1801
1947
|
setCurrentRect({ x, y, w: 0, h: 0 });
|
|
1802
1948
|
};
|
|
1803
|
-
const
|
|
1949
|
+
const handlePointerMove = (clientX, clientY) => {
|
|
1804
1950
|
if (isInkDrawing) {
|
|
1805
1951
|
const rect2 = containerRef.current?.getBoundingClientRect();
|
|
1806
1952
|
if (!rect2) return;
|
|
1807
|
-
const x = (
|
|
1808
|
-
const y = (
|
|
1953
|
+
const x = (clientX - rect2.left) / rect2.width;
|
|
1954
|
+
const y = (clientY - rect2.top) / rect2.height;
|
|
1809
1955
|
setInkPoints((prev) => [...prev, { x, y }]);
|
|
1810
1956
|
return;
|
|
1811
1957
|
}
|
|
1812
1958
|
if (!isDragging) return;
|
|
1813
1959
|
const rect = containerRef.current?.getBoundingClientRect();
|
|
1814
1960
|
if (!rect) return;
|
|
1815
|
-
const currentX =
|
|
1816
|
-
const currentY =
|
|
1961
|
+
const currentX = clientX - rect.left;
|
|
1962
|
+
const currentY = clientY - rect.top;
|
|
1817
1963
|
setCurrentRect({
|
|
1818
1964
|
x: Math.min(startPos.x, currentX),
|
|
1819
1965
|
y: Math.min(startPos.y, currentY),
|
|
@@ -1821,7 +1967,7 @@ var PageRenderer = ({
|
|
|
1821
1967
|
h: Math.abs(currentY - startPos.y)
|
|
1822
1968
|
});
|
|
1823
1969
|
};
|
|
1824
|
-
const
|
|
1970
|
+
const handlePointerUp = () => {
|
|
1825
1971
|
if (isInkDrawing) {
|
|
1826
1972
|
setIsInkDrawing(false);
|
|
1827
1973
|
if (inkPoints.length > 1) {
|
|
@@ -1986,6 +2132,37 @@ var PageRenderer = ({
|
|
|
1986
2132
|
}
|
|
1987
2133
|
}
|
|
1988
2134
|
};
|
|
2135
|
+
const handleMouseDown = (e) => {
|
|
2136
|
+
handlePointerDown(e.clientX, e.clientY, e.target);
|
|
2137
|
+
};
|
|
2138
|
+
const handleMouseMove = (e) => {
|
|
2139
|
+
handlePointerMove(e.clientX, e.clientY);
|
|
2140
|
+
};
|
|
2141
|
+
const handleMouseUp = () => {
|
|
2142
|
+
handlePointerUp();
|
|
2143
|
+
};
|
|
2144
|
+
const handleTouchStart = (event) => {
|
|
2145
|
+
if (event.touches.length > 1) return;
|
|
2146
|
+
const point = getTouchPoint(event);
|
|
2147
|
+
if (!point) return;
|
|
2148
|
+
handlePointerDown(point.x, point.y, event.target);
|
|
2149
|
+
if ((activeTool === "ink" || !canSelectText) && event.cancelable) {
|
|
2150
|
+
event.preventDefault();
|
|
2151
|
+
}
|
|
2152
|
+
};
|
|
2153
|
+
const handleTouchMove = (event) => {
|
|
2154
|
+
if (event.touches.length > 1) return;
|
|
2155
|
+
const point = getTouchPoint(event);
|
|
2156
|
+
if (!point) return;
|
|
2157
|
+
handlePointerMove(point.x, point.y);
|
|
2158
|
+
if ((isInkDrawing || isDragging) && event.cancelable) {
|
|
2159
|
+
event.preventDefault();
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
const handleTouchEnd = (event) => {
|
|
2163
|
+
if (event.touches.length > 0) return;
|
|
2164
|
+
handlePointerUp();
|
|
2165
|
+
};
|
|
1989
2166
|
const getPageFilter = () => {
|
|
1990
2167
|
switch (pageTheme) {
|
|
1991
2168
|
case "sepia":
|
|
@@ -1998,15 +2175,35 @@ var PageRenderer = ({
|
|
|
1998
2175
|
return "none";
|
|
1999
2176
|
}
|
|
2000
2177
|
};
|
|
2178
|
+
const elementScale = zoom * fitScale;
|
|
2179
|
+
const elementBaseWidth = isElementRender ? isMobileElementViewport && availableWidth != null ? Math.max(260, Math.round(availableWidth)) : pageSize?.width ?? 640 : pageSize?.width ?? 640;
|
|
2180
|
+
const elementBaseHeight = pageSize?.height ?? (isElementRender ? 700 : 900);
|
|
2181
|
+
const elementContainerStyle = isElementRender ? {
|
|
2182
|
+
width: `${Math.max(1, Math.round(elementBaseWidth * elementScale))}px`,
|
|
2183
|
+
height: `${Math.max(
|
|
2184
|
+
1,
|
|
2185
|
+
Math.round(elementBaseHeight * elementScale)
|
|
2186
|
+
)}px`
|
|
2187
|
+
} : void 0;
|
|
2001
2188
|
return /* @__PURE__ */ jsxs4(
|
|
2002
2189
|
"div",
|
|
2003
2190
|
{
|
|
2004
2191
|
ref: containerRef,
|
|
2005
|
-
className: `relative inline-block shadow-2xl bg-white mb-10 ${canSelectText ? "" : "no-select cursor-crosshair"}`,
|
|
2006
|
-
style: {
|
|
2192
|
+
className: `relative inline-block shadow-2xl bg-white ${isMobileElementViewport ? "mb-0" : "mb-10"} ${canSelectText ? "" : "no-select cursor-crosshair"}`,
|
|
2193
|
+
style: {
|
|
2194
|
+
scrollMarginTop: "20px",
|
|
2195
|
+
minHeight: "100px",
|
|
2196
|
+
overflow: "hidden",
|
|
2197
|
+
...elementContainerStyle,
|
|
2198
|
+
touchAction: activeTool === "ink" || activeTool === "text" || activeTool === "comment" ? "none" : "auto"
|
|
2199
|
+
},
|
|
2007
2200
|
onMouseDown: handleMouseDown,
|
|
2008
2201
|
onMouseMove: handleMouseMove,
|
|
2009
2202
|
onMouseUp: handleMouseUp,
|
|
2203
|
+
onTouchStart: handleTouchStart,
|
|
2204
|
+
onTouchMove: handleTouchMove,
|
|
2205
|
+
onTouchEnd: handleTouchEnd,
|
|
2206
|
+
onTouchCancel: handleTouchEnd,
|
|
2010
2207
|
children: [
|
|
2011
2208
|
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..." }) }),
|
|
2012
2209
|
/* @__PURE__ */ jsx4(
|
|
@@ -2027,7 +2224,11 @@ var PageRenderer = ({
|
|
|
2027
2224
|
className: "block",
|
|
2028
2225
|
style: {
|
|
2029
2226
|
filter: getPageFilter(),
|
|
2030
|
-
display: isElementRender ? "block" : "none"
|
|
2227
|
+
display: isElementRender ? "block" : "none",
|
|
2228
|
+
width: `${elementBaseWidth}px`,
|
|
2229
|
+
height: `${elementBaseHeight}px`,
|
|
2230
|
+
transform: `scale(${elementScale})`,
|
|
2231
|
+
transformOrigin: "top left"
|
|
2031
2232
|
}
|
|
2032
2233
|
}
|
|
2033
2234
|
),
|
|
@@ -2477,14 +2678,26 @@ var PageRenderer_default = PageRenderer;
|
|
|
2477
2678
|
|
|
2478
2679
|
// components/Viewer.tsx
|
|
2479
2680
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2681
|
+
var withAlpha3 = (hex, alpha) => {
|
|
2682
|
+
const normalized = hex.replace("#", "").trim();
|
|
2683
|
+
const value = normalized.length === 3 ? normalized.split("").map((c) => c + c).join("") : normalized;
|
|
2684
|
+
if (value.length !== 6) return hex;
|
|
2685
|
+
const r = parseInt(value.slice(0, 2), 16);
|
|
2686
|
+
const g = parseInt(value.slice(2, 4), 16);
|
|
2687
|
+
const b = parseInt(value.slice(4, 6), 16);
|
|
2688
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
2689
|
+
};
|
|
2480
2690
|
var BASE_OVERSCAN = 6;
|
|
2481
2691
|
var MIN_ZOOM = 0.2;
|
|
2482
2692
|
var MAX_ZOOM = 5;
|
|
2483
2693
|
var WIDTH_SNAP_PX = 4;
|
|
2484
2694
|
var WIDTH_HYSTERESIS_PX = 6;
|
|
2695
|
+
var HEIGHT_SNAP_PX = 4;
|
|
2696
|
+
var HEIGHT_HYSTERESIS_PX = 6;
|
|
2485
2697
|
var MOBILE_HEADER_HIDE_DELTA_PX = 28;
|
|
2486
2698
|
var MOBILE_HEADER_SHOW_DELTA_PX = 16;
|
|
2487
2699
|
var MOBILE_HEADER_TOP_RESET_PX = 12;
|
|
2700
|
+
var MOBILE_LANDSCAPE_MAX_HEIGHT_PX2 = 500;
|
|
2488
2701
|
var Viewer = ({ engine, style }) => {
|
|
2489
2702
|
const viewerState = useViewerStore5();
|
|
2490
2703
|
const {
|
|
@@ -2495,6 +2708,7 @@ var Viewer = ({ engine, style }) => {
|
|
|
2495
2708
|
uiTheme,
|
|
2496
2709
|
scrollToPageSignal,
|
|
2497
2710
|
setDocumentState,
|
|
2711
|
+
triggerScrollToPage,
|
|
2498
2712
|
accentColor,
|
|
2499
2713
|
annotationColor,
|
|
2500
2714
|
setAnnotationColor,
|
|
@@ -2502,7 +2716,10 @@ var Viewer = ({ engine, style }) => {
|
|
|
2502
2716
|
} = viewerState;
|
|
2503
2717
|
const mobileTopbarVisible = viewerState.mobileTopbarVisible ?? true;
|
|
2504
2718
|
const isDark = uiTheme === "dark";
|
|
2719
|
+
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
2720
|
+
const isSingleViewportMode = renderTargetType === "element" || renderTargetType === "webview";
|
|
2505
2721
|
const viewerRef = useRef4(null);
|
|
2722
|
+
const singleNavInFlightRef = useRef4(false);
|
|
2506
2723
|
const colorPickerRef = useRef4(null);
|
|
2507
2724
|
const pageRefs = useRef4([]);
|
|
2508
2725
|
const intersectionRatiosRef = useRef4({});
|
|
@@ -2510,6 +2727,7 @@ var Viewer = ({ engine, style }) => {
|
|
|
2510
2727
|
const jumpRef = useRef4(false);
|
|
2511
2728
|
const jumpTimerRef = useRef4(null);
|
|
2512
2729
|
const lastWidthRef = useRef4(null);
|
|
2730
|
+
const lastHeightRef = useRef4(null);
|
|
2513
2731
|
const lastScrollTopRef = useRef4(0);
|
|
2514
2732
|
const scrollDownAccumulatorRef = useRef4(0);
|
|
2515
2733
|
const scrollUpAccumulatorRef = useRef4(0);
|
|
@@ -2523,12 +2741,16 @@ var Viewer = ({ engine, style }) => {
|
|
|
2523
2741
|
rafId: null
|
|
2524
2742
|
});
|
|
2525
2743
|
const [availableWidth, setAvailableWidth] = useState5(null);
|
|
2744
|
+
const [availableHeight, setAvailableHeight] = useState5(null);
|
|
2745
|
+
const [viewerBounds, setViewerBounds] = useState5(null);
|
|
2526
2746
|
const [basePageSize, setBasePageSize] = useState5(null);
|
|
2527
2747
|
const [pageSizes, setPageSizes] = useState5({});
|
|
2528
2748
|
const [colorPickerOpen, setColorPickerOpen] = useState5(false);
|
|
2529
|
-
const
|
|
2530
|
-
const
|
|
2531
|
-
const
|
|
2749
|
+
const isLandscape = availableWidth !== null && availableHeight !== null && availableWidth > availableHeight;
|
|
2750
|
+
const isLandscapeShort = isLandscape && availableHeight !== null && availableHeight <= MOBILE_LANDSCAPE_MAX_HEIGHT_PX2;
|
|
2751
|
+
const isCompact = availableWidth !== null && (availableWidth < 820 || isLandscapeShort);
|
|
2752
|
+
const isMobileViewport = availableWidth !== null && (availableWidth < 640 || isLandscapeShort);
|
|
2753
|
+
const paddingY = isSingleViewportMode && isMobileViewport ? "py-0" : isCompact ? "py-10" : "py-16";
|
|
2532
2754
|
const toolDockPosition = isCompact ? "bottom-4" : "bottom-8";
|
|
2533
2755
|
const colorPalette = [
|
|
2534
2756
|
"#fbbf24",
|
|
@@ -2540,6 +2762,45 @@ var Viewer = ({ engine, style }) => {
|
|
|
2540
2762
|
"#8b5cf6",
|
|
2541
2763
|
"#111827"
|
|
2542
2764
|
];
|
|
2765
|
+
const destinationNavEngine = engine;
|
|
2766
|
+
const canUseDestinationNavigation = isSingleViewportMode && typeof destinationNavEngine.goToAdjacentDestination === "function";
|
|
2767
|
+
const destinationNavigationState = isSingleViewportMode && typeof destinationNavEngine.getDestinationNavigationState === "function" ? destinationNavEngine.getDestinationNavigationState() : null;
|
|
2768
|
+
const canGoPrev = destinationNavigationState?.hasPrev ?? currentPage > 1;
|
|
2769
|
+
const canGoNext = destinationNavigationState?.hasNext ?? currentPage < pageCount;
|
|
2770
|
+
const viewerOverflowClass = isSingleViewportMode ? "overflow-hidden" : "overflow-y-scroll overflow-x-hidden";
|
|
2771
|
+
const navigateBy = (delta) => {
|
|
2772
|
+
if (pageCount <= 0) return;
|
|
2773
|
+
if (canUseDestinationNavigation) {
|
|
2774
|
+
if (singleNavInFlightRef.current) return;
|
|
2775
|
+
singleNavInFlightRef.current = true;
|
|
2776
|
+
void (async () => {
|
|
2777
|
+
try {
|
|
2778
|
+
const resolved = await destinationNavEngine.goToAdjacentDestination(
|
|
2779
|
+
delta
|
|
2780
|
+
);
|
|
2781
|
+
if (resolved == null) return;
|
|
2782
|
+
setDocumentState({
|
|
2783
|
+
currentPage: resolved + 1,
|
|
2784
|
+
scrollToPageSignal: null
|
|
2785
|
+
});
|
|
2786
|
+
} finally {
|
|
2787
|
+
singleNavInFlightRef.current = false;
|
|
2788
|
+
}
|
|
2789
|
+
})();
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const enginePage = Number(engine.getCurrentPage?.());
|
|
2793
|
+
const normalizedEnginePage = Number.isFinite(enginePage) && enginePage >= 1 ? Math.floor(enginePage) : null;
|
|
2794
|
+
const basePage = normalizedEnginePage != null && Math.abs(normalizedEnginePage - currentPage) <= 1 ? normalizedEnginePage : currentPage;
|
|
2795
|
+
const clampedPage = Math.max(1, Math.min(pageCount, basePage + delta));
|
|
2796
|
+
if (clampedPage === basePage) return;
|
|
2797
|
+
engine.goToPage(clampedPage);
|
|
2798
|
+
if (isSingleViewportMode) {
|
|
2799
|
+
setDocumentState({ currentPage: clampedPage, scrollToPageSignal: null });
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
triggerScrollToPage(clampedPage - 1);
|
|
2803
|
+
};
|
|
2543
2804
|
const setMobileTopbarVisibility = (visible) => {
|
|
2544
2805
|
if (mobileTopbarVisibleRef.current === visible) return;
|
|
2545
2806
|
mobileTopbarVisibleRef.current = visible;
|
|
@@ -2578,21 +2839,32 @@ var Viewer = ({ engine, style }) => {
|
|
|
2578
2839
|
const measurementTarget = viewerElement.parentElement ?? viewerElement;
|
|
2579
2840
|
let rafId = null;
|
|
2580
2841
|
const normalizeWidth = (rawWidth) => Math.max(0, Math.floor(rawWidth / WIDTH_SNAP_PX) * WIDTH_SNAP_PX);
|
|
2581
|
-
const
|
|
2842
|
+
const normalizeHeight = (rawHeight) => Math.max(0, Math.floor(rawHeight / HEIGHT_SNAP_PX) * HEIGHT_SNAP_PX);
|
|
2843
|
+
const updateSize = () => {
|
|
2582
2844
|
const rawWidth = measurementTarget.getBoundingClientRect?.().width ?? measurementTarget.clientWidth ?? measurementTarget.offsetWidth;
|
|
2845
|
+
const rawHeight = measurementTarget.getBoundingClientRect?.().height ?? measurementTarget.clientHeight ?? measurementTarget.offsetHeight;
|
|
2583
2846
|
const nextWidth = normalizeWidth(rawWidth);
|
|
2584
|
-
|
|
2847
|
+
const nextHeight = normalizeHeight(rawHeight);
|
|
2848
|
+
if (nextWidth <= 0 || nextHeight <= 0) return;
|
|
2585
2849
|
const previousWidth = lastWidthRef.current;
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2850
|
+
const previousHeight = lastHeightRef.current;
|
|
2851
|
+
const widthChanged = previousWidth == null || Math.abs(nextWidth - previousWidth) >= WIDTH_HYSTERESIS_PX;
|
|
2852
|
+
const heightChanged = previousHeight == null || Math.abs(nextHeight - previousHeight) >= HEIGHT_HYSTERESIS_PX;
|
|
2853
|
+
if (!widthChanged && !heightChanged) return;
|
|
2854
|
+
if (widthChanged) {
|
|
2855
|
+
lastWidthRef.current = nextWidth;
|
|
2856
|
+
setAvailableWidth(nextWidth);
|
|
2857
|
+
}
|
|
2858
|
+
if (heightChanged) {
|
|
2859
|
+
lastHeightRef.current = nextHeight;
|
|
2860
|
+
setAvailableHeight(nextHeight);
|
|
2861
|
+
}
|
|
2590
2862
|
};
|
|
2591
2863
|
const scheduleWidthUpdate = () => {
|
|
2592
2864
|
if (rafId != null) cancelAnimationFrame(rafId);
|
|
2593
2865
|
rafId = requestAnimationFrame(() => {
|
|
2594
2866
|
rafId = null;
|
|
2595
|
-
|
|
2867
|
+
updateSize();
|
|
2596
2868
|
});
|
|
2597
2869
|
};
|
|
2598
2870
|
scheduleWidthUpdate();
|
|
@@ -2615,10 +2887,48 @@ var Viewer = ({ engine, style }) => {
|
|
|
2615
2887
|
observer.disconnect();
|
|
2616
2888
|
};
|
|
2617
2889
|
}, []);
|
|
2890
|
+
useEffect4(() => {
|
|
2891
|
+
if (!isSingleViewportMode) return;
|
|
2892
|
+
const viewerElement = viewerRef.current;
|
|
2893
|
+
if (!viewerElement) return;
|
|
2894
|
+
let rafId = null;
|
|
2895
|
+
const updateBounds = () => {
|
|
2896
|
+
const rect = viewerElement.getBoundingClientRect();
|
|
2897
|
+
setViewerBounds({
|
|
2898
|
+
left: rect.left,
|
|
2899
|
+
width: rect.width,
|
|
2900
|
+
top: rect.top,
|
|
2901
|
+
height: rect.height
|
|
2902
|
+
});
|
|
2903
|
+
};
|
|
2904
|
+
const scheduleUpdate = () => {
|
|
2905
|
+
if (rafId != null) cancelAnimationFrame(rafId);
|
|
2906
|
+
rafId = requestAnimationFrame(() => {
|
|
2907
|
+
rafId = null;
|
|
2908
|
+
updateBounds();
|
|
2909
|
+
});
|
|
2910
|
+
};
|
|
2911
|
+
updateBounds();
|
|
2912
|
+
viewerElement.addEventListener("scroll", scheduleUpdate, { passive: true });
|
|
2913
|
+
window.addEventListener("resize", scheduleUpdate);
|
|
2914
|
+
window.addEventListener("scroll", scheduleUpdate, { passive: true });
|
|
2915
|
+
let observer = null;
|
|
2916
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
2917
|
+
observer = new ResizeObserver(() => scheduleUpdate());
|
|
2918
|
+
observer.observe(viewerElement);
|
|
2919
|
+
}
|
|
2920
|
+
return () => {
|
|
2921
|
+
if (rafId != null) cancelAnimationFrame(rafId);
|
|
2922
|
+
viewerElement.removeEventListener("scroll", scheduleUpdate);
|
|
2923
|
+
window.removeEventListener("resize", scheduleUpdate);
|
|
2924
|
+
window.removeEventListener("scroll", scheduleUpdate);
|
|
2925
|
+
observer?.disconnect();
|
|
2926
|
+
};
|
|
2927
|
+
}, [isSingleViewportMode]);
|
|
2618
2928
|
useEffect4(() => {
|
|
2619
2929
|
const root = viewerRef.current;
|
|
2620
2930
|
if (!root) return;
|
|
2621
|
-
if (!isMobileViewport) {
|
|
2931
|
+
if (isSingleViewportMode || !isMobileViewport) {
|
|
2622
2932
|
lastScrollTopRef.current = root.scrollTop;
|
|
2623
2933
|
scrollDownAccumulatorRef.current = 0;
|
|
2624
2934
|
scrollUpAccumulatorRef.current = 0;
|
|
@@ -2662,7 +2972,7 @@ var Viewer = ({ engine, style }) => {
|
|
|
2662
2972
|
return () => {
|
|
2663
2973
|
root.removeEventListener("scroll", handleScroll);
|
|
2664
2974
|
};
|
|
2665
|
-
}, [isMobileViewport, setDocumentState]);
|
|
2975
|
+
}, [isSingleViewportMode, isMobileViewport, setDocumentState]);
|
|
2666
2976
|
useEffect4(() => {
|
|
2667
2977
|
const previousPage = previousCurrentPageRef.current;
|
|
2668
2978
|
previousCurrentPageRef.current = currentPage;
|
|
@@ -2691,6 +3001,19 @@ var Viewer = ({ engine, style }) => {
|
|
|
2691
3001
|
}, [engine, pageCount]);
|
|
2692
3002
|
useEffect4(() => {
|
|
2693
3003
|
if (scrollToPageSignal == null) return;
|
|
3004
|
+
if (isSingleViewportMode) {
|
|
3005
|
+
const nextPageIndex = Math.max(
|
|
3006
|
+
0,
|
|
3007
|
+
Math.min(Math.max(pageCount - 1, 0), scrollToPageSignal)
|
|
3008
|
+
);
|
|
3009
|
+
const root2 = viewerRef.current;
|
|
3010
|
+
if (root2) root2.scrollTop = 0;
|
|
3011
|
+
setDocumentState({
|
|
3012
|
+
currentPage: nextPageIndex + 1,
|
|
3013
|
+
scrollToPageSignal: null
|
|
3014
|
+
});
|
|
3015
|
+
return;
|
|
3016
|
+
}
|
|
2694
3017
|
const root = viewerRef.current;
|
|
2695
3018
|
const target = pageRefs.current[scrollToPageSignal];
|
|
2696
3019
|
if (root) {
|
|
@@ -2727,16 +3050,57 @@ var Viewer = ({ engine, style }) => {
|
|
|
2727
3050
|
setDocumentState({ scrollToPageSignal: null });
|
|
2728
3051
|
}, [
|
|
2729
3052
|
scrollToPageSignal,
|
|
3053
|
+
isSingleViewportMode,
|
|
2730
3054
|
setDocumentState,
|
|
2731
3055
|
basePageSize,
|
|
2732
3056
|
availableWidth,
|
|
2733
3057
|
zoom,
|
|
2734
3058
|
pageCount
|
|
2735
3059
|
]);
|
|
3060
|
+
useEffect4(() => {
|
|
3061
|
+
if (!isSingleViewportMode) return;
|
|
3062
|
+
const root = viewerRef.current;
|
|
3063
|
+
if (!root) return;
|
|
3064
|
+
root.scrollTop = 0;
|
|
3065
|
+
}, [isSingleViewportMode, currentPage]);
|
|
2736
3066
|
useEffect4(() => {
|
|
2737
3067
|
setPageSizes({});
|
|
2738
3068
|
}, [zoom]);
|
|
2739
3069
|
useEffect4(() => {
|
|
3070
|
+
if (pageCount <= 1) return;
|
|
3071
|
+
const handleKeyNavigation = (event) => {
|
|
3072
|
+
if (event.defaultPrevented) return;
|
|
3073
|
+
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
|
3074
|
+
const target = event.target;
|
|
3075
|
+
if (target) {
|
|
3076
|
+
const tag = target.tagName;
|
|
3077
|
+
const isEditable = tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || target.isContentEditable || target.getAttribute("contenteditable") === "true";
|
|
3078
|
+
if (isEditable) return;
|
|
3079
|
+
}
|
|
3080
|
+
if (event.key === "ArrowLeft") {
|
|
3081
|
+
event.preventDefault();
|
|
3082
|
+
if (!canGoPrev) return;
|
|
3083
|
+
navigateBy(-1);
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
if (event.key === "ArrowRight") {
|
|
3087
|
+
event.preventDefault();
|
|
3088
|
+
if (!canGoNext) return;
|
|
3089
|
+
navigateBy(1);
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
3092
|
+
window.addEventListener("keydown", handleKeyNavigation);
|
|
3093
|
+
return () => window.removeEventListener("keydown", handleKeyNavigation);
|
|
3094
|
+
}, [
|
|
3095
|
+
currentPage,
|
|
3096
|
+
pageCount,
|
|
3097
|
+
triggerScrollToPage,
|
|
3098
|
+
engine,
|
|
3099
|
+
canGoPrev,
|
|
3100
|
+
canGoNext
|
|
3101
|
+
]);
|
|
3102
|
+
useEffect4(() => {
|
|
3103
|
+
if (isSingleViewportMode) return;
|
|
2740
3104
|
const root = viewerRef.current;
|
|
2741
3105
|
if (!root) return;
|
|
2742
3106
|
const flushCurrentPage = () => {
|
|
@@ -2782,11 +3146,15 @@ var Viewer = ({ engine, style }) => {
|
|
|
2782
3146
|
pageElements.forEach((el) => observer.unobserve(el));
|
|
2783
3147
|
observer.disconnect();
|
|
2784
3148
|
};
|
|
2785
|
-
}, [pageCount, setDocumentState, currentPage]);
|
|
3149
|
+
}, [pageCount, setDocumentState, currentPage, isSingleViewportMode]);
|
|
3150
|
+
const safeCurrentPageIndex = Math.max(
|
|
3151
|
+
0,
|
|
3152
|
+
Math.min(Math.max(pageCount - 1, 0), currentPage - 1)
|
|
3153
|
+
);
|
|
2786
3154
|
const virtualOverscan = zoom > 1.35 ? 4 : BASE_OVERSCAN;
|
|
2787
|
-
const virtualAnchor =
|
|
2788
|
-
const virtualStart = Math.max(0, virtualAnchor - virtualOverscan);
|
|
2789
|
-
const virtualEnd = Math.min(pageCount - 1, virtualAnchor + virtualOverscan);
|
|
3155
|
+
const virtualAnchor = safeCurrentPageIndex;
|
|
3156
|
+
const virtualStart = isSingleViewportMode ? safeCurrentPageIndex : Math.max(0, virtualAnchor - virtualOverscan);
|
|
3157
|
+
const virtualEnd = isSingleViewportMode ? safeCurrentPageIndex : Math.min(pageCount - 1, virtualAnchor + virtualOverscan);
|
|
2790
3158
|
const fallbackSize = useMemo3(() => {
|
|
2791
3159
|
if (basePageSize && availableWidth) {
|
|
2792
3160
|
const fitScale = Math.min(
|
|
@@ -2810,7 +3178,17 @@ var Viewer = ({ engine, style }) => {
|
|
|
2810
3178
|
return availableWidth ? Math.max(680, availableWidth * 1.3) : 1100;
|
|
2811
3179
|
return Math.round(heights.reduce((sum, h) => sum + h, 0) / heights.length);
|
|
2812
3180
|
}, [pageSizes, availableWidth]);
|
|
2813
|
-
const pages = Array.from({ length: pageCount }).map((_, i) => i);
|
|
3181
|
+
const pages = isSingleViewportMode ? pageCount > 0 ? [safeCurrentPageIndex] : [] : Array.from({ length: pageCount }).map((_, i) => i);
|
|
3182
|
+
const viewerStyle = useMemo3(
|
|
3183
|
+
() => isSingleViewportMode ? {
|
|
3184
|
+
...style ?? {},
|
|
3185
|
+
overflow: "hidden",
|
|
3186
|
+
overflowY: "hidden",
|
|
3187
|
+
overflowX: "hidden",
|
|
3188
|
+
overscrollBehavior: "none"
|
|
3189
|
+
} : style ?? {},
|
|
3190
|
+
[isSingleViewportMode, style]
|
|
3191
|
+
);
|
|
2814
3192
|
const handlePageMeasured = (pageIndex, size) => {
|
|
2815
3193
|
setPageSizes((prev) => {
|
|
2816
3194
|
const current = prev[pageIndex];
|
|
@@ -2903,8 +3281,8 @@ var Viewer = ({ engine, style }) => {
|
|
|
2903
3281
|
onTouchMove: handleTouchMove,
|
|
2904
3282
|
onTouchEnd: handleTouchEnd,
|
|
2905
3283
|
onTouchCancel: handleTouchEnd,
|
|
2906
|
-
className: `papyrus-viewer papyrus-theme min-w-0 w-full flex-1
|
|
2907
|
-
style,
|
|
3284
|
+
className: `papyrus-viewer papyrus-theme min-h-0 min-w-0 w-full flex-1 ${viewerOverflowClass} flex flex-col items-center ${paddingY} relative custom-scrollbar scroll-smooth ${isDark ? "bg-[#121212]" : "bg-[#e9ecef]"}`,
|
|
3285
|
+
style: viewerStyle,
|
|
2908
3286
|
children: [
|
|
2909
3287
|
/* @__PURE__ */ jsx5("div", { className: "flex flex-col items-center gap-6 w-full min-w-0", children: pages.map((idx) => /* @__PURE__ */ jsx5(
|
|
2910
3288
|
"div",
|
|
@@ -2913,13 +3291,14 @@ var Viewer = ({ engine, style }) => {
|
|
|
2913
3291
|
pageRefs.current[idx] = element;
|
|
2914
3292
|
},
|
|
2915
3293
|
"data-page-index": idx,
|
|
2916
|
-
className:
|
|
3294
|
+
className: `page-container ${isSingleViewportMode ? "relative" : ""}`,
|
|
2917
3295
|
children: idx >= virtualStart && idx <= virtualEnd ? /* @__PURE__ */ jsx5(
|
|
2918
3296
|
PageRenderer_default,
|
|
2919
3297
|
{
|
|
2920
3298
|
engine,
|
|
2921
3299
|
pageIndex: idx,
|
|
2922
3300
|
availableWidth: availableWidth ?? void 0,
|
|
3301
|
+
availableHeight: availableHeight ?? void 0,
|
|
2923
3302
|
onMeasuredSize: handlePageMeasured
|
|
2924
3303
|
}
|
|
2925
3304
|
) : /* @__PURE__ */ jsx5(
|
|
@@ -2933,8 +3312,94 @@ var Viewer = ({ engine, style }) => {
|
|
|
2933
3312
|
}
|
|
2934
3313
|
)
|
|
2935
3314
|
},
|
|
2936
|
-
idx
|
|
3315
|
+
isSingleViewportMode ? "single-viewport" : idx
|
|
2937
3316
|
)) }),
|
|
3317
|
+
isSingleViewportMode && pageCount > 1 && viewerBounds && /* @__PURE__ */ jsxs5(
|
|
3318
|
+
"div",
|
|
3319
|
+
{
|
|
3320
|
+
className: "pointer-events-none fixed z-[75] flex items-center justify-between px-1.5 sm:px-2.5",
|
|
3321
|
+
style: {
|
|
3322
|
+
left: viewerBounds.left,
|
|
3323
|
+
width: viewerBounds.width,
|
|
3324
|
+
top: viewerBounds.top + viewerBounds.height / 2,
|
|
3325
|
+
transform: "translateY(-50%)"
|
|
3326
|
+
},
|
|
3327
|
+
children: [
|
|
3328
|
+
/* @__PURE__ */ jsx5(
|
|
3329
|
+
"button",
|
|
3330
|
+
{
|
|
3331
|
+
onClick: () => navigateBy(-1),
|
|
3332
|
+
disabled: !canGoPrev,
|
|
3333
|
+
className: `pointer-events-auto h-12 w-9 sm:h-14 sm:w-10 rounded-lg border backdrop-blur-md transition-all ${!canGoPrev ? "opacity-40 cursor-not-allowed" : "hover:scale-[1.03] active:scale-95"} ${isDark ? "bg-[#111827]/85 text-gray-100" : "bg-white/90 text-gray-700"}`,
|
|
3334
|
+
style: {
|
|
3335
|
+
borderColor: withAlpha3(accentColor, isDark ? 0.45 : 0.3),
|
|
3336
|
+
color: !canGoPrev ? void 0 : accentColor,
|
|
3337
|
+
boxShadow: `0 10px 24px ${withAlpha3(
|
|
3338
|
+
accentColor,
|
|
3339
|
+
isDark ? 0.18 : 0.12
|
|
3340
|
+
)}`
|
|
3341
|
+
},
|
|
3342
|
+
"aria-label": "Cap\xEDtulo anterior",
|
|
3343
|
+
title: "Cap\xEDtulo anterior",
|
|
3344
|
+
children: /* @__PURE__ */ jsx5(
|
|
3345
|
+
"svg",
|
|
3346
|
+
{
|
|
3347
|
+
className: "w-5 h-5 mx-auto",
|
|
3348
|
+
fill: "none",
|
|
3349
|
+
stroke: "currentColor",
|
|
3350
|
+
viewBox: "0 0 24 24",
|
|
3351
|
+
children: /* @__PURE__ */ jsx5(
|
|
3352
|
+
"path",
|
|
3353
|
+
{
|
|
3354
|
+
strokeLinecap: "round",
|
|
3355
|
+
strokeLinejoin: "round",
|
|
3356
|
+
strokeWidth: 2,
|
|
3357
|
+
d: "M15 19l-7-7 7-7"
|
|
3358
|
+
}
|
|
3359
|
+
)
|
|
3360
|
+
}
|
|
3361
|
+
)
|
|
3362
|
+
}
|
|
3363
|
+
),
|
|
3364
|
+
/* @__PURE__ */ jsx5(
|
|
3365
|
+
"button",
|
|
3366
|
+
{
|
|
3367
|
+
onClick: () => navigateBy(1),
|
|
3368
|
+
disabled: !canGoNext,
|
|
3369
|
+
className: `pointer-events-auto h-12 w-9 sm:h-14 sm:w-10 rounded-lg border backdrop-blur-md transition-all ${!canGoNext ? "opacity-40 cursor-not-allowed" : "hover:scale-[1.03] active:scale-95"} ${isDark ? "bg-[#111827]/85 text-gray-100" : "bg-white/90 text-gray-700"}`,
|
|
3370
|
+
style: {
|
|
3371
|
+
borderColor: withAlpha3(accentColor, isDark ? 0.45 : 0.3),
|
|
3372
|
+
color: !canGoNext ? void 0 : accentColor,
|
|
3373
|
+
boxShadow: `0 10px 24px ${withAlpha3(
|
|
3374
|
+
accentColor,
|
|
3375
|
+
isDark ? 0.18 : 0.12
|
|
3376
|
+
)}`
|
|
3377
|
+
},
|
|
3378
|
+
"aria-label": "Pr\xF3ximo cap\xEDtulo",
|
|
3379
|
+
title: "Pr\xF3ximo cap\xEDtulo",
|
|
3380
|
+
children: /* @__PURE__ */ jsx5(
|
|
3381
|
+
"svg",
|
|
3382
|
+
{
|
|
3383
|
+
className: "w-5 h-5 mx-auto",
|
|
3384
|
+
fill: "none",
|
|
3385
|
+
stroke: "currentColor",
|
|
3386
|
+
viewBox: "0 0 24 24",
|
|
3387
|
+
children: /* @__PURE__ */ jsx5(
|
|
3388
|
+
"path",
|
|
3389
|
+
{
|
|
3390
|
+
strokeLinecap: "round",
|
|
3391
|
+
strokeLinejoin: "round",
|
|
3392
|
+
strokeWidth: 2,
|
|
3393
|
+
d: "M9 5l7 7-7 7"
|
|
3394
|
+
}
|
|
3395
|
+
)
|
|
3396
|
+
}
|
|
3397
|
+
)
|
|
3398
|
+
}
|
|
3399
|
+
)
|
|
3400
|
+
]
|
|
3401
|
+
}
|
|
3402
|
+
),
|
|
2938
3403
|
toolDockOpen && /* @__PURE__ */ jsx5(
|
|
2939
3404
|
"div",
|
|
2940
3405
|
{
|