@ai-matrx/office 0.8.49 → 0.8.51
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/CHANGELOG.md +22 -1
- package/dist/pdf-model.cjs +117 -3
- package/dist/pdf-model.cjs.map +1 -1
- package/dist/pdf-model.d.cts +173 -3
- package/dist/pdf-model.d.ts +173 -3
- package/dist/pdf-model.js +117 -3
- package/dist/pdf-model.js.map +1 -1
- package/dist/pdf.cjs +644 -190
- package/dist/pdf.cjs.map +1 -1
- package/dist/pdf.d.cts +173 -3
- package/dist/pdf.d.ts +173 -3
- package/dist/pdf.js +621 -154
- package/dist/pdf.js.map +1 -1
- package/package.json +3 -3
package/dist/pdf.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/pdf/PdfWorkspace.tsx
|
|
4
|
-
import { Fragment as
|
|
5
|
-
import { cn } from "@ai-matrx/design-system/cn";
|
|
6
|
-
import { Button as
|
|
4
|
+
import { Fragment as Fragment3, useCallback, useEffect as useEffect4, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState as useState4 } from "react";
|
|
5
|
+
import { cn as cn2 } from "@ai-matrx/design-system/cn";
|
|
6
|
+
import { Button as Button3, Field as Field3, PageRangeInput as PageRangeInput2, SegmentedControl as SegmentedControl3, ToolRail, parsePageRanges as parsePageRanges2 } from "@ai-matrx/design-system/controls";
|
|
7
7
|
import {
|
|
8
8
|
ChevronLeftIcon,
|
|
9
9
|
ChevronRightIcon,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
RotateCwIcon,
|
|
20
20
|
SearchIcon,
|
|
21
21
|
Share2Icon,
|
|
22
|
-
Trash2Icon,
|
|
22
|
+
Trash2Icon as Trash2Icon2,
|
|
23
23
|
Undo2Icon,
|
|
24
24
|
XIcon,
|
|
25
25
|
Columns2Icon as Columns2Icon2,
|
|
@@ -118,6 +118,7 @@ function LeftPanel(p) {
|
|
|
118
118
|
const turn = p.decisions.turns[page] ?? 0;
|
|
119
119
|
return /* @__PURE__ */ jsxs("div", { className: "relative flex h-full w-full items-center justify-center overflow-hidden bg-card", children: [
|
|
120
120
|
/* @__PURE__ */ jsx("div", { style: { transform: turn ? `rotate(${turn}deg)` : void 0 }, className: "flex h-full w-full items-center justify-center", children: p.host.renderThumbnail ? p.host.renderThumbnail(page, THUMB_W) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: page }) }),
|
|
121
|
+
p.host.renderPageBadge ? /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-1 top-1 z-10", "data-pdf-page-badge": page, children: p.host.renderPageBadge(page) }) : null,
|
|
121
122
|
page === p.currentPage ? /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute inset-0 border-2 border-primary" }) : null,
|
|
122
123
|
gone.has(page) ? /* @__PURE__ */ jsx("span", { className: "absolute inset-0 flex items-center justify-center bg-background/80 text-xs font-medium text-destructive", children: "Deleted" }) : null
|
|
123
124
|
] });
|
|
@@ -390,9 +391,94 @@ var canRedo = (h) => h.future.length > 0;
|
|
|
390
391
|
var undoLabel = (h) => h.present.label;
|
|
391
392
|
var redoLabel = (h) => h.future[0]?.label ?? "";
|
|
392
393
|
|
|
394
|
+
// src/pdf/markup.ts
|
|
395
|
+
var PDF_MARKUP_TOOLS = [
|
|
396
|
+
{ id: "highlight", label: "Highlight", key: "h", status: "ready", engineTool: "highlight" },
|
|
397
|
+
{ id: "freetext", label: "Text box", key: "x", status: "ready", engineTool: "freetext" },
|
|
398
|
+
{ id: "ink", label: "Draw", key: "d", status: "ready", engineTool: "ink" },
|
|
399
|
+
{ id: "image", label: "Image", key: "i", status: "ready", engineTool: "stamp" },
|
|
400
|
+
{ id: "signature", label: "Signature", key: "g", status: "ready", engineTool: "stamp", needs: "signaturePad" },
|
|
401
|
+
{ id: "underline", label: "Underline and strike out", status: "soon", reason: "The PDF writer we use makes highlights only." },
|
|
402
|
+
{ id: "shapes", label: "Shapes", status: "soon", reason: "Rectangles, ovals and arrows need their own writer." },
|
|
403
|
+
{ id: "note", label: "Sticky note", status: "soon", reason: "Comment on a highlight, text box or drawing for now." },
|
|
404
|
+
{ id: "links", label: "Links", status: "soon", reason: "The PDF writer we use cannot add or change links." },
|
|
405
|
+
{ id: "edit-text", label: "Edit page text", status: "soon", reason: "Changing the page's own text needs a server edit with a before-and-after check." }
|
|
406
|
+
];
|
|
407
|
+
var pdfMarkupTool = (id) => PDF_MARKUP_TOOLS.find((t) => t.id === id);
|
|
408
|
+
var MARKUP_SWATCHES = [
|
|
409
|
+
{ value: "#ffd400", label: "Yellow" },
|
|
410
|
+
{ value: "#5fe36b", label: "Green" },
|
|
411
|
+
{ value: "#4cc9f0", label: "Blue" },
|
|
412
|
+
{ value: "#ff6fb5", label: "Pink" },
|
|
413
|
+
{ value: "#ff4d4d", label: "Red" },
|
|
414
|
+
{ value: "#1f2937", label: "Black" }
|
|
415
|
+
];
|
|
416
|
+
var MARKUP_STYLE_FIELDS = {
|
|
417
|
+
highlight: ["color"],
|
|
418
|
+
freetext: ["color", "size"],
|
|
419
|
+
ink: ["color", "opacity", "size"],
|
|
420
|
+
stamp: [],
|
|
421
|
+
signature: []
|
|
422
|
+
};
|
|
423
|
+
var MARKUP_SIZES = {
|
|
424
|
+
freetext: [8, 10, 12, 14, 18, 24, 36],
|
|
425
|
+
ink: [1, 2, 3, 5, 8, 12]
|
|
426
|
+
};
|
|
427
|
+
var MARKUP_KIND_LABEL = {
|
|
428
|
+
highlight: "Highlight",
|
|
429
|
+
freetext: "Text box",
|
|
430
|
+
ink: "Drawing",
|
|
431
|
+
stamp: "Image",
|
|
432
|
+
signature: "Signature"
|
|
433
|
+
};
|
|
434
|
+
var MARKUP_PROMISES = {
|
|
435
|
+
underline: "Underline or strike out text, with the same colors as highlights.",
|
|
436
|
+
shapes: "Draw rectangles, ovals, lines and arrows on a page.",
|
|
437
|
+
note: "Pin a sticky note anywhere on a page.",
|
|
438
|
+
links: "Add, change and remove links to a page or a web address.",
|
|
439
|
+
"edit-text": "Change the page's own text, see a before-and-after view, then save."
|
|
440
|
+
};
|
|
441
|
+
var markupComingSoonId = (id) => id === "links" ? "pdf.links" : id === "edit-text" ? "pdf.edit-text" : `pdf.markup.${id}`;
|
|
442
|
+
var PDF_MARKUP_COMING_SOON = Object.fromEntries(
|
|
443
|
+
PDF_MARKUP_TOOLS.filter((t) => t.status === "soon").map((t) => {
|
|
444
|
+
const id = markupComingSoonId(t.id);
|
|
445
|
+
return [id, { id, label: t.label, owner: "pdf", promise: MARKUP_PROMISES[t.id] ?? "", stage: "planned", surfaces: ["PDF workspace markup panel"] }];
|
|
446
|
+
})
|
|
447
|
+
);
|
|
448
|
+
var TIMELINE_LIMIT = 100;
|
|
449
|
+
var emptyTimeline = () => ({ past: [], future: [] });
|
|
450
|
+
function recordStep(t, origin, label) {
|
|
451
|
+
return { past: [...t.past, { origin, label }].slice(-TIMELINE_LIMIT), future: [] };
|
|
452
|
+
}
|
|
453
|
+
var nextUndo = (t) => t.past[t.past.length - 1] ?? null;
|
|
454
|
+
var nextRedo = (t) => t.future[0] ?? null;
|
|
455
|
+
function stepBack(t) {
|
|
456
|
+
const last = t.past[t.past.length - 1];
|
|
457
|
+
return last ? { past: t.past.slice(0, -1), future: [last, ...t.future] } : t;
|
|
458
|
+
}
|
|
459
|
+
function stepForward(t) {
|
|
460
|
+
const next = t.future[0];
|
|
461
|
+
return next ? { past: [...t.past, next], future: t.future.slice(1) } : t;
|
|
462
|
+
}
|
|
463
|
+
function markupUndoneElsewhere(t) {
|
|
464
|
+
for (let i = t.past.length - 1; i >= 0; i--) {
|
|
465
|
+
if (t.past[i].origin === "markup") return { past: [...t.past.slice(0, i), ...t.past.slice(i + 1)], future: [t.past[i], ...t.future] };
|
|
466
|
+
}
|
|
467
|
+
return t;
|
|
468
|
+
}
|
|
469
|
+
function markupRedoneElsewhere(t) {
|
|
470
|
+
const i = t.future.findIndex((s) => s.origin === "markup");
|
|
471
|
+
if (i < 0) return t;
|
|
472
|
+
return { past: [...t.past, t.future[i]], future: [...t.future.slice(0, i), ...t.future.slice(i + 1)] };
|
|
473
|
+
}
|
|
474
|
+
function dropOrigin(t, origin) {
|
|
475
|
+
return { past: t.past.filter((s) => s.origin !== origin), future: t.future.filter((s) => s.origin !== origin) };
|
|
476
|
+
}
|
|
477
|
+
|
|
393
478
|
// src/pdf/tools.ts
|
|
394
479
|
var PDF_TOOLS = [
|
|
395
480
|
{ id: "browse", label: "Browse", key: "v", status: "ready", opens: "none", group: 0 },
|
|
481
|
+
{ id: "markup", label: "Comment and mark up", key: "m", status: "ready", capability: "client:pdfjs-annotation-editors,save-new-version", opens: "panel", group: 0, hint: "Highlight, write, draw, sign. Saves a new version." },
|
|
396
482
|
{ id: "organize", label: "Organize pages", key: "o", status: "ready", capability: "rotate-pages,delete-pages,reorder-pages", opens: "panel", group: 0 },
|
|
397
483
|
{ id: "extract-pages", label: "Extract pages", key: "e", status: "ready", capability: "extract-pages", opens: "panel", group: 0 },
|
|
398
484
|
{ id: "split", label: "Split", key: "s", status: "ready", capability: "split", opens: "panel", group: 0 },
|
|
@@ -413,7 +499,7 @@ var PDF_TOOLS = [
|
|
|
413
499
|
var pdfTool = (id) => PDF_TOOLS.find((t) => t.id === id);
|
|
414
500
|
var comingSoonIdOf = (id) => `pdf.${id}`;
|
|
415
501
|
var PROMISES = {
|
|
416
|
-
edit: "Change text and swap images
|
|
502
|
+
edit: "Change the page's own text and swap images, with a before-and-after view.",
|
|
417
503
|
forms: "Fill in a form and keep the values in the file.",
|
|
418
504
|
protect: "Lock a PDF with a password and limit what others can do.",
|
|
419
505
|
ocr: "Turn a scanned PDF into one you can search and copy from.",
|
|
@@ -438,14 +524,15 @@ var PDF_COMING_SOON = {
|
|
|
438
524
|
return [id, { id, label: t.label, owner: "pdf", promise: PROMISES[t.id] ?? "", stage: "planned", surfaces: ["PDF workspace tool rail"] }];
|
|
439
525
|
})
|
|
440
526
|
),
|
|
441
|
-
...PANEL_PROMISES
|
|
527
|
+
...PANEL_PROMISES,
|
|
528
|
+
...PDF_MARKUP_COMING_SOON
|
|
442
529
|
};
|
|
443
530
|
|
|
444
531
|
// src/pdf/shortcuts.ts
|
|
445
532
|
var PDF_SHORTCUTS = [
|
|
446
533
|
{ action: "undo", label: "Undo", keys: "Mod+Z", section: "Edit" },
|
|
447
534
|
{ action: "redo", label: "Redo", keys: "Mod+Shift+Z", section: "Edit" },
|
|
448
|
-
{ action: "save", label: "
|
|
535
|
+
{ action: "save", label: "Save markup or apply page changes", keys: "Mod+S", section: "Edit" },
|
|
449
536
|
{ action: "find", label: "Find", keys: "Mod+F", section: "Edit" },
|
|
450
537
|
{ action: "print", label: "Print", keys: "Mod+P", section: "Edit" },
|
|
451
538
|
{ action: "zoom-in", label: "Zoom in", keys: "Mod+=", section: "View" },
|
|
@@ -472,6 +559,14 @@ var PDF_SHORTCUTS = [
|
|
|
472
559
|
section: "Tools"
|
|
473
560
|
}))
|
|
474
561
|
];
|
|
562
|
+
var PDF_MARKUP_SHORTCUTS = PDF_MARKUP_TOOLS.filter(
|
|
563
|
+
(t) => t.status === "ready" && t.key
|
|
564
|
+
).map((t) => ({ tool: t.id, label: t.label, keys: t.key.toUpperCase() }));
|
|
565
|
+
function matchMarkupKey(e, typing) {
|
|
566
|
+
if (typing || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.key.length !== 1) return null;
|
|
567
|
+
const k = e.key.toLowerCase();
|
|
568
|
+
return PDF_MARKUP_SHORTCUTS.find((s) => s.keys.toLowerCase() === k)?.tool ?? null;
|
|
569
|
+
}
|
|
475
570
|
function parse(keys) {
|
|
476
571
|
const parts = keys.split("+");
|
|
477
572
|
const key = parts[parts.length - 1] === "" ? "+" : parts[parts.length - 1];
|
|
@@ -498,6 +593,209 @@ function formatKeys(keys, mac) {
|
|
|
498
593
|
return keys.split("+").map((k) => k === "Mod" ? mac ? "\u2318" : "Ctrl" : k === "Shift" ? mac ? "\u21E7" : "Shift" : k === "ArrowRight" ? "\u2192" : k === "ArrowLeft" ? "\u2190" : k).join(mac ? "" : "+");
|
|
499
594
|
}
|
|
500
595
|
|
|
596
|
+
// src/pdf/markup-panel.tsx
|
|
597
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
598
|
+
import { cn } from "@ai-matrx/design-system/cn";
|
|
599
|
+
import { AnchoredToolbar, Button as Button2, ColorSwatchRow, Field as Field2, OpacitySlider, Select as Select2 } from "@ai-matrx/design-system/controls";
|
|
600
|
+
import {
|
|
601
|
+
HighlighterIcon,
|
|
602
|
+
ImageIcon,
|
|
603
|
+
Link2Icon,
|
|
604
|
+
MessageSquareIcon,
|
|
605
|
+
PencilIcon,
|
|
606
|
+
PenToolIcon,
|
|
607
|
+
ProportionsIcon,
|
|
608
|
+
StickyNoteIcon,
|
|
609
|
+
Trash2Icon,
|
|
610
|
+
TypeIcon,
|
|
611
|
+
WrapTextIcon
|
|
612
|
+
} from "@ai-matrx/design-system/icons";
|
|
613
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
614
|
+
var MARKUP_ICONS = {
|
|
615
|
+
highlight: HighlighterIcon,
|
|
616
|
+
freetext: TypeIcon,
|
|
617
|
+
ink: PencilIcon,
|
|
618
|
+
image: ImageIcon,
|
|
619
|
+
signature: PenToolIcon,
|
|
620
|
+
underline: WrapTextIcon,
|
|
621
|
+
shapes: ProportionsIcon,
|
|
622
|
+
note: StickyNoteIcon,
|
|
623
|
+
links: Link2Icon,
|
|
624
|
+
"edit-text": TypeIcon
|
|
625
|
+
};
|
|
626
|
+
function Section({ label, children }) {
|
|
627
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1.5", children: [
|
|
628
|
+
/* @__PURE__ */ jsx3("span", { className: "text-xs font-medium text-muted-foreground", children: label }),
|
|
629
|
+
children
|
|
630
|
+
] });
|
|
631
|
+
}
|
|
632
|
+
function StyleControls({ kind, style, onChange, compact }) {
|
|
633
|
+
const fields = MARKUP_STYLE_FIELDS[kind];
|
|
634
|
+
if (!fields.length) return null;
|
|
635
|
+
const sizes = kind === "freetext" ? MARKUP_SIZES.freetext : kind === "ink" ? MARKUP_SIZES.ink : null;
|
|
636
|
+
return /* @__PURE__ */ jsxs3("div", { className: cn("flex gap-2", compact ? "items-center" : "flex-col"), children: [
|
|
637
|
+
fields.includes("color") ? /* @__PURE__ */ jsx3(ColorSwatchRow, { colors: MARKUP_SWATCHES, value: style.color, onChange: (color) => onChange({ color }), "aria-label": "Color" }) : null,
|
|
638
|
+
fields.includes("opacity") ? /* @__PURE__ */ jsx3("div", { className: compact ? "w-52 shrink-0" : "w-full min-w-0", children: /* @__PURE__ */ jsx3(OpacitySlider, { value: style.opacity, onChange: (opacity) => onChange({ opacity }), className: "w-full" }) }) : null,
|
|
639
|
+
fields.includes("size") && sizes ? /* @__PURE__ */ jsx3("div", { className: compact ? "w-24 shrink-0" : void 0, children: /* @__PURE__ */ jsx3(
|
|
640
|
+
Select2,
|
|
641
|
+
{
|
|
642
|
+
"aria-label": kind === "freetext" ? "Font size" : "Line width",
|
|
643
|
+
value: String(sizes.includes(style.size) ? style.size : sizes[0]),
|
|
644
|
+
onValueChange: (v) => onChange({ size: Number(v) }),
|
|
645
|
+
options: sizes.map((n) => ({ value: String(n), label: `${n} pt` }))
|
|
646
|
+
}
|
|
647
|
+
) }) : null
|
|
648
|
+
] });
|
|
649
|
+
}
|
|
650
|
+
function MarkupPanel({ host, engine, snapshot, active, onPick, blocked, busy, online, error, onSave, onDiscard, onSoon, goToPage, reloadKey }) {
|
|
651
|
+
const [confirmDiscard, setConfirmDiscard] = useState3(false);
|
|
652
|
+
const [signature, setSignature] = useState3(null);
|
|
653
|
+
const [existing, setExisting] = useState3(null);
|
|
654
|
+
const pending = snapshot?.pending ?? 0;
|
|
655
|
+
const tools = PDF_MARKUP_TOOLS.filter((t) => t.needs !== "signaturePad" || host.signaturePad);
|
|
656
|
+
const ready = tools.filter((t) => t.status === "ready");
|
|
657
|
+
const soon = tools.filter((t) => t.status === "soon");
|
|
658
|
+
const def = tools.find((t) => t.id === active);
|
|
659
|
+
const styleKind = def?.engineTool && def.engineTool !== "stamp" ? def.engineTool : null;
|
|
660
|
+
const Pad = host.signaturePad;
|
|
661
|
+
useEffect3(() => {
|
|
662
|
+
let live = true;
|
|
663
|
+
host.existingAnnotations?.().then((list) => live && setExisting(list), () => live && setExisting([]));
|
|
664
|
+
return () => {
|
|
665
|
+
live = false;
|
|
666
|
+
};
|
|
667
|
+
}, [host, reloadKey]);
|
|
668
|
+
useEffect3(() => {
|
|
669
|
+
if (!pending) setConfirmDiscard(false);
|
|
670
|
+
}, [pending]);
|
|
671
|
+
const pickImage = () => {
|
|
672
|
+
const input = document.createElement("input");
|
|
673
|
+
input.type = "file";
|
|
674
|
+
input.accept = "image/png,image/jpeg,image/webp,image/gif,image/svg+xml";
|
|
675
|
+
input.onchange = () => {
|
|
676
|
+
const f = input.files?.[0];
|
|
677
|
+
if (f && engine) void engine.addImage(f, "stamp");
|
|
678
|
+
};
|
|
679
|
+
input.click();
|
|
680
|
+
};
|
|
681
|
+
const row = (t) => {
|
|
682
|
+
const Icon = MARKUP_ICONS[t.id];
|
|
683
|
+
const on = t.id === active;
|
|
684
|
+
return /* @__PURE__ */ jsxs3("li", { children: [
|
|
685
|
+
/* @__PURE__ */ jsxs3(
|
|
686
|
+
"button",
|
|
687
|
+
{
|
|
688
|
+
type: "button",
|
|
689
|
+
"aria-pressed": t.status === "ready" ? on : void 0,
|
|
690
|
+
disabled: t.status === "ready" && (!engine || Boolean(blocked)),
|
|
691
|
+
onClick: () => t.status === "soon" ? onSoon(markupComingSoonId(t.id)) : onPick(t.id),
|
|
692
|
+
className: cn(
|
|
693
|
+
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:text-disabled-foreground",
|
|
694
|
+
on && "bg-accent"
|
|
695
|
+
),
|
|
696
|
+
children: [
|
|
697
|
+
/* @__PURE__ */ jsx3(Icon, { className: "size-4 shrink-0" }),
|
|
698
|
+
/* @__PURE__ */ jsx3("span", { className: "min-w-0 flex-1 truncate", children: t.label }),
|
|
699
|
+
t.status === "soon" ? /* @__PURE__ */ jsx3("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: "Soon" }) : /* @__PURE__ */ jsx3("kbd", { className: "text-muted-foreground", children: t.key?.toUpperCase() })
|
|
700
|
+
]
|
|
701
|
+
}
|
|
702
|
+
),
|
|
703
|
+
t.status === "soon" && t.reason ? /* @__PURE__ */ jsx3("div", { className: "pl-8 pr-2 text-xs text-muted-foreground", children: t.reason }) : null
|
|
704
|
+
] }, t.id);
|
|
705
|
+
};
|
|
706
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-4", "data-pdf-markup-panel": "", children: [
|
|
707
|
+
blocked ? /* @__PURE__ */ jsx3("div", { role: "status", className: "rounded-md border border-border px-2 py-1.5 text-xs", children: blocked }) : null,
|
|
708
|
+
!engine && !blocked ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "Opening the document\u2026" }) : null,
|
|
709
|
+
/* @__PURE__ */ jsx3("ul", { className: "flex flex-col", "aria-label": "Markup tools", children: ready.map(row) }),
|
|
710
|
+
styleKind && snapshot ? /* @__PURE__ */ jsx3(Section, { label: "Look", children: /* @__PURE__ */ jsx3(StyleControls, { kind: styleKind, style: snapshot.styles[styleKind], onChange: (p) => engine?.setStyle(p) }) }) : null,
|
|
711
|
+
active === "highlight" ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "Select text on the page to highlight it." }) : null,
|
|
712
|
+
active === "freetext" ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "Click on the page, then type." }) : null,
|
|
713
|
+
active === "ink" ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "Drag on the page to draw." }) : null,
|
|
714
|
+
active === "image" ? /* @__PURE__ */ jsx3(Button2, { variant: "outline", icon: /* @__PURE__ */ jsx3(ImageIcon, {}), disabled: !engine, onClick: pickImage, children: "Choose an image" }) : null,
|
|
715
|
+
active === "signature" && Pad ? /* @__PURE__ */ jsxs3(Section, { label: "Signature", children: [
|
|
716
|
+
/* @__PURE__ */ jsx3(Pad, { value: signature, onChange: setSignature }),
|
|
717
|
+
/* @__PURE__ */ jsx3(Button2, { variant: "outline", icon: /* @__PURE__ */ jsx3(PenToolIcon, {}), disabled: !engine || !signature, onClick: () => signature && engine && void engine.addImage(signature, "signature"), children: "Place on page" })
|
|
718
|
+
] }) : null,
|
|
719
|
+
/* @__PURE__ */ jsxs3(Section, { label: "Changes", children: [
|
|
720
|
+
/* @__PURE__ */ jsx3("div", { className: "text-xs", role: "status", "data-pdf-markup-pending": "", children: pending ? `Not saved: ${snapshot?.summary}` : "No unsaved markup" }),
|
|
721
|
+
error ? /* @__PURE__ */ jsx3("div", { role: "alert", className: "text-xs text-destructive", children: error }) : null,
|
|
722
|
+
!online ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "You are offline. Markup is kept here until you are back online." }) : null,
|
|
723
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
|
|
724
|
+
/* @__PURE__ */ jsx3(Button2, { variant: "primary", disabled: !pending || busy || !online, onClick: onSave, children: busy ? "Saving" : "Save" }),
|
|
725
|
+
confirmDiscard ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
726
|
+
/* @__PURE__ */ jsxs3(Button2, { variant: "outline", onClick: () => {
|
|
727
|
+
setConfirmDiscard(false);
|
|
728
|
+
onDiscard();
|
|
729
|
+
}, children: [
|
|
730
|
+
"Discard ",
|
|
731
|
+
pending
|
|
732
|
+
] }),
|
|
733
|
+
/* @__PURE__ */ jsx3(Button2, { variant: "quiet", onClick: () => setConfirmDiscard(false), children: "Keep" })
|
|
734
|
+
] }) : /* @__PURE__ */ jsx3(Button2, { variant: "outline", disabled: !pending || busy, onClick: () => setConfirmDiscard(true), children: "Discard" })
|
|
735
|
+
] })
|
|
736
|
+
] }),
|
|
737
|
+
host.existingAnnotations ? /* @__PURE__ */ jsxs3(Section, { label: "In this file", children: [
|
|
738
|
+
existing === null ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "Reading\u2026" }) : existing.filter((a) => a.subtype !== "Link" && a.subtype !== "Widget").length === 0 ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "No comments or markup yet" }) : /* @__PURE__ */ jsx3("ul", { className: "flex flex-col", "aria-label": "Annotations in this file", children: existing.filter((a) => a.subtype !== "Link" && a.subtype !== "Widget").map((a, i) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsxs3("button", { type: "button", onClick: () => goToPage(a.page), className: "flex w-full items-start gap-2 rounded-md px-2 py-1 text-left text-xs hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [
|
|
739
|
+
/* @__PURE__ */ jsxs3("span", { className: "w-8 shrink-0 tabular-nums text-muted-foreground", children: [
|
|
740
|
+
"p. ",
|
|
741
|
+
a.page
|
|
742
|
+
] }),
|
|
743
|
+
/* @__PURE__ */ jsxs3("span", { className: "min-w-0 flex-1", children: [
|
|
744
|
+
/* @__PURE__ */ jsxs3("span", { className: "block", children: [
|
|
745
|
+
a.subtype,
|
|
746
|
+
a.author ? ` \xB7 ${a.author}` : ""
|
|
747
|
+
] }),
|
|
748
|
+
a.contents ? /* @__PURE__ */ jsx3("span", { className: "block truncate text-muted-foreground", children: a.contents }) : null
|
|
749
|
+
] })
|
|
750
|
+
] }) }, `${a.page}-${i}`)) }),
|
|
751
|
+
/* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: "Markup already saved shows on the page; changing it in place is coming." })
|
|
752
|
+
] }) : null,
|
|
753
|
+
/* @__PURE__ */ jsx3(Section, { label: "Coming soon", children: /* @__PURE__ */ jsx3("ul", { className: "flex flex-col", "aria-label": "Markup coming soon", children: soon.map(row) }) })
|
|
754
|
+
] });
|
|
755
|
+
}
|
|
756
|
+
function MarkupFloatingToolbar({ engine, snapshot, enabled, onDone }) {
|
|
757
|
+
const sel = snapshot?.selection ?? null;
|
|
758
|
+
const [commenting, setCommenting] = useState3(false);
|
|
759
|
+
const [draft, setDraft] = useState3("");
|
|
760
|
+
const selKey = sel ? `${sel.kind}:${Math.round(sel.rect.x)}:${Math.round(sel.rect.y)}` : "";
|
|
761
|
+
useEffect3(() => {
|
|
762
|
+
setCommenting(false);
|
|
763
|
+
setDraft(sel?.comment ?? "");
|
|
764
|
+
}, [selKey]);
|
|
765
|
+
if (!engine || !enabled || !sel) return null;
|
|
766
|
+
return /* @__PURE__ */ jsx3(AnchoredToolbar, { open: true, rect: sel.rect, "aria-label": `${MARKUP_KIND_LABEL[sel.kind]} options`, children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", "data-pdf-markup-toolbar": "", children: [
|
|
767
|
+
/* @__PURE__ */ jsx3(StyleControls, { compact: true, kind: sel.kind, style: sel.style, onChange: (p) => engine.setStyle(p) }),
|
|
768
|
+
commenting ? /* @__PURE__ */ jsx3(
|
|
769
|
+
Field2,
|
|
770
|
+
{
|
|
771
|
+
autoFocus: true,
|
|
772
|
+
value: draft,
|
|
773
|
+
"aria-label": "Comment",
|
|
774
|
+
placeholder: "Comment",
|
|
775
|
+
onChange: (e) => setDraft(e.target.value),
|
|
776
|
+
onKeyDown: (e) => {
|
|
777
|
+
if (e.key === "Enter") {
|
|
778
|
+
engine.setComment(draft);
|
|
779
|
+
setCommenting(false);
|
|
780
|
+
onDone();
|
|
781
|
+
}
|
|
782
|
+
if (e.key === "Escape") {
|
|
783
|
+
e.stopPropagation();
|
|
784
|
+
setCommenting(false);
|
|
785
|
+
onDone();
|
|
786
|
+
}
|
|
787
|
+
},
|
|
788
|
+
onBlur: () => {
|
|
789
|
+
engine.setComment(draft);
|
|
790
|
+
setCommenting(false);
|
|
791
|
+
},
|
|
792
|
+
width: "md"
|
|
793
|
+
}
|
|
794
|
+
) : /* @__PURE__ */ jsx3(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx3(MessageSquareIcon, {}), "aria-label": sel.comment ? "Edit comment" : "Add comment", title: sel.comment || "Add comment", pressed: Boolean(sel.comment), onClick: () => setCommenting(true) }),
|
|
795
|
+
/* @__PURE__ */ jsx3(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx3(Trash2Icon, {}), "aria-label": "Delete", title: "Delete", onClick: () => engine.deleteSelected() })
|
|
796
|
+
] }) });
|
|
797
|
+
}
|
|
798
|
+
|
|
501
799
|
// src/pdf/tool-icons.tsx
|
|
502
800
|
import {
|
|
503
801
|
ArrowLeftRightIcon,
|
|
@@ -512,24 +810,26 @@ import {
|
|
|
512
810
|
LayoutGridIcon,
|
|
513
811
|
ListChecksIcon,
|
|
514
812
|
LockIcon,
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
813
|
+
MessageSquareIcon as MessageSquareIcon2,
|
|
814
|
+
PencilIcon as PencilIcon2,
|
|
815
|
+
PenToolIcon as PenToolIcon2,
|
|
816
|
+
ProportionsIcon as ProportionsIcon2,
|
|
518
817
|
SplitIcon,
|
|
519
|
-
TypeIcon
|
|
818
|
+
TypeIcon as TypeIcon2
|
|
520
819
|
} from "@ai-matrx/design-system/icons";
|
|
521
820
|
var PDF_TOOL_ICONS = {
|
|
522
821
|
browse: HandIcon,
|
|
822
|
+
markup: MessageSquareIcon2,
|
|
523
823
|
organize: LayoutGridIcon,
|
|
524
824
|
"extract-pages": ArrowUpFromLineIcon,
|
|
525
825
|
split: SplitIcon,
|
|
526
|
-
crop:
|
|
826
|
+
crop: ProportionsIcon2,
|
|
527
827
|
redact: EraserIcon,
|
|
528
828
|
compress: ChevronsDownUpIcon,
|
|
529
829
|
scan: CameraIcon,
|
|
530
|
-
sign:
|
|
531
|
-
"extract-data":
|
|
532
|
-
edit:
|
|
830
|
+
sign: PenToolIcon2,
|
|
831
|
+
"extract-data": TypeIcon2,
|
|
832
|
+
edit: PencilIcon2,
|
|
533
833
|
forms: ListChecksIcon,
|
|
534
834
|
protect: LockIcon,
|
|
535
835
|
ocr: CaptionsIcon,
|
|
@@ -539,7 +839,7 @@ var PDF_TOOL_ICONS = {
|
|
|
539
839
|
};
|
|
540
840
|
|
|
541
841
|
// src/pdf/PdfWorkspace.tsx
|
|
542
|
-
import { Fragment as
|
|
842
|
+
import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
543
843
|
var ZOOM_STEPS = [0.5, 0.75, 1, 1.25, 1.5, 2, 3];
|
|
544
844
|
var COMPACT_BELOW_PX = 768;
|
|
545
845
|
var BAR = "flex h-10 shrink-0 items-center gap-1 border-b border-border bg-background px-2";
|
|
@@ -551,7 +851,7 @@ async function announceSoon(id) {
|
|
|
551
851
|
await announceComingSoon(id);
|
|
552
852
|
}
|
|
553
853
|
function useWidth(ref) {
|
|
554
|
-
const [size, setSize] =
|
|
854
|
+
const [size, setSize] = useState4({ w: 1200, h: 800 });
|
|
555
855
|
useLayoutEffect(() => {
|
|
556
856
|
const node = ref.current;
|
|
557
857
|
if (!node) return void 0;
|
|
@@ -564,36 +864,36 @@ function useWidth(ref) {
|
|
|
564
864
|
}, [ref]);
|
|
565
865
|
return size;
|
|
566
866
|
}
|
|
567
|
-
function ToolRows({ tool, hostTools, onPick }) {
|
|
568
|
-
return /* @__PURE__ */
|
|
569
|
-
hostTools.map((t) => /* @__PURE__ */
|
|
867
|
+
function ToolRows({ tool, hostTools, onPick, canMarkup = false }) {
|
|
868
|
+
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
869
|
+
hostTools.map((t) => /* @__PURE__ */ jsx4("li", { role: "none", children: /* @__PURE__ */ jsxs4(
|
|
570
870
|
"button",
|
|
571
871
|
{
|
|
572
872
|
type: "button",
|
|
573
873
|
role: "menuitem",
|
|
574
874
|
"aria-current": tool === `host:${t.id}` ? "true" : void 0,
|
|
575
|
-
className:
|
|
875
|
+
className: cn2("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", tool === `host:${t.id}` && "bg-accent"),
|
|
576
876
|
onClick: () => onPick(`host:${t.id}`),
|
|
577
877
|
children: [
|
|
578
|
-
/* @__PURE__ */
|
|
579
|
-
/* @__PURE__ */
|
|
878
|
+
/* @__PURE__ */ jsx4(t.icon, { className: "size-4 shrink-0" }),
|
|
879
|
+
/* @__PURE__ */ jsx4("span", { className: "flex-1 whitespace-nowrap", children: t.label })
|
|
580
880
|
]
|
|
581
881
|
}
|
|
582
882
|
) }, `host:${t.id}`)),
|
|
583
|
-
PDF_TOOLS.filter((t) => t.id !== "browse").map((t) => {
|
|
883
|
+
PDF_TOOLS.filter((t) => t.id !== "browse" && (t.id !== "markup" || canMarkup)).map((t) => {
|
|
584
884
|
const Icon = PDF_TOOL_ICONS[t.id];
|
|
585
|
-
return /* @__PURE__ */
|
|
885
|
+
return /* @__PURE__ */ jsx4("li", { role: "none", children: /* @__PURE__ */ jsxs4(
|
|
586
886
|
"button",
|
|
587
887
|
{
|
|
588
888
|
type: "button",
|
|
589
889
|
role: "menuitem",
|
|
590
890
|
"aria-current": t.id === tool ? "true" : void 0,
|
|
591
|
-
className:
|
|
891
|
+
className: cn2("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", t.id === tool && "bg-accent"),
|
|
592
892
|
onClick: () => onPick(t.id),
|
|
593
893
|
children: [
|
|
594
|
-
/* @__PURE__ */
|
|
595
|
-
/* @__PURE__ */
|
|
596
|
-
t.status === "soon" ? /* @__PURE__ */
|
|
894
|
+
/* @__PURE__ */ jsx4(Icon, { className: "size-4 shrink-0" }),
|
|
895
|
+
/* @__PURE__ */ jsx4("span", { className: "flex-1 whitespace-nowrap", children: t.label }),
|
|
896
|
+
t.status === "soon" ? /* @__PURE__ */ jsx4("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
|
|
597
897
|
]
|
|
598
898
|
}
|
|
599
899
|
) }, t.id);
|
|
@@ -607,40 +907,48 @@ function PdfWorkspace({ host, className }) {
|
|
|
607
907
|
const viewSize = useWidth(scrollerRef);
|
|
608
908
|
const compact = rootSize.w < COMPACT_BELOW_PX;
|
|
609
909
|
const online = host.online !== false;
|
|
610
|
-
const [pageCount, setPageCount] =
|
|
611
|
-
const [history, setHistory] =
|
|
910
|
+
const [pageCount, setPageCount] = useState4(0);
|
|
911
|
+
const [history, setHistory] = useState4(() => startHistory(freshDecisions(0)));
|
|
612
912
|
const decisions = history.present.value;
|
|
613
913
|
const dirty = isDirty(decisions);
|
|
614
|
-
const [tool, setTool] =
|
|
615
|
-
const [hostToolId, setHostToolId] =
|
|
914
|
+
const [tool, setTool] = useState4("browse");
|
|
915
|
+
const [hostToolId, setHostToolId] = useState4(null);
|
|
616
916
|
const hostTools = useMemo2(() => host.tools ?? [], [host.tools]);
|
|
617
917
|
const hostTool = hostToolId ? hostTools.find((t) => t.id === hostToolId) ?? null : null;
|
|
618
918
|
const activeToolKey = hostTool ? `host:${hostTool.id}` : tool;
|
|
619
|
-
const [leftOpen, setLeftOpen] =
|
|
620
|
-
const [rightOpen, setRightOpen] =
|
|
621
|
-
const [leftTab, setLeftTab] =
|
|
622
|
-
const [read, setRead] =
|
|
623
|
-
const [spread, setSpread] =
|
|
624
|
-
const [zoomMode, setZoomMode] =
|
|
625
|
-
const [customZoom, setCustomZoom] =
|
|
626
|
-
const [selected, setSelected] =
|
|
627
|
-
const [currentPage, setCurrentPage] =
|
|
628
|
-
const [pageDraft, setPageDraft] =
|
|
629
|
-
const [findOpen, setFindOpen] =
|
|
630
|
-
const [findFocus, setFindFocus] =
|
|
631
|
-
const [pop, setPop] =
|
|
632
|
-
const [sheet, setSheet] =
|
|
633
|
-
const [toolQuery, setToolQuery] =
|
|
634
|
-
const [busy, setBusy] =
|
|
635
|
-
const [error, setError] =
|
|
636
|
-
const [result, setResult] =
|
|
637
|
-
const [reloadKey, setReloadKey] =
|
|
638
|
-
const [sizeVersion, setSizeVersion] =
|
|
919
|
+
const [leftOpen, setLeftOpen] = useState4(true);
|
|
920
|
+
const [rightOpen, setRightOpen] = useState4(true);
|
|
921
|
+
const [leftTab, setLeftTab] = useState4("pages");
|
|
922
|
+
const [read, setRead] = useState4(false);
|
|
923
|
+
const [spread, setSpread] = useState4("single");
|
|
924
|
+
const [zoomMode, setZoomMode] = useState4("fit-width");
|
|
925
|
+
const [customZoom, setCustomZoom] = useState4(1);
|
|
926
|
+
const [selected, setSelected] = useState4([]);
|
|
927
|
+
const [currentPage, setCurrentPage] = useState4(1);
|
|
928
|
+
const [pageDraft, setPageDraft] = useState4("1");
|
|
929
|
+
const [findOpen, setFindOpen] = useState4(false);
|
|
930
|
+
const [findFocus, setFindFocus] = useState4(0);
|
|
931
|
+
const [pop, setPop] = useState4(null);
|
|
932
|
+
const [sheet, setSheet] = useState4(null);
|
|
933
|
+
const [toolQuery, setToolQuery] = useState4("");
|
|
934
|
+
const [busy, setBusy] = useState4(false);
|
|
935
|
+
const [error, setError] = useState4(null);
|
|
936
|
+
const [result, setResult] = useState4(null);
|
|
937
|
+
const [reloadKey, setReloadKey] = useState4(0);
|
|
938
|
+
const [sizeVersion, setSizeVersion] = useState4(0);
|
|
639
939
|
const sizes = useRef2(/* @__PURE__ */ new Map());
|
|
640
|
-
const [editingName, setEditingName] =
|
|
641
|
-
const [nameDraft, setNameDraft] =
|
|
642
|
-
const [printMode, setPrintMode] =
|
|
643
|
-
const [printRange, setPrintRange] =
|
|
940
|
+
const [editingName, setEditingName] = useState4(false);
|
|
941
|
+
const [nameDraft, setNameDraft] = useState4(host.file.name);
|
|
942
|
+
const [printMode, setPrintMode] = useState4("all");
|
|
943
|
+
const [printRange, setPrintRange] = useState4("");
|
|
944
|
+
const canMarkup = Boolean(host.saveMarkup);
|
|
945
|
+
const [engine, setEngine] = useState4(null);
|
|
946
|
+
const [snap, setSnap] = useState4(null);
|
|
947
|
+
const [timeline, setTimeline] = useState4(emptyTimeline);
|
|
948
|
+
const [markupTool, setMarkupTool] = useState4("highlight");
|
|
949
|
+
const [markupBusy, setMarkupBusy] = useState4(false);
|
|
950
|
+
const [markupError, setMarkupError] = useState4(null);
|
|
951
|
+
const markupPending = snap?.pending ?? 0;
|
|
644
952
|
const selectedPages = useMemo2(() => selected.map(Number).sort((a, b) => a - b), [selected]);
|
|
645
953
|
const toolDef = pdfTool(tool);
|
|
646
954
|
const first = sizes.current.get(1);
|
|
@@ -673,7 +981,7 @@ function PdfWorkspace({ host, className }) {
|
|
|
673
981
|
},
|
|
674
982
|
[pageCount]
|
|
675
983
|
);
|
|
676
|
-
|
|
984
|
+
useEffect4(() => {
|
|
677
985
|
const node = scrollerRef.current;
|
|
678
986
|
if (!node) return void 0;
|
|
679
987
|
let raf = 0;
|
|
@@ -700,7 +1008,10 @@ function PdfWorkspace({ host, className }) {
|
|
|
700
1008
|
cancelAnimationFrame(raf);
|
|
701
1009
|
};
|
|
702
1010
|
}, [pageCount]);
|
|
703
|
-
const commit = useCallback((label, next) =>
|
|
1011
|
+
const commit = useCallback((label, next) => {
|
|
1012
|
+
setHistory((h) => pushStep(h, label, next));
|
|
1013
|
+
setTimeline((t) => recordStep(t, "page", label));
|
|
1014
|
+
}, []);
|
|
704
1015
|
const targets = useCallback(() => selectedPages.length ? selectedPages : [currentPage], [selectedPages, currentPage]);
|
|
705
1016
|
const turn = useCallback(
|
|
706
1017
|
(by) => {
|
|
@@ -763,9 +1074,106 @@ function PdfWorkspace({ host, className }) {
|
|
|
763
1074
|
);
|
|
764
1075
|
const apply = useCallback(() => {
|
|
765
1076
|
const ops = compilePageOps(decisions);
|
|
766
|
-
if (ops.length)
|
|
767
|
-
|
|
768
|
-
|
|
1077
|
+
if (!ops.length) return;
|
|
1078
|
+
if (markupPending) {
|
|
1079
|
+
setError("Save or discard your markup first: applying page changes reloads the file.");
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
setTimeline((t) => dropOrigin(t, "page"));
|
|
1083
|
+
void runTool({ tool: "apply-pages", ops });
|
|
1084
|
+
}, [decisions, runTool, markupPending]);
|
|
1085
|
+
const discard = useCallback(() => {
|
|
1086
|
+
setHistory(startHistory(freshDecisions(pageCount)));
|
|
1087
|
+
setTimeline((t) => dropOrigin(t, "page"));
|
|
1088
|
+
}, [pageCount]);
|
|
1089
|
+
const onMarkupEngine = useCallback((e) => {
|
|
1090
|
+
setEngine(e);
|
|
1091
|
+
setSnap(e ? e.getSnapshot() : null);
|
|
1092
|
+
}, []);
|
|
1093
|
+
useEffect4(() => {
|
|
1094
|
+
if (!engine) return void 0;
|
|
1095
|
+
const offSnap = engine.subscribe(setSnap);
|
|
1096
|
+
const offHistory = engine.onHistory((ev) => {
|
|
1097
|
+
if (ev.type === "step") setTimeline((t) => recordStep(t, "markup", ev.label));
|
|
1098
|
+
else if (ev.external) setTimeline((t) => ev.type === "undo" ? markupUndoneElsewhere(t) : markupRedoneElsewhere(t));
|
|
1099
|
+
});
|
|
1100
|
+
return () => {
|
|
1101
|
+
offSnap();
|
|
1102
|
+
offHistory();
|
|
1103
|
+
};
|
|
1104
|
+
}, [engine]);
|
|
1105
|
+
const markupOn = tool === "markup" && !hostTool && !dirty;
|
|
1106
|
+
const markupEngineTool = pdfMarkupTool(markupTool)?.engineTool ?? "highlight";
|
|
1107
|
+
useEffect4(() => {
|
|
1108
|
+
if (engine) void engine.setTool(markupOn ? markupEngineTool : "none");
|
|
1109
|
+
}, [engine, markupOn, markupEngineTool]);
|
|
1110
|
+
useEffect4(() => {
|
|
1111
|
+
engine?.setCurrentPage(currentPage);
|
|
1112
|
+
}, [engine, currentPage]);
|
|
1113
|
+
const saveMarkup = useCallback(async () => {
|
|
1114
|
+
if (!engine || !host.saveMarkup || !markupPending || markupBusy) return;
|
|
1115
|
+
if (!online) {
|
|
1116
|
+
setMarkupError("You are offline. Your markup is kept here; save when you are back online.");
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
setMarkupBusy(true);
|
|
1120
|
+
setMarkupError(null);
|
|
1121
|
+
try {
|
|
1122
|
+
const summary = engine.getSnapshot().summary;
|
|
1123
|
+
const bytes = await engine.save();
|
|
1124
|
+
const out = await host.saveMarkup(bytes, summary);
|
|
1125
|
+
setResult(out);
|
|
1126
|
+
setTimeline((t) => dropOrigin(t, "markup"));
|
|
1127
|
+
sizes.current.clear();
|
|
1128
|
+
setReloadKey((k) => k + 1);
|
|
1129
|
+
} catch (e) {
|
|
1130
|
+
const message = e instanceof Error ? e.message : "The markup was not saved.";
|
|
1131
|
+
setMarkupError(message);
|
|
1132
|
+
host.notify?.(message, "error");
|
|
1133
|
+
} finally {
|
|
1134
|
+
setMarkupBusy(false);
|
|
1135
|
+
}
|
|
1136
|
+
}, [engine, host, markupPending, markupBusy, online]);
|
|
1137
|
+
const discardMarkup = useCallback(() => {
|
|
1138
|
+
setTimeline((t) => dropOrigin(t, "markup"));
|
|
1139
|
+
setMarkupError(null);
|
|
1140
|
+
setReloadKey((k) => k + 1);
|
|
1141
|
+
}, []);
|
|
1142
|
+
useEffect4(() => {
|
|
1143
|
+
if (!markupPending) return void 0;
|
|
1144
|
+
const onBeforeUnload = (event) => {
|
|
1145
|
+
event.preventDefault();
|
|
1146
|
+
event.returnValue = "";
|
|
1147
|
+
};
|
|
1148
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
1149
|
+
return () => window.removeEventListener("beforeunload", onBeforeUnload);
|
|
1150
|
+
}, [markupPending]);
|
|
1151
|
+
const undoAll = useCallback(() => {
|
|
1152
|
+
const step = nextUndo(timeline);
|
|
1153
|
+
if (step?.origin === "markup" && engine) {
|
|
1154
|
+
setTimeline(stepBack);
|
|
1155
|
+
engine.undo();
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
if (step) setTimeline(stepBack);
|
|
1159
|
+
setHistory((h) => undo(h));
|
|
1160
|
+
}, [timeline, engine]);
|
|
1161
|
+
const redoAll = useCallback(() => {
|
|
1162
|
+
const step = nextRedo(timeline);
|
|
1163
|
+
if (step?.origin === "markup" && engine) {
|
|
1164
|
+
setTimeline(stepForward);
|
|
1165
|
+
engine.redo();
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
if (step) setTimeline(stepForward);
|
|
1169
|
+
setHistory((h) => redo(h));
|
|
1170
|
+
}, [timeline, engine]);
|
|
1171
|
+
const undoStep = nextUndo(timeline);
|
|
1172
|
+
const redoStep = nextRedo(timeline);
|
|
1173
|
+
const undoText = undoStep ? `Undo ${undoStep.label}` : undoLabel(history) ? `Undo ${undoLabel(history)}` : "Undo";
|
|
1174
|
+
const redoText = redoStep ? `Redo ${redoStep.label}` : redoLabel(history) ? `Redo ${redoLabel(history)}` : "Redo";
|
|
1175
|
+
const canUndoAll = Boolean(undoStep) || canUndo(history);
|
|
1176
|
+
const canRedoAll = Boolean(redoStep) || canRedo(history);
|
|
769
1177
|
const chooseTool = useCallback(
|
|
770
1178
|
(id) => {
|
|
771
1179
|
if (id.startsWith("host:")) {
|
|
@@ -777,7 +1185,7 @@ function PdfWorkspace({ host, className }) {
|
|
|
777
1185
|
return;
|
|
778
1186
|
}
|
|
779
1187
|
const def = pdfTool(id);
|
|
780
|
-
if (!def) return;
|
|
1188
|
+
if (!def || def.id === "markup" && !host.saveMarkup) return;
|
|
781
1189
|
setHostToolId(null);
|
|
782
1190
|
if (def.status === "soon") {
|
|
783
1191
|
announceSoon(comingSoonIdOf(def.id)).catch(fail);
|
|
@@ -798,23 +1206,23 @@ function PdfWorkspace({ host, className }) {
|
|
|
798
1206
|
const railGroups = useMemo2(() => {
|
|
799
1207
|
const groups = [[], [], []];
|
|
800
1208
|
for (const t of PDF_TOOLS) {
|
|
801
|
-
if (t.status !== "ready") continue;
|
|
1209
|
+
if (t.status !== "ready" || t.id === "markup" && !canMarkup) continue;
|
|
802
1210
|
groups[t.group].push({ id: t.id, label: t.label, icon: PDF_TOOL_ICONS[t.id], ...t.key ? { shortcut: t.key.toUpperCase() } : null });
|
|
803
1211
|
}
|
|
804
1212
|
for (const t of hostTools) groups[t.group ?? 1].push({ id: `host:${t.id}`, label: t.label, icon: t.icon });
|
|
805
1213
|
groups[2].push({ id: ALL_TOOLS, label: "All tools", icon: MoreHorizontalIcon });
|
|
806
1214
|
return groups.filter((g) => g.length);
|
|
807
|
-
}, [hostTools]);
|
|
1215
|
+
}, [hostTools, canMarkup]);
|
|
808
1216
|
const runAction = useCallback(
|
|
809
1217
|
(action) => {
|
|
810
1218
|
if (action.startsWith("tool:")) return chooseTool(action.slice(5));
|
|
811
1219
|
switch (action) {
|
|
812
1220
|
case "undo":
|
|
813
|
-
return
|
|
1221
|
+
return undoAll();
|
|
814
1222
|
case "redo":
|
|
815
|
-
return
|
|
1223
|
+
return redoAll();
|
|
816
1224
|
case "save":
|
|
817
|
-
return apply();
|
|
1225
|
+
return markupPending ? void saveMarkup() : apply();
|
|
818
1226
|
case "find":
|
|
819
1227
|
setFindOpen(true);
|
|
820
1228
|
setFindFocus((n) => n + 1);
|
|
@@ -859,7 +1267,7 @@ function PdfWorkspace({ host, className }) {
|
|
|
859
1267
|
return void 0;
|
|
860
1268
|
}
|
|
861
1269
|
},
|
|
862
|
-
[chooseTool, apply, host.print, stepZoom, goToPage, currentPage, pageCount, turn, del, decisions.order]
|
|
1270
|
+
[chooseTool, apply, host.print, stepZoom, goToPage, currentPage, pageCount, turn, del, decisions.order, undoAll, redoAll, markupPending, saveMarkup]
|
|
863
1271
|
);
|
|
864
1272
|
const onKeyDown = (e) => {
|
|
865
1273
|
const t = e.target;
|
|
@@ -871,19 +1279,30 @@ function PdfWorkspace({ host, className }) {
|
|
|
871
1279
|
else if (sheet) setSheet(null);
|
|
872
1280
|
return;
|
|
873
1281
|
}
|
|
1282
|
+
if (markupOn) {
|
|
1283
|
+
const pick = matchMarkupKey(e.nativeEvent, typing);
|
|
1284
|
+
if (pick && pdfMarkupTool(pick)?.status === "ready" && (pick !== "signature" || host.signaturePad)) {
|
|
1285
|
+
e.preventDefault();
|
|
1286
|
+
setMarkupTool(pick);
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
874
1290
|
const action = matchShortcut(e.nativeEvent, typing);
|
|
875
1291
|
if (!action) return;
|
|
1292
|
+
if (markupOn && t.closest("[data-pdf-thumbnails]") === null && (action === "delete-pages" || action === "page-next" || action === "page-prev" || action === "select-all-pages")) return;
|
|
876
1293
|
if ((action === "page-next" || action === "page-prev") && t !== scrollerRef.current && t !== rootRef.current) return;
|
|
877
1294
|
if (action === "delete-pages" && t.closest("[data-pdf-thumbnails]") === null && t !== scrollerRef.current) return;
|
|
878
1295
|
e.preventDefault();
|
|
1296
|
+
if (action === "undo" || action === "redo") e.stopPropagation();
|
|
879
1297
|
runAction(action);
|
|
880
1298
|
};
|
|
881
|
-
const
|
|
882
|
-
const
|
|
1299
|
+
const unsaved = dirty || markupPending > 0;
|
|
1300
|
+
const saveState = !online ? "Offline" : busy || markupBusy ? "Saving" : error && dirty || markupError && markupPending ? "Not saved" : unsaved ? "Unsaved" : "Saved";
|
|
1301
|
+
const savePill = /* @__PURE__ */ jsx4(
|
|
883
1302
|
"span",
|
|
884
1303
|
{
|
|
885
1304
|
role: "status",
|
|
886
|
-
className:
|
|
1305
|
+
className: cn2(
|
|
887
1306
|
"inline-flex h-6 shrink-0 items-center justify-center whitespace-nowrap rounded-full border px-2 text-xs",
|
|
888
1307
|
saveState === "Unsaved" || saveState === "Offline" || saveState === "Not saved" ? "border-border text-foreground" : "border-border text-muted-foreground",
|
|
889
1308
|
busy && "animate-pulse"
|
|
@@ -891,13 +1310,13 @@ function PdfWorkspace({ host, className }) {
|
|
|
891
1310
|
children: saveState
|
|
892
1311
|
}
|
|
893
1312
|
);
|
|
894
|
-
const iconBtn = (label, icon, onClick, opts) => /* @__PURE__ */
|
|
1313
|
+
const iconBtn = (label, icon, onClick, opts) => /* @__PURE__ */ jsx4(Button3, { variant: "quiet", icon, "aria-label": label, title: label, onClick, ...opts?.disabled !== void 0 ? { disabled: opts.disabled } : null, ...opts?.pressed !== void 0 ? { pressed: opts.pressed } : null });
|
|
895
1314
|
const toolHits = toolQuery.trim() ? [
|
|
896
1315
|
...hostTools.map((t) => ({ id: `host:${t.id}`, label: t.label, status: "ready" })),
|
|
897
1316
|
...PDF_TOOLS
|
|
898
1317
|
].filter((t) => t.label.toLowerCase().includes(toolQuery.trim().toLowerCase())) : [];
|
|
899
|
-
const nameNode = editingName && host.rename ? /* @__PURE__ */
|
|
900
|
-
|
|
1318
|
+
const nameNode = editingName && host.rename ? /* @__PURE__ */ jsx4(
|
|
1319
|
+
Field3,
|
|
901
1320
|
{
|
|
902
1321
|
autoFocus: true,
|
|
903
1322
|
value: nameDraft,
|
|
@@ -916,7 +1335,7 @@ function PdfWorkspace({ host, className }) {
|
|
|
916
1335
|
},
|
|
917
1336
|
width: "lg"
|
|
918
1337
|
}
|
|
919
|
-
) : /* @__PURE__ */
|
|
1338
|
+
) : /* @__PURE__ */ jsx4(
|
|
920
1339
|
"button",
|
|
921
1340
|
{
|
|
922
1341
|
type: "button",
|
|
@@ -926,18 +1345,36 @@ function PdfWorkspace({ host, className }) {
|
|
|
926
1345
|
children: host.file.name
|
|
927
1346
|
}
|
|
928
1347
|
);
|
|
929
|
-
const rightPanel = hostTool ? /* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */
|
|
932
|
-
hostTool.hint ? /* @__PURE__ */
|
|
1348
|
+
const rightPanel = hostTool ? /* @__PURE__ */ jsxs4("div", { className: "flex h-full min-h-0 flex-col", "data-pdf-host-tool": hostTool.id, children: [
|
|
1349
|
+
/* @__PURE__ */ jsxs4("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
|
|
1350
|
+
/* @__PURE__ */ jsx4("div", { className: "text-sm font-medium", children: hostTool.label }),
|
|
1351
|
+
hostTool.hint ? /* @__PURE__ */ jsx4("div", { className: "text-xs text-muted-foreground", children: hostTool.hint }) : null
|
|
933
1352
|
] }),
|
|
934
|
-
/* @__PURE__ */
|
|
935
|
-
] }) : /* @__PURE__ */
|
|
936
|
-
/* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
toolDef.hint ? /* @__PURE__ */
|
|
1353
|
+
/* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1 overflow-hidden", children: hostTool.render({ currentPage, pageCount, selected: selectedPages, goToPage, reload: () => setReloadKey((k) => k + 1) }) })
|
|
1354
|
+
] }) : /* @__PURE__ */ jsxs4("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
1355
|
+
/* @__PURE__ */ jsxs4("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
|
|
1356
|
+
/* @__PURE__ */ jsx4("div", { className: "text-sm font-medium", children: toolDef.label }),
|
|
1357
|
+
toolDef.hint ? /* @__PURE__ */ jsx4("div", { className: "text-xs text-muted-foreground", children: toolDef.hint }) : null
|
|
939
1358
|
] }),
|
|
940
|
-
/* @__PURE__ */
|
|
1359
|
+
/* @__PURE__ */ jsx4("div", { className: "min-h-0 flex-1 overflow-y-auto p-3", children: tool === "markup" ? /* @__PURE__ */ jsx4(
|
|
1360
|
+
MarkupPanel,
|
|
1361
|
+
{
|
|
1362
|
+
host,
|
|
1363
|
+
engine,
|
|
1364
|
+
snapshot: snap,
|
|
1365
|
+
active: markupTool,
|
|
1366
|
+
onPick: setMarkupTool,
|
|
1367
|
+
blocked: dirty ? "Apply or discard the page changes first." : null,
|
|
1368
|
+
busy: markupBusy,
|
|
1369
|
+
online,
|
|
1370
|
+
error: markupError,
|
|
1371
|
+
onSave: () => void saveMarkup(),
|
|
1372
|
+
onDiscard: discardMarkup,
|
|
1373
|
+
onSoon: (id) => void announceSoon(id).catch(fail),
|
|
1374
|
+
goToPage,
|
|
1375
|
+
reloadKey
|
|
1376
|
+
}
|
|
1377
|
+
) : /* @__PURE__ */ jsx4(
|
|
941
1378
|
ToolPanel,
|
|
942
1379
|
{
|
|
943
1380
|
tool: toolDef,
|
|
@@ -961,7 +1398,7 @@ function PdfWorkspace({ host, className }) {
|
|
|
961
1398
|
}
|
|
962
1399
|
) })
|
|
963
1400
|
] });
|
|
964
|
-
const leftPanel = /* @__PURE__ */
|
|
1401
|
+
const leftPanel = /* @__PURE__ */ jsx4(
|
|
965
1402
|
LeftPanel,
|
|
966
1403
|
{
|
|
967
1404
|
host,
|
|
@@ -981,12 +1418,12 @@ function PdfWorkspace({ host, className }) {
|
|
|
981
1418
|
);
|
|
982
1419
|
const viewerPage = hostTool?.followsPage ? currentPage : 1;
|
|
983
1420
|
const viewer = useMemo2(
|
|
984
|
-
() => /* @__PURE__ */
|
|
985
|
-
[host, reloadKey, zoom, spread, decisions.turns, viewerPage, onDocumentLoad, onPageSize]
|
|
1421
|
+
() => /* @__PURE__ */ jsx4(Fragment3, { children: host.renderViewer({ zoom, spread, rotations: decisions.turns, reloadKey, currentPage: viewerPage, onDocumentLoad, onPageSize, ...canMarkup ? { onMarkupEngine } : null }) }, reloadKey),
|
|
1422
|
+
[host, reloadKey, zoom, spread, decisions.turns, viewerPage, onDocumentLoad, onPageSize, canMarkup, onMarkupEngine]
|
|
986
1423
|
);
|
|
987
1424
|
const showChrome = !read;
|
|
988
1425
|
const popCard = "absolute right-2 top-11 z-30 rounded-lg border border-border bg-popover p-3 text-popover-foreground shadow-lg";
|
|
989
|
-
return /* @__PURE__ */
|
|
1426
|
+
return /* @__PURE__ */ jsxs4(
|
|
990
1427
|
"div",
|
|
991
1428
|
{
|
|
992
1429
|
ref: rootRef,
|
|
@@ -994,18 +1431,18 @@ function PdfWorkspace({ host, className }) {
|
|
|
994
1431
|
"data-pdf-workspace": "",
|
|
995
1432
|
"data-compact": compact ? "" : void 0,
|
|
996
1433
|
onKeyDown,
|
|
997
|
-
className:
|
|
1434
|
+
className: cn2("relative flex h-full min-h-0 w-full flex-col overflow-hidden bg-background text-foreground outline-none", className),
|
|
998
1435
|
children: [
|
|
999
|
-
showChrome ? /* @__PURE__ */
|
|
1000
|
-
iconBtn("Page thumbnails", /* @__PURE__ */
|
|
1436
|
+
showChrome ? /* @__PURE__ */ jsxs4("div", { className: BAR, "data-pdf-topbar": "", children: [
|
|
1437
|
+
iconBtn("Page thumbnails", /* @__PURE__ */ jsx4(PanelLeftIcon, {}), () => compact ? setSheet("pages") : setLeftOpen((v) => !v), { pressed: !compact && leftOpen }),
|
|
1001
1438
|
nameNode,
|
|
1002
1439
|
savePill,
|
|
1003
|
-
/* @__PURE__ */
|
|
1004
|
-
!compact ? /* @__PURE__ */
|
|
1005
|
-
/* @__PURE__ */
|
|
1006
|
-
|
|
1440
|
+
/* @__PURE__ */ jsx4("div", { className: "flex-1" }),
|
|
1441
|
+
!compact ? /* @__PURE__ */ jsxs4("div", { className: "relative w-56", children: [
|
|
1442
|
+
/* @__PURE__ */ jsx4(
|
|
1443
|
+
Field3,
|
|
1007
1444
|
{
|
|
1008
|
-
icon: /* @__PURE__ */
|
|
1445
|
+
icon: /* @__PURE__ */ jsx4(SearchIcon, {}),
|
|
1009
1446
|
value: toolQuery,
|
|
1010
1447
|
"aria-label": "Search tools",
|
|
1011
1448
|
placeholder: "Search tools",
|
|
@@ -1023,7 +1460,7 @@ function PdfWorkspace({ host, className }) {
|
|
|
1023
1460
|
}
|
|
1024
1461
|
}
|
|
1025
1462
|
),
|
|
1026
|
-
pop === "tool-search" && toolHits.length ? /* @__PURE__ */
|
|
1463
|
+
pop === "tool-search" && toolHits.length ? /* @__PURE__ */ jsx4("ul", { role: "listbox", className: "absolute left-0 right-0 top-9 z-30 rounded-lg border border-border bg-popover p-1 shadow-lg", children: toolHits.map((t) => /* @__PURE__ */ jsx4("li", { role: "option", "aria-selected": false, children: /* @__PURE__ */ jsxs4(
|
|
1027
1464
|
"button",
|
|
1028
1465
|
{
|
|
1029
1466
|
type: "button",
|
|
@@ -1035,25 +1472,25 @@ function PdfWorkspace({ host, className }) {
|
|
|
1035
1472
|
setPop(null);
|
|
1036
1473
|
},
|
|
1037
1474
|
children: [
|
|
1038
|
-
/* @__PURE__ */
|
|
1039
|
-
t.status === "soon" ? /* @__PURE__ */
|
|
1475
|
+
/* @__PURE__ */ jsx4("span", { children: t.label }),
|
|
1476
|
+
t.status === "soon" ? /* @__PURE__ */ jsx4("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
|
|
1040
1477
|
]
|
|
1041
1478
|
}
|
|
1042
1479
|
) }, t.id)) }) : null
|
|
1043
1480
|
] }) : null,
|
|
1044
|
-
iconBtn(
|
|
1045
|
-
iconBtn(
|
|
1046
|
-
!compact && iconBtn("Find", /* @__PURE__ */
|
|
1047
|
-
host.share && !compact ? iconBtn("Share", /* @__PURE__ */
|
|
1048
|
-
iconBtn("Download", /* @__PURE__ */
|
|
1049
|
-
host.print && !compact ? iconBtn("Print", /* @__PURE__ */
|
|
1050
|
-
!compact ? iconBtn("Keyboard shortcuts", /* @__PURE__ */
|
|
1051
|
-
compact ? /* @__PURE__ */
|
|
1481
|
+
iconBtn(undoText, /* @__PURE__ */ jsx4(Undo2Icon, {}), () => runAction("undo"), { disabled: !canUndoAll }),
|
|
1482
|
+
iconBtn(redoText, /* @__PURE__ */ jsx4(Undo2Icon, { className: "-scale-x-100" }), () => runAction("redo"), { disabled: !canRedoAll }),
|
|
1483
|
+
!compact && iconBtn("Find", /* @__PURE__ */ jsx4(SearchIcon, {}), () => runAction("find"), { pressed: findOpen }),
|
|
1484
|
+
host.share && !compact ? iconBtn("Share", /* @__PURE__ */ jsx4(Share2Icon, {}), () => host.share()) : null,
|
|
1485
|
+
iconBtn("Download", /* @__PURE__ */ jsx4(DownloadIcon, {}), () => host.download()),
|
|
1486
|
+
host.print && !compact ? iconBtn("Print", /* @__PURE__ */ jsx4(PrinterIcon, {}), () => setPop(pop === "print" ? null : "print"), { pressed: pop === "print" }) : null,
|
|
1487
|
+
!compact ? iconBtn("Keyboard shortcuts", /* @__PURE__ */ jsx4(KeyboardIcon, {}), () => runAction("shortcuts"), { pressed: pop === "shortcuts" }) : null,
|
|
1488
|
+
compact ? /* @__PURE__ */ jsx4(Button3, { variant: "outline", onClick: () => setSheet("tools"), children: "Tools" }) : iconBtn("Properties panel", /* @__PURE__ */ jsx4(PanelRightIcon, {}), () => setRightOpen((v) => !v), { pressed: rightOpen })
|
|
1052
1489
|
] }) : null,
|
|
1053
|
-
showChrome ? /* @__PURE__ */
|
|
1054
|
-
iconBtn("Previous page", /* @__PURE__ */
|
|
1055
|
-
/* @__PURE__ */
|
|
1056
|
-
|
|
1490
|
+
showChrome ? /* @__PURE__ */ jsxs4("div", { className: BAR, "data-pdf-viewbar": "", children: [
|
|
1491
|
+
iconBtn("Previous page", /* @__PURE__ */ jsx4(ChevronLeftIcon, {}), () => goToPage(currentPage - 1), { disabled: currentPage <= 1 }),
|
|
1492
|
+
/* @__PURE__ */ jsx4(
|
|
1493
|
+
Field3,
|
|
1057
1494
|
{
|
|
1058
1495
|
type: "number",
|
|
1059
1496
|
min: 1,
|
|
@@ -1066,16 +1503,16 @@ function PdfWorkspace({ host, className }) {
|
|
|
1066
1503
|
className: compact ? "w-12" : "w-16"
|
|
1067
1504
|
}
|
|
1068
1505
|
),
|
|
1069
|
-
/* @__PURE__ */
|
|
1506
|
+
/* @__PURE__ */ jsxs4("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: [
|
|
1070
1507
|
"of ",
|
|
1071
1508
|
pageCount || "-"
|
|
1072
1509
|
] }),
|
|
1073
|
-
iconBtn("Next page", /* @__PURE__ */
|
|
1074
|
-
/* @__PURE__ */
|
|
1075
|
-
iconBtn("Zoom out", /* @__PURE__ */
|
|
1076
|
-
/* @__PURE__ */
|
|
1077
|
-
iconBtn("Zoom in", /* @__PURE__ */
|
|
1078
|
-
!compact ? /* @__PURE__ */
|
|
1510
|
+
iconBtn("Next page", /* @__PURE__ */ jsx4(ChevronRightIcon, {}), () => goToPage(currentPage + 1), { disabled: currentPage >= pageCount }),
|
|
1511
|
+
/* @__PURE__ */ jsx4("div", { className: "mx-1 h-5 w-px bg-border" }),
|
|
1512
|
+
iconBtn("Zoom out", /* @__PURE__ */ jsx4(MinusIcon, {}), () => stepZoom(-1)),
|
|
1513
|
+
/* @__PURE__ */ jsx4("span", { className: cn2("min-w-[3rem] text-center text-xs tabular-nums", compact && "hidden"), "data-pdf-zoom": "", children: `${Math.round(zoom * 100)}%` }),
|
|
1514
|
+
iconBtn("Zoom in", /* @__PURE__ */ jsx4(PlusIcon, {}), () => stepZoom(1)),
|
|
1515
|
+
!compact ? /* @__PURE__ */ jsx4(
|
|
1079
1516
|
SegmentedControl3,
|
|
1080
1517
|
{
|
|
1081
1518
|
value: zoomMode === "custom" ? null : zoomMode,
|
|
@@ -1084,34 +1521,35 @@ function PdfWorkspace({ host, className }) {
|
|
|
1084
1521
|
"aria-label": "Fit"
|
|
1085
1522
|
}
|
|
1086
1523
|
) : null,
|
|
1087
|
-
/* @__PURE__ */
|
|
1088
|
-
!compact ? /* @__PURE__ */
|
|
1089
|
-
iconBtn("Rotate left", /* @__PURE__ */
|
|
1090
|
-
iconBtn("Rotate right", /* @__PURE__ */
|
|
1091
|
-
iconBtn("Delete page", /* @__PURE__ */
|
|
1092
|
-
/* @__PURE__ */
|
|
1093
|
-
iconBtn("Two pages side by side", /* @__PURE__ */
|
|
1524
|
+
/* @__PURE__ */ jsx4("div", { className: "flex-1" }),
|
|
1525
|
+
!compact ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1526
|
+
iconBtn("Rotate left", /* @__PURE__ */ jsx4(RotateCcwIcon, {}), () => turn(-90)),
|
|
1527
|
+
iconBtn("Rotate right", /* @__PURE__ */ jsx4(RotateCwIcon, {}), () => turn(90)),
|
|
1528
|
+
iconBtn("Delete page", /* @__PURE__ */ jsx4(Trash2Icon2, {}), del, { disabled: decisions.order.length - decisions.deleted.length <= 1 }),
|
|
1529
|
+
/* @__PURE__ */ jsx4("div", { className: "mx-1 h-5 w-px bg-border" }),
|
|
1530
|
+
iconBtn("Two pages side by side", /* @__PURE__ */ jsx4(Columns2Icon2, {}), () => runAction("two-up"), { pressed: spread === "two-up" })
|
|
1094
1531
|
] }) : null,
|
|
1095
|
-
iconBtn("Reading mode", /* @__PURE__ */
|
|
1532
|
+
iconBtn("Reading mode", /* @__PURE__ */ jsx4(ExpandIcon, {}), () => setRead(true))
|
|
1096
1533
|
] }) : null,
|
|
1097
|
-
/* @__PURE__ */
|
|
1098
|
-
showChrome && !compact ? /* @__PURE__ */
|
|
1099
|
-
showChrome && !compact && leftOpen ? /* @__PURE__ */
|
|
1100
|
-
/* @__PURE__ */
|
|
1101
|
-
findOpen ? /* @__PURE__ */
|
|
1102
|
-
/* @__PURE__ */
|
|
1103
|
-
|
|
1534
|
+
/* @__PURE__ */ jsxs4("div", { className: "relative flex min-h-0 flex-1", children: [
|
|
1535
|
+
showChrome && !compact ? /* @__PURE__ */ jsx4("div", { className: "flex w-10 shrink-0 flex-col items-center border-r border-border bg-background py-2", "data-pdf-rail": "", children: /* @__PURE__ */ jsx4(ToolRail, { groups: railGroups, value: pop === "all-tools" ? ALL_TOOLS : activeToolKey, onValueChange: (id) => id === ALL_TOOLS ? setPop((p) => p === "all-tools" ? null : "all-tools") : id && chooseTool(id), "aria-label": "PDF tools" }) }) : null,
|
|
1536
|
+
showChrome && !compact && leftOpen ? /* @__PURE__ */ jsx4("aside", { className: "w-56 shrink-0 border-r border-border bg-background", "aria-label": "Pages", "data-pdf-left": "", children: leftPanel }) : null,
|
|
1537
|
+
/* @__PURE__ */ jsxs4("main", { className: "relative flex min-w-0 flex-1 flex-col", children: [
|
|
1538
|
+
findOpen ? /* @__PURE__ */ jsx4("div", { className: "absolute right-4 top-2 z-20", children: /* @__PURE__ */ jsx4(RenderedFindBar, { rootRef: scrollerRef, onClose: () => setFindOpen(false), label: "Find in document", focusRequest: findFocus, compact: true }) }) : null,
|
|
1539
|
+
/* @__PURE__ */ jsx4("div", { ref: scrollerRef, tabIndex: 0, "data-pdf-scroller": "", className: "min-h-0 flex-1 overflow-auto bg-muted outline-none", children: viewer }),
|
|
1540
|
+
/* @__PURE__ */ jsx4(MarkupFloatingToolbar, { engine, snapshot: snap, enabled: markupOn && !read, onDone: () => scrollerRef.current?.focus({ preventScroll: true }) }),
|
|
1541
|
+
read ? /* @__PURE__ */ jsx4("div", { className: "absolute right-3 top-3 z-20", children: /* @__PURE__ */ jsx4(Button3, { variant: "outline", icon: /* @__PURE__ */ jsx4(XIcon, {}), onClick: () => setRead(false), children: "Exit reading mode" }) }) : null
|
|
1104
1542
|
] }),
|
|
1105
|
-
showChrome && !compact && rightOpen ? /* @__PURE__ */
|
|
1106
|
-
pop === "all-tools" ? /* @__PURE__ */
|
|
1543
|
+
showChrome && !compact && rightOpen ? /* @__PURE__ */ jsx4("aside", { className: cn2("shrink-0 border-l border-border bg-background", hostTool?.wide ? "w-[28rem]" : "w-72"), "aria-label": "Properties", "data-pdf-right": "", children: rightPanel }) : null,
|
|
1544
|
+
pop === "all-tools" ? /* @__PURE__ */ jsx4("ul", { role: "menu", "aria-label": "All tools", className: "absolute left-12 top-2 z-30 w-72 rounded-lg border border-border bg-popover p-1 shadow-lg", "data-pdf-all-tools": "", children: /* @__PURE__ */ jsx4(ToolRows, { tool: activeToolKey, hostTools, canMarkup, onPick: (id) => {
|
|
1107
1545
|
setPop(null);
|
|
1108
1546
|
chooseTool(id);
|
|
1109
1547
|
} }) }) : null,
|
|
1110
|
-
pop === "print" && host.print ? /* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
printMode === "pages" ? /* @__PURE__ */
|
|
1113
|
-
/* @__PURE__ */
|
|
1114
|
-
|
|
1548
|
+
pop === "print" && host.print ? /* @__PURE__ */ jsx4("div", { className: cn2(popCard, "w-72"), role: "dialog", "aria-label": "Print", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-3", children: [
|
|
1549
|
+
/* @__PURE__ */ jsx4(SegmentedControl3, { fill: true, value: printMode, onValueChange: setPrintMode, data: [{ value: "all", label: "All pages" }, { value: "pages", label: "Pages" }], "aria-label": "What to print" }),
|
|
1550
|
+
printMode === "pages" ? /* @__PURE__ */ jsx4(PageRangeInput2, { value: printRange, onChange: (t) => setPrintRange(t), pageCount }) : null,
|
|
1551
|
+
/* @__PURE__ */ jsx4(
|
|
1552
|
+
Button3,
|
|
1115
1553
|
{
|
|
1116
1554
|
variant: "primary",
|
|
1117
1555
|
disabled: busy || printMode === "pages" && !parsePageRanges2(printRange, pageCount).valid,
|
|
@@ -1124,18 +1562,27 @@ function PdfWorkspace({ host, className }) {
|
|
|
1124
1562
|
}
|
|
1125
1563
|
)
|
|
1126
1564
|
] }) }) : null,
|
|
1127
|
-
pop === "shortcuts" ? /* @__PURE__ */
|
|
1128
|
-
/* @__PURE__ */
|
|
1129
|
-
|
|
1130
|
-
/* @__PURE__ */
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
/* @__PURE__ */
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1565
|
+
pop === "shortcuts" ? /* @__PURE__ */ jsxs4("div", { className: cn2(popCard, "max-h-[70%] w-80 overflow-y-auto"), role: "dialog", "aria-label": "Keyboard shortcuts", "data-pdf-shortcuts": "", children: [
|
|
1566
|
+
["Edit", "View", "Pages", "Tools"].map((section) => /* @__PURE__ */ jsxs4("div", { className: "mb-3 last:mb-0", children: [
|
|
1567
|
+
/* @__PURE__ */ jsx4("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: section }),
|
|
1568
|
+
PDF_SHORTCUTS.filter((s) => s.section === section).map((s) => /* @__PURE__ */ jsxs4("div", { className: "flex justify-between gap-2 py-0.5 text-xs", children: [
|
|
1569
|
+
/* @__PURE__ */ jsx4("span", { children: s.label }),
|
|
1570
|
+
/* @__PURE__ */ jsx4("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: formatKeys(s.keys, isMac()) })
|
|
1571
|
+
] }, s.action))
|
|
1572
|
+
] }, section)),
|
|
1573
|
+
canMarkup ? /* @__PURE__ */ jsxs4("div", { className: "mb-0", children: [
|
|
1574
|
+
/* @__PURE__ */ jsx4("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Markup" }),
|
|
1575
|
+
PDF_MARKUP_SHORTCUTS.filter((s) => s.tool !== "signature" || host.signaturePad).map((s) => /* @__PURE__ */ jsxs4("div", { className: "flex justify-between gap-2 py-0.5 text-xs", children: [
|
|
1576
|
+
/* @__PURE__ */ jsx4("span", { children: s.label }),
|
|
1577
|
+
/* @__PURE__ */ jsx4("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: s.keys })
|
|
1578
|
+
] }, s.tool))
|
|
1579
|
+
] }) : null
|
|
1580
|
+
] }) : null,
|
|
1581
|
+
compact && sheet ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1582
|
+
/* @__PURE__ */ jsx4("button", { type: "button", "aria-label": "Close", className: "absolute inset-0 z-30 bg-background/60", onClick: () => setSheet(null) }),
|
|
1583
|
+
/* @__PURE__ */ jsx4("div", { className: "absolute inset-x-0 bottom-0 z-40 flex max-h-[75%] flex-col rounded-t-xl border-t border-border bg-background", role: "dialog", "aria-label": sheet === "pages" ? "Pages" : "Tools", children: sheet === "pages" ? /* @__PURE__ */ jsx4("div", { className: "h-[60vh] min-h-0", children: leftPanel }) : /* @__PURE__ */ jsxs4("div", { className: "flex min-h-0 flex-col overflow-y-auto", children: [
|
|
1584
|
+
/* @__PURE__ */ jsx4("ul", { className: "p-2", role: "menu", "aria-label": "Tools", children: /* @__PURE__ */ jsx4(ToolRows, { tool: activeToolKey, hostTools, canMarkup, onPick: chooseTool }) }),
|
|
1585
|
+
/* @__PURE__ */ jsx4("div", { className: "border-t border-border", children: rightPanel })
|
|
1139
1586
|
] }) })
|
|
1140
1587
|
] }) : null
|
|
1141
1588
|
] })
|
|
@@ -1145,27 +1592,47 @@ function PdfWorkspace({ host, className }) {
|
|
|
1145
1592
|
}
|
|
1146
1593
|
export {
|
|
1147
1594
|
HISTORY_LIMIT,
|
|
1595
|
+
MARKUP_KIND_LABEL,
|
|
1596
|
+
MARKUP_SIZES,
|
|
1597
|
+
MARKUP_STYLE_FIELDS,
|
|
1598
|
+
MARKUP_SWATCHES,
|
|
1148
1599
|
PDF_COMING_SOON,
|
|
1600
|
+
PDF_MARKUP_COMING_SOON,
|
|
1601
|
+
PDF_MARKUP_SHORTCUTS,
|
|
1602
|
+
PDF_MARKUP_TOOLS,
|
|
1149
1603
|
PDF_SHORTCUTS,
|
|
1150
1604
|
PDF_TOOLS,
|
|
1151
1605
|
PdfWorkspace,
|
|
1606
|
+
TIMELINE_LIMIT,
|
|
1152
1607
|
canRedo,
|
|
1153
1608
|
canUndo,
|
|
1154
1609
|
comingSoonIdOf,
|
|
1155
1610
|
compilePageOps,
|
|
1156
1611
|
deletePages,
|
|
1612
|
+
dropOrigin,
|
|
1613
|
+
emptyTimeline,
|
|
1157
1614
|
formatKeys,
|
|
1158
1615
|
freshDecisions,
|
|
1159
1616
|
isDirty,
|
|
1160
1617
|
keptOrder,
|
|
1618
|
+
markupComingSoonId,
|
|
1619
|
+
markupRedoneElsewhere,
|
|
1620
|
+
markupUndoneElsewhere,
|
|
1621
|
+
matchMarkupKey,
|
|
1161
1622
|
matchShortcut,
|
|
1623
|
+
nextRedo,
|
|
1624
|
+
nextUndo,
|
|
1625
|
+
pdfMarkupTool,
|
|
1162
1626
|
pdfTool,
|
|
1163
1627
|
pushStep,
|
|
1628
|
+
recordStep,
|
|
1164
1629
|
redo,
|
|
1165
1630
|
redoLabel,
|
|
1166
1631
|
reorderPages,
|
|
1167
1632
|
restorePages,
|
|
1168
1633
|
startHistory,
|
|
1634
|
+
stepBack,
|
|
1635
|
+
stepForward,
|
|
1169
1636
|
turnPages,
|
|
1170
1637
|
undo,
|
|
1171
1638
|
undoLabel
|