@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.
- package/LICENSE +94 -0
- package/README.md +97 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/.tsbuildinfo.preact +1 -0
- package/dist/.tsbuildinfo.react +1 -0
- package/dist/.tsbuildinfo.solid +1 -0
- package/dist/ApiClient.d.ts +121 -0
- package/dist/ApiClient.d.ts.map +1 -0
- package/dist/ApiClient.js +512 -0
- package/dist/ApiClient.js.map +1 -0
- package/dist/CommentEngine.d.ts +111 -0
- package/dist/CommentEngine.d.ts.map +1 -0
- package/dist/CommentEngine.js +277 -0
- package/dist/CommentEngine.js.map +1 -0
- package/dist/EventBus.d.ts +122 -0
- package/dist/EventBus.d.ts.map +1 -0
- package/dist/EventBus.js +34 -0
- package/dist/EventBus.js.map +1 -0
- package/dist/IdbCache.d.ts +22 -0
- package/dist/IdbCache.d.ts.map +1 -0
- package/dist/IdbCache.js +79 -0
- package/dist/IdbCache.js.map +1 -0
- package/dist/PageLoop.d.ts +424 -0
- package/dist/PageLoop.d.ts.map +1 -0
- package/dist/PageLoop.js +1092 -0
- package/dist/PageLoop.js.map +1 -0
- package/dist/PageTracker.d.ts +32 -0
- package/dist/PageTracker.d.ts.map +1 -0
- package/dist/PageTracker.js +105 -0
- package/dist/PageTracker.js.map +1 -0
- package/dist/UIRenderer.d.ts +218 -0
- package/dist/UIRenderer.d.ts.map +1 -0
- package/dist/UIRenderer.js +2 -0
- package/dist/UIRenderer.js.map +1 -0
- package/dist/auth/resolveInitialToken.d.ts +49 -0
- package/dist/auth/resolveInitialToken.d.ts.map +1 -0
- package/dist/auth/resolveInitialToken.js +97 -0
- package/dist/auth/resolveInitialToken.js.map +1 -0
- package/dist/errorCode.d.ts +12 -0
- package/dist/errorCode.d.ts.map +1 -0
- package/dist/errorCode.js +21 -0
- package/dist/errorCode.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/BrowserNotifications.d.ts +68 -0
- package/dist/notifications/BrowserNotifications.d.ts.map +1 -0
- package/dist/notifications/BrowserNotifications.js +147 -0
- package/dist/notifications/BrowserNotifications.js.map +1 -0
- package/dist/preact/index.d.ts +37 -0
- package/dist/preact/index.d.ts.map +1 -0
- package/dist/preact/index.js +150 -0
- package/dist/preact/index.js.map +1 -0
- package/dist/preact/style.css +12 -0
- package/dist/react/index.d.ts +52 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +165 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/style.css +12 -0
- package/dist/safeStorage.d.ts +26 -0
- package/dist/safeStorage.d.ts.map +1 -0
- package/dist/safeStorage.js +78 -0
- package/dist/safeStorage.js.map +1 -0
- package/dist/solid/index.d.ts +40 -0
- package/dist/solid/index.d.ts.map +1 -0
- package/dist/solid/index.jsx +134 -0
- package/dist/solid/index.jsx.map +1 -0
- package/dist/solid/style.css +12 -0
- 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":"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"}
|