@inkbar/react 0.4.0 → 0.6.0

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/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, createContext, createElement, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
- import { InkError, acceptSuggestion, confidenceBucket, createMemorySignatureStore, createSignatureLibrary, createSnapshotHistory, isAbandonedNote, openDocument, repliesTo, threadOf, threadRoot } from "@inkbar/core";
4
- import { COLOR_COLUMNS, FONT_FAMILIES, MARKUP_ICONS, MARKUP_KINDS, SHAPE_COLUMNS, SHAPE_ICONS, SHAPE_KINDS, SIGNATURE_FONTS, SIGNATURE_LABELS, SIGNATURE_TABS, TOOL_LABELS, ZOOM_LEVELS, colorMenuModel, createFocusReturn, createSignatureDraft, createToolOptions, cssVariableDefaults, focusInitialMenuItem, fontFamilyValue, handleMenuKeys, handleRadioKeys, handleTabKeys, highlightMenuModel, lineStyleMenuModel, menuItems, mountViewer, observeAnnotationElement, shapeMenuModel, shouldKeepOpenOnAnnotation, signatureMenuModel, signaturePreview, textStyleMenuModel, threadDeleteLabel, threadPopoverLabel, trackToolStyle } from "@inkbar/core/dom";
3
+ import { InkError, createMemorySignatureStore, createSignatureLibrary, createSnapshotHistory, isAbandonedNote, openDocument, readonly, repliesTo, threadOf, threadRoot, writable } from "@inkbar/core";
4
+ import { COLOR_COLUMNS, FONT_FAMILIES, MARKUP_ICONS, MARKUP_KINDS, SHAPE_COLUMNS, SHAPE_ICONS, SHAPE_KINDS, SIGNATURE_FONTS, SIGNATURE_LABELS, SIGNATURE_TABS, TOOL_LABELS, ZOOM_LEVELS, colorMenuModel, createFocusReturn, createPluginHost, createSignatureDraft, createToolOptions, cssVariableDefaults, focusInitialMenuItem, fontFamilyValue, handleMenuKeys, handleRadioKeys, handleTabKeys, highlightMenuModel, lineStyleMenuModel, menuItems, mountViewer, observeAnnotationElement, shapeMenuModel, shouldKeepOpenOnAnnotation, signatureMenuModel, signaturePreview, textStyleMenuModel, threadDeleteLabel, threadPopoverLabel, trackToolStyle, unknownCommand } from "@inkbar/core/dom";
5
5
  import { Dialog, Popover, Select, Slot, Toolbar } from "radix-ui";
6
6
  import { createPortal, flushSync } from "react-dom";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -19,14 +19,14 @@ function useInkContext(consumer) {
19
19
  }
20
20
  //#endregion
21
21
  //#region src/use-readable.ts
22
- const noop = () => void 0;
22
+ const noop$1 = () => void 0;
23
23
  /**
24
24
  * Mirrors a core `Readable` into React. A missing readable yields `fallback`, which must be a
25
25
  * stable reference (a module constant): `useSyncExternalStore` compares snapshots by identity
26
26
  * and loops on a fresh value. On the server the fallback is rendered.
27
27
  */
28
28
  function useReadable(readable, fallback) {
29
- const subscribe = useCallback((onChange) => readable ? readable.subscribe(() => onChange()) : noop, [readable]);
29
+ const subscribe = useCallback((onChange) => readable ? readable.subscribe(() => onChange()) : noop$1, [readable]);
30
30
  const get = useCallback(() => readable ? readable.get() : fallback, [readable, fallback]);
31
31
  const server = useCallback(() => fallback, [fallback]);
32
32
  return useSyncExternalStore(subscribe, get, server);
@@ -57,10 +57,7 @@ function useAnnotations() {
57
57
  add: (input) => store?.add(input),
58
58
  update: (id, patch) => store?.update(id, patch),
59
59
  remove: (id) => store?.remove(id) ?? false,
60
- select: (ids) => store?.select(ids),
61
- /** Keeps a suggested redaction as the user's own, in the default redaction colour. */
62
- accept: (id) => store ? acceptSuggestion(store, id) : void 0,
63
- dismiss: (id) => store?.remove(id) ?? false
60
+ select: (ids) => store?.select(ids)
64
61
  }), [store]);
65
62
  return useMemo(() => ({
66
63
  annotations,
@@ -193,6 +190,40 @@ function useEmbedded() {
193
190
  }), [summary]);
194
191
  }
195
192
  //#endregion
193
+ //#region src/hooks/useCommand.ts
194
+ /**
195
+ * A plugin command by its namespaced id (`sandbox.suggest`), for hosts building their own
196
+ * control. Plugins are set up once InkRoot has mounted, so until then (and on the server) the
197
+ * command is disabled with no label; it fills in then. Once they are set up, an id no plugin
198
+ * passed to InkRoot registered stays that way, and logs `InkError('UnknownCommand')` to the
199
+ * console once.
200
+ */
201
+ function useCommand(id) {
202
+ const { pluginHost } = useInkContext("useCommand()");
203
+ const command = pluginHost?.command(id);
204
+ const logged = useRef(false);
205
+ useEffect(() => {
206
+ if (!pluginHost || command || logged.current) return;
207
+ logged.current = true;
208
+ console.error(unknownCommand(id, [...pluginHost.commands.keys()]));
209
+ }, [
210
+ pluginHost,
211
+ command,
212
+ id
213
+ ]);
214
+ const enabled = useReadable(command?.enabled, false);
215
+ const busy = useReadable(command?.busy, false);
216
+ const status = useReadable(command?.status, "");
217
+ return {
218
+ run: useCallback(async () => command?.run(), [command]),
219
+ enabled,
220
+ busy,
221
+ status,
222
+ label: command?.label ?? "",
223
+ icon: command?.icon
224
+ };
225
+ }
226
+ //#endregion
196
227
  //#region src/use-latest.ts
197
228
  /**
198
229
  * A ref that always holds the latest render's value, for callbacks that outlive a render
@@ -212,6 +243,7 @@ function useLatest(value) {
212
243
  const IDLE$1 = { state: "idle" };
213
244
  const NO_ANNOTATIONS = [];
214
245
  const NO_IDS = [];
246
+ const noop = () => void 0;
215
247
  /** Signed URLs change; the key does not. Bytes and Blobs are their own identity. */
216
248
  function identity(src) {
217
249
  if (src === null) return null;
@@ -229,7 +261,7 @@ function isDevelopment() {
229
261
  function setTestHook(el, doc) {
230
262
  if (el && isDevelopment()) el.__inkDocument = doc;
231
263
  }
232
- function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsChange, onChange, tool, onToolChange, tools: hostTools, fonts: fontsProp, signatures: signatureStore, signatureFonts: signatureFontsProp, page, onPageChange, zoom, onZoomChange, history, asChild = false, onReady, onError, onActivate, children, ref, ...rest }) {
264
+ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsChange, onChange, tool, onToolChange, tools: hostTools, plugins: pluginsProp, decorateAnnotation, fonts: fontsProp, signatures: signatureStore, signatureFonts: signatureFontsProp, page, onPageChange, zoom, onZoomChange, history, asChild = false, onReady, onError, onActivate, children, ref, ...rest }) {
233
265
  const rootElRef = useRef(null);
234
266
  const [rootEl, setRootElState] = useState(null);
235
267
  const setRootEl = useCallback((el) => {
@@ -243,6 +275,8 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
243
275
  const [internalTool, setInternalTool] = useState(tool ?? "select-text");
244
276
  const [activated, setActivatedState] = useState(null);
245
277
  const [viewport, setViewportState] = useState(null);
278
+ /** The viewport as of the last `setViewport`, for the plugin host's `scrollTo`. */
279
+ const viewportRef = useRef(null);
246
280
  const [tools, setToolsState] = useState(null);
247
281
  const documentStatus = useReadable(document?.status, IDLE$1);
248
282
  const status = override ?? documentStatus;
@@ -268,7 +302,10 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
268
302
  onPageChange,
269
303
  onZoomChange,
270
304
  hostTools,
271
- tool: internalTool
305
+ plugins: pluginsProp,
306
+ decorateAnnotation,
307
+ tool: internalTool,
308
+ tools
272
309
  });
273
310
  const snapshotHistory = useMemo(() => createSnapshotHistory({
274
311
  apply: (list) => documentRef.current?.annotations.replaceAll(list, "history"),
@@ -277,6 +314,71 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
277
314
  useLayoutEffect(() => {
278
315
  snapshotHistory.observe(annotations);
279
316
  }, [annotations, snapshotHistory]);
317
+ /** What the plugin host follows; plain readables, so StrictMode's second render is harmless. */
318
+ const [plugins] = useState(() => ({
319
+ document: writable(null),
320
+ root: writable(null),
321
+ activated: writable(null)
322
+ }));
323
+ const setActivated = useCallback((id) => {
324
+ setActivatedState(id);
325
+ plugins.activated.set(id);
326
+ if (id) latest.current.onActivate?.(id);
327
+ }, [latest, plugins]);
328
+ /**
329
+ * The history the plugins reach, picked by the open document's mode when they call: the host
330
+ * reads it as soon as a document opens, before this root has rendered with that document.
331
+ */
332
+ const pluginHistory = useMemo(() => {
333
+ const active = () => {
334
+ const doc = documentRef.current;
335
+ if (!doc) return void 0;
336
+ return doc.annotations.mode === "controlled" ? snapshotHistory : doc.history;
337
+ };
338
+ return {
339
+ batch: (fn) => {
340
+ const h = active();
341
+ if (h) h.batch(fn);
342
+ else fn();
343
+ },
344
+ track: (store) => active()?.track(store) ?? noop,
345
+ react: (fn) => active()?.react(fn) ?? noop
346
+ };
347
+ }, [snapshotHistory]);
348
+ const hostRef = useRef(null);
349
+ const [pluginHost, setPluginHost] = useState(null);
350
+ const hostMounts = useRef(0);
351
+ useLayoutEffect(() => {
352
+ hostMounts.current += 1;
353
+ if (!hostRef.current || hostRef.current.destroyed) {
354
+ const host = createPluginHost({
355
+ plugins: latest.current.plugins ?? [],
356
+ document: readonly(plugins.document),
357
+ history: () => pluginHistory,
358
+ activated: readonly(plugins.activated),
359
+ setActivated: (id) => {
360
+ if (id === null || documentRef.current?.annotations.get(id)) setActivated(id);
361
+ },
362
+ root: readonly(plugins.root),
363
+ decorate: () => latest.current.decorateAnnotation,
364
+ viewport: () => viewportRef.current
365
+ });
366
+ hostRef.current = {
367
+ host,
368
+ destroyed: false
369
+ };
370
+ setPluginHost(host);
371
+ }
372
+ return () => {
373
+ hostMounts.current -= 1;
374
+ queueMicrotask(() => {
375
+ const current = hostRef.current;
376
+ if (hostMounts.current > 0 || !current || current.destroyed) return;
377
+ current.destroyed = true;
378
+ current.host.destroy();
379
+ });
380
+ };
381
+ }, []);
280
382
  const srcIdentity = identity(src);
281
383
  useEffect(() => {
282
384
  let cancelled = false;
@@ -288,6 +390,9 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
288
390
  }
289
391
  setOverride({ state: "loading" });
290
392
  let opened = null;
393
+ /** The plugins told of `opened`, and the snapshot history's hold on its store. */
394
+ let told = null;
395
+ let detach = noop;
291
396
  const run = async () => {
292
397
  try {
293
398
  const doc = await openDocument(source, {
@@ -317,6 +422,9 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
317
422
  const current = latest.current.annotations;
318
423
  if (doc.annotations.mode === "controlled" && current !== void 0) doc.annotations.setControlledValue(current);
319
424
  documentRef.current = doc;
425
+ if (doc.annotations.mode === "controlled") detach = snapshotHistory.attach(doc.annotations);
426
+ told = plugins;
427
+ told.document.set(doc);
320
428
  setDocument(doc);
321
429
  setOverride(null);
322
430
  setTestHook(rootElRef.current, doc);
@@ -338,6 +446,8 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
338
446
  });
339
447
  return () => {
340
448
  cancelled = true;
449
+ told?.document.set(null);
450
+ detach();
341
451
  opened?.destroy();
342
452
  documentRef.current = null;
343
453
  setDocument(null);
@@ -347,7 +457,8 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
347
457
  srcIdentity,
348
458
  password,
349
459
  snapshotHistory,
350
- latest
460
+ latest,
461
+ plugins
351
462
  ]);
352
463
  useLayoutEffect(() => {
353
464
  if (annotations && document?.annotations.mode === "controlled") document.annotations.setControlledValue(annotations);
@@ -356,16 +467,15 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
356
467
  if (tool) setInternalTool(tool);
357
468
  }, [tool]);
358
469
  const setTool = useCallback((id) => {
470
+ const tc = latest.current.tools;
471
+ if (latest.current.tool === id && tc?.active.get() === id) tc.set(id);
359
472
  setInternalTool(id);
360
473
  latest.current.onToolChange?.(id);
361
474
  }, [latest]);
362
- const setActivated = useCallback((id) => {
363
- setActivatedState(id);
364
- if (id) latest.current.onActivate?.(id);
365
- }, [latest]);
366
475
  const reportZoom = useCallback((z) => latest.current.onZoomChange?.(z), [latest]);
367
476
  const reportPage = useCallback((p) => latest.current.onPageChange?.(p), [latest]);
368
477
  const setViewport = useCallback((vp, tc) => {
478
+ viewportRef.current = vp;
369
479
  setViewportState(vp);
370
480
  setToolsState(tc);
371
481
  }, []);
@@ -383,7 +493,9 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
383
493
  if (controlled) snapshotHistory.batch(fn);
384
494
  else if (documentRef.current) documentRef.current.history.batch(fn);
385
495
  else fn();
386
- }
496
+ },
497
+ track: (store) => pluginHistory.track(store),
498
+ react: (fn) => pluginHistory.react(fn)
387
499
  }), [
388
500
  controlled,
389
501
  historyEnabled,
@@ -391,19 +503,21 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
391
503
  snapCanRedo,
392
504
  coreCanUndo,
393
505
  coreCanRedo,
394
- snapshotHistory
506
+ snapshotHistory,
507
+ pluginHistory
395
508
  ]);
396
509
  const latestHistory = useLatest(inkHistory);
397
510
  /** The document's tool style; it batches through `history`, whichever mode is on. */
398
511
  const styleTracker = useMemo(() => document ? trackToolStyle({
399
512
  store: document.annotations,
400
513
  history: { batch: (fn) => latestHistory.current.batch(fn) },
401
- tools: latest.current.hostTools,
514
+ tools: [...latest.current.hostTools ?? [], ...pluginHost?.tools ?? []],
402
515
  tool: latest.current.tool
403
516
  }) : null, [
404
517
  document,
405
518
  latest,
406
- latestHistory
519
+ latestHistory,
520
+ pluginHost
407
521
  ]);
408
522
  useEffect(() => {
409
523
  styleTracker?.setTool(internalTool);
@@ -427,6 +541,22 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
427
541
  }, [signatures]);
428
542
  /** Tool options last as long as the root; the tool style is per document. */
429
543
  const [toolOptions] = useState(createToolOptions);
544
+ const decorate = useCallback((a) => latest.current.decorateAnnotation?.(a), [latest]);
545
+ const [decorateListeners] = useState(() => /* @__PURE__ */ new Set());
546
+ const onDecorateAnnotationChange = useCallback((listener) => {
547
+ decorateListeners.add(listener);
548
+ return () => {
549
+ decorateListeners.delete(listener);
550
+ };
551
+ }, [decorateListeners]);
552
+ const decoratedOnce = useRef(false);
553
+ useEffect(() => {
554
+ if (!decoratedOnce.current) {
555
+ decoratedOnce.current = true;
556
+ return;
557
+ }
558
+ for (const listener of decorateListeners) listener();
559
+ }, [decorateAnnotation, decorateListeners]);
430
560
  const ctx = useMemo(() => ({
431
561
  assets,
432
562
  document,
@@ -438,6 +568,8 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
438
568
  tool: internalTool,
439
569
  setTool,
440
570
  hostTools,
571
+ decorateAnnotation: decorate,
572
+ onDecorateAnnotationChange,
441
573
  fonts,
442
574
  toolOptions,
443
575
  signatures,
@@ -449,6 +581,8 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
449
581
  reportPage,
450
582
  activated,
451
583
  setActivated,
584
+ pluginHost,
585
+ viewportEl: plugins.root,
452
586
  history: inkHistory,
453
587
  rootEl,
454
588
  setViewport
@@ -463,6 +597,8 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
463
597
  internalTool,
464
598
  setTool,
465
599
  hostTools,
600
+ decorate,
601
+ onDecorateAnnotationChange,
466
602
  fonts,
467
603
  toolOptions,
468
604
  signatures,
@@ -474,6 +610,8 @@ function InkRoot({ src, assets, password, defaults, annotations, onAnnotationsCh
474
610
  reportPage,
475
611
  activated,
476
612
  setActivated,
613
+ pluginHost,
614
+ plugins,
477
615
  inkHistory,
478
616
  rootEl,
479
617
  setViewport
@@ -523,7 +661,6 @@ function InkAnnotation({ annotation, element, children }) {
523
661
  return () => observer.disconnect();
524
662
  }, [element]);
525
663
  const { id } = annotation;
526
- const isSuggestion = annotation.type === "redact" && annotation.origin === "suggested";
527
664
  const locked = annotation.locked === true;
528
665
  const selected = ctx.selection.includes(id);
529
666
  const store = ctx.document?.annotations;
@@ -532,21 +669,14 @@ function InkAnnotation({ annotation, element, children }) {
532
669
  state: observed ?? (selected ? "selected" : "idle"),
533
670
  locked,
534
671
  select: () => store?.select([id]),
535
- remove: () => void store?.remove(id),
536
- accept: () => {
537
- if (store && isSuggestion) acceptSuggestion(store, id);
538
- },
539
- dismiss: () => {
540
- if (isSuggestion) store?.remove(id);
541
- }
672
+ remove: () => void store?.remove(id)
542
673
  }), [
543
674
  annotation,
544
675
  observed,
545
676
  selected,
546
677
  locked,
547
678
  store,
548
- id,
549
- isSuggestion
679
+ id
550
680
  ]);
551
681
  return /* @__PURE__ */ jsx(Fragment$1, { children: children(slotProps) });
552
682
  }
@@ -657,6 +787,8 @@ function InkViewport({ mode = "continuous", rotation = 0, gap = 16, buffer = 2,
657
787
  buffer: b,
658
788
  tool: c.tool,
659
789
  ...c.hostTools ? { tools: c.hostTools } : {},
790
+ ...c.pluginHost ? { plugins: c.pluginHost } : {},
791
+ decorateAnnotation: (a) => latest.current.ctx.decorateAnnotation(a),
660
792
  ...c.toolStyle ? { toolStyle: c.toolStyle } : {},
661
793
  toolHistory: { batch: (fn) => latest.current.ctx.history.batch(fn) },
662
794
  toolOptions: c.toolOptions,
@@ -682,7 +814,14 @@ function InkViewport({ mode = "continuous", rotation = 0, gap = 16, buffer = 2,
682
814
  lastReportedPage.current = p;
683
815
  latest.current.ctx.reportPage(p);
684
816
  };
685
- stops = [vp.zoom.subscribe(reportZoom), vp.currentPage.subscribe(reportPage)];
817
+ const followTool = (id) => {
818
+ if (id !== latest.current.ctx.tool) latest.current.ctx.setTool(id);
819
+ };
820
+ stops = [
821
+ vp.zoom.subscribe(reportZoom),
822
+ vp.currentPage.subscribe(reportPage),
823
+ tc.active.subscribe(followTool)
824
+ ];
686
825
  reportZoom(vp.zoom.get());
687
826
  reportPage(vp.currentPage.get());
688
827
  },
@@ -716,8 +855,19 @@ function InkViewport({ mode = "continuous", rotation = 0, gap = 16, buffer = 2,
716
855
  }, [
717
856
  ctx.document,
718
857
  ctx.status.state,
858
+ ctx.pluginHost,
719
859
  latest
720
860
  ]);
861
+ const { viewportEl } = ctx;
862
+ useLayoutEffect(() => {
863
+ const el = scroll.current;
864
+ viewportEl.set(el);
865
+ return () => {
866
+ if (viewportEl.get() === el) viewportEl.set(null);
867
+ };
868
+ }, [viewportEl]);
869
+ const { onDecorateAnnotationChange } = ctx;
870
+ useEffect(() => onDecorateAnnotationChange(() => engine.current?.redecorate()), [onDecorateAnnotationChange]);
721
871
  useEffect(() => {
722
872
  const built = builtWith.current;
723
873
  if (!engine.current || !built) return;
@@ -854,10 +1004,13 @@ const ICONS = {
854
1004
  signature: Signature
855
1005
  };
856
1006
  function InkToolButton({ tool, label, asChild = false, className, children, onClick, ...rest }) {
1007
+ const { hostTools, pluginHost } = useInkContext("InkToolButton");
857
1008
  const { tool: current, setTool } = useTool();
858
1009
  const active = current === tool;
859
- const name = label ?? TOOL_LABELS[tool] ?? tool;
860
- const icon = ICONS[tool];
1010
+ /** A host or plugin tool by this id, for the name and icon core has none for. */
1011
+ const own = [...hostTools ?? [], ...pluginHost?.tools ?? []].find((t) => t.id === tool);
1012
+ const name = label ?? TOOL_LABELS[tool] ?? own?.label ?? tool;
1013
+ const icon = ICONS[tool] ?? own?.icon;
861
1014
  return /* @__PURE__ */ jsx(Toolbar.Button, {
862
1015
  asChild,
863
1016
  className: cx("ink-tool-button", className),
@@ -879,6 +1032,38 @@ function InkToolButton({ tool, label, asChild = false, className, children, onCl
879
1032
  });
880
1033
  }
881
1034
  //#endregion
1035
+ //#region src/components/InkCommand.tsx
1036
+ function InkCommand({ id, asChild = false, children, disabled, onClick, ...rest }) {
1037
+ const { run, enabled, busy, status, label, icon } = useCommand(id);
1038
+ return /* @__PURE__ */ jsxs("div", {
1039
+ className: "ink-command",
1040
+ children: [/* @__PURE__ */ jsx(Toolbar.Button, {
1041
+ asChild,
1042
+ type: "button",
1043
+ "data-command": id,
1044
+ "aria-label": label || void 0,
1045
+ title: label || void 0,
1046
+ "aria-hidden": label ? void 0 : true,
1047
+ "aria-busy": busy,
1048
+ "aria-disabled": busy || void 0,
1049
+ ...rest,
1050
+ disabled: disabled === true || !enabled && !busy,
1051
+ onClick: (e) => {
1052
+ onClick?.(e);
1053
+ if (!e.defaultPrevented) run();
1054
+ },
1055
+ children: typeof children === "function" ? children({
1056
+ label,
1057
+ busy
1058
+ }) : children ?? (icon ? /* @__PURE__ */ jsx(InkIcon, { icon }) : label)
1059
+ }), /* @__PURE__ */ jsx("span", {
1060
+ className: "ink-command-status",
1061
+ role: "status",
1062
+ children: status
1063
+ })]
1064
+ });
1065
+ }
1066
+ //#endregion
882
1067
  //#region src/components/InkToolbarSeparator.tsx
883
1068
  /**
884
1069
  * A divider between groups of controls in a horizontal toolbar. Not focusable. A plain div
@@ -1152,11 +1337,11 @@ function InkNextPage({ asChild = false, className, children, disabled, onClick,
1152
1337
  }
1153
1338
  //#endregion
1154
1339
  //#region src/components/InkColorMenu.tsx
1155
- function InkColorMenu({ property, label, className, children, standalone = false, group, disabled, ...rest }) {
1340
+ function InkColorMenu({ property, label, className, children, standalone = false, group, fallback, disabled, ...rest }) {
1156
1341
  const Wrap = standalone ? Fragment : Toolbar.Button;
1157
1342
  const { current, supported, set } = useToolStyle(group);
1158
1343
  const [open, setOpen] = useState(false);
1159
- const model = colorMenuModel(property, current, supported);
1344
+ const model = colorMenuModel(property, current, supported, fallback);
1160
1345
  const name = label ?? model.label;
1161
1346
  /** ArrowDown opens on the first item; a click opens on the checked one. */
1162
1347
  const focusFirst = useRef(false);
@@ -1295,93 +1480,100 @@ function InkShapeMenu({ className, children, ...rest }) {
1295
1480
  const active = tool === "shape";
1296
1481
  /** ArrowDown opens on the first item; a click opens on the checked one. */
1297
1482
  const focusFirst = useRef(false);
1298
- return /* @__PURE__ */ jsxs("div", {
1299
- role: "group",
1300
- "aria-label": "Shape",
1301
- className: cx("ink-split-button ink-shape-menu", className),
1302
- "data-ink-shape-menu": "",
1303
- ...rest,
1304
- children: [/* @__PURE__ */ jsx(Toolbar.Button, {
1305
- className: "ink-tool-button ink-shape-button",
1306
- "data-ink-tool-button": "",
1307
- "data-ink-shape-button": "",
1308
- "data-tool": "shape",
1309
- "data-kind": model.kind,
1310
- "data-state": active ? "on" : "off",
1311
- "aria-pressed": active,
1312
- "aria-label": model.label,
1313
- title: model.label,
1314
- onClick: () => setTool("shape"),
1315
- children: children ?? /* @__PURE__ */ jsx(InkIcon, { icon: SHAPE_ICONS[model.kind] })
1316
- }), /* @__PURE__ */ jsxs(Popover.Root, {
1317
- open,
1318
- onOpenChange: setOpen,
1319
- children: [/* @__PURE__ */ jsx(Toolbar.Button, {
1320
- asChild: true,
1321
- children: /* @__PURE__ */ jsx(Popover.Trigger, {
1322
- asChild: true,
1323
- children: /* @__PURE__ */ jsx("button", {
1324
- type: "button",
1325
- className: "ink-menu-trigger ink-shape-menu-trigger",
1326
- "data-ink-shape-menu-trigger": "",
1327
- "data-state": open ? "open" : "closed",
1328
- "aria-label": "Choose shape",
1329
- title: "Choose shape",
1330
- "aria-haspopup": "menu",
1331
- onKeyDown: (e) => {
1332
- if (e.key !== "ArrowDown" || e.defaultPrevented || open) return;
1483
+ return /* @__PURE__ */ jsx(Popover.Root, {
1484
+ open,
1485
+ onOpenChange: setOpen,
1486
+ children: /* @__PURE__ */ jsx(Popover.Anchor, {
1487
+ asChild: true,
1488
+ children: /* @__PURE__ */ jsxs("div", {
1489
+ role: "group",
1490
+ "aria-label": "Shape",
1491
+ className: cx("ink-split-button ink-shape-menu", className),
1492
+ "data-ink-shape-menu": "",
1493
+ ...rest,
1494
+ children: [
1495
+ /* @__PURE__ */ jsx(Toolbar.Button, {
1496
+ className: "ink-tool-button ink-shape-button",
1497
+ "data-ink-tool-button": "",
1498
+ "data-ink-shape-button": "",
1499
+ "data-tool": "shape",
1500
+ "data-kind": model.kind,
1501
+ "data-state": active ? "on" : "off",
1502
+ "aria-pressed": active,
1503
+ "aria-label": model.label,
1504
+ title: model.label,
1505
+ onClick: () => setTool("shape"),
1506
+ children: children ?? /* @__PURE__ */ jsx(InkIcon, { icon: SHAPE_ICONS[model.kind] })
1507
+ }),
1508
+ /* @__PURE__ */ jsx(Toolbar.Button, {
1509
+ asChild: true,
1510
+ children: /* @__PURE__ */ jsx(Popover.Trigger, {
1511
+ asChild: true,
1512
+ children: /* @__PURE__ */ jsx("button", {
1513
+ type: "button",
1514
+ className: "ink-menu-trigger ink-shape-menu-trigger",
1515
+ "data-ink-shape-menu-trigger": "",
1516
+ "data-state": open ? "open" : "closed",
1517
+ "aria-label": "Choose shape",
1518
+ title: "Choose shape",
1519
+ "aria-haspopup": "menu",
1520
+ onKeyDown: (e) => {
1521
+ if (e.key !== "ArrowDown" || e.defaultPrevented || open) return;
1522
+ e.preventDefault();
1523
+ focusFirst.current = true;
1524
+ setOpen(true);
1525
+ },
1526
+ children: /* @__PURE__ */ jsx(InkIcon, { icon: ChevronDown })
1527
+ })
1528
+ })
1529
+ }),
1530
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Content, {
1531
+ role: "menu",
1532
+ "aria-label": "Shapes",
1533
+ className: "ink-select-content ink-menu-content",
1534
+ "data-ink-select-content": "",
1535
+ "data-ink-menu": "shape",
1536
+ side: "bottom",
1537
+ align: "start",
1538
+ sideOffset: 4,
1539
+ collisionPadding: 8,
1540
+ onOpenAutoFocus: (e) => {
1333
1541
  e.preventDefault();
1334
- focusFirst.current = true;
1335
- setOpen(true);
1336
- },
1337
- children: /* @__PURE__ */ jsx(InkIcon, { icon: ChevronDown })
1338
- })
1339
- })
1340
- }), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Content, {
1341
- role: "menu",
1342
- "aria-label": "Shapes",
1343
- className: "ink-select-content ink-menu-content",
1344
- "data-ink-select-content": "",
1345
- "data-ink-menu": "shape",
1346
- side: "bottom",
1347
- align: "start",
1348
- sideOffset: 4,
1349
- collisionPadding: 8,
1350
- onOpenAutoFocus: (e) => {
1351
- e.preventDefault();
1352
- const menu = e.currentTarget;
1353
- if (focusFirst.current) menuItems(menu)[0]?.focus({ preventScroll: true });
1354
- else focusInitialMenuItem(menu);
1355
- focusFirst.current = false;
1356
- },
1357
- onKeyDown: (e) => handleMenuKeys(e.nativeEvent, e.currentTarget),
1358
- children: /* @__PURE__ */ jsx("div", {
1359
- role: "group",
1360
- "aria-label": "Shapes",
1361
- className: "ink-shape-grid",
1362
- "data-ink-menu-columns": SHAPE_COLUMNS,
1363
- children: model.items.map((item) => /* @__PURE__ */ jsxs("button", {
1364
- type: "button",
1365
- role: "menuitemradio",
1366
- "aria-checked": item.checked,
1367
- tabIndex: -1,
1368
- className: "ink-select-item ink-menu-item ink-shape-item",
1369
- "data-ink-select-item": "",
1370
- "data-key": item.kind,
1371
- "data-state": item.checked ? "checked" : "unchecked",
1372
- onClick: () => {
1373
- setOptions({ kind: item.kind });
1374
- setTool("shape");
1375
- setOpen(false);
1542
+ const menu = e.currentTarget;
1543
+ if (focusFirst.current) menuItems(menu)[0]?.focus({ preventScroll: true });
1544
+ else focusInitialMenuItem(menu);
1545
+ focusFirst.current = false;
1376
1546
  },
1377
- children: [/* @__PURE__ */ jsx(InkIcon, {
1378
- icon: SHAPE_ICONS[item.kind],
1379
- size: 16
1380
- }), item.label]
1381
- }, item.kind))
1382
- })
1383
- }) })]
1384
- })]
1547
+ onKeyDown: (e) => handleMenuKeys(e.nativeEvent, e.currentTarget),
1548
+ children: /* @__PURE__ */ jsx("div", {
1549
+ role: "group",
1550
+ "aria-label": "Shapes",
1551
+ className: "ink-shape-grid",
1552
+ "data-ink-menu-columns": SHAPE_COLUMNS,
1553
+ children: model.items.map((item) => /* @__PURE__ */ jsxs("button", {
1554
+ type: "button",
1555
+ role: "menuitemradio",
1556
+ "aria-checked": item.checked,
1557
+ tabIndex: -1,
1558
+ className: "ink-select-item ink-menu-item ink-shape-item",
1559
+ "data-ink-select-item": "",
1560
+ "data-key": item.kind,
1561
+ "data-state": item.checked ? "checked" : "unchecked",
1562
+ onClick: () => {
1563
+ setOptions({ kind: item.kind });
1564
+ setTool("shape");
1565
+ setOpen(false);
1566
+ },
1567
+ children: [/* @__PURE__ */ jsx(InkIcon, {
1568
+ icon: SHAPE_ICONS[item.kind],
1569
+ size: 16
1570
+ }), item.label]
1571
+ }, item.kind))
1572
+ })
1573
+ }) })
1574
+ ]
1575
+ })
1576
+ })
1385
1577
  });
1386
1578
  }
1387
1579
  //#endregion
@@ -1654,153 +1846,157 @@ function InkSignatureMenu({ className, children, fonts, ...rest }) {
1654
1846
  removedRow.current = null;
1655
1847
  });
1656
1848
  };
1657
- return /* @__PURE__ */ jsxs("div", {
1658
- role: "group",
1659
- "aria-label": SIGNATURE_LABELS.sign,
1660
- className: cx("ink-split-button ink-signature-menu", className),
1661
- "data-ink-signature-menu": "",
1662
- ...rest,
1663
- children: [
1664
- /* @__PURE__ */ jsx(Toolbar.Button, {
1665
- ref: main,
1666
- className: "ink-tool-button ink-signature-button",
1667
- "data-ink-tool-button": "",
1668
- "data-ink-signature-button": "",
1669
- "data-tool": "signature",
1670
- "data-state": active ? "on" : "off",
1671
- "aria-pressed": active,
1849
+ return /* @__PURE__ */ jsx(Popover.Root, {
1850
+ open,
1851
+ onOpenChange: setOpen,
1852
+ children: /* @__PURE__ */ jsx(Popover.Anchor, {
1853
+ asChild: true,
1854
+ children: /* @__PURE__ */ jsxs("div", {
1855
+ role: "group",
1672
1856
  "aria-label": SIGNATURE_LABELS.sign,
1673
- title: SIGNATURE_LABELS.sign,
1674
- onClick: () => {
1675
- if (model.selected) setTool("signature");
1676
- else setOpen(true);
1677
- },
1678
- children: children ?? /* @__PURE__ */ jsx(InkIcon, { icon: Signature })
1679
- }),
1680
- /* @__PURE__ */ jsxs(Popover.Root, {
1681
- open,
1682
- onOpenChange: setOpen,
1683
- children: [/* @__PURE__ */ jsx(Toolbar.Button, {
1684
- asChild: true,
1685
- children: /* @__PURE__ */ jsx(Popover.Trigger, {
1857
+ className: cx("ink-split-button ink-signature-menu", className),
1858
+ "data-ink-signature-menu": "",
1859
+ ...rest,
1860
+ children: [
1861
+ /* @__PURE__ */ jsx(Toolbar.Button, {
1862
+ ref: main,
1863
+ className: "ink-tool-button ink-signature-button",
1864
+ "data-ink-tool-button": "",
1865
+ "data-ink-signature-button": "",
1866
+ "data-tool": "signature",
1867
+ "data-state": active ? "on" : "off",
1868
+ "aria-pressed": active,
1869
+ "aria-label": SIGNATURE_LABELS.sign,
1870
+ title: SIGNATURE_LABELS.sign,
1871
+ onClick: () => {
1872
+ if (model.selected) setTool("signature");
1873
+ else setOpen(true);
1874
+ },
1875
+ children: children ?? /* @__PURE__ */ jsx(InkIcon, { icon: Signature })
1876
+ }),
1877
+ /* @__PURE__ */ jsx(Toolbar.Button, {
1686
1878
  asChild: true,
1687
- children: /* @__PURE__ */ jsx("button", {
1688
- type: "button",
1689
- className: "ink-menu-trigger ink-signature-menu-trigger",
1690
- "data-ink-signature-menu-trigger": "",
1691
- "data-state": open ? "open" : "closed",
1692
- "aria-label": SIGNATURE_LABELS.trigger,
1693
- title: SIGNATURE_LABELS.trigger,
1694
- "aria-haspopup": "menu",
1695
- onKeyDown: (e) => {
1696
- if (e.key !== "ArrowDown" || e.defaultPrevented || open) return;
1697
- e.preventDefault();
1698
- focusFirst.current = true;
1699
- setOpen(true);
1700
- },
1701
- children: /* @__PURE__ */ jsx(InkIcon, { icon: ChevronDown })
1879
+ children: /* @__PURE__ */ jsx(Popover.Trigger, {
1880
+ asChild: true,
1881
+ children: /* @__PURE__ */ jsx("button", {
1882
+ type: "button",
1883
+ className: "ink-menu-trigger ink-signature-menu-trigger",
1884
+ "data-ink-signature-menu-trigger": "",
1885
+ "data-state": open ? "open" : "closed",
1886
+ "aria-label": SIGNATURE_LABELS.trigger,
1887
+ title: SIGNATURE_LABELS.trigger,
1888
+ "aria-haspopup": "menu",
1889
+ onKeyDown: (e) => {
1890
+ if (e.key !== "ArrowDown" || e.defaultPrevented || open) return;
1891
+ e.preventDefault();
1892
+ focusFirst.current = true;
1893
+ setOpen(true);
1894
+ },
1895
+ children: /* @__PURE__ */ jsx(InkIcon, { icon: ChevronDown })
1896
+ })
1702
1897
  })
1703
- })
1704
- }), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, {
1705
- ref: menu,
1706
- role: "menu",
1707
- "aria-label": SIGNATURE_LABELS.menu,
1708
- className: "ink-select-content ink-menu-content",
1709
- "data-ink-select-content": "",
1710
- "data-ink-menu": "signature",
1711
- side: "bottom",
1712
- align: "start",
1713
- sideOffset: 4,
1714
- collisionPadding: 8,
1715
- onOpenAutoFocus: (e) => {
1716
- e.preventDefault();
1717
- const el = e.currentTarget;
1718
- if (focusFirst.current) menuItems(el)[0]?.focus({ preventScroll: true });
1719
- else focusInitialMenuItem(el);
1720
- focusFirst.current = false;
1721
- },
1722
- onCloseAutoFocus: (e) => {
1723
- if (toDialog.current) e.preventDefault();
1724
- toDialog.current = false;
1725
- },
1726
- onKeyDown: (e) => handleMenuKeys(e.nativeEvent, e.currentTarget),
1727
- children: [
1728
- model.items.length > 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
1729
- role: "group",
1730
- "aria-label": SIGNATURE_LABELS.saved,
1731
- className: "ink-signature-list",
1732
- "data-ink-menu-columns": 2,
1733
- children: model.items.map((item, row) => [/* @__PURE__ */ jsx("button", {
1898
+ }),
1899
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, {
1900
+ ref: menu,
1901
+ role: "menu",
1902
+ "aria-label": SIGNATURE_LABELS.menu,
1903
+ className: "ink-select-content ink-menu-content",
1904
+ "data-ink-select-content": "",
1905
+ "data-ink-menu": "signature",
1906
+ side: "bottom",
1907
+ align: "start",
1908
+ sideOffset: 4,
1909
+ collisionPadding: 8,
1910
+ onOpenAutoFocus: (e) => {
1911
+ e.preventDefault();
1912
+ const el = e.currentTarget;
1913
+ if (focusFirst.current) menuItems(el)[0]?.focus({ preventScroll: true });
1914
+ else focusInitialMenuItem(el);
1915
+ focusFirst.current = false;
1916
+ },
1917
+ onCloseAutoFocus: (e) => {
1918
+ if (toDialog.current) e.preventDefault();
1919
+ toDialog.current = false;
1920
+ },
1921
+ onKeyDown: (e) => handleMenuKeys(e.nativeEvent, e.currentTarget),
1922
+ children: [
1923
+ model.items.length > 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
1924
+ role: "group",
1925
+ "aria-label": SIGNATURE_LABELS.saved,
1926
+ className: "ink-signature-list",
1927
+ "data-ink-menu-columns": 2,
1928
+ children: model.items.map((item, row) => [/* @__PURE__ */ jsx("button", {
1929
+ type: "button",
1930
+ role: "menuitemradio",
1931
+ "aria-checked": item.checked,
1932
+ "aria-label": item.label,
1933
+ tabIndex: -1,
1934
+ className: "ink-select-item ink-menu-item ink-signature-item",
1935
+ "data-ink-select-item": "",
1936
+ "data-key": item.id,
1937
+ "data-state": item.checked ? "checked" : "unchecked",
1938
+ onClick: () => {
1939
+ setOptions({ templateId: item.id });
1940
+ setTool("signature");
1941
+ setOpen(false);
1942
+ },
1943
+ children: /* @__PURE__ */ jsx(SignaturePreview, { template: item.template })
1944
+ }, `${item.id}-choose`), /* @__PURE__ */ jsx("button", {
1945
+ type: "button",
1946
+ role: "menuitem",
1947
+ "aria-label": item.removeLabel,
1948
+ title: item.removeLabel,
1949
+ tabIndex: -1,
1950
+ className: "ink-menu-item ink-signature-remove",
1951
+ "data-ink-signature-remove": "",
1952
+ "data-key": item.id,
1953
+ onClick: () => remove(item.id, row),
1954
+ children: /* @__PURE__ */ jsx(InkIcon, {
1955
+ icon: Trash2,
1956
+ size: 16
1957
+ })
1958
+ }, `${item.id}-remove`)])
1959
+ }), /* @__PURE__ */ jsx("div", {
1960
+ role: "separator",
1961
+ className: "ink-select-separator"
1962
+ })] }) : null,
1963
+ /* @__PURE__ */ jsxs("button", {
1734
1964
  type: "button",
1735
- role: "menuitemradio",
1736
- "aria-checked": item.checked,
1737
- "aria-label": item.label,
1965
+ role: "menuitem",
1738
1966
  tabIndex: -1,
1739
- className: "ink-select-item ink-menu-item ink-signature-item",
1967
+ className: "ink-select-item ink-menu-item ink-signature-create",
1740
1968
  "data-ink-select-item": "",
1741
- "data-key": item.id,
1742
- "data-state": item.checked ? "checked" : "unchecked",
1969
+ "data-ink-signature-create": "",
1743
1970
  onClick: () => {
1744
- setOptions({ templateId: item.id });
1745
- setTool("signature");
1971
+ toDialog.current = true;
1746
1972
  setOpen(false);
1973
+ setCreating(true);
1747
1974
  },
1748
- children: /* @__PURE__ */ jsx(SignaturePreview, { template: item.template })
1749
- }, `${item.id}-choose`), /* @__PURE__ */ jsx("button", {
1750
- type: "button",
1751
- role: "menuitem",
1752
- "aria-label": item.removeLabel,
1753
- title: item.removeLabel,
1754
- tabIndex: -1,
1755
- className: "ink-menu-item ink-signature-remove",
1756
- "data-ink-signature-remove": "",
1757
- "data-key": item.id,
1758
- onClick: () => remove(item.id, row),
1759
- children: /* @__PURE__ */ jsx(InkIcon, {
1760
- icon: Trash2,
1975
+ children: [/* @__PURE__ */ jsx(InkIcon, {
1976
+ icon: Plus,
1761
1977
  size: 16
1762
- })
1763
- }, `${item.id}-remove`)])
1764
- }), /* @__PURE__ */ jsx("div", {
1765
- role: "separator",
1766
- className: "ink-select-separator"
1767
- })] }) : null,
1768
- /* @__PURE__ */ jsxs("button", {
1769
- type: "button",
1770
- role: "menuitem",
1771
- tabIndex: -1,
1772
- className: "ink-select-item ink-menu-item ink-signature-create",
1773
- "data-ink-select-item": "",
1774
- "data-ink-signature-create": "",
1775
- onClick: () => {
1776
- toDialog.current = true;
1777
- setOpen(false);
1778
- setCreating(true);
1779
- },
1780
- children: [/* @__PURE__ */ jsx(InkIcon, {
1781
- icon: Plus,
1782
- size: 16
1783
- }), SIGNATURE_LABELS.create]
1784
- }),
1785
- removeError ? /* @__PURE__ */ jsx("p", {
1786
- className: "ink-field-error ink-signature-remove-error",
1787
- role: "alert",
1788
- "data-ink-signature-remove-error": "",
1789
- children: removeError
1790
- }) : null
1791
- ]
1792
- }) })]
1793
- }),
1794
- /* @__PURE__ */ jsx(InkSignatureDialog, {
1795
- open: creating,
1796
- onOpenChange: setCreating,
1797
- fonts,
1798
- onCloseAutoFocus: (e) => {
1799
- e.preventDefault();
1800
- main.current?.focus({ preventScroll: true });
1801
- }
1978
+ }), SIGNATURE_LABELS.create]
1979
+ }),
1980
+ removeError ? /* @__PURE__ */ jsx("p", {
1981
+ className: "ink-field-error ink-signature-remove-error",
1982
+ role: "alert",
1983
+ "data-ink-signature-remove-error": "",
1984
+ children: removeError
1985
+ }) : null
1986
+ ]
1987
+ }) }),
1988
+ /* @__PURE__ */ jsx(InkSignatureDialog, {
1989
+ open: creating,
1990
+ onOpenChange: setCreating,
1991
+ fonts,
1992
+ onCloseAutoFocus: (e) => {
1993
+ e.preventDefault();
1994
+ main.current?.focus({ preventScroll: true });
1995
+ }
1996
+ })
1997
+ ]
1802
1998
  })
1803
- ]
1999
+ })
1804
2000
  });
1805
2001
  }
1806
2002
  //#endregion
@@ -1808,122 +2004,131 @@ function InkSignatureMenu({ className, children, fonts, ...rest }) {
1808
2004
  function InkHighlightMenu({ className, children, ...rest }) {
1809
2005
  const { tool, setTool } = useTool();
1810
2006
  const [options, setOptions] = useToolOptions("highlight");
2007
+ const { document: doc } = useDocument();
1811
2008
  const [open, setOpen] = useState(false);
1812
2009
  const model = highlightMenuModel(options, MARKUP_KINDS);
1813
2010
  const active = tool === "highlight";
1814
2011
  /** ArrowDown opens on the first item; a click opens on the checked one. */
1815
2012
  const focusFirst = useRef(false);
1816
- return /* @__PURE__ */ jsxs("div", {
1817
- role: "group",
1818
- "aria-label": "Highlight",
1819
- className: cx("ink-split-button ink-highlight-menu", className),
1820
- "data-ink-highlight-menu": "",
1821
- ...rest,
1822
- children: [/* @__PURE__ */ jsx(Toolbar.Button, {
1823
- className: "ink-tool-button ink-highlight-button",
1824
- "data-ink-tool-button": "",
1825
- "data-ink-highlight-button": "",
1826
- "data-tool": "highlight",
1827
- "data-kind": model.kind,
1828
- "data-state": active ? "on" : "off",
1829
- "aria-pressed": active,
1830
- "aria-label": model.label,
1831
- title: model.label,
1832
- onClick: () => setTool("highlight"),
1833
- children: children ?? /* @__PURE__ */ jsx(InkIcon, { icon: MARKUP_ICONS[model.kind] })
1834
- }), /* @__PURE__ */ jsxs(Popover.Root, {
1835
- open,
1836
- onOpenChange: setOpen,
1837
- children: [/* @__PURE__ */ jsx(Toolbar.Button, {
1838
- asChild: true,
1839
- children: /* @__PURE__ */ jsx(Popover.Trigger, {
1840
- asChild: true,
1841
- children: /* @__PURE__ */ jsx("button", {
1842
- type: "button",
1843
- className: "ink-menu-trigger ink-highlight-menu-trigger",
1844
- "data-ink-highlight-menu-trigger": "",
1845
- "data-state": open ? "open" : "closed",
1846
- "aria-label": "Choose highlight",
1847
- title: "Choose highlight",
1848
- "aria-haspopup": "menu",
1849
- onKeyDown: (e) => {
1850
- if (e.key !== "ArrowDown" || e.defaultPrevented || open) return;
1851
- e.preventDefault();
1852
- focusFirst.current = true;
1853
- setOpen(true);
1854
- },
1855
- children: /* @__PURE__ */ jsx(InkIcon, { icon: ChevronDown })
1856
- })
1857
- })
1858
- }), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, {
1859
- role: "menu",
1860
- "aria-label": "Highlights",
1861
- className: "ink-select-content ink-menu-content",
1862
- "data-ink-select-content": "",
1863
- "data-ink-menu": "highlight",
1864
- side: "bottom",
1865
- align: "start",
1866
- sideOffset: 4,
1867
- collisionPadding: 8,
1868
- onOpenAutoFocus: (e) => {
1869
- e.preventDefault();
1870
- const menu = e.currentTarget;
1871
- if (focusFirst.current) menuItems(menu)[0]?.focus({ preventScroll: true });
1872
- else focusInitialMenuItem(menu);
1873
- focusFirst.current = false;
1874
- },
1875
- onKeyDownCapture: (e) => {
1876
- if (e.currentTarget.contains(e.target)) handleMenuKeys(e.nativeEvent, e.currentTarget);
1877
- },
2013
+ return /* @__PURE__ */ jsx(Popover.Root, {
2014
+ open,
2015
+ onOpenChange: setOpen,
2016
+ children: /* @__PURE__ */ jsx(Popover.Anchor, {
2017
+ asChild: true,
2018
+ children: /* @__PURE__ */ jsxs("div", {
2019
+ role: "group",
2020
+ "aria-label": "Highlight",
2021
+ className: cx("ink-split-button ink-highlight-menu", className),
2022
+ "data-ink-highlight-menu": "",
2023
+ ...rest,
1878
2024
  children: [
1879
- /* @__PURE__ */ jsx("div", {
1880
- role: "group",
1881
- "aria-label": "Kinds",
1882
- className: "ink-highlight-kinds",
1883
- children: model.items.map((item) => /* @__PURE__ */ jsxs("button", {
1884
- type: "button",
1885
- role: "menuitemradio",
1886
- "aria-checked": item.checked,
1887
- tabIndex: -1,
1888
- className: "ink-select-item ink-menu-item ink-highlight-item",
1889
- "data-ink-select-item": "",
1890
- "data-key": item.kind,
1891
- "data-state": item.checked ? "checked" : "unchecked",
1892
- onClick: () => {
1893
- setOptions({ kind: item.kind });
1894
- setTool("highlight");
1895
- setOpen(false);
1896
- },
1897
- children: [/* @__PURE__ */ jsx(InkIcon, {
1898
- icon: MARKUP_ICONS[item.kind],
1899
- size: 16
1900
- }), item.label]
1901
- }, item.kind))
2025
+ /* @__PURE__ */ jsx(Toolbar.Button, {
2026
+ className: "ink-tool-button ink-highlight-button",
2027
+ "data-ink-tool-button": "",
2028
+ "data-ink-highlight-button": "",
2029
+ "data-tool": "highlight",
2030
+ "data-kind": model.kind,
2031
+ "data-state": active ? "on" : "off",
2032
+ "aria-pressed": active,
2033
+ "aria-label": model.label,
2034
+ title: model.label,
2035
+ onClick: () => setTool("highlight"),
2036
+ children: children ?? /* @__PURE__ */ jsx(InkIcon, { icon: MARKUP_ICONS[model.kind] })
1902
2037
  }),
1903
- /* @__PURE__ */ jsx("div", {
1904
- role: "separator",
1905
- className: "ink-select-separator"
2038
+ /* @__PURE__ */ jsx(Toolbar.Button, {
2039
+ asChild: true,
2040
+ children: /* @__PURE__ */ jsx(Popover.Trigger, {
2041
+ asChild: true,
2042
+ children: /* @__PURE__ */ jsx("button", {
2043
+ type: "button",
2044
+ className: "ink-menu-trigger ink-highlight-menu-trigger",
2045
+ "data-ink-highlight-menu-trigger": "",
2046
+ "data-state": open ? "open" : "closed",
2047
+ "aria-label": "Choose highlight",
2048
+ title: "Choose highlight",
2049
+ "aria-haspopup": "menu",
2050
+ onKeyDown: (e) => {
2051
+ if (e.key !== "ArrowDown" || e.defaultPrevented || open) return;
2052
+ e.preventDefault();
2053
+ focusFirst.current = true;
2054
+ setOpen(true);
2055
+ },
2056
+ children: /* @__PURE__ */ jsx(InkIcon, { icon: ChevronDown })
2057
+ })
2058
+ })
1906
2059
  }),
1907
- /* @__PURE__ */ jsxs("div", {
1908
- role: "group",
1909
- "aria-label": "Colour",
1910
- className: "ink-highlight-colour",
1911
- children: [/* @__PURE__ */ jsx("span", {
1912
- className: "ink-highlight-colour-label",
1913
- "aria-hidden": "true",
1914
- children: "Colour"
1915
- }), /* @__PURE__ */ jsx(InkColorMenu, {
1916
- property: "border",
1917
- group: "markup",
1918
- label: "Highlight colour",
1919
- standalone: true,
1920
- role: "menuitem",
1921
- tabIndex: -1
1922
- })]
1923
- })
2060
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, {
2061
+ role: "menu",
2062
+ "aria-label": "Highlights",
2063
+ className: "ink-select-content ink-menu-content",
2064
+ "data-ink-select-content": "",
2065
+ "data-ink-menu": "highlight",
2066
+ side: "bottom",
2067
+ align: "start",
2068
+ sideOffset: 4,
2069
+ collisionPadding: 8,
2070
+ onOpenAutoFocus: (e) => {
2071
+ e.preventDefault();
2072
+ const menu = e.currentTarget;
2073
+ if (focusFirst.current) menuItems(menu)[0]?.focus({ preventScroll: true });
2074
+ else focusInitialMenuItem(menu);
2075
+ focusFirst.current = false;
2076
+ },
2077
+ onKeyDownCapture: (e) => {
2078
+ if (e.currentTarget.contains(e.target)) handleMenuKeys(e.nativeEvent, e.currentTarget);
2079
+ },
2080
+ children: [
2081
+ /* @__PURE__ */ jsx("div", {
2082
+ role: "group",
2083
+ "aria-label": "Kinds",
2084
+ className: "ink-highlight-kinds",
2085
+ children: model.items.map((item) => /* @__PURE__ */ jsxs("button", {
2086
+ type: "button",
2087
+ role: "menuitemradio",
2088
+ "aria-checked": item.checked,
2089
+ tabIndex: -1,
2090
+ className: "ink-select-item ink-menu-item ink-highlight-item",
2091
+ "data-ink-select-item": "",
2092
+ "data-key": item.kind,
2093
+ "data-state": item.checked ? "checked" : "unchecked",
2094
+ onClick: () => {
2095
+ setOptions({ kind: item.kind });
2096
+ setTool("highlight");
2097
+ setOpen(false);
2098
+ },
2099
+ children: [/* @__PURE__ */ jsx(InkIcon, {
2100
+ icon: MARKUP_ICONS[item.kind],
2101
+ size: 16
2102
+ }), item.label]
2103
+ }, item.kind))
2104
+ }),
2105
+ /* @__PURE__ */ jsx("div", {
2106
+ role: "separator",
2107
+ className: "ink-select-separator"
2108
+ }),
2109
+ /* @__PURE__ */ jsxs("div", {
2110
+ role: "group",
2111
+ "aria-label": "Colour",
2112
+ className: "ink-highlight-colour",
2113
+ children: [/* @__PURE__ */ jsx("span", {
2114
+ className: "ink-highlight-colour-label",
2115
+ "aria-hidden": "true",
2116
+ children: "Colour"
2117
+ }), /* @__PURE__ */ jsx(InkColorMenu, {
2118
+ property: "border",
2119
+ group: "markup",
2120
+ label: "Highlight colour",
2121
+ standalone: true,
2122
+ fallback: doc?.annotations.defaults.color(model.kind),
2123
+ role: "menuitem",
2124
+ tabIndex: -1
2125
+ })]
2126
+ })
2127
+ ]
2128
+ }) })
1924
2129
  ]
1925
- }) })]
1926
- })]
2130
+ })
2131
+ })
1927
2132
  });
1928
2133
  }
1929
2134
  //#endregion
@@ -2429,84 +2634,6 @@ function ThreadForm({ editingRoot, locked, deleteLabel, onSubmit, onRemove, onCl
2429
2634
  });
2430
2635
  }
2431
2636
  //#endregion
2432
- //#region src/components/InkSuggestionCard.tsx
2433
- function InkSuggestionCard({ children }) {
2434
- const ctx = useInkContext("InkSuggestionCard");
2435
- const { annotations, selection, accept: acceptId, dismiss: dismissId, select } = useAnnotations();
2436
- function suggestion(id) {
2437
- const a = id ? annotations.find((x) => x.id === id) : void 0;
2438
- return a?.type === "redact" && a.origin === "suggested" ? a : null;
2439
- }
2440
- const target = suggestion(ctx.activated) ?? (selection.length === 1 ? suggestion(selection[0]) : null);
2441
- const anchor = useAnnotationElement(ctx.rootEl, target?.id ?? null);
2442
- const anchorRef = useMemo(() => ({ current: anchor }), [anchor]);
2443
- const focusReturn = useFocusReturn(ctx);
2444
- const open = target !== null && anchor !== null;
2445
- const bucket = target?.suggestion ? confidenceBucket(target.suggestion.confidence) : void 0;
2446
- function close() {
2447
- ctx.setActivated(null);
2448
- select([]);
2449
- }
2450
- function accept() {
2451
- if (target) acceptId(target.id);
2452
- close();
2453
- }
2454
- /** Locked annotations cannot be removed: dismissing one does nothing and keeps the card. */
2455
- function dismiss() {
2456
- if (!target || target.locked) return;
2457
- focusReturn.beforeRemove(anchor, "[data-origin=\"suggested\"]");
2458
- dismissId(target.id);
2459
- close();
2460
- }
2461
- return /* @__PURE__ */ jsxs(Popover.Root, {
2462
- open,
2463
- onOpenChange: (next) => !next && close(),
2464
- children: [/* @__PURE__ */ jsx(Popover.Anchor, { virtualRef: anchorRef }), /* @__PURE__ */ jsx(Popover.Portal, { children: target && anchor ? /* @__PURE__ */ jsx(Popover.Content, {
2465
- className: "ink-suggestion-card",
2466
- "data-ink-suggestion-card": "",
2467
- side: "bottom",
2468
- sideOffset: 6,
2469
- collisionPadding: 8,
2470
- "aria-label": "Suggested redaction",
2471
- onInteractOutside: keepOpenOnAnnotation,
2472
- onCloseAutoFocus: (e) => focusReturn.onCloseAutoFocus(e, anchor),
2473
- children: children ? children({
2474
- annotation: target,
2475
- suggestion: target.suggestion,
2476
- bucket,
2477
- accept,
2478
- dismiss,
2479
- close
2480
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
2481
- /* @__PURE__ */ jsxs("p", {
2482
- className: "ink-suggestion-reason",
2483
- children: [/* @__PURE__ */ jsx("strong", { children: target.suggestion?.reason ?? "Suggested redaction" }), bucket ? /* @__PURE__ */ jsxs("span", {
2484
- className: "ink-suggestion-confidence",
2485
- "data-confidence": bucket,
2486
- children: [bucket, " confidence"]
2487
- }) : null]
2488
- }),
2489
- target.suggestion?.explanation ? /* @__PURE__ */ jsx("p", {
2490
- className: "ink-suggestion-explanation",
2491
- children: target.suggestion.explanation
2492
- }) : null,
2493
- /* @__PURE__ */ jsxs("div", {
2494
- className: "ink-suggestion-actions",
2495
- children: [/* @__PURE__ */ jsx("button", {
2496
- type: "button",
2497
- onClick: accept,
2498
- children: "Accept"
2499
- }), !target.locked ? /* @__PURE__ */ jsx("button", {
2500
- type: "button",
2501
- onClick: dismiss,
2502
- children: "Dismiss"
2503
- }) : null]
2504
- })
2505
- ] })
2506
- }) : null })]
2507
- });
2508
- }
2509
- //#endregion
2510
- export { InkAnnotation, InkColorMenu, InkHighlightMenu, InkIcon, InkLineStyleMenu, InkNextPage, InkPage, InkPageCount, InkPageInput, InkPrevPage, InkRedo, InkRoot, InkShapeMenu, InkSignatureDialog, InkSignatureMenu, InkSuggestionCard, InkTextStyleMenu, InkThreadPopover, InkToolButton, InkToolbar, InkToolbarSeparator, InkUndo, InkViewport, InkZoomIn, InkZoomOut, InkZoomSelect, useAnnotations, useDocument, useEmbedded, useHistory, useInkContext, usePage, useReadable, useSelection, useTool, useToolOptions, useToolStyle, useZoom };
2637
+ export { InkAnnotation, InkColorMenu, InkCommand, InkHighlightMenu, InkIcon, InkLineStyleMenu, InkNextPage, InkPage, InkPageCount, InkPageInput, InkPrevPage, InkRedo, InkRoot, InkShapeMenu, InkSignatureDialog, InkSignatureMenu, InkTextStyleMenu, InkThreadPopover, InkToolButton, InkToolbar, InkToolbarSeparator, InkUndo, InkViewport, InkZoomIn, InkZoomOut, InkZoomSelect, useAnnotations, useCommand, useDocument, useEmbedded, useHistory, useInkContext, usePage, useReadable, useSelection, useTool, useToolOptions, useToolStyle, useZoom };
2511
2638
 
2512
2639
  //# sourceMappingURL=index.mjs.map