@lightworkai.official/debug-capture 0.17.2 → 0.17.3
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 +8 -0
- package/dist/debug-capture.js +9 -9
- package/dist/index.mjs +15 -15
- package/dist/ui/callFrame.d.ts +1 -0
- package/package.json +1 -1
- package/src/live.ts +49 -3
- package/src/ui/callFrame.ts +14 -3
package/dist/ui/callFrame.d.ts
CHANGED
|
@@ -55,3 +55,4 @@ export declare function callFrameFor(): string | null;
|
|
|
55
55
|
/** Open the call's frame — once per call; the same call again changes nothing. Shown when it says it is ready. */
|
|
56
56
|
export declare function openCallFrame(src: string, id: string, h: FrameHandlers, readyWithinMs?: number, token?: Promise<string | null> | null): void;
|
|
57
57
|
export declare function closeCallFrame(): void;
|
|
58
|
+
export declare function nameTabAtRest(handle: string, readableBy: string): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightworkai.official/debug-capture",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
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
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
import { getConfig } from "./config";
|
|
26
26
|
import { preparePortalUrl } from "./portal";
|
|
27
27
|
import { hideRing, quietRing, ringAnswering, ringFallback, ringOpened, ringShownFor, showRing, type RingHandlers } from "./ui/ring";
|
|
28
|
-
import { answerInFrame, callFrameFor, closeCallFrame, frameUrl, openCallFrame, voiceInFrame } from "./ui/callFrame";
|
|
28
|
+
import { answerInFrame, callFrameFor, closeCallFrame, frameUrl, nameTabAtRest, openCallFrame, voiceInFrame } from "./ui/callFrame";
|
|
29
|
+
import { readInkOp } from "./ink";
|
|
30
|
+
import { inkOnPage, removeInkLayer } from "./ui/inkOverlay";
|
|
29
31
|
|
|
30
32
|
export interface RingingCall {
|
|
31
33
|
id: string;
|
|
@@ -99,6 +101,11 @@ export function startLiveRing(): void {
|
|
|
99
101
|
started = true;
|
|
100
102
|
stopped = false;
|
|
101
103
|
warmUp(config.host);
|
|
104
|
+
try {
|
|
105
|
+
nameTabAtRest(`lw-tab:${tabId()}`, new URL(config.host).origin);
|
|
106
|
+
} catch {
|
|
107
|
+
/* not a host we can name */
|
|
108
|
+
}
|
|
102
109
|
void connect();
|
|
103
110
|
}
|
|
104
111
|
|
|
@@ -166,6 +173,8 @@ async function connect(): Promise<void> {
|
|
|
166
173
|
socket = ws;
|
|
167
174
|
ws.onopen = () => {
|
|
168
175
|
retry = 0;
|
|
176
|
+
// Which tab this is (§21): the call's marks for a screen shared from here reach it on this socket.
|
|
177
|
+
ws.send(JSON.stringify({ type: "tab", id: tabId() }));
|
|
169
178
|
clearInterval(pinger);
|
|
170
179
|
pinger = setInterval(() => {
|
|
171
180
|
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: "ping" }));
|
|
@@ -173,8 +182,16 @@ async function connect(): Promise<void> {
|
|
|
173
182
|
};
|
|
174
183
|
ws.onmessage = (e) => {
|
|
175
184
|
try {
|
|
176
|
-
const frame = JSON.parse(String(e.data)) as { type?: string; call?: RingingCall };
|
|
177
|
-
if (frame.type === "call-state" && frame.call) hear(frame.call);
|
|
185
|
+
const frame = JSON.parse(String(e.data)) as { type?: string; call?: RingingCall | string; op?: unknown };
|
|
186
|
+
if (frame.type === "call-state" && frame.call && typeof frame.call === "object") hear(frame.call);
|
|
187
|
+
// The team's marks on this page, shared from the portal's call (§21) — read again, never drawn as received.
|
|
188
|
+
else if (frame.type === "call-ink" && typeof frame.call === "string") {
|
|
189
|
+
const op = readInkOp(frame.op);
|
|
190
|
+
if (op) {
|
|
191
|
+
inkCall = frame.call;
|
|
192
|
+
inkOnPage(op);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
178
195
|
} catch {
|
|
179
196
|
/* not ours to read */
|
|
180
197
|
}
|
|
@@ -254,8 +271,37 @@ function frameFor(call: RingingCall): void {
|
|
|
254
271
|
}
|
|
255
272
|
const portalWay = (call: RingingCall): RingHandlers => ({ open: () => answer(call), decline: () => void decline(call) });
|
|
256
273
|
|
|
274
|
+
/** This tab's own id (§21), kept across its reloads — what its name says, and what its socket says. */
|
|
275
|
+
let ownTabId: string | null = null;
|
|
276
|
+
function tabId(): string {
|
|
277
|
+
if (ownTabId) return ownTabId;
|
|
278
|
+
try {
|
|
279
|
+
ownTabId = sessionStorage.getItem(TAB_KEY);
|
|
280
|
+
} catch {
|
|
281
|
+
/* no storage: a new id each load */
|
|
282
|
+
}
|
|
283
|
+
if (!ownTabId || !/^[A-Za-z0-9_-]{8,40}$/.test(ownTabId)) {
|
|
284
|
+
const bytes = new Uint8Array(12);
|
|
285
|
+
crypto.getRandomValues(bytes);
|
|
286
|
+
ownTabId = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
287
|
+
try {
|
|
288
|
+
sessionStorage.setItem(TAB_KEY, ownTabId);
|
|
289
|
+
} catch {
|
|
290
|
+
/* this load only */
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return ownTabId;
|
|
294
|
+
}
|
|
295
|
+
const TAB_KEY = "lw-tab-id";
|
|
296
|
+
/** The call whose marks are on this page (§21): they go with it. */
|
|
297
|
+
let inkCall: string | null = null;
|
|
298
|
+
|
|
257
299
|
/** The support host's word on a call: ringing here shows the card (and its frame); anything else takes the card away — the frame stays, it IS the call. */
|
|
258
300
|
function hear(call: RingingCall): void {
|
|
301
|
+
if (!LIVE_STATES.has(call.status) && inkCall === call.id && callFrameFor() !== call.id) {
|
|
302
|
+
inkCall = null;
|
|
303
|
+
removeInkLayer();
|
|
304
|
+
}
|
|
259
305
|
if (!ringsHere(call)) {
|
|
260
306
|
hideRing(call.id);
|
|
261
307
|
// Answered in this tab's frame, then the page reloaded: the frame comes back and takes the call back.
|
package/src/ui/callFrame.ts
CHANGED
|
@@ -169,7 +169,7 @@ iframe.on { visibility: visible; }`;
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
export function closeCallFrame(): void {
|
|
172
|
-
if (host) nameTab(null);
|
|
172
|
+
if (host) nameTab(resting?.handle ?? null, resting?.readableBy);
|
|
173
173
|
removeInkLayer();
|
|
174
174
|
clearTimeout(readyTimer);
|
|
175
175
|
window.removeEventListener("message", onMessage);
|
|
@@ -250,15 +250,26 @@ function onMessage(e: MessageEvent): void {
|
|
|
250
250
|
* browser without Capture Handle simply has no name to give, and the call says it cannot tell.
|
|
251
251
|
*/
|
|
252
252
|
type CaptureHandleConfig = { handle?: string; exposeOrigin?: boolean; permittedOrigins?: string[] };
|
|
253
|
-
function nameTab(handle: string | null): void {
|
|
253
|
+
function nameTab(handle: string | null, readableBy = origin): void {
|
|
254
254
|
const md = (typeof navigator === "undefined" ? undefined : navigator.mediaDevices) as (MediaDevices & { setCaptureHandleConfig?: (c: CaptureHandleConfig) => void }) | undefined;
|
|
255
255
|
try {
|
|
256
|
-
md?.setCaptureHandleConfig?.(handle ? { handle, exposeOrigin: false, permittedOrigins: [
|
|
256
|
+
md?.setCaptureHandleConfig?.(handle && readableBy ? { handle, exposeOrigin: false, permittedOrigins: [readableBy] } : {});
|
|
257
257
|
} catch {
|
|
258
258
|
/* not a top-level page, or not this browser */
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* The tab's name between calls (0.17.3, the support host's §21): `lw-tab:<id>` — so a call answered on the support
|
|
264
|
+
* host's portal, with THIS tab shared, can tell it is a tab of ours and have the team's marks drawn here (through the
|
|
265
|
+
* live socket). A call's frame names the tab for its call while it lasts, and gives this name back when it goes.
|
|
266
|
+
*/
|
|
267
|
+
let resting: { handle: string; readableBy: string } | null = null;
|
|
268
|
+
export function nameTabAtRest(handle: string, readableBy: string): void {
|
|
269
|
+
resting = { handle, readableBy };
|
|
270
|
+
if (!host?.isConnected) nameTab(handle, readableBy);
|
|
271
|
+
}
|
|
272
|
+
|
|
262
273
|
/** The corner moved by its top line: from where the press began, by how far the pointer went on the screen. */
|
|
263
274
|
function onDrag(m: FrameMessage): void {
|
|
264
275
|
if (!frame || layout !== "card" || typeof m.x !== "number" || typeof m.y !== "number") return;
|