@dogsbay/plugin-ask 0.2.0-beta.82 → 0.2.0-beta.84
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/client.js +52 -8
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/md.d.ts +14 -0
- package/dist/md.d.ts.map +1 -0
- package/dist/md.js +115 -0
- package/dist/md.js.map +1 -0
- package/dist/protocol.d.ts +13 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +51 -0
- package/dist/protocol.js.map +1 -1
- package/dist/schema.d.ts +7 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +4 -0
- package/dist/schema.js.map +1 -1
- package/package.json +2 -2
- package/src/client.ts +62 -8
- package/src/styles.css +32 -1
package/dist/client.js
CHANGED
|
@@ -12,8 +12,36 @@
|
|
|
12
12
|
*/
|
|
13
13
|
// @ts-expect-error: virtual module resolved by the Dogsbay Vite alias at build.
|
|
14
14
|
import config from "virtual:dogsbay-plugin-config/@dogsbay/plugin-ask";
|
|
15
|
-
import { buildAskRequest, extractText } from "./protocol.js";
|
|
15
|
+
import { buildAskRequest, extractText, parseSources } from "./protocol.js";
|
|
16
|
+
import { renderMarkdown, escapeHtml } from "./md.js";
|
|
16
17
|
const cfg = config;
|
|
18
|
+
// Turnstile token (refreshed per ask). Populated by the widget callbacks.
|
|
19
|
+
let turnstileToken = null;
|
|
20
|
+
let turnstileScriptLoaded = false;
|
|
21
|
+
function turnstileApi() {
|
|
22
|
+
return window.turnstile;
|
|
23
|
+
}
|
|
24
|
+
function loadTurnstile() {
|
|
25
|
+
if (turnstileScriptLoaded || !cfg.turnstileSitekey)
|
|
26
|
+
return;
|
|
27
|
+
turnstileScriptLoaded = true;
|
|
28
|
+
const w = window;
|
|
29
|
+
w.__dbaTsCb = (t) => { turnstileToken = t; };
|
|
30
|
+
w.__dbaTsExpired = () => { turnstileToken = null; };
|
|
31
|
+
const s = document.createElement("script");
|
|
32
|
+
s.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
|
|
33
|
+
s.async = true;
|
|
34
|
+
s.defer = true;
|
|
35
|
+
document.head.appendChild(s);
|
|
36
|
+
}
|
|
37
|
+
function sourcesHtml(sources) {
|
|
38
|
+
const items = sources
|
|
39
|
+
.map((s) => s.href
|
|
40
|
+
? `<li><a href="${escapeHtml(s.href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(s.label)}</a></li>`
|
|
41
|
+
: `<li>${escapeHtml(s.label)}</li>`)
|
|
42
|
+
.join("");
|
|
43
|
+
return `<div class="dba-sources"><div class="dba-sources-title">Sources</div><ul>${items}</ul></div>`;
|
|
44
|
+
}
|
|
17
45
|
const ROOT_ID = "dba-root";
|
|
18
46
|
function mount() {
|
|
19
47
|
if (typeof document === "undefined")
|
|
@@ -31,6 +59,9 @@ function mount() {
|
|
|
31
59
|
const dialog = document.createElement("dialog");
|
|
32
60
|
dialog.className = "dba-dialog";
|
|
33
61
|
dialog.setAttribute("aria-label", cfg.title);
|
|
62
|
+
const tsMarkup = cfg.turnstileSitekey
|
|
63
|
+
? `<div class="cf-turnstile dba-turnstile" data-sitekey="${cfg.turnstileSitekey}" data-appearance="interaction-only" data-callback="__dbaTsCb" data-expired-callback="__dbaTsExpired" data-error-callback="__dbaTsExpired"></div>`
|
|
64
|
+
: "";
|
|
34
65
|
dialog.innerHTML = `
|
|
35
66
|
<form class="dba-form" method="dialog">
|
|
36
67
|
<header class="dba-header">
|
|
@@ -41,9 +72,11 @@ function mount() {
|
|
|
41
72
|
<input class="dba-input" type="text" autocomplete="off" enterkeyhint="search" />
|
|
42
73
|
<button type="submit" class="dba-submit">Ask</button>
|
|
43
74
|
</div>
|
|
75
|
+
${tsMarkup}
|
|
44
76
|
<div class="dba-answer" aria-live="polite" tabindex="-1"></div>
|
|
45
77
|
</form>
|
|
46
78
|
`;
|
|
79
|
+
loadTurnstile();
|
|
47
80
|
// textContent (not innerHTML) for user-configurable strings — no injection.
|
|
48
81
|
dialog.querySelector(".dba-title").textContent = cfg.title;
|
|
49
82
|
const input = dialog.querySelector(".dba-input");
|
|
@@ -68,7 +101,10 @@ function mount() {
|
|
|
68
101
|
const res = await fetch(cfg.endpoint, {
|
|
69
102
|
method: "POST",
|
|
70
103
|
headers: { "Content-Type": "application/json" },
|
|
71
|
-
body: JSON.stringify(
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
...buildAskRequest(cfg.site, query, cfg.mode),
|
|
106
|
+
...(turnstileToken ? { turnstileToken } : {}),
|
|
107
|
+
}),
|
|
72
108
|
});
|
|
73
109
|
if (!res.ok || !res.body) {
|
|
74
110
|
throw new Error(`Request failed (${res.status})`);
|
|
@@ -77,22 +113,27 @@ function mount() {
|
|
|
77
113
|
answer.dataset.state = "streaming";
|
|
78
114
|
const reader = res.body.getReader();
|
|
79
115
|
const decoder = new TextDecoder();
|
|
80
|
-
let buf = "";
|
|
116
|
+
let buf = ""; // line buffer (don't parse a frame split across reads)
|
|
117
|
+
let rawAll = ""; // whole stream, for the trailing `event: chunks` sources
|
|
118
|
+
let answerMd = ""; // accumulated answer markdown
|
|
81
119
|
for (;;) {
|
|
82
120
|
const { value, done } = await reader.read();
|
|
83
121
|
if (done)
|
|
84
122
|
break;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
123
|
+
const text = decoder.decode(value, { stream: true });
|
|
124
|
+
rawAll += text;
|
|
125
|
+
buf += text;
|
|
88
126
|
const nl = buf.lastIndexOf("\n");
|
|
89
127
|
if (nl >= 0) {
|
|
90
|
-
|
|
128
|
+
answerMd += extractText(buf.slice(0, nl + 1));
|
|
91
129
|
buf = buf.slice(nl + 1);
|
|
130
|
+
answer.innerHTML = renderMarkdown(answerMd);
|
|
92
131
|
}
|
|
93
132
|
}
|
|
94
133
|
if (buf)
|
|
95
|
-
|
|
134
|
+
answerMd += extractText(buf);
|
|
135
|
+
const sources = parseSources(rawAll, cfg.linkBase);
|
|
136
|
+
answer.innerHTML = renderMarkdown(answerMd) + (sources.length ? sourcesHtml(sources) : "");
|
|
96
137
|
answer.dataset.state = "done";
|
|
97
138
|
}
|
|
98
139
|
catch (err) {
|
|
@@ -101,6 +142,9 @@ function mount() {
|
|
|
101
142
|
}
|
|
102
143
|
finally {
|
|
103
144
|
submit.disabled = false;
|
|
145
|
+
// Turnstile tokens are single-use — refresh for the next question.
|
|
146
|
+
turnstileApi()?.reset?.();
|
|
147
|
+
turnstileToken = null;
|
|
104
148
|
}
|
|
105
149
|
});
|
|
106
150
|
root.append(button, dialog);
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,gFAAgF;AAChF,OAAO,MAAM,MAAM,mDAAmD,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,gFAAgF;AAChF,OAAO,MAAM,MAAM,mDAAmD,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAe,MAAM,eAAe,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAerD,MAAM,GAAG,GAAG,MAAuB,CAAC;AAEpC,0EAA0E;AAC1E,IAAI,cAAc,GAAkB,IAAI,CAAC;AACzC,IAAI,qBAAqB,GAAG,KAAK,CAAC;AAKlC,SAAS,YAAY;IACnB,OAAQ,MAAkD,CAAC,SAAS,CAAC;AACvE,CAAC;AAED,SAAS,aAAa;IACpB,IAAI,qBAAqB,IAAI,CAAC,GAAG,CAAC,gBAAgB;QAAE,OAAO;IAC3D,qBAAqB,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,GAAG,MAA4C,CAAC;IACvD,CAAC,CAAC,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,cAAc,GAAG,GAAG,EAAE,GAAG,cAAc,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC,CAAC,GAAG,GAAG,uDAAuD,CAAC;IAChE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC;IACf,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC;IACf,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,WAAW,CAAC,OAAiB;IACpC,MAAM,KAAK,GAAG,OAAO;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACT,CAAC,CAAC,IAAI;QACJ,CAAC,CAAC,gBAAgB,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,+CAA+C,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW;QACjH,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CACtC;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,4EAA4E,KAAK,aAAa,CAAC;AACxG,CAAC;AAED,MAAM,OAAO,GAAG,UAAU,CAAC;AAE3B,SAAS,KAAK;IACZ,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC5C,IAAI,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,kBAAkB;IAEhE,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC;IAClB,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAErC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;IACvB,MAAM,CAAC,SAAS,GAAG,aAAa,CAAC;IACjC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;IACrC,MAAM,CAAC,YAAY,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,CAAC,SAAS,GAAG,YAAY,CAAC;IAChC,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB;QACnC,CAAC,CAAC,yDAAyD,GAAG,CAAC,gBAAgB,mJAAmJ;QAClO,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,CAAC,SAAS,GAAG;;;;;;;;;;QAUb,QAAQ;;;GAGb,CAAC;IACF,aAAa,EAAE,CAAC;IAEhB,4EAA4E;IAC5E,MAAM,CAAC,aAAa,CAAqB,YAAY,CAAE,CAAC,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC;IAChF,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,CAAmB,YAAY,CAAE,CAAC;IACpE,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAiB,aAAa,CAAE,CAAC;IACpE,MAAM,IAAI,GAAG,MAAM,CAAC,aAAa,CAAkB,WAAW,CAAE,CAAC;IACjE,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAoB,aAAa,CAAE,CAAC;IAEvE,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,SAAS,EAAE,CAAC;QACnB,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,aAAa,CAAoB,YAAY,CAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAEvG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1C,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;QACjC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC;QAEzB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;gBACpC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,GAAG,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC;oBAC7C,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC9C,CAAC;aACH,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;YACpD,CAAC;YACD,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;YACxB,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC;YACnC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;YAClC,IAAI,GAAG,GAAG,EAAE,CAAC,CAAO,uDAAuD;YAC3E,IAAI,MAAM,GAAG,EAAE,CAAC,CAAI,yDAAyD;YAC7E,IAAI,QAAQ,GAAG,EAAE,CAAC,CAAE,8BAA8B;YAClD,SAAS,CAAC;gBACR,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI;oBAAE,MAAM;gBAChB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrD,MAAM,IAAI,IAAI,CAAC;gBACf,GAAG,IAAI,IAAI,CAAC;gBACZ,MAAM,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBACjC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;oBACZ,QAAQ,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;oBAC9C,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;oBACxB,MAAM,CAAC,SAAS,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;gBAC9C,CAAC;YACH,CAAC;YACD,IAAI,GAAG;gBAAE,QAAQ,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;YACnD,MAAM,CAAC,SAAS,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3F,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;QAChC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC;YAC/B,MAAM,CAAC,WAAW,GAAG,iCAAkC,GAAa,CAAC,OAAO,EAAE,CAAC;QACjF,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;YACxB,mEAAmE;YACnE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;YAC1B,cAAc,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,OAAO;IACd,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC5C,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;AAC7C,CAAC;AAED,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;IACpC,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACtC,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACN,KAAK,EAAE,CAAC;IACV,CAAC;IACD,QAAQ,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IACpD,QAAQ,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;AAC1D,CAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAS5D,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAS5D,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,aAAa,CA+B7D"}
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ export default function ask(input) {
|
|
|
35
35
|
styles() {
|
|
36
36
|
return [STYLES_PATH];
|
|
37
37
|
},
|
|
38
|
-
defineClientConfig() {
|
|
38
|
+
defineClientConfig(ctx) {
|
|
39
39
|
// JSON-safe runtime config exposed at
|
|
40
40
|
// virtual:dogsbay-plugin-config/@dogsbay/plugin-ask
|
|
41
41
|
return {
|
|
@@ -46,6 +46,9 @@ export default function ask(input) {
|
|
|
46
46
|
title: opts.title,
|
|
47
47
|
placeholder: opts.placeholder,
|
|
48
48
|
position: opts.position,
|
|
49
|
+
// Base URL used to turn cited doc paths into links.
|
|
50
|
+
linkBase: ctx.config.site.url ?? "",
|
|
51
|
+
turnstileSitekey: opts.turnstileSitekey,
|
|
49
52
|
};
|
|
50
53
|
},
|
|
51
54
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAmB,MAAM,aAAa,CAAC;AAE5D,6EAA6E;AAC7E,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACpD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AACvD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;AAIjE,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,KAAkB;IAC5C,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAEjC,OAAO;QACL,IAAI,EAAE,qBAAqB;QAE3B,aAAa;YACX,OAAO,CAAC,WAAW,CAAC,CAAC;QACvB,CAAC;QAED,MAAM;YACJ,OAAO,CAAC,WAAW,CAAC,CAAC;QACvB,CAAC;QAED,kBAAkB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAmB,MAAM,aAAa,CAAC;AAE5D,6EAA6E;AAC7E,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACpD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AACvD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;AAIjE,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,KAAkB;IAC5C,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAEjC,OAAO;QACL,IAAI,EAAE,qBAAqB;QAE3B,aAAa;YACX,OAAO,CAAC,WAAW,CAAC,CAAC;QACvB,CAAC;QAED,MAAM;YACJ,OAAO,CAAC,WAAW,CAAC,CAAC;QACvB,CAAC;QAED,kBAAkB,CAAC,GAAG;YACpB,sCAAsC;YACtC,oDAAoD;YACpD,OAAO;gBACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,oDAAoD;gBACpD,QAAQ,EAAG,GAAG,CAAC,MAAM,CAAC,IAAyB,CAAC,GAAG,IAAI,EAAE;gBACzD,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;aACxC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/md.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal, dependency-free, XSS-safe Markdown → HTML renderer for LLM answers.
|
|
3
|
+
*
|
|
4
|
+
* Safety model: the entire source is HTML-escaped FIRST, so any markup in the
|
|
5
|
+
* answer becomes inert text; only the tags this renderer emits are real. Link
|
|
6
|
+
* targets are restricted to http(s)/relative/anchor (no javascript: etc.).
|
|
7
|
+
*
|
|
8
|
+
* Scope: headings, bold, italic, inline code, fenced code, ordered/unordered
|
|
9
|
+
* lists, links, paragraphs — enough for documentation answers. Unsupported
|
|
10
|
+
* constructs degrade to readable text.
|
|
11
|
+
*/
|
|
12
|
+
export declare function escapeHtml(s: string): string;
|
|
13
|
+
export declare function renderMarkdown(src: string): string;
|
|
14
|
+
//# sourceMappingURL=md.d.ts.map
|
package/dist/md.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"md.d.ts","sourceRoot":"","sources":["../src/md.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAM5C;AAmBD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAuClD"}
|
package/dist/md.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal, dependency-free, XSS-safe Markdown → HTML renderer for LLM answers.
|
|
3
|
+
*
|
|
4
|
+
* Safety model: the entire source is HTML-escaped FIRST, so any markup in the
|
|
5
|
+
* answer becomes inert text; only the tags this renderer emits are real. Link
|
|
6
|
+
* targets are restricted to http(s)/relative/anchor (no javascript: etc.).
|
|
7
|
+
*
|
|
8
|
+
* Scope: headings, bold, italic, inline code, fenced code, ordered/unordered
|
|
9
|
+
* lists, links, paragraphs — enough for documentation answers. Unsupported
|
|
10
|
+
* constructs degrade to readable text.
|
|
11
|
+
*/
|
|
12
|
+
export function escapeHtml(s) {
|
|
13
|
+
return s
|
|
14
|
+
.replace(/&/g, "&")
|
|
15
|
+
.replace(/</g, "<")
|
|
16
|
+
.replace(/>/g, ">")
|
|
17
|
+
.replace(/"/g, """);
|
|
18
|
+
}
|
|
19
|
+
function isSafeUrl(u) {
|
|
20
|
+
// u is already HTML-escaped. Allow absolute http(s), root/relative, anchors.
|
|
21
|
+
return /^(https?:\/\/|\/|#|\.\/|[A-Za-z0-9._~-]+\/)/.test(u) && !/^\s*javascript:/i.test(u);
|
|
22
|
+
}
|
|
23
|
+
// Inline formatting on already-escaped text.
|
|
24
|
+
function inline(text) {
|
|
25
|
+
let t = text.replace(/`([^`]+)`/g, (_m, c) => `<code>${c}</code>`);
|
|
26
|
+
t = t.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_m, label, url) => isSafeUrl(url) ? `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>` : label);
|
|
27
|
+
t = t.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
28
|
+
t = t.replace(/(^|[^*])\*([^*\n]+)\*/g, "$1<em>$2</em>");
|
|
29
|
+
t = t.replace(/(^|[^_])_([^_\n]+)_/g, "$1<em>$2</em>");
|
|
30
|
+
return t;
|
|
31
|
+
}
|
|
32
|
+
export function renderMarkdown(src) {
|
|
33
|
+
const lines = escapeHtml(src).split(/\r?\n/);
|
|
34
|
+
let html = "";
|
|
35
|
+
let ul = false;
|
|
36
|
+
let ol = false;
|
|
37
|
+
const closeLists = () => {
|
|
38
|
+
if (ul) {
|
|
39
|
+
html += "</ul>";
|
|
40
|
+
ul = false;
|
|
41
|
+
}
|
|
42
|
+
if (ol) {
|
|
43
|
+
html += "</ol>";
|
|
44
|
+
ol = false;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
let i = 0;
|
|
48
|
+
while (i < lines.length) {
|
|
49
|
+
const line = lines[i];
|
|
50
|
+
if (/^\s*```/.test(line)) {
|
|
51
|
+
closeLists();
|
|
52
|
+
i++;
|
|
53
|
+
let code = "";
|
|
54
|
+
while (i < lines.length && !/^\s*```/.test(lines[i])) {
|
|
55
|
+
code += lines[i] + "\n";
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
i++; // closing fence
|
|
59
|
+
html += `<pre><code>${code}</code></pre>`;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const h = line.match(/^(#{1,6})\s+(.*)$/);
|
|
63
|
+
if (h) {
|
|
64
|
+
closeLists();
|
|
65
|
+
const lvl = h[1].length;
|
|
66
|
+
html += `<h${lvl}>${inline(h[2])}</h${lvl}>`;
|
|
67
|
+
i++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const uli = line.match(/^\s*[-*+]\s+(.*)$/);
|
|
71
|
+
if (uli) {
|
|
72
|
+
if (ol) {
|
|
73
|
+
html += "</ol>";
|
|
74
|
+
ol = false;
|
|
75
|
+
}
|
|
76
|
+
if (!ul) {
|
|
77
|
+
html += "<ul>";
|
|
78
|
+
ul = true;
|
|
79
|
+
}
|
|
80
|
+
html += `<li>${inline(uli[1])}</li>`;
|
|
81
|
+
i++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const oli = line.match(/^\s*\d+\.\s+(.*)$/);
|
|
85
|
+
if (oli) {
|
|
86
|
+
if (ul) {
|
|
87
|
+
html += "</ul>";
|
|
88
|
+
ul = false;
|
|
89
|
+
}
|
|
90
|
+
if (!ol) {
|
|
91
|
+
html += "<ol>";
|
|
92
|
+
ol = true;
|
|
93
|
+
}
|
|
94
|
+
html += `<li>${inline(oli[1])}</li>`;
|
|
95
|
+
i++;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (line.trim() === "") {
|
|
99
|
+
closeLists();
|
|
100
|
+
i++;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
closeLists();
|
|
104
|
+
let para = line;
|
|
105
|
+
i++;
|
|
106
|
+
while (i < lines.length && lines[i].trim() !== "" && !/^(\s*```|#{1,6}\s|\s*[-*+]\s|\s*\d+\.\s)/.test(lines[i])) {
|
|
107
|
+
para += " " + lines[i];
|
|
108
|
+
i++;
|
|
109
|
+
}
|
|
110
|
+
html += `<p>${inline(para)}</p>`;
|
|
111
|
+
}
|
|
112
|
+
closeLists();
|
|
113
|
+
return html;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=md.js.map
|
package/dist/md.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"md.js","sourceRoot":"","sources":["../src/md.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -22,4 +22,17 @@ export declare function buildAskRequest(site: string, query: string, mode: AskMo
|
|
|
22
22
|
* instance — see README "Streaming format".
|
|
23
23
|
*/
|
|
24
24
|
export declare function extractText(chunk: string): string;
|
|
25
|
+
export interface Source {
|
|
26
|
+
key: string;
|
|
27
|
+
label: string;
|
|
28
|
+
href: string | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Map an AI Search item key (a "__"-encoded doc path) back to a doc link.
|
|
32
|
+
* Non-lossy keys reverse to `<linkBase>/<path>`; lossy (hash-suffixed) keys
|
|
33
|
+
* can't be reconstructed, so they get a readable label with no href.
|
|
34
|
+
*/
|
|
35
|
+
export declare function sourceLink(key: string, linkBase: string): Source;
|
|
36
|
+
/** Find the AI Search `event: chunks` frame in a raw stream and map it to deduped sources. */
|
|
37
|
+
export declare function parseSources(rawStream: string, linkBase: string): Source[];
|
|
25
38
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,UAAU,CAEtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA8BjD"}
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,UAAU,CAEtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA8BjD;AAED,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAUD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUhE;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB1E"}
|
package/dist/protocol.js
CHANGED
|
@@ -48,4 +48,55 @@ export function extractText(chunk) {
|
|
|
48
48
|
})
|
|
49
49
|
.join("");
|
|
50
50
|
}
|
|
51
|
+
// Ingest-script lossy shortener produces `<head>__<leaf>__<8hex>.md` for paths
|
|
52
|
+
// too long to fit the filename limit — those can't be reversed to a real URL.
|
|
53
|
+
const LOSSY = /__[0-9a-f]{8}\.md$/;
|
|
54
|
+
function humanize(seg) {
|
|
55
|
+
return seg.replace(/[-_]+/g, " ").trim();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Map an AI Search item key (a "__"-encoded doc path) back to a doc link.
|
|
59
|
+
* Non-lossy keys reverse to `<linkBase>/<path>`; lossy (hash-suffixed) keys
|
|
60
|
+
* can't be reconstructed, so they get a readable label with no href.
|
|
61
|
+
*/
|
|
62
|
+
export function sourceLink(key, linkBase) {
|
|
63
|
+
const base = (linkBase || "").replace(/\/+$/, "");
|
|
64
|
+
if (LOSSY.test(key)) {
|
|
65
|
+
const parts = key.replace(/\.md$/, "").split("__");
|
|
66
|
+
const leaf = parts.length >= 2 ? parts[parts.length - 2] : parts[0];
|
|
67
|
+
return { key, label: humanize(leaf), href: null };
|
|
68
|
+
}
|
|
69
|
+
const path = key.replace(/\.md$/, "").split("__").join("/");
|
|
70
|
+
const leaf = path.split("/").pop() || path;
|
|
71
|
+
return { key, label: humanize(leaf), href: base ? `${base}/${path}` : `/${path}` };
|
|
72
|
+
}
|
|
73
|
+
/** Find the AI Search `event: chunks` frame in a raw stream and map it to deduped sources. */
|
|
74
|
+
export function parseSources(rawStream, linkBase) {
|
|
75
|
+
for (const raw of rawStream.split(/\r?\n/)) {
|
|
76
|
+
const line = raw.trimStart();
|
|
77
|
+
if (!line.startsWith("data:"))
|
|
78
|
+
continue;
|
|
79
|
+
const payload = line.slice("data:".length).trim();
|
|
80
|
+
if (!payload.startsWith("["))
|
|
81
|
+
continue;
|
|
82
|
+
try {
|
|
83
|
+
const arr = JSON.parse(payload);
|
|
84
|
+
const seen = new Set();
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const c of arr) {
|
|
87
|
+
const key = typeof c.item === "string" ? c.item : c.item?.key;
|
|
88
|
+
if (!key || seen.has(key))
|
|
89
|
+
continue;
|
|
90
|
+
seen.add(key);
|
|
91
|
+
out.push(sourceLink(key, linkBase));
|
|
92
|
+
}
|
|
93
|
+
if (out.length)
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// not the chunks frame; keep scanning
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
51
102
|
//# sourceMappingURL=protocol.js.map
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAYA,8DAA8D;AAC9D,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAa,EAAE,IAAa;IACxE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK;SACT,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACjC,IAAI,OAAO,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC9B,sEAAsE;QACtE,uEAAuE;QACvE,uDAAuD;QACvD,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QAC7E,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACrD,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QACtD,kEAAkE;QAClE,oDAAoD;QACpD,6DAA6D;QAC7D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAwB,CAAC;YACvD,MAAM,KAAK,GACT,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO;gBAChC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO;gBAClC,GAAG,CAAC,QAAQ;gBACZ,GAAG,CAAC,IAAI;gBACR,GAAG,CAAC,KAAK,CAAC;YACZ,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -31,6 +31,12 @@ export interface AskOptions {
|
|
|
31
31
|
placeholder?: string;
|
|
32
32
|
/** Corner the floating button sits in. Default "bottom-right". */
|
|
33
33
|
position?: AskPosition;
|
|
34
|
+
/**
|
|
35
|
+
* Cloudflare Turnstile sitekey. If set, the widget obtains a Turnstile
|
|
36
|
+
* token and sends it with each request; the proxy verifies it. Omit to
|
|
37
|
+
* disable the bot check.
|
|
38
|
+
*/
|
|
39
|
+
turnstileSitekey?: string;
|
|
34
40
|
}
|
|
35
41
|
export interface ResolvedAskOptions {
|
|
36
42
|
endpoint: string;
|
|
@@ -40,6 +46,7 @@ export interface ResolvedAskOptions {
|
|
|
40
46
|
title: string;
|
|
41
47
|
placeholder: string;
|
|
42
48
|
position: AskPosition;
|
|
49
|
+
turnstileSitekey: string;
|
|
43
50
|
}
|
|
44
51
|
/**
|
|
45
52
|
* Validate + resolve user options. Throws structured errors naming the
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAC1C,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,aAAa,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAC1C,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,aAAa,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAaD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAiC/D"}
|
package/dist/schema.js
CHANGED
|
@@ -13,6 +13,7 @@ const DEFAULTS = {
|
|
|
13
13
|
title: "Ask the docs",
|
|
14
14
|
placeholder: "Ask a question…",
|
|
15
15
|
position: "bottom-right",
|
|
16
|
+
turnstileSitekey: "",
|
|
16
17
|
};
|
|
17
18
|
const PKG = "@dogsbay/plugin-ask";
|
|
18
19
|
/**
|
|
@@ -45,6 +46,9 @@ export function parseOptions(input) {
|
|
|
45
46
|
}
|
|
46
47
|
out.position = opts.position;
|
|
47
48
|
}
|
|
49
|
+
if (opts.turnstileSitekey !== undefined) {
|
|
50
|
+
out.turnstileSitekey = requireNonEmptyString(opts.turnstileSitekey, "turnstileSitekey");
|
|
51
|
+
}
|
|
48
52
|
return out;
|
|
49
53
|
}
|
|
50
54
|
function requireNonEmptyString(value, field) {
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/plugin-ask",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.84",
|
|
4
4
|
"description": "Floating \"Ask AI\" widget for Dogsbay docs sites — calls a shared AI Search proxy (dogsbay-ask).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"README.md"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@dogsbay/types": "0.2.0-beta.
|
|
25
|
+
"@dogsbay/types": "0.2.0-beta.84"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"typescript": "^5.7.0",
|
package/src/client.ts
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
// @ts-expect-error: virtual module resolved by the Dogsbay Vite alias at build.
|
|
14
14
|
import config from "virtual:dogsbay-plugin-config/@dogsbay/plugin-ask";
|
|
15
|
-
import { buildAskRequest, extractText } from "./protocol.js";
|
|
15
|
+
import { buildAskRequest, extractText, parseSources, type Source } from "./protocol.js";
|
|
16
|
+
import { renderMarkdown, escapeHtml } from "./md.js";
|
|
16
17
|
import type { AskMode, AskPosition } from "./schema.js";
|
|
17
18
|
|
|
18
19
|
interface RuntimeConfig {
|
|
@@ -23,10 +24,47 @@ interface RuntimeConfig {
|
|
|
23
24
|
title: string;
|
|
24
25
|
placeholder: string;
|
|
25
26
|
position: AskPosition;
|
|
27
|
+
linkBase: string;
|
|
28
|
+
turnstileSitekey: string;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
const cfg = config as RuntimeConfig;
|
|
29
32
|
|
|
33
|
+
// Turnstile token (refreshed per ask). Populated by the widget callbacks.
|
|
34
|
+
let turnstileToken: string | null = null;
|
|
35
|
+
let turnstileScriptLoaded = false;
|
|
36
|
+
|
|
37
|
+
interface TurnstileApi {
|
|
38
|
+
reset?: (widget?: unknown) => void;
|
|
39
|
+
}
|
|
40
|
+
function turnstileApi(): TurnstileApi | undefined {
|
|
41
|
+
return (window as unknown as { turnstile?: TurnstileApi }).turnstile;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function loadTurnstile(): void {
|
|
45
|
+
if (turnstileScriptLoaded || !cfg.turnstileSitekey) return;
|
|
46
|
+
turnstileScriptLoaded = true;
|
|
47
|
+
const w = window as unknown as Record<string, unknown>;
|
|
48
|
+
w.__dbaTsCb = (t: string) => { turnstileToken = t; };
|
|
49
|
+
w.__dbaTsExpired = () => { turnstileToken = null; };
|
|
50
|
+
const s = document.createElement("script");
|
|
51
|
+
s.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
|
|
52
|
+
s.async = true;
|
|
53
|
+
s.defer = true;
|
|
54
|
+
document.head.appendChild(s);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function sourcesHtml(sources: Source[]): string {
|
|
58
|
+
const items = sources
|
|
59
|
+
.map((s) =>
|
|
60
|
+
s.href
|
|
61
|
+
? `<li><a href="${escapeHtml(s.href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(s.label)}</a></li>`
|
|
62
|
+
: `<li>${escapeHtml(s.label)}</li>`,
|
|
63
|
+
)
|
|
64
|
+
.join("");
|
|
65
|
+
return `<div class="dba-sources"><div class="dba-sources-title">Sources</div><ul>${items}</ul></div>`;
|
|
66
|
+
}
|
|
67
|
+
|
|
30
68
|
const ROOT_ID = "dba-root";
|
|
31
69
|
|
|
32
70
|
function mount(): void {
|
|
@@ -46,6 +84,9 @@ function mount(): void {
|
|
|
46
84
|
const dialog = document.createElement("dialog");
|
|
47
85
|
dialog.className = "dba-dialog";
|
|
48
86
|
dialog.setAttribute("aria-label", cfg.title);
|
|
87
|
+
const tsMarkup = cfg.turnstileSitekey
|
|
88
|
+
? `<div class="cf-turnstile dba-turnstile" data-sitekey="${cfg.turnstileSitekey}" data-appearance="interaction-only" data-callback="__dbaTsCb" data-expired-callback="__dbaTsExpired" data-error-callback="__dbaTsExpired"></div>`
|
|
89
|
+
: "";
|
|
49
90
|
dialog.innerHTML = `
|
|
50
91
|
<form class="dba-form" method="dialog">
|
|
51
92
|
<header class="dba-header">
|
|
@@ -56,9 +97,11 @@ function mount(): void {
|
|
|
56
97
|
<input class="dba-input" type="text" autocomplete="off" enterkeyhint="search" />
|
|
57
98
|
<button type="submit" class="dba-submit">Ask</button>
|
|
58
99
|
</div>
|
|
100
|
+
${tsMarkup}
|
|
59
101
|
<div class="dba-answer" aria-live="polite" tabindex="-1"></div>
|
|
60
102
|
</form>
|
|
61
103
|
`;
|
|
104
|
+
loadTurnstile();
|
|
62
105
|
|
|
63
106
|
// textContent (not innerHTML) for user-configurable strings — no injection.
|
|
64
107
|
dialog.querySelector<HTMLHeadingElement>(".dba-title")!.textContent = cfg.title;
|
|
@@ -87,7 +130,10 @@ function mount(): void {
|
|
|
87
130
|
const res = await fetch(cfg.endpoint, {
|
|
88
131
|
method: "POST",
|
|
89
132
|
headers: { "Content-Type": "application/json" },
|
|
90
|
-
body: JSON.stringify(
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
...buildAskRequest(cfg.site, query, cfg.mode),
|
|
135
|
+
...(turnstileToken ? { turnstileToken } : {}),
|
|
136
|
+
}),
|
|
91
137
|
});
|
|
92
138
|
if (!res.ok || !res.body) {
|
|
93
139
|
throw new Error(`Request failed (${res.status})`);
|
|
@@ -96,26 +142,34 @@ function mount(): void {
|
|
|
96
142
|
answer.dataset.state = "streaming";
|
|
97
143
|
const reader = res.body.getReader();
|
|
98
144
|
const decoder = new TextDecoder();
|
|
99
|
-
let buf = "";
|
|
145
|
+
let buf = ""; // line buffer (don't parse a frame split across reads)
|
|
146
|
+
let rawAll = ""; // whole stream, for the trailing `event: chunks` sources
|
|
147
|
+
let answerMd = ""; // accumulated answer markdown
|
|
100
148
|
for (;;) {
|
|
101
149
|
const { value, done } = await reader.read();
|
|
102
150
|
if (done) break;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
151
|
+
const text = decoder.decode(value, { stream: true });
|
|
152
|
+
rawAll += text;
|
|
153
|
+
buf += text;
|
|
106
154
|
const nl = buf.lastIndexOf("\n");
|
|
107
155
|
if (nl >= 0) {
|
|
108
|
-
|
|
156
|
+
answerMd += extractText(buf.slice(0, nl + 1));
|
|
109
157
|
buf = buf.slice(nl + 1);
|
|
158
|
+
answer.innerHTML = renderMarkdown(answerMd);
|
|
110
159
|
}
|
|
111
160
|
}
|
|
112
|
-
if (buf)
|
|
161
|
+
if (buf) answerMd += extractText(buf);
|
|
162
|
+
const sources = parseSources(rawAll, cfg.linkBase);
|
|
163
|
+
answer.innerHTML = renderMarkdown(answerMd) + (sources.length ? sourcesHtml(sources) : "");
|
|
113
164
|
answer.dataset.state = "done";
|
|
114
165
|
} catch (err) {
|
|
115
166
|
answer.dataset.state = "error";
|
|
116
167
|
answer.textContent = `Sorry — something went wrong. ${(err as Error).message}`;
|
|
117
168
|
} finally {
|
|
118
169
|
submit.disabled = false;
|
|
170
|
+
// Turnstile tokens are single-use — refresh for the next question.
|
|
171
|
+
turnstileApi()?.reset?.();
|
|
172
|
+
turnstileToken = null;
|
|
119
173
|
}
|
|
120
174
|
});
|
|
121
175
|
|
package/src/styles.css
CHANGED
|
@@ -69,10 +69,11 @@
|
|
|
69
69
|
|
|
70
70
|
.dba-answer {
|
|
71
71
|
min-height: 2rem; max-height: 50vh; overflow-y: auto;
|
|
72
|
-
padding: 0.75rem;
|
|
72
|
+
padding: 0.75rem; line-height: 1.55;
|
|
73
73
|
background: color-mix(in srgb, var(--dba-text) 4%, transparent);
|
|
74
74
|
border-radius: var(--dba-radius);
|
|
75
75
|
}
|
|
76
|
+
.dba-turnstile { margin: 0.25rem 0; min-height: 0; }
|
|
76
77
|
.dba-answer[data-state="loading"] { opacity: 0.6; }
|
|
77
78
|
.dba-answer[data-state="error"] { color: #b91c1c; }
|
|
78
79
|
|
|
@@ -80,3 +81,33 @@
|
|
|
80
81
|
.dba-dialog[open] { animation: dba-in 0.12s ease-out; }
|
|
81
82
|
}
|
|
82
83
|
@keyframes dba-in { from { transform: translateY(6px); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
84
|
+
|
|
85
|
+
/* Rendered Markdown inside the answer */
|
|
86
|
+
.dba-answer h1, .dba-answer h2, .dba-answer h3, .dba-answer h4 {
|
|
87
|
+
margin: 0.8em 0 0.3em; line-height: 1.25; font-weight: 600;
|
|
88
|
+
}
|
|
89
|
+
.dba-answer h1 { font-size: 1.2rem; }
|
|
90
|
+
.dba-answer h2 { font-size: 1.1rem; }
|
|
91
|
+
.dba-answer h3 { font-size: 1rem; }
|
|
92
|
+
.dba-answer h4 { font-size: 0.95rem; }
|
|
93
|
+
.dba-answer p { margin: 0.5em 0; }
|
|
94
|
+
.dba-answer ul, .dba-answer ol { margin: 0.5em 0; padding-left: 1.4em; }
|
|
95
|
+
.dba-answer li { margin: 0.15em 0; }
|
|
96
|
+
.dba-answer a { color: var(--dba-accent); text-decoration: underline; }
|
|
97
|
+
.dba-answer code {
|
|
98
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em;
|
|
99
|
+
padding: 0.1em 0.3em; border-radius: 4px;
|
|
100
|
+
background: color-mix(in srgb, var(--dba-text) 9%, transparent);
|
|
101
|
+
}
|
|
102
|
+
.dba-answer pre {
|
|
103
|
+
margin: 0.6em 0; padding: 0.7em 0.8em; overflow-x: auto; border-radius: var(--dba-radius);
|
|
104
|
+
background: color-mix(in srgb, var(--dba-text) 9%, transparent);
|
|
105
|
+
}
|
|
106
|
+
.dba-answer pre code { padding: 0; background: none; }
|
|
107
|
+
|
|
108
|
+
/* Sources list */
|
|
109
|
+
.dba-sources { margin-top: 1em; padding-top: 0.6em; border-top: 1px solid var(--dba-border); }
|
|
110
|
+
.dba-sources-title { font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.7; margin-bottom: 0.3em; }
|
|
111
|
+
.dba-sources ul { margin: 0; padding-left: 1.2em; }
|
|
112
|
+
.dba-sources li { margin: 0.1em 0; font-size: 0.9em; }
|
|
113
|
+
.dba-sources a { color: var(--dba-accent); }
|