@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.
- package/.eslintignore +2 -0
- package/.eslintrc.cjs +51 -0
- package/.np-config.json +5 -0
- package/.storybook/main.ts +27 -0
- package/.storybook/preview.scss +4 -0
- package/.storybook/preview.ts +16 -0
- package/.vscode/extensions.json +3 -0
- package/README.md +4 -21
- package/env.d.ts +1 -0
- package/index.html +13 -0
- package/package.json +2 -69
- package/resources/workflow.json +293 -0
- package/scripts/pack.js +11 -0
- package/scripts/postbuild.js +16 -0
- package/{App.vue → src/App.vue} +2 -2
- package/src/__stories__/App.stories.ts +43 -0
- package/src/__tests__/index.spec.ts +223 -0
- package/src/__tests__/utils/create.ts +16 -0
- package/src/__tests__/utils/fetch.ts +18 -0
- package/src/__tests__/utils/selectors.ts +53 -0
- package/src/api/generic.ts +64 -0
- package/src/api/message.ts +31 -0
- package/src/components/Button.vue +41 -0
- package/{components → src/components}/Chat.vue +15 -19
- package/src/components/ChatWindow.vue +125 -0
- package/src/components/GetStarted.vue +24 -0
- package/{components → src/components}/GetStartedFooter.vue +4 -4
- package/{components → src/components}/Input.vue +40 -35
- package/src/components/Layout.vue +82 -0
- package/src/components/Message.vue +97 -0
- package/src/components/MessageTyping.vue +109 -0
- package/{components → src/components}/MessagesList.vue +8 -8
- package/{components → src/components}/PoweredBy.vue +7 -6
- package/src/composables/useChat.ts +7 -0
- package/src/composables/useI18n.ts +16 -0
- package/src/composables/useOptions.ts +11 -0
- package/src/constants/defaults.ts +25 -0
- package/{constants/localStorage.mjs → src/constants/localStorage.ts} +1 -1
- package/src/constants/symbols.ts +8 -0
- package/src/event-buses/chatEventBus.ts +3 -0
- package/src/index.ts +42 -0
- package/src/main.scss +40 -0
- package/src/plugins/chat.ts +101 -0
- package/src/types/chat.ts +12 -0
- package/src/types/messages.ts +6 -0
- package/src/types/options.ts +23 -0
- package/src/types/webhook.ts +17 -0
- package/src/utils/event-bus.ts +51 -0
- package/src/utils/mount.ts +16 -0
- package/tsconfig.json +27 -0
- package/vite.config.ts +51 -0
- package/vitest.config.ts +20 -0
- package/__stories__/App.stories.d.ts +0 -16
- package/__stories__/App.stories.js +0 -38
- package/__stories__/App.stories.mjs +0 -32
- package/__tests__/index.spec.d.ts +0 -1
- package/__tests__/index.spec.js +0 -146
- package/__tests__/index.spec.mjs +0 -172
- package/__tests__/setup.js +0 -3
- package/__tests__/setup.mjs +0 -1
- package/__tests__/utils/create.d.ts +0 -5
- package/__tests__/utils/create.js +0 -16
- package/__tests__/utils/create.mjs +0 -10
- package/__tests__/utils/fetch.d.ts +0 -3
- package/__tests__/utils/fetch.js +0 -20
- package/__tests__/utils/fetch.mjs +0 -9
- package/__tests__/utils/index.js +0 -38
- package/__tests__/utils/index.mjs +0 -3
- package/__tests__/utils/selectors.d.ts +0 -12
- package/__tests__/utils/selectors.js +0 -58
- package/__tests__/utils/selectors.mjs +0 -41
- package/api/generic.d.ts +0 -6
- package/api/generic.js +0 -68
- package/api/generic.mjs +0 -54
- package/api/index.js +0 -27
- package/api/index.mjs +0 -2
- package/api/message.d.ts +0 -3
- package/api/message.js +0 -33
- package/api/message.mjs +0 -30
- package/chat.bundle.es.js +0 -10760
- package/chat.bundle.umd.js +0 -18
- package/components/Button.vue +0 -34
- package/components/ChatWindow.vue +0 -104
- package/components/GetStarted.vue +0 -24
- package/components/Layout.vue +0 -66
- package/components/Message.vue +0 -94
- package/components/MessageTyping.vue +0 -101
- package/components/index.js +0 -76
- package/components/index.mjs +0 -10
- package/composables/index.js +0 -38
- package/composables/index.mjs +0 -3
- package/composables/useChat.d.ts +0 -1
- package/composables/useChat.js +0 -11
- package/composables/useChat.mjs +0 -5
- package/composables/useI18n.d.ts +0 -4
- package/composables/useI18n.js +0 -23
- package/composables/useI18n.mjs +0 -12
- package/composables/useOptions.d.ts +0 -3
- package/composables/useOptions.js +0 -14
- package/composables/useOptions.mjs +0 -8
- package/constants/defaults.d.ts +0 -3
- package/constants/defaults.js +0 -32
- package/constants/defaults.mjs +0 -26
- package/constants/index.js +0 -38
- package/constants/index.mjs +0 -3
- package/constants/localStorage.d.ts +0 -2
- package/constants/localStorage.js +0 -8
- package/constants/symbols.d.ts +0 -3
- package/constants/symbols.js +0 -8
- package/constants/symbols.mjs +0 -2
- package/css/index.css +0 -31
- package/event-buses/chatEventBus.d.ts +0 -1
- package/event-buses/chatEventBus.js +0 -8
- package/event-buses/chatEventBus.mjs +0 -2
- package/event-buses/index.js +0 -16
- package/event-buses/index.mjs +0 -1
- package/index.d.ts +0 -3
- package/index.js +0 -43
- package/index.mjs +0 -36
- package/main.css +0 -151
- package/plugins/chat.d.ts +0 -3
- package/plugins/chat.js +0 -85
- package/plugins/chat.mjs +0 -83
- package/plugins/index.js +0 -16
- package/plugins/index.mjs +0 -1
- package/style.css +0 -1
- package/types/App.vue.d.ts +0 -8
- package/types/__stories__/App.stories.d.ts +0 -17
- package/types/__tests__/index.spec.d.ts +0 -1
- package/types/__tests__/setup.d.ts +0 -0
- package/types/__tests__/utils/create.d.ts +0 -5
- package/types/__tests__/utils/fetch.d.ts +0 -4
- package/types/__tests__/utils/index.d.ts +0 -3
- package/types/__tests__/utils/selectors.d.ts +0 -12
- package/types/api/generic.d.ts +0 -6
- package/types/api/index.d.ts +0 -2
- package/types/api/message.d.ts +0 -3
- package/types/chat.d.ts +0 -11
- package/types/chat.js +0 -1
- package/types/chat.mjs +0 -0
- package/types/components/Button.vue.d.ts +0 -9
- package/types/components/Chat.vue.d.ts +0 -2
- package/types/components/ChatWindow.vue.d.ts +0 -2
- package/types/components/GetStarted.vue.d.ts +0 -2
- package/types/components/GetStartedFooter.vue.d.ts +0 -2
- package/types/components/Input.vue.d.ts +0 -2
- package/types/components/Layout.vue.d.ts +0 -11
- package/types/components/Message.vue.d.ts +0 -21
- package/types/components/MessageTyping.vue.d.ts +0 -15
- package/types/components/MessagesList.vue.d.ts +0 -14
- package/types/components/PoweredBy.vue.d.ts +0 -2
- package/types/components/index.d.ts +0 -10
- package/types/composables/index.d.ts +0 -3
- package/types/composables/useChat.d.ts +0 -2
- package/types/composables/useI18n.d.ts +0 -4
- package/types/composables/useOptions.d.ts +0 -4
- package/types/constants/defaults.d.ts +0 -3
- package/types/constants/index.d.ts +0 -3
- package/types/constants/localStorage.d.ts +0 -2
- package/types/constants/symbols.d.ts +0 -4
- package/types/event-buses/chatEventBus.d.ts +0 -1
- package/types/event-buses/index.d.ts +0 -1
- package/types/index.js +0 -49
- package/types/index.mjs +0 -4
- package/types/messages.d.ts +0 -6
- package/types/messages.js +0 -1
- package/types/messages.mjs +0 -0
- package/types/options.d.ts +0 -25
- package/types/options.js +0 -1
- package/types/options.mjs +0 -0
- package/types/plugins/chat.d.ts +0 -3
- package/types/plugins/index.d.ts +0 -1
- package/types/src/App.vue.d.ts +0 -8
- package/types/src/__stories__/App.stories.d.ts +0 -17
- package/types/src/__tests__/index.spec.d.ts +0 -1
- package/types/src/__tests__/setup.d.ts +0 -0
- package/types/src/__tests__/utils/create.d.ts +0 -5
- package/types/src/__tests__/utils/fetch.d.ts +0 -4
- package/types/src/__tests__/utils/index.d.ts +0 -3
- package/types/src/__tests__/utils/selectors.d.ts +0 -12
- package/types/src/api/generic.d.ts +0 -6
- package/types/src/api/index.d.ts +0 -2
- package/types/src/api/message.d.ts +0 -3
- package/types/src/components/Button.vue.d.ts +0 -9
- package/types/src/components/Chat.vue.d.ts +0 -2
- package/types/src/components/ChatWindow.vue.d.ts +0 -2
- package/types/src/components/GetStarted.vue.d.ts +0 -2
- package/types/src/components/GetStartedFooter.vue.d.ts +0 -2
- package/types/src/components/Input.vue.d.ts +0 -2
- package/types/src/components/Layout.vue.d.ts +0 -11
- package/types/src/components/Message.vue.d.ts +0 -21
- package/types/src/components/MessageTyping.vue.d.ts +0 -15
- package/types/src/components/MessagesList.vue.d.ts +0 -14
- package/types/src/components/PoweredBy.vue.d.ts +0 -2
- package/types/src/components/index.d.ts +0 -10
- package/types/src/composables/index.d.ts +0 -3
- package/types/src/composables/useChat.d.ts +0 -2
- package/types/src/composables/useI18n.d.ts +0 -4
- package/types/src/composables/useOptions.d.ts +0 -4
- package/types/src/constants/defaults.d.ts +0 -3
- package/types/src/constants/index.d.ts +0 -3
- package/types/src/constants/localStorage.d.ts +0 -2
- package/types/src/constants/symbols.d.ts +0 -4
- package/types/src/event-buses/chatEventBus.d.ts +0 -1
- package/types/src/event-buses/index.d.ts +0 -1
- package/types/src/index.d.ts +0 -2
- package/types/src/plugins/chat.d.ts +0 -3
- package/types/src/plugins/index.d.ts +0 -1
- package/types/src/types/chat.d.ts +0 -11
- package/types/src/types/index.d.ts +0 -4
- package/types/src/types/messages.d.ts +0 -6
- package/types/src/types/options.d.ts +0 -25
- package/types/src/types/webhook.d.ts +0 -15
- package/types/src/utils/event-bus.d.ts +0 -8
- package/types/src/utils/mount.d.ts +0 -1
- package/types/types/chat.d.ts +0 -11
- package/types/types/index.d.ts +0 -4
- package/types/types/messages.d.ts +0 -6
- package/types/types/options.d.ts +0 -25
- package/types/types/webhook.d.ts +0 -15
- package/types/utils/event-bus.d.ts +0 -8
- package/types/utils/index.d.ts +0 -2
- package/types/utils/mount.d.ts +0 -1
- package/types/webhook.d.ts +0 -15
- package/types/webhook.js +0 -1
- package/types/webhook.mjs +0 -0
- package/utils/event-bus.d.ts +0 -8
- package/utils/event-bus.js +0 -38
- package/utils/event-bus.mjs +0 -32
- package/utils/index.d.ts +0 -2
- package/utils/index.js +0 -27
- package/utils/index.mjs +0 -2
- package/utils/mount.d.ts +0 -1
- package/utils/mount.js +0 -19
- package/utils/mount.mjs +0 -13
- /package/{favicon.ico → public/favicon.ico} +0 -0
- /package/{__tests__/setup.d.ts → src/__tests__/setup.ts} +0 -0
- /package/{__tests__/utils/index.d.ts → src/__tests__/utils/index.ts} +0 -0
- /package/{api/index.d.ts → src/api/index.ts} +0 -0
- /package/{components/index.d.ts → src/components/index.ts} +0 -0
- /package/{composables/index.d.ts → src/composables/index.ts} +0 -0
- /package/{constants/index.d.ts → src/constants/index.ts} +0 -0
- /package/{event-buses/index.d.ts → src/event-buses/index.ts} +0 -0
- /package/{plugins/index.d.ts → src/plugins/index.ts} +0 -0
- /package/{shims.d.ts → src/shims.d.ts} +0 -0
- /package/{types/index.d.ts → src/types/index.ts} +0 -0
- /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,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
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -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 {};
|
package/__tests__/index.spec.js
DELETED
|
@@ -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
|
-
});
|