@inkbar/svelte 0.5.0 → 0.7.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 (45) hide show
  1. package/README.md +27 -3
  2. package/dist/components/InkColorMenu.svelte +7 -18
  3. package/dist/components/InkColorMenu.svelte.d.ts.map +1 -1
  4. package/dist/components/InkCommand.svelte +63 -0
  5. package/dist/components/InkCommand.svelte.d.ts +17 -0
  6. package/dist/components/InkCommand.svelte.d.ts.map +1 -0
  7. package/dist/components/InkFindBar.svelte +213 -0
  8. package/dist/components/InkFindBar.svelte.d.ts +6 -0
  9. package/dist/components/InkFindBar.svelte.d.ts.map +1 -0
  10. package/dist/components/InkHighlightMenu.svelte +6 -13
  11. package/dist/components/InkHighlightMenu.svelte.d.ts.map +1 -1
  12. package/dist/components/InkLineStyleMenu.svelte +7 -17
  13. package/dist/components/InkLineStyleMenu.svelte.d.ts.map +1 -1
  14. package/dist/components/InkRoot.svelte +138 -3
  15. package/dist/components/InkRoot.svelte.d.ts +7 -1
  16. package/dist/components/InkRoot.svelte.d.ts.map +1 -1
  17. package/dist/components/InkSearchButton.svelte +56 -0
  18. package/dist/components/InkSearchButton.svelte.d.ts +5 -0
  19. package/dist/components/InkSearchButton.svelte.d.ts.map +1 -0
  20. package/dist/components/InkShapeMenu.svelte +6 -13
  21. package/dist/components/InkShapeMenu.svelte.d.ts.map +1 -1
  22. package/dist/components/InkSignatureMenu.svelte +6 -13
  23. package/dist/components/InkSignatureMenu.svelte.d.ts.map +1 -1
  24. package/dist/components/InkToolButton.svelte +11 -3
  25. package/dist/components/InkToolButton.svelte.d.ts.map +1 -1
  26. package/dist/components/InkViewport.svelte +15 -0
  27. package/dist/components/InkViewport.svelte.d.ts.map +1 -1
  28. package/dist/components/menu-open-focus.d.ts +12 -0
  29. package/dist/components/menu-open-focus.d.ts.map +1 -0
  30. package/dist/components/menu-open-focus.js +35 -0
  31. package/dist/components/search-button.d.ts +10 -0
  32. package/dist/components/search-button.d.ts.map +1 -0
  33. package/dist/components/search-button.js +1 -0
  34. package/dist/context.d.ts +21 -4
  35. package/dist/context.d.ts.map +1 -1
  36. package/dist/hooks/useCommand.svelte.d.ts +23 -0
  37. package/dist/hooks/useCommand.svelte.d.ts.map +1 -0
  38. package/dist/hooks/useCommand.svelte.js +44 -0
  39. package/dist/hooks/useSearch.svelte.d.ts +11 -0
  40. package/dist/hooks/useSearch.svelte.d.ts.map +1 -0
  41. package/dist/hooks/useSearch.svelte.js +31 -0
  42. package/dist/index.d.ts +10 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +5 -0
  45. package/package.json +6 -5
@@ -11,7 +11,13 @@
11
11
  PdfAssets,
12
12
  SignatureStore,
13
13
  } from '@inkbar/core'
14
- import type { DecorateAnnotation, Tool, ToolId, ZoomValue } from '@inkbar/core/dom'
14
+ import type {
15
+ DecorateAnnotation,
16
+ InkPlugin,
17
+ Tool,
18
+ ToolId,
19
+ ZoomValue,
20
+ } from '@inkbar/core/dom'
15
21
  import type { Snippet } from 'svelte'
16
22
  import type { HTMLAttributes } from 'svelte/elements'
17
23
 
@@ -39,6 +45,12 @@
39
45
  * document opens.
40
46
  */
41
47
  tools?: Tool[] | undefined
48
+ /**
49
+ * Plugins, set up once when the root is created; a later change to this prop is not
50
+ * applied. Their tools join `tools`, their decoration and page layers apply to every
51
+ * document, and their commands are placed with `InkCommand` or `useCommand`.
52
+ */
53
+ plugins?: InkPlugin[] | undefined
42
54
  /**
43
55
  * Adds `data-*` attributes, classes and a screen-reader description to each annotation's
44
56
  * element (see `DecorateAnnotation`). Decorations re-apply whenever the annotations change
@@ -86,13 +98,20 @@
86
98
  createSnapshotHistory,
87
99
  InkError as InkErrorClass,
88
100
  openDocument,
101
+ readonly,
102
+ writable,
89
103
  } from '@inkbar/core'
90
104
  import {
105
+ createPluginHost,
106
+ createSearchController,
91
107
  createToolOptions,
92
108
  cssVariableDefaults,
93
109
  FONT_FAMILIES,
110
+ handleFindKey,
111
+ pageSelectionText,
94
112
  SIGNATURE_FONTS,
95
113
  trackToolStyle,
114
+ viewerSearchOf,
96
115
  } from '@inkbar/core/dom'
97
116
  import { DEV } from 'esm-env'
98
117
  import { untrack } from 'svelte'
@@ -101,7 +120,13 @@
101
120
  import { setInkContext, type InkContext, type InkHistory } from '../context.js'
102
121
  import { useReadable } from '../use-readable.svelte.js'
103
122
 
104
- import type { ToolController, Viewport } from '@inkbar/core/dom'
123
+ import type { PluginHistory } from '@inkbar/core'
124
+ import type {
125
+ PluginHost,
126
+ SearchController,
127
+ ToolController,
128
+ Viewport,
129
+ } from '@inkbar/core/dom'
105
130
 
106
131
  let {
107
132
  src,
@@ -114,6 +139,7 @@
114
139
  tool = $bindable(),
115
140
  onToolChange,
116
141
  tools: hostTools,
142
+ plugins,
117
143
  decorateAnnotation,
118
144
  fonts,
119
145
  signatures: signatureStore,
@@ -146,6 +172,7 @@
146
172
  const IDLE: InkStatus = { state: 'idle' }
147
173
  const NO_ANNOTATIONS: readonly Annotation[] = []
148
174
  const NO_IDS: readonly string[] = []
175
+ const noop = (): void => undefined
149
176
 
150
177
  /** Signed URLs change; the key does not. Bytes and Blobs are their own identity. */
151
178
  function identity(source: DocumentSource | null): unknown {
@@ -160,6 +187,8 @@
160
187
  }
161
188
 
162
189
  let document = $state.raw<InkDocument | null>(null)
190
+ /** The same document outside the reactive graph, for the plugins' history. */
191
+ let documentNow: InkDocument | null = null
163
192
  /** Overrides the document's status while opening, and when opening itself failed. */
164
193
  let override = $state.raw<InkStatus | null>(null)
165
194
  let activated = $state<string | null>(null)
@@ -206,6 +235,8 @@
206
235
  const pw = sourcePassword
207
236
  let cancelled = false
208
237
  let opened: InkDocument | null = null
238
+ /** The snapshot history's hold on a controlled document's store. */
239
+ let detach: () => void = noop
209
240
  untrack(() => snapshotHistory.clear())
210
241
  const source = untrack(() => src)
211
242
  if (source === null) {
@@ -249,7 +280,13 @@
249
280
  const current = untrack(() => annotations)
250
281
  if (doc.annotations.mode === 'controlled' && current !== undefined)
251
282
  doc.annotations.setControlledValue(current)
283
+ documentNow = doc
252
284
  document = doc
285
+ // Controlled edits run the plugins' reactions inside their own snapshot step.
286
+ if (doc.annotations.mode === 'controlled')
287
+ detach = snapshotHistory.attach(doc.annotations)
288
+ // The document is current, so the host reads its history when it hears of it.
289
+ hostDocument.set(doc)
253
290
  override = null
254
291
  setTestHook(ref, doc)
255
292
  const s = doc.status.get()
@@ -272,7 +309,11 @@
272
309
  })()
273
310
  return () => {
274
311
  cancelled = true
312
+ // Cleared first, so the plugins close their stores while the document is still alive.
313
+ hostDocument.set(null)
314
+ detach()
275
315
  opened?.destroy()
316
+ documentNow = null
276
317
  document = null
277
318
  setTestHook(ref, undefined)
278
319
  }
@@ -295,6 +336,7 @@
295
336
  function setActivated(id: string | null): void {
296
337
  activated = id
297
338
  activatedNow = id
339
+ hostActivated.set(id)
298
340
  if (id) onActivate?.(id)
299
341
  }
300
342
  function reportZoom(z: ZoomValue): void {
@@ -325,8 +367,93 @@
325
367
  else if (document) document.history.batch(fn)
326
368
  else fn()
327
369
  },
370
+ track: (store) => pluginHistory.track(store),
371
+ react: (fn) => pluginHistory.react(fn),
372
+ }
373
+
374
+ /**
375
+ * The history the plugins reach, picked by the open document's mode when they call. The host
376
+ * reads it as soon as a document opens, so it goes by `documentNow`, which is set by then.
377
+ */
378
+ const activeHistory = (): PluginHistory | undefined => {
379
+ const doc = documentNow
380
+ if (!doc) return undefined
381
+ return doc.annotations.mode === 'controlled' ? snapshotHistory : doc.history
382
+ }
383
+ const pluginHistory: PluginHistory = {
384
+ batch: (fn) => {
385
+ const h = activeHistory()
386
+ if (h) h.batch(fn)
387
+ else fn()
388
+ },
389
+ track: (store) => activeHistory()?.track(store) ?? noop,
390
+ react: (fn) => activeHistory()?.react(fn) ?? noop,
328
391
  }
329
392
 
393
+ /*
394
+ * One plugin host for the root's life, built in an effect so that no `setup` runs on the
395
+ * server, where effects never run and nothing would clean it up. Until then `pluginHost` is
396
+ * null and InkCommand renders its command disabled. The open effect above runs first, but
397
+ * the document it opens arrives after an await, so the host is in place by then. It follows
398
+ * these writables rather than the root's state: the open effect sets `hostDocument` once
399
+ * `documentNow` is current.
400
+ */
401
+ const hostDocument = writable<InkDocument | null>(null)
402
+ const hostActivated = writable<string | null>(null)
403
+ const viewportEl = writable<HTMLElement | null>(null)
404
+ let pluginHost = $state.raw<PluginHost | null>(null)
405
+ /** One search controller for the root's life, built with the plugin host that shares it. */
406
+ let search = $state.raw<SearchController | null>(null)
407
+ $effect(() => {
408
+ const controller = untrack(() =>
409
+ createSearchController({
410
+ document: readonly(hostDocument),
411
+ viewport: () => viewport,
412
+ })
413
+ )
414
+ const host = untrack(() =>
415
+ createPluginHost({
416
+ // Read once, as documented on the prop.
417
+ plugins: plugins ?? [],
418
+ document: readonly(hostDocument),
419
+ history: () => pluginHistory,
420
+ activated: readonly(hostActivated),
421
+ // As in `createViewer`: an id not in the open document is ignored.
422
+ setActivated: (id) => {
423
+ if (id === null || documentNow?.annotations.get(id)) setActivated(id)
424
+ },
425
+ root: readonly(viewportEl),
426
+ decorate: () => decorateAnnotation,
427
+ viewport: () => viewport,
428
+ search: viewerSearchOf(controller),
429
+ })
430
+ )
431
+ search = controller
432
+ pluginHost = host
433
+ // Destroyed after InkViewport's engine and its plugin layers, as in `createViewer`: Svelte
434
+ // creates a component's effects after its template and tears them down in creation order,
435
+ // so the children's teardowns run first.
436
+ return () => {
437
+ host.destroy()
438
+ controller.destroy()
439
+ }
440
+ })
441
+
442
+ /*
443
+ * Ctrl/Cmd+F while focus is in the root and a find bar is mounted; else the browser's. A
444
+ * native listener: the handler reads the event's `currentTarget`, which is the root here.
445
+ */
446
+ $effect(() => {
447
+ const el = ref
448
+ const controller = search
449
+ if (!el || !controller) return
450
+ const onKeydown = (e: KeyboardEvent): void => {
451
+ handleFindKey(e, controller, () => pageSelectionText(el))
452
+ }
453
+ el.addEventListener('keydown', onKeydown)
454
+ return () => el.removeEventListener('keydown', onKeydown)
455
+ })
456
+
330
457
  /** The document's tool style; it batches through `inkHistory`, whichever mode is on. */
331
458
  const styleTracker = $derived.by(() => {
332
459
  const doc = document
@@ -335,7 +462,8 @@
335
462
  trackToolStyle({
336
463
  store: doc.annotations,
337
464
  history: { batch: (fn) => inkHistory.batch(fn) },
338
- tools: hostTools,
465
+ // The plugins' tools too, so a plugin tool's style group applies.
466
+ tools: [...(hostTools ?? []), ...(pluginHost?.tools ?? [])],
339
467
  tool: currentTool,
340
468
  })
341
469
  )
@@ -361,6 +489,13 @@
361
489
  get selection() {
362
490
  return selection.current
363
491
  },
492
+ get pluginHost() {
493
+ return pluginHost
494
+ },
495
+ get search() {
496
+ return search
497
+ },
498
+ viewportEl,
364
499
  get viewport() {
365
500
  return viewport
366
501
  },
@@ -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 { DecorateAnnotation, Tool, ToolId, ZoomValue } from '@inkbar/core/dom';
3
+ import type { DecorateAnnotation, InkPlugin, 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,12 @@ export interface InkRootProps extends Omit<HTMLAttributes<HTMLDivElement>, 'chil
24
24
  * document opens.
25
25
  */
26
26
  tools?: Tool[] | undefined;
27
+ /**
28
+ * Plugins, set up once when the root is created; a later change to this prop is not
29
+ * applied. Their tools join `tools`, their decoration and page layers apply to every
30
+ * document, and their commands are placed with `InkCommand` or `useCommand`.
31
+ */
32
+ plugins?: InkPlugin[] | undefined;
27
33
  /**
28
34
  * Adds `data-*` attributes, classes and a screen-reader description to each annotation's
29
35
  * element (see `DecorateAnnotation`). Decorations re-apply whenever the annotations change
@@ -1 +1 @@
1
- {"version":3,"file":"InkRoot.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkRoot.svelte.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"InkRoot.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkRoot.svelte.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ /*
3
+ * Opens the find bar; pressed while it is open. Pressing it again focuses the field. Place
4
+ * it in a toolbar next to `InkFindBar`, which it needs for the field to focus. Disabled until
5
+ * a document is open.
6
+ */
7
+ import { Search } from '@inkbar/icons'
8
+ import { Toolbar } from 'bits-ui'
9
+ import { tick } from 'svelte'
10
+
11
+ import { useInkContext } from '../context.js'
12
+ import { useDocument } from '../hooks/useDocument.svelte.js'
13
+ import { useSearch } from '../hooks/useSearch.svelte.js'
14
+
15
+ import { asBitsProps } from './child-snippet.js'
16
+ import InkIcon from './InkIcon.svelte'
17
+
18
+ import type { InkSearchButtonProps } from './search-button.js'
19
+
20
+ let {
21
+ class: className,
22
+ children,
23
+ child,
24
+ disabled,
25
+ onclick,
26
+ ...rest
27
+ }: InkSearchButtonProps = $props()
28
+
29
+ const ctx = useInkContext('InkSearchButton')
30
+ const search = useSearch()
31
+ const doc = useDocument()
32
+ </script>
33
+
34
+ <Toolbar.Button
35
+ {...asBitsProps(rest)}
36
+ class={['ink-search-button', className]}
37
+ data-ink-search-button=""
38
+ aria-label="Find"
39
+ title="Find"
40
+ aria-pressed={search.state.open}
41
+ data-state={search.state.open ? 'on' : 'off'}
42
+ disabled={!doc.isReady || !!disabled}
43
+ {...child ? { child } : {}}
44
+ onclick={async (e) => {
45
+ onclick?.(e)
46
+ if (e.defaultPrevented) return
47
+ search.open()
48
+ // The bar shows once this update lands; its field takes focus then.
49
+ await tick()
50
+ const field = ctx.rootEl?.querySelector<HTMLInputElement>('[data-ink-find-bar] input')
51
+ field?.focus()
52
+ field?.select()
53
+ }}
54
+ >
55
+ {#if children}{@render children()}{:else}<InkIcon icon={Search} />{/if}
56
+ </Toolbar.Button>
@@ -0,0 +1,5 @@
1
+ import type { InkSearchButtonProps } from './search-button.js';
2
+ declare const InkSearchButton: import("svelte").Component<InkSearchButtonProps, {}, "">;
3
+ type InkSearchButton = ReturnType<typeof InkSearchButton>;
4
+ export default InkSearchButton;
5
+ //# sourceMappingURL=InkSearchButton.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkSearchButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkSearchButton.svelte.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AA8C/D,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -18,9 +18,7 @@
18
18
 
19
19
  <script lang="ts">
20
20
  import {
21
- focusInitialMenuItem,
22
21
  handleMenuKeys,
23
- menuItems,
24
22
  SHAPE_COLUMNS,
25
23
  SHAPE_ICONS,
26
24
  SHAPE_KINDS,
@@ -35,6 +33,7 @@
35
33
  import { useToolOptions } from '../hooks/useToolOptions.svelte.js'
36
34
 
37
35
  import InkIcon from './InkIcon.svelte'
36
+ import { createMenuOpenFocus } from './menu-open-focus.js'
38
37
 
39
38
  let { class: className, children, ...rest }: InkShapeMenuProps = $props()
40
39
 
@@ -50,15 +49,14 @@
50
49
  let menu = $state<HTMLElement | null>(null)
51
50
 
52
51
  /** ArrowDown opens on the first item; a click opens on the checked one. */
53
- let focusFirst = false
54
- // Bits runs the open-focus callback more than once, so the flag outlives it; reset on close.
52
+ const openFocus = createMenuOpenFocus()
55
53
  $effect(() => {
56
- if (!open) focusFirst = false
54
+ if (!open) openFocus.reset()
57
55
  })
58
56
  function onTriggerKeydown(e: KeyboardEvent): void {
59
57
  if (e.key !== 'ArrowDown' || e.defaultPrevented || open) return
60
58
  e.preventDefault()
61
- focusFirst = true
59
+ openFocus.first()
62
60
  open = true
63
61
  }
64
62
 
@@ -123,13 +121,8 @@
123
121
  align="start"
124
122
  sideOffset={4}
125
123
  collisionPadding={8}
126
- onOpenAutoFocus={(e) => {
127
- e.preventDefault()
128
- if (menu) {
129
- if (focusFirst) menuItems(menu)[0]?.focus({ preventScroll: true })
130
- else focusInitialMenuItem(menu)
131
- }
132
- }}
124
+ onOpenAutoFocus={(e) => openFocus.onOpenAutoFocus(e, menu)}
125
+ onfocusin={openFocus.onfocusin}
133
126
  onkeydown={(e) => menu && handleMenuKeys(e, menu)}
134
127
  >
135
128
  <div
@@ -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;AAsGH,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;AA+FH,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -21,9 +21,7 @@
21
21
 
22
22
  <script lang="ts">
23
23
  import {
24
- focusInitialMenuItem,
25
24
  handleMenuKeys,
26
- menuItems,
27
25
  SIGNATURE_LABELS,
28
26
  signatureMenuModel,
29
27
  signaturePreview,
@@ -39,6 +37,7 @@
39
37
 
40
38
  import InkIcon from './InkIcon.svelte'
41
39
  import InkSignatureDialog from './InkSignatureDialog.svelte'
40
+ import { createMenuOpenFocus } from './menu-open-focus.js'
42
41
 
43
42
  import type { SignatureOptions, SignatureTemplate } from '@inkbar/core'
44
43
 
@@ -65,10 +64,9 @@
65
64
  let main = $state<HTMLElement | null>(null)
66
65
 
67
66
  /** ArrowDown opens on the first item; a click opens on the checked one. */
68
- let focusFirst = false
69
- // Bits runs the open-focus callback more than once, so the flag outlives it; reset on close.
67
+ const openFocus = createMenuOpenFocus()
70
68
  $effect(() => {
71
- if (!open) focusFirst = false
69
+ if (!open) openFocus.reset()
72
70
  })
73
71
  /** Set while the menu closes for the dialog, which takes focus instead of the trigger. */
74
72
  let toDialog = false
@@ -76,7 +74,7 @@
76
74
  function onTriggerKeydown(e: KeyboardEvent): void {
77
75
  if (e.key !== 'ArrowDown' || e.defaultPrevented || open) return
78
76
  e.preventDefault()
79
- focusFirst = true
77
+ openFocus.first()
80
78
  open = true
81
79
  }
82
80
 
@@ -168,13 +166,8 @@
168
166
  align="start"
169
167
  sideOffset={4}
170
168
  collisionPadding={8}
171
- onOpenAutoFocus={(e) => {
172
- e.preventDefault()
173
- if (menu) {
174
- if (focusFirst) menuItems(menu)[0]?.focus({ preventScroll: true })
175
- else focusInitialMenuItem(menu)
176
- }
177
- }}
169
+ onOpenAutoFocus={(e) => openFocus.onOpenAutoFocus(e, menu)}
170
+ onfocusin={openFocus.onfocusin}
178
171
  onCloseAutoFocus={(e) => {
179
172
  if (toDialog) e.preventDefault()
180
173
  toDialog = false
@@ -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;AAyLH,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;AAkLH,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -27,6 +27,7 @@
27
27
  } from '@inkbar/icons'
28
28
  import { Toolbar } from 'bits-ui'
29
29
 
30
+ import { useInkContext } from '../context.js'
30
31
  import { useTool } from '../hooks/useTool.svelte.js'
31
32
 
32
33
  import { asBitsProps } from './child-snippet.js'
@@ -56,11 +57,18 @@
56
57
  ...rest
57
58
  }: InkToolButtonProps = $props()
58
59
 
60
+ const ctx = useInkContext('InkToolButton')
59
61
  const current = useTool()
60
62
  const active = $derived(current.tool === tool)
61
- // A host tool has no built-in name or icon: `label` and `children` supply them.
62
- const name = $derived(label ?? TOOL_LABELS[tool] ?? tool)
63
- const icon = $derived(ICONS[tool])
63
+ /** A host or plugin tool by this id, for the name and icon core has none for. */
64
+ const own = $derived(
65
+ [...(ctx.hostTools ?? []), ...(ctx.pluginHost?.tools ?? [])].find(
66
+ (t) => t.id === tool
67
+ )
68
+ )
69
+ // `label` and `children` win; then core's name and icon; then the tool's own.
70
+ const name = $derived(label ?? TOOL_LABELS[tool] ?? own?.label ?? tool)
71
+ const icon = $derived(ICONS[tool] ?? own?.icon)
64
72
  </script>
65
73
 
66
74
  <Toolbar.Button
@@ -1 +1 @@
1
- {"version":3,"file":"InkToolButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolButton.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACpE,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC;AA4EH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"InkToolButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/InkToolButton.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACpE,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC;AAqFH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -126,6 +126,11 @@
126
126
  buffer,
127
127
  tool: ctx.tool,
128
128
  ...(ctx.hostTools ? { tools: ctx.hostTools } : {}),
129
+ // The engine adds the plugins' tools and layers, and decorates through the host, which
130
+ // reads `decorateAnnotation` first.
131
+ ...(ctx.pluginHost ? { plugins: ctx.pluginHost } : {}),
132
+ // The highlights draw from the shared search.
133
+ ...(ctx.search ? { search: ctx.search } : {}),
129
134
  // Read through, so the effect below can re-apply a new function without a rebuild.
130
135
  decorateAnnotation: (a) => ctx.decorateAnnotation?.(a),
131
136
  ...(ctx.toolStyle ? { toolStyle: ctx.toolStyle } : {}),
@@ -191,6 +196,16 @@
191
196
  })
192
197
  }
193
198
 
199
+ // The plugins' `root`. Before the build below, so it is set when the engine mounts.
200
+ $effect(() => {
201
+ const el = scroll
202
+ const { viewportEl } = ctx
203
+ viewportEl.set(el)
204
+ return () => {
205
+ if (viewportEl.get() === el) viewportEl.set(null)
206
+ }
207
+ })
208
+
194
209
  // Build for a new document or status; tear down on the next change and on unmount. Only the
195
210
  // values read before `untrack` are dependencies, each through a derived, which notifies only
196
211
  // when the value changes: the status is a new object on every engine report.
@@ -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;AA+SH,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;AA8TH,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface MenuOpenFocus {
2
+ /** Makes the next open start on the first item (ArrowDown on the trigger). */
3
+ first: () => void;
4
+ /** For the content's `onOpenAutoFocus`. */
5
+ onOpenAutoFocus: (e: Event, menu: HTMLElement | null) => void;
6
+ /** For the content's `onfocusin`: remembers where the user is. */
7
+ onfocusin: (e: FocusEvent) => void;
8
+ /** Call when the menu closes. */
9
+ reset: () => void;
10
+ }
11
+ export declare function createMenuOpenFocus(): MenuOpenFocus;
12
+ //# sourceMappingURL=menu-open-focus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-open-focus.d.ts","sourceRoot":"","sources":["../../src/lib/components/menu-open-focus.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,2CAA2C;IAC3C,eAAe,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7D,kEAAkE;IAClE,SAAS,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAA;IAClC,iCAAiC;IACjC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,wBAAgB,mBAAmB,IAAI,aAAa,CAsBnD"}
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Where a toolbar menu puts focus as it opens: the first item when ArrowDown opened it, else the
3
+ * checked one. Bits can remount a popover's focus scope just after it opens, which sends focus
4
+ * to the trigger and runs the open-focus callback again; by then the user may have moved on, so
5
+ * a repeat run goes back to the item they last focused.
6
+ */
7
+ import { focusInitialMenuItem, menuItems } from '@inkbar/core/dom';
8
+ export function createMenuOpenFocus() {
9
+ let first = false;
10
+ let last = null;
11
+ return {
12
+ first: () => {
13
+ first = true;
14
+ },
15
+ onOpenAutoFocus: (e, menu) => {
16
+ e.preventDefault();
17
+ if (!menu)
18
+ return;
19
+ if (last && menu.contains(last))
20
+ last.focus({ preventScroll: true });
21
+ else if (first)
22
+ menuItems(menu)[0]?.focus({ preventScroll: true });
23
+ else
24
+ focusInitialMenuItem(menu);
25
+ },
26
+ onfocusin: (e) => {
27
+ if (e.target instanceof HTMLElement)
28
+ last = e.target;
29
+ },
30
+ reset: () => {
31
+ first = false;
32
+ last = null;
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,10 @@
1
+ import type { ChildSnippet } from './child-snippet.js';
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLButtonAttributes } from 'svelte/elements';
4
+ /** Props of `InkSearchButton`: a toolbar button that may render through `child`. */
5
+ export interface InkSearchButtonProps extends Omit<HTMLButtonAttributes, 'children'> {
6
+ children?: Snippet | undefined;
7
+ /** Render your own button: spread `props` on it. */
8
+ child?: ChildSnippet | undefined;
9
+ }
10
+ //# sourceMappingURL=search-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-button.d.ts","sourceRoot":"","sources":["../../src/lib/components/search-button.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,oFAAoF;AACpF,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,UAAU,CAAC;IAClF,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,oDAAoD;IACpD,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;CACjC"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/context.d.ts CHANGED
@@ -1,8 +1,11 @@
1
- import type { Annotation, InkDocument, InkStatus, PdfAssets, SignatureLibrary } from '@inkbar/core';
2
- import type { DecorateAnnotation, Tool, ToolController, ToolId, ToolOptions, ToolStyle, Viewport, ZoomValue } from '@inkbar/core/dom';
1
+ import type { Annotation, InkDocument, InkStatus, PdfAssets, PluginHistory, SignatureLibrary, Writable } from '@inkbar/core';
2
+ import type { DecorateAnnotation, PluginHost, SearchController, Tool, ToolController, ToolId, ToolOptions, ToolStyle, Viewport, ZoomValue } from '@inkbar/core/dom';
3
3
  export type { InkStatus };
4
- /** Undo and redo for the current document, whichever mode its annotations are in. */
5
- export interface InkHistory {
4
+ /**
5
+ * Undo and redo for the current document, whichever mode its annotations are in. `track` and
6
+ * `react` (from `PluginHistory`) reach the same history, so plugin stores undo with the edits.
7
+ */
8
+ export interface InkHistory extends PluginHistory {
6
9
  readonly canUndo: boolean;
7
10
  readonly canRedo: boolean;
8
11
  undo(): void;
@@ -27,6 +30,20 @@ export interface InkContext {
27
30
  readonly status: InkStatus;
28
31
  readonly annotations: readonly Annotation[];
29
32
  readonly selection: readonly string[];
33
+ /**
34
+ * The plugins from InkRoot's `plugins` prop, set up once the root has mounted and kept for
35
+ * its life; `null` before that, and on the server. InkViewport hands it to the engine;
36
+ * InkCommand places its commands.
37
+ */
38
+ readonly pluginHost: PluginHost | null;
39
+ /**
40
+ * The root's search controller, built with the plugin host and kept for its life; `null`
41
+ * before the root has mounted, and on the server. InkViewport hands it to the engine, which
42
+ * draws the highlights; `useSearch` and the find bar read it.
43
+ */
44
+ readonly search: SearchController | null;
45
+ /** InkViewport's scroll element while it is mounted: the plugins' `root`. */
46
+ readonly viewportEl: Writable<HTMLElement | null>;
30
47
  /** Set by InkViewport once it has built its viewport and tool controller. */
31
48
  readonly viewport: Viewport | null;
32
49
  readonly tools: ToolController | null;