@fairfox/polly 0.23.0 → 0.25.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 (116) hide show
  1. package/README.md +55 -1
  2. package/dist/cli/polly.js +21 -1
  3. package/dist/cli/polly.js.map +3 -3
  4. package/dist/src/actions/error.d.ts +26 -0
  5. package/dist/src/actions/event-delegation.d.ts +48 -0
  6. package/dist/src/actions/form.d.ts +72 -0
  7. package/dist/src/actions/index.d.ts +13 -0
  8. package/dist/src/actions/index.js +525 -0
  9. package/dist/src/actions/index.js.map +15 -0
  10. package/dist/src/actions/overlay.d.ts +26 -0
  11. package/dist/src/actions/registry.d.ts +25 -0
  12. package/dist/src/actions/store.d.ts +26 -0
  13. package/dist/src/actions/testing.d.ts +26 -0
  14. package/dist/src/background/index.js +26 -1
  15. package/dist/src/background/index.js.map +2 -2
  16. package/dist/src/background/message-router.js +26 -1
  17. package/dist/src/background/message-router.js.map +2 -2
  18. package/dist/src/client/index.js +27 -2
  19. package/dist/src/client/index.js.map +3 -3
  20. package/dist/src/elysia/index.js +27 -2
  21. package/dist/src/elysia/index.js.map +3 -3
  22. package/dist/src/elysia/peer-repo-plugin.d.ts +1 -1
  23. package/dist/src/index.js +26 -1
  24. package/dist/src/index.js.map +2 -2
  25. package/dist/src/mesh-node.d.ts +89 -0
  26. package/dist/src/mesh-node.js +619 -0
  27. package/dist/src/mesh-node.js.map +14 -0
  28. package/dist/src/mesh.d.ts +10 -0
  29. package/dist/src/mesh.js +951 -24
  30. package/dist/src/mesh.js.map +17 -9
  31. package/dist/src/peer.d.ts +1 -0
  32. package/dist/src/peer.js +130 -84
  33. package/dist/src/peer.js.map +11 -10
  34. package/dist/src/polly-ui/ActionForm.d.ts +21 -0
  35. package/dist/src/polly-ui/ActionInput.d.ts +41 -0
  36. package/dist/src/polly-ui/ConfirmDialog.d.ts +24 -0
  37. package/dist/src/polly-ui/Layout.d.ts +51 -0
  38. package/dist/src/polly-ui/Modal.d.ts +52 -0
  39. package/dist/src/polly-ui/OverlayRoot.d.ts +10 -0
  40. package/dist/src/polly-ui/TextInput.d.ts +31 -0
  41. package/dist/src/polly-ui/Toast.d.ts +19 -0
  42. package/dist/src/polly-ui/index.css +319 -0
  43. package/dist/src/polly-ui/index.d.ts +17 -0
  44. package/dist/src/polly-ui/index.js +953 -0
  45. package/dist/src/polly-ui/index.js.map +22 -0
  46. package/dist/src/polly-ui/internal/focus-trap.d.ts +10 -0
  47. package/dist/src/polly-ui/internal/input-base.d.ts +18 -0
  48. package/dist/src/polly-ui/internal/scroll-lock.d.ts +9 -0
  49. package/dist/src/polly-ui/styles.css +70 -0
  50. package/dist/src/polly-ui/theme.css +163 -0
  51. package/dist/src/shared/adapters/index.js +26 -1
  52. package/dist/src/shared/adapters/index.js.map +2 -2
  53. package/dist/src/shared/lib/blob-cache.d.ts +58 -0
  54. package/dist/src/shared/lib/blob-store-impl.d.ts +33 -0
  55. package/dist/src/shared/lib/blob-store.d.ts +87 -0
  56. package/dist/src/shared/lib/blob-transfer.d.ts +58 -0
  57. package/dist/src/shared/lib/context-helpers.js +26 -1
  58. package/dist/src/shared/lib/context-helpers.js.map +2 -2
  59. package/dist/src/shared/lib/crdt-specialised.d.ts +1 -1
  60. package/dist/src/shared/lib/crdt-state.d.ts +1 -1
  61. package/dist/src/shared/lib/errors.js +26 -1
  62. package/dist/src/shared/lib/errors.js.map +2 -2
  63. package/dist/src/shared/lib/keyring-storage.d.ts +57 -0
  64. package/dist/src/shared/lib/mesh-client.d.ts +91 -0
  65. package/dist/src/shared/lib/mesh-network-adapter.d.ts +1 -1
  66. package/dist/src/shared/lib/mesh-signaling-client.d.ts +6 -0
  67. package/dist/src/shared/lib/mesh-state.d.ts +1 -1
  68. package/dist/src/shared/lib/mesh-webrtc-adapter.d.ts +20 -1
  69. package/dist/src/shared/lib/message-bus.js +26 -1
  70. package/dist/src/shared/lib/message-bus.js.map +2 -2
  71. package/dist/src/shared/lib/peer-relay-adapter.d.ts +1 -1
  72. package/dist/src/shared/lib/peer-repo-server.d.ts +1 -1
  73. package/dist/src/shared/lib/peer-state.d.ts +1 -1
  74. package/dist/src/shared/lib/resource.js +26 -1
  75. package/dist/src/shared/lib/resource.js.map +2 -2
  76. package/dist/src/shared/lib/state.js +26 -1
  77. package/dist/src/shared/lib/state.js.map +2 -2
  78. package/dist/src/shared/lib/test-helpers.js +26 -1
  79. package/dist/src/shared/lib/test-helpers.js.map +2 -2
  80. package/dist/src/shared/lib/wasm-init.d.ts +17 -0
  81. package/dist/src/shared/state/app-state.js +26 -1
  82. package/dist/src/shared/state/app-state.js.map +2 -2
  83. package/dist/src/shared/types/messages.js +26 -1
  84. package/dist/src/shared/types/messages.js.map +2 -2
  85. package/dist/tools/quality/src/cli.js +647 -28
  86. package/dist/tools/quality/src/cli.js.map +11 -5
  87. package/dist/tools/quality/src/css/check-layout.d.ts +19 -0
  88. package/dist/tools/quality/src/css/check-quality.d.ts +24 -0
  89. package/dist/tools/quality/src/css/check-unused.d.ts +20 -0
  90. package/dist/tools/quality/src/css/check-vars.d.ts +22 -0
  91. package/dist/tools/quality/src/css/shared.d.ts +33 -0
  92. package/dist/tools/quality/src/index.d.ts +37 -0
  93. package/dist/tools/quality/src/index.js +735 -0
  94. package/dist/tools/quality/src/index.js.map +16 -0
  95. package/dist/tools/quality/src/logger.d.ts +26 -0
  96. package/dist/tools/quality/src/no-as-casting.d.ts +44 -0
  97. package/dist/tools/test/src/adapters/index.js +26 -1
  98. package/dist/tools/test/src/adapters/index.js.map +2 -2
  99. package/dist/tools/test/src/browser/index.js +26 -1
  100. package/dist/tools/test/src/browser/index.js.map +2 -2
  101. package/dist/tools/test/src/browser/run.js +238 -0
  102. package/dist/tools/test/src/browser/run.js.map +11 -0
  103. package/dist/tools/test/src/index.js +26 -1
  104. package/dist/tools/test/src/index.js.map +2 -2
  105. package/dist/tools/test/src/test-utils.js +26 -1
  106. package/dist/tools/test/src/test-utils.js.map +2 -2
  107. package/dist/tools/test/src/visual/compare.d.ts +23 -0
  108. package/dist/tools/test/src/visual/harness.d.ts +53 -0
  109. package/dist/tools/test/src/visual/index.d.ts +12 -0
  110. package/dist/tools/test/src/visual/index.js +13968 -0
  111. package/dist/tools/test/src/visual/index.js.map +41 -0
  112. package/dist/tools/verify/src/cli.js +3 -3
  113. package/dist/tools/verify/src/cli.js.map +1 -1
  114. package/dist/tools/verify/src/config.js +26 -1
  115. package/dist/tools/verify/src/config.js.map +2 -2
  116. package/package.json +42 -3
@@ -0,0 +1,953 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __toCommonJS = (from) => {
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
34
+ if (entry)
35
+ return entry;
36
+ entry = __defProp({}, "__esModule", { value: true });
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
45
+ __moduleCache.set(from, entry);
46
+ return entry;
47
+ };
48
+ var __moduleCache;
49
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
50
+ var __returnValue = (v) => v;
51
+ function __exportSetter(name, newValue) {
52
+ this[name] = __returnValue.bind(null, newValue);
53
+ }
54
+ var __export = (target, all) => {
55
+ for (var name in all)
56
+ __defProp(target, name, {
57
+ get: all[name],
58
+ enumerable: true,
59
+ configurable: true,
60
+ set: __exportSetter.bind(all, name)
61
+ });
62
+ };
63
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
64
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
65
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
66
+ }) : x)(function(x) {
67
+ if (typeof require !== "undefined")
68
+ return require.apply(this, arguments);
69
+ throw Error('Dynamic require of "' + x + '" is not supported');
70
+ });
71
+
72
+ // src/polly-ui/ActionForm.module.css
73
+ var ActionForm_module_default = {
74
+ form: "form_oqTahQ"
75
+ };
76
+
77
+ // src/polly-ui/ActionForm.tsx
78
+ import { jsxDEV } from "preact/jsx-dev-runtime";
79
+ function ActionForm(props) {
80
+ const { form, children, className, id } = props;
81
+ const combined = className ? `${ActionForm_module_default["form"]} ${className}` : ActionForm_module_default["form"];
82
+ return /* @__PURE__ */ jsxDEV("form", {
83
+ id,
84
+ class: combined,
85
+ "data-polly-ui": true,
86
+ "data-polly-action-form": form.name,
87
+ "data-action": `${form.name}:submit`,
88
+ "aria-busy": form.isSubmitting.value,
89
+ "aria-label": props["aria-label"],
90
+ "aria-labelledby": props["aria-labelledby"],
91
+ noValidate: true,
92
+ children
93
+ }, undefined, false, undefined, this);
94
+ }
95
+ // src/polly-ui/ActionInput.tsx
96
+ import { useCallback, useEffect, useRef, useState } from "preact/hooks";
97
+
98
+ // src/polly-ui/ActionInput.module.css
99
+ var ActionInput_module_default = {
100
+ root: "root_-n6UrQ",
101
+ view: "view_-n6UrQ",
102
+ edit: "edit_-n6UrQ",
103
+ placeholder: "placeholder_-n6UrQ"
104
+ };
105
+
106
+ // src/polly-ui/ActionInput.tsx
107
+ import { jsxDEV as jsxDEV2 } from "preact/jsx-dev-runtime";
108
+ function ActionInput(props) {
109
+ const variant = props.variant ?? "single";
110
+ const saveOn = props.saveOn ?? "blur";
111
+ const [mode, setMode] = useState("view");
112
+ const [draft, setDraft] = useState(props.value);
113
+ const inputRef = useRef(null);
114
+ useEffect(() => {
115
+ if (mode === "view")
116
+ setDraft(props.value);
117
+ }, [props.value, mode]);
118
+ useEffect(() => {
119
+ if (mode === "edit" && inputRef.current) {
120
+ inputRef.current.focus();
121
+ inputRef.current.select?.();
122
+ }
123
+ }, [mode]);
124
+ const enterEdit = useCallback(() => {
125
+ if (props.disabled)
126
+ return;
127
+ setDraft(props.value);
128
+ setMode("edit");
129
+ }, [props.disabled, props.value]);
130
+ const commit = useCallback((next) => {
131
+ if (next === props.value) {
132
+ setMode("view");
133
+ return;
134
+ }
135
+ const dataAttrs = {
136
+ ...props.actionData ?? {},
137
+ value: next
138
+ };
139
+ const hidden = document.createElement("button");
140
+ hidden.setAttribute("data-action", props.action);
141
+ for (const [key, value] of Object.entries(dataAttrs)) {
142
+ const dashKey = key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
143
+ hidden.setAttribute(`data-action-${dashKey}`, value);
144
+ }
145
+ hidden.style.position = "fixed";
146
+ hidden.style.opacity = "0";
147
+ hidden.style.pointerEvents = "none";
148
+ hidden.tabIndex = -1;
149
+ document.body.appendChild(hidden);
150
+ try {
151
+ hidden.click();
152
+ } finally {
153
+ hidden.remove();
154
+ }
155
+ setMode("view");
156
+ }, [props.action, props.actionData, props.value]);
157
+ const cancel = useCallback(() => {
158
+ setDraft(props.value);
159
+ setMode("view");
160
+ }, [props.value]);
161
+ const onKeyDown = (event) => {
162
+ if (event.key === "Escape") {
163
+ event.preventDefault();
164
+ cancel();
165
+ return;
166
+ }
167
+ if (event.key === "Enter") {
168
+ const cmd = event.metaKey || event.ctrlKey;
169
+ if (saveOn === "enter" && variant === "single") {
170
+ event.preventDefault();
171
+ commit(draft);
172
+ } else if (saveOn === "enter" && variant === "multi" && cmd || saveOn === "cmd-enter") {
173
+ if (cmd) {
174
+ event.preventDefault();
175
+ commit(draft);
176
+ }
177
+ }
178
+ }
179
+ };
180
+ const className = props.className ? `${ActionInput_module_default["root"]} ${props.className}` : ActionInput_module_default["root"];
181
+ if (mode === "view") {
182
+ const rendered = props.renderView ? props.renderView(props.value) : props.value;
183
+ const isEmpty = props.value.length === 0;
184
+ return /* @__PURE__ */ jsxDEV2("div", {
185
+ class: `${className} ${ActionInput_module_default["view"]}`,
186
+ "data-polly-ui": true,
187
+ "data-polly-action-input": true,
188
+ "data-polly-interactive": true,
189
+ "data-state": isEmpty ? "empty" : "filled",
190
+ "data-variant": variant,
191
+ tabIndex: props.disabled ? -1 : 0,
192
+ role: "button",
193
+ "aria-label": props.ariaLabel ?? "Edit",
194
+ "aria-disabled": props.disabled ? "true" : undefined,
195
+ onClick: enterEdit,
196
+ onKeyDown: (e) => {
197
+ if (e.key === "Enter" || e.key === " ") {
198
+ e.preventDefault();
199
+ enterEdit();
200
+ }
201
+ },
202
+ children: isEmpty && props.placeholder ? /* @__PURE__ */ jsxDEV2("span", {
203
+ class: ActionInput_module_default["placeholder"],
204
+ children: props.placeholder
205
+ }, undefined, false, undefined, this) : rendered
206
+ }, undefined, false, undefined, this);
207
+ }
208
+ const common = {
209
+ class: `${ActionInput_module_default["edit"]} ${ActionInput_module_default["root"]}`,
210
+ "data-polly-ui": true,
211
+ "data-polly-action-input": true,
212
+ "data-state": "editing",
213
+ "data-variant": variant,
214
+ placeholder: props.placeholder,
215
+ value: draft,
216
+ onInput: (e) => setDraft(e.currentTarget.value),
217
+ onBlur: () => {
218
+ if (saveOn === "blur")
219
+ commit(draft);
220
+ else
221
+ cancel();
222
+ },
223
+ onKeyDown,
224
+ "aria-label": props.ariaLabel ?? "Edit",
225
+ disabled: props.disabled
226
+ };
227
+ if (variant === "multi") {
228
+ return /* @__PURE__ */ jsxDEV2("textarea", {
229
+ ref: (el) => {
230
+ inputRef.current = el;
231
+ },
232
+ ...common
233
+ }, undefined, false, undefined, this);
234
+ }
235
+ return /* @__PURE__ */ jsxDEV2("input", {
236
+ ref: (el) => {
237
+ inputRef.current = el;
238
+ },
239
+ type: "text",
240
+ ...common
241
+ }, undefined, false, undefined, this);
242
+ }
243
+ // src/polly-ui/ConfirmDialog.tsx
244
+ import { signal as signal2 } from "@preact/signals";
245
+
246
+ // src/polly-ui/ConfirmDialog.module.css
247
+ var ConfirmDialog_module_default = {
248
+ actions: "actions_ZfTX4A",
249
+ cancel: "cancel_ZfTX4A",
250
+ confirm: "confirm_ZfTX4A",
251
+ confirmDanger: "confirmDanger_ZfTX4A"
252
+ };
253
+
254
+ // src/polly-ui/Modal.tsx
255
+ import { createContext } from "preact";
256
+ import { createPortal } from "preact/compat";
257
+ import { useContext, useEffect as useEffect3, useId, useRef as useRef3, useState as useState2 } from "preact/hooks";
258
+
259
+ // src/actions/overlay.ts
260
+ import { signal } from "@preact/signals";
261
+ var stack = signal([]);
262
+ function overlayStack() {
263
+ return stack.value;
264
+ }
265
+ function hasOpenOverlay() {
266
+ return stack.value.length > 0;
267
+ }
268
+ function topOverlay() {
269
+ const s = stack.value;
270
+ return s[s.length - 1];
271
+ }
272
+ function pushOverlay(entry) {
273
+ stack.value = [...stack.value, entry];
274
+ }
275
+ function popOverlay(id) {
276
+ const s = stack.value;
277
+ if (s.length === 0)
278
+ return;
279
+ if (id === undefined) {
280
+ const top = s[s.length - 1];
281
+ stack.value = s.slice(0, -1);
282
+ return top;
283
+ }
284
+ const idx = s.findIndex((e) => e.id === id);
285
+ if (idx === -1)
286
+ return;
287
+ const entry = s[idx];
288
+ stack.value = [...s.slice(0, idx), ...s.slice(idx + 1)];
289
+ return entry;
290
+ }
291
+ function closeTopOverlay() {
292
+ const top = topOverlay();
293
+ if (!top)
294
+ return;
295
+ top.onClose?.();
296
+ return popOverlay(top.id);
297
+ }
298
+ function resetOverlayStack() {
299
+ stack.value = [];
300
+ }
301
+
302
+ // src/polly-ui/internal/focus-trap.ts
303
+ var FOCUSABLE_SELECTOR = [
304
+ "a[href]",
305
+ "area[href]",
306
+ "button:not([disabled])",
307
+ "input:not([disabled]):not([type=hidden])",
308
+ "select:not([disabled])",
309
+ "textarea:not([disabled])",
310
+ "iframe",
311
+ "object",
312
+ "embed",
313
+ '[tabindex]:not([tabindex="-1"])',
314
+ "[contenteditable]"
315
+ ].join(",");
316
+ function getFocusable(root) {
317
+ const nodes = Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR));
318
+ return nodes.filter((el) => !el.hasAttribute("disabled") && el.getAttribute("aria-hidden") !== "true");
319
+ }
320
+ function installFocusTrap(root) {
321
+ const previousActive = document.activeElement instanceof HTMLElement ? document.activeElement : null;
322
+ const focusable = getFocusable(root);
323
+ const first = focusable[0] ?? root;
324
+ if (!root.contains(document.activeElement)) {
325
+ first.focus();
326
+ }
327
+ function handleKeyDown(event) {
328
+ if (event.key !== "Tab")
329
+ return;
330
+ const items = getFocusable(root);
331
+ if (items.length === 0) {
332
+ event.preventDefault();
333
+ return;
334
+ }
335
+ const firstItem = items[0];
336
+ const lastItem = items[items.length - 1];
337
+ if (!firstItem || !lastItem)
338
+ return;
339
+ const active = document.activeElement;
340
+ if (event.shiftKey) {
341
+ if (active === firstItem || !root.contains(active)) {
342
+ event.preventDefault();
343
+ lastItem.focus();
344
+ }
345
+ } else if (active === lastItem) {
346
+ event.preventDefault();
347
+ firstItem.focus();
348
+ }
349
+ }
350
+ document.addEventListener("keydown", handleKeyDown);
351
+ return () => {
352
+ document.removeEventListener("keydown", handleKeyDown);
353
+ if (previousActive && document.contains(previousActive)) {
354
+ previousActive.focus();
355
+ }
356
+ };
357
+ }
358
+
359
+ // src/polly-ui/Modal.module.css
360
+ var Modal_module_default = {
361
+ container: "container_fLMrWA",
362
+ backdrop: "backdrop_fLMrWA",
363
+ surface: "surface_fLMrWA",
364
+ header: "header_fLMrWA",
365
+ title: "title_fLMrWA",
366
+ body: "body_fLMrWA",
367
+ footer: "footer_fLMrWA",
368
+ close: "close_fLMrWA"
369
+ };
370
+
371
+ // src/polly-ui/OverlayRoot.tsx
372
+ import { effect } from "@preact/signals";
373
+ import { useEffect as useEffect2, useRef as useRef2 } from "preact/hooks";
374
+
375
+ // src/polly-ui/internal/scroll-lock.ts
376
+ var count = 0;
377
+ function acquireScrollLock() {
378
+ if (count === 0) {
379
+ const html = document.documentElement;
380
+ const gutter = window.innerWidth - html.clientWidth;
381
+ html.style.setProperty("--polly-scrollbar-gutter", `${gutter}px`);
382
+ html.setAttribute("data-polly-scroll-locked", "true");
383
+ }
384
+ count += 1;
385
+ let released = false;
386
+ return () => {
387
+ if (released)
388
+ return;
389
+ released = true;
390
+ count -= 1;
391
+ if (count <= 0) {
392
+ count = 0;
393
+ const html = document.documentElement;
394
+ html.removeAttribute("data-polly-scroll-locked");
395
+ html.style.removeProperty("--polly-scrollbar-gutter");
396
+ }
397
+ };
398
+ }
399
+
400
+ // src/polly-ui/OverlayRoot.module.css
401
+ var OverlayRoot_module_default = {
402
+ root: "root_CqRu1Q"
403
+ };
404
+
405
+ // src/polly-ui/OverlayRoot.tsx
406
+ import { jsxDEV as jsxDEV3 } from "preact/jsx-dev-runtime";
407
+ function OverlayRoot() {
408
+ const ref = useRef2(null);
409
+ useEffect2(() => {
410
+ let release = null;
411
+ const dispose = effect(() => {
412
+ if (hasOpenOverlay()) {
413
+ if (!release)
414
+ release = acquireScrollLock();
415
+ } else if (release) {
416
+ release();
417
+ release = null;
418
+ }
419
+ });
420
+ return () => {
421
+ dispose();
422
+ release?.();
423
+ };
424
+ }, []);
425
+ return /* @__PURE__ */ jsxDEV3("div", {
426
+ ref,
427
+ class: OverlayRoot_module_default["root"],
428
+ "data-polly-ui": true,
429
+ "data-polly-overlay-root": true
430
+ }, undefined, false, undefined, this);
431
+ }
432
+ function getOverlayRootNode() {
433
+ return document.querySelector("[data-polly-overlay-root]");
434
+ }
435
+
436
+ // src/polly-ui/Modal.tsx
437
+ import { jsxDEV as jsxDEV4 } from "preact/jsx-dev-runtime";
438
+ var Ctx = createContext(null);
439
+ function useModal() {
440
+ const ctx = useContext(Ctx);
441
+ if (!ctx)
442
+ throw new Error("Modal sub-components must render inside <Modal.Root>");
443
+ return ctx;
444
+ }
445
+ function Root({ when, onClose, children, "aria-label": ariaLabel }) {
446
+ const id = useId();
447
+ const titleId = `${id}-title`;
448
+ const descId = `${id}-desc`;
449
+ const mountRef = useRef3(null);
450
+ const [portalNode, setPortalNode] = useState2(null);
451
+ const isOpen = typeof when === "boolean" ? when : when.value;
452
+ useEffect3(() => {
453
+ if (!isOpen) {
454
+ setPortalNode(null);
455
+ return;
456
+ }
457
+ setPortalNode(getOverlayRootNode());
458
+ }, [isOpen]);
459
+ useEffect3(() => {
460
+ if (!isOpen || !portalNode)
461
+ return;
462
+ const entry = { id, onClose };
463
+ pushOverlay(entry);
464
+ const el = mountRef.current;
465
+ const cleanup = el ? installFocusTrap(el) : () => {};
466
+ return () => {
467
+ cleanup();
468
+ popOverlay(id);
469
+ };
470
+ }, [isOpen, portalNode, id, onClose]);
471
+ if (!isOpen || !portalNode)
472
+ return null;
473
+ const close = () => {
474
+ onClose?.();
475
+ };
476
+ const ctx = { id, titleId, descId, close };
477
+ const content = /* @__PURE__ */ jsxDEV4(Ctx.Provider, {
478
+ value: ctx,
479
+ children: /* @__PURE__ */ jsxDEV4("div", {
480
+ ref: mountRef,
481
+ class: Modal_module_default["container"],
482
+ "data-polly-ui": true,
483
+ "data-polly-modal-content": true,
484
+ "data-overlay-id": id,
485
+ "data-state": "open",
486
+ role: "dialog",
487
+ "aria-modal": "true",
488
+ "aria-labelledby": ariaLabel ? undefined : titleId,
489
+ "aria-label": ariaLabel,
490
+ "aria-describedby": descId,
491
+ children
492
+ }, undefined, false, undefined, this)
493
+ }, undefined, false, undefined, this);
494
+ return createPortal(content, portalNode);
495
+ }
496
+ function Backdrop() {
497
+ const { close } = useModal();
498
+ return /* @__PURE__ */ jsxDEV4("div", {
499
+ class: Modal_module_default["backdrop"],
500
+ "data-polly-modal-backdrop": true,
501
+ onClick: close,
502
+ "aria-hidden": "true"
503
+ }, undefined, false, undefined, this);
504
+ }
505
+ function Content({ children }) {
506
+ return /* @__PURE__ */ jsxDEV4("div", {
507
+ class: Modal_module_default["surface"],
508
+ "data-polly-modal-surface": true,
509
+ children
510
+ }, undefined, false, undefined, this);
511
+ }
512
+ function Header({ children }) {
513
+ return /* @__PURE__ */ jsxDEV4("header", {
514
+ class: Modal_module_default["header"],
515
+ "data-polly-modal-header": true,
516
+ children
517
+ }, undefined, false, undefined, this);
518
+ }
519
+ function Title({ children }) {
520
+ const { titleId } = useModal();
521
+ return /* @__PURE__ */ jsxDEV4("h2", {
522
+ id: titleId,
523
+ class: Modal_module_default["title"],
524
+ "data-polly-modal-title": true,
525
+ children
526
+ }, undefined, false, undefined, this);
527
+ }
528
+ function Body({ children }) {
529
+ const { descId } = useModal();
530
+ return /* @__PURE__ */ jsxDEV4("div", {
531
+ id: descId,
532
+ class: Modal_module_default["body"],
533
+ "data-polly-modal-body": true,
534
+ children
535
+ }, undefined, false, undefined, this);
536
+ }
537
+ function Footer({ children }) {
538
+ return /* @__PURE__ */ jsxDEV4("footer", {
539
+ class: Modal_module_default["footer"],
540
+ "data-polly-modal-footer": true,
541
+ children
542
+ }, undefined, false, undefined, this);
543
+ }
544
+ function Close({ children, action }) {
545
+ const { close } = useModal();
546
+ if (action) {
547
+ return /* @__PURE__ */ jsxDEV4("button", {
548
+ type: "button",
549
+ class: Modal_module_default["close"],
550
+ "data-polly-ui": true,
551
+ "data-polly-interactive": true,
552
+ "data-polly-modal-close": true,
553
+ "data-action": action,
554
+ children
555
+ }, undefined, false, undefined, this);
556
+ }
557
+ return /* @__PURE__ */ jsxDEV4("button", {
558
+ type: "button",
559
+ class: Modal_module_default["close"],
560
+ "data-polly-ui": true,
561
+ "data-polly-interactive": true,
562
+ "data-polly-modal-close": true,
563
+ onClick: close,
564
+ children
565
+ }, undefined, false, undefined, this);
566
+ }
567
+ var Modal = {
568
+ Root,
569
+ Backdrop,
570
+ Content,
571
+ Header,
572
+ Title,
573
+ Body,
574
+ Footer,
575
+ Close
576
+ };
577
+
578
+ // src/polly-ui/ConfirmDialog.tsx
579
+ import { jsxDEV as jsxDEV5 } from "preact/jsx-dev-runtime";
580
+ var pending = signal2(null);
581
+ var isOpen = signal2(false);
582
+ var nextId = 0;
583
+ function confirm(options) {
584
+ return new Promise((resolve) => {
585
+ nextId += 1;
586
+ pending.value = {
587
+ id: nextId,
588
+ title: options.title,
589
+ body: options.body,
590
+ danger: options.danger,
591
+ confirmLabel: options.confirmLabel,
592
+ cancelLabel: options.cancelLabel,
593
+ resolve
594
+ };
595
+ isOpen.value = true;
596
+ });
597
+ }
598
+ function close(result) {
599
+ const current = pending.value;
600
+ if (!current)
601
+ return;
602
+ isOpen.value = false;
603
+ pending.value = null;
604
+ current.resolve(result);
605
+ }
606
+ function Host() {
607
+ const current = pending.value;
608
+ if (!current)
609
+ return null;
610
+ return /* @__PURE__ */ jsxDEV5(Modal.Root, {
611
+ when: isOpen,
612
+ onClose: () => close(false),
613
+ children: [
614
+ /* @__PURE__ */ jsxDEV5(Modal.Backdrop, {}, undefined, false, undefined, this),
615
+ /* @__PURE__ */ jsxDEV5(Modal.Content, {
616
+ children: [
617
+ /* @__PURE__ */ jsxDEV5(Modal.Header, {
618
+ children: /* @__PURE__ */ jsxDEV5(Modal.Title, {
619
+ children: current.title
620
+ }, undefined, false, undefined, this)
621
+ }, undefined, false, undefined, this),
622
+ current.body ? /* @__PURE__ */ jsxDEV5(Modal.Body, {
623
+ children: current.body
624
+ }, undefined, false, undefined, this) : null,
625
+ /* @__PURE__ */ jsxDEV5(Modal.Footer, {
626
+ children: /* @__PURE__ */ jsxDEV5("div", {
627
+ class: ConfirmDialog_module_default["actions"],
628
+ "data-polly-confirm-actions": true,
629
+ children: [
630
+ /* @__PURE__ */ jsxDEV5("button", {
631
+ type: "button",
632
+ class: ConfirmDialog_module_default["cancel"],
633
+ "data-polly-ui": true,
634
+ "data-polly-interactive": true,
635
+ "data-polly-confirm-cancel": true,
636
+ onClick: () => close(false),
637
+ children: current.cancelLabel ?? "Cancel"
638
+ }, undefined, false, undefined, this),
639
+ /* @__PURE__ */ jsxDEV5("button", {
640
+ type: "button",
641
+ class: current.danger ? ConfirmDialog_module_default["confirmDanger"] : ConfirmDialog_module_default["confirm"],
642
+ "data-polly-ui": true,
643
+ "data-polly-interactive": true,
644
+ "data-polly-confirm-ok": true,
645
+ onClick: () => close(true),
646
+ children: current.confirmLabel ?? "OK"
647
+ }, undefined, false, undefined, this)
648
+ ]
649
+ }, undefined, true, undefined, this)
650
+ }, undefined, false, undefined, this)
651
+ ]
652
+ }, undefined, true, undefined, this)
653
+ ]
654
+ }, undefined, true, undefined, this);
655
+ }
656
+ var ConfirmDialog = { Host, confirm };
657
+ // src/polly-ui/Layout.tsx
658
+ import { createElement } from "preact";
659
+
660
+ // src/polly-ui/Layout.module.css
661
+ var Layout_module_default = {
662
+ layout: "layout_QgwWPg",
663
+ stackOnMobile: "stackOnMobile_QgwWPg"
664
+ };
665
+
666
+ // src/polly-ui/Layout.tsx
667
+ function Layout(props) {
668
+ const {
669
+ children,
670
+ as = "div",
671
+ rows,
672
+ columns,
673
+ contents,
674
+ subgrid,
675
+ gap,
676
+ padding,
677
+ height,
678
+ minHeight,
679
+ justifyItems,
680
+ alignItems,
681
+ justifyContent,
682
+ alignContent,
683
+ justifySelf,
684
+ alignSelf,
685
+ autoFlow,
686
+ autoRows,
687
+ autoColumns,
688
+ stackOnMobile,
689
+ className,
690
+ onClick,
691
+ onKeyDown,
692
+ role,
693
+ tabIndex,
694
+ id
695
+ } = props;
696
+ const style = {};
697
+ if (contents) {
698
+ style["display"] = "contents";
699
+ } else if (subgrid) {
700
+ style["--l-col"] = "1 / -1";
701
+ style["--l-cols"] = "subgrid";
702
+ if (padding)
703
+ style["--l-p"] = padding;
704
+ if (alignItems)
705
+ style["--l-ai"] = alignItems;
706
+ if (gap)
707
+ style["--l-gap"] = gap;
708
+ } else {
709
+ if (padding)
710
+ style["--l-p"] = padding;
711
+ if (height)
712
+ style["--l-h"] = height;
713
+ if (minHeight)
714
+ style["--l-mh"] = minHeight;
715
+ if (rows)
716
+ style["--l-rows"] = rows;
717
+ if (columns)
718
+ style["--l-cols"] = columns;
719
+ if (gap)
720
+ style["--l-gap"] = gap;
721
+ if (justifyItems)
722
+ style["--l-ji"] = justifyItems;
723
+ if (alignItems)
724
+ style["--l-ai"] = alignItems;
725
+ if (justifyContent)
726
+ style["--l-jc"] = justifyContent;
727
+ if (alignContent)
728
+ style["--l-ac"] = alignContent;
729
+ if (autoFlow)
730
+ style["--l-flow"] = autoFlow;
731
+ if (autoRows)
732
+ style["--l-arows"] = autoRows;
733
+ if (autoColumns)
734
+ style["--l-acols"] = autoColumns;
735
+ }
736
+ if (justifySelf)
737
+ style["--l-js"] = justifySelf;
738
+ if (alignSelf)
739
+ style["--l-as"] = alignSelf;
740
+ const baseClass = stackOnMobile ? `${Layout_module_default["layout"]} ${Layout_module_default["stackOnMobile"]}` : Layout_module_default["layout"];
741
+ const combined = contents ? className : className ? `${baseClass} ${className}` : baseClass;
742
+ return createElement(as, {
743
+ id,
744
+ className: combined,
745
+ style,
746
+ onClick,
747
+ onKeyDown,
748
+ role,
749
+ tabIndex,
750
+ "aria-label": props["aria-label"],
751
+ "aria-labelledby": props["aria-labelledby"],
752
+ "aria-describedby": props["aria-describedby"],
753
+ "data-polly-layout": true
754
+ }, children);
755
+ }
756
+ // src/polly-ui/internal/input-base.ts
757
+ function buildInputA11y(props) {
758
+ const attrs = {
759
+ "data-polly-ui": true,
760
+ "data-polly-input": true,
761
+ "aria-invalid": props.invalid ? "true" : undefined,
762
+ "aria-required": props.required ? "true" : undefined,
763
+ "aria-describedby": props.describedBy,
764
+ name: props.name,
765
+ id: props.id,
766
+ placeholder: props.placeholder,
767
+ disabled: props.disabled,
768
+ readOnly: props.readOnly
769
+ };
770
+ if (props.invalid)
771
+ attrs["data-state"] = "invalid";
772
+ return attrs;
773
+ }
774
+
775
+ // src/polly-ui/TextInput.module.css
776
+ var TextInput_module_default = {
777
+ input: "input_ez4_Vg"
778
+ };
779
+
780
+ // src/polly-ui/TextInput.tsx
781
+ import { jsxDEV as jsxDEV6 } from "preact/jsx-dev-runtime";
782
+ function isSignal(v) {
783
+ return typeof v === "object" && v !== null && "value" in v && "peek" in v;
784
+ }
785
+ function TextInput(props) {
786
+ const variant = props.variant ?? "single";
787
+ const a11y = buildInputA11y({
788
+ name: props.name,
789
+ id: props.id,
790
+ required: props.required,
791
+ invalid: props.invalid,
792
+ describedBy: props.describedBy,
793
+ placeholder: props.placeholder,
794
+ disabled: props.disabled,
795
+ readOnly: props.readOnly
796
+ });
797
+ const controlled = isSignal(props.value);
798
+ const stringValue = controlled ? props.value.value : undefined;
799
+ const defaultValue = controlled ? undefined : props.value ?? "";
800
+ const className = props.className ? `${TextInput_module_default["input"]} ${props.className}` : TextInput_module_default["input"];
801
+ if (variant === "multi") {
802
+ return /* @__PURE__ */ jsxDEV6("textarea", {
803
+ ...a11y,
804
+ class: className,
805
+ "data-polly-input-variant": "multi",
806
+ rows: props.rows,
807
+ autoFocus: props.autoFocus,
808
+ value: stringValue,
809
+ defaultValue,
810
+ onInput: (e) => {
811
+ if (controlled) {
812
+ props.value.value = e.currentTarget.value;
813
+ }
814
+ }
815
+ }, undefined, false, undefined, this);
816
+ }
817
+ return /* @__PURE__ */ jsxDEV6("input", {
818
+ ...a11y,
819
+ type: "text",
820
+ class: className,
821
+ "data-polly-input-variant": "single",
822
+ autoFocus: props.autoFocus,
823
+ value: stringValue,
824
+ defaultValue,
825
+ onInput: (e) => {
826
+ if (controlled) {
827
+ props.value.value = e.currentTarget.value;
828
+ }
829
+ }
830
+ }, undefined, false, undefined, this);
831
+ }
832
+ // src/polly-ui/Toast.tsx
833
+ import { createPortal as createPortal2 } from "preact/compat";
834
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "preact/hooks";
835
+
836
+ // src/actions/error.ts
837
+ import { signal as signal3 } from "@preact/signals";
838
+ var errorState = signal3([]);
839
+ var nextId2 = 0;
840
+ function allocId() {
841
+ nextId2 += 1;
842
+ return `polly-err-${nextId2}`;
843
+ }
844
+ function setError(message, opts = {}) {
845
+ const entry = {
846
+ id: allocId(),
847
+ message,
848
+ severity: opts.severity ?? "error",
849
+ action: opts.action,
850
+ createdAt: Date.now()
851
+ };
852
+ errorState.value = [...errorState.value, entry];
853
+ return entry.id;
854
+ }
855
+ function clearError(id) {
856
+ if (id === undefined) {
857
+ errorState.value = [];
858
+ return;
859
+ }
860
+ errorState.value = errorState.value.filter((e) => e.id !== id);
861
+ }
862
+ function submitError(action, err) {
863
+ const message = err instanceof Error ? err.message : String(err);
864
+ return setError(message, { action, severity: "error" });
865
+ }
866
+
867
+ // src/polly-ui/Toast.module.css
868
+ var Toast_module_default = {
869
+ viewport: "viewport_xVzRuA",
870
+ item: "item_xVzRuA",
871
+ message: "message_xVzRuA",
872
+ close: "close_xVzRuA"
873
+ };
874
+
875
+ // src/polly-ui/Toast.tsx
876
+ import { jsxDEV as jsxDEV7 } from "preact/jsx-dev-runtime";
877
+ function Viewport(props) {
878
+ const autoDismissMs = props.autoDismissMs ?? 5000;
879
+ const [portalNode, setPortalNode] = useState3(null);
880
+ const [paused, setPaused] = useState3(false);
881
+ const entries = errorState.value;
882
+ useEffect4(() => {
883
+ setPortalNode(getOverlayRootNode());
884
+ }, []);
885
+ useEffect4(() => {
886
+ if (paused || entries.length === 0)
887
+ return;
888
+ const head = entries[0];
889
+ if (!head)
890
+ return;
891
+ const timer = window.setTimeout(() => {
892
+ clearError(head.id);
893
+ }, autoDismissMs);
894
+ return () => window.clearTimeout(timer);
895
+ }, [paused, entries, autoDismissMs]);
896
+ if (!portalNode)
897
+ return null;
898
+ const content = /* @__PURE__ */ jsxDEV7("div", {
899
+ class: `${Toast_module_default["viewport"]} ${props.className ?? ""}`.trim(),
900
+ "data-polly-ui": true,
901
+ "data-polly-toast-viewport": true,
902
+ onMouseEnter: () => setPaused(true),
903
+ onMouseLeave: () => setPaused(false),
904
+ children: entries.map((entry) => /* @__PURE__ */ jsxDEV7(ToastItem, {
905
+ entry
906
+ }, entry.id, false, undefined, this))
907
+ }, undefined, false, undefined, this);
908
+ return createPortal2(content, portalNode);
909
+ }
910
+ function ToastItem({ entry }) {
911
+ const liveness = entry.severity === "error" ? "assertive" : "polite";
912
+ const ref = useRef4(null);
913
+ return /* @__PURE__ */ jsxDEV7("div", {
914
+ ref,
915
+ class: Toast_module_default["item"],
916
+ "data-polly-ui": true,
917
+ "data-polly-toast-item": true,
918
+ "data-severity": entry.severity,
919
+ role: entry.severity === "error" ? "alert" : "status",
920
+ "aria-live": liveness,
921
+ children: [
922
+ /* @__PURE__ */ jsxDEV7("span", {
923
+ class: Toast_module_default["message"],
924
+ children: entry.message
925
+ }, undefined, false, undefined, this),
926
+ /* @__PURE__ */ jsxDEV7("button", {
927
+ type: "button",
928
+ class: Toast_module_default["close"],
929
+ "data-polly-ui": true,
930
+ "data-polly-interactive": true,
931
+ "data-polly-toast-close": true,
932
+ onClick: () => clearError(entry.id),
933
+ "aria-label": "Dismiss",
934
+ children: "×"
935
+ }, undefined, false, undefined, this)
936
+ ]
937
+ }, undefined, true, undefined, this);
938
+ }
939
+ var Toast = { Viewport };
940
+ export {
941
+ getOverlayRootNode,
942
+ confirm,
943
+ Toast,
944
+ TextInput,
945
+ OverlayRoot,
946
+ Modal,
947
+ Layout,
948
+ ConfirmDialog,
949
+ ActionInput,
950
+ ActionForm
951
+ };
952
+
953
+ //# debugId=17892EC9C7CDCA6D64756E2164756E21