@skippr/live-agent-sdk 0.94.0 → 0.95.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.
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
 
9
9
  // src/components/LiveAgent.tsx
10
10
  import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
11
- import { Fragment as Fragment7, useCallback as useCallback32, useEffect as useEffect49, useMemo as useMemo7, useRef as useRef42, useState as useState39 } from "react";
11
+ import { Fragment as Fragment7, useCallback as useCallback32, useEffect as useEffect49, useMemo as useMemo8, useRef as useRef42, useState as useState39 } from "react";
12
12
  // ../../node_modules/.bun/lucide-react@1.8.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/createLucideIcon.js
13
13
  import { forwardRef as forwardRef2, createElement as createElement3 } from "react";
14
14
 
@@ -2944,6 +2944,7 @@ import { useEffect as useEffect10, useState as useState8 } from "react";
2944
2944
  var targetListeners = new Set;
2945
2945
  var currentTarget = null;
2946
2946
  var currentTargetOwner = null;
2947
+ var currentTargetSetAt = 0;
2947
2948
  function notifyTargetListeners() {
2948
2949
  for (const listener of targetListeners)
2949
2950
  listener(currentTarget);
@@ -2951,6 +2952,7 @@ function notifyTargetListeners() {
2951
2952
  function setCursorTarget(owner, target) {
2952
2953
  currentTarget = target;
2953
2954
  currentTargetOwner = owner;
2955
+ currentTargetSetAt = Date.now();
2954
2956
  notifyTargetListeners();
2955
2957
  }
2956
2958
  function clearCursorTarget(owner) {
@@ -2960,6 +2962,14 @@ function clearCursorTarget(owner) {
2960
2962
  currentTargetOwner = null;
2961
2963
  notifyTargetListeners();
2962
2964
  }
2965
+ function getCursorTarget() {
2966
+ return currentTarget;
2967
+ }
2968
+ function cursorTargetElapsedMs() {
2969
+ if (!currentTarget)
2970
+ return 0;
2971
+ return Date.now() - currentTargetSetAt;
2972
+ }
2963
2973
  function subscribeCursorTarget(listener) {
2964
2974
  targetListeners.add(listener);
2965
2975
  listener(currentTarget);
@@ -4857,7 +4867,7 @@ function AutoStartMedia({
4857
4867
  // src/components/DomCapture.tsx
4858
4868
  import { useConnectionState as useConnectionState2, useLocalParticipant as useLocalParticipant5 } from "@livekit/components-react/hooks";
4859
4869
  import { ConnectionState as ConnectionState2, ScreenSharePresets as ScreenSharePresets2, Track as Track3 } from "livekit-client";
4860
- import { useEffect as useEffect22, useRef as useRef17 } from "react";
4870
+ import { useContext as useContext9, useEffect as useEffect22, useMemo as useMemo4, useRef as useRef17 } from "react";
4861
4871
 
4862
4872
  // src/capture/a11yUtils.ts
4863
4873
  var ROLE_BY_TAG = {
@@ -5635,6 +5645,137 @@ function installDomEventListeners(localParticipant, options = {}) {
5635
5645
  };
5636
5646
  }
5637
5647
 
5648
+ // src/lib/brandForeground.ts
5649
+ var DARK_FOREGROUND = "#2d2b3d";
5650
+ var LIGHT_FOREGROUND = "#ffffff";
5651
+ var LUMINANCE_THRESHOLD = 0.179;
5652
+ var HEX_COLOR = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
5653
+ function isHexColor(value) {
5654
+ return HEX_COLOR.test(value);
5655
+ }
5656
+ function linearChannel(hex, offset) {
5657
+ const channel = Number.parseInt(hex.slice(offset, offset + 2), 16) / 255;
5658
+ return channel <= 0.03928 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
5659
+ }
5660
+ function brandForeground(accentColor) {
5661
+ const hex = accentColor.replace("#", "");
5662
+ const fullHex = hex.length === 3 ? hex.split("").map((char) => char + char).join("") : hex;
5663
+ const luminance = 0.2126 * linearChannel(fullHex, 0) + 0.7152 * linearChannel(fullHex, 2) + 0.0722 * linearChannel(fullHex, 4);
5664
+ return luminance > LUMINANCE_THRESHOLD ? DARK_FOREGROUND : LIGHT_FOREGROUND;
5665
+ }
5666
+
5667
+ // src/capture/paintCursorOverlay.ts
5668
+ var DEFAULT_BRAND_COLOR = "#2bc0ae";
5669
+ var DEFAULT_BRAND_FOREGROUND = "#ffffff";
5670
+ var RING_INK_IN_DURATION_MS = 900;
5671
+ var POINTER_SIZE_PX = 16;
5672
+ var LABEL_FONT_SIZE_PX = 11;
5673
+ var LABEL_PADDING_X_PX = 10;
5674
+ var LABEL_PADDING_Y_PX = 5;
5675
+ var LABEL_GAP_BELOW_RING_PX = 6;
5676
+ var GESTURE_EDGE_INSET_PX = 96;
5677
+ var GESTURE_LABEL_GAP_PX = 18;
5678
+ var DEFAULT_BRAND_PALETTE = {
5679
+ color: DEFAULT_BRAND_COLOR,
5680
+ foreground: DEFAULT_BRAND_FOREGROUND
5681
+ };
5682
+ function resolveBrandPalette(accentColor) {
5683
+ if (!accentColor || !isHexColor(accentColor))
5684
+ return DEFAULT_BRAND_PALETTE;
5685
+ return { color: accentColor, foreground: brandForeground(accentColor) };
5686
+ }
5687
+ function easeInOut2(progress) {
5688
+ return progress < 0.5 ? 2 * progress * progress : 1 - (-2 * progress + 2) ** 2 / 2;
5689
+ }
5690
+ function tracePillPath(ctx, x, y, width, height, radius) {
5691
+ const clampedRadius = Math.min(radius, width / 2, height / 2);
5692
+ ctx.beginPath();
5693
+ ctx.moveTo(x + clampedRadius, y);
5694
+ ctx.arcTo(x + width, y, x + width, y + height, clampedRadius);
5695
+ ctx.arcTo(x + width, y + height, x, y + height, clampedRadius);
5696
+ ctx.arcTo(x, y + height, x, y, clampedRadius);
5697
+ ctx.arcTo(x, y, x + width, y, clampedRadius);
5698
+ ctx.closePath();
5699
+ }
5700
+ function ringPerimeter(rect) {
5701
+ const width = rect.width + RING_PAD * 2 - 2;
5702
+ const height = rect.height + RING_PAD * 2 - 2;
5703
+ const radius = ringRadius(rect);
5704
+ return 2 * (width - 2 * radius) + 2 * (height - 2 * radius) + 2 * Math.PI * radius;
5705
+ }
5706
+ function drawRingInkingIn(ctx, rect, transform, elapsedMs, brand) {
5707
+ const x = (rect.left - RING_PAD) * transform.scale + transform.offsetX;
5708
+ const y = (rect.top - RING_PAD) * transform.scale + transform.offsetY;
5709
+ tracePillPath(ctx, x, y, (rect.width + RING_PAD * 2) * transform.scale, (rect.height + RING_PAD * 2) * transform.scale, ringRadius(rect) * transform.scale);
5710
+ const dashLength = ringPerimeter(rect) * transform.scale;
5711
+ const inkedFraction = easeInOut2(Math.min(1, elapsedMs / RING_INK_IN_DURATION_MS));
5712
+ ctx.lineWidth = 2 * transform.scale;
5713
+ ctx.strokeStyle = brand.color;
5714
+ ctx.setLineDash([dashLength]);
5715
+ ctx.lineDashOffset = dashLength * (1 - inkedFraction);
5716
+ ctx.stroke();
5717
+ ctx.setLineDash([]);
5718
+ ctx.lineDashOffset = 0;
5719
+ }
5720
+ function drawLabelPill(ctx, label, centerX, topY, transform, brand) {
5721
+ const fontSize = LABEL_FONT_SIZE_PX * transform.scale;
5722
+ const paddingX = LABEL_PADDING_X_PX * transform.scale;
5723
+ const paddingY = LABEL_PADDING_Y_PX * transform.scale;
5724
+ ctx.font = `500 ${fontSize}px system-ui, sans-serif`;
5725
+ const pillWidth = ctx.measureText(label).width + paddingX * 2;
5726
+ const pillHeight = fontSize + paddingY * 2;
5727
+ tracePillPath(ctx, centerX - pillWidth / 2, topY, pillWidth, pillHeight, pillHeight / 2);
5728
+ ctx.fillStyle = brand.color;
5729
+ ctx.fill();
5730
+ ctx.fillStyle = brand.foreground;
5731
+ ctx.textAlign = "center";
5732
+ ctx.textBaseline = "middle";
5733
+ ctx.fillText(label, centerX, topY + pillHeight / 2);
5734
+ }
5735
+ function drawPointer(ctx, x, y, scale, brand) {
5736
+ const size = POINTER_SIZE_PX * scale;
5737
+ ctx.beginPath();
5738
+ ctx.moveTo(x, y);
5739
+ ctx.lineTo(x, y + size);
5740
+ ctx.lineTo(x + size * 0.28, y + size * 0.72);
5741
+ ctx.lineTo(x + size * 0.62, y + size * 0.66);
5742
+ ctx.closePath();
5743
+ ctx.fillStyle = brand.color;
5744
+ ctx.fill();
5745
+ ctx.lineWidth = 1 * scale;
5746
+ ctx.strokeStyle = "#ffffff";
5747
+ ctx.stroke();
5748
+ }
5749
+ function paintCursorOverlay(ctx, transform, brand = DEFAULT_BRAND_PALETTE) {
5750
+ const target = getCursorTarget();
5751
+ if (!target)
5752
+ return;
5753
+ const elapsedMs = cursorTargetElapsedMs();
5754
+ ctx.save();
5755
+ if (target.kind === "gesture" && target.gesture) {
5756
+ const pointerX = window.innerWidth / 2 * transform.scale + transform.offsetX;
5757
+ const edgeY = target.gesture.direction === "down" ? window.innerHeight - GESTURE_EDGE_INSET_PX : GESTURE_EDGE_INSET_PX;
5758
+ const pointerY = edgeY * transform.scale + transform.offsetY;
5759
+ drawPointer(ctx, pointerX, pointerY, transform.scale, brand);
5760
+ drawLabelPill(ctx, target.label, pointerX, pointerY + GESTURE_LABEL_GAP_PX * transform.scale, transform, brand);
5761
+ ctx.restore();
5762
+ return;
5763
+ }
5764
+ const element = target.element;
5765
+ if (!element?.isConnected) {
5766
+ ctx.restore();
5767
+ return;
5768
+ }
5769
+ const rect = getRectInTopViewport(element);
5770
+ drawRingInkingIn(ctx, rect, transform, elapsedMs, brand);
5771
+ const ringCenterX = (rect.left + rect.width / 2) * transform.scale + transform.offsetX;
5772
+ const labelTopY = (rect.bottom + RING_PAD + LABEL_GAP_BELOW_RING_PX) * transform.scale + transform.offsetY;
5773
+ drawLabelPill(ctx, target.label, ringCenterX, labelTopY, transform, brand);
5774
+ const ringCenterY = (rect.top + rect.height / 2) * transform.scale + transform.offsetY;
5775
+ drawPointer(ctx, ringCenterX, ringCenterY, transform.scale, brand);
5776
+ ctx.restore();
5777
+ }
5778
+
5638
5779
  // src/capture/snapdom.ts
5639
5780
  var cachedSnapdomModule = null;
5640
5781
  function loadSnapdom() {
@@ -5687,7 +5828,7 @@ function getCanvasCaptureStream(canvas, fps) {
5687
5828
  }
5688
5829
  return captureStreamFn.call(canvas, fps);
5689
5830
  }
5690
- async function paintViewportSnapshot(canvas, ctx, isStillWanted) {
5831
+ async function rasterizeViewportToPageLayer(width, height) {
5691
5832
  const dpr = window.devicePixelRatio || 1;
5692
5833
  const snapshotCanvas = await snapToCanvas(document.documentElement, {
5693
5834
  filter: shouldIncludeInSnapshot,
@@ -5696,20 +5837,36 @@ async function paintViewportSnapshot(canvas, ctx, isStillWanted) {
5696
5837
  fast: false,
5697
5838
  dpr
5698
5839
  });
5699
- if (!isStillWanted())
5700
- return;
5701
5840
  const sourceX = window.scrollX * dpr;
5702
5841
  const sourceY = window.scrollY * dpr;
5703
5842
  const sourceWidth = window.innerWidth * dpr;
5704
5843
  const sourceHeight = window.innerHeight * dpr;
5705
- const fitScale = Math.min(canvas.width / sourceWidth, canvas.height / sourceHeight);
5844
+ const fitScale = Math.min(width / sourceWidth, height / sourceHeight);
5706
5845
  const destWidth = sourceWidth * fitScale;
5707
5846
  const destHeight = sourceHeight * fitScale;
5708
- const destX = (canvas.width - destWidth) / 2;
5709
- const destY = (canvas.height - destHeight) / 2;
5847
+ const destX = (width - destWidth) / 2;
5848
+ const destY = (height - destHeight) / 2;
5849
+ const layer = document.createElement("canvas");
5850
+ layer.width = width;
5851
+ layer.height = height;
5852
+ const layerCtx = layer.getContext("2d");
5853
+ if (!layerCtx)
5854
+ throw new Error("Failed to get 2D context for page layer");
5855
+ layerCtx.fillStyle = "#ffffff";
5856
+ layerCtx.fillRect(0, 0, width, height);
5857
+ layerCtx.drawImage(snapshotCanvas, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
5858
+ return {
5859
+ canvas: layer,
5860
+ viewportToCanvas: { scale: dpr * fitScale, offsetX: destX, offsetY: destY }
5861
+ };
5862
+ }
5863
+ function compositePageAndCursorOverlay(ctx, canvas, cachedPage, brand) {
5710
5864
  ctx.fillStyle = "#ffffff";
5711
5865
  ctx.fillRect(0, 0, canvas.width, canvas.height);
5712
- ctx.drawImage(snapshotCanvas, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
5866
+ if (!cachedPage)
5867
+ return;
5868
+ ctx.drawImage(cachedPage.canvas, 0, 0);
5869
+ paintCursorOverlay(ctx, cachedPage.viewportToCanvas, brand);
5713
5870
  }
5714
5871
  async function gzipIfBeneficial(snapshotBytes) {
5715
5872
  if (typeof CompressionStream === "undefined" || snapshotBytes.byteLength <= DOM_SNAPSHOT_GZIP_THRESHOLD_BYTES) {
@@ -5761,6 +5918,10 @@ function DomCapture({ pushOrTapMicMode = false }) {
5761
5918
  const { localParticipant } = useLocalParticipant5();
5762
5919
  const connectionState = useConnectionState2();
5763
5920
  const { isHeld } = useSessionHoldContext();
5921
+ const accentColor = useContext9(LiveAgentContext)?.appearance?.accentColor ?? null;
5922
+ const brand = useMemo4(() => resolveBrandPalette(accentColor), [accentColor]);
5923
+ const brandRef = useRef17(brand);
5924
+ brandRef.current = brand;
5764
5925
  const didStartRef = useRef17(false);
5765
5926
  const pausedRef = useRef17(false);
5766
5927
  const freezeCaptureRef = useRef17(null);
@@ -5787,6 +5948,7 @@ function DomCapture({ pushOrTapMicMode = false }) {
5787
5948
  let a11yPublishInFlight = false;
5788
5949
  let a11yPublishPending = false;
5789
5950
  let consecutiveCaptureFailures = 0;
5951
+ let cachedPage = null;
5790
5952
  if (!pushOrTapMicMode && !localParticipant.isMicrophoneEnabled) {
5791
5953
  localParticipant.setMicrophoneEnabled(true).catch((error) => console.error("Failed to enable microphone:", error));
5792
5954
  }
@@ -5802,14 +5964,15 @@ function DomCapture({ pushOrTapMicMode = false }) {
5802
5964
  for (const track of canvasStream.getTracks())
5803
5965
  track.stop();
5804
5966
  });
5805
- const tickSnapshot = async () => {
5967
+ const refreshPageAndComposite = async () => {
5806
5968
  if (cancelled || snapshotInFlight || pausedRef.current)
5807
5969
  return;
5808
5970
  snapshotInFlight = true;
5809
5971
  try {
5810
- await paintViewportSnapshot(canvas, ctx, () => !cancelled && !pausedRef.current);
5811
- if (cancelled)
5972
+ cachedPage = await rasterizeViewportToPageLayer(canvas.width, canvas.height);
5973
+ if (cancelled || pausedRef.current)
5812
5974
  return;
5975
+ compositePageAndCursorOverlay(ctx, canvas, cachedPage, brandRef.current);
5813
5976
  consecutiveCaptureFailures = 0;
5814
5977
  } catch {
5815
5978
  if (cancelled)
@@ -5822,6 +5985,37 @@ function DomCapture({ pushOrTapMicMode = false }) {
5822
5985
  snapshotInFlight = false;
5823
5986
  }
5824
5987
  };
5988
+ let overlayAnimationFrame = null;
5989
+ const animateOverlayOverCachedPage = () => {
5990
+ if (cancelled)
5991
+ return;
5992
+ if (!pausedRef.current)
5993
+ compositePageAndCursorOverlay(ctx, canvas, cachedPage, brandRef.current);
5994
+ if (cursorTargetElapsedMs() >= RING_INK_IN_DURATION_MS) {
5995
+ overlayAnimationFrame = null;
5996
+ return;
5997
+ }
5998
+ overlayAnimationFrame = requestAnimationFrame(animateOverlayOverCachedPage);
5999
+ };
6000
+ const startOverlayAnimation = () => {
6001
+ if (overlayAnimationFrame === null) {
6002
+ overlayAnimationFrame = requestAnimationFrame(animateOverlayOverCachedPage);
6003
+ }
6004
+ };
6005
+ const stopOverlayAnimation = () => {
6006
+ if (overlayAnimationFrame !== null) {
6007
+ cancelAnimationFrame(overlayAnimationFrame);
6008
+ overlayAnimationFrame = null;
6009
+ }
6010
+ if (!pausedRef.current)
6011
+ compositePageAndCursorOverlay(ctx, canvas, cachedPage, brandRef.current);
6012
+ };
6013
+ const cleanupCursorSubscription = subscribeCursorTarget((target) => {
6014
+ if (target)
6015
+ startOverlayAnimation();
6016
+ else
6017
+ stopOverlayAnimation();
6018
+ });
5825
6019
  const tickA11yPublish = async () => {
5826
6020
  if (cancelled || pausedRef.current)
5827
6021
  return;
@@ -5849,19 +6043,19 @@ function DomCapture({ pushOrTapMicMode = false }) {
5849
6043
  const cleanupDomEventListeners = installDomEventListeners(localParticipant, {
5850
6044
  onTriggerEvent: () => {
5851
6045
  tickA11yPublish();
5852
- tickSnapshot();
6046
+ refreshPageAndComposite();
5853
6047
  }
5854
6048
  });
5855
6049
  const cleanupSnapshotRequest = onSnapshotRequest(() => {
5856
6050
  tickA11yPublish();
5857
- tickSnapshot();
6051
+ refreshPageAndComposite();
5858
6052
  });
5859
6053
  let snapshotTimer = null;
5860
6054
  const scheduleNextSnapshot = (delay) => {
5861
6055
  if (cancelled)
5862
6056
  return;
5863
6057
  snapshotTimer = setTimeout(async () => {
5864
- await tickSnapshot();
6058
+ await refreshPageAndComposite();
5865
6059
  scheduleNextSnapshot(SNAPSHOT_INTERVAL_MS);
5866
6060
  }, delay);
5867
6061
  };
@@ -5875,7 +6069,7 @@ function DomCapture({ pushOrTapMicMode = false }) {
5875
6069
  resumeCaptureRef.current = () => {
5876
6070
  pausedRef.current = false;
5877
6071
  tickA11yPublish();
5878
- tickSnapshot();
6072
+ refreshPageAndComposite();
5879
6073
  };
5880
6074
  return () => {
5881
6075
  cancelled = true;
@@ -5885,6 +6079,9 @@ function DomCapture({ pushOrTapMicMode = false }) {
5885
6079
  if (snapshotTimer)
5886
6080
  clearTimeout(snapshotTimer);
5887
6081
  clearInterval(a11yPublishTimer);
6082
+ if (overlayAnimationFrame !== null)
6083
+ cancelAnimationFrame(overlayAnimationFrame);
6084
+ cleanupCursorSubscription();
5888
6085
  cleanupDomEventListeners();
5889
6086
  cleanupSnapshotRequest();
5890
6087
  unpublishAndStopTrack(localParticipant, videoTrack);
@@ -6011,7 +6208,7 @@ function HighlightOverlay() {
6011
6208
  }
6012
6209
 
6013
6210
  // src/components/MinimizedBubble.tsx
6014
- import { useContext as useContext9, useEffect as useEffect25, useRef as useRef20, useState as useState18 } from "react";
6211
+ import { useContext as useContext10, useEffect as useEffect25, useRef as useRef20, useState as useState18 } from "react";
6015
6212
 
6016
6213
  // src/hooks/useLauncherDrag.ts
6017
6214
  import { useEffect as useEffect24, useRef as useRef19 } from "react";
@@ -6381,7 +6578,7 @@ function MinimizedBubble({
6381
6578
  welcomeDismissed,
6382
6579
  onDismissWelcome
6383
6580
  }) {
6384
- const ctx = useContext9(LiveAgentContext);
6581
+ const ctx = useContext10(LiveAgentContext);
6385
6582
  if (!ctx) {
6386
6583
  throw new Error("MinimizedBubble must be used within a <LiveAgent> provider");
6387
6584
  }
@@ -6877,7 +7074,7 @@ function PageActionHandler() {
6877
7074
  }
6878
7075
 
6879
7076
  // src/components/SessionControlBar.tsx
6880
- import { useCallback as useCallback20, useContext as useContext10, useEffect as useEffect30, useState as useState21 } from "react";
7077
+ import { useCallback as useCallback20, useContext as useContext11, useEffect as useEffect30, useState as useState21 } from "react";
6881
7078
 
6882
7079
  // src/hooks/useActionControl.ts
6883
7080
  import { useLocalParticipant as useLocalParticipant6 } from "@livekit/components-react/hooks";
@@ -7318,7 +7515,7 @@ function SessionControlBar() {
7318
7515
  variant,
7319
7516
  agentControls
7320
7517
  } = useLiveAgent();
7321
- const internalCtx = useContext10(LiveAgentContext);
7518
+ const internalCtx = useContext11(LiveAgentContext);
7322
7519
  if (!internalCtx) {
7323
7520
  throw new Error("SessionControlBar must be used within a <LiveAgent> provider");
7324
7521
  }
@@ -7626,27 +7823,6 @@ function SessionControlBar() {
7626
7823
  // src/components/ShadowHost.tsx
7627
7824
  import { useLayoutEffect as useLayoutEffect2, useRef as useRef25, useState as useState22 } from "react";
7628
7825
  import { createPortal } from "react-dom";
7629
-
7630
- // src/lib/brandForeground.ts
7631
- var DARK_FOREGROUND = "#2d2b3d";
7632
- var LIGHT_FOREGROUND = "#ffffff";
7633
- var LUMINANCE_THRESHOLD = 0.179;
7634
- var HEX_COLOR = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
7635
- function isHexColor(value) {
7636
- return HEX_COLOR.test(value);
7637
- }
7638
- function linearChannel(hex, offset) {
7639
- const channel = Number.parseInt(hex.slice(offset, offset + 2), 16) / 255;
7640
- return channel <= 0.03928 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
7641
- }
7642
- function brandForeground(accentColor) {
7643
- const hex = accentColor.replace("#", "");
7644
- const fullHex = hex.length === 3 ? hex.split("").map((char) => char + char).join("") : hex;
7645
- const luminance = 0.2126 * linearChannel(fullHex, 0) + 0.7152 * linearChannel(fullHex, 2) + 0.0722 * linearChannel(fullHex, 4);
7646
- return luminance > LUMINANCE_THRESHOLD ? DARK_FOREGROUND : LIGHT_FOREGROUND;
7647
- }
7648
-
7649
- // src/components/ShadowHost.tsx
7650
7826
  import { jsx as jsx25, Fragment as Fragment3 } from "react/jsx-runtime";
7651
7827
  var PROPERTIES_STYLE_ID = "skippr-tw-properties";
7652
7828
  var PROPERTY_RULE = /@property\s+[^{]+\{[^}]*\}/g;
@@ -7698,14 +7874,14 @@ function ShadowHost({
7698
7874
  }
7699
7875
 
7700
7876
  // src/components/Sidebar.tsx
7701
- import { useContext as useContext17, useEffect as useEffect36 } from "react";
7877
+ import { useContext as useContext18, useEffect as useEffect36 } from "react";
7702
7878
 
7703
7879
  // src/hooks/useCombinedMessages.ts
7704
- import { useContext as useContext11, useMemo as useMemo6 } from "react";
7880
+ import { useContext as useContext12, useMemo as useMemo7 } from "react";
7705
7881
 
7706
7882
  // src/hooks/useChatMessages.ts
7707
7883
  import { useChat, useLocalParticipant as useLocalParticipant9 } from "@livekit/components-react/hooks";
7708
- import { useMemo as useMemo4 } from "react";
7884
+ import { useMemo as useMemo5 } from "react";
7709
7885
 
7710
7886
  // src/lib/filterSystemMessages.ts
7711
7887
  var SYSTEM_MESSAGE_PATTERN = /^\[\w+\]$/;
@@ -7718,7 +7894,7 @@ function useChatMessages() {
7718
7894
  const { chatMessages: rawMessages, send, isSending } = useChat();
7719
7895
  const { localParticipant } = useLocalParticipant9();
7720
7896
  const localIdentity = localParticipant.identity;
7721
- const chatMessages = useMemo4(() => {
7897
+ const chatMessages = useMemo5(() => {
7722
7898
  const sortedMessages = rawMessages.map((msg) => ({
7723
7899
  id: msg.id,
7724
7900
  role: msg.from?.identity === localIdentity ? "user" : "assistant",
@@ -7733,7 +7909,7 @@ function useChatMessages() {
7733
7909
 
7734
7910
  // src/hooks/useStreamingTranscript.ts
7735
7911
  import { useLocalParticipant as useLocalParticipant10 } from "@livekit/components-react/hooks";
7736
- import { useMemo as useMemo5, useRef as useRef26 } from "react";
7912
+ import { useMemo as useMemo6, useRef as useRef26 } from "react";
7737
7913
  function useStreamingTranscript() {
7738
7914
  const transcriptions = useSharedTranscriptions();
7739
7915
  const { localParticipant } = useLocalParticipant10();
@@ -7745,7 +7921,7 @@ function useStreamingTranscript() {
7745
7921
  priorUserStreamIdsRef.current = new Set(transcriptions.filter((stream) => stream.participantInfo.identity === localIdentity).map((stream) => stream.streamInfo.id));
7746
7922
  }
7747
7923
  wasCapturingSpeechRef.current = isCapturingSpeech;
7748
- const transcriptMessages = useMemo5(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).filter((stream) => {
7924
+ const transcriptMessages = useMemo6(() => filterSystemMessages(transcriptions.filter((stream) => stream.text.trim().length > 0).filter((stream) => {
7749
7925
  if (!isCapturingSpeech)
7750
7926
  return true;
7751
7927
  const isUser = stream.participantInfo.identity === localIdentity;
@@ -7783,15 +7959,15 @@ function useCombinedMessages() {
7783
7959
  const { transcriptMessages } = useStreamingTranscript();
7784
7960
  const { chatMessages, sendChatMessage, isSendingChat } = useChatMessages();
7785
7961
  const { state: agentState } = useAgentVoiceState();
7786
- const historyMessages = useContext11(LiveAgentContext)?.historyMessages ?? [];
7787
- const liveMessages = useMemo6(() => {
7962
+ const historyMessages = useContext12(LiveAgentContext)?.historyMessages ?? [];
7963
+ const liveMessages = useMemo7(() => {
7788
7964
  if (chatMessages.length === 0)
7789
7965
  return transcriptMessages;
7790
7966
  if (transcriptMessages.length === 0)
7791
7967
  return chatMessages;
7792
7968
  return mergeChatsIntoTranscripts(transcriptMessages, chatMessages);
7793
7969
  }, [transcriptMessages, chatMessages]);
7794
- const allMessages = useMemo6(() => {
7970
+ const allMessages = useMemo7(() => {
7795
7971
  if (historyMessages.length === 0)
7796
7972
  return liveMessages;
7797
7973
  const seenIds = new Set(liveMessages.map((message) => message.id));
@@ -7831,7 +8007,7 @@ function useLinkCard() {
7831
8007
  }
7832
8008
 
7833
8009
  // src/hooks/usePhaseUpdates.ts
7834
- import { useCallback as useCallback22, useContext as useContext12, useEffect as useEffect31 } from "react";
8010
+ import { useCallback as useCallback22, useContext as useContext13, useEffect as useEffect31 } from "react";
7835
8011
  function parsePhases(json) {
7836
8012
  try {
7837
8013
  const data = JSON.parse(json);
@@ -7844,7 +8020,7 @@ function parsePhases(json) {
7844
8020
  function usePhaseUpdates() {
7845
8021
  const parse = useCallback22(parsePhases, []);
7846
8022
  const livePhases = useAgentState("phases", parse, []);
7847
- const ctx = useContext12(LiveAgentContext);
8023
+ const ctx = useContext13(LiveAgentContext);
7848
8024
  useEffect31(() => {
7849
8025
  if (livePhases.length > 0)
7850
8026
  ctx?.setPhasesSnapshot(livePhases);
@@ -7889,14 +8065,14 @@ function useSessionRemaining() {
7889
8065
  }
7890
8066
 
7891
8067
  // src/components/ChatHeader.tsx
7892
- import { useContext as useContext14, useRef as useRef29 } from "react";
8068
+ import { useContext as useContext15, useRef as useRef29 } from "react";
7893
8069
 
7894
8070
  // src/hooks/useTextMode.ts
7895
8071
  import { useLocalParticipant as useLocalParticipant11 } from "@livekit/components-react/hooks";
7896
- import { useCallback as useCallback23, useContext as useContext13, useRef as useRef28 } from "react";
8072
+ import { useCallback as useCallback23, useContext as useContext14, useRef as useRef28 } from "react";
7897
8073
  var textEncoder6 = new TextEncoder;
7898
8074
  function useTextMode() {
7899
- const ctx = useContext13(LiveAgentContext);
8075
+ const ctx = useContext14(LiveAgentContext);
7900
8076
  if (!ctx) {
7901
8077
  throw new Error("useTextMode must be used within a <LiveAgent> provider");
7902
8078
  }
@@ -7929,7 +8105,7 @@ import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
7929
8105
  var TABS_PILL_BASE = "skippr:cursor-pointer skippr:rounded-full skippr:border-none skippr:bg-transparent skippr:px-[9px] skippr:py-[3px] skippr:text-[11.5px] skippr:font-semibold skippr:text-[#8b877f] skippr:transition-colors";
7930
8106
  var TABS_PILL_ACTIVE = "skippr:bg-white skippr:text-[#2D2D3F] skippr:shadow-[0_1px_2px_rgba(45,45,63,0.08)]";
7931
8107
  function ChatHeader() {
7932
- const ctx = useContext14(LiveAgentContext);
8108
+ const ctx = useContext15(LiveAgentContext);
7933
8109
  if (!ctx) {
7934
8110
  throw new Error("ChatHeader must be used within a <LiveAgent> provider");
7935
8111
  }
@@ -8060,11 +8236,11 @@ function LoadingDots({ label }) {
8060
8236
  import { useCallback as useCallback25, useLayoutEffect as useLayoutEffect3, useRef as useRef32, useState as useState27 } from "react";
8061
8237
 
8062
8238
  // src/components/ChatInput.tsx
8063
- import { useContext as useContext15, useEffect as useEffect33, useRef as useRef30, useState as useState24 } from "react";
8239
+ import { useContext as useContext16, useEffect as useEffect33, useRef as useRef30, useState as useState24 } from "react";
8064
8240
  import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
8065
8241
  var MAX_INPUT_HEIGHT = 60;
8066
8242
  function ChatInput({ sendChatMessage, isSendingChat, autoFocus = false }) {
8067
- const ctx = useContext15(LiveAgentContext);
8243
+ const ctx = useContext16(LiveAgentContext);
8068
8244
  const reportChatActivity = ctx?.reportChatActivity;
8069
8245
  const isPanelOpen = ctx?.isPanelOpen;
8070
8246
  const [localDraft, setLocalDraft] = useState24("");
@@ -8517,10 +8693,10 @@ function MessageList({
8517
8693
  }
8518
8694
 
8519
8695
  // src/components/SessionAgenda.tsx
8520
- import { useContext as useContext16 } from "react";
8696
+ import { useContext as useContext17 } from "react";
8521
8697
  import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
8522
8698
  function SessionAgenda({ phases, hasStarted }) {
8523
- const ctx = useContext16(LiveAgentContext);
8699
+ const ctx = useContext17(LiveAgentContext);
8524
8700
  const title = ctx?.activeModule?.name ?? "Agenda";
8525
8701
  if (phases.length === 0 || !hasStarted) {
8526
8702
  return /* @__PURE__ */ jsx34("div", {
@@ -8649,7 +8825,7 @@ function Sidebar({
8649
8825
  agentMode,
8650
8826
  displayModules
8651
8827
  } = useLiveAgent();
8652
- const internalCtx = useContext17(LiveAgentContext);
8828
+ const internalCtx = useContext18(LiveAgentContext);
8653
8829
  const defaultAgentId = internalCtx?.defaultAgentId ?? null;
8654
8830
  const startDefaultSession = internalCtx?.startDefaultSession ?? (() => {});
8655
8831
  const talkDisabled = !(internalCtx?.hasResolvedModules ?? false);
@@ -8930,7 +9106,7 @@ function ConnectedBody({
8930
9106
  }
8931
9107
 
8932
9108
  // src/components/SidebarTrigger.tsx
8933
- import { useContext as useContext18 } from "react";
9109
+ import { useContext as useContext19 } from "react";
8934
9110
 
8935
9111
  // src/components/Logo.tsx
8936
9112
  import { jsx as jsx37, jsxs as jsxs31 } from "react/jsx-runtime";
@@ -8982,7 +9158,7 @@ function SidebarTrigger() {
8982
9158
  isStarting,
8983
9159
  isPausing
8984
9160
  } = useLiveAgent();
8985
- const internalCtx = useContext18(LiveAgentContext);
9161
+ const internalCtx = useContext19(LiveAgentContext);
8986
9162
  const launcherBottomPx = internalCtx?.launcherBottomPx ?? DEFAULT_LAUNCHER_BOTTOM_PX;
8987
9163
  const popsDown = internalCtx?.popsDown ?? false;
8988
9164
  const logoUrl = internalCtx?.appearance?.logoUrl ?? null;
@@ -9009,15 +9185,15 @@ function SidebarTrigger() {
9009
9185
  }
9010
9186
 
9011
9187
  // src/components/VoiceBar.tsx
9012
- import { useCallback as useCallback31, useContext as useContext23, useState as useState38 } from "react";
9188
+ import { useCallback as useCallback31, useContext as useContext24, useState as useState38 } from "react";
9013
9189
 
9014
9190
  // src/hooks/useBarNotifications.ts
9015
- import { useContext as useContext20 } from "react";
9191
+ import { useContext as useContext21 } from "react";
9016
9192
 
9017
9193
  // src/hooks/useAdoptionTips.ts
9018
- import { useContext as useContext19 } from "react";
9194
+ import { useContext as useContext20 } from "react";
9019
9195
  function useAdoptionTips() {
9020
- const ctx = useContext19(LiveAgentContext);
9196
+ const ctx = useContext20(LiveAgentContext);
9021
9197
  if (!ctx) {
9022
9198
  throw new Error("useAdoptionTips must be used within a <LiveAgent> provider");
9023
9199
  }
@@ -9039,7 +9215,7 @@ function useAdoptionTips() {
9039
9215
 
9040
9216
  // src/hooks/useBarNotifications.ts
9041
9217
  function useBarNotifications() {
9042
- const ctx = useContext20(LiveAgentContext);
9218
+ const ctx = useContext21(LiveAgentContext);
9043
9219
  if (!ctx) {
9044
9220
  throw new Error("useBarNotifications must be used within a <LiveAgent> provider");
9045
9221
  }
@@ -9082,7 +9258,7 @@ function useBarNotifications() {
9082
9258
 
9083
9259
  // src/components/VoiceBarFrame.tsx
9084
9260
  import { useIsSpeaking, useLocalParticipant as useLocalParticipant13 } from "@livekit/components-react/hooks";
9085
- import { useCallback as useCallback29, useContext as useContext22, useEffect as useEffect46, useRef as useRef39, useState as useState35 } from "react";
9261
+ import { useCallback as useCallback29, useContext as useContext23, useEffect as useEffect46, useRef as useRef39, useState as useState35 } from "react";
9086
9262
 
9087
9263
  // src/hooks/useHighlightControl.ts
9088
9264
  import { useLocalParticipant as useLocalParticipant12 } from "@livekit/components-react/hooks";
@@ -9356,7 +9532,7 @@ function useVoiceBarDrag(containerRef, onDrop, followRefs, reportDragging) {
9356
9532
  }
9357
9533
 
9358
9534
  // src/components/VoiceBarChatLine.tsx
9359
- import { useCallback as useCallback27, useContext as useContext21, useEffect as useEffect42, useRef as useRef36, useState as useState31 } from "react";
9535
+ import { useCallback as useCallback27, useContext as useContext22, useEffect as useEffect42, useRef as useRef36, useState as useState31 } from "react";
9360
9536
  import { jsx as jsx39, jsxs as jsxs32 } from "react/jsx-runtime";
9361
9537
  var IDLE_HIDE_MS = 6000;
9362
9538
  function VoiceBarChatLine({
@@ -9366,7 +9542,7 @@ function VoiceBarChatLine({
9366
9542
  embedded = false
9367
9543
  }) {
9368
9544
  const { sendChatMessage, isSendingChat } = useChatMessages();
9369
- const ctx = useContext21(LiveAgentContext);
9545
+ const ctx = useContext22(LiveAgentContext);
9370
9546
  const reportChatActivity = ctx?.reportChatActivity;
9371
9547
  const [localDraft, setLocalDraft] = useState31("");
9372
9548
  const text = ctx?.chatDraft ?? localDraft;
@@ -10001,7 +10177,7 @@ function VoiceBarFrame({
10001
10177
  onDrop,
10002
10178
  setDragging
10003
10179
  }) {
10004
- const ctx = useContext22(LiveAgentContext);
10180
+ const ctx = useContext23(LiveAgentContext);
10005
10181
  if (!ctx) {
10006
10182
  throw new Error("VoiceBarFrame must be used within a <LiveAgent> provider");
10007
10183
  }
@@ -11013,7 +11189,7 @@ function VoiceBarLauncher({
11013
11189
  // src/components/VoiceBar.tsx
11014
11190
  import { jsx as jsx46 } from "react/jsx-runtime";
11015
11191
  function VoiceBar({ revealed }) {
11016
- const ctx = useContext23(LiveAgentContext);
11192
+ const ctx = useContext24(LiveAgentContext);
11017
11193
  if (!ctx) {
11018
11194
  throw new Error("VoiceBar must be used within a <LiveAgent> provider");
11019
11195
  }
@@ -11263,7 +11439,7 @@ function LiveAgent(props) {
11263
11439
  const agentMode = sessionAgentMode ?? operatingModule?.mode ?? null;
11264
11440
  const shouldAnimateCursor = operatingModule?.uiPreferences?.animateCursor ?? animateAgentCursor;
11265
11441
  const buddyDefaultMissing = buddyActive && hasResolvedModules && !modulesError && !defaultModule && !isSessionLive;
11266
- const resumableSession = useMemo7(() => getResumableSession(operatingModule), [operatingModule]);
11442
+ const resumableSession = useMemo8(() => getResumableSession(operatingModule), [operatingModule]);
11267
11443
  const resumeSession = useCallback32(async () => {
11268
11444
  if (!resumableSession)
11269
11445
  return;
@@ -11431,12 +11607,12 @@ function LiveAgent(props) {
11431
11607
  enabled: idleHistoryEnabled,
11432
11608
  authedFetch
11433
11609
  });
11434
- const idleHistory = useMemo7(() => ({
11610
+ const idleHistory = useMemo8(() => ({
11435
11611
  messages: idleHistoryMessages,
11436
11612
  phases: idleHistoryPhases,
11437
11613
  isLoading: isLoadingHistory
11438
11614
  }), [idleHistoryMessages, idleHistoryPhases, isLoadingHistory]);
11439
- const ctx = useMemo7(() => ({
11615
+ const ctx = useMemo8(() => ({
11440
11616
  connection,
11441
11617
  shouldConnect,
11442
11618
  isConnected,