@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/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,
@@ -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 right on the page.",
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: "Apply page changes", keys: "Mod+S", section: "Edit" },
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
- PencilIcon,
516
- PenToolIcon,
517
- ProportionsIcon,
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: ProportionsIcon,
826
+ crop: ProportionsIcon2,
527
827
  redact: EraserIcon,
528
828
  compress: ChevronsDownUpIcon,
529
829
  scan: CameraIcon,
530
- sign: PenToolIcon,
531
- "extract-data": TypeIcon,
532
- edit: PencilIcon,
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 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";
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] = useState3({ w: 1200, h: 800 });
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__ */ jsxs3(Fragment3, { children: [
569
- 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(
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: 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"),
576
876
  onClick: () => onPick(`host:${t.id}`),
577
877
  children: [
578
- /* @__PURE__ */ jsx3(t.icon, { className: "size-4 shrink-0" }),
579
- /* @__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 })
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__ */ jsx3("li", { role: "none", children: /* @__PURE__ */ jsxs3(
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: 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"),
592
892
  onClick: () => onPick(t.id),
593
893
  children: [
594
- /* @__PURE__ */ jsx3(Icon, { className: "size-4 shrink-0" }),
595
- /* @__PURE__ */ jsx3("span", { className: "flex-1 whitespace-nowrap", children: t.label }),
596
- 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
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] = useState3(0);
611
- const [history, setHistory] = useState3(() => startHistory(freshDecisions(0)));
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] = useState3("browse");
615
- const [hostToolId, setHostToolId] = useState3(null);
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] = useState3(true);
620
- const [rightOpen, setRightOpen] = useState3(true);
621
- const [leftTab, setLeftTab] = useState3("pages");
622
- const [read, setRead] = useState3(false);
623
- const [spread, setSpread] = useState3("single");
624
- const [zoomMode, setZoomMode] = useState3("fit-width");
625
- const [customZoom, setCustomZoom] = useState3(1);
626
- const [selected, setSelected] = useState3([]);
627
- const [currentPage, setCurrentPage] = useState3(1);
628
- const [pageDraft, setPageDraft] = useState3("1");
629
- const [findOpen, setFindOpen] = useState3(false);
630
- const [findFocus, setFindFocus] = useState3(0);
631
- const [pop, setPop] = useState3(null);
632
- const [sheet, setSheet] = useState3(null);
633
- const [toolQuery, setToolQuery] = useState3("");
634
- const [busy, setBusy] = useState3(false);
635
- const [error, setError] = useState3(null);
636
- const [result, setResult] = useState3(null);
637
- const [reloadKey, setReloadKey] = useState3(0);
638
- 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);
639
939
  const sizes = useRef2(/* @__PURE__ */ new Map());
640
- const [editingName, setEditingName] = useState3(false);
641
- const [nameDraft, setNameDraft] = useState3(host.file.name);
642
- const [printMode, setPrintMode] = useState3("all");
643
- 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;
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
- useEffect3(() => {
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) => 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
+ }, []);
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) void runTool({ tool: "apply-pages", ops });
767
- }, [decisions, runTool]);
768
- 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);
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 setHistory((h) => undo(h));
1221
+ return undoAll();
814
1222
  case "redo":
815
- return setHistory((h) => redo(h));
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 saveState = !online ? "Offline" : busy ? "Saving" : error && dirty ? "Not saved" : dirty ? "Unsaved" : "Saved";
882
- 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(
883
1302
  "span",
884
1303
  {
885
1304
  role: "status",
886
- className: cn(
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__ */ 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 });
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__ */ jsx3(
900
- Field2,
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__ */ jsx3(
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__ */ jsxs3("div", { className: "flex h-full min-h-0 flex-col", "data-pdf-host-tool": hostTool.id, children: [
930
- /* @__PURE__ */ jsxs3("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
931
- /* @__PURE__ */ jsx3("div", { className: "text-sm font-medium", children: hostTool.label }),
932
- 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
933
1352
  ] }),
934
- /* @__PURE__ */ jsx3("div", { className: "min-h-0 flex-1 overflow-hidden", children: hostTool.render({ currentPage, pageCount, selected: selectedPages, goToPage, reload: () => setReloadKey((k) => k + 1) }) })
935
- ] }) : /* @__PURE__ */ jsxs3("div", { className: "flex h-full min-h-0 flex-col", children: [
936
- /* @__PURE__ */ jsxs3("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
937
- /* @__PURE__ */ jsx3("div", { className: "text-sm font-medium", children: toolDef.label }),
938
- 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
939
1358
  ] }),
940
- /* @__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(
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__ */ jsx3(
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__ */ jsx3(Fragment2, { children: host.renderViewer({ zoom, spread, rotations: decisions.turns, reloadKey, currentPage: viewerPage, onDocumentLoad, onPageSize }) }, reloadKey),
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__ */ jsxs3(
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: 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),
998
1435
  children: [
999
- showChrome ? /* @__PURE__ */ jsxs3("div", { className: BAR, "data-pdf-topbar": "", children: [
1000
- 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 }),
1001
1438
  nameNode,
1002
1439
  savePill,
1003
- /* @__PURE__ */ jsx3("div", { className: "flex-1" }),
1004
- !compact ? /* @__PURE__ */ jsxs3("div", { className: "relative w-56", children: [
1005
- /* @__PURE__ */ jsx3(
1006
- Field2,
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__ */ jsx3(SearchIcon, {}),
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__ */ 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(
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__ */ jsx3("span", { children: t.label }),
1039
- 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
1040
1477
  ]
1041
1478
  }
1042
1479
  ) }, t.id)) }) : null
1043
1480
  ] }) : null,
1044
- iconBtn(undoLabel(history) ? `Undo ${undoLabel(history)}` : "Undo", /* @__PURE__ */ jsx3(Undo2Icon, {}), () => runAction("undo"), { disabled: !canUndo(history) }),
1045
- iconBtn(redoLabel(history) ? `Redo ${redoLabel(history)}` : "Redo", /* @__PURE__ */ jsx3(Undo2Icon, { className: "-scale-x-100" }), () => runAction("redo"), { disabled: !canRedo(history) }),
1046
- !compact && iconBtn("Find", /* @__PURE__ */ jsx3(SearchIcon, {}), () => runAction("find"), { pressed: findOpen }),
1047
- host.share && !compact ? iconBtn("Share", /* @__PURE__ */ jsx3(Share2Icon, {}), () => host.share()) : null,
1048
- iconBtn("Download", /* @__PURE__ */ jsx3(DownloadIcon, {}), () => host.download()),
1049
- host.print && !compact ? iconBtn("Print", /* @__PURE__ */ jsx3(PrinterIcon, {}), () => setPop(pop === "print" ? null : "print"), { pressed: pop === "print" }) : null,
1050
- !compact ? iconBtn("Keyboard shortcuts", /* @__PURE__ */ jsx3(KeyboardIcon, {}), () => runAction("shortcuts"), { pressed: pop === "shortcuts" }) : null,
1051
- 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 })
1052
1489
  ] }) : null,
1053
- showChrome ? /* @__PURE__ */ jsxs3("div", { className: BAR, "data-pdf-viewbar": "", children: [
1054
- iconBtn("Previous page", /* @__PURE__ */ jsx3(ChevronLeftIcon, {}), () => goToPage(currentPage - 1), { disabled: currentPage <= 1 }),
1055
- /* @__PURE__ */ jsx3(
1056
- 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,
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__ */ jsxs3("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: [
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__ */ jsx3(ChevronRightIcon, {}), () => goToPage(currentPage + 1), { disabled: currentPage >= pageCount }),
1074
- /* @__PURE__ */ jsx3("div", { className: "mx-1 h-5 w-px bg-border" }),
1075
- iconBtn("Zoom out", /* @__PURE__ */ jsx3(MinusIcon, {}), () => stepZoom(-1)),
1076
- /* @__PURE__ */ jsx3("span", { className: cn("min-w-[3rem] text-center text-xs tabular-nums", compact && "hidden"), "data-pdf-zoom": "", children: `${Math.round(zoom * 100)}%` }),
1077
- iconBtn("Zoom in", /* @__PURE__ */ jsx3(PlusIcon, {}), () => stepZoom(1)),
1078
- !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(
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__ */ jsx3("div", { className: "flex-1" }),
1088
- !compact ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
1089
- iconBtn("Rotate left", /* @__PURE__ */ jsx3(RotateCcwIcon, {}), () => turn(-90)),
1090
- iconBtn("Rotate right", /* @__PURE__ */ jsx3(RotateCwIcon, {}), () => turn(90)),
1091
- iconBtn("Delete page", /* @__PURE__ */ jsx3(Trash2Icon, {}), del, { disabled: decisions.order.length - decisions.deleted.length <= 1 }),
1092
- /* @__PURE__ */ jsx3("div", { className: "mx-1 h-5 w-px bg-border" }),
1093
- 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" })
1094
1531
  ] }) : null,
1095
- iconBtn("Reading mode", /* @__PURE__ */ jsx3(ExpandIcon, {}), () => setRead(true))
1532
+ iconBtn("Reading mode", /* @__PURE__ */ jsx4(ExpandIcon, {}), () => setRead(true))
1096
1533
  ] }) : null,
1097
- /* @__PURE__ */ jsxs3("div", { className: "relative flex min-h-0 flex-1", children: [
1098
- 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,
1099
- 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,
1100
- /* @__PURE__ */ jsxs3("main", { className: "relative flex min-w-0 flex-1 flex-col", children: [
1101
- 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,
1102
- /* @__PURE__ */ jsx3("div", { ref: scrollerRef, tabIndex: 0, "data-pdf-scroller": "", className: "min-h-0 flex-1 overflow-auto bg-muted outline-none", children: viewer }),
1103
- 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
1104
1542
  ] }),
1105
- 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,
1106
- 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) => {
1107
1545
  setPop(null);
1108
1546
  chooseTool(id);
1109
1547
  } }) }) : null,
1110
- 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: [
1111
- /* @__PURE__ */ jsx3(SegmentedControl3, { fill: true, value: printMode, onValueChange: setPrintMode, data: [{ value: "all", label: "All pages" }, { value: "pages", label: "Pages" }], "aria-label": "What to print" }),
1112
- printMode === "pages" ? /* @__PURE__ */ jsx3(PageRangeInput2, { value: printRange, onChange: (t) => setPrintRange(t), pageCount }) : null,
1113
- /* @__PURE__ */ jsx3(
1114
- 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,
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__ */ 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: [
1128
- /* @__PURE__ */ jsx3("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: section }),
1129
- PDF_SHORTCUTS.filter((s) => s.section === section).map((s) => /* @__PURE__ */ jsxs3("div", { className: "flex justify-between gap-2 py-0.5 text-xs", children: [
1130
- /* @__PURE__ */ jsx3("span", { children: s.label }),
1131
- /* @__PURE__ */ jsx3("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: formatKeys(s.keys, isMac()) })
1132
- ] }, s.action))
1133
- ] }, section)) }) : null,
1134
- compact && sheet ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
1135
- /* @__PURE__ */ jsx3("button", { type: "button", "aria-label": "Close", className: "absolute inset-0 z-30 bg-background/60", onClick: () => setSheet(null) }),
1136
- /* @__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: [
1137
- /* @__PURE__ */ jsx3("ul", { className: "p-2", role: "menu", "aria-label": "Tools", children: /* @__PURE__ */ jsx3(ToolRows, { tool: activeToolKey, hostTools, onPick: chooseTool }) }),
1138
- /* @__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 })
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