@skippr/live-agent-sdk 0.27.0 → 0.29.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 +5 -2
- package/dist/esm/lib-exports.js +1457 -302
- 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/HighlightOverlay.d.ts +1 -0
- package/dist/types/components/LiveAgent.d.ts +10 -2
- package/dist/types/components/MeetingControls.d.ts +2 -1
- package/dist/types/context/LiveAgentContext.d.ts +6 -0
- package/dist/types/hooks/useAgentVoiceState.d.ts +1 -1
- package/dist/types/hooks/useSession.d.ts +4 -1
- package/dist/types/lib/constants.d.ts +7 -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
|
|
11
|
+
import { useCallback as useCallback7, useEffect as useEffect14, useMemo as useMemo5, useRef as useRef8, useState as useState9 } from "react";
|
|
4
12
|
|
|
5
13
|
// src/context/LiveAgentContext.tsx
|
|
6
14
|
import { createContext } from "react";
|
|
@@ -165,7 +173,14 @@ 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
|
+
agentControls,
|
|
180
|
+
authToken,
|
|
181
|
+
appKey,
|
|
182
|
+
userToken
|
|
183
|
+
}) {
|
|
169
184
|
const [connection, setConnection] = useState2(null);
|
|
170
185
|
const [shouldConnect, setShouldConnect] = useState2(false);
|
|
171
186
|
const [isStarting, setIsStarting] = useState2(false);
|
|
@@ -193,6 +208,7 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
193
208
|
stale = true;
|
|
194
209
|
};
|
|
195
210
|
}, [authToken, appKey, userToken]);
|
|
211
|
+
const highlightOptIn = agentControls?.highlight;
|
|
196
212
|
const startSession = useCallback2(async () => {
|
|
197
213
|
if (!bearerToken) {
|
|
198
214
|
setError("No auth token available");
|
|
@@ -202,20 +218,27 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
202
218
|
setError("");
|
|
203
219
|
setErrorCode(null);
|
|
204
220
|
let screenStream = null;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
221
|
+
if (captureMode === "screenshare") {
|
|
222
|
+
try {
|
|
223
|
+
screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
224
|
+
video: { displaySurface: "browser" }
|
|
225
|
+
});
|
|
226
|
+
} catch {
|
|
227
|
+
screenStream = null;
|
|
228
|
+
}
|
|
211
229
|
}
|
|
230
|
+
const requestAgentControls = highlightOptIn === true ? { highlight: true } : undefined;
|
|
212
231
|
const headers = { Authorization: `Bearer ${bearerToken}` };
|
|
213
232
|
try {
|
|
214
233
|
const createResp = await fetch(`${API_URL2}/v1/sessions`, {
|
|
215
234
|
method: "POST",
|
|
216
235
|
credentials: "omit",
|
|
217
236
|
headers: { "Content-Type": "application/json", ...headers },
|
|
218
|
-
body: JSON.stringify({
|
|
237
|
+
body: JSON.stringify({
|
|
238
|
+
agentId,
|
|
239
|
+
captureMode,
|
|
240
|
+
agentControls: requestAgentControls
|
|
241
|
+
})
|
|
219
242
|
});
|
|
220
243
|
if (!createResp.ok) {
|
|
221
244
|
const body = await createResp.json().catch(() => null);
|
|
@@ -250,7 +273,7 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
250
273
|
} finally {
|
|
251
274
|
setIsStarting(false);
|
|
252
275
|
}
|
|
253
|
-
}, [agentId, bearerToken]);
|
|
276
|
+
}, [agentId, captureMode, highlightOptIn, bearerToken]);
|
|
254
277
|
const disconnect = useCallback2(async () => {
|
|
255
278
|
if (sessionId && bearerToken) {
|
|
256
279
|
try {
|
|
@@ -283,6 +306,16 @@ function useSession({ agentId, authToken, appKey, userToken }) {
|
|
|
283
306
|
pendingScreenStream
|
|
284
307
|
};
|
|
285
308
|
}
|
|
309
|
+
|
|
310
|
+
// src/lib/constants.ts
|
|
311
|
+
var SIDEBAR_WIDTH = 360;
|
|
312
|
+
var WIDGET_ROOT_ID = "skippr-sdk-root";
|
|
313
|
+
var REF_ATTR = "data-skippr-ref";
|
|
314
|
+
var PRIVATE_ATTR = "data-skippr-private";
|
|
315
|
+
var DOM_SNAPSHOT_TOPIC = "skippr.dom-snapshot";
|
|
316
|
+
var DOM_EVENTS_TOPIC = "skippr.dom-events";
|
|
317
|
+
var HIGHLIGHT_TOPIC = "skippr.highlight";
|
|
318
|
+
var NAME_MAX_CHARS = 80;
|
|
286
319
|
// ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
|
|
287
320
|
import { forwardRef as forwardRef2, createElement as createElement3 } from "react";
|
|
288
321
|
|
|
@@ -513,7 +546,7 @@ var __iconNode16 = [
|
|
|
513
546
|
];
|
|
514
547
|
var Send = createLucideIcon("send", __iconNode16);
|
|
515
548
|
// src/hooks/useAgentVoiceState.ts
|
|
516
|
-
import { useVoiceAssistant } from "@livekit/components-react";
|
|
549
|
+
import { useVoiceAssistant } from "@livekit/components-react/hooks";
|
|
517
550
|
function useAgentVoiceState() {
|
|
518
551
|
const { state } = useVoiceAssistant();
|
|
519
552
|
return {
|
|
@@ -535,7 +568,7 @@ function useLiveAgent() {
|
|
|
535
568
|
}
|
|
536
569
|
|
|
537
570
|
// src/hooks/useMediaControls.ts
|
|
538
|
-
import { useLocalParticipant } from "@livekit/components-react";
|
|
571
|
+
import { useLocalParticipant } from "@livekit/components-react/hooks";
|
|
539
572
|
import { ScreenSharePresets } from "livekit-client";
|
|
540
573
|
import { useCallback as useCallback3 } from "react";
|
|
541
574
|
var SCREEN_SHARE_OPTIONS = {
|
|
@@ -682,7 +715,7 @@ function SpeakingBars() {
|
|
|
682
715
|
}
|
|
683
716
|
|
|
684
717
|
// src/components/AutoStartMedia.tsx
|
|
685
|
-
import { useConnectionState, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react";
|
|
718
|
+
import { useConnectionState, useLocalParticipant as useLocalParticipant2 } from "@livekit/components-react/hooks";
|
|
686
719
|
import { ConnectionState, Track } from "livekit-client";
|
|
687
720
|
import { useEffect as useEffect3, useRef } from "react";
|
|
688
721
|
function AutoStartMedia({ pendingScreenStream }) {
|
|
@@ -711,8 +744,1105 @@ function AutoStartMedia({ pendingScreenStream }) {
|
|
|
711
744
|
return null;
|
|
712
745
|
}
|
|
713
746
|
|
|
747
|
+
// src/components/DomCapture.tsx
|
|
748
|
+
import { useConnectionState as useConnectionState2, useLocalParticipant as useLocalParticipant3 } from "@livekit/components-react/hooks";
|
|
749
|
+
import { ConnectionState as ConnectionState2, ScreenSharePresets as ScreenSharePresets2, Track as Track2 } from "livekit-client";
|
|
750
|
+
import { useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
751
|
+
|
|
752
|
+
// src/capture/a11yUtils.ts
|
|
753
|
+
var ROLE_BY_TAG = {
|
|
754
|
+
a: "link",
|
|
755
|
+
button: "button",
|
|
756
|
+
input: "input",
|
|
757
|
+
select: "select",
|
|
758
|
+
textarea: "textarea",
|
|
759
|
+
label: "label",
|
|
760
|
+
nav: "navigation",
|
|
761
|
+
main: "main",
|
|
762
|
+
header: "header",
|
|
763
|
+
footer: "footer",
|
|
764
|
+
aside: "complementary",
|
|
765
|
+
form: "form",
|
|
766
|
+
h1: "heading",
|
|
767
|
+
h2: "heading",
|
|
768
|
+
h3: "heading",
|
|
769
|
+
h4: "heading",
|
|
770
|
+
h5: "heading",
|
|
771
|
+
h6: "heading",
|
|
772
|
+
img: "image",
|
|
773
|
+
ul: "list",
|
|
774
|
+
ol: "list",
|
|
775
|
+
li: "listitem",
|
|
776
|
+
table: "table",
|
|
777
|
+
tr: "row",
|
|
778
|
+
td: "cell",
|
|
779
|
+
th: "columnheader",
|
|
780
|
+
video: "video",
|
|
781
|
+
iframe: "iframe",
|
|
782
|
+
canvas: "canvas",
|
|
783
|
+
audio: "audio",
|
|
784
|
+
embed: "embed",
|
|
785
|
+
object: "object"
|
|
786
|
+
};
|
|
787
|
+
function inferRole(element) {
|
|
788
|
+
const explicitRole = element.getAttribute("role");
|
|
789
|
+
if (explicitRole)
|
|
790
|
+
return explicitRole;
|
|
791
|
+
const tagName = element.tagName.toLowerCase();
|
|
792
|
+
if (tagName === "input") {
|
|
793
|
+
const inputType = element.type;
|
|
794
|
+
if (inputType === "checkbox")
|
|
795
|
+
return "checkbox";
|
|
796
|
+
if (inputType === "radio")
|
|
797
|
+
return "radio";
|
|
798
|
+
if (inputType === "submit" || inputType === "button")
|
|
799
|
+
return "button";
|
|
800
|
+
return "input";
|
|
801
|
+
}
|
|
802
|
+
return ROLE_BY_TAG[tagName] ?? null;
|
|
803
|
+
}
|
|
804
|
+
function accessibleName(element, options = {}) {
|
|
805
|
+
const ariaLabel = element.getAttribute("aria-label");
|
|
806
|
+
if (ariaLabel)
|
|
807
|
+
return ariaLabel.trim().slice(0, NAME_MAX_CHARS);
|
|
808
|
+
if (!options.quick) {
|
|
809
|
+
const labelledBy = element.getAttribute("aria-labelledby");
|
|
810
|
+
if (labelledBy) {
|
|
811
|
+
const labelElement = document.getElementById(labelledBy);
|
|
812
|
+
if (labelElement)
|
|
813
|
+
return (labelElement.textContent || "").trim().slice(0, NAME_MAX_CHARS);
|
|
814
|
+
}
|
|
815
|
+
if (element.tagName === "INPUT" || element.tagName === "SELECT" || element.tagName === "TEXTAREA") {
|
|
816
|
+
const elementId = element.id;
|
|
817
|
+
if (elementId) {
|
|
818
|
+
const associatedLabel = document.querySelector(`label[for="${CSS.escape(elementId)}"]`);
|
|
819
|
+
if (associatedLabel?.textContent) {
|
|
820
|
+
return associatedLabel.textContent.trim().slice(0, NAME_MAX_CHARS);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
const placeholder = element.getAttribute("placeholder");
|
|
824
|
+
if (placeholder)
|
|
825
|
+
return placeholder.trim().slice(0, NAME_MAX_CHARS);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (element.tagName === "IMG") {
|
|
829
|
+
const altText = element.getAttribute("alt");
|
|
830
|
+
if (altText)
|
|
831
|
+
return altText.trim().slice(0, NAME_MAX_CHARS);
|
|
832
|
+
}
|
|
833
|
+
const titleAttr = element.getAttribute("title");
|
|
834
|
+
if (titleAttr)
|
|
835
|
+
return titleAttr.trim().slice(0, NAME_MAX_CHARS);
|
|
836
|
+
const textContent = (element.textContent || "").replace(/\s+/g, " ").trim();
|
|
837
|
+
if (textContent)
|
|
838
|
+
return textContent.slice(0, NAME_MAX_CHARS);
|
|
839
|
+
return "";
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/capture/a11yTree.ts
|
|
843
|
+
var REF_PREFIX = "r-";
|
|
844
|
+
var VALUE_MAX_CHARS = 80;
|
|
845
|
+
var HREF_MAX_CHARS = 60;
|
|
846
|
+
var PAGE_CONTENT_MAX_BYTES = 14000;
|
|
847
|
+
var NODE_LIMIT = 1500;
|
|
848
|
+
var SENSITIVE_IFRAME_HOSTS = [
|
|
849
|
+
"js.stripe.com",
|
|
850
|
+
"checkout.stripe.com",
|
|
851
|
+
"connect.stripe.com",
|
|
852
|
+
"plaid.com",
|
|
853
|
+
"production.plaid.com",
|
|
854
|
+
"cdn.plaid.com",
|
|
855
|
+
"auth0.com",
|
|
856
|
+
"login.auth0.com",
|
|
857
|
+
"recaptcha.net",
|
|
858
|
+
"www.google.com",
|
|
859
|
+
"hcaptcha.com",
|
|
860
|
+
"newassets.hcaptcha.com",
|
|
861
|
+
"challenges.cloudflare.com"
|
|
862
|
+
];
|
|
863
|
+
var MASK_AUTOCOMPLETE_VALUES = new Set([
|
|
864
|
+
"cc-number",
|
|
865
|
+
"cc-exp",
|
|
866
|
+
"cc-exp-month",
|
|
867
|
+
"cc-exp-year",
|
|
868
|
+
"cc-csc",
|
|
869
|
+
"cc-name",
|
|
870
|
+
"cc-given-name",
|
|
871
|
+
"cc-family-name",
|
|
872
|
+
"current-password",
|
|
873
|
+
"new-password",
|
|
874
|
+
"one-time-code"
|
|
875
|
+
]);
|
|
876
|
+
var MASK_INPUT_TYPES = new Set(["password", "hidden"]);
|
|
877
|
+
var MASK_NAME_PATTERN = /password|secret|token|api[_-]?key|ssn|tax[_-]?id|social[_-]?security|cvv|cvc|pin/i;
|
|
878
|
+
var refCounter = 0;
|
|
879
|
+
function generateNextRef() {
|
|
880
|
+
refCounter += 1;
|
|
881
|
+
return `${REF_PREFIX}${refCounter}`;
|
|
882
|
+
}
|
|
883
|
+
function advanceCounterPastExisting(ref) {
|
|
884
|
+
const parsedRefNumber = Number.parseInt(ref.slice(REF_PREFIX.length), 10);
|
|
885
|
+
if (Number.isFinite(parsedRefNumber) && parsedRefNumber > refCounter) {
|
|
886
|
+
refCounter = parsedRefNumber;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function intersectsViewport(rect, viewportWidth, viewportHeight) {
|
|
890
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
891
|
+
return false;
|
|
892
|
+
if (rect.bottom <= 0 || rect.top >= viewportHeight)
|
|
893
|
+
return false;
|
|
894
|
+
if (rect.right <= 0 || rect.left >= viewportWidth)
|
|
895
|
+
return false;
|
|
896
|
+
return true;
|
|
897
|
+
}
|
|
898
|
+
function isVisible(element, rect) {
|
|
899
|
+
if (!intersectsViewport(rect, window.innerWidth, window.innerHeight))
|
|
900
|
+
return false;
|
|
901
|
+
const style = window.getComputedStyle(element);
|
|
902
|
+
if (style.visibility === "hidden" || style.display === "none")
|
|
903
|
+
return false;
|
|
904
|
+
if (Number.parseFloat(style.opacity || "1") <= 0.01)
|
|
905
|
+
return false;
|
|
906
|
+
return true;
|
|
907
|
+
}
|
|
908
|
+
function isInteractive(element, role) {
|
|
909
|
+
if (role && role !== "heading" && role !== "image" && role !== "list" && role !== "listitem") {
|
|
910
|
+
const interactiveRoles = [
|
|
911
|
+
"button",
|
|
912
|
+
"link",
|
|
913
|
+
"input",
|
|
914
|
+
"select",
|
|
915
|
+
"textarea",
|
|
916
|
+
"checkbox",
|
|
917
|
+
"radio",
|
|
918
|
+
"tab",
|
|
919
|
+
"menuitem",
|
|
920
|
+
"option",
|
|
921
|
+
"switch",
|
|
922
|
+
"slider"
|
|
923
|
+
];
|
|
924
|
+
if (interactiveRoles.includes(role))
|
|
925
|
+
return true;
|
|
926
|
+
}
|
|
927
|
+
if (element.tabIndex >= 0)
|
|
928
|
+
return true;
|
|
929
|
+
if (element.hasAttribute("onclick") || element.hasAttribute("contenteditable"))
|
|
930
|
+
return true;
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
function isBlindRegion(role) {
|
|
934
|
+
return role === "video" || role === "iframe" || role === "canvas" || role === "audio" || role === "embed" || role === "object";
|
|
935
|
+
}
|
|
936
|
+
function getIframeHost(element) {
|
|
937
|
+
const src = element.getAttribute("src");
|
|
938
|
+
if (!src)
|
|
939
|
+
return null;
|
|
940
|
+
try {
|
|
941
|
+
return new URL(src, window.location.href).host;
|
|
942
|
+
} catch {
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
function isSensitiveIframe(element) {
|
|
947
|
+
const host = getIframeHost(element);
|
|
948
|
+
if (!host)
|
|
949
|
+
return false;
|
|
950
|
+
return SENSITIVE_IFRAME_HOSTS.some((sensitiveHost) => host === sensitiveHost || host.endsWith(`.${sensitiveHost}`));
|
|
951
|
+
}
|
|
952
|
+
function isSameOriginIframe(element) {
|
|
953
|
+
const src = element.getAttribute("src");
|
|
954
|
+
if (!src)
|
|
955
|
+
return true;
|
|
956
|
+
try {
|
|
957
|
+
return new URL(src, window.location.href).origin === window.location.origin;
|
|
958
|
+
} catch {
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
function nearestHeadingText(element) {
|
|
963
|
+
let currentNode = element;
|
|
964
|
+
while (currentNode && currentNode !== document.body) {
|
|
965
|
+
let previousSibling = currentNode.previousElementSibling;
|
|
966
|
+
while (previousSibling) {
|
|
967
|
+
if (/^H[1-6]$/.test(previousSibling.tagName) && previousSibling.textContent) {
|
|
968
|
+
return previousSibling.textContent.trim().slice(0, NAME_MAX_CHARS);
|
|
969
|
+
}
|
|
970
|
+
previousSibling = previousSibling.previousElementSibling;
|
|
971
|
+
}
|
|
972
|
+
currentNode = currentNode.parentElement;
|
|
973
|
+
}
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
function shouldMaskValue(input) {
|
|
977
|
+
if (input.tagName === "INPUT") {
|
|
978
|
+
const inputElement = input;
|
|
979
|
+
if (MASK_INPUT_TYPES.has(inputElement.type))
|
|
980
|
+
return true;
|
|
981
|
+
const autocompleteAttr = (inputElement.getAttribute("autocomplete") || "").toLowerCase().trim();
|
|
982
|
+
if (MASK_AUTOCOMPLETE_VALUES.has(autocompleteAttr))
|
|
983
|
+
return true;
|
|
984
|
+
}
|
|
985
|
+
const fieldName = (input.getAttribute("name") || "").trim();
|
|
986
|
+
const fieldId = (input.getAttribute("id") || "").trim();
|
|
987
|
+
if (MASK_NAME_PATTERN.test(fieldName) || MASK_NAME_PATTERN.test(fieldId))
|
|
988
|
+
return true;
|
|
989
|
+
return false;
|
|
990
|
+
}
|
|
991
|
+
function escapeAttributeValue(value) {
|
|
992
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/[\r\n]+/g, " ");
|
|
993
|
+
}
|
|
994
|
+
function extractAttrs(element, role) {
|
|
995
|
+
const attributes = [];
|
|
996
|
+
if (role === "link") {
|
|
997
|
+
const href = element.getAttribute("href");
|
|
998
|
+
if (href)
|
|
999
|
+
attributes.push(`href="${escapeAttributeValue(href.slice(0, HREF_MAX_CHARS))}"`);
|
|
1000
|
+
}
|
|
1001
|
+
if (role === "input" || role === "checkbox" || role === "radio") {
|
|
1002
|
+
const input = element;
|
|
1003
|
+
if (input.type && input.type !== "text")
|
|
1004
|
+
attributes.push(`type="${input.type}"`);
|
|
1005
|
+
if (shouldMaskValue(input)) {
|
|
1006
|
+
attributes.push("valueMasked");
|
|
1007
|
+
} else if (input.value && role === "input") {
|
|
1008
|
+
attributes.push(`value="${escapeAttributeValue(input.value.slice(0, VALUE_MAX_CHARS))}"`);
|
|
1009
|
+
}
|
|
1010
|
+
if (input.checked)
|
|
1011
|
+
attributes.push("checked");
|
|
1012
|
+
}
|
|
1013
|
+
if (role === "textarea") {
|
|
1014
|
+
const textarea = element;
|
|
1015
|
+
if (shouldMaskValue(textarea)) {
|
|
1016
|
+
attributes.push("valueMasked");
|
|
1017
|
+
} else if (textarea.value) {
|
|
1018
|
+
attributes.push(`value="${escapeAttributeValue(textarea.value.slice(0, VALUE_MAX_CHARS))}"`);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (role === "select") {
|
|
1022
|
+
const select = element;
|
|
1023
|
+
const selectedOption = select.options[select.selectedIndex];
|
|
1024
|
+
if (selectedOption?.value?.trim()) {
|
|
1025
|
+
attributes.push(`selected="${escapeAttributeValue(selectedOption.value.slice(0, VALUE_MAX_CHARS))}"`);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (role === "heading") {
|
|
1029
|
+
const headingLevel = element.tagName.match(/^H([1-6])$/)?.[1];
|
|
1030
|
+
if (headingLevel)
|
|
1031
|
+
attributes.push(`level="${headingLevel}"`);
|
|
1032
|
+
}
|
|
1033
|
+
if (role === "video" || role === "audio") {
|
|
1034
|
+
const mediaElement = element;
|
|
1035
|
+
try {
|
|
1036
|
+
if (Number.isFinite(mediaElement.currentTime)) {
|
|
1037
|
+
attributes.push(`currentTime="${mediaElement.currentTime.toFixed(1)}"`);
|
|
1038
|
+
}
|
|
1039
|
+
if (Number.isFinite(mediaElement.duration) && mediaElement.duration > 0) {
|
|
1040
|
+
attributes.push(`duration="${mediaElement.duration.toFixed(1)}"`);
|
|
1041
|
+
}
|
|
1042
|
+
attributes.push(mediaElement.paused ? "paused" : "playing");
|
|
1043
|
+
if (mediaElement.muted)
|
|
1044
|
+
attributes.push("muted");
|
|
1045
|
+
const mediaSrc = mediaElement.currentSrc || mediaElement.getAttribute("src");
|
|
1046
|
+
if (mediaSrc) {
|
|
1047
|
+
try {
|
|
1048
|
+
attributes.push(`srcHost="${new URL(mediaSrc, window.location.href).host}"`);
|
|
1049
|
+
} catch {}
|
|
1050
|
+
}
|
|
1051
|
+
} catch {}
|
|
1052
|
+
}
|
|
1053
|
+
if (role === "iframe") {
|
|
1054
|
+
const host = getIframeHost(element);
|
|
1055
|
+
if (host)
|
|
1056
|
+
attributes.push(`srcHost="${host}"`);
|
|
1057
|
+
if (isSensitiveIframe(element)) {
|
|
1058
|
+
attributes.push("sensitive");
|
|
1059
|
+
} else if (!isSameOriginIframe(element)) {
|
|
1060
|
+
attributes.push("cross-origin");
|
|
1061
|
+
}
|
|
1062
|
+
const iframeRect = element.getBoundingClientRect();
|
|
1063
|
+
attributes.push(`size="${Math.round(iframeRect.width)}x${Math.round(iframeRect.height)}"`);
|
|
1064
|
+
}
|
|
1065
|
+
if (role === "canvas") {
|
|
1066
|
+
const canvas = element;
|
|
1067
|
+
attributes.push(`size="${canvas.width}x${canvas.height}"`);
|
|
1068
|
+
const heading = nearestHeadingText(element);
|
|
1069
|
+
if (heading)
|
|
1070
|
+
attributes.push(`nearestHeading="${escapeAttributeValue(heading)}"`);
|
|
1071
|
+
}
|
|
1072
|
+
if (role === "image") {
|
|
1073
|
+
const image = element;
|
|
1074
|
+
const imageSrc = image.getAttribute("src") || "";
|
|
1075
|
+
if (/\.gif(\?|$)/i.test(imageSrc))
|
|
1076
|
+
attributes.push("animated");
|
|
1077
|
+
}
|
|
1078
|
+
const isNativelyDisabled = "disabled" in element && element.disabled === true;
|
|
1079
|
+
if (isNativelyDisabled || element.getAttribute("aria-disabled") === "true") {
|
|
1080
|
+
attributes.push("disabled");
|
|
1081
|
+
}
|
|
1082
|
+
return attributes;
|
|
1083
|
+
}
|
|
1084
|
+
function ensureStableRef(element) {
|
|
1085
|
+
const existingRef = element.getAttribute(REF_ATTR);
|
|
1086
|
+
if (existingRef) {
|
|
1087
|
+
advanceCounterPastExisting(existingRef);
|
|
1088
|
+
return existingRef;
|
|
1089
|
+
}
|
|
1090
|
+
const newRef = generateNextRef();
|
|
1091
|
+
element.setAttribute(REF_ATTR, newRef);
|
|
1092
|
+
return newRef;
|
|
1093
|
+
}
|
|
1094
|
+
function formatNodeAsLine(entry) {
|
|
1095
|
+
const indent = " ".repeat(entry.depth);
|
|
1096
|
+
const namePart = entry.name ? ` "${escapeAttributeValue(entry.name)}"` : "";
|
|
1097
|
+
const attrsPart = entry.attrs.length > 0 ? ` ${entry.attrs.join(" ")}` : "";
|
|
1098
|
+
return `${indent}${entry.role}${namePart} ${entry.ref}${attrsPart}`;
|
|
1099
|
+
}
|
|
1100
|
+
function shouldSkipSubtree(element) {
|
|
1101
|
+
if (element.id === WIDGET_ROOT_ID)
|
|
1102
|
+
return true;
|
|
1103
|
+
if (element.hasAttribute(PRIVATE_ATTR))
|
|
1104
|
+
return true;
|
|
1105
|
+
const tagName = element.tagName;
|
|
1106
|
+
if (tagName === "SCRIPT" || tagName === "STYLE" || tagName === "NOSCRIPT")
|
|
1107
|
+
return true;
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
function shouldEmitElement(element, role) {
|
|
1111
|
+
if (role === null)
|
|
1112
|
+
return false;
|
|
1113
|
+
return isInteractive(element, role) || role === "heading" || role === "image" || role === "label" || isBlindRegion(role);
|
|
1114
|
+
}
|
|
1115
|
+
function getSameOriginIframeBody(element) {
|
|
1116
|
+
if (element.tagName !== "IFRAME")
|
|
1117
|
+
return null;
|
|
1118
|
+
if (!isSameOriginIframe(element) || isSensitiveIframe(element))
|
|
1119
|
+
return null;
|
|
1120
|
+
try {
|
|
1121
|
+
return element.contentDocument?.body ?? null;
|
|
1122
|
+
} catch {
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function walkAndCollect(element, depth, entries) {
|
|
1127
|
+
if (shouldSkipSubtree(element))
|
|
1128
|
+
return;
|
|
1129
|
+
const rect = element.getBoundingClientRect();
|
|
1130
|
+
const role = inferRole(element);
|
|
1131
|
+
const isVisibleAndEmittable = role !== null && isVisible(element, rect) && shouldEmitElement(element, role);
|
|
1132
|
+
let childDepth = depth;
|
|
1133
|
+
if (isVisibleAndEmittable && role) {
|
|
1134
|
+
const ref = ensureStableRef(element);
|
|
1135
|
+
entries.push({
|
|
1136
|
+
depth,
|
|
1137
|
+
role,
|
|
1138
|
+
name: accessibleName(element),
|
|
1139
|
+
ref,
|
|
1140
|
+
attrs: extractAttrs(element, role),
|
|
1141
|
+
area: rect.width * rect.height
|
|
1142
|
+
});
|
|
1143
|
+
childDepth = depth + 1;
|
|
1144
|
+
}
|
|
1145
|
+
if (role === "iframe") {
|
|
1146
|
+
const iframeBody = getSameOriginIframeBody(element);
|
|
1147
|
+
if (iframeBody) {
|
|
1148
|
+
for (const child of Array.from(iframeBody.children)) {
|
|
1149
|
+
walkAndCollect(child, childDepth, entries);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
if (role && isBlindRegion(role))
|
|
1155
|
+
return;
|
|
1156
|
+
for (const child of Array.from(element.children)) {
|
|
1157
|
+
walkAndCollect(child, childDepth, entries);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function dropSmallestAreaEntriesUntilUnderLimit(entries) {
|
|
1161
|
+
if (entries.length <= NODE_LIMIT)
|
|
1162
|
+
return entries;
|
|
1163
|
+
const indexedEntries = entries.map((entry, index2) => ({ entry, index: index2 }));
|
|
1164
|
+
const entriesByAreaAscending = indexedEntries.slice(1).sort((a, b) => a.entry.area - b.entry.area);
|
|
1165
|
+
const indicesToDrop = new Set(entriesByAreaAscending.slice(0, entries.length - NODE_LIMIT).map((indexed) => indexed.index));
|
|
1166
|
+
const keptEntries = [];
|
|
1167
|
+
for (let i = 0;i < entries.length; i++) {
|
|
1168
|
+
if (!indicesToDrop.has(i))
|
|
1169
|
+
keptEntries.push(entries[i]);
|
|
1170
|
+
}
|
|
1171
|
+
return keptEntries;
|
|
1172
|
+
}
|
|
1173
|
+
function buildAccessibilityTree() {
|
|
1174
|
+
const collectedEntries = [];
|
|
1175
|
+
const pageTitle = document.title || window.location.pathname;
|
|
1176
|
+
collectedEntries.push({
|
|
1177
|
+
depth: 0,
|
|
1178
|
+
role: "page",
|
|
1179
|
+
name: pageTitle.slice(0, NAME_MAX_CHARS),
|
|
1180
|
+
ref: `${REF_PREFIX}0`,
|
|
1181
|
+
attrs: [],
|
|
1182
|
+
area: window.innerWidth * window.innerHeight
|
|
1183
|
+
});
|
|
1184
|
+
if (document.body) {
|
|
1185
|
+
for (const child of Array.from(document.body.children)) {
|
|
1186
|
+
walkAndCollect(child, 1, collectedEntries);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const exceededNodeLimit = collectedEntries.length > NODE_LIMIT;
|
|
1190
|
+
const finalEntries = exceededNodeLimit ? dropSmallestAreaEntriesUntilUnderLimit(collectedEntries) : collectedEntries;
|
|
1191
|
+
const treeLines = finalEntries.map(formatNodeAsLine);
|
|
1192
|
+
let pageContent = treeLines.join(`
|
|
1193
|
+
`);
|
|
1194
|
+
const exceededByteLimit = pageContent.length > PAGE_CONTENT_MAX_BYTES;
|
|
1195
|
+
if (exceededByteLimit) {
|
|
1196
|
+
pageContent = `${pageContent.slice(0, PAGE_CONTENT_MAX_BYTES)}
|
|
1197
|
+
<!-- truncated -->`;
|
|
1198
|
+
}
|
|
1199
|
+
return {
|
|
1200
|
+
pageContent,
|
|
1201
|
+
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
1202
|
+
refCount: finalEntries.length - 1,
|
|
1203
|
+
truncated: exceededNodeLimit || exceededByteLimit
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// src/capture/domEvents.ts
|
|
1208
|
+
var MUTATION_DEBOUNCE_MS = 250;
|
|
1209
|
+
var textEncoder = new TextEncoder;
|
|
1210
|
+
function isInsideWidget(target) {
|
|
1211
|
+
if (!(target instanceof Node))
|
|
1212
|
+
return false;
|
|
1213
|
+
let node = target;
|
|
1214
|
+
while (node) {
|
|
1215
|
+
if (node instanceof Element && node.id === WIDGET_ROOT_ID)
|
|
1216
|
+
return true;
|
|
1217
|
+
node = node.parentNode;
|
|
1218
|
+
}
|
|
1219
|
+
return false;
|
|
1220
|
+
}
|
|
1221
|
+
function findNearestRef(element) {
|
|
1222
|
+
let currentNode = element;
|
|
1223
|
+
while (currentNode) {
|
|
1224
|
+
const ref = currentNode.getAttribute(REF_ATTR);
|
|
1225
|
+
if (ref)
|
|
1226
|
+
return { ref, node: currentNode };
|
|
1227
|
+
currentNode = currentNode.parentElement;
|
|
1228
|
+
}
|
|
1229
|
+
return null;
|
|
1230
|
+
}
|
|
1231
|
+
function installDomEventListeners(localParticipant, options = {}) {
|
|
1232
|
+
const { onTriggerEvent } = options;
|
|
1233
|
+
function publishDomEvent(event) {
|
|
1234
|
+
try {
|
|
1235
|
+
localParticipant.publishData(textEncoder.encode(JSON.stringify(event)), {
|
|
1236
|
+
reliable: true,
|
|
1237
|
+
topic: DOM_EVENTS_TOPIC
|
|
1238
|
+
}).catch(() => {
|
|
1239
|
+
return;
|
|
1240
|
+
});
|
|
1241
|
+
} catch {}
|
|
1242
|
+
if (onTriggerEvent) {
|
|
1243
|
+
try {
|
|
1244
|
+
onTriggerEvent();
|
|
1245
|
+
} catch {}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
const onClick = (event) => {
|
|
1249
|
+
if (isInsideWidget(event.target))
|
|
1250
|
+
return;
|
|
1251
|
+
if (!(event.target instanceof Element))
|
|
1252
|
+
return;
|
|
1253
|
+
const matchedRef = findNearestRef(event.target);
|
|
1254
|
+
const targetElement = matchedRef?.node ?? event.target;
|
|
1255
|
+
publishDomEvent({
|
|
1256
|
+
type: "click",
|
|
1257
|
+
ref: matchedRef?.ref ?? null,
|
|
1258
|
+
role: inferRole(targetElement) ?? targetElement.tagName.toLowerCase(),
|
|
1259
|
+
name: accessibleName(targetElement, { quick: true }),
|
|
1260
|
+
timestamp: Date.now()
|
|
1261
|
+
});
|
|
1262
|
+
};
|
|
1263
|
+
document.addEventListener("click", onClick, true);
|
|
1264
|
+
const onSubmit = (event) => {
|
|
1265
|
+
if (isInsideWidget(event.target))
|
|
1266
|
+
return;
|
|
1267
|
+
if (!(event.target instanceof HTMLFormElement))
|
|
1268
|
+
return;
|
|
1269
|
+
const submittedForm = event.target;
|
|
1270
|
+
publishDomEvent({
|
|
1271
|
+
type: "submit",
|
|
1272
|
+
ref: submittedForm.getAttribute(REF_ATTR),
|
|
1273
|
+
formName: (submittedForm.name || submittedForm.id || "").slice(0, NAME_MAX_CHARS),
|
|
1274
|
+
action: (submittedForm.action || "").slice(0, NAME_MAX_CHARS),
|
|
1275
|
+
timestamp: Date.now()
|
|
1276
|
+
});
|
|
1277
|
+
};
|
|
1278
|
+
document.addEventListener("submit", onSubmit, true);
|
|
1279
|
+
const originalPushState = history.pushState;
|
|
1280
|
+
const originalReplaceState = history.replaceState;
|
|
1281
|
+
let previousUrl = window.location.href;
|
|
1282
|
+
const emitNavigationIfChanged = () => {
|
|
1283
|
+
const nextUrl = window.location.href;
|
|
1284
|
+
if (nextUrl === previousUrl)
|
|
1285
|
+
return;
|
|
1286
|
+
publishDomEvent({
|
|
1287
|
+
type: "navigation",
|
|
1288
|
+
from: previousUrl,
|
|
1289
|
+
to: nextUrl,
|
|
1290
|
+
title: document.title.slice(0, NAME_MAX_CHARS),
|
|
1291
|
+
timestamp: Date.now()
|
|
1292
|
+
});
|
|
1293
|
+
previousUrl = nextUrl;
|
|
1294
|
+
};
|
|
1295
|
+
history.pushState = function patchedPushState(...args) {
|
|
1296
|
+
originalPushState.apply(this, args);
|
|
1297
|
+
queueMicrotask(emitNavigationIfChanged);
|
|
1298
|
+
};
|
|
1299
|
+
history.replaceState = function patchedReplaceState(...args) {
|
|
1300
|
+
originalReplaceState.apply(this, args);
|
|
1301
|
+
queueMicrotask(emitNavigationIfChanged);
|
|
1302
|
+
};
|
|
1303
|
+
const onPopState = () => emitNavigationIfChanged();
|
|
1304
|
+
const onHashChange = () => emitNavigationIfChanged();
|
|
1305
|
+
window.addEventListener("popstate", onPopState);
|
|
1306
|
+
window.addEventListener("hashchange", onHashChange);
|
|
1307
|
+
let pendingAddedRefs = new Set;
|
|
1308
|
+
let pendingRemovedRefs = new Set;
|
|
1309
|
+
let pendingMutationCount = 0;
|
|
1310
|
+
let mutationFlushTimer = null;
|
|
1311
|
+
const flushPendingMutations = () => {
|
|
1312
|
+
mutationFlushTimer = null;
|
|
1313
|
+
if (pendingMutationCount === 0)
|
|
1314
|
+
return;
|
|
1315
|
+
publishDomEvent({
|
|
1316
|
+
type: "mutation",
|
|
1317
|
+
summary: `${pendingMutationCount} subtree changes`,
|
|
1318
|
+
addedRefs: Array.from(pendingAddedRefs),
|
|
1319
|
+
removedRefs: Array.from(pendingRemovedRefs),
|
|
1320
|
+
timestamp: Date.now()
|
|
1321
|
+
});
|
|
1322
|
+
pendingAddedRefs = new Set;
|
|
1323
|
+
pendingRemovedRefs = new Set;
|
|
1324
|
+
pendingMutationCount = 0;
|
|
1325
|
+
};
|
|
1326
|
+
const collectDescendantRefs = (node, targetSet) => {
|
|
1327
|
+
if (!(node instanceof Element))
|
|
1328
|
+
return;
|
|
1329
|
+
if (node.id === WIDGET_ROOT_ID)
|
|
1330
|
+
return;
|
|
1331
|
+
const ref = node.getAttribute(REF_ATTR);
|
|
1332
|
+
if (ref)
|
|
1333
|
+
targetSet.add(ref);
|
|
1334
|
+
for (const child of Array.from(node.children))
|
|
1335
|
+
collectDescendantRefs(child, targetSet);
|
|
1336
|
+
};
|
|
1337
|
+
const observer = new MutationObserver((mutations) => {
|
|
1338
|
+
let observedCount = 0;
|
|
1339
|
+
for (const mutation of mutations) {
|
|
1340
|
+
if (mutation.type !== "childList")
|
|
1341
|
+
continue;
|
|
1342
|
+
if (mutation.target instanceof Element && isInsideWidget(mutation.target))
|
|
1343
|
+
continue;
|
|
1344
|
+
if (mutation.addedNodes.length === 0 && mutation.removedNodes.length === 0)
|
|
1345
|
+
continue;
|
|
1346
|
+
for (const added of Array.from(mutation.addedNodes)) {
|
|
1347
|
+
collectDescendantRefs(added, pendingAddedRefs);
|
|
1348
|
+
}
|
|
1349
|
+
for (const removed of Array.from(mutation.removedNodes)) {
|
|
1350
|
+
collectDescendantRefs(removed, pendingRemovedRefs);
|
|
1351
|
+
}
|
|
1352
|
+
observedCount += 1;
|
|
1353
|
+
}
|
|
1354
|
+
if (observedCount === 0)
|
|
1355
|
+
return;
|
|
1356
|
+
pendingMutationCount += observedCount;
|
|
1357
|
+
if (mutationFlushTimer)
|
|
1358
|
+
clearTimeout(mutationFlushTimer);
|
|
1359
|
+
mutationFlushTimer = setTimeout(flushPendingMutations, MUTATION_DEBOUNCE_MS);
|
|
1360
|
+
});
|
|
1361
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
1362
|
+
return () => {
|
|
1363
|
+
document.removeEventListener("click", onClick, true);
|
|
1364
|
+
document.removeEventListener("submit", onSubmit, true);
|
|
1365
|
+
window.removeEventListener("popstate", onPopState);
|
|
1366
|
+
window.removeEventListener("hashchange", onHashChange);
|
|
1367
|
+
history.pushState = originalPushState;
|
|
1368
|
+
history.replaceState = originalReplaceState;
|
|
1369
|
+
if (mutationFlushTimer)
|
|
1370
|
+
clearTimeout(mutationFlushTimer);
|
|
1371
|
+
observer.disconnect();
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// src/capture/snapdom.ts
|
|
1376
|
+
var cachedSnapdomModule = null;
|
|
1377
|
+
function loadSnapdom() {
|
|
1378
|
+
if (!cachedSnapdomModule)
|
|
1379
|
+
cachedSnapdomModule = import("@zumer/snapdom");
|
|
1380
|
+
return cachedSnapdomModule;
|
|
1381
|
+
}
|
|
1382
|
+
async function snapToCanvas(element, options = {}) {
|
|
1383
|
+
const snapdomModule = await loadSnapdom();
|
|
1384
|
+
return snapdomModule.snapdom.toCanvas(element, options);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
// src/components/DomCapture.tsx
|
|
1388
|
+
var SNAPSHOT_INTERVAL_MS = 2000;
|
|
1389
|
+
var A11Y_PUBLISH_INTERVAL_MS = 2000;
|
|
1390
|
+
var CAPTURE_PRESET = ScreenSharePresets2.h1080fps30;
|
|
1391
|
+
var CAPTURE_FPS = CAPTURE_PRESET.encoding.maxFramerate ?? 30;
|
|
1392
|
+
var CAPTURE_BITRATE = 4000000;
|
|
1393
|
+
var DOM_SNAPSHOT_GZIP_THRESHOLD_BYTES = 14000;
|
|
1394
|
+
var MAX_CANVAS_DPR = 1.5;
|
|
1395
|
+
var CONSECUTIVE_FAILURES_BEFORE_REPORT = 3;
|
|
1396
|
+
var textEncoder2 = new TextEncoder;
|
|
1397
|
+
function shouldIncludeInSnapshot(element) {
|
|
1398
|
+
if (!(element instanceof Element))
|
|
1399
|
+
return true;
|
|
1400
|
+
if (element.id === WIDGET_ROOT_ID)
|
|
1401
|
+
return false;
|
|
1402
|
+
if (element.hasAttribute?.(PRIVATE_ATTR))
|
|
1403
|
+
return false;
|
|
1404
|
+
return true;
|
|
1405
|
+
}
|
|
1406
|
+
function createPublishingCanvas() {
|
|
1407
|
+
const dprBoost = Math.min(window.devicePixelRatio || 1, MAX_CANVAS_DPR);
|
|
1408
|
+
const canvas = document.createElement("canvas");
|
|
1409
|
+
canvas.width = Math.round(CAPTURE_PRESET.width * dprBoost);
|
|
1410
|
+
canvas.height = Math.round(CAPTURE_PRESET.height * dprBoost);
|
|
1411
|
+
const ctx = canvas.getContext("2d");
|
|
1412
|
+
if (!ctx)
|
|
1413
|
+
return null;
|
|
1414
|
+
ctx.fillStyle = "#ffffff";
|
|
1415
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
1416
|
+
return { canvas, ctx };
|
|
1417
|
+
}
|
|
1418
|
+
function getCanvasCaptureStream(canvas, fps) {
|
|
1419
|
+
const captureStreamFn = canvas.captureStream;
|
|
1420
|
+
if (typeof captureStreamFn !== "function") {
|
|
1421
|
+
console.error("canvas.captureStream is not supported in this browser");
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
return captureStreamFn.call(canvas, fps);
|
|
1425
|
+
}
|
|
1426
|
+
async function paintViewportSnapshot(canvas, ctx) {
|
|
1427
|
+
const dpr = window.devicePixelRatio || 1;
|
|
1428
|
+
const snapshotCanvas = await snapToCanvas(document.documentElement, {
|
|
1429
|
+
filter: shouldIncludeInSnapshot,
|
|
1430
|
+
filterMode: "remove",
|
|
1431
|
+
backgroundColor: "#ffffff",
|
|
1432
|
+
fast: true,
|
|
1433
|
+
dpr
|
|
1434
|
+
});
|
|
1435
|
+
const sourceX = window.scrollX * dpr;
|
|
1436
|
+
const sourceY = window.scrollY * dpr;
|
|
1437
|
+
const sourceWidth = window.innerWidth * dpr;
|
|
1438
|
+
const sourceHeight = window.innerHeight * dpr;
|
|
1439
|
+
const fitScale = Math.min(canvas.width / sourceWidth, canvas.height / sourceHeight);
|
|
1440
|
+
const destWidth = sourceWidth * fitScale;
|
|
1441
|
+
const destHeight = sourceHeight * fitScale;
|
|
1442
|
+
const destX = (canvas.width - destWidth) / 2;
|
|
1443
|
+
const destY = (canvas.height - destHeight) / 2;
|
|
1444
|
+
ctx.fillStyle = "#ffffff";
|
|
1445
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
1446
|
+
ctx.drawImage(snapshotCanvas, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
|
|
1447
|
+
}
|
|
1448
|
+
async function gzipIfBeneficial(snapshotBytes) {
|
|
1449
|
+
if (typeof CompressionStream === "undefined" || snapshotBytes.byteLength <= DOM_SNAPSHOT_GZIP_THRESHOLD_BYTES) {
|
|
1450
|
+
return { bytes: snapshotBytes, compressionFlag: 0 };
|
|
1451
|
+
}
|
|
1452
|
+
try {
|
|
1453
|
+
const blob = new Blob([new Uint8Array(snapshotBytes)]);
|
|
1454
|
+
const stream = blob.stream().pipeThrough(new CompressionStream("gzip"));
|
|
1455
|
+
const compressedBytes = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
1456
|
+
return { bytes: compressedBytes, compressionFlag: 1 };
|
|
1457
|
+
} catch {
|
|
1458
|
+
return { bytes: snapshotBytes, compressionFlag: 0 };
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
async function buildDomSnapshotFrame() {
|
|
1462
|
+
const a11yTree = buildAccessibilityTree();
|
|
1463
|
+
const snapshotJson = JSON.stringify({
|
|
1464
|
+
pageContent: a11yTree.pageContent,
|
|
1465
|
+
viewport: a11yTree.viewport,
|
|
1466
|
+
truncated: a11yTree.truncated,
|
|
1467
|
+
url: window.location.href,
|
|
1468
|
+
title: document.title,
|
|
1469
|
+
capturedAt: new Date().toISOString()
|
|
1470
|
+
});
|
|
1471
|
+
const snapshotBytes = textEncoder2.encode(snapshotJson);
|
|
1472
|
+
const { bytes, compressionFlag } = await gzipIfBeneficial(snapshotBytes);
|
|
1473
|
+
const frame = new Uint8Array(bytes.byteLength + 1);
|
|
1474
|
+
frame[0] = compressionFlag;
|
|
1475
|
+
frame.set(bytes, 1);
|
|
1476
|
+
return frame;
|
|
1477
|
+
}
|
|
1478
|
+
function reportCaptureError(localParticipant) {
|
|
1479
|
+
try {
|
|
1480
|
+
localParticipant.publishData(textEncoder2.encode(JSON.stringify({ type: "capture_error" })), {
|
|
1481
|
+
reliable: true,
|
|
1482
|
+
topic: DOM_SNAPSHOT_TOPIC
|
|
1483
|
+
}).catch(() => {
|
|
1484
|
+
return;
|
|
1485
|
+
});
|
|
1486
|
+
} catch {}
|
|
1487
|
+
}
|
|
1488
|
+
async function unpublishAndStopTrack(localParticipant, videoTrack) {
|
|
1489
|
+
try {
|
|
1490
|
+
await localParticipant.unpublishTrack(videoTrack);
|
|
1491
|
+
} catch {}
|
|
1492
|
+
videoTrack.stop();
|
|
1493
|
+
}
|
|
1494
|
+
function DomCapture() {
|
|
1495
|
+
const { localParticipant } = useLocalParticipant3();
|
|
1496
|
+
const connectionState = useConnectionState2();
|
|
1497
|
+
const didStartRef = useRef2(false);
|
|
1498
|
+
useEffect4(() => {
|
|
1499
|
+
if (didStartRef.current)
|
|
1500
|
+
return;
|
|
1501
|
+
if (connectionState !== ConnectionState2.Connected)
|
|
1502
|
+
return;
|
|
1503
|
+
const canvasAndCtx = createPublishingCanvas();
|
|
1504
|
+
if (!canvasAndCtx)
|
|
1505
|
+
return;
|
|
1506
|
+
const { canvas, ctx } = canvasAndCtx;
|
|
1507
|
+
const canvasStream = getCanvasCaptureStream(canvas, CAPTURE_FPS);
|
|
1508
|
+
if (!canvasStream)
|
|
1509
|
+
return;
|
|
1510
|
+
const videoTrack = canvasStream.getVideoTracks()[0] ?? null;
|
|
1511
|
+
if (!videoTrack)
|
|
1512
|
+
return;
|
|
1513
|
+
videoTrack.contentHint = "text";
|
|
1514
|
+
didStartRef.current = true;
|
|
1515
|
+
let cancelled = false;
|
|
1516
|
+
let snapshotInFlight = false;
|
|
1517
|
+
let a11yPublishInFlight = false;
|
|
1518
|
+
let consecutiveCaptureFailures = 0;
|
|
1519
|
+
localParticipant.setMicrophoneEnabled(true).catch((error) => console.error("Failed to enable microphone:", error));
|
|
1520
|
+
localParticipant.publishTrack(videoTrack, {
|
|
1521
|
+
source: Track2.Source.ScreenShare,
|
|
1522
|
+
videoEncoding: {
|
|
1523
|
+
maxBitrate: CAPTURE_BITRATE,
|
|
1524
|
+
maxFramerate: CAPTURE_FPS
|
|
1525
|
+
},
|
|
1526
|
+
simulcast: false
|
|
1527
|
+
}).catch((error) => {
|
|
1528
|
+
console.error("Failed to publish DOM capture track:", error);
|
|
1529
|
+
for (const track of canvasStream.getTracks())
|
|
1530
|
+
track.stop();
|
|
1531
|
+
});
|
|
1532
|
+
const tickSnapshot = async () => {
|
|
1533
|
+
if (cancelled || snapshotInFlight)
|
|
1534
|
+
return;
|
|
1535
|
+
snapshotInFlight = true;
|
|
1536
|
+
try {
|
|
1537
|
+
await paintViewportSnapshot(canvas, ctx);
|
|
1538
|
+
if (cancelled)
|
|
1539
|
+
return;
|
|
1540
|
+
consecutiveCaptureFailures = 0;
|
|
1541
|
+
} catch {
|
|
1542
|
+
if (cancelled)
|
|
1543
|
+
return;
|
|
1544
|
+
consecutiveCaptureFailures += 1;
|
|
1545
|
+
if (consecutiveCaptureFailures === CONSECUTIVE_FAILURES_BEFORE_REPORT) {
|
|
1546
|
+
reportCaptureError(localParticipant);
|
|
1547
|
+
}
|
|
1548
|
+
} finally {
|
|
1549
|
+
snapshotInFlight = false;
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1552
|
+
const tickA11yPublish = async () => {
|
|
1553
|
+
if (cancelled || a11yPublishInFlight)
|
|
1554
|
+
return;
|
|
1555
|
+
a11yPublishInFlight = true;
|
|
1556
|
+
try {
|
|
1557
|
+
const frame = await buildDomSnapshotFrame();
|
|
1558
|
+
if (cancelled)
|
|
1559
|
+
return;
|
|
1560
|
+
await localParticipant.publishData(frame, {
|
|
1561
|
+
reliable: true,
|
|
1562
|
+
topic: DOM_SNAPSHOT_TOPIC
|
|
1563
|
+
});
|
|
1564
|
+
} catch {} finally {
|
|
1565
|
+
a11yPublishInFlight = false;
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
const cleanupDomEventListeners = installDomEventListeners(localParticipant, {
|
|
1569
|
+
onTriggerEvent: () => {
|
|
1570
|
+
tickA11yPublish();
|
|
1571
|
+
tickSnapshot();
|
|
1572
|
+
}
|
|
1573
|
+
});
|
|
1574
|
+
tickSnapshot();
|
|
1575
|
+
tickA11yPublish();
|
|
1576
|
+
const snapshotTimer = setInterval(tickSnapshot, SNAPSHOT_INTERVAL_MS);
|
|
1577
|
+
const a11yPublishTimer = setInterval(tickA11yPublish, A11Y_PUBLISH_INTERVAL_MS);
|
|
1578
|
+
return () => {
|
|
1579
|
+
cancelled = true;
|
|
1580
|
+
didStartRef.current = false;
|
|
1581
|
+
clearInterval(snapshotTimer);
|
|
1582
|
+
clearInterval(a11yPublishTimer);
|
|
1583
|
+
cleanupDomEventListeners();
|
|
1584
|
+
unpublishAndStopTrack(localParticipant, videoTrack);
|
|
1585
|
+
for (const track of canvasStream.getTracks())
|
|
1586
|
+
track.stop();
|
|
1587
|
+
};
|
|
1588
|
+
}, [connectionState, localParticipant]);
|
|
1589
|
+
return null;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
// src/components/HighlightOverlay.tsx
|
|
1593
|
+
import { useDataChannel } from "@livekit/components-react/hooks";
|
|
1594
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef3, useState as useState3 } from "react";
|
|
1595
|
+
import { jsx as jsx2, jsxs as jsxs2, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
1596
|
+
var TOOLTIP_MAX_WIDTH = 280;
|
|
1597
|
+
var TOOLTIP_GAP = 8;
|
|
1598
|
+
var VIEWPORT_MARGIN = 8;
|
|
1599
|
+
var TOOLTIP_TOP_THRESHOLD = 48;
|
|
1600
|
+
var Z_INDEX = 2147483646;
|
|
1601
|
+
var RING_INNER = "oklab(0.585 0.0288678 -0.231205 / 0.95)";
|
|
1602
|
+
var RING_OUTER = "oklab(0.585 0.0288678 -0.231205 / 0.25)";
|
|
1603
|
+
var textDecoder = new TextDecoder;
|
|
1604
|
+
function parseHighlightMessage(payload) {
|
|
1605
|
+
try {
|
|
1606
|
+
const json = JSON.parse(textDecoder.decode(payload));
|
|
1607
|
+
if (!json || typeof json !== "object")
|
|
1608
|
+
return null;
|
|
1609
|
+
if (json.type === "show" && typeof json.ref === "string") {
|
|
1610
|
+
return {
|
|
1611
|
+
type: "show",
|
|
1612
|
+
ref: json.ref,
|
|
1613
|
+
message: typeof json.message === "string" ? json.message : ""
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
if (json.type === "clear")
|
|
1617
|
+
return { type: "clear" };
|
|
1618
|
+
return null;
|
|
1619
|
+
} catch {
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
function safeGetIframeDocument(iframe) {
|
|
1624
|
+
try {
|
|
1625
|
+
return iframe.contentDocument;
|
|
1626
|
+
} catch {
|
|
1627
|
+
return null;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
function safeQuerySelector(doc, selector) {
|
|
1631
|
+
try {
|
|
1632
|
+
return doc.querySelector(selector);
|
|
1633
|
+
} catch {
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
function safeQueryAllIframes(doc) {
|
|
1638
|
+
try {
|
|
1639
|
+
return Array.from(doc.querySelectorAll("iframe"));
|
|
1640
|
+
} catch {
|
|
1641
|
+
return [];
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function findElementInDocumentTree(rootDoc, selector) {
|
|
1645
|
+
const hit = safeQuerySelector(rootDoc, selector);
|
|
1646
|
+
if (hit)
|
|
1647
|
+
return hit;
|
|
1648
|
+
for (const iframe of safeQueryAllIframes(rootDoc)) {
|
|
1649
|
+
const innerDoc = safeGetIframeDocument(iframe);
|
|
1650
|
+
if (!innerDoc)
|
|
1651
|
+
continue;
|
|
1652
|
+
const nested = findElementInDocumentTree(innerDoc, selector);
|
|
1653
|
+
if (nested)
|
|
1654
|
+
return nested;
|
|
1655
|
+
}
|
|
1656
|
+
return null;
|
|
1657
|
+
}
|
|
1658
|
+
function findElementByRef(ref) {
|
|
1659
|
+
const selector = `[${REF_ATTR}="${CSS.escape(ref)}"]`;
|
|
1660
|
+
return findElementInDocumentTree(document, selector);
|
|
1661
|
+
}
|
|
1662
|
+
function findIframeHostingDocument(targetDoc) {
|
|
1663
|
+
const queue = [document];
|
|
1664
|
+
while (queue.length > 0) {
|
|
1665
|
+
const doc = queue.shift();
|
|
1666
|
+
if (!doc)
|
|
1667
|
+
continue;
|
|
1668
|
+
for (const iframe of safeQueryAllIframes(doc)) {
|
|
1669
|
+
if (safeGetIframeDocument(iframe) === targetDoc)
|
|
1670
|
+
return iframe;
|
|
1671
|
+
const innerDoc = safeGetIframeDocument(iframe);
|
|
1672
|
+
if (innerDoc)
|
|
1673
|
+
queue.push(innerDoc);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return null;
|
|
1677
|
+
}
|
|
1678
|
+
function getRectInTopViewport(el) {
|
|
1679
|
+
let rect = el.getBoundingClientRect();
|
|
1680
|
+
let ownerDoc = el.ownerDocument;
|
|
1681
|
+
while (ownerDoc && ownerDoc !== document) {
|
|
1682
|
+
const hostingIframe = findIframeHostingDocument(ownerDoc);
|
|
1683
|
+
if (!hostingIframe)
|
|
1684
|
+
return rect;
|
|
1685
|
+
const iframeRect = hostingIframe.getBoundingClientRect();
|
|
1686
|
+
rect = new DOMRect(rect.left + iframeRect.left, rect.top + iframeRect.top, rect.width, rect.height);
|
|
1687
|
+
ownerDoc = hostingIframe.ownerDocument;
|
|
1688
|
+
}
|
|
1689
|
+
return rect;
|
|
1690
|
+
}
|
|
1691
|
+
function findScrollableAncestor(el) {
|
|
1692
|
+
const win = el.ownerDocument?.defaultView ?? window;
|
|
1693
|
+
let node = el.parentElement;
|
|
1694
|
+
while (node) {
|
|
1695
|
+
const overflow = win.getComputedStyle(node).overflow;
|
|
1696
|
+
if (/auto|scroll|overlay/.test(overflow))
|
|
1697
|
+
return node;
|
|
1698
|
+
node = node.parentElement;
|
|
1699
|
+
}
|
|
1700
|
+
return el.ownerDocument?.documentElement ?? document.documentElement;
|
|
1701
|
+
}
|
|
1702
|
+
function HighlightOverlay() {
|
|
1703
|
+
const [overlayState, setOverlayState] = useState3(null);
|
|
1704
|
+
const targetElementRef = useRef3(null);
|
|
1705
|
+
const pendingFrameRef = useRef3(null);
|
|
1706
|
+
const clearOverlay = useCallback4(() => {
|
|
1707
|
+
targetElementRef.current = null;
|
|
1708
|
+
setOverlayState(null);
|
|
1709
|
+
}, []);
|
|
1710
|
+
const recomputeRect = useCallback4(() => {
|
|
1711
|
+
pendingFrameRef.current = null;
|
|
1712
|
+
const target = targetElementRef.current;
|
|
1713
|
+
if (!target)
|
|
1714
|
+
return;
|
|
1715
|
+
if (!target.isConnected) {
|
|
1716
|
+
clearOverlay();
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
const rect2 = getRectInTopViewport(target);
|
|
1720
|
+
setOverlayState((prev) => prev ? { ...prev, rect: rect2 } : prev);
|
|
1721
|
+
}, [clearOverlay]);
|
|
1722
|
+
const scheduleRecompute = useCallback4(() => {
|
|
1723
|
+
if (pendingFrameRef.current !== null)
|
|
1724
|
+
return;
|
|
1725
|
+
pendingFrameRef.current = window.requestAnimationFrame(recomputeRect);
|
|
1726
|
+
}, [recomputeRect]);
|
|
1727
|
+
const onHighlightMessage = useCallback4((msg) => {
|
|
1728
|
+
const parsed = parseHighlightMessage(msg.payload);
|
|
1729
|
+
if (!parsed)
|
|
1730
|
+
return;
|
|
1731
|
+
if (parsed.type === "clear") {
|
|
1732
|
+
clearOverlay();
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
const target = findElementByRef(parsed.ref);
|
|
1736
|
+
if (!target) {
|
|
1737
|
+
clearOverlay();
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
targetElementRef.current = target;
|
|
1741
|
+
setOverlayState({
|
|
1742
|
+
ref: parsed.ref,
|
|
1743
|
+
message: parsed.message ?? "",
|
|
1744
|
+
rect: getRectInTopViewport(target)
|
|
1745
|
+
});
|
|
1746
|
+
}, [clearOverlay]);
|
|
1747
|
+
useDataChannel(HIGHLIGHT_TOPIC, onHighlightMessage);
|
|
1748
|
+
useEffect5(() => {
|
|
1749
|
+
if (!overlayState)
|
|
1750
|
+
return;
|
|
1751
|
+
const target = targetElementRef.current;
|
|
1752
|
+
if (!target)
|
|
1753
|
+
return;
|
|
1754
|
+
const onScroll = () => scheduleRecompute();
|
|
1755
|
+
const onResize = () => scheduleRecompute();
|
|
1756
|
+
const watchedWindows = new Set;
|
|
1757
|
+
watchedWindows.add(window);
|
|
1758
|
+
const ownerWindow = target.ownerDocument?.defaultView;
|
|
1759
|
+
if (ownerWindow && ownerWindow !== window)
|
|
1760
|
+
watchedWindows.add(ownerWindow);
|
|
1761
|
+
for (const win of watchedWindows) {
|
|
1762
|
+
win.addEventListener("scroll", onScroll, { capture: true, passive: true });
|
|
1763
|
+
win.addEventListener("resize", onResize, { passive: true });
|
|
1764
|
+
}
|
|
1765
|
+
const resizeObserver = new ResizeObserver(scheduleRecompute);
|
|
1766
|
+
resizeObserver.observe(target);
|
|
1767
|
+
const scrollContainer = findScrollableAncestor(target);
|
|
1768
|
+
const mutationObserver = new MutationObserver(() => {
|
|
1769
|
+
if (!targetElementRef.current?.isConnected) {
|
|
1770
|
+
clearOverlay();
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
scheduleRecompute();
|
|
1774
|
+
});
|
|
1775
|
+
mutationObserver.observe(scrollContainer, { childList: true, subtree: true });
|
|
1776
|
+
return () => {
|
|
1777
|
+
for (const win of watchedWindows) {
|
|
1778
|
+
win.removeEventListener("scroll", onScroll, { capture: true });
|
|
1779
|
+
win.removeEventListener("resize", onResize);
|
|
1780
|
+
}
|
|
1781
|
+
resizeObserver.disconnect();
|
|
1782
|
+
mutationObserver.disconnect();
|
|
1783
|
+
if (pendingFrameRef.current !== null) {
|
|
1784
|
+
window.cancelAnimationFrame(pendingFrameRef.current);
|
|
1785
|
+
pendingFrameRef.current = null;
|
|
1786
|
+
}
|
|
1787
|
+
};
|
|
1788
|
+
}, [overlayState, scheduleRecompute, clearOverlay]);
|
|
1789
|
+
if (!overlayState)
|
|
1790
|
+
return null;
|
|
1791
|
+
const { rect, message } = overlayState;
|
|
1792
|
+
const ringStyle = {
|
|
1793
|
+
position: "fixed",
|
|
1794
|
+
left: `${rect.left}px`,
|
|
1795
|
+
top: `${rect.top}px`,
|
|
1796
|
+
width: `${rect.width}px`,
|
|
1797
|
+
height: `${rect.height}px`,
|
|
1798
|
+
pointerEvents: "none",
|
|
1799
|
+
zIndex: Z_INDEX,
|
|
1800
|
+
boxShadow: `0 0 0 2px ${RING_INNER}, 0 0 0 6px ${RING_OUTER}`,
|
|
1801
|
+
borderRadius: "4px",
|
|
1802
|
+
transition: "none"
|
|
1803
|
+
};
|
|
1804
|
+
const showTooltipBelow = rect.top < TOOLTIP_TOP_THRESHOLD;
|
|
1805
|
+
const tooltipTop = showTooltipBelow ? rect.bottom + TOOLTIP_GAP : rect.top - TOOLTIP_GAP;
|
|
1806
|
+
const tooltipTransform = showTooltipBelow ? "translateY(0)" : "translateY(-100%)";
|
|
1807
|
+
const tooltipLeft = Math.min(Math.max(rect.left, VIEWPORT_MARGIN), window.innerWidth - TOOLTIP_MAX_WIDTH - VIEWPORT_MARGIN);
|
|
1808
|
+
const tooltipStyle = {
|
|
1809
|
+
position: "fixed",
|
|
1810
|
+
left: `${tooltipLeft}px`,
|
|
1811
|
+
top: `${tooltipTop}px`,
|
|
1812
|
+
transform: tooltipTransform,
|
|
1813
|
+
maxWidth: `${TOOLTIP_MAX_WIDTH}px`,
|
|
1814
|
+
pointerEvents: "none",
|
|
1815
|
+
zIndex: Z_INDEX,
|
|
1816
|
+
background: "rgba(45, 43, 61, 0.96)",
|
|
1817
|
+
color: "#ffffff",
|
|
1818
|
+
padding: "6px 10px",
|
|
1819
|
+
borderRadius: "8px",
|
|
1820
|
+
fontSize: "13px",
|
|
1821
|
+
lineHeight: "1.35",
|
|
1822
|
+
fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Inter, sans-serif',
|
|
1823
|
+
boxShadow: "0 6px 20px rgba(0, 0, 0, 0.25)",
|
|
1824
|
+
whiteSpace: "normal",
|
|
1825
|
+
wordWrap: "break-word"
|
|
1826
|
+
};
|
|
1827
|
+
return /* @__PURE__ */ jsxs2(Fragment2, {
|
|
1828
|
+
children: [
|
|
1829
|
+
/* @__PURE__ */ jsx2("div", {
|
|
1830
|
+
"data-skippr-private": "true",
|
|
1831
|
+
style: ringStyle,
|
|
1832
|
+
"aria-hidden": "true"
|
|
1833
|
+
}),
|
|
1834
|
+
message && /* @__PURE__ */ jsx2("div", {
|
|
1835
|
+
"data-skippr-private": "true",
|
|
1836
|
+
style: tooltipStyle,
|
|
1837
|
+
role: "tooltip",
|
|
1838
|
+
children: message
|
|
1839
|
+
})
|
|
1840
|
+
]
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
|
|
714
1844
|
// src/components/MinimizedBubble.tsx
|
|
715
|
-
import { useEffect as
|
|
1845
|
+
import { useEffect as useEffect6 } from "react";
|
|
716
1846
|
|
|
717
1847
|
// src/lib/utils.ts
|
|
718
1848
|
import { clsx } from "clsx";
|
|
@@ -723,12 +1853,12 @@ function cn(...inputs) {
|
|
|
723
1853
|
|
|
724
1854
|
// src/components/Logo.tsx
|
|
725
1855
|
import { useId } from "react";
|
|
726
|
-
import { jsx as
|
|
1856
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
727
1857
|
function Logo({ className }) {
|
|
728
1858
|
const reactId = useId().replace(/:/g, "");
|
|
729
1859
|
const clipId = `skippr-logo-clip-${reactId}`;
|
|
730
1860
|
const gradientId = `skippr-logo-gradient-${reactId}`;
|
|
731
|
-
return /* @__PURE__ */
|
|
1861
|
+
return /* @__PURE__ */ jsxs3("svg", {
|
|
732
1862
|
width: "1em",
|
|
733
1863
|
height: "1em",
|
|
734
1864
|
viewBox: "0 0 30 30",
|
|
@@ -738,14 +1868,14 @@ function Logo({ className }) {
|
|
|
738
1868
|
"aria-label": "Skippr",
|
|
739
1869
|
className,
|
|
740
1870
|
children: [
|
|
741
|
-
/* @__PURE__ */
|
|
1871
|
+
/* @__PURE__ */ jsxs3("g", {
|
|
742
1872
|
clipPath: `url(#${clipId})`,
|
|
743
1873
|
children: [
|
|
744
|
-
/* @__PURE__ */
|
|
1874
|
+
/* @__PURE__ */ jsx3("path", {
|
|
745
1875
|
d: "M0 10C0 4.47715 4.47715 0 10 0H20C25.5228 0 30 4.47715 30 10V20C30 25.5228 25.5228 30 20 30H10C4.47715 30 0 25.5228 0 20V10Z",
|
|
746
1876
|
fill: "#2D2D3F"
|
|
747
1877
|
}),
|
|
748
|
-
/* @__PURE__ */
|
|
1878
|
+
/* @__PURE__ */ jsx3("rect", {
|
|
749
1879
|
x: "7.83325",
|
|
750
1880
|
y: "14.9404",
|
|
751
1881
|
width: "12.4083",
|
|
@@ -754,11 +1884,11 @@ function Logo({ className }) {
|
|
|
754
1884
|
transform: "rotate(-45 7.83325 14.9404)",
|
|
755
1885
|
fill: "#52FFF9"
|
|
756
1886
|
}),
|
|
757
|
-
/* @__PURE__ */
|
|
1887
|
+
/* @__PURE__ */ jsx3("path", {
|
|
758
1888
|
d: "M18.8975 12.5928C20.2728 12.5928 21.3877 13.647 21.3877 14.9474C21.3877 16.2479 20.2728 17.3021 18.8975 17.3021L11.4269 17.3021C10.0516 17.3021 8.93665 16.2479 8.93665 14.9474C8.93665 13.647 10.0516 12.5928 11.4269 12.5928L18.8975 12.5928Z",
|
|
759
1889
|
fill: `url(#${gradientId})`
|
|
760
1890
|
}),
|
|
761
|
-
/* @__PURE__ */
|
|
1891
|
+
/* @__PURE__ */ jsx3("rect", {
|
|
762
1892
|
x: "10.1665",
|
|
763
1893
|
y: "20.4404",
|
|
764
1894
|
width: "12.4083",
|
|
@@ -769,9 +1899,9 @@ function Logo({ className }) {
|
|
|
769
1899
|
})
|
|
770
1900
|
]
|
|
771
1901
|
}),
|
|
772
|
-
/* @__PURE__ */
|
|
1902
|
+
/* @__PURE__ */ jsxs3("defs", {
|
|
773
1903
|
children: [
|
|
774
|
-
/* @__PURE__ */
|
|
1904
|
+
/* @__PURE__ */ jsxs3("linearGradient", {
|
|
775
1905
|
id: gradientId,
|
|
776
1906
|
x1: "18.9237",
|
|
777
1907
|
y1: "16.9997",
|
|
@@ -779,19 +1909,19 @@ function Logo({ className }) {
|
|
|
779
1909
|
y2: "14.1904",
|
|
780
1910
|
gradientUnits: "userSpaceOnUse",
|
|
781
1911
|
children: [
|
|
782
|
-
/* @__PURE__ */
|
|
1912
|
+
/* @__PURE__ */ jsx3("stop", {
|
|
783
1913
|
offset: "0.473958",
|
|
784
1914
|
stopColor: "white"
|
|
785
1915
|
}),
|
|
786
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsx3("stop", {
|
|
787
1917
|
offset: "1",
|
|
788
1918
|
stopColor: "#52FFF9"
|
|
789
1919
|
})
|
|
790
1920
|
]
|
|
791
1921
|
}),
|
|
792
|
-
/* @__PURE__ */
|
|
1922
|
+
/* @__PURE__ */ jsx3("clipPath", {
|
|
793
1923
|
id: clipId,
|
|
794
|
-
children: /* @__PURE__ */
|
|
1924
|
+
children: /* @__PURE__ */ jsx3("rect", {
|
|
795
1925
|
width: "30",
|
|
796
1926
|
height: "30",
|
|
797
1927
|
fill: "white"
|
|
@@ -804,18 +1934,18 @@ function Logo({ className }) {
|
|
|
804
1934
|
}
|
|
805
1935
|
|
|
806
1936
|
// src/components/ui/tooltip.tsx
|
|
807
|
-
import { jsx as
|
|
1937
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
808
1938
|
var ALIGN_CLASSES = {
|
|
809
1939
|
center: "skippr:left-1/2 skippr:-translate-x-1/2",
|
|
810
1940
|
start: "skippr:left-0",
|
|
811
1941
|
end: "skippr:right-0"
|
|
812
1942
|
};
|
|
813
1943
|
function Tooltip({ label, children, position = "top", align = "center" }) {
|
|
814
|
-
return /* @__PURE__ */
|
|
1944
|
+
return /* @__PURE__ */ jsxs4("span", {
|
|
815
1945
|
className: "skippr:relative skippr:inline-flex skippr:group",
|
|
816
1946
|
children: [
|
|
817
1947
|
children,
|
|
818
|
-
/* @__PURE__ */
|
|
1948
|
+
/* @__PURE__ */ jsx4("span", {
|
|
819
1949
|
className: cn("skippr:pointer-events-none skippr:absolute skippr:z-10", "skippr:whitespace-nowrap skippr:rounded-md skippr:bg-foreground skippr:px-2 skippr:py-1", "skippr:text-[11px] skippr:text-background skippr:font-medium", "skippr:opacity-0 skippr:scale-95 skippr:transition-all skippr:duration-150", "skippr:group-hover:opacity-100 skippr:group-hover:scale-100", "skippr:group-focus-within:opacity-100 skippr:group-focus-within:scale-100", ALIGN_CLASSES[align], position === "top" && "skippr:bottom-full skippr:mb-1.5", position === "bottom" && "skippr:top-full skippr:mt-1.5"),
|
|
820
1950
|
"aria-hidden": "true",
|
|
821
1951
|
children: label
|
|
@@ -825,66 +1955,67 @@ function Tooltip({ label, children, position = "top", align = "center" }) {
|
|
|
825
1955
|
}
|
|
826
1956
|
|
|
827
1957
|
// src/components/MinimizedBubble.tsx
|
|
828
|
-
import { jsx as
|
|
1958
|
+
import { jsx as jsx5, jsxs as jsxs5, Fragment as Fragment3 } from "react/jsx-runtime";
|
|
829
1959
|
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
1960
|
function ConnectedBubbleContent() {
|
|
831
|
-
const { expandPanel, disconnect, position } = useLiveAgent();
|
|
1961
|
+
const { expandPanel, disconnect, position, captureMode } = useLiveAgent();
|
|
832
1962
|
const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
|
|
833
1963
|
const tooltipAlign = position === "right" ? "end" : "start";
|
|
834
|
-
|
|
1964
|
+
const showScreenShareToggle = captureMode === "screenshare";
|
|
1965
|
+
return /* @__PURE__ */ jsxs5(Fragment3, {
|
|
835
1966
|
children: [
|
|
836
|
-
/* @__PURE__ */
|
|
1967
|
+
/* @__PURE__ */ jsx5(Tooltip, {
|
|
837
1968
|
label: isMuted ? "Unmute" : "Mute",
|
|
838
1969
|
align: tooltipAlign,
|
|
839
|
-
children: /* @__PURE__ */
|
|
1970
|
+
children: /* @__PURE__ */ jsx5("button", {
|
|
840
1971
|
type: "button",
|
|
841
1972
|
onClick: toggleMute,
|
|
842
1973
|
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
843
1974
|
className: cn(BUBBLE_BUTTON, isMuted ? "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90" : "skippr:bg-white skippr:text-foreground skippr:hover:bg-muted"),
|
|
844
|
-
children: isMuted ? /* @__PURE__ */
|
|
1975
|
+
children: isMuted ? /* @__PURE__ */ jsx5(MicOff, {
|
|
845
1976
|
className: "skippr:size-5"
|
|
846
|
-
}) : /* @__PURE__ */
|
|
1977
|
+
}) : /* @__PURE__ */ jsx5(Mic, {
|
|
847
1978
|
className: "skippr:size-5"
|
|
848
1979
|
})
|
|
849
1980
|
})
|
|
850
1981
|
}),
|
|
851
|
-
/* @__PURE__ */
|
|
1982
|
+
showScreenShareToggle && /* @__PURE__ */ jsx5(Tooltip, {
|
|
852
1983
|
label: isScreenSharing ? "Stop sharing" : "Share screen",
|
|
853
1984
|
align: tooltipAlign,
|
|
854
|
-
children: /* @__PURE__ */
|
|
1985
|
+
children: /* @__PURE__ */ jsx5("button", {
|
|
855
1986
|
type: "button",
|
|
856
1987
|
onClick: toggleScreenShare,
|
|
857
1988
|
"aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
|
|
858
1989
|
className: cn(BUBBLE_BUTTON, isScreenSharing ? "skippr:bg-bubble skippr:text-white skippr:hover:brightness-110" : "skippr:bg-white skippr:text-foreground skippr:hover:bg-muted"),
|
|
859
|
-
children: isScreenSharing ? /* @__PURE__ */
|
|
1990
|
+
children: isScreenSharing ? /* @__PURE__ */ jsx5(MonitorOff, {
|
|
860
1991
|
className: "skippr:size-5"
|
|
861
|
-
}) : /* @__PURE__ */
|
|
1992
|
+
}) : /* @__PURE__ */ jsx5(Monitor, {
|
|
862
1993
|
className: "skippr:size-5"
|
|
863
1994
|
})
|
|
864
1995
|
})
|
|
865
1996
|
}),
|
|
866
|
-
/* @__PURE__ */
|
|
1997
|
+
/* @__PURE__ */ jsx5(Tooltip, {
|
|
867
1998
|
label: "End session",
|
|
868
1999
|
align: tooltipAlign,
|
|
869
|
-
children: /* @__PURE__ */
|
|
2000
|
+
children: /* @__PURE__ */ jsx5("button", {
|
|
870
2001
|
type: "button",
|
|
871
2002
|
onClick: () => disconnect(),
|
|
872
2003
|
"aria-label": "End session",
|
|
873
2004
|
className: cn(BUBBLE_BUTTON, "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90"),
|
|
874
|
-
children: /* @__PURE__ */
|
|
2005
|
+
children: /* @__PURE__ */ jsx5(PhoneOff, {
|
|
875
2006
|
className: "skippr:size-5"
|
|
876
2007
|
})
|
|
877
2008
|
})
|
|
878
2009
|
}),
|
|
879
|
-
/* @__PURE__ */
|
|
2010
|
+
/* @__PURE__ */ jsx5(Tooltip, {
|
|
880
2011
|
label: "Open chat & transcript",
|
|
881
2012
|
align: tooltipAlign,
|
|
882
|
-
children: /* @__PURE__ */
|
|
2013
|
+
children: /* @__PURE__ */ jsx5("button", {
|
|
883
2014
|
type: "button",
|
|
884
2015
|
onClick: expandPanel,
|
|
885
2016
|
"aria-label": "Open chat & transcript",
|
|
886
2017
|
className: cn(BUBBLE_BUTTON, "skippr:bg-bubble skippr:hover:brightness-110"),
|
|
887
|
-
children: /* @__PURE__ */
|
|
2018
|
+
children: /* @__PURE__ */ jsx5(Logo, {
|
|
888
2019
|
className: "skippr:size-7"
|
|
889
2020
|
})
|
|
890
2021
|
})
|
|
@@ -895,19 +2026,19 @@ function ConnectedBubbleContent() {
|
|
|
895
2026
|
function IdleBubbleContent() {
|
|
896
2027
|
const { expandPanel, position } = useLiveAgent();
|
|
897
2028
|
const tooltipAlign = position === "right" ? "end" : "start";
|
|
898
|
-
return /* @__PURE__ */
|
|
2029
|
+
return /* @__PURE__ */ jsx5(Tooltip, {
|
|
899
2030
|
label: "Open Skippr assistant",
|
|
900
2031
|
align: tooltipAlign,
|
|
901
|
-
children: /* @__PURE__ */
|
|
2032
|
+
children: /* @__PURE__ */ jsxs5("button", {
|
|
902
2033
|
type: "button",
|
|
903
2034
|
onClick: expandPanel,
|
|
904
2035
|
"aria-label": "Skippr assistant",
|
|
905
2036
|
className: cn(BUBBLE_BUTTON, "skippr:relative skippr:bg-bubble skippr:hover:brightness-110"),
|
|
906
2037
|
children: [
|
|
907
|
-
/* @__PURE__ */
|
|
2038
|
+
/* @__PURE__ */ jsx5(Logo, {
|
|
908
2039
|
className: "skippr:relative skippr:z-10 skippr:size-7"
|
|
909
2040
|
}),
|
|
910
|
-
/* @__PURE__ */
|
|
2041
|
+
/* @__PURE__ */ jsx5("span", {
|
|
911
2042
|
className: "skippr:absolute skippr:-inset-[3px] skippr:animate-pulse skippr:rounded-[17px] skippr:border-2 skippr:border-bubble/50"
|
|
912
2043
|
})
|
|
913
2044
|
]
|
|
@@ -919,18 +2050,18 @@ function WelcomeBubble({
|
|
|
919
2050
|
position,
|
|
920
2051
|
onDismiss
|
|
921
2052
|
}) {
|
|
922
|
-
|
|
2053
|
+
useEffect6(() => {
|
|
923
2054
|
const timer = setTimeout(onDismiss, 5000);
|
|
924
2055
|
return () => clearTimeout(timer);
|
|
925
2056
|
}, [onDismiss]);
|
|
926
|
-
return /* @__PURE__ */
|
|
2057
|
+
return /* @__PURE__ */ jsxs5("button", {
|
|
927
2058
|
type: "button",
|
|
928
2059
|
className: cn("skippr:absolute skippr:bottom-full skippr:mb-3", "skippr:max-w-64 skippr:rounded-xl skippr:bg-card skippr:shadow-lg", "skippr:border skippr:border-border skippr:px-4 skippr:py-3", "skippr:text-sm skippr:text-foreground skippr:leading-relaxed skippr:text-left", "skippr:animate-[skippr-fade-in_0.3s_ease-out]", "skippr:cursor-pointer", position === "right" ? "skippr:right-0" : "skippr:left-0"),
|
|
929
2060
|
onClick: onDismiss,
|
|
930
2061
|
"aria-label": "Dismiss",
|
|
931
2062
|
children: [
|
|
932
2063
|
message,
|
|
933
|
-
/* @__PURE__ */
|
|
2064
|
+
/* @__PURE__ */ jsx5("span", {
|
|
934
2065
|
className: cn("skippr:absolute skippr:top-full skippr:size-2.5", "skippr:border-l skippr:border-t skippr:border-border skippr:bg-card", "skippr:rotate-[225deg]", position === "right" ? "skippr:right-5" : "skippr:left-5", "skippr:-mt-[5px]")
|
|
935
2066
|
})
|
|
936
2067
|
]
|
|
@@ -943,27 +2074,27 @@ function MinimizedBubble({
|
|
|
943
2074
|
}) {
|
|
944
2075
|
const { isConnected, isStarting, position } = useLiveAgent();
|
|
945
2076
|
const inSession = isConnected || isStarting;
|
|
946
|
-
return /* @__PURE__ */
|
|
2077
|
+
return /* @__PURE__ */ jsxs5("div", {
|
|
947
2078
|
className: cn("skippr:fixed skippr:bottom-6 skippr:z-[9999]", "skippr:flex skippr:items-center skippr:gap-2", position === "right" ? "skippr:right-6" : "skippr:left-6"),
|
|
948
2079
|
children: [
|
|
949
|
-
welcomeMessage && !inSession && !welcomeDismissed && /* @__PURE__ */
|
|
2080
|
+
welcomeMessage && !inSession && !welcomeDismissed && /* @__PURE__ */ jsx5(WelcomeBubble, {
|
|
950
2081
|
message: welcomeMessage,
|
|
951
2082
|
position,
|
|
952
2083
|
onDismiss: onDismissWelcome
|
|
953
2084
|
}),
|
|
954
|
-
inSession ? /* @__PURE__ */
|
|
2085
|
+
inSession ? /* @__PURE__ */ jsx5(ConnectedBubbleContent, {}) : /* @__PURE__ */ jsx5(IdleBubbleContent, {})
|
|
955
2086
|
]
|
|
956
2087
|
});
|
|
957
2088
|
}
|
|
958
2089
|
|
|
959
2090
|
// src/components/Sidebar.tsx
|
|
960
|
-
import { useEffect as
|
|
2091
|
+
import { useEffect as useEffect13 } from "react";
|
|
961
2092
|
|
|
962
2093
|
// src/hooks/useCombinedMessages.ts
|
|
963
2094
|
import { useMemo as useMemo4 } from "react";
|
|
964
2095
|
|
|
965
2096
|
// src/hooks/useChatMessages.ts
|
|
966
|
-
import { useChat, useLocalParticipant as
|
|
2097
|
+
import { useChat, useLocalParticipant as useLocalParticipant4 } from "@livekit/components-react/hooks";
|
|
967
2098
|
import { useMemo as useMemo2 } from "react";
|
|
968
2099
|
|
|
969
2100
|
// src/lib/filterSystemMessages.ts
|
|
@@ -975,7 +2106,7 @@ function filterSystemMessages(messages) {
|
|
|
975
2106
|
// src/hooks/useChatMessages.ts
|
|
976
2107
|
function useChatMessages() {
|
|
977
2108
|
const { chatMessages: rawMessages, send, isSending } = useChat();
|
|
978
|
-
const { localParticipant } =
|
|
2109
|
+
const { localParticipant } = useLocalParticipant4();
|
|
979
2110
|
const localIdentity = localParticipant.identity;
|
|
980
2111
|
const chatMessages = useMemo2(() => {
|
|
981
2112
|
const sortedMessages = rawMessages.map((msg) => ({
|
|
@@ -991,11 +2122,11 @@ function useChatMessages() {
|
|
|
991
2122
|
}
|
|
992
2123
|
|
|
993
2124
|
// src/hooks/useStreamingTranscript.ts
|
|
994
|
-
import { useLocalParticipant as
|
|
2125
|
+
import { useLocalParticipant as useLocalParticipant5, useTranscriptions } from "@livekit/components-react/hooks";
|
|
995
2126
|
import { useMemo as useMemo3 } from "react";
|
|
996
2127
|
function useStreamingTranscript() {
|
|
997
2128
|
const transcriptions = useTranscriptions();
|
|
998
|
-
const { localParticipant } =
|
|
2129
|
+
const { localParticipant } = useLocalParticipant5();
|
|
999
2130
|
const localIdentity = localParticipant.identity;
|
|
1000
2131
|
const transcriptMessages = useMemo3(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).map((stream) => ({
|
|
1001
2132
|
id: stream.streamInfo.id,
|
|
@@ -1039,15 +2170,15 @@ function useCombinedMessages() {
|
|
|
1039
2170
|
}
|
|
1040
2171
|
|
|
1041
2172
|
// src/hooks/usePhaseUpdates.ts
|
|
1042
|
-
import { useCallback as
|
|
2173
|
+
import { useCallback as useCallback5 } from "react";
|
|
1043
2174
|
|
|
1044
2175
|
// src/hooks/useAgentState.ts
|
|
1045
|
-
import { useRemoteParticipants } from "@livekit/components-react";
|
|
1046
|
-
import { useEffect as
|
|
2176
|
+
import { useRemoteParticipants } from "@livekit/components-react/hooks";
|
|
2177
|
+
import { useEffect as useEffect7, useState as useState4 } from "react";
|
|
1047
2178
|
function useAgentState(attributeKey, parse, initial) {
|
|
1048
|
-
const [value, setValue] =
|
|
2179
|
+
const [value, setValue] = useState4(initial);
|
|
1049
2180
|
const remoteParticipants = useRemoteParticipants();
|
|
1050
|
-
|
|
2181
|
+
useEffect7(() => {
|
|
1051
2182
|
const agentParticipant = remoteParticipants.find((p) => p.attributes?.[attributeKey]);
|
|
1052
2183
|
if (agentParticipant) {
|
|
1053
2184
|
const attr = agentParticipant.attributes?.[attributeKey];
|
|
@@ -1093,13 +2224,13 @@ function parsePhases(json) {
|
|
|
1093
2224
|
return null;
|
|
1094
2225
|
}
|
|
1095
2226
|
function usePhaseUpdates() {
|
|
1096
|
-
const parse =
|
|
2227
|
+
const parse = useCallback5(parsePhases, []);
|
|
1097
2228
|
const phases = useAgentState("phases", parse, []);
|
|
1098
2229
|
return { phases };
|
|
1099
2230
|
}
|
|
1100
2231
|
|
|
1101
2232
|
// src/hooks/useSessionRemaining.ts
|
|
1102
|
-
import { useEffect as
|
|
2233
|
+
import { useEffect as useEffect8, useRef as useRef4, useState as useState5 } from "react";
|
|
1103
2234
|
|
|
1104
2235
|
// src/lib/format.ts
|
|
1105
2236
|
function formatTime(seconds) {
|
|
@@ -1115,9 +2246,9 @@ function parseNumber(s) {
|
|
|
1115
2246
|
// src/hooks/useSessionRemaining.ts
|
|
1116
2247
|
function useSessionRemaining() {
|
|
1117
2248
|
const maxCallDuration = useAgentState("maxCallDuration", parseNumber, null);
|
|
1118
|
-
const endTimeRef =
|
|
1119
|
-
const [remaining, setRemaining] =
|
|
1120
|
-
|
|
2249
|
+
const endTimeRef = useRef4(null);
|
|
2250
|
+
const [remaining, setRemaining] = useState5(null);
|
|
2251
|
+
useEffect8(() => {
|
|
1121
2252
|
if (maxCallDuration === null || endTimeRef.current !== null)
|
|
1122
2253
|
return;
|
|
1123
2254
|
const endTime = Date.now() + maxCallDuration * 1000;
|
|
@@ -1133,14 +2264,11 @@ function useSessionRemaining() {
|
|
|
1133
2264
|
return remaining;
|
|
1134
2265
|
}
|
|
1135
2266
|
|
|
1136
|
-
// src/lib/constants.ts
|
|
1137
|
-
var SIDEBAR_WIDTH = 360;
|
|
1138
|
-
|
|
1139
2267
|
// src/hooks/useElapsedSeconds.ts
|
|
1140
|
-
import { useEffect as
|
|
2268
|
+
import { useEffect as useEffect9, useState as useState6 } from "react";
|
|
1141
2269
|
function useElapsedSeconds(isRunning) {
|
|
1142
|
-
const [elapsed, setElapsed] =
|
|
1143
|
-
|
|
2270
|
+
const [elapsed, setElapsed] = useState6(0);
|
|
2271
|
+
useEffect9(() => {
|
|
1144
2272
|
if (!isRunning) {
|
|
1145
2273
|
setElapsed(0);
|
|
1146
2274
|
return;
|
|
@@ -1156,50 +2284,50 @@ function useElapsedSeconds(isRunning) {
|
|
|
1156
2284
|
}
|
|
1157
2285
|
|
|
1158
2286
|
// src/components/ChatHeader.tsx
|
|
1159
|
-
import { jsx as
|
|
2287
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1160
2288
|
function ChatHeader() {
|
|
1161
2289
|
const { isConnected, minimizePanel, minimizable } = useLiveAgent();
|
|
1162
2290
|
const elapsed = useElapsedSeconds(isConnected);
|
|
1163
|
-
return /* @__PURE__ */
|
|
2291
|
+
return /* @__PURE__ */ jsxs6("header", {
|
|
1164
2292
|
className: "skippr:sticky skippr:top-0 skippr:z-10 skippr:flex skippr:shrink-0 skippr:items-center skippr:justify-between skippr:border-b skippr:border-border skippr:bg-primary skippr:px-4 skippr:py-3",
|
|
1165
2293
|
children: [
|
|
1166
|
-
/* @__PURE__ */
|
|
2294
|
+
/* @__PURE__ */ jsx6("p", {
|
|
1167
2295
|
className: "skippr:text-sm skippr:font-semibold skippr:text-primary-foreground",
|
|
1168
2296
|
children: "Skippr"
|
|
1169
2297
|
}),
|
|
1170
|
-
/* @__PURE__ */
|
|
2298
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
1171
2299
|
className: "skippr:flex skippr:items-center skippr:gap-2",
|
|
1172
2300
|
children: [
|
|
1173
|
-
isConnected && /* @__PURE__ */
|
|
2301
|
+
isConnected && /* @__PURE__ */ jsxs6("div", {
|
|
1174
2302
|
className: "skippr:flex skippr:items-center skippr:gap-1.5 skippr:rounded-full skippr:bg-primary-foreground/20 skippr:px-2.5 skippr:py-1",
|
|
1175
2303
|
children: [
|
|
1176
|
-
/* @__PURE__ */
|
|
2304
|
+
/* @__PURE__ */ jsxs6("span", {
|
|
1177
2305
|
className: "skippr:relative skippr:flex skippr:size-1.5",
|
|
1178
2306
|
children: [
|
|
1179
|
-
/* @__PURE__ */
|
|
2307
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1180
2308
|
className: "skippr:absolute skippr:inline-flex skippr:h-full skippr:w-full skippr:animate-ping skippr:rounded-full skippr:bg-red-400 skippr:opacity-75"
|
|
1181
2309
|
}),
|
|
1182
|
-
/* @__PURE__ */
|
|
2310
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1183
2311
|
className: "skippr:relative skippr:inline-flex skippr:size-1.5 skippr:rounded-full skippr:bg-red-400"
|
|
1184
2312
|
})
|
|
1185
2313
|
]
|
|
1186
2314
|
}),
|
|
1187
|
-
/* @__PURE__ */
|
|
2315
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1188
2316
|
className: "skippr:text-[10px] skippr:font-medium skippr:text-primary-foreground",
|
|
1189
2317
|
children: "REC"
|
|
1190
2318
|
}),
|
|
1191
|
-
/* @__PURE__ */
|
|
2319
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1192
2320
|
className: "skippr:text-[10px] skippr:font-mono skippr:text-primary-foreground",
|
|
1193
2321
|
children: formatTime(elapsed)
|
|
1194
2322
|
})
|
|
1195
2323
|
]
|
|
1196
2324
|
}),
|
|
1197
|
-
minimizable && /* @__PURE__ */
|
|
2325
|
+
minimizable && /* @__PURE__ */ jsx6("button", {
|
|
1198
2326
|
type: "button",
|
|
1199
2327
|
onClick: minimizePanel,
|
|
1200
2328
|
"aria-label": "Minimize",
|
|
1201
2329
|
className: "skippr:flex skippr:size-6 skippr:cursor-pointer skippr:items-center skippr:justify-center skippr:rounded-md skippr:text-primary-foreground/70 skippr:transition-colors skippr:hover:bg-primary-foreground/10 skippr:hover:text-primary-foreground",
|
|
1202
|
-
children: /* @__PURE__ */
|
|
2330
|
+
children: /* @__PURE__ */ jsx6(Minimize2, {
|
|
1203
2331
|
className: "skippr:size-3.5"
|
|
1204
2332
|
})
|
|
1205
2333
|
})
|
|
@@ -1210,26 +2338,26 @@ function ChatHeader() {
|
|
|
1210
2338
|
}
|
|
1211
2339
|
|
|
1212
2340
|
// src/components/LoadingDots.tsx
|
|
1213
|
-
import { jsx as
|
|
2341
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1214
2342
|
function LoadingDots({ label }) {
|
|
1215
|
-
return /* @__PURE__ */
|
|
2343
|
+
return /* @__PURE__ */ jsxs7("div", {
|
|
1216
2344
|
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:py-4",
|
|
1217
2345
|
children: [
|
|
1218
|
-
/* @__PURE__ */
|
|
2346
|
+
/* @__PURE__ */ jsxs7("div", {
|
|
1219
2347
|
className: "skippr:flex skippr:gap-1",
|
|
1220
2348
|
children: [
|
|
1221
|
-
/* @__PURE__ */
|
|
2349
|
+
/* @__PURE__ */ jsx7("span", {
|
|
1222
2350
|
className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:0ms]"
|
|
1223
2351
|
}),
|
|
1224
|
-
/* @__PURE__ */
|
|
2352
|
+
/* @__PURE__ */ jsx7("span", {
|
|
1225
2353
|
className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:150ms]"
|
|
1226
2354
|
}),
|
|
1227
|
-
/* @__PURE__ */
|
|
2355
|
+
/* @__PURE__ */ jsx7("span", {
|
|
1228
2356
|
className: "skippr:size-1.5 skippr:rounded-full skippr:bg-muted-foreground/40 skippr:animate-bounce skippr:[animation-delay:300ms]"
|
|
1229
2357
|
})
|
|
1230
2358
|
]
|
|
1231
2359
|
}),
|
|
1232
|
-
/* @__PURE__ */
|
|
2360
|
+
/* @__PURE__ */ jsx7("p", {
|
|
1233
2361
|
className: "skippr:text-xs skippr:text-muted-foreground",
|
|
1234
2362
|
children: label
|
|
1235
2363
|
})
|
|
@@ -1238,11 +2366,11 @@ function LoadingDots({ label }) {
|
|
|
1238
2366
|
}
|
|
1239
2367
|
|
|
1240
2368
|
// src/components/LoginFlow.tsx
|
|
1241
|
-
import { useCallback as
|
|
2369
|
+
import { useCallback as useCallback6, useEffect as useEffect10, useRef as useRef5, useState as useState7 } from "react";
|
|
1242
2370
|
|
|
1243
2371
|
// src/components/ui/button.tsx
|
|
1244
2372
|
import { forwardRef as forwardRef3 } from "react";
|
|
1245
|
-
import { jsx as
|
|
2373
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1246
2374
|
var variantClasses = {
|
|
1247
2375
|
default: "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90",
|
|
1248
2376
|
destructive: "skippr:bg-destructive skippr:text-white skippr:hover:bg-destructive/90",
|
|
@@ -1261,7 +2389,7 @@ var sizeClasses = {
|
|
|
1261
2389
|
"icon-lg": "skippr:size-10"
|
|
1262
2390
|
};
|
|
1263
2391
|
var Button = forwardRef3(({ className, variant = "default", size = "default", ...props }, ref) => {
|
|
1264
|
-
return /* @__PURE__ */
|
|
2392
|
+
return /* @__PURE__ */ jsx8("button", {
|
|
1265
2393
|
className: cn("skippr:inline-flex skippr:items-center skippr:justify-center skippr:gap-2 skippr:whitespace-nowrap skippr:rounded-md skippr:text-sm skippr:font-medium skippr:ring-offset-background skippr:transition-all skippr:cursor-pointer skippr:focus-visible:outline-none skippr:focus-visible:ring-2 skippr:focus-visible:ring-ring skippr:focus-visible:ring-offset-2 skippr:disabled:pointer-events-none skippr:disabled:opacity-50 skippr:shrink-0 skippr:[&_svg]:pointer-events-none skippr:[&_svg:not([class*='size-'])]:size-4 skippr:[&_svg]:shrink-0", variantClasses[variant], sizeClasses[size], className),
|
|
1266
2394
|
ref,
|
|
1267
2395
|
...props
|
|
@@ -1270,28 +2398,28 @@ var Button = forwardRef3(({ className, variant = "default", size = "default", ..
|
|
|
1270
2398
|
Button.displayName = "Button";
|
|
1271
2399
|
|
|
1272
2400
|
// src/components/LoginFlow.tsx
|
|
1273
|
-
import { jsx as
|
|
2401
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1274
2402
|
var OTP_LENGTH = 6;
|
|
1275
2403
|
var DIGIT_KEYS = ["d0", "d1", "d2", "d3", "d4", "d5"];
|
|
1276
2404
|
function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
|
|
1277
|
-
const [step, setStep] =
|
|
1278
|
-
const [email, setEmail] =
|
|
1279
|
-
const handleRequestOtp =
|
|
2405
|
+
const [step, setStep] = useState7("email");
|
|
2406
|
+
const [email, setEmail] = useState7("");
|
|
2407
|
+
const handleRequestOtp = useCallback6(async (emailValue) => {
|
|
1280
2408
|
const success = await requestOtp(emailValue);
|
|
1281
2409
|
if (success)
|
|
1282
2410
|
setStep("otp");
|
|
1283
2411
|
}, [requestOtp]);
|
|
1284
|
-
const handleVerifyOtp =
|
|
2412
|
+
const handleVerifyOtp = useCallback6(async (code) => {
|
|
1285
2413
|
await verifyOtp(email, code);
|
|
1286
2414
|
}, [verifyOtp, email]);
|
|
1287
|
-
const handleBack =
|
|
2415
|
+
const handleBack = useCallback6(() => {
|
|
1288
2416
|
setStep("email");
|
|
1289
2417
|
}, []);
|
|
1290
|
-
const handleResend =
|
|
2418
|
+
const handleResend = useCallback6(async () => {
|
|
1291
2419
|
await requestOtp(email);
|
|
1292
2420
|
}, [requestOtp, email]);
|
|
1293
2421
|
if (step === "otp") {
|
|
1294
|
-
return /* @__PURE__ */
|
|
2422
|
+
return /* @__PURE__ */ jsx9(OtpStep, {
|
|
1295
2423
|
email,
|
|
1296
2424
|
onSubmit: handleVerifyOtp,
|
|
1297
2425
|
onResend: handleResend,
|
|
@@ -1300,7 +2428,7 @@ function LoginFlow({ requestOtp, verifyOtp, error, isSubmitting }) {
|
|
|
1300
2428
|
isSubmitting
|
|
1301
2429
|
});
|
|
1302
2430
|
}
|
|
1303
|
-
return /* @__PURE__ */
|
|
2431
|
+
return /* @__PURE__ */ jsx9(EmailStep, {
|
|
1304
2432
|
email,
|
|
1305
2433
|
onEmailChange: setEmail,
|
|
1306
2434
|
onSubmit: handleRequestOtp,
|
|
@@ -1314,30 +2442,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
|
|
|
1314
2442
|
if (email.trim())
|
|
1315
2443
|
onSubmit(email.trim());
|
|
1316
2444
|
}
|
|
1317
|
-
return /* @__PURE__ */
|
|
2445
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
1318
2446
|
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
|
|
1319
2447
|
children: [
|
|
1320
|
-
/* @__PURE__ */
|
|
2448
|
+
/* @__PURE__ */ jsxs8("div", {
|
|
1321
2449
|
className: "skippr:mb-4 skippr:text-center",
|
|
1322
2450
|
children: [
|
|
1323
|
-
/* @__PURE__ */
|
|
2451
|
+
/* @__PURE__ */ jsx9(Mail, {
|
|
1324
2452
|
className: "skippr:mx-auto skippr:mb-2 skippr:size-6 skippr:text-primary"
|
|
1325
2453
|
}),
|
|
1326
|
-
/* @__PURE__ */
|
|
2454
|
+
/* @__PURE__ */ jsx9("p", {
|
|
1327
2455
|
className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
|
|
1328
2456
|
children: "Sign in to continue"
|
|
1329
2457
|
}),
|
|
1330
|
-
/* @__PURE__ */
|
|
2458
|
+
/* @__PURE__ */ jsx9("p", {
|
|
1331
2459
|
className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
|
|
1332
2460
|
children: "Your email will be used to identify you across sessions"
|
|
1333
2461
|
})
|
|
1334
2462
|
]
|
|
1335
2463
|
}),
|
|
1336
|
-
/* @__PURE__ */
|
|
2464
|
+
/* @__PURE__ */ jsxs8("form", {
|
|
1337
2465
|
onSubmit: handleSubmit,
|
|
1338
2466
|
className: "skippr:flex skippr:flex-col skippr:gap-3",
|
|
1339
2467
|
children: [
|
|
1340
|
-
/* @__PURE__ */
|
|
2468
|
+
/* @__PURE__ */ jsx9("input", {
|
|
1341
2469
|
type: "email",
|
|
1342
2470
|
placeholder: "you@example.com",
|
|
1343
2471
|
value: email,
|
|
@@ -1346,15 +2474,15 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
|
|
|
1346
2474
|
required: true,
|
|
1347
2475
|
className: "skippr:w-full skippr:rounded-md skippr:border skippr:border-border skippr:bg-background skippr:px-3 skippr:py-2 skippr:text-sm skippr:text-foreground skippr:placeholder-muted-foreground skippr:outline-none focus:skippr:ring-2 focus:skippr:ring-primary/30 focus:skippr:border-primary disabled:skippr:opacity-50"
|
|
1348
2476
|
}),
|
|
1349
|
-
/* @__PURE__ */
|
|
2477
|
+
/* @__PURE__ */ jsx9(Button, {
|
|
1350
2478
|
type: "submit",
|
|
1351
2479
|
disabled: isSubmitting || !email.trim(),
|
|
1352
2480
|
className: "skippr:w-full",
|
|
1353
|
-
children: isSubmitting ? /* @__PURE__ */
|
|
2481
|
+
children: isSubmitting ? /* @__PURE__ */ jsx9(LoaderCircle, {
|
|
1354
2482
|
className: "skippr:size-4 skippr:animate-spin"
|
|
1355
2483
|
}) : "Continue"
|
|
1356
2484
|
}),
|
|
1357
|
-
error && /* @__PURE__ */
|
|
2485
|
+
error && /* @__PURE__ */ jsx9("p", {
|
|
1358
2486
|
className: "skippr:text-xs skippr:text-center skippr:text-destructive",
|
|
1359
2487
|
children: error
|
|
1360
2488
|
})
|
|
@@ -1364,30 +2492,30 @@ function EmailStep({ email, onEmailChange, onSubmit, error, isSubmitting }) {
|
|
|
1364
2492
|
});
|
|
1365
2493
|
}
|
|
1366
2494
|
function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
1367
|
-
const [digits, setDigits] =
|
|
1368
|
-
const [resendCooldown, setResendCooldown] =
|
|
1369
|
-
const inputRefs =
|
|
1370
|
-
const submittedRef =
|
|
1371
|
-
|
|
2495
|
+
const [digits, setDigits] = useState7(Array(OTP_LENGTH).fill(""));
|
|
2496
|
+
const [resendCooldown, setResendCooldown] = useState7(0);
|
|
2497
|
+
const inputRefs = useRef5([]);
|
|
2498
|
+
const submittedRef = useRef5(false);
|
|
2499
|
+
useEffect10(() => {
|
|
1372
2500
|
inputRefs.current[0]?.focus();
|
|
1373
2501
|
}, []);
|
|
1374
|
-
|
|
2502
|
+
useEffect10(() => {
|
|
1375
2503
|
if (error)
|
|
1376
2504
|
submittedRef.current = false;
|
|
1377
2505
|
}, [error]);
|
|
1378
|
-
|
|
2506
|
+
useEffect10(() => {
|
|
1379
2507
|
if (resendCooldown <= 0)
|
|
1380
2508
|
return;
|
|
1381
2509
|
const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);
|
|
1382
2510
|
return () => clearTimeout(timer);
|
|
1383
2511
|
}, [resendCooldown]);
|
|
1384
|
-
const submitCode =
|
|
2512
|
+
const submitCode = useCallback6((code) => {
|
|
1385
2513
|
if (submittedRef.current || isSubmitting)
|
|
1386
2514
|
return;
|
|
1387
2515
|
submittedRef.current = true;
|
|
1388
2516
|
onSubmit(code);
|
|
1389
2517
|
}, [onSubmit, isSubmitting]);
|
|
1390
|
-
const handleDigitChange =
|
|
2518
|
+
const handleDigitChange = useCallback6((index2, value) => {
|
|
1391
2519
|
const digit = value.replace(/\D/g, "").slice(-1);
|
|
1392
2520
|
const newDigits = [...digits];
|
|
1393
2521
|
newDigits[index2] = digit;
|
|
@@ -1401,12 +2529,12 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
1401
2529
|
submitCode(code);
|
|
1402
2530
|
}
|
|
1403
2531
|
}, [digits, submitCode]);
|
|
1404
|
-
const handleKeyDown =
|
|
2532
|
+
const handleKeyDown = useCallback6((index2, e) => {
|
|
1405
2533
|
if (e.key === "Backspace" && !digits[index2] && index2 > 0) {
|
|
1406
2534
|
inputRefs.current[index2 - 1]?.focus();
|
|
1407
2535
|
}
|
|
1408
2536
|
}, [digits]);
|
|
1409
|
-
const handlePaste =
|
|
2537
|
+
const handlePaste = useCallback6((e) => {
|
|
1410
2538
|
e.preventDefault();
|
|
1411
2539
|
const pasted = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, OTP_LENGTH);
|
|
1412
2540
|
if (pasted.length > 0) {
|
|
@@ -1434,22 +2562,22 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
1434
2562
|
submittedRef.current = false;
|
|
1435
2563
|
inputRefs.current[0]?.focus();
|
|
1436
2564
|
}
|
|
1437
|
-
return /* @__PURE__ */
|
|
2565
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
1438
2566
|
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:px-4 skippr:py-4",
|
|
1439
2567
|
children: [
|
|
1440
|
-
/* @__PURE__ */
|
|
2568
|
+
/* @__PURE__ */ jsxs8("div", {
|
|
1441
2569
|
className: "skippr:mb-4 skippr:text-center",
|
|
1442
2570
|
children: [
|
|
1443
|
-
/* @__PURE__ */
|
|
2571
|
+
/* @__PURE__ */ jsx9("p", {
|
|
1444
2572
|
className: "skippr:text-sm skippr:font-medium skippr:text-foreground",
|
|
1445
2573
|
children: "Enter verification code"
|
|
1446
2574
|
}),
|
|
1447
|
-
/* @__PURE__ */
|
|
2575
|
+
/* @__PURE__ */ jsxs8("p", {
|
|
1448
2576
|
className: "skippr:mt-1 skippr:text-xs skippr:text-muted-foreground",
|
|
1449
2577
|
children: [
|
|
1450
2578
|
"We sent a 6-digit code to",
|
|
1451
2579
|
" ",
|
|
1452
|
-
/* @__PURE__ */
|
|
2580
|
+
/* @__PURE__ */ jsx9("span", {
|
|
1453
2581
|
className: "skippr:font-medium skippr:text-foreground",
|
|
1454
2582
|
children: email
|
|
1455
2583
|
})
|
|
@@ -1457,13 +2585,13 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
1457
2585
|
})
|
|
1458
2586
|
]
|
|
1459
2587
|
}),
|
|
1460
|
-
/* @__PURE__ */
|
|
2588
|
+
/* @__PURE__ */ jsxs8("form", {
|
|
1461
2589
|
onSubmit: handleSubmit,
|
|
1462
2590
|
className: "skippr:flex skippr:flex-col skippr:gap-3",
|
|
1463
2591
|
children: [
|
|
1464
|
-
/* @__PURE__ */
|
|
2592
|
+
/* @__PURE__ */ jsx9("div", {
|
|
1465
2593
|
className: "skippr:flex skippr:justify-center skippr:gap-1.5",
|
|
1466
|
-
children: digits.map((digit, index2) => /* @__PURE__ */
|
|
2594
|
+
children: digits.map((digit, index2) => /* @__PURE__ */ jsx9("input", {
|
|
1467
2595
|
ref: (el) => {
|
|
1468
2596
|
inputRefs.current[index2] = el;
|
|
1469
2597
|
},
|
|
@@ -1478,29 +2606,29 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
1478
2606
|
className: "skippr:h-10 skippr:w-10 skippr:rounded-md skippr:border skippr:border-border skippr:bg-background skippr:text-center skippr:text-sm skippr:font-semibold skippr:text-foreground skippr:outline-none focus:skippr:ring-2 focus:skippr:ring-primary/30 focus:skippr:border-primary disabled:skippr:opacity-50"
|
|
1479
2607
|
}, DIGIT_KEYS[index2]))
|
|
1480
2608
|
}),
|
|
1481
|
-
error && /* @__PURE__ */
|
|
2609
|
+
error && /* @__PURE__ */ jsx9("p", {
|
|
1482
2610
|
className: "skippr:text-xs skippr:text-center skippr:text-destructive",
|
|
1483
2611
|
children: error
|
|
1484
2612
|
}),
|
|
1485
|
-
/* @__PURE__ */
|
|
2613
|
+
/* @__PURE__ */ jsx9(Button, {
|
|
1486
2614
|
type: "submit",
|
|
1487
2615
|
disabled: isSubmitting || digits.join("").length !== OTP_LENGTH,
|
|
1488
2616
|
className: "skippr:w-full",
|
|
1489
|
-
children: isSubmitting ? /* @__PURE__ */
|
|
2617
|
+
children: isSubmitting ? /* @__PURE__ */ jsx9(LoaderCircle, {
|
|
1490
2618
|
className: "skippr:size-4 skippr:animate-spin"
|
|
1491
2619
|
}) : "Verify"
|
|
1492
2620
|
}),
|
|
1493
|
-
/* @__PURE__ */
|
|
2621
|
+
/* @__PURE__ */ jsxs8("div", {
|
|
1494
2622
|
className: "skippr:flex skippr:items-center skippr:justify-between skippr:text-xs",
|
|
1495
2623
|
children: [
|
|
1496
|
-
/* @__PURE__ */
|
|
2624
|
+
/* @__PURE__ */ jsx9("button", {
|
|
1497
2625
|
type: "button",
|
|
1498
2626
|
onClick: onBack,
|
|
1499
2627
|
disabled: isSubmitting,
|
|
1500
2628
|
className: "skippr:text-muted-foreground hover:skippr:text-foreground skippr:transition-colors disabled:skippr:opacity-50",
|
|
1501
2629
|
children: "Change email"
|
|
1502
2630
|
}),
|
|
1503
|
-
/* @__PURE__ */
|
|
2631
|
+
/* @__PURE__ */ jsx9("button", {
|
|
1504
2632
|
type: "button",
|
|
1505
2633
|
onClick: handleResend,
|
|
1506
2634
|
disabled: isSubmitting || resendCooldown > 0,
|
|
@@ -1516,50 +2644,50 @@ function OtpStep({ email, onSubmit, onResend, onBack, error, isSubmitting }) {
|
|
|
1516
2644
|
}
|
|
1517
2645
|
|
|
1518
2646
|
// src/components/MeetingControls.tsx
|
|
1519
|
-
import { jsx as
|
|
2647
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1520
2648
|
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 }) {
|
|
2649
|
+
function MeetingControls({ onHangUp, showScreenShareToggle = true }) {
|
|
1522
2650
|
const { isMuted, toggleMute, isScreenSharing, toggleScreenShare } = useMediaControls();
|
|
1523
|
-
return /* @__PURE__ */
|
|
2651
|
+
return /* @__PURE__ */ jsxs9("div", {
|
|
1524
2652
|
className: "skippr:shrink-0 skippr:border-t skippr:border-border skippr:bg-background skippr:px-4 skippr:py-4",
|
|
1525
2653
|
children: [
|
|
1526
|
-
/* @__PURE__ */
|
|
2654
|
+
/* @__PURE__ */ jsxs9("div", {
|
|
1527
2655
|
className: "skippr:flex skippr:items-center skippr:justify-center skippr:gap-3",
|
|
1528
2656
|
children: [
|
|
1529
|
-
/* @__PURE__ */
|
|
2657
|
+
/* @__PURE__ */ jsx10("button", {
|
|
1530
2658
|
type: "button",
|
|
1531
2659
|
onClick: toggleMute,
|
|
1532
2660
|
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
1533
2661
|
className: cn(CONTROL_BUTTON, isMuted ? "skippr:bg-destructive/15 skippr:text-destructive skippr:hover:bg-destructive/25" : "skippr:bg-muted skippr:text-foreground skippr:hover:bg-muted/80"),
|
|
1534
|
-
children: isMuted ? /* @__PURE__ */
|
|
2662
|
+
children: isMuted ? /* @__PURE__ */ jsx10(MicOff, {
|
|
1535
2663
|
className: "skippr:size-5"
|
|
1536
|
-
}) : /* @__PURE__ */
|
|
2664
|
+
}) : /* @__PURE__ */ jsx10(Mic, {
|
|
1537
2665
|
className: "skippr:size-5"
|
|
1538
2666
|
})
|
|
1539
2667
|
}),
|
|
1540
|
-
/* @__PURE__ */
|
|
2668
|
+
showScreenShareToggle && /* @__PURE__ */ jsx10("button", {
|
|
1541
2669
|
type: "button",
|
|
1542
2670
|
onClick: toggleScreenShare,
|
|
1543
2671
|
"aria-label": isScreenSharing ? "Stop sharing screen" : "Share screen",
|
|
1544
2672
|
className: cn(CONTROL_BUTTON, isScreenSharing ? "skippr:bg-bubble skippr:text-white skippr:hover:brightness-110" : "skippr:bg-muted skippr:text-foreground skippr:hover:bg-muted/80"),
|
|
1545
|
-
children: isScreenSharing ? /* @__PURE__ */
|
|
2673
|
+
children: isScreenSharing ? /* @__PURE__ */ jsx10(MonitorOff, {
|
|
1546
2674
|
className: "skippr:size-5"
|
|
1547
|
-
}) : /* @__PURE__ */
|
|
2675
|
+
}) : /* @__PURE__ */ jsx10(Monitor, {
|
|
1548
2676
|
className: "skippr:size-5"
|
|
1549
2677
|
})
|
|
1550
2678
|
}),
|
|
1551
|
-
/* @__PURE__ */
|
|
2679
|
+
/* @__PURE__ */ jsx10("button", {
|
|
1552
2680
|
type: "button",
|
|
1553
2681
|
onClick: onHangUp,
|
|
1554
2682
|
"aria-label": "End session",
|
|
1555
2683
|
className: cn(CONTROL_BUTTON, "skippr:bg-destructive skippr:text-destructive-foreground skippr:hover:bg-destructive/90"),
|
|
1556
|
-
children: /* @__PURE__ */
|
|
2684
|
+
children: /* @__PURE__ */ jsx10(PhoneOff, {
|
|
1557
2685
|
className: "skippr:size-5"
|
|
1558
2686
|
})
|
|
1559
2687
|
})
|
|
1560
2688
|
]
|
|
1561
2689
|
}),
|
|
1562
|
-
/* @__PURE__ */
|
|
2690
|
+
/* @__PURE__ */ jsx10("p", {
|
|
1563
2691
|
className: "skippr:mt-3 skippr:text-center skippr:text-[10px] skippr:text-muted-foreground",
|
|
1564
2692
|
children: "Powered by Skippr"
|
|
1565
2693
|
})
|
|
@@ -1568,17 +2696,17 @@ function MeetingControls({ onHangUp }) {
|
|
|
1568
2696
|
}
|
|
1569
2697
|
|
|
1570
2698
|
// src/components/MessageList.tsx
|
|
1571
|
-
import { useEffect as
|
|
2699
|
+
import { useEffect as useEffect12, useRef as useRef7 } from "react";
|
|
1572
2700
|
|
|
1573
2701
|
// src/components/ChatInput.tsx
|
|
1574
|
-
import { useEffect as
|
|
1575
|
-
import { jsx as
|
|
2702
|
+
import { useEffect as useEffect11, useRef as useRef6, useState as useState8 } from "react";
|
|
2703
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1576
2704
|
var MAX_INPUT_HEIGHT = 60;
|
|
1577
2705
|
function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
|
|
1578
|
-
const [inputText, setInputText] =
|
|
1579
|
-
const textareaRef =
|
|
2706
|
+
const [inputText, setInputText] = useState8("");
|
|
2707
|
+
const textareaRef = useRef6(null);
|
|
1580
2708
|
const canSend = inputText.trim().length > 0 && !isSendingChat;
|
|
1581
|
-
|
|
2709
|
+
useEffect11(() => {
|
|
1582
2710
|
if (autoFocus)
|
|
1583
2711
|
textareaRef.current?.focus();
|
|
1584
2712
|
}, [autoFocus]);
|
|
@@ -1609,13 +2737,13 @@ function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
|
|
|
1609
2737
|
handleSubmit(e);
|
|
1610
2738
|
}
|
|
1611
2739
|
}
|
|
1612
|
-
return /* @__PURE__ */
|
|
2740
|
+
return /* @__PURE__ */ jsx11("form", {
|
|
1613
2741
|
onSubmit: handleSubmit,
|
|
1614
2742
|
className: "skippr:border-t skippr:border-border skippr:p-3",
|
|
1615
|
-
children: /* @__PURE__ */
|
|
2743
|
+
children: /* @__PURE__ */ jsxs10("div", {
|
|
1616
2744
|
className: "skippr:flex skippr:items-center skippr:gap-2 skippr:rounded-xl skippr:bg-background skippr:ring-1 skippr:ring-foreground/10 skippr:px-3 skippr:py-2",
|
|
1617
2745
|
children: [
|
|
1618
|
-
/* @__PURE__ */
|
|
2746
|
+
/* @__PURE__ */ jsx11("textarea", {
|
|
1619
2747
|
ref: textareaRef,
|
|
1620
2748
|
rows: 1,
|
|
1621
2749
|
value: inputText,
|
|
@@ -1625,12 +2753,12 @@ function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
|
|
|
1625
2753
|
className: "skippr:flex-1 skippr:resize-none skippr:overflow-y-auto skippr:bg-transparent skippr:text-sm skippr:leading-5 skippr:text-foreground skippr:placeholder:text-muted-foreground skippr:outline-none",
|
|
1626
2754
|
style: { maxHeight: `${MAX_INPUT_HEIGHT}px` }
|
|
1627
2755
|
}),
|
|
1628
|
-
/* @__PURE__ */
|
|
2756
|
+
/* @__PURE__ */ jsx11("button", {
|
|
1629
2757
|
type: "submit",
|
|
1630
2758
|
disabled: !canSend,
|
|
1631
2759
|
"aria-label": "Send message",
|
|
1632
2760
|
className: cn("skippr:flex skippr:size-8 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-lg skippr:transition-colors", canSend ? "skippr:bg-primary skippr:text-primary-foreground skippr:hover:bg-primary/90" : "skippr:bg-muted-foreground/20 skippr:text-muted-foreground/60"),
|
|
1633
|
-
children: /* @__PURE__ */
|
|
2761
|
+
children: /* @__PURE__ */ jsx11(Send, {
|
|
1634
2762
|
className: "skippr:size-3.5"
|
|
1635
2763
|
})
|
|
1636
2764
|
})
|
|
@@ -1640,7 +2768,7 @@ function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
|
|
|
1640
2768
|
}
|
|
1641
2769
|
|
|
1642
2770
|
// src/components/ChatMessage.tsx
|
|
1643
|
-
import { jsx as
|
|
2771
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1644
2772
|
function formatTimestamp(ts) {
|
|
1645
2773
|
return new Date(ts).toLocaleTimeString("en-US", {
|
|
1646
2774
|
hour: "numeric",
|
|
@@ -1650,23 +2778,23 @@ function formatTimestamp(ts) {
|
|
|
1650
2778
|
}
|
|
1651
2779
|
function ChatMessage({ message }) {
|
|
1652
2780
|
const isAgent = message.role === "assistant";
|
|
1653
|
-
return /* @__PURE__ */
|
|
2781
|
+
return /* @__PURE__ */ jsxs11("div", {
|
|
1654
2782
|
className: cn("skippr:flex skippr:gap-2", isAgent ? "skippr:items-start" : "skippr:justify-end"),
|
|
1655
2783
|
children: [
|
|
1656
|
-
isAgent && /* @__PURE__ */
|
|
2784
|
+
isAgent && /* @__PURE__ */ jsx12("div", {
|
|
1657
2785
|
className: "skippr:mt-0.5 skippr:flex skippr:size-7 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-md skippr:bg-primary",
|
|
1658
|
-
children: /* @__PURE__ */
|
|
2786
|
+
children: /* @__PURE__ */ jsx12(Sparkles, {
|
|
1659
2787
|
className: "skippr:size-3.5 skippr:text-primary-foreground"
|
|
1660
2788
|
})
|
|
1661
2789
|
}),
|
|
1662
|
-
/* @__PURE__ */
|
|
2790
|
+
/* @__PURE__ */ jsxs11("div", {
|
|
1663
2791
|
className: cn("skippr:flex skippr:max-w-[80%] skippr:flex-col", isAgent ? "skippr:items-start" : "skippr:items-end"),
|
|
1664
2792
|
children: [
|
|
1665
|
-
/* @__PURE__ */
|
|
2793
|
+
/* @__PURE__ */ jsx12("div", {
|
|
1666
2794
|
className: cn("skippr:rounded-2xl skippr:px-4 skippr:py-2.5 skippr:text-sm skippr:leading-relaxed", isAgent ? "skippr:border skippr:border-border skippr:bg-card skippr:text-foreground" : "skippr:bg-primary skippr:text-primary-foreground"),
|
|
1667
2795
|
children: message.content
|
|
1668
2796
|
}),
|
|
1669
|
-
message.timestamp && /* @__PURE__ */
|
|
2797
|
+
message.timestamp && /* @__PURE__ */ jsx12("span", {
|
|
1670
2798
|
className: "skippr:mt-1 skippr:px-1 skippr:text-[10px] skippr:text-muted-foreground/60",
|
|
1671
2799
|
children: formatTimestamp(message.timestamp)
|
|
1672
2800
|
})
|
|
@@ -1677,33 +2805,33 @@ function ChatMessage({ message }) {
|
|
|
1677
2805
|
}
|
|
1678
2806
|
|
|
1679
2807
|
// src/components/TypingIndicator.tsx
|
|
1680
|
-
import { jsx as
|
|
2808
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1681
2809
|
function TypingIndicator() {
|
|
1682
|
-
return /* @__PURE__ */
|
|
2810
|
+
return /* @__PURE__ */ jsxs12("div", {
|
|
1683
2811
|
className: "skippr:flex skippr:items-start skippr:gap-2 skippr:animate-skippr-tab-fade",
|
|
1684
2812
|
children: [
|
|
1685
|
-
/* @__PURE__ */
|
|
2813
|
+
/* @__PURE__ */ jsx13("div", {
|
|
1686
2814
|
className: "skippr:mt-0.5 skippr:flex skippr:size-7 skippr:shrink-0 skippr:items-center skippr:justify-center skippr:rounded-md skippr:bg-primary",
|
|
1687
|
-
children: /* @__PURE__ */
|
|
2815
|
+
children: /* @__PURE__ */ jsx13(Sparkles, {
|
|
1688
2816
|
className: "skippr:size-3.5 skippr:text-primary-foreground"
|
|
1689
2817
|
})
|
|
1690
2818
|
}),
|
|
1691
|
-
/* @__PURE__ */
|
|
2819
|
+
/* @__PURE__ */ jsxs12("div", {
|
|
1692
2820
|
className: "skippr:inline-flex skippr:items-center skippr:gap-1 skippr:rounded-2xl skippr:border skippr:border-primary/20 skippr:bg-primary/10 skippr:px-4 skippr:py-2 skippr:text-xs skippr:text-primary",
|
|
1693
2821
|
children: [
|
|
1694
|
-
/* @__PURE__ */
|
|
2822
|
+
/* @__PURE__ */ jsx13("span", {
|
|
1695
2823
|
children: "Agent is analyzing your screen"
|
|
1696
2824
|
}),
|
|
1697
|
-
/* @__PURE__ */
|
|
2825
|
+
/* @__PURE__ */ jsxs12("span", {
|
|
1698
2826
|
className: "skippr:inline-flex skippr:items-center skippr:gap-[2px]",
|
|
1699
2827
|
children: [
|
|
1700
|
-
/* @__PURE__ */
|
|
2828
|
+
/* @__PURE__ */ jsx13("span", {
|
|
1701
2829
|
className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:0ms]"
|
|
1702
2830
|
}),
|
|
1703
|
-
/* @__PURE__ */
|
|
2831
|
+
/* @__PURE__ */ jsx13("span", {
|
|
1704
2832
|
className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:200ms]"
|
|
1705
2833
|
}),
|
|
1706
|
-
/* @__PURE__ */
|
|
2834
|
+
/* @__PURE__ */ jsx13("span", {
|
|
1707
2835
|
className: "skippr:size-1 skippr:rounded-full skippr:bg-primary skippr:animate-skippr-thinking-dot skippr:[animation-delay:400ms]"
|
|
1708
2836
|
})
|
|
1709
2837
|
]
|
|
@@ -1715,7 +2843,7 @@ function TypingIndicator() {
|
|
|
1715
2843
|
}
|
|
1716
2844
|
|
|
1717
2845
|
// src/components/MessageList.tsx
|
|
1718
|
-
import { jsx as
|
|
2846
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1719
2847
|
function MessageList({
|
|
1720
2848
|
messages,
|
|
1721
2849
|
isStreaming,
|
|
@@ -1723,31 +2851,31 @@ function MessageList({
|
|
|
1723
2851
|
isSendingChat,
|
|
1724
2852
|
autoFocus = false
|
|
1725
2853
|
}) {
|
|
1726
|
-
const scrollRef =
|
|
2854
|
+
const scrollRef = useRef7(null);
|
|
1727
2855
|
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
1728
|
-
|
|
2856
|
+
useEffect12(() => {
|
|
1729
2857
|
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1730
2858
|
}, [messages.length, lastMessage?.content]);
|
|
1731
2859
|
const showTyping = isStreaming && lastMessage?.role === "assistant" && lastMessage.content === "";
|
|
1732
|
-
return /* @__PURE__ */
|
|
2860
|
+
return /* @__PURE__ */ jsxs13("div", {
|
|
1733
2861
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
|
|
1734
2862
|
children: [
|
|
1735
|
-
/* @__PURE__ */
|
|
2863
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
1736
2864
|
className: "skippr:min-h-0 skippr:flex-1 skippr:space-y-4 skippr:overflow-y-auto skippr:p-4",
|
|
1737
2865
|
children: [
|
|
1738
|
-
messages.length === 0 && !showTyping && /* @__PURE__ */
|
|
2866
|
+
messages.length === 0 && !showTyping && /* @__PURE__ */ jsx14(LoadingDots, {
|
|
1739
2867
|
label: "Waiting for conversation to begin..."
|
|
1740
2868
|
}),
|
|
1741
|
-
messages.map((message) => /* @__PURE__ */
|
|
2869
|
+
messages.map((message) => /* @__PURE__ */ jsx14(ChatMessage, {
|
|
1742
2870
|
message
|
|
1743
2871
|
}, message.id)),
|
|
1744
|
-
showTyping && /* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
2872
|
+
showTyping && /* @__PURE__ */ jsx14(TypingIndicator, {}),
|
|
2873
|
+
/* @__PURE__ */ jsx14("div", {
|
|
1746
2874
|
ref: scrollRef
|
|
1747
2875
|
})
|
|
1748
2876
|
]
|
|
1749
2877
|
}),
|
|
1750
|
-
/* @__PURE__ */
|
|
2878
|
+
/* @__PURE__ */ jsx14(ChatInput, {
|
|
1751
2879
|
sendChatMessage,
|
|
1752
2880
|
isSendingChat,
|
|
1753
2881
|
autoFocus
|
|
@@ -1757,49 +2885,49 @@ function MessageList({
|
|
|
1757
2885
|
}
|
|
1758
2886
|
|
|
1759
2887
|
// src/components/SessionAgenda.tsx
|
|
1760
|
-
import { jsx as
|
|
2888
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1761
2889
|
function SessionAgenda({ phases, hasStarted }) {
|
|
1762
2890
|
if (phases.length === 0 || !hasStarted) {
|
|
1763
|
-
return /* @__PURE__ */
|
|
2891
|
+
return /* @__PURE__ */ jsx15("div", {
|
|
1764
2892
|
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
1765
|
-
children: /* @__PURE__ */
|
|
2893
|
+
children: /* @__PURE__ */ jsx15(LoadingDots, {
|
|
1766
2894
|
label: "Waiting for agenda to load..."
|
|
1767
2895
|
})
|
|
1768
2896
|
});
|
|
1769
2897
|
}
|
|
1770
|
-
return /* @__PURE__ */
|
|
2898
|
+
return /* @__PURE__ */ jsx15("div", {
|
|
1771
2899
|
className: "skippr:flex-1 skippr:overflow-y-auto skippr:px-4 skippr:py-4",
|
|
1772
|
-
children: /* @__PURE__ */
|
|
2900
|
+
children: /* @__PURE__ */ jsx15("div", {
|
|
1773
2901
|
className: "skippr:space-y-1",
|
|
1774
2902
|
children: phases.map((phase) => {
|
|
1775
2903
|
const isActive = phase.status === "active";
|
|
1776
2904
|
const isCompleted = phase.status === "completed";
|
|
1777
|
-
return /* @__PURE__ */
|
|
2905
|
+
return /* @__PURE__ */ jsxs14("div", {
|
|
1778
2906
|
className: cn("skippr:flex skippr:items-start skippr:gap-2.5 skippr:rounded-lg skippr:p-2 skippr:transition-colors", isActive && "skippr:bg-primary/10"),
|
|
1779
2907
|
children: [
|
|
1780
|
-
/* @__PURE__ */
|
|
2908
|
+
/* @__PURE__ */ jsx15("div", {
|
|
1781
2909
|
className: "skippr:mt-0.5",
|
|
1782
|
-
children: isCompleted ? /* @__PURE__ */
|
|
2910
|
+
children: isCompleted ? /* @__PURE__ */ jsx15(CircleCheck, {
|
|
1783
2911
|
className: "skippr:size-4 skippr:text-chart-3"
|
|
1784
|
-
}) : isActive ? /* @__PURE__ */
|
|
2912
|
+
}) : isActive ? /* @__PURE__ */ jsx15(Circle, {
|
|
1785
2913
|
className: "skippr:size-4 skippr:fill-primary/30 skippr:text-primary"
|
|
1786
|
-
}) : /* @__PURE__ */
|
|
2914
|
+
}) : /* @__PURE__ */ jsx15(Circle, {
|
|
1787
2915
|
className: "skippr:size-4 skippr:text-muted-foreground/30"
|
|
1788
2916
|
})
|
|
1789
2917
|
}),
|
|
1790
|
-
/* @__PURE__ */
|
|
2918
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
1791
2919
|
className: "skippr:min-w-0 skippr:flex-1",
|
|
1792
2920
|
children: [
|
|
1793
|
-
/* @__PURE__ */
|
|
2921
|
+
/* @__PURE__ */ jsx15("p", {
|
|
1794
2922
|
className: cn("skippr:text-sm", isCompleted && "skippr:text-muted-foreground skippr:line-through", isActive && "skippr:font-medium skippr:text-foreground", phase.status === "pending" && "skippr:text-muted-foreground"),
|
|
1795
2923
|
children: phase.name
|
|
1796
2924
|
}),
|
|
1797
|
-
phase.highlights.length > 0 && /* @__PURE__ */
|
|
2925
|
+
phase.highlights.length > 0 && /* @__PURE__ */ jsx15("ul", {
|
|
1798
2926
|
className: "skippr:mt-1 skippr:space-y-0.5",
|
|
1799
|
-
children: phase.highlights.map((text) => /* @__PURE__ */
|
|
2927
|
+
children: phase.highlights.map((text) => /* @__PURE__ */ jsxs14("li", {
|
|
1800
2928
|
className: cn("skippr:flex skippr:items-center skippr:gap-1.5 skippr:text-[11px] skippr:leading-tight", isCompleted ? "skippr:text-muted-foreground/40 skippr:line-through" : "skippr:text-muted-foreground/70"),
|
|
1801
2929
|
children: [
|
|
1802
|
-
/* @__PURE__ */
|
|
2930
|
+
/* @__PURE__ */ jsx15("span", {
|
|
1803
2931
|
className: "skippr:size-1 skippr:shrink-0 skippr:rounded-full skippr:bg-current"
|
|
1804
2932
|
}),
|
|
1805
2933
|
text
|
|
@@ -1816,12 +2944,12 @@ function SessionAgenda({ phases, hasStarted }) {
|
|
|
1816
2944
|
}
|
|
1817
2945
|
|
|
1818
2946
|
// src/components/SessionWarningBanner.tsx
|
|
1819
|
-
import { jsx as
|
|
2947
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
1820
2948
|
var SESSION_WARNING_THRESHOLD_SECS = 60;
|
|
1821
2949
|
function SessionWarningBanner({ remaining }) {
|
|
1822
2950
|
if (remaining === null || remaining <= 0 || remaining > SESSION_WARNING_THRESHOLD_SECS)
|
|
1823
2951
|
return null;
|
|
1824
|
-
return /* @__PURE__ */
|
|
2952
|
+
return /* @__PURE__ */ jsx16("div", {
|
|
1825
2953
|
"data-testid": "session-warning-banner",
|
|
1826
2954
|
className: "skippr:bg-red-50 skippr:px-4 skippr:py-1.5 skippr:text-center skippr:text-xs skippr:font-medium skippr:text-red-700",
|
|
1827
2955
|
children: "Session ending soon"
|
|
@@ -1829,24 +2957,24 @@ function SessionWarningBanner({ remaining }) {
|
|
|
1829
2957
|
}
|
|
1830
2958
|
|
|
1831
2959
|
// src/components/StartSessionPrompt.tsx
|
|
1832
|
-
import { jsx as
|
|
2960
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1833
2961
|
function StartSessionPrompt({
|
|
1834
2962
|
onStartSession,
|
|
1835
2963
|
isStarting,
|
|
1836
2964
|
error,
|
|
1837
2965
|
label = "Talk to Skippr"
|
|
1838
2966
|
}) {
|
|
1839
|
-
return /* @__PURE__ */
|
|
2967
|
+
return /* @__PURE__ */ jsxs15("div", {
|
|
1840
2968
|
className: "skippr:flex skippr:flex-1 skippr:flex-col skippr:items-center skippr:justify-center skippr:gap-3 skippr:px-4",
|
|
1841
2969
|
children: [
|
|
1842
|
-
/* @__PURE__ */
|
|
2970
|
+
/* @__PURE__ */ jsx17("button", {
|
|
1843
2971
|
type: "button",
|
|
1844
2972
|
onClick: onStartSession,
|
|
1845
2973
|
disabled: isStarting,
|
|
1846
2974
|
className: "skippr:cursor-pointer skippr:rounded-xl skippr:bg-primary skippr:px-8 skippr:py-3 skippr:text-sm skippr:font-medium skippr:text-primary-foreground skippr:transition-all skippr:hover:bg-primary/90 skippr:disabled:cursor-not-allowed skippr:disabled:opacity-60",
|
|
1847
2975
|
children: isStarting ? "Starting..." : label
|
|
1848
2976
|
}),
|
|
1849
|
-
error && /* @__PURE__ */
|
|
2977
|
+
error && /* @__PURE__ */ jsx17("p", {
|
|
1850
2978
|
className: "skippr:text-xs skippr:text-destructive",
|
|
1851
2979
|
children: error
|
|
1852
2980
|
})
|
|
@@ -1855,7 +2983,7 @@ function StartSessionPrompt({
|
|
|
1855
2983
|
}
|
|
1856
2984
|
|
|
1857
2985
|
// src/components/Sidebar.tsx
|
|
1858
|
-
import { jsx as
|
|
2986
|
+
import { jsx as jsx18, jsxs as jsxs16, Fragment as Fragment4 } from "react/jsx-runtime";
|
|
1859
2987
|
function Sidebar({
|
|
1860
2988
|
hideControls = false,
|
|
1861
2989
|
hideHeader = false,
|
|
@@ -1878,11 +3006,12 @@ function Sidebar({
|
|
|
1878
3006
|
isAuthSubmitting,
|
|
1879
3007
|
sidebarTab: activeTab,
|
|
1880
3008
|
setSidebarTab: setActiveTab,
|
|
1881
|
-
autoFocusChat
|
|
3009
|
+
autoFocusChat,
|
|
3010
|
+
captureMode
|
|
1882
3011
|
} = useLiveAgent();
|
|
1883
3012
|
const isFloating = variant === "floating";
|
|
1884
3013
|
const isSidebar = variant === "sidebar";
|
|
1885
|
-
|
|
3014
|
+
useEffect13(() => {
|
|
1886
3015
|
if (!isSidebar)
|
|
1887
3016
|
return;
|
|
1888
3017
|
const prop = position === "right" ? "marginRight" : "marginLeft";
|
|
@@ -1896,22 +3025,22 @@ function Sidebar({
|
|
|
1896
3025
|
document.body.style.transition = "";
|
|
1897
3026
|
};
|
|
1898
3027
|
}, [isSidebar, isPanelOpen, position]);
|
|
1899
|
-
return /* @__PURE__ */
|
|
3028
|
+
return /* @__PURE__ */ jsxs16("div", {
|
|
1900
3029
|
className: cn("skippr:fixed skippr:z-[9999]", "skippr:bg-card", "skippr:flex skippr:flex-col", "skippr:overflow-hidden", isFloating && "skippr:border skippr:border-border skippr:bottom-[88px] skippr:h-[calc(100vh-112px)] skippr:rounded-2xl skippr:shadow-[0_8px_30px_rgba(0,0,0,0.16),0_4px_12px_rgba(0,0,0,0.08)]", isFloating && (position === "right" ? "skippr:right-6" : "skippr:left-6"), isFloating && "skippr:transition-[opacity,transform] skippr:duration-300 skippr:ease-in-out", isFloating && (position === "right" ? "skippr:origin-bottom-right" : "skippr:origin-bottom-left"), isFloating && !isPanelOpen && "skippr:scale-0 skippr:opacity-0 skippr:pointer-events-none", isFloating && isPanelOpen && "skippr:scale-100 skippr:opacity-100", isSidebar && "skippr:top-0 skippr:h-full", isSidebar && "skippr:transition-[width] skippr:duration-300 skippr:ease-in-out", isSidebar && position === "right" && "skippr:right-0 skippr:border-l skippr:border-l-border", isSidebar && position === "left" && "skippr:left-0 skippr:border-r skippr:border-r-border", isSidebar && !isPanelOpen && "skippr:w-0 skippr:border-0"),
|
|
1901
3030
|
style: { width: isPanelOpen ? SIDEBAR_WIDTH : undefined },
|
|
1902
3031
|
children: [
|
|
1903
|
-
!hideHeader && /* @__PURE__ */
|
|
1904
|
-
!isAuthenticated && isValidating ? /* @__PURE__ */
|
|
3032
|
+
!hideHeader && /* @__PURE__ */ jsx18(ChatHeader, {}),
|
|
3033
|
+
!isAuthenticated && isValidating ? /* @__PURE__ */ jsx18("div", {
|
|
1905
3034
|
className: "skippr:flex skippr:flex-1 skippr:items-center skippr:justify-center",
|
|
1906
|
-
children: /* @__PURE__ */
|
|
3035
|
+
children: /* @__PURE__ */ jsx18(LoadingDots, {
|
|
1907
3036
|
label: "Loading..."
|
|
1908
3037
|
})
|
|
1909
|
-
}) : !isAuthenticated ? /* @__PURE__ */
|
|
3038
|
+
}) : !isAuthenticated ? /* @__PURE__ */ jsx18(LoginFlow, {
|
|
1910
3039
|
requestOtp,
|
|
1911
3040
|
verifyOtp,
|
|
1912
3041
|
error: authError,
|
|
1913
3042
|
isSubmitting: isAuthSubmitting
|
|
1914
|
-
}) : /* @__PURE__ */
|
|
3043
|
+
}) : /* @__PURE__ */ jsx18(AuthenticatedContent, {
|
|
1915
3044
|
isConnected,
|
|
1916
3045
|
onStartSession: startSession,
|
|
1917
3046
|
onDisconnect: disconnect,
|
|
@@ -1921,7 +3050,8 @@ function Sidebar({
|
|
|
1921
3050
|
onTabChange: setActiveTab,
|
|
1922
3051
|
hideControls,
|
|
1923
3052
|
startSessionLabel,
|
|
1924
|
-
autoFocusChat
|
|
3053
|
+
autoFocusChat,
|
|
3054
|
+
showScreenShareToggle: captureMode === "screenshare"
|
|
1925
3055
|
})
|
|
1926
3056
|
]
|
|
1927
3057
|
});
|
|
@@ -1936,52 +3066,53 @@ function AuthenticatedContent({
|
|
|
1936
3066
|
onTabChange,
|
|
1937
3067
|
hideControls,
|
|
1938
3068
|
startSessionLabel,
|
|
1939
|
-
autoFocusChat
|
|
3069
|
+
autoFocusChat,
|
|
3070
|
+
showScreenShareToggle
|
|
1940
3071
|
}) {
|
|
1941
|
-
return /* @__PURE__ */
|
|
3072
|
+
return /* @__PURE__ */ jsxs16(Fragment4, {
|
|
1942
3073
|
children: [
|
|
1943
|
-
isConnected && /* @__PURE__ */
|
|
1944
|
-
/* @__PURE__ */
|
|
3074
|
+
isConnected && /* @__PURE__ */ jsx18(ConnectedBanner, {}),
|
|
3075
|
+
/* @__PURE__ */ jsxs16("div", {
|
|
1945
3076
|
className: "skippr:flex skippr:gap-2 skippr:border-b skippr:border-border skippr:px-3 skippr:py-2",
|
|
1946
3077
|
children: [
|
|
1947
|
-
/* @__PURE__ */
|
|
3078
|
+
/* @__PURE__ */ jsxs16("button", {
|
|
1948
3079
|
type: "button",
|
|
1949
3080
|
className: cn("skippr:relative skippr:inline-flex skippr:cursor-pointer skippr:items-center skippr:gap-1.5 skippr:rounded-lg skippr:px-3 skippr:py-2 skippr:text-sm skippr:font-medium skippr:transition-all", activeTab === "chat" ? "skippr:text-foreground" : "skippr:text-muted-foreground skippr:hover:text-foreground"),
|
|
1950
3081
|
onClick: () => onTabChange("chat"),
|
|
1951
3082
|
children: [
|
|
1952
|
-
/* @__PURE__ */
|
|
3083
|
+
/* @__PURE__ */ jsx18(MessageCircle, {
|
|
1953
3084
|
className: "skippr:size-3.5"
|
|
1954
3085
|
}),
|
|
1955
3086
|
"Chat",
|
|
1956
|
-
activeTab === "chat" && /* @__PURE__ */
|
|
3087
|
+
activeTab === "chat" && /* @__PURE__ */ jsx18("span", {
|
|
1957
3088
|
className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
|
|
1958
3089
|
})
|
|
1959
3090
|
]
|
|
1960
3091
|
}),
|
|
1961
|
-
/* @__PURE__ */
|
|
3092
|
+
/* @__PURE__ */ jsxs16("button", {
|
|
1962
3093
|
type: "button",
|
|
1963
3094
|
className: cn("skippr:relative skippr:inline-flex skippr:cursor-pointer skippr:items-center skippr:gap-1.5 skippr:rounded-lg skippr:px-3 skippr:py-2 skippr:text-sm skippr:font-medium skippr:transition-all", activeTab === "agenda" ? "skippr:text-foreground" : "skippr:text-muted-foreground skippr:hover:text-foreground"),
|
|
1964
3095
|
onClick: () => onTabChange("agenda"),
|
|
1965
3096
|
children: [
|
|
1966
|
-
/* @__PURE__ */
|
|
3097
|
+
/* @__PURE__ */ jsx18(Calendar, {
|
|
1967
3098
|
className: "skippr:size-3.5"
|
|
1968
3099
|
}),
|
|
1969
3100
|
"Agenda",
|
|
1970
|
-
activeTab === "agenda" && /* @__PURE__ */
|
|
3101
|
+
activeTab === "agenda" && /* @__PURE__ */ jsx18("span", {
|
|
1971
3102
|
className: "skippr:absolute skippr:-bottom-2 skippr:left-3 skippr:right-3 skippr:h-0.5 skippr:rounded-full skippr:bg-foreground"
|
|
1972
3103
|
})
|
|
1973
3104
|
]
|
|
1974
3105
|
})
|
|
1975
3106
|
]
|
|
1976
3107
|
}),
|
|
1977
|
-
/* @__PURE__ */
|
|
3108
|
+
/* @__PURE__ */ jsx18("div", {
|
|
1978
3109
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col",
|
|
1979
|
-
children: isConnected || isStarting ? /* @__PURE__ */
|
|
3110
|
+
children: isConnected || isStarting ? /* @__PURE__ */ jsx18(ConnectedBody, {
|
|
1980
3111
|
activeTab,
|
|
1981
3112
|
autoFocusChat
|
|
1982
|
-
}) : /* @__PURE__ */
|
|
3113
|
+
}) : /* @__PURE__ */ jsx18("div", {
|
|
1983
3114
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
|
|
1984
|
-
children: /* @__PURE__ */
|
|
3115
|
+
children: /* @__PURE__ */ jsx18(StartSessionPrompt, {
|
|
1985
3116
|
onStartSession,
|
|
1986
3117
|
isStarting,
|
|
1987
3118
|
error,
|
|
@@ -1989,15 +3120,16 @@ function AuthenticatedContent({
|
|
|
1989
3120
|
})
|
|
1990
3121
|
}, `${activeTab}-empty`)
|
|
1991
3122
|
}),
|
|
1992
|
-
isConnected && !hideControls && /* @__PURE__ */
|
|
1993
|
-
onHangUp: onDisconnect
|
|
3123
|
+
isConnected && !hideControls && /* @__PURE__ */ jsx18(MeetingControls, {
|
|
3124
|
+
onHangUp: onDisconnect,
|
|
3125
|
+
showScreenShareToggle
|
|
1994
3126
|
})
|
|
1995
3127
|
]
|
|
1996
3128
|
});
|
|
1997
3129
|
}
|
|
1998
3130
|
function ConnectedBanner() {
|
|
1999
3131
|
const remaining = useSessionRemaining();
|
|
2000
|
-
return /* @__PURE__ */
|
|
3132
|
+
return /* @__PURE__ */ jsx18(SessionWarningBanner, {
|
|
2001
3133
|
remaining
|
|
2002
3134
|
});
|
|
2003
3135
|
}
|
|
@@ -2008,17 +3140,17 @@ function ConnectedBody({
|
|
|
2008
3140
|
const { allMessages, agentState, sendChatMessage, isSendingChat } = useCombinedMessages();
|
|
2009
3141
|
const { phases } = usePhaseUpdates();
|
|
2010
3142
|
if (activeTab === "agenda") {
|
|
2011
|
-
return /* @__PURE__ */
|
|
3143
|
+
return /* @__PURE__ */ jsx18("div", {
|
|
2012
3144
|
className: "skippr:min-h-0 skippr:flex-1 skippr:overflow-y-auto skippr:animate-skippr-tab-fade",
|
|
2013
|
-
children: /* @__PURE__ */
|
|
3145
|
+
children: /* @__PURE__ */ jsx18(SessionAgenda, {
|
|
2014
3146
|
phases,
|
|
2015
3147
|
hasStarted: allMessages.length > 0 || agentState === "speaking"
|
|
2016
3148
|
})
|
|
2017
3149
|
}, "agenda");
|
|
2018
3150
|
}
|
|
2019
|
-
return /* @__PURE__ */
|
|
3151
|
+
return /* @__PURE__ */ jsx18("div", {
|
|
2020
3152
|
className: "skippr:flex skippr:min-h-0 skippr:flex-1 skippr:flex-col skippr:animate-skippr-tab-fade",
|
|
2021
|
-
children: /* @__PURE__ */
|
|
3153
|
+
children: /* @__PURE__ */ jsx18(MessageList, {
|
|
2022
3154
|
messages: allMessages,
|
|
2023
3155
|
isStreaming: agentState === "speaking",
|
|
2024
3156
|
sendChatMessage,
|
|
@@ -2029,45 +3161,55 @@ function ConnectedBody({
|
|
|
2029
3161
|
}
|
|
2030
3162
|
|
|
2031
3163
|
// src/components/SidebarTrigger.tsx
|
|
2032
|
-
import { jsx as
|
|
3164
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
2033
3165
|
function SidebarTrigger() {
|
|
2034
3166
|
const { isPanelOpen, togglePanel, minimizePanel, minimizable, position, isMinimized } = useLiveAgent();
|
|
2035
3167
|
if (isMinimized)
|
|
2036
3168
|
return null;
|
|
2037
3169
|
const handleClick = isPanelOpen && minimizable ? minimizePanel : togglePanel;
|
|
2038
|
-
return /* @__PURE__ */
|
|
3170
|
+
return /* @__PURE__ */ jsx19("button", {
|
|
2039
3171
|
type: "button",
|
|
2040
3172
|
onClick: handleClick,
|
|
2041
3173
|
title: isPanelOpen ? "Close chat" : "Open chat",
|
|
2042
3174
|
"aria-label": isPanelOpen ? "Close chat" : "Open chat",
|
|
2043
3175
|
className: cn("skippr:fixed skippr:bottom-6 skippr:z-[9998]", "skippr:flex skippr:size-12 skippr:items-center skippr:justify-center", "skippr:rounded-[14px] skippr:bg-bubble skippr:text-white", "skippr:shadow-[0_4px_16px_rgba(45,43,61,0.45),0_2px_4px_rgba(0,0,0,0.1)] skippr:transition-all", "skippr:cursor-pointer skippr:hover:brightness-110 skippr:hover:-translate-y-0.5 skippr:active:translate-y-0", position === "right" ? "skippr:right-6" : "skippr:left-6"),
|
|
2044
|
-
children: isPanelOpen ? /* @__PURE__ */
|
|
3176
|
+
children: isPanelOpen ? /* @__PURE__ */ jsx19(ChevronDown, {
|
|
2045
3177
|
className: "skippr:size-5"
|
|
2046
|
-
}) : /* @__PURE__ */
|
|
3178
|
+
}) : /* @__PURE__ */ jsx19(Logo, {
|
|
2047
3179
|
className: "skippr:size-7"
|
|
2048
3180
|
})
|
|
2049
3181
|
});
|
|
2050
3182
|
}
|
|
2051
3183
|
|
|
2052
3184
|
// src/components/LiveAgent.tsx
|
|
2053
|
-
import { jsx as
|
|
2054
|
-
function LiveAgent({
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
3185
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3186
|
+
function LiveAgent(props) {
|
|
3187
|
+
const {
|
|
3188
|
+
agentId,
|
|
3189
|
+
authToken: authTokenProp,
|
|
3190
|
+
appKey,
|
|
3191
|
+
userToken,
|
|
3192
|
+
position = "right",
|
|
3193
|
+
variant = "floating",
|
|
3194
|
+
minimizable = true,
|
|
3195
|
+
defaultOpen = false,
|
|
3196
|
+
welcomeMessage,
|
|
3197
|
+
hideControls = false,
|
|
3198
|
+
hideHeader = false,
|
|
3199
|
+
startSessionLabel = "Talk to Skippr",
|
|
3200
|
+
autoFocusChat = true,
|
|
3201
|
+
showAgentStateBanner = true,
|
|
3202
|
+
children
|
|
3203
|
+
} = props;
|
|
3204
|
+
const captureMode = props.captureMode ?? "screenshare";
|
|
3205
|
+
let agentControls;
|
|
3206
|
+
if ("agentControls" in props && props.agentControls) {
|
|
3207
|
+
if (captureMode === "auto") {
|
|
3208
|
+
agentControls = props.agentControls;
|
|
3209
|
+
} else {
|
|
3210
|
+
console.warn('[Skippr] agentControls requires captureMode: "auto"');
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
2071
3213
|
const auth = useAuth({ appKey });
|
|
2072
3214
|
const effectiveAuthToken = authTokenProp || auth.authToken || undefined;
|
|
2073
3215
|
const {
|
|
@@ -2081,16 +3223,18 @@ function LiveAgent({
|
|
|
2081
3223
|
pendingScreenStream
|
|
2082
3224
|
} = useSession({
|
|
2083
3225
|
agentId,
|
|
3226
|
+
captureMode,
|
|
3227
|
+
agentControls,
|
|
2084
3228
|
authToken: effectiveAuthToken,
|
|
2085
3229
|
appKey,
|
|
2086
3230
|
userToken
|
|
2087
3231
|
});
|
|
2088
|
-
const [isPanelOpen, setIsPanelOpen] =
|
|
2089
|
-
const [isMinimized, setIsMinimized] =
|
|
2090
|
-
const [sidebarTab, setSidebarTab] =
|
|
2091
|
-
const [welcomeDismissed, setWelcomeDismissed] =
|
|
2092
|
-
const dismissWelcome =
|
|
2093
|
-
const [currentPosition, setCurrentPosition] =
|
|
3232
|
+
const [isPanelOpen, setIsPanelOpen] = useState9(defaultOpen);
|
|
3233
|
+
const [isMinimized, setIsMinimized] = useState9(minimizable && !defaultOpen);
|
|
3234
|
+
const [sidebarTab, setSidebarTab] = useState9("agenda");
|
|
3235
|
+
const [welcomeDismissed, setWelcomeDismissed] = useState9(false);
|
|
3236
|
+
const dismissWelcome = useCallback7(() => setWelcomeDismissed(true), []);
|
|
3237
|
+
const [currentPosition, setCurrentPosition] = useState9(() => {
|
|
2094
3238
|
try {
|
|
2095
3239
|
const saved = localStorage.getItem("skippr_widget_position");
|
|
2096
3240
|
if (saved === "left" || saved === "right")
|
|
@@ -2098,20 +3242,20 @@ function LiveAgent({
|
|
|
2098
3242
|
} catch {}
|
|
2099
3243
|
return position;
|
|
2100
3244
|
});
|
|
2101
|
-
const setPositionWithPersist =
|
|
3245
|
+
const setPositionWithPersist = useCallback7((pos) => {
|
|
2102
3246
|
setCurrentPosition(pos);
|
|
2103
3247
|
try {
|
|
2104
3248
|
localStorage.setItem("skippr_widget_position", pos);
|
|
2105
3249
|
} catch {}
|
|
2106
3250
|
}, []);
|
|
2107
|
-
const openPanel =
|
|
2108
|
-
const closePanel =
|
|
2109
|
-
const togglePanel =
|
|
2110
|
-
const expandPanel =
|
|
3251
|
+
const openPanel = useCallback7(() => setIsPanelOpen(true), []);
|
|
3252
|
+
const closePanel = useCallback7(() => setIsPanelOpen(false), []);
|
|
3253
|
+
const togglePanel = useCallback7(() => setIsPanelOpen((prev) => !prev), []);
|
|
3254
|
+
const expandPanel = useCallback7(() => {
|
|
2111
3255
|
setIsMinimized(false);
|
|
2112
3256
|
setIsPanelOpen(true);
|
|
2113
3257
|
}, []);
|
|
2114
|
-
const minimizePanel =
|
|
3258
|
+
const minimizePanel = useCallback7(() => {
|
|
2115
3259
|
if (!minimizable)
|
|
2116
3260
|
return;
|
|
2117
3261
|
setIsMinimized(true);
|
|
@@ -2119,8 +3263,8 @@ function LiveAgent({
|
|
|
2119
3263
|
}, [minimizable]);
|
|
2120
3264
|
const isConnected = connection !== null;
|
|
2121
3265
|
const isAuthenticated = !!userToken || !!authTokenProp || auth.isAuthenticated;
|
|
2122
|
-
const prevConnectionRef =
|
|
2123
|
-
|
|
3266
|
+
const prevConnectionRef = useRef8(connection);
|
|
3267
|
+
useEffect14(() => {
|
|
2124
3268
|
const connectionChanged = prevConnectionRef.current !== connection;
|
|
2125
3269
|
prevConnectionRef.current = connection;
|
|
2126
3270
|
if (connectionChanged && minimizable) {
|
|
@@ -2157,7 +3301,9 @@ function LiveAgent({
|
|
|
2157
3301
|
isAuthSubmitting: auth.isSubmitting,
|
|
2158
3302
|
sidebarTab,
|
|
2159
3303
|
setSidebarTab,
|
|
2160
|
-
autoFocusChat
|
|
3304
|
+
autoFocusChat,
|
|
3305
|
+
captureMode,
|
|
3306
|
+
agentControls
|
|
2161
3307
|
}), [
|
|
2162
3308
|
connection,
|
|
2163
3309
|
shouldConnect,
|
|
@@ -2186,32 +3332,41 @@ function LiveAgent({
|
|
|
2186
3332
|
auth.logout,
|
|
2187
3333
|
auth.isSubmitting,
|
|
2188
3334
|
sidebarTab,
|
|
2189
|
-
autoFocusChat
|
|
3335
|
+
autoFocusChat,
|
|
3336
|
+
captureMode,
|
|
3337
|
+
agentControls
|
|
2190
3338
|
]);
|
|
2191
|
-
return /* @__PURE__ */
|
|
3339
|
+
return /* @__PURE__ */ jsx20(LiveAgentContext.Provider, {
|
|
2192
3340
|
value: ctx,
|
|
2193
|
-
children: /* @__PURE__ */
|
|
3341
|
+
children: /* @__PURE__ */ jsxs17(LiveKitRoom, {
|
|
2194
3342
|
serverUrl: connection?.livekitUrl,
|
|
2195
3343
|
token: connection?.token,
|
|
2196
3344
|
connect: shouldConnect,
|
|
2197
3345
|
audio: true,
|
|
2198
3346
|
onDisconnected: disconnect,
|
|
2199
3347
|
children: [
|
|
2200
|
-
connection && /* @__PURE__ */
|
|
2201
|
-
|
|
2202
|
-
connection && /* @__PURE__ */ jsx19(AutoStartMedia, {
|
|
3348
|
+
connection && /* @__PURE__ */ jsx20(RoomAudioRenderer, {}),
|
|
3349
|
+
connection && captureMode === "screenshare" && /* @__PURE__ */ jsx20(AutoStartMedia, {
|
|
2203
3350
|
pendingScreenStream
|
|
2204
3351
|
}),
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
3352
|
+
connection && captureMode === "auto" && /* @__PURE__ */ jsx20(DomCapture, {}),
|
|
3353
|
+
connection && captureMode === "auto" && agentControls?.highlight && /* @__PURE__ */ jsx20(HighlightOverlay, {}),
|
|
3354
|
+
/* @__PURE__ */ jsxs17("div", {
|
|
3355
|
+
id: WIDGET_ROOT_ID,
|
|
3356
|
+
children: [
|
|
3357
|
+
showAgentStateBanner && /* @__PURE__ */ jsx20(AgentStateBanner, {}),
|
|
3358
|
+
isMinimized && /* @__PURE__ */ jsx20(MinimizedBubble, {
|
|
3359
|
+
welcomeMessage,
|
|
3360
|
+
welcomeDismissed,
|
|
3361
|
+
onDismissWelcome: dismissWelcome
|
|
3362
|
+
}),
|
|
3363
|
+
/* @__PURE__ */ jsx20(SidebarTrigger, {}),
|
|
3364
|
+
/* @__PURE__ */ jsx20(Sidebar, {
|
|
3365
|
+
hideControls,
|
|
3366
|
+
hideHeader,
|
|
3367
|
+
startSessionLabel
|
|
3368
|
+
})
|
|
3369
|
+
]
|
|
2215
3370
|
}),
|
|
2216
3371
|
children
|
|
2217
3372
|
]
|
|
@@ -2219,9 +3374,9 @@ function LiveAgent({
|
|
|
2219
3374
|
});
|
|
2220
3375
|
}
|
|
2221
3376
|
// src/hooks/useIsLocalSpeaking.ts
|
|
2222
|
-
import { useIsSpeaking, useLocalParticipant as
|
|
3377
|
+
import { useIsSpeaking, useLocalParticipant as useLocalParticipant6 } from "@livekit/components-react/hooks";
|
|
2223
3378
|
function useIsLocalSpeaking() {
|
|
2224
|
-
const { localParticipant } =
|
|
3379
|
+
const { localParticipant } = useLocalParticipant6();
|
|
2225
3380
|
return useIsSpeaking(localParticipant);
|
|
2226
3381
|
}
|
|
2227
3382
|
export {
|