@a3s-lab/office 0.32.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -7
- package/dist/0~3539.js +2 -2
- package/dist/0~3557.js +1 -1
- package/dist/0~3635.js +1 -1
- package/dist/0~7048.js +1 -2
- package/dist/{0~6090.js → 0~8136.js} +71 -2
- package/dist/{0~4595.js → 0~9073.js} +3 -72
- package/dist/0~9445.js +1 -1
- package/dist/0~document-editor.js +2 -2
- package/dist/0~markdown-editor.js +2 -2
- package/dist/0~pdf-page-organization-engine.js +187 -0
- package/dist/0~pdf-viewer.js +1014 -30
- package/dist/0~presentation-editor.js +2 -2
- package/dist/0~spreadsheet-editor.js +2 -2
- package/dist/core.d.ts +1 -0
- package/dist/internal/features/work/editors/pdf-editor-extensions.d.ts +11 -0
- package/dist/internal/features/work/editors/pdf-page-organization-engine.d.ts +2 -0
- package/dist/internal/features/work/editors/pdf-page-organization-plan.d.ts +10 -0
- package/dist/internal/features/work/editors/pdf-page-organization-types.d.ts +68 -0
- package/dist/internal/features/work/editors/pdf-page-organization-worker-client.d.ts +2 -0
- package/dist/internal/features/work/editors/pdf-page-organization-worker-protocol.d.ts +13 -0
- package/dist/internal/features/work/editors/pdf-page-organization.d.ts +3 -0
- package/dist/internal/features/work/editors/pdf-page-organization.worker.d.ts +1 -0
- package/dist/internal/features/work/editors/pdf-page-organizer-dialog.d.ts +17 -0
- package/dist/internal/features/work/editors/pdf-thumbnail-rail.d.ts +4 -0
- package/dist/internal/features/work/editors/pdf-toolbar.d.ts +2 -1
- package/dist/internal/features/work/editors/pdf-viewer.d.ts +3 -1
- package/dist/internal/features/work/editors/use-pdf-page-organization.d.ts +38 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/pdf-page-organization.worker.js +19667 -0
- package/dist/pdf-page-organization.worker.js.LICENSE.txt +14 -0
- package/dist/react.d.ts +1 -0
- package/dist/styles.css +315 -0
- package/dist/vue.d.ts +3 -1
- package/dist/vue.js +2 -0
- package/dist/web-component.d.ts +3 -1
- package/dist/web-component.js +9 -0
- package/docs/latest/en/browser-editor-architecture.md +29 -6
- package/package.json +5 -1
package/dist/0~pdf-viewer.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { __webpack_require__ } from "./0~rslib-runtime.js";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { AnnotationPlugin, DocumentManagerPlugin, ExportPlugin, FormPlugin, HistoryPlugin, PDFViewer, PdfAnnotationSubtype, ScrollPlugin, SearchPlugin, ThumbnailPlugin, ViewportPlugin, ZoomMode, ZoomPlugin } from "@embedpdf/react-pdf-viewer";
|
|
4
|
-
import { AlertCircle, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Ellipsis, GalleryVerticalEnd, Highlighter, Loader2, Minus, MousePointer2, MoveHorizontal, Pencil, Plus, Redo2, Save, Scan, Search, SlidersHorizontal, Strikethrough, Trash2, Type, Underline, Undo2, X } from "lucide-react";
|
|
4
|
+
import { AlertCircle, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Download, Ellipsis, FilePlus2, Files, GalleryVerticalEnd, Highlighter, LayoutGrid, Loader2, Minus, MousePointer2, MoveHorizontal, Pencil, Plus, Redo2, RotateCcw, RotateCw, Save, Scan, Scissors, Search, SlidersHorizontal, Strikethrough, Trash2, Type, Underline, Undo2, X } from "lucide-react";
|
|
5
5
|
import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { PdfErrorCode } from "@embedpdf/models";
|
|
7
7
|
import { readOfficePdfCollaborationSource as readWorkOfficePdfCollaborationSource, createOfficePdfCollaborationBinding as createWorkOfficePdfCollaborationBinding, assertPdfCollaborationSource as assertWorkOfficePdfCollaborationSource } from "./3818.js";
|
|
8
8
|
import { createOfficeEditorExtension, useOfficeEditorRuntime, useOfficeEditorKeyboardShortcuts } from "./0~3557.js";
|
|
9
9
|
import { isOfficeShortcutBlocked, OfficeColorPicker } from "./0~3635.js";
|
|
10
|
-
import { officePresenceColorStyle, moveOfficeMenuFocus, useOfficeRemoteParticipants, WorkOfficeCollaborationParticipants,
|
|
10
|
+
import { officePresenceColorStyle, moveOfficeMenuFocus, useOfficeEditorWheelZoom, useOfficeRemoteParticipants, WorkOfficeCollaborationParticipants, Dialog, OfficePresenceAvatar, useOfficePublishPresenceLocation, button_Button, icon_button_IconButton, office_text_field_OfficeTextField, Popover, useDialogFocusScope, office_text_field_OfficeFileInput } from "./0~8136.js";
|
|
11
11
|
import { StateView, useOfficeCollaborationLocationNavigator, PDF_EVIDENCE_COORDINATE_BASIS } from "./432.js";
|
|
12
12
|
var pdf_viewer_namespaceObject = {};
|
|
13
13
|
__webpack_require__.r(pdf_viewer_namespaceObject);
|
|
@@ -714,6 +714,83 @@ function sortPdfJson(value) {
|
|
|
714
714
|
sortPdfJson(child)
|
|
715
715
|
]));
|
|
716
716
|
}
|
|
717
|
+
const PDF_PAGE_ORGANIZATION_MAX_SOURCE_BYTES = 268435456;
|
|
718
|
+
const PDF_PAGE_ORGANIZATION_MAX_IMPORT_BYTES = 134217728;
|
|
719
|
+
class PdfPageOrganizationError extends Error {
|
|
720
|
+
code;
|
|
721
|
+
constructor(code, message){
|
|
722
|
+
super(message);
|
|
723
|
+
this.name = 'PdfPageOrganizationError';
|
|
724
|
+
this.code = code;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
function validatedPdfPageIndexes(pageIndexes, pageCount, options = {}) {
|
|
728
|
+
if (!Array.isArray(pageIndexes) || !options.allowEmpty && 0 === pageIndexes.length) throw new PdfPageOrganizationError('pdf.pages.invalid-index', 'Select at least one PDF page.');
|
|
729
|
+
const seen = new Set();
|
|
730
|
+
for (const pageIndex of pageIndexes){
|
|
731
|
+
if (!Number.isSafeInteger(pageIndex) || pageIndex < 0 || pageIndex >= pageCount) throw new PdfPageOrganizationError('pdf.pages.invalid-index', `Page index ${String(pageIndex)} is outside the current ${pageCount}-page PDF.`);
|
|
732
|
+
if (seen.has(pageIndex)) throw new PdfPageOrganizationError('pdf.pages.duplicate-index', `Page index ${pageIndex} appears more than once.`);
|
|
733
|
+
seen.add(pageIndex);
|
|
734
|
+
}
|
|
735
|
+
const result = [
|
|
736
|
+
...pageIndexes
|
|
737
|
+
];
|
|
738
|
+
return options.preserveOrder ? result : result.sort((left, right)=>left - right);
|
|
739
|
+
}
|
|
740
|
+
function validatedPdfPageOrder(pageOrder, pageCount) {
|
|
741
|
+
if (!Array.isArray(pageOrder) || pageOrder.length !== pageCount) throw invalidOrder(pageCount);
|
|
742
|
+
try {
|
|
743
|
+
const order = validatedPdfPageIndexes(pageOrder, pageCount, {
|
|
744
|
+
preserveOrder: true
|
|
745
|
+
});
|
|
746
|
+
if (order.length === pageCount) return order;
|
|
747
|
+
} catch {}
|
|
748
|
+
throw invalidOrder(pageCount);
|
|
749
|
+
}
|
|
750
|
+
function reorderedPdfPageIndexes(pageCount, pageIndexes, targetIndex) {
|
|
751
|
+
const selected = validatedPdfPageIndexes(pageIndexes, pageCount);
|
|
752
|
+
if (!Number.isSafeInteger(targetIndex) || targetIndex < 0 || targetIndex > pageCount) throw new PdfPageOrganizationError('pdf.pages.invalid-index', `Insertion gap ${String(targetIndex)} is outside the current ${pageCount}-page PDF.`);
|
|
753
|
+
const selectedSet = new Set(selected);
|
|
754
|
+
const remaining = Array.from({
|
|
755
|
+
length: pageCount
|
|
756
|
+
}, (_, index)=>index).filter((index)=>!selectedSet.has(index));
|
|
757
|
+
const removedBeforeTarget = selected.filter((index)=>index < targetIndex).length;
|
|
758
|
+
const insertionIndex = Math.max(0, Math.min(remaining.length, targetIndex - removedBeforeTarget));
|
|
759
|
+
remaining.splice(insertionIndex, 0, ...selected);
|
|
760
|
+
return remaining;
|
|
761
|
+
}
|
|
762
|
+
function validatedPdfInsertionIndex(index, pageCount) {
|
|
763
|
+
if (!Number.isSafeInteger(index) || index < 0 || index > pageCount) throw new PdfPageOrganizationError('pdf.pages.invalid-index', `Insertion gap ${String(index)} is outside the current ${pageCount}-page PDF.`);
|
|
764
|
+
return index;
|
|
765
|
+
}
|
|
766
|
+
function validatedPdfPageSize(size) {
|
|
767
|
+
const candidate = size ?? {
|
|
768
|
+
height: 841.89,
|
|
769
|
+
width: 595.28
|
|
770
|
+
};
|
|
771
|
+
if (!Number.isFinite(candidate.width) || !Number.isFinite(candidate.height) || candidate.width < 18 || candidate.height < 18 || candidate.width > 14400 || candidate.height > 14400) throw new PdfPageOrganizationError('pdf.pages.invalid-page-size', "PDF page dimensions must be between 18 and 14400 points.");
|
|
772
|
+
return {
|
|
773
|
+
height: candidate.height,
|
|
774
|
+
width: candidate.width
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
function validatedPdfSplitBoundaries(splitAfterPageIndexes, pageCount) {
|
|
778
|
+
if (!Array.isArray(splitAfterPageIndexes) || 0 === splitAfterPageIndexes.length) throw invalidSplit(pageCount);
|
|
779
|
+
let previous = -1;
|
|
780
|
+
for (const pageIndex of splitAfterPageIndexes){
|
|
781
|
+
if (!Number.isSafeInteger(pageIndex) || pageIndex < 0 || pageIndex >= pageCount - 1 || pageIndex <= previous) throw invalidSplit(pageCount);
|
|
782
|
+
previous = pageIndex;
|
|
783
|
+
}
|
|
784
|
+
return [
|
|
785
|
+
...splitAfterPageIndexes
|
|
786
|
+
];
|
|
787
|
+
}
|
|
788
|
+
function invalidOrder(pageCount) {
|
|
789
|
+
return new PdfPageOrganizationError('pdf.pages.invalid-order', `A page order must contain every index from 0 through ${Math.max(0, pageCount - 1)} exactly once.`);
|
|
790
|
+
}
|
|
791
|
+
function invalidSplit(pageCount) {
|
|
792
|
+
return new PdfPageOrganizationError('pdf.pages.invalid-split', `Split boundaries must be unique, ascending, and before the last page of the ${pageCount}-page PDF.`);
|
|
793
|
+
}
|
|
717
794
|
function createPdfEditorExtensions() {
|
|
718
795
|
return [
|
|
719
796
|
createOfficeEditorExtension({
|
|
@@ -822,6 +899,65 @@ function createPdfEditorExtensions() {
|
|
|
822
899
|
}
|
|
823
900
|
})
|
|
824
901
|
}),
|
|
902
|
+
createOfficeEditorExtension({
|
|
903
|
+
name: 'pdfPageOrganization',
|
|
904
|
+
addCommands: ()=>({
|
|
905
|
+
deletePages: {
|
|
906
|
+
canExecute: (context, pageIndexes)=>canOrganizeSelectedPages(context, pageIndexes) && pageIndexes.length < context.viewer.state.totalPages,
|
|
907
|
+
execute: ({ pages }, pageIndexes)=>pages.mutate({
|
|
908
|
+
kind: 'delete',
|
|
909
|
+
pageIndexes
|
|
910
|
+
})
|
|
911
|
+
},
|
|
912
|
+
extractPages: {
|
|
913
|
+
canExecute: canOrganizeSelectedPages,
|
|
914
|
+
execute: ({ pages }, pageIndexes)=>pages.exportPages({
|
|
915
|
+
kind: 'extract',
|
|
916
|
+
pageIndexes
|
|
917
|
+
})
|
|
918
|
+
},
|
|
919
|
+
insertBlankPage: {
|
|
920
|
+
canExecute: (context, index)=>canOrganizePages(context) && safelyValidate(()=>validatedPdfInsertionIndex(index, context.viewer.state.totalPages)),
|
|
921
|
+
execute: ({ pages }, index)=>pages.mutate({
|
|
922
|
+
index,
|
|
923
|
+
kind: 'insert-blank'
|
|
924
|
+
})
|
|
925
|
+
},
|
|
926
|
+
mergePages: {
|
|
927
|
+
canExecute: (context, index, source)=>canOrganizePages(context) && source instanceof Blob && source.size > 0 && safelyValidate(()=>validatedPdfInsertionIndex(index, context.viewer.state.totalPages)),
|
|
928
|
+
execute: ({ pages }, index, source)=>pages.mutate({
|
|
929
|
+
index,
|
|
930
|
+
kind: 'merge'
|
|
931
|
+
}, source)
|
|
932
|
+
},
|
|
933
|
+
openPageOrganizer: {
|
|
934
|
+
canExecute: canOrganizePages,
|
|
935
|
+
execute: ({ openPageOrganizer })=>openPageOrganizer()
|
|
936
|
+
},
|
|
937
|
+
reorderPages: {
|
|
938
|
+
canExecute: (context, pageOrder)=>canOrganizePages(context) && safelyValidate(()=>validatedPdfPageOrder(pageOrder, context.viewer.state.totalPages)),
|
|
939
|
+
execute: ({ pages }, pageOrder)=>pages.mutate({
|
|
940
|
+
kind: 'reorder',
|
|
941
|
+
pageOrder
|
|
942
|
+
})
|
|
943
|
+
},
|
|
944
|
+
rotatePages: {
|
|
945
|
+
canExecute: (context, pageIndexes, degrees)=>canOrganizeSelectedPages(context, pageIndexes) && (90 === degrees || 180 === degrees || 270 === degrees),
|
|
946
|
+
execute: ({ pages }, pageIndexes, degrees)=>pages.mutate({
|
|
947
|
+
degrees,
|
|
948
|
+
kind: 'rotate',
|
|
949
|
+
pageIndexes
|
|
950
|
+
})
|
|
951
|
+
},
|
|
952
|
+
splitPages: {
|
|
953
|
+
canExecute: (context, splitAfterPageIndexes)=>canOrganizePages(context) && safelyValidate(()=>validatedPdfSplitBoundaries(splitAfterPageIndexes, context.viewer.state.totalPages)),
|
|
954
|
+
execute: ({ pages }, splitAfterPageIndexes)=>pages.exportPages({
|
|
955
|
+
kind: 'split',
|
|
956
|
+
splitAfterPageIndexes
|
|
957
|
+
})
|
|
958
|
+
}
|
|
959
|
+
})
|
|
960
|
+
}),
|
|
825
961
|
createOfficeEditorExtension({
|
|
826
962
|
name: 'pdfKeyboardShortcuts',
|
|
827
963
|
addKeyboardShortcuts: ()=>({
|
|
@@ -879,6 +1015,20 @@ function canSearch(viewer) {
|
|
|
879
1015
|
function canZoom(viewer) {
|
|
880
1016
|
return documentReady(viewer) && viewer.state.features.zoom;
|
|
881
1017
|
}
|
|
1018
|
+
function canOrganizePages(context) {
|
|
1019
|
+
return context.editable && documentReady(context.viewer) && context.pages.state.available && !context.pages.state.busy;
|
|
1020
|
+
}
|
|
1021
|
+
function canOrganizeSelectedPages(context, pageIndexes) {
|
|
1022
|
+
return canOrganizePages(context) && safelyValidate(()=>validatedPdfPageIndexes(pageIndexes, context.viewer.state.totalPages));
|
|
1023
|
+
}
|
|
1024
|
+
function safelyValidate(validate) {
|
|
1025
|
+
try {
|
|
1026
|
+
validate();
|
|
1027
|
+
return true;
|
|
1028
|
+
} catch {
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
882
1032
|
function canSave({ editable, save, viewer }) {
|
|
883
1033
|
return editable && save.enabled && documentReady(viewer) && viewer.state.features.export;
|
|
884
1034
|
}
|
|
@@ -1538,6 +1688,381 @@ function pdfThumbnailKeyboardPage(key, page, totalPages) {
|
|
|
1538
1688
|
if ('ArrowDown' === key || 'ArrowRight' === key) return Math.min(totalPages, page + 1);
|
|
1539
1689
|
return null;
|
|
1540
1690
|
}
|
|
1691
|
+
const PDF_ORGANIZER_ITEM_HEIGHT = 184;
|
|
1692
|
+
const PDF_ORGANIZER_VIEWPORT_HEIGHT = 552;
|
|
1693
|
+
function PdfPageOrganizerDialog({ busy, can, commands, currentPage, diagnostics, error, registry, restoreFocusTarget, totalPages, onClose, onDismissError }) {
|
|
1694
|
+
const initialIndex = Math.min(Math.max(0, totalPages - 1), Math.max(0, currentPage - 1));
|
|
1695
|
+
const [selected, setSelected] = useState(()=>new Set([
|
|
1696
|
+
initialIndex
|
|
1697
|
+
]));
|
|
1698
|
+
const [anchorIndex, setAnchorIndex] = useState(initialIndex);
|
|
1699
|
+
const [submitting, setSubmitting] = useState(false);
|
|
1700
|
+
const selectionAnchorRef = useRef(initialIndex);
|
|
1701
|
+
const mergeInputRef = useRef(null);
|
|
1702
|
+
const effectiveBusy = busy || submitting;
|
|
1703
|
+
const selectedIndexes = useMemo(()=>[
|
|
1704
|
+
...selected
|
|
1705
|
+
].sort((left, right)=>left - right), [
|
|
1706
|
+
selected
|
|
1707
|
+
]);
|
|
1708
|
+
const range = useMemo(()=>calculatePdfThumbnailRange({
|
|
1709
|
+
anchorIndex,
|
|
1710
|
+
itemHeight: PDF_ORGANIZER_ITEM_HEIGHT,
|
|
1711
|
+
totalPages,
|
|
1712
|
+
viewportHeight: PDF_ORGANIZER_VIEWPORT_HEIGHT
|
|
1713
|
+
}), [
|
|
1714
|
+
anchorIndex,
|
|
1715
|
+
totalPages
|
|
1716
|
+
]);
|
|
1717
|
+
const visibleIndexes = useMemo(()=>Array.from({
|
|
1718
|
+
length: Math.max(0, range.end - range.start)
|
|
1719
|
+
}, (_, index)=>range.start + index), [
|
|
1720
|
+
range.end,
|
|
1721
|
+
range.start
|
|
1722
|
+
]);
|
|
1723
|
+
const insertionIndex = selectedIndexes.length > 0 ? Math.min(totalPages, (selectedIndexes.at(-1) ?? 0) + 1) : totalPages;
|
|
1724
|
+
const splitBoundaries = selectedIndexes.filter((pageIndex)=>pageIndex < totalPages - 1);
|
|
1725
|
+
const moveLeftOrder = pageMoveOrder(totalPages, selectedIndexes, 'left');
|
|
1726
|
+
const moveRightOrder = pageMoveOrder(totalPages, selectedIndexes, 'right');
|
|
1727
|
+
const commitMutation = async (execute)=>{
|
|
1728
|
+
if (effectiveBusy) return;
|
|
1729
|
+
setSubmitting(true);
|
|
1730
|
+
const applied = await execute();
|
|
1731
|
+
if (applied) onClose();
|
|
1732
|
+
else setSubmitting(false);
|
|
1733
|
+
};
|
|
1734
|
+
const runExport = async (execute)=>{
|
|
1735
|
+
if (effectiveBusy) return;
|
|
1736
|
+
setSubmitting(true);
|
|
1737
|
+
await execute();
|
|
1738
|
+
setSubmitting(false);
|
|
1739
|
+
};
|
|
1740
|
+
return /*#__PURE__*/ jsxs(Dialog, {
|
|
1741
|
+
title: "组织 PDF 页面",
|
|
1742
|
+
description: "选择页面后插入、删除、旋转、重排、抽取、合并或拆分。",
|
|
1743
|
+
className: "work-pdf-page-organizer-dialog",
|
|
1744
|
+
closeDisabled: effectiveBusy,
|
|
1745
|
+
restoreFocusTarget: restoreFocusTarget,
|
|
1746
|
+
onClose: onClose,
|
|
1747
|
+
footer: /*#__PURE__*/ jsx(button_Button, {
|
|
1748
|
+
tone: "primary",
|
|
1749
|
+
disabled: effectiveBusy,
|
|
1750
|
+
onClick: onClose,
|
|
1751
|
+
children: "完成"
|
|
1752
|
+
}),
|
|
1753
|
+
children: [
|
|
1754
|
+
/*#__PURE__*/ jsx(office_text_field_OfficeFileInput, {
|
|
1755
|
+
ref: mergeInputRef,
|
|
1756
|
+
accept: ".pdf,application/pdf",
|
|
1757
|
+
"aria-label": "选择要合并的 PDF",
|
|
1758
|
+
disabled: effectiveBusy,
|
|
1759
|
+
onFileSelect: (file)=>{
|
|
1760
|
+
if (!can.mergePages(insertionIndex, file)) return;
|
|
1761
|
+
commitMutation(()=>commands.mergePages(insertionIndex, file));
|
|
1762
|
+
}
|
|
1763
|
+
}),
|
|
1764
|
+
/*#__PURE__*/ jsxs("div", {
|
|
1765
|
+
className: "work-pdf-page-organizer-actions",
|
|
1766
|
+
role: "toolbar",
|
|
1767
|
+
"aria-label": "PDF 页面组织命令",
|
|
1768
|
+
children: [
|
|
1769
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1770
|
+
tone: "secondary",
|
|
1771
|
+
"aria-label": "插入空白页",
|
|
1772
|
+
disabled: !can.insertBlankPage(insertionIndex) || effectiveBusy,
|
|
1773
|
+
onClick: ()=>void commitMutation(()=>commands.insertBlankPage(insertionIndex)),
|
|
1774
|
+
children: [
|
|
1775
|
+
/*#__PURE__*/ jsx(FilePlus2, {
|
|
1776
|
+
size: 15
|
|
1777
|
+
}),
|
|
1778
|
+
" 插入空白页"
|
|
1779
|
+
]
|
|
1780
|
+
}),
|
|
1781
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1782
|
+
tone: "secondary",
|
|
1783
|
+
"aria-label": "合并另一个 PDF",
|
|
1784
|
+
disabled: effectiveBusy,
|
|
1785
|
+
onClick: ()=>mergeInputRef.current?.click(),
|
|
1786
|
+
children: [
|
|
1787
|
+
/*#__PURE__*/ jsx(Files, {
|
|
1788
|
+
size: 15
|
|
1789
|
+
}),
|
|
1790
|
+
" 合并 PDF"
|
|
1791
|
+
]
|
|
1792
|
+
}),
|
|
1793
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1794
|
+
tone: "secondary",
|
|
1795
|
+
"aria-label": "前移所选页",
|
|
1796
|
+
disabled: !moveLeftOrder || !can.reorderPages(moveLeftOrder) || effectiveBusy,
|
|
1797
|
+
onClick: ()=>{
|
|
1798
|
+
if (moveLeftOrder) commitMutation(()=>commands.reorderPages(moveLeftOrder));
|
|
1799
|
+
},
|
|
1800
|
+
children: [
|
|
1801
|
+
/*#__PURE__*/ jsx(ChevronLeft, {
|
|
1802
|
+
size: 15
|
|
1803
|
+
}),
|
|
1804
|
+
" 前移"
|
|
1805
|
+
]
|
|
1806
|
+
}),
|
|
1807
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1808
|
+
tone: "secondary",
|
|
1809
|
+
"aria-label": "后移所选页",
|
|
1810
|
+
disabled: !moveRightOrder || !can.reorderPages(moveRightOrder) || effectiveBusy,
|
|
1811
|
+
onClick: ()=>{
|
|
1812
|
+
if (moveRightOrder) commitMutation(()=>commands.reorderPages(moveRightOrder));
|
|
1813
|
+
},
|
|
1814
|
+
children: [
|
|
1815
|
+
/*#__PURE__*/ jsx(ChevronRight, {
|
|
1816
|
+
size: 15
|
|
1817
|
+
}),
|
|
1818
|
+
" 后移"
|
|
1819
|
+
]
|
|
1820
|
+
}),
|
|
1821
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1822
|
+
tone: "secondary",
|
|
1823
|
+
"aria-label": "向左旋转所选页",
|
|
1824
|
+
disabled: !can.rotatePages(selectedIndexes, 270) || effectiveBusy,
|
|
1825
|
+
onClick: ()=>void commitMutation(()=>commands.rotatePages(selectedIndexes, 270)),
|
|
1826
|
+
children: [
|
|
1827
|
+
/*#__PURE__*/ jsx(RotateCcw, {
|
|
1828
|
+
size: 15
|
|
1829
|
+
}),
|
|
1830
|
+
" 左转"
|
|
1831
|
+
]
|
|
1832
|
+
}),
|
|
1833
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1834
|
+
tone: "secondary",
|
|
1835
|
+
"aria-label": "向右旋转所选页",
|
|
1836
|
+
disabled: !can.rotatePages(selectedIndexes, 90) || effectiveBusy,
|
|
1837
|
+
onClick: ()=>void commitMutation(()=>commands.rotatePages(selectedIndexes, 90)),
|
|
1838
|
+
children: [
|
|
1839
|
+
/*#__PURE__*/ jsx(RotateCw, {
|
|
1840
|
+
size: 15
|
|
1841
|
+
}),
|
|
1842
|
+
" 右转"
|
|
1843
|
+
]
|
|
1844
|
+
}),
|
|
1845
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1846
|
+
tone: "secondary",
|
|
1847
|
+
"aria-label": "抽取所选页",
|
|
1848
|
+
disabled: !can.extractPages(selectedIndexes) || effectiveBusy,
|
|
1849
|
+
onClick: ()=>void runExport(()=>commands.extractPages(selectedIndexes)),
|
|
1850
|
+
children: [
|
|
1851
|
+
/*#__PURE__*/ jsx(Download, {
|
|
1852
|
+
size: 15
|
|
1853
|
+
}),
|
|
1854
|
+
" 抽取"
|
|
1855
|
+
]
|
|
1856
|
+
}),
|
|
1857
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1858
|
+
tone: "secondary",
|
|
1859
|
+
"aria-label": "在所选页后拆分",
|
|
1860
|
+
disabled: !can.splitPages(splitBoundaries) || effectiveBusy,
|
|
1861
|
+
onClick: ()=>void runExport(()=>commands.splitPages(splitBoundaries)),
|
|
1862
|
+
children: [
|
|
1863
|
+
/*#__PURE__*/ jsx(Scissors, {
|
|
1864
|
+
size: 15
|
|
1865
|
+
}),
|
|
1866
|
+
" 拆分"
|
|
1867
|
+
]
|
|
1868
|
+
}),
|
|
1869
|
+
/*#__PURE__*/ jsxs(button_Button, {
|
|
1870
|
+
tone: "danger",
|
|
1871
|
+
"aria-label": "删除所选页",
|
|
1872
|
+
disabled: !can.deletePages(selectedIndexes) || effectiveBusy,
|
|
1873
|
+
onClick: ()=>void commitMutation(()=>commands.deletePages(selectedIndexes)),
|
|
1874
|
+
children: [
|
|
1875
|
+
/*#__PURE__*/ jsx(Trash2, {
|
|
1876
|
+
size: 15
|
|
1877
|
+
}),
|
|
1878
|
+
" 删除"
|
|
1879
|
+
]
|
|
1880
|
+
})
|
|
1881
|
+
]
|
|
1882
|
+
}),
|
|
1883
|
+
/*#__PURE__*/ jsxs("div", {
|
|
1884
|
+
className: "work-pdf-page-organizer-selection",
|
|
1885
|
+
children: [
|
|
1886
|
+
/*#__PURE__*/ jsxs("output", {
|
|
1887
|
+
"aria-live": "polite",
|
|
1888
|
+
children: [
|
|
1889
|
+
"已选择 ",
|
|
1890
|
+
selectedIndexes.length,
|
|
1891
|
+
" / ",
|
|
1892
|
+
totalPages,
|
|
1893
|
+
" 页"
|
|
1894
|
+
]
|
|
1895
|
+
}),
|
|
1896
|
+
/*#__PURE__*/ jsx("button", {
|
|
1897
|
+
type: "button",
|
|
1898
|
+
disabled: effectiveBusy,
|
|
1899
|
+
onClick: ()=>setSelected(new Set(Array.from({
|
|
1900
|
+
length: totalPages
|
|
1901
|
+
}, (_, index)=>index))),
|
|
1902
|
+
children: "全选"
|
|
1903
|
+
}),
|
|
1904
|
+
/*#__PURE__*/ jsx("button", {
|
|
1905
|
+
type: "button",
|
|
1906
|
+
disabled: effectiveBusy,
|
|
1907
|
+
onClick: ()=>setSelected(new Set([
|
|
1908
|
+
initialIndex
|
|
1909
|
+
])),
|
|
1910
|
+
children: "仅当前页"
|
|
1911
|
+
})
|
|
1912
|
+
]
|
|
1913
|
+
}),
|
|
1914
|
+
/*#__PURE__*/ jsx("section", {
|
|
1915
|
+
className: "work-pdf-page-organizer-viewport",
|
|
1916
|
+
"aria-label": "可重排 PDF 页面",
|
|
1917
|
+
onScroll: (event)=>setAnchorIndex(Math.floor(event.currentTarget.scrollTop / PDF_ORGANIZER_ITEM_HEIGHT)),
|
|
1918
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
1919
|
+
className: "work-pdf-page-organizer-list",
|
|
1920
|
+
children: [
|
|
1921
|
+
/*#__PURE__*/ jsx(OrganizerSpacer, {
|
|
1922
|
+
height: range.start * PDF_ORGANIZER_ITEM_HEIGHT
|
|
1923
|
+
}),
|
|
1924
|
+
visibleIndexes.map((pageIndex)=>/*#__PURE__*/ jsx(OrganizerPage, {
|
|
1925
|
+
current: pageIndex === initialIndex,
|
|
1926
|
+
pageIndex: pageIndex,
|
|
1927
|
+
registry: registry,
|
|
1928
|
+
selected: selected.has(pageIndex),
|
|
1929
|
+
disabled: effectiveBusy,
|
|
1930
|
+
onDrop: (targetIndex)=>{
|
|
1931
|
+
const order = reorderedPdfPageIndexes(totalPages, selectedIndexes, targetIndex);
|
|
1932
|
+
if (can.reorderPages(order)) commitMutation(()=>commands.reorderPages(order));
|
|
1933
|
+
},
|
|
1934
|
+
onSelect: (event)=>{
|
|
1935
|
+
setSelected((current)=>nextPageSelection(current, pageIndex, selectionAnchorRef.current, event));
|
|
1936
|
+
if (!event.shiftKey) selectionAnchorRef.current = pageIndex;
|
|
1937
|
+
}
|
|
1938
|
+
}, pageIndex)),
|
|
1939
|
+
/*#__PURE__*/ jsx(OrganizerSpacer, {
|
|
1940
|
+
height: Math.max(0, totalPages - range.end) * PDF_ORGANIZER_ITEM_HEIGHT
|
|
1941
|
+
})
|
|
1942
|
+
]
|
|
1943
|
+
})
|
|
1944
|
+
}),
|
|
1945
|
+
error && /*#__PURE__*/ jsxs("div", {
|
|
1946
|
+
className: "work-pdf-page-organizer-error",
|
|
1947
|
+
role: "alert",
|
|
1948
|
+
children: [
|
|
1949
|
+
/*#__PURE__*/ jsxs("div", {
|
|
1950
|
+
children: [
|
|
1951
|
+
/*#__PURE__*/ jsx("strong", {
|
|
1952
|
+
children: "无法安全完成页面操作"
|
|
1953
|
+
}),
|
|
1954
|
+
/*#__PURE__*/ jsx("p", {
|
|
1955
|
+
children: error.message
|
|
1956
|
+
}),
|
|
1957
|
+
/*#__PURE__*/ jsx("code", {
|
|
1958
|
+
children: error.code
|
|
1959
|
+
})
|
|
1960
|
+
]
|
|
1961
|
+
}),
|
|
1962
|
+
/*#__PURE__*/ jsx("button", {
|
|
1963
|
+
type: "button",
|
|
1964
|
+
"aria-label": "关闭错误提示",
|
|
1965
|
+
onClick: onDismissError,
|
|
1966
|
+
children: "\xd7"
|
|
1967
|
+
})
|
|
1968
|
+
]
|
|
1969
|
+
}),
|
|
1970
|
+
diagnostics.length > 0 && /*#__PURE__*/ jsx("ul", {
|
|
1971
|
+
className: "work-pdf-page-organizer-diagnostics",
|
|
1972
|
+
role: "status",
|
|
1973
|
+
children: diagnostics.map((diagnostic)=>/*#__PURE__*/ jsx("li", {
|
|
1974
|
+
children: diagnostic.message
|
|
1975
|
+
}, diagnostic.code))
|
|
1976
|
+
}),
|
|
1977
|
+
/*#__PURE__*/ jsx("p", {
|
|
1978
|
+
className: "work-pdf-page-organizer-boundary",
|
|
1979
|
+
children: "页面操作在 Worker 中生成新 PDF,并由一个撤销记录恢复。签名、加密、表单、目录或标记结构无法安全重写时会明确停止。"
|
|
1980
|
+
})
|
|
1981
|
+
]
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
function OrganizerPage({ current, disabled, pageIndex, registry, selected, onDrop, onSelect }) {
|
|
1985
|
+
const { sourceUrl, state } = usePdfThumbnailSource(registry, pageIndex + 1);
|
|
1986
|
+
return /*#__PURE__*/ jsxs("button", {
|
|
1987
|
+
type: "button",
|
|
1988
|
+
className: selected ? 'selected' : void 0,
|
|
1989
|
+
"aria-current": current ? 'page' : void 0,
|
|
1990
|
+
"aria-label": `选择第 ${pageIndex + 1} 页`,
|
|
1991
|
+
"aria-pressed": selected,
|
|
1992
|
+
"data-pdf-organizer-page-index": pageIndex,
|
|
1993
|
+
disabled: disabled,
|
|
1994
|
+
draggable: !disabled,
|
|
1995
|
+
onClick: onSelect,
|
|
1996
|
+
onDragOver: (event)=>{
|
|
1997
|
+
event.preventDefault();
|
|
1998
|
+
event.dataTransfer.dropEffect = 'move';
|
|
1999
|
+
},
|
|
2000
|
+
onDragStart: (event)=>{
|
|
2001
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
2002
|
+
event.dataTransfer.setData('text/plain', String(pageIndex));
|
|
2003
|
+
},
|
|
2004
|
+
onDrop: (event)=>{
|
|
2005
|
+
event.preventDefault();
|
|
2006
|
+
onDrop(pageIndex);
|
|
2007
|
+
},
|
|
2008
|
+
children: [
|
|
2009
|
+
/*#__PURE__*/ jsxs("span", {
|
|
2010
|
+
className: "work-pdf-page-organizer-number",
|
|
2011
|
+
children: [
|
|
2012
|
+
pageIndex + 1,
|
|
2013
|
+
current && /*#__PURE__*/ jsx("small", {
|
|
2014
|
+
children: "当前"
|
|
2015
|
+
})
|
|
2016
|
+
]
|
|
2017
|
+
}),
|
|
2018
|
+
/*#__PURE__*/ jsx("span", {
|
|
2019
|
+
className: "work-pdf-page-organizer-preview",
|
|
2020
|
+
"data-state": state,
|
|
2021
|
+
children: sourceUrl && /*#__PURE__*/ jsx("img", {
|
|
2022
|
+
src: sourceUrl,
|
|
2023
|
+
alt: "",
|
|
2024
|
+
draggable: false
|
|
2025
|
+
})
|
|
2026
|
+
})
|
|
2027
|
+
]
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
function nextPageSelection(current, pageIndex, anchorIndex, event) {
|
|
2031
|
+
if (event.shiftKey) {
|
|
2032
|
+
const start = Math.min(anchorIndex, pageIndex);
|
|
2033
|
+
const end = Math.max(anchorIndex, pageIndex);
|
|
2034
|
+
return new Set(Array.from({
|
|
2035
|
+
length: end - start + 1
|
|
2036
|
+
}, (_, index)=>start + index));
|
|
2037
|
+
}
|
|
2038
|
+
if (event.metaKey || event.ctrlKey) {
|
|
2039
|
+
const next = new Set(current);
|
|
2040
|
+
if (next.has(pageIndex)) next.delete(pageIndex);
|
|
2041
|
+
else next.add(pageIndex);
|
|
2042
|
+
return next;
|
|
2043
|
+
}
|
|
2044
|
+
return new Set([
|
|
2045
|
+
pageIndex
|
|
2046
|
+
]);
|
|
2047
|
+
}
|
|
2048
|
+
function pageMoveOrder(pageCount, selected, direction) {
|
|
2049
|
+
if (0 === selected.length) return null;
|
|
2050
|
+
if ('left' === direction) {
|
|
2051
|
+
const first = selected[0] ?? 0;
|
|
2052
|
+
return first <= 0 ? null : reorderedPdfPageIndexes(pageCount, selected, first - 1);
|
|
2053
|
+
}
|
|
2054
|
+
const last = selected.at(-1) ?? pageCount - 1;
|
|
2055
|
+
return last >= pageCount - 1 ? null : reorderedPdfPageIndexes(pageCount, selected, last + 2);
|
|
2056
|
+
}
|
|
2057
|
+
function OrganizerSpacer({ height }) {
|
|
2058
|
+
if (height <= 0) return null;
|
|
2059
|
+
return /*#__PURE__*/ jsx("span", {
|
|
2060
|
+
"aria-hidden": "true",
|
|
2061
|
+
style: {
|
|
2062
|
+
height
|
|
2063
|
+
}
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
1541
2066
|
function StatusBadge({ tone = 'neutral', children, className = '' }) {
|
|
1542
2067
|
return /*#__PURE__*/ jsx("span", {
|
|
1543
2068
|
className: `ds-status-badge ${tone}${className ? ` ${className}` : ''}`,
|
|
@@ -1554,8 +2079,9 @@ const pdfKeyboardShortcuts = {
|
|
|
1554
2079
|
zoomIn: 'Control+= Meta+= Control+Shift++ Meta+Shift++',
|
|
1555
2080
|
zoomOut: 'Control+- Meta+-'
|
|
1556
2081
|
};
|
|
1557
|
-
function PdfToolbar({ annotationState, can, commands, editable, pageNavigation, saveAvailable, saveLabel, saveState, searchInputRef, state }) {
|
|
2082
|
+
function PdfToolbar({ annotationState, can, commands, editable, pageOrganizationAvailable, pageNavigation, saveAvailable, saveLabel, saveState, searchInputRef, state }) {
|
|
1558
2083
|
const showSave = saveAvailable ?? editable;
|
|
2084
|
+
const showPageOrganization = pageOrganizationAvailable ?? editable;
|
|
1559
2085
|
const [pageValue, setPageValue] = useState('');
|
|
1560
2086
|
const [searchValue, setSearchValue] = useState('');
|
|
1561
2087
|
const cancelPageBlurCommitRef = useRef(false);
|
|
@@ -1662,6 +2188,23 @@ function PdfToolbar({ annotationState, can, commands, editable, pageNavigation,
|
|
|
1662
2188
|
})
|
|
1663
2189
|
]
|
|
1664
2190
|
}),
|
|
2191
|
+
showPageOrganization && /*#__PURE__*/ jsx("div", {
|
|
2192
|
+
className: "work-pdf-toolbar-group work-pdf-page-organization",
|
|
2193
|
+
children: /*#__PURE__*/ jsxs(button_Button, {
|
|
2194
|
+
tone: "secondary",
|
|
2195
|
+
className: "work-pdf-page-organizer-trigger",
|
|
2196
|
+
"data-pdf-page-organizer-trigger": true,
|
|
2197
|
+
"aria-label": "组织 PDF 页面",
|
|
2198
|
+
disabled: !can.openPageOrganizer(),
|
|
2199
|
+
onClick: commands.openPageOrganizer,
|
|
2200
|
+
children: [
|
|
2201
|
+
/*#__PURE__*/ jsx(LayoutGrid, {
|
|
2202
|
+
size: 15
|
|
2203
|
+
}),
|
|
2204
|
+
"组织页面"
|
|
2205
|
+
]
|
|
2206
|
+
})
|
|
2207
|
+
}),
|
|
1665
2208
|
editable && /*#__PURE__*/ jsxs("fieldset", {
|
|
1666
2209
|
className: "work-pdf-toolbar-group work-pdf-annotation",
|
|
1667
2210
|
children: [
|
|
@@ -1759,6 +2302,7 @@ function PdfToolbar({ annotationState, can, commands, editable, pageNavigation,
|
|
|
1759
2302
|
can: can,
|
|
1760
2303
|
commands: commands,
|
|
1761
2304
|
editable: editable,
|
|
2305
|
+
pageOrganizationAvailable: showPageOrganization,
|
|
1762
2306
|
state: state
|
|
1763
2307
|
}),
|
|
1764
2308
|
/*#__PURE__*/ jsx("search", {
|
|
@@ -2104,7 +2648,7 @@ function movePdfAnnotationStyleOption(event) {
|
|
|
2104
2648
|
});
|
|
2105
2649
|
next?.click();
|
|
2106
2650
|
}
|
|
2107
|
-
function PdfToolbarOverflow({ annotationState, can, commands, editable, state }) {
|
|
2651
|
+
function PdfToolbarOverflow({ annotationState, can, commands, editable, pageOrganizationAvailable, state }) {
|
|
2108
2652
|
const hasOverflowTools = editable && annotationState.available || editable && state.features.history || state.features.navigation || state.features.search || state.features.zoom;
|
|
2109
2653
|
return /*#__PURE__*/ jsx(Popover, {
|
|
2110
2654
|
label: "更多 PDF 工具",
|
|
@@ -2254,6 +2798,18 @@ function PdfToolbarOverflow({ annotationState, can, commands, editable, state })
|
|
|
2254
2798
|
})
|
|
2255
2799
|
]
|
|
2256
2800
|
}),
|
|
2801
|
+
pageOrganizationAvailable && /*#__PURE__*/ jsx("fieldset", {
|
|
2802
|
+
className: "work-pdf-overflow-group",
|
|
2803
|
+
"aria-label": "页面组织",
|
|
2804
|
+
children: /*#__PURE__*/ jsx(PdfOverflowAction, {
|
|
2805
|
+
label: "组织页面",
|
|
2806
|
+
disabled: !can.openPageOrganizer(),
|
|
2807
|
+
onSelect: ()=>select(commands.openPageOrganizer),
|
|
2808
|
+
children: /*#__PURE__*/ jsx(LayoutGrid, {
|
|
2809
|
+
size: 15
|
|
2810
|
+
})
|
|
2811
|
+
})
|
|
2812
|
+
}),
|
|
2257
2813
|
state.features.navigation && /*#__PURE__*/ jsxs("fieldset", {
|
|
2258
2814
|
className: "work-pdf-overflow-group",
|
|
2259
2815
|
"aria-label": "翻页",
|
|
@@ -2740,6 +3296,357 @@ function pdf_viewer_controller_safely(read) {
|
|
|
2740
3296
|
function errorMessage(error) {
|
|
2741
3297
|
return error instanceof Error && error.message ? error.message : 'PDF viewer initialization failed.';
|
|
2742
3298
|
}
|
|
3299
|
+
const PDF_PAGE_ORGANIZATION_WORKER_TIMEOUT_MS = 120000;
|
|
3300
|
+
async function runPdfPageOrganizationJob(job, signal) {
|
|
3301
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3302
|
+
if ("u" < typeof Worker) return runDirectly(job, signal);
|
|
3303
|
+
let worker;
|
|
3304
|
+
try {
|
|
3305
|
+
worker = new Worker(new URL('./pdf-page-organization.worker.js', import.meta.url), {
|
|
3306
|
+
name: 'a3s-office-pdf-page-organization'
|
|
3307
|
+
});
|
|
3308
|
+
} catch {
|
|
3309
|
+
return runDirectly(job, signal);
|
|
3310
|
+
}
|
|
3311
|
+
return new Promise((resolve, reject)=>{
|
|
3312
|
+
let settled = false;
|
|
3313
|
+
const timeout = setTimeout(()=>{
|
|
3314
|
+
finish(void 0, new PdfPageOrganizationError('pdf.pages.invalid-source', 'PDF page organization timed out.'));
|
|
3315
|
+
}, PDF_PAGE_ORGANIZATION_WORKER_TIMEOUT_MS);
|
|
3316
|
+
const finish = (result, error)=>{
|
|
3317
|
+
if (settled) return;
|
|
3318
|
+
settled = true;
|
|
3319
|
+
clearTimeout(timeout);
|
|
3320
|
+
signal?.removeEventListener('abort', handleAbort);
|
|
3321
|
+
worker.removeEventListener('error', handleWorkerError);
|
|
3322
|
+
worker.removeEventListener('message', handleMessage);
|
|
3323
|
+
worker.removeEventListener('messageerror', handleWorkerError);
|
|
3324
|
+
worker.terminate();
|
|
3325
|
+
if (error) reject(error);
|
|
3326
|
+
else if (result) resolve(result);
|
|
3327
|
+
else reject(new Error('PDF page worker returned no result.'));
|
|
3328
|
+
};
|
|
3329
|
+
const handleAbort = ()=>finish(void 0, pdfPageOrganizationAbortError());
|
|
3330
|
+
const handleWorkerError = ()=>finish(void 0, new PdfPageOrganizationError('pdf.pages.invalid-source', 'The PDF page worker failed before producing a result.'));
|
|
3331
|
+
const handleMessage = (event)=>{
|
|
3332
|
+
const response = event.data;
|
|
3333
|
+
if (!response || 'object' != typeof response) return void handleWorkerError();
|
|
3334
|
+
if ('failure' === response.kind) return void finish(void 0, new PdfPageOrganizationError(response.code, response.message));
|
|
3335
|
+
if ('success' === response.kind) finish(response.result);
|
|
3336
|
+
else handleWorkerError();
|
|
3337
|
+
};
|
|
3338
|
+
signal?.addEventListener('abort', handleAbort, {
|
|
3339
|
+
once: true
|
|
3340
|
+
});
|
|
3341
|
+
worker.addEventListener('error', handleWorkerError, {
|
|
3342
|
+
once: true
|
|
3343
|
+
});
|
|
3344
|
+
worker.addEventListener('message', handleMessage);
|
|
3345
|
+
worker.addEventListener('messageerror', handleWorkerError, {
|
|
3346
|
+
once: true
|
|
3347
|
+
});
|
|
3348
|
+
if (signal?.aborted) return void handleAbort();
|
|
3349
|
+
const transferableJob = transferablePdfPageJob(job);
|
|
3350
|
+
const request = {
|
|
3351
|
+
job: transferableJob.job,
|
|
3352
|
+
kind: 'run'
|
|
3353
|
+
};
|
|
3354
|
+
try {
|
|
3355
|
+
worker.postMessage(request, {
|
|
3356
|
+
transfer: transferableJob.transfer
|
|
3357
|
+
});
|
|
3358
|
+
} catch {
|
|
3359
|
+
handleWorkerError();
|
|
3360
|
+
}
|
|
3361
|
+
});
|
|
3362
|
+
}
|
|
3363
|
+
async function runDirectly(job, signal) {
|
|
3364
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3365
|
+
const { applyPdfPageOrganizationJob } = await import("./0~pdf-page-organization-engine.js");
|
|
3366
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3367
|
+
const result = await applyPdfPageOrganizationJob(job);
|
|
3368
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3369
|
+
return result;
|
|
3370
|
+
}
|
|
3371
|
+
function transferablePdfPageJob(job) {
|
|
3372
|
+
const source = transferableSource(job.source);
|
|
3373
|
+
if ('export' === job.kind) return {
|
|
3374
|
+
job: {
|
|
3375
|
+
...job,
|
|
3376
|
+
source: source.source
|
|
3377
|
+
},
|
|
3378
|
+
transfer: [
|
|
3379
|
+
source.source
|
|
3380
|
+
]
|
|
3381
|
+
};
|
|
3382
|
+
const imported = job.importSource ? transferableSource(job.importSource) : void 0;
|
|
3383
|
+
return {
|
|
3384
|
+
job: {
|
|
3385
|
+
...job,
|
|
3386
|
+
...imported ? {
|
|
3387
|
+
importSource: imported.source
|
|
3388
|
+
} : {},
|
|
3389
|
+
source: source.source
|
|
3390
|
+
},
|
|
3391
|
+
transfer: imported ? [
|
|
3392
|
+
source.source,
|
|
3393
|
+
imported.source
|
|
3394
|
+
] : [
|
|
3395
|
+
source.source
|
|
3396
|
+
]
|
|
3397
|
+
};
|
|
3398
|
+
}
|
|
3399
|
+
function transferableSource(source) {
|
|
3400
|
+
if (source instanceof ArrayBuffer) return {
|
|
3401
|
+
source
|
|
3402
|
+
};
|
|
3403
|
+
const copy = new Uint8Array(source.byteLength);
|
|
3404
|
+
copy.set(source);
|
|
3405
|
+
return {
|
|
3406
|
+
source: copy.buffer
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
function pdfPageOrganizationAbortError() {
|
|
3410
|
+
if ("u" > typeof DOMException) return new DOMException('PDF page organization was cancelled.', 'AbortError');
|
|
3411
|
+
const error = new Error('PDF page organization was cancelled.');
|
|
3412
|
+
error.name = 'AbortError';
|
|
3413
|
+
return error;
|
|
3414
|
+
}
|
|
3415
|
+
const initialState = {
|
|
3416
|
+
available: false,
|
|
3417
|
+
busy: false,
|
|
3418
|
+
canRedo: false,
|
|
3419
|
+
canUndo: false,
|
|
3420
|
+
diagnostics: [],
|
|
3421
|
+
error: null,
|
|
3422
|
+
revision: 0
|
|
3423
|
+
};
|
|
3424
|
+
function usePdfPageOrganization(options, runJob = runPdfPageOrganizationJob) {
|
|
3425
|
+
const [state, setState] = useState({
|
|
3426
|
+
...initialState,
|
|
3427
|
+
available: options.enabled
|
|
3428
|
+
});
|
|
3429
|
+
const undoRef = useRef([]);
|
|
3430
|
+
const redoRef = useRef([]);
|
|
3431
|
+
const abortRef = useRef(null);
|
|
3432
|
+
const optionsRef = useRef(options);
|
|
3433
|
+
const runJobRef = useRef(runJob);
|
|
3434
|
+
optionsRef.current = options;
|
|
3435
|
+
runJobRef.current = runJob;
|
|
3436
|
+
useEffect(()=>{
|
|
3437
|
+
abortRef.current?.abort();
|
|
3438
|
+
abortRef.current = null;
|
|
3439
|
+
undoRef.current = [];
|
|
3440
|
+
redoRef.current = [];
|
|
3441
|
+
setState({
|
|
3442
|
+
...initialState,
|
|
3443
|
+
available: options.enabled
|
|
3444
|
+
});
|
|
3445
|
+
}, [
|
|
3446
|
+
options.enabled,
|
|
3447
|
+
options.resetKey
|
|
3448
|
+
]);
|
|
3449
|
+
useEffect(()=>()=>{
|
|
3450
|
+
abortRef.current?.abort();
|
|
3451
|
+
}, []);
|
|
3452
|
+
const mutate = useCallback(async (mutation, importSource)=>{
|
|
3453
|
+
const currentOptions = optionsRef.current;
|
|
3454
|
+
if (!currentOptions.enabled || abortRef.current) return false;
|
|
3455
|
+
const abort = new AbortController();
|
|
3456
|
+
abortRef.current = abort;
|
|
3457
|
+
setState((current)=>({
|
|
3458
|
+
...current,
|
|
3459
|
+
busy: true,
|
|
3460
|
+
diagnostics: [],
|
|
3461
|
+
error: null
|
|
3462
|
+
}));
|
|
3463
|
+
try {
|
|
3464
|
+
const before = await currentOptions.readCurrentSource();
|
|
3465
|
+
const job = {
|
|
3466
|
+
...importSource ? {
|
|
3467
|
+
importSource: await importSource.arrayBuffer()
|
|
3468
|
+
} : {},
|
|
3469
|
+
kind: 'mutate',
|
|
3470
|
+
mutation,
|
|
3471
|
+
source: await before.arrayBuffer()
|
|
3472
|
+
};
|
|
3473
|
+
const result = await runJobRef.current(job, abort.signal);
|
|
3474
|
+
if ('mutated' !== result.kind) throw new Error('PDF page mutation returned export files.');
|
|
3475
|
+
const after = pdfBlob(result.source);
|
|
3476
|
+
const entry = {
|
|
3477
|
+
after,
|
|
3478
|
+
before
|
|
3479
|
+
};
|
|
3480
|
+
undoRef.current = [
|
|
3481
|
+
...undoRef.current,
|
|
3482
|
+
entry
|
|
3483
|
+
];
|
|
3484
|
+
redoRef.current = [];
|
|
3485
|
+
currentOptions.replaceSource(after);
|
|
3486
|
+
setState((current)=>({
|
|
3487
|
+
...current,
|
|
3488
|
+
busy: false,
|
|
3489
|
+
canRedo: false,
|
|
3490
|
+
canUndo: true,
|
|
3491
|
+
diagnostics: result.diagnostics,
|
|
3492
|
+
error: null,
|
|
3493
|
+
revision: current.revision + 1
|
|
3494
|
+
}));
|
|
3495
|
+
return true;
|
|
3496
|
+
} catch (error) {
|
|
3497
|
+
if (abort.signal.aborted) return false;
|
|
3498
|
+
setState((current)=>({
|
|
3499
|
+
...current,
|
|
3500
|
+
busy: false,
|
|
3501
|
+
diagnostics: [],
|
|
3502
|
+
error: controllerError(error)
|
|
3503
|
+
}));
|
|
3504
|
+
return false;
|
|
3505
|
+
} finally{
|
|
3506
|
+
if (abortRef.current === abort) abortRef.current = null;
|
|
3507
|
+
}
|
|
3508
|
+
}, []);
|
|
3509
|
+
const exportPages = useCallback(async (operation)=>{
|
|
3510
|
+
const currentOptions = optionsRef.current;
|
|
3511
|
+
if (!currentOptions.enabled || abortRef.current) return false;
|
|
3512
|
+
const abort = new AbortController();
|
|
3513
|
+
abortRef.current = abort;
|
|
3514
|
+
setState((current)=>({
|
|
3515
|
+
...current,
|
|
3516
|
+
busy: true,
|
|
3517
|
+
diagnostics: [],
|
|
3518
|
+
error: null
|
|
3519
|
+
}));
|
|
3520
|
+
try {
|
|
3521
|
+
const before = await currentOptions.readCurrentSource();
|
|
3522
|
+
const result = await runJobRef.current({
|
|
3523
|
+
kind: 'export',
|
|
3524
|
+
operation,
|
|
3525
|
+
source: await before.arrayBuffer()
|
|
3526
|
+
}, abort.signal);
|
|
3527
|
+
if ('exported' !== result.kind) throw new Error('PDF page export returned a mutated source.');
|
|
3528
|
+
const files = namedPdfExports(currentOptions.fileName, operation.kind, result);
|
|
3529
|
+
const accepted = currentOptions.onExport ? await currentOptions.onExport(files) : downloadPdfExports(files);
|
|
3530
|
+
if (!accepted) throw new Error('The host rejected the PDF page export.');
|
|
3531
|
+
setState((current)=>({
|
|
3532
|
+
...current,
|
|
3533
|
+
busy: false,
|
|
3534
|
+
diagnostics: result.diagnostics,
|
|
3535
|
+
error: null
|
|
3536
|
+
}));
|
|
3537
|
+
return true;
|
|
3538
|
+
} catch (error) {
|
|
3539
|
+
if (abort.signal.aborted) return false;
|
|
3540
|
+
setState((current)=>({
|
|
3541
|
+
...current,
|
|
3542
|
+
busy: false,
|
|
3543
|
+
diagnostics: [],
|
|
3544
|
+
error: controllerError(error)
|
|
3545
|
+
}));
|
|
3546
|
+
return false;
|
|
3547
|
+
} finally{
|
|
3548
|
+
if (abortRef.current === abort) abortRef.current = null;
|
|
3549
|
+
}
|
|
3550
|
+
}, []);
|
|
3551
|
+
const undo = useCallback(()=>{
|
|
3552
|
+
if (!optionsRef.current.enabled || abortRef.current) return;
|
|
3553
|
+
const entry = undoRef.current.at(-1);
|
|
3554
|
+
if (!entry) return;
|
|
3555
|
+
undoRef.current = undoRef.current.slice(0, -1);
|
|
3556
|
+
redoRef.current = [
|
|
3557
|
+
...redoRef.current,
|
|
3558
|
+
entry
|
|
3559
|
+
];
|
|
3560
|
+
optionsRef.current.replaceSource(entry.before);
|
|
3561
|
+
setState((current)=>({
|
|
3562
|
+
...current,
|
|
3563
|
+
canRedo: true,
|
|
3564
|
+
canUndo: undoRef.current.length > 0,
|
|
3565
|
+
diagnostics: [],
|
|
3566
|
+
error: null,
|
|
3567
|
+
revision: current.revision + 1
|
|
3568
|
+
}));
|
|
3569
|
+
}, []);
|
|
3570
|
+
const redo = useCallback(()=>{
|
|
3571
|
+
if (!optionsRef.current.enabled || abortRef.current) return;
|
|
3572
|
+
const entry = redoRef.current.at(-1);
|
|
3573
|
+
if (!entry) return;
|
|
3574
|
+
redoRef.current = redoRef.current.slice(0, -1);
|
|
3575
|
+
undoRef.current = [
|
|
3576
|
+
...undoRef.current,
|
|
3577
|
+
entry
|
|
3578
|
+
];
|
|
3579
|
+
optionsRef.current.replaceSource(entry.after);
|
|
3580
|
+
setState((current)=>({
|
|
3581
|
+
...current,
|
|
3582
|
+
canRedo: redoRef.current.length > 0,
|
|
3583
|
+
canUndo: true,
|
|
3584
|
+
diagnostics: [],
|
|
3585
|
+
error: null,
|
|
3586
|
+
revision: current.revision + 1
|
|
3587
|
+
}));
|
|
3588
|
+
}, []);
|
|
3589
|
+
const dismissError = useCallback(()=>{
|
|
3590
|
+
setState((current)=>({
|
|
3591
|
+
...current,
|
|
3592
|
+
error: null
|
|
3593
|
+
}));
|
|
3594
|
+
}, []);
|
|
3595
|
+
return useMemo(()=>({
|
|
3596
|
+
state,
|
|
3597
|
+
dismissError,
|
|
3598
|
+
exportPages,
|
|
3599
|
+
mutate,
|
|
3600
|
+
redo,
|
|
3601
|
+
undo
|
|
3602
|
+
}), [
|
|
3603
|
+
dismissError,
|
|
3604
|
+
exportPages,
|
|
3605
|
+
mutate,
|
|
3606
|
+
redo,
|
|
3607
|
+
state,
|
|
3608
|
+
undo
|
|
3609
|
+
]);
|
|
3610
|
+
}
|
|
3611
|
+
function namedPdfExports(fileName, kind, result) {
|
|
3612
|
+
const stem = fileName.replace(/\.pdf$/i, '') || 'document';
|
|
3613
|
+
return result.files.map((file, index)=>({
|
|
3614
|
+
fileName: 'extract' === kind ? `${stem}-extracted.pdf` : `${stem}-part-${index + 1}.pdf`,
|
|
3615
|
+
pageCount: file.pageCount,
|
|
3616
|
+
pdf: pdfBlob(file.source)
|
|
3617
|
+
}));
|
|
3618
|
+
}
|
|
3619
|
+
function pdfBlob(source) {
|
|
3620
|
+
const copy = new Uint8Array(source.byteLength);
|
|
3621
|
+
copy.set(source);
|
|
3622
|
+
return new Blob([
|
|
3623
|
+
copy.buffer
|
|
3624
|
+
], {
|
|
3625
|
+
type: 'application/pdf'
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
function downloadPdfExports(files) {
|
|
3629
|
+
if ("u" < typeof document || 'function' != typeof URL.createObjectURL) return false;
|
|
3630
|
+
for (const file of files){
|
|
3631
|
+
const url = URL.createObjectURL(file.pdf);
|
|
3632
|
+
const anchor = document.createElement('a');
|
|
3633
|
+
anchor.href = url;
|
|
3634
|
+
anchor.download = file.fileName;
|
|
3635
|
+
anchor.hidden = true;
|
|
3636
|
+
document.body.append(anchor);
|
|
3637
|
+
anchor.click();
|
|
3638
|
+
anchor.remove();
|
|
3639
|
+
queueMicrotask(()=>URL.revokeObjectURL(url));
|
|
3640
|
+
}
|
|
3641
|
+
return true;
|
|
3642
|
+
}
|
|
3643
|
+
function controllerError(error) {
|
|
3644
|
+
const candidate = error;
|
|
3645
|
+
return {
|
|
3646
|
+
code: 'string' == typeof candidate?.code && candidate.code.startsWith('pdf.pages.') ? candidate.code : 'pdf.pages.invalid-source',
|
|
3647
|
+
message: 'string' == typeof candidate?.message && candidate.message ? candidate.message.slice(0, 512) : 'Unable to organize this PDF.'
|
|
3648
|
+
};
|
|
3649
|
+
}
|
|
2743
3650
|
const PDFIUM_WASM_PATH = '/vendor/embedpdf/pdfium.wasm';
|
|
2744
3651
|
const PDF_VIEWER_READY_TIMEOUT_MS = 45000;
|
|
2745
3652
|
const PDF_MOBILE_PAGE_NAVIGATION_QUERY = '(max-width: 640px)';
|
|
@@ -2753,13 +3660,16 @@ const a3sPdfUiSchema = {
|
|
|
2753
3660
|
overlays: {},
|
|
2754
3661
|
selectionMenus: {}
|
|
2755
3662
|
};
|
|
2756
|
-
function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf', loadSource, onCollaborationChange, onEvidenceRegionSelect, onPageChange, onSave, saveLabel = '保存', selectedEvidenceRegionId, sourceKey, wasmUrl, worker = true }) {
|
|
3663
|
+
function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf', loadSource, onCollaborationChange, onEvidenceRegionSelect, onPageChange, onPageExport, onSave, saveLabel = '保存', selectedEvidenceRegionId, sourceKey, wasmUrl, worker = true }) {
|
|
3664
|
+
const [sourceBlob, setSourceBlob] = useState(null);
|
|
2757
3665
|
const [sourceUrl, setSourceUrl] = useState(null);
|
|
2758
3666
|
const [loadError, setLoadError] = useState(null);
|
|
2759
3667
|
const [saveState, setSaveState] = useState('idle');
|
|
2760
3668
|
const [retryCount, setRetryCount] = useState(0);
|
|
2761
3669
|
const [registry, setRegistry] = useState(null);
|
|
2762
3670
|
const [, refreshCollaborationHistory] = useState(0);
|
|
3671
|
+
const [hostSourceGeneration, setHostSourceGeneration] = useState(0);
|
|
3672
|
+
const [pageOrganizerOpen, setPageOrganizerOpen] = useState(false);
|
|
2763
3673
|
const [mobilePageNavigationOpen, setMobilePageNavigationOpen] = useState(false);
|
|
2764
3674
|
const pdfRootRef = useRef(null);
|
|
2765
3675
|
const searchInputRef = useRef(null);
|
|
@@ -2767,6 +3677,8 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2767
3677
|
const mobilePageNavigationToggleRef = useRef(null);
|
|
2768
3678
|
const mobilePageNavigationCloseRef = useRef(null);
|
|
2769
3679
|
const collaborationProjectionRef = useRef(void 0);
|
|
3680
|
+
const editable = collaboration ? 'edit' === collaboration.mode : Boolean(onSave);
|
|
3681
|
+
const pageOrganizationEnabled = Boolean(onSave) && !collaboration && !evidenceOverlay;
|
|
2770
3682
|
const collaborationHistory = collaboration ? {
|
|
2771
3683
|
canRedo: collaborationProjectionRef.current?.binding.canRedo() ?? false,
|
|
2772
3684
|
canUndo: collaborationProjectionRef.current?.binding.canUndo() ?? false,
|
|
@@ -2774,11 +3686,54 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2774
3686
|
undo: ()=>collaborationProjectionRef.current?.binding.undo()
|
|
2775
3687
|
} : void 0;
|
|
2776
3688
|
const controller = usePdfViewerController(registry, collaborationHistory);
|
|
3689
|
+
const replacePageSource = useCallback((source)=>{
|
|
3690
|
+
setRegistry(null);
|
|
3691
|
+
setSourceUrl(null);
|
|
3692
|
+
setSourceBlob('application/pdf' === source.type ? source : new Blob([
|
|
3693
|
+
source
|
|
3694
|
+
], {
|
|
3695
|
+
type: 'application/pdf'
|
|
3696
|
+
}));
|
|
3697
|
+
setLoadError(null);
|
|
3698
|
+
setSaveState('idle');
|
|
3699
|
+
setMobilePageNavigationOpen(false);
|
|
3700
|
+
setPageOrganizerOpen(false);
|
|
3701
|
+
}, []);
|
|
3702
|
+
const pageOrganization = usePdfPageOrganization({
|
|
3703
|
+
enabled: pageOrganizationEnabled,
|
|
3704
|
+
fileName,
|
|
3705
|
+
onExport: onPageExport,
|
|
3706
|
+
readCurrentSource: ()=>controller.saveAsCopy(),
|
|
3707
|
+
replaceSource: replacePageSource,
|
|
3708
|
+
resetKey: `${sourceKey ?? 'source'}:${retryCount}:${hostSourceGeneration}`
|
|
3709
|
+
});
|
|
3710
|
+
const viewerController = useMemo(()=>({
|
|
3711
|
+
...controller,
|
|
3712
|
+
state: {
|
|
3713
|
+
...controller.state,
|
|
3714
|
+
canRedo: controller.state.canRedo || pageOrganization.state.canRedo,
|
|
3715
|
+
canUndo: controller.state.canUndo || pageOrganization.state.canUndo,
|
|
3716
|
+
features: {
|
|
3717
|
+
...controller.state.features,
|
|
3718
|
+
history: controller.state.features.history || pageOrganization.state.available
|
|
3719
|
+
}
|
|
3720
|
+
},
|
|
3721
|
+
redo: ()=>{
|
|
3722
|
+
if (controller.state.canRedo) controller.redo();
|
|
3723
|
+
else pageOrganization.redo();
|
|
3724
|
+
},
|
|
3725
|
+
undo: ()=>{
|
|
3726
|
+
if (controller.state.canUndo) controller.undo();
|
|
3727
|
+
else pageOrganization.undo();
|
|
3728
|
+
}
|
|
3729
|
+
}), [
|
|
3730
|
+
controller,
|
|
3731
|
+
pageOrganization
|
|
3732
|
+
]);
|
|
2777
3733
|
const annotation = usePdfAnnotationController(registry);
|
|
2778
|
-
const viewerReady =
|
|
3734
|
+
const viewerReady = viewerController.state.ready && viewerController.state.documentOpen;
|
|
2779
3735
|
const mobilePageNavigationModal = usePdfMobilePageNavigationModal();
|
|
2780
3736
|
const mobilePageNavigationModalOpen = mobilePageNavigationOpen && mobilePageNavigationModal;
|
|
2781
|
-
const editable = collaboration ? 'edit' === collaboration.mode : Boolean(onSave);
|
|
2782
3737
|
const collaborationRef = useRef(collaboration);
|
|
2783
3738
|
if (collaborationRef.current !== collaboration) throw new Error('PdfViewer collaboration sessions cannot be replaced while mounted. Remount the viewer for another shared PDF.');
|
|
2784
3739
|
const closeMobilePageNavigation = useCallback(()=>{
|
|
@@ -2796,28 +3751,28 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2796
3751
|
});
|
|
2797
3752
|
useEffect(()=>{
|
|
2798
3753
|
let disposed = false;
|
|
2799
|
-
let objectUrl = null;
|
|
2800
3754
|
setRegistry(null);
|
|
2801
3755
|
setSaveState('idle');
|
|
3756
|
+
setSourceBlob(null);
|
|
2802
3757
|
setSourceUrl(null);
|
|
2803
3758
|
setLoadError(null);
|
|
2804
3759
|
setMobilePageNavigationOpen(false);
|
|
3760
|
+
setPageOrganizerOpen(false);
|
|
2805
3761
|
loadSource().then(async (source)=>{
|
|
2806
3762
|
if (disposed) return;
|
|
2807
3763
|
if (collaboration) await assertPdfCollaborationBlob(collaboration, source);
|
|
2808
3764
|
if (disposed) return;
|
|
2809
|
-
|
|
3765
|
+
setSourceBlob('application/pdf' === source.type ? source : new Blob([
|
|
2810
3766
|
source
|
|
2811
3767
|
], {
|
|
2812
3768
|
type: 'application/pdf'
|
|
2813
3769
|
}));
|
|
2814
|
-
|
|
3770
|
+
setHostSourceGeneration((value)=>value + 1);
|
|
2815
3771
|
}).catch((error)=>{
|
|
2816
3772
|
if (!disposed) setLoadError(pdfErrorMessage(error));
|
|
2817
3773
|
});
|
|
2818
3774
|
return ()=>{
|
|
2819
3775
|
disposed = true;
|
|
2820
|
-
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
2821
3776
|
};
|
|
2822
3777
|
}, [
|
|
2823
3778
|
collaboration,
|
|
@@ -2825,6 +3780,14 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2825
3780
|
retryCount,
|
|
2826
3781
|
sourceKey
|
|
2827
3782
|
]);
|
|
3783
|
+
useEffect(()=>{
|
|
3784
|
+
if (!sourceBlob) return void setSourceUrl(null);
|
|
3785
|
+
const objectUrl = URL.createObjectURL(sourceBlob);
|
|
3786
|
+
setSourceUrl(objectUrl);
|
|
3787
|
+
return ()=>URL.revokeObjectURL(objectUrl);
|
|
3788
|
+
}, [
|
|
3789
|
+
sourceBlob
|
|
3790
|
+
]);
|
|
2828
3791
|
useEffect(()=>{
|
|
2829
3792
|
if (controller.state.error) setLoadError(controller.state.error);
|
|
2830
3793
|
}, [
|
|
@@ -2888,10 +3851,10 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2888
3851
|
onCollaborationChange
|
|
2889
3852
|
]);
|
|
2890
3853
|
useEffect(()=>{
|
|
2891
|
-
if (
|
|
3854
|
+
if (viewerController.state.currentPage > 0) onPageChange?.(viewerController.state.currentPage);
|
|
2892
3855
|
}, [
|
|
2893
|
-
|
|
2894
|
-
|
|
3856
|
+
onPageChange,
|
|
3857
|
+
viewerController.state.currentPage
|
|
2895
3858
|
]);
|
|
2896
3859
|
useEffect(()=>{
|
|
2897
3860
|
if (!sourceUrl || viewerReady || loadError) return;
|
|
@@ -2927,17 +3890,19 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2927
3890
|
searchInputRef.current?.focus();
|
|
2928
3891
|
searchInputRef.current?.select();
|
|
2929
3892
|
},
|
|
3893
|
+
openPageOrganizer: ()=>setPageOrganizerOpen(true),
|
|
3894
|
+
pages: pageOrganization,
|
|
2930
3895
|
save: {
|
|
2931
3896
|
enabled: Boolean(onSave) && 'saving' !== saveState,
|
|
2932
3897
|
execute: savePdf
|
|
2933
3898
|
},
|
|
2934
|
-
viewer:
|
|
3899
|
+
viewer: viewerController
|
|
2935
3900
|
}, pdfExtensions);
|
|
2936
3901
|
const pdfCommands = pdfEditor.commands;
|
|
2937
3902
|
const selectedAnnotationLocation = annotation.getSelectionLocation();
|
|
2938
|
-
const pdfPresenceLocation = viewerReady &&
|
|
3903
|
+
const pdfPresenceLocation = viewerReady && viewerController.state.currentPage > 0 ? {
|
|
2939
3904
|
kind: 'pdf',
|
|
2940
|
-
pageIndex: selectedAnnotationLocation?.pageIndex ??
|
|
3905
|
+
pageIndex: selectedAnnotationLocation?.pageIndex ?? viewerController.state.currentPage - 1,
|
|
2941
3906
|
...selectedAnnotationLocation ? {
|
|
2942
3907
|
annotationId: selectedAnnotationLocation.annotationId
|
|
2943
3908
|
} : {}
|
|
@@ -2945,8 +3910,8 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2945
3910
|
useOfficePublishPresenceLocation(pdfPresenceLocation);
|
|
2946
3911
|
const navigateToPdfParticipant = useCallback((participant)=>{
|
|
2947
3912
|
const location = participant.location;
|
|
2948
|
-
if (location?.kind !== 'pdf' || !viewerReady || location.pageIndex >=
|
|
2949
|
-
|
|
3913
|
+
if (location?.kind !== 'pdf' || !viewerReady || location.pageIndex >= viewerController.state.totalPages) return false;
|
|
3914
|
+
viewerController.goToPage(location.pageIndex + 1);
|
|
2950
3915
|
if (location.annotationId) annotation.locateAnnotation(location.pageIndex, location.annotationId);
|
|
2951
3916
|
requestAnimationFrame(()=>{
|
|
2952
3917
|
const target = pdfRootRef.current?.querySelector('.work-pdf-embed');
|
|
@@ -2957,7 +3922,7 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2957
3922
|
return true;
|
|
2958
3923
|
}, [
|
|
2959
3924
|
annotation,
|
|
2960
|
-
|
|
3925
|
+
viewerController,
|
|
2961
3926
|
viewerReady
|
|
2962
3927
|
]);
|
|
2963
3928
|
useOfficeCollaborationLocationNavigator(navigateToPdfParticipant);
|
|
@@ -3006,8 +3971,9 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
3006
3971
|
can: pdfEditor.can(),
|
|
3007
3972
|
commands: pdfCommands,
|
|
3008
3973
|
editable: editable,
|
|
3974
|
+
pageOrganizationAvailable: pageOrganization.state.available,
|
|
3009
3975
|
saveAvailable: editable && Boolean(onSave),
|
|
3010
|
-
pageNavigation: viewerReady && registry &&
|
|
3976
|
+
pageNavigation: viewerReady && registry && viewerController.state.totalPages > 0 ? {
|
|
3011
3977
|
controlsId: mobilePageNavigationId,
|
|
3012
3978
|
expanded: mobilePageNavigationOpen,
|
|
3013
3979
|
onOpen: ()=>setMobilePageNavigationOpen(true),
|
|
@@ -3016,22 +3982,35 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
3016
3982
|
searchInputRef: searchInputRef,
|
|
3017
3983
|
saveLabel: saveLabel,
|
|
3018
3984
|
saveState: saveState,
|
|
3019
|
-
state:
|
|
3985
|
+
state: viewerController.state
|
|
3986
|
+
}),
|
|
3987
|
+
pageOrganizerOpen && viewerReady && registry && viewerController.state.totalPages > 0 && /*#__PURE__*/ jsx(PdfPageOrganizerDialog, {
|
|
3988
|
+
busy: pageOrganization.state.busy,
|
|
3989
|
+
can: pdfEditor.can(),
|
|
3990
|
+
commands: pdfCommands,
|
|
3991
|
+
currentPage: viewerController.state.currentPage,
|
|
3992
|
+
diagnostics: pageOrganization.state.diagnostics,
|
|
3993
|
+
error: pageOrganization.state.error,
|
|
3994
|
+
registry: registry,
|
|
3995
|
+
restoreFocusTarget: ()=>pdfPageOrganizerRestoreFocusTarget(pdfRootRef.current),
|
|
3996
|
+
totalPages: viewerController.state.totalPages,
|
|
3997
|
+
onClose: ()=>setPageOrganizerOpen(false),
|
|
3998
|
+
onDismissError: pageOrganization.dismissError
|
|
3020
3999
|
}),
|
|
3021
4000
|
/*#__PURE__*/ jsxs("div", {
|
|
3022
4001
|
className: "work-pdf-workspace",
|
|
3023
4002
|
"data-mobile-page-navigation": mobilePageNavigationOpen ? 'open' : 'closed',
|
|
3024
4003
|
children: [
|
|
3025
|
-
viewerReady && registry &&
|
|
3026
|
-
currentPage:
|
|
4004
|
+
viewerReady && registry && viewerController.state.totalPages > 0 && /*#__PURE__*/ jsx(PdfThumbnailRail, {
|
|
4005
|
+
currentPage: viewerController.state.currentPage,
|
|
3027
4006
|
mobileCloseButtonRef: mobilePageNavigationCloseRef,
|
|
3028
4007
|
mobileNavigationId: mobilePageNavigationId,
|
|
3029
4008
|
mobileNavigationModal: mobilePageNavigationModalOpen,
|
|
3030
4009
|
registry: registry,
|
|
3031
|
-
totalPages:
|
|
4010
|
+
totalPages: viewerController.state.totalPages,
|
|
3032
4011
|
onCloseMobileNavigation: closeMobilePageNavigation,
|
|
3033
4012
|
onSelectPage: (page)=>{
|
|
3034
|
-
|
|
4013
|
+
viewerController.goToPage(page);
|
|
3035
4014
|
if (mobilePageNavigationModalOpen) closeMobilePageNavigation();
|
|
3036
4015
|
}
|
|
3037
4016
|
}),
|
|
@@ -3109,11 +4088,11 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
3109
4088
|
},
|
|
3110
4089
|
onReady: setRegistry
|
|
3111
4090
|
}, sourceUrl),
|
|
3112
|
-
viewerReady &&
|
|
3113
|
-
pageIndex:
|
|
4091
|
+
viewerReady && viewerController.state.currentPage > 0 && /*#__PURE__*/ jsx(PdfCollaborationPresenceLayer, {
|
|
4092
|
+
pageIndex: viewerController.state.currentPage - 1
|
|
3114
4093
|
}),
|
|
3115
4094
|
evidenceOverlay && registry && viewerReady && /*#__PURE__*/ jsx(PdfEvidenceOverlayLayer, {
|
|
3116
|
-
currentPage:
|
|
4095
|
+
currentPage: viewerController.state.currentPage,
|
|
3117
4096
|
evidenceOverlay: evidenceOverlay,
|
|
3118
4097
|
onEvidenceRegionSelect: onEvidenceRegionSelect,
|
|
3119
4098
|
registry: registry,
|
|
@@ -3142,6 +4121,11 @@ function pdfErrorMessage(error) {
|
|
|
3142
4121
|
if (error instanceof Error && error.message) return error.message;
|
|
3143
4122
|
return 'Unable to read this PDF file.';
|
|
3144
4123
|
}
|
|
4124
|
+
function pdfPageOrganizerRestoreFocusTarget(root) {
|
|
4125
|
+
const directTrigger = root?.querySelector('[data-pdf-page-organizer-trigger]');
|
|
4126
|
+
if (directTrigger && directTrigger.getClientRects().length > 0) return directTrigger;
|
|
4127
|
+
return root?.querySelector('.work-pdf-overflow-trigger') ?? null;
|
|
4128
|
+
}
|
|
3145
4129
|
async function assertPdfCollaborationBlob(session, source) {
|
|
3146
4130
|
const expected = readWorkOfficePdfCollaborationSource(session);
|
|
3147
4131
|
if (source.size !== expected.byteLength) throw new Error(`The loaded PDF source does not match collaboration artifact '${session.artifactId}'.`);
|
|
@@ -3176,4 +4160,4 @@ function pdfMediaQueryMatches(query) {
|
|
|
3176
4160
|
if ("u" < typeof window || 'function' != typeof window.matchMedia) return true;
|
|
3177
4161
|
return window.matchMedia(query).matches;
|
|
3178
4162
|
}
|
|
3179
|
-
export { pdf_viewer_namespaceObject };
|
|
4163
|
+
export { PDF_PAGE_ORGANIZATION_MAX_IMPORT_BYTES, PDF_PAGE_ORGANIZATION_MAX_SOURCE_BYTES, PdfPageOrganizationError, pdf_viewer_namespaceObject, validatedPdfInsertionIndex, validatedPdfPageIndexes, validatedPdfPageOrder, validatedPdfPageSize, validatedPdfSplitBoundaries };
|