@papyrus-sdk/ui-react-native 0.2.7 → 0.2.8
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 +26 -0
- package/dist/index.js +1356 -301
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1401 -320
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -575,12 +575,30 @@ https://github.com/nodeca/pako/blob/main/LICENSE\r
|
|
|
575
575
|
});
|
|
576
576
|
|
|
577
577
|
// components/Viewer.tsx
|
|
578
|
-
import {
|
|
579
|
-
|
|
578
|
+
import {
|
|
579
|
+
useMemo as useMemo3,
|
|
580
|
+
useRef as useRef3,
|
|
581
|
+
useEffect as useEffect3,
|
|
582
|
+
useCallback as useCallback2,
|
|
583
|
+
useState as useState2
|
|
584
|
+
} from "react";
|
|
585
|
+
import {
|
|
586
|
+
FlatList,
|
|
587
|
+
ScrollView as ScrollView2,
|
|
588
|
+
StyleSheet as StyleSheet3,
|
|
589
|
+
View as View3,
|
|
590
|
+
useWindowDimensions as useWindowDimensions2
|
|
591
|
+
} from "react-native";
|
|
580
592
|
import { useViewerStore as useViewerStore3 } from "@papyrus-sdk/core";
|
|
581
593
|
|
|
582
594
|
// components/PageRenderer.tsx
|
|
583
|
-
import {
|
|
595
|
+
import {
|
|
596
|
+
useCallback,
|
|
597
|
+
useEffect,
|
|
598
|
+
useMemo,
|
|
599
|
+
useRef,
|
|
600
|
+
useState
|
|
601
|
+
} from "react";
|
|
584
602
|
import {
|
|
585
603
|
View,
|
|
586
604
|
StyleSheet,
|
|
@@ -592,7 +610,193 @@ import {
|
|
|
592
610
|
useWindowDimensions
|
|
593
611
|
} from "react-native";
|
|
594
612
|
import { useViewerStore } from "@papyrus-sdk/core";
|
|
595
|
-
import {
|
|
613
|
+
import {
|
|
614
|
+
PapyrusPageView
|
|
615
|
+
} from "@papyrus-sdk/engine-native";
|
|
616
|
+
|
|
617
|
+
// perf/mobilePerf.ts
|
|
618
|
+
var DEFAULT_PREFIX = "[Papyrus Perf]";
|
|
619
|
+
var FRAME_BUDGET_MS = 1e3 / 60;
|
|
620
|
+
var getPerfGlobal = () => globalThis.__PAPYRUS_MOBILE_PERF__;
|
|
621
|
+
var getPerfConfig = () => {
|
|
622
|
+
const value = getPerfGlobal();
|
|
623
|
+
if (value === true) {
|
|
624
|
+
return {
|
|
625
|
+
enabled: true,
|
|
626
|
+
sampleMemory: true,
|
|
627
|
+
logPrefix: DEFAULT_PREFIX,
|
|
628
|
+
verbose: false
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
if (!value || typeof value !== "object") {
|
|
632
|
+
return {
|
|
633
|
+
enabled: false,
|
|
634
|
+
sampleMemory: false,
|
|
635
|
+
logPrefix: DEFAULT_PREFIX,
|
|
636
|
+
verbose: false
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
enabled: value.enabled ?? true,
|
|
641
|
+
sampleMemory: value.sampleMemory ?? true,
|
|
642
|
+
logPrefix: value.logPrefix ?? DEFAULT_PREFIX,
|
|
643
|
+
verbose: value.verbose ?? false
|
|
644
|
+
};
|
|
645
|
+
};
|
|
646
|
+
var round = (value) => Math.round(value * 100) / 100;
|
|
647
|
+
var bytesToMb = (bytes) => round(bytes / (1024 * 1024));
|
|
648
|
+
var getNumericValue = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
649
|
+
var readHermesHeapBytes = () => {
|
|
650
|
+
const runtimeProperties = globalThis.HermesInternal?.getRuntimeProperties?.();
|
|
651
|
+
if (!runtimeProperties || typeof runtimeProperties !== "object") return null;
|
|
652
|
+
const candidates = [
|
|
653
|
+
"JSHeapSize",
|
|
654
|
+
"js_heap_size",
|
|
655
|
+
"HeapSize",
|
|
656
|
+
"heapSize",
|
|
657
|
+
"TotalAllocatedBytes",
|
|
658
|
+
"totalAllocatedBytes",
|
|
659
|
+
"mallocSize"
|
|
660
|
+
];
|
|
661
|
+
for (const key of candidates) {
|
|
662
|
+
const value = getNumericValue(runtimeProperties[key]);
|
|
663
|
+
if (value !== null) return value;
|
|
664
|
+
}
|
|
665
|
+
return null;
|
|
666
|
+
};
|
|
667
|
+
var logPerf = (scope, event, payload) => {
|
|
668
|
+
const config = getPerfConfig();
|
|
669
|
+
if (!config.enabled) return;
|
|
670
|
+
const line = `${config.logPrefix}[${scope}] ${event}`;
|
|
671
|
+
if (payload) {
|
|
672
|
+
console.log(line, payload);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
console.log(line);
|
|
676
|
+
};
|
|
677
|
+
var isMobilePerfEnabled = () => getPerfConfig().enabled;
|
|
678
|
+
var perfNow = () => {
|
|
679
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
680
|
+
return performance.now();
|
|
681
|
+
}
|
|
682
|
+
return Date.now();
|
|
683
|
+
};
|
|
684
|
+
var sampleMemory = (scope, event, payload) => {
|
|
685
|
+
const config = getPerfConfig();
|
|
686
|
+
if (!config.enabled || !config.sampleMemory) return;
|
|
687
|
+
const performanceMemory = globalThis.performance?.memory;
|
|
688
|
+
const jsHeapUsedBytes = getNumericValue(performanceMemory?.usedJSHeapSize);
|
|
689
|
+
const jsHeapTotalBytes = getNumericValue(performanceMemory?.totalJSHeapSize);
|
|
690
|
+
const hermesHeapBytes = readHermesHeapBytes();
|
|
691
|
+
if (jsHeapUsedBytes === null && jsHeapTotalBytes === null && hermesHeapBytes === null) return;
|
|
692
|
+
logPerf(scope, `memory.${event}`, {
|
|
693
|
+
...payload,
|
|
694
|
+
jsHeapUsedMb: jsHeapUsedBytes === null ? void 0 : bytesToMb(jsHeapUsedBytes),
|
|
695
|
+
jsHeapTotalMb: jsHeapTotalBytes === null ? void 0 : bytesToMb(jsHeapTotalBytes),
|
|
696
|
+
hermesHeapMb: hermesHeapBytes === null ? void 0 : bytesToMb(hermesHeapBytes)
|
|
697
|
+
});
|
|
698
|
+
};
|
|
699
|
+
var createBurstMonitor = (scope, label, threshold = 12, windowMs = 1e3) => {
|
|
700
|
+
let windowStart = 0;
|
|
701
|
+
let calls = 0;
|
|
702
|
+
return (payload) => {
|
|
703
|
+
const config = getPerfConfig();
|
|
704
|
+
if (!config.enabled) return;
|
|
705
|
+
const now = perfNow();
|
|
706
|
+
if (windowStart === 0 || now - windowStart > windowMs) {
|
|
707
|
+
windowStart = now;
|
|
708
|
+
calls = 0;
|
|
709
|
+
}
|
|
710
|
+
calls += 1;
|
|
711
|
+
if (calls === threshold || config.verbose) {
|
|
712
|
+
logPerf(scope, `${label}.burst`, {
|
|
713
|
+
calls,
|
|
714
|
+
windowMs: round(now - windowStart),
|
|
715
|
+
...payload
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
};
|
|
720
|
+
var createRenderCounter = (scope, label = "render", reportEvery = 30) => {
|
|
721
|
+
let count = 0;
|
|
722
|
+
return (payload) => {
|
|
723
|
+
const config = getPerfConfig();
|
|
724
|
+
if (!config.enabled) return;
|
|
725
|
+
count += 1;
|
|
726
|
+
if (count === 1 || count % reportEvery === 0 || config.verbose) {
|
|
727
|
+
logPerf(scope, label, {
|
|
728
|
+
count,
|
|
729
|
+
...payload
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
};
|
|
734
|
+
var createScrollPerfMonitor = (scope, label = "scroll") => {
|
|
735
|
+
let active = false;
|
|
736
|
+
let startAt = 0;
|
|
737
|
+
let lastEventAt = 0;
|
|
738
|
+
let sampleEvents = 0;
|
|
739
|
+
let droppedFrames = 0;
|
|
740
|
+
let maxFrameGapMs = 0;
|
|
741
|
+
const reset = () => {
|
|
742
|
+
active = false;
|
|
743
|
+
startAt = 0;
|
|
744
|
+
lastEventAt = 0;
|
|
745
|
+
sampleEvents = 0;
|
|
746
|
+
droppedFrames = 0;
|
|
747
|
+
maxFrameGapMs = 0;
|
|
748
|
+
};
|
|
749
|
+
return {
|
|
750
|
+
begin: (reason, payload) => {
|
|
751
|
+
const config = getPerfConfig();
|
|
752
|
+
if (!config.enabled) return;
|
|
753
|
+
if (active) return;
|
|
754
|
+
active = true;
|
|
755
|
+
startAt = perfNow();
|
|
756
|
+
if (reason && config.verbose) {
|
|
757
|
+
logPerf(scope, `${label}.begin`, {
|
|
758
|
+
reason,
|
|
759
|
+
...payload
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
track: (timestampMs) => {
|
|
764
|
+
if (!isMobilePerfEnabled()) return;
|
|
765
|
+
const now = typeof timestampMs === "number" && Number.isFinite(timestampMs) ? timestampMs : perfNow();
|
|
766
|
+
if (!active) {
|
|
767
|
+
active = true;
|
|
768
|
+
startAt = now;
|
|
769
|
+
}
|
|
770
|
+
if (lastEventAt > 0) {
|
|
771
|
+
const frameGap = Math.max(0, now - lastEventAt);
|
|
772
|
+
maxFrameGapMs = Math.max(maxFrameGapMs, frameGap);
|
|
773
|
+
droppedFrames += Math.max(0, Math.round(frameGap / FRAME_BUDGET_MS) - 1);
|
|
774
|
+
}
|
|
775
|
+
sampleEvents += 1;
|
|
776
|
+
lastEventAt = now;
|
|
777
|
+
},
|
|
778
|
+
end: (reason, payload) => {
|
|
779
|
+
if (!isMobilePerfEnabled() || !active) return;
|
|
780
|
+
const stopAt = lastEventAt || perfNow();
|
|
781
|
+
const durationMs = Math.max(0, stopAt - startAt);
|
|
782
|
+
const estimatedFrameTotal = Math.max(sampleEvents + droppedFrames, 1);
|
|
783
|
+
const fpsEstimate = durationMs > 0 ? sampleEvents * 1e3 / durationMs : 0;
|
|
784
|
+
logPerf(scope, `${label}.${reason}`, {
|
|
785
|
+
durationMs: round(durationMs),
|
|
786
|
+
sampleEvents,
|
|
787
|
+
droppedFrames,
|
|
788
|
+
droppedFramesPct: round(droppedFrames / estimatedFrameTotal * 100),
|
|
789
|
+
fpsEstimate: round(fpsEstimate),
|
|
790
|
+
maxFrameGapMs: round(maxFrameGapMs),
|
|
791
|
+
...payload
|
|
792
|
+
});
|
|
793
|
+
reset();
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
};
|
|
797
|
+
var logPerfEvent = logPerf;
|
|
798
|
+
|
|
799
|
+
// components/PageRenderer.tsx
|
|
596
800
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
597
801
|
var PageRenderer = ({
|
|
598
802
|
engine,
|
|
@@ -609,6 +813,11 @@ var PageRenderer = ({
|
|
|
609
813
|
const { width: windowWidth } = useWindowDimensions();
|
|
610
814
|
const isAndroid = Platform.OS === "android";
|
|
611
815
|
const isNative = Platform.OS === "android" || Platform.OS === "ios";
|
|
816
|
+
const perfEnabled = isMobilePerfEnabled();
|
|
817
|
+
const renderCountRef = useRef(0);
|
|
818
|
+
const setStateBurstRef = useRef(
|
|
819
|
+
createBurstMonitor("PageRenderer", "setDocumentState", 18, 700)
|
|
820
|
+
);
|
|
612
821
|
const {
|
|
613
822
|
zoom,
|
|
614
823
|
rotation,
|
|
@@ -625,6 +834,19 @@ var PageRenderer = ({
|
|
|
625
834
|
activeSearchIndex,
|
|
626
835
|
setSelectionActive
|
|
627
836
|
} = useViewerStore();
|
|
837
|
+
const setDocumentStateTracked = useCallback(
|
|
838
|
+
(state, reason) => {
|
|
839
|
+
if (perfEnabled) {
|
|
840
|
+
setStateBurstRef.current({
|
|
841
|
+
reason,
|
|
842
|
+
page: pageIndex + 1,
|
|
843
|
+
keys: Object.keys(state).join(",")
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
setDocumentState(state);
|
|
847
|
+
},
|
|
848
|
+
[pageIndex, perfEnabled, setDocumentState]
|
|
849
|
+
);
|
|
628
850
|
const pageAnnotations = useMemo(
|
|
629
851
|
() => annotations.filter((ann) => ann.pageIndex === pageIndex),
|
|
630
852
|
[annotations, pageIndex]
|
|
@@ -633,6 +855,17 @@ var PageRenderer = ({
|
|
|
633
855
|
() => searchResults.map((result, index) => ({ result, index })).filter(({ result }) => result.pageIndex === pageIndex),
|
|
634
856
|
[searchResults, pageIndex]
|
|
635
857
|
);
|
|
858
|
+
renderCountRef.current += 1;
|
|
859
|
+
if (perfEnabled && (renderCountRef.current === 1 || renderCountRef.current % 20 === 0)) {
|
|
860
|
+
logPerfEvent("PageRenderer", "render", {
|
|
861
|
+
page: pageIndex + 1,
|
|
862
|
+
renderCount: renderCountRef.current,
|
|
863
|
+
zoom,
|
|
864
|
+
rotation,
|
|
865
|
+
annotationCount: pageAnnotations.length,
|
|
866
|
+
searchHits: pageSearchHits.length
|
|
867
|
+
});
|
|
868
|
+
}
|
|
636
869
|
const [selectionRect, setSelectionRect] = useState(null);
|
|
637
870
|
const [selectionRects, setSelectionRects] = useState([]);
|
|
638
871
|
const [selectionBounds, setSelectionBounds] = useState(null);
|
|
@@ -642,30 +875,72 @@ var PageRenderer = ({
|
|
|
642
875
|
const selectionRectRef = useRef(null);
|
|
643
876
|
const selectionBoundsRef = useRef(null);
|
|
644
877
|
const selectionBoundsStart = useRef(null);
|
|
645
|
-
const lastTapRef = useRef(
|
|
878
|
+
const lastTapRef = useRef(
|
|
879
|
+
null
|
|
880
|
+
);
|
|
646
881
|
const pinchRef = useRef(null);
|
|
647
882
|
useEffect(() => {
|
|
648
883
|
if (!layout.width || !layout.height) return;
|
|
649
884
|
const viewTag = findNodeHandle(viewRef.current);
|
|
650
885
|
if (viewTag) {
|
|
651
886
|
const renderScale = isNative ? scale / Math.max(zoom, 0.5) : scale;
|
|
652
|
-
|
|
887
|
+
const startedAt = perfEnabled ? perfNow() : 0;
|
|
888
|
+
void Promise.resolve(engine.renderPage(pageIndex, viewTag, renderScale)).then(() => {
|
|
889
|
+
if (!perfEnabled) return;
|
|
890
|
+
const renderDurationMs = perfNow() - startedAt;
|
|
891
|
+
if (renderDurationMs >= 40) {
|
|
892
|
+
logPerfEvent("PageRenderer", "renderPage.slow", {
|
|
893
|
+
page: pageIndex + 1,
|
|
894
|
+
renderDurationMs: Math.round(renderDurationMs * 100) / 100,
|
|
895
|
+
layoutWidth: layout.width,
|
|
896
|
+
layoutHeight: layout.height,
|
|
897
|
+
renderScale: Math.round(renderScale * 100) / 100
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
}).catch((error) => {
|
|
901
|
+
logPerfEvent("PageRenderer", "renderPage.error", {
|
|
902
|
+
page: pageIndex + 1,
|
|
903
|
+
message: error instanceof Error ? error.message : String(error)
|
|
904
|
+
});
|
|
905
|
+
});
|
|
653
906
|
}
|
|
654
|
-
}, [
|
|
907
|
+
}, [
|
|
908
|
+
engine,
|
|
909
|
+
pageIndex,
|
|
910
|
+
scale,
|
|
911
|
+
zoom,
|
|
912
|
+
rotation,
|
|
913
|
+
layout.width,
|
|
914
|
+
layout.height,
|
|
915
|
+
isNative,
|
|
916
|
+
perfEnabled
|
|
917
|
+
]);
|
|
655
918
|
useEffect(() => {
|
|
656
919
|
let active = true;
|
|
657
920
|
const loadDimensions = async () => {
|
|
921
|
+
const startedAt = perfEnabled ? perfNow() : 0;
|
|
658
922
|
const dims = await engine.getPageDimensions(pageIndex);
|
|
659
923
|
if (!active) return;
|
|
660
924
|
if (dims.width > 0 && dims.height > 0) {
|
|
661
925
|
setPageSize({ width: dims.width, height: dims.height });
|
|
662
926
|
}
|
|
927
|
+
if (perfEnabled) {
|
|
928
|
+
const durationMs = perfNow() - startedAt;
|
|
929
|
+
if (durationMs >= 20 || pageIndex === 0) {
|
|
930
|
+
logPerfEvent("PageRenderer", "pageDimensions", {
|
|
931
|
+
page: pageIndex + 1,
|
|
932
|
+
durationMs: Math.round(durationMs * 100) / 100,
|
|
933
|
+
width: dims.width,
|
|
934
|
+
height: dims.height
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
}
|
|
663
938
|
};
|
|
664
939
|
void loadDimensions();
|
|
665
940
|
return () => {
|
|
666
941
|
active = false;
|
|
667
942
|
};
|
|
668
|
-
}, [engine, pageIndex]);
|
|
943
|
+
}, [engine, pageIndex, perfEnabled]);
|
|
669
944
|
const handleLayout = (event) => {
|
|
670
945
|
const { width, height } = event.nativeEvent.layout;
|
|
671
946
|
if (width !== layout.width || height !== layout.height) {
|
|
@@ -765,7 +1040,7 @@ var PageRenderer = ({
|
|
|
765
1040
|
if (!distance) return;
|
|
766
1041
|
const scale2 = distance / pinchRef.current.distance;
|
|
767
1042
|
const nextZoom = clamp(pinchRef.current.zoom * scale2, 0.5, 4);
|
|
768
|
-
|
|
1043
|
+
setDocumentStateTracked({ zoom: nextZoom }, "pinchMove");
|
|
769
1044
|
engine.setZoom(nextZoom);
|
|
770
1045
|
};
|
|
771
1046
|
const handlePinchEnd = () => {
|
|
@@ -813,7 +1088,12 @@ var PageRenderer = ({
|
|
|
813
1088
|
const top = Math.max(0, Math.min(start.y, currentY));
|
|
814
1089
|
const right = Math.min(layout.width, Math.max(start.x, currentX));
|
|
815
1090
|
const bottom = Math.min(layout.height, Math.max(start.y, currentY));
|
|
816
|
-
const rect = {
|
|
1091
|
+
const rect = {
|
|
1092
|
+
x: left,
|
|
1093
|
+
y: top,
|
|
1094
|
+
width: right - left,
|
|
1095
|
+
height: bottom - top
|
|
1096
|
+
};
|
|
817
1097
|
selectionRectRef.current = rect;
|
|
818
1098
|
setSelectionRect(rect);
|
|
819
1099
|
},
|
|
@@ -909,7 +1189,13 @@ var PageRenderer = ({
|
|
|
909
1189
|
if (selectionRects.length === 0) return;
|
|
910
1190
|
if (type === "comment") {
|
|
911
1191
|
const first = selectionRects[0];
|
|
912
|
-
addAnnotationAt(
|
|
1192
|
+
addAnnotationAt(
|
|
1193
|
+
first.x,
|
|
1194
|
+
first.y,
|
|
1195
|
+
Math.max(0.08, first.width),
|
|
1196
|
+
Math.max(0.06, first.height),
|
|
1197
|
+
"comment"
|
|
1198
|
+
);
|
|
913
1199
|
clearSelection();
|
|
914
1200
|
return;
|
|
915
1201
|
}
|
|
@@ -943,12 +1229,18 @@ var PageRenderer = ({
|
|
|
943
1229
|
horizontal: true,
|
|
944
1230
|
scrollEnabled,
|
|
945
1231
|
showsHorizontalScrollIndicator: false,
|
|
946
|
-
contentContainerStyle: [
|
|
1232
|
+
contentContainerStyle: [
|
|
1233
|
+
styles.scrollContent,
|
|
1234
|
+
{ paddingHorizontal: horizontalPadding }
|
|
1235
|
+
],
|
|
947
1236
|
children: /* @__PURE__ */ jsxs(
|
|
948
1237
|
Pressable,
|
|
949
1238
|
{
|
|
950
1239
|
...panResponder.panHandlers,
|
|
951
|
-
style: [
|
|
1240
|
+
style: [
|
|
1241
|
+
styles.container,
|
|
1242
|
+
{ width: pageWidth, height: pageHeight, marginBottom: spacing }
|
|
1243
|
+
],
|
|
952
1244
|
onLayout: handleLayout,
|
|
953
1245
|
onPress: handlePress,
|
|
954
1246
|
onStartShouldSetResponder: (event) => shouldHandlePinch(event.nativeEvent.touches),
|
|
@@ -959,7 +1251,13 @@ var PageRenderer = ({
|
|
|
959
1251
|
onResponderTerminate: handlePinchEnd,
|
|
960
1252
|
children: [
|
|
961
1253
|
/* @__PURE__ */ jsx(PageViewComponent, { ref: viewRef, style: styles.page }),
|
|
962
|
-
/* @__PURE__ */ jsx(
|
|
1254
|
+
/* @__PURE__ */ jsx(
|
|
1255
|
+
View,
|
|
1256
|
+
{
|
|
1257
|
+
pointerEvents: "none",
|
|
1258
|
+
style: [styles.themeOverlay, themeOverlayStyle]
|
|
1259
|
+
}
|
|
1260
|
+
),
|
|
963
1261
|
/* @__PURE__ */ jsxs(View, { pointerEvents: "box-none", style: styles.selectionLayer, children: [
|
|
964
1262
|
/* @__PURE__ */ jsx(View, { pointerEvents: "none", children: selectionRects.map((rect, index) => {
|
|
965
1263
|
const style = {
|
|
@@ -968,7 +1266,13 @@ var PageRenderer = ({
|
|
|
968
1266
|
width: `${rect.width * 100}%`,
|
|
969
1267
|
height: `${rect.height * 100}%`
|
|
970
1268
|
};
|
|
971
|
-
return /* @__PURE__ */ jsx(
|
|
1269
|
+
return /* @__PURE__ */ jsx(
|
|
1270
|
+
View,
|
|
1271
|
+
{
|
|
1272
|
+
style: [styles.selectionHighlight, style]
|
|
1273
|
+
},
|
|
1274
|
+
`sel-${index}`
|
|
1275
|
+
);
|
|
972
1276
|
}) }),
|
|
973
1277
|
selectionBoundsPx ? /* @__PURE__ */ jsxs(
|
|
974
1278
|
View,
|
|
@@ -989,14 +1293,20 @@ var PageRenderer = ({
|
|
|
989
1293
|
View,
|
|
990
1294
|
{
|
|
991
1295
|
...startHandleResponder.panHandlers,
|
|
992
|
-
style: [
|
|
1296
|
+
style: [
|
|
1297
|
+
styles.selectionHandle,
|
|
1298
|
+
{ left: -8, top: -8, borderColor: accentColor }
|
|
1299
|
+
]
|
|
993
1300
|
}
|
|
994
1301
|
),
|
|
995
1302
|
/* @__PURE__ */ jsx(
|
|
996
1303
|
View,
|
|
997
1304
|
{
|
|
998
1305
|
...endHandleResponder.panHandlers,
|
|
999
|
-
style: [
|
|
1306
|
+
style: [
|
|
1307
|
+
styles.selectionHandle,
|
|
1308
|
+
{ right: -8, bottom: -8, borderColor: accentColor }
|
|
1309
|
+
]
|
|
1000
1310
|
}
|
|
1001
1311
|
)
|
|
1002
1312
|
]
|
|
@@ -1035,9 +1345,15 @@ var PageRenderer = ({
|
|
|
1035
1345
|
{
|
|
1036
1346
|
style: [
|
|
1037
1347
|
styles.searchHighlight,
|
|
1038
|
-
{
|
|
1348
|
+
{
|
|
1349
|
+
borderColor: accentColor,
|
|
1350
|
+
backgroundColor: `${accentColor}26`
|
|
1351
|
+
},
|
|
1039
1352
|
isActive && styles.searchHighlightActive,
|
|
1040
|
-
isActive && {
|
|
1353
|
+
isActive && {
|
|
1354
|
+
borderColor: accentColor,
|
|
1355
|
+
backgroundColor: `${accentColor}40`
|
|
1356
|
+
},
|
|
1041
1357
|
highlightStyle
|
|
1042
1358
|
]
|
|
1043
1359
|
},
|
|
@@ -1067,8 +1383,29 @@ var PageRenderer = ({
|
|
|
1067
1383
|
isSelected && { borderColor: accentColor }
|
|
1068
1384
|
],
|
|
1069
1385
|
children: [
|
|
1070
|
-
(ann.type === "comment" || ann.type === "text") && /* @__PURE__ */ jsx(
|
|
1071
|
-
|
|
1386
|
+
(ann.type === "comment" || ann.type === "text") && /* @__PURE__ */ jsx(
|
|
1387
|
+
View,
|
|
1388
|
+
{
|
|
1389
|
+
style: [styles.annotationBadge, { borderColor: ann.color }],
|
|
1390
|
+
children: /* @__PURE__ */ jsx(
|
|
1391
|
+
View,
|
|
1392
|
+
{
|
|
1393
|
+
style: [
|
|
1394
|
+
styles.annotationDot,
|
|
1395
|
+
{ backgroundColor: ann.color }
|
|
1396
|
+
]
|
|
1397
|
+
}
|
|
1398
|
+
)
|
|
1399
|
+
}
|
|
1400
|
+
),
|
|
1401
|
+
isSelected && /* @__PURE__ */ jsx(
|
|
1402
|
+
Pressable,
|
|
1403
|
+
{
|
|
1404
|
+
onPress: () => removeAnnotation(ann.id),
|
|
1405
|
+
style: styles.deleteButton,
|
|
1406
|
+
children: /* @__PURE__ */ jsx(View, { style: styles.deleteDot })
|
|
1407
|
+
}
|
|
1408
|
+
)
|
|
1072
1409
|
]
|
|
1073
1410
|
},
|
|
1074
1411
|
ann.id
|
|
@@ -1086,9 +1423,46 @@ var PageRenderer = ({
|
|
|
1086
1423
|
}
|
|
1087
1424
|
],
|
|
1088
1425
|
children: [
|
|
1089
|
-
/* @__PURE__ */ jsx(
|
|
1090
|
-
|
|
1091
|
-
|
|
1426
|
+
/* @__PURE__ */ jsx(
|
|
1427
|
+
Pressable,
|
|
1428
|
+
{
|
|
1429
|
+
onPress: () => applySelection("comment"),
|
|
1430
|
+
style: styles.selectionAction,
|
|
1431
|
+
children: /* @__PURE__ */ jsx(View, { style: styles.selectionActionDot })
|
|
1432
|
+
}
|
|
1433
|
+
),
|
|
1434
|
+
/* @__PURE__ */ jsx(
|
|
1435
|
+
Pressable,
|
|
1436
|
+
{
|
|
1437
|
+
onPress: () => applySelection("highlight"),
|
|
1438
|
+
style: styles.selectionAction,
|
|
1439
|
+
children: /* @__PURE__ */ jsx(
|
|
1440
|
+
View,
|
|
1441
|
+
{
|
|
1442
|
+
style: [
|
|
1443
|
+
styles.selectionSwatch,
|
|
1444
|
+
{ backgroundColor: annotationColor }
|
|
1445
|
+
]
|
|
1446
|
+
}
|
|
1447
|
+
)
|
|
1448
|
+
}
|
|
1449
|
+
),
|
|
1450
|
+
/* @__PURE__ */ jsx(
|
|
1451
|
+
Pressable,
|
|
1452
|
+
{
|
|
1453
|
+
onPress: () => applySelection("strikeout"),
|
|
1454
|
+
style: [styles.selectionAction, styles.selectionActionLast],
|
|
1455
|
+
children: /* @__PURE__ */ jsx(
|
|
1456
|
+
View,
|
|
1457
|
+
{
|
|
1458
|
+
style: [
|
|
1459
|
+
styles.selectionStrike,
|
|
1460
|
+
{ backgroundColor: annotationColor }
|
|
1461
|
+
]
|
|
1462
|
+
}
|
|
1463
|
+
)
|
|
1464
|
+
}
|
|
1465
|
+
)
|
|
1092
1466
|
]
|
|
1093
1467
|
}
|
|
1094
1468
|
) : null
|
|
@@ -1386,8 +1760,27 @@ var WebViewViewer_default = WebViewViewer;
|
|
|
1386
1760
|
|
|
1387
1761
|
// components/Viewer.tsx
|
|
1388
1762
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1763
|
+
var LIST_TOP_PADDING = 18;
|
|
1764
|
+
var LIST_BOTTOM_PADDING = 120;
|
|
1765
|
+
var CONTINUOUS_PAGE_SPACING = 28;
|
|
1766
|
+
var DOUBLE_PAGE_SPACING = 20;
|
|
1767
|
+
var DEFAULT_PAGE_ASPECT_RATIO = 0.77;
|
|
1768
|
+
var FLATLIST_WINDOW_SIZE = 8;
|
|
1769
|
+
var FLATLIST_MAX_TO_RENDER_PER_BATCH = 6;
|
|
1770
|
+
var FLATLIST_UPDATE_CELLS_BATCHING_PERIOD = 40;
|
|
1771
|
+
var FLATLIST_INITIAL_NUM_TO_RENDER = 6;
|
|
1772
|
+
var SCROLL_RETRY_DELAY_MS = 120;
|
|
1773
|
+
var SCROLL_MAX_RETRIES = 10;
|
|
1389
1774
|
var Viewer = ({ engine }) => {
|
|
1390
|
-
const {
|
|
1775
|
+
const {
|
|
1776
|
+
pageCount,
|
|
1777
|
+
currentPage,
|
|
1778
|
+
scrollToPageSignal,
|
|
1779
|
+
setDocumentState,
|
|
1780
|
+
uiTheme,
|
|
1781
|
+
viewMode,
|
|
1782
|
+
zoom
|
|
1783
|
+
} = useViewerStore3();
|
|
1391
1784
|
const listRef = useRef3(null);
|
|
1392
1785
|
const isDark = uiTheme === "dark";
|
|
1393
1786
|
const { width: windowWidth } = useWindowDimensions2();
|
|
@@ -1395,7 +1788,38 @@ var Viewer = ({ engine }) => {
|
|
|
1395
1788
|
const isSingle = viewMode === "single";
|
|
1396
1789
|
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
1397
1790
|
const isWebView = renderTargetType === "webview";
|
|
1398
|
-
const
|
|
1791
|
+
const perfEnabled = isMobilePerfEnabled();
|
|
1792
|
+
const mountedAtRef = useRef3(perfNow());
|
|
1793
|
+
const readyLoggedRef = useRef3(false);
|
|
1794
|
+
const renderCounterRef = useRef3(createRenderCounter("Viewer", "render", 40));
|
|
1795
|
+
const setStateBurstRef = useRef3(
|
|
1796
|
+
createBurstMonitor("Viewer", "setDocumentState", 12, 800)
|
|
1797
|
+
);
|
|
1798
|
+
const viewableBurstRef = useRef3(
|
|
1799
|
+
createBurstMonitor("Viewer", "onViewableItemsChanged", 12, 800)
|
|
1800
|
+
);
|
|
1801
|
+
const scrollMonitorRef = useRef3(createScrollPerfMonitor("Viewer"));
|
|
1802
|
+
const dimensionsCacheRef = useRef3(/* @__PURE__ */ new Map());
|
|
1803
|
+
const dimensionsPendingRef = useRef3(/* @__PURE__ */ new Set());
|
|
1804
|
+
const layoutRefreshTimeoutRef = useRef3(
|
|
1805
|
+
null
|
|
1806
|
+
);
|
|
1807
|
+
const pendingScrollIndexRef = useRef3(null);
|
|
1808
|
+
const pendingScrollAttemptsRef = useRef3(0);
|
|
1809
|
+
const pendingScrollTimeoutRef = useRef3(
|
|
1810
|
+
null
|
|
1811
|
+
);
|
|
1812
|
+
const [layoutRevision, setLayoutRevision] = useState2(0);
|
|
1813
|
+
renderCounterRef.current({
|
|
1814
|
+
pageCount,
|
|
1815
|
+
currentPage,
|
|
1816
|
+
viewMode,
|
|
1817
|
+
renderTargetType
|
|
1818
|
+
});
|
|
1819
|
+
const pages = useMemo3(
|
|
1820
|
+
() => Array.from({ length: pageCount }).map((_, i) => i),
|
|
1821
|
+
[pageCount]
|
|
1822
|
+
);
|
|
1399
1823
|
const rows = useMemo3(() => {
|
|
1400
1824
|
if (!isDouble) return [];
|
|
1401
1825
|
const result = [];
|
|
@@ -1404,48 +1828,354 @@ var Viewer = ({ engine }) => {
|
|
|
1404
1828
|
}
|
|
1405
1829
|
return result;
|
|
1406
1830
|
}, [isDouble, pageCount]);
|
|
1831
|
+
const scheduleLayoutRefresh = useCallback2(() => {
|
|
1832
|
+
if (layoutRefreshTimeoutRef.current) return;
|
|
1833
|
+
layoutRefreshTimeoutRef.current = setTimeout(() => {
|
|
1834
|
+
layoutRefreshTimeoutRef.current = null;
|
|
1835
|
+
setLayoutRevision((value) => value + 1);
|
|
1836
|
+
}, 120);
|
|
1837
|
+
}, []);
|
|
1838
|
+
const clearPendingScrollRetry = useCallback2(() => {
|
|
1839
|
+
if (pendingScrollTimeoutRef.current) {
|
|
1840
|
+
clearTimeout(pendingScrollTimeoutRef.current);
|
|
1841
|
+
pendingScrollTimeoutRef.current = null;
|
|
1842
|
+
}
|
|
1843
|
+
}, []);
|
|
1844
|
+
const clearPendingScrollTarget = useCallback2(() => {
|
|
1845
|
+
pendingScrollIndexRef.current = null;
|
|
1846
|
+
pendingScrollAttemptsRef.current = 0;
|
|
1847
|
+
clearPendingScrollRetry();
|
|
1848
|
+
}, [clearPendingScrollRetry]);
|
|
1849
|
+
const scheduleScrollRetry = useCallback2(
|
|
1850
|
+
(reason) => {
|
|
1851
|
+
const pendingIndex = pendingScrollIndexRef.current;
|
|
1852
|
+
if (pendingIndex === null) return;
|
|
1853
|
+
if (pendingScrollAttemptsRef.current >= SCROLL_MAX_RETRIES) {
|
|
1854
|
+
if (perfEnabled) {
|
|
1855
|
+
logPerfEvent("Viewer", "scroll.retry.giveup", {
|
|
1856
|
+
reason,
|
|
1857
|
+
targetIndex: pendingIndex,
|
|
1858
|
+
attempts: pendingScrollAttemptsRef.current
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
clearPendingScrollTarget();
|
|
1862
|
+
return;
|
|
1863
|
+
}
|
|
1864
|
+
clearPendingScrollRetry();
|
|
1865
|
+
pendingScrollTimeoutRef.current = setTimeout(() => {
|
|
1866
|
+
pendingScrollTimeoutRef.current = null;
|
|
1867
|
+
const targetIndex = pendingScrollIndexRef.current;
|
|
1868
|
+
if (targetIndex === null) return;
|
|
1869
|
+
pendingScrollAttemptsRef.current += 1;
|
|
1870
|
+
listRef.current?.scrollToIndex({
|
|
1871
|
+
index: targetIndex,
|
|
1872
|
+
animated: false,
|
|
1873
|
+
viewPosition: 0
|
|
1874
|
+
});
|
|
1875
|
+
if (perfEnabled) {
|
|
1876
|
+
logPerfEvent("Viewer", "scroll.retry", {
|
|
1877
|
+
reason,
|
|
1878
|
+
targetIndex,
|
|
1879
|
+
attempt: pendingScrollAttemptsRef.current
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
}, SCROLL_RETRY_DELAY_MS);
|
|
1883
|
+
},
|
|
1884
|
+
[clearPendingScrollRetry, clearPendingScrollTarget, perfEnabled]
|
|
1885
|
+
);
|
|
1886
|
+
useEffect3(
|
|
1887
|
+
() => () => {
|
|
1888
|
+
if (layoutRefreshTimeoutRef.current) {
|
|
1889
|
+
clearTimeout(layoutRefreshTimeoutRef.current);
|
|
1890
|
+
}
|
|
1891
|
+
clearPendingScrollRetry();
|
|
1892
|
+
},
|
|
1893
|
+
[clearPendingScrollRetry]
|
|
1894
|
+
);
|
|
1895
|
+
const ensurePageDimensions = useCallback2(
|
|
1896
|
+
(pageIndex) => {
|
|
1897
|
+
if (pageIndex < 0 || pageIndex >= pageCount) return;
|
|
1898
|
+
if (dimensionsCacheRef.current.has(pageIndex)) return;
|
|
1899
|
+
if (dimensionsPendingRef.current.has(pageIndex)) return;
|
|
1900
|
+
dimensionsPendingRef.current.add(pageIndex);
|
|
1901
|
+
void engine.getPageDimensions(pageIndex).then((dims) => {
|
|
1902
|
+
if (dims.width <= 0 || dims.height <= 0) return;
|
|
1903
|
+
const previous = dimensionsCacheRef.current.get(pageIndex);
|
|
1904
|
+
if (previous && previous.width === dims.width && previous.height === dims.height) {
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
dimensionsCacheRef.current.set(pageIndex, {
|
|
1908
|
+
width: dims.width,
|
|
1909
|
+
height: dims.height
|
|
1910
|
+
});
|
|
1911
|
+
scheduleLayoutRefresh();
|
|
1912
|
+
}).catch((error) => {
|
|
1913
|
+
if (!perfEnabled) return;
|
|
1914
|
+
logPerfEvent("Viewer", "pageDimensions.error", {
|
|
1915
|
+
page: pageIndex + 1,
|
|
1916
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1917
|
+
});
|
|
1918
|
+
}).finally(() => {
|
|
1919
|
+
dimensionsPendingRef.current.delete(pageIndex);
|
|
1920
|
+
});
|
|
1921
|
+
},
|
|
1922
|
+
[engine, pageCount, perfEnabled, scheduleLayoutRefresh]
|
|
1923
|
+
);
|
|
1924
|
+
const getPageAspectRatio = useCallback2((pageIndex) => {
|
|
1925
|
+
const dims = dimensionsCacheRef.current.get(pageIndex);
|
|
1926
|
+
if (!dims || dims.width <= 0 || dims.height <= 0) {
|
|
1927
|
+
return DEFAULT_PAGE_ASPECT_RATIO;
|
|
1928
|
+
}
|
|
1929
|
+
return dims.width / dims.height;
|
|
1930
|
+
}, []);
|
|
1931
|
+
useEffect3(() => {
|
|
1932
|
+
if (!perfEnabled) return;
|
|
1933
|
+
logPerfEvent("Viewer", "mount", { viewMode, renderTargetType });
|
|
1934
|
+
sampleMemory("Viewer", "mount", { pageCount });
|
|
1935
|
+
return () => {
|
|
1936
|
+
logPerfEvent("Viewer", "unmount");
|
|
1937
|
+
};
|
|
1938
|
+
}, [perfEnabled]);
|
|
1939
|
+
useEffect3(() => {
|
|
1940
|
+
if (!perfEnabled || readyLoggedRef.current || pageCount <= 0) return;
|
|
1941
|
+
readyLoggedRef.current = true;
|
|
1942
|
+
logPerfEvent("Viewer", "document.ready", {
|
|
1943
|
+
pageCount,
|
|
1944
|
+
initialLoadMs: Math.round((perfNow() - mountedAtRef.current) * 100) / 100
|
|
1945
|
+
});
|
|
1946
|
+
sampleMemory("Viewer", "document.ready", { pageCount });
|
|
1947
|
+
}, [pageCount, perfEnabled]);
|
|
1948
|
+
useEffect3(() => {
|
|
1949
|
+
if (isWebView || isSingle || pageCount <= 0) return;
|
|
1950
|
+
const warmupCount = Math.min(pageCount, 12);
|
|
1951
|
+
for (let i = 0; i < warmupCount; i += 1) {
|
|
1952
|
+
ensurePageDimensions(i);
|
|
1953
|
+
}
|
|
1954
|
+
}, [ensurePageDimensions, isSingle, isWebView, pageCount]);
|
|
1955
|
+
const setDocumentStateTracked = useCallback2(
|
|
1956
|
+
(state, reason) => {
|
|
1957
|
+
if (perfEnabled) {
|
|
1958
|
+
setStateBurstRef.current({
|
|
1959
|
+
reason,
|
|
1960
|
+
keys: Object.keys(state).join(",")
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
setDocumentState(state);
|
|
1964
|
+
},
|
|
1965
|
+
[perfEnabled, setDocumentState]
|
|
1966
|
+
);
|
|
1967
|
+
const columnGap = 12;
|
|
1968
|
+
const horizontalPadding = 16;
|
|
1969
|
+
const columnWidth = isDouble ? (windowWidth - horizontalPadding * 2 - columnGap) / 2 : windowWidth;
|
|
1970
|
+
const listLayoutMetrics = useMemo3(() => {
|
|
1971
|
+
const offsets = [];
|
|
1972
|
+
const lengths = [];
|
|
1973
|
+
let offset = LIST_TOP_PADDING;
|
|
1974
|
+
if (isDouble) {
|
|
1975
|
+
const safeZoom2 = Math.max(zoom, 0.25);
|
|
1976
|
+
const pageWidth2 = columnWidth * 0.92 * safeZoom2;
|
|
1977
|
+
const estimatedLength2 = pageWidth2 / DEFAULT_PAGE_ASPECT_RATIO + DOUBLE_PAGE_SPACING;
|
|
1978
|
+
for (let i = 0; i < rows.length; i += 1) {
|
|
1979
|
+
const row = rows[i];
|
|
1980
|
+
const leftRatio = getPageAspectRatio(row.left);
|
|
1981
|
+
const rightRatio = row.right === null ? leftRatio : getPageAspectRatio(row.right);
|
|
1982
|
+
const leftLength = pageWidth2 / leftRatio + DOUBLE_PAGE_SPACING;
|
|
1983
|
+
const rightLength = pageWidth2 / rightRatio + DOUBLE_PAGE_SPACING;
|
|
1984
|
+
const rowLength = Math.max(leftLength, rightLength);
|
|
1985
|
+
offsets.push(offset);
|
|
1986
|
+
lengths.push(rowLength);
|
|
1987
|
+
offset += rowLength;
|
|
1988
|
+
}
|
|
1989
|
+
return { offsets, lengths, estimatedLength: estimatedLength2 };
|
|
1990
|
+
}
|
|
1991
|
+
const safeZoom = Math.max(zoom, 0.25);
|
|
1992
|
+
const pageWidth = windowWidth * 0.92 * safeZoom;
|
|
1993
|
+
const estimatedLength = pageWidth / DEFAULT_PAGE_ASPECT_RATIO + CONTINUOUS_PAGE_SPACING;
|
|
1994
|
+
for (let i = 0; i < pageCount; i += 1) {
|
|
1995
|
+
const ratio = getPageAspectRatio(i);
|
|
1996
|
+
const length = pageWidth / ratio + CONTINUOUS_PAGE_SPACING;
|
|
1997
|
+
offsets.push(offset);
|
|
1998
|
+
lengths.push(length);
|
|
1999
|
+
offset += length;
|
|
2000
|
+
}
|
|
2001
|
+
return { offsets, lengths, estimatedLength };
|
|
2002
|
+
}, [
|
|
2003
|
+
columnWidth,
|
|
2004
|
+
getPageAspectRatio,
|
|
2005
|
+
isDouble,
|
|
2006
|
+
layoutRevision,
|
|
2007
|
+
pageCount,
|
|
2008
|
+
rows,
|
|
2009
|
+
windowWidth,
|
|
2010
|
+
zoom
|
|
2011
|
+
]);
|
|
2012
|
+
const getFallbackOffsetForIndex = useCallback2(
|
|
2013
|
+
(index) => {
|
|
2014
|
+
if (listLayoutMetrics.lengths.length === 0) {
|
|
2015
|
+
return LIST_TOP_PADDING;
|
|
2016
|
+
}
|
|
2017
|
+
const safeIndex = Math.max(
|
|
2018
|
+
0,
|
|
2019
|
+
Math.min(index, listLayoutMetrics.lengths.length - 1)
|
|
2020
|
+
);
|
|
2021
|
+
const cachedOffset = listLayoutMetrics.offsets[safeIndex];
|
|
2022
|
+
if (typeof cachedOffset === "number") return cachedOffset;
|
|
2023
|
+
return LIST_TOP_PADDING + listLayoutMetrics.estimatedLength * safeIndex;
|
|
2024
|
+
},
|
|
2025
|
+
[listLayoutMetrics]
|
|
2026
|
+
);
|
|
2027
|
+
const getItemLayout = useCallback2(
|
|
2028
|
+
(_, index) => {
|
|
2029
|
+
if (listLayoutMetrics.lengths.length === 0) {
|
|
2030
|
+
return {
|
|
2031
|
+
index,
|
|
2032
|
+
length: listLayoutMetrics.estimatedLength,
|
|
2033
|
+
offset: LIST_TOP_PADDING
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
const safeIndex = Math.max(
|
|
2037
|
+
0,
|
|
2038
|
+
Math.min(index, listLayoutMetrics.lengths.length - 1)
|
|
2039
|
+
);
|
|
2040
|
+
if (isDouble) {
|
|
2041
|
+
const row = rows[safeIndex];
|
|
2042
|
+
if (row) {
|
|
2043
|
+
ensurePageDimensions(row.left);
|
|
2044
|
+
if (row.right !== null) {
|
|
2045
|
+
ensurePageDimensions(row.right);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
} else {
|
|
2049
|
+
ensurePageDimensions(safeIndex);
|
|
2050
|
+
}
|
|
2051
|
+
const cachedLength = listLayoutMetrics.lengths[safeIndex];
|
|
2052
|
+
const cachedOffset = listLayoutMetrics.offsets[safeIndex];
|
|
2053
|
+
return {
|
|
2054
|
+
index,
|
|
2055
|
+
length: typeof cachedLength === "number" ? cachedLength : listLayoutMetrics.estimatedLength,
|
|
2056
|
+
offset: typeof cachedOffset === "number" ? cachedOffset : LIST_TOP_PADDING + listLayoutMetrics.estimatedLength * safeIndex
|
|
2057
|
+
};
|
|
2058
|
+
},
|
|
2059
|
+
[ensurePageDimensions, isDouble, listLayoutMetrics, rows]
|
|
2060
|
+
);
|
|
1407
2061
|
useEffect3(() => {
|
|
1408
2062
|
if (isWebView) {
|
|
2063
|
+
clearPendingScrollTarget();
|
|
1409
2064
|
if (scrollToPageSignal === null) return;
|
|
1410
2065
|
if (pageCount === 0) return;
|
|
1411
2066
|
if (scrollToPageSignal < 0 || scrollToPageSignal >= pageCount) return;
|
|
1412
2067
|
const nextPage = scrollToPageSignal + 1;
|
|
1413
2068
|
engine.goToPage(nextPage);
|
|
1414
|
-
|
|
2069
|
+
setDocumentStateTracked(
|
|
2070
|
+
{ currentPage: nextPage, scrollToPageSignal: null },
|
|
2071
|
+
"scrollToPageSignal.webview"
|
|
2072
|
+
);
|
|
1415
2073
|
return;
|
|
1416
2074
|
}
|
|
1417
2075
|
if (scrollToPageSignal === null) return;
|
|
1418
2076
|
if (pageCount === 0) return;
|
|
1419
2077
|
if (scrollToPageSignal < 0 || scrollToPageSignal >= pageCount) return;
|
|
1420
2078
|
if (isSingle) {
|
|
1421
|
-
|
|
2079
|
+
clearPendingScrollTarget();
|
|
2080
|
+
setDocumentStateTracked(
|
|
2081
|
+
{ currentPage: scrollToPageSignal + 1, scrollToPageSignal: null },
|
|
2082
|
+
"scrollToPageSignal.single"
|
|
2083
|
+
);
|
|
1422
2084
|
return;
|
|
1423
2085
|
}
|
|
2086
|
+
ensurePageDimensions(scrollToPageSignal);
|
|
2087
|
+
if (isDouble) {
|
|
2088
|
+
ensurePageDimensions(scrollToPageSignal - 1);
|
|
2089
|
+
ensurePageDimensions(scrollToPageSignal + 1);
|
|
2090
|
+
}
|
|
1424
2091
|
const targetIndex = isDouble ? Math.floor(scrollToPageSignal / 2) : scrollToPageSignal;
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
2092
|
+
pendingScrollIndexRef.current = targetIndex;
|
|
2093
|
+
pendingScrollAttemptsRef.current = 0;
|
|
2094
|
+
clearPendingScrollRetry();
|
|
2095
|
+
setDocumentStateTracked(
|
|
2096
|
+
{
|
|
2097
|
+
currentPage: scrollToPageSignal + 1,
|
|
2098
|
+
scrollToPageSignal: null
|
|
2099
|
+
},
|
|
2100
|
+
"scrollToPageSignal.flatList"
|
|
2101
|
+
);
|
|
2102
|
+
listRef.current?.scrollToIndex({
|
|
2103
|
+
index: targetIndex,
|
|
2104
|
+
animated: true,
|
|
2105
|
+
viewPosition: 0
|
|
2106
|
+
});
|
|
2107
|
+
}, [
|
|
2108
|
+
clearPendingScrollRetry,
|
|
2109
|
+
clearPendingScrollTarget,
|
|
2110
|
+
ensurePageDimensions,
|
|
2111
|
+
scrollToPageSignal,
|
|
2112
|
+
pageCount,
|
|
2113
|
+
setDocumentStateTracked,
|
|
2114
|
+
isDouble,
|
|
2115
|
+
isSingle,
|
|
2116
|
+
isWebView,
|
|
2117
|
+
engine
|
|
2118
|
+
]);
|
|
2119
|
+
const onViewableItemsChanged = useCallback2(
|
|
1429
2120
|
({ viewableItems }) => {
|
|
2121
|
+
if (perfEnabled) {
|
|
2122
|
+
viewableBurstRef.current({
|
|
2123
|
+
viewableCount: viewableItems.length,
|
|
2124
|
+
mode: isDouble ? "double" : "continuous"
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
const pendingIndex = pendingScrollIndexRef.current;
|
|
2128
|
+
if (pendingIndex !== null) {
|
|
2129
|
+
const reachedTarget = viewableItems.some((token) => {
|
|
2130
|
+
if (isDouble) {
|
|
2131
|
+
const row = token.item;
|
|
2132
|
+
if (!row) return false;
|
|
2133
|
+
if (row.left === pendingIndex) return true;
|
|
2134
|
+
return row.right === pendingIndex;
|
|
2135
|
+
}
|
|
2136
|
+
return token.index === pendingIndex;
|
|
2137
|
+
});
|
|
2138
|
+
if (reachedTarget) {
|
|
2139
|
+
if (perfEnabled) {
|
|
2140
|
+
logPerfEvent("Viewer", "scroll.retry.resolved", {
|
|
2141
|
+
targetIndex: pendingIndex,
|
|
2142
|
+
attempts: pendingScrollAttemptsRef.current
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
clearPendingScrollTarget();
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
1430
2148
|
const first = viewableItems[0];
|
|
1431
2149
|
if (!first) return;
|
|
1432
2150
|
if (isDouble) {
|
|
1433
2151
|
const item = first.item;
|
|
1434
2152
|
if (!item) return;
|
|
2153
|
+
ensurePageDimensions(item.left);
|
|
2154
|
+
if (item.right !== null) {
|
|
2155
|
+
ensurePageDimensions(item.right);
|
|
2156
|
+
}
|
|
1435
2157
|
const page = item.left + 1;
|
|
1436
2158
|
if (page !== currentPage) {
|
|
1437
|
-
|
|
2159
|
+
setDocumentStateTracked({ currentPage: page }, "viewable.double");
|
|
1438
2160
|
}
|
|
1439
2161
|
return;
|
|
1440
2162
|
}
|
|
1441
2163
|
if (first.index !== null && first.index !== void 0) {
|
|
2164
|
+
ensurePageDimensions(first.index);
|
|
1442
2165
|
const page = first.index + 1;
|
|
1443
2166
|
if (page !== currentPage) {
|
|
1444
|
-
|
|
2167
|
+
setDocumentStateTracked({ currentPage: page }, "viewable.continuous");
|
|
1445
2168
|
}
|
|
1446
2169
|
}
|
|
1447
2170
|
},
|
|
1448
|
-
[
|
|
2171
|
+
[
|
|
2172
|
+
clearPendingScrollTarget,
|
|
2173
|
+
currentPage,
|
|
2174
|
+
ensurePageDimensions,
|
|
2175
|
+
isDouble,
|
|
2176
|
+
perfEnabled,
|
|
2177
|
+
setDocumentStateTracked
|
|
2178
|
+
]
|
|
1449
2179
|
);
|
|
1450
2180
|
if (isWebView) {
|
|
1451
2181
|
return /* @__PURE__ */ jsx3(View3, { style: [styles3.container, isDark && styles3.containerDark], children: /* @__PURE__ */ jsx3(WebViewViewer_default, { engine }) });
|
|
@@ -1457,50 +2187,137 @@ var Viewer = ({ engine }) => {
|
|
|
1457
2187
|
contentContainerStyle: styles3.singleContent,
|
|
1458
2188
|
showsVerticalScrollIndicator: false,
|
|
1459
2189
|
scrollEnabled: true,
|
|
1460
|
-
|
|
2190
|
+
onScroll: perfEnabled ? (event) => {
|
|
2191
|
+
const timestampValue = event.nativeEvent.timestamp;
|
|
2192
|
+
const timestamp = typeof timestampValue === "number" ? timestampValue : void 0;
|
|
2193
|
+
scrollMonitorRef.current.track(timestamp);
|
|
2194
|
+
} : void 0,
|
|
2195
|
+
onScrollBeginDrag: perfEnabled ? () => {
|
|
2196
|
+
scrollMonitorRef.current.begin("single.beginDrag");
|
|
2197
|
+
} : void 0,
|
|
2198
|
+
onMomentumScrollBegin: perfEnabled ? () => {
|
|
2199
|
+
scrollMonitorRef.current.begin("single.momentumBegin");
|
|
2200
|
+
} : void 0,
|
|
2201
|
+
onScrollEndDrag: perfEnabled ? () => {
|
|
2202
|
+
scrollMonitorRef.current.end("single.endDrag");
|
|
2203
|
+
sampleMemory("Viewer", "single.endDrag", { pageCount });
|
|
2204
|
+
} : void 0,
|
|
2205
|
+
onMomentumScrollEnd: perfEnabled ? () => {
|
|
2206
|
+
scrollMonitorRef.current.end("single.momentumEnd");
|
|
2207
|
+
sampleMemory("Viewer", "single.momentumEnd", { pageCount });
|
|
2208
|
+
} : void 0,
|
|
2209
|
+
scrollEventThrottle: perfEnabled ? 16 : void 0,
|
|
2210
|
+
children: /* @__PURE__ */ jsx3(
|
|
2211
|
+
PageRenderer_default,
|
|
2212
|
+
{
|
|
2213
|
+
engine,
|
|
2214
|
+
pageIndex: Math.max(0, currentPage - 1),
|
|
2215
|
+
spacing: 32
|
|
2216
|
+
}
|
|
2217
|
+
)
|
|
1461
2218
|
}
|
|
1462
2219
|
) });
|
|
1463
2220
|
}
|
|
1464
|
-
const columnGap = 12;
|
|
1465
|
-
const horizontalPadding = 16;
|
|
1466
|
-
const columnWidth = isDouble ? (windowWidth - horizontalPadding * 2 - columnGap) / 2 : windowWidth;
|
|
1467
2221
|
return /* @__PURE__ */ jsx3(View3, { style: [styles3.container, isDark && styles3.containerDark], children: /* @__PURE__ */ jsx3(
|
|
1468
2222
|
FlatList,
|
|
1469
2223
|
{
|
|
1470
2224
|
ref: listRef,
|
|
1471
2225
|
data: isDouble ? rows : pages,
|
|
2226
|
+
initialNumToRender: FLATLIST_INITIAL_NUM_TO_RENDER,
|
|
2227
|
+
windowSize: FLATLIST_WINDOW_SIZE,
|
|
2228
|
+
maxToRenderPerBatch: FLATLIST_MAX_TO_RENDER_PER_BATCH,
|
|
2229
|
+
updateCellsBatchingPeriod: FLATLIST_UPDATE_CELLS_BATCHING_PERIOD,
|
|
2230
|
+
removeClippedSubviews: true,
|
|
2231
|
+
getItemLayout,
|
|
1472
2232
|
keyExtractor: (item) => isDouble ? `row-${item.left}` : `page-${item}`,
|
|
1473
2233
|
contentContainerStyle: styles3.listContent,
|
|
1474
|
-
renderItem: ({ item }) => isDouble ? /* @__PURE__ */ jsxs3(
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
{
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
2234
|
+
renderItem: ({ item }) => isDouble ? /* @__PURE__ */ jsxs3(
|
|
2235
|
+
View3,
|
|
2236
|
+
{
|
|
2237
|
+
style: [styles3.row, { paddingHorizontal: horizontalPadding }],
|
|
2238
|
+
children: [
|
|
2239
|
+
/* @__PURE__ */ jsx3(View3, { style: { width: columnWidth }, children: /* @__PURE__ */ jsx3(
|
|
2240
|
+
PageRenderer_default,
|
|
2241
|
+
{
|
|
2242
|
+
engine,
|
|
2243
|
+
pageIndex: item.left,
|
|
2244
|
+
availableWidth: columnWidth,
|
|
2245
|
+
horizontalPadding: 8,
|
|
2246
|
+
spacing: DOUBLE_PAGE_SPACING
|
|
2247
|
+
}
|
|
2248
|
+
) }),
|
|
2249
|
+
item.right !== null ? /* @__PURE__ */ jsx3(View3, { style: { width: columnWidth }, children: /* @__PURE__ */ jsx3(
|
|
2250
|
+
PageRenderer_default,
|
|
2251
|
+
{
|
|
2252
|
+
engine,
|
|
2253
|
+
pageIndex: item.right,
|
|
2254
|
+
availableWidth: columnWidth,
|
|
2255
|
+
horizontalPadding: 8,
|
|
2256
|
+
spacing: DOUBLE_PAGE_SPACING
|
|
2257
|
+
}
|
|
2258
|
+
) }) : /* @__PURE__ */ jsx3(View3, { style: { width: columnWidth } })
|
|
2259
|
+
]
|
|
2260
|
+
}
|
|
2261
|
+
) : /* @__PURE__ */ jsx3(
|
|
2262
|
+
PageRenderer_default,
|
|
2263
|
+
{
|
|
2264
|
+
engine,
|
|
2265
|
+
pageIndex: item,
|
|
2266
|
+
spacing: CONTINUOUS_PAGE_SPACING
|
|
2267
|
+
}
|
|
2268
|
+
),
|
|
1496
2269
|
onViewableItemsChanged,
|
|
1497
2270
|
viewabilityConfig: { itemVisiblePercentThreshold: 60 },
|
|
1498
2271
|
scrollEnabled: true,
|
|
1499
2272
|
onScrollToIndexFailed: ({ index, averageItemLength }) => {
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
2273
|
+
const dataLength = isDouble ? rows.length : pages.length;
|
|
2274
|
+
if (index < 0 || index >= dataLength) return;
|
|
2275
|
+
pendingScrollIndexRef.current = index;
|
|
2276
|
+
const offset = Math.max(0, getFallbackOffsetForIndex(index));
|
|
2277
|
+
listRef.current?.scrollToOffset({ offset, animated: false });
|
|
2278
|
+
if (!isDouble) {
|
|
2279
|
+
ensurePageDimensions(index);
|
|
2280
|
+
} else {
|
|
2281
|
+
const row = rows[index];
|
|
2282
|
+
if (row) {
|
|
2283
|
+
ensurePageDimensions(row.left);
|
|
2284
|
+
if (row.right !== null) {
|
|
2285
|
+
ensurePageDimensions(row.right);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
scheduleScrollRetry("onScrollToIndexFailed");
|
|
2290
|
+
if (perfEnabled) {
|
|
2291
|
+
logPerfEvent("Viewer", "scrollToIndexFailed", {
|
|
2292
|
+
index,
|
|
2293
|
+
averageItemLength,
|
|
2294
|
+
fallbackOffset: offset,
|
|
2295
|
+
fallbackSource: "cached-item-layout",
|
|
2296
|
+
itemCount: dataLength,
|
|
2297
|
+
retryAttempt: pendingScrollAttemptsRef.current
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
1503
2300
|
},
|
|
2301
|
+
onScroll: perfEnabled ? (event) => {
|
|
2302
|
+
const timestampValue = event.nativeEvent.timestamp;
|
|
2303
|
+
const timestamp = typeof timestampValue === "number" ? timestampValue : void 0;
|
|
2304
|
+
scrollMonitorRef.current.track(timestamp);
|
|
2305
|
+
} : void 0,
|
|
2306
|
+
onScrollBeginDrag: perfEnabled ? () => {
|
|
2307
|
+
scrollMonitorRef.current.begin("continuous.beginDrag");
|
|
2308
|
+
} : void 0,
|
|
2309
|
+
onMomentumScrollBegin: perfEnabled ? () => {
|
|
2310
|
+
scrollMonitorRef.current.begin("continuous.momentumBegin");
|
|
2311
|
+
} : void 0,
|
|
2312
|
+
onScrollEndDrag: perfEnabled ? () => {
|
|
2313
|
+
scrollMonitorRef.current.end("continuous.endDrag");
|
|
2314
|
+
sampleMemory("Viewer", "continuous.endDrag", { pageCount });
|
|
2315
|
+
} : void 0,
|
|
2316
|
+
onMomentumScrollEnd: perfEnabled ? () => {
|
|
2317
|
+
scrollMonitorRef.current.end("continuous.momentumEnd");
|
|
2318
|
+
sampleMemory("Viewer", "continuous.momentumEnd", { pageCount });
|
|
2319
|
+
} : void 0,
|
|
2320
|
+
scrollEventThrottle: perfEnabled ? 16 : void 0,
|
|
1504
2321
|
showsVerticalScrollIndicator: false
|
|
1505
2322
|
}
|
|
1506
2323
|
) });
|
|
@@ -1514,8 +2331,8 @@ var styles3 = StyleSheet3.create({
|
|
|
1514
2331
|
backgroundColor: "#0f1115"
|
|
1515
2332
|
},
|
|
1516
2333
|
listContent: {
|
|
1517
|
-
paddingTop:
|
|
1518
|
-
paddingBottom:
|
|
2334
|
+
paddingTop: LIST_TOP_PADDING,
|
|
2335
|
+
paddingBottom: LIST_BOTTOM_PADDING
|
|
1519
2336
|
},
|
|
1520
2337
|
singleContent: {
|
|
1521
2338
|
paddingTop: 18,
|
|
@@ -1529,7 +2346,7 @@ var styles3 = StyleSheet3.create({
|
|
|
1529
2346
|
var Viewer_default = Viewer;
|
|
1530
2347
|
|
|
1531
2348
|
// components/Topbar.tsx
|
|
1532
|
-
import { useEffect as useEffect4, useState as
|
|
2349
|
+
import { useEffect as useEffect4, useState as useState3 } from "react";
|
|
1533
2350
|
import { View as View4, Text, Pressable as Pressable2, StyleSheet as StyleSheet4 } from "react-native";
|
|
1534
2351
|
import { useViewerStore as useViewerStore4 } from "@papyrus-sdk/core";
|
|
1535
2352
|
|
|
@@ -1631,7 +2448,7 @@ var Topbar = ({ engine, onOpenSettings }) => {
|
|
|
1631
2448
|
triggerScrollToPage,
|
|
1632
2449
|
accentColor
|
|
1633
2450
|
} = useViewerStore4();
|
|
1634
|
-
const [pageLabel, setPageLabel] =
|
|
2451
|
+
const [pageLabel, setPageLabel] = useState3(`${currentPage}`);
|
|
1635
2452
|
const isDark = uiTheme === "dark";
|
|
1636
2453
|
const navIconColor = isDark ? "#e5e7eb" : "#111827";
|
|
1637
2454
|
useEffect4(() => {
|
|
@@ -1983,7 +2800,13 @@ var styles5 = StyleSheet5.create({
|
|
|
1983
2800
|
var ToolDock_default = ToolDock;
|
|
1984
2801
|
|
|
1985
2802
|
// components/RightSheet.tsx
|
|
1986
|
-
import {
|
|
2803
|
+
import {
|
|
2804
|
+
useCallback as useCallback3,
|
|
2805
|
+
useEffect as useEffect5,
|
|
2806
|
+
useMemo as useMemo4,
|
|
2807
|
+
useRef as useRef4,
|
|
2808
|
+
useState as useState4
|
|
2809
|
+
} from "react";
|
|
1987
2810
|
import {
|
|
1988
2811
|
Modal,
|
|
1989
2812
|
View as View6,
|
|
@@ -2025,7 +2848,7 @@ var PageThumbnail = ({
|
|
|
2025
2848
|
onPress
|
|
2026
2849
|
}) => {
|
|
2027
2850
|
const viewRef = useRef4(null);
|
|
2028
|
-
const [layoutReady, setLayoutReady] =
|
|
2851
|
+
const [layoutReady, setLayoutReady] = useState4(false);
|
|
2029
2852
|
useEffect5(() => {
|
|
2030
2853
|
if (!layoutReady || !useNativePreview) return;
|
|
2031
2854
|
const viewTag = findNodeHandle2(viewRef.current);
|
|
@@ -2051,7 +2874,29 @@ var PageThumbnail = ({
|
|
|
2051
2874
|
isActive && { borderColor: accentColor }
|
|
2052
2875
|
],
|
|
2053
2876
|
children: [
|
|
2054
|
-
/* @__PURE__ */ jsx7(
|
|
2877
|
+
/* @__PURE__ */ jsx7(
|
|
2878
|
+
View6,
|
|
2879
|
+
{
|
|
2880
|
+
onLayout: handleLayout,
|
|
2881
|
+
style: [styles6.thumbFrame, { width: frameWidth, height: frameHeight }],
|
|
2882
|
+
children: useNativePreview ? /* @__PURE__ */ jsx7(PapyrusPageView2, { ref: viewRef, style: styles6.thumbView }) : /* @__PURE__ */ jsx7(
|
|
2883
|
+
View6,
|
|
2884
|
+
{
|
|
2885
|
+
style: [styles6.thumbFallback, isDark && styles6.thumbFallbackDark],
|
|
2886
|
+
children: /* @__PURE__ */ jsx7(
|
|
2887
|
+
Text3,
|
|
2888
|
+
{
|
|
2889
|
+
style: [
|
|
2890
|
+
styles6.thumbFallbackText,
|
|
2891
|
+
isDark && styles6.thumbFallbackTextDark
|
|
2892
|
+
],
|
|
2893
|
+
children: pageIndex + 1
|
|
2894
|
+
}
|
|
2895
|
+
)
|
|
2896
|
+
}
|
|
2897
|
+
)
|
|
2898
|
+
}
|
|
2899
|
+
),
|
|
2055
2900
|
/* @__PURE__ */ jsx7(Text3, { style: [styles6.thumbLabel, isDark && styles6.thumbLabelDark], children: pageIndex + 1 })
|
|
2056
2901
|
]
|
|
2057
2902
|
}
|
|
@@ -2118,14 +2963,20 @@ var RightSheet = ({ engine }) => {
|
|
|
2118
2963
|
locale,
|
|
2119
2964
|
accentColor
|
|
2120
2965
|
} = useViewerStore6();
|
|
2121
|
-
const [pagesMode, setPagesMode] =
|
|
2122
|
-
|
|
2123
|
-
|
|
2966
|
+
const [pagesMode, setPagesMode] = useState4(
|
|
2967
|
+
"thumbnails"
|
|
2968
|
+
);
|
|
2969
|
+
const [query, setQuery] = useState4("");
|
|
2970
|
+
const [isSearching, setIsSearching] = useState4(false);
|
|
2124
2971
|
const searchService = useMemo4(() => new SearchService(engine), [engine]);
|
|
2125
2972
|
const isDark = uiTheme === "dark";
|
|
2126
2973
|
const accentSoft = withAlpha(accentColor, 0.2);
|
|
2127
2974
|
const accentStrong = withAlpha(accentColor, 0.35);
|
|
2128
2975
|
const t = getStrings(locale);
|
|
2976
|
+
const perfEnabled = isMobilePerfEnabled();
|
|
2977
|
+
const setStateBurstRef = useRef4(
|
|
2978
|
+
createBurstMonitor("RightSheet", "setDocumentState", 10, 800)
|
|
2979
|
+
);
|
|
2129
2980
|
const sheetHeight = Math.min(640, Dimensions.get("window").height * 0.72);
|
|
2130
2981
|
const windowWidth = Dimensions.get("window").width;
|
|
2131
2982
|
const gridGutter = 12;
|
|
@@ -2134,24 +2985,76 @@ var RightSheet = ({ engine }) => {
|
|
|
2134
2985
|
const frameWidth = cardWidth - 16;
|
|
2135
2986
|
const frameHeight = frameWidth * 1.28;
|
|
2136
2987
|
const renderTarget = engine.getRenderTargetType?.();
|
|
2137
|
-
const hasNativePageView = Boolean(
|
|
2988
|
+
const hasNativePageView = Boolean(
|
|
2989
|
+
UIManager.getViewManagerConfig?.("PapyrusPageView")
|
|
2990
|
+
);
|
|
2138
2991
|
const useNativePreview = renderTarget !== "webview" && hasNativePageView;
|
|
2139
2992
|
const closeSheet = () => toggleSidebarRight();
|
|
2993
|
+
const setDocumentStateTracked = useCallback3(
|
|
2994
|
+
(state, reason) => {
|
|
2995
|
+
if (perfEnabled) {
|
|
2996
|
+
setStateBurstRef.current({
|
|
2997
|
+
reason,
|
|
2998
|
+
keys: Object.keys(state).join(",")
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
3001
|
+
setDocumentState(state);
|
|
3002
|
+
},
|
|
3003
|
+
[perfEnabled, setDocumentState]
|
|
3004
|
+
);
|
|
3005
|
+
useEffect5(() => {
|
|
3006
|
+
if (!perfEnabled || !sidebarRightOpen) return;
|
|
3007
|
+
logPerfEvent("RightSheet", "open", {
|
|
3008
|
+
tab: sidebarRightTab,
|
|
3009
|
+
pageCount,
|
|
3010
|
+
currentPage
|
|
3011
|
+
});
|
|
3012
|
+
sampleMemory("RightSheet", "open", {
|
|
3013
|
+
tab: sidebarRightTab,
|
|
3014
|
+
pageCount
|
|
3015
|
+
});
|
|
3016
|
+
}, [perfEnabled, sidebarRightOpen]);
|
|
3017
|
+
useEffect5(() => {
|
|
3018
|
+
if (!perfEnabled || !sidebarRightOpen) return;
|
|
3019
|
+
return () => {
|
|
3020
|
+
logPerfEvent("RightSheet", "close");
|
|
3021
|
+
};
|
|
3022
|
+
}, [perfEnabled, sidebarRightOpen]);
|
|
3023
|
+
useEffect5(() => {
|
|
3024
|
+
if (!perfEnabled || !sidebarRightOpen || sidebarRightTab !== "pages" || pagesMode !== "thumbnails")
|
|
3025
|
+
return;
|
|
3026
|
+
sampleMemory("RightSheet", "thumbnails.open.start", { pageCount });
|
|
3027
|
+
const timeout = setTimeout(() => {
|
|
3028
|
+
sampleMemory("RightSheet", "thumbnails.open.steady", { pageCount });
|
|
3029
|
+
}, 1200);
|
|
3030
|
+
return () => clearTimeout(timeout);
|
|
3031
|
+
}, [pageCount, pagesMode, perfEnabled, sidebarRightOpen, sidebarRightTab]);
|
|
2140
3032
|
const handleSearch = async () => {
|
|
2141
3033
|
const trimmed = query.trim();
|
|
2142
3034
|
if (!trimmed) {
|
|
2143
3035
|
setSearch("", []);
|
|
2144
3036
|
return;
|
|
2145
3037
|
}
|
|
3038
|
+
const startedAt = perfEnabled ? perfNow() : 0;
|
|
2146
3039
|
setIsSearching(true);
|
|
2147
3040
|
try {
|
|
2148
3041
|
const results = await searchService.search(trimmed);
|
|
2149
3042
|
setSearch(trimmed, results);
|
|
3043
|
+
if (perfEnabled) {
|
|
3044
|
+
logPerfEvent("RightSheet", "search.completed", {
|
|
3045
|
+
queryLength: trimmed.length,
|
|
3046
|
+
results: results.length,
|
|
3047
|
+
durationMs: Math.round((perfNow() - startedAt) * 100) / 100
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
2150
3050
|
} finally {
|
|
2151
3051
|
setIsSearching(false);
|
|
2152
3052
|
}
|
|
2153
3053
|
};
|
|
2154
|
-
const pages = useMemo4(
|
|
3054
|
+
const pages = useMemo4(
|
|
3055
|
+
() => Array.from({ length: pageCount }, (_, i) => i),
|
|
3056
|
+
[pageCount]
|
|
3057
|
+
);
|
|
2155
3058
|
const renderHighlightedSnippet = (text, isActive) => {
|
|
2156
3059
|
const trimmedQuery = searchQuery.trim();
|
|
2157
3060
|
if (trimmedQuery.length < 2) {
|
|
@@ -2185,7 +3088,10 @@ var RightSheet = ({ engine }) => {
|
|
|
2185
3088
|
if (index > cursor) {
|
|
2186
3089
|
parts.push({ text: text.slice(cursor, index), match: false });
|
|
2187
3090
|
}
|
|
2188
|
-
parts.push({
|
|
3091
|
+
parts.push({
|
|
3092
|
+
text: text.slice(index, index + trimmedQuery.length),
|
|
3093
|
+
match: true
|
|
3094
|
+
});
|
|
2189
3095
|
cursor = index + trimmedQuery.length;
|
|
2190
3096
|
}
|
|
2191
3097
|
return /* @__PURE__ */ jsx7(
|
|
@@ -2204,7 +3110,10 @@ var RightSheet = ({ engine }) => {
|
|
|
2204
3110
|
part.match && styles6.matchText,
|
|
2205
3111
|
part.match && isDark && styles6.matchTextDark,
|
|
2206
3112
|
part.match && isActive && styles6.matchTextActive,
|
|
2207
|
-
part.match && isActive && {
|
|
3113
|
+
part.match && isActive && {
|
|
3114
|
+
backgroundColor: accentStrong,
|
|
3115
|
+
color: accentColor
|
|
3116
|
+
}
|
|
2208
3117
|
],
|
|
2209
3118
|
children: part.text
|
|
2210
3119
|
},
|
|
@@ -2214,247 +3123,419 @@ var RightSheet = ({ engine }) => {
|
|
|
2214
3123
|
);
|
|
2215
3124
|
};
|
|
2216
3125
|
if (!sidebarRightOpen) return null;
|
|
2217
|
-
return /* @__PURE__ */ jsx7(
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
3126
|
+
return /* @__PURE__ */ jsx7(
|
|
3127
|
+
Modal,
|
|
3128
|
+
{
|
|
3129
|
+
visible: true,
|
|
3130
|
+
transparent: true,
|
|
3131
|
+
animationType: "slide",
|
|
3132
|
+
onRequestClose: closeSheet,
|
|
3133
|
+
children: /* @__PURE__ */ jsxs7(View6, { style: styles6.modalRoot, children: [
|
|
3134
|
+
/* @__PURE__ */ jsx7(Pressable4, { style: styles6.backdrop, onPress: closeSheet }),
|
|
3135
|
+
/* @__PURE__ */ jsxs7(
|
|
3136
|
+
View6,
|
|
3137
|
+
{
|
|
3138
|
+
style: [
|
|
3139
|
+
styles6.sheet,
|
|
3140
|
+
{ height: sheetHeight },
|
|
3141
|
+
isDark && styles6.sheetDark
|
|
3142
|
+
],
|
|
3143
|
+
children: [
|
|
3144
|
+
/* @__PURE__ */ jsx7(View6, { style: [styles6.handle, isDark && styles6.handleDark] }),
|
|
3145
|
+
/* @__PURE__ */ jsx7(View6, { style: styles6.tabs, children: ["pages", "search", "annotations"].map((tab) => /* @__PURE__ */ jsx7(
|
|
3146
|
+
Pressable4,
|
|
3147
|
+
{
|
|
3148
|
+
onPress: () => toggleSidebarRight(tab),
|
|
3149
|
+
style: [
|
|
3150
|
+
styles6.tabButton,
|
|
3151
|
+
isDark && styles6.tabButtonDark,
|
|
3152
|
+
sidebarRightTab === tab && styles6.tabButtonActive,
|
|
3153
|
+
sidebarRightTab === tab && { backgroundColor: accentColor }
|
|
3154
|
+
],
|
|
3155
|
+
children: /* @__PURE__ */ jsx7(
|
|
3156
|
+
Text3,
|
|
3157
|
+
{
|
|
3158
|
+
style: [
|
|
3159
|
+
styles6.tabText,
|
|
3160
|
+
isDark && styles6.tabTextDark,
|
|
3161
|
+
sidebarRightTab === tab && styles6.tabTextActive
|
|
3162
|
+
],
|
|
3163
|
+
children: tab === "pages" ? t.pages : tab === "search" ? t.search : t.notes
|
|
3164
|
+
}
|
|
3165
|
+
)
|
|
3166
|
+
},
|
|
3167
|
+
tab
|
|
3168
|
+
)) }),
|
|
3169
|
+
sidebarRightTab === "pages" ? /* @__PURE__ */ jsxs7(View6, { style: styles6.pagesContent, children: [
|
|
3170
|
+
/* @__PURE__ */ jsxs7(View6, { style: styles6.pageHeader, children: [
|
|
3171
|
+
/* @__PURE__ */ jsxs7(
|
|
3172
|
+
Text3,
|
|
3173
|
+
{
|
|
3174
|
+
style: [styles6.pageStatus, isDark && styles6.pageStatusDark],
|
|
3175
|
+
children: [
|
|
3176
|
+
t.page,
|
|
3177
|
+
" ",
|
|
3178
|
+
currentPage,
|
|
3179
|
+
" / ",
|
|
3180
|
+
pageCount
|
|
3181
|
+
]
|
|
3182
|
+
}
|
|
3183
|
+
),
|
|
3184
|
+
/* @__PURE__ */ jsxs7(
|
|
3185
|
+
View6,
|
|
3186
|
+
{
|
|
3187
|
+
style: [styles6.segmented, isDark && styles6.segmentedDark],
|
|
3188
|
+
children: [
|
|
3189
|
+
/* @__PURE__ */ jsx7(
|
|
3190
|
+
Pressable4,
|
|
3191
|
+
{
|
|
3192
|
+
onPress: () => setPagesMode("thumbnails"),
|
|
3193
|
+
style: [
|
|
3194
|
+
styles6.segmentButton,
|
|
3195
|
+
pagesMode === "thumbnails" && styles6.segmentButtonActive,
|
|
3196
|
+
pagesMode === "thumbnails" && {
|
|
3197
|
+
backgroundColor: accentColor
|
|
3198
|
+
}
|
|
3199
|
+
],
|
|
3200
|
+
children: /* @__PURE__ */ jsx7(
|
|
3201
|
+
Text3,
|
|
3202
|
+
{
|
|
3203
|
+
style: [
|
|
3204
|
+
styles6.segmentText,
|
|
3205
|
+
isDark && styles6.segmentTextDark,
|
|
3206
|
+
pagesMode === "thumbnails" && styles6.segmentTextActive
|
|
3207
|
+
],
|
|
3208
|
+
children: t.pagesTab
|
|
3209
|
+
}
|
|
3210
|
+
)
|
|
3211
|
+
}
|
|
3212
|
+
),
|
|
3213
|
+
/* @__PURE__ */ jsx7(
|
|
3214
|
+
Pressable4,
|
|
3215
|
+
{
|
|
3216
|
+
onPress: () => setPagesMode("summary"),
|
|
3217
|
+
style: [
|
|
3218
|
+
styles6.segmentButton,
|
|
3219
|
+
pagesMode === "summary" && styles6.segmentButtonActive,
|
|
3220
|
+
pagesMode === "summary" && {
|
|
3221
|
+
backgroundColor: accentColor
|
|
3222
|
+
}
|
|
3223
|
+
],
|
|
3224
|
+
children: /* @__PURE__ */ jsx7(
|
|
3225
|
+
Text3,
|
|
3226
|
+
{
|
|
3227
|
+
style: [
|
|
3228
|
+
styles6.segmentText,
|
|
3229
|
+
isDark && styles6.segmentTextDark,
|
|
3230
|
+
pagesMode === "summary" && styles6.segmentTextActive
|
|
3231
|
+
],
|
|
3232
|
+
children: t.summaryTab
|
|
3233
|
+
}
|
|
3234
|
+
)
|
|
3235
|
+
}
|
|
3236
|
+
)
|
|
3237
|
+
]
|
|
3238
|
+
}
|
|
3239
|
+
)
|
|
3240
|
+
] }),
|
|
3241
|
+
pagesMode === "thumbnails" ? /* @__PURE__ */ jsx7(
|
|
3242
|
+
FlatList2,
|
|
2266
3243
|
{
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
3244
|
+
data: pages,
|
|
3245
|
+
keyExtractor: (item) => `thumb-${item}`,
|
|
3246
|
+
numColumns: 2,
|
|
3247
|
+
contentContainerStyle: styles6.thumbGrid,
|
|
3248
|
+
columnWrapperStyle: styles6.thumbRow,
|
|
3249
|
+
showsVerticalScrollIndicator: false,
|
|
3250
|
+
initialNumToRender: 6,
|
|
3251
|
+
renderItem: ({ item }) => /* @__PURE__ */ jsx7(
|
|
3252
|
+
PageThumbnail,
|
|
3253
|
+
{
|
|
3254
|
+
engine,
|
|
3255
|
+
pageIndex: item,
|
|
3256
|
+
isActive: item + 1 === currentPage,
|
|
3257
|
+
isDark,
|
|
3258
|
+
zoom,
|
|
3259
|
+
cardWidth,
|
|
3260
|
+
frameWidth,
|
|
3261
|
+
frameHeight,
|
|
3262
|
+
accentColor,
|
|
3263
|
+
useNativePreview,
|
|
3264
|
+
onPress: () => {
|
|
3265
|
+
engine.goToPage(item + 1);
|
|
3266
|
+
setDocumentStateTracked(
|
|
3267
|
+
{ currentPage: item + 1 },
|
|
3268
|
+
"thumbnail.press"
|
|
3269
|
+
);
|
|
3270
|
+
triggerScrollToPage(item);
|
|
3271
|
+
closeSheet();
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
)
|
|
2273
3275
|
}
|
|
2274
|
-
)
|
|
2275
|
-
|
|
2276
|
-
),
|
|
2277
|
-
/* @__PURE__ */ jsx7(
|
|
2278
|
-
Pressable4,
|
|
2279
|
-
{
|
|
2280
|
-
onPress: () => setPagesMode("summary"),
|
|
2281
|
-
style: [
|
|
2282
|
-
styles6.segmentButton,
|
|
2283
|
-
pagesMode === "summary" && styles6.segmentButtonActive,
|
|
2284
|
-
pagesMode === "summary" && { backgroundColor: accentColor }
|
|
2285
|
-
],
|
|
2286
|
-
children: /* @__PURE__ */ jsx7(
|
|
2287
|
-
Text3,
|
|
3276
|
+
) : /* @__PURE__ */ jsx7(
|
|
3277
|
+
ScrollView3,
|
|
2288
3278
|
{
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
3279
|
+
contentContainerStyle: styles6.summaryContent,
|
|
3280
|
+
showsVerticalScrollIndicator: false,
|
|
3281
|
+
children: outline.length === 0 ? /* @__PURE__ */ jsx7(
|
|
3282
|
+
Text3,
|
|
3283
|
+
{
|
|
3284
|
+
style: [styles6.emptyText, isDark && styles6.emptyTextDark],
|
|
3285
|
+
children: t.noSummary
|
|
3286
|
+
}
|
|
3287
|
+
) : outline.map((item, index) => /* @__PURE__ */ jsx7(
|
|
3288
|
+
OutlineNode,
|
|
3289
|
+
{
|
|
3290
|
+
item,
|
|
3291
|
+
isDark,
|
|
3292
|
+
untitledLabel: t.untitled,
|
|
3293
|
+
onSelect: (pageIndex) => {
|
|
3294
|
+
engine.goToPage(pageIndex + 1);
|
|
3295
|
+
setDocumentStateTracked(
|
|
3296
|
+
{ currentPage: pageIndex + 1 },
|
|
3297
|
+
"outline.select"
|
|
3298
|
+
);
|
|
3299
|
+
triggerScrollToPage(pageIndex);
|
|
3300
|
+
closeSheet();
|
|
3301
|
+
}
|
|
3302
|
+
},
|
|
3303
|
+
`${item.title}-${index}`
|
|
3304
|
+
))
|
|
2295
3305
|
}
|
|
2296
3306
|
)
|
|
2297
|
-
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
3307
|
+
] }) : /* @__PURE__ */ jsx7(
|
|
3308
|
+
ScrollView3,
|
|
3309
|
+
{
|
|
3310
|
+
contentContainerStyle: styles6.content,
|
|
3311
|
+
showsVerticalScrollIndicator: false,
|
|
3312
|
+
children: sidebarRightTab === "search" ? /* @__PURE__ */ jsxs7(View6, { children: [
|
|
3313
|
+
/* @__PURE__ */ jsxs7(
|
|
3314
|
+
View6,
|
|
3315
|
+
{
|
|
3316
|
+
style: [styles6.searchBox, isDark && styles6.searchBoxDark],
|
|
3317
|
+
children: [
|
|
3318
|
+
/* @__PURE__ */ jsx7(
|
|
3319
|
+
TextInput,
|
|
3320
|
+
{
|
|
3321
|
+
value: query,
|
|
3322
|
+
onChangeText: setQuery,
|
|
3323
|
+
placeholder: t.searchPlaceholder,
|
|
3324
|
+
placeholderTextColor: isDark ? "#9ca3af" : "#6b7280",
|
|
3325
|
+
style: [
|
|
3326
|
+
styles6.searchInput,
|
|
3327
|
+
isDark && styles6.searchInputDark
|
|
3328
|
+
],
|
|
3329
|
+
onSubmitEditing: handleSearch,
|
|
3330
|
+
returnKeyType: "search"
|
|
3331
|
+
}
|
|
3332
|
+
),
|
|
3333
|
+
/* @__PURE__ */ jsx7(
|
|
3334
|
+
Pressable4,
|
|
3335
|
+
{
|
|
3336
|
+
onPress: handleSearch,
|
|
3337
|
+
style: [
|
|
3338
|
+
styles6.searchButton,
|
|
3339
|
+
{ backgroundColor: accentColor }
|
|
3340
|
+
],
|
|
3341
|
+
children: /* @__PURE__ */ jsx7(Text3, { style: styles6.searchButtonText, children: t.searchGo })
|
|
3342
|
+
}
|
|
3343
|
+
)
|
|
3344
|
+
]
|
|
3345
|
+
}
|
|
3346
|
+
),
|
|
3347
|
+
/* @__PURE__ */ jsxs7(View6, { style: styles6.searchMeta, children: [
|
|
3348
|
+
/* @__PURE__ */ jsxs7(
|
|
3349
|
+
Text3,
|
|
3350
|
+
{
|
|
3351
|
+
style: [
|
|
3352
|
+
styles6.searchCount,
|
|
3353
|
+
isDark && styles6.searchCountDark,
|
|
3354
|
+
{ color: accentColor }
|
|
3355
|
+
],
|
|
3356
|
+
children: [
|
|
3357
|
+
searchResults.length,
|
|
3358
|
+
" ",
|
|
3359
|
+
t.results
|
|
3360
|
+
]
|
|
3361
|
+
}
|
|
3362
|
+
),
|
|
3363
|
+
/* @__PURE__ */ jsxs7(View6, { style: styles6.searchNav, children: [
|
|
3364
|
+
/* @__PURE__ */ jsx7(
|
|
3365
|
+
Pressable4,
|
|
3366
|
+
{
|
|
3367
|
+
onPress: prevSearchResult,
|
|
3368
|
+
disabled: searchResults.length === 0,
|
|
3369
|
+
style: [
|
|
3370
|
+
styles6.searchNavButton,
|
|
3371
|
+
isDark && styles6.searchNavButtonDark,
|
|
3372
|
+
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
3373
|
+
],
|
|
3374
|
+
children: /* @__PURE__ */ jsx7(
|
|
3375
|
+
IconChevronLeft,
|
|
3376
|
+
{
|
|
3377
|
+
size: 14,
|
|
3378
|
+
color: isDark ? "#e5e7eb" : "#111827"
|
|
3379
|
+
}
|
|
3380
|
+
)
|
|
3381
|
+
}
|
|
3382
|
+
),
|
|
3383
|
+
/* @__PURE__ */ jsx7(
|
|
3384
|
+
Pressable4,
|
|
3385
|
+
{
|
|
3386
|
+
onPress: nextSearchResult,
|
|
3387
|
+
disabled: searchResults.length === 0,
|
|
3388
|
+
style: [
|
|
3389
|
+
styles6.searchNavButton,
|
|
3390
|
+
isDark && styles6.searchNavButtonDark,
|
|
3391
|
+
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
3392
|
+
],
|
|
3393
|
+
children: /* @__PURE__ */ jsx7(
|
|
3394
|
+
IconChevronRight,
|
|
3395
|
+
{
|
|
3396
|
+
size: 14,
|
|
3397
|
+
color: isDark ? "#e5e7eb" : "#111827"
|
|
3398
|
+
}
|
|
3399
|
+
)
|
|
3400
|
+
}
|
|
3401
|
+
)
|
|
3402
|
+
] })
|
|
3403
|
+
] }),
|
|
3404
|
+
isSearching && /* @__PURE__ */ jsxs7(View6, { style: styles6.searchStatus, children: [
|
|
3405
|
+
/* @__PURE__ */ jsx7(ActivityIndicator, { size: "small", color: accentColor }),
|
|
3406
|
+
/* @__PURE__ */ jsx7(
|
|
3407
|
+
Text3,
|
|
3408
|
+
{
|
|
3409
|
+
style: [
|
|
3410
|
+
styles6.searchStatusText,
|
|
3411
|
+
isDark && styles6.searchStatusTextDark
|
|
3412
|
+
],
|
|
3413
|
+
children: t.searching
|
|
3414
|
+
}
|
|
3415
|
+
)
|
|
3416
|
+
] }),
|
|
3417
|
+
!isSearching && searchResults.length === 0 && /* @__PURE__ */ jsx7(
|
|
3418
|
+
Text3,
|
|
3419
|
+
{
|
|
3420
|
+
style: [styles6.emptyText, isDark && styles6.emptyTextDark],
|
|
3421
|
+
children: t.noResults
|
|
3422
|
+
}
|
|
3423
|
+
),
|
|
3424
|
+
!isSearching && searchResults.map((res, idx) => {
|
|
3425
|
+
const isActive = idx === activeSearchIndex;
|
|
3426
|
+
return /* @__PURE__ */ jsxs7(
|
|
3427
|
+
Pressable4,
|
|
3428
|
+
{
|
|
3429
|
+
onPress: () => {
|
|
3430
|
+
setDocumentStateTracked(
|
|
3431
|
+
{ activeSearchIndex: idx },
|
|
3432
|
+
"searchResult.select"
|
|
3433
|
+
);
|
|
3434
|
+
triggerScrollToPage(res.pageIndex);
|
|
3435
|
+
closeSheet();
|
|
3436
|
+
},
|
|
3437
|
+
style: [
|
|
3438
|
+
styles6.resultCard,
|
|
3439
|
+
isDark && styles6.resultCardDark,
|
|
3440
|
+
isActive && styles6.resultCardActive,
|
|
3441
|
+
isActive && { borderColor: accentColor }
|
|
3442
|
+
],
|
|
3443
|
+
children: [
|
|
3444
|
+
/* @__PURE__ */ jsxs7(
|
|
3445
|
+
Text3,
|
|
3446
|
+
{
|
|
3447
|
+
style: [
|
|
3448
|
+
styles6.resultPage,
|
|
3449
|
+
isDark && styles6.resultPageDark,
|
|
3450
|
+
{ color: accentColor }
|
|
3451
|
+
],
|
|
3452
|
+
children: [
|
|
3453
|
+
t.page,
|
|
3454
|
+
" ",
|
|
3455
|
+
res.pageIndex + 1
|
|
3456
|
+
]
|
|
3457
|
+
}
|
|
3458
|
+
),
|
|
3459
|
+
renderHighlightedSnippet(res.text, isActive)
|
|
3460
|
+
]
|
|
3461
|
+
},
|
|
3462
|
+
`${res.pageIndex}-${idx}`
|
|
3463
|
+
);
|
|
3464
|
+
})
|
|
3465
|
+
] }) : /* @__PURE__ */ jsx7(View6, { children: annotations.length === 0 ? /* @__PURE__ */ jsx7(
|
|
3466
|
+
Text3,
|
|
3467
|
+
{
|
|
3468
|
+
style: [styles6.emptyText, isDark && styles6.emptyTextDark],
|
|
3469
|
+
children: t.noAnnotations
|
|
3470
|
+
}
|
|
3471
|
+
) : annotations.map((ann) => /* @__PURE__ */ jsxs7(
|
|
3472
|
+
Pressable4,
|
|
3473
|
+
{
|
|
3474
|
+
onPress: () => {
|
|
3475
|
+
setSelectedAnnotation(ann.id);
|
|
3476
|
+
triggerScrollToPage(ann.pageIndex);
|
|
3477
|
+
closeSheet();
|
|
3478
|
+
},
|
|
3479
|
+
style: [styles6.noteCard, isDark && styles6.noteCardDark],
|
|
3480
|
+
children: [
|
|
3481
|
+
/* @__PURE__ */ jsxs7(View6, { style: styles6.noteHeader, children: [
|
|
3482
|
+
/* @__PURE__ */ jsx7(
|
|
3483
|
+
View6,
|
|
3484
|
+
{
|
|
3485
|
+
style: [
|
|
3486
|
+
styles6.noteDot,
|
|
3487
|
+
{ backgroundColor: ann.color }
|
|
3488
|
+
]
|
|
3489
|
+
}
|
|
3490
|
+
),
|
|
3491
|
+
/* @__PURE__ */ jsxs7(
|
|
3492
|
+
Text3,
|
|
3493
|
+
{
|
|
3494
|
+
style: [
|
|
3495
|
+
styles6.noteTitle,
|
|
3496
|
+
isDark && styles6.noteTitleDark
|
|
3497
|
+
],
|
|
3498
|
+
children: [
|
|
3499
|
+
t.page,
|
|
3500
|
+
" ",
|
|
3501
|
+
ann.pageIndex + 1
|
|
3502
|
+
]
|
|
3503
|
+
}
|
|
3504
|
+
)
|
|
3505
|
+
] }),
|
|
3506
|
+
/* @__PURE__ */ jsx7(
|
|
3507
|
+
Text3,
|
|
3508
|
+
{
|
|
3509
|
+
style: [
|
|
3510
|
+
styles6.noteType,
|
|
3511
|
+
isDark && styles6.noteTypeDark,
|
|
3512
|
+
{ color: accentColor }
|
|
3513
|
+
],
|
|
3514
|
+
children: ann.type === "comment" || ann.type === "text" ? t.note.toUpperCase() : ann.type.toUpperCase()
|
|
3515
|
+
}
|
|
3516
|
+
),
|
|
3517
|
+
ann.content ? /* @__PURE__ */ jsx7(
|
|
3518
|
+
Text3,
|
|
3519
|
+
{
|
|
3520
|
+
style: [
|
|
3521
|
+
styles6.noteContent,
|
|
3522
|
+
isDark && styles6.noteContentDark
|
|
3523
|
+
],
|
|
3524
|
+
children: ann.content
|
|
3525
|
+
}
|
|
3526
|
+
) : null
|
|
3527
|
+
]
|
|
3528
|
+
},
|
|
3529
|
+
ann.id
|
|
3530
|
+
)) })
|
|
2329
3531
|
}
|
|
2330
|
-
|
|
2331
|
-
|
|
3532
|
+
)
|
|
3533
|
+
]
|
|
2332
3534
|
}
|
|
2333
|
-
)
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
isDark,
|
|
2338
|
-
untitledLabel: t.untitled,
|
|
2339
|
-
onSelect: (pageIndex) => {
|
|
2340
|
-
engine.goToPage(pageIndex + 1);
|
|
2341
|
-
setDocumentState({ currentPage: pageIndex + 1 });
|
|
2342
|
-
triggerScrollToPage(pageIndex);
|
|
2343
|
-
closeSheet();
|
|
2344
|
-
}
|
|
2345
|
-
},
|
|
2346
|
-
`${item.title}-${index}`
|
|
2347
|
-
)) })
|
|
2348
|
-
] }) : /* @__PURE__ */ jsx7(ScrollView3, { contentContainerStyle: styles6.content, showsVerticalScrollIndicator: false, children: sidebarRightTab === "search" ? /* @__PURE__ */ jsxs7(View6, { children: [
|
|
2349
|
-
/* @__PURE__ */ jsxs7(View6, { style: [styles6.searchBox, isDark && styles6.searchBoxDark], children: [
|
|
2350
|
-
/* @__PURE__ */ jsx7(
|
|
2351
|
-
TextInput,
|
|
2352
|
-
{
|
|
2353
|
-
value: query,
|
|
2354
|
-
onChangeText: setQuery,
|
|
2355
|
-
placeholder: t.searchPlaceholder,
|
|
2356
|
-
placeholderTextColor: isDark ? "#9ca3af" : "#6b7280",
|
|
2357
|
-
style: [styles6.searchInput, isDark && styles6.searchInputDark],
|
|
2358
|
-
onSubmitEditing: handleSearch,
|
|
2359
|
-
returnKeyType: "search"
|
|
2360
|
-
}
|
|
2361
|
-
),
|
|
2362
|
-
/* @__PURE__ */ jsx7(Pressable4, { onPress: handleSearch, style: [styles6.searchButton, { backgroundColor: accentColor }], children: /* @__PURE__ */ jsx7(Text3, { style: styles6.searchButtonText, children: t.searchGo }) })
|
|
2363
|
-
] }),
|
|
2364
|
-
/* @__PURE__ */ jsxs7(View6, { style: styles6.searchMeta, children: [
|
|
2365
|
-
/* @__PURE__ */ jsxs7(Text3, { style: [styles6.searchCount, isDark && styles6.searchCountDark, { color: accentColor }], children: [
|
|
2366
|
-
searchResults.length,
|
|
2367
|
-
" ",
|
|
2368
|
-
t.results
|
|
2369
|
-
] }),
|
|
2370
|
-
/* @__PURE__ */ jsxs7(View6, { style: styles6.searchNav, children: [
|
|
2371
|
-
/* @__PURE__ */ jsx7(
|
|
2372
|
-
Pressable4,
|
|
2373
|
-
{
|
|
2374
|
-
onPress: prevSearchResult,
|
|
2375
|
-
disabled: searchResults.length === 0,
|
|
2376
|
-
style: [
|
|
2377
|
-
styles6.searchNavButton,
|
|
2378
|
-
isDark && styles6.searchNavButtonDark,
|
|
2379
|
-
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
2380
|
-
],
|
|
2381
|
-
children: /* @__PURE__ */ jsx7(IconChevronLeft, { size: 14, color: isDark ? "#e5e7eb" : "#111827" })
|
|
2382
|
-
}
|
|
2383
|
-
),
|
|
2384
|
-
/* @__PURE__ */ jsx7(
|
|
2385
|
-
Pressable4,
|
|
2386
|
-
{
|
|
2387
|
-
onPress: nextSearchResult,
|
|
2388
|
-
disabled: searchResults.length === 0,
|
|
2389
|
-
style: [
|
|
2390
|
-
styles6.searchNavButton,
|
|
2391
|
-
isDark && styles6.searchNavButtonDark,
|
|
2392
|
-
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
2393
|
-
],
|
|
2394
|
-
children: /* @__PURE__ */ jsx7(IconChevronRight, { size: 14, color: isDark ? "#e5e7eb" : "#111827" })
|
|
2395
|
-
}
|
|
2396
|
-
)
|
|
2397
|
-
] })
|
|
2398
|
-
] }),
|
|
2399
|
-
isSearching && /* @__PURE__ */ jsxs7(View6, { style: styles6.searchStatus, children: [
|
|
2400
|
-
/* @__PURE__ */ jsx7(ActivityIndicator, { size: "small", color: accentColor }),
|
|
2401
|
-
/* @__PURE__ */ jsx7(Text3, { style: [styles6.searchStatusText, isDark && styles6.searchStatusTextDark], children: t.searching })
|
|
2402
|
-
] }),
|
|
2403
|
-
!isSearching && searchResults.length === 0 && /* @__PURE__ */ jsx7(Text3, { style: [styles6.emptyText, isDark && styles6.emptyTextDark], children: t.noResults }),
|
|
2404
|
-
!isSearching && searchResults.map((res, idx) => {
|
|
2405
|
-
const isActive = idx === activeSearchIndex;
|
|
2406
|
-
return /* @__PURE__ */ jsxs7(
|
|
2407
|
-
Pressable4,
|
|
2408
|
-
{
|
|
2409
|
-
onPress: () => {
|
|
2410
|
-
setDocumentState({ activeSearchIndex: idx });
|
|
2411
|
-
triggerScrollToPage(res.pageIndex);
|
|
2412
|
-
closeSheet();
|
|
2413
|
-
},
|
|
2414
|
-
style: [
|
|
2415
|
-
styles6.resultCard,
|
|
2416
|
-
isDark && styles6.resultCardDark,
|
|
2417
|
-
isActive && styles6.resultCardActive,
|
|
2418
|
-
isActive && { borderColor: accentColor }
|
|
2419
|
-
],
|
|
2420
|
-
children: [
|
|
2421
|
-
/* @__PURE__ */ jsxs7(Text3, { style: [styles6.resultPage, isDark && styles6.resultPageDark, { color: accentColor }], children: [
|
|
2422
|
-
t.page,
|
|
2423
|
-
" ",
|
|
2424
|
-
res.pageIndex + 1
|
|
2425
|
-
] }),
|
|
2426
|
-
renderHighlightedSnippet(res.text, isActive)
|
|
2427
|
-
]
|
|
2428
|
-
},
|
|
2429
|
-
`${res.pageIndex}-${idx}`
|
|
2430
|
-
);
|
|
2431
|
-
})
|
|
2432
|
-
] }) : /* @__PURE__ */ jsx7(View6, { children: annotations.length === 0 ? /* @__PURE__ */ jsx7(Text3, { style: [styles6.emptyText, isDark && styles6.emptyTextDark], children: t.noAnnotations }) : annotations.map((ann) => /* @__PURE__ */ jsxs7(
|
|
2433
|
-
Pressable4,
|
|
2434
|
-
{
|
|
2435
|
-
onPress: () => {
|
|
2436
|
-
setSelectedAnnotation(ann.id);
|
|
2437
|
-
triggerScrollToPage(ann.pageIndex);
|
|
2438
|
-
closeSheet();
|
|
2439
|
-
},
|
|
2440
|
-
style: [styles6.noteCard, isDark && styles6.noteCardDark],
|
|
2441
|
-
children: [
|
|
2442
|
-
/* @__PURE__ */ jsxs7(View6, { style: styles6.noteHeader, children: [
|
|
2443
|
-
/* @__PURE__ */ jsx7(View6, { style: [styles6.noteDot, { backgroundColor: ann.color }] }),
|
|
2444
|
-
/* @__PURE__ */ jsxs7(Text3, { style: [styles6.noteTitle, isDark && styles6.noteTitleDark], children: [
|
|
2445
|
-
t.page,
|
|
2446
|
-
" ",
|
|
2447
|
-
ann.pageIndex + 1
|
|
2448
|
-
] })
|
|
2449
|
-
] }),
|
|
2450
|
-
/* @__PURE__ */ jsx7(Text3, { style: [styles6.noteType, isDark && styles6.noteTypeDark, { color: accentColor }], children: ann.type === "comment" || ann.type === "text" ? t.note.toUpperCase() : ann.type.toUpperCase() }),
|
|
2451
|
-
ann.content ? /* @__PURE__ */ jsx7(Text3, { style: [styles6.noteContent, isDark && styles6.noteContentDark], children: ann.content }) : null
|
|
2452
|
-
]
|
|
2453
|
-
},
|
|
2454
|
-
ann.id
|
|
2455
|
-
)) }) })
|
|
2456
|
-
] })
|
|
2457
|
-
] }) });
|
|
3535
|
+
)
|
|
3536
|
+
] })
|
|
3537
|
+
}
|
|
3538
|
+
);
|
|
2458
3539
|
};
|
|
2459
3540
|
var styles6 = StyleSheet6.create({
|
|
2460
3541
|
modalRoot: {
|
|
@@ -2835,7 +3916,7 @@ var styles6 = StyleSheet6.create({
|
|
|
2835
3916
|
var RightSheet_default = RightSheet;
|
|
2836
3917
|
|
|
2837
3918
|
// components/AnnotationEditor.tsx
|
|
2838
|
-
import { useEffect as useEffect6, useState as
|
|
3919
|
+
import { useEffect as useEffect6, useState as useState5 } from "react";
|
|
2839
3920
|
import { Modal as Modal2, View as View7, Text as Text4, TextInput as TextInput2, Pressable as Pressable5, StyleSheet as StyleSheet7 } from "react-native";
|
|
2840
3921
|
import { useViewerStore as useViewerStore7 } from "@papyrus-sdk/core";
|
|
2841
3922
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -2843,7 +3924,7 @@ var AnnotationEditor = () => {
|
|
|
2843
3924
|
const { annotations, selectedAnnotationId, updateAnnotation, setSelectedAnnotation, uiTheme, locale, accentColor } = useViewerStore7();
|
|
2844
3925
|
const annotation = annotations.find((ann) => ann.id === selectedAnnotationId);
|
|
2845
3926
|
const isEditable = annotation && (annotation.type === "text" || annotation.type === "comment");
|
|
2846
|
-
const [draft, setDraft] =
|
|
3927
|
+
const [draft, setDraft] = useState5("");
|
|
2847
3928
|
const isDark = uiTheme === "dark";
|
|
2848
3929
|
const t = getStrings(locale);
|
|
2849
3930
|
useEffect6(() => {
|
|
@@ -3619,7 +4700,7 @@ var styles9 = StyleSheet9.create({
|
|
|
3619
4700
|
var SettingsSheet_default = SettingsSheet;
|
|
3620
4701
|
|
|
3621
4702
|
// components/CoverPreview.tsx
|
|
3622
|
-
import { useEffect as useEffect7, useMemo as useMemo5, useRef as useRef5, useState as
|
|
4703
|
+
import { useEffect as useEffect7, useMemo as useMemo5, useRef as useRef5, useState as useState6 } from "react";
|
|
3623
4704
|
import {
|
|
3624
4705
|
ActivityIndicator as ActivityIndicator2,
|
|
3625
4706
|
StyleSheet as StyleSheet10,
|
|
@@ -3677,11 +4758,11 @@ var CoverPreview = ({
|
|
|
3677
4758
|
onLoadEnd,
|
|
3678
4759
|
onError
|
|
3679
4760
|
}) => {
|
|
3680
|
-
const [engine, setEngine] =
|
|
3681
|
-
const [layoutReady, setLayoutReady] =
|
|
3682
|
-
const [loaded, setLoaded] =
|
|
3683
|
-
const [loading, setLoading] =
|
|
3684
|
-
const [error, setError] =
|
|
4761
|
+
const [engine, setEngine] = useState6(null);
|
|
4762
|
+
const [layoutReady, setLayoutReady] = useState6(false);
|
|
4763
|
+
const [loaded, setLoaded] = useState6(false);
|
|
4764
|
+
const [loading, setLoading] = useState6(false);
|
|
4765
|
+
const [error, setError] = useState6(null);
|
|
3685
4766
|
const viewRef = useRef5(null);
|
|
3686
4767
|
const resolvedType = useMemo5(() => inferDocumentType(source, type), [source, type]);
|
|
3687
4768
|
const isPdf = resolvedType === "pdf";
|