@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.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
@@ -157,6 +177,7 @@ function LeftPanel(p) {
157
177
  const turn = p.decisions.turns[page] ?? 0;
158
178
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative flex h-full w-full items-center justify-center overflow-hidden bg-card", children: [
159
179
  /* @__PURE__ */ (0, import_jsx_runtime.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__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground", children: page }) }),
180
+ p.host.renderPageBadge ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute right-1 top-1 z-10", "data-pdf-page-badge": page, children: p.host.renderPageBadge(page) }) : null,
160
181
  page === p.currentPage ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pointer-events-none absolute inset-0 border-2 border-primary" }) : null,
161
182
  gone.has(page) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "absolute inset-0 flex items-center justify-center bg-background/80 text-xs font-medium text-destructive", children: "Deleted" }) : null
162
183
  ] });
@@ -429,9 +450,94 @@ var canRedo = (h) => h.future.length > 0;
429
450
  var undoLabel = (h) => h.present.label;
430
451
  var redoLabel = (h) => h.future[0]?.label ?? "";
431
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
+
432
537
  // src/pdf/tools.ts
433
538
  var PDF_TOOLS = [
434
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." },
435
541
  { id: "organize", label: "Organize pages", key: "o", status: "ready", capability: "rotate-pages,delete-pages,reorder-pages", opens: "panel", group: 0 },
436
542
  { id: "extract-pages", label: "Extract pages", key: "e", status: "ready", capability: "extract-pages", opens: "panel", group: 0 },
437
543
  { id: "split", label: "Split", key: "s", status: "ready", capability: "split", opens: "panel", group: 0 },
@@ -452,7 +558,7 @@ var PDF_TOOLS = [
452
558
  var pdfTool = (id) => PDF_TOOLS.find((t) => t.id === id);
453
559
  var comingSoonIdOf = (id) => `pdf.${id}`;
454
560
  var PROMISES = {
455
- 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.",
456
562
  forms: "Fill in a form and keep the values in the file.",
457
563
  protect: "Lock a PDF with a password and limit what others can do.",
458
564
  ocr: "Turn a scanned PDF into one you can search and copy from.",
@@ -477,14 +583,15 @@ var PDF_COMING_SOON = {
477
583
  return [id, { id, label: t.label, owner: "pdf", promise: PROMISES[t.id] ?? "", stage: "planned", surfaces: ["PDF workspace tool rail"] }];
478
584
  })
479
585
  ),
480
- ...PANEL_PROMISES
586
+ ...PANEL_PROMISES,
587
+ ...PDF_MARKUP_COMING_SOON
481
588
  };
482
589
 
483
590
  // src/pdf/shortcuts.ts
484
591
  var PDF_SHORTCUTS = [
485
592
  { action: "undo", label: "Undo", keys: "Mod+Z", section: "Edit" },
486
593
  { action: "redo", label: "Redo", keys: "Mod+Shift+Z", section: "Edit" },
487
- { 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" },
488
595
  { action: "find", label: "Find", keys: "Mod+F", section: "Edit" },
489
596
  { action: "print", label: "Print", keys: "Mod+P", section: "Edit" },
490
597
  { action: "zoom-in", label: "Zoom in", keys: "Mod+=", section: "View" },
@@ -511,6 +618,14 @@ var PDF_SHORTCUTS = [
511
618
  section: "Tools"
512
619
  }))
513
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
+ }
514
629
  function parse(keys) {
515
630
  const parts = keys.split("+");
516
631
  const key = parts[parts.length - 1] === "" ? "+" : parts[parts.length - 1];
@@ -537,30 +652,222 @@ function formatKeys(keys, mac) {
537
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 ? "" : "+");
538
653
  }
539
654
 
540
- // 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");
541
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");
542
848
  var PDF_TOOL_ICONS = {
543
- browse: import_icons.HandIcon,
544
- organize: import_icons.LayoutGridIcon,
545
- "extract-pages": import_icons.ArrowUpFromLineIcon,
546
- split: import_icons.SplitIcon,
547
- crop: import_icons.ProportionsIcon,
548
- redact: import_icons.EraserIcon,
549
- compress: import_icons.ChevronsDownUpIcon,
550
- scan: import_icons.CameraIcon,
551
- sign: import_icons.PenToolIcon,
552
- "extract-data": import_icons.TypeIcon,
553
- edit: import_icons.PencilIcon,
554
- forms: import_icons.ListChecksIcon,
555
- protect: import_icons.LockIcon,
556
- ocr: import_icons.CaptionsIcon,
557
- convert: import_icons.ArrowLeftRightIcon,
558
- compare: import_icons.Columns2Icon,
559
- 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
560
867
  };
561
868
 
562
869
  // src/pdf/PdfWorkspace.tsx
563
- var import_jsx_runtime3 = require("react/jsx-runtime");
870
+ var import_jsx_runtime4 = require("react/jsx-runtime");
564
871
  var ZOOM_STEPS = [0.5, 0.75, 1, 1.25, 1.5, 2, 3];
565
872
  var COMPACT_BELOW_PX = 768;
566
873
  var BAR = "flex h-10 shrink-0 items-center gap-1 border-b border-border bg-background px-2";
@@ -572,8 +879,8 @@ async function announceSoon(id) {
572
879
  await announceComingSoon(id);
573
880
  }
574
881
  function useWidth(ref) {
575
- const [size, setSize] = (0, import_react3.useState)({ w: 1200, h: 800 });
576
- (0, import_react3.useLayoutEffect)(() => {
882
+ const [size, setSize] = (0, import_react4.useState)({ w: 1200, h: 800 });
883
+ (0, import_react4.useLayoutEffect)(() => {
577
884
  const node = ref.current;
578
885
  if (!node) return void 0;
579
886
  const measure = () => setSize({ w: node.clientWidth || 1200, h: node.clientHeight || 800 });
@@ -585,36 +892,36 @@ function useWidth(ref) {
585
892
  }, [ref]);
586
893
  return size;
587
894
  }
588
- function ToolRows({ tool, hostTools, onPick }) {
589
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
590
- 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)(
591
898
  "button",
592
899
  {
593
900
  type: "button",
594
901
  role: "menuitem",
595
902
  "aria-current": tool === `host:${t.id}` ? "true" : void 0,
596
- 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"),
597
904
  onClick: () => onPick(`host:${t.id}`),
598
905
  children: [
599
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(t.icon, { className: "size-4 shrink-0" }),
600
- /* @__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 })
601
908
  ]
602
909
  }
603
910
  ) }, `host:${t.id}`)),
604
- PDF_TOOLS.filter((t) => t.id !== "browse").map((t) => {
911
+ PDF_TOOLS.filter((t) => t.id !== "browse" && (t.id !== "markup" || canMarkup)).map((t) => {
605
912
  const Icon = PDF_TOOL_ICONS[t.id];
606
- 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)(
607
914
  "button",
608
915
  {
609
916
  type: "button",
610
917
  role: "menuitem",
611
918
  "aria-current": t.id === tool ? "true" : void 0,
612
- 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"),
613
920
  onClick: () => onPick(t.id),
614
921
  children: [
615
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "size-4 shrink-0" }),
616
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "flex-1 whitespace-nowrap", children: t.label }),
617
- 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
618
925
  ]
619
926
  }
620
927
  ) }, t.id);
@@ -622,50 +929,58 @@ function ToolRows({ tool, hostTools, onPick }) {
622
929
  ] });
623
930
  }
624
931
  function PdfWorkspace({ host, className }) {
625
- const rootRef = (0, import_react3.useRef)(null);
626
- const scrollerRef = (0, import_react3.useRef)(null);
932
+ const rootRef = (0, import_react4.useRef)(null);
933
+ const scrollerRef = (0, import_react4.useRef)(null);
627
934
  const rootSize = useWidth(rootRef);
628
935
  const viewSize = useWidth(scrollerRef);
629
936
  const compact = rootSize.w < COMPACT_BELOW_PX;
630
937
  const online = host.online !== false;
631
- const [pageCount, setPageCount] = (0, import_react3.useState)(0);
632
- 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)));
633
940
  const decisions = history.present.value;
634
941
  const dirty = isDirty(decisions);
635
- const [tool, setTool] = (0, import_react3.useState)("browse");
636
- const [hostToolId, setHostToolId] = (0, import_react3.useState)(null);
637
- 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]);
638
945
  const hostTool = hostToolId ? hostTools.find((t) => t.id === hostToolId) ?? null : null;
639
946
  const activeToolKey = hostTool ? `host:${hostTool.id}` : tool;
640
- const [leftOpen, setLeftOpen] = (0, import_react3.useState)(true);
641
- const [rightOpen, setRightOpen] = (0, import_react3.useState)(true);
642
- const [leftTab, setLeftTab] = (0, import_react3.useState)("pages");
643
- const [read, setRead] = (0, import_react3.useState)(false);
644
- const [spread, setSpread] = (0, import_react3.useState)("single");
645
- const [zoomMode, setZoomMode] = (0, import_react3.useState)("fit-width");
646
- const [customZoom, setCustomZoom] = (0, import_react3.useState)(1);
647
- const [selected, setSelected] = (0, import_react3.useState)([]);
648
- const [currentPage, setCurrentPage] = (0, import_react3.useState)(1);
649
- const [pageDraft, setPageDraft] = (0, import_react3.useState)("1");
650
- const [findOpen, setFindOpen] = (0, import_react3.useState)(false);
651
- const [findFocus, setFindFocus] = (0, import_react3.useState)(0);
652
- const [pop, setPop] = (0, import_react3.useState)(null);
653
- const [sheet, setSheet] = (0, import_react3.useState)(null);
654
- const [toolQuery, setToolQuery] = (0, import_react3.useState)("");
655
- const [busy, setBusy] = (0, import_react3.useState)(false);
656
- const [error, setError] = (0, import_react3.useState)(null);
657
- const [result, setResult] = (0, import_react3.useState)(null);
658
- const [reloadKey, setReloadKey] = (0, import_react3.useState)(0);
659
- const [sizeVersion, setSizeVersion] = (0, import_react3.useState)(0);
660
- const sizes = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
661
- const [editingName, setEditingName] = (0, import_react3.useState)(false);
662
- const [nameDraft, setNameDraft] = (0, import_react3.useState)(host.file.name);
663
- const [printMode, setPrintMode] = (0, import_react3.useState)("all");
664
- const [printRange, setPrintRange] = (0, import_react3.useState)("");
665
- 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]);
666
981
  const toolDef = pdfTool(tool);
667
982
  const first = sizes.current.get(1);
668
- const fitPage = (0, import_react3.useMemo)(() => {
983
+ const fitPage = (0, import_react4.useMemo)(() => {
669
984
  void sizeVersion;
670
985
  if (!first) return 1;
671
986
  const ratio = first.h / first.w;
@@ -674,7 +989,7 @@ function PdfWorkspace({ host, className }) {
674
989
  return clampZoom(availH * cols / (Math.max(1, viewSize.w) * ratio));
675
990
  }, [first, spread, viewSize.h, viewSize.w, sizeVersion]);
676
991
  const zoom = zoomMode === "fit-width" ? 1 : zoomMode === "fit-page" ? fitPage : customZoom;
677
- const stepZoom = (0, import_react3.useCallback)(
992
+ const stepZoom = (0, import_react4.useCallback)(
678
993
  (dir) => {
679
994
  const cur = zoomMode === "fit-width" ? 1 : zoomMode === "fit-page" ? fitPage : customZoom;
680
995
  const next = dir > 0 ? ZOOM_STEPS.find((s) => s > cur + 0.01) : [...ZOOM_STEPS].reverse().find((s) => s < cur - 0.01);
@@ -684,7 +999,7 @@ function PdfWorkspace({ host, className }) {
684
999
  },
685
1000
  [zoomMode, fitPage, customZoom]
686
1001
  );
687
- const goToPage = (0, import_react3.useCallback)(
1002
+ const goToPage = (0, import_react4.useCallback)(
688
1003
  (n) => {
689
1004
  const page = Math.min(Math.max(1, Math.round(n)), Math.max(1, pageCount));
690
1005
  setCurrentPage(page);
@@ -694,7 +1009,7 @@ function PdfWorkspace({ host, className }) {
694
1009
  },
695
1010
  [pageCount]
696
1011
  );
697
- (0, import_react3.useEffect)(() => {
1012
+ (0, import_react4.useEffect)(() => {
698
1013
  const node = scrollerRef.current;
699
1014
  if (!node) return void 0;
700
1015
  let raf = 0;
@@ -721,34 +1036,37 @@ function PdfWorkspace({ host, className }) {
721
1036
  cancelAnimationFrame(raf);
722
1037
  };
723
1038
  }, [pageCount]);
724
- const commit = (0, import_react3.useCallback)((label, next) => setHistory((h) => pushStep(h, label, next)), []);
725
- const targets = (0, import_react3.useCallback)(() => selectedPages.length ? selectedPages : [currentPage], [selectedPages, currentPage]);
726
- 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)(
727
1045
  (by) => {
728
1046
  const t = targets();
729
1047
  commit(`Rotate ${t.length === 1 ? `page ${t[0]}` : `${t.length} pages`}`, turnPages(decisions, t, by));
730
1048
  },
731
1049
  [targets, commit, decisions]
732
1050
  );
733
- const del = (0, import_react3.useCallback)(() => {
1051
+ const del = (0, import_react4.useCallback)(() => {
734
1052
  const t = targets();
735
1053
  commit(`Delete ${t.length === 1 ? `page ${t[0]}` : `${t.length} pages`}`, deletePages(decisions, t));
736
1054
  }, [targets, commit, decisions]);
737
- const restore = (0, import_react3.useCallback)(() => {
1055
+ const restore = (0, import_react4.useCallback)(() => {
738
1056
  const t = targets();
739
1057
  commit(`Restore ${t.length === 1 ? `page ${t[0]}` : `${t.length} pages`}`, restorePages(decisions, t));
740
1058
  }, [targets, commit, decisions]);
741
- const onDocumentLoad = (0, import_react3.useCallback)((n) => {
1059
+ const onDocumentLoad = (0, import_react4.useCallback)((n) => {
742
1060
  setPageCount(n);
743
1061
  setHistory((h) => h.present.value.order.length === n ? h : startHistory(freshDecisions(n)));
744
1062
  }, []);
745
- const onPageSize = (0, import_react3.useCallback)((page, w, h) => {
1063
+ const onPageSize = (0, import_react4.useCallback)((page, w, h) => {
746
1064
  const prev = sizes.current.get(page);
747
1065
  if (prev && prev.w === w && prev.h === h) return;
748
1066
  sizes.current.set(page, { w, h });
749
1067
  if (page === 1) queueMicrotask(() => setSizeVersion((v) => v + 1));
750
1068
  }, []);
751
- const fail = (0, import_react3.useCallback)(
1069
+ const fail = (0, import_react4.useCallback)(
752
1070
  (e) => {
753
1071
  const message = e instanceof Error ? e.message : "That did not work.";
754
1072
  setError(message);
@@ -756,7 +1074,7 @@ function PdfWorkspace({ host, className }) {
756
1074
  },
757
1075
  [host]
758
1076
  );
759
- const runTool = (0, import_react3.useCallback)(
1077
+ const runTool = (0, import_react4.useCallback)(
760
1078
  async (request) => {
761
1079
  if (!online) {
762
1080
  setError("You are offline. Changes are not queued; try again when you are back online.");
@@ -782,12 +1100,109 @@ function PdfWorkspace({ host, className }) {
782
1100
  },
783
1101
  [host, online, pageCount, fail]
784
1102
  );
785
- const apply = (0, import_react3.useCallback)(() => {
1103
+ const apply = (0, import_react4.useCallback)(() => {
786
1104
  const ops = compilePageOps(decisions);
787
- if (ops.length) void runTool({ tool: "apply-pages", ops });
788
- }, [decisions, runTool]);
789
- const discard = (0, import_react3.useCallback)(() => setHistory(startHistory(freshDecisions(pageCount))), [pageCount]);
790
- 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)(
791
1206
  (id) => {
792
1207
  if (id.startsWith("host:")) {
793
1208
  setHostToolId(id.slice(5));
@@ -798,7 +1213,7 @@ function PdfWorkspace({ host, className }) {
798
1213
  return;
799
1214
  }
800
1215
  const def = pdfTool(id);
801
- if (!def) return;
1216
+ if (!def || def.id === "markup" && !host.saveMarkup) return;
802
1217
  setHostToolId(null);
803
1218
  if (def.status === "soon") {
804
1219
  announceSoon(comingSoonIdOf(def.id)).catch(fail);
@@ -816,26 +1231,26 @@ function PdfWorkspace({ host, className }) {
816
1231
  },
817
1232
  [host, compact, fail]
818
1233
  );
819
- const railGroups = (0, import_react3.useMemo)(() => {
1234
+ const railGroups = (0, import_react4.useMemo)(() => {
820
1235
  const groups = [[], [], []];
821
1236
  for (const t of PDF_TOOLS) {
822
- if (t.status !== "ready") continue;
1237
+ if (t.status !== "ready" || t.id === "markup" && !canMarkup) continue;
823
1238
  groups[t.group].push({ id: t.id, label: t.label, icon: PDF_TOOL_ICONS[t.id], ...t.key ? { shortcut: t.key.toUpperCase() } : null });
824
1239
  }
825
1240
  for (const t of hostTools) groups[t.group ?? 1].push({ id: `host:${t.id}`, label: t.label, icon: t.icon });
826
- 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 });
827
1242
  return groups.filter((g) => g.length);
828
- }, [hostTools]);
829
- const runAction = (0, import_react3.useCallback)(
1243
+ }, [hostTools, canMarkup]);
1244
+ const runAction = (0, import_react4.useCallback)(
830
1245
  (action) => {
831
1246
  if (action.startsWith("tool:")) return chooseTool(action.slice(5));
832
1247
  switch (action) {
833
1248
  case "undo":
834
- return setHistory((h) => undo(h));
1249
+ return undoAll();
835
1250
  case "redo":
836
- return setHistory((h) => redo(h));
1251
+ return redoAll();
837
1252
  case "save":
838
- return apply();
1253
+ return markupPending ? void saveMarkup() : apply();
839
1254
  case "find":
840
1255
  setFindOpen(true);
841
1256
  setFindFocus((n) => n + 1);
@@ -880,7 +1295,7 @@ function PdfWorkspace({ host, className }) {
880
1295
  return void 0;
881
1296
  }
882
1297
  },
883
- [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]
884
1299
  );
885
1300
  const onKeyDown = (e) => {
886
1301
  const t = e.target;
@@ -892,19 +1307,30 @@ function PdfWorkspace({ host, className }) {
892
1307
  else if (sheet) setSheet(null);
893
1308
  return;
894
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
+ }
895
1318
  const action = matchShortcut(e.nativeEvent, typing);
896
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;
897
1321
  if ((action === "page-next" || action === "page-prev") && t !== scrollerRef.current && t !== rootRef.current) return;
898
1322
  if (action === "delete-pages" && t.closest("[data-pdf-thumbnails]") === null && t !== scrollerRef.current) return;
899
1323
  e.preventDefault();
1324
+ if (action === "undo" || action === "redo") e.stopPropagation();
900
1325
  runAction(action);
901
1326
  };
902
- const saveState = !online ? "Offline" : busy ? "Saving" : error && dirty ? "Not saved" : dirty ? "Unsaved" : "Saved";
903
- 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)(
904
1330
  "span",
905
1331
  {
906
1332
  role: "status",
907
- className: (0, import_cn.cn)(
1333
+ className: (0, import_cn2.cn)(
908
1334
  "inline-flex h-6 shrink-0 items-center justify-center whitespace-nowrap rounded-full border px-2 text-xs",
909
1335
  saveState === "Unsaved" || saveState === "Offline" || saveState === "Not saved" ? "border-border text-foreground" : "border-border text-muted-foreground",
910
1336
  busy && "animate-pulse"
@@ -912,13 +1338,13 @@ function PdfWorkspace({ host, className }) {
912
1338
  children: saveState
913
1339
  }
914
1340
  );
915
- 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 });
916
1342
  const toolHits = toolQuery.trim() ? [
917
1343
  ...hostTools.map((t) => ({ id: `host:${t.id}`, label: t.label, status: "ready" })),
918
1344
  ...PDF_TOOLS
919
1345
  ].filter((t) => t.label.toLowerCase().includes(toolQuery.trim().toLowerCase())) : [];
920
- const nameNode = editingName && host.rename ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
921
- import_controls4.Field,
1346
+ const nameNode = editingName && host.rename ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1347
+ import_controls5.Field,
922
1348
  {
923
1349
  autoFocus: true,
924
1350
  value: nameDraft,
@@ -937,7 +1363,7 @@ function PdfWorkspace({ host, className }) {
937
1363
  },
938
1364
  width: "lg"
939
1365
  }
940
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1366
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
941
1367
  "button",
942
1368
  {
943
1369
  type: "button",
@@ -947,18 +1373,36 @@ function PdfWorkspace({ host, className }) {
947
1373
  children: host.file.name
948
1374
  }
949
1375
  );
950
- 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: [
951
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
952
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-sm font-medium", children: hostTool.label }),
953
- 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
954
1380
  ] }),
955
- /* @__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) }) })
956
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex h-full min-h-0 flex-col", children: [
957
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "shrink-0 border-b border-border px-3 py-2", children: [
958
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-sm font-medium", children: toolDef.label }),
959
- 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
960
1386
  ] }),
961
- /* @__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)(
962
1406
  ToolPanel,
963
1407
  {
964
1408
  tool: toolDef,
@@ -982,7 +1426,7 @@ function PdfWorkspace({ host, className }) {
982
1426
  }
983
1427
  ) })
984
1428
  ] });
985
- const leftPanel = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1429
+ const leftPanel = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
986
1430
  LeftPanel,
987
1431
  {
988
1432
  host,
@@ -1001,13 +1445,13 @@ function PdfWorkspace({ host, className }) {
1001
1445
  }
1002
1446
  );
1003
1447
  const viewerPage = hostTool?.followsPage ? currentPage : 1;
1004
- const viewer = (0, import_react3.useMemo)(
1005
- () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react3.Fragment, { children: host.renderViewer({ zoom, spread, rotations: decisions.turns, reloadKey, currentPage: viewerPage, onDocumentLoad, onPageSize }) }, reloadKey),
1006
- [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]
1007
1451
  );
1008
1452
  const showChrome = !read;
1009
1453
  const popCard = "absolute right-2 top-11 z-30 rounded-lg border border-border bg-popover p-3 text-popover-foreground shadow-lg";
1010
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1454
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1011
1455
  "div",
1012
1456
  {
1013
1457
  ref: rootRef,
@@ -1015,18 +1459,18 @@ function PdfWorkspace({ host, className }) {
1015
1459
  "data-pdf-workspace": "",
1016
1460
  "data-compact": compact ? "" : void 0,
1017
1461
  onKeyDown,
1018
- 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),
1019
1463
  children: [
1020
- showChrome ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: BAR, "data-pdf-topbar": "", children: [
1021
- 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 }),
1022
1466
  nameNode,
1023
1467
  savePill,
1024
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1" }),
1025
- !compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative w-56", children: [
1026
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1027
- 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,
1028
1472
  {
1029
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.SearchIcon, {}),
1473
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_icons3.SearchIcon, {}),
1030
1474
  value: toolQuery,
1031
1475
  "aria-label": "Search tools",
1032
1476
  placeholder: "Search tools",
@@ -1044,7 +1488,7 @@ function PdfWorkspace({ host, className }) {
1044
1488
  }
1045
1489
  }
1046
1490
  ),
1047
- 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)(
1048
1492
  "button",
1049
1493
  {
1050
1494
  type: "button",
@@ -1056,25 +1500,25 @@ function PdfWorkspace({ host, className }) {
1056
1500
  setPop(null);
1057
1501
  },
1058
1502
  children: [
1059
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t.label }),
1060
- 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
1061
1505
  ]
1062
1506
  }
1063
1507
  ) }, t.id)) }) : null
1064
1508
  ] }) : null,
1065
- iconBtn(undoLabel(history) ? `Undo ${undoLabel(history)}` : "Undo", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Undo2Icon, {}), () => runAction("undo"), { disabled: !canUndo(history) }),
1066
- 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) }),
1067
- !compact && iconBtn("Find", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.SearchIcon, {}), () => runAction("find"), { pressed: findOpen }),
1068
- host.share && !compact ? iconBtn("Share", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Share2Icon, {}), () => host.share()) : null,
1069
- iconBtn("Download", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.DownloadIcon, {}), () => host.download()),
1070
- host.print && !compact ? iconBtn("Print", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.PrinterIcon, {}), () => setPop(pop === "print" ? null : "print"), { pressed: pop === "print" }) : null,
1071
- !compact ? iconBtn("Keyboard shortcuts", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.KeyboardIcon, {}), () => runAction("shortcuts"), { pressed: pop === "shortcuts" }) : null,
1072
- 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 })
1073
1517
  ] }) : null,
1074
- showChrome ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: BAR, "data-pdf-viewbar": "", children: [
1075
- iconBtn("Previous page", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.ChevronLeftIcon, {}), () => goToPage(currentPage - 1), { disabled: currentPage <= 1 }),
1076
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1077
- 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,
1078
1522
  {
1079
1523
  type: "number",
1080
1524
  min: 1,
@@ -1087,17 +1531,17 @@ function PdfWorkspace({ host, className }) {
1087
1531
  className: compact ? "w-12" : "w-16"
1088
1532
  }
1089
1533
  ),
1090
- /* @__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: [
1091
1535
  "of ",
1092
1536
  pageCount || "-"
1093
1537
  ] }),
1094
- iconBtn("Next page", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.ChevronRightIcon, {}), () => goToPage(currentPage + 1), { disabled: currentPage >= pageCount }),
1095
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mx-1 h-5 w-px bg-border" }),
1096
- iconBtn("Zoom out", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.MinusIcon, {}), () => stepZoom(-1)),
1097
- /* @__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)}%` }),
1098
- iconBtn("Zoom in", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.PlusIcon, {}), () => stepZoom(1)),
1099
- !compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1100
- 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,
1101
1545
  {
1102
1546
  value: zoomMode === "custom" ? null : zoomMode,
1103
1547
  onValueChange: (v) => setZoomMode(v),
@@ -1105,39 +1549,40 @@ function PdfWorkspace({ host, className }) {
1105
1549
  "aria-label": "Fit"
1106
1550
  }
1107
1551
  ) : null,
1108
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1" }),
1109
- !compact ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1110
- iconBtn("Rotate left", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.RotateCcwIcon, {}), () => turn(-90)),
1111
- iconBtn("Rotate right", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.RotateCwIcon, {}), () => turn(90)),
1112
- iconBtn("Delete page", /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.Trash2Icon, {}), del, { disabled: decisions.order.length - decisions.deleted.length <= 1 }),
1113
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mx-1 h-5 w-px bg-border" }),
1114
- 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" })
1115
1559
  ] }) : null,
1116
- 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))
1117
1561
  ] }) : null,
1118
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative flex min-h-0 flex-1", children: [
1119
- 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,
1120
- 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,
1121
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("main", { className: "relative flex min-w-0 flex-1 flex-col", children: [
1122
- 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,
1123
- /* @__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 }),
1124
- 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
1125
1570
  ] }),
1126
- 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,
1127
- 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) => {
1128
1573
  setPop(null);
1129
1574
  chooseTool(id);
1130
1575
  } }) }) : null,
1131
- 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: [
1132
- /* @__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" }),
1133
- printMode === "pages" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_controls4.PageRangeInput, { value: printRange, onChange: (t) => setPrintRange(t), pageCount }) : null,
1134
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1135
- 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,
1136
1581
  {
1137
1582
  variant: "primary",
1138
- disabled: busy || printMode === "pages" && !(0, import_controls4.parsePageRanges)(printRange, pageCount).valid,
1583
+ disabled: busy || printMode === "pages" && !(0, import_controls5.parsePageRanges)(printRange, pageCount).valid,
1139
1584
  onClick: () => {
1140
- 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;
1141
1586
  setBusy(true);
1142
1587
  host.print(pages).then(() => setPop(null), fail).finally(() => setBusy(false));
1143
1588
  },
@@ -1145,18 +1590,27 @@ function PdfWorkspace({ host, className }) {
1145
1590
  }
1146
1591
  )
1147
1592
  ] }) }) : null,
1148
- 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: [
1149
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: section }),
1150
- 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: [
1151
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: s.label }),
1152
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("kbd", { className: "rounded border border-border px-1 text-muted-foreground", children: formatKeys(s.keys, isMac()) })
1153
- ] }, s.action))
1154
- ] }, section)) }) : null,
1155
- compact && sheet ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1156
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "aria-label": "Close", className: "absolute inset-0 z-30 bg-background/60", onClick: () => setSheet(null) }),
1157
- /* @__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: [
1158
- /* @__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 }) }),
1159
- /* @__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 })
1160
1614
  ] }) })
1161
1615
  ] }) : null
1162
1616
  ] })