diff --git a/.gitattributes b/.gitattributes index b175f24b..848f77f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,9 @@ # Keep LF line endings in webroot assets files. Otherwise, building them under Windows would change the line endings to CLRF and cause changes without actually editing the source files. **/wwwroot/**/*.js text eol=lf **/wwwroot/**/*.css text eol=lf +**/wwwroot/**/*.map text eol=lf + +# Keep LF line endings in source asset files so that generated source maps embed +# LF content regardless of the platform and CI builds stay deterministic. +**/Assets/**/*.js text eol=lf +**/Assets/**/*.css text eol=lf diff --git a/.github/workflows/publish_npm_preview.yml b/.github/workflows/publish_npm_preview.yml index b61d7bca..f4f0bdb6 100644 --- a/.github/workflows/publish_npm_preview.yml +++ b/.github/workflows/publish_npm_preview.yml @@ -78,6 +78,6 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | - PREVIEW_VERSION="$(node -p "require('./package.json').version")-preview.${{ github.run_number }}" + PREVIEW_VERSION="$(node -p "require('./package.json').version").${{ github.run_number }}" npm version "${PREVIEW_VERSION}" --no-git-tag-version --allow-same-version npm publish --access public --tag preview diff --git a/gulpfile.js b/gulpfile.js index f87d594b..401c7788 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -215,9 +215,6 @@ function buildCssPipeline(assetGroup, doConcat, doRebuild) { var ext = path.extname(inputPath).toLowerCase(); return ext === ".less" || ext === ".scss"; }); - // Source maps are useless if neither concatenating nor transforming. - if ((!doConcat || assetGroup.inputPaths.length < 2) && !containsLessOrScss) - generateSourceMaps = false; var minifiedStream = gulp.src(assetGroup.inputPaths) // Minified output, source mapping completely disabled. .pipe(gulpif(!doRebuild, @@ -271,7 +268,7 @@ function buildCssPipeline(assetGroup, doConcat, doRebuild) { }))) .pipe(gulpif(doConcat, concat(assetGroup.outputFileName))) .pipe(gulpif(generateRTL, postcss([rtl()]))) - .pipe(gulpif(generateSourceMaps, sourcemaps.write())) + .pipe(gulpif(generateSourceMaps, sourcemaps.write('.'))) .pipe(eol('\n')) .pipe(gulp.dest(assetGroup.outputDir)); // Uncomment to copy assets to wwwroot @@ -286,9 +283,6 @@ function buildJsPipeline(assetGroup, doConcat, doRebuild) { throw "Input file '" + inputPath + "' is not of a valid type for output file '" + assetGroup.outputPath + "'."; }); var generateSourceMaps = assetGroup.hasOwnProperty("generateSourceMaps") ? assetGroup.generateSourceMaps : false; - // Source maps are useless if neither concatenating nor transforming. - if ((!doConcat || assetGroup.inputPaths.length < 2) && !assetGroup.inputPaths.some(function (inputPath) { return path.extname(inputPath).toLowerCase() === ".ts"; })) - generateSourceMaps = false; var tsCompilerOptions = assetGroup.hasOwnProperty("tsCompilerOptions") ? assetGroup.tsCompilerOptions : { declaration: false, @@ -329,16 +323,17 @@ function buildJsPipeline(assetGroup, doConcat, doRebuild) { } var devStream = createJsStream(generateSourceMaps) - .pipe(gulpif(generateSourceMaps, sourcemaps.write())) + .pipe(gulpif(generateSourceMaps, sourcemaps.write('.'))) .pipe(gulp.dest(assetGroup.outputDir)); // Uncomment to copy assets to wwwroot //.pipe(gulp.dest(assetGroup.webroot)); - var minifiedStream = createJsStream(false) + var minifiedStream = createJsStream(generateSourceMaps) .pipe(terser()) .pipe(rename({ suffix: ".min" })) + .pipe(gulpif(generateSourceMaps, sourcemaps.write('.'))) .pipe(eol('\n')) .pipe(gulp.dest(assetGroup.outputDir)); // Uncomment to copy assets to wwwroot diff --git a/package-lock.json b/package-lock.json index d17595e2..3f63d360 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2493,9 +2493,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5661,9 +5661,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -7828,9 +7828,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, "license": "ISC", "bin": { diff --git a/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/ChatModeProfileSettings.cs b/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/ChatModeProfileSettings.cs index 6b92de7a..cf20090a 100644 --- a/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/ChatModeProfileSettings.cs +++ b/src/Abstractions/CrestApps.Core.AI.Abstractions/Models/ChatModeProfileSettings.cs @@ -18,4 +18,12 @@ public class ChatModeProfileSettings /// When null or empty, the provider's default voice is used. /// public string VoiceName { get; set; } + + /// + /// Gets or sets whether to show text-to-speech playback controls on + /// assistant messages. Disabled by default. When enabled the UI displays + /// a play button on each assistant message, allowing the user to listen + /// to the response via the configured TTS deployment. + /// + public bool EnableTextToSpeechPlayback { get; set; } } diff --git a/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md b/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md index 97262e73..2a3ef0e4 100644 --- a/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md +++ b/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md @@ -28,3 +28,8 @@ description: Initial standalone release notes for the CrestApps.Core repository. - clarifies deployment-store registration by introducing `IAIDeploymentStore` for persisted deployments, moves Chat Interactions ahead of AI Profiles / AI Chat in the MVC sample onboarding flow, and adds dedicated AI Profile documentation that explains how profiles power reusable chat, agents, orchestration, retrieval, and session processing - centralizes reusable MCP runtime registration in `AddCoreAIMcpServices()`, moves the shared MCP metadata, capability-resolution, tool-registry, SSE settings-handler, and invoke-function services into `CrestApps.Core.AI.Mcp`, and splits optional StdIO transport registration so hosts can enable it only where needed - treats aborted and canceled request-stream failures in the Aspire AppHost as observed task exceptions so local development no longer floods the console with benign unobserved-task noise +- generates external `.map` source map files for all JS and CSS assets in the gulp build pipeline, copies them into `dist/` during npm package preparation, and includes them in the `@crestapps/ai-chat-ui` package exports +- adds per-message text-to-speech play/pause button on assistant messages in the AI Chat and Chat Interaction UIs when a TTS deployment is configured, allowing users to hear any message read aloud regardless of whether the profile uses Conversation mode +- adds Chat History page listing previous sessions per AI Profile sorted by creation date, with resume, delete, delete-all, and new-chat actions +- adds Test page for Utility and Agent AI Profiles providing a single-prompt/single-response streamed UI +- renames the "Chat" button to "New Chat" on the AI Profile list and adds "Chat History" and "Test" buttons for applicable profile types diff --git a/src/CrestApps.Core.Docs/docs/core/chat.md b/src/CrestApps.Core.Docs/docs/core/chat.md index 57c7d0cd..d07aa6a3 100644 --- a/src/CrestApps.Core.Docs/docs/core/chat.md +++ b/src/CrestApps.Core.Docs/docs/core/chat.md @@ -743,6 +743,8 @@ Pin a specific version in production by appending `@` after the package | `chat-widget.css` / `.min.css` | Styles for the floating AI Chat widget | | `document-drop-zone.css` / `.min.css` | Styles for the document drop-zone component | +Each JavaScript and CSS file also ships with a companion `.map` source map file (e.g. `ai-chat.js.map`, `chat-widget.css.map`). Source maps are generated for both the dev and minified builds and are included in the `dist/` folder and npm package exports. + ### Required Client-Side Dependencies Both widgets require the following libraries to be loaded on the page **before** the widget script: @@ -761,3 +763,19 @@ Both widgets require the following libraries to be loaded on the page **before** | [Chart.js](https://www.chartjs.org/) | Interactive chart rendering | `https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js` | | [highlight.js](https://highlightjs.org/) | Code syntax highlighting | `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js` | +### Text-to-Speech Play Icon + +When a text-to-speech (TTS) deployment is configured, each assistant message shows a play icon that reads the message aloud via server-side speech synthesis. Clicking the icon calls `SynthesizeSpeech` on the SignalR hub, which streams audio chunks back to the client and plays them as MP3. Clicking the icon again pauses playback. + +To enable this feature, pass `textToSpeechEnabled: true` in the widget initialization config: + +```js +window.openAIChatManager.initialize({ + // ... other config ... + textToSpeechEnabled: true, + ttsVoiceName: 'alloy' // optional voice name override +}); +``` + +The play icon appears on all completed assistant messages regardless of whether the profile uses Conversation mode. The only requirement is that a TTS deployment is available on the server. + diff --git a/src/Primitives/CrestApps.Core.AI.Chat/Hubs/AIChatHubCore.cs b/src/Primitives/CrestApps.Core.AI.Chat/Hubs/AIChatHubCore.cs index 2df154fd..b9e6e580 100644 --- a/src/Primitives/CrestApps.Core.AI.Chat/Hubs/AIChatHubCore.cs +++ b/src/Primitives/CrestApps.Core.AI.Chat/Hubs/AIChatHubCore.cs @@ -745,11 +745,7 @@ await ExecuteInScopeAsync(async services => return; } - if (!profile.TryGetSettings(out var chatModeSettings) || chatModeSettings.ChatMode != ChatMode.Conversation) - { - await Clients.Caller.ReceiveError(GetTtsNotEnabledMessage()); - return; - } + profile.TryGetSettings(out var chatModeSettings); var deploymentSettings = await GetDeploymentSettingsAsync(services); if (string.IsNullOrEmpty(deploymentSettings.DefaultTextToSpeechDeploymentName)) @@ -766,7 +762,7 @@ await ExecuteInScopeAsync(async services => } using var textToSpeechClient = await clientFactory.CreateTextToSpeechClientAsync(deployment); - var effectiveVoiceName = !string.IsNullOrWhiteSpace(voiceName) ? voiceName : !string.IsNullOrWhiteSpace(chatModeSettings.VoiceName) ? chatModeSettings.VoiceName : deploymentSettings.DefaultTextToSpeechVoiceId; + var effectiveVoiceName = !string.IsNullOrWhiteSpace(voiceName) ? voiceName : !string.IsNullOrWhiteSpace(chatModeSettings?.VoiceName) ? chatModeSettings.VoiceName : deploymentSettings.DefaultTextToSpeechVoiceId; await StreamSpeechAsync(textToSpeechClient, sessionId ?? string.Empty, text, effectiveVoiceName, cancellationToken); }); } diff --git a/src/Resources/CrestApps.AI.Resources/Assets.json b/src/Resources/CrestApps.AI.Resources/Assets.json index 36acd744..08be2d34 100644 --- a/src/Resources/CrestApps.AI.Resources/Assets.json +++ b/src/Resources/CrestApps.AI.Resources/Assets.json @@ -3,36 +3,42 @@ "inputs": [ "Assets/js/ai-chat.js" ], - "output": "wwwroot/scripts/ai-chat.js" + "output": "wwwroot/scripts/ai-chat.js", + "generateSourceMaps": true }, { "inputs": [ "Assets/js/chat-interaction.js" ], - "output": "wwwroot/scripts/chat-interaction.js" + "output": "wwwroot/scripts/chat-interaction.js", + "generateSourceMaps": true }, { "inputs": [ "Assets/js/document-drop-zone.js" ], - "output": "wwwroot/scripts/document-drop-zone.js" + "output": "wwwroot/scripts/document-drop-zone.js", + "generateSourceMaps": true }, { "inputs": [ "Assets/js/technical-name-generator.js" ], - "output": "wwwroot/scripts/technical-name-generator.js" + "output": "wwwroot/scripts/technical-name-generator.js", + "generateSourceMaps": true }, { "inputs": [ "Assets/css/chat-widget.css" ], - "output": "wwwroot/styles/chat-widget.css" + "output": "wwwroot/styles/chat-widget.css", + "generateSourceMaps": true }, { "inputs": [ "Assets/css/document-drop-zone.css" ], - "output": "wwwroot/styles/document-drop-zone.css" + "output": "wwwroot/styles/document-drop-zone.css", + "generateSourceMaps": true } ] diff --git a/src/Resources/CrestApps.AI.Resources/Assets/css/chat-widget.css b/src/Resources/CrestApps.AI.Resources/Assets/css/chat-widget.css index ab506e2d..0d70246b 100644 --- a/src/Resources/CrestApps.AI.Resources/Assets/css/chat-widget.css +++ b/src/Resources/CrestApps.AI.Resources/Assets/css/chat-widget.css @@ -140,7 +140,7 @@ .ai-chat-widget-messages .ai-chat-message-body { position: relative; - padding: 0 0 1.75rem 0; + padding: 0 0 0.25rem 0; overflow-wrap: break-word; word-break: break-word; min-width: 0; @@ -154,16 +154,17 @@ .ai-chat-widget-messages .message-buttons-container { position: absolute; right: 0; - bottom: 0.25rem; + top: 0; display: inline-flex; gap: 0.35rem; opacity: 0; pointer-events: none; transition: opacity 0.15s ease-in-out; - background: rgba(255, 255, 255, 0.92); + background: #fff; padding: 0.15rem 0.35rem; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + z-index: 1; } .ai-chat-widget-messages .ai-chat-message-item:hover .message-buttons-container, @@ -182,6 +183,12 @@ color: var(--bs-body-color, #212529) !important; } +.ai-chat-widget-messages .message-buttons-container > .button-message-toolbox.tts-playing { + color: var(--bs-primary, #0d6efd) !important; + opacity: 1; + pointer-events: auto; +} + /* ── Notifications ── */ .ai-chat-widget-messages .ai-chat-notification { margin: 0.75rem 0.5rem 0; diff --git a/src/Resources/CrestApps.AI.Resources/Assets/js/ai-chat.js b/src/Resources/CrestApps.AI.Resources/Assets/js/ai-chat.js index 61227fba..d47c96c4 100644 --- a/src/Resources/CrestApps.AI.Resources/Assets/js/ai-chat.js +++ b/src/Resources/CrestApps.AI.Resources/Assets/js/ai-chat.js @@ -37,6 +37,9 @@ window.openAIChatManager = function () { + @@ -444,13 +447,16 @@ window.openAIChatManager = function () { preRecordingPrompt: '', micButton: null, speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation', - textToSpeechEnabled: config.chatMode === 'Conversation', + textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled, ttsVoiceName: config.ttsVoiceName || null, audioChunks: [], audioPlayQueue: [], isPlayingAudio: false, currentAudioElement: null, ttsButton: null, + ttsPlayingMessageIndex: -1, + ttsAudioCache: {}, + singleResponseMode: !!config.singleResponseMode, conversationModeEnabled: config.chatMode === 'Conversation', conversationButton: null, isConversationMode: false, @@ -1171,6 +1177,11 @@ window.openAIChatManager = function () { // Prevent stale ReceiveTranscript events from repopulating the prompt. this._audioInputSent = true; + // In single-response mode, clear all previous messages. + if (this.singleResponseMode) { + this.messages.splice(0, this.messages.length); + } + this.addMessage({ role: 'user', content: trimmedPrompt @@ -1507,20 +1518,39 @@ window.openAIChatManager = function () { } } }, - synthesizeSpeech(text) { + synthesizeSpeech(text, cacheIndex) { if (!this.textToSpeechEnabled || !text || !this.connection) { return; } this.audioChunks = []; this.isPlayingAudio = true; + this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1; this.connection.invoke("SynthesizeSpeech", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName) .catch(err => { console.error("TTS synthesis error:", err); this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; + this._ttsCacheIndex = -1; }); }, + toggleMessageTts(message, index) { + if (this.ttsPlayingMessageIndex === index) { + this.stopAudio(); + return; + } + + this.stopAudio(); + this.ttsPlayingMessageIndex = index; + + if (this.ttsAudioCache[index]) { + this.playAudioBlob(this.ttsAudioCache[index]); + return; + } + + this.synthesizeSpeech(message.content, index); + }, playCollectedAudio() { if (this.audioChunks.length === 0) { if (!this.currentAudioElement && this.audioPlayQueue.length === 0) { @@ -1540,6 +1570,11 @@ window.openAIChatManager = function () { const blob = new Blob([combined], { type: 'audio/mp3' }); + if (this._ttsCacheIndex >= 0) { + this.ttsAudioCache[this._ttsCacheIndex] = blob; + this._ttsCacheIndex = -1; + } + // If audio is already playing, queue this blob for sequential playback. if (this.currentAudioElement) { this.audioPlayQueue.push(blob); @@ -1580,6 +1615,7 @@ window.openAIChatManager = function () { this.playAudioBlob(nextBlob); } else { this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; this.conversationModeOnAudioEnded(); } }, @@ -1592,6 +1628,7 @@ window.openAIChatManager = function () { this.audioChunks = []; this.audioPlayQueue = []; this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; }, toggleConversationMode() { if (this.isConversationMode) { diff --git a/src/Resources/CrestApps.AI.Resources/Assets/js/chat-interaction.js b/src/Resources/CrestApps.AI.Resources/Assets/js/chat-interaction.js index 83e78b5f..9dacd832 100644 --- a/src/Resources/CrestApps.AI.Resources/Assets/js/chat-interaction.js +++ b/src/Resources/CrestApps.AI.Resources/Assets/js/chat-interaction.js @@ -24,6 +24,9 @@ window.chatInteractionManager = function () {

{{ message.title }}

+ @@ -368,12 +371,14 @@ window.chatInteractionManager = function () { preRecordingPrompt: '', micButton: null, speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation', - textToSpeechEnabled: config.chatMode === 'Conversation', + textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled, ttsVoiceName: config.ttsVoiceName || null, audioChunks: [], audioPlayQueue: [], isPlayingAudio: false, currentAudioElement: null, + ttsPlayingMessageIndex: -1, + ttsAudioCache: {}, conversationModeEnabled: config.chatMode === 'Conversation', conversationButton: null, isConversationMode: false, @@ -1019,20 +1024,39 @@ window.chatInteractionManager = function () { this.debouncedSaveSettings(); } }, - synthesizeSpeech(text) { + synthesizeSpeech(text, cacheIndex) { if (!this.textToSpeechEnabled || !text || !this.connection) { return; } this.audioChunks = []; this.isPlayingAudio = true; + this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1; this.connection.invoke("SynthesizeSpeech", this.getItemId(), text, this.ttsVoiceName) .catch(err => { console.error("TTS synthesis error:", err); this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; + this._ttsCacheIndex = -1; }); }, + toggleMessageTts(message, index) { + if (this.ttsPlayingMessageIndex === index) { + this.stopAudio(); + return; + } + + this.stopAudio(); + this.ttsPlayingMessageIndex = index; + + if (this.ttsAudioCache[index]) { + this.playAudioBlob(this.ttsAudioCache[index]); + return; + } + + this.synthesizeSpeech(message.content, index); + }, playCollectedAudio() { if (this.audioChunks.length === 0) { if (!this.isPlayingAudio && this.audioPlayQueue.length === 0) { @@ -1052,6 +1076,11 @@ window.chatInteractionManager = function () { const blob = new Blob([combined], { type: 'audio/mp3' }); + if (this._ttsCacheIndex >= 0) { + this.ttsAudioCache[this._ttsCacheIndex] = blob; + this._ttsCacheIndex = -1; + } + // If audio is currently playing, queue this blob for later. if (this.isPlayingAudio && this.currentAudioElement) { this.audioPlayQueue.push(blob); @@ -1092,6 +1121,7 @@ window.chatInteractionManager = function () { this.playAudioBlob(nextBlob); } else { this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; this.conversationModeOnAudioEnded(); } }, @@ -1104,6 +1134,7 @@ window.chatInteractionManager = function () { this.audioChunks = []; this.audioPlayQueue = []; this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; }, toggleConversationMode() { if (this.isConversationMode) { diff --git a/src/Resources/CrestApps.AI.Resources/package.json b/src/Resources/CrestApps.AI.Resources/package.json index 75b9dd88..533fe604 100644 --- a/src/Resources/CrestApps.AI.Resources/package.json +++ b/src/Resources/CrestApps.AI.Resources/package.json @@ -33,16 +33,26 @@ ".": "./dist/ai-chat.js", "./ai-chat": "./dist/ai-chat.js", "./ai-chat.min": "./dist/ai-chat.min.js", + "./ai-chat.js.map": "./dist/ai-chat.js.map", + "./ai-chat.min.js.map": "./dist/ai-chat.min.js.map", "./chat-interaction": "./dist/chat-interaction.js", "./chat-interaction.min": "./dist/chat-interaction.min.js", + "./chat-interaction.js.map": "./dist/chat-interaction.js.map", + "./chat-interaction.min.js.map": "./dist/chat-interaction.min.js.map", "./document-drop-zone": "./dist/document-drop-zone.js", "./document-drop-zone.min": "./dist/document-drop-zone.min.js", + "./document-drop-zone.js.map": "./dist/document-drop-zone.js.map", + "./document-drop-zone.min.js.map": "./dist/document-drop-zone.min.js.map", "./technical-name-generator": "./dist/technical-name-generator.js", "./technical-name-generator.min": "./dist/technical-name-generator.min.js", + "./technical-name-generator.js.map": "./dist/technical-name-generator.js.map", + "./technical-name-generator.min.js.map": "./dist/technical-name-generator.min.js.map", "./chat-widget.css": "./dist/chat-widget.css", "./chat-widget.min.css": "./dist/chat-widget.min.css", + "./chat-widget.css.map": "./dist/chat-widget.css.map", "./document-drop-zone.css": "./dist/document-drop-zone.css", "./document-drop-zone.min.css": "./dist/document-drop-zone.min.css", + "./document-drop-zone.css.map": "./dist/document-drop-zone.css.map", "./dist/*": "./dist/*" }, "scripts": { diff --git a/src/Resources/CrestApps.AI.Resources/scripts/prepare-dist.js b/src/Resources/CrestApps.AI.Resources/scripts/prepare-dist.js index c9c950e2..30907ded 100644 --- a/src/Resources/CrestApps.AI.Resources/scripts/prepare-dist.js +++ b/src/Resources/CrestApps.AI.Resources/scripts/prepare-dist.js @@ -9,20 +9,20 @@ const root = path.join(__dirname, '..'); const dest = path.join(root, 'dist'); const sources = [ - { dir: path.join(root, 'wwwroot', 'scripts'), ext: '.js' }, - { dir: path.join(root, 'wwwroot', 'styles'), ext: '.css' }, + { dir: path.join(root, 'wwwroot', 'scripts'), exts: ['.js', '.js.map'] }, + { dir: path.join(root, 'wwwroot', 'styles'), exts: ['.css', '.css.map'] }, ]; fs.mkdirSync(dest, { recursive: true }); let count = 0; -for (const { dir, ext } of sources) { +for (const { dir, exts } of sources) { if (!fs.existsSync(dir)) { continue; } - const files = fs.readdirSync(dir).filter(f => f.endsWith(ext)); + const files = fs.readdirSync(dir).filter(f => exts.some(ext => f.endsWith(ext))); for (const file of files) { fs.copyFileSync(path.join(dir, file), path.join(dest, file)); diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.js index cda3fae9..f4398479 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.js @@ -34,7 +34,7 @@ window.openAIChatManager = function () { thumbsDownTitle: 'Thumbs down', copyTitle: 'Click here to copy response to clipboard.', codeCopiedText: 'Copied!', - messageTemplate: "\n
\n
\n
\n
{{ userLabel }}
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n
\n ", + messageTemplate: "\n
\n
\n
\n
{{ userLabel }}
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n
\n ", indicatorTemplate: "\n
\n \n Assistant\n
\n " }; @@ -388,13 +388,16 @@ window.openAIChatManager = function () { preRecordingPrompt: '', micButton: null, speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation', - textToSpeechEnabled: config.chatMode === 'Conversation', + textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled, ttsVoiceName: config.ttsVoiceName || null, audioChunks: [], audioPlayQueue: [], isPlayingAudio: false, currentAudioElement: null, ttsButton: null, + ttsPlayingMessageIndex: -1, + ttsAudioCache: {}, + singleResponseMode: !!config.singleResponseMode, conversationModeEnabled: config.chatMode === 'Conversation', conversationButton: null, isConversationMode: false, @@ -1190,6 +1193,11 @@ window.openAIChatManager = function () { // Prevent stale ReceiveTranscript events from repopulating the prompt. this._audioInputSent = true; + + // In single-response mode, clear all previous messages. + if (this.singleResponseMode) { + this.messages.splice(0, this.messages.length); + } this.addMessage({ role: 'user', content: trimmedPrompt @@ -1545,18 +1553,34 @@ window.openAIChatManager = function () { } } }, - synthesizeSpeech: function synthesizeSpeech(text) { + synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) { var _this0 = this; if (!this.textToSpeechEnabled || !text || !this.connection) { return; } this.audioChunks = []; this.isPlayingAudio = true; + this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1; this.connection.invoke("SynthesizeSpeech", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName)["catch"](function (err) { console.error("TTS synthesis error:", err); _this0.isPlayingAudio = false; + _this0.ttsPlayingMessageIndex = -1; + _this0._ttsCacheIndex = -1; }); }, + toggleMessageTts: function toggleMessageTts(message, index) { + if (this.ttsPlayingMessageIndex === index) { + this.stopAudio(); + return; + } + this.stopAudio(); + this.ttsPlayingMessageIndex = index; + if (this.ttsAudioCache[index]) { + this.playAudioBlob(this.ttsAudioCache[index]); + return; + } + this.synthesizeSpeech(message.content, index); + }, playCollectedAudio: function playCollectedAudio() { if (this.audioChunks.length === 0) { if (!this.currentAudioElement && this.audioPlayQueue.length === 0) { @@ -1586,6 +1610,10 @@ window.openAIChatManager = function () { var blob = new Blob([combined], { type: 'audio/mp3' }); + if (this._ttsCacheIndex >= 0) { + this.ttsAudioCache[this._ttsCacheIndex] = blob; + this._ttsCacheIndex = -1; + } // If audio is already playing, queue this blob for sequential playback. if (this.currentAudioElement) { @@ -1623,6 +1651,7 @@ window.openAIChatManager = function () { this.playAudioBlob(nextBlob); } else { this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; this.conversationModeOnAudioEnded(); } }, @@ -1635,6 +1664,7 @@ window.openAIChatManager = function () { this.audioChunks = []; this.audioPlayQueue = []; this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; }, toggleConversationMode: function toggleConversationMode() { if (this.isConversationMode) { @@ -2461,4 +2491,5 @@ document.addEventListener('click', function (e) { })["catch"](function (err) { console.error('Failed to download image:', err); }); -}); \ No newline at end of file +}); +//# sourceMappingURL=ai-chat.js.map diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.js.map b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.js.map new file mode 100644 index 00000000..a3416638 --- /dev/null +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["ai-chat.js"],"names":["e","t","r","Symbol","n","iterator","o","toStringTag","i","c","prototype","Generator","u","Object","create","_regeneratorDefine2","f","p","y","G","v","a","d","bind","length","l","TypeError","call","done","value","GeneratorFunction","GeneratorFunctionPrototype","getPrototypeOf","setPrototypeOf","__proto__","displayName","_regenerator","w","m","defineProperty","_regeneratorDefine","_invoke","enumerable","configurable","writable","asyncGeneratorStep","Promise","resolve","then","_asyncToGenerator","arguments","apply","_next","_throw","_createForOfIteratorHelper","Array","isArray","_unsupportedIterableToArray","_n","F","s","next","_toConsumableArray","_arrayWithoutHoles","_iterableToArray","_nonIterableSpread","from","_arrayLikeToArray","_slicedToArray","_arrayWithHoles","_iterableToArrayLimit","_nonIterableRest","toString","slice","constructor","name","test","push","_typeof","window","openAIChatManager","defaultConfig","generatedImageAltText","generatedImageMaxWidth","downloadImageTitle","downloadChartTitle","downloadChartButtonText","userLabel","assistantLabel","thumbsUpTitle","thumbsDownTitle","copyTitle","codeCopiedText","messageTemplate","indicatorTemplate","sanitizeUrl","url","trimmed","trim","escapeHtmlEntities","text","span","document","createElement","textContent","innerHTML","normalizeReference","reference","_ref","_normalized$index","_ref2","_normalized$text","_ref3","_normalized$link","normalized","assign","index","Index","Text","link","Link","normalizeReferences","references","_i","_Object$entries","entries","_normalizeReference","_Object$entries$_i","key","renderer","marked","Renderer","data","href","concat","code","lang","highlighted","hljs","getLanguage","highlight","language","_","highlightAuto","langDisplay","image","src","alt","maxWidth","chartCounter","_pendingCharts","__chartConfigs","createChartHtml","chartId","use","extensions","level","start","idx","indexOf","undefined","tokenizer","extracted","tryExtractChartMarker","startIndex","type","raw","substring","endIndex","json","token","config","jsonStart","depth","inString","escape","ch","jsonEnd","closeBracketIndex","renderChartsInMessage","message","charts","requestAnimationFrame","_iterator","_step","renderChartOnCanvas","err","canvas","getElementById","Chart","console","warn","offsetParent","_cfg$options","_cfg$options2","_cfg$options2$aspectR","_chartInstance","destroy","cfg","JSON","parse","options","responsive","maintainAspectRatio","aspectRatio","error","renderPendingCharts","configs","_i2","_Object$keys","keys","parseMarkdownContent","content","html","DOMPurify","sanitize","ADD_TAGS","ADD_ATTR","initialize","instanceConfig","signalRHubUrl","appElementSelector","chatContainerElementSelector","inputElementSelector","sendButtonElementSelector","app","Vue","createApp","inputElement","buttonElement","chatContainer","placeholder","isSessionStarted","isPlaceholderVisible","chatWidgetStateName","chatWidgetStateSession","chatHistorySection","widgetIsInitialized","isStreaming","isNavigatingAway","autoScroll","stream","messages","notifications","prompt","documents","existingDocuments","isUploading","uploadErrors","isDragOver","documentBar","metricsEnabled","isRecording","mediaRecorder","preRecordingPrompt","micButton","speechToTextEnabled","chatMode","textToSpeechEnabled","ttsVoiceName","audioChunks","audioPlayQueue","isPlayingAudio","currentAudioElement","ttsButton","ttsPlayingMessageIndex","ttsAudioCache","singleResponseMode","conversationModeEnabled","conversationButton","isConversationMode","notificationDismissTimers","computed","lastAssistantIndex","role","methods","handleBeforeUnload","handleDragOver","sessionDocumentsEnabled","preventDefault","stopPropagation","inputArea","closest","classList","add","handleDragLeave","remove","handleDrop","dataTransfer","files","uploadFiles","triggerFileInput","fileInput","click","handleFileInputChange","target","_this","_callee","sessionId","profileId","formData","response","errorText","uploadError","result","j","_t","_context","uploadDocumentUrl","getSessionId","getProfileId","renderDocumentBar","FormData","append","fetch","method","body","ok","extractReadableErrorMessage","fileName","initializeSession","uploaded","failed","removeDocument","doc","_this2","_callee2","removeError","_t2","_context2","removeDocumentUrl","headers","stringify","itemId","documentId","splice","status","formatFileSize","bytes","toFixed","escapeHtml","failedItem","failedName","errorMsg","supportedExtensionsText","replaceChildren","RETURN_DOM_FRAGMENT","self","closeButtons","querySelectorAll","addEventListener","docToRemove","parseInt","getAttribute","errorCloseButtons","addBtn","querySelector","div","fallbackMessage","startsWith","parsed","title","detail","normalizeAssistantAppearance","appearance","label","icon","cssClass","disableStreamingAnimation","getAssistantLabel","getAssistantRoleClasses","classes","getAssistantIconClasses","shouldAnimateAssistantIcon","getAssistantIcon","startConnection","_this3","_callee3","_t3","_context3","connection","signalR","HubConnectionBuilder","withUrl","withAutomaticReconnect","build","serverTimeoutInMilliseconds","keepAliveIntervalInMilliseconds","on","_data$messages","forEach","msg","addMessage","$nextTick","refreshAllFeedbackIcons","initialPrompt","sendMessage","stopRecording","_attemptedSessionRecovery","localStorage","removeItem","startNewSession","messageId","userRating","find","id","isFinal","_conversationPartialTranscript","escaped","replace","_conversationPartialMessage","hidePlaceholder","htmlContent","isPartial","scrollToBottom","_audioInputSent","dispatchEvent","Event","stopAudio","_conversationAssistantMessage","oldMsg","responseId","hideTypingIndicator","msgIndex","newMessage","base64Audio","contentType","binaryString","atob","Uint8Array","charCodeAt","playCollectedAudio","notification","receiveNotification","updateNotification","notificationType","removeNotification","onreconnecting","onreconnected","info","reloadCurrentSession","autoCreateSession","onclose","addMessageInternal","_this4","fireEvent","CustomEvent","_this5","processedContent","citedRefs","filter","_ref4","_ref5","includes","sort","_ref6","_ref7","_ref8","_ref9","b","displayIndex","_iterator2","_step2","_step2$value","replaceAll","_displayIndex","_placeholder","_iterator3","_step3","_step3$value","_iterator4","_step4","_step4$value","addMessages","_this6","showPlaceholder","isOrchestratorAvailable","applyOrchestratorAvailability","unavailableMessage","orchestratorUnavailableMessage","disabled","micButtonElement","style","display","conversationButtonElement","event","isIndicator","trimmedPrompt","streamMessage","startRecording","_this7","navigator","mediaDevices","getUserMedia","audio","echoCancellation","noiseSuppression","autoGainControl","mimeType","MediaRecorder","isTypeSupported","audioBitsPerSecond","subject","Subject","pendingChunk","size","_callee4","uint8Array","base64","_context4","arrayBuffer","reduce","str","byte","String","fromCharCode","btoa","getTracks","track","stop","complete","documentElement","send","updateMicButton","toggleRecording","sessionProfileId","_this8","dispose","streamingStarted","showTypingIndicator","lastResponseId","messageIndex","currentSessionId","subscribe","chunk","_i3","_Object$entries2","_normalizeReference2","_Object$entries2$_i","_i4","_Object$entries3","_Object$entries3$_i","_this8$stream","processReferences","streamingFinished","synthesizeSpeech","_this8$stream2","getServiceDownMessage","_this9","_ref1","_ref10","_ref11","_ref12","_ref13","_ref14","processed","_iterator5","_step5","_step5$value","_iterator6","_step6","_step6$value","_iterator7","_step7","_step7$value","stopIcon","setAttribute","startIcon","removeAttribute","focus","icons","cacheIndex","_this0","_ttsCacheIndex","invoke","toggleMessageTts","playAudioBlob","totalLength","sum","combined","offset","_iterator8","_step8","set","blob","Blob","_this1","URL","createObjectURL","Audio","revokeObjectURL","playNextInQueue","play","nextBlob","shift","conversationModeOnAudioEnded","pause","currentTime","toggleConversationMode","stopConversationMode","startConversationMode","_this10","updateConversationButton","_conversationSubject","_conversationStream","AudioCtx","AudioContext","webkitAudioContext","_conversationAudioCtx","_conversationAnalyser","createAnalyser","fftSize","micSource","createMediaStreamSource","connect","analyser","interruptVolumeThreshold","freqData","frequencyBinCount","getByteFrequencyData","k","avg","_callee5","_context5","close","dismissible","autoDismissMs","endHtml","blur","startHtml","conversationModeSendPrompt","generatePrompt","element","templateProfileId","createSessionUrl","baseUrl","param","fullUrl","toLowerCase","location","origin","searchParams","originalLength","removedCount","_this11","clearNotificationDismiss","existingIndex","findIndex","scheduleNotificationDismiss","_this12","setTimeout","timerId","clearTimeout","dismissNotification","handleNotificationAction","actionName","_this13","scrollTop","scrollHeight","clientHeight","handleUserInput","setSessionId","resetSession","sessionRating","initializeApp","_this14","placeholderElementSelector","widget","loadSession","documentBarSelector","className","multiple","allowedExtensions","accept","parentElement","appendChild","threshold","atBottom","shiftKey","lastMsg","pop","promptGenerators","getElementsByClassName","chatSessions","showChatScreen","btn","block","codeEl","clipboard","writeText","copiedText","micButtonElementSelector","conversationButtonElementSelector","force","setItem","initializeWidget","_this15","chatWidgetContainer","showHistoryButton","toggle","closeHistoryButton","newChatButton","getItem","copyResponse","updateFeedbackIcons","container","upBtn","downBtn","upClass","upIcon","fontSize","downClass","downIcon","FontAwesome","dom","i2svg","node","containers","$el","msgId","rateMessage","isPositive","newRating","feedbackContainer","btnClass","offsetWidth","onEnd","removeEventListener","watch","handler","deep","active","mounted","_this16","_callee6","_context6","beforeUnmount","template","mount","download","toDataURL","img","res","removeChild"],"mappings":"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","file":"ai-chat.js","sourcesContent":["window.openAIChatManager = function () {\n\n // Defaults (can be overridden by instanceConfig)\n var defaultConfig = {\n // UI defaults for generated media\n generatedImageAltText: 'Generated Image',\n generatedImageMaxWidth: 400,\n downloadImageTitle: 'Download image',\n downloadChartTitle: 'Download chart as image',\n downloadChartButtonText: 'Download',\n userLabel: 'You',\n assistantLabel: 'Assistant',\n thumbsUpTitle: 'Thumbs up',\n thumbsDownTitle: 'Thumbs down',\n copyTitle: 'Click here to copy response to clipboard.',\n codeCopiedText: 'Copied!',\n messageTemplate: `\n
\n
\n
\n
{{ userLabel }}
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n
\n `,\n indicatorTemplate: `\n
\n \n Assistant\n
\n `\n };\n\n // Sanitize URLs to prevent javascript: protocol injection.\n function sanitizeUrl(url) {\n if (!url) return '';\n var trimmed = url.trim();\n if (/^javascript:/i.test(trimmed) || /^vbscript:/i.test(trimmed) || /^data:text\\/html/i.test(trimmed)) {\n return '';\n }\n return url;\n }\n\n // Safely HTML-encode a string using the DOM (avoids regex-based HTML filtering).\n function escapeHtmlEntities(text) {\n var span = document.createElement('span');\n span.textContent = text;\n return span.innerHTML;\n }\n\n function normalizeReference(reference) {\n if (!reference || typeof reference !== 'object') {\n return null;\n }\n\n const normalized = Object.assign({}, reference);\n normalized.index = normalized.index ?? normalized.Index ?? 0;\n normalized.text = normalized.text ?? normalized.Text ?? null;\n normalized.link = normalized.link ?? normalized.Link ?? null;\n\n return normalized;\n }\n\n function normalizeReferences(references) {\n if (!references || typeof references !== 'object') {\n return {};\n }\n\n const normalized = {};\n\n for (const [key, value] of Object.entries(references)) {\n normalized[key] = normalizeReference(value) ?? {};\n }\n\n return normalized;\n }\n\n const renderer = new marked.Renderer();\n\n // Modify the link rendering to open in a new tab\n renderer.link = function (data) {\n var href = sanitizeUrl(data.href);\n if (!href) return data.text || '';\n return `${data.text}`;\n };\n\n // Custom code block renderer with highlight.js integration and copy button.\n renderer.code = function (data) {\n var code = data.text || '';\n var lang = (data.lang || '').trim();\n var highlighted = code;\n\n if (typeof hljs !== 'undefined') {\n if (lang && hljs.getLanguage(lang)) {\n try {\n highlighted = hljs.highlight(code, { language: lang }).value;\n } catch (_) { }\n } else {\n try {\n highlighted = hljs.highlightAuto(code).value;\n } catch (_) { }\n }\n } else {\n highlighted = escapeHtmlEntities(code);\n }\n\n var langDisplay = lang ? escapeHtmlEntities(lang) : 'code';\n return `
${langDisplay}
${highlighted}
`;\n };\n\n // Custom image renderer for generated images with thumbnail styling and download button.\n // Handles both URL and data-URI sources (data URIs are converted to blobs for download).\n renderer.image = function (data) {\n const src = sanitizeUrl(data.href);\n if (!src) return '';\n const alt = data.text || defaultConfig.generatedImageAltText;\n const maxWidth = defaultConfig.generatedImageMaxWidth;\n return `
\n \"${alt}\"\n
\n \n \n \n
\n
`;\n };\n\n // Chart counter for unique IDs\n let chartCounter = 0;\n\n // Collector for charts discovered during marked parsing.\n let _pendingCharts = [];\n\n // Global chart config map: any page (e.g., Chat Interactions) that uses\n // the shared marked instance can call window.renderPendingCharts() after\n // its DOM update to render charts it didn't create itself.\n window.__chartConfigs = window.__chartConfigs || {};\n\n function createChartHtml(chartId) {\n return `
`\n + ``\n + `
`\n + `
`\n + ``\n + `
`;\n }\n\n // Register [chart:{...json...}] as a native marked block extension so the\n // markdown parser handles chart markers inline with surrounding text.\n marked.use({\n extensions: [{\n name: 'chart',\n level: 'block',\n start(src) {\n const idx = src.indexOf('[chart:');\n return idx >= 0 ? idx : undefined;\n },\n tokenizer(src) {\n const extracted = tryExtractChartMarker(src);\n if (!extracted || extracted.startIndex !== 0) {\n return undefined;\n }\n\n const chartId = `chat_chart_${++chartCounter}`;\n\n return {\n type: 'chart',\n raw: src.substring(0, extracted.endIndex),\n chartId: chartId,\n json: extracted.json,\n };\n },\n renderer(token) {\n _pendingCharts.push({ chartId: token.chartId, config: token.json });\n window.__chartConfigs[token.chartId] = token.json;\n return createChartHtml(token.chartId);\n }\n }]\n });\n\n // Extract a [chart:{...json...}] marker. This avoids regex issues with nested brackets.\n function tryExtractChartMarker(text) {\n const token = '[chart:';\n const start = text.indexOf(token);\n if (start < 0) {\n return null;\n }\n\n // Find JSON object boundary by balancing braces\n const jsonStart = start + token.length;\n let i = jsonStart;\n while (i < text.length && (text[i] === ' ' || text[i] === '\\n' || text[i] === '\\r' || text[i] === '\\t')) {\n i++;\n }\n\n if (i >= text.length || text[i] !== '{') {\n return null;\n }\n\n let depth = 0;\n let inString = false;\n let escape = false;\n\n for (; i < text.length; i++) {\n const ch = text[i];\n\n if (inString) {\n if (escape) {\n escape = false;\n continue;\n }\n if (ch === '\\\\') {\n escape = true;\n continue;\n }\n if (ch === '\"') {\n inString = false;\n }\n continue;\n }\n\n if (ch === '\"') {\n inString = true;\n continue;\n }\n\n if (ch === '{') {\n depth++;\n } else if (ch === '}') {\n depth--;\n if (depth === 0) {\n const jsonEnd = i;\n // Expect closing bracket after JSON\n const closeBracketIndex = text.indexOf(']', jsonEnd + 1);\n if (closeBracketIndex < 0) {\n return null;\n }\n\n const json = text.substring(jsonStart, jsonEnd + 1).trim();\n return {\n startIndex: start,\n endIndex: closeBracketIndex + 1,\n json: json\n };\n }\n }\n }\n\n return null;\n }\n\n function renderChartsInMessage(message) {\n if (!message || !message._pendingCharts || !message._pendingCharts.length) {\n return;\n }\n\n // Copy and clear pending charts immediately to prevent duplicate renders.\n const charts = [...message._pendingCharts];\n message._pendingCharts = [];\n\n // Defer to requestAnimationFrame so the browser has fully laid out the\n // canvas elements before Chart.js reads their dimensions.\n requestAnimationFrame(() => {\n for (const c of charts) {\n renderChartOnCanvas(c.chartId, c.config);\n }\n });\n }\n\n function renderChartOnCanvas(chartId, config) {\n const canvas = document.getElementById(chartId);\n if (!canvas) {\n return false;\n }\n\n if (typeof Chart === 'undefined') {\n console.warn('Chart.js is not loaded. To render interactive charts, include the Chart.js library on the page (e.g., ).');\n return false;\n }\n\n // When the canvas is inside a hidden container (e.g., a widget panel with\n // display:none), it has zero dimensions and Chart.js cannot render correctly.\n // Keep the config in __chartConfigs so renderPendingCharts() can retry later\n // once the container becomes visible.\n if (canvas.offsetParent === null) {\n window.__chartConfigs[chartId] = config;\n return false;\n }\n\n try {\n if (canvas._chartInstance) {\n canvas._chartInstance.destroy();\n }\n\n const cfg = typeof config === 'string' ? JSON.parse(config) : config;\n cfg.options ??= {};\n cfg.options.responsive = true;\n cfg.options.maintainAspectRatio = true;\n cfg.options.aspectRatio ??= 4 / 3;\n\n canvas._chartInstance = new Chart(canvas, cfg);\n delete window.__chartConfigs[chartId];\n return true;\n } catch (e) {\n console.error('Error creating chart:', e);\n return false;\n }\n }\n\n // Global function: renders any chart canvases whose configs are in the\n // global __chartConfigs map. Called by pages (e.g., Chat Interactions)\n // that share the marked instance but have their own rendering pipeline.\n window.renderPendingCharts = function () {\n if (typeof Chart === 'undefined') {\n return;\n }\n\n const configs = window.__chartConfigs;\n if (!configs) {\n return;\n }\n\n requestAnimationFrame(() => {\n for (const chartId of Object.keys(configs)) {\n renderChartOnCanvas(chartId, configs[chartId]);\n }\n });\n };\n\n // Parse markdown content via marked (which natively handles [chart:...] markers\n // through the registered extension) and collect pending chart configs for later\n // Chart.js rendering.\n function parseMarkdownContent(content, message) {\n _pendingCharts = [];\n const html = marked.parse(content, { renderer });\n message._pendingCharts = _pendingCharts.length > 0 ? [..._pendingCharts] : [];\n return DOMPurify.sanitize(html, { ADD_TAGS: ['canvas'], ADD_ATTR: ['target'] });\n }\n\n const initialize = (instanceConfig) => {\n\n const config = Object.assign({}, defaultConfig, instanceConfig);\n // Keep defaultConfig in sync so renderers use overridden values\n defaultConfig = config;\n\n if (!config.signalRHubUrl) {\n console.error('The signalRHubUrl is required.');\n return;\n }\n\n if (!config.appElementSelector) {\n console.error('The appElementSelector is required.');\n return;\n }\n\n if (!config.chatContainerElementSelector) {\n console.error('The chatContainerElementSelector is required.');\n return;\n }\n\n if (!config.inputElementSelector) {\n console.error('The inputElementSelector is required.');\n return;\n }\n\n if (!config.sendButtonElementSelector) {\n console.error('The sendButtonElementSelector is required.');\n return;\n }\n\n const app = Vue.createApp({\n data() {\n return {\n inputElement: null,\n buttonElement: null,\n chatContainer: null,\n placeholder: null,\n isSessionStarted: false,\n isPlaceholderVisible: true,\n chatWidgetStateName: null,\n chatWidgetStateSession: null,\n chatHistorySection: null,\n widgetIsInitialized: false,\n isStreaming: false,\n isNavigatingAway: false,\n autoScroll: true,\n stream: null,\n messages: [],\n notifications: [],\n prompt: '',\n documents: config.existingDocuments || [],\n isUploading: false,\n uploadErrors: [],\n isDragOver: false,\n documentBar: null,\n metricsEnabled: !!config.metricsEnabled,\n userLabel: config.userLabel,\n assistantLabel: config.assistantLabel,\n thumbsUpTitle: config.thumbsUpTitle,\n thumbsDownTitle: config.thumbsDownTitle,\n copyTitle: config.copyTitle,\n isRecording: false,\n mediaRecorder: null,\n preRecordingPrompt: '',\n micButton: null,\n speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation',\n textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled,\n ttsVoiceName: config.ttsVoiceName || null,\n audioChunks: [],\n audioPlayQueue: [],\n isPlayingAudio: false,\n currentAudioElement: null,\n ttsButton: null,\n ttsPlayingMessageIndex: -1,\n ttsAudioCache: {},\n singleResponseMode: !!config.singleResponseMode,\n conversationModeEnabled: config.chatMode === 'Conversation',\n conversationButton: null,\n isConversationMode: false,\n notificationDismissTimers: {},\n };\n },\n computed: {\n lastAssistantIndex() {\n for (var i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n }\n },\n methods: {\n handleBeforeUnload() {\n this.isNavigatingAway = true;\n },\n handleDragOver(e) {\n if (!config.sessionDocumentsEnabled) return;\n e.preventDefault();\n e.stopPropagation();\n this.isDragOver = true;\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) inputArea.classList.add('ai-chat-drag-over');\n },\n handleDragLeave(e) {\n if (!config.sessionDocumentsEnabled) return;\n e.preventDefault();\n e.stopPropagation();\n this.isDragOver = false;\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) inputArea.classList.remove('ai-chat-drag-over');\n },\n handleDrop(e) {\n if (!config.sessionDocumentsEnabled) return;\n e.preventDefault();\n e.stopPropagation();\n this.isDragOver = false;\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) inputArea.classList.remove('ai-chat-drag-over');\n if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n this.uploadFiles(e.dataTransfer.files);\n }\n },\n triggerFileInput() {\n if (!config.sessionDocumentsEnabled) return;\n var fileInput = document.getElementById('ai-chat-doc-input');\n if (fileInput) fileInput.click();\n },\n handleFileInputChange(e) {\n var files = e.target.files;\n if (files && files.length > 0) {\n this.uploadFiles(files);\n }\n e.target.value = '';\n },\n async uploadFiles(files) {\n if (!config.uploadDocumentUrl) return;\n\n var sessionId = this.getSessionId();\n var profileId = this.getProfileId();\n\n if (!sessionId && !profileId) {\n console.warn('Cannot upload documents without a session or profile.');\n return;\n }\n\n this.isUploading = true;\n this.uploadErrors = [];\n this.renderDocumentBar();\n try {\n var formData = new FormData();\n if (sessionId) {\n formData.append('sessionId', sessionId);\n } else {\n formData.append('profileId', profileId);\n }\n for (var i = 0; i < files.length; i++) {\n formData.append('files', files[i]);\n }\n\n var response = await fetch(config.uploadDocumentUrl, {\n method: 'POST',\n body: formData\n });\n\n if (!response.ok) {\n var errorText = await response.text();\n var uploadError = this.extractReadableErrorMessage(errorText, 'Upload failed. Please try again.');\n console.error('Upload failed:', errorText);\n this.uploadErrors = [{ fileName: '', error: uploadError }];\n return;\n }\n\n var result = await response.json();\n\n // If the server created a new session, initialize it.\n if (result.sessionId && !sessionId) {\n this.initializeSession(result.sessionId);\n }\n\n if (result.uploaded && result.uploaded.length > 0) {\n for (var j = 0; j < result.uploaded.length; j++) {\n this.documents.push(result.uploaded[j]);\n }\n }\n if (result.failed && result.failed.length > 0) {\n this.uploadErrors = result.failed;\n }\n } catch (err) {\n console.error('Upload error:', err);\n this.uploadErrors = [{ fileName: '', error: 'Upload failed. Please try again.' }];\n } finally {\n this.isUploading = false;\n this.renderDocumentBar();\n }\n },\n async removeDocument(doc) {\n if (!config.removeDocumentUrl) return;\n\n try {\n var sessionId = this.getSessionId();\n var response = await fetch(config.removeDocumentUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ itemId: sessionId, documentId: doc.documentId })\n });\n\n if (response.ok) {\n var idx = this.documents.indexOf(doc);\n if (idx > -1) this.documents.splice(idx, 1);\n } else {\n var errorText = await response.text();\n var removeError = this.extractReadableErrorMessage(errorText, 'Failed to remove document. Please try again.');\n console.error('Failed to remove document:', response.status, errorText);\n this.uploadErrors = [{ fileName: doc.fileName || '', error: removeError }];\n this.renderDocumentBar();\n }\n } catch (err) {\n console.error('Remove document error:', err);\n this.uploadErrors = [{ fileName: doc.fileName || '', error: 'Failed to remove document. Please try again.' }];\n this.renderDocumentBar();\n }\n },\n formatFileSize(bytes) {\n if (bytes < 1024) return bytes + ' B';\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';\n return (bytes / (1024 * 1024)).toFixed(1) + ' MB';\n },\n renderDocumentBar() {\n if (!this.documentBar) return;\n\n if (!config.sessionDocumentsEnabled) {\n this.documentBar.classList.add('d-none');\n return;\n }\n\n this.documentBar.classList.remove('d-none');\n\n var html = '
';\n html += '
';\n\n for (var i = 0; i < this.documents.length; i++) {\n var doc = this.documents[i];\n var name = doc.fileName || 'Document';\n if (name.length > 20) name = name.substring(0, 17) + '...';\n html += '';\n html += ' ';\n html += this.escapeHtml(name);\n html += ' ';\n html += '';\n }\n\n for (var m = 0; m < this.uploadErrors.length; m++) {\n var failedItem = this.uploadErrors[m];\n var failedName = failedItem.fileName || 'File';\n var errorMsg = failedItem.error || 'Upload failed';\n if (failedName.length > 15) failedName = failedName.substring(0, 12) + '...';\n html += '';\n html += ' ';\n html += this.escapeHtml(failedName);\n html += ' ';\n html += '';\n }\n\n if (this.isUploading) {\n html += '';\n html += ' Uploading...';\n html += '';\n }\n\n html += '';\n html += '
';\n if (config.supportedExtensionsText) {\n html += '
Supported formats: ' + this.escapeHtml(config.supportedExtensionsText) + '
';\n }\n\n html += '
';\n\n this.documentBar.replaceChildren(DOMPurify.sanitize(html, { RETURN_DOM_FRAGMENT: true }));\n\n // Bind remove handlers\n var self = this;\n var closeButtons = this.documentBar.querySelectorAll('[data-doc-index]');\n for (var j = 0; j < closeButtons.length; j++) {\n closeButtons[j].addEventListener('click', (function (idx) {\n return function (e) {\n e.preventDefault();\n e.stopPropagation();\n var docToRemove = self.documents[idx];\n if (docToRemove) self.removeDocument(docToRemove);\n };\n })(parseInt(closeButtons[j].getAttribute('data-doc-index'))));\n }\n\n // Bind error dismiss handlers\n var errorCloseButtons = this.documentBar.querySelectorAll('[data-error-index]');\n for (var n = 0; n < errorCloseButtons.length; n++) {\n errorCloseButtons[n].addEventListener('click', (function (idx) {\n return function (e) {\n e.preventDefault();\n e.stopPropagation();\n self.uploadErrors.splice(idx, 1);\n self.renderDocumentBar();\n };\n })(parseInt(errorCloseButtons[n].getAttribute('data-error-index'))));\n }\n\n // Bind add button\n var addBtn = this.documentBar.querySelector('.ai-chat-doc-add-btn');\n if (addBtn) {\n addBtn.addEventListener('click', function (e) {\n e.preventDefault();\n self.triggerFileInput();\n });\n }\n },\n escapeHtml(text) {\n var div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n },\n extractReadableErrorMessage(errorText, fallbackMessage) {\n if (!errorText || typeof errorText !== 'string') {\n return fallbackMessage;\n }\n\n var trimmed = errorText.trim();\n if (!trimmed) {\n return fallbackMessage;\n }\n\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n var parsed = JSON.parse(trimmed);\n var message = parsed?.error || parsed?.message || parsed?.title || parsed?.detail;\n return typeof message === 'string' && message.trim()\n ? message.trim()\n : fallbackMessage;\n } catch (err) {\n return fallbackMessage;\n }\n }\n\n if (/<[^>]+>/.test(trimmed)) {\n return fallbackMessage;\n }\n\n return trimmed;\n },\n normalizeAssistantAppearance(appearance) {\n if (!appearance) {\n return null;\n }\n\n var label = typeof appearance.label === 'string' ? appearance.label.trim() : '';\n var icon = typeof appearance.icon === 'string' ? appearance.icon.trim() : '';\n var cssClass = typeof appearance.cssClass === 'string' ? appearance.cssClass.trim() : '';\n var disableStreamingAnimation = !!appearance.disableStreamingAnimation;\n\n if (!label && !icon && !cssClass && !disableStreamingAnimation) {\n return null;\n }\n\n return {\n label: label,\n icon: icon,\n cssClass: cssClass,\n disableStreamingAnimation: disableStreamingAnimation,\n };\n },\n getAssistantLabel(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.label ? appearance.label : this.assistantLabel;\n },\n getAssistantRoleClasses(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n var classes = ['ai-chat-msg-role'];\n\n if (appearance && appearance.cssClass) {\n classes.push(appearance.cssClass);\n } else {\n classes.push('ai-chat-msg-role-assistant');\n }\n\n return classes;\n },\n getAssistantIconClasses(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return [this.shouldAnimateAssistantIcon(message, index) ? 'ai-streaming-icon' : 'ai-bot-icon', appearance && appearance.cssClass ? appearance.cssClass : ''];\n },\n getAssistantIcon(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.icon ? appearance.icon : 'fa fa-robot';\n },\n shouldAnimateAssistantIcon(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return !!message &&\n message.isStreaming &&\n index === this.lastAssistantIndex &&\n !(appearance && appearance.disableStreamingAnimation);\n },\n async startConnection() {\n this.connection = new signalR.HubConnectionBuilder()\n .withUrl(config.signalRHubUrl)\n .withAutomaticReconnect()\n .build();\n\n // Allow long-running operations (e.g., multi-step MCP tool calls)\n // without the client disconnecting prematurely.\n this.connection.serverTimeoutInMilliseconds = 600000;\n this.connection.keepAliveIntervalInMilliseconds = 15000;\n\n this.connection.on(\"LoadSession\", (data) => {\n this.initializeSession(data.sessionId, true);\n this.messages = [];\n this.documents = data.documents || [];\n\n (data.messages ?? []).forEach(msg => {\n this.addMessage(msg);\n\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n });\n });\n\n // Update feedback icons in the DOM after all messages have rendered.\n this.$nextTick(() => {\n this.refreshAllFeedbackIcons();\n });\n\n // When the session is new (no messages) and an initial prompt is configured,\n // automatically send it as the first user message to trigger an AI response.\n if (this.messages.length === 0 && config.initialPrompt) {\n this.prompt = config.initialPrompt;\n this.sendMessage();\n }\n });\n\n this.connection.on(\"ReceiveError\", (error) => {\n console.error(\"SignalR Error: \", error);\n\n if (this.isRecording) {\n this.stopRecording();\n }\n\n // If this is a widget with a stale cached session (e.g., profile was deleted),\n // clear the cached session and start fresh with the current profile.\n if (this.widgetIsInitialized && !this.isSessionStarted && !this._attemptedSessionRecovery) {\n this._attemptedSessionRecovery = true;\n localStorage.removeItem(this.chatWidgetStateSession);\n this.startNewSession();\n }\n });\n\n this.connection.on(\"MessageRated\", (messageId, userRating) => {\n var msg = this.messages.find(m => m.id === messageId);\n if (msg) {\n msg.userRating = userRating;\n }\n });\n\n this.connection.on(\"ReceiveTranscript\", (sessionId, text, isFinal) => {\n if (this.isConversationMode) {\n if (!isFinal && text) {\n this._conversationPartialTranscript = text;\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n var html = '

' + escaped + '

';\n\n // Show partial transcript as a live user message.\n if (!this._conversationPartialMessage) {\n this.hidePlaceholder();\n this._conversationPartialMessage = {\n role: 'user',\n content: text,\n htmlContent: html,\n isPartial: true\n };\n this.messages.push(this._conversationPartialMessage);\n } else {\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = html;\n }\n this.scrollToBottom();\n }\n return;\n }\n\n if (text && !this._audioInputSent) {\n this.prompt = this.preRecordingPrompt + text;\n if (this.inputElement) {\n this.inputElement.value = this.prompt;\n this.inputElement.dispatchEvent(new Event('input'));\n }\n }\n });\n\n this.connection.on(\"ReceiveConversationUserMessage\", (sessionId, text) => {\n if (text) {\n this.stopAudio();\n\n // If there's an interrupted assistant message still streaming,\n // mark it as done to stop the spinner animation.\n if (this._conversationAssistantMessage) {\n var oldMsg = this.messages[this._conversationAssistantMessage.index];\n if (oldMsg) {\n oldMsg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Replace the partial transcript message with the final one.\n if (this._conversationPartialMessage) {\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = '

' + escaped + '

';\n this._conversationPartialMessage.isPartial = false;\n this._conversationPartialMessage = null;\n } else {\n this.addMessage({\n role: 'user',\n content: text\n });\n }\n this.scrollToBottom();\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantToken\", (sessionId, messageId, token, responseId, appearance) => {\n if (!this._conversationAssistantMessage) {\n this.stopAudio();\n this.hideTypingIndicator();\n\n // Ensure no stale streaming indicators remain from prior messages.\n for (var j = 0; j < this.messages.length; j++) {\n if (this.messages[j].isStreaming) {\n this.messages[j].isStreaming = false;\n }\n }\n\n var msgIndex = this.messages.length;\n var newMessage = {\n id: messageId,\n role: \"assistant\",\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n userRating: null,\n appearance: this.normalizeAssistantAppearance(appearance),\n };\n this.messages.push(newMessage);\n this._conversationAssistantMessage = { index: msgIndex, content: '' };\n }\n\n this._conversationAssistantMessage.content += token;\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n if (!msg.appearance) {\n msg.appearance = this.normalizeAssistantAppearance(appearance);\n }\n msg.content = this._conversationAssistantMessage.content;\n msg.htmlContent = parseMarkdownContent(msg.content, msg);\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n this.scrollToBottom();\n });\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantComplete\", (sessionId, messageId) => {\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n });\n\n this.connection.on(\"ReceiveAudioChunk\", (sessionId, base64Audio, contentType) => {\n if (base64Audio) {\n const binaryString = atob(base64Audio);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n this.audioChunks.push(bytes);\n }\n });\n\n this.connection.on(\"ReceiveAudioComplete\", (sessionId) => {\n this.playCollectedAudio();\n });\n\n this.connection.on(\"ReceiveNotification\", (notification) => {\n this.receiveNotification(notification);\n });\n\n this.connection.on(\"UpdateNotification\", (notification) => {\n this.updateNotification(notification);\n });\n\n this.connection.on(\"RemoveNotification\", (notificationType) => {\n this.removeNotification(notificationType);\n });\n\n this.connection.onreconnecting(() => {\n console.warn(\"SignalR: reconnecting...\");\n });\n\n this.connection.onreconnected(() => {\n console.info(\"SignalR: reconnected.\");\n\n if (this.isSessionStarted) {\n this.reloadCurrentSession();\n } else if (config.autoCreateSession) {\n this.startNewSession();\n }\n });\n\n this.connection.onclose((error) => {\n if (this.isNavigatingAway) {\n return;\n }\n\n if (error) {\n console.warn(\"SignalR connection closed with error:\", error.message || error);\n }\n });\n\n try {\n await this.connection.start();\n } catch (err) {\n console.error(\"SignalR Connection Error: \", err);\n }\n },\n addMessageInternal(message) {\n if (message.role === 'assistant') {\n message.appearance = this.normalizeAssistantAppearance(message.appearance);\n }\n\n if (message.content && !message.htmlContent) {\n message.htmlContent = parseMarkdownContent(message.content, message);\n }\n this.fireEvent(new CustomEvent(\"addingOpenAIPromotMessage\", { detail: { message: message } }));\n this.messages.push(message);\n\n this.$nextTick(() => {\n this.fireEvent(new CustomEvent(\"addedOpenAIPromotMessage\", { detail: { message: message } }));\n });\n },\n addMessage(message) {\n\n // Ensure userRating is always defined for Vue reactivity.\n if (message.userRating === undefined) {\n message.userRating = null;\n }\n\n if (message.content) {\n let processedContent = message.content.trim();\n message.references = normalizeReferences(message.references);\n\n if (message.references && typeof message.references === \"object\" && Object.keys(message.references).length) {\n\n // Only include references that were actually cited in the response.\n const citedRefs = Object.entries(message.references).filter(([key]) => processedContent.includes(key));\n\n if (citedRefs.length) {\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders.\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processedContent = processedContent.replaceAll(key, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processedContent = processedContent.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n // if we have multiple references, add a comma to ensure we don't concatenate numbers.\n processedContent = processedContent.replaceAll('', ',');\n\n processedContent += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || key;\n processedContent += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n }\n }\n\n message.content = processedContent;\n message.htmlContent = parseMarkdownContent(processedContent, message);\n }\n\n this.addMessageInternal(message);\n this.hidePlaceholder();\n\n this.$nextTick(() => {\n // Render any pending charts once the DOM is updated\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n addMessages(messages) {\n\n for (let i = 0; i < messages.length; i++) {\n this.addMessageInternal(messages[i]);\n }\n\n this.hidePlaceholder();\n this.$nextTick(() => {\n this.scrollToBottom();\n });\n },\n hidePlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.add('d-none');\n }\n this.isPlaceholderVisible = false;\n },\n showPlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.remove('d-none');\n }\n this.isPlaceholderVisible = true;\n },\n isOrchestratorAvailable() {\n return config.isOrchestratorAvailable !== false;\n },\n applyOrchestratorAvailability() {\n if (this.isOrchestratorAvailable()) {\n return true;\n }\n\n const unavailableMessage = config.orchestratorUnavailableMessage || \"This orchestrator is not currently available.\";\n\n if (this.inputElement) {\n this.inputElement.disabled = true;\n this.inputElement.value = '';\n this.inputElement.placeholder = unavailableMessage;\n }\n\n if (this.buttonElement) {\n this.buttonElement.disabled = true;\n }\n\n if (this.micButtonElement) {\n this.micButtonElement.disabled = true;\n this.micButtonElement.style.display = 'none';\n }\n\n if (this.conversationButtonElement) {\n this.conversationButtonElement.disabled = true;\n this.conversationButtonElement.style.display = 'none';\n }\n\n return false;\n },\n fireEvent(event) {\n document.dispatchEvent(event);\n },\n isIndicator(message) {\n return message.role === 'indicator';\n },\n sendMessage() {\n if (!this.applyOrchestratorAvailability()) {\n return;\n }\n\n let trimmedPrompt = this.prompt.trim();\n\n if (!trimmedPrompt) {\n return;\n }\n\n // Stop any active recording before sending.\n if (this.isRecording) {\n this.stopRecording();\n }\n\n // Prevent stale ReceiveTranscript events from repopulating the prompt.\n this._audioInputSent = true;\n\n // In single-response mode, clear all previous messages.\n if (this.singleResponseMode) {\n this.messages.splice(0, this.messages.length);\n }\n\n this.addMessage({\n role: 'user',\n content: trimmedPrompt\n });\n\n this.streamMessage(this.getProfileId(), trimmedPrompt, null);\n this.inputElement.value = '';\n this.prompt = '';\n },\n startRecording() {\n if (this.isRecording || !this.connection) {\n return;\n }\n\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this.preRecordingPrompt = this.prompt;\n this._audioInputSent = false;\n\n var subject = new signalR.Subject();\n var profileId = this.getProfileId();\n var sessionId = this.getSessionId() || '';\n var pendingChunk = Promise.resolve();\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n subject.next(base64);\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => subject.complete());\n });\n\n var language = navigator.language || document.documentElement.lang || 'en-US';\n this.connection.send(\"SendAudioStream\", profileId, sessionId, subject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n this.updateMicButton();\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n });\n },\n stopRecording() {\n if (!this.isRecording || !this.mediaRecorder) {\n return;\n }\n\n this.mediaRecorder.stop();\n this.isRecording = false;\n this.updateMicButton();\n },\n toggleRecording() {\n if (this.isRecording) {\n this.stopRecording();\n } else {\n this.startRecording();\n }\n },\n updateMicButton() {\n if (!this.micButton) {\n return;\n }\n\n if (this.isRecording) {\n this.micButton.classList.add('stt-recording');\n this.micButton.innerHTML = '';\n } else {\n this.micButton.classList.remove('stt-recording');\n this.micButton.innerHTML = '';\n }\n },\n streamMessage(profileId, trimmedPrompt, sessionProfileId) {\n\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n\n this.streamingStarted();\n this.showTypingIndicator();\n this.autoScroll = true;\n\n var content = '';\n var references = {};\n var lastResponseId = null;\n\n // Get the index after showing typing indicator.\n var messageIndex = this.messages.length;\n var currentSessionId = this.getSessionId();\n\n this.stream = this.connection.stream(\"SendMessage\", profileId, trimmedPrompt, currentSessionId, sessionProfileId)\n .subscribe({\n next: (chunk) => {\n let message = this.messages[messageIndex];\n\n if (!message) {\n\n if (chunk.sessionId && !currentSessionId) {\n this.initializeSession(chunk.sessionId);\n }\n\n this.hideTypingIndicator();\n // Re-assign the index after hiding the typing indicator.\n messageIndex = this.messages.length;\n let newMessage = {\n id: chunk.messageId,\n role: \"assistant\",\n title: chunk.title,\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n userRating: null,\n };\n\n this.messages.push(newMessage);\n\n message = newMessage;\n }\n\n if (chunk.title && (!message.title || message.title !== chunk.title)) {\n message.title = chunk.title;\n }\n\n if (chunk.references && typeof chunk.references === \"object\" && Object.keys(chunk.references).length) {\n\n for (const [key, value] of Object.entries(chunk.references)) {\n references[key] = normalizeReference(value) ?? {};\n }\n }\n\n if (chunk.content) {\n\n // When the responseId changes (e.g., after an internal tool call),\n // insert a line break to visually separate response segments.\n if (chunk.responseId && lastResponseId && chunk.responseId !== lastResponseId) {\n content += '\\n\\n';\n }\n\n if (chunk.responseId) {\n lastResponseId = chunk.responseId;\n }\n\n let processedContent = chunk.content;\n\n for (const [key, value] of Object.entries(references)) {\n processedContent = processedContent.replaceAll(key, `${value.index}`);\n }\n\n // Append processed content to the message.\n // if we have multiple references, add a comma to ensure we don't concatenate numbers.\n content += processedContent.replaceAll('
', ',');\n }\n\n // Update the existing message\n message.content = content;\n\n message.htmlContent = parseMarkdownContent(content, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n complete: () => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n if (!msg || !msg.content) {\n // No content received at all.\n this.hideTypingIndicator();\n }\n\n // Trigger text-to-speech only in conversation mode.\n if (this.isConversationMode && this.textToSpeechEnabled && msg && msg.content) {\n this.synthesizeSpeech(msg.content);\n }\n\n this.stream?.dispose();\n this.stream = null;\n },\n error: (err) => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n this.hideTypingIndicator();\n\n if (!this.isNavigatingAway) {\n this.addMessage(this.getServiceDownMessage());\n }\n\n this.stream?.dispose();\n this.stream = null;\n\n console.error(\"Stream error:\", err);\n }\n });\n },\n getServiceDownMessage() {\n let newMessage = {\n role: \"assistant\",\n content: \"Our service is currently unavailable. Please try again later. We apologize for the inconvenience.\",\n htmlContent: \"\",\n };\n\n return newMessage;\n },\n processReferences(references, messageIndex) {\n references = normalizeReferences(references);\n\n if (Object.keys(references).length) {\n\n let message = this.messages[messageIndex];\n const content = message.content || '';\n\n // Only include references that were actually cited in the response.\n // Check both raw [doc:N] markers and already-rendered tags from streaming.\n const citedRefs = Object.entries(references).filter(([key, value]) =>\n content.includes(key) || content.includes(`${value.index}`)\n );\n\n if (!citedRefs.length) {\n return;\n }\n\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders to avoid collisions during remapping.\n let processed = content.trim();\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processed = processed.replaceAll(key, placeholder);\n processed = processed.replaceAll(`${value.index}`, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processed = processed.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n processed = processed.replaceAll('', ',');\n\n processed += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || key;\n processed += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n\n message.content = processed;\n message.htmlContent = parseMarkdownContent(processed, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n }\n },\n streamingStarted() {\n var stopIcon = this.buttonElement.getAttribute('data-stop-icon');\n\n if (stopIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(stopIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n\n if (this.inputElement) {\n this.inputElement.setAttribute('disabled', 'disabled');\n }\n },\n streamingFinished() {\n var startIcon = this.buttonElement.getAttribute('data-start-icon');\n\n if (startIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(startIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n\n if (this.inputElement) {\n this.inputElement.removeAttribute('disabled');\n this.inputElement.focus();\n }\n\n // Directly manipulate the DOM to stop all streaming animations.\n if (this.chatContainer) {\n var icons = this.chatContainer.querySelectorAll('.ai-streaming-icon');\n for (var i = 0; i < icons.length; i++) {\n icons[i].classList.remove('ai-streaming-icon');\n icons[i].classList.add('ai-bot-icon');\n }\n }\n\n // Also update Vue data for consistency.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n },\n synthesizeSpeech(text, cacheIndex) {\n if (!this.textToSpeechEnabled || !text || !this.connection) {\n return;\n }\n\n this.audioChunks = [];\n this.isPlayingAudio = true;\n this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;\n\n this.connection.invoke(\"SynthesizeSpeech\", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName)\n .catch(err => {\n console.error(\"TTS synthesis error:\", err);\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this._ttsCacheIndex = -1;\n });\n },\n toggleMessageTts(message, index) {\n if (this.ttsPlayingMessageIndex === index) {\n this.stopAudio();\n return;\n }\n\n this.stopAudio();\n this.ttsPlayingMessageIndex = index;\n\n if (this.ttsAudioCache[index]) {\n this.playAudioBlob(this.ttsAudioCache[index]);\n return;\n }\n\n this.synthesizeSpeech(message.content, index);\n },\n playCollectedAudio() {\n if (this.audioChunks.length === 0) {\n if (!this.currentAudioElement && this.audioPlayQueue.length === 0) {\n this.isPlayingAudio = false;\n }\n return;\n }\n\n const totalLength = this.audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of this.audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n this.audioChunks = [];\n\n const blob = new Blob([combined], { type: 'audio/mp3' });\n\n if (this._ttsCacheIndex >= 0) {\n this.ttsAudioCache[this._ttsCacheIndex] = blob;\n this._ttsCacheIndex = -1;\n }\n\n // If audio is already playing, queue this blob for sequential playback.\n if (this.currentAudioElement) {\n this.audioPlayQueue.push(blob);\n return;\n }\n\n this.playAudioBlob(blob);\n },\n playAudioBlob(blob) {\n const url = URL.createObjectURL(blob);\n const audio = new Audio(url);\n\n this.currentAudioElement = audio;\n this.isPlayingAudio = true;\n\n audio.addEventListener('ended', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.addEventListener('error', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.play().catch(err => {\n console.error(\"Audio playback error:\", err);\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.isPlayingAudio = false;\n });\n },\n playNextInQueue() {\n if (this.audioPlayQueue.length > 0) {\n var nextBlob = this.audioPlayQueue.shift();\n this.playAudioBlob(nextBlob);\n } else {\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this.conversationModeOnAudioEnded();\n }\n },\n stopAudio() {\n if (this.currentAudioElement) {\n this.currentAudioElement.pause();\n this.currentAudioElement.currentTime = 0;\n this.currentAudioElement = null;\n }\n this.audioChunks = [];\n this.audioPlayQueue = [];\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n },\n toggleConversationMode() {\n if (this.isConversationMode) {\n this.stopConversationMode();\n } else {\n this.startConversationMode();\n }\n },\n startConversationMode() {\n if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {\n return;\n }\n\n this.isConversationMode = true;\n this.updateConversationButton();\n this._conversationPartialTranscript = '';\n this._conversationAssistantMessage = null;\n this._conversationPartialMessage = null;\n\n // Remove any previous conversation ended notification.\n this.removeNotification('conversation-ended');\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this._conversationSubject = new signalR.Subject();\n this._conversationStream = stream;\n\n // Create an AnalyserNode for volume-based interrupt detection.\n // During TTS playback, detect when the user speaks above\n // the threshold to stop TTS (interrupt). Audio chunks are\n // always forwarded — browser echo cancellation handles\n // speaker echo so the STT stream has no gaps.\n var AudioCtx = window.AudioContext || window.webkitAudioContext;\n if (AudioCtx) {\n this._conversationAudioCtx = new AudioCtx();\n this._conversationAnalyser = this._conversationAudioCtx.createAnalyser();\n this._conversationAnalyser.fftSize = 256;\n var micSource = this._conversationAudioCtx.createMediaStreamSource(stream);\n micSource.connect(this._conversationAnalyser);\n }\n\n var pendingChunk = Promise.resolve();\n var analyser = this._conversationAnalyser;\n var interruptVolumeThreshold = 30;\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n // During TTS playback, check mic volume to detect\n // user interruption (speaking above threshold).\n if (this.isPlayingAudio && analyser) {\n var freqData = new Uint8Array(analyser.frequencyBinCount);\n analyser.getByteFrequencyData(freqData);\n var sum = 0;\n for (var k = 0; k < freqData.length; k++) { sum += freqData[k]; }\n var avg = sum / freqData.length;\n\n if (avg >= interruptVolumeThreshold) {\n // User is speaking — interrupt TTS playback.\n this.stopAudio();\n }\n }\n\n // Always send audio to STT — browser echo cancellation\n // handles speaker echo; continuous audio avoids gaps\n // that increase recognition latency.\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n try {\n this._conversationSubject.next(base64);\n } catch (err) {\n // Subject may have been completed already.\n }\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => {\n try {\n this._conversationSubject.complete();\n } catch (err) {\n // Already completed.\n }\n });\n });\n\n var profileId = this.getProfileId();\n var sessionId = this.getSessionId() || '';\n var language = navigator.language || document.documentElement.lang || 'en-US';\n\n this.connection.send(\"StartConversation\", profileId, sessionId, this._conversationSubject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n this.isConversationMode = false;\n this.updateConversationButton();\n });\n },\n stopConversationMode() {\n if (!this.isConversationMode) {\n return;\n }\n\n this.isConversationMode = false;\n this.updateConversationButton();\n\n // Signal the server to cancel all in-progress STT/TTS streams immediately.\n if (this.connection) {\n this.connection.invoke(\"StopConversation\").catch(function () { });\n }\n\n if (this.isRecording && this.mediaRecorder) {\n this.mediaRecorder.stop();\n this.isRecording = false;\n }\n\n this.stopAudio();\n this._conversationPartialTranscript = '';\n this._conversationPartialMessage = null;\n\n // Clean up the AudioContext used for volume monitoring.\n if (this._conversationAudioCtx) {\n this._conversationAudioCtx.close().catch(function () { });\n this._conversationAudioCtx = null;\n this._conversationAnalyser = null;\n }\n\n // Mark any in-flight assistant message as done to stop the spinner.\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Safety net: clear all lingering streaming indicators.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n\n // Show a \"conversation ended\" notification system message.\n this.receiveNotification({\n type: 'conversation-ended',\n content: 'Conversation ended.',\n icon: 'fa-solid fa-circle-check',\n dismissible: true,\n autoDismissMs: 5000\n });\n },\n updateConversationButton() {\n if (!this.conversationButton) {\n return;\n }\n\n if (this.isConversationMode) {\n this.conversationButton.classList.add('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-dark', 'btn-outline-secondary');\n this.conversationButton.title = this.conversationButton.getAttribute('data-end-title') || 'End Conversation';\n var endHtml = this.conversationButton.getAttribute('data-end-html');\n if (endHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(endHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n } else {\n this.conversationButton.classList.remove('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-secondary');\n this.conversationButton.classList.add('btn-outline-dark');\n this.conversationButton.blur();\n this.conversationButton.title = this.conversationButton.getAttribute('data-start-title') || 'Start Conversation';\n var startHtml = this.conversationButton.getAttribute('data-start-html');\n if (startHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(startHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n }\n },\n conversationModeSendPrompt() {\n // Legacy: only used by AudioInput mode's ReceiveTranscript.\n },\n conversationModeOnAudioEnded() {\n // Legacy: in conversation mode, audio playback continuation\n // is handled by the persistent stream. This method is only\n // called from playCollectedAudio for non-conversation TTS.\n },\n generatePrompt(element) {\n if (!element) {\n console.error('The element paramter is required.');\n\n return;\n }\n\n let templateProfileId = element.getAttribute('data-profile-id');\n let sessionId = this.getSessionId();\n let sessionProfileId = this.getProfileId();\n\n if (!templateProfileId || !sessionId) {\n\n console.error('The given element is missing data-profile-id or the session has not yet started.');\n return;\n }\n\n // streamMessage() already shows the typing indicator.\n this.streamMessage(templateProfileId, null, sessionProfileId);\n },\n createSessionUrl(baseUrl, param, value) {\n\n const fullUrl = baseUrl.toLowerCase().startsWith('http') ? baseUrl : window.location.origin + baseUrl;\n const url = new URL(fullUrl);\n\n url.searchParams.set(param, value);\n\n return url.toString();\n },\n showTypingIndicator() {\n this.addMessage({\n role: 'indicator',\n htmlContent: config.indicatorTemplate\n });\n },\n hideTypingIndicator() {\n const originalLength = this.messages.length;\n this.messages = this.messages.filter(msg => msg.role !== 'indicator');\n const removedCount = originalLength - this.messages.length;\n return removedCount;\n },\n receiveNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n var existingIndex = this.notifications.findIndex(n => n.type === notification.type);\n if (existingIndex >= 0) {\n this.notifications.splice(existingIndex, 1, notification);\n } else {\n this.notifications.push(notification);\n }\n this.scheduleNotificationDismiss(notification);\n this.$nextTick(() => {\n this.scrollToBottom();\n });\n },\n updateNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n var existingIndex = this.notifications.findIndex(n => n.type === notification.type);\n if (existingIndex >= 0) {\n this.notifications.splice(existingIndex, 1, notification);\n this.scheduleNotificationDismiss(notification);\n }\n },\n scheduleNotificationDismiss(notification) {\n if (!notification || !notification.type || !notification.autoDismissMs || notification.autoDismissMs <= 0) {\n return;\n }\n this.notificationDismissTimers[notification.type] = setTimeout(() => {\n this.removeNotification(notification.type);\n }, notification.autoDismissMs);\n },\n clearNotificationDismiss(notificationType) {\n var timerId = this.notificationDismissTimers[notificationType];\n if (!timerId) {\n return;\n }\n clearTimeout(timerId);\n delete this.notificationDismissTimers[notificationType];\n },\n removeNotification(notificationType) {\n this.clearNotificationDismiss(notificationType);\n this.notifications = this.notifications.filter(n => n.type !== notificationType);\n },\n dismissNotification(notificationType) {\n this.removeNotification(notificationType);\n },\n handleNotificationAction(notificationType, actionName) {\n if (!this.connection) {\n return;\n }\n var sessionId = this.getSessionId();\n this.connection.invoke(\"HandleNotificationAction\", sessionId, notificationType, actionName).catch(function (err) {\n console.error(\"Error handling notification action:\", err);\n });\n },\n scrollToBottom() {\n if (!this.autoScroll) {\n return;\n }\n setTimeout(() => {\n this.chatContainer.scrollTop = this.chatContainer.scrollHeight - this.chatContainer.clientHeight;\n }, 50);\n },\n handleUserInput(event) {\n this.prompt = event.target.value;\n },\n getProfileId() {\n return this.inputElement.getAttribute('data-profile-id');\n },\n setSessionId(sessionId) {\n this.inputElement.setAttribute('data-session-id', sessionId || '');\n },\n resetSession() {\n this.stopRecording();\n this.setSessionId('');\n this.isSessionStarted = false;\n this.sessionRating = null;\n if (this.widgetIsInitialized) {\n localStorage.removeItem(this.chatWidgetStateSession);\n }\n this.messages = [];\n this.documents = [];\n if (!config.autoCreateSession) {\n this.showPlaceholder();\n }\n\n if (config.autoCreateSession) {\n this.startNewSession();\n }\n },\n startNewSession() {\n if (!this.applyOrchestratorAvailability()) {\n return;\n }\n\n const profileId = this.getProfileId();\n if (!profileId || !this.connection) {\n return;\n }\n\n this.connection.invoke(\"StartSession\", profileId, null).catch(err => console.error(err));\n },\n initializeApp() {\n this.inputElement = document.querySelector(config.inputElementSelector);\n this.buttonElement = document.querySelector(config.sendButtonElementSelector);\n this.chatContainer = document.querySelector(config.chatContainerElementSelector);\n this.placeholder = document.querySelector(config.placeholderElementSelector);\n this.applyOrchestratorAvailability();\n\n const sessionId = this.getSessionId();\n if (!config.widget && sessionId) {\n this.loadSession(sessionId);\n } else if (this.isOrchestratorAvailable() && config.autoCreateSession && !config.widget && !sessionId) {\n this.startNewSession();\n }\n\n // Initialize document bar if enabled.\n if (config.sessionDocumentsEnabled && config.documentBarSelector) {\n this.documentBar = document.querySelector(config.documentBarSelector);\n if (this.documentBar) {\n this.renderDocumentBar();\n\n // Create hidden file input for document uploads.\n var fileInput = document.createElement('input');\n fileInput.type = 'file';\n fileInput.id = 'ai-chat-doc-input';\n fileInput.className = 'd-none';\n fileInput.multiple = true;\n if (config.allowedExtensions) {\n fileInput.accept = config.allowedExtensions;\n }\n fileInput.addEventListener('change', (e) => this.handleFileInputChange(e));\n this.documentBar.parentElement.appendChild(fileInput);\n\n // Set up drag-and-drop on the input area.\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) {\n inputArea.addEventListener('dragover', (e) => this.handleDragOver(e));\n inputArea.addEventListener('dragleave', (e) => this.handleDragLeave(e));\n inputArea.addEventListener('drop', (e) => this.handleDrop(e));\n }\n }\n }\n\n // Pause auto-scroll when the user manually scrolls up during streaming.\n this.chatContainer.addEventListener('scroll', () => {\n if (!this.stream) {\n return;\n }\n const threshold = 30;\n const atBottom = this.chatContainer.scrollHeight - this.chatContainer.clientHeight - this.chatContainer.scrollTop <= threshold;\n this.autoScroll = atBottom;\n });\n\n this.inputElement.addEventListener('keydown', event => {\n\n if (this.stream != null) {\n return;\n }\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n this.buttonElement.click();\n }\n });\n\n this.inputElement.addEventListener('input', (e) => {\n this.handleUserInput(e);\n\n if (e.target.value.trim()) {\n this.buttonElement.removeAttribute('disabled');\n } else {\n this.buttonElement.setAttribute('disabled', true);\n }\n });\n\n this.buttonElement.addEventListener('click', () => {\n\n if (this.stream != null) {\n this.stream.dispose();\n this.stream = null;\n\n this.streamingFinished();\n this.hideTypingIndicator();\n\n // Clean up: remove empty assistant message or stop streaming animation.\n if (this.messages.length > 0) {\n const lastMsg = this.messages[this.messages.length - 1];\n if (lastMsg.role === 'assistant' && !lastMsg.content) {\n this.messages.pop();\n } else if (lastMsg.isStreaming) {\n lastMsg.isStreaming = false;\n }\n }\n\n return;\n }\n\n this.sendMessage();\n });\n\n const promptGenerators = document.getElementsByClassName('profile-generated-prompt');\n\n for (var i = 0; i < promptGenerators.length; i++) {\n promptGenerators[i].addEventListener('click', (e) => {\n e.preventDefault();\n this.generatePrompt(e.target);\n });\n }\n\n const chatSessions = document.getElementsByClassName('chat-session-history-item');\n\n for (var i = 0; i < chatSessions.length; i++) {\n chatSessions[i].addEventListener('click', (e) => {\n e.preventDefault();\n\n var sessionId = e.target.getAttribute('data-session-id');\n\n if (!sessionId) {\n console.error('an element with the class chat-session-history-item with no data-session-id set.');\n\n return;\n }\n\n this.loadSession(sessionId);\n this.showChatScreen();\n });\n }\n\n for (let i = 0; i < config.messages.length; i++) {\n this.addMessage(config.messages[i]);\n }\n\n // Update feedback icons in the DOM after initial messages have rendered.\n this.$nextTick(() => {\n this.refreshAllFeedbackIcons();\n });\n\n // Delegate click for code block copy buttons.\n if (this.chatContainer) {\n this.chatContainer.addEventListener('click', (e) => {\n var btn = e.target.closest('.ai-code-copy-btn');\n if (!btn) {\n return;\n }\n\n var block = btn.closest('.ai-code-block') || btn.closest('pre');\n if (!block) {\n return;\n }\n\n var codeEl = block.querySelector('code');\n if (codeEl) {\n navigator.clipboard.writeText(codeEl.textContent);\n var copiedText = config.codeCopiedText || 'Copied!';\n btn.innerHTML = ' ' + copiedText;\n setTimeout(() => {\n btn.innerHTML = '';\n }, 2000);\n }\n });\n }\n\n // Initialize speech-to-text microphone button.\n if (this.speechToTextEnabled && config.micButtonElementSelector) {\n this.micButton = document.querySelector(config.micButtonElementSelector);\n if (this.micButton) {\n this.micButton.style.display = '';\n this.micButton.addEventListener('click', () => {\n this.toggleRecording();\n });\n }\n }\n\n // Initialize conversation mode button.\n if (this.conversationModeEnabled && config.conversationButtonElementSelector) {\n this.conversationButton = document.querySelector(config.conversationButtonElementSelector);\n if (this.conversationButton) {\n this.conversationButton.addEventListener('click', () => {\n this.toggleConversationMode();\n });\n }\n }\n },\n loadSession(sessionId) {\n this.connection.invoke(\"LoadSession\", sessionId).catch(err => console.error(err));\n },\n reloadCurrentSession() {\n\n var sessionId = this.getSessionId();\n if (sessionId) {\n this.loadSession(sessionId);\n }\n },\n initializeSession(sessionId, force) {\n if (this.isSessionStarted && !force) {\n return\n }\n this.fireEvent(new CustomEvent(\"initializingSessionOpenAIChat\", { detail: { sessionId: sessionId } }))\n this.setSessionId(sessionId);\n this.isSessionStarted = true;\n\n if (this.widgetIsInitialized) {\n localStorage.setItem(this.chatWidgetStateSession, sessionId);\n }\n },\n initializeWidget() {\n\n if (!config.widget.chatWidgetContainer) {\n console.error('The widget chatWidgetContainer is required.');\n return;\n }\n\n if (!config.widget.chatWidgetStateName) {\n console.error('The widget chatWidgetStateName is required.');\n return;\n }\n\n const chatWidgetContainer = document.querySelector(config.widget.chatWidgetContainer);\n\n if (!chatWidgetContainer) {\n return;\n }\n\n if (config.widget.chatHistorySection) {\n this.chatHistorySection = document.querySelector(config.widget.chatHistorySection);\n }\n\n this.chatWidgetStateName = config.widget.chatWidgetStateName;\n this.chatWidgetStateSession = config.widget.chatWidgetStateName + 'Session';\n this.widgetIsInitialized = true;\n\n // Auto-load the last session so the user always sees previous chat history.\n this.reloadCurrentSession();\n\n if (config.autoCreateSession && !this.getSessionId()) {\n this.startNewSession();\n }\n\n if (config.widget.showHistoryButton && this.chatHistorySection) {\n\n const showHistoryButton = document.querySelector(config.widget.showHistoryButton);\n\n if (showHistoryButton) {\n showHistoryButton.addEventListener('click', () => {\n this.chatHistorySection.classList.toggle('show');\n });\n }\n\n if (config.widget.closeHistoryButton) {\n var closeHistoryButton = document.querySelector(config.widget.closeHistoryButton);\n\n if (closeHistoryButton) {\n closeHistoryButton.addEventListener('click', () => {\n this.showChatScreen();\n });\n }\n }\n }\n\n if (config.widget.newChatButton) {\n const newChatButton = document.querySelector(config.widget.newChatButton);\n\n if (newChatButton) {\n newChatButton.addEventListener('click', () => {\n this.resetSession();\n this.showChatScreen();\n });\n }\n }\n },\n showChatScreen() {\n\n if (!this.chatHistorySection) {\n return;\n }\n\n this.chatHistorySection.classList.remove('show');\n },\n getSessionId() {\n let sessionId = this.inputElement.getAttribute('data-session-id');\n\n if (!sessionId && this.widgetIsInitialized) {\n sessionId = localStorage.getItem(this.chatWidgetStateSession);\n }\n\n return sessionId;\n },\n copyResponse(message) {\n navigator.clipboard.writeText(message);\n },\n updateFeedbackIcons(container, userRating) {\n if (!container) {\n return;\n }\n\n var upBtn = container.querySelector('.rate-up-btn');\n var downBtn = container.querySelector('.rate-down-btn');\n\n // Font Awesome SVG+JS replaces with , so we must replace\n // the entire button content and let Font Awesome re-process.\n if (upBtn) {\n var upClass = userRating === true ? 'fa-solid fa-thumbs-up' : 'fa-regular fa-thumbs-up';\n var upIcon = document.createElement('i');\n upIcon.className = upClass;\n upIcon.style.fontSize = '0.9rem';\n upBtn.textContent = '';\n upBtn.appendChild(upIcon);\n }\n\n if (downBtn) {\n var downClass = userRating === false ? 'fa-solid fa-thumbs-down' : 'fa-regular fa-thumbs-down';\n var downIcon = document.createElement('i');\n downIcon.className = downClass;\n downIcon.style.fontSize = '0.9rem';\n downBtn.textContent = '';\n downBtn.appendChild(downIcon);\n }\n\n // Trigger Font Awesome SVG+JS to convert the new elements.\n if (window.FontAwesome && FontAwesome.dom && FontAwesome.dom.i2svg) {\n FontAwesome.dom.i2svg({ node: container });\n }\n },\n refreshAllFeedbackIcons() {\n var containers = this.$el.querySelectorAll('.ai-chat-message-assistant-feedback');\n\n for (var i = 0; i < containers.length; i++) {\n var msgId = containers[i].getAttribute('data-message-id');\n var msg = this.messages.find(m => m.id === msgId);\n\n if (msg) {\n this.updateFeedbackIcons(containers[i], msg.userRating);\n }\n }\n },\n rateMessage(message, isPositive, event) {\n var sessionId = this.getSessionId();\n\n if (!sessionId || !message.id || !this.connection) {\n return;\n }\n\n // Toggle: clicking the same rating again clears it.\n var newRating = message.userRating === isPositive ? null : isPositive;\n message.userRating = newRating;\n\n // Find the feedback container by message ID for reliable DOM targeting.\n var feedbackContainer = this.$el.querySelector(\n '.ai-chat-message-assistant-feedback[data-message-id=\"' + message.id + '\"]'\n );\n\n this.updateFeedbackIcons(feedbackContainer, newRating);\n\n // Trigger spark animation after Font Awesome has re-processed icons.\n if (newRating !== null && feedbackContainer) {\n setTimeout(() => {\n var btnClass = isPositive ? '.rate-up-btn' : '.rate-down-btn';\n var btn = feedbackContainer.querySelector(btnClass);\n\n if (btn) {\n btn.classList.remove('spark-effect');\n void btn.offsetWidth;\n btn.classList.add('spark-effect');\n btn.addEventListener('animationend', function onEnd() {\n btn.removeEventListener('animationend', onEnd);\n btn.classList.remove('spark-effect');\n });\n }\n }, 50);\n }\n\n this.connection.invoke(\"RateMessage\", sessionId, message.id, isPositive).catch(function (err) {\n console.error('Failed to rate message:', err);\n });\n }\n },\n watch: {\n documents: {\n handler() { this.renderDocumentBar(); },\n deep: true\n },\n isUploading() { this.renderDocumentBar(); },\n isPlayingAudio() {\n // Reserved for future use — volume-based interrupt detection\n // no longer mutes tracks; browser echo cancellation handles echo.\n },\n isConversationMode(active) {\n // Hide/show mic button.\n if (this.micButton) {\n this.micButton.style.display = active ? 'none' : (this.speechToTextEnabled ? '' : 'none');\n }\n\n // Hide/show send button.\n if (this.buttonElement) {\n this.buttonElement.style.display = active ? 'none' : '';\n }\n\n // Disable/enable textarea.\n if (this.inputElement) {\n this.inputElement.disabled = active;\n if (active) {\n this.inputElement.placeholder = '';\n }\n }\n }\n },\n mounted() {\n (async () => {\n await this.startConnection();\n this.initializeApp();\n if (config.widget) {\n this.initializeWidget();\n }\n })();\n\n window.addEventListener('beforeunload', this.handleBeforeUnload);\n },\n beforeUnmount() {\n window.removeEventListener('beforeunload', this.handleBeforeUnload);\n\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n if (this.connection) {\n this.connection.stop();\n }\n },\n template: config.messageTemplate\n }).mount(config.appElementSelector);\n\n return app;\n };\n\n return {\n initialize: initialize\n };\n}();\n\n// Download chart as image via event delegation (DOMPurify strips inline onclick).\ndocument.addEventListener('click', function (e) {\n const btn = e.target.closest('.download-chart-btn');\n if (!btn) {\n return;\n }\n const chartId = btn.getAttribute('data-chart-id');\n const canvas = chartId ? document.getElementById(chartId) : null;\n if (!canvas) {\n console.error('Chart canvas not found:', chartId);\n return;\n }\n const link = document.createElement('a');\n link.download = 'chart-' + chartId + '.png';\n link.href = canvas.toDataURL('image/png');\n link.click();\n});\n\n// Intercept download clicks for data-URI images and convert to blob downloads.\ndocument.addEventListener('click', function (e) {\n const link = e.target.closest('.ai-download-image');\n if (!link) {\n return;\n }\n\n const container = link.closest('.generated-image-container');\n const img = container?.querySelector('img');\n if (!img) {\n return;\n }\n\n const src = img.src;\n if (!src || !src.startsWith('data:')) {\n return; // Normal URL – let the default behaviour handle it.\n }\n\n e.preventDefault();\n\n fetch(src)\n .then(function (res) { return res.blob(); })\n .then(function (blob) {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = link.getAttribute('download') || 'generated-image.png';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(function () { URL.revokeObjectURL(url); }, 100);\n })\n .catch(function (err) { console.error('Failed to download image:', err); });\n});\n"]} \ No newline at end of file diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.min.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.min.js index 0d768b20..b3571d2a 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.min.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/ai-chat.min.js @@ -1 +1,2 @@ -function _regenerator(){/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */var e,t,n="function"==typeof Symbol?Symbol:{},i=n.iterator||"@@iterator",o=n.toStringTag||"@@toStringTag";function s(n,i,o,s){var c=i&&i.prototype instanceof r?i:r,l=Object.create(c.prototype);return _regeneratorDefine2(l,"_invoke",function(n,i,o){var s,r,c,l=0,d=o||[],u=!1,h={p:0,n:0,v:e,a:f,f:f.bind(e,4),d:function(t,n){return s=t,r=0,c=e,h.n=n,a}};function f(n,i){for(r=n,c=i,t=0;!u&&l&&!o&&t3?(o=m===i)&&(c=s[(r=s[4])?5:(r=3,3)],s[4]=s[5]=e):s[0]<=f&&((o=n<2&&fi||i>m)&&(s[4]=n,s[5]=i,h.n=m,r=0))}if(o||n>1)return a;throw u=!0,i}return function(o,d,m){if(l>1)throw TypeError("Generator is already running");for(u&&1===d&&f(d,m),r=d,c=m;(t=r<2?e:c)||!u;){s||(r?r<3?(r>1&&(h.n=-1),f(r,c)):h.n=c:h.v=c);try{if(l=2,s){if(r||(o="next"),t=s[o]){if(!(t=t.call(s,c)))throw TypeError("iterator result is not an object");if(!t.done)return t;c=t.value,r<2&&(r=0)}else 1===r&&(t=s.return)&&t.call(s),r<2&&(c=TypeError("The iterator does not provide a '"+o+"' method"),r=1);s=e}else if((t=(u=h.n<0)?c:n.call(i,h))!==a)break}catch(t){s=e,r=1,c=t}finally{l=1}}return{value:t,done:u}}}(n,o,s),!0),l}var a={};function r(){}function c(){}function l(){}t=Object.getPrototypeOf;var d=[][i]?t(t([][i]())):(_regeneratorDefine2(t={},i,function(){return this}),t),u=l.prototype=r.prototype=Object.create(d);function h(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,l):(e.__proto__=l,_regeneratorDefine2(e,o,"GeneratorFunction")),e.prototype=Object.create(u),e}return c.prototype=l,_regeneratorDefine2(u,"constructor",l),_regeneratorDefine2(l,"constructor",c),c.displayName="GeneratorFunction",_regeneratorDefine2(l,o,"GeneratorFunction"),_regeneratorDefine2(u),_regeneratorDefine2(u,o,"Generator"),_regeneratorDefine2(u,i,function(){return this}),_regeneratorDefine2(u,"toString",function(){return"[object Generator]"}),(_regenerator=function(){return{w:s,m:h}})()}function _regeneratorDefine2(e,t,n,i){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}_regeneratorDefine2=function(e,t,n,i){function s(t,n){_regeneratorDefine2(e,t,function(e){return this._invoke(t,n,e)})}t?o?o(e,t,{value:n,enumerable:!i,configurable:!i,writable:!i}):e[t]=n:(s("next",0),s("throw",1),s("return",2))},_regeneratorDefine2(e,t,n,i)}function asyncGeneratorStep(e,t,n,i,o,s,a){try{var r=e[s](a),c=r.value}catch(e){return void n(e)}r.done?t(c):Promise.resolve(c).then(i,o)}function _asyncToGenerator(e){return function(){var t=this,n=arguments;return new Promise(function(i,o){var s=e.apply(t,n);function a(e){asyncGeneratorStep(s,i,o,a,r,"next",e)}function r(e){asyncGeneratorStep(s,i,o,a,r,"throw",e)}a(void 0)})}}function _createForOfIteratorHelper(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=_unsupportedIterableToArray(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var i=0,o=function(){};return{s:o,n:function(){return i>=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,r=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){r=!0,s=e},f:function(){try{a||null==n.return||n.return()}finally{if(r)throw s}}}}function _toConsumableArray(e){return _arrayWithoutHoles(e)||_iterableToArray(e)||_unsupportedIterableToArray(e)||_nonIterableSpread()}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _iterableToArray(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}function _arrayWithoutHoles(e){if(Array.isArray(e))return _arrayLikeToArray(e)}function _slicedToArray(e,t){return _arrayWithHoles(e)||_iterableToArrayLimit(e,t)||_unsupportedIterableToArray(e,t)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(e,t){if(e){if("string"==typeof e)return _arrayLikeToArray(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_arrayLikeToArray(e,t):void 0}}function _arrayLikeToArray(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=Array(t);n\n
\n
\n
{{ userLabel }}
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n \n ',indicatorTemplate:'\n
\n \n Assistant\n
\n '};function t(e){if(!e)return"";var t=e.trim();return/^javascript:/i.test(t)||/^vbscript:/i.test(t)||/^data:text\/html/i.test(t)?"":e}function n(e){var t=document.createElement("span");return t.textContent=e,t.innerHTML}function i(e){var t,n,i,o,s,a;if(!e||"object"!==_typeof(e))return null;var r=Object.assign({},e);return r.index=null!==(t=null!==(n=r.index)&&void 0!==n?n:r.Index)&&void 0!==t?t:0,r.text=null!==(i=null!==(o=r.text)&&void 0!==o?o:r.Text)&&void 0!==i?i:null,r.link=null!==(s=null!==(a=r.link)&&void 0!==a?a:r.Link)&&void 0!==s?s:null,r}function o(e){if(!e||"object"!==_typeof(e))return{};for(var t={},n=0,o=Object.entries(e);n').concat(e.text,"
"):e.text||""},s.code=function(e){var t=e.text||"",i=(e.lang||"").trim(),o=t;if("undefined"!=typeof hljs)if(i&&hljs.getLanguage(i))try{o=hljs.highlight(t,{language:i}).value}catch(e){}else try{o=hljs.highlightAuto(t).value}catch(e){}else o=n(t);var s=i?n(i):"code";return'
'.concat(s,'
').concat(o,"
")},s.image=function(n){var i=t(n.href);if(!i)return"";var o=n.text||e.generatedImageAltText,s=e.generatedImageMaxWidth;return'
\n ').concat(o,'\n
\n \n \n \n
\n
')};var a=0,r=[];function c(e){if(e&&e._pendingCharts&&e._pendingCharts.length){var t=_toConsumableArray(e._pendingCharts);e._pendingCharts=[],requestAnimationFrame(function(){var e,n=_createForOfIteratorHelper(t);try{for(n.s();!(e=n.n()).done;){var i=e.value;l(i.chartId,i.config)}}catch(e){n.e(e)}finally{n.f()}})}}function l(e,t){var n=document.getElementById(e);if(!n)return!1;if("undefined"==typeof Chart)return console.warn('Chart.js is not loaded. To render interactive charts, include the Chart.js library on the page (e.g., ).');\n return false;\n }\n\n // When the canvas is inside a hidden container (e.g., a widget panel with\n // display:none), it has zero dimensions and Chart.js cannot render correctly.\n // Keep the config in __chartConfigs so renderPendingCharts() can retry later\n // once the container becomes visible.\n if (canvas.offsetParent === null) {\n window.__chartConfigs[chartId] = config;\n return false;\n }\n\n try {\n if (canvas._chartInstance) {\n canvas._chartInstance.destroy();\n }\n\n const cfg = typeof config === 'string' ? JSON.parse(config) : config;\n cfg.options ??= {};\n cfg.options.responsive = true;\n cfg.options.maintainAspectRatio = true;\n cfg.options.aspectRatio ??= 4 / 3;\n\n canvas._chartInstance = new Chart(canvas, cfg);\n delete window.__chartConfigs[chartId];\n return true;\n } catch (e) {\n console.error('Error creating chart:', e);\n return false;\n }\n }\n\n // Global function: renders any chart canvases whose configs are in the\n // global __chartConfigs map. Called by pages (e.g., Chat Interactions)\n // that share the marked instance but have their own rendering pipeline.\n window.renderPendingCharts = function () {\n if (typeof Chart === 'undefined') {\n return;\n }\n\n const configs = window.__chartConfigs;\n if (!configs) {\n return;\n }\n\n requestAnimationFrame(() => {\n for (const chartId of Object.keys(configs)) {\n renderChartOnCanvas(chartId, configs[chartId]);\n }\n });\n };\n\n // Parse markdown content via marked (which natively handles [chart:...] markers\n // through the registered extension) and collect pending chart configs for later\n // Chart.js rendering.\n function parseMarkdownContent(content, message) {\n _pendingCharts = [];\n const html = marked.parse(content, { renderer });\n message._pendingCharts = _pendingCharts.length > 0 ? [..._pendingCharts] : [];\n return DOMPurify.sanitize(html, { ADD_TAGS: ['canvas'], ADD_ATTR: ['target'] });\n }\n\n const initialize = (instanceConfig) => {\n\n const config = Object.assign({}, defaultConfig, instanceConfig);\n // Keep defaultConfig in sync so renderers use overridden values\n defaultConfig = config;\n\n if (!config.signalRHubUrl) {\n console.error('The signalRHubUrl is required.');\n return;\n }\n\n if (!config.appElementSelector) {\n console.error('The appElementSelector is required.');\n return;\n }\n\n if (!config.chatContainerElementSelector) {\n console.error('The chatContainerElementSelector is required.');\n return;\n }\n\n if (!config.inputElementSelector) {\n console.error('The inputElementSelector is required.');\n return;\n }\n\n if (!config.sendButtonElementSelector) {\n console.error('The sendButtonElementSelector is required.');\n return;\n }\n\n const app = Vue.createApp({\n data() {\n return {\n inputElement: null,\n buttonElement: null,\n chatContainer: null,\n placeholder: null,\n isSessionStarted: false,\n isPlaceholderVisible: true,\n chatWidgetStateName: null,\n chatWidgetStateSession: null,\n chatHistorySection: null,\n widgetIsInitialized: false,\n isStreaming: false,\n isNavigatingAway: false,\n autoScroll: true,\n stream: null,\n messages: [],\n notifications: [],\n prompt: '',\n documents: config.existingDocuments || [],\n isUploading: false,\n uploadErrors: [],\n isDragOver: false,\n documentBar: null,\n metricsEnabled: !!config.metricsEnabled,\n userLabel: config.userLabel,\n assistantLabel: config.assistantLabel,\n thumbsUpTitle: config.thumbsUpTitle,\n thumbsDownTitle: config.thumbsDownTitle,\n copyTitle: config.copyTitle,\n isRecording: false,\n mediaRecorder: null,\n preRecordingPrompt: '',\n micButton: null,\n speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation',\n textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled,\n ttsVoiceName: config.ttsVoiceName || null,\n audioChunks: [],\n audioPlayQueue: [],\n isPlayingAudio: false,\n currentAudioElement: null,\n ttsButton: null,\n ttsPlayingMessageIndex: -1,\n ttsAudioCache: {},\n singleResponseMode: !!config.singleResponseMode,\n conversationModeEnabled: config.chatMode === 'Conversation',\n conversationButton: null,\n isConversationMode: false,\n notificationDismissTimers: {},\n };\n },\n computed: {\n lastAssistantIndex() {\n for (var i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n }\n },\n methods: {\n handleBeforeUnload() {\n this.isNavigatingAway = true;\n },\n handleDragOver(e) {\n if (!config.sessionDocumentsEnabled) return;\n e.preventDefault();\n e.stopPropagation();\n this.isDragOver = true;\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) inputArea.classList.add('ai-chat-drag-over');\n },\n handleDragLeave(e) {\n if (!config.sessionDocumentsEnabled) return;\n e.preventDefault();\n e.stopPropagation();\n this.isDragOver = false;\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) inputArea.classList.remove('ai-chat-drag-over');\n },\n handleDrop(e) {\n if (!config.sessionDocumentsEnabled) return;\n e.preventDefault();\n e.stopPropagation();\n this.isDragOver = false;\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) inputArea.classList.remove('ai-chat-drag-over');\n if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n this.uploadFiles(e.dataTransfer.files);\n }\n },\n triggerFileInput() {\n if (!config.sessionDocumentsEnabled) return;\n var fileInput = document.getElementById('ai-chat-doc-input');\n if (fileInput) fileInput.click();\n },\n handleFileInputChange(e) {\n var files = e.target.files;\n if (files && files.length > 0) {\n this.uploadFiles(files);\n }\n e.target.value = '';\n },\n async uploadFiles(files) {\n if (!config.uploadDocumentUrl) return;\n\n var sessionId = this.getSessionId();\n var profileId = this.getProfileId();\n\n if (!sessionId && !profileId) {\n console.warn('Cannot upload documents without a session or profile.');\n return;\n }\n\n this.isUploading = true;\n this.uploadErrors = [];\n this.renderDocumentBar();\n try {\n var formData = new FormData();\n if (sessionId) {\n formData.append('sessionId', sessionId);\n } else {\n formData.append('profileId', profileId);\n }\n for (var i = 0; i < files.length; i++) {\n formData.append('files', files[i]);\n }\n\n var response = await fetch(config.uploadDocumentUrl, {\n method: 'POST',\n body: formData\n });\n\n if (!response.ok) {\n var errorText = await response.text();\n var uploadError = this.extractReadableErrorMessage(errorText, 'Upload failed. Please try again.');\n console.error('Upload failed:', errorText);\n this.uploadErrors = [{ fileName: '', error: uploadError }];\n return;\n }\n\n var result = await response.json();\n\n // If the server created a new session, initialize it.\n if (result.sessionId && !sessionId) {\n this.initializeSession(result.sessionId);\n }\n\n if (result.uploaded && result.uploaded.length > 0) {\n for (var j = 0; j < result.uploaded.length; j++) {\n this.documents.push(result.uploaded[j]);\n }\n }\n if (result.failed && result.failed.length > 0) {\n this.uploadErrors = result.failed;\n }\n } catch (err) {\n console.error('Upload error:', err);\n this.uploadErrors = [{ fileName: '', error: 'Upload failed. Please try again.' }];\n } finally {\n this.isUploading = false;\n this.renderDocumentBar();\n }\n },\n async removeDocument(doc) {\n if (!config.removeDocumentUrl) return;\n\n try {\n var sessionId = this.getSessionId();\n var response = await fetch(config.removeDocumentUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ itemId: sessionId, documentId: doc.documentId })\n });\n\n if (response.ok) {\n var idx = this.documents.indexOf(doc);\n if (idx > -1) this.documents.splice(idx, 1);\n } else {\n var errorText = await response.text();\n var removeError = this.extractReadableErrorMessage(errorText, 'Failed to remove document. Please try again.');\n console.error('Failed to remove document:', response.status, errorText);\n this.uploadErrors = [{ fileName: doc.fileName || '', error: removeError }];\n this.renderDocumentBar();\n }\n } catch (err) {\n console.error('Remove document error:', err);\n this.uploadErrors = [{ fileName: doc.fileName || '', error: 'Failed to remove document. Please try again.' }];\n this.renderDocumentBar();\n }\n },\n formatFileSize(bytes) {\n if (bytes < 1024) return bytes + ' B';\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';\n return (bytes / (1024 * 1024)).toFixed(1) + ' MB';\n },\n renderDocumentBar() {\n if (!this.documentBar) return;\n\n if (!config.sessionDocumentsEnabled) {\n this.documentBar.classList.add('d-none');\n return;\n }\n\n this.documentBar.classList.remove('d-none');\n\n var html = '
';\n html += '
';\n\n for (var i = 0; i < this.documents.length; i++) {\n var doc = this.documents[i];\n var name = doc.fileName || 'Document';\n if (name.length > 20) name = name.substring(0, 17) + '...';\n html += '';\n html += ' ';\n html += this.escapeHtml(name);\n html += ' ';\n html += '';\n }\n\n for (var m = 0; m < this.uploadErrors.length; m++) {\n var failedItem = this.uploadErrors[m];\n var failedName = failedItem.fileName || 'File';\n var errorMsg = failedItem.error || 'Upload failed';\n if (failedName.length > 15) failedName = failedName.substring(0, 12) + '...';\n html += '';\n html += ' ';\n html += this.escapeHtml(failedName);\n html += ' ';\n html += '';\n }\n\n if (this.isUploading) {\n html += '';\n html += ' Uploading...';\n html += '';\n }\n\n html += '';\n html += '
';\n if (config.supportedExtensionsText) {\n html += '
Supported formats: ' + this.escapeHtml(config.supportedExtensionsText) + '
';\n }\n\n html += '
';\n\n this.documentBar.replaceChildren(DOMPurify.sanitize(html, { RETURN_DOM_FRAGMENT: true }));\n\n // Bind remove handlers\n var self = this;\n var closeButtons = this.documentBar.querySelectorAll('[data-doc-index]');\n for (var j = 0; j < closeButtons.length; j++) {\n closeButtons[j].addEventListener('click', (function (idx) {\n return function (e) {\n e.preventDefault();\n e.stopPropagation();\n var docToRemove = self.documents[idx];\n if (docToRemove) self.removeDocument(docToRemove);\n };\n })(parseInt(closeButtons[j].getAttribute('data-doc-index'))));\n }\n\n // Bind error dismiss handlers\n var errorCloseButtons = this.documentBar.querySelectorAll('[data-error-index]');\n for (var n = 0; n < errorCloseButtons.length; n++) {\n errorCloseButtons[n].addEventListener('click', (function (idx) {\n return function (e) {\n e.preventDefault();\n e.stopPropagation();\n self.uploadErrors.splice(idx, 1);\n self.renderDocumentBar();\n };\n })(parseInt(errorCloseButtons[n].getAttribute('data-error-index'))));\n }\n\n // Bind add button\n var addBtn = this.documentBar.querySelector('.ai-chat-doc-add-btn');\n if (addBtn) {\n addBtn.addEventListener('click', function (e) {\n e.preventDefault();\n self.triggerFileInput();\n });\n }\n },\n escapeHtml(text) {\n var div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n },\n extractReadableErrorMessage(errorText, fallbackMessage) {\n if (!errorText || typeof errorText !== 'string') {\n return fallbackMessage;\n }\n\n var trimmed = errorText.trim();\n if (!trimmed) {\n return fallbackMessage;\n }\n\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n var parsed = JSON.parse(trimmed);\n var message = parsed?.error || parsed?.message || parsed?.title || parsed?.detail;\n return typeof message === 'string' && message.trim()\n ? message.trim()\n : fallbackMessage;\n } catch (err) {\n return fallbackMessage;\n }\n }\n\n if (/<[^>]+>/.test(trimmed)) {\n return fallbackMessage;\n }\n\n return trimmed;\n },\n normalizeAssistantAppearance(appearance) {\n if (!appearance) {\n return null;\n }\n\n var label = typeof appearance.label === 'string' ? appearance.label.trim() : '';\n var icon = typeof appearance.icon === 'string' ? appearance.icon.trim() : '';\n var cssClass = typeof appearance.cssClass === 'string' ? appearance.cssClass.trim() : '';\n var disableStreamingAnimation = !!appearance.disableStreamingAnimation;\n\n if (!label && !icon && !cssClass && !disableStreamingAnimation) {\n return null;\n }\n\n return {\n label: label,\n icon: icon,\n cssClass: cssClass,\n disableStreamingAnimation: disableStreamingAnimation,\n };\n },\n getAssistantLabel(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.label ? appearance.label : this.assistantLabel;\n },\n getAssistantRoleClasses(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n var classes = ['ai-chat-msg-role'];\n\n if (appearance && appearance.cssClass) {\n classes.push(appearance.cssClass);\n } else {\n classes.push('ai-chat-msg-role-assistant');\n }\n\n return classes;\n },\n getAssistantIconClasses(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return [this.shouldAnimateAssistantIcon(message, index) ? 'ai-streaming-icon' : 'ai-bot-icon', appearance && appearance.cssClass ? appearance.cssClass : ''];\n },\n getAssistantIcon(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.icon ? appearance.icon : 'fa fa-robot';\n },\n shouldAnimateAssistantIcon(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return !!message &&\n message.isStreaming &&\n index === this.lastAssistantIndex &&\n !(appearance && appearance.disableStreamingAnimation);\n },\n async startConnection() {\n this.connection = new signalR.HubConnectionBuilder()\n .withUrl(config.signalRHubUrl)\n .withAutomaticReconnect()\n .build();\n\n // Allow long-running operations (e.g., multi-step MCP tool calls)\n // without the client disconnecting prematurely.\n this.connection.serverTimeoutInMilliseconds = 600000;\n this.connection.keepAliveIntervalInMilliseconds = 15000;\n\n this.connection.on(\"LoadSession\", (data) => {\n this.initializeSession(data.sessionId, true);\n this.messages = [];\n this.documents = data.documents || [];\n\n (data.messages ?? []).forEach(msg => {\n this.addMessage(msg);\n\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n });\n });\n\n // Update feedback icons in the DOM after all messages have rendered.\n this.$nextTick(() => {\n this.refreshAllFeedbackIcons();\n });\n\n // When the session is new (no messages) and an initial prompt is configured,\n // automatically send it as the first user message to trigger an AI response.\n if (this.messages.length === 0 && config.initialPrompt) {\n this.prompt = config.initialPrompt;\n this.sendMessage();\n }\n });\n\n this.connection.on(\"ReceiveError\", (error) => {\n console.error(\"SignalR Error: \", error);\n\n if (this.isRecording) {\n this.stopRecording();\n }\n\n // If this is a widget with a stale cached session (e.g., profile was deleted),\n // clear the cached session and start fresh with the current profile.\n if (this.widgetIsInitialized && !this.isSessionStarted && !this._attemptedSessionRecovery) {\n this._attemptedSessionRecovery = true;\n localStorage.removeItem(this.chatWidgetStateSession);\n this.startNewSession();\n }\n });\n\n this.connection.on(\"MessageRated\", (messageId, userRating) => {\n var msg = this.messages.find(m => m.id === messageId);\n if (msg) {\n msg.userRating = userRating;\n }\n });\n\n this.connection.on(\"ReceiveTranscript\", (sessionId, text, isFinal) => {\n if (this.isConversationMode) {\n if (!isFinal && text) {\n this._conversationPartialTranscript = text;\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n var html = '

' + escaped + '

';\n\n // Show partial transcript as a live user message.\n if (!this._conversationPartialMessage) {\n this.hidePlaceholder();\n this._conversationPartialMessage = {\n role: 'user',\n content: text,\n htmlContent: html,\n isPartial: true\n };\n this.messages.push(this._conversationPartialMessage);\n } else {\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = html;\n }\n this.scrollToBottom();\n }\n return;\n }\n\n if (text && !this._audioInputSent) {\n this.prompt = this.preRecordingPrompt + text;\n if (this.inputElement) {\n this.inputElement.value = this.prompt;\n this.inputElement.dispatchEvent(new Event('input'));\n }\n }\n });\n\n this.connection.on(\"ReceiveConversationUserMessage\", (sessionId, text) => {\n if (text) {\n this.stopAudio();\n\n // If there's an interrupted assistant message still streaming,\n // mark it as done to stop the spinner animation.\n if (this._conversationAssistantMessage) {\n var oldMsg = this.messages[this._conversationAssistantMessage.index];\n if (oldMsg) {\n oldMsg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Replace the partial transcript message with the final one.\n if (this._conversationPartialMessage) {\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = '

' + escaped + '

';\n this._conversationPartialMessage.isPartial = false;\n this._conversationPartialMessage = null;\n } else {\n this.addMessage({\n role: 'user',\n content: text\n });\n }\n this.scrollToBottom();\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantToken\", (sessionId, messageId, token, responseId, appearance) => {\n if (!this._conversationAssistantMessage) {\n this.stopAudio();\n this.hideTypingIndicator();\n\n // Ensure no stale streaming indicators remain from prior messages.\n for (var j = 0; j < this.messages.length; j++) {\n if (this.messages[j].isStreaming) {\n this.messages[j].isStreaming = false;\n }\n }\n\n var msgIndex = this.messages.length;\n var newMessage = {\n id: messageId,\n role: \"assistant\",\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n userRating: null,\n appearance: this.normalizeAssistantAppearance(appearance),\n };\n this.messages.push(newMessage);\n this._conversationAssistantMessage = { index: msgIndex, content: '' };\n }\n\n this._conversationAssistantMessage.content += token;\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n if (!msg.appearance) {\n msg.appearance = this.normalizeAssistantAppearance(appearance);\n }\n msg.content = this._conversationAssistantMessage.content;\n msg.htmlContent = parseMarkdownContent(msg.content, msg);\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n this.scrollToBottom();\n });\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantComplete\", (sessionId, messageId) => {\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n });\n\n this.connection.on(\"ReceiveAudioChunk\", (sessionId, base64Audio, contentType) => {\n if (base64Audio) {\n const binaryString = atob(base64Audio);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n this.audioChunks.push(bytes);\n }\n });\n\n this.connection.on(\"ReceiveAudioComplete\", (sessionId) => {\n this.playCollectedAudio();\n });\n\n this.connection.on(\"ReceiveNotification\", (notification) => {\n this.receiveNotification(notification);\n });\n\n this.connection.on(\"UpdateNotification\", (notification) => {\n this.updateNotification(notification);\n });\n\n this.connection.on(\"RemoveNotification\", (notificationType) => {\n this.removeNotification(notificationType);\n });\n\n this.connection.onreconnecting(() => {\n console.warn(\"SignalR: reconnecting...\");\n });\n\n this.connection.onreconnected(() => {\n console.info(\"SignalR: reconnected.\");\n\n if (this.isSessionStarted) {\n this.reloadCurrentSession();\n } else if (config.autoCreateSession) {\n this.startNewSession();\n }\n });\n\n this.connection.onclose((error) => {\n if (this.isNavigatingAway) {\n return;\n }\n\n if (error) {\n console.warn(\"SignalR connection closed with error:\", error.message || error);\n }\n });\n\n try {\n await this.connection.start();\n } catch (err) {\n console.error(\"SignalR Connection Error: \", err);\n }\n },\n addMessageInternal(message) {\n if (message.role === 'assistant') {\n message.appearance = this.normalizeAssistantAppearance(message.appearance);\n }\n\n if (message.content && !message.htmlContent) {\n message.htmlContent = parseMarkdownContent(message.content, message);\n }\n this.fireEvent(new CustomEvent(\"addingOpenAIPromotMessage\", { detail: { message: message } }));\n this.messages.push(message);\n\n this.$nextTick(() => {\n this.fireEvent(new CustomEvent(\"addedOpenAIPromotMessage\", { detail: { message: message } }));\n });\n },\n addMessage(message) {\n\n // Ensure userRating is always defined for Vue reactivity.\n if (message.userRating === undefined) {\n message.userRating = null;\n }\n\n if (message.content) {\n let processedContent = message.content.trim();\n message.references = normalizeReferences(message.references);\n\n if (message.references && typeof message.references === \"object\" && Object.keys(message.references).length) {\n\n // Only include references that were actually cited in the response.\n const citedRefs = Object.entries(message.references).filter(([key]) => processedContent.includes(key));\n\n if (citedRefs.length) {\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders.\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processedContent = processedContent.replaceAll(key, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processedContent = processedContent.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n // if we have multiple references, add a comma to ensure we don't concatenate numbers.\n processedContent = processedContent.replaceAll('
', ',');\n\n processedContent += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || key;\n processedContent += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n }\n }\n\n message.content = processedContent;\n message.htmlContent = parseMarkdownContent(processedContent, message);\n }\n\n this.addMessageInternal(message);\n this.hidePlaceholder();\n\n this.$nextTick(() => {\n // Render any pending charts once the DOM is updated\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n addMessages(messages) {\n\n for (let i = 0; i < messages.length; i++) {\n this.addMessageInternal(messages[i]);\n }\n\n this.hidePlaceholder();\n this.$nextTick(() => {\n this.scrollToBottom();\n });\n },\n hidePlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.add('d-none');\n }\n this.isPlaceholderVisible = false;\n },\n showPlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.remove('d-none');\n }\n this.isPlaceholderVisible = true;\n },\n isOrchestratorAvailable() {\n return config.isOrchestratorAvailable !== false;\n },\n applyOrchestratorAvailability() {\n if (this.isOrchestratorAvailable()) {\n return true;\n }\n\n const unavailableMessage = config.orchestratorUnavailableMessage || \"This orchestrator is not currently available.\";\n\n if (this.inputElement) {\n this.inputElement.disabled = true;\n this.inputElement.value = '';\n this.inputElement.placeholder = unavailableMessage;\n }\n\n if (this.buttonElement) {\n this.buttonElement.disabled = true;\n }\n\n if (this.micButtonElement) {\n this.micButtonElement.disabled = true;\n this.micButtonElement.style.display = 'none';\n }\n\n if (this.conversationButtonElement) {\n this.conversationButtonElement.disabled = true;\n this.conversationButtonElement.style.display = 'none';\n }\n\n return false;\n },\n fireEvent(event) {\n document.dispatchEvent(event);\n },\n isIndicator(message) {\n return message.role === 'indicator';\n },\n sendMessage() {\n if (!this.applyOrchestratorAvailability()) {\n return;\n }\n\n let trimmedPrompt = this.prompt.trim();\n\n if (!trimmedPrompt) {\n return;\n }\n\n // Stop any active recording before sending.\n if (this.isRecording) {\n this.stopRecording();\n }\n\n // Prevent stale ReceiveTranscript events from repopulating the prompt.\n this._audioInputSent = true;\n\n // In single-response mode, clear all previous messages.\n if (this.singleResponseMode) {\n this.messages.splice(0, this.messages.length);\n }\n\n this.addMessage({\n role: 'user',\n content: trimmedPrompt\n });\n\n this.streamMessage(this.getProfileId(), trimmedPrompt, null);\n this.inputElement.value = '';\n this.prompt = '';\n },\n startRecording() {\n if (this.isRecording || !this.connection) {\n return;\n }\n\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this.preRecordingPrompt = this.prompt;\n this._audioInputSent = false;\n\n var subject = new signalR.Subject();\n var profileId = this.getProfileId();\n var sessionId = this.getSessionId() || '';\n var pendingChunk = Promise.resolve();\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n subject.next(base64);\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => subject.complete());\n });\n\n var language = navigator.language || document.documentElement.lang || 'en-US';\n this.connection.send(\"SendAudioStream\", profileId, sessionId, subject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n this.updateMicButton();\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n });\n },\n stopRecording() {\n if (!this.isRecording || !this.mediaRecorder) {\n return;\n }\n\n this.mediaRecorder.stop();\n this.isRecording = false;\n this.updateMicButton();\n },\n toggleRecording() {\n if (this.isRecording) {\n this.stopRecording();\n } else {\n this.startRecording();\n }\n },\n updateMicButton() {\n if (!this.micButton) {\n return;\n }\n\n if (this.isRecording) {\n this.micButton.classList.add('stt-recording');\n this.micButton.innerHTML = '';\n } else {\n this.micButton.classList.remove('stt-recording');\n this.micButton.innerHTML = '';\n }\n },\n streamMessage(profileId, trimmedPrompt, sessionProfileId) {\n\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n\n this.streamingStarted();\n this.showTypingIndicator();\n this.autoScroll = true;\n\n var content = '';\n var references = {};\n var lastResponseId = null;\n\n // Get the index after showing typing indicator.\n var messageIndex = this.messages.length;\n var currentSessionId = this.getSessionId();\n\n this.stream = this.connection.stream(\"SendMessage\", profileId, trimmedPrompt, currentSessionId, sessionProfileId)\n .subscribe({\n next: (chunk) => {\n let message = this.messages[messageIndex];\n\n if (!message) {\n\n if (chunk.sessionId && !currentSessionId) {\n this.initializeSession(chunk.sessionId);\n }\n\n this.hideTypingIndicator();\n // Re-assign the index after hiding the typing indicator.\n messageIndex = this.messages.length;\n let newMessage = {\n id: chunk.messageId,\n role: \"assistant\",\n title: chunk.title,\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n userRating: null,\n };\n\n this.messages.push(newMessage);\n\n message = newMessage;\n }\n\n if (chunk.title && (!message.title || message.title !== chunk.title)) {\n message.title = chunk.title;\n }\n\n if (chunk.references && typeof chunk.references === \"object\" && Object.keys(chunk.references).length) {\n\n for (const [key, value] of Object.entries(chunk.references)) {\n references[key] = normalizeReference(value) ?? {};\n }\n }\n\n if (chunk.content) {\n\n // When the responseId changes (e.g., after an internal tool call),\n // insert a line break to visually separate response segments.\n if (chunk.responseId && lastResponseId && chunk.responseId !== lastResponseId) {\n content += '\\n\\n';\n }\n\n if (chunk.responseId) {\n lastResponseId = chunk.responseId;\n }\n\n let processedContent = chunk.content;\n\n for (const [key, value] of Object.entries(references)) {\n processedContent = processedContent.replaceAll(key, `${value.index}`);\n }\n\n // Append processed content to the message.\n // if we have multiple references, add a comma to ensure we don't concatenate numbers.\n content += processedContent.replaceAll('
', ',');\n }\n\n // Update the existing message\n message.content = content;\n\n message.htmlContent = parseMarkdownContent(content, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n complete: () => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n if (!msg || !msg.content) {\n // No content received at all.\n this.hideTypingIndicator();\n }\n\n // Trigger text-to-speech only in conversation mode.\n if (this.isConversationMode && this.textToSpeechEnabled && msg && msg.content) {\n this.synthesizeSpeech(msg.content);\n }\n\n this.stream?.dispose();\n this.stream = null;\n },\n error: (err) => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n this.hideTypingIndicator();\n\n if (!this.isNavigatingAway) {\n this.addMessage(this.getServiceDownMessage());\n }\n\n this.stream?.dispose();\n this.stream = null;\n\n console.error(\"Stream error:\", err);\n }\n });\n },\n getServiceDownMessage() {\n let newMessage = {\n role: \"assistant\",\n content: \"Our service is currently unavailable. Please try again later. We apologize for the inconvenience.\",\n htmlContent: \"\",\n };\n\n return newMessage;\n },\n processReferences(references, messageIndex) {\n references = normalizeReferences(references);\n\n if (Object.keys(references).length) {\n\n let message = this.messages[messageIndex];\n const content = message.content || '';\n\n // Only include references that were actually cited in the response.\n // Check both raw [doc:N] markers and already-rendered tags from streaming.\n const citedRefs = Object.entries(references).filter(([key, value]) =>\n content.includes(key) || content.includes(`${value.index}`)\n );\n\n if (!citedRefs.length) {\n return;\n }\n\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders to avoid collisions during remapping.\n let processed = content.trim();\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processed = processed.replaceAll(key, placeholder);\n processed = processed.replaceAll(`${value.index}`, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processed = processed.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n processed = processed.replaceAll('', ',');\n\n processed += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || key;\n processed += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n\n message.content = processed;\n message.htmlContent = parseMarkdownContent(processed, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n }\n },\n streamingStarted() {\n var stopIcon = this.buttonElement.getAttribute('data-stop-icon');\n\n if (stopIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(stopIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n\n if (this.inputElement) {\n this.inputElement.setAttribute('disabled', 'disabled');\n }\n },\n streamingFinished() {\n var startIcon = this.buttonElement.getAttribute('data-start-icon');\n\n if (startIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(startIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n\n if (this.inputElement) {\n this.inputElement.removeAttribute('disabled');\n this.inputElement.focus();\n }\n\n // Directly manipulate the DOM to stop all streaming animations.\n if (this.chatContainer) {\n var icons = this.chatContainer.querySelectorAll('.ai-streaming-icon');\n for (var i = 0; i < icons.length; i++) {\n icons[i].classList.remove('ai-streaming-icon');\n icons[i].classList.add('ai-bot-icon');\n }\n }\n\n // Also update Vue data for consistency.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n },\n synthesizeSpeech(text, cacheIndex) {\n if (!this.textToSpeechEnabled || !text || !this.connection) {\n return;\n }\n\n this.audioChunks = [];\n this.isPlayingAudio = true;\n this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;\n\n this.connection.invoke(\"SynthesizeSpeech\", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName)\n .catch(err => {\n console.error(\"TTS synthesis error:\", err);\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this._ttsCacheIndex = -1;\n });\n },\n toggleMessageTts(message, index) {\n if (this.ttsPlayingMessageIndex === index) {\n this.stopAudio();\n return;\n }\n\n this.stopAudio();\n this.ttsPlayingMessageIndex = index;\n\n if (this.ttsAudioCache[index]) {\n this.playAudioBlob(this.ttsAudioCache[index]);\n return;\n }\n\n this.synthesizeSpeech(message.content, index);\n },\n playCollectedAudio() {\n if (this.audioChunks.length === 0) {\n if (!this.currentAudioElement && this.audioPlayQueue.length === 0) {\n this.isPlayingAudio = false;\n }\n return;\n }\n\n const totalLength = this.audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of this.audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n this.audioChunks = [];\n\n const blob = new Blob([combined], { type: 'audio/mp3' });\n\n if (this._ttsCacheIndex >= 0) {\n this.ttsAudioCache[this._ttsCacheIndex] = blob;\n this._ttsCacheIndex = -1;\n }\n\n // If audio is already playing, queue this blob for sequential playback.\n if (this.currentAudioElement) {\n this.audioPlayQueue.push(blob);\n return;\n }\n\n this.playAudioBlob(blob);\n },\n playAudioBlob(blob) {\n const url = URL.createObjectURL(blob);\n const audio = new Audio(url);\n\n this.currentAudioElement = audio;\n this.isPlayingAudio = true;\n\n audio.addEventListener('ended', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.addEventListener('error', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.play().catch(err => {\n console.error(\"Audio playback error:\", err);\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.isPlayingAudio = false;\n });\n },\n playNextInQueue() {\n if (this.audioPlayQueue.length > 0) {\n var nextBlob = this.audioPlayQueue.shift();\n this.playAudioBlob(nextBlob);\n } else {\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this.conversationModeOnAudioEnded();\n }\n },\n stopAudio() {\n if (this.currentAudioElement) {\n this.currentAudioElement.pause();\n this.currentAudioElement.currentTime = 0;\n this.currentAudioElement = null;\n }\n this.audioChunks = [];\n this.audioPlayQueue = [];\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n },\n toggleConversationMode() {\n if (this.isConversationMode) {\n this.stopConversationMode();\n } else {\n this.startConversationMode();\n }\n },\n startConversationMode() {\n if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {\n return;\n }\n\n this.isConversationMode = true;\n this.updateConversationButton();\n this._conversationPartialTranscript = '';\n this._conversationAssistantMessage = null;\n this._conversationPartialMessage = null;\n\n // Remove any previous conversation ended notification.\n this.removeNotification('conversation-ended');\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this._conversationSubject = new signalR.Subject();\n this._conversationStream = stream;\n\n // Create an AnalyserNode for volume-based interrupt detection.\n // During TTS playback, detect when the user speaks above\n // the threshold to stop TTS (interrupt). Audio chunks are\n // always forwarded — browser echo cancellation handles\n // speaker echo so the STT stream has no gaps.\n var AudioCtx = window.AudioContext || window.webkitAudioContext;\n if (AudioCtx) {\n this._conversationAudioCtx = new AudioCtx();\n this._conversationAnalyser = this._conversationAudioCtx.createAnalyser();\n this._conversationAnalyser.fftSize = 256;\n var micSource = this._conversationAudioCtx.createMediaStreamSource(stream);\n micSource.connect(this._conversationAnalyser);\n }\n\n var pendingChunk = Promise.resolve();\n var analyser = this._conversationAnalyser;\n var interruptVolumeThreshold = 30;\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n // During TTS playback, check mic volume to detect\n // user interruption (speaking above threshold).\n if (this.isPlayingAudio && analyser) {\n var freqData = new Uint8Array(analyser.frequencyBinCount);\n analyser.getByteFrequencyData(freqData);\n var sum = 0;\n for (var k = 0; k < freqData.length; k++) { sum += freqData[k]; }\n var avg = sum / freqData.length;\n\n if (avg >= interruptVolumeThreshold) {\n // User is speaking — interrupt TTS playback.\n this.stopAudio();\n }\n }\n\n // Always send audio to STT — browser echo cancellation\n // handles speaker echo; continuous audio avoids gaps\n // that increase recognition latency.\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n try {\n this._conversationSubject.next(base64);\n } catch (err) {\n // Subject may have been completed already.\n }\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => {\n try {\n this._conversationSubject.complete();\n } catch (err) {\n // Already completed.\n }\n });\n });\n\n var profileId = this.getProfileId();\n var sessionId = this.getSessionId() || '';\n var language = navigator.language || document.documentElement.lang || 'en-US';\n\n this.connection.send(\"StartConversation\", profileId, sessionId, this._conversationSubject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n this.isConversationMode = false;\n this.updateConversationButton();\n });\n },\n stopConversationMode() {\n if (!this.isConversationMode) {\n return;\n }\n\n this.isConversationMode = false;\n this.updateConversationButton();\n\n // Signal the server to cancel all in-progress STT/TTS streams immediately.\n if (this.connection) {\n this.connection.invoke(\"StopConversation\").catch(function () { });\n }\n\n if (this.isRecording && this.mediaRecorder) {\n this.mediaRecorder.stop();\n this.isRecording = false;\n }\n\n this.stopAudio();\n this._conversationPartialTranscript = '';\n this._conversationPartialMessage = null;\n\n // Clean up the AudioContext used for volume monitoring.\n if (this._conversationAudioCtx) {\n this._conversationAudioCtx.close().catch(function () { });\n this._conversationAudioCtx = null;\n this._conversationAnalyser = null;\n }\n\n // Mark any in-flight assistant message as done to stop the spinner.\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Safety net: clear all lingering streaming indicators.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n\n // Show a \"conversation ended\" notification system message.\n this.receiveNotification({\n type: 'conversation-ended',\n content: 'Conversation ended.',\n icon: 'fa-solid fa-circle-check',\n dismissible: true,\n autoDismissMs: 5000\n });\n },\n updateConversationButton() {\n if (!this.conversationButton) {\n return;\n }\n\n if (this.isConversationMode) {\n this.conversationButton.classList.add('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-dark', 'btn-outline-secondary');\n this.conversationButton.title = this.conversationButton.getAttribute('data-end-title') || 'End Conversation';\n var endHtml = this.conversationButton.getAttribute('data-end-html');\n if (endHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(endHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n } else {\n this.conversationButton.classList.remove('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-secondary');\n this.conversationButton.classList.add('btn-outline-dark');\n this.conversationButton.blur();\n this.conversationButton.title = this.conversationButton.getAttribute('data-start-title') || 'Start Conversation';\n var startHtml = this.conversationButton.getAttribute('data-start-html');\n if (startHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(startHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n }\n },\n conversationModeSendPrompt() {\n // Legacy: only used by AudioInput mode's ReceiveTranscript.\n },\n conversationModeOnAudioEnded() {\n // Legacy: in conversation mode, audio playback continuation\n // is handled by the persistent stream. This method is only\n // called from playCollectedAudio for non-conversation TTS.\n },\n generatePrompt(element) {\n if (!element) {\n console.error('The element paramter is required.');\n\n return;\n }\n\n let templateProfileId = element.getAttribute('data-profile-id');\n let sessionId = this.getSessionId();\n let sessionProfileId = this.getProfileId();\n\n if (!templateProfileId || !sessionId) {\n\n console.error('The given element is missing data-profile-id or the session has not yet started.');\n return;\n }\n\n // streamMessage() already shows the typing indicator.\n this.streamMessage(templateProfileId, null, sessionProfileId);\n },\n createSessionUrl(baseUrl, param, value) {\n\n const fullUrl = baseUrl.toLowerCase().startsWith('http') ? baseUrl : window.location.origin + baseUrl;\n const url = new URL(fullUrl);\n\n url.searchParams.set(param, value);\n\n return url.toString();\n },\n showTypingIndicator() {\n this.addMessage({\n role: 'indicator',\n htmlContent: config.indicatorTemplate\n });\n },\n hideTypingIndicator() {\n const originalLength = this.messages.length;\n this.messages = this.messages.filter(msg => msg.role !== 'indicator');\n const removedCount = originalLength - this.messages.length;\n return removedCount;\n },\n receiveNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n var existingIndex = this.notifications.findIndex(n => n.type === notification.type);\n if (existingIndex >= 0) {\n this.notifications.splice(existingIndex, 1, notification);\n } else {\n this.notifications.push(notification);\n }\n this.scheduleNotificationDismiss(notification);\n this.$nextTick(() => {\n this.scrollToBottom();\n });\n },\n updateNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n var existingIndex = this.notifications.findIndex(n => n.type === notification.type);\n if (existingIndex >= 0) {\n this.notifications.splice(existingIndex, 1, notification);\n this.scheduleNotificationDismiss(notification);\n }\n },\n scheduleNotificationDismiss(notification) {\n if (!notification || !notification.type || !notification.autoDismissMs || notification.autoDismissMs <= 0) {\n return;\n }\n this.notificationDismissTimers[notification.type] = setTimeout(() => {\n this.removeNotification(notification.type);\n }, notification.autoDismissMs);\n },\n clearNotificationDismiss(notificationType) {\n var timerId = this.notificationDismissTimers[notificationType];\n if (!timerId) {\n return;\n }\n clearTimeout(timerId);\n delete this.notificationDismissTimers[notificationType];\n },\n removeNotification(notificationType) {\n this.clearNotificationDismiss(notificationType);\n this.notifications = this.notifications.filter(n => n.type !== notificationType);\n },\n dismissNotification(notificationType) {\n this.removeNotification(notificationType);\n },\n handleNotificationAction(notificationType, actionName) {\n if (!this.connection) {\n return;\n }\n var sessionId = this.getSessionId();\n this.connection.invoke(\"HandleNotificationAction\", sessionId, notificationType, actionName).catch(function (err) {\n console.error(\"Error handling notification action:\", err);\n });\n },\n scrollToBottom() {\n if (!this.autoScroll) {\n return;\n }\n setTimeout(() => {\n this.chatContainer.scrollTop = this.chatContainer.scrollHeight - this.chatContainer.clientHeight;\n }, 50);\n },\n handleUserInput(event) {\n this.prompt = event.target.value;\n },\n getProfileId() {\n return this.inputElement.getAttribute('data-profile-id');\n },\n setSessionId(sessionId) {\n this.inputElement.setAttribute('data-session-id', sessionId || '');\n },\n resetSession() {\n this.stopRecording();\n this.setSessionId('');\n this.isSessionStarted = false;\n this.sessionRating = null;\n if (this.widgetIsInitialized) {\n localStorage.removeItem(this.chatWidgetStateSession);\n }\n this.messages = [];\n this.documents = [];\n if (!config.autoCreateSession) {\n this.showPlaceholder();\n }\n\n if (config.autoCreateSession) {\n this.startNewSession();\n }\n },\n startNewSession() {\n if (!this.applyOrchestratorAvailability()) {\n return;\n }\n\n const profileId = this.getProfileId();\n if (!profileId || !this.connection) {\n return;\n }\n\n this.connection.invoke(\"StartSession\", profileId, null).catch(err => console.error(err));\n },\n initializeApp() {\n this.inputElement = document.querySelector(config.inputElementSelector);\n this.buttonElement = document.querySelector(config.sendButtonElementSelector);\n this.chatContainer = document.querySelector(config.chatContainerElementSelector);\n this.placeholder = document.querySelector(config.placeholderElementSelector);\n this.applyOrchestratorAvailability();\n\n const sessionId = this.getSessionId();\n if (!config.widget && sessionId) {\n this.loadSession(sessionId);\n } else if (this.isOrchestratorAvailable() && config.autoCreateSession && !config.widget && !sessionId) {\n this.startNewSession();\n }\n\n // Initialize document bar if enabled.\n if (config.sessionDocumentsEnabled && config.documentBarSelector) {\n this.documentBar = document.querySelector(config.documentBarSelector);\n if (this.documentBar) {\n this.renderDocumentBar();\n\n // Create hidden file input for document uploads.\n var fileInput = document.createElement('input');\n fileInput.type = 'file';\n fileInput.id = 'ai-chat-doc-input';\n fileInput.className = 'd-none';\n fileInput.multiple = true;\n if (config.allowedExtensions) {\n fileInput.accept = config.allowedExtensions;\n }\n fileInput.addEventListener('change', (e) => this.handleFileInputChange(e));\n this.documentBar.parentElement.appendChild(fileInput);\n\n // Set up drag-and-drop on the input area.\n var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;\n if (inputArea) {\n inputArea.addEventListener('dragover', (e) => this.handleDragOver(e));\n inputArea.addEventListener('dragleave', (e) => this.handleDragLeave(e));\n inputArea.addEventListener('drop', (e) => this.handleDrop(e));\n }\n }\n }\n\n // Pause auto-scroll when the user manually scrolls up during streaming.\n this.chatContainer.addEventListener('scroll', () => {\n if (!this.stream) {\n return;\n }\n const threshold = 30;\n const atBottom = this.chatContainer.scrollHeight - this.chatContainer.clientHeight - this.chatContainer.scrollTop <= threshold;\n this.autoScroll = atBottom;\n });\n\n this.inputElement.addEventListener('keydown', event => {\n\n if (this.stream != null) {\n return;\n }\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n this.buttonElement.click();\n }\n });\n\n this.inputElement.addEventListener('input', (e) => {\n this.handleUserInput(e);\n\n if (e.target.value.trim()) {\n this.buttonElement.removeAttribute('disabled');\n } else {\n this.buttonElement.setAttribute('disabled', true);\n }\n });\n\n this.buttonElement.addEventListener('click', () => {\n\n if (this.stream != null) {\n this.stream.dispose();\n this.stream = null;\n\n this.streamingFinished();\n this.hideTypingIndicator();\n\n // Clean up: remove empty assistant message or stop streaming animation.\n if (this.messages.length > 0) {\n const lastMsg = this.messages[this.messages.length - 1];\n if (lastMsg.role === 'assistant' && !lastMsg.content) {\n this.messages.pop();\n } else if (lastMsg.isStreaming) {\n lastMsg.isStreaming = false;\n }\n }\n\n return;\n }\n\n this.sendMessage();\n });\n\n const promptGenerators = document.getElementsByClassName('profile-generated-prompt');\n\n for (var i = 0; i < promptGenerators.length; i++) {\n promptGenerators[i].addEventListener('click', (e) => {\n e.preventDefault();\n this.generatePrompt(e.target);\n });\n }\n\n const chatSessions = document.getElementsByClassName('chat-session-history-item');\n\n for (var i = 0; i < chatSessions.length; i++) {\n chatSessions[i].addEventListener('click', (e) => {\n e.preventDefault();\n\n var sessionId = e.target.getAttribute('data-session-id');\n\n if (!sessionId) {\n console.error('an element with the class chat-session-history-item with no data-session-id set.');\n\n return;\n }\n\n this.loadSession(sessionId);\n this.showChatScreen();\n });\n }\n\n for (let i = 0; i < config.messages.length; i++) {\n this.addMessage(config.messages[i]);\n }\n\n // Update feedback icons in the DOM after initial messages have rendered.\n this.$nextTick(() => {\n this.refreshAllFeedbackIcons();\n });\n\n // Delegate click for code block copy buttons.\n if (this.chatContainer) {\n this.chatContainer.addEventListener('click', (e) => {\n var btn = e.target.closest('.ai-code-copy-btn');\n if (!btn) {\n return;\n }\n\n var block = btn.closest('.ai-code-block') || btn.closest('pre');\n if (!block) {\n return;\n }\n\n var codeEl = block.querySelector('code');\n if (codeEl) {\n navigator.clipboard.writeText(codeEl.textContent);\n var copiedText = config.codeCopiedText || 'Copied!';\n btn.innerHTML = ' ' + copiedText;\n setTimeout(() => {\n btn.innerHTML = '';\n }, 2000);\n }\n });\n }\n\n // Initialize speech-to-text microphone button.\n if (this.speechToTextEnabled && config.micButtonElementSelector) {\n this.micButton = document.querySelector(config.micButtonElementSelector);\n if (this.micButton) {\n this.micButton.style.display = '';\n this.micButton.addEventListener('click', () => {\n this.toggleRecording();\n });\n }\n }\n\n // Initialize conversation mode button.\n if (this.conversationModeEnabled && config.conversationButtonElementSelector) {\n this.conversationButton = document.querySelector(config.conversationButtonElementSelector);\n if (this.conversationButton) {\n this.conversationButton.addEventListener('click', () => {\n this.toggleConversationMode();\n });\n }\n }\n },\n loadSession(sessionId) {\n this.connection.invoke(\"LoadSession\", sessionId).catch(err => console.error(err));\n },\n reloadCurrentSession() {\n\n var sessionId = this.getSessionId();\n if (sessionId) {\n this.loadSession(sessionId);\n }\n },\n initializeSession(sessionId, force) {\n if (this.isSessionStarted && !force) {\n return\n }\n this.fireEvent(new CustomEvent(\"initializingSessionOpenAIChat\", { detail: { sessionId: sessionId } }))\n this.setSessionId(sessionId);\n this.isSessionStarted = true;\n\n if (this.widgetIsInitialized) {\n localStorage.setItem(this.chatWidgetStateSession, sessionId);\n }\n },\n initializeWidget() {\n\n if (!config.widget.chatWidgetContainer) {\n console.error('The widget chatWidgetContainer is required.');\n return;\n }\n\n if (!config.widget.chatWidgetStateName) {\n console.error('The widget chatWidgetStateName is required.');\n return;\n }\n\n const chatWidgetContainer = document.querySelector(config.widget.chatWidgetContainer);\n\n if (!chatWidgetContainer) {\n return;\n }\n\n if (config.widget.chatHistorySection) {\n this.chatHistorySection = document.querySelector(config.widget.chatHistorySection);\n }\n\n this.chatWidgetStateName = config.widget.chatWidgetStateName;\n this.chatWidgetStateSession = config.widget.chatWidgetStateName + 'Session';\n this.widgetIsInitialized = true;\n\n // Auto-load the last session so the user always sees previous chat history.\n this.reloadCurrentSession();\n\n if (config.autoCreateSession && !this.getSessionId()) {\n this.startNewSession();\n }\n\n if (config.widget.showHistoryButton && this.chatHistorySection) {\n\n const showHistoryButton = document.querySelector(config.widget.showHistoryButton);\n\n if (showHistoryButton) {\n showHistoryButton.addEventListener('click', () => {\n this.chatHistorySection.classList.toggle('show');\n });\n }\n\n if (config.widget.closeHistoryButton) {\n var closeHistoryButton = document.querySelector(config.widget.closeHistoryButton);\n\n if (closeHistoryButton) {\n closeHistoryButton.addEventListener('click', () => {\n this.showChatScreen();\n });\n }\n }\n }\n\n if (config.widget.newChatButton) {\n const newChatButton = document.querySelector(config.widget.newChatButton);\n\n if (newChatButton) {\n newChatButton.addEventListener('click', () => {\n this.resetSession();\n this.showChatScreen();\n });\n }\n }\n },\n showChatScreen() {\n\n if (!this.chatHistorySection) {\n return;\n }\n\n this.chatHistorySection.classList.remove('show');\n },\n getSessionId() {\n let sessionId = this.inputElement.getAttribute('data-session-id');\n\n if (!sessionId && this.widgetIsInitialized) {\n sessionId = localStorage.getItem(this.chatWidgetStateSession);\n }\n\n return sessionId;\n },\n copyResponse(message) {\n navigator.clipboard.writeText(message);\n },\n updateFeedbackIcons(container, userRating) {\n if (!container) {\n return;\n }\n\n var upBtn = container.querySelector('.rate-up-btn');\n var downBtn = container.querySelector('.rate-down-btn');\n\n // Font Awesome SVG+JS replaces with , so we must replace\n // the entire button content and let Font Awesome re-process.\n if (upBtn) {\n var upClass = userRating === true ? 'fa-solid fa-thumbs-up' : 'fa-regular fa-thumbs-up';\n var upIcon = document.createElement('i');\n upIcon.className = upClass;\n upIcon.style.fontSize = '0.9rem';\n upBtn.textContent = '';\n upBtn.appendChild(upIcon);\n }\n\n if (downBtn) {\n var downClass = userRating === false ? 'fa-solid fa-thumbs-down' : 'fa-regular fa-thumbs-down';\n var downIcon = document.createElement('i');\n downIcon.className = downClass;\n downIcon.style.fontSize = '0.9rem';\n downBtn.textContent = '';\n downBtn.appendChild(downIcon);\n }\n\n // Trigger Font Awesome SVG+JS to convert the new elements.\n if (window.FontAwesome && FontAwesome.dom && FontAwesome.dom.i2svg) {\n FontAwesome.dom.i2svg({ node: container });\n }\n },\n refreshAllFeedbackIcons() {\n var containers = this.$el.querySelectorAll('.ai-chat-message-assistant-feedback');\n\n for (var i = 0; i < containers.length; i++) {\n var msgId = containers[i].getAttribute('data-message-id');\n var msg = this.messages.find(m => m.id === msgId);\n\n if (msg) {\n this.updateFeedbackIcons(containers[i], msg.userRating);\n }\n }\n },\n rateMessage(message, isPositive, event) {\n var sessionId = this.getSessionId();\n\n if (!sessionId || !message.id || !this.connection) {\n return;\n }\n\n // Toggle: clicking the same rating again clears it.\n var newRating = message.userRating === isPositive ? null : isPositive;\n message.userRating = newRating;\n\n // Find the feedback container by message ID for reliable DOM targeting.\n var feedbackContainer = this.$el.querySelector(\n '.ai-chat-message-assistant-feedback[data-message-id=\"' + message.id + '\"]'\n );\n\n this.updateFeedbackIcons(feedbackContainer, newRating);\n\n // Trigger spark animation after Font Awesome has re-processed icons.\n if (newRating !== null && feedbackContainer) {\n setTimeout(() => {\n var btnClass = isPositive ? '.rate-up-btn' : '.rate-down-btn';\n var btn = feedbackContainer.querySelector(btnClass);\n\n if (btn) {\n btn.classList.remove('spark-effect');\n void btn.offsetWidth;\n btn.classList.add('spark-effect');\n btn.addEventListener('animationend', function onEnd() {\n btn.removeEventListener('animationend', onEnd);\n btn.classList.remove('spark-effect');\n });\n }\n }, 50);\n }\n\n this.connection.invoke(\"RateMessage\", sessionId, message.id, isPositive).catch(function (err) {\n console.error('Failed to rate message:', err);\n });\n }\n },\n watch: {\n documents: {\n handler() { this.renderDocumentBar(); },\n deep: true\n },\n isUploading() { this.renderDocumentBar(); },\n isPlayingAudio() {\n // Reserved for future use — volume-based interrupt detection\n // no longer mutes tracks; browser echo cancellation handles echo.\n },\n isConversationMode(active) {\n // Hide/show mic button.\n if (this.micButton) {\n this.micButton.style.display = active ? 'none' : (this.speechToTextEnabled ? '' : 'none');\n }\n\n // Hide/show send button.\n if (this.buttonElement) {\n this.buttonElement.style.display = active ? 'none' : '';\n }\n\n // Disable/enable textarea.\n if (this.inputElement) {\n this.inputElement.disabled = active;\n if (active) {\n this.inputElement.placeholder = '';\n }\n }\n }\n },\n mounted() {\n (async () => {\n await this.startConnection();\n this.initializeApp();\n if (config.widget) {\n this.initializeWidget();\n }\n })();\n\n window.addEventListener('beforeunload', this.handleBeforeUnload);\n },\n beforeUnmount() {\n window.removeEventListener('beforeunload', this.handleBeforeUnload);\n\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n if (this.connection) {\n this.connection.stop();\n }\n },\n template: config.messageTemplate\n }).mount(config.appElementSelector);\n\n return app;\n };\n\n return {\n initialize: initialize\n };\n}();\n\n// Download chart as image via event delegation (DOMPurify strips inline onclick).\ndocument.addEventListener('click', function (e) {\n const btn = e.target.closest('.download-chart-btn');\n if (!btn) {\n return;\n }\n const chartId = btn.getAttribute('data-chart-id');\n const canvas = chartId ? document.getElementById(chartId) : null;\n if (!canvas) {\n console.error('Chart canvas not found:', chartId);\n return;\n }\n const link = document.createElement('a');\n link.download = 'chart-' + chartId + '.png';\n link.href = canvas.toDataURL('image/png');\n link.click();\n});\n\n// Intercept download clicks for data-URI images and convert to blob downloads.\ndocument.addEventListener('click', function (e) {\n const link = e.target.closest('.ai-download-image');\n if (!link) {\n return;\n }\n\n const container = link.closest('.generated-image-container');\n const img = container?.querySelector('img');\n if (!img) {\n return;\n }\n\n const src = img.src;\n if (!src || !src.startsWith('data:')) {\n return; // Normal URL – let the default behaviour handle it.\n }\n\n e.preventDefault();\n\n fetch(src)\n .then(function (res) { return res.blob(); })\n .then(function (blob) {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = link.getAttribute('download') || 'generated-image.png';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(function () { URL.revokeObjectURL(url); }, 100);\n })\n .catch(function (err) { console.error('Failed to download image:', err); });\n});\n"]} diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.js index f1b0bb6f..41d375db 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.js @@ -30,7 +30,7 @@ window.chatInteractionManager = function () { downloadChartButtonText: 'Download', codeCopiedText: 'Copied!', assistantLabel: 'Assistant', - messageTemplate: "\n
\n
\n
\n
You
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n
\n ", + messageTemplate: "\n
\n
\n
\n
You
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n
\n ", indicatorTemplate: "\n
\n \n Assistant\n
\n ", // Localizable strings untitledText: 'Untitled', @@ -325,12 +325,14 @@ window.chatInteractionManager = function () { preRecordingPrompt: '', micButton: null, speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation', - textToSpeechEnabled: config.chatMode === 'Conversation', + textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled, ttsVoiceName: config.ttsVoiceName || null, audioChunks: [], audioPlayQueue: [], isPlayingAudio: false, currentAudioElement: null, + ttsPlayingMessageIndex: -1, + ttsAudioCache: {}, conversationModeEnabled: config.chatMode === 'Conversation', conversationButton: null, isConversationMode: false, @@ -1028,18 +1030,34 @@ window.chatInteractionManager = function () { this.debouncedSaveSettings(); } }, - synthesizeSpeech: function synthesizeSpeech(text) { + synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) { var _this7 = this; if (!this.textToSpeechEnabled || !text || !this.connection) { return; } this.audioChunks = []; this.isPlayingAudio = true; + this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1; this.connection.invoke("SynthesizeSpeech", this.getItemId(), text, this.ttsVoiceName)["catch"](function (err) { console.error("TTS synthesis error:", err); _this7.isPlayingAudio = false; + _this7.ttsPlayingMessageIndex = -1; + _this7._ttsCacheIndex = -1; }); }, + toggleMessageTts: function toggleMessageTts(message, index) { + if (this.ttsPlayingMessageIndex === index) { + this.stopAudio(); + return; + } + this.stopAudio(); + this.ttsPlayingMessageIndex = index; + if (this.ttsAudioCache[index]) { + this.playAudioBlob(this.ttsAudioCache[index]); + return; + } + this.synthesizeSpeech(message.content, index); + }, playCollectedAudio: function playCollectedAudio() { if (this.audioChunks.length === 0) { if (!this.isPlayingAudio && this.audioPlayQueue.length === 0) { @@ -1069,6 +1087,10 @@ window.chatInteractionManager = function () { var blob = new Blob([combined], { type: 'audio/mp3' }); + if (this._ttsCacheIndex >= 0) { + this.ttsAudioCache[this._ttsCacheIndex] = blob; + this._ttsCacheIndex = -1; + } // If audio is currently playing, queue this blob for later. if (this.isPlayingAudio && this.currentAudioElement) { @@ -1106,6 +1128,7 @@ window.chatInteractionManager = function () { this.playAudioBlob(nextBlob); } else { this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; this.conversationModeOnAudioEnded(); } }, @@ -1118,6 +1141,7 @@ window.chatInteractionManager = function () { this.audioChunks = []; this.audioPlayQueue = []; this.isPlayingAudio = false; + this.ttsPlayingMessageIndex = -1; }, toggleConversationMode: function toggleConversationMode() { if (this.isConversationMode) { @@ -2092,4 +2116,5 @@ document.addEventListener('click', function (e) { })["catch"](function (err) { console.error('Failed to download image:', err); }); -}); \ No newline at end of file +}); +//# sourceMappingURL=chat-interaction.js.map diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.js.map b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.js.map new file mode 100644 index 00000000..9d76948b --- /dev/null +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["chat-interaction.js"],"names":["e","t","r","Symbol","n","iterator","o","toStringTag","i","c","prototype","Generator","u","Object","create","_regeneratorDefine2","f","p","y","G","v","a","d","bind","length","l","TypeError","call","done","value","GeneratorFunction","GeneratorFunctionPrototype","getPrototypeOf","setPrototypeOf","__proto__","displayName","_regenerator","w","m","defineProperty","_regeneratorDefine","_invoke","enumerable","configurable","writable","asyncGeneratorStep","Promise","resolve","then","_asyncToGenerator","arguments","apply","_next","_throw","_createForOfIteratorHelper","Array","isArray","_unsupportedIterableToArray","_n","F","s","next","_toConsumableArray","_arrayWithoutHoles","_iterableToArray","_nonIterableSpread","_arrayLikeToArray","toString","slice","constructor","name","from","test","window","chatInteractionManager","defaultConfig","generatedImageAltText","generatedImageMaxWidth","downloadImageTitle","downloadChartTitle","downloadChartButtonText","codeCopiedText","assistantLabel","messageTemplate","indicatorTemplate","untitledText","clearHistoryTitle","clearHistoryMessage","clearHistoryOkText","clearHistoryCancelText","sanitizeUrl","url","trimmed","trim","escapeHtmlEntities","text","span","document","createElement","textContent","innerHTML","renderer","marked","Renderer","link","data","href","concat","code","lang","highlighted","hljs","getLanguage","highlight","language","_","highlightAuto","langDisplay","image","src","alt","maxWidth","chartCounter","_pendingCharts","__chartConfigs","createChartHtml","chartId","use","extensions","level","start","idx","indexOf","undefined","tokenizer","extracted","tryExtractChartMarker","startIndex","type","raw","substring","endIndex","json","token","push","config","jsonStart","depth","inString","escape","ch","jsonEnd","closeBracketIndex","renderChartsInMessage","message","charts","requestAnimationFrame","_iterator","_step","canvas","getElementById","Chart","console","warn","offsetParent","_cfg$options","_cfg$options2","_cfg$options2$aspectR","_chartInstance","destroy","cfg","JSON","parse","options","responsive","maintainAspectRatio","aspectRatio","error","err","parseMarkdownContent","content","html","DOMPurify","sanitize","ADD_TAGS","ADD_ATTR","initialize","instanceConfig","assign","signalRHubUrl","appElementSelector","chatContainerElementSelector","inputElementSelector","sendButtonElementSelector","app","Vue","createApp","inputElement","buttonElement","chatContainer","placeholder","isInteractionStarted","isPlaceholderVisible","isStreaming","isNavigatingAway","autoScroll","stream","messages","prompt","initialFieldValues","Map","settingsDirty","saveSettingsTimeout","saveIndicatorTimeout","isRecording","mediaRecorder","preRecordingPrompt","micButton","speechToTextEnabled","chatMode","textToSpeechEnabled","ttsVoiceName","audioChunks","audioPlayQueue","isPlayingAudio","currentAudioElement","ttsPlayingMessageIndex","ttsAudioCache","conversationModeEnabled","conversationButton","isConversationMode","notifications","notificationDismissTimers","computed","lastAssistantIndex","role","methods","handleBeforeUnload","startConnection","_this","_callee","_t","_context","connection","signalR","HubConnectionBuilder","withUrl","withAutomaticReconnect","build","serverTimeoutInMilliseconds","keepAliveIntervalInMilliseconds","on","_data$messages","initializeInteraction","itemId","titleInput","querySelector","title","forEach","msg","addMessage","$nextTick","historyItem","showSaveIndicator","stopRecording","isFinal","_conversationPartialTranscript","escaped","replace","_conversationPartialMessage","hidePlaceholder","htmlContent","isPartial","scrollToBottom","_audioInputSent","dispatchEvent","Event","stopAudio","_conversationAssistantMessage","oldMsg","index","messageId","responseId","appearance","hideTypingIndicator","j","msgIndex","newMessage","id","normalizeAssistantAppearance","base64Audio","contentType","binaryString","atob","bytes","Uint8Array","charCodeAt","playCollectedAudio","showPlaceholder","clearHistoryBtn","classList","add","notification","receiveNotification","updateNotification","notificationType","removeNotification","onreconnecting","onreconnected","info","reloadCurrentInteraction","onclose","addMessageInternal","_this2","fireEvent","CustomEvent","detail","_this3","processedContent","references","_typeof","keys","citedRefs","entries","filter","_ref","_ref2","_slicedToArray","key","includes","sort","_ref3","_ref4","_ref5","_ref6","b","displayIndex","_iterator2","_step2","_step2$value","replaceAll","_displayIndex","_placeholder","_iterator3","_step3","_step3$value","_iterator4","_step4","_step4$value","label","remove","event","icon","cssClass","disableStreamingAnimation","getAssistantLabel","getAssistantRoleClasses","classes","getAssistantIconClasses","shouldAnimateAssistantIcon","getAssistantIcon","isIndicator","sendMessage","_this4","_callee2","trimmedPrompt","_context2","flushPendingSave","streamMessage","_this5","dispose","streamingStarted","showTypingIndicator","lastResponseId","messageIndex","currentItemId","getItemId","subscribe","chunk","sessionId","setItemId","_i","_Object$entries","_Object$entries$_i","_i2","_Object$entries2","_Object$entries2$_i","complete","_this5$stream","processReferences","streamingFinished","synthesizeSpeech","_this5$stream2","getServiceDownMessage","_this6","_ref7","_ref8","_ref9","_ref0","_ref1","_ref10","processed","_iterator5","_step5","_step5$value","_iterator6","_step6","_step6$value","_iterator7","_step7","_step7$value","stopIcon","getAttribute","replaceChildren","RETURN_DOM_FRAGMENT","startIcon","icons","querySelectorAll","debouncedSaveSettings","cacheIndex","_this7","_ttsCacheIndex","invoke","toggleMessageTts","playAudioBlob","totalLength","reduce","sum","combined","offset","_iterator8","_step8","set","blob","Blob","_this8","URL","createObjectURL","audio","Audio","addEventListener","revokeObjectURL","playNextInQueue","play","nextBlob","shift","conversationModeOnAudioEnded","pause","currentTime","toggleConversationMode","stopConversationMode","startConversationMode","_this9","updateConversationButton","navigator","mediaDevices","getUserMedia","echoCancellation","noiseSuppression","autoGainControl","mimeType","MediaRecorder","isTypeSupported","audioBitsPerSecond","_conversationSubject","Subject","_conversationStream","AudioCtx","AudioContext","webkitAudioContext","_conversationAudioCtx","_conversationAnalyser","createAnalyser","fftSize","micSource","createMediaStreamSource","connect","pendingChunk","analyser","interruptVolumeThreshold","size","freqData","frequencyBinCount","getByteFrequencyData","k","avg","_callee3","uint8Array","base64","_context3","arrayBuffer","str","byte","String","fromCharCode","btoa","getTracks","track","stop","documentElement","send","close","dismissible","autoDismissMs","endHtml","blur","startHtml","conversationModeSendPrompt","originalLength","removedCount","_this0","setTimeout","scrollTop","scrollHeight","clientHeight","handleUserInput","target","getSaveIndicatorElement","saveIndicatorElementSelector","className","_this1","indicator","clearTimeout","clearPendingSettingsSave","getFieldLabel","input","_input$closest","closest","dataset","setting","getValidationFeedbackElement","feedback","nextElementSibling","contains","insertAdjacentElement","clearSettingValidationError","removeAttribute","setSettingValidationError","setAttribute","getSettingValidationMessage","disabled","number","Number","fieldLabel","isNaN","min","max","validateSettingInput","validateSettings","_this10","isValid","getSettingInputs","queueSettingsSave","explicitInputs","getSelectedGroupValues","groupName","fallbackSelector","explicitSelections","values","checkbox","checkboxes","baseName","hiddenInput","getPromptTemplateSelections","promptTemplates","card","templateIdInput","promptParametersInput","templateId","promptParameters","clearNotificationDismiss","findIndex","splice","scheduleNotificationDismiss","_this11","timerId","dismissNotification","handleNotificationAction","actionName","resetInteraction","initializeApp","_this12","placeholderElementSelector","loadInteraction","threshold","atBottom","shiftKey","click","lastMsg","pop","chatInteractionItems","getElementsByClassName","preventDefault","btn","block","codeEl","clipboard","writeText","copiedText","matches","clearHistory","micButtonElementSelector","style","display","toggleRecording","conversationButtonElementSelector","self","confirmDialog","okText","cancelText","callback","confirmed","_this13","saveSettings","getSelectedToolNames","getSelectedMcpConnectionIds","getSelectedA2AConnectionIds","getSelectedAgentNames","_this14","settings","inputs","match","toLowerCase","checked","parseFloat","toolNames","mcpConnectionIds","a2aConnectionIds","agentNames","promptTemplateIds","map","template","force","copyResponse","startRecording","_this15","subject","_callee4","_context4","updateMicButton","watch","active","mounted","_this16","_callee5","_context5","beforeUnmount","removeEventListener","mount","download","toDataURL","container","img","startsWith","fetch","res","body","appendChild","removeChild"],"mappings":"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","file":"chat-interaction.js","sourcesContent":["window.chatInteractionManager = function () {\n\n // Defaults (can be overridden by instanceConfig)\n var defaultConfig = {\n // UI defaults for generated media\n generatedImageAltText: 'Generated Image',\n generatedImageMaxWidth: 400,\n downloadImageTitle: 'Download image',\n downloadChartTitle: 'Download chart as image',\n downloadChartButtonText: 'Download',\n codeCopiedText: 'Copied!',\n assistantLabel: 'Assistant',\n\n messageTemplate: `\n
\n
\n
\n
You
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n
\n `,\n indicatorTemplate: `\n
\n \n Assistant\n
\n `,\n // Localizable strings\n untitledText: 'Untitled',\n clearHistoryTitle: 'Clear History',\n clearHistoryMessage: 'Are you sure you want to clear the chat history? This action cannot be undone. Your documents, parameters, and tools will be preserved.',\n clearHistoryOkText: 'Yes',\n clearHistoryCancelText: 'Cancel'\n };\n\n // Sanitize URLs to prevent javascript: protocol injection.\n function sanitizeUrl(url) {\n if (!url) return '';\n var trimmed = url.trim();\n if (/^javascript:/i.test(trimmed) || /^vbscript:/i.test(trimmed) || /^data:text\\/html/i.test(trimmed)) {\n return '';\n }\n return url;\n }\n\n // Safely HTML-encode a string using the DOM (avoids regex-based HTML filtering).\n function escapeHtmlEntities(text) {\n var span = document.createElement('span');\n span.textContent = text;\n return span.innerHTML;\n }\n\n const renderer = new marked.Renderer();\n\n // Modify the link rendering to open in a new tab\n renderer.link = function (data) {\n var href = sanitizeUrl(data.href);\n if (!href) return data.text || '';\n return `
${data.text}`;\n };\n\n // Custom code block renderer with highlight.js integration and copy button.\n renderer.code = function (data) {\n var code = data.text || '';\n var lang = (data.lang || '').trim();\n var highlighted = code;\n\n if (typeof hljs !== 'undefined') {\n if (lang && hljs.getLanguage(lang)) {\n try {\n highlighted = hljs.highlight(code, { language: lang }).value;\n } catch (_) { }\n } else {\n try {\n highlighted = hljs.highlightAuto(code).value;\n } catch (_) { }\n }\n } else {\n highlighted = escapeHtmlEntities(code);\n }\n\n var langDisplay = lang ? escapeHtmlEntities(lang) : 'code';\n return `
${langDisplay}
${highlighted}
`;\n };\n\n // Custom image renderer for generated images with thumbnail styling and download button.\n // Handles both URL and data-URI sources (data URIs are converted to blobs for download).\n renderer.image = function (data) {\n const src = sanitizeUrl(data.href);\n if (!src) return '';\n const alt = data.text || defaultConfig.generatedImageAltText;\n const maxWidth = defaultConfig.generatedImageMaxWidth;\n return `
\n \"${alt}\"\n
\n \n \n \n
\n
`;\n };\n\n // Chart counter for unique IDs\n let chartCounter = 0;\n\n // Collector for charts discovered during marked parsing.\n let _pendingCharts = [];\n\n // Global chart config map shared with ai-chat.js\n window.__chartConfigs = window.__chartConfigs || {};\n\n function createChartHtml(chartId) {\n return `
`\n + ``\n + `
`\n + `
`\n + ``\n + `
`;\n }\n\n // Register [chart:{...json...}] as a native marked block extension so the\n // markdown parser handles chart markers inline with surrounding text.\n marked.use({\n extensions: [{\n name: 'chart',\n level: 'block',\n start(src) {\n const idx = src.indexOf('[chart:');\n return idx >= 0 ? idx : undefined;\n },\n tokenizer(src) {\n const extracted = tryExtractChartMarker(src);\n if (!extracted || extracted.startIndex !== 0) {\n return undefined;\n }\n\n const chartId = `chat_chart_${++chartCounter}`;\n\n return {\n type: 'chart',\n raw: src.substring(0, extracted.endIndex),\n chartId: chartId,\n json: extracted.json,\n };\n },\n renderer(token) {\n _pendingCharts.push({ chartId: token.chartId, config: token.json });\n window.__chartConfigs[token.chartId] = token.json;\n return createChartHtml(token.chartId);\n }\n }]\n });\n\n // Extract a [chart:{...json...}] marker. This avoids regex issues with nested brackets.\n function tryExtractChartMarker(text) {\n const token = '[chart:';\n const start = text.indexOf(token);\n if (start < 0) {\n return null;\n }\n\n // Find JSON object boundary by balancing braces\n const jsonStart = start + token.length;\n let i = jsonStart;\n while (i < text.length && (text[i] === ' ' || text[i] === '\\n' || text[i] === '\\r' || text[i] === '\\t')) {\n i++;\n }\n\n if (i >= text.length || text[i] !== '{') {\n return null;\n }\n\n let depth = 0;\n let inString = false;\n let escape = false;\n\n for (; i < text.length; i++) {\n const ch = text[i];\n\n if (inString) {\n if (escape) {\n escape = false;\n continue;\n }\n if (ch === '\\\\') {\n escape = true;\n continue;\n }\n if (ch === '\"') {\n inString = false;\n }\n continue;\n }\n\n if (ch === '\"') {\n inString = true;\n continue;\n }\n\n if (ch === '{') {\n depth++;\n } else if (ch === '}') {\n depth--;\n if (depth === 0) {\n const jsonEnd = i;\n // Expect closing bracket after JSON\n const closeBracketIndex = text.indexOf(']', jsonEnd + 1);\n if (closeBracketIndex < 0) {\n return null;\n }\n\n const json = text.substring(jsonStart, jsonEnd + 1).trim();\n return {\n startIndex: start,\n endIndex: closeBracketIndex + 1,\n json: json\n };\n }\n }\n }\n\n return null;\n }\n\n function renderChartsInMessage(message) {\n if (!message || !message._pendingCharts || !message._pendingCharts.length) {\n return;\n }\n\n // Copy and clear pending charts immediately to prevent duplicate renders.\n const charts = [...message._pendingCharts];\n message._pendingCharts = [];\n\n // Defer to requestAnimationFrame so the browser has fully laid out the\n // canvas elements before Chart.js reads their dimensions.\n requestAnimationFrame(() => {\n for (const c of charts) {\n const canvas = document.getElementById(c.chartId);\n if (!canvas) {\n continue;\n }\n\n if (typeof Chart === 'undefined') {\n console.warn('Chart.js is not loaded. To render interactive charts, include the Chart.js library on the page (e.g., ).');\n continue;\n }\n\n // When the canvas is inside a hidden container (e.g., display:none),\n // it has zero dimensions. Keep the config for later rendering.\n if (canvas.offsetParent === null) {\n window.__chartConfigs[c.chartId] = c.config;\n continue;\n }\n\n try {\n // Destroy existing chart instance if re-rendering\n if (canvas._chartInstance) {\n canvas._chartInstance.destroy();\n }\n\n const cfg = typeof c.config === 'string' ? JSON.parse(c.config) : c.config;\n cfg.options ??= {};\n cfg.options.responsive = true;\n cfg.options.maintainAspectRatio = true;\n cfg.options.aspectRatio ??= 4 / 3;\n\n canvas._chartInstance = new Chart(canvas, cfg);\n } catch (e) {\n console.error('Error creating chart:', e);\n }\n }\n });\n }\n\n // Parse markdown content via marked (which natively handles [chart:...] markers\n // through the registered extension) and collect pending chart configs for later\n // Chart.js rendering.\n function parseMarkdownContent(content, message) {\n _pendingCharts = [];\n const html = marked.parse(content, { renderer });\n message._pendingCharts = _pendingCharts.length > 0 ? [..._pendingCharts] : [];\n return DOMPurify.sanitize(html, { ADD_TAGS: ['canvas'], ADD_ATTR: ['target'] });\n }\n\n const initialize = (instanceConfig) => {\n\n const config = Object.assign({}, defaultConfig, instanceConfig);\n // Keep defaultConfig in sync so renderers use overridden values\n defaultConfig = config;\n\n if (!config.signalRHubUrl) {\n console.error('The signalRHubUrl is required.');\n return;\n }\n\n if (!config.appElementSelector) {\n console.error('The appElementSelector is required.');\n return;\n }\n\n if (!config.chatContainerElementSelector) {\n console.error('The chatContainerElementSelector is required.');\n return;\n }\n\n if (!config.inputElementSelector) {\n console.error('The inputElementSelector is required.');\n return;\n }\n\n if (!config.sendButtonElementSelector) {\n console.error('The sendButtonElementSelector is required.');\n return;\n }\n\n const app = Vue.createApp({\n data() {\n return {\n inputElement: null,\n buttonElement: null,\n chatContainer: null,\n placeholder: null,\n isInteractionStarted: false,\n isPlaceholderVisible: true,\n isStreaming: false,\n isNavigatingAway: false,\n autoScroll: true,\n stream: null,\n messages: [],\n prompt: '',\n initialFieldValues: new Map(),\n settingsDirty: false,\n saveSettingsTimeout: null,\n saveIndicatorTimeout: null,\n isRecording: false,\n mediaRecorder: null,\n preRecordingPrompt: '',\n micButton: null,\n speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation',\n textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled,\n ttsVoiceName: config.ttsVoiceName || null,\n audioChunks: [],\n audioPlayQueue: [],\n isPlayingAudio: false,\n currentAudioElement: null,\n ttsPlayingMessageIndex: -1,\n ttsAudioCache: {},\n conversationModeEnabled: config.chatMode === 'Conversation',\n conversationButton: null,\n isConversationMode: false,\n notifications: [],\n notificationDismissTimers: {},\n };\n },\n computed: {\n lastAssistantIndex() {\n for (var i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n }\n },\n methods: {\n handleBeforeUnload() {\n this.isNavigatingAway = true;\n },\n async startConnection() {\n this.connection = new signalR.HubConnectionBuilder()\n .withUrl(config.signalRHubUrl)\n .withAutomaticReconnect()\n .build();\n\n // Allow long-running operations (e.g., multi-step MCP tool calls)\n // without the client disconnecting prematurely.\n this.connection.serverTimeoutInMilliseconds = 600000;\n this.connection.keepAliveIntervalInMilliseconds = 15000;\n\n this.connection.on(\"LoadInteraction\", (data) => {\n this.initializeInteraction(data.itemId, true);\n this.messages = [];// Update the title field if it exists\n const titleInput = document.querySelector('[data-chat-interaction-title], .setting-input[data-setting=\"title\"], input[name=\"ChatInteraction.Title\"], input[name=\"Title\"]');\n if (titleInput && data.title) {\n titleInput.value = data.title;\n }\n\n (data.messages ?? []).forEach(msg => {\n this.addMessage(msg);\n\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n });\n });\n });\n\n this.connection.on(\"SettingsSaved\", (itemId, title) => {\n // Update the history list item if it exists\n // Use a more specific selector to only target history list items, not other elements like the Clear History button\n const historyItem = document.querySelector(`.chat-interaction-history-item[data-interaction-id=\"${itemId}\"]`);\n if (historyItem) {\n historyItem.textContent = title || config.untitledText;\n }\n\n const titleInput = document.querySelector('[data-chat-interaction-title], .setting-input[data-setting=\"title\"], input[name=\"ChatInteraction.Title\"], input[name=\"Title\"]');\n if (titleInput && title) {\n titleInput.value = title;\n }\n\n this.showSaveIndicator('Saved', 'text-success');\n });\n\n this.connection.on(\"ReceiveError\", (error) => {\n console.error(\"SignalR Error: \", error);\n this.showSaveIndicator('Save failed', 'text-danger');\n\n if (this.isRecording) {\n this.stopRecording();\n }\n });\n\n this.connection.on(\"ReceiveTranscript\", (itemId, text, isFinal) => {\n if (this.isConversationMode) {\n if (!isFinal && text) {\n this._conversationPartialTranscript = text;\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n var html = '

' + escaped + '

';\n\n // Show partial transcript as a live user message.\n if (!this._conversationPartialMessage) {\n this.hidePlaceholder();\n this._conversationPartialMessage = {\n role: 'user',\n content: text,\n htmlContent: html,\n isPartial: true\n };\n this.messages.push(this._conversationPartialMessage);\n } else {\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = html;\n }\n this.scrollToBottom();\n }\n return;\n }\n\n if (text && !this._audioInputSent) {\n this.prompt = this.preRecordingPrompt + text;\n if (this.inputElement) {\n this.inputElement.value = this.prompt;\n this.inputElement.dispatchEvent(new Event('input'));\n }\n }\n });\n\n this.connection.on(\"ReceiveConversationUserMessage\", (itemId, text) => {\n if (text) {\n this.stopAudio();\n\n // If there's an interrupted assistant message still streaming,\n // mark it as done to stop the spinner animation.\n if (this._conversationAssistantMessage) {\n var oldMsg = this.messages[this._conversationAssistantMessage.index];\n if (oldMsg) {\n oldMsg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Replace the partial transcript message with the final one.\n if (this._conversationPartialMessage) {\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = '

' + escaped + '

';\n this._conversationPartialMessage.isPartial = false;\n this._conversationPartialMessage = null;\n } else {\n this.addMessage({\n role: 'user',\n content: text\n });\n }\n this.scrollToBottom();\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantToken\", (itemId, messageId, token, responseId, appearance) => {\n if (!this._conversationAssistantMessage) {\n this.stopAudio();\n this.hideTypingIndicator();\n\n // Ensure no stale streaming indicators remain from prior messages.\n for (var j = 0; j < this.messages.length; j++) {\n if (this.messages[j].isStreaming) {\n this.messages[j].isStreaming = false;\n }\n }\n\n var msgIndex = this.messages.length;\n var newMessage = {\n id: messageId,\n role: \"assistant\",\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n appearance: this.normalizeAssistantAppearance(appearance),\n };\n this.messages.push(newMessage);\n this._conversationAssistantMessage = { index: msgIndex, content: '' };\n }\n\n this._conversationAssistantMessage.content += token;\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n if (!msg.appearance) {\n msg.appearance = this.normalizeAssistantAppearance(appearance);\n }\n msg.content = this._conversationAssistantMessage.content;\n msg.htmlContent = parseMarkdownContent(msg.content, msg);\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n this.scrollToBottom();\n });\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantComplete\", (itemId, messageId) => {\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n });\n\n this.connection.on(\"ReceiveAudioChunk\", (itemId, base64Audio, contentType) => {\n if (base64Audio) {\n const binaryString = atob(base64Audio);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n this.audioChunks.push(bytes);\n }\n });\n\n this.connection.on(\"ReceiveAudioComplete\", (itemId) => {\n this.playCollectedAudio();\n });\n\n this.connection.on(\"HistoryCleared\", (itemId) => {\n // Clear messages and show placeholder\n this.messages = [];\n this.showPlaceholder();\n\n // Hide the clear history button since there's no history now\n const clearHistoryBtn = document.getElementById('clearHistoryBtn');\n if (clearHistoryBtn) {\n clearHistoryBtn.classList.add('d-none');\n }\n });\n\n this.connection.on(\"ReceiveNotification\", (notification) => {\n this.receiveNotification(notification);\n });\n\n this.connection.on(\"UpdateNotification\", (notification) => {\n this.updateNotification(notification);\n });\n\n this.connection.on(\"RemoveNotification\", (notificationType) => {\n this.removeNotification(notificationType);\n });\n\n this.connection.onreconnecting(() => {\n console.warn(\"SignalR: reconnecting...\");\n });\n\n this.connection.onreconnected(() => {\n console.info(\"SignalR: reconnected.\");\n this.reloadCurrentInteraction();\n });\n\n this.connection.onclose((error) => {\n if (this.isNavigatingAway) {\n return;\n }\n\n if (error) {\n console.warn(\"SignalR connection closed with error:\", error.message || error);\n }\n });\n\n try {\n await this.connection.start();\n } catch (err) {\n console.error(\"SignalR Connection Error: \", err);\n }\n },\n addMessageInternal(message) {\n if (message.role === 'assistant') {\n message.appearance = this.normalizeAssistantAppearance(message.appearance);\n }\n\n if (message.content && !message.htmlContent) {\n message.htmlContent = parseMarkdownContent(message.content, message);\n }\n this.fireEvent(new CustomEvent(\"addingChatInteractionMessage\", { detail: { message: message } }));\n this.messages.push(message);\n\n this.$nextTick(() => {\n this.fireEvent(new CustomEvent(\"addedChatInteractionMessage\", { detail: { message: message } }));\n });\n },\n addMessage(message) {\n if (message.content) {\n let processedContent = message.content.trim();\n\n if (message.references && typeof message.references === \"object\" && Object.keys(message.references).length) {\n\n // Only include references that were actually cited in the response.\n const citedRefs = Object.entries(message.references).filter(([key]) => processedContent.includes(key));\n\n if (citedRefs.length) {\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders.\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processedContent = processedContent.replaceAll(key, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processedContent = processedContent.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n processedContent = processedContent.replaceAll('
', ',');\n\n processedContent += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || `[doc:${value.index}]`;\n processedContent += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n }\n }\n\n message.content = processedContent;\n message.htmlContent = parseMarkdownContent(processedContent, message);\n }\n\n this.addMessageInternal(message);\n this.hidePlaceholder();\n\n this.$nextTick(() => {\n // Render any pending charts once the DOM is updated\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n hidePlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.add('d-none');\n }\n this.isPlaceholderVisible = false;\n },\n showPlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.remove('d-none');\n }\n this.isPlaceholderVisible = true;\n },\n fireEvent(event) {\n document.dispatchEvent(event);\n },\n normalizeAssistantAppearance(appearance) {\n if (!appearance) {\n return null;\n }\n\n var label = typeof appearance.label === 'string' ? appearance.label.trim() : '';\n var icon = typeof appearance.icon === 'string' ? appearance.icon.trim() : '';\n var cssClass = typeof appearance.cssClass === 'string' ? appearance.cssClass.trim() : '';\n var disableStreamingAnimation = !!appearance.disableStreamingAnimation;\n\n if (!label && !icon && !cssClass && !disableStreamingAnimation) {\n return null;\n }\n\n return {\n label: label,\n icon: icon,\n cssClass: cssClass,\n disableStreamingAnimation: disableStreamingAnimation,\n };\n },\n getAssistantLabel(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.label ? appearance.label : defaultConfig.assistantLabel;\n },\n getAssistantRoleClasses(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n var classes = ['ai-chat-msg-role'];\n\n if (appearance && appearance.cssClass) {\n classes.push(appearance.cssClass);\n } else {\n classes.push('ai-chat-msg-role-assistant');\n }\n\n return classes;\n },\n getAssistantIconClasses(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return [this.shouldAnimateAssistantIcon(message, index) ? 'ai-streaming-icon' : 'ai-bot-icon', appearance && appearance.cssClass ? appearance.cssClass : ''];\n },\n getAssistantIcon(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.icon ? appearance.icon : 'fa fa-robot';\n },\n shouldAnimateAssistantIcon(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return !!message &&\n message.isStreaming &&\n index === this.lastAssistantIndex &&\n !(appearance && appearance.disableStreamingAnimation);\n },\n isIndicator(message) {\n return message.role === 'indicator';\n },\n async sendMessage() {\n let trimmedPrompt = this.prompt.trim();\n\n if (!trimmedPrompt) {\n return;\n }\n\n // Stop any active recording before sending.\n if (this.isRecording) {\n this.stopRecording();\n }\n\n // Prevent stale ReceiveTranscript events from repopulating the prompt.\n this._audioInputSent = true;\n\n // Flush any pending settings save before sending a message\n // to prevent concurrent hub calls that can cause database deadlocks.\n await this.flushPendingSave();\n\n this.addMessage({\n role: 'user',\n content: trimmedPrompt\n });\n\n // Show the clear history button since we now have prompts\n const clearHistoryBtn = document.getElementById('clearHistoryBtn');\n if (clearHistoryBtn) {\n clearHistoryBtn.classList.remove('d-none');\n }\n\n this.streamMessage(trimmedPrompt);\n this.inputElement.value = '';\n this.prompt = '';\n },\n streamMessage(trimmedPrompt) {\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n\n this.streamingStarted();\n this.showTypingIndicator();\n this.autoScroll = true;\n\n var content = '';\n var references = {};\n var lastResponseId = null;\n\n var messageIndex = this.messages.length;\n var currentItemId = this.getItemId();\n\n this.stream = this.connection.stream(\"SendMessage\", currentItemId, trimmedPrompt)\n .subscribe({\n next: (chunk) => {\n let message = this.messages[messageIndex];\n\n if (!message) {\n if (chunk.sessionId && !currentItemId) {\n this.setItemId(chunk.sessionId);\n }\n\n this.hideTypingIndicator();\n messageIndex = this.messages.length;\n let newMessage = {\n role: \"assistant\",\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n };\n\n this.messages.push(newMessage);\n message = newMessage;\n }\n\n if (chunk.references && typeof chunk.references === \"object\" && Object.keys(chunk.references).length) {\n for (const [key, value] of Object.entries(chunk.references)) {\n references[key] = value;\n }\n }\n\n if (chunk.content) {\n // When the responseId changes (e.g., after an internal tool call),\n // insert a line break to visually separate response segments.\n if (chunk.responseId && lastResponseId && chunk.responseId !== lastResponseId) {\n content += '\\n\\n';\n }\n\n if (chunk.responseId) {\n lastResponseId = chunk.responseId;\n }\n\n let processedContent = chunk.content;\n\n for (const [key, value] of Object.entries(references)) {\n processedContent = processedContent.replaceAll(key, `${value.index}`);\n }\n\n content += processedContent.replaceAll('
', ',');\n }\n\n message.content = content;\n\n message.htmlContent = parseMarkdownContent(content, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n complete: () => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n if (!msg || !msg.content) {\n // No content received at all.\n this.hideTypingIndicator();\n }\n\n // Trigger text-to-speech only in conversation mode.\n if (this.isConversationMode && this.textToSpeechEnabled && msg && msg.content) {\n this.synthesizeSpeech(msg.content);\n }\n\n this.stream?.dispose();\n this.stream = null;\n },\n error: (err) => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n this.hideTypingIndicator();\n\n if (!this.isNavigatingAway) {\n this.addMessage(this.getServiceDownMessage());\n }\n\n this.stream?.dispose();\n this.stream = null;\n\n console.error(\"Stream error:\", err);\n }\n });\n },\n getServiceDownMessage() {\n return {\n role: \"assistant\",\n content: \"Our service is currently unavailable. Please try again later. We apologize for the inconvenience.\",\n htmlContent: \"\",\n };\n },\n processReferences(references, messageIndex) {\n if (Object.keys(references).length) {\n let message = this.messages[messageIndex];\n const content = message.content || '';\n\n // Only include references that were actually cited in the response.\n // Check both raw [doc:N] markers and already-rendered tags from streaming.\n const citedRefs = Object.entries(references).filter(([key, value]) =>\n content.includes(key) || content.includes(`${value.index}`)\n );\n\n if (!citedRefs.length) {\n return;\n }\n\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders to avoid collisions during remapping.\n let processed = content.trim();\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processed = processed.replaceAll(key, placeholder);\n processed = processed.replaceAll(`${value.index}`, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processed = processed.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n processed = processed.replaceAll('', ',');\n\n processed += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || `[doc:${value.index}]`;\n processed += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n\n message.content = processed;\n message.htmlContent = parseMarkdownContent(processed, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n }\n },\n streamingStarted() {\n var stopIcon = this.buttonElement.getAttribute('data-stop-icon');\n\n if (stopIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(stopIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n },\n streamingFinished() {\n var startIcon = this.buttonElement.getAttribute('data-start-icon');\n\n if (startIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(startIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n\n // Directly manipulate the DOM to stop all streaming animations.\n if (this.chatContainer) {\n var icons = this.chatContainer.querySelectorAll('.ai-streaming-icon');\n for (var i = 0; i < icons.length; i++) {\n icons[i].classList.remove('ai-streaming-icon');\n icons[i].classList.add('ai-bot-icon');\n }\n }\n\n // Also update Vue data for consistency.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n\n // Save any settings that were deferred during streaming.\n if (this.settingsDirty) {\n this.debouncedSaveSettings();\n }\n },\n synthesizeSpeech(text, cacheIndex) {\n if (!this.textToSpeechEnabled || !text || !this.connection) {\n return;\n }\n\n this.audioChunks = [];\n this.isPlayingAudio = true;\n this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;\n\n this.connection.invoke(\"SynthesizeSpeech\", this.getItemId(), text, this.ttsVoiceName)\n .catch(err => {\n console.error(\"TTS synthesis error:\", err);\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this._ttsCacheIndex = -1;\n });\n },\n toggleMessageTts(message, index) {\n if (this.ttsPlayingMessageIndex === index) {\n this.stopAudio();\n return;\n }\n\n this.stopAudio();\n this.ttsPlayingMessageIndex = index;\n\n if (this.ttsAudioCache[index]) {\n this.playAudioBlob(this.ttsAudioCache[index]);\n return;\n }\n\n this.synthesizeSpeech(message.content, index);\n },\n playCollectedAudio() {\n if (this.audioChunks.length === 0) {\n if (!this.isPlayingAudio && this.audioPlayQueue.length === 0) {\n this.isPlayingAudio = false;\n }\n return;\n }\n\n const totalLength = this.audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of this.audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n this.audioChunks = [];\n\n const blob = new Blob([combined], { type: 'audio/mp3' });\n\n if (this._ttsCacheIndex >= 0) {\n this.ttsAudioCache[this._ttsCacheIndex] = blob;\n this._ttsCacheIndex = -1;\n }\n\n // If audio is currently playing, queue this blob for later.\n if (this.isPlayingAudio && this.currentAudioElement) {\n this.audioPlayQueue.push(blob);\n return;\n }\n\n this.playAudioBlob(blob);\n },\n playAudioBlob(blob) {\n const url = URL.createObjectURL(blob);\n const audio = new Audio(url);\n\n this.currentAudioElement = audio;\n this.isPlayingAudio = true;\n\n audio.addEventListener('ended', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.addEventListener('error', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.play().catch(err => {\n console.error(\"Audio playback error:\", err);\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.isPlayingAudio = false;\n });\n },\n playNextInQueue() {\n if (this.audioPlayQueue.length > 0) {\n const nextBlob = this.audioPlayQueue.shift();\n this.playAudioBlob(nextBlob);\n } else {\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this.conversationModeOnAudioEnded();\n }\n },\n stopAudio() {\n if (this.currentAudioElement) {\n this.currentAudioElement.pause();\n this.currentAudioElement.currentTime = 0;\n this.currentAudioElement = null;\n }\n this.audioChunks = [];\n this.audioPlayQueue = [];\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n },\n toggleConversationMode() {\n if (this.isConversationMode) {\n this.stopConversationMode();\n } else {\n this.startConversationMode();\n }\n },\n startConversationMode() {\n if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {\n return;\n }\n\n this.isConversationMode = true;\n this.updateConversationButton();\n this._conversationPartialTranscript = '';\n this._conversationAssistantMessage = null;\n this._conversationPartialMessage = null;\n\n // Remove any previous conversation ended notification.\n this.removeNotification('conversation-ended');\n\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this._conversationSubject = new signalR.Subject();\n this._conversationStream = stream;\n\n // Create an AnalyserNode for volume-based interrupt detection.\n var AudioCtx = window.AudioContext || window.webkitAudioContext;\n if (AudioCtx) {\n this._conversationAudioCtx = new AudioCtx();\n this._conversationAnalyser = this._conversationAudioCtx.createAnalyser();\n this._conversationAnalyser.fftSize = 256;\n var micSource = this._conversationAudioCtx.createMediaStreamSource(stream);\n micSource.connect(this._conversationAnalyser);\n }\n\n var pendingChunk = Promise.resolve();\n var analyser = this._conversationAnalyser;\n var interruptVolumeThreshold = 30;\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n // During TTS playback, check mic volume to detect\n // user interruption (speaking above threshold).\n if (this.isPlayingAudio && analyser) {\n var freqData = new Uint8Array(analyser.frequencyBinCount);\n analyser.getByteFrequencyData(freqData);\n var sum = 0;\n for (var k = 0; k < freqData.length; k++) { sum += freqData[k]; }\n var avg = sum / freqData.length;\n\n if (avg >= interruptVolumeThreshold) {\n // User is speaking — interrupt TTS playback.\n this.stopAudio();\n }\n }\n\n // Always send audio to STT — browser echo cancellation\n // handles speaker echo; continuous audio avoids gaps\n // that increase recognition latency.\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n try {\n this._conversationSubject.next(base64);\n } catch (err) {\n // Subject may have been completed already.\n }\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => {\n try {\n this._conversationSubject.complete();\n } catch (err) {\n // Already completed.\n }\n });\n });\n\n var itemId = this.getItemId();\n\n var language = navigator.language || document.documentElement.lang || 'en-US';\n this.connection.send(\"StartConversation\", itemId, this._conversationSubject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n this.isConversationMode = false;\n this.updateConversationButton();\n });\n },\n stopConversationMode() {\n if (!this.isConversationMode) {\n return;\n }\n\n this.isConversationMode = false;\n this.updateConversationButton();\n\n // Signal the server to cancel all in-progress STT/TTS streams immediately.\n if (this.connection) {\n this.connection.invoke(\"StopConversation\").catch(function () { });\n }\n\n if (this.isRecording && this.mediaRecorder) {\n this.mediaRecorder.stop();\n this.isRecording = false;\n }\n\n this.stopAudio();\n this._conversationPartialTranscript = '';\n this._conversationPartialMessage = null;\n\n // Clean up the AudioContext used for volume monitoring.\n if (this._conversationAudioCtx) {\n this._conversationAudioCtx.close().catch(function () { });\n this._conversationAudioCtx = null;\n this._conversationAnalyser = null;\n }\n\n // Mark any in-flight assistant message as done to stop the spinner.\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Safety net: clear all lingering streaming indicators.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n\n // Show a \"conversation ended\" notification system message.\n this.receiveNotification({\n type: 'conversation-ended',\n content: 'Conversation ended.',\n icon: 'fa-solid fa-circle-check',\n dismissible: true,\n autoDismissMs: 5000\n });\n },\n updateConversationButton() {\n if (!this.conversationButton) {\n return;\n }\n\n if (this.isConversationMode) {\n this.conversationButton.classList.add('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-dark', 'btn-outline-secondary');\n this.conversationButton.title = this.conversationButton.getAttribute('data-end-title') || 'End Conversation';\n var endHtml = this.conversationButton.getAttribute('data-end-html');\n if (endHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(endHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n } else {\n this.conversationButton.classList.remove('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-secondary');\n this.conversationButton.classList.add('btn-outline-dark');\n this.conversationButton.blur();\n this.conversationButton.title = this.conversationButton.getAttribute('data-start-title') || 'Start Conversation';\n var startHtml = this.conversationButton.getAttribute('data-start-html');\n if (startHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(startHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n }\n },\n conversationModeSendPrompt() {\n // Legacy: only used by AudioInput mode's ReceiveTranscript.\n },\n conversationModeOnAudioEnded() {\n // Legacy: in conversation mode, continuation is handled\n // by the persistent stream.\n },\n showTypingIndicator() {\n this.addMessage({\n role: 'indicator',\n htmlContent: config.indicatorTemplate\n });\n },\n hideTypingIndicator() {\n const originalLength = this.messages.length;\n this.messages = this.messages.filter(msg => msg.role !== 'indicator');\n const removedCount = originalLength - this.messages.length;\n return removedCount;\n },\n scrollToBottom() {\n if (!this.autoScroll) {\n return;\n }\n setTimeout(() => {\n this.chatContainer.scrollTop = this.chatContainer.scrollHeight - this.chatContainer.clientHeight;\n }, 50);\n },\n handleUserInput(event) {\n this.prompt = event.target.value;\n },\n getItemId() {\n return this.inputElement.getAttribute('data-interaction-id')\n || this.inputElement.getAttribute('data-item-id');\n },\n getSaveIndicatorElement() {\n if (config.saveIndicatorElementSelector) {\n return document.querySelector(config.saveIndicatorElementSelector);\n }\n\n return document.querySelector('[data-chat-interaction-save-indicator]');\n },\n showSaveIndicator(text, className) {\n const indicator = this.getSaveIndicatorElement();\n if (!indicator) {\n return;\n }\n\n indicator.textContent = text || '';\n indicator.className = 'settings-save-indicator ' + (className || 'text-muted');\n\n if (this.saveIndicatorTimeout) {\n clearTimeout(this.saveIndicatorTimeout);\n this.saveIndicatorTimeout = null;\n }\n\n if (!text) {\n return;\n }\n\n this.saveIndicatorTimeout = setTimeout(() => {\n indicator.textContent = '';\n this.saveIndicatorTimeout = null;\n }, 3000);\n },\n clearPendingSettingsSave() {\n if (this.saveSettingsTimeout) {\n clearTimeout(this.saveSettingsTimeout);\n this.saveSettingsTimeout = null;\n }\n },\n getFieldLabel(input) {\n const label = input\n .closest('.mb-3, .col-6, .col, .form-group, .form-floating')\n ?.querySelector('label');\n\n if (!label) {\n return input.dataset.setting || 'This field';\n }\n\n return (label.textContent || '')\n .replace(/\\s+/g, ' ')\n .trim()\n || input.dataset.setting\n || 'This field';\n },\n getValidationFeedbackElement(input) {\n let feedback = input.nextElementSibling;\n if (feedback && feedback.classList.contains('invalid-feedback')) {\n return feedback;\n }\n\n feedback = document.createElement('div');\n feedback.className = 'invalid-feedback';\n input.insertAdjacentElement('afterend', feedback);\n\n return feedback;\n },\n clearSettingValidationError(input) {\n input.classList.remove('is-invalid');\n input.removeAttribute('aria-invalid');\n\n const feedback = input.nextElementSibling;\n if (feedback && feedback.classList.contains('invalid-feedback')) {\n feedback.textContent = '';\n }\n },\n setSettingValidationError(input, message) {\n const feedback = this.getValidationFeedbackElement(input);\n input.classList.add('is-invalid');\n input.setAttribute('aria-invalid', 'true');\n feedback.textContent = message;\n },\n getSettingValidationMessage(input) {\n if (!input || input.disabled || input.type !== 'number') {\n return null;\n }\n\n const value = (input.value || '').trim();\n if (!value) {\n return null;\n }\n\n const number = Number(value);\n const fieldLabel = this.getFieldLabel(input);\n\n if (Number.isNaN(number)) {\n return `${fieldLabel} must be a valid number.`;\n }\n\n const min = input.getAttribute('min');\n if (min !== null && number < Number(min)) {\n return `${fieldLabel} must be ${min} or greater.`;\n }\n\n const max = input.getAttribute('max');\n if (max !== null && number > Number(max)) {\n return `${fieldLabel} must be ${max} or less.`;\n }\n\n return null;\n },\n validateSettingInput(input) {\n const message = this.getSettingValidationMessage(input);\n if (!message) {\n this.clearSettingValidationError(input);\n return true;\n }\n\n this.setSettingValidationError(input, message);\n return false;\n },\n validateSettings() {\n let isValid = true;\n\n this.getSettingInputs().forEach(input => {\n isValid = this.validateSettingInput(input) && isValid;\n });\n\n return isValid;\n },\n queueSettingsSave() {\n if (!this.validateSettings()) {\n this.clearPendingSettingsSave();\n this.settingsDirty = false;\n this.showSaveIndicator('Fix errors', 'text-danger');\n return;\n }\n\n this.settingsDirty = true;\n this.showSaveIndicator('Saving...', 'text-warning');\n this.debouncedSaveSettings();\n },\n getSettingInputs() {\n const explicitInputs = document.querySelectorAll('.setting-input[data-setting]');\n if (explicitInputs.length > 0) {\n return explicitInputs;\n }\n\n return document.querySelectorAll(\n 'input[name^=\"ChatInteraction.\"]:not([name*=\".Tools[\"]):not([name*=\".Connections[\"]), ' +\n 'select[name^=\"ChatInteraction.\"]:not([name*=\".Tools[\"]):not([name*=\".Connections[\"]), ' +\n 'textarea[name^=\"ChatInteraction.\"]:not([name*=\".Tools[\"]):not([name*=\".Connections[\"])'\n );\n },\n getSelectedGroupValues(groupName, fallbackSelector) {\n const explicitSelections = document.querySelectorAll(`.capability-checkbox[data-save-group=\"${groupName}\"]:checked`);\n if (explicitSelections.length > 0) {\n const values = [];\n\n explicitSelections.forEach(checkbox => {\n const value = checkbox.getAttribute('data-item-id') || checkbox.value;\n if (value) {\n values.push(value);\n }\n });\n\n return values;\n }\n\n if (!fallbackSelector) {\n return [];\n }\n\n const values = [];\n const checkboxes = document.querySelectorAll(fallbackSelector);\n\n checkboxes.forEach(checkbox => {\n const baseName = checkbox.name.replace('.IsSelected', '.ItemId');\n const hiddenInput = document.querySelector(`input[type=\"hidden\"][name=\"${baseName}\"]`);\n\n if (hiddenInput && hiddenInput.value) {\n values.push(hiddenInput.value);\n }\n });\n\n return values;\n },\n getPromptTemplateSelections() {\n const promptTemplates = [];\n\n document.querySelectorAll('.prompt-template-card').forEach(card => {\n const templateIdInput = card.querySelector('.prompt-template-id-input');\n const promptParametersInput = card.querySelector('.prompt-template-parameters-input');\n const templateId = templateIdInput ? templateIdInput.value : card.getAttribute('data-template-id');\n\n if (!templateId) {\n return;\n }\n\n promptTemplates.push({\n templateId: templateId,\n promptParameters: promptParametersInput ? (promptParametersInput.value || '').trim() : ''\n });\n });\n\n return promptTemplates;\n },\n receiveNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n // Replace existing notification with same type, or add new one.\n var idx = this.notifications.findIndex(n => n.type === notification.type);\n if (idx >= 0) {\n this.notifications.splice(idx, 1, notification);\n } else {\n this.notifications.push(notification);\n }\n this.scheduleNotificationDismiss(notification);\n this.scrollToBottom();\n },\n updateNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n var idx = this.notifications.findIndex(n => n.type === notification.type);\n if (idx >= 0) {\n this.notifications.splice(idx, 1, notification);\n this.scheduleNotificationDismiss(notification);\n this.scrollToBottom();\n }\n },\n scheduleNotificationDismiss(notification) {\n if (!notification || !notification.type || !notification.autoDismissMs || notification.autoDismissMs <= 0) {\n return;\n }\n this.notificationDismissTimers[notification.type] = setTimeout(() => {\n this.removeNotification(notification.type);\n }, notification.autoDismissMs);\n },\n clearNotificationDismiss(notificationType) {\n var timerId = this.notificationDismissTimers[notificationType];\n if (!timerId) {\n return;\n }\n clearTimeout(timerId);\n delete this.notificationDismissTimers[notificationType];\n },\n removeNotification(notificationType) {\n this.clearNotificationDismiss(notificationType);\n this.notifications = this.notifications.filter(n => n.type !== notificationType);\n },\n dismissNotification(notificationType) {\n this.removeNotification(notificationType);\n },\n handleNotificationAction(notificationType, actionName) {\n if (!this.connection) {\n return;\n }\n var itemId = this.getItemId();\n this.connection.invoke('HandleNotificationAction', itemId, notificationType, actionName)\n .catch(err => console.error('Failed to handle notification action:', err));\n },\n setItemId(itemId) {\n this.inputElement.setAttribute('data-interaction-id', itemId || '');\n this.inputElement.setAttribute('data-item-id', itemId || '');\n },\n resetInteraction() {\n this.setItemId('');\n this.isInteractionStarted = false;\n this.messages = [];\n this.showPlaceholder();\n },\n initializeApp() {\n this.inputElement = document.querySelector(config.inputElementSelector);\n this.buttonElement = document.querySelector(config.sendButtonElementSelector);\n this.chatContainer = document.querySelector(config.chatContainerElementSelector);\n this.placeholder = document.querySelector(config.placeholderElementSelector);\n\n const itemId = this.getItemId();\n if (itemId) {\n this.loadInteraction(itemId);\n }\n\n // Pause auto-scroll when the user manually scrolls up during streaming.\n this.chatContainer.addEventListener('scroll', () => {\n if (!this.stream) {\n return;\n }\n const threshold = 30;\n const atBottom = this.chatContainer.scrollHeight - this.chatContainer.clientHeight - this.chatContainer.scrollTop <= threshold;\n this.autoScroll = atBottom;\n });\n\n this.inputElement.addEventListener('keyup', event => {\n if (this.stream != null) {\n return;\n }\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n this.buttonElement.click();\n }\n });\n\n this.inputElement.addEventListener('input', (e) => {\n this.handleUserInput(e);\n\n if (e.target.value.trim()) {\n this.buttonElement.removeAttribute('disabled');\n } else {\n this.buttonElement.setAttribute('disabled', true);\n }\n });\n\n this.inputElement.addEventListener('paste', (e) => {\n // Use setTimeout to allow the paste to complete before checking the value\n setTimeout(() => {\n this.prompt = this.inputElement.value;\n if (this.inputElement.value.trim()) {\n this.buttonElement.removeAttribute('disabled');\n } else {\n this.buttonElement.setAttribute('disabled', true);\n }\n }, 0);\n });\n\n this.buttonElement.addEventListener('click', () => {\n if (this.stream != null) {\n this.stream.dispose();\n this.stream = null;\n\n this.streamingFinished();\n this.hideTypingIndicator();\n\n // Clean up: remove empty assistant message or stop streaming animation.\n if (this.messages.length > 0) {\n const lastMsg = this.messages[this.messages.length - 1];\n if (lastMsg.role === 'assistant' && !lastMsg.content) {\n this.messages.pop();\n } else if (lastMsg.isStreaming) {\n lastMsg.isStreaming = false;\n }\n }\n\n return;\n }\n\n this.sendMessage();\n });\n\n const chatInteractionItems = document.getElementsByClassName('chat-interaction-history-item');\n\n for (var i = 0; i < chatInteractionItems.length; i++) {\n chatInteractionItems[i].addEventListener('click', (e) => {\n e.preventDefault();\n\n var itemId = e.target.getAttribute('data-interaction-id');\n\n if (!itemId) {\n console.error('An element with the class chat-interaction-history-item with no data-interaction-id set.');\n return;\n }\n\n this.loadInteraction(itemId);\n });\n }\n\n for (let i = 0; i < config.messages.length; i++) {\n this.addMessage(config.messages[i]);\n }\n\n // Delegate click for code block copy buttons.\n if (this.chatContainer) {\n this.chatContainer.addEventListener('click', (e) => {\n var btn = e.target.closest('.ai-code-copy-btn');\n if (!btn) {\n return;\n }\n\n var block = btn.closest('.ai-code-block') || btn.closest('pre');\n if (!block) {\n return;\n }\n\n var codeEl = block.querySelector('code');\n if (codeEl) {\n navigator.clipboard.writeText(codeEl.textContent);\n var copiedText = config.codeCopiedText || 'Copied!';\n btn.innerHTML = ' ' + copiedText;\n setTimeout(() => {\n btn.innerHTML = '';\n }, 2000);\n }\n });\n }\n\n document.addEventListener('input', event => {\n if (event.target.matches('.setting-input[data-setting]')) {\n this.validateSettingInput(event.target);\n this.queueSettingsSave();\n }\n });\n\n document.addEventListener('change', event => {\n if (event.target.matches('.setting-input[data-setting], .capability-checkbox[data-save-group], .group-toggle, .ci-agent-global-toggle')) {\n if (event.target.matches('.setting-input[data-setting]')) {\n this.validateSettingInput(event.target);\n }\n\n this.queueSettingsSave();\n return;\n }\n\n if (event.target.closest('.prompt-template-parameters-input, .prompt-template-id-input')) {\n this.queueSettingsSave();\n }\n });\n\n document.addEventListener('click', event => {\n if (!event.target.closest('.prompt-template-add-btn, .remove-prompt-template-btn')) {\n return;\n }\n\n setTimeout(() => {\n this.queueSettingsSave();\n }, 0);\n });\n\n // Add event listener for clear history button\n const clearHistoryBtn = document.getElementById('clearHistoryBtn');\n if (clearHistoryBtn) {\n clearHistoryBtn.addEventListener('click', () => {\n const itemId = clearHistoryBtn.getAttribute('data-interaction-id');\n if (itemId) {\n this.clearHistory(itemId);\n }\n });\n }\n\n // Initialize speech-to-text microphone button.\n if (this.speechToTextEnabled && config.micButtonElementSelector) {\n this.micButton = document.querySelector(config.micButtonElementSelector);\n if (this.micButton) {\n this.micButton.style.display = '';\n this.micButton.addEventListener('click', () => {\n this.toggleRecording();\n });\n }\n }\n\n // Initialize conversation mode button.\n if (this.conversationModeEnabled && config.conversationButtonElementSelector) {\n this.conversationButton = document.querySelector(config.conversationButtonElementSelector);\n if (this.conversationButton) {\n this.conversationButton.addEventListener('click', () => {\n this.toggleConversationMode();\n });\n }\n }\n },\n loadInteraction(itemId) {\n this.connection.invoke(\"LoadInteraction\", itemId).catch(err => console.error(err));\n },\n reloadCurrentInteraction() {\n const itemId = this.getItemId();\n if (itemId) {\n this.loadInteraction(itemId);\n }\n },\n clearHistory(itemId) {\n const self = this;\n confirmDialog({\n title: config.clearHistoryTitle,\n message: config.clearHistoryMessage,\n okText: config.clearHistoryOkText,\n cancelText: config.clearHistoryCancelText,\n callback: function (confirmed) {\n if (confirmed) {\n // Cancel any active stream before clearing history.\n if (self.stream) {\n self.stream.dispose();\n self.stream = null;\n self.hideTypingIndicator();\n self.streamingFinished();\n }\n\n self.connection.invoke(\"ClearHistory\", itemId)\n .catch(err => console.error('Error clearing history:', err));\n }\n }\n });\n },\n debouncedSaveSettings() {\n // Clear any existing timeout to reset the debounce timer\n this.clearPendingSettingsSave();\n\n // Don't save while streaming — it will be saved when streaming completes.\n if (this.stream) {\n return;\n }\n\n // Set a new timeout to save after 850ms of no changes\n this.saveSettingsTimeout = setTimeout(() => {\n if (this.settingsDirty) {\n this.saveSettings();\n this.settingsDirty = false;\n }\n this.saveSettingsTimeout = null;\n }, 850);\n },\n getSelectedToolNames() {\n return this.getSelectedGroupValues(\n 'toolNames',\n 'input[type=\"checkbox\"][name$=\"].IsSelected\"][name^=\"ChatInteraction.Tools[\"]:checked'\n );\n },\n getSelectedMcpConnectionIds() {\n return this.getSelectedGroupValues(\n 'mcpConnectionIds',\n 'input[type=\"checkbox\"][name$=\"].IsSelected\"][name^=\"ChatInteraction.Connections[\"]:checked'\n );\n },\n getSelectedA2AConnectionIds() {\n return this.getSelectedGroupValues('a2aConnectionIds');\n },\n getSelectedAgentNames() {\n return this.getSelectedGroupValues(\n 'agentNames',\n 'input[type=\"checkbox\"][name$=\"].IsSelected\"][name^=\"ChatInteraction.Agents[\"]:checked'\n );\n },\n saveSettings() {\n const itemId = this.getItemId();\n if (!itemId) {\n return Promise.resolve();\n }\n\n if (!this.validateSettings()) {\n this.showSaveIndicator('Fix errors', 'text-danger');\n return Promise.resolve();\n }\n\n const settings = {};\n\n // Collect all form inputs with the \"ChatInteraction.\" prefix generically.\n // This avoids coupling the JS to specific field names — new fields added by\n // any module are automatically included.\n const inputs = this.getSettingInputs();\n\n inputs.forEach(input => {\n const key = input.dataset.setting\n || ((input.name || '').replace('ChatInteraction.', '').replace(/^[A-Z]/, match => match.toLowerCase()));\n\n if (!key) {\n return;\n }\n\n if (input.type === 'checkbox') {\n settings[key] = input.checked;\n } else if (input.type === 'number') {\n settings[key] = input.value ? parseFloat(input.value) : null;\n } else {\n settings[key] = input.value || null;\n }\n });\n\n // Add tool, MCP connection, and agent collections (special handling).\n settings.toolNames = this.getSelectedToolNames();\n settings.mcpConnectionIds = this.getSelectedMcpConnectionIds();\n settings.a2aConnectionIds = this.getSelectedA2AConnectionIds();\n settings.agentNames = this.getSelectedAgentNames();\n\n const promptTemplates = this.getPromptTemplateSelections();\n if (promptTemplates.length > 0) {\n settings.promptTemplates = promptTemplates;\n settings.promptTemplateIds = promptTemplates.map(template => template.templateId);\n }\n\n return this.connection.invoke(\"SaveSettings\", itemId, settings)\n .catch(err => {\n console.error('Error saving settings:', err);\n this.showSaveIndicator('Save failed', 'text-danger');\n });\n },\n flushPendingSave() {\n if (this.saveSettingsTimeout) {\n clearTimeout(this.saveSettingsTimeout);\n this.saveSettingsTimeout = null;\n }\n\n if (this.settingsDirty) {\n this.settingsDirty = false;\n return this.saveSettings();\n }\n\n return Promise.resolve();\n },\n initializeInteraction(itemId, force) {\n if (this.isInteractionStarted && !force) {\n return;\n }\n this.fireEvent(new CustomEvent(\"initializingChatInteraction\", { detail: { itemId: itemId } }));\n this.setItemId(itemId);\n this.isInteractionStarted = true;\n },\n copyResponse(message) {\n navigator.clipboard.writeText(message);\n },\n startRecording() {\n if (this.isRecording || !this.connection) {\n return;\n }\n\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this.preRecordingPrompt = this.prompt;\n this._audioInputSent = false;\n\n var subject = new signalR.Subject();\n var itemId = this.getItemId();\n var pendingChunk = Promise.resolve();\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n subject.next(base64);\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => subject.complete());\n });\n\n var language = navigator.language || document.documentElement.lang || 'en-US';\n this.connection.send(\"SendAudioStream\", itemId, subject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n this.updateMicButton();\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n });\n },\n stopRecording() {\n if (!this.isRecording || !this.mediaRecorder) {\n return;\n }\n\n this.mediaRecorder.stop();\n this.isRecording = false;\n this.updateMicButton();\n },\n toggleRecording() {\n if (this.isRecording) {\n this.stopRecording();\n } else {\n this.startRecording();\n }\n },\n updateMicButton() {\n if (!this.micButton) {\n return;\n }\n\n if (this.isRecording) {\n this.micButton.classList.add('stt-recording');\n this.micButton.innerHTML = '';\n } else {\n this.micButton.classList.remove('stt-recording');\n this.micButton.innerHTML = '';\n }\n }\n },\n watch: {\n isPlayingAudio() {\n // Reserved for future use — volume-based interrupt detection\n // no longer mutes tracks; browser echo cancellation handles echo.\n },\n isConversationMode(active) {\n if (this.micButton) {\n this.micButton.style.display = active ? 'none' : (this.speechToTextEnabled ? '' : 'none');\n }\n\n if (this.buttonElement) {\n this.buttonElement.style.display = active ? 'none' : '';\n }\n\n if (this.inputElement) {\n this.inputElement.disabled = active;\n if (active) {\n this.inputElement.placeholder = '';\n }\n }\n }\n },\n mounted() {\n (async () => {\n await this.startConnection();\n this.initializeApp();\n })();\n\n window.addEventListener('beforeunload', this.handleBeforeUnload);\n },\n beforeUnmount() {\n window.removeEventListener('beforeunload', this.handleBeforeUnload);\n\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n if (this.connection) {\n this.connection.stop();\n }\n },\n template: config.messageTemplate\n }).mount(config.appElementSelector);\n\n return app;\n };\n\n return {\n initialize: initialize\n };\n}();\n\n// Download chart as image via event delegation (DOMPurify strips inline onclick).\ndocument.addEventListener('click', function (e) {\n const btn = e.target.closest('.download-chart-btn');\n if (!btn) {\n return;\n }\n const chartId = btn.getAttribute('data-chart-id');\n const canvas = chartId ? document.getElementById(chartId) : null;\n if (!canvas) {\n console.error('Chart canvas not found:', chartId);\n return;\n }\n const link = document.createElement('a');\n link.download = 'chart-' + chartId + '.png';\n link.href = canvas.toDataURL('image/png');\n link.click();\n});\n\n// Intercept download clicks for data-URI images and convert to blob downloads.\ndocument.addEventListener('click', function (e) {\n const link = e.target.closest('.ai-download-image');\n if (!link) {\n return;\n }\n\n const container = link.closest('.generated-image-container');\n const img = container?.querySelector('img');\n if (!img) {\n return;\n }\n\n const src = img.src;\n if (!src || !src.startsWith('data:')) {\n return; // Normal URL – let the default behaviour handle it.\n }\n\n e.preventDefault();\n\n fetch(src)\n .then(function (res) { return res.blob(); })\n .then(function (blob) {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = link.getAttribute('download') || 'generated-image.png';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(function () { URL.revokeObjectURL(url); }, 100);\n })\n .catch(function (err) { console.error('Failed to download image:', err); });\n});\n"]} \ No newline at end of file diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.min.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.min.js index c2960ff7..397a6c7a 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.min.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/chat-interaction.min.js @@ -1 +1,2 @@ -function _slicedToArray(e,t){return _arrayWithHoles(e)||_iterableToArrayLimit(e,t)||_unsupportedIterableToArray(e,t)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _iterableToArrayLimit(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var i,o,a,r,s=[],c=!0,l=!1;try{if(a=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;c=!1}else for(;!(c=(i=a.call(n)).done)&&(s.push(i.value),s.length!==t);c=!0);}catch(e){l=!0,o=e}finally{try{if(!c&&null!=n.return&&(r=n.return(),Object(r)!==r))return}finally{if(l)throw o}}return s}}function _arrayWithHoles(e){if(Array.isArray(e))return e}function _typeof(e){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_typeof(e)}function _regenerator(){/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */var e,t,n="function"==typeof Symbol?Symbol:{},i=n.iterator||"@@iterator",o=n.toStringTag||"@@toStringTag";function a(n,i,o,a){var c=i&&i.prototype instanceof s?i:s,l=Object.create(c.prototype);return _regeneratorDefine2(l,"_invoke",function(n,i,o){var a,s,c,l=0,u=o||[],d=!1,h={p:0,n:0,v:e,a:m,f:m.bind(e,4),d:function(t,n){return a=t,s=0,c=e,h.n=n,r}};function m(n,i){for(s=n,c=i,t=0;!d&&l&&!o&&t3?(o=f===i)&&(c=a[(s=a[4])?5:(s=3,3)],a[4]=a[5]=e):a[0]<=m&&((o=n<2&&mi||i>f)&&(a[4]=n,a[5]=i,h.n=f,s=0))}if(o||n>1)return r;throw d=!0,i}return function(o,u,f){if(l>1)throw TypeError("Generator is already running");for(d&&1===u&&m(u,f),s=u,c=f;(t=s<2?e:c)||!d;){a||(s?s<3?(s>1&&(h.n=-1),m(s,c)):h.n=c:h.v=c);try{if(l=2,a){if(s||(o="next"),t=a[o]){if(!(t=t.call(a,c)))throw TypeError("iterator result is not an object");if(!t.done)return t;c=t.value,s<2&&(s=0)}else 1===s&&(t=a.return)&&t.call(a),s<2&&(c=TypeError("The iterator does not provide a '"+o+"' method"),s=1);a=e}else if((t=(d=h.n<0)?c:n.call(i,h))!==r)break}catch(t){a=e,s=1,c=t}finally{l=1}}return{value:t,done:d}}}(n,o,a),!0),l}var r={};function s(){}function c(){}function l(){}t=Object.getPrototypeOf;var u=[][i]?t(t([][i]())):(_regeneratorDefine2(t={},i,function(){return this}),t),d=l.prototype=s.prototype=Object.create(u);function h(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,l):(e.__proto__=l,_regeneratorDefine2(e,o,"GeneratorFunction")),e.prototype=Object.create(d),e}return c.prototype=l,_regeneratorDefine2(d,"constructor",l),_regeneratorDefine2(l,"constructor",c),c.displayName="GeneratorFunction",_regeneratorDefine2(l,o,"GeneratorFunction"),_regeneratorDefine2(d),_regeneratorDefine2(d,o,"Generator"),_regeneratorDefine2(d,i,function(){return this}),_regeneratorDefine2(d,"toString",function(){return"[object Generator]"}),(_regenerator=function(){return{w:a,m:h}})()}function _regeneratorDefine2(e,t,n,i){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}_regeneratorDefine2=function(e,t,n,i){function a(t,n){_regeneratorDefine2(e,t,function(e){return this._invoke(t,n,e)})}t?o?o(e,t,{value:n,enumerable:!i,configurable:!i,writable:!i}):e[t]=n:(a("next",0),a("throw",1),a("return",2))},_regeneratorDefine2(e,t,n,i)}function asyncGeneratorStep(e,t,n,i,o,a,r){try{var s=e[a](r),c=s.value}catch(e){return void n(e)}s.done?t(c):Promise.resolve(c).then(i,o)}function _asyncToGenerator(e){return function(){var t=this,n=arguments;return new Promise(function(i,o){var a=e.apply(t,n);function r(e){asyncGeneratorStep(a,i,o,r,s,"next",e)}function s(e){asyncGeneratorStep(a,i,o,r,s,"throw",e)}r(void 0)})}}function _createForOfIteratorHelper(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=_unsupportedIterableToArray(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var i=0,o=function(){};return{s:o,n:function(){return i>=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,r=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return r=e.done,e},e:function(e){s=!0,a=e},f:function(){try{r||null==n.return||n.return()}finally{if(s)throw a}}}}function _toConsumableArray(e){return _arrayWithoutHoles(e)||_iterableToArray(e)||_unsupportedIterableToArray(e)||_nonIterableSpread()}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(e,t){if(e){if("string"==typeof e)return _arrayLikeToArray(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_arrayLikeToArray(e,t):void 0}}function _iterableToArray(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}function _arrayWithoutHoles(e){if(Array.isArray(e))return _arrayLikeToArray(e)}function _arrayLikeToArray(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=Array(t);n\n
\n
\n
You
\n
\n \n {{ getAssistantLabel(message) }}\n
\n
\n

{{ message.title }}

\n
\n \n \n \n
\n
\n
\n
\n
\n \n {{ notification.content }}\n \n
\n
\n \n
\n
\n \n ',indicatorTemplate:'\n
\n \n Assistant\n
\n ',untitledText:"Untitled",clearHistoryTitle:"Clear History",clearHistoryMessage:"Are you sure you want to clear the chat history? This action cannot be undone. Your documents, parameters, and tools will be preserved.",clearHistoryOkText:"Yes",clearHistoryCancelText:"Cancel"};function t(e){if(!e)return"";var t=e.trim();return/^javascript:/i.test(t)||/^vbscript:/i.test(t)||/^data:text\/html/i.test(t)?"":e}function n(e){var t=document.createElement("span");return t.textContent=e,t.innerHTML}var i=new marked.Renderer;i.link=function(e){var n=t(e.href);return n?'
').concat(e.text,""):e.text||""},i.code=function(e){var t=e.text||"",i=(e.lang||"").trim(),o=t;if("undefined"!=typeof hljs)if(i&&hljs.getLanguage(i))try{o=hljs.highlight(t,{language:i}).value}catch(e){}else try{o=hljs.highlightAuto(t).value}catch(e){}else o=n(t);var a=i?n(i):"code";return'
'.concat(a,'
').concat(o,"
")},i.image=function(n){var i=t(n.href);if(!i)return"";var o=n.text||e.generatedImageAltText,a=e.generatedImageMaxWidth;return'
\n ').concat(o,'\n
\n \n \n \n
\n
')};var o=0,a=[];function r(e){if(e&&e._pendingCharts&&e._pendingCharts.length){var t=_toConsumableArray(e._pendingCharts);e._pendingCharts=[],requestAnimationFrame(function(){var e,n=_createForOfIteratorHelper(t);try{for(n.s();!(e=n.n()).done;){var i=e.value,o=document.getElementById(i.chartId);if(o)if("undefined"!=typeof Chart)if(null!==o.offsetParent)try{var a,r,s;o._chartInstance&&o._chartInstance.destroy();var c="string"==typeof i.config?JSON.parse(i.config):i.config;null!==(a=c.options)&&void 0!==a||(c.options={}),c.options.responsive=!0,c.options.maintainAspectRatio=!0,null!==(s=(r=c.options).aspectRatio)&&void 0!==s||(r.aspectRatio=4/3),o._chartInstance=new Chart(o,c)}catch(e){console.error("Error creating chart:",e)}else window.__chartConfigs[i.chartId]=i.config;else console.warn('Chart.js is not loaded. To render interactive charts, include the Chart.js library on the page (e.g., ).');\n continue;\n }\n\n // When the canvas is inside a hidden container (e.g., display:none),\n // it has zero dimensions. Keep the config for later rendering.\n if (canvas.offsetParent === null) {\n window.__chartConfigs[c.chartId] = c.config;\n continue;\n }\n\n try {\n // Destroy existing chart instance if re-rendering\n if (canvas._chartInstance) {\n canvas._chartInstance.destroy();\n }\n\n const cfg = typeof c.config === 'string' ? JSON.parse(c.config) : c.config;\n cfg.options ??= {};\n cfg.options.responsive = true;\n cfg.options.maintainAspectRatio = true;\n cfg.options.aspectRatio ??= 4 / 3;\n\n canvas._chartInstance = new Chart(canvas, cfg);\n } catch (e) {\n console.error('Error creating chart:', e);\n }\n }\n });\n }\n\n // Parse markdown content via marked (which natively handles [chart:...] markers\n // through the registered extension) and collect pending chart configs for later\n // Chart.js rendering.\n function parseMarkdownContent(content, message) {\n _pendingCharts = [];\n const html = marked.parse(content, { renderer });\n message._pendingCharts = _pendingCharts.length > 0 ? [..._pendingCharts] : [];\n return DOMPurify.sanitize(html, { ADD_TAGS: ['canvas'], ADD_ATTR: ['target'] });\n }\n\n const initialize = (instanceConfig) => {\n\n const config = Object.assign({}, defaultConfig, instanceConfig);\n // Keep defaultConfig in sync so renderers use overridden values\n defaultConfig = config;\n\n if (!config.signalRHubUrl) {\n console.error('The signalRHubUrl is required.');\n return;\n }\n\n if (!config.appElementSelector) {\n console.error('The appElementSelector is required.');\n return;\n }\n\n if (!config.chatContainerElementSelector) {\n console.error('The chatContainerElementSelector is required.');\n return;\n }\n\n if (!config.inputElementSelector) {\n console.error('The inputElementSelector is required.');\n return;\n }\n\n if (!config.sendButtonElementSelector) {\n console.error('The sendButtonElementSelector is required.');\n return;\n }\n\n const app = Vue.createApp({\n data() {\n return {\n inputElement: null,\n buttonElement: null,\n chatContainer: null,\n placeholder: null,\n isInteractionStarted: false,\n isPlaceholderVisible: true,\n isStreaming: false,\n isNavigatingAway: false,\n autoScroll: true,\n stream: null,\n messages: [],\n prompt: '',\n initialFieldValues: new Map(),\n settingsDirty: false,\n saveSettingsTimeout: null,\n saveIndicatorTimeout: null,\n isRecording: false,\n mediaRecorder: null,\n preRecordingPrompt: '',\n micButton: null,\n speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation',\n textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled,\n ttsVoiceName: config.ttsVoiceName || null,\n audioChunks: [],\n audioPlayQueue: [],\n isPlayingAudio: false,\n currentAudioElement: null,\n ttsPlayingMessageIndex: -1,\n ttsAudioCache: {},\n conversationModeEnabled: config.chatMode === 'Conversation',\n conversationButton: null,\n isConversationMode: false,\n notifications: [],\n notificationDismissTimers: {},\n };\n },\n computed: {\n lastAssistantIndex() {\n for (var i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i].role === 'assistant') {\n return i;\n }\n }\n return -1;\n }\n },\n methods: {\n handleBeforeUnload() {\n this.isNavigatingAway = true;\n },\n async startConnection() {\n this.connection = new signalR.HubConnectionBuilder()\n .withUrl(config.signalRHubUrl)\n .withAutomaticReconnect()\n .build();\n\n // Allow long-running operations (e.g., multi-step MCP tool calls)\n // without the client disconnecting prematurely.\n this.connection.serverTimeoutInMilliseconds = 600000;\n this.connection.keepAliveIntervalInMilliseconds = 15000;\n\n this.connection.on(\"LoadInteraction\", (data) => {\n this.initializeInteraction(data.itemId, true);\n this.messages = [];// Update the title field if it exists\n const titleInput = document.querySelector('[data-chat-interaction-title], .setting-input[data-setting=\"title\"], input[name=\"ChatInteraction.Title\"], input[name=\"Title\"]');\n if (titleInput && data.title) {\n titleInput.value = data.title;\n }\n\n (data.messages ?? []).forEach(msg => {\n this.addMessage(msg);\n\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n });\n });\n });\n\n this.connection.on(\"SettingsSaved\", (itemId, title) => {\n // Update the history list item if it exists\n // Use a more specific selector to only target history list items, not other elements like the Clear History button\n const historyItem = document.querySelector(`.chat-interaction-history-item[data-interaction-id=\"${itemId}\"]`);\n if (historyItem) {\n historyItem.textContent = title || config.untitledText;\n }\n\n const titleInput = document.querySelector('[data-chat-interaction-title], .setting-input[data-setting=\"title\"], input[name=\"ChatInteraction.Title\"], input[name=\"Title\"]');\n if (titleInput && title) {\n titleInput.value = title;\n }\n\n this.showSaveIndicator('Saved', 'text-success');\n });\n\n this.connection.on(\"ReceiveError\", (error) => {\n console.error(\"SignalR Error: \", error);\n this.showSaveIndicator('Save failed', 'text-danger');\n\n if (this.isRecording) {\n this.stopRecording();\n }\n });\n\n this.connection.on(\"ReceiveTranscript\", (itemId, text, isFinal) => {\n if (this.isConversationMode) {\n if (!isFinal && text) {\n this._conversationPartialTranscript = text;\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n var html = '

' + escaped + '

';\n\n // Show partial transcript as a live user message.\n if (!this._conversationPartialMessage) {\n this.hidePlaceholder();\n this._conversationPartialMessage = {\n role: 'user',\n content: text,\n htmlContent: html,\n isPartial: true\n };\n this.messages.push(this._conversationPartialMessage);\n } else {\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = html;\n }\n this.scrollToBottom();\n }\n return;\n }\n\n if (text && !this._audioInputSent) {\n this.prompt = this.preRecordingPrompt + text;\n if (this.inputElement) {\n this.inputElement.value = this.prompt;\n this.inputElement.dispatchEvent(new Event('input'));\n }\n }\n });\n\n this.connection.on(\"ReceiveConversationUserMessage\", (itemId, text) => {\n if (text) {\n this.stopAudio();\n\n // If there's an interrupted assistant message still streaming,\n // mark it as done to stop the spinner animation.\n if (this._conversationAssistantMessage) {\n var oldMsg = this.messages[this._conversationAssistantMessage.index];\n if (oldMsg) {\n oldMsg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Replace the partial transcript message with the final one.\n if (this._conversationPartialMessage) {\n var escaped = text.replace(/&/g, '&').replace(//g, '>');\n this._conversationPartialMessage.content = text;\n this._conversationPartialMessage.htmlContent = '

' + escaped + '

';\n this._conversationPartialMessage.isPartial = false;\n this._conversationPartialMessage = null;\n } else {\n this.addMessage({\n role: 'user',\n content: text\n });\n }\n this.scrollToBottom();\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantToken\", (itemId, messageId, token, responseId, appearance) => {\n if (!this._conversationAssistantMessage) {\n this.stopAudio();\n this.hideTypingIndicator();\n\n // Ensure no stale streaming indicators remain from prior messages.\n for (var j = 0; j < this.messages.length; j++) {\n if (this.messages[j].isStreaming) {\n this.messages[j].isStreaming = false;\n }\n }\n\n var msgIndex = this.messages.length;\n var newMessage = {\n id: messageId,\n role: \"assistant\",\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n appearance: this.normalizeAssistantAppearance(appearance),\n };\n this.messages.push(newMessage);\n this._conversationAssistantMessage = { index: msgIndex, content: '' };\n }\n\n this._conversationAssistantMessage.content += token;\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n if (!msg.appearance) {\n msg.appearance = this.normalizeAssistantAppearance(appearance);\n }\n msg.content = this._conversationAssistantMessage.content;\n msg.htmlContent = parseMarkdownContent(msg.content, msg);\n this.$nextTick(() => {\n renderChartsInMessage(msg);\n this.scrollToBottom();\n });\n }\n });\n\n this.connection.on(\"ReceiveConversationAssistantComplete\", (itemId, messageId) => {\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n });\n\n this.connection.on(\"ReceiveAudioChunk\", (itemId, base64Audio, contentType) => {\n if (base64Audio) {\n const binaryString = atob(base64Audio);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n this.audioChunks.push(bytes);\n }\n });\n\n this.connection.on(\"ReceiveAudioComplete\", (itemId) => {\n this.playCollectedAudio();\n });\n\n this.connection.on(\"HistoryCleared\", (itemId) => {\n // Clear messages and show placeholder\n this.messages = [];\n this.showPlaceholder();\n\n // Hide the clear history button since there's no history now\n const clearHistoryBtn = document.getElementById('clearHistoryBtn');\n if (clearHistoryBtn) {\n clearHistoryBtn.classList.add('d-none');\n }\n });\n\n this.connection.on(\"ReceiveNotification\", (notification) => {\n this.receiveNotification(notification);\n });\n\n this.connection.on(\"UpdateNotification\", (notification) => {\n this.updateNotification(notification);\n });\n\n this.connection.on(\"RemoveNotification\", (notificationType) => {\n this.removeNotification(notificationType);\n });\n\n this.connection.onreconnecting(() => {\n console.warn(\"SignalR: reconnecting...\");\n });\n\n this.connection.onreconnected(() => {\n console.info(\"SignalR: reconnected.\");\n this.reloadCurrentInteraction();\n });\n\n this.connection.onclose((error) => {\n if (this.isNavigatingAway) {\n return;\n }\n\n if (error) {\n console.warn(\"SignalR connection closed with error:\", error.message || error);\n }\n });\n\n try {\n await this.connection.start();\n } catch (err) {\n console.error(\"SignalR Connection Error: \", err);\n }\n },\n addMessageInternal(message) {\n if (message.role === 'assistant') {\n message.appearance = this.normalizeAssistantAppearance(message.appearance);\n }\n\n if (message.content && !message.htmlContent) {\n message.htmlContent = parseMarkdownContent(message.content, message);\n }\n this.fireEvent(new CustomEvent(\"addingChatInteractionMessage\", { detail: { message: message } }));\n this.messages.push(message);\n\n this.$nextTick(() => {\n this.fireEvent(new CustomEvent(\"addedChatInteractionMessage\", { detail: { message: message } }));\n });\n },\n addMessage(message) {\n if (message.content) {\n let processedContent = message.content.trim();\n\n if (message.references && typeof message.references === \"object\" && Object.keys(message.references).length) {\n\n // Only include references that were actually cited in the response.\n const citedRefs = Object.entries(message.references).filter(([key]) => processedContent.includes(key));\n\n if (citedRefs.length) {\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders.\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processedContent = processedContent.replaceAll(key, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processedContent = processedContent.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n processedContent = processedContent.replaceAll('
', ',');\n\n processedContent += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || `[doc:${value.index}]`;\n processedContent += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n }\n }\n\n message.content = processedContent;\n message.htmlContent = parseMarkdownContent(processedContent, message);\n }\n\n this.addMessageInternal(message);\n this.hidePlaceholder();\n\n this.$nextTick(() => {\n // Render any pending charts once the DOM is updated\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n hidePlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.add('d-none');\n }\n this.isPlaceholderVisible = false;\n },\n showPlaceholder() {\n if (this.placeholder) {\n this.placeholder.classList.remove('d-none');\n }\n this.isPlaceholderVisible = true;\n },\n fireEvent(event) {\n document.dispatchEvent(event);\n },\n normalizeAssistantAppearance(appearance) {\n if (!appearance) {\n return null;\n }\n\n var label = typeof appearance.label === 'string' ? appearance.label.trim() : '';\n var icon = typeof appearance.icon === 'string' ? appearance.icon.trim() : '';\n var cssClass = typeof appearance.cssClass === 'string' ? appearance.cssClass.trim() : '';\n var disableStreamingAnimation = !!appearance.disableStreamingAnimation;\n\n if (!label && !icon && !cssClass && !disableStreamingAnimation) {\n return null;\n }\n\n return {\n label: label,\n icon: icon,\n cssClass: cssClass,\n disableStreamingAnimation: disableStreamingAnimation,\n };\n },\n getAssistantLabel(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.label ? appearance.label : defaultConfig.assistantLabel;\n },\n getAssistantRoleClasses(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n var classes = ['ai-chat-msg-role'];\n\n if (appearance && appearance.cssClass) {\n classes.push(appearance.cssClass);\n } else {\n classes.push('ai-chat-msg-role-assistant');\n }\n\n return classes;\n },\n getAssistantIconClasses(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return [this.shouldAnimateAssistantIcon(message, index) ? 'ai-streaming-icon' : 'ai-bot-icon', appearance && appearance.cssClass ? appearance.cssClass : ''];\n },\n getAssistantIcon(message) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return appearance && appearance.icon ? appearance.icon : 'fa fa-robot';\n },\n shouldAnimateAssistantIcon(message, index) {\n var appearance = message ? this.normalizeAssistantAppearance(message.appearance) : null;\n return !!message &&\n message.isStreaming &&\n index === this.lastAssistantIndex &&\n !(appearance && appearance.disableStreamingAnimation);\n },\n isIndicator(message) {\n return message.role === 'indicator';\n },\n async sendMessage() {\n let trimmedPrompt = this.prompt.trim();\n\n if (!trimmedPrompt) {\n return;\n }\n\n // Stop any active recording before sending.\n if (this.isRecording) {\n this.stopRecording();\n }\n\n // Prevent stale ReceiveTranscript events from repopulating the prompt.\n this._audioInputSent = true;\n\n // Flush any pending settings save before sending a message\n // to prevent concurrent hub calls that can cause database deadlocks.\n await this.flushPendingSave();\n\n this.addMessage({\n role: 'user',\n content: trimmedPrompt\n });\n\n // Show the clear history button since we now have prompts\n const clearHistoryBtn = document.getElementById('clearHistoryBtn');\n if (clearHistoryBtn) {\n clearHistoryBtn.classList.remove('d-none');\n }\n\n this.streamMessage(trimmedPrompt);\n this.inputElement.value = '';\n this.prompt = '';\n },\n streamMessage(trimmedPrompt) {\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n\n this.streamingStarted();\n this.showTypingIndicator();\n this.autoScroll = true;\n\n var content = '';\n var references = {};\n var lastResponseId = null;\n\n var messageIndex = this.messages.length;\n var currentItemId = this.getItemId();\n\n this.stream = this.connection.stream(\"SendMessage\", currentItemId, trimmedPrompt)\n .subscribe({\n next: (chunk) => {\n let message = this.messages[messageIndex];\n\n if (!message) {\n if (chunk.sessionId && !currentItemId) {\n this.setItemId(chunk.sessionId);\n }\n\n this.hideTypingIndicator();\n messageIndex = this.messages.length;\n let newMessage = {\n role: \"assistant\",\n content: \"\",\n htmlContent: \"\",\n isStreaming: true,\n };\n\n this.messages.push(newMessage);\n message = newMessage;\n }\n\n if (chunk.references && typeof chunk.references === \"object\" && Object.keys(chunk.references).length) {\n for (const [key, value] of Object.entries(chunk.references)) {\n references[key] = value;\n }\n }\n\n if (chunk.content) {\n // When the responseId changes (e.g., after an internal tool call),\n // insert a line break to visually separate response segments.\n if (chunk.responseId && lastResponseId && chunk.responseId !== lastResponseId) {\n content += '\\n\\n';\n }\n\n if (chunk.responseId) {\n lastResponseId = chunk.responseId;\n }\n\n let processedContent = chunk.content;\n\n for (const [key, value] of Object.entries(references)) {\n processedContent = processedContent.replaceAll(key, `${value.index}`);\n }\n\n content += processedContent.replaceAll('
', ',');\n }\n\n message.content = content;\n\n message.htmlContent = parseMarkdownContent(content, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n },\n complete: () => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n if (!msg || !msg.content) {\n // No content received at all.\n this.hideTypingIndicator();\n }\n\n // Trigger text-to-speech only in conversation mode.\n if (this.isConversationMode && this.textToSpeechEnabled && msg && msg.content) {\n this.synthesizeSpeech(msg.content);\n }\n\n this.stream?.dispose();\n this.stream = null;\n },\n error: (err) => {\n this.processReferences(references, messageIndex);\n this.streamingFinished();\n\n let msg = this.messages[messageIndex];\n if (msg) {\n msg.isStreaming = false;\n }\n\n this.hideTypingIndicator();\n\n if (!this.isNavigatingAway) {\n this.addMessage(this.getServiceDownMessage());\n }\n\n this.stream?.dispose();\n this.stream = null;\n\n console.error(\"Stream error:\", err);\n }\n });\n },\n getServiceDownMessage() {\n return {\n role: \"assistant\",\n content: \"Our service is currently unavailable. Please try again later. We apologize for the inconvenience.\",\n htmlContent: \"\",\n };\n },\n processReferences(references, messageIndex) {\n if (Object.keys(references).length) {\n let message = this.messages[messageIndex];\n const content = message.content || '';\n\n // Only include references that were actually cited in the response.\n // Check both raw [doc:N] markers and already-rendered tags from streaming.\n const citedRefs = Object.entries(references).filter(([key, value]) =>\n content.includes(key) || content.includes(`${value.index}`)\n );\n\n if (!citedRefs.length) {\n return;\n }\n\n // Sort by original index so display indices follow a natural order.\n citedRefs.sort(([, a], [, b]) => a.index - b.index);\n\n // Phase 1: Replace all markers with unique placeholders to avoid collisions during remapping.\n let processed = content.trim();\n let displayIndex = 1;\n for (const [key, value] of citedRefs) {\n const placeholder = `__CITE_${value.index}__`;\n processed = processed.replaceAll(key, placeholder);\n processed = processed.replaceAll(`${value.index}`, placeholder);\n value._displayIndex = displayIndex++;\n value._placeholder = placeholder;\n }\n\n // Phase 2: Replace placeholders with sequential display indices.\n for (const [, value] of citedRefs) {\n processed = processed.replaceAll(value._placeholder, `${value._displayIndex}`);\n }\n\n processed = processed.replaceAll('', ',');\n\n processed += '

';\n\n for (const [key, value] of citedRefs) {\n const label = value.text || `[doc:${value.index}]`;\n processed += value.link\n ? `**${value._displayIndex}**. [${label}](${value.link})
`\n : `**${value._displayIndex}**. ${label}
`;\n }\n\n message.content = processed;\n message.htmlContent = parseMarkdownContent(processed, message);\n\n this.messages[messageIndex] = message;\n\n this.$nextTick(() => {\n renderChartsInMessage(message);\n this.scrollToBottom();\n });\n }\n },\n streamingStarted() {\n var stopIcon = this.buttonElement.getAttribute('data-stop-icon');\n\n if (stopIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(stopIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n },\n streamingFinished() {\n var startIcon = this.buttonElement.getAttribute('data-start-icon');\n\n if (startIcon) {\n this.buttonElement.replaceChildren(DOMPurify.sanitize(startIcon, { RETURN_DOM_FRAGMENT: true }));\n }\n\n // Directly manipulate the DOM to stop all streaming animations.\n if (this.chatContainer) {\n var icons = this.chatContainer.querySelectorAll('.ai-streaming-icon');\n for (var i = 0; i < icons.length; i++) {\n icons[i].classList.remove('ai-streaming-icon');\n icons[i].classList.add('ai-bot-icon');\n }\n }\n\n // Also update Vue data for consistency.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n\n // Save any settings that were deferred during streaming.\n if (this.settingsDirty) {\n this.debouncedSaveSettings();\n }\n },\n synthesizeSpeech(text, cacheIndex) {\n if (!this.textToSpeechEnabled || !text || !this.connection) {\n return;\n }\n\n this.audioChunks = [];\n this.isPlayingAudio = true;\n this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;\n\n this.connection.invoke(\"SynthesizeSpeech\", this.getItemId(), text, this.ttsVoiceName)\n .catch(err => {\n console.error(\"TTS synthesis error:\", err);\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this._ttsCacheIndex = -1;\n });\n },\n toggleMessageTts(message, index) {\n if (this.ttsPlayingMessageIndex === index) {\n this.stopAudio();\n return;\n }\n\n this.stopAudio();\n this.ttsPlayingMessageIndex = index;\n\n if (this.ttsAudioCache[index]) {\n this.playAudioBlob(this.ttsAudioCache[index]);\n return;\n }\n\n this.synthesizeSpeech(message.content, index);\n },\n playCollectedAudio() {\n if (this.audioChunks.length === 0) {\n if (!this.isPlayingAudio && this.audioPlayQueue.length === 0) {\n this.isPlayingAudio = false;\n }\n return;\n }\n\n const totalLength = this.audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of this.audioChunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n this.audioChunks = [];\n\n const blob = new Blob([combined], { type: 'audio/mp3' });\n\n if (this._ttsCacheIndex >= 0) {\n this.ttsAudioCache[this._ttsCacheIndex] = blob;\n this._ttsCacheIndex = -1;\n }\n\n // If audio is currently playing, queue this blob for later.\n if (this.isPlayingAudio && this.currentAudioElement) {\n this.audioPlayQueue.push(blob);\n return;\n }\n\n this.playAudioBlob(blob);\n },\n playAudioBlob(blob) {\n const url = URL.createObjectURL(blob);\n const audio = new Audio(url);\n\n this.currentAudioElement = audio;\n this.isPlayingAudio = true;\n\n audio.addEventListener('ended', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.addEventListener('error', () => {\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.playNextInQueue();\n });\n\n audio.play().catch(err => {\n console.error(\"Audio playback error:\", err);\n URL.revokeObjectURL(url);\n this.currentAudioElement = null;\n this.isPlayingAudio = false;\n });\n },\n playNextInQueue() {\n if (this.audioPlayQueue.length > 0) {\n const nextBlob = this.audioPlayQueue.shift();\n this.playAudioBlob(nextBlob);\n } else {\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n this.conversationModeOnAudioEnded();\n }\n },\n stopAudio() {\n if (this.currentAudioElement) {\n this.currentAudioElement.pause();\n this.currentAudioElement.currentTime = 0;\n this.currentAudioElement = null;\n }\n this.audioChunks = [];\n this.audioPlayQueue = [];\n this.isPlayingAudio = false;\n this.ttsPlayingMessageIndex = -1;\n },\n toggleConversationMode() {\n if (this.isConversationMode) {\n this.stopConversationMode();\n } else {\n this.startConversationMode();\n }\n },\n startConversationMode() {\n if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {\n return;\n }\n\n this.isConversationMode = true;\n this.updateConversationButton();\n this._conversationPartialTranscript = '';\n this._conversationAssistantMessage = null;\n this._conversationPartialMessage = null;\n\n // Remove any previous conversation ended notification.\n this.removeNotification('conversation-ended');\n\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this._conversationSubject = new signalR.Subject();\n this._conversationStream = stream;\n\n // Create an AnalyserNode for volume-based interrupt detection.\n var AudioCtx = window.AudioContext || window.webkitAudioContext;\n if (AudioCtx) {\n this._conversationAudioCtx = new AudioCtx();\n this._conversationAnalyser = this._conversationAudioCtx.createAnalyser();\n this._conversationAnalyser.fftSize = 256;\n var micSource = this._conversationAudioCtx.createMediaStreamSource(stream);\n micSource.connect(this._conversationAnalyser);\n }\n\n var pendingChunk = Promise.resolve();\n var analyser = this._conversationAnalyser;\n var interruptVolumeThreshold = 30;\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n // During TTS playback, check mic volume to detect\n // user interruption (speaking above threshold).\n if (this.isPlayingAudio && analyser) {\n var freqData = new Uint8Array(analyser.frequencyBinCount);\n analyser.getByteFrequencyData(freqData);\n var sum = 0;\n for (var k = 0; k < freqData.length; k++) { sum += freqData[k]; }\n var avg = sum / freqData.length;\n\n if (avg >= interruptVolumeThreshold) {\n // User is speaking — interrupt TTS playback.\n this.stopAudio();\n }\n }\n\n // Always send audio to STT — browser echo cancellation\n // handles speaker echo; continuous audio avoids gaps\n // that increase recognition latency.\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n try {\n this._conversationSubject.next(base64);\n } catch (err) {\n // Subject may have been completed already.\n }\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => {\n try {\n this._conversationSubject.complete();\n } catch (err) {\n // Already completed.\n }\n });\n });\n\n var itemId = this.getItemId();\n\n var language = navigator.language || document.documentElement.lang || 'en-US';\n this.connection.send(\"StartConversation\", itemId, this._conversationSubject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n this.isConversationMode = false;\n this.updateConversationButton();\n });\n },\n stopConversationMode() {\n if (!this.isConversationMode) {\n return;\n }\n\n this.isConversationMode = false;\n this.updateConversationButton();\n\n // Signal the server to cancel all in-progress STT/TTS streams immediately.\n if (this.connection) {\n this.connection.invoke(\"StopConversation\").catch(function () { });\n }\n\n if (this.isRecording && this.mediaRecorder) {\n this.mediaRecorder.stop();\n this.isRecording = false;\n }\n\n this.stopAudio();\n this._conversationPartialTranscript = '';\n this._conversationPartialMessage = null;\n\n // Clean up the AudioContext used for volume monitoring.\n if (this._conversationAudioCtx) {\n this._conversationAudioCtx.close().catch(function () { });\n this._conversationAudioCtx = null;\n this._conversationAnalyser = null;\n }\n\n // Mark any in-flight assistant message as done to stop the spinner.\n if (this._conversationAssistantMessage) {\n var msg = this.messages[this._conversationAssistantMessage.index];\n if (msg) {\n msg.isStreaming = false;\n }\n this._conversationAssistantMessage = null;\n }\n\n // Safety net: clear all lingering streaming indicators.\n for (var i = 0; i < this.messages.length; i++) {\n if (this.messages[i].isStreaming) {\n this.messages[i].isStreaming = false;\n }\n }\n\n // Show a \"conversation ended\" notification system message.\n this.receiveNotification({\n type: 'conversation-ended',\n content: 'Conversation ended.',\n icon: 'fa-solid fa-circle-check',\n dismissible: true,\n autoDismissMs: 5000\n });\n },\n updateConversationButton() {\n if (!this.conversationButton) {\n return;\n }\n\n if (this.isConversationMode) {\n this.conversationButton.classList.add('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-dark', 'btn-outline-secondary');\n this.conversationButton.title = this.conversationButton.getAttribute('data-end-title') || 'End Conversation';\n var endHtml = this.conversationButton.getAttribute('data-end-html');\n if (endHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(endHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n } else {\n this.conversationButton.classList.remove('active', 'btn-primary');\n this.conversationButton.classList.remove('btn-dark', 'btn-outline-secondary');\n this.conversationButton.classList.add('btn-outline-dark');\n this.conversationButton.blur();\n this.conversationButton.title = this.conversationButton.getAttribute('data-start-title') || 'Start Conversation';\n var startHtml = this.conversationButton.getAttribute('data-start-html');\n if (startHtml) {\n this.conversationButton.replaceChildren(DOMPurify.sanitize(startHtml, { RETURN_DOM_FRAGMENT: true }));\n }\n }\n },\n conversationModeSendPrompt() {\n // Legacy: only used by AudioInput mode's ReceiveTranscript.\n },\n conversationModeOnAudioEnded() {\n // Legacy: in conversation mode, continuation is handled\n // by the persistent stream.\n },\n showTypingIndicator() {\n this.addMessage({\n role: 'indicator',\n htmlContent: config.indicatorTemplate\n });\n },\n hideTypingIndicator() {\n const originalLength = this.messages.length;\n this.messages = this.messages.filter(msg => msg.role !== 'indicator');\n const removedCount = originalLength - this.messages.length;\n return removedCount;\n },\n scrollToBottom() {\n if (!this.autoScroll) {\n return;\n }\n setTimeout(() => {\n this.chatContainer.scrollTop = this.chatContainer.scrollHeight - this.chatContainer.clientHeight;\n }, 50);\n },\n handleUserInput(event) {\n this.prompt = event.target.value;\n },\n getItemId() {\n return this.inputElement.getAttribute('data-interaction-id')\n || this.inputElement.getAttribute('data-item-id');\n },\n getSaveIndicatorElement() {\n if (config.saveIndicatorElementSelector) {\n return document.querySelector(config.saveIndicatorElementSelector);\n }\n\n return document.querySelector('[data-chat-interaction-save-indicator]');\n },\n showSaveIndicator(text, className) {\n const indicator = this.getSaveIndicatorElement();\n if (!indicator) {\n return;\n }\n\n indicator.textContent = text || '';\n indicator.className = 'settings-save-indicator ' + (className || 'text-muted');\n\n if (this.saveIndicatorTimeout) {\n clearTimeout(this.saveIndicatorTimeout);\n this.saveIndicatorTimeout = null;\n }\n\n if (!text) {\n return;\n }\n\n this.saveIndicatorTimeout = setTimeout(() => {\n indicator.textContent = '';\n this.saveIndicatorTimeout = null;\n }, 3000);\n },\n clearPendingSettingsSave() {\n if (this.saveSettingsTimeout) {\n clearTimeout(this.saveSettingsTimeout);\n this.saveSettingsTimeout = null;\n }\n },\n getFieldLabel(input) {\n const label = input\n .closest('.mb-3, .col-6, .col, .form-group, .form-floating')\n ?.querySelector('label');\n\n if (!label) {\n return input.dataset.setting || 'This field';\n }\n\n return (label.textContent || '')\n .replace(/\\s+/g, ' ')\n .trim()\n || input.dataset.setting\n || 'This field';\n },\n getValidationFeedbackElement(input) {\n let feedback = input.nextElementSibling;\n if (feedback && feedback.classList.contains('invalid-feedback')) {\n return feedback;\n }\n\n feedback = document.createElement('div');\n feedback.className = 'invalid-feedback';\n input.insertAdjacentElement('afterend', feedback);\n\n return feedback;\n },\n clearSettingValidationError(input) {\n input.classList.remove('is-invalid');\n input.removeAttribute('aria-invalid');\n\n const feedback = input.nextElementSibling;\n if (feedback && feedback.classList.contains('invalid-feedback')) {\n feedback.textContent = '';\n }\n },\n setSettingValidationError(input, message) {\n const feedback = this.getValidationFeedbackElement(input);\n input.classList.add('is-invalid');\n input.setAttribute('aria-invalid', 'true');\n feedback.textContent = message;\n },\n getSettingValidationMessage(input) {\n if (!input || input.disabled || input.type !== 'number') {\n return null;\n }\n\n const value = (input.value || '').trim();\n if (!value) {\n return null;\n }\n\n const number = Number(value);\n const fieldLabel = this.getFieldLabel(input);\n\n if (Number.isNaN(number)) {\n return `${fieldLabel} must be a valid number.`;\n }\n\n const min = input.getAttribute('min');\n if (min !== null && number < Number(min)) {\n return `${fieldLabel} must be ${min} or greater.`;\n }\n\n const max = input.getAttribute('max');\n if (max !== null && number > Number(max)) {\n return `${fieldLabel} must be ${max} or less.`;\n }\n\n return null;\n },\n validateSettingInput(input) {\n const message = this.getSettingValidationMessage(input);\n if (!message) {\n this.clearSettingValidationError(input);\n return true;\n }\n\n this.setSettingValidationError(input, message);\n return false;\n },\n validateSettings() {\n let isValid = true;\n\n this.getSettingInputs().forEach(input => {\n isValid = this.validateSettingInput(input) && isValid;\n });\n\n return isValid;\n },\n queueSettingsSave() {\n if (!this.validateSettings()) {\n this.clearPendingSettingsSave();\n this.settingsDirty = false;\n this.showSaveIndicator('Fix errors', 'text-danger');\n return;\n }\n\n this.settingsDirty = true;\n this.showSaveIndicator('Saving...', 'text-warning');\n this.debouncedSaveSettings();\n },\n getSettingInputs() {\n const explicitInputs = document.querySelectorAll('.setting-input[data-setting]');\n if (explicitInputs.length > 0) {\n return explicitInputs;\n }\n\n return document.querySelectorAll(\n 'input[name^=\"ChatInteraction.\"]:not([name*=\".Tools[\"]):not([name*=\".Connections[\"]), ' +\n 'select[name^=\"ChatInteraction.\"]:not([name*=\".Tools[\"]):not([name*=\".Connections[\"]), ' +\n 'textarea[name^=\"ChatInteraction.\"]:not([name*=\".Tools[\"]):not([name*=\".Connections[\"])'\n );\n },\n getSelectedGroupValues(groupName, fallbackSelector) {\n const explicitSelections = document.querySelectorAll(`.capability-checkbox[data-save-group=\"${groupName}\"]:checked`);\n if (explicitSelections.length > 0) {\n const values = [];\n\n explicitSelections.forEach(checkbox => {\n const value = checkbox.getAttribute('data-item-id') || checkbox.value;\n if (value) {\n values.push(value);\n }\n });\n\n return values;\n }\n\n if (!fallbackSelector) {\n return [];\n }\n\n const values = [];\n const checkboxes = document.querySelectorAll(fallbackSelector);\n\n checkboxes.forEach(checkbox => {\n const baseName = checkbox.name.replace('.IsSelected', '.ItemId');\n const hiddenInput = document.querySelector(`input[type=\"hidden\"][name=\"${baseName}\"]`);\n\n if (hiddenInput && hiddenInput.value) {\n values.push(hiddenInput.value);\n }\n });\n\n return values;\n },\n getPromptTemplateSelections() {\n const promptTemplates = [];\n\n document.querySelectorAll('.prompt-template-card').forEach(card => {\n const templateIdInput = card.querySelector('.prompt-template-id-input');\n const promptParametersInput = card.querySelector('.prompt-template-parameters-input');\n const templateId = templateIdInput ? templateIdInput.value : card.getAttribute('data-template-id');\n\n if (!templateId) {\n return;\n }\n\n promptTemplates.push({\n templateId: templateId,\n promptParameters: promptParametersInput ? (promptParametersInput.value || '').trim() : ''\n });\n });\n\n return promptTemplates;\n },\n receiveNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n // Replace existing notification with same type, or add new one.\n var idx = this.notifications.findIndex(n => n.type === notification.type);\n if (idx >= 0) {\n this.notifications.splice(idx, 1, notification);\n } else {\n this.notifications.push(notification);\n }\n this.scheduleNotificationDismiss(notification);\n this.scrollToBottom();\n },\n updateNotification(notification) {\n if (!notification || !notification.type) {\n return;\n }\n this.clearNotificationDismiss(notification.type);\n var idx = this.notifications.findIndex(n => n.type === notification.type);\n if (idx >= 0) {\n this.notifications.splice(idx, 1, notification);\n this.scheduleNotificationDismiss(notification);\n this.scrollToBottom();\n }\n },\n scheduleNotificationDismiss(notification) {\n if (!notification || !notification.type || !notification.autoDismissMs || notification.autoDismissMs <= 0) {\n return;\n }\n this.notificationDismissTimers[notification.type] = setTimeout(() => {\n this.removeNotification(notification.type);\n }, notification.autoDismissMs);\n },\n clearNotificationDismiss(notificationType) {\n var timerId = this.notificationDismissTimers[notificationType];\n if (!timerId) {\n return;\n }\n clearTimeout(timerId);\n delete this.notificationDismissTimers[notificationType];\n },\n removeNotification(notificationType) {\n this.clearNotificationDismiss(notificationType);\n this.notifications = this.notifications.filter(n => n.type !== notificationType);\n },\n dismissNotification(notificationType) {\n this.removeNotification(notificationType);\n },\n handleNotificationAction(notificationType, actionName) {\n if (!this.connection) {\n return;\n }\n var itemId = this.getItemId();\n this.connection.invoke('HandleNotificationAction', itemId, notificationType, actionName)\n .catch(err => console.error('Failed to handle notification action:', err));\n },\n setItemId(itemId) {\n this.inputElement.setAttribute('data-interaction-id', itemId || '');\n this.inputElement.setAttribute('data-item-id', itemId || '');\n },\n resetInteraction() {\n this.setItemId('');\n this.isInteractionStarted = false;\n this.messages = [];\n this.showPlaceholder();\n },\n initializeApp() {\n this.inputElement = document.querySelector(config.inputElementSelector);\n this.buttonElement = document.querySelector(config.sendButtonElementSelector);\n this.chatContainer = document.querySelector(config.chatContainerElementSelector);\n this.placeholder = document.querySelector(config.placeholderElementSelector);\n\n const itemId = this.getItemId();\n if (itemId) {\n this.loadInteraction(itemId);\n }\n\n // Pause auto-scroll when the user manually scrolls up during streaming.\n this.chatContainer.addEventListener('scroll', () => {\n if (!this.stream) {\n return;\n }\n const threshold = 30;\n const atBottom = this.chatContainer.scrollHeight - this.chatContainer.clientHeight - this.chatContainer.scrollTop <= threshold;\n this.autoScroll = atBottom;\n });\n\n this.inputElement.addEventListener('keyup', event => {\n if (this.stream != null) {\n return;\n }\n\n if (event.key === \"Enter\" && !event.shiftKey) {\n this.buttonElement.click();\n }\n });\n\n this.inputElement.addEventListener('input', (e) => {\n this.handleUserInput(e);\n\n if (e.target.value.trim()) {\n this.buttonElement.removeAttribute('disabled');\n } else {\n this.buttonElement.setAttribute('disabled', true);\n }\n });\n\n this.inputElement.addEventListener('paste', (e) => {\n // Use setTimeout to allow the paste to complete before checking the value\n setTimeout(() => {\n this.prompt = this.inputElement.value;\n if (this.inputElement.value.trim()) {\n this.buttonElement.removeAttribute('disabled');\n } else {\n this.buttonElement.setAttribute('disabled', true);\n }\n }, 0);\n });\n\n this.buttonElement.addEventListener('click', () => {\n if (this.stream != null) {\n this.stream.dispose();\n this.stream = null;\n\n this.streamingFinished();\n this.hideTypingIndicator();\n\n // Clean up: remove empty assistant message or stop streaming animation.\n if (this.messages.length > 0) {\n const lastMsg = this.messages[this.messages.length - 1];\n if (lastMsg.role === 'assistant' && !lastMsg.content) {\n this.messages.pop();\n } else if (lastMsg.isStreaming) {\n lastMsg.isStreaming = false;\n }\n }\n\n return;\n }\n\n this.sendMessage();\n });\n\n const chatInteractionItems = document.getElementsByClassName('chat-interaction-history-item');\n\n for (var i = 0; i < chatInteractionItems.length; i++) {\n chatInteractionItems[i].addEventListener('click', (e) => {\n e.preventDefault();\n\n var itemId = e.target.getAttribute('data-interaction-id');\n\n if (!itemId) {\n console.error('An element with the class chat-interaction-history-item with no data-interaction-id set.');\n return;\n }\n\n this.loadInteraction(itemId);\n });\n }\n\n for (let i = 0; i < config.messages.length; i++) {\n this.addMessage(config.messages[i]);\n }\n\n // Delegate click for code block copy buttons.\n if (this.chatContainer) {\n this.chatContainer.addEventListener('click', (e) => {\n var btn = e.target.closest('.ai-code-copy-btn');\n if (!btn) {\n return;\n }\n\n var block = btn.closest('.ai-code-block') || btn.closest('pre');\n if (!block) {\n return;\n }\n\n var codeEl = block.querySelector('code');\n if (codeEl) {\n navigator.clipboard.writeText(codeEl.textContent);\n var copiedText = config.codeCopiedText || 'Copied!';\n btn.innerHTML = ' ' + copiedText;\n setTimeout(() => {\n btn.innerHTML = '';\n }, 2000);\n }\n });\n }\n\n document.addEventListener('input', event => {\n if (event.target.matches('.setting-input[data-setting]')) {\n this.validateSettingInput(event.target);\n this.queueSettingsSave();\n }\n });\n\n document.addEventListener('change', event => {\n if (event.target.matches('.setting-input[data-setting], .capability-checkbox[data-save-group], .group-toggle, .ci-agent-global-toggle')) {\n if (event.target.matches('.setting-input[data-setting]')) {\n this.validateSettingInput(event.target);\n }\n\n this.queueSettingsSave();\n return;\n }\n\n if (event.target.closest('.prompt-template-parameters-input, .prompt-template-id-input')) {\n this.queueSettingsSave();\n }\n });\n\n document.addEventListener('click', event => {\n if (!event.target.closest('.prompt-template-add-btn, .remove-prompt-template-btn')) {\n return;\n }\n\n setTimeout(() => {\n this.queueSettingsSave();\n }, 0);\n });\n\n // Add event listener for clear history button\n const clearHistoryBtn = document.getElementById('clearHistoryBtn');\n if (clearHistoryBtn) {\n clearHistoryBtn.addEventListener('click', () => {\n const itemId = clearHistoryBtn.getAttribute('data-interaction-id');\n if (itemId) {\n this.clearHistory(itemId);\n }\n });\n }\n\n // Initialize speech-to-text microphone button.\n if (this.speechToTextEnabled && config.micButtonElementSelector) {\n this.micButton = document.querySelector(config.micButtonElementSelector);\n if (this.micButton) {\n this.micButton.style.display = '';\n this.micButton.addEventListener('click', () => {\n this.toggleRecording();\n });\n }\n }\n\n // Initialize conversation mode button.\n if (this.conversationModeEnabled && config.conversationButtonElementSelector) {\n this.conversationButton = document.querySelector(config.conversationButtonElementSelector);\n if (this.conversationButton) {\n this.conversationButton.addEventListener('click', () => {\n this.toggleConversationMode();\n });\n }\n }\n },\n loadInteraction(itemId) {\n this.connection.invoke(\"LoadInteraction\", itemId).catch(err => console.error(err));\n },\n reloadCurrentInteraction() {\n const itemId = this.getItemId();\n if (itemId) {\n this.loadInteraction(itemId);\n }\n },\n clearHistory(itemId) {\n const self = this;\n confirmDialog({\n title: config.clearHistoryTitle,\n message: config.clearHistoryMessage,\n okText: config.clearHistoryOkText,\n cancelText: config.clearHistoryCancelText,\n callback: function (confirmed) {\n if (confirmed) {\n // Cancel any active stream before clearing history.\n if (self.stream) {\n self.stream.dispose();\n self.stream = null;\n self.hideTypingIndicator();\n self.streamingFinished();\n }\n\n self.connection.invoke(\"ClearHistory\", itemId)\n .catch(err => console.error('Error clearing history:', err));\n }\n }\n });\n },\n debouncedSaveSettings() {\n // Clear any existing timeout to reset the debounce timer\n this.clearPendingSettingsSave();\n\n // Don't save while streaming — it will be saved when streaming completes.\n if (this.stream) {\n return;\n }\n\n // Set a new timeout to save after 850ms of no changes\n this.saveSettingsTimeout = setTimeout(() => {\n if (this.settingsDirty) {\n this.saveSettings();\n this.settingsDirty = false;\n }\n this.saveSettingsTimeout = null;\n }, 850);\n },\n getSelectedToolNames() {\n return this.getSelectedGroupValues(\n 'toolNames',\n 'input[type=\"checkbox\"][name$=\"].IsSelected\"][name^=\"ChatInteraction.Tools[\"]:checked'\n );\n },\n getSelectedMcpConnectionIds() {\n return this.getSelectedGroupValues(\n 'mcpConnectionIds',\n 'input[type=\"checkbox\"][name$=\"].IsSelected\"][name^=\"ChatInteraction.Connections[\"]:checked'\n );\n },\n getSelectedA2AConnectionIds() {\n return this.getSelectedGroupValues('a2aConnectionIds');\n },\n getSelectedAgentNames() {\n return this.getSelectedGroupValues(\n 'agentNames',\n 'input[type=\"checkbox\"][name$=\"].IsSelected\"][name^=\"ChatInteraction.Agents[\"]:checked'\n );\n },\n saveSettings() {\n const itemId = this.getItemId();\n if (!itemId) {\n return Promise.resolve();\n }\n\n if (!this.validateSettings()) {\n this.showSaveIndicator('Fix errors', 'text-danger');\n return Promise.resolve();\n }\n\n const settings = {};\n\n // Collect all form inputs with the \"ChatInteraction.\" prefix generically.\n // This avoids coupling the JS to specific field names — new fields added by\n // any module are automatically included.\n const inputs = this.getSettingInputs();\n\n inputs.forEach(input => {\n const key = input.dataset.setting\n || ((input.name || '').replace('ChatInteraction.', '').replace(/^[A-Z]/, match => match.toLowerCase()));\n\n if (!key) {\n return;\n }\n\n if (input.type === 'checkbox') {\n settings[key] = input.checked;\n } else if (input.type === 'number') {\n settings[key] = input.value ? parseFloat(input.value) : null;\n } else {\n settings[key] = input.value || null;\n }\n });\n\n // Add tool, MCP connection, and agent collections (special handling).\n settings.toolNames = this.getSelectedToolNames();\n settings.mcpConnectionIds = this.getSelectedMcpConnectionIds();\n settings.a2aConnectionIds = this.getSelectedA2AConnectionIds();\n settings.agentNames = this.getSelectedAgentNames();\n\n const promptTemplates = this.getPromptTemplateSelections();\n if (promptTemplates.length > 0) {\n settings.promptTemplates = promptTemplates;\n settings.promptTemplateIds = promptTemplates.map(template => template.templateId);\n }\n\n return this.connection.invoke(\"SaveSettings\", itemId, settings)\n .catch(err => {\n console.error('Error saving settings:', err);\n this.showSaveIndicator('Save failed', 'text-danger');\n });\n },\n flushPendingSave() {\n if (this.saveSettingsTimeout) {\n clearTimeout(this.saveSettingsTimeout);\n this.saveSettingsTimeout = null;\n }\n\n if (this.settingsDirty) {\n this.settingsDirty = false;\n return this.saveSettings();\n }\n\n return Promise.resolve();\n },\n initializeInteraction(itemId, force) {\n if (this.isInteractionStarted && !force) {\n return;\n }\n this.fireEvent(new CustomEvent(\"initializingChatInteraction\", { detail: { itemId: itemId } }));\n this.setItemId(itemId);\n this.isInteractionStarted = true;\n },\n copyResponse(message) {\n navigator.clipboard.writeText(message);\n },\n startRecording() {\n if (this.isRecording || !this.connection) {\n return;\n }\n\n navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } })\n .then(stream => {\n var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')\n ? 'audio/ogg;codecs=opus'\n : MediaRecorder.isTypeSupported('audio/webm;codecs=opus')\n ? 'audio/webm;codecs=opus'\n : 'audio/webm';\n\n this.mediaRecorder = new MediaRecorder(stream, {\n mimeType: mimeType,\n audioBitsPerSecond: 128000,\n });\n\n this.preRecordingPrompt = this.prompt;\n this._audioInputSent = false;\n\n var subject = new signalR.Subject();\n var itemId = this.getItemId();\n var pendingChunk = Promise.resolve();\n\n this.mediaRecorder.addEventListener('dataavailable', (e) => {\n if (e.data && e.data.size > 0) {\n pendingChunk = pendingChunk.then(async () => {\n var data = await e.data.arrayBuffer();\n var uint8Array = new Uint8Array(data);\n var binaryString = uint8Array.reduce(function (str, byte) { return str + String.fromCharCode(byte); }, '');\n var base64 = btoa(binaryString);\n subject.next(base64);\n });\n }\n });\n\n this.mediaRecorder.addEventListener('stop', () => {\n stream.getTracks().forEach(track => track.stop());\n pendingChunk.then(() => subject.complete());\n });\n\n var language = navigator.language || document.documentElement.lang || 'en-US';\n this.connection.send(\"SendAudioStream\", itemId, subject, mimeType, language);\n this.mediaRecorder.start(250);\n this.isRecording = true;\n this.updateMicButton();\n })\n .catch(err => {\n console.error('Microphone access denied:', err);\n });\n },\n stopRecording() {\n if (!this.isRecording || !this.mediaRecorder) {\n return;\n }\n\n this.mediaRecorder.stop();\n this.isRecording = false;\n this.updateMicButton();\n },\n toggleRecording() {\n if (this.isRecording) {\n this.stopRecording();\n } else {\n this.startRecording();\n }\n },\n updateMicButton() {\n if (!this.micButton) {\n return;\n }\n\n if (this.isRecording) {\n this.micButton.classList.add('stt-recording');\n this.micButton.innerHTML = '';\n } else {\n this.micButton.classList.remove('stt-recording');\n this.micButton.innerHTML = '';\n }\n }\n },\n watch: {\n isPlayingAudio() {\n // Reserved for future use — volume-based interrupt detection\n // no longer mutes tracks; browser echo cancellation handles echo.\n },\n isConversationMode(active) {\n if (this.micButton) {\n this.micButton.style.display = active ? 'none' : (this.speechToTextEnabled ? '' : 'none');\n }\n\n if (this.buttonElement) {\n this.buttonElement.style.display = active ? 'none' : '';\n }\n\n if (this.inputElement) {\n this.inputElement.disabled = active;\n if (active) {\n this.inputElement.placeholder = '';\n }\n }\n }\n },\n mounted() {\n (async () => {\n await this.startConnection();\n this.initializeApp();\n })();\n\n window.addEventListener('beforeunload', this.handleBeforeUnload);\n },\n beforeUnmount() {\n window.removeEventListener('beforeunload', this.handleBeforeUnload);\n\n if (this.stream) {\n this.stream.dispose();\n this.stream = null;\n }\n if (this.connection) {\n this.connection.stop();\n }\n },\n template: config.messageTemplate\n }).mount(config.appElementSelector);\n\n return app;\n };\n\n return {\n initialize: initialize\n };\n}();\n\n// Download chart as image via event delegation (DOMPurify strips inline onclick).\ndocument.addEventListener('click', function (e) {\n const btn = e.target.closest('.download-chart-btn');\n if (!btn) {\n return;\n }\n const chartId = btn.getAttribute('data-chart-id');\n const canvas = chartId ? document.getElementById(chartId) : null;\n if (!canvas) {\n console.error('Chart canvas not found:', chartId);\n return;\n }\n const link = document.createElement('a');\n link.download = 'chart-' + chartId + '.png';\n link.href = canvas.toDataURL('image/png');\n link.click();\n});\n\n// Intercept download clicks for data-URI images and convert to blob downloads.\ndocument.addEventListener('click', function (e) {\n const link = e.target.closest('.ai-download-image');\n if (!link) {\n return;\n }\n\n const container = link.closest('.generated-image-container');\n const img = container?.querySelector('img');\n if (!img) {\n return;\n }\n\n const src = img.src;\n if (!src || !src.startsWith('data:')) {\n return; // Normal URL – let the default behaviour handle it.\n }\n\n e.preventDefault();\n\n fetch(src)\n .then(function (res) { return res.blob(); })\n .then(function (blob) {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = link.getAttribute('download') || 'generated-image.png';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n setTimeout(function () { URL.revokeObjectURL(url); }, 100);\n })\n .catch(function (err) { console.error('Failed to download image:', err); });\n});\n"]} diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.js index 34208193..2a3dbe6d 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.js @@ -74,4 +74,5 @@ openPicker(); }); }; -})(window, document); \ No newline at end of file +})(window, document); +//# sourceMappingURL=document-drop-zone.js.map diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.js.map b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.js.map new file mode 100644 index 00000000..b8ced54d --- /dev/null +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["document-drop-zone.js"],"names":["window","document","assignFiles","fileInput","files","dataTransfer","DataTransfer","Array","prototype","forEach","call","file","items","add","dispatchEvent","Event","bubbles","initDocumentDropZone","options","dropZone","querySelector","browseButton","openPicker","click","preventDefaults","event","preventDefault","stopPropagation","setDragState","isActive","classList","toggle","eventName","addEventListener","contains","relatedTarget","length","target","closest"],"mappings":"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","file":"document-drop-zone.js","sourcesContent":["(function (window, document) {\n 'use strict';\n\n function assignFiles(fileInput, files) {\n if (!fileInput || !files) {\n return;\n }\n\n var dataTransfer = new DataTransfer();\n\n Array.prototype.forEach.call(files, function (file) {\n dataTransfer.items.add(file);\n });\n\n fileInput.files = dataTransfer.files;\n fileInput.dispatchEvent(new Event('change', { bubbles: true }));\n }\n\n window.initDocumentDropZone = function initDocumentDropZone(options) {\n if (!options) {\n return;\n }\n\n var dropZone = typeof options.dropZone === 'string'\n ? document.querySelector(options.dropZone)\n : options.dropZone;\n var fileInput = typeof options.fileInput === 'string'\n ? document.querySelector(options.fileInput)\n : options.fileInput;\n var browseButton = typeof options.browseButton === 'string'\n ? document.querySelector(options.browseButton)\n : options.browseButton;\n\n if (!dropZone || !fileInput) {\n return;\n }\n\n function openPicker() {\n fileInput.click();\n }\n\n function preventDefaults(event) {\n event.preventDefault();\n event.stopPropagation();\n }\n\n function setDragState(isActive) {\n dropZone.classList.toggle('document-drop-zone--dragover', isActive);\n }\n\n ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(function (eventName) {\n dropZone.addEventListener(eventName, preventDefaults);\n });\n\n dropZone.addEventListener('dragenter', function () { setDragState(true); });\n dropZone.addEventListener('dragover', function () { setDragState(true); });\n dropZone.addEventListener('dragleave', function (event) {\n if (!dropZone.contains(event.relatedTarget)) {\n setDragState(false);\n }\n });\n dropZone.addEventListener('drop', function (event) {\n setDragState(false);\n var files = event.dataTransfer ? event.dataTransfer.files : null;\n if (files && files.length > 0) {\n assignFiles(fileInput, files);\n }\n });\n\n if (browseButton) {\n browseButton.addEventListener('click', function (event) {\n event.preventDefault();\n event.stopPropagation();\n openPicker();\n });\n }\n\n dropZone.addEventListener('click', function (event) {\n if (event.target.closest('[data-drop-zone-browse]')) {\n return;\n }\n\n openPicker();\n });\n };\n})(window, document);\n"]} \ No newline at end of file diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.min.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.min.js index 1b665c29..eb179f5f 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.min.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.min.js @@ -1 +1,2 @@ !function(e,t){"use strict";e.initDocumentDropZone=function(e){if(e){var n="string"==typeof e.dropZone?t.querySelector(e.dropZone):e.dropZone,r="string"==typeof e.fileInput?t.querySelector(e.fileInput):e.fileInput,o="string"==typeof e.browseButton?t.querySelector(e.browseButton):e.browseButton;n&&r&&(["dragenter","dragover","dragleave","drop"].forEach(function(e){n.addEventListener(e,i)}),n.addEventListener("dragenter",function(){d(!0)}),n.addEventListener("dragover",function(){d(!0)}),n.addEventListener("dragleave",function(e){n.contains(e.relatedTarget)||d(!1)}),n.addEventListener("drop",function(e){d(!1);var t=e.dataTransfer?e.dataTransfer.files:null;t&&t.length>0&&function(e,t){if(e&&t){var n=new DataTransfer;Array.prototype.forEach.call(t,function(e){n.items.add(e)}),e.files=n.files,e.dispatchEvent(new Event("change",{bubbles:!0}))}}(r,t)}),o&&o.addEventListener("click",function(e){e.preventDefault(),e.stopPropagation(),a()}),n.addEventListener("click",function(e){e.target.closest("[data-drop-zone-browse]")||a()}))}function a(){r.click()}function i(e){e.preventDefault(),e.stopPropagation()}function d(e){n.classList.toggle("document-drop-zone--dragover",e)}}}(window,document); +//# sourceMappingURL=document-drop-zone.min.js.map diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.min.js.map b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.min.js.map new file mode 100644 index 00000000..55f9063b --- /dev/null +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/document-drop-zone.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["document-drop-zone.js"],"names":["window","document","initDocumentDropZone","options","dropZone","querySelector","fileInput","browseButton","forEach","eventName","addEventListener","preventDefaults","setDragState","event","contains","relatedTarget","files","dataTransfer","length","DataTransfer","Array","prototype","call","file","items","add","dispatchEvent","Event","bubbles","assignFiles","preventDefault","stopPropagation","openPicker","target","closest","click","isActive","classList","toggle"],"mappings":"CAAA,SAAWA,EAAQC,GACf,aAiBAD,EAAOE,qBAAuB,SAA8BC,GACxD,GAAKA,EAAL,CAIA,IAAIC,EAAuC,iBAArBD,EAAQC,SACxBH,EAASI,cAAcF,EAAQC,UAC/BD,EAAQC,SACVE,EAAyC,iBAAtBH,EAAQG,UACzBL,EAASI,cAAcF,EAAQG,WAC/BH,EAAQG,UACVC,EAA+C,iBAAzBJ,EAAQI,aAC5BN,EAASI,cAAcF,EAAQI,cAC/BJ,EAAQI,aAETH,GAAaE,IAiBlB,CAAC,YAAa,WAAY,YAAa,QAAQE,QAAQ,SAAUC,GAC7DL,EAASM,iBAAiBD,EAAWE,EACzC,GAEAP,EAASM,iBAAiB,YAAa,WAAcE,GAAa,EAAO,GACzER,EAASM,iBAAiB,WAAY,WAAcE,GAAa,EAAO,GACxER,EAASM,iBAAiB,YAAa,SAAUG,GACxCT,EAASU,SAASD,EAAME,gBACzBH,GAAa,EAErB,GACAR,EAASM,iBAAiB,OAAQ,SAAUG,GACxCD,GAAa,GACb,IAAII,EAAQH,EAAMI,aAAeJ,EAAMI,aAAaD,MAAQ,KACxDA,GAASA,EAAME,OAAS,GA7DpC,SAAqBZ,EAAWU,GAC5B,GAAKV,GAAcU,EAAnB,CAIA,IAAIC,EAAe,IAAIE,aAEvBC,MAAMC,UAAUb,QAAQc,KAAKN,EAAO,SAAUO,GAC1CN,EAAaO,MAAMC,IAAIF,EAC3B,GAEAjB,EAAUU,MAAQC,EAAaD,MAC/BV,EAAUoB,cAAc,IAAIC,MAAM,SAAU,CAAEC,SAAS,IATvD,CAUJ,CAiDYC,CAAYvB,EAAWU,EAE/B,GAEIT,GACAA,EAAaG,iBAAiB,QAAS,SAAUG,GAC7CA,EAAMiB,iBACNjB,EAAMkB,kBACNC,GACJ,GAGJ5B,EAASM,iBAAiB,QAAS,SAAUG,GACrCA,EAAMoB,OAAOC,QAAQ,4BAIzBF,GACJ,GA9DA,CAgBA,SAASA,IACL1B,EAAU6B,OACd,CAEA,SAASxB,EAAgBE,GACrBA,EAAMiB,iBACNjB,EAAMkB,iBACV,CAEA,SAASnB,EAAawB,GAClBhC,EAASiC,UAAUC,OAAO,+BAAgCF,EAC9D,CAoCJ,CACH,CArFD,CAqFGpC,OAAQC","file":"document-drop-zone.min.js","sourcesContent":["(function (window, document) {\n 'use strict';\n\n function assignFiles(fileInput, files) {\n if (!fileInput || !files) {\n return;\n }\n\n var dataTransfer = new DataTransfer();\n\n Array.prototype.forEach.call(files, function (file) {\n dataTransfer.items.add(file);\n });\n\n fileInput.files = dataTransfer.files;\n fileInput.dispatchEvent(new Event('change', { bubbles: true }));\n }\n\n window.initDocumentDropZone = function initDocumentDropZone(options) {\n if (!options) {\n return;\n }\n\n var dropZone = typeof options.dropZone === 'string'\n ? document.querySelector(options.dropZone)\n : options.dropZone;\n var fileInput = typeof options.fileInput === 'string'\n ? document.querySelector(options.fileInput)\n : options.fileInput;\n var browseButton = typeof options.browseButton === 'string'\n ? document.querySelector(options.browseButton)\n : options.browseButton;\n\n if (!dropZone || !fileInput) {\n return;\n }\n\n function openPicker() {\n fileInput.click();\n }\n\n function preventDefaults(event) {\n event.preventDefault();\n event.stopPropagation();\n }\n\n function setDragState(isActive) {\n dropZone.classList.toggle('document-drop-zone--dragover', isActive);\n }\n\n ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(function (eventName) {\n dropZone.addEventListener(eventName, preventDefaults);\n });\n\n dropZone.addEventListener('dragenter', function () { setDragState(true); });\n dropZone.addEventListener('dragover', function () { setDragState(true); });\n dropZone.addEventListener('dragleave', function (event) {\n if (!dropZone.contains(event.relatedTarget)) {\n setDragState(false);\n }\n });\n dropZone.addEventListener('drop', function (event) {\n setDragState(false);\n var files = event.dataTransfer ? event.dataTransfer.files : null;\n if (files && files.length > 0) {\n assignFiles(fileInput, files);\n }\n });\n\n if (browseButton) {\n browseButton.addEventListener('click', function (event) {\n event.preventDefault();\n event.stopPropagation();\n openPicker();\n });\n }\n\n dropZone.addEventListener('click', function (event) {\n if (event.target.closest('[data-drop-zone-browse]')) {\n return;\n }\n\n openPicker();\n });\n };\n})(window, document);\n"]} diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.js index fa34f353..1f09d8d8 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.js @@ -52,4 +52,5 @@ } }); window.initAutoGenerateTechnicalName = initAutoGenerateTechnicalName; -})(); \ No newline at end of file +})(); +//# sourceMappingURL=technical-name-generator.js.map diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.js.map b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.js.map new file mode 100644 index 00000000..75503a02 --- /dev/null +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["technical-name-generator.js"],"names":["toTitleCaseNoSpaces","value","trim","split","filter","w","length","map","charAt","toUpperCase","slice","toLowerCase","join","initAutoGenerateTechnicalName","displayId","nameId","document","addEventListener","displayEl","getElementById","nameEl","userEdited","generated","autoEls","querySelectorAll","i","el","getAttribute","window"],"mappings":";;;;;AAAA,CAAC,YAAY;EACX;EACA,SAASA,mBAAmBA,CAACC,KAAK,EAAE;IAClC,OAAO,CAACA,KAAK,IAAI,EAAE,EAChBC,IAAI,CAAC;IACN;IAAA,CACCC,KAAK,CAAC,eAAe,CAAC,CACtBC,MAAM,CAAC,UAAUC,CAAC,EAAE;MAAE,OAAOA,CAAC,CAACC,MAAM,GAAG,CAAC;IAAE,CAAC,CAAC,CAC7CC,GAAG,CAAC,UAAUF,CAAC,EAAE;MAAE,OAAOA,CAAC,CAACG,MAAM,CAAC,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC,GAAGJ,CAAC,CAACK,KAAK,CAAC,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC;IAAE,CAAC,CAAC,CAClFC,IAAI,CAAC,EAAE,CAAC;EACb;EAEA,SAASC,6BAA6BA,CAACC,SAAS,EAAEC,MAAM,EAAE;IACxD,IAAI,CAACD,SAAS,IAAI,CAACC,MAAM,EAAE;IAC3BC,QAAQ,CAACC,gBAAgB,CAAC,kBAAkB,EAAE,YAAY;MACxD,IAAIC,SAAS,GAAGF,QAAQ,CAACG,cAAc,CAACL,SAAS,CAAC;MAClD,IAAIM,MAAM,GAAGJ,QAAQ,CAACG,cAAc,CAACJ,MAAM,CAAC;MAC5C,IAAI,CAACG,SAAS,IAAI,CAACE,MAAM,EAAE;;MAE3B;MACA,IAAIC,UAAU,GAAGD,MAAM,CAACnB,KAAK,CAACC,IAAI,CAAC,CAAC,KAAK,EAAE;;MAE3C;MACAkB,MAAM,CAACH,gBAAgB,CAAC,OAAO,EAAE,YAAY;QAC3CI,UAAU,GAAGD,MAAM,CAACnB,KAAK,CAACC,IAAI,CAAC,CAAC,KAAK,EAAE;MACzC,CAAC,CAAC;;MAEF;MACAgB,SAAS,CAACD,gBAAgB,CAAC,OAAO,EAAE,YAAY;QAC9C,IAAII,UAAU,EAAE;QAChB,IAAIC,SAAS,GAAGtB,mBAAmB,CAACkB,SAAS,CAACjB,KAAK,IAAI,EAAE,CAAC;QAC1DmB,MAAM,CAACnB,KAAK,GAAGqB,SAAS;MAC1B,CAAC,CAAC;IACJ,CAAC,CAAC;EACJ;;EAEA;EACAN,QAAQ,CAACC,gBAAgB,CAAC,kBAAkB,EAAE,YAAY;IACxD,IAAIM,OAAO,GAAGP,QAAQ,CAACQ,gBAAgB,CAAC,uBAAuB,CAAC;IAChE,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGF,OAAO,CAACjB,MAAM,EAAEmB,CAAC,EAAE,EAAE;MACvC,IAAIC,EAAE,GAAGH,OAAO,CAACE,CAAC,CAAC;MACnB,IAAIX,SAAS,GAAGY,EAAE,CAACC,YAAY,CAAC,iBAAiB,CAAC;MAClD,IAAIZ,MAAM,GAAGW,EAAE,CAACC,YAAY,CAAC,cAAc,CAAC;MAC5C,IAAIb,SAAS,IAAIC,MAAM,EAAE;QACvB;QACAF,6BAA6B,CAACC,SAAS,EAAEC,MAAM,CAAC;MAClD;IACF;EACF,CAAC,CAAC;EAEFa,MAAM,CAACf,6BAA6B,GAAGA,6BAA6B;AACtE,CAAC,EAAE,CAAC","file":"technical-name-generator.js","sourcesContent":["(function () {\n // Expose initializer globally so views can call it with specific field IDs.\n function toTitleCaseNoSpaces(value) {\n return (value || '')\n .trim()\n // split on whitespace or common separators or any non-alphanumeric\n .split(/[^A-Za-z0-9]+/)\n .filter(function (w) { return w.length > 0; })\n .map(function (w) { return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase(); })\n .join('');\n }\n\n function initAutoGenerateTechnicalName(displayId, nameId) {\n if (!displayId || !nameId) return;\n document.addEventListener('DOMContentLoaded', function () {\n var displayEl = document.getElementById(displayId);\n var nameEl = document.getElementById(nameId);\n if (!displayEl || !nameEl) return;\n\n // Track whether the user has entered a non-empty value in the Name field.\n var userEdited = nameEl.value.trim() !== '';\n\n // Resume auto-generation only when the Name field is empty again.\n nameEl.addEventListener('input', function () {\n userEdited = nameEl.value.trim() !== '';\n });\n\n // Generate on keyup as requested\n displayEl.addEventListener('keyup', function () {\n if (userEdited) return;\n var generated = toTitleCaseNoSpaces(displayEl.value || '');\n nameEl.value = generated;\n });\n });\n }\n\n // Automatically initialize for any elements using the data attributes.\n document.addEventListener('DOMContentLoaded', function () {\n var autoEls = document.querySelectorAll('[data-auto-tech-name]');\n for (var i = 0; i < autoEls.length; i++) {\n var el = autoEls[i];\n var displayId = el.getAttribute('data-display-id');\n var nameId = el.getAttribute('data-name-id');\n if (displayId && nameId) {\n // call initializer immediately (it will attach listeners)\n initAutoGenerateTechnicalName(displayId, nameId);\n }\n }\n });\n\n window.initAutoGenerateTechnicalName = initAutoGenerateTechnicalName;\n})();\n"]} \ No newline at end of file diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.min.js b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.min.js index 6cff9458..978ca03f 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.min.js +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/scripts/technical-name-generator.min.js @@ -1 +1,2 @@ !function(){function t(t,e){t&&e&&document.addEventListener("DOMContentLoaded",function(){var n=document.getElementById(t),a=document.getElementById(e);if(n&&a){var i=""!==a.value.trim();a.addEventListener("input",function(){i=""!==a.value.trim()}),n.addEventListener("keyup",function(){if(!i){var t=(n.value||""||"").trim().split(/[^A-Za-z0-9]+/).filter(function(t){return t.length>0}).map(function(t){return t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()}).join("");a.value=t}})}})}document.addEventListener("DOMContentLoaded",function(){for(var e=document.querySelectorAll("[data-auto-tech-name]"),n=0;n 0; })\n .map(function (w) { return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase(); })\n .join('');\n }\n\n function initAutoGenerateTechnicalName(displayId, nameId) {\n if (!displayId || !nameId) return;\n document.addEventListener('DOMContentLoaded', function () {\n var displayEl = document.getElementById(displayId);\n var nameEl = document.getElementById(nameId);\n if (!displayEl || !nameEl) return;\n\n // Track whether the user has entered a non-empty value in the Name field.\n var userEdited = nameEl.value.trim() !== '';\n\n // Resume auto-generation only when the Name field is empty again.\n nameEl.addEventListener('input', function () {\n userEdited = nameEl.value.trim() !== '';\n });\n\n // Generate on keyup as requested\n displayEl.addEventListener('keyup', function () {\n if (userEdited) return;\n var generated = toTitleCaseNoSpaces(displayEl.value || '');\n nameEl.value = generated;\n });\n });\n }\n\n // Automatically initialize for any elements using the data attributes.\n document.addEventListener('DOMContentLoaded', function () {\n var autoEls = document.querySelectorAll('[data-auto-tech-name]');\n for (var i = 0; i < autoEls.length; i++) {\n var el = autoEls[i];\n var displayId = el.getAttribute('data-display-id');\n var nameId = el.getAttribute('data-name-id');\n if (displayId && nameId) {\n // call initializer immediately (it will attach listeners)\n initAutoGenerateTechnicalName(displayId, nameId);\n }\n }\n });\n\n window.initAutoGenerateTechnicalName = initAutoGenerateTechnicalName;\n})();\n"]} diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.css b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.css index ab506e2d..26942bf7 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.css +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.css @@ -140,7 +140,7 @@ .ai-chat-widget-messages .ai-chat-message-body { position: relative; - padding: 0 0 1.75rem 0; + padding: 0 0 0.25rem 0; overflow-wrap: break-word; word-break: break-word; min-width: 0; @@ -154,16 +154,17 @@ .ai-chat-widget-messages .message-buttons-container { position: absolute; right: 0; - bottom: 0.25rem; + top: 0; display: inline-flex; gap: 0.35rem; opacity: 0; pointer-events: none; transition: opacity 0.15s ease-in-out; - background: rgba(255, 255, 255, 0.92); + background: #fff; padding: 0.15rem 0.35rem; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + z-index: 1; } .ai-chat-widget-messages .ai-chat-message-item:hover .message-buttons-container, @@ -182,6 +183,12 @@ color: var(--bs-body-color, #212529) !important; } +.ai-chat-widget-messages .message-buttons-container > .button-message-toolbox.tts-playing { + color: var(--bs-primary, #0d6efd) !important; + opacity: 1; + pointer-events: auto; +} + /* ── Notifications ── */ .ai-chat-widget-messages .ai-chat-notification { margin: 0.75rem 0.5rem 0; @@ -312,3 +319,5 @@ bottom: 4.5rem; } } + +/*# sourceMappingURL=chat-widget.css.map */ diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.css.map b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.css.map new file mode 100644 index 00000000..a9a9dd3a --- /dev/null +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["chat-widget.css"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"chat-widget.css","sourcesContent":["/* CrestApps AI Chat Widget */\n:root {\n --ai-widget-primary: var(--bs-secondary, #6c757d);\n --ai-widget-width: 380px;\n --ai-widget-height: 520px;\n}\n\n.ai-chat-widget-toggle {\n position: fixed;\n bottom: 1.25rem;\n right: 1.25rem;\n z-index: 10000;\n width: 52px;\n height: 52px;\n border-radius: 50%;\n background: var(--ai-widget-primary);\n color: white;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.5rem;\n box-shadow: 0 4px 12px rgba(0,0,0,0.2);\n transition: transform 0.2s, box-shadow 0.2s;\n}\n\n.ai-chat-widget-toggle:hover {\n transform: scale(1.1);\n box-shadow: 0 6px 16px rgba(0,0,0,0.3);\n}\n\n.ai-chat-widget-container {\n position: fixed;\n bottom: 5rem;\n right: 1.25rem;\n z-index: 10001;\n width: var(--ai-widget-width);\n height: var(--ai-widget-height);\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.18);\n display: none;\n flex-direction: column;\n overflow: hidden;\n}\n\n.ai-chat-widget-container.open {\n display: flex;\n}\n\n.ai-chat-widget-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n background: var(--ai-widget-primary);\n color: white;\n}\n\n.ai-chat-widget-header .title {\n font-weight: 600;\n font-size: 0.95rem;\n}\n\n.ai-chat-widget-header button {\n background: none;\n border: none;\n color: white;\n cursor: pointer;\n font-size: 1rem;\n padding: 0 0.25rem;\n opacity: 0.8;\n}\n\n.ai-chat-widget-header button:hover {\n opacity: 1;\n}\n\n.ai-chat-widget-profile-select {\n padding: 0.5rem 0.75rem;\n border-bottom: 1px solid #e9ecef;\n background: #f8f9fa;\n}\n\n.ai-chat-widget-profile-select select {\n width: 100%;\n padding: 0.35rem 0.5rem;\n border: 1px solid #ced4da;\n border-radius: 6px;\n font-size: 0.85rem;\n}\n\n.ai-chat-widget-messages {\n flex: 1;\n overflow-y: auto;\n padding: 0.75rem;\n font-size: 0.9rem;\n}\n\n/* ── Widget message items (ai-chat.js Vue template) ── */\n.ai-chat-widget-messages .ai-chat-messages {\n padding: 0.5rem;\n}\n\n.ai-chat-widget-messages .ai-chat-message-item {\n position: relative;\n padding: 0.5rem 0;\n}\n\n.ai-chat-widget-messages .ai-chat-message-item + .ai-chat-message-item::before {\n content: '';\n display: block;\n width: 100%;\n margin-bottom: 0.5rem;\n border-top: 1px solid rgba(33, 37, 41, 0.08);\n}\n\n.ai-chat-widget-messages .ai-chat-msg-role {\n font-size: 0.7rem;\n font-weight: 600;\n margin-bottom: 2px;\n}\n\n.ai-chat-widget-messages .ai-chat-msg-role-user {\n color: #0d6efd;\n}\n\n.ai-chat-widget-messages .ai-chat-msg-role-assistant {\n color: #6f42c1;\n}\n\n.ai-chat-widget-messages .ai-chat-msg-role-assistant i.ai-streaming-icon {\n animation: ai-widget-pulse 1.5s infinite;\n}\n\n.ai-chat-widget-messages .ai-chat-msg-role-assistant i.ai-bot-icon {\n color: #6f42c1;\n}\n\n.ai-chat-widget-messages .ai-chat-message-body {\n position: relative;\n padding: 0 0 0.25rem 0;\n overflow-wrap: break-word;\n word-break: break-word;\n min-width: 0;\n}\n\n.ai-chat-widget-messages .ai-chat-message-body p:last-child {\n margin-bottom: 0;\n}\n\n/* ── Message action buttons (copy, thumbs up/down) ── */\n.ai-chat-widget-messages .message-buttons-container {\n position: absolute;\n right: 0;\n top: 0;\n display: inline-flex;\n gap: 0.35rem;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.15s ease-in-out;\n background: #fff;\n padding: 0.15rem 0.35rem;\n border-radius: 4px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n z-index: 1;\n}\n\n.ai-chat-widget-messages .ai-chat-message-item:hover .message-buttons-container,\n.ai-chat-widget-messages .ai-chat-message-item:focus-within .message-buttons-container {\n opacity: 1;\n pointer-events: auto;\n}\n\n.ai-chat-widget-messages .message-buttons-container > .button-message-toolbox {\n color: var(--bs-secondary-color, #6c757d) !important;\n text-decoration: none;\n}\n\n.ai-chat-widget-messages .message-buttons-container > .button-message-toolbox:hover,\n.ai-chat-widget-messages .message-buttons-container > .button-message-toolbox:focus {\n color: var(--bs-body-color, #212529) !important;\n}\n\n.ai-chat-widget-messages .message-buttons-container > .button-message-toolbox.tts-playing {\n color: var(--bs-primary, #0d6efd) !important;\n opacity: 1;\n pointer-events: auto;\n}\n\n/* ── Notifications ── */\n.ai-chat-widget-messages .ai-chat-notification {\n margin: 0.75rem 0.5rem 0;\n padding: 0.625rem 0.75rem;\n border: 1px solid rgba(33, 37, 41, 0.12);\n border-radius: 0.5rem;\n background: rgba(248, 249, 250, 0.98);\n}\n\n.ai-chat-widget-messages .ai-chat-notification-content {\n display: flex;\n align-items: flex-start;\n gap: 0.5rem;\n}\n\n.ai-chat-widget-messages .ai-chat-notification-icon {\n margin-top: 0.1rem;\n color: var(--bs-secondary-color, #6c757d);\n}\n\n.ai-chat-widget-messages .ai-chat-notification-text {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.ai-chat-widget-messages .ai-chat-notification-dismiss {\n margin-left: auto !important;\n color: var(--bs-secondary-color, #6c757d) !important;\n}\n\n.ai-chat-widget-messages .ai-chat-notification-actions {\n display: flex;\n gap: 0.5rem;\n margin-top: 0.5rem;\n}\n\n/* ── Chart containers inside widget ── */\n.ai-chat-widget-messages .chart-container {\n width: 100%;\n max-width: 100% !important;\n min-height: 280px;\n}\n\n/* ── Code blocks & inline code ── */\n.ai-chat-widget-messages .ai-chat-message-body pre {\n background: #2d2d2d;\n color: #f8f8f2;\n padding: 0.5rem;\n border-radius: 4px;\n overflow-x: auto;\n font-size: 0.8rem;\n}\n\n.ai-chat-widget-messages .ai-chat-message-body code {\n font-size: 0.85em;\n}\n\n@keyframes ai-widget-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n.ai-chat-widget-input {\n border-top: 1px solid #e9ecef;\n padding: 0.5rem 0.75rem;\n display: flex;\n gap: 0.5rem;\n align-items: flex-end;\n}\n\n.ai-chat-widget-input textarea {\n flex: 1;\n border: 1px solid #ced4da;\n border-radius: 8px;\n padding: 0.4rem 0.6rem;\n font-size: 0.9rem;\n resize: none;\n max-height: 80px;\n min-height: 36px;\n line-height: 1.4;\n outline: none;\n}\n\n.ai-chat-widget-input textarea:focus {\n border-color: var(--ai-widget-primary);\n box-shadow: 0 0 0 2px rgba(65,182,112,0.15);\n}\n\n.ai-chat-widget-input button {\n background: var(--ai-widget-primary);\n color: white;\n border: none;\n border-radius: 8px;\n padding: 0.4rem 0.75rem;\n cursor: pointer;\n font-size: 0.9rem;\n white-space: nowrap;\n}\n\n.ai-chat-widget-input button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.ai-chat-widget-status {\n padding: 0.25rem 0.75rem;\n font-size: 0.8rem;\n color: #6c757d;\n display: none;\n}\n\n.ai-chat-widget-status.visible {\n display: block;\n}\n\n.ai-chat-widget-welcome {\n text-align: center;\n color: #6c757d;\n padding: 2rem 1rem;\n font-size: 0.9rem;\n}\n\n@media (max-width: 576px) {\n .ai-chat-widget-container {\n width: calc(100vw - 1rem);\n height: calc(100vh - 6rem);\n right: 0.5rem;\n bottom: 4.5rem;\n }\n}\n"]} diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.min.css b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.min.css index 884718db..160e9140 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.min.css +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/chat-widget.min.css @@ -1 +1 @@ -:root{--ai-widget-primary:var(--bs-secondary, #6c757d);--ai-widget-width:380px;--ai-widget-height:520px}.ai-chat-widget-toggle{position:fixed;bottom:1.25rem;right:1.25rem;z-index:10000;width:52px;height:52px;border-radius:50%;background:var(--ai-widget-primary);color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:1.5rem;box-shadow:0 4px 12px rgba(0,0,0,.2);transition:transform .2s,box-shadow .2s}.ai-chat-widget-toggle:hover{transform:scale(1.1);box-shadow:0 6px 16px rgba(0,0,0,.3)}.ai-chat-widget-container{position:fixed;bottom:5rem;right:1.25rem;z-index:10001;width:var(--ai-widget-width);height:var(--ai-widget-height);background:#fff;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,.18);display:none;flex-direction:column;overflow:hidden}.ai-chat-widget-container.open{display:flex}.ai-chat-widget-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:var(--ai-widget-primary);color:#fff}.ai-chat-widget-header .title{font-weight:600;font-size:.95rem}.ai-chat-widget-header button{background:0 0;border:none;color:#fff;cursor:pointer;font-size:1rem;padding:0 .25rem;opacity:.8}.ai-chat-widget-header button:hover{opacity:1}.ai-chat-widget-profile-select{padding:.5rem .75rem;border-bottom:1px solid #e9ecef;background:#f8f9fa}.ai-chat-widget-profile-select select{width:100%;padding:.35rem .5rem;border:1px solid #ced4da;border-radius:6px;font-size:.85rem}.ai-chat-widget-messages{flex:1;overflow-y:auto;padding:.75rem;font-size:.9rem}.ai-chat-widget-messages .ai-chat-messages{padding:.5rem}.ai-chat-widget-messages .ai-chat-message-item{position:relative;padding:.5rem 0}.ai-chat-widget-messages .ai-chat-message-item+.ai-chat-message-item::before{content:'';display:block;width:100%;margin-bottom:.5rem;border-top:1px solid rgba(33,37,41,.08)}.ai-chat-widget-messages .ai-chat-msg-role{font-size:.7rem;font-weight:600;margin-bottom:2px}.ai-chat-widget-messages .ai-chat-msg-role-user{color:#0d6efd}.ai-chat-widget-messages .ai-chat-msg-role-assistant{color:#6f42c1}.ai-chat-widget-messages .ai-chat-msg-role-assistant i.ai-streaming-icon{animation:ai-widget-pulse 1.5s infinite}.ai-chat-widget-messages .ai-chat-msg-role-assistant i.ai-bot-icon{color:#6f42c1}.ai-chat-widget-messages .ai-chat-message-body{position:relative;padding:0 0 1.75rem 0;overflow-wrap:break-word;word-break:break-word;min-width:0}.ai-chat-widget-messages .ai-chat-message-body p:last-child{margin-bottom:0}.ai-chat-widget-messages .message-buttons-container{position:absolute;right:0;bottom:.25rem;display:inline-flex;gap:.35rem;opacity:0;pointer-events:none;transition:opacity .15s ease-in-out;background:rgba(255,255,255,.92);padding:.15rem .35rem;border-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,.08)}.ai-chat-widget-messages .ai-chat-message-item:focus-within .message-buttons-container,.ai-chat-widget-messages .ai-chat-message-item:hover .message-buttons-container{opacity:1;pointer-events:auto}.ai-chat-widget-messages .message-buttons-container>.button-message-toolbox{color:var(--bs-secondary-color,#6c757d)!important;text-decoration:none}.ai-chat-widget-messages .message-buttons-container>.button-message-toolbox:focus,.ai-chat-widget-messages .message-buttons-container>.button-message-toolbox:hover{color:var(--bs-body-color,#212529)!important}.ai-chat-widget-messages .ai-chat-notification{margin:.75rem .5rem 0;padding:.625rem .75rem;border:1px solid rgba(33,37,41,.12);border-radius:.5rem;background:rgba(248,249,250,.98)}.ai-chat-widget-messages .ai-chat-notification-content{display:flex;align-items:flex-start;gap:.5rem}.ai-chat-widget-messages .ai-chat-notification-icon{margin-top:.1rem;color:var(--bs-secondary-color,#6c757d)}.ai-chat-widget-messages .ai-chat-notification-text{flex:1 1 auto;min-width:0}.ai-chat-widget-messages .ai-chat-notification-dismiss{margin-left:auto!important;color:var(--bs-secondary-color,#6c757d)!important}.ai-chat-widget-messages .ai-chat-notification-actions{display:flex;gap:.5rem;margin-top:.5rem}.ai-chat-widget-messages .chart-container{width:100%;max-width:100%!important;min-height:280px}.ai-chat-widget-messages .ai-chat-message-body pre{background:#2d2d2d;color:#f8f8f2;padding:.5rem;border-radius:4px;overflow-x:auto;font-size:.8rem}.ai-chat-widget-messages .ai-chat-message-body code{font-size:.85em}@keyframes ai-widget-pulse{0%,100%{opacity:1}50%{opacity:.4}}.ai-chat-widget-input{border-top:1px solid #e9ecef;padding:.5rem .75rem;display:flex;gap:.5rem;align-items:flex-end}.ai-chat-widget-input textarea{flex:1;border:1px solid #ced4da;border-radius:8px;padding:.4rem .6rem;font-size:.9rem;resize:none;max-height:80px;min-height:36px;line-height:1.4;outline:0}.ai-chat-widget-input textarea:focus{border-color:var(--ai-widget-primary);box-shadow:0 0 0 2px rgba(65,182,112,.15)}.ai-chat-widget-input button{background:var(--ai-widget-primary);color:#fff;border:none;border-radius:8px;padding:.4rem .75rem;cursor:pointer;font-size:.9rem;white-space:nowrap}.ai-chat-widget-input button:disabled{opacity:.5;cursor:not-allowed}.ai-chat-widget-status{padding:.25rem .75rem;font-size:.8rem;color:#6c757d;display:none}.ai-chat-widget-status.visible{display:block}.ai-chat-widget-welcome{text-align:center;color:#6c757d;padding:2rem 1rem;font-size:.9rem}@media (max-width:576px){.ai-chat-widget-container{width:calc(100vw - 1rem);height:calc(100vh - 6rem);right:.5rem;bottom:4.5rem}} +:root{--ai-widget-primary:var(--bs-secondary, #6c757d);--ai-widget-width:380px;--ai-widget-height:520px}.ai-chat-widget-toggle{position:fixed;bottom:1.25rem;right:1.25rem;z-index:10000;width:52px;height:52px;border-radius:50%;background:var(--ai-widget-primary);color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:1.5rem;box-shadow:0 4px 12px rgba(0,0,0,.2);transition:transform .2s,box-shadow .2s}.ai-chat-widget-toggle:hover{transform:scale(1.1);box-shadow:0 6px 16px rgba(0,0,0,.3)}.ai-chat-widget-container{position:fixed;bottom:5rem;right:1.25rem;z-index:10001;width:var(--ai-widget-width);height:var(--ai-widget-height);background:#fff;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,.18);display:none;flex-direction:column;overflow:hidden}.ai-chat-widget-container.open{display:flex}.ai-chat-widget-header{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;background:var(--ai-widget-primary);color:#fff}.ai-chat-widget-header .title{font-weight:600;font-size:.95rem}.ai-chat-widget-header button{background:0 0;border:none;color:#fff;cursor:pointer;font-size:1rem;padding:0 .25rem;opacity:.8}.ai-chat-widget-header button:hover{opacity:1}.ai-chat-widget-profile-select{padding:.5rem .75rem;border-bottom:1px solid #e9ecef;background:#f8f9fa}.ai-chat-widget-profile-select select{width:100%;padding:.35rem .5rem;border:1px solid #ced4da;border-radius:6px;font-size:.85rem}.ai-chat-widget-messages{flex:1;overflow-y:auto;padding:.75rem;font-size:.9rem}.ai-chat-widget-messages .ai-chat-messages{padding:.5rem}.ai-chat-widget-messages .ai-chat-message-item{position:relative;padding:.5rem 0}.ai-chat-widget-messages .ai-chat-message-item+.ai-chat-message-item::before{content:'';display:block;width:100%;margin-bottom:.5rem;border-top:1px solid rgba(33,37,41,.08)}.ai-chat-widget-messages .ai-chat-msg-role{font-size:.7rem;font-weight:600;margin-bottom:2px}.ai-chat-widget-messages .ai-chat-msg-role-user{color:#0d6efd}.ai-chat-widget-messages .ai-chat-msg-role-assistant{color:#6f42c1}.ai-chat-widget-messages .ai-chat-msg-role-assistant i.ai-streaming-icon{animation:ai-widget-pulse 1.5s infinite}.ai-chat-widget-messages .ai-chat-msg-role-assistant i.ai-bot-icon{color:#6f42c1}.ai-chat-widget-messages .ai-chat-message-body{position:relative;padding:0 0 .25rem 0;overflow-wrap:break-word;word-break:break-word;min-width:0}.ai-chat-widget-messages .ai-chat-message-body p:last-child{margin-bottom:0}.ai-chat-widget-messages .message-buttons-container{position:absolute;right:0;top:0;display:inline-flex;gap:.35rem;opacity:0;pointer-events:none;transition:opacity .15s ease-in-out;background:#fff;padding:.15rem .35rem;border-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,.08);z-index:1}.ai-chat-widget-messages .ai-chat-message-item:focus-within .message-buttons-container,.ai-chat-widget-messages .ai-chat-message-item:hover .message-buttons-container{opacity:1;pointer-events:auto}.ai-chat-widget-messages .message-buttons-container>.button-message-toolbox{color:var(--bs-secondary-color,#6c757d)!important;text-decoration:none}.ai-chat-widget-messages .message-buttons-container>.button-message-toolbox:focus,.ai-chat-widget-messages .message-buttons-container>.button-message-toolbox:hover{color:var(--bs-body-color,#212529)!important}.ai-chat-widget-messages .message-buttons-container>.button-message-toolbox.tts-playing{color:var(--bs-primary,#0d6efd)!important;opacity:1;pointer-events:auto}.ai-chat-widget-messages .ai-chat-notification{margin:.75rem .5rem 0;padding:.625rem .75rem;border:1px solid rgba(33,37,41,.12);border-radius:.5rem;background:rgba(248,249,250,.98)}.ai-chat-widget-messages .ai-chat-notification-content{display:flex;align-items:flex-start;gap:.5rem}.ai-chat-widget-messages .ai-chat-notification-icon{margin-top:.1rem;color:var(--bs-secondary-color,#6c757d)}.ai-chat-widget-messages .ai-chat-notification-text{flex:1 1 auto;min-width:0}.ai-chat-widget-messages .ai-chat-notification-dismiss{margin-left:auto!important;color:var(--bs-secondary-color,#6c757d)!important}.ai-chat-widget-messages .ai-chat-notification-actions{display:flex;gap:.5rem;margin-top:.5rem}.ai-chat-widget-messages .chart-container{width:100%;max-width:100%!important;min-height:280px}.ai-chat-widget-messages .ai-chat-message-body pre{background:#2d2d2d;color:#f8f8f2;padding:.5rem;border-radius:4px;overflow-x:auto;font-size:.8rem}.ai-chat-widget-messages .ai-chat-message-body code{font-size:.85em}@keyframes ai-widget-pulse{0%,100%{opacity:1}50%{opacity:.4}}.ai-chat-widget-input{border-top:1px solid #e9ecef;padding:.5rem .75rem;display:flex;gap:.5rem;align-items:flex-end}.ai-chat-widget-input textarea{flex:1;border:1px solid #ced4da;border-radius:8px;padding:.4rem .6rem;font-size:.9rem;resize:none;max-height:80px;min-height:36px;line-height:1.4;outline:0}.ai-chat-widget-input textarea:focus{border-color:var(--ai-widget-primary);box-shadow:0 0 0 2px rgba(65,182,112,.15)}.ai-chat-widget-input button{background:var(--ai-widget-primary);color:#fff;border:none;border-radius:8px;padding:.4rem .75rem;cursor:pointer;font-size:.9rem;white-space:nowrap}.ai-chat-widget-input button:disabled{opacity:.5;cursor:not-allowed}.ai-chat-widget-status{padding:.25rem .75rem;font-size:.8rem;color:#6c757d;display:none}.ai-chat-widget-status.visible{display:block}.ai-chat-widget-welcome{text-align:center;color:#6c757d;padding:2rem 1rem;font-size:.9rem}@media (max-width:576px){.ai-chat-widget-container{width:calc(100vw - 1rem);height:calc(100vh - 6rem);right:.5rem;bottom:4.5rem}} diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/document-drop-zone.css b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/document-drop-zone.css index 6c30a943..50264bc0 100644 --- a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/document-drop-zone.css +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/document-drop-zone.css @@ -99,3 +99,5 @@ color: var(--bs-secondary-color, #6c757d); font-size: 0.875rem; } + +/*# sourceMappingURL=document-drop-zone.css.map */ diff --git a/src/Resources/CrestApps.AI.Resources/wwwroot/styles/document-drop-zone.css.map b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/document-drop-zone.css.map new file mode 100644 index 00000000..ab1c7a26 --- /dev/null +++ b/src/Resources/CrestApps.AI.Resources/wwwroot/styles/document-drop-zone.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["document-drop-zone.css"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"document-drop-zone.css","sourcesContent":[".attached-documents-panel {\n border: 1px solid var(--bs-border-color, #dee2e6);\n border-radius: 0.75rem;\n background: linear-gradient(180deg, rgba(13, 110, 253, 0.04), rgba(13, 110, 253, 0.01));\n padding: 1rem;\n}\n\n.document-drop-zone {\n position: relative;\n border: 2px dashed rgba(13, 110, 253, 0.35);\n border-radius: 1rem;\n background: rgba(248, 249, 250, 0.8);\n transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;\n}\n\n.document-drop-zone:hover {\n border-color: rgba(13, 110, 253, 0.55);\n background: rgba(248, 249, 250, 0.95);\n}\n\n.document-drop-zone--dragover {\n border-color: var(--bs-primary, #0d6efd);\n background: rgba(13, 110, 253, 0.08);\n box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.12);\n transform: translateY(-1px);\n}\n\n.document-drop-zone__body {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.75rem;\n padding: 1.5rem 1rem;\n text-align: center;\n}\n\n.document-drop-zone__icon {\n width: 3.5rem;\n height: 3.5rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n background: rgba(13, 110, 253, 0.12);\n color: var(--bs-primary, #0d6efd);\n font-size: 1.5rem;\n}\n\n.document-drop-zone__title {\n font-weight: 600;\n}\n\n.document-drop-zone__actions {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: 0.75rem;\n}\n\n.attached-documents-list {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.attached-document-card,\n.pending-doc-row {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 0.875rem;\n border: 1px solid var(--bs-border-color, #dee2e6);\n border-radius: 0.75rem;\n background: #fff;\n}\n\n.attached-document-icon {\n width: 2.25rem;\n height: 2.25rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 0.65rem;\n background: rgba(13, 110, 253, 0.1);\n color: var(--bs-primary, #0d6efd);\n flex-shrink: 0;\n}\n\n.attached-document-meta {\n min-width: 0;\n}\n\n.attached-document-name {\n font-weight: 500;\n word-break: break-word;\n}\n\n.attached-document-size {\n color: var(--bs-secondary-color, #6c757d);\n font-size: 0.875rem;\n}\n"]} diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs index 3da0cde4..4d65160f 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs @@ -50,6 +50,8 @@ public sealed class AIProfileViewModel public string VoiceName { get; set; } + public bool EnableTextToSpeechPlayback { get; set; } + // AI Parameters (from AIProfileMetadata) public string SystemMessage { get; set; } @@ -190,6 +192,7 @@ public static AIProfileViewModel FromProfile(AIProfile profile) ChatMode = chatModeSettings?.ChatMode ?? ChatMode.TextInput, VoiceName = chatModeSettings?.VoiceName, + EnableTextToSpeechPlayback = chatModeSettings?.EnableTextToSpeechPlayback ?? false, LockSystemMessage = settings.LockSystemMessage, IsListable = settings.IsListable, @@ -372,6 +375,7 @@ public void ApplyTo(AIProfile profile) settings.VoiceName = ChatMode == ChatMode.Conversation ? VoiceName?.Trim() : null; + settings.EnableTextToSpeechPlayback = EnableTextToSpeechPlayback; }); var toolNames = SelectedToolNames?.Where(n => !string.IsNullOrWhiteSpace(n)).ToArray(); diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml index e84014f5..d110504b 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml @@ -163,6 +163,14 @@
Select a voice for conversation mode. Leave this empty to use the site's default voice.
+
+
+ + +
+
When enabled, assistant messages show a play button that reads the response aloud using the configured text-to-speech deployment.
+
+
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Index.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Index.cshtml index 548b24c0..8499c86f 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Index.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Index.cshtml @@ -25,7 +25,7 @@ else Title Technical Name Type - Actions + Actions @@ -45,9 +45,18 @@ else
+
+ Chat History + + } + else if (profile.Type == CrestApps.Core.AI.Models.AIProfileType.Utility || profile.Type == CrestApps.Core.AI.Models.AIProfileType.Agent) + { + + Test + } Edit diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Controllers/AIChatController.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Controllers/AIChatController.cs index 81d0ec6d..beb078fc 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Controllers/AIChatController.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Controllers/AIChatController.cs @@ -25,21 +25,11 @@ public AIChatController( _promptStore = promptStore; } - public async Task Index() - { - var profiles = await _profileManager.GetAsync(AIProfileType.Chat); - var sessions = await _sessionManager.PageAsync(1, 50); - - ViewData["Sessions"] = sessions; - - return View(profiles); - } - public async Task Chat(string sessionId) { if (string.IsNullOrEmpty(sessionId)) { - return RedirectToAction(nameof(Index)); + return RedirectToAction("Index", "AIProfile", new { area = "AI" }); } var session = await _sessionManager.FindByIdAsync(sessionId); @@ -86,10 +76,74 @@ public async Task CreateSession(string profileId) [HttpPost] [ValidateAntiForgeryToken] - public async Task DeleteSession(string sessionId) + public async Task DeleteSession(string sessionId, string returnProfileId = null) { await _sessionManager.DeleteAsync(sessionId); - return RedirectToAction(nameof(Index)); + if (!string.IsNullOrEmpty(returnProfileId)) + { + return RedirectToAction(nameof(Sessions), new { profileId = returnProfileId }); + } + + return RedirectToAction("Index", "AIProfile", new { area = "AI" }); + } + + public async Task Sessions(string profileId) + { + if (string.IsNullOrEmpty(profileId)) + { + return RedirectToAction("Index", "AIProfile", new { area = "AI" }); + } + + var profile = await _profileManager.FindByIdAsync(profileId); + + if (profile == null) + { + return NotFound(); + } + + var sessions = await _sessionManager.PageAsync(1, 200, new AIChatSessionQueryContext + { + ProfileId = profileId, + Sorted = true, + }); + + ViewData["Profile"] = profile; + + return View(sessions); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DeleteAllSessions(string profileId) + { + if (!string.IsNullOrEmpty(profileId)) + { + await _sessionManager.DeleteAllAsync(profileId); + } + + return RedirectToAction(nameof(Sessions), new { profileId }); + } + + public async Task Test(string profileId) + { + if (string.IsNullOrEmpty(profileId)) + { + return RedirectToAction("Index", "AIProfile", new { area = "AI" }); + } + + var profile = await _profileManager.FindByIdAsync(profileId); + + if (profile == null) + { + return NotFound(); + } + + if (profile.Type != AIProfileType.Utility && profile.Type != AIProfileType.Agent) + { + return RedirectToAction("Index", "AIProfile", new { area = "AI" }); + } + + return View(profile); } } diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Chat.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Chat.cshtml index a1b65f71..6d4bb8df 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Chat.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Chat.cshtml @@ -31,10 +31,11 @@ Model.TryGetSettings(out chatModeSettings); var configuredChatMode = chatModeSettings?.ChatMode ?? ChatMode.TextInput; var speechToTextEnabled = !string.IsNullOrWhiteSpace(defaultDeploymentSettings.DefaultSpeechToTextDeploymentName); - var textToSpeechEnabled = !string.IsNullOrWhiteSpace(defaultDeploymentSettings.DefaultTextToSpeechDeploymentName); + var hasTtsDeployment = !string.IsNullOrWhiteSpace(defaultDeploymentSettings.DefaultTextToSpeechDeploymentName); + var textToSpeechEnabled = (chatModeSettings?.EnableTextToSpeechPlayback ?? false) && hasTtsDeployment; var chatMode = configuredChatMode switch { - ChatMode.Conversation when speechToTextEnabled && textToSpeechEnabled => ChatMode.Conversation, + ChatMode.Conversation when speechToTextEnabled && hasTtsDeployment => ChatMode.Conversation, ChatMode.AudioInput when speechToTextEnabled => ChatMode.AudioInput, ChatMode.Conversation when speechToTextEnabled => ChatMode.AudioInput, _ => ChatMode.TextInput, @@ -63,7 +64,7 @@ border-top: 1px solid rgba(33, 37, 41, 0.08); } .ai-chat-messages .ai-chat-message-item p:last-child { margin-bottom: 0; } - .ai-chat-messages .ai-chat-message-body { padding: 0 5.5rem 1.75rem 0; } + .ai-chat-messages .ai-chat-message-body { position: relative; padding: 0 0 0.25rem 0; } .ai-chat-msg-role { font-size: 0.75rem; font-weight: 600; margin-bottom: 2px; } .ai-chat-msg-role-user { color: #0d6efd; } .ai-chat-msg-role-assistant { color: #6f42c1; } @@ -72,12 +73,17 @@ .ai-chat-messages .message-buttons-container { position: absolute; right: 0; - bottom: 0.25rem; + top: 0; display: inline-flex; gap: 0.35rem; opacity: 0; pointer-events: none; transition: opacity 0.15s ease-in-out; + background: #fff; + padding: 0.15rem 0.35rem; + border-radius: 4px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + z-index: 1; } .ai-chat-messages .ai-chat-message-item:hover .message-buttons-container, .ai-chat-messages .ai-chat-message-item:focus-within .message-buttons-container { @@ -92,6 +98,11 @@ .ai-chat-messages .message-buttons-container > .button-message-toolbox:focus { color: var(--bs-body-color, #212529) !important; } + .ai-chat-messages .message-buttons-container > .button-message-toolbox.tts-playing { + color: var(--bs-primary, #0d6efd) !important; + opacity: 1; + pointer-events: auto; + } #chat-conversation-btn:not(.active) { background-color: transparent !important; border-color: var(--bs-dark, #212529) !important; @@ -131,7 +142,7 @@ @@ -180,7 +191,7 @@
@section Scripts { - + @@ -202,6 +213,7 @@ micButtonElementSelector: '#chat-mic-btn', conversationButtonElementSelector: '#chat-conversation-btn', ttsVoiceName: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(ttsVoiceName)), + textToSpeechEnabled: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(textToSpeechEnabled)), sessionDocumentsEnabled: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(allowSessionDocuments)), existingDocuments: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(existingDocuments)), documentBarSelector: '#chat-document-bar', diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Index.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Index.cshtml deleted file mode 100644 index c6159f36..00000000 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Index.cshtml +++ /dev/null @@ -1,86 +0,0 @@ -@model IEnumerable -@{ - ViewData["Title"] = "AI Chat"; - var sessions = ViewData["Sessions"] as CrestApps.Core.AI.Models.AIChatSessionResult; -} - -

AI Chat

-
- -
-
-

Chat Profiles

-

Select a profile to start a new chat session.

- - @if (!Model.Any()) - { -
- No chat profiles available. - Create one first. -
- } - else - { -
- @foreach (var profile in Model) - { -
-
-
-
@(profile.DisplayText ?? profile.Name)
- @if (!string.IsNullOrEmpty(profile.WelcomeMessage)) - { -

@profile.WelcomeMessage

- } -
- @if (!string.IsNullOrEmpty(profile.Source)) - { - @profile.Source - } - @profile.Type -
-
- -
-
- } -
- } -
- -
-

Recent Sessions

- @if (sessions != null && sessions.Sessions.Any()) - { -
- @foreach (var session in sessions.Sessions) - { - - } -
- } - else - { -

No chat sessions yet. Start a new chat from a profile.

- } -
-
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Sessions.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Sessions.cshtml new file mode 100644 index 00000000..42c86b09 --- /dev/null +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Sessions.cshtml @@ -0,0 +1,81 @@ +@model CrestApps.Core.AI.Models.AIChatSessionResult +@{ + var profile = ViewData["Profile"] as CrestApps.Core.AI.Models.AIProfile; + ViewData["Title"] = "Chat History - " + (profile?.DisplayText ?? profile?.Name ?? "Unknown"); +} + +
+

Chat History — @(profile?.DisplayText ?? profile?.Name)

+
+
+ + +
+ @if (Model.Sessions.Any()) + { +
+ + +
+ } + + Back to Profiles + +
+
+ +@if (!Model.Sessions.Any()) +{ +
+ No chat sessions found for this profile. Start a new chat to get going. +
+} +else +{ +
+ + + + + + + + + + + @foreach (var session in Model.Sessions.OrderByDescending(s => s.CreatedUtc)) + { + + + + + + + } + +
TitleCreatedLast ActivityActions
+ + @(session.Title ?? "Untitled") + + + @session.CreatedUtc.ToString("g") + + @session.LastActivityUtc.ToString("g") + + + Resume + +
+ + + +
+
+
+} diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Test.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Test.cshtml new file mode 100644 index 00000000..b971b134 --- /dev/null +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/AIChat/Test.cshtml @@ -0,0 +1,127 @@ +@using CrestApps.Core +@using CrestApps.Core.AI.Models +@using CrestApps.Core.Services +@using Microsoft.Extensions.Options +@model CrestApps.Core.AI.Models.AIProfile +@inject CrestApps.Core.Mvc.Web.Services.SiteSettingsStore SiteSettings +@{ + ViewData["Title"] = "Test - " + (Model.DisplayText ?? Model.Name); + var defaultDeploymentSettings = SiteSettings.Get(); + ChatModeProfileSettings chatModeSettings = null; + Model.TryGetSettings(out chatModeSettings); + var textToSpeechEnabled = !string.IsNullOrWhiteSpace(defaultDeploymentSettings.DefaultTextToSpeechDeploymentName); + var ttsVoiceName = chatModeSettings?.VoiceName ?? defaultDeploymentSettings.DefaultTextToSpeechVoiceId; +} + + + + + + +
+

Test — @(Model.DisplayText ?? Model.Name)

+ + Back to Profiles + +
+ +
+
+
+
+
+ Send a message to test this @Model.Type.ToString().ToLower() profile. +
+
+
+
+
+
+ + +
+
+
+
+ +@section Scripts { + + + + + + + + +} diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/Shared/_ChatWidget.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/Shared/_ChatWidget.cshtml index 5a2e464b..4a2b761c 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/Shared/_ChatWidget.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AIChat/Views/Shared/_ChatWidget.cshtml @@ -22,10 +22,11 @@ } var configuredChatMode = profileChatModeSettings?.ChatMode ?? ChatMode.TextInput; var speechToTextEnabled = !string.IsNullOrWhiteSpace(defaultDeploymentSettings.DefaultSpeechToTextDeploymentName); - var textToSpeechEnabled = !string.IsNullOrWhiteSpace(defaultDeploymentSettings.DefaultTextToSpeechDeploymentName); + var hasTtsDeployment = !string.IsNullOrWhiteSpace(defaultDeploymentSettings.DefaultTextToSpeechDeploymentName); + var textToSpeechEnabled = (profileChatModeSettings?.EnableTextToSpeechPlayback ?? false) && hasTtsDeployment; var effectiveChatMode = configuredChatMode switch { - ChatMode.Conversation when speechToTextEnabled && textToSpeechEnabled => ChatMode.Conversation, + ChatMode.Conversation when speechToTextEnabled && hasTtsDeployment => ChatMode.Conversation, ChatMode.AudioInput when speechToTextEnabled => ChatMode.AudioInput, ChatMode.Conversation when speechToTextEnabled => ChatMode.AudioInput, _ => ChatMode.TextInput, @@ -113,7 +114,7 @@ - + diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Controllers/SettingsController.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Controllers/SettingsController.cs index 7f4b99d0..39ef2ae1 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Controllers/SettingsController.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Controllers/SettingsController.cs @@ -72,6 +72,7 @@ public async Task Index() EnableDistributedCaching = settings.EnableDistributedCaching, EnableOpenTelemetry = settings.EnableOpenTelemetry, ChatInteractionChatMode = chatInteractionSettings.ChatMode, + ChatInteractionEnableTextToSpeechPlayback = chatInteractionSettings.EnableTextToSpeechPlayback, DefaultOrchestratorEnablePreemptiveRag = defaultOrchestratorSettings.EnablePreemptiveRag, MemoryIndexProfileName = memorySettings.IndexProfileName, MemoryTopN = memorySettings.TopN, @@ -208,6 +209,7 @@ public async Task Save(SettingsViewModel model) _siteSettings.Set(settings => { settings.ChatMode = model.ChatInteractionChatMode; + settings.EnableTextToSpeechPlayback = model.ChatInteractionEnableTextToSpeechPlayback; }); _siteSettings.Set(new DefaultOrchestratorSettings diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/ViewModels/SettingsViewModel.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/ViewModels/SettingsViewModel.cs index 7a11c90a..00669b87 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/ViewModels/SettingsViewModel.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/ViewModels/SettingsViewModel.cs @@ -22,6 +22,8 @@ public sealed class SettingsViewModel public ChatMode ChatInteractionChatMode { get; set; } + public bool ChatInteractionEnableTextToSpeechPlayback { get; set; } + public string MemoryIndexProfileName { get; set; } public int MemoryTopN { get; set; } = 5; diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml index 8c40ddd7..3a5da315 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml @@ -153,6 +153,14 @@
Controls whether Chat Interactions use text input only, microphone dictation, or full two-way conversation mode.
+
+
+ + +
+
When enabled, assistant messages in Chat Interactions show a play button that reads the response aloud using the configured text-to-speech deployment.
+
+
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Controllers/ChatInteractionController.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Controllers/ChatInteractionController.cs index f18e5a94..d790a7a7 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Controllers/ChatInteractionController.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Controllers/ChatInteractionController.cs @@ -249,7 +249,7 @@ public async Task Chat(string id) ChatMode = effectiveChatMode, SpeechToTextEnabled = effectiveChatMode is ChatMode.AudioInput or ChatMode.Conversation, ConversationModeEnabled = effectiveChatMode == ChatMode.Conversation, - TextToSpeechEnabled = effectiveChatMode == ChatMode.Conversation, + TextToSpeechEnabled = chatInteractionSettings.EnableTextToSpeechPlayback && hasTextToSpeech, TextToSpeechVoiceName = deploymentDefaults.DefaultTextToSpeechVoiceId, }; diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Hubs/ChatInteractionHub.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Hubs/ChatInteractionHub.cs index e2d4a9de..57e84880 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Hubs/ChatInteractionHub.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Hubs/ChatInteractionHub.cs @@ -178,11 +178,6 @@ public async Task SynthesizeSpeech(string itemId, string text, string voiceName } var chatInteractionSettings = _siteSettings.Get(); - if (chatInteractionSettings.ChatMode != ChatMode.Conversation) - { - await Clients.Caller.ReceiveError("Text-to-speech is not enabled for chat interactions."); - return; - } var textToSpeechDeployment = await _deploymentManager.ResolveOrDefaultAsync(AIDeploymentType.TextToSpeech); if (textToSpeechDeployment is null) diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Models/ChatInteractionSettings.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Models/ChatInteractionSettings.cs index 59c991d7..1e48f813 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Models/ChatInteractionSettings.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Models/ChatInteractionSettings.cs @@ -7,4 +7,10 @@ public sealed class ChatInteractionSettings public ChatMode ChatMode { get; set; } = ChatMode.TextInput; public bool EnableUserMemory { get; set; } = true; + + /// + /// Gets or sets whether to show text-to-speech playback controls on + /// assistant messages in Chat Interactions. Disabled by default. + /// + public bool EnableTextToSpeechPlayback { get; set; } } diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Views/ChatInteraction/Chat.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Views/ChatInteraction/Chat.cshtml index d2399d65..d6eafd3e 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Views/ChatInteraction/Chat.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/ChatInteractions/Views/ChatInteraction/Chat.cshtml @@ -34,6 +34,7 @@ border-top: 1px solid var(--bs-border-color, #dee2e6); } .ai-chat-messages .ai-chat-message-item p:last-child { margin-bottom: 0; } + .ai-chat-messages .ai-chat-message-item .lh-base { position: relative; } .ai-chat-msg-role { font-size: 0.75rem; font-weight: 600; margin-bottom: 2px; } .ai-chat-msg-role-user { color: #0d6efd; } .ai-chat-msg-role-assistant { color: #6f42c1; } @@ -41,12 +42,21 @@ .ai-chat-msg-role-assistant i.ai-bot-icon { color: #6f42c1; } .ai-chat-messages .message-buttons-container { position: absolute; - bottom: 0.25rem; - right: 0.25rem; + right: 0; + top: 0; display: none; + background: #fff; + padding: 0.15rem 0.35rem; + border-radius: 4px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + z-index: 1; } .ai-chat-messages .ai-chat-message-item:hover .message-buttons-container { - display: inline-block; + display: inline-flex; + gap: 0.35rem; + } + .ai-chat-messages .message-buttons-container > .tts-playing { + color: var(--bs-primary, #0d6efd) !important; } @@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } @@ -551,7 +561,7 @@ @section Scripts { - + @@ -1339,13 +1349,46 @@ }, copyResponse: function (content) { navigator.clipboard.writeText(content); + }, + toggleMessageTts: function (message, index) { + if (this.ttsPlayingMessageIndex === index) { + stopAudio(); + this.ttsPlayingMessageIndex = -1; + return; + } + + stopAudio(); + this.ttsPlayingMessageIndex = index; + + if (ttsAudioCache[index]) { + playAudioBlob(ttsAudioCache[index]); + return; + } + + if (!textToSpeechEnabled || !message.content || !connection) { + return; + } + + audioChunks = []; + isPlayingAudio = true; + ttsCacheIndex = index; + + var self = this; + connection.invoke("SynthesizeSpeech", itemId, message.content, ttsVoiceName) + .catch(function (err) { + console.error("TTS synthesis error:", err); + isPlayingAudio = false; + self.ttsPlayingMessageIndex = -1; + ttsCacheIndex = -1; + }); } }, data: function () { return { messages: existingMessages.map(function (m) { return createChatMessage(m.role, m.content, false, false, m.references || m.References); - }) + }), + ttsPlayingMessageIndex: -1 }; }, template: @@ -1363,6 +1406,11 @@ '
' + '
' + '' + + (textToSpeechEnabled + ? '' + : '') + '' + @@ -1398,6 +1446,8 @@ var audioPlayQueue = []; var isPlayingAudio = false; var currentAudioElement = null; + var ttsAudioCache = {}; + var ttsCacheIndex = -1; function toggleDataSourceDependentFields() { var hasDataSource = !!(dataSourceSelect && dataSourceSelect.value && dataSourceSelect.value.trim() !== ''); @@ -1495,6 +1545,7 @@ audioChunks = []; audioPlayQueue = []; isPlayingAudio = false; + vm.ttsPlayingMessageIndex = -1; } function playAudioBlob(blob) { @@ -1514,6 +1565,7 @@ } isPlayingAudio = false; + vm.ttsPlayingMessageIndex = -1; } audio.addEventListener('ended', onEnded); @@ -1545,6 +1597,11 @@ var blob = new Blob([combined], { type: 'audio/mp3' }); + if (ttsCacheIndex >= 0) { + ttsAudioCache[ttsCacheIndex] = blob; + ttsCacheIndex = -1; + } + if (currentAudioElement) { audioPlayQueue.push(blob); return; diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Program.cs b/src/Startup/CrestApps.Core.Mvc.Web/Program.cs index 1f24dca6..d7137424 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Program.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Program.cs @@ -157,6 +157,7 @@ builder.Services.AddSingleton, SiteSettingsConfigureInteractionDocumentOptions>(); builder.Services.AddSingleton, SiteSettingsConfigureAIDataSourceOptions>(); builder.Services.AddSingleton, SiteSettingsConfigureChatInteractionMemoryOptions>(); +builder.Services.AddSingleton, SiteSettingsConfigureDefaultDeploymentOptions>(); // ============================================================================= // 4. ASP.NET CORE MVC SETUP // ============================================================================= diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Services/SiteSettingsOptionsConfigurations.cs b/src/Startup/CrestApps.Core.Mvc.Web/Services/SiteSettingsOptionsConfigurations.cs index 8db04dd0..ad23edea 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Services/SiteSettingsOptionsConfigurations.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Services/SiteSettingsOptionsConfigurations.cs @@ -113,3 +113,30 @@ public void Configure(ChatInteractionMemoryOptions options) options.EnableUserMemory = settings.EnableUserMemory ?? true; } } + +/// +/// Populates from the admin-managed +/// settings stored in so that framework services +/// (e.g. AIChatHubCore) can resolve default deployment names via IOptions. +/// +internal sealed class SiteSettingsConfigureDefaultDeploymentOptions : IConfigureOptions +{ + private readonly SiteSettingsStore _siteSettings; + + public SiteSettingsConfigureDefaultDeploymentOptions(SiteSettingsStore siteSettings) + { + _siteSettings = siteSettings; + } + + public void Configure(DefaultAIDeploymentSettings options) + { + var settings = _siteSettings.Get(); + options.DefaultChatDeploymentName = settings.DefaultChatDeploymentName; + options.DefaultUtilityDeploymentName = settings.DefaultUtilityDeploymentName; + options.DefaultEmbeddingDeploymentName = settings.DefaultEmbeddingDeploymentName; + options.DefaultImageDeploymentName = settings.DefaultImageDeploymentName; + options.DefaultSpeechToTextDeploymentName = settings.DefaultSpeechToTextDeploymentName; + options.DefaultTextToSpeechDeploymentName = settings.DefaultTextToSpeechDeploymentName; + options.DefaultTextToSpeechVoiceId = settings.DefaultTextToSpeechVoiceId; + } +} diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Views/Home/Index.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Views/Home/Index.cshtml index 68980124..0c2582ee 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Views/Home/Index.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Views/Home/Index.cshtml @@ -85,20 +85,7 @@
-
-
-
-
- -
AI Chat
-
-

Manage reusable interaction sessions and orchestration-driven conversation flows powered by AI Profiles.

- - Start Chat - -
-
-
+
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Views/Shared/_Layout.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Views/Shared/_Layout.cshtml index 7f99a375..a2f62508 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Views/Shared/_Layout.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Views/Shared/_Layout.cshtml @@ -7,6 +7,7 @@ +