@pluno/product-agent-web 0.1.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.
@@ -0,0 +1,901 @@
1
+ import { PlunoProductAgent as $ } from "./product-agent-sdk.js";
2
+ const y = {
3
+ launcherLabel: "Ask AI...",
4
+ accentColor: "#18181b",
5
+ colorScheme: "light",
6
+ position: "bottom-right"
7
+ };
8
+ async function I(e = {}) {
9
+ const o = {
10
+ launcherLabel: e.launcherLabel ?? y.launcherLabel,
11
+ accentColor: e.accentColor ?? y.accentColor,
12
+ colorScheme: e.colorScheme ?? y.colorScheme,
13
+ position: e.position ?? y.position,
14
+ token: e.token,
15
+ tokenProvider: e.tokenProvider,
16
+ tokenEndpoint: e.tokenEndpoint,
17
+ backendUrl: e.backendUrl,
18
+ webSocketFactory: e.webSocketFactory
19
+ }, n = document.createElement("div");
20
+ n.className = `pluno-pa-widget pluno-pa-widget--${o.position}`, n.innerHTML = `
21
+ <form class="pluno-pa-widget__launcher">
22
+ <input class="pluno-pa-widget__launcher-input" type="text" placeholder="${A(o.launcherLabel)}" aria-label="${A(o.launcherLabel)}" />
23
+ </form>
24
+ <button type="button" class="pluno-pa-widget__close" aria-label="Close" hidden>
25
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
26
+ <path d="m6 9 6 6 6-6" />
27
+ </svg>
28
+ </button>
29
+ <section class="pluno-pa-widget__panel" hidden>
30
+ <div class="pluno-pa-widget__timeline-wrap">
31
+ <main class="pluno-pa-widget__timeline">
32
+ <div class="pluno-pa-widget__empty">What can I help you with?</div>
33
+ </main>
34
+ <button type="button" class="pluno-pa-widget__new-chat" aria-label="New chat" title="New chat" hidden>
35
+ <svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
36
+ <path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
37
+ <path d="M18.4 2.6a2.1 2.1 0 0 1 3 3L12 15l-4 1 1-4Z" />
38
+ </svg>
39
+ </button>
40
+ </div>
41
+ <form class="pluno-pa-widget__composer">
42
+ <div class="pluno-pa-widget__composer-inner">
43
+ <textarea class="pluno-pa-widget__input" rows="1" placeholder="What can I help you with?"></textarea>
44
+ <button type="button" class="pluno-pa-widget__send" aria-label="Send">
45
+ <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
46
+ <line x1="12" y1="19" x2="12" y2="5" />
47
+ <polyline points="5 12 12 5 19 12" />
48
+ </svg>
49
+ </button>
50
+ </div>
51
+ </form>
52
+ </section>
53
+ `, document.body.appendChild(n), n.classList.add(`pluno-pa-widget--${o.colorScheme}`), B(o.accentColor);
54
+ const t = n.querySelector(".pluno-pa-widget__launcher"), r = n.querySelector(".pluno-pa-widget__launcher-input"), g = n.querySelector(".pluno-pa-widget__panel"), l = n.querySelector(".pluno-pa-widget__close"), c = n.querySelector(".pluno-pa-widget__composer"), a = n.querySelector(".pluno-pa-widget__input"), i = n.querySelector(".pluno-pa-widget__send"), s = n.querySelector(".pluno-pa-widget__new-chat"), h = n.querySelector(".pluno-pa-widget__timeline");
55
+ if (!t || !r || !g || !l || !c || !a || !i || !s || !h)
56
+ throw new Error("Failed to mount Product Agent widget");
57
+ const p = o.token || o.webSocketFactory ? void 0 : o.tokenProvider ?? (async () => {
58
+ if (!o.tokenEndpoint)
59
+ throw new Error("Product Agent widget requires token or tokenEndpoint");
60
+ const d = await fetch(o.tokenEndpoint, {
61
+ method: "POST",
62
+ credentials: "include",
63
+ headers: { "content-type": "application/json" },
64
+ body: JSON.stringify({ origin: location.origin, url: location.href })
65
+ });
66
+ if (!d.ok)
67
+ throw new Error("Failed to load Product Agent token");
68
+ const k = await d.json();
69
+ if (!k.token)
70
+ throw new Error("Product Agent token endpoint did not return a token");
71
+ return k.token;
72
+ }), u = await $.init({
73
+ token: o.token,
74
+ tokenProvider: p,
75
+ backendUrl: o.backendUrl,
76
+ webSocketFactory: o.webSocketFactory
77
+ }), m = F(o), v = new Set(m.openToolGroupKeys);
78
+ let f = null;
79
+ const b = () => {
80
+ H(o, {
81
+ isOpen: n.classList.contains("pluno-pa-widget--open"),
82
+ composerValue: a.value,
83
+ openToolGroupKeys: [...v]
84
+ });
85
+ }, _ = (d = "", k = !0) => {
86
+ if (f !== null && (window.clearTimeout(f), f = null), g.hidden = !1, l.hidden = !1, n.classList.remove("pluno-pa-widget--closing"), !k) {
87
+ n.classList.add("pluno-pa-widget--open"), t.hidden = !0, d && (a.value = d, x(a)), a.focus(), a.setSelectionRange(a.value.length, a.value.length), b();
88
+ return;
89
+ }
90
+ window.requestAnimationFrame(() => {
91
+ n.classList.add("pluno-pa-widget--open"), d && (a.value = d, x(a)), a.focus(), a.setSelectionRange(a.value.length, a.value.length), b(), f = window.setTimeout(() => {
92
+ t.hidden = !0;
93
+ }, 220);
94
+ });
95
+ }, S = () => {
96
+ f !== null && (window.clearTimeout(f), f = null), t.hidden = !1, n.classList.add("pluno-pa-widget--closing"), n.classList.remove("pluno-pa-widget--open"), b(), f = window.setTimeout(() => {
97
+ g.hidden = !0, l.hidden = !0, n.classList.remove("pluno-pa-widget--closing");
98
+ }, 220);
99
+ };
100
+ t.addEventListener("submit", (d) => {
101
+ d.preventDefault(), _(r.value), r.value = "";
102
+ }), r.addEventListener("focus", () => {
103
+ _(r.value), r.value = "";
104
+ }), r.addEventListener("input", () => {
105
+ if (!r.value)
106
+ return;
107
+ const d = r.value;
108
+ _(d), r.value = "";
109
+ }), l.addEventListener("click", S);
110
+ const L = (d) => {
111
+ if (!(!d.metaKey || d.key.toLowerCase() !== "k")) {
112
+ if (d.preventDefault(), g.hidden || !n.classList.contains("pluno-pa-widget--open")) {
113
+ _();
114
+ return;
115
+ }
116
+ S();
117
+ }
118
+ };
119
+ return document.addEventListener("keydown", L), c.addEventListener("submit", (d) => {
120
+ d.preventDefault(), E(u, a);
121
+ }), i.addEventListener("click", () => {
122
+ if (u.getState().isThinking) {
123
+ u.stop();
124
+ return;
125
+ }
126
+ E(u, a);
127
+ }), s.addEventListener("click", () => {
128
+ u.startNewSession(), v.clear(), a.value = "", x(a), b(), a.focus();
129
+ }), a.addEventListener("input", () => {
130
+ x(a), b();
131
+ }), a.addEventListener("keydown", (d) => {
132
+ d.key !== "Enter" || d.shiftKey || (d.preventDefault(), E(u, a));
133
+ }), m.composerValue && (a.value = m.composerValue, x(a)), m.isOpen && _(a.value, !1), u.on(
134
+ "state",
135
+ (d) => T(h, i, s, d, v, b)
136
+ ), T(h, i, s, u.getState(), v, b), {
137
+ agent: u,
138
+ destroy: () => {
139
+ u.destroy(), document.removeEventListener("keydown", L), n.remove();
140
+ }
141
+ };
142
+ }
143
+ function E(e, o) {
144
+ const n = o.value.trim();
145
+ n && (o.value = "", o.style.height = "", o.style.overflowY = "hidden", e.sendMessage(n), o.dispatchEvent(new Event("input", { bubbles: !0 })));
146
+ }
147
+ function x(e) {
148
+ e.style.height = "0px";
149
+ const o = Math.min(e.scrollHeight, 140);
150
+ e.style.height = `${o}px`, e.style.overflowY = e.scrollHeight > 140 ? "auto" : "hidden";
151
+ }
152
+ function T(e, o, n, t, r, g) {
153
+ const l = [...t.messages];
154
+ e.innerHTML = "", O(o, t.isThinking);
155
+ const c = l.length > 0 || !!t.assistantDraft;
156
+ if (n.hidden = !c, l.length === 0 && !t.assistantDraft && !t.lastError) {
157
+ e.innerHTML = '<div class="pluno-pa-widget__empty">What can I help you with?</div>';
158
+ return;
159
+ }
160
+ let a = null, i = null, s = [];
161
+ const h = () => {
162
+ if (s.length === 0)
163
+ return;
164
+ const p = W(s, r, g);
165
+ e.appendChild(p), a = null, i = p, s = [];
166
+ };
167
+ for (let p = 0; p < l.length; p += 1) {
168
+ const u = l[p];
169
+ if (u.role === "tool") {
170
+ s.push(u);
171
+ continue;
172
+ }
173
+ if (u.role === "system")
174
+ continue;
175
+ h();
176
+ const m = document.createElement("div");
177
+ m.className = `pluno-pa-widget__message pluno-pa-widget__message--${u.role}`, u.role === "assistant" ? N(m, u.content) : m.textContent = u.content, e.appendChild(m), a = m, i = null;
178
+ }
179
+ if (h(), t.assistantDraft) {
180
+ const p = document.createElement("div");
181
+ p.className = "pluno-pa-widget__message pluno-pa-widget__message--assistant", N(p, t.assistantDraft), e.appendChild(p), a = p, i = null;
182
+ }
183
+ if (t.isThinking && !t.assistantDraft) {
184
+ const p = document.createElement("div");
185
+ p.className = "pluno-pa-widget__status", p.textContent = "Thinking...", e.appendChild(p), a = null, i = p;
186
+ }
187
+ if (t.lastError) {
188
+ const p = document.createElement("div");
189
+ p.className = "pluno-pa-widget__error", p.textContent = t.lastError, e.appendChild(p), a = p, i = null;
190
+ }
191
+ a?.classList.add("pluno-pa-widget__message--bottom-reserve"), i?.classList.add("pluno-pa-widget__bottom-reserve-compact"), e.scrollTop = e.scrollHeight;
192
+ }
193
+ function W(e, o, n) {
194
+ const t = document.createElement("article");
195
+ t.className = "pluno-pa-widget__tool-group", t.title = new Date(e[e.length - 1].createdAt).toLocaleString();
196
+ const r = document.createElement("details"), g = M(e);
197
+ r.open = o.has(g), r.addEventListener("toggle", () => {
198
+ r.open ? o.add(g) : o.delete(g), n();
199
+ });
200
+ const l = document.createElement("summary");
201
+ l.className = "pluno-pa-widget__tool-summary", e.some((i) => i.loading) && l.appendChild(z());
202
+ const a = document.createElement("span");
203
+ if (a.textContent = e[e.length - 1].content || "Run tool", l.appendChild(a), r.appendChild(l), e.length > 0) {
204
+ const i = document.createElement("div");
205
+ i.className = "pluno-pa-widget__tool-lines";
206
+ for (const s of e) {
207
+ const h = document.createElement("div");
208
+ h.className = "pluno-pa-widget__tool-line", s.loading && h.appendChild(z());
209
+ const p = document.createElement("span");
210
+ p.textContent = s.content || "Run tool", h.appendChild(p), i.appendChild(h);
211
+ }
212
+ r.appendChild(i);
213
+ }
214
+ return t.appendChild(r), t;
215
+ }
216
+ function M(e) {
217
+ return `tools:${e.map((o) => o.id).join("|")}`;
218
+ }
219
+ function z() {
220
+ const e = document.createElement("span");
221
+ return e.className = "pluno-pa-widget__spinner", e.setAttribute("aria-hidden", "true"), e;
222
+ }
223
+ function O(e, o) {
224
+ e.setAttribute("aria-label", o ? "Stop" : "Send"), e.classList.toggle("pluno-pa-widget__send--stop", o), e.innerHTML = o ? '<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2" /></svg>' : '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="19" x2="12" y2="5" /><polyline points="5 12 12 5 19 12" /></svg>';
225
+ }
226
+ function N(e, o) {
227
+ const n = o.replace(/\r\n/g, `
228
+ `).split(`
229
+ `);
230
+ let t = 0;
231
+ for (; t < n.length; ) {
232
+ const r = n[t];
233
+ if (!r.trim()) {
234
+ t += 1;
235
+ continue;
236
+ }
237
+ if (r.match(/^```(\w+)?\s*$/)) {
238
+ const i = [];
239
+ for (t += 1; t < n.length && !/^```\s*$/.test(n[t]); )
240
+ i.push(n[t]), t += 1;
241
+ t += t < n.length ? 1 : 0;
242
+ const s = document.createElement("pre"), h = document.createElement("code");
243
+ h.textContent = i.join(`
244
+ `), s.appendChild(h), e.appendChild(s);
245
+ continue;
246
+ }
247
+ const l = r.match(/^(#{1,3})\s+(.+)$/);
248
+ if (l) {
249
+ const i = document.createElement(`h${l[1].length + 2}`);
250
+ C(i, l[2]), e.appendChild(i), t += 1;
251
+ continue;
252
+ }
253
+ if (/^\s*[-*+]\s+/.test(r)) {
254
+ const i = document.createElement("ul");
255
+ for (; t < n.length && /^\s*[-*+]\s+/.test(n[t]); ) {
256
+ const s = document.createElement("li");
257
+ C(s, n[t].replace(/^\s*[-*+]\s+/, "")), i.appendChild(s), t += 1;
258
+ }
259
+ e.appendChild(i);
260
+ continue;
261
+ }
262
+ if (/^\s*\d+\.\s+/.test(r)) {
263
+ const i = document.createElement("ol");
264
+ for (; t < n.length && /^\s*\d+\.\s+/.test(n[t]); ) {
265
+ const s = document.createElement("li");
266
+ C(s, n[t].replace(/^\s*\d+\.\s+/, "")), i.appendChild(s), t += 1;
267
+ }
268
+ e.appendChild(i);
269
+ continue;
270
+ }
271
+ const c = [r];
272
+ for (t += 1; t < n.length && n[t].trim() && !/^```/.test(n[t]) && !/^(#{1,3})\s+/.test(n[t]) && !/^\s*[-*+]\s+/.test(n[t]) && !/^\s*\d+\.\s+/.test(n[t]); )
273
+ c.push(n[t]), t += 1;
274
+ const a = document.createElement("p");
275
+ c.forEach((i, s) => {
276
+ s > 0 && a.appendChild(document.createElement("br")), C(a, i);
277
+ }), e.appendChild(a);
278
+ }
279
+ }
280
+ function C(e, o) {
281
+ const n = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[[^\]]+\]\(([^)\s]+)\))/g;
282
+ let t = 0;
283
+ for (const r of o.matchAll(n)) {
284
+ const g = r.index ?? 0;
285
+ g > t && e.appendChild(document.createTextNode(o.slice(t, g)));
286
+ const l = r[0];
287
+ if (l.startsWith("`")) {
288
+ const c = document.createElement("code");
289
+ c.textContent = l.slice(1, -1), e.appendChild(c);
290
+ } else if (l.startsWith("**")) {
291
+ const c = document.createElement("strong");
292
+ c.textContent = l.slice(2, -2), e.appendChild(c);
293
+ } else if (l.startsWith("*")) {
294
+ const c = document.createElement("em");
295
+ c.textContent = l.slice(1, -1), e.appendChild(c);
296
+ } else {
297
+ const c = l.match(/^\[([^\]]+)\]\(([^)\s]+)\)$/);
298
+ if (c && j(c[2])) {
299
+ const a = document.createElement("a");
300
+ a.href = c[2], a.target = "_top", a.rel = "noreferrer", a.textContent = c[1], e.appendChild(a);
301
+ } else
302
+ e.appendChild(document.createTextNode(l));
303
+ }
304
+ t = g + l.length;
305
+ }
306
+ t < o.length && e.appendChild(document.createTextNode(o.slice(t)));
307
+ }
308
+ function j(e) {
309
+ if (e.startsWith("/") || e.startsWith("#"))
310
+ return !0;
311
+ try {
312
+ const o = new URL(e);
313
+ return ["http:", "https:", "mailto:"].includes(o.protocol);
314
+ } catch {
315
+ return !1;
316
+ }
317
+ }
318
+ function B(e) {
319
+ if (document.getElementById("pluno-pa-widget-styles"))
320
+ return;
321
+ const o = document.createElement("style");
322
+ o.id = "pluno-pa-widget-styles", o.textContent = `
323
+ .pluno-pa-widget {
324
+ --pluno-pa-accent: ${e};
325
+ --pluno-pa-bg: #fafaf9;
326
+ --pluno-pa-surface: #f4f4f2;
327
+ --pluno-pa-bubble: #eeeded;
328
+ --pluno-pa-text: #18181b;
329
+ --pluno-pa-muted: #5c5c63;
330
+ --pluno-pa-faint: #8e8e95;
331
+ --pluno-pa-divider: rgba(24, 24, 27, 0.08);
332
+ --pluno-pa-launcher-hairline: rgba(24, 24, 27, 0.06);
333
+ --pluno-pa-launcher-width: min(175px, calc(100vw - 36px));
334
+ --pluno-pa-accent-soft: rgba(24, 24, 27, 0.12);
335
+ --pluno-pa-accent-contrast: #fafafa;
336
+ position: fixed;
337
+ z-index: 2147483645;
338
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, "Helvetica Neue", sans-serif;
339
+ font-size: 14px;
340
+ color: var(--pluno-pa-text);
341
+ width: var(--pluno-pa-launcher-width);
342
+ height: 41px;
343
+ transition:
344
+ width 220ms cubic-bezier(0.2, 0.8, 0.2, 1),
345
+ height 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
346
+ }
347
+ .pluno-pa-widget--dark {
348
+ --pluno-pa-bg: #0f0f10;
349
+ --pluno-pa-surface: #18181a;
350
+ --pluno-pa-bubble: #232326;
351
+ --pluno-pa-text: #f5f5f4;
352
+ --pluno-pa-muted: #a1a1a6;
353
+ --pluno-pa-faint: #6b6b72;
354
+ --pluno-pa-divider: rgba(245, 245, 244, 0.12);
355
+ --pluno-pa-launcher-hairline: rgba(245, 245, 244, 0.1);
356
+ --pluno-pa-accent-soft: rgba(245, 245, 244, 0.22);
357
+ --pluno-pa-accent-contrast: #18181b;
358
+ }
359
+ .pluno-pa-widget * {
360
+ box-sizing: border-box;
361
+ }
362
+ .pluno-pa-widget [hidden] {
363
+ display: none !important;
364
+ }
365
+ .pluno-pa-widget--bottom-right { right: 26px; bottom: 24px; }
366
+ .pluno-pa-widget--bottom-left { left: 26px; bottom: 24px; }
367
+ .pluno-pa-widget--open,
368
+ .pluno-pa-widget--closing {
369
+ width: min(420px, calc(100vw - 28px));
370
+ height: min(680px, calc(100vh - 28px));
371
+ }
372
+ .pluno-pa-widget__launcher {
373
+ position: absolute;
374
+ right: 0;
375
+ bottom: 0;
376
+ width: var(--pluno-pa-launcher-width);
377
+ height: 41px;
378
+ border: 0;
379
+ border-radius: 21px;
380
+ background: var(--pluno-pa-surface);
381
+ margin: 0;
382
+ box-shadow:
383
+ 0 10px 30px rgba(24, 24, 27, 0.18),
384
+ inset 0 0 0 1px var(--pluno-pa-launcher-hairline);
385
+ transform-origin: bottom right;
386
+ transition:
387
+ opacity 160ms ease,
388
+ transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1),
389
+ box-shadow 150ms ease,
390
+ background 150ms ease;
391
+ }
392
+ .pluno-pa-widget__launcher-input {
393
+ width: 100%;
394
+ height: 100%;
395
+ border: 0;
396
+ outline: none;
397
+ background: transparent;
398
+ color: var(--pluno-pa-text);
399
+ padding: 0 12px 0 18px;
400
+ font: inherit;
401
+ font-size: 15px;
402
+ line-height: 41px;
403
+ }
404
+ .pluno-pa-widget__launcher-input::placeholder {
405
+ color: var(--pluno-pa-faint);
406
+ }
407
+ .pluno-pa-widget--bottom-left .pluno-pa-widget__launcher {
408
+ left: 0;
409
+ right: auto;
410
+ transform-origin: bottom left;
411
+ }
412
+ .pluno-pa-widget__launcher:hover {
413
+ box-shadow:
414
+ 0 12px 32px rgba(24, 24, 27, 0.16),
415
+ inset 0 0 0 1px color-mix(in srgb, var(--pluno-pa-accent) 55%, transparent);
416
+ }
417
+ .pluno-pa-widget__launcher:focus-within {
418
+ box-shadow:
419
+ 0 12px 32px rgba(24, 24, 27, 0.16),
420
+ inset 0 0 0 1px var(--pluno-pa-launcher-hairline);
421
+ }
422
+ .pluno-pa-widget--open .pluno-pa-widget__launcher {
423
+ opacity: 0;
424
+ pointer-events: none;
425
+ transform: translateY(-8px) scale(0.96);
426
+ }
427
+ .pluno-pa-widget__panel {
428
+ position: absolute;
429
+ right: 0;
430
+ bottom: 0;
431
+ width: 100%;
432
+ height: 100%;
433
+ display: flex;
434
+ flex-direction: column;
435
+ min-height: 0;
436
+ background: var(--pluno-pa-bg);
437
+ border: 1px solid var(--pluno-pa-divider);
438
+ border-radius: 14px;
439
+ box-shadow: 0 18px 54px rgba(24, 24, 27, 0.18);
440
+ overflow: hidden;
441
+ opacity: 0;
442
+ transform: translateY(14px) scale(0.96);
443
+ transform-origin: bottom right;
444
+ transition:
445
+ opacity 180ms ease,
446
+ transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
447
+ }
448
+ .pluno-pa-widget--bottom-left .pluno-pa-widget__panel {
449
+ left: 0;
450
+ right: auto;
451
+ transform-origin: bottom left;
452
+ }
453
+ .pluno-pa-widget--open .pluno-pa-widget__panel {
454
+ opacity: 1;
455
+ transform: translateY(0) scale(1);
456
+ }
457
+ .pluno-pa-widget__close {
458
+ position: absolute;
459
+ top: -42px;
460
+ right: 0;
461
+ z-index: 2;
462
+ width: 32px;
463
+ height: 32px;
464
+ display: inline-flex;
465
+ align-items: center;
466
+ justify-content: center;
467
+ border: 1px solid var(--pluno-pa-divider);
468
+ border-radius: 999px;
469
+ background: color-mix(in srgb, var(--pluno-pa-bg) 92%, transparent);
470
+ color: var(--pluno-pa-muted);
471
+ cursor: pointer;
472
+ padding: 0;
473
+ opacity: 0;
474
+ transform: translateY(8px) scale(0.96);
475
+ transition:
476
+ opacity 150ms ease,
477
+ background 120ms ease,
478
+ color 120ms ease,
479
+ transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
480
+ }
481
+ .pluno-pa-widget--open .pluno-pa-widget__close {
482
+ opacity: 1;
483
+ transform: translateY(0) scale(1);
484
+ }
485
+ .pluno-pa-widget__close:hover {
486
+ background: var(--pluno-pa-surface);
487
+ color: var(--pluno-pa-text);
488
+ }
489
+ .pluno-pa-widget__close:active {
490
+ transform: scale(0.96);
491
+ }
492
+ @media (prefers-reduced-motion: reduce) {
493
+ .pluno-pa-widget__launcher,
494
+ .pluno-pa-widget__panel,
495
+ .pluno-pa-widget__close,
496
+ .pluno-pa-widget {
497
+ transition: none;
498
+ }
499
+ }
500
+ .pluno-pa-widget__timeline-wrap {
501
+ flex: 1 1 0;
502
+ min-height: 0;
503
+ position: relative;
504
+ }
505
+ .pluno-pa-widget__timeline-wrap::before {
506
+ content: "";
507
+ position: absolute;
508
+ top: 0;
509
+ left: 0;
510
+ right: 0;
511
+ height: 18px;
512
+ pointer-events: none;
513
+ z-index: 1;
514
+ background: linear-gradient(
515
+ to bottom,
516
+ var(--pluno-pa-bg) 0%,
517
+ color-mix(in srgb, var(--pluno-pa-bg) 72%, transparent) 42%,
518
+ transparent 100%
519
+ );
520
+ }
521
+ .pluno-pa-widget__timeline {
522
+ height: 100%;
523
+ display: flex;
524
+ flex-direction: column;
525
+ gap: 14px;
526
+ overflow: auto;
527
+ padding: 22px 14px 12px;
528
+ min-height: 0;
529
+ scrollbar-color: var(--pluno-pa-divider) transparent;
530
+ scrollbar-width: thin;
531
+ }
532
+ .pluno-pa-widget__new-chat {
533
+ position: absolute;
534
+ right: 14px;
535
+ bottom: 14px;
536
+ z-index: 2;
537
+ width: 36px;
538
+ height: 36px;
539
+ display: inline-flex;
540
+ align-items: center;
541
+ justify-content: center;
542
+ border: 1px solid var(--pluno-pa-divider);
543
+ border-radius: 999px;
544
+ background: color-mix(in srgb, var(--pluno-pa-bg) 92%, transparent);
545
+ color: var(--pluno-pa-text);
546
+ box-shadow: 0 6px 14px rgba(24, 24, 27, 0.08);
547
+ cursor: pointer;
548
+ padding: 0;
549
+ transition:
550
+ background 120ms ease,
551
+ transform 120ms ease,
552
+ box-shadow 120ms ease;
553
+ }
554
+ .pluno-pa-widget__new-chat:hover:not(:disabled) {
555
+ background: var(--pluno-pa-surface);
556
+ box-shadow: 0 8px 18px rgba(24, 24, 27, 0.1);
557
+ }
558
+ .pluno-pa-widget__new-chat:active:not(:disabled) {
559
+ transform: scale(0.96);
560
+ }
561
+ .pluno-pa-widget__timeline::-webkit-scrollbar,
562
+ .pluno-pa-widget__message--assistant pre::-webkit-scrollbar,
563
+ .pluno-pa-widget__input::-webkit-scrollbar {
564
+ width: 8px;
565
+ height: 8px;
566
+ }
567
+ .pluno-pa-widget__timeline::-webkit-scrollbar-track,
568
+ .pluno-pa-widget__message--assistant pre::-webkit-scrollbar-track,
569
+ .pluno-pa-widget__input::-webkit-scrollbar-track {
570
+ background: transparent;
571
+ }
572
+ .pluno-pa-widget__timeline::-webkit-scrollbar-thumb,
573
+ .pluno-pa-widget__message--assistant pre::-webkit-scrollbar-thumb,
574
+ .pluno-pa-widget__input::-webkit-scrollbar-thumb {
575
+ background: var(--pluno-pa-divider);
576
+ border-radius: 999px;
577
+ }
578
+ .pluno-pa-widget__timeline::-webkit-scrollbar-corner,
579
+ .pluno-pa-widget__message--assistant pre::-webkit-scrollbar-corner,
580
+ .pluno-pa-widget__input::-webkit-scrollbar-corner {
581
+ background: transparent;
582
+ }
583
+ .pluno-pa-widget__message {
584
+ min-width: 0;
585
+ color: var(--pluno-pa-text);
586
+ font-size: 14px;
587
+ line-height: 1.55;
588
+ white-space: pre-wrap;
589
+ overflow-wrap: anywhere;
590
+ word-break: break-word;
591
+ }
592
+ .pluno-pa-widget__message--user {
593
+ align-self: flex-end;
594
+ max-width: 85%;
595
+ margin-left: auto;
596
+ background: var(--pluno-pa-bubble);
597
+ border-radius: 16px 16px 4px 16px;
598
+ padding: 8px 12px;
599
+ }
600
+ .pluno-pa-widget__message--assistant {
601
+ align-self: stretch;
602
+ padding: 0 2px;
603
+ }
604
+ .pluno-pa-widget__message--bottom-reserve {
605
+ margin-bottom: 46px;
606
+ }
607
+ .pluno-pa-widget__bottom-reserve-compact {
608
+ margin-bottom: 28px;
609
+ }
610
+ .pluno-pa-widget__message--assistant > :first-child {
611
+ margin-top: 0;
612
+ }
613
+ .pluno-pa-widget__message--assistant > :last-child {
614
+ margin-bottom: 0;
615
+ }
616
+ .pluno-pa-widget__message--assistant p,
617
+ .pluno-pa-widget__message--assistant ul,
618
+ .pluno-pa-widget__message--assistant ol,
619
+ .pluno-pa-widget__message--assistant pre,
620
+ .pluno-pa-widget__message--assistant h3,
621
+ .pluno-pa-widget__message--assistant h4,
622
+ .pluno-pa-widget__message--assistant h5 {
623
+ margin: 0 0 10px;
624
+ color: inherit;
625
+ }
626
+ .pluno-pa-widget__message--assistant h3,
627
+ .pluno-pa-widget__message--assistant h4,
628
+ .pluno-pa-widget__message--assistant h5 {
629
+ font-size: 14px;
630
+ line-height: 1.35;
631
+ font-weight: 650;
632
+ }
633
+ .pluno-pa-widget__message--assistant ul,
634
+ .pluno-pa-widget__message--assistant ol {
635
+ padding-left: 20px;
636
+ }
637
+ .pluno-pa-widget__message--assistant code {
638
+ font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
639
+ font-size: 12px;
640
+ background: rgba(24, 24, 27, 0.05);
641
+ padding: 1px 5px;
642
+ border-radius: 4px;
643
+ overflow-wrap: anywhere;
644
+ word-break: break-word;
645
+ }
646
+ .pluno-pa-widget__message--assistant pre {
647
+ background: var(--pluno-pa-bubble);
648
+ color: var(--pluno-pa-text);
649
+ padding: 12px;
650
+ border-radius: 8px;
651
+ overflow-x: auto;
652
+ font-size: 12px;
653
+ line-height: 1.5;
654
+ white-space: pre-wrap;
655
+ scrollbar-color: var(--pluno-pa-divider) transparent;
656
+ scrollbar-width: thin;
657
+ }
658
+ .pluno-pa-widget__message--assistant pre code {
659
+ background: transparent;
660
+ padding: 0;
661
+ white-space: pre-wrap;
662
+ }
663
+ .pluno-pa-widget__message--assistant a {
664
+ color: var(--pluno-pa-accent);
665
+ text-decoration: none;
666
+ border-bottom: 1px solid color-mix(in srgb, var(--pluno-pa-accent) 30%, transparent);
667
+ overflow-wrap: anywhere;
668
+ word-break: break-word;
669
+ }
670
+ .pluno-pa-widget__message--assistant a:hover {
671
+ border-bottom-color: var(--pluno-pa-accent);
672
+ }
673
+ .pluno-pa-widget__tool-group {
674
+ font-size: 12px;
675
+ color: var(--pluno-pa-muted);
676
+ line-height: 1.35;
677
+ }
678
+ .pluno-pa-widget__tool-group details {
679
+ padding: 0;
680
+ }
681
+ .pluno-pa-widget__tool-summary {
682
+ display: inline-flex;
683
+ align-items: center;
684
+ gap: 8px;
685
+ cursor: pointer;
686
+ list-style: none;
687
+ padding: 2px 0;
688
+ color: var(--pluno-pa-muted);
689
+ }
690
+ .pluno-pa-widget__tool-summary::-webkit-details-marker {
691
+ display: none;
692
+ }
693
+ .pluno-pa-widget__tool-summary::before {
694
+ content: "";
695
+ display: inline-block;
696
+ width: 0;
697
+ height: 0;
698
+ border-left: 4px solid var(--pluno-pa-faint);
699
+ border-top: 3px solid transparent;
700
+ border-bottom: 3px solid transparent;
701
+ transition: transform 150ms ease;
702
+ }
703
+ .pluno-pa-widget__tool-group details[open] .pluno-pa-widget__tool-summary::before {
704
+ transform: rotate(90deg);
705
+ }
706
+ .pluno-pa-widget__tool-lines {
707
+ margin: 6px 0 0 14px;
708
+ display: flex;
709
+ flex-direction: column;
710
+ gap: 4px;
711
+ padding-left: 10px;
712
+ border-left: 1px solid var(--pluno-pa-divider);
713
+ }
714
+ .pluno-pa-widget__tool-line {
715
+ display: inline-flex;
716
+ align-items: center;
717
+ gap: 6px;
718
+ font-size: 12px;
719
+ color: var(--pluno-pa-muted);
720
+ line-height: 1.35;
721
+ }
722
+ .pluno-pa-widget__spinner {
723
+ flex: 0 0 auto;
724
+ width: 10px;
725
+ height: 10px;
726
+ border-radius: 999px;
727
+ border: 1.5px solid var(--pluno-pa-accent-soft);
728
+ border-top-color: var(--pluno-pa-accent);
729
+ animation: pluno-pa-spin 0.8s linear infinite;
730
+ }
731
+ @keyframes pluno-pa-spin {
732
+ to { transform: rotate(360deg); }
733
+ }
734
+ .pluno-pa-widget__composer {
735
+ flex: 0 0 auto;
736
+ margin: 0;
737
+ padding: 10px 12px 12px;
738
+ background: var(--pluno-pa-bg);
739
+ border-top: 1px solid var(--pluno-pa-divider);
740
+ }
741
+ .pluno-pa-widget__composer-inner {
742
+ position: relative;
743
+ display: flex;
744
+ align-items: flex-end;
745
+ background: var(--pluno-pa-surface);
746
+ border: 1px solid var(--pluno-pa-divider);
747
+ border-radius: 14px;
748
+ padding: 8px 44px 8px 12px;
749
+ transition: border-color 150ms ease;
750
+ }
751
+ .pluno-pa-widget__composer-inner:focus-within {
752
+ border-color: var(--pluno-pa-accent);
753
+ }
754
+ .pluno-pa-widget__input {
755
+ flex: 1;
756
+ width: 100%;
757
+ resize: none;
758
+ min-height: 22px;
759
+ max-height: 160px;
760
+ border: 0;
761
+ outline: none;
762
+ background: transparent;
763
+ color: inherit;
764
+ padding: 3px 0;
765
+ font: inherit;
766
+ font-size: 14px;
767
+ line-height: 1.45;
768
+ overflow-y: hidden;
769
+ scrollbar-color: var(--pluno-pa-divider) transparent;
770
+ scrollbar-width: thin;
771
+ }
772
+ .pluno-pa-widget__input::placeholder {
773
+ color: var(--pluno-pa-faint);
774
+ }
775
+ .pluno-pa-widget__send {
776
+ position: absolute;
777
+ right: 6px;
778
+ top: 50%;
779
+ margin-top: -14px;
780
+ width: 28px;
781
+ height: 28px;
782
+ display: inline-flex;
783
+ align-items: center;
784
+ justify-content: center;
785
+ border: 0;
786
+ border-radius: 999px;
787
+ background: var(--pluno-pa-accent);
788
+ color: var(--pluno-pa-accent-contrast);
789
+ padding: 0;
790
+ cursor: pointer;
791
+ transition: filter 120ms ease, transform 120ms ease;
792
+ }
793
+ .pluno-pa-widget__send:hover:not(:disabled) {
794
+ filter: brightness(0.92);
795
+ }
796
+ .pluno-pa-widget__send:active:not(:disabled) {
797
+ transform: scale(0.96);
798
+ }
799
+ .pluno-pa-widget__send--stop svg {
800
+ width: 18px;
801
+ height: 18px;
802
+ }
803
+ .pluno-pa-widget__send:disabled {
804
+ background: var(--pluno-pa-surface);
805
+ color: var(--pluno-pa-faint);
806
+ cursor: not-allowed;
807
+ }
808
+ .pluno-pa-widget__empty,
809
+ .pluno-pa-widget__status,
810
+ .pluno-pa-widget__error {
811
+ color: var(--pluno-pa-faint);
812
+ font-size: 13px;
813
+ text-align: center;
814
+ margin: auto;
815
+ padding: 24px 0;
816
+ }
817
+ .pluno-pa-widget__status {
818
+ display: inline-flex;
819
+ align-items: center;
820
+ gap: 8px;
821
+ align-self: flex-start;
822
+ margin: 0;
823
+ color: var(--pluno-pa-muted);
824
+ font-size: 12px;
825
+ padding: 2px 0;
826
+ text-align: left;
827
+ }
828
+ .pluno-pa-widget__status::before {
829
+ content: "";
830
+ width: 6px;
831
+ height: 6px;
832
+ border-radius: 999px;
833
+ background: var(--pluno-pa-accent);
834
+ animation: pluno-pa-pulse 1.2s ease-in-out infinite;
835
+ }
836
+ @keyframes pluno-pa-pulse {
837
+ 0%, 100% { opacity: 0.25; transform: scale(0.85); }
838
+ 50% { opacity: 1; transform: scale(1); }
839
+ }
840
+ .pluno-pa-widget__error {
841
+ margin: 0 14px 8px;
842
+ padding: 6px 10px;
843
+ border-radius: 8px;
844
+ background: rgba(239, 68, 68, 0.14);
845
+ color: #ef4444;
846
+ font-size: 12px;
847
+ line-height: 1.4;
848
+ text-align: left;
849
+ }
850
+ .pluno-pa-widget__status.pluno-pa-widget__bottom-reserve-compact {
851
+ margin-bottom: 28px;
852
+ }
853
+ .pluno-pa-widget__error.pluno-pa-widget__message--bottom-reserve {
854
+ margin-bottom: 46px;
855
+ }
856
+ `, document.head.appendChild(o);
857
+ }
858
+ function A(e) {
859
+ const o = document.createElement("span");
860
+ return o.textContent = e, o.innerHTML;
861
+ }
862
+ function P(e) {
863
+ return `pluno.productAgent.widgetState.${location.origin}.${e.backendUrl ?? ""}.${e.tokenEndpoint ?? ""}.${e.position ?? ""}`;
864
+ }
865
+ function F(e) {
866
+ try {
867
+ const o = window.localStorage.getItem(P(e));
868
+ if (!o)
869
+ return { isOpen: !1, composerValue: "", openToolGroupKeys: [] };
870
+ const n = JSON.parse(o);
871
+ return {
872
+ isOpen: n.isOpen === !0,
873
+ composerValue: typeof n.composerValue == "string" ? n.composerValue : "",
874
+ openToolGroupKeys: Array.isArray(n.openToolGroupKeys) ? n.openToolGroupKeys.filter((t) => typeof t == "string") : []
875
+ };
876
+ } catch {
877
+ return { isOpen: !1, composerValue: "", openToolGroupKeys: [] };
878
+ }
879
+ }
880
+ function H(e, o) {
881
+ try {
882
+ window.localStorage.setItem(P(e), JSON.stringify(o));
883
+ } catch {
884
+ return;
885
+ }
886
+ }
887
+ const w = document.currentScript;
888
+ w && w.dataset.plunoAutoMount !== "false" && I({
889
+ token: w?.dataset.plunoToken,
890
+ tokenEndpoint: w?.dataset.plunoTokenEndpoint,
891
+ backendUrl: w?.dataset.plunoBackendUrl,
892
+ launcherLabel: w?.dataset.plunoLauncherLabel,
893
+ accentColor: w?.dataset.plunoAccentColor,
894
+ colorScheme: w?.dataset.plunoColorScheme === "dark" ? "dark" : "light",
895
+ position: w?.dataset.plunoPosition === "bottom-left" ? "bottom-left" : "bottom-right"
896
+ }).catch((e) => {
897
+ console.error("Failed to mount Pluno Product Agent widget", e);
898
+ });
899
+ export {
900
+ I as mountPlunoProductAgentWidget
901
+ };