@skippr/live-agent-sdk 0.27.0 → 0.28.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/README.md +2 -1
- package/dist/esm/lib-exports.js +952 -70
- package/dist/skippr-sdk.js +153 -144
- package/dist/types/capture/a11yTree.d.ts +11 -0
- package/dist/types/capture/a11yUtils.d.ts +6 -0
- package/dist/types/capture/domEvents.d.ts +31 -0
- package/dist/types/capture/snapdom.d.ts +2 -0
- package/dist/types/components/DomCapture.d.ts +1 -0
- package/dist/types/components/LiveAgent.d.ts +3 -1
- package/dist/types/components/MeetingControls.d.ts +2 -1
- package/dist/types/context/LiveAgentContext.d.ts +2 -0
- package/dist/types/hooks/useAgentVoiceState.d.ts +1 -1
- package/dist/types/hooks/useSession.d.ts +3 -1
- package/dist/types/lib/constants.d.ts +6 -0
- package/package.json +2 -1
package/dist/esm/lib-exports.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
1
9
|
// src/components/LiveAgent.tsx
|
|
2
10
|
import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
|
|
3
|
-
import { useCallback as useCallback6, useEffect as
|
|
11
|
+
import { useCallback as useCallback6, useEffect as useEffect13, useMemo as useMemo5, useRef as useRef7, useState as useState8 } from "react";
|
|
4
12
|
|
|
5
13
|
// src/context/LiveAgentContext.tsx
|
|
6
14
|
import { createContext } from "react";
|
|
@@ -165,7 +173,13 @@ async function exchangeForBearerToken(appKey, userToken) {
|
|
|
165
173
|
const { token } = await resp.json();
|
|
166
174
|
return token;
|
|
167
175
|
}
|
|
168
|
-
function useSession({
|
|
176
|
+
function useSession({
|
|
177
|
+
agentId,
|
|
178
|
+
captureMode = "screenshare",
|
|
179
|
+
authToken,
|
|
180
|
+
appKey,
|
|
181
|
+
userToken
|
|
182
|
+
}) {
|
|
169
183
|
const [connection, setConnection] = useState2(null);
|
|
170
184
|
const [shouldConnect, setShouldConnect] = useState2(false);
|
|
171
185
|
const [isStarting, setIsStarting] = useState2(false);
|
|
@@ -202,12 +216,14 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
202
216
|
setError("");
|
|
203
217
|
setErrorCode(null);
|
|
204
218
|
let screenStream = null;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
219
|
+
if (captureMode === "screenshare") {
|
|
220
|
+
try {
|
|
221
|
+
screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
222
|
+
video: { displaySurface: "browser" }
|
|
223
|
+
});
|
|
224
|
+
} catch {
|
|
225
|
+
screenStream = null;
|
|
226
|
+
}
|
|
211
227
|
}
|
|
212
228
|
const headers = { Authorization: `Bearer ${bearerToken}` };
|
|
213
229
|
try {
|
|
@@ -215,7 +231,7 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
215
231
|
method: "POST",
|
|
216
232
|
credentials: "omit",
|
|
217
233
|
headers: { "Content-Type": "application/json", ...headers },
|
|
218
|
-
body: JSON.stringify({ agentId })
|
|
234
|
+
body: JSON.stringify({ agentId, captureMode })
|
|
219
235
|
});
|
|
220
236
|
if (!createResp.ok) {
|
|
221
237
|
const body = await createResp.json().catch(() => null);
|
|
@@ -250,7 +266,7 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
250
266
|
} finally {
|
|
251
267
|
setIsStarting(false);
|
|
252
268
|
}
|
|
253
|
-
}, [agentId, bearerToken]);
|
|
269
|
+
}, [agentId, captureMode, bearerToken]);
|
|
254
270
|
const disconnect = useCallback2(async () => {
|
|
255
271
|
if (sessionId && bearerToken) {
|
|
256
272
|
try {
|
|
@@ -283,6 +299,15 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
283
299
|
pendingScreenStream
|
|
284
300
|
};
|
|
285
301
|
}
|
|
302
|
+
|
|
303
|
+
// src/lib/constants.ts
|
|
304
|
+
var SIDEBAR_WIDTH = 360;
|
|
305
|
+
var WIDGET_ROOT_ID = "skippr-sdk-root";
|
|
306
|
+
var REF_ATTR = "data-skippr-ref";
|
|
307
|
+
var PRIVATE_ATTR = "data-skippr-private";
|
|
308
|
+
var DOM_SNAPSHOT_TOPIC = "skippr.dom-snapshot";
|
|
309
|
+
var DOM_EVENTS_TOPIC = "skippr.dom-events";
|
|
310
|
+
var NAME_MAX_CHARS = 80;
|
|
286
311
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
|
|
287
312
|
import { forwardRef as forwardRef2, createElement as createElement3 } from "react";
|
|
288
313
|
|
|
@@ -513,7 +538,7 @@ var __iconNode16 = [
|
|
|
513
538
|
];
|
|
514
539
|
var Send = createLucideIcon("send", __iconNode16);
|
|
515
540
|
// src/hooks/useAgentVoiceState.ts
|
|
516
|
-
import { useVoiceAssistant } from "@livekit/components-react";
|
|
541
|
+
import { useVoiceAssistant } from "@livekit/components-react/hooks";
|
|
517
542
|
function useAgentVoiceState() {
|
|
518
543
|
const { state } = useVoiceAssistant();
|
|
519
544
|
return {
|
|
@@ -535,7 +560,7 @@ function useLiveAgent() {
|
|
|
535
560
|
}
|
|
536
561
|
|
|
537
562
|
// src/hooks/useMediaControls.ts
|
|
538
|
-
import { useLocalParticipant } from "@livekit/components-react";
|
|
563
|
+
import { useLocalParticipant } from "@livekit/components-react/hooks";
|
|
539
564
|
import { ScreenSharePresets } from "livekit-client";
|
|
540
565
|
import { useCallback as useCallback3 } from "react";
|
|
541
566
|
var SCREEN_SHARE_OPTIONS = {
|
|
@@ -682,7 +707,7 @@ function SpeakingBars() {
|
|
|
682
707
|
}
|
|
683
708
|
|
|
684
709
|
// src/components/AutoStartMedia.tsx
|
|
685
|
-
import { useConnectionState, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
|
|
710
|
+
import { useConnectionState, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react/hooks";
|
|
686
711
|
import { ConnectionState, Track } from "livekit-client";
|
|
687
712
|
import { useEffect as useEffect3, useRef } from "react";
|
|
688
713
|
function AutoStartMedia({ pendingScreenStream }) {
|
|
@@ -711,8 +736,853 @@ function AutoStartMedia({ pendingScreenStream }) {
|
|
|
711
736
|
return null;
|
|
712
737
|
}
|
|
713
738
|
|
|
739
|
+
// src/components/DomCapture.tsx
|
|
740
|
+
import { useConnectionState as useConnectionState2, useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react/hooks";
|
|
741
|
+
import { ConnectionState as ConnectionState2, ScreenSharePresets as ScreenSharePresets2, Track as Track2 } from "livekit-client";
|
|
742
|
+
import { useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
743
|
+
|
|
744
|
+
// src/capture/a11yUtils.ts
|
|
745
|
+
var ROLE_BY_TAG = {
|
|
746
|
+
a: "link",
|
|
747
|
+
button: "button",
|
|
748
|
+
input: "input",
|
|
749
|
+
select: "select",
|
|
750
|
+
textarea: "textarea",
|
|
751
|
+
label: "label",
|
|
752
|
+
nav: "navigation",
|
|
753
|
+
main: "main",
|
|
754
|
+
header: "header",
|
|
755
|
+
footer: "footer",
|
|
756
|
+
aside: "complementary",
|
|
757
|
+
form: "form",
|
|
758
|
+
h1: "heading",
|
|
759
|
+
h2: "heading",
|
|
760
|
+
h3: "heading",
|
|
761
|
+
h4: "heading",
|
|
762
|
+
h5: "heading",
|
|
763
|
+
h6: "heading",
|
|
764
|
+
img: "image",
|
|
765
|
+
ul: "list",
|
|
766
|
+
ol: "list",
|
|
767
|
+
li: "listitem",
|
|
768
|
+
table: "table",
|
|
769
|
+
tr: "row",
|
|
770
|
+
td: "cell",
|
|
771
|
+
th: "columnheader",
|
|
772
|
+
video: "video",
|
|
773
|
+
iframe: "iframe",
|
|
774
|
+
canvas: "canvas",
|
|
775
|
+
audio: "audio",
|
|
776
|
+
embed: "embed",
|
|
777
|
+
object: "object"
|
|
778
|
+
};
|
|
779
|
+
function inferRole(element) {
|
|
780
|
+
const explicitRole = element.getAttribute("role");
|
|
781
|
+
if (explicitRole)
|
|
782
|
+
return explicitRole;
|
|
783
|
+
const tagName = element.tagName.toLowerCase();
|
|
784
|
+
if (tagName === "input") {
|
|
785
|
+
const inputType = element.type;
|
|
786
|
+
if (inputType === "checkbox")
|
|
787
|
+
return "checkbox";
|
|
788
|
+
if (inputType === "radio")
|
|
789
|
+
return "radio";
|
|
790
|
+
if (inputType === "submit" || inputType === "button")
|
|
791
|
+
return "button";
|
|
792
|
+
return "input";
|
|
793
|
+
}
|
|
794
|
+
return ROLE_BY_TAG[tagName] ?? null;
|
|
795
|
+
}
|
|
796
|
+
function accessibleName(element, options = {}) {
|
|
797
|
+
const ariaLabel = element.getAttribute("aria-label");
|
|
798
|
+
if (ariaLabel)
|
|
799
|
+
return ariaLabel.trim().slice(0, NAME_MAX_CHARS);
|
|
800
|
+
if (!options.quick) {
|
|
801
|
+
const labelledBy = element.getAttribute("aria-labelledby");
|
|
802
|
+
if (labelledBy) {
|
|
803
|
+
const labelElement = document.getElementById(labelledBy);
|
|
804
|
+
if (labelElement)
|
|
805
|
+
return (labelElement.textContent || "").trim().slice(0, NAME_MAX_CHARS);
|
|
806
|
+
}
|
|
807
|
+
if (element.tagName === "INPUT" || element.tagName === "SELECT" || element.tagName === "TEXTAREA") {
|
|
808
|
+
const elementId = element.id;
|
|
809
|
+
if (elementId) {
|
|
810
|
+
const associatedLabel = document.querySelector(`label[for="${CSS.escape(elementId)}"]`);
|
|
811
|
+
if (associatedLabel?.textContent) {
|
|
812
|
+
return associatedLabel.textContent.trim().slice(0, NAME_MAX_CHARS);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const placeholder = element.getAttribute("placeholder");
|
|
816
|
+
if (placeholder)
|
|
817
|
+
return placeholder.trim().slice(0, NAME_MAX_CHARS);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (element.tagName === "IMG") {
|
|
821
|
+
const altText = element.getAttribute("alt");
|
|
822
|
+
if (altText)
|
|
823
|
+
return altText.trim().slice(0, NAME_MAX_CHARS);
|
|
824
|
+
}
|
|
825
|
+
const titleAttr = element.getAttribute("title");
|
|
826
|
+
if (titleAttr)
|
|
827
|
+
return titleAttr.trim().slice(0, NAME_MAX_CHARS);
|
|
828
|
+
const textContent = (element.textContent || "").replace(/\s+/g, " ").trim();
|
|
829
|
+
if (textContent)
|
|
830
|
+
return textContent.slice(0, NAME_MAX_CHARS);
|
|
831
|
+
return "";
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/capture/a11yTree.ts
|
|
835
|
+
var REF_PREFIX = "r-";
|
|
836
|
+
var VALUE_MAX_CHARS = 80;
|
|
837
|
+
var HREF_MAX_CHARS = 60;
|
|
838
|
+
var PAGE_CONTENT_MAX_BYTES = 14000;
|
|
839
|
+
var NODE_LIMIT = 1500;
|
|
840
|
+
var SENSITIVE_IFRAME_HOSTS = [
|
|
841
|
+
"js.stripe.com",
|
|
842
|
+
"checkout.stripe.com",
|
|
843
|
+
"connect.stripe.com",
|
|
844
|
+
"plaid.com",
|
|
845
|
+
"production.plaid.com",
|
|
846
|
+
"cdn.plaid.com",
|
|
847
|
+
"auth0.com",
|
|
848
|
+
"login.auth0.com",
|
|
849
|
+
"recaptcha.net",
|
|
850
|
+
"www.google.com",
|
|
851
|
+
"hcaptcha.com",
|
|
852
|
+
"newassets.hcaptcha.com",
|
|
853
|
+
"challenges.cloudflare.com"
|
|
854
|
+
];
|
|
855
|
+
var MASK_AUTOCOMPLETE_VALUES = new Set([
|
|
856
|
+
"cc-number",
|
|
857
|
+
"cc-exp",
|
|
858
|
+
"cc-exp-month",
|
|
859
|
+
"cc-exp-year",
|
|
860
|
+
"cc-csc",
|
|
861
|
+
"cc-name",
|
|
862
|
+
"cc-given-name",
|
|
863
|
+
"cc-family-name",
|
|
864
|
+
"current-password",
|
|
865
|
+
"new-password",
|
|
866
|
+
"one-time-code"
|
|
867
|
+
]);
|
|
868
|
+
var MASK_INPUT_TYPES = new Set(["password", "hidden"]);
|
|
869
|
+
var MASK_NAME_PATTERN = /password|secret|token|api[_-]?key|ssn|tax[_-]?id|social[_-]?security|cvv|cvc|pin/i;
|
|
870
|
+
var refCounter = 0;
|
|
871
|
+
function generateNextRef() {
|
|
872
|
+
refCounter += 1;
|
|
873
|
+
return `${REF_PREFIX}${refCounter}`;
|
|
874
|
+
}
|
|
875
|
+
function advanceCounterPastExisting(ref) {
|
|
876
|
+
const parsedRefNumber = Number.parseInt(ref.slice(REF_PREFIX.length), 10);
|
|
877
|
+
if (Number.isFinite(parsedRefNumber) && parsedRefNumber > refCounter) {
|
|
878
|
+
refCounter = parsedRefNumber;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function intersectsViewport(rect, viewportWidth, viewportHeight) {
|
|
882
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
883
|
+
return false;
|
|
884
|
+
if (rect.bottom <= 0 || rect.top >= viewportHeight)
|
|
885
|
+
return false;
|
|
886
|
+
if (rect.right <= 0 || rect.left >= viewportWidth)
|
|
887
|
+
return false;
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
890
|
+
function isVisible(element, rect) {
|
|
891
|
+
if (!intersectsViewport(rect, window.innerWidth, window.innerHeight))
|
|
892
|
+
return false;
|
|
893
|
+
const style = window.getComputedStyle(element);
|
|
894
|
+
if (style.visibility === "hidden" || style.display === "none")
|
|
895
|
+
return false;
|
|
896
|
+
if (Number.parseFloat(style.opacity || "1") <= 0.01)
|
|
897
|
+
return false;
|
|
898
|
+
return true;
|
|
899
|
+
}
|
|
900
|
+
function isInteractive(element, role) {
|
|
901
|
+
if (role && role !== "heading" && role !== "image" && role !== "list" && role !== "listitem") {
|
|
902
|
+
const interactiveRoles = [
|
|
903
|
+
"button",
|
|
904
|
+
"link",
|
|
905
|
+
"input",
|
|
906
|
+
"select",
|
|
907
|
+
"textarea",
|
|
908
|
+
"checkbox",
|
|
909
|
+
"radio",
|
|
910
|
+
"tab",
|
|
911
|
+
"menuitem",
|
|
912
|
+
"option",
|
|
913
|
+
"switch",
|
|
914
|
+
"slider"
|
|
915
|
+
];
|
|
916
|
+
if (interactiveRoles.includes(role))
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
919
|
+
if (element.tabIndex >= 0)
|
|
920
|
+
return true;
|
|
921
|
+
if (element.hasAttribute("onclick") || element.hasAttribute("contenteditable"))
|
|
922
|
+
return true;
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
function isBlindRegion(role) {
|
|
926
|
+
return role === "video" || role === "iframe" || role === "canvas" || role === "audio" || role === "embed" || role === "object";
|
|
927
|
+
}
|
|
928
|
+
function getIframeHost(element) {
|
|
929
|
+
const src = element.getAttribute("src");
|
|
930
|
+
if (!src)
|
|
931
|
+
return null;
|
|
932
|
+
try {
|
|
933
|
+
return new URL(src, window.location.href).host;
|
|
934
|
+
} catch {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
function isSensitiveIframe(element) {
|
|
939
|
+
const host = getIframeHost(element);
|
|
940
|
+
if (!host)
|
|
941
|
+
return false;
|
|
942
|
+
return SENSITIVE_IFRAME_HOSTS.some((sensitiveHost) => host === sensitiveHost || host.endsWith(`.${sensitiveHost}`));
|
|
943
|
+
}
|
|
944
|
+
function isSameOriginIframe(element) {
|
|
945
|
+
const src = element.getAttribute("src");
|
|
946
|
+
if (!src)
|
|
947
|
+
return true;
|
|
948
|
+
try {
|
|
949
|
+
return new URL(src, window.location.href).origin === window.location.origin;
|
|
950
|
+
} catch {
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
function nearestHeadingText(element) {
|
|
955
|
+
let currentNode = element;
|
|
956
|
+
while (currentNode && currentNode !== document.body) {
|
|
957
|
+
let previousSibling = currentNode.previousElementSibling;
|
|
958
|
+
while (previousSibling) {
|
|
959
|
+
if (/^H[1-6]$/.test(previousSibling.tagName) && previousSibling.textContent) {
|
|
960
|
+
return previousSibling.textContent.trim().slice(0, NAME_MAX_CHARS);
|
|
961
|
+
}
|
|
962
|
+
previousSibling = previousSibling.previousElementSibling;
|
|
963
|
+
}
|
|
964
|
+
currentNode = currentNode.parentElement;
|
|
965
|
+
}
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
function shouldMaskValue(input) {
|
|
969
|
+
if (input.tagName === "INPUT") {
|
|
970
|
+
const inputElement = input;
|
|
971
|
+
if (MASK_INPUT_TYPES.has(inputElement.type))
|
|
972
|
+
return true;
|
|
973
|
+
const autocompleteAttr = (inputElement.getAttribute("autocomplete") || "").toLowerCase().trim();
|
|
974
|
+
if (MASK_AUTOCOMPLETE_VALUES.has(autocompleteAttr))
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
const fieldName = (input.getAttribute("name") || "").trim();
|
|
978
|
+
const fieldId = (input.getAttribute("id") || "").trim();
|
|
979
|
+
if (MASK_NAME_PATTERN.test(fieldName) || MASK_NAME_PATTERN.test(fieldId))
|
|
980
|
+
return true;
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
function escapeAttributeValue(value) {
|
|
984
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/[\r\n]+/g, " ");
|
|
985
|
+
}
|
|
986
|
+
function extractAttrs(element, role) {
|
|
987
|
+
const attributes = [];
|
|
988
|
+
if (role === "link") {
|
|
989
|
+
const href = element.getAttribute("href");
|
|
990
|
+
if (href)
|
|
991
|
+
attributes.push(`href="${escapeAttributeValue(href.slice(0, HREF_MAX_CHARS))}"`);
|
|
992
|
+
}
|
|
993
|
+
if (role === "input" || role === "checkbox" || role === "radio") {
|
|
994
|
+
const input = element;
|
|
995
|
+
if (input.type && input.type !== "text")
|
|
996
|
+
attributes.push(`type="${input.type}"`);
|
|
997
|
+
if (shouldMaskValue(input)) {
|
|
998
|
+
attributes.push("valueMasked");
|
|
999
|
+
} else if (input.value && role === "input") {
|
|
1000
|
+
attributes.push(`value="${escapeAttributeValue(input.value.slice(0, VALUE_MAX_CHARS))}"`);
|
|
1001
|
+
}
|
|
1002
|
+
if (input.checked)
|
|
1003
|
+
attributes.push("checked");
|
|
1004
|
+
}
|
|
1005
|
+
if (role === "textarea") {
|
|
1006
|
+
const textarea = element;
|
|
1007
|
+
if (shouldMaskValue(textarea)) {
|
|
1008
|
+
attributes.push("valueMasked");
|
|
1009
|
+
} else if (textarea.value) {
|
|
1010
|
+
attributes.push(`value="${escapeAttributeValue(textarea.value.slice(0, VALUE_MAX_CHARS))}"`);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (role === "select") {
|
|
1014
|
+
const select = element;
|
|
1015
|
+
const selectedOption = select.options[select.selectedIndex];
|
|
1016
|
+
if (selectedOption?.value?.trim()) {
|
|
1017
|
+
attributes.push(`selected="${escapeAttributeValue(selectedOption.value.slice(0, VALUE_MAX_CHARS))}"`);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (role === "heading") {
|
|
1021
|
+
const headingLevel = element.tagName.match(/^H([1-6])$/)?.[1];
|
|
1022
|
+
if (headingLevel)
|
|
1023
|
+
attributes.push(`level="${headingLevel}"`);
|
|
1024
|
+
}
|
|
1025
|
+
if (role === "video" || role === "audio") {
|
|
1026
|
+
const mediaElement = element;
|
|
1027
|
+
try {
|
|
1028
|
+
if (Number.isFinite(mediaElement.currentTime)) {
|
|
1029
|
+
attributes.push(`currentTime="${mediaElement.currentTime.toFixed(1)}"`);
|
|
1030
|
+
}
|
|
1031
|
+
if (Number.isFinite(mediaElement.duration) && mediaElement.duration > 0) {
|
|
1032
|
+
attributes.push(`duration="${mediaElement.duration.toFixed(1)}"`);
|
|
1033
|
+
}
|
|
1034
|
+
attributes.push(mediaElement.paused ? "paused" : "playing");
|
|
1035
|
+
if (mediaElement.muted)
|
|
1036
|
+
attributes.push("muted");
|
|
1037
|
+
const mediaSrc = mediaElement.currentSrc || mediaElement.getAttribute("src");
|
|
1038
|
+
if (mediaSrc) {
|
|
1039
|
+
try {
|
|
1040
|
+
attributes.push(`srcHost="${new URL(mediaSrc, window.location.href).host}"`);
|
|
1041
|
+
} catch {}
|
|
1042
|
+
}
|
|
1043
|
+
} catch {}
|
|
1044
|
+
}
|
|
1045
|
+
if (role === "iframe") {
|
|
1046
|
+
const host = getIframeHost(element);
|
|
1047
|
+
if (host)
|
|
1048
|
+
attributes.push(`srcHost="${host}"`);
|
|
1049
|
+
if (isSensitiveIframe(element)) {
|
|
1050
|
+
attributes.push("sensitive");
|
|
1051
|
+
} else if (!isSameOriginIframe(element)) {
|
|
1052
|
+
attributes.push("cross-origin");
|
|
1053
|
+
}
|
|
1054
|
+
const iframeRect = element.getBoundingClientRect();
|
|
1055
|
+
attributes.push(`size="${Math.round(iframeRect.width)}x${Math.round(iframeRect.height)}"`);
|
|
1056
|
+
}
|
|
1057
|
+
if (role === "canvas") {
|
|
1058
|
+
const canvas = element;
|
|
1059
|
+
attributes.push(`size="${canvas.width}x${canvas.height}"`);
|
|
1060
|
+
const heading = nearestHeadingText(element);
|
|
1061
|
+
if (heading)
|
|
1062
|
+
attributes.push(`nearestHeading="${escapeAttributeValue(heading)}"`);
|
|
1063
|
+
}
|
|
1064
|
+
if (role === "image") {
|
|
1065
|
+
const image = element;
|
|
1066
|
+
const imageSrc = image.getAttribute("src") || "";
|
|
1067
|
+
if (/\.gif(\?|$)/i.test(imageSrc))
|
|
1068
|
+
attributes.push("animated");
|
|
1069
|
+
}
|
|
1070
|
+
const isNativelyDisabled = "disabled" in element && element.disabled === true;
|
|
1071
|
+
if (isNativelyDisabled || element.getAttribute("aria-disabled") === "true") {
|
|
1072
|
+
attributes.push("disabled");
|
|
1073
|
+
}
|
|
1074
|
+
return attributes;
|
|
1075
|
+
}
|
|
1076
|
+
function ensureStableRef(element) {
|
|
1077
|
+
const existingRef = element.getAttribute(REF_ATTR);
|
|
1078
|
+
if (existingRef) {
|
|
1079
|
+
advanceCounterPastExisting(existingRef);
|
|
1080
|
+
return existingRef;
|
|
1081
|
+
}
|
|
1082
|
+
const newRef = generateNextRef();
|
|
1083
|
+
element.setAttribute(REF_ATTR, newRef);
|
|
1084
|
+
return newRef;
|
|
1085
|
+
}
|
|
1086
|
+
function formatNodeAsLine(entry) {
|
|
1087
|
+
const indent = " ".repeat(entry.depth);
|
|
1088
|
+
const namePart = entry.name ? ` "${escapeAttributeValue(entry.name)}"` : "";
|
|
1089
|
+
const attrsPart = entry.attrs.length > 0 ? ` ${entry.attrs.join(" ")}` : "";
|
|
1090
|
+
return `${indent}${entry.role}${namePart} ${entry.ref}${attrsPart}`;
|
|
1091
|
+
}
|
|
1092
|
+
function shouldSkipSubtree(element) {
|
|
1093
|
+
if (element.id === WIDGET_ROOT_ID)
|
|
1094
|
+
return true;
|
|
1095
|
+
if (element.hasAttribute(PRIVATE_ATTR))
|
|
1096
|
+
return true;
|
|
1097
|
+
const tagName = element.tagName;
|
|
1098
|
+
if (tagName === "SCRIPT" || tagName === "STYLE" || tagName === "NOSCRIPT")
|
|
1099
|
+
return true;
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
function shouldEmitElement(element, role) {
|
|
1103
|
+
if (role === null)
|
|
1104
|
+
return false;
|
|
1105
|
+
return isInteractive(element, role) || role === "heading" || role === "image" || role === "label" || isBlindRegion(role);
|
|
1106
|
+
}
|
|
1107
|
+
function getSameOriginIframeBody(element) {
|
|
1108
|
+
if (element.tagName !== "IFRAME")
|
|
1109
|
+
return null;
|
|
1110
|
+
if (!isSameOriginIframe(element) || isSensitiveIframe(element))
|
|
1111
|
+
return null;
|
|
1112
|
+
try {
|
|
1113
|
+
return element.contentDocument?.body ?? null;
|
|
1114
|
+
} catch {
|
|
1115
|
+
return null;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
function walkAndCollect(element, depth, entries) {
|
|
1119
|
+
if (shouldSkipSubtree(element))
|
|
1120
|
+
return;
|
|
1121
|
+
const rect = element.getBoundingClientRect();
|
|
1122
|
+
const role = inferRole(element);
|
|
1123
|
+
const isVisibleAndEmittable = role !== null && isVisible(element, rect) && shouldEmitElement(element, role);
|
|
1124
|
+
let childDepth = depth;
|
|
1125
|
+
if (isVisibleAndEmittable && role) {
|
|
1126
|
+
const ref = ensureStableRef(element);
|
|
1127
|
+
entries.push({
|
|
1128
|
+
depth,
|
|
1129
|
+
role,
|
|
1130
|
+
name: accessibleName(element),
|
|
1131
|
+
ref,
|
|
1132
|
+
attrs: extractAttrs(element, role),
|
|
1133
|
+
area: rect.width * rect.height
|
|
1134
|
+
});
|
|
1135
|
+
childDepth = depth + 1;
|
|
1136
|
+
}
|
|
1137
|
+
if (role === "iframe") {
|
|
1138
|
+
const iframeBody = getSameOriginIframeBody(element);
|
|
1139
|
+
if (iframeBody) {
|
|
1140
|
+
for (const child of Array.from(iframeBody.children)) {
|
|
1141
|
+
walkAndCollect(child, childDepth, entries);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
if (role && isBlindRegion(role))
|
|
1147
|
+
return;
|
|
1148
|
+
for (const child of Array.from(element.children)) {
|
|
1149
|
+
walkAndCollect(child, childDepth, entries);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
function dropSmallestAreaEntriesUntilUnderLimit(entries) {
|
|
1153
|
+
if (entries.length <= NODE_LIMIT)
|
|
1154
|
+
return entries;
|
|
1155
|
+
const indexedEntries = entries.map((entry, index2) => ({ entry, index: index2 }));
|
|
1156
|
+
const entriesByAreaAscending = indexedEntries.slice(1).sort((a, b) => a.entry.area - b.entry.area);
|
|
1157
|
+
const indicesToDrop = new Set(entriesByAreaAscending.slice(0, entries.length - NODE_LIMIT).map((indexed) => indexed.index));
|
|
1158
|
+
const keptEntries = [];
|
|
1159
|
+
for (let i = 0;i < entries.length; i++) {
|
|
1160
|
+
if (!indicesToDrop.has(i))
|
|
1161
|
+
keptEntries.push(entries[i]);
|
|
1162
|
+
}
|
|
1163
|
+
return keptEntries;
|
|
1164
|
+
}
|
|
1165
|
+
function buildAccessibilityTree() {
|
|
1166
|
+
const collectedEntries = [];
|
|
1167
|
+
const pageTitle = document.title || window.location.pathname;
|
|
1168
|
+
collectedEntries.push({
|
|
1169
|
+
depth: 0,
|
|
1170
|
+
role: "page",
|
|
1171
|
+
name: pageTitle.slice(0, NAME_MAX_CHARS),
|
|
1172
|
+
ref: `${REF_PREFIX}0`,
|
|
1173
|
+
attrs: [],
|
|
1174
|
+
area: window.innerWidth * window.innerHeight
|
|
1175
|
+
});
|
|
1176
|
+
if (document.body) {
|
|
1177
|
+
for (const child of Array.from(document.body.children)) {
|
|
1178
|
+
walkAndCollect(child, 1, collectedEntries);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
const exceededNodeLimit = collectedEntries.length > NODE_LIMIT;
|
|
1182
|
+
const finalEntries = exceededNodeLimit ? dropSmallestAreaEntriesUntilUnderLimit(collectedEntries) : collectedEntries;
|
|
1183
|
+
const treeLines = finalEntries.map(formatNodeAsLine);
|
|
1184
|
+
let pageContent = treeLines.join(`
|
|
1185
|
+
`);
|
|
1186
|
+
const exceededByteLimit = pageContent.length > PAGE_CONTENT_MAX_BYTES;
|
|
1187
|
+
if (exceededByteLimit) {
|
|
1188
|
+
pageContent = `${pageContent.slice(0, PAGE_CONTENT_MAX_BYTES)}
|
|
1189
|
+
<!-- truncated -->`;
|
|
1190
|
+
}
|
|
1191
|
+
return {
|
|
1192
|
+
pageContent,
|
|
1193
|
+
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
1194
|
+
refCount: finalEntries.length - 1,
|
|
1195
|
+
truncated: exceededNodeLimit || exceededByteLimit
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/capture/domEvents.ts
|
|
1200
|
+
var MUTATION_DEBOUNCE_MS = 250;
|
|
1201
|
+
var textEncoder = new TextEncoder;
|
|
1202
|
+
function isInsideWidget(target) {
|
|
1203
|
+
if (!(target instanceof Node))
|
|
1204
|
+
return false;
|
|
1205
|
+
let node = target;
|
|
1206
|
+
while (node) {
|
|
1207
|
+
if (node instanceof Element && node.id === WIDGET_ROOT_ID)
|
|
1208
|
+
return true;
|
|
1209
|
+
node = node.parentNode;
|
|
1210
|
+
}
|
|
1211
|
+
return false;
|
|
1212
|
+
}
|
|
1213
|
+
function findNearestRef(element) {
|
|
1214
|
+
let currentNode = element;
|
|
1215
|
+
while (currentNode) {
|
|
1216
|
+
const ref = currentNode.getAttribute(REF_ATTR);
|
|
1217
|
+
if (ref)
|
|
1218
|
+
return { ref, node: currentNode };
|
|
1219
|
+
currentNode = currentNode.parentElement;
|
|
1220
|
+
}
|
|
1221
|
+
return null;
|
|
1222
|
+
}
|
|
1223
|
+
function installDomEventListeners(localParticipant, options = {}) {
|
|
1224
|
+
const { onTriggerEvent } = options;
|
|
1225
|
+
function publishDomEvent(event) {
|
|
1226
|
+
try {
|
|
1227
|
+
localParticipant.publishData(textEncoder.encode(JSON.stringify(event)), {
|
|
1228
|
+
reliable: true,
|
|
1229
|
+
topic: DOM_EVENTS_TOPIC
|
|
1230
|
+
}).catch(() => {
|
|
1231
|
+
return;
|
|
1232
|
+
});
|
|
1233
|
+
} catch {}
|
|
1234
|
+
if (onTriggerEvent) {
|
|
1235
|
+
try {
|
|
1236
|
+
onTriggerEvent();
|
|
1237
|
+
} catch {}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
const onClick = (event) => {
|
|
1241
|
+
if (isInsideWidget(event.target))
|
|
1242
|
+
return;
|
|
1243
|
+
if (!(event.target instanceof Element))
|
|
1244
|
+
return;
|
|
1245
|
+
const matchedRef = findNearestRef(event.target);
|
|
1246
|
+
const targetElement = matchedRef?.node ?? event.target;
|
|
1247
|
+
publishDomEvent({
|
|
1248
|
+
type: "click",
|
|
1249
|
+
ref: matchedRef?.ref ?? null,
|
|
1250
|
+
role: inferRole(targetElement) ?? targetElement.tagName.toLowerCase(),
|
|
1251
|
+
name: accessibleName(targetElement, { quick: true }),
|
|
1252
|
+
timestamp: Date.now()
|
|
1253
|
+
});
|
|
1254
|
+
};
|
|
1255
|
+
document.addEventListener("click", onClick, true);
|
|
1256
|
+
const onSubmit = (event) => {
|
|
1257
|
+
if (isInsideWidget(event.target))
|
|
1258
|
+
return;
|
|
1259
|
+
if (!(event.target instanceof HTMLFormElement))
|
|
1260
|
+
return;
|
|
1261
|
+
const submittedForm = event.target;
|
|
1262
|
+
publishDomEvent({
|
|
1263
|
+
type: "submit",
|
|
1264
|
+
ref: submittedForm.getAttribute(REF_ATTR),
|
|
1265
|
+
formName: (submittedForm.name || submittedForm.id || "").slice(0, NAME_MAX_CHARS),
|
|
1266
|
+
action: (submittedForm.action || "").slice(0, NAME_MAX_CHARS),
|
|
1267
|
+
timestamp: Date.now()
|
|
1268
|
+
});
|
|
1269
|
+
};
|
|
1270
|
+
document.addEventListener("submit", onSubmit, true);
|
|
1271
|
+
const originalPushState = history.pushState;
|
|
1272
|
+
const originalReplaceState = history.replaceState;
|
|
1273
|
+
let previousUrl = window.location.href;
|
|
1274
|
+
const emitNavigationIfChanged = () => {
|
|
1275
|
+
const nextUrl = window.location.href;
|
|
1276
|
+
if (nextUrl === previousUrl)
|
|
1277
|
+
return;
|
|
1278
|
+
publishDomEvent({
|
|
1279
|
+
type: "navigation",
|
|
1280
|
+
from: previousUrl,
|
|
1281
|
+
to: nextUrl,
|
|
1282
|
+
title: document.title.slice(0, NAME_MAX_CHARS),
|
|
1283
|
+
timestamp: Date.now()
|
|
1284
|
+
});
|
|
1285
|
+
previousUrl = nextUrl;
|
|
1286
|
+
};
|
|
1287
|
+
history.pushState = function patchedPushState(...args) {
|
|
1288
|
+
originalPushState.apply(this, args);
|
|
1289
|
+
queueMicrotask(emitNavigationIfChanged);
|
|
1290
|
+
};
|
|
1291
|
+
history.replaceState = function patchedReplaceState(...args) {
|
|
1292
|
+
originalReplaceState.apply(this, args);
|
|
1293
|
+
queueMicrotask(emitNavigationIfChanged);
|
|
1294
|
+
};
|
|
1295
|
+
const onPopState = () => emitNavigationIfChanged();
|
|
1296
|
+
const onHashChange = () => emitNavigationIfChanged();
|
|
1297
|
+
window.addEventListener("popstate", onPopState);
|
|
1298
|
+
window.addEventListener("hashchange", onHashChange);
|
|
1299
|
+
let pendingAddedRefs = new Set;
|
|
1300
|
+
let pendingRemovedRefs = new Set;
|
|
1301
|
+
let pendingMutationCount = 0;
|
|
1302
|
+
let mutationFlushTimer = null;
|
|
1303
|
+
const flushPendingMutations = () => {
|
|
1304
|
+
mutationFlushTimer = null;
|
|
1305
|
+
if (pendingMutationCount === 0)
|
|
1306
|
+
return;
|
|
1307
|
+
publishDomEvent({
|
|
1308
|
+
type: "mutation",
|
|
1309
|
+
summary: `${pendingMutationCount} subtree changes`,
|
|
1310
|
+
addedRefs: Array.from(pendingAddedRefs),
|
|
1311
|
+
removedRefs: Array.from(pendingRemovedRefs),
|
|
1312
|
+
timestamp: Date.now()
|
|
1313
|
+
});
|
|
1314
|
+
pendingAddedRefs = new Set;
|
|
1315
|
+
pendingRemovedRefs = new Set;
|
|
1316
|
+
pendingMutationCount = 0;
|
|
1317
|
+
};
|
|
1318
|
+
const collectDescendantRefs = (node, targetSet) => {
|
|
1319
|
+
if (!(node instanceof Element))
|
|
1320
|
+
return;
|
|
1321
|
+
if (node.id === WIDGET_ROOT_ID)
|
|
1322
|
+
return;
|
|
1323
|
+
const ref = node.getAttribute(REF_ATTR);
|
|
1324
|
+
if (ref)
|
|
1325
|
+
targetSet.add(ref);
|
|
1326
|
+
for (const child of Array.from(node.children))
|
|
1327
|
+
collectDescendantRefs(child, targetSet);
|
|
1328
|
+
};
|
|
1329
|
+
const observer = new MutationObserver((mutations) => {
|
|
1330
|
+
let observedCount = 0;
|
|
1331
|
+
for (const mutation of mutations) {
|
|
1332
|
+
if (mutation.type !== "childList")
|
|
1333
|
+
continue;
|
|
1334
|
+
if (mutation.target instanceof Element && isInsideWidget(mutation.target))
|
|
1335
|
+
continue;
|
|
1336
|
+
if (mutation.addedNodes.length === 0 && mutation.removedNodes.length === 0)
|
|
1337
|
+
continue;
|
|
1338
|
+
for (const added of Array.from(mutation.addedNodes)) {
|
|
1339
|
+
collectDescendantRefs(added, pendingAddedRefs);
|
|
1340
|
+
}
|
|
1341
|
+
for (const removed of Array.from(mutation.removedNodes)) {
|
|
1342
|
+
collectDescendantRefs(removed, pendingRemovedRefs);
|
|
1343
|
+
}
|
|
1344
|
+
observedCount += 1;
|
|
1345
|
+
}
|
|
1346
|
+
if (observedCount === 0)
|
|
1347
|
+
return;
|
|
1348
|
+
pendingMutationCount += observedCount;
|
|
1349
|
+
if (mutationFlushTimer)
|
|
1350
|
+
clearTimeout(mutationFlushTimer);
|
|
1351
|
+
mutationFlushTimer = setTimeout(flushPendingMutations, MUTATION_DEBOUNCE_MS);
|
|
1352
|
+
});
|
|
1353
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
1354
|
+
return () => {
|
|
1355
|
+
document.removeEventListener("click", onClick, true);
|
|
1356
|
+
document.removeEventListener("submit", onSubmit, true);
|
|
1357
|
+
window.removeEventListener("popstate", onPopState);
|
|
1358
|
+
window.removeEventListener("hashchange", onHashChange);
|
|
1359
|
+
history.pushState = originalPushState;
|
|
1360
|
+
history.replaceState = originalReplaceState;
|
|
1361
|
+
if (mutationFlushTimer)
|
|
1362
|
+
clearTimeout(mutationFlushTimer);
|
|
1363
|
+
observer.disconnect();
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// src/capture/snapdom.ts
|
|
1368
|
+
var cachedSnapdomModule = null;
|
|
1369
|
+
function loadSnapdom() {
|
|
1370
|
+
if (!cachedSnapdomModule)
|
|
1371
|
+
cachedSnapdomModule = import("@zumer/snapdom");
|
|
1372
|
+
return cachedSnapdomModule;
|
|
1373
|
+
}
|
|
1374
|
+
async function snapToCanvas(element, options = {}) {
|
|
1375
|
+
const snapdomModule = await loadSnapdom();
|
|
1376
|
+
return snapdomModule.snapdom.toCanvas(element, options);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// src/components/DomCapture.tsx
|
|
1380
|
+
var SNAPSHOT_INTERVAL_MS = 2000;
|
|
1381
|
+
var A11Y_PUBLISH_INTERVAL_MS = 2000;
|
|
1382
|
+
var CAPTURE_PRESET = ScreenSharePresets2.h1080fps30;
|
|
1383
|
+
var CAPTURE_FPS = CAPTURE_PRESET.encoding.maxFramerate ?? 30;
|
|
1384
|
+
var CAPTURE_BITRATE = 4000000;
|
|
1385
|
+
var DOM_SNAPSHOT_GZIP_THRESHOLD_BYTES = 14000;
|
|
1386
|
+
var MAX_CANVAS_DPR = 1.5;
|
|
1387
|
+
var CONSECUTIVE_FAILURES_BEFORE_REPORT = 3;
|
|
1388
|
+
var textEncoder2 = new TextEncoder;
|
|
1389
|
+
function shouldIncludeInSnapshot(element) {
|
|
1390
|
+
if (!(element instanceof Element))
|
|
1391
|
+
return true;
|
|
1392
|
+
if (element.id === WIDGET_ROOT_ID)
|
|
1393
|
+
return false;
|
|
1394
|
+
if (element.hasAttribute?.(PRIVATE_ATTR))
|
|
1395
|
+
return false;
|
|
1396
|
+
return true;
|
|
1397
|
+
}
|
|
1398
|
+
function createPublishingCanvas() {
|
|
1399
|
+
const dprBoost = Math.min(window.devicePixelRatio || 1, MAX_CANVAS_DPR);
|
|
1400
|
+
const canvas = document.createElement("canvas");
|
|
1401
|
+
canvas.width = Math.round(CAPTURE_PRESET.width * dprBoost);
|
|
1402
|
+
canvas.height = Math.round(CAPTURE_PRESET.height * dprBoost);
|
|
1403
|
+
const ctx = canvas.getContext("2d");
|
|
1404
|
+
if (!ctx)
|
|
1405
|
+
return null;
|
|
1406
|
+
ctx.fillStyle = "#ffffff";
|
|
1407
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
1408
|
+
return { canvas, ctx };
|
|
1409
|
+
}
|
|
1410
|
+
function getCanvasCaptureStream(canvas, fps) {
|
|
1411
|
+
const captureStreamFn = canvas.captureStream;
|
|
1412
|
+
if (typeof captureStreamFn !== "function") {
|
|
1413
|
+
console.error("canvas.captureStream is not supported in this browser");
|
|
1414
|
+
return null;
|
|
1415
|
+
}
|
|
1416
|
+
return captureStreamFn.call(canvas, fps);
|
|
1417
|
+
}
|
|
1418
|
+
async function paintViewportSnapshot(canvas, ctx) {
|
|
1419
|
+
const dpr = window.devicePixelRatio || 1;
|
|
1420
|
+
const snapshotCanvas = await snapToCanvas(document.documentElement, {
|
|
1421
|
+
filter: shouldIncludeInSnapshot,
|
|
1422
|
+
filterMode: "remove",
|
|
1423
|
+
backgroundColor: "#ffffff",
|
|
1424
|
+
fast: true,
|
|
1425
|
+
dpr
|
|
1426
|
+
});
|
|
1427
|
+
const sourceX = window.scrollX * dpr;
|
|
1428
|
+
const sourceY = window.scrollY * dpr;
|
|
1429
|
+
const sourceWidth = window.innerWidth * dpr;
|
|
1430
|
+
const sourceHeight = window.innerHeight * dpr;
|
|
1431
|
+
const fitScale = Math.min(canvas.width / sourceWidth, canvas.height / sourceHeight);
|
|
1432
|
+
const destWidth = sourceWidth * fitScale;
|
|
1433
|
+
const destHeight = sourceHeight * fitScale;
|
|
1434
|
+
const destX = (canvas.width - destWidth) / 2;
|
|
1435
|
+
const destY = (canvas.height - destHeight) / 2;
|
|
1436
|
+
ctx.fillStyle = "#ffffff";
|
|
1437
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
1438
|
+
ctx.drawImage(snapshotCanvas, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
|
|
1439
|
+
}
|
|
1440
|
+
async function gzipIfBeneficial(snapshotBytes) {
|
|
1441
|
+
if (typeof CompressionStream === "undefined" || snapshotBytes.byteLength <= DOM_SNAPSHOT_GZIP_THRESHOLD_BYTES) {
|
|
1442
|
+
return { bytes: snapshotBytes, compressionFlag: 0 };
|
|
1443
|
+
}
|
|
1444
|
+
try {
|
|
1445
|
+
const blob = new Blob([new Uint8Array(snapshotBytes)]);
|
|
1446
|
+
const stream = blob.stream().pipeThrough(new CompressionStream("gzip"));
|
|
1447
|
+
const compressedBytes = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
1448
|
+
return { bytes: compressedBytes, compressionFlag: 1 };
|
|
1449
|
+
} catch {
|
|
1450
|
+
return { bytes: snapshotBytes, compressionFlag: 0 };
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
async function buildDomSnapshotFrame() {
|
|
1454
|
+
const a11yTree = buildAccessibilityTree();
|
|
1455
|
+
const snapshotJson = JSON.stringify({
|
|
1456
|
+
pageContent: a11yTree.pageContent,
|
|
1457
|
+
viewport: a11yTree.viewport,
|
|
1458
|
+
truncated: a11yTree.truncated,
|
|
1459
|
+
url: window.location.href,
|
|
1460
|
+
title: document.title,
|
|
1461
|
+
capturedAt: new Date().toISOString()
|
|
1462
|
+
});
|
|
1463
|
+
const snapshotBytes = textEncoder2.encode(snapshotJson);
|
|
1464
|
+
const { bytes, compressionFlag } = await gzipIfBeneficial(snapshotBytes);
|
|
1465
|
+
const frame = new Uint8Array(bytes.byteLength + 1);
|
|
1466
|
+
frame[0] = compressionFlag;
|
|
1467
|
+
frame.set(bytes, 1);
|
|
1468
|
+
return frame;
|
|
1469
|
+
}
|
|
1470
|
+
function reportCaptureError(localParticipant) {
|
|
1471
|
+
try {
|
|
1472
|
+
localParticipant.publishData(textEncoder2.encode(JSON.stringify({ type: "capture_error" })), {
|
|
1473
|
+
reliable: true,
|
|
1474
|
+
topic: DOM_SNAPSHOT_TOPIC
|
|
1475
|
+
}).catch(() => {
|
|
1476
|
+
return;
|
|
1477
|
+
});
|
|
1478
|
+
} catch {}
|
|
1479
|
+
}
|
|
1480
|
+
async function unpublishAndStopTrack(localParticipant, videoTrack) {
|
|
1481
|
+
try {
|
|
1482
|
+
await localParticipant.unpublishTrack(videoTrack);
|
|
1483
|
+
} catch {}
|
|
1484
|
+
videoTrack.stop();
|
|
1485
|
+
}
|
|
1486
|
+
function DomCapture() {
|
|
1487
|
+
const { localParticipant } = useLocalParticipant3();
|
|
1488
|
+
const connectionState = useConnectionState2();
|
|
1489
|
+
const didStartRef = useRef2(false);
|
|
1490
|
+
useEffect4(() => {
|
|
1491
|
+
if (didStartRef.current)
|
|
1492
|
+
return;
|
|
1493
|
+
if (connectionState !== ConnectionState2.Connected)
|
|
1494
|
+
return;
|
|
1495
|
+
const canvasAndCtx = createPublishingCanvas();
|
|
1496
|
+
if (!canvasAndCtx)
|
|
1497
|
+
return;
|
|
1498
|
+
const { canvas, ctx } = canvasAndCtx;
|
|
1499
|
+
const canvasStream = getCanvasCaptureStream(canvas, CAPTURE_FPS);
|
|
1500
|
+
if (!canvasStream)
|
|
1501
|
+
return;
|
|
1502
|
+
const videoTrack = canvasStream.getVideoTracks()[0] ?? null;
|
|
1503
|
+
if (!videoTrack)
|
|
1504
|
+
return;
|
|
1505
|
+
videoTrack.contentHint = "text";
|
|
1506
|
+
didStartRef.current = true;
|
|
1507
|
+
let cancelled = false;
|
|
1508
|
+
let snapshotInFlight = false;
|
|
1509
|
+
let a11yPublishInFlight = false;
|
|
1510
|
+
let consecutiveCaptureFailures = 0;
|
|
1511
|
+
localParticipant.setMicrophoneEnabled(true).catch((error) => console.error("Failed to enable microphone:", error));
|
|
1512
|
+
localParticipant.publishTrack(videoTrack, {
|
|
1513
|
+
source: Track2.Source.ScreenShare,
|
|
1514
|
+
videoEncoding: {
|
|
1515
|
+
maxBitrate: CAPTURE_BITRATE,
|
|
1516
|
+
maxFramerate: CAPTURE_FPS
|
|
1517
|
+
},
|
|
1518
|
+
simulcast: false
|
|
1519
|
+
}).catch((error) => {
|
|
1520
|
+
console.error("Failed to publish DOM capture track:", error);
|
|
1521
|
+
for (const track of canvasStream.getTracks())
|
|
1522
|
+
track.stop();
|
|
1523
|
+
});
|
|
1524
|
+
const tickSnapshot = async () => {
|
|
1525
|
+
if (cancelled || snapshotInFlight)
|
|
1526
|
+
return;
|
|
1527
|
+
snapshotInFlight = true;
|
|
1528
|
+
try {
|
|
1529
|
+
await paintViewportSnapshot(canvas, ctx);
|
|
1530
|
+
if (cancelled)
|
|
1531
|
+
return;
|
|
1532
|
+
consecutiveCaptureFailures = 0;
|
|
1533
|
+
} catch {
|
|
1534
|
+
if (cancelled)
|
|
1535
|
+
return;
|
|
1536
|
+
consecutiveCaptureFailures += 1;
|
|
1537
|
+
if (consecutiveCaptureFailures === CONSECUTIVE_FAILURES_BEFORE_REPORT) {
|
|
1538
|
+
reportCaptureError(localParticipant);
|
|
1539
|
+
}
|
|
1540
|
+
} finally {
|
|
1541
|
+
snapshotInFlight = false;
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
const tickA11yPublish = async () => {
|
|
1545
|
+
if (cancelled || a11yPublishInFlight)
|
|
1546
|
+
return;
|
|
1547
|
+
a11yPublishInFlight = true;
|
|
1548
|
+
try {
|
|
1549
|
+
const frame = await buildDomSnapshotFrame();
|
|
1550
|
+
if (cancelled)
|
|
1551
|
+
return;
|
|
1552
|
+
await localParticipant.publishData(frame, {
|
|
1553
|
+
reliable: true,
|
|
1554
|
+
topic: DOM_SNAPSHOT_TOPIC
|
|
1555
|
+
});
|
|
1556
|
+
} catch {} finally {
|
|
1557
|
+
a11yPublishInFlight = false;
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
const cleanupDomEventListeners = installDomEventListeners(localParticipant, {
|
|
1561
|
+
onTriggerEvent: () => {
|
|
1562
|
+
tickA11yPublish();
|
|
1563
|
+
tickSnapshot();
|
|
1564
|
+
}
|
|
1565
|
+
});
|
|
1566
|
+
tickSnapshot();
|
|
1567
|
+
tickA11yPublish();
|
|
1568
|
+
const snapshotTimer = setInterval(tickSnapshot, SNAPSHOT_INTERVAL_MS);
|
|
1569
|
+
const a11yPublishTimer = setInterval(tickA11yPublish, A11Y_PUBLISH_INTERVAL_MS);
|
|
1570
|
+
return () => {
|
|
1571
|
+
cancelled = true;
|
|
1572
|
+
didStartRef.current = false;
|
|
1573
|
+
clearInterval(snapshotTimer);
|
|
1574
|
+
clearInterval(a11yPublishTimer);
|
|
1575
|
+
cleanupDomEventListeners();
|
|
1576
|
+
unpublishAndStopTrack(localParticipant, videoTrack);
|
|
1577
|
+
for (const track of canvasStream.getTracks())
|
|
1578
|
+
track.stop();
|
|
1579
|
+
};
|
|
1580
|
+
}, [connectionState, localParticipant]);
|
|
1581
|
+
return null;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
714
1584
|
// src/components/MinimizedBubble.tsx
|
|
715
|
-
import { useEffect as
|
|
1585
|
+
import { useEffect as useEffect5 } from "react";
|
|
716
1586
|
|
|
717
1587
|
// src/lib/utils.ts
|
|
718
1588
|
import { clsx } from "clsx";
|
|
@@ -828,9 +1698,10 @@ function Tooltip({ label, children, position = "top", align = "center" }) {
|
|
|
828
1698
|
import { jsx as jsx4, jsxs as jsxs4, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
829
1699
|
var BUBBLE_BUTTON = "skippr:flex skippr:size-12 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-[14px] skippr:shadow-[0_4px_16px_rgba(45,43,61,0.45),0_2px_4px_rgba(0,0,0,0.1)] skippr:transition-all skippr:hover:-translate-y-0.5 skippr:active:translate-y-0";
|
|
830
1700
|
function ConnectedBubbleContent() {
|
|
831
|
-
const { expandPanel, disconnect, position } = useLiveAgent();
|
|
1701
|
+
const { expandPanel, disconnect, position, captureMode } = useLiveAgent();
|
|
832
1702
|
const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
|
|
833
1703
|
const tooltipAlign = position === "right" ? "end" : "start";
|
|
1704
|
+
const showScreenShareToggle = captureMode === "screenshare";
|
|
834
1705
|
return /* @__PURE__ */ jsxs4(Fragment2, {
|
|
835
1706
|
children: [
|
|
836
1707
|
/* @__PURE__ */ jsx4(Tooltip, {
|
|
@@ -848,7 +1719,7 @@ function ConnectedBubbleContent() {
|
|
|
848
1719
|
})
|
|
849
1720
|
})
|
|
850
1721
|
}),
|
|
851
|
-
/* @__PURE__ */ jsx4(Tooltip, {
|
|
1722
|
+
showScreenShareToggle && /* @__PURE__ */ jsx4(Tooltip, {
|
|
852
1723
|
label: isScreenSharing ? "Stop sharing" : "Share screen",
|
|
853
1724
|
align: tooltipAlign,
|
|
854
1725
|
children: /* @__PURE__ */ jsx4("button", {
|
|
@@ -919,7 +1790,7 @@ function WelcomeBubble({
|
|
|
919
1790
|
position,
|
|
920
1791
|
onDismiss
|
|
921
1792
|
}) {
|
|
922
|
-
|
|
1793
|
+
useEffect5(() => {
|
|
923
1794
|
const timer = setTimeout(onDismiss, 5000);
|
|
924
1795
|
return () => clearTimeout(timer);
|
|
925
1796
|
}, [onDismiss]);
|
|
@@ -957,13 +1828,13 @@ function MinimizedBubble({
|
|
|
957
1828
|
}
|
|
958
1829
|
|
|
959
1830
|
// src/components/Sidebar.tsx
|
|
960
|
-
import { useEffect as
|
|
1831
|
+
import { useEffect as useEffect12 } from "react";
|
|
961
1832
|
|
|
962
1833
|
// src/hooks/useCombinedMessages.ts
|
|
963
1834
|
import { useMemo as useMemo4 } from "react";
|
|
964
1835
|
|
|
965
1836
|
// src/hooks/useChatMessages.ts
|
|
966
|
-
import { useChat, useLocalParticipant as
|
|
1837
|
+
import { useChat, useLocalParticipant as useLocalParticipant4 } from "@livekit/components-react/hooks";
|
|
967
1838
|
import { useMemo as useMemo2 } from "react";
|
|
968
1839
|
|
|
969
1840
|
// src/lib/filterSystemMessages.ts
|
|
@@ -975,7 +1846,7 @@ function filterSystemMessages(messages) {
|
|
|
975
1846
|
// src/hooks/useChatMessages.ts
|
|
976
1847
|
function useChatMessages() {
|
|
977
1848
|
const { chatMessages: rawMessages, send, isSending } = useChat();
|
|
978
|
-
const { localParticipant } =
|
|
1849
|
+
const { localParticipant } = useLocalParticipant4();
|
|
979
1850
|
const localIdentity = localParticipant.identity;
|
|
980
1851
|
const chatMessages = useMemo2(() => {
|
|
981
1852
|
const sortedMessages = rawMessages.map((msg) => ({
|
|
@@ -991,11 +1862,11 @@ function useChatMessages() {
|
|
|
991
1862
|
}
|
|
992
1863
|
|
|
993
1864
|
// src/hooks/useStreamingTranscript.ts
|
|
994
|
-
import { useLocalParticipant as
|
|
1865
|
+
import { useLocalParticipant as useLocalParticipant5, useTranscriptions } from "@livekit/components-react/hooks";
|
|
995
1866
|
import { useMemo as useMemo3 } from "react";
|
|
996
1867
|
function useStreamingTranscript() {
|
|
997
1868
|
const transcriptions = useTranscriptions();
|
|
998
|
-
const { localParticipant } =
|
|
1869
|
+
const { localParticipant } = useLocalParticipant5();
|
|
999
1870
|
const localIdentity = localParticipant.identity;
|
|
1000
1871
|
const transcriptMessages = useMemo3(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
|
|
1001
1872
|
id: stream.streamInfo.id,
|
|
@@ -1042,12 +1913,12 @@ function useCombinedMessages() {
|
|
|
1042
1913
|
import { useCallback as useCallback4 } from "react";
|
|
1043
1914
|
|
|
1044
1915
|
// src/hooks/useAgentState.ts
|
|
1045
|
-
import { useRemoteParticipants } from "@livekit/components-react";
|
|
1046
|
-
import { useEffect as
|
|
1916
|
+
import { useRemoteParticipants } from "@livekit/components-react/hooks";
|
|
1917
|
+
import { useEffect as useEffect6, useState as useState3 } from "react";
|
|
1047
1918
|
function useAgentState(attributeKey, parse, initial) {
|
|
1048
1919
|
const [value, setValue] = useState3(initial);
|
|
1049
1920
|
const remoteParticipants = useRemoteParticipants();
|
|
1050
|
-
|
|
1921
|
+
useEffect6(() => {
|
|
1051
1922
|
const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
|
|
1052
1923
|
if (agentParticipant) {
|
|
1053
1924
|
const attr = agentParticipant.attributes?.[attributeKey];
|
|
@@ -1099,7 +1970,7 @@ function usePhaseUpdates() {
|
|
|
1099
1970
|
}
|
|
1100
1971
|
|
|
1101
1972
|
// src/hooks/useSessionRemaining.ts
|
|
1102
|
-
import { useEffect as
|
|
1973
|
+
import { useEffect as useEffect7, useRef as useRef3, useState as useState4 } from "react";
|
|
1103
1974
|
|
|
1104
1975
|
// src/lib/format.ts
|
|
1105
1976
|
function formatTime(seconds) {
|
|
@@ -1115,9 +1986,9 @@ function parseNumber(s) {
|
|
|
1115
1986
|
// src/hooks/useSessionRemaining.ts
|
|
1116
1987
|
function useSessionRemaining() {
|
|
1117
1988
|
const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
|
|
1118
|
-
const endTimeRef =
|
|
1989
|
+
const endTimeRef = useRef3(null);
|
|
1119
1990
|
const [remaining, setRemaining] = useState4(null);
|
|
1120
|
-
|
|
1991
|
+
useEffect7(() => {
|
|
1121
1992
|
if (maxCallDuration === null || endTimeRef.current !== null)
|
|
1122
1993
|
return;
|
|
1123
1994
|
const endTime = Date.now() + maxCallDuration * 1000;
|
|
@@ -1133,14 +2004,11 @@ function useSessionRemaining() {
|
|
|
1133
2004
|
return remaining;
|
|
1134
2005
|
}
|
|
1135
2006
|
|
|
1136
|
-
// src/lib/constants.ts
|
|
1137
|
-
var SIDEBAR_WIDTH = 360;
|
|
1138
|
-
|
|
1139
2007
|
// src/hooks/useElapsedSeconds.ts
|
|
1140
|
-
import { useEffect as
|
|
2008
|
+
import { useEffect as useEffect8, useState as useState5 } from "react";
|
|
1141
2009
|
function useElapsedSeconds(isRunning) {
|
|
1142
2010
|
const [elapsed, setElapsed] = useState5(0);
|
|
1143
|
-
|
|
2011
|
+
useEffect8(() => {
|
|
1144
2012
|
if (!isRunning) {
|
|
1145
2013
|
setElapsed(0);
|
|
1146
2014
|
return;
|
|
@@ -1238,7 +2106,7 @@ function LoadingDots({ label }) {
|
|
|
1238
2106
|
}
|
|
1239
2107
|
|
|
1240
2108
|
// src/components/LoginFlow.tsx
|
|
1241
|
-
import { useCallback as useCallback5, useEffect as
|
|
2109
|
+
import { useCallback as useCallback5, useEffect as useEffect9, useRef as useRef4, useState as useState6 } from "react";
|
|
1242
2110
|
|
|
1243
2111
|
// src/components/ui/button.tsx
|
|
1244
2112
|
import { forwardRef as forwardRef3 } from "react";
|
|
@@ -1366,16 +2234,16 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
|
|
|
1366
2234
|
function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
1367
2235
|
const [digits, setDigits] = useState6(Array(OTP_LENGTH).fill(""));
|
|
1368
2236
|
const [resendCooldown, setResendCooldown] = useState6(0);
|
|
1369
|
-
const inputRefs =
|
|
1370
|
-
const submittedRef =
|
|
1371
|
-
|
|
2237
|
+
const inputRefs = useRef4([]);
|
|
2238
|
+
const submittedRef = useRef4(false);
|
|
2239
|
+
useEffect9(() => {
|
|
1372
2240
|
inputRefs.current[0]?.focus();
|
|
1373
2241
|
}, []);
|
|
1374
|
-
|
|
2242
|
+
useEffect9(() => {
|
|
1375
2243
|
if (error)
|
|
1376
2244
|
submittedRef.current = false;
|
|
1377
2245
|
}, [error]);
|
|
1378
|
-
|
|
2246
|
+
useEffect9(() => {
|
|
1379
2247
|
if (resendCooldown <= 0)
|
|
1380
2248
|
return;
|
|
1381
2249
|
const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
|
|
@@ -1518,7 +2386,7 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
1518
2386
|
// src/components/MeetingControls.tsx
|
|
1519
2387
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1520
2388
|
var CONTROL_BUTTON = "skippr:flex skippr:size-11 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-full skippr:transition-colors";
|
|
1521
|
-
function MeetingControls({ onHangUp }) {
|
|
2389
|
+
function MeetingControls({ onHangUp, showScreenShareToggle = true }) {
|
|
1522
2390
|
const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
|
|
1523
2391
|
return /* @__PURE__ */ jsxs8("div", {
|
|
1524
2392
|
className: "skippr:shrink-0 skippr:border-t skippr:border-border skippr:bg-background skippr:px-4 skippr:py-4",
|
|
@@ -1537,7 +2405,7 @@ function MeetingControls({ onHangUp }) {
|
|
|
1537
2405
|
className: "skippr:size-5"
|
|
1538
2406
|
})
|
|
1539
2407
|
}),
|
|
1540
|
-
/* @__PURE__ */ jsx9("button", {
|
|
2408
|
+
showScreenShareToggle && /* @__PURE__ */ jsx9("button", {
|
|
1541
2409
|
type: "button",
|
|
1542
2410
|
onClick: toggleScreenShare,
|
|
1543
2411
|
"aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
|
|
@@ -1568,17 +2436,17 @@ function MeetingControls({ onHangUp }) {
|
|
|
1568
2436
|
}
|
|
1569
2437
|
|
|
1570
2438
|
// src/components/MessageList.tsx
|
|
1571
|
-
import { useEffect as
|
|
2439
|
+
import { useEffect as useEffect11, useRef as useRef6 } from "react";
|
|
1572
2440
|
|
|
1573
2441
|
// src/components/ChatInput.tsx
|
|
1574
|
-
import { useEffect as
|
|
2442
|
+
import { useEffect as useEffect10, useRef as useRef5, useState as useState7 } from "react";
|
|
1575
2443
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1576
2444
|
var MAX_INPUT_HEIGHT = 60;
|
|
1577
2445
|
function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
|
|
1578
2446
|
const [inputText, setInputText] = useState7("");
|
|
1579
|
-
const textareaRef =
|
|
2447
|
+
const textareaRef = useRef5(null);
|
|
1580
2448
|
const canSend = inputText.trim().length > 0 && !isSendingChat;
|
|
1581
|
-
|
|
2449
|
+
useEffect10(() => {
|
|
1582
2450
|
if (autoFocus)
|
|
1583
2451
|
textareaRef.current?.focus();
|
|
1584
2452
|
}, [autoFocus]);
|
|
@@ -1723,9 +2591,9 @@ function MessageList({
|
|
|
1723
2591
|
isSendingChat,
|
|
1724
2592
|
autoFocus = false
|
|
1725
2593
|
}) {
|
|
1726
|
-
const scrollRef =
|
|
2594
|
+
const scrollRef = useRef6(null);
|
|
1727
2595
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
1728
|
-
|
|
2596
|
+
useEffect11(() => {
|
|
1729
2597
|
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1730
2598
|
}, [messages.length, lastMessage?.content]);
|
|
1731
2599
|
const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
|
|
@@ -1878,11 +2746,12 @@ function Sidebar({
|
|
|
1878
2746
|
isAuthSubmitting,
|
|
1879
2747
|
sidebarTab: activeTab,
|
|
1880
2748
|
setSidebarTab: setActiveTab,
|
|
1881
|
-
autoFocusChat
|
|
2749
|
+
autoFocusChat,
|
|
2750
|
+
captureMode
|
|
1882
2751
|
} = useLiveAgent();
|
|
1883
2752
|
const isFloating = variant === "floating";
|
|
1884
2753
|
const isSidebar = variant === "sidebar";
|
|
1885
|
-
|
|
2754
|
+
useEffect12(() => {
|
|
1886
2755
|
if (!isSidebar)
|
|
1887
2756
|
return;
|
|
1888
2757
|
const prop = position === "right" ? "marginRight" : "marginLeft";
|
|
@@ -1921,7 +2790,8 @@ function Sidebar({
|
|
|
1921
2790
|
onTabChange: setActiveTab,
|
|
1922
2791
|
hideControls,
|
|
1923
2792
|
startSessionLabel,
|
|
1924
|
-
autoFocusChat
|
|
2793
|
+
autoFocusChat,
|
|
2794
|
+
showScreenShareToggle: captureMode === "screenshare"
|
|
1925
2795
|
})
|
|
1926
2796
|
]
|
|
1927
2797
|
});
|
|
@@ -1936,7 +2806,8 @@ function AuthenticatedContent({
|
|
|
1936
2806
|
onTabChange,
|
|
1937
2807
|
hideControls,
|
|
1938
2808
|
startSessionLabel,
|
|
1939
|
-
autoFocusChat
|
|
2809
|
+
autoFocusChat,
|
|
2810
|
+
showScreenShareToggle
|
|
1940
2811
|
}) {
|
|
1941
2812
|
return /* @__PURE__ */ jsxs15(Fragment3, {
|
|
1942
2813
|
children: [
|
|
@@ -1990,7 +2861,8 @@ function AuthenticatedContent({
|
|
|
1990
2861
|
}, `${activeTab}-empty`)
|
|
1991
2862
|
}),
|
|
1992
2863
|
isConnected && !hideControls && /* @__PURE__ */ jsx17(MeetingControls, {
|
|
1993
|
-
onHangUp: onDisconnect
|
|
2864
|
+
onHangUp: onDisconnect,
|
|
2865
|
+
showScreenShareToggle
|
|
1994
2866
|
})
|
|
1995
2867
|
]
|
|
1996
2868
|
});
|
|
@@ -2066,6 +2938,7 @@ function LiveAgent({
|
|
|
2066
2938
|
startSessionLabel = "Talk to Skippr",
|
|
2067
2939
|
autoFocusChat = true,
|
|
2068
2940
|
showAgentStateBanner = true,
|
|
2941
|
+
captureMode = "screenshare",
|
|
2069
2942
|
children
|
|
2070
2943
|
}) {
|
|
2071
2944
|
const auth = useAuth({ appKey });
|
|
@@ -2081,6 +2954,7 @@ function LiveAgent({
|
|
|
2081
2954
|
pendingScreenStream
|
|
2082
2955
|
} = useSession({
|
|
2083
2956
|
agentId,
|
|
2957
|
+
captureMode,
|
|
2084
2958
|
authToken: effectiveAuthToken,
|
|
2085
2959
|
appKey,
|
|
2086
2960
|
userToken
|
|
@@ -2119,8 +2993,8 @@ function LiveAgent({
|
|
|
2119
2993
|
}, [minimizable]);
|
|
2120
2994
|
const isConnected = connection !== null;
|
|
2121
2995
|
const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
|
|
2122
|
-
const prevConnectionRef =
|
|
2123
|
-
|
|
2996
|
+
const prevConnectionRef = useRef7(connection);
|
|
2997
|
+
useEffect13(() => {
|
|
2124
2998
|
const connectionChanged = prevConnectionRef.current !== connection;
|
|
2125
2999
|
prevConnectionRef.current = connection;
|
|
2126
3000
|
if (connectionChanged && minimizable) {
|
|
@@ -2157,7 +3031,8 @@ function LiveAgent({
|
|
|
2157
3031
|
isAuthSubmitting: auth.isSubmitting,
|
|
2158
3032
|
sidebarTab,
|
|
2159
3033
|
setSidebarTab,
|
|
2160
|
-
autoFocusChat
|
|
3034
|
+
autoFocusChat,
|
|
3035
|
+
captureMode
|
|
2161
3036
|
}), [
|
|
2162
3037
|
connection,
|
|
2163
3038
|
shouldConnect,
|
|
@@ -2186,7 +3061,8 @@ function LiveAgent({
|
|
|
2186
3061
|
auth.logout,
|
|
2187
3062
|
auth.isSubmitting,
|
|
2188
3063
|
sidebarTab,
|
|
2189
|
-
autoFocusChat
|
|
3064
|
+
autoFocusChat,
|
|
3065
|
+
captureMode
|
|
2190
3066
|
]);
|
|
2191
3067
|
return /* @__PURE__ */ jsx19(LiveAgentContext.Provider, {
|
|
2192
3068
|
value: ctx,
|
|
@@ -2198,20 +3074,26 @@ function LiveAgent({
|
|
|
2198
3074
|
onDisconnected: disconnect,
|
|
2199
3075
|
children: [
|
|
2200
3076
|
connection && /* @__PURE__ */ jsx19(RoomAudioRenderer, {}),
|
|
2201
|
-
|
|
2202
|
-
connection && /* @__PURE__ */ jsx19(AutoStartMedia, {
|
|
3077
|
+
connection && captureMode === "screenshare" && /* @__PURE__ */ jsx19(AutoStartMedia, {
|
|
2203
3078
|
pendingScreenStream
|
|
2204
3079
|
}),
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
3080
|
+
connection && captureMode === "auto" && /* @__PURE__ */ jsx19(DomCapture, {}),
|
|
3081
|
+
/* @__PURE__ */ jsxs16("div", {
|
|
3082
|
+
id: WIDGET_ROOT_ID,
|
|
3083
|
+
children: [
|
|
3084
|
+
showAgentStateBanner && /* @__PURE__ */ jsx19(AgentStateBanner, {}),
|
|
3085
|
+
isMinimized && /* @__PURE__ */ jsx19(MinimizedBubble, {
|
|
3086
|
+
welcomeMessage,
|
|
3087
|
+
welcomeDismissed,
|
|
3088
|
+
onDismissWelcome: dismissWelcome
|
|
3089
|
+
}),
|
|
3090
|
+
/* @__PURE__ */ jsx19(SidebarTrigger, {}),
|
|
3091
|
+
/* @__PURE__ */ jsx19(Sidebar, {
|
|
3092
|
+
hideControls,
|
|
3093
|
+
hideHeader,
|
|
3094
|
+
startSessionLabel
|
|
3095
|
+
})
|
|
3096
|
+
]
|
|
2215
3097
|
}),
|
|
2216
3098
|
children
|
|
2217
3099
|
]
|
|
@@ -2219,9 +3101,9 @@ function LiveAgent({
|
|
|
2219
3101
|
});
|
|
2220
3102
|
}
|
|
2221
3103
|
// src/hooks/useIsLocalSpeaking.ts
|
|
2222
|
-
import { useIsSpeaking, useLocalParticipant as
|
|
3104
|
+
import { useIsSpeaking, useLocalParticipant as useLocalParticipant6 } from "@livekit/components-react/hooks";
|
|
2223
3105
|
function useIsLocalSpeaking() {
|
|
2224
|
-
const { localParticipant } =
|
|
3106
|
+
const { localParticipant } = useLocalParticipant6();
|
|
2225
3107
|
return useIsSpeaking(localParticipant);
|
|
2226
3108
|
}
|
|
2227
3109
|
export {
|