vuepress-plugin-ai-chatbot 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +83 -2
- package/package.json +50 -4
- package/src/index.d.ts +13 -0
- package/src/index.js +27 -0
- package/src/loader.js +104 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Asyntai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,84 @@
|
|
|
1
|
-
#
|
|
1
|
+
# vuepress-plugin-ai-chatbot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add the [Asyntai](https://asyntai.com) AI chat assistant to your VuePress site.
|
|
4
|
+
It answers visitor questions from your own documentation, in more than 80
|
|
5
|
+
languages.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -D vuepress-plugin-ai-chatbot
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then add the plugin in `.vuepress/config.js` (or `config.ts`):
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { defineUserConfig } from 'vuepress';
|
|
17
|
+
import { aiChatbotPlugin } from 'vuepress-plugin-ai-chatbot';
|
|
18
|
+
|
|
19
|
+
export default defineUserConfig({
|
|
20
|
+
plugins: [
|
|
21
|
+
aiChatbotPlugin({ widgetId: 'asyntai_1a2b3c4d5e6f' }),
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Build the site. The chat bubble is on every page.
|
|
27
|
+
|
|
28
|
+
## Where the widget ID comes from
|
|
29
|
+
|
|
30
|
+
Open the Install page in your [Asyntai dashboard](https://asyntai.com/dashboard).
|
|
31
|
+
The widget ID is the value of `data-asyntai-id` in the snippet shown there. It
|
|
32
|
+
starts with `asyntai_`.
|
|
33
|
+
|
|
34
|
+
Creating an account is free, and the Free plan is enough to try the widget.
|
|
35
|
+
|
|
36
|
+
## Options
|
|
37
|
+
|
|
38
|
+
| Option | Type | Default | What it does |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `widgetId` | string | required | Your Asyntai widget ID |
|
|
41
|
+
| `enableInDevelopment` | boolean | `true` | Set to `false` to keep the widget off the site during `vuepress dev` |
|
|
42
|
+
| `scriptUrl` | string | the Asyntai CDN | Advanced. Load the widget from another URL |
|
|
43
|
+
|
|
44
|
+
A missing or malformed `widgetId` stops the build with a message that says how
|
|
45
|
+
to fix it.
|
|
46
|
+
|
|
47
|
+
## What the plugin does
|
|
48
|
+
|
|
49
|
+
- It adds the Asyntai loader to the head of every page.
|
|
50
|
+
- The loader waits for the page's `load` event, so your own content always
|
|
51
|
+
renders first.
|
|
52
|
+
- It works with the default theme and with any other theme, and the chat stays
|
|
53
|
+
open while the visitor moves between pages.
|
|
54
|
+
- If your `head` config already has an Asyntai snippet, the plugin adds no
|
|
55
|
+
second one.
|
|
56
|
+
|
|
57
|
+
## Answering from your content
|
|
58
|
+
|
|
59
|
+
Asyntai reads your published pages. Add your site URL in the dashboard, and the
|
|
60
|
+
assistant answers from the same pages the widget sits on. You can also add extra
|
|
61
|
+
documents and question and answer pairs.
|
|
62
|
+
|
|
63
|
+
## VuePress versions
|
|
64
|
+
|
|
65
|
+
VuePress 2. Both `vuepress build` and `vuepress dev` put the widget on every
|
|
66
|
+
page.
|
|
67
|
+
|
|
68
|
+
## TypeScript
|
|
69
|
+
|
|
70
|
+
Types ship with the package.
|
|
71
|
+
|
|
72
|
+
## Removing it
|
|
73
|
+
|
|
74
|
+
Delete the `aiChatbotPlugin` line from the config and build again. The widget is
|
|
75
|
+
gone.
|
|
76
|
+
|
|
77
|
+
## Support
|
|
78
|
+
|
|
79
|
+
Email [hello@asyntai.com](mailto:hello@asyntai.com), or read the
|
|
80
|
+
[VuePress guide](https://asyntai.com/documentation/integrations/vuepress/).
|
|
81
|
+
|
|
82
|
+
## Licence
|
|
83
|
+
|
|
84
|
+
MIT. See [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vuepress-plugin-ai-chatbot",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Add the Asyntai AI chat assistant to your VuePress site. It answers visitor questions from your own docs, in 80+ languages.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"types": "./src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.d.ts",
|
|
11
|
+
"import": "./src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"vuepress",
|
|
22
|
+
"vuepress-plugin",
|
|
23
|
+
"vuepress2",
|
|
24
|
+
"chatbot",
|
|
25
|
+
"ai",
|
|
26
|
+
"chat",
|
|
27
|
+
"live-chat",
|
|
28
|
+
"support",
|
|
29
|
+
"documentation",
|
|
30
|
+
"asyntai"
|
|
31
|
+
],
|
|
32
|
+
"homepage": "https://asyntai.com/documentation/integrations/vuepress/",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/asyntai/vuepress-plugin-ai-chatbot/issues",
|
|
35
|
+
"email": "hello@asyntai.com"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/asyntai/vuepress-plugin-ai-chatbot.git"
|
|
40
|
+
},
|
|
41
|
+
"author": "Asyntai <hello@asyntai.com> (https://asyntai.com)",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18.19"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "node tests/test_plugin.js"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"vuepress": "^2.0.0-rc.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Plugin } from 'vuepress/core';
|
|
2
|
+
|
|
3
|
+
export interface AiChatbotPluginOptions {
|
|
4
|
+
/** Your Asyntai widget ID, for example `asyntai_1a2b3c4d5e6f`. Required. */
|
|
5
|
+
widgetId: string;
|
|
6
|
+
/** Set to false to keep the widget off the site during `vuepress dev`. */
|
|
7
|
+
enableInDevelopment?: boolean;
|
|
8
|
+
/** Advanced. Load the widget script from another URL. */
|
|
9
|
+
scriptUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export declare const aiChatbotPlugin: (options: AiChatbotPluginOptions) => Plugin;
|
|
13
|
+
export default aiChatbotPlugin;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asyntai AI chatbot for VuePress 2.
|
|
3
|
+
*
|
|
4
|
+
* Adds the Asyntai chat widget to every page of the site. VuePress owns the
|
|
5
|
+
* HTML of each page, so the plugin adds the ordinary embed loader to the site
|
|
6
|
+
* head and changes nothing else about the site.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { NAME, addToHead, validateOptions } from './loader.js';
|
|
10
|
+
|
|
11
|
+
export const aiChatbotPlugin = (options) => {
|
|
12
|
+
const settings = validateOptions(options);
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
name: NAME,
|
|
16
|
+
|
|
17
|
+
onInitialized(app) {
|
|
18
|
+
if (app.env.isDev && !settings.enableInDevelopment) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
app.siteData.head = app.siteData.head || [];
|
|
22
|
+
addToHead(app.siteData.head, settings);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default aiChatbotPlugin;
|
package/src/loader.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Option handling and the embed snippet for vuepress-plugin-ai-chatbot.
|
|
3
|
+
*
|
|
4
|
+
* This file has no dependencies, so the tests run it on its own, without a
|
|
5
|
+
* VuePress install.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_SCRIPT_URL = 'https://widget.asyntai.com/static/js/chat-widget.js';
|
|
9
|
+
export const NAME = 'vuepress-plugin-ai-chatbot';
|
|
10
|
+
|
|
11
|
+
const WIDGET_ID_PATTERN = /^asyntai_[A-Za-z0-9]+$/;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* VuePress calls the plugin before it builds anything, so a wrong option stops
|
|
15
|
+
* the build with a clear message instead of producing a site with no chat on it.
|
|
16
|
+
*/
|
|
17
|
+
export function validateOptions(options) {
|
|
18
|
+
const opts = options || {};
|
|
19
|
+
const widgetId = typeof opts.widgetId === 'string' ? opts.widgetId.trim() : '';
|
|
20
|
+
|
|
21
|
+
if (!widgetId) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
NAME +
|
|
24
|
+
': "widgetId" is required. Copy it from the Install page of your ' +
|
|
25
|
+
'Asyntai dashboard, for example asyntai_1a2b3c4d5e6f.',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (!WIDGET_ID_PATTERN.test(widgetId)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
NAME +
|
|
31
|
+
': "' +
|
|
32
|
+
widgetId +
|
|
33
|
+
'" does not look like a widget ID. It starts with asyntai_, for ' +
|
|
34
|
+
'example asyntai_1a2b3c4d5e6f.',
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const scriptUrl =
|
|
39
|
+
typeof opts.scriptUrl === 'string' && opts.scriptUrl.trim()
|
|
40
|
+
? opts.scriptUrl.trim()
|
|
41
|
+
: DEFAULT_SCRIPT_URL;
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
widgetId,
|
|
45
|
+
scriptUrl,
|
|
46
|
+
// Set to false to keep the widget off the site during `vuepress dev`.
|
|
47
|
+
enableInDevelopment: opts.enableInDevelopment !== false,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** JSON.stringify, safe inside an HTML <script> element. */
|
|
52
|
+
function jsString(value) {
|
|
53
|
+
return JSON.stringify(value).replace(/</g, '\\u003c');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The loader waits for the page's load event before it fetches the widget.
|
|
58
|
+
* An ordinary `<script async src>` holds the load event open until its own
|
|
59
|
+
* fetch finishes, so an unreachable server would delay the host page. This
|
|
60
|
+
* pattern cannot.
|
|
61
|
+
*/
|
|
62
|
+
export function buildLoader(widgetId, scriptUrl) {
|
|
63
|
+
return (
|
|
64
|
+
'(function(){var l=function(){' +
|
|
65
|
+
'if(document.querySelector("script[data-asyntai-id]"))return;' +
|
|
66
|
+
'var s=document.createElement("script");' +
|
|
67
|
+
's.src=' +
|
|
68
|
+
jsString(scriptUrl) +
|
|
69
|
+
';s.async=true;' +
|
|
70
|
+
's.setAttribute("data-asyntai-id",' +
|
|
71
|
+
jsString(widgetId) +
|
|
72
|
+
');' +
|
|
73
|
+
'document.head.appendChild(s)};' +
|
|
74
|
+
'if(document.readyState==="complete"){l()}' +
|
|
75
|
+
'else{window.addEventListener("load",l)}})();'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The VuePress head entry: [tag, attributes, inner code]. */
|
|
80
|
+
export function buildHeadTag(settings) {
|
|
81
|
+
return [
|
|
82
|
+
'script',
|
|
83
|
+
{ 'data-asyntai-loader': true },
|
|
84
|
+
buildLoader(settings.widgetId, settings.scriptUrl),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** True for a head entry this plugin (or a hand-made snippet) already added. */
|
|
89
|
+
export function isAsyntaiTag(entry) {
|
|
90
|
+
if (!Array.isArray(entry) || entry[0] !== 'script') {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const attrs = entry[1] || {};
|
|
94
|
+
return 'data-asyntai-loader' in attrs || 'data-asyntai-id' in attrs;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Adds the loader to the site head once. Returns true when it added it. */
|
|
98
|
+
export function addToHead(head, settings) {
|
|
99
|
+
if (head.some(isAsyntaiTag)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
head.push(buildHeadTag(settings));
|
|
103
|
+
return true;
|
|
104
|
+
}
|