@lightworkai.official/debug-capture 0.19.9 → 0.19.11

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.
@@ -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 in a corner at a height. The most specific rule wins; a person who moved it keeps their place.
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;
@@ -1,4 +1,9 @@
1
1
  import { type PlaceRule } from "./bubblePlace";
2
+ /**
3
+ * The desk's size for it, px (0.19.10; the owner, 11-02: "make it smaller or bigger as a config") — the desk offers
4
+ * 44 to 72; anything else it might say is brought within reach of a finger and of the corner. Pure.
5
+ */
6
+ export declare function bubbleSize(v: unknown): number;
2
7
  /** The panel's address: the support host's page, told the realm, signing in by hand-off (no token in the address). */
3
8
  export declare function supportFrameUrl(host: string, realmKey: string): string;
4
9
  /** Where the panel's เปิดในระบบแจ้งปัญหา may land: the person's chat or their เรื่อง — anything else, their chat. Pure. */
@@ -9,9 +14,9 @@ export declare function supportFaceUrl(host: string): string;
9
14
  * How far the call's card and frame rise from their own 16px so they clear the bubble — its offset, its size and the
10
15
  * gap above it (0.19.1: 0.19.0 assumed the bubble at the corner, and a raised one sat under a ringing call). Pure.
11
16
  */
12
- export declare function chatLift(bubbleY: number): number;
17
+ export declare function chatLift(bubbleY: number, s?: number): number;
13
18
  /** The panel above the bubble, never past the top of the window — its height for the bubble's offset (0.19.1). Pure. */
14
- export declare function panelHeight(bubbleY: number): string;
19
+ export declare function panelHeight(bubbleY: number, s?: number): string;
15
20
  export interface BubbleSpot {
16
21
  side: "right" | "left";
17
22
  /** In from that side, px. */
@@ -19,24 +24,35 @@ export interface BubbleSpot {
19
24
  /** Up from the bottom, px — to the bubble's lower edge. */
20
25
  y: number;
21
26
  }
22
- /** Let go at (px, py), its centre: the nearer side, the height it was dropped at — inside the window. Pure. */
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
+ */
23
32
  export declare function settleBubble(px: number, py: number, view: {
24
33
  w: number;
25
34
  h: number;
26
- }, inset: number): BubbleSpot;
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;
27
38
  /** A height that keeps the whole bubble on screen. Pure. */
28
- export declare function clampBubbleY(y: number, viewH: number): number;
39
+ export declare function clampBubbleY(y: number, viewH: number, s?: number): number;
29
40
  /** How far the call's card rises to clear the bubble: only a bubble on its side, low enough to meet it. Pure. */
30
- export declare function liftFor(spot: BubbleSpot): number;
41
+ export declare function liftFor(spot: BubbleSpot, s?: number): number;
31
42
  /**
32
43
  * The panel by the bubble, where it was not dragged: above it when the room above is enough (or more than below),
33
44
  * else below it — its own height, never past the window. Pure.
34
45
  */
35
- export declare function anchorPanel(spot: BubbleSpot, viewH: number): {
46
+ export declare function anchorPanel(spot: BubbleSpot, viewH: number, s?: number): {
36
47
  bottom?: number;
37
48
  top?: number;
38
49
  height: number;
39
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;
40
56
  /** A dragged panel's top-left, inside the window by the margin. Pure. */
41
57
  export declare function clampPanel(p: {
42
58
  x: number;
@@ -75,6 +91,8 @@ export interface DeskLook {
75
91
  avoid?: boolean;
76
92
  /** A page's own place (0.19.6): hidden there, or started in a corner at a height — the desk's rules. */
77
93
  places?: PlaceRule[];
94
+ /** ขนาดปุ่ม, px (0.19.10) — 56 when the desk does not say. */
95
+ size?: number;
78
96
  }
79
97
  /** The face's address for a desk's look: its own path on the support host, the shipped one, or none (the glyph). Pure. */
80
98
  export declare function lookFaceUrl(host: string, look: DeskLook | null): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightworkai.official/debug-capture",
3
- "version": "0.19.9",
3
+ "version": "0.19.11",
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",
@@ -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 in a corner at a height. The most specific rule wins; a person who moved it keeps their place.
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. */
@@ -25,15 +25,25 @@ import { strings } from "./strings";
25
25
  const HOST_TAG = "lw-support-chat";
26
26
  const FRAME = "lw-support-frame";
27
27
  const HOST = "lw-support-host";
28
+ /** The button's size until the desk says its own (ปุ่มแชท › ขนาดปุ่ม, 0.19.10). */
28
29
  const SIZE = 56;
29
30
  const GAP = 12;
31
+ /**
32
+ * The desk's size for it, px (0.19.10; the owner, 11-02: "make it smaller or bigger as a config") — the desk offers
33
+ * 44 to 72; anything else it might say is brought within reach of a finger and of the corner. Pure.
34
+ */
35
+ export function bubbleSize(v: unknown): number {
36
+ return typeof v === "number" && Number.isFinite(v) ? Math.round(Math.min(Math.max(v, 32), 96)) : SIZE;
37
+ }
38
+ /** Its size now — the desk's, once its look is read. */
39
+ let size = SIZE;
30
40
 
31
41
  const ICON = `<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/></svg>`;
32
42
 
33
43
  const CSS = `
34
44
  :host { all: initial; }
35
45
  .bubble {
36
- position: fixed; z-index: 2147483000; width: ${SIZE}px; height: ${SIZE}px; border-radius: 50%;
46
+ position: fixed; z-index: 2147483000; width: var(--lw-chat-size, ${SIZE}px); height: var(--lw-chat-size, ${SIZE}px); border-radius: 50%;
37
47
  display: grid; place-items: center; border: 0; cursor: pointer; padding: 0;
38
48
  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);
39
49
  transition: transform 150ms ease, background 150ms ease;
@@ -43,6 +53,7 @@ const CSS = `
43
53
  .bubble { touch-action: none; }
44
54
  .bubble.dragging { cursor: grabbing; transform: scale(1.06); transition: none; box-shadow: 0 18px 35px -8px rgba(0, 0, 0, 0.4); }
45
55
  .bubble:focus-visible { outline: 3px solid rgba(0, 70, 143, 0.35); outline-offset: 3px; }
56
+ .bubble > svg { width: calc(var(--lw-chat-size, ${SIZE}px) * 0.46); height: calc(var(--lw-chat-size, ${SIZE}px) * 0.46); }
46
57
  .face { display: none; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; pointer-events: none; user-select: none; -webkit-user-drag: none; }
47
58
  .bubble.has-face, .bubble.has-face:hover { background: #fff; }
48
59
  .has-face .face { display: block; }
@@ -64,7 +75,7 @@ const CSS = `
64
75
  content: ""; position: absolute; top: 50%; right: -4px; width: 10px; height: 10px; background: #fff; border-radius: 2px;
65
76
  transform: translateY(-50%) rotate(45deg); box-shadow: 1px -1px 0 rgba(0, 0, 0, 0.04);
66
77
  }
67
- .label .text { display: block; max-width: min(240px, calc(100vw - ${SIZE + 64}px)); overflow: hidden; text-overflow: ellipsis; }
78
+ .label .text { display: block; max-width: min(240px, calc(100vw - var(--lw-chat-size, ${SIZE}px) - 64px)); overflow: hidden; text-overflow: ellipsis; }
68
79
  .at-left .label { right: auto; left: calc(100% + 12px); transform: translate(-4px, -50%); }
69
80
  .at-left .label::after { right: auto; left: -4px; }
70
81
  .new .label { color: var(--lw-chat-color, #00468f); }
@@ -72,7 +83,7 @@ const CSS = `
72
83
  @media (hover: hover) { .bubble:hover .label { opacity: 1; visibility: visible; pointer-events: auto; transform: translate(0, -50%); } }
73
84
  .unlabelled .label, .bubble.dragging .label, .open .label { display: none; }
74
85
  .panel {
75
- position: fixed; z-index: 2147483001; width: min(380px, calc(100vw - 32px)); height: min(640px, calc(100vh - ${SIZE + GAP + 32}px));
86
+ position: fixed; z-index: 2147483001; width: min(380px, calc(100vw - 32px)); height: min(640px, calc(100vh - var(--lw-chat-size, ${SIZE}px) - ${GAP + 32}px));
76
87
  border-radius: 20px; overflow: hidden; background: #fff; border: 1px solid rgba(15, 23, 42, 0.08);
77
88
  box-shadow: 0 28px 64px -16px rgba(15, 23, 42, 0.3), 0 10px 24px -10px rgba(15, 23, 42, 0.14);
78
89
  animation: in 180ms cubic-bezier(0.16, 1, 0.3, 1);
@@ -126,12 +137,12 @@ export function supportFaceUrl(host: string): string {
126
137
  * How far the call's card and frame rise from their own 16px so they clear the bubble — its offset, its size and the
127
138
  * gap above it (0.19.1: 0.19.0 assumed the bubble at the corner, and a raised one sat under a ringing call). Pure.
128
139
  */
129
- export function chatLift(bubbleY: number): number {
130
- return bubbleY + SIZE + GAP - 16;
140
+ export function chatLift(bubbleY: number, s = SIZE): number {
141
+ return bubbleY + s + GAP - 16;
131
142
  }
132
143
  /** The panel above the bubble, never past the top of the window — its height for the bubble's offset (0.19.1). Pure. */
133
- export function panelHeight(bubbleY: number): string {
134
- return `min(640px, calc(100vh - ${bubbleY + SIZE + GAP + 16}px))`;
144
+ export function panelHeight(bubbleY: number, s = SIZE): string {
145
+ return `min(640px, calc(100vh - ${bubbleY + s + GAP + 16}px))`;
135
146
  }
136
147
 
137
148
  /*
@@ -150,29 +161,47 @@ export interface BubbleSpot {
150
161
  const MARGIN = 8;
151
162
  /** The ring's card is ~180px tall at the corner: a bubble above that leaves it there. */
152
163
  const RING_ZONE = 200;
153
- /** Let go at (px, py), its centre: the nearer side, the height it was dropped at — inside the window. Pure. */
154
- export function settleBubble(px: number, py: number, view: { w: number; h: number }, inset: number): BubbleSpot {
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
+ */
171
+ export function settleBubble(px: number, py: number, view: { w: number; h: number }, inset: number, s = SIZE): BubbleSpot {
155
172
  const side = px < view.w / 2 ? "left" : "right";
156
- return { side, x: inset, y: clampBubbleY(view.h - py - SIZE / 2, view.h) };
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)));
157
179
  }
158
180
  /** A height that keeps the whole bubble on screen. Pure. */
159
- export function clampBubbleY(y: number, viewH: number): number {
160
- return Math.round(Math.min(Math.max(y, MARGIN), Math.max(MARGIN, viewH - SIZE - MARGIN)));
181
+ export function clampBubbleY(y: number, viewH: number, s = SIZE): number {
182
+ return Math.round(Math.min(Math.max(y, MARGIN), Math.max(MARGIN, viewH - s - MARGIN)));
161
183
  }
162
184
  /** How far the call's card rises to clear the bubble: only a bubble on its side, low enough to meet it. Pure. */
163
- export function liftFor(spot: BubbleSpot): number {
164
- return spot.side === "right" && spot.y < RING_ZONE ? chatLift(spot.y) : 0;
185
+ export function liftFor(spot: BubbleSpot, s = SIZE): number {
186
+ return spot.side === "right" && spot.y < RING_ZONE ? chatLift(spot.y, s) : 0;
165
187
  }
166
188
  /**
167
189
  * The panel by the bubble, where it was not dragged: above it when the room above is enough (or more than below),
168
190
  * else below it — its own height, never past the window. Pure.
169
191
  */
170
- export function anchorPanel(spot: BubbleSpot, viewH: number): { bottom?: number; top?: number; height: number } {
171
- const above = viewH - (spot.y + SIZE + GAP) - 16;
192
+ export function anchorPanel(spot: BubbleSpot, viewH: number, s = SIZE): { bottom?: number; top?: number; height: number } {
193
+ const above = viewH - (spot.y + s + GAP) - 16;
172
194
  const below = spot.y - GAP - 16;
173
- if (above >= 420 || above >= below) return { bottom: spot.y + SIZE + GAP, height: Math.max(240, Math.min(640, above)) };
195
+ if (above >= 420 || above >= below) return { bottom: spot.y + s + GAP, height: Math.max(240, Math.min(640, above)) };
174
196
  return { top: viewH - spot.y + GAP, height: Math.max(240, Math.min(640, below)) };
175
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
+ }
176
205
  /** A dragged panel's top-left, inside the window by the margin. Pure. */
177
206
  export function clampPanel(p: { x: number; y: number }, box: { w: number; h: number }, view: { w: number; h: number }): { x: number; y: number } {
178
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))) };
@@ -233,6 +262,8 @@ export interface DeskLook {
233
262
  avoid?: boolean;
234
263
  /** A page's own place (0.19.6): hidden there, or started in a corner at a height — the desk's rules. */
235
264
  places?: PlaceRule[];
265
+ /** ขนาดปุ่ม, px (0.19.10) — 56 when the desk does not say. */
266
+ size?: number;
236
267
  }
237
268
  let deskLook: DeskLook | null = null;
238
269
  /** The face's address for a desk's look: its own path on the support host, the shipped one, or none (the glyph). Pure. */
@@ -251,6 +282,15 @@ function wearLook(look: DeskLook | null): void {
251
282
  const color = look?.color && /^#[0-9a-f]{6}$/i.test(look.color) ? look.color : null;
252
283
  if (color) root.style.setProperty("--lw-chat-color", color);
253
284
  else root.style.removeProperty("--lw-chat-color");
285
+ // Its size (0.19.10): the button drawn so, and every place it stands, the panel and the call's card measured from it.
286
+ const s = bubbleSize(look?.size);
287
+ if (s !== size) {
288
+ size = s;
289
+ if (s === SIZE) root.style.removeProperty("--lw-chat-size");
290
+ else root.style.setProperty("--lw-chat-size", `${s}px`);
291
+ columns = null;
292
+ layout();
293
+ }
254
294
  const face = shadow?.querySelector<HTMLImageElement>(".face");
255
295
  const url = lookFaceUrl(getConfig().host, look);
256
296
  // Its rules and its switch may have changed where it stands.
@@ -329,10 +369,11 @@ function loadPlace(): void {
329
369
  const def = chatPlace(getConfig().chat);
330
370
  if (def) spot = { side: def.side, x: def.x, y: def.y };
331
371
  try {
332
- 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;
333
373
  const b = kept?.bubble;
334
374
  movedByHand = !!b && (b.side === "left" || b.side === "right") && typeof b.y === "number" && Number.isFinite(b.y);
335
- if (movedByHand) spot = { ...spot, side: b!.side as BubbleSpot["side"], y: b!.y as number };
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 } : {}) };
336
377
  const p = kept?.panel;
337
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;
338
379
  opened = kept?.opened === true;
@@ -342,7 +383,7 @@ function loadPlace(): void {
342
383
  }
343
384
  function savePlace(): void {
344
385
  try {
345
- 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 } : {}) }));
346
387
  } catch {
347
388
  /* this visit only */
348
389
  }
@@ -369,7 +410,7 @@ function readColumn(side: Side, x: number): Rect[] {
369
410
  const k = `${side}:${x}`;
370
411
  let boxes = columns.boxes.get(k);
371
412
  if (!boxes) {
372
- const col = side === "right" ? { left: v.w - x - SIZE, right: v.w - x } : { left: x, right: x + SIZE };
413
+ const col = side === "right" ? { left: v.w - x - size, right: v.w - x } : { left: x, right: x + size };
373
414
  boxes = scanColumns([col], v, { extra: avoidSetting().extra })[0] ?? [];
374
415
  columns.boxes.set(k, boxes);
375
416
  }
@@ -384,12 +425,18 @@ function avoidSetting(): { on: boolean; extra: string | null } {
384
425
  if (own === false || deskLook?.avoid === false) return { on: false, extra: null };
385
426
  return { on: true, extra: typeof own === "string" && own.trim() ? own.trim() : null };
386
427
  }
387
- /** Where it wants to be on this page: put by hand — kept; else the desk's rule for the page; else the config's place. */
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
+ */
388
434
  function wanted(): BubbleSpot {
389
435
  const rule = placeFor(pagePath(), deskLook?.places);
390
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;
391
438
  if (movedByHand || !rule || rule.show === "hide") return spot;
392
- 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 };
393
440
  }
394
441
  /** Look at the page afresh — its columns read again as placing asks for them — and place again. */
395
442
  function rescan(): void {
@@ -451,8 +498,9 @@ const onPageSettled = (e: Event) => {
451
498
  if (ours(e.target)) return;
452
499
  const r = e.target instanceof Element ? e.target.getBoundingClientRect() : null;
453
500
  const v = view();
454
- const x = spot.x;
455
- const inColumn = !!r && r.width > 0 && (r.left < x + SIZE || r.right > v.w - x - SIZE);
501
+ // Its column where it stands — far in from the edge too (0.19.11).
502
+ const x = (shown ?? spot).x;
503
+ const inColumn = !!r && r.width > 0 && (r.left < x + size || r.right > v.w - x - size);
456
504
  queueRescan(inColumn ? "soon" : "later");
457
505
  };
458
506
  /** What a person did, or the window itself changed: looked at at once. */
@@ -500,32 +548,36 @@ function layout(): void {
500
548
  if (!bubble || !panel) return;
501
549
  const v = view();
502
550
  const want = wanted();
503
- const wantY = clampBubbleY(want.y, v.h);
551
+ const wantY = clampBubbleY(want.y, v.h, size);
552
+ const wantX = clampBubbleX(want.x, v.w, size);
504
553
  // While the panel is open it stays where it was opened from — never moved under the person's eyes.
505
- const wish = `${pagePath()}|${want.side}|${want.x}|${wantY}`;
554
+ const wish = `${pagePath()}|${want.side}|${wantX}|${wantY}`;
506
555
  const prev = shown && shownFor === wish ? shown : null;
507
- const home = { side: want.side, x: want.x, y: wantY };
556
+ const home = { side: want.side, x: wantX, y: wantY };
508
557
  const placing = !!columns && avoidSetting().on;
509
- const free = !isOpen || !shown ? (placing ? placeSpot(home, readColumn, v, SIZE, MARGIN, Math.max(MARGIN, v.h - SIZE - MARGIN), prev) : home) : shown;
558
+ const free = !isOpen || !shown ? (placing ? placeSpot(home, readColumn, v, size, MARGIN, Math.max(MARGIN, v.h - size - MARGIN), prev) : home) : shown;
510
559
  shown = { side: free.side, x: free.x, y: free.y };
511
560
  shownFor = wish;
512
- if (placing) placedSig = JSON.stringify([blockedRanges(readColumn(home.side, home.x), home.side === "right" ? { left: v.w - home.x - SIZE, right: v.w - home.x } : { left: home.x, right: home.x + SIZE }, v.h, SIZE), shown]);
561
+ if (placing) placedSig = JSON.stringify([blockedRanges(readColumn(home.side, home.x), home.side === "right" ? { left: v.w - home.x - size, right: v.w - home.x } : { left: home.x, right: home.x + size }, v.h, size), shown]);
513
562
  const at = shown;
514
563
  const y = at.y;
515
564
  const other = at.side === "right" ? "left" : "right";
516
565
  bubble.style.display = ruleHidden && !isOpen ? "none" : "";
517
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);
518
570
  if (panelAt) {
519
571
  // The height it had when it was picked up — a drag moves it, never resizes it under the hand.
520
572
  const box = { w: Math.min(PANEL_W, v.w - 32), h: Math.min(panelAt.h, v.h - 2 * MARGIN) };
521
573
  const p = clampPanel(panelAt, box, v);
522
574
  Object.assign(panel.style, { left: `${p.x}px`, top: `${p.y}px`, right: "", bottom: "", height: `${box.h}px` });
523
575
  } else {
524
- const a = anchorPanel(at, v.h);
525
- 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` });
576
+ const a = anchorPanel(at, v.h, size);
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` });
526
578
  }
527
579
  // The call's card and frame sit above the bubble while it is there and low on their side (ring.ts, callFrame.ts).
528
- const lift = root && root.style.display !== "none" && bubble.style.display !== "none" ? liftFor(at) : 0;
580
+ const lift = root && root.style.display !== "none" && bubble.style.display !== "none" ? liftFor(at, size) : 0;
529
581
  // Written only when it differs from what <html> carries (read back, so a host that rewrites its style attribute
530
582
  // gets it again): a property there restyles the host's whole page, and layout() runs on every read of the page.
531
583
  const liftNow = lift ? `${lift}px` : "";
@@ -542,6 +594,14 @@ let pressed: { id: number; sx: number; sy: number; moved: boolean } | null = nul
542
594
  let swallowClick = false;
543
595
  function onBubbleDown(e: PointerEvent): void {
544
596
  if (e.button !== 0) return;
597
+ /*
598
+ * The press is the bubble's alone (0.19.10; the owner, 11-02: "it can move only top/bottom on the right side … it
599
+ * cannot [move to] the left"): the bubble follows the pointer at once, and the browser then looks for what to drag at
600
+ * the point first pressed — the host page's own link or picture under it there (a left menu's link) — and starts
601
+ * dragging THAT, cancelling ours (pointercancel): the bubble went back where it was. Taken here, no native drag or
602
+ * text selection begins; the press is still a click, and the panel is still focused when it opens.
603
+ */
604
+ e.preventDefault();
545
605
  pressed = { id: e.pointerId, sx: e.clientX, sy: e.clientY, moved: false };
546
606
  try {
547
607
  bubble?.setPointerCapture(e.pointerId);
@@ -554,7 +614,7 @@ function onBubbleMove(e: PointerEvent): void {
554
614
  if (!pressed.moved && Math.hypot(e.clientX - pressed.sx, e.clientY - pressed.sy) < 6) return;
555
615
  pressed.moved = true;
556
616
  bubble.classList.add("dragging");
557
- Object.assign(bubble.style, { left: `${e.clientX - SIZE / 2}px`, top: `${e.clientY - SIZE / 2}px`, right: "", bottom: "" });
617
+ Object.assign(bubble.style, { left: `${e.clientX - size / 2}px`, top: `${e.clientY - size / 2}px`, right: "", bottom: "" });
558
618
  }
559
619
  function onBubbleUp(e: PointerEvent): void {
560
620
  if (!pressed || e.pointerId !== pressed.id) return;
@@ -566,7 +626,7 @@ function onBubbleUp(e: PointerEvent): void {
566
626
  swallowClick = true;
567
627
  setTimeout(() => (swallowClick = false), 0);
568
628
  if (e.type === "pointerup") {
569
- spot = settleBubble(e.clientX, e.clientY, view(), chatPlace(getConfig().chat)?.x ?? 16);
629
+ spot = settleBubble(e.clientX, e.clientY, view(), chatPlace(getConfig().chat)?.x ?? 16, size);
570
630
  movedByHand = true;
571
631
  // Moved by hand: the panel opens by it again.
572
632
  panelAt = null;
@@ -772,6 +832,7 @@ export function refreshSupportChat(): Promise<void> {
772
832
 
773
833
  export function unmountSupportChat(): void {
774
834
  deskLook = null;
835
+ size = SIZE;
775
836
  stopFrames?.();
776
837
  stopFrames = null;
777
838
  window.removeEventListener("message", onMessage);