botdocs 0.3.0 ā 0.4.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/dist/src/builder/embedder.js +1 -1
- package/dist/src/builder/index.js +18 -0
- package/dist/src/builder/markdown-processor.d.ts +8 -1
- package/dist/src/builder/markdown-processor.js +40 -7
- package/dist/src/builder/site-generator.d.ts +17 -2
- package/dist/src/builder/site-generator.js +82 -27
- package/dist/src/cli/index.js +2 -2
- package/dist/src/cli/options.js +0 -1
- package/dist/src/types/config.d.ts +1 -0
- package/dist-client/assets/chatbox-P1j6YP1y.js +7 -0
- package/dist-client/assets/rag-engine-CU9vjCAg.js +1 -0
- package/dist-client/bundle.js +1 -1
- package/dist-client/wasm/ort-wasm-simd-threaded.asyncify.wasm +0 -0
- package/man/botdocs.1 +122 -0
- package/package.json +9 -2
- package/src/styles/chat.css +131 -0
- package/src/styles/themes/classic.css +29 -0
- package/src/styles/themes/material.css +29 -0
- package/src/styles/themes/minimal.css +34 -0
- package/src/styles/themes/modern.css +34 -0
- package/src/styles/themes/slate.css +32 -36
- package/src/templates/layout.html +7 -0
- package/dist-client/assets/chatbox-CmTTiB2Z.js +0 -1
- package/dist-client/assets/rag-engine-L2vj3Y0G.js +0 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var t=Object.defineProperty,e=(e,n,o)=>((e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o)(e,"symbol"!=typeof n?n+"":n,o);import{g as n}from"./chatbox-P1j6YP1y.js";import"../bundle.js";function o(t,e){if(t.length!==e.length)throw new Error("Vectors must have the same length");let n=0,o=0,r=0;for(let i=0;i<t.length;i++)n+=t[i]*e[i],o+=t[i]*t[i],r+=e[i]*e[i];return o=Math.sqrt(o),r=Math.sqrt(r),0===o||0===r?0:n/(o*r)}class r{constructor(){e(this,"vectorDB",null),e(this,"isLoading",!1)}async loadVectorDB(){if(!this.vectorDB)if(this.isLoading)for(;this.isLoading;)await new Promise(t=>setTimeout(t,100));else{this.isLoading=!0;try{console.log("Loading vector database...");const t=await fetch("/vector-db.json");this.vectorDB=await t.json(),console.log(`Loaded ${this.vectorDB.chunks.length} chunks`)}finally{this.isLoading=!1}}}async search(t,e=5){this.vectorDB||await this.loadVectorDB();const n=this.vectorDB.chunks.map(e=>({chunk:e,score:o(t,e.embedding)}));return n.sort((t,e)=>e.score-t.score),n.slice(0,e)}isReady(){return null!==this.vectorDB}getInfo(){return this.vectorDB?{chunkCount:this.vectorDB.chunks.length,dimension:this.vectorDB.dimension,model:this.vectorDB.model}:null}}let i=null;function s(){return i||(i=new r),i}function a(t){const e=[],n=t.split("\n");let o=[],r=null;const i=()=>{if(0===o.length)return;const t=o.join(" ").trim();if(o=[],t)for(const n of function(t){const e=[],n=t.replace(/`[^`]*`/g,t=>(e.push(t),`\0${e.length-1}\0`));return(n.match(/[^.!?]+[.!?]*(?:\s+|$)/g)||[n]).map(t=>t.replace(/\0(\d+)\0/g,(t,n)=>e[Number(n)])).map(t=>t.trim()).filter(Boolean)}(t))n&&e.push({text:n,kind:"prose"})};let s=0;for(;s<n.length;){const t=n[s];if(t.trim().startsWith("```")){i(),r=null;const o=[t];for(s++;s<n.length&&!n[s].trim().startsWith("```");)o.push(n[s]),s++;s<n.length&&o.push(n[s]),e.push({text:o.join("\n"),kind:"code"}),s++;continue}if(/^#{1,6}\s+/.test(t.trim()))i(),r=null,s++;else{if(/^\s*([-*+]|\d+\.)\s+/.test(t)){i();const n=t.trim();n&&(e.push({text:n,kind:"list"}),r=e.length-1),s++;continue}""!==t.trim()?null===r?(o.push(t.trim()),s++):(e[r].text+=" "+t.trim(),s++):(i(),r=null,s++)}}return i(),e}let c=!1,l=5;const h=.7;async function u(){return n().isModelCached()}async function d(t){if(!c)try{const e=s();await e.loadVectorDB(),t&&t(5);const o=n();await o.initialize(e=>{t&&t(5+.95*e)}),c=!0,t&&t(100)}catch(e){throw console.error("Search initialization failed:",e),e}}async function m(t,e={}){if(!c)throw new Error("Search system not initialized");const r=e.topK||l;try{const e=n(),i=await e.embed(t),c=s(),l=await c.search(i,r),u=l.slice(0,3),d=await Promise.all(u.map(t=>async function(t,e,n,r=3){const i=t.trim(),s=a(i);if(0===s.length)return{snippet:i,topSegment:i,topScore:0};const c=await n(s.map(t=>t.text)),l=s.map((t,n)=>({...t,index:n,score:o(e,c[n])})).sort((t,e)=>e.score-t.score);return{snippet:l.slice(0,Math.min(r,l.length)).sort((t,e)=>t.index-e.index).map(t=>t.text).join("\n\n"),topSegment:l[0].text,topScore:l[0].score}}(t.chunk.text,i,t=>e.embedBatch(t)))),m=function(t,e,n,o){if(0===e.length)return"I couldn't find anything about that in the documentation. Try rephrasing your question or browse the navigation menu to explore available topics.";const r=/^how (to|do|can)/i.test(t),i=/^what (is|are)/i.test(t),s=/^why/i.test(t);let a="";const c=n.map((t,e)=>({result:t,extract:o[e]})).filter(({result:t,extract:e})=>t.score>h&&e.topScore>h);if(c.length>1){a+="**Quick answer:**\n\n";for(const{result:t,extract:e}of c){const n=f(t.chunk.metadata);a+=`- ${e.topSegment} ([${g(t.chunk.metadata)}](${n}))\n`}a+="\n---\n\n"}r?a+="Here's how you can do that:\n\n":i?a+="Let me explain:\n\n":s?a+="Here's what the docs say about that:\n\n":1===e.length?a+="I found this relevant section:\n\n":a+=`I found ${n.length} relevant sections:\n\n`;if(n.forEach((t,e)=>{const{chunk:n,score:r}=t,i=n.metadata.heading||n.metadata.title,s=f(n.metadata),c=o[e].snippet;e>0&&(a+="\n\n---\n\n"),a+=`**${i}**\n\n`,a+=c,a+=`\n\nā [View full section](${s})`,r>h&&(a+=" *(highly relevant)*")}),e.length>3){const t=e.length-3;a+=`\n\n---\n\nš” **Found ${t} more related section${t>1?"s":""}** ā check the sources below for more details.`}else 1===e.length&&(a+="\n\n*Have a follow-up question? Just ask!*");return a}(t,l,u,d),p=l.map(t=>({title:g(t.chunk.metadata),url:f(t.chunk.metadata)}));return{answer:m,sources:Array.from(new Map(p.map(t=>[t.url,t])).values())}}catch(i){throw console.error("Search query error:",i),i}}function f(t){let e=t.url;return t.headingId&&(e+=`#${t.headingId}`),e}function g(t){return t.heading?`${t.title} ā ${t.heading}`:t.title}function p(){return c}function w(t){l=t}export{d as initializeRAG,u as isModelCached,p as isRAGReady,m as queryRAG,w as setTopK};
|
package/dist-client/bundle.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e={},t=function(t,n,o){let r=Promise.resolve();if(n&&n.length>0){let t=function(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:"fulfilled",value:e}),e=>({status:"rejected",reason:e}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),c=(null==o?void 0:o.nonce)||(null==o?void 0:o.getAttribute("nonce"));r=t(n.map(t=>{if((t=function(e){return"/"+e}(t))in e)return;e[t]=!0;const n=t.endsWith(".css"),o=n?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${t}"]${o}`))return;const r=document.createElement("link");return r.rel=n?"stylesheet":"modulepreload",n||(r.as="script"),r.crossOrigin="",r.href=t,c&&r.setAttribute("nonce",c),document.head.appendChild(r),n?new Promise((e,n)=>{r.addEventListener("load",e),r.addEventListener("error",()=>n(new Error(`Unable to preload CSS for ${t}`)))}):void 0}))}function c(e){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(e=>{for(const t of e||[])"rejected"===t.status&&c(t.reason);return t().catch(c)})},n="botdocs-theme";function o(){const e="dark"===document.documentElement.getAttribute("data-theme")?"light":"dark";r(e),localStorage.setItem(n,e)}function r(e){document.documentElement.setAttribute("data-theme",e)}document.addEventListener("DOMContentLoaded",()=>{!function(){const e=localStorage.getItem(n),t=window.matchMedia("(prefers-color-scheme: dark)").matches;r(e||(t?"dark":"light"));const c=document.querySelector(".theme-toggle");c&&c.addEventListener("click",o),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",e=>{localStorage.getItem(n)||r(e.matches?"dark":"light")})}(),function(){const e=window.location.pathname;document.querySelectorAll(".sidebar-nav a").forEach(t=>{const n=t.getAttribute("href");(n===e||"/"===e&&"/index.html"===n)&&t.classList.add("active")}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=e.getAttribute("href");if(n&&n.length>1){const e=document.querySelector(n);e&&e.scrollIntoView({behavior:"smooth"})}})})}();document.getElementById("chat-widget")&&t(async()=>{const{initChat:e}=await import("./assets/chatbox-
|
|
1
|
+
const e={},t=function(t,n,o){let r=Promise.resolve();if(n&&n.length>0){let t=function(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:"fulfilled",value:e}),e=>({status:"rejected",reason:e}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),c=(null==o?void 0:o.nonce)||(null==o?void 0:o.getAttribute("nonce"));r=t(n.map(t=>{if((t=function(e){return"/"+e}(t))in e)return;e[t]=!0;const n=t.endsWith(".css"),o=n?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${t}"]${o}`))return;const r=document.createElement("link");return r.rel=n?"stylesheet":"modulepreload",n||(r.as="script"),r.crossOrigin="",r.href=t,c&&r.setAttribute("nonce",c),document.head.appendChild(r),n?new Promise((e,n)=>{r.addEventListener("load",e),r.addEventListener("error",()=>n(new Error(`Unable to preload CSS for ${t}`)))}):void 0}))}function c(e){const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(e=>{for(const t of e||[])"rejected"===t.status&&c(t.reason);return t().catch(c)})},n="botdocs-theme";function o(){const e="dark"===document.documentElement.getAttribute("data-theme")?"light":"dark";r(e),localStorage.setItem(n,e)}function r(e){document.documentElement.setAttribute("data-theme",e)}document.addEventListener("DOMContentLoaded",()=>{!function(){const e=localStorage.getItem(n),t=window.matchMedia("(prefers-color-scheme: dark)").matches;r(e||(t?"dark":"light"));const c=document.querySelector(".theme-toggle");c&&c.addEventListener("click",o),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",e=>{localStorage.getItem(n)||r(e.matches?"dark":"light")})}(),function(){const e=window.location.pathname;document.querySelectorAll(".sidebar-nav a").forEach(t=>{const n=t.getAttribute("href");(n===e||"/"===e&&"/index.html"===n)&&t.classList.add("active")}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",t=>{t.preventDefault();const n=e.getAttribute("href");if(n&&n.length>1){const e=document.querySelector(n);e&&e.scrollIntoView({behavior:"smooth"})}})})}();document.getElementById("chat-widget")&&t(async()=>{const{initChat:e}=await import("./assets/chatbox-P1j6YP1y.js").then(e=>e.c);return{initChat:e}},[]).then(({initChat:e})=>{e()})});export{t as _};
|
|
Binary file
|
package/man/botdocs.1
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
.TH BOTDOCS 1 "2026-03-28" "botdocs 0.3.0" "User Commands"
|
|
2
|
+
.SH NAME
|
|
3
|
+
botdocs \- convert markdown documentation into a static website with AI chatbot
|
|
4
|
+
.SH SYNOPSIS
|
|
5
|
+
.B botdocs
|
|
6
|
+
.RI [ options ]
|
|
7
|
+
.I input
|
|
8
|
+
.SH DESCRIPTION
|
|
9
|
+
.B botdocs
|
|
10
|
+
converts a directory of markdown files into a fully static documentation website.
|
|
11
|
+
An optional client-side semantic search chatbot is included by default, powered by
|
|
12
|
+
Transformers.js running entirely in the browser with no backend required.
|
|
13
|
+
.PP
|
|
14
|
+
The build pipeline scans markdown files, parses front matter, converts to HTML,
|
|
15
|
+
generates vector embeddings for semantic search, and bundles everything into a
|
|
16
|
+
self-contained static site.
|
|
17
|
+
.SH ARGUMENTS
|
|
18
|
+
.TP
|
|
19
|
+
.I input
|
|
20
|
+
Input directory containing markdown files. Required.
|
|
21
|
+
.SH OPTIONS
|
|
22
|
+
.TP
|
|
23
|
+
.BR \-o ", " \-\-output " " \fIdir\fR
|
|
24
|
+
Output directory for the generated site. Defaults to
|
|
25
|
+
.BR output .
|
|
26
|
+
.TP
|
|
27
|
+
.B \-\-no\-chat
|
|
28
|
+
Disable the AI chatbot functionality. The generated site will not include
|
|
29
|
+
semantic search or the chat interface.
|
|
30
|
+
.TP
|
|
31
|
+
.BR \-c ", " \-\-config " " \fIfile\fR
|
|
32
|
+
Path to a configuration file. If not specified, botdocs looks for
|
|
33
|
+
.B botdocs.config.json
|
|
34
|
+
in the input directory.
|
|
35
|
+
.TP
|
|
36
|
+
.BR \-t ", " \-\-theme " " \fItheme\fR
|
|
37
|
+
Theme to use for the generated site. Available themes:
|
|
38
|
+
.BR classic " (default), " material ", " minimal ", " slate ", " modern .
|
|
39
|
+
.TP
|
|
40
|
+
.BR \-v ", " \-\-verbose
|
|
41
|
+
Enable verbose logging during the build process.
|
|
42
|
+
.TP
|
|
43
|
+
.BR \-V ", " \-\-version
|
|
44
|
+
Output the version number.
|
|
45
|
+
.TP
|
|
46
|
+
.BR \-h ", " \-\-help
|
|
47
|
+
Display help information.
|
|
48
|
+
.SH CONFIGURATION
|
|
49
|
+
A
|
|
50
|
+
.B botdocs.config.json
|
|
51
|
+
file can be placed in the input directory to configure the build:
|
|
52
|
+
.PP
|
|
53
|
+
.RS 4
|
|
54
|
+
.nf
|
|
55
|
+
{
|
|
56
|
+
"title": "My Documentation",
|
|
57
|
+
"chat": { "enabled": true },
|
|
58
|
+
"build": {
|
|
59
|
+
"chunkSize": 500,
|
|
60
|
+
"chunkOverlap": 50,
|
|
61
|
+
"topK": 5
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
.fi
|
|
65
|
+
.RE
|
|
66
|
+
.PP
|
|
67
|
+
CLI options override configuration file settings.
|
|
68
|
+
.SH EXAMPLES
|
|
69
|
+
Build a documentation site from a directory:
|
|
70
|
+
.PP
|
|
71
|
+
.RS 4
|
|
72
|
+
.nf
|
|
73
|
+
botdocs ./docs
|
|
74
|
+
.fi
|
|
75
|
+
.RE
|
|
76
|
+
.PP
|
|
77
|
+
Build with a custom output directory and verbose logging:
|
|
78
|
+
.PP
|
|
79
|
+
.RS 4
|
|
80
|
+
.nf
|
|
81
|
+
botdocs ./docs -o ./site -v
|
|
82
|
+
.fi
|
|
83
|
+
.RE
|
|
84
|
+
.PP
|
|
85
|
+
Build without the chatbot:
|
|
86
|
+
.PP
|
|
87
|
+
.RS 4
|
|
88
|
+
.nf
|
|
89
|
+
botdocs ./docs --no-chat
|
|
90
|
+
.fi
|
|
91
|
+
.RE
|
|
92
|
+
.PP
|
|
93
|
+
Use the material theme:
|
|
94
|
+
.PP
|
|
95
|
+
.RS 4
|
|
96
|
+
.nf
|
|
97
|
+
botdocs ./docs -t material
|
|
98
|
+
.fi
|
|
99
|
+
.RE
|
|
100
|
+
.SH EXIT STATUS
|
|
101
|
+
.TP
|
|
102
|
+
.B 0
|
|
103
|
+
Successful build.
|
|
104
|
+
.TP
|
|
105
|
+
.B 1
|
|
106
|
+
Build failed.
|
|
107
|
+
.SH FILES
|
|
108
|
+
.TP
|
|
109
|
+
.I botdocs.config.json
|
|
110
|
+
Optional configuration file in the input directory.
|
|
111
|
+
.TP
|
|
112
|
+
.I output/
|
|
113
|
+
Default output directory containing the generated static site.
|
|
114
|
+
.TP
|
|
115
|
+
.I output/assets/vector\-db.json
|
|
116
|
+
Vector database used by the client-side semantic search.
|
|
117
|
+
.SH AUTHORS
|
|
118
|
+
usr-wwelsh <https://wwel.sh>
|
|
119
|
+
.SH BUGS
|
|
120
|
+
Report bugs at:
|
|
121
|
+
.UR https://github.com/usr-wwelsh/botdocs/issues
|
|
122
|
+
.UE
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botdocs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI tool that converts markdown documentation into a static website with an optional client-side AI chatbot",
|
|
5
5
|
"author": "usr-wwelsh <https://wwel.sh>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
8
|
"botdocs": "./bin/botdocs.js"
|
|
9
9
|
},
|
|
10
|
+
"man": "./man/botdocs.1",
|
|
10
11
|
"main": "./dist/src/cli/index.js",
|
|
11
12
|
"type": "module",
|
|
12
13
|
"files": [
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"bin/",
|
|
16
17
|
"src/templates/",
|
|
17
18
|
"src/styles/",
|
|
19
|
+
"man/",
|
|
18
20
|
"README.md",
|
|
19
21
|
"LICENSE"
|
|
20
22
|
],
|
|
@@ -35,8 +37,8 @@
|
|
|
35
37
|
"cli"
|
|
36
38
|
],
|
|
37
39
|
"dependencies": {
|
|
40
|
+
"@huggingface/transformers": "^4.2.0",
|
|
38
41
|
"@shikijs/markdown-it": "^1.0.0",
|
|
39
|
-
"@xenova/transformers": "^2.17.0",
|
|
40
42
|
"commander": "^12.0.0",
|
|
41
43
|
"gray-matter": "^4.0.3",
|
|
42
44
|
"markdown-it": "^14.0.0",
|
|
@@ -57,6 +59,11 @@
|
|
|
57
59
|
"typescript": "^5.3.0",
|
|
58
60
|
"vite": "^6.0.0"
|
|
59
61
|
},
|
|
62
|
+
"overrides": {
|
|
63
|
+
"onnxruntime-node": "^1.27.0",
|
|
64
|
+
"adm-zip": "^0.6.0",
|
|
65
|
+
"sharp": "^0.35.3"
|
|
66
|
+
},
|
|
60
67
|
"engines": {
|
|
61
68
|
"node": ">=18.0.0"
|
|
62
69
|
},
|
package/src/styles/chat.css
CHANGED
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
align-items: center;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
.chat-info,
|
|
71
72
|
.chat-clear,
|
|
72
73
|
.chat-close {
|
|
73
74
|
background: none;
|
|
@@ -89,11 +90,50 @@
|
|
|
89
90
|
font-size: 1.5rem;
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
.chat-info:hover,
|
|
92
94
|
.chat-clear:hover,
|
|
93
95
|
.chat-close:hover {
|
|
94
96
|
background: rgba(255, 255, 255, 0.2);
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
.chat-disclosure {
|
|
100
|
+
padding: 1rem 1.25rem;
|
|
101
|
+
background: var(--bg-tertiary);
|
|
102
|
+
color: var(--text-secondary);
|
|
103
|
+
font-size: 0.8125rem;
|
|
104
|
+
line-height: 1.5;
|
|
105
|
+
border-bottom: 1px solid var(--border-color);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.chat-disclosure p {
|
|
109
|
+
margin: 0 0 0.5rem 0;
|
|
110
|
+
color: var(--text-primary);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.chat-disclosure ul {
|
|
114
|
+
margin: 0 0 0.75rem 0;
|
|
115
|
+
padding-left: 1.25rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.chat-disclosure li {
|
|
119
|
+
margin-bottom: 0.375rem;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.chat-disclosure-close {
|
|
123
|
+
padding: 0.375rem 0.875rem;
|
|
124
|
+
background: var(--link-color);
|
|
125
|
+
color: white;
|
|
126
|
+
border: none;
|
|
127
|
+
border-radius: 0.375rem;
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
font-size: 0.8125rem;
|
|
130
|
+
font-weight: 500;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.chat-disclosure-close:hover {
|
|
134
|
+
background: var(--link-hover);
|
|
135
|
+
}
|
|
136
|
+
|
|
97
137
|
.chat-messages {
|
|
98
138
|
flex: 1;
|
|
99
139
|
overflow-y: auto;
|
|
@@ -398,6 +438,92 @@
|
|
|
398
438
|
transition: width 0.3s ease;
|
|
399
439
|
}
|
|
400
440
|
|
|
441
|
+
.chat-consent p {
|
|
442
|
+
margin: 0 0 0.75rem 0;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.chat-consent-actions {
|
|
446
|
+
display: flex;
|
|
447
|
+
gap: 0.5rem;
|
|
448
|
+
justify-content: center;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.chat-consent-download,
|
|
452
|
+
.chat-consent-retry {
|
|
453
|
+
padding: 0.5rem 1rem;
|
|
454
|
+
background: var(--link-color);
|
|
455
|
+
color: white;
|
|
456
|
+
border: none;
|
|
457
|
+
border-radius: 0.375rem;
|
|
458
|
+
cursor: pointer;
|
|
459
|
+
font-size: 0.8125rem;
|
|
460
|
+
font-weight: 500;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.chat-consent-download:hover,
|
|
464
|
+
.chat-consent-retry:hover {
|
|
465
|
+
background: var(--link-hover);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.chat-consent-retry {
|
|
469
|
+
margin-left: 0.5rem;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.chat-consent-decline {
|
|
473
|
+
padding: 0.5rem 1rem;
|
|
474
|
+
background: none;
|
|
475
|
+
color: var(--text-secondary);
|
|
476
|
+
border: 1px solid var(--border-color);
|
|
477
|
+
border-radius: 0.375rem;
|
|
478
|
+
cursor: pointer;
|
|
479
|
+
font-size: 0.8125rem;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.chat-consent-decline:hover {
|
|
483
|
+
background: var(--bg-secondary);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* AI Use Disclosure Banner (full-bleed, breaks out of .main-content padding) */
|
|
487
|
+
.ai-disclosure {
|
|
488
|
+
margin: 3rem -3rem 0 -3rem;
|
|
489
|
+
padding: 1rem 3rem;
|
|
490
|
+
background: var(--link-color);
|
|
491
|
+
color: white;
|
|
492
|
+
text-align: center;
|
|
493
|
+
font-size: 0.875rem;
|
|
494
|
+
font-weight: 500;
|
|
495
|
+
line-height: 1.5;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/* Footer Attribution */
|
|
499
|
+
.botdocs-attribution {
|
|
500
|
+
margin-top: 4rem;
|
|
501
|
+
padding-top: 2rem;
|
|
502
|
+
border-top: 1px solid var(--border-color);
|
|
503
|
+
text-align: center;
|
|
504
|
+
font-size: 0.875rem;
|
|
505
|
+
color: var(--text-secondary);
|
|
506
|
+
opacity: 0.8;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.ai-disclosure + .botdocs-attribution {
|
|
510
|
+
margin-top: 1.5rem;
|
|
511
|
+
padding-top: 0;
|
|
512
|
+
border-top: none;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.botdocs-attribution a {
|
|
516
|
+
color: var(--text-secondary);
|
|
517
|
+
text-decoration: none;
|
|
518
|
+
font-weight: 500;
|
|
519
|
+
transition: color 0.2s;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.botdocs-attribution a:hover {
|
|
523
|
+
color: var(--link-color);
|
|
524
|
+
text-decoration: none;
|
|
525
|
+
}
|
|
526
|
+
|
|
401
527
|
/* Mobile responsiveness */
|
|
402
528
|
@media (max-width: 768px) {
|
|
403
529
|
.chat-widget {
|
|
@@ -417,4 +543,9 @@
|
|
|
417
543
|
height: 3.5rem;
|
|
418
544
|
font-size: 1.5rem;
|
|
419
545
|
}
|
|
546
|
+
|
|
547
|
+
.ai-disclosure {
|
|
548
|
+
margin: 2rem -1.5rem 0 -1.5rem;
|
|
549
|
+
padding: 0.875rem 1.5rem;
|
|
550
|
+
}
|
|
420
551
|
}
|
|
@@ -96,6 +96,35 @@ body {
|
|
|
96
96
|
font-weight: 500;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
.nav-list li.nav-group > a {
|
|
100
|
+
font-weight: 600;
|
|
101
|
+
position: relative;
|
|
102
|
+
padding-right: 1.75rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.nav-list li.nav-group > a::after {
|
|
106
|
+
content: 'āŗ';
|
|
107
|
+
position: absolute;
|
|
108
|
+
right: 0.75rem;
|
|
109
|
+
transition: transform 0.2s;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.nav-list li.nav-group:has(> .nav-list) > a::after {
|
|
113
|
+
transform: rotate(90deg);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.nav-list .nav-list {
|
|
117
|
+
margin-top: 0.375rem;
|
|
118
|
+
padding-left: 0.75rem;
|
|
119
|
+
border-left: 1px solid var(--border-color);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.nav-list .nav-list a {
|
|
123
|
+
font-weight: 400;
|
|
124
|
+
font-size: 0.9rem;
|
|
125
|
+
padding: 0.375rem 0.75rem;
|
|
126
|
+
}
|
|
127
|
+
|
|
99
128
|
/* Main Content */
|
|
100
129
|
.main-content {
|
|
101
130
|
margin-left: var(--sidebar-width);
|
|
@@ -105,6 +105,35 @@ body {
|
|
|
105
105
|
box-shadow: var(--shadow-1);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
.nav-list li.nav-group > a {
|
|
109
|
+
font-weight: 700;
|
|
110
|
+
position: relative;
|
|
111
|
+
padding-right: 1.75rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.nav-list li.nav-group > a::after {
|
|
115
|
+
content: 'āŗ';
|
|
116
|
+
position: absolute;
|
|
117
|
+
right: 1rem;
|
|
118
|
+
transition: transform 0.2s;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.nav-list li.nav-group:has(> .nav-list) > a::after {
|
|
122
|
+
transform: rotate(90deg);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.nav-list .nav-list {
|
|
126
|
+
margin-top: 0.25rem;
|
|
127
|
+
padding-left: 0.75rem;
|
|
128
|
+
border-left: 2px solid var(--bg-tertiary);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.nav-list .nav-list a {
|
|
132
|
+
font-weight: 400;
|
|
133
|
+
font-size: 0.85rem;
|
|
134
|
+
padding: 0.5rem 0.75rem;
|
|
135
|
+
}
|
|
136
|
+
|
|
108
137
|
/* Main Content */
|
|
109
138
|
.main-content {
|
|
110
139
|
margin-left: var(--sidebar-width);
|
|
@@ -109,6 +109,40 @@ body {
|
|
|
109
109
|
border-bottom-color: var(--text-primary);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
.nav-list li.nav-group {
|
|
113
|
+
flex-basis: 100%;
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-wrap: wrap;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
align-items: baseline;
|
|
118
|
+
gap: 0.375rem 1rem;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.nav-list li.nav-group > a {
|
|
122
|
+
color: var(--text-primary);
|
|
123
|
+
font-weight: 600;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.nav-list li.nav-group > a::after {
|
|
127
|
+
content: 'āŗ';
|
|
128
|
+
margin-left: 0.25rem;
|
|
129
|
+
display: inline-block;
|
|
130
|
+
transition: transform 0.2s;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.nav-list li.nav-group:has(> .nav-list) > a::after {
|
|
134
|
+
transform: rotate(90deg);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.nav-list .nav-list {
|
|
138
|
+
margin: 0;
|
|
139
|
+
gap: 0.375rem 1rem;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.nav-list .nav-list a {
|
|
143
|
+
font-size: 0.8rem;
|
|
144
|
+
}
|
|
145
|
+
|
|
112
146
|
/* Main Content - centered, no sidebar offset */
|
|
113
147
|
.main-content {
|
|
114
148
|
margin-left: 0;
|
|
@@ -122,6 +122,40 @@ body {
|
|
|
122
122
|
box-shadow: 0 4px 12px rgba(100, 108, 255, 0.3);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
.nav-list li.nav-group {
|
|
126
|
+
flex-basis: 100%;
|
|
127
|
+
display: flex;
|
|
128
|
+
flex-wrap: wrap;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: 0.5rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.nav-list li.nav-group > a {
|
|
134
|
+
font-weight: 700;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.nav-list li.nav-group > a::after {
|
|
138
|
+
content: 'āŗ';
|
|
139
|
+
margin-left: 0.375rem;
|
|
140
|
+
display: inline-block;
|
|
141
|
+
transition: transform 0.2s;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.nav-list li.nav-group:has(> .nav-list) > a::after {
|
|
145
|
+
transform: rotate(90deg);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.nav-list .nav-list {
|
|
149
|
+
margin: 0;
|
|
150
|
+
gap: 0.375rem;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.nav-list .nav-list a {
|
|
154
|
+
padding: 0.375rem 0.875rem;
|
|
155
|
+
font-size: 0.85rem;
|
|
156
|
+
font-weight: 400;
|
|
157
|
+
}
|
|
158
|
+
|
|
125
159
|
/* Main Content - full width, no sidebar offset */
|
|
126
160
|
.main-content {
|
|
127
161
|
margin-left: 0;
|
|
@@ -112,47 +112,49 @@ body {
|
|
|
112
112
|
font-weight: 600;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
.nav-list li.nav-group > a {
|
|
116
|
+
color: var(--sidebar-text);
|
|
117
|
+
font-weight: 600;
|
|
118
|
+
position: relative;
|
|
119
|
+
padding-right: 1.75rem;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.nav-list li.nav-group > a::after {
|
|
123
|
+
content: 'āŗ';
|
|
124
|
+
position: absolute;
|
|
125
|
+
right: 1rem;
|
|
126
|
+
transition: transform 0.2s;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.nav-list li.nav-group:has(> .nav-list) > a::after {
|
|
130
|
+
transform: rotate(90deg);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.nav-list .nav-list {
|
|
134
|
+
margin-top: 0.125rem;
|
|
135
|
+
padding-left: 1rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.nav-list .nav-list a {
|
|
139
|
+
font-weight: 400;
|
|
140
|
+
font-size: 0.85rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
/* Main Content - Split screen with code examples */
|
|
116
144
|
.main-content {
|
|
117
145
|
margin-left: var(--sidebar-width);
|
|
118
146
|
flex: 1;
|
|
119
147
|
padding: 0;
|
|
120
148
|
background: var(--bg-primary);
|
|
121
|
-
display: flex;
|
|
122
|
-
gap: 0;
|
|
123
149
|
}
|
|
124
150
|
|
|
125
151
|
.content-wrapper {
|
|
126
|
-
flex: 1;
|
|
127
152
|
max-width: none;
|
|
128
153
|
padding: 3rem 2.5rem;
|
|
129
154
|
background: var(--bg-primary);
|
|
130
155
|
min-width: 0;
|
|
131
156
|
}
|
|
132
157
|
|
|
133
|
-
/* Code blocks stick to right side for split-screen effect */
|
|
134
|
-
.content-wrapper pre {
|
|
135
|
-
position: relative;
|
|
136
|
-
margin-left: calc(-50vw + 50%);
|
|
137
|
-
margin-right: -2.5rem;
|
|
138
|
-
margin-top: -3rem;
|
|
139
|
-
margin-bottom: 2rem;
|
|
140
|
-
width: calc(100% + 50vw - 50% + 2.5rem);
|
|
141
|
-
max-width: 800px;
|
|
142
|
-
float: right;
|
|
143
|
-
clear: right;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/* Adjust paragraph spacing when followed by code */
|
|
147
|
-
.content-wrapper p + pre {
|
|
148
|
-
margin-top: -2rem;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.content-wrapper h3 + pre,
|
|
152
|
-
.content-wrapper h4 + pre {
|
|
153
|
-
margin-top: 1rem;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
158
|
/* Typography - clean and technical */
|
|
157
159
|
h1, h2, h3, h4, h5, h6 {
|
|
158
160
|
margin-top: 2.5rem;
|
|
@@ -228,8 +230,10 @@ code {
|
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
pre {
|
|
231
|
-
background
|
|
232
|
-
|
|
233
|
+
/* Shiki inlines a light-theme background/color on the element itself;
|
|
234
|
+
override it so slate's dark code style actually wins. */
|
|
235
|
+
background: var(--code-bg) !important;
|
|
236
|
+
color: var(--code-text) !important;
|
|
233
237
|
padding: 1.5rem;
|
|
234
238
|
border-radius: 6px;
|
|
235
239
|
overflow-x: auto;
|
|
@@ -692,14 +696,6 @@ img {
|
|
|
692
696
|
padding: 2rem 1.5rem;
|
|
693
697
|
}
|
|
694
698
|
|
|
695
|
-
.content-wrapper pre {
|
|
696
|
-
margin-left: 0;
|
|
697
|
-
margin-right: 0;
|
|
698
|
-
width: 100%;
|
|
699
|
-
float: none;
|
|
700
|
-
clear: both;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
699
|
.layout {
|
|
704
700
|
flex-direction: column;
|
|
705
701
|
}
|
|
@@ -35,6 +35,13 @@
|
|
|
35
35
|
{{content}}
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
+
{{#if chatEnabled}}
|
|
39
|
+
<p class="ai-disclosure">
|
|
40
|
+
š This site's search assistant runs a local AI model in your browser ā no cloud AI,
|
|
41
|
+
no tracking. See the ā¹ļø in the chat panel for details.
|
|
42
|
+
</p>
|
|
43
|
+
{{/if}}
|
|
44
|
+
|
|
38
45
|
{{#if attribution}}
|
|
39
46
|
<!-- Footer Attribution -->
|
|
40
47
|
<footer class="botdocs-attribution">
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as t}from"../bundle.js";function e(t){let e=t;return e=function(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}(e),e=e.replace(/```(\w+)?\n([\s\S]*?)```/g,(t,e,n)=>`<pre><code${e?` class="language-${e}"`:""}>${n.trim()}</code></pre>`),e=e.replace(/`([^`]+)`/g,"<code>$1</code>"),e=e.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),e=e.replace(/__(.+?)__/g,"<strong>$1</strong>"),e=e.replace(/\*(.+?)\*/g,"<em>$1</em>"),e=e.replace(/_(.+?)_/g,"<em>$1</em>"),e=e.replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank">$1</a>'),e=e.replace(/^### (.+)$/gm,"<h3>$1</h3>"),e=e.replace(/^## (.+)$/gm,"<h2>$1</h2>"),e=e.replace(/^# (.+)$/gm,"<h1>$1</h1>"),e=e.replace(/^---$/gm,"<hr />"),e=e.replace(/\n\n/g,"</p><p>"),e=e.replace(/\n/g,"<br />"),e=`<p>${e}</p>`,e=e.replace(/<p><\/p>/g,""),e=e.replace(/<p>(<pre>)/g,"$1"),e=e.replace(/(<\/pre>)<\/p>/g,"$1"),e=e.replace(/<p>(<h[1-3]>)/g,"$1"),e=e.replace(/(<\/h[1-3]>)<\/p>/g,"$1"),e=e.replace(/<p>(<hr \/>)<\/p>/g,"$1"),e}let n=!1,a=[],s=!1;const c="botdocs_chat_history",o="botdocs_chat_open",i="botdocs_chat_timestamp",r=6e5;function l(){const t=document.getElementById("chat-toggle"),e=document.getElementById("chat-container");t&&e?(!function(){try{const t=localStorage.getItem(i),e=Date.now();if(t){if(e-parseInt(t,10)>r)return void I()}const s=localStorage.getItem(c);s&&(a=JSON.parse(s));const l=localStorage.getItem(o);l&&(n=JSON.parse(l))}catch(t){console.error("Failed to load chat state:",t),a=[],n=!1}}(),t.addEventListener("click",()=>{d()}),h(e),a.length>0&&function(){const t=document.getElementById("chat-messages");if(!t)return;t.innerHTML="";for(const e of a)y(e,t)}(),n&&(e.style.display="flex",s||g().then(()=>{s=!0}))):console.error("Chat elements not found")}async function d(){n=!n;const t=document.getElementById("chat-container");t&&(t.style.display=n?"flex":"none"),n&&!s&&(await g(),s=!0),function(){try{localStorage.setItem(o,JSON.stringify(n))}catch(t){console.error("Failed to save chat state:",t)}}()}function h(t){t.innerHTML='\n <div class="chat-header">\n <h3>Ask me anything</h3>\n <div class="chat-header-actions">\n <button class="chat-clear" id="chat-clear" title="Clear chat history">šļø</button>\n <button class="chat-close" id="chat-close">Ć</button>\n </div>\n </div>\n <div class="chat-status" id="chat-status" style="display: none;">\n Initializing chat...\n </div>\n <div class="chat-messages" id="chat-messages">\n <div class="chat-message assistant">\n <div class="message-content">\n š Hi! I\'m your documentation assistant. Ask me anything about these docs!\n </div>\n </div>\n </div>\n <div class="chat-input-container">\n <div class="chat-input-wrapper">\n <textarea\n class="chat-input"\n id="chat-input"\n placeholder="Ask a question..."\n rows="1"\n ></textarea>\n <button class="chat-send" id="chat-send">Send</button>\n </div>\n </div>\n ';const e=document.getElementById("chat-close"),n=document.getElementById("chat-clear"),a=document.getElementById("chat-send"),c=document.getElementById("chat-input");if(e&&e.addEventListener("click",d),n&&n.addEventListener("click",m),a&&c&&(a.addEventListener("click",()=>u(c)),c.addEventListener("keypress",t=>{"Enter"!==t.key||t.shiftKey||(t.preventDefault(),u(c))}),c.addEventListener("input",()=>{c.style.height="auto",c.style.height=c.scrollHeight+"px"})),s){const t=document.getElementById("chat-status");t&&(t.style.display="none")}}async function g(){const e=document.getElementById("chat-status");if(e)try{e.style.display="block",e.textContent="Loading AI models...";const{initializeRAG:n,isRAGReady:a}=await t(async()=>{const{initializeRAG:t,isRAGReady:e}=await import("./rag-engine-L2vj3Y0G.js");return{initializeRAG:t,isRAGReady:e}},[]);await n(t=>{e.innerHTML=`\n Loading models... ${Math.round(t)}%\n <div class="progress-bar">\n <div class="progress-fill" style="width: ${t}%"></div>\n </div>\n `}),a()&&(e.style.display="none")}catch(n){console.error("Failed to initialize RAG:",n),e.textContent="ā ļø Chat unavailable"}}function m(){if(confirm("Clear chat history?")){I();const t=document.getElementById("chat-messages");t&&(t.innerHTML='\n <div class="chat-message assistant">\n <div class="message-content">\n š Hi! I\'m your documentation assistant. Ask me anything about these docs!\n </div>\n </div>\n ')}}async function u(e){const n=e.value.trim();if(!n)return;p({role:"user",content:n}),e.value="",e.style.height="auto";const a=document.getElementById("chat-send");a&&(a.disabled=!0),e.disabled=!0,function(){const t=document.getElementById("chat-messages");if(!t)return;const e=document.createElement("div");e.id="loading-indicator",e.className="chat-message assistant",e.innerHTML='\n <div class="loading-indicator">\n <div class="loading-dot"></div>\n <div class="loading-dot"></div>\n <div class="loading-dot"></div>\n </div>\n ',t.appendChild(e),t.scrollTop=t.scrollHeight}();try{const{queryRAG:e}=await t(async()=>{const{queryRAG:t}=await import("./rag-engine-L2vj3Y0G.js");return{queryRAG:t}},[]),a=await e(n);v(),p({role:"assistant",content:a.answer,citations:a.sources})}catch(s){console.error("Chat error:",s),v(),p({role:"assistant",content:"Sorry, I encountered an error. Please try again."})}finally{a&&(a.disabled=!1),e.disabled=!1,e.focus()}}function p(t){a.push(t),function(){try{localStorage.setItem(c,JSON.stringify(a)),localStorage.setItem(i,Date.now().toString())}catch(t){console.error("Failed to save chat history:",t)}}();const e=document.getElementById("chat-messages");e&&y(t,e)}function y(t,n){const a=document.createElement("div");a.className=`chat-message ${t.role}`;let s="";s="user"===t.role?f(t.content):e(t.content);let c=`<div class="message-content">${s}</div>`;if(t.citations&&t.citations.length>0){c+='<div class="message-citations">',c+='<div class="citations-label">š Sources:</div>';for(const e of t.citations)c+=`<a href="${f(e.url)}" class="citation-link">${f(e.title)}</a>`;c+="</div>"}a.innerHTML=c,n.appendChild(a),n.scrollTo({top:n.scrollHeight,behavior:"smooth"})}function v(){const t=document.getElementById("loading-indicator");t&&t.remove()}function f(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function I(){try{localStorage.removeItem(c),localStorage.removeItem(i),a=[]}catch(t){console.error("Failed to clear chat history:",t)}}export{p as addMessage,l as initChat,h as renderChatUI,d as toggleChat};
|