@n8n/chat 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.cjs +10 -0
  3. package/.np-config.json +5 -0
  4. package/.storybook/main.ts +27 -0
  5. package/.storybook/preview.scss +4 -0
  6. package/.storybook/preview.ts +16 -0
  7. package/.vscode/extensions.json +3 -0
  8. package/build.config.js +21 -0
  9. package/env.d.ts +1 -0
  10. package/index.html +13 -0
  11. package/package.json +2 -34
  12. package/resources/images/fullscreen.png +0 -0
  13. package/resources/images/windowed.png +0 -0
  14. package/resources/workflow-manual.json +238 -0
  15. package/resources/workflow.json +119 -0
  16. package/scripts/pack.js +11 -0
  17. package/scripts/postbuild.js +36 -0
  18. package/src/__stories__/App.stories.ts +43 -0
  19. package/src/__tests__/index.spec.ts +218 -0
  20. package/src/__tests__/utils/create.ts +16 -0
  21. package/src/__tests__/utils/fetch.ts +18 -0
  22. package/src/__tests__/utils/selectors.ts +53 -0
  23. package/src/api/generic.ts +63 -0
  24. package/src/api/message.ts +37 -0
  25. package/src/components/Button.vue +41 -0
  26. package/src/components/ChatWindow.vue +125 -0
  27. package/{components → src/components}/GetStarted.vue +7 -7
  28. package/{components → src/components}/GetStartedFooter.vue +2 -2
  29. package/{components → src/components}/Input.vue +40 -35
  30. package/src/components/Layout.vue +82 -0
  31. package/src/components/Message.vue +97 -0
  32. package/src/components/MessageTyping.vue +109 -0
  33. package/{components → src/components}/MessagesList.vue +4 -4
  34. package/{components → src/components}/PoweredBy.vue +7 -6
  35. package/src/composables/useChat.ts +7 -0
  36. package/src/composables/useI18n.ts +16 -0
  37. package/src/composables/useOptions.ts +11 -0
  38. package/src/constants/defaults.ts +29 -0
  39. package/{constants/localStorage.mjs → src/constants/localStorage.ts} +1 -1
  40. package/src/constants/symbols.ts +8 -0
  41. package/src/css/_tokens.scss +36 -0
  42. package/src/css/index.scss +1 -0
  43. package/src/event-buses/chatEventBus.ts +3 -0
  44. package/src/index.ts +42 -0
  45. package/src/main.scss +5 -0
  46. package/src/plugins/chat.ts +105 -0
  47. package/src/types/chat.ts +12 -0
  48. package/src/types/messages.ts +6 -0
  49. package/src/types/options.ts +28 -0
  50. package/src/types/webhook.ts +17 -0
  51. package/src/utils/event-bus.ts +51 -0
  52. package/src/utils/mount.ts +16 -0
  53. package/tsconfig.json +27 -0
  54. package/vite.config.ts +53 -0
  55. package/vitest.config.ts +20 -0
  56. package/__stories__/App.stories.d.ts +0 -16
  57. package/__stories__/App.stories.js +0 -38
  58. package/__stories__/App.stories.mjs +0 -32
  59. package/__tests__/index.spec.d.ts +0 -1
  60. package/__tests__/index.spec.js +0 -146
  61. package/__tests__/index.spec.mjs +0 -172
  62. package/__tests__/setup.js +0 -3
  63. package/__tests__/setup.mjs +0 -1
  64. package/__tests__/utils/create.d.ts +0 -5
  65. package/__tests__/utils/create.js +0 -16
  66. package/__tests__/utils/create.mjs +0 -10
  67. package/__tests__/utils/fetch.d.ts +0 -3
  68. package/__tests__/utils/fetch.js +0 -20
  69. package/__tests__/utils/fetch.mjs +0 -9
  70. package/__tests__/utils/index.js +0 -38
  71. package/__tests__/utils/index.mjs +0 -3
  72. package/__tests__/utils/selectors.d.ts +0 -12
  73. package/__tests__/utils/selectors.js +0 -58
  74. package/__tests__/utils/selectors.mjs +0 -41
  75. package/api/generic.d.ts +0 -6
  76. package/api/generic.js +0 -68
  77. package/api/generic.mjs +0 -54
  78. package/api/index.js +0 -27
  79. package/api/index.mjs +0 -2
  80. package/api/message.d.ts +0 -3
  81. package/api/message.js +0 -33
  82. package/api/message.mjs +0 -30
  83. package/chat.bundle.es.js +0 -10753
  84. package/chat.bundle.umd.js +0 -18
  85. package/chat.es.js +0 -6864
  86. package/chat.umd.js +0 -18
  87. package/components/Button.vue +0 -34
  88. package/components/ChatWindow.vue +0 -104
  89. package/components/Layout.vue +0 -66
  90. package/components/Message.vue +0 -94
  91. package/components/MessageTyping.vue +0 -101
  92. package/components/index.js +0 -76
  93. package/components/index.mjs +0 -10
  94. package/composables/index.js +0 -38
  95. package/composables/index.mjs +0 -3
  96. package/composables/useChat.d.ts +0 -1
  97. package/composables/useChat.js +0 -11
  98. package/composables/useChat.mjs +0 -5
  99. package/composables/useI18n.d.ts +0 -4
  100. package/composables/useI18n.js +0 -23
  101. package/composables/useI18n.mjs +0 -12
  102. package/composables/useOptions.d.ts +0 -3
  103. package/composables/useOptions.js +0 -14
  104. package/composables/useOptions.mjs +0 -8
  105. package/constants/defaults.d.ts +0 -3
  106. package/constants/defaults.js +0 -32
  107. package/constants/defaults.mjs +0 -26
  108. package/constants/index.js +0 -38
  109. package/constants/index.mjs +0 -3
  110. package/constants/localStorage.d.ts +0 -2
  111. package/constants/localStorage.js +0 -8
  112. package/constants/symbols.d.ts +0 -3
  113. package/constants/symbols.js +0 -8
  114. package/constants/symbols.mjs +0 -2
  115. package/css/index.css +0 -31
  116. package/event-buses/chatEventBus.d.ts +0 -1
  117. package/event-buses/chatEventBus.js +0 -8
  118. package/event-buses/chatEventBus.mjs +0 -2
  119. package/event-buses/index.js +0 -16
  120. package/event-buses/index.mjs +0 -1
  121. package/index.d.ts +0 -3
  122. package/index.js +0 -43
  123. package/index.mjs +0 -36
  124. package/main.css +0 -151
  125. package/plugins/chat.d.ts +0 -3
  126. package/plugins/chat.js +0 -85
  127. package/plugins/chat.mjs +0 -83
  128. package/plugins/index.js +0 -16
  129. package/plugins/index.mjs +0 -1
  130. package/style.css +0 -1
  131. package/types/App.vue.d.ts +0 -8
  132. package/types/__stories__/App.stories.d.ts +0 -17
  133. package/types/__tests__/index.spec.d.ts +0 -1
  134. package/types/__tests__/setup.d.ts +0 -0
  135. package/types/__tests__/utils/create.d.ts +0 -5
  136. package/types/__tests__/utils/fetch.d.ts +0 -4
  137. package/types/__tests__/utils/index.d.ts +0 -3
  138. package/types/__tests__/utils/selectors.d.ts +0 -12
  139. package/types/api/generic.d.ts +0 -6
  140. package/types/api/index.d.ts +0 -2
  141. package/types/api/message.d.ts +0 -3
  142. package/types/chat.d.ts +0 -11
  143. package/types/chat.js +0 -1
  144. package/types/chat.mjs +0 -0
  145. package/types/components/Button.vue.d.ts +0 -9
  146. package/types/components/Chat.vue.d.ts +0 -2
  147. package/types/components/ChatWindow.vue.d.ts +0 -2
  148. package/types/components/GetStarted.vue.d.ts +0 -2
  149. package/types/components/GetStartedFooter.vue.d.ts +0 -2
  150. package/types/components/Input.vue.d.ts +0 -2
  151. package/types/components/Layout.vue.d.ts +0 -11
  152. package/types/components/Message.vue.d.ts +0 -21
  153. package/types/components/MessageTyping.vue.d.ts +0 -15
  154. package/types/components/MessagesList.vue.d.ts +0 -14
  155. package/types/components/PoweredBy.vue.d.ts +0 -2
  156. package/types/components/index.d.ts +0 -10
  157. package/types/composables/index.d.ts +0 -3
  158. package/types/composables/useChat.d.ts +0 -2
  159. package/types/composables/useI18n.d.ts +0 -4
  160. package/types/composables/useOptions.d.ts +0 -4
  161. package/types/constants/defaults.d.ts +0 -3
  162. package/types/constants/index.d.ts +0 -3
  163. package/types/constants/localStorage.d.ts +0 -2
  164. package/types/constants/symbols.d.ts +0 -4
  165. package/types/event-buses/chatEventBus.d.ts +0 -1
  166. package/types/event-buses/index.d.ts +0 -1
  167. package/types/index.js +0 -49
  168. package/types/index.mjs +0 -4
  169. package/types/messages.d.ts +0 -6
  170. package/types/messages.js +0 -1
  171. package/types/messages.mjs +0 -0
  172. package/types/options.d.ts +0 -25
  173. package/types/options.js +0 -1
  174. package/types/options.mjs +0 -0
  175. package/types/plugins/chat.d.ts +0 -3
  176. package/types/plugins/index.d.ts +0 -1
  177. package/types/types/chat.d.ts +0 -11
  178. package/types/types/index.d.ts +0 -4
  179. package/types/types/messages.d.ts +0 -6
  180. package/types/types/options.d.ts +0 -25
  181. package/types/types/webhook.d.ts +0 -15
  182. package/types/utils/event-bus.d.ts +0 -8
  183. package/types/utils/mount.d.ts +0 -1
  184. package/types/webhook.d.ts +0 -15
  185. package/types/webhook.js +0 -1
  186. package/types/webhook.mjs +0 -0
  187. package/utils/event-bus.d.ts +0 -8
  188. package/utils/event-bus.js +0 -38
  189. package/utils/event-bus.mjs +0 -32
  190. package/utils/index.d.ts +0 -2
  191. package/utils/index.js +0 -27
  192. package/utils/index.mjs +0 -2
  193. package/utils/mount.d.ts +0 -1
  194. package/utils/mount.js +0 -19
  195. package/utils/mount.mjs +0 -13
  196. package/{favicon.ico → public/favicon.ico} +0 -0
  197. package/{App.vue → src/App.vue} +1 -1
  198. package/{__tests__/setup.d.ts → src/__tests__/setup.ts} +0 -0
  199. package/{__tests__/utils/index.d.ts → src/__tests__/utils/index.ts} +0 -0
  200. package/{api/index.d.ts → src/api/index.ts} +0 -0
  201. package/{components → src/components}/Chat.vue +1 -1
  202. /package/{components/index.d.ts → src/components/index.ts} +0 -0
  203. /package/{composables/index.d.ts → src/composables/index.ts} +0 -0
  204. /package/{constants/index.d.ts → src/constants/index.ts} +0 -0
  205. /package/{event-buses/index.d.ts → src/event-buses/index.ts} +0 -0
  206. /package/{plugins/index.d.ts → src/plugins/index.ts} +0 -0
  207. /package/{shims.d.ts → src/shims.d.ts} +0 -0
  208. /package/{types/index.d.ts → src/types/index.ts} +0 -0
  209. /package/{types/utils/index.d.ts → src/utils/index.ts} +0 -0
@@ -0,0 +1,105 @@
1
+ import type { Plugin } from 'vue';
2
+ import { computed, nextTick, ref } from 'vue';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import type { ChatMessage, ChatOptions } from '@n8n/chat/types';
5
+ import { chatEventBus } from '@n8n/chat/event-buses';
6
+ import * as api from '@n8n/chat/api';
7
+ import { ChatOptionsSymbol, ChatSymbol, localStorageSessionIdKey } from '@n8n/chat/constants';
8
+
9
+ // eslint-disable-next-line @typescript-eslint/naming-convention
10
+ export const ChatPlugin: Plugin<ChatOptions> = {
11
+ install(app, options) {
12
+ app.provide(ChatOptionsSymbol, options);
13
+
14
+ const messages = ref<ChatMessage[]>([]);
15
+ const currentSessionId = ref<string | null>(null);
16
+ const waitingForResponse = ref(false);
17
+
18
+ const initialMessages = computed<ChatMessage[]>(() =>
19
+ (options.initialMessages ?? []).map((text) => ({
20
+ id: uuidv4(),
21
+ text,
22
+ sender: 'bot',
23
+ createdAt: new Date().toISOString(),
24
+ })),
25
+ );
26
+
27
+ async function sendMessage(text: string) {
28
+ const sentMessage: ChatMessage = {
29
+ id: uuidv4(),
30
+ text,
31
+ sender: 'user',
32
+ createdAt: new Date().toISOString(),
33
+ };
34
+
35
+ messages.value.push(sentMessage);
36
+ waitingForResponse.value = true;
37
+
38
+ void nextTick(() => {
39
+ chatEventBus.emit('scrollToBottom');
40
+ });
41
+
42
+ const sendMessageResponse = await api.sendMessage(
43
+ text,
44
+ currentSessionId.value as string,
45
+ options,
46
+ );
47
+
48
+ const receivedMessage: ChatMessage = {
49
+ id: uuidv4(),
50
+ text: sendMessageResponse.output,
51
+ sender: 'bot',
52
+ createdAt: new Date().toISOString(),
53
+ };
54
+ messages.value.push(receivedMessage);
55
+
56
+ waitingForResponse.value = false;
57
+
58
+ void nextTick(() => {
59
+ chatEventBus.emit('scrollToBottom');
60
+ });
61
+ }
62
+
63
+ async function loadPreviousSession() {
64
+ if (!options.loadPreviousSession) {
65
+ return;
66
+ }
67
+
68
+ const sessionId = localStorage.getItem(localStorageSessionIdKey) ?? uuidv4();
69
+ const previousMessagesResponse = await api.loadPreviousSession(sessionId, options);
70
+ const timestamp = new Date().toISOString();
71
+
72
+ messages.value = (previousMessagesResponse?.data || []).map((message, index) => ({
73
+ id: `${index}`,
74
+ text: message.kwargs.content,
75
+ sender: message.id.includes('HumanMessage') ? 'user' : 'bot',
76
+ createdAt: timestamp,
77
+ }));
78
+
79
+ if (messages.value.length) {
80
+ currentSessionId.value = sessionId;
81
+ }
82
+
83
+ return sessionId;
84
+ }
85
+
86
+ async function startNewSession() {
87
+ currentSessionId.value = uuidv4();
88
+
89
+ localStorage.setItem(localStorageSessionIdKey, currentSessionId.value);
90
+ }
91
+
92
+ const chatStore = {
93
+ initialMessages,
94
+ messages,
95
+ currentSessionId,
96
+ waitingForResponse,
97
+ loadPreviousSession,
98
+ startNewSession,
99
+ sendMessage,
100
+ };
101
+
102
+ app.provide(ChatSymbol, chatStore);
103
+ app.config.globalProperties.$chat = chatStore;
104
+ },
105
+ };
@@ -0,0 +1,12 @@
1
+ import type { Ref } from 'vue';
2
+ import type { ChatMessage } from '@n8n/chat/types/messages';
3
+
4
+ export interface Chat {
5
+ initialMessages: Ref<ChatMessage[]>;
6
+ messages: Ref<ChatMessage[]>;
7
+ currentSessionId: Ref<string | null>;
8
+ waitingForResponse: Ref<boolean>;
9
+ loadPreviousSession: () => Promise<string | undefined>;
10
+ startNewSession: () => Promise<void>;
11
+ sendMessage: (text: string) => Promise<void>;
12
+ }
@@ -0,0 +1,6 @@
1
+ export interface ChatMessage {
2
+ id: string;
3
+ text: string;
4
+ createdAt: string;
5
+ sender: 'user' | 'bot';
6
+ }
@@ -0,0 +1,28 @@
1
+ export interface ChatOptions {
2
+ webhookUrl: string;
3
+ webhookConfig?: {
4
+ method?: 'GET' | 'POST';
5
+ headers?: Record<string, string>;
6
+ };
7
+ target?: string | Element;
8
+ mode?: 'window' | 'fullscreen';
9
+ showWelcomeScreen?: boolean;
10
+ loadPreviousSession?: boolean;
11
+ chatInputKey?: string;
12
+ chatSessionKey?: string;
13
+ defaultLanguage?: 'en';
14
+ initialMessages?: string[];
15
+ metadata?: Record<string, unknown>;
16
+ i18n: Record<
17
+ string,
18
+ {
19
+ title: string;
20
+ subtitle: string;
21
+ footer: string;
22
+ getStarted: string;
23
+ inputPlaceholder: string;
24
+ [message: string]: string;
25
+ }
26
+ >;
27
+ theme?: {};
28
+ }
@@ -0,0 +1,17 @@
1
+ export interface LoadPreviousSessionResponseItem {
2
+ id: string[];
3
+ kwargs: {
4
+ content: string;
5
+ additional_kwargs: Record<string, unknown>;
6
+ };
7
+ lc: number;
8
+ type: string;
9
+ }
10
+
11
+ export interface LoadPreviousSessionResponse {
12
+ data: LoadPreviousSessionResponseItem[];
13
+ }
14
+
15
+ export interface SendMessageResponse {
16
+ output: string;
17
+ }
@@ -0,0 +1,51 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-types
2
+ export type CallbackFn = Function;
3
+ export type UnregisterFn = () => void;
4
+
5
+ export interface EventBus {
6
+ on: (eventName: string, fn: CallbackFn) => UnregisterFn;
7
+ off: (eventName: string, fn: CallbackFn) => void;
8
+ emit: <T = Event>(eventName: string, event?: T) => void;
9
+ }
10
+
11
+ export function createEventBus(): EventBus {
12
+ const handlers = new Map<string, CallbackFn[]>();
13
+
14
+ function off(eventName: string, fn: CallbackFn) {
15
+ const eventFns = handlers.get(eventName);
16
+
17
+ if (eventFns) {
18
+ eventFns.splice(eventFns.indexOf(fn) >>> 0, 1);
19
+ }
20
+ }
21
+
22
+ function on(eventName: string, fn: CallbackFn): UnregisterFn {
23
+ let eventFns = handlers.get(eventName);
24
+
25
+ if (!eventFns) {
26
+ eventFns = [fn];
27
+ } else {
28
+ eventFns.push(fn);
29
+ }
30
+
31
+ handlers.set(eventName, eventFns);
32
+
33
+ return () => off(eventName, fn);
34
+ }
35
+
36
+ function emit<T = Event>(eventName: string, event?: T) {
37
+ const eventFns = handlers.get(eventName);
38
+
39
+ if (eventFns) {
40
+ eventFns.slice().forEach(async (handler) => {
41
+ await handler(event);
42
+ });
43
+ }
44
+ }
45
+
46
+ return {
47
+ on,
48
+ off,
49
+ emit,
50
+ };
51
+ }
@@ -0,0 +1,16 @@
1
+ export function createDefaultMountingTarget(mountingTarget: string) {
2
+ const mountingTargetNode = document.querySelector(mountingTarget);
3
+ if (!mountingTargetNode) {
4
+ const generatedMountingTargetNode = document.createElement('div');
5
+
6
+ if (mountingTarget.startsWith('#')) {
7
+ generatedMountingTargetNode.id = mountingTarget.replace('#', '');
8
+ }
9
+
10
+ if (mountingTarget.startsWith('.')) {
11
+ generatedMountingTargetNode.classList.add(mountingTarget.replace('.', ''));
12
+ }
13
+
14
+ document.body.appendChild(generatedMountingTargetNode);
15
+ }
16
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "outDir": "dist",
6
+ "target": "esnext",
7
+ "module": "esnext",
8
+ "allowJs": true,
9
+ "importHelpers": true,
10
+ "incremental": false,
11
+ "allowSyntheticDefaultImports": true,
12
+ "resolveJsonModule": true,
13
+ "baseUrl": ".",
14
+ "types": ["vitest/globals", "unplugin-icons/types/vue"],
15
+ "paths": {
16
+ "@/*": ["src/*"],
17
+ "@n8n/chat/*": ["src/*"]
18
+ },
19
+ "lib": ["esnext", "dom", "dom.iterable", "scripthost"],
20
+ // TODO: remove all options below this line
21
+ "noUnusedLocals": false,
22
+ "useUnknownInCatchVariables": false
23
+ },
24
+ "include": ["src/**/*.ts", "src/**/*.vue", "**/*.d.ts"]
25
+ }
26
+
27
+
package/vite.config.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { fileURLToPath, URL } from 'node:url';
2
+
3
+ import { defineConfig } from 'vite';
4
+ import { resolve } from 'path';
5
+ import vue from '@vitejs/plugin-vue';
6
+ import icons from 'unplugin-icons/vite';
7
+ import dts from 'vite-plugin-dts';
8
+
9
+ const includeVue = process.env.INCLUDE_VUE === 'true';
10
+ const srcPath = fileURLToPath(new URL('./src', import.meta.url));
11
+
12
+ // https://vitejs.dev/config/
13
+ export default defineConfig({
14
+ plugins: [
15
+ vue(),
16
+ icons({
17
+ compiler: 'vue3',
18
+ }),
19
+ dts(),
20
+ ],
21
+ resolve: {
22
+ alias: {
23
+ '@': srcPath,
24
+ '@n8n/chat': srcPath,
25
+ },
26
+ },
27
+ define: {
28
+ 'process.env.NODE_ENV': process.env.NODE_ENV ? `"${process.env.NODE_ENV}"` : '"development"',
29
+ },
30
+ build: {
31
+ emptyOutDir: !includeVue,
32
+ lib: {
33
+ entry: resolve(__dirname, 'src', 'index.ts'),
34
+ name: 'N8nChat',
35
+ fileName: (format) => (includeVue ? `chat.bundle.${format}.js` : `chat.${format}.js`),
36
+ },
37
+ rollupOptions: {
38
+ // make sure to externalize deps that shouldn't be bundled
39
+ // into your library
40
+ external: includeVue ? [] : ['vue'],
41
+ output: {
42
+ exports: 'named',
43
+ // Provide global variables to use in the UMD build
44
+ // for externalized deps
45
+ globals: includeVue
46
+ ? {}
47
+ : {
48
+ vue: 'Vue',
49
+ },
50
+ },
51
+ },
52
+ },
53
+ });
@@ -0,0 +1,20 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { mergeConfig, defineConfig } from 'vite';
3
+ import { configDefaults } from 'vitest/config';
4
+ import viteConfig from './vite.config';
5
+
6
+ export default mergeConfig(
7
+ viteConfig,
8
+ defineConfig({
9
+ test: {
10
+ globals: true,
11
+ environment: 'jsdom',
12
+ exclude: [...configDefaults.exclude, 'e2e/*'],
13
+ root: fileURLToPath(new URL('./', import.meta.url)),
14
+ setupFiles: ['./src/__tests__/setup.ts'],
15
+ transformMode: {
16
+ web: [/\.[jt]sx$/],
17
+ },
18
+ },
19
+ }),
20
+ );
@@ -1,16 +0,0 @@
1
- import type { StoryObj } from '@storybook/vue3';
2
- declare const meta: {
3
- title: string;
4
- render: (args: ChatOptions) => {
5
- setup(): {};
6
- template: string;
7
- };
8
- parameters: {
9
- layout: string;
10
- };
11
- tags: string[];
12
- };
13
- export default meta;
14
- type Story = StoryObj<typeof meta>;
15
- export declare const Fullscreen: Story;
16
- export declare const Windowed: Story;
@@ -1,38 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- module.exports = exports.Windowed = exports.Fullscreen = void 0;
7
- var _index = require("@n8n/chat/index");
8
- var _vue = require("vue");
9
- const webhookUrl = "http://localhost:5678/webhook/f406671e-c954-4691-b39a-66c90aa2f103/chat";
10
- const meta = {
11
- title: "Chat",
12
- render: args => ({
13
- setup() {
14
- (0, _vue.onMounted)(() => {
15
- (0, _index.createChat)(args);
16
- });
17
- return {};
18
- },
19
- template: '<div id="n8n-chat" />'
20
- }),
21
- parameters: {
22
- layout: "fullscreen"
23
- },
24
- tags: ["autodocs"]
25
- };
26
- module.exports = meta;
27
- const Fullscreen = exports.Fullscreen = {
28
- args: {
29
- webhookUrl,
30
- mode: "fullscreen"
31
- }
32
- };
33
- const Windowed = exports.Windowed = {
34
- args: {
35
- webhookUrl,
36
- mode: "window"
37
- }
38
- };
@@ -1,32 +0,0 @@
1
- import { createChat } from "@n8n/chat/index";
2
- import { onMounted } from "vue";
3
- const webhookUrl = "http://localhost:5678/webhook/f406671e-c954-4691-b39a-66c90aa2f103/chat";
4
- const meta = {
5
- title: "Chat",
6
- render: (args) => ({
7
- setup() {
8
- onMounted(() => {
9
- createChat(args);
10
- });
11
- return {};
12
- },
13
- template: '<div id="n8n-chat" />'
14
- }),
15
- parameters: {
16
- layout: "fullscreen"
17
- },
18
- tags: ["autodocs"]
19
- };
20
- export default meta;
21
- export const Fullscreen = {
22
- args: {
23
- webhookUrl,
24
- mode: "fullscreen"
25
- }
26
- };
27
- export const Windowed = {
28
- args: {
29
- webhookUrl,
30
- mode: "window"
31
- }
32
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,146 +0,0 @@
1
- "use strict";
2
-
3
- var _vue = require("@testing-library/vue");
4
- var _utils = require("@n8n/chat/__tests__/utils");
5
- var _index = require("@n8n/chat/index");
6
- describe("createChat()", () => {
7
- let app;
8
- afterEach(() => {
9
- vi.clearAllMocks();
10
- app.unmount();
11
- });
12
- describe("mode", () => {
13
- it("should create fullscreen chat app with default options", () => {
14
- const fetchSpy = vi.spyOn(window, "fetch");
15
- fetchSpy.mockImplementationOnce((0, _utils.createFetchResponse)((0, _utils.createGetLatestMessagesResponse)()));
16
- app = (0, _index.createChat)({
17
- mode: "fullscreen"
18
- });
19
- expect((0, _utils.getMountingTarget)()).toBeVisible();
20
- expect((0, _utils.getChatWrapper)()).toBeVisible();
21
- expect((0, _utils.getChatWindowWrapper)()).not.toBeInTheDocument();
22
- });
23
- it("should create window chat app with default options", () => {
24
- const fetchSpy = vi.spyOn(window, "fetch");
25
- fetchSpy.mockImplementationOnce((0, _utils.createFetchResponse)((0, _utils.createGetLatestMessagesResponse)()));
26
- app = (0, _index.createChat)({
27
- mode: "window"
28
- });
29
- expect((0, _utils.getMountingTarget)()).toBeDefined();
30
- expect((0, _utils.getChatWindowWrapper)()).toBeVisible();
31
- expect((0, _utils.getChatWrapper)()).not.toBeVisible();
32
- });
33
- it("should open window chat app using toggle button", async () => {
34
- const fetchSpy = vi.spyOn(window, "fetch");
35
- fetchSpy.mockImplementationOnce((0, _utils.createFetchResponse)((0, _utils.createGetLatestMessagesResponse)()));
36
- app = (0, _index.createChat)();
37
- expect((0, _utils.getMountingTarget)()).toBeVisible();
38
- expect((0, _utils.getChatWindowWrapper)()).toBeVisible();
39
- const trigger = (0, _utils.getChatWindowToggle)();
40
- await _vue.fireEvent.click(trigger);
41
- expect((0, _utils.getChatWrapper)()).toBeVisible();
42
- });
43
- });
44
- describe("loadPreviousMessages", () => {
45
- it("should load previous messages on mount", async () => {
46
- const fetchSpy = vi.spyOn(global, "fetch");
47
- fetchSpy.mockImplementation((0, _utils.createFetchResponse)((0, _utils.createGetLatestMessagesResponse)()));
48
- app = (0, _index.createChat)({
49
- mode: "fullscreen",
50
- showWelcomeScreen: true
51
- });
52
- const getStartedButton = (0, _utils.getGetStartedButton)();
53
- await _vue.fireEvent.click(getStartedButton);
54
- expect(fetchSpy.mock.calls[0][1]).toEqual(expect.objectContaining({
55
- method: "POST",
56
- headers: {
57
- "Content-Type": "application/json"
58
- },
59
- body: expect.stringContaining('"action":"loadPreviousSession"'),
60
- mode: "cors",
61
- cache: "no-cache"
62
- }));
63
- });
64
- });
65
- describe("initialMessages", () => {
66
- it.each(["fullscreen", "window"])("should show initial default messages in %s mode", async mode => {
67
- const fetchSpy = vi.spyOn(window, "fetch");
68
- fetchSpy.mockImplementationOnce((0, _utils.createFetchResponse)((0, _utils.createGetLatestMessagesResponse)()));
69
- const initialMessages = ["Hello tester!", "How are you?"];
70
- app = (0, _index.createChat)({
71
- mode,
72
- initialMessages
73
- });
74
- if (mode === "window") {
75
- const trigger = (0, _utils.getChatWindowToggle)();
76
- await _vue.fireEvent.click(trigger);
77
- }
78
- expect((0, _utils.getChatMessages)().length).toBe(initialMessages.length);
79
- expect((0, _utils.getChatMessageByText)(initialMessages[0])).toBeInTheDocument();
80
- expect((0, _utils.getChatMessageByText)(initialMessages[1])).toBeInTheDocument();
81
- });
82
- });
83
- describe("sendMessage", () => {
84
- it.each(["window", "fullscreen"])("should send a message and render a text message in %s mode", async mode => {
85
- const input = "Hello User World!";
86
- const output = "Hello Bot World!";
87
- const fetchSpy = vi.spyOn(window, "fetch");
88
- fetchSpy.mockImplementationOnce((0, _utils.createFetchResponse)(_utils.createGetLatestMessagesResponse)).mockImplementationOnce((0, _utils.createFetchResponse)((0, _utils.createSendMessageResponse)(output)));
89
- app = (0, _index.createChat)({
90
- mode
91
- });
92
- if (mode === "window") {
93
- const trigger = (0, _utils.getChatWindowToggle)();
94
- await _vue.fireEvent.click(trigger);
95
- }
96
- expect((0, _utils.getChatMessageTyping)()).not.toBeInTheDocument();
97
- expect((0, _utils.getChatMessages)().length).toBe(2);
98
- await (0, _vue.waitFor)(() => expect((0, _utils.getChatInputTextarea)()).toBeInTheDocument());
99
- const textarea = (0, _utils.getChatInputTextarea)();
100
- const sendButton = (0, _utils.getChatInputSendButton)();
101
- await _vue.fireEvent.update(textarea, input);
102
- expect(sendButton).not.toBeDisabled();
103
- await _vue.fireEvent.click(sendButton);
104
- expect(fetchSpy.mock.calls[1][1]).toEqual(expect.objectContaining({
105
- method: "POST",
106
- headers: {
107
- "Content-Type": "application/json"
108
- },
109
- body: expect.stringMatching(/"action":"sendMessage"/),
110
- mode: "cors",
111
- cache: "no-cache"
112
- }));
113
- expect(fetchSpy.mock.calls[1][1]?.body).toContain(`"${input}"`);
114
- expect((0, _utils.getChatMessages)().length).toBe(3);
115
- expect((0, _utils.getChatMessageByText)(input)).toBeInTheDocument();
116
- expect((0, _utils.getChatMessageTyping)()).toBeVisible();
117
- await (0, _vue.waitFor)(() => expect((0, _utils.getChatMessageTyping)()).not.toBeInTheDocument());
118
- expect((0, _utils.getChatMessageByText)(output)).toBeInTheDocument();
119
- });
120
- it.each(["fullscreen", "window"])("should send a message and render a code markdown message in %s mode", async mode => {
121
- const input = "Teach me javascript!";
122
- const output = '# Code\n```js\nconsole.log("Hello World!");\n```';
123
- const fetchSpy = vi.spyOn(window, "fetch");
124
- fetchSpy.mockImplementationOnce((0, _utils.createFetchResponse)(_utils.createGetLatestMessagesResponse)).mockImplementationOnce((0, _utils.createFetchResponse)((0, _utils.createSendMessageResponse)(output)));
125
- app = (0, _index.createChat)({
126
- mode
127
- });
128
- if (mode === "window") {
129
- const trigger = (0, _utils.getChatWindowToggle)();
130
- await _vue.fireEvent.click(trigger);
131
- }
132
- await (0, _vue.waitFor)(() => expect((0, _utils.getChatInputTextarea)()).toBeInTheDocument());
133
- const textarea = (0, _utils.getChatInputTextarea)();
134
- const sendButton = (0, _utils.getChatInputSendButton)();
135
- await _vue.fireEvent.update(textarea, input);
136
- await _vue.fireEvent.click(sendButton);
137
- expect((0, _utils.getChatMessageByText)(input)).toBeInTheDocument();
138
- expect((0, _utils.getChatMessages)().length).toBe(3);
139
- await (0, _vue.waitFor)(() => expect((0, _utils.getChatMessageTyping)()).not.toBeInTheDocument());
140
- const lastMessage = (0, _utils.getChatMessage)(-1);
141
- expect(lastMessage).toBeInTheDocument();
142
- expect(lastMessage.querySelector("h1")).toHaveTextContent("Code");
143
- expect(lastMessage.querySelector("code")).toHaveTextContent('console.log("Hello World!");');
144
- });
145
- });
146
- });