@iblai/iblai-js 2.27.0 → 2.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -96359,7 +96359,11 @@ var advancedCustomDomain$3 = {
96359
96359
  noProjects: "You have not deployed any apps yet. Deploy one first, then come back to give it a domain.",
96360
96360
  customDomainLabel: "Domain",
96361
96361
  domainHelpPlatform: "Without the scheme or a leading “www.” — for example, app.example.com.",
96362
- domainHelpApp: "Leave this empty and a subdomain will be assigned to the app for you, with no DNS records to set.",
96362
+ domainHelpApp: "A domain you own, without the scheme or a leading “www.” — for example, app.example.com. Leave it empty to use the subdomain above.",
96363
+ subdomainLabel: "Subdomain",
96364
+ subdomainHelp: "Lowercase letters, digits and hyphens, like a username. The app is served at <subdomain>.<the platform’s shared domain>, with no DNS records to set.",
96365
+ oneOfSubdomainOrDomain: "Fill in either the subdomain or a domain you own, not both.",
96366
+ subdomainInvalid: "A subdomain is 1–63 lowercase letters, digits and hyphens, and cannot start or end with a hyphen.",
96363
96367
  cancel: "Cancel",
96364
96368
  save: "Save",
96365
96369
  expandAriaLabel: "Expand domains",
@@ -96381,6 +96385,9 @@ var advancedCustomDomain$3 = {
96381
96385
  copyNameHostAriaLabel: "Copy name/host: {domain}",
96382
96386
  copyValueAriaLabel: "Copy value/target",
96383
96387
  noRecordsYet: "No records yet. Check the domain again to fetch them.",
96388
+ sharedDomainLine: "Runs on our shared domain — nothing to set up.",
96389
+ openSite: "Open {domain}",
96390
+ servesApp: "Serves {app}",
96384
96391
  confirmRemoveTitle: "Remove this domain?",
96385
96392
  confirmDeleteBody: "{domain} will stop signing users in to this organization.",
96386
96393
  confirmDetachBody: "{domain} will stop serving the app, here and at the hosting provider.",
@@ -96838,6 +96845,9 @@ var analyticsAnalyticsFinancialStats$3 = {
96838
96845
  viewTraces: "Traces →",
96839
96846
  costByLLMFooter: "{shown} models · ranked by cost"
96840
96847
  };
96848
+ var agentSettingsLink$3 = {
96849
+ openSettings: "Open settings for {name}"
96850
+ };
96841
96851
  var analyticsAgentScopeLink$3 = {
96842
96852
  scopeToAgent: "View analytics for {name}"
96843
96853
  };
@@ -97059,6 +97069,8 @@ var analyticsAnalyticsTopicsStats$3 = {
97059
97069
  topicsRankedFooter: "{shown} of {total} topics · ranked by message volume"
97060
97070
  };
97061
97071
  var analyticsAnalyticsTranscriptsStats$3 = {
97072
+ conversationFallbackTitle: "Conversation",
97073
+ labelEmail: "Email:",
97062
97074
  anonymous: "Anonymous",
97063
97075
  justNow: "Just now",
97064
97076
  minutesAgo: "{count} min ago",
@@ -97503,6 +97515,7 @@ var careerEducationTab$3 = {
97503
97515
  nothingToShow: "Nothing to show here."
97504
97516
  };
97505
97517
  var chatHistoryChatHistoryTab$3 = {
97518
+ userSentiment: "User Sentiment",
97506
97519
  tabConversations: "Conversations",
97507
97520
  tabExports: "Exports",
97508
97521
  searchAgents: "Search Agents",
@@ -98547,6 +98560,10 @@ var disclaimersTabLabels$3 = {
98547
98560
  }
98548
98561
  };
98549
98562
  var documentSidebar$3 = {
98563
+ relevance: "Relevance {percent}%",
98564
+ relevanceHint: "How closely this passage matched the question",
98565
+ preview: "Preview",
98566
+ openInNewTab: "Open {title} in a new tab",
98550
98567
  retrievedDocuments: "Retrieved Documents"
98551
98568
  };
98552
98569
  var embedTabLabels$3 = {
@@ -98656,6 +98673,12 @@ var historyTabLabels$3 = {
98656
98673
  outOf: "out of 5"
98657
98674
  },
98658
98675
  conversation: {
98676
+ sentiment: {
98677
+ positive: "Positive",
98678
+ neutral: "Neutral",
98679
+ negative: "Negative",
98680
+ userSentiment: "User Sentiment"
98681
+ },
98659
98682
  title: "Conversation",
98660
98683
  anonymous: "Anonymous",
98661
98684
  noResponse: "No response available",
@@ -103336,6 +103359,7 @@ var en = {
103336
103359
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail$3,
103337
103360
  analyticsAnalyticsCourses: analyticsAnalyticsCourses$3,
103338
103361
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats$3,
103362
+ agentSettingsLink: agentSettingsLink$3,
103339
103363
  analyticsAgentScopeLink: analyticsAgentScopeLink$3,
103340
103364
  analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats$3,
103341
103365
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats$3,
@@ -103887,7 +103911,11 @@ var advancedCustomDomain$2 = {
103887
103911
  noProjects: "Vous n’avez encore déployé aucune application. Déployez-en une, puis revenez lui donner un domaine.",
103888
103912
  customDomainLabel: "Domaine",
103889
103913
  domainHelpPlatform: "Sans le schéma ni un « www. » en tête — par exemple, app.example.com.",
103890
- domainHelpApp: "Laissez ce champ vide et un sous-domaine sera attribué à l’application, sans aucun enregistrement DNS à créer.",
103914
+ domainHelpApp: "Un domaine qui vous appartient, sans le schéma ni un « www. » en tête — par exemple, app.example.com. Laissez ce champ vide pour utiliser le sous-domaine ci-dessus.",
103915
+ subdomainLabel: "Sous-domaine",
103916
+ subdomainHelp: "Lettres minuscules, chiffres et tirets, comme un nom d’utilisateur. L’application est servie à <sous-domaine>.<le domaine partagé de la plateforme>, sans aucun enregistrement DNS à créer.",
103917
+ oneOfSubdomainOrDomain: "Renseignez soit le sous-domaine, soit un domaine qui vous appartient, pas les deux.",
103918
+ subdomainInvalid: "Un sous-domaine compte de 1 à 63 lettres minuscules, chiffres et tirets, et ne peut ni commencer ni finir par un tiret.",
103891
103919
  cancel: "Annuler",
103892
103920
  save: "Enregistrer",
103893
103921
  expandAriaLabel: "Déplier les domaines",
@@ -103909,6 +103937,9 @@ var advancedCustomDomain$2 = {
103909
103937
  copyNameHostAriaLabel: "Copier le nom/hôte : {domain}",
103910
103938
  copyValueAriaLabel: "Copier la valeur/cible",
103911
103939
  noRecordsYet: "Aucun enregistrement pour l’instant. Vérifiez le domaine à nouveau pour les obtenir.",
103940
+ sharedDomainLine: "Fonctionne sur notre domaine partagé — rien à configurer.",
103941
+ openSite: "Ouvrir {domain}",
103942
+ servesApp: "Dessert {app}",
103912
103943
  confirmRemoveTitle: "Supprimer ce domaine ?",
103913
103944
  confirmDeleteBody: "{domain} ne permettra plus de se connecter à cette organisation.",
103914
103945
  confirmDetachBody: "{domain} cessera de servir l’application, ici comme chez l’hébergeur.",
@@ -104366,6 +104397,9 @@ var analyticsAnalyticsFinancialStats$2 = {
104366
104397
  viewTraces: "Traces →",
104367
104398
  costByLLMFooter: "{shown} modèles · classés par coût"
104368
104399
  };
104400
+ var agentSettingsLink$2 = {
104401
+ openSettings: "Ouvrir les paramètres de {name}"
104402
+ };
104369
104403
  var analyticsAgentScopeLink$2 = {
104370
104404
  scopeToAgent: "Voir les analyses de {name}"
104371
104405
  };
@@ -104587,6 +104621,8 @@ var analyticsAnalyticsTopicsStats$2 = {
104587
104621
  topicsRankedFooter: "{shown} sur {total} sujets · classés par volume de messages"
104588
104622
  };
104589
104623
  var analyticsAnalyticsTranscriptsStats$2 = {
104624
+ conversationFallbackTitle: "Conversation",
104625
+ labelEmail: "E-mail :",
104590
104626
  anonymous: "Anonyme",
104591
104627
  justNow: "À l'instant",
104592
104628
  minutesAgo: "Il y a {count} min",
@@ -105031,6 +105067,7 @@ var careerEducationTab$2 = {
105031
105067
  nothingToShow: "Rien à afficher ici."
105032
105068
  };
105033
105069
  var chatHistoryChatHistoryTab$2 = {
105070
+ userSentiment: "Sentiment de l'utilisateur",
105034
105071
  tabConversations: "Conversations",
105035
105072
  tabExports: "Exportations",
105036
105073
  searchAgents: "Rechercher des agents",
@@ -106241,6 +106278,10 @@ var disclaimersTabLabels$2 = {
106241
106278
  }
106242
106279
  };
106243
106280
  var documentSidebar$2 = {
106281
+ relevance: "Pertinence {percent}%",
106282
+ relevanceHint: "Dans quelle mesure ce passage correspond à la question",
106283
+ preview: "Aperçu",
106284
+ openInNewTab: "Ouvrir {title} dans un nouvel onglet",
106244
106285
  retrievedDocuments: "Documents récupérés"
106245
106286
  };
106246
106287
  var embedTabLabels$2 = {
@@ -106350,6 +106391,12 @@ var historyTabLabels$2 = {
106350
106391
  outOf: "sur 5"
106351
106392
  },
106352
106393
  conversation: {
106394
+ sentiment: {
106395
+ positive: "Positif",
106396
+ neutral: "Neutre",
106397
+ negative: "Négatif",
106398
+ userSentiment: "Sentiment de l'utilisateur"
106399
+ },
106353
106400
  title: "Conversation",
106354
106401
  anonymous: "Anonyme",
106355
106402
  noResponse: "Aucune réponse disponible",
@@ -110864,6 +110911,7 @@ var fr = {
110864
110911
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail$2,
110865
110912
  analyticsAnalyticsCourses: analyticsAnalyticsCourses$2,
110866
110913
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats$2,
110914
+ agentSettingsLink: agentSettingsLink$2,
110867
110915
  analyticsAgentScopeLink: analyticsAgentScopeLink$2,
110868
110916
  analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats$2,
110869
110917
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats$2,
@@ -111415,7 +111463,11 @@ var advancedCustomDomain$1 = {
111415
111463
  noProjects: "Todavía no has desplegado ninguna aplicación. Despliega una primero y vuelve para darle un dominio.",
111416
111464
  customDomainLabel: "Dominio",
111417
111465
  domainHelpPlatform: "Sin el esquema ni un “www.” inicial — por ejemplo, app.example.com.",
111418
- domainHelpApp: "Déjalo vacío y se asignará un subdominio a la aplicación, sin registros DNS que configurar.",
111466
+ domainHelpApp: "Un dominio tuyo, sin el esquema ni un “www.” inicial — por ejemplo, app.example.com. Déjalo vacío para usar el subdominio de arriba.",
111467
+ subdomainLabel: "Subdominio",
111468
+ subdomainHelp: "Letras minúsculas, dígitos y guiones, como un nombre de usuario. La aplicación se sirve en <subdominio>.<el dominio compartido de la plataforma>, sin registros DNS que configurar.",
111469
+ oneOfSubdomainOrDomain: "Rellena el subdominio o un dominio tuyo, no ambos.",
111470
+ subdomainInvalid: "Un subdominio tiene entre 1 y 63 letras minúsculas, dígitos y guiones, y no puede empezar ni terminar con un guion.",
111419
111471
  cancel: "Cancelar",
111420
111472
  save: "Guardar",
111421
111473
  expandAriaLabel: "Expandir dominios",
@@ -111437,6 +111489,9 @@ var advancedCustomDomain$1 = {
111437
111489
  copyNameHostAriaLabel: "Copiar nombre/host: {domain}",
111438
111490
  copyValueAriaLabel: "Copiar valor/destino",
111439
111491
  noRecordsYet: "Aún no hay registros. Comprueba el dominio de nuevo para obtenerlos.",
111492
+ sharedDomainLine: "Funciona en nuestro dominio compartido — no hay nada que configurar.",
111493
+ openSite: "Abrir {domain}",
111494
+ servesApp: "Sirve {app}",
111440
111495
  confirmRemoveTitle: "¿Eliminar este dominio?",
111441
111496
  confirmDeleteBody: "{domain} dejará de permitir el inicio de sesión en esta organización.",
111442
111497
  confirmDetachBody: "{domain} dejará de servir la aplicación, aquí y en el proveedor de alojamiento.",
@@ -111894,6 +111949,9 @@ var analyticsAnalyticsFinancialStats$1 = {
111894
111949
  viewTraces: "Trazas →",
111895
111950
  costByLLMFooter: "{shown} modelos · ordenados por costo"
111896
111951
  };
111952
+ var agentSettingsLink$1 = {
111953
+ openSettings: "Abrir la configuración de {name}"
111954
+ };
111897
111955
  var analyticsAgentScopeLink$1 = {
111898
111956
  scopeToAgent: "Ver analíticas de {name}"
111899
111957
  };
@@ -112115,6 +112173,8 @@ var analyticsAnalyticsTopicsStats$1 = {
112115
112173
  topicsRankedFooter: "{shown} de {total} temas · ordenados por volumen de mensajes"
112116
112174
  };
112117
112175
  var analyticsAnalyticsTranscriptsStats$1 = {
112176
+ conversationFallbackTitle: "Conversación",
112177
+ labelEmail: "Correo electrónico:",
112118
112178
  anonymous: "Anónimo",
112119
112179
  justNow: "Ahora mismo",
112120
112180
  minutesAgo: "Hace {count} min",
@@ -112559,6 +112619,7 @@ var careerEducationTab$1 = {
112559
112619
  nothingToShow: "No hay nada que mostrar aquí."
112560
112620
  };
112561
112621
  var chatHistoryChatHistoryTab$1 = {
112622
+ userSentiment: "Sentimiento del usuario",
112562
112623
  tabConversations: "Conversaciones",
112563
112624
  tabExports: "Exportaciones",
112564
112625
  searchAgents: "Buscar agentes",
@@ -113769,6 +113830,10 @@ var disclaimersTabLabels$1 = {
113769
113830
  }
113770
113831
  };
113771
113832
  var documentSidebar$1 = {
113833
+ relevance: "Relevancia {percent}%",
113834
+ relevanceHint: "Cuánto coincide este fragmento con la pregunta",
113835
+ preview: "Vista previa",
113836
+ openInNewTab: "Abrir {title} en una pestaña nueva",
113772
113837
  retrievedDocuments: "Documentos recuperados"
113773
113838
  };
113774
113839
  var embedTabLabels$1 = {
@@ -113878,6 +113943,12 @@ var historyTabLabels$1 = {
113878
113943
  outOf: "de 5"
113879
113944
  },
113880
113945
  conversation: {
113946
+ sentiment: {
113947
+ positive: "Positivo",
113948
+ neutral: "Neutral",
113949
+ negative: "Negativo",
113950
+ userSentiment: "Sentimiento del usuario"
113951
+ },
113881
113952
  title: "Conversación",
113882
113953
  anonymous: "Anónimo",
113883
113954
  noResponse: "No hay respuesta disponible",
@@ -118392,6 +118463,7 @@ var es = {
118392
118463
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail$1,
118393
118464
  analyticsAnalyticsCourses: analyticsAnalyticsCourses$1,
118394
118465
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats$1,
118466
+ agentSettingsLink: agentSettingsLink$1,
118395
118467
  analyticsAgentScopeLink: analyticsAgentScopeLink$1,
118396
118468
  analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats$1,
118397
118469
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats$1,
@@ -118943,7 +119015,11 @@ var advancedCustomDomain = {
118943
119015
  noProjects: "你还没有部署任何应用。请先部署一个,然后回来为它设置域名。",
118944
119016
  customDomainLabel: "域名",
118945
119017
  domainHelpPlatform: "不要带协议前缀或开头的“www.”,例如 app.example.com。",
118946
- domainHelpApp: "留空即可自动为该应用分配一个子域名,无需设置任何 DNS 记录。",
119018
+ domainHelpApp: "你拥有的域名,不要带协议前缀或开头的“www.”,例如 app.example.com。留空则使用上方的子域名。",
119019
+ subdomainLabel: "子域名",
119020
+ subdomainHelp: "小写字母、数字和连字符,像用户名一样。应用将通过 <子域名>.<平台的共享域名> 提供服务,无需设置任何 DNS 记录。",
119021
+ oneOfSubdomainOrDomain: "请填写子域名或你拥有的域名,二者只填其一。",
119022
+ subdomainInvalid: "子域名为 1 到 63 个小写字母、数字和连字符,且不能以连字符开头或结尾。",
118947
119023
  cancel: "取消",
118948
119024
  save: "保存",
118949
119025
  expandAriaLabel: "展开域名",
@@ -118965,6 +119041,9 @@ var advancedCustomDomain = {
118965
119041
  copyNameHostAriaLabel: "复制名称/主机:{domain}",
118966
119042
  copyValueAriaLabel: "复制值/目标",
118967
119043
  noRecordsYet: "暂无记录。请重新检查该域名以获取记录。",
119044
+ sharedDomainLine: "运行在我们的共享域名上——无需任何设置。",
119045
+ openSite: "打开 {domain}",
119046
+ servesApp: "服务于 {app}",
118968
119047
  confirmRemoveTitle: "要移除这个域名吗?",
118969
119048
  confirmDeleteBody: "{domain} 将不再用于登录该组织。",
118970
119049
  confirmDetachBody: "{domain} 将不再为该应用提供服务,本处和托管服务商处都会解除。",
@@ -119422,6 +119501,9 @@ var analyticsAnalyticsFinancialStats = {
119422
119501
  viewTraces: "追踪 →",
119423
119502
  costByLLMFooter: "{shown} 个模型 · 按成本排序"
119424
119503
  };
119504
+ var agentSettingsLink = {
119505
+ openSettings: "打开 {name} 的设置"
119506
+ };
119425
119507
  var analyticsAgentScopeLink = {
119426
119508
  scopeToAgent: "查看 {name} 的分析"
119427
119509
  };
@@ -119643,6 +119725,8 @@ var analyticsAnalyticsTopicsStats = {
119643
119725
  topicsRankedFooter: "共 {total} 个主题,显示 {shown} 个 · 按消息量排序"
119644
119726
  };
119645
119727
  var analyticsAnalyticsTranscriptsStats = {
119728
+ conversationFallbackTitle: "对话",
119729
+ labelEmail: "邮箱:",
119646
119730
  anonymous: "匿名",
119647
119731
  justNow: "刚刚",
119648
119732
  minutesAgo: "{count} 分钟前",
@@ -120087,6 +120171,7 @@ var careerEducationTab = {
120087
120171
  nothingToShow: "暂无内容。"
120088
120172
  };
120089
120173
  var chatHistoryChatHistoryTab = {
120174
+ userSentiment: "用户情绪",
120090
120175
  tabConversations: "对话",
120091
120176
  tabExports: "导出",
120092
120177
  searchAgents: "搜索代理",
@@ -121297,6 +121382,10 @@ var disclaimersTabLabels = {
121297
121382
  }
121298
121383
  };
121299
121384
  var documentSidebar = {
121385
+ relevance: "相关度 {percent}%",
121386
+ relevanceHint: "该片段与问题的匹配程度",
121387
+ preview: "预览",
121388
+ openInNewTab: "在新标签页中打开 {title}",
121300
121389
  retrievedDocuments: "检索到的文档"
121301
121390
  };
121302
121391
  var embedTabLabels = {
@@ -121406,6 +121495,12 @@ var historyTabLabels = {
121406
121495
  outOf: "满分 5 分"
121407
121496
  },
121408
121497
  conversation: {
121498
+ sentiment: {
121499
+ positive: "积极",
121500
+ neutral: "中性",
121501
+ negative: "消极",
121502
+ userSentiment: "用户情绪"
121503
+ },
121409
121504
  title: "对话",
121410
121505
  anonymous: "匿名",
121411
121506
  noResponse: "暂无回复",
@@ -125920,6 +126015,7 @@ var zh = {
125920
126015
  analyticsAnalyticsCourseDetail: analyticsAnalyticsCourseDetail,
125921
126016
  analyticsAnalyticsCourses: analyticsAnalyticsCourses,
125922
126017
  analyticsAnalyticsFinancialStats: analyticsAnalyticsFinancialStats,
126018
+ agentSettingsLink: agentSettingsLink,
125923
126019
  analyticsAgentScopeLink: analyticsAgentScopeLink,
125924
126020
  analyticsAnalyticsMemoryStats: analyticsAnalyticsMemoryStats,
125925
126021
  analyticsAnalyticsMonetizationStats: analyticsAnalyticsMonetizationStats,
@@ -181054,7 +181150,7 @@ function MemoryTab({ org, username }) {
181054
181150
  username: editingMemory.mentorId ? currentUsername : username, memory: editingMemory, mentorId: editingMemory.mentorId }))] }));
181055
181151
  }
181056
181152
 
181057
- var _a$1;
181153
+ var _a$2;
181058
181154
  const dl = dataLayer;
181059
181155
  /**
181060
181156
  * Read a named export, tolerating absence. In real ESM a missing export reads
@@ -181087,7 +181183,7 @@ const pick = (name, fallback) => { var _a; return ((_a = read(name)) !== null &&
181087
181183
  /** Whether the resolved data-layer actually supports the chat-privacy feature. */
181088
181184
  const CHAT_PRIVACY_SUPPORTED = !!read('ChatPrivacyModeEnum') && !!read('CHAT_PRIVACY_MODES');
181089
181185
  const ChatPrivacyModeEnum = read('ChatPrivacyModeEnum');
181090
- const CHAT_PRIVACY_MODES = ((_a$1 = read('CHAT_PRIVACY_MODES')) !== null && _a$1 !== void 0 ? _a$1 : []);
181186
+ const CHAT_PRIVACY_MODES = ((_a$2 = read('CHAT_PRIVACY_MODES')) !== null && _a$2 !== void 0 ? _a$2 : []);
181091
181187
  read('chatPrivacyApiSlice');
181092
181188
  const useGetTenantChatPrivacyConfigQuery = pick('useGetTenantChatPrivacyConfigQuery', noopQuery);
181093
181189
  const useUpdateTenantChatPrivacyConfigMutation = pick('useUpdateTenantChatPrivacyConfigMutation', noopMutation);
@@ -181187,6 +181283,142 @@ function ChatPrivacyTab({ org, username }) {
181187
181283
 
181188
181284
  function c$3(e,u,c){var i=this,a=useRef(null),o=useRef(0),f=useRef(null),l=useRef([]),v=useRef(),m=useRef(),d=useRef(e),g=useRef(true);d.current=e;var p="undefined"!=typeof window,w=!u&&0!==u&&p;if("function"!=typeof e)throw new TypeError("Expected a function");u=+u||0;var s=!!(c=c||{}).leading,x=!("trailing"in c)||!!c.trailing,h="maxWait"in c,y="debounceOnServer"in c&&!!c.debounceOnServer,F=h?Math.max(+c.maxWait||0,u):null;useEffect(function(){return g.current=true,function(){g.current=false;}},[]);var A=useMemo(function(){var r=function(r){var n=l.current,t=v.current;return l.current=v.current=null,o.current=r,m.current=d.current.apply(t,n)},n=function(r,n){w&&cancelAnimationFrame(f.current),f.current=w?requestAnimationFrame(r):setTimeout(r,n);},t=function(r){if(!g.current)return false;var n=r-a.current;return !a.current||n>=u||n<0||h&&r-o.current>=F},e=function(n){return f.current=null,x&&l.current?r(n):(l.current=v.current=null,m.current)},c=function r(){var c=Date.now();if(t(c))return e(c);if(g.current){var i=u-(c-a.current),f=h?Math.min(i,F-(c-o.current)):i;n(r,f);}},A=function(){if(p||y){var e=Date.now(),d=t(e);if(l.current=[].slice.call(arguments),v.current=i,a.current=e,d){if(!f.current&&g.current)return o.current=a.current,n(c,u),s?r(a.current):m.current;if(h)return n(c,u),r(a.current)}return f.current||n(c,u),m.current}};return A.cancel=function(){f.current&&(w?cancelAnimationFrame(f.current):clearTimeout(f.current)),o.current=0,l.current=a.current=v.current=f.current=null;},A.isPending=function(){return !!f.current},A.flush=function(){return f.current?e(Date.now()):m.current},A},[s,h,u,F,x,w,p,y]);return A}function i$9(r,n){return r===n}function a$3(n,t,a){var o=i$9,f=useRef(n),l=useState({})[1],v=c$3(useCallback(function(r){f.current=r,l({});},[l]),t,a),m=useRef(n);return o(m.current,n)||(v(n),m.current=n),[f.current,v]}
181189
181285
 
181286
+ var _a$1;
181287
+ /**
181288
+ * One context object for every copy of this module. The SDK ships several
181289
+ * bundles (`index`, `next`, …) that each inline their own copy of this file,
181290
+ * so a host provider from one bundle would never reach a link rendered by
181291
+ * another (the profile modal comes from `next`; Transcripts from `index`).
181292
+ * Registering the context on `globalThis` under a well-known symbol makes
181293
+ * the copies share it — React is a single external instance, so the object
181294
+ * is interchangeable between them.
181295
+ */
181296
+ const CONTEXT_KEY = Symbol.for('@iblai/web-containers/agent-settings-launcher');
181297
+ const registry = globalThis;
181298
+ const AgentSettingsLauncherContext = (_a$1 = registry[CONTEXT_KEY]) !== null && _a$1 !== void 0 ? _a$1 : (registry[CONTEXT_KEY] = React__default.createContext(null));
181299
+ function useAgentSettingsLauncher() {
181300
+ return React__default.useContext(AgentSettingsLauncherContext);
181301
+ }
181302
+ /**
181303
+ * An agent's name, linked to its settings where the host lets this viewer
181304
+ * open them; plain text otherwise. Truncates inside an `inline-flex` box so
181305
+ * it keeps the baseline of the label beside it, and neither its clicks nor
181306
+ * its key presses reach a clickable row around it.
181307
+ */
181308
+ function AgentSettingsLink({ uniqueId, name, fallback = '-', className, testId, }) {
181309
+ var _a, _b;
181310
+ const t = useT();
181311
+ const launcher = useAgentSettingsLauncher();
181312
+ const display = name || uniqueId || fallback;
181313
+ const target = uniqueId ? { uniqueId, name: name || uniqueId } : null;
181314
+ const canOpen = !!launcher && !!target && ((_b = (_a = launcher.canOpenAgentSettings) === null || _a === void 0 ? void 0 : _a.call(launcher, target)) !== null && _b !== void 0 ? _b : true);
181315
+ if (!canOpen || !target) {
181316
+ return (jsx("span", { className: cn('inline-flex max-w-full', className), "data-testid": testId, children: jsx("span", { className: "min-w-0 truncate", children: display }) }));
181317
+ }
181318
+ return (jsx("button", { type: "button", onClick: (event) => {
181319
+ event.stopPropagation();
181320
+ launcher.openAgentSettings(target);
181321
+ },
181322
+ // Rows that open on Enter/Space would otherwise swallow the key press.
181323
+ onKeyDown: (event) => event.stopPropagation(), className: cn('inline-flex max-w-full cursor-pointer text-left hover:text-blue-600 hover:underline focus:text-blue-600 focus:outline-none', className), "aria-label": t('agentSettingsLink.openSettings', { name: display }), "data-testid": testId, children: jsx("span", { className: "min-w-0 truncate", children: display }) }));
181324
+ }
181325
+
181326
+ /**
181327
+ * A conversation's sentiment the way Analytics → Transcripts shows it: a
181328
+ * thumb and "<Sentiment> User Sentiment" — green when positive, blue when
181329
+ * negative, grey otherwise. Renders nothing when the record carries none.
181330
+ */
181331
+ function ConversationSentiment({ sentiment, labels, className, compact = false, }) {
181332
+ const key = (sentiment !== null && sentiment !== void 0 ? sentiment : '').trim().toLowerCase();
181333
+ if (!key)
181334
+ return null;
181335
+ const Icon = key === 'negative' ? ThumbsDown : ThumbsUp;
181336
+ const tone = key === 'negative' ? 'text-blue-600' : key === 'positive' ? 'text-green-600' : 'text-gray-600';
181337
+ const word = key === 'negative'
181338
+ ? labels.negative
181339
+ : key === 'positive'
181340
+ ? labels.positive
181341
+ : key === 'neutral'
181342
+ ? labels.neutral
181343
+ : key.charAt(0).toUpperCase() + key.slice(1);
181344
+ const text = `${word} ${labels.userSentiment}`;
181345
+ if (compact) {
181346
+ return (jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { asChild: true, children: jsxs("span", { className: cn('inline-flex cursor-default items-center', tone, className), "data-testid": "conversation-sentiment", "data-sentiment": key, "data-compact": "true", tabIndex: 0, children: [jsx(Icon, { className: "h-4 w-4", "aria-hidden": "true" }), jsx("span", { className: "sr-only", children: text })] }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: text })] }) }));
181347
+ }
181348
+ return (jsxs("div", { className: cn('flex items-center gap-1 text-sm', tone, className), "data-testid": "conversation-sentiment", "data-sentiment": key, children: [jsx(Icon, { className: "h-4 w-4", "aria-hidden": "true" }), jsxs("span", { children: [word, " ", labels.userSentiment] })] }));
181349
+ }
181350
+
181351
+ /**
181352
+ * A conversation's topic names, whatever shape the endpoint uses: analytics
181353
+ * returns `["Biology"]`, the chat-history endpoints `[{ name: "Biology" }]`.
181354
+ * Blank entries are dropped and repeats collapsed; order is kept.
181355
+ */
181356
+ function conversationTopics(topics) {
181357
+ if (!Array.isArray(topics))
181358
+ return [];
181359
+ const names = [];
181360
+ for (const topic of topics) {
181361
+ const raw = typeof topic === 'string'
181362
+ ? topic
181363
+ : topic && typeof topic === 'object'
181364
+ ? topic.name
181365
+ : undefined;
181366
+ const name = typeof raw === 'string' ? raw.trim() : '';
181367
+ if (name && !names.includes(name))
181368
+ names.push(name);
181369
+ }
181370
+ return names;
181371
+ }
181372
+
181373
+ /** A conversation's topics as the chips Analytics → Transcripts draws. */
181374
+ function ConversationTopics({ topics, className, children, leading, }) {
181375
+ const names = conversationTopics(topics);
181376
+ if (names.length === 0 && !children && !leading)
181377
+ return null;
181378
+ return (jsxs("div", { className: cn('flex flex-wrap items-center gap-2', className), "data-testid": "conversation-topics", children: [leading, names.map((name) => (jsx("span", { className: "rounded-[5px] bg-gray-100 px-3 py-1 text-xs text-gray-600", children: name }, name))), children] }));
181379
+ }
181380
+
181381
+ const clean$2 = (value) => typeof value === 'string' && value.trim() ? value.trim() : undefined;
181382
+ /** The first human turn of a conversation, from an array or its JSON string form. */
181383
+ function firstHumanMessage(messages) {
181384
+ let turns = messages;
181385
+ if (typeof turns === 'string') {
181386
+ try {
181387
+ turns = JSON.parse(turns);
181388
+ }
181389
+ catch (_a) {
181390
+ return undefined;
181391
+ }
181392
+ }
181393
+ if (!Array.isArray(turns))
181394
+ return undefined;
181395
+ const first = turns[0];
181396
+ return clean$2(first === null || first === void 0 ? void 0 : first.human);
181397
+ }
181398
+ /**
181399
+ * One title for a conversation, on every surface: the backend's `title` when
181400
+ * it has one, else `first_user_message`, else the first human turn, else the
181401
+ * caller's fallback label. Endpoints differ in which of these they return;
181402
+ * this keeps Transcripts and both History tabs naming a conversation alike.
181403
+ */
181404
+ function conversationTitle(source, fallback) {
181405
+ var _a, _b, _c;
181406
+ return ((_c = (_b = (_a = clean$2(source === null || source === void 0 ? void 0 : source.title)) !== null && _a !== void 0 ? _a : clean$2(source === null || source === void 0 ? void 0 : source.first_user_message)) !== null && _b !== void 0 ? _b : firstHumanMessage(source === null || source === void 0 ? void 0 : source.messages)) !== null && _c !== void 0 ? _c : fallback);
181407
+ }
181408
+
181409
+ /**
181410
+ * A conversation's timestamp the way every history surface shows it:
181411
+ * "Sep 4, 2026, 3:56 PM · 21 days ago". Empty for a missing or invalid value.
181412
+ */
181413
+ const formatSessionDate = (value) => {
181414
+ if (!value)
181415
+ return '';
181416
+ const date = new Date(value);
181417
+ if (Number.isNaN(date.getTime()))
181418
+ return '';
181419
+ return `${format$1(date, 'MMM d, yyyy, h:mm a')} · ${formatDistanceToNow(date, { addSuffix: true })}`;
181420
+ };
181421
+
181190
181422
  const offsetFormatCache = {};
181191
181423
  const offsetCache = {};
181192
181424
 
@@ -220351,14 +220583,6 @@ function stripMarkdown$1(text) {
220351
220583
  * When a conversation happened: the calendar date and time, followed by the
220352
220584
  * relative "x ago" so both the exact day and recency read at a glance.
220353
220585
  */
220354
- const formatSessionDate = (value) => {
220355
- if (!value)
220356
- return '';
220357
- const date = new Date(value);
220358
- if (Number.isNaN(date.getTime()))
220359
- return '';
220360
- return `${format$1(date, 'MMM d, yyyy, h:mm a')} · ${formatDistanceToNow(date, { addSuffix: true })}`;
220361
- };
220362
220586
  /**
220363
220587
  * The list endpoint may serialize `messages` as a JSON string while the detail
220364
220588
  * endpoint returns a proper array; accept either shape.
@@ -220405,11 +220629,6 @@ function downloadTextFile(filename, content, mime) {
220405
220629
  document.body.removeChild(link);
220406
220630
  URL.revokeObjectURL(blobUrl);
220407
220631
  }
220408
- const SENTIMENT_CLASSES = {
220409
- positive: 'bg-green-100 text-green-800 border-transparent',
220410
- negative: 'bg-red-100 text-red-800 border-transparent',
220411
- neutral: 'bg-gray-100 text-gray-600 border-transparent',
220412
- };
220413
220632
  /**
220414
220633
  * Agent scope filter — the same inline searchable autocomplete the Billing
220415
220634
  * tab's Agent Limits section and the Memory tab use (`SearchSelect`): type to
@@ -220473,7 +220692,7 @@ function ConversationMessages({ messages, username, agent, fallbackEmail, }) {
220473
220692
  name: file.name,
220474
220693
  contentType: file.content_type,
220475
220694
  url: file.url,
220476
- })) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-700", children: agent.name }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_c = message.ai) !== null && _c !== void 0 ? _c : '' })] })] })] }, index));
220695
+ })) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx(AgentMessageAvatar, { agent: agent, fallback: t('chatHistoryChatHistoryTab.aiAvatarLabel') }), jsxs("div", { className: "min-w-0 flex-1 overflow-x-hidden", children: [jsx(AgentSettingsLink, { uniqueId: agent.uniqueId, name: agent.name, className: "font-medium text-gray-700" }), jsx(Markdown, { className: "mt-1 text-sm text-gray-500", children: (_c = message.ai) !== null && _c !== void 0 ? _c : '' })] })] })] }, index));
220477
220696
  }) }));
220478
220697
  }
220479
220698
  /**
@@ -220482,12 +220701,11 @@ function ConversationMessages({ messages, username, agent, fallbackEmail, }) {
220482
220701
  * download of just this conversation.
220483
220702
  */
220484
220703
  function ConversationPreview({ org, username, session, agent, }) {
220485
- var _a;
220486
220704
  const t = useT();
220487
220705
  const inlineMessages = normalizeMessages(session.messages);
220488
220706
  const { data: detail, isLoading } = useGetUserChatHistorySessionQuery({ org, userId: username, sessionId: session.id }, { skip: inlineMessages.length > 0 });
220489
220707
  const messages = inlineMessages.length > 0 ? inlineMessages : normalizeMessages(detail === null || detail === void 0 ? void 0 : detail.messages);
220490
- const title = stripMarkdown$1(session.title || ((_a = messages[0]) === null || _a === void 0 ? void 0 : _a.human) || t('chatHistoryChatHistoryTab.conversation'));
220708
+ const title = stripMarkdown$1(conversationTitle({ title: session.title, first_user_message: session.first_user_message, messages }, t('chatHistoryChatHistoryTab.conversation')));
220491
220709
  if (isLoading) {
220492
220710
  return (jsx("div", { className: "flex items-center justify-center py-10", children: jsx(Spinner, {}) }));
220493
220711
  }
@@ -220524,6 +220742,7 @@ function ChatHistoryTab({ org, username }) {
220524
220742
  var _a;
220525
220743
  const known = agentInfo[session.mentor];
220526
220744
  return {
220745
+ uniqueId: session.mentor || undefined,
220527
220746
  name: session.mentor_name ||
220528
220747
  (known === null || known === void 0 ? void 0 : known.name) ||
220529
220748
  ((_a = filters.agent) === null || _a === void 0 ? void 0 : _a.name) ||
@@ -220536,6 +220755,12 @@ function ChatHistoryTab({ org, username }) {
220536
220755
  const sentimentOptions = ((_a = filterOptions === null || filterOptions === void 0 ? void 0 : filterOptions.sentiment) === null || _a === void 0 ? void 0 : _a.length)
220537
220756
  ? filterOptions.sentiment
220538
220757
  : DEFAULT_SENTIMENTS;
220758
+ const sentimentLabels = React__default.useMemo(() => ({
220759
+ positive: t('chatHistoryChatHistoryTab.positive'),
220760
+ neutral: t('chatHistoryChatHistoryTab.neutral'),
220761
+ negative: t('chatHistoryChatHistoryTab.negative'),
220762
+ userSentiment: t('chatHistoryChatHistoryTab.userSentiment'),
220763
+ }), [t]);
220539
220764
  const sentimentLabel = React__default.useCallback((value) => {
220540
220765
  const key = value.toLowerCase();
220541
220766
  if (key === 'positive')
@@ -220559,22 +220784,18 @@ function ChatHistoryTab({ org, username }) {
220559
220784
  : t('chatHistoryChatHistoryTab.export')] }) })] }) }), isLoading && (jsx("div", { className: "flex w-full flex-1 items-center justify-center py-10", children: jsx(Spinner, {}) })), isError && !isLoading && (jsx("div", { className: "flex flex-1 flex-col items-center justify-center py-6", children: jsx("p", { className: "text-sm text-gray-600", children: isForbidden
220560
220785
  ? t('chatHistoryChatHistoryTab.forbidden')
220561
220786
  : t('chatHistoryChatHistoryTab.loadFailed') }) })), sessions.length === 0 && !isLoading && !isError && (jsx("div", { className: "flex flex-1 flex-col items-center justify-center py-6", children: jsx("p", { className: "text-sm text-gray-600", children: t('chatHistoryChatHistoryTab.noConversations') }) })), sessions.length > 0 && (jsxs("div", { className: "grid min-h-[320px] flex-1 grid-cols-1 gap-6 md:grid-cols-2", children: [jsxs("div", { className: "col-span-full flex min-h-0 flex-col gap-3 md:col-span-1", children: [jsx("div", { className: "scrollbar-hide min-h-0 flex-1 overflow-y-auto rounded-md border border-neutral-200", tabIndex: 0, role: "region", "aria-label": t('chatHistoryChatHistoryTab.conversationListLabel'), children: sessions.map((session) => {
220562
- var _a, _b, _c, _d, _e;
220787
+ var _a;
220563
220788
  const firstMessage = normalizeMessages(session.messages)[0];
220564
- const title = stripMarkdown$1(session.title ||
220565
- (firstMessage === null || firstMessage === void 0 ? void 0 : firstMessage.human) ||
220566
- session.first_user_message ||
220567
- t('chatHistoryChatHistoryTab.conversation'));
220789
+ const title = stripMarkdown$1(conversationTitle(session, t('chatHistoryChatHistoryTab.conversation')));
220568
220790
  const preview = stripMarkdown$1((_a = firstMessage === null || firstMessage === void 0 ? void 0 : firstMessage.ai) !== null && _a !== void 0 ? _a : '');
220569
- const sentiment = (_c = (_b = session.sentiment) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== null && _c !== void 0 ? _c : '';
220570
220791
  return (jsxs("div", { role: "button", tabIndex: 0, "data-testid": "history-conversation-row", "data-session-id": session.id, className: cn('flex cursor-pointer items-center justify-between border-b border-neutral-200 p-4 last:border-b-0', (selectedSession === null || selectedSession === void 0 ? void 0 : selectedSession.id) === session.id ? 'bg-gray-100' : 'hover:bg-gray-50'), onClick: () => handleConversationClick(session), onKeyDown: (event) => {
220571
220792
  if (event.key === 'Enter' || event.key === ' ') {
220572
220793
  event.preventDefault();
220573
220794
  handleConversationClick(session);
220574
220795
  }
220575
- }, children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [jsxs("span", { className: "flex items-center gap-2 text-sm text-gray-600", children: [formatSessionDate(session.inserted_at), sentiment && (jsx(Badge, { variant: "outline", className: cn('font-normal', (_d = SENTIMENT_CLASSES[sentiment]) !== null && _d !== void 0 ? _d : SENTIMENT_CLASSES.neutral), children: sentimentLabel((_e = session.sentiment) !== null && _e !== void 0 ? _e : '') }))] }), jsx("span", { className: "max-w-[100px] truncate text-sm text-gray-900 sm:max-w-[200px]", children: resolveAgent(session).name })] }), jsx("div", { className: "font-medium text-gray-900", children: textTruncate$2(title, 50) }), preview && (jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: textTruncate$2(preview, 60) }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, session.id));
220796
+ }, children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [jsx("span", { className: "shrink-0 text-sm text-gray-600", children: formatSessionDate(session.inserted_at) }), jsx(AgentSettingsLink, { uniqueId: resolveAgent(session).uniqueId, name: resolveAgent(session).name, className: "min-w-0 text-right text-sm text-gray-900" })] }), jsx("div", { className: "font-medium text-gray-900", children: textTruncate$2(title, 50) }), jsx(ConversationTopics, { topics: session.topics, className: "mt-1", leading: jsx(ConversationSentiment, { sentiment: session.sentiment, labels: sentimentLabels, compact: true }) }), preview && (jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: textTruncate$2(preview, 60) }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, session.id));
220576
220797
  }) }), totalPages > 1 && (jsx("div", { className: "flex-shrink-0", children: jsx(IblPagination, { currentPage: page, totalPages: totalPages, onPageChange: setPage, disabled: isFetching || isLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden min-h-0 flex-col overflow-y-auto rounded-lg border border-neutral-200 bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('chatHistoryChatHistoryTab.conversationPreviewLabel'), children: selectedSession ? (jsx(ConversationPreview, { org: org, username: username, session: selectedSession, agent: resolveAgent(selectedSession) })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: t('chatHistoryChatHistoryTab.selectPrompt') })) })] })), jsx(Dialog, { open: isPreviewModalOpen, onOpenChange: setIsPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: selectedSession
220577
- ? stripMarkdown$1(selectedSession.title || t('chatHistoryChatHistoryTab.conversation'))
220798
+ ? stripMarkdown$1(conversationTitle(selectedSession, t('chatHistoryChatHistoryTab.conversation')))
220578
220799
  : t('chatHistoryChatHistoryTab.conversation') }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: selectedSession && (jsx(ConversationPreview, { org: org, username: username, session: selectedSession, agent: resolveAgent(selectedSession) })) })] }) })] }), jsx(TabsContent, { value: "exports", className: "mt-0 min-h-0 flex-1 overflow-y-auto data-[state=inactive]:hidden", children: jsx(ReportHistory, { org: org, username: username, onDownload: downloadFromUrl }) })] }));
220579
220800
  }
220580
220801
 
@@ -222940,6 +223161,201 @@ function InvitedUsersDialog({ tenant, onClose }) {
222940
223161
  }, children: jsx(DialogPortal, { forceMount: true, children: jsxs(DialogContent, { "aria-label": t('profileInvitedUsers.dialogAriaLabel'), className: "max-w-sm rounded-2xl px-6 py-8 pointer-events-auto", "aria-describedby": "invited-users-dialog", children: [jsx(DialogHeader, { className: "relative flex-row justify-between", children: jsx(DialogTitle, { className: "text-blue-600 text-xl font-semibold", children: t('profileInvitedUsers.title') }) }), jsxs("div", { className: "mt-4 space-y-1", children: [jsx("p", { className: "text-gray-500 text-sm", children: t('profileInvitedUsers.pendingDescription') }), jsx("form", { children: jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('profileInvitedUsers.searchPlaceholder'), className: "mt-2" }) })] }), jsxs("div", { children: [jsx("h2", { className: "text-md text-gray-600 font-semibold mb-4", children: t('profileInvitedUsers.emailHeading') }), invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.map((user) => (jsx("div", { className: "border-b-gray-50 pb-2 mb-4 text-sm text-gray-600 dark:text-gray-400", children: user.email }, user.id))), (invitedUsers === null || invitedUsers === void 0 ? void 0 : invitedUsers.results.length) === 0 && (jsx("p", { className: "text-gray", children: t('profileInvitedUsers.noRecords') }))] })] }) }) }));
222941
223162
  }
222942
223163
 
223164
+ /**
223165
+ * Whether, and how, a retrieved document can be shown inside the app.
223166
+ *
223167
+ * The Datasets tab accepts PDFs, Office files, CSV/TXT/Markdown, images,
223168
+ * audio, video, ZIPs, GitHub repos, YouTube videos, web pages and crawls, and
223169
+ * links to Google Drive, OneDrive and Dropbox. Only some of those a browser
223170
+ * can render on its own; the rest get no Preview button rather than a blank
223171
+ * frame. Decided from the source URL (extension and host) and, when the
223172
+ * backend sends one, the document's kind.
223173
+ */
223174
+ const IMAGE = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp', 'avif']);
223175
+ const AUDIO = new Set(['mp3', 'wav', 'ogg', 'oga', 'm4a', 'aac', 'flac']);
223176
+ const VIDEO = new Set(['mp4', 'webm', 'ogv', 'mov', 'm4v']);
223177
+ /** Browsers cannot render these; a frame would only offer a download. */
223178
+ const NOT_VIEWABLE = new Set([
223179
+ 'doc',
223180
+ 'docx',
223181
+ 'ppt',
223182
+ 'pptx',
223183
+ 'xls',
223184
+ 'xlsx',
223185
+ 'odt',
223186
+ 'odp',
223187
+ 'ods',
223188
+ 'rtf',
223189
+ 'zip',
223190
+ 'rar',
223191
+ '7z',
223192
+ 'tar',
223193
+ 'gz',
223194
+ 'tgz',
223195
+ ]);
223196
+ /** Hosts that refuse to be framed, or need the viewer's own sign-in. */
223197
+ const UNFRAMEABLE_HOSTS = [
223198
+ 'github.com',
223199
+ 'dropbox.com',
223200
+ 'onedrive.live.com',
223201
+ '1drv.ms',
223202
+ 'sharepoint.com',
223203
+ ];
223204
+ const hostMatches = (host, domain) => host === domain || host.endsWith(`.${domain}`);
223205
+ const extensionOf = (pathname) => {
223206
+ var _a;
223207
+ const last = (_a = pathname.split('/').pop()) !== null && _a !== void 0 ? _a : '';
223208
+ const dot = last.lastIndexOf('.');
223209
+ return dot > 0 ? last.slice(dot + 1).toLowerCase() : '';
223210
+ };
223211
+ const youtubeEmbed = (url) => {
223212
+ var _a;
223213
+ const host = url.hostname.toLowerCase().replace(/^(www|m)\./, '');
223214
+ let id = null;
223215
+ if (host === 'youtu.be')
223216
+ id = (_a = url.pathname.split('/')[1]) !== null && _a !== void 0 ? _a : null;
223217
+ else if (host === 'youtube.com' || host === 'youtube-nocookie.com') {
223218
+ const [, section, rest] = url.pathname.split('/');
223219
+ if (section === 'watch')
223220
+ id = url.searchParams.get('v');
223221
+ else if (section === 'embed' || section === 'shorts' || section === 'live' || section === 'v')
223222
+ id = rest !== null && rest !== void 0 ? rest : null;
223223
+ }
223224
+ return id && /^[\w-]{6,}$/.test(id) ? `https://www.youtube-nocookie.com/embed/${id}` : null;
223225
+ };
223226
+ const googleEmbed = (url) => {
223227
+ const host = url.hostname.toLowerCase();
223228
+ if (host === 'drive.google.com') {
223229
+ const file = url.pathname.match(/^\/file\/d\/([\w-]+)/);
223230
+ if (file)
223231
+ return `https://drive.google.com/file/d/${file[1]}/preview`;
223232
+ const open = url.pathname === '/open' && url.searchParams.get('id');
223233
+ if (open)
223234
+ return `https://drive.google.com/file/d/${open}/preview`;
223235
+ return null;
223236
+ }
223237
+ if (host === 'docs.google.com') {
223238
+ const doc = url.pathname.match(/^\/(document|spreadsheets|presentation)\/d\/([\w-]+)/);
223239
+ return doc ? `https://docs.google.com/${doc[1]}/d/${doc[2]}/preview` : null;
223240
+ }
223241
+ return null;
223242
+ };
223243
+ /** A kind the backend may name on the document, when it says one. */
223244
+ const kindFromHint = (hint) => {
223245
+ const h = (hint !== null && hint !== void 0 ? hint : '').toLowerCase();
223246
+ if (!h)
223247
+ return null;
223248
+ if (/image|photo|picture/.test(h))
223249
+ return 'image';
223250
+ if (/audio|podcast/.test(h))
223251
+ return 'audio';
223252
+ if (/video(?!.*youtube)/.test(h) && !/youtube/.test(h))
223253
+ return 'video';
223254
+ if (/docx|word|powerpoint|pptx|excel|xlsx|spreadsheet|zip|archive|course/.test(h))
223255
+ return 'none';
223256
+ return null;
223257
+ };
223258
+ /**
223259
+ * `null` when the document cannot be shown here: no URL, a host that refuses
223260
+ * framing, or a file type a browser cannot render (Office files, archives).
223261
+ */
223262
+ function previewPlan(source, hint) {
223263
+ const raw = (source !== null && source !== void 0 ? source : '').trim();
223264
+ if (!/^https?:\/\//i.test(raw))
223265
+ return null;
223266
+ let url;
223267
+ try {
223268
+ url = new URL(raw);
223269
+ }
223270
+ catch (_a) {
223271
+ return null;
223272
+ }
223273
+ const host = url.hostname.toLowerCase();
223274
+ const yt = youtubeEmbed(url);
223275
+ if (yt)
223276
+ return { kind: 'frame', src: yt };
223277
+ // A YouTube page that is not a video (a channel, a playlist) cannot be framed.
223278
+ if (/(^|\.)(youtube\.com|youtu\.be|youtube-nocookie\.com)$/.test(host))
223279
+ return null;
223280
+ const google = googleEmbed(url);
223281
+ if (google)
223282
+ return { kind: 'frame', src: google };
223283
+ if (host === 'drive.google.com' || host === 'docs.google.com')
223284
+ return null;
223285
+ if (host === 'raw.githubusercontent.com')
223286
+ return { kind: 'frame', src: raw };
223287
+ if (UNFRAMEABLE_HOSTS.some((domain) => hostMatches(host, domain)))
223288
+ return null;
223289
+ const hinted = kindFromHint(hint);
223290
+ if (hinted === 'none')
223291
+ return null;
223292
+ if (hinted)
223293
+ return { kind: hinted, src: raw };
223294
+ const ext = extensionOf(url.pathname);
223295
+ if (IMAGE.has(ext))
223296
+ return { kind: 'image', src: raw };
223297
+ if (AUDIO.has(ext))
223298
+ return { kind: 'audio', src: raw };
223299
+ if (VIDEO.has(ext))
223300
+ return { kind: 'video', src: raw };
223301
+ if (NOT_VIEWABLE.has(ext))
223302
+ return null;
223303
+ // PDFs, text, CSV, Markdown, HTML, JSON and extension-less API URLs: the
223304
+ // browser renders these itself (its PDF viewer, or as text or a page).
223305
+ return { kind: 'frame', src: raw };
223306
+ }
223307
+
223308
+ /** The shape a retrieved document is listed by: the session query's `VectorResult`
223309
+ * and a transcript's `TranscriptRetrievedDocument` both satisfy it. */
223310
+ /** Only `http(s)` sources are links; a bare file name or dataset id is not. */
223311
+ const isHttpUrl = (value) => /^https?:\/\//i.test((value !== null && value !== void 0 ? value : '').trim());
223312
+ /** `0.7` → `70`; anything unusable → undefined. */
223313
+ const relevancePercent = (score) => typeof score === 'number' && Number.isFinite(score)
223314
+ ? Math.round(Math.max(0, Math.min(1, score)) * 100)
223315
+ : undefined;
223316
+ /**
223317
+ * The document's URL with a text fragment for the matched passage's opening
223318
+ * words, so a browser that supports fragments (Chromium for HTML pages) opens
223319
+ * the page scrolled to, and highlighting, the passage. PDFs and other
223320
+ * viewers ignore it; a frame cannot honour it, so only new-tab links get it.
223321
+ */
223322
+ const deepLink = (url, snippet) => {
223323
+ const words = (snippet !== null && snippet !== void 0 ? snippet : '').replace(/\s+/g, ' ').trim().split(' ').slice(0, 8).join(' ');
223324
+ const text = words.replace(/[\s.,;:!?"'”’)\]]+$/, '');
223325
+ if (text.length < 12)
223326
+ return url;
223327
+ return `${url.split('#')[0]}#:~:text=${encodeURIComponent(text)}`;
223328
+ };
223329
+ const hostOf = (value) => {
223330
+ try {
223331
+ return new URL(value).host;
223332
+ }
223333
+ catch (_a) {
223334
+ return '';
223335
+ }
223336
+ };
223337
+ /**
223338
+ * One retrieved document: its title (a new-tab link when the source is a
223339
+ * URL), its relevance, the matched passage, and — for URLs — a preview the
223340
+ * reader can open (the title link beside it still opens the source in a new
223341
+ * tab).
223342
+ */
223343
+ function RetrievedDocumentRow({ doc }) {
223344
+ var _a, _b;
223345
+ const t = useT();
223346
+ const [previewOpen, setPreviewOpen] = React.useState(false);
223347
+ const source = ((_a = doc.source) !== null && _a !== void 0 ? _a : '').trim();
223348
+ const url = isHttpUrl(source) ? source : null;
223349
+ const title = doc.title || (url ? hostOf(url) : source);
223350
+ const relevance = relevancePercent(doc.score);
223351
+ const openHref = url ? deepLink(url, doc.snippet) : null;
223352
+ // Preview only what a browser can show; Office files, archives and
223353
+ // unframeable hosts get no button rather than a blank frame.
223354
+ const preview = previewPlan(source, doc.type);
223355
+ return (jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", "data-testid": "retrieved-document", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-start justify-between gap-2", children: [jsx("div", { className: "min-w-0", children: url ? (jsxs("a", { href: openHref !== null && openHref !== void 0 ? openHref : url, target: "_blank", rel: "noopener noreferrer", className: "inline-flex min-w-0 items-center gap-1 text-sm font-semibold text-[#646464] hover:text-blue-600", "aria-label": t('documentSidebar.openInNewTab', { title }), "data-testid": "retrieved-document-link", children: [jsx("span", { className: "min-w-0 break-words", children: title }), jsx(SquareArrowOutUpRight, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" })] })) : (jsx("span", { className: "text-sm font-semibold break-words text-[#646464]", children: title })) }), relevance !== undefined && (jsx("span", { className: "shrink-0 rounded-[5px] bg-blue-50 px-2 py-0.5 text-xs text-blue-700", title: t('documentSidebar.relevanceHint'), "data-testid": "retrieved-document-relevance", children: t('documentSidebar.relevance', { percent: relevance }) }))] }), preview && (jsxs("div", { className: "pt-1", children: [jsxs(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => setPreviewOpen(true), className: "h-7 gap-1.5 px-2.5 text-xs", "data-testid": "retrieved-document-preview-toggle", children: [jsx(Eye, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), t('documentSidebar.preview')] }), jsx(Dialog, { open: previewOpen, onOpenChange: setPreviewOpen, children: jsxs(DialogContent, { className: "flex flex-col gap-0 overflow-hidden rounded-lg p-0",
223356
+ // Inline: the size must not depend on a host's Tailwind scan.
223357
+ style: { width: '94vw', maxWidth: '94vw', height: '92vh' }, "data-testid": "retrieved-document-preview", children: [jsxs(DialogHeader, { className: "border-b border-gray-200 bg-white px-5 py-3 pr-12 text-left", children: [jsxs(DialogTitle, { className: "flex min-w-0 items-center gap-2 text-base font-semibold text-gray-800", children: [jsx("span", { className: "min-w-0 truncate", children: title }), jsx("a", { href: (_b = openHref !== null && openHref !== void 0 ? openHref : url) !== null && _b !== void 0 ? _b : undefined, target: "_blank", rel: "noopener noreferrer", className: "inline-flex shrink-0 items-center text-blue-600 hover:text-blue-700", "aria-label": t('documentSidebar.openInNewTab', { title }), children: jsx(SquareArrowOutUpRight, { className: "h-4 w-4", "aria-hidden": "true" }) })] }), jsx(DialogDescription, { className: "flex flex-wrap items-center gap-2 text-xs text-gray-500", children: relevance !== undefined && (jsx("span", { className: "rounded-[5px] bg-blue-50 px-2 py-0.5 text-blue-700", children: t('documentSidebar.relevance', { percent: relevance }) })) })] }), jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center bg-gray-100", children: preview.kind === 'image' ? (jsx("img", { src: preview.src, alt: title, className: "h-full w-full object-contain" })) : preview.kind === 'audio' ? (jsx("audio", { src: preview.src, controls: true, className: "w-full max-w-2xl px-6" })) : preview.kind === 'video' ? (jsx("video", { src: preview.src, controls: true, className: "h-full w-full bg-black" })) : (jsx("iframe", { src: preview.src, title: title, referrerPolicy: "no-referrer", allow: "fullscreen; autoplay; encrypted-media; picture-in-picture", className: "h-full w-full border-0 bg-white" })) })] }) })] })), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }));
223358
+ }
222943
223359
  function DocumentSidebar({ isModal = false, sessionId = '', documents, }) {
222944
223360
  const t = useT();
222945
223361
  const [isCollapsed, setIsCollapsed] = React.useState(false);
@@ -222972,10 +223388,7 @@ function DocumentSidebar({ isModal = false, sessionId = '', documents, }) {
222972
223388
  backgroundColor: isCollapsed && !isModal ? 'white' : '#F3F6FB',
222973
223389
  // In the dialog the parent bounds the height; the rail caps itself.
222974
223390
  maxHeight: isModal ? undefined : 'calc(100vh - 80px)',
222975
- }, children: jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "min-h-0 flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => {
222976
- var _a, _b;
222977
- return (jsxs(React.Fragment, { children: [jsx("div", { className: "rounded-lg p-2 hover:bg-gray-50", children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Radio$1, { className: "mt-1 h-4 w-4 text-gray-400" }), jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Link$2, { href: (_a = doc.source) !== null && _a !== void 0 ? _a : '', target: "_blank", className: "min-w-0", children: jsx("span", { className: "text-sm font-semibold break-words text-[#646464] hover:text-blue-600", children: doc.title }) }), jsx("div", { className: "flex items-center gap-2 self-center", children: jsx("span", { className: "text-xs text-gray-500", children: (_b = doc === null || doc === void 0 ? void 0 : doc.score) === null || _b === void 0 ? void 0 : _b.toFixed(2) }) })] }), jsx("p", { className: "line-clamp-6 pt-[10px] text-[14px] text-gray-600", children: doc.snippet })] })] }) }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index));
222978
- }) }) }) }))] }) })) : null }));
223391
+ }, children: jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [!isModal && (jsxs("div", { className: cn('flex cursor-pointer items-center gap-2 border-b border-gray-200 p-4 transition-colors duration-200', isCollapsed && 'h-[80px] justify-center rounded-tl-[5px] rounded-bl-[5px] p-2', 'hover:bg-blue-100'), onClick: toggleCollapse, style: { backgroundColor: '#F3F6FB' }, children: [jsx(FileText, { className: "h-5 w-5 shrink-0 text-blue-600" }), !isCollapsed && (jsxs(Fragment$1, { children: [jsx("h2", { className: "text-base font-medium text-[#646464]", children: t('documentSidebar.retrievedDocuments') }), jsx(ChevronRight, { className: cn('ml-auto h-4 w-4 text-gray-400 transition-transform', isCollapsed ? 'rotate-180' : '') })] }))] })), (!isCollapsed || isModal) && (jsx(Fragment$1, { children: jsx("div", { className: "min-h-0 flex-1 overflow-auto", style: { backgroundColor: '#F3F6FB' }, children: jsx("div", { className: "mt-0 space-y-1 p-2", children: vectorDocuments.map((doc, index) => (jsxs(React.Fragment, { children: [jsx(RetrievedDocumentRow, { doc: doc }), index < vectorDocuments.length - 1 && (jsx("div", { className: "my-2 border-t border-gray-200" }))] }, index))) }) }) }))] }) })) : null }));
222979
223392
  }
222980
223393
 
222981
223394
  /**
@@ -230320,15 +230733,23 @@ const sanitizeDomain = (domain) => domain
230320
230733
  .replace(/^https?:\/\//, '')
230321
230734
  .replace(/\/.*$/, '')
230322
230735
  .replace(/^www\./, '');
230323
- const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, projects, isLoadingProjects, isSubmitting, onSubmitPlatformDomain, onSubmitAppDomain, }) => {
230736
+ /** One label under the platform's shared domain — the backend's own rule. */
230737
+ const SUBDOMAIN_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
230738
+ /** Typed like a username: only case and surrounding whitespace are forgiven. */
230739
+ const sanitizeSubdomain = (subdomain) => subdomain.trim().toLowerCase();
230740
+ const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, defaultMode = 'platform', projects, isLoadingProjects, isSubmitting, onSubmitPlatformDomain, onSubmitAppDomain, }) => {
230741
+ var _a;
230324
230742
  const t = useT();
230325
- const [mode, setMode] = useState(canAddPlatformDomain ? 'platform' : 'app');
230743
+ const initialMode = canAddPlatformDomain ? defaultMode : 'app';
230744
+ const [mode, setMode] = useState(initialMode);
230326
230745
  const [domainValue, setDomainValue] = useState('');
230746
+ const [subdomainValue, setSubdomainValue] = useState('');
230327
230747
  const [projectId, setProjectId] = useState('');
230328
230748
  const reset = () => {
230329
230749
  setDomainValue('');
230750
+ setSubdomainValue('');
230330
230751
  setProjectId('');
230331
- setMode(canAddPlatformDomain ? 'platform' : 'app');
230752
+ setMode(initialMode);
230332
230753
  };
230333
230754
  const close = () => {
230334
230755
  reset();
@@ -230340,10 +230761,27 @@ const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, projects, i
230340
230761
  await onSubmitPlatformDomain(sanitizeDomain(domainValue));
230341
230762
  }
230342
230763
  else {
230343
- await onSubmitAppDomain(Number(projectId), sanitizeDomain(domainValue));
230764
+ await onSubmitAppDomain(Number(projectId), {
230765
+ domain: sanitizeDomain(domainValue),
230766
+ subdomain: sanitizeSubdomain(subdomainValue),
230767
+ });
230344
230768
  }
230345
230769
  };
230346
- const canSubmit = mode === 'platform' ? domainValue.trim().length > 0 : projectId.trim().length > 0;
230770
+ const hasDomain = domainValue.trim().length > 0;
230771
+ const subdomain = sanitizeSubdomain(subdomainValue);
230772
+ const hasSubdomain = subdomain.length > 0;
230773
+ // An app is served at exactly one address, and a subdomain has to be one the
230774
+ // backend will take — say which rule is unmet, and only once something was
230775
+ // typed, so an untouched form stays quiet.
230776
+ const appAddressProblem = mode === 'app' && hasDomain && hasSubdomain
230777
+ ? 'both'
230778
+ : mode === 'app' && hasSubdomain && !SUBDOMAIN_RE.test(subdomain)
230779
+ ? 'invalid'
230780
+ : null;
230781
+ const canSubmit = mode === 'platform'
230782
+ ? hasDomain
230783
+ : projectId.trim().length > 0 && (hasDomain || hasSubdomain) && appAddressProblem === null;
230784
+ const selectedProject = projects.find((project) => String(project.id) === projectId);
230347
230785
  return (jsx(Dialog, { open: open, onOpenChange: (next) => {
230348
230786
  if (!next) {
230349
230787
  reset();
@@ -230352,9 +230790,11 @@ const AddDomainDialog = ({ open, onOpenChange, canAddPlatformDomain, projects, i
230352
230790
  }, children: jsxs(DialogContent, { className: "sm:max-w-[480px]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: t('advancedCustomDomain.dialogTitle') }), jsx(DialogDescription, { children: t('advancedCustomDomain.dialogDescription') })] }), jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-kind", className: "text-sm", children: t('advancedCustomDomain.kindLabel') }), jsxs(Select$1, { value: mode, onValueChange: (next) => setMode(next), children: [jsx(SelectTrigger, { id: "add-domain-kind", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [canAddPlatformDomain && (jsx(SelectItem, { value: "platform", children: t('advancedCustomDomain.kindPlatform') })), jsx(SelectItem, { value: "app", children: t('advancedCustomDomain.kindApp') })] })] })] }), mode === 'app' && (jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-project", className: "text-sm", children: t('advancedCustomDomain.projectLabel') }), isLoadingProjects ? (jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.loading') })) : projects.length === 0 ? (
230353
230791
  // The backend lists only the caller's own projects, so this is
230354
230792
  // "you have not deployed anything", not "the platform has not".
230355
- jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noProjects') })) : (jsxs(Select$1, { value: projectId, onValueChange: setProjectId, children: [jsx(SelectTrigger, { id: "add-domain-project", children: jsx(SelectValue, { placeholder: t('advancedCustomDomain.projectPlaceholder') }) }), jsx(SelectContent, { children: projects.map((project) => (jsx(SelectItem, { value: String(project.id), children: project.name }, project.id))) })] }))] })), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-name", className: "text-sm", children: t('advancedCustomDomain.customDomainLabel') }), jsx(Input, { id: "add-domain-name", value: domainValue, onChange: (event) => setDomainValue(event.target.value), placeholder: "example.com", className: "w-full", required: mode === 'platform' }), jsx("p", { className: "text-xs text-[#646464]", children: mode === 'app'
230793
+ jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noProjects') })) : (jsxs(Select$1, { value: projectId, onValueChange: setProjectId, children: [jsx(SelectTrigger, { id: "add-domain-project", children: jsx(SelectValue, { placeholder: t('advancedCustomDomain.projectPlaceholder') }) }), jsx(SelectContent, { children: projects.map((project) => (jsx(SelectItem, { value: String(project.id), children: project.name }, project.id))) })] }))] })), mode === 'app' && (jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-subdomain", className: "text-sm", children: t('advancedCustomDomain.subdomainLabel') }), jsx(Input, { id: "add-domain-subdomain", value: subdomainValue, onChange: (event) => setSubdomainValue(event.target.value),
230794
+ // The app's own name is the suggestion the deploy skill makes.
230795
+ placeholder: (_a = selectedProject === null || selectedProject === void 0 ? void 0 : selectedProject.name) !== null && _a !== void 0 ? _a : '', className: "w-full" }), jsx("p", { className: "text-xs text-[#646464]", children: t('advancedCustomDomain.subdomainHelp') })] })), jsxs("div", { className: "space-y-2", children: [jsx("label", { htmlFor: "add-domain-name", className: "text-sm", children: t('advancedCustomDomain.customDomainLabel') }), jsx(Input, { id: "add-domain-name", value: domainValue, onChange: (event) => setDomainValue(event.target.value), placeholder: "example.com", className: "w-full", required: mode === 'platform' }), jsx("p", { className: "text-xs text-[#646464]", children: mode === 'app'
230356
230796
  ? t('advancedCustomDomain.domainHelpApp')
230357
- : t('advancedCustomDomain.domainHelpPlatform') })] }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: close, children: t('advancedCustomDomain.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSubmitting || !canSubmit, children: [isSubmitting && jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('advancedCustomDomain.save')] })] })] })] }) }));
230797
+ : t('advancedCustomDomain.domainHelpPlatform') }), appAddressProblem === 'both' && (jsx("p", { className: "text-xs text-[#646464]", role: "status", children: t('advancedCustomDomain.oneOfSubdomainOrDomain') })), appAddressProblem === 'invalid' && (jsx("p", { className: "text-xs text-[#646464]", role: "status", children: t('advancedCustomDomain.subdomainInvalid') }))] }), jsxs("div", { className: "flex justify-end gap-2", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: close, children: t('advancedCustomDomain.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSubmitting || !canSubmit, children: [isSubmitting && jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t('advancedCustomDomain.save')] })] })] })] }) }));
230358
230798
  };
230359
230799
 
230360
230800
  var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
@@ -230546,7 +230986,7 @@ const resolveCname = async (domain, target) => {
230546
230986
  const body = await response.json();
230547
230987
  return Boolean((_a = body === null || body === void 0 ? void 0 : body.Answer) === null || _a === void 0 ? void 0 : _a.some((answer) => { var _a; return ((_a = answer.data) !== null && _a !== void 0 ? _a : '').replace(/\.$/, '') === target; }));
230548
230988
  };
230549
- const DomainRow = ({ row, sslValue, onReverify, onRemove, isBusy, onStatusChange, }) => {
230989
+ const DomainRow = ({ row, sharedApp, sslValue, onReverify, onRemove, isBusy, onStatusChange, }) => {
230550
230990
  var _a;
230551
230991
  const t = useT();
230552
230992
  const isHosted = row.spa === VERCEL_SPA;
@@ -230618,7 +231058,7 @@ const DomainRow = ({ row, sslValue, onReverify, onRemove, isBusy, onStatusChange
230618
231058
  const statusBadge = (jsx("span", { className: status === 'verified'
230619
231059
  ? 'rounded bg-green-100 px-2 py-1 text-xs text-green-800'
230620
231060
  : 'rounded bg-gray-100 px-2 py-1 text-xs text-gray-600', children: status === 'checking' ? (jsxs("span", { className: "flex items-center gap-1", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), t('advancedCustomDomain.verifying')] })) : (statusLabel()) }));
230621
- return (jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-3 dark:border-gray-700", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm", children: row.custom_domain }), jsx("span", { className: "text-xs text-[#646464]", children: isHosted ? t('advancedCustomDomain.kindApp') : t('advancedCustomDomain.kindPlatform') }), statusBadge] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", onClick: () => (isHosted ? void onReverify(row) : void checkSpaDomain()), disabled: isBusy || status === 'checking' || (!isHosted && !row.registered_with_dns_pro), "aria-label": t('advancedCustomDomain.reverifyAriaLabel', { domain: row.custom_domain }), children: isBusy || status === 'checking' ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsxs(Fragment$1, { children: [jsx(RefreshCw, { className: "mr-1 h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.reverify')] })) }), jsx(Button$1, { variant: "outline", size: "sm", className: "text-red-600 hover:text-red-700", onClick: () => setConfirmOpen(true), disabled: isBusy, "aria-label": t('advancedCustomDomain.removeAriaLabel', { domain: row.custom_domain }), children: isHosted ? t('advancedCustomDomain.detach') : t('advancedCustomDomain.delete') })] })] }), records.length > 0 ? (jsxs(Table$1, { className: "table-fixed", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { children: [jsx(TableHead, { className: "w-[90px]", children: t('advancedCustomDomain.tableType') }), jsx(TableHead, { className: "w-1/3", children: t('advancedCustomDomain.tableNameHost') }), jsx(TableHead, { children: t('advancedCustomDomain.tableValue') })] }) }), jsx(TableBody, { children: records.map((record, index) => (jsxs(TableRow$1, { children: [jsx(TableCell$1, { className: "text-sm", children: record.type }), jsx(TableCell$1, { children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.name }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.name, `name-${index}`), "aria-label": t('advancedCustomDomain.copyNameHostAriaLabel', {
231061
+ return (jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-3 dark:border-gray-700", children: [jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm", children: row.custom_domain }), jsx("span", { className: "text-xs text-[#646464]", children: isHosted ? t('advancedCustomDomain.kindApp') : t('advancedCustomDomain.kindPlatform') }), statusBadge] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Button$1, { variant: "outline", size: "sm", onClick: () => (isHosted ? void onReverify(row) : void checkSpaDomain()), disabled: isBusy || status === 'checking' || (!isHosted && !row.registered_with_dns_pro), "aria-label": t('advancedCustomDomain.reverifyAriaLabel', { domain: row.custom_domain }), children: isBusy || status === 'checking' ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsxs(Fragment$1, { children: [jsx(RefreshCw, { className: "mr-1 h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.reverify')] })) }), jsx(Button$1, { variant: "outline", size: "sm", className: "text-red-600 hover:text-red-700", onClick: () => setConfirmOpen(true), disabled: isBusy, "aria-label": t('advancedCustomDomain.removeAriaLabel', { domain: row.custom_domain }), children: isHosted ? t('advancedCustomDomain.detach') : t('advancedCustomDomain.delete') })] })] }), sharedApp ? (jsxs("div", { className: "space-y-1 text-sm text-[#646464]", children: [jsxs("p", { children: [t('advancedCustomDomain.sharedDomainLine'), ' ', jsx("a", { href: `https://${row.custom_domain}`, target: "_blank", rel: "noreferrer", className: "underline", children: t('advancedCustomDomain.openSite', { domain: row.custom_domain }) })] }), jsx("p", { className: "text-xs", children: t('advancedCustomDomain.servesApp', { app: sharedApp.name }) })] })) : records.length > 0 ? (jsxs(Table$1, { className: "table-fixed", children: [jsx(TableHeader$1, { children: jsxs(TableRow$1, { children: [jsx(TableHead, { className: "w-[90px]", children: t('advancedCustomDomain.tableType') }), jsx(TableHead, { className: "w-1/3", children: t('advancedCustomDomain.tableNameHost') }), jsx(TableHead, { children: t('advancedCustomDomain.tableValue') })] }) }), jsx(TableBody, { children: records.map((record, index) => (jsxs(TableRow$1, { children: [jsx(TableCell$1, { className: "text-sm", children: record.type }), jsx(TableCell$1, { children: jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.name }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.name, `name-${index}`), "aria-label": t('advancedCustomDomain.copyNameHostAriaLabel', {
230622
231062
  domain: record.name,
230623
231063
  }), children: copiedKey === `name-${index}` ? (jsx(Check, { className: "h-4 w-4 text-blue-600", "aria-hidden": "true" })) : (jsx(Copy, { className: "h-4 w-4", "aria-hidden": "true" })) })] }) }), jsxs(TableCell$1, { children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm break-all", children: record.value }), jsx(Button$1, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: () => handleCopy(record.value, `value-${index}`), "aria-label": t('advancedCustomDomain.copyValueAriaLabel'), children: copiedKey === `value-${index}` ? (jsx(Check, { className: "h-4 w-4 text-blue-600", "aria-hidden": "true" })) : (jsx(Copy, { className: "h-4 w-4", "aria-hidden": "true" })) })] }), record.reason && jsx("p", { className: "mt-1 text-xs text-[#646464]", children: record.reason })] })] }, `${record.type}-${record.name}-${record.value}`))) })] })) : (jsx("p", { className: "text-sm text-[#646464]", children: t('advancedCustomDomain.noRecordsYet') })), jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: jsxs(AlertDialogContent, { children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: t('advancedCustomDomain.confirmRemoveTitle') }), jsx(AlertDialogDescription, { children: isHosted
230624
231064
  ? t('advancedCustomDomain.confirmDetachBody', { domain: row.custom_domain })
@@ -230676,9 +231116,20 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
230676
231116
  const { data, isLoading, isFetching, refetch } = useGetCustomDomainsQuery({
230677
231117
  params: { platform_key: platformKey },
230678
231118
  });
230679
- // Only the caller's own deployed apps are listable, and only the Add dialog
230680
- // needs them — so this stays idle until it opens.
230681
- const { data: projectData, isFetching: isLoadingProjects } = useGetHostingProjectsQuery({ org: platformKey, username }, { skip: !isModalOpen || !username });
231119
+ // Only the caller's own deployed apps are listable. The Add dialog offers
231120
+ // them, and a row on the platform's shared domain names the app it serves
231121
+ // (`shared_domain` is that address), so the list is wanted from the start.
231122
+ const { data: projectData, isFetching: isLoadingProjects } = useGetHostingProjectsQuery({ org: platformKey, username }, { skip: !username });
231123
+ // The caller's app served at a shared-domain address, by that address. A
231124
+ // row without a match is an own domain (or another user's app): it keeps
231125
+ // its records.
231126
+ const sharedAppFor = (row) => {
231127
+ var _a;
231128
+ return row.spa === VERCEL_SPA
231129
+ ? ((_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : []).find((project) => !!project.shared_domain &&
231130
+ project.shared_domain.toLowerCase() === row.custom_domain.toLowerCase())
231131
+ : undefined;
231132
+ };
230682
231133
  const [createCustomDomain, { isLoading: isCreating }] = useCreateCustomDomainMutation();
230683
231134
  const [deleteCustomDomain] = useDeleteCustomDomainMutation();
230684
231135
  const [createHostingProjectDomain, { isLoading: isAttaching }] = useCreateHostingProjectDomainMutation();
@@ -230707,10 +231158,15 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
230707
231158
  : typeof window !== 'undefined'
230708
231159
  ? window.location.hostname.split('.').slice(-2).join('.')
230709
231160
  : 'iblai.app'}`;
230710
- // A platform gets one sign-in domain per SPA, so the Add dialog offers that
230711
- // kind only while this SPA has none. Deployed apps have no such limit.
230712
- const canAddPlatformDomain = useMemo(() => Boolean(currentSPA) &&
230713
- !customDomains.some((domain) => { var _a; return domain.spa !== VERCEL_SPA && ((_a = domain.spa) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(currentSPA.toLowerCase())); }), [customDomains, currentSPA]);
231161
+ // The Add dialog offers the sign-in kind whenever it knows which app to bind
231162
+ // the domain to. How many sign-in domains an app may have is the platform's
231163
+ // decision, not the panel's: a refusal comes back as the platform's own
231164
+ // message (`reportError` shows it), so no count is enforced here.
231165
+ const canAddPlatformDomain = Boolean(currentSPA);
231166
+ // The kind the dialog opens on: an app that already has a sign-in domain is
231167
+ // more often adding one for a deployed app; the sign-in kind stays one
231168
+ // click away either way.
231169
+ const hasPlatformDomain = useMemo(() => customDomains.some((domain) => { var _a; return domain.spa !== VERCEL_SPA && ((_a = domain.spa) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(currentSPA.toLowerCase())); }), [customDomains, currentSPA]);
230714
231170
  /**
230715
231171
  * The status decides what the user should do next, so it gets its own
230716
231172
  * message. 409 shares 400's: both mean "not this domain, not here", and the
@@ -230735,10 +231191,10 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
230735
231191
  };
230736
231192
  const reportError = (error, fallback) => {
230737
231193
  const status = error === null || error === void 0 ? void 0 : error.status;
230738
- // The server's own words go underneath the headline — except on 409, the
230739
- // one status whose body names the organization holding the domain.
230740
- const description = status === 409 ? undefined : serverDetail(error);
230741
- toast.error(headlineForStatus(status, fallback), { description });
231194
+ // The server's own words go underneath the headline. A 409 body never
231195
+ // names the organization holding the domain — the backend keeps that out
231196
+ // on purpose — and for a taken subdomain it carries the way out.
231197
+ toast.error(headlineForStatus(status, fallback), { description: serverDetail(error) });
230742
231198
  };
230743
231199
  const handleAddPlatformDomain = async (domain) => {
230744
231200
  if (!domain) {
@@ -230762,15 +231218,16 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
230762
231218
  reportError(error, t('advancedCustomDomain.createError'));
230763
231219
  }
230764
231220
  };
230765
- const handleAddAppDomain = async (project, domain) => {
231221
+ const handleAddAppDomain = async (project, address) => {
230766
231222
  try {
230767
- // An empty domain is the "assign me a subdomain" path, which the backend
230768
- // reads as the absent key — not as an empty string.
231223
+ // Only the address that was filled in is sent; the dialog guarantees
231224
+ // exactly one. The backend reads an absent key, never an empty string.
230769
231225
  await createHostingProjectDomain({
230770
231226
  org: platformKey,
230771
231227
  username,
230772
231228
  project,
230773
- domain: domain || undefined,
231229
+ domain: address.domain || undefined,
231230
+ subdomain: address.subdomain || undefined,
230774
231231
  }).unwrap();
230775
231232
  await refetch();
230776
231233
  toast.success(t('advancedCustomDomain.createSuccess'));
@@ -230835,7 +231292,7 @@ const CustomDomainsContent = ({ platformKey, username, currentSPA, currentPlatfo
230835
231292
  setIsModalOpen(true);
230836
231293
  }, children: [jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }), t('advancedCustomDomain.add')] }), jsx("button", { onClick: () => setIsCollapsed((previous) => !previous), className: "rounded p-1 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 focus:ring-offset-1 focus:outline-none dark:hover:bg-gray-800", "aria-label": isCollapsed
230837
231294
  ? t('advancedCustomDomain.expandAriaLabel')
230838
- : t('advancedCustomDomain.collapseAriaLabel'), type: "button", children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: isLoading ? (jsx("div", { className: "p-4 text-gray-500", children: t('advancedCustomDomain.loading') })) : customDomains.length > 0 ? (jsx("div", { className: "space-y-3", children: sortedDomains.map((row) => (jsx(DomainRow, { row: row, sslValue: sslValue, onReverify: handleReverify, onRemove: handleRemove, isBusy: busyDomainId === row.id, onStatusChange: handleStatusChange }, row.id))) })) : (jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: t('advancedCustomDomain.noDomainConfigured') })) })), jsx(AddDomainDialog, { open: isModalOpen, onOpenChange: setIsModalOpen, canAddPlatformDomain: canAddPlatformDomain, projects: (_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : [], isLoadingProjects: isLoadingProjects, isSubmitting: isCreating || isAttaching, onSubmitPlatformDomain: handleAddPlatformDomain, onSubmitAppDomain: handleAddAppDomain })] }));
231295
+ : t('advancedCustomDomain.collapseAriaLabel'), type: "button", children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: isLoading ? (jsx("div", { className: "p-4 text-gray-500", children: t('advancedCustomDomain.loading') })) : customDomains.length > 0 ? (jsx("div", { className: "space-y-3", children: sortedDomains.map((row) => (jsx(DomainRow, { row: row, sharedApp: sharedAppFor(row), sslValue: sslValue, onReverify: handleReverify, onRemove: handleRemove, isBusy: busyDomainId === row.id, onStatusChange: handleStatusChange }, row.id))) })) : (jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: t('advancedCustomDomain.noDomainConfigured') })) })), jsx(AddDomainDialog, { open: isModalOpen, onOpenChange: setIsModalOpen, canAddPlatformDomain: canAddPlatformDomain, defaultMode: hasPlatformDomain ? 'app' : 'platform', projects: (_a = projectData === null || projectData === void 0 ? void 0 : projectData.projects) !== null && _a !== void 0 ? _a : [], isLoadingProjects: isLoadingProjects, isSubmitting: isCreating || isAttaching, onSubmitPlatformDomain: handleAddPlatformDomain, onSubmitAppDomain: handleAddAppDomain })] }));
230839
231296
  };
230840
231297
 
230841
231298
  const StudentMentorCreationContent = ({ platformKey, }) => {
@@ -275358,7 +275815,7 @@ function LocalModelRow({ name, size, logo, providerLabel, status, progress, disa
275358
275815
  style: { width: `${status === 'starting' ? 6 : Math.max(pct, 6)}%` } })), jsx("span", { "aria-hidden": "true", className: "relative z-10 flex h-8 w-8 flex-shrink-0 items-center justify-center overflow-hidden rounded-lg", children: jsx(LLMProviderLogo, { logo: logo, label: providerLabel !== null && providerLabel !== void 0 ? providerLabel : name, alt: "", className: cn({ grayscale: isBusyDisabled }) }) }), jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 flex-col gap-1.5", children: [jsx("span", { className: "truncate text-sm font-medium text-[#646464] lowercase", children: name }), jsxs("span", { className: "flex min-w-0 items-center gap-1.5 text-sm text-gray-500", children: [jsx("span", { className: "flex-shrink-0 rounded bg-gray-100 px-2 py-0.5 text-sm font-medium text-gray-500", children: labels.onDevice }), status === 'not-installed' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: size }), jsx(Download, { className: "h-4 w-4", "aria-hidden": "true" })] })), status === 'starting' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { children: labels.starting })] })), status === 'downloading' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsxs("span", { className: "tabular-nums group-hover:hidden group-focus-visible:hidden", children: [pct, "%"] }), jsxs("span", { className: "hidden items-center gap-1 text-blue-600 group-hover:flex group-focus-visible:flex", children: [jsx(X$2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), labels.cancel] })] })), status === 'selected' && (jsxs("span", { className: "flex items-center gap-1 text-blue-600", children: [jsx(Check, { className: "h-4 w-4", "aria-hidden": "true" }), labels.inUse] })), status === 'error' && (jsxs(Fragment$1, { children: [jsx("span", { "aria-hidden": "true", children: "\u00B7" }), jsx("span", { className: "truncate text-red-500", children: errorMessage || labels.downloadFailedRetry }), jsx(RotateCcw, { className: "h-4 w-4 flex-shrink-0 text-red-500", "aria-hidden": "true" })] }))] })] })] }));
275359
275816
  }
275360
275817
 
275361
- function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, labels: labelsProp, }) {
275818
+ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, labels: labelsProp, cloudOnly = false, }) {
275362
275819
  var _a;
275363
275820
  const t = useT();
275364
275821
  const labels = React__default.useMemo(() => labelsProp !== null && labelsProp !== void 0 ? labelsProp : buildLLMTabLabels(t).providerModal, [labelsProp, t]);
@@ -275426,13 +275883,13 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
275426
275883
  }, [searchQuery, llmProvider]);
275427
275884
  // Local models belonging to THIS provider (merged into the same list).
275428
275885
  const localModels = React__default.useMemo(() => {
275429
- if (!isAvailable)
275886
+ if (cloudOnly || !isAvailable)
275430
275887
  return [];
275431
275888
  const key = getProviderName(llmProvider.name);
275432
275889
  // Per-device catalog: Tauri iOS lists only models with phone-sized weights.
275433
275890
  return localModelsForThisDevice().filter((m) => getProviderName(m.provider) === key &&
275434
275891
  m.name.toLowerCase().includes(searchQuery.toLowerCase()));
275435
- }, [isAvailable, llmProvider.name, searchQuery]);
275892
+ }, [cloudOnly, isAvailable, llmProvider.name, searchQuery]);
275436
275893
  const switchLLMAllowed = canSwitchLLm(llmProvider);
275437
275894
  const switchProviderAllowed = canSwitchProvider(llms, llmProvider.name);
275438
275895
  // Only an actual pull counts as "busy". A status refresh ('checking') must NOT
@@ -275458,7 +275915,7 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
275458
275915
  // is off.
275459
275916
  const isCloudActive = (llm) => (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name) === llm.llm_name &&
275460
275917
  (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider) === llmProvider.name &&
275461
- !localSel.enabled;
275918
+ (cloudOnly || !localSel.enabled);
275462
275919
  // Order the WHOLE list — cloud + local — available (usable now) first,
275463
275920
  // unavailable last, with the in-use model leading. "Available" for a cloud
275464
275921
  // model means the provider is switchable (has credentials + models); for a
@@ -275527,7 +275984,7 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
275527
275984
  };
275528
275985
  const selectCloud = (llmName) => {
275529
275986
  // Picking a cloud model turns local mode off so routing uses the cloud LLM.
275530
- if (localSel.enabled) {
275987
+ if (!cloudOnly && localSel.enabled) {
275531
275988
  setLocalLLMEnabled(false);
275532
275989
  setLocalSel((prev) => ({ ...prev, enabled: false }));
275533
275990
  window.dispatchEvent(new Event(LOCAL_LLM_CHANGED_EVENT));
@@ -281164,6 +281621,12 @@ const AGENT_HISTORY_TAB_LABELS = {
281164
281621
  aiAgent: 'AI Agent',
281165
281622
  noConversations: 'No conversations found',
281166
281623
  documentsChip: (count) => `Documents · ${count}`,
281624
+ sentiment: {
281625
+ positive: 'Positive',
281626
+ neutral: 'Neutral',
281627
+ negative: 'Negative',
281628
+ userSentiment: 'User Sentiment',
281629
+ },
281167
281630
  },
281168
281631
  summary: {
281169
281632
  notAvailable: 'Summary not available',
@@ -281203,6 +281666,12 @@ function buildHistoryTabLabels(t) {
281203
281666
  aiAgent: t('historyTabLabels.conversation.aiAgent'),
281204
281667
  noConversations: t('historyTabLabels.conversation.noConversations'),
281205
281668
  documentsChip: (count) => t('historyTabLabels.conversation.documentsChip', { count }),
281669
+ sentiment: {
281670
+ positive: t('historyTabLabels.conversation.sentiment.positive'),
281671
+ neutral: t('historyTabLabels.conversation.sentiment.neutral'),
281672
+ negative: t('historyTabLabels.conversation.sentiment.negative'),
281673
+ userSentiment: t('historyTabLabels.conversation.sentiment.userSentiment'),
281674
+ },
281206
281675
  },
281207
281676
  summary: {
281208
281677
  notAvailable: t('historyTabLabels.summary.notAvailable'),
@@ -281237,7 +281706,7 @@ function textTruncate$1(str, length, ending) {
281237
281706
  return str;
281238
281707
  }
281239
281708
  function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport, isExporting = false, labels: labelsOverride, currentSPA, }) {
281240
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
281709
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
281241
281710
  const t = useT();
281242
281711
  const { tenantKey, mentorId, username } = useAgentSettings();
281243
281712
  const labels = React__default.useMemo(() => resolveHistoryTabLabels(labelsOverride, t), [labelsOverride, t]);
@@ -281325,12 +281794,10 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
281325
281794
  const owner = resolveUserIdentity(conversation, {
281326
281795
  anonymousLabel: labels.conversation.anonymous,
281327
281796
  });
281328
- const timeAgo = formatDistanceToNow(new Date(conversation.inserted_at), {
281329
- addSuffix: true,
281330
- });
281331
- const title = (firstMessage === null || firstMessage === void 0 ? void 0 : firstMessage.human)
281332
- ? textTruncate$1(firstMessage.human, 50)
281333
- : labels.conversation.title;
281797
+ // The same date pattern as the profile History tab.
281798
+ const when = formatSessionDate(conversation.inserted_at);
281799
+ // title → first_user_message → first human turn → label, as everywhere.
281800
+ const title = textTruncate$1(conversationTitle(conversation, labels.conversation.title), 50);
281334
281801
  const preview = (firstMessage === null || firstMessage === void 0 ? void 0 : firstMessage.ai)
281335
281802
  ? textTruncate$1(firstMessage.ai, 60)
281336
281803
  : labels.conversation.noResponse;
@@ -281342,14 +281809,14 @@ function AgentHistoryTab({ renderMessageContent, PaginationComponent, onExport,
281342
281809
  const rollup = summarizeTranscriptTurns(messages);
281343
281810
  return (jsxs("div", { className: cn('flex cursor-pointer items-center justify-between border-b p-4 last:border-b-0', (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.id) === conversation.id
281344
281811
  ? 'bg-gray-100'
281345
- : 'hover:bg-gray-50'), onClick: () => handleConversationClick(conversation), children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("span", { className: "text-sm text-gray-600", children: timeAgo }), jsx(UserProfileLink, { tenantKey: tenantKey, username: owner.profileUsername, display: owner.label, currentSPA: currentSPA, className: "max-w-[100px] text-sm text-gray-900 sm:max-w-[200px]" })] }), jsx("div", { className: "font-medium text-gray-900", children: title }), jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: preview }), (rollup.documentsCount > 0 || rollup.toolCallsCount > 0) && (jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [rollup.documentsCount > 0 && (
281812
+ : 'hover:bg-gray-50'), onClick: () => handleConversationClick(conversation), children: [jsxs("div", { className: "w-full", children: [jsxs("div", { className: "mb-2 flex items-center justify-between", children: [jsx("span", { className: "text-sm text-gray-600", children: when }), jsx(UserProfileLink, { tenantKey: tenantKey, username: owner.profileUsername, display: owner.label, currentSPA: currentSPA, className: "max-w-[100px] text-sm text-gray-900 sm:max-w-[200px]" })] }), jsx("div", { className: "font-medium text-gray-900", children: title }), jsx(ConversationTopics, { topics: conversation.topics, className: "mt-1", leading: jsx(ConversationSentiment, { sentiment: conversation.sentiment, labels: labels.conversation.sentiment, compact: true }) }), jsx("p", { className: "line-clamp-1 text-sm text-gray-600", children: preview }), (rollup.documentsCount > 0 || rollup.toolCallsCount > 0) && (jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [rollup.documentsCount > 0 && (
281346
281813
  // The chat's documents dialog, as a chip: one
281347
281814
  // click shows what the conversation retrieved.
281348
281815
  jsx(RetrievedDocumentsButton, { documents: documents, label: labels.conversation.documentsChip(rollup.documentsCount), className: "h-6 gap-1 rounded-md border-transparent bg-blue-50 px-2 text-xs font-normal text-blue-700 hover:bg-blue-100 [&_svg]:h-3 [&_svg]:w-3" })), jsx(TranscriptRollupBadges, { documentsCount: 0, toolCallsCount: rollup.toolCallsCount })] }))] }), jsx("div", { className: "flex items-center gap-2 text-sm text-gray-600", children: jsx(ChevronRight, { className: "h-4 w-4" }) })] }, conversation.id));
281349
- }) }), totalPages > 1 && PaginationComponent && (jsx("div", { className: "my-2", children: jsx(PaginationComponent, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isChatHistoryFetching || isChatHistoryLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden max-h-[500px] flex-col overflow-y-auto rounded-lg border bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('tabsHistoryTab.conversationPreviewLabel'), children: selectedConversation && previewConversationContent ? (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: ((_m = selectedConversation.messages[0]) === null || _m === void 0 ? void 0 : _m.human) || labels.conversation.title }), jsx("span", { className: "text-sm text-gray-500", children: format$1(new Date(selectedConversation.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: selectedConversation.id, documents: conversationDocuments(selectedConversation.messages).length > 0
281816
+ }) }), totalPages > 1 && PaginationComponent && (jsx("div", { className: "my-2", children: jsx(PaginationComponent, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isChatHistoryFetching || isChatHistoryLoading, disableNumberedButtons: isMobile }) }))] }), jsx("div", { className: "scrollbar-hide hidden max-h-[500px] flex-col overflow-y-auto rounded-lg border bg-white p-6 shadow-sm md:flex", tabIndex: 0, role: "region", "aria-label": t('tabsHistoryTab.conversationPreviewLabel'), children: selectedConversation && previewConversationContent ? (jsxs(Fragment$1, { children: [jsxs("div", { className: "mb-4", children: [jsx("h3", { className: "mb-1 text-base font-semibold text-gray-700", children: conversationTitle(selectedConversation, labels.conversation.title) }), jsx("span", { className: "text-sm text-gray-500", children: format$1(new Date(selectedConversation.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: selectedConversation.id, documents: conversationDocuments(selectedConversation.messages).length > 0
281350
281817
  ? conversationDocuments(selectedConversation.messages)
281351
- : undefined, className: "mt-2" })] }), jsxs("div", { className: "space-y-6", children: [selectedConversation.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: selectedOwner.initial }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: selectedOwner.profileUsername, display: selectedOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-500", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-500", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message, tenantKey: tenantKey, mentorId: mentorId })] })] })] }, index))), (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.memory_tracked) && (jsxs("div", { className: "border-t border-gray-200 pt-4", children: [jsxs(Button$1, { variant: "outline", onClick: () => setShowConversationMemory(!showConversationMemory), className: "flex w-full items-center justify-center gap-2 border-gray-200 text-gray-700 hover:bg-gray-50", children: [jsx(Brain, { className: "h-4 w-4" }), showConversationMemory ? labels.memory.hide : labels.memory.show] }), showConversationMemory && (jsx("div", { className: "mt-4 space-y-6", children: isConversationMemoryLoading ? (jsx("div", { className: "flex items-center justify-center py-4", children: jsx(Spinner, {}) })) : ((_o = conversationMemory === null || conversationMemory === void 0 ? void 0 : conversationMemory.entries) === null || _o === void 0 ? void 0 : _o.length) ? (jsx(Fragment$1, { children: conversationMemory.entries.map((entry, index) => (jsxs("div", { children: [jsx("h4", { className: "mb-3 font-semibold text-gray-900", children: entry.key }), jsx("ul", { className: "space-y-2", children: jsx("li", { className: "flex items-start gap-2 text-sm text-gray-600", children: entry.value }) })] }, index))) })) : (jsx("div", { className: "text-sm text-gray-500", children: labels.memory.notAvailable })) }))] }))] })] })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: labels.conversation.selectPrompt })) })] }))] }) }), jsx(Dialog, { open: isConversationPreviewModalOpen, onOpenChange: setIsConversationPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: previewConversationContent
281352
- ? ((_p = previewConversationContent.messages[0]) === null || _p === void 0 ? void 0 : _p.human) || labels.conversation.title
281818
+ : undefined, className: "mt-2" })] }), jsxs("div", { className: "space-y-6", children: [selectedConversation.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: selectedOwner.initial }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: selectedOwner.profileUsername, display: selectedOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-500", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-medium text-gray-700", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-500", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message, tenantKey: tenantKey, mentorId: mentorId })] })] })] }, index))), (selectedConversation === null || selectedConversation === void 0 ? void 0 : selectedConversation.memory_tracked) && (jsxs("div", { className: "border-t border-gray-200 pt-4", children: [jsxs(Button$1, { variant: "outline", onClick: () => setShowConversationMemory(!showConversationMemory), className: "flex w-full items-center justify-center gap-2 border-gray-200 text-gray-700 hover:bg-gray-50", children: [jsx(Brain, { className: "h-4 w-4" }), showConversationMemory ? labels.memory.hide : labels.memory.show] }), showConversationMemory && (jsx("div", { className: "mt-4 space-y-6", children: isConversationMemoryLoading ? (jsx("div", { className: "flex items-center justify-center py-4", children: jsx(Spinner, {}) })) : ((_m = conversationMemory === null || conversationMemory === void 0 ? void 0 : conversationMemory.entries) === null || _m === void 0 ? void 0 : _m.length) ? (jsx(Fragment$1, { children: conversationMemory.entries.map((entry, index) => (jsxs("div", { children: [jsx("h4", { className: "mb-3 font-semibold text-gray-900", children: entry.key }), jsx("ul", { className: "space-y-2", children: jsx("li", { className: "flex items-start gap-2 text-sm text-gray-600", children: entry.value }) })] }, index))) })) : (jsx("div", { className: "text-sm text-gray-500", children: labels.memory.notAvailable })) }))] }))] })] })) : (jsx("div", { className: "flex h-full items-center justify-center text-gray-500", children: labels.conversation.selectPrompt })) })] }))] }) }), jsx(Dialog, { open: isConversationPreviewModalOpen, onOpenChange: setIsConversationPreviewModalOpen, children: jsxs(DialogContent, { className: "scrollbar-hide mx-auto max-h-[80vh] max-w-[90vw] overflow-x-hidden overflow-y-auto", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: previewConversationContent
281819
+ ? conversationTitle(previewConversationContent, labels.conversation.title)
281353
281820
  : labels.conversation.title }) }), jsx("div", { className: "mx-auto max-w-full overflow-x-hidden", children: previewConversationContent && (jsxs("div", { className: "scrollbar-hide space-y-4 overflow-y-auto", children: [jsx("div", { className: "text-sm text-gray-500", children: format$1(new Date(previewConversationContent.inserted_at), "MMM dd, yyyy 'at' h:mm a") }), jsx(RetrievedDocumentsButton, { sessionId: previewConversationContent.id, documents: conversationDocuments(previewConversationContent.messages).length > 0
281354
281821
  ? conversationDocuments(previewConversationContent.messages)
281355
281822
  : undefined }), previewConversationContent.messages.map((message, index) => (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-100", children: jsx("span", { className: "text-sm font-medium text-blue-600", children: previewOwner.initial }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "truncate font-medium text-gray-900", children: jsx(UserProfileLink, { tenantKey: tenantKey, username: previewOwner.profileUsername, display: previewOwner.label, currentSPA: currentSPA }) }), jsx("p", { className: "mt-1 text-sm whitespace-pre-line text-gray-900", children: message.human }), jsx(TranscriptFileCards, { files: attachmentCards(message.human_files) })] })] }), jsxs("div", { className: "flex items-start gap-3 border-t border-gray-200 pt-4", children: [jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-100", children: jsx("span", { className: "text-sm font-medium text-gray-600", children: t('tabsHistoryTab.aiAvatarLabel') }) }), jsxs("div", { className: "overflow-x-hidden", children: [jsx("div", { className: "font-medium text-gray-900", children: labels.conversation.aiAgent }), jsx("div", { className: "mt-1 text-sm text-gray-900", children: renderAiContent(message.ai) }), jsx(TranscriptFileCards, { files: attachmentCards(message.ai_files) }), jsx(TranscriptTurnDetails, { turn: message, tenantKey: tenantKey, mentorId: mentorId })] })] })] }, index)))] })) })] }) })] }));