@pageloop/client 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 (70) hide show
  1. package/LICENSE +94 -0
  2. package/README.md +97 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/.tsbuildinfo.preact +1 -0
  5. package/dist/.tsbuildinfo.react +1 -0
  6. package/dist/.tsbuildinfo.solid +1 -0
  7. package/dist/ApiClient.d.ts +121 -0
  8. package/dist/ApiClient.d.ts.map +1 -0
  9. package/dist/ApiClient.js +512 -0
  10. package/dist/ApiClient.js.map +1 -0
  11. package/dist/CommentEngine.d.ts +111 -0
  12. package/dist/CommentEngine.d.ts.map +1 -0
  13. package/dist/CommentEngine.js +277 -0
  14. package/dist/CommentEngine.js.map +1 -0
  15. package/dist/EventBus.d.ts +122 -0
  16. package/dist/EventBus.d.ts.map +1 -0
  17. package/dist/EventBus.js +34 -0
  18. package/dist/EventBus.js.map +1 -0
  19. package/dist/IdbCache.d.ts +22 -0
  20. package/dist/IdbCache.d.ts.map +1 -0
  21. package/dist/IdbCache.js +79 -0
  22. package/dist/IdbCache.js.map +1 -0
  23. package/dist/PageLoop.d.ts +424 -0
  24. package/dist/PageLoop.d.ts.map +1 -0
  25. package/dist/PageLoop.js +1092 -0
  26. package/dist/PageLoop.js.map +1 -0
  27. package/dist/PageTracker.d.ts +32 -0
  28. package/dist/PageTracker.d.ts.map +1 -0
  29. package/dist/PageTracker.js +105 -0
  30. package/dist/PageTracker.js.map +1 -0
  31. package/dist/UIRenderer.d.ts +218 -0
  32. package/dist/UIRenderer.d.ts.map +1 -0
  33. package/dist/UIRenderer.js +2 -0
  34. package/dist/UIRenderer.js.map +1 -0
  35. package/dist/auth/resolveInitialToken.d.ts +49 -0
  36. package/dist/auth/resolveInitialToken.d.ts.map +1 -0
  37. package/dist/auth/resolveInitialToken.js +97 -0
  38. package/dist/auth/resolveInitialToken.js.map +1 -0
  39. package/dist/errorCode.d.ts +12 -0
  40. package/dist/errorCode.d.ts.map +1 -0
  41. package/dist/errorCode.js +21 -0
  42. package/dist/errorCode.js.map +1 -0
  43. package/dist/index.d.ts +20 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +18 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/notifications/BrowserNotifications.d.ts +68 -0
  48. package/dist/notifications/BrowserNotifications.d.ts.map +1 -0
  49. package/dist/notifications/BrowserNotifications.js +147 -0
  50. package/dist/notifications/BrowserNotifications.js.map +1 -0
  51. package/dist/preact/index.d.ts +37 -0
  52. package/dist/preact/index.d.ts.map +1 -0
  53. package/dist/preact/index.js +150 -0
  54. package/dist/preact/index.js.map +1 -0
  55. package/dist/preact/style.css +12 -0
  56. package/dist/react/index.d.ts +52 -0
  57. package/dist/react/index.d.ts.map +1 -0
  58. package/dist/react/index.js +165 -0
  59. package/dist/react/index.js.map +1 -0
  60. package/dist/react/style.css +12 -0
  61. package/dist/safeStorage.d.ts +26 -0
  62. package/dist/safeStorage.d.ts.map +1 -0
  63. package/dist/safeStorage.js +78 -0
  64. package/dist/safeStorage.js.map +1 -0
  65. package/dist/solid/index.d.ts +40 -0
  66. package/dist/solid/index.d.ts.map +1 -0
  67. package/dist/solid/index.jsx +134 -0
  68. package/dist/solid/index.jsx.map +1 -0
  69. package/dist/solid/style.css +12 -0
  70. package/package.json +85 -0
@@ -0,0 +1,424 @@
1
+ import type { Project, TrackedPage, User, ProjectThemeId } from '@pageloop/shared';
2
+ import { ApiClient } from './ApiClient.js';
3
+ import { CommentEngine } from './CommentEngine.js';
4
+ import { PageTracker } from './PageTracker.js';
5
+ import { EventBus } from './EventBus.js';
6
+ import { IdbCache } from './IdbCache.js';
7
+ import type { UIRenderer } from './UIRenderer.js';
8
+ import { BrowserNotifications } from './notifications/BrowserNotifications.js';
9
+ export interface PageLoopConfig {
10
+ endpoint: string;
11
+ projectId: string;
12
+ /** Initial token, if known (e.g. from a host-app session). */
13
+ token?: string;
14
+ transport?: 'auto' | 'ws' | 'sse' | 'rest';
15
+ /**
16
+ * Auto-register the current URL as a tracked page on first visit if no
17
+ * matching tracked page exists. Default: true. The user must have the
18
+ * `editor` or `owner` role for the project to register pages.
19
+ */
20
+ autoDiscover?: boolean;
21
+ /**
22
+ * Verbose console logging on every lifecycle step. Useful when figuring
23
+ * out why a comment didn't post or a connection didn't open. Default false.
24
+ */
25
+ debug?: boolean;
26
+ /**
27
+ * Version string of the running bundle (CR-7). The auto-update loader
28
+ * threads the booted manifest version through here. When it's newer
29
+ * than the last version the user saw (tracked in localStorage), the
30
+ * toolbar shows a one-time "What's new" pill linking
31
+ * {@link releaseNotesUrl}. Silent when unset — embedders that don't
32
+ * use the loader pass nothing and never see the pill.
33
+ */
34
+ version?: string;
35
+ /** URL of the release notes for {@link version} (CR-7). The "What's
36
+ * new" pill links here; the pill is suppressed when this is unset. */
37
+ releaseNotesUrl?: string;
38
+ /**
39
+ * UI options that don't fit on the rendered surfaces themselves —
40
+ * positioning, theming, locale, etc. Renderers read these from
41
+ * `UIRendererDeps.config()` (see UIRenderer).
42
+ */
43
+ ui?: {
44
+ /**
45
+ * Where the toolbar mounts. `top` (default) sticks it to the top of
46
+ * the viewport; `bottom` sticks it to the bottom. Use `bottom` when
47
+ * the host page already has a fixed top header that PageLoop can't
48
+ * push down. Only consulted when `widgetMode === 'docked'`.
49
+ */
50
+ toolbarPosition?: 'top' | 'bottom';
51
+ /** Widget display mode. `'docked'` (default) renders the full
52
+ * toolbar pinned to a viewport edge; `'mini'` renders a
53
+ * corner bubble that expands on click into an action row. */
54
+ widgetMode?: 'docked' | 'mini';
55
+ /** Corner the mini widget docks to. Ignored when
56
+ * `widgetMode !== 'mini'`. Default `'lower-right'`. */
57
+ widgetCorner?: 'lower-right' | 'lower-left' | 'upper-right' | 'upper-left';
58
+ /** Where the sidebar mounts and slides in from. Default `right`.
59
+ * Vertical positions (top / bottom) make the sidebar a horizontal
60
+ * strip; useful with `sidebarDisplayMode: 'in-page'`. */
61
+ sidebarPosition?: 'left' | 'right' | 'top' | 'bottom';
62
+ /** @deprecated — kept so existing snippets / saved server settings
63
+ * keep working. New code reads `sidebarPosition`; this is mapped
64
+ * through when `sidebarPosition` is unset. */
65
+ sidebarSide?: 'left' | 'right';
66
+ /** Default sidebar mode. `flyout` overlays the page; `in-page`
67
+ * pushes host content via flex-layout on <body>. User toggle
68
+ * stored in localStorage takes precedence. */
69
+ sidebarDisplayMode?: 'flyout' | 'in-page';
70
+ theme?: ProjectThemeId;
71
+ /** Show a reply input inside the hover/click comment-tooltip. When
72
+ * on, clicking a bubble keeps the lightweight tooltip instead of
73
+ * jumping into the sidebar; the user opens the full sidebar
74
+ * manually from the toolbar or a "more" affordance in the
75
+ * tooltip header. */
76
+ allowInlineCommenting?: boolean;
77
+ /** Hover delay before the comment-tooltip opens, in ms. 0 = instant.
78
+ * Clamped to [0, 2000]. Default 200ms. Click is always instant. */
79
+ tooltipDelayMs?: number;
80
+ /** Continuously track anchor element positions so bubbles
81
+ * follow their commented content through animations + layout
82
+ * shifts. Default false. */
83
+ bubbleFollowAnimations?: boolean;
84
+ /** Base CSS `z-index` for comment bubbles. When set, overrides
85
+ * the widget's `--pl-z-bubbles` CSS variable so the bubble
86
+ * layer sits at exactly this stacking level. Floating
87
+ * (hovered) bubbles get `value + 1`. Toolbar / sidebar /
88
+ * popovers stack independently. Use this when the host page
89
+ * has its own modals or overlays that should appear above
90
+ * (or below) the bubbles. Default `2147483640` (just below
91
+ * int32-max). */
92
+ bubbleZIndex?: number;
93
+ /** OAuth sign-in transport — see UIRenderer.ts for the full
94
+ * doc. `'auto'` picks redirect when same-origin, popup when
95
+ * cross-origin. Default `'auto'`. */
96
+ signInMode?: 'auto' | 'popup' | 'redirect';
97
+ };
98
+ }
99
+ export interface PageLoopDeps {
100
+ renderer: UIRenderer;
101
+ }
102
+ /**
103
+ * Public entry point. Composes ApiClient + PageTracker + CommentEngine + the
104
+ * provided UIRenderer, and runs the bootstrap → mount → subscribe lifecycle.
105
+ *
106
+ * Each framework wrapper (vanilla, react, preact, solid) instantiates this
107
+ * class internally and wires its own renderer into `deps.renderer`.
108
+ */
109
+ export declare class PageLoop {
110
+ private readonly config;
111
+ readonly bus: EventBus;
112
+ readonly api: ApiClient;
113
+ readonly tracker: PageTracker;
114
+ readonly comments: CommentEngine;
115
+ readonly cache: IdbCache;
116
+ /** OS-level notification surface — listens for server-pushed
117
+ * `notification.show` events and surfaces them via the standard
118
+ * Web Notification API when the user has granted permission.
119
+ * Permission is requested explicitly from the settings UI; this
120
+ * manager never prompts silently. */
121
+ readonly notifications: BrowserNotifications;
122
+ private readonly renderer;
123
+ private project;
124
+ private currentPage;
125
+ /** Currently authenticated user, populated from bootstrap. */
126
+ private currentUser;
127
+ /** Current user's role in the project; bootstrap doesn't return this directly,
128
+ * so we infer from `user.platformRole === 'superadmin'` || comment-create
129
+ * succeeded. For v1 we ask the server explicitly only when needed; the
130
+ * conservative default for new users is 'commenter' (most common case). */
131
+ private currentRole;
132
+ /** Signed-in user's preferred bubble colour (`#rrggbb`), resolved by the
133
+ * server at bootstrap from their account `appearance` settings. Null when
134
+ * anonymous or unset. Surfaced to the renderer via the `bubbleColor()`
135
+ * dep + kept fresh when the user recolours from the widget settings
136
+ * panel (the `bubbles:recolor` bus subscription below). */
137
+ private currentBubbleColor;
138
+ /** Wall-clock of the last successful bootstrap — throttles the
139
+ * focus-driven session auto-refresh (`onVisible`). */
140
+ private lastBootstrapAt;
141
+ /** Guard so the visibilitychange listener is wired exactly once. */
142
+ private visibilityWired;
143
+ /** Phase 2 subscriptions — wired by `runPhase2()`, dropped by
144
+ * `tearDownPhase2()`. The always-on `projects.updated` listener
145
+ * lives separately on `offProjectUpdated` so it survives transitions. */
146
+ private offHandlers;
147
+ /** Always-on subscription to `projects.updated`. Survives phase 2
148
+ * teardown so a disabled-then-enabled cycle can wake back up. */
149
+ private offProjectUpdated;
150
+ /** Always-on subscription to `bubbles:recolor` (widget settings panel).
151
+ * Keeps `currentBubbleColor` in sync so the renderer's `bubbleColor()`
152
+ * dep — and thus the colour picker on reopen — reflects the live value
153
+ * rather than the stale bootstrap one. */
154
+ private offBubbleRecolor;
155
+ /**
156
+ * Tracks whether the user-facing widget (Phase 2) is currently
157
+ * mounted. The master kill-switch `behavior.enabled` (default true)
158
+ * gates this — when false, Phase 2 stays down: no renderer, no
159
+ * WebSocket, no event subscriptions, no DOM listeners on the host
160
+ * page. Phase 1 (bootstrap + cache reconcile) still runs so a
161
+ * subsequent flip-on via `config:changed` can transition us up
162
+ * without a page reload.
163
+ */
164
+ private phase2Active;
165
+ /**
166
+ * Set by `dismiss()` — the user hid all PageLoop UI (e.g. for a
167
+ * screenshot). Keeps `reEvaluatePhase2()` from silently re-mounting on
168
+ * the next settings reconcile / tab-refocus. Not persisted, so a real
169
+ * page reload starts fresh with the widget visible again.
170
+ */
171
+ private dismissed;
172
+ /** Captured from the renderer's `mount` return so deep-link
173
+ * handlers (`?pl-comment=<id>`) can call `focusComment` after the
174
+ * sidebar is up. Stays null when the renderer hasn't mounted yet
175
+ * (e.g. during a stop/restart). */
176
+ private sidebarHandle;
177
+ /** Same idea for the toolbar handle — needed so refreshBubbles()
178
+ * can push the live comment count to the docked toolbar / mini
179
+ * widget bubble. Was previously discarded from the mount return,
180
+ * which is why the mini bubble's centered count stuck at 0. */
181
+ private toolbarHandle;
182
+ /** Latest set of comment ids whose anchor couldn't resolve in the
183
+ * current DOM. Updated by the `bubbles:dangling` event the bubble
184
+ * layer emits at the end of every repaint. Threaded into
185
+ * SidebarState on the next refreshSidebar() so the comments tab
186
+ * can mark those rows as "no anchor". */
187
+ private danglingCommentIds;
188
+ /**
189
+ * Server-provided settings, hydrated synchronously from localStorage on
190
+ * boot and revalidated by bootstrap. Snippet config still wins per-key
191
+ * via `effectiveUi()` — this is just the fallback set.
192
+ */
193
+ private serverSettings;
194
+ constructor(config: PageLoopConfig, deps: PageLoopDeps);
195
+ /**
196
+ * Effective UI config. Server wins per-key — admin is the source of
197
+ * truth for these knobs. Snippet values act as initial fallbacks for
198
+ * the synchronous first paint (before bootstrap returns); once
199
+ * reconciled, the server values overwrite. Identity / security keys
200
+ * (endpoint, projectId, token, debug) are snippet-only and live on
201
+ * `this.config` directly — they're never reachable from here.
202
+ */
203
+ private effectiveUi;
204
+ /** Same precedence rule for behaviour knobs — server wins. */
205
+ private effectiveBehavior;
206
+ /**
207
+ * Master kill-switch reader. Defaults to true when the admin has
208
+ * never set the value (backward compat — existing projects keep
209
+ * working). Phase-2 mount is gated on this; on a live flip via
210
+ * `config:changed` the same getter drives the up/down transition.
211
+ */
212
+ private isWidgetEnabled;
213
+ /**
214
+ * Reconcile server settings into the cache + emit `config:changed` if
215
+ * any visible knob actually moved. Called once after bootstrap; cheap
216
+ * enough to JSON.stringify-compare since the blob is tiny.
217
+ */
218
+ private reconcileSettings;
219
+ /**
220
+ * Mount the renderer with the standard dep-getter set. Factored out
221
+ * of `start()` so both the optimistic-paint path (cached project,
222
+ * before bootstrap) and the slow path (no cache, after bootstrap)
223
+ * share one mount call site. Captures the sidebar handle + replays
224
+ * the connection state so the toolbar's "online" badge picks up
225
+ * any already-completed `api.connect()` resolution.
226
+ */
227
+ private mountRenderer;
228
+ start(): Promise<void>;
229
+ /**
230
+ * Phase 2 — the user-facing widget. Mounts the renderer, opens the
231
+ * WebSocket, starts the page tracker, and subscribes the bus to
232
+ * comment/page events. Idempotent: subsequent calls while
233
+ * `phase2Active === true` are no-ops.
234
+ *
235
+ * Kept separate from `start()` so the master kill-switch
236
+ * (`behavior.enabled`) can gate this whole surface without
237
+ * threading branches through every step. Cold-boot enters here
238
+ * once when enabled; live admin flips re-enter via
239
+ * `reEvaluatePhase2()`.
240
+ */
241
+ private runPhase2;
242
+ /**
243
+ * Phase 2 teardown — symmetric inverse of `runPhase2()`. Unmounts
244
+ * the renderer, stops the tracker, disconnects the WS, drops every
245
+ * Phase 2 subscription. The always-on `projects.updated` listener
246
+ * lives on `offProjectUpdated` and is untouched here so it can
247
+ * wake us back up when the admin flips the kill-switch on.
248
+ */
249
+ private tearDownPhase2;
250
+ /**
251
+ * Remove ALL PageLoop UI from the page — toolbar/mini, sidebar, bubbles,
252
+ * tooltips, chat, toasts — and disconnect the socket, until the next page
253
+ * load. Nothing is persisted: a refresh re-initializes the widget
254
+ * normally. Intended for screenshots / temporarily clearing the page; the
255
+ * toolbar + mini both expose an "X" that calls this. Idempotent.
256
+ */
257
+ dismiss(): Promise<void>;
258
+ /**
259
+ * Drive Phase 2 to whatever state the latest effective
260
+ * `behavior.enabled` value implies. Called after every
261
+ * reconcileSettings so a live admin flip transitions us up or
262
+ * down without a page reload.
263
+ */
264
+ private reEvaluatePhase2;
265
+ stop(): Promise<void>;
266
+ getCurrentPage(): TrackedPage | null;
267
+ /** Project this PageLoop instance is bound to (null until bootstrap). */
268
+ getProject(): Project | null;
269
+ /** Authenticated user — null when anonymous. Updated after bootstrap. */
270
+ getCurrentUser(): User | null;
271
+ /** Caller's role in the project — `'anonymous'` when not signed in. */
272
+ getCurrentRole(): 'owner' | 'editor' | 'commenter' | 'viewer' | 'anonymous';
273
+ /**
274
+ * Apply a freshly-obtained session token (sign-in modal, email/password
275
+ * login, etc.) WITHOUT reloading the host page: set the bearer, persist
276
+ * it, then re-resolve identity + comments via bootstrap and repaint.
277
+ * Emits `auth:changed` so the toolbar/mini swap to the signed-in state.
278
+ * Pass null to sign out. Because the server's JWT is identity-only
279
+ * (`{sub}`) and resolves org/role per-request from the DB, this also
280
+ * picks up any membership/role change the user has accrued since their
281
+ * last bootstrap — no special token-refresh dance required.
282
+ */
283
+ applyToken(token: string | null): Promise<void>;
284
+ /**
285
+ * Re-run bootstrap under the CURRENT token and fold the result into live
286
+ * state (identity, role, project settings, comments) without remounting.
287
+ * Powers both `applyToken` (after a sign-in) and the focus-driven
288
+ * auto-refresh below. Because the JWT is identity-only and the server
289
+ * resolves org/role per-request, this is also how a user picks up a
290
+ * membership/role change (e.g. an admin adding them to an org) — no token
291
+ * reissue needed; the next bootstrap simply resolves the new permissions.
292
+ */
293
+ private reBootstrap;
294
+ /**
295
+ * Tab-focus auto-refresh. When a signed-in user returns to the tab and
296
+ * it's been a while since the last bootstrap, re-resolve their session so
297
+ * any org/role change an admin made while they were away (e.g. adding a
298
+ * just-registered user to an organization) is picked up without a manual
299
+ * reload. Throttled so routine tab-switching doesn't spam the server.
300
+ */
301
+ private onVisible;
302
+ /** Auto-register the current URL as a tracked page (for autoDiscover mode). */
303
+ registerCurrentPage(title?: string): Promise<TrackedPage>;
304
+ private resolvePage;
305
+ private debug;
306
+ /**
307
+ * Schedule non-critical work for after the host page has settled.
308
+ * Uses `requestIdleCallback` where available (most modern browsers
309
+ * outside Safari pre-17), `setTimeout(0)` as a fallback. The
310
+ * timeout cap ensures the callback eventually runs even if the
311
+ * host stays busy — we don't want widget features to silently
312
+ * never activate.
313
+ */
314
+ private scheduleIdle;
315
+ /**
316
+ * Consume `?pl-comment=<id>` (and optional `?pl-reply=<id>`) on
317
+ * the current URL: open the sidebar focused on that comment +
318
+ * animate the bubble. When `pl-reply` is also present, expand
319
+ * the comment's replies thread and scroll to the specific reply.
320
+ * Strips the params from the URL on success so a refresh doesn't
321
+ * re-focus.
322
+ *
323
+ * No-op when params are missing, the renderer isn't mounted, or
324
+ * the comment id isn't part of the currently-loaded page's set —
325
+ * the latter case means a stale deep-link; we silently drop it
326
+ * rather than confuse the user with an error toast.
327
+ */
328
+ private maybeFocusFromUrl;
329
+ /**
330
+ * Programmatic entry point matching the URL deep-link semantics.
331
+ * Hosts that own their own routing can call this instead of (or
332
+ * after) appending `?pl-comment=<id>` to the URL — same end-state:
333
+ * sidebar opens, comment scrolls into view, bubble flashes. When
334
+ * `replyId` is set, also expands the replies thread + scrolls.
335
+ *
336
+ * No-op until `start()` resolves and the renderer is mounted.
337
+ */
338
+ focusComment(commentId: string, replyId?: string): void;
339
+ /**
340
+ * Build a same-page deep-link URL pointing at a specific comment
341
+ * (and optionally a reply within it). Hosts use this to construct
342
+ * notification links / "permalink" buttons / cross-page navigation
343
+ * targets without re-deriving the param convention each time.
344
+ *
345
+ * Returns a same-origin URL with the existing path/hash preserved
346
+ * and `pl-comment` / `pl-reply` query params appended.
347
+ */
348
+ static buildDeepLink(input: {
349
+ /** Base URL (absolute or path-only). Default: current location
350
+ * when called in a browser; throws on the server. */
351
+ url?: string;
352
+ commentId: string;
353
+ replyId?: string;
354
+ }): string;
355
+ private refreshAfterChange;
356
+ private refreshSidebar;
357
+ private refreshBubbles;
358
+ /**
359
+ * Public hook for hosts that have just performed a layout change
360
+ * the widget can't observe on its own (SPA route swap, accordion
361
+ * open, a custom width-toggle on an admin shell, anything that
362
+ * shifts anchored elements without firing scroll/resize/CSS-
363
+ * transition events). Re-runs the bubble positioning routine
364
+ * without rebuilding the bubble DOM. Safe to call before mount
365
+ * — no-op until the renderer is up.
366
+ *
367
+ * Background work the widget runs anyway:
368
+ * - A `ResizeObserver` on `<body>` + a 3s baseline poll catch
369
+ * most cases without needing this call.
370
+ * - The `bubbleFollowAnimations` project setting layers a rAF
371
+ * loop on top during CSS transitions for sub-frame precision.
372
+ * This API is for the cases neither of the above covers — when
373
+ * you definitively know a layout shift just happened.
374
+ */
375
+ reflowBubbles(): void;
376
+ /**
377
+ * Re-resolve every bubble's anchor against the current DOM and
378
+ * re-render. Use this AFTER a page-level content change (saved
379
+ * edit, in-place rewrite, etc.) where the cached anchor ranges
380
+ * are now stale — `reflowBubbles()` only re-measures positions of
381
+ * the EXISTING anchors and would leave them clustered at wherever
382
+ * the disappeared element used to be.
383
+ *
384
+ * Pushes the current comment list back through the renderer's
385
+ * `renderBubbles` + `renderSidebar` pipeline; the bubble layer's
386
+ * repaint re-runs anchor resolution from scratch and emits the
387
+ * `bubbles:dangling` event so the sidebar can mark unresolved
388
+ * comments.
389
+ */
390
+ repositionBubbles(): void;
391
+ /**
392
+ * Programmatically hide or show the bubble overlay. Orthogonal to
393
+ * the user's toolbar toggle — both have to be "shown" for bubbles
394
+ * to appear. Use this when the host puts the page into a state
395
+ * where anchors can't resolve (admin's raw textarea editor mode,
396
+ * a lazy viewer hydrating), then pair the un-hide with
397
+ * `repositionBubbles()` so the layer reseats against the new DOM.
398
+ *
399
+ * `animationMs` overrides the fade duration for this transition
400
+ * (default 150ms). Pass `0` to hide/show instantly — handy when
401
+ * the host page is mid-animation and a cross-fade would visually
402
+ * compete (e.g. width toggles that re-flow anchored content).
403
+ *
404
+ * No-op for renderers that don't implement an overlay layer.
405
+ */
406
+ setBubblesHidden(hidden: boolean, animationMs?: number): void;
407
+ /**
408
+ * Show a transient notification in the bottom-right corner.
409
+ * Stacks with other in-flight messages and auto-dismisses after
410
+ * `durationMs` (default 2000; pass 0 to pin until the user
411
+ * clicks). `kind` colors the left-edge stripe (info/success/error).
412
+ *
413
+ * The underlying surface is the renderer's Toaster, driven by the
414
+ * `toast:show` bus channel. This method is the canonical entry
415
+ * point for widget code that wants to fire a notification; both
416
+ * vanilla calls and framework-wrapper consumers reach the same
417
+ * stack.
418
+ */
419
+ notify(message: string, opts?: {
420
+ kind?: 'info' | 'success' | 'error';
421
+ durationMs?: number;
422
+ }): void;
423
+ }
424
+ //# sourceMappingURL=PageLoop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageLoop.d.ts","sourceRoot":"","sources":["../src/PageLoop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,OAAO,EAEP,WAAW,EAEX,IAAI,EACJ,cAAc,EACd,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAG/E,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC;IAC3C;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;2EACuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,EAAE,CAAC,EAAE;QACJ;;;;;WAKG;QACH,eAAe,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;QACnC;;sEAE8D;QAC9D,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;QAC/B;gEACwD;QACxD,YAAY,CAAC,EAAE,aAAa,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC;QAC3E;;kEAE0D;QAC1D,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACtD;;uDAE+C;QAC/C,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAC/B;;uDAE+C;QAC/C,kBAAkB,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;QAC1C,KAAK,CAAC,EAAE,cAAc,CAAC;QACvB;;;;8BAIsB;QACtB,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC;4EACoE;QACpE,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;qCAE6B;QAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;QACjC;;;;;;;0BAOkB;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB;;8CAEsC;QACtC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;KAC3C,CAAC;CACF;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,UAAU,CAAC;CACrB;AA+GD;;;;;;GAMG;AACH,qBAAa,QAAQ;IAuFnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAtFxB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB;;;;0CAIsC;IACtC,QAAQ,CAAC,aAAa,EAAE,oBAAoB,CAAC;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IAEtC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,WAAW,CAA4B;IAC/C,8DAA8D;IAC9D,OAAO,CAAC,WAAW,CAAqB;IACxC;;;gFAG4E;IAC5E,OAAO,CAAC,WAAW,CAA0E;IAC7F;;;;gEAI4D;IAC5D,OAAO,CAAC,kBAAkB,CAAuB;IACjD;2DACuD;IACvD,OAAO,CAAC,eAAe,CAAK;IAC5B,oEAAoE;IACpE,OAAO,CAAC,eAAe,CAAS;IAChC;;8EAE0E;IAC1E,OAAO,CAAC,WAAW,CAAyB;IAC5C;sEACkE;IAClE,OAAO,CAAC,iBAAiB,CAA6B;IACtD;;;+CAG2C;IAC3C,OAAO,CAAC,gBAAgB,CAA6B;IACrD;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY,CAAS;IAC7B;;;;;OAKG;IACH,OAAO,CAAC,SAAS,CAAS;IAC1B;;;wCAGoC;IACpC,OAAO,CAAC,aAAa,CAAwD;IAC7E;;;oEAGgE;IAChE,OAAO,CAAC,aAAa,CAAwD;IAC7E;;;;8CAI0C;IAC1C,OAAO,CAAC,kBAAkB,CAAkC;IAC5D;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAkB;gBAGtB,MAAM,EAAE,cAAc,EACvC,IAAI,EAAE,YAAY;IAqDnB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IA8BnB,8DAA8D;IAC9D,OAAO,CAAC,iBAAiB;IASzB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAIvB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;;;;;;OAOG;YACW,aAAa;IAoCrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsN5B;;;;;;;;;;;OAWG;YACW,SAAS;IAoFvB;;;;;;OAMG;YACW,cAAc;IAa5B;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B;;;;;OAKG;YACW,gBAAgB;IAWxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,cAAc,IAAI,WAAW,GAAG,IAAI;IAIpC,yEAAyE;IACzE,UAAU,IAAI,OAAO,GAAG,IAAI;IAI5B,yEAAyE;IACzE,cAAc,IAAI,IAAI,GAAG,IAAI;IAI7B,uEAAuE;IACvE,cAAc,IAAI,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW;IAI3E;;;;;;;;;OASG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrD;;;;;;;;OAQG;YACW,WAAW;IAwCzB;;;;;;OAMG;IACH,OAAO,CAAC,SAAS,CAKf;IAEF,+EAA+E;IACzE,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;YAoBjD,WAAW;IAiCzB,OAAO,CAAC,KAAK;IAIb;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAYpB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,iBAAiB;IAgCzB;;;;;;;;OAQG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAUvD;;;;;;;;OAQG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE;QAC3B;8DACsD;QACtD,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,MAAM;IAoBV,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,cAAc;IAgBtB;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,IAAI,IAAI;IAIrB;;;;;;;;;;;;;OAaG;IACH,iBAAiB,IAAI,IAAI;IAKzB;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAI7D;;;;;;;;;;;OAWG;IACH,MAAM,CACL,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GACjE,IAAI;CAOP"}