@n8n/chat 0.5.1 → 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 -62
- package/resources/workflow.json +293 -0
- package/scripts/pack.js +11 -0
- package/scripts/postbuild.js +16 -0
- package/src/App.vue +23 -0
- package/src/__stories__/App.stories.ts +43 -0
- package/src/__tests__/index.spec.ts +223 -0
- package/src/__tests__/setup.ts +1 -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/src/components/Chat.vue +48 -0
- package/src/components/ChatWindow.vue +125 -0
- package/src/components/GetStarted.vue +24 -0
- package/src/components/GetStartedFooter.vue +20 -0
- package/src/components/Input.vue +93 -0
- package/src/components/Layout.vue +82 -0
- package/src/components/Message.vue +97 -0
- package/src/components/MessageTyping.vue +109 -0
- package/src/components/MessagesList.vue +37 -0
- package/src/components/PoweredBy.vue +17 -0
- 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/src/constants/localStorage.ts +2 -0
- 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/shims.d.ts +6 -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/chat.bundle.es.js +0 -10760
- package/chat.bundle.umd.js +0 -18
- package/chat.es.js +0 -6870
- package/chat.umd.js +0 -18
- 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/selectors.d.ts +0 -12
- package/types/api/generic.d.ts +0 -6
- package/types/api/message.d.ts +0 -3
- 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/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/localStorage.d.ts +0 -2
- package/types/constants/symbols.d.ts +0 -4
- package/types/event-buses/chatEventBus.d.ts +0 -1
- package/types/index.d.ts +0 -2
- package/types/plugins/chat.d.ts +0 -3
- package/types/types/chat.d.ts +0 -11
- 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/mount.d.ts +0 -1
- /package/{favicon.ico → public/favicon.ico} +0 -0
- /package/{types/__tests__/utils/index.d.ts → src/__tests__/utils/index.ts} +0 -0
- /package/{types/api/index.d.ts → src/api/index.ts} +0 -0
- /package/{types/components/index.d.ts → src/components/index.ts} +0 -0
- /package/{types/composables/index.d.ts → src/composables/index.ts} +0 -0
- /package/{types/constants/index.d.ts → src/constants/index.ts} +0 -0
- /package/{types/event-buses/index.d.ts → src/event-buses/index.ts} +0 -0
- /package/{types/plugins/index.d.ts → src/plugins/index.ts} +0 -0
- /package/{types/types/index.d.ts → src/types/index.ts} +0 -0
- /package/{types/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
|
+
};
|
package/src/shims.d.ts
ADDED
|
@@ -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
|
+
);
|