@humanfirst-chat/js 0.1.1 → 0.1.3
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/README.md +35 -0
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -47,6 +47,41 @@ HFChat.close();
|
|
|
47
47
|
HFChat.toggle();
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
### Open with focus
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
HFChat.open({ focus: true });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Custom CTA on a specific page (example: /premium)
|
|
57
|
+
|
|
58
|
+
Use a page-level CTA that opens the chat with focus, and hide the default
|
|
59
|
+
launcher on that route.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
"use client";
|
|
63
|
+
|
|
64
|
+
import { useEffect } from "react";
|
|
65
|
+
import { usePathname } from "next/navigation";
|
|
66
|
+
import HFChat from "@humanfirst-chat/js";
|
|
67
|
+
|
|
68
|
+
export function PremiumChatCTA() {
|
|
69
|
+
const pathname = usePathname();
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (pathname !== "/premium") return;
|
|
73
|
+
HFChat.hide();
|
|
74
|
+
return () => HFChat.show();
|
|
75
|
+
}, [pathname]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<button type="button" onClick={() => HFChat.open({ focus: true })}>
|
|
79
|
+
Any question? Chat with me
|
|
80
|
+
</button>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
50
85
|
## Configuration
|
|
51
86
|
|
|
52
87
|
```ts
|
package/dist/index.d.mts
CHANGED
|
@@ -9,6 +9,8 @@ interface HFChatConfig {
|
|
|
9
9
|
siteId?: string;
|
|
10
10
|
/** Start with widget hidden */
|
|
11
11
|
hidden?: boolean;
|
|
12
|
+
/** Hide the launcher after the chat window is closed */
|
|
13
|
+
hideOnClose?: boolean;
|
|
12
14
|
/** Log verbosity level */
|
|
13
15
|
logs?: HFChatLogLevel;
|
|
14
16
|
/** Override API base URL (for self-hosting/testing) */
|
|
@@ -20,6 +22,11 @@ interface HFChatWidgetSettings {
|
|
|
20
22
|
widgetColor?: string;
|
|
21
23
|
widgetPosition?: HFChatPosition;
|
|
22
24
|
widgetSize?: HFChatSize;
|
|
25
|
+
/** Hide the launcher after the chat window is closed */
|
|
26
|
+
hideOnClose?: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface HFChatOpenOptions {
|
|
29
|
+
focus?: boolean;
|
|
23
30
|
}
|
|
24
31
|
interface HFChatIdentifyMeta {
|
|
25
32
|
email?: string;
|
|
@@ -29,7 +36,7 @@ interface HFChatIdentifyMeta {
|
|
|
29
36
|
interface HFChatAPI {
|
|
30
37
|
show(): void;
|
|
31
38
|
hide(): void;
|
|
32
|
-
open(): void;
|
|
39
|
+
open(options?: HFChatOpenOptions): void;
|
|
33
40
|
close(): void;
|
|
34
41
|
toggle(): void;
|
|
35
42
|
isOpen(): boolean;
|
|
@@ -57,8 +64,9 @@ declare global {
|
|
|
57
64
|
HumanFirstChat?: {
|
|
58
65
|
init: (opts: unknown) => HFChatAPI;
|
|
59
66
|
};
|
|
67
|
+
__HFCHAT_DISABLE_AUTO_INIT?: boolean;
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
70
|
declare const HFChat: HFChatGlobal;
|
|
63
71
|
|
|
64
|
-
export { HFChat, type HFChatAPI, type HFChatConfig, type HFChatGlobal, type HFChatIdentifyMeta, type HFChatLoaderConfig, type HFChatLogLevel, type HFChatWidgetSettings, HFChat as default };
|
|
72
|
+
export { HFChat, type HFChatAPI, type HFChatConfig, type HFChatGlobal, type HFChatIdentifyMeta, type HFChatLoaderConfig, type HFChatLogLevel, type HFChatOpenOptions, type HFChatWidgetSettings, HFChat as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface HFChatConfig {
|
|
|
9
9
|
siteId?: string;
|
|
10
10
|
/** Start with widget hidden */
|
|
11
11
|
hidden?: boolean;
|
|
12
|
+
/** Hide the launcher after the chat window is closed */
|
|
13
|
+
hideOnClose?: boolean;
|
|
12
14
|
/** Log verbosity level */
|
|
13
15
|
logs?: HFChatLogLevel;
|
|
14
16
|
/** Override API base URL (for self-hosting/testing) */
|
|
@@ -20,6 +22,11 @@ interface HFChatWidgetSettings {
|
|
|
20
22
|
widgetColor?: string;
|
|
21
23
|
widgetPosition?: HFChatPosition;
|
|
22
24
|
widgetSize?: HFChatSize;
|
|
25
|
+
/** Hide the launcher after the chat window is closed */
|
|
26
|
+
hideOnClose?: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface HFChatOpenOptions {
|
|
29
|
+
focus?: boolean;
|
|
23
30
|
}
|
|
24
31
|
interface HFChatIdentifyMeta {
|
|
25
32
|
email?: string;
|
|
@@ -29,7 +36,7 @@ interface HFChatIdentifyMeta {
|
|
|
29
36
|
interface HFChatAPI {
|
|
30
37
|
show(): void;
|
|
31
38
|
hide(): void;
|
|
32
|
-
open(): void;
|
|
39
|
+
open(options?: HFChatOpenOptions): void;
|
|
33
40
|
close(): void;
|
|
34
41
|
toggle(): void;
|
|
35
42
|
isOpen(): boolean;
|
|
@@ -57,8 +64,9 @@ declare global {
|
|
|
57
64
|
HumanFirstChat?: {
|
|
58
65
|
init: (opts: unknown) => HFChatAPI;
|
|
59
66
|
};
|
|
67
|
+
__HFCHAT_DISABLE_AUTO_INIT?: boolean;
|
|
60
68
|
}
|
|
61
69
|
}
|
|
62
70
|
declare const HFChat: HFChatGlobal;
|
|
63
71
|
|
|
64
|
-
export { HFChat, type HFChatAPI, type HFChatConfig, type HFChatGlobal, type HFChatIdentifyMeta, type HFChatLoaderConfig, type HFChatLogLevel, type HFChatWidgetSettings, HFChat as default };
|
|
72
|
+
export { HFChat, type HFChatAPI, type HFChatConfig, type HFChatGlobal, type HFChatIdentifyMeta, type HFChatLoaderConfig, type HFChatLogLevel, type HFChatOpenOptions, type HFChatWidgetSettings, HFChat as default };
|
package/dist/index.js
CHANGED
|
@@ -51,10 +51,12 @@ function injectScript(src) {
|
|
|
51
51
|
);
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
+
window.__HFCHAT_DISABLE_AUTO_INIT = true;
|
|
54
55
|
const script = document.createElement("script");
|
|
55
56
|
script.async = true;
|
|
56
57
|
script.src = src;
|
|
57
58
|
script.setAttribute(SCRIPT_DATA_ATTR, "1");
|
|
59
|
+
script.setAttribute("data-auto-init", "false");
|
|
58
60
|
script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`));
|
|
59
61
|
script.onload = () => {
|
|
60
62
|
setTimeout(() => {
|
|
@@ -75,6 +77,7 @@ async function loadWidget(opts = {}) {
|
|
|
75
77
|
const api = window.HumanFirstChat.init({
|
|
76
78
|
siteId: opts.siteId,
|
|
77
79
|
hidden: opts.hidden,
|
|
80
|
+
hideOnClose: opts.hideOnClose,
|
|
78
81
|
logs: opts.logs,
|
|
79
82
|
baseURL: opts.baseURL,
|
|
80
83
|
host: opts.host
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n if (candidate === selfProxy) return null\n return candidate as HFChatAPI\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) return resolve()\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`))\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) resolve()\n else reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n lastInitOpts = { ...lastInitOpts, ...opts }\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) return existingApi\n\n if (loaderPromise) return loaderPromise\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n await init()\n\n const existing = getRealApi(HFChat)\n if (existing && typeof (existing as any)[methodName] === 'function') return existing\n\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n if (api && typeof fn === 'function') {\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n if (prop === 'init') return init\n\n if (typeof window === 'undefined') return () => {}\n\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n return (...args: unknown[]) => {\n const methodName = String(prop)\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') fn.apply(api, args)\n })\n .catch((error) => {\n console.error(error instanceof Error ? error.message : error)\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton (like HEYO), without breaking when the real widget assigns window.HFChat.\n if (!window.HFChat) window.HFChat = HFChat\n}\n\nexport default HFChat\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatOpenOptions,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n __HFCHAT_DISABLE_AUTO_INIT?: boolean\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n if (candidate === selfProxy) return null\n return candidate as HFChatAPI\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) return resolve()\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n window.__HFCHAT_DISABLE_AUTO_INIT = true\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.setAttribute('data-auto-init', 'false')\n script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`))\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) resolve()\n else reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n hideOnClose: opts.hideOnClose,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n lastInitOpts = { ...lastInitOpts, ...opts }\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) return existingApi\n\n if (loaderPromise) return loaderPromise\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n await init()\n\n const existing = getRealApi(HFChat)\n if (existing && typeof (existing as any)[methodName] === 'function') return existing\n\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n if (api && typeof fn === 'function') {\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n if (prop === 'init') return init\n\n if (typeof window === 'undefined') return () => {}\n\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n return (...args: unknown[]) => {\n const methodName = String(prop)\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') fn.apply(api, args)\n })\n .catch((error) => {\n console.error(error instanceof Error ? error.message : error)\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton (like HEYO), without breaking when the real widget assigns window.HFChat.\n if (!window.HFChat) window.HFChat = HFChat\n}\n\nexport default HFChat\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCA,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AAEzB,IAAI,gBAA2C;AAC/C,IAAI;AAEJ,SAAS,WAAW,WAA2C;AAC7D,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,YAAY,OAAO;AACzB,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,cAAc,UAAW,QAAO;AACpC,SAAO;AACT;AAEA,SAAS,aAAa,KAA4B;AAChD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,OAAO,aAAa,YAAa,QAAO,QAAQ;AACpD,QAAI,OAAO,eAAgB,QAAO,QAAQ;AAE1C,UAAM,WAAW,SAAS;AAAA,MACxB,UAAU,gBAAgB;AAAA,IAC5B;AACA,QAAI,UAAU;AACZ,eAAS,iBAAiB,QAAQ,MAAM,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;AACjE,eAAS;AAAA,QACP;AAAA,QACA,MAAM,OAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,QACtD,EAAE,MAAM,KAAK;AAAA,MACf;AACA;AAAA,IACF;AAEA,WAAO,6BAA6B;AACpC,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,MAAM;AACb,WAAO,aAAa,kBAAkB,GAAG;AACzC,WAAO,aAAa,kBAAkB,OAAO;AAC7C,WAAO,UAAU,MAAM,OAAO,IAAI,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAC/E,WAAO,SAAS,MAAM;AACpB,iBAAW,MAAM;AACf,YAAI,OAAO,eAAgB,SAAQ;AAAA,YAC9B,QAAO,IAAI,MAAM,gDAAgD,CAAC;AAAA,MACzE,GAAG,CAAC;AAAA,IACN;AACA,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAEA,eAAe,WAAW,OAA2B,CAAC,GAAuB;AAC3E,MAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAE3C,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,aAAa,SAAS;AAE5B,MAAI,CAAC,OAAO,gBAAgB,MAAM;AAChC,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAEA,QAAM,MAAM,OAAO,eAAe,KAAK;AAAA,IACrC,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,MAAM,KAAK;AAAA,EACb,CAAC;AAED,SAAO;AACT;AAEA,eAAe,KAAK,OAA2B,CAAC,GAAuB;AACrE,iBAAe,EAAE,GAAG,cAAc,GAAG,KAAK;AAE1C,QAAM,cAAc,WAAW,MAAM;AACrC,MAAI,YAAa,QAAO;AAExB,MAAI,cAAe,QAAO;AAC1B,kBAAgB,WAAW,YAAY;AACvC,SAAO;AACT;AAEA,eAAe,cAAc,YAAwC;AACnE,QAAM,KAAK;AAEX,QAAM,WAAW,WAAW,MAAM;AAClC,MAAI,YAAY,OAAQ,SAAiB,UAAU,MAAM,WAAY,QAAO;AAE5E,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,WAAW;AACf,UAAM,cAAc;AAEpB,UAAM,cAAc,MAAM;AACxB,YAAM,MAAM,WAAW,MAAM;AAC7B,YAAM,KAAK,MAAO,IAAY,UAAU,IAAI;AAE5C,UAAI,OAAO,OAAO,OAAO,YAAY;AACnC,gBAAQ,GAAG;AACX;AAAA,MACF;AAEA,UAAI,WAAW,aAAa;AAC1B,oBAAY;AACZ,mBAAW,aAAa,GAAG;AAC3B;AAAA,MACF;AAEA,aAAO,IAAI,MAAM,kBAAkB,UAAU,iCAAiC,CAAC;AAAA,IACjF;AAEA,eAAW,aAAa,GAAG;AAAA,EAC7B,CAAC;AACH;AAEO,IAAM,SAAuB,IAAI;AAAA,EACtC,CAAC;AAAA,EACD;AAAA,IACE,IAAI,SAAS,MAAM;AACjB,UAAI,SAAS,OAAQ,QAAO;AAE5B,UAAI,OAAO,WAAW,YAAa,QAAO,MAAM;AAAA,MAAC;AAEjD,UAAI,SAAS,SAAS;AACpB,cAAM,MAAM,WAAW,MAAM;AAC7B,eAAO,CAAC,CAAC,KAAK;AAAA,MAChB;AAEA,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM;AACX,gBAAM,MAAM,WAAW,MAAM;AAC7B,iBAAO,KAAK,SAAS,KAAK;AAAA,QAC5B;AAAA,MACF;AAEA,aAAO,IAAI,SAAoB;AAC7B,cAAM,aAAa,OAAO,IAAI;AAC9B,sBAAc,UAAU,EACrB,KAAK,CAAC,QAAQ;AACb,gBAAM,KAAM,IAAY,UAAU;AAClC,cAAI,OAAO,OAAO,WAAY,IAAG,MAAM,KAAK,IAAI;AAAA,QAClD,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,kBAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAAA,QAC9D,CAAC;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAI,OAAO,WAAW,aAAa;AAEjC,MAAI,CAAC,OAAO,OAAQ,QAAO,SAAS;AACtC;AAEA,IAAO,gBAAQ;","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -26,10 +26,12 @@ function injectScript(src) {
|
|
|
26
26
|
);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
+
window.__HFCHAT_DISABLE_AUTO_INIT = true;
|
|
29
30
|
const script = document.createElement("script");
|
|
30
31
|
script.async = true;
|
|
31
32
|
script.src = src;
|
|
32
33
|
script.setAttribute(SCRIPT_DATA_ATTR, "1");
|
|
34
|
+
script.setAttribute("data-auto-init", "false");
|
|
33
35
|
script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`));
|
|
34
36
|
script.onload = () => {
|
|
35
37
|
setTimeout(() => {
|
|
@@ -50,6 +52,7 @@ async function loadWidget(opts = {}) {
|
|
|
50
52
|
const api = window.HumanFirstChat.init({
|
|
51
53
|
siteId: opts.siteId,
|
|
52
54
|
hidden: opts.hidden,
|
|
55
|
+
hideOnClose: opts.hideOnClose,
|
|
53
56
|
logs: opts.logs,
|
|
54
57
|
baseURL: opts.baseURL,
|
|
55
58
|
host: opts.host
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n if (candidate === selfProxy) return null\n return candidate as HFChatAPI\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) return resolve()\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`))\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) resolve()\n else reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n lastInitOpts = { ...lastInitOpts, ...opts }\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) return existingApi\n\n if (loaderPromise) return loaderPromise\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n await init()\n\n const existing = getRealApi(HFChat)\n if (existing && typeof (existing as any)[methodName] === 'function') return existing\n\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n if (api && typeof fn === 'function') {\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n if (prop === 'init') return init\n\n if (typeof window === 'undefined') return () => {}\n\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n return (...args: unknown[]) => {\n const methodName = String(prop)\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') fn.apply(api, args)\n })\n .catch((error) => {\n console.error(error instanceof Error ? error.message : error)\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton (like HEYO), without breaking when the real widget assigns window.HFChat.\n if (!window.HFChat) window.HFChat = HFChat\n}\n\nexport default HFChat\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatOpenOptions,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n __HFCHAT_DISABLE_AUTO_INIT?: boolean\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n if (candidate === selfProxy) return null\n return candidate as HFChatAPI\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) return resolve()\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n window.__HFCHAT_DISABLE_AUTO_INIT = true\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.setAttribute('data-auto-init', 'false')\n script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`))\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) resolve()\n else reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n hideOnClose: opts.hideOnClose,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n lastInitOpts = { ...lastInitOpts, ...opts }\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) return existingApi\n\n if (loaderPromise) return loaderPromise\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n await init()\n\n const existing = getRealApi(HFChat)\n if (existing && typeof (existing as any)[methodName] === 'function') return existing\n\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n if (api && typeof fn === 'function') {\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n if (prop === 'init') return init\n\n if (typeof window === 'undefined') return () => {}\n\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n return (...args: unknown[]) => {\n const methodName = String(prop)\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') fn.apply(api, args)\n })\n .catch((error) => {\n console.error(error instanceof Error ? error.message : error)\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton (like HEYO), without breaking when the real widget assigns window.HFChat.\n if (!window.HFChat) window.HFChat = HFChat\n}\n\nexport default HFChat\n"],"mappings":";AAmCA,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AAEzB,IAAI,gBAA2C;AAC/C,IAAI;AAEJ,SAAS,WAAW,WAA2C;AAC7D,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,YAAY,OAAO;AACzB,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,cAAc,UAAW,QAAO;AACpC,SAAO;AACT;AAEA,SAAS,aAAa,KAA4B;AAChD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,OAAO,aAAa,YAAa,QAAO,QAAQ;AACpD,QAAI,OAAO,eAAgB,QAAO,QAAQ;AAE1C,UAAM,WAAW,SAAS;AAAA,MACxB,UAAU,gBAAgB;AAAA,IAC5B;AACA,QAAI,UAAU;AACZ,eAAS,iBAAiB,QAAQ,MAAM,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;AACjE,eAAS;AAAA,QACP;AAAA,QACA,MAAM,OAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,QACtD,EAAE,MAAM,KAAK;AAAA,MACf;AACA;AAAA,IACF;AAEA,WAAO,6BAA6B;AACpC,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,MAAM;AACb,WAAO,aAAa,kBAAkB,GAAG;AACzC,WAAO,aAAa,kBAAkB,OAAO;AAC7C,WAAO,UAAU,MAAM,OAAO,IAAI,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAC/E,WAAO,SAAS,MAAM;AACpB,iBAAW,MAAM;AACf,YAAI,OAAO,eAAgB,SAAQ;AAAA,YAC9B,QAAO,IAAI,MAAM,gDAAgD,CAAC;AAAA,MACzE,GAAG,CAAC;AAAA,IACN;AACA,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAEA,eAAe,WAAW,OAA2B,CAAC,GAAuB;AAC3E,MAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAE3C,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,aAAa,SAAS;AAE5B,MAAI,CAAC,OAAO,gBAAgB,MAAM;AAChC,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAEA,QAAM,MAAM,OAAO,eAAe,KAAK;AAAA,IACrC,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,MAAM,KAAK;AAAA,EACb,CAAC;AAED,SAAO;AACT;AAEA,eAAe,KAAK,OAA2B,CAAC,GAAuB;AACrE,iBAAe,EAAE,GAAG,cAAc,GAAG,KAAK;AAE1C,QAAM,cAAc,WAAW,MAAM;AACrC,MAAI,YAAa,QAAO;AAExB,MAAI,cAAe,QAAO;AAC1B,kBAAgB,WAAW,YAAY;AACvC,SAAO;AACT;AAEA,eAAe,cAAc,YAAwC;AACnE,QAAM,KAAK;AAEX,QAAM,WAAW,WAAW,MAAM;AAClC,MAAI,YAAY,OAAQ,SAAiB,UAAU,MAAM,WAAY,QAAO;AAE5E,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,WAAW;AACf,UAAM,cAAc;AAEpB,UAAM,cAAc,MAAM;AACxB,YAAM,MAAM,WAAW,MAAM;AAC7B,YAAM,KAAK,MAAO,IAAY,UAAU,IAAI;AAE5C,UAAI,OAAO,OAAO,OAAO,YAAY;AACnC,gBAAQ,GAAG;AACX;AAAA,MACF;AAEA,UAAI,WAAW,aAAa;AAC1B,oBAAY;AACZ,mBAAW,aAAa,GAAG;AAC3B;AAAA,MACF;AAEA,aAAO,IAAI,MAAM,kBAAkB,UAAU,iCAAiC,CAAC;AAAA,IACjF;AAEA,eAAW,aAAa,GAAG;AAAA,EAC7B,CAAC;AACH;AAEO,IAAM,SAAuB,IAAI;AAAA,EACtC,CAAC;AAAA,EACD;AAAA,IACE,IAAI,SAAS,MAAM;AACjB,UAAI,SAAS,OAAQ,QAAO;AAE5B,UAAI,OAAO,WAAW,YAAa,QAAO,MAAM;AAAA,MAAC;AAEjD,UAAI,SAAS,SAAS;AACpB,cAAM,MAAM,WAAW,MAAM;AAC7B,eAAO,CAAC,CAAC,KAAK;AAAA,MAChB;AAEA,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM;AACX,gBAAM,MAAM,WAAW,MAAM;AAC7B,iBAAO,KAAK,SAAS,KAAK;AAAA,QAC5B;AAAA,MACF;AAEA,aAAO,IAAI,SAAoB;AAC7B,cAAM,aAAa,OAAO,IAAI;AAC9B,sBAAc,UAAU,EACrB,KAAK,CAAC,QAAQ;AACb,gBAAM,KAAM,IAAY,UAAU;AAClC,cAAI,OAAO,OAAO,WAAY,IAAG,MAAM,KAAK,IAAI;AAAA,QAClD,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,kBAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAAA,QAC9D,CAAC;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAI,OAAO,WAAW,aAAa;AAEjC,MAAI,CAAC,OAAO,OAAQ,QAAO,SAAS;AACtC;AAEA,IAAO,gBAAQ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanfirst-chat/js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "HumanFirst.chat JavaScript SDK",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@types/node": "^25.0.
|
|
23
|
+
"@types/node": "^25.0.8",
|
|
24
24
|
"tsup": "^8.5.1",
|
|
25
25
|
"typescript": "^5.9.3"
|
|
26
26
|
}
|