@lightworkai.official/debug-capture 0.19.4 → 0.19.5
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 -1
- package/dist/config.d.ts +6 -0
- package/dist/debug-capture.js +19 -19
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +24 -24
- package/dist/ui/chatBubble.d.ts +10 -0
- package/package.json +1 -1
- package/src/config.ts +6 -0
- package/src/index.ts +1 -1
- package/src/ui/chatBubble.ts +92 -25
package/dist/ui/chatBubble.d.ts
CHANGED
|
@@ -65,6 +65,14 @@ export declare function bubbleLabel(s: {
|
|
|
65
65
|
shown: boolean;
|
|
66
66
|
isNew: boolean;
|
|
67
67
|
} | null;
|
|
68
|
+
export interface DeskLook {
|
|
69
|
+
label?: string;
|
|
70
|
+
color?: string;
|
|
71
|
+
/** A path on the support host; null: the chat glyph on the colour. */
|
|
72
|
+
faceUrl?: string | null;
|
|
73
|
+
}
|
|
74
|
+
/** The face's address for a desk's look: its own path on the support host, the shipped one, or none (the glyph). Pure. */
|
|
75
|
+
export declare function lookFaceUrl(host: string, look: DeskLook | null): string | null;
|
|
68
76
|
/** Open the panel — from the bubble, or from the host app's own button (`openSupportChat`). */
|
|
69
77
|
export declare function openSupportChat(): void;
|
|
70
78
|
/** Fold the panel — it stays loaded, so the chat is there as it was the next time. */
|
|
@@ -72,4 +80,6 @@ export declare function closeSupportChat(refocus?: boolean): void;
|
|
|
72
80
|
/** The bubble, once — hidden until the host's token says somebody is signed in. */
|
|
73
81
|
export declare function mountSupportChat(): void;
|
|
74
82
|
/** Take it away — for a host that unmounts, and for the tests. */
|
|
83
|
+
/** Ask again whether there is a bubble, and what waits (0.19.5) — after a sign-in or a sign-out, or `show` changed. */
|
|
84
|
+
export declare function refreshSupportChat(): Promise<void>;
|
|
75
85
|
export declare function unmountSupportChat(): void;
|
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.5",
|
|
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/config.ts
CHANGED
|
@@ -232,6 +232,12 @@ export interface ChatOptions {
|
|
|
232
232
|
* of "แชทกับทีมงาน", or `false` for the button alone.
|
|
233
233
|
*/
|
|
234
234
|
label?: string | false;
|
|
235
|
+
/**
|
|
236
|
+
* Whether the bubble is for whoever is signed in now (0.19.5) — asked before every read; false: no bubble. For a
|
|
237
|
+
* host whose own staff should not chat with its support desk, or not on every page. Ask again after a sign-in with
|
|
238
|
+
* `refreshSupportChat()`.
|
|
239
|
+
*/
|
|
240
|
+
show?: () => boolean;
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
/** The bubble's place, from `chat`: null when it is off. */
|
package/src/index.ts
CHANGED
|
@@ -77,7 +77,7 @@ export type { OpenPortalOptions } from "./portal";
|
|
|
77
77
|
* แชทกับทีม (0.19): `chat` in the config mounts the bubble on install; a host with its own button opens and closes
|
|
78
78
|
* the same panel. The panel is the support host's page, framed — as the tickets are, maintained where they live.
|
|
79
79
|
*/
|
|
80
|
-
export { openSupportChat, closeSupportChat, mountSupportChat, unmountSupportChat, supportFrameUrl, badgeText, chatLift, panelHeight } from "./ui/chatBubble";
|
|
80
|
+
export { openSupportChat, closeSupportChat, mountSupportChat, unmountSupportChat, refreshSupportChat, supportFrameUrl, badgeText, chatLift, panelHeight } from "./ui/chatBubble";
|
|
81
81
|
export type { ChatOptions } from "./config";
|
|
82
82
|
|
|
83
83
|
export type * from "./types";
|
package/src/ui/chatBubble.ts
CHANGED
|
@@ -33,11 +33,11 @@ const CSS = `
|
|
|
33
33
|
.bubble {
|
|
34
34
|
position: fixed; z-index: 2147483000; width: ${SIZE}px; height: ${SIZE}px; border-radius: 50%;
|
|
35
35
|
display: grid; place-items: center; border: 0; cursor: pointer; padding: 0;
|
|
36
|
-
background: #00468f; color: #fff; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3), 0 4px 10px -4px rgba(0, 0, 0, 0.2);
|
|
36
|
+
background: var(--lw-chat-color, #00468f); color: #fff; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3), 0 4px 10px -4px rgba(0, 0, 0, 0.2);
|
|
37
37
|
transition: transform 150ms ease, background 150ms ease;
|
|
38
38
|
font-family: var(--lw-host-font, ${OWN_FONTS});
|
|
39
39
|
}
|
|
40
|
-
.bubble:hover {
|
|
40
|
+
.bubble:hover { filter: brightness(0.92); transform: translateY(-1px); }
|
|
41
41
|
.bubble { touch-action: none; }
|
|
42
42
|
.bubble.dragging { cursor: grabbing; transform: scale(1.06); transition: none; box-shadow: 0 18px 35px -8px rgba(0, 0, 0, 0.4); }
|
|
43
43
|
.bubble:focus-visible { outline: 3px solid rgba(0, 70, 143, 0.35); outline-offset: 3px; }
|
|
@@ -65,7 +65,7 @@ const CSS = `
|
|
|
65
65
|
.label .text { display: block; max-width: min(240px, calc(100vw - ${SIZE + 64}px)); overflow: hidden; text-overflow: ellipsis; }
|
|
66
66
|
.at-left .label { right: auto; left: calc(100% + 12px); transform: translate(-4px, -50%); }
|
|
67
67
|
.at-left .label::after { right: auto; left: -4px; }
|
|
68
|
-
.new .label { color: #00468f; }
|
|
68
|
+
.new .label { color: var(--lw-chat-color, #00468f); }
|
|
69
69
|
.labelled .label { opacity: 1; visibility: visible; pointer-events: auto; transform: translate(0, -50%); }
|
|
70
70
|
@media (hover: hover) { .bubble:hover .label { opacity: 1; visibility: visible; pointer-events: auto; transform: translate(0, -50%); } }
|
|
71
71
|
.unlabelled .label, .bubble.dragging .label, .open .label { display: none; }
|
|
@@ -207,7 +207,9 @@ let opened = false;
|
|
|
207
207
|
function setLabel(): void {
|
|
208
208
|
if (!label || !bubble) return;
|
|
209
209
|
const config = getConfig();
|
|
210
|
-
|
|
210
|
+
// The host's own words in code win; else the desk's (ปุ่มแชท, its look); else the shipped ones.
|
|
211
|
+
const hostOwn = config.chat && config.chat !== true ? config.chat.label : undefined;
|
|
212
|
+
const own = hostOwn !== undefined ? hostOwn : deskLook?.label;
|
|
211
213
|
const l = bubbleLabel({ own, unread, opened, open: isOpen }, strings(config.locale));
|
|
212
214
|
bubble.classList.toggle("unlabelled", !l);
|
|
213
215
|
bubble.classList.toggle("labelled", !!l?.shown);
|
|
@@ -216,28 +218,83 @@ function setLabel(): void {
|
|
|
216
218
|
label.querySelector(".text")!.textContent = l?.text ?? "";
|
|
217
219
|
}
|
|
218
220
|
|
|
221
|
+
/*
|
|
222
|
+
* The desk's look (0.19.5, the support desk's ปุ่มแชท — docs/CHAT-BUBBLE-PLAN.md): its face (or the chat glyph on its
|
|
223
|
+
* colour), its colour, the words beside the button — set on the desk, read with the first answer here.
|
|
224
|
+
*/
|
|
225
|
+
export interface DeskLook {
|
|
226
|
+
label?: string;
|
|
227
|
+
color?: string;
|
|
228
|
+
/** A path on the support host; null: the chat glyph on the colour. */
|
|
229
|
+
faceUrl?: string | null;
|
|
230
|
+
}
|
|
231
|
+
let deskLook: DeskLook | null = null;
|
|
232
|
+
/** The face's address for a desk's look: its own path on the support host, the shipped one, or none (the glyph). Pure. */
|
|
233
|
+
export function lookFaceUrl(host: string, look: DeskLook | null): string | null {
|
|
234
|
+
if (!look || look.faceUrl === undefined) return supportFaceUrl(host);
|
|
235
|
+
if (look.faceUrl === null) return null;
|
|
236
|
+
try {
|
|
237
|
+
return new URL(look.faceUrl.replace(/^\//, ""), base(host)).toString();
|
|
238
|
+
} catch {
|
|
239
|
+
return supportFaceUrl(host);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function wearLook(look: DeskLook | null): void {
|
|
243
|
+
deskLook = look;
|
|
244
|
+
if (!root || !bubble) return;
|
|
245
|
+
const color = look?.color && /^#[0-9a-f]{6}$/i.test(look.color) ? look.color : null;
|
|
246
|
+
if (color) root.style.setProperty("--lw-chat-color", color);
|
|
247
|
+
else root.style.removeProperty("--lw-chat-color");
|
|
248
|
+
const face = shadow?.querySelector<HTMLImageElement>(".face");
|
|
249
|
+
const url = lookFaceUrl(getConfig().host, look);
|
|
250
|
+
if (face) {
|
|
251
|
+
if (!url) {
|
|
252
|
+
bubble.classList.remove("has-face");
|
|
253
|
+
face.removeAttribute("src");
|
|
254
|
+
} else if (face.getAttribute("src") !== url) {
|
|
255
|
+
bubble.classList.remove("has-face");
|
|
256
|
+
face.src = url;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
setLabel();
|
|
260
|
+
}
|
|
261
|
+
|
|
219
262
|
let asking: Promise<void> | null = null;
|
|
220
263
|
/** What waits for the person, read with the host's token — no token: nobody is signed in, and there is no bubble. */
|
|
221
264
|
async function refreshUnread(): Promise<void> {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
265
|
+
if (asking) return asking;
|
|
266
|
+
/*
|
|
267
|
+
* Kept until it settles, then let go — by THIS read only. (`asking ??= (async () => { … finally { asking = null } })()`
|
|
268
|
+
* lost the let-go when the read ended before its first await — `show` saying no: the finally ran before the
|
|
269
|
+
* assignment, and every read after was that settled one, never asked again.)
|
|
270
|
+
*/
|
|
271
|
+
const run = readUnread();
|
|
272
|
+
asking = run;
|
|
273
|
+
try {
|
|
274
|
+
await run;
|
|
275
|
+
} finally {
|
|
276
|
+
if (asking === run) asking = null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function readUnread(): Promise<void> {
|
|
280
|
+
try {
|
|
281
|
+
const config = getConfig();
|
|
282
|
+
// The host's own say (0.19.5): not for whoever is signed in now — no bubble, nothing asked.
|
|
283
|
+
const opts = config.chat && config.chat !== true ? config.chat : null;
|
|
284
|
+
if (opts?.show && !opts.show()) return void show(false);
|
|
285
|
+
const token = await hostToken();
|
|
286
|
+
if (!token) return void show(false);
|
|
287
|
+
const res = await fetch(new URL("ingest/support-unread", base(config.host)).toString(), { headers: { "x-realm-key": config.realmKey, authorization: `Bearer ${token}` } });
|
|
288
|
+
// A support host from before the chat, a desk that turned its bubble off, or a token it does not accept: no bubble.
|
|
289
|
+
if (res.status === 404 || res.status === 401 || res.status === 403) return void show(false);
|
|
290
|
+
if (!res.ok) return;
|
|
291
|
+
const out = (await res.json()) as { total?: number; look?: DeskLook };
|
|
292
|
+
if (out.look && typeof out.look === "object") wearLook(out.look);
|
|
293
|
+
show(true);
|
|
294
|
+
if (!isOpen) setBadge(typeof out.total === "number" ? out.total : 0);
|
|
295
|
+
} catch {
|
|
296
|
+
/* the next nudge asks again */
|
|
297
|
+
}
|
|
241
298
|
}
|
|
242
299
|
|
|
243
300
|
function show(on: boolean): void {
|
|
@@ -491,8 +548,12 @@ export function mountSupportChat(): void {
|
|
|
491
548
|
// The desk's face (the owner, 10-31: "all the support icon that user see … use this icon"): the support host's own,
|
|
492
549
|
// the one its replies wear — the chat glyph until it is there, and if it never comes.
|
|
493
550
|
const face = shadow.querySelector<HTMLImageElement>(".face")!;
|
|
494
|
-
|
|
495
|
-
face.
|
|
551
|
+
// Every picture it is given (the desk's look may change it, 0.19.5): worn once loaded, the glyph if it fails.
|
|
552
|
+
face.addEventListener("load", () => {
|
|
553
|
+
if (face.getAttribute("src")) bubble?.classList.add("has-face");
|
|
554
|
+
});
|
|
555
|
+
face.addEventListener("error", () => bubble?.classList.remove("has-face"));
|
|
556
|
+
face.src = lookFaceUrl(config.host, deskLook) ?? supportFaceUrl(config.host);
|
|
496
557
|
// Where the person left it on this device, else the config's place (0.19.2).
|
|
497
558
|
loadPlace();
|
|
498
559
|
layout();
|
|
@@ -521,7 +582,13 @@ export function mountSupportChat(): void {
|
|
|
521
582
|
}
|
|
522
583
|
|
|
523
584
|
/** Take it away — for a host that unmounts, and for the tests. */
|
|
585
|
+
/** Ask again whether there is a bubble, and what waits (0.19.5) — after a sign-in or a sign-out, or `show` changed. */
|
|
586
|
+
export function refreshSupportChat(): Promise<void> {
|
|
587
|
+
return root ? refreshUnread() : Promise.resolve();
|
|
588
|
+
}
|
|
589
|
+
|
|
524
590
|
export function unmountSupportChat(): void {
|
|
591
|
+
deskLook = null;
|
|
525
592
|
stopFrames?.();
|
|
526
593
|
stopFrames = null;
|
|
527
594
|
window.removeEventListener("message", onMessage);
|