diff --git a/packages/@n8n/chat/package.json b/packages/@n8n/chat/package.json index 6508081f45..6d27169d72 100644 --- a/packages/@n8n/chat/package.json +++ b/packages/@n8n/chat/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "@vueuse/core": "^10.11.0", - "highlight.js": "^11.8.0", + "highlight.js": "catalog:frontend", "markdown-it-link-attributes": "^4.0.1", "uuid": "catalog:", "vue": "catalog:frontend", diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index d46579c6d1..f80861f7d3 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -53,6 +53,7 @@ "fast-json-stable-stringify": "^2.1.0", "file-saver": "^2.0.2", "flatted": "^3.2.4", + "highlight.js": "catalog:frontend", "humanize-duration": "^3.27.2", "jsonpath": "^1.1.1", "lodash-es": "^4.17.21", diff --git a/packages/editor-ui/src/components/RunDataAi/AiRunContentBlock.vue b/packages/editor-ui/src/components/RunDataAi/AiRunContentBlock.vue index a94bf38f8b..7a31030929 100644 --- a/packages/editor-ui/src/components/RunDataAi/AiRunContentBlock.vue +++ b/packages/editor-ui/src/components/RunDataAi/AiRunContentBlock.vue @@ -5,6 +5,7 @@ import { ref, onMounted } from 'vue'; import type { ParsedAiContent } from './useAiContentParsers'; import { useAiContentParsers } from './useAiContentParsers'; import VueMarkdown from 'vue-markdown-render'; +import hljs from 'highlight.js/lib/core'; import { useClipboard } from '@/composables/useClipboard'; import { useI18n } from '@/composables/useI18n'; import { useToast } from '@/composables/useToast'; @@ -38,6 +39,27 @@ function getInitialExpandedState() { return !collapsedTypes[props.runData.inOut].includes(props.runData.type); } +function isJsonString(text: string) { + try { + JSON.parse(text); + return true; + } catch (e) { + return false; + } +} + +const markdownOptions = { + highlight(str: string, lang: string) { + if (lang && hljs.getLanguage(lang)) { + try { + return hljs.highlight(str, { language: lang }).value; + } catch {} + } + + return ''; // use external default escaping + }, +}; + function parseAiRunData(run: IAiDataContent) { if (!run.data) { return; @@ -75,7 +97,13 @@ function jsonToMarkdown(data: JsonMarkdown): string { } if (typeof data === 'string') { - return formatToJsonMarkdown(data); + // If data is a valid JSON string – format it as JSON markdown + if (isJsonString(data)) { + return formatToJsonMarkdown(data); + } + + // Return original string otherwise + return data; } return formatToJsonMarkdown(JSON.stringify(data, null, 2)); @@ -145,10 +173,15 @@ onMounted(() => {

{ } pre { - background-color: var(--color-foreground-light); + background: var(--chat--message--pre--background); border-radius: var(--border-radius-base); line-height: var(--font-line-height-xloose); padding: var(--spacing-s); diff --git a/packages/editor-ui/src/components/RunDataAi/useAiContentParsers.ts b/packages/editor-ui/src/components/RunDataAi/useAiContentParsers.ts index 53275675d6..c7e204548e 100644 --- a/packages/editor-ui/src/components/RunDataAi/useAiContentParsers.ts +++ b/packages/editor-ui/src/components/RunDataAi/useAiContentParsers.ts @@ -47,10 +47,12 @@ const outputTypeParsers: { parsed: true, }; } + // Use the memory parser if the response is a memory-like(chat) object if (response.messages && Array.isArray(response.messages)) { return outputTypeParsers[NodeConnectionType.AiMemory](execData); } + if (response.generations) { const generations = response.generations as LmGeneration[]; @@ -220,8 +222,8 @@ export const useAiContentParsers = () => { } const contentJson = executionData.map((node) => { - const hasBinarData = !isObjectEmpty(node.binary); - return hasBinarData ? node.binary : node.json; + const hasBinaryData = !isObjectEmpty(node.binary); + return hasBinaryData ? node.binary : node.json; }); const parser = outputTypeParsers[endpointType as AllowedEndpointType]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8895ba097b..5aa9c26537 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ catalogs: '@vitest/coverage-v8': specifier: ^1.6.0 version: 1.6.0 + highlight.js: + specifier: ^11.8.0 + version: 11.9.0 vite: specifier: ^5.2.12 version: 5.2.12 @@ -255,7 +258,7 @@ importers: specifier: ^10.11.0 version: 10.11.0(vue@3.4.21(typescript@5.5.2)) highlight.js: - specifier: ^11.8.0 + specifier: catalog:frontend version: 11.9.0 markdown-it-link-attributes: specifier: ^4.0.1 @@ -1340,6 +1343,9 @@ importers: flatted: specifier: ^3.2.4 version: 3.2.7 + highlight.js: + specifier: catalog:frontend + version: 11.9.0 humanize-duration: specifier: ^3.27.2 version: 3.27.3 @@ -13139,8 +13145,8 @@ packages: vue-component-type-helpers@2.0.19: resolution: {integrity: sha512-cN3f1aTxxKo4lzNeQAkVopswuImUrb5Iurll9Gaw5cqpnbTAxtEMM1mgi6ou4X79OCyqYv1U1mzBHJkzmiK82w==} - vue-component-type-helpers@2.1.4: - resolution: {integrity: sha512-aVqB3KxwpM76cYRkpnezl1J62E/1omzHQfx1yuz7zcbxmzmP/PeSgI20NEmkdeGnjZPVzm0V9fB4ZyRu5BBj4A==} + vue-component-type-helpers@2.1.6: + resolution: {integrity: sha512-ng11B8B/ZADUMMOsRbqv0arc442q7lifSubD0v8oDXIFoMg/mXwAPUunrroIDkY+mcD0dHKccdaznSVp8EoX3w==} vue-demi@0.14.5: resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} @@ -18571,7 +18577,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.4.21(typescript@5.5.2) - vue-component-type-helpers: 2.1.4 + vue-component-type-helpers: 2.1.6 transitivePeerDependencies: - encoding - prettier @@ -28055,7 +28061,7 @@ snapshots: vue-component-type-helpers@2.0.19: {} - vue-component-type-helpers@2.1.4: {} + vue-component-type-helpers@2.1.6: {} vue-demi@0.14.5(vue@3.4.21(typescript@5.5.2)): dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e23937456c..aa76b0aa69 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -31,3 +31,4 @@ catalogs: vue: ^3.4.21 vue-tsc: ^2.0.19 vue-markdown-render: ^2.2.1 + highlight.js: ^11.8.0