eleventy-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 +42 -4
- package/src/index.d.ts +19 -0
- package/src/index.js +134 -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
|
+
# eleventy-plugin-ai-chatbot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add the [Asyntai](https://asyntai.com) AI chat assistant to your Eleventy (11ty)
|
|
4
|
+
site. It answers visitor questions from your own content, in more than 80
|
|
5
|
+
languages.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install eleventy-plugin-ai-chatbot
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then add the plugin in your Eleventy configuration.
|
|
14
|
+
|
|
15
|
+
`eleventy.config.js` (Eleventy 3, ESM):
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import aiChatbot from 'eleventy-plugin-ai-chatbot';
|
|
19
|
+
|
|
20
|
+
export default function (eleventyConfig) {
|
|
21
|
+
eleventyConfig.addPlugin(aiChatbot, { widgetId: 'asyntai_1a2b3c4d5e6f' });
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`.eleventy.js` (Eleventy 1, 2 or 3, CommonJS):
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const aiChatbot = require('eleventy-plugin-ai-chatbot');
|
|
29
|
+
|
|
30
|
+
module.exports = function (eleventyConfig) {
|
|
31
|
+
eleventyConfig.addPlugin(aiChatbot, { widgetId: 'asyntai_1a2b3c4d5e6f' });
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Build the site. The chat bubble is on every page.
|
|
36
|
+
|
|
37
|
+
## Where the widget ID comes from
|
|
38
|
+
|
|
39
|
+
Open the Install page in your [Asyntai dashboard](https://asyntai.com/dashboard).
|
|
40
|
+
The widget ID is the value of `data-asyntai-id` in the snippet shown there. It
|
|
41
|
+
starts with `asyntai_`.
|
|
42
|
+
|
|
43
|
+
Creating an account is free, and the Free plan is enough to try the widget.
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
| Option | Type | Default | What it does |
|
|
48
|
+
| --- | --- | --- | --- |
|
|
49
|
+
| `widgetId` | string | required | Your Asyntai widget ID |
|
|
50
|
+
| `enableInDevelopment` | boolean | `true` | Set to `false` to keep the widget off the site during `eleventy --serve` and `--watch` |
|
|
51
|
+
| `scriptUrl` | string | the Asyntai CDN | Advanced. Load the widget from another URL |
|
|
52
|
+
|
|
53
|
+
A missing or malformed `widgetId` stops the build with a message that says how
|
|
54
|
+
to fix it.
|
|
55
|
+
|
|
56
|
+
## What the plugin does
|
|
57
|
+
|
|
58
|
+
- It adds the Asyntai loader to every HTML page, just before `</body>`.
|
|
59
|
+
- The loader waits for the page's `load` event, so your own content always
|
|
60
|
+
renders first.
|
|
61
|
+
- Feeds, sitemaps, JSON and CSS files stay as they are.
|
|
62
|
+
- A page that already has the Asyntai snippet keeps it and gets no second one.
|
|
63
|
+
|
|
64
|
+
It works with every template language: Nunjucks, Liquid, Markdown, WebC and the
|
|
65
|
+
rest.
|
|
66
|
+
|
|
67
|
+
## Answering from your content
|
|
68
|
+
|
|
69
|
+
Asyntai reads your published pages. Add your site URL in the dashboard, and the
|
|
70
|
+
assistant answers from the same pages the widget sits on. You can also add extra
|
|
71
|
+
documents and question and answer pairs.
|
|
72
|
+
|
|
73
|
+
## Removing it
|
|
74
|
+
|
|
75
|
+
Delete the `addPlugin` line and build again. The widget is gone.
|
|
76
|
+
|
|
77
|
+
## Support
|
|
78
|
+
|
|
79
|
+
Email [hello@asyntai.com](mailto:hello@asyntai.com), or read the
|
|
80
|
+
[Eleventy guide](https://asyntai.com/documentation/integrations/eleventy/).
|
|
81
|
+
|
|
82
|
+
## Licence
|
|
83
|
+
|
|
84
|
+
MIT. See [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eleventy-plugin-ai-chatbot",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Add the Asyntai AI chat assistant to your Eleventy (11ty) site. It answers visitor questions from your own content, in 80+ languages.",
|
|
5
|
+
"main": "./src/index.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"src",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"eleventy-plugin",
|
|
14
|
+
"eleventy",
|
|
15
|
+
"11ty",
|
|
16
|
+
"chatbot",
|
|
17
|
+
"ai",
|
|
18
|
+
"chat",
|
|
19
|
+
"live-chat",
|
|
20
|
+
"support",
|
|
21
|
+
"customer-support",
|
|
22
|
+
"asyntai"
|
|
23
|
+
],
|
|
24
|
+
"homepage": "https://asyntai.com/documentation/integrations/eleventy/",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/asyntai/eleventy-plugin-ai-chatbot/issues",
|
|
27
|
+
"email": "hello@asyntai.com"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/asyntai/eleventy-plugin-ai-chatbot.git"
|
|
32
|
+
},
|
|
33
|
+
"author": "Asyntai <hello@asyntai.com> (https://asyntai.com)",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "node tests/test_plugin.js"
|
|
40
|
+
},
|
|
41
|
+
"11ty": {
|
|
42
|
+
"compatibility": ">=1.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface AsyntaiOptions {
|
|
2
|
+
/** Your Asyntai widget ID, for example `asyntai_1a2b3c4d5e6f`. Required. */
|
|
3
|
+
widgetId: string;
|
|
4
|
+
/** Set to false to keep the widget off the site during `eleventy --serve`. */
|
|
5
|
+
enableInDevelopment?: boolean;
|
|
6
|
+
/** Advanced. Load the widget script from another URL. */
|
|
7
|
+
scriptUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare function eleventyPluginAiChatbot(eleventyConfig: any, options: AsyntaiOptions): void;
|
|
11
|
+
|
|
12
|
+
declare namespace eleventyPluginAiChatbot {
|
|
13
|
+
function validateOptions(options: AsyntaiOptions): Required<AsyntaiOptions>;
|
|
14
|
+
function buildLoader(widgetId: string, scriptUrl: string): string;
|
|
15
|
+
function injectIntoHtml(html: string, tag: string): string;
|
|
16
|
+
const DEFAULT_SCRIPT_URL: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export = eleventyPluginAiChatbot;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asyntai AI chatbot for Eleventy (11ty).
|
|
3
|
+
*
|
|
4
|
+
* Adds the Asyntai chat widget to every HTML page the site builds. Eleventy has
|
|
5
|
+
* no API for scripts, so the plugin is an HTML transform: it puts the ordinary
|
|
6
|
+
* embed loader just before </body> and changes nothing else about the page.
|
|
7
|
+
*
|
|
8
|
+
* CommonJS on purpose: Eleventy 1 and 2 load plugins with require(), and
|
|
9
|
+
* Eleventy 3 loads CommonJS plugins as well.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const DEFAULT_SCRIPT_URL = 'https://widget.asyntai.com/static/js/chat-widget.js';
|
|
13
|
+
const WIDGET_ID_PATTERN = /^asyntai_[A-Za-z0-9]+$/;
|
|
14
|
+
const NAME = 'eleventy-plugin-ai-chatbot';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Eleventy runs the plugin before it builds anything, so a wrong option stops
|
|
18
|
+
* the build with a clear message instead of producing a site with no chat.
|
|
19
|
+
*/
|
|
20
|
+
function validateOptions(options) {
|
|
21
|
+
const opts = options || {};
|
|
22
|
+
const widgetId = typeof opts.widgetId === 'string' ? opts.widgetId.trim() : '';
|
|
23
|
+
|
|
24
|
+
if (!widgetId) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
NAME +
|
|
27
|
+
': "widgetId" is required. Copy it from the Install page of your ' +
|
|
28
|
+
'Asyntai dashboard, for example asyntai_1a2b3c4d5e6f.',
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (!WIDGET_ID_PATTERN.test(widgetId)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
NAME +
|
|
34
|
+
': "' +
|
|
35
|
+
widgetId +
|
|
36
|
+
'" does not look like a widget ID. It starts with asyntai_, for ' +
|
|
37
|
+
'example asyntai_1a2b3c4d5e6f.',
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const scriptUrl =
|
|
42
|
+
typeof opts.scriptUrl === 'string' && opts.scriptUrl.trim()
|
|
43
|
+
? opts.scriptUrl.trim()
|
|
44
|
+
: DEFAULT_SCRIPT_URL;
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
widgetId,
|
|
48
|
+
scriptUrl,
|
|
49
|
+
// Set to false to keep the widget off the site during `eleventy --serve`.
|
|
50
|
+
enableInDevelopment: opts.enableInDevelopment !== false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** JSON.stringify, safe inside an HTML <script> element. */
|
|
55
|
+
function jsString(value) {
|
|
56
|
+
return JSON.stringify(value).replace(/</g, '\\u003c');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The loader waits for the page's load event before it fetches the widget.
|
|
61
|
+
* An ordinary `<script async src>` holds the load event open until its own
|
|
62
|
+
* fetch finishes, so an unreachable server would delay the host page. This
|
|
63
|
+
* pattern cannot.
|
|
64
|
+
*/
|
|
65
|
+
function buildLoader(widgetId, scriptUrl) {
|
|
66
|
+
return (
|
|
67
|
+
'(function(){var l=function(){' +
|
|
68
|
+
'if(document.querySelector("script[data-asyntai-id]"))return;' +
|
|
69
|
+
'var s=document.createElement("script");' +
|
|
70
|
+
's.src=' +
|
|
71
|
+
jsString(scriptUrl) +
|
|
72
|
+
';s.async=true;' +
|
|
73
|
+
's.setAttribute("data-asyntai-id",' +
|
|
74
|
+
jsString(widgetId) +
|
|
75
|
+
');' +
|
|
76
|
+
'document.head.appendChild(s)};' +
|
|
77
|
+
'if(document.readyState==="complete"){l()}' +
|
|
78
|
+
'else{window.addEventListener("load",l)}})();'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Puts the tag before the last </body>, or before </html> when the page has no
|
|
84
|
+
* body tag. A fragment with neither is left alone: it is not a whole page.
|
|
85
|
+
* A page that already carries an Asyntai snippet is left alone too.
|
|
86
|
+
*/
|
|
87
|
+
function injectIntoHtml(html, tag) {
|
|
88
|
+
if (typeof html !== 'string' || html.includes('data-asyntai-id')) {
|
|
89
|
+
return html;
|
|
90
|
+
}
|
|
91
|
+
const lower = html.toLowerCase();
|
|
92
|
+
let at = lower.lastIndexOf('</body>');
|
|
93
|
+
if (at === -1) {
|
|
94
|
+
at = lower.lastIndexOf('</html>');
|
|
95
|
+
}
|
|
96
|
+
if (at === -1) {
|
|
97
|
+
return html;
|
|
98
|
+
}
|
|
99
|
+
return html.slice(0, at) + tag + html.slice(at);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function isDevelopment() {
|
|
103
|
+
const mode = process.env.ELEVENTY_RUN_MODE;
|
|
104
|
+
return mode === 'serve' || mode === 'watch';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function eleventyPluginAiChatbot(eleventyConfig, options) {
|
|
108
|
+
const settings = validateOptions(options);
|
|
109
|
+
const tag =
|
|
110
|
+
'<script data-asyntai-loader>' +
|
|
111
|
+
buildLoader(settings.widgetId, settings.scriptUrl) +
|
|
112
|
+
'</script>';
|
|
113
|
+
|
|
114
|
+
eleventyConfig.addTransform('asyntai-ai-chatbot', function (content, legacyOutputPath) {
|
|
115
|
+
if (!settings.enableInDevelopment && isDevelopment()) {
|
|
116
|
+
return content;
|
|
117
|
+
}
|
|
118
|
+
// Eleventy 3 has this.page.outputPath; Eleventy 1 passes it as an argument.
|
|
119
|
+
const outputPath =
|
|
120
|
+
(this && this.page && this.page.outputPath) ||
|
|
121
|
+
(this && this.outputPath) ||
|
|
122
|
+
legacyOutputPath;
|
|
123
|
+
if (typeof outputPath !== 'string' || !outputPath.toLowerCase().endsWith('.html')) {
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
126
|
+
return injectIntoHtml(content, tag);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = eleventyPluginAiChatbot;
|
|
131
|
+
module.exports.validateOptions = validateOptions;
|
|
132
|
+
module.exports.buildLoader = buildLoader;
|
|
133
|
+
module.exports.injectIntoHtml = injectIntoHtml;
|
|
134
|
+
module.exports.DEFAULT_SCRIPT_URL = DEFAULT_SCRIPT_URL;
|