@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.js
CHANGED
|
@@ -625,6 +625,190 @@ var import_react = require("react");
|
|
|
625
625
|
var import_react_native = require("react-native");
|
|
626
626
|
var import_core = require("@papyrus-sdk/core");
|
|
627
627
|
var import_engine_native = require("@papyrus-sdk/engine-native");
|
|
628
|
+
|
|
629
|
+
// perf/mobilePerf.ts
|
|
630
|
+
var DEFAULT_PREFIX = "[Papyrus Perf]";
|
|
631
|
+
var FRAME_BUDGET_MS = 1e3 / 60;
|
|
632
|
+
var getPerfGlobal = () => globalThis.__PAPYRUS_MOBILE_PERF__;
|
|
633
|
+
var getPerfConfig = () => {
|
|
634
|
+
const value = getPerfGlobal();
|
|
635
|
+
if (value === true) {
|
|
636
|
+
return {
|
|
637
|
+
enabled: true,
|
|
638
|
+
sampleMemory: true,
|
|
639
|
+
logPrefix: DEFAULT_PREFIX,
|
|
640
|
+
verbose: false
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
if (!value || typeof value !== "object") {
|
|
644
|
+
return {
|
|
645
|
+
enabled: false,
|
|
646
|
+
sampleMemory: false,
|
|
647
|
+
logPrefix: DEFAULT_PREFIX,
|
|
648
|
+
verbose: false
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
enabled: value.enabled ?? true,
|
|
653
|
+
sampleMemory: value.sampleMemory ?? true,
|
|
654
|
+
logPrefix: value.logPrefix ?? DEFAULT_PREFIX,
|
|
655
|
+
verbose: value.verbose ?? false
|
|
656
|
+
};
|
|
657
|
+
};
|
|
658
|
+
var round = (value) => Math.round(value * 100) / 100;
|
|
659
|
+
var bytesToMb = (bytes) => round(bytes / (1024 * 1024));
|
|
660
|
+
var getNumericValue = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
661
|
+
var readHermesHeapBytes = () => {
|
|
662
|
+
const runtimeProperties = globalThis.HermesInternal?.getRuntimeProperties?.();
|
|
663
|
+
if (!runtimeProperties || typeof runtimeProperties !== "object") return null;
|
|
664
|
+
const candidates = [
|
|
665
|
+
"JSHeapSize",
|
|
666
|
+
"js_heap_size",
|
|
667
|
+
"HeapSize",
|
|
668
|
+
"heapSize",
|
|
669
|
+
"TotalAllocatedBytes",
|
|
670
|
+
"totalAllocatedBytes",
|
|
671
|
+
"mallocSize"
|
|
672
|
+
];
|
|
673
|
+
for (const key of candidates) {
|
|
674
|
+
const value = getNumericValue(runtimeProperties[key]);
|
|
675
|
+
if (value !== null) return value;
|
|
676
|
+
}
|
|
677
|
+
return null;
|
|
678
|
+
};
|
|
679
|
+
var logPerf = (scope, event, payload) => {
|
|
680
|
+
const config = getPerfConfig();
|
|
681
|
+
if (!config.enabled) return;
|
|
682
|
+
const line = `${config.logPrefix}[${scope}] ${event}`;
|
|
683
|
+
if (payload) {
|
|
684
|
+
console.log(line, payload);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
console.log(line);
|
|
688
|
+
};
|
|
689
|
+
var isMobilePerfEnabled = () => getPerfConfig().enabled;
|
|
690
|
+
var perfNow = () => {
|
|
691
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
692
|
+
return performance.now();
|
|
693
|
+
}
|
|
694
|
+
return Date.now();
|
|
695
|
+
};
|
|
696
|
+
var sampleMemory = (scope, event, payload) => {
|
|
697
|
+
const config = getPerfConfig();
|
|
698
|
+
if (!config.enabled || !config.sampleMemory) return;
|
|
699
|
+
const performanceMemory = globalThis.performance?.memory;
|
|
700
|
+
const jsHeapUsedBytes = getNumericValue(performanceMemory?.usedJSHeapSize);
|
|
701
|
+
const jsHeapTotalBytes = getNumericValue(performanceMemory?.totalJSHeapSize);
|
|
702
|
+
const hermesHeapBytes = readHermesHeapBytes();
|
|
703
|
+
if (jsHeapUsedBytes === null && jsHeapTotalBytes === null && hermesHeapBytes === null) return;
|
|
704
|
+
logPerf(scope, `memory.${event}`, {
|
|
705
|
+
...payload,
|
|
706
|
+
jsHeapUsedMb: jsHeapUsedBytes === null ? void 0 : bytesToMb(jsHeapUsedBytes),
|
|
707
|
+
jsHeapTotalMb: jsHeapTotalBytes === null ? void 0 : bytesToMb(jsHeapTotalBytes),
|
|
708
|
+
hermesHeapMb: hermesHeapBytes === null ? void 0 : bytesToMb(hermesHeapBytes)
|
|
709
|
+
});
|
|
710
|
+
};
|
|
711
|
+
var createBurstMonitor = (scope, label, threshold = 12, windowMs = 1e3) => {
|
|
712
|
+
let windowStart = 0;
|
|
713
|
+
let calls = 0;
|
|
714
|
+
return (payload) => {
|
|
715
|
+
const config = getPerfConfig();
|
|
716
|
+
if (!config.enabled) return;
|
|
717
|
+
const now = perfNow();
|
|
718
|
+
if (windowStart === 0 || now - windowStart > windowMs) {
|
|
719
|
+
windowStart = now;
|
|
720
|
+
calls = 0;
|
|
721
|
+
}
|
|
722
|
+
calls += 1;
|
|
723
|
+
if (calls === threshold || config.verbose) {
|
|
724
|
+
logPerf(scope, `${label}.burst`, {
|
|
725
|
+
calls,
|
|
726
|
+
windowMs: round(now - windowStart),
|
|
727
|
+
...payload
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
};
|
|
732
|
+
var createRenderCounter = (scope, label = "render", reportEvery = 30) => {
|
|
733
|
+
let count = 0;
|
|
734
|
+
return (payload) => {
|
|
735
|
+
const config = getPerfConfig();
|
|
736
|
+
if (!config.enabled) return;
|
|
737
|
+
count += 1;
|
|
738
|
+
if (count === 1 || count % reportEvery === 0 || config.verbose) {
|
|
739
|
+
logPerf(scope, label, {
|
|
740
|
+
count,
|
|
741
|
+
...payload
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
};
|
|
746
|
+
var createScrollPerfMonitor = (scope, label = "scroll") => {
|
|
747
|
+
let active = false;
|
|
748
|
+
let startAt = 0;
|
|
749
|
+
let lastEventAt = 0;
|
|
750
|
+
let sampleEvents = 0;
|
|
751
|
+
let droppedFrames = 0;
|
|
752
|
+
let maxFrameGapMs = 0;
|
|
753
|
+
const reset = () => {
|
|
754
|
+
active = false;
|
|
755
|
+
startAt = 0;
|
|
756
|
+
lastEventAt = 0;
|
|
757
|
+
sampleEvents = 0;
|
|
758
|
+
droppedFrames = 0;
|
|
759
|
+
maxFrameGapMs = 0;
|
|
760
|
+
};
|
|
761
|
+
return {
|
|
762
|
+
begin: (reason, payload) => {
|
|
763
|
+
const config = getPerfConfig();
|
|
764
|
+
if (!config.enabled) return;
|
|
765
|
+
if (active) return;
|
|
766
|
+
active = true;
|
|
767
|
+
startAt = perfNow();
|
|
768
|
+
if (reason && config.verbose) {
|
|
769
|
+
logPerf(scope, `${label}.begin`, {
|
|
770
|
+
reason,
|
|
771
|
+
...payload
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
track: (timestampMs) => {
|
|
776
|
+
if (!isMobilePerfEnabled()) return;
|
|
777
|
+
const now = typeof timestampMs === "number" && Number.isFinite(timestampMs) ? timestampMs : perfNow();
|
|
778
|
+
if (!active) {
|
|
779
|
+
active = true;
|
|
780
|
+
startAt = now;
|
|
781
|
+
}
|
|
782
|
+
if (lastEventAt > 0) {
|
|
783
|
+
const frameGap = Math.max(0, now - lastEventAt);
|
|
784
|
+
maxFrameGapMs = Math.max(maxFrameGapMs, frameGap);
|
|
785
|
+
droppedFrames += Math.max(0, Math.round(frameGap / FRAME_BUDGET_MS) - 1);
|
|
786
|
+
}
|
|
787
|
+
sampleEvents += 1;
|
|
788
|
+
lastEventAt = now;
|
|
789
|
+
},
|
|
790
|
+
end: (reason, payload) => {
|
|
791
|
+
if (!isMobilePerfEnabled() || !active) return;
|
|
792
|
+
const stopAt = lastEventAt || perfNow();
|
|
793
|
+
const durationMs = Math.max(0, stopAt - startAt);
|
|
794
|
+
const estimatedFrameTotal = Math.max(sampleEvents + droppedFrames, 1);
|
|
795
|
+
const fpsEstimate = durationMs > 0 ? sampleEvents * 1e3 / durationMs : 0;
|
|
796
|
+
logPerf(scope, `${label}.${reason}`, {
|
|
797
|
+
durationMs: round(durationMs),
|
|
798
|
+
sampleEvents,
|
|
799
|
+
droppedFrames,
|
|
800
|
+
droppedFramesPct: round(droppedFrames / estimatedFrameTotal * 100),
|
|
801
|
+
fpsEstimate: round(fpsEstimate),
|
|
802
|
+
maxFrameGapMs: round(maxFrameGapMs),
|
|
803
|
+
...payload
|
|
804
|
+
});
|
|
805
|
+
reset();
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
};
|
|
809
|
+
var logPerfEvent = logPerf;
|
|
810
|
+
|
|
811
|
+
// components/PageRenderer.tsx
|
|
628
812
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
629
813
|
var PageRenderer = ({
|
|
630
814
|
engine,
|
|
@@ -641,6 +825,11 @@ var PageRenderer = ({
|
|
|
641
825
|
const { width: windowWidth } = (0, import_react_native.useWindowDimensions)();
|
|
642
826
|
const isAndroid = import_react_native.Platform.OS === "android";
|
|
643
827
|
const isNative = import_react_native.Platform.OS === "android" || import_react_native.Platform.OS === "ios";
|
|
828
|
+
const perfEnabled = isMobilePerfEnabled();
|
|
829
|
+
const renderCountRef = (0, import_react.useRef)(0);
|
|
830
|
+
const setStateBurstRef = (0, import_react.useRef)(
|
|
831
|
+
createBurstMonitor("PageRenderer", "setDocumentState", 18, 700)
|
|
832
|
+
);
|
|
644
833
|
const {
|
|
645
834
|
zoom,
|
|
646
835
|
rotation,
|
|
@@ -657,6 +846,19 @@ var PageRenderer = ({
|
|
|
657
846
|
activeSearchIndex,
|
|
658
847
|
setSelectionActive
|
|
659
848
|
} = (0, import_core.useViewerStore)();
|
|
849
|
+
const setDocumentStateTracked = (0, import_react.useCallback)(
|
|
850
|
+
(state, reason) => {
|
|
851
|
+
if (perfEnabled) {
|
|
852
|
+
setStateBurstRef.current({
|
|
853
|
+
reason,
|
|
854
|
+
page: pageIndex + 1,
|
|
855
|
+
keys: Object.keys(state).join(",")
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
setDocumentState(state);
|
|
859
|
+
},
|
|
860
|
+
[pageIndex, perfEnabled, setDocumentState]
|
|
861
|
+
);
|
|
660
862
|
const pageAnnotations = (0, import_react.useMemo)(
|
|
661
863
|
() => annotations.filter((ann) => ann.pageIndex === pageIndex),
|
|
662
864
|
[annotations, pageIndex]
|
|
@@ -665,6 +867,17 @@ var PageRenderer = ({
|
|
|
665
867
|
() => searchResults.map((result, index) => ({ result, index })).filter(({ result }) => result.pageIndex === pageIndex),
|
|
666
868
|
[searchResults, pageIndex]
|
|
667
869
|
);
|
|
870
|
+
renderCountRef.current += 1;
|
|
871
|
+
if (perfEnabled && (renderCountRef.current === 1 || renderCountRef.current % 20 === 0)) {
|
|
872
|
+
logPerfEvent("PageRenderer", "render", {
|
|
873
|
+
page: pageIndex + 1,
|
|
874
|
+
renderCount: renderCountRef.current,
|
|
875
|
+
zoom,
|
|
876
|
+
rotation,
|
|
877
|
+
annotationCount: pageAnnotations.length,
|
|
878
|
+
searchHits: pageSearchHits.length
|
|
879
|
+
});
|
|
880
|
+
}
|
|
668
881
|
const [selectionRect, setSelectionRect] = (0, import_react.useState)(null);
|
|
669
882
|
const [selectionRects, setSelectionRects] = (0, import_react.useState)([]);
|
|
670
883
|
const [selectionBounds, setSelectionBounds] = (0, import_react.useState)(null);
|
|
@@ -674,30 +887,72 @@ var PageRenderer = ({
|
|
|
674
887
|
const selectionRectRef = (0, import_react.useRef)(null);
|
|
675
888
|
const selectionBoundsRef = (0, import_react.useRef)(null);
|
|
676
889
|
const selectionBoundsStart = (0, import_react.useRef)(null);
|
|
677
|
-
const lastTapRef = (0, import_react.useRef)(
|
|
890
|
+
const lastTapRef = (0, import_react.useRef)(
|
|
891
|
+
null
|
|
892
|
+
);
|
|
678
893
|
const pinchRef = (0, import_react.useRef)(null);
|
|
679
894
|
(0, import_react.useEffect)(() => {
|
|
680
895
|
if (!layout.width || !layout.height) return;
|
|
681
896
|
const viewTag = (0, import_react_native.findNodeHandle)(viewRef.current);
|
|
682
897
|
if (viewTag) {
|
|
683
898
|
const renderScale = isNative ? scale / Math.max(zoom, 0.5) : scale;
|
|
684
|
-
|
|
899
|
+
const startedAt = perfEnabled ? perfNow() : 0;
|
|
900
|
+
void Promise.resolve(engine.renderPage(pageIndex, viewTag, renderScale)).then(() => {
|
|
901
|
+
if (!perfEnabled) return;
|
|
902
|
+
const renderDurationMs = perfNow() - startedAt;
|
|
903
|
+
if (renderDurationMs >= 40) {
|
|
904
|
+
logPerfEvent("PageRenderer", "renderPage.slow", {
|
|
905
|
+
page: pageIndex + 1,
|
|
906
|
+
renderDurationMs: Math.round(renderDurationMs * 100) / 100,
|
|
907
|
+
layoutWidth: layout.width,
|
|
908
|
+
layoutHeight: layout.height,
|
|
909
|
+
renderScale: Math.round(renderScale * 100) / 100
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
}).catch((error) => {
|
|
913
|
+
logPerfEvent("PageRenderer", "renderPage.error", {
|
|
914
|
+
page: pageIndex + 1,
|
|
915
|
+
message: error instanceof Error ? error.message : String(error)
|
|
916
|
+
});
|
|
917
|
+
});
|
|
685
918
|
}
|
|
686
|
-
}, [
|
|
919
|
+
}, [
|
|
920
|
+
engine,
|
|
921
|
+
pageIndex,
|
|
922
|
+
scale,
|
|
923
|
+
zoom,
|
|
924
|
+
rotation,
|
|
925
|
+
layout.width,
|
|
926
|
+
layout.height,
|
|
927
|
+
isNative,
|
|
928
|
+
perfEnabled
|
|
929
|
+
]);
|
|
687
930
|
(0, import_react.useEffect)(() => {
|
|
688
931
|
let active = true;
|
|
689
932
|
const loadDimensions = async () => {
|
|
933
|
+
const startedAt = perfEnabled ? perfNow() : 0;
|
|
690
934
|
const dims = await engine.getPageDimensions(pageIndex);
|
|
691
935
|
if (!active) return;
|
|
692
936
|
if (dims.width > 0 && dims.height > 0) {
|
|
693
937
|
setPageSize({ width: dims.width, height: dims.height });
|
|
694
938
|
}
|
|
939
|
+
if (perfEnabled) {
|
|
940
|
+
const durationMs = perfNow() - startedAt;
|
|
941
|
+
if (durationMs >= 20 || pageIndex === 0) {
|
|
942
|
+
logPerfEvent("PageRenderer", "pageDimensions", {
|
|
943
|
+
page: pageIndex + 1,
|
|
944
|
+
durationMs: Math.round(durationMs * 100) / 100,
|
|
945
|
+
width: dims.width,
|
|
946
|
+
height: dims.height
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
}
|
|
695
950
|
};
|
|
696
951
|
void loadDimensions();
|
|
697
952
|
return () => {
|
|
698
953
|
active = false;
|
|
699
954
|
};
|
|
700
|
-
}, [engine, pageIndex]);
|
|
955
|
+
}, [engine, pageIndex, perfEnabled]);
|
|
701
956
|
const handleLayout = (event) => {
|
|
702
957
|
const { width, height } = event.nativeEvent.layout;
|
|
703
958
|
if (width !== layout.width || height !== layout.height) {
|
|
@@ -797,7 +1052,7 @@ var PageRenderer = ({
|
|
|
797
1052
|
if (!distance) return;
|
|
798
1053
|
const scale2 = distance / pinchRef.current.distance;
|
|
799
1054
|
const nextZoom = clamp(pinchRef.current.zoom * scale2, 0.5, 4);
|
|
800
|
-
|
|
1055
|
+
setDocumentStateTracked({ zoom: nextZoom }, "pinchMove");
|
|
801
1056
|
engine.setZoom(nextZoom);
|
|
802
1057
|
};
|
|
803
1058
|
const handlePinchEnd = () => {
|
|
@@ -845,7 +1100,12 @@ var PageRenderer = ({
|
|
|
845
1100
|
const top = Math.max(0, Math.min(start.y, currentY));
|
|
846
1101
|
const right = Math.min(layout.width, Math.max(start.x, currentX));
|
|
847
1102
|
const bottom = Math.min(layout.height, Math.max(start.y, currentY));
|
|
848
|
-
const rect = {
|
|
1103
|
+
const rect = {
|
|
1104
|
+
x: left,
|
|
1105
|
+
y: top,
|
|
1106
|
+
width: right - left,
|
|
1107
|
+
height: bottom - top
|
|
1108
|
+
};
|
|
849
1109
|
selectionRectRef.current = rect;
|
|
850
1110
|
setSelectionRect(rect);
|
|
851
1111
|
},
|
|
@@ -941,7 +1201,13 @@ var PageRenderer = ({
|
|
|
941
1201
|
if (selectionRects.length === 0) return;
|
|
942
1202
|
if (type === "comment") {
|
|
943
1203
|
const first = selectionRects[0];
|
|
944
|
-
addAnnotationAt(
|
|
1204
|
+
addAnnotationAt(
|
|
1205
|
+
first.x,
|
|
1206
|
+
first.y,
|
|
1207
|
+
Math.max(0.08, first.width),
|
|
1208
|
+
Math.max(0.06, first.height),
|
|
1209
|
+
"comment"
|
|
1210
|
+
);
|
|
945
1211
|
clearSelection();
|
|
946
1212
|
return;
|
|
947
1213
|
}
|
|
@@ -975,12 +1241,18 @@ var PageRenderer = ({
|
|
|
975
1241
|
horizontal: true,
|
|
976
1242
|
scrollEnabled,
|
|
977
1243
|
showsHorizontalScrollIndicator: false,
|
|
978
|
-
contentContainerStyle: [
|
|
1244
|
+
contentContainerStyle: [
|
|
1245
|
+
styles.scrollContent,
|
|
1246
|
+
{ paddingHorizontal: horizontalPadding }
|
|
1247
|
+
],
|
|
979
1248
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
980
1249
|
import_react_native.Pressable,
|
|
981
1250
|
{
|
|
982
1251
|
...panResponder.panHandlers,
|
|
983
|
-
style: [
|
|
1252
|
+
style: [
|
|
1253
|
+
styles.container,
|
|
1254
|
+
{ width: pageWidth, height: pageHeight, marginBottom: spacing }
|
|
1255
|
+
],
|
|
984
1256
|
onLayout: handleLayout,
|
|
985
1257
|
onPress: handlePress,
|
|
986
1258
|
onStartShouldSetResponder: (event) => shouldHandlePinch(event.nativeEvent.touches),
|
|
@@ -991,7 +1263,13 @@ var PageRenderer = ({
|
|
|
991
1263
|
onResponderTerminate: handlePinchEnd,
|
|
992
1264
|
children: [
|
|
993
1265
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageViewComponent, { ref: viewRef, style: styles.page }),
|
|
994
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1267
|
+
import_react_native.View,
|
|
1268
|
+
{
|
|
1269
|
+
pointerEvents: "none",
|
|
1270
|
+
style: [styles.themeOverlay, themeOverlayStyle]
|
|
1271
|
+
}
|
|
1272
|
+
),
|
|
995
1273
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { pointerEvents: "box-none", style: styles.selectionLayer, children: [
|
|
996
1274
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { pointerEvents: "none", children: selectionRects.map((rect, index) => {
|
|
997
1275
|
const style = {
|
|
@@ -1000,7 +1278,13 @@ var PageRenderer = ({
|
|
|
1000
1278
|
width: `${rect.width * 100}%`,
|
|
1001
1279
|
height: `${rect.height * 100}%`
|
|
1002
1280
|
};
|
|
1003
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1282
|
+
import_react_native.View,
|
|
1283
|
+
{
|
|
1284
|
+
style: [styles.selectionHighlight, style]
|
|
1285
|
+
},
|
|
1286
|
+
`sel-${index}`
|
|
1287
|
+
);
|
|
1004
1288
|
}) }),
|
|
1005
1289
|
selectionBoundsPx ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1006
1290
|
import_react_native.View,
|
|
@@ -1021,14 +1305,20 @@ var PageRenderer = ({
|
|
|
1021
1305
|
import_react_native.View,
|
|
1022
1306
|
{
|
|
1023
1307
|
...startHandleResponder.panHandlers,
|
|
1024
|
-
style: [
|
|
1308
|
+
style: [
|
|
1309
|
+
styles.selectionHandle,
|
|
1310
|
+
{ left: -8, top: -8, borderColor: accentColor }
|
|
1311
|
+
]
|
|
1025
1312
|
}
|
|
1026
1313
|
),
|
|
1027
1314
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1028
1315
|
import_react_native.View,
|
|
1029
1316
|
{
|
|
1030
1317
|
...endHandleResponder.panHandlers,
|
|
1031
|
-
style: [
|
|
1318
|
+
style: [
|
|
1319
|
+
styles.selectionHandle,
|
|
1320
|
+
{ right: -8, bottom: -8, borderColor: accentColor }
|
|
1321
|
+
]
|
|
1032
1322
|
}
|
|
1033
1323
|
)
|
|
1034
1324
|
]
|
|
@@ -1067,9 +1357,15 @@ var PageRenderer = ({
|
|
|
1067
1357
|
{
|
|
1068
1358
|
style: [
|
|
1069
1359
|
styles.searchHighlight,
|
|
1070
|
-
{
|
|
1360
|
+
{
|
|
1361
|
+
borderColor: accentColor,
|
|
1362
|
+
backgroundColor: `${accentColor}26`
|
|
1363
|
+
},
|
|
1071
1364
|
isActive && styles.searchHighlightActive,
|
|
1072
|
-
isActive && {
|
|
1365
|
+
isActive && {
|
|
1366
|
+
borderColor: accentColor,
|
|
1367
|
+
backgroundColor: `${accentColor}40`
|
|
1368
|
+
},
|
|
1073
1369
|
highlightStyle
|
|
1074
1370
|
]
|
|
1075
1371
|
},
|
|
@@ -1099,8 +1395,29 @@ var PageRenderer = ({
|
|
|
1099
1395
|
isSelected && { borderColor: accentColor }
|
|
1100
1396
|
],
|
|
1101
1397
|
children: [
|
|
1102
|
-
(ann.type === "comment" || ann.type === "text") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1103
|
-
|
|
1398
|
+
(ann.type === "comment" || ann.type === "text") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1399
|
+
import_react_native.View,
|
|
1400
|
+
{
|
|
1401
|
+
style: [styles.annotationBadge, { borderColor: ann.color }],
|
|
1402
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1403
|
+
import_react_native.View,
|
|
1404
|
+
{
|
|
1405
|
+
style: [
|
|
1406
|
+
styles.annotationDot,
|
|
1407
|
+
{ backgroundColor: ann.color }
|
|
1408
|
+
]
|
|
1409
|
+
}
|
|
1410
|
+
)
|
|
1411
|
+
}
|
|
1412
|
+
),
|
|
1413
|
+
isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1414
|
+
import_react_native.Pressable,
|
|
1415
|
+
{
|
|
1416
|
+
onPress: () => removeAnnotation(ann.id),
|
|
1417
|
+
style: styles.deleteButton,
|
|
1418
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.deleteDot })
|
|
1419
|
+
}
|
|
1420
|
+
)
|
|
1104
1421
|
]
|
|
1105
1422
|
},
|
|
1106
1423
|
ann.id
|
|
@@ -1118,9 +1435,46 @@ var PageRenderer = ({
|
|
|
1118
1435
|
}
|
|
1119
1436
|
],
|
|
1120
1437
|
children: [
|
|
1121
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1122
|
-
|
|
1123
|
-
|
|
1438
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1439
|
+
import_react_native.Pressable,
|
|
1440
|
+
{
|
|
1441
|
+
onPress: () => applySelection("comment"),
|
|
1442
|
+
style: styles.selectionAction,
|
|
1443
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.selectionActionDot })
|
|
1444
|
+
}
|
|
1445
|
+
),
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1447
|
+
import_react_native.Pressable,
|
|
1448
|
+
{
|
|
1449
|
+
onPress: () => applySelection("highlight"),
|
|
1450
|
+
style: styles.selectionAction,
|
|
1451
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1452
|
+
import_react_native.View,
|
|
1453
|
+
{
|
|
1454
|
+
style: [
|
|
1455
|
+
styles.selectionSwatch,
|
|
1456
|
+
{ backgroundColor: annotationColor }
|
|
1457
|
+
]
|
|
1458
|
+
}
|
|
1459
|
+
)
|
|
1460
|
+
}
|
|
1461
|
+
),
|
|
1462
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1463
|
+
import_react_native.Pressable,
|
|
1464
|
+
{
|
|
1465
|
+
onPress: () => applySelection("strikeout"),
|
|
1466
|
+
style: [styles.selectionAction, styles.selectionActionLast],
|
|
1467
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1468
|
+
import_react_native.View,
|
|
1469
|
+
{
|
|
1470
|
+
style: [
|
|
1471
|
+
styles.selectionStrike,
|
|
1472
|
+
{ backgroundColor: annotationColor }
|
|
1473
|
+
]
|
|
1474
|
+
}
|
|
1475
|
+
)
|
|
1476
|
+
}
|
|
1477
|
+
)
|
|
1124
1478
|
]
|
|
1125
1479
|
}
|
|
1126
1480
|
) : null
|
|
@@ -1418,8 +1772,27 @@ var WebViewViewer_default = WebViewViewer;
|
|
|
1418
1772
|
|
|
1419
1773
|
// components/Viewer.tsx
|
|
1420
1774
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1775
|
+
var LIST_TOP_PADDING = 18;
|
|
1776
|
+
var LIST_BOTTOM_PADDING = 120;
|
|
1777
|
+
var CONTINUOUS_PAGE_SPACING = 28;
|
|
1778
|
+
var DOUBLE_PAGE_SPACING = 20;
|
|
1779
|
+
var DEFAULT_PAGE_ASPECT_RATIO = 0.77;
|
|
1780
|
+
var FLATLIST_WINDOW_SIZE = 8;
|
|
1781
|
+
var FLATLIST_MAX_TO_RENDER_PER_BATCH = 6;
|
|
1782
|
+
var FLATLIST_UPDATE_CELLS_BATCHING_PERIOD = 40;
|
|
1783
|
+
var FLATLIST_INITIAL_NUM_TO_RENDER = 6;
|
|
1784
|
+
var SCROLL_RETRY_DELAY_MS = 120;
|
|
1785
|
+
var SCROLL_MAX_RETRIES = 10;
|
|
1421
1786
|
var Viewer = ({ engine }) => {
|
|
1422
|
-
const {
|
|
1787
|
+
const {
|
|
1788
|
+
pageCount,
|
|
1789
|
+
currentPage,
|
|
1790
|
+
scrollToPageSignal,
|
|
1791
|
+
setDocumentState,
|
|
1792
|
+
uiTheme,
|
|
1793
|
+
viewMode,
|
|
1794
|
+
zoom
|
|
1795
|
+
} = (0, import_core3.useViewerStore)();
|
|
1423
1796
|
const listRef = (0, import_react3.useRef)(null);
|
|
1424
1797
|
const isDark = uiTheme === "dark";
|
|
1425
1798
|
const { width: windowWidth } = (0, import_react_native3.useWindowDimensions)();
|
|
@@ -1427,7 +1800,38 @@ var Viewer = ({ engine }) => {
|
|
|
1427
1800
|
const isSingle = viewMode === "single";
|
|
1428
1801
|
const renderTargetType = engine.getRenderTargetType?.() ?? "canvas";
|
|
1429
1802
|
const isWebView = renderTargetType === "webview";
|
|
1430
|
-
const
|
|
1803
|
+
const perfEnabled = isMobilePerfEnabled();
|
|
1804
|
+
const mountedAtRef = (0, import_react3.useRef)(perfNow());
|
|
1805
|
+
const readyLoggedRef = (0, import_react3.useRef)(false);
|
|
1806
|
+
const renderCounterRef = (0, import_react3.useRef)(createRenderCounter("Viewer", "render", 40));
|
|
1807
|
+
const setStateBurstRef = (0, import_react3.useRef)(
|
|
1808
|
+
createBurstMonitor("Viewer", "setDocumentState", 12, 800)
|
|
1809
|
+
);
|
|
1810
|
+
const viewableBurstRef = (0, import_react3.useRef)(
|
|
1811
|
+
createBurstMonitor("Viewer", "onViewableItemsChanged", 12, 800)
|
|
1812
|
+
);
|
|
1813
|
+
const scrollMonitorRef = (0, import_react3.useRef)(createScrollPerfMonitor("Viewer"));
|
|
1814
|
+
const dimensionsCacheRef = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
1815
|
+
const dimensionsPendingRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
|
|
1816
|
+
const layoutRefreshTimeoutRef = (0, import_react3.useRef)(
|
|
1817
|
+
null
|
|
1818
|
+
);
|
|
1819
|
+
const pendingScrollIndexRef = (0, import_react3.useRef)(null);
|
|
1820
|
+
const pendingScrollAttemptsRef = (0, import_react3.useRef)(0);
|
|
1821
|
+
const pendingScrollTimeoutRef = (0, import_react3.useRef)(
|
|
1822
|
+
null
|
|
1823
|
+
);
|
|
1824
|
+
const [layoutRevision, setLayoutRevision] = (0, import_react3.useState)(0);
|
|
1825
|
+
renderCounterRef.current({
|
|
1826
|
+
pageCount,
|
|
1827
|
+
currentPage,
|
|
1828
|
+
viewMode,
|
|
1829
|
+
renderTargetType
|
|
1830
|
+
});
|
|
1831
|
+
const pages = (0, import_react3.useMemo)(
|
|
1832
|
+
() => Array.from({ length: pageCount }).map((_, i) => i),
|
|
1833
|
+
[pageCount]
|
|
1834
|
+
);
|
|
1431
1835
|
const rows = (0, import_react3.useMemo)(() => {
|
|
1432
1836
|
if (!isDouble) return [];
|
|
1433
1837
|
const result = [];
|
|
@@ -1436,48 +1840,354 @@ var Viewer = ({ engine }) => {
|
|
|
1436
1840
|
}
|
|
1437
1841
|
return result;
|
|
1438
1842
|
}, [isDouble, pageCount]);
|
|
1843
|
+
const scheduleLayoutRefresh = (0, import_react3.useCallback)(() => {
|
|
1844
|
+
if (layoutRefreshTimeoutRef.current) return;
|
|
1845
|
+
layoutRefreshTimeoutRef.current = setTimeout(() => {
|
|
1846
|
+
layoutRefreshTimeoutRef.current = null;
|
|
1847
|
+
setLayoutRevision((value) => value + 1);
|
|
1848
|
+
}, 120);
|
|
1849
|
+
}, []);
|
|
1850
|
+
const clearPendingScrollRetry = (0, import_react3.useCallback)(() => {
|
|
1851
|
+
if (pendingScrollTimeoutRef.current) {
|
|
1852
|
+
clearTimeout(pendingScrollTimeoutRef.current);
|
|
1853
|
+
pendingScrollTimeoutRef.current = null;
|
|
1854
|
+
}
|
|
1855
|
+
}, []);
|
|
1856
|
+
const clearPendingScrollTarget = (0, import_react3.useCallback)(() => {
|
|
1857
|
+
pendingScrollIndexRef.current = null;
|
|
1858
|
+
pendingScrollAttemptsRef.current = 0;
|
|
1859
|
+
clearPendingScrollRetry();
|
|
1860
|
+
}, [clearPendingScrollRetry]);
|
|
1861
|
+
const scheduleScrollRetry = (0, import_react3.useCallback)(
|
|
1862
|
+
(reason) => {
|
|
1863
|
+
const pendingIndex = pendingScrollIndexRef.current;
|
|
1864
|
+
if (pendingIndex === null) return;
|
|
1865
|
+
if (pendingScrollAttemptsRef.current >= SCROLL_MAX_RETRIES) {
|
|
1866
|
+
if (perfEnabled) {
|
|
1867
|
+
logPerfEvent("Viewer", "scroll.retry.giveup", {
|
|
1868
|
+
reason,
|
|
1869
|
+
targetIndex: pendingIndex,
|
|
1870
|
+
attempts: pendingScrollAttemptsRef.current
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
clearPendingScrollTarget();
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
clearPendingScrollRetry();
|
|
1877
|
+
pendingScrollTimeoutRef.current = setTimeout(() => {
|
|
1878
|
+
pendingScrollTimeoutRef.current = null;
|
|
1879
|
+
const targetIndex = pendingScrollIndexRef.current;
|
|
1880
|
+
if (targetIndex === null) return;
|
|
1881
|
+
pendingScrollAttemptsRef.current += 1;
|
|
1882
|
+
listRef.current?.scrollToIndex({
|
|
1883
|
+
index: targetIndex,
|
|
1884
|
+
animated: false,
|
|
1885
|
+
viewPosition: 0
|
|
1886
|
+
});
|
|
1887
|
+
if (perfEnabled) {
|
|
1888
|
+
logPerfEvent("Viewer", "scroll.retry", {
|
|
1889
|
+
reason,
|
|
1890
|
+
targetIndex,
|
|
1891
|
+
attempt: pendingScrollAttemptsRef.current
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
}, SCROLL_RETRY_DELAY_MS);
|
|
1895
|
+
},
|
|
1896
|
+
[clearPendingScrollRetry, clearPendingScrollTarget, perfEnabled]
|
|
1897
|
+
);
|
|
1898
|
+
(0, import_react3.useEffect)(
|
|
1899
|
+
() => () => {
|
|
1900
|
+
if (layoutRefreshTimeoutRef.current) {
|
|
1901
|
+
clearTimeout(layoutRefreshTimeoutRef.current);
|
|
1902
|
+
}
|
|
1903
|
+
clearPendingScrollRetry();
|
|
1904
|
+
},
|
|
1905
|
+
[clearPendingScrollRetry]
|
|
1906
|
+
);
|
|
1907
|
+
const ensurePageDimensions = (0, import_react3.useCallback)(
|
|
1908
|
+
(pageIndex) => {
|
|
1909
|
+
if (pageIndex < 0 || pageIndex >= pageCount) return;
|
|
1910
|
+
if (dimensionsCacheRef.current.has(pageIndex)) return;
|
|
1911
|
+
if (dimensionsPendingRef.current.has(pageIndex)) return;
|
|
1912
|
+
dimensionsPendingRef.current.add(pageIndex);
|
|
1913
|
+
void engine.getPageDimensions(pageIndex).then((dims) => {
|
|
1914
|
+
if (dims.width <= 0 || dims.height <= 0) return;
|
|
1915
|
+
const previous = dimensionsCacheRef.current.get(pageIndex);
|
|
1916
|
+
if (previous && previous.width === dims.width && previous.height === dims.height) {
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
dimensionsCacheRef.current.set(pageIndex, {
|
|
1920
|
+
width: dims.width,
|
|
1921
|
+
height: dims.height
|
|
1922
|
+
});
|
|
1923
|
+
scheduleLayoutRefresh();
|
|
1924
|
+
}).catch((error) => {
|
|
1925
|
+
if (!perfEnabled) return;
|
|
1926
|
+
logPerfEvent("Viewer", "pageDimensions.error", {
|
|
1927
|
+
page: pageIndex + 1,
|
|
1928
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1929
|
+
});
|
|
1930
|
+
}).finally(() => {
|
|
1931
|
+
dimensionsPendingRef.current.delete(pageIndex);
|
|
1932
|
+
});
|
|
1933
|
+
},
|
|
1934
|
+
[engine, pageCount, perfEnabled, scheduleLayoutRefresh]
|
|
1935
|
+
);
|
|
1936
|
+
const getPageAspectRatio = (0, import_react3.useCallback)((pageIndex) => {
|
|
1937
|
+
const dims = dimensionsCacheRef.current.get(pageIndex);
|
|
1938
|
+
if (!dims || dims.width <= 0 || dims.height <= 0) {
|
|
1939
|
+
return DEFAULT_PAGE_ASPECT_RATIO;
|
|
1940
|
+
}
|
|
1941
|
+
return dims.width / dims.height;
|
|
1942
|
+
}, []);
|
|
1943
|
+
(0, import_react3.useEffect)(() => {
|
|
1944
|
+
if (!perfEnabled) return;
|
|
1945
|
+
logPerfEvent("Viewer", "mount", { viewMode, renderTargetType });
|
|
1946
|
+
sampleMemory("Viewer", "mount", { pageCount });
|
|
1947
|
+
return () => {
|
|
1948
|
+
logPerfEvent("Viewer", "unmount");
|
|
1949
|
+
};
|
|
1950
|
+
}, [perfEnabled]);
|
|
1951
|
+
(0, import_react3.useEffect)(() => {
|
|
1952
|
+
if (!perfEnabled || readyLoggedRef.current || pageCount <= 0) return;
|
|
1953
|
+
readyLoggedRef.current = true;
|
|
1954
|
+
logPerfEvent("Viewer", "document.ready", {
|
|
1955
|
+
pageCount,
|
|
1956
|
+
initialLoadMs: Math.round((perfNow() - mountedAtRef.current) * 100) / 100
|
|
1957
|
+
});
|
|
1958
|
+
sampleMemory("Viewer", "document.ready", { pageCount });
|
|
1959
|
+
}, [pageCount, perfEnabled]);
|
|
1960
|
+
(0, import_react3.useEffect)(() => {
|
|
1961
|
+
if (isWebView || isSingle || pageCount <= 0) return;
|
|
1962
|
+
const warmupCount = Math.min(pageCount, 12);
|
|
1963
|
+
for (let i = 0; i < warmupCount; i += 1) {
|
|
1964
|
+
ensurePageDimensions(i);
|
|
1965
|
+
}
|
|
1966
|
+
}, [ensurePageDimensions, isSingle, isWebView, pageCount]);
|
|
1967
|
+
const setDocumentStateTracked = (0, import_react3.useCallback)(
|
|
1968
|
+
(state, reason) => {
|
|
1969
|
+
if (perfEnabled) {
|
|
1970
|
+
setStateBurstRef.current({
|
|
1971
|
+
reason,
|
|
1972
|
+
keys: Object.keys(state).join(",")
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
setDocumentState(state);
|
|
1976
|
+
},
|
|
1977
|
+
[perfEnabled, setDocumentState]
|
|
1978
|
+
);
|
|
1979
|
+
const columnGap = 12;
|
|
1980
|
+
const horizontalPadding = 16;
|
|
1981
|
+
const columnWidth = isDouble ? (windowWidth - horizontalPadding * 2 - columnGap) / 2 : windowWidth;
|
|
1982
|
+
const listLayoutMetrics = (0, import_react3.useMemo)(() => {
|
|
1983
|
+
const offsets = [];
|
|
1984
|
+
const lengths = [];
|
|
1985
|
+
let offset = LIST_TOP_PADDING;
|
|
1986
|
+
if (isDouble) {
|
|
1987
|
+
const safeZoom2 = Math.max(zoom, 0.25);
|
|
1988
|
+
const pageWidth2 = columnWidth * 0.92 * safeZoom2;
|
|
1989
|
+
const estimatedLength2 = pageWidth2 / DEFAULT_PAGE_ASPECT_RATIO + DOUBLE_PAGE_SPACING;
|
|
1990
|
+
for (let i = 0; i < rows.length; i += 1) {
|
|
1991
|
+
const row = rows[i];
|
|
1992
|
+
const leftRatio = getPageAspectRatio(row.left);
|
|
1993
|
+
const rightRatio = row.right === null ? leftRatio : getPageAspectRatio(row.right);
|
|
1994
|
+
const leftLength = pageWidth2 / leftRatio + DOUBLE_PAGE_SPACING;
|
|
1995
|
+
const rightLength = pageWidth2 / rightRatio + DOUBLE_PAGE_SPACING;
|
|
1996
|
+
const rowLength = Math.max(leftLength, rightLength);
|
|
1997
|
+
offsets.push(offset);
|
|
1998
|
+
lengths.push(rowLength);
|
|
1999
|
+
offset += rowLength;
|
|
2000
|
+
}
|
|
2001
|
+
return { offsets, lengths, estimatedLength: estimatedLength2 };
|
|
2002
|
+
}
|
|
2003
|
+
const safeZoom = Math.max(zoom, 0.25);
|
|
2004
|
+
const pageWidth = windowWidth * 0.92 * safeZoom;
|
|
2005
|
+
const estimatedLength = pageWidth / DEFAULT_PAGE_ASPECT_RATIO + CONTINUOUS_PAGE_SPACING;
|
|
2006
|
+
for (let i = 0; i < pageCount; i += 1) {
|
|
2007
|
+
const ratio = getPageAspectRatio(i);
|
|
2008
|
+
const length = pageWidth / ratio + CONTINUOUS_PAGE_SPACING;
|
|
2009
|
+
offsets.push(offset);
|
|
2010
|
+
lengths.push(length);
|
|
2011
|
+
offset += length;
|
|
2012
|
+
}
|
|
2013
|
+
return { offsets, lengths, estimatedLength };
|
|
2014
|
+
}, [
|
|
2015
|
+
columnWidth,
|
|
2016
|
+
getPageAspectRatio,
|
|
2017
|
+
isDouble,
|
|
2018
|
+
layoutRevision,
|
|
2019
|
+
pageCount,
|
|
2020
|
+
rows,
|
|
2021
|
+
windowWidth,
|
|
2022
|
+
zoom
|
|
2023
|
+
]);
|
|
2024
|
+
const getFallbackOffsetForIndex = (0, import_react3.useCallback)(
|
|
2025
|
+
(index) => {
|
|
2026
|
+
if (listLayoutMetrics.lengths.length === 0) {
|
|
2027
|
+
return LIST_TOP_PADDING;
|
|
2028
|
+
}
|
|
2029
|
+
const safeIndex = Math.max(
|
|
2030
|
+
0,
|
|
2031
|
+
Math.min(index, listLayoutMetrics.lengths.length - 1)
|
|
2032
|
+
);
|
|
2033
|
+
const cachedOffset = listLayoutMetrics.offsets[safeIndex];
|
|
2034
|
+
if (typeof cachedOffset === "number") return cachedOffset;
|
|
2035
|
+
return LIST_TOP_PADDING + listLayoutMetrics.estimatedLength * safeIndex;
|
|
2036
|
+
},
|
|
2037
|
+
[listLayoutMetrics]
|
|
2038
|
+
);
|
|
2039
|
+
const getItemLayout = (0, import_react3.useCallback)(
|
|
2040
|
+
(_, index) => {
|
|
2041
|
+
if (listLayoutMetrics.lengths.length === 0) {
|
|
2042
|
+
return {
|
|
2043
|
+
index,
|
|
2044
|
+
length: listLayoutMetrics.estimatedLength,
|
|
2045
|
+
offset: LIST_TOP_PADDING
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
const safeIndex = Math.max(
|
|
2049
|
+
0,
|
|
2050
|
+
Math.min(index, listLayoutMetrics.lengths.length - 1)
|
|
2051
|
+
);
|
|
2052
|
+
if (isDouble) {
|
|
2053
|
+
const row = rows[safeIndex];
|
|
2054
|
+
if (row) {
|
|
2055
|
+
ensurePageDimensions(row.left);
|
|
2056
|
+
if (row.right !== null) {
|
|
2057
|
+
ensurePageDimensions(row.right);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
} else {
|
|
2061
|
+
ensurePageDimensions(safeIndex);
|
|
2062
|
+
}
|
|
2063
|
+
const cachedLength = listLayoutMetrics.lengths[safeIndex];
|
|
2064
|
+
const cachedOffset = listLayoutMetrics.offsets[safeIndex];
|
|
2065
|
+
return {
|
|
2066
|
+
index,
|
|
2067
|
+
length: typeof cachedLength === "number" ? cachedLength : listLayoutMetrics.estimatedLength,
|
|
2068
|
+
offset: typeof cachedOffset === "number" ? cachedOffset : LIST_TOP_PADDING + listLayoutMetrics.estimatedLength * safeIndex
|
|
2069
|
+
};
|
|
2070
|
+
},
|
|
2071
|
+
[ensurePageDimensions, isDouble, listLayoutMetrics, rows]
|
|
2072
|
+
);
|
|
1439
2073
|
(0, import_react3.useEffect)(() => {
|
|
1440
2074
|
if (isWebView) {
|
|
2075
|
+
clearPendingScrollTarget();
|
|
1441
2076
|
if (scrollToPageSignal === null) return;
|
|
1442
2077
|
if (pageCount === 0) return;
|
|
1443
2078
|
if (scrollToPageSignal < 0 || scrollToPageSignal >= pageCount) return;
|
|
1444
2079
|
const nextPage = scrollToPageSignal + 1;
|
|
1445
2080
|
engine.goToPage(nextPage);
|
|
1446
|
-
|
|
2081
|
+
setDocumentStateTracked(
|
|
2082
|
+
{ currentPage: nextPage, scrollToPageSignal: null },
|
|
2083
|
+
"scrollToPageSignal.webview"
|
|
2084
|
+
);
|
|
1447
2085
|
return;
|
|
1448
2086
|
}
|
|
1449
2087
|
if (scrollToPageSignal === null) return;
|
|
1450
2088
|
if (pageCount === 0) return;
|
|
1451
2089
|
if (scrollToPageSignal < 0 || scrollToPageSignal >= pageCount) return;
|
|
1452
2090
|
if (isSingle) {
|
|
1453
|
-
|
|
2091
|
+
clearPendingScrollTarget();
|
|
2092
|
+
setDocumentStateTracked(
|
|
2093
|
+
{ currentPage: scrollToPageSignal + 1, scrollToPageSignal: null },
|
|
2094
|
+
"scrollToPageSignal.single"
|
|
2095
|
+
);
|
|
1454
2096
|
return;
|
|
1455
2097
|
}
|
|
2098
|
+
ensurePageDimensions(scrollToPageSignal);
|
|
2099
|
+
if (isDouble) {
|
|
2100
|
+
ensurePageDimensions(scrollToPageSignal - 1);
|
|
2101
|
+
ensurePageDimensions(scrollToPageSignal + 1);
|
|
2102
|
+
}
|
|
1456
2103
|
const targetIndex = isDouble ? Math.floor(scrollToPageSignal / 2) : scrollToPageSignal;
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
2104
|
+
pendingScrollIndexRef.current = targetIndex;
|
|
2105
|
+
pendingScrollAttemptsRef.current = 0;
|
|
2106
|
+
clearPendingScrollRetry();
|
|
2107
|
+
setDocumentStateTracked(
|
|
2108
|
+
{
|
|
2109
|
+
currentPage: scrollToPageSignal + 1,
|
|
2110
|
+
scrollToPageSignal: null
|
|
2111
|
+
},
|
|
2112
|
+
"scrollToPageSignal.flatList"
|
|
2113
|
+
);
|
|
2114
|
+
listRef.current?.scrollToIndex({
|
|
2115
|
+
index: targetIndex,
|
|
2116
|
+
animated: true,
|
|
2117
|
+
viewPosition: 0
|
|
2118
|
+
});
|
|
2119
|
+
}, [
|
|
2120
|
+
clearPendingScrollRetry,
|
|
2121
|
+
clearPendingScrollTarget,
|
|
2122
|
+
ensurePageDimensions,
|
|
2123
|
+
scrollToPageSignal,
|
|
2124
|
+
pageCount,
|
|
2125
|
+
setDocumentStateTracked,
|
|
2126
|
+
isDouble,
|
|
2127
|
+
isSingle,
|
|
2128
|
+
isWebView,
|
|
2129
|
+
engine
|
|
2130
|
+
]);
|
|
1460
2131
|
const onViewableItemsChanged = (0, import_react3.useCallback)(
|
|
1461
2132
|
({ viewableItems }) => {
|
|
2133
|
+
if (perfEnabled) {
|
|
2134
|
+
viewableBurstRef.current({
|
|
2135
|
+
viewableCount: viewableItems.length,
|
|
2136
|
+
mode: isDouble ? "double" : "continuous"
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
const pendingIndex = pendingScrollIndexRef.current;
|
|
2140
|
+
if (pendingIndex !== null) {
|
|
2141
|
+
const reachedTarget = viewableItems.some((token) => {
|
|
2142
|
+
if (isDouble) {
|
|
2143
|
+
const row = token.item;
|
|
2144
|
+
if (!row) return false;
|
|
2145
|
+
if (row.left === pendingIndex) return true;
|
|
2146
|
+
return row.right === pendingIndex;
|
|
2147
|
+
}
|
|
2148
|
+
return token.index === pendingIndex;
|
|
2149
|
+
});
|
|
2150
|
+
if (reachedTarget) {
|
|
2151
|
+
if (perfEnabled) {
|
|
2152
|
+
logPerfEvent("Viewer", "scroll.retry.resolved", {
|
|
2153
|
+
targetIndex: pendingIndex,
|
|
2154
|
+
attempts: pendingScrollAttemptsRef.current
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
clearPendingScrollTarget();
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
1462
2160
|
const first = viewableItems[0];
|
|
1463
2161
|
if (!first) return;
|
|
1464
2162
|
if (isDouble) {
|
|
1465
2163
|
const item = first.item;
|
|
1466
2164
|
if (!item) return;
|
|
2165
|
+
ensurePageDimensions(item.left);
|
|
2166
|
+
if (item.right !== null) {
|
|
2167
|
+
ensurePageDimensions(item.right);
|
|
2168
|
+
}
|
|
1467
2169
|
const page = item.left + 1;
|
|
1468
2170
|
if (page !== currentPage) {
|
|
1469
|
-
|
|
2171
|
+
setDocumentStateTracked({ currentPage: page }, "viewable.double");
|
|
1470
2172
|
}
|
|
1471
2173
|
return;
|
|
1472
2174
|
}
|
|
1473
2175
|
if (first.index !== null && first.index !== void 0) {
|
|
2176
|
+
ensurePageDimensions(first.index);
|
|
1474
2177
|
const page = first.index + 1;
|
|
1475
2178
|
if (page !== currentPage) {
|
|
1476
|
-
|
|
2179
|
+
setDocumentStateTracked({ currentPage: page }, "viewable.continuous");
|
|
1477
2180
|
}
|
|
1478
2181
|
}
|
|
1479
2182
|
},
|
|
1480
|
-
[
|
|
2183
|
+
[
|
|
2184
|
+
clearPendingScrollTarget,
|
|
2185
|
+
currentPage,
|
|
2186
|
+
ensurePageDimensions,
|
|
2187
|
+
isDouble,
|
|
2188
|
+
perfEnabled,
|
|
2189
|
+
setDocumentStateTracked
|
|
2190
|
+
]
|
|
1481
2191
|
);
|
|
1482
2192
|
if (isWebView) {
|
|
1483
2193
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [styles3.container, isDark && styles3.containerDark], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WebViewViewer_default, { engine }) });
|
|
@@ -1489,50 +2199,137 @@ var Viewer = ({ engine }) => {
|
|
|
1489
2199
|
contentContainerStyle: styles3.singleContent,
|
|
1490
2200
|
showsVerticalScrollIndicator: false,
|
|
1491
2201
|
scrollEnabled: true,
|
|
1492
|
-
|
|
2202
|
+
onScroll: perfEnabled ? (event) => {
|
|
2203
|
+
const timestampValue = event.nativeEvent.timestamp;
|
|
2204
|
+
const timestamp = typeof timestampValue === "number" ? timestampValue : void 0;
|
|
2205
|
+
scrollMonitorRef.current.track(timestamp);
|
|
2206
|
+
} : void 0,
|
|
2207
|
+
onScrollBeginDrag: perfEnabled ? () => {
|
|
2208
|
+
scrollMonitorRef.current.begin("single.beginDrag");
|
|
2209
|
+
} : void 0,
|
|
2210
|
+
onMomentumScrollBegin: perfEnabled ? () => {
|
|
2211
|
+
scrollMonitorRef.current.begin("single.momentumBegin");
|
|
2212
|
+
} : void 0,
|
|
2213
|
+
onScrollEndDrag: perfEnabled ? () => {
|
|
2214
|
+
scrollMonitorRef.current.end("single.endDrag");
|
|
2215
|
+
sampleMemory("Viewer", "single.endDrag", { pageCount });
|
|
2216
|
+
} : void 0,
|
|
2217
|
+
onMomentumScrollEnd: perfEnabled ? () => {
|
|
2218
|
+
scrollMonitorRef.current.end("single.momentumEnd");
|
|
2219
|
+
sampleMemory("Viewer", "single.momentumEnd", { pageCount });
|
|
2220
|
+
} : void 0,
|
|
2221
|
+
scrollEventThrottle: perfEnabled ? 16 : void 0,
|
|
2222
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2223
|
+
PageRenderer_default,
|
|
2224
|
+
{
|
|
2225
|
+
engine,
|
|
2226
|
+
pageIndex: Math.max(0, currentPage - 1),
|
|
2227
|
+
spacing: 32
|
|
2228
|
+
}
|
|
2229
|
+
)
|
|
1493
2230
|
}
|
|
1494
2231
|
) });
|
|
1495
2232
|
}
|
|
1496
|
-
const columnGap = 12;
|
|
1497
|
-
const horizontalPadding = 16;
|
|
1498
|
-
const columnWidth = isDouble ? (windowWidth - horizontalPadding * 2 - columnGap) / 2 : windowWidth;
|
|
1499
2233
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: [styles3.container, isDark && styles3.containerDark], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1500
2234
|
import_react_native3.FlatList,
|
|
1501
2235
|
{
|
|
1502
2236
|
ref: listRef,
|
|
1503
2237
|
data: isDouble ? rows : pages,
|
|
2238
|
+
initialNumToRender: FLATLIST_INITIAL_NUM_TO_RENDER,
|
|
2239
|
+
windowSize: FLATLIST_WINDOW_SIZE,
|
|
2240
|
+
maxToRenderPerBatch: FLATLIST_MAX_TO_RENDER_PER_BATCH,
|
|
2241
|
+
updateCellsBatchingPeriod: FLATLIST_UPDATE_CELLS_BATCHING_PERIOD,
|
|
2242
|
+
removeClippedSubviews: true,
|
|
2243
|
+
getItemLayout,
|
|
1504
2244
|
keyExtractor: (item) => isDouble ? `row-${item.left}` : `page-${item}`,
|
|
1505
2245
|
contentContainerStyle: styles3.listContent,
|
|
1506
|
-
renderItem: ({ item }) => isDouble ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
{
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
2246
|
+
renderItem: ({ item }) => isDouble ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2247
|
+
import_react_native3.View,
|
|
2248
|
+
{
|
|
2249
|
+
style: [styles3.row, { paddingHorizontal: horizontalPadding }],
|
|
2250
|
+
children: [
|
|
2251
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: { width: columnWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2252
|
+
PageRenderer_default,
|
|
2253
|
+
{
|
|
2254
|
+
engine,
|
|
2255
|
+
pageIndex: item.left,
|
|
2256
|
+
availableWidth: columnWidth,
|
|
2257
|
+
horizontalPadding: 8,
|
|
2258
|
+
spacing: DOUBLE_PAGE_SPACING
|
|
2259
|
+
}
|
|
2260
|
+
) }),
|
|
2261
|
+
item.right !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: { width: columnWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2262
|
+
PageRenderer_default,
|
|
2263
|
+
{
|
|
2264
|
+
engine,
|
|
2265
|
+
pageIndex: item.right,
|
|
2266
|
+
availableWidth: columnWidth,
|
|
2267
|
+
horizontalPadding: 8,
|
|
2268
|
+
spacing: DOUBLE_PAGE_SPACING
|
|
2269
|
+
}
|
|
2270
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.View, { style: { width: columnWidth } })
|
|
2271
|
+
]
|
|
2272
|
+
}
|
|
2273
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2274
|
+
PageRenderer_default,
|
|
2275
|
+
{
|
|
2276
|
+
engine,
|
|
2277
|
+
pageIndex: item,
|
|
2278
|
+
spacing: CONTINUOUS_PAGE_SPACING
|
|
2279
|
+
}
|
|
2280
|
+
),
|
|
1528
2281
|
onViewableItemsChanged,
|
|
1529
2282
|
viewabilityConfig: { itemVisiblePercentThreshold: 60 },
|
|
1530
2283
|
scrollEnabled: true,
|
|
1531
2284
|
onScrollToIndexFailed: ({ index, averageItemLength }) => {
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
2285
|
+
const dataLength = isDouble ? rows.length : pages.length;
|
|
2286
|
+
if (index < 0 || index >= dataLength) return;
|
|
2287
|
+
pendingScrollIndexRef.current = index;
|
|
2288
|
+
const offset = Math.max(0, getFallbackOffsetForIndex(index));
|
|
2289
|
+
listRef.current?.scrollToOffset({ offset, animated: false });
|
|
2290
|
+
if (!isDouble) {
|
|
2291
|
+
ensurePageDimensions(index);
|
|
2292
|
+
} else {
|
|
2293
|
+
const row = rows[index];
|
|
2294
|
+
if (row) {
|
|
2295
|
+
ensurePageDimensions(row.left);
|
|
2296
|
+
if (row.right !== null) {
|
|
2297
|
+
ensurePageDimensions(row.right);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
scheduleScrollRetry("onScrollToIndexFailed");
|
|
2302
|
+
if (perfEnabled) {
|
|
2303
|
+
logPerfEvent("Viewer", "scrollToIndexFailed", {
|
|
2304
|
+
index,
|
|
2305
|
+
averageItemLength,
|
|
2306
|
+
fallbackOffset: offset,
|
|
2307
|
+
fallbackSource: "cached-item-layout",
|
|
2308
|
+
itemCount: dataLength,
|
|
2309
|
+
retryAttempt: pendingScrollAttemptsRef.current
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
1535
2312
|
},
|
|
2313
|
+
onScroll: perfEnabled ? (event) => {
|
|
2314
|
+
const timestampValue = event.nativeEvent.timestamp;
|
|
2315
|
+
const timestamp = typeof timestampValue === "number" ? timestampValue : void 0;
|
|
2316
|
+
scrollMonitorRef.current.track(timestamp);
|
|
2317
|
+
} : void 0,
|
|
2318
|
+
onScrollBeginDrag: perfEnabled ? () => {
|
|
2319
|
+
scrollMonitorRef.current.begin("continuous.beginDrag");
|
|
2320
|
+
} : void 0,
|
|
2321
|
+
onMomentumScrollBegin: perfEnabled ? () => {
|
|
2322
|
+
scrollMonitorRef.current.begin("continuous.momentumBegin");
|
|
2323
|
+
} : void 0,
|
|
2324
|
+
onScrollEndDrag: perfEnabled ? () => {
|
|
2325
|
+
scrollMonitorRef.current.end("continuous.endDrag");
|
|
2326
|
+
sampleMemory("Viewer", "continuous.endDrag", { pageCount });
|
|
2327
|
+
} : void 0,
|
|
2328
|
+
onMomentumScrollEnd: perfEnabled ? () => {
|
|
2329
|
+
scrollMonitorRef.current.end("continuous.momentumEnd");
|
|
2330
|
+
sampleMemory("Viewer", "continuous.momentumEnd", { pageCount });
|
|
2331
|
+
} : void 0,
|
|
2332
|
+
scrollEventThrottle: perfEnabled ? 16 : void 0,
|
|
1536
2333
|
showsVerticalScrollIndicator: false
|
|
1537
2334
|
}
|
|
1538
2335
|
) });
|
|
@@ -1546,8 +2343,8 @@ var styles3 = import_react_native3.StyleSheet.create({
|
|
|
1546
2343
|
backgroundColor: "#0f1115"
|
|
1547
2344
|
},
|
|
1548
2345
|
listContent: {
|
|
1549
|
-
paddingTop:
|
|
1550
|
-
paddingBottom:
|
|
2346
|
+
paddingTop: LIST_TOP_PADDING,
|
|
2347
|
+
paddingBottom: LIST_BOTTOM_PADDING
|
|
1551
2348
|
},
|
|
1552
2349
|
singleContent: {
|
|
1553
2350
|
paddingTop: 18,
|
|
@@ -2069,7 +2866,29 @@ var PageThumbnail = ({
|
|
|
2069
2866
|
isActive && { borderColor: accentColor }
|
|
2070
2867
|
],
|
|
2071
2868
|
children: [
|
|
2072
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2869
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2870
|
+
import_react_native6.View,
|
|
2871
|
+
{
|
|
2872
|
+
onLayout: handleLayout,
|
|
2873
|
+
style: [styles6.thumbFrame, { width: frameWidth, height: frameHeight }],
|
|
2874
|
+
children: useNativePreview ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_engine_native2.PapyrusPageView, { ref: viewRef, style: styles6.thumbView }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2875
|
+
import_react_native6.View,
|
|
2876
|
+
{
|
|
2877
|
+
style: [styles6.thumbFallback, isDark && styles6.thumbFallbackDark],
|
|
2878
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2879
|
+
import_react_native6.Text,
|
|
2880
|
+
{
|
|
2881
|
+
style: [
|
|
2882
|
+
styles6.thumbFallbackText,
|
|
2883
|
+
isDark && styles6.thumbFallbackTextDark
|
|
2884
|
+
],
|
|
2885
|
+
children: pageIndex + 1
|
|
2886
|
+
}
|
|
2887
|
+
)
|
|
2888
|
+
}
|
|
2889
|
+
)
|
|
2890
|
+
}
|
|
2891
|
+
),
|
|
2073
2892
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: [styles6.thumbLabel, isDark && styles6.thumbLabelDark], children: pageIndex + 1 })
|
|
2074
2893
|
]
|
|
2075
2894
|
}
|
|
@@ -2136,7 +2955,9 @@ var RightSheet = ({ engine }) => {
|
|
|
2136
2955
|
locale,
|
|
2137
2956
|
accentColor
|
|
2138
2957
|
} = (0, import_core6.useViewerStore)();
|
|
2139
|
-
const [pagesMode, setPagesMode] = (0, import_react5.useState)(
|
|
2958
|
+
const [pagesMode, setPagesMode] = (0, import_react5.useState)(
|
|
2959
|
+
"thumbnails"
|
|
2960
|
+
);
|
|
2140
2961
|
const [query, setQuery] = (0, import_react5.useState)("");
|
|
2141
2962
|
const [isSearching, setIsSearching] = (0, import_react5.useState)(false);
|
|
2142
2963
|
const searchService = (0, import_react5.useMemo)(() => new import_core6.SearchService(engine), [engine]);
|
|
@@ -2144,6 +2965,10 @@ var RightSheet = ({ engine }) => {
|
|
|
2144
2965
|
const accentSoft = withAlpha(accentColor, 0.2);
|
|
2145
2966
|
const accentStrong = withAlpha(accentColor, 0.35);
|
|
2146
2967
|
const t = getStrings(locale);
|
|
2968
|
+
const perfEnabled = isMobilePerfEnabled();
|
|
2969
|
+
const setStateBurstRef = (0, import_react5.useRef)(
|
|
2970
|
+
createBurstMonitor("RightSheet", "setDocumentState", 10, 800)
|
|
2971
|
+
);
|
|
2147
2972
|
const sheetHeight = Math.min(640, import_react_native6.Dimensions.get("window").height * 0.72);
|
|
2148
2973
|
const windowWidth = import_react_native6.Dimensions.get("window").width;
|
|
2149
2974
|
const gridGutter = 12;
|
|
@@ -2152,24 +2977,76 @@ var RightSheet = ({ engine }) => {
|
|
|
2152
2977
|
const frameWidth = cardWidth - 16;
|
|
2153
2978
|
const frameHeight = frameWidth * 1.28;
|
|
2154
2979
|
const renderTarget = engine.getRenderTargetType?.();
|
|
2155
|
-
const hasNativePageView = Boolean(
|
|
2980
|
+
const hasNativePageView = Boolean(
|
|
2981
|
+
import_react_native6.UIManager.getViewManagerConfig?.("PapyrusPageView")
|
|
2982
|
+
);
|
|
2156
2983
|
const useNativePreview = renderTarget !== "webview" && hasNativePageView;
|
|
2157
2984
|
const closeSheet = () => toggleSidebarRight();
|
|
2985
|
+
const setDocumentStateTracked = (0, import_react5.useCallback)(
|
|
2986
|
+
(state, reason) => {
|
|
2987
|
+
if (perfEnabled) {
|
|
2988
|
+
setStateBurstRef.current({
|
|
2989
|
+
reason,
|
|
2990
|
+
keys: Object.keys(state).join(",")
|
|
2991
|
+
});
|
|
2992
|
+
}
|
|
2993
|
+
setDocumentState(state);
|
|
2994
|
+
},
|
|
2995
|
+
[perfEnabled, setDocumentState]
|
|
2996
|
+
);
|
|
2997
|
+
(0, import_react5.useEffect)(() => {
|
|
2998
|
+
if (!perfEnabled || !sidebarRightOpen) return;
|
|
2999
|
+
logPerfEvent("RightSheet", "open", {
|
|
3000
|
+
tab: sidebarRightTab,
|
|
3001
|
+
pageCount,
|
|
3002
|
+
currentPage
|
|
3003
|
+
});
|
|
3004
|
+
sampleMemory("RightSheet", "open", {
|
|
3005
|
+
tab: sidebarRightTab,
|
|
3006
|
+
pageCount
|
|
3007
|
+
});
|
|
3008
|
+
}, [perfEnabled, sidebarRightOpen]);
|
|
3009
|
+
(0, import_react5.useEffect)(() => {
|
|
3010
|
+
if (!perfEnabled || !sidebarRightOpen) return;
|
|
3011
|
+
return () => {
|
|
3012
|
+
logPerfEvent("RightSheet", "close");
|
|
3013
|
+
};
|
|
3014
|
+
}, [perfEnabled, sidebarRightOpen]);
|
|
3015
|
+
(0, import_react5.useEffect)(() => {
|
|
3016
|
+
if (!perfEnabled || !sidebarRightOpen || sidebarRightTab !== "pages" || pagesMode !== "thumbnails")
|
|
3017
|
+
return;
|
|
3018
|
+
sampleMemory("RightSheet", "thumbnails.open.start", { pageCount });
|
|
3019
|
+
const timeout = setTimeout(() => {
|
|
3020
|
+
sampleMemory("RightSheet", "thumbnails.open.steady", { pageCount });
|
|
3021
|
+
}, 1200);
|
|
3022
|
+
return () => clearTimeout(timeout);
|
|
3023
|
+
}, [pageCount, pagesMode, perfEnabled, sidebarRightOpen, sidebarRightTab]);
|
|
2158
3024
|
const handleSearch = async () => {
|
|
2159
3025
|
const trimmed = query.trim();
|
|
2160
3026
|
if (!trimmed) {
|
|
2161
3027
|
setSearch("", []);
|
|
2162
3028
|
return;
|
|
2163
3029
|
}
|
|
3030
|
+
const startedAt = perfEnabled ? perfNow() : 0;
|
|
2164
3031
|
setIsSearching(true);
|
|
2165
3032
|
try {
|
|
2166
3033
|
const results = await searchService.search(trimmed);
|
|
2167
3034
|
setSearch(trimmed, results);
|
|
3035
|
+
if (perfEnabled) {
|
|
3036
|
+
logPerfEvent("RightSheet", "search.completed", {
|
|
3037
|
+
queryLength: trimmed.length,
|
|
3038
|
+
results: results.length,
|
|
3039
|
+
durationMs: Math.round((perfNow() - startedAt) * 100) / 100
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
2168
3042
|
} finally {
|
|
2169
3043
|
setIsSearching(false);
|
|
2170
3044
|
}
|
|
2171
3045
|
};
|
|
2172
|
-
const pages = (0, import_react5.useMemo)(
|
|
3046
|
+
const pages = (0, import_react5.useMemo)(
|
|
3047
|
+
() => Array.from({ length: pageCount }, (_, i) => i),
|
|
3048
|
+
[pageCount]
|
|
3049
|
+
);
|
|
2173
3050
|
const renderHighlightedSnippet = (text, isActive) => {
|
|
2174
3051
|
const trimmedQuery = searchQuery.trim();
|
|
2175
3052
|
if (trimmedQuery.length < 2) {
|
|
@@ -2203,7 +3080,10 @@ var RightSheet = ({ engine }) => {
|
|
|
2203
3080
|
if (index > cursor) {
|
|
2204
3081
|
parts.push({ text: text.slice(cursor, index), match: false });
|
|
2205
3082
|
}
|
|
2206
|
-
parts.push({
|
|
3083
|
+
parts.push({
|
|
3084
|
+
text: text.slice(index, index + trimmedQuery.length),
|
|
3085
|
+
match: true
|
|
3086
|
+
});
|
|
2207
3087
|
cursor = index + trimmedQuery.length;
|
|
2208
3088
|
}
|
|
2209
3089
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
@@ -2222,7 +3102,10 @@ var RightSheet = ({ engine }) => {
|
|
|
2222
3102
|
part.match && styles6.matchText,
|
|
2223
3103
|
part.match && isDark && styles6.matchTextDark,
|
|
2224
3104
|
part.match && isActive && styles6.matchTextActive,
|
|
2225
|
-
part.match && isActive && {
|
|
3105
|
+
part.match && isActive && {
|
|
3106
|
+
backgroundColor: accentStrong,
|
|
3107
|
+
color: accentColor
|
|
3108
|
+
}
|
|
2226
3109
|
],
|
|
2227
3110
|
children: part.text
|
|
2228
3111
|
},
|
|
@@ -2232,247 +3115,419 @@ var RightSheet = ({ engine }) => {
|
|
|
2232
3115
|
);
|
|
2233
3116
|
};
|
|
2234
3117
|
if (!sidebarRightOpen) return null;
|
|
2235
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
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
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
3118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3119
|
+
import_react_native6.Modal,
|
|
3120
|
+
{
|
|
3121
|
+
visible: true,
|
|
3122
|
+
transparent: true,
|
|
3123
|
+
animationType: "slide",
|
|
3124
|
+
onRequestClose: closeSheet,
|
|
3125
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.modalRoot, children: [
|
|
3126
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Pressable, { style: styles6.backdrop, onPress: closeSheet }),
|
|
3127
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3128
|
+
import_react_native6.View,
|
|
3129
|
+
{
|
|
3130
|
+
style: [
|
|
3131
|
+
styles6.sheet,
|
|
3132
|
+
{ height: sheetHeight },
|
|
3133
|
+
isDark && styles6.sheetDark
|
|
3134
|
+
],
|
|
3135
|
+
children: [
|
|
3136
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: [styles6.handle, isDark && styles6.handleDark] }),
|
|
3137
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: styles6.tabs, children: ["pages", "search", "annotations"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3138
|
+
import_react_native6.Pressable,
|
|
3139
|
+
{
|
|
3140
|
+
onPress: () => toggleSidebarRight(tab),
|
|
3141
|
+
style: [
|
|
3142
|
+
styles6.tabButton,
|
|
3143
|
+
isDark && styles6.tabButtonDark,
|
|
3144
|
+
sidebarRightTab === tab && styles6.tabButtonActive,
|
|
3145
|
+
sidebarRightTab === tab && { backgroundColor: accentColor }
|
|
3146
|
+
],
|
|
3147
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3148
|
+
import_react_native6.Text,
|
|
3149
|
+
{
|
|
3150
|
+
style: [
|
|
3151
|
+
styles6.tabText,
|
|
3152
|
+
isDark && styles6.tabTextDark,
|
|
3153
|
+
sidebarRightTab === tab && styles6.tabTextActive
|
|
3154
|
+
],
|
|
3155
|
+
children: tab === "pages" ? t.pages : tab === "search" ? t.search : t.notes
|
|
3156
|
+
}
|
|
3157
|
+
)
|
|
3158
|
+
},
|
|
3159
|
+
tab
|
|
3160
|
+
)) }),
|
|
3161
|
+
sidebarRightTab === "pages" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.pagesContent, children: [
|
|
3162
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.pageHeader, children: [
|
|
3163
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3164
|
+
import_react_native6.Text,
|
|
3165
|
+
{
|
|
3166
|
+
style: [styles6.pageStatus, isDark && styles6.pageStatusDark],
|
|
3167
|
+
children: [
|
|
3168
|
+
t.page,
|
|
3169
|
+
" ",
|
|
3170
|
+
currentPage,
|
|
3171
|
+
" / ",
|
|
3172
|
+
pageCount
|
|
3173
|
+
]
|
|
3174
|
+
}
|
|
3175
|
+
),
|
|
3176
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3177
|
+
import_react_native6.View,
|
|
3178
|
+
{
|
|
3179
|
+
style: [styles6.segmented, isDark && styles6.segmentedDark],
|
|
3180
|
+
children: [
|
|
3181
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3182
|
+
import_react_native6.Pressable,
|
|
3183
|
+
{
|
|
3184
|
+
onPress: () => setPagesMode("thumbnails"),
|
|
3185
|
+
style: [
|
|
3186
|
+
styles6.segmentButton,
|
|
3187
|
+
pagesMode === "thumbnails" && styles6.segmentButtonActive,
|
|
3188
|
+
pagesMode === "thumbnails" && {
|
|
3189
|
+
backgroundColor: accentColor
|
|
3190
|
+
}
|
|
3191
|
+
],
|
|
3192
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3193
|
+
import_react_native6.Text,
|
|
3194
|
+
{
|
|
3195
|
+
style: [
|
|
3196
|
+
styles6.segmentText,
|
|
3197
|
+
isDark && styles6.segmentTextDark,
|
|
3198
|
+
pagesMode === "thumbnails" && styles6.segmentTextActive
|
|
3199
|
+
],
|
|
3200
|
+
children: t.pagesTab
|
|
3201
|
+
}
|
|
3202
|
+
)
|
|
3203
|
+
}
|
|
3204
|
+
),
|
|
3205
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3206
|
+
import_react_native6.Pressable,
|
|
3207
|
+
{
|
|
3208
|
+
onPress: () => setPagesMode("summary"),
|
|
3209
|
+
style: [
|
|
3210
|
+
styles6.segmentButton,
|
|
3211
|
+
pagesMode === "summary" && styles6.segmentButtonActive,
|
|
3212
|
+
pagesMode === "summary" && {
|
|
3213
|
+
backgroundColor: accentColor
|
|
3214
|
+
}
|
|
3215
|
+
],
|
|
3216
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3217
|
+
import_react_native6.Text,
|
|
3218
|
+
{
|
|
3219
|
+
style: [
|
|
3220
|
+
styles6.segmentText,
|
|
3221
|
+
isDark && styles6.segmentTextDark,
|
|
3222
|
+
pagesMode === "summary" && styles6.segmentTextActive
|
|
3223
|
+
],
|
|
3224
|
+
children: t.summaryTab
|
|
3225
|
+
}
|
|
3226
|
+
)
|
|
3227
|
+
}
|
|
3228
|
+
)
|
|
3229
|
+
]
|
|
3230
|
+
}
|
|
3231
|
+
)
|
|
3232
|
+
] }),
|
|
3233
|
+
pagesMode === "thumbnails" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3234
|
+
import_react_native6.FlatList,
|
|
2284
3235
|
{
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
3236
|
+
data: pages,
|
|
3237
|
+
keyExtractor: (item) => `thumb-${item}`,
|
|
3238
|
+
numColumns: 2,
|
|
3239
|
+
contentContainerStyle: styles6.thumbGrid,
|
|
3240
|
+
columnWrapperStyle: styles6.thumbRow,
|
|
3241
|
+
showsVerticalScrollIndicator: false,
|
|
3242
|
+
initialNumToRender: 6,
|
|
3243
|
+
renderItem: ({ item }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3244
|
+
PageThumbnail,
|
|
3245
|
+
{
|
|
3246
|
+
engine,
|
|
3247
|
+
pageIndex: item,
|
|
3248
|
+
isActive: item + 1 === currentPage,
|
|
3249
|
+
isDark,
|
|
3250
|
+
zoom,
|
|
3251
|
+
cardWidth,
|
|
3252
|
+
frameWidth,
|
|
3253
|
+
frameHeight,
|
|
3254
|
+
accentColor,
|
|
3255
|
+
useNativePreview,
|
|
3256
|
+
onPress: () => {
|
|
3257
|
+
engine.goToPage(item + 1);
|
|
3258
|
+
setDocumentStateTracked(
|
|
3259
|
+
{ currentPage: item + 1 },
|
|
3260
|
+
"thumbnail.press"
|
|
3261
|
+
);
|
|
3262
|
+
triggerScrollToPage(item);
|
|
3263
|
+
closeSheet();
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
)
|
|
2291
3267
|
}
|
|
2292
|
-
)
|
|
2293
|
-
|
|
2294
|
-
),
|
|
2295
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2296
|
-
import_react_native6.Pressable,
|
|
2297
|
-
{
|
|
2298
|
-
onPress: () => setPagesMode("summary"),
|
|
2299
|
-
style: [
|
|
2300
|
-
styles6.segmentButton,
|
|
2301
|
-
pagesMode === "summary" && styles6.segmentButtonActive,
|
|
2302
|
-
pagesMode === "summary" && { backgroundColor: accentColor }
|
|
2303
|
-
],
|
|
2304
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2305
|
-
import_react_native6.Text,
|
|
3268
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3269
|
+
import_react_native6.ScrollView,
|
|
2306
3270
|
{
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
3271
|
+
contentContainerStyle: styles6.summaryContent,
|
|
3272
|
+
showsVerticalScrollIndicator: false,
|
|
3273
|
+
children: outline.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3274
|
+
import_react_native6.Text,
|
|
3275
|
+
{
|
|
3276
|
+
style: [styles6.emptyText, isDark && styles6.emptyTextDark],
|
|
3277
|
+
children: t.noSummary
|
|
3278
|
+
}
|
|
3279
|
+
) : outline.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3280
|
+
OutlineNode,
|
|
3281
|
+
{
|
|
3282
|
+
item,
|
|
3283
|
+
isDark,
|
|
3284
|
+
untitledLabel: t.untitled,
|
|
3285
|
+
onSelect: (pageIndex) => {
|
|
3286
|
+
engine.goToPage(pageIndex + 1);
|
|
3287
|
+
setDocumentStateTracked(
|
|
3288
|
+
{ currentPage: pageIndex + 1 },
|
|
3289
|
+
"outline.select"
|
|
3290
|
+
);
|
|
3291
|
+
triggerScrollToPage(pageIndex);
|
|
3292
|
+
closeSheet();
|
|
3293
|
+
}
|
|
3294
|
+
},
|
|
3295
|
+
`${item.title}-${index}`
|
|
3296
|
+
))
|
|
2313
3297
|
}
|
|
2314
3298
|
)
|
|
2315
|
-
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
3299
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3300
|
+
import_react_native6.ScrollView,
|
|
3301
|
+
{
|
|
3302
|
+
contentContainerStyle: styles6.content,
|
|
3303
|
+
showsVerticalScrollIndicator: false,
|
|
3304
|
+
children: sidebarRightTab === "search" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { children: [
|
|
3305
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3306
|
+
import_react_native6.View,
|
|
3307
|
+
{
|
|
3308
|
+
style: [styles6.searchBox, isDark && styles6.searchBoxDark],
|
|
3309
|
+
children: [
|
|
3310
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3311
|
+
import_react_native6.TextInput,
|
|
3312
|
+
{
|
|
3313
|
+
value: query,
|
|
3314
|
+
onChangeText: setQuery,
|
|
3315
|
+
placeholder: t.searchPlaceholder,
|
|
3316
|
+
placeholderTextColor: isDark ? "#9ca3af" : "#6b7280",
|
|
3317
|
+
style: [
|
|
3318
|
+
styles6.searchInput,
|
|
3319
|
+
isDark && styles6.searchInputDark
|
|
3320
|
+
],
|
|
3321
|
+
onSubmitEditing: handleSearch,
|
|
3322
|
+
returnKeyType: "search"
|
|
3323
|
+
}
|
|
3324
|
+
),
|
|
3325
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3326
|
+
import_react_native6.Pressable,
|
|
3327
|
+
{
|
|
3328
|
+
onPress: handleSearch,
|
|
3329
|
+
style: [
|
|
3330
|
+
styles6.searchButton,
|
|
3331
|
+
{ backgroundColor: accentColor }
|
|
3332
|
+
],
|
|
3333
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: styles6.searchButtonText, children: t.searchGo })
|
|
3334
|
+
}
|
|
3335
|
+
)
|
|
3336
|
+
]
|
|
3337
|
+
}
|
|
3338
|
+
),
|
|
3339
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.searchMeta, children: [
|
|
3340
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3341
|
+
import_react_native6.Text,
|
|
3342
|
+
{
|
|
3343
|
+
style: [
|
|
3344
|
+
styles6.searchCount,
|
|
3345
|
+
isDark && styles6.searchCountDark,
|
|
3346
|
+
{ color: accentColor }
|
|
3347
|
+
],
|
|
3348
|
+
children: [
|
|
3349
|
+
searchResults.length,
|
|
3350
|
+
" ",
|
|
3351
|
+
t.results
|
|
3352
|
+
]
|
|
3353
|
+
}
|
|
3354
|
+
),
|
|
3355
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.searchNav, children: [
|
|
3356
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3357
|
+
import_react_native6.Pressable,
|
|
3358
|
+
{
|
|
3359
|
+
onPress: prevSearchResult,
|
|
3360
|
+
disabled: searchResults.length === 0,
|
|
3361
|
+
style: [
|
|
3362
|
+
styles6.searchNavButton,
|
|
3363
|
+
isDark && styles6.searchNavButtonDark,
|
|
3364
|
+
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
3365
|
+
],
|
|
3366
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3367
|
+
IconChevronLeft,
|
|
3368
|
+
{
|
|
3369
|
+
size: 14,
|
|
3370
|
+
color: isDark ? "#e5e7eb" : "#111827"
|
|
3371
|
+
}
|
|
3372
|
+
)
|
|
3373
|
+
}
|
|
3374
|
+
),
|
|
3375
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3376
|
+
import_react_native6.Pressable,
|
|
3377
|
+
{
|
|
3378
|
+
onPress: nextSearchResult,
|
|
3379
|
+
disabled: searchResults.length === 0,
|
|
3380
|
+
style: [
|
|
3381
|
+
styles6.searchNavButton,
|
|
3382
|
+
isDark && styles6.searchNavButtonDark,
|
|
3383
|
+
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
3384
|
+
],
|
|
3385
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3386
|
+
IconChevronRight,
|
|
3387
|
+
{
|
|
3388
|
+
size: 14,
|
|
3389
|
+
color: isDark ? "#e5e7eb" : "#111827"
|
|
3390
|
+
}
|
|
3391
|
+
)
|
|
3392
|
+
}
|
|
3393
|
+
)
|
|
3394
|
+
] })
|
|
3395
|
+
] }),
|
|
3396
|
+
isSearching && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.searchStatus, children: [
|
|
3397
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.ActivityIndicator, { size: "small", color: accentColor }),
|
|
3398
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3399
|
+
import_react_native6.Text,
|
|
3400
|
+
{
|
|
3401
|
+
style: [
|
|
3402
|
+
styles6.searchStatusText,
|
|
3403
|
+
isDark && styles6.searchStatusTextDark
|
|
3404
|
+
],
|
|
3405
|
+
children: t.searching
|
|
3406
|
+
}
|
|
3407
|
+
)
|
|
3408
|
+
] }),
|
|
3409
|
+
!isSearching && searchResults.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3410
|
+
import_react_native6.Text,
|
|
3411
|
+
{
|
|
3412
|
+
style: [styles6.emptyText, isDark && styles6.emptyTextDark],
|
|
3413
|
+
children: t.noResults
|
|
3414
|
+
}
|
|
3415
|
+
),
|
|
3416
|
+
!isSearching && searchResults.map((res, idx) => {
|
|
3417
|
+
const isActive = idx === activeSearchIndex;
|
|
3418
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3419
|
+
import_react_native6.Pressable,
|
|
3420
|
+
{
|
|
3421
|
+
onPress: () => {
|
|
3422
|
+
setDocumentStateTracked(
|
|
3423
|
+
{ activeSearchIndex: idx },
|
|
3424
|
+
"searchResult.select"
|
|
3425
|
+
);
|
|
3426
|
+
triggerScrollToPage(res.pageIndex);
|
|
3427
|
+
closeSheet();
|
|
3428
|
+
},
|
|
3429
|
+
style: [
|
|
3430
|
+
styles6.resultCard,
|
|
3431
|
+
isDark && styles6.resultCardDark,
|
|
3432
|
+
isActive && styles6.resultCardActive,
|
|
3433
|
+
isActive && { borderColor: accentColor }
|
|
3434
|
+
],
|
|
3435
|
+
children: [
|
|
3436
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3437
|
+
import_react_native6.Text,
|
|
3438
|
+
{
|
|
3439
|
+
style: [
|
|
3440
|
+
styles6.resultPage,
|
|
3441
|
+
isDark && styles6.resultPageDark,
|
|
3442
|
+
{ color: accentColor }
|
|
3443
|
+
],
|
|
3444
|
+
children: [
|
|
3445
|
+
t.page,
|
|
3446
|
+
" ",
|
|
3447
|
+
res.pageIndex + 1
|
|
3448
|
+
]
|
|
3449
|
+
}
|
|
3450
|
+
),
|
|
3451
|
+
renderHighlightedSnippet(res.text, isActive)
|
|
3452
|
+
]
|
|
3453
|
+
},
|
|
3454
|
+
`${res.pageIndex}-${idx}`
|
|
3455
|
+
);
|
|
3456
|
+
})
|
|
3457
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { children: annotations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3458
|
+
import_react_native6.Text,
|
|
3459
|
+
{
|
|
3460
|
+
style: [styles6.emptyText, isDark && styles6.emptyTextDark],
|
|
3461
|
+
children: t.noAnnotations
|
|
3462
|
+
}
|
|
3463
|
+
) : annotations.map((ann) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3464
|
+
import_react_native6.Pressable,
|
|
3465
|
+
{
|
|
3466
|
+
onPress: () => {
|
|
3467
|
+
setSelectedAnnotation(ann.id);
|
|
3468
|
+
triggerScrollToPage(ann.pageIndex);
|
|
3469
|
+
closeSheet();
|
|
3470
|
+
},
|
|
3471
|
+
style: [styles6.noteCard, isDark && styles6.noteCardDark],
|
|
3472
|
+
children: [
|
|
3473
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.noteHeader, children: [
|
|
3474
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3475
|
+
import_react_native6.View,
|
|
3476
|
+
{
|
|
3477
|
+
style: [
|
|
3478
|
+
styles6.noteDot,
|
|
3479
|
+
{ backgroundColor: ann.color }
|
|
3480
|
+
]
|
|
3481
|
+
}
|
|
3482
|
+
),
|
|
3483
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
3484
|
+
import_react_native6.Text,
|
|
3485
|
+
{
|
|
3486
|
+
style: [
|
|
3487
|
+
styles6.noteTitle,
|
|
3488
|
+
isDark && styles6.noteTitleDark
|
|
3489
|
+
],
|
|
3490
|
+
children: [
|
|
3491
|
+
t.page,
|
|
3492
|
+
" ",
|
|
3493
|
+
ann.pageIndex + 1
|
|
3494
|
+
]
|
|
3495
|
+
}
|
|
3496
|
+
)
|
|
3497
|
+
] }),
|
|
3498
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3499
|
+
import_react_native6.Text,
|
|
3500
|
+
{
|
|
3501
|
+
style: [
|
|
3502
|
+
styles6.noteType,
|
|
3503
|
+
isDark && styles6.noteTypeDark,
|
|
3504
|
+
{ color: accentColor }
|
|
3505
|
+
],
|
|
3506
|
+
children: ann.type === "comment" || ann.type === "text" ? t.note.toUpperCase() : ann.type.toUpperCase()
|
|
3507
|
+
}
|
|
3508
|
+
),
|
|
3509
|
+
ann.content ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3510
|
+
import_react_native6.Text,
|
|
3511
|
+
{
|
|
3512
|
+
style: [
|
|
3513
|
+
styles6.noteContent,
|
|
3514
|
+
isDark && styles6.noteContentDark
|
|
3515
|
+
],
|
|
3516
|
+
children: ann.content
|
|
3517
|
+
}
|
|
3518
|
+
) : null
|
|
3519
|
+
]
|
|
3520
|
+
},
|
|
3521
|
+
ann.id
|
|
3522
|
+
)) })
|
|
2347
3523
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
3524
|
+
)
|
|
3525
|
+
]
|
|
2350
3526
|
}
|
|
2351
|
-
)
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
isDark,
|
|
2356
|
-
untitledLabel: t.untitled,
|
|
2357
|
-
onSelect: (pageIndex) => {
|
|
2358
|
-
engine.goToPage(pageIndex + 1);
|
|
2359
|
-
setDocumentState({ currentPage: pageIndex + 1 });
|
|
2360
|
-
triggerScrollToPage(pageIndex);
|
|
2361
|
-
closeSheet();
|
|
2362
|
-
}
|
|
2363
|
-
},
|
|
2364
|
-
`${item.title}-${index}`
|
|
2365
|
-
)) })
|
|
2366
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.ScrollView, { contentContainerStyle: styles6.content, showsVerticalScrollIndicator: false, children: sidebarRightTab === "search" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { children: [
|
|
2367
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: [styles6.searchBox, isDark && styles6.searchBoxDark], children: [
|
|
2368
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2369
|
-
import_react_native6.TextInput,
|
|
2370
|
-
{
|
|
2371
|
-
value: query,
|
|
2372
|
-
onChangeText: setQuery,
|
|
2373
|
-
placeholder: t.searchPlaceholder,
|
|
2374
|
-
placeholderTextColor: isDark ? "#9ca3af" : "#6b7280",
|
|
2375
|
-
style: [styles6.searchInput, isDark && styles6.searchInputDark],
|
|
2376
|
-
onSubmitEditing: handleSearch,
|
|
2377
|
-
returnKeyType: "search"
|
|
2378
|
-
}
|
|
2379
|
-
),
|
|
2380
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Pressable, { onPress: handleSearch, style: [styles6.searchButton, { backgroundColor: accentColor }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: styles6.searchButtonText, children: t.searchGo }) })
|
|
2381
|
-
] }),
|
|
2382
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.searchMeta, children: [
|
|
2383
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.Text, { style: [styles6.searchCount, isDark && styles6.searchCountDark, { color: accentColor }], children: [
|
|
2384
|
-
searchResults.length,
|
|
2385
|
-
" ",
|
|
2386
|
-
t.results
|
|
2387
|
-
] }),
|
|
2388
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.searchNav, children: [
|
|
2389
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2390
|
-
import_react_native6.Pressable,
|
|
2391
|
-
{
|
|
2392
|
-
onPress: prevSearchResult,
|
|
2393
|
-
disabled: searchResults.length === 0,
|
|
2394
|
-
style: [
|
|
2395
|
-
styles6.searchNavButton,
|
|
2396
|
-
isDark && styles6.searchNavButtonDark,
|
|
2397
|
-
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
2398
|
-
],
|
|
2399
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconChevronLeft, { size: 14, color: isDark ? "#e5e7eb" : "#111827" })
|
|
2400
|
-
}
|
|
2401
|
-
),
|
|
2402
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2403
|
-
import_react_native6.Pressable,
|
|
2404
|
-
{
|
|
2405
|
-
onPress: nextSearchResult,
|
|
2406
|
-
disabled: searchResults.length === 0,
|
|
2407
|
-
style: [
|
|
2408
|
-
styles6.searchNavButton,
|
|
2409
|
-
isDark && styles6.searchNavButtonDark,
|
|
2410
|
-
searchResults.length === 0 && styles6.searchNavButtonDisabled
|
|
2411
|
-
],
|
|
2412
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IconChevronRight, { size: 14, color: isDark ? "#e5e7eb" : "#111827" })
|
|
2413
|
-
}
|
|
2414
|
-
)
|
|
2415
|
-
] })
|
|
2416
|
-
] }),
|
|
2417
|
-
isSearching && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.searchStatus, children: [
|
|
2418
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.ActivityIndicator, { size: "small", color: accentColor }),
|
|
2419
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: [styles6.searchStatusText, isDark && styles6.searchStatusTextDark], children: t.searching })
|
|
2420
|
-
] }),
|
|
2421
|
-
!isSearching && searchResults.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: [styles6.emptyText, isDark && styles6.emptyTextDark], children: t.noResults }),
|
|
2422
|
-
!isSearching && searchResults.map((res, idx) => {
|
|
2423
|
-
const isActive = idx === activeSearchIndex;
|
|
2424
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2425
|
-
import_react_native6.Pressable,
|
|
2426
|
-
{
|
|
2427
|
-
onPress: () => {
|
|
2428
|
-
setDocumentState({ activeSearchIndex: idx });
|
|
2429
|
-
triggerScrollToPage(res.pageIndex);
|
|
2430
|
-
closeSheet();
|
|
2431
|
-
},
|
|
2432
|
-
style: [
|
|
2433
|
-
styles6.resultCard,
|
|
2434
|
-
isDark && styles6.resultCardDark,
|
|
2435
|
-
isActive && styles6.resultCardActive,
|
|
2436
|
-
isActive && { borderColor: accentColor }
|
|
2437
|
-
],
|
|
2438
|
-
children: [
|
|
2439
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.Text, { style: [styles6.resultPage, isDark && styles6.resultPageDark, { color: accentColor }], children: [
|
|
2440
|
-
t.page,
|
|
2441
|
-
" ",
|
|
2442
|
-
res.pageIndex + 1
|
|
2443
|
-
] }),
|
|
2444
|
-
renderHighlightedSnippet(res.text, isActive)
|
|
2445
|
-
]
|
|
2446
|
-
},
|
|
2447
|
-
`${res.pageIndex}-${idx}`
|
|
2448
|
-
);
|
|
2449
|
-
})
|
|
2450
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { children: annotations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: [styles6.emptyText, isDark && styles6.emptyTextDark], children: t.noAnnotations }) : annotations.map((ann) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2451
|
-
import_react_native6.Pressable,
|
|
2452
|
-
{
|
|
2453
|
-
onPress: () => {
|
|
2454
|
-
setSelectedAnnotation(ann.id);
|
|
2455
|
-
triggerScrollToPage(ann.pageIndex);
|
|
2456
|
-
closeSheet();
|
|
2457
|
-
},
|
|
2458
|
-
style: [styles6.noteCard, isDark && styles6.noteCardDark],
|
|
2459
|
-
children: [
|
|
2460
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.View, { style: styles6.noteHeader, children: [
|
|
2461
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.View, { style: [styles6.noteDot, { backgroundColor: ann.color }] }),
|
|
2462
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react_native6.Text, { style: [styles6.noteTitle, isDark && styles6.noteTitleDark], children: [
|
|
2463
|
-
t.page,
|
|
2464
|
-
" ",
|
|
2465
|
-
ann.pageIndex + 1
|
|
2466
|
-
] })
|
|
2467
|
-
] }),
|
|
2468
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: [styles6.noteType, isDark && styles6.noteTypeDark, { color: accentColor }], children: ann.type === "comment" || ann.type === "text" ? t.note.toUpperCase() : ann.type.toUpperCase() }),
|
|
2469
|
-
ann.content ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native6.Text, { style: [styles6.noteContent, isDark && styles6.noteContentDark], children: ann.content }) : null
|
|
2470
|
-
]
|
|
2471
|
-
},
|
|
2472
|
-
ann.id
|
|
2473
|
-
)) }) })
|
|
2474
|
-
] })
|
|
2475
|
-
] }) });
|
|
3527
|
+
)
|
|
3528
|
+
] })
|
|
3529
|
+
}
|
|
3530
|
+
);
|
|
2476
3531
|
};
|
|
2477
3532
|
var styles6 = import_react_native6.StyleSheet.create({
|
|
2478
3533
|
modalRoot: {
|