@n8n/chat 0.5.1 → 0.5.2
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/App.vue +23 -0
- package/__stories__/App.stories.d.ts +16 -0
- package/__stories__/App.stories.js +38 -0
- package/__stories__/App.stories.mjs +32 -0
- package/__tests__/index.spec.d.ts +1 -0
- package/__tests__/index.spec.js +146 -0
- package/__tests__/index.spec.mjs +172 -0
- package/__tests__/setup.d.ts +1 -0
- package/__tests__/setup.js +3 -0
- package/__tests__/setup.mjs +1 -0
- package/__tests__/utils/create.d.ts +5 -0
- package/__tests__/utils/create.js +16 -0
- package/__tests__/utils/create.mjs +10 -0
- package/__tests__/utils/fetch.d.ts +3 -0
- package/__tests__/utils/fetch.js +20 -0
- package/__tests__/utils/fetch.mjs +9 -0
- package/__tests__/utils/index.d.ts +3 -0
- package/__tests__/utils/index.js +38 -0
- package/__tests__/utils/index.mjs +3 -0
- package/__tests__/utils/selectors.d.ts +12 -0
- package/__tests__/utils/selectors.js +58 -0
- package/__tests__/utils/selectors.mjs +41 -0
- package/api/generic.d.ts +6 -0
- package/api/generic.js +68 -0
- package/api/generic.mjs +54 -0
- package/api/index.d.ts +2 -0
- package/api/index.js +27 -0
- package/api/index.mjs +2 -0
- package/api/message.d.ts +3 -0
- package/api/message.js +33 -0
- package/api/message.mjs +30 -0
- package/components/Button.vue +34 -0
- package/components/Chat.vue +52 -0
- package/components/ChatWindow.vue +104 -0
- package/components/GetStarted.vue +24 -0
- package/components/GetStartedFooter.vue +20 -0
- package/components/Input.vue +88 -0
- package/components/Layout.vue +66 -0
- package/components/Message.vue +94 -0
- package/components/MessageTyping.vue +101 -0
- package/components/MessagesList.vue +37 -0
- package/components/PoweredBy.vue +16 -0
- package/components/index.d.ts +10 -0
- package/components/index.js +76 -0
- package/components/index.mjs +10 -0
- package/composables/index.d.ts +3 -0
- package/composables/index.js +38 -0
- package/composables/index.mjs +3 -0
- package/composables/useChat.d.ts +1 -0
- package/composables/useChat.js +11 -0
- package/composables/useChat.mjs +5 -0
- package/composables/useI18n.d.ts +4 -0
- package/composables/useI18n.js +23 -0
- package/composables/useI18n.mjs +12 -0
- package/composables/useOptions.d.ts +3 -0
- package/composables/useOptions.js +14 -0
- package/composables/useOptions.mjs +8 -0
- package/constants/defaults.d.ts +3 -0
- package/constants/defaults.js +32 -0
- package/constants/defaults.mjs +26 -0
- package/constants/index.d.ts +3 -0
- package/constants/index.js +38 -0
- package/constants/index.mjs +3 -0
- package/constants/localStorage.d.ts +2 -0
- package/constants/localStorage.js +8 -0
- package/constants/localStorage.mjs +2 -0
- package/constants/symbols.d.ts +3 -0
- package/constants/symbols.js +8 -0
- package/constants/symbols.mjs +2 -0
- package/css/index.css +31 -0
- package/event-buses/chatEventBus.d.ts +1 -0
- package/event-buses/chatEventBus.js +8 -0
- package/event-buses/chatEventBus.mjs +2 -0
- package/event-buses/index.d.ts +1 -0
- package/event-buses/index.js +16 -0
- package/event-buses/index.mjs +1 -0
- package/index.d.ts +3 -0
- package/index.js +43 -0
- package/index.mjs +36 -0
- package/main.css +151 -0
- package/package.json +9 -2
- package/plugins/chat.d.ts +3 -0
- package/plugins/chat.js +85 -0
- package/plugins/chat.mjs +83 -0
- package/plugins/index.d.ts +1 -0
- package/plugins/index.js +16 -0
- package/plugins/index.mjs +1 -0
- package/shims.d.ts +6 -0
- package/types/chat.d.ts +11 -0
- package/types/chat.js +1 -0
- package/types/chat.mjs +0 -0
- package/types/index.d.ts +4 -2
- package/types/index.js +49 -0
- package/types/index.mjs +4 -0
- package/types/messages.d.ts +6 -0
- package/types/messages.js +1 -0
- package/types/messages.mjs +0 -0
- package/types/options.d.ts +25 -0
- package/types/options.js +1 -0
- package/types/options.mjs +0 -0
- package/types/src/App.vue.d.ts +8 -0
- package/types/src/__stories__/App.stories.d.ts +17 -0
- package/types/src/__tests__/index.spec.d.ts +1 -0
- package/types/src/__tests__/setup.d.ts +0 -0
- package/types/src/__tests__/utils/create.d.ts +5 -0
- package/types/src/__tests__/utils/fetch.d.ts +4 -0
- package/types/src/__tests__/utils/index.d.ts +3 -0
- package/types/src/__tests__/utils/selectors.d.ts +12 -0
- package/types/src/api/generic.d.ts +6 -0
- package/types/src/api/index.d.ts +2 -0
- package/types/src/api/message.d.ts +3 -0
- package/types/src/components/Button.vue.d.ts +9 -0
- package/types/src/components/Chat.vue.d.ts +2 -0
- package/types/src/components/ChatWindow.vue.d.ts +2 -0
- package/types/src/components/GetStarted.vue.d.ts +2 -0
- package/types/src/components/GetStartedFooter.vue.d.ts +2 -0
- package/types/src/components/Input.vue.d.ts +2 -0
- package/types/src/components/Layout.vue.d.ts +11 -0
- package/types/src/components/Message.vue.d.ts +21 -0
- package/types/src/components/MessageTyping.vue.d.ts +15 -0
- package/types/src/components/MessagesList.vue.d.ts +14 -0
- package/types/src/components/PoweredBy.vue.d.ts +2 -0
- package/types/src/components/index.d.ts +10 -0
- package/types/src/composables/index.d.ts +3 -0
- package/types/src/composables/useChat.d.ts +2 -0
- package/types/src/composables/useI18n.d.ts +4 -0
- package/types/src/composables/useOptions.d.ts +4 -0
- package/types/src/constants/defaults.d.ts +3 -0
- package/types/src/constants/index.d.ts +3 -0
- package/types/src/constants/localStorage.d.ts +2 -0
- package/types/src/constants/symbols.d.ts +4 -0
- package/types/src/event-buses/chatEventBus.d.ts +1 -0
- package/types/src/event-buses/index.d.ts +1 -0
- package/types/src/index.d.ts +2 -0
- package/types/src/plugins/chat.d.ts +3 -0
- package/types/src/plugins/index.d.ts +1 -0
- package/types/src/types/chat.d.ts +11 -0
- package/types/src/types/index.d.ts +4 -0
- package/types/src/types/messages.d.ts +6 -0
- package/types/src/types/options.d.ts +25 -0
- package/types/src/types/webhook.d.ts +15 -0
- package/types/src/utils/event-bus.d.ts +8 -0
- package/types/src/utils/index.d.ts +2 -0
- package/types/src/utils/mount.d.ts +1 -0
- package/types/webhook.d.ts +15 -0
- package/types/webhook.js +1 -0
- package/types/webhook.mjs +0 -0
- package/utils/event-bus.d.ts +8 -0
- package/utils/event-bus.js +38 -0
- package/utils/event-bus.mjs +32 -0
- package/utils/index.d.ts +2 -0
- package/utils/index.js +27 -0
- package/utils/index.mjs +2 -0
- package/utils/mount.d.ts +1 -0
- package/utils/mount.js +19 -0
- package/utils/mount.mjs +13 -0
- package/chat.es.js +0 -6870
- package/chat.umd.js +0 -18
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { screen } from "@testing-library/vue";
|
|
2
|
+
import { defaultMountingTarget } from "@n8n/chat/constants";
|
|
3
|
+
export function getMountingTarget(target = defaultMountingTarget) {
|
|
4
|
+
return document.querySelector(target);
|
|
5
|
+
}
|
|
6
|
+
export function getChatWindowWrapper() {
|
|
7
|
+
return document.querySelector(".chat-window-wrapper");
|
|
8
|
+
}
|
|
9
|
+
export function getChatWindowToggle() {
|
|
10
|
+
return document.querySelector(".chat-window-toggle");
|
|
11
|
+
}
|
|
12
|
+
export function getChatWrapper() {
|
|
13
|
+
return document.querySelector(".chat-wrapper");
|
|
14
|
+
}
|
|
15
|
+
export function getChatMessages() {
|
|
16
|
+
return document.querySelectorAll(".chat-message:not(.chat-message-typing)");
|
|
17
|
+
}
|
|
18
|
+
export function getChatMessage(index) {
|
|
19
|
+
const messages = getChatMessages();
|
|
20
|
+
return index < 0 ? messages[messages.length + index] : messages[index];
|
|
21
|
+
}
|
|
22
|
+
export function getChatMessageByText(text) {
|
|
23
|
+
return screen.queryByText(text, {
|
|
24
|
+
selector: ".chat-message:not(.chat-message-typing) .chat-message-markdown p"
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function getChatMessageTyping() {
|
|
28
|
+
return document.querySelector(".chat-message-typing");
|
|
29
|
+
}
|
|
30
|
+
export function getGetStartedButton() {
|
|
31
|
+
return document.querySelector(".chat-get-started .chat-button");
|
|
32
|
+
}
|
|
33
|
+
export function getChatInput() {
|
|
34
|
+
return document.querySelector(".chat-input");
|
|
35
|
+
}
|
|
36
|
+
export function getChatInputTextarea() {
|
|
37
|
+
return document.querySelector(".chat-input textarea");
|
|
38
|
+
}
|
|
39
|
+
export function getChatInputSendButton() {
|
|
40
|
+
return document.querySelector(".chat-input .chat-input-send-button");
|
|
41
|
+
}
|
package/api/generic.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function authenticatedFetch<T>(...args: Parameters<typeof fetch>): Promise<T>;
|
|
2
|
+
export declare function get<T>(url: string, query?: object, options?: RequestInit): Promise<T>;
|
|
3
|
+
export declare function post<T>(url: string, body?: object, options?: RequestInit): Promise<T>;
|
|
4
|
+
export declare function put<T>(url: string, body?: object, options?: RequestInit): Promise<T>;
|
|
5
|
+
export declare function patch<T>(url: string, body?: object, options?: RequestInit): Promise<T>;
|
|
6
|
+
export declare function del<T>(url: string, body?: object, options?: RequestInit): Promise<T>;
|
package/api/generic.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.authenticatedFetch = authenticatedFetch;
|
|
7
|
+
exports.del = del;
|
|
8
|
+
exports.get = get;
|
|
9
|
+
exports.patch = patch;
|
|
10
|
+
exports.post = post;
|
|
11
|
+
exports.put = put;
|
|
12
|
+
async function getAccessToken() {
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
async function authenticatedFetch(...args) {
|
|
16
|
+
const accessToken = await getAccessToken();
|
|
17
|
+
const response = await fetch(args[0], {
|
|
18
|
+
...args[1],
|
|
19
|
+
mode: "cors",
|
|
20
|
+
cache: "no-cache",
|
|
21
|
+
headers: {
|
|
22
|
+
"Content-Type": "application/json",
|
|
23
|
+
...(accessToken ? {
|
|
24
|
+
authorization: `Bearer ${accessToken}`
|
|
25
|
+
} : {}),
|
|
26
|
+
...args[1]?.headers
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return await response.json();
|
|
30
|
+
}
|
|
31
|
+
async function get(url, query = {}, options = {}) {
|
|
32
|
+
let resolvedUrl = url;
|
|
33
|
+
if (Object.keys(query).length > 0) {
|
|
34
|
+
resolvedUrl = `${resolvedUrl}?${new URLSearchParams(query).toString()}`;
|
|
35
|
+
}
|
|
36
|
+
return authenticatedFetch(resolvedUrl, {
|
|
37
|
+
...options,
|
|
38
|
+
method: "GET"
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function post(url, body = {}, options = {}) {
|
|
42
|
+
return authenticatedFetch(url, {
|
|
43
|
+
...options,
|
|
44
|
+
method: "POST",
|
|
45
|
+
body: JSON.stringify(body)
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async function put(url, body = {}, options = {}) {
|
|
49
|
+
return authenticatedFetch(url, {
|
|
50
|
+
...options,
|
|
51
|
+
method: "PUT",
|
|
52
|
+
body: JSON.stringify(body)
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function patch(url, body = {}, options = {}) {
|
|
56
|
+
return authenticatedFetch(url, {
|
|
57
|
+
...options,
|
|
58
|
+
method: "PATCH",
|
|
59
|
+
body: JSON.stringify(body)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async function del(url, body = {}, options = {}) {
|
|
63
|
+
return authenticatedFetch(url, {
|
|
64
|
+
...options,
|
|
65
|
+
method: "DELETE",
|
|
66
|
+
body: JSON.stringify(body)
|
|
67
|
+
});
|
|
68
|
+
}
|
package/api/generic.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
async function getAccessToken() {
|
|
2
|
+
return "";
|
|
3
|
+
}
|
|
4
|
+
export async function authenticatedFetch(...args) {
|
|
5
|
+
const accessToken = await getAccessToken();
|
|
6
|
+
const response = await fetch(args[0], {
|
|
7
|
+
...args[1],
|
|
8
|
+
mode: "cors",
|
|
9
|
+
cache: "no-cache",
|
|
10
|
+
headers: {
|
|
11
|
+
"Content-Type": "application/json",
|
|
12
|
+
...accessToken ? { authorization: `Bearer ${accessToken}` } : {},
|
|
13
|
+
...args[1]?.headers
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return await response.json();
|
|
17
|
+
}
|
|
18
|
+
export async function get(url, query = {}, options = {}) {
|
|
19
|
+
let resolvedUrl = url;
|
|
20
|
+
if (Object.keys(query).length > 0) {
|
|
21
|
+
resolvedUrl = `${resolvedUrl}?${new URLSearchParams(
|
|
22
|
+
query
|
|
23
|
+
).toString()}`;
|
|
24
|
+
}
|
|
25
|
+
return authenticatedFetch(resolvedUrl, { ...options, method: "GET" });
|
|
26
|
+
}
|
|
27
|
+
export async function post(url, body = {}, options = {}) {
|
|
28
|
+
return authenticatedFetch(url, {
|
|
29
|
+
...options,
|
|
30
|
+
method: "POST",
|
|
31
|
+
body: JSON.stringify(body)
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export async function put(url, body = {}, options = {}) {
|
|
35
|
+
return authenticatedFetch(url, {
|
|
36
|
+
...options,
|
|
37
|
+
method: "PUT",
|
|
38
|
+
body: JSON.stringify(body)
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export async function patch(url, body = {}, options = {}) {
|
|
42
|
+
return authenticatedFetch(url, {
|
|
43
|
+
...options,
|
|
44
|
+
method: "PATCH",
|
|
45
|
+
body: JSON.stringify(body)
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export async function del(url, body = {}, options = {}) {
|
|
49
|
+
return authenticatedFetch(url, {
|
|
50
|
+
...options,
|
|
51
|
+
method: "DELETE",
|
|
52
|
+
body: JSON.stringify(body)
|
|
53
|
+
});
|
|
54
|
+
}
|
package/api/index.d.ts
ADDED
package/api/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _generic = require("./generic");
|
|
7
|
+
Object.keys(_generic).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _generic[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return _generic[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
var _message = require("./message");
|
|
18
|
+
Object.keys(_message).forEach(function (key) {
|
|
19
|
+
if (key === "default" || key === "__esModule") return;
|
|
20
|
+
if (key in exports && exports[key] === _message[key]) return;
|
|
21
|
+
Object.defineProperty(exports, key, {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () {
|
|
24
|
+
return _message[key];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
package/api/index.mjs
ADDED
package/api/message.d.ts
ADDED
package/api/message.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.loadPreviousSession = loadPreviousSession;
|
|
7
|
+
exports.sendMessage = sendMessage;
|
|
8
|
+
var _generic = require("@n8n/chat/api/generic");
|
|
9
|
+
async function loadPreviousSession(sessionId, options) {
|
|
10
|
+
const method = options.webhookConfig?.method === "POST" ? _generic.post : _generic.get;
|
|
11
|
+
return method(`${options.webhookUrl}`, {
|
|
12
|
+
action: "loadPreviousSession",
|
|
13
|
+
[options.chatSessionKey]: sessionId,
|
|
14
|
+
...(options.metadata ? {
|
|
15
|
+
metadata: options.metadata
|
|
16
|
+
} : {})
|
|
17
|
+
}, {
|
|
18
|
+
headers: options.webhookConfig?.headers
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function sendMessage(message, sessionId, options) {
|
|
22
|
+
const method = options.webhookConfig?.method === "POST" ? _generic.post : _generic.get;
|
|
23
|
+
return method(`${options.webhookUrl}`, {
|
|
24
|
+
action: "sendMessage",
|
|
25
|
+
[options.chatSessionKey]: sessionId,
|
|
26
|
+
[options.chatInputKey]: message,
|
|
27
|
+
...(options.metadata ? {
|
|
28
|
+
metadata: options.metadata
|
|
29
|
+
} : {})
|
|
30
|
+
}, {
|
|
31
|
+
headers: options.webhookConfig?.headers
|
|
32
|
+
});
|
|
33
|
+
}
|
package/api/message.mjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { get, post } from "@n8n/chat/api/generic";
|
|
2
|
+
export async function loadPreviousSession(sessionId, options) {
|
|
3
|
+
const method = options.webhookConfig?.method === "POST" ? post : get;
|
|
4
|
+
return method(
|
|
5
|
+
`${options.webhookUrl}`,
|
|
6
|
+
{
|
|
7
|
+
action: "loadPreviousSession",
|
|
8
|
+
[options.chatSessionKey]: sessionId,
|
|
9
|
+
...options.metadata ? { metadata: options.metadata } : {}
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
headers: options.webhookConfig?.headers
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
export async function sendMessage(message, sessionId, options) {
|
|
17
|
+
const method = options.webhookConfig?.method === "POST" ? post : get;
|
|
18
|
+
return method(
|
|
19
|
+
`${options.webhookUrl}`,
|
|
20
|
+
{
|
|
21
|
+
action: "sendMessage",
|
|
22
|
+
[options.chatSessionKey]: sessionId,
|
|
23
|
+
[options.chatInputKey]: message,
|
|
24
|
+
...options.metadata ? { metadata: options.metadata } : {}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
headers: options.webhookConfig?.headers
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button class="chat-button">
|
|
3
|
+
<slot />
|
|
4
|
+
</button>
|
|
5
|
+
</template>
|
|
6
|
+
<style>
|
|
7
|
+
.chat-button {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
text-align: center;
|
|
10
|
+
vertical-align: middle;
|
|
11
|
+
user-select: none;
|
|
12
|
+
color: var(--chat--button--color, var(--chat--color-light));
|
|
13
|
+
background-color: var(--chat--button--background, var(--chat--color-primary));
|
|
14
|
+
border: 1px solid transparent;
|
|
15
|
+
padding: var(--chat--button--padding, calc(var(--chat--spacing) * 1 / 2) var(--chat--spacing));
|
|
16
|
+
font-size: 1rem;
|
|
17
|
+
line-height: 1.5;
|
|
18
|
+
border-radius: var(--chat--button--border-radius, var(--chat--border-radius));
|
|
19
|
+
transition: color var(--chat--transition-duration) ease-in-out, background-color var(--chat--transition-duration) ease-in-out, border-color var(--chat--transition-duration) ease-in-out, box-shadow var(--chat--transition-duration) ease-in-out;
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
}
|
|
22
|
+
.chat-button:hover {
|
|
23
|
+
color: var(--chat--button--hover--color, var(--chat--color-light));
|
|
24
|
+
background-color: var(--chat--button--hover--background, var(--chat--color-primary-shade-50));
|
|
25
|
+
text-decoration: none;
|
|
26
|
+
}
|
|
27
|
+
.chat-button:focus {
|
|
28
|
+
outline: 0;
|
|
29
|
+
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
|
30
|
+
}
|
|
31
|
+
.chat-button:disabled {
|
|
32
|
+
opacity: 0.65;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Layout from '@n8n/chat/components/Layout.vue';
|
|
3
|
+
import GetStarted from '@n8n/chat/components/GetStarted.vue';
|
|
4
|
+
import GetStartedFooter from '@n8n/chat/components/GetStartedFooter.vue';
|
|
5
|
+
import MessagesList from '@n8n/chat/components/MessagesList.vue';
|
|
6
|
+
import Input from '@n8n/chat/components/Input.vue';
|
|
7
|
+
import { nextTick, onMounted } from 'vue';
|
|
8
|
+
import { useI18n, useChat, useOptions } from '@n8n/chat/composables';
|
|
9
|
+
import { chatEventBus } from '@n8n/chat/event-buses';
|
|
10
|
+
|
|
11
|
+
const { t } = useI18n();
|
|
12
|
+
const chatStore = useChat();
|
|
13
|
+
|
|
14
|
+
const { messages, currentSessionId } = chatStore;
|
|
15
|
+
const { options } = useOptions();
|
|
16
|
+
|
|
17
|
+
async function getStarted() {
|
|
18
|
+
void chatStore.startNewSession();
|
|
19
|
+
void nextTick(() => {
|
|
20
|
+
chatEventBus.emit('scrollToBottom');
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function initialize() {
|
|
25
|
+
await chatStore.loadPreviousSession();
|
|
26
|
+
void nextTick(() => {
|
|
27
|
+
chatEventBus.emit('scrollToBottom');
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onMounted(async () => {
|
|
32
|
+
await initialize();
|
|
33
|
+
if (!options.showWelcomeScreen && !currentSessionId.value) {
|
|
34
|
+
await getStarted();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<Layout class="chat-wrapper">
|
|
41
|
+
<template #header>
|
|
42
|
+
<h1>{{ t('title') }}</h1>
|
|
43
|
+
<p>{{ t('subtitle') }}</p>
|
|
44
|
+
</template>
|
|
45
|
+
<GetStarted v-if="!currentSessionId && options.showWelcomeScreen" @click:button="getStarted" />
|
|
46
|
+
<MessagesList v-else :messages="messages" />
|
|
47
|
+
<template #footer>
|
|
48
|
+
<Input v-if="currentSessionId" />
|
|
49
|
+
<GetStartedFooter v-else />
|
|
50
|
+
</template>
|
|
51
|
+
</Layout>
|
|
52
|
+
</template>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
// eslint-disable-next-line import/no-unresolved
|
|
3
|
+
import IconChat from 'virtual:icons/mdi/chat';
|
|
4
|
+
// eslint-disable-next-line import/no-unresolved
|
|
5
|
+
import IconChevronDown from 'virtual:icons/mdi/chevron-down';
|
|
6
|
+
import Chat from '@n8n/chat/components/Chat.vue';
|
|
7
|
+
import { nextTick, ref } from 'vue';
|
|
8
|
+
import { chatEventBus } from '@n8n/chat/event-buses';
|
|
9
|
+
|
|
10
|
+
const isOpen = ref(false);
|
|
11
|
+
|
|
12
|
+
function toggle() {
|
|
13
|
+
isOpen.value = !isOpen.value;
|
|
14
|
+
|
|
15
|
+
if (isOpen.value) {
|
|
16
|
+
void nextTick(() => {
|
|
17
|
+
chatEventBus.emit('scrollToBottom');
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div class="chat-window-wrapper">
|
|
25
|
+
<Transition name="chat-window-transition">
|
|
26
|
+
<div class="chat-window" v-show="isOpen">
|
|
27
|
+
<Chat />
|
|
28
|
+
</div>
|
|
29
|
+
</Transition>
|
|
30
|
+
<div class="chat-window-toggle" @click="toggle">
|
|
31
|
+
<Transition name="chat-window-toggle-transition" mode="out-in">
|
|
32
|
+
<IconChat v-if="!isOpen" height="32" width="32" />
|
|
33
|
+
<IconChevronDown v-else height="32" width="32" />
|
|
34
|
+
</Transition>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.chat-window-wrapper {
|
|
41
|
+
position: fixed;
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
bottom: var(--chat--window--bottom, var(--chat--spacing));
|
|
45
|
+
right: var(--chat--window--right, var(--chat--spacing));
|
|
46
|
+
z-index: var(--chat--window--z-index, 9999);
|
|
47
|
+
max-width: calc(100% - var(--chat--window--right, var(--chat--spacing)) * 2);
|
|
48
|
+
max-height: calc(100% - var(--chat--window--bottom, var(--chat--spacing)) * 2);
|
|
49
|
+
}
|
|
50
|
+
.chat-window-wrapper .chat-window {
|
|
51
|
+
display: flex;
|
|
52
|
+
width: var(--chat--window--width);
|
|
53
|
+
height: var(--chat--window--height);
|
|
54
|
+
max-width: 100%;
|
|
55
|
+
max-height: 100%;
|
|
56
|
+
border: var(--chat--window--border, 1px solid var(--chat--color-light-shade-100));
|
|
57
|
+
border-radius: var(--chat--window--border-radius, var(--chat--border-radius));
|
|
58
|
+
margin-bottom: var(--chat--window--margin-bottom, var(--chat--spacing));
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
transform-origin: bottom right;
|
|
61
|
+
}
|
|
62
|
+
.chat-window-wrapper .chat-window .chat-layout {
|
|
63
|
+
width: auto;
|
|
64
|
+
height: auto;
|
|
65
|
+
flex: 1;
|
|
66
|
+
}
|
|
67
|
+
.chat-window-wrapper .chat-window-toggle {
|
|
68
|
+
flex: 0 0 auto;
|
|
69
|
+
background: var(--chat--toggle--background);
|
|
70
|
+
color: var(--chat--toggle--color);
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
width: var(--chat--toggle--width, var(--chat--toggle--size));
|
|
73
|
+
height: var(--chat--toggle--height, var(--chat--toggle--size));
|
|
74
|
+
border-radius: var(--chat--toggle--border-radius, 50%);
|
|
75
|
+
display: inline-flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
margin-left: auto;
|
|
79
|
+
transition: transform var(--chat--transition-duration) ease, background var(--chat--transition-duration) ease;
|
|
80
|
+
}
|
|
81
|
+
.chat-window-wrapper .chat-window-toggle:hover, .chat-window-wrapper .chat-window-toggle:focus {
|
|
82
|
+
transform: scale(1.05);
|
|
83
|
+
background: var(--chat--toggle--hover--background);
|
|
84
|
+
}
|
|
85
|
+
.chat-window-wrapper .chat-window-toggle:active {
|
|
86
|
+
transform: scale(0.95);
|
|
87
|
+
background: var(--chat--toggle--active--background);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.chat-window-transition-enter-active, .chat-window-transition-leave-active {
|
|
91
|
+
transition: transform var(--chat--transition-duration) ease, opacity var(--chat--transition-duration) ease;
|
|
92
|
+
}
|
|
93
|
+
.chat-window-transition-enter-from, .chat-window-transition-leave-to {
|
|
94
|
+
transform: scale(0);
|
|
95
|
+
opacity: 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.chat-window-toggle-transition-enter-active, .chat-window-toggle-transition-leave-active {
|
|
99
|
+
transition: opacity var(--chat--transition-duration) ease;
|
|
100
|
+
}
|
|
101
|
+
.chat-window-toggle-transition-enter-from, .chat-window-toggle-transition-leave-to {
|
|
102
|
+
opacity: 0;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Button from '@n8n/chat/components/Button.vue';
|
|
3
|
+
import { useI18n } from '@n8n/chat/composables';
|
|
4
|
+
|
|
5
|
+
const { t } = useI18n();
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<div class="chat-get-started">
|
|
9
|
+
<Button @click="$emit('click:button')">
|
|
10
|
+
{{ t('getStarted') }}
|
|
11
|
+
</Button>
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
.chat-get-started {
|
|
17
|
+
padding-top: var(--chat--spacing);
|
|
18
|
+
padding-bottom: var(--chat--spacing);
|
|
19
|
+
display: flex;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
align-items: center;
|
|
22
|
+
height: 100%;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from '@n8n/chat/composables';
|
|
3
|
+
import PoweredBy from '@n8n/chat/components/PoweredBy.vue';
|
|
4
|
+
|
|
5
|
+
const { t, te } = useI18n();
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<div class="chat-get-started-footer">
|
|
9
|
+
<div v-if="te('footer')">
|
|
10
|
+
{{ t('footer') }}
|
|
11
|
+
</div>
|
|
12
|
+
<PoweredBy />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
.chat-get-started-footer {
|
|
18
|
+
padding: var(--chat--spacing);
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// eslint-disable-next-line import/no-unresolved
|
|
3
|
+
import IconSend from 'virtual:icons/mdi/send';
|
|
4
|
+
import { useI18n, useChat } from '@n8n/chat/composables';
|
|
5
|
+
import { computed, ref } from 'vue';
|
|
6
|
+
|
|
7
|
+
const chatStore = useChat();
|
|
8
|
+
const { waitingForResponse } = chatStore;
|
|
9
|
+
const { t } = useI18n();
|
|
10
|
+
|
|
11
|
+
const input = ref('');
|
|
12
|
+
|
|
13
|
+
const isSubmitDisabled = computed(() => {
|
|
14
|
+
return input.value === '' || waitingForResponse.value;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
async function onSubmit(event: MouseEvent | KeyboardEvent) {
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
|
|
20
|
+
if (isSubmitDisabled.value) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const messageText = input.value;
|
|
25
|
+
input.value = '';
|
|
26
|
+
await chatStore.sendMessage(messageText);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function onSubmitKeydown(event: KeyboardEvent) {
|
|
30
|
+
if (event.shiftKey) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await onSubmit(event);
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div class="chat-input">
|
|
40
|
+
<textarea
|
|
41
|
+
v-model="input"
|
|
42
|
+
rows="1"
|
|
43
|
+
:placeholder="t('inputPlaceholder')"
|
|
44
|
+
@keydown.enter="onSubmitKeydown"
|
|
45
|
+
/>
|
|
46
|
+
<button :disabled="isSubmitDisabled" class="chat-input-send-button" @click="onSubmit">
|
|
47
|
+
<IconSend height="32" width="32" />
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<style>
|
|
53
|
+
.chat-input {
|
|
54
|
+
display: flex;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
align-items: center;
|
|
57
|
+
width: 100%;
|
|
58
|
+
}
|
|
59
|
+
.chat-input textarea {
|
|
60
|
+
font-family: inherit;
|
|
61
|
+
font-size: inherit;
|
|
62
|
+
width: 100%;
|
|
63
|
+
border: 0;
|
|
64
|
+
padding: var(--chat--spacing);
|
|
65
|
+
max-height: var(--chat--textarea--height);
|
|
66
|
+
resize: none;
|
|
67
|
+
}
|
|
68
|
+
.chat-input .chat-input-send-button {
|
|
69
|
+
height: var(--chat--textarea--height);
|
|
70
|
+
width: var(--chat--textarea--height);
|
|
71
|
+
background: white;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
color: var(--chat--color-secondary);
|
|
74
|
+
border: 0;
|
|
75
|
+
font-size: 24px;
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
transition: color var(--chat--transition-duration) ease;
|
|
80
|
+
}
|
|
81
|
+
.chat-input .chat-input-send-button:hover, .chat-input .chat-input-send-button:focus {
|
|
82
|
+
color: var(--chat--color-secondary-shade-50);
|
|
83
|
+
}
|
|
84
|
+
.chat-input .chat-input-send-button[disabled] {
|
|
85
|
+
cursor: default;
|
|
86
|
+
color: var(--chat--color-disabled);
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
|
3
|
+
import { chatEventBus } from '@n8n/chat/event-buses';
|
|
4
|
+
|
|
5
|
+
const chatBodyRef = ref<HTMLElement | null>(null);
|
|
6
|
+
|
|
7
|
+
function scrollToBottom() {
|
|
8
|
+
const element = chatBodyRef.value as HTMLElement;
|
|
9
|
+
if (element) {
|
|
10
|
+
element.scrollTop = element.scrollHeight;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
onMounted(() => {
|
|
15
|
+
chatEventBus.on('scrollToBottom', scrollToBottom);
|
|
16
|
+
window.addEventListener('resize', scrollToBottom);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
onBeforeUnmount(() => {
|
|
20
|
+
chatEventBus.off('scrollToBottom', scrollToBottom);
|
|
21
|
+
window.removeEventListener('resize', scrollToBottom);
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
<template>
|
|
25
|
+
<main class="chat-layout">
|
|
26
|
+
<div v-if="$slots.header" class="chat-header">
|
|
27
|
+
<slot name="header" />
|
|
28
|
+
</div>
|
|
29
|
+
<div v-if="$slots.default" class="chat-body" ref="chatBodyRef">
|
|
30
|
+
<slot />
|
|
31
|
+
</div>
|
|
32
|
+
<div v-if="$slots.footer" class="chat-footer">
|
|
33
|
+
<slot name="footer" />
|
|
34
|
+
</div>
|
|
35
|
+
</main>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style>
|
|
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));
|
|
65
|
+
}
|
|
66
|
+
</style>
|