@yak-io/javascript 0.6.0 → 0.8.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/README.md +181 -0
- package/dist/client.d.ts +24 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +84 -4
- package/dist/embed.d.ts +50 -9
- package/dist/embed.d.ts.map +1 -1
- package/dist/embed.js +243 -70
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/server/createYakHandler.d.ts.map +1 -1
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/sources.d.ts +1 -1
- package/dist/tool-name.d.ts +10 -0
- package/dist/tool-name.d.ts.map +1 -0
- package/dist/tool-name.js +24 -0
- package/dist/types/config.d.ts +1 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +55 -2
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/voice-machine.d.ts +69 -0
- package/dist/voice-machine.d.ts.map +1 -0
- package/dist/voice-machine.js +163 -0
- package/dist/voice-session.d.ts +102 -0
- package/dist/voice-session.d.ts.map +1 -0
- package/dist/voice-session.js +530 -0
- package/package.json +4 -2
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# @yak-io/javascript
|
|
2
|
+
|
|
3
|
+
Framework-agnostic core SDK for embedding the Yak chat widget. This package provides the low-level client and DOM rendering layer. Most developers should use a framework-specific package (`@yak-io/react`, `@yak-io/vue`, etc.) instead.
|
|
4
|
+
|
|
5
|
+
## When to use this package directly
|
|
6
|
+
|
|
7
|
+
- You are building a vanilla JS / TypeScript app
|
|
8
|
+
- You are building a new framework adapter
|
|
9
|
+
- You need the server-side handler utilities (`./server` export) outside Next.js
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @yak-io/javascript
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quickstart — Vanilla JS
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { YakEmbed } from "@yak-io/javascript";
|
|
21
|
+
|
|
22
|
+
const embed = new YakEmbed({
|
|
23
|
+
appId: "your-app-id",
|
|
24
|
+
theme: { position: "bottom-right", colorMode: "system" },
|
|
25
|
+
trigger: { label: "Ask with AI" },
|
|
26
|
+
getConfig: async () => ({
|
|
27
|
+
routes: {
|
|
28
|
+
routes: [
|
|
29
|
+
{ path: "/", title: "Home", description: "Landing page" },
|
|
30
|
+
{ path: "/docs", title: "Docs", description: "Documentation" },
|
|
31
|
+
],
|
|
32
|
+
generated_at: new Date().toISOString(),
|
|
33
|
+
},
|
|
34
|
+
tools: {
|
|
35
|
+
tools: [
|
|
36
|
+
{
|
|
37
|
+
name: "tasks.list",
|
|
38
|
+
displayName: "List Tasks",
|
|
39
|
+
description: "Return all tasks",
|
|
40
|
+
inputSchema: { type: "object", properties: {} },
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
generated_at: new Date().toISOString(),
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
onToolCall: async (name, args) => {
|
|
47
|
+
if (name === "tasks.list") {
|
|
48
|
+
return { tasks: [] };
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
51
|
+
},
|
|
52
|
+
onRedirect: (path) => {
|
|
53
|
+
window.location.assign(path);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
embed.mount();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Server-side utilities
|
|
61
|
+
|
|
62
|
+
Use `@yak-io/javascript/server` to build framework-agnostic API handlers:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { createYakHandler } from "@yak-io/javascript/server";
|
|
66
|
+
|
|
67
|
+
// Works with any Request/Response runtime (Remix, Fastify, etc.)
|
|
68
|
+
const { GET, POST } = createYakHandler({
|
|
69
|
+
routes: [
|
|
70
|
+
{ path: "/", title: "Home" },
|
|
71
|
+
{ path: "/tasks", title: "Tasks" },
|
|
72
|
+
],
|
|
73
|
+
tools: {
|
|
74
|
+
getTools: async () => [
|
|
75
|
+
{
|
|
76
|
+
name: "tasks.list",
|
|
77
|
+
displayName: "List Tasks",
|
|
78
|
+
description: "Return all tasks",
|
|
79
|
+
inputSchema: { type: "object", properties: {} },
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
executeTool: async (name, args) => {
|
|
83
|
+
if (name === "tasks.list") return { tasks: [] };
|
|
84
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## API Reference
|
|
91
|
+
|
|
92
|
+
### `YakEmbed`
|
|
93
|
+
|
|
94
|
+
High-level class that handles DOM rendering (panel, iframe, trigger button) and client wiring.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
new YakEmbed(config: YakEmbedConfig)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Key methods:**
|
|
101
|
+
|
|
102
|
+
| Method | Description |
|
|
103
|
+
|--------|-------------|
|
|
104
|
+
| `mount()` | Injects the widget into the DOM |
|
|
105
|
+
| `destroy()` | Removes the widget from the DOM |
|
|
106
|
+
| `open()` | Open the chat panel |
|
|
107
|
+
| `close()` | Close the chat panel |
|
|
108
|
+
| `toggle()` | Toggle open/close |
|
|
109
|
+
| `openWithPrompt(prompt)` | Open and pre-fill a prompt |
|
|
110
|
+
| `getState()` | Get current `{ isOpen, isReady }` state |
|
|
111
|
+
| `onStateChange(fn)` | Subscribe to state changes, returns unsubscribe |
|
|
112
|
+
| `getClient()` | Access the underlying `YakClient` |
|
|
113
|
+
|
|
114
|
+
**Configuration (`YakEmbedConfig`):**
|
|
115
|
+
|
|
116
|
+
| Option | Type | Description |
|
|
117
|
+
|--------|------|-------------|
|
|
118
|
+
| `appId` | `string` | Your Yak app ID |
|
|
119
|
+
| `theme` | `Theme` | Position, color mode, and widget colors |
|
|
120
|
+
| `trigger` | `boolean \| TriggerButtonConfig` | Show built-in trigger button |
|
|
121
|
+
| `getConfig` | `ChatConfigProvider` | Async function returning routes + tools config |
|
|
122
|
+
| `chatConfig` | `ChatConfig` | Static config (alternative to `getConfig`) |
|
|
123
|
+
| `onToolCall` | `ToolCallHandler` | Handle tool calls from the assistant |
|
|
124
|
+
| `onGraphQLSchemaCall` | `GraphQLSchemaHandler` | Handle GraphQL schema tool calls |
|
|
125
|
+
| `onRESTSchemaCall` | `RESTSchemaHandler` | Handle REST/OpenAPI schema tool calls |
|
|
126
|
+
| `onRedirect` | `(path: string) => void` | Handle navigation requests |
|
|
127
|
+
| `onToolCallComplete` | `(event: ToolCallEvent) => void` | Called after each tool call |
|
|
128
|
+
| `options.disableRestartButton` | `boolean` | Hide the restart session button |
|
|
129
|
+
|
|
130
|
+
### `YakClient`
|
|
131
|
+
|
|
132
|
+
Low-level iframe communication client. Use `YakEmbed` for most cases.
|
|
133
|
+
|
|
134
|
+
### Logging utilities
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { enableYakLogging, disableYakLogging, isYakLoggingEnabled } from "@yak-io/javascript";
|
|
138
|
+
|
|
139
|
+
enableYakLogging(); // Turn on verbose SDK logging
|
|
140
|
+
disableYakLogging(); // Turn off SDK logging
|
|
141
|
+
isYakLoggingEnabled(); // Returns current state
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
In development, set `window.__YAK_INTERNAL_DEV__ = true` before mounting to connect to a locally running chat UI.
|
|
145
|
+
|
|
146
|
+
## Types
|
|
147
|
+
|
|
148
|
+
All types are exported from the package root:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import type {
|
|
152
|
+
ChatConfig,
|
|
153
|
+
ChatConfigProvider,
|
|
154
|
+
RouteManifest,
|
|
155
|
+
RouteInfo,
|
|
156
|
+
ToolManifest,
|
|
157
|
+
ToolDefinition,
|
|
158
|
+
ToolCallHandler,
|
|
159
|
+
ToolCallEvent,
|
|
160
|
+
ToolCallPayload,
|
|
161
|
+
ToolCallResult,
|
|
162
|
+
GraphQLSchemaHandler,
|
|
163
|
+
RESTSchemaHandler,
|
|
164
|
+
GraphQLRequest,
|
|
165
|
+
RESTRequest,
|
|
166
|
+
SchemaSource,
|
|
167
|
+
GraphQLSchemaSource,
|
|
168
|
+
OpenAPISchemaSource,
|
|
169
|
+
Theme,
|
|
170
|
+
ThemeColors,
|
|
171
|
+
TriggerButtonConfig,
|
|
172
|
+
WidgetPosition,
|
|
173
|
+
YakClientConfig,
|
|
174
|
+
YakEmbedConfig,
|
|
175
|
+
YakEmbedState,
|
|
176
|
+
} from "@yak-io/javascript";
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
Proprietary — see LICENSE file.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ChatConfig } from "./types/config.js";
|
|
2
|
-
import type { Theme } from "./types/messaging.js";
|
|
3
|
-
import type {
|
|
2
|
+
import type { Theme, UserIdentity } from "./types/messaging.js";
|
|
3
|
+
import type { GraphQLSchemaHandler, RESTSchemaHandler, ToolCallEventListener, ToolCallHandler } from "./types/tools.js";
|
|
4
4
|
declare global {
|
|
5
5
|
interface Window {
|
|
6
6
|
__YAK_INTERNAL_DEV__?: boolean;
|
|
@@ -104,6 +104,28 @@ export interface YakClientConfig {
|
|
|
104
104
|
/** Disable the restart session button in the header */
|
|
105
105
|
disableRestartButton?: boolean;
|
|
106
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Signed end-user identity. When supplied, the widget persists conversations
|
|
109
|
+
* server-side keyed to this user and surfaces a history pane. The `hash`
|
|
110
|
+
* must be HMAC-SHA256(apiSecret, id) computed on the integrator's backend —
|
|
111
|
+
* never expose `apiSecret` to the browser.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* // Integrator backend (Node.js)
|
|
116
|
+
* const hash = crypto
|
|
117
|
+
* .createHmac("sha256", process.env.YAK_API_SECRET)
|
|
118
|
+
* .update(currentUser.id)
|
|
119
|
+
* .digest("hex");
|
|
120
|
+
*
|
|
121
|
+
* // Browser
|
|
122
|
+
* new YakClient({
|
|
123
|
+
* appId: "app_abc",
|
|
124
|
+
* user: { id: currentUser.id, hash },
|
|
125
|
+
* });
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
user?: UserIdentity;
|
|
107
129
|
}
|
|
108
130
|
export declare class YakClient {
|
|
109
131
|
private config;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAGV,KAAK,EACL,YAAY,EACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAEV,oBAAoB,EAEpB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAuD1B,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAC/B,uBAAuB,CAAC,EAAE,OAAO,CAAC;KACnC;CACF;AA+BD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;;;;;;;;;;;;;;;;OAkBG;IACH,mBAAmB,CAAC,EAAE,oBAAoB,CAAC;IAC3C;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IACrC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,EAAE,qBAAqB,CAAC;IAC3C,iCAAiC;IACjC,OAAO,CAAC,EAAE;QACR,uDAAuD;QACvD,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC;IACF;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAmD;IACtE,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAiC;gBAErC,MAAM,EAAE,eAAe;IAQ5B,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,eAAe,CAAC;IAUvD;;;OAGG;IACI,eAAe,IAAI,MAAM;IAIhC;;;;;;;;;OASG;IACI,WAAW,IAAI,MAAM;IAuB5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;OAEG;IACI,QAAQ,IAAI,MAAM;IAIzB;;OAEG;IACI,QAAQ,IAAI,KAAK,GAAG,SAAS;IAIpC;;;;;;;;;;OAUG;IACI,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAavC;;;OAGG;IACI,SAAS,IAAI,IAAI;IAYxB;;OAEG;IACI,OAAO,IAAI,OAAO;IAIlB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAOrC,aAAa,CAAC,MAAM,EAAE,OAAO;IAa7B,KAAK;IAOL,OAAO;IAQd,OAAO,CAAC,cAAc;IA+BtB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,cAAc,CAGpB;IAEF,OAAO,CAAC,aAAa,CAkInB;IAEF,OAAO,CAAC,kBAAkB;IAwC1B,OAAO,CAAC,eAAe;YAoBT,cAAc;YA4Bd,uBAAuB;YAuBvB,oBAAoB;IAuBlC,OAAO,CAAC,sBAAsB;IAuB9B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,mBAAmB;IAU3B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;CAsB1B"}
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isYakLoggingEnabled, logger } from "./logger.js";
|
|
2
|
+
import { debounce, extractPageContext } from "./page-context.js";
|
|
2
3
|
import { EMBED_PROTOCOL_VERSION } from "./version.js";
|
|
3
|
-
|
|
4
|
+
/** localStorage key for the per-app signed session token. */
|
|
5
|
+
const SESSION_STORAGE_KEY = (appId) => `yak:session:${appId}`;
|
|
6
|
+
/** localStorage key for the per-app active-conversation pointer. */
|
|
7
|
+
const CONVERSATION_POINTER_KEY = (appId) => `yak:conversation:${appId}`;
|
|
8
|
+
/** Read a stored session token for this app, if any. SSR-safe. */
|
|
9
|
+
function readStoredSessionToken(appId) {
|
|
10
|
+
if (typeof window === "undefined" || typeof window.localStorage === "undefined")
|
|
11
|
+
return undefined;
|
|
12
|
+
try {
|
|
13
|
+
return window.localStorage.getItem(SESSION_STORAGE_KEY(appId)) ?? undefined;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function writeStoredSessionToken(appId, token) {
|
|
20
|
+
if (typeof window === "undefined" || typeof window.localStorage === "undefined")
|
|
21
|
+
return;
|
|
22
|
+
try {
|
|
23
|
+
window.localStorage.setItem(SESSION_STORAGE_KEY(appId), token);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// localStorage can throw in private-browsing modes; silently ignore.
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function readStoredConversationPointer(appId) {
|
|
30
|
+
if (typeof window === "undefined" || typeof window.localStorage === "undefined")
|
|
31
|
+
return undefined;
|
|
32
|
+
try {
|
|
33
|
+
return window.localStorage.getItem(CONVERSATION_POINTER_KEY(appId)) ?? undefined;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function writeStoredConversationPointer(appId, pointer) {
|
|
40
|
+
if (typeof window === "undefined" || typeof window.localStorage === "undefined")
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
window.localStorage.setItem(CONVERSATION_POINTER_KEY(appId), pointer);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// localStorage can throw in private-browsing modes; silently ignore.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function clearStoredConversationPointer(appId) {
|
|
50
|
+
if (typeof window === "undefined" || typeof window.localStorage === "undefined")
|
|
51
|
+
return;
|
|
52
|
+
try {
|
|
53
|
+
window.localStorage.removeItem(CONVERSATION_POINTER_KEY(appId));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// localStorage can throw in private-browsing modes; silently ignore.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
4
59
|
/**
|
|
5
60
|
* Determines the iframe origin based on the current environment.
|
|
6
61
|
* - Internal dev (localhost on *.yak.* domain) -> http://localhost:3001
|
|
@@ -43,7 +98,10 @@ export class YakClient {
|
|
|
43
98
|
}
|
|
44
99
|
updateConfig(newConfig) {
|
|
45
100
|
this.config = { ...this.config, ...newConfig };
|
|
46
|
-
|
|
101
|
+
// Resend config when the iframe is ready and we have anything to deliver —
|
|
102
|
+
// tool/route manifests, or a user identity that needs to land in the
|
|
103
|
+
// widget so persistence/history light up.
|
|
104
|
+
if (this.readyTarget && (this.config.chatConfig || this.config.user)) {
|
|
47
105
|
this.sendConfigToIframe(this.readyTarget.window, this.readyTarget.origin);
|
|
48
106
|
}
|
|
49
107
|
}
|
|
@@ -220,7 +278,6 @@ export class YakClient {
|
|
|
220
278
|
logger.debug("Navigation detected, sending page context");
|
|
221
279
|
this.sendPageContext();
|
|
222
280
|
};
|
|
223
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: message handler requires branching per message type
|
|
224
281
|
handleMessage = (event) => {
|
|
225
282
|
if (typeof window === "undefined")
|
|
226
283
|
return;
|
|
@@ -304,6 +361,24 @@ export class YakClient {
|
|
|
304
361
|
}
|
|
305
362
|
break;
|
|
306
363
|
}
|
|
364
|
+
case "yak:session": {
|
|
365
|
+
const { sessionToken } = message.payload;
|
|
366
|
+
logger.debug("Session token received from iframe; persisting");
|
|
367
|
+
writeStoredSessionToken(this.config.appId, sessionToken);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
case "yak:conversation": {
|
|
371
|
+
const { pointer } = message.payload;
|
|
372
|
+
if (pointer === null) {
|
|
373
|
+
logger.debug("Conversation pointer cleared by iframe");
|
|
374
|
+
clearStoredConversationPointer(this.config.appId);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
logger.debug("Conversation pointer received from iframe; persisting");
|
|
378
|
+
writeStoredConversationPointer(this.config.appId, pointer);
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
307
382
|
case "yak:close": {
|
|
308
383
|
logger.debug("Close message received from iframe");
|
|
309
384
|
this.config.onClose?.();
|
|
@@ -319,6 +394,8 @@ export class YakClient {
|
|
|
319
394
|
const loggingEnabled = typeof window !== "undefined" && typeof window.__YAK_LOGGING_ENABLED__ === "boolean"
|
|
320
395
|
? window.__YAK_LOGGING_ENABLED__
|
|
321
396
|
: undefined;
|
|
397
|
+
const storedSessionToken = readStoredSessionToken(this.config.appId);
|
|
398
|
+
const storedConversationPointer = readStoredConversationPointer(this.config.appId);
|
|
322
399
|
const configMessage = {
|
|
323
400
|
type: "yak:config",
|
|
324
401
|
payload: {
|
|
@@ -330,6 +407,9 @@ export class YakClient {
|
|
|
330
407
|
schemaSources: this.config.chatConfig?.schemaSources ?? undefined,
|
|
331
408
|
options: this.config.options,
|
|
332
409
|
loggingEnabled,
|
|
410
|
+
user: this.config.user,
|
|
411
|
+
sessionToken: storedSessionToken,
|
|
412
|
+
conversationPointer: storedConversationPointer,
|
|
333
413
|
},
|
|
334
414
|
};
|
|
335
415
|
logger.debug("Posting config to iframe origin:", {
|
package/dist/embed.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { YakClient, type YakClientConfig } from "./client.js";
|
|
2
|
+
import type { ChatConfigProvider } from "./types/config.js";
|
|
3
|
+
import { type VoiceMachine } from "./voice-machine.js";
|
|
4
|
+
import { type VoiceStateListener, YakVoiceSession } from "./voice-session.js";
|
|
5
|
+
export type WidgetMode = "chat" | "voice" | "both";
|
|
2
6
|
export type TriggerButtonConfig = {
|
|
3
|
-
/** Label displayed on the trigger button. Default: "Ask with AI" */
|
|
4
|
-
label?: string;
|
|
5
7
|
/** Custom color overrides for light mode */
|
|
6
8
|
lightButton?: {
|
|
7
9
|
background?: string;
|
|
@@ -20,6 +22,20 @@ export type YakEmbedConfig = YakClientConfig & {
|
|
|
20
22
|
target?: HTMLElement;
|
|
21
23
|
/** Show the floating trigger button. Default: true */
|
|
22
24
|
trigger?: boolean | TriggerButtonConfig;
|
|
25
|
+
/**
|
|
26
|
+
* Which experiences this widget exposes.
|
|
27
|
+
* "chat" — chat icon only (opens the chat iframe).
|
|
28
|
+
* "voice" — voice icon only (starts a WebRTC voice session).
|
|
29
|
+
* "both" — both icons in one pill.
|
|
30
|
+
* Default: "chat".
|
|
31
|
+
*/
|
|
32
|
+
mode?: WidgetMode;
|
|
33
|
+
/**
|
|
34
|
+
* Async provider for chat config (routes + tools). Used by the voice
|
|
35
|
+
* session on every `start()` and by the iframe via postMessage. Takes
|
|
36
|
+
* precedence over the static `chatConfig` on `YakClientConfig`.
|
|
37
|
+
*/
|
|
38
|
+
getConfig?: ChatConfigProvider;
|
|
23
39
|
};
|
|
24
40
|
export type YakEmbedState = {
|
|
25
41
|
isOpen: boolean;
|
|
@@ -28,14 +44,16 @@ export type YakEmbedState = {
|
|
|
28
44
|
};
|
|
29
45
|
export type YakEmbedStateListener = (state: YakEmbedState) => void;
|
|
30
46
|
/**
|
|
31
|
-
* Drop-in widget that renders the yak
|
|
32
|
-
*
|
|
47
|
+
* Drop-in widget that renders the yak trigger pill plus, depending on mode,
|
|
48
|
+
* the chat iframe panel and/or a WebRTC voice session. Composes both
|
|
49
|
+
* `YakClient` (chat) and `YakVoiceSession` (voice) under one trigger.
|
|
33
50
|
*
|
|
34
51
|
* @example
|
|
35
52
|
* ```ts
|
|
36
53
|
* const embed = new YakEmbed({
|
|
37
54
|
* appId: "my-app",
|
|
38
|
-
*
|
|
55
|
+
* mode: "both",
|
|
56
|
+
* theme: { position: "bottom-left" },
|
|
39
57
|
* onToolCall: async (name, args) => { ... },
|
|
40
58
|
* });
|
|
41
59
|
* embed.mount();
|
|
@@ -43,29 +61,40 @@ export type YakEmbedStateListener = (state: YakEmbedState) => void;
|
|
|
43
61
|
*/
|
|
44
62
|
export declare class YakEmbed {
|
|
45
63
|
private readonly client;
|
|
64
|
+
private readonly voice;
|
|
46
65
|
private readonly config;
|
|
66
|
+
private readonly mode;
|
|
47
67
|
private styleEl;
|
|
48
68
|
private panelRoot;
|
|
49
69
|
private container;
|
|
50
70
|
private iframe;
|
|
51
|
-
private
|
|
71
|
+
private triggerEl;
|
|
72
|
+
private chatButton;
|
|
73
|
+
private voiceButton;
|
|
52
74
|
private isOpen;
|
|
53
75
|
private isReady;
|
|
54
76
|
private isExpanded;
|
|
55
77
|
private hasBeenOpened;
|
|
56
78
|
private pendingPrompt;
|
|
57
79
|
private mounted;
|
|
80
|
+
private voiceMachine;
|
|
58
81
|
private stateListeners;
|
|
82
|
+
private voiceListeners;
|
|
83
|
+
private unsubscribeVoice;
|
|
59
84
|
private mobileQuery;
|
|
60
85
|
private mobileHandler;
|
|
61
86
|
private expandHandler;
|
|
62
87
|
constructor(config: YakEmbedConfig);
|
|
63
88
|
/** The underlying headless YakClient for advanced usage */
|
|
64
89
|
getClient(): YakClient;
|
|
90
|
+
/** The underlying voice session — null when mode === "chat". */
|
|
91
|
+
getVoiceSession(): YakVoiceSession | null;
|
|
92
|
+
/** Current widget mode (immutable for the lifetime of the embed). */
|
|
93
|
+
getMode(): WidgetMode;
|
|
65
94
|
/**
|
|
66
95
|
* Mount the widget into the DOM. Call once after construction.
|
|
67
|
-
* Inserts styles and trigger button (if enabled). The iframe is
|
|
68
|
-
* created on the first call to open().
|
|
96
|
+
* Inserts styles and trigger button (if enabled). The chat iframe is
|
|
97
|
+
* lazily created on the first call to open().
|
|
69
98
|
*/
|
|
70
99
|
mount(target?: HTMLElement): void;
|
|
71
100
|
/** Remove all DOM elements and event listeners. */
|
|
@@ -82,12 +111,24 @@ export declare class YakEmbed {
|
|
|
82
111
|
getState(): YakEmbedState;
|
|
83
112
|
/** Subscribe to state changes. Returns an unsubscribe function. */
|
|
84
113
|
onStateChange(listener: YakEmbedStateListener): () => void;
|
|
114
|
+
/** Start a voice session. Must be invoked from a user gesture. */
|
|
115
|
+
voiceStart(): Promise<void>;
|
|
116
|
+
/** Stop the current voice session. */
|
|
117
|
+
voiceStop(): Promise<void>;
|
|
118
|
+
/** Toggle: start if idle/error, stop if active. */
|
|
119
|
+
voiceToggle(): Promise<void>;
|
|
120
|
+
/** Current voice machine snapshot. */
|
|
121
|
+
getVoiceState(): VoiceMachine;
|
|
122
|
+
/** Subscribe to voice state changes. */
|
|
123
|
+
onVoiceStateChange(listener: VoiceStateListener): () => void;
|
|
85
124
|
private createPanel;
|
|
86
125
|
private createTrigger;
|
|
87
126
|
private buildTriggerClasses;
|
|
88
127
|
private applyTriggerCustomColors;
|
|
89
128
|
private updatePanelState;
|
|
90
|
-
private
|
|
129
|
+
private updateChatButtonState;
|
|
130
|
+
private updateVoiceButtonState;
|
|
131
|
+
private iconForVoiceState;
|
|
91
132
|
private sendPendingPrompt;
|
|
92
133
|
private sendFocusIfOpen;
|
|
93
134
|
private notifyMobileState;
|
package/dist/embed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../src/embed.ts"],"names":[],"mappings":"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"}
|