diff --git a/package-lock.json b/package-lock.json index e008ae2f..d00fdd51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@fullcalendar/common": "^5.11.5", "@microsoft/signalr": "^8.0.0", "@pgrabovets/json-view": "^2.7.5", + "@popperjs/core": "^2.11.8", "@sveltejs/adapter-static": "^3.0.0", "@sveltestrap/sveltestrap": "^6.2.3", "@twilio/voice-sdk": "^2.9.0", diff --git a/package.json b/package.json index 12daac75..bef3fca2 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@fullcalendar/common": "^5.11.5", "@microsoft/signalr": "^8.0.0", "@pgrabovets/json-view": "^2.7.5", + "@popperjs/core": "^2.11.8", "@sveltejs/adapter-static": "^3.0.0", "@sveltestrap/sveltestrap": "^6.2.3", "@twilio/voice-sdk": "^2.9.0", diff --git a/src/lib/common/markdown/Markdown.svelte b/src/lib/common/markdown/Markdown.svelte index 8c95e9ea..70b16578 100644 --- a/src/lib/common/markdown/Markdown.svelte +++ b/src/lib/common/markdown/Markdown.svelte @@ -1,6 +1,10 @@
diff --git a/src/lib/common/tooltip/BotsharpTooltip.svelte b/src/lib/common/tooltip/BotsharpTooltip.svelte new file mode 100644 index 00000000..c1d00bd2 --- /dev/null +++ b/src/lib/common/tooltip/BotsharpTooltip.svelte @@ -0,0 +1,232 @@ + + +{#if isOpen} + + + +{/if} \ No newline at end of file diff --git a/src/lib/helpers/types/agentTypes.js b/src/lib/helpers/types/agentTypes.js index 21253f9b..5cf7c6b6 100644 --- a/src/lib/helpers/types/agentTypes.js +++ b/src/lib/helpers/types/agentTypes.js @@ -145,6 +145,7 @@ * @property {string?} [template_name] * @property {string?} [template_display_name] * @property {string?} [visibility_expression] + * @property {string?} [description] */ /** diff --git a/src/lib/helpers/utils/common.js b/src/lib/helpers/utils/common.js index daa9d96d..6ac0218d 100644 --- a/src/lib/helpers/utils/common.js +++ b/src/lib/helpers/utils/common.js @@ -76,4 +76,9 @@ export function truncateByPrefix(str, prefix) { */ export function removeDuplicates(arr, key) { return [...new Map(arr.map(item => [item[key], item])).values()]; -} \ No newline at end of file +} + +/** + * @param {(string | null)[]} args + */ +export const classnames = (...args) => args.filter(Boolean).join(' '); \ No newline at end of file diff --git a/src/lib/scss/custom/pages/_agent.scss b/src/lib/scss/custom/pages/_agent.scss index 2cddf4b5..15ef802d 100644 --- a/src/lib/scss/custom/pages/_agent.scss +++ b/src/lib/scss/custom/pages/_agent.scss @@ -249,4 +249,21 @@ gap: 5px; flex-wrap: wrap; margin: 5px 0px; +} + +.agent-utility-desc { + .tooltip-inner { + text-align: start; + max-width: fit-content; + padding: 20px; + } + + .markdown-div { + max-height: 500px; + font-size: 15px; + } + + &.show { + opacity: 1 !important; + } } \ No newline at end of file diff --git a/src/lib/services/signalr-service.js b/src/lib/services/signalr-service.js index 83bc2ad8..d5f9c9ae 100644 --- a/src/lib/services/signalr-service.js +++ b/src/lib/services/signalr-service.js @@ -48,7 +48,7 @@ export const signalr = { // create a new connection object with the hub URL and some options let user = getUserStore(); connection = new HubConnectionBuilder() - .withUrl(endpoints.chatHubUrl + `?conversationId=${conversationId}&access_token=${user.token}`) // the hub URL, change it according to your server + .withUrl(endpoints.chatHubUrl + `?conversation-id=${conversationId}&access_token=${user.token}`) // the hub URL, change it according to your server .withAutomaticReconnect() // enable automatic reconnection .configureLogging(LogLevel.Information) // configure the logging level .build(); diff --git a/src/routes/chat/[agentId]/[conversationId]/persist-log/persist-log.svelte b/src/routes/chat/[agentId]/[conversationId]/persist-log/persist-log.svelte index 8d44b09e..85537e98 100644 --- a/src/routes/chat/[agentId]/[conversationId]/persist-log/persist-log.svelte +++ b/src/routes/chat/[agentId]/[conversationId]/persist-log/persist-log.svelte @@ -48,9 +48,9 @@ let selectedTab = contentLogTab; /** @type {import('$conversationTypes').ConversationLogFilter} */ - let contentLogFilter = { size: 100, startTime: utcNow }; + let contentLogFilter = { size: 80, startTime: utcNow }; /** @type {import('$conversationTypes').ConversationLogFilter} */ - let stateLogFilter = { size: 100, startTime: utcNow }; + let stateLogFilter = { size: 80, startTime: utcNow }; const options = { scrollbars: { @@ -59,7 +59,7 @@ autoHideDelay: 100, dragScroll: true, clickScroll: false, - theme: 'os-theme-dark', + theme: 'os-theme-light', pointers: ['mouse', 'touch', 'pen'] } }; @@ -69,7 +69,7 @@ await getChatStateLogs(); initScrollbars(); - scrollToBottom(); + scroll(); }); beforeUpdate(() => {}); @@ -84,17 +84,17 @@ function refresh() { if (autoScroll) { - scrollToBottom(); + scroll(); } - } - function scrollToBottom() { + /** @param {boolean} goToTop */ + function scroll(goToTop = false) { // @ts-ignore scrollbars.forEach(scrollbar => { setTimeout(() => { const { viewport } = scrollbar.elements(); - viewport.scrollTo({ top: viewport.scrollHeight, behavior: 'smooth' }); + viewport.scrollTo({ top: goToTop ? 0 : viewport.scrollHeight, behavior: 'smooth' }); }, 200); }); } @@ -104,18 +104,8 @@ const elem = document.querySelector(item.id); if (!elem) return; + // @ts-ignore const scrollbar = OverlayScrollbars(elem, options); - scrollbar.on("scroll", async (e) => { - const curScrollTop = e.elements().scrollOffsetElement.scrollTop; - if (curScrollTop <= 3 && curScrollTop > 0) { - if (item.type === contentLogTab) { - await getChatContentLogs(); - } else if (item.type === conversationStateLogTab) { - await getChatStateLogs(); - } - } - }); - scrollbars = [ ...scrollbars, scrollbar]; }); } @@ -161,7 +151,7 @@ function handleCleanScreen() { cleanLogs(); - cleanScreen && cleanScreen(); + cleanScreen?.(); } /** @param {number} selected */ @@ -171,6 +161,15 @@ } selectedTab = selected; } + + async function goToTopLog() { + scroll(true); + if (selectedTab === contentLogTab) { + await getChatContentLogs(); + } else if (selectedTab === conversationStateLogTab) { + await getChatStateLogs(); + } + }
@@ -180,9 +179,34 @@ +
+
+ +
@@ -191,7 +215,7 @@ class="btn btn-sm btn-secondary btn-rounded chat-send waves-effect waves-light" on:click={() => closeWindow()} > - +
diff --git a/src/routes/page/agent/[agentId]/agent-components/agent-utility.svelte b/src/routes/page/agent/[agentId]/agent-components/agent-utility.svelte index 69f54f87..e76f2a79 100644 --- a/src/routes/page/agent/[agentId]/agent-components/agent-utility.svelte +++ b/src/routes/page/agent/[agentId]/agent-components/agent-utility.svelte @@ -3,10 +3,15 @@ import { Card, CardBody, Input, Button } from '@sveltestrap/sveltestrap'; import { getAgentUtilityOptions } from '$lib/services/agent-service'; import { truncateByPrefix } from '$lib/helpers/utils/common'; + import Markdown from '$lib/common/markdown/Markdown.svelte'; + import BotSharpTooltip from '$lib/common/tooltip/BotSharpTooltip.svelte'; const limit = 100; const prefix = "util-"; + let windowWidth = 0; + let windowHeight = 0; + /** @type {import('$agentTypes').AgentModel} */ export let agent; @@ -38,7 +43,7 @@ let innerUtilities = []; onMount(async () =>{ - init(); + resizeWindow(); getAgentUtilityOptions().then(data => { const list = data || []; list.forEach(utility => { @@ -55,6 +60,7 @@ contents.push(content); utilityMapper[utility.category] = contents; }); + init(); }); }); @@ -72,6 +78,11 @@ innerRefresh(list); } + function resizeWindow() { + windowWidth = window.innerWidth; + windowHeight = window.innerHeight; + } + /** * @param {any} e * @param {number} idx @@ -164,6 +175,8 @@ if (type === 'function') { foundItem.function_name = null; foundItem.function_display_name = null; + foundItem.template_name = null; + foundItem.template_display_name = null; } else if (type === 'template') { foundItem.template_name = null; foundItem.template_display_name = null; @@ -199,7 +212,7 @@ name: x.name, disabled: x.disabled, visibility_expression: x.visibility_expression, - items: x.items.map(it => ({ ...it })) + items: x.items.map(it => ({ ...it, description: null })) } }) || []; } @@ -233,6 +246,17 @@ return list; } + /** + * @param {string} category + * @param {string} name + * @param {string} key + */ + function getUtilityItemDescription(category, name, key) { + // @ts-ignore + const desc = utilityMapper[category]?.find(x => x.name === name)?.items?.find(x => x.function_name === key)?.description; + return desc || ''; + } + /** @param {number} uid */ function resetUtility(uid) { const found = innerUtilities.find((_, index) => index === uid); @@ -245,6 +269,8 @@ } + resizeWindow()}/> +
@@ -258,7 +284,7 @@ { toggleMergeUtility(e);}} + on:change={e => toggleMergeUtility(e)} />
Merge utilities @@ -275,6 +301,9 @@ {/if} {#each innerUtilities as utility, uid (uid)} + { + @const utilityCategoryOptions = getUtilityOptions(Object.keys(utilityMapper), 'Select a category') + }
@@ -293,7 +322,7 @@ data-bs-placement="top" title="Uncheck to disable utility" > - +
@@ -305,7 +334,7 @@ disabled={utility.disabled} on:change={e => changeUtilityCategory(e, uid)} > - {#each getUtilityOptions(Object.keys(utilityMapper), 'Select a category') as option} + {#each utilityCategoryOptions as option} @@ -326,6 +355,9 @@
{#if utility.category} + { + @const utilityOptions = getUtilityOptions(utilityMapper[utility.category]?.map((/** @type {any} */ x) => x.name), 'Select a utility') + }
@@ -336,6 +368,7 @@ changeUtilityName(e, uid)} > - {#each getUtilityOptions(utilityMapper[utility.category]?.map((/** @type {any} */ x) => x.name), 'Select a utility') as option} + {#each utilityOptions as option} @@ -384,9 +417,35 @@ {#each utility.items as item, fid (fid)}
{#if item.function_name} + { + @const description = getUtilityItemDescription(utility.category, utility.name, item.function_name) + }
-
- {'Function'} +
+
{'Function'}
+ {#if description} +
+ + + + +
+ {/if}