@ai-matrx/office 0.8.50 → 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/dist/pdf.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
 
3
3
  // src/pdf/PdfWorkspace.tsx
4
- import { Fragment as Fragment2, useCallback, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
5
- import { cn } from "@ai-matrx/design-system/cn";
6
- import { Button as Button2, Field as Field2, PageRangeInput as PageRangeInput2, SegmentedControl as SegmentedControl3, ToolRail, parsePageRanges as parsePageRanges2 } from "@ai-matrx/design-system/controls";
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,
@@ -391,9 +391,94 @@ var canRedo = (h) => h.future.length > 0;
391
391
  var undoLabel = (h) => h.present.label;
392
392
  var redoLabel = (h) => h.future[0]?.label ?? "";
393
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
+
394
478
  // src/pdf/tools.ts
395
479
  var PDF_TOOLS = [
396
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." },
397
482
  { id: "organize", label: "Organize pages", key: "o", status: "ready", capability: "rotate-pages,delete-pages,reorder-pages", opens: "panel", group: 0 },
398
483
  { id: "extract-pages", label: "Extract pages", key: "e", status: "ready", capability: "extract-pages", opens: "panel", group: 0 },
399
484
  { id: "split", label: "Split", key: "s", status: "ready", capability: "split", opens: "panel", group: 0 },
@@ -414,7 +499,7 @@ var PDF_TOOLS = [
414
499
  var pdfTool = (id) => PDF_TOOLS.find((t) => t.id === id);
415
500
  var comingSoonIdOf = (id) => `pdf.${id}`;
416
501
  var PROMISES = {
417
- edit: "Change text and swap images right on the page.",
502
+ edit: "Change the page's own text and swap images, with a before-and-after view.",
418
503
  forms: "Fill in a form and keep the values in the file.",
419
504
  protect: "Lock a PDF with a password and limit what others can do.",
420
505
  ocr: "Turn a scanned PDF into one you can search and copy from.",
@@ -439,14 +524,15 @@ var PDF_COMING_SOON = {
439
524
  return [id, { id, label: t.label, owner: "pdf", promise: PROMISES[t.id] ?? "", stage: "planned", surfaces: ["PDF workspace tool rail"] }];
440
525
  })
441
526
  ),
442
- ...PANEL_PROMISES
527
+ ...PANEL_PROMISES,
528
+ ...PDF_MARKUP_COMING_SOON
443
529
  };
444
530
 
445
531
  // src/pdf/shortcuts.ts
446
532
  var PDF_SHORTCUTS = [
447
533
  { action: "undo", label: "Undo", keys: "Mod+Z", section: "Edit" },
448
534
  { action: "redo", label: "Redo", keys: "Mod+Shift+Z", section: "Edit" },
449
- { action: "save", label: "Apply page changes", keys: "Mod+S", section: "Edit" },
535
+ { action: "save", label: "Save markup or apply page changes", keys: "Mod+S", section: "Edit" },
450
536
  { action: "find", label: "Find", keys: "Mod+F", section: "Edit" },
451
537
  { action: "print", label: "Print", keys: "Mod+P", section: "Edit" },
452
538
  { action: "zoom-in", label: "Zoom in", keys: "Mod+=", section: "View" },
@@ -473,6 +559,14 @@ var PDF_SHORTCUTS = [
473
559
  section: "Tools"
474
560
  }))
475
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
+ }
476
570
  function parse(keys) {
477
571
  const parts = keys.split("+");
478
572
  const key = parts[parts.length - 1] === "" ? "+" : parts[parts.length - 1];
@@ -499,6 +593,209 @@ function formatKeys(keys, mac) {
499
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 ? "" : "+");
500
594
  }
501
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
+
502
799
  // src/pdf/tool-icons.tsx
503
800
  import {
504
801
  ArrowLeftRightIcon,
@@ -513,24 +810,26 @@ import {
513
810
  LayoutGridIcon,
514
811
  ListChecksIcon,
515
812
  LockIcon,
516
- PencilIcon,
517
- PenToolIcon,
518
- ProportionsIcon,
813
+ MessageSquareIcon as MessageSquareIcon2,
814
+ PencilIcon as PencilIcon2,
815
+ PenToolIcon as PenToolIcon2,
816
+ ProportionsIcon as ProportionsIcon2,
519
817
  SplitIcon,
520
- TypeIcon
818
+ TypeIcon as TypeIcon2
521
819
  } from "@ai-matrx/design-system/icons";
522
820
  var PDF_TOOL_ICONS = {
523
821
  browse: HandIcon,
822
+ markup: MessageSquareIcon2,
524
823
  organize: LayoutGridIcon,
525
824
  "extract-pages": ArrowUpFromLineIcon,
526
825
  split: SplitIcon,
527
- crop: ProportionsIcon,
826
+ crop: ProportionsIcon2,
528
827
  redact: EraserIcon,
529
828
  compress: ChevronsDownUpIcon,
530
829
  scan: CameraIcon,
531
- sign: PenToolIcon,
532
- "extract-data": TypeIcon,
533
- edit: PencilIcon,
830
+ sign: PenToolIcon2,
831
+ "extract-data": TypeIcon2,
832
+ edit: PencilIcon2,
534
833
  forms: ListChecksIcon,
535
834
  protect: LockIcon,
536
835
  ocr: CaptionsIcon,
@@ -540,7 +839,7 @@ var PDF_TOOL_ICONS = {
540
839
  };
541
840
 
542
841
  // src/pdf/PdfWorkspace.tsx
543
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
842
+ import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
544
843
  var ZOOM_STEPS = [0.5, 0.75, 1, 1.25, 1.5, 2, 3];
545
844
  var COMPACT_BELOW_PX = 768;
546
845
  var BAR = "flex h-10 shrink-0 items-center gap-1 border-b border-border bg-background px-2";
@@ -552,7 +851,7 @@ async function announceSoon(id) {
552
851
  await announceComingSoon(id);
553
852
  }
554
853
  function useWidth(ref) {
555
- const [size, setSize] = useState3({ w: 1200, h: 800 });
854
+ const [size, setSize] = useState4({ w: 1200, h: 800 });
556
855
  useLayoutEffect(() => {
557
856
  const node = ref.current;
558
857
  if (!node) return void 0;
@@ -565,36 +864,36 @@ function useWidth(ref) {
565
864
  }, [ref]);
566
865
  return size;
567
866
  }
568
- function ToolRows({ tool, hostTools, onPick }) {
569
- return /* @__PURE__ */ jsxs3(Fragment3, { children: [
570
- hostTools.map((t) => /* @__PURE__ */ jsx3("li", { role: "none", children: /* @__PURE__ */ jsxs3(
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(
571
870
  "button",
572
871
  {
573
872
  type: "button",
574
873
  role: "menuitem",
575
874
  "aria-current": tool === `host:${t.id}` ? "true" : void 0,
576
- className: cn("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"),
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"),
577
876
  onClick: () => onPick(`host:${t.id}`),
578
877
  children: [
579
- /* @__PURE__ */ jsx3(t.icon, { className: "size-4 shrink-0" }),
580
- /* @__PURE__ */ jsx3("span", { className: "flex-1 whitespace-nowrap", children: t.label })
878
+ /* @__PURE__ */ jsx4(t.icon, { className: "size-4 shrink-0" }),
879
+ /* @__PURE__ */ jsx4("span", { className: "flex-1 whitespace-nowrap", children: t.label })
581
880
  ]
582
881
  }
583
882
  ) }, `host:${t.id}`)),
584
- PDF_TOOLS.filter((t) => t.id !== "browse").map((t) => {
883
+ PDF_TOOLS.filter((t) => t.id !== "browse" && (t.id !== "markup" || canMarkup)).map((t) => {
585
884
  const Icon = PDF_TOOL_ICONS[t.id];
586
- return /* @__PURE__ */ jsx3("li", { role: "none", children: /* @__PURE__ */ jsxs3(
885
+ return /* @__PURE__ */ jsx4("li", { role: "none", children: /* @__PURE__ */ jsxs4(
587
886
  "button",
588
887
  {
589
888
  type: "button",
590
889
  role: "menuitem",
591
890
  "aria-current": t.id === tool ? "true" : void 0,
592
- className: cn("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"),
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"),
593
892
  onClick: () => onPick(t.id),
594
893
  children: [
595
- /* @__PURE__ */ jsx3(Icon, { className: "size-4 shrink-0" }),
596
- /* @__PURE__ */ jsx3("span", { className: "flex-1 whitespace-nowrap", children: t.label }),
597
- t.status === "soon" ? /* @__PURE__ */ jsx3("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
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
598
897
  ]
599
898
  }
600
899
  ) }, t.id);
@@ -608,40 +907,48 @@ function PdfWorkspace({ host, className }) {
608
907
  const viewSize = useWidth(scrollerRef);
609
908
  const compact = rootSize.w < COMPACT_BELOW_PX;
610
909
  const online = host.online !== false;
611
- const [pageCount, setPageCount] = useState3(0);
612
- const [history, setHistory] = useState3(() => startHistory(freshDecisions(0)));
910
+ const [pageCount, setPageCount] = useState4(0);
911
+ const [history, setHistory] = useState4(() => startHistory(freshDecisions(0)));
613
912
  const decisions = history.present.value;
614
913
  const dirty = isDirty(decisions);
615
- const [tool, setTool] = useState3("browse");
616
- const [hostToolId, setHostToolId] = useState3(null);
914
+ const [tool, setTool] = useState4("browse");
915
+ const [hostToolId, setHostToolId] = useState4(null);
617
916
  const hostTools = useMemo2(() => host.tools ?? [], [host.tools]);
618
917
  const hostTool = hostToolId ? hostTools.find((t) => t.id === hostToolId) ?? null : null;
619
918
  const activeToolKey = hostTool ? `host:${hostTool.id}` : tool;
620
- const [leftOpen, setLeftOpen] = useState3(true);
621
- const [rightOpen, setRightOpen] = useState3(true);
622
- const [leftTab, setLeftTab] = useState3("pages");
623
- const [read, setRead] = useState3(false);
624
- const [spread, setSpread] = useState3("single");
625
- const [zoomMode, setZoomMode] = useState3("fit-width");
626
- const [customZoom, setCustomZoom] = useState3(1);
627
- const [selected, setSelected] = useState3([]);
628
- const [currentPage, setCurrentPage] = useState3(1);
629
- const [pageDraft, setPageDraft] = useState3("1");
630
- const [findOpen, setFindOpen] = useState3(false);
631
- const [findFocus, setFindFocus] = useState3(0);
632
- const [pop, setPop] = useState3(null);
633
- const [sheet, setSheet] = useState3(null);
634
- const [toolQuery, setToolQuery] = useState3("");
635
- const [busy, setBusy] = useState3(false);
636
- const [error, setError] = useState3(null);
637
- const [result, setResult] = useState3(null);
638
- const [reloadKey, setReloadKey] = useState3(0);
639
- const [sizeVersion, setSizeVersion] = useState3(0);
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);
640
939
  const sizes = useRef2(/* @__PURE__ */ new Map());
641
- const [editingName, setEditingName] = useState3(false);
642
- const [nameDraft, setNameDraft] = useState3(host.file.name);
643
- const [printMode, setPrintMode] = useState3("all");
644
- const [printRange, setPrintRange] = useState3("");
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;
645
952
  const selectedPages = useMemo2(() => selected.map(Number).sort((a, b) => a - b), [selected]);
646
953
  const toolDef = pdfTool(tool);
647
954
  const first = sizes.current.get(1);
@@ -674,7 +981,7 @@ function PdfWorkspace({ host, className }) {
674
981
  },
675
982
  [pageCount]
676
983
  );
677
- useEffect3(() => {
984
+ useEffect4(() => {
678
985
  const node = scrollerRef.current;
679
986
  if (!node) return void 0;
680
987
  let raf = 0;
@@ -701,7 +1008,10 @@ function PdfWorkspace({ host, className }) {
701
1008
  cancelAnimationFrame(raf);
702
1009
  };
703
1010
  }, [pageCount]);
704
- const commit = useCallback((label, next) => setHistory((h) => pushStep(h, label, next)), []);
1011
+ const commit = useCallback((label, next) => {
1012
+ setHistory((h) => pushStep(h, label, next));
1013
+ setTimeline((t) => recordStep(t, "page", label));
1014
+ }, []);
705
1015
  const targets = useCallback(() => selectedPages.length ? selectedPages : [currentPage], [selectedPages, currentPage]);
706
1016
  const turn = useCallback(
707
1017
  (by) => {
@@ -764,9 +1074,106 @@ function PdfWorkspace({ host, className }) {
764
1074
  );
765
1075
  const apply = useCallback(() => {
766
1076
  const ops = compilePageOps(decisions);
767
- if (ops.length) void runTool({ tool: "apply-pages", ops });
768
- }, [decisions, runTool]);
769
- const discard = useCallback(() => setHistory(startHistory(freshDecisions(pageCount))), [pageCount]);
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);
770
1177
  const chooseTool = useCallback(
771
1178
  (id) => {
772
1179
  if (id.startsWith("host:")) {
@@ -778,7 +1185,7 @@ function PdfWorkspace({ host, className }) {
778
1185
  return;
779
1186
  }
780
1187
  const def = pdfTool(id);
781
- if (!def) return;
1188
+ if (!def || def.id === "markup" && !host.saveMarkup) return;
782
1189
  setHostToolId(null);
783
1190
  if (def.status === "soon") {
784
1191
  announceSoon(comingSoonIdOf(def.id)).catch(fail);
@@ -799,23 +1206,23 @@ function PdfWorkspace({ host, className }) {
799
1206
  const railGroups = useMemo2(() => {
800
1207
  const groups = [[], [], []];
801
1208
  for (const t of PDF_TOOLS) {
802
- if (t.status !== "ready") continue;
1209
+ if (t.status !== "ready" || t.id === "markup" && !canMarkup) continue;
803
1210
  groups[t.group].push({ id: t.id, label: t.label, icon: PDF_TOOL_ICONS[t.id], ...t.key ? { shortcut: t.key.toUpperCase() } : null });
804
1211
  }
805
1212
  for (const t of hostTools) groups[t.group ?? 1].push({ id: `host:${t.id}`, label: t.label, icon: t.icon });
806
1213
  groups[2].push({ id: ALL_TOOLS, label: "All tools", icon: MoreHorizontalIcon });
807
1214
  return groups.filter((g) => g.length);
808
- }, [hostTools]);
1215
+ }, [hostTools, canMarkup]);
809
1216
  const runAction = useCallback(
810
1217
  (action) => {
811
1218
  if (action.startsWith("tool:")) return chooseTool(action.slice(5));
812
1219
  switch (action) {
813
1220
  case "undo":
814
- return setHistory((h) => undo(h));
1221
+ return undoAll();
815
1222
  case "redo":
816
- return setHistory((h) => redo(h));
1223
+ return redoAll();
817
1224
  case "save":
818
- return apply();
1225
+ return markupPending ? void saveMarkup() : apply();
819
1226
  case "find":
820
1227
  setFindOpen(true);
821
1228
  setFindFocus((n) => n + 1);
@@ -860,7 +1267,7 @@ function PdfWorkspace({ host, className }) {
860
1267
  return void 0;
861
1268
  }
862
1269
  },
863
- [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]
864
1271
  );
865
1272
  const onKeyDown = (e) => {
866
1273
  const t = e.target;
@@ -872,19 +1279,30 @@ function PdfWorkspace({ host, className }) {
872
1279
  else if (sheet) setSheet(null);
873
1280
  return;
874
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
+ }
875
1290
  const action = matchShortcut(e.nativeEvent, typing);
876
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;
877
1293
  if ((action === "page-next" || action === "page-prev") && t !== scrollerRef.current && t !== rootRef.current) return;
878
1294
  if (action === "delete-pages" && t.closest("[data-pdf-thumbnails]") === null && t !== scrollerRef.current) return;
879
1295
  e.preventDefault();
1296
+ if (action === "undo" || action === "redo") e.stopPropagation();
880
1297
  runAction(action);
881
1298
  };
882
- const saveState = !online ? "Offline" : busy ? "Saving" : error && dirty ? "Not saved" : dirty ? "Unsaved" : "Saved";
883
- const savePill = /* @__PURE__ */ jsx3(
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(
884
1302
  "span",
885
1303
  {
886
1304
  role: "status",
887
- className: cn(
1305
+ className: cn2(
888
1306
  "inline-flex h-6 shrink-0 items-center justify-center whitespace-nowrap rounded-full border px-2 text-xs",
889
1307
  saveState === "Unsaved" || saveState === "Offline" || saveState === "Not saved" ? "border-border text-foreground" : "border-border text-muted-foreground",
890
1308
  busy && "animate-pulse"
@@ -892,13 +1310,13 @@ function PdfWorkspace({ host, className }) {
892
1310
  children: saveState
893
1311
  }
894
1312
  );
895
- const iconBtn = (label, icon, onClick, opts) => /* @__PURE__ */ jsx3(Button2, { 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 });
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 });
896
1314
  const toolHits = toolQuery.trim() ? [
897
1315
  ...hostTools.map((t) => ({ id: `host:${t.id}`, label: t.label, status: "ready" })),
898
1316
  ...PDF_TOOLS
899
1317
  ].filter((t) => t.label.toLowerCase().includes(toolQuery.trim().toLowerCase())) : [];
900
- const nameNode = editingName && host.rename ? /* @__PURE__ */ jsx3(
901
- Field2,
1318
+ const nameNode = editingName && host.rename ? /* @__PURE__ */ jsx4(
1319
+ Field3,
902
1320
  {
903
1321
  autoFocus: true,
904
1322
  value: nameDraft,
@@ -917,7 +1335,7 @@ function PdfWorkspace({ host, className }) {
917
1335
  },
918
1336
  width: "lg"
919
1337
  }
920
- ) : /* @__PURE__ */ jsx3(
1338
+ ) : /* @__PURE__ */ jsx4(
921
1339
  "button",
922
1340
  {
923
1341
  type: "button",
@@ -927,18 +1345,36 @@ function PdfWorkspace({ host, className }) {
927
1345
  children: host.file.name
928
1346
  }
929
1347
  );
930
- const rightPanel = hostTool ? /* @__PURE__ */ jsxs3("div", { className: "flex h-full min-h-0 flex-col", "data-pdf-host-tool": hostTool.id, children: [
931
- /* @__PURE__ */ jsxs3("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
932
- /* @__PURE__ */ jsx3("div", { className: "text-sm font-medium", children: hostTool.label }),
933
- hostTool.hint ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: hostTool.hint }) : null
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
934
1352
  ] }),
935
- /* @__PURE__ */ jsx3("div", { className: "min-h-0 flex-1 overflow-hidden", children: hostTool.render({ currentPage, pageCount, selected: selectedPages, goToPage, reload: () => setReloadKey((k) => k + 1) }) })
936
- ] }) : /* @__PURE__ */ jsxs3("div", { className: "flex h-full min-h-0 flex-col", children: [
937
- /* @__PURE__ */ jsxs3("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
938
- /* @__PURE__ */ jsx3("div", { className: "text-sm font-medium", children: toolDef.label }),
939
- toolDef.hint ? /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: toolDef.hint }) : null
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
940
1358
  ] }),
941
- /* @__PURE__ */ jsx3("div", { className: "min-h-0 flex-1 overflow-y-auto p-3", children: /* @__PURE__ */ jsx3(
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(
942
1378
  ToolPanel,
943
1379
  {
944
1380
  tool: toolDef,
@@ -962,7 +1398,7 @@ function PdfWorkspace({ host, className }) {
962
1398
  }
963
1399
  ) })
964
1400
  ] });
965
- const leftPanel = /* @__PURE__ */ jsx3(
1401
+ const leftPanel = /* @__PURE__ */ jsx4(
966
1402
  LeftPanel,
967
1403
  {
968
1404
  host,
@@ -982,12 +1418,12 @@ function PdfWorkspace({ host, className }) {
982
1418
  );
983
1419
  const viewerPage = hostTool?.followsPage ? currentPage : 1;
984
1420
  const viewer = useMemo2(
985
- () => /* @__PURE__ */ jsx3(Fragment2, { children: host.renderViewer({ zoom, spread, rotations: decisions.turns, reloadKey, currentPage: viewerPage, onDocumentLoad, onPageSize }) }, reloadKey),
986
- [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]
987
1423
  );
988
1424
  const showChrome = !read;
989
1425
  const popCard = "absolute right-2 top-11 z-30 rounded-lg border border-border bg-popover p-3 text-popover-foreground shadow-lg";
990
- return /* @__PURE__ */ jsxs3(
1426
+ return /* @__PURE__ */ jsxs4(
991
1427
  "div",
992
1428
  {
993
1429
  ref: rootRef,
@@ -995,18 +1431,18 @@ function PdfWorkspace({ host, className }) {
995
1431
  "data-pdf-workspace": "",
996
1432
  "data-compact": compact ? "" : void 0,
997
1433
  onKeyDown,
998
- className: cn("relative flex h-full min-h-0 w-full flex-col overflow-hidden bg-background text-foreground outline-none", className),
1434
+ className: cn2("relative flex h-full min-h-0 w-full flex-col overflow-hidden bg-background text-foreground outline-none", className),
999
1435
  children: [
1000
- showChrome ? /* @__PURE__ */ jsxs3("div", { className: BAR, "data-pdf-topbar": "", children: [
1001
- iconBtn("Page thumbnails", /* @__PURE__ */ jsx3(PanelLeftIcon, {}), () => compact ? setSheet("pages") : setLeftOpen((v) => !v), { pressed: !compact && leftOpen }),
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 }),
1002
1438
  nameNode,
1003
1439
  savePill,
1004
- /* @__PURE__ */ jsx3("div", { className: "flex-1" }),
1005
- !compact ? /* @__PURE__ */ jsxs3("div", { className: "relative w-56", children: [
1006
- /* @__PURE__ */ jsx3(
1007
- Field2,
1440
+ /* @__PURE__ */ jsx4("div", { className: "flex-1" }),
1441
+ !compact ? /* @__PURE__ */ jsxs4("div", { className: "relative w-56", children: [
1442
+ /* @__PURE__ */ jsx4(
1443
+ Field3,
1008
1444
  {
1009
- icon: /* @__PURE__ */ jsx3(SearchIcon, {}),
1445
+ icon: /* @__PURE__ */ jsx4(SearchIcon, {}),
1010
1446
  value: toolQuery,
1011
1447
  "aria-label": "Search tools",
1012
1448
  placeholder: "Search tools",
@@ -1024,7 +1460,7 @@ function PdfWorkspace({ host, className }) {
1024
1460
  }
1025
1461
  }
1026
1462
  ),
1027
- pop === "tool-search" && toolHits.length ? /* @__PURE__ */ jsx3("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__ */ jsx3("li", { role: "option", "aria-selected": false, children: /* @__PURE__ */ jsxs3(
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(
1028
1464
  "button",
1029
1465
  {
1030
1466
  type: "button",
@@ -1036,25 +1472,25 @@ function PdfWorkspace({ host, className }) {
1036
1472
  setPop(null);
1037
1473
  },
1038
1474
  children: [
1039
- /* @__PURE__ */ jsx3("span", { children: t.label }),
1040
- t.status === "soon" ? /* @__PURE__ */ jsx3("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
1475
+ /* @__PURE__ */ jsx4("span", { children: t.label }),
1476
+ t.status === "soon" ? /* @__PURE__ */ jsx4("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
1041
1477
  ]
1042
1478
  }
1043
1479
  ) }, t.id)) }) : null
1044
1480
  ] }) : null,
1045
- iconBtn(undoLabel(history) ? `Undo ${undoLabel(history)}` : "Undo", /* @__PURE__ */ jsx3(Undo2Icon, {}), () => runAction("undo"), { disabled: !canUndo(history) }),
1046
- iconBtn(redoLabel(history) ? `Redo ${redoLabel(history)}` : "Redo", /* @__PURE__ */ jsx3(Undo2Icon, { className: "-scale-x-100" }), () => runAction("redo"), { disabled: !canRedo(history) }),
1047
- !compact && iconBtn("Find", /* @__PURE__ */ jsx3(SearchIcon, {}), () => runAction("find"), { pressed: findOpen }),
1048
- host.share && !compact ? iconBtn("Share", /* @__PURE__ */ jsx3(Share2Icon, {}), () => host.share()) : null,
1049
- iconBtn("Download", /* @__PURE__ */ jsx3(DownloadIcon, {}), () => host.download()),
1050
- host.print && !compact ? iconBtn("Print", /* @__PURE__ */ jsx3(PrinterIcon, {}), () => setPop(pop === "print" ? null : "print"), { pressed: pop === "print" }) : null,
1051
- !compact ? iconBtn("Keyboard shortcuts", /* @__PURE__ */ jsx3(KeyboardIcon, {}), () => runAction("shortcuts"), { pressed: pop === "shortcuts" }) : null,
1052
- compact ? /* @__PURE__ */ jsx3(Button2, { variant: "outline", onClick: () => setSheet("tools"), children: "Tools" }) : iconBtn("Properties panel", /* @__PURE__ */ jsx3(PanelRightIcon, {}), () => setRightOpen((v) => !v), { pressed: rightOpen })
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 })
1053
1489
  ] }) : null,
1054
- showChrome ? /* @__PURE__ */ jsxs3("div", { className: BAR, "data-pdf-viewbar": "", children: [
1055
- iconBtn("Previous page", /* @__PURE__ */ jsx3(ChevronLeftIcon, {}), () => goToPage(currentPage - 1), { disabled: currentPage <= 1 }),
1056
- /* @__PURE__ */ jsx3(
1057
- Field2,
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,
1058
1494
  {
1059
1495
  type: "number",
1060
1496
  min: 1,
@@ -1067,16 +1503,16 @@ function PdfWorkspace({ host, className }) {
1067
1503
  className: compact ? "w-12" : "w-16"
1068
1504
  }
1069
1505
  ),
1070
- /* @__PURE__ */ jsxs3("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: [
1506
+ /* @__PURE__ */ jsxs4("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: [
1071
1507
  "of ",
1072
1508
  pageCount || "-"
1073
1509
  ] }),
1074
- iconBtn("Next page", /* @__PURE__ */ jsx3(ChevronRightIcon, {}), () => goToPage(currentPage + 1), { disabled: currentPage >= pageCount }),
1075
- /* @__PURE__ */ jsx3("div", { className: "mx-1 h-5 w-px bg-border" }),
1076
- iconBtn("Zoom out", /* @__PURE__ */ jsx3(MinusIcon, {}), () => stepZoom(-1)),
1077
- /* @__PURE__ */ jsx3("span", { className: cn("min-w-[3rem] text-center text-xs tabular-nums", compact && "hidden"), "data-pdf-zoom": "", children: `${Math.round(zoom * 100)}%` }),
1078
- iconBtn("Zoom in", /* @__PURE__ */ jsx3(PlusIcon, {}), () => stepZoom(1)),
1079
- !compact ? /* @__PURE__ */ jsx3(
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(
1080
1516
  SegmentedControl3,
1081
1517
  {
1082
1518
  value: zoomMode === "custom" ? null : zoomMode,
@@ -1085,34 +1521,35 @@ function PdfWorkspace({ host, className }) {
1085
1521
  "aria-label": "Fit"
1086
1522
  }
1087
1523
  ) : null,
1088
- /* @__PURE__ */ jsx3("div", { className: "flex-1" }),
1089
- !compact ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
1090
- iconBtn("Rotate left", /* @__PURE__ */ jsx3(RotateCcwIcon, {}), () => turn(-90)),
1091
- iconBtn("Rotate right", /* @__PURE__ */ jsx3(RotateCwIcon, {}), () => turn(90)),
1092
- iconBtn("Delete page", /* @__PURE__ */ jsx3(Trash2Icon, {}), del, { disabled: decisions.order.length - decisions.deleted.length <= 1 }),
1093
- /* @__PURE__ */ jsx3("div", { className: "mx-1 h-5 w-px bg-border" }),
1094
- iconBtn("Two pages side by side", /* @__PURE__ */ jsx3(Columns2Icon2, {}), () => runAction("two-up"), { pressed: spread === "two-up" })
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" })
1095
1531
  ] }) : null,
1096
- iconBtn("Reading mode", /* @__PURE__ */ jsx3(ExpandIcon, {}), () => setRead(true))
1532
+ iconBtn("Reading mode", /* @__PURE__ */ jsx4(ExpandIcon, {}), () => setRead(true))
1097
1533
  ] }) : null,
1098
- /* @__PURE__ */ jsxs3("div", { className: "relative flex min-h-0 flex-1", children: [
1099
- showChrome && !compact ? /* @__PURE__ */ jsx3("div", { className: "flex w-10 shrink-0 flex-col items-center border-r border-border bg-background py-2", "data-pdf-rail": "", children: /* @__PURE__ */ jsx3(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,
1100
- showChrome && !compact && leftOpen ? /* @__PURE__ */ jsx3("aside", { className: "w-56 shrink-0 border-r border-border bg-background", "aria-label": "Pages", "data-pdf-left": "", children: leftPanel }) : null,
1101
- /* @__PURE__ */ jsxs3("main", { className: "relative flex min-w-0 flex-1 flex-col", children: [
1102
- findOpen ? /* @__PURE__ */ jsx3("div", { className: "absolute right-4 top-2 z-20", children: /* @__PURE__ */ jsx3(RenderedFindBar, { rootRef: scrollerRef, onClose: () => setFindOpen(false), label: "Find in document", focusRequest: findFocus, compact: true }) }) : null,
1103
- /* @__PURE__ */ jsx3("div", { ref: scrollerRef, tabIndex: 0, "data-pdf-scroller": "", className: "min-h-0 flex-1 overflow-auto bg-muted outline-none", children: viewer }),
1104
- read ? /* @__PURE__ */ jsx3("div", { className: "absolute right-3 top-3 z-20", children: /* @__PURE__ */ jsx3(Button2, { variant: "outline", icon: /* @__PURE__ */ jsx3(XIcon, {}), onClick: () => setRead(false), children: "Exit reading mode" }) }) : null
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
1105
1542
  ] }),
1106
- showChrome && !compact && rightOpen ? /* @__PURE__ */ jsx3("aside", { className: cn("shrink-0 border-l border-border bg-background", hostTool?.wide ? "w-[28rem]" : "w-72"), "aria-label": "Properties", "data-pdf-right": "", children: rightPanel }) : null,
1107
- pop === "all-tools" ? /* @__PURE__ */ jsx3("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__ */ jsx3(ToolRows, { tool: activeToolKey, hostTools, onPick: (id) => {
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) => {
1108
1545
  setPop(null);
1109
1546
  chooseTool(id);
1110
1547
  } }) }) : null,
1111
- pop === "print" && host.print ? /* @__PURE__ */ jsx3("div", { className: cn(popCard, "w-72"), role: "dialog", "aria-label": "Print", children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-3", children: [
1112
- /* @__PURE__ */ jsx3(SegmentedControl3, { fill: true, value: printMode, onValueChange: setPrintMode, data: [{ value: "all", label: "All pages" }, { value: "pages", label: "Pages" }], "aria-label": "What to print" }),
1113
- printMode === "pages" ? /* @__PURE__ */ jsx3(PageRangeInput2, { value: printRange, onChange: (t) => setPrintRange(t), pageCount }) : null,
1114
- /* @__PURE__ */ jsx3(
1115
- Button2,
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,
1116
1553
  {
1117
1554
  variant: "primary",
1118
1555
  disabled: busy || printMode === "pages" && !parsePageRanges2(printRange, pageCount).valid,
@@ -1125,18 +1562,27 @@ function PdfWorkspace({ host, className }) {
1125
1562
  }
1126
1563
  )
1127
1564
  ] }) }) : null,
1128
- pop === "shortcuts" ? /* @__PURE__ */ jsx3("div", { className: cn(popCard, "max-h-[70%] w-80 overflow-y-auto"), role: "dialog", "aria-label": "Keyboard shortcuts", "data-pdf-shortcuts": "", children: ["Edit", "View", "Pages", "Tools"].map((section) => /* @__PURE__ */ jsxs3("div", { className: "mb-3 last:mb-0", children: [
1129
- /* @__PURE__ */ jsx3("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: section }),
1130
- PDF_SHORTCUTS.filter((s) => s.section === section).map((s) => /* @__PURE__ */ jsxs3("div", { className: "flex justify-between gap-2 py-0.5 text-xs", children: [
1131
- /* @__PURE__ */ jsx3("span", { children: s.label }),
1132
- /* @__PURE__ */ jsx3("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: formatKeys(s.keys, isMac()) })
1133
- ] }, s.action))
1134
- ] }, section)) }) : null,
1135
- compact && sheet ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
1136
- /* @__PURE__ */ jsx3("button", { type: "button", "aria-label": "Close", className: "absolute inset-0 z-30 bg-background/60", onClick: () => setSheet(null) }),
1137
- /* @__PURE__ */ jsx3("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__ */ jsx3("div", { className: "h-[60vh] min-h-0", children: leftPanel }) : /* @__PURE__ */ jsxs3("div", { className: "flex min-h-0 flex-col overflow-y-auto", children: [
1138
- /* @__PURE__ */ jsx3("ul", { className: "p-2", role: "menu", "aria-label": "Tools", children: /* @__PURE__ */ jsx3(ToolRows, { tool: activeToolKey, hostTools, onPick: chooseTool }) }),
1139
- /* @__PURE__ */ jsx3("div", { className: "border-t border-border", children: rightPanel })
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 })
1140
1586
  ] }) })
1141
1587
  ] }) : null
1142
1588
  ] })
@@ -1146,27 +1592,47 @@ function PdfWorkspace({ host, className }) {
1146
1592
  }
1147
1593
  export {
1148
1594
  HISTORY_LIMIT,
1595
+ MARKUP_KIND_LABEL,
1596
+ MARKUP_SIZES,
1597
+ MARKUP_STYLE_FIELDS,
1598
+ MARKUP_SWATCHES,
1149
1599
  PDF_COMING_SOON,
1600
+ PDF_MARKUP_COMING_SOON,
1601
+ PDF_MARKUP_SHORTCUTS,
1602
+ PDF_MARKUP_TOOLS,
1150
1603
  PDF_SHORTCUTS,
1151
1604
  PDF_TOOLS,
1152
1605
  PdfWorkspace,
1606
+ TIMELINE_LIMIT,
1153
1607
  canRedo,
1154
1608
  canUndo,
1155
1609
  comingSoonIdOf,
1156
1610
  compilePageOps,
1157
1611
  deletePages,
1612
+ dropOrigin,
1613
+ emptyTimeline,
1158
1614
  formatKeys,
1159
1615
  freshDecisions,
1160
1616
  isDirty,
1161
1617
  keptOrder,
1618
+ markupComingSoonId,
1619
+ markupRedoneElsewhere,
1620
+ markupUndoneElsewhere,
1621
+ matchMarkupKey,
1162
1622
  matchShortcut,
1623
+ nextRedo,
1624
+ nextUndo,
1625
+ pdfMarkupTool,
1163
1626
  pdfTool,
1164
1627
  pushStep,
1628
+ recordStep,
1165
1629
  redo,
1166
1630
  redoLabel,
1167
1631
  reorderPages,
1168
1632
  restorePages,
1169
1633
  startHistory,
1634
+ stepBack,
1635
+ stepForward,
1170
1636
  turnPages,
1171
1637
  undo,
1172
1638
  undoLabel