@myscheme/voice-navigation-sdk 0.1.3 → 0.1.6

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.
Files changed (41) hide show
  1. package/README.md +250 -1
  2. package/dist/actions.d.ts.map +1 -1
  3. package/dist/actions.js +397 -2
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +18 -3
  7. package/dist/microphone-handler.d.ts +2 -0
  8. package/dist/microphone-handler.d.ts.map +1 -1
  9. package/dist/microphone-handler.js +11 -2
  10. package/dist/navigation-controller.d.ts +2 -0
  11. package/dist/navigation-controller.d.ts.map +1 -1
  12. package/dist/navigation-controller.js +89 -10
  13. package/dist/server/azure-speech-handler.d.ts +12 -0
  14. package/dist/server/azure-speech-handler.d.ts.map +1 -0
  15. package/dist/server/azure-speech-handler.js +40 -0
  16. package/dist/server/bedrock-embedding-handler.d.ts +15 -0
  17. package/dist/server/bedrock-embedding-handler.d.ts.map +1 -0
  18. package/dist/server/bedrock-embedding-handler.js +123 -0
  19. package/dist/server/bedrock-handler.d.ts +14 -0
  20. package/dist/server/bedrock-handler.d.ts.map +1 -0
  21. package/dist/server/bedrock-handler.js +114 -0
  22. package/dist/server/index.d.ts +4 -0
  23. package/dist/server/index.d.ts.map +1 -1
  24. package/dist/server/index.js +4 -0
  25. package/dist/server/opensearch-env-handler.d.ts +22 -0
  26. package/dist/server/opensearch-env-handler.d.ts.map +1 -0
  27. package/dist/server/opensearch-env-handler.js +221 -0
  28. package/dist/services/azure-speech.d.ts +10 -2
  29. package/dist/services/azure-speech.d.ts.map +1 -1
  30. package/dist/services/azure-speech.js +66 -3
  31. package/dist/services/bedrock.d.ts +13 -4
  32. package/dist/services/bedrock.d.ts.map +1 -1
  33. package/dist/services/bedrock.js +171 -17
  34. package/dist/services/vector-search.d.ts.map +1 -1
  35. package/dist/services/vector-search.js +15 -2
  36. package/dist/types.d.ts +24 -11
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/ui.d.ts +4 -3
  39. package/dist/ui.d.ts.map +1 -1
  40. package/dist/ui.js +150 -17
  41. package/package.json +3 -3
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { parseNavigationXML, fetchNavigationXML, loadNavigationPages, } from "./
8
8
  export type { PageXMLConfig } from "./services/xml-parser.js";
9
9
  export * from "./types.js";
10
10
  export * from "./actions.js";
11
- export * from "./ui.js";
11
+ export { createFloatingControl, attachToCustomButton, setState, updateStatus, updateTranscript, updateActionIndicator, showError, emitEvent, } from "./ui.js";
12
12
  import { VoiceNavigationController } from "./navigation-controller.js";
13
13
  import type { NavigationConfig } from "./types.js";
14
14
  export declare function initNavigationOnMicrophone(config: NavigationConfig): VoiceNavigationController;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA8JnD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,CAuE3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,SAAS,EACT,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+JnD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,CAoG3B"}
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ export { PageRegistry } from "./services/page-registry.js";
6
6
  export { parseNavigationXML, fetchNavigationXML, loadNavigationPages, } from "./services/xml-parser.js";
7
7
  export * from "./types.js";
8
8
  export * from "./actions.js";
9
- export * from "./ui.js";
9
+ export { createFloatingControl, attachToCustomButton, setState, updateStatus, updateTranscript, updateActionIndicator, showError, emitEvent, } from "./ui.js";
10
10
  import { VoiceNavigationController } from "./navigation-controller.js";
11
11
  const CONFIG_STORAGE_KEY = "__voice_navigation_config";
12
12
  const resolveOpenSearchConfig = (config) => {
@@ -37,6 +37,7 @@ const buildPersistableConfig = (config) => {
37
37
  pages: normalizedConfig.pages ? { ...normalizedConfig.pages } : undefined,
38
38
  language: normalizedConfig.language,
39
39
  autoStart: normalizedConfig.autoStart,
40
+ ui: normalizedConfig.ui ? { ...normalizedConfig.ui } : undefined,
40
41
  };
41
42
  if (hasCustomHandlers) {
42
43
  persistable.hasCustomHandlers = true;
@@ -92,6 +93,7 @@ export function initNavigationOnMicrophone(config) {
92
93
  }
93
94
  const globalWindow = window;
94
95
  const normalizedConfig = normalizeNavigationConfig(config);
96
+ console.log("[Voice Navigation Init] UI Config received:", normalizedConfig.ui);
95
97
  const previousConfigRaw = globalWindow.__voiceNavigationConfig;
96
98
  const previousConfig = previousConfigRaw
97
99
  ? normalizeNavigationConfig(previousConfigRaw)
@@ -107,6 +109,7 @@ export function initNavigationOnMicrophone(config) {
107
109
  console.warn("Failed to persist voice navigation config:", error);
108
110
  }
109
111
  if (globalWindow.__navigateOnMicrophoneInitialized) {
112
+ console.log("[Voice Navigation] Already initialized, checking for config changes...");
110
113
  const existingController = globalWindow.navigationController;
111
114
  const previousOpenSearchConfig = resolveOpenSearchConfig(previousConfig);
112
115
  const currentOpenSearchConfig = resolveOpenSearchConfig(normalizedConfig);
@@ -118,8 +121,20 @@ export function initNavigationOnMicrophone(config) {
118
121
  wantsVectorSearch &&
119
122
  describeOpenSearchConfig(previousOpenSearchConfig) !==
120
123
  describeOpenSearchConfig(currentOpenSearchConfig);
121
- if (wantsVectorSearch &&
122
- (!hadVectorSearch || vectorConfigChanged || !existingHasVectorSearch)) {
124
+ const previousUI = previousConfig?.ui;
125
+ const currentUI = normalizedConfig.ui;
126
+ console.log("[Voice Navigation] Previous UI config:", previousUI);
127
+ console.log("[Voice Navigation] Current UI config:", currentUI);
128
+ const uiConfigChanged = previousUI?.showDefaultButton !== currentUI?.showDefaultButton ||
129
+ previousUI?.customButtonSelector !== currentUI?.customButtonSelector ||
130
+ previousUI?.buttonPlacement !== currentUI?.buttonPlacement;
131
+ console.log("[Voice Navigation] UI config changed:", uiConfigChanged);
132
+ if ((wantsVectorSearch &&
133
+ (!hadVectorSearch ||
134
+ vectorConfigChanged ||
135
+ !existingHasVectorSearch)) ||
136
+ uiConfigChanged) {
137
+ console.log("[Voice Navigation] Destroying existing controller and recreating...");
123
138
  try {
124
139
  existingController?.destroy?.();
125
140
  }
@@ -22,6 +22,7 @@ export declare class MicrophoneHandler {
22
22
  private hasPermission;
23
23
  private audioContext;
24
24
  private audioContextUnlocked;
25
+ private activeCallbacks;
25
26
  constructor(config: MicrophoneConfig);
26
27
  hasMicrophonePermission(): boolean;
27
28
  hasUnlockedAudioContext(): boolean;
@@ -36,6 +37,7 @@ export declare class MicrophoneHandler {
36
37
  clearSilenceTimer(): void;
37
38
  getAggregatedText(): string;
38
39
  scheduleSilenceCheck(callbacks?: NavigationCallbacks): void;
40
+ rescheduleSilenceCheck(): void;
39
41
  sendToBedrockAgent(text: string): Promise<AgentActionResponse>;
40
42
  disposeRecognizer(): void;
41
43
  createRecognizer(): Promise<sdk.SpeechRecognizer>;
@@ -1 +1 @@
1
- {"version":3,"file":"microphone-handler.d.ts","sourceRoot":"","sources":["../src/microphone-handler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,GAAG,MAAM,wCAAwC,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,UAAU,gBAAgB;IACxB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAqC;IACvD,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,oBAAoB,CAAkB;gBAElC,MAAM,EAAE,gBAAgB;IAUpC,uBAAuB,IAAI,OAAO;IAIlC,uBAAuB,IAAI,OAAO;IAO5B,kBAAkB,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAoB1D,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBzC,kBAAkB,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAwDzD,2BAA2B,IAAI,OAAO,CAAC,OAAO,CAAC;IA2BrD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAYnC,wBAAwB,IAAI,IAAI;IAO1B,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBnC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBzC,iBAAiB,IAAI,IAAI;IAUzB,iBAAiB,IAAI,MAAM;IAO3B,oBAAoB,CAAC,SAAS,GAAE,mBAAwB,GAAG,IAAI;IAczD,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAiBpE,iBAAiB,IAAI,IAAI;IAgCnB,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAmBjD,cAAc,CAAC,SAAS,GAAE,mBAAwB,GAAG,OAAO,CAAC,OAAO,CAAC;IAiHrE,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;WAoCzB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC;CAYvD"}
1
+ {"version":3,"file":"microphone-handler.d.ts","sourceRoot":"","sources":["../src/microphone-handler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,GAAG,MAAM,wCAAwC,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,UAAU,gBAAgB;IACxB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAqC;IACvD,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,eAAe,CAA2B;gBAEtC,MAAM,EAAE,gBAAgB;IAUpC,uBAAuB,IAAI,OAAO;IAIlC,uBAAuB,IAAI,OAAO;IAO5B,kBAAkB,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAoB1D,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBzC,kBAAkB,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAwDzD,2BAA2B,IAAI,OAAO,CAAC,OAAO,CAAC;IA2BrD,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAYnC,wBAAwB,IAAI,IAAI;IAO1B,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBnC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAkBzC,iBAAiB,IAAI,IAAI;IAUzB,iBAAiB,IAAI,MAAM;IAO3B,oBAAoB,CAAC,SAAS,GAAE,mBAAwB,GAAG,IAAI;IAiB/D,sBAAsB,IAAI,IAAI;IAUxB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAiBpE,iBAAiB,IAAI,IAAI;IAgCnB,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAmBjD,cAAc,CAAC,SAAS,GAAE,mBAAwB,GAAG,OAAO,CAAC,OAAO,CAAC;IAoHrE,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;WAoCzB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC;CAYvD"}
@@ -10,6 +10,7 @@ export class MicrophoneHandler {
10
10
  this.hasPermission = false;
11
11
  this.audioContext = null;
12
12
  this.audioContextUnlocked = false;
13
+ this.activeCallbacks = {};
13
14
  this.azureSpeechService = config.azureSpeechService;
14
15
  this.bedrockService = config.bedrockService;
15
16
  this.language = config.language || "en-IN";
@@ -156,13 +157,20 @@ export class MicrophoneHandler {
156
157
  }
157
158
  scheduleSilenceCheck(callbacks = {}) {
158
159
  this.clearSilenceTimer();
160
+ const callbacksToUse = Object.keys(callbacks).length > 0 ? callbacks : this.activeCallbacks;
159
161
  this.silenceTimerId = setTimeout(() => {
160
162
  console.log("Silence detected - triggering callback");
161
- if (callbacks.onSilence) {
162
- callbacks.onSilence();
163
+ if (callbacksToUse.onSilence) {
164
+ callbacksToUse.onSilence();
163
165
  }
164
166
  }, this.silenceTimeout);
165
167
  }
168
+ rescheduleSilenceCheck() {
169
+ if (!this.isRecording) {
170
+ return;
171
+ }
172
+ this.scheduleSilenceCheck(this.activeCallbacks);
173
+ }
166
174
  async sendToBedrockAgent(text) {
167
175
  if (!text || !text.trim()) {
168
176
  return { action: "unknown" };
@@ -216,6 +224,7 @@ export class MicrophoneHandler {
216
224
  console.warn("Already recording");
217
225
  return false;
218
226
  }
227
+ this.activeCallbacks = callbacks;
219
228
  if (!this.hasPermission) {
220
229
  const granted = await this.requestMicrophonePermission();
221
230
  if (!granted) {
@@ -14,7 +14,9 @@ export declare class VoiceNavigationController {
14
14
  private removeAutoStartListeners;
15
15
  private vectorSearchService;
16
16
  private pagesInitialized;
17
+ private uiInitialized;
17
18
  constructor(config: NavigationConfig);
19
+ private initializeUIAsync;
18
20
  private shouldAutoStart;
19
21
  setAutoStart(enabled: boolean): void;
20
22
  prepareForNavigation(options?: {
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-controller.d.ts","sourceRoot":"","sources":["../src/navigation-controller.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,YAAY,CAAC;AA0DpB,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,EAAE,CAAa;IACvB,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,qBAAqB,CAAkB;IAC/C,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,+BAA+B,CAAkB;IACzD,OAAO,CAAC,wBAAwB,CAA6B;IAC7D,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,gBAAgB,CAAgB;gBAE5B,MAAM,EAAE,gBAAgB;IAgFpC,OAAO,CAAC,eAAe;IAoBhB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAepC,oBAAoB,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;YAOtD,eAAe;IAmE7B,OAAO,CAAC,kBAAkB;YASZ,eAAe;IAkBhB,KAAK,CAChB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAO,GACzC,OAAO,CAAC,IAAI,CAAC;IA0EH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BlC,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,aAAa;YAWP,aAAa;YAOb,wBAAwB;IAuCtC,OAAO,CAAC,WAAW;YAoBL,iBAAiB;YAwBjB,iBAAiB;IA0HxB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;YAI5B,yBAAyB;IAgMvC,OAAO,CAAC,oBAAoB;IAqD5B,OAAO,CAAC,yBAAyB;IAoCjC,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,4BAA4B;IAiB7B,OAAO,IAAI,IAAI;IAgBtB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,kBAAkB;IAwD1B,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,uBAAuB;IAkD/B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;CAmBjC"}
1
+ {"version":3,"file":"navigation-controller.d.ts","sourceRoot":"","sources":["../src/navigation-controller.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,YAAY,CAAC;AA2DpB,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,qBAAqB,CAAkB;IAC/C,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,+BAA+B,CAAkB;IACzD,OAAO,CAAC,wBAAwB,CAA6B;IAC7D,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,MAAM,EAAE,gBAAgB;YAmItB,iBAAiB;IAiD/B,OAAO,CAAC,eAAe;IAoBhB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAepC,oBAAoB,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;YAOtD,eAAe;IAmE7B,OAAO,CAAC,kBAAkB;YASZ,eAAe;IAwBhB,KAAK,CAChB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAO,GACzC,OAAO,CAAC,IAAI,CAAC;IAmFH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiClC,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,aAAa;YAWP,aAAa;YAOb,wBAAwB;IA2CtC,OAAO,CAAC,WAAW;YAoBL,iBAAiB;YAwBjB,iBAAiB;IA8HxB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;YAI5B,yBAAyB;IAgMvC,OAAO,CAAC,oBAAoB;IAqD5B,OAAO,CAAC,yBAAyB;IAoCjC,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,4BAA4B;IAiB7B,OAAO,IAAI,IAAI;IAgBtB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,kBAAkB;IAwD1B,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,uBAAuB;IAkD/B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,wBAAwB;CAmBjC"}
@@ -5,7 +5,7 @@ import { PageRegistry } from "./services/page-registry.js";
5
5
  import { loadNavigationPages } from "./services/xml-parser.js";
6
6
  import { VectorSearchService, } from "./services/vector-search.js";
7
7
  import { performAgentAction, formatActionLabel, extractAgentAction, setPageRegistry, } from "./actions.js";
8
- import { createFloatingControl, setState, updateStatus, updateTranscript, updateActionIndicator, showError, emitEvent, } from "./ui.js";
8
+ import { createFloatingControl, attachToCustomButton, setState, updateStatus, updateTranscript, updateActionIndicator, showError, emitEvent, } from "./ui.js";
9
9
  const AUTO_START_STORAGE_KEY = "__navigate_auto_start";
10
10
  const RESUME_STATE_STORAGE_KEY = "__navigate_resume_state";
11
11
  const flowLog = (...args) => {
@@ -42,19 +42,48 @@ export class VoiceNavigationController {
42
42
  ...config,
43
43
  opensearch: openSearchConfig,
44
44
  };
45
- if (!this.config.azure?.subscriptionKey || !this.config.azure?.region) {
46
- throw new Error("Azure Speech configuration is required");
45
+ console.log("[Voice Navigation Controller] Constructor called with UI config:", this.config.ui);
46
+ const hasAzureEndpoint = !!this.config.azure?.tokenEndpoint;
47
+ const hasAzureCredentials = !!(this.config.azure?.subscriptionKey && this.config.azure?.region);
48
+ if (!this.config.azure) {
49
+ this.config.azure = { tokenEndpoint: "/api/speech/token" };
50
+ console.log("[VoiceNavigation] Auto-configured Azure endpoint: /api/speech/token");
51
+ }
52
+ else if (!hasAzureEndpoint && !hasAzureCredentials) {
53
+ this.config.azure.tokenEndpoint = "/api/speech/token";
54
+ console.log("[VoiceNavigation] Auto-configured Azure endpoint: /api/speech/token");
55
+ }
56
+ const hasAwsEndpoint = !!this.config.aws?.bedrockEndpoint;
57
+ const hasAwsCredentials = !!(this.config.aws?.accessKeyId &&
58
+ this.config.aws?.secretAccessKey &&
59
+ this.config.aws?.modelId);
60
+ if (!this.config.aws) {
61
+ this.config.aws = {
62
+ bedrockEndpoint: "/api/speech/bedrock",
63
+ embeddingEndpoint: "/api/speech/bedrock-embedding",
64
+ };
65
+ console.log("[VoiceNavigation] Auto-configured AWS endpoints: /api/speech/bedrock, /api/speech/bedrock-embedding");
66
+ }
67
+ else if (!hasAwsEndpoint && !hasAwsCredentials) {
68
+ this.config.aws.bedrockEndpoint = "/api/speech/bedrock";
69
+ this.config.aws.embeddingEndpoint = "/api/speech/bedrock-embedding";
70
+ console.log("[VoiceNavigation] Auto-configured AWS endpoints: /api/speech/bedrock, /api/speech/bedrock-embedding");
47
71
  }
48
- if (!this.config.aws?.accessKeyId ||
49
- !this.config.aws?.secretAccessKey ||
50
- !this.config.aws?.modelId) {
51
- throw new Error("AWS Bedrock configuration is required");
72
+ if (openSearchConfig &&
73
+ !openSearchConfig.searchEndpoint &&
74
+ !openSearchConfig.node) {
75
+ openSearchConfig.searchEndpoint = "/api/opensearch/search";
76
+ openSearchConfig.suggestEndpoint = "/api/opensearch/suggest";
77
+ console.log("[VoiceNavigation] Auto-configured OpenSearch endpoints: /api/opensearch/search, /api/opensearch/suggest");
52
78
  }
53
79
  this.azureSpeechService = new AzureSpeechService({
80
+ tokenEndpoint: this.config.azure.tokenEndpoint,
54
81
  subscriptionKey: this.config.azure.subscriptionKey,
55
82
  region: this.config.azure.region,
56
83
  });
57
84
  this.bedrockService = new BedrockService({
85
+ bedrockEndpoint: this.config.aws.bedrockEndpoint,
86
+ embeddingEndpoint: this.config.aws.embeddingEndpoint,
58
87
  region: this.config.aws.region || "ap-south-1",
59
88
  accessKeyId: this.config.aws.accessKeyId,
60
89
  secretAccessKey: this.config.aws.secretAccessKey,
@@ -80,8 +109,7 @@ export class VoiceNavigationController {
80
109
  }
81
110
  }
82
111
  this.resumeAfterNavigation = this.consumeResumeState();
83
- this.ui = createFloatingControl();
84
- this.setupEventHandlers();
112
+ this.uiInitialized = this.initializeUIAsync();
85
113
  if (this.resumeAfterNavigation) {
86
114
  void this.start({ reason: "auto" });
87
115
  }
@@ -89,6 +117,39 @@ export class VoiceNavigationController {
89
117
  void this.start({ reason: "auto" });
90
118
  }
91
119
  }
120
+ async initializeUIAsync() {
121
+ const uiConfig = this.config.ui || {};
122
+ const showDefaultButton = uiConfig.showDefaultButton !== false;
123
+ const customButtonSelector = uiConfig.customButtonSelector;
124
+ const buttonPlacement = uiConfig.buttonPlacement || "bottom-right";
125
+ console.log("[Voice Navigation] UI Config:", {
126
+ showDefaultButton,
127
+ customButtonSelector,
128
+ buttonPlacement,
129
+ });
130
+ if (!showDefaultButton && customButtonSelector) {
131
+ console.log(`[Voice Navigation] Attempting to use custom button: ${customButtonSelector}`);
132
+ try {
133
+ this.ui = await attachToCustomButton(customButtonSelector);
134
+ flowLog(`✓ Attached to custom button: ${customButtonSelector}`);
135
+ }
136
+ catch (error) {
137
+ console.error("Failed to attach to custom button:", error);
138
+ console.warn("Falling back to default button due to custom button error");
139
+ this.ui = createFloatingControl(buttonPlacement);
140
+ }
141
+ }
142
+ else if (!showDefaultButton && !customButtonSelector) {
143
+ console.warn("showDefaultButton is false but no customButtonSelector provided. Creating default button.");
144
+ this.ui = createFloatingControl(buttonPlacement);
145
+ }
146
+ else {
147
+ console.log(`[Voice Navigation] Creating default button at: ${buttonPlacement}`);
148
+ this.ui = createFloatingControl(buttonPlacement);
149
+ flowLog(`✓ Created default button at position: ${buttonPlacement}`);
150
+ }
151
+ this.setupEventHandlers();
152
+ }
92
153
  shouldAutoStart() {
93
154
  if (this.config.autoStart === true) {
94
155
  return true;
@@ -164,6 +225,10 @@ export class VoiceNavigationController {
164
225
  });
165
226
  }
166
227
  async toggleRecording() {
228
+ if (!this.ui || !this.ui.root) {
229
+ console.warn("UI not initialized, cannot toggle recording");
230
+ return;
231
+ }
167
232
  const currentState = this.ui.root.dataset.state;
168
233
  if (currentState === "listening" || currentState === "thinking") {
169
234
  await this.stop();
@@ -177,6 +242,11 @@ export class VoiceNavigationController {
177
242
  async start(options = {}) {
178
243
  const reason = options.reason ?? "user";
179
244
  this.lastStartReason = reason;
245
+ await this.uiInitialized;
246
+ if (!this.ui || !this.ui.root) {
247
+ console.error("UI not properly initialized");
248
+ return;
249
+ }
180
250
  try {
181
251
  await this.pagesInitialized;
182
252
  }
@@ -239,6 +309,10 @@ export class VoiceNavigationController {
239
309
  }
240
310
  }
241
311
  async stop() {
312
+ if (!this.ui || !this.ui.root) {
313
+ console.warn("UI not initialized, cannot stop");
314
+ return;
315
+ }
242
316
  this.isProcessing = false;
243
317
  setState(this.ui, "thinking");
244
318
  emitEvent("state-change", { state: "thinking" });
@@ -304,6 +378,7 @@ export class VoiceNavigationController {
304
378
  }
305
379
  updateTranscript(this.ui, "");
306
380
  this.isProcessing = false;
381
+ this.microphoneHandler.rescheduleSilenceCheck();
307
382
  }
308
383
  }
309
384
  handleError(error) {
@@ -395,6 +470,10 @@ export class VoiceNavigationController {
395
470
  handler: this,
396
471
  onStop: () => this.stop(),
397
472
  transcript: originalText,
473
+ parameters: {
474
+ text: actionData.text || query,
475
+ query: query,
476
+ },
398
477
  });
399
478
  if (typeof this.config.actionHandlers?.[action] === "function") {
400
479
  try {
@@ -693,7 +772,7 @@ export class VoiceNavigationController {
693
772
  }
694
773
  destroy() {
695
774
  this.clearAutoStartFallback();
696
- if (this.ui.root.parentElement) {
775
+ if (this.ui && this.ui.root && this.ui.root.parentElement) {
697
776
  this.ui.root.parentElement.removeChild(this.ui.root);
698
777
  }
699
778
  const styles = document.getElementById("navigate-control-styles");
@@ -0,0 +1,12 @@
1
+ type NextApiRequest = {
2
+ method?: string;
3
+ body?: unknown;
4
+ };
5
+ type NextApiResponse = {
6
+ status: (code: number) => NextApiResponse;
7
+ json: (data: unknown) => void;
8
+ setHeader: (name: string, value: string | string[]) => void;
9
+ };
10
+ export declare function azureSpeechTokenHandler(req: NextApiRequest, res: NextApiResponse): Promise<void>;
11
+ export {};
12
+ //# sourceMappingURL=azure-speech-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azure-speech-handler.d.ts","sourceRoot":"","sources":["../../src/server/azure-speech-handler.ts"],"names":[],"mappings":"AAiBA,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7D,CAAC;AAWF,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CA0Df"}
@@ -0,0 +1,40 @@
1
+ export async function azureSpeechTokenHandler(req, res) {
2
+ if (req.method !== "POST") {
3
+ res.setHeader("Allow", ["POST"]);
4
+ res.status(405).json({ error: "Method not allowed" });
5
+ return;
6
+ }
7
+ const key = process.env.AZURE_SPEECH_KEY;
8
+ const region = process.env.AZURE_SPEECH_REGION;
9
+ if (!key || !region) {
10
+ console.error("[VoiceNavigation] Missing Azure Speech credentials. Set AZURE_SPEECH_KEY and AZURE_SPEECH_REGION in your .env file");
11
+ res.status(500).json({
12
+ error: "Azure Speech configuration is missing",
13
+ hint: "Set AZURE_SPEECH_KEY and AZURE_SPEECH_REGION environment variables",
14
+ });
15
+ return;
16
+ }
17
+ try {
18
+ const url = `https://${region}.api.cognitive.microsoft.com/sts/v1.0/issueToken`;
19
+ const response = await fetch(url, {
20
+ method: "POST",
21
+ headers: {
22
+ "Ocp-Apim-Subscription-Key": key,
23
+ },
24
+ });
25
+ if (!response.ok) {
26
+ throw new Error(`Azure API responded with status ${response.status}`);
27
+ }
28
+ const token = await response.text();
29
+ res.setHeader("Cache-Control", "no-store, no-cache, max-age=0, must-revalidate");
30
+ const responseData = { token, region };
31
+ res.status(200).json(responseData);
32
+ }
33
+ catch (error) {
34
+ console.error("[VoiceNavigation] Failed to fetch Azure speech token:", error);
35
+ res.status(500).json({
36
+ error: "Failed to get token",
37
+ message: error instanceof Error ? error.message : "Unknown error",
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,15 @@
1
+ type NextApiRequest = {
2
+ method?: string;
3
+ body?: {
4
+ text?: string | string[];
5
+ inputType?: string;
6
+ };
7
+ };
8
+ type NextApiResponse = {
9
+ status: (code: number) => NextApiResponse;
10
+ json: (data: unknown) => void;
11
+ setHeader: (name: string, value: string | string[]) => void;
12
+ };
13
+ export declare function bedrockEmbeddingHandler(req: NextApiRequest, res: NextApiResponse): Promise<void>;
14
+ export {};
15
+ //# sourceMappingURL=bedrock-embedding-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bedrock-embedding-handler.d.ts","sourceRoot":"","sources":["../../src/server/bedrock-embedding-handler.ts"],"names":[],"mappings":"AAsBA,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7D,CAAC;AA0BF,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAwJf"}
@@ -0,0 +1,123 @@
1
+ import crypto from "crypto";
2
+ function sign(key, msg) {
3
+ return crypto.createHmac("sha256", key).update(msg, "utf8").digest();
4
+ }
5
+ function getSignatureKey(key, dateStamp, region, service) {
6
+ const kDate = sign("AWS4" + key, dateStamp);
7
+ const kRegion = sign(kDate, region);
8
+ const kService = sign(kRegion, service);
9
+ const kSigning = sign(kService, "aws4_request");
10
+ return kSigning;
11
+ }
12
+ export async function bedrockEmbeddingHandler(req, res) {
13
+ if (req.method !== "POST") {
14
+ res.setHeader("Allow", ["POST"]);
15
+ res.status(405).json({ error: "Method not allowed" });
16
+ return;
17
+ }
18
+ const region = process.env.AWS_REGION || "ap-south-1";
19
+ const accessKey = process.env.AWS_ACCESS_KEY_ID;
20
+ const secretKey = process.env.AWS_SECRET_ACCESS_KEY;
21
+ const sessionToken = process.env.AWS_SESSION_TOKEN;
22
+ const embeddingModelId = process.env.AWS_EMBEDDING_MODEL_ID || "cohere.embed-multilingual-v3";
23
+ if (!accessKey || !secretKey) {
24
+ console.error("[VoiceNavigation] Missing AWS credentials. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in your .env file");
25
+ res.status(500).json({
26
+ error: "Missing AWS credentials",
27
+ hint: "Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables",
28
+ });
29
+ return;
30
+ }
31
+ if (!embeddingModelId) {
32
+ res.status(500).json({ error: "Missing AWS_EMBEDDING_MODEL_ID" });
33
+ return;
34
+ }
35
+ const { text, inputType = "search_query" } = req.body || {};
36
+ if (!text) {
37
+ res.status(400).json({ error: "Missing text parameter" });
38
+ return;
39
+ }
40
+ const requestBody = {
41
+ texts: Array.isArray(text) ? text : [text],
42
+ input_type: inputType,
43
+ truncate: "END",
44
+ };
45
+ const body = JSON.stringify(requestBody);
46
+ const service = "bedrock";
47
+ const host = `bedrock-runtime.${region}.amazonaws.com`;
48
+ const encodedModelId = encodeURIComponent(embeddingModelId);
49
+ const path = `/model/${encodedModelId}/invoke`;
50
+ const endpoint = `https://${host}${path}`;
51
+ const canonicalPath = path.replace(/%/g, "%25");
52
+ const amzDate = new Date().toISOString().replace(/[:-]|\.\d{3}/g, "");
53
+ const dateStamp = amzDate.slice(0, 8);
54
+ const headers = {
55
+ "content-type": "application/json",
56
+ host: host,
57
+ "x-amz-date": amzDate,
58
+ };
59
+ if (sessionToken) {
60
+ headers["x-amz-security-token"] = sessionToken;
61
+ }
62
+ const signedHeaders = Object.keys(headers)
63
+ .map((h) => h.toLowerCase())
64
+ .sort()
65
+ .join(";");
66
+ const canonicalHeaders = Object.keys(headers)
67
+ .map((h) => h.toLowerCase())
68
+ .sort()
69
+ .map((k) => `${k}:${headers[k]}\n`)
70
+ .join("");
71
+ const canonicalRequest = "POST\n" +
72
+ canonicalPath +
73
+ "\n" +
74
+ "\n" +
75
+ canonicalHeaders +
76
+ "\n" +
77
+ signedHeaders +
78
+ "\n" +
79
+ crypto.createHash("sha256").update(body, "utf8").digest("hex");
80
+ const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
81
+ const stringToSign = "AWS4-HMAC-SHA256\n" +
82
+ amzDate +
83
+ "\n" +
84
+ credentialScope +
85
+ "\n" +
86
+ crypto.createHash("sha256").update(canonicalRequest, "utf8").digest("hex");
87
+ const signingKey = getSignatureKey(secretKey, dateStamp, region, service);
88
+ const signature = crypto
89
+ .createHmac("sha256", signingKey)
90
+ .update(stringToSign, "utf8")
91
+ .digest("hex");
92
+ const authorizationHeader = `AWS4-HMAC-SHA256 Credential=${accessKey}/${credentialScope}, ` +
93
+ `SignedHeaders=${signedHeaders}, Signature=${signature}`;
94
+ const fetchHeaders = {
95
+ ...headers,
96
+ Authorization: authorizationHeader,
97
+ };
98
+ try {
99
+ const response = await fetch(endpoint, {
100
+ method: "POST",
101
+ headers: fetchHeaders,
102
+ body,
103
+ });
104
+ if (!response.ok) {
105
+ const errorText = await response.text();
106
+ console.error("[VoiceNavigation] Bedrock Embedding API error:", response.status, errorText);
107
+ res.status(response.status).json({
108
+ error: "Bedrock Embedding API error",
109
+ details: errorText,
110
+ });
111
+ return;
112
+ }
113
+ const json = await response.json();
114
+ res.status(200).json(json);
115
+ }
116
+ catch (error) {
117
+ console.error("[VoiceNavigation] Bedrock embedding generation error:", error);
118
+ res.status(500).json({
119
+ error: "Failed to generate embeddings",
120
+ message: error instanceof Error ? error.message : "Unknown error",
121
+ });
122
+ }
123
+ }
@@ -0,0 +1,14 @@
1
+ type NextApiRequest = {
2
+ method?: string;
3
+ body?: {
4
+ body?: unknown;
5
+ };
6
+ };
7
+ type NextApiResponse = {
8
+ status: (code: number) => NextApiResponse;
9
+ json: (data: unknown) => void;
10
+ setHeader: (name: string, value: string | string[]) => void;
11
+ };
12
+ export declare function bedrockHandler(req: NextApiRequest, res: NextApiResponse): Promise<void>;
13
+ export {};
14
+ //# sourceMappingURL=bedrock-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bedrock-handler.d.ts","sourceRoot":"","sources":["../../src/server/bedrock-handler.ts"],"names":[],"mappings":"AAsBA,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7D,CAAC;AA0BF,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CA0If"}