@plucky-ai/chat-sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +59 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -25
- package/dist/index.d.ts +29 -25
- package/dist/index.js +58 -22
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -2
- package/package.json +10 -11
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
|
|
25
|
-
|
|
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:
|
|
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(
|
|
151
|
-
|
|
152
|
-
document.documentElement.style.setProperty(`--${idPrefix}-w`,
|
|
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 (
|
|
292
|
+
if (typeof payload.token === "string" && payload.token) safeLocalStorageSet(storageKey, payload.token);
|
|
269
293
|
});
|
|
294
|
+
bus.on("PLUCKY_SET_FULLSCREEN", (payload) => setFullscreen(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 && !
|
|
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 && !
|
|
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,28 @@ 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
|
+
state.fullscreen = fullscreen;
|
|
409
|
+
if (resizeListener) {
|
|
410
|
+
window.removeEventListener("resize", resizeListener);
|
|
411
|
+
resizeListener = null;
|
|
412
|
+
}
|
|
413
|
+
if (fullscreen) {
|
|
414
|
+
sidebar.setWidth(getFullscreenWidth());
|
|
415
|
+
sidebar.setMode("overlay");
|
|
416
|
+
resizeListener = () => {
|
|
417
|
+
if (state.fullscreen) sidebar.setWidth(getFullscreenWidth());
|
|
418
|
+
};
|
|
419
|
+
window.addEventListener("resize", resizeListener);
|
|
420
|
+
} else {
|
|
421
|
+
sidebar.setWidth(state.width);
|
|
422
|
+
sidebar.setMode("push");
|
|
423
|
+
}
|
|
424
|
+
}
|
|
378
425
|
const api$1 = {
|
|
379
426
|
init: init$1,
|
|
380
427
|
setMode,
|
|
@@ -390,22 +437,11 @@ function createAPI() {
|
|
|
390
437
|
registerManyTools,
|
|
391
438
|
removeTool,
|
|
392
439
|
setInput,
|
|
393
|
-
setWidth
|
|
440
|
+
setWidth,
|
|
441
|
+
setFullscreen
|
|
394
442
|
};
|
|
395
443
|
return api$1;
|
|
396
444
|
}
|
|
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
445
|
|
|
410
446
|
//#endregion
|
|
411
447
|
//#region src/index.ts
|
package/dist/index.cjs.map
CHANGED
|
@@ -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 setFullscreen(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 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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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)
|
|
50
|
-
setMode(mode: Mode)
|
|
51
|
-
switchMode(mode: 'push' | 'overlay')
|
|
52
|
-
open()
|
|
53
|
-
close()
|
|
54
|
-
toggle()
|
|
55
|
-
on<T = any>(event:
|
|
56
|
-
isReady()
|
|
57
|
-
setInput(input: string)
|
|
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
|
-
})
|
|
63
|
-
isOpen()
|
|
64
|
-
registerTool(tool: ToolConfig)
|
|
65
|
-
registerManyTools(tools: ToolConfig
|
|
66
|
-
removeTool(name: string)
|
|
67
|
-
setWidth(px: number)
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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)
|
|
50
|
-
setMode(mode: Mode)
|
|
51
|
-
switchMode(mode: 'push' | 'overlay')
|
|
52
|
-
open()
|
|
53
|
-
close()
|
|
54
|
-
toggle()
|
|
55
|
-
on<T = any>(event:
|
|
56
|
-
isReady()
|
|
57
|
-
setInput(input: string)
|
|
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
|
-
})
|
|
63
|
-
isOpen()
|
|
64
|
-
registerTool(tool: ToolConfig)
|
|
65
|
-
registerManyTools(tools: ToolConfig
|
|
66
|
-
removeTool(name: string)
|
|
67
|
-
setWidth(px: number)
|
|
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
|
|
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:
|
|
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(
|
|
126
|
-
|
|
127
|
-
document.documentElement.style.setProperty(`--${idPrefix}-w`,
|
|
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 (
|
|
267
|
+
if (typeof payload.token === "string" && payload.token) safeLocalStorageSet(storageKey, payload.token);
|
|
244
268
|
});
|
|
269
|
+
bus.on("PLUCKY_SET_FULLSCREEN", (payload) => setFullscreen(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 && !
|
|
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 && !
|
|
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,28 @@ 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
|
+
state.fullscreen = fullscreen;
|
|
384
|
+
if (resizeListener) {
|
|
385
|
+
window.removeEventListener("resize", resizeListener);
|
|
386
|
+
resizeListener = null;
|
|
387
|
+
}
|
|
388
|
+
if (fullscreen) {
|
|
389
|
+
sidebar.setWidth(getFullscreenWidth());
|
|
390
|
+
sidebar.setMode("overlay");
|
|
391
|
+
resizeListener = () => {
|
|
392
|
+
if (state.fullscreen) sidebar.setWidth(getFullscreenWidth());
|
|
393
|
+
};
|
|
394
|
+
window.addEventListener("resize", resizeListener);
|
|
395
|
+
} else {
|
|
396
|
+
sidebar.setWidth(state.width);
|
|
397
|
+
sidebar.setMode("push");
|
|
398
|
+
}
|
|
399
|
+
}
|
|
353
400
|
const api$1 = {
|
|
354
401
|
init: init$1,
|
|
355
402
|
setMode,
|
|
@@ -365,22 +412,11 @@ function createAPI() {
|
|
|
365
412
|
registerManyTools,
|
|
366
413
|
removeTool,
|
|
367
414
|
setInput,
|
|
368
|
-
setWidth
|
|
415
|
+
setWidth,
|
|
416
|
+
setFullscreen
|
|
369
417
|
};
|
|
370
418
|
return api$1;
|
|
371
419
|
}
|
|
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
420
|
|
|
385
421
|
//#endregion
|
|
386
422
|
//#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 setFullscreen(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 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":";;;;AAIA,IAAa,MAAb,MAAiB;CACf,AAAQ,2BAAW,IAAI,KAAgC;CAEvD,GAAG,OAAoB,IAAa;AAClC,MAAI,CAAC,KAAK,SAAS,IAAI,MAAM,CAAE,MAAK,SAAS,IAAI,uBAAO,IAAI,KAAK,CAAC;AAClE,OAAK,SAAS,IAAI,MAAM,CAAE,IAAI,GAAG;AACjC,eAAa,KAAK,SAAS,IAAI,MAAM,CAAE,OAAO,GAAG;;CAGnD,KAAK,OAAoB,SAAe;AACtC,OAAK,SAAS,IAAI,MAAM,EAAE,SAAS,MAAM,EAAE,QAAQ,CAAC;;;AAIxD,SAAgB,gBAAgB,KAAU,KAAa,QAAgB;CACrE,MAAM,SAAS,MAAoB;AACjC,MAAI,EAAE,WAAW,OAAQ;AACzB,MAAI,CAAC,EAAE,QAAQ,OAAO,EAAE,SAAS,SAAU;AAC3C,MAAI,EAAE,UAAU,EAAE,MAAO;AACzB,MAAI,KAAK,EAAE,KAAK,MAAM,EAAE,KAAK,QAAQ;;AAEvC,KAAI,iBAAiB,WAAW,MAAM;AACtC,cAAa,IAAI,oBAAoB,WAAW,MAAM;;;;;ACExD,SAAgB,gBAA4B;CAC1C,IAAIA,OAA8B;CAClC,IAAIC,SAAmC;CACvC,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAIC,OAAa;CACjB,IAAIC,UAA2B;CAC/B,IAAI,SAAS;CACb,IAAI,WAAW;CACf,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,IAAI,aAAa;CACjB,IAAI,cAAc;CAClB,IAAIC;CAEJ,MAAM,eAAe,UAAgB;EACnC,IAAI,MAAM,SAAS,eAAe,MAAM;AACxC,MAAI,CAAC,KAAK;AACR,SAAM,SAAS,cAAc,QAAQ;AACrC,OAAI,KAAK;AACT,OAAI,MAAO,KAAI,aAAa,SAAS,MAAM;AAC3C,YAAS,KAAK,YAAY,IAAI;;AAEhC,MAAI,cAAcC;;CAGpB,MAAM,YAAY;YACR,SAAS,MAAM,QAAQ;;OAE5B,WAAW;8BACY,SAAS;;;;GAIpC,OAAO;;;;iBAIO,SAAS;;aAEb,OAAO;;;GAGjB,OAAO;;;GAGP,OAAO,GAAG,YAAY;;;;CAKvB,SAAS,MAAM,MAAmB;AAChC,MAAI,QAAS;AAEb,YAAU,KAAK,WAAW;AAC1B,WAAS,KAAK,UAAU;AACxB,aAAW,KAAK,YAAY;AAC5B,UAAQ,KAAK;AACb,SAAO,KAAK,eAAe;AAE3B,WAAS,GAAG,SAAS;AACrB,UAAQ,GAAG,SAAS;AACpB,eAAa,GAAG,SAAS;AACzB,gBAAc,GAAG,SAAS;AAE1B,cAAY,KAAK,CAAC;AAElB,SAAO,SAAS,eAAe,OAAO;AACtC,MAAI,CAAC,MAAM;AACT,UAAO,SAAS,cAAc,MAAM;AACpC,QAAK,KAAK;AACV,QAAK,MAAM,SAAS,OAAO,OAAO;AAClC,YAAS,KAAK,YAAY,KAAK;;AAGjC,MAAI,CAAC,QAAQ;AACX,YAAS,SAAS,cAAc,SAAS;AACzC,UAAO,QAAQ,KAAK,eAAe;AACnC,UAAO,aAAa,cAAc,OAAO,MAAM;AAE/C,UAAO,MAAM,GADA,KAAK,QAAQ,QAAQ,OAAO,GAAG,CACvB,UAAU,mBAAmB,KAAK,MAAM;AAC7D,QAAK,YAAY,OAAO;;AAI1B,MAAI,OAAQ,MAAK,UAAU,IAAI,YAAY;MACtC,MAAK,UAAU,OAAO,YAAY;AAEvC,MAAI,CAAC,UAAU,SAAS,OAAQ,UAAS,KAAK,UAAU,IAAI,WAAW;MAClE,UAAS,KAAK,UAAU,OAAO,WAAW;AAE/C,YAAU;;CAGZ,SAAS,UAAU;AACjB,MAAI,CAAC,QAAS;AACd,WAAS,KAAK,UAAU,OAAO,WAAW;AAC1C,QAAM,QAAQ;AACd,WAAS,eAAe,MAAM,EAAE,QAAQ;AACxC,SAAO;AACP,WAAS;AACT,YAAU;AACV,WAAS;;CAGX,SAAS,QAAQ,MAAY;AAC3B,SAAO;AACP,MAAI,CAAC,QAAS;AACd,MAAI,CAAC,UAAU,SAAS,OAAQ,UAAS,KAAK,UAAU,IAAI,WAAW;MAClE,UAAS,KAAK,UAAU,OAAO,WAAW;;CAGjD,SAAS,SAAS,OAAwB;EACxC,MAAM,gBACJ,OAAO,UAAU,WAAW,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC,CAAC,MAAM;AACtE,WAAS,gBAAgB,MAAM,YAAY,KAAK,SAAS,KAAK,cAAc;;CAG9E,SAAS,UAAU,KAAW;AAC5B,WAASC;AACT,MAAI,KAAM,MAAK,MAAM,SAAS,OAAO,OAAO;;CAG9C,SAAS,OAAO;AACd,MAAI,CAAC,WAAW,OAAQ;AACxB,WAAS;AAET,WAAS,KAAK,UAAU,OAAO,WAAW;AAE1C,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,MAAM;AACR,QAAK,aAAa,eAAe,OAAO;AAEvC,GAAC,KAAa,QAAQ;;AAGzB,UAAQ,eAAe,YACrB;GAAE,MAAM;GAAqB,SAAS;GAAU,EAChD,IACD;;CAGH,SAAS,OAAO;AACd,MAAI,CAAC,WAAW,CAAC,OAAQ;AACzB,WAAS;AACT,QAAM,UAAU,OAAO,YAAY;AACnC,MAAI,MAAM;AACR,QAAK,gBAAgB,cAAc;AACnC,OAAI;AACD,IAAC,KAAa,QAAQ;WACjB;;AAEV,MAAI,SAAS,OAAQ,UAAS,KAAK,UAAU,IAAI,WAAW;AAC5D,UAAQ,eAAe,YACrB;GAAE,MAAM;GAAqB,SAAS;GAAW,EACjD,IACD;;AAGH,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAgB;EAChB,eAAe;EACf,oBAAoB;EACpB,iBAAiB;EACjB,aAAa,CAAC,CAAC,QAAQ;EACxB;;;;;ACxMH,SAAgB,oBAAoB,KAA4B;AAC9D,KAAI;AACF,SAAO,OAAO,aAAa,QAAQ,IAAI;SACjC;AACN,SAAO;;;AAIX,SAAgB,oBAAoB,KAAa,OAAqB;AACpE,KAAI;AACF,SAAO,aAAa,QAAQ,KAAK,MAAM;SACjC;;;;;ACQV,SAAgB,YAAuB;CACrC,MAAM,MAAM,IAAI,KAAK;CACrB,IAAI,SAAS;CAGb,MAAM,UAAU,eAAe;CAG/B,IAAIC,QAAoC;EACtC,OAAO;EACP,SAAS;EACT,MAAM;EACN,aAAa;EACb,MAAM,EAAE;EACR,OAAO,EAAE;EACT,MAAM,EAAE;EACR,OAAO;EACP,YAAY;EACb;CAGD,IAAIC,cAAkC;CAGtC,IAAIC,iBAAsC;CAG1C,SAAS,oBAA4B;AACnC,SAAO,OAAO,aAAa,SAAS,gBAAgB;;CAItD,SAAS,qBAA6B;EACpC,MAAM,iBAAiB,mBAAmB;AAC1C,SAAO,iBAAiB,IAAI,gBAAgB,eAAe,OAAO;;CAIpE,SAAS,KAAK,MAAmB,SAAe;EAC9C,MAAM,SAAS,QAAQ,WAAW;EAClC,MAAM,eAAe,IAAI,IAAI,MAAM,QAAQ,CAAC;AAC5C,UAAQ,eAAe,YAAY;GAAE;GAAM;GAAS,EAAE,aAAa;;CAGrE,SAAS,WAAW,MAA0B;AAC5C,MAAI,SAAS,YAAa;AAG1B,UAAQ,QAAQ,KAAK;AACrB,gBAAc;;CAGhB,SAASC,OAAK,MAA8B;AAC1C,MAAI,OAAQ,QAAOC;EACnB,MAAM,EAAE,MAAO,GAAG,SAAS;AAC3B,UAAQ;GAAE,GAAG;GAAO,GAAG;GAAM;AAC7B,MAAI,SAAS,MAAM,SAAS,EAC1B,mBAAkB,OAAO,MAAM;AAEjC,gBAAc,MAAM;AACpB,kBAAgB,KAAK,QAAQ,IAAI,IAAI,MAAM,QAAQ,CAAC,OAAO;AAE3D,MAAI,GAAG,sBAAsB,QAAQ,MAAM,CAAC;AAG5C,MAAI,GAAG,+BAA+B;GAEpC,MAAM,gBAAgB,oBADH,QAAQ,MAAM,QACoB;AACrD,QAAK,sBAAsB;IACzB,OAAO,MAAM;IACb,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,cAAc,iBAAiB;IAC/B,OAAO,gBAAgB;IACvB,MAAM,MAAM;IACb,CAAC;AACF,OAAI,CAAC,cACH,MAAK,0BAA0B,EAAE,QAAQ,WAAW,CAAC;IAEvD;AAGF,UAAQ,MAAM;GACZ,OAAO,MAAM;GACb,SAAS,MAAM;GACf,aAAa;GACb,aAAa;GACb,SAAS,MAAM;GAChB,CAAC;AAGF,MAAI,GAAG,qBAAqB,OAAe,QAAQ,SAAS,GAAG,CAAC;AAChE,MAAI,GAAG,uBAAuB,MAAY,QAAQ,EAAE,CAAC;AACrD,MAAI,GAAG,2BAA2B,YAAgC;GAChE,MAAM,aAAa,QAAQ,MAAM;AACjC,OAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,MAC/C,qBAAoB,YAAY,QAAQ,MAAM;IAEhD;AACF,MAAI,GAAG,0BAA0B,YAC/B,cAAc,QAAQ,WAAW,CAClC;AACD,MAAI,GACF,4BACA,OAAO,aAAgE;GACrE,MAAM,cAAc,SAAS,SAAS,SAAS,SAAS,SAAS;AACjE,OAAI,OAAO,YAAY,YAAY,SAAU;GAC7C,MAAM,gBAAgB,YAAY,QAC/B,QAAQ,MAAM,EAAE,SAAS,cAAc,CACvC,KAAK,MAAM,EAAE,UAAU;GAC1B,MAAM,uBAAuB,YAAY,QACtC,QAAQ,MAAM,EAAE,SAAS,WAAW,CACpC,QAAQ,MAAM,CAAC,cAAc,SAAS,EAAE,GAAG,CAAC;AAC/C,OAAI,qBAAqB,WAAW,EAAG;GACvC,MAAM,WAAW,qBAAqB,IACpC,OAAO,aAA8C;IACnD,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM,EAAE,SAAS,SAAS,KAAK;AAC9D,QAAI,CAAC,KACH,QAAO;KACL,WAAW,SAAS;KACpB,SAAS;KACT,MAAM;KACP;AAEH,QAAI,CAAC,KAAK,GACR,QAAO;KACL,WAAW,SAAS;KACpB,SAAS;KACT,MAAM;KACP;AAEH,QAAI;KACF,IAAI,QAAQ,SAAS;AACrB,SAAI,OAAO,UAAU,SACnB,SAAQ,KAAK,MAAM,MAAM;KAE3B,MAAM,SAAS,MAAM,KAAK,GAAG,MAAM;AACnC,YAAO;MACL,WAAW,SAAS;MACpB,SAAS;MACT,MAAM;MACP;aACM,GAAG;AACV,YAAO;MACL,WAAW,SAAS;MACpB,SAAS,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;MAC7D,MAAM;MACP;;KAGN;AAGD,eAAY;IACV,SAFc,MAAM,QAAQ,IAAI,SAAS;IAGzC,eAAe,YAAY;IAC3B,QAAQ,SAAS;IAClB,CAAC;IAEL;AAGD,WAAS;AACT,SAAOA;;CAGT,SAAS,YAAY,MAKlB;EACD,MAAM,EAAE,SAAS,eAAe,YAAY,WAAW;AACvD,MAAI,QAAQ,UAAU,CACpB,SAAQ,MAAM;AAEhB,OAAK,uBAAuB;GAC1B;GACA,YAAY,cAAc;GAC1B;GACA;GACD,CAAC;;CAEJ,SAAS,SAAS,OAAe;AAC/B,UAAQ,WAAW,EAAE,eAAe,OAAO;AAC3C,OAAK,oBAAoB,EAAE,OAAO,CAAC;;CAGrC,SAAS,QAAQ,MAAY;AAC3B,QAAM,OAAO;AACb,gBAAc;AACd,UAAQ,QAAQ,KAAK;AACrB,OAAK,sBAAsB,KAAK;;CAGlC,SAAS,GAAG,OAAoB,IAAsB;AACpD,SAAO,IAAI,GAAG,OAAO,GAAG;;CAG1B,SAAS,OAAO;AACd,UAAQ,MAAM;;CAGhB,SAAS,QAAQ;AACf,UAAQ,MAAM;;CAGhB,SAAS,SAAS;AAChB,MAAI,QAAQ,UAAU,CACpB,SAAQ,MAAM;MAEd,SAAQ,MAAM;;CAIlB,SAAS,aAAa,MAAkB,SAAS,MAAM;EACrD,MAAM,mBAAmB,gBAAgB;AACzC,MAAI,MAAM,MAAM,MAAM,MAAM,EAAE,SAAS,KAAK,KAAK,CAC/C,OAAM,QAAQ,MAAM,MAAM,QAAQ,MAAM,EAAE,SAAS,KAAK,KAAK;AAE/D,QAAM,MAAM,KAAK,KAAK;AACtB,QAAM,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;EACxD,MAAM,eAAe,gBAAgB;AACrC,MAAI,UAAU,CAAC,OAAO,kBAAkB,aAAa,CACnD,0BAAyB;;CAG7B,SAAS,kBAAkB,OAA0B,SAAS,MAAM;EAClE,MAAM,mBAAmB,gBAAgB;AACzC,OAAK,MAAM,QAAQ,MACjB,cAAa,MAAM,MAAM;EAE3B,MAAM,eAAe,gBAAgB;AACrC,MAAI,UAAU,CAAC,OAAO,kBAAkB,aAAa,CACnD,0BAAyB;;CAG7B,SAAS,WAAW,MAAc,SAAS,MAAM;AAC/C,MAAI,CAAC,MAAM,MAAM,MAAM,MAAM,EAAE,SAAS,KAAK,CAC3C,SAAQ,KAAK,QAAQ,KAAK,uBAAuB;AAEnD,QAAM,QAAQ,MAAM,MAAM,QAAQ,MAAM,EAAE,SAAS,KAAK;AACxD,MAAI,OACF,0BAAyB;;CAI7B,SAAS,0BAA0B;AACjC,OAAK,8BAA8B,EAAE,OAAO,gBAAgB,EAAE,CAAC;;CAGjE,SAAS,iBAA0C;AACjD,SAAO,MAAM,MAAM,KAAK,OAAO;GAC7B,MAAM,EAAE;GACR,aAAa,EAAE;GACf,oBAAoB,EAAE;GACtB,oBAAoB,EAAE;GACtB,aACE,EAAE,uBAAuB,YACrB,EAAE,aAAa,EAAE,YAAY,GAC7B,EAAE;GACT,EAAE;;CAGL,SAAS,SAAS,IAAY;AAC5B,QAAM,QAAQ;AACd,UAAQ,SAAS,GAAG;;CAGtB,SAAS,cAAc,YAAqB;AAC1C,QAAM,aAAa;AAGnB,MAAI,gBAAgB;AAClB,UAAO,oBAAoB,UAAU,eAAe;AACpD,oBAAiB;;AAGnB,MAAI,YAAY;AAEd,WAAQ,SAAS,oBAAoB,CAAC;AACtC,WAAQ,QAAQ,UAAU;AAG1B,0BAAuB;AACrB,QAAI,MAAM,WACR,SAAQ,SAAS,oBAAoB,CAAC;;AAG1C,UAAO,iBAAiB,UAAU,eAAe;SAC5C;AACL,WAAQ,SAAS,MAAM,MAAM;AAC7B,WAAQ,QAAQ,OAAO;;;CAI3B,MAAMC,QAAiB;EACrB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,eAAe,QAAQ,OAAO;EAC9B;EACA,cAAc,CAAC,QAAQ,UAAU;EACjC;EACA;EACA;EACA;EACA;EACA;EACD;AACD,QAAOD;;;;;ACzUT,IAAIE,MAAwB;AAE5B,SAAgB,KAAK,MAAmB;AACtC,KAAI,CAAC,IAAK,OAAM,WAAW;AAC3B,QAAO,IAAI,KAAK,KAAK;;AAGvB,SAAgB,SAAS;AACvB,KAAI,CAAC,IAAK,OAAM,WAAW;AAC3B,QAAO"}
|
package/dist/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plucky-ai/chat-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
}
|