@inkbar/svelte 0.4.0 → 0.5.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.
Files changed (34) hide show
  1. package/README.md +3 -5
  2. package/dist/components/InkAnnotation.svelte +0 -12
  3. package/dist/components/InkAnnotation.svelte.d.ts.map +1 -1
  4. package/dist/components/InkColorMenu.svelte +6 -1
  5. package/dist/components/InkColorMenu.svelte.d.ts +2 -0
  6. package/dist/components/InkColorMenu.svelte.d.ts.map +1 -1
  7. package/dist/components/InkHighlightMenu.svelte +9 -0
  8. package/dist/components/InkHighlightMenu.svelte.d.ts.map +1 -1
  9. package/dist/components/InkRoot.svelte +14 -1
  10. package/dist/components/InkRoot.svelte.d.ts +8 -1
  11. package/dist/components/InkRoot.svelte.d.ts.map +1 -1
  12. package/dist/components/InkShapeMenu.svelte +4 -0
  13. package/dist/components/InkShapeMenu.svelte.d.ts.map +1 -1
  14. package/dist/components/InkSignatureMenu.svelte +4 -0
  15. package/dist/components/InkSignatureMenu.svelte.d.ts.map +1 -1
  16. package/dist/components/InkThreadPopover.svelte +7 -0
  17. package/dist/components/InkThreadPopover.svelte.d.ts.map +1 -1
  18. package/dist/components/InkViewport.svelte +19 -2
  19. package/dist/components/InkViewport.svelte.d.ts.map +1 -1
  20. package/dist/components/popover-focus.d.ts +16 -2
  21. package/dist/components/popover-focus.d.ts.map +1 -1
  22. package/dist/components/popover-focus.js +24 -3
  23. package/dist/context.d.ts +8 -1
  24. package/dist/context.d.ts.map +1 -1
  25. package/dist/hooks/useAnnotations.svelte.d.ts +795 -1258
  26. package/dist/hooks/useAnnotations.svelte.d.ts.map +1 -1
  27. package/dist/hooks/useAnnotations.svelte.js +1 -7
  28. package/dist/index.d.ts +2 -4
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +0 -1
  31. package/package.json +5 -5
  32. package/dist/components/InkSuggestionCard.svelte +0 -121
  33. package/dist/components/InkSuggestionCard.svelte.d.ts +0 -9
  34. package/dist/components/InkSuggestionCard.svelte.d.ts.map +0 -1
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  ---
22
22
 
23
- Drop a PDF viewer into your Svelte app and let people highlight text, leave notes with replies and review redactions. `@inkbar/svelte` gives you components to build your own toolbars, popovers for comment threads and suggested redactions, and hooks for anything else. `@inkbar/theme` styles them all.
23
+ Drop a PDF viewer into your Svelte app and let people highlight text, leave notes with replies and redact. `@inkbar/svelte` gives you components to build your own toolbars, a popover for comment threads, and hooks for anything else. `@inkbar/theme` styles them all.
24
24
 
25
25
  Annotations are plain typed objects. Bind them with `bind:annotations` to keep them in your own state, or let Inkbar hold them with undo and redo.
26
26
 
@@ -52,7 +52,6 @@ Svelte 5.46.4 or later is a peer dependency. Add the Vite plugin from `@inkbar/c
52
52
  InkZoomSelect,
53
53
  InkZoomIn,
54
54
  InkThreadPopover,
55
- InkSuggestionCard,
56
55
  } from '@inkbar/svelte'
57
56
  import type { Annotation } from '@inkbar/core'
58
57
  import { assets } from 'virtual:inkbar-assets'
@@ -85,7 +84,6 @@ Svelte 5.46.4 or later is a peer dependency. Add the Vite plugin from `@inkbar/c
85
84
  <InkZoomIn />
86
85
  </InkToolbar>
87
86
  <InkThreadPopover />
88
- <InkSuggestionCard />
89
87
  </InkRoot>
90
88
  ```
91
89
 
@@ -161,7 +159,7 @@ The highlight tool leaves `contents` free for the comment. `doc.text(page)` retu
161
159
 
162
160
  ## Custom rendering
163
161
 
164
- Slots are snippets. `InkViewport` takes `loading`, `empty`, `error`, `annotation` and `pageError`. `InkThreadPopover` and `InkSuggestionCard` take `children` as a snippet that receives the popover's props. `InkToolButton` takes `children` as a snippet of `{ active, label }`. The props each receives are typed as `AnnotationSlotProps`, `ThreadPopoverSlotProps` and `SuggestionCardSlotProps`, the same types the Vue and React packages export.
162
+ Slots are snippets. `InkViewport` takes `loading`, `empty`, `error`, `annotation` and `pageError`. `InkThreadPopover` takes `children` as a snippet that receives the popover's props. `InkToolButton` takes `children` as a snippet of `{ active, label }`. The props each receives are typed as `AnnotationSlotProps` and `ThreadPopoverSlotProps`, the same types the Vue and React packages export.
165
163
 
166
164
  `InkRoot`, `InkToolbar`, `InkToolButton`, `InkUndo`, `InkRedo`, `InkPrevPage`, `InkNextPage`, `InkZoomIn` and `InkZoomOut` take a `child` snippet to render your own element. Spread the `props` it gives you on that element. The snippet type is exported as `ChildSnippet`.
167
165
 
@@ -183,7 +181,7 @@ The scroll area takes focus. From there:
183
181
 
184
182
  On a focused annotation:
185
183
 
186
- - `Enter` or `Space` selects and activates it. A note opens its thread and a suggested redaction its card; other types are only selected.
184
+ - `Enter` or `Space` selects and activates it. A note or text markup opens its thread; other types are only selected.
187
185
  - `ArrowUp` and `ArrowDown` move to the previous or next annotation on the page.
188
186
  - `Shift` + arrow moves it by 1 pt, or by 10 pt with `Alt` as well.
189
187
  - `Ctrl` or `Cmd` + `Shift` + arrow resizes an area redaction, text box or shape.
@@ -17,8 +17,6 @@
17
17
  * default visual for every annotation that is not custom, so this never draws a second one.
18
18
  * InkPage portals it into the annotation's overlay element.
19
19
  */
20
- import { acceptSuggestion } from '@inkbar/core'
21
-
22
20
  import { useInkContext } from '../context.js'
23
21
 
24
22
  import type { AnnotationState } from '@inkbar/core/dom'
@@ -48,9 +46,6 @@
48
46
  return () => observer.disconnect()
49
47
  })
50
48
 
51
- const isSuggestion = $derived(
52
- annotation.type === 'redact' && annotation.origin === 'suggested'
53
- )
54
49
  const locked = $derived(annotation.locked === true)
55
50
  const selected = $derived(ctx.selection.includes(annotation.id))
56
51
  const store = () => ctx.document?.annotations
@@ -60,13 +55,6 @@
60
55
  locked,
61
56
  select: () => store()?.select([annotation.id]),
62
57
  remove: () => void store()?.remove(annotation.id),
63
- accept: () => {
64
- const s = store()
65
- if (s && isSuggestion) acceptSuggestion(s, annotation.id)
66
- },
67
- dismiss: () => {
68
- if (isSuggestion) store()?.remove(annotation.id)
69
- },
70
58
  })
71
59
  </script>
72
60
 
@@ -1 +1 @@
1
- {"version":3,"file":"InkAnnotation.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkAnnotation.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,UAAU,CAAA;IACtB,mFAAmF;IACnF,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IACjC,QAAQ,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAA;CACzC;AA2EH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"InkAnnotation.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkAnnotation.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,UAAU,CAAA;IACtB,mFAAmF;IACnF,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IACjC,QAAQ,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAA;CACzC;AA6DH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -28,6 +28,8 @@
28
28
  * tool is active, and recolours the selection only when it is all in the group.
29
29
  */
30
30
  group?: StyleGroup | undefined
31
+ /** The colour new annotations get while the style leaves it unset; the swatch shows it. */
32
+ fallback?: string | undefined
31
33
  }
32
34
  </script>
33
35
 
@@ -54,6 +56,7 @@
54
56
  children,
55
57
  standalone = false,
56
58
  group,
59
+ fallback,
57
60
  disabled,
58
61
  onclick,
59
62
  onkeydown,
@@ -63,7 +66,9 @@
63
66
  const style = useToolStyle(() => group)
64
67
  let open = $state(false)
65
68
 
66
- const model = $derived(colorMenuModel(property, style.current, style.supported))
69
+ const model = $derived(
70
+ colorMenuModel(property, style.current, style.supported, fallback)
71
+ )
67
72
  const name = $derived(label ?? model.label)
68
73
  const trigger = $derived(model.trigger)
69
74
 
@@ -26,6 +26,8 @@ export interface InkColorMenuProps extends Omit<HTMLButtonAttributes, 'children'
26
26
  * tool is active, and recolours the selection only when it is all in the group.
27
27
  */
28
28
  group?: StyleGroup | undefined;
29
+ /** The colour new annotations get while the style leaves it unset; the swatch shows it. */
30
+ fallback?: string | undefined;
29
31
  }
30
32
  declare const InkColorMenu: import("svelte").Component<InkColorMenuProps, {}, "">;
31
33
  type InkColorMenu = ReturnType<typeof InkColorMenu>;
@@ -1 +1 @@
1
- {"version":3,"file":"InkColorMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkColorMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D;;;;;;GAMG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAC/E,iCAAiC;IACjC,QAAQ,EAAE,aAAa,CAAA;IACvB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAChC;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;CAC/B;AA0JH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"InkColorMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkColorMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D;;;;;;GAMG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAC/E,iCAAiC;IACjC,QAAQ,EAAE,aAAa,CAAA;IACvB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAChC;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;IAC9B,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC9B;AA6JH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -31,6 +31,7 @@
31
31
  import { ChevronDown } from '@inkbar/icons'
32
32
  import { Popover, Toolbar } from 'bits-ui'
33
33
 
34
+ import { useDocument } from '../hooks/useDocument.svelte.js'
34
35
  import { useTool } from '../hooks/useTool.svelte.js'
35
36
  import { useToolOptions } from '../hooks/useToolOptions.svelte.js'
36
37
 
@@ -40,10 +41,15 @@
40
41
  let { class: className, children, ...rest }: InkHighlightMenuProps = $props()
41
42
 
42
43
  const current = useTool()
44
+ const inkDocument = useDocument()
43
45
  const highlight = useToolOptions<HighlightOptions>('highlight')
44
46
  let open = $state(false)
47
+ /** The split button: the panel lines up under it rather than the chevron. */
48
+ let group = $state<HTMLElement | null>(null)
45
49
 
46
50
  const model = $derived(highlightMenuModel(highlight.options, MARKUP_KINDS))
51
+ /** The colour the chosen kind gets while the markup style leaves it unset. */
52
+ const fallback = $derived(inkDocument.document?.annotations.defaults.color(model.kind))
47
53
  const active = $derived(current.tool === 'highlight')
48
54
 
49
55
  let menu = $state<HTMLElement | null>(null)
@@ -69,6 +75,7 @@
69
75
  </script>
70
76
 
71
77
  <div
78
+ bind:this={group}
72
79
  role="group"
73
80
  aria-label="Highlight"
74
81
  class={['ink-split-button ink-highlight-menu', className]}
@@ -116,6 +123,7 @@
116
123
  class="ink-select-content ink-menu-content"
117
124
  data-ink-select-content=""
118
125
  data-ink-menu="highlight"
126
+ customAnchor={group}
119
127
  side="bottom"
120
128
  align="start"
121
129
  sideOffset={4}
@@ -156,6 +164,7 @@
156
164
  group="markup"
157
165
  label="Highlight colour"
158
166
  standalone
167
+ {fallback}
159
168
  role="menuitem"
160
169
  tabindex={-1}
161
170
  />
@@ -1 +1 @@
1
- {"version":3,"file":"InkHighlightMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkHighlightMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AA2GH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"InkHighlightMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkHighlightMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAkHH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -11,7 +11,7 @@
11
11
  PdfAssets,
12
12
  SignatureStore,
13
13
  } from '@inkbar/core'
14
- import type { Tool, ToolId, ZoomValue } from '@inkbar/core/dom'
14
+ import type { DecorateAnnotation, Tool, ToolId, ZoomValue } from '@inkbar/core/dom'
15
15
  import type { Snippet } from 'svelte'
16
16
  import type { HTMLAttributes } from 'svelte/elements'
17
17
 
@@ -39,6 +39,13 @@
39
39
  * document opens.
40
40
  */
41
41
  tools?: Tool[] | undefined
42
+ /**
43
+ * Adds `data-*` attributes, classes and a screen-reader description to each annotation's
44
+ * element (see `DecorateAnnotation`). Decorations re-apply whenever the annotations change
45
+ * and when this prop changes, without rebuilding the viewer. Pass a new function when its
46
+ * output would change for any other reason.
47
+ */
48
+ decorateAnnotation?: DecorateAnnotation | undefined
42
49
  /**
43
50
  * The font families text boxes offer, replacing the PDF standard families
44
51
  * (`FONT_FAMILIES`). Fonts beyond those are the PDF writer's to embed.
@@ -107,6 +114,7 @@
107
114
  tool = $bindable(),
108
115
  onToolChange,
109
116
  tools: hostTools,
117
+ decorateAnnotation,
110
118
  fonts,
111
119
  signatures: signatureStore,
112
120
  signatureFonts,
@@ -279,6 +287,8 @@
279
287
  })
280
288
 
281
289
  function setTool(id: ToolId): void {
290
+ // Picking the active tool again starts it afresh, as after a one-shot tool's use.
291
+ if (currentTool === id && tools?.active.get() === id) tools.set(id)
282
292
  tool = id
283
293
  onToolChange?.(id)
284
294
  }
@@ -364,6 +374,9 @@
364
374
  get hostTools() {
365
375
  return hostTools
366
376
  },
377
+ get decorateAnnotation() {
378
+ return decorateAnnotation
379
+ },
367
380
  get fonts() {
368
381
  return fonts ?? FONT_FAMILIES
369
382
  },
@@ -1,6 +1,6 @@
1
1
  import type { ChildSnippet } from './child-snippet.js';
2
2
  import type { Annotation, AnnotationChange, AnnotationDefaults, DocumentSource, InkDocument, InkError, InkStatus, PdfAssets, SignatureStore } from '@inkbar/core';
3
- import type { Tool, ToolId, ZoomValue } from '@inkbar/core/dom';
3
+ import type { DecorateAnnotation, Tool, ToolId, ZoomValue } from '@inkbar/core/dom';
4
4
  import type { Snippet } from 'svelte';
5
5
  import type { HTMLAttributes } from 'svelte/elements';
6
6
  export interface InkRootProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onchange' | 'onerror'> {
@@ -24,6 +24,13 @@ export interface InkRootProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chil
24
24
  * document opens.
25
25
  */
26
26
  tools?: Tool[] | undefined;
27
+ /**
28
+ * Adds `data-*` attributes, classes and a screen-reader description to each annotation's
29
+ * element (see `DecorateAnnotation`). Decorations re-apply whenever the annotations change
30
+ * and when this prop changes, without rebuilding the viewer. Pass a new function when its
31
+ * output would change for any other reason.
32
+ */
33
+ decorateAnnotation?: DecorateAnnotation | undefined;
27
34
  /**
28
35
  * The font families text boxes offer, replacing the PDF standard families
29
36
  * (`FONT_FAMILIES`). Fonts beyond those are the PDF writer's to embed.
@@ -1 +1 @@
1
- {"version":3,"file":"InkRoot.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkRoot.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,QAAQ,EACR,SAAS,EACT,SAAS,EACT,cAAc,EACf,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,YAAa,SAAQ,IAAI,CACxC,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,GAAG,UAAU,GAAG,SAAS,CACpC;IACC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;IACzC;;;;OAIG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,GAAG,SAAS,CAAA;IACtC,mBAAmB,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAChE,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC3D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACnD;;;OAGG;IACH,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,SAAS,CAAA;IAC1B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,cAAc,GAAG,SAAS,CAAA;IACvC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACnD,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACtD,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAClD,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACjD,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC/C,gDAAgD;IAChD,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC,CAAA;IACzE;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;CACrB;AAqBH,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAoW/E,QAAA,MAAM,OAAO;;uBAhCc,WAAW,GAAG,IAAI;qBAGpB,SAAS;oDA6BmB,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"InkRoot.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkRoot.svelte.ts"],"names":[],"mappings":"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"}
@@ -41,6 +41,8 @@
41
41
  const current = useTool()
42
42
  const shape = useToolOptions<ShapeOptions>('shape')
43
43
  let open = $state(false)
44
+ /** The split button: the panel lines up under it rather than the chevron. */
45
+ let group = $state<HTMLElement | null>(null)
44
46
 
45
47
  const model = $derived(shapeMenuModel(shape.options, SHAPE_KINDS))
46
48
  const active = $derived(current.tool === 'shape')
@@ -68,6 +70,7 @@
68
70
  </script>
69
71
 
70
72
  <div
73
+ bind:this={group}
71
74
  role="group"
72
75
  aria-label="Shape"
73
76
  class={['ink-split-button ink-shape-menu', className]}
@@ -115,6 +118,7 @@
115
118
  class="ink-select-content ink-menu-content"
116
119
  data-ink-select-content=""
117
120
  data-ink-menu="shape"
121
+ customAnchor={group}
118
122
  side="bottom"
119
123
  align="start"
120
124
  sideOffset={4}
@@ -1 +1 @@
1
- {"version":3,"file":"InkShapeMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkShapeMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAoGH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"InkShapeMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkShapeMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAC7C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAsGH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -58,6 +58,8 @@
58
58
  const model = $derived(signatureMenuModel(templates.current, signature.options))
59
59
  const active = $derived(current.tool === 'signature')
60
60
  let open = $state(false)
61
+ /** The split button: the panel lines up under it rather than the chevron. */
62
+ let group = $state<HTMLElement | null>(null)
61
63
  let creating = $state(false)
62
64
  let menu = $state<HTMLElement | null>(null)
63
65
  let main = $state<HTMLElement | null>(null)
@@ -115,6 +117,7 @@
115
117
  </script>
116
118
 
117
119
  <div
120
+ bind:this={group}
118
121
  role="group"
119
122
  aria-label={SIGNATURE_LABELS.sign}
120
123
  class={['ink-split-button ink-signature-menu', className]}
@@ -160,6 +163,7 @@
160
163
  class="ink-select-content ink-menu-content"
161
164
  data-ink-select-content=""
162
165
  data-ink-menu="signature"
166
+ customAnchor={group}
163
167
  side="bottom"
164
168
  align="start"
165
169
  sideOffset={4}
@@ -1 +1 @@
1
- {"version":3,"file":"InkSignatureMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSignatureMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,mFAAmF;IACnF,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CACtC;AAuLH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"InkSignatureMenu.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSignatureMenu.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,mFAAmF;IACnF,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CACtC;AAyLH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -137,6 +137,13 @@
137
137
  }
138
138
  </script>
139
139
 
140
+ <svelte:document
141
+ onpointerdowncapture={(e) =>
142
+ open && focusReturn.onPressOutside(e, '[data-ink-thread-popover]', close)}
143
+ onpointerup={focusReturn.onPressEnd}
144
+ onpointercancel={focusReturn.onPressEnd}
145
+ />
146
+
140
147
  <Popover.Root bind:open={() => open, onOpenChange}>
141
148
  <Popover.Portal>
142
149
  {#if note && anchor}
@@ -1 +1 @@
1
- {"version":3,"file":"InkThreadPopover.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkThreadPopover.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,sBAAsB,CAAC,CAAC,GAAG,SAAS,CAAA;CACzD;AA6LH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"InkThreadPopover.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkThreadPopover.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,sBAAsB,CAAC,CAAC,GAAG,SAAS,CAAA;CACzD;AAgMH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -43,7 +43,7 @@
43
43
  import InkPage from './InkPage.svelte'
44
44
 
45
45
  import type { InkDocument, InkStatus } from '@inkbar/core'
46
- import type { PageMount, ViewerEngine, ZoomValue } from '@inkbar/core/dom'
46
+ import type { PageMount, ToolId, ViewerEngine, ZoomValue } from '@inkbar/core/dom'
47
47
 
48
48
  let {
49
49
  mode = 'continuous',
@@ -126,6 +126,8 @@
126
126
  buffer,
127
127
  tool: ctx.tool,
128
128
  ...(ctx.hostTools ? { tools: ctx.hostTools } : {}),
129
+ // Read through, so the effect below can re-apply a new function without a rebuild.
130
+ decorateAnnotation: (a) => ctx.decorateAnnotation?.(a),
129
131
  ...(ctx.toolStyle ? { toolStyle: ctx.toolStyle } : {}),
130
132
  toolHistory: { batch: (fn) => ctx.history.batch(fn) },
131
133
  toolOptions: ctx.toolOptions,
@@ -155,7 +157,15 @@
155
157
  lastReportedPage = p
156
158
  ctx.reportPage(p)
157
159
  }
158
- stops = [vp.zoom.subscribe(reportZoom), vp.currentPage.subscribe(reportPage)]
160
+ // A tool that switches itself (a one-shot tool once used) is reported as a pick.
161
+ const followTool = (id: ToolId): void => {
162
+ if (id !== ctx.tool) ctx.setTool(id)
163
+ }
164
+ stops = [
165
+ vp.zoom.subscribe(reportZoom),
166
+ vp.currentPage.subscribe(reportPage),
167
+ tc.active.subscribe(followTool),
168
+ ]
159
169
  // Whatever the build restored is reported once; nothing is when it matched.
160
170
  reportZoom(vp.zoom.get())
161
171
  reportPage(vp.currentPage.get())
@@ -229,6 +239,13 @@
229
239
  })
230
240
  })
231
241
 
242
+ // A new decorateAnnotation re-applies to the mounted annotation elements at once.
243
+ const decorate = $derived(ctx.decorateAnnotation)
244
+ $effect(() => {
245
+ void decorate
246
+ untrack(() => engine?.redecorate())
247
+ })
248
+
232
249
  // The setters below run untracked: core relays out inside them and the engine's page
233
250
  // callbacks read and write `pages`. Tracked, each effect would depend on the list it changes
234
251
  // and run again, without end when the host holds a zoom the viewport clamps.
@@ -1 +1 @@
1
- {"version":3,"file":"InkViewport.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkViewport.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,SAAS,CAAA;IACvD,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;CACrE;AA8RH,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"InkViewport.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkViewport.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,cAAc,CAAC,cAAc,CAAC,EAC9B,UAAU,CACX;IACC,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,SAAS,CAAA;IACvD,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;CACrE;AA+SH,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -5,6 +5,18 @@ export interface PopoverFocusReturn {
5
5
  beforeRemove: FocusReturn['beforeRemove'];
6
6
  /** Bits UI's `onOpenAutoFocus` handler. Leaves the default alone: focus moves into the popover. */
7
7
  onOpenAutoFocus: (event: Event) => void;
8
+ /**
9
+ * A document-level pointer press, captured. `inside` selects the popover's content. A press on
10
+ * the viewer's own scroll element, outside the popover, that will not activate an annotation
11
+ * closes it now, through `close`, before the press moves focus. Bits UI closes it a beat
12
+ * later, and its focus trap pulls focus back into the popover in between, which ends a text
13
+ * selection the press has just begun. Nothing is focused when it closes this way: the press
14
+ * places focus. Every other press (a toolbar, a host's menu portaled out of the popover) is
15
+ * left to Bits UI, whose layer stack knows what belongs to the popover.
16
+ */
17
+ onPressOutside: (event: PointerEvent, inside: string, close: () => void) => void;
18
+ /** A pointer release or cancel, captured. */
19
+ onPressEnd: () => void;
8
20
  /** Bits UI's `onCloseAutoFocus` handler. Prevents the default; the planner places focus. */
9
21
  onCloseAutoFocus: (event: Event, anchor: HTMLElement | null) => void;
10
22
  }
@@ -21,8 +33,10 @@ export declare function keepOpenOnAnnotation(event: PointerEvent): void;
21
33
  * close reaches the planner one microtask late, and only when no open was reported after it.
22
34
  *
23
35
  * This relies on Bits UI reporting a remount as a close and then an open within one synchronous
24
- * call (observed in bits-ui 2.19.5, `FocusScope.mount`). If that changes, the browser case 'a
25
- * card opened by selection alone takes focus without passing it through the annotation' fails.
36
+ * call (observed in bits-ui 2.19.5, `FocusScope.mount`). The unit case 'a close followed by an
37
+ * open in the same tick moves no focus and uses up the removal plan' pins the planner's side, and
38
+ * the browser case 'a popover opened without passing focus through the annotation takes focus'
39
+ * checks that an open moves focus straight into the popover.
26
40
  *
27
41
  * A close that is superseded this way moves no focus, but it still uses up whatever
28
42
  * `beforeRemove` planned for it, so the plan cannot leak into a later close. A close that is
@@ -1 +1 @@
1
- {"version":3,"file":"popover-focus.d.ts","sourceRoot":"","sources":["../../src/lib/components/popover-focus.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,WAAW,CAAC,cAAc,CAAC,CAAA;IACzC,mGAAmG;IACnG,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACvC,4FAA4F;IAC5F,gBAAgB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;CACrE;AAED,8GAA8G;AAC9G,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAE9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,kBAAkB,CAkClE"}
1
+ {"version":3,"file":"popover-focus.d.ts","sourceRoot":"","sources":["../../src/lib/components/popover-focus.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;AAKzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,WAAW,CAAC,cAAc,CAAC,CAAA;IACzC,mGAAmG;IACnG,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACvC;;;;;;;;OAQG;IACH,cAAc,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,KAAK,IAAI,CAAA;IAChF,6CAA6C;IAC7C,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,4FAA4F;IAC5F,gBAAgB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;CACrE;AAED,8GAA8G;AAC9G,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAE9D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,kBAAkB,CAiDlE"}
@@ -1,4 +1,5 @@
1
1
  import { createFocusReturn, shouldKeepOpenOnAnnotation, } from '@inkbar/core/dom';
2
+ import { flushSync } from 'svelte';
2
3
  import { peekActivated } from '../context.js';
3
4
  /** Bits UI's `onInteractOutside` handler: a press that will activate an annotation keeps the popover open. */
4
5
  export function keepOpenOnAnnotation(event) {
@@ -16,8 +17,10 @@ export function keepOpenOnAnnotation(event) {
16
17
  * close reaches the planner one microtask late, and only when no open was reported after it.
17
18
  *
18
19
  * This relies on Bits UI reporting a remount as a close and then an open within one synchronous
19
- * call (observed in bits-ui 2.19.5, `FocusScope.mount`). If that changes, the browser case 'a
20
- * card opened by selection alone takes focus without passing it through the annotation' fails.
20
+ * call (observed in bits-ui 2.19.5, `FocusScope.mount`). The unit case 'a close followed by an
21
+ * open in the same tick moves no focus and uses up the removal plan' pins the planner's side, and
22
+ * the browser case 'a popover opened without passing focus through the annotation takes focus'
23
+ * checks that an open moves focus straight into the popover.
21
24
  *
22
25
  * A close that is superseded this way moves no focus, but it still uses up whatever
23
26
  * `beforeRemove` planned for it, so the plan cannot leak into a later close. A close that is
@@ -35,7 +38,25 @@ export function useFocusReturn(ctx) {
35
38
  /** Open and close reports so far, so a deferred close can tell what followed it. */
36
39
  let opens = 0;
37
40
  let closes = 0;
41
+ /** A press closed the popover, and the planner answers that close without moving focus. */
42
+ let pressed = false;
38
43
  return {
44
+ onPressOutside(event, inside, close) {
45
+ const target = event.target;
46
+ if (event.button !== 0 || !(target instanceof Element))
47
+ return;
48
+ if (target.closest(inside) || shouldKeepOpenOnAnnotation(target))
49
+ return;
50
+ const scroll = target.closest('[data-ink-scroll]');
51
+ if (!scroll || !ctx.rootEl?.contains(scroll))
52
+ return;
53
+ pressed = true;
54
+ close();
55
+ flushSync();
56
+ },
57
+ onPressEnd() {
58
+ pressed = false;
59
+ },
39
60
  beforeRemove: (anchor, prefer) => planner.beforeRemove(anchor, prefer),
40
61
  onOpenAutoFocus() {
41
62
  opens += 1;
@@ -50,7 +71,7 @@ export function useFocusReturn(ctx) {
50
71
  if (close !== closes)
51
72
  return;
52
73
  // An open followed: the popover is open, so only the plan is cleared.
53
- discarding = opens !== opensBefore;
74
+ discarding = opens !== opensBefore || pressed;
54
75
  try {
55
76
  planner.onCloseAutoFocus(event, anchor);
56
77
  }
package/dist/context.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Annotation, InkDocument, InkStatus, PdfAssets, SignatureLibrary } from '@inkbar/core';
2
- import type { Tool, ToolController, ToolId, ToolOptions, ToolStyle, Viewport, ZoomValue } from '@inkbar/core/dom';
2
+ import type { DecorateAnnotation, Tool, ToolController, ToolId, ToolOptions, ToolStyle, Viewport, ZoomValue } from '@inkbar/core/dom';
3
3
  export type { InkStatus };
4
4
  /** Undo and redo for the current document, whichever mode its annotations are in. */
5
5
  export interface InkHistory {
@@ -35,6 +35,8 @@ export interface InkContext {
35
35
  setTool(id: ToolId): void;
36
36
  /** Host tools from InkRoot's `tools` prop; InkViewport registers them. */
37
37
  readonly hostTools: Tool[] | undefined;
38
+ /** InkRoot's `decorateAnnotation` prop. InkViewport re-applies decorations when it changes. */
39
+ readonly decorateAnnotation: DecorateAnnotation | undefined;
38
40
  /** The font families text boxes offer: InkRoot's `fonts`, else `FONT_FAMILIES`. */
39
41
  readonly fonts: readonly string[];
40
42
  /** The tools' options for InkRoot's life, so they persist across documents. */
@@ -52,6 +54,11 @@ export interface InkContext {
52
54
  reportPage(p: number): void;
53
55
  /** Last activated annotation (Enter, click with the note tool, etc.). Popovers key off this. */
54
56
  readonly activated: string | null;
57
+ /**
58
+ * Activates annotation `id`, or clears the activation with `null`. This is the context's
59
+ * own: unlike core's `Viewer.setActivated`, it calls InkRoot's `onActivate` for an id, and
60
+ * it does not check that the id is in the document.
61
+ */
55
62
  setActivated(id: string | null): void;
56
63
  readonly history: InkHistory;
57
64
  readonly rootEl: HTMLElement | null;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/lib/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/lib/context.ts"],"names":[],"mappings":"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"}