@n8n/chat 0.9.1 → 0.10.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 (207) 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 +4 -0
  5. package/.storybook/preview.scss +4 -0
  6. package/.storybook/preview.ts +15 -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 -35
  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 +39 -34
  30. package/{components → src/components}/Layout.vue +42 -26
  31. package/{components → src/components}/Message.vue +46 -39
  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 +115 -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 +18 -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 +60 -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 -10761
  84. package/chat.bundle.umd.js +0 -18
  85. package/chat.es.js +0 -6872
  86. package/chat.umd.js +0 -18
  87. package/components/Button.vue +0 -34
  88. package/components/ChatWindow.vue +0 -104
  89. package/components/MessageTyping.vue +0 -101
  90. package/components/index.js +0 -76
  91. package/components/index.mjs +0 -10
  92. package/composables/index.js +0 -38
  93. package/composables/index.mjs +0 -3
  94. package/composables/useChat.d.ts +0 -1
  95. package/composables/useChat.js +0 -11
  96. package/composables/useChat.mjs +0 -5
  97. package/composables/useI18n.d.ts +0 -4
  98. package/composables/useI18n.js +0 -23
  99. package/composables/useI18n.mjs +0 -12
  100. package/composables/useOptions.d.ts +0 -3
  101. package/composables/useOptions.js +0 -14
  102. package/composables/useOptions.mjs +0 -8
  103. package/constants/defaults.d.ts +0 -3
  104. package/constants/defaults.js +0 -32
  105. package/constants/defaults.mjs +0 -26
  106. package/constants/index.js +0 -38
  107. package/constants/index.mjs +0 -3
  108. package/constants/localStorage.d.ts +0 -2
  109. package/constants/localStorage.js +0 -8
  110. package/constants/symbols.d.ts +0 -3
  111. package/constants/symbols.js +0 -8
  112. package/constants/symbols.mjs +0 -2
  113. package/css/index.css +0 -31
  114. package/event-buses/chatEventBus.d.ts +0 -1
  115. package/event-buses/chatEventBus.js +0 -8
  116. package/event-buses/chatEventBus.mjs +0 -2
  117. package/event-buses/index.js +0 -16
  118. package/event-buses/index.mjs +0 -1
  119. package/index.d.ts +0 -3
  120. package/index.js +0 -43
  121. package/index.mjs +0 -36
  122. package/main.css +0 -151
  123. package/plugins/chat.d.ts +0 -3
  124. package/plugins/chat.js +0 -91
  125. package/plugins/chat.mjs +0 -90
  126. package/plugins/index.js +0 -16
  127. package/plugins/index.mjs +0 -1
  128. package/style.css +0 -1
  129. package/types/App.vue.d.ts +0 -8
  130. package/types/__stories__/App.stories.d.ts +0 -17
  131. package/types/__tests__/index.spec.d.ts +0 -1
  132. package/types/__tests__/setup.d.ts +0 -0
  133. package/types/__tests__/utils/create.d.ts +0 -5
  134. package/types/__tests__/utils/fetch.d.ts +0 -4
  135. package/types/__tests__/utils/index.d.ts +0 -3
  136. package/types/__tests__/utils/selectors.d.ts +0 -12
  137. package/types/api/generic.d.ts +0 -6
  138. package/types/api/index.d.ts +0 -2
  139. package/types/api/message.d.ts +0 -3
  140. package/types/chat.d.ts +0 -11
  141. package/types/chat.js +0 -1
  142. package/types/chat.mjs +0 -0
  143. package/types/components/Button.vue.d.ts +0 -9
  144. package/types/components/Chat.vue.d.ts +0 -2
  145. package/types/components/ChatWindow.vue.d.ts +0 -2
  146. package/types/components/GetStarted.vue.d.ts +0 -2
  147. package/types/components/GetStartedFooter.vue.d.ts +0 -2
  148. package/types/components/Input.vue.d.ts +0 -2
  149. package/types/components/Layout.vue.d.ts +0 -11
  150. package/types/components/Message.vue.d.ts +0 -21
  151. package/types/components/MessageTyping.vue.d.ts +0 -15
  152. package/types/components/MessagesList.vue.d.ts +0 -14
  153. package/types/components/PoweredBy.vue.d.ts +0 -2
  154. package/types/components/index.d.ts +0 -10
  155. package/types/composables/index.d.ts +0 -3
  156. package/types/composables/useChat.d.ts +0 -2
  157. package/types/composables/useI18n.d.ts +0 -4
  158. package/types/composables/useOptions.d.ts +0 -4
  159. package/types/constants/defaults.d.ts +0 -3
  160. package/types/constants/index.d.ts +0 -3
  161. package/types/constants/localStorage.d.ts +0 -2
  162. package/types/constants/symbols.d.ts +0 -4
  163. package/types/event-buses/chatEventBus.d.ts +0 -1
  164. package/types/event-buses/index.d.ts +0 -1
  165. package/types/index.js +0 -49
  166. package/types/index.mjs +0 -4
  167. package/types/messages.d.ts +0 -6
  168. package/types/messages.js +0 -1
  169. package/types/messages.mjs +0 -0
  170. package/types/options.d.ts +0 -25
  171. package/types/options.js +0 -1
  172. package/types/options.mjs +0 -0
  173. package/types/plugins/chat.d.ts +0 -3
  174. package/types/plugins/index.d.ts +0 -1
  175. package/types/types/chat.d.ts +0 -11
  176. package/types/types/index.d.ts +0 -4
  177. package/types/types/messages.d.ts +0 -6
  178. package/types/types/options.d.ts +0 -25
  179. package/types/types/webhook.d.ts +0 -16
  180. package/types/utils/event-bus.d.ts +0 -8
  181. package/types/utils/mount.d.ts +0 -1
  182. package/types/webhook.d.ts +0 -16
  183. package/types/webhook.js +0 -1
  184. package/types/webhook.mjs +0 -0
  185. package/utils/event-bus.d.ts +0 -8
  186. package/utils/event-bus.js +0 -38
  187. package/utils/event-bus.mjs +0 -32
  188. package/utils/index.d.ts +0 -2
  189. package/utils/index.js +0 -27
  190. package/utils/index.mjs +0 -2
  191. package/utils/mount.d.ts +0 -1
  192. package/utils/mount.js +0 -19
  193. package/utils/mount.mjs +0 -13
  194. /package/{favicon.ico → public/favicon.ico} +0 -0
  195. /package/{App.vue → src/App.vue} +0 -0
  196. /package/{__tests__/setup.d.ts → src/__tests__/setup.ts} +0 -0
  197. /package/{__tests__/utils/index.d.ts → src/__tests__/utils/index.ts} +0 -0
  198. /package/{api/index.d.ts → src/api/index.ts} +0 -0
  199. /package/{components → src/components}/Chat.vue +0 -0
  200. /package/{components/index.d.ts → src/components/index.ts} +0 -0
  201. /package/{composables/index.d.ts → src/composables/index.ts} +0 -0
  202. /package/{constants/index.d.ts → src/constants/index.ts} +0 -0
  203. /package/{event-buses/index.d.ts → src/event-buses/index.ts} +0 -0
  204. /package/{plugins/index.d.ts → src/plugins/index.ts} +0 -0
  205. /package/{shims.d.ts → src/shims.d.ts} +0 -0
  206. /package/{types/index.d.ts → src/types/index.ts} +0 -0
  207. /package/{types/utils/index.d.ts → src/utils/index.ts} +0 -0
@@ -35,32 +35,48 @@ onBeforeUnmount(() => {
35
35
  </main>
36
36
  </template>
37
37
 
38
- <style>
38
+ <style lang="scss">
39
39
  .chat-layout {
40
- width: 100%;
41
- height: 100%;
42
- display: flex;
43
- overflow-y: auto;
44
- flex-direction: column;
45
- font-family: var(--chat--font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif);
46
- }
47
- .chat-layout .chat-header {
48
- padding: var(--chat--header--padding, var(--chat--spacing));
49
- background: var(--chat--header--background, var(--chat--color-dark));
50
- color: var(--chat--header--color, var(--chat--color-light));
51
- }
52
- .chat-layout .chat-body {
53
- background: var(--chat--body--background, var(--chat--color-light));
54
- flex: 1;
55
- display: flex;
56
- flex-direction: column;
57
- overflow-y: auto;
58
- position: relative;
59
- min-height: 100px;
60
- }
61
- .chat-layout .chat-footer {
62
- border-top: 1px solid var(--chat--color-light-shade-100);
63
- background: var(--chat--footer--background, var(--chat--color-light));
64
- color: var(--chat--footer--color, var(--chat--color-dark));
40
+ width: 100%;
41
+ height: 100%;
42
+ display: flex;
43
+ overflow-y: auto;
44
+ flex-direction: column;
45
+ font-family: var(
46
+ --chat--font-family,
47
+ (
48
+ -apple-system,
49
+ BlinkMacSystemFont,
50
+ 'Segoe UI',
51
+ Roboto,
52
+ Oxygen-Sans,
53
+ Ubuntu,
54
+ Cantarell,
55
+ 'Helvetica Neue',
56
+ sans-serif
57
+ )
58
+ );
59
+
60
+ .chat-header {
61
+ padding: var(--chat--header--padding, var(--chat--spacing));
62
+ background: var(--chat--header--background, var(--chat--color-dark));
63
+ color: var(--chat--header--color, var(--chat--color-light));
64
+ }
65
+
66
+ .chat-body {
67
+ background: var(--chat--body--background, var(--chat--color-light));
68
+ flex: 1;
69
+ display: flex;
70
+ flex-direction: column;
71
+ overflow-y: auto;
72
+ position: relative;
73
+ min-height: 100px;
74
+ }
75
+
76
+ .chat-footer {
77
+ border-top: 1px solid var(--chat--color-light-shade-100);
78
+ background: var(--chat--footer--background, var(--chat--color-light));
79
+ color: var(--chat--footer--color, var(--chat--color-dark));
80
+ }
65
81
  }
66
82
  </style>
@@ -46,45 +46,52 @@ const markdownOptions = {
46
46
  </div>
47
47
  </template>
48
48
 
49
- <style>
49
+ <style lang="scss">
50
50
  .chat-message {
51
- display: block;
52
- max-width: 80%;
53
- padding: var(--chat--message--padding, var(--chat--spacing));
54
- border-radius: var(--chat--message--border-radius, var(--chat--border-radius));
55
- }
56
- .chat-message + .chat-message {
57
- margin-top: var(--chat--message--margin-bottom, calc(var(--chat--spacing) * 0.5));
58
- }
59
- .chat-message.chat-message-from-bot {
60
- background-color: var(--chat--message--bot--background);
61
- color: var(--chat--message--bot--color);
62
- border-bottom-left-radius: 0;
63
- }
64
- .chat-message.chat-message-from-user {
65
- background-color: var(--chat--message--user--background);
66
- color: var(--chat--message--user--color);
67
- margin-left: auto;
68
- border-bottom-right-radius: 0;
69
- }
70
- .chat-message > .chat-message-markdown {
71
- display: block;
72
- box-sizing: border-box;
73
- }
74
- .chat-message > .chat-message-markdown > *:first-child {
75
- margin-top: 0;
76
- }
77
- .chat-message > .chat-message-markdown > *:last-child {
78
- margin-bottom: 0;
79
- }
80
- .chat-message > .chat-message-markdown pre {
81
- font-family: inherit;
82
- font-size: inherit;
83
- margin: 0;
84
- white-space: pre-wrap;
85
- box-sizing: border-box;
86
- padding: var(--chat--spacing);
87
- background: var(--chat--message--pre--background);
88
- border-radius: var(--chat--border-radius);
51
+ display: block;
52
+ max-width: 80%;
53
+ padding: var(--chat--message--padding, var(--chat--spacing));
54
+ border-radius: var(--chat--message--border-radius, var(--chat--border-radius));
55
+
56
+ + .chat-message {
57
+ margin-top: var(--chat--message--margin-bottom, calc(var(--chat--spacing) * 0.5));
58
+ }
59
+
60
+ &.chat-message-from-bot {
61
+ background-color: var(--chat--message--bot--background);
62
+ color: var(--chat--message--bot--color);
63
+ border-bottom-left-radius: 0;
64
+ }
65
+
66
+ &.chat-message-from-user {
67
+ background-color: var(--chat--message--user--background);
68
+ color: var(--chat--message--user--color);
69
+ margin-left: auto;
70
+ border-bottom-right-radius: 0;
71
+ }
72
+
73
+ > .chat-message-markdown {
74
+ display: block;
75
+ box-sizing: border-box;
76
+
77
+ > *:first-child {
78
+ margin-top: 0;
79
+ }
80
+
81
+ > *:last-child {
82
+ margin-bottom: 0;
83
+ }
84
+
85
+ pre {
86
+ font-family: inherit;
87
+ font-size: inherit;
88
+ margin: 0;
89
+ white-space: pre-wrap;
90
+ box-sizing: border-box;
91
+ padding: var(--chat--spacing);
92
+ background: var(--chat--message--pre--background);
93
+ border-radius: var(--chat--border-radius);
94
+ }
95
+ }
89
96
  }
90
97
  </style>
@@ -0,0 +1,109 @@
1
+ <script lang="ts" setup>
2
+ import type { PropType } from 'vue';
3
+ import { computed } from 'vue';
4
+ import { Message } from './index';
5
+ import type { ChatMessage } from '@n8n/chat/types';
6
+
7
+ const props = defineProps({
8
+ animation: {
9
+ type: String as PropType<'bouncing' | 'scaling'>,
10
+ default: 'bouncing',
11
+ },
12
+ });
13
+
14
+ const message: ChatMessage = {
15
+ id: 'typing',
16
+ text: '',
17
+ sender: 'bot',
18
+ createdAt: '',
19
+ };
20
+
21
+ const classes = computed(() => {
22
+ return {
23
+ // eslint-disable-next-line @typescript-eslint/naming-convention
24
+ 'chat-message-typing': true,
25
+ [`chat-message-typing-animation-${props.animation}`]: true,
26
+ };
27
+ });
28
+ </script>
29
+ <template>
30
+ <Message :class="classes" :message="message">
31
+ <div class="chat-message-typing-body">
32
+ <span class="chat-message-typing-circle"></span>
33
+ <span class="chat-message-typing-circle"></span>
34
+ <span class="chat-message-typing-circle"></span>
35
+ </div>
36
+ </Message>
37
+ </template>
38
+ <style lang="scss">
39
+ .chat-message-typing {
40
+ max-width: 80px;
41
+
42
+ &.chat-message-typing-animation-scaling .chat-message-typing-circle {
43
+ animation: chat-message-typing-animation-scaling 800ms ease-in-out infinite;
44
+ animation-delay: 3600ms;
45
+ }
46
+
47
+ &.chat-message-typing-animation-bouncing .chat-message-typing-circle {
48
+ animation: chat-message-typing-animation-bouncing 800ms ease-in-out infinite;
49
+ animation-delay: 3600ms;
50
+ }
51
+
52
+ .chat-message-typing-body {
53
+ display: flex;
54
+ justify-content: center;
55
+ align-items: center;
56
+ }
57
+
58
+ .chat-message-typing-circle {
59
+ display: block;
60
+ height: 10px;
61
+ width: 10px;
62
+ border-radius: 50%;
63
+ background-color: var(--chat--color-typing);
64
+ margin: 3px;
65
+
66
+ &:nth-child(1) {
67
+ animation-delay: 0ms;
68
+ }
69
+
70
+ &:nth-child(2) {
71
+ animation-delay: 333ms;
72
+ }
73
+
74
+ &:nth-child(3) {
75
+ animation-delay: 666ms;
76
+ }
77
+ }
78
+ }
79
+
80
+ @keyframes chat-message-typing-animation-scaling {
81
+ 0% {
82
+ transform: scale(1);
83
+ }
84
+ 33% {
85
+ transform: scale(1);
86
+ }
87
+ 50% {
88
+ transform: scale(1.4);
89
+ }
90
+ 100% {
91
+ transform: scale(1);
92
+ }
93
+ }
94
+
95
+ @keyframes chat-message-typing-animation-bouncing {
96
+ 0% {
97
+ transform: translateY(0);
98
+ }
99
+ 33% {
100
+ transform: translateY(0);
101
+ }
102
+ 50% {
103
+ transform: translateY(-10px);
104
+ }
105
+ 100% {
106
+ transform: translateY(0);
107
+ }
108
+ }
109
+ </style>
@@ -28,10 +28,10 @@ const { initialMessages, waitingForResponse } = chatStore;
28
28
  </div>
29
29
  </template>
30
30
 
31
- <style>
31
+ <style lang="scss">
32
32
  .chat-messages-list {
33
- margin-top: auto;
34
- display: block;
35
- padding: var(--chat--messages-list--padding, var(--chat--spacing));
33
+ margin-top: auto;
34
+ display: block;
35
+ padding: var(--chat--messages-list--padding, var(--chat--spacing));
36
36
  }
37
37
  </style>
@@ -5,12 +5,13 @@
5
5
  </div>
6
6
  </template>
7
7
 
8
- <style>
8
+ <style lang="scss">
9
9
  .chat-powered-by {
10
- text-align: center;
11
- }
12
- .chat-powered-by a {
13
- color: var(--chat--color-primary);
14
- text-decoration: none;
10
+ text-align: center;
11
+
12
+ a {
13
+ color: var(--chat--color-primary);
14
+ text-decoration: none;
15
+ }
15
16
  }
16
17
  </style>
@@ -0,0 +1,7 @@
1
+ import { inject } from 'vue';
2
+ import { ChatSymbol } from '@n8n/chat/constants';
3
+ import type { Chat } from '@n8n/chat/types';
4
+
5
+ export function useChat() {
6
+ return inject(ChatSymbol) as Chat;
7
+ }
@@ -0,0 +1,16 @@
1
+ import { useOptions } from '@n8n/chat/composables/useOptions';
2
+
3
+ export function useI18n() {
4
+ const { options } = useOptions();
5
+ const language = options?.defaultLanguage ?? 'en';
6
+
7
+ function t(key: string): string {
8
+ return options?.i18n?.[language]?.[key] ?? key;
9
+ }
10
+
11
+ function te(key: string): boolean {
12
+ return !!options?.i18n?.[language]?.[key];
13
+ }
14
+
15
+ return { t, te };
16
+ }
@@ -0,0 +1,11 @@
1
+ import { inject } from 'vue';
2
+ import { ChatOptionsSymbol } from '@n8n/chat/constants';
3
+ import type { ChatOptions } from '@n8n/chat/types';
4
+
5
+ export function useOptions() {
6
+ const options = inject(ChatOptionsSymbol) as ChatOptions;
7
+
8
+ return {
9
+ options,
10
+ };
11
+ }
@@ -0,0 +1,29 @@
1
+ import type { ChatOptions } from '@n8n/chat/types';
2
+
3
+ export const defaultOptions: ChatOptions = {
4
+ webhookUrl: 'http://localhost:5678',
5
+ webhookConfig: {
6
+ method: 'POST',
7
+ headers: {},
8
+ },
9
+ target: '#n8n-chat',
10
+ mode: 'window',
11
+ loadPreviousSession: true,
12
+ chatInputKey: 'chatInput',
13
+ chatSessionKey: 'sessionId',
14
+ defaultLanguage: 'en',
15
+ showWelcomeScreen: false,
16
+ initialMessages: ['Hi there! 👋', 'My name is Nathan. How can I assist you today?'],
17
+ i18n: {
18
+ en: {
19
+ title: 'Hi there! 👋',
20
+ subtitle: "Start a chat. We're here to help you 24/7.",
21
+ footer: '',
22
+ getStarted: 'New Conversation',
23
+ inputPlaceholder: 'Type your question..',
24
+ },
25
+ },
26
+ theme: {},
27
+ };
28
+
29
+ export const defaultMountingTarget = '#n8n-chat';
@@ -1,2 +1,2 @@
1
- export const localStorageNamespace = "n8n-chat";
1
+ export const localStorageNamespace = 'n8n-chat';
2
2
  export const localStorageSessionIdKey = `${localStorageNamespace}/sessionId`;
@@ -0,0 +1,8 @@
1
+ import type { InjectionKey } from 'vue';
2
+ import type { Chat, ChatOptions } from '@n8n/chat/types';
3
+
4
+ // eslint-disable-next-line @typescript-eslint/naming-convention
5
+ export const ChatSymbol = 'Chat' as unknown as InjectionKey<Chat>;
6
+
7
+ // eslint-disable-next-line @typescript-eslint/naming-convention
8
+ export const ChatOptionsSymbol = 'ChatOptions' as unknown as InjectionKey<ChatOptions>;
@@ -0,0 +1,36 @@
1
+ :root {
2
+ --chat--color-primary: #e74266;
3
+ --chat--color-primary-shade-50: #db4061;
4
+ --chat--color-primary-shade-100: #cf3c5c;
5
+ --chat--color-secondary: #20b69e;
6
+ --chat--color-secondary-shade-50: #1ca08a;
7
+ --chat--color-white: #ffffff;
8
+ --chat--color-light: #f2f4f8;
9
+ --chat--color-light-shade-50: #e6e9f1;
10
+ --chat--color-light-shade-100: #c2c5cc;
11
+ --chat--color-medium: #d2d4d9;
12
+ --chat--color-dark: #101330;
13
+ --chat--color-disabled: #777980;
14
+ --chat--color-typing: #404040;
15
+
16
+ --chat--spacing: 1rem;
17
+ --chat--border-radius: 0.25rem;
18
+ --chat--transition-duration: 0.15s;
19
+
20
+ --chat--window--width: 400px;
21
+ --chat--window--height: 600px;
22
+
23
+ --chat--textarea--height: 50px;
24
+
25
+ --chat--message--bot--background: var(--chat--color-white);
26
+ --chat--message--bot--color: var(--chat--color-dark);
27
+ --chat--message--user--background: var(--chat--color-secondary);
28
+ --chat--message--user--color: var(--chat--color-white);
29
+ --chat--message--pre--background: rgba(0, 0, 0, 0.05);
30
+
31
+ --chat--toggle--background: var(--chat--color-primary);
32
+ --chat--toggle--hover--background: var(--chat--color-primary-shade-50);
33
+ --chat--toggle--active--background: var(--chat--color-primary-shade-100);
34
+ --chat--toggle--color: var(--chat--color-white);
35
+ --chat--toggle--size: 64px;
36
+ }
@@ -0,0 +1 @@
1
+ @import 'tokens';
@@ -0,0 +1,3 @@
1
+ import { createEventBus } from '@n8n/chat/utils';
2
+
3
+ export const chatEventBus = createEventBus();
package/src/index.ts ADDED
@@ -0,0 +1,42 @@
1
+ import './main.scss';
2
+
3
+ import { createApp } from 'vue';
4
+ import App from './App.vue';
5
+ import type { ChatOptions } from '@n8n/chat/types';
6
+ import { defaultMountingTarget, defaultOptions } from '@n8n/chat/constants';
7
+ import { createDefaultMountingTarget } from '@n8n/chat/utils';
8
+ import { ChatPlugin } from '@n8n/chat/plugins';
9
+
10
+ export function createChat(options?: Partial<ChatOptions>) {
11
+ const resolvedOptions: ChatOptions = {
12
+ ...defaultOptions,
13
+ ...options,
14
+ webhookConfig: {
15
+ ...defaultOptions.webhookConfig,
16
+ ...options?.webhookConfig,
17
+ },
18
+ i18n: {
19
+ ...defaultOptions.i18n,
20
+ ...options?.i18n,
21
+ en: {
22
+ ...defaultOptions.i18n?.en,
23
+ ...options?.i18n?.en,
24
+ },
25
+ },
26
+ theme: {
27
+ ...defaultOptions.theme,
28
+ ...options?.theme,
29
+ },
30
+ };
31
+
32
+ const mountingTarget = resolvedOptions.target ?? defaultMountingTarget;
33
+ if (typeof mountingTarget === 'string') {
34
+ createDefaultMountingTarget(mountingTarget);
35
+ }
36
+
37
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
38
+ const app = createApp(App);
39
+ app.use(ChatPlugin, resolvedOptions);
40
+ app.mount(mountingTarget);
41
+ return app;
42
+ }
package/src/main.scss ADDED
@@ -0,0 +1,5 @@
1
+ .n8n-chat {
2
+ @import 'highlight.js/styles/github';
3
+ }
4
+
5
+ @import 'css';
@@ -0,0 +1,115 @@
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
+ let textMessage = sendMessageResponse.output ?? sendMessageResponse.text ?? '';
49
+
50
+ if (textMessage === '' && Object.keys(sendMessageResponse).length > 0) {
51
+ try {
52
+ textMessage = JSON.stringify(sendMessageResponse, null, 2);
53
+ } catch (e) {
54
+ // Failed to stringify the object so fallback to empty string
55
+ }
56
+ }
57
+
58
+ const receivedMessage: ChatMessage = {
59
+ id: uuidv4(),
60
+ text: textMessage,
61
+ sender: 'bot',
62
+ createdAt: new Date().toISOString(),
63
+ };
64
+ messages.value.push(receivedMessage);
65
+
66
+ waitingForResponse.value = false;
67
+
68
+ void nextTick(() => {
69
+ chatEventBus.emit('scrollToBottom');
70
+ });
71
+ }
72
+
73
+ async function loadPreviousSession() {
74
+ if (!options.loadPreviousSession) {
75
+ return;
76
+ }
77
+
78
+ const sessionId = localStorage.getItem(localStorageSessionIdKey) ?? uuidv4();
79
+ const previousMessagesResponse = await api.loadPreviousSession(sessionId, options);
80
+ const timestamp = new Date().toISOString();
81
+
82
+ messages.value = (previousMessagesResponse?.data || []).map((message, index) => ({
83
+ id: `${index}`,
84
+ text: message.kwargs.content,
85
+ sender: message.id.includes('HumanMessage') ? 'user' : 'bot',
86
+ createdAt: timestamp,
87
+ }));
88
+
89
+ if (messages.value.length) {
90
+ currentSessionId.value = sessionId;
91
+ }
92
+
93
+ return sessionId;
94
+ }
95
+
96
+ async function startNewSession() {
97
+ currentSessionId.value = uuidv4();
98
+
99
+ localStorage.setItem(localStorageSessionIdKey, currentSessionId.value);
100
+ }
101
+
102
+ const chatStore = {
103
+ initialMessages,
104
+ messages,
105
+ currentSessionId,
106
+ waitingForResponse,
107
+ loadPreviousSession,
108
+ startNewSession,
109
+ sendMessage,
110
+ };
111
+
112
+ app.provide(ChatSymbol, chatStore);
113
+ app.config.globalProperties.$chat = chatStore;
114
+ },
115
+ };
@@ -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,18 @@
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
+ text?: string;
18
+ }