@a3s-lab/office 0.32.0 → 0.34.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 +62 -8
- package/dist/0~3539.js +2 -2
- package/dist/0~3557.js +1 -1
- package/dist/0~3635.js +1 -1
- package/dist/0~5809.js +123 -0
- package/dist/0~7048.js +1 -2
- package/dist/{0~7614.js → 0~7828.js} +376 -5
- 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 +1018 -30
- package/dist/0~presentation-editor.js +580 -68
- package/dist/0~spreadsheet-editor.js +2 -2
- package/dist/0~work-pptx-export.js +50 -14
- package/dist/0~work-pptx-import.js +21 -9
- package/dist/4121.js +122 -1
- package/dist/4560.js +33 -0
- 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/presentation-animation-panel.d.ts +11 -0
- package/dist/internal/features/work/editors/presentation-command-types.d.ts +16 -1
- package/dist/internal/features/work/editors/presentation-presenter-view.d.ts +2 -1
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +11 -3
- package/dist/internal/features/work/editors/use-pdf-page-organization.d.ts +38 -0
- package/dist/internal/features/work/editors/use-presentation-animation-commands.d.ts +15 -0
- package/dist/internal/features/work/work-pptx-animation.d.ts +13 -0
- package/dist/internal/features/work/work-pptx-groups.d.ts +4 -0
- package/dist/internal/features/work/work-presentation-animation-constraints.d.ts +4 -0
- package/dist/internal/features/work/work-presentation-animation.d.ts +20 -0
- package/dist/internal/features/work/work-presentation-clipboard.d.ts +10 -3
- package/dist/internal/features/work/work-types.d.ts +17 -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 +438 -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 +8 -1
- package/dist/0~work-presentation-transition.js +0 -38
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,27 @@ 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
|
+
title: "组织 PDF 页面",
|
|
2199
|
+
disabled: !can.openPageOrganizer(),
|
|
2200
|
+
onClick: commands.openPageOrganizer,
|
|
2201
|
+
children: [
|
|
2202
|
+
/*#__PURE__*/ jsx(LayoutGrid, {
|
|
2203
|
+
size: 15
|
|
2204
|
+
}),
|
|
2205
|
+
/*#__PURE__*/ jsx("span", {
|
|
2206
|
+
className: "work-pdf-page-organizer-label",
|
|
2207
|
+
children: "组织页面"
|
|
2208
|
+
})
|
|
2209
|
+
]
|
|
2210
|
+
})
|
|
2211
|
+
}),
|
|
1665
2212
|
editable && /*#__PURE__*/ jsxs("fieldset", {
|
|
1666
2213
|
className: "work-pdf-toolbar-group work-pdf-annotation",
|
|
1667
2214
|
children: [
|
|
@@ -1759,6 +2306,7 @@ function PdfToolbar({ annotationState, can, commands, editable, pageNavigation,
|
|
|
1759
2306
|
can: can,
|
|
1760
2307
|
commands: commands,
|
|
1761
2308
|
editable: editable,
|
|
2309
|
+
pageOrganizationAvailable: showPageOrganization,
|
|
1762
2310
|
state: state
|
|
1763
2311
|
}),
|
|
1764
2312
|
/*#__PURE__*/ jsx("search", {
|
|
@@ -2104,7 +2652,7 @@ function movePdfAnnotationStyleOption(event) {
|
|
|
2104
2652
|
});
|
|
2105
2653
|
next?.click();
|
|
2106
2654
|
}
|
|
2107
|
-
function PdfToolbarOverflow({ annotationState, can, commands, editable, state }) {
|
|
2655
|
+
function PdfToolbarOverflow({ annotationState, can, commands, editable, pageOrganizationAvailable, state }) {
|
|
2108
2656
|
const hasOverflowTools = editable && annotationState.available || editable && state.features.history || state.features.navigation || state.features.search || state.features.zoom;
|
|
2109
2657
|
return /*#__PURE__*/ jsx(Popover, {
|
|
2110
2658
|
label: "更多 PDF 工具",
|
|
@@ -2254,6 +2802,18 @@ function PdfToolbarOverflow({ annotationState, can, commands, editable, state })
|
|
|
2254
2802
|
})
|
|
2255
2803
|
]
|
|
2256
2804
|
}),
|
|
2805
|
+
pageOrganizationAvailable && /*#__PURE__*/ jsx("fieldset", {
|
|
2806
|
+
className: "work-pdf-overflow-group",
|
|
2807
|
+
"aria-label": "页面组织",
|
|
2808
|
+
children: /*#__PURE__*/ jsx(PdfOverflowAction, {
|
|
2809
|
+
label: "组织页面",
|
|
2810
|
+
disabled: !can.openPageOrganizer(),
|
|
2811
|
+
onSelect: ()=>select(commands.openPageOrganizer),
|
|
2812
|
+
children: /*#__PURE__*/ jsx(LayoutGrid, {
|
|
2813
|
+
size: 15
|
|
2814
|
+
})
|
|
2815
|
+
})
|
|
2816
|
+
}),
|
|
2257
2817
|
state.features.navigation && /*#__PURE__*/ jsxs("fieldset", {
|
|
2258
2818
|
className: "work-pdf-overflow-group",
|
|
2259
2819
|
"aria-label": "翻页",
|
|
@@ -2740,6 +3300,357 @@ function pdf_viewer_controller_safely(read) {
|
|
|
2740
3300
|
function errorMessage(error) {
|
|
2741
3301
|
return error instanceof Error && error.message ? error.message : 'PDF viewer initialization failed.';
|
|
2742
3302
|
}
|
|
3303
|
+
const PDF_PAGE_ORGANIZATION_WORKER_TIMEOUT_MS = 120000;
|
|
3304
|
+
async function runPdfPageOrganizationJob(job, signal) {
|
|
3305
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3306
|
+
if ("u" < typeof Worker) return runDirectly(job, signal);
|
|
3307
|
+
let worker;
|
|
3308
|
+
try {
|
|
3309
|
+
worker = new Worker(new URL('./pdf-page-organization.worker.js', import.meta.url), {
|
|
3310
|
+
name: 'a3s-office-pdf-page-organization'
|
|
3311
|
+
});
|
|
3312
|
+
} catch {
|
|
3313
|
+
return runDirectly(job, signal);
|
|
3314
|
+
}
|
|
3315
|
+
return new Promise((resolve, reject)=>{
|
|
3316
|
+
let settled = false;
|
|
3317
|
+
const timeout = setTimeout(()=>{
|
|
3318
|
+
finish(void 0, new PdfPageOrganizationError('pdf.pages.invalid-source', 'PDF page organization timed out.'));
|
|
3319
|
+
}, PDF_PAGE_ORGANIZATION_WORKER_TIMEOUT_MS);
|
|
3320
|
+
const finish = (result, error)=>{
|
|
3321
|
+
if (settled) return;
|
|
3322
|
+
settled = true;
|
|
3323
|
+
clearTimeout(timeout);
|
|
3324
|
+
signal?.removeEventListener('abort', handleAbort);
|
|
3325
|
+
worker.removeEventListener('error', handleWorkerError);
|
|
3326
|
+
worker.removeEventListener('message', handleMessage);
|
|
3327
|
+
worker.removeEventListener('messageerror', handleWorkerError);
|
|
3328
|
+
worker.terminate();
|
|
3329
|
+
if (error) reject(error);
|
|
3330
|
+
else if (result) resolve(result);
|
|
3331
|
+
else reject(new Error('PDF page worker returned no result.'));
|
|
3332
|
+
};
|
|
3333
|
+
const handleAbort = ()=>finish(void 0, pdfPageOrganizationAbortError());
|
|
3334
|
+
const handleWorkerError = ()=>finish(void 0, new PdfPageOrganizationError('pdf.pages.invalid-source', 'The PDF page worker failed before producing a result.'));
|
|
3335
|
+
const handleMessage = (event)=>{
|
|
3336
|
+
const response = event.data;
|
|
3337
|
+
if (!response || 'object' != typeof response) return void handleWorkerError();
|
|
3338
|
+
if ('failure' === response.kind) return void finish(void 0, new PdfPageOrganizationError(response.code, response.message));
|
|
3339
|
+
if ('success' === response.kind) finish(response.result);
|
|
3340
|
+
else handleWorkerError();
|
|
3341
|
+
};
|
|
3342
|
+
signal?.addEventListener('abort', handleAbort, {
|
|
3343
|
+
once: true
|
|
3344
|
+
});
|
|
3345
|
+
worker.addEventListener('error', handleWorkerError, {
|
|
3346
|
+
once: true
|
|
3347
|
+
});
|
|
3348
|
+
worker.addEventListener('message', handleMessage);
|
|
3349
|
+
worker.addEventListener('messageerror', handleWorkerError, {
|
|
3350
|
+
once: true
|
|
3351
|
+
});
|
|
3352
|
+
if (signal?.aborted) return void handleAbort();
|
|
3353
|
+
const transferableJob = transferablePdfPageJob(job);
|
|
3354
|
+
const request = {
|
|
3355
|
+
job: transferableJob.job,
|
|
3356
|
+
kind: 'run'
|
|
3357
|
+
};
|
|
3358
|
+
try {
|
|
3359
|
+
worker.postMessage(request, {
|
|
3360
|
+
transfer: transferableJob.transfer
|
|
3361
|
+
});
|
|
3362
|
+
} catch {
|
|
3363
|
+
handleWorkerError();
|
|
3364
|
+
}
|
|
3365
|
+
});
|
|
3366
|
+
}
|
|
3367
|
+
async function runDirectly(job, signal) {
|
|
3368
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3369
|
+
const { applyPdfPageOrganizationJob } = await import("./0~pdf-page-organization-engine.js");
|
|
3370
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3371
|
+
const result = await applyPdfPageOrganizationJob(job);
|
|
3372
|
+
if (signal?.aborted) throw pdfPageOrganizationAbortError();
|
|
3373
|
+
return result;
|
|
3374
|
+
}
|
|
3375
|
+
function transferablePdfPageJob(job) {
|
|
3376
|
+
const source = transferableSource(job.source);
|
|
3377
|
+
if ('export' === job.kind) return {
|
|
3378
|
+
job: {
|
|
3379
|
+
...job,
|
|
3380
|
+
source: source.source
|
|
3381
|
+
},
|
|
3382
|
+
transfer: [
|
|
3383
|
+
source.source
|
|
3384
|
+
]
|
|
3385
|
+
};
|
|
3386
|
+
const imported = job.importSource ? transferableSource(job.importSource) : void 0;
|
|
3387
|
+
return {
|
|
3388
|
+
job: {
|
|
3389
|
+
...job,
|
|
3390
|
+
...imported ? {
|
|
3391
|
+
importSource: imported.source
|
|
3392
|
+
} : {},
|
|
3393
|
+
source: source.source
|
|
3394
|
+
},
|
|
3395
|
+
transfer: imported ? [
|
|
3396
|
+
source.source,
|
|
3397
|
+
imported.source
|
|
3398
|
+
] : [
|
|
3399
|
+
source.source
|
|
3400
|
+
]
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
function transferableSource(source) {
|
|
3404
|
+
if (source instanceof ArrayBuffer) return {
|
|
3405
|
+
source
|
|
3406
|
+
};
|
|
3407
|
+
const copy = new Uint8Array(source.byteLength);
|
|
3408
|
+
copy.set(source);
|
|
3409
|
+
return {
|
|
3410
|
+
source: copy.buffer
|
|
3411
|
+
};
|
|
3412
|
+
}
|
|
3413
|
+
function pdfPageOrganizationAbortError() {
|
|
3414
|
+
if ("u" > typeof DOMException) return new DOMException('PDF page organization was cancelled.', 'AbortError');
|
|
3415
|
+
const error = new Error('PDF page organization was cancelled.');
|
|
3416
|
+
error.name = 'AbortError';
|
|
3417
|
+
return error;
|
|
3418
|
+
}
|
|
3419
|
+
const initialState = {
|
|
3420
|
+
available: false,
|
|
3421
|
+
busy: false,
|
|
3422
|
+
canRedo: false,
|
|
3423
|
+
canUndo: false,
|
|
3424
|
+
diagnostics: [],
|
|
3425
|
+
error: null,
|
|
3426
|
+
revision: 0
|
|
3427
|
+
};
|
|
3428
|
+
function usePdfPageOrganization(options, runJob = runPdfPageOrganizationJob) {
|
|
3429
|
+
const [state, setState] = useState({
|
|
3430
|
+
...initialState,
|
|
3431
|
+
available: options.enabled
|
|
3432
|
+
});
|
|
3433
|
+
const undoRef = useRef([]);
|
|
3434
|
+
const redoRef = useRef([]);
|
|
3435
|
+
const abortRef = useRef(null);
|
|
3436
|
+
const optionsRef = useRef(options);
|
|
3437
|
+
const runJobRef = useRef(runJob);
|
|
3438
|
+
optionsRef.current = options;
|
|
3439
|
+
runJobRef.current = runJob;
|
|
3440
|
+
useEffect(()=>{
|
|
3441
|
+
abortRef.current?.abort();
|
|
3442
|
+
abortRef.current = null;
|
|
3443
|
+
undoRef.current = [];
|
|
3444
|
+
redoRef.current = [];
|
|
3445
|
+
setState({
|
|
3446
|
+
...initialState,
|
|
3447
|
+
available: options.enabled
|
|
3448
|
+
});
|
|
3449
|
+
}, [
|
|
3450
|
+
options.enabled,
|
|
3451
|
+
options.resetKey
|
|
3452
|
+
]);
|
|
3453
|
+
useEffect(()=>()=>{
|
|
3454
|
+
abortRef.current?.abort();
|
|
3455
|
+
}, []);
|
|
3456
|
+
const mutate = useCallback(async (mutation, importSource)=>{
|
|
3457
|
+
const currentOptions = optionsRef.current;
|
|
3458
|
+
if (!currentOptions.enabled || abortRef.current) return false;
|
|
3459
|
+
const abort = new AbortController();
|
|
3460
|
+
abortRef.current = abort;
|
|
3461
|
+
setState((current)=>({
|
|
3462
|
+
...current,
|
|
3463
|
+
busy: true,
|
|
3464
|
+
diagnostics: [],
|
|
3465
|
+
error: null
|
|
3466
|
+
}));
|
|
3467
|
+
try {
|
|
3468
|
+
const before = await currentOptions.readCurrentSource();
|
|
3469
|
+
const job = {
|
|
3470
|
+
...importSource ? {
|
|
3471
|
+
importSource: await importSource.arrayBuffer()
|
|
3472
|
+
} : {},
|
|
3473
|
+
kind: 'mutate',
|
|
3474
|
+
mutation,
|
|
3475
|
+
source: await before.arrayBuffer()
|
|
3476
|
+
};
|
|
3477
|
+
const result = await runJobRef.current(job, abort.signal);
|
|
3478
|
+
if ('mutated' !== result.kind) throw new Error('PDF page mutation returned export files.');
|
|
3479
|
+
const after = pdfBlob(result.source);
|
|
3480
|
+
const entry = {
|
|
3481
|
+
after,
|
|
3482
|
+
before
|
|
3483
|
+
};
|
|
3484
|
+
undoRef.current = [
|
|
3485
|
+
...undoRef.current,
|
|
3486
|
+
entry
|
|
3487
|
+
];
|
|
3488
|
+
redoRef.current = [];
|
|
3489
|
+
currentOptions.replaceSource(after);
|
|
3490
|
+
setState((current)=>({
|
|
3491
|
+
...current,
|
|
3492
|
+
busy: false,
|
|
3493
|
+
canRedo: false,
|
|
3494
|
+
canUndo: true,
|
|
3495
|
+
diagnostics: result.diagnostics,
|
|
3496
|
+
error: null,
|
|
3497
|
+
revision: current.revision + 1
|
|
3498
|
+
}));
|
|
3499
|
+
return true;
|
|
3500
|
+
} catch (error) {
|
|
3501
|
+
if (abort.signal.aborted) return false;
|
|
3502
|
+
setState((current)=>({
|
|
3503
|
+
...current,
|
|
3504
|
+
busy: false,
|
|
3505
|
+
diagnostics: [],
|
|
3506
|
+
error: controllerError(error)
|
|
3507
|
+
}));
|
|
3508
|
+
return false;
|
|
3509
|
+
} finally{
|
|
3510
|
+
if (abortRef.current === abort) abortRef.current = null;
|
|
3511
|
+
}
|
|
3512
|
+
}, []);
|
|
3513
|
+
const exportPages = useCallback(async (operation)=>{
|
|
3514
|
+
const currentOptions = optionsRef.current;
|
|
3515
|
+
if (!currentOptions.enabled || abortRef.current) return false;
|
|
3516
|
+
const abort = new AbortController();
|
|
3517
|
+
abortRef.current = abort;
|
|
3518
|
+
setState((current)=>({
|
|
3519
|
+
...current,
|
|
3520
|
+
busy: true,
|
|
3521
|
+
diagnostics: [],
|
|
3522
|
+
error: null
|
|
3523
|
+
}));
|
|
3524
|
+
try {
|
|
3525
|
+
const before = await currentOptions.readCurrentSource();
|
|
3526
|
+
const result = await runJobRef.current({
|
|
3527
|
+
kind: 'export',
|
|
3528
|
+
operation,
|
|
3529
|
+
source: await before.arrayBuffer()
|
|
3530
|
+
}, abort.signal);
|
|
3531
|
+
if ('exported' !== result.kind) throw new Error('PDF page export returned a mutated source.');
|
|
3532
|
+
const files = namedPdfExports(currentOptions.fileName, operation.kind, result);
|
|
3533
|
+
const accepted = currentOptions.onExport ? await currentOptions.onExport(files) : downloadPdfExports(files);
|
|
3534
|
+
if (!accepted) throw new Error('The host rejected the PDF page export.');
|
|
3535
|
+
setState((current)=>({
|
|
3536
|
+
...current,
|
|
3537
|
+
busy: false,
|
|
3538
|
+
diagnostics: result.diagnostics,
|
|
3539
|
+
error: null
|
|
3540
|
+
}));
|
|
3541
|
+
return true;
|
|
3542
|
+
} catch (error) {
|
|
3543
|
+
if (abort.signal.aborted) return false;
|
|
3544
|
+
setState((current)=>({
|
|
3545
|
+
...current,
|
|
3546
|
+
busy: false,
|
|
3547
|
+
diagnostics: [],
|
|
3548
|
+
error: controllerError(error)
|
|
3549
|
+
}));
|
|
3550
|
+
return false;
|
|
3551
|
+
} finally{
|
|
3552
|
+
if (abortRef.current === abort) abortRef.current = null;
|
|
3553
|
+
}
|
|
3554
|
+
}, []);
|
|
3555
|
+
const undo = useCallback(()=>{
|
|
3556
|
+
if (!optionsRef.current.enabled || abortRef.current) return;
|
|
3557
|
+
const entry = undoRef.current.at(-1);
|
|
3558
|
+
if (!entry) return;
|
|
3559
|
+
undoRef.current = undoRef.current.slice(0, -1);
|
|
3560
|
+
redoRef.current = [
|
|
3561
|
+
...redoRef.current,
|
|
3562
|
+
entry
|
|
3563
|
+
];
|
|
3564
|
+
optionsRef.current.replaceSource(entry.before);
|
|
3565
|
+
setState((current)=>({
|
|
3566
|
+
...current,
|
|
3567
|
+
canRedo: true,
|
|
3568
|
+
canUndo: undoRef.current.length > 0,
|
|
3569
|
+
diagnostics: [],
|
|
3570
|
+
error: null,
|
|
3571
|
+
revision: current.revision + 1
|
|
3572
|
+
}));
|
|
3573
|
+
}, []);
|
|
3574
|
+
const redo = useCallback(()=>{
|
|
3575
|
+
if (!optionsRef.current.enabled || abortRef.current) return;
|
|
3576
|
+
const entry = redoRef.current.at(-1);
|
|
3577
|
+
if (!entry) return;
|
|
3578
|
+
redoRef.current = redoRef.current.slice(0, -1);
|
|
3579
|
+
undoRef.current = [
|
|
3580
|
+
...undoRef.current,
|
|
3581
|
+
entry
|
|
3582
|
+
];
|
|
3583
|
+
optionsRef.current.replaceSource(entry.after);
|
|
3584
|
+
setState((current)=>({
|
|
3585
|
+
...current,
|
|
3586
|
+
canRedo: redoRef.current.length > 0,
|
|
3587
|
+
canUndo: true,
|
|
3588
|
+
diagnostics: [],
|
|
3589
|
+
error: null,
|
|
3590
|
+
revision: current.revision + 1
|
|
3591
|
+
}));
|
|
3592
|
+
}, []);
|
|
3593
|
+
const dismissError = useCallback(()=>{
|
|
3594
|
+
setState((current)=>({
|
|
3595
|
+
...current,
|
|
3596
|
+
error: null
|
|
3597
|
+
}));
|
|
3598
|
+
}, []);
|
|
3599
|
+
return useMemo(()=>({
|
|
3600
|
+
state,
|
|
3601
|
+
dismissError,
|
|
3602
|
+
exportPages,
|
|
3603
|
+
mutate,
|
|
3604
|
+
redo,
|
|
3605
|
+
undo
|
|
3606
|
+
}), [
|
|
3607
|
+
dismissError,
|
|
3608
|
+
exportPages,
|
|
3609
|
+
mutate,
|
|
3610
|
+
redo,
|
|
3611
|
+
state,
|
|
3612
|
+
undo
|
|
3613
|
+
]);
|
|
3614
|
+
}
|
|
3615
|
+
function namedPdfExports(fileName, kind, result) {
|
|
3616
|
+
const stem = fileName.replace(/\.pdf$/i, '') || 'document';
|
|
3617
|
+
return result.files.map((file, index)=>({
|
|
3618
|
+
fileName: 'extract' === kind ? `${stem}-extracted.pdf` : `${stem}-part-${index + 1}.pdf`,
|
|
3619
|
+
pageCount: file.pageCount,
|
|
3620
|
+
pdf: pdfBlob(file.source)
|
|
3621
|
+
}));
|
|
3622
|
+
}
|
|
3623
|
+
function pdfBlob(source) {
|
|
3624
|
+
const copy = new Uint8Array(source.byteLength);
|
|
3625
|
+
copy.set(source);
|
|
3626
|
+
return new Blob([
|
|
3627
|
+
copy.buffer
|
|
3628
|
+
], {
|
|
3629
|
+
type: 'application/pdf'
|
|
3630
|
+
});
|
|
3631
|
+
}
|
|
3632
|
+
function downloadPdfExports(files) {
|
|
3633
|
+
if ("u" < typeof document || 'function' != typeof URL.createObjectURL) return false;
|
|
3634
|
+
for (const file of files){
|
|
3635
|
+
const url = URL.createObjectURL(file.pdf);
|
|
3636
|
+
const anchor = document.createElement('a');
|
|
3637
|
+
anchor.href = url;
|
|
3638
|
+
anchor.download = file.fileName;
|
|
3639
|
+
anchor.hidden = true;
|
|
3640
|
+
document.body.append(anchor);
|
|
3641
|
+
anchor.click();
|
|
3642
|
+
anchor.remove();
|
|
3643
|
+
queueMicrotask(()=>URL.revokeObjectURL(url));
|
|
3644
|
+
}
|
|
3645
|
+
return true;
|
|
3646
|
+
}
|
|
3647
|
+
function controllerError(error) {
|
|
3648
|
+
const candidate = error;
|
|
3649
|
+
return {
|
|
3650
|
+
code: 'string' == typeof candidate?.code && candidate.code.startsWith('pdf.pages.') ? candidate.code : 'pdf.pages.invalid-source',
|
|
3651
|
+
message: 'string' == typeof candidate?.message && candidate.message ? candidate.message.slice(0, 512) : 'Unable to organize this PDF.'
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
2743
3654
|
const PDFIUM_WASM_PATH = '/vendor/embedpdf/pdfium.wasm';
|
|
2744
3655
|
const PDF_VIEWER_READY_TIMEOUT_MS = 45000;
|
|
2745
3656
|
const PDF_MOBILE_PAGE_NAVIGATION_QUERY = '(max-width: 640px)';
|
|
@@ -2753,13 +3664,16 @@ const a3sPdfUiSchema = {
|
|
|
2753
3664
|
overlays: {},
|
|
2754
3665
|
selectionMenus: {}
|
|
2755
3666
|
};
|
|
2756
|
-
function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf', loadSource, onCollaborationChange, onEvidenceRegionSelect, onPageChange, onSave, saveLabel = '保存', selectedEvidenceRegionId, sourceKey, wasmUrl, worker = true }) {
|
|
3667
|
+
function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf', loadSource, onCollaborationChange, onEvidenceRegionSelect, onPageChange, onPageExport, onSave, saveLabel = '保存', selectedEvidenceRegionId, sourceKey, wasmUrl, worker = true }) {
|
|
3668
|
+
const [sourceBlob, setSourceBlob] = useState(null);
|
|
2757
3669
|
const [sourceUrl, setSourceUrl] = useState(null);
|
|
2758
3670
|
const [loadError, setLoadError] = useState(null);
|
|
2759
3671
|
const [saveState, setSaveState] = useState('idle');
|
|
2760
3672
|
const [retryCount, setRetryCount] = useState(0);
|
|
2761
3673
|
const [registry, setRegistry] = useState(null);
|
|
2762
3674
|
const [, refreshCollaborationHistory] = useState(0);
|
|
3675
|
+
const [hostSourceGeneration, setHostSourceGeneration] = useState(0);
|
|
3676
|
+
const [pageOrganizerOpen, setPageOrganizerOpen] = useState(false);
|
|
2763
3677
|
const [mobilePageNavigationOpen, setMobilePageNavigationOpen] = useState(false);
|
|
2764
3678
|
const pdfRootRef = useRef(null);
|
|
2765
3679
|
const searchInputRef = useRef(null);
|
|
@@ -2767,6 +3681,8 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2767
3681
|
const mobilePageNavigationToggleRef = useRef(null);
|
|
2768
3682
|
const mobilePageNavigationCloseRef = useRef(null);
|
|
2769
3683
|
const collaborationProjectionRef = useRef(void 0);
|
|
3684
|
+
const editable = collaboration ? 'edit' === collaboration.mode : Boolean(onSave);
|
|
3685
|
+
const pageOrganizationEnabled = Boolean(onSave) && !collaboration && !evidenceOverlay;
|
|
2770
3686
|
const collaborationHistory = collaboration ? {
|
|
2771
3687
|
canRedo: collaborationProjectionRef.current?.binding.canRedo() ?? false,
|
|
2772
3688
|
canUndo: collaborationProjectionRef.current?.binding.canUndo() ?? false,
|
|
@@ -2774,11 +3690,54 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2774
3690
|
undo: ()=>collaborationProjectionRef.current?.binding.undo()
|
|
2775
3691
|
} : void 0;
|
|
2776
3692
|
const controller = usePdfViewerController(registry, collaborationHistory);
|
|
3693
|
+
const replacePageSource = useCallback((source)=>{
|
|
3694
|
+
setRegistry(null);
|
|
3695
|
+
setSourceUrl(null);
|
|
3696
|
+
setSourceBlob('application/pdf' === source.type ? source : new Blob([
|
|
3697
|
+
source
|
|
3698
|
+
], {
|
|
3699
|
+
type: 'application/pdf'
|
|
3700
|
+
}));
|
|
3701
|
+
setLoadError(null);
|
|
3702
|
+
setSaveState('idle');
|
|
3703
|
+
setMobilePageNavigationOpen(false);
|
|
3704
|
+
setPageOrganizerOpen(false);
|
|
3705
|
+
}, []);
|
|
3706
|
+
const pageOrganization = usePdfPageOrganization({
|
|
3707
|
+
enabled: pageOrganizationEnabled,
|
|
3708
|
+
fileName,
|
|
3709
|
+
onExport: onPageExport,
|
|
3710
|
+
readCurrentSource: ()=>controller.saveAsCopy(),
|
|
3711
|
+
replaceSource: replacePageSource,
|
|
3712
|
+
resetKey: `${sourceKey ?? 'source'}:${retryCount}:${hostSourceGeneration}`
|
|
3713
|
+
});
|
|
3714
|
+
const viewerController = useMemo(()=>({
|
|
3715
|
+
...controller,
|
|
3716
|
+
state: {
|
|
3717
|
+
...controller.state,
|
|
3718
|
+
canRedo: controller.state.canRedo || pageOrganization.state.canRedo,
|
|
3719
|
+
canUndo: controller.state.canUndo || pageOrganization.state.canUndo,
|
|
3720
|
+
features: {
|
|
3721
|
+
...controller.state.features,
|
|
3722
|
+
history: controller.state.features.history || pageOrganization.state.available
|
|
3723
|
+
}
|
|
3724
|
+
},
|
|
3725
|
+
redo: ()=>{
|
|
3726
|
+
if (controller.state.canRedo) controller.redo();
|
|
3727
|
+
else pageOrganization.redo();
|
|
3728
|
+
},
|
|
3729
|
+
undo: ()=>{
|
|
3730
|
+
if (controller.state.canUndo) controller.undo();
|
|
3731
|
+
else pageOrganization.undo();
|
|
3732
|
+
}
|
|
3733
|
+
}), [
|
|
3734
|
+
controller,
|
|
3735
|
+
pageOrganization
|
|
3736
|
+
]);
|
|
2777
3737
|
const annotation = usePdfAnnotationController(registry);
|
|
2778
|
-
const viewerReady =
|
|
3738
|
+
const viewerReady = viewerController.state.ready && viewerController.state.documentOpen;
|
|
2779
3739
|
const mobilePageNavigationModal = usePdfMobilePageNavigationModal();
|
|
2780
3740
|
const mobilePageNavigationModalOpen = mobilePageNavigationOpen && mobilePageNavigationModal;
|
|
2781
|
-
const editable = collaboration ? 'edit' === collaboration.mode : Boolean(onSave);
|
|
2782
3741
|
const collaborationRef = useRef(collaboration);
|
|
2783
3742
|
if (collaborationRef.current !== collaboration) throw new Error('PdfViewer collaboration sessions cannot be replaced while mounted. Remount the viewer for another shared PDF.');
|
|
2784
3743
|
const closeMobilePageNavigation = useCallback(()=>{
|
|
@@ -2796,28 +3755,28 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2796
3755
|
});
|
|
2797
3756
|
useEffect(()=>{
|
|
2798
3757
|
let disposed = false;
|
|
2799
|
-
let objectUrl = null;
|
|
2800
3758
|
setRegistry(null);
|
|
2801
3759
|
setSaveState('idle');
|
|
3760
|
+
setSourceBlob(null);
|
|
2802
3761
|
setSourceUrl(null);
|
|
2803
3762
|
setLoadError(null);
|
|
2804
3763
|
setMobilePageNavigationOpen(false);
|
|
3764
|
+
setPageOrganizerOpen(false);
|
|
2805
3765
|
loadSource().then(async (source)=>{
|
|
2806
3766
|
if (disposed) return;
|
|
2807
3767
|
if (collaboration) await assertPdfCollaborationBlob(collaboration, source);
|
|
2808
3768
|
if (disposed) return;
|
|
2809
|
-
|
|
3769
|
+
setSourceBlob('application/pdf' === source.type ? source : new Blob([
|
|
2810
3770
|
source
|
|
2811
3771
|
], {
|
|
2812
3772
|
type: 'application/pdf'
|
|
2813
3773
|
}));
|
|
2814
|
-
|
|
3774
|
+
setHostSourceGeneration((value)=>value + 1);
|
|
2815
3775
|
}).catch((error)=>{
|
|
2816
3776
|
if (!disposed) setLoadError(pdfErrorMessage(error));
|
|
2817
3777
|
});
|
|
2818
3778
|
return ()=>{
|
|
2819
3779
|
disposed = true;
|
|
2820
|
-
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
2821
3780
|
};
|
|
2822
3781
|
}, [
|
|
2823
3782
|
collaboration,
|
|
@@ -2825,6 +3784,14 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2825
3784
|
retryCount,
|
|
2826
3785
|
sourceKey
|
|
2827
3786
|
]);
|
|
3787
|
+
useEffect(()=>{
|
|
3788
|
+
if (!sourceBlob) return void setSourceUrl(null);
|
|
3789
|
+
const objectUrl = URL.createObjectURL(sourceBlob);
|
|
3790
|
+
setSourceUrl(objectUrl);
|
|
3791
|
+
return ()=>URL.revokeObjectURL(objectUrl);
|
|
3792
|
+
}, [
|
|
3793
|
+
sourceBlob
|
|
3794
|
+
]);
|
|
2828
3795
|
useEffect(()=>{
|
|
2829
3796
|
if (controller.state.error) setLoadError(controller.state.error);
|
|
2830
3797
|
}, [
|
|
@@ -2888,10 +3855,10 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2888
3855
|
onCollaborationChange
|
|
2889
3856
|
]);
|
|
2890
3857
|
useEffect(()=>{
|
|
2891
|
-
if (
|
|
3858
|
+
if (viewerController.state.currentPage > 0) onPageChange?.(viewerController.state.currentPage);
|
|
2892
3859
|
}, [
|
|
2893
|
-
|
|
2894
|
-
|
|
3860
|
+
onPageChange,
|
|
3861
|
+
viewerController.state.currentPage
|
|
2895
3862
|
]);
|
|
2896
3863
|
useEffect(()=>{
|
|
2897
3864
|
if (!sourceUrl || viewerReady || loadError) return;
|
|
@@ -2927,17 +3894,19 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2927
3894
|
searchInputRef.current?.focus();
|
|
2928
3895
|
searchInputRef.current?.select();
|
|
2929
3896
|
},
|
|
3897
|
+
openPageOrganizer: ()=>setPageOrganizerOpen(true),
|
|
3898
|
+
pages: pageOrganization,
|
|
2930
3899
|
save: {
|
|
2931
3900
|
enabled: Boolean(onSave) && 'saving' !== saveState,
|
|
2932
3901
|
execute: savePdf
|
|
2933
3902
|
},
|
|
2934
|
-
viewer:
|
|
3903
|
+
viewer: viewerController
|
|
2935
3904
|
}, pdfExtensions);
|
|
2936
3905
|
const pdfCommands = pdfEditor.commands;
|
|
2937
3906
|
const selectedAnnotationLocation = annotation.getSelectionLocation();
|
|
2938
|
-
const pdfPresenceLocation = viewerReady &&
|
|
3907
|
+
const pdfPresenceLocation = viewerReady && viewerController.state.currentPage > 0 ? {
|
|
2939
3908
|
kind: 'pdf',
|
|
2940
|
-
pageIndex: selectedAnnotationLocation?.pageIndex ??
|
|
3909
|
+
pageIndex: selectedAnnotationLocation?.pageIndex ?? viewerController.state.currentPage - 1,
|
|
2941
3910
|
...selectedAnnotationLocation ? {
|
|
2942
3911
|
annotationId: selectedAnnotationLocation.annotationId
|
|
2943
3912
|
} : {}
|
|
@@ -2945,8 +3914,8 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2945
3914
|
useOfficePublishPresenceLocation(pdfPresenceLocation);
|
|
2946
3915
|
const navigateToPdfParticipant = useCallback((participant)=>{
|
|
2947
3916
|
const location = participant.location;
|
|
2948
|
-
if (location?.kind !== 'pdf' || !viewerReady || location.pageIndex >=
|
|
2949
|
-
|
|
3917
|
+
if (location?.kind !== 'pdf' || !viewerReady || location.pageIndex >= viewerController.state.totalPages) return false;
|
|
3918
|
+
viewerController.goToPage(location.pageIndex + 1);
|
|
2950
3919
|
if (location.annotationId) annotation.locateAnnotation(location.pageIndex, location.annotationId);
|
|
2951
3920
|
requestAnimationFrame(()=>{
|
|
2952
3921
|
const target = pdfRootRef.current?.querySelector('.work-pdf-embed');
|
|
@@ -2957,7 +3926,7 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
2957
3926
|
return true;
|
|
2958
3927
|
}, [
|
|
2959
3928
|
annotation,
|
|
2960
|
-
|
|
3929
|
+
viewerController,
|
|
2961
3930
|
viewerReady
|
|
2962
3931
|
]);
|
|
2963
3932
|
useOfficeCollaborationLocationNavigator(navigateToPdfParticipant);
|
|
@@ -3006,8 +3975,9 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
3006
3975
|
can: pdfEditor.can(),
|
|
3007
3976
|
commands: pdfCommands,
|
|
3008
3977
|
editable: editable,
|
|
3978
|
+
pageOrganizationAvailable: pageOrganization.state.available,
|
|
3009
3979
|
saveAvailable: editable && Boolean(onSave),
|
|
3010
|
-
pageNavigation: viewerReady && registry &&
|
|
3980
|
+
pageNavigation: viewerReady && registry && viewerController.state.totalPages > 0 ? {
|
|
3011
3981
|
controlsId: mobilePageNavigationId,
|
|
3012
3982
|
expanded: mobilePageNavigationOpen,
|
|
3013
3983
|
onOpen: ()=>setMobilePageNavigationOpen(true),
|
|
@@ -3016,22 +3986,35 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
3016
3986
|
searchInputRef: searchInputRef,
|
|
3017
3987
|
saveLabel: saveLabel,
|
|
3018
3988
|
saveState: saveState,
|
|
3019
|
-
state:
|
|
3989
|
+
state: viewerController.state
|
|
3990
|
+
}),
|
|
3991
|
+
pageOrganizerOpen && viewerReady && registry && viewerController.state.totalPages > 0 && /*#__PURE__*/ jsx(PdfPageOrganizerDialog, {
|
|
3992
|
+
busy: pageOrganization.state.busy,
|
|
3993
|
+
can: pdfEditor.can(),
|
|
3994
|
+
commands: pdfCommands,
|
|
3995
|
+
currentPage: viewerController.state.currentPage,
|
|
3996
|
+
diagnostics: pageOrganization.state.diagnostics,
|
|
3997
|
+
error: pageOrganization.state.error,
|
|
3998
|
+
registry: registry,
|
|
3999
|
+
restoreFocusTarget: ()=>pdfPageOrganizerRestoreFocusTarget(pdfRootRef.current),
|
|
4000
|
+
totalPages: viewerController.state.totalPages,
|
|
4001
|
+
onClose: ()=>setPageOrganizerOpen(false),
|
|
4002
|
+
onDismissError: pageOrganization.dismissError
|
|
3020
4003
|
}),
|
|
3021
4004
|
/*#__PURE__*/ jsxs("div", {
|
|
3022
4005
|
className: "work-pdf-workspace",
|
|
3023
4006
|
"data-mobile-page-navigation": mobilePageNavigationOpen ? 'open' : 'closed',
|
|
3024
4007
|
children: [
|
|
3025
|
-
viewerReady && registry &&
|
|
3026
|
-
currentPage:
|
|
4008
|
+
viewerReady && registry && viewerController.state.totalPages > 0 && /*#__PURE__*/ jsx(PdfThumbnailRail, {
|
|
4009
|
+
currentPage: viewerController.state.currentPage,
|
|
3027
4010
|
mobileCloseButtonRef: mobilePageNavigationCloseRef,
|
|
3028
4011
|
mobileNavigationId: mobilePageNavigationId,
|
|
3029
4012
|
mobileNavigationModal: mobilePageNavigationModalOpen,
|
|
3030
4013
|
registry: registry,
|
|
3031
|
-
totalPages:
|
|
4014
|
+
totalPages: viewerController.state.totalPages,
|
|
3032
4015
|
onCloseMobileNavigation: closeMobilePageNavigation,
|
|
3033
4016
|
onSelectPage: (page)=>{
|
|
3034
|
-
|
|
4017
|
+
viewerController.goToPage(page);
|
|
3035
4018
|
if (mobilePageNavigationModalOpen) closeMobilePageNavigation();
|
|
3036
4019
|
}
|
|
3037
4020
|
}),
|
|
@@ -3109,11 +4092,11 @@ function PdfViewer({ collaboration, evidenceOverlay, fileName = 'document.pdf',
|
|
|
3109
4092
|
},
|
|
3110
4093
|
onReady: setRegistry
|
|
3111
4094
|
}, sourceUrl),
|
|
3112
|
-
viewerReady &&
|
|
3113
|
-
pageIndex:
|
|
4095
|
+
viewerReady && viewerController.state.currentPage > 0 && /*#__PURE__*/ jsx(PdfCollaborationPresenceLayer, {
|
|
4096
|
+
pageIndex: viewerController.state.currentPage - 1
|
|
3114
4097
|
}),
|
|
3115
4098
|
evidenceOverlay && registry && viewerReady && /*#__PURE__*/ jsx(PdfEvidenceOverlayLayer, {
|
|
3116
|
-
currentPage:
|
|
4099
|
+
currentPage: viewerController.state.currentPage,
|
|
3117
4100
|
evidenceOverlay: evidenceOverlay,
|
|
3118
4101
|
onEvidenceRegionSelect: onEvidenceRegionSelect,
|
|
3119
4102
|
registry: registry,
|
|
@@ -3142,6 +4125,11 @@ function pdfErrorMessage(error) {
|
|
|
3142
4125
|
if (error instanceof Error && error.message) return error.message;
|
|
3143
4126
|
return 'Unable to read this PDF file.';
|
|
3144
4127
|
}
|
|
4128
|
+
function pdfPageOrganizerRestoreFocusTarget(root) {
|
|
4129
|
+
const directTrigger = root?.querySelector('[data-pdf-page-organizer-trigger]');
|
|
4130
|
+
if (directTrigger && directTrigger.getClientRects().length > 0) return directTrigger;
|
|
4131
|
+
return root?.querySelector('.work-pdf-overflow-trigger') ?? null;
|
|
4132
|
+
}
|
|
3145
4133
|
async function assertPdfCollaborationBlob(session, source) {
|
|
3146
4134
|
const expected = readWorkOfficePdfCollaborationSource(session);
|
|
3147
4135
|
if (source.size !== expected.byteLength) throw new Error(`The loaded PDF source does not match collaboration artifact '${session.artifactId}'.`);
|
|
@@ -3176,4 +4164,4 @@ function pdfMediaQueryMatches(query) {
|
|
|
3176
4164
|
if ("u" < typeof window || 'function' != typeof window.matchMedia) return true;
|
|
3177
4165
|
return window.matchMedia(query).matches;
|
|
3178
4166
|
}
|
|
3179
|
-
export { pdf_viewer_namespaceObject };
|
|
4167
|
+
export { PDF_PAGE_ORGANIZATION_MAX_IMPORT_BYTES, PDF_PAGE_ORGANIZATION_MAX_SOURCE_BYTES, PdfPageOrganizationError, pdf_viewer_namespaceObject, validatedPdfInsertionIndex, validatedPdfPageIndexes, validatedPdfPageOrder, validatedPdfPageSize, validatedPdfSplitBoundaries };
|