@iblai/iblai-js 2.7.7 → 2.7.8

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.
@@ -6720,7 +6720,8 @@ var voiceTabLabels$3 = {
6720
6720
  },
6721
6721
  subTabs: {
6722
6722
  voice: "Voice",
6723
- callConfig: "Voice call"
6723
+ callConfig: "Voice Call",
6724
+ settings: "Settings"
6724
6725
  },
6725
6726
  mentorVoice: {
6726
6727
  title: "Agent Voice",
@@ -6781,6 +6782,26 @@ var voiceTabLabels$3 = {
6781
6782
  }
6782
6783
  }
6783
6784
  },
6785
+ transcription: {
6786
+ toggle: {
6787
+ label: "Dictation",
6788
+ tooltip: "Lets people speak into the microphone instead of typing, and turns what the agent hears into text. This is the opposite of the voice settings on the other sub-tabs: those decide how the agent sounds, this decides how well it understands what is said to it.",
6789
+ helpText: "When on, a microphone button appears in the chat box."
6790
+ },
6791
+ language: {
6792
+ label: "Spoken language",
6793
+ tooltip: "The language people will speak in. Naming it up front makes transcription noticeably more accurate. Leave it unset and the provider guesses from the audio.",
6794
+ placeholder: "Detect automatically"
6795
+ },
6796
+ instructions: {
6797
+ label: "Transcription hints",
6798
+ tooltip: "Words the transcriber is likely to get wrong — product names, acronyms, people, technical terms. Listing them here helps it spell them correctly.",
6799
+ placeholder: "e.g. Expect the terms ibl.ai, LTI, and Open edX. Spell names as written.",
6800
+ helpText: "Optional. Some transcription providers use these hints; the ones that do not simply ignore them. Leave blank if you are not sure."
6801
+ },
6802
+ saveButton: "Save dictation",
6803
+ savingButton: "Saving…"
6804
+ },
6784
6805
  callConfig: {
6785
6806
  title: "Voice call",
6786
6807
  description: "Customize how voice calls work. Screen sharing always uses live conversation.",
@@ -6832,7 +6853,9 @@ var voiceTabLabels$3 = {
6832
6853
  voiceSaved: "Voice saved",
6833
6854
  voiceError: "Couldn't save voice settings",
6834
6855
  callConfigSaved: "Voice call settings saved",
6835
- callConfigError: "Couldn't save voice call settings"
6856
+ callConfigError: "Couldn't save voice call settings",
6857
+ transcriptionSaved: "Dictation settings saved",
6858
+ transcriptionError: "Couldn't save dictation settings"
6836
6859
  }
6837
6860
  };
6838
6861
  var voiceTabVoicePicker$3 = {
@@ -13884,7 +13907,8 @@ var voiceTabLabels$2 = {
13884
13907
  },
13885
13908
  subTabs: {
13886
13909
  voice: "Voix",
13887
- callConfig: "Appel vocal"
13910
+ callConfig: "Appel vocal",
13911
+ settings: "Paramètres"
13888
13912
  },
13889
13913
  mentorVoice: {
13890
13914
  title: "Voix de l'agent",
@@ -13945,6 +13969,26 @@ var voiceTabLabels$2 = {
13945
13969
  }
13946
13970
  }
13947
13971
  },
13972
+ transcription: {
13973
+ toggle: {
13974
+ label: "Dictée",
13975
+ tooltip: "Permet aux utilisateurs de parler dans le micro plutôt que d'écrire, et transforme en texte ce que l'agent entend. C'est l'inverse des réglages vocaux des autres sous-onglets : ceux-ci déterminent la voix de l'agent, celui-ci détermine sa capacité à comprendre ce qu'on lui dit.",
13976
+ helpText: "Une fois activée, un bouton micro apparaît dans la zone de discussion."
13977
+ },
13978
+ language: {
13979
+ label: "Langue parlée",
13980
+ tooltip: "La langue dans laquelle les utilisateurs vont parler. L'indiquer à l'avance rend la transcription nettement plus précise. Si vous ne la définissez pas, le fournisseur la devine à partir de l'audio.",
13981
+ placeholder: "Détecter automatiquement"
13982
+ },
13983
+ instructions: {
13984
+ label: "Indications de transcription",
13985
+ tooltip: "Les mots que la transcription risque d'écorcher : noms de produits, sigles, personnes, termes techniques. Les lister ici l'aide à les orthographier correctement.",
13986
+ placeholder: "ex. : Attendez-vous aux termes ibl.ai, LTI et Open edX. Orthographiez les noms tels quels.",
13987
+ helpText: "Facultatif. Certains fournisseurs de transcription utilisent ces indications ; les autres les ignorent tout simplement. Laissez vide en cas de doute."
13988
+ },
13989
+ saveButton: "Enregistrer la dictée",
13990
+ savingButton: "Enregistrement…"
13991
+ },
13948
13992
  callConfig: {
13949
13993
  title: "Appel vocal",
13950
13994
  description: "Personnalisez le fonctionnement des appels vocaux. Le partage d'écran utilise toujours la conversation en direct.",
@@ -13996,7 +14040,9 @@ var voiceTabLabels$2 = {
13996
14040
  voiceSaved: "Voix enregistrée",
13997
14041
  voiceError: "Impossible d'enregistrer les paramètres vocaux",
13998
14042
  callConfigSaved: "Paramètres d'appel vocal enregistrés",
13999
- callConfigError: "Impossible d'enregistrer les paramètres d'appel vocal"
14043
+ callConfigError: "Impossible d'enregistrer les paramètres d'appel vocal",
14044
+ transcriptionSaved: "Paramètres de dictée enregistrés",
14045
+ transcriptionError: "Impossible d'enregistrer les paramètres de dictée"
14000
14046
  }
14001
14047
  };
14002
14048
  var voiceTabVoicePicker$2 = {
@@ -20882,7 +20928,8 @@ var voiceTabLabels$1 = {
20882
20928
  },
20883
20929
  subTabs: {
20884
20930
  voice: "Voz",
20885
- callConfig: "Llamada de voz"
20931
+ callConfig: "Llamada de voz",
20932
+ settings: "Ajustes"
20886
20933
  },
20887
20934
  mentorVoice: {
20888
20935
  title: "Voz del agente",
@@ -20943,6 +20990,26 @@ var voiceTabLabels$1 = {
20943
20990
  }
20944
20991
  }
20945
20992
  },
20993
+ transcription: {
20994
+ toggle: {
20995
+ label: "Dictado",
20996
+ tooltip: "Permite que las personas hablen por el micrófono en lugar de escribir, y convierte en texto lo que el agente escucha. Es lo contrario de los ajustes de voz de las otras subpestañas: aquellos deciden cómo suena el agente, este decide qué tan bien entiende lo que se le dice.",
20997
+ helpText: "Cuando está activado, aparece un botón de micrófono en el cuadro de chat."
20998
+ },
20999
+ language: {
21000
+ label: "Idioma hablado",
21001
+ tooltip: "El idioma en el que hablarán las personas. Indicarlo de antemano hace que la transcripción sea notablemente más precisa. Si lo dejas sin definir, el proveedor lo deduce del audio.",
21002
+ placeholder: "Detectar automáticamente"
21003
+ },
21004
+ instructions: {
21005
+ label: "Sugerencias de transcripción",
21006
+ tooltip: "Palabras que la transcripción probablemente escriba mal: nombres de productos, siglas, personas, términos técnicos. Enumerarlas aquí ayuda a escribirlas correctamente.",
21007
+ placeholder: "p. ej. Espera los términos ibl.ai, LTI y Open edX. Escribe los nombres tal cual.",
21008
+ helpText: "Opcional. Algunos proveedores de transcripción usan estas sugerencias; los que no, simplemente las ignoran. Déjalo en blanco si tienes dudas."
21009
+ },
21010
+ saveButton: "Guardar dictado",
21011
+ savingButton: "Guardando…"
21012
+ },
20946
21013
  callConfig: {
20947
21014
  title: "Llamada de voz",
20948
21015
  description: "Personaliza cómo funcionan las llamadas de voz. El uso compartido de pantalla siempre usa conversación en vivo.",
@@ -20994,7 +21061,9 @@ var voiceTabLabels$1 = {
20994
21061
  voiceSaved: "Voz guardada",
20995
21062
  voiceError: "No se pudo guardar la configuración de voz",
20996
21063
  callConfigSaved: "Configuración de llamada de voz guardada",
20997
- callConfigError: "No se pudo guardar la configuración de llamada de voz"
21064
+ callConfigError: "No se pudo guardar la configuración de llamada de voz",
21065
+ transcriptionSaved: "Ajustes de dictado guardados",
21066
+ transcriptionError: "No se pudieron guardar los ajustes de dictado"
20998
21067
  }
20999
21068
  };
21000
21069
  var voiceTabVoicePicker$1 = {
@@ -27880,7 +27949,8 @@ var voiceTabLabels = {
27880
27949
  },
27881
27950
  subTabs: {
27882
27951
  voice: "语音",
27883
- callConfig: "语音通话"
27952
+ callConfig: "语音通话",
27953
+ settings: "设置"
27884
27954
  },
27885
27955
  mentorVoice: {
27886
27956
  title: "智能体声音",
@@ -27941,6 +28011,26 @@ var voiceTabLabels = {
27941
28011
  }
27942
28012
  }
27943
28013
  },
28014
+ transcription: {
28015
+ toggle: {
28016
+ label: "语音听写",
28017
+ tooltip: "让用户用麦克风说话而不是打字,并将智能体听到的内容转写为文字。这与其他子标签页的语音设置方向相反:那些决定智能体如何发声,这里决定它能多准确地听懂别人说的话。",
28018
+ helpText: "开启后,聊天框中会显示麦克风按钮。"
28019
+ },
28020
+ language: {
28021
+ label: "口语语言",
28022
+ tooltip: "用户将使用的语言。预先指定可以明显提高转写准确率。若不设置,服务商会根据音频自行判断。",
28023
+ placeholder: "自动检测"
28024
+ },
28025
+ instructions: {
28026
+ label: "转写提示",
28027
+ tooltip: "转写容易写错的词:产品名、缩写、人名、专业术语。在此列出可帮助它正确拼写。",
28028
+ placeholder: "例如:会出现 ibl.ai、LTI 和 Open edX 等术语。人名按原样拼写。",
28029
+ helpText: "选填。部分转写服务商会使用这些提示,不支持的则直接忽略。不确定时可留空。"
28030
+ },
28031
+ saveButton: "保存听写设置",
28032
+ savingButton: "保存中…"
28033
+ },
27944
28034
  callConfig: {
27945
28035
  title: "语音通话",
27946
28036
  description: "自定义语音通话的工作方式。屏幕共享始终使用实时对话。",
@@ -27992,7 +28082,9 @@ var voiceTabLabels = {
27992
28082
  voiceSaved: "声音已保存",
27993
28083
  voiceError: "无法保存语音设置",
27994
28084
  callConfigSaved: "语音通话设置已保存",
27995
- callConfigError: "无法保存语音通话设置"
28085
+ callConfigError: "无法保存语音通话设置",
28086
+ transcriptionSaved: "听写设置已保存",
28087
+ transcriptionError: "无法保存听写设置"
27996
28088
  }
27997
28089
  };
27998
28090
  var voiceTabVoicePicker = {
@@ -239096,6 +239188,7 @@ function ChatInputForm({ onSubmit, onScreenSharingClick, isScreenSharingModalOpe
239096
239188
  const { handleMicrophoneBtnClick, processing, recording, time } = useVoiceChat({
239097
239189
  sendMessage: handleSelectPrompt,
239098
239190
  tenantKey,
239191
+ mentorId,
239099
239192
  });
239100
239193
  const attachedFiles = useSelector(selectAttachedFiles);
239101
239194
  // Check if any files are currently uploading
@@ -102265,7 +102265,8 @@ var voiceTabLabels$3 = {
102265
102265
  },
102266
102266
  subTabs: {
102267
102267
  voice: "Voice",
102268
- callConfig: "Voice call"
102268
+ callConfig: "Voice Call",
102269
+ settings: "Settings"
102269
102270
  },
102270
102271
  mentorVoice: {
102271
102272
  title: "Agent Voice",
@@ -102326,6 +102327,26 @@ var voiceTabLabels$3 = {
102326
102327
  }
102327
102328
  }
102328
102329
  },
102330
+ transcription: {
102331
+ toggle: {
102332
+ label: "Dictation",
102333
+ tooltip: "Lets people speak into the microphone instead of typing, and turns what the agent hears into text. This is the opposite of the voice settings on the other sub-tabs: those decide how the agent sounds, this decides how well it understands what is said to it.",
102334
+ helpText: "When on, a microphone button appears in the chat box."
102335
+ },
102336
+ language: {
102337
+ label: "Spoken language",
102338
+ tooltip: "The language people will speak in. Naming it up front makes transcription noticeably more accurate. Leave it unset and the provider guesses from the audio.",
102339
+ placeholder: "Detect automatically"
102340
+ },
102341
+ instructions: {
102342
+ label: "Transcription hints",
102343
+ tooltip: "Words the transcriber is likely to get wrong — product names, acronyms, people, technical terms. Listing them here helps it spell them correctly.",
102344
+ placeholder: "e.g. Expect the terms ibl.ai, LTI, and Open edX. Spell names as written.",
102345
+ helpText: "Optional. Some transcription providers use these hints; the ones that do not simply ignore them. Leave blank if you are not sure."
102346
+ },
102347
+ saveButton: "Save dictation",
102348
+ savingButton: "Saving…"
102349
+ },
102329
102350
  callConfig: {
102330
102351
  title: "Voice call",
102331
102352
  description: "Customize how voice calls work. Screen sharing always uses live conversation.",
@@ -102377,7 +102398,9 @@ var voiceTabLabels$3 = {
102377
102398
  voiceSaved: "Voice saved",
102378
102399
  voiceError: "Couldn't save voice settings",
102379
102400
  callConfigSaved: "Voice call settings saved",
102380
- callConfigError: "Couldn't save voice call settings"
102401
+ callConfigError: "Couldn't save voice call settings",
102402
+ transcriptionSaved: "Dictation settings saved",
102403
+ transcriptionError: "Couldn't save dictation settings"
102381
102404
  }
102382
102405
  };
102383
102406
  var voiceTabVoicePicker$3 = {
@@ -109429,7 +109452,8 @@ var voiceTabLabels$2 = {
109429
109452
  },
109430
109453
  subTabs: {
109431
109454
  voice: "Voix",
109432
- callConfig: "Appel vocal"
109455
+ callConfig: "Appel vocal",
109456
+ settings: "Paramètres"
109433
109457
  },
109434
109458
  mentorVoice: {
109435
109459
  title: "Voix de l'agent",
@@ -109490,6 +109514,26 @@ var voiceTabLabels$2 = {
109490
109514
  }
109491
109515
  }
109492
109516
  },
109517
+ transcription: {
109518
+ toggle: {
109519
+ label: "Dictée",
109520
+ tooltip: "Permet aux utilisateurs de parler dans le micro plutôt que d'écrire, et transforme en texte ce que l'agent entend. C'est l'inverse des réglages vocaux des autres sous-onglets : ceux-ci déterminent la voix de l'agent, celui-ci détermine sa capacité à comprendre ce qu'on lui dit.",
109521
+ helpText: "Une fois activée, un bouton micro apparaît dans la zone de discussion."
109522
+ },
109523
+ language: {
109524
+ label: "Langue parlée",
109525
+ tooltip: "La langue dans laquelle les utilisateurs vont parler. L'indiquer à l'avance rend la transcription nettement plus précise. Si vous ne la définissez pas, le fournisseur la devine à partir de l'audio.",
109526
+ placeholder: "Détecter automatiquement"
109527
+ },
109528
+ instructions: {
109529
+ label: "Indications de transcription",
109530
+ tooltip: "Les mots que la transcription risque d'écorcher : noms de produits, sigles, personnes, termes techniques. Les lister ici l'aide à les orthographier correctement.",
109531
+ placeholder: "ex. : Attendez-vous aux termes ibl.ai, LTI et Open edX. Orthographiez les noms tels quels.",
109532
+ helpText: "Facultatif. Certains fournisseurs de transcription utilisent ces indications ; les autres les ignorent tout simplement. Laissez vide en cas de doute."
109533
+ },
109534
+ saveButton: "Enregistrer la dictée",
109535
+ savingButton: "Enregistrement…"
109536
+ },
109493
109537
  callConfig: {
109494
109538
  title: "Appel vocal",
109495
109539
  description: "Personnalisez le fonctionnement des appels vocaux. Le partage d'écran utilise toujours la conversation en direct.",
@@ -109541,7 +109585,9 @@ var voiceTabLabels$2 = {
109541
109585
  voiceSaved: "Voix enregistrée",
109542
109586
  voiceError: "Impossible d'enregistrer les paramètres vocaux",
109543
109587
  callConfigSaved: "Paramètres d'appel vocal enregistrés",
109544
- callConfigError: "Impossible d'enregistrer les paramètres d'appel vocal"
109588
+ callConfigError: "Impossible d'enregistrer les paramètres d'appel vocal",
109589
+ transcriptionSaved: "Paramètres de dictée enregistrés",
109590
+ transcriptionError: "Impossible d'enregistrer les paramètres de dictée"
109545
109591
  }
109546
109592
  };
109547
109593
  var voiceTabVoicePicker$2 = {
@@ -116427,7 +116473,8 @@ var voiceTabLabels$1 = {
116427
116473
  },
116428
116474
  subTabs: {
116429
116475
  voice: "Voz",
116430
- callConfig: "Llamada de voz"
116476
+ callConfig: "Llamada de voz",
116477
+ settings: "Ajustes"
116431
116478
  },
116432
116479
  mentorVoice: {
116433
116480
  title: "Voz del agente",
@@ -116488,6 +116535,26 @@ var voiceTabLabels$1 = {
116488
116535
  }
116489
116536
  }
116490
116537
  },
116538
+ transcription: {
116539
+ toggle: {
116540
+ label: "Dictado",
116541
+ tooltip: "Permite que las personas hablen por el micrófono en lugar de escribir, y convierte en texto lo que el agente escucha. Es lo contrario de los ajustes de voz de las otras subpestañas: aquellos deciden cómo suena el agente, este decide qué tan bien entiende lo que se le dice.",
116542
+ helpText: "Cuando está activado, aparece un botón de micrófono en el cuadro de chat."
116543
+ },
116544
+ language: {
116545
+ label: "Idioma hablado",
116546
+ tooltip: "El idioma en el que hablarán las personas. Indicarlo de antemano hace que la transcripción sea notablemente más precisa. Si lo dejas sin definir, el proveedor lo deduce del audio.",
116547
+ placeholder: "Detectar automáticamente"
116548
+ },
116549
+ instructions: {
116550
+ label: "Sugerencias de transcripción",
116551
+ tooltip: "Palabras que la transcripción probablemente escriba mal: nombres de productos, siglas, personas, términos técnicos. Enumerarlas aquí ayuda a escribirlas correctamente.",
116552
+ placeholder: "p. ej. Espera los términos ibl.ai, LTI y Open edX. Escribe los nombres tal cual.",
116553
+ helpText: "Opcional. Algunos proveedores de transcripción usan estas sugerencias; los que no, simplemente las ignoran. Déjalo en blanco si tienes dudas."
116554
+ },
116555
+ saveButton: "Guardar dictado",
116556
+ savingButton: "Guardando…"
116557
+ },
116491
116558
  callConfig: {
116492
116559
  title: "Llamada de voz",
116493
116560
  description: "Personaliza cómo funcionan las llamadas de voz. El uso compartido de pantalla siempre usa conversación en vivo.",
@@ -116539,7 +116606,9 @@ var voiceTabLabels$1 = {
116539
116606
  voiceSaved: "Voz guardada",
116540
116607
  voiceError: "No se pudo guardar la configuración de voz",
116541
116608
  callConfigSaved: "Configuración de llamada de voz guardada",
116542
- callConfigError: "No se pudo guardar la configuración de llamada de voz"
116609
+ callConfigError: "No se pudo guardar la configuración de llamada de voz",
116610
+ transcriptionSaved: "Ajustes de dictado guardados",
116611
+ transcriptionError: "No se pudieron guardar los ajustes de dictado"
116543
116612
  }
116544
116613
  };
116545
116614
  var voiceTabVoicePicker$1 = {
@@ -123425,7 +123494,8 @@ var voiceTabLabels = {
123425
123494
  },
123426
123495
  subTabs: {
123427
123496
  voice: "语音",
123428
- callConfig: "语音通话"
123497
+ callConfig: "语音通话",
123498
+ settings: "设置"
123429
123499
  },
123430
123500
  mentorVoice: {
123431
123501
  title: "智能体声音",
@@ -123486,6 +123556,26 @@ var voiceTabLabels = {
123486
123556
  }
123487
123557
  }
123488
123558
  },
123559
+ transcription: {
123560
+ toggle: {
123561
+ label: "语音听写",
123562
+ tooltip: "让用户用麦克风说话而不是打字,并将智能体听到的内容转写为文字。这与其他子标签页的语音设置方向相反:那些决定智能体如何发声,这里决定它能多准确地听懂别人说的话。",
123563
+ helpText: "开启后,聊天框中会显示麦克风按钮。"
123564
+ },
123565
+ language: {
123566
+ label: "口语语言",
123567
+ tooltip: "用户将使用的语言。预先指定可以明显提高转写准确率。若不设置,服务商会根据音频自行判断。",
123568
+ placeholder: "自动检测"
123569
+ },
123570
+ instructions: {
123571
+ label: "转写提示",
123572
+ tooltip: "转写容易写错的词:产品名、缩写、人名、专业术语。在此列出可帮助它正确拼写。",
123573
+ placeholder: "例如:会出现 ibl.ai、LTI 和 Open edX 等术语。人名按原样拼写。",
123574
+ helpText: "选填。部分转写服务商会使用这些提示,不支持的则直接忽略。不确定时可留空。"
123575
+ },
123576
+ saveButton: "保存听写设置",
123577
+ savingButton: "保存中…"
123578
+ },
123489
123579
  callConfig: {
123490
123580
  title: "语音通话",
123491
123581
  description: "自定义语音通话的工作方式。屏幕共享始终使用实时对话。",
@@ -123537,7 +123627,9 @@ var voiceTabLabels = {
123537
123627
  voiceSaved: "声音已保存",
123538
123628
  voiceError: "无法保存语音设置",
123539
123629
  callConfigSaved: "语音通话设置已保存",
123540
- callConfigError: "无法保存语音通话设置"
123630
+ callConfigError: "无法保存语音通话设置",
123631
+ transcriptionSaved: "听写设置已保存",
123632
+ transcriptionError: "无法保存听写设置"
123541
123633
  }
123542
123634
  };
123543
123635
  var voiceTabVoicePicker = {
@@ -261873,6 +261965,7 @@ function ChatInputForm({ onSubmit, onScreenSharingClick, isScreenSharingModalOpe
261873
261965
  const { handleMicrophoneBtnClick, processing, recording, time } = useVoiceChat({
261874
261966
  sendMessage: handleSelectPrompt,
261875
261967
  tenantKey,
261968
+ mentorId,
261876
261969
  });
261877
261970
  const attachedFiles = useSelector(selectAttachedFiles);
261878
261971
  // Check if any files are currently uploading
@@ -273782,7 +273875,8 @@ const AGENT_VOICE_TAB_LABELS = {
273782
273875
  },
273783
273876
  subTabs: {
273784
273877
  voice: 'Voice',
273785
- callConfig: 'Voice call',
273878
+ callConfig: 'Voice Call',
273879
+ settings: 'Settings',
273786
273880
  },
273787
273881
  mentorVoice: {
273788
273882
  title: 'Agent Voice',
@@ -273840,6 +273934,26 @@ const AGENT_VOICE_TAB_LABELS = {
273840
273934
  },
273841
273935
  },
273842
273936
  },
273937
+ transcription: {
273938
+ toggle: {
273939
+ label: 'Dictation',
273940
+ tooltip: 'Lets people speak into the microphone instead of typing, and turns what the agent hears into text. This is the opposite of the voice settings on the other sub-tabs: those decide how the agent sounds, this decides how well it understands what is said to it.',
273941
+ helpText: 'When on, a microphone button appears in the chat box.',
273942
+ },
273943
+ language: {
273944
+ label: 'Spoken language',
273945
+ tooltip: 'The language people will speak in. Naming it up front makes transcription noticeably more accurate. Leave it unset and the provider guesses from the audio.',
273946
+ placeholder: 'Detect automatically',
273947
+ },
273948
+ instructions: {
273949
+ label: 'Transcription hints',
273950
+ tooltip: 'Words the transcriber is likely to get wrong — product names, acronyms, people, technical terms. Listing them here helps it spell them correctly.',
273951
+ placeholder: 'e.g. Expect the terms ibl.ai, LTI, and Open edX. Spell names as written.',
273952
+ helpText: 'Optional. Some transcription providers use these hints; the ones that do not simply ignore them. Leave blank if you are not sure.',
273953
+ },
273954
+ saveButton: 'Save dictation',
273955
+ savingButton: 'Saving…',
273956
+ },
273843
273957
  callConfig: {
273844
273958
  title: 'Voice call',
273845
273959
  description: 'Customize how voice calls work. Screen sharing always uses live conversation.',
@@ -273891,6 +274005,8 @@ const AGENT_VOICE_TAB_LABELS = {
273891
274005
  voiceError: "Couldn't save voice settings",
273892
274006
  callConfigSaved: 'Voice call settings saved',
273893
274007
  callConfigError: "Couldn't save voice call settings",
274008
+ transcriptionSaved: 'Dictation settings saved',
274009
+ transcriptionError: "Couldn't save dictation settings",
273894
274010
  },
273895
274011
  };
273896
274012
  function buildVoiceTabLabels(t) {
@@ -273907,6 +274023,7 @@ function buildVoiceTabLabels(t) {
273907
274023
  subTabs: {
273908
274024
  voice: t('voiceTabLabels.subTabs.voice'),
273909
274025
  callConfig: t('voiceTabLabels.subTabs.callConfig'),
274026
+ settings: t('voiceTabLabels.subTabs.settings'),
273910
274027
  },
273911
274028
  mentorVoice: {
273912
274029
  title: t('voiceTabLabels.mentorVoice.title'),
@@ -273966,6 +274083,26 @@ function buildVoiceTabLabels(t) {
273966
274083
  },
273967
274084
  },
273968
274085
  },
274086
+ transcription: {
274087
+ toggle: {
274088
+ label: t('voiceTabLabels.transcription.toggle.label'),
274089
+ tooltip: t('voiceTabLabels.transcription.toggle.tooltip'),
274090
+ helpText: t('voiceTabLabels.transcription.toggle.helpText'),
274091
+ },
274092
+ language: {
274093
+ label: t('voiceTabLabels.transcription.language.label'),
274094
+ tooltip: t('voiceTabLabels.transcription.language.tooltip'),
274095
+ placeholder: t('voiceTabLabels.transcription.language.placeholder'),
274096
+ },
274097
+ instructions: {
274098
+ label: t('voiceTabLabels.transcription.instructions.label'),
274099
+ tooltip: t('voiceTabLabels.transcription.instructions.tooltip'),
274100
+ placeholder: t('voiceTabLabels.transcription.instructions.placeholder'),
274101
+ helpText: t('voiceTabLabels.transcription.instructions.helpText'),
274102
+ },
274103
+ saveButton: t('voiceTabLabels.transcription.saveButton'),
274104
+ savingButton: t('voiceTabLabels.transcription.savingButton'),
274105
+ },
273969
274106
  callConfig: {
273970
274107
  title: t('voiceTabLabels.callConfig.title'),
273971
274108
  description: t('voiceTabLabels.callConfig.description'),
@@ -274018,6 +274155,8 @@ function buildVoiceTabLabels(t) {
274018
274155
  voiceError: t('voiceTabLabels.toasts.voiceError'),
274019
274156
  callConfigSaved: t('voiceTabLabels.toasts.callConfigSaved'),
274020
274157
  callConfigError: t('voiceTabLabels.toasts.callConfigError'),
274158
+ transcriptionSaved: t('voiceTabLabels.toasts.transcriptionSaved'),
274159
+ transcriptionError: t('voiceTabLabels.toasts.transcriptionError'),
274021
274160
  },
274022
274161
  };
274023
274162
  }
@@ -274577,6 +274716,39 @@ function FieldWrap({ label, tooltip, children }) {
274577
274716
  return (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { className: "text-sm font-medium text-[#646464]", children: label }), tooltip && (jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { type: "button", "aria-label": t('voiceTabCallConfigSection.moreInfoAbout', { label }), children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { side: "top", align: "start", sideOffset: 6, collisionPadding: 16, className: "ibl-tooltip-content z-50 max-w-[300px]", children: jsx("p", { className: "text-xs leading-relaxed whitespace-normal", children: tooltip }) })] }) }))] }), children] }));
274578
274717
  }
274579
274718
 
274719
+ /**
274720
+ * Languages offered for **speech-to-text** (dictation) transcription.
274721
+ *
274722
+ * Deliberately separate from `LANGUAGE_OPTIONS` in `call-config-section.tsx`,
274723
+ * which is the realtime *voice call* list and carries different provider
274724
+ * constraints. These 17 are the major languages the backend confirms are safe
274725
+ * across all four transcription providers — do not reorder or prune without a
274726
+ * backend change.
274727
+ *
274728
+ * Display names are the English endonym-free common names, hardcoded here the
274729
+ * same way the neighbouring realtime `LANGUAGE_OPTIONS` list does it — a
274730
+ * 17-entry translation layer per locale buys nothing an admin would notice.
274731
+ */
274732
+ const TRANSCRIPTION_LANGUAGE_OPTIONS = [
274733
+ { value: 'en', label: 'English' },
274734
+ { value: 'es', label: 'Spanish' },
274735
+ { value: 'fr', label: 'French' },
274736
+ { value: 'de', label: 'German' },
274737
+ { value: 'pt', label: 'Portuguese' },
274738
+ { value: 'it', label: 'Italian' },
274739
+ { value: 'nl', label: 'Dutch' },
274740
+ { value: 'sv', label: 'Swedish' },
274741
+ { value: 'pl', label: 'Polish' },
274742
+ { value: 'tr', label: 'Turkish' },
274743
+ { value: 'ru', label: 'Russian' },
274744
+ { value: 'ar', label: 'Arabic' },
274745
+ { value: 'hi', label: 'Hindi' },
274746
+ { value: 'ja', label: 'Japanese' },
274747
+ { value: 'ko', label: 'Korean' },
274748
+ { value: 'zh', label: 'Chinese' },
274749
+ { value: 'la', label: 'Latin' },
274750
+ ];
274751
+
274580
274752
  /**
274581
274753
  * Display-only card for a single prompt. Mirrors the visual pattern
274582
274754
  * used by `AgentPromptsTab` (System Prompt / Proactive Prompt cards):
@@ -274616,13 +274788,31 @@ function usesVoiceCatalogue(provider) {
274616
274788
  const SUB_TAB_VALUES = {
274617
274789
  voice: 'voice',
274618
274790
  callConfig: 'call-config',
274791
+ settings: 'settings',
274619
274792
  };
274793
+ /**
274794
+ * Reverse of `SUB_TAB_VALUES`. `<Tabs>` is controlled here (the pinned save
274795
+ * footer needs to know which sub-tab is open), and it hands back the Radix
274796
+ * *value*, which we map back to the `SubTab` key we hold in state.
274797
+ */
274798
+ const SUB_TAB_KEYS = Object.fromEntries(Object.entries(SUB_TAB_VALUES).map(([key, value]) => [value, key]));
274620
274799
  /**
274621
274800
  * Soft cap for the voice-instructions textarea. The backend column is an
274622
274801
  * unbounded TextField — this is purely a UI guardrail (TTS style prompts
274623
274802
  * gain nothing from being longer than this).
274624
274803
  */
274625
274804
  const VOICE_INSTRUCTIONS_MAX = 1000;
274805
+ /**
274806
+ * Soft cap for the transcription-hints textarea. Same reasoning as
274807
+ * `VOICE_INSTRUCTIONS_MAX`: the backend column is unbounded, but a hint list
274808
+ * longer than this stops helping the transcriber and starts costing tokens.
274809
+ */
274810
+ const TRANSCRIPTION_INSTRUCTIONS_MAX = 1000;
274811
+ /**
274812
+ * `<Select>` cannot hold an empty-string item value, so the "not set" choice
274813
+ * needs a sentinel. It is mapped back to `''` before anything is sent.
274814
+ */
274815
+ const TRANSCRIPTION_LANGUAGE_UNSET = '__unset__';
274626
274816
  /**
274627
274817
  * Pull a voice **name** out of the mentor settings response, which can
274628
274818
  * return a plain string, a numeric id, or a serialized object with a
@@ -274639,7 +274829,7 @@ function readVoiceName(value) {
274639
274829
  return null;
274640
274830
  }
274641
274831
  function AgentVoiceTab({ labels: labelsOverride, defaultSubTab = 'voice', renderPromptContent, tenantKey: tenantKeyProp, mentorId: mentorIdProp, username: usernameProp, enableRBAC: enableRBACProp, }) {
274642
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
274832
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
274643
274833
  const ctx = useAgentSettingsOptional();
274644
274834
  const tenantKey = (_a = tenantKeyProp !== null && tenantKeyProp !== void 0 ? tenantKeyProp : ctx === null || ctx === void 0 ? void 0 : ctx.tenantKey) !== null && _a !== void 0 ? _a : '';
274645
274835
  const mentorId = (_b = mentorIdProp !== null && mentorIdProp !== void 0 ? mentorIdProp : ctx === null || ctx === void 0 ? void 0 : ctx.mentorId) !== null && _b !== void 0 ? _b : '';
@@ -274684,6 +274874,14 @@ function AgentVoiceTab({ labels: labelsOverride, defaultSubTab = 'voice', render
274684
274874
  name: savedIblaiName,
274685
274875
  });
274686
274876
  const savedInstructions = typeof (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.voice_instructions) === 'string' ? mentorSettings.voice_instructions : '';
274877
+ // Transcription (speech-to-text) — an unset language is a first-class
274878
+ // state, so it stays `''` rather than defaulting to English.
274879
+ const savedTranscriptionLanguage = typeof (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.transcription_language) === 'string'
274880
+ ? mentorSettings.transcription_language
274881
+ : '';
274882
+ const savedTranscriptionInstructions = typeof (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.transcription_instructions) === 'string'
274883
+ ? mentorSettings.transcription_instructions
274884
+ : '';
274687
274885
  // Picked voices override the resolved server values once the user
274688
274886
  // makes a choice. Null means "use what came from the server".
274689
274887
  const [provider, setProvider] = React__default.useState(initialProvider);
@@ -274691,13 +274889,20 @@ function AgentVoiceTab({ labels: labelsOverride, defaultSubTab = 'voice', render
274691
274889
  const [pickedGoogleVoice, setPickedGoogleVoice] = React__default.useState(null);
274692
274890
  const [pickedIblaiVoice, setPickedIblaiVoice] = React__default.useState(null);
274693
274891
  const [instructions, setInstructions] = React__default.useState(savedInstructions);
274892
+ const [transcriptionLanguage, setTranscriptionLanguage] = React__default.useState(savedTranscriptionLanguage);
274893
+ const [transcriptionInstructions, setTranscriptionInstructions] = React__default.useState(savedTranscriptionInstructions);
274694
274894
  const [selectedPrompt, setSelectedPrompt] = React__default.useState(null);
274895
+ // Controlled so the pinned footer below the scroll area knows which
274896
+ // sub-tab's Save it should be rendering.
274897
+ const [subTab, setSubTab] = React__default.useState(defaultSubTab);
274695
274898
  React__default.useEffect(() => {
274696
274899
  setProvider(initialProvider);
274697
274900
  setPickedOpenaiVoice(null);
274698
274901
  setPickedGoogleVoice(null);
274699
274902
  setPickedIblaiVoice(null);
274700
274903
  setInstructions(savedInstructions);
274904
+ setTranscriptionLanguage(savedTranscriptionLanguage);
274905
+ setTranscriptionInstructions(savedTranscriptionInstructions);
274701
274906
  // eslint-disable-next-line react-hooks/exhaustive-deps
274702
274907
  }, [mentorId, rawMentorSettings]);
274703
274908
  const effectiveOpenaiVoice = (_j = pickedOpenaiVoice !== null && pickedOpenaiVoice !== void 0 ? pickedOpenaiVoice : resolvedOpenaiVoice) !== null && _j !== void 0 ? _j : null;
@@ -274735,11 +274940,46 @@ function AgentVoiceTab({ labels: labelsOverride, defaultSubTab = 'voice', render
274735
274940
  setIsTogglingVoiceCall(false);
274736
274941
  }
274737
274942
  };
274943
+ // "Dictation" master toggle. Tracked separately from the voice-call
274944
+ // toggle — sharing `isTogglingVoiceCall` would make flipping one switch
274945
+ // disable the other.
274946
+ const serverVoiceRecordEnabled = (_o = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.show_voice_record) !== null && _o !== void 0 ? _o : true;
274947
+ const [voiceRecordEnabled, setVoiceRecordEnabled] = React__default.useState(serverVoiceRecordEnabled);
274948
+ const [isTogglingVoiceRecord, setIsTogglingVoiceRecord] = React__default.useState(false);
274949
+ React__default.useEffect(() => {
274950
+ setVoiceRecordEnabled(serverVoiceRecordEnabled);
274951
+ }, [serverVoiceRecordEnabled]);
274952
+ const handleToggleVoiceRecord = async (checked) => {
274953
+ setVoiceRecordEnabled(checked); // optimistic
274954
+ setIsTogglingVoiceRecord(true);
274955
+ try {
274956
+ await editMentor({
274957
+ mentor: mentorId,
274958
+ org: tenantKey,
274959
+ // @ts-expect-error userId is accepted at runtime
274960
+ userId: username,
274961
+ formData: { show_voice_record: checked },
274962
+ }).unwrap();
274963
+ toast.success(labels.toasts.transcriptionSaved);
274964
+ }
274965
+ catch (error) {
274966
+ console.error('[AgentVoiceTab] toggle dictation error', error);
274967
+ setVoiceRecordEnabled(!checked); // roll back
274968
+ toast.error(labels.toasts.transcriptionError);
274969
+ }
274970
+ finally {
274971
+ setIsTogglingVoiceRecord(false);
274972
+ }
274973
+ };
274738
274974
  const isDisabled = isMentorSettingsLoading || isSavingMentor;
274739
274975
  const instructionsDirty = instructions !== savedInstructions;
274740
274976
  // Soft cap only — the modal editor doesn't enforce a max length, so we
274741
274977
  // block Save instead of truncating what the user wrote.
274742
274978
  const instructionsOverCap = instructions.length > VOICE_INSTRUCTIONS_MAX;
274979
+ const transcriptionLanguageDirty = transcriptionLanguage !== savedTranscriptionLanguage;
274980
+ const transcriptionInstructionsDirty = transcriptionInstructions !== savedTranscriptionInstructions;
274981
+ const transcriptionOverCap = transcriptionInstructions.length > TRANSCRIPTION_INSTRUCTIONS_MAX;
274982
+ const transcriptionDirty = transcriptionLanguageDirty || transcriptionInstructionsDirty;
274743
274983
  const isDirty = provider !== initialProvider ||
274744
274984
  pickedOpenaiVoice != null ||
274745
274985
  pickedGoogleVoice != null ||
@@ -274820,6 +275060,31 @@ function AgentVoiceTab({ labels: labelsOverride, defaultSubTab = 'voice', render
274820
275060
  toast.error(labels.toasts.voiceError);
274821
275061
  }
274822
275062
  };
275063
+ const handleSaveTranscription = async () => {
275064
+ try {
275065
+ // Same "absent = keep" contract as the voice save above: only send
275066
+ // what the admin actually touched.
275067
+ const formData = {};
275068
+ if (transcriptionLanguageDirty) {
275069
+ formData.transcription_language = transcriptionLanguage;
275070
+ }
275071
+ if (transcriptionInstructionsDirty) {
275072
+ formData.transcription_instructions = transcriptionInstructions;
275073
+ }
275074
+ await editMentor({
275075
+ mentor: mentorId,
275076
+ org: tenantKey,
275077
+ // @ts-expect-error userId is accepted at runtime
275078
+ userId: username,
275079
+ formData,
275080
+ }).unwrap();
275081
+ toast.success(labels.toasts.transcriptionSaved);
275082
+ }
275083
+ catch (error) {
275084
+ console.error('[AgentVoiceTab] save transcription error', error);
275085
+ toast.error(labels.toasts.transcriptionError);
275086
+ }
275087
+ };
274823
275088
  // Only `browser` has no voice to show -- it uses the listener's OS voice.
274824
275089
  const selectedVoice = provider === 'openai'
274825
275090
  ? effectiveOpenaiVoice
@@ -274843,47 +275108,51 @@ function AgentVoiceTab({ labels: labelsOverride, defaultSubTab = 'voice', render
274843
275108
  return renderPromptContent(text);
274844
275109
  return jsx("p", { className: "text-sm whitespace-pre-wrap text-gray-700", children: text });
274845
275110
  };
274846
- return (jsxs(Fragment$1, { children: [jsx("div", { className: "flex h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.header.description })] }) }), jsx("div", { className: "flex-1 space-y-4 p-3 lg:p-4", style: { overflowY: 'auto', overflowX: 'hidden' }, "data-testid": "voice-tab-body", children: isMentorSettingsLoading ? (jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "voice-tab-loading", children: jsx(Spinner, {}) })) : (jsxs(CapabilityGate, { title: labels.capability.title, description: labels.capability.description, offHint: labels.capability.offHint, enabled: voiceCallEnabled, onToggle: handleToggleVoiceCall, disabled: isDisabled || isTogglingVoiceCall, ariaLabel: labels.capability.title, toggleTestId: "voice-capability-toggle", children: [jsxs(Tabs, { defaultValue: SUB_TAB_VALUES[defaultSubTab], className: "w-full", children: [jsxs(TabsList, { "aria-label": labels.header.title, "data-testid": "voice-sub-tabs", children: [jsx(TabsTrigger, { value: SUB_TAB_VALUES.voice, "data-testid": "voice-sub-tab-voice", children: labels.subTabs.voice }), jsx(TabsTrigger, { value: SUB_TAB_VALUES.callConfig, "data-testid": "voice-sub-tab-call-config", children: labels.subTabs.callConfig })] }), jsxs(TabsContent, { value: SUB_TAB_VALUES.voice, className: "space-y-4", children: [jsx("p", { className: "text-xs text-gray-600", children: labels.mentorVoice.description }), jsx(WithFormPermissions, { name: "voice_provider",
274847
- // @ts-expect-error permissions.field is opaque
274848
- permissions: (_o = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _o === void 0 ? void 0 : _o.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { className: "text-sm font-medium text-[#646464]", children: labels.mentorVoice.providerLabel }), jsx(FieldTooltip, { text: labels.mentorVoice.providerTooltip })] }), jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", role: "radiogroup", "aria-label": labels.mentorVoice.providerLabel, children: providerOptions.map((option) => {
274849
- const isSelected = provider === option.value;
274850
- const isDisabledOption = isDisabled || disabled;
274851
- return (jsxs("button", { type: "button", role: "radio", "aria-checked": isSelected, "aria-label": option.label, disabled: isDisabledOption, "data-testid": `voice-provider-${option.value}`, "data-voice-provider": option.value, onClick: () => setProvider(option.value), className: cn('flex flex-col items-start gap-2 rounded-lg border-2 p-3 text-left transition-all hover:shadow-sm', isSelected
274852
- ? 'border-blue-500 bg-blue-50/40'
274853
- : 'border-gray-200 bg-white hover:border-gray-300', isDisabledOption && 'cursor-not-allowed opacity-60'), children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center", children: option.logo.type === 'image' ? (jsx(Image$4, { src: option.logo.src, alt: option.logo.alt, width: 24, height: 24, className: "h-6 w-6 object-contain" })) : (jsx(option.logo.Icon, { className: "h-5 w-5 text-blue-600", "aria-hidden": "true" })) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: option.label })] }), jsx("p", { className: "text-xs text-gray-500", children: option.description })] }, option.value));
274854
- }) })] })) }), usesVoiceCatalogue(provider) && (jsx(WithFormPermissions, { name: `${provider}_voice`,
274855
- // @ts-expect-error permissions.field is opaque
274856
- permissions: (_p = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _p === void 0 ? void 0 : _p.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { className: "text-sm font-medium text-[#646464]", children: t('tabsVoiceTab.voiceLabel') }), jsx(VoicePickerTrigger, { org: tenantKey, userId: username, provider: provider, selectedVoice: selectedVoice, onSelect: onSelectVoice, disabled: isDisabled || disabled, labels: labels.mentorVoice, modalTitle: t('tabsVoiceTab.selectVoiceModalTitle', {
274857
- voiceType: labels.mentorVoice.voicePickerLabel[provider],
274858
- }), modalDescription: t('tabsVoiceTab.selectVoiceModalDescription'), testId: "mentor-voice-trigger" })] })) })), (provider === 'openai' || provider === 'google') && (jsx(WithFormPermissions, { name: "voice_instructions",
274859
- // @ts-expect-error permissions.field is opaque
274860
- permissions: (_q = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _q === void 0 ? void 0 : _q.field, enableRBAC: enableRBAC, children: ({ disabled }) => {
274861
- const isFieldDisabled = isDisabled || disabled;
274862
- const presetKeys = ['warm', 'calm', 'energetic'];
274863
- return (jsxs("div", { className: "space-y-2", children: [jsx(PromptCard$1, { label: labels.mentorVoice.instructions.label, placeholder: labels.mentorVoice.instructions.placeholder, value: instructions, disabled: isFieldDisabled, renderContent: renderInstructionsContent, testId: "voice-instructions-card", headerExtra: jsx(FieldTooltip, { text: labels.mentorVoice.instructions.tooltip }), onEdit: () => setSelectedPrompt({
274864
- label: labels.mentorVoice.instructions.label,
274865
- isSystem: true,
274866
- name: 'voice_instructions',
274867
- prompt: instructions,
274868
- allowEmpty: true,
274869
- hideEditorLabel: true,
274870
- }) }), jsxs("div", { className: "flex items-start justify-between gap-3", children: [jsx("p", { id: "voice-instructions-help", className: "text-xs text-gray-500", children: labels.mentorVoice.instructions.helpText }), jsxs("span", { id: "voice-instructions-counter", "aria-live": "polite", "data-testid": "voice-instructions-counter", className: cn('flex-shrink-0 text-xs text-gray-400', instructionsOverCap && 'text-red-500'), children: [instructions.length, "/", VOICE_INSTRUCTIONS_MAX] })] }), jsxs("div", { className: "flex flex-wrap items-center gap-2", role: "group", "aria-label": labels.mentorVoice.instructions.presetsLabel, children: [jsxs("span", { className: "text-xs text-gray-500", children: [labels.mentorVoice.instructions.presetsLabel, ":"] }), presetKeys.map((key) => (jsx("button", { type: "button", disabled: isFieldDisabled, "data-testid": `voice-instructions-preset-${key}`, onClick: () => setInstructions(labels.mentorVoice.instructions.presets[key].text), className: cn('inline-flex items-center rounded-full border border-gray-300 bg-white px-3 py-1 text-xs text-gray-700 transition-colors hover:bg-gray-50', isFieldDisabled && 'cursor-not-allowed opacity-50'), children: labels.mentorVoice.instructions.presets[key].label }, key)))] })] }));
274871
- } })), jsx("div", { className: "flex items-center justify-end border-t border-gray-100 pt-3", children: jsx(Button$1, { type: "button", onClick: handleSaveVoice, disabled: isDisabled || !isDirty || instructionsOverCap, "data-testid": "voice-save-button", className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isSavingMentor
274872
- ? labels.mentorVoice.savingButton
274873
- : labels.mentorVoice.saveButton }) })] }), jsxs(TabsContent, { value: SUB_TAB_VALUES.callConfig, className: "space-y-4", children: [jsx("p", { className: "text-xs text-gray-600", children: labels.callConfig.description }), mentorId && username && tenantKey && (jsx(CallConfigSection, { org: tenantKey, userId: username, mentorId: mentorId, isDisabled: isDisabled, labels: labels.callConfig, voicePickerLabels: labels.mentorVoice, initialConfig: (_r = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.call_configuration) !== null && _r !== void 0 ? _r : null, renderPromptContent: renderPromptContent, toastLabels: {
274874
- saved: labels.toasts.callConfigSaved,
274875
- error: labels.toasts.callConfigError,
274876
- } }))] })] }), selectedPrompt && (
274877
- // `EditPromptModal` calls strict `useAgentSettings()`. The host
274878
- // may not have wrapped this tab in `<AgentSettingsProvider>`
274879
- // (older integrations pass identity via props), so re-provide
274880
- // whatever we resolved same pattern as `AgentScreenShareTab`.
274881
- jsx(AgentSettingsProvider, { tenantKey: tenantKey, mentorId: mentorId, username: username, enableRBAC: enableRBAC, rbacPermissions: ctx === null || ctx === void 0 ? void 0 : ctx.rbacPermissions, executeGatedAction: ctx === null || ctx === void 0 ? void 0 : ctx.executeGatedAction, visibilityOptions: ctx === null || ctx === void 0 ? void 0 : ctx.visibilityOptions, children: jsx(EditPromptModal$1, { isOpen: true, onClose: () => setSelectedPrompt(null), selectedPrompt: selectedPrompt, isEditing: isSavingMentor, handleSave: (prompt, values) => {
274882
- if (prompt.name === 'voice_instructions') {
274883
- setInstructions(values.prompt);
274884
- }
274885
- setSelectedPrompt(null);
274886
- } }) }))] })) })] }));
275111
+ return (jsxs(Fragment$1, { children: [jsx("div", { className: "flex h-[73px] flex-shrink-0 items-center border-b border-gray-200 bg-white p-4 lg:block", children: jsxs("div", { children: [jsx("h3", { className: "mb-1 text-base font-medium text-gray-900", children: labels.header.title }), jsx("p", { className: "text-xs text-gray-600", children: labels.header.description })] }) }), jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [jsx("div", { className: "flex-1 space-y-4 p-3 lg:p-4", style: { overflowY: 'auto', overflowX: 'hidden' }, "data-testid": "voice-tab-body", children: isMentorSettingsLoading ? (jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "voice-tab-loading", children: jsx(Spinner, {}) })) : (jsx(Fragment$1, { children: jsxs(CapabilityGate, { title: labels.capability.title, description: labels.capability.description, offHint: labels.capability.offHint, enabled: voiceCallEnabled, onToggle: handleToggleVoiceCall, disabled: isDisabled || isTogglingVoiceCall, ariaLabel: labels.capability.title, toggleTestId: "voice-capability-toggle", children: [jsxs(Tabs, { value: SUB_TAB_VALUES[subTab], onValueChange: (value) => setSubTab(SUB_TAB_KEYS[value]), className: "w-full", children: [jsxs(TabsList, { "aria-label": labels.header.title, "data-testid": "voice-sub-tabs", children: [jsx(TabsTrigger, { value: SUB_TAB_VALUES.voice, "data-testid": "voice-sub-tab-voice", children: labels.subTabs.voice }), jsx(TabsTrigger, { value: SUB_TAB_VALUES.callConfig, "data-testid": "voice-sub-tab-call-config", children: labels.subTabs.callConfig }), jsx(TabsTrigger, { value: SUB_TAB_VALUES.settings, "data-testid": "voice-sub-tab-settings", children: labels.subTabs.settings })] }), jsxs(TabsContent, { value: SUB_TAB_VALUES.voice, className: "mt-4 space-y-4", children: [jsx("p", { className: "text-xs text-gray-600", children: labels.mentorVoice.description }), jsx(WithFormPermissions, { name: "voice_provider",
275112
+ // @ts-expect-error permissions.field is opaque
275113
+ permissions: (_p = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _p === void 0 ? void 0 : _p.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { className: "text-sm font-medium text-[#646464]", children: labels.mentorVoice.providerLabel }), jsx(FieldTooltip, { text: labels.mentorVoice.providerTooltip })] }), jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", role: "radiogroup", "aria-label": labels.mentorVoice.providerLabel, children: providerOptions.map((option) => {
275114
+ const isSelected = provider === option.value;
275115
+ const isDisabledOption = isDisabled || disabled;
275116
+ return (jsxs("button", { type: "button", role: "radio", "aria-checked": isSelected, "aria-label": option.label, disabled: isDisabledOption, "data-testid": `voice-provider-${option.value}`, "data-voice-provider": option.value, onClick: () => setProvider(option.value), className: cn('flex flex-col items-start gap-2 rounded-lg border-2 p-3 text-left transition-all hover:shadow-sm', isSelected
275117
+ ? 'border-blue-500 bg-blue-50/40'
275118
+ : 'border-gray-200 bg-white hover:border-gray-300', isDisabledOption && 'cursor-not-allowed opacity-60'), children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center", children: option.logo.type === 'image' ? (jsx(Image$4, { src: option.logo.src, alt: option.logo.alt, width: 24, height: 24, className: "h-6 w-6 object-contain" })) : (jsx(option.logo.Icon, { className: "h-5 w-5 text-blue-600", "aria-hidden": "true" })) }), jsx("span", { className: "text-sm font-medium text-gray-900", children: option.label })] }), jsx("p", { className: "text-xs text-gray-500", children: option.description })] }, option.value));
275119
+ }) })] })) }), usesVoiceCatalogue(provider) && (jsx(WithFormPermissions, { name: `${provider}_voice`,
275120
+ // @ts-expect-error permissions.field is opaque
275121
+ permissions: (_q = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _q === void 0 ? void 0 : _q.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsxs("div", { className: "space-y-2", children: [jsx(Label$1, { className: "text-sm font-medium text-[#646464]", children: t('tabsVoiceTab.voiceLabel') }), jsx(VoicePickerTrigger, { org: tenantKey, userId: username, provider: provider, selectedVoice: selectedVoice, onSelect: onSelectVoice, disabled: isDisabled || disabled, labels: labels.mentorVoice, modalTitle: t('tabsVoiceTab.selectVoiceModalTitle', {
275122
+ voiceType: labels.mentorVoice.voicePickerLabel[provider],
275123
+ }), modalDescription: t('tabsVoiceTab.selectVoiceModalDescription'), testId: "mentor-voice-trigger" })] })) })), (provider === 'openai' || provider === 'google') && (jsx(WithFormPermissions, { name: "voice_instructions",
275124
+ // @ts-expect-error permissions.field is opaque
275125
+ permissions: (_r = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _r === void 0 ? void 0 : _r.field, enableRBAC: enableRBAC, children: ({ disabled }) => {
275126
+ const isFieldDisabled = isDisabled || disabled;
275127
+ const presetKeys = ['warm', 'calm', 'energetic'];
275128
+ return (jsxs("div", { className: "space-y-2", children: [jsx(PromptCard$1, { label: labels.mentorVoice.instructions.label, placeholder: labels.mentorVoice.instructions.placeholder, value: instructions, disabled: isFieldDisabled, renderContent: renderInstructionsContent, testId: "voice-instructions-card", headerExtra: jsx(FieldTooltip, { text: labels.mentorVoice.instructions.tooltip }), onEdit: () => setSelectedPrompt({
275129
+ label: labels.mentorVoice.instructions.label,
275130
+ isSystem: true,
275131
+ name: 'voice_instructions',
275132
+ prompt: instructions,
275133
+ allowEmpty: true,
275134
+ hideEditorLabel: true,
275135
+ }) }), jsxs("div", { className: "flex items-start justify-between gap-3", children: [jsx("p", { id: "voice-instructions-help", className: "text-xs text-gray-500", children: labels.mentorVoice.instructions.helpText }), jsxs("span", { id: "voice-instructions-counter", "aria-live": "polite", "data-testid": "voice-instructions-counter", className: cn('flex-shrink-0 text-xs text-gray-400', instructionsOverCap && 'text-red-500'), children: [instructions.length, "/", VOICE_INSTRUCTIONS_MAX] })] }), jsxs("div", { className: "flex flex-wrap items-center gap-2", role: "group", "aria-label": labels.mentorVoice.instructions.presetsLabel, children: [jsxs("span", { className: "text-xs text-gray-500", children: [labels.mentorVoice.instructions.presetsLabel, ":"] }), presetKeys.map((key) => (jsx("button", { type: "button", disabled: isFieldDisabled, "data-testid": `voice-instructions-preset-${key}`, onClick: () => setInstructions(labels.mentorVoice.instructions.presets[key].text), className: cn('inline-flex items-center rounded-full border border-gray-300 bg-white px-3 py-1 text-xs text-gray-700 transition-colors hover:bg-gray-50', isFieldDisabled && 'cursor-not-allowed opacity-50'), children: labels.mentorVoice.instructions.presets[key].label }, key)))] })] }));
275136
+ } })), jsx("div", { className: "flex items-center justify-end border-t border-gray-100 pt-3", children: jsx(Button$1, { type: "button", onClick: handleSaveVoice, disabled: isDisabled || !isDirty || instructionsOverCap, "data-testid": "voice-save-button", className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isSavingMentor
275137
+ ? labels.mentorVoice.savingButton
275138
+ : labels.mentorVoice.saveButton }) })] }), jsxs(TabsContent, { value: SUB_TAB_VALUES.callConfig, className: "mt-4 space-y-4", children: [jsx("p", { className: "text-xs text-gray-600", children: labels.callConfig.description }), mentorId && username && tenantKey && (jsx(CallConfigSection, { org: tenantKey, userId: username, mentorId: mentorId, isDisabled: isDisabled, labels: labels.callConfig, voicePickerLabels: labels.mentorVoice, initialConfig: (_s = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.call_configuration) !== null && _s !== void 0 ? _s : null, renderPromptContent: renderPromptContent, toastLabels: {
275139
+ saved: labels.toasts.callConfigSaved,
275140
+ error: labels.toasts.callConfigError,
275141
+ } }))] }), jsx(TabsContent, { value: SUB_TAB_VALUES.settings, className: "mt-4 space-y-4", children: jsxs("div", { className: "space-y-4", "data-testid": "transcription-section", children: [jsxs("div", { className: "space-y-1", children: [jsxs("div", { className: "flex items-center justify-between gap-4", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-[#646464]", children: labels.transcription.toggle.label }), jsx(FieldTooltip, { text: labels.transcription.toggle.tooltip })] }), jsx(CustomSwitch, { checked: voiceRecordEnabled, onCheckedChange: handleToggleVoiceRecord, disabled: isDisabled || isTogglingVoiceRecord, "aria-label": labels.transcription.toggle.label, "data-testid": "voice-record-capability-toggle", className: "flex-shrink-0" })] }), jsx("p", { className: "text-xs text-gray-500", children: labels.transcription.toggle.helpText })] }), jsx(WithFormPermissions, { name: "transcription_language",
275142
+ // @ts-expect-error permissions.field is opaque
275143
+ permissions: (_t = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _t === void 0 ? void 0 : _t.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsxs("div", { className: "space-y-2", "data-testid": "transcription-language-field", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { className: "text-sm font-medium text-[#646464]", children: labels.transcription.language.label }), jsx(FieldTooltip, { text: labels.transcription.language.tooltip })] }), jsxs(Select$1, { value: transcriptionLanguage || TRANSCRIPTION_LANGUAGE_UNSET, onValueChange: (value) => setTranscriptionLanguage(value === TRANSCRIPTION_LANGUAGE_UNSET ? '' : value), disabled: isDisabled || disabled, children: [jsx(SelectTrigger, { "aria-label": labels.transcription.language.label, "data-testid": "transcription-language-select", children: jsx(SelectValue, { placeholder: labels.transcription.language.placeholder }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: TRANSCRIPTION_LANGUAGE_UNSET, children: labels.transcription.language.placeholder }), TRANSCRIPTION_LANGUAGE_OPTIONS.map((option) => (jsx(SelectItem, { value: option.value, children: option.label }, option.value)))] })] })] })) }), jsx(WithFormPermissions, { name: "transcription_instructions",
275144
+ // @ts-expect-error permissions.field is opaque
275145
+ permissions: (_u = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _u === void 0 ? void 0 : _u.field, enableRBAC: enableRBAC, children: ({ disabled }) => (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Label$1, { htmlFor: "transcription-instructions", className: "text-sm font-medium text-[#646464]", children: labels.transcription.instructions.label }), jsx(FieldTooltip, { text: labels.transcription.instructions.tooltip })] }), jsx(Textarea, { id: "transcription-instructions", rows: 4, value: transcriptionInstructions, onChange: (event) => setTranscriptionInstructions(event.target.value), placeholder: labels.transcription.instructions.placeholder, disabled: isDisabled || disabled, "aria-label": labels.transcription.instructions.label, "aria-describedby": "transcription-instructions-help", "data-testid": "transcription-instructions-textarea" }), jsxs("div", { className: "flex items-start justify-between gap-3", children: [jsx("p", { id: "transcription-instructions-help", className: "text-xs text-gray-500", children: labels.transcription.instructions.helpText }), jsxs("span", { "aria-live": "polite", "data-testid": "transcription-instructions-counter", className: cn('flex-shrink-0 text-xs text-gray-400', transcriptionOverCap && 'text-red-500'), children: [transcriptionInstructions.length, "/", TRANSCRIPTION_INSTRUCTIONS_MAX] })] })] })) })] }) })] }), selectedPrompt && (
275146
+ // `EditPromptModal` calls strict `useAgentSettings()`. The host
275147
+ // may not have wrapped this tab in `<AgentSettingsProvider>`
275148
+ // (older integrations pass identity via props), so re-provide
275149
+ // whatever we resolved — same pattern as `AgentScreenShareTab`.
275150
+ jsx(AgentSettingsProvider, { tenantKey: tenantKey, mentorId: mentorId, username: username, enableRBAC: enableRBAC, rbacPermissions: ctx === null || ctx === void 0 ? void 0 : ctx.rbacPermissions, executeGatedAction: ctx === null || ctx === void 0 ? void 0 : ctx.executeGatedAction, visibilityOptions: ctx === null || ctx === void 0 ? void 0 : ctx.visibilityOptions, children: jsx(EditPromptModal$1, { isOpen: true, onClose: () => setSelectedPrompt(null), selectedPrompt: selectedPrompt, isEditing: isSavingMentor, handleSave: (prompt, values) => {
275151
+ if (prompt.name === 'voice_instructions') {
275152
+ setInstructions(values.prompt);
275153
+ }
275154
+ setSelectedPrompt(null);
275155
+ } }) }))] }) })) }), !isMentorSettingsLoading && subTab === 'settings' && (jsx("div", { className: "flex flex-shrink-0 justify-end gap-2 border-t border-gray-200 bg-white px-3 py-4", children: jsx(Button$1, { type: "button", onClick: handleSaveTranscription, disabled: isDisabled || !transcriptionDirty || transcriptionOverCap, "aria-label": labels.transcription.saveButton, "data-testid": "transcription-save-button", className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: isSavingMentor ? (jsxs("span", { className: "flex items-center gap-2", children: [jsx(Spinner, { size: "sm", className: "text-white" }), labels.transcription.savingButton] })) : (labels.transcription.saveButton) }) }))] })] }));
274887
275156
  }
274888
275157
  function FieldTooltip({ text }) {
274889
275158
  const t = useT();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "2.7.7",
3
+ "version": "2.7.8",
4
4
  "description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
5
5
  "type": "module",
6
6
  "engines": {
@@ -76,10 +76,10 @@
76
76
  "axios": "1.13.6",
77
77
  "dotenv": "16.6.1",
78
78
  "winston": "3.19.0",
79
- "@iblai/web-utils": "2.2.2-reconnect.2",
79
+ "@iblai/mcp": "1.12.2",
80
+ "@iblai/web-containers": "1.19.5",
80
81
  "@iblai/data-layer": "1.13.0",
81
- "@iblai/web-containers": "1.19.4",
82
- "@iblai/mcp": "1.12.2"
82
+ "@iblai/web-utils": "2.2.2-reconnect.2"
83
83
  },
84
84
  "peerDependencies": {
85
85
  "@iblai/iblai-api": "4.166.0-ai",