@absolutejs/voice 0.0.22-beta.494 → 0.0.22-beta.495

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.
@@ -0,0 +1,77 @@
1
+ import { type PropType } from "vue";
2
+ import type { VoiceControllerOptions } from "../types";
3
+ export type VoiceWidgetTheme = {
4
+ accent?: string;
5
+ background?: string;
6
+ errorAccent?: string;
7
+ fontFamily?: string;
8
+ foreground?: string;
9
+ radius?: number | string;
10
+ };
11
+ export type VoiceWidgetLabels = {
12
+ callEnded?: string;
13
+ connecting?: string;
14
+ endCall?: string;
15
+ idle?: string;
16
+ listening?: string;
17
+ mute?: string;
18
+ speaking?: string;
19
+ startCall?: string;
20
+ thinking?: string;
21
+ unmute?: string;
22
+ };
23
+ export declare const VoiceWidget: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
24
+ controllerOptions: {
25
+ default: () => {};
26
+ type: PropType<VoiceControllerOptions>;
27
+ };
28
+ labels: {
29
+ default: () => {};
30
+ type: PropType<VoiceWidgetLabels>;
31
+ };
32
+ path: {
33
+ default: string;
34
+ type: StringConstructor;
35
+ };
36
+ theme: {
37
+ default: () => {};
38
+ type: PropType<VoiceWidgetTheme>;
39
+ };
40
+ title: {
41
+ default: string;
42
+ type: StringConstructor;
43
+ };
44
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
45
+ [key: string]: any;
46
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
47
+ error: (_message: string) => true;
48
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
49
+ controllerOptions: {
50
+ default: () => {};
51
+ type: PropType<VoiceControllerOptions>;
52
+ };
53
+ labels: {
54
+ default: () => {};
55
+ type: PropType<VoiceWidgetLabels>;
56
+ };
57
+ path: {
58
+ default: string;
59
+ type: StringConstructor;
60
+ };
61
+ theme: {
62
+ default: () => {};
63
+ type: PropType<VoiceWidgetTheme>;
64
+ };
65
+ title: {
66
+ default: string;
67
+ type: StringConstructor;
68
+ };
69
+ }>> & Readonly<{
70
+ onError?: ((_message: string) => any) | undefined;
71
+ }>, {
72
+ title: string;
73
+ path: string;
74
+ theme: VoiceWidgetTheme;
75
+ controllerOptions: VoiceControllerOptions;
76
+ labels: VoiceWidgetLabels;
77
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -31,6 +31,8 @@ export { useVoiceDeliveryRuntime } from "./useVoiceDeliveryRuntime";
31
31
  export { useVoiceCampaignDialerProof } from "./useVoiceCampaignDialerProof";
32
32
  export { useVoiceStream } from "./useVoiceStream";
33
33
  export { useVoiceController } from "./useVoiceController";
34
+ export { VoiceWidget } from "./VoiceWidget";
35
+ export type { VoiceWidgetLabels, VoiceWidgetTheme, } from "./VoiceWidget";
34
36
  export { useVoiceProviderStatus } from "./useVoiceProviderStatus";
35
37
  export { useVoiceProviderCapabilities } from "./useVoiceProviderCapabilities";
36
38
  export { useVoiceProviderContracts } from "./useVoiceProviderContracts";
package/dist/vue/index.js CHANGED
@@ -11867,6 +11867,221 @@ function useVoiceController(path, options = {}) {
11867
11867
  turns
11868
11868
  };
11869
11869
  }
11870
+ // src/vue/VoiceWidget.ts
11871
+ import { computed as computed10, defineComponent as defineComponent18, h as h18 } from "vue";
11872
+
11873
+ // src/agentState.ts
11874
+ var deriveVoiceAgentUIState = (input) => {
11875
+ if (!input.isConnected) {
11876
+ return "idle";
11877
+ }
11878
+ if (input.isPlaying) {
11879
+ return "speaking";
11880
+ }
11881
+ if (input.isRecording && input.hasActivePartial) {
11882
+ return "listening";
11883
+ }
11884
+ if (input.isRecording) {
11885
+ return "listening";
11886
+ }
11887
+ if (input.lastTranscriptAt && !input.lastAssistantAt) {
11888
+ return "thinking";
11889
+ }
11890
+ if (input.lastTranscriptAt && input.lastAssistantAt && input.lastTranscriptAt > input.lastAssistantAt) {
11891
+ return "thinking";
11892
+ }
11893
+ return "idle";
11894
+ };
11895
+ var describeVoiceAgentUIState = (state) => {
11896
+ switch (state) {
11897
+ case "idle":
11898
+ return "Idle";
11899
+ case "listening":
11900
+ return "Listening";
11901
+ case "speaking":
11902
+ return "Speaking";
11903
+ case "thinking":
11904
+ return "Thinking";
11905
+ }
11906
+ };
11907
+ var voiceAgentUIStateOrder = [
11908
+ "idle",
11909
+ "listening",
11910
+ "thinking",
11911
+ "speaking"
11912
+ ];
11913
+
11914
+ // src/vue/VoiceWidget.ts
11915
+ var DEFAULT_THEME = {
11916
+ accent: "#3b82f6",
11917
+ background: "#0f172a",
11918
+ errorAccent: "#ef4444",
11919
+ fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
11920
+ foreground: "#f8fafc",
11921
+ radius: 16
11922
+ };
11923
+ var DEFAULT_LABELS = {
11924
+ callEnded: "Call ended",
11925
+ connecting: "Connecting\u2026",
11926
+ endCall: "End call",
11927
+ idle: "Idle",
11928
+ listening: "Listening",
11929
+ mute: "Mute",
11930
+ speaking: "Speaking",
11931
+ startCall: "Start call",
11932
+ thinking: "Thinking",
11933
+ unmute: "Unmute"
11934
+ };
11935
+ var resolveRadius = (radius) => typeof radius === "number" ? `${radius}px` : radius;
11936
+ var VoiceWidget = defineComponent18({
11937
+ name: "VoiceWidget",
11938
+ props: {
11939
+ controllerOptions: {
11940
+ default: () => ({}),
11941
+ type: Object
11942
+ },
11943
+ labels: {
11944
+ default: () => ({}),
11945
+ type: Object
11946
+ },
11947
+ path: { default: "/voice", type: String },
11948
+ theme: {
11949
+ default: () => ({}),
11950
+ type: Object
11951
+ },
11952
+ title: { default: "Voice", type: String }
11953
+ },
11954
+ emits: { error: (_message) => true },
11955
+ setup(props, { emit }) {
11956
+ const controller = useVoiceController(props.path, props.controllerOptions);
11957
+ const theme = computed10(() => ({ ...DEFAULT_THEME, ...props.theme }));
11958
+ const labels = computed10(() => ({ ...DEFAULT_LABELS, ...props.labels }));
11959
+ const agentState = computed10(() => {
11960
+ const lastAssistantAt = controller.assistantAudio.value.at(-1)?.receivedAt;
11961
+ const lastTranscriptAt = controller.turns.value.at(-1)?.committedAt;
11962
+ return deriveVoiceAgentUIState({
11963
+ hasActivePartial: controller.partial.value.length > 0,
11964
+ isConnected: controller.isConnected.value,
11965
+ isPlaying: false,
11966
+ isRecording: controller.isRecording.value,
11967
+ lastAssistantAt,
11968
+ lastTranscriptAt
11969
+ });
11970
+ });
11971
+ return () => {
11972
+ const t = theme.value;
11973
+ const l = labels.value;
11974
+ if (controller.error.value) {
11975
+ emit("error", controller.error.value);
11976
+ }
11977
+ const stateLabel = controller.error.value ? "Error" : !controller.isConnected.value && controller.status.value !== "idle" ? l.connecting : controller.status.value === "completed" ? l.callEnded : agentState.value === "listening" ? l.listening : agentState.value === "speaking" ? l.speaking : agentState.value === "thinking" ? l.thinking : l.idle;
11978
+ const showStart = !controller.isRecording.value && controller.status.value !== "completed";
11979
+ return h18("div", {
11980
+ "aria-live": "polite",
11981
+ "data-agent-state": agentState.value,
11982
+ role: "region",
11983
+ style: {
11984
+ background: t.background,
11985
+ borderRadius: resolveRadius(t.radius),
11986
+ color: t.foreground,
11987
+ fontFamily: t.fontFamily,
11988
+ minWidth: "240px",
11989
+ padding: "20px 22px"
11990
+ }
11991
+ }, [
11992
+ h18("div", {
11993
+ style: {
11994
+ alignItems: "center",
11995
+ display: "flex",
11996
+ gap: "10px",
11997
+ marginBottom: "12px"
11998
+ }
11999
+ }, [
12000
+ h18("span", {
12001
+ "aria-hidden": "true",
12002
+ style: {
12003
+ background: controller.error.value ? t.errorAccent : agentState.value === "idle" ? "rgba(148,163,184,0.6)" : t.accent,
12004
+ borderRadius: "50%",
12005
+ height: "10px",
12006
+ width: "10px"
12007
+ }
12008
+ }),
12009
+ h18("strong", { style: { fontSize: "15px" } }, props.title),
12010
+ h18("span", {
12011
+ style: {
12012
+ fontSize: "13px",
12013
+ marginLeft: "auto",
12014
+ opacity: "0.7"
12015
+ }
12016
+ }, stateLabel)
12017
+ ]),
12018
+ controller.partial.value ? h18("p", {
12019
+ style: {
12020
+ fontSize: "13px",
12021
+ margin: "8px 0 12px",
12022
+ opacity: "0.85",
12023
+ wordBreak: "break-word"
12024
+ }
12025
+ }, `\u201C${controller.partial.value}\u201D`) : null,
12026
+ h18("div", { style: { display: "flex", gap: "10px" } }, [
12027
+ showStart ? h18("button", {
12028
+ onClick: () => {
12029
+ controller.startRecording();
12030
+ },
12031
+ style: {
12032
+ background: t.accent,
12033
+ border: "none",
12034
+ borderRadius: "12px",
12035
+ color: t.foreground,
12036
+ cursor: "pointer",
12037
+ fontSize: "14px",
12038
+ fontWeight: "500",
12039
+ padding: "10px 14px"
12040
+ },
12041
+ type: "button"
12042
+ }, l.startCall) : null,
12043
+ controller.isRecording.value ? h18("button", {
12044
+ onClick: () => controller.stopRecording(),
12045
+ style: {
12046
+ background: "transparent",
12047
+ border: "1px solid rgba(255,255,255,0.18)",
12048
+ borderRadius: "12px",
12049
+ color: t.foreground,
12050
+ cursor: "pointer",
12051
+ fontSize: "14px",
12052
+ fontWeight: "500",
12053
+ padding: "10px 14px"
12054
+ },
12055
+ type: "button"
12056
+ }, l.mute) : null,
12057
+ controller.isConnected.value ? h18("button", {
12058
+ onClick: () => {
12059
+ controller.close();
12060
+ },
12061
+ style: {
12062
+ background: t.errorAccent,
12063
+ border: "none",
12064
+ borderRadius: "12px",
12065
+ color: t.foreground,
12066
+ cursor: "pointer",
12067
+ fontSize: "14px",
12068
+ fontWeight: "500",
12069
+ padding: "10px 14px"
12070
+ },
12071
+ type: "button"
12072
+ }, l.endCall) : null
12073
+ ]),
12074
+ controller.error.value ? h18("p", {
12075
+ style: {
12076
+ color: t.errorAccent,
12077
+ fontSize: "12px",
12078
+ marginTop: "12px"
12079
+ }
12080
+ }, controller.error.value) : null
12081
+ ]);
12082
+ };
12083
+ }
12084
+ });
11870
12085
  // src/vue/useVoiceTraceTimeline.ts
11871
12086
  import { onUnmounted as onUnmounted23, ref as ref18, shallowRef as shallowRef22 } from "vue";
11872
12087
  function useVoiceTraceTimeline(path = "/api/voice-traces", options = {}) {
@@ -12036,6 +12251,7 @@ export {
12036
12251
  useVoiceCampaignDialerProof,
12037
12252
  useVoiceCallDebugger,
12038
12253
  useVoiceAgentSquadStatus,
12254
+ VoiceWidget,
12039
12255
  VoiceTurnQuality,
12040
12256
  VoiceTurnLatency,
12041
12257
  VoiceSessionSnapshot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.494",
3
+ "version": "0.0.22-beta.495",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",