@ai-matrx/office 0.8.50 → 0.8.52

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.cjs CHANGED
@@ -32,27 +32,47 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  var pdf_exports = {};
33
33
  __export(pdf_exports, {
34
34
  HISTORY_LIMIT: () => HISTORY_LIMIT,
35
+ MARKUP_KIND_LABEL: () => MARKUP_KIND_LABEL,
36
+ MARKUP_SIZES: () => MARKUP_SIZES,
37
+ MARKUP_STYLE_FIELDS: () => MARKUP_STYLE_FIELDS,
38
+ MARKUP_SWATCHES: () => MARKUP_SWATCHES,
35
39
  PDF_COMING_SOON: () => PDF_COMING_SOON,
40
+ PDF_MARKUP_COMING_SOON: () => PDF_MARKUP_COMING_SOON,
41
+ PDF_MARKUP_SHORTCUTS: () => PDF_MARKUP_SHORTCUTS,
42
+ PDF_MARKUP_TOOLS: () => PDF_MARKUP_TOOLS,
36
43
  PDF_SHORTCUTS: () => PDF_SHORTCUTS,
37
44
  PDF_TOOLS: () => PDF_TOOLS,
38
45
  PdfWorkspace: () => PdfWorkspace,
46
+ TIMELINE_LIMIT: () => TIMELINE_LIMIT,
39
47
  canRedo: () => canRedo,
40
48
  canUndo: () => canUndo,
41
49
  comingSoonIdOf: () => comingSoonIdOf,
42
50
  compilePageOps: () => compilePageOps,
43
51
  deletePages: () => deletePages,
52
+ dropOrigin: () => dropOrigin,
53
+ emptyTimeline: () => emptyTimeline,
44
54
  formatKeys: () => formatKeys,
45
55
  freshDecisions: () => freshDecisions,
46
56
  isDirty: () => isDirty,
47
57
  keptOrder: () => keptOrder,
58
+ markupComingSoonId: () => markupComingSoonId,
59
+ markupRedoneElsewhere: () => markupRedoneElsewhere,
60
+ markupUndoneElsewhere: () => markupUndoneElsewhere,
61
+ matchMarkupKey: () => matchMarkupKey,
48
62
  matchShortcut: () => matchShortcut,
63
+ nextRedo: () => nextRedo,
64
+ nextUndo: () => nextUndo,
65
+ pdfMarkupTool: () => pdfMarkupTool,
49
66
  pdfTool: () => pdfTool,
50
67
  pushStep: () => pushStep,
68
+ recordStep: () => recordStep,
51
69
  redo: () => redo,
52
70
  redoLabel: () => redoLabel,
53
71
  reorderPages: () => reorderPages,
54
72
  restorePages: () => restorePages,
55
73
  startHistory: () => startHistory,
74
+ stepBack: () => stepBack,
75
+ stepForward: () => stepForward,
56
76
  turnPages: () => turnPages,
57
77
  undo: () => undo,
58
78
  undoLabel: () => undoLabel
@@ -60,10 +80,10 @@ __export(pdf_exports, {
60
80
  module.exports = __toCommonJS(pdf_exports);
61
81
 
62
82
  // src/pdf/PdfWorkspace.tsx
63
- var import_react3 = require("react");
64
- var import_cn = require("@ai-matrx/design-system/cn");
65
- var import_controls4 = require("@ai-matrx/design-system/controls");
66
- var import_icons2 = require("@ai-matrx/design-system/icons");
83
+ var import_react4 = require("react");
84
+ var import_cn2 = require("@ai-matrx/design-system/cn");
85
+ var import_controls5 = require("@ai-matrx/design-system/controls");
86
+ var import_icons3 = require("@ai-matrx/design-system/icons");
67
87
  var import_RenderedFindBar = require("@ai-matrx/rich-content/rich-document/search/RenderedFindBar");
68
88
 
69
89
  // src/pdf/page-decisions.ts
@@ -430,9 +450,94 @@ var canRedo = (h) => h.future.length > 0;
430
450
  var undoLabel = (h) => h.present.label;
431
451
  var redoLabel = (h) => h.future[0]?.label ?? "";
432
452
 
453
+ // src/pdf/markup.ts
454
+ var PDF_MARKUP_TOOLS = [
455
+ { id: "highlight", label: "Highlight", key: "h", status: "ready", engineTool: "highlight" },
456
+ { id: "freetext", label: "Text box", key: "x", status: "ready", engineTool: "freetext" },
457
+ { id: "ink", label: "Draw", key: "d", status: "ready", engineTool: "ink" },
458
+ { id: "image", label: "Image", key: "i", status: "ready", engineTool: "stamp" },
459
+ { id: "signature", label: "Signature", key: "g", status: "ready", engineTool: "stamp", needs: "signaturePad" },
460
+ { id: "underline", label: "Underline and strike out", status: "soon", reason: "The PDF writer we use makes highlights only." },
461
+ { id: "shapes", label: "Shapes", status: "soon", reason: "Rectangles, ovals and arrows need their own writer." },
462
+ { id: "note", label: "Sticky note", status: "soon", reason: "Comment on a highlight, text box or drawing for now." },
463
+ { id: "links", label: "Links", status: "soon", reason: "The PDF writer we use cannot add or change links." },
464
+ { 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." }
465
+ ];
466
+ var pdfMarkupTool = (id) => PDF_MARKUP_TOOLS.find((t) => t.id === id);
467
+ var MARKUP_SWATCHES = [
468
+ { value: "#ffd400", label: "Yellow" },
469
+ { value: "#5fe36b", label: "Green" },
470
+ { value: "#4cc9f0", label: "Blue" },
471
+ { value: "#ff6fb5", label: "Pink" },
472
+ { value: "#ff4d4d", label: "Red" },
473
+ { value: "#1f2937", label: "Black" }
474
+ ];
475
+ var MARKUP_STYLE_FIELDS = {
476
+ highlight: ["color"],
477
+ freetext: ["color", "size"],
478
+ ink: ["color", "opacity", "size"],
479
+ stamp: [],
480
+ signature: []
481
+ };
482
+ var MARKUP_SIZES = {
483
+ freetext: [8, 10, 12, 14, 18, 24, 36],
484
+ ink: [1, 2, 3, 5, 8, 12]
485
+ };
486
+ var MARKUP_KIND_LABEL = {
487
+ highlight: "Highlight",
488
+ freetext: "Text box",
489
+ ink: "Drawing",
490
+ stamp: "Image",
491
+ signature: "Signature"
492
+ };
493
+ var MARKUP_PROMISES = {
494
+ underline: "Underline or strike out text, with the same colors as highlights.",
495
+ shapes: "Draw rectangles, ovals, lines and arrows on a page.",
496
+ note: "Pin a sticky note anywhere on a page.",
497
+ links: "Add, change and remove links to a page or a web address.",
498
+ "edit-text": "Change the page's own text, see a before-and-after view, then save."
499
+ };
500
+ var markupComingSoonId = (id) => id === "links" ? "pdf.links" : id === "edit-text" ? "pdf.edit-text" : `pdf.markup.${id}`;
501
+ var PDF_MARKUP_COMING_SOON = Object.fromEntries(
502
+ PDF_MARKUP_TOOLS.filter((t) => t.status === "soon").map((t) => {
503
+ const id = markupComingSoonId(t.id);
504
+ return [id, { id, label: t.label, owner: "pdf", promise: MARKUP_PROMISES[t.id] ?? "", stage: "planned", surfaces: ["PDF workspace markup panel"] }];
505
+ })
506
+ );
507
+ var TIMELINE_LIMIT = 100;
508
+ var emptyTimeline = () => ({ past: [], future: [] });
509
+ function recordStep(t, origin, label) {
510
+ return { past: [...t.past, { origin, label }].slice(-TIMELINE_LIMIT), future: [] };
511
+ }
512
+ var nextUndo = (t) => t.past[t.past.length - 1] ?? null;
513
+ var nextRedo = (t) => t.future[0] ?? null;
514
+ function stepBack(t) {
515
+ const last = t.past[t.past.length - 1];
516
+ return last ? { past: t.past.slice(0, -1), future: [last, ...t.future] } : t;
517
+ }
518
+ function stepForward(t) {
519
+ const next = t.future[0];
520
+ return next ? { past: [...t.past, next], future: t.future.slice(1) } : t;
521
+ }
522
+ function markupUndoneElsewhere(t) {
523
+ for (let i = t.past.length - 1; i >= 0; i--) {
524
+ if (t.past[i].origin === "markup") return { past: [...t.past.slice(0, i), ...t.past.slice(i + 1)], future: [t.past[i], ...t.future] };
525
+ }
526
+ return t;
527
+ }
528
+ function markupRedoneElsewhere(t) {
529
+ const i = t.future.findIndex((s) => s.origin === "markup");
530
+ if (i < 0) return t;
531
+ return { past: [...t.past, t.future[i]], future: [...t.future.slice(0, i), ...t.future.slice(i + 1)] };
532
+ }
533
+ function dropOrigin(t, origin) {
534
+ return { past: t.past.filter((s) => s.origin !== origin), future: t.future.filter((s) => s.origin !== origin) };
535
+ }
536
+
433
537
  // src/pdf/tools.ts
434
538
  var PDF_TOOLS = [
435
539
  { id: "browse", label: "Browse", key: "v", status: "ready", opens: "none", group: 0 },
540
+ { 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." },
436
541
  { id: "organize", label: "Organize pages", key: "o", status: "ready", capability: "rotate-pages,delete-pages,reorder-pages", opens: "panel", group: 0 },
437
542
  { id: "extract-pages", label: "Extract pages", key: "e", status: "ready", capability: "extract-pages", opens: "panel", group: 0 },
438
543
  { id: "split", label: "Split", key: "s", status: "ready", capability: "split", opens: "panel", group: 0 },
@@ -453,7 +558,7 @@ var PDF_TOOLS = [
453
558
  var pdfTool = (id) => PDF_TOOLS.find((t) => t.id === id);
454
559
  var comingSoonIdOf = (id) => `pdf.${id}`;
455
560
  var PROMISES = {
456
- edit: "Change text and swap images right on the page.",
561
+ edit: "Change the page's own text and swap images, with a before-and-after view.",
457
562
  forms: "Fill in a form and keep the values in the file.",
458
563
  protect: "Lock a PDF with a password and limit what others can do.",
459
564
  ocr: "Turn a scanned PDF into one you can search and copy from.",
@@ -478,14 +583,15 @@ var PDF_COMING_SOON = {
478
583
  return [id, { id, label: t.label, owner: "pdf", promise: PROMISES[t.id] ?? "", stage: "planned", surfaces: ["PDF workspace tool rail"] }];
479
584
  })
480
585
  ),
481
- ...PANEL_PROMISES
586
+ ...PANEL_PROMISES,
587
+ ...PDF_MARKUP_COMING_SOON
482
588
  };
483
589
 
484
590
  // src/pdf/shortcuts.ts
485
591
  var PDF_SHORTCUTS = [
486
592
  { action: "undo", label: "Undo", keys: "Mod+Z", section: "Edit" },
487
593
  { action: "redo", label: "Redo", keys: "Mod+Shift+Z", section: "Edit" },
488
- { action: "save", label: "Apply page changes", keys: "Mod+S", section: "Edit" },
594
+ { action: "save", label: "Save markup or apply page changes", keys: "Mod+S", section: "Edit" },
489
595
  { action: "find", label: "Find", keys: "Mod+F", section: "Edit" },
490
596
  { action: "print", label: "Print", keys: "Mod+P", section: "Edit" },
491
597
  { action: "zoom-in", label: "Zoom in", keys: "Mod+=", section: "View" },
@@ -512,6 +618,14 @@ var PDF_SHORTCUTS = [
512
618
  section: "Tools"
513
619
  }))
514
620
  ];
621
+ var PDF_MARKUP_SHORTCUTS = PDF_MARKUP_TOOLS.filter(
622
+ (t) => t.status === "ready" && t.key
623
+ ).map((t) => ({ tool: t.id, label: t.label, keys: t.key.toUpperCase() }));
624
+ function matchMarkupKey(e, typing) {
625
+ if (typing || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.key.length !== 1) return null;
626
+ const k = e.key.toLowerCase();
627
+ return PDF_MARKUP_SHORTCUTS.find((s) => s.keys.toLowerCase() === k)?.tool ?? null;
628
+ }
515
629
  function parse(keys) {
516
630
  const parts = keys.split("+");
517
631
  const key = parts[parts.length - 1] === "" ? "+" : parts[parts.length - 1];
@@ -538,30 +652,222 @@ function formatKeys(keys, mac) {
538
652
  return keys.split("+").map((k) => k === "Mod" ? mac ? "\u2318" : "Ctrl" : k === "Shift" ? mac ? "\u21E7" : "Shift" : k === "ArrowRight" ? "\u2192" : k === "ArrowLeft" ? "\u2190" : k).join(mac ? "" : "+");
539
653
  }
540
654
 
541
- // src/pdf/tool-icons.tsx
655
+ // src/pdf/markup-panel.tsx
656
+ var import_react3 = require("react");
657
+ var import_cn = require("@ai-matrx/design-system/cn");
658
+ var import_controls4 = require("@ai-matrx/design-system/controls");
542
659
  var import_icons = require("@ai-matrx/design-system/icons");
660
+ var import_jsx_runtime3 = require("react/jsx-runtime");
661
+ var MARKUP_ICONS = {
662
+ highlight: import_icons.HighlighterIcon,
663
+ freetext: import_icons.TypeIcon,
664
+ ink: import_icons.PencilIcon,
665
+ image: import_icons.ImageIcon,
666
+ signature: import_icons.PenToolIcon,
667
+ underline: import_icons.WrapTextIcon,
668
+ shapes: import_icons.ProportionsIcon,
669
+ note: import_icons.StickyNoteIcon,
670
+ links: import_icons.Link2Icon,
671
+ "edit-text": import_icons.TypeIcon
672
+ };
673
+ function Section({ label, children }) {
674
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: label }),
676
+ children
677
+ ] });
678
+ }
679
+ function StyleControls({ kind, style, onChange, compact }) {
680
+ const fields = MARKUP_STYLE_FIELDS[kind];
681
+ if (!fields.length) return null;
682
+ const sizes = kind === "freetext" ? MARKUP_SIZES.freetext : kind === "ink" ? MARKUP_SIZES.ink : null;
683
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: (0, import_cn.cn)("flex gap-2", compact ? "items-center" : "flex-col"), children: [
684
+ fields.includes("color") ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.ColorSwatchRow, { colors: MARKUP_SWATCHES, value: style.color, onChange: (color) => onChange({ color }), "aria-label": "Color" }) : null,
685
+ fields.includes("opacity") ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: compact ? "w-52 shrink-0" : "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.OpacitySlider, { value: style.opacity, onChange: (opacity) => onChange({ opacity }), className: "w-full" }) }) : null,
686
+ fields.includes("size") && sizes ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: compact ? "w-24 shrink-0" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
687
+ import_controls4.Select,
688
+ {
689
+ "aria-label": kind === "freetext" ? "Font size" : "Line width",
690
+ value: String(sizes.includes(style.size) ? style.size : sizes[0]),
691
+ onValueChange: (v) => onChange({ size: Number(v) }),
692
+ options: sizes.map((n) => ({ value: String(n), label: `${n} pt` }))
693
+ }
694
+ ) }) : null
695
+ ] });
696
+ }
697
+ function MarkupPanel({ host, engine, snapshot, active, onPick, blocked, busy, online, error, onSave, onDiscard, onSoon, goToPage, reloadKey }) {
698
+ const [confirmDiscard, setConfirmDiscard] = (0, import_react3.useState)(false);
699
+ const [signature, setSignature] = (0, import_react3.useState)(null);
700
+ const [existing, setExisting] = (0, import_react3.useState)(null);
701
+ const pending = snapshot?.pending ?? 0;
702
+ const tools = PDF_MARKUP_TOOLS.filter((t) => t.needs !== "signaturePad" || host.signaturePad);
703
+ const ready = tools.filter((t) => t.status === "ready");
704
+ const soon = tools.filter((t) => t.status === "soon");
705
+ const def = tools.find((t) => t.id === active);
706
+ const styleKind = def?.engineTool && def.engineTool !== "stamp" ? def.engineTool : null;
707
+ const Pad = host.signaturePad;
708
+ (0, import_react3.useEffect)(() => {
709
+ let live = true;
710
+ host.existingAnnotations?.().then((list) => live && setExisting(list), () => live && setExisting([]));
711
+ return () => {
712
+ live = false;
713
+ };
714
+ }, [host, reloadKey]);
715
+ (0, import_react3.useEffect)(() => {
716
+ if (!pending) setConfirmDiscard(false);
717
+ }, [pending]);
718
+ const pickImage = () => {
719
+ const input = document.createElement("input");
720
+ input.type = "file";
721
+ input.accept = "image/png,image/jpeg,image/webp,image/gif,image/svg+xml";
722
+ input.onchange = () => {
723
+ const f = input.files?.[0];
724
+ if (f && engine) void engine.addImage(f, "stamp");
725
+ };
726
+ input.click();
727
+ };
728
+ const row = (t) => {
729
+ const Icon = MARKUP_ICONS[t.id];
730
+ const on = t.id === active;
731
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
733
+ "button",
734
+ {
735
+ type: "button",
736
+ "aria-pressed": t.status === "ready" ? on : void 0,
737
+ disabled: t.status === "ready" && (!engine || Boolean(blocked)),
738
+ onClick: () => t.status === "soon" ? onSoon(markupComingSoonId(t.id)) : onPick(t.id),
739
+ className: (0, import_cn.cn)(
740
+ "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",
741
+ on && "bg-accent"
742
+ ),
743
+ children: [
744
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "size-4 shrink-0" }),
745
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "min-w-0 flex-1 truncate", children: t.label }),
746
+ t.status === "soon" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: "Soon" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("kbd", { className: "text-muted-foreground", children: t.key?.toUpperCase() })
747
+ ]
748
+ }
749
+ ),
750
+ t.status === "soon" && t.reason ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pl-8 pr-2 text-xs text-muted-foreground", children: t.reason }) : null
751
+ ] }, t.id);
752
+ };
753
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col gap-4", "data-pdf-markup-panel": "", children: [
754
+ blocked ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "status", className: "rounded-md border border-border px-2 py-1.5 text-xs", children: blocked }) : null,
755
+ !engine && !blocked ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "Opening the document\u2026" }) : null,
756
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "flex flex-col", "aria-label": "Markup tools", children: ready.map(row) }),
757
+ styleKind && snapshot ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Section, { label: "Look", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StyleControls, { kind: styleKind, style: snapshot.styles[styleKind], onChange: (p) => engine?.setStyle(p) }) }) : null,
758
+ active === "highlight" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "Select text on the page to highlight it." }) : null,
759
+ active === "freetext" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "Click on the page, then type." }) : null,
760
+ active === "ink" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "Drag on the page to draw." }) : null,
761
+ active === "image" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ImageIcon, {}), disabled: !engine, onClick: pickImage, children: "Choose an image" }) : null,
762
+ active === "signature" && Pad ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { label: "Signature", children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Pad, { value: signature, onChange: setSignature }),
764
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.PenToolIcon, {}), disabled: !engine || !signature, onClick: () => signature && engine && void engine.addImage(signature, "signature"), children: "Place on page" })
765
+ ] }) : null,
766
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { label: "Changes", children: [
767
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs", role: "status", "data-pdf-markup-pending": "", children: pending ? `Not saved: ${snapshot?.summary}` : "No unsaved markup" }),
768
+ error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "alert", className: "text-xs text-destructive", children: error }) : null,
769
+ !online ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "You are offline. Markup is kept here until you are back online." }) : null,
770
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex gap-2", children: [
771
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "primary", disabled: !pending || busy || !online, onClick: onSave, children: busy ? "Saving" : "Save" }),
772
+ confirmDiscard ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
773
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_controls4.Button, { variant: "outline", onClick: () => {
774
+ setConfirmDiscard(false);
775
+ onDiscard();
776
+ }, children: [
777
+ "Discard ",
778
+ pending
779
+ ] }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "quiet", onClick: () => setConfirmDiscard(false), children: "Keep" })
781
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "outline", disabled: !pending || busy, onClick: () => setConfirmDiscard(true), children: "Discard" })
782
+ ] })
783
+ ] }),
784
+ host.existingAnnotations ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { label: "In this file", children: [
785
+ existing === null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "Reading\u2026" }) : existing.filter((a) => a.subtype !== "Link" && a.subtype !== "Widget").length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "No comments or markup yet" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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__ */ (0, import_jsx_runtime3.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("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: [
786
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "w-8 shrink-0 tabular-nums text-muted-foreground", children: [
787
+ "p. ",
788
+ a.page
789
+ ] }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "min-w-0 flex-1", children: [
791
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "block", children: [
792
+ a.subtype,
793
+ a.author ? ` \xB7 ${a.author}` : ""
794
+ ] }),
795
+ a.contents ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "block truncate text-muted-foreground", children: a.contents }) : null
796
+ ] })
797
+ ] }) }, `${a.page}-${i}`)) }),
798
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: "Markup already saved shows on the page; changing it in place is coming." })
799
+ ] }) : null,
800
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Section, { label: "Coming soon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "flex flex-col", "aria-label": "Markup coming soon", children: soon.map(row) }) })
801
+ ] });
802
+ }
803
+ function MarkupFloatingToolbar({ engine, snapshot, enabled, onDone }) {
804
+ const sel = snapshot?.selection ?? null;
805
+ const [commenting, setCommenting] = (0, import_react3.useState)(false);
806
+ const [draft, setDraft] = (0, import_react3.useState)("");
807
+ const selKey = sel ? `${sel.kind}:${Math.round(sel.rect.x)}:${Math.round(sel.rect.y)}` : "";
808
+ (0, import_react3.useEffect)(() => {
809
+ setCommenting(false);
810
+ setDraft(sel?.comment ?? "");
811
+ }, [selKey]);
812
+ if (!engine || !enabled || !sel) return null;
813
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.AnchoredToolbar, { open: true, rect: sel.rect, "aria-label": `${MARKUP_KIND_LABEL[sel.kind]} options`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-pdf-markup-toolbar": "", children: [
814
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StyleControls, { compact: true, kind: sel.kind, style: sel.style, onChange: (p) => engine.setStyle(p) }),
815
+ commenting ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
816
+ import_controls4.Field,
817
+ {
818
+ autoFocus: true,
819
+ value: draft,
820
+ "aria-label": "Comment",
821
+ placeholder: "Comment",
822
+ onChange: (e) => setDraft(e.target.value),
823
+ onKeyDown: (e) => {
824
+ if (e.key === "Enter") {
825
+ engine.setComment(draft);
826
+ setCommenting(false);
827
+ onDone();
828
+ }
829
+ if (e.key === "Escape") {
830
+ e.stopPropagation();
831
+ setCommenting(false);
832
+ onDone();
833
+ }
834
+ },
835
+ onBlur: () => {
836
+ engine.setComment(draft);
837
+ setCommenting(false);
838
+ },
839
+ width: "md"
840
+ }
841
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.MessageSquareIcon, {}), "aria-label": sel.comment ? "Edit comment" : "Add comment", title: sel.comment || "Add comment", pressed: Boolean(sel.comment), onClick: () => setCommenting(true) }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "quiet", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.Trash2Icon, {}), "aria-label": "Delete", title: "Delete", onClick: () => engine.deleteSelected() })
843
+ ] }) });
844
+ }
845
+
846
+ // src/pdf/tool-icons.tsx
847
+ var import_icons2 = require("@ai-matrx/design-system/icons");
543
848
  var PDF_TOOL_ICONS = {
544
- browse: import_icons.HandIcon,
545
- organize: import_icons.LayoutGridIcon,
546
- "extract-pages": import_icons.ArrowUpFromLineIcon,
547
- split: import_icons.SplitIcon,
548
- crop: import_icons.ProportionsIcon,
549
- redact: import_icons.EraserIcon,
550
- compress: import_icons.ChevronsDownUpIcon,
551
- scan: import_icons.CameraIcon,
552
- sign: import_icons.PenToolIcon,
553
- "extract-data": import_icons.TypeIcon,
554
- edit: import_icons.PencilIcon,
555
- forms: import_icons.ListChecksIcon,
556
- protect: import_icons.LockIcon,
557
- ocr: import_icons.CaptionsIcon,
558
- convert: import_icons.ArrowLeftRightIcon,
559
- compare: import_icons.Columns2Icon,
560
- stamp: import_icons.FlagIcon
849
+ browse: import_icons2.HandIcon,
850
+ markup: import_icons2.MessageSquareIcon,
851
+ organize: import_icons2.LayoutGridIcon,
852
+ "extract-pages": import_icons2.ArrowUpFromLineIcon,
853
+ split: import_icons2.SplitIcon,
854
+ crop: import_icons2.ProportionsIcon,
855
+ redact: import_icons2.EraserIcon,
856
+ compress: import_icons2.ChevronsDownUpIcon,
857
+ scan: import_icons2.CameraIcon,
858
+ sign: import_icons2.PenToolIcon,
859
+ "extract-data": import_icons2.TypeIcon,
860
+ edit: import_icons2.PencilIcon,
861
+ forms: import_icons2.ListChecksIcon,
862
+ protect: import_icons2.LockIcon,
863
+ ocr: import_icons2.CaptionsIcon,
864
+ convert: import_icons2.ArrowLeftRightIcon,
865
+ compare: import_icons2.Columns2Icon,
866
+ stamp: import_icons2.FlagIcon
561
867
  };
562
868
 
563
869
  // src/pdf/PdfWorkspace.tsx
564
- var import_jsx_runtime3 = require("react/jsx-runtime");
870
+ var import_jsx_runtime4 = require("react/jsx-runtime");
565
871
  var ZOOM_STEPS = [0.5, 0.75, 1, 1.25, 1.5, 2, 3];
566
872
  var COMPACT_BELOW_PX = 768;
567
873
  var BAR = "flex h-10 shrink-0 items-center gap-1 border-b border-border bg-background px-2";
@@ -573,8 +879,8 @@ async function announceSoon(id) {
573
879
  await announceComingSoon(id);
574
880
  }
575
881
  function useWidth(ref) {
576
- const [size, setSize] = (0, import_react3.useState)({ w: 1200, h: 800 });
577
- (0, import_react3.useLayoutEffect)(() => {
882
+ const [size, setSize] = (0, import_react4.useState)({ w: 1200, h: 800 });
883
+ (0, import_react4.useLayoutEffect)(() => {
578
884
  const node = ref.current;
579
885
  if (!node) return void 0;
580
886
  const measure = () => setSize({ w: node.clientWidth || 1200, h: node.clientHeight || 800 });
@@ -586,36 +892,36 @@ function useWidth(ref) {
586
892
  }, [ref]);
587
893
  return size;
588
894
  }
589
- function ToolRows({ tool, hostTools, onPick }) {
590
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
591
- hostTools.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { role: "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
895
+ function ToolRows({ tool, hostTools, onPick, canMarkup = false }) {
896
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
897
+ hostTools.map((t) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { role: "none", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
592
898
  "button",
593
899
  {
594
900
  type: "button",
595
901
  role: "menuitem",
596
902
  "aria-current": tool === `host:${t.id}` ? "true" : void 0,
597
- className: (0, import_cn.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"),
903
+ className: (0, import_cn2.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"),
598
904
  onClick: () => onPick(`host:${t.id}`),
599
905
  children: [
600
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(t.icon, { className: "size-4 shrink-0" }),
601
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "flex-1 whitespace-nowrap", children: t.label })
906
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(t.icon, { className: "size-4 shrink-0" }),
907
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "flex-1 whitespace-nowrap", children: t.label })
602
908
  ]
603
909
  }
604
910
  ) }, `host:${t.id}`)),
605
- PDF_TOOLS.filter((t) => t.id !== "browse").map((t) => {
911
+ PDF_TOOLS.filter((t) => t.id !== "browse" && (t.id !== "markup" || canMarkup)).map((t) => {
606
912
  const Icon = PDF_TOOL_ICONS[t.id];
607
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { role: "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
913
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { role: "none", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
608
914
  "button",
609
915
  {
610
916
  type: "button",
611
917
  role: "menuitem",
612
918
  "aria-current": t.id === tool ? "true" : void 0,
613
- className: (0, import_cn.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"),
919
+ className: (0, import_cn2.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"),
614
920
  onClick: () => onPick(t.id),
615
921
  children: [
616
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "size-4 shrink-0" }),
617
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "flex-1 whitespace-nowrap", children: t.label }),
618
- t.status === "soon" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
922
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: "size-4 shrink-0" }),
923
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "flex-1 whitespace-nowrap", children: t.label }),
924
+ t.status === "soon" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
619
925
  ]
620
926
  }
621
927
  ) }, t.id);
@@ -623,50 +929,58 @@ function ToolRows({ tool, hostTools, onPick }) {
623
929
  ] });
624
930
  }
625
931
  function PdfWorkspace({ host, className }) {
626
- const rootRef = (0, import_react3.useRef)(null);
627
- const scrollerRef = (0, import_react3.useRef)(null);
932
+ const rootRef = (0, import_react4.useRef)(null);
933
+ const scrollerRef = (0, import_react4.useRef)(null);
628
934
  const rootSize = useWidth(rootRef);
629
935
  const viewSize = useWidth(scrollerRef);
630
936
  const compact = rootSize.w < COMPACT_BELOW_PX;
631
937
  const online = host.online !== false;
632
- const [pageCount, setPageCount] = (0, import_react3.useState)(0);
633
- const [history, setHistory] = (0, import_react3.useState)(() => startHistory(freshDecisions(0)));
938
+ const [pageCount, setPageCount] = (0, import_react4.useState)(0);
939
+ const [history, setHistory] = (0, import_react4.useState)(() => startHistory(freshDecisions(0)));
634
940
  const decisions = history.present.value;
635
941
  const dirty = isDirty(decisions);
636
- const [tool, setTool] = (0, import_react3.useState)("browse");
637
- const [hostToolId, setHostToolId] = (0, import_react3.useState)(null);
638
- const hostTools = (0, import_react3.useMemo)(() => host.tools ?? [], [host.tools]);
942
+ const [tool, setTool] = (0, import_react4.useState)("browse");
943
+ const [hostToolId, setHostToolId] = (0, import_react4.useState)(null);
944
+ const hostTools = (0, import_react4.useMemo)(() => host.tools ?? [], [host.tools]);
639
945
  const hostTool = hostToolId ? hostTools.find((t) => t.id === hostToolId) ?? null : null;
640
946
  const activeToolKey = hostTool ? `host:${hostTool.id}` : tool;
641
- const [leftOpen, setLeftOpen] = (0, import_react3.useState)(true);
642
- const [rightOpen, setRightOpen] = (0, import_react3.useState)(true);
643
- const [leftTab, setLeftTab] = (0, import_react3.useState)("pages");
644
- const [read, setRead] = (0, import_react3.useState)(false);
645
- const [spread, setSpread] = (0, import_react3.useState)("single");
646
- const [zoomMode, setZoomMode] = (0, import_react3.useState)("fit-width");
647
- const [customZoom, setCustomZoom] = (0, import_react3.useState)(1);
648
- const [selected, setSelected] = (0, import_react3.useState)([]);
649
- const [currentPage, setCurrentPage] = (0, import_react3.useState)(1);
650
- const [pageDraft, setPageDraft] = (0, import_react3.useState)("1");
651
- const [findOpen, setFindOpen] = (0, import_react3.useState)(false);
652
- const [findFocus, setFindFocus] = (0, import_react3.useState)(0);
653
- const [pop, setPop] = (0, import_react3.useState)(null);
654
- const [sheet, setSheet] = (0, import_react3.useState)(null);
655
- const [toolQuery, setToolQuery] = (0, import_react3.useState)("");
656
- const [busy, setBusy] = (0, import_react3.useState)(false);
657
- const [error, setError] = (0, import_react3.useState)(null);
658
- const [result, setResult] = (0, import_react3.useState)(null);
659
- const [reloadKey, setReloadKey] = (0, import_react3.useState)(0);
660
- const [sizeVersion, setSizeVersion] = (0, import_react3.useState)(0);
661
- const sizes = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
662
- const [editingName, setEditingName] = (0, import_react3.useState)(false);
663
- const [nameDraft, setNameDraft] = (0, import_react3.useState)(host.file.name);
664
- const [printMode, setPrintMode] = (0, import_react3.useState)("all");
665
- const [printRange, setPrintRange] = (0, import_react3.useState)("");
666
- const selectedPages = (0, import_react3.useMemo)(() => selected.map(Number).sort((a, b) => a - b), [selected]);
947
+ const [leftOpen, setLeftOpen] = (0, import_react4.useState)(true);
948
+ const [rightOpen, setRightOpen] = (0, import_react4.useState)(true);
949
+ const [leftTab, setLeftTab] = (0, import_react4.useState)("pages");
950
+ const [read, setRead] = (0, import_react4.useState)(false);
951
+ const [spread, setSpread] = (0, import_react4.useState)("single");
952
+ const [zoomMode, setZoomMode] = (0, import_react4.useState)("fit-width");
953
+ const [customZoom, setCustomZoom] = (0, import_react4.useState)(1);
954
+ const [selected, setSelected] = (0, import_react4.useState)([]);
955
+ const [currentPage, setCurrentPage] = (0, import_react4.useState)(1);
956
+ const [pageDraft, setPageDraft] = (0, import_react4.useState)("1");
957
+ const [findOpen, setFindOpen] = (0, import_react4.useState)(false);
958
+ const [findFocus, setFindFocus] = (0, import_react4.useState)(0);
959
+ const [pop, setPop] = (0, import_react4.useState)(null);
960
+ const [sheet, setSheet] = (0, import_react4.useState)(null);
961
+ const [toolQuery, setToolQuery] = (0, import_react4.useState)("");
962
+ const [busy, setBusy] = (0, import_react4.useState)(false);
963
+ const [error, setError] = (0, import_react4.useState)(null);
964
+ const [result, setResult] = (0, import_react4.useState)(null);
965
+ const [reloadKey, setReloadKey] = (0, import_react4.useState)(0);
966
+ const [sizeVersion, setSizeVersion] = (0, import_react4.useState)(0);
967
+ const sizes = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
968
+ const [editingName, setEditingName] = (0, import_react4.useState)(false);
969
+ const [nameDraft, setNameDraft] = (0, import_react4.useState)(host.file.name);
970
+ const [printMode, setPrintMode] = (0, import_react4.useState)("all");
971
+ const [printRange, setPrintRange] = (0, import_react4.useState)("");
972
+ const canMarkup = Boolean(host.saveMarkup);
973
+ const [engine, setEngine] = (0, import_react4.useState)(null);
974
+ const [snap, setSnap] = (0, import_react4.useState)(null);
975
+ const [timeline, setTimeline] = (0, import_react4.useState)(emptyTimeline);
976
+ const [markupTool, setMarkupTool] = (0, import_react4.useState)("highlight");
977
+ const [markupBusy, setMarkupBusy] = (0, import_react4.useState)(false);
978
+ const [markupError, setMarkupError] = (0, import_react4.useState)(null);
979
+ const markupPending = snap?.pending ?? 0;
980
+ const selectedPages = (0, import_react4.useMemo)(() => selected.map(Number).sort((a, b) => a - b), [selected]);
667
981
  const toolDef = pdfTool(tool);
668
982
  const first = sizes.current.get(1);
669
- const fitPage = (0, import_react3.useMemo)(() => {
983
+ const fitPage = (0, import_react4.useMemo)(() => {
670
984
  void sizeVersion;
671
985
  if (!first) return 1;
672
986
  const ratio = first.h / first.w;
@@ -675,7 +989,7 @@ function PdfWorkspace({ host, className }) {
675
989
  return clampZoom(availH * cols / (Math.max(1, viewSize.w) * ratio));
676
990
  }, [first, spread, viewSize.h, viewSize.w, sizeVersion]);
677
991
  const zoom = zoomMode === "fit-width" ? 1 : zoomMode === "fit-page" ? fitPage : customZoom;
678
- const stepZoom = (0, import_react3.useCallback)(
992
+ const stepZoom = (0, import_react4.useCallback)(
679
993
  (dir) => {
680
994
  const cur = zoomMode === "fit-width" ? 1 : zoomMode === "fit-page" ? fitPage : customZoom;
681
995
  const next = dir > 0 ? ZOOM_STEPS.find((s) => s > cur + 0.01) : [...ZOOM_STEPS].reverse().find((s) => s < cur - 0.01);
@@ -685,7 +999,7 @@ function PdfWorkspace({ host, className }) {
685
999
  },
686
1000
  [zoomMode, fitPage, customZoom]
687
1001
  );
688
- const goToPage = (0, import_react3.useCallback)(
1002
+ const goToPage = (0, import_react4.useCallback)(
689
1003
  (n) => {
690
1004
  const page = Math.min(Math.max(1, Math.round(n)), Math.max(1, pageCount));
691
1005
  setCurrentPage(page);
@@ -695,7 +1009,7 @@ function PdfWorkspace({ host, className }) {
695
1009
  },
696
1010
  [pageCount]
697
1011
  );
698
- (0, import_react3.useEffect)(() => {
1012
+ (0, import_react4.useEffect)(() => {
699
1013
  const node = scrollerRef.current;
700
1014
  if (!node) return void 0;
701
1015
  let raf = 0;
@@ -722,34 +1036,37 @@ function PdfWorkspace({ host, className }) {
722
1036
  cancelAnimationFrame(raf);
723
1037
  };
724
1038
  }, [pageCount]);
725
- const commit = (0, import_react3.useCallback)((label, next) => setHistory((h) => pushStep(h, label, next)), []);
726
- const targets = (0, import_react3.useCallback)(() => selectedPages.length ? selectedPages : [currentPage], [selectedPages, currentPage]);
727
- const turn = (0, import_react3.useCallback)(
1039
+ const commit = (0, import_react4.useCallback)((label, next) => {
1040
+ setHistory((h) => pushStep(h, label, next));
1041
+ setTimeline((t) => recordStep(t, "page", label));
1042
+ }, []);
1043
+ const targets = (0, import_react4.useCallback)(() => selectedPages.length ? selectedPages : [currentPage], [selectedPages, currentPage]);
1044
+ const turn = (0, import_react4.useCallback)(
728
1045
  (by) => {
729
1046
  const t = targets();
730
1047
  commit(`Rotate ${t.length === 1 ? `page ${t[0]}` : `${t.length} pages`}`, turnPages(decisions, t, by));
731
1048
  },
732
1049
  [targets, commit, decisions]
733
1050
  );
734
- const del = (0, import_react3.useCallback)(() => {
1051
+ const del = (0, import_react4.useCallback)(() => {
735
1052
  const t = targets();
736
1053
  commit(`Delete ${t.length === 1 ? `page ${t[0]}` : `${t.length} pages`}`, deletePages(decisions, t));
737
1054
  }, [targets, commit, decisions]);
738
- const restore = (0, import_react3.useCallback)(() => {
1055
+ const restore = (0, import_react4.useCallback)(() => {
739
1056
  const t = targets();
740
1057
  commit(`Restore ${t.length === 1 ? `page ${t[0]}` : `${t.length} pages`}`, restorePages(decisions, t));
741
1058
  }, [targets, commit, decisions]);
742
- const onDocumentLoad = (0, import_react3.useCallback)((n) => {
1059
+ const onDocumentLoad = (0, import_react4.useCallback)((n) => {
743
1060
  setPageCount(n);
744
1061
  setHistory((h) => h.present.value.order.length === n ? h : startHistory(freshDecisions(n)));
745
1062
  }, []);
746
- const onPageSize = (0, import_react3.useCallback)((page, w, h) => {
1063
+ const onPageSize = (0, import_react4.useCallback)((page, w, h) => {
747
1064
  const prev = sizes.current.get(page);
748
1065
  if (prev && prev.w === w && prev.h === h) return;
749
1066
  sizes.current.set(page, { w, h });
750
1067
  if (page === 1) queueMicrotask(() => setSizeVersion((v) => v + 1));
751
1068
  }, []);
752
- const fail = (0, import_react3.useCallback)(
1069
+ const fail = (0, import_react4.useCallback)(
753
1070
  (e) => {
754
1071
  const message = e instanceof Error ? e.message : "That did not work.";
755
1072
  setError(message);
@@ -757,7 +1074,7 @@ function PdfWorkspace({ host, className }) {
757
1074
  },
758
1075
  [host]
759
1076
  );
760
- const runTool = (0, import_react3.useCallback)(
1077
+ const runTool = (0, import_react4.useCallback)(
761
1078
  async (request) => {
762
1079
  if (!online) {
763
1080
  setError("You are offline. Changes are not queued; try again when you are back online.");
@@ -783,12 +1100,109 @@ function PdfWorkspace({ host, className }) {
783
1100
  },
784
1101
  [host, online, pageCount, fail]
785
1102
  );
786
- const apply = (0, import_react3.useCallback)(() => {
1103
+ const apply = (0, import_react4.useCallback)(() => {
787
1104
  const ops = compilePageOps(decisions);
788
- if (ops.length) void runTool({ tool: "apply-pages", ops });
789
- }, [decisions, runTool]);
790
- const discard = (0, import_react3.useCallback)(() => setHistory(startHistory(freshDecisions(pageCount))), [pageCount]);
791
- const chooseTool = (0, import_react3.useCallback)(
1105
+ if (!ops.length) return;
1106
+ if (markupPending) {
1107
+ setError("Save or discard your markup first: applying page changes reloads the file.");
1108
+ return;
1109
+ }
1110
+ setTimeline((t) => dropOrigin(t, "page"));
1111
+ void runTool({ tool: "apply-pages", ops });
1112
+ }, [decisions, runTool, markupPending]);
1113
+ const discard = (0, import_react4.useCallback)(() => {
1114
+ setHistory(startHistory(freshDecisions(pageCount)));
1115
+ setTimeline((t) => dropOrigin(t, "page"));
1116
+ }, [pageCount]);
1117
+ const onMarkupEngine = (0, import_react4.useCallback)((e) => {
1118
+ setEngine(e);
1119
+ setSnap(e ? e.getSnapshot() : null);
1120
+ }, []);
1121
+ (0, import_react4.useEffect)(() => {
1122
+ if (!engine) return void 0;
1123
+ const offSnap = engine.subscribe(setSnap);
1124
+ const offHistory = engine.onHistory((ev) => {
1125
+ if (ev.type === "step") setTimeline((t) => recordStep(t, "markup", ev.label));
1126
+ else if (ev.external) setTimeline((t) => ev.type === "undo" ? markupUndoneElsewhere(t) : markupRedoneElsewhere(t));
1127
+ });
1128
+ return () => {
1129
+ offSnap();
1130
+ offHistory();
1131
+ };
1132
+ }, [engine]);
1133
+ const markupOn = tool === "markup" && !hostTool && !dirty;
1134
+ const markupEngineTool = pdfMarkupTool(markupTool)?.engineTool ?? "highlight";
1135
+ (0, import_react4.useEffect)(() => {
1136
+ if (engine) void engine.setTool(markupOn ? markupEngineTool : "none");
1137
+ }, [engine, markupOn, markupEngineTool]);
1138
+ (0, import_react4.useEffect)(() => {
1139
+ engine?.setCurrentPage(currentPage);
1140
+ }, [engine, currentPage]);
1141
+ const saveMarkup = (0, import_react4.useCallback)(async () => {
1142
+ if (!engine || !host.saveMarkup || !markupPending || markupBusy) return;
1143
+ if (!online) {
1144
+ setMarkupError("You are offline. Your markup is kept here; save when you are back online.");
1145
+ return;
1146
+ }
1147
+ setMarkupBusy(true);
1148
+ setMarkupError(null);
1149
+ try {
1150
+ const summary = engine.getSnapshot().summary;
1151
+ const bytes = await engine.save();
1152
+ const out = await host.saveMarkup(bytes, summary);
1153
+ setResult(out);
1154
+ setTimeline((t) => dropOrigin(t, "markup"));
1155
+ sizes.current.clear();
1156
+ setReloadKey((k) => k + 1);
1157
+ } catch (e) {
1158
+ const message = e instanceof Error ? e.message : "The markup was not saved.";
1159
+ setMarkupError(message);
1160
+ host.notify?.(message, "error");
1161
+ } finally {
1162
+ setMarkupBusy(false);
1163
+ }
1164
+ }, [engine, host, markupPending, markupBusy, online]);
1165
+ const discardMarkup = (0, import_react4.useCallback)(() => {
1166
+ setTimeline((t) => dropOrigin(t, "markup"));
1167
+ setMarkupError(null);
1168
+ setReloadKey((k) => k + 1);
1169
+ }, []);
1170
+ (0, import_react4.useEffect)(() => {
1171
+ if (!markupPending) return void 0;
1172
+ const onBeforeUnload = (event) => {
1173
+ event.preventDefault();
1174
+ event.returnValue = "";
1175
+ };
1176
+ window.addEventListener("beforeunload", onBeforeUnload);
1177
+ return () => window.removeEventListener("beforeunload", onBeforeUnload);
1178
+ }, [markupPending]);
1179
+ const undoAll = (0, import_react4.useCallback)(() => {
1180
+ const step = nextUndo(timeline);
1181
+ if (step?.origin === "markup" && engine) {
1182
+ setTimeline(stepBack);
1183
+ engine.undo();
1184
+ return;
1185
+ }
1186
+ if (step) setTimeline(stepBack);
1187
+ setHistory((h) => undo(h));
1188
+ }, [timeline, engine]);
1189
+ const redoAll = (0, import_react4.useCallback)(() => {
1190
+ const step = nextRedo(timeline);
1191
+ if (step?.origin === "markup" && engine) {
1192
+ setTimeline(stepForward);
1193
+ engine.redo();
1194
+ return;
1195
+ }
1196
+ if (step) setTimeline(stepForward);
1197
+ setHistory((h) => redo(h));
1198
+ }, [timeline, engine]);
1199
+ const undoStep = nextUndo(timeline);
1200
+ const redoStep = nextRedo(timeline);
1201
+ const undoText = undoStep ? `Undo ${undoStep.label}` : undoLabel(history) ? `Undo ${undoLabel(history)}` : "Undo";
1202
+ const redoText = redoStep ? `Redo ${redoStep.label}` : redoLabel(history) ? `Redo ${redoLabel(history)}` : "Redo";
1203
+ const canUndoAll = Boolean(undoStep) || canUndo(history);
1204
+ const canRedoAll = Boolean(redoStep) || canRedo(history);
1205
+ const chooseTool = (0, import_react4.useCallback)(
792
1206
  (id) => {
793
1207
  if (id.startsWith("host:")) {
794
1208
  setHostToolId(id.slice(5));
@@ -799,7 +1213,7 @@ function PdfWorkspace({ host, className }) {
799
1213
  return;
800
1214
  }
801
1215
  const def = pdfTool(id);
802
- if (!def) return;
1216
+ if (!def || def.id === "markup" && !host.saveMarkup) return;
803
1217
  setHostToolId(null);
804
1218
  if (def.status === "soon") {
805
1219
  announceSoon(comingSoonIdOf(def.id)).catch(fail);
@@ -817,26 +1231,26 @@ function PdfWorkspace({ host, className }) {
817
1231
  },
818
1232
  [host, compact, fail]
819
1233
  );
820
- const railGroups = (0, import_react3.useMemo)(() => {
1234
+ const railGroups = (0, import_react4.useMemo)(() => {
821
1235
  const groups = [[], [], []];
822
1236
  for (const t of PDF_TOOLS) {
823
- if (t.status !== "ready") continue;
1237
+ if (t.status !== "ready" || t.id === "markup" && !canMarkup) continue;
824
1238
  groups[t.group].push({ id: t.id, label: t.label, icon: PDF_TOOL_ICONS[t.id], ...t.key ? { shortcut: t.key.toUpperCase() } : null });
825
1239
  }
826
1240
  for (const t of hostTools) groups[t.group ?? 1].push({ id: `host:${t.id}`, label: t.label, icon: t.icon });
827
- groups[2].push({ id: ALL_TOOLS, label: "All tools", icon: import_icons2.MoreHorizontalIcon });
1241
+ groups[2].push({ id: ALL_TOOLS, label: "All tools", icon: import_icons3.MoreHorizontalIcon });
828
1242
  return groups.filter((g) => g.length);
829
- }, [hostTools]);
830
- const runAction = (0, import_react3.useCallback)(
1243
+ }, [hostTools, canMarkup]);
1244
+ const runAction = (0, import_react4.useCallback)(
831
1245
  (action) => {
832
1246
  if (action.startsWith("tool:")) return chooseTool(action.slice(5));
833
1247
  switch (action) {
834
1248
  case "undo":
835
- return setHistory((h) => undo(h));
1249
+ return undoAll();
836
1250
  case "redo":
837
- return setHistory((h) => redo(h));
1251
+ return redoAll();
838
1252
  case "save":
839
- return apply();
1253
+ return markupPending ? void saveMarkup() : apply();
840
1254
  case "find":
841
1255
  setFindOpen(true);
842
1256
  setFindFocus((n) => n + 1);
@@ -881,7 +1295,7 @@ function PdfWorkspace({ host, className }) {
881
1295
  return void 0;
882
1296
  }
883
1297
  },
884
- [chooseTool, apply, host.print, stepZoom, goToPage, currentPage, pageCount, turn, del, decisions.order]
1298
+ [chooseTool, apply, host.print, stepZoom, goToPage, currentPage, pageCount, turn, del, decisions.order, undoAll, redoAll, markupPending, saveMarkup]
885
1299
  );
886
1300
  const onKeyDown = (e) => {
887
1301
  const t = e.target;
@@ -893,19 +1307,30 @@ function PdfWorkspace({ host, className }) {
893
1307
  else if (sheet) setSheet(null);
894
1308
  return;
895
1309
  }
1310
+ if (markupOn) {
1311
+ const pick = matchMarkupKey(e.nativeEvent, typing);
1312
+ if (pick && pdfMarkupTool(pick)?.status === "ready" && (pick !== "signature" || host.signaturePad)) {
1313
+ e.preventDefault();
1314
+ setMarkupTool(pick);
1315
+ return;
1316
+ }
1317
+ }
896
1318
  const action = matchShortcut(e.nativeEvent, typing);
897
1319
  if (!action) return;
1320
+ if (markupOn && t.closest("[data-pdf-thumbnails]") === null && (action === "delete-pages" || action === "page-next" || action === "page-prev" || action === "select-all-pages")) return;
898
1321
  if ((action === "page-next" || action === "page-prev") && t !== scrollerRef.current && t !== rootRef.current) return;
899
1322
  if (action === "delete-pages" && t.closest("[data-pdf-thumbnails]") === null && t !== scrollerRef.current) return;
900
1323
  e.preventDefault();
1324
+ if (action === "undo" || action === "redo") e.stopPropagation();
901
1325
  runAction(action);
902
1326
  };
903
- const saveState = !online ? "Offline" : busy ? "Saving" : error && dirty ? "Not saved" : dirty ? "Unsaved" : "Saved";
904
- const savePill = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1327
+ const unsaved = dirty || markupPending > 0;
1328
+ const saveState = !online ? "Offline" : busy || markupBusy ? "Saving" : error && dirty || markupError && markupPending ? "Not saved" : unsaved ? "Unsaved" : "Saved";
1329
+ const savePill = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
905
1330
  "span",
906
1331
  {
907
1332
  role: "status",
908
- className: (0, import_cn.cn)(
1333
+ className: (0, import_cn2.cn)(
909
1334
  "inline-flex h-6 shrink-0 items-center justify-center whitespace-nowrap rounded-full border px-2 text-xs",
910
1335
  saveState === "Unsaved" || saveState === "Offline" || saveState === "Not saved" ? "border-border text-foreground" : "border-border text-muted-foreground",
911
1336
  busy && "animate-pulse"
@@ -913,13 +1338,13 @@ function PdfWorkspace({ host, className }) {
913
1338
  children: saveState
914
1339
  }
915
1340
  );
916
- const iconBtn = (label, icon, onClick, opts) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { 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 });
1341
+ const iconBtn = (label, icon, onClick, opts) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_controls5.Button, { 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 });
917
1342
  const toolHits = toolQuery.trim() ? [
918
1343
  ...hostTools.map((t) => ({ id: `host:${t.id}`, label: t.label, status: "ready" })),
919
1344
  ...PDF_TOOLS
920
1345
  ].filter((t) => t.label.toLowerCase().includes(toolQuery.trim().toLowerCase())) : [];
921
- const nameNode = editingName && host.rename ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
922
- import_controls4.Field,
1346
+ const nameNode = editingName && host.rename ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1347
+ import_controls5.Field,
923
1348
  {
924
1349
  autoFocus: true,
925
1350
  value: nameDraft,
@@ -938,7 +1363,7 @@ function PdfWorkspace({ host, className }) {
938
1363
  },
939
1364
  width: "lg"
940
1365
  }
941
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1366
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
942
1367
  "button",
943
1368
  {
944
1369
  type: "button",
@@ -948,18 +1373,36 @@ function PdfWorkspace({ host, className }) {
948
1373
  children: host.file.name
949
1374
  }
950
1375
  );
951
- const rightPanel = hostTool ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex h-full min-h-0 flex-col", "data-pdf-host-tool": hostTool.id, children: [
952
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
953
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-sm font-medium", children: hostTool.label }),
954
- hostTool.hint ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: hostTool.hint }) : null
1376
+ const rightPanel = hostTool ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex h-full min-h-0 flex-col", "data-pdf-host-tool": hostTool.id, children: [
1377
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
1378
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-sm font-medium", children: hostTool.label }),
1379
+ hostTool.hint ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-xs text-muted-foreground", children: hostTool.hint }) : null
955
1380
  ] }),
956
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "min-h-0 flex-1 overflow-hidden", children: hostTool.render({ currentPage, pageCount, selected: selectedPages, goToPage, reload: () => setReloadKey((k) => k + 1) }) })
957
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex h-full min-h-0 flex-col", children: [
958
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
959
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-sm font-medium", children: toolDef.label }),
960
- toolDef.hint ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-xs text-muted-foreground", children: toolDef.hint }) : null
1381
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "min-h-0 flex-1 overflow-hidden", children: hostTool.render({ currentPage, pageCount, selected: selectedPages, goToPage, reload: () => setReloadKey((k) => k + 1) }) })
1382
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex h-full min-h-0 flex-col", children: [
1383
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
1384
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-sm font-medium", children: toolDef.label }),
1385
+ toolDef.hint ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-xs text-muted-foreground", children: toolDef.hint }) : null
961
1386
  ] }),
962
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto p-3", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1387
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto p-3", children: tool === "markup" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1388
+ MarkupPanel,
1389
+ {
1390
+ host,
1391
+ engine,
1392
+ snapshot: snap,
1393
+ active: markupTool,
1394
+ onPick: setMarkupTool,
1395
+ blocked: dirty ? "Apply or discard the page changes first." : null,
1396
+ busy: markupBusy,
1397
+ online,
1398
+ error: markupError,
1399
+ onSave: () => void saveMarkup(),
1400
+ onDiscard: discardMarkup,
1401
+ onSoon: (id) => void announceSoon(id).catch(fail),
1402
+ goToPage,
1403
+ reloadKey
1404
+ }
1405
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
963
1406
  ToolPanel,
964
1407
  {
965
1408
  tool: toolDef,
@@ -983,7 +1426,7 @@ function PdfWorkspace({ host, className }) {
983
1426
  }
984
1427
  ) })
985
1428
  ] });
986
- const leftPanel = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1429
+ const leftPanel = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
987
1430
  LeftPanel,
988
1431
  {
989
1432
  host,
@@ -1002,13 +1445,13 @@ function PdfWorkspace({ host, className }) {
1002
1445
  }
1003
1446
  );
1004
1447
  const viewerPage = hostTool?.followsPage ? currentPage : 1;
1005
- const viewer = (0, import_react3.useMemo)(
1006
- () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react3.Fragment, { children: host.renderViewer({ zoom, spread, rotations: decisions.turns, reloadKey, currentPage: viewerPage, onDocumentLoad, onPageSize }) }, reloadKey),
1007
- [host, reloadKey, zoom, spread, decisions.turns, viewerPage, onDocumentLoad, onPageSize]
1448
+ const viewer = (0, import_react4.useMemo)(
1449
+ () => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react4.Fragment, { children: host.renderViewer({ zoom, spread, rotations: decisions.turns, reloadKey, currentPage: viewerPage, onDocumentLoad, onPageSize, ...canMarkup ? { onMarkupEngine } : null }) }, reloadKey),
1450
+ [host, reloadKey, zoom, spread, decisions.turns, viewerPage, onDocumentLoad, onPageSize, canMarkup, onMarkupEngine]
1008
1451
  );
1009
1452
  const showChrome = !read;
1010
1453
  const popCard = "absolute right-2 top-11 z-30 rounded-lg border border-border bg-popover p-3 text-popover-foreground shadow-lg";
1011
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1454
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1012
1455
  "div",
1013
1456
  {
1014
1457
  ref: rootRef,
@@ -1016,18 +1459,18 @@ function PdfWorkspace({ host, className }) {
1016
1459
  "data-pdf-workspace": "",
1017
1460
  "data-compact": compact ? "" : void 0,
1018
1461
  onKeyDown,
1019
- className: (0, import_cn.cn)("relative flex h-full min-h-0 w-full flex-col overflow-hidden bg-background text-foreground outline-none", className),
1462
+ className: (0, import_cn2.cn)("relative flex h-full min-h-0 w-full flex-col overflow-hidden bg-background text-foreground outline-none", className),
1020
1463
  children: [
1021
- showChrome ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: BAR, "data-pdf-topbar": "", children: [
1022
- iconBtn("Page thumbnails", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.PanelLeftIcon, {}), () => compact ? setSheet("pages") : setLeftOpen((v) => !v), { pressed: !compact && leftOpen }),
1464
+ showChrome ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: BAR, "data-pdf-topbar": "", children: [
1465
+ iconBtn("Page thumbnails", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.PanelLeftIcon, {}), () => compact ? setSheet("pages") : setLeftOpen((v) => !v), { pressed: !compact && leftOpen }),
1023
1466
  nameNode,
1024
1467
  savePill,
1025
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1" }),
1026
- !compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative w-56", children: [
1027
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1028
- import_controls4.Field,
1468
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-1" }),
1469
+ !compact ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative w-56", children: [
1470
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1471
+ import_controls5.Field,
1029
1472
  {
1030
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.SearchIcon, {}),
1473
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.SearchIcon, {}),
1031
1474
  value: toolQuery,
1032
1475
  "aria-label": "Search tools",
1033
1476
  placeholder: "Search tools",
@@ -1045,7 +1488,7 @@ function PdfWorkspace({ host, className }) {
1045
1488
  }
1046
1489
  }
1047
1490
  ),
1048
- pop === "tool-search" && toolHits.length ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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__ */ (0, import_jsx_runtime3.jsx)("li", { role: "option", "aria-selected": false, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1491
+ pop === "tool-search" && toolHits.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)("li", { role: "option", "aria-selected": false, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1049
1492
  "button",
1050
1493
  {
1051
1494
  type: "button",
@@ -1057,25 +1500,25 @@ function PdfWorkspace({ host, className }) {
1057
1500
  setPop(null);
1058
1501
  },
1059
1502
  children: [
1060
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t.label }),
1061
- t.status === "soon" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
1503
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t.label }),
1504
+ t.status === "soon" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-muted-foreground", children: "Coming soon" }) : null
1062
1505
  ]
1063
1506
  }
1064
1507
  ) }, t.id)) }) : null
1065
1508
  ] }) : null,
1066
- iconBtn(undoLabel(history) ? `Undo ${undoLabel(history)}` : "Undo", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Undo2Icon, {}), () => runAction("undo"), { disabled: !canUndo(history) }),
1067
- iconBtn(redoLabel(history) ? `Redo ${redoLabel(history)}` : "Redo", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Undo2Icon, { className: "-scale-x-100" }), () => runAction("redo"), { disabled: !canRedo(history) }),
1068
- !compact && iconBtn("Find", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.SearchIcon, {}), () => runAction("find"), { pressed: findOpen }),
1069
- host.share && !compact ? iconBtn("Share", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Share2Icon, {}), () => host.share()) : null,
1070
- iconBtn("Download", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.DownloadIcon, {}), () => host.download()),
1071
- host.print && !compact ? iconBtn("Print", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.PrinterIcon, {}), () => setPop(pop === "print" ? null : "print"), { pressed: pop === "print" }) : null,
1072
- !compact ? iconBtn("Keyboard shortcuts", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.KeyboardIcon, {}), () => runAction("shortcuts"), { pressed: pop === "shortcuts" }) : null,
1073
- compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "outline", onClick: () => setSheet("tools"), children: "Tools" }) : iconBtn("Properties panel", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.PanelRightIcon, {}), () => setRightOpen((v) => !v), { pressed: rightOpen })
1509
+ iconBtn(undoText, /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.Undo2Icon, {}), () => runAction("undo"), { disabled: !canUndoAll }),
1510
+ iconBtn(redoText, /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.Undo2Icon, { className: "-scale-x-100" }), () => runAction("redo"), { disabled: !canRedoAll }),
1511
+ !compact && iconBtn("Find", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.SearchIcon, {}), () => runAction("find"), { pressed: findOpen }),
1512
+ host.share && !compact ? iconBtn("Share", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.Share2Icon, {}), () => host.share()) : null,
1513
+ iconBtn("Download", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.DownloadIcon, {}), () => host.download()),
1514
+ host.print && !compact ? iconBtn("Print", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.PrinterIcon, {}), () => setPop(pop === "print" ? null : "print"), { pressed: pop === "print" }) : null,
1515
+ !compact ? iconBtn("Keyboard shortcuts", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.KeyboardIcon, {}), () => runAction("shortcuts"), { pressed: pop === "shortcuts" }) : null,
1516
+ compact ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_controls5.Button, { variant: "outline", onClick: () => setSheet("tools"), children: "Tools" }) : iconBtn("Properties panel", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.PanelRightIcon, {}), () => setRightOpen((v) => !v), { pressed: rightOpen })
1074
1517
  ] }) : null,
1075
- showChrome ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: BAR, "data-pdf-viewbar": "", children: [
1076
- iconBtn("Previous page", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.ChevronLeftIcon, {}), () => goToPage(currentPage - 1), { disabled: currentPage <= 1 }),
1077
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1078
- import_controls4.Field,
1518
+ showChrome ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: BAR, "data-pdf-viewbar": "", children: [
1519
+ iconBtn("Previous page", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.ChevronLeftIcon, {}), () => goToPage(currentPage - 1), { disabled: currentPage <= 1 }),
1520
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1521
+ import_controls5.Field,
1079
1522
  {
1080
1523
  type: "number",
1081
1524
  min: 1,
@@ -1088,17 +1531,17 @@ function PdfWorkspace({ host, className }) {
1088
1531
  className: compact ? "w-12" : "w-16"
1089
1532
  }
1090
1533
  ),
1091
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: [
1534
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: [
1092
1535
  "of ",
1093
1536
  pageCount || "-"
1094
1537
  ] }),
1095
- iconBtn("Next page", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.ChevronRightIcon, {}), () => goToPage(currentPage + 1), { disabled: currentPage >= pageCount }),
1096
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mx-1 h-5 w-px bg-border" }),
1097
- iconBtn("Zoom out", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.MinusIcon, {}), () => stepZoom(-1)),
1098
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: (0, import_cn.cn)("min-w-[3rem] text-center text-xs tabular-nums", compact && "hidden"), "data-pdf-zoom": "", children: `${Math.round(zoom * 100)}%` }),
1099
- iconBtn("Zoom in", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.PlusIcon, {}), () => stepZoom(1)),
1100
- !compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1101
- import_controls4.SegmentedControl,
1538
+ iconBtn("Next page", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.ChevronRightIcon, {}), () => goToPage(currentPage + 1), { disabled: currentPage >= pageCount }),
1539
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-1 h-5 w-px bg-border" }),
1540
+ iconBtn("Zoom out", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.MinusIcon, {}), () => stepZoom(-1)),
1541
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: (0, import_cn2.cn)("min-w-[3rem] text-center text-xs tabular-nums", compact && "hidden"), "data-pdf-zoom": "", children: `${Math.round(zoom * 100)}%` }),
1542
+ iconBtn("Zoom in", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.PlusIcon, {}), () => stepZoom(1)),
1543
+ !compact ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1544
+ import_controls5.SegmentedControl,
1102
1545
  {
1103
1546
  value: zoomMode === "custom" ? null : zoomMode,
1104
1547
  onValueChange: (v) => setZoomMode(v),
@@ -1106,39 +1549,40 @@ function PdfWorkspace({ host, className }) {
1106
1549
  "aria-label": "Fit"
1107
1550
  }
1108
1551
  ) : null,
1109
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1" }),
1110
- !compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1111
- iconBtn("Rotate left", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.RotateCcwIcon, {}), () => turn(-90)),
1112
- iconBtn("Rotate right", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.RotateCwIcon, {}), () => turn(90)),
1113
- iconBtn("Delete page", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Trash2Icon, {}), del, { disabled: decisions.order.length - decisions.deleted.length <= 1 }),
1114
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mx-1 h-5 w-px bg-border" }),
1115
- iconBtn("Two pages side by side", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Columns2Icon, {}), () => runAction("two-up"), { pressed: spread === "two-up" })
1552
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-1" }),
1553
+ !compact ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1554
+ iconBtn("Rotate left", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.RotateCcwIcon, {}), () => turn(-90)),
1555
+ iconBtn("Rotate right", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.RotateCwIcon, {}), () => turn(90)),
1556
+ iconBtn("Delete page", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.Trash2Icon, {}), del, { disabled: decisions.order.length - decisions.deleted.length <= 1 }),
1557
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-1 h-5 w-px bg-border" }),
1558
+ iconBtn("Two pages side by side", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.Columns2Icon, {}), () => runAction("two-up"), { pressed: spread === "two-up" })
1116
1559
  ] }) : null,
1117
- iconBtn("Reading mode", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.ExpandIcon, {}), () => setRead(true))
1560
+ iconBtn("Reading mode", /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.ExpandIcon, {}), () => setRead(true))
1118
1561
  ] }) : null,
1119
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative flex min-h-0 flex-1", children: [
1120
- showChrome && !compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex w-10 shrink-0 flex-col items-center border-r border-border bg-background py-2", "data-pdf-rail": "", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.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,
1121
- showChrome && !compact && leftOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("aside", { className: "w-56 shrink-0 border-r border-border bg-background", "aria-label": "Pages", "data-pdf-left": "", children: leftPanel }) : null,
1122
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("main", { className: "relative flex min-w-0 flex-1 flex-col", children: [
1123
- findOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute right-4 top-2 z-20", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_RenderedFindBar.RenderedFindBar, { rootRef: scrollerRef, onClose: () => setFindOpen(false), label: "Find in document", focusRequest: findFocus, compact: true }) }) : null,
1124
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: scrollerRef, tabIndex: 0, "data-pdf-scroller": "", className: "min-h-0 flex-1 overflow-auto bg-muted outline-none", children: viewer }),
1125
- read ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute right-3 top-3 z-20", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.XIcon, {}), onClick: () => setRead(false), children: "Exit reading mode" }) }) : null
1562
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative flex min-h-0 flex-1", children: [
1563
+ showChrome && !compact ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex w-10 shrink-0 flex-col items-center border-r border-border bg-background py-2", "data-pdf-rail": "", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_controls5.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,
1564
+ showChrome && !compact && leftOpen ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("aside", { className: "w-56 shrink-0 border-r border-border bg-background", "aria-label": "Pages", "data-pdf-left": "", children: leftPanel }) : null,
1565
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("main", { className: "relative flex min-w-0 flex-1 flex-col", children: [
1566
+ findOpen ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute right-4 top-2 z-20", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_RenderedFindBar.RenderedFindBar, { rootRef: scrollerRef, onClose: () => setFindOpen(false), label: "Find in document", focusRequest: findFocus, compact: true }) }) : null,
1567
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: scrollerRef, tabIndex: 0, "data-pdf-scroller": "", className: "min-h-0 flex-1 overflow-auto bg-muted outline-none", children: viewer }),
1568
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MarkupFloatingToolbar, { engine, snapshot: snap, enabled: markupOn && !read, onDone: () => scrollerRef.current?.focus({ preventScroll: true }) }),
1569
+ read ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute right-3 top-3 z-20", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_controls5.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.XIcon, {}), onClick: () => setRead(false), children: "Exit reading mode" }) }) : null
1126
1570
  ] }),
1127
- showChrome && !compact && rightOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("aside", { className: (0, import_cn.cn)("shrink-0 border-l border-border bg-background", hostTool?.wide ? "w-[28rem]" : "w-72"), "aria-label": "Properties", "data-pdf-right": "", children: rightPanel }) : null,
1128
- pop === "all-tools" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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__ */ (0, import_jsx_runtime3.jsx)(ToolRows, { tool: activeToolKey, hostTools, onPick: (id) => {
1571
+ showChrome && !compact && rightOpen ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("aside", { className: (0, import_cn2.cn)("shrink-0 border-l border-border bg-background", hostTool?.wide ? "w-[28rem]" : "w-72"), "aria-label": "Properties", "data-pdf-right": "", children: rightPanel }) : null,
1572
+ pop === "all-tools" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)(ToolRows, { tool: activeToolKey, hostTools, canMarkup, onPick: (id) => {
1129
1573
  setPop(null);
1130
1574
  chooseTool(id);
1131
1575
  } }) }) : null,
1132
- pop === "print" && host.print ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: (0, import_cn.cn)(popCard, "w-72"), role: "dialog", "aria-label": "Print", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col gap-3", children: [
1133
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.SegmentedControl, { fill: true, value: printMode, onValueChange: setPrintMode, data: [{ value: "all", label: "All pages" }, { value: "pages", label: "Pages" }], "aria-label": "What to print" }),
1134
- printMode === "pages" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.PageRangeInput, { value: printRange, onChange: (t) => setPrintRange(t), pageCount }) : null,
1135
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1136
- import_controls4.Button,
1576
+ pop === "print" && host.print ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: (0, import_cn2.cn)(popCard, "w-72"), role: "dialog", "aria-label": "Print", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col gap-3", children: [
1577
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_controls5.SegmentedControl, { fill: true, value: printMode, onValueChange: setPrintMode, data: [{ value: "all", label: "All pages" }, { value: "pages", label: "Pages" }], "aria-label": "What to print" }),
1578
+ printMode === "pages" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_controls5.PageRangeInput, { value: printRange, onChange: (t) => setPrintRange(t), pageCount }) : null,
1579
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1580
+ import_controls5.Button,
1137
1581
  {
1138
1582
  variant: "primary",
1139
- disabled: busy || printMode === "pages" && !(0, import_controls4.parsePageRanges)(printRange, pageCount).valid,
1583
+ disabled: busy || printMode === "pages" && !(0, import_controls5.parsePageRanges)(printRange, pageCount).valid,
1140
1584
  onClick: () => {
1141
- const pages = printMode === "pages" ? (0, import_controls4.parsePageRanges)(printRange, pageCount).pages : null;
1585
+ const pages = printMode === "pages" ? (0, import_controls5.parsePageRanges)(printRange, pageCount).pages : null;
1142
1586
  setBusy(true);
1143
1587
  host.print(pages).then(() => setPop(null), fail).finally(() => setBusy(false));
1144
1588
  },
@@ -1146,18 +1590,27 @@ function PdfWorkspace({ host, className }) {
1146
1590
  }
1147
1591
  )
1148
1592
  ] }) }) : null,
1149
- pop === "shortcuts" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: (0, import_cn.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__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mb-3 last:mb-0", children: [
1150
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: section }),
1151
- PDF_SHORTCUTS.filter((s) => s.section === section).map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex justify-between gap-2 py-0.5 text-xs", children: [
1152
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: s.label }),
1153
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: formatKeys(s.keys, isMac()) })
1154
- ] }, s.action))
1155
- ] }, section)) }) : null,
1156
- compact && sheet ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1157
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "aria-label": "Close", className: "absolute inset-0 z-30 bg-background/60", onClick: () => setSheet(null) }),
1158
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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__ */ (0, import_jsx_runtime3.jsx)("div", { className: "h-[60vh] min-h-0", children: leftPanel }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex min-h-0 flex-col overflow-y-auto", children: [
1159
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "p-2", role: "menu", "aria-label": "Tools", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToolRows, { tool: activeToolKey, hostTools, onPick: chooseTool }) }),
1160
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "border-t border-border", children: rightPanel })
1593
+ pop === "shortcuts" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: (0, import_cn2.cn)(popCard, "max-h-[70%] w-80 overflow-y-auto"), role: "dialog", "aria-label": "Keyboard shortcuts", "data-pdf-shortcuts": "", children: [
1594
+ ["Edit", "View", "Pages", "Tools"].map((section) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mb-3 last:mb-0", children: [
1595
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: section }),
1596
+ PDF_SHORTCUTS.filter((s) => s.section === section).map((s) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex justify-between gap-2 py-0.5 text-xs", children: [
1597
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: s.label }),
1598
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: formatKeys(s.keys, isMac()) })
1599
+ ] }, s.action))
1600
+ ] }, section)),
1601
+ canMarkup ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mb-0", children: [
1602
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: "Markup" }),
1603
+ PDF_MARKUP_SHORTCUTS.filter((s) => s.tool !== "signature" || host.signaturePad).map((s) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex justify-between gap-2 py-0.5 text-xs", children: [
1604
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: s.label }),
1605
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: s.keys })
1606
+ ] }, s.tool))
1607
+ ] }) : null
1608
+ ] }) : null,
1609
+ compact && sheet ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1610
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": "Close", className: "absolute inset-0 z-30 bg-background/60", onClick: () => setSheet(null) }),
1611
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)("div", { className: "h-[60vh] min-h-0", children: leftPanel }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-0 flex-col overflow-y-auto", children: [
1612
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "p-2", role: "menu", "aria-label": "Tools", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolRows, { tool: activeToolKey, hostTools, canMarkup, onPick: chooseTool }) }),
1613
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "border-t border-border", children: rightPanel })
1161
1614
  ] }) })
1162
1615
  ] }) : null
1163
1616
  ] })