@dogsbay/plugin-ask 0.2.0-beta.87 → 0.2.0-beta.88

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/README.md CHANGED
@@ -34,6 +34,8 @@ plugins:
34
34
  | `endpoint` | yes | — | URL of the backend the widget calls. |
35
35
  | `site` | yes | — | Site id sent with each request to select the corpus. |
36
36
  | `mode` | no | `answer` | `answer` (generated answer) or `search` (results). |
37
+ | `conversational` | no | `true` | Multi-turn chat with context; `false` = single-shot. |
38
+ | `maxTurns` | no | `8` | Max prior messages sent as context. |
37
39
  | `buttonLabel` | no | `Ask AI` | Floating button text. |
38
40
  | `title` | no | `Ask the docs` | Modal heading. |
39
41
  | `placeholder` | no | `Ask a question…` | Input placeholder. |
package/dist/client.js CHANGED
@@ -1,22 +1,21 @@
1
1
  /**
2
2
  * Client runtime for `@dogsbay/plugin-ask`.
3
3
  *
4
- * Runs for side effects on every page. Two render targets share one code path:
5
- *
4
+ * Renders a multi-turn "Ask AI" conversation in two targets that share one
5
+ * code path:
6
6
  * - **Docked panel** — a server-rendered shell (`RightRailAsk.astro`,
7
- * `[data-dba-panel="rail"]`) that DocsLayout places in its `right-rail`
8
- * slot. Visible on desktop; hidden < lg because the rail is `hidden lg:block`.
9
- * - **Floating modal** — a body-level button + `<dialog>` built here. Used when
10
- * there's no rail (floating placement, wide/API pages) and as the mobile
11
- * entry when a docked panel exists.
7
+ * `[data-dba-panel="rail"]`) placed in DocsLayout's `right-rail` slot.
8
+ * - **Floating modal** — a body-level button + `<dialog>` built here; the
9
+ * fallback on rail-less layouts and the mobile entry when a panel exists.
12
10
  *
13
- * Astro view transitions swap the document, so we mount on `astro:page-load`
14
- * and tear the floating widget down on `astro:before-swap` (the docked shell is
15
- * part of the swapped document, so it's re-wired each navigation).
11
+ * Each turn appends to a `.dba-thread`; with `conversational` on (default) the
12
+ * history is sent as context so follow-ups are understood. Astro view
13
+ * transitions swap the document, so we mount on `astro:page-load` and tear the
14
+ * floating widget down on `astro:before-swap` (the docked shell is re-wired).
16
15
  */
17
16
  // @ts-expect-error: virtual module resolved by the Dogsbay Vite alias at build.
18
17
  import config from "virtual:dogsbay-plugin-config/@dogsbay/plugin-ask";
19
- import { buildAskRequest, extractText, parseSources } from "./protocol.js";
18
+ import { buildAskRequest, buildChatRequest, trimHistory, extractText, parseSources, shouldRetryTurnstile, } from "./protocol.js";
20
19
  import { renderMarkdown, escapeHtml } from "./md.js";
21
20
  const cfg = config;
22
21
  // Turnstile token (refreshed per ask). Populated by the widget callbacks.
@@ -46,78 +45,160 @@ function sourcesHtml(sources) {
46
45
  .join("");
47
46
  return `<div class="dba-sources"><div class="dba-sources-title">Sources</div><ul>${items}</ul></div>`;
48
47
  }
49
- /**
50
- * The one ask flow: POST the question, stream the answer, render Markdown +
51
- * a Sources list. Shared by the docked panel and the floating dialog.
52
- */
53
- async function runAsk({ input, answer, submit }) {
54
- const query = input.value.trim();
55
- if (!query)
56
- return;
57
- submit.disabled = true;
48
+ /** POST a prebuilt request body with the current Turnstile token (if any). */
49
+ function postBody(body) {
50
+ return fetch(cfg.endpoint, {
51
+ method: "POST",
52
+ headers: { "Content-Type": "application/json" },
53
+ body: JSON.stringify({ ...body, ...(turnstileToken ? { turnstileToken } : {}) }),
54
+ });
55
+ }
56
+ /** Resolve once a Turnstile token is available, or after `timeoutMs`. */
57
+ function waitForTurnstileToken(timeoutMs) {
58
+ if (turnstileToken)
59
+ return Promise.resolve(turnstileToken);
60
+ return new Promise((resolve) => {
61
+ const start = Date.now();
62
+ const tick = () => {
63
+ if (turnstileToken)
64
+ return resolve(turnstileToken);
65
+ if (Date.now() - start >= timeoutMs)
66
+ return resolve(null);
67
+ setTimeout(tick, 150);
68
+ };
69
+ tick();
70
+ });
71
+ }
72
+ /** Stream a proxy response into an answer element; returns the answer Markdown. */
73
+ async function streamInto(res, answer) {
74
+ answer.textContent = "";
75
+ answer.dataset.state = "streaming";
76
+ const reader = res.body.getReader();
77
+ const decoder = new TextDecoder();
78
+ let buf = ""; // line buffer (don't parse a frame split across reads)
79
+ let rawAll = ""; // whole stream, for the trailing `event: chunks` sources
80
+ let answerMd = ""; // accumulated answer markdown
81
+ for (;;) {
82
+ const { value, done } = await reader.read();
83
+ if (done)
84
+ break;
85
+ const text = decoder.decode(value, { stream: true });
86
+ rawAll += text;
87
+ buf += text;
88
+ const nl = buf.lastIndexOf("\n");
89
+ if (nl >= 0) {
90
+ answerMd += extractText(buf.slice(0, nl + 1));
91
+ buf = buf.slice(nl + 1);
92
+ answer.innerHTML = renderMarkdown(answerMd);
93
+ }
94
+ }
95
+ if (buf)
96
+ answerMd += extractText(buf);
97
+ const sources = parseSources(rawAll, cfg.linkBase);
98
+ answer.innerHTML = renderMarkdown(answerMd) + (sources.length ? sourcesHtml(sources) : "");
99
+ answer.dataset.state = "done";
100
+ return answerMd;
101
+ }
102
+ function appendUserTurn(thread, text) {
103
+ const turn = document.createElement("div");
104
+ turn.className = "dba-turn dba-turn-user";
105
+ turn.textContent = text; // textContent — no injection from the question
106
+ thread.appendChild(turn);
107
+ }
108
+ function appendAssistantTurn(thread) {
109
+ const turn = document.createElement("div");
110
+ turn.className = "dba-turn dba-turn-assistant";
111
+ const answer = document.createElement("div");
112
+ answer.className = "dba-answer";
113
+ answer.setAttribute("aria-live", "polite");
58
114
  answer.dataset.state = "loading";
59
115
  answer.textContent = "…";
60
- try {
61
- const res = await fetch(cfg.endpoint, {
62
- method: "POST",
63
- headers: { "Content-Type": "application/json" },
64
- body: JSON.stringify({
65
- ...buildAskRequest(cfg.site, query, cfg.mode),
66
- ...(turnstileToken ? { turnstileToken } : {}),
67
- }),
68
- });
69
- if (!res.ok || !res.body) {
70
- throw new Error(`Request failed (${res.status})`);
116
+ turn.appendChild(answer);
117
+ thread.appendChild(turn);
118
+ return answer;
119
+ }
120
+ /**
121
+ * Wire one conversation surface (docked panel or floating dialog). Each keeps
122
+ * its own history closure. Shares the stream + Turnstile-retry logic.
123
+ */
124
+ function wireConversation(els) {
125
+ const history = [];
126
+ const reset = () => {
127
+ els.thread.innerHTML = "";
128
+ history.length = 0;
129
+ };
130
+ els.newChat?.addEventListener("click", () => {
131
+ reset();
132
+ els.input.focus();
133
+ });
134
+ els.form.addEventListener("submit", async (e) => {
135
+ e.preventDefault();
136
+ const query = els.input.value.trim();
137
+ if (!query)
138
+ return;
139
+ els.input.value = "";
140
+ // Single-shot modes show one exchange at a time.
141
+ const conversational = cfg.conversational && cfg.mode !== "search";
142
+ if (!conversational)
143
+ els.thread.innerHTML = "";
144
+ appendUserTurn(els.thread, query);
145
+ const answer = appendAssistantTurn(els.thread);
146
+ answer.scrollIntoView?.({ block: "nearest" });
147
+ els.submit.disabled = true;
148
+ // Build the request body. Conversational answer mode sends trimmed history;
149
+ // search and non-conversational answer stay single-shot.
150
+ let body;
151
+ if (cfg.mode === "search") {
152
+ body = buildAskRequest(cfg.site, query, "search");
153
+ }
154
+ else if (conversational) {
155
+ history.push({ role: "user", content: query });
156
+ body = buildChatRequest(cfg.site, trimHistory(history, cfg.maxTurns));
157
+ }
158
+ else {
159
+ body = buildAskRequest(cfg.site, query, "answer");
71
160
  }
72
- answer.textContent = "";
73
- answer.dataset.state = "streaming";
74
- const reader = res.body.getReader();
75
- const decoder = new TextDecoder();
76
- let buf = ""; // line buffer (don't parse a frame split across reads)
77
- let rawAll = ""; // whole stream, for the trailing `event: chunks` sources
78
- let answerMd = ""; // accumulated answer markdown
79
- for (;;) {
80
- const { value, done } = await reader.read();
81
- if (done)
82
- break;
83
- const text = decoder.decode(value, { stream: true });
84
- rawAll += text;
85
- buf += text;
86
- const nl = buf.lastIndexOf("\n");
87
- if (nl >= 0) {
88
- answerMd += extractText(buf.slice(0, nl + 1));
89
- buf = buf.slice(nl + 1);
90
- answer.innerHTML = renderMarkdown(answerMd);
161
+ try {
162
+ let res = await postBody(body);
163
+ // Turnstile race: lazy-loaded on first interaction, so a fast submit can
164
+ // beat the token and the proxy 403s. Wait once for a fresh token + retry.
165
+ if (shouldRetryTurnstile(res.status, !!cfg.turnstileSitekey)) {
166
+ loadTurnstile();
167
+ if (turnstileToken) {
168
+ turnstileApi()?.reset?.();
169
+ turnstileToken = null;
170
+ }
171
+ const token = await waitForTurnstileToken(4000);
172
+ if (token)
173
+ res = await postBody(body);
91
174
  }
175
+ if (!res.ok || !res.body) {
176
+ throw new Error(`Request failed (${res.status})`);
177
+ }
178
+ const answerMd = await streamInto(res, answer);
179
+ if (conversational)
180
+ history.push({ role: "assistant", content: answerMd });
181
+ }
182
+ catch (err) {
183
+ answer.dataset.state = "error";
184
+ answer.textContent = `Sorry — something went wrong. ${err.message}`;
185
+ // Drop the unanswered user turn from history so the next send isn't
186
+ // skewed by a question that never got a reply.
187
+ if (conversational && history[history.length - 1]?.role === "user")
188
+ history.pop();
189
+ }
190
+ finally {
191
+ els.submit.disabled = false;
192
+ // Turnstile tokens are single-use — refresh for the next question.
193
+ turnstileApi()?.reset?.();
194
+ turnstileToken = null;
92
195
  }
93
- if (buf)
94
- answerMd += extractText(buf);
95
- const sources = parseSources(rawAll, cfg.linkBase);
96
- answer.innerHTML = renderMarkdown(answerMd) + (sources.length ? sourcesHtml(sources) : "");
97
- answer.dataset.state = "done";
98
- }
99
- catch (err) {
100
- answer.dataset.state = "error";
101
- answer.textContent = `Sorry — something went wrong. ${err.message}`;
102
- }
103
- finally {
104
- submit.disabled = false;
105
- // Turnstile tokens are single-use — refresh for the next question.
106
- turnstileApi()?.reset?.();
107
- turnstileToken = null;
108
- }
109
- }
110
- function wireForm(els) {
111
- els.form.addEventListener("submit", (e) => {
112
- e.preventDefault();
113
- void runAsk(els);
114
196
  });
115
197
  }
116
198
  const ROOT_ID = "dba-root";
117
199
  /**
118
200
  * Wire the server-rendered docked panel if present. Returns whether a docked
119
- * shell exists on the page (so the caller knows to mark the floating button as
120
- * a mobile-only entry). Idempotent via a `data-dba-wired` marker.
201
+ * shell exists (so the caller knows to mark the floating button mobile-only).
121
202
  */
122
203
  function wireDockedPanel() {
123
204
  const panel = document.querySelector('[data-dba-panel="rail"]');
@@ -128,14 +209,20 @@ function wireDockedPanel() {
128
209
  panel.dataset.dbaWired = "1";
129
210
  const form = panel.querySelector("[data-dba-form]");
130
211
  const input = panel.querySelector(".dba-input");
131
- const answer = panel.querySelector(".dba-answer");
132
212
  const submit = panel.querySelector(".dba-submit");
133
- if (!form || !input || !answer || !submit)
213
+ const thread = panel.querySelector("[data-dba-thread]");
214
+ if (!form || !input || !submit || !thread)
134
215
  return true;
135
216
  // Lazy-load Turnstile on first interaction so a page that merely shows the
136
217
  // docked panel still sets no third-party cookies until the reader engages.
137
218
  input.addEventListener("focus", loadTurnstile, { once: true });
138
- wireForm({ form, input, answer, submit });
219
+ wireConversation({
220
+ form,
221
+ input,
222
+ submit,
223
+ thread,
224
+ newChat: panel.querySelector("[data-dba-newchat]"),
225
+ });
139
226
  return true;
140
227
  }
141
228
  /**
@@ -161,27 +248,33 @@ function mountFloating(hasDocked) {
161
248
  const tsMarkup = cfg.turnstileSitekey
162
249
  ? `<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>`
163
250
  : "";
251
+ const newChatMarkup = cfg.conversational
252
+ ? `<button type="button" class="dba-newchat" data-dba-newchat>New chat</button>`
253
+ : "";
164
254
  dialog.innerHTML = `
165
255
  <form class="dba-form" method="dialog" data-dba-form>
166
256
  <header class="dba-header">
167
257
  <h2 class="dba-title"></h2>
168
- <button type="button" class="dba-close" aria-label="Close">×</button>
258
+ <div class="dba-header-actions">
259
+ ${newChatMarkup}
260
+ <button type="button" class="dba-close" aria-label="Close">×</button>
261
+ </div>
169
262
  </header>
263
+ <div class="dba-thread" data-dba-thread></div>
170
264
  <div class="dba-inputrow">
171
265
  <input class="dba-input" type="text" autocomplete="off" enterkeyhint="search" />
172
266
  <button type="submit" class="dba-submit">Ask</button>
173
267
  </div>
174
268
  ${tsMarkup}
175
- <div class="dba-answer" aria-live="polite" tabindex="-1"></div>
176
269
  </form>
177
270
  `;
178
271
  // textContent (not innerHTML) for user-configurable strings — no injection.
179
272
  dialog.querySelector(".dba-title").textContent = cfg.title;
180
273
  const input = dialog.querySelector(".dba-input");
181
274
  input.placeholder = cfg.placeholder;
182
- const answer = dialog.querySelector(".dba-answer");
183
275
  const form = dialog.querySelector(".dba-form");
184
276
  const submit = dialog.querySelector(".dba-submit");
277
+ const thread = dialog.querySelector("[data-dba-thread]");
185
278
  button.addEventListener("click", () => {
186
279
  // Lazy-load Turnstile only when the user engages, so normal page loads set
187
280
  // no third-party cookies (Lighthouse-clean) and don't fetch the widget.
@@ -190,23 +283,25 @@ function mountFloating(hasDocked) {
190
283
  input.focus();
191
284
  });
192
285
  dialog.querySelector(".dba-close").addEventListener("click", () => dialog.close());
193
- wireForm({ form, input, answer, submit });
286
+ wireConversation({
287
+ form,
288
+ input,
289
+ submit,
290
+ thread,
291
+ newChat: dialog.querySelector("[data-dba-newchat]"),
292
+ });
194
293
  root.append(button, dialog);
195
294
  document.body.appendChild(root);
196
295
  }
197
296
  function mount() {
198
297
  if (typeof document === "undefined")
199
298
  return;
200
- // Wire the docked panel first; its presence decides whether the floating
201
- // button is a full widget (no rail) or just the mobile entry (rail present).
202
299
  const hasDocked = wireDockedPanel();
203
300
  mountFloating(hasDocked);
204
301
  }
205
302
  function unmount() {
206
303
  if (typeof document === "undefined")
207
304
  return;
208
- // Only the floating widget is ours to remove; the docked shell is part of the
209
- // document Astro swaps, and is re-wired by mount() on the next page-load.
210
305
  document.getElementById(ROOT_ID)?.remove();
211
306
  }
212
307
  if (typeof document !== "undefined") {
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -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;AAa5D,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,aAAa,CAwC7D"}
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;AAa5D,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,aAAa,CA0C7D"}
package/dist/index.js CHANGED
@@ -59,6 +59,8 @@ export default function ask(input) {
59
59
  placeholder: opts.placeholder,
60
60
  position: opts.position,
61
61
  placement: opts.placement,
62
+ conversational: opts.conversational,
63
+ maxTurns: opts.maxTurns,
62
64
  // Base URL used to turn cited doc paths into links.
63
65
  linkBase: ctx.config.site.url ?? "",
64
66
  turnstileSitekey: opts.turnstileSitekey,
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;AACjE,4EAA4E;AAC5E,uEAAuE;AACvE,2DAA2D;AAC3D,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAC;AAI/E,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,iBAAiB;YACf,uEAAuE;YACvE,wEAAwE;YACxE,uEAAuE;YACvE,IAAI,IAAI,CAAC,SAAS,KAAK,UAAU;gBAAE,OAAO,EAAE,CAAC;YAC7C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;QAC1C,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,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,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"}
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;AACjE,4EAA4E;AAC5E,uEAAuE;AACvE,2DAA2D;AAC3D,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAC;AAI/E,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,iBAAiB;YACf,uEAAuE;YACvE,wEAAwE;YACxE,uEAAuE;YACvE,IAAI,IAAI,CAAC,SAAS,KAAK,UAAU;gBAAE,OAAO,EAAE,CAAC;YAC7C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;QAC1C,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,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,cAAc,EAAE,IAAI,CAAC,cAAc;gBACnC,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"}
@@ -8,8 +8,33 @@ export interface AskRequest {
8
8
  query: string;
9
9
  mode: AskMode;
10
10
  }
11
+ export interface ChatMessage {
12
+ role: "user" | "assistant";
13
+ content: string;
14
+ }
15
+ export interface ChatRequest {
16
+ site: string;
17
+ mode: "chat";
18
+ messages: ChatMessage[];
19
+ }
11
20
  /** Build the JSON body the proxy expects. Trims the query. */
12
21
  export declare function buildAskRequest(site: string, query: string, mode: AskMode): AskRequest;
22
+ /**
23
+ * Whether a failed request should be retried once after obtaining a Turnstile
24
+ * token. The proxy 403s when a Turnstile secret is configured but no valid
25
+ * token arrived — which happens if the widget submits before the bot token has
26
+ * resolved (the docked panel lazy-loads Turnstile on focus). Retry only that
27
+ * case: a 403 while Turnstile is enabled on this site.
28
+ */
29
+ export declare function shouldRetryTurnstile(status: number, turnstileEnabled: boolean): boolean;
30
+ /** Build the JSON body for a multi-turn chat request. */
31
+ export declare function buildChatRequest(site: string, messages: ChatMessage[]): ChatRequest;
32
+ /**
33
+ * Trim conversation history to the last `maxTurns` messages, never leading with
34
+ * an `assistant` turn — the proxy prepends the `system` message, so the first
35
+ * sent message must be a `user`. Returns a new array.
36
+ */
37
+ export declare function trimHistory(messages: ChatMessage[], maxTurns: number): ChatMessage[];
13
38
  /**
14
39
  * Tolerant extraction of human-readable text from a streamed proxy chunk.
15
40
  *
@@ -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;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"}
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,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,UAAU,CAEtF;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,GAAG,OAAO,CAEvF;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,CAEnF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAMpF;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
@@ -2,6 +2,32 @@
2
2
  export function buildAskRequest(site, query, mode) {
3
3
  return { site, query: query.trim(), mode };
4
4
  }
5
+ /**
6
+ * Whether a failed request should be retried once after obtaining a Turnstile
7
+ * token. The proxy 403s when a Turnstile secret is configured but no valid
8
+ * token arrived — which happens if the widget submits before the bot token has
9
+ * resolved (the docked panel lazy-loads Turnstile on focus). Retry only that
10
+ * case: a 403 while Turnstile is enabled on this site.
11
+ */
12
+ export function shouldRetryTurnstile(status, turnstileEnabled) {
13
+ return status === 403 && turnstileEnabled;
14
+ }
15
+ /** Build the JSON body for a multi-turn chat request. */
16
+ export function buildChatRequest(site, messages) {
17
+ return { site, mode: "chat", messages };
18
+ }
19
+ /**
20
+ * Trim conversation history to the last `maxTurns` messages, never leading with
21
+ * an `assistant` turn — the proxy prepends the `system` message, so the first
22
+ * sent message must be a `user`. Returns a new array.
23
+ */
24
+ export function trimHistory(messages, maxTurns) {
25
+ let trimmed = messages.slice(-Math.max(1, maxTurns));
26
+ while (trimmed.length > 0 && trimmed[0].role === "assistant") {
27
+ trimmed = trimmed.slice(1);
28
+ }
29
+ return trimmed;
30
+ }
5
31
  /**
6
32
  * Tolerant extraction of human-readable text from a streamed proxy chunk.
7
33
  *
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
package/dist/schema.d.ts CHANGED
@@ -32,6 +32,17 @@ export interface AskOptions {
32
32
  site?: string;
33
33
  /** Retrieval mode: "answer" (RAG answer, default) or "search" (chunks). */
34
34
  mode?: AskMode;
35
+ /**
36
+ * Multi-turn conversation: keep prior turns on screen and send them as
37
+ * context so follow-ups are understood. Default `true`. Set `false` for the
38
+ * single-shot behaviour (one question → one answer, no history).
39
+ */
40
+ conversational?: boolean;
41
+ /**
42
+ * Max prior messages sent as context when `conversational` (bounds prompt
43
+ * size / cost / latency). Positive integer; default `8`.
44
+ */
45
+ maxTurns?: number;
35
46
  /** Floating button label. Default "Ask AI". */
36
47
  buttonLabel?: string;
37
48
  /** Modal heading. Default "Ask the docs". */
@@ -61,6 +72,8 @@ export interface ResolvedAskOptions {
61
72
  placeholder: string;
62
73
  position: AskPosition;
63
74
  placement: AskPlacement;
75
+ conversational: boolean;
76
+ maxTurns: number;
64
77
  turnstileSitekey: string;
65
78
  }
66
79
  /**
@@ -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;AACzD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAExD,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;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB;;;;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,SAAS,EAAE,YAAY,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAcD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAuC/D"}
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;AACzD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAExD,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;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+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;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB;;;;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,SAAS,EAAE,YAAY,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAgBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAmD/D"}
package/dist/schema.js CHANGED
@@ -14,6 +14,8 @@ const DEFAULTS = {
14
14
  placeholder: "Ask a question…",
15
15
  position: "bottom-right",
16
16
  placement: "auto",
17
+ conversational: true,
18
+ maxTurns: 8,
17
19
  turnstileSitekey: "",
18
20
  };
19
21
  const PKG = "@dogsbay/plugin-ask";
@@ -53,6 +55,18 @@ export function parseOptions(input) {
53
55
  }
54
56
  out.placement = opts.placement;
55
57
  }
58
+ if (opts.conversational !== undefined) {
59
+ if (typeof opts.conversational !== "boolean") {
60
+ throw new Error(`${PKG}: options.conversational must be a boolean.`);
61
+ }
62
+ out.conversational = opts.conversational;
63
+ }
64
+ if (opts.maxTurns !== undefined) {
65
+ if (typeof opts.maxTurns !== "number" || !Number.isInteger(opts.maxTurns) || opts.maxTurns < 1) {
66
+ throw new Error(`${PKG}: options.maxTurns must be a positive integer.`);
67
+ }
68
+ out.maxTurns = opts.maxTurns;
69
+ }
56
70
  if (opts.turnstileSitekey !== undefined) {
57
71
  out.turnstileSitekey = requireNonEmptyString(opts.turnstileSitekey, "turnstileSitekey");
58
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
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.87",
3
+ "version": "0.2.0-beta.88",
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",
@@ -23,7 +23,7 @@
23
23
  "README.md"
24
24
  ],
25
25
  "dependencies": {
26
- "@dogsbay/types": "0.2.0-beta.87"
26
+ "@dogsbay/types": "0.2.0-beta.88"
27
27
  },
28
28
  "devDependencies": {
29
29
  "typescript": "^5.7.0",
@@ -19,6 +19,7 @@ import config from "virtual:dogsbay-plugin-config/@dogsbay/plugin-ask";
19
19
  interface RailConfig {
20
20
  title: string;
21
21
  placeholder: string;
22
+ conversational?: boolean;
22
23
  turnstileSitekey?: string;
23
24
  }
24
25
  const cfg = config as RailConfig;
@@ -28,7 +29,11 @@ const cfg = config as RailConfig;
28
29
  <form class="dba-form" data-dba-form>
29
30
  <header class="dba-header">
30
31
  <h2 class="dba-title">{cfg.title}</h2>
32
+ {cfg.conversational !== false && (
33
+ <button type="button" class="dba-newchat" data-dba-newchat>New chat</button>
34
+ )}
31
35
  </header>
36
+ <div class="dba-thread" data-dba-thread></div>
32
37
  <div class="dba-inputrow">
33
38
  <input
34
39
  class="dba-input"
@@ -49,7 +54,6 @@ const cfg = config as RailConfig;
49
54
  data-error-callback="__dbaTsExpired"
50
55
  />
51
56
  )}
52
- <div class="dba-answer" aria-live="polite" tabindex="-1"></div>
53
57
  </form>
54
58
  </section>
55
59
  <slot />
package/src/client.ts CHANGED
@@ -1,22 +1,30 @@
1
1
  /**
2
2
  * Client runtime for `@dogsbay/plugin-ask`.
3
3
  *
4
- * Runs for side effects on every page. Two render targets share one code path:
5
- *
4
+ * Renders a multi-turn "Ask AI" conversation in two targets that share one
5
+ * code path:
6
6
  * - **Docked panel** — a server-rendered shell (`RightRailAsk.astro`,
7
- * `[data-dba-panel="rail"]`) that DocsLayout places in its `right-rail`
8
- * slot. Visible on desktop; hidden < lg because the rail is `hidden lg:block`.
9
- * - **Floating modal** — a body-level button + `<dialog>` built here. Used when
10
- * there's no rail (floating placement, wide/API pages) and as the mobile
11
- * entry when a docked panel exists.
7
+ * `[data-dba-panel="rail"]`) placed in DocsLayout's `right-rail` slot.
8
+ * - **Floating modal** — a body-level button + `<dialog>` built here; the
9
+ * fallback on rail-less layouts and the mobile entry when a panel exists.
12
10
  *
13
- * Astro view transitions swap the document, so we mount on `astro:page-load`
14
- * and tear the floating widget down on `astro:before-swap` (the docked shell is
15
- * part of the swapped document, so it's re-wired each navigation).
11
+ * Each turn appends to a `.dba-thread`; with `conversational` on (default) the
12
+ * history is sent as context so follow-ups are understood. Astro view
13
+ * transitions swap the document, so we mount on `astro:page-load` and tear the
14
+ * floating widget down on `astro:before-swap` (the docked shell is re-wired).
16
15
  */
17
16
  // @ts-expect-error: virtual module resolved by the Dogsbay Vite alias at build.
18
17
  import config from "virtual:dogsbay-plugin-config/@dogsbay/plugin-ask";
19
- import { buildAskRequest, extractText, parseSources, type Source } from "./protocol.js";
18
+ import {
19
+ buildAskRequest,
20
+ buildChatRequest,
21
+ trimHistory,
22
+ extractText,
23
+ parseSources,
24
+ shouldRetryTurnstile,
25
+ type ChatMessage,
26
+ type Source,
27
+ } from "./protocol.js";
20
28
  import { renderMarkdown, escapeHtml } from "./md.js";
21
29
  import type { AskMode, AskPlacement, AskPosition } from "./schema.js";
22
30
 
@@ -29,6 +37,8 @@ interface RuntimeConfig {
29
37
  placeholder: string;
30
38
  position: AskPosition;
31
39
  placement: AskPlacement;
40
+ conversational: boolean;
41
+ maxTurns: number;
32
42
  linkBase: string;
33
43
  turnstileSitekey: string;
34
44
  }
@@ -70,76 +80,159 @@ function sourcesHtml(sources: Source[]): string {
70
80
  return `<div class="dba-sources"><div class="dba-sources-title">Sources</div><ul>${items}</ul></div>`;
71
81
  }
72
82
 
73
- interface AskEls {
83
+ /** POST a prebuilt request body with the current Turnstile token (if any). */
84
+ function postBody(body: object): Promise<Response> {
85
+ return fetch(cfg.endpoint, {
86
+ method: "POST",
87
+ headers: { "Content-Type": "application/json" },
88
+ body: JSON.stringify({ ...body, ...(turnstileToken ? { turnstileToken } : {}) }),
89
+ });
90
+ }
91
+
92
+ /** Resolve once a Turnstile token is available, or after `timeoutMs`. */
93
+ function waitForTurnstileToken(timeoutMs: number): Promise<string | null> {
94
+ if (turnstileToken) return Promise.resolve(turnstileToken);
95
+ return new Promise((resolve) => {
96
+ const start = Date.now();
97
+ const tick = () => {
98
+ if (turnstileToken) return resolve(turnstileToken);
99
+ if (Date.now() - start >= timeoutMs) return resolve(null);
100
+ setTimeout(tick, 150);
101
+ };
102
+ tick();
103
+ });
104
+ }
105
+
106
+ /** Stream a proxy response into an answer element; returns the answer Markdown. */
107
+ async function streamInto(res: Response, answer: HTMLDivElement): Promise<string> {
108
+ answer.textContent = "";
109
+ answer.dataset.state = "streaming";
110
+ const reader = res.body!.getReader();
111
+ const decoder = new TextDecoder();
112
+ let buf = ""; // line buffer (don't parse a frame split across reads)
113
+ let rawAll = ""; // whole stream, for the trailing `event: chunks` sources
114
+ let answerMd = ""; // accumulated answer markdown
115
+ for (;;) {
116
+ const { value, done } = await reader.read();
117
+ if (done) break;
118
+ const text = decoder.decode(value, { stream: true });
119
+ rawAll += text;
120
+ buf += text;
121
+ const nl = buf.lastIndexOf("\n");
122
+ if (nl >= 0) {
123
+ answerMd += extractText(buf.slice(0, nl + 1));
124
+ buf = buf.slice(nl + 1);
125
+ answer.innerHTML = renderMarkdown(answerMd);
126
+ }
127
+ }
128
+ if (buf) answerMd += extractText(buf);
129
+ const sources = parseSources(rawAll, cfg.linkBase);
130
+ answer.innerHTML = renderMarkdown(answerMd) + (sources.length ? sourcesHtml(sources) : "");
131
+ answer.dataset.state = "done";
132
+ return answerMd;
133
+ }
134
+
135
+ function appendUserTurn(thread: HTMLElement, text: string): void {
136
+ const turn = document.createElement("div");
137
+ turn.className = "dba-turn dba-turn-user";
138
+ turn.textContent = text; // textContent — no injection from the question
139
+ thread.appendChild(turn);
140
+ }
141
+
142
+ function appendAssistantTurn(thread: HTMLElement): HTMLDivElement {
143
+ const turn = document.createElement("div");
144
+ turn.className = "dba-turn dba-turn-assistant";
145
+ const answer = document.createElement("div");
146
+ answer.className = "dba-answer";
147
+ answer.setAttribute("aria-live", "polite");
148
+ answer.dataset.state = "loading";
149
+ answer.textContent = "…";
150
+ turn.appendChild(answer);
151
+ thread.appendChild(turn);
152
+ return answer;
153
+ }
154
+
155
+ interface ConvoEls {
74
156
  form: HTMLFormElement;
75
157
  input: HTMLInputElement;
76
- answer: HTMLDivElement;
77
158
  submit: HTMLButtonElement;
159
+ thread: HTMLElement;
160
+ newChat?: HTMLButtonElement | null;
78
161
  }
79
162
 
80
163
  /**
81
- * The one ask flow: POST the question, stream the answer, render Markdown +
82
- * a Sources list. Shared by the docked panel and the floating dialog.
164
+ * Wire one conversation surface (docked panel or floating dialog). Each keeps
165
+ * its own history closure. Shares the stream + Turnstile-retry logic.
83
166
  */
84
- async function runAsk({ input, answer, submit }: AskEls): Promise<void> {
85
- const query = input.value.trim();
86
- if (!query) return;
167
+ function wireConversation(els: ConvoEls): void {
168
+ const history: ChatMessage[] = [];
87
169
 
88
- submit.disabled = true;
89
- answer.dataset.state = "loading";
90
- answer.textContent = "…";
170
+ const reset = () => {
171
+ els.thread.innerHTML = "";
172
+ history.length = 0;
173
+ };
174
+ els.newChat?.addEventListener("click", () => {
175
+ reset();
176
+ els.input.focus();
177
+ });
178
+
179
+ els.form.addEventListener("submit", async (e) => {
180
+ e.preventDefault();
181
+ const query = els.input.value.trim();
182
+ if (!query) return;
183
+ els.input.value = "";
184
+
185
+ // Single-shot modes show one exchange at a time.
186
+ const conversational = cfg.conversational && cfg.mode !== "search";
187
+ if (!conversational) els.thread.innerHTML = "";
91
188
 
92
- try {
93
- const res = await fetch(cfg.endpoint, {
94
- method: "POST",
95
- headers: { "Content-Type": "application/json" },
96
- body: JSON.stringify({
97
- ...buildAskRequest(cfg.site, query, cfg.mode),
98
- ...(turnstileToken ? { turnstileToken } : {}),
99
- }),
100
- });
101
- if (!res.ok || !res.body) {
102
- throw new Error(`Request failed (${res.status})`);
189
+ appendUserTurn(els.thread, query);
190
+ const answer = appendAssistantTurn(els.thread);
191
+ answer.scrollIntoView?.({ block: "nearest" });
192
+ els.submit.disabled = true;
193
+
194
+ // Build the request body. Conversational answer mode sends trimmed history;
195
+ // search and non-conversational answer stay single-shot.
196
+ let body: object;
197
+ if (cfg.mode === "search") {
198
+ body = buildAskRequest(cfg.site, query, "search");
199
+ } else if (conversational) {
200
+ history.push({ role: "user", content: query });
201
+ body = buildChatRequest(cfg.site, trimHistory(history, cfg.maxTurns));
202
+ } else {
203
+ body = buildAskRequest(cfg.site, query, "answer");
103
204
  }
104
- answer.textContent = "";
105
- answer.dataset.state = "streaming";
106
- const reader = res.body.getReader();
107
- const decoder = new TextDecoder();
108
- let buf = ""; // line buffer (don't parse a frame split across reads)
109
- let rawAll = ""; // whole stream, for the trailing `event: chunks` sources
110
- let answerMd = ""; // accumulated answer markdown
111
- for (;;) {
112
- const { value, done } = await reader.read();
113
- if (done) break;
114
- const text = decoder.decode(value, { stream: true });
115
- rawAll += text;
116
- buf += text;
117
- const nl = buf.lastIndexOf("\n");
118
- if (nl >= 0) {
119
- answerMd += extractText(buf.slice(0, nl + 1));
120
- buf = buf.slice(nl + 1);
121
- answer.innerHTML = renderMarkdown(answerMd);
205
+
206
+ try {
207
+ let res = await postBody(body);
208
+ // Turnstile race: lazy-loaded on first interaction, so a fast submit can
209
+ // beat the token and the proxy 403s. Wait once for a fresh token + retry.
210
+ if (shouldRetryTurnstile(res.status, !!cfg.turnstileSitekey)) {
211
+ loadTurnstile();
212
+ if (turnstileToken) {
213
+ turnstileApi()?.reset?.();
214
+ turnstileToken = null;
215
+ }
216
+ const token = await waitForTurnstileToken(4000);
217
+ if (token) res = await postBody(body);
122
218
  }
219
+ if (!res.ok || !res.body) {
220
+ throw new Error(`Request failed (${res.status})`);
221
+ }
222
+ const answerMd = await streamInto(res, answer);
223
+ if (conversational) history.push({ role: "assistant", content: answerMd });
224
+ } catch (err) {
225
+ answer.dataset.state = "error";
226
+ answer.textContent = `Sorry — something went wrong. ${(err as Error).message}`;
227
+ // Drop the unanswered user turn from history so the next send isn't
228
+ // skewed by a question that never got a reply.
229
+ if (conversational && history[history.length - 1]?.role === "user") history.pop();
230
+ } finally {
231
+ els.submit.disabled = false;
232
+ // Turnstile tokens are single-use — refresh for the next question.
233
+ turnstileApi()?.reset?.();
234
+ turnstileToken = null;
123
235
  }
124
- if (buf) answerMd += extractText(buf);
125
- const sources = parseSources(rawAll, cfg.linkBase);
126
- answer.innerHTML = renderMarkdown(answerMd) + (sources.length ? sourcesHtml(sources) : "");
127
- answer.dataset.state = "done";
128
- } catch (err) {
129
- answer.dataset.state = "error";
130
- answer.textContent = `Sorry — something went wrong. ${(err as Error).message}`;
131
- } finally {
132
- submit.disabled = false;
133
- // Turnstile tokens are single-use — refresh for the next question.
134
- turnstileApi()?.reset?.();
135
- turnstileToken = null;
136
- }
137
- }
138
-
139
- function wireForm(els: AskEls): void {
140
- els.form.addEventListener("submit", (e) => {
141
- e.preventDefault();
142
- void runAsk(els);
143
236
  });
144
237
  }
145
238
 
@@ -147,8 +240,7 @@ const ROOT_ID = "dba-root";
147
240
 
148
241
  /**
149
242
  * Wire the server-rendered docked panel if present. Returns whether a docked
150
- * shell exists on the page (so the caller knows to mark the floating button as
151
- * a mobile-only entry). Idempotent via a `data-dba-wired` marker.
243
+ * shell exists (so the caller knows to mark the floating button mobile-only).
152
244
  */
153
245
  function wireDockedPanel(): boolean {
154
246
  const panel = document.querySelector<HTMLElement>('[data-dba-panel="rail"]');
@@ -158,14 +250,20 @@ function wireDockedPanel(): boolean {
158
250
 
159
251
  const form = panel.querySelector<HTMLFormElement>("[data-dba-form]");
160
252
  const input = panel.querySelector<HTMLInputElement>(".dba-input");
161
- const answer = panel.querySelector<HTMLDivElement>(".dba-answer");
162
253
  const submit = panel.querySelector<HTMLButtonElement>(".dba-submit");
163
- if (!form || !input || !answer || !submit) return true;
254
+ const thread = panel.querySelector<HTMLElement>("[data-dba-thread]");
255
+ if (!form || !input || !submit || !thread) return true;
164
256
 
165
257
  // Lazy-load Turnstile on first interaction so a page that merely shows the
166
258
  // docked panel still sets no third-party cookies until the reader engages.
167
259
  input.addEventListener("focus", loadTurnstile, { once: true });
168
- wireForm({ form, input, answer, submit });
260
+ wireConversation({
261
+ form,
262
+ input,
263
+ submit,
264
+ thread,
265
+ newChat: panel.querySelector<HTMLButtonElement>("[data-dba-newchat]"),
266
+ });
169
267
  return true;
170
268
  }
171
269
 
@@ -193,18 +291,24 @@ function mountFloating(hasDocked: boolean): void {
193
291
  const tsMarkup = cfg.turnstileSitekey
194
292
  ? `<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>`
195
293
  : "";
294
+ const newChatMarkup = cfg.conversational
295
+ ? `<button type="button" class="dba-newchat" data-dba-newchat>New chat</button>`
296
+ : "";
196
297
  dialog.innerHTML = `
197
298
  <form class="dba-form" method="dialog" data-dba-form>
198
299
  <header class="dba-header">
199
300
  <h2 class="dba-title"></h2>
200
- <button type="button" class="dba-close" aria-label="Close">×</button>
301
+ <div class="dba-header-actions">
302
+ ${newChatMarkup}
303
+ <button type="button" class="dba-close" aria-label="Close">×</button>
304
+ </div>
201
305
  </header>
306
+ <div class="dba-thread" data-dba-thread></div>
202
307
  <div class="dba-inputrow">
203
308
  <input class="dba-input" type="text" autocomplete="off" enterkeyhint="search" />
204
309
  <button type="submit" class="dba-submit">Ask</button>
205
310
  </div>
206
311
  ${tsMarkup}
207
- <div class="dba-answer" aria-live="polite" tabindex="-1"></div>
208
312
  </form>
209
313
  `;
210
314
 
@@ -212,9 +316,9 @@ function mountFloating(hasDocked: boolean): void {
212
316
  dialog.querySelector<HTMLHeadingElement>(".dba-title")!.textContent = cfg.title;
213
317
  const input = dialog.querySelector<HTMLInputElement>(".dba-input")!;
214
318
  input.placeholder = cfg.placeholder;
215
- const answer = dialog.querySelector<HTMLDivElement>(".dba-answer")!;
216
319
  const form = dialog.querySelector<HTMLFormElement>(".dba-form")!;
217
320
  const submit = dialog.querySelector<HTMLButtonElement>(".dba-submit")!;
321
+ const thread = dialog.querySelector<HTMLElement>("[data-dba-thread]")!;
218
322
 
219
323
  button.addEventListener("click", () => {
220
324
  // Lazy-load Turnstile only when the user engages, so normal page loads set
@@ -225,7 +329,13 @@ function mountFloating(hasDocked: boolean): void {
225
329
  });
226
330
  dialog.querySelector<HTMLButtonElement>(".dba-close")!.addEventListener("click", () => dialog.close());
227
331
 
228
- wireForm({ form, input, answer, submit });
332
+ wireConversation({
333
+ form,
334
+ input,
335
+ submit,
336
+ thread,
337
+ newChat: dialog.querySelector<HTMLButtonElement>("[data-dba-newchat]"),
338
+ });
229
339
 
230
340
  root.append(button, dialog);
231
341
  document.body.appendChild(root);
@@ -233,16 +343,12 @@ function mountFloating(hasDocked: boolean): void {
233
343
 
234
344
  function mount(): void {
235
345
  if (typeof document === "undefined") return;
236
- // Wire the docked panel first; its presence decides whether the floating
237
- // button is a full widget (no rail) or just the mobile entry (rail present).
238
346
  const hasDocked = wireDockedPanel();
239
347
  mountFloating(hasDocked);
240
348
  }
241
349
 
242
350
  function unmount(): void {
243
351
  if (typeof document === "undefined") return;
244
- // Only the floating widget is ours to remove; the docked shell is part of the
245
- // document Astro swaps, and is re-wired by mount() on the next page-load.
246
352
  document.getElementById(ROOT_ID)?.remove();
247
353
  }
248
354
 
package/src/styles.css CHANGED
@@ -28,8 +28,43 @@
28
28
  /* The docked form fills the rail column. */
29
29
  .dba-panel .dba-form { padding: 0; gap: 0.5rem; }
30
30
  .dba-panel .dba-title { font-size: 0.95rem; }
31
- /* The rail aside scrolls, so let the answer grow rather than scroll internally. */
31
+ /* The rail aside scrolls, so let answers grow rather than scroll internally. */
32
32
  .dba-panel .dba-answer { max-height: none; }
33
+ /* Keep the input visible while the rail scrolls the conversation. */
34
+ .dba-panel .dba-inputrow {
35
+ position: sticky;
36
+ bottom: 0;
37
+ background: var(--dba-surface);
38
+ padding-block: 0.4rem;
39
+ }
40
+
41
+ /* Conversation thread (docked panel + floating dialog). */
42
+ .dba-thread { display: flex; flex-direction: column; gap: 0.75rem; }
43
+ .dba-thread:empty { display: none; }
44
+ .dba-turn-user {
45
+ align-self: flex-end;
46
+ max-width: 92%;
47
+ padding: 0.4rem 0.7rem;
48
+ white-space: pre-wrap;
49
+ background: color-mix(in srgb, var(--dba-accent) 14%, transparent);
50
+ border-radius: var(--dba-radius);
51
+ }
52
+ .dba-turn-assistant { align-self: stretch; }
53
+ .dba-header-actions { display: flex; align-items: center; gap: 0.4rem; }
54
+ .dba-newchat {
55
+ font: inherit;
56
+ font-size: 0.78rem;
57
+ padding: 0.2rem 0.5rem;
58
+ cursor: pointer;
59
+ color: var(--dba-text);
60
+ opacity: 0.7;
61
+ background: none;
62
+ border: 1px solid var(--dba-border);
63
+ border-radius: var(--dba-radius);
64
+ }
65
+ .dba-newchat:hover { opacity: 1; }
66
+ /* In the modal, bound the thread so it scrolls and the input stays visible. */
67
+ .dba-dialog .dba-thread { max-height: 55vh; overflow-y: auto; }
33
68
 
34
69
  .dba-trigger {
35
70
  position: fixed;