@lightworkai.official/debug-capture 0.19.10 → 0.19.12
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/README.md +5 -2
- package/dist/debug-capture.js +17 -17
- package/dist/index.mjs +22 -22
- package/dist/live.d.ts +12 -0
- package/dist/ui/bubblePlace.d.ts +6 -1
- package/dist/ui/chatBubble.d.ts +12 -1
- package/package.json +1 -1
- package/src/live.ts +24 -6
- package/src/ui/bubblePlace.ts +6 -1
- package/src/ui/chatBubble.ts +41 -11
package/dist/live.d.ts
CHANGED
|
@@ -9,6 +9,18 @@ export interface RingingCall {
|
|
|
9
9
|
ringUntil: string;
|
|
10
10
|
side?: string | null;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* The support host restarting (a deploy — it says 1012 as it stops): every widget on every page of every host app lost
|
|
14
|
+
* its socket in the same instant, and each came back after exactly 2 s — the whole fleet's tickets, sign-ins and
|
|
15
|
+
* sockets on the support host's one thread in the same second. Back somewhere in this window instead, at random.
|
|
16
|
+
*/
|
|
17
|
+
export declare const RESTART_SPREAD_MS = 15000;
|
|
18
|
+
/**
|
|
19
|
+
* How long before trying again — each step ×0.5–1.5, never the same moment as every other widget's (the desk's own tabs
|
|
20
|
+
* do the same) — `restart`: the host said it restarts (1012); `slow`:
|
|
21
|
+
* refused for now (429), or nobody signed in yet; else the next step of the ladder. Pure but for the dice, for the tests.
|
|
22
|
+
*/
|
|
23
|
+
export declare function retryWait(how: "restart" | "slow" | null, attempt: number, roll?: () => number): number;
|
|
12
24
|
/** `wss://support.example/api/presence/ws?t=…` — the support host's scheme, the socket's own path. Pure, for the tests. */
|
|
13
25
|
export declare function liveSocketUrl(host: string, ticket: string): string;
|
|
14
26
|
/** Where the card sends them: their request, with the call — the support host rings it there. Pure, for the tests. */
|
package/dist/ui/bubblePlace.d.ts
CHANGED
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
* STAY_MARGIN does not move it, so it does not jitter as the page settles.
|
|
24
24
|
|
|
25
25
|
* A PAGE'S OWN PLACE. The desk's rules (ปุ่มแชท › ตำแหน่งปุ่ม): pages whose path starts so — hidden there, or
|
|
26
|
-
* started
|
|
26
|
+
* started on a side at x in from its edge and y up from the bottom, at its own z-index (0.19.11). The most specific
|
|
27
|
+
* rule wins; a person who moved it keeps their place (the rule's z-index still holds).
|
|
27
28
|
*
|
|
28
29
|
* The arithmetic is pure (bubblePlace.test.ts); `scanColumns` reads the page.
|
|
29
30
|
*/
|
|
@@ -80,6 +81,10 @@ export interface PlaceRule {
|
|
|
80
81
|
show: "hide" | Side;
|
|
81
82
|
/** Up from the bottom, px. */
|
|
82
83
|
y: number;
|
|
84
|
+
/** In from its side's edge, px (0.19.11) — the host's own inset when the desk does not say. */
|
|
85
|
+
x?: number;
|
|
86
|
+
/** Its z-index on the page (0.19.11), the panel one above — null or absent: on top of everything. */
|
|
87
|
+
z?: number | null;
|
|
83
88
|
}
|
|
84
89
|
/** A rule's path as a test of a page's path: from its start, ending at a segment's end. Pure; null when malformed. */
|
|
85
90
|
export declare function pathTest(pattern: string): RegExp | null;
|
package/dist/ui/chatBubble.d.ts
CHANGED
|
@@ -24,11 +24,17 @@ export interface BubbleSpot {
|
|
|
24
24
|
/** Up from the bottom, px — to the bubble's lower edge. */
|
|
25
25
|
y: number;
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Let go at (px, py), its centre: just there (0.19.11, card f34152d3 — "move ได้เฉพาะ top/bottom left กับ top/bottom
|
|
29
|
+
* right อยากได้แบบ free hand"), measured from the nearer side so it keeps its place from that edge as the window
|
|
30
|
+
* changes; within SNAP of the edge it lines up at the inset. Inside the window. Pure.
|
|
31
|
+
*/
|
|
28
32
|
export declare function settleBubble(px: number, py: number, view: {
|
|
29
33
|
w: number;
|
|
30
34
|
h: number;
|
|
31
35
|
}, inset: number, s?: number): BubbleSpot;
|
|
36
|
+
/** In from its edge, keeping the whole bubble on screen. Pure. */
|
|
37
|
+
export declare function clampBubbleX(x: number, viewW: number, s?: number): number;
|
|
32
38
|
/** A height that keeps the whole bubble on screen. Pure. */
|
|
33
39
|
export declare function clampBubbleY(y: number, viewH: number, s?: number): number;
|
|
34
40
|
/** How far the call's card rises to clear the bubble: only a bubble on its side, low enough to meet it. Pure. */
|
|
@@ -42,6 +48,11 @@ export declare function anchorPanel(spot: BubbleSpot, viewH: number, s?: number)
|
|
|
42
48
|
top?: number;
|
|
43
49
|
height: number;
|
|
44
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* The panel by the bubble, in from the same edge as the bubble — a bubble far in from it (0.19.11) still has its whole
|
|
53
|
+
* panel on screen. Pure.
|
|
54
|
+
*/
|
|
55
|
+
export declare function panelInset(x: number, viewW: number): number;
|
|
45
56
|
/** A dragged panel's top-left, inside the window by the margin. Pure. */
|
|
46
57
|
export declare function clampPanel(p: {
|
|
47
58
|
x: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightworkai.official/debug-capture",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.12",
|
|
4
4
|
"description": "Report-a-problem widget: rolling network/console/action capture, optional rrweb session replay, annotated screenshot, and submission to a Lightwork Support host",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.mjs",
|
package/src/live.ts
CHANGED
|
@@ -42,6 +42,22 @@ export interface RingingCall {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const RETRY_MS = [2000, 5000, 15_000, 30_000, 60_000];
|
|
45
|
+
/**
|
|
46
|
+
* The support host restarting (a deploy — it says 1012 as it stops): every widget on every page of every host app lost
|
|
47
|
+
* its socket in the same instant, and each came back after exactly 2 s — the whole fleet's tickets, sign-ins and
|
|
48
|
+
* sockets on the support host's one thread in the same second. Back somewhere in this window instead, at random.
|
|
49
|
+
*/
|
|
50
|
+
export const RESTART_SPREAD_MS = 15_000;
|
|
51
|
+
/**
|
|
52
|
+
* How long before trying again — each step ×0.5–1.5, never the same moment as every other widget's (the desk's own tabs
|
|
53
|
+
* do the same) — `restart`: the host said it restarts (1012); `slow`:
|
|
54
|
+
* refused for now (429), or nobody signed in yet; else the next step of the ladder. Pure but for the dice, for the tests.
|
|
55
|
+
*/
|
|
56
|
+
export function retryWait(how: "restart" | "slow" | null, attempt: number, roll: () => number = Math.random): number {
|
|
57
|
+
if (how === "restart") return 1000 + Math.round(roll() * RESTART_SPREAD_MS);
|
|
58
|
+
const step = how === "slow" ? RETRY_MS[RETRY_MS.length - 1]! : RETRY_MS[Math.min(attempt, RETRY_MS.length - 1)]!;
|
|
59
|
+
return Math.round(step * (0.5 + roll()));
|
|
60
|
+
}
|
|
45
61
|
const LIVE_STATES = new Set(["ringing", "active"]);
|
|
46
62
|
const PING_MS = 20_000;
|
|
47
63
|
|
|
@@ -144,9 +160,9 @@ export function stopLiveRing(): void {
|
|
|
144
160
|
closeCallFrame();
|
|
145
161
|
}
|
|
146
162
|
|
|
147
|
-
function schedule(slow =
|
|
163
|
+
function schedule(how: "restart" | "slow" | null = null): void {
|
|
148
164
|
if (stopped) return;
|
|
149
|
-
const wait =
|
|
165
|
+
const wait = retryWait(how, how ? retry : retry++);
|
|
150
166
|
clearTimeout(reconnectTimer);
|
|
151
167
|
reconnectTimer = setTimeout(() => void connect(), wait);
|
|
152
168
|
}
|
|
@@ -160,7 +176,7 @@ async function connect(): Promise<void> {
|
|
|
160
176
|
stopped = true;
|
|
161
177
|
return;
|
|
162
178
|
}
|
|
163
|
-
return schedule(
|
|
179
|
+
return schedule("slow");
|
|
164
180
|
}
|
|
165
181
|
tokenless = 0;
|
|
166
182
|
let res: Response;
|
|
@@ -174,7 +190,8 @@ async function connect(): Promise<void> {
|
|
|
174
190
|
stopped = true;
|
|
175
191
|
return;
|
|
176
192
|
}
|
|
177
|
-
|
|
193
|
+
// Refused for now: slowly. The host restarting (its proxy answering for it): back at random within the window.
|
|
194
|
+
if (!res.ok) return schedule(res.status === 429 ? "slow" : res.status === 502 || res.status === 503 ? "restart" : null);
|
|
178
195
|
let out: { calls?: boolean; ticket?: string | null; call?: RingingCall | null };
|
|
179
196
|
try {
|
|
180
197
|
out = await res.json();
|
|
@@ -221,10 +238,11 @@ async function connect(): Promise<void> {
|
|
|
221
238
|
/* not ours to read */
|
|
222
239
|
}
|
|
223
240
|
};
|
|
224
|
-
ws.onclose = () => {
|
|
241
|
+
ws.onclose = (e) => {
|
|
225
242
|
clearInterval(pinger);
|
|
226
243
|
if (socket === ws) socket = null;
|
|
227
|
-
|
|
244
|
+
// 1012: the support host is restarting — every widget heard it at once; each comes back at its own moment.
|
|
245
|
+
schedule(e.code === 1012 ? "restart" : null);
|
|
228
246
|
};
|
|
229
247
|
}
|
|
230
248
|
|
package/src/ui/bubblePlace.ts
CHANGED
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
* STAY_MARGIN does not move it, so it does not jitter as the page settles.
|
|
24
24
|
|
|
25
25
|
* A PAGE'S OWN PLACE. The desk's rules (ปุ่มแชท › ตำแหน่งปุ่ม): pages whose path starts so — hidden there, or
|
|
26
|
-
* started
|
|
26
|
+
* started on a side at x in from its edge and y up from the bottom, at its own z-index (0.19.11). The most specific
|
|
27
|
+
* rule wins; a person who moved it keeps their place (the rule's z-index still holds).
|
|
27
28
|
*
|
|
28
29
|
* The arithmetic is pure (bubblePlace.test.ts); `scanColumns` reads the page.
|
|
29
30
|
*/
|
|
@@ -139,6 +140,10 @@ export interface PlaceRule {
|
|
|
139
140
|
show: "hide" | Side;
|
|
140
141
|
/** Up from the bottom, px. */
|
|
141
142
|
y: number;
|
|
143
|
+
/** In from its side's edge, px (0.19.11) — the host's own inset when the desk does not say. */
|
|
144
|
+
x?: number;
|
|
145
|
+
/** Its z-index on the page (0.19.11), the panel one above — null or absent: on top of everything. */
|
|
146
|
+
z?: number | null;
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
/** A rule's path as a test of a page's path: from its start, ending at a segment's end. Pure; null when malformed. */
|
package/src/ui/chatBubble.ts
CHANGED
|
@@ -161,10 +161,21 @@ export interface BubbleSpot {
|
|
|
161
161
|
const MARGIN = 8;
|
|
162
162
|
/** The ring's card is ~180px tall at the corner: a bubble above that leaves it there. */
|
|
163
163
|
const RING_ZONE = 200;
|
|
164
|
-
/** Let go
|
|
164
|
+
/** Let go this near its edge (px past the inset), it lines up with the corner at the inset. */
|
|
165
|
+
const SNAP = 24;
|
|
166
|
+
/**
|
|
167
|
+
* Let go at (px, py), its centre: just there (0.19.11, card f34152d3 — "move ได้เฉพาะ top/bottom left กับ top/bottom
|
|
168
|
+
* right อยากได้แบบ free hand"), measured from the nearer side so it keeps its place from that edge as the window
|
|
169
|
+
* changes; within SNAP of the edge it lines up at the inset. Inside the window. Pure.
|
|
170
|
+
*/
|
|
165
171
|
export function settleBubble(px: number, py: number, view: { w: number; h: number }, inset: number, s = SIZE): BubbleSpot {
|
|
166
172
|
const side = px < view.w / 2 ? "left" : "right";
|
|
167
|
-
|
|
173
|
+
const fromEdge = side === "left" ? px - s / 2 : view.w - px - s / 2;
|
|
174
|
+
return { side, x: fromEdge <= inset + SNAP ? inset : clampBubbleX(fromEdge, view.w, s), y: clampBubbleY(view.h - py - s / 2, view.h, s) };
|
|
175
|
+
}
|
|
176
|
+
/** In from its edge, keeping the whole bubble on screen. Pure. */
|
|
177
|
+
export function clampBubbleX(x: number, viewW: number, s = SIZE): number {
|
|
178
|
+
return Math.round(Math.min(Math.max(x, MARGIN), Math.max(MARGIN, viewW - s - MARGIN)));
|
|
168
179
|
}
|
|
169
180
|
/** A height that keeps the whole bubble on screen. Pure. */
|
|
170
181
|
export function clampBubbleY(y: number, viewH: number, s = SIZE): number {
|
|
@@ -184,6 +195,13 @@ export function anchorPanel(spot: BubbleSpot, viewH: number, s = SIZE): { bottom
|
|
|
184
195
|
if (above >= 420 || above >= below) return { bottom: spot.y + s + GAP, height: Math.max(240, Math.min(640, above)) };
|
|
185
196
|
return { top: viewH - spot.y + GAP, height: Math.max(240, Math.min(640, below)) };
|
|
186
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* The panel by the bubble, in from the same edge as the bubble — a bubble far in from it (0.19.11) still has its whole
|
|
200
|
+
* panel on screen. Pure.
|
|
201
|
+
*/
|
|
202
|
+
export function panelInset(x: number, viewW: number): number {
|
|
203
|
+
return Math.round(Math.max(MARGIN, Math.min(x, viewW - Math.min(PANEL_W, viewW - 32) - MARGIN)));
|
|
204
|
+
}
|
|
187
205
|
/** A dragged panel's top-left, inside the window by the margin. Pure. */
|
|
188
206
|
export function clampPanel(p: { x: number; y: number }, box: { w: number; h: number }, view: { w: number; h: number }): { x: number; y: number } {
|
|
189
207
|
return { x: Math.round(Math.min(Math.max(p.x, MARGIN), Math.max(MARGIN, view.w - box.w - MARGIN))), y: Math.round(Math.min(Math.max(p.y, MARGIN), Math.max(MARGIN, view.h - box.h - MARGIN))) };
|
|
@@ -351,10 +369,11 @@ function loadPlace(): void {
|
|
|
351
369
|
const def = chatPlace(getConfig().chat);
|
|
352
370
|
if (def) spot = { side: def.side, x: def.x, y: def.y };
|
|
353
371
|
try {
|
|
354
|
-
const kept = JSON.parse(localStorage.getItem(placeKey()) ?? "null") as { bubble?: { side?: unknown; y?: unknown }; panel?: { x?: unknown; y?: unknown; h?: unknown } | null; opened?: unknown } | null;
|
|
372
|
+
const kept = JSON.parse(localStorage.getItem(placeKey()) ?? "null") as { bubble?: { side?: unknown; x?: unknown; y?: unknown }; panel?: { x?: unknown; y?: unknown; h?: unknown } | null; opened?: unknown } | null;
|
|
355
373
|
const b = kept?.bubble;
|
|
356
374
|
movedByHand = !!b && (b.side === "left" || b.side === "right") && typeof b.y === "number" && Number.isFinite(b.y);
|
|
357
|
-
|
|
375
|
+
// Kept before 0.19.11 without its x: at the edge, where it was.
|
|
376
|
+
if (movedByHand) spot = { ...spot, side: b!.side as BubbleSpot["side"], y: b!.y as number, ...(typeof b!.x === "number" && Number.isFinite(b!.x) ? { x: b!.x } : {}) };
|
|
358
377
|
const p = kept?.panel;
|
|
359
378
|
panelAt = p && typeof p.x === "number" && typeof p.y === "number" && Number.isFinite(p.x) && Number.isFinite(p.y) ? { x: p.x, y: p.y, h: typeof p.h === "number" && p.h >= 240 ? p.h : 640 } : null;
|
|
360
379
|
opened = kept?.opened === true;
|
|
@@ -364,7 +383,7 @@ function loadPlace(): void {
|
|
|
364
383
|
}
|
|
365
384
|
function savePlace(): void {
|
|
366
385
|
try {
|
|
367
|
-
localStorage.setItem(placeKey(), JSON.stringify({ ...(movedByHand ? { bubble: { side: spot.side, y: spot.y } } : {}), panel: panelAt, ...(opened ? { opened: true } : {}) }));
|
|
386
|
+
localStorage.setItem(placeKey(), JSON.stringify({ ...(movedByHand ? { bubble: { side: spot.side, x: spot.x, y: spot.y } } : {}), panel: panelAt, ...(opened ? { opened: true } : {}) }));
|
|
368
387
|
} catch {
|
|
369
388
|
/* this visit only */
|
|
370
389
|
}
|
|
@@ -406,12 +425,18 @@ function avoidSetting(): { on: boolean; extra: string | null } {
|
|
|
406
425
|
if (own === false || deskLook?.avoid === false) return { on: false, extra: null };
|
|
407
426
|
return { on: true, extra: typeof own === "string" && own.trim() ? own.trim() : null };
|
|
408
427
|
}
|
|
409
|
-
/**
|
|
428
|
+
/** The desk's z-index for this page (0.19.11) — null: the stylesheet's, on top of everything. */
|
|
429
|
+
let ruleZ: number | null = null;
|
|
430
|
+
/**
|
|
431
|
+
* Where it wants to be on this page: put by hand — kept; else the desk's rule for the page — its side, x in from that
|
|
432
|
+
* edge and y up (0.19.11: x the desk's too); else the config's place. The rule's z-index holds either way.
|
|
433
|
+
*/
|
|
410
434
|
function wanted(): BubbleSpot {
|
|
411
435
|
const rule = placeFor(pagePath(), deskLook?.places);
|
|
412
436
|
ruleHidden = rule?.show === "hide";
|
|
437
|
+
ruleZ = typeof rule?.z === "number" && Number.isFinite(rule.z) ? Math.round(Math.min(Math.max(rule.z, 0), 2147483000)) : null;
|
|
413
438
|
if (movedByHand || !rule || rule.show === "hide") return spot;
|
|
414
|
-
return { side: rule.show, x: spot.x, y: rule.y };
|
|
439
|
+
return { side: rule.show, x: typeof rule.x === "number" && Number.isFinite(rule.x) ? rule.x : spot.x, y: rule.y };
|
|
415
440
|
}
|
|
416
441
|
/** Look at the page afresh — its columns read again as placing asks for them — and place again. */
|
|
417
442
|
function rescan(): void {
|
|
@@ -473,7 +498,8 @@ const onPageSettled = (e: Event) => {
|
|
|
473
498
|
if (ours(e.target)) return;
|
|
474
499
|
const r = e.target instanceof Element ? e.target.getBoundingClientRect() : null;
|
|
475
500
|
const v = view();
|
|
476
|
-
|
|
501
|
+
// Its column where it stands — far in from the edge too (0.19.11).
|
|
502
|
+
const x = (shown ?? spot).x;
|
|
477
503
|
const inColumn = !!r && r.width > 0 && (r.left < x + size || r.right > v.w - x - size);
|
|
478
504
|
queueRescan(inColumn ? "soon" : "later");
|
|
479
505
|
};
|
|
@@ -523,10 +549,11 @@ function layout(): void {
|
|
|
523
549
|
const v = view();
|
|
524
550
|
const want = wanted();
|
|
525
551
|
const wantY = clampBubbleY(want.y, v.h, size);
|
|
552
|
+
const wantX = clampBubbleX(want.x, v.w, size);
|
|
526
553
|
// While the panel is open it stays where it was opened from — never moved under the person's eyes.
|
|
527
|
-
const wish = `${pagePath()}|${want.side}|${
|
|
554
|
+
const wish = `${pagePath()}|${want.side}|${wantX}|${wantY}`;
|
|
528
555
|
const prev = shown && shownFor === wish ? shown : null;
|
|
529
|
-
const home = { side: want.side, x:
|
|
556
|
+
const home = { side: want.side, x: wantX, y: wantY };
|
|
530
557
|
const placing = !!columns && avoidSetting().on;
|
|
531
558
|
const free = !isOpen || !shown ? (placing ? placeSpot(home, readColumn, v, size, MARGIN, Math.max(MARGIN, v.h - size - MARGIN), prev) : home) : shown;
|
|
532
559
|
shown = { side: free.side, x: free.x, y: free.y };
|
|
@@ -537,6 +564,9 @@ function layout(): void {
|
|
|
537
564
|
const other = at.side === "right" ? "left" : "right";
|
|
538
565
|
bubble.style.display = ruleHidden && !isOpen ? "none" : "";
|
|
539
566
|
Object.assign(bubble.style, { [at.side]: `${at.x}px`, [other]: "", bottom: `${y}px`, top: "" });
|
|
567
|
+
// The page's z-index (0.19.11): under the host's own layer when the desk says so — the panel just above its bubble.
|
|
568
|
+
bubble.style.zIndex = ruleZ === null ? "" : String(ruleZ);
|
|
569
|
+
panel.style.zIndex = ruleZ === null ? "" : String(ruleZ + 1);
|
|
540
570
|
if (panelAt) {
|
|
541
571
|
// The height it had when it was picked up — a drag moves it, never resizes it under the hand.
|
|
542
572
|
const box = { w: Math.min(PANEL_W, v.w - 32), h: Math.min(panelAt.h, v.h - 2 * MARGIN) };
|
|
@@ -544,7 +574,7 @@ function layout(): void {
|
|
|
544
574
|
Object.assign(panel.style, { left: `${p.x}px`, top: `${p.y}px`, right: "", bottom: "", height: `${box.h}px` });
|
|
545
575
|
} else {
|
|
546
576
|
const a = anchorPanel(at, v.h, size);
|
|
547
|
-
Object.assign(panel.style, { [at.side]: `${at.x}px`, [other]: "", bottom: a.bottom !== undefined ? `${a.bottom}px` : "", top: a.top !== undefined ? `${a.top}px` : "", height: `${a.height}px` });
|
|
577
|
+
Object.assign(panel.style, { [at.side]: `${panelInset(at.x, v.w)}px`, [other]: "", bottom: a.bottom !== undefined ? `${a.bottom}px` : "", top: a.top !== undefined ? `${a.top}px` : "", height: `${a.height}px` });
|
|
548
578
|
}
|
|
549
579
|
// The call's card and frame sit above the bubble while it is there and low on their side (ring.ts, callFrame.ts).
|
|
550
580
|
const lift = root && root.style.display !== "none" && bubble.style.display !== "none" ? liftFor(at, size) : 0;
|