@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.
@@ -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 and message handling for the yak widget.
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;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;;;;;;;;;;;;;OAeG;IACH,mBAAmB,CAAC,EAAE,oBAAoB,CAAC;IAC3C;;;;;;;;;;;;;;;;;;;OAmBG;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;AAEF;;GAEG;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,CAmOtC"}
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"}
@@ -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
- * YakProvider sets up the context and message handling for the yak widget.
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
- return _jsx(YakContext.Provider, { value: contextValue, children: children });
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
  }
@@ -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 and iframe panel.
13
- * The iframe loads immediately and shows its own skeleton while waiting for config.
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({ iframeClassName, triggerLabel, }?: YakWidgetProps): React.JSX.Element;
30
+ export declare function YakWidget({ triggerLabel, position, colorMode, lightButton, darkButton, }?: YakWidgetProps): React.JSX.Element;
16
31
  //# sourceMappingURL=YakWidget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"YakWidget.d.ts","sourceRoot":"","sources":["../src/YakWidget.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAqU3D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,eAAe,EACf,YAA4B,GAC7B,GAAE,cAAmB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAwJzC"}
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 widget styles consolidated in one place
5
+ * All button styles consolidated in one place
7
6
  */
8
- function getWidgetStyles() {
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="right"] {
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 and iframe panel.
300
- * The iframe loads immediately and shows its own skeleton while waiting for config.
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({ iframeClassName, triggerLabel = "Ask with AI", } = {}) {
303
- const { config, getEmbedUrl, registerIframeWindow, unregisterIframeWindow, isOpen, isIframeReady, open, close, } = useYak();
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 && hasBeenOpened && !isIframeReady;
308
- // Get URLs from the client (computed based on environment)
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: getWidgetStyles() }), _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" })) })] }), isDrawer && hasBeenOpened && (_jsx("div", { className: "yak-widget-backdrop", "data-open": isOpen && isIframeReady, onClick: close, "aria-hidden": "true" })), hasBeenOpened && (_jsx("div", { className: containerClasses, "data-position": position, "data-open": isOpen && isIframeReady, children: _jsx("iframe", { ref: iframeRef, src: iframeSrc, className: `yak-widget-iframe ${iframeClassName ?? ""}`.trim(), title: "yak-chat-host" }) }))] }));
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, IframeMessageFromHost, ChatConfig, ToolCallEvent } from "@yak-io/javascript";
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
- * Context value with config and iframe management
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
- export declare const YakContext: import("react").Context<YakContextValue | null>;
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.
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,aAAa,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;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,4DAA4D;IAC5D,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,oCAAoC;IACpC,WAAW,EAAE,MAAM,MAAM,CAAC;IAC1B,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,WAAW,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACtD,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,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,+CAA+C;IAC/C,qBAAqB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,MAAM,IAAI,CAAC;CACtE,CAAC;AAEF,eAAO,MAAM,UAAU,iDAA8C,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,IAAI,eAAe,CAMxC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAkBnE"}
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
@@ -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,GAClB,MAAM,oBAAoB,CAAC"}
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
- * Simple logger utility for the yak React SDK.
3
- * Debug/info logs are only emitted in development mode.
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 declare const logger: {
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;;;;GAIG;AAIH,eAAO,MAAM,MAAM;qBACA,MAAM,SAAS,OAAO,KAAG,IAAI;oBAU9B,MAAM,SAAS,OAAO,KAAG,IAAI;oBAU7B,MAAM,SAAS,OAAO,KAAG,IAAI;qBAQ5B,MAAM,SAAS,OAAO,KAAG,IAAI;CAO/C,CAAC"}
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"}
@@ -1,44 +1,5 @@
1
1
  /**
2
- * Simple logger utility for the yak React SDK.
3
- * Debug/info logs are only emitted in development mode.
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
- const isDev = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
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.2.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.2.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.2",
52
- "@types/react": "^19.2.7",
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"