@absolutejs/voice 0.0.22-beta.504 → 0.0.22-beta.506

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.
@@ -12738,6 +12738,383 @@ var VoiceWidget = ({
12738
12738
  ]
12739
12739
  }, undefined, true, undefined, this);
12740
12740
  };
12741
+ // src/react/VoiceCallPlayer.tsx
12742
+ import { useEffect as useEffect26, useMemo as useMemo2, useRef as useRef27, useState } from "react";
12743
+
12744
+ // src/client/callPlayer.ts
12745
+ var cloneState = (state) => ({
12746
+ ...state
12747
+ });
12748
+ var normalizeTranscriptTimes = (transcripts, baseEpoch) => {
12749
+ if (typeof baseEpoch !== "number") {
12750
+ return transcripts;
12751
+ }
12752
+ return transcripts.map((transcript) => {
12753
+ const adjusted = { ...transcript };
12754
+ if (typeof adjusted.startedAtMs === "number" && adjusted.startedAtMs >= baseEpoch) {
12755
+ adjusted.startedAtMs = adjusted.startedAtMs - baseEpoch;
12756
+ }
12757
+ if (typeof adjusted.endedAtMs === "number" && adjusted.endedAtMs >= baseEpoch) {
12758
+ adjusted.endedAtMs = adjusted.endedAtMs - baseEpoch;
12759
+ }
12760
+ return adjusted;
12761
+ });
12762
+ };
12763
+ var findActiveTranscript = (transcripts, positionMs) => {
12764
+ let candidate;
12765
+ for (let index = 0;index < transcripts.length; index += 1) {
12766
+ const transcript = transcripts[index];
12767
+ if (typeof transcript.startedAtMs !== "number")
12768
+ continue;
12769
+ if (transcript.startedAtMs > positionMs)
12770
+ break;
12771
+ if (typeof transcript.endedAtMs === "number" && transcript.endedAtMs < positionMs) {
12772
+ continue;
12773
+ }
12774
+ candidate = { id: transcript.id, index };
12775
+ }
12776
+ return candidate ?? {};
12777
+ };
12778
+ var createVoiceCallPlayer = (options = {}) => {
12779
+ let transcripts = normalizeTranscriptTimes(options.transcripts ?? [], options.recordingStartedAtEpochMs);
12780
+ let state = {
12781
+ audioUrl: options.audioUrl,
12782
+ buffered: 0,
12783
+ currentTimeMs: 0,
12784
+ durationMs: 0,
12785
+ isPlaying: false,
12786
+ isReady: false,
12787
+ playbackRate: options.initialPlaybackRate ?? 1
12788
+ };
12789
+ const listeners = new Set;
12790
+ const notify = () => {
12791
+ for (const listener of listeners)
12792
+ listener();
12793
+ };
12794
+ const update = (next) => {
12795
+ state = { ...state, ...next };
12796
+ notify();
12797
+ };
12798
+ const refreshActive = () => {
12799
+ const { id, index } = findActiveTranscript(transcripts, state.currentTimeMs);
12800
+ if (id !== state.activeTranscriptId || index !== state.activeTranscriptIndex) {
12801
+ state = {
12802
+ ...state,
12803
+ activeTranscriptId: id,
12804
+ activeTranscriptIndex: index
12805
+ };
12806
+ notify();
12807
+ }
12808
+ };
12809
+ return {
12810
+ getState: () => cloneState(state),
12811
+ pause: () => {
12812
+ if (!state.isPlaying)
12813
+ return;
12814
+ update({ isPlaying: false });
12815
+ },
12816
+ play: async () => {
12817
+ update({ isPlaying: true });
12818
+ },
12819
+ reset: () => {
12820
+ state = {
12821
+ audioUrl: state.audioUrl,
12822
+ buffered: 0,
12823
+ currentTimeMs: 0,
12824
+ durationMs: 0,
12825
+ isPlaying: false,
12826
+ isReady: false,
12827
+ playbackRate: 1
12828
+ };
12829
+ notify();
12830
+ },
12831
+ seekMs: (positionMs) => {
12832
+ const clamped = Math.max(0, Math.min(state.durationMs || Number.POSITIVE_INFINITY, positionMs));
12833
+ update({ currentTimeMs: clamped });
12834
+ refreshActive();
12835
+ },
12836
+ seekToTranscript: (transcriptId) => {
12837
+ const found = transcripts.find((t) => t.id === transcriptId);
12838
+ if (!found || typeof found.startedAtMs !== "number") {
12839
+ return;
12840
+ }
12841
+ update({ currentTimeMs: Math.max(0, found.startedAtMs) });
12842
+ refreshActive();
12843
+ },
12844
+ setAudioUrl: (url) => {
12845
+ update({ audioUrl: url, isReady: false });
12846
+ },
12847
+ setBuffered: (seconds) => {
12848
+ update({ buffered: Math.max(0, seconds) });
12849
+ },
12850
+ setDuration: (durationMs) => {
12851
+ update({ durationMs: Math.max(0, durationMs) });
12852
+ },
12853
+ setError: (error) => {
12854
+ update({ error });
12855
+ },
12856
+ setPlaybackRate: (rate) => {
12857
+ update({ playbackRate: Math.max(0.25, Math.min(4, rate)) });
12858
+ },
12859
+ setPlaying: (playing) => {
12860
+ if (playing === state.isPlaying)
12861
+ return;
12862
+ update({ isPlaying: playing });
12863
+ },
12864
+ setReady: (ready) => {
12865
+ update({ isReady: ready });
12866
+ },
12867
+ setTime: (positionMs) => {
12868
+ const next = Math.max(0, positionMs);
12869
+ if (next === state.currentTimeMs)
12870
+ return;
12871
+ update({ currentTimeMs: next });
12872
+ refreshActive();
12873
+ },
12874
+ setTranscripts: (next) => {
12875
+ transcripts = normalizeTranscriptTimes(next, options.recordingStartedAtEpochMs);
12876
+ refreshActive();
12877
+ },
12878
+ subscribe: (listener) => {
12879
+ listeners.add(listener);
12880
+ return () => {
12881
+ listeners.delete(listener);
12882
+ };
12883
+ },
12884
+ transcripts: () => transcripts
12885
+ };
12886
+ };
12887
+ var formatVoiceCallPlayerTimestamp = (ms) => {
12888
+ const seconds = Math.max(0, Math.floor(ms / 1000));
12889
+ const minutes = Math.floor(seconds / 60);
12890
+ const remaining = seconds % 60;
12891
+ return `${String(minutes).padStart(2, "0")}:${String(remaining).padStart(2, "0")}`;
12892
+ };
12893
+
12894
+ // src/react/VoiceCallPlayer.tsx
12895
+ import { jsxDEV as jsxDEV23 } from "react/jsx-dev-runtime";
12896
+ var VoiceCallPlayer = ({
12897
+ audioUrl,
12898
+ className,
12899
+ onError,
12900
+ player: playerProp,
12901
+ recordingStartedAtEpochMs,
12902
+ title = "Call replay",
12903
+ transcripts
12904
+ }) => {
12905
+ const player = useMemo2(() => playerProp ?? createVoiceCallPlayer({
12906
+ audioUrl,
12907
+ recordingStartedAtEpochMs,
12908
+ transcripts
12909
+ }), [playerProp, audioUrl, recordingStartedAtEpochMs, transcripts]);
12910
+ const [state, setState] = useState(() => player.getState());
12911
+ const audioRef = useRef27(null);
12912
+ const errorRef = useRef27(undefined);
12913
+ useEffect26(() => {
12914
+ const unsubscribe = player.subscribe(() => {
12915
+ setState(player.getState());
12916
+ });
12917
+ return () => {
12918
+ unsubscribe();
12919
+ };
12920
+ }, [player]);
12921
+ useEffect26(() => {
12922
+ player.setAudioUrl(audioUrl);
12923
+ }, [audioUrl, player]);
12924
+ useEffect26(() => {
12925
+ if (transcripts) {
12926
+ player.setTranscripts(transcripts);
12927
+ }
12928
+ }, [transcripts, player]);
12929
+ useEffect26(() => {
12930
+ if (state.error && state.error !== errorRef.current) {
12931
+ errorRef.current = state.error;
12932
+ onError?.(state.error);
12933
+ }
12934
+ }, [state.error, onError]);
12935
+ useEffect26(() => {
12936
+ const el = audioRef.current;
12937
+ if (!el)
12938
+ return;
12939
+ if (state.isPlaying && el.paused) {
12940
+ el.play().catch((err) => {
12941
+ player.setError(err instanceof Error ? err.message : String(err));
12942
+ player.setPlaying(false);
12943
+ });
12944
+ } else if (!state.isPlaying && !el.paused) {
12945
+ el.pause();
12946
+ }
12947
+ if (Math.abs(el.currentTime * 1000 - state.currentTimeMs) > 250) {
12948
+ el.currentTime = state.currentTimeMs / 1000;
12949
+ }
12950
+ el.playbackRate = state.playbackRate;
12951
+ }, [state.isPlaying, state.currentTimeMs, state.playbackRate, player]);
12952
+ const onTimeUpdate = () => {
12953
+ const el = audioRef.current;
12954
+ if (!el)
12955
+ return;
12956
+ player.setTime(el.currentTime * 1000);
12957
+ };
12958
+ const onLoadedMetadata = () => {
12959
+ const el = audioRef.current;
12960
+ if (!el)
12961
+ return;
12962
+ player.setDuration(el.duration * 1000);
12963
+ player.setReady(true);
12964
+ };
12965
+ const handleSeek = (ratio) => {
12966
+ player.seekMs(state.durationMs * ratio);
12967
+ };
12968
+ return /* @__PURE__ */ jsxDEV23("section", {
12969
+ "aria-label": "voice-call-player",
12970
+ className: className ?? "absolute-voice-call-player",
12971
+ style: {
12972
+ background: "#0f172a",
12973
+ borderRadius: 16,
12974
+ color: "#f8fafc",
12975
+ fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
12976
+ padding: 20
12977
+ },
12978
+ children: [
12979
+ /* @__PURE__ */ jsxDEV23("header", {
12980
+ style: {
12981
+ alignItems: "center",
12982
+ display: "flex",
12983
+ gap: 12,
12984
+ marginBottom: 12
12985
+ },
12986
+ children: [
12987
+ /* @__PURE__ */ jsxDEV23("strong", {
12988
+ style: { fontSize: 16 },
12989
+ children: title
12990
+ }, undefined, false, undefined, this),
12991
+ /* @__PURE__ */ jsxDEV23("span", {
12992
+ style: { fontSize: 13, marginLeft: "auto", opacity: 0.7 },
12993
+ children: [
12994
+ formatVoiceCallPlayerTimestamp(state.currentTimeMs),
12995
+ " /",
12996
+ " ",
12997
+ formatVoiceCallPlayerTimestamp(state.durationMs)
12998
+ ]
12999
+ }, undefined, true, undefined, this)
13000
+ ]
13001
+ }, undefined, true, undefined, this),
13002
+ /* @__PURE__ */ jsxDEV23("audio", {
13003
+ onEnded: () => player.setPlaying(false),
13004
+ onError: () => player.setError("Audio playback error"),
13005
+ onLoadedMetadata,
13006
+ onPause: () => player.setPlaying(false),
13007
+ onPlay: () => player.setPlaying(true),
13008
+ onTimeUpdate,
13009
+ preload: "metadata",
13010
+ ref: audioRef,
13011
+ src: state.audioUrl,
13012
+ style: { display: "none" }
13013
+ }, undefined, false, undefined, this),
13014
+ /* @__PURE__ */ jsxDEV23("div", {
13015
+ style: {
13016
+ alignItems: "center",
13017
+ display: "flex",
13018
+ gap: 12,
13019
+ marginBottom: 14
13020
+ },
13021
+ children: [
13022
+ /* @__PURE__ */ jsxDEV23("button", {
13023
+ "aria-label": state.isPlaying ? "Pause" : "Play",
13024
+ onClick: () => {
13025
+ if (state.isPlaying) {
13026
+ player.pause();
13027
+ } else {
13028
+ player.play();
13029
+ }
13030
+ },
13031
+ style: {
13032
+ background: "#3b82f6",
13033
+ border: "none",
13034
+ borderRadius: 12,
13035
+ color: "#f8fafc",
13036
+ cursor: "pointer",
13037
+ fontSize: 14,
13038
+ fontWeight: 500,
13039
+ padding: "8px 14px"
13040
+ },
13041
+ type: "button",
13042
+ children: state.isPlaying ? "Pause" : "Play"
13043
+ }, undefined, false, undefined, this),
13044
+ /* @__PURE__ */ jsxDEV23("input", {
13045
+ "aria-label": "seek",
13046
+ max: 1,
13047
+ min: 0,
13048
+ onChange: (event) => handleSeek(Number(event.target.value)),
13049
+ step: 0.001,
13050
+ style: { flex: 1 },
13051
+ type: "range",
13052
+ value: state.durationMs > 0 ? state.currentTimeMs / state.durationMs : 0
13053
+ }, undefined, false, undefined, this),
13054
+ /* @__PURE__ */ jsxDEV23("select", {
13055
+ "aria-label": "playback rate",
13056
+ onChange: (event) => player.setPlaybackRate(Number(event.target.value)),
13057
+ style: {
13058
+ background: "rgba(255,255,255,0.08)",
13059
+ border: "1px solid rgba(255,255,255,0.18)",
13060
+ borderRadius: 8,
13061
+ color: "#f8fafc",
13062
+ fontSize: 13,
13063
+ padding: "4px 8px"
13064
+ },
13065
+ value: state.playbackRate,
13066
+ children: [0.5, 0.75, 1, 1.25, 1.5, 2].map((rate) => /* @__PURE__ */ jsxDEV23("option", {
13067
+ value: rate,
13068
+ children: [
13069
+ rate,
13070
+ "\xD7"
13071
+ ]
13072
+ }, rate, true, undefined, this))
13073
+ }, undefined, false, undefined, this)
13074
+ ]
13075
+ }, undefined, true, undefined, this),
13076
+ /* @__PURE__ */ jsxDEV23("ol", {
13077
+ style: {
13078
+ display: "flex",
13079
+ flexDirection: "column",
13080
+ gap: 6,
13081
+ listStyle: "none",
13082
+ margin: 0,
13083
+ maxHeight: 280,
13084
+ overflowY: "auto",
13085
+ padding: 0
13086
+ },
13087
+ children: player.transcripts().map((transcript) => {
13088
+ const active = transcript.id === state.activeTranscriptId;
13089
+ const startedAt = transcript.startedAtMs ?? 0;
13090
+ return /* @__PURE__ */ jsxDEV23("li", {
13091
+ style: {
13092
+ background: active ? "rgba(59,130,246,0.18)" : "transparent",
13093
+ borderRadius: 8,
13094
+ cursor: "pointer",
13095
+ fontSize: 13,
13096
+ padding: "8px 12px"
13097
+ },
13098
+ onClick: () => player.seekToTranscript(transcript.id),
13099
+ children: [
13100
+ /* @__PURE__ */ jsxDEV23("div", {
13101
+ style: { color: "#cbd5e1", fontSize: 12 },
13102
+ children: formatVoiceCallPlayerTimestamp(startedAt)
13103
+ }, undefined, false, undefined, this),
13104
+ /* @__PURE__ */ jsxDEV23("div", {
13105
+ children: transcript.text
13106
+ }, undefined, false, undefined, this)
13107
+ ]
13108
+ }, transcript.id, true, undefined, this);
13109
+ })
13110
+ }, undefined, false, undefined, this),
13111
+ state.error ? /* @__PURE__ */ jsxDEV23("p", {
13112
+ style: { color: "#ef4444", fontSize: 12, marginTop: 12 },
13113
+ children: state.error
13114
+ }, undefined, false, undefined, this) : null
13115
+ ]
13116
+ }, undefined, true, undefined, this);
13117
+ };
12741
13118
  // src/client/costDashboard.ts
12742
13119
  var padTwo = (value) => String(value).padStart(2, "0");
12743
13120
  var formatBucketKey = (epochMs, bucketBy) => {
@@ -12819,7 +13196,7 @@ var buildVoiceCostDashboardReport = (options) => {
12819
13196
  };
12820
13197
 
12821
13198
  // src/react/VoiceCostDashboard.tsx
12822
- import { jsxDEV as jsxDEV23 } from "react/jsx-dev-runtime";
13199
+ import { jsxDEV as jsxDEV24 } from "react/jsx-dev-runtime";
12823
13200
  var formatUsd = (value, currency = "USD") => new Intl.NumberFormat("en-US", {
12824
13201
  currency,
12825
13202
  maximumFractionDigits: 4,
@@ -12827,38 +13204,38 @@ var formatUsd = (value, currency = "USD") => new Intl.NumberFormat("en-US", {
12827
13204
  style: "currency"
12828
13205
  }).format(value);
12829
13206
  var formatInteger = (value) => new Intl.NumberFormat("en-US").format(value);
12830
- var renderRow = (bucket, currency, isTotal) => /* @__PURE__ */ jsxDEV23("tr", {
13207
+ var renderRow = (bucket, currency, isTotal) => /* @__PURE__ */ jsxDEV24("tr", {
12831
13208
  "data-bucket-key": bucket.bucketKey,
12832
13209
  style: {
12833
13210
  borderTop: isTotal ? "2px solid rgba(255,255,255,0.15)" : undefined,
12834
13211
  fontWeight: isTotal ? 600 : undefined
12835
13212
  },
12836
13213
  children: [
12837
- /* @__PURE__ */ jsxDEV23("td", {
13214
+ /* @__PURE__ */ jsxDEV24("td", {
12838
13215
  style: { padding: "8px 12px" },
12839
13216
  children: bucket.bucketKey
12840
13217
  }, undefined, false, undefined, this),
12841
- /* @__PURE__ */ jsxDEV23("td", {
13218
+ /* @__PURE__ */ jsxDEV24("td", {
12842
13219
  style: { padding: "8px 12px", textAlign: "right" },
12843
13220
  children: formatInteger(bucket.callCount)
12844
13221
  }, undefined, false, undefined, this),
12845
- /* @__PURE__ */ jsxDEV23("td", {
13222
+ /* @__PURE__ */ jsxDEV24("td", {
12846
13223
  style: { padding: "8px 12px", textAlign: "right" },
12847
13224
  children: formatUsd(bucket.llmUsd, currency)
12848
13225
  }, undefined, false, undefined, this),
12849
- /* @__PURE__ */ jsxDEV23("td", {
13226
+ /* @__PURE__ */ jsxDEV24("td", {
12850
13227
  style: { padding: "8px 12px", textAlign: "right" },
12851
13228
  children: formatUsd(bucket.ttsUsd, currency)
12852
13229
  }, undefined, false, undefined, this),
12853
- /* @__PURE__ */ jsxDEV23("td", {
13230
+ /* @__PURE__ */ jsxDEV24("td", {
12854
13231
  style: { padding: "8px 12px", textAlign: "right" },
12855
13232
  children: formatUsd(bucket.sttUsd, currency)
12856
13233
  }, undefined, false, undefined, this),
12857
- /* @__PURE__ */ jsxDEV23("td", {
13234
+ /* @__PURE__ */ jsxDEV24("td", {
12858
13235
  style: { padding: "8px 12px", textAlign: "right" },
12859
13236
  children: formatUsd(bucket.telephonyUsd, currency)
12860
13237
  }, undefined, false, undefined, this),
12861
- /* @__PURE__ */ jsxDEV23("td", {
13238
+ /* @__PURE__ */ jsxDEV24("td", {
12862
13239
  style: { padding: "8px 12px", textAlign: "right" },
12863
13240
  children: formatUsd(bucket.totalUsd, currency)
12864
13241
  }, undefined, false, undefined, this)
@@ -12872,7 +13249,7 @@ var VoiceCostDashboard = ({
12872
13249
  ...options
12873
13250
  }) => {
12874
13251
  const report = buildVoiceCostDashboardReport(options);
12875
- return /* @__PURE__ */ jsxDEV23("section", {
13252
+ return /* @__PURE__ */ jsxDEV24("section", {
12876
13253
  "aria-label": "voice-cost-dashboard",
12877
13254
  className: className ?? "absolute-voice-cost-dashboard",
12878
13255
  style: {
@@ -12883,7 +13260,7 @@ var VoiceCostDashboard = ({
12883
13260
  padding: 20
12884
13261
  },
12885
13262
  children: [
12886
- /* @__PURE__ */ jsxDEV23("header", {
13263
+ /* @__PURE__ */ jsxDEV24("header", {
12887
13264
  style: {
12888
13265
  alignItems: "baseline",
12889
13266
  display: "flex",
@@ -12891,11 +13268,11 @@ var VoiceCostDashboard = ({
12891
13268
  marginBottom: 12
12892
13269
  },
12893
13270
  children: [
12894
- /* @__PURE__ */ jsxDEV23("strong", {
13271
+ /* @__PURE__ */ jsxDEV24("strong", {
12895
13272
  style: { fontSize: 16 },
12896
13273
  children: title
12897
13274
  }, undefined, false, undefined, this),
12898
- /* @__PURE__ */ jsxDEV23("span", {
13275
+ /* @__PURE__ */ jsxDEV24("span", {
12899
13276
  style: { fontSize: 13, opacity: 0.7 },
12900
13277
  children: [
12901
13278
  report.buckets.length,
@@ -12906,52 +13283,52 @@ var VoiceCostDashboard = ({
12906
13283
  }, undefined, true, undefined, this)
12907
13284
  ]
12908
13285
  }, undefined, true, undefined, this),
12909
- report.buckets.length === 0 ? /* @__PURE__ */ jsxDEV23("p", {
13286
+ report.buckets.length === 0 ? /* @__PURE__ */ jsxDEV24("p", {
12910
13287
  style: { fontSize: 13, opacity: 0.7 },
12911
13288
  children: emptyMessage
12912
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV23("table", {
13289
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV24("table", {
12913
13290
  style: {
12914
13291
  borderCollapse: "collapse",
12915
13292
  fontSize: 13,
12916
13293
  width: "100%"
12917
13294
  },
12918
13295
  children: [
12919
- /* @__PURE__ */ jsxDEV23("thead", {
12920
- children: /* @__PURE__ */ jsxDEV23("tr", {
13296
+ /* @__PURE__ */ jsxDEV24("thead", {
13297
+ children: /* @__PURE__ */ jsxDEV24("tr", {
12921
13298
  style: { opacity: 0.7, textAlign: "left" },
12922
13299
  children: [
12923
- /* @__PURE__ */ jsxDEV23("th", {
13300
+ /* @__PURE__ */ jsxDEV24("th", {
12924
13301
  style: { padding: "8px 12px" },
12925
13302
  children: "Bucket"
12926
13303
  }, undefined, false, undefined, this),
12927
- /* @__PURE__ */ jsxDEV23("th", {
13304
+ /* @__PURE__ */ jsxDEV24("th", {
12928
13305
  style: { padding: "8px 12px", textAlign: "right" },
12929
13306
  children: "Calls"
12930
13307
  }, undefined, false, undefined, this),
12931
- /* @__PURE__ */ jsxDEV23("th", {
13308
+ /* @__PURE__ */ jsxDEV24("th", {
12932
13309
  style: { padding: "8px 12px", textAlign: "right" },
12933
13310
  children: "LLM"
12934
13311
  }, undefined, false, undefined, this),
12935
- /* @__PURE__ */ jsxDEV23("th", {
13312
+ /* @__PURE__ */ jsxDEV24("th", {
12936
13313
  style: { padding: "8px 12px", textAlign: "right" },
12937
13314
  children: "TTS"
12938
13315
  }, undefined, false, undefined, this),
12939
- /* @__PURE__ */ jsxDEV23("th", {
13316
+ /* @__PURE__ */ jsxDEV24("th", {
12940
13317
  style: { padding: "8px 12px", textAlign: "right" },
12941
13318
  children: "STT"
12942
13319
  }, undefined, false, undefined, this),
12943
- /* @__PURE__ */ jsxDEV23("th", {
13320
+ /* @__PURE__ */ jsxDEV24("th", {
12944
13321
  style: { padding: "8px 12px", textAlign: "right" },
12945
13322
  children: "Tel."
12946
13323
  }, undefined, false, undefined, this),
12947
- /* @__PURE__ */ jsxDEV23("th", {
13324
+ /* @__PURE__ */ jsxDEV24("th", {
12948
13325
  style: { padding: "8px 12px", textAlign: "right" },
12949
13326
  children: "Total"
12950
13327
  }, undefined, false, undefined, this)
12951
13328
  ]
12952
13329
  }, undefined, true, undefined, this)
12953
13330
  }, undefined, false, undefined, this),
12954
- /* @__PURE__ */ jsxDEV23("tbody", {
13331
+ /* @__PURE__ */ jsxDEV24("tbody", {
12955
13332
  children: [
12956
13333
  report.buckets.map((bucket) => renderRow(bucket, currency, false)),
12957
13334
  renderRow(report.grandTotal, currency, true)
@@ -12963,7 +13340,7 @@ var VoiceCostDashboard = ({
12963
13340
  }, undefined, true, undefined, this);
12964
13341
  };
12965
13342
  // src/react/VoiceLiveCallViewer.tsx
12966
- import { useEffect as useEffect26, useMemo as useMemo2, useState } from "react";
13343
+ import { useEffect as useEffect27, useMemo as useMemo3, useState as useState2 } from "react";
12967
13344
 
12968
13345
  // src/client/liveCallViewer.ts
12969
13346
  var EVENT_BUFFER_LIMIT = 200;
@@ -13074,7 +13451,7 @@ var createLiveCallViewer = (options) => {
13074
13451
  };
13075
13452
 
13076
13453
  // src/react/VoiceLiveCallViewer.tsx
13077
- import { jsxDEV as jsxDEV24 } from "react/jsx-dev-runtime";
13454
+ import { jsxDEV as jsxDEV25 } from "react/jsx-dev-runtime";
13078
13455
  var CATEGORY_COLOR = {
13079
13456
  agent_audio: "#3b82f6",
13080
13457
  agent_text: "#3b82f6",
@@ -13094,9 +13471,9 @@ var VoiceLiveCallViewer = ({
13094
13471
  title,
13095
13472
  viewer: viewerProp
13096
13473
  }) => {
13097
- const viewer = useMemo2(() => viewerProp ?? createLiveCallViewer({ sessionId: sessionId ?? "live" }), [viewerProp, sessionId]);
13098
- const [state, setState] = useState(() => viewer.getState());
13099
- useEffect26(() => {
13474
+ const viewer = useMemo3(() => viewerProp ?? createLiveCallViewer({ sessionId: sessionId ?? "live" }), [viewerProp, sessionId]);
13475
+ const [state, setState] = useState2(() => viewer.getState());
13476
+ useEffect27(() => {
13100
13477
  const unsubscribe = viewer.subscribe(() => {
13101
13478
  setState(viewer.getState());
13102
13479
  });
@@ -13104,7 +13481,7 @@ var VoiceLiveCallViewer = ({
13104
13481
  unsubscribe();
13105
13482
  };
13106
13483
  }, [viewer]);
13107
- return /* @__PURE__ */ jsxDEV24("section", {
13484
+ return /* @__PURE__ */ jsxDEV25("section", {
13108
13485
  "aria-label": "voice-live-call-viewer",
13109
13486
  className: className ?? "absolute-voice-live-call-viewer",
13110
13487
  "data-agent-state": state.agentState,
@@ -13116,7 +13493,7 @@ var VoiceLiveCallViewer = ({
13116
13493
  padding: 20
13117
13494
  },
13118
13495
  children: [
13119
- /* @__PURE__ */ jsxDEV24("header", {
13496
+ /* @__PURE__ */ jsxDEV25("header", {
13120
13497
  style: {
13121
13498
  alignItems: "center",
13122
13499
  display: "flex",
@@ -13124,11 +13501,11 @@ var VoiceLiveCallViewer = ({
13124
13501
  marginBottom: 12
13125
13502
  },
13126
13503
  children: [
13127
- /* @__PURE__ */ jsxDEV24("strong", {
13504
+ /* @__PURE__ */ jsxDEV25("strong", {
13128
13505
  style: { fontSize: 16 },
13129
13506
  children: title ?? "Live call"
13130
13507
  }, undefined, false, undefined, this),
13131
- /* @__PURE__ */ jsxDEV24("span", {
13508
+ /* @__PURE__ */ jsxDEV25("span", {
13132
13509
  style: {
13133
13510
  background: "rgba(59,130,246,0.18)",
13134
13511
  borderRadius: 999,
@@ -13138,7 +13515,7 @@ var VoiceLiveCallViewer = ({
13138
13515
  },
13139
13516
  children: state.agentState
13140
13517
  }, undefined, false, undefined, this),
13141
- /* @__PURE__ */ jsxDEV24("span", {
13518
+ /* @__PURE__ */ jsxDEV25("span", {
13142
13519
  style: { fontSize: 13, marginLeft: "auto", opacity: 0.7 },
13143
13520
  children: [
13144
13521
  state.sessionId,
@@ -13148,7 +13525,7 @@ var VoiceLiveCallViewer = ({
13148
13525
  }, undefined, true, undefined, this)
13149
13526
  ]
13150
13527
  }, undefined, true, undefined, this),
13151
- state.partialTranscript ? /* @__PURE__ */ jsxDEV24("p", {
13528
+ state.partialTranscript ? /* @__PURE__ */ jsxDEV25("p", {
13152
13529
  style: {
13153
13530
  background: "rgba(16,185,129,0.12)",
13154
13531
  borderRadius: 12,
@@ -13163,7 +13540,7 @@ var VoiceLiveCallViewer = ({
13163
13540
  "\u201D"
13164
13541
  ]
13165
13542
  }, undefined, true, undefined, this) : null,
13166
- /* @__PURE__ */ jsxDEV24("ol", {
13543
+ /* @__PURE__ */ jsxDEV25("ol", {
13167
13544
  style: {
13168
13545
  display: "flex",
13169
13546
  flexDirection: "column",
@@ -13174,7 +13551,7 @@ var VoiceLiveCallViewer = ({
13174
13551
  overflowY: "auto",
13175
13552
  padding: 0
13176
13553
  },
13177
- children: state.events.map((event, index) => /* @__PURE__ */ jsxDEV24("li", {
13554
+ children: state.events.map((event, index) => /* @__PURE__ */ jsxDEV25("li", {
13178
13555
  style: {
13179
13556
  alignItems: "center",
13180
13557
  borderLeft: `3px solid ${CATEGORY_COLOR[event.kind] ?? "#94a3b8"}`,
@@ -13184,7 +13561,7 @@ var VoiceLiveCallViewer = ({
13184
13561
  paddingLeft: 12
13185
13562
  },
13186
13563
  children: [
13187
- /* @__PURE__ */ jsxDEV24("span", {
13564
+ /* @__PURE__ */ jsxDEV25("span", {
13188
13565
  style: {
13189
13566
  color: "#cbd5e1",
13190
13567
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
@@ -13193,11 +13570,11 @@ var VoiceLiveCallViewer = ({
13193
13570
  },
13194
13571
  children: formatRelative(event.at - (state.events[0]?.at ?? event.at))
13195
13572
  }, undefined, false, undefined, this),
13196
- /* @__PURE__ */ jsxDEV24("strong", {
13573
+ /* @__PURE__ */ jsxDEV25("strong", {
13197
13574
  style: { fontSize: 13 },
13198
13575
  children: event.title
13199
13576
  }, undefined, false, undefined, this),
13200
- event.detail ? /* @__PURE__ */ jsxDEV24("span", {
13577
+ event.detail ? /* @__PURE__ */ jsxDEV25("span", {
13201
13578
  style: { opacity: 0.85 },
13202
13579
  children: event.detail
13203
13580
  }, undefined, false, undefined, this) : null
@@ -13259,7 +13636,7 @@ var buildReplayTimelineReport = (input) => {
13259
13636
  };
13260
13637
 
13261
13638
  // src/react/VoiceReplayTimeline.tsx
13262
- import { jsxDEV as jsxDEV25 } from "react/jsx-dev-runtime";
13639
+ import { jsxDEV as jsxDEV26 } from "react/jsx-dev-runtime";
13263
13640
  var CATEGORY_COLOR2 = {
13264
13641
  agent: "#3b82f6",
13265
13642
  lifecycle: "#94a3b8",
@@ -13278,7 +13655,7 @@ var VoiceReplayTimeline = ({
13278
13655
  title
13279
13656
  }) => {
13280
13657
  const report = buildReplayTimelineReport({ artifact });
13281
- return /* @__PURE__ */ jsxDEV25("section", {
13658
+ return /* @__PURE__ */ jsxDEV26("section", {
13282
13659
  "aria-label": "voice-replay-timeline",
13283
13660
  className: className ?? "absolute-voice-replay-timeline",
13284
13661
  style: {
@@ -13289,7 +13666,7 @@ var VoiceReplayTimeline = ({
13289
13666
  padding: 20
13290
13667
  },
13291
13668
  children: [
13292
- /* @__PURE__ */ jsxDEV25("header", {
13669
+ /* @__PURE__ */ jsxDEV26("header", {
13293
13670
  style: {
13294
13671
  alignItems: "baseline",
13295
13672
  display: "flex",
@@ -13297,11 +13674,11 @@ var VoiceReplayTimeline = ({
13297
13674
  marginBottom: 12
13298
13675
  },
13299
13676
  children: [
13300
- /* @__PURE__ */ jsxDEV25("strong", {
13677
+ /* @__PURE__ */ jsxDEV26("strong", {
13301
13678
  style: { fontSize: 16 },
13302
13679
  children: title ?? report.metadata.title ?? "Replay"
13303
13680
  }, undefined, false, undefined, this),
13304
- /* @__PURE__ */ jsxDEV25("span", {
13681
+ /* @__PURE__ */ jsxDEV26("span", {
13305
13682
  style: { fontSize: 13, opacity: 0.7 },
13306
13683
  children: [
13307
13684
  report.events.length,
@@ -13317,10 +13694,10 @@ var VoiceReplayTimeline = ({
13317
13694
  }, undefined, true, undefined, this)
13318
13695
  ]
13319
13696
  }, undefined, true, undefined, this),
13320
- report.events.length === 0 ? /* @__PURE__ */ jsxDEV25("p", {
13697
+ report.events.length === 0 ? /* @__PURE__ */ jsxDEV26("p", {
13321
13698
  style: { fontSize: 13, opacity: 0.7 },
13322
13699
  children: "No timeline events."
13323
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV25("ol", {
13700
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV26("ol", {
13324
13701
  style: {
13325
13702
  display: "flex",
13326
13703
  flexDirection: "column",
@@ -13329,7 +13706,7 @@ var VoiceReplayTimeline = ({
13329
13706
  margin: 0,
13330
13707
  padding: 0
13331
13708
  },
13332
- children: report.events.map((event, index) => /* @__PURE__ */ jsxDEV25("li", {
13709
+ children: report.events.map((event, index) => /* @__PURE__ */ jsxDEV26("li", {
13333
13710
  style: {
13334
13711
  alignItems: "center",
13335
13712
  borderLeft: `3px solid ${CATEGORY_COLOR2[event.category]}`,
@@ -13339,7 +13716,7 @@ var VoiceReplayTimeline = ({
13339
13716
  paddingLeft: 12
13340
13717
  },
13341
13718
  children: [
13342
- /* @__PURE__ */ jsxDEV25("span", {
13719
+ /* @__PURE__ */ jsxDEV26("span", {
13343
13720
  style: {
13344
13721
  color: "#cbd5e1",
13345
13722
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
@@ -13348,11 +13725,11 @@ var VoiceReplayTimeline = ({
13348
13725
  },
13349
13726
  children: formatRelative2(event.at - report.startedAt)
13350
13727
  }, undefined, false, undefined, this),
13351
- /* @__PURE__ */ jsxDEV25("strong", {
13728
+ /* @__PURE__ */ jsxDEV26("strong", {
13352
13729
  style: { fontSize: 13 },
13353
13730
  children: event.label
13354
13731
  }, undefined, false, undefined, this),
13355
- event.detail ? /* @__PURE__ */ jsxDEV25("span", {
13732
+ event.detail ? /* @__PURE__ */ jsxDEV26("span", {
13356
13733
  style: { opacity: 0.85 },
13357
13734
  children: event.detail
13358
13735
  }, undefined, false, undefined, this) : null
@@ -13363,7 +13740,7 @@ var VoiceReplayTimeline = ({
13363
13740
  }, undefined, true, undefined, this);
13364
13741
  };
13365
13742
  // src/react/useVoiceWorkflowStatus.tsx
13366
- import { useEffect as useEffect27, useRef as useRef27, useSyncExternalStore as useSyncExternalStore26 } from "react";
13743
+ import { useEffect as useEffect28, useRef as useRef28, useSyncExternalStore as useSyncExternalStore26 } from "react";
13367
13744
 
13368
13745
  // src/client/workflowStatus.ts
13369
13746
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -13446,12 +13823,12 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
13446
13823
 
13447
13824
  // src/react/useVoiceWorkflowStatus.tsx
13448
13825
  var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
13449
- const storeRef = useRef27(null);
13826
+ const storeRef = useRef28(null);
13450
13827
  if (!storeRef.current) {
13451
13828
  storeRef.current = createVoiceWorkflowStatusStore(path, options);
13452
13829
  }
13453
13830
  const store = storeRef.current;
13454
- useEffect27(() => {
13831
+ useEffect28(() => {
13455
13832
  store.refresh().catch(() => {});
13456
13833
  return () => store.close();
13457
13834
  }, [store]);
@@ -13510,6 +13887,7 @@ export {
13510
13887
  VoiceLiveCallViewer,
13511
13888
  VoiceDeliveryRuntime,
13512
13889
  VoiceCostDashboard,
13890
+ VoiceCallPlayer,
13513
13891
  VoiceCallDebuggerLaunch,
13514
13892
  VoiceAgentSquadStatus
13515
13893
  };