From 0352ec0546dd3656ff8f0c53693d825abf380280 Mon Sep 17 00:00:00 2001 From: krithik-fynd <156886696+krithik-fynd@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:28:41 +0530 Subject: [PATCH 1/2] fix reinitialization on remount --- src/components/CopilotProvider.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/components/CopilotProvider.tsx b/src/components/CopilotProvider.tsx index 3fa1bd4..337343c 100644 --- a/src/components/CopilotProvider.tsx +++ b/src/components/CopilotProvider.tsx @@ -30,12 +30,20 @@ const injectCopilotScript = ( ) => { const safeBotName = validateBotName(key); const scriptId = `copilot-loader-script${safeBotName === 'copilot' ? '' : `-${safeBotName}`}`; - if (document.getElementById(scriptId)) return; + const existingScript = document.getElementById(scriptId); - const inlineScript = document.createElement('script'); - inlineScript.id = scriptId; - inlineScript.type = 'application/javascript'; - inlineScript.innerHTML = ` + if (existingScript) { + const copilotFn = (window as any)[safeBotName]; + if (typeof copilotFn === 'function') { + copilotFn('init', config, function () { + (window as any)[`_${safeBotName}_ready`] = true; + }); + } + } else { + const inlineScript = document.createElement('script'); + inlineScript.id = scriptId; + inlineScript.type = 'application/javascript'; + inlineScript.innerHTML = ` (function(w,d,s,o,f,js,fjs){ w[o]=w[o]||function(){ (w[o].q=w[o].q||[]).push(arguments); @@ -53,7 +61,8 @@ const injectCopilotScript = ( }); `; - document.body.appendChild(inlineScript); + document.body.appendChild(inlineScript); + } waitForCopilot(safeBotName).then((copilot: CopilotAPI | null) => { if (copilot) { From 882ce7457909ede6b823b415c2e1d4d871893f54 Mon Sep 17 00:00:00 2001 From: krithik-fynd <156886696+krithik-fynd@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:05:31 +0530 Subject: [PATCH 2/2] chore: include dist build --- .gitignore | 1 - dist/components/Copilot.d.ts | 7 + dist/components/CopilotProvider.d.ts | 16 ++ dist/core/CopilotInstanceManager.d.ts | 2 + dist/core/hooks/useCopilot.d.ts | 11 ++ dist/core/hooks/useCopilotTools.d.ts | 7 + dist/core/hooks/useCopilotUser.d.ts | 6 + dist/core/waitForCopilot.d.ts | 2 + dist/index.cjs.js | 219 ++++++++++++++++++++++++++ dist/index.cjs.js.map | 1 + dist/index.d.ts | 83 ++++++++++ dist/index.esm.js | 213 +++++++++++++++++++++++++ dist/index.esm.js.map | 1 + dist/types/CopilotTypes.d.ts | 29 ++++ dist/types/SafeBotName.d.ts | 4 + dist/utills/validateBotName.d.ts | 1 + 16 files changed, 602 insertions(+), 1 deletion(-) create mode 100644 dist/components/Copilot.d.ts create mode 100644 dist/components/CopilotProvider.d.ts create mode 100644 dist/core/CopilotInstanceManager.d.ts create mode 100644 dist/core/hooks/useCopilot.d.ts create mode 100644 dist/core/hooks/useCopilotTools.d.ts create mode 100644 dist/core/hooks/useCopilotUser.d.ts create mode 100644 dist/core/waitForCopilot.d.ts create mode 100644 dist/index.cjs.js create mode 100644 dist/index.cjs.js.map create mode 100644 dist/index.d.ts create mode 100644 dist/index.esm.js create mode 100644 dist/index.esm.js.map create mode 100644 dist/types/CopilotTypes.d.ts create mode 100644 dist/types/SafeBotName.d.ts create mode 100644 dist/utills/validateBotName.d.ts diff --git a/.gitignore b/.gitignore index 22c4c04..b38ba86 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ node_modules/ # Production build files build/ -/dist # Environment variables .env .env.local diff --git a/dist/components/Copilot.d.ts b/dist/components/Copilot.d.ts new file mode 100644 index 0000000..3210163 --- /dev/null +++ b/dist/components/Copilot.d.ts @@ -0,0 +1,7 @@ +import { type ToolDefinition } from '../types/CopilotTypes'; +type Props = { + tools?: ToolDefinition | ToolDefinition[]; + botName?: string | number; +}; +export declare const Copilot: ({ tools, botName }: Props) => null; +export {}; diff --git a/dist/components/CopilotProvider.d.ts b/dist/components/CopilotProvider.d.ts new file mode 100644 index 0000000..219bdbe --- /dev/null +++ b/dist/components/CopilotProvider.d.ts @@ -0,0 +1,16 @@ +import React from 'react'; +interface SharedProps { + children: React.ReactNode; +} +interface SingleInstance { + token: string; + config?: Record; + scriptUrl?: string; + botName?: string; +} +interface MultiInstance { + instances: SingleInstance[]; +} +type CopilotProviderProps = (SingleInstance | MultiInstance) & SharedProps; +export declare const CopilotProvider: (props: CopilotProviderProps) => import("react/jsx-runtime").JSX.Element; +export {}; diff --git a/dist/core/CopilotInstanceManager.d.ts b/dist/core/CopilotInstanceManager.d.ts new file mode 100644 index 0000000..693ba99 --- /dev/null +++ b/dist/core/CopilotInstanceManager.d.ts @@ -0,0 +1,2 @@ +import type { CopilotAPI } from '../types/CopilotTypes'; +export declare const copilotInstances: Map; diff --git a/dist/core/hooks/useCopilot.d.ts b/dist/core/hooks/useCopilot.d.ts new file mode 100644 index 0000000..8f7e903 --- /dev/null +++ b/dist/core/hooks/useCopilot.d.ts @@ -0,0 +1,11 @@ +import type { CopilotAPI, ToolDefinition } from '../../types/CopilotTypes'; +export declare const useCopilot: (idOrIndex?: string | number) => { + show: () => void | undefined; + hide: () => void | undefined; + addTool: (toolOrTools: ToolDefinition | ToolDefinition[]) => void; + removeTool: (name: string) => void | undefined; + removeAllTools: () => void | undefined; + setUser: (user: Record) => void | undefined; + unsetUser: () => void | undefined; + raw: CopilotAPI | undefined; +}; diff --git a/dist/core/hooks/useCopilotTools.d.ts b/dist/core/hooks/useCopilotTools.d.ts new file mode 100644 index 0000000..909122d --- /dev/null +++ b/dist/core/hooks/useCopilotTools.d.ts @@ -0,0 +1,7 @@ +import type { ToolDefinition } from '../../types/CopilotTypes'; +interface Options { + removeOnUnmount?: boolean; + idOrIndex?: string | number; +} +export declare const useCopilotTool: (toolOrTools: ToolDefinition | ToolDefinition[], options?: Options) => void; +export {}; diff --git a/dist/core/hooks/useCopilotUser.d.ts b/dist/core/hooks/useCopilotUser.d.ts new file mode 100644 index 0000000..af2ed51 --- /dev/null +++ b/dist/core/hooks/useCopilotUser.d.ts @@ -0,0 +1,6 @@ +interface Options { + unsetOnUnmount?: boolean; + idOrIndex?: string | number; +} +export declare const useCopilotUser: (user: Record, options?: Options) => void; +export {}; diff --git a/dist/core/waitForCopilot.d.ts b/dist/core/waitForCopilot.d.ts new file mode 100644 index 0000000..3065f04 --- /dev/null +++ b/dist/core/waitForCopilot.d.ts @@ -0,0 +1,2 @@ +import type { CopilotAPI } from '../types/CopilotTypes'; +export declare const waitForCopilot: (botName: string, timeout?: number, interval?: number) => Promise; diff --git a/dist/index.cjs.js b/dist/index.cjs.js new file mode 100644 index 0000000..7740c3d --- /dev/null +++ b/dist/index.cjs.js @@ -0,0 +1,219 @@ +'use strict'; + +var jsxRuntime = require('react/jsx-runtime'); +var react = require('react'); + +const waitForCopilot = (botName, timeout = 5000, interval = 100) => { + return new Promise((resolve) => { + if (typeof window === 'undefined') + return resolve(null); + let tries = 0; + const maxTries = Math.ceil(timeout / interval); + const check = () => { + const copilotFn = window[botName]; + const isReady = window[`_${botName}_ready`]; + const hasRealAPI = typeof copilotFn === 'function' && + typeof copilotFn.tools?.add === 'function' && + typeof copilotFn.users?.set === 'function'; + if (isReady && hasRealAPI) { + const copilotAPI = { + show: () => copilotFn('event', 'open'), + hide: () => copilotFn('event', 'close'), + tools: { + add: (tools) => copilotFn.tools.add(tools), + remove: (name) => copilotFn.tools.remove?.(name), + removeAll: () => copilotFn.tools.removeAll?.(), + }, + users: { + set: (user) => copilotFn.users.set(user), + unset: () => copilotFn.users.unset(), + }, + }; + return resolve(copilotAPI); + } + if (++tries >= maxTries) { + console.warn(`[${botName}] Copilot not ready after timeout.`); + return resolve(null); + } + setTimeout(check, interval); + }; + check(); + }); +}; + +const copilotInstances = new Map(); + +const validateBotName = (botName) => { + const isValid = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(botName); + if (!isValid) { + throw new Error(`[CopilotProvider] Invalid botName "${botName}". It must start with a letter, $, or _, and contain only letters, numbers, $, or _.`); + } + return botName; +}; + +const defaultBotName = 'copilot'; + +const injectCopilotScript = (key, token, config = {}, scriptUrl) => { + const safeBotName = validateBotName(key); + const scriptId = `copilot-loader-script${safeBotName === 'copilot' ? '' : `-${safeBotName}`}`; + const existingScript = document.getElementById(scriptId); + if (existingScript) { + const copilotFn = window[safeBotName]; + if (typeof copilotFn === 'function') { + copilotFn('init', config, function () { + window[`_${safeBotName}_ready`] = true; + }); + } + } + else { + const inlineScript = document.createElement('script'); + inlineScript.id = scriptId; + inlineScript.type = 'application/javascript'; + inlineScript.innerHTML = ` + (function(w,d,s,o,f,js,fjs){ + w[o]=w[o]||function(){ + (w[o].q=w[o].q||[]).push(arguments); + }; + js=d.createElement(s), fjs=d.getElementsByTagName(s)[0]; + js.id=o; + js.src="${scriptUrl ?? 'https://script.copilot.live/v1/copilot.min.js'}?tkn=${token}"; + js.async=1; + js.referrerPolicy="origin"; + fjs.parentNode.insertBefore(js,fjs); + })(window,document,"script","${safeBotName}"); + + ${safeBotName}("init", ${JSON.stringify(config)}, function () { + window["_${safeBotName}_ready"] = true; + }); + `; + document.body.appendChild(inlineScript); + } + waitForCopilot(safeBotName).then((copilot) => { + if (copilot) { + copilotInstances.set(key, copilot); + } + }); +}; +const CopilotProvider = (props) => { + react.useEffect(() => { + // MULTI mode + if ('instances' in props && Array.isArray(props.instances)) { + props.instances.forEach(({ token, config = {}, scriptUrl, botName }, index) => { + const instanceKey = botName || `${defaultBotName}${index}`; + injectCopilotScript(instanceKey, token, config, scriptUrl); + }); + } + // SINGLE mode + else if ('token' in props) { + const { token, config = {}, scriptUrl, botName } = props; + injectCopilotScript(botName || defaultBotName, token, config, scriptUrl); + } + }, [props]); + return jsxRuntime.jsx(jsxRuntime.Fragment, { children: props.children }); +}; + +const MAX_WAIT_TIME = 5000; // in ms +const useCopilot = (idOrIndex) => { + const [copilot, setCopilot] = react.useState(); + const [hasErrored, setHasErrored] = react.useState(false); + react.useEffect(() => { + const interval = 100; + const maxTries = MAX_WAIT_TIME / interval; + let tries = 0; + const id = setInterval(() => { + const keys = Array.from(copilotInstances.keys()); + const key = idOrIndex === undefined + ? keys[0] + : typeof idOrIndex === 'number' + ? keys[idOrIndex] + : idOrIndex; + if (key && copilotInstances.has(key)) { + setCopilot(copilotInstances.get(key)); + clearInterval(id); + } + else if (++tries >= maxTries) { + setHasErrored(true); + clearInterval(id); + } + }, interval); + return () => clearInterval(id); + }, [idOrIndex]); + react.useEffect(() => { + if (hasErrored) { + console.error(`[useCopilot] Copilot "${String(idOrIndex ?? '0')}" not found`); + } + }, [hasErrored, idOrIndex]); + const addTool = (toolOrTools) => { + if (!copilot?.tools?.add) + return; + const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools]; + tools.forEach(tool => copilot.tools.add(tool)); + }; + return { + show: () => copilot?.show(), + hide: () => copilot?.hide(), + addTool, + removeTool: (name) => copilot?.tools?.remove(name), + removeAllTools: () => copilot?.tools?.removeAll?.(), + setUser: (user) => copilot?.users?.set(user), + unsetUser: () => copilot?.users?.unset(), + raw: copilot, + }; +}; + +const Copilot = ({ tools, botName }) => { + const { addTool, removeAllTools } = useCopilot(botName); + react.useEffect(() => { + if (!tools || !addTool) { + if (!tools) { + console.warn('[Copilot] No tools provided.'); + } + if (!addTool) { + console.warn(`[Copilot] Copilot instance for "${botName ?? 0}" not ready or missing.`); + } + return; + } + addTool(tools); + return () => { + if (typeof removeAllTools === 'function') { + removeAllTools(); + } + }; + }, [tools, addTool]); + return null; +}; + +const useCopilotTool = (toolOrTools, options) => { + const { addTool, removeTool } = useCopilot(options?.idOrIndex); + react.useEffect(() => { + const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools]; + addTool?.(tools); + return () => { + if (options?.removeOnUnmount) { + tools.forEach(tool => { + if (tool?.name) + removeTool?.(tool.name); + }); + } + }; + }, [addTool, removeTool, toolOrTools, options?.removeOnUnmount]); +}; + +const useCopilotUser = (user, options) => { + const { setUser, unsetUser } = useCopilot(options?.idOrIndex); + react.useEffect(() => { + setUser?.(user); + return () => { + if (options?.unsetOnUnmount) { + unsetUser?.(); + } + }; + }, [setUser, unsetUser, user, options?.unsetOnUnmount]); +}; + +exports.Copilot = Copilot; +exports.CopilotProvider = CopilotProvider; +exports.useCopilot = useCopilot; +exports.useCopilotTool = useCopilotTool; +exports.useCopilotUser = useCopilotUser; +//# sourceMappingURL=index.cjs.js.map diff --git a/dist/index.cjs.js.map b/dist/index.cjs.js.map new file mode 100644 index 0000000..d246a65 --- /dev/null +++ b/dist/index.cjs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.cjs.js","sources":["../src/core/waitForCopilot.ts","../src/core/CopilotInstanceManager.ts","../src/utills/validateBotName.ts","../src/types/CopilotTypes.ts","../src/components/CopilotProvider.tsx","../src/core/hooks/useCopilot.ts","../src/components/Copilot.tsx","../src/core/hooks/useCopilotTools.ts","../src/core/hooks/useCopilotUser.ts"],"sourcesContent":["import type { CopilotAPI } from '../types/CopilotTypes';\n\nexport const waitForCopilot = (\n botName: string,\n timeout = 5000,\n interval = 100\n): Promise => {\n return new Promise((resolve) => {\n if (typeof window === 'undefined') return resolve(null);\n\n let tries = 0;\n const maxTries = Math.ceil(timeout / interval);\n\n const check = () => {\n const copilotFn = (window as any)[botName];\n const isReady = (window as any)[`_${botName}_ready`];\n\n const hasRealAPI =\n typeof copilotFn === 'function' &&\n typeof copilotFn.tools?.add === 'function' &&\n typeof copilotFn.users?.set === 'function';\n\n if (isReady && hasRealAPI) {\n const copilotAPI: CopilotAPI = {\n show: () => copilotFn('event', 'open'),\n hide: () => copilotFn('event', 'close'),\n tools: {\n add: (tools) => copilotFn.tools.add(tools),\n remove: (name) => copilotFn.tools.remove?.(name),\n removeAll: () => copilotFn.tools.removeAll?.(),\n },\n users: {\n set: (user) => copilotFn.users.set(user),\n unset: () => copilotFn.users.unset(),\n },\n };\n\n return resolve(copilotAPI);\n }\n\n if (++tries >= maxTries) {\n console.warn(`[${botName}] Copilot not ready after timeout.`);\n return resolve(null);\n }\n\n setTimeout(check, interval);\n };\n\n check();\n });\n};","import type { CopilotAPI } from '../types/CopilotTypes';\n\nexport const copilotInstances = new Map();","export const validateBotName = (botName: string): string => {\n const isValid = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(botName);\n if (!isValid) {\n throw new Error(\n `[CopilotProvider] Invalid botName \"${botName}\". It must start with a letter, $, or _, and contain only letters, numbers, $, or _.`\n );\n }\n return botName;\n };"," export const defaultBotName = 'copilot'; \n export type ToolParameter = {\n type: string;\n description?: string;\n };\n \n export type ToolDefinition = {\n name: string;\n description: string;\n parameters?: {\n type: 'object';\n properties: Record;\n required?: string[];\n };\n timeout?: number;\n handler: (args: Record) => Promise | any;\n };\n \n export type CopilotAPI = {\n show: () => void;\n hide: () => void;\n tools: {\n add: (tool: ToolDefinition | ToolDefinition[]) => void;\n remove: (name: string) => void;\n removeAll?: () => void;\n };\n users: {\n set: (user: Record) => void;\n unset: () => void;\n };\n };","// Updated CopilotProvider with automatic mode detection\nimport React, { useEffect } from 'react';\nimport { waitForCopilot } from '../core/waitForCopilot';\nimport { copilotInstances } from '../core/CopilotInstanceManager';\nimport { validateBotName } from '../utills/validateBotName';\nimport { defaultBotName, type CopilotAPI } from '../types/CopilotTypes';\n\ninterface SharedProps {\n children: React.ReactNode;\n}\n\ninterface SingleInstance {\n token: string;\n config?: Record;\n scriptUrl?: string;\n botName?: string;\n}\n\ninterface MultiInstance {\n instances: SingleInstance[];\n}\n\ntype CopilotProviderProps = (SingleInstance | MultiInstance) & SharedProps;\n\nconst injectCopilotScript = (\n key: string,\n token: string,\n config: Record = {},\n scriptUrl?: string,\n) => {\n const safeBotName = validateBotName(key);\n const scriptId = `copilot-loader-script${safeBotName === 'copilot' ? '' : `-${safeBotName}`}`;\n const existingScript = document.getElementById(scriptId);\n\n if (existingScript) {\n const copilotFn = (window as any)[safeBotName];\n if (typeof copilotFn === 'function') {\n copilotFn('init', config, function () {\n (window as any)[`_${safeBotName}_ready`] = true;\n });\n }\n } else {\n const inlineScript = document.createElement('script');\n inlineScript.id = scriptId;\n inlineScript.type = 'application/javascript';\n inlineScript.innerHTML = `\n (function(w,d,s,o,f,js,fjs){\n w[o]=w[o]||function(){\n (w[o].q=w[o].q||[]).push(arguments);\n };\n js=d.createElement(s), fjs=d.getElementsByTagName(s)[0];\n js.id=o;\n js.src=\"${scriptUrl ?? 'https://script.copilot.live/v1/copilot.min.js'}?tkn=${token}\";\n js.async=1;\n js.referrerPolicy=\"origin\";\n fjs.parentNode.insertBefore(js,fjs);\n })(window,document,\"script\",\"${safeBotName}\");\n\n ${safeBotName}(\"init\", ${JSON.stringify(config)}, function () {\n window[\"_${safeBotName}_ready\"] = true;\n });\n `;\n\n document.body.appendChild(inlineScript);\n }\n\n waitForCopilot(safeBotName).then((copilot: CopilotAPI | null) => {\n if (copilot) {\n copilotInstances.set(key, copilot);\n }\n });\n};\n\nexport const CopilotProvider = (props: CopilotProviderProps) => {\n useEffect(() => {\n // MULTI mode\n if ('instances' in props && Array.isArray(props.instances)) {\n props.instances.forEach(({ token, config = {}, scriptUrl, botName }, index) => {\n const instanceKey = botName || `${defaultBotName}${index}`;\n injectCopilotScript(instanceKey, token, config, scriptUrl);\n });\n }\n // SINGLE mode\n else if ('token' in props) {\n const { token, config = {}, scriptUrl, botName } = props;\n injectCopilotScript(botName || defaultBotName, token, config, scriptUrl);\n }\n }, [props]);\n\n return <>{props.children};\n};\n","import { useEffect, useState } from 'react';\nimport { copilotInstances } from '../CopilotInstanceManager';\nimport type { CopilotAPI, ToolDefinition } from '../../types/CopilotTypes';\n\nconst MAX_WAIT_TIME = 5000; // in ms\n\nexport const useCopilot = (idOrIndex?: string | number) => {\n const [copilot, setCopilot] = useState();\n const [hasErrored, setHasErrored] = useState(false);\n\n useEffect(() => {\n const interval = 100;\n const maxTries = MAX_WAIT_TIME / interval;\n let tries = 0;\n\n const id = setInterval(() => {\n const keys = Array.from(copilotInstances.keys());\n const key =\n idOrIndex === undefined\n ? keys[0]\n : typeof idOrIndex === 'number'\n ? keys[idOrIndex]\n : idOrIndex;\n \n if (key && copilotInstances.has(key)) {\n setCopilot(copilotInstances.get(key));\n clearInterval(id);\n } else if (++tries >= maxTries) {\n setHasErrored(true);\n clearInterval(id);\n }\n }, interval);\n\n return () => clearInterval(id);\n }, [idOrIndex]);\n\n useEffect(() => {\n if (hasErrored) {\n console.error(`[useCopilot] Copilot \"${String(idOrIndex ?? '0')}\" not found`);\n }\n }, [hasErrored, idOrIndex]);\n\n const addTool = (toolOrTools: ToolDefinition | ToolDefinition[]) => {\n if (!copilot?.tools?.add) return;\n\n const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];\n tools.forEach(tool => copilot.tools.add(tool));\n };\n\n return {\n show: () => copilot?.show(),\n hide: () => copilot?.hide(),\n addTool,\n removeTool: (name: string) => copilot?.tools?.remove(name),\n removeAllTools: () => copilot?.tools?.removeAll?.(),\n setUser: (user: Record) => copilot?.users?.set(user),\n unsetUser: () => copilot?.users?.unset(),\n raw: copilot,\n };\n};","import { useEffect } from 'react';\nimport { useCopilot } from '../core/hooks/useCopilot';\nimport { type ToolDefinition } from '../types/CopilotTypes';\n\ntype Props = {\n tools?: ToolDefinition | ToolDefinition[];\n botName?: string | number; // string name or index\n};\n\nexport const Copilot = ({ tools, botName }: Props) => {\n const { addTool, removeAllTools } = useCopilot(botName);\n\n useEffect(() => {\n if (!tools || !addTool) {\n if (!tools) {\n console.warn('[Copilot] No tools provided.');\n }\n if (!addTool) {\n console.warn(`[Copilot] Copilot instance for \"${botName ?? 0}\" not ready or missing.`);\n }\n return;\n }\n\n addTool(tools);\n \n return () => {\n if (typeof removeAllTools === 'function') {\n removeAllTools();\n }\n };\n }, [tools, addTool]);\n\n return null;\n};","import { useEffect } from 'react';\nimport { useCopilot } from './useCopilot';\nimport type { ToolDefinition } from '../../types/CopilotTypes';\n\ninterface Options {\n removeOnUnmount?: boolean;\n idOrIndex?: string | number;\n}\n\nexport const useCopilotTool = (\n toolOrTools: ToolDefinition | ToolDefinition[],\n options?: Options\n) => {\n const { addTool, removeTool } = useCopilot(options?.idOrIndex);\n\n useEffect(() => {\n const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];\n\n addTool?.(tools);\n\n return () => {\n if (options?.removeOnUnmount) {\n tools.forEach(tool => {\n if (tool?.name) removeTool?.(tool.name);\n });\n }\n };\n }, [addTool, removeTool, toolOrTools, options?.removeOnUnmount]);\n};","import { useEffect } from 'react';\nimport { useCopilot } from './useCopilot';\n\ninterface Options {\n unsetOnUnmount?: boolean;\n idOrIndex?: string | number;\n}\n\nexport const useCopilotUser = (\n user: Record,\n options?: Options\n) => {\n const { setUser, unsetUser } = useCopilot(options?.idOrIndex);\n\n useEffect(() => {\n setUser?.(user);\n\n return () => {\n if (options?.unsetOnUnmount) {\n unsetUser?.();\n }\n };\n }, [setUser, unsetUser, user, options?.unsetOnUnmount]);\n};"],"names":["useEffect","_jsx","_Fragment","useState"],"mappings":"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"} diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..46647f8 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,83 @@ +import * as react_jsx_runtime from 'react/jsx-runtime'; +import React from 'react'; + +interface SharedProps { + children: React.ReactNode; +} +interface SingleInstance { + token: string; + config?: Record; + scriptUrl?: string; + botName?: string; +} +interface MultiInstance { + instances: SingleInstance[]; +} +type CopilotProviderProps = (SingleInstance | MultiInstance) & SharedProps; +declare const CopilotProvider: (props: CopilotProviderProps) => react_jsx_runtime.JSX.Element; + +type ToolParameter = { + type: string; + description?: string; +}; +type ToolDefinition = { + name: string; + description: string; + parameters?: { + type: 'object'; + properties: Record; + required?: string[]; + }; + timeout?: number; + handler: (args: Record) => Promise | any; +}; +type CopilotAPI = { + show: () => void; + hide: () => void; + tools: { + add: (tool: ToolDefinition | ToolDefinition[]) => void; + remove: (name: string) => void; + removeAll?: () => void; + }; + users: { + set: (user: Record) => void; + unset: () => void; + }; +}; + +type Props = { + tools?: ToolDefinition | ToolDefinition[]; + botName?: string | number; +}; +declare const Copilot: ({ tools, botName }: Props) => null; + +/** + * Type-safe botName constraint — must match /^[a-zA-Z_$][a-zA-Z0-9_$]*$/ + */ +type SafeBotName = T extends `${infer First}${infer Rest}` ? First extends Lowercase | Uppercase | '_' | '$' ? Rest extends `${string}` ? T extends `${string}-${string}` | `${string}.${string}` | `${string} ${string}` ? never : T : never : never : never; + +declare const useCopilot: (idOrIndex?: string | number) => { + show: () => void | undefined; + hide: () => void | undefined; + addTool: (toolOrTools: ToolDefinition | ToolDefinition[]) => void; + removeTool: (name: string) => void | undefined; + removeAllTools: () => void | undefined; + setUser: (user: Record) => void | undefined; + unsetUser: () => void | undefined; + raw: CopilotAPI | undefined; +}; + +interface Options$1 { + removeOnUnmount?: boolean; + idOrIndex?: string | number; +} +declare const useCopilotTool: (toolOrTools: ToolDefinition | ToolDefinition[], options?: Options$1) => void; + +interface Options { + unsetOnUnmount?: boolean; + idOrIndex?: string | number; +} +declare const useCopilotUser: (user: Record, options?: Options) => void; + +export { Copilot, CopilotProvider, useCopilot, useCopilotTool, useCopilotUser }; +export type { CopilotAPI, SafeBotName, ToolDefinition }; diff --git a/dist/index.esm.js b/dist/index.esm.js new file mode 100644 index 0000000..8549be4 --- /dev/null +++ b/dist/index.esm.js @@ -0,0 +1,213 @@ +import { jsx, Fragment } from 'react/jsx-runtime'; +import { useEffect, useState } from 'react'; + +const waitForCopilot = (botName, timeout = 5000, interval = 100) => { + return new Promise((resolve) => { + if (typeof window === 'undefined') + return resolve(null); + let tries = 0; + const maxTries = Math.ceil(timeout / interval); + const check = () => { + const copilotFn = window[botName]; + const isReady = window[`_${botName}_ready`]; + const hasRealAPI = typeof copilotFn === 'function' && + typeof copilotFn.tools?.add === 'function' && + typeof copilotFn.users?.set === 'function'; + if (isReady && hasRealAPI) { + const copilotAPI = { + show: () => copilotFn('event', 'open'), + hide: () => copilotFn('event', 'close'), + tools: { + add: (tools) => copilotFn.tools.add(tools), + remove: (name) => copilotFn.tools.remove?.(name), + removeAll: () => copilotFn.tools.removeAll?.(), + }, + users: { + set: (user) => copilotFn.users.set(user), + unset: () => copilotFn.users.unset(), + }, + }; + return resolve(copilotAPI); + } + if (++tries >= maxTries) { + console.warn(`[${botName}] Copilot not ready after timeout.`); + return resolve(null); + } + setTimeout(check, interval); + }; + check(); + }); +}; + +const copilotInstances = new Map(); + +const validateBotName = (botName) => { + const isValid = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(botName); + if (!isValid) { + throw new Error(`[CopilotProvider] Invalid botName "${botName}". It must start with a letter, $, or _, and contain only letters, numbers, $, or _.`); + } + return botName; +}; + +const defaultBotName = 'copilot'; + +const injectCopilotScript = (key, token, config = {}, scriptUrl) => { + const safeBotName = validateBotName(key); + const scriptId = `copilot-loader-script${safeBotName === 'copilot' ? '' : `-${safeBotName}`}`; + const existingScript = document.getElementById(scriptId); + if (existingScript) { + const copilotFn = window[safeBotName]; + if (typeof copilotFn === 'function') { + copilotFn('init', config, function () { + window[`_${safeBotName}_ready`] = true; + }); + } + } + else { + const inlineScript = document.createElement('script'); + inlineScript.id = scriptId; + inlineScript.type = 'application/javascript'; + inlineScript.innerHTML = ` + (function(w,d,s,o,f,js,fjs){ + w[o]=w[o]||function(){ + (w[o].q=w[o].q||[]).push(arguments); + }; + js=d.createElement(s), fjs=d.getElementsByTagName(s)[0]; + js.id=o; + js.src="${scriptUrl ?? 'https://script.copilot.live/v1/copilot.min.js'}?tkn=${token}"; + js.async=1; + js.referrerPolicy="origin"; + fjs.parentNode.insertBefore(js,fjs); + })(window,document,"script","${safeBotName}"); + + ${safeBotName}("init", ${JSON.stringify(config)}, function () { + window["_${safeBotName}_ready"] = true; + }); + `; + document.body.appendChild(inlineScript); + } + waitForCopilot(safeBotName).then((copilot) => { + if (copilot) { + copilotInstances.set(key, copilot); + } + }); +}; +const CopilotProvider = (props) => { + useEffect(() => { + // MULTI mode + if ('instances' in props && Array.isArray(props.instances)) { + props.instances.forEach(({ token, config = {}, scriptUrl, botName }, index) => { + const instanceKey = botName || `${defaultBotName}${index}`; + injectCopilotScript(instanceKey, token, config, scriptUrl); + }); + } + // SINGLE mode + else if ('token' in props) { + const { token, config = {}, scriptUrl, botName } = props; + injectCopilotScript(botName || defaultBotName, token, config, scriptUrl); + } + }, [props]); + return jsx(Fragment, { children: props.children }); +}; + +const MAX_WAIT_TIME = 5000; // in ms +const useCopilot = (idOrIndex) => { + const [copilot, setCopilot] = useState(); + const [hasErrored, setHasErrored] = useState(false); + useEffect(() => { + const interval = 100; + const maxTries = MAX_WAIT_TIME / interval; + let tries = 0; + const id = setInterval(() => { + const keys = Array.from(copilotInstances.keys()); + const key = idOrIndex === undefined + ? keys[0] + : typeof idOrIndex === 'number' + ? keys[idOrIndex] + : idOrIndex; + if (key && copilotInstances.has(key)) { + setCopilot(copilotInstances.get(key)); + clearInterval(id); + } + else if (++tries >= maxTries) { + setHasErrored(true); + clearInterval(id); + } + }, interval); + return () => clearInterval(id); + }, [idOrIndex]); + useEffect(() => { + if (hasErrored) { + console.error(`[useCopilot] Copilot "${String(idOrIndex ?? '0')}" not found`); + } + }, [hasErrored, idOrIndex]); + const addTool = (toolOrTools) => { + if (!copilot?.tools?.add) + return; + const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools]; + tools.forEach(tool => copilot.tools.add(tool)); + }; + return { + show: () => copilot?.show(), + hide: () => copilot?.hide(), + addTool, + removeTool: (name) => copilot?.tools?.remove(name), + removeAllTools: () => copilot?.tools?.removeAll?.(), + setUser: (user) => copilot?.users?.set(user), + unsetUser: () => copilot?.users?.unset(), + raw: copilot, + }; +}; + +const Copilot = ({ tools, botName }) => { + const { addTool, removeAllTools } = useCopilot(botName); + useEffect(() => { + if (!tools || !addTool) { + if (!tools) { + console.warn('[Copilot] No tools provided.'); + } + if (!addTool) { + console.warn(`[Copilot] Copilot instance for "${botName ?? 0}" not ready or missing.`); + } + return; + } + addTool(tools); + return () => { + if (typeof removeAllTools === 'function') { + removeAllTools(); + } + }; + }, [tools, addTool]); + return null; +}; + +const useCopilotTool = (toolOrTools, options) => { + const { addTool, removeTool } = useCopilot(options?.idOrIndex); + useEffect(() => { + const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools]; + addTool?.(tools); + return () => { + if (options?.removeOnUnmount) { + tools.forEach(tool => { + if (tool?.name) + removeTool?.(tool.name); + }); + } + }; + }, [addTool, removeTool, toolOrTools, options?.removeOnUnmount]); +}; + +const useCopilotUser = (user, options) => { + const { setUser, unsetUser } = useCopilot(options?.idOrIndex); + useEffect(() => { + setUser?.(user); + return () => { + if (options?.unsetOnUnmount) { + unsetUser?.(); + } + }; + }, [setUser, unsetUser, user, options?.unsetOnUnmount]); +}; + +export { Copilot, CopilotProvider, useCopilot, useCopilotTool, useCopilotUser }; +//# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map new file mode 100644 index 0000000..8b428ac --- /dev/null +++ b/dist/index.esm.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.esm.js","sources":["../src/core/waitForCopilot.ts","../src/core/CopilotInstanceManager.ts","../src/utills/validateBotName.ts","../src/types/CopilotTypes.ts","../src/components/CopilotProvider.tsx","../src/core/hooks/useCopilot.ts","../src/components/Copilot.tsx","../src/core/hooks/useCopilotTools.ts","../src/core/hooks/useCopilotUser.ts"],"sourcesContent":["import type { CopilotAPI } from '../types/CopilotTypes';\n\nexport const waitForCopilot = (\n botName: string,\n timeout = 5000,\n interval = 100\n): Promise => {\n return new Promise((resolve) => {\n if (typeof window === 'undefined') return resolve(null);\n\n let tries = 0;\n const maxTries = Math.ceil(timeout / interval);\n\n const check = () => {\n const copilotFn = (window as any)[botName];\n const isReady = (window as any)[`_${botName}_ready`];\n\n const hasRealAPI =\n typeof copilotFn === 'function' &&\n typeof copilotFn.tools?.add === 'function' &&\n typeof copilotFn.users?.set === 'function';\n\n if (isReady && hasRealAPI) {\n const copilotAPI: CopilotAPI = {\n show: () => copilotFn('event', 'open'),\n hide: () => copilotFn('event', 'close'),\n tools: {\n add: (tools) => copilotFn.tools.add(tools),\n remove: (name) => copilotFn.tools.remove?.(name),\n removeAll: () => copilotFn.tools.removeAll?.(),\n },\n users: {\n set: (user) => copilotFn.users.set(user),\n unset: () => copilotFn.users.unset(),\n },\n };\n\n return resolve(copilotAPI);\n }\n\n if (++tries >= maxTries) {\n console.warn(`[${botName}] Copilot not ready after timeout.`);\n return resolve(null);\n }\n\n setTimeout(check, interval);\n };\n\n check();\n });\n};","import type { CopilotAPI } from '../types/CopilotTypes';\n\nexport const copilotInstances = new Map();","export const validateBotName = (botName: string): string => {\n const isValid = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(botName);\n if (!isValid) {\n throw new Error(\n `[CopilotProvider] Invalid botName \"${botName}\". It must start with a letter, $, or _, and contain only letters, numbers, $, or _.`\n );\n }\n return botName;\n };"," export const defaultBotName = 'copilot'; \n export type ToolParameter = {\n type: string;\n description?: string;\n };\n \n export type ToolDefinition = {\n name: string;\n description: string;\n parameters?: {\n type: 'object';\n properties: Record;\n required?: string[];\n };\n timeout?: number;\n handler: (args: Record) => Promise | any;\n };\n \n export type CopilotAPI = {\n show: () => void;\n hide: () => void;\n tools: {\n add: (tool: ToolDefinition | ToolDefinition[]) => void;\n remove: (name: string) => void;\n removeAll?: () => void;\n };\n users: {\n set: (user: Record) => void;\n unset: () => void;\n };\n };","// Updated CopilotProvider with automatic mode detection\nimport React, { useEffect } from 'react';\nimport { waitForCopilot } from '../core/waitForCopilot';\nimport { copilotInstances } from '../core/CopilotInstanceManager';\nimport { validateBotName } from '../utills/validateBotName';\nimport { defaultBotName, type CopilotAPI } from '../types/CopilotTypes';\n\ninterface SharedProps {\n children: React.ReactNode;\n}\n\ninterface SingleInstance {\n token: string;\n config?: Record;\n scriptUrl?: string;\n botName?: string;\n}\n\ninterface MultiInstance {\n instances: SingleInstance[];\n}\n\ntype CopilotProviderProps = (SingleInstance | MultiInstance) & SharedProps;\n\nconst injectCopilotScript = (\n key: string,\n token: string,\n config: Record = {},\n scriptUrl?: string,\n) => {\n const safeBotName = validateBotName(key);\n const scriptId = `copilot-loader-script${safeBotName === 'copilot' ? '' : `-${safeBotName}`}`;\n const existingScript = document.getElementById(scriptId);\n\n if (existingScript) {\n const copilotFn = (window as any)[safeBotName];\n if (typeof copilotFn === 'function') {\n copilotFn('init', config, function () {\n (window as any)[`_${safeBotName}_ready`] = true;\n });\n }\n } else {\n const inlineScript = document.createElement('script');\n inlineScript.id = scriptId;\n inlineScript.type = 'application/javascript';\n inlineScript.innerHTML = `\n (function(w,d,s,o,f,js,fjs){\n w[o]=w[o]||function(){\n (w[o].q=w[o].q||[]).push(arguments);\n };\n js=d.createElement(s), fjs=d.getElementsByTagName(s)[0];\n js.id=o;\n js.src=\"${scriptUrl ?? 'https://script.copilot.live/v1/copilot.min.js'}?tkn=${token}\";\n js.async=1;\n js.referrerPolicy=\"origin\";\n fjs.parentNode.insertBefore(js,fjs);\n })(window,document,\"script\",\"${safeBotName}\");\n\n ${safeBotName}(\"init\", ${JSON.stringify(config)}, function () {\n window[\"_${safeBotName}_ready\"] = true;\n });\n `;\n\n document.body.appendChild(inlineScript);\n }\n\n waitForCopilot(safeBotName).then((copilot: CopilotAPI | null) => {\n if (copilot) {\n copilotInstances.set(key, copilot);\n }\n });\n};\n\nexport const CopilotProvider = (props: CopilotProviderProps) => {\n useEffect(() => {\n // MULTI mode\n if ('instances' in props && Array.isArray(props.instances)) {\n props.instances.forEach(({ token, config = {}, scriptUrl, botName }, index) => {\n const instanceKey = botName || `${defaultBotName}${index}`;\n injectCopilotScript(instanceKey, token, config, scriptUrl);\n });\n }\n // SINGLE mode\n else if ('token' in props) {\n const { token, config = {}, scriptUrl, botName } = props;\n injectCopilotScript(botName || defaultBotName, token, config, scriptUrl);\n }\n }, [props]);\n\n return <>{props.children};\n};\n","import { useEffect, useState } from 'react';\nimport { copilotInstances } from '../CopilotInstanceManager';\nimport type { CopilotAPI, ToolDefinition } from '../../types/CopilotTypes';\n\nconst MAX_WAIT_TIME = 5000; // in ms\n\nexport const useCopilot = (idOrIndex?: string | number) => {\n const [copilot, setCopilot] = useState();\n const [hasErrored, setHasErrored] = useState(false);\n\n useEffect(() => {\n const interval = 100;\n const maxTries = MAX_WAIT_TIME / interval;\n let tries = 0;\n\n const id = setInterval(() => {\n const keys = Array.from(copilotInstances.keys());\n const key =\n idOrIndex === undefined\n ? keys[0]\n : typeof idOrIndex === 'number'\n ? keys[idOrIndex]\n : idOrIndex;\n \n if (key && copilotInstances.has(key)) {\n setCopilot(copilotInstances.get(key));\n clearInterval(id);\n } else if (++tries >= maxTries) {\n setHasErrored(true);\n clearInterval(id);\n }\n }, interval);\n\n return () => clearInterval(id);\n }, [idOrIndex]);\n\n useEffect(() => {\n if (hasErrored) {\n console.error(`[useCopilot] Copilot \"${String(idOrIndex ?? '0')}\" not found`);\n }\n }, [hasErrored, idOrIndex]);\n\n const addTool = (toolOrTools: ToolDefinition | ToolDefinition[]) => {\n if (!copilot?.tools?.add) return;\n\n const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];\n tools.forEach(tool => copilot.tools.add(tool));\n };\n\n return {\n show: () => copilot?.show(),\n hide: () => copilot?.hide(),\n addTool,\n removeTool: (name: string) => copilot?.tools?.remove(name),\n removeAllTools: () => copilot?.tools?.removeAll?.(),\n setUser: (user: Record) => copilot?.users?.set(user),\n unsetUser: () => copilot?.users?.unset(),\n raw: copilot,\n };\n};","import { useEffect } from 'react';\nimport { useCopilot } from '../core/hooks/useCopilot';\nimport { type ToolDefinition } from '../types/CopilotTypes';\n\ntype Props = {\n tools?: ToolDefinition | ToolDefinition[];\n botName?: string | number; // string name or index\n};\n\nexport const Copilot = ({ tools, botName }: Props) => {\n const { addTool, removeAllTools } = useCopilot(botName);\n\n useEffect(() => {\n if (!tools || !addTool) {\n if (!tools) {\n console.warn('[Copilot] No tools provided.');\n }\n if (!addTool) {\n console.warn(`[Copilot] Copilot instance for \"${botName ?? 0}\" not ready or missing.`);\n }\n return;\n }\n\n addTool(tools);\n \n return () => {\n if (typeof removeAllTools === 'function') {\n removeAllTools();\n }\n };\n }, [tools, addTool]);\n\n return null;\n};","import { useEffect } from 'react';\nimport { useCopilot } from './useCopilot';\nimport type { ToolDefinition } from '../../types/CopilotTypes';\n\ninterface Options {\n removeOnUnmount?: boolean;\n idOrIndex?: string | number;\n}\n\nexport const useCopilotTool = (\n toolOrTools: ToolDefinition | ToolDefinition[],\n options?: Options\n) => {\n const { addTool, removeTool } = useCopilot(options?.idOrIndex);\n\n useEffect(() => {\n const tools = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];\n\n addTool?.(tools);\n\n return () => {\n if (options?.removeOnUnmount) {\n tools.forEach(tool => {\n if (tool?.name) removeTool?.(tool.name);\n });\n }\n };\n }, [addTool, removeTool, toolOrTools, options?.removeOnUnmount]);\n};","import { useEffect } from 'react';\nimport { useCopilot } from './useCopilot';\n\ninterface Options {\n unsetOnUnmount?: boolean;\n idOrIndex?: string | number;\n}\n\nexport const useCopilotUser = (\n user: Record,\n options?: Options\n) => {\n const { setUser, unsetUser } = useCopilot(options?.idOrIndex);\n\n useEffect(() => {\n setUser?.(user);\n\n return () => {\n if (options?.unsetOnUnmount) {\n unsetUser?.();\n }\n };\n }, [setUser, unsetUser, user, options?.unsetOnUnmount]);\n};"],"names":["_jsx","_Fragment"],"mappings":"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"} diff --git a/dist/types/CopilotTypes.d.ts b/dist/types/CopilotTypes.d.ts new file mode 100644 index 0000000..f8cd661 --- /dev/null +++ b/dist/types/CopilotTypes.d.ts @@ -0,0 +1,29 @@ +export declare const defaultBotName = "copilot"; +export type ToolParameter = { + type: string; + description?: string; +}; +export type ToolDefinition = { + name: string; + description: string; + parameters?: { + type: 'object'; + properties: Record; + required?: string[]; + }; + timeout?: number; + handler: (args: Record) => Promise | any; +}; +export type CopilotAPI = { + show: () => void; + hide: () => void; + tools: { + add: (tool: ToolDefinition | ToolDefinition[]) => void; + remove: (name: string) => void; + removeAll?: () => void; + }; + users: { + set: (user: Record) => void; + unset: () => void; + }; +}; diff --git a/dist/types/SafeBotName.d.ts b/dist/types/SafeBotName.d.ts new file mode 100644 index 0000000..4a619d7 --- /dev/null +++ b/dist/types/SafeBotName.d.ts @@ -0,0 +1,4 @@ +/** + * Type-safe botName constraint — must match /^[a-zA-Z_$][a-zA-Z0-9_$]*$/ + */ +export type SafeBotName = T extends `${infer First}${infer Rest}` ? First extends Lowercase | Uppercase | '_' | '$' ? Rest extends `${string}` ? T extends `${string}-${string}` | `${string}.${string}` | `${string} ${string}` ? never : T : never : never : never; diff --git a/dist/utills/validateBotName.d.ts b/dist/utills/validateBotName.d.ts new file mode 100644 index 0000000..c4066c1 --- /dev/null +++ b/dist/utills/validateBotName.d.ts @@ -0,0 +1 @@ +export declare const validateBotName: (botName: string) => string;