@plucky-ai/chat-sdk 0.1.0 → 0.2.1

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/index.cjs CHANGED
@@ -21,8 +21,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- let lodash = require("lodash");
25
- lodash = __toESM(lodash);
24
+ let dequal = require("dequal");
25
+ dequal = __toESM(dequal);
26
26
  let zod = require("zod");
27
27
  zod = __toESM(zod);
28
28
 
@@ -88,7 +88,7 @@ body.${shiftClass} {
88
88
  inset-block: 0;
89
89
  inset-inline-end: 0;
90
90
  width: var(--${idPrefix}-w);
91
- max-width: 90vw;
91
+ max-width: 100vw;
92
92
  z-index: ${zIndex};
93
93
  background: transparent;
94
94
  }
@@ -147,9 +147,9 @@ body.${shiftClass} {
147
147
  if (!hidden && mode === "push") document.body.classList.add(shiftClass);
148
148
  else document.body.classList.remove(shiftClass);
149
149
  }
150
- function setWidth(px) {
151
- widthPx = Math.max(0, Math.round(px));
152
- document.documentElement.style.setProperty(`--${idPrefix}-w`, `${widthPx}px`);
150
+ function setWidth(width) {
151
+ const widthCssValue = typeof width === "number" ? `${Math.max(0, Math.round(width))}px` : width;
152
+ document.documentElement.style.setProperty(`--${idPrefix}-w`, widthCssValue);
153
153
  }
154
154
  function setZIndex(z$1) {
155
155
  zIndex = z$1;
@@ -201,6 +201,21 @@ body.${shiftClass} {
201
201
  };
202
202
  }
203
203
 
204
+ //#endregion
205
+ //#region src/utils.ts
206
+ function safeLocalStorageGet(key) {
207
+ try {
208
+ return window.localStorage.getItem(key);
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
213
+ function safeLocalStorageSet(key, value) {
214
+ try {
215
+ window.localStorage.setItem(key, value);
216
+ } catch {}
217
+ }
218
+
204
219
  //#endregion
205
220
  //#region src/api.ts
206
221
  function createAPI() {
@@ -215,9 +230,18 @@ function createAPI() {
215
230
  user: {},
216
231
  tools: [],
217
232
  tags: [],
218
- width: 360
233
+ width: 360,
234
+ fullscreen: false
219
235
  };
220
236
  let currentMode = "push";
237
+ let resizeListener = null;
238
+ function getScrollbarWidth() {
239
+ return window.innerWidth - document.documentElement.clientWidth;
240
+ }
241
+ function getFullscreenWidth() {
242
+ const scrollbarWidth = getScrollbarWidth();
243
+ return scrollbarWidth > 0 ? `calc(100vw - ${scrollbarWidth}px)` : "100vw";
244
+ }
221
245
  function post(type, payload) {
222
246
  const iframe = sidebar.getIframe();
223
247
  const targetOrigin = new URL(state.baseUrl).origin;
@@ -265,8 +289,9 @@ function createAPI() {
265
289
  bus.on("PLUCKY_SWITCH_MODE", (m) => setMode(m));
266
290
  bus.on("PLUCKY_SESSION_UPDATED", (payload) => {
267
291
  const storageKey = `pls::${state.appId}`;
268
- if (payload && typeof payload.token === "string" && payload.token) safeLocalStorageSet(storageKey, payload.token);
292
+ if (typeof payload.token === "string" && payload.token) safeLocalStorageSet(storageKey, payload.token);
269
293
  });
294
+ bus.on("PLUCKY_SET_FULLSCREEN", (payload) => handleSetFullscreen(payload.fullscreen));
270
295
  bus.on("PLUCKY_RESPONSE_RECEIVED", async (response) => {
271
296
  const lastMessage = response.messages[response.messages.length - 1];
272
297
  if (typeof lastMessage.content === "string") return;
@@ -350,13 +375,13 @@ function createAPI() {
350
375
  state.tools.push(tool);
351
376
  state.tools.sort((a, b) => a.name.localeCompare(b.name));
352
377
  const newToolState = getAllToolJSON();
353
- if (notify && !lodash.default.isEqual(currentToolState, newToolState)) notifyToolConfigUpdated();
378
+ if (notify && !(0, dequal.dequal)(currentToolState, newToolState)) notifyToolConfigUpdated();
354
379
  }
355
380
  function registerManyTools(tools, notify = true) {
356
381
  const currentToolState = getAllToolJSON();
357
382
  for (const tool of tools) registerTool(tool, false);
358
383
  const newToolState = getAllToolJSON();
359
- if (notify && !lodash.default.isEqual(currentToolState, newToolState)) notifyToolConfigUpdated();
384
+ if (notify && !(0, dequal.dequal)(currentToolState, newToolState)) notifyToolConfigUpdated();
360
385
  }
361
386
  function removeTool(name, notify = true) {
362
387
  if (!state.tools.find((t) => t.name === name)) console.warn(`Tool ${name} not found, skipping.`);
@@ -375,6 +400,32 @@ function createAPI() {
375
400
  inputSchema: t.inputSchema instanceof zod.ZodObject ? zod.z.toJSONSchema(t.inputSchema) : t.inputSchema
376
401
  }));
377
402
  }
403
+ function setWidth(px) {
404
+ state.width = px;
405
+ sidebar.setWidth(px);
406
+ }
407
+ function setFullscreen(fullscreen) {
408
+ post("PLUCKY_SET_FULLSCREEN", { fullscreen });
409
+ handleSetFullscreen(fullscreen);
410
+ }
411
+ function handleSetFullscreen(fullscreen) {
412
+ state.fullscreen = fullscreen;
413
+ if (resizeListener) {
414
+ window.removeEventListener("resize", resizeListener);
415
+ resizeListener = null;
416
+ }
417
+ if (fullscreen) {
418
+ sidebar.setWidth(getFullscreenWidth());
419
+ sidebar.setMode("overlay");
420
+ resizeListener = () => {
421
+ if (state.fullscreen) sidebar.setWidth(getFullscreenWidth());
422
+ };
423
+ window.addEventListener("resize", resizeListener);
424
+ } else {
425
+ sidebar.setWidth(state.width);
426
+ sidebar.setMode("push");
427
+ }
428
+ }
378
429
  const api$1 = {
379
430
  init: init$1,
380
431
  setMode,
@@ -390,22 +441,11 @@ function createAPI() {
390
441
  registerManyTools,
391
442
  removeTool,
392
443
  setInput,
393
- setWidth: sidebar.setWidth
444
+ setWidth,
445
+ setFullscreen
394
446
  };
395
447
  return api$1;
396
448
  }
397
- function safeLocalStorageGet(key) {
398
- try {
399
- return window.localStorage.getItem(key);
400
- } catch {
401
- return null;
402
- }
403
- }
404
- function safeLocalStorageSet(key, value) {
405
- try {
406
- window.localStorage.setItem(key, value);
407
- } catch {}
408
- }
409
449
 
410
450
  //#endregion
411
451
  //#region src/index.ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["host: HTMLDivElement | null","iframe: HTMLIFrameElement | null","mode: Mode","nonce: string | undefined","css","z","state: Required<PluckyAPIOptions>","currentMode: 'push' | 'overlay'","init","api","_","ZodObject","z","api: PluckyAPI","api: PluckyAPI | null"],"sources":["../src/bus.ts","../src/sidebar.ts","../src/api.ts","../src/index.ts"],"sourcesContent":["import type { MessageType } from './types'\n\ntype Handler = (p: any) => void\n\nexport class Bus {\n private handlers = new Map<MessageType, Set<Handler>>()\n\n on(event: MessageType, cb: Handler) {\n if (!this.handlers.has(event)) this.handlers.set(event, new Set())\n this.handlers.get(event)!.add(cb)\n return () => this.handlers.get(event)!.delete(cb)\n }\n\n emit(event: MessageType, payload?: any) {\n this.handlers.get(event)?.forEach((h) => h(payload))\n }\n}\n\nexport function bindPostMessage(bus: Bus, win: Window, origin: string) {\n const onMsg = (e: MessageEvent) => {\n if (e.origin !== origin) return\n if (!e.data || typeof e.data !== 'object') return\n if (!('type' in e.data)) return\n bus.emit(e.data.type, e.data.payload)\n }\n win.addEventListener('message', onMsg)\n return () => win.removeEventListener('message', onMsg)\n}\n","export type Mode = 'push' | 'overlay'\n\nexport interface SidebarOpts {\n appId: string\n baseUrl: string\n widthPx?: number\n zIndex?: number\n idPrefix?: string\n nonce?: string\n iframeTitle?: string\n initialMode?: Mode // 'push' (default) or 'overlay'\n}\n\nexport interface SidebarAPI {\n mount(opts: SidebarOpts): void\n unmount(): void\n setMode(mode: Mode): void\n setWidth(px: number): void\n setZIndex(z: number): void\n hide(): void // NEW: hide container + remove padding, keep iframe loaded\n show(): void // NEW: restore visibility + padding if mode==='push'\n isHidden(): boolean\n getMode(): Mode\n getContainer(): HTMLDivElement | null\n getIframe(): HTMLIFrameElement | null\n ready(): boolean\n}\n\nexport function createSidebar(): SidebarAPI {\n let host: HTMLDivElement | null = null\n let iframe: HTMLIFrameElement | null = null\n let mounted = false\n let hidden = false\n let mode: Mode = 'push'\n let widthPx = 360\n let zIndex = 2147483646\n let idPrefix = 'plucky'\n let cssId = ''\n let hostId = ''\n let shiftClass = ''\n let hiddenClass = ''\n let nonce: string | undefined\n\n const ensureStyle = (css: string) => {\n let tag = document.getElementById(cssId) as HTMLStyleElement | null\n if (!tag) {\n tag = document.createElement('style')\n tag.id = cssId\n if (nonce) tag.setAttribute('nonce', nonce)\n document.head.appendChild(tag)\n }\n tag.textContent = css\n }\n\n const css = () => `\n:root { --${idPrefix}-w: ${widthPx}px; }\n\nbody.${shiftClass} {\n padding-inline-end: var(--${idPrefix}-w) !important;\n}\n\n/* Fixed sidebar; overlay vs push is just whether body has padding */\n#${hostId} {\n position: fixed;\n inset-block: 0;\n inset-inline-end: 0;\n width: var(--${idPrefix}-w);\n max-width: 90vw;\n z-index: ${zIndex};\n background: transparent;\n}\n#${hostId} > iframe { width: 100%; height: 100%; border: 0; display: block; }\n\n/* Hidden state: keep in DOM, don’t render or accept focus/clicks */\n#${hostId}.${hiddenClass} {\n display: none !important;\n}\n`\n\n function mount(opts: SidebarOpts) {\n if (mounted) return\n\n widthPx = opts.widthPx ?? widthPx\n zIndex = opts.zIndex ?? zIndex\n idPrefix = opts.idPrefix ?? idPrefix\n nonce = opts.nonce\n mode = opts.initialMode ?? mode\n\n hostId = `${idPrefix}-sidebar`\n cssId = `${idPrefix}-sidebar-css`\n shiftClass = `${idPrefix}-shift`\n hiddenClass = `${idPrefix}-hidden`\n\n ensureStyle(css())\n\n host = document.getElementById(hostId) as HTMLDivElement | null\n if (!host) {\n host = document.createElement('div')\n host.id = hostId\n host.style.zIndex = String(zIndex)\n document.body.appendChild(host)\n }\n\n if (!iframe) {\n iframe = document.createElement('iframe')\n iframe.title = opts.iframeTitle ?? 'Plucky'\n iframe.setAttribute('aria-label', iframe.title)\n const base = opts.baseUrl.replace(/\\/$/, '')\n iframe.src = `${base}/widget/${encodeURIComponent(opts.appId)}`\n host.appendChild(iframe)\n }\n\n // Apply current visibility + mode\n if (hidden) host.classList.add(hiddenClass)\n else host.classList.remove(hiddenClass)\n\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n\n mounted = true\n }\n\n function unmount() {\n if (!mounted) return\n document.body.classList.remove(shiftClass)\n host?.remove()\n document.getElementById(cssId)?.remove()\n host = null\n iframe = null\n mounted = false\n hidden = false\n }\n\n function setMode(next: Mode) {\n mode = next\n if (!mounted) return\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n }\n\n function setWidth(px: number) {\n widthPx = Math.max(0, Math.round(px))\n document.documentElement.style.setProperty(\n `--${idPrefix}-w`,\n `${widthPx}px`,\n )\n }\n\n function setZIndex(z: number) {\n zIndex = z\n if (host) host.style.zIndex = String(zIndex)\n }\n\n function hide() {\n if (!mounted || hidden) return\n hidden = true\n // Remove page padding if present\n document.body.classList.remove(shiftClass)\n // Hide the host container without unmounting the iframe\n host?.classList.add(hiddenClass)\n if (host) {\n host.setAttribute('aria-hidden', 'true')\n // Disable interactivity for assistive tech even if CSS changes\n ;(host as any).inert = true // harmless if not supported\n }\n // Optional: signal iframe to pause work\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'hidden' },\n '*',\n )\n }\n\n function show() {\n if (!mounted || !hidden) return\n hidden = false\n host?.classList.remove(hiddenClass)\n if (host) {\n host.removeAttribute('aria-hidden')\n try {\n ;(host as any).inert = false\n } catch {}\n }\n if (mode === 'push') document.body.classList.add(shiftClass)\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'visible' },\n '*',\n )\n }\n\n return {\n mount,\n unmount,\n setMode,\n setWidth,\n setZIndex,\n hide,\n show,\n isHidden: () => hidden,\n getMode: () => mode,\n getContainer: () => host,\n getIframe: () => iframe,\n ready: () => !!iframe?.contentWindow,\n }\n}\n","import type { SavedMessage, ToolResultContentBlock } from '@plucky-ai/llm'\nimport _ from 'lodash'\nimport { z, ZodObject } from 'zod'\nimport { bindPostMessage, Bus } from './bus'\nimport { createSidebar } from './sidebar'\nimport type {\n InitOptions,\n MessageType,\n Mode,\n PluckyAPI,\n PluckyAPIOptions,\n SimpleToolConfig,\n ToolConfig,\n} from './types'\n\nexport function createAPI(): PluckyAPI {\n const bus = new Bus()\n let inited = false\n\n // Layout controllers\n const sidebar = createSidebar()\n\n // runtime state with sane defaults\n let state: Required<PluckyAPIOptions> = {\n appId: '',\n baseUrl: 'https://widget.plucky.ai',\n mode: 'push',\n containerId: undefined as unknown as string,\n user: {},\n tools: [],\n tags: [],\n width: 360,\n }\n\n // Track current mode and last mount options\n let currentMode: 'push' | 'overlay' = 'push'\n\n // Sidebar is the single DOM/layout owner; we post directly to its iframe\n function post(type: MessageType, payload?: any) {\n const iframe = sidebar.getIframe()\n const targetOrigin = new URL(state.baseUrl).origin\n iframe?.contentWindow?.postMessage({ type, payload }, targetOrigin)\n }\n\n function switchMode(next: 'push' | 'overlay') {\n if (next === currentMode) return\n\n // Just switch mode on the single layout\n sidebar.setMode(next)\n currentMode = next\n }\n\n function init(opts: InitOptions): PluckyAPI {\n if (inited) return api\n const { tools, ...rest } = opts\n state = { ...state, ...rest } as Required<PluckyAPIOptions>\n if (tools && tools.length > 0) {\n registerManyTools(tools, false)\n }\n currentMode = state.mode as 'push' | 'overlay'\n bindPostMessage(bus, window, new URL(state.baseUrl).origin)\n\n bus.on('PLUCKY_CLOSE', () => sidebar.hide())\n\n // Listen for iframe mount signal before sending boot\n bus.on('PLUCKY_WIDGET_MOUNTED', () => {\n const storageKey = `pls::${state.appId}`\n const existingToken = safeLocalStorageGet(storageKey)\n post('PLUCKY_LOAD_CONFIG', {\n appId: state.appId,\n user: state.user,\n mode: state.mode,\n sessionToken: existingToken || undefined,\n tools: getAllToolJSON(),\n tags: state.tags,\n })\n if (!existingToken) {\n post('PLUCKY_SESSION_REQUEST', { reason: 'missing' })\n }\n })\n\n // Mount the sidebar (creates iframe inside it)\n sidebar.mount({\n appId: state.appId,\n baseUrl: state.baseUrl,\n iframeTitle: 'Plucky',\n initialMode: currentMode,\n widthPx: state.width,\n })\n\n // Wire bus-driven layout changes to the sidebar\n bus.on('PLUCKY_SET_WIDTH', (px: number) => sidebar.setWidth(px))\n bus.on('PLUCKY_SWITCH_MODE', (m: Mode) => setMode(m))\n bus.on('PLUCKY_SESSION_UPDATED', (payload: { token?: string }) => {\n const storageKey = `pls::${state.appId}`\n if (payload && typeof payload.token === 'string' && payload.token) {\n safeLocalStorageSet(storageKey, payload.token)\n }\n })\n bus.on(\n 'PLUCKY_RESPONSE_RECEIVED',\n async (response: { messages: SavedMessage[]; chatId: string }) => {\n const lastMessage = response.messages[response.messages.length - 1]\n if (typeof lastMessage.content === 'string') return\n const toolResultIds = lastMessage.content\n .filter((m) => m.type === 'tool_result')\n .map((m) => m.toolUseId)\n const unfulfilledToolCalls = lastMessage.content\n .filter((m) => m.type === 'tool_use')\n .filter((m) => !toolResultIds.includes(m.id))\n if (unfulfilledToolCalls.length === 0) return\n const promises = unfulfilledToolCalls.map(\n async (toolCall): Promise<ToolResultContentBlock> => {\n const tool = state.tools.find((t) => t.name === toolCall.name)\n if (!tool) {\n return {\n toolUseId: toolCall.id,\n content: 'Tool not found.',\n type: 'tool_result',\n }\n }\n if (!tool.cb) {\n return {\n toolUseId: toolCall.id,\n content: 'Received.',\n type: 'tool_result',\n }\n }\n try {\n let input = toolCall.input as Record<string, unknown> | string\n if (typeof input === 'string') {\n input = JSON.parse(input) as Record<string, unknown>\n }\n const result = await tool.cb(input)\n return {\n toolUseId: toolCall.id,\n content: result,\n type: 'tool_result',\n }\n } catch (e) {\n return {\n toolUseId: toolCall.id,\n content: `Error: ${e instanceof Error ? e.message : String(e)}`,\n type: 'tool_result',\n }\n }\n },\n )\n\n const results = await Promise.all(promises)\n sendMessage({\n content: results,\n lastMessageId: lastMessage.id,\n chatId: response.chatId,\n })\n },\n )\n\n // Wait for iframe to signal it's ready before sending boot\n inited = true\n return api\n }\n\n function sendMessage(args: {\n content: string | Array<ToolResultContentBlock>\n lastMessageId?: string\n createChat?: boolean\n chatId?: string\n }) {\n const { content, lastMessageId, createChat, chatId } = args\n if (sidebar.isHidden()) {\n sidebar.show()\n }\n post('PLUCKY_SEND_MESSAGE', {\n content,\n createChat: createChat ?? false,\n lastMessageId,\n chatId,\n })\n }\n function setInput(input: string) {\n sidebar.getIframe()?.contentWindow?.focus()\n post('PLUCKY_SET_INPUT', { input })\n }\n\n function setMode(mode: Mode) {\n state.mode = mode\n currentMode = mode as 'push' | 'overlay'\n sidebar.setMode(mode)\n post('PLUCKY_SWITCH_MODE', mode)\n }\n\n function on(event: MessageType, cb: (p: any) => void) {\n return bus.on(event, cb)\n }\n\n function open() {\n sidebar.show()\n }\n\n function close() {\n sidebar.hide()\n }\n\n function toggle() {\n if (sidebar.isHidden()) {\n sidebar.show()\n } else {\n sidebar.hide()\n }\n }\n\n function registerTool(tool: ToolConfig, notify = true) {\n const currentToolState = getAllToolJSON()\n if (state.tools.find((t) => t.name === tool.name)) {\n state.tools = state.tools.filter((t) => t.name !== tool.name)\n }\n state.tools.push(tool)\n state.tools.sort((a, b) => a.name.localeCompare(b.name))\n const newToolState = getAllToolJSON()\n if (notify && !_.isEqual(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function registerManyTools(tools: ToolConfig[], notify = true) {\n const currentToolState = getAllToolJSON()\n for (const tool of tools) {\n registerTool(tool, false)\n }\n const newToolState = getAllToolJSON()\n if (notify && !_.isEqual(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function removeTool(name: string, notify = true) {\n if (!state.tools.find((t) => t.name === name)) {\n console.warn(`Tool ${name} not found, skipping.`)\n }\n state.tools = state.tools.filter((t) => t.name !== name)\n if (notify) {\n notifyToolConfigUpdated()\n }\n }\n\n function notifyToolConfigUpdated() {\n post('PLUCKY_TOOL_CONFIG_UPDATED', { tools: getAllToolJSON() })\n }\n\n function getAllToolJSON(): SimpleToolConfig[] {\n return state.tools.map((t) => ({\n name: t.name,\n description: t.description,\n defaultLoadingText: t.defaultLoadingText,\n defaultSuccessText: t.defaultSuccessText,\n inputSchema:\n t.inputSchema instanceof ZodObject\n ? z.toJSONSchema(t.inputSchema)\n : t.inputSchema,\n }))\n }\n\n const api: PluckyAPI = {\n init,\n setMode,\n switchMode,\n open,\n close,\n toggle,\n on,\n isReady: () => sidebar.ready(),\n sendMessage,\n isOpen: () => !sidebar.isHidden(),\n registerTool,\n registerManyTools,\n removeTool,\n setInput,\n setWidth: sidebar.setWidth,\n }\n return api\n}\n\nfunction safeLocalStorageGet(key: string): string | null {\n try {\n return window.localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction safeLocalStorageSet(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value)\n } catch {}\n}\n","import { createAPI } from './api'\nimport type { InitOptions, PluckyAPI } from './types'\n\n// singleton exported API for convenience\nlet api: PluckyAPI | null = null\n\nexport function init(opts: InitOptions) {\n if (!api) api = createAPI()\n return api.init(opts)\n}\n\nexport function getAPI() {\n if (!api) api = createAPI()\n return api\n}\n\n// convenience re-exports\nexport type * from './types'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["host: HTMLDivElement | null","iframe: HTMLIFrameElement | null","mode: Mode","widthPx: number | string","nonce: string | undefined","css","z","state: Required<PluckyAPIOptions>","currentMode: 'push' | 'overlay'","resizeListener: (() => void) | null","init","api","ZodObject","z","api: PluckyAPI","api: PluckyAPI | null"],"sources":["../src/bus.ts","../src/sidebar.ts","../src/utils.ts","../src/api.ts","../src/index.ts"],"sourcesContent":["import type { MessageType } from './types'\n\ntype Handler = (p: any) => void\n\nexport class Bus {\n private handlers = new Map<MessageType, Set<Handler>>()\n\n on(event: MessageType, cb: Handler) {\n if (!this.handlers.has(event)) this.handlers.set(event, new Set())\n this.handlers.get(event)!.add(cb)\n return () => this.handlers.get(event)!.delete(cb)\n }\n\n emit(event: MessageType, payload?: any) {\n this.handlers.get(event)?.forEach((h) => h(payload))\n }\n}\n\nexport function bindPostMessage(bus: Bus, win: Window, origin: string) {\n const onMsg = (e: MessageEvent) => {\n if (e.origin !== origin) return\n if (!e.data || typeof e.data !== 'object') return\n if (!('type' in e.data)) return\n bus.emit(e.data.type, e.data.payload)\n }\n win.addEventListener('message', onMsg)\n return () => win.removeEventListener('message', onMsg)\n}\n","export type Mode = 'push' | 'overlay'\n\nexport interface SidebarOpts {\n appId: string\n baseUrl: string\n widthPx?: number\n zIndex?: number\n idPrefix?: string\n nonce?: string\n iframeTitle?: string\n initialMode?: Mode // 'push' (default) or 'overlay'\n}\n\nexport interface SidebarAPI {\n mount: (opts: SidebarOpts) => void\n unmount: () => void\n setMode: (mode: Mode) => void\n setWidth: (px: number | string) => void\n setZIndex: (z: number) => void\n hide: () => void // NEW: hide container + remove padding, keep iframe loaded\n show: () => void // NEW: restore visibility + padding if mode==='push'\n isHidden: () => boolean\n getMode: () => Mode\n getContainer: () => HTMLDivElement | null\n getIframe: () => HTMLIFrameElement | null\n ready: () => boolean\n}\n\nexport function createSidebar(): SidebarAPI {\n let host: HTMLDivElement | null = null\n let iframe: HTMLIFrameElement | null = null\n let mounted = false\n let hidden = false\n let mode: Mode = 'push'\n let widthPx: number | string = 360\n let zIndex = 2147483646\n let idPrefix = 'plucky'\n let cssId = ''\n let hostId = ''\n let shiftClass = ''\n let hiddenClass = ''\n let nonce: string | undefined\n\n const ensureStyle = (css: string) => {\n let tag = document.getElementById(cssId) as HTMLStyleElement | null\n if (!tag) {\n tag = document.createElement('style')\n tag.id = cssId\n if (nonce) tag.setAttribute('nonce', nonce)\n document.head.appendChild(tag)\n }\n tag.textContent = css\n }\n\n const css = () => `\n:root { --${idPrefix}-w: ${widthPx}px; }\n\nbody.${shiftClass} {\n padding-inline-end: var(--${idPrefix}-w) !important;\n}\n\n/* Fixed sidebar; overlay vs push is just whether body has padding */\n#${hostId} {\n position: fixed;\n inset-block: 0;\n inset-inline-end: 0;\n width: var(--${idPrefix}-w);\n max-width: 100vw;\n z-index: ${zIndex};\n background: transparent;\n}\n#${hostId} > iframe { width: 100%; height: 100%; border: 0; display: block; }\n\n/* Hidden state: keep in DOM, don’t render or accept focus/clicks */\n#${hostId}.${hiddenClass} {\n display: none !important;\n}\n`\n\n function mount(opts: SidebarOpts) {\n if (mounted) return\n\n widthPx = opts.widthPx ?? widthPx\n zIndex = opts.zIndex ?? zIndex\n idPrefix = opts.idPrefix ?? idPrefix\n nonce = opts.nonce\n mode = opts.initialMode ?? mode\n\n hostId = `${idPrefix}-sidebar`\n cssId = `${idPrefix}-sidebar-css`\n shiftClass = `${idPrefix}-shift`\n hiddenClass = `${idPrefix}-hidden`\n\n ensureStyle(css())\n\n host = document.getElementById(hostId) as HTMLDivElement | null\n if (!host) {\n host = document.createElement('div')\n host.id = hostId\n host.style.zIndex = String(zIndex)\n document.body.appendChild(host)\n }\n\n if (!iframe) {\n iframe = document.createElement('iframe')\n iframe.title = opts.iframeTitle ?? 'Plucky'\n iframe.setAttribute('aria-label', iframe.title)\n const base = opts.baseUrl.replace(/\\/$/, '')\n iframe.src = `${base}/widget/${encodeURIComponent(opts.appId)}`\n host.appendChild(iframe)\n }\n\n // Apply current visibility + mode\n if (hidden) host.classList.add(hiddenClass)\n else host.classList.remove(hiddenClass)\n\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n\n mounted = true\n }\n\n function unmount() {\n if (!mounted) return\n document.body.classList.remove(shiftClass)\n host?.remove()\n document.getElementById(cssId)?.remove()\n host = null\n iframe = null\n mounted = false\n hidden = false\n }\n\n function setMode(next: Mode) {\n mode = next\n if (!mounted) return\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n }\n\n function setWidth(width: number | string) {\n const widthCssValue =\n typeof width === 'number' ? `${Math.max(0, Math.round(width))}px` : width\n document.documentElement.style.setProperty(`--${idPrefix}-w`, widthCssValue)\n }\n\n function setZIndex(z: number) {\n zIndex = z\n if (host) host.style.zIndex = String(zIndex)\n }\n\n function hide() {\n if (!mounted || hidden) return\n hidden = true\n // Remove page padding if present\n document.body.classList.remove(shiftClass)\n // Hide the host container without unmounting the iframe\n host?.classList.add(hiddenClass)\n if (host) {\n host.setAttribute('aria-hidden', 'true')\n // Disable interactivity for assistive tech even if CSS changes\n ;(host as any).inert = true // harmless if not supported\n }\n // Optional: signal iframe to pause work\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'hidden' },\n '*',\n )\n }\n\n function show() {\n if (!mounted || !hidden) return\n hidden = false\n host?.classList.remove(hiddenClass)\n if (host) {\n host.removeAttribute('aria-hidden')\n try {\n ;(host as any).inert = false\n } catch {}\n }\n if (mode === 'push') document.body.classList.add(shiftClass)\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'visible' },\n '*',\n )\n }\n\n return {\n mount,\n unmount,\n setMode,\n setWidth,\n setZIndex,\n hide,\n show,\n isHidden: () => hidden,\n getMode: () => mode,\n getContainer: () => host,\n getIframe: () => iframe,\n ready: () => !!iframe?.contentWindow,\n }\n}\n","export function safeLocalStorageGet(key: string): string | null {\n try {\n return window.localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nexport function safeLocalStorageSet(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value)\n } catch {}\n}\n","import type {\n SavedMessage,\n ToolResultContentBlock,\n} from '@plucky-ai/llm-schemas'\nimport { dequal } from 'dequal'\nimport { ZodObject, z } from 'zod'\nimport { Bus, bindPostMessage } from './bus'\nimport { createSidebar } from './sidebar'\nimport type {\n InitOptions,\n MessageType,\n Mode,\n PluckyAPI,\n PluckyAPIOptions,\n SimpleToolConfig,\n ToolConfig,\n} from './types'\nimport { safeLocalStorageGet, safeLocalStorageSet } from './utils'\n\nexport function createAPI(): PluckyAPI {\n const bus = new Bus()\n let inited = false\n\n // Layout controllers\n const sidebar = createSidebar()\n\n // runtime state with sane defaults\n let state: Required<PluckyAPIOptions> = {\n appId: '',\n baseUrl: 'https://widget.plucky.ai',\n mode: 'push',\n containerId: undefined as unknown as string,\n user: {},\n tools: [],\n tags: [],\n width: 360,\n fullscreen: false,\n }\n\n // Track current mode and last mount options\n let currentMode: 'push' | 'overlay' = 'push'\n\n // Track resize listener for fullscreen mode\n let resizeListener: (() => void) | null = null\n\n // Helper to calculate scrollbar width\n function getScrollbarWidth(): number {\n return window.innerWidth - document.documentElement.clientWidth\n }\n\n // Helper to get fullscreen width CSS value accounting for scrollbar\n function getFullscreenWidth(): string {\n const scrollbarWidth = getScrollbarWidth()\n return scrollbarWidth > 0 ? `calc(100vw - ${scrollbarWidth}px)` : '100vw'\n }\n\n // Sidebar is the single DOM/layout owner; we post directly to its iframe\n function post(type: MessageType, payload?: any) {\n const iframe = sidebar.getIframe()\n const targetOrigin = new URL(state.baseUrl).origin\n iframe?.contentWindow?.postMessage({ type, payload }, targetOrigin)\n }\n\n function switchMode(next: 'push' | 'overlay') {\n if (next === currentMode) return\n\n // Just switch mode on the single layout\n sidebar.setMode(next)\n currentMode = next\n }\n\n function init(opts: InitOptions): PluckyAPI {\n if (inited) return api\n const { tools, ...rest } = opts\n state = { ...state, ...rest } as Required<PluckyAPIOptions>\n if (tools && tools.length > 0) {\n registerManyTools(tools, false)\n }\n currentMode = state.mode as 'push' | 'overlay'\n bindPostMessage(bus, window, new URL(state.baseUrl).origin)\n\n bus.on('PLUCKY_CLOSE', () => sidebar.hide())\n\n // Listen for iframe mount signal before sending boot\n bus.on('PLUCKY_WIDGET_MOUNTED', () => {\n const storageKey = `pls::${state.appId}`\n const existingToken = safeLocalStorageGet(storageKey)\n post('PLUCKY_LOAD_CONFIG', {\n appId: state.appId,\n user: state.user,\n mode: state.mode,\n sessionToken: existingToken || undefined,\n tools: getAllToolJSON(),\n tags: state.tags,\n })\n if (!existingToken) {\n post('PLUCKY_SESSION_REQUEST', { reason: 'missing' })\n }\n })\n\n // Mount the sidebar (creates iframe inside it)\n sidebar.mount({\n appId: state.appId,\n baseUrl: state.baseUrl,\n iframeTitle: 'Plucky',\n initialMode: currentMode,\n widthPx: state.width,\n })\n\n // Wire bus-driven layout changes to the sidebar\n bus.on('PLUCKY_SET_WIDTH', (px: number) => sidebar.setWidth(px))\n bus.on('PLUCKY_SWITCH_MODE', (m: Mode) => setMode(m))\n bus.on('PLUCKY_SESSION_UPDATED', (payload: { token?: string }) => {\n const storageKey = `pls::${state.appId}`\n if (typeof payload.token === 'string' && payload.token) {\n safeLocalStorageSet(storageKey, payload.token)\n }\n })\n bus.on('PLUCKY_SET_FULLSCREEN', (payload: { fullscreen: boolean }) =>\n handleSetFullscreen(payload.fullscreen),\n )\n bus.on(\n 'PLUCKY_RESPONSE_RECEIVED',\n async (response: { messages: Array<SavedMessage>; chatId: string }) => {\n const lastMessage = response.messages[response.messages.length - 1]\n if (typeof lastMessage.content === 'string') return\n const toolResultIds = lastMessage.content\n .filter((m) => m.type === 'tool_result')\n .map((m) => m.toolUseId)\n const unfulfilledToolCalls = lastMessage.content\n .filter((m) => m.type === 'tool_use')\n .filter((m) => !toolResultIds.includes(m.id))\n if (unfulfilledToolCalls.length === 0) return\n const promises = unfulfilledToolCalls.map(\n async (toolCall): Promise<ToolResultContentBlock> => {\n const tool = state.tools.find((t) => t.name === toolCall.name)\n if (!tool) {\n return {\n toolUseId: toolCall.id,\n content: 'Tool not found.',\n type: 'tool_result',\n }\n }\n if (!tool.cb) {\n return {\n toolUseId: toolCall.id,\n content: 'Received.',\n type: 'tool_result',\n }\n }\n try {\n let input = toolCall.input as Record<string, unknown> | string\n if (typeof input === 'string') {\n input = JSON.parse(input) as Record<string, unknown>\n }\n const result = await tool.cb(input)\n return {\n toolUseId: toolCall.id,\n content: result,\n type: 'tool_result',\n }\n } catch (e) {\n return {\n toolUseId: toolCall.id,\n content: `Error: ${e instanceof Error ? e.message : String(e)}`,\n type: 'tool_result',\n }\n }\n },\n )\n\n const results = await Promise.all(promises)\n sendMessage({\n content: results,\n lastMessageId: lastMessage.id,\n chatId: response.chatId,\n })\n },\n )\n\n // Wait for iframe to signal it's ready before sending boot\n inited = true\n return api\n }\n\n function sendMessage(args: {\n content: string | Array<ToolResultContentBlock>\n lastMessageId?: string\n createChat?: boolean\n chatId?: string\n }) {\n const { content, lastMessageId, createChat, chatId } = args\n if (sidebar.isHidden()) {\n sidebar.show()\n }\n post('PLUCKY_SEND_MESSAGE', {\n content,\n createChat: createChat ?? false,\n lastMessageId,\n chatId,\n })\n }\n function setInput(input: string) {\n sidebar.getIframe()?.contentWindow?.focus()\n post('PLUCKY_SET_INPUT', { input })\n }\n\n function setMode(mode: Mode) {\n state.mode = mode\n currentMode = mode as 'push' | 'overlay'\n sidebar.setMode(mode)\n post('PLUCKY_SWITCH_MODE', mode)\n }\n\n function on(event: MessageType, cb: (p: any) => void) {\n return bus.on(event, cb)\n }\n\n function open() {\n sidebar.show()\n }\n\n function close() {\n sidebar.hide()\n }\n\n function toggle() {\n if (sidebar.isHidden()) {\n sidebar.show()\n } else {\n sidebar.hide()\n }\n }\n\n function registerTool(tool: ToolConfig, notify = true) {\n const currentToolState = getAllToolJSON()\n if (state.tools.find((t) => t.name === tool.name)) {\n state.tools = state.tools.filter((t) => t.name !== tool.name)\n }\n state.tools.push(tool)\n state.tools.sort((a, b) => a.name.localeCompare(b.name))\n const newToolState = getAllToolJSON()\n if (notify && !dequal(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function registerManyTools(tools: Array<ToolConfig>, notify = true) {\n const currentToolState = getAllToolJSON()\n for (const tool of tools) {\n registerTool(tool, false)\n }\n const newToolState = getAllToolJSON()\n if (notify && !dequal(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function removeTool(name: string, notify = true) {\n if (!state.tools.find((t) => t.name === name)) {\n console.warn(`Tool ${name} not found, skipping.`)\n }\n state.tools = state.tools.filter((t) => t.name !== name)\n if (notify) {\n notifyToolConfigUpdated()\n }\n }\n\n function notifyToolConfigUpdated() {\n post('PLUCKY_TOOL_CONFIG_UPDATED', { tools: getAllToolJSON() })\n }\n\n function getAllToolJSON(): Array<SimpleToolConfig> {\n return state.tools.map((t) => ({\n name: t.name,\n description: t.description,\n defaultLoadingText: t.defaultLoadingText,\n defaultSuccessText: t.defaultSuccessText,\n inputSchema:\n t.inputSchema instanceof ZodObject\n ? z.toJSONSchema(t.inputSchema)\n : t.inputSchema,\n }))\n }\n\n function setWidth(px: number) {\n state.width = px\n sidebar.setWidth(px)\n }\n\n function setFullscreen(fullscreen: boolean) {\n post('PLUCKY_SET_FULLSCREEN', { fullscreen })\n handleSetFullscreen(fullscreen)\n }\n\n function handleSetFullscreen(fullscreen: boolean) {\n state.fullscreen = fullscreen\n\n // Clean up existing resize listener if any\n if (resizeListener) {\n window.removeEventListener('resize', resizeListener)\n resizeListener = null\n }\n\n if (fullscreen) {\n // Use CSS calc to account for scrollbar width, which stays responsive on resize\n sidebar.setWidth(getFullscreenWidth())\n sidebar.setMode('overlay')\n\n // Add resize listener to update width if scrollbar appears/disappears\n resizeListener = () => {\n if (state.fullscreen) {\n sidebar.setWidth(getFullscreenWidth())\n }\n }\n window.addEventListener('resize', resizeListener)\n } else {\n sidebar.setWidth(state.width)\n sidebar.setMode('push')\n }\n }\n\n const api: PluckyAPI = {\n init,\n setMode,\n switchMode,\n open,\n close,\n toggle,\n on,\n isReady: () => sidebar.ready(),\n sendMessage,\n isOpen: () => !sidebar.isHidden(),\n registerTool,\n registerManyTools,\n removeTool,\n setInput,\n setWidth,\n setFullscreen,\n }\n return api\n}\n","import { createAPI } from './api'\nimport type { InitOptions, PluckyAPI } from './types'\n\n// singleton exported API for convenience\nlet api: PluckyAPI | null = null\n\nexport function init(opts: InitOptions) {\n if (!api) api = createAPI()\n return api.init(opts)\n}\n\nexport function getAPI() {\n if (!api) api = createAPI()\n return api\n}\n\n// convenience re-exports\nexport type * from './types'\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ToolResultContentBlock } from "@plucky-ai/llm";
2
- import { ZodObject } from "zod";
2
+ import { ZodObject, z } from "zod";
3
3
 
4
4
  //#region src/types.d.ts
5
5
  type Mode = 'push' | 'overlay';
@@ -10,9 +10,10 @@ interface SimpleToolConfig {
10
10
  defaultSuccessText?: string | null;
11
11
  inputSchema?: ZodObject | Record<string, unknown>;
12
12
  }
13
- interface ToolConfig extends SimpleToolConfig {
14
- inputSchema?: ZodObject | Record<string, unknown>;
15
- cb?: (input: Record<string, unknown>) => Promise<string>;
13
+ type InferToolInput<T$1 extends ZodObject<any> | Record<string, unknown> | undefined> = T$1 extends ZodObject<any> ? z.infer<T$1> : Record<string, unknown>;
14
+ interface ToolConfig<TInputSchema extends ZodObject<any> | Record<string, unknown> | undefined = undefined> extends Omit<SimpleToolConfig, 'inputSchema'> {
15
+ inputSchema?: TInputSchema;
16
+ cb?: (input: InferToolInput<TInputSchema>) => Promise<string> | string;
16
17
  }
17
18
  interface InitOptions {
18
19
  appId: string;
@@ -26,13 +27,15 @@ interface InitOptions {
26
27
  externalId?: string;
27
28
  metadata?: Record<string, any>;
28
29
  };
29
- tags?: string[];
30
- tools?: ToolConfig[];
30
+ tags?: Array<string>;
31
+ tools?: Array<ToolConfig<any>>;
31
32
  width?: number;
33
+ fullscreen?: boolean;
32
34
  }
33
35
  interface PluckyAPIOptions extends InitOptions {
34
- tools: ToolConfig[];
35
- tags: string[];
36
+ tools: Array<ToolConfig<any>>;
37
+ tags: Array<string>;
38
+ fullscreen?: boolean;
36
39
  }
37
40
  interface MountOptions {
38
41
  appId: string;
@@ -46,27 +49,28 @@ interface MountOptions {
46
49
  dir?: 'ltr' | 'rtl';
47
50
  }
48
51
  interface PluckyAPI {
49
- init(opts: InitOptions): PluckyAPI;
50
- setMode(mode: Mode): void;
51
- switchMode(mode: 'push' | 'overlay'): void;
52
- open(): void;
53
- close(): void;
54
- toggle(): void;
55
- on<T = any>(event: string, cb: (payload: T) => void): () => void;
56
- isReady(): boolean;
57
- setInput(input: string): void;
58
- sendMessage(args: {
52
+ init: (opts: InitOptions) => PluckyAPI;
53
+ setMode: (mode: Mode) => void;
54
+ switchMode: (mode: 'push' | 'overlay') => void;
55
+ open: () => void;
56
+ close: () => void;
57
+ toggle: () => void;
58
+ on: <T = any>(event: MessageType, cb: (payload: T) => void) => () => void;
59
+ isReady: () => boolean;
60
+ setInput: (input: string) => void;
61
+ sendMessage: (args: {
59
62
  content: string | Array<ToolResultContentBlock>;
60
63
  lastMessageId?: string;
61
64
  createChat?: boolean;
62
- }): void;
63
- isOpen(): boolean;
64
- registerTool(tool: ToolConfig): void;
65
- registerManyTools(tools: ToolConfig[]): void;
66
- removeTool(name: string): void;
67
- setWidth(px: number): void;
65
+ }) => void;
66
+ isOpen: () => boolean;
67
+ registerTool: (tool: ToolConfig<any>) => void;
68
+ registerManyTools: (tools: Array<ToolConfig<any>>) => void;
69
+ removeTool: (name: string) => void;
70
+ setWidth: (px: number) => void;
71
+ setFullscreen: (fullscreen: boolean) => void;
68
72
  }
69
- type MessageType = 'PLUCKY_LOAD_CONFIG' | 'PLUCKY_TOOL_CONFIG_UPDATED' | 'PLUCKY_SESSION_LOADED' | 'PLUCKY_CLOSE' | 'PLUCKY_SET_WIDTH' | 'PLUCKY_SWITCH_MODE' | 'PLUCKY_TOOL_CALL' | 'PLUCKY_WIDGET_MOUNTED' | 'PLUCKY_RESPONSE_RECEIVED' | 'PLUCKY_SEND_MESSAGE' | 'PLUCKY_SESSION_REQUEST' | 'PLUCKY_SESSION_UPDATED' | 'PLUCKY_SET_INPUT';
73
+ type MessageType = 'PLUCKY_LOAD_CONFIG' | 'PLUCKY_TOOL_CONFIG_UPDATED' | 'PLUCKY_SESSION_LOADED' | 'PLUCKY_CLOSE' | 'PLUCKY_SET_WIDTH' | 'PLUCKY_SWITCH_MODE' | 'PLUCKY_TOOL_CALL' | 'PLUCKY_WIDGET_MOUNTED' | 'PLUCKY_RESPONSE_RECEIVED' | 'PLUCKY_SEND_MESSAGE' | 'PLUCKY_SESSION_REQUEST' | 'PLUCKY_SESSION_UPDATED' | 'PLUCKY_SET_INPUT' | 'PLUCKY_SET_FULLSCREEN';
70
74
  //#endregion
71
75
  //#region src/index.d.ts
72
76
  declare function init(opts: InitOptions): PluckyAPI;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ZodObject } from "zod";
1
+ import { ZodObject, z } from "zod";
2
2
  import { ToolResultContentBlock } from "@plucky-ai/llm";
3
3
 
4
4
  //#region src/types.d.ts
@@ -10,9 +10,10 @@ interface SimpleToolConfig {
10
10
  defaultSuccessText?: string | null;
11
11
  inputSchema?: ZodObject | Record<string, unknown>;
12
12
  }
13
- interface ToolConfig extends SimpleToolConfig {
14
- inputSchema?: ZodObject | Record<string, unknown>;
15
- cb?: (input: Record<string, unknown>) => Promise<string>;
13
+ type InferToolInput<T$1 extends ZodObject<any> | Record<string, unknown> | undefined> = T$1 extends ZodObject<any> ? z.infer<T$1> : Record<string, unknown>;
14
+ interface ToolConfig<TInputSchema extends ZodObject<any> | Record<string, unknown> | undefined = undefined> extends Omit<SimpleToolConfig, 'inputSchema'> {
15
+ inputSchema?: TInputSchema;
16
+ cb?: (input: InferToolInput<TInputSchema>) => Promise<string> | string;
16
17
  }
17
18
  interface InitOptions {
18
19
  appId: string;
@@ -26,13 +27,15 @@ interface InitOptions {
26
27
  externalId?: string;
27
28
  metadata?: Record<string, any>;
28
29
  };
29
- tags?: string[];
30
- tools?: ToolConfig[];
30
+ tags?: Array<string>;
31
+ tools?: Array<ToolConfig<any>>;
31
32
  width?: number;
33
+ fullscreen?: boolean;
32
34
  }
33
35
  interface PluckyAPIOptions extends InitOptions {
34
- tools: ToolConfig[];
35
- tags: string[];
36
+ tools: Array<ToolConfig<any>>;
37
+ tags: Array<string>;
38
+ fullscreen?: boolean;
36
39
  }
37
40
  interface MountOptions {
38
41
  appId: string;
@@ -46,27 +49,28 @@ interface MountOptions {
46
49
  dir?: 'ltr' | 'rtl';
47
50
  }
48
51
  interface PluckyAPI {
49
- init(opts: InitOptions): PluckyAPI;
50
- setMode(mode: Mode): void;
51
- switchMode(mode: 'push' | 'overlay'): void;
52
- open(): void;
53
- close(): void;
54
- toggle(): void;
55
- on<T = any>(event: string, cb: (payload: T) => void): () => void;
56
- isReady(): boolean;
57
- setInput(input: string): void;
58
- sendMessage(args: {
52
+ init: (opts: InitOptions) => PluckyAPI;
53
+ setMode: (mode: Mode) => void;
54
+ switchMode: (mode: 'push' | 'overlay') => void;
55
+ open: () => void;
56
+ close: () => void;
57
+ toggle: () => void;
58
+ on: <T = any>(event: MessageType, cb: (payload: T) => void) => () => void;
59
+ isReady: () => boolean;
60
+ setInput: (input: string) => void;
61
+ sendMessage: (args: {
59
62
  content: string | Array<ToolResultContentBlock>;
60
63
  lastMessageId?: string;
61
64
  createChat?: boolean;
62
- }): void;
63
- isOpen(): boolean;
64
- registerTool(tool: ToolConfig): void;
65
- registerManyTools(tools: ToolConfig[]): void;
66
- removeTool(name: string): void;
67
- setWidth(px: number): void;
65
+ }) => void;
66
+ isOpen: () => boolean;
67
+ registerTool: (tool: ToolConfig<any>) => void;
68
+ registerManyTools: (tools: Array<ToolConfig<any>>) => void;
69
+ removeTool: (name: string) => void;
70
+ setWidth: (px: number) => void;
71
+ setFullscreen: (fullscreen: boolean) => void;
68
72
  }
69
- type MessageType = 'PLUCKY_LOAD_CONFIG' | 'PLUCKY_TOOL_CONFIG_UPDATED' | 'PLUCKY_SESSION_LOADED' | 'PLUCKY_CLOSE' | 'PLUCKY_SET_WIDTH' | 'PLUCKY_SWITCH_MODE' | 'PLUCKY_TOOL_CALL' | 'PLUCKY_WIDGET_MOUNTED' | 'PLUCKY_RESPONSE_RECEIVED' | 'PLUCKY_SEND_MESSAGE' | 'PLUCKY_SESSION_REQUEST' | 'PLUCKY_SESSION_UPDATED' | 'PLUCKY_SET_INPUT';
73
+ type MessageType = 'PLUCKY_LOAD_CONFIG' | 'PLUCKY_TOOL_CONFIG_UPDATED' | 'PLUCKY_SESSION_LOADED' | 'PLUCKY_CLOSE' | 'PLUCKY_SET_WIDTH' | 'PLUCKY_SWITCH_MODE' | 'PLUCKY_TOOL_CALL' | 'PLUCKY_WIDGET_MOUNTED' | 'PLUCKY_RESPONSE_RECEIVED' | 'PLUCKY_SEND_MESSAGE' | 'PLUCKY_SESSION_REQUEST' | 'PLUCKY_SESSION_UPDATED' | 'PLUCKY_SET_INPUT' | 'PLUCKY_SET_FULLSCREEN';
70
74
  //#endregion
71
75
  //#region src/index.d.ts
72
76
  declare function init(opts: InitOptions): PluckyAPI;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import _ from "lodash";
1
+ import { dequal } from "dequal";
2
2
  import { ZodObject, z } from "zod";
3
3
 
4
4
  //#region src/bus.ts
@@ -63,7 +63,7 @@ body.${shiftClass} {
63
63
  inset-block: 0;
64
64
  inset-inline-end: 0;
65
65
  width: var(--${idPrefix}-w);
66
- max-width: 90vw;
66
+ max-width: 100vw;
67
67
  z-index: ${zIndex};
68
68
  background: transparent;
69
69
  }
@@ -122,9 +122,9 @@ body.${shiftClass} {
122
122
  if (!hidden && mode === "push") document.body.classList.add(shiftClass);
123
123
  else document.body.classList.remove(shiftClass);
124
124
  }
125
- function setWidth(px) {
126
- widthPx = Math.max(0, Math.round(px));
127
- document.documentElement.style.setProperty(`--${idPrefix}-w`, `${widthPx}px`);
125
+ function setWidth(width) {
126
+ const widthCssValue = typeof width === "number" ? `${Math.max(0, Math.round(width))}px` : width;
127
+ document.documentElement.style.setProperty(`--${idPrefix}-w`, widthCssValue);
128
128
  }
129
129
  function setZIndex(z$1) {
130
130
  zIndex = z$1;
@@ -176,6 +176,21 @@ body.${shiftClass} {
176
176
  };
177
177
  }
178
178
 
179
+ //#endregion
180
+ //#region src/utils.ts
181
+ function safeLocalStorageGet(key) {
182
+ try {
183
+ return window.localStorage.getItem(key);
184
+ } catch {
185
+ return null;
186
+ }
187
+ }
188
+ function safeLocalStorageSet(key, value) {
189
+ try {
190
+ window.localStorage.setItem(key, value);
191
+ } catch {}
192
+ }
193
+
179
194
  //#endregion
180
195
  //#region src/api.ts
181
196
  function createAPI() {
@@ -190,9 +205,18 @@ function createAPI() {
190
205
  user: {},
191
206
  tools: [],
192
207
  tags: [],
193
- width: 360
208
+ width: 360,
209
+ fullscreen: false
194
210
  };
195
211
  let currentMode = "push";
212
+ let resizeListener = null;
213
+ function getScrollbarWidth() {
214
+ return window.innerWidth - document.documentElement.clientWidth;
215
+ }
216
+ function getFullscreenWidth() {
217
+ const scrollbarWidth = getScrollbarWidth();
218
+ return scrollbarWidth > 0 ? `calc(100vw - ${scrollbarWidth}px)` : "100vw";
219
+ }
196
220
  function post(type, payload) {
197
221
  const iframe = sidebar.getIframe();
198
222
  const targetOrigin = new URL(state.baseUrl).origin;
@@ -240,8 +264,9 @@ function createAPI() {
240
264
  bus.on("PLUCKY_SWITCH_MODE", (m) => setMode(m));
241
265
  bus.on("PLUCKY_SESSION_UPDATED", (payload) => {
242
266
  const storageKey = `pls::${state.appId}`;
243
- if (payload && typeof payload.token === "string" && payload.token) safeLocalStorageSet(storageKey, payload.token);
267
+ if (typeof payload.token === "string" && payload.token) safeLocalStorageSet(storageKey, payload.token);
244
268
  });
269
+ bus.on("PLUCKY_SET_FULLSCREEN", (payload) => handleSetFullscreen(payload.fullscreen));
245
270
  bus.on("PLUCKY_RESPONSE_RECEIVED", async (response) => {
246
271
  const lastMessage = response.messages[response.messages.length - 1];
247
272
  if (typeof lastMessage.content === "string") return;
@@ -325,13 +350,13 @@ function createAPI() {
325
350
  state.tools.push(tool);
326
351
  state.tools.sort((a, b) => a.name.localeCompare(b.name));
327
352
  const newToolState = getAllToolJSON();
328
- if (notify && !_.isEqual(currentToolState, newToolState)) notifyToolConfigUpdated();
353
+ if (notify && !dequal(currentToolState, newToolState)) notifyToolConfigUpdated();
329
354
  }
330
355
  function registerManyTools(tools, notify = true) {
331
356
  const currentToolState = getAllToolJSON();
332
357
  for (const tool of tools) registerTool(tool, false);
333
358
  const newToolState = getAllToolJSON();
334
- if (notify && !_.isEqual(currentToolState, newToolState)) notifyToolConfigUpdated();
359
+ if (notify && !dequal(currentToolState, newToolState)) notifyToolConfigUpdated();
335
360
  }
336
361
  function removeTool(name, notify = true) {
337
362
  if (!state.tools.find((t) => t.name === name)) console.warn(`Tool ${name} not found, skipping.`);
@@ -350,6 +375,32 @@ function createAPI() {
350
375
  inputSchema: t.inputSchema instanceof ZodObject ? z.toJSONSchema(t.inputSchema) : t.inputSchema
351
376
  }));
352
377
  }
378
+ function setWidth(px) {
379
+ state.width = px;
380
+ sidebar.setWidth(px);
381
+ }
382
+ function setFullscreen(fullscreen) {
383
+ post("PLUCKY_SET_FULLSCREEN", { fullscreen });
384
+ handleSetFullscreen(fullscreen);
385
+ }
386
+ function handleSetFullscreen(fullscreen) {
387
+ state.fullscreen = fullscreen;
388
+ if (resizeListener) {
389
+ window.removeEventListener("resize", resizeListener);
390
+ resizeListener = null;
391
+ }
392
+ if (fullscreen) {
393
+ sidebar.setWidth(getFullscreenWidth());
394
+ sidebar.setMode("overlay");
395
+ resizeListener = () => {
396
+ if (state.fullscreen) sidebar.setWidth(getFullscreenWidth());
397
+ };
398
+ window.addEventListener("resize", resizeListener);
399
+ } else {
400
+ sidebar.setWidth(state.width);
401
+ sidebar.setMode("push");
402
+ }
403
+ }
353
404
  const api$1 = {
354
405
  init: init$1,
355
406
  setMode,
@@ -365,22 +416,11 @@ function createAPI() {
365
416
  registerManyTools,
366
417
  removeTool,
367
418
  setInput,
368
- setWidth: sidebar.setWidth
419
+ setWidth,
420
+ setFullscreen
369
421
  };
370
422
  return api$1;
371
423
  }
372
- function safeLocalStorageGet(key) {
373
- try {
374
- return window.localStorage.getItem(key);
375
- } catch {
376
- return null;
377
- }
378
- }
379
- function safeLocalStorageSet(key, value) {
380
- try {
381
- window.localStorage.setItem(key, value);
382
- } catch {}
383
- }
384
424
 
385
425
  //#endregion
386
426
  //#region src/index.ts
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["host: HTMLDivElement | null","iframe: HTMLIFrameElement | null","mode: Mode","nonce: string | undefined","css","z","state: Required<PluckyAPIOptions>","currentMode: 'push' | 'overlay'","init","api","api: PluckyAPI","api: PluckyAPI | null"],"sources":["../src/bus.ts","../src/sidebar.ts","../src/api.ts","../src/index.ts"],"sourcesContent":["import type { MessageType } from './types'\n\ntype Handler = (p: any) => void\n\nexport class Bus {\n private handlers = new Map<MessageType, Set<Handler>>()\n\n on(event: MessageType, cb: Handler) {\n if (!this.handlers.has(event)) this.handlers.set(event, new Set())\n this.handlers.get(event)!.add(cb)\n return () => this.handlers.get(event)!.delete(cb)\n }\n\n emit(event: MessageType, payload?: any) {\n this.handlers.get(event)?.forEach((h) => h(payload))\n }\n}\n\nexport function bindPostMessage(bus: Bus, win: Window, origin: string) {\n const onMsg = (e: MessageEvent) => {\n if (e.origin !== origin) return\n if (!e.data || typeof e.data !== 'object') return\n if (!('type' in e.data)) return\n bus.emit(e.data.type, e.data.payload)\n }\n win.addEventListener('message', onMsg)\n return () => win.removeEventListener('message', onMsg)\n}\n","export type Mode = 'push' | 'overlay'\n\nexport interface SidebarOpts {\n appId: string\n baseUrl: string\n widthPx?: number\n zIndex?: number\n idPrefix?: string\n nonce?: string\n iframeTitle?: string\n initialMode?: Mode // 'push' (default) or 'overlay'\n}\n\nexport interface SidebarAPI {\n mount(opts: SidebarOpts): void\n unmount(): void\n setMode(mode: Mode): void\n setWidth(px: number): void\n setZIndex(z: number): void\n hide(): void // NEW: hide container + remove padding, keep iframe loaded\n show(): void // NEW: restore visibility + padding if mode==='push'\n isHidden(): boolean\n getMode(): Mode\n getContainer(): HTMLDivElement | null\n getIframe(): HTMLIFrameElement | null\n ready(): boolean\n}\n\nexport function createSidebar(): SidebarAPI {\n let host: HTMLDivElement | null = null\n let iframe: HTMLIFrameElement | null = null\n let mounted = false\n let hidden = false\n let mode: Mode = 'push'\n let widthPx = 360\n let zIndex = 2147483646\n let idPrefix = 'plucky'\n let cssId = ''\n let hostId = ''\n let shiftClass = ''\n let hiddenClass = ''\n let nonce: string | undefined\n\n const ensureStyle = (css: string) => {\n let tag = document.getElementById(cssId) as HTMLStyleElement | null\n if (!tag) {\n tag = document.createElement('style')\n tag.id = cssId\n if (nonce) tag.setAttribute('nonce', nonce)\n document.head.appendChild(tag)\n }\n tag.textContent = css\n }\n\n const css = () => `\n:root { --${idPrefix}-w: ${widthPx}px; }\n\nbody.${shiftClass} {\n padding-inline-end: var(--${idPrefix}-w) !important;\n}\n\n/* Fixed sidebar; overlay vs push is just whether body has padding */\n#${hostId} {\n position: fixed;\n inset-block: 0;\n inset-inline-end: 0;\n width: var(--${idPrefix}-w);\n max-width: 90vw;\n z-index: ${zIndex};\n background: transparent;\n}\n#${hostId} > iframe { width: 100%; height: 100%; border: 0; display: block; }\n\n/* Hidden state: keep in DOM, don’t render or accept focus/clicks */\n#${hostId}.${hiddenClass} {\n display: none !important;\n}\n`\n\n function mount(opts: SidebarOpts) {\n if (mounted) return\n\n widthPx = opts.widthPx ?? widthPx\n zIndex = opts.zIndex ?? zIndex\n idPrefix = opts.idPrefix ?? idPrefix\n nonce = opts.nonce\n mode = opts.initialMode ?? mode\n\n hostId = `${idPrefix}-sidebar`\n cssId = `${idPrefix}-sidebar-css`\n shiftClass = `${idPrefix}-shift`\n hiddenClass = `${idPrefix}-hidden`\n\n ensureStyle(css())\n\n host = document.getElementById(hostId) as HTMLDivElement | null\n if (!host) {\n host = document.createElement('div')\n host.id = hostId\n host.style.zIndex = String(zIndex)\n document.body.appendChild(host)\n }\n\n if (!iframe) {\n iframe = document.createElement('iframe')\n iframe.title = opts.iframeTitle ?? 'Plucky'\n iframe.setAttribute('aria-label', iframe.title)\n const base = opts.baseUrl.replace(/\\/$/, '')\n iframe.src = `${base}/widget/${encodeURIComponent(opts.appId)}`\n host.appendChild(iframe)\n }\n\n // Apply current visibility + mode\n if (hidden) host.classList.add(hiddenClass)\n else host.classList.remove(hiddenClass)\n\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n\n mounted = true\n }\n\n function unmount() {\n if (!mounted) return\n document.body.classList.remove(shiftClass)\n host?.remove()\n document.getElementById(cssId)?.remove()\n host = null\n iframe = null\n mounted = false\n hidden = false\n }\n\n function setMode(next: Mode) {\n mode = next\n if (!mounted) return\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n }\n\n function setWidth(px: number) {\n widthPx = Math.max(0, Math.round(px))\n document.documentElement.style.setProperty(\n `--${idPrefix}-w`,\n `${widthPx}px`,\n )\n }\n\n function setZIndex(z: number) {\n zIndex = z\n if (host) host.style.zIndex = String(zIndex)\n }\n\n function hide() {\n if (!mounted || hidden) return\n hidden = true\n // Remove page padding if present\n document.body.classList.remove(shiftClass)\n // Hide the host container without unmounting the iframe\n host?.classList.add(hiddenClass)\n if (host) {\n host.setAttribute('aria-hidden', 'true')\n // Disable interactivity for assistive tech even if CSS changes\n ;(host as any).inert = true // harmless if not supported\n }\n // Optional: signal iframe to pause work\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'hidden' },\n '*',\n )\n }\n\n function show() {\n if (!mounted || !hidden) return\n hidden = false\n host?.classList.remove(hiddenClass)\n if (host) {\n host.removeAttribute('aria-hidden')\n try {\n ;(host as any).inert = false\n } catch {}\n }\n if (mode === 'push') document.body.classList.add(shiftClass)\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'visible' },\n '*',\n )\n }\n\n return {\n mount,\n unmount,\n setMode,\n setWidth,\n setZIndex,\n hide,\n show,\n isHidden: () => hidden,\n getMode: () => mode,\n getContainer: () => host,\n getIframe: () => iframe,\n ready: () => !!iframe?.contentWindow,\n }\n}\n","import type { SavedMessage, ToolResultContentBlock } from '@plucky-ai/llm'\nimport _ from 'lodash'\nimport { z, ZodObject } from 'zod'\nimport { bindPostMessage, Bus } from './bus'\nimport { createSidebar } from './sidebar'\nimport type {\n InitOptions,\n MessageType,\n Mode,\n PluckyAPI,\n PluckyAPIOptions,\n SimpleToolConfig,\n ToolConfig,\n} from './types'\n\nexport function createAPI(): PluckyAPI {\n const bus = new Bus()\n let inited = false\n\n // Layout controllers\n const sidebar = createSidebar()\n\n // runtime state with sane defaults\n let state: Required<PluckyAPIOptions> = {\n appId: '',\n baseUrl: 'https://widget.plucky.ai',\n mode: 'push',\n containerId: undefined as unknown as string,\n user: {},\n tools: [],\n tags: [],\n width: 360,\n }\n\n // Track current mode and last mount options\n let currentMode: 'push' | 'overlay' = 'push'\n\n // Sidebar is the single DOM/layout owner; we post directly to its iframe\n function post(type: MessageType, payload?: any) {\n const iframe = sidebar.getIframe()\n const targetOrigin = new URL(state.baseUrl).origin\n iframe?.contentWindow?.postMessage({ type, payload }, targetOrigin)\n }\n\n function switchMode(next: 'push' | 'overlay') {\n if (next === currentMode) return\n\n // Just switch mode on the single layout\n sidebar.setMode(next)\n currentMode = next\n }\n\n function init(opts: InitOptions): PluckyAPI {\n if (inited) return api\n const { tools, ...rest } = opts\n state = { ...state, ...rest } as Required<PluckyAPIOptions>\n if (tools && tools.length > 0) {\n registerManyTools(tools, false)\n }\n currentMode = state.mode as 'push' | 'overlay'\n bindPostMessage(bus, window, new URL(state.baseUrl).origin)\n\n bus.on('PLUCKY_CLOSE', () => sidebar.hide())\n\n // Listen for iframe mount signal before sending boot\n bus.on('PLUCKY_WIDGET_MOUNTED', () => {\n const storageKey = `pls::${state.appId}`\n const existingToken = safeLocalStorageGet(storageKey)\n post('PLUCKY_LOAD_CONFIG', {\n appId: state.appId,\n user: state.user,\n mode: state.mode,\n sessionToken: existingToken || undefined,\n tools: getAllToolJSON(),\n tags: state.tags,\n })\n if (!existingToken) {\n post('PLUCKY_SESSION_REQUEST', { reason: 'missing' })\n }\n })\n\n // Mount the sidebar (creates iframe inside it)\n sidebar.mount({\n appId: state.appId,\n baseUrl: state.baseUrl,\n iframeTitle: 'Plucky',\n initialMode: currentMode,\n widthPx: state.width,\n })\n\n // Wire bus-driven layout changes to the sidebar\n bus.on('PLUCKY_SET_WIDTH', (px: number) => sidebar.setWidth(px))\n bus.on('PLUCKY_SWITCH_MODE', (m: Mode) => setMode(m))\n bus.on('PLUCKY_SESSION_UPDATED', (payload: { token?: string }) => {\n const storageKey = `pls::${state.appId}`\n if (payload && typeof payload.token === 'string' && payload.token) {\n safeLocalStorageSet(storageKey, payload.token)\n }\n })\n bus.on(\n 'PLUCKY_RESPONSE_RECEIVED',\n async (response: { messages: SavedMessage[]; chatId: string }) => {\n const lastMessage = response.messages[response.messages.length - 1]\n if (typeof lastMessage.content === 'string') return\n const toolResultIds = lastMessage.content\n .filter((m) => m.type === 'tool_result')\n .map((m) => m.toolUseId)\n const unfulfilledToolCalls = lastMessage.content\n .filter((m) => m.type === 'tool_use')\n .filter((m) => !toolResultIds.includes(m.id))\n if (unfulfilledToolCalls.length === 0) return\n const promises = unfulfilledToolCalls.map(\n async (toolCall): Promise<ToolResultContentBlock> => {\n const tool = state.tools.find((t) => t.name === toolCall.name)\n if (!tool) {\n return {\n toolUseId: toolCall.id,\n content: 'Tool not found.',\n type: 'tool_result',\n }\n }\n if (!tool.cb) {\n return {\n toolUseId: toolCall.id,\n content: 'Received.',\n type: 'tool_result',\n }\n }\n try {\n let input = toolCall.input as Record<string, unknown> | string\n if (typeof input === 'string') {\n input = JSON.parse(input) as Record<string, unknown>\n }\n const result = await tool.cb(input)\n return {\n toolUseId: toolCall.id,\n content: result,\n type: 'tool_result',\n }\n } catch (e) {\n return {\n toolUseId: toolCall.id,\n content: `Error: ${e instanceof Error ? e.message : String(e)}`,\n type: 'tool_result',\n }\n }\n },\n )\n\n const results = await Promise.all(promises)\n sendMessage({\n content: results,\n lastMessageId: lastMessage.id,\n chatId: response.chatId,\n })\n },\n )\n\n // Wait for iframe to signal it's ready before sending boot\n inited = true\n return api\n }\n\n function sendMessage(args: {\n content: string | Array<ToolResultContentBlock>\n lastMessageId?: string\n createChat?: boolean\n chatId?: string\n }) {\n const { content, lastMessageId, createChat, chatId } = args\n if (sidebar.isHidden()) {\n sidebar.show()\n }\n post('PLUCKY_SEND_MESSAGE', {\n content,\n createChat: createChat ?? false,\n lastMessageId,\n chatId,\n })\n }\n function setInput(input: string) {\n sidebar.getIframe()?.contentWindow?.focus()\n post('PLUCKY_SET_INPUT', { input })\n }\n\n function setMode(mode: Mode) {\n state.mode = mode\n currentMode = mode as 'push' | 'overlay'\n sidebar.setMode(mode)\n post('PLUCKY_SWITCH_MODE', mode)\n }\n\n function on(event: MessageType, cb: (p: any) => void) {\n return bus.on(event, cb)\n }\n\n function open() {\n sidebar.show()\n }\n\n function close() {\n sidebar.hide()\n }\n\n function toggle() {\n if (sidebar.isHidden()) {\n sidebar.show()\n } else {\n sidebar.hide()\n }\n }\n\n function registerTool(tool: ToolConfig, notify = true) {\n const currentToolState = getAllToolJSON()\n if (state.tools.find((t) => t.name === tool.name)) {\n state.tools = state.tools.filter((t) => t.name !== tool.name)\n }\n state.tools.push(tool)\n state.tools.sort((a, b) => a.name.localeCompare(b.name))\n const newToolState = getAllToolJSON()\n if (notify && !_.isEqual(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function registerManyTools(tools: ToolConfig[], notify = true) {\n const currentToolState = getAllToolJSON()\n for (const tool of tools) {\n registerTool(tool, false)\n }\n const newToolState = getAllToolJSON()\n if (notify && !_.isEqual(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function removeTool(name: string, notify = true) {\n if (!state.tools.find((t) => t.name === name)) {\n console.warn(`Tool ${name} not found, skipping.`)\n }\n state.tools = state.tools.filter((t) => t.name !== name)\n if (notify) {\n notifyToolConfigUpdated()\n }\n }\n\n function notifyToolConfigUpdated() {\n post('PLUCKY_TOOL_CONFIG_UPDATED', { tools: getAllToolJSON() })\n }\n\n function getAllToolJSON(): SimpleToolConfig[] {\n return state.tools.map((t) => ({\n name: t.name,\n description: t.description,\n defaultLoadingText: t.defaultLoadingText,\n defaultSuccessText: t.defaultSuccessText,\n inputSchema:\n t.inputSchema instanceof ZodObject\n ? z.toJSONSchema(t.inputSchema)\n : t.inputSchema,\n }))\n }\n\n const api: PluckyAPI = {\n init,\n setMode,\n switchMode,\n open,\n close,\n toggle,\n on,\n isReady: () => sidebar.ready(),\n sendMessage,\n isOpen: () => !sidebar.isHidden(),\n registerTool,\n registerManyTools,\n removeTool,\n setInput,\n setWidth: sidebar.setWidth,\n }\n return api\n}\n\nfunction safeLocalStorageGet(key: string): string | null {\n try {\n return window.localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction safeLocalStorageSet(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value)\n } catch {}\n}\n","import { createAPI } from './api'\nimport type { InitOptions, PluckyAPI } from './types'\n\n// singleton exported API for convenience\nlet api: PluckyAPI | null = null\n\nexport function init(opts: InitOptions) {\n if (!api) api = createAPI()\n return api.init(opts)\n}\n\nexport function getAPI() {\n if (!api) api = createAPI()\n return api\n}\n\n// convenience re-exports\nexport type * from './types'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["host: HTMLDivElement | null","iframe: HTMLIFrameElement | null","mode: Mode","widthPx: number | string","nonce: string | undefined","css","z","state: Required<PluckyAPIOptions>","currentMode: 'push' | 'overlay'","resizeListener: (() => void) | null","init","api","api: PluckyAPI","api: PluckyAPI | null"],"sources":["../src/bus.ts","../src/sidebar.ts","../src/utils.ts","../src/api.ts","../src/index.ts"],"sourcesContent":["import type { MessageType } from './types'\n\ntype Handler = (p: any) => void\n\nexport class Bus {\n private handlers = new Map<MessageType, Set<Handler>>()\n\n on(event: MessageType, cb: Handler) {\n if (!this.handlers.has(event)) this.handlers.set(event, new Set())\n this.handlers.get(event)!.add(cb)\n return () => this.handlers.get(event)!.delete(cb)\n }\n\n emit(event: MessageType, payload?: any) {\n this.handlers.get(event)?.forEach((h) => h(payload))\n }\n}\n\nexport function bindPostMessage(bus: Bus, win: Window, origin: string) {\n const onMsg = (e: MessageEvent) => {\n if (e.origin !== origin) return\n if (!e.data || typeof e.data !== 'object') return\n if (!('type' in e.data)) return\n bus.emit(e.data.type, e.data.payload)\n }\n win.addEventListener('message', onMsg)\n return () => win.removeEventListener('message', onMsg)\n}\n","export type Mode = 'push' | 'overlay'\n\nexport interface SidebarOpts {\n appId: string\n baseUrl: string\n widthPx?: number\n zIndex?: number\n idPrefix?: string\n nonce?: string\n iframeTitle?: string\n initialMode?: Mode // 'push' (default) or 'overlay'\n}\n\nexport interface SidebarAPI {\n mount: (opts: SidebarOpts) => void\n unmount: () => void\n setMode: (mode: Mode) => void\n setWidth: (px: number | string) => void\n setZIndex: (z: number) => void\n hide: () => void // NEW: hide container + remove padding, keep iframe loaded\n show: () => void // NEW: restore visibility + padding if mode==='push'\n isHidden: () => boolean\n getMode: () => Mode\n getContainer: () => HTMLDivElement | null\n getIframe: () => HTMLIFrameElement | null\n ready: () => boolean\n}\n\nexport function createSidebar(): SidebarAPI {\n let host: HTMLDivElement | null = null\n let iframe: HTMLIFrameElement | null = null\n let mounted = false\n let hidden = false\n let mode: Mode = 'push'\n let widthPx: number | string = 360\n let zIndex = 2147483646\n let idPrefix = 'plucky'\n let cssId = ''\n let hostId = ''\n let shiftClass = ''\n let hiddenClass = ''\n let nonce: string | undefined\n\n const ensureStyle = (css: string) => {\n let tag = document.getElementById(cssId) as HTMLStyleElement | null\n if (!tag) {\n tag = document.createElement('style')\n tag.id = cssId\n if (nonce) tag.setAttribute('nonce', nonce)\n document.head.appendChild(tag)\n }\n tag.textContent = css\n }\n\n const css = () => `\n:root { --${idPrefix}-w: ${widthPx}px; }\n\nbody.${shiftClass} {\n padding-inline-end: var(--${idPrefix}-w) !important;\n}\n\n/* Fixed sidebar; overlay vs push is just whether body has padding */\n#${hostId} {\n position: fixed;\n inset-block: 0;\n inset-inline-end: 0;\n width: var(--${idPrefix}-w);\n max-width: 100vw;\n z-index: ${zIndex};\n background: transparent;\n}\n#${hostId} > iframe { width: 100%; height: 100%; border: 0; display: block; }\n\n/* Hidden state: keep in DOM, don’t render or accept focus/clicks */\n#${hostId}.${hiddenClass} {\n display: none !important;\n}\n`\n\n function mount(opts: SidebarOpts) {\n if (mounted) return\n\n widthPx = opts.widthPx ?? widthPx\n zIndex = opts.zIndex ?? zIndex\n idPrefix = opts.idPrefix ?? idPrefix\n nonce = opts.nonce\n mode = opts.initialMode ?? mode\n\n hostId = `${idPrefix}-sidebar`\n cssId = `${idPrefix}-sidebar-css`\n shiftClass = `${idPrefix}-shift`\n hiddenClass = `${idPrefix}-hidden`\n\n ensureStyle(css())\n\n host = document.getElementById(hostId) as HTMLDivElement | null\n if (!host) {\n host = document.createElement('div')\n host.id = hostId\n host.style.zIndex = String(zIndex)\n document.body.appendChild(host)\n }\n\n if (!iframe) {\n iframe = document.createElement('iframe')\n iframe.title = opts.iframeTitle ?? 'Plucky'\n iframe.setAttribute('aria-label', iframe.title)\n const base = opts.baseUrl.replace(/\\/$/, '')\n iframe.src = `${base}/widget/${encodeURIComponent(opts.appId)}`\n host.appendChild(iframe)\n }\n\n // Apply current visibility + mode\n if (hidden) host.classList.add(hiddenClass)\n else host.classList.remove(hiddenClass)\n\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n\n mounted = true\n }\n\n function unmount() {\n if (!mounted) return\n document.body.classList.remove(shiftClass)\n host?.remove()\n document.getElementById(cssId)?.remove()\n host = null\n iframe = null\n mounted = false\n hidden = false\n }\n\n function setMode(next: Mode) {\n mode = next\n if (!mounted) return\n if (!hidden && mode === 'push') document.body.classList.add(shiftClass)\n else document.body.classList.remove(shiftClass)\n }\n\n function setWidth(width: number | string) {\n const widthCssValue =\n typeof width === 'number' ? `${Math.max(0, Math.round(width))}px` : width\n document.documentElement.style.setProperty(`--${idPrefix}-w`, widthCssValue)\n }\n\n function setZIndex(z: number) {\n zIndex = z\n if (host) host.style.zIndex = String(zIndex)\n }\n\n function hide() {\n if (!mounted || hidden) return\n hidden = true\n // Remove page padding if present\n document.body.classList.remove(shiftClass)\n // Hide the host container without unmounting the iframe\n host?.classList.add(hiddenClass)\n if (host) {\n host.setAttribute('aria-hidden', 'true')\n // Disable interactivity for assistive tech even if CSS changes\n ;(host as any).inert = true // harmless if not supported\n }\n // Optional: signal iframe to pause work\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'hidden' },\n '*',\n )\n }\n\n function show() {\n if (!mounted || !hidden) return\n hidden = false\n host?.classList.remove(hiddenClass)\n if (host) {\n host.removeAttribute('aria-hidden')\n try {\n ;(host as any).inert = false\n } catch {}\n }\n if (mode === 'push') document.body.classList.add(shiftClass)\n iframe?.contentWindow?.postMessage(\n { type: 'PLUCKY_VISIBILITY', payload: 'visible' },\n '*',\n )\n }\n\n return {\n mount,\n unmount,\n setMode,\n setWidth,\n setZIndex,\n hide,\n show,\n isHidden: () => hidden,\n getMode: () => mode,\n getContainer: () => host,\n getIframe: () => iframe,\n ready: () => !!iframe?.contentWindow,\n }\n}\n","export function safeLocalStorageGet(key: string): string | null {\n try {\n return window.localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nexport function safeLocalStorageSet(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value)\n } catch {}\n}\n","import type {\n SavedMessage,\n ToolResultContentBlock,\n} from '@plucky-ai/llm-schemas'\nimport { dequal } from 'dequal'\nimport { ZodObject, z } from 'zod'\nimport { Bus, bindPostMessage } from './bus'\nimport { createSidebar } from './sidebar'\nimport type {\n InitOptions,\n MessageType,\n Mode,\n PluckyAPI,\n PluckyAPIOptions,\n SimpleToolConfig,\n ToolConfig,\n} from './types'\nimport { safeLocalStorageGet, safeLocalStorageSet } from './utils'\n\nexport function createAPI(): PluckyAPI {\n const bus = new Bus()\n let inited = false\n\n // Layout controllers\n const sidebar = createSidebar()\n\n // runtime state with sane defaults\n let state: Required<PluckyAPIOptions> = {\n appId: '',\n baseUrl: 'https://widget.plucky.ai',\n mode: 'push',\n containerId: undefined as unknown as string,\n user: {},\n tools: [],\n tags: [],\n width: 360,\n fullscreen: false,\n }\n\n // Track current mode and last mount options\n let currentMode: 'push' | 'overlay' = 'push'\n\n // Track resize listener for fullscreen mode\n let resizeListener: (() => void) | null = null\n\n // Helper to calculate scrollbar width\n function getScrollbarWidth(): number {\n return window.innerWidth - document.documentElement.clientWidth\n }\n\n // Helper to get fullscreen width CSS value accounting for scrollbar\n function getFullscreenWidth(): string {\n const scrollbarWidth = getScrollbarWidth()\n return scrollbarWidth > 0 ? `calc(100vw - ${scrollbarWidth}px)` : '100vw'\n }\n\n // Sidebar is the single DOM/layout owner; we post directly to its iframe\n function post(type: MessageType, payload?: any) {\n const iframe = sidebar.getIframe()\n const targetOrigin = new URL(state.baseUrl).origin\n iframe?.contentWindow?.postMessage({ type, payload }, targetOrigin)\n }\n\n function switchMode(next: 'push' | 'overlay') {\n if (next === currentMode) return\n\n // Just switch mode on the single layout\n sidebar.setMode(next)\n currentMode = next\n }\n\n function init(opts: InitOptions): PluckyAPI {\n if (inited) return api\n const { tools, ...rest } = opts\n state = { ...state, ...rest } as Required<PluckyAPIOptions>\n if (tools && tools.length > 0) {\n registerManyTools(tools, false)\n }\n currentMode = state.mode as 'push' | 'overlay'\n bindPostMessage(bus, window, new URL(state.baseUrl).origin)\n\n bus.on('PLUCKY_CLOSE', () => sidebar.hide())\n\n // Listen for iframe mount signal before sending boot\n bus.on('PLUCKY_WIDGET_MOUNTED', () => {\n const storageKey = `pls::${state.appId}`\n const existingToken = safeLocalStorageGet(storageKey)\n post('PLUCKY_LOAD_CONFIG', {\n appId: state.appId,\n user: state.user,\n mode: state.mode,\n sessionToken: existingToken || undefined,\n tools: getAllToolJSON(),\n tags: state.tags,\n })\n if (!existingToken) {\n post('PLUCKY_SESSION_REQUEST', { reason: 'missing' })\n }\n })\n\n // Mount the sidebar (creates iframe inside it)\n sidebar.mount({\n appId: state.appId,\n baseUrl: state.baseUrl,\n iframeTitle: 'Plucky',\n initialMode: currentMode,\n widthPx: state.width,\n })\n\n // Wire bus-driven layout changes to the sidebar\n bus.on('PLUCKY_SET_WIDTH', (px: number) => sidebar.setWidth(px))\n bus.on('PLUCKY_SWITCH_MODE', (m: Mode) => setMode(m))\n bus.on('PLUCKY_SESSION_UPDATED', (payload: { token?: string }) => {\n const storageKey = `pls::${state.appId}`\n if (typeof payload.token === 'string' && payload.token) {\n safeLocalStorageSet(storageKey, payload.token)\n }\n })\n bus.on('PLUCKY_SET_FULLSCREEN', (payload: { fullscreen: boolean }) =>\n handleSetFullscreen(payload.fullscreen),\n )\n bus.on(\n 'PLUCKY_RESPONSE_RECEIVED',\n async (response: { messages: Array<SavedMessage>; chatId: string }) => {\n const lastMessage = response.messages[response.messages.length - 1]\n if (typeof lastMessage.content === 'string') return\n const toolResultIds = lastMessage.content\n .filter((m) => m.type === 'tool_result')\n .map((m) => m.toolUseId)\n const unfulfilledToolCalls = lastMessage.content\n .filter((m) => m.type === 'tool_use')\n .filter((m) => !toolResultIds.includes(m.id))\n if (unfulfilledToolCalls.length === 0) return\n const promises = unfulfilledToolCalls.map(\n async (toolCall): Promise<ToolResultContentBlock> => {\n const tool = state.tools.find((t) => t.name === toolCall.name)\n if (!tool) {\n return {\n toolUseId: toolCall.id,\n content: 'Tool not found.',\n type: 'tool_result',\n }\n }\n if (!tool.cb) {\n return {\n toolUseId: toolCall.id,\n content: 'Received.',\n type: 'tool_result',\n }\n }\n try {\n let input = toolCall.input as Record<string, unknown> | string\n if (typeof input === 'string') {\n input = JSON.parse(input) as Record<string, unknown>\n }\n const result = await tool.cb(input)\n return {\n toolUseId: toolCall.id,\n content: result,\n type: 'tool_result',\n }\n } catch (e) {\n return {\n toolUseId: toolCall.id,\n content: `Error: ${e instanceof Error ? e.message : String(e)}`,\n type: 'tool_result',\n }\n }\n },\n )\n\n const results = await Promise.all(promises)\n sendMessage({\n content: results,\n lastMessageId: lastMessage.id,\n chatId: response.chatId,\n })\n },\n )\n\n // Wait for iframe to signal it's ready before sending boot\n inited = true\n return api\n }\n\n function sendMessage(args: {\n content: string | Array<ToolResultContentBlock>\n lastMessageId?: string\n createChat?: boolean\n chatId?: string\n }) {\n const { content, lastMessageId, createChat, chatId } = args\n if (sidebar.isHidden()) {\n sidebar.show()\n }\n post('PLUCKY_SEND_MESSAGE', {\n content,\n createChat: createChat ?? false,\n lastMessageId,\n chatId,\n })\n }\n function setInput(input: string) {\n sidebar.getIframe()?.contentWindow?.focus()\n post('PLUCKY_SET_INPUT', { input })\n }\n\n function setMode(mode: Mode) {\n state.mode = mode\n currentMode = mode as 'push' | 'overlay'\n sidebar.setMode(mode)\n post('PLUCKY_SWITCH_MODE', mode)\n }\n\n function on(event: MessageType, cb: (p: any) => void) {\n return bus.on(event, cb)\n }\n\n function open() {\n sidebar.show()\n }\n\n function close() {\n sidebar.hide()\n }\n\n function toggle() {\n if (sidebar.isHidden()) {\n sidebar.show()\n } else {\n sidebar.hide()\n }\n }\n\n function registerTool(tool: ToolConfig, notify = true) {\n const currentToolState = getAllToolJSON()\n if (state.tools.find((t) => t.name === tool.name)) {\n state.tools = state.tools.filter((t) => t.name !== tool.name)\n }\n state.tools.push(tool)\n state.tools.sort((a, b) => a.name.localeCompare(b.name))\n const newToolState = getAllToolJSON()\n if (notify && !dequal(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function registerManyTools(tools: Array<ToolConfig>, notify = true) {\n const currentToolState = getAllToolJSON()\n for (const tool of tools) {\n registerTool(tool, false)\n }\n const newToolState = getAllToolJSON()\n if (notify && !dequal(currentToolState, newToolState)) {\n notifyToolConfigUpdated()\n }\n }\n function removeTool(name: string, notify = true) {\n if (!state.tools.find((t) => t.name === name)) {\n console.warn(`Tool ${name} not found, skipping.`)\n }\n state.tools = state.tools.filter((t) => t.name !== name)\n if (notify) {\n notifyToolConfigUpdated()\n }\n }\n\n function notifyToolConfigUpdated() {\n post('PLUCKY_TOOL_CONFIG_UPDATED', { tools: getAllToolJSON() })\n }\n\n function getAllToolJSON(): Array<SimpleToolConfig> {\n return state.tools.map((t) => ({\n name: t.name,\n description: t.description,\n defaultLoadingText: t.defaultLoadingText,\n defaultSuccessText: t.defaultSuccessText,\n inputSchema:\n t.inputSchema instanceof ZodObject\n ? z.toJSONSchema(t.inputSchema)\n : t.inputSchema,\n }))\n }\n\n function setWidth(px: number) {\n state.width = px\n sidebar.setWidth(px)\n }\n\n function setFullscreen(fullscreen: boolean) {\n post('PLUCKY_SET_FULLSCREEN', { fullscreen })\n handleSetFullscreen(fullscreen)\n }\n\n function handleSetFullscreen(fullscreen: boolean) {\n state.fullscreen = fullscreen\n\n // Clean up existing resize listener if any\n if (resizeListener) {\n window.removeEventListener('resize', resizeListener)\n resizeListener = null\n }\n\n if (fullscreen) {\n // Use CSS calc to account for scrollbar width, which stays responsive on resize\n sidebar.setWidth(getFullscreenWidth())\n sidebar.setMode('overlay')\n\n // Add resize listener to update width if scrollbar appears/disappears\n resizeListener = () => {\n if (state.fullscreen) {\n sidebar.setWidth(getFullscreenWidth())\n }\n }\n window.addEventListener('resize', resizeListener)\n } else {\n sidebar.setWidth(state.width)\n sidebar.setMode('push')\n }\n }\n\n const api: PluckyAPI = {\n init,\n setMode,\n switchMode,\n open,\n close,\n toggle,\n on,\n isReady: () => sidebar.ready(),\n sendMessage,\n isOpen: () => !sidebar.isHidden(),\n registerTool,\n registerManyTools,\n removeTool,\n setInput,\n setWidth,\n setFullscreen,\n }\n return api\n}\n","import { createAPI } from './api'\nimport type { InitOptions, PluckyAPI } from './types'\n\n// singleton exported API for convenience\nlet api: PluckyAPI | null = null\n\nexport function init(opts: InitOptions) {\n if (!api) api = createAPI()\n return api.init(opts)\n}\n\nexport function getAPI() {\n if (!api) api = createAPI()\n return api\n}\n\n// convenience re-exports\nexport type * from './types'\n"],"mappings":"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"}
package/dist/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@plucky-ai/chat-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "./index.cjs",
6
- "module": "./index.mjs",
6
+ "module": "./index.js",
7
7
  "types": "./index.d.ts",
8
8
  "exports": {
9
9
  ".": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plucky-ai/chat-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -12,24 +12,23 @@
12
12
  "require": "./dist/index.cjs"
13
13
  }
14
14
  },
15
+ "dependencies": {
16
+ "dequal": "^2.0.3",
17
+ "zod": "^4.1.11",
18
+ "@plucky-ai/llm": "^0.1.0",
19
+ "@plucky-ai/llm-schemas": "^0.1.0"
20
+ },
15
21
  "devDependencies": {
16
- "@types/lodash": "^4.17.20",
17
- "tsdown": "^0.15.4",
22
+ "tsdown": "^0.15.9",
18
23
  "tsx": "^4.20.5",
19
24
  "typescript": "^5.9.2"
20
25
  },
21
- "dependencies": {
22
- "lodash": "^4.17.21",
23
- "zod": "^4.1.11",
24
- "@plucky-ai/llm": "0.1.0"
25
- },
26
26
  "publishConfig": {
27
27
  "access": "public",
28
28
  "registry": "https://registry.npmjs.org/"
29
29
  },
30
30
  "scripts": {
31
- "dev": "pnpm exec tsdown --watch",
32
- "build": "pnpm exec tsdown",
33
- "release": "pnpm build && pnpm publish --access public"
31
+ "build:watch": "pnpm exec tsdown --watch ./src",
32
+ "build": "pnpm exec tsdown --config tsdown.config.ts"
34
33
  }
35
34
  }