@arraypress/extension-kit 1.10.0 → 1.11.1

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 (69) hide show
  1. package/README.md +103 -21
  2. package/bin/extension-kit.js +2 -1
  3. package/package.json +1 -1
  4. package/src/ai/host.d.ts +183 -0
  5. package/src/ai/host.js +225 -0
  6. package/src/ai/model.d.ts +2 -1
  7. package/src/ai/model.js +10 -2
  8. package/src/build/vendor.d.ts +29 -8
  9. package/src/build/vendor.js +136 -40
  10. package/src/content/highlight.d.ts +15 -6
  11. package/src/content/highlight.js +42 -17
  12. package/src/content/overlay.d.ts +19 -4
  13. package/src/content/overlay.js +44 -6
  14. package/src/content/slices.d.ts +3 -1
  15. package/src/content/slices.js +29 -14
  16. package/src/content/watch.d.ts +4 -2
  17. package/src/content/watch.js +17 -3
  18. package/src/dom/focus.d.ts +11 -0
  19. package/src/dom/focus.js +15 -0
  20. package/src/frame/window.d.ts +1 -1
  21. package/src/frame/window.js +8 -7
  22. package/src/index.d.ts +10 -3
  23. package/src/index.js +8 -3
  24. package/src/panel/show-on-page.d.ts +82 -0
  25. package/src/panel/show-on-page.js +83 -0
  26. package/src/scripting/call.d.ts +29 -9
  27. package/src/scripting/call.js +35 -11
  28. package/src/scripting/context-menus.d.ts +45 -0
  29. package/src/scripting/context-menus.js +53 -0
  30. package/src/scripting/registration.d.ts +2 -1
  31. package/src/scripting/registration.js +2 -1
  32. package/src/scripting/tab.d.ts +17 -5
  33. package/src/scripting/tab.js +15 -7
  34. package/src/settings/saver.js +16 -6
  35. package/src/settings/storage.d.ts +33 -2
  36. package/src/settings/storage.js +73 -3
  37. package/src/settings/store.d.ts +17 -0
  38. package/src/settings/store.js +87 -20
  39. package/src/sites/sites.d.ts +6 -1
  40. package/src/sites/sites.js +20 -3
  41. package/src/styles/chrome-ui.css +59 -30
  42. package/src/testing/e2e.d.ts +35 -4
  43. package/src/testing/e2e.js +67 -5
  44. package/src/testing/fake-model.d.ts +9 -1
  45. package/src/testing/fake-model.js +44 -23
  46. package/src/testing/fake-storage.d.ts +41 -13
  47. package/src/testing/fake-storage.js +163 -51
  48. package/src/testing/index.d.ts +3 -3
  49. package/src/testing/index.js +5 -3
  50. package/src/testing/live-run.d.ts +23 -2
  51. package/src/testing/live-run.js +64 -18
  52. package/src/testing/messages.d.ts +7 -3
  53. package/src/testing/messages.js +50 -9
  54. package/src/testing/package.d.ts +56 -7
  55. package/src/testing/package.js +57 -18
  56. package/src/testing/prompt.d.ts +4 -2
  57. package/src/testing/prompt.js +7 -3
  58. package/src/time/pool.d.ts +2 -1
  59. package/src/time/pool.js +4 -2
  60. package/src/toolbar/theme.d.ts +10 -2
  61. package/src/toolbar/theme.js +12 -4
  62. package/src/ui/menu.d.ts +13 -3
  63. package/src/ui/menu.js +48 -11
  64. package/src/ui/model-row.js +5 -1
  65. package/src/ui/site-list.js +12 -3
  66. package/src/ui/text-dialog.d.ts +3 -2
  67. package/src/ui/text-dialog.js +10 -3
  68. package/src/ui/toast.d.ts +3 -0
  69. package/src/ui/toast.js +23 -1
@@ -4,8 +4,9 @@
4
4
  * folder's name (`askText`); several, as chrome://settings/searchEngines asks
5
5
  * for a site search (`askFields`). The action button stays disabled while a
6
6
  * field is blank or isn't valid, with the reason under that field. Enter
7
- * means the action button, Esc and Cancel cancel, and the focus goes back to
8
- * what opened it. The page needs no markup: the dialog (`#text-dialog`) is
7
+ * means the action button, Esc and Cancel cancel (as does the dialog being
8
+ * closed any other way), and the focus goes back to what opened it. A reason
9
+ * is announced as it appears, and its field marked invalid. The page needs no markup: the dialog (`#text-dialog`) is
9
10
  * made the first time, its fields each time it's asked.
10
11
  *
11
12
  * @module ui/text-dialog
@@ -178,7 +179,7 @@ function fieldFor(doc, request, only) {
178
179
  input.value = typeof request.value === 'string' ? request.value : '';
179
180
  if (request.placeholder) input.placeholder = request.placeholder;
180
181
  if (request.maxLength !== undefined) input.maxLength = request.maxLength;
181
- const error = element(doc, 'span', { id: ids.error, class: 'hint error' });
182
+ const error = element(doc, 'span', { id: ids.error, class: 'hint error', 'aria-live': 'polite' });
182
183
  error.hidden = true;
183
184
  const hint = element(doc, 'span', { id: ids.hint, class: 'hint secondary' });
184
185
  hint.hidden = true;
@@ -226,6 +227,9 @@ export function askFields({
226
227
  const { reason } = waiting ? { reason: '' } : checkText(input.value, request.validate);
227
228
  error.textContent = reason;
228
229
  error.hidden = !reason;
230
+ // Said to a screen reader as it appears, and the field marked (the stylesheet turns its underline red).
231
+ if (reason) input.setAttribute('aria-invalid', 'true');
232
+ else input.removeAttribute('aria-invalid');
229
233
  hint.textContent = waiting ? request.waiting ?? '' : typeof request.hint === 'function' ? request.hint(input.value) : request.hint ?? '';
230
234
  hint.hidden = !hint.textContent;
231
235
  };
@@ -291,6 +295,9 @@ export function askFields({
291
295
  event.preventDefault();
292
296
  settle(null);
293
297
  }, { signal });
298
+ // Closed any other way (the browser's own close after a second Esc, another script): cancelled, not left unanswered.
299
+ // (The event comes a task after the closing: one from the ask before this, already answered, finds the dialog open again.)
300
+ parts.dialog.addEventListener('close', () => { if (!parts.dialog.open) settle(null); }, { signal });
294
301
 
295
302
  parts.dialog.showModal();
296
303
  // The first field still to fill in, or the first, its text selected to type over.
package/src/ui/toast.d.ts CHANGED
@@ -2,6 +2,9 @@
2
2
  * Chrome's toast (cr-toast), bottom left, as chrome://bookmarks shows one
3
3
  * after a delete: "Deleted “Research” Undo". One per page, made by
4
4
  * {@link createToast}; a new message replaces the last and restarts the wait.
5
+ * While the keyboard focus is on Undo, or the pointer is over the toast, it
6
+ * stays, and the wait starts again in full once they leave (cr-toast stops
7
+ * its timer on `focusin` and restarts it on `focusout`).
5
8
  *
6
9
  * It's a manual `popover`, so it shows in the top layer: over anything the
7
10
  * page stacks (a content script's toast over a site's own fixed bars), and
package/src/ui/toast.js CHANGED
@@ -2,6 +2,9 @@
2
2
  * Chrome's toast (cr-toast), bottom left, as chrome://bookmarks shows one
3
3
  * after a delete: "Deleted “Research” Undo". One per page, made by
4
4
  * {@link createToast}; a new message replaces the last and restarts the wait.
5
+ * While the keyboard focus is on Undo, or the pointer is over the toast, it
6
+ * stays, and the wait starts again in full once they leave (cr-toast stops
7
+ * its timer on `focusin` and restarts it on `focusout`).
5
8
  *
6
9
  * It's a manual `popover`, so it shows in the top layer: over anything the
7
10
  * page stacks (a content script's toast over a site's own fixed bars), and
@@ -75,11 +78,27 @@ export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDurat
75
78
  /** @type {(() => unknown) | null} */
76
79
  let undoLast = null;
77
80
 
81
+ /** How long the toast showing now stays, once nothing holds it. */
82
+ let stay = duration;
83
+
78
84
  const hide = () => {
79
85
  clearTimeout(timer);
80
86
  toast.togglePopover(false);
81
87
  undoLast = null;
82
88
  };
89
+ /** Whether the keyboard focus is in the toast (on Undo) or the pointer over it: it stays while either is. */
90
+ const held = () => toast.matches(':focus-within, :hover');
91
+ /** The wait starts again, in full, unless the toast is held: as cr-toast's, whose focusout restarts it. */
92
+ const wait = () => {
93
+ clearTimeout(timer);
94
+ if (!held() && toast.matches(':popover-open')) timer = setTimeout(hide, stay);
95
+ };
96
+ // cr-toast stops its timer on focusin and restarts it on focusout, so Undo can't go from under
97
+ // the keyboard; the pointer over it holds it the same way (WCAG 2.2.1, Timing Adjustable).
98
+ toast.addEventListener('focusin', () => clearTimeout(timer));
99
+ toast.addEventListener('pointerenter', () => clearTimeout(timer));
100
+ toast.addEventListener('focusout', () => queueMicrotask(wait));
101
+ toast.addEventListener('pointerleave', wait);
83
102
  undoButton.addEventListener('click', () => {
84
103
  const undo = undoLast;
85
104
  hide();
@@ -93,10 +112,13 @@ export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDurat
93
112
  text.textContent = message;
94
113
  undoButton.hidden = !undo;
95
114
  undoLast = undo;
115
+ // Taken off the page since (a single-page site replaced its body): put back, or showing it would throw.
116
+ if (!toast.isConnected) (root?.isConnected === false ? doc.body : root ?? doc.body).append(toast);
96
117
  // Hidden and shown again, so it's the newest in the top layer (over a popover since).
97
118
  toast.togglePopover(false);
98
119
  toast.togglePopover(true);
99
- timer = setTimeout(hide, undo ? undoDuration : duration);
120
+ stay = undo ? undoDuration : duration;
121
+ wait();
100
122
  },
101
123
  hide,
102
124
  };