@kavchat/widget 0.0.1 → 0.0.3

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,85 @@
1
+ import type { LiveChat, LiveConversation } from '@kavchat/core';
2
+ /**
3
+ * הרשת של צ'אט עם נציג (docs/live-chat-protocol.md, סעיפים 2 ו-3).
4
+ * בלי preflight: אין כותרות בכלל, וגוף ה-POST הוא מחרוזת JSON, שהדפדפן שולח כ-text/plain.
5
+ * הקרדנשל עובר רק בגוף הבקשה. בכתובת יש רק כרטיס הזרם החד-פעמי
6
+ */
7
+ export interface LiveApi {
8
+ apiBase: string;
9
+ publicKey: string;
10
+ }
11
+ export interface LiveRes {
12
+ status: number;
13
+ ok: boolean;
14
+ data: any;
15
+ code: string;
16
+ message: string;
17
+ }
18
+ export declare const liveUrl: (c: LiveApi, path: string) => string;
19
+ export declare const livePost: (c: LiveApi, path: string, body: unknown) => Promise<LiveRes>;
20
+ export declare const liveGet: (c: LiveApi, path: string) => Promise<LiveRes>;
21
+ /** רשת, 429 ו-5xx: מנסים שוב. 4xx אחר: לא */
22
+ export declare const retryable: (r: LiveRes) => boolean;
23
+ export declare const sleep: (ms: number) => Promise<unknown>;
24
+ /** בלי שום אירוע 90 שניות: סוגרים ומתחברים מחדש. EventSource לא חושף את שורות ה-ping, ולכן זמן נדיב מה-60 שבפרוטוקול */
25
+ export declare const LIVE_IDLE_MS = 90000;
26
+ export interface LiveCallbacks {
27
+ change(chat: LiveChat): void;
28
+ typing(on: boolean): void;
29
+ /** reconnecting: החיבור נפל ומנסים שוב. replaced: לשונית או מכשיר אחר לקחו את השיחה */
30
+ notice(kind: 'reconnecting' | 'replaced' | null): void;
31
+ /** השיחה הסתיימה (או שהקרדנשל כבר לא תקף): מוחקים אותו מהאחסון */
32
+ ended(): void;
33
+ }
34
+ export declare class LiveConnection {
35
+ private api;
36
+ credential: string;
37
+ private cb;
38
+ chat: LiveChat;
39
+ stopped: boolean;
40
+ random: () => number;
41
+ private es;
42
+ private timer;
43
+ private idle;
44
+ private typingTimer;
45
+ private attempt;
46
+ /** כישלונות זרם לפני ready, כשכרטיס הזרם התקבל: כנראה פרוקסי שחוסם SSE */
47
+ private blocked;
48
+ private pollUntil;
49
+ private polling;
50
+ private typingOn;
51
+ private typingAt;
52
+ private numberTried;
53
+ constructor(api: LiveApi, credential: string, conversation: LiveConversation, cb: LiveCallbacks);
54
+ get final(): boolean;
55
+ private body;
56
+ private set;
57
+ /** POST /live/sync עד hasMore=false. false: נכשל */
58
+ sync(): Promise<boolean>;
59
+ /** קודים שמשנים את הזרימה (סעיף 2.5). true: טופל */
60
+ private fail;
61
+ connect(): void;
62
+ private openStream;
63
+ private touch;
64
+ private closeStream;
65
+ private bye;
66
+ private retry;
67
+ /** אחרי השהיה: סנכרון לפי lastSeq, ואז זרם חדש */
68
+ private schedule;
69
+ private onOnline;
70
+ /** long-poll כשאין EventSource, כשהזרם חסום, או לזמן מה אחרי live_busy */
71
+ private poll;
72
+ private showTyping;
73
+ /** שליחת הודעה: מוצגת מיד כ"נשלח", עם clientId שנוצר פעם אחת ומשמש לכל ניסיון חוזר */
74
+ send(raw: string): 'ok' | 'empty' | 'too_long' | 'closed';
75
+ retrySend(clientId: string): void;
76
+ private deliver;
77
+ /** "מקליד" לנציג: לכל היותר פעם ב-3 שניות, false כשהשדה התרוקן או נשלח. בלי ניסיונות חוזרים */
78
+ typing(on: boolean): void;
79
+ /** "להשאיר הודעה במקום" או "סיום השיחה". בטוח לחזור עליהן */
80
+ command(path: 'leave-message' | 'end'): Promise<boolean>;
81
+ /** מצב סופי: סוגרים את הזרם ומוחקים את הקרדנשל. אם מספר הפנייה עוד לא ידוע, סנכרון אחד אחרי 2 שניות */
82
+ private finish;
83
+ private end;
84
+ stop(): void;
85
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type * as Live from './live';
2
+ export declare const loadLive: () => Promise<typeof Live>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * טעינת הצ'אט עם נציג. בחבילת npm ובבדיקות זה import רגיל.
3
+ * בקובץ של ה-CDN הבנייה מחליפה את הקובץ הזה ב-live-load-cdn.ts, שטוען את kavchat-live.js רק כשצריך
4
+ */
5
+ export declare const loadLive: () => Promise<typeof import("./live")>;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * עיצוב מסך הצ'אט עם נציג. נוסף ל-Shadow DOM רק כשהקובץ הנפרד נטען.
3
+ * תכונות לוגיות בלבד, כמו בשאר הווידג'ט, כדי ש-RTL יהפוך הכל לבד
4
+ */
5
+ export declare const LIVE_CSS = "\n.lstatus{flex:none;padding:8px 14px;font-size:13px;line-height:1.6;color:#374151;background:#fff;border-block-end:1px solid #e3e6ea}\n.lsys{align-self:center;max-width:90%;text-align:center;font-size:12px;color:#6b7280;padding:2px 8px}\n.lsep{margin-block:6px;border-block-start:1px solid #e3e6ea;padding-block-start:8px;align-self:stretch;max-width:none}\n.lname{display:block;font-size:11px;font-weight:600;color:#6b7280}\n.lp-sending{opacity:.65}\n.lp-failed{opacity:.85;outline:1px solid #b42318}\n.lp-failed .btn{display:block;margin-block-start:6px;padding:3px 8px;font-size:12px;color:#b42318;background:#fff;border-color:#b42318}\n.ltyping{flex:none;padding:0 14px 4px;font-size:12px;color:#6b7280}\n.lerror{flex:none;padding:0 14px}\n.lerror:empty{display:none}\n.lactions{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:4px 14px 8px}\n.lactions:empty{display:none}\n.lactions .btn{padding:6px 12px;font-size:14px}\n.note{margin:0;font-size:14px;color:#4b5563}\n.composer input:disabled{opacity:.5}\n.send:disabled{opacity:.5;cursor:default}\n.launcher.kc-unread::after{content:\"\";position:absolute;inset-block-start:4px;inset-inline-end:4px;width:12px;height:12px;border-radius:50%;background:#e11d48;border:2px solid #fff}\n";
@@ -0,0 +1,67 @@
1
+ import type { BotChip, BotEffect, UiTexts } from '@kavchat/core';
2
+ import { LiveApi } from './live-client';
3
+ /**
4
+ * צד המבקר בצ'אט עם נציג, בתוך חלון הווידג'ט: הכפתור "שיחה עם נציג", טופס הבקשה, מסך ההמתנה, השיחה עצמה,
5
+ * ושחזור אחרי טעינה מחדש. נטען רק כש-liveChat.enabled (או כשיש שיחה פתוחה שמורה), כקובץ נפרד ב-CDN,
6
+ * כדי שהקובץ הראשי יישאר בתקציב. המנוע (סדר, כפילויות, טקסטים) ב-@kavchat/core
7
+ */
8
+ export type LiveEffect = Extract<BotEffect, {
9
+ type: 'contact' | 'live';
10
+ }>;
11
+ export interface FormPrefill {
12
+ note?: string;
13
+ name?: string;
14
+ email?: string;
15
+ phone?: string;
16
+ message?: string;
17
+ }
18
+ /**
19
+ * מה שהבקר צריך מהווידג'ט. KavchatWidget עצמו מועבר לכאן: השמות כאן הם השדות והמתודות (הפרטיים) שלו
20
+ */
21
+ export interface LiveHost {
22
+ api: LiveApi;
23
+ /** liveChat.enabled false: הקובץ נטען רק כדי לשחזר שיחה פתוחה, בלי להציע שיחה חדשה */
24
+ release: {
25
+ liveChat?: {
26
+ enabled: boolean;
27
+ };
28
+ } | null;
29
+ locale: string;
30
+ t: UiTexts;
31
+ ui: {
32
+ root: ShadowRoot;
33
+ panel: HTMLElement;
34
+ chatView: HTMLElement;
35
+ formView: HTMLElement;
36
+ live: HTMLElement;
37
+ launcher: HTMLElement;
38
+ } | null;
39
+ opts: {
40
+ userToken?: string;
41
+ userName?: string;
42
+ };
43
+ session: {
44
+ id: string;
45
+ } | null;
46
+ isOpen: boolean;
47
+ renderChips(): void;
48
+ showChat(): void;
49
+ showForm(effect: LiveEffect, pre: FormPrefill): void;
50
+ emit(event: {
51
+ type: 'live';
52
+ state: string;
53
+ id: string;
54
+ }): void;
55
+ }
56
+ export interface LiveController {
57
+ /** הכפתורים עם "שיחה עם נציג" לפני "פנייה לצוות", רק כשיש נציג זמין */
58
+ chips(chips: BotChip[]): BotChip[];
59
+ /** למה אין נציג, לשורה שמעל טופס הפנייה ('' בלי הסבר) */
60
+ note(): string;
61
+ start(effect: LiveEffect): Promise<void>;
62
+ restore(): Promise<void>;
63
+ /** true כשמסך הצ'אט עם הנציג מוצג והפוקוס עבר אליו */
64
+ focus(): boolean;
65
+ destroy(): void;
66
+ }
67
+ export declare function createLive(host: LiveHost): LiveController;
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * העיצוב כולו בתוך ה-Shadow DOM. all: initial על ה-host עוצר ירושה של גופן, צבע וכיוון מהאתר.
3
- * רק תכונות לוגיות (inline-start/end), כדי ש-dir="rtl" יהפוך את הכל בלי עקיפות ידניות
3
+ * רק תכונות לוגיות (inline-start/end), כדי ש-dir="rtl" יהפוך את הכל בלי עקיפות ידניות.
4
+ * שורות עם overflow:hidden (שם וכותרת משנה) מקבלות line-height נדיב, כדי שתנועות וטונים בתאית,
5
+ * שיושבים מעל ומתחת לשורה, לא ייחתכו. אין לבועות גובה קבוע ואין בהן overflow:hidden
4
6
  */
5
- export declare const CSS = "\n:host{all:initial}\n.kc{font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Noto Sans Hebrew\",\"Noto Sans Arabic\",Arial,sans-serif;font-size:15px;line-height:1.45;color:#1f2328;-webkit-font-smoothing:antialiased}\n.kc *,.kc *::before,.kc *::after{box-sizing:border-box}\n.kc [hidden]{display:none!important}\n.kc button,.kc input,.kc select,.kc textarea{font:inherit;color:inherit;margin:0}\n.kc :focus-visible{outline:2px solid var(--kc-accent);outline-offset:2px}\n.launcher{position:fixed;z-index:2147483000;inset-block-end:20px;inset-inline-end:20px;width:56px;height:56px;border-radius:50%;border:0;padding:0;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 6px 20px rgba(0,0,0,.22);transition:transform .15s ease}\n.launcher:hover{transform:scale(1.05)}\n.launcher svg{width:28px;height:28px}\n.kc[data-pos=start] .launcher,.kc[data-pos=start] .panel{inset-inline-end:auto;inset-inline-start:20px}\n.panel{position:fixed;z-index:2147483001;inset-block-end:88px;inset-inline-end:20px;width:380px;max-width:calc(100vw - 40px);height:620px;max-height:calc(100vh - 108px);display:flex;flex-direction:column;background:#fff;border-radius:16px;box-shadow:0 12px 40px rgba(0,0,0,.24);overflow:hidden;animation:kc-in .18s ease-out}\n@keyframes kc-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}\n.header{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--kc-primary);color:var(--kc-on-primary);flex:none}\n.avatar{width:38px;height:38px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;overflow:hidden;background:rgba(255,255,255,.22);font-weight:600;font-size:15px}\n.avatar img{width:100%;height:100%;object-fit:cover}\n.titles{min-width:0;flex:1}\n.name{font-weight:600;font-size:16px;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.subtitle{font-size:13px;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.close{flex:none;width:36px;height:36px;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}\n.close:hover{background:rgba(127,127,127,.2)}\n.close svg{width:22px;height:22px}\n.banner{flex:none;background:#fff4e5;color:#7a4100;font-size:13px;padding:6px 14px}\n.view{flex:1;min-height:0;display:flex;flex-direction:column;background:#f6f7f9}\n.messages{flex:1;min-height:0;overflow-y:auto;padding:16px 14px 8px;display:flex;flex-direction:column;gap:6px;overscroll-behavior:contain}\n.msg{max-width:85%;padding:9px 13px;border-radius:16px;white-space:pre-wrap;overflow-wrap:anywhere}\n.msg-bot{align-self:flex-start;background:#fff;border:1px solid #e3e6ea;border-end-start-radius:4px}\n.msg-user{align-self:flex-end;background:var(--kc-primary);color:var(--kc-on-primary);border-end-end-radius:4px}\n.msg-user+.msg-bot,.msg-bot+.msg-user{margin-block-start:6px}\n.msg a{color:inherit;text-decoration:underline}\n.ai{display:block;font-size:11px;font-weight:600;color:#6b7280}\n.typing{color:#9ca3af;font-weight:700;animation:kc-dot 1s infinite}\n@keyframes kc-dot{50%{opacity:.3}}\n.msg img{display:block;max-width:100%;height:auto;border-radius:10px;margin-block-end:6px}\n.chips{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:4px 14px 10px;max-height:40%;overflow-y:auto}\n.chip{border:1px solid var(--kc-accent);background:#fff;color:var(--kc-accent);border-radius:999px;padding:6px 13px;cursor:pointer;font-size:14px;line-height:1.3;text-align:start}\n.chip:hover{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}\n.chip-home,.chip-back{border-color:#cfd4da;color:#4b5563}\n.composer{flex:none;display:flex;align-items:center;gap:8px;padding:8px 10px;border-block-start:1px solid #e3e6ea;background:#fff}\n.composer input{flex:1;min-width:0;border:0;outline:0;background:transparent;padding:8px 6px;font-size:16px}\n.kc .composer input:focus-visible{outline:0}\n.composer:focus-within{box-shadow:inset 0 2px 0 var(--kc-accent)}\n.send{flex:none;width:40px;height:40px;border:0;border-radius:50%;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}\n.send svg{width:20px;height:20px}\n.kc[dir=rtl] .send svg{transform:scaleX(-1)}\n.form{flex:1;min-height:0;overflow-y:auto;padding:16px 16px 12px;background:#fff;display:flex;flex-direction:column;gap:10px}\n.form h2{margin:0 0 4px;font-size:17px;font-weight:600}\n.field label{display:block;font-size:13px;color:#4b5563;margin-block-end:3px}\n.field input,.field select,.field textarea{display:block;width:100%;border:1px solid #cfd4da;border-radius:8px;padding:8px 10px;background:#fff;font-size:16px}\n.field textarea{min-height:96px;resize:vertical}\n.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--kc-accent);outline:0;box-shadow:0 0 0 2px color-mix(in srgb,var(--kc-accent) 25%,transparent)}\n.error{color:#b42318;font-size:14px}\n.actions{display:flex;gap:8px;flex-wrap:wrap;margin-block-start:4px}\n.btn{border-radius:8px;padding:9px 16px;cursor:pointer;border:1px solid #cfd4da;background:#fff}\n.btn-primary{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}\n.btn:disabled{opacity:.6;cursor:default}\n.sent{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;background:#fff}\n.badge{flex:none;display:block;text-align:center;font-size:12px;color:#6b7280;padding:6px;background:#fff;border-block-start:1px solid #eef0f2;text-decoration:none}\n.badge:hover{color:#1f2328}\n.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n@media (max-width:480px){.panel,.kc[data-pos=start] .panel{inset:0;width:100%;max-width:none;height:100%;max-height:none;border-radius:0}}\n@media (prefers-reduced-motion:reduce){.kc *{animation:none!important;transition:none!important}}\n";
7
+ export declare const CSS = "\n:host{all:initial}\n.kc{font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Noto Sans Hebrew\",\"Noto Sans Arabic\",\"Noto Sans Thai\",Arial,sans-serif;font-size:15px;line-height:1.45;color:#1f2328;-webkit-font-smoothing:antialiased}\n.kc *,.kc *::before,.kc *::after{box-sizing:border-box}\n.kc [hidden]{display:none!important}\n.kc button,.kc input,.kc select,.kc textarea{font:inherit;color:inherit;margin:0}\n.kc :focus-visible{outline:2px solid var(--kc-accent);outline-offset:2px}\n.launcher{position:fixed;z-index:2147483000;inset-block-end:20px;inset-inline-end:20px;width:56px;height:56px;border-radius:50%;border:0;padding:0;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 6px 20px rgba(0,0,0,.22);transition:transform .15s ease}\n.launcher:hover{transform:scale(1.05)}\n.launcher svg{width:28px;height:28px}\n.kc[data-pos=start] .launcher,.kc[data-pos=start] .panel{inset-inline-end:auto;inset-inline-start:20px}\n.panel{position:fixed;z-index:2147483001;inset-block-end:88px;inset-inline-end:20px;width:380px;max-width:calc(100vw - 40px);height:620px;max-height:calc(100vh - 108px);display:flex;flex-direction:column;background:#fff;border-radius:16px;box-shadow:0 12px 40px rgba(0,0,0,.24);overflow:hidden;animation:kc-in .18s ease-out}\n@keyframes kc-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}\n.header{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--kc-primary);color:var(--kc-on-primary);flex:none}\n.avatar{width:38px;height:38px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;overflow:hidden;background:rgba(255,255,255,.22);font-weight:600;font-size:15px}\n.avatar img{width:100%;height:100%;object-fit:cover}\n.titles{min-width:0;flex:1}\n.name{font-weight:600;font-size:16px;line-height:1.6;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.subtitle{font-size:13px;line-height:1.6;opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.close{flex:none;width:36px;height:36px;border:0;border-radius:50%;background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}\n.close:hover{background:rgba(127,127,127,.2)}\n.close svg{width:22px;height:22px}\n.banner{flex:none;background:#fff4e5;color:#7a4100;font-size:13px;padding:6px 14px}\n.view{flex:1;min-height:0;display:flex;flex-direction:column;background:#f6f7f9}\n.messages{flex:1;min-height:0;overflow-y:auto;padding:16px 14px 8px;display:flex;flex-direction:column;gap:6px;overscroll-behavior:contain}\n.msg{max-width:85%;padding:9px 13px;border-radius:16px;white-space:pre-wrap;overflow-wrap:anywhere}\n.msg-bot{align-self:flex-start;background:#fff;border:1px solid #e3e6ea;border-end-start-radius:4px}\n.msg-user{align-self:flex-end;background:var(--kc-primary);color:var(--kc-on-primary);border-end-end-radius:4px}\n.msg-user+.msg-bot,.msg-bot+.msg-user{margin-block-start:6px}\n.msg a{color:inherit;text-decoration:underline}\n.ai{display:block;font-size:11px;font-weight:600;color:#6b7280}\n.typing{color:#9ca3af;font-weight:700;animation:kc-dot 1s infinite}\n@keyframes kc-dot{50%{opacity:.3}}\n.msg img{display:block;max-width:100%;height:auto;border-radius:10px;margin-block-end:6px}\n.chips{flex:none;display:flex;flex-wrap:wrap;gap:6px;padding:4px 14px 10px;max-height:40%;overflow-y:auto}\n.chip{border:1px solid var(--kc-accent);background:#fff;color:var(--kc-accent);border-radius:999px;padding:6px 13px;cursor:pointer;font-size:14px;line-height:1.4;text-align:start}\n.chip:hover{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}\n.chip-home,.chip-back{border-color:#cfd4da;color:#4b5563}\n.composer{flex:none;display:flex;align-items:center;gap:8px;padding:8px 10px;border-block-start:1px solid #e3e6ea;background:#fff}\n.composer input{flex:1;min-width:0;border:0;outline:0;background:transparent;padding:8px 6px;font-size:16px}\n.kc .composer input:focus-visible{outline:0}\n.composer:focus-within{box-shadow:inset 0 2px 0 var(--kc-accent)}\n.send{flex:none;width:40px;height:40px;border:0;border-radius:50%;background:var(--kc-primary);color:var(--kc-on-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}\n.send svg{width:20px;height:20px}\n.kc[dir=rtl] .send svg{transform:scaleX(-1)}\n.form{flex:1;min-height:0;overflow-y:auto;padding:16px 16px 12px;background:#fff;display:flex;flex-direction:column;gap:10px}\n.form h2{margin:0 0 4px;font-size:17px;font-weight:600}\n.field label{display:block;font-size:13px;color:#4b5563;margin-block-end:3px}\n.field input,.field select,.field textarea{display:block;width:100%;border:1px solid #cfd4da;border-radius:8px;padding:8px 10px;background:#fff;font-size:16px}\n.field textarea{min-height:96px;resize:vertical}\n.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--kc-accent);outline:0;box-shadow:0 0 0 2px color-mix(in srgb,var(--kc-accent) 25%,transparent)}\n.error{color:#b42318;font-size:14px}\n.actions{display:flex;gap:8px;flex-wrap:wrap;margin-block-start:4px}\n.btn{border-radius:8px;padding:9px 16px;cursor:pointer;border:1px solid #cfd4da;background:#fff}\n.btn-primary{background:var(--kc-primary);color:var(--kc-on-primary);border-color:var(--kc-primary)}\n.btn:disabled{opacity:.6;cursor:default}\n.sent{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;background:#fff}\n.badge{flex:none;display:block;text-align:center;font-size:12px;color:#6b7280;padding:6px;background:#fff;border-block-start:1px solid #eef0f2;text-decoration:none}\n.badge:hover{color:#1f2328}\n.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n@media (max-width:480px){.panel,.kc[data-pos=start] .panel{inset:0;width:100%;max-width:none;height:100%;max-height:none;border-radius:0}}\n@media (prefers-reduced-motion:reduce){.kc *{animation:none!important;transition:none!important}}\n";
@@ -23,6 +23,12 @@ export type KavchatEvent = {
23
23
  type: 'ticket';
24
24
  id: string;
25
25
  number: number;
26
+ }
27
+ /** מצב הצ'אט עם נציג: queued, active, closed, ticket */
28
+ | {
29
+ type: 'live';
30
+ state: string;
31
+ id: string;
26
32
  };
27
33
  export interface ScreenDeclaration {
28
34
  id: string;
@@ -86,6 +92,7 @@ export declare class KavchatWidget {
86
92
  private destroyed;
87
93
  /** מחכים לתשובת AI: מציגים "מקליד" ומתעלמים מקלט חדש */
88
94
  private waiting;
95
+ private live;
89
96
  constructor(options: KavchatOptions);
90
97
  /** ה-ShadowRoot, לבדיקות ולאוטומציה באתר המארח */
91
98
  get shadowRoot(): ShadowRoot | null;
@@ -103,6 +110,11 @@ export declare class KavchatWidget {
103
110
  /** שולח את רשימת המסכים רק כשהשתנתה או פעם בשבוע. שגיאה נבלעת: ננסה שוב בטעינה הבאה */
104
111
  private registerScreens;
105
112
  private apply;
113
+ /**
114
+ * הצ'אט עם נציג נטען רק כשהוא מופעל בגרסה, או כשיש שיחה פתוחה שמורה. כל mount מקבל בקר חדש,
115
+ * שמשחזר את השיחה מהאחסון. כישלון בטעינה משאיר את הבוט ואת טופס הפנייה כמו שהם
116
+ */
117
+ private setupLive;
106
118
  private restoreSession;
107
119
  /**
108
120
  * התסריט נבדק בשפת הבוט (מילות עצירה וכו'), והטקסטים הקבועים של הצ'אט מוצגים בשפת הממשק.
@@ -127,6 +139,7 @@ export declare class KavchatWidget {
127
139
  private renderMessages;
128
140
  private renderChips;
129
141
  private showChat;
142
+ /** pre: מה שהמשתמש כבר כתב בבקשה לנציג, ולמה אין נציג (note) */
130
143
  private showForm;
131
144
  private showSent;
132
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kavchat/widget",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Kavchat web widget: the embeddable scripted support chat for websites.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "prepublishOnly": "yarn build"
26
26
  },
27
27
  "dependencies": {
28
- "@kavchat/core": "0.0.1"
28
+ "@kavchat/core": "0.0.3"
29
29
  },
30
30
  "devDependencies": {
31
31
  "esbuild": "^0.28.2",