passline-ai 2.0.0 → 2.1.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.
package/README.md CHANGED
@@ -1,6 +1,40 @@
1
- # Passline
1
+ # passline-ai
2
2
 
3
- React chat widget for the Passline website chatbot API.
3
+ React chat widget for [Passline](https://passline.ai). Drop it into a React or Next.js app with an embed token.
4
+
5
+ **Requirements:** Node.js 18+, React 18+, a secure context (`https` or `localhost`) so Web Crypto can run.
6
+
7
+ ```bash
8
+ npm install passline-ai
9
+ ```
10
+
11
+ ```tsx
12
+ import { PasslineChat } from "passline-ai";
13
+
14
+ <PasslineChat token="YOUR_EMBED_TOKEN" />;
15
+ ```
16
+
17
+ That is the full installer setup.
18
+
19
+ ---
20
+
21
+ ## Contents
22
+
23
+ - [Install](#install)
24
+ - [Quick start](#quick-start)
25
+ - [Exports](#exports)
26
+ - [`PasslineChat`](#passlinechat)
27
+ - [Theme](#theme)
28
+ - [Labels](#labels)
29
+ - [Render props](#render-props)
30
+ - [Session persistence](#session-persistence)
31
+ - [`usePasslineChat`](#usepasslinechat)
32
+ - [`usePasslineSession`](#usepasslinesession)
33
+ - [Types](#types)
34
+ - [Next.js](#nextjs)
35
+ - [Behavior](#behavior)
36
+
37
+ ---
4
38
 
5
39
  ## Install
6
40
 
@@ -8,32 +42,509 @@ React chat widget for the Passline website chatbot API.
8
42
  npm install passline-ai
9
43
  ```
10
44
 
11
- Peer dependencies: `react` and `react-dom` >= 18.
45
+ Peer dependencies:
46
+
47
+ | Package | Version |
48
+ | ----------- | ---------- |
49
+ | `react` | `>=18.0.0` |
50
+ | `react-dom` | `>=18.0.0` |
51
+
52
+ Styles ship inside the bundle. Do not import a separate CSS file.
53
+
54
+ ---
12
55
 
13
- ## Usage
56
+ ## Quick start
57
+
58
+ ### React
14
59
 
15
60
  ```tsx
16
61
  import { PasslineChat } from "passline-ai";
17
62
 
18
63
  export function App() {
19
- return (
20
- <PasslineChat
21
- token={process.env.NEXT_PUBLIC_PASSLINE_TOKEN!}
22
- origin="https://api.passline.ai"
64
+ return <PasslineChat token="YOUR_EMBED_TOKEN" />;
65
+ }
66
+ ```
67
+
68
+ ### Themed widget
69
+
70
+ ```tsx
71
+ <PasslineChat
72
+ token="YOUR_EMBED_TOKEN"
73
+ locale="en"
74
+ autoOpen
75
+ theme={{
76
+ primaryColor: "#0f766e",
77
+ buttonSize: "large",
78
+ panelWidth: "400px",
79
+ }}
80
+ labels={{
81
+ typeMessage: "Ask anything…",
82
+ send: "Send",
83
+ }}
84
+ onMessageSend={(text) => console.log("sent", text)}
85
+ onError={(error) => console.error(error)}
86
+ />
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Exports
92
+
93
+ Everything below is available from `"passline-ai"`:
94
+
95
+ | Export | Kind | Description |
96
+ | ------------------------------ | --------- | --------------------------------------------- |
97
+ | `PasslineChat` | component | Floating chat button + panel |
98
+ | `usePasslineChat` | hook | Widget state, open/close, send, new chat |
99
+ | `usePasslineSession` | hook | Session create/restore, messages, persistence |
100
+ | `DEFAULT_PASSLINE_CHAT_LABELS` | constant | Default chrome strings |
101
+ | `mergePasslineChatLabels` | function | Merge partial labels onto the defaults |
102
+ | `PasslineChatProps` | type | Props for `PasslineChat` |
103
+ | `PasslineTheme` | type | Theme tokens |
104
+ | `PasslineChatLabels` | type | Label keys |
105
+ | `PasslineMessage` | type | One chat message |
106
+ | `PasslineSession` | type | Active session + branding |
107
+
108
+ ---
109
+
110
+ ## `PasslineChat`
111
+
112
+ Floating launcher (bottom-right) that opens a dialog panel. Branding (assistant name, company name, logo) comes from the session API after the first successful connect.
113
+
114
+ ```tsx
115
+ import { PasslineChat } from "passline-ai";
116
+
117
+ <PasslineChat token="YOUR_EMBED_TOKEN" />;
118
+ ```
119
+
120
+ ### Props
121
+
122
+ | Prop | Type | Default | Description |
123
+ | ------------------- | ------------------------------------ | ----------- | ---------------------------------------------------------------------------------------------- |
124
+ | `token` | `string` | required | Embed token from the Passline dashboard. |
125
+ | `locale` | `string` | — | BCP 47 tag sent when creating a session (`en`, `nb`, `fr`). Also namespaces persisted storage. |
126
+ | `labels` | `Partial<PasslineChatLabels>` | defaults | Overrides for widget chrome. Conversation copy comes from the API. |
127
+ | `theme` | `PasslineTheme` | defaults | Colors, sizes, fonts, and optional class names. |
128
+ | `className` | `string` | `""` | Extra class on the wrapper. |
129
+ | `buttonClassName` | `string` | `""` | Extra class on the launcher button. |
130
+ | `panelClassName` | `string` | `""` | Extra class on the dialog panel. |
131
+ | `autoOpen` | `boolean` | `false` | Open the panel on mount. |
132
+ | `persistSession` | `boolean` | `true` | Store the session in `localStorage`. |
133
+ | `maxSessionAge` | `number` | `604800000` | Max age of a stored session in ms (7 days). |
134
+ | `maxStoredMessages` | `number` | `200` | Max messages kept in `localStorage`. |
135
+ | `renderButton` | `(props) => ReactNode` | built-in | Replace the launcher. |
136
+ | `renderHeader` | `(props) => ReactNode` | built-in | Replace the panel header. |
137
+ | `renderMessage` | `(props) => ReactNode` | built-in | Replace each bubble. |
138
+ | `renderInput` | `(props) => ReactNode` | built-in | Replace the composer. |
139
+ | `onOpen` | `() => void` | — | Fires when the panel opens (not on first mount if closed). |
140
+ | `onClose` | `() => void` | — | Fires when the panel closes. |
141
+ | `onMessageSend` | `(message: string) => void` | — | Fires with the user text before the request. |
142
+ | `onMessageReceive` | `(message: PasslineMessage) => void` | — | Fires with the assistant reply. |
143
+ | `onError` | `(error: Error) => void` | — | Network, session, or send failures. |
144
+ | `onSessionStart` | `(session: PasslineSession) => void` | — | Fires once per new `sessionId`. |
145
+ | `onNewChat` | `() => void` | — | Fires after the header “new chat” action. |
146
+
147
+ ---
148
+
149
+ ## Theme
150
+
151
+ Pass a partial `theme` object. Unset keys use the defaults below.
152
+
153
+ ```tsx
154
+ <PasslineChat
155
+ token="YOUR_EMBED_TOKEN"
156
+ theme={{
157
+ primaryColor: "#4f46e5",
158
+ buttonSize: "small",
159
+ panelClassName: "my-chat-panel",
160
+ }}
161
+ />
162
+ ```
163
+
164
+ ### Tokens
165
+
166
+ | Token | Type | Default |
167
+ | -------------------------- | -------------------------------- | ----------------------------------- |
168
+ | `primaryColor` | `string` | `#0f766e` |
169
+ | `primaryHoverColor` | `string` | `#0d9488` |
170
+ | `backgroundColor` | `string` | `#f8fafc` |
171
+ | `textColor` | `string` | `#0f172a` |
172
+ | `userBubbleColor` | `string` | `#0f766e` |
173
+ | `userBubbleTextColor` | `string` | `#ffffff` |
174
+ | `assistantBubbleColor` | `string` | `#ffffff` |
175
+ | `assistantBubbleTextColor` | `string` | `#1e293b` |
176
+ | `borderRadius` | `string \| number` | `16px` |
177
+ | `borderWidth` | `string \| number` | `1px` |
178
+ | `borderColor` | `string` | `#e2e8f0` |
179
+ | `buttonSize` | `"small" \| "medium" \| "large"` | `medium` (`44px` / `56px` / `64px`) |
180
+ | `buttonBorderRadius` | `string \| number` | `999px` |
181
+ | `buttonBackgroundColor` | `string` | `#0f766e` |
182
+ | `buttonHoverColor` | `string` | `#0d9488` |
183
+ | `buttonIconColor` | `string` | `#ffffff` |
184
+ | `panelWidth` | `string \| number` | `380px` |
185
+ | `panelHeight` | `string \| number` | `560px` |
186
+ | `panelShadow` | `string` | `0 12px 40px rgba(0,0,0,.22)` |
187
+ | `fontFamily` | `string` | system UI stack |
188
+ | `fontSize` | `string` | `14px` |
189
+ | `spacing` | `string \| number` | `12px` |
190
+ | `padding` | `string \| number` | `12px` |
191
+ | `className` | `string` | `""` |
192
+ | `buttonClassName` | `string` | `""` |
193
+ | `panelClassName` | `string` | `""` |
194
+ | `headerClassName` | `string` | `""` |
195
+ | `messagesClassName` | `string` | `""` |
196
+ | `inputClassName` | `string` | `""` |
197
+
198
+ `className`, `buttonClassName`, and `panelClassName` on the component are also applied (in addition to the theme class names).
199
+
200
+ The launcher uses the organization logo when the session provides `logoUrl`. If the image fails, it falls back to the chat icon. The header falls back to initials from the company or assistant name.
201
+
202
+ ---
203
+
204
+ ## Labels
205
+
206
+ Chrome strings only. The greeting and assistant replies come from the API.
207
+
208
+ ```tsx
209
+ import {
210
+ PasslineChat,
211
+ DEFAULT_PASSLINE_CHAT_LABELS,
212
+ mergePasslineChatLabels,
213
+ } from "passline-ai";
214
+
215
+ <PasslineChat
216
+ token="YOUR_EMBED_TOKEN"
217
+ labels={{
218
+ assistant: "Support",
219
+ typeMessage: "Type a message…",
220
+ send: "Send",
221
+ }}
222
+ />;
223
+ ```
224
+
225
+ `mergePasslineChatLabels(partial)` returns a full `PasslineChatLabels` object (`{ ...DEFAULT_PASSLINE_CHAT_LABELS, ...partial }`).
226
+
227
+ | Key | Default | Where it appears |
228
+ | ------------------ | ---------------------- | ----------------------------------------------- |
229
+ | `assistant` | `Assistant` | Header title fallback, avatar initials fallback |
230
+ | `offline` | `Offline` | Subtitle, input placeholder when offline |
231
+ | `unavailable` | `Unavailable` | Subtitle / placeholder when the session failed |
232
+ | `connecting` | `Connecting…` | Subtitle, placeholder, empty-state |
233
+ | `online` | `Online` | Subtitle when connected with no company name |
234
+ | `typeMessage` | `Type a message…` | Input placeholder when ready |
235
+ | `send` | `Send` | Send button |
236
+ | `openChat` | `Open chat` | Launcher `aria-label` when closed |
237
+ | `closeChat` | `Close chat` | Launcher `aria-label` when open |
238
+ | `newChat` | `Start new chat` | Header button `aria-label` |
239
+ | `newChatTitle` | `Start new chat` | Header button `title` |
240
+ | `typing` | `Assistant is typing…` | Shown while a reply is in flight |
241
+ | `panelAriaLabel` | `Passline chat` | Dialog `aria-label` |
242
+ | `organizationLogo` | `Organization logo` | Logo `alt` fallback |
243
+ | `defaultGreeting` | `Hi! How can I help?` | Used only if the API greeting is empty |
244
+
245
+ ---
246
+
247
+ ## Render props
248
+
249
+ Use these to replace a slice of the built-in UI. Session and send logic stay the same.
250
+
251
+ ### `renderButton`
252
+
253
+ ```tsx
254
+ renderButton={({ onClick, isOpen, className }) => (
255
+ <button type="button" className={className} onClick={onClick}>
256
+ {isOpen ? "Close" : "Chat"}
257
+ </button>
258
+ )}
259
+ ```
260
+
261
+ | Field | Type | Description |
262
+ | ----------- | ------------ | ---------------- |
263
+ | `onClick` | `() => void` | Toggle the panel |
264
+ | `isOpen` | `boolean` | Panel visibility |
265
+ | `className` | `string` | Reserved (empty) |
266
+
267
+ ### `renderHeader`
268
+
269
+ ```tsx
270
+ renderHeader={({ title, subtitle, avatar, onNewChat }) => (
271
+ <header>
272
+ <strong>{title}</strong>
273
+ <span>{subtitle}</span>
274
+ <span>{avatar}</span>
275
+ <button type="button" onClick={onNewChat}>
276
+ New
277
+ </button>
278
+ </header>
279
+ )}
280
+ ```
281
+
282
+ | Field | Type | Description |
283
+ | ----------- | ------------ | ------------------------------------------------------------ |
284
+ | `title` | `string` | Assistant name (or label fallback) |
285
+ | `subtitle` | `string` | Company name, or online / connecting / offline / unavailable |
286
+ | `avatar` | `string` | Initials derived from branding |
287
+ | `onNewChat` | `() => void` | Clears storage and starts a new session |
288
+
289
+ ### `renderMessage`
290
+
291
+ ```tsx
292
+ renderMessage={({ message, className }) => (
293
+ <div className={className} data-role={message.role}>
294
+ {message.content}
295
+ </div>
296
+ )}
297
+ ```
298
+
299
+ | Field | Type | Description |
300
+ | ----------- | ----------------- | ----------------------------------- |
301
+ | `message` | `PasslineMessage` | `role` is `"user"` or `"assistant"` |
302
+ | `className` | `string` | Reserved (empty) |
303
+
304
+ ### `renderInput`
305
+
306
+ ```tsx
307
+ renderInput={({ value, onChange, onSend, disabled, placeholder, className }) => (
308
+ <form
309
+ className={className}
310
+ onSubmit={(event) => {
311
+ event.preventDefault();
312
+ onSend();
313
+ }}
314
+ >
315
+ <input
316
+ value={value}
317
+ placeholder={placeholder}
318
+ disabled={disabled}
319
+ onChange={(event) => onChange(event.target.value)}
23
320
  />
24
- );
321
+ <button type="submit" disabled={disabled}>
322
+ Send
323
+ </button>
324
+ </form>
325
+ )}
326
+ ```
327
+
328
+ | Field | Type | Description |
329
+ | ------------- | ------------------------- | ---------------------------------------------------------------- |
330
+ | `value` | `string` | Draft text |
331
+ | `onChange` | `(value: string) => void` | Update draft |
332
+ | `onSend` | `() => void` | Send the current draft |
333
+ | `disabled` | `boolean` | True while loading, offline, sending, or before a session exists |
334
+ | `placeholder` | `string` | Status-aware placeholder |
335
+ | `className` | `string` | Reserved (empty) |
336
+
337
+ The default input sends on Enter (Shift+Enter is ignored; it is a single-line field).
338
+
339
+ ---
340
+
341
+ ## Session persistence
342
+
343
+ When `persistSession` is `true` (default), the widget writes to `localStorage`:
344
+
345
+ | Key | When |
346
+ | -------------------------------- | --------------- |
347
+ | `passline:chat:{token}` | No `locale` |
348
+ | `passline:chat:{token}:{locale}` | `locale` is set |
349
+
350
+ Stored fields: `sessionId`, `assistantName`, `companyName`, `logoUrl`, `messages`, `updatedAt`.
351
+
352
+ - Entries older than `maxSessionAge` are discarded.
353
+ - Only the last `maxStoredMessages` messages are kept.
354
+ - Quota / private-mode failures are ignored; the session stays in memory.
355
+ - `newChat` clears the stored entry and creates a new session.
356
+ - An expired or invalid server session is cleared and recreated. The last user message is not retried automatically.
357
+
358
+ Set `persistSession={false}` for a fresh session every page load.
359
+
360
+ ---
361
+
362
+ ## `usePasslineChat`
363
+
364
+ Headless hook used by `PasslineChat`. Use it to build a custom shell.
365
+
366
+ ```tsx
367
+ import { usePasslineChat } from "passline-ai";
368
+
369
+ const {
370
+ isOpen,
371
+ setIsOpen,
372
+ toggleOpen,
373
+ isSending,
374
+ isLoading,
375
+ offline,
376
+ messages,
377
+ session,
378
+ error,
379
+ sendMessage,
380
+ newChat,
381
+ } = usePasslineChat(token, {
382
+ locale: "en",
383
+ defaultGreeting: "Hi! How can I help?",
384
+ persistSession: true,
385
+ maxSessionAge: 7 * 24 * 60 * 60 * 1000,
386
+ maxStoredMessages: 200,
387
+ autoOpen: false,
388
+ onMessageSend: (text) => {},
389
+ onMessageReceive: (message) => {},
390
+ onError: (error) => {},
391
+ onSessionStart: (session) => {},
392
+ });
393
+ ```
394
+
395
+ A session is created (or restored) when the panel is open and the client is online.
396
+
397
+ ### Options
398
+
399
+ | Option | Type | Default |
400
+ | ------------------- | ------------------------------------ | --------------------- |
401
+ | `locale` | `string` | — |
402
+ | `defaultGreeting` | `string` | `Hi! How can I help?` |
403
+ | `persistSession` | `boolean` | `true` |
404
+ | `maxSessionAge` | `number` | 7 days in ms |
405
+ | `maxStoredMessages` | `number` | `200` |
406
+ | `autoOpen` | `boolean` | `false` |
407
+ | `onMessageSend` | `(message: string) => void` | — |
408
+ | `onMessageReceive` | `(message: PasslineMessage) => void` | — |
409
+ | `onError` | `(error: Error) => void` | — |
410
+ | `onSessionStart` | `(session: PasslineSession) => void` | — |
411
+
412
+ ### Return value
413
+
414
+ | Field | Type | Description |
415
+ | ------------- | ------------------------------------ | -------------------------------- |
416
+ | `isOpen` | `boolean` | Panel open state |
417
+ | `setIsOpen` | `(open: boolean) => void` | Set open state |
418
+ | `toggleOpen` | `() => void` | Toggle open state |
419
+ | `isSending` | `boolean` | Reply request in flight |
420
+ | `isLoading` | `boolean` | Session create/restore in flight |
421
+ | `offline` | `boolean` | Gateway unreachable |
422
+ | `messages` | `PasslineMessage[]` | Current thread |
423
+ | `session` | `PasslineSession \| null` | Branding + `sessionId` |
424
+ | `error` | `string \| null` | Last error message |
425
+ | `sendMessage` | `(content: string) => Promise<void>` | Send user text |
426
+ | `newChat` | `() => Promise<void>` | Reset and start a new session |
427
+
428
+ `sendMessage` no-ops when the text is empty, a send is already in flight, a session is loading, or the client is offline.
429
+
430
+ ---
431
+
432
+ ## `usePasslineSession`
433
+
434
+ Lower-level session hook. Prefer `usePasslineChat` unless you need `createSession` / `ensureSession` / `clearStoredSession` directly.
435
+
436
+ ```tsx
437
+ import { usePasslineSession } from "passline-ai";
438
+
439
+ const {
440
+ session,
441
+ messages,
442
+ isLoading,
443
+ offline,
444
+ error,
445
+ sendMessage,
446
+ newChat,
447
+ createSession,
448
+ ensureSession,
449
+ clearStoredSession,
450
+ } = usePasslineSession(
451
+ token,
452
+ persistSession, // default true
453
+ maxSessionAge, // default 7 days
454
+ maxStoredMessages, // default 200
455
+ locale, // optional
456
+ defaultGreeting, // default "Hi! How can I help?"
457
+ );
458
+ ```
459
+
460
+ ### Return value
461
+
462
+ | Field | Type | Description |
463
+ | -------------------- | -------------------------------------------------- | --------------------------------------------- |
464
+ | `session` | `PasslineSession \| null` | Active session |
465
+ | `messages` | `PasslineMessage[]` | `session.messages` or `[]` |
466
+ | `isLoading` | `boolean` | Session request in flight |
467
+ | `offline` | `boolean` | Gateway unreachable |
468
+ | `error` | `string \| null` | Last error |
469
+ | `sendMessage` | `(content: string) => Promise<PasslineMessage>` | Appends user + assistant messages |
470
+ | `newChat` | `() => Promise<PasslineSession>` | Clear storage and create a session |
471
+ | `createSession` | `(forceNew?: boolean) => Promise<PasslineSession>` | Restore cache, or create (`true` skips cache) |
472
+ | `ensureSession` | `() => Promise<PasslineSession \| null>` | Return current session or create one |
473
+ | `clearStoredSession` | `() => void` | Remove the `localStorage` entry |
474
+
475
+ Changing `token` or `locale` resets in-memory state and reloads the matching cache key.
476
+
477
+ ---
478
+
479
+ ## Types
480
+
481
+ ```ts
482
+ interface PasslineMessage {
483
+ id?: string;
484
+ role: "user" | "assistant";
485
+ content: string;
486
+ timestamp?: number;
487
+ }
488
+
489
+ interface PasslineSession {
490
+ sessionId: string;
491
+ assistantName: string;
492
+ companyName: string | null;
493
+ logoUrl: string | null;
494
+ greeting?: string;
495
+ messages: PasslineMessage[];
496
+ }
497
+ ```
498
+
499
+ `PasslineChatProps`, `PasslineTheme`, and `PasslineChatLabels` match the tables above.
500
+
501
+ ---
502
+
503
+ ## Next.js
504
+
505
+ Mark the host as a client component. The widget uses hooks, `window`, and `localStorage`.
506
+
507
+ ### App Router
508
+
509
+ ```tsx
510
+ "use client";
511
+
512
+ import { PasslineChat } from "passline-ai";
513
+
514
+ export function SupportChat() {
515
+ return <PasslineChat token={process.env.NEXT_PUBLIC_PASSLINE_TOKEN!} />;
25
516
  }
26
517
  ```
27
518
 
28
- `origin` should be the API host that serves `POST /api/chatbot/session`. Styles ship inside the bundle.
519
+ ### Pages Router
520
+
521
+ ```tsx
522
+ import { PasslineChat } from "passline-ai";
523
+
524
+ export default function Page() {
525
+ return <PasslineChat token={process.env.NEXT_PUBLIC_PASSLINE_TOKEN!} />;
526
+ }
527
+ ```
528
+
529
+ Do not call the widget from a Server Component without a client boundary.
530
+
531
+ ---
532
+
533
+ ## Behavior
534
+
535
+ | Topic | Detail |
536
+ | -------------- | -------------------------------------------------------------------------------------- |
537
+ | Token | Required. Issued in the Passline dashboard. |
538
+ | Branding | `assistantName`, `companyName`, and `logoUrl` arrive with the session. |
539
+ | Greeting | API greeting is the first assistant message. `labels.defaultGreeting` is the fallback. |
540
+ | Input lock | Disabled while loading, offline, sending, or before `sessionId` exists. |
541
+ | Offline | Fetch / 5xx failures mark the widget offline and lock the input. |
542
+ | New chat | Header control clears storage and opens a new session. |
543
+ | Locale | Sent on session create; also splits the storage key. |
544
+ | Secure context | Required (`https` or `localhost`) for the Web Crypto handshake. |
29
545
 
30
- ## Scripts
546
+ ---
31
547
 
32
- | Command | Purpose |
33
- | ----------------------------- | ----------------------------------- |
34
- | `npm run build` | Produce `dist/` for npm |
35
- | `npm run pack:check` | Show what `npm publish` will upload |
36
- | `npm run dev` | Local demo at http://127.0.0.1:5173 |
37
- | `npm publish --access public` | Publish `passline-ai` |
548
+ ## License
38
549
 
39
- Copy `.env.example` to `.env` for the demo. Publishing a GitHub Release on `main` runs `.github/workflows/publish.yml` when `NPM_TOKEN` is set.
550
+ MIT
@@ -21,11 +21,18 @@ export declare function isOfflineError(err: unknown): boolean;
21
21
  export declare function isSessionInvalidError(err: unknown): boolean;
22
22
  export declare class PasslineApi {
23
23
  private token;
24
- private origin;
25
- constructor(token: string, origin?: string);
26
- private getHeaders;
24
+ private channel;
25
+ private handshake;
26
+ constructor(token: string);
27
+ private gatewayUrl;
28
+ private postJson;
29
+ private throwIfFailed;
30
+ private ensureChannel;
31
+ private openChannel;
27
32
  private request;
28
- createSession(): Promise<ChatbotSessionResponse>;
33
+ createSession(options?: {
34
+ locale?: string;
35
+ }): Promise<ChatbotSessionResponse>;
29
36
  sendMessage(sessionId: string, message: string): Promise<ChatbotMessageResponse>;
30
37
  }
31
38
  export declare function toPasslineSession(response: ChatbotSessionResponse, messages?: PasslineSession["messages"]): PasslineSession;
@@ -0,0 +1,19 @@
1
+ export interface PasslineChatLabels {
2
+ assistant: string;
3
+ offline: string;
4
+ unavailable: string;
5
+ connecting: string;
6
+ online: string;
7
+ typeMessage: string;
8
+ send: string;
9
+ openChat: string;
10
+ closeChat: string;
11
+ newChat: string;
12
+ newChatTitle: string;
13
+ typing: string;
14
+ panelAriaLabel: string;
15
+ organizationLogo: string;
16
+ defaultGreeting: string;
17
+ }
18
+ export declare const DEFAULT_PASSLINE_CHAT_LABELS: PasslineChatLabels;
19
+ export declare function mergePasslineChatLabels(labels?: Partial<PasslineChatLabels>): PasslineChatLabels;
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
+ import type { PasslineChatLabels } from "./labels";
2
3
  export interface PasslineMessage {
3
4
  id?: string;
4
5
  role: "user" | "assistant";
@@ -46,7 +47,10 @@ export interface PasslineTheme {
46
47
  }
47
48
  export interface PasslineChatProps {
48
49
  token: string;
49
- origin?: string;
50
+ /** BCP 47 language tag sent when creating a session (e.g. `en`, `nb`, `fr`). */
51
+ locale?: string;
52
+ /** Overrides for widget chrome strings. Conversation copy comes from the API. */
53
+ labels?: Partial<PasslineChatLabels>;
50
54
  theme?: PasslineTheme;
51
55
  className?: string;
52
56
  buttonClassName?: string;
@@ -0,0 +1,40 @@
1
+ export declare const PROXY_PATH = "/api/p";
2
+ /** Public widget gateway. This is the only host the npm package calls. */
3
+ export declare const PASSLINE_GATEWAY = "https://api.passline.ai";
4
+ export type HandshakeRequest = {
5
+ v: 1;
6
+ op: "h";
7
+ c: string;
8
+ };
9
+ export type HandshakeResponse = {
10
+ v: 1;
11
+ s: string;
12
+ t: string;
13
+ };
14
+ export type RpcRequest = {
15
+ v: 1;
16
+ op: "x";
17
+ t: string;
18
+ i: string;
19
+ c: string;
20
+ };
21
+ export type RpcResponse = {
22
+ v: 1;
23
+ i: string;
24
+ c: string;
25
+ };
26
+ export declare function utf8(value: string): Uint8Array<ArrayBuffer>;
27
+ export declare function b64encode(data: ArrayBuffer | Uint8Array): string;
28
+ export declare function b64decode(value: string): Uint8Array<ArrayBuffer>;
29
+ export declare function generateEcdhPair(): Promise<CryptoKeyPair>;
30
+ export declare function exportPublicKey(key: CryptoKey): Promise<string>;
31
+ export declare function importPublicKey(spki: string): Promise<CryptoKey>;
32
+ export declare function sharedAesKey(privateKey: CryptoKey, publicKey: CryptoKey): Promise<CryptoKey>;
33
+ export declare function encryptJson(key: CryptoKey, payload: unknown): Promise<{
34
+ i: string;
35
+ c: string;
36
+ }>;
37
+ export declare function decryptJson<T>(key: CryptoKey, i: string, c: string): Promise<T>;
38
+ export declare function sealTicket(secret: string, sharedBits: ArrayBuffer, ttlMs?: number): Promise<string>;
39
+ export declare function openTicket(secret: string, ticket: string): Promise<CryptoKey>;
40
+ export declare function ecdhBits(privateKey: CryptoKey, publicKey: CryptoKey): Promise<ArrayBuffer>;
@@ -1,6 +1,7 @@
1
1
  import { PasslineMessage, PasslineSession } from "../component/types";
2
2
  export declare function usePasslineChat(token: string, options?: {
3
- origin?: string;
3
+ locale?: string;
4
+ defaultGreeting?: string;
4
5
  persistSession?: boolean;
5
6
  maxSessionAge?: number;
6
7
  maxStoredMessages?: number;