@skippr/live-agent-sdk 0.96.0 → 0.98.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/dist/esm/lib-exports.js +233 -46
- package/dist/skippr-sdk.js +156 -156
- package/dist/types/capture/a11yTree.d.ts +2 -0
- package/dist/types/capture/snapdom.d.ts +11 -1
- package/dist/types/components/DomCapture.d.ts +3 -0
- package/dist/types/context/LiveAgentContext.d.ts +2 -1
- package/dist/types/lib/constants.d.ts +7 -0
- package/package.json +2 -2
package/dist/esm/lib-exports.js
CHANGED
|
@@ -394,6 +394,12 @@ var CHAT_SURFACE = {
|
|
|
394
394
|
Room: "room"
|
|
395
395
|
};
|
|
396
396
|
var DEFAULT_CHAT_SURFACE = CHAT_SURFACE.Line;
|
|
397
|
+
var ACTION_PLAN_MODE = {
|
|
398
|
+
Always: "always",
|
|
399
|
+
Auto: "auto",
|
|
400
|
+
Never: "never"
|
|
401
|
+
};
|
|
402
|
+
var DEFAULT_ACTION_PLAN_MODE = ACTION_PLAN_MODE.Auto;
|
|
397
403
|
var LAUNCHER_MODE = {
|
|
398
404
|
Avatar: "avatar",
|
|
399
405
|
Strip: "strip",
|
|
@@ -2182,7 +2188,11 @@ function isBuddyActive(appearance) {
|
|
|
2182
2188
|
return appearance?.buddy?.enabled === true;
|
|
2183
2189
|
}
|
|
2184
2190
|
function buddyEffectiveControls(base) {
|
|
2185
|
-
const next = {
|
|
2191
|
+
const next = {
|
|
2192
|
+
...base ?? {},
|
|
2193
|
+
micMode: MIC_MODE.PushToTalk,
|
|
2194
|
+
actionPlanMode: ACTION_PLAN_MODE.Never
|
|
2195
|
+
};
|
|
2186
2196
|
delete next.chat;
|
|
2187
2197
|
return next;
|
|
2188
2198
|
}
|
|
@@ -4058,6 +4068,9 @@ function useSession({
|
|
|
4058
4068
|
enabledControls.highlight = true;
|
|
4059
4069
|
if (agentControls?.actions === true)
|
|
4060
4070
|
enabledControls.actions = true;
|
|
4071
|
+
if (agentControls?.actionPlanMode) {
|
|
4072
|
+
enabledControls.actionPlanMode = agentControls.actionPlanMode;
|
|
4073
|
+
}
|
|
4061
4074
|
const micMode = agentControls?.micMode;
|
|
4062
4075
|
if (isRequestResponseMicMode(micMode))
|
|
4063
4076
|
enabledControls.micMode = micMode;
|
|
@@ -4963,6 +4976,7 @@ function accessibleName(element, options = {}) {
|
|
|
4963
4976
|
var REF_PREFIX = "r-";
|
|
4964
4977
|
var VALUE_MAX_CHARS = 80;
|
|
4965
4978
|
var HREF_MAX_CHARS = 60;
|
|
4979
|
+
var TABLE_ROW_MAX_CHARS = 160;
|
|
4966
4980
|
var PAGE_CONTENT_MAX_BYTES = 14000;
|
|
4967
4981
|
var NODE_LIMIT = 1500;
|
|
4968
4982
|
var SENSITIVE_IFRAME_HOSTS = [
|
|
@@ -5308,8 +5322,9 @@ function ensureStableRef(element) {
|
|
|
5308
5322
|
function formatNodeAsLine(entry) {
|
|
5309
5323
|
const indent = " ".repeat(entry.depth);
|
|
5310
5324
|
const namePart = entry.name ? ` "${escapeAttributeValue(entry.name)}"` : "";
|
|
5325
|
+
const refPart = entry.ref ? ` ${entry.ref}` : "";
|
|
5311
5326
|
const attrsPart = entry.attrs.length > 0 ? ` ${entry.attrs.join(" ")}` : "";
|
|
5312
|
-
return `${indent}${entry.role}${namePart}
|
|
5327
|
+
return `${indent}${entry.role}${namePart}${refPart}${attrsPart}`;
|
|
5313
5328
|
}
|
|
5314
5329
|
function shouldSkipSubtree(element) {
|
|
5315
5330
|
if (element.id === WIDGET_ROOT_ID)
|
|
@@ -5337,13 +5352,66 @@ function getSameOriginIframeBody(element) {
|
|
|
5337
5352
|
return null;
|
|
5338
5353
|
}
|
|
5339
5354
|
}
|
|
5340
|
-
function
|
|
5355
|
+
function normalizeText(text) {
|
|
5356
|
+
return (text ?? "").replace(/\s+/g, " ").trim();
|
|
5357
|
+
}
|
|
5358
|
+
function formatTableRowText(row) {
|
|
5359
|
+
const cellValues = Array.from(row.children).filter((cell) => cell.tagName === "TD" || cell.tagName === "TH").map((cell) => normalizeText(cell.textContent).slice(0, VALUE_MAX_CHARS));
|
|
5360
|
+
return cellValues.join(" | ").slice(0, TABLE_ROW_MAX_CHARS).trim();
|
|
5361
|
+
}
|
|
5362
|
+
function isTableHeaderRow(row) {
|
|
5363
|
+
return Array.from(row.children).some((cell) => cell.tagName === "TH");
|
|
5364
|
+
}
|
|
5365
|
+
function directTextOfElement(element) {
|
|
5366
|
+
let combinedText = "";
|
|
5367
|
+
for (const childNode of Array.from(element.childNodes)) {
|
|
5368
|
+
if (childNode.nodeType === Node.TEXT_NODE)
|
|
5369
|
+
combinedText += `${childNode.textContent} `;
|
|
5370
|
+
}
|
|
5371
|
+
return normalizeText(combinedText);
|
|
5372
|
+
}
|
|
5373
|
+
var REFLESS_TEXT_ENTRY_REF = "";
|
|
5374
|
+
function collectRefLessTextLeaf(text, depth, rect, entries) {
|
|
5375
|
+
entries.push({
|
|
5376
|
+
depth,
|
|
5377
|
+
role: "text",
|
|
5378
|
+
name: text.slice(0, VALUE_MAX_CHARS),
|
|
5379
|
+
ref: REFLESS_TEXT_ENTRY_REF,
|
|
5380
|
+
attrs: [],
|
|
5381
|
+
area: rect.width * rect.height
|
|
5382
|
+
});
|
|
5383
|
+
}
|
|
5384
|
+
function collectTableRowAsSingleEntry(row, depth, rect, entries) {
|
|
5385
|
+
const rowText = formatTableRowText(row);
|
|
5386
|
+
if (!rowText)
|
|
5387
|
+
return false;
|
|
5388
|
+
entries.push({
|
|
5389
|
+
depth,
|
|
5390
|
+
role: "row",
|
|
5391
|
+
name: rowText,
|
|
5392
|
+
ref: ensureStableRef(row),
|
|
5393
|
+
attrs: isTableHeaderRow(row) ? ["header"] : [],
|
|
5394
|
+
area: rect.width * rect.height
|
|
5395
|
+
});
|
|
5396
|
+
for (const child of Array.from(row.children)) {
|
|
5397
|
+
walkAndCollect(child, depth + 1, entries, true);
|
|
5398
|
+
}
|
|
5399
|
+
return true;
|
|
5400
|
+
}
|
|
5401
|
+
function walkAndCollect(element, depth, entries, suppressTextLeaves = false) {
|
|
5341
5402
|
if (shouldSkipSubtree(element))
|
|
5342
5403
|
return;
|
|
5343
5404
|
const rect = element.getBoundingClientRect();
|
|
5405
|
+
if (element.tagName === "TR") {
|
|
5406
|
+
if (!isVisible(element, rect))
|
|
5407
|
+
return;
|
|
5408
|
+
if (collectTableRowAsSingleEntry(element, depth, rect, entries))
|
|
5409
|
+
return;
|
|
5410
|
+
}
|
|
5344
5411
|
const role = inferRole(element) ?? (isCursorClickable(element, rect) ? "button" : null);
|
|
5345
5412
|
const isVisibleAndEmittable = role !== null && isVisible(element, rect) && shouldEmitElement(element, role);
|
|
5346
5413
|
let childDepth = depth;
|
|
5414
|
+
let childSuppressTextLeaves = suppressTextLeaves;
|
|
5347
5415
|
if (isVisibleAndEmittable && role) {
|
|
5348
5416
|
const ref = ensureStableRef(element);
|
|
5349
5417
|
const name = accessibleName(element);
|
|
@@ -5356,12 +5424,20 @@ function walkAndCollect(element, depth, entries) {
|
|
|
5356
5424
|
area: rect.width * rect.height
|
|
5357
5425
|
});
|
|
5358
5426
|
childDepth = depth + 1;
|
|
5427
|
+
const nameAlreadyCoversSubtreeText = name !== "";
|
|
5428
|
+
if (nameAlreadyCoversSubtreeText)
|
|
5429
|
+
childSuppressTextLeaves = true;
|
|
5430
|
+
} else if (!suppressTextLeaves) {
|
|
5431
|
+
const ownText = directTextOfElement(element);
|
|
5432
|
+
if (ownText && isVisible(element, rect)) {
|
|
5433
|
+
collectRefLessTextLeaf(ownText, depth, rect, entries);
|
|
5434
|
+
}
|
|
5359
5435
|
}
|
|
5360
5436
|
if (role === "iframe") {
|
|
5361
5437
|
const iframeBody = getSameOriginIframeBody(element);
|
|
5362
5438
|
if (iframeBody) {
|
|
5363
5439
|
for (const child of Array.from(iframeBody.children)) {
|
|
5364
|
-
walkAndCollect(child, childDepth, entries);
|
|
5440
|
+
walkAndCollect(child, childDepth, entries, childSuppressTextLeaves);
|
|
5365
5441
|
}
|
|
5366
5442
|
}
|
|
5367
5443
|
return;
|
|
@@ -5369,15 +5445,19 @@ function walkAndCollect(element, depth, entries) {
|
|
|
5369
5445
|
if (role && isBlindRegion(role))
|
|
5370
5446
|
return;
|
|
5371
5447
|
for (const child of Array.from(element.children)) {
|
|
5372
|
-
walkAndCollect(child, childDepth, entries);
|
|
5448
|
+
walkAndCollect(child, childDepth, entries, childSuppressTextLeaves);
|
|
5373
5449
|
}
|
|
5374
5450
|
}
|
|
5375
|
-
|
|
5451
|
+
var TRUNCATION_DROP_ORDER_BY_ROLE = { text: 0, row: 1 };
|
|
5452
|
+
function truncationDropOrder(entry) {
|
|
5453
|
+
return TRUNCATION_DROP_ORDER_BY_ROLE[entry.role] ?? 2;
|
|
5454
|
+
}
|
|
5455
|
+
function dropInformationalEntriesFirstUntilUnderLimit(entries) {
|
|
5376
5456
|
if (entries.length <= NODE_LIMIT)
|
|
5377
5457
|
return entries;
|
|
5378
5458
|
const indexedEntries = entries.map((entry, index2) => ({ entry, index: index2 }));
|
|
5379
|
-
const
|
|
5380
|
-
const indicesToDrop = new Set(
|
|
5459
|
+
const entriesMostDroppableFirst = indexedEntries.slice(1).sort((a, b) => truncationDropOrder(a.entry) - truncationDropOrder(b.entry) || a.entry.area - b.entry.area);
|
|
5460
|
+
const indicesToDrop = new Set(entriesMostDroppableFirst.slice(0, entries.length - NODE_LIMIT).map((indexed) => indexed.index));
|
|
5381
5461
|
const keptEntries = [];
|
|
5382
5462
|
for (let i = 0;i < entries.length; i++) {
|
|
5383
5463
|
if (!indicesToDrop.has(i))
|
|
@@ -5402,7 +5482,7 @@ function buildAccessibilityTree() {
|
|
|
5402
5482
|
}
|
|
5403
5483
|
}
|
|
5404
5484
|
const exceededNodeLimit = collectedEntries.length > NODE_LIMIT;
|
|
5405
|
-
const finalEntries = exceededNodeLimit ?
|
|
5485
|
+
const finalEntries = exceededNodeLimit ? dropInformationalEntriesFirstUntilUnderLimit(collectedEntries) : collectedEntries;
|
|
5406
5486
|
const treeLines = finalEntries.map(formatNodeAsLine);
|
|
5407
5487
|
let pageContent = treeLines.join(`
|
|
5408
5488
|
`);
|
|
@@ -5777,21 +5857,46 @@ function paintCursorOverlay(ctx, transform, brand = DEFAULT_BRAND_PALETTE) {
|
|
|
5777
5857
|
}
|
|
5778
5858
|
|
|
5779
5859
|
// src/capture/snapdom.ts
|
|
5860
|
+
var UNLOADABLE_IMAGE_PLACEHOLDER_FILL = "#e2e8f0";
|
|
5861
|
+
function placeholderForUnloadableImage({
|
|
5862
|
+
width = 100,
|
|
5863
|
+
height = 100
|
|
5864
|
+
}) {
|
|
5865
|
+
const placeholderSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><rect width="100%" height="100%" fill="${UNLOADABLE_IMAGE_PLACEHOLDER_FILL}"/></svg>`;
|
|
5866
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(placeholderSvg)}`;
|
|
5867
|
+
}
|
|
5780
5868
|
var cachedSnapdomModule = null;
|
|
5781
5869
|
function loadSnapdom() {
|
|
5782
|
-
if (!cachedSnapdomModule)
|
|
5870
|
+
if (!cachedSnapdomModule) {
|
|
5783
5871
|
cachedSnapdomModule = import("@zumer/snapdom");
|
|
5872
|
+
}
|
|
5784
5873
|
return cachedSnapdomModule;
|
|
5785
5874
|
}
|
|
5875
|
+
function preCacheSnapshotResources() {
|
|
5876
|
+
loadSnapdom().then(({ preCache }) => preCache(document, { embedFonts: true })).catch(() => {
|
|
5877
|
+
return;
|
|
5878
|
+
});
|
|
5879
|
+
}
|
|
5786
5880
|
async function snapToCanvas(element, options = {}) {
|
|
5787
5881
|
const snapdomModule = await loadSnapdom();
|
|
5788
|
-
|
|
5882
|
+
const optionsWithEngineDefaults = {
|
|
5883
|
+
embedFonts: true,
|
|
5884
|
+
fallbackURL: placeholderForUnloadableImage,
|
|
5885
|
+
...options,
|
|
5886
|
+
snap: snapdomModule.snapdom
|
|
5887
|
+
};
|
|
5888
|
+
return snapdomModule.snapdom.toCanvas(element, optionsWithEngineDefaults);
|
|
5789
5889
|
}
|
|
5790
5890
|
|
|
5791
5891
|
// src/components/DomCapture.tsx
|
|
5792
5892
|
var SNAPSHOT_INTERVAL_MS = 3000;
|
|
5793
5893
|
var A11Y_PUBLISH_INTERVAL_MS = 2000;
|
|
5794
5894
|
var FIRST_SNAPSHOT_DELAY_MS = 400;
|
|
5895
|
+
var RASTERIZE_TIMEOUT_MS = 1e4;
|
|
5896
|
+
var FULL_QUALITY_TIMEOUTS_BEFORE_COOLDOWN = 1;
|
|
5897
|
+
var FAST_CAPTURES_BEFORE_FULL_QUALITY_RETRY = 10;
|
|
5898
|
+
var RASTERIZE_TIMED_OUT = Symbol("rasterize-timed-out");
|
|
5899
|
+
var SCROLL_SETTLE_MS = 300;
|
|
5795
5900
|
var CAPTURE_PRESET = ScreenSharePresets2.h1080fps30;
|
|
5796
5901
|
var CAPTURE_FPS = CAPTURE_PRESET.encoding.maxFramerate ?? 30;
|
|
5797
5902
|
var CAPTURE_BITRATE = 4000000;
|
|
@@ -5799,15 +5904,20 @@ var DOM_SNAPSHOT_GZIP_THRESHOLD_BYTES = 14000;
|
|
|
5799
5904
|
var MAX_CANVAS_DPR = 1.5;
|
|
5800
5905
|
var CONSECUTIVE_FAILURES_BEFORE_REPORT = 3;
|
|
5801
5906
|
var textEncoder4 = new TextEncoder;
|
|
5907
|
+
var SNAPSHOT_EXCLUDE_SELECTORS = [`#${WIDGET_ROOT_ID}`, `[${PRIVATE_ATTR}]`];
|
|
5908
|
+
function isCapturableIframe(element) {
|
|
5909
|
+
return isSameOriginIframe(element) && !isSensitiveIframe(element);
|
|
5910
|
+
}
|
|
5802
5911
|
function shouldIncludeInSnapshot(element) {
|
|
5803
|
-
if (
|
|
5804
|
-
return true;
|
|
5805
|
-
if (element.id === WIDGET_ROOT_ID)
|
|
5806
|
-
return false;
|
|
5807
|
-
if (element.hasAttribute?.(PRIVATE_ATTR))
|
|
5912
|
+
if (element instanceof HTMLIFrameElement && !isCapturableIframe(element))
|
|
5808
5913
|
return false;
|
|
5809
5914
|
return true;
|
|
5810
5915
|
}
|
|
5916
|
+
function shouldIncludeInFirstFrame(element) {
|
|
5917
|
+
if (element.tagName === "IMG")
|
|
5918
|
+
return false;
|
|
5919
|
+
return shouldIncludeInSnapshot(element);
|
|
5920
|
+
}
|
|
5811
5921
|
function createPublishingCanvas() {
|
|
5812
5922
|
const dprBoost = Math.min(window.devicePixelRatio || 1, MAX_CANVAS_DPR);
|
|
5813
5923
|
const canvas = document.createElement("canvas");
|
|
@@ -5828,22 +5938,23 @@ function getCanvasCaptureStream(canvas, fps) {
|
|
|
5828
5938
|
}
|
|
5829
5939
|
return captureStreamFn.call(canvas, fps);
|
|
5830
5940
|
}
|
|
5831
|
-
async function rasterizeViewportToPageLayer(width, height) {
|
|
5832
|
-
const
|
|
5941
|
+
async function rasterizeViewportToPageLayer(width, height, { skipCrossOriginImageSettling, includeImages }) {
|
|
5942
|
+
const requestedDpr = Math.min(window.devicePixelRatio || 1, MAX_CANVAS_DPR);
|
|
5943
|
+
const viewportWidthAtCaptureStart = window.innerWidth;
|
|
5833
5944
|
const snapshotCanvas = await snapToCanvas(document.documentElement, {
|
|
5834
|
-
|
|
5835
|
-
|
|
5945
|
+
exclude: SNAPSHOT_EXCLUDE_SELECTORS,
|
|
5946
|
+
excludeMode: "remove",
|
|
5947
|
+
filter: includeImages ? shouldIncludeInSnapshot : shouldIncludeInFirstFrame,
|
|
5948
|
+
filterMode: "hide",
|
|
5836
5949
|
backgroundColor: "#ffffff",
|
|
5837
|
-
fast:
|
|
5838
|
-
dpr
|
|
5950
|
+
fast: skipCrossOriginImageSettling,
|
|
5951
|
+
dpr: requestedDpr,
|
|
5952
|
+
clip: "viewport"
|
|
5839
5953
|
});
|
|
5840
|
-
const
|
|
5841
|
-
const
|
|
5842
|
-
const
|
|
5843
|
-
const
|
|
5844
|
-
const fitScale = Math.min(width / sourceWidth, height / sourceHeight);
|
|
5845
|
-
const destWidth = sourceWidth * fitScale;
|
|
5846
|
-
const destHeight = sourceHeight * fitScale;
|
|
5954
|
+
const canvasPixelsPerCssPixel = viewportWidthAtCaptureStart > 0 ? snapshotCanvas.width / viewportWidthAtCaptureStart : requestedDpr;
|
|
5955
|
+
const fitScale = Math.min(width / snapshotCanvas.width, height / snapshotCanvas.height);
|
|
5956
|
+
const destWidth = snapshotCanvas.width * fitScale;
|
|
5957
|
+
const destHeight = snapshotCanvas.height * fitScale;
|
|
5847
5958
|
const destX = (width - destWidth) / 2;
|
|
5848
5959
|
const destY = (height - destHeight) / 2;
|
|
5849
5960
|
const layer = document.createElement("canvas");
|
|
@@ -5854,10 +5965,14 @@ async function rasterizeViewportToPageLayer(width, height) {
|
|
|
5854
5965
|
throw new Error("Failed to get 2D context for page layer");
|
|
5855
5966
|
layerCtx.fillStyle = "#ffffff";
|
|
5856
5967
|
layerCtx.fillRect(0, 0, width, height);
|
|
5857
|
-
layerCtx.drawImage(snapshotCanvas,
|
|
5968
|
+
layerCtx.drawImage(snapshotCanvas, destX, destY, destWidth, destHeight);
|
|
5858
5969
|
return {
|
|
5859
5970
|
canvas: layer,
|
|
5860
|
-
viewportToCanvas: {
|
|
5971
|
+
viewportToCanvas: {
|
|
5972
|
+
scale: canvasPixelsPerCssPixel * fitScale,
|
|
5973
|
+
offsetX: destX,
|
|
5974
|
+
offsetY: destY
|
|
5975
|
+
}
|
|
5861
5976
|
};
|
|
5862
5977
|
}
|
|
5863
5978
|
function compositePageAndCursorOverlay(ctx, canvas, cachedPage, brand) {
|
|
@@ -5881,6 +5996,12 @@ async function gzipIfBeneficial(snapshotBytes) {
|
|
|
5881
5996
|
return { bytes: snapshotBytes, compressionFlag: 0 };
|
|
5882
5997
|
}
|
|
5883
5998
|
}
|
|
5999
|
+
function frameWithCompressionFlag(compressionFlag, payload) {
|
|
6000
|
+
const frame = new Uint8Array(payload.byteLength + 1);
|
|
6001
|
+
frame[0] = compressionFlag;
|
|
6002
|
+
frame.set(payload, 1);
|
|
6003
|
+
return frame;
|
|
6004
|
+
}
|
|
5884
6005
|
async function buildDomSnapshotFrame() {
|
|
5885
6006
|
const a11yTree = buildAccessibilityTree();
|
|
5886
6007
|
const snapshotJson = JSON.stringify({
|
|
@@ -5893,14 +6014,12 @@ async function buildDomSnapshotFrame() {
|
|
|
5893
6014
|
});
|
|
5894
6015
|
const snapshotBytes = textEncoder4.encode(snapshotJson);
|
|
5895
6016
|
const { bytes, compressionFlag } = await gzipIfBeneficial(snapshotBytes);
|
|
5896
|
-
|
|
5897
|
-
frame[0] = compressionFlag;
|
|
5898
|
-
frame.set(bytes, 1);
|
|
5899
|
-
return frame;
|
|
6017
|
+
return frameWithCompressionFlag(compressionFlag, bytes);
|
|
5900
6018
|
}
|
|
5901
6019
|
function reportCaptureError(localParticipant) {
|
|
5902
6020
|
try {
|
|
5903
|
-
|
|
6021
|
+
const payload = textEncoder4.encode(JSON.stringify({ type: "capture_error" }));
|
|
6022
|
+
localParticipant.publishData(frameWithCompressionFlag(0, payload), {
|
|
5904
6023
|
reliable: true,
|
|
5905
6024
|
topic: DOM_SNAPSHOT_TOPIC
|
|
5906
6025
|
}).catch(() => {
|
|
@@ -5943,12 +6062,18 @@ function DomCapture({ pushOrTapMicMode = false }) {
|
|
|
5943
6062
|
return;
|
|
5944
6063
|
videoTrack.contentHint = "text";
|
|
5945
6064
|
didStartRef.current = true;
|
|
6065
|
+
preCacheSnapshotResources();
|
|
5946
6066
|
let cancelled = false;
|
|
5947
6067
|
let snapshotInFlight = false;
|
|
5948
6068
|
let a11yPublishInFlight = false;
|
|
5949
6069
|
let a11yPublishPending = false;
|
|
5950
6070
|
let consecutiveCaptureFailures = 0;
|
|
5951
6071
|
let cachedPage = null;
|
|
6072
|
+
let rasterizeSequence = 0;
|
|
6073
|
+
let lastCommittedSequence = 0;
|
|
6074
|
+
let firstFrameCommitted = false;
|
|
6075
|
+
let capturesUntilFullQualityRetry = 0;
|
|
6076
|
+
let consecutiveFullQualityTimeouts = 0;
|
|
5952
6077
|
if (!pushOrTapMicMode && !localParticipant.isMicrophoneEnabled) {
|
|
5953
6078
|
localParticipant.setMicrophoneEnabled(true).catch((error) => console.error("Failed to enable microphone:", error));
|
|
5954
6079
|
}
|
|
@@ -5964,16 +6089,61 @@ function DomCapture({ pushOrTapMicMode = false }) {
|
|
|
5964
6089
|
for (const track of canvasStream.getTracks())
|
|
5965
6090
|
track.stop();
|
|
5966
6091
|
});
|
|
5967
|
-
const
|
|
5968
|
-
if (cancelled ||
|
|
6092
|
+
const commitPageLayerIfNewest = (sequence, page) => {
|
|
6093
|
+
if (cancelled || pausedRef.current || sequence <= lastCommittedSequence)
|
|
6094
|
+
return false;
|
|
6095
|
+
lastCommittedSequence = sequence;
|
|
6096
|
+
cachedPage = page;
|
|
6097
|
+
compositePageAndCursorOverlay(ctx, canvas, page, brandRef.current);
|
|
6098
|
+
firstFrameCommitted = true;
|
|
6099
|
+
return true;
|
|
6100
|
+
};
|
|
6101
|
+
const rasterizeAndCommitWaitingOutTimeout = async (options) => {
|
|
6102
|
+
const sequence = ++rasterizeSequence;
|
|
6103
|
+
const captureInProgress = rasterizeViewportToPageLayer(canvas.width, canvas.height, options);
|
|
6104
|
+
const captureOrTimeout = await Promise.race([
|
|
6105
|
+
captureInProgress,
|
|
6106
|
+
new Promise((resolve) => setTimeout(() => resolve(RASTERIZE_TIMED_OUT), RASTERIZE_TIMEOUT_MS))
|
|
6107
|
+
]);
|
|
6108
|
+
if (captureOrTimeout === RASTERIZE_TIMED_OUT) {
|
|
6109
|
+
captureInProgress.then((lateCapture) => commitPageLayerIfNewest(sequence, lateCapture)).catch(() => {
|
|
6110
|
+
return;
|
|
6111
|
+
});
|
|
6112
|
+
return { timedOut: true };
|
|
6113
|
+
}
|
|
6114
|
+
commitPageLayerIfNewest(sequence, captureOrTimeout);
|
|
6115
|
+
return { timedOut: false };
|
|
6116
|
+
};
|
|
6117
|
+
let followUpRefreshQueued = false;
|
|
6118
|
+
const refreshPageAndComposite = async ({ queueWhenBusy = false } = {}) => {
|
|
6119
|
+
if (cancelled || snapshotInFlight || pausedRef.current) {
|
|
6120
|
+
if (snapshotInFlight && !cancelled && queueWhenBusy)
|
|
6121
|
+
followUpRefreshQueued = true;
|
|
5969
6122
|
return;
|
|
6123
|
+
}
|
|
5970
6124
|
snapshotInFlight = true;
|
|
5971
6125
|
try {
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
6126
|
+
const isImagelessFirstFrame = !firstFrameCommitted;
|
|
6127
|
+
const useFullQuality = firstFrameCommitted && capturesUntilFullQualityRetry === 0;
|
|
6128
|
+
const { timedOut } = await rasterizeAndCommitWaitingOutTimeout({
|
|
6129
|
+
skipCrossOriginImageSettling: !useFullQuality,
|
|
6130
|
+
includeImages: !isImagelessFirstFrame
|
|
6131
|
+
});
|
|
6132
|
+
if (isImagelessFirstFrame && firstFrameCommitted)
|
|
6133
|
+
followUpRefreshQueued = true;
|
|
5976
6134
|
consecutiveCaptureFailures = 0;
|
|
6135
|
+
if (useFullQuality) {
|
|
6136
|
+
if (timedOut) {
|
|
6137
|
+
consecutiveFullQualityTimeouts += 1;
|
|
6138
|
+
if (consecutiveFullQualityTimeouts >= FULL_QUALITY_TIMEOUTS_BEFORE_COOLDOWN) {
|
|
6139
|
+
capturesUntilFullQualityRetry = FAST_CAPTURES_BEFORE_FULL_QUALITY_RETRY;
|
|
6140
|
+
}
|
|
6141
|
+
} else {
|
|
6142
|
+
consecutiveFullQualityTimeouts = 0;
|
|
6143
|
+
}
|
|
6144
|
+
} else if (capturesUntilFullQualityRetry > 0) {
|
|
6145
|
+
capturesUntilFullQualityRetry -= 1;
|
|
6146
|
+
}
|
|
5977
6147
|
} catch {
|
|
5978
6148
|
if (cancelled)
|
|
5979
6149
|
return;
|
|
@@ -5983,6 +6153,10 @@ function DomCapture({ pushOrTapMicMode = false }) {
|
|
|
5983
6153
|
}
|
|
5984
6154
|
} finally {
|
|
5985
6155
|
snapshotInFlight = false;
|
|
6156
|
+
if (followUpRefreshQueued && !cancelled) {
|
|
6157
|
+
followUpRefreshQueued = false;
|
|
6158
|
+
refreshPageAndComposite();
|
|
6159
|
+
}
|
|
5986
6160
|
}
|
|
5987
6161
|
};
|
|
5988
6162
|
let overlayAnimationFrame = null;
|
|
@@ -6048,20 +6222,30 @@ function DomCapture({ pushOrTapMicMode = false }) {
|
|
|
6048
6222
|
});
|
|
6049
6223
|
const cleanupSnapshotRequest = onSnapshotRequest(() => {
|
|
6050
6224
|
tickA11yPublish();
|
|
6051
|
-
refreshPageAndComposite();
|
|
6225
|
+
refreshPageAndComposite({ queueWhenBusy: true });
|
|
6052
6226
|
});
|
|
6227
|
+
let scrollSettleTimer = null;
|
|
6228
|
+
const refreshOnceScrollSettles = () => {
|
|
6229
|
+
if (scrollSettleTimer)
|
|
6230
|
+
clearTimeout(scrollSettleTimer);
|
|
6231
|
+
scrollSettleTimer = setTimeout(() => {
|
|
6232
|
+
tickA11yPublish();
|
|
6233
|
+
refreshPageAndComposite({ queueWhenBusy: true });
|
|
6234
|
+
}, SCROLL_SETTLE_MS);
|
|
6235
|
+
};
|
|
6236
|
+
window.addEventListener("scroll", refreshOnceScrollSettles, { passive: true, capture: true });
|
|
6053
6237
|
let snapshotTimer = null;
|
|
6054
6238
|
const scheduleNextSnapshot = (delay) => {
|
|
6055
6239
|
if (cancelled)
|
|
6056
6240
|
return;
|
|
6057
|
-
snapshotTimer = setTimeout(
|
|
6058
|
-
|
|
6241
|
+
snapshotTimer = setTimeout(() => {
|
|
6242
|
+
refreshPageAndComposite();
|
|
6059
6243
|
scheduleNextSnapshot(SNAPSHOT_INTERVAL_MS);
|
|
6060
6244
|
}, delay);
|
|
6061
6245
|
};
|
|
6062
6246
|
scheduleNextSnapshot(FIRST_SNAPSHOT_DELAY_MS);
|
|
6063
6247
|
tickA11yPublish();
|
|
6064
|
-
const a11yPublishTimer = setInterval(tickA11yPublish, A11Y_PUBLISH_INTERVAL_MS);
|
|
6248
|
+
const a11yPublishTimer = setInterval(() => void tickA11yPublish(), A11Y_PUBLISH_INTERVAL_MS);
|
|
6065
6249
|
freezeCaptureRef.current = () => {
|
|
6066
6250
|
pausedRef.current = true;
|
|
6067
6251
|
drawPausedOverlay(canvas, ctx);
|
|
@@ -6078,6 +6262,9 @@ function DomCapture({ pushOrTapMicMode = false }) {
|
|
|
6078
6262
|
resumeCaptureRef.current = null;
|
|
6079
6263
|
if (snapshotTimer)
|
|
6080
6264
|
clearTimeout(snapshotTimer);
|
|
6265
|
+
if (scrollSettleTimer)
|
|
6266
|
+
clearTimeout(scrollSettleTimer);
|
|
6267
|
+
window.removeEventListener("scroll", refreshOnceScrollSettles, { capture: true });
|
|
6081
6268
|
clearInterval(a11yPublishTimer);
|
|
6082
6269
|
if (overlayAnimationFrame !== null)
|
|
6083
6270
|
cancelAnimationFrame(overlayAnimationFrame);
|