@yak-io/react 0.2.0 → 0.4.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/YakProvider.d.ts +2 -72
- package/dist/YakProvider.d.ts.map +1 -1
- package/dist/YakProvider.js +271 -15
- package/dist/YakWidget.d.ts +20 -5
- package/dist/YakWidget.d.ts.map +1 -1
- package/dist/YakWidget.js +49 -178
- package/dist/context.d.ts +24 -12
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +19 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/logger.d.ts +3 -9
- package/dist/internal/logger.d.ts.map +1 -1
- package/dist/internal/logger.js +3 -42
- package/package.json +4 -4
package/dist/YakProvider.d.ts
CHANGED
|
@@ -10,90 +10,19 @@ export type YakProviderProps = {
|
|
|
10
10
|
* Provider function for chat configuration (routes + tools).
|
|
11
11
|
* The consuming platform decides how to get the config (static, fetch, etc.)
|
|
12
12
|
* Called when the widget is opened.
|
|
13
|
-
*
|
|
14
|
-
* @example Static config
|
|
15
|
-
* ```tsx
|
|
16
|
-
* getConfig={() => ({
|
|
17
|
-
* routes: { routes: [...], generated_at: "..." },
|
|
18
|
-
* tools: { tools: [...], generated_at: "..." },
|
|
19
|
-
* })}
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* @example Fetch from server
|
|
23
|
-
* ```tsx
|
|
24
|
-
* getConfig={async () => {
|
|
25
|
-
* const res = await fetch("/api/yak/config");
|
|
26
|
-
* return res.json();
|
|
27
|
-
* }}
|
|
28
|
-
* ```
|
|
29
13
|
*/
|
|
30
14
|
getConfig?: ChatConfigProvider;
|
|
31
15
|
/**
|
|
32
16
|
* Handler for tool calls from the chat widget.
|
|
33
17
|
* The consuming platform decides how to execute (browser, server fetch, etc.)
|
|
34
|
-
*
|
|
35
|
-
* @example Browser-only execution
|
|
36
|
-
* ```tsx
|
|
37
|
-
* onToolCall={async (name, args) => {
|
|
38
|
-
* if (name === "ui.scrollTo") {
|
|
39
|
-
* document.getElementById((args as {id: string}).id)?.scrollIntoView();
|
|
40
|
-
* return { success: true };
|
|
41
|
-
* }
|
|
42
|
-
* throw new Error(`Unknown tool: ${name}`);
|
|
43
|
-
* }}
|
|
44
|
-
* ```
|
|
45
|
-
*
|
|
46
|
-
* @example Server delegation
|
|
47
|
-
* ```tsx
|
|
48
|
-
* onToolCall={async (name, args) => {
|
|
49
|
-
* const res = await fetch("/api/yak/tools", {
|
|
50
|
-
* method: "POST",
|
|
51
|
-
* headers: { "Content-Type": "application/json" },
|
|
52
|
-
* body: JSON.stringify({ name, args }),
|
|
53
|
-
* });
|
|
54
|
-
* const data = await res.json();
|
|
55
|
-
* if (!data.ok) throw new Error(data.error);
|
|
56
|
-
* return data.result;
|
|
57
|
-
* }}
|
|
58
|
-
* ```
|
|
59
18
|
*/
|
|
60
19
|
onToolCall?: ToolCallHandler;
|
|
61
20
|
/**
|
|
62
21
|
* Handler for GraphQL schema tool calls.
|
|
63
|
-
* Called when the LLM generates a GraphQL operation based on provided schema context.
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* ```tsx
|
|
67
|
-
* onGraphQLSchemaCall={async (schemaName, request) => {
|
|
68
|
-
* const response = await fetch("/graphql", {
|
|
69
|
-
* method: "POST",
|
|
70
|
-
* headers: { "Content-Type": "application/json" },
|
|
71
|
-
* body: JSON.stringify(request),
|
|
72
|
-
* });
|
|
73
|
-
* return response.json();
|
|
74
|
-
* }}
|
|
75
|
-
* ```
|
|
76
22
|
*/
|
|
77
23
|
onGraphQLSchemaCall?: GraphQLSchemaHandler;
|
|
78
24
|
/**
|
|
79
25
|
* Handler for REST/OpenAPI schema tool calls.
|
|
80
|
-
* Called when the LLM generates a REST request based on provided OpenAPI schema context.
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```tsx
|
|
84
|
-
* onRESTSchemaCall={async (schemaName, request) => {
|
|
85
|
-
* const url = new URL(request.path, "https://api.example.com");
|
|
86
|
-
* if (request.query) {
|
|
87
|
-
* Object.entries(request.query).forEach(([k, v]) => url.searchParams.set(k, v));
|
|
88
|
-
* }
|
|
89
|
-
* const response = await fetch(url, {
|
|
90
|
-
* method: request.method,
|
|
91
|
-
* headers: request.body ? { "Content-Type": "application/json" } : undefined,
|
|
92
|
-
* body: request.body ? JSON.stringify(request.body) : undefined,
|
|
93
|
-
* });
|
|
94
|
-
* return response.json();
|
|
95
|
-
* }}
|
|
96
|
-
* ```
|
|
97
26
|
*/
|
|
98
27
|
onRESTSchemaCall?: RESTSchemaHandler;
|
|
99
28
|
/** Optional theme configuration */
|
|
@@ -106,7 +35,8 @@ export type YakProviderProps = {
|
|
|
106
35
|
children: React.ReactNode;
|
|
107
36
|
};
|
|
108
37
|
/**
|
|
109
|
-
* YakProvider sets up the context
|
|
38
|
+
* YakProvider sets up the context, message handling, and renders the chat iframe.
|
|
39
|
+
* The iframe is rendered as a fixed-position panel that can be opened/closed.
|
|
110
40
|
*/
|
|
111
41
|
export declare function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall, onRESTSchemaCall, theme, onRedirect, disableRestartButton, children, }: YakProviderProps): React.JSX.Element;
|
|
112
42
|
//# sourceMappingURL=YakProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"YakProvider.d.ts","sourceRoot":"","sources":["../src/YakProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAEjF,OAAO,EAEL,KAAK,KAAK,EAGV,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAEpB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd
|
|
1
|
+
{"version":3,"file":"YakProvider.d.ts","sourceRoot":"","sources":["../src/YakProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAEjF,OAAO,EAEL,KAAK,KAAK,EAGV,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAEpB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;OAEG;IACH,mBAAmB,CAAC,EAAE,oBAAoB,CAAC;IAC3C;;OAEG;IACH,gBAAgB,CAAC,EAAE,iBAAiB,CAAC;IACrC,mCAAmC;IACnC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,qEAAqE;IACrE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,uDAAuD;IACvD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,0BAA0B;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAmMF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,EACL,UAAU,EACV,oBAAoB,EACpB,QAAQ,GACT,EAAE,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAwUtC"}
|
package/dist/YakProvider.js
CHANGED
|
@@ -1,11 +1,204 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useMemo, useEffect, useRef } from "react";
|
|
4
4
|
import { YakContext } from "./context.js";
|
|
5
5
|
import { YakClient, } from "@yak-io/javascript";
|
|
6
6
|
import { logger } from "./internal/logger.js";
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* All iframe/panel styles consolidated in one place
|
|
9
|
+
*/
|
|
10
|
+
function getIframeStyles() {
|
|
11
|
+
return `
|
|
12
|
+
/* ===========================================
|
|
13
|
+
YAK IFRAME STYLES (rendered by provider)
|
|
14
|
+
=========================================== */
|
|
15
|
+
|
|
16
|
+
/* ===========================================
|
|
17
|
+
ROOT LAYER (full viewport, pointer-events pass-through)
|
|
18
|
+
=========================================== */
|
|
19
|
+
.yak-panel-root {
|
|
20
|
+
position: fixed;
|
|
21
|
+
top: 0;
|
|
22
|
+
left: 0;
|
|
23
|
+
right: 0;
|
|
24
|
+
bottom: 0;
|
|
25
|
+
width: 100vw;
|
|
26
|
+
height: 100vh;
|
|
27
|
+
pointer-events: none;
|
|
28
|
+
z-index: 9998;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* ===========================================
|
|
32
|
+
CONTAINER
|
|
33
|
+
=========================================== */
|
|
34
|
+
.yak-panel-container {
|
|
35
|
+
position: absolute;
|
|
36
|
+
width: 500px;
|
|
37
|
+
height: 600px;
|
|
38
|
+
max-width: calc(100vw - 40px);
|
|
39
|
+
max-height: calc(100vh - 120px);
|
|
40
|
+
border-radius: 15px;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
background-color: transparent;
|
|
43
|
+
pointer-events: auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Container position variants (chatbox mode) - all 9 positions */
|
|
47
|
+
.yak-panel-container[data-position="top-left"]:not(.yak-panel-drawer) {
|
|
48
|
+
top: 16px;
|
|
49
|
+
left: 16px;
|
|
50
|
+
}
|
|
51
|
+
.yak-panel-container[data-position="top-center"]:not(.yak-panel-drawer) {
|
|
52
|
+
top: 16px;
|
|
53
|
+
left: 50%;
|
|
54
|
+
transform: translateX(-50%);
|
|
55
|
+
}
|
|
56
|
+
.yak-panel-container[data-position="top-right"]:not(.yak-panel-drawer) {
|
|
57
|
+
top: 16px;
|
|
58
|
+
right: 16px;
|
|
59
|
+
}
|
|
60
|
+
.yak-panel-container[data-position="left-center"]:not(.yak-panel-drawer) {
|
|
61
|
+
top: 50%;
|
|
62
|
+
left: 16px;
|
|
63
|
+
transform: translateY(-50%);
|
|
64
|
+
}
|
|
65
|
+
.yak-panel-container[data-position="right-center"]:not(.yak-panel-drawer) {
|
|
66
|
+
top: 50%;
|
|
67
|
+
right: 16px;
|
|
68
|
+
transform: translateY(-50%);
|
|
69
|
+
}
|
|
70
|
+
.yak-panel-container[data-position="bottom-left"]:not(.yak-panel-drawer) {
|
|
71
|
+
bottom: 16px;
|
|
72
|
+
left: 16px;
|
|
73
|
+
}
|
|
74
|
+
.yak-panel-container[data-position="bottom-center"]:not(.yak-panel-drawer) {
|
|
75
|
+
bottom: 16px;
|
|
76
|
+
left: 50%;
|
|
77
|
+
transform: translateX(-50%);
|
|
78
|
+
}
|
|
79
|
+
.yak-panel-container[data-position="bottom-right"]:not(.yak-panel-drawer) {
|
|
80
|
+
bottom: 16px;
|
|
81
|
+
right: 16px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Container visibility (chatbox mode) */
|
|
85
|
+
.yak-panel-container:not(.yak-panel-drawer) {
|
|
86
|
+
display: none;
|
|
87
|
+
}
|
|
88
|
+
.yak-panel-container:not(.yak-panel-drawer)[data-open="true"] {
|
|
89
|
+
display: block;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* ===========================================
|
|
93
|
+
EXPANDED MODE (full viewport for data table)
|
|
94
|
+
=========================================== */
|
|
95
|
+
.yak-panel-container[data-expanded="true"] {
|
|
96
|
+
width: calc(100vw - 32px) !important;
|
|
97
|
+
height: calc(100vh - 32px) !important;
|
|
98
|
+
max-width: none !important;
|
|
99
|
+
max-height: none !important;
|
|
100
|
+
top: 16px !important;
|
|
101
|
+
left: 16px !important;
|
|
102
|
+
right: 16px !important;
|
|
103
|
+
bottom: 16px !important;
|
|
104
|
+
border-radius: 15px !important;
|
|
105
|
+
border: 1px solid rgba(0, 0, 0, 0.1) !important;
|
|
106
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15) !important;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@media (prefers-color-scheme: dark) {
|
|
110
|
+
.yak-panel-container[data-expanded="true"]:not(.yak-panel-light) {
|
|
111
|
+
border-color: rgba(255, 255, 255, 0.1) !important;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.yak-panel-container.yak-panel-dark[data-expanded="true"] {
|
|
116
|
+
border-color: rgba(255, 255, 255, 0.1) !important;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.yak-panel-container.yak-panel-light[data-expanded="true"] {
|
|
120
|
+
border-color: rgba(0, 0, 0, 0.1) !important;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ===========================================
|
|
124
|
+
DRAWER MODE
|
|
125
|
+
=========================================== */
|
|
126
|
+
.yak-panel-container.yak-panel-drawer {
|
|
127
|
+
height: calc(100% - 32px);
|
|
128
|
+
max-width: 100vw;
|
|
129
|
+
max-height: none;
|
|
130
|
+
border-radius: 15px;
|
|
131
|
+
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Drawer position - only left-center and right-center are relevant for drawer mode */
|
|
135
|
+
.yak-panel-container.yak-panel-drawer[data-position="left-center"],
|
|
136
|
+
.yak-panel-container.yak-panel-drawer[data-position="top-left"],
|
|
137
|
+
.yak-panel-container.yak-panel-drawer[data-position="bottom-left"] {
|
|
138
|
+
top: 16px;
|
|
139
|
+
left: 16px;
|
|
140
|
+
bottom: 16px;
|
|
141
|
+
transform: translateX(calc(-100% - 16px));
|
|
142
|
+
}
|
|
143
|
+
.yak-panel-container.yak-panel-drawer[data-position="right-center"],
|
|
144
|
+
.yak-panel-container.yak-panel-drawer[data-position="top-right"],
|
|
145
|
+
.yak-panel-container.yak-panel-drawer[data-position="bottom-right"] {
|
|
146
|
+
top: 16px;
|
|
147
|
+
right: 16px;
|
|
148
|
+
bottom: 16px;
|
|
149
|
+
transform: translateX(calc(100% + 16px));
|
|
150
|
+
}
|
|
151
|
+
/* Center positions default to right side for drawer */
|
|
152
|
+
.yak-panel-container.yak-panel-drawer[data-position="top-center"],
|
|
153
|
+
.yak-panel-container.yak-panel-drawer[data-position="bottom-center"] {
|
|
154
|
+
top: 16px;
|
|
155
|
+
right: 16px;
|
|
156
|
+
bottom: 16px;
|
|
157
|
+
transform: translateX(calc(100% + 16px));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Drawer open state */
|
|
161
|
+
.yak-panel-container.yak-panel-drawer[data-open="true"] {
|
|
162
|
+
transform: translateX(0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ===========================================
|
|
166
|
+
IFRAME
|
|
167
|
+
=========================================== */
|
|
168
|
+
.yak-panel-iframe {
|
|
169
|
+
position: absolute;
|
|
170
|
+
inset: 0;
|
|
171
|
+
width: 100%;
|
|
172
|
+
height: 100%;
|
|
173
|
+
border: none;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* ===========================================
|
|
177
|
+
MOBILE RESPONSIVE
|
|
178
|
+
=========================================== */
|
|
179
|
+
@media (max-width: 640px) {
|
|
180
|
+
.yak-panel-container:not(.yak-panel-drawer) {
|
|
181
|
+
width: 100% !important;
|
|
182
|
+
height: 100% !important;
|
|
183
|
+
height: 100dvh !important;
|
|
184
|
+
max-width: none !important;
|
|
185
|
+
max-height: none !important;
|
|
186
|
+
top: 0 !important;
|
|
187
|
+
left: 0 !important;
|
|
188
|
+
right: 0 !important;
|
|
189
|
+
bottom: 0 !important;
|
|
190
|
+
border-radius: 0 !important;
|
|
191
|
+
}
|
|
192
|
+
.yak-panel-container.yak-panel-drawer {
|
|
193
|
+
width: 100% !important;
|
|
194
|
+
max-width: none !important;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* YakProvider sets up the context, message handling, and renders the chat iframe.
|
|
201
|
+
* The iframe is rendered as a fixed-position panel that can be opened/closed.
|
|
9
202
|
*/
|
|
10
203
|
export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall, onRESTSchemaCall, theme, onRedirect, disableRestartButton, children, }) {
|
|
11
204
|
const [iframeWindow, setIframeWindow] = useState(null);
|
|
@@ -13,6 +206,9 @@ export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall,
|
|
|
13
206
|
const [isWidgetOpen, setIsWidgetOpen] = useState(false);
|
|
14
207
|
const [pendingPrompt, setPendingPrompt] = useState(null);
|
|
15
208
|
const [isIframeReady, setIsIframeReady] = useState(false);
|
|
209
|
+
const [hasBeenOpened, setHasBeenOpened] = useState(false);
|
|
210
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
211
|
+
const iframeRef = useRef(null);
|
|
16
212
|
// Store event subscribers for tool call events
|
|
17
213
|
const toolEventSubscribersRef = useRef(new Set());
|
|
18
214
|
// Handler that notifies all subscribers when a tool call completes
|
|
@@ -50,6 +246,7 @@ export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall,
|
|
|
50
246
|
const client = clientRef.current;
|
|
51
247
|
// Get the iframe origin from the client (computed based on environment)
|
|
52
248
|
const iframeOrigin = client.getIframeOrigin();
|
|
249
|
+
const iframeSrc = client.getEmbedUrl();
|
|
53
250
|
// Update client config when props change
|
|
54
251
|
useEffect(() => {
|
|
55
252
|
client.updateConfig({
|
|
@@ -77,6 +274,64 @@ export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall,
|
|
|
77
274
|
client.mount();
|
|
78
275
|
return () => client.unmount();
|
|
79
276
|
}, [client]);
|
|
277
|
+
// Track when widget is first opened
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
if (isWidgetOpen && !hasBeenOpened) {
|
|
280
|
+
setHasBeenOpened(true);
|
|
281
|
+
}
|
|
282
|
+
}, [isWidgetOpen, hasBeenOpened]);
|
|
283
|
+
// Register iframe window when loaded
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
if (!hasBeenOpened)
|
|
286
|
+
return;
|
|
287
|
+
const iframe = iframeRef.current;
|
|
288
|
+
if (!iframe)
|
|
289
|
+
return;
|
|
290
|
+
const handleLoad = () => {
|
|
291
|
+
if (iframe.contentWindow) {
|
|
292
|
+
logger.debug("Iframe window registered");
|
|
293
|
+
setIframeWindow(iframe.contentWindow);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
iframe.addEventListener("load", handleLoad);
|
|
297
|
+
return () => {
|
|
298
|
+
iframe.removeEventListener("load", handleLoad);
|
|
299
|
+
logger.debug("Iframe window unregistered");
|
|
300
|
+
setIframeWindow(null);
|
|
301
|
+
};
|
|
302
|
+
}, [hasBeenOpened]);
|
|
303
|
+
// Listen for expansion messages from iframe
|
|
304
|
+
useEffect(() => {
|
|
305
|
+
const handleMessage = (event) => {
|
|
306
|
+
const iframe = iframeRef.current;
|
|
307
|
+
if (!iframe)
|
|
308
|
+
return;
|
|
309
|
+
// Check for expansion control messages
|
|
310
|
+
if (event.data?.type === "YAK_SET_EXPANDED") {
|
|
311
|
+
setIsExpanded(Boolean(event.data.expanded));
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
window.addEventListener("message", handleMessage);
|
|
315
|
+
return () => window.removeEventListener("message", handleMessage);
|
|
316
|
+
}, []);
|
|
317
|
+
// Detect mobile/fullscreen and notify iframe
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
if (!isIframeReady)
|
|
320
|
+
return;
|
|
321
|
+
const mobileQuery = window.matchMedia("(max-width: 640px)");
|
|
322
|
+
const notifyFullscreen = (isFullscreen) => {
|
|
323
|
+
const msg = { type: "yak:viewport", payload: { fullscreen: isFullscreen } };
|
|
324
|
+
iframeWindow?.postMessage(msg, iframeOrigin);
|
|
325
|
+
};
|
|
326
|
+
// Send initial state
|
|
327
|
+
notifyFullscreen(mobileQuery.matches);
|
|
328
|
+
// Listen for changes
|
|
329
|
+
const handleChange = (e) => {
|
|
330
|
+
notifyFullscreen(e.matches);
|
|
331
|
+
};
|
|
332
|
+
mobileQuery.addEventListener("change", handleChange);
|
|
333
|
+
return () => mobileQuery.removeEventListener("change", handleChange);
|
|
334
|
+
}, [isIframeReady, iframeWindow, iframeOrigin]);
|
|
80
335
|
// Get chat config when widget is opened
|
|
81
336
|
useEffect(() => {
|
|
82
337
|
if (typeof window === "undefined" || !isWidgetOpen || !getConfig)
|
|
@@ -117,14 +372,6 @@ export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall,
|
|
|
117
372
|
// Methods to get URLs from the client
|
|
118
373
|
const getIframeOrigin = useCallback(() => client.getIframeOrigin(), [client]);
|
|
119
374
|
const getEmbedUrl = useCallback(() => client.getEmbedUrl(), [client]);
|
|
120
|
-
const registerIframeWindow = useCallback((win) => {
|
|
121
|
-
logger.debug("Iframe window registered");
|
|
122
|
-
setIframeWindow(win);
|
|
123
|
-
}, []);
|
|
124
|
-
const unregisterIframeWindow = useCallback(() => {
|
|
125
|
-
logger.debug("Iframe window unregistered");
|
|
126
|
-
setIframeWindow(null);
|
|
127
|
-
}, []);
|
|
128
375
|
const sendMessage = useCallback((message) => {
|
|
129
376
|
iframeWindow?.postMessage(message, iframeOrigin);
|
|
130
377
|
}, [iframeWindow, iframeOrigin]);
|
|
@@ -175,8 +422,6 @@ export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall,
|
|
|
175
422
|
config,
|
|
176
423
|
getIframeOrigin,
|
|
177
424
|
getEmbedUrl,
|
|
178
|
-
registerIframeWindow,
|
|
179
|
-
unregisterIframeWindow,
|
|
180
425
|
sendMessage,
|
|
181
426
|
isOpen: isWidgetOpen,
|
|
182
427
|
isIframeReady,
|
|
@@ -189,8 +434,6 @@ export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall,
|
|
|
189
434
|
config,
|
|
190
435
|
getIframeOrigin,
|
|
191
436
|
getEmbedUrl,
|
|
192
|
-
registerIframeWindow,
|
|
193
|
-
unregisterIframeWindow,
|
|
194
437
|
sendMessage,
|
|
195
438
|
isWidgetOpen,
|
|
196
439
|
isIframeReady,
|
|
@@ -199,5 +442,18 @@ export function YakProvider({ appId, getConfig, onToolCall, onGraphQLSchemaCall,
|
|
|
199
442
|
openWithPrompt,
|
|
200
443
|
subscribeToToolEvents,
|
|
201
444
|
]);
|
|
202
|
-
|
|
445
|
+
// Determine position styles based on theme
|
|
446
|
+
const position = theme?.position ?? "bottom-right";
|
|
447
|
+
const colorMode = theme?.colorMode;
|
|
448
|
+
const displayMode = theme?.displayMode ?? "chatbox";
|
|
449
|
+
const isDrawer = displayMode === "drawer";
|
|
450
|
+
// Determine color mode class for the container
|
|
451
|
+
const colorModeClass = colorMode === "light" ? "yak-panel-light" : colorMode === "dark" ? "yak-panel-dark" : "";
|
|
452
|
+
// Build container class names
|
|
453
|
+
const containerClasses = [
|
|
454
|
+
"yak-panel-container",
|
|
455
|
+
isDrawer && "yak-panel-drawer",
|
|
456
|
+
colorModeClass,
|
|
457
|
+
].filter(Boolean).join(" ");
|
|
458
|
+
return (_jsxs(YakContext.Provider, { value: contextValue, children: [children, _jsx("style", { children: getIframeStyles() }), hasBeenOpened && (_jsx("div", { className: "yak-panel-root", "data-expanded": isExpanded, children: _jsx("div", { className: containerClasses, "data-position": position, "data-open": isWidgetOpen && isIframeReady, "data-expanded": isExpanded, children: _jsx("iframe", { ref: iframeRef, src: iframeSrc, className: "yak-panel-iframe", title: "yak-chat-host" }) }) }))] }));
|
|
203
459
|
}
|
package/dist/YakWidget.d.ts
CHANGED
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { WidgetPosition } from "@yak-io/javascript";
|
|
2
3
|
/**
|
|
3
4
|
* Props for YakWidget
|
|
4
5
|
*/
|
|
5
6
|
export type YakWidgetProps = {
|
|
6
|
-
/** Optional CSS class name for the iframe */
|
|
7
|
-
iframeClassName?: string;
|
|
8
7
|
/** Text to display next to the logo */
|
|
9
8
|
triggerLabel?: string;
|
|
9
|
+
/** Position of the button (defaults to theme position or "bottom-right") */
|
|
10
|
+
position?: WidgetPosition;
|
|
11
|
+
/** Color mode override */
|
|
12
|
+
colorMode?: "light" | "dark" | "system";
|
|
13
|
+
/** Custom button colors for light mode */
|
|
14
|
+
lightButton?: {
|
|
15
|
+
background?: string;
|
|
16
|
+
color?: string;
|
|
17
|
+
border?: string;
|
|
18
|
+
};
|
|
19
|
+
/** Custom button colors for dark mode */
|
|
20
|
+
darkButton?: {
|
|
21
|
+
background?: string;
|
|
22
|
+
color?: string;
|
|
23
|
+
border?: string;
|
|
24
|
+
};
|
|
10
25
|
};
|
|
11
26
|
/**
|
|
12
|
-
* YakWidget renders a fixed-position launcher button
|
|
13
|
-
* The iframe
|
|
27
|
+
* YakWidget renders a fixed-position launcher button.
|
|
28
|
+
* The chat iframe is rendered by YakProvider - this is just the trigger button.
|
|
14
29
|
*/
|
|
15
|
-
export declare function YakWidget({
|
|
30
|
+
export declare function YakWidget({ triggerLabel, position, colorMode, lightButton, darkButton, }?: YakWidgetProps): React.JSX.Element;
|
|
16
31
|
//# sourceMappingURL=YakWidget.d.ts.map
|
package/dist/YakWidget.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"YakWidget.d.ts","sourceRoot":"","sources":["../src/YakWidget.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"YakWidget.d.ts","sourceRoot":"","sources":["../src/YakWidget.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAiPzD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,0BAA0B;IAC1B,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACxC,0CAA0C;IAC1C,WAAW,CAAC,EAAE;QACZ,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,yCAAyC;IACzC,UAAU,CAAC,EAAE;QACX,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,YAA4B,EAC5B,QAAyB,EACzB,SAAS,EACT,WAAW,EACX,UAAU,GACX,GAAE,cAAmB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAkEzC"}
|
package/dist/YakWidget.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useEffect, useState } from "react";
|
|
4
3
|
import { useYak } from "./context.js";
|
|
5
4
|
/**
|
|
6
|
-
* All
|
|
5
|
+
* All button styles consolidated in one place
|
|
7
6
|
*/
|
|
8
|
-
function
|
|
7
|
+
function getButtonStyles() {
|
|
9
8
|
return `
|
|
10
9
|
/* ===========================================
|
|
11
|
-
YAK WIDGET STYLES
|
|
10
|
+
YAK WIDGET BUTTON STYLES
|
|
12
11
|
=========================================== */
|
|
13
12
|
|
|
14
13
|
/* Trigger Button Base */
|
|
@@ -32,13 +31,47 @@ function getWidgetStyles() {
|
|
|
32
31
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
/* Trigger position variants */
|
|
36
|
-
.yak-widget-trigger[data-position="left"] {
|
|
34
|
+
/* Trigger position variants - all 9 positions */
|
|
35
|
+
.yak-widget-trigger[data-position="top-left"] {
|
|
36
|
+
top: 28px;
|
|
37
|
+
left: 28px;
|
|
38
|
+
flex-direction: row-reverse;
|
|
39
|
+
}
|
|
40
|
+
.yak-widget-trigger[data-position="top-center"] {
|
|
41
|
+
top: 28px;
|
|
42
|
+
left: 50%;
|
|
43
|
+
transform: translateX(-50%);
|
|
44
|
+
flex-direction: row;
|
|
45
|
+
}
|
|
46
|
+
.yak-widget-trigger[data-position="top-right"] {
|
|
47
|
+
top: 28px;
|
|
48
|
+
right: 28px;
|
|
49
|
+
flex-direction: row;
|
|
50
|
+
}
|
|
51
|
+
.yak-widget-trigger[data-position="left-center"] {
|
|
52
|
+
top: 50%;
|
|
53
|
+
left: 28px;
|
|
54
|
+
transform: translateY(-50%);
|
|
55
|
+
flex-direction: row-reverse;
|
|
56
|
+
}
|
|
57
|
+
.yak-widget-trigger[data-position="right-center"] {
|
|
58
|
+
top: 50%;
|
|
59
|
+
right: 28px;
|
|
60
|
+
transform: translateY(-50%);
|
|
61
|
+
flex-direction: row;
|
|
62
|
+
}
|
|
63
|
+
.yak-widget-trigger[data-position="bottom-left"] {
|
|
37
64
|
bottom: 28px;
|
|
38
65
|
left: 28px;
|
|
39
66
|
flex-direction: row-reverse;
|
|
40
67
|
}
|
|
41
|
-
.yak-widget-trigger[data-position="
|
|
68
|
+
.yak-widget-trigger[data-position="bottom-center"] {
|
|
69
|
+
bottom: 28px;
|
|
70
|
+
left: 50%;
|
|
71
|
+
transform: translateX(-50%);
|
|
72
|
+
flex-direction: row;
|
|
73
|
+
}
|
|
74
|
+
.yak-widget-trigger[data-position="bottom-right"] {
|
|
42
75
|
bottom: 28px;
|
|
43
76
|
right: 28px;
|
|
44
77
|
flex-direction: row;
|
|
@@ -85,7 +118,6 @@ function getWidgetStyles() {
|
|
|
85
118
|
/* Loading/disabled state for trigger button */
|
|
86
119
|
.yak-widget-trigger:disabled {
|
|
87
120
|
cursor: wait;
|
|
88
|
-
opacity: 0.8;
|
|
89
121
|
}
|
|
90
122
|
|
|
91
123
|
/* Custom button styles for forced light mode */
|
|
@@ -171,122 +203,6 @@ function getWidgetStyles() {
|
|
|
171
203
|
.yak-widget-trigger.yak-widget-dark:not(.yak-widget-custom-dark) .yak-widget-icon-bg {
|
|
172
204
|
background-color: rgba(255, 255, 255, 0.1);
|
|
173
205
|
}
|
|
174
|
-
|
|
175
|
-
/* ===========================================
|
|
176
|
-
BACKDROP
|
|
177
|
-
=========================================== */
|
|
178
|
-
.yak-widget-backdrop {
|
|
179
|
-
position: fixed;
|
|
180
|
-
top: 0;
|
|
181
|
-
left: 0;
|
|
182
|
-
right: 0;
|
|
183
|
-
bottom: 0;
|
|
184
|
-
background-color: rgba(0, 0, 0, 0.5);
|
|
185
|
-
z-index: 9997;
|
|
186
|
-
opacity: 0;
|
|
187
|
-
visibility: hidden;
|
|
188
|
-
transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out;
|
|
189
|
-
}
|
|
190
|
-
.yak-widget-backdrop[data-open="true"] {
|
|
191
|
-
opacity: 1;
|
|
192
|
-
visibility: visible;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/* ===========================================
|
|
196
|
-
CONTAINER
|
|
197
|
-
=========================================== */
|
|
198
|
-
.yak-widget-container {
|
|
199
|
-
position: fixed;
|
|
200
|
-
width: 500px;
|
|
201
|
-
height: 600px;
|
|
202
|
-
max-width: calc(100vw - 40px);
|
|
203
|
-
max-height: calc(100vh - 120px);
|
|
204
|
-
border-radius: 15px;
|
|
205
|
-
overflow: hidden;
|
|
206
|
-
z-index: 9998;
|
|
207
|
-
background-color: transparent;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/* Container position variants (chatbox mode) */
|
|
211
|
-
.yak-widget-container[data-position="left"]:not(.yak-widget-drawer) {
|
|
212
|
-
bottom: 16px;
|
|
213
|
-
left: 16px;
|
|
214
|
-
}
|
|
215
|
-
.yak-widget-container[data-position="right"]:not(.yak-widget-drawer) {
|
|
216
|
-
bottom: 16px;
|
|
217
|
-
right: 16px;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/* Container visibility (chatbox mode) */
|
|
221
|
-
.yak-widget-container:not(.yak-widget-drawer) {
|
|
222
|
-
display: none;
|
|
223
|
-
}
|
|
224
|
-
.yak-widget-container:not(.yak-widget-drawer)[data-open="true"] {
|
|
225
|
-
display: block;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/* ===========================================
|
|
229
|
-
DRAWER MODE
|
|
230
|
-
=========================================== */
|
|
231
|
-
.yak-widget-container.yak-widget-drawer {
|
|
232
|
-
height: calc(100% - 32px);
|
|
233
|
-
max-width: 100vw;
|
|
234
|
-
max-height: none;
|
|
235
|
-
border-radius: 15px;
|
|
236
|
-
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/* Drawer position */
|
|
240
|
-
.yak-widget-container.yak-widget-drawer[data-position="left"] {
|
|
241
|
-
top: 16px;
|
|
242
|
-
left: 16px;
|
|
243
|
-
bottom: 16px;
|
|
244
|
-
transform: translateX(calc(-100% - 16px));
|
|
245
|
-
}
|
|
246
|
-
.yak-widget-container.yak-widget-drawer[data-position="right"] {
|
|
247
|
-
top: 16px;
|
|
248
|
-
right: 16px;
|
|
249
|
-
bottom: 16px;
|
|
250
|
-
transform: translateX(calc(100% + 16px));
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/* Drawer open state */
|
|
254
|
-
.yak-widget-container.yak-widget-drawer[data-open="true"] {
|
|
255
|
-
transform: translateX(0);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/* ===========================================
|
|
259
|
-
IFRAME
|
|
260
|
-
=========================================== */
|
|
261
|
-
.yak-widget-iframe {
|
|
262
|
-
position: absolute;
|
|
263
|
-
inset: 0;
|
|
264
|
-
width: 100%;
|
|
265
|
-
height: 100%;
|
|
266
|
-
border: none;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/* ===========================================
|
|
270
|
-
MOBILE RESPONSIVE
|
|
271
|
-
=========================================== */
|
|
272
|
-
@media (max-width: 640px) {
|
|
273
|
-
.yak-widget-container:not(.yak-widget-drawer) {
|
|
274
|
-
width: 100% !important;
|
|
275
|
-
height: 100% !important;
|
|
276
|
-
height: 100dvh !important;
|
|
277
|
-
max-width: none !important;
|
|
278
|
-
max-height: none !important;
|
|
279
|
-
top: 0 !important;
|
|
280
|
-
left: 0 !important;
|
|
281
|
-
right: 0 !important;
|
|
282
|
-
bottom: 0 !important;
|
|
283
|
-
border-radius: 0 !important;
|
|
284
|
-
}
|
|
285
|
-
.yak-widget-container.yak-widget-drawer {
|
|
286
|
-
width: 100% !important;
|
|
287
|
-
max-width: none !important;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
206
|
`;
|
|
291
207
|
}
|
|
292
208
|
/**
|
|
@@ -296,53 +212,18 @@ function BrainCircuitIcon({ size = 20, className }) {
|
|
|
296
212
|
return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: className, children: [_jsx("path", { d: "M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z" }), _jsx("path", { d: "M9 13a4.5 4.5 0 0 0 3-4" }), _jsx("path", { d: "M6.003 5.125A3 3 0 0 0 6.401 6.5" }), _jsx("path", { d: "M3.477 10.896a4 4 0 0 1 .585-.396" }), _jsx("path", { d: "M6 18a4 4 0 0 1-1.967-.516" }), _jsx("path", { d: "M12 13h4" }), _jsx("path", { d: "M12 18h6a2 2 0 0 1 2 2v1" }), _jsx("path", { d: "M12 8h8" }), _jsx("path", { d: "M16 8V5a2 2 0 0 1 2-2" }), _jsx("circle", { cx: "16", cy: "13", r: ".5" }), _jsx("circle", { cx: "18", cy: "3", r: ".5" }), _jsx("circle", { cx: "20", cy: "21", r: ".5" }), _jsx("circle", { cx: "20", cy: "8", r: ".5" })] }));
|
|
297
213
|
}
|
|
298
214
|
/**
|
|
299
|
-
* YakWidget renders a fixed-position launcher button
|
|
300
|
-
* The iframe
|
|
215
|
+
* YakWidget renders a fixed-position launcher button.
|
|
216
|
+
* The chat iframe is rendered by YakProvider - this is just the trigger button.
|
|
301
217
|
*/
|
|
302
|
-
export function YakWidget({
|
|
303
|
-
const {
|
|
304
|
-
const iframeRef = useRef(null);
|
|
305
|
-
const [hasBeenOpened, setHasBeenOpened] = useState(false);
|
|
218
|
+
export function YakWidget({ triggerLabel = "Ask with AI", position = "bottom-right", colorMode, lightButton, darkButton, } = {}) {
|
|
219
|
+
const { open, isOpen, isIframeReady } = useYak();
|
|
306
220
|
// Track if we're in a loading state (open but iframe not ready)
|
|
307
|
-
const isLoading = isOpen &&
|
|
308
|
-
//
|
|
309
|
-
const iframeSrc = getEmbedUrl();
|
|
310
|
-
// Track when widget is first opened
|
|
311
|
-
useEffect(() => {
|
|
312
|
-
if (isOpen && !hasBeenOpened) {
|
|
313
|
-
setHasBeenOpened(true);
|
|
314
|
-
}
|
|
315
|
-
}, [isOpen, hasBeenOpened]);
|
|
316
|
-
// Register iframe window when loaded
|
|
317
|
-
useEffect(() => {
|
|
318
|
-
if (!hasBeenOpened)
|
|
319
|
-
return;
|
|
320
|
-
const iframe = iframeRef.current;
|
|
321
|
-
if (!iframe)
|
|
322
|
-
return;
|
|
323
|
-
const handleLoad = () => {
|
|
324
|
-
if (iframe.contentWindow) {
|
|
325
|
-
registerIframeWindow(iframe.contentWindow);
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
iframe.addEventListener("load", handleLoad);
|
|
329
|
-
return () => {
|
|
330
|
-
iframe.removeEventListener("load", handleLoad);
|
|
331
|
-
unregisterIframeWindow();
|
|
332
|
-
};
|
|
333
|
-
}, [registerIframeWindow, unregisterIframeWindow, hasBeenOpened]);
|
|
334
|
-
// Determine position styles based on theme
|
|
335
|
-
const position = config.theme?.position ?? "right";
|
|
336
|
-
const colorMode = config.theme?.colorMode;
|
|
337
|
-
const displayMode = config.theme?.displayMode ?? "chatbox";
|
|
338
|
-
const isDrawer = displayMode === "drawer";
|
|
339
|
-
// Determine color mode class for the widget
|
|
340
|
-
const colorModeClass = colorMode === "light" ? "yak-widget-light" : colorMode === "dark" ? "yak-widget-dark" : "";
|
|
341
|
-
// Check if custom button theme is provided (now nested in light/dark)
|
|
342
|
-
const lightButton = config.theme?.light?.button;
|
|
343
|
-
const darkButton = config.theme?.dark?.button;
|
|
221
|
+
const isLoading = isOpen && !isIframeReady;
|
|
222
|
+
// Check if custom button theme is provided
|
|
344
223
|
const hasLightCustom = lightButton?.background || lightButton?.color || lightButton?.border;
|
|
345
224
|
const hasDarkCustom = darkButton?.background || darkButton?.color || darkButton?.border;
|
|
225
|
+
// Determine color mode class for the widget
|
|
226
|
+
const colorModeClass = colorMode === "light" ? "yak-widget-light" : colorMode === "dark" ? "yak-widget-dark" : "";
|
|
346
227
|
// Determine which custom class to apply based on color mode
|
|
347
228
|
let customButtonClass = "";
|
|
348
229
|
if (colorMode === "light" && hasLightCustom) {
|
|
@@ -351,10 +232,6 @@ export function YakWidget({ iframeClassName, triggerLabel = "Ask with AI", } = {
|
|
|
351
232
|
else if (colorMode === "dark" && hasDarkCustom) {
|
|
352
233
|
customButtonClass = "yak-widget-custom-dark";
|
|
353
234
|
}
|
|
354
|
-
else if (colorMode === "system" || colorMode === undefined) {
|
|
355
|
-
// For system mode, we need to handle both via media queries
|
|
356
|
-
// We'll use a data attribute approach instead
|
|
357
|
-
}
|
|
358
235
|
// Build inline style for button CSS variables
|
|
359
236
|
const buttonStyle = {};
|
|
360
237
|
if (lightButton?.background)
|
|
@@ -369,17 +246,11 @@ export function YakWidget({ iframeClassName, triggerLabel = "Ask with AI", } = {
|
|
|
369
246
|
buttonStyle["--yak-btn-dark-color"] = darkButton.color;
|
|
370
247
|
if (darkButton?.border)
|
|
371
248
|
buttonStyle["--yak-btn-dark-border"] = darkButton.border;
|
|
372
|
-
// Build container class names
|
|
373
|
-
const containerClasses = [
|
|
374
|
-
"yak-widget-container",
|
|
375
|
-
isDrawer && "yak-widget-drawer",
|
|
376
|
-
colorModeClass,
|
|
377
|
-
].filter(Boolean).join(" ");
|
|
378
249
|
// Build button class names
|
|
379
250
|
const buttonClasses = [
|
|
380
251
|
"yak-widget-trigger",
|
|
381
252
|
colorModeClass,
|
|
382
253
|
customButtonClass,
|
|
383
254
|
].filter(Boolean).join(" ");
|
|
384
|
-
return (_jsxs(_Fragment, { children: [_jsx("style", { children:
|
|
255
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { children: getButtonStyles() }), _jsxs("button", { onClick: open, className: buttonClasses, style: Object.keys(buttonStyle).length > 0 ? buttonStyle : undefined, "data-position": position, "data-has-light-custom": hasLightCustom || undefined, "data-has-dark-custom": hasDarkCustom || undefined, "aria-label": isLoading ? "Loading chat" : "Open chat", disabled: isLoading, children: [_jsx("span", { className: "yak-widget-trigger-label", children: triggerLabel }), _jsx("div", { className: "yak-widget-icon-bg", children: isLoading ? (_jsx("div", { className: "yak-widget-spinner", "aria-hidden": "true" })) : (_jsx(BrainCircuitIcon, { size: 20, className: "yak-widget-icon" })) })] })] }));
|
|
385
256
|
}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Theme,
|
|
1
|
+
import type { Theme, ChatConfig, ToolCallEvent, IframeMessageFromHost } from "@yak-io/javascript";
|
|
2
2
|
/**
|
|
3
3
|
* Configuration for the yak provider
|
|
4
4
|
*/
|
|
@@ -13,17 +13,10 @@ export type YakConfig = {
|
|
|
13
13
|
*/
|
|
14
14
|
export type ToolCallEventHandler = (event: ToolCallEvent) => void;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Public API for controlling the Yak chat widget.
|
|
17
|
+
* This is what consumers get from useYak().
|
|
17
18
|
*/
|
|
18
19
|
export type YakContextValue = {
|
|
19
|
-
config: YakConfig;
|
|
20
|
-
/** Get the iframe origin URL (determined by environment) */
|
|
21
|
-
getIframeOrigin: () => string;
|
|
22
|
-
/** Get the full iframe embed URL */
|
|
23
|
-
getEmbedUrl: () => string;
|
|
24
|
-
registerIframeWindow: (win: Window) => void;
|
|
25
|
-
unregisterIframeWindow: () => void;
|
|
26
|
-
sendMessage: (message: IframeMessageFromHost) => void;
|
|
27
20
|
/** Whether the chat widget is currently open */
|
|
28
21
|
isOpen: boolean;
|
|
29
22
|
/** Whether the iframe is ready to receive messages */
|
|
@@ -34,11 +27,25 @@ export type YakContextValue = {
|
|
|
34
27
|
close: () => void;
|
|
35
28
|
/** Open the chat widget and trigger a specific prompt */
|
|
36
29
|
openWithPrompt: (prompt: string) => void;
|
|
37
|
-
setIsIframeReady: (ready: boolean) => void;
|
|
38
30
|
/** Subscribe to tool call completion events */
|
|
39
31
|
subscribeToToolEvents: (handler: ToolCallEventHandler) => () => void;
|
|
40
32
|
};
|
|
41
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Internal context with additional methods for iframe management.
|
|
35
|
+
* Not exposed to consumers - only used by provider internals.
|
|
36
|
+
*/
|
|
37
|
+
export type YakInternalContextValue = YakContextValue & {
|
|
38
|
+
config: YakConfig;
|
|
39
|
+
/** Get the iframe origin URL (determined by environment) */
|
|
40
|
+
getIframeOrigin: () => string;
|
|
41
|
+
/** Get the full iframe embed URL */
|
|
42
|
+
getEmbedUrl: () => string;
|
|
43
|
+
/** Send a message to the iframe */
|
|
44
|
+
sendMessage: (message: IframeMessageFromHost) => void;
|
|
45
|
+
/** Set iframe ready state (called by provider) */
|
|
46
|
+
setIsIframeReady: (ready: boolean) => void;
|
|
47
|
+
};
|
|
48
|
+
export declare const YakContext: import("react").Context<YakInternalContextValue | null>;
|
|
42
49
|
/**
|
|
43
50
|
* Hook to access the Yak chat widget API.
|
|
44
51
|
* Provides methods for opening, closing, and triggering prompts.
|
|
@@ -60,6 +67,11 @@ export declare const YakContext: import("react").Context<YakContextValue | null>
|
|
|
60
67
|
* @throws {Error} if used outside YakProvider
|
|
61
68
|
*/
|
|
62
69
|
export declare function useYak(): YakContextValue;
|
|
70
|
+
/**
|
|
71
|
+
* Internal hook for components that need full context access.
|
|
72
|
+
* Not exported publicly.
|
|
73
|
+
*/
|
|
74
|
+
export declare function useYakInternal(): YakInternalContextValue;
|
|
63
75
|
/**
|
|
64
76
|
* Hook to subscribe to tool call completion events.
|
|
65
77
|
* Useful for page-level cache invalidation when chatbot tools modify data.
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAElG;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;AAElE;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,gDAAgD;IAChD,MAAM,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,aAAa,EAAE,OAAO,CAAC;IACvB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,yDAAyD;IACzD,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,+CAA+C;IAC/C,qBAAqB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,MAAM,IAAI,CAAC;CACtE,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,eAAe,GAAG;IACtD,MAAM,EAAE,SAAS,CAAC;IAClB,4DAA4D;IAC5D,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,oCAAoC;IACpC,WAAW,EAAE,MAAM,MAAM,CAAC;IAC1B,mCAAmC;IACnC,WAAW,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACtD,kDAAkD;IAClD,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C,CAAC;AAEF,eAAO,MAAM,UAAU,yDAAsD,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,IAAI,eAAe,CAcxC;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,uBAAuB,CAMxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAkBnE"}
|
package/dist/context.js
CHANGED
|
@@ -26,6 +26,25 @@ export function useYak() {
|
|
|
26
26
|
if (!context) {
|
|
27
27
|
throw new Error("useYak must be used within YakProvider");
|
|
28
28
|
}
|
|
29
|
+
// Return only the public API
|
|
30
|
+
return {
|
|
31
|
+
isOpen: context.isOpen,
|
|
32
|
+
isIframeReady: context.isIframeReady,
|
|
33
|
+
open: context.open,
|
|
34
|
+
close: context.close,
|
|
35
|
+
openWithPrompt: context.openWithPrompt,
|
|
36
|
+
subscribeToToolEvents: context.subscribeToToolEvents,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Internal hook for components that need full context access.
|
|
41
|
+
* Not exported publicly.
|
|
42
|
+
*/
|
|
43
|
+
export function useYakInternal() {
|
|
44
|
+
const context = useContext(YakContext);
|
|
45
|
+
if (!context) {
|
|
46
|
+
throw new Error("useYakInternal must be used within YakProvider");
|
|
47
|
+
}
|
|
29
48
|
return context;
|
|
30
49
|
}
|
|
31
50
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export { YakProvider } from "./YakProvider.js";
|
|
|
4
4
|
export type { YakProviderProps } from "./YakProvider.js";
|
|
5
5
|
export { YakWidget } from "./YakWidget.js";
|
|
6
6
|
export type { YakWidgetProps } from "./YakWidget.js";
|
|
7
|
-
export { type GraphQLSchemaHandler, type RESTSchemaHandler, type GraphQLRequest, type RESTRequest, type ToolCallHandler, type ToolCallEvent, type SchemaSource, type GraphQLSchemaSource, type OpenAPISchemaSource, type Theme, type ThemeColors, type ButtonColors, } from "@yak-io/javascript";
|
|
7
|
+
export { type GraphQLSchemaHandler, type RESTSchemaHandler, type GraphQLRequest, type RESTRequest, type ToolCallHandler, type ToolCallEvent, type SchemaSource, type GraphQLSchemaSource, type OpenAPISchemaSource, type Theme, type ThemeColors, type ButtonColors, type WidgetPosition, } from "@yak-io/javascript";
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC"}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Warnings and errors are always logged.
|
|
2
|
+
* Re-export logger from @yak-io/javascript.
|
|
3
|
+
* All logging configuration is centralized there.
|
|
5
4
|
*/
|
|
6
|
-
export
|
|
7
|
-
debug: (message: string, data?: unknown) => void;
|
|
8
|
-
info: (message: string, data?: unknown) => void;
|
|
9
|
-
warn: (message: string, data?: unknown) => void;
|
|
10
|
-
error: (message: string, data?: unknown) => void;
|
|
11
|
-
};
|
|
5
|
+
export { logger } from "@yak-io/javascript";
|
|
12
6
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/internal/logger.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/internal/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/internal/logger.js
CHANGED
|
@@ -1,44 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Warnings and errors are always logged.
|
|
2
|
+
* Re-export logger from @yak-io/javascript.
|
|
3
|
+
* All logging configuration is centralized there.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
export const logger = {
|
|
8
|
-
debug: (message, data) => {
|
|
9
|
-
if (isDev) {
|
|
10
|
-
if (data !== undefined) {
|
|
11
|
-
console.log(`[yak-chat-host] ${message}`, data);
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
console.log(`[yak-chat-host] ${message}`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
info: (message, data) => {
|
|
19
|
-
if (isDev) {
|
|
20
|
-
if (data !== undefined) {
|
|
21
|
-
console.info(`[yak-chat-host] ${message}`, data);
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
console.info(`[yak-chat-host] ${message}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
warn: (message, data) => {
|
|
29
|
-
if (data !== undefined) {
|
|
30
|
-
console.warn(`[yak-chat-host] ${message}`, data);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
console.warn(`[yak-chat-host] ${message}`);
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
error: (message, data) => {
|
|
37
|
-
if (data !== undefined) {
|
|
38
|
-
console.error(`[yak-chat-host] ${message}`, data);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
console.error(`[yak-chat-host] ${message}`);
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
};
|
|
5
|
+
export { logger } from "@yak-io/javascript";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yak-io/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "React SDK for embedding yak chatbot",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
"./package.json": "./package.json"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@yak-io/javascript": "0.
|
|
44
|
+
"@yak-io/javascript": "0.4.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"react": "^18.0.0 || ^19.0.0",
|
|
48
48
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@types/node": "^24.10.
|
|
52
|
-
"@types/react": "^19.2.
|
|
51
|
+
"@types/node": "^24.10.4",
|
|
52
|
+
"@types/react": "^19.2.10",
|
|
53
53
|
"@types/react-dom": "^19.2.0",
|
|
54
54
|
"typescript": "^5.3.0",
|
|
55
55
|
"@repo/typescript-config": "0.0.0"
|