@n8n/chat 0.5.2 → 0.6.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 (247) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.cjs +51 -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/README.md +4 -21
  9. package/env.d.ts +1 -0
  10. package/index.html +13 -0
  11. package/package.json +2 -69
  12. package/resources/workflow.json +293 -0
  13. package/scripts/pack.js +11 -0
  14. package/scripts/postbuild.js +16 -0
  15. package/{App.vue → src/App.vue} +2 -2
  16. package/src/__stories__/App.stories.ts +43 -0
  17. package/src/__tests__/index.spec.ts +223 -0
  18. package/src/__tests__/utils/create.ts +16 -0
  19. package/src/__tests__/utils/fetch.ts +18 -0
  20. package/src/__tests__/utils/selectors.ts +53 -0
  21. package/src/api/generic.ts +64 -0
  22. package/src/api/message.ts +31 -0
  23. package/src/components/Button.vue +41 -0
  24. package/{components → src/components}/Chat.vue +15 -19
  25. package/src/components/ChatWindow.vue +125 -0
  26. package/src/components/GetStarted.vue +24 -0
  27. package/{components → src/components}/GetStartedFooter.vue +4 -4
  28. package/{components → src/components}/Input.vue +40 -35
  29. package/src/components/Layout.vue +82 -0
  30. package/src/components/Message.vue +97 -0
  31. package/src/components/MessageTyping.vue +109 -0
  32. package/{components → src/components}/MessagesList.vue +8 -8
  33. package/{components → src/components}/PoweredBy.vue +7 -6
  34. package/src/composables/useChat.ts +7 -0
  35. package/src/composables/useI18n.ts +16 -0
  36. package/src/composables/useOptions.ts +11 -0
  37. package/src/constants/defaults.ts +25 -0
  38. package/{constants/localStorage.mjs → src/constants/localStorage.ts} +1 -1
  39. package/src/constants/symbols.ts +8 -0
  40. package/src/event-buses/chatEventBus.ts +3 -0
  41. package/src/index.ts +42 -0
  42. package/src/main.scss +40 -0
  43. package/src/plugins/chat.ts +101 -0
  44. package/src/types/chat.ts +12 -0
  45. package/src/types/messages.ts +6 -0
  46. package/src/types/options.ts +23 -0
  47. package/src/types/webhook.ts +17 -0
  48. package/src/utils/event-bus.ts +51 -0
  49. package/src/utils/mount.ts +16 -0
  50. package/tsconfig.json +27 -0
  51. package/vite.config.ts +51 -0
  52. package/vitest.config.ts +20 -0
  53. package/__stories__/App.stories.d.ts +0 -16
  54. package/__stories__/App.stories.js +0 -38
  55. package/__stories__/App.stories.mjs +0 -32
  56. package/__tests__/index.spec.d.ts +0 -1
  57. package/__tests__/index.spec.js +0 -146
  58. package/__tests__/index.spec.mjs +0 -172
  59. package/__tests__/setup.js +0 -3
  60. package/__tests__/setup.mjs +0 -1
  61. package/__tests__/utils/create.d.ts +0 -5
  62. package/__tests__/utils/create.js +0 -16
  63. package/__tests__/utils/create.mjs +0 -10
  64. package/__tests__/utils/fetch.d.ts +0 -3
  65. package/__tests__/utils/fetch.js +0 -20
  66. package/__tests__/utils/fetch.mjs +0 -9
  67. package/__tests__/utils/index.js +0 -38
  68. package/__tests__/utils/index.mjs +0 -3
  69. package/__tests__/utils/selectors.d.ts +0 -12
  70. package/__tests__/utils/selectors.js +0 -58
  71. package/__tests__/utils/selectors.mjs +0 -41
  72. package/api/generic.d.ts +0 -6
  73. package/api/generic.js +0 -68
  74. package/api/generic.mjs +0 -54
  75. package/api/index.js +0 -27
  76. package/api/index.mjs +0 -2
  77. package/api/message.d.ts +0 -3
  78. package/api/message.js +0 -33
  79. package/api/message.mjs +0 -30
  80. package/chat.bundle.es.js +0 -10760
  81. package/chat.bundle.umd.js +0 -18
  82. package/components/Button.vue +0 -34
  83. package/components/ChatWindow.vue +0 -104
  84. package/components/GetStarted.vue +0 -24
  85. package/components/Layout.vue +0 -66
  86. package/components/Message.vue +0 -94
  87. package/components/MessageTyping.vue +0 -101
  88. package/components/index.js +0 -76
  89. package/components/index.mjs +0 -10
  90. package/composables/index.js +0 -38
  91. package/composables/index.mjs +0 -3
  92. package/composables/useChat.d.ts +0 -1
  93. package/composables/useChat.js +0 -11
  94. package/composables/useChat.mjs +0 -5
  95. package/composables/useI18n.d.ts +0 -4
  96. package/composables/useI18n.js +0 -23
  97. package/composables/useI18n.mjs +0 -12
  98. package/composables/useOptions.d.ts +0 -3
  99. package/composables/useOptions.js +0 -14
  100. package/composables/useOptions.mjs +0 -8
  101. package/constants/defaults.d.ts +0 -3
  102. package/constants/defaults.js +0 -32
  103. package/constants/defaults.mjs +0 -26
  104. package/constants/index.js +0 -38
  105. package/constants/index.mjs +0 -3
  106. package/constants/localStorage.d.ts +0 -2
  107. package/constants/localStorage.js +0 -8
  108. package/constants/symbols.d.ts +0 -3
  109. package/constants/symbols.js +0 -8
  110. package/constants/symbols.mjs +0 -2
  111. package/css/index.css +0 -31
  112. package/event-buses/chatEventBus.d.ts +0 -1
  113. package/event-buses/chatEventBus.js +0 -8
  114. package/event-buses/chatEventBus.mjs +0 -2
  115. package/event-buses/index.js +0 -16
  116. package/event-buses/index.mjs +0 -1
  117. package/index.d.ts +0 -3
  118. package/index.js +0 -43
  119. package/index.mjs +0 -36
  120. package/main.css +0 -151
  121. package/plugins/chat.d.ts +0 -3
  122. package/plugins/chat.js +0 -85
  123. package/plugins/chat.mjs +0 -83
  124. package/plugins/index.js +0 -16
  125. package/plugins/index.mjs +0 -1
  126. package/style.css +0 -1
  127. package/types/App.vue.d.ts +0 -8
  128. package/types/__stories__/App.stories.d.ts +0 -17
  129. package/types/__tests__/index.spec.d.ts +0 -1
  130. package/types/__tests__/setup.d.ts +0 -0
  131. package/types/__tests__/utils/create.d.ts +0 -5
  132. package/types/__tests__/utils/fetch.d.ts +0 -4
  133. package/types/__tests__/utils/index.d.ts +0 -3
  134. package/types/__tests__/utils/selectors.d.ts +0 -12
  135. package/types/api/generic.d.ts +0 -6
  136. package/types/api/index.d.ts +0 -2
  137. package/types/api/message.d.ts +0 -3
  138. package/types/chat.d.ts +0 -11
  139. package/types/chat.js +0 -1
  140. package/types/chat.mjs +0 -0
  141. package/types/components/Button.vue.d.ts +0 -9
  142. package/types/components/Chat.vue.d.ts +0 -2
  143. package/types/components/ChatWindow.vue.d.ts +0 -2
  144. package/types/components/GetStarted.vue.d.ts +0 -2
  145. package/types/components/GetStartedFooter.vue.d.ts +0 -2
  146. package/types/components/Input.vue.d.ts +0 -2
  147. package/types/components/Layout.vue.d.ts +0 -11
  148. package/types/components/Message.vue.d.ts +0 -21
  149. package/types/components/MessageTyping.vue.d.ts +0 -15
  150. package/types/components/MessagesList.vue.d.ts +0 -14
  151. package/types/components/PoweredBy.vue.d.ts +0 -2
  152. package/types/components/index.d.ts +0 -10
  153. package/types/composables/index.d.ts +0 -3
  154. package/types/composables/useChat.d.ts +0 -2
  155. package/types/composables/useI18n.d.ts +0 -4
  156. package/types/composables/useOptions.d.ts +0 -4
  157. package/types/constants/defaults.d.ts +0 -3
  158. package/types/constants/index.d.ts +0 -3
  159. package/types/constants/localStorage.d.ts +0 -2
  160. package/types/constants/symbols.d.ts +0 -4
  161. package/types/event-buses/chatEventBus.d.ts +0 -1
  162. package/types/event-buses/index.d.ts +0 -1
  163. package/types/index.js +0 -49
  164. package/types/index.mjs +0 -4
  165. package/types/messages.d.ts +0 -6
  166. package/types/messages.js +0 -1
  167. package/types/messages.mjs +0 -0
  168. package/types/options.d.ts +0 -25
  169. package/types/options.js +0 -1
  170. package/types/options.mjs +0 -0
  171. package/types/plugins/chat.d.ts +0 -3
  172. package/types/plugins/index.d.ts +0 -1
  173. package/types/src/App.vue.d.ts +0 -8
  174. package/types/src/__stories__/App.stories.d.ts +0 -17
  175. package/types/src/__tests__/index.spec.d.ts +0 -1
  176. package/types/src/__tests__/setup.d.ts +0 -0
  177. package/types/src/__tests__/utils/create.d.ts +0 -5
  178. package/types/src/__tests__/utils/fetch.d.ts +0 -4
  179. package/types/src/__tests__/utils/index.d.ts +0 -3
  180. package/types/src/__tests__/utils/selectors.d.ts +0 -12
  181. package/types/src/api/generic.d.ts +0 -6
  182. package/types/src/api/index.d.ts +0 -2
  183. package/types/src/api/message.d.ts +0 -3
  184. package/types/src/components/Button.vue.d.ts +0 -9
  185. package/types/src/components/Chat.vue.d.ts +0 -2
  186. package/types/src/components/ChatWindow.vue.d.ts +0 -2
  187. package/types/src/components/GetStarted.vue.d.ts +0 -2
  188. package/types/src/components/GetStartedFooter.vue.d.ts +0 -2
  189. package/types/src/components/Input.vue.d.ts +0 -2
  190. package/types/src/components/Layout.vue.d.ts +0 -11
  191. package/types/src/components/Message.vue.d.ts +0 -21
  192. package/types/src/components/MessageTyping.vue.d.ts +0 -15
  193. package/types/src/components/MessagesList.vue.d.ts +0 -14
  194. package/types/src/components/PoweredBy.vue.d.ts +0 -2
  195. package/types/src/components/index.d.ts +0 -10
  196. package/types/src/composables/index.d.ts +0 -3
  197. package/types/src/composables/useChat.d.ts +0 -2
  198. package/types/src/composables/useI18n.d.ts +0 -4
  199. package/types/src/composables/useOptions.d.ts +0 -4
  200. package/types/src/constants/defaults.d.ts +0 -3
  201. package/types/src/constants/index.d.ts +0 -3
  202. package/types/src/constants/localStorage.d.ts +0 -2
  203. package/types/src/constants/symbols.d.ts +0 -4
  204. package/types/src/event-buses/chatEventBus.d.ts +0 -1
  205. package/types/src/event-buses/index.d.ts +0 -1
  206. package/types/src/index.d.ts +0 -2
  207. package/types/src/plugins/chat.d.ts +0 -3
  208. package/types/src/plugins/index.d.ts +0 -1
  209. package/types/src/types/chat.d.ts +0 -11
  210. package/types/src/types/index.d.ts +0 -4
  211. package/types/src/types/messages.d.ts +0 -6
  212. package/types/src/types/options.d.ts +0 -25
  213. package/types/src/types/webhook.d.ts +0 -15
  214. package/types/src/utils/event-bus.d.ts +0 -8
  215. package/types/src/utils/mount.d.ts +0 -1
  216. package/types/types/chat.d.ts +0 -11
  217. package/types/types/index.d.ts +0 -4
  218. package/types/types/messages.d.ts +0 -6
  219. package/types/types/options.d.ts +0 -25
  220. package/types/types/webhook.d.ts +0 -15
  221. package/types/utils/event-bus.d.ts +0 -8
  222. package/types/utils/index.d.ts +0 -2
  223. package/types/utils/mount.d.ts +0 -1
  224. package/types/webhook.d.ts +0 -15
  225. package/types/webhook.js +0 -1
  226. package/types/webhook.mjs +0 -0
  227. package/utils/event-bus.d.ts +0 -8
  228. package/utils/event-bus.js +0 -38
  229. package/utils/event-bus.mjs +0 -32
  230. package/utils/index.d.ts +0 -2
  231. package/utils/index.js +0 -27
  232. package/utils/index.mjs +0 -2
  233. package/utils/mount.d.ts +0 -1
  234. package/utils/mount.js +0 -19
  235. package/utils/mount.mjs +0 -13
  236. /package/{favicon.ico → public/favicon.ico} +0 -0
  237. /package/{__tests__/setup.d.ts → src/__tests__/setup.ts} +0 -0
  238. /package/{__tests__/utils/index.d.ts → src/__tests__/utils/index.ts} +0 -0
  239. /package/{api/index.d.ts → src/api/index.ts} +0 -0
  240. /package/{components/index.d.ts → src/components/index.ts} +0 -0
  241. /package/{composables/index.d.ts → src/composables/index.ts} +0 -0
  242. /package/{constants/index.d.ts → src/constants/index.ts} +0 -0
  243. /package/{event-buses/index.d.ts → src/event-buses/index.ts} +0 -0
  244. /package/{plugins/index.d.ts → src/plugins/index.ts} +0 -0
  245. /package/{shims.d.ts → src/shims.d.ts} +0 -0
  246. /package/{types/index.d.ts → src/types/index.ts} +0 -0
  247. /package/{types/src/utils/index.d.ts → src/utils/index.ts} +0 -0
@@ -0,0 +1,101 @@
1
+ import type { Plugin } from 'vue';
2
+ import { computed, nextTick, ref } from 'vue';
3
+ import type { ChatMessage, ChatOptions } from '@/types';
4
+ import { v4 as uuidv4 } from 'uuid';
5
+ import { chatEventBus } from '@/event-buses';
6
+ import * as api from '@/api';
7
+ import { ChatOptionsSymbol, ChatSymbol, localStorageSessionIdKey } from '@/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
+ const sessionId = localStorage.getItem(localStorageSessionIdKey) ?? uuidv4();
65
+ const previousMessagesResponse = await api.loadPreviousSession(sessionId, options);
66
+ const timestamp = new Date().toISOString();
67
+
68
+ messages.value = (previousMessagesResponse?.data || []).map((message, index) => ({
69
+ id: `${index}`,
70
+ text: message.kwargs.content,
71
+ sender: message.id.includes('HumanMessage') ? 'user' : 'bot',
72
+ createdAt: timestamp,
73
+ }));
74
+
75
+ if (messages.value.length) {
76
+ currentSessionId.value = sessionId;
77
+ }
78
+
79
+ return sessionId;
80
+ }
81
+
82
+ async function startNewSession() {
83
+ currentSessionId.value = uuidv4();
84
+
85
+ localStorage.setItem(localStorageSessionIdKey, currentSessionId.value);
86
+ }
87
+
88
+ const chatStore = {
89
+ initialMessages,
90
+ messages,
91
+ currentSessionId,
92
+ waitingForResponse,
93
+ loadPreviousSession,
94
+ startNewSession,
95
+ sendMessage,
96
+ };
97
+
98
+ app.provide(ChatSymbol, chatStore);
99
+ app.config.globalProperties.$chat = chatStore;
100
+ },
101
+ };
@@ -0,0 +1,12 @@
1
+ import type { ChatMessage } from '@/types/messages';
2
+ import type { Ref } from 'vue';
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>;
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,23 @@
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
+ defaultLanguage?: 'en';
10
+ initialMessages?: string[];
11
+ i18n: Record<
12
+ string,
13
+ {
14
+ title: string;
15
+ subtitle: string;
16
+ footer: string;
17
+ getStarted: string;
18
+ inputPlaceholder: string;
19
+ [message: string]: string;
20
+ }
21
+ >;
22
+ theme?: {};
23
+ }
@@ -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-design-system/*": ["../design-system/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,51 @@
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
+
11
+ // https://vitejs.dev/config/
12
+ export default defineConfig({
13
+ plugins: [
14
+ vue(),
15
+ icons({
16
+ compiler: 'vue3',
17
+ }),
18
+ dts(),
19
+ ],
20
+ resolve: {
21
+ alias: {
22
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
23
+ },
24
+ },
25
+ define: {
26
+ 'process.env.NODE_ENV': process.env.NODE_ENV ? `"${process.env.NODE_ENV}"` : '"development"',
27
+ },
28
+ build: {
29
+ emptyOutDir: !includeVue,
30
+ lib: {
31
+ entry: resolve(__dirname, 'src', 'index.ts'),
32
+ name: 'N8nChat',
33
+ fileName: (format) => (includeVue ? `chat.bundle.${format}.js` : `chat.${format}.js`),
34
+ },
35
+ rollupOptions: {
36
+ // make sure to externalize deps that shouldn't be bundled
37
+ // into your library
38
+ external: includeVue ? [] : ['vue'],
39
+ output: {
40
+ exports: 'named',
41
+ // Provide global variables to use in the UMD build
42
+ // for externalized deps
43
+ globals: includeVue
44
+ ? {}
45
+ : {
46
+ vue: 'Vue',
47
+ },
48
+ },
49
+ },
50
+ },
51
+ });
@@ -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
- });