@docgenlab.com/chat-widget 0.1.3 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -83,9 +83,46 @@ DocGenLab.destroy();
83
83
  | `enableImageUpload` | boolean | `true` | Allow end-users to attach screenshots |
84
84
  | `hideBranding` | boolean | `false` | Hide "Powered by DocGenLab" — paid tier |
85
85
  | `openOnLoad` | boolean | `false` | Auto-open the panel on first load |
86
+ | `endUserId` | string | — | Customer's authenticated user id. Conversations follow this user across browsers + devices (vs the default anonymous-per-browser cookie). Without `endUserHash`, the id is unverified. |
87
+ | `endUserHash` | string | — | HMAC-SHA256 of `endUserId` signed with your identity-verification secret. Required only when the agent enforces identity verification. |
86
88
  | `onReady` | `(agent) => void` | — | Fired when the agent resolves |
87
89
  | `onError` | `(err) => void` | — | Fired on unrecoverable errors |
88
90
 
91
+ ### Identity verification (optional)
92
+
93
+ For unauthenticated visitors, the widget assigns an anonymous UUID per browser via `localStorage`. Conversations are scoped to that UUID.
94
+
95
+ If your site has authenticated users and you want chat history to follow each user across devices:
96
+
97
+ ```tsx
98
+ <DocGenLabChat
99
+ agentKey="pk_live_..."
100
+ endUserId={currentUser.id}
101
+ endUserHash={currentUser.docgenlabHash}
102
+ />
103
+ ```
104
+
105
+ Generate `endUserHash` on YOUR backend (never on the client) using HMAC-SHA256:
106
+
107
+ ```ts
108
+ import { createHmac } from 'crypto';
109
+ const hash = createHmac('sha256', process.env.DOCGENLAB_IDENTITY_SECRET)
110
+ .update(user.id)
111
+ .digest('hex');
112
+ ```
113
+
114
+ Without `endUserHash`, the user id is unverified (any browser can claim any id). Standard Intercom-style trust model.
115
+
116
+ ### Responsive behaviour
117
+
118
+ The widget adapts automatically across breakpoints:
119
+
120
+ - **Phone portrait (≤480 wide):** full-width bottom sheet, 90dvh tall (respects iOS Safari's collapsible bottom bar)
121
+ - **Phone landscape / short screens (≤600 high):** side panel anchored to the launcher corner, doesn't fight the on-screen keyboard
122
+ - **Tablet (481–768 wide):** slimmer 340px panel with reduced inset
123
+ - **Tiny phones (≤360 wide):** smaller launcher (48px), tightened padding
124
+ - **Touch devices:** larger tap-targets via `@media (hover: none) and (pointer: coarse)`
125
+
89
126
  ## Browser support
90
127
 
91
128
  ES2018+ targets. Edge, Chrome, Firefox, Safari (last 2 versions). IE11 not supported.
package/dist/api.d.ts CHANGED
@@ -6,22 +6,72 @@
6
6
  * X-DocGenLab-Key — the publishable key (server resolves to agent)
7
7
  * X-DocGenLab-Visitor-Id — a stable UUID minted in localStorage
8
8
  */
9
- import type { AgentInfo, StreamEvent } from './types';
9
+ import type { AgentInfo, StreamEvent, TicketRef } from './types';
10
10
  export interface ClientOptions {
11
11
  agentKey: string;
12
12
  apiBaseUrl: string;
13
+ /** Customer's stable user identifier. When provided, takes precedence
14
+ * over the anonymous-per-browser UUID so conversations follow the user
15
+ * across browsers + devices. Prefixed with 'user:' internally so an
16
+ * attacker can't guess + collide with another user's anonymous UUID. */
17
+ endUserId?: string;
18
+ /** HMAC verification companion (currently passed to the server for
19
+ * future enforcement). */
20
+ endUserHash?: string;
13
21
  }
14
22
  export declare class WidgetClient {
15
23
  private readonly agentKey;
16
24
  private readonly baseUrl;
17
25
  private readonly visitorId;
26
+ private readonly endUserHash?;
18
27
  constructor(opts: ClientOptions);
19
28
  private headers;
20
29
  getAgent(): Promise<AgentInfo>;
30
+ /** Load a previous conversation's messages so the widget can show
31
+ * history after a refresh / re-open. The visitor-id header is the
32
+ * authorization — the backend only returns conversations owned by
33
+ * the requesting visitor on this agent. */
34
+ getConversation(conversationId: string): Promise<any>;
35
+ /** Reconstruct the ticket card that SHOULD currently be on the
36
+ * conversation, based on persisted state. Lets the widget restore
37
+ * raise-ticket / "already on it" / created cards after refresh.
38
+ * Returns kind=null when nothing should show. */
39
+ getTicketCardState(conversationId: string): Promise<{
40
+ kind: 'offer' | 'existing' | 'created' | null;
41
+ conversation_id: string | null;
42
+ connector_type?: string | null;
43
+ connector_name?: string | null;
44
+ ticket_id?: string | null;
45
+ external_ticket_number?: string | null;
46
+ external_url?: string | null;
47
+ status?: string | null;
48
+ }>;
21
49
  uploadImage(file: File): Promise<string>;
22
50
  /** Fetch a previously-uploaded image as a blob URL. Caller must
23
51
  * URL.revokeObjectURL() when done to avoid memory leaks. */
24
52
  fetchImage(imagePath: string): Promise<string>;
53
+ /** Raise a support ticket from a conversation. Requires explicit user
54
+ * consent — the widget only calls this after the user clicks the
55
+ * in-chat "Raise a ticket" chip. */
56
+ raiseTicket(conversationId: string, body: {
57
+ consent: boolean;
58
+ end_user_email?: string;
59
+ end_user_phone?: string;
60
+ priority?: 'low' | 'medium' | 'high' | 'urgent';
61
+ /** Escape hatch from the "We're already on it" card — bypasses
62
+ * the duplicate guard so the user can raise a fresh ticket
63
+ * for a different issue even when a recent one is still open. */
64
+ force_new?: boolean;
65
+ }): Promise<TicketRef>;
66
+ /** List all tickets for THIS visitor on THIS agent. Used by the widget
67
+ * to hydrate ticket cards on conversation reload and to power the
68
+ * "Manage tickets" modal. Optionally filtered by conversation. */
69
+ listTickets(conversationId?: string): Promise<TicketRef[]>;
70
+ /** Fetch the latest status of a ticket the user raised. */
71
+ getTicket(ticketId: string, refresh?: boolean): Promise<TicketRef>;
72
+ /** Fetch a video-source frame attached to a citation chunk.
73
+ * Same auth pattern as fetchImage. */
74
+ fetchSourceFrame(sourceId: string, framePath: string): Promise<string>;
25
75
  /** Stream a chat response via SSE. Returns an abort fn the caller invokes
26
76
  * to cancel the stream. */
27
77
  streamChat(body: {