@stubwise/widget 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.
- package/dist/core/api.d.ts +93 -0
- package/dist/core/dsn.d.ts +23 -0
- package/dist/core/sse.d.ts +55 -0
- package/dist/core/storage.d.ts +14 -0
- package/dist/i18n.d.ts +46 -0
- package/dist/index.d.ts +31 -0
- package/dist/stubwise-widget.iife.js +199 -0
- package/dist/stubwise-widget.js +1093 -0
- package/dist/ui/chat.d.ts +30 -0
- package/dist/ui/styles.d.ts +12 -0
- package/dist/ui/ticket-card.d.ts +16 -0
- package/dist/ui/widget.d.ts +19 -0
- package/package.json +33 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client HTTP minimale verso la superficie pubblica del widget
|
|
3
|
+
* (`<origin>/widget/<slug>/...`, vedi `apps/server/src/routes/widget.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Ogni richiesta porta l'header `X-Stubwise-Key` (autentica il PROGETTO, non
|
|
6
|
+
* l'utente: la chiave è pubblica nel sorgente della pagina ospite, come l'SDK di
|
|
7
|
+
* ingestion). Le shape di risposta sono allineate ai contratti Zod del server.
|
|
8
|
+
*
|
|
9
|
+
* Modello d'errore (uniforme su tutte le funzioni):
|
|
10
|
+
* - errore di RETE (fetch rigetta con `TypeError`: host irraggiungibile, CORS,
|
|
11
|
+
* connessione caduta) → l'errore PROPAGA GREZZO, non viene incapsulato. Il
|
|
12
|
+
* chiamante (la UI) lo cattura come "generico".
|
|
13
|
+
* - errore HTTP (risposta con status non-2xx) → diventa un {@link WidgetApiError}
|
|
14
|
+
* con `status` e (best effort) `code`/`message` dal body.
|
|
15
|
+
*/
|
|
16
|
+
import type { WidgetDsn } from "./dsn.js";
|
|
17
|
+
import type { WidgetCitation } from "./sse.js";
|
|
18
|
+
/** Base di chiamata: coordinate del backend risolte dal DSN. */
|
|
19
|
+
export type WidgetApiBase = WidgetDsn;
|
|
20
|
+
/** Config pubblica del widget: spento, oppure attivo coi campi di rendering. */
|
|
21
|
+
export type WidgetConfig = {
|
|
22
|
+
enabled: false;
|
|
23
|
+
} | {
|
|
24
|
+
enabled: true;
|
|
25
|
+
title: string;
|
|
26
|
+
welcomeMessage: string;
|
|
27
|
+
accentColor: string;
|
|
28
|
+
language: "it" | "en";
|
|
29
|
+
chatEnabled: boolean;
|
|
30
|
+
};
|
|
31
|
+
/** Identità DICHIARATA dal sito ospite (non autenticata). */
|
|
32
|
+
export interface WidgetUser {
|
|
33
|
+
id: string;
|
|
34
|
+
email?: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Messaggio dello storico conversazione (GET messages). */
|
|
38
|
+
export interface WidgetMessage {
|
|
39
|
+
id: string;
|
|
40
|
+
role: string;
|
|
41
|
+
content: string;
|
|
42
|
+
/**
|
|
43
|
+
* Riferimenti Docs della risposta RAG. Stesso tipo OPACO dello stream
|
|
44
|
+
* ({@link WidgetCitation}, allineato al contratto server `unknown`/jsonb):
|
|
45
|
+
* array di record generici o `null` se il messaggio non ne ha.
|
|
46
|
+
*/
|
|
47
|
+
citations: WidgetCitation[] | null;
|
|
48
|
+
ticketId: string | null;
|
|
49
|
+
createdAt: string;
|
|
50
|
+
}
|
|
51
|
+
/** Corpo di conferma di un ticket dal widget (proposta + utente). */
|
|
52
|
+
export interface WidgetTicketConfirm {
|
|
53
|
+
title: string;
|
|
54
|
+
body: string;
|
|
55
|
+
type: "bug" | "feedback" | "feature";
|
|
56
|
+
userId: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Errore di un endpoint widget con status e codice applicativo del server
|
|
60
|
+
* (es. `widget_chat_cap_reached`, `widget_ticket_cap_reached`, `widget_disabled`,
|
|
61
|
+
* `conversation_not_found`, `chat_unavailable`, `invalid_ingestion_key`). Il
|
|
62
|
+
* chiamante mappa `status`/`code` su messaggi utente attuabili.
|
|
63
|
+
*/
|
|
64
|
+
export declare class WidgetApiError extends Error {
|
|
65
|
+
readonly status: number;
|
|
66
|
+
readonly code?: string;
|
|
67
|
+
constructor(status: number, code?: string, message?: string);
|
|
68
|
+
}
|
|
69
|
+
/** GET /widget/:slug/config → config pubblica (spento o attivo). */
|
|
70
|
+
export declare function fetchConfig(base: WidgetApiBase): Promise<WidgetConfig>;
|
|
71
|
+
/** POST /widget/:slug/conversations → apre/riprende una conversazione. */
|
|
72
|
+
export declare function createConversation(base: WidgetApiBase, user: WidgetUser): Promise<{
|
|
73
|
+
conversationId: string;
|
|
74
|
+
}>;
|
|
75
|
+
/** GET /widget/:slug/conversations/:id/messages?userId= → storico cronologico. */
|
|
76
|
+
export declare function fetchMessages(base: WidgetApiBase, conversationId: string, userId: string): Promise<{
|
|
77
|
+
messages: WidgetMessage[];
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* POST /widget/:slug/conversations/:id/messages → invia un messaggio utente.
|
|
81
|
+
* Ritorna la Response GREZZA per il consumo dello stream SSE (vedi
|
|
82
|
+
* {@link ./sse.ts#parseSseStream}); lancia {@link WidgetApiError} sugli errori
|
|
83
|
+
* PRIMA dello stream (401/404/429/503).
|
|
84
|
+
*/
|
|
85
|
+
export declare function sendMessage(base: WidgetApiBase, conversationId: string, input: {
|
|
86
|
+
content: string;
|
|
87
|
+
userId: string;
|
|
88
|
+
}, signal?: AbortSignal): Promise<Response>;
|
|
89
|
+
/** POST /widget/:slug/conversations/:id/tickets → conferma un ticket. */
|
|
90
|
+
export declare function confirmTicket(base: WidgetApiBase, conversationId: string, body: WidgetTicketConfirm): Promise<{
|
|
91
|
+
ticketId: string;
|
|
92
|
+
number: number;
|
|
93
|
+
}>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordinate del backend del widget, estratte dal DSN. Il widget è standalone:
|
|
3
|
+
* questa logica è adattata da `packages/sdk/src/core/transport.ts` (parseDsn) ma
|
|
4
|
+
* non importa nulla dall'SDK.
|
|
5
|
+
*/
|
|
6
|
+
export interface WidgetDsn {
|
|
7
|
+
/** Origine completa del backend (`https://host[:port][/prefix]`), senza `/p/slug`. */
|
|
8
|
+
origin: string;
|
|
9
|
+
/** Slug del progetto/widget. */
|
|
10
|
+
slug: string;
|
|
11
|
+
/** Chiave da inviare nell'header `X-Stubwise-Key`. */
|
|
12
|
+
key: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Estrae origine, slug e chiave da un DSN nel formato `https://KEY@host/p/slug`.
|
|
16
|
+
* Porta ed eventuale prefisso di path vengono preservati nell'`origin` (le route
|
|
17
|
+
* pubbliche vivono su `<origin>/widget/<slug>/...`).
|
|
18
|
+
*
|
|
19
|
+
* Unico punto del widget autorizzato a lanciare: un DSN malformato è un errore di
|
|
20
|
+
* configurazione e deve fallire forte all'init, non in silenzio a runtime. Il DSN
|
|
21
|
+
* grezzo non finisce mai nei messaggi (può contenere la chiave).
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseWidgetDsn(dsn: string): WidgetDsn;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser dello stream SSE della chat widget. Adatta il loop di lettura di
|
|
3
|
+
* `apps/web/src/components/docs-chat.tsx` (reader + TextDecoder stream:true +
|
|
4
|
+
* buffer tagliato sui separatori `\n\n`), ma tipizza gli eventi e li consegna a
|
|
5
|
+
* un callback invece di mutare lo stato React.
|
|
6
|
+
*
|
|
7
|
+
* Protocollo di trasporto lato server: ogni evento è una riga `data: {json}\n\n`
|
|
8
|
+
* (vedi `apps/server/src/routes/docs-chat-core.ts#writeSseEvent`).
|
|
9
|
+
*/
|
|
10
|
+
/** Proposta di ticket emessa dalla sentinel della chat (title/body/type). */
|
|
11
|
+
export interface WidgetTicketProposal {
|
|
12
|
+
title: string;
|
|
13
|
+
body: string;
|
|
14
|
+
type: "bug" | "feedback" | "feature";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Citazione di una fonte Docs allegata al `done`. Forma OPACA lato widget:
|
|
18
|
+
* il contratto del server la tipizza come `unknown`/jsonb (vedi
|
|
19
|
+
* `buildCitations` → `{slug,title,kind,repositoryId,...}`), quindi la
|
|
20
|
+
* modelliamo come record generico e leggiamo solo `title` alla renderizzazione.
|
|
21
|
+
* UNICO tipo citazione del widget: `api.ts` lo riusa per lo storico messaggi.
|
|
22
|
+
*/
|
|
23
|
+
export type WidgetCitation = Record<string, unknown>;
|
|
24
|
+
/** Eventi tipizzati che lo stream della chat widget può emettere. */
|
|
25
|
+
export type WidgetSseEvent = {
|
|
26
|
+
type: "delta";
|
|
27
|
+
text: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: "ticket_proposal";
|
|
30
|
+
proposal: WidgetTicketProposal;
|
|
31
|
+
} | {
|
|
32
|
+
type: "done";
|
|
33
|
+
conversationId: string;
|
|
34
|
+
citations: WidgetCitation[];
|
|
35
|
+
} | {
|
|
36
|
+
type: "error";
|
|
37
|
+
message?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Legge lo stream SSE di `response` fino alla fine, invocando `onEvent` per ogni
|
|
41
|
+
* evento riconosciuto. Si risolve alla chiusura dello stream (anche senza un
|
|
42
|
+
* `done` esplicito, es. troncamento). Se `response.body` è assente si risolve
|
|
43
|
+
* subito senza emettere nulla.
|
|
44
|
+
*
|
|
45
|
+
* Buffering: i chunk di rete possono spezzare un evento in un punto qualsiasi
|
|
46
|
+
* (anche a metà di `data: ` o del JSON), quindi si accumula in un buffer e si
|
|
47
|
+
* tagliano solo gli eventi COMPLETI (delimitati da `\n\n`); il resto resta in
|
|
48
|
+
* buffer per il chunk successivo.
|
|
49
|
+
*
|
|
50
|
+
* Robustezza: se `onEvent` LANCIA (bug del consumer) si cancella il reader nel
|
|
51
|
+
* `finally` per non lasciare la connessione appesa, poi si ri-propaga. Un buffer
|
|
52
|
+
* che supera {@link MAX_BUFFER_BYTES} senza mai chiudere un evento fa emettere un
|
|
53
|
+
* `error` sintetico e interrompe il parse (guardia contro stream ostili).
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseSseStream(response: Response, onEvent: (event: WidgetSseEvent) => void): Promise<void>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistenza dell'id di conversazione del widget su localStorage, per slug.
|
|
3
|
+
* Il client riaggancia lo storico al reload passando questo id.
|
|
4
|
+
*
|
|
5
|
+
* localStorage può LANCIARE (Safari private mode, cookie di terze parti bloccati,
|
|
6
|
+
* quota): il widget è embeddato nei siti dei clienti e non deve mai romperli, quindi
|
|
7
|
+
* ogni accesso è protetto — il getter degrada a `null`, setter e clear a no-op.
|
|
8
|
+
*/
|
|
9
|
+
/** Id di conversazione salvato per lo slug, o null (assente o storage non accessibile). */
|
|
10
|
+
export declare function getConversationId(slug: string): string | null;
|
|
11
|
+
/** Salva l'id di conversazione per lo slug. No-op se lo storage non è accessibile. */
|
|
12
|
+
export declare function setConversationId(slug: string, id: string): void;
|
|
13
|
+
/** Rimuove l'id salvato per lo slug. No-op se lo storage non è accessibile. */
|
|
14
|
+
export declare function clearConversationId(slug: string): void;
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dizionario piatto delle stringhe UI del widget, in italiano e inglese. La
|
|
3
|
+
* lingua è scelta da `config.language` (impostata dal progetto lato server): il
|
|
4
|
+
* widget non fa detection dal browser, così l'esperienza è coerente col resto
|
|
5
|
+
* del prodotto ospite.
|
|
6
|
+
*
|
|
7
|
+
* Volutamente minimale (nessun ICU / plurali / interpolazione): le poche
|
|
8
|
+
* stringhe dinamiche (numero ticket, titolo fonte) sono composte a mano nel
|
|
9
|
+
* punto d'uso. Niente dipendenze da `@stubwise/i18n` (il widget è standalone).
|
|
10
|
+
*/
|
|
11
|
+
/** Chiavi delle stringhe UI (stesse per ogni lingua). */
|
|
12
|
+
export interface WidgetStrings {
|
|
13
|
+
/** Messaggio di benvenuto di fallback se il server non ne fornisce uno. */
|
|
14
|
+
welcomeFallback: string;
|
|
15
|
+
/** Placeholder del campo di composizione. */
|
|
16
|
+
composerPlaceholder: string;
|
|
17
|
+
/** Label del bottone d'invio (aria-label, il bottone mostra un'icona). */
|
|
18
|
+
send: string;
|
|
19
|
+
/** aria-label della bolla/bottone di chiusura. */
|
|
20
|
+
openLabel: string;
|
|
21
|
+
closeLabel: string;
|
|
22
|
+
/** Nota sotto il titolo del pannello. */
|
|
23
|
+
assistantNote: string;
|
|
24
|
+
/** Messaggio di cortesia su errore generico dello stream/HTTP. */
|
|
25
|
+
errorGeneric: string;
|
|
26
|
+
/** Messaggio dedicato al cap giornaliero della chat (429). */
|
|
27
|
+
errorCapReached: string;
|
|
28
|
+
/** Prefisso di una citazione ("fonte: <title>"). */
|
|
29
|
+
sourcePrefix: string;
|
|
30
|
+
/** Nota quando la chat è disabilitata (storico leggibile, composer spento). */
|
|
31
|
+
chatDisabledNote: string;
|
|
32
|
+
/** Card ticket. */
|
|
33
|
+
ticketBug: string;
|
|
34
|
+
ticketFeedback: string;
|
|
35
|
+
ticketFeature: string;
|
|
36
|
+
ticketTitlePlaceholder: string;
|
|
37
|
+
ticketBodyPlaceholder: string;
|
|
38
|
+
ticketSubmit: string;
|
|
39
|
+
ticketCancel: string;
|
|
40
|
+
/** Prefisso della conferma ticket ("✓ inviata" + numero composto a mano). */
|
|
41
|
+
ticketSent: string;
|
|
42
|
+
ticketError: string;
|
|
43
|
+
ticketRetry: string;
|
|
44
|
+
}
|
|
45
|
+
/** Restituisce il dizionario per la lingua data (default `it` per input ignoti). */
|
|
46
|
+
export declare function getStrings(language: "it" | "en"): WidgetStrings;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type WidgetUser } from "./core/api.js";
|
|
2
|
+
export * from "./core/dsn.js";
|
|
3
|
+
export * from "./core/api.js";
|
|
4
|
+
export * from "./core/sse.js";
|
|
5
|
+
export * from "./core/storage.js";
|
|
6
|
+
/** Opzioni di init: DSN del progetto + identità DICHIARATA dell'utente. */
|
|
7
|
+
export interface InitWidgetOptions {
|
|
8
|
+
dsn: string;
|
|
9
|
+
user: WidgetUser;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Reset del flag di init. USO SOLO NEI TEST: isola i casi che chiamano
|
|
13
|
+
* `initWidget` (la guardia è un modulo-singleton, altrimenti persisterebbe tra
|
|
14
|
+
* test). Non fa teardown del DOM: quello lo fa il test.
|
|
15
|
+
*/
|
|
16
|
+
export declare function __resetWidgetForTests(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Esegue `cb` quando `document.body` è disponibile. Se il body c'è già (script
|
|
19
|
+
* con `defer` o in fondo al `<body>`) chiama subito; altrimenti (script nel
|
|
20
|
+
* `<head>` senza `defer`) rimanda a `DOMContentLoaded` una sola volta. Estratta
|
|
21
|
+
* per essere testabile in isolamento (simulare `document.body` null in happy-dom
|
|
22
|
+
* non è praticabile).
|
|
23
|
+
*/
|
|
24
|
+
export declare function whenBodyReady(cb: () => void): void;
|
|
25
|
+
/**
|
|
26
|
+
* Inizializza il widget. Robusto per costruzione: NON lancia MAI (è embeddato in
|
|
27
|
+
* siti terzi e non deve mai romperli). Su qualunque errore — DSN malformato,
|
|
28
|
+
* fetch della config fallito, `enabled: false` — non monta nulla e al più logga
|
|
29
|
+
* un `console.warn`. Doppia chiamata → no-op (guardia `initStarted`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function initWidget(options: InitWidgetOptions): Promise<void>;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
var Stubwise=(function(P){"use strict";const rt=/^(?<prefix>(?:\/[^/]+)*)\/p\/(?<slug>[^/]+)\/?$/;function he(t){let e;try{e=new URL(t)}catch{throw new Error("[stubwise] DSN malformato: la stringa fornita non è un URL valido (atteso https://KEY@host/p/slug)")}if(e.protocol!=="https:"&&e.protocol!=="http:")throw new Error(`[stubwise] DSN malformato: protocollo "${e.protocol}" non supportato`);const n=decodeURIComponent(e.username);if(!n)throw new Error("[stubwise] DSN malformato: chiave mancante (atteso https://KEY@host/p/slug)");const r=rt.exec(e.pathname),s=r?.groups?.slug;if(!s)throw new Error(`[stubwise] DSN malformato: path "${e.pathname}" non corrisponde a /p/<slug>`);const o=r?.groups?.prefix??"";return{origin:`${e.protocol}//${e.host}${o}`,slug:s,key:n}}class j extends Error{status;code;constructor(e,n,r){super(r??n??`HTTP ${e}`),this.name="WidgetApiError",this.status=e,this.code=n}}function L(t,e){return`${t.origin}/widget/${t.slug}${e}`}function U(t,e){const n={"X-Stubwise-Key":t.key};return e&&(n["content-type"]="application/json"),n}async function M(t){let e,n;try{const r=await t.json();e=r.code,n=r.message}catch{}throw new j(t.status,e,n)}async function me(t){const e=await fetch(L(t,"/config"),{method:"GET",headers:U(t,!1)});return e.ok||await M(e),await e.json()}async function ge(t,e){const n=await fetch(L(t,"/conversations"),{method:"POST",headers:U(t,!0),body:JSON.stringify({user:e})});return n.ok||await M(n),await n.json()}async function ve(t,e,n){const r=new URLSearchParams({userId:n}),s=await fetch(L(t,`/conversations/${e}/messages?${r.toString()}`),{method:"GET",headers:U(t,!1)});return s.ok||await M(s),await s.json()}async function we(t,e,n,r){const s=await fetch(L(t,`/conversations/${e}/messages`),{method:"POST",headers:U(t,!0),body:JSON.stringify(n),signal:r});return s.ok||await M(s),s}async function be(t,e,n){const r=await fetch(L(t,`/conversations/${e}/tickets`),{method:"POST",headers:U(t,!0),body:JSON.stringify(n)});return r.ok||await M(r),await r.json()}var q,v,ye,H,xe,ke,Se,te,G,W,Ce,ne,re,oe,K={},J=[],ot=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,Y=Array.isArray;function A(t,e){for(var n in e)t[n]=e[n];return t}function ie(t){t&&t.parentNode&&t.parentNode.removeChild(t)}function it(t,e,n){var r,s,o,a={};for(o in e)o=="key"?r=e[o]:o=="ref"?s=e[o]:a[o]=e[o];if(arguments.length>2&&(a.children=arguments.length>3?q.call(arguments,2):n),typeof t=="function"&&t.defaultProps!=null)for(o in t.defaultProps)a[o]===void 0&&(a[o]=t.defaultProps[o]);return V(t,a,r,s,null)}function V(t,e,n,r,s){var o={type:t,props:e,key:n,ref:r,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:s??++ye,__i:-1,__u:0};return s==null&&v.vnode!=null&&v.vnode(o),o}function B(t){return t.children}function X(t,e){this.props=t,this.context=e}function z(t,e){if(e==null)return t.__?z(t.__,t.__i+1):null;for(var n;e<t.__k.length;e++)if((n=t.__k[e])!=null&&n.__e!=null)return n.__e;return typeof t.type=="function"?z(t):null}function st(t){if(t.__P&&t.__d){var e=t.__v,n=e.__e,r=[],s=[],o=A({},e);o.__v=e.__v+1,v.vnode&&v.vnode(o),se(t.__P,o,e,t.__n,t.__P.namespaceURI,32&e.__u?[n]:null,r,n??z(e),!!(32&e.__u),s),o.__v=e.__v,o.__.__k[o.__i]=o,De(r,o,s),e.__e=e.__=null,o.__e!=n&&$e(o)}}function $e(t){if((t=t.__)!=null&&t.__c!=null)return t.__e=t.__c.base=null,t.__k.some(function(e){if(e!=null&&e.__e!=null)return t.__e=t.__c.base=e.__e}),$e(t)}function Te(t){(!t.__d&&(t.__d=!0)&&H.push(t)&&!Q.__r++||xe!=v.debounceRendering)&&((xe=v.debounceRendering)||ke)(Q)}function Q(){try{for(var t,e=1;H.length;)H.length>e&&H.sort(Se),t=H.shift(),e=H.length,st(t)}finally{H.length=Q.__r=0}}function Pe(t,e,n,r,s,o,a,l,d,c,f){var g,i,_,p,T,w,x,m=r&&r.__k||J,u=e.length;for(d=at(n,e,m,d,u),g=0;g<u;g++)(_=n.__k[g])!=null&&(i=_.__i!=-1&&m[_.__i]||K,_.__i=g,w=se(t,_,i,s,o,a,l,d,c,f),p=_.__e,_.ref&&i.ref!=_.ref&&(i.ref&&ae(i.ref,null,_),f.push(_.ref,_.__c||p,_)),T==null&&p!=null&&(T=p),(x=!!(4&_.__u))||i.__k===_.__k?(d=Ie(_,d,t,x),x&&i.__e&&(i.__e=null)):typeof _.type=="function"&&w!==void 0?d=w:p&&(d=p.nextSibling),_.__u&=-7);return n.__e=T,d}function at(t,e,n,r,s){var o,a,l,d,c,f=n.length,g=f,i=0;for(t.__k=new Array(s),o=0;o<s;o++)(a=e[o])!=null&&typeof a!="boolean"&&typeof a!="function"?(typeof a=="string"||typeof a=="number"||typeof a=="bigint"||a.constructor==String?a=t.__k[o]=V(null,a,null,null,null):Y(a)?a=t.__k[o]=V(B,{children:a},null,null,null):a.constructor===void 0&&a.__b>0?a=t.__k[o]=V(a.type,a.props,a.key,a.ref?a.ref:null,a.__v):t.__k[o]=a,d=o+i,a.__=t,a.__b=t.__b+1,l=null,(c=a.__i=ct(a,n,d,g))!=-1&&(g--,(l=n[c])&&(l.__u|=2)),l==null||l.__v==null?(c==-1&&(s>f?i--:s<f&&i++),typeof a.type!="function"&&(a.__u|=4)):c!=d&&(c==d-1?i--:c==d+1?i++:(c>d?i--:i++,a.__u|=4))):t.__k[o]=null;if(g)for(o=0;o<f;o++)(l=n[o])!=null&&(2&l.__u)==0&&(l.__e==r&&(r=z(l)),He(l,l));return r}function Ie(t,e,n,r){var s,o;if(typeof t.type=="function"){for(s=t.__k,o=0;s&&o<s.length;o++)s[o]&&(s[o].__=t,e=Ie(s[o],e,n,r));return e}t.__e!=e&&(r&&(e&&t.type&&!e.parentNode&&(e=z(t)),n.insertBefore(t.__e,e||null)),e=t.__e);do e=e&&e.nextSibling;while(e!=null&&e.nodeType==8);return e}function ct(t,e,n,r){var s,o,a,l=t.key,d=t.type,c=e[n],f=c!=null&&(2&c.__u)==0;if(c===null&&l==null||f&&l==c.key&&d==c.type)return n;if(r>(f?1:0)){for(s=n-1,o=n+1;s>=0||o<e.length;)if((c=e[a=s>=0?s--:o++])!=null&&(2&c.__u)==0&&l==c.key&&d==c.type)return a}return-1}function Ee(t,e,n){e[0]=="-"?t.setProperty(e,n??""):t[e]=n==null?"":typeof n!="number"||ot.test(e)?n:n+"px"}function Z(t,e,n,r,s){var o,a;e:if(e=="style")if(typeof n=="string")t.style.cssText=n;else{if(typeof r=="string"&&(t.style.cssText=r=""),r)for(e in r)n&&e in n||Ee(t.style,e,"");if(n)for(e in n)r&&n[e]==r[e]||Ee(t.style,e,n[e])}else if(e[0]=="o"&&e[1]=="n")o=e!=(e=e.replace(Ce,"$1")),a=e.toLowerCase(),e=a in t||e=="onFocusOut"||e=="onFocusIn"?a.slice(2):e.slice(2),t.l||(t.l={}),t.l[e+o]=n,n?r?n[W]=r[W]:(n[W]=ne,t.addEventListener(e,o?oe:re,o)):t.removeEventListener(e,o?oe:re,o);else{if(s=="http://www.w3.org/2000/svg")e=e.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(e!="width"&&e!="height"&&e!="href"&&e!="list"&&e!="form"&&e!="tabIndex"&&e!="download"&&e!="rowSpan"&&e!="colSpan"&&e!="role"&&e!="popover"&&e in t)try{t[e]=n??"";break e}catch{}typeof n=="function"||(n==null||n===!1&&e[4]!="-"?t.removeAttribute(e):t.setAttribute(e,e=="popover"&&n==1?"":n))}}function Ne(t){return function(e){if(this.l){var n=this.l[e.type+t];if(e[G]==null)e[G]=ne++;else if(e[G]<n[W])return;return n(v.event?v.event(e):e)}}}function se(t,e,n,r,s,o,a,l,d,c){var f,g,i,_,p,T,w,x,m,u,$,C,k,N,I,R,b=e.type;if(e.constructor!==void 0)return null;128&n.__u&&(d=!!(32&n.__u),o=[l=e.__e=n.__e]),(f=v.__b)&&f(e);e:if(typeof b=="function"){g=a.length;try{if(m=e.props,u=b.prototype&&b.prototype.render,$=(f=b.contextType)&&r[f.__c],C=f?$?$.props.value:f.__:r,n.__c?x=(i=e.__c=n.__c).__=i.__E:(u?e.__c=i=new b(m,C):(e.__c=i=new X(m,C),i.constructor=b,i.render=_t),$&&$.sub(i),i.state||(i.state={}),i.__n=r,_=i.__d=!0,i.__h=[],i._sb=[]),u&&i.__s==null&&(i.__s=i.state),u&&b.getDerivedStateFromProps!=null&&(i.__s==i.state&&(i.__s=A({},i.__s)),A(i.__s,b.getDerivedStateFromProps(m,i.__s))),p=i.props,T=i.state,i.__v=e,_)u&&b.getDerivedStateFromProps==null&&i.componentWillMount!=null&&i.componentWillMount(),u&&i.componentDidMount!=null&&i.__h.push(i.componentDidMount);else{if(u&&b.getDerivedStateFromProps==null&&m!==p&&i.componentWillReceiveProps!=null&&i.componentWillReceiveProps(m,C),e.__v==n.__v||!i.__e&&i.shouldComponentUpdate!=null&&i.shouldComponentUpdate(m,i.__s,C)===!1){e.__v!=n.__v&&(i.props=m,i.state=i.__s,i.__d=!1),e.__e=n.__e,e.__k=n.__k,e.__k.some(function(E){E&&(E.__=e)}),J.push.apply(i.__h,i._sb),i._sb=[],i.__h.length&&a.push(i);break e}i.componentWillUpdate!=null&&i.componentWillUpdate(m,i.__s,C),u&&i.componentDidUpdate!=null&&i.__h.push(function(){i.componentDidUpdate(p,T,w)})}if(i.context=C,i.props=m,i.__P=t,i.__e=!1,k=v.__r,N=0,u)i.state=i.__s,i.__d=!1,k&&k(e),f=i.render(i.props,i.state,i.context),J.push.apply(i.__h,i._sb),i._sb=[];else do i.__d=!1,k&&k(e),f=i.render(i.props,i.state,i.context),i.state=i.__s;while(i.__d&&++N<25);i.state=i.__s,i.getChildContext!=null&&(r=A(A({},r),i.getChildContext())),u&&!_&&i.getSnapshotBeforeUpdate!=null&&(w=i.getSnapshotBeforeUpdate(p,T)),I=f!=null&&f.type===B&&f.key==null?Re(f.props.children):f,l=Pe(t,Y(I)?I:[I],e,n,r,s,o,a,l,d,c),i.base=e.__e,e.__u&=-161,i.__h.length&&a.push(i),x&&(i.__E=i.__=null)}catch(E){if(a.length=g,e.__v=null,d||o!=null){if(E.then){for(e.__u|=d?160:128;l&&l.nodeType==8&&l.nextSibling;)l=l.nextSibling;o!=null&&(o[o.indexOf(l)]=null),e.__e=l}else if(o!=null)for(R=o.length;R--;)ie(o[R])}else e.__e=n.__e;e.__k==null&&(e.__k=n.__k||[]),E.then||Ae(e),v.__e(E,e,n)}}else o==null&&e.__v==n.__v?(e.__k=n.__k,e.__e=n.__e):l=e.__e=lt(n.__e,e,n,r,s,o,a,d,c);return(f=v.diffed)&&f(e),128&e.__u?void 0:l}function Ae(t){t&&(t.__c&&(t.__c.__e=!0),t.__k&&t.__k.some(Ae))}function De(t,e,n){for(var r=0;r<n.length;r++)ae(n[r],n[++r],n[++r]);v.__c&&v.__c(e,t),t.some(function(s){try{t=s.__h,s.__h=[],t.some(function(o){o.call(s)})}catch(o){v.__e(o,s.__v)}})}function Re(t){return typeof t!="object"||t==null||t.__b>0?t:Y(t)?t.map(Re):t.constructor!==void 0?null:A({},t)}function lt(t,e,n,r,s,o,a,l,d){var c,f,g,i,_,p,T,w=n.props||K,x=e.props,m=e.type;if(m=="svg"?s="http://www.w3.org/2000/svg":m=="math"?s="http://www.w3.org/1998/Math/MathML":s||(s="http://www.w3.org/1999/xhtml"),o!=null){for(c=0;c<o.length;c++)if((_=o[c])&&"setAttribute"in _==!!m&&(m?_.localName==m:_.nodeType==3)){t=_,o[c]=null;break}}if(t==null){if(m==null)return document.createTextNode(x);t=document.createElementNS(s,m,x.is&&x),l&&(v.__m&&v.__m(e,o),l=!1),o=null}if(m==null)w===x||l&&t.data==x||(t.data=x);else{if(o=m=="textarea"&&x.defaultValue!=null?null:o&&q.call(t.childNodes),!l&&o!=null)for(w={},c=0;c<t.attributes.length;c++)w[(_=t.attributes[c]).name]=_.value;for(c in w)_=w[c],c=="dangerouslySetInnerHTML"?g=_:c=="children"||c in x||c=="value"&&"defaultValue"in x||c=="checked"&&"defaultChecked"in x||Z(t,c,null,_,s);for(c in x)_=x[c],c=="children"?i=_:c=="dangerouslySetInnerHTML"?f=_:c=="value"?p=_:c=="checked"?T=_:l&&typeof _!="function"||w[c]===_||Z(t,c,_,w[c],s);if(f)l||g&&(f.__html==g.__html||f.__html==t.innerHTML)||(t.innerHTML=f.__html),e.__k=[];else if(g&&(t.innerHTML=""),Pe(e.type=="template"?t.content:t,Y(i)?i:[i],e,n,r,m=="foreignObject"?"http://www.w3.org/1999/xhtml":s,o,a,o?o[0]:n.__k&&z(n,0),l,d),o!=null)for(c=o.length;c--;)ie(o[c]);l&&m!="textarea"||(c="value",m=="progress"&&p==null?t.removeAttribute("value"):p!=null&&(p!==t[c]||m=="progress"&&!p||m=="option"&&p!=w[c])&&Z(t,c,p,w[c],s),c="checked",T!=null&&T!=t[c]&&Z(t,c,T,w[c],s))}return t}function ae(t,e,n){try{if(typeof t=="function"){var r=typeof t.__u=="function";r&&t.__u(),r&&e==null||(t.__u=t(e))}else t.current=e}catch(s){v.__e(s,n)}}function He(t,e,n){var r,s;if(v.unmount&&v.unmount(t),(r=t.ref)&&(r.current&&r.current!=t.__e||ae(r,null,e)),(r=t.__c)!=null){if(r.componentWillUnmount)try{r.componentWillUnmount()}catch(o){v.__e(o,e)}r.base=r.__P=r.__n=null}if(r=t.__k)for(s=0;s<r.length;s++)r[s]&&He(r[s],e,n||typeof t.type!="function");n||ie(t.__e),t.__c=t.__=t.__e=void 0}function _t(t,e,n){return this.constructor(t,n)}function dt(t,e,n){var r,s,o,a;e==document&&(e=document.documentElement),v.__&&v.__(t,e),s=(r=!1)?null:e.__k,o=[],a=[],se(e,t=e.__k=it(B,null,[t]),s||K,K,e.namespaceURI,s?null:e.firstChild?q.call(e.childNodes):null,o,s?s.__e:e.firstChild,r,a),De(o,t,a),t.props.children=null}q=J.slice,v={__e:function(t,e,n,r){for(var s,o,a;e=e.__;)if((s=e.__c)&&!s.__)try{if((o=s.constructor)&&o.getDerivedStateFromError!=null&&(s.setState(o.getDerivedStateFromError(t)),a=s.__d),s.componentDidCatch!=null&&(s.componentDidCatch(t,r||{}),a=s.__d),a)return s.__E=s}catch(l){t=l}throw t}},ye=0,X.prototype.setState=function(t,e){var n;n=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=A({},this.state),typeof t=="function"&&(t=t(A({},n),this.props)),t&&A(n,t),t!=null&&this.__v&&(e&&this._sb.push(e),Te(this))},X.prototype.forceUpdate=function(t){this.__v&&(this.__e=!0,t&&this.__h.push(t),Te(this))},X.prototype.render=B,H=[],ke=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,Se=function(t,e){return t.__v.__b-e.__v.__b},Q.__r=0,te=Math.random().toString(8),G="__d"+te,W="__a"+te,Ce=/(PointerCapture)$|Capture$/i,ne=0,re=Ne(!1),oe=Ne(!0);var ut=0;function h(t,e,n,r,s,o){e||(e={});var a,l,d=e;if("ref"in d)for(l in d={},e)l=="ref"?a=e[l]:d[l]=e[l];var c={type:t,props:d,key:n,ref:a,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--ut,__i:-1,__u:0,__source:s,__self:o};if(typeof t=="function"&&(a=t.defaultProps))for(l in a)d[l]===void 0&&(d[l]=a[l]);return v.vnode&&v.vnode(c),c}var O,y,ce,Fe,ee=0,ze=[],S=v,Le=S.__b,Ue=S.__r,Me=S.diffed,We=S.__c,Be=S.unmount,Oe=S.__;function le(t,e){S.__h&&S.__h(y,t,ee||e),ee=0;var n=y.__H||(y.__H={__:[],__h:[]});return t>=n.__.length&&n.__.push({}),n.__[t]}function D(t){return ee=1,ft(Je,t)}function ft(t,e,n){var r=le(O++,2);if(r.t=t,!r.__c&&(r.__=[Je(void 0,e),function(l){var d=r.__N?r.__N[0]:r.__[0],c=r.t(d,l);d!==c&&(r.__N=[c,r.__[1]],r.__c.setState({}))}],r.__c=y,!y.__f)){var s=function(l,d,c){if(!r.__c.__H)return!0;var f=!1,g=r.__c.props!==l;if(r.__c.__H.__.some(function(_){if(_.__N){f=!0;var p=_.__[0];_.__=_.__N,_.__N=void 0,p!==_.__[0]&&(g=!0)}}),o){var i=o.call(this,l,d,c);return f?i||g:i}return!f||g};y.__f=!0;var o=y.shouldComponentUpdate,a=y.componentWillUpdate;y.componentWillUpdate=function(l,d,c){if(this.__e){var f=o;o=void 0,s(l,d,c),o=f}a&&a.call(this,l,d,c)},y.shouldComponentUpdate=s}return r.__N||r.__}function _e(t,e){var n=le(O++,3);!S.__s&&Ke(n.__H,e)&&(n.__=t,n.u=e,y.__H.__h.push(n))}function de(t){return ee=5,pt(function(){return{current:t}},[])}function pt(t,e){var n=le(O++,7);return Ke(n.__H,e)&&(n.__=t(),n.__H=e,n.__h=t),n.__}function je(){for(var t;t=ze.shift();){var e=t.__H;if(t.__P&&e)try{e.__h.some(ue),e.__h.some(Ge),e.__h=[]}catch(n){e.__h=[],S.__e(n,t.__v)}}}S.__b=function(t){y=null,Le&&Le(t)},S.__=function(t,e){t&&e.__k&&e.__k.__m&&(t.__m=e.__k.__m),Oe&&Oe(t,e)},S.__r=function(t){Ue&&Ue(t),O=0;var e=(y=t.__c).__H;e&&(ce===y?(e.__h=[],y.__h=[],e.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(e.__h.length&&je(),O=0)),ce=y},S.diffed=function(t){Me&&Me(t);var e=t.__c;e&&e.__H&&(e.__H.__h.length&&(ze.push(e)!==1&&Fe===S.requestAnimationFrame||((Fe=S.requestAnimationFrame)||ht)(je)),e.__H.__.some(function(n){n.u&&(n.__H=n.u,n.u=void 0)})),ce=y=null},S.__c=function(t,e){e.some(function(n){try{n.__h.some(ue),n.__h=n.__h.filter(function(r){return!r.__||Ge(r)})}catch(r){e.some(function(s){s.__h&&(s.__h=[])}),e=[],S.__e(r,n.__v)}}),We&&We(t,e)},S.unmount=function(t){Be&&Be(t);var e,n=t.__c;n&&n.__H&&(n.__H.__.some(function(r){try{ue(r)}catch(s){e=s}}),n.__H=void 0,e&&S.__e(e,n.__v))};var qe=typeof requestAnimationFrame=="function";function ht(t){var e,n=function(){clearTimeout(r),qe&&cancelAnimationFrame(e),setTimeout(t)},r=setTimeout(n,35);qe&&(e=requestAnimationFrame(n))}function ue(t){var e=y,n=t.__c;typeof n=="function"&&(t.__c=void 0,n()),y=e}function Ge(t){var e=y;t.__c=t.__(),y=e}function Ke(t,e){return!t||t.length!==e.length||e.some(function(n,r){return n!==t[r]})}function Je(t,e){return typeof e=="function"?e(t):e}function fe(t){return`stubwise-widget:${t}:conversation`}function Ye(t){try{return localStorage.getItem(fe(t))}catch{return null}}function Ve(t,e){try{localStorage.setItem(fe(t),e)}catch{}}function Xe(t){try{localStorage.removeItem(fe(t))}catch{}}const Qe={it:{welcomeFallback:"Ciao! Come posso aiutarti?",composerPlaceholder:"Scrivi un messaggio…",send:"Invia",openLabel:"Apri la chat di assistenza",closeLabel:"Chiudi la chat",assistantNote:"Risponde l'assistente AI",errorGeneric:"Si è verificato un errore. Riprova tra poco.",errorCapReached:"L'assistente non è disponibile oggi. Puoi comunque inviare una segnalazione descrivendo il problema.",sourcePrefix:"fonte:",chatDisabledNote:"La chat non è al momento disponibile.",ticketBug:"bug",ticketFeedback:"feedback",ticketFeature:"richiesta",ticketTitlePlaceholder:"Titolo",ticketBodyPlaceholder:"Descrizione",ticketSubmit:"Invia segnalazione",ticketCancel:"Annulla",ticketSent:"Segnalazione inviata",ticketError:"Invio non riuscito.",ticketRetry:"Riprova"},en:{welcomeFallback:"Hi! How can I help you?",composerPlaceholder:"Type a message…",send:"Send",openLabel:"Open support chat",closeLabel:"Close chat",assistantNote:"You are chatting with the AI assistant",errorGeneric:"Something went wrong. Please try again shortly.",errorCapReached:"The assistant is unavailable today. You can still send a report describing the problem.",sourcePrefix:"source:",chatDisabledNote:"Chat is currently unavailable.",ticketBug:"bug",ticketFeedback:"feedback",ticketFeature:"request",ticketTitlePlaceholder:"Title",ticketBodyPlaceholder:"Description",ticketSubmit:"Send report",ticketCancel:"Cancel",ticketSent:"Report sent",ticketError:"Sending failed.",ticketRetry:"Retry"}};function mt(t){return Qe[t]??Qe.it}const gt=1e6;function vt(t){if(typeof t!="object"||t===null)return null;const e=t;switch(e.type){case"delta":return typeof e.text=="string"?{type:"delta",text:e.text}:null;case"ticket_proposal":{const n=e.proposal;if(typeof n!="object"||n===null)return null;const r=n;return typeof r.title!="string"||typeof r.body!="string"||r.type!=="bug"&&r.type!=="feedback"&&r.type!=="feature"?null:{type:"ticket_proposal",proposal:{title:r.title,body:r.body,type:r.type}}}case"done":return{type:"done",conversationId:typeof e.conversationId=="string"?e.conversationId:"",citations:Array.isArray(e.citations)?e.citations:[]};case"error":return{type:"error",...typeof e.message=="string"?{message:e.message}:{}};default:return null}}async function Ze(t,e){const n=t.body;if(!n)return;const r=n.getReader(),s=new TextDecoder;let o="";try{for(;;){const{done:a,value:l}=await r.read();if(a)break;if(o+=s.decode(l,{stream:!0}),o.length>gt){e({type:"error",message:"stream buffer overflow"});break}let d;for(;(d=o.indexOf(`
|
|
2
|
+
|
|
3
|
+
`))!==-1;){const c=o.slice(0,d);o=o.slice(d+2);const f=c.trim();if(!f.startsWith("data:"))continue;const g=f.slice(5).trim();if(!g)continue;let i;try{i=JSON.parse(g)}catch{continue}const _=vt(i);_&&e(_)}}}finally{await r.cancel().catch(()=>{})}}function wt(t,e){return t==="bug"?e.ticketBug:t==="feedback"?e.ticketFeedback:e.ticketFeature}function bt({proposal:t,strings:e,onConfirm:n,onCancel:r}){const[s,o]=D(t.title),[a,l]=D(t.body),[d,c]=D("form"),[f,g]=D(null),[i,_]=D(!1);if(d==="confirmed")return h("div",{class:"sw-card",children:h("div",{class:"sw-card-confirmed",children:["✓ ",e.ticketSent,f!==null?` #${f}`:""]})});const p=d==="sending";async function T(){c("sending"),_(!1);try{const{number:w}=await n({title:s,body:a});g(w),c("confirmed")}catch{_(!0),c("form")}}return h("div",{class:"sw-card",children:[h("span",{class:"sw-badge",children:wt(t.type,e)}),h("input",{class:"sw-input",value:s,placeholder:e.ticketTitlePlaceholder,disabled:p,onInput:w=>o(w.target.value)}),h("textarea",{class:"sw-textarea",value:a,placeholder:e.ticketBodyPlaceholder,disabled:p,onInput:w=>l(w.target.value)}),i?h("div",{class:"sw-card-msg",children:e.ticketError}):null,h("div",{class:"sw-card-actions",children:[h("button",{class:"sw-btn",disabled:p,onClick:T,children:i?e.ticketRetry:e.ticketSubmit}),h("button",{class:"sw-btn sw-btn-secondary",disabled:p,onClick:r,children:e.ticketCancel})]})]})}function yt(t){const e=t.title;return typeof e=="string"?e:null}let et=0;function F(){return et+=1,`m${et}`}function xt({base:t,user:e,strings:n,welcomeMessage:r,chatEnabled:s,initialConversationId:o}){const[a,l]=D([]),[d,c]=D(""),[f,g]=D(!1),i=de(o),_=de(null),p=de(null);_e(()=>()=>{p.current?.abort()},[]),_e(()=>{let u=!1;async function $(){const C=i.current;if(!C){l([{kind:"assistant",id:F(),text:r,citations:[]}]);return}try{const{messages:k}=await ve(t,C,e.id);if(u)return;if(k.length===0){l([{kind:"assistant",id:F(),text:r,citations:[]}]);return}l(k.map(N=>N.role==="user"?{kind:"user",id:N.id,text:N.content}:{kind:"assistant",id:N.id,text:N.content,citations:Array.isArray(N.citations)?N.citations:[]}))}catch(k){if(u)return;k instanceof j&&k.status===404?(i.current=null,Xe(t.slug),l([{kind:"assistant",id:F(),text:r,citations:[]}])):l([{kind:"assistant",id:F(),text:r,citations:[]}])}}return $(),()=>{u=!0}},[]),_e(()=>{const u=_.current;u&&(u.scrollTop=u.scrollHeight)},[a]);async function T(){if(i.current)return i.current;const{conversationId:u}=await ge(t,e);return i.current=u,Ve(t.slug,u),u}async function w(){const u=d.trim();if(!u||f)return;c(""),g(!0);const $={kind:"user",id:F(),text:u},C=F();l(I=>[...I,$,{kind:"assistant",id:C,text:"",citations:[]}]);function k(I){l(R=>R.map(b=>b.kind==="assistant"&&b.id===C?I(b):b))}const N=new AbortController;p.current=N;try{const I=await T(),R=await we(t,I,{content:u,userId:e.id},N.signal);await Ze(R,b=>{b.type==="delta"?k(E=>({...E,text:E.text+b.text})):b.type==="ticket_proposal"?l(E=>[...E,{kind:"ticket",id:F(),proposal:b.proposal}]):b.type==="done"?k(E=>({...E,citations:b.citations})):b.type==="error"&&k(E=>({...E,text:E.text||n.errorGeneric}))})}catch(I){if(I instanceof DOMException&&I.name==="AbortError")return;const R=I instanceof j&&(I.status===429||I.code==="widget_chat_cap_reached");k(b=>({...b,text:R?n.errorCapReached:n.errorGeneric}))}finally{p.current===N&&(p.current=null),g(!1)}}async function x(u,$){const C=await T(),{number:k}=await be(t,C,{title:$.title,body:$.body,type:u.type,userId:e.id});return{number:k}}function m(u){l($=>$.filter(C=>C.id!==u))}return h(B,{children:[h("div",{class:"sw-messages",ref:_,children:a.map(u=>u.kind==="ticket"?h(bt,{proposal:u.proposal,strings:n,onConfirm:$=>x(u.proposal,$),onCancel:()=>m(u.id)},u.id):u.kind==="user"?h("div",{class:"sw-msg sw-msg-user",children:u.text},u.id):h("div",{children:[h("div",{class:"sw-msg sw-msg-assistant",children:u.text}),u.citations.map(($,C)=>{const k=yt($);return k?h("div",{class:"sw-citation",children:[n.sourcePrefix," ",k]},C):null})]},u.id))}),s?h("div",{class:"sw-composer",children:[h("textarea",{class:"sw-composer-input",rows:1,value:d,placeholder:n.composerPlaceholder,"aria-label":n.composerPlaceholder,disabled:f,onInput:u=>c(u.target.value),onKeyDown:u=>{u.key==="Enter"&&!u.shiftKey&&(u.preventDefault(),w())}}),h("button",{class:"sw-btn",disabled:f||d.trim().length===0,"aria-label":n.send,onClick:()=>{w()},children:n.send})]}):h("div",{class:"sw-composer",children:h("div",{class:"sw-composer-note",children:n.chatDisabledNote})})]})}function kt(t){return`
|
|
4
|
+
:host {
|
|
5
|
+
--sw-accent: ${/^#[0-9a-fA-F]{3,8}$/.test(t)?t:"#3b82f6"};
|
|
6
|
+
--sw-bg: #ffffff;
|
|
7
|
+
--sw-fg: #1a1a1a;
|
|
8
|
+
--sw-muted: #6b7280;
|
|
9
|
+
--sw-border: #e5e7eb;
|
|
10
|
+
--sw-panel-bg: #f9fafb;
|
|
11
|
+
all: initial;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.sw-root, .sw-root * {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Bolla lanciatrice, fissa in basso a destra. */
|
|
20
|
+
.sw-bubble {
|
|
21
|
+
position: fixed;
|
|
22
|
+
bottom: 20px;
|
|
23
|
+
right: 20px;
|
|
24
|
+
width: 56px;
|
|
25
|
+
height: 56px;
|
|
26
|
+
border-radius: 50%;
|
|
27
|
+
border: none;
|
|
28
|
+
background: var(--sw-accent);
|
|
29
|
+
color: #fff;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
z-index: 2147483000;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.24);
|
|
36
|
+
font-size: 24px;
|
|
37
|
+
line-height: 1;
|
|
38
|
+
padding: 0;
|
|
39
|
+
}
|
|
40
|
+
.sw-bubble:hover { filter: brightness(1.05); }
|
|
41
|
+
|
|
42
|
+
/* Pannello chat. */
|
|
43
|
+
.sw-panel {
|
|
44
|
+
position: fixed;
|
|
45
|
+
bottom: 88px;
|
|
46
|
+
right: 20px;
|
|
47
|
+
width: 380px;
|
|
48
|
+
height: 600px;
|
|
49
|
+
max-height: calc(100vh - 108px);
|
|
50
|
+
background: var(--sw-bg);
|
|
51
|
+
color: var(--sw-fg);
|
|
52
|
+
border: 1px solid var(--sw-border);
|
|
53
|
+
border-radius: 12px;
|
|
54
|
+
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18);
|
|
55
|
+
z-index: 2147483000;
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.sw-header {
|
|
62
|
+
background: var(--sw-accent);
|
|
63
|
+
color: #fff;
|
|
64
|
+
padding: 14px 16px;
|
|
65
|
+
flex: 0 0 auto;
|
|
66
|
+
}
|
|
67
|
+
.sw-header-title { font-size: 15px; font-weight: 600; }
|
|
68
|
+
.sw-header-note { font-size: 12px; opacity: 0.85; margin-top: 2px; }
|
|
69
|
+
|
|
70
|
+
.sw-messages {
|
|
71
|
+
flex: 1 1 auto;
|
|
72
|
+
overflow-y: auto;
|
|
73
|
+
padding: 14px;
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: column;
|
|
76
|
+
gap: 10px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.sw-msg {
|
|
80
|
+
max-width: 85%;
|
|
81
|
+
padding: 8px 12px;
|
|
82
|
+
border-radius: 12px;
|
|
83
|
+
font-size: 14px;
|
|
84
|
+
line-height: 1.4;
|
|
85
|
+
white-space: pre-wrap;
|
|
86
|
+
word-break: break-word;
|
|
87
|
+
}
|
|
88
|
+
.sw-msg-user {
|
|
89
|
+
align-self: flex-end;
|
|
90
|
+
background: var(--sw-accent);
|
|
91
|
+
color: #fff;
|
|
92
|
+
border-bottom-right-radius: 4px;
|
|
93
|
+
}
|
|
94
|
+
.sw-msg-assistant {
|
|
95
|
+
align-self: flex-start;
|
|
96
|
+
background: var(--sw-panel-bg);
|
|
97
|
+
color: var(--sw-fg);
|
|
98
|
+
border: 1px solid var(--sw-border);
|
|
99
|
+
border-bottom-left-radius: 4px;
|
|
100
|
+
}
|
|
101
|
+
.sw-citation {
|
|
102
|
+
font-size: 11px;
|
|
103
|
+
color: var(--sw-muted);
|
|
104
|
+
margin-top: 2px;
|
|
105
|
+
align-self: flex-start;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Card ticket. */
|
|
109
|
+
.sw-card {
|
|
110
|
+
align-self: stretch;
|
|
111
|
+
border: 1px solid var(--sw-border);
|
|
112
|
+
border-radius: 10px;
|
|
113
|
+
padding: 12px;
|
|
114
|
+
background: var(--sw-panel-bg);
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-direction: column;
|
|
117
|
+
gap: 8px;
|
|
118
|
+
}
|
|
119
|
+
.sw-badge {
|
|
120
|
+
align-self: flex-start;
|
|
121
|
+
font-size: 11px;
|
|
122
|
+
font-weight: 600;
|
|
123
|
+
text-transform: uppercase;
|
|
124
|
+
letter-spacing: 0.04em;
|
|
125
|
+
padding: 2px 8px;
|
|
126
|
+
border-radius: 999px;
|
|
127
|
+
background: var(--sw-accent);
|
|
128
|
+
color: #fff;
|
|
129
|
+
}
|
|
130
|
+
.sw-input, .sw-textarea {
|
|
131
|
+
width: 100%;
|
|
132
|
+
border: 1px solid var(--sw-border);
|
|
133
|
+
border-radius: 8px;
|
|
134
|
+
padding: 8px 10px;
|
|
135
|
+
font-size: 14px;
|
|
136
|
+
color: var(--sw-fg);
|
|
137
|
+
background: #fff;
|
|
138
|
+
}
|
|
139
|
+
.sw-textarea { resize: vertical; min-height: 72px; }
|
|
140
|
+
.sw-card-actions { display: flex; gap: 8px; }
|
|
141
|
+
.sw-card-msg { font-size: 12px; color: #b91c1c; }
|
|
142
|
+
.sw-card-confirmed { font-size: 14px; font-weight: 600; color: #15803d; }
|
|
143
|
+
|
|
144
|
+
/* Composer. */
|
|
145
|
+
.sw-composer {
|
|
146
|
+
flex: 0 0 auto;
|
|
147
|
+
border-top: 1px solid var(--sw-border);
|
|
148
|
+
padding: 10px;
|
|
149
|
+
display: flex;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
align-items: flex-end;
|
|
152
|
+
}
|
|
153
|
+
.sw-composer-input {
|
|
154
|
+
flex: 1 1 auto;
|
|
155
|
+
border: 1px solid var(--sw-border);
|
|
156
|
+
border-radius: 8px;
|
|
157
|
+
padding: 8px 10px;
|
|
158
|
+
font-size: 14px;
|
|
159
|
+
resize: none;
|
|
160
|
+
max-height: 96px;
|
|
161
|
+
color: var(--sw-fg);
|
|
162
|
+
background: #fff;
|
|
163
|
+
}
|
|
164
|
+
.sw-composer-note {
|
|
165
|
+
flex: 1 1 auto;
|
|
166
|
+
font-size: 12px;
|
|
167
|
+
color: var(--sw-muted);
|
|
168
|
+
padding: 8px 4px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.sw-btn {
|
|
172
|
+
border: none;
|
|
173
|
+
border-radius: 8px;
|
|
174
|
+
padding: 8px 14px;
|
|
175
|
+
font-size: 13px;
|
|
176
|
+
font-weight: 600;
|
|
177
|
+
cursor: pointer;
|
|
178
|
+
background: var(--sw-accent);
|
|
179
|
+
color: #fff;
|
|
180
|
+
}
|
|
181
|
+
.sw-btn:disabled { opacity: 0.5; cursor: default; }
|
|
182
|
+
.sw-btn-secondary {
|
|
183
|
+
background: transparent;
|
|
184
|
+
color: var(--sw-fg);
|
|
185
|
+
border: 1px solid var(--sw-border);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Full-screen sotto 480px. */
|
|
189
|
+
@media (max-width: 480px) {
|
|
190
|
+
.sw-panel {
|
|
191
|
+
inset: 0;
|
|
192
|
+
width: 100%;
|
|
193
|
+
height: 100%;
|
|
194
|
+
max-height: 100%;
|
|
195
|
+
border-radius: 0;
|
|
196
|
+
border: none;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
`}function St({base:t,config:e,user:n}){const[r,s]=D(!1),o=mt(e.language);return h("div",{class:"sw-root",children:[r?h("div",{class:"sw-panel",role:"dialog","aria-label":e.title,children:[h("div",{class:"sw-header",children:[h("div",{class:"sw-header-title",children:e.title}),h("div",{class:"sw-header-note",children:o.assistantNote})]}),h(xt,{base:t,user:n,strings:o,welcomeMessage:e.welcomeMessage||o.welcomeFallback,chatEnabled:e.chatEnabled,initialConversationId:Ye(t.slug)})]}):null,h("button",{class:"sw-bubble","aria-label":r?o.closeLabel:o.openLabel,onClick:()=>s(a=>!a),children:r?"✕":"💬"})]})}function Ct(t,e,n){const r=document.createElement("div");r.setAttribute("data-stubwise-widget",""),document.body.appendChild(r);const s=r.attachShadow({mode:"open"}),o=document.createElement("style");o.textContent=kt(e.accentColor),s.appendChild(o);const a=document.createElement("div");return s.appendChild(a),dt(h(St,{base:t,config:e,user:n}),a),r}let pe=!1;function $t(){pe=!1}function tt(t){if(!(typeof document>"u")){if(document.body){t();return}document.addEventListener("DOMContentLoaded",t,{once:!0})}}async function nt(t){try{if(typeof document>"u"||pe)return;pe=!0;const e=he(t.dsn),n=await me(e);if(!n.enabled)return;tt(()=>Ct(e,n,t.user))}catch(e){console.warn("[stubwise] widget init non riuscito:",e)}}return typeof window<"u"&&(window.Stubwise={initWidget:nt},window.dispatchEvent(new Event("stubwise:ready"))),P.WidgetApiError=j,P.__resetWidgetForTests=$t,P.clearConversationId=Xe,P.confirmTicket=be,P.createConversation=ge,P.fetchConfig=me,P.fetchMessages=ve,P.getConversationId=Ye,P.initWidget=nt,P.parseSseStream=Ze,P.parseWidgetDsn=he,P.sendMessage=we,P.setConversationId=Ve,P.whenBodyReady=tt,Object.defineProperty(P,Symbol.toStringTag,{value:"Module"}),P})({});
|