@bubblav/ai-chatbot-vue 1.0.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/README.md +186 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +184 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# @bubblav/ai-chatbot-vue
|
|
2
|
+
|
|
3
|
+
Vue component for embedding the BubblaV AI chatbot widget in your Vue application.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bubblav/ai-chatbot-vue
|
|
9
|
+
# or
|
|
10
|
+
yarn add @bubblav/ai-chatbot-vue
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @bubblav/ai-chatbot-vue
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Basic Usage
|
|
18
|
+
|
|
19
|
+
```vue
|
|
20
|
+
<script setup>
|
|
21
|
+
import { BubblaVWidget } from '@bubblav/ai-chatbot-vue';
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<BubblaVWidget
|
|
26
|
+
website-id="your-website-id"
|
|
27
|
+
/>
|
|
28
|
+
</template>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### With Custom Styling
|
|
32
|
+
|
|
33
|
+
```vue
|
|
34
|
+
<script setup>
|
|
35
|
+
import { BubblaVWidget } from '@bubblav/ai-chatbot-vue';
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<BubblaVWidget
|
|
40
|
+
website-id="your-website-id"
|
|
41
|
+
bubble-color="#3b82f6"
|
|
42
|
+
bubble-icon-color="#ffffff"
|
|
43
|
+
desktop-position="bottom-right"
|
|
44
|
+
mobile-position="bottom-right"
|
|
45
|
+
/>
|
|
46
|
+
</template>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### With Template Ref for SDK Access
|
|
50
|
+
|
|
51
|
+
```vue
|
|
52
|
+
<script setup>
|
|
53
|
+
import { ref } from 'vue';
|
|
54
|
+
import { BubblaVWidget } from '@bubblav/ai-chatbot-vue';
|
|
55
|
+
|
|
56
|
+
const widgetRef = ref(null);
|
|
57
|
+
|
|
58
|
+
const openWidget = () => {
|
|
59
|
+
widgetRef.value?.open();
|
|
60
|
+
};
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<template>
|
|
64
|
+
<button @click="openWidget">Open Chat</button>
|
|
65
|
+
<BubblaVWidget
|
|
66
|
+
ref="widgetRef"
|
|
67
|
+
website-id="your-website-id"
|
|
68
|
+
/>
|
|
69
|
+
</template>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Using Composables
|
|
73
|
+
|
|
74
|
+
```vue
|
|
75
|
+
<script setup>
|
|
76
|
+
import { useBubblaVWidget } from '@bubblav/ai-chatbot-vue';
|
|
77
|
+
|
|
78
|
+
const widget = useBubblaVWidget();
|
|
79
|
+
|
|
80
|
+
const handleClick = () => {
|
|
81
|
+
widget.value?.sendMessage('Hello, I need help!');
|
|
82
|
+
};
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<button @click="handleClick">Send Message</button>
|
|
87
|
+
</template>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Props
|
|
91
|
+
|
|
92
|
+
| Prop | Type | Required | Default | Description |
|
|
93
|
+
|------|------|----------|---------|-------------|
|
|
94
|
+
| `website-id` | `string` | Yes | - | Your website ID from the BubblaV dashboard |
|
|
95
|
+
| `api-url` | `string` | No | Production API | Custom API URL (for self-hosted deployments) |
|
|
96
|
+
| `bubble-color` | `string` | No | - | Bubble button color (hex) |
|
|
97
|
+
| `bubble-icon-color` | `string` | No | - | Bubble icon color (hex) |
|
|
98
|
+
| `desktop-position` | `'bottom-left' \| 'bottom-right'` | No | `'bottom-right'` | Desktop position |
|
|
99
|
+
| `mobile-position` | `'bottom-left' \| 'bottom-right'` | No | `'bottom-right'` | Mobile position |
|
|
100
|
+
| `powered-by-visible` | `boolean` | No | `true` | Show/hide powered by branding |
|
|
101
|
+
| `bot-name` | `string` | No | `'Bot'` | Custom bot name |
|
|
102
|
+
| `greeting-message` | `string` | No | - | Greeting message when widget opens |
|
|
103
|
+
| `textbox-placeholder` | `string` | No | - | Input placeholder text |
|
|
104
|
+
| `show-action-buttons` | `boolean` | No | `true` | Show/hide action buttons |
|
|
105
|
+
|
|
106
|
+
## SDK Methods (via Ref)
|
|
107
|
+
|
|
108
|
+
| Method | Description |
|
|
109
|
+
|--------|-------------|
|
|
110
|
+
| `open()` | Open the widget |
|
|
111
|
+
| `close()` | Close the widget |
|
|
112
|
+
| `toggle()` | Toggle widget open/close |
|
|
113
|
+
| `isOpen()` | Check if widget is open |
|
|
114
|
+
| `sendMessage(text, conversationId?)` | Send a message programmatically |
|
|
115
|
+
| `showGreeting(message?)` | Show greeting message |
|
|
116
|
+
| `hideGreeting()` | Hide greeting message |
|
|
117
|
+
| `getConfig()` | Get current widget configuration |
|
|
118
|
+
| `setDebug(enabled)` | Enable/disable debug mode |
|
|
119
|
+
|
|
120
|
+
## Composables
|
|
121
|
+
|
|
122
|
+
### `useBubblaVWidget()`
|
|
123
|
+
|
|
124
|
+
Returns a ref containing the BubblaV SDK instance when ready, or `null` while loading.
|
|
125
|
+
|
|
126
|
+
```vue
|
|
127
|
+
<script setup>
|
|
128
|
+
import { useBubblaVWidget } from '@bubblav/ai-chatbot-vue';
|
|
129
|
+
|
|
130
|
+
const widget = useBubblaVWidget();
|
|
131
|
+
</script>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `useBubblaVEvent(eventName, callback)`
|
|
135
|
+
|
|
136
|
+
Listen to widget events.
|
|
137
|
+
|
|
138
|
+
```vue
|
|
139
|
+
<script setup>
|
|
140
|
+
import { useBubblaVEvent } from '@bubblav/ai-chatbot-vue';
|
|
141
|
+
|
|
142
|
+
useBubblaVEvent('widget_opened', () => {
|
|
143
|
+
console.log('Widget opened!');
|
|
144
|
+
});
|
|
145
|
+
</script>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `useBubblaVWidgetState()`
|
|
149
|
+
|
|
150
|
+
Get the current open/closed state of the widget as a ref.
|
|
151
|
+
|
|
152
|
+
```vue
|
|
153
|
+
<script setup>
|
|
154
|
+
import { useBubblaVWidgetState } from '@bubblav/ai-chatbot-vue';
|
|
155
|
+
|
|
156
|
+
const isOpen = useBubblaVWidgetState();
|
|
157
|
+
</script>
|
|
158
|
+
|
|
159
|
+
<template>
|
|
160
|
+
<div v-if="isOpen">Widget is open</div>
|
|
161
|
+
</template>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Getting Your Website ID
|
|
165
|
+
|
|
166
|
+
1. Go to [bubblav.com/dashboard](https://www.bubblav.com/dashboard)
|
|
167
|
+
2. Select your website
|
|
168
|
+
3. Go to **Installation**
|
|
169
|
+
4. Copy your website ID
|
|
170
|
+
|
|
171
|
+
## Server-Side Rendering (SSR)
|
|
172
|
+
|
|
173
|
+
This component is SSR-safe. The widget script only loads in the browser.
|
|
174
|
+
|
|
175
|
+
## TypeScript
|
|
176
|
+
|
|
177
|
+
This package is written in TypeScript and includes full type definitions.
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
|
182
|
+
|
|
183
|
+
## Support
|
|
184
|
+
|
|
185
|
+
- Documentation: [docs.bubblav.com](https://docs.bubblav.com)
|
|
186
|
+
- Issues: [GitHub Issues](https://github.com/tonnguyen/botcanchat/issues)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("vue"),b="https://www.bubblav.com/widget.js";function g(t){const o={};for(const[n,i]of Object.entries(t)){if(i==null)continue;const a="data-"+n.replace(/([A-Z])/g,"-$1").toLowerCase();o[a]=String(i)}return o}function w(t){if(t&&typeof window<"u")try{return new URL(t,window.location.origin).origin+"/widget.js"}catch{return b}return b}function p(t){return typeof t=="string"&&t.length>0&&t.length<100}function v(t){const{websiteId:o,...n}=t;return n}function s(){return typeof window<"u"&&typeof document<"u"}function B(t){if(!s())return!1;const o=document.getElementsByTagName("script");for(let n=0;n<o.length;n++)if(o[n].src===t)return!0;return!1}const I=u.defineComponent({__name:"BubblaVWidget",props:{websiteId:{},apiUrl:{default:void 0},bubbleColor:{default:void 0},bubbleIconColor:{default:void 0},desktopPosition:{default:void 0},mobilePosition:{default:void 0},poweredByVisible:{type:Boolean,default:void 0},botName:{default:void 0},greetingMessage:{default:void 0},textboxPlaceholder:{default:void 0},showActionButtons:{type:Boolean,default:void 0}},setup(t,{expose:o}){const n=t,i=u.ref(!1);let a=null;const r=u.ref(null);function c(){if(!s()||i.value)return;if(!p(n.websiteId)){console.warn(`[BubblaV] Invalid website ID format: "${n.websiteId}". Please check your website ID in the BubblaV dashboard.`);return}const e=w(n.apiUrl);if(B(e)){console.warn("[BubblaV] Widget script already loaded. Only one widget instance should be active."),window.BubblaV&&(r.value=window.BubblaV);return}i.value=!0;const l=document.createElement("script");l.src=e,l.async=!0,l.defer=!0,l.setAttribute("data-site-id",n.websiteId);const m=v(n),V=g(m);for(const[h,y]of Object.entries(V))l.setAttribute(h,y);l.onload=()=>{window.BubblaV&&(r.value=window.BubblaV)},l.onerror=()=>{console.error(`[BubblaV] Failed to load widget script from ${e}. Please check your network connection and ensure the URL is correct.`),i.value=!1},document.body.appendChild(l),a=l}function f(){a&&a.parentNode&&a.parentNode.removeChild(a),a=null,r.value=null,i.value=!1}return o({get open(){var e;return((e=r.value)==null?void 0:e.open)??(()=>!1)},get close(){var e;return((e=r.value)==null?void 0:e.close)??(()=>!1)},get toggle(){var e;return((e=r.value)==null?void 0:e.toggle)??(()=>!1)},get isOpen(){var e;return((e=r.value)==null?void 0:e.isOpen)??(()=>!1)},get sendMessage(){var e;return((e=r.value)==null?void 0:e.sendMessage)??(()=>{})},get showGreeting(){var e;return((e=r.value)==null?void 0:e.showGreeting)??(()=>{})},get hideGreeting(){var e;return((e=r.value)==null?void 0:e.hideGreeting)??(()=>{})},get getConfig(){var e;return((e=r.value)==null?void 0:e.getConfig)??(()=>({}))},get setDebug(){var e;return((e=r.value)==null?void 0:e.setDebug)??(()=>{})}}),u.onMounted(()=>{c()}),u.onUnmounted(()=>{f()}),u.watch(()=>n.websiteId,()=>{f(),c()}),(e,l)=>null}});function W(){const t=u.ref(()=>s()&&window.BubblaV?window.BubblaV:null);let o=null,n=null;return u.onMounted(()=>{t.value||(o=setInterval(()=>{s()&&window.BubblaV&&(t.value=window.BubblaV,o&&clearInterval(o))},100),n=setTimeout(()=>{o&&clearInterval(o)},5e3))}),u.onUnmounted(()=>{o&&clearInterval(o),n&&clearTimeout(n)}),t}function d(t,o){const n=o;u.onMounted(()=>{const i=s()?window.BubblaV:null;i&&i.on(t,n)}),u.onUnmounted(()=>{const i=s()?window.BubblaV:null;i&&i.off(t,n)})}function C(){const t=u.ref(!1);return d("widget_opened",()=>{t.value=!0}),d("widget_closed",()=>{t.value=!1}),t}exports.BubblaVWidget=I;exports.getConfigProps=v;exports.getWidgetScriptUrl=w;exports.isBrowser=s;exports.isScriptLoaded=B;exports.propsToDataAttributes=g;exports.useBubblaVEvent=d;exports.useBubblaVWidget=W;exports.useBubblaVWidgetState=C;exports.validateWebsiteId=p;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/utils.ts","../src/BubblaVWidget.vue","../src/composables.ts"],"sourcesContent":["/**\n * Utility functions for @bubblav/ai-chatbot-vue\n */\n\nimport type { BubblaVWidgetProps } from './types';\n\nconst WIDGET_SCRIPT_URL = 'https://www.bubblav.com/widget.js';\n\n/**\n * Convert camelCase props to data-* attribute names\n */\nexport function propsToDataAttributes(props: Record<string, unknown>): Record<string, string> {\n const dataAttrs: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (value === undefined || value === null) continue;\n\n // Convert camelCase to kebab-case for data attributes\n const dataKey = 'data-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();\n dataAttrs[dataKey] = String(value);\n }\n\n return dataAttrs;\n}\n\n/**\n * Get the widget script URL\n */\nexport function getWidgetScriptUrl(apiUrl?: string): string {\n // If custom API URL provided, derive widget URL from it\n if (apiUrl && typeof window !== 'undefined') {\n try {\n const url = new URL(apiUrl, window.location.origin);\n // Replace /api/chat with /widget.js\n return url.origin + '/widget.js';\n } catch {\n return WIDGET_SCRIPT_URL;\n }\n }\n\n return WIDGET_SCRIPT_URL;\n}\n\n/**\n * Validate website ID format\n */\nexport function validateWebsiteId(websiteId: string): boolean {\n // Basic validation: should be a non-empty string\n // Format varies, so we just check it's a reasonable string\n return typeof websiteId === 'string' && websiteId.length > 0 && websiteId.length < 100;\n}\n\n/**\n * Filter props to only include widget config (exclude websiteId)\n */\nexport function getConfigProps(props: BubblaVWidgetProps): Omit<BubblaVWidgetProps, 'websiteId'> {\n const { websiteId: _websiteId, ...configProps } = props;\n return configProps;\n}\n\n/**\n * Check if we're in a browser environment\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Check if a widget script is already loaded\n */\nexport function isScriptLoaded(url: string): boolean {\n if (!isBrowser()) return false;\n\n const scripts = document.getElementsByTagName('script');\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src === url) {\n return true;\n }\n }\n return false;\n}\n","<script setup lang=\"ts\">\n/**\n * BubblaVWidget Vue Component\n */\nimport { onMounted, onUnmounted, ref, watch } from 'vue';\nimport type { BubblaVWidgetSDK } from './types';\nimport {\n propsToDataAttributes,\n getWidgetScriptUrl,\n validateWebsiteId,\n getConfigProps,\n isBrowser,\n isScriptLoaded,\n} from './utils';\n\n// Props definition\ninterface Props {\n /** Required: Your website ID from the BubblaV dashboard */\n websiteId: string;\n\n /** Optional: Custom API URL (defaults to production) */\n apiUrl?: string;\n\n /** Optional: Bubble button color (hex) */\n bubbleColor?: string;\n\n /** Optional: Bubble icon color (hex) */\n bubbleIconColor?: string;\n\n /** Optional: Desktop position */\n desktopPosition?: 'bottom-left' | 'bottom-right';\n\n /** Optional: Mobile position */\n mobilePosition?: 'bottom-left' | 'bottom-right';\n\n /** Optional: Show/hide powered by branding */\n poweredByVisible?: boolean;\n\n /** Optional: Custom bot name */\n botName?: string;\n\n /** Optional: Greeting message shown when widget opens */\n greetingMessage?: string;\n\n /** Optional: Placeholder text for input field */\n textboxPlaceholder?: string;\n\n /** Optional: Whether to show action buttons */\n showActionButtons?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n apiUrl: undefined,\n bubbleColor: undefined,\n bubbleIconColor: undefined,\n desktopPosition: undefined,\n mobilePosition: undefined,\n poweredByVisible: undefined,\n botName: undefined,\n greetingMessage: undefined,\n textboxPlaceholder: undefined,\n showActionButtons: undefined,\n});\n\n// Internal state\nconst isInitialized = ref(false);\nlet scriptElement: HTMLScriptElement | null = null;\nconst sdk = ref<BubblaVWidgetSDK | null>(null);\n\n// Initialize widget\nfunction initWidget() {\n // Skip if not in browser (SSR)\n if (!isBrowser()) {\n return;\n }\n\n // Prevent double initialization\n if (isInitialized.value) {\n return;\n }\n\n // Validate website ID\n if (!validateWebsiteId(props.websiteId)) {\n console.warn(\n `[BubblaV] Invalid website ID format: \"${props.websiteId}\". ` +\n `Please check your website ID in the BubblaV dashboard.`\n );\n return;\n }\n\n // Get script URL\n const scriptUrl = getWidgetScriptUrl(props.apiUrl);\n\n // Check if script is already loaded\n if (isScriptLoaded(scriptUrl)) {\n console.warn(\n `[BubblaV] Widget script already loaded. ` +\n `Only one widget instance should be active.`\n );\n // Use existing SDK\n if (window.BubblaV) {\n sdk.value = window.BubblaV;\n }\n return;\n }\n\n // Mark as initialized\n isInitialized.value = true;\n\n // Create script element\n const script = document.createElement('script');\n script.src = scriptUrl;\n script.async = true;\n script.defer = true;\n\n // Set data attributes\n script.setAttribute('data-site-id', props.websiteId);\n\n // Set optional config as data attributes\n const config = getConfigProps(props);\n const dataAttrs = propsToDataAttributes(config);\n for (const [key, value] of Object.entries(dataAttrs)) {\n script.setAttribute(key, value);\n }\n\n // Handle load event\n script.onload = () => {\n // SDK should be available now\n if (window.BubblaV) {\n sdk.value = window.BubblaV;\n }\n };\n\n // Handle error event\n script.onerror = () => {\n console.error(\n `[BubblaV] Failed to load widget script from ${scriptUrl}. ` +\n `Please check your network connection and ensure the URL is correct.`\n );\n isInitialized.value = false;\n };\n\n // Add script to document\n document.body.appendChild(script);\n scriptElement = script;\n}\n\n// Cleanup widget\nfunction cleanupWidget() {\n if (scriptElement && scriptElement.parentNode) {\n scriptElement.parentNode.removeChild(scriptElement);\n }\n scriptElement = null;\n sdk.value = null;\n isInitialized.value = false;\n}\n\n// Expose SDK methods\ndefineExpose({\n get open() {\n return sdk.value?.open ?? (() => false);\n },\n get close() {\n return sdk.value?.close ?? (() => false);\n },\n get toggle() {\n return sdk.value?.toggle ?? (() => false);\n },\n get isOpen() {\n return sdk.value?.isOpen ?? (() => false);\n },\n get sendMessage() {\n return sdk.value?.sendMessage ?? (() => {});\n },\n get showGreeting() {\n return sdk.value?.showGreeting ?? (() => {});\n },\n get hideGreeting() {\n return sdk.value?.hideGreeting ?? (() => {});\n },\n get getConfig() {\n return sdk.value?.getConfig ?? (() => ({}));\n },\n get setDebug() {\n return sdk.value?.setDebug ?? (() => {});\n },\n});\n\n// Initialize on mount\nonMounted(() => {\n initWidget();\n});\n\n// Cleanup on unmount\nonUnmounted(() => {\n cleanupWidget();\n});\n\n// Watch for websiteId changes (re-init if changes)\nwatch(() => props.websiteId, () => {\n cleanupWidget();\n initWidget();\n});\n</script>\n\n<template>\n <!-- Component renders nothing (script-only widget) -->\n</template>\n","/**\n * Vue composables for @bubblav/ai-chatbot-vue\n */\n\nimport { ref, onMounted, onUnmounted, type Ref } from 'vue';\nimport type { BubblaVSDK } from './types';\nimport { isBrowser } from './utils';\n\n/**\n * Composable to access the BubblaV SDK programmatically\n * Returns null until the widget is ready\n */\nexport function useBubblaVWidget(): Ref<BubblaVSDK | null> {\n const sdk = ref<BubblaVSDK | null>(() => {\n // Check if SDK is already available\n if (isBrowser() && window.BubblaV) {\n return window.BubblaV;\n }\n return null;\n });\n\n let interval: ReturnType<typeof setInterval> | null = null;\n let timeout: ReturnType<typeof setTimeout> | null = null;\n\n onMounted(() => {\n // If SDK already available, no need to set up listener\n if (sdk.value) return;\n\n // Poll for SDK availability\n interval = setInterval(() => {\n if (isBrowser() && window.BubblaV) {\n sdk.value = window.BubblaV;\n if (interval) clearInterval(interval);\n }\n }, 100);\n\n // Cleanup interval after 5 seconds\n timeout = setTimeout(() => {\n if (interval) clearInterval(interval);\n }, 5000);\n });\n\n onUnmounted(() => {\n if (interval) clearInterval(interval);\n if (timeout) clearTimeout(timeout);\n });\n\n return sdk;\n}\n\n/**\n * Composable to listen to widget events\n *\n * @example\n * ```vue\n * <script setup>\n * import { useBubblaVEvent } from '@bubblav/ai-chatbot-vue';\n *\n * useBubblaVEvent('widget_opened', () => {\n * console.log('Widget opened!');\n * });\n * </script>\n * ```\n */\nexport function useBubblaVEvent(\n eventName: string,\n callback: (...args: unknown[]) => void\n): void {\n const listener = callback;\n\n onMounted(() => {\n const sdk = isBrowser() ? window.BubblaV : null;\n if (!sdk) return;\n\n sdk.on(eventName, listener);\n });\n\n onUnmounted(() => {\n const sdk = isBrowser() ? window.BubblaV : null;\n if (!sdk) return;\n\n sdk.off(eventName, listener);\n });\n}\n\n/**\n * Composable to get widget open/closed state\n */\nexport function useBubblaVWidgetState(): Ref<boolean> {\n const isOpen = ref(false);\n\n useBubblaVEvent('widget_opened', () => {\n isOpen.value = true;\n });\n\n useBubblaVEvent('widget_closed', () => {\n isOpen.value = false;\n });\n\n return isOpen;\n}\n"],"names":["WIDGET_SCRIPT_URL","propsToDataAttributes","props","dataAttrs","key","value","dataKey","getWidgetScriptUrl","apiUrl","validateWebsiteId","websiteId","getConfigProps","_websiteId","configProps","isBrowser","isScriptLoaded","url","scripts","i","__props","isInitialized","ref","scriptElement","sdk","initWidget","scriptUrl","script","config","cleanupWidget","__expose","_a","onMounted","onUnmounted","watch","useBubblaVWidget","interval","timeout","useBubblaVEvent","eventName","callback","listener","useBubblaVWidgetState","isOpen"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { ComponentOptionsMixin } from 'vue';
|
|
2
|
+
import { ComponentProvideOptions } from 'vue';
|
|
3
|
+
import { DefineComponent } from 'vue';
|
|
4
|
+
import { PublicProps } from 'vue';
|
|
5
|
+
import { Ref } from 'vue';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Global window.BubblaV SDK interface
|
|
9
|
+
*/
|
|
10
|
+
export declare interface BubblaVSDK extends BubblaVWidgetSDK {
|
|
11
|
+
/** Register event listener */
|
|
12
|
+
on: (event: string, callback: (...args: unknown[]) => void) => void;
|
|
13
|
+
/** Unregister event listener */
|
|
14
|
+
off: (event: string, callback: (...args: unknown[]) => void) => void;
|
|
15
|
+
/** Emit event */
|
|
16
|
+
emit: (event: string, data?: unknown) => void;
|
|
17
|
+
/** Ready callback for widget loaded */
|
|
18
|
+
ready: (callback: () => void) => void;
|
|
19
|
+
/** Track analytics event */
|
|
20
|
+
track: (eventName: string, properties?: Record<string, unknown>) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export declare const BubblaVWidget: DefineComponent<Props, {
|
|
24
|
+
open: () => void;
|
|
25
|
+
close: () => void;
|
|
26
|
+
toggle: () => void;
|
|
27
|
+
isOpen: () => boolean;
|
|
28
|
+
sendMessage: (text: string, conversationId?: string) => void;
|
|
29
|
+
showGreeting: (message?: string) => void;
|
|
30
|
+
hideGreeting: () => void;
|
|
31
|
+
getConfig: () => {};
|
|
32
|
+
setDebug: (enabled: boolean) => void;
|
|
33
|
+
}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<Props> & Readonly<{}>, {
|
|
34
|
+
apiUrl: string;
|
|
35
|
+
bubbleColor: string;
|
|
36
|
+
bubbleIconColor: string;
|
|
37
|
+
desktopPosition: "bottom-left" | "bottom-right";
|
|
38
|
+
mobilePosition: "bottom-left" | "bottom-right";
|
|
39
|
+
poweredByVisible: boolean;
|
|
40
|
+
botName: string;
|
|
41
|
+
greetingMessage: string;
|
|
42
|
+
textboxPlaceholder: string;
|
|
43
|
+
showActionButtons: boolean;
|
|
44
|
+
}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Props for the BubblaVWidget component
|
|
48
|
+
*/
|
|
49
|
+
export declare interface BubblaVWidgetProps {
|
|
50
|
+
/** Required: Your website ID from the BubblaV dashboard */
|
|
51
|
+
websiteId: string;
|
|
52
|
+
/** Optional: Custom API URL (defaults to production) */
|
|
53
|
+
apiUrl?: string;
|
|
54
|
+
/** Optional: Bubble button color (hex) */
|
|
55
|
+
bubbleColor?: string;
|
|
56
|
+
/** Optional: Bubble icon color (hex) */
|
|
57
|
+
bubbleIconColor?: string;
|
|
58
|
+
/** Optional: Desktop position */
|
|
59
|
+
desktopPosition?: WidgetPosition;
|
|
60
|
+
/** Optional: Mobile position */
|
|
61
|
+
mobilePosition?: WidgetPosition;
|
|
62
|
+
/** Optional: Show/hide powered by branding */
|
|
63
|
+
poweredByVisible?: boolean;
|
|
64
|
+
/** Optional: Custom bot name */
|
|
65
|
+
botName?: string;
|
|
66
|
+
/** Optional: Greeting message shown when widget opens */
|
|
67
|
+
greetingMessage?: string;
|
|
68
|
+
/** Optional: Placeholder text for input field */
|
|
69
|
+
textboxPlaceholder?: string;
|
|
70
|
+
/** Optional: Whether to show action buttons */
|
|
71
|
+
showActionButtons?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* SDK methods available via expose
|
|
76
|
+
*/
|
|
77
|
+
export declare interface BubblaVWidgetSDK {
|
|
78
|
+
/** Open the widget */
|
|
79
|
+
open: () => void;
|
|
80
|
+
/** Close the widget */
|
|
81
|
+
close: () => void;
|
|
82
|
+
/** Toggle widget open/close state */
|
|
83
|
+
toggle: () => void;
|
|
84
|
+
/** Check if widget is currently open */
|
|
85
|
+
isOpen: () => boolean;
|
|
86
|
+
/** Send a message programmatically */
|
|
87
|
+
sendMessage: (text: string, conversationId?: string) => void;
|
|
88
|
+
/** Show greeting message */
|
|
89
|
+
showGreeting: (message?: string) => void;
|
|
90
|
+
/** Hide greeting message */
|
|
91
|
+
hideGreeting: () => void;
|
|
92
|
+
/** Get current widget configuration */
|
|
93
|
+
getConfig: () => Record<string, unknown>;
|
|
94
|
+
/** Enable or disable debug mode */
|
|
95
|
+
setDebug: (enabled: boolean) => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Filter props to only include widget config (exclude websiteId)
|
|
100
|
+
*/
|
|
101
|
+
export declare function getConfigProps(props: BubblaVWidgetProps): Omit<BubblaVWidgetProps, 'websiteId'>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the widget script URL
|
|
105
|
+
*/
|
|
106
|
+
export declare function getWidgetScriptUrl(apiUrl?: string): string;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if we're in a browser environment
|
|
110
|
+
*/
|
|
111
|
+
export declare function isBrowser(): boolean;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if a widget script is already loaded
|
|
115
|
+
*/
|
|
116
|
+
export declare function isScriptLoaded(url: string): boolean;
|
|
117
|
+
|
|
118
|
+
declare interface Props {
|
|
119
|
+
/** Required: Your website ID from the BubblaV dashboard */
|
|
120
|
+
websiteId: string;
|
|
121
|
+
/** Optional: Custom API URL (defaults to production) */
|
|
122
|
+
apiUrl?: string;
|
|
123
|
+
/** Optional: Bubble button color (hex) */
|
|
124
|
+
bubbleColor?: string;
|
|
125
|
+
/** Optional: Bubble icon color (hex) */
|
|
126
|
+
bubbleIconColor?: string;
|
|
127
|
+
/** Optional: Desktop position */
|
|
128
|
+
desktopPosition?: 'bottom-left' | 'bottom-right';
|
|
129
|
+
/** Optional: Mobile position */
|
|
130
|
+
mobilePosition?: 'bottom-left' | 'bottom-right';
|
|
131
|
+
/** Optional: Show/hide powered by branding */
|
|
132
|
+
poweredByVisible?: boolean;
|
|
133
|
+
/** Optional: Custom bot name */
|
|
134
|
+
botName?: string;
|
|
135
|
+
/** Optional: Greeting message shown when widget opens */
|
|
136
|
+
greetingMessage?: string;
|
|
137
|
+
/** Optional: Placeholder text for input field */
|
|
138
|
+
textboxPlaceholder?: string;
|
|
139
|
+
/** Optional: Whether to show action buttons */
|
|
140
|
+
showActionButtons?: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Convert camelCase props to data-* attribute names
|
|
145
|
+
*/
|
|
146
|
+
export declare function propsToDataAttributes(props: Record<string, unknown>): Record<string, string>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Composable to listen to widget events
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```vue
|
|
153
|
+
* <script setup>
|
|
154
|
+
* import { useBubblaVEvent } from '@bubblav/ai-chatbot-vue';
|
|
155
|
+
*
|
|
156
|
+
* useBubblaVEvent('widget_opened', () => {
|
|
157
|
+
* console.log('Widget opened!');
|
|
158
|
+
* });
|
|
159
|
+
* </script>
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare function useBubblaVEvent(eventName: string, callback: (...args: unknown[]) => void): void;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Composable to access the BubblaV SDK programmatically
|
|
166
|
+
* Returns null until the widget is ready
|
|
167
|
+
*/
|
|
168
|
+
export declare function useBubblaVWidget(): Ref<BubblaVSDK | null>;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Composable to get widget open/closed state
|
|
172
|
+
*/
|
|
173
|
+
export declare function useBubblaVWidgetState(): Ref<boolean>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Validate website ID format
|
|
177
|
+
*/
|
|
178
|
+
export declare function validateWebsiteId(websiteId: string): boolean;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Type definitions for @bubblav/ai-chatbot-vue
|
|
182
|
+
*/
|
|
183
|
+
/** Widget position options */
|
|
184
|
+
declare type WidgetPosition = 'bottom-left' | 'bottom-right';
|
|
185
|
+
|
|
186
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { defineComponent as h, ref as s, onMounted as d, onUnmounted as c, watch as V } from "vue";
|
|
2
|
+
const g = "https://www.bubblav.com/widget.js";
|
|
3
|
+
function y(t) {
|
|
4
|
+
const o = {};
|
|
5
|
+
for (const [n, i] of Object.entries(t)) {
|
|
6
|
+
if (i == null) continue;
|
|
7
|
+
const u = "data-" + n.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
8
|
+
o[u] = String(i);
|
|
9
|
+
}
|
|
10
|
+
return o;
|
|
11
|
+
}
|
|
12
|
+
function I(t) {
|
|
13
|
+
if (t && typeof window < "u")
|
|
14
|
+
try {
|
|
15
|
+
return new URL(t, window.location.origin).origin + "/widget.js";
|
|
16
|
+
} catch {
|
|
17
|
+
return g;
|
|
18
|
+
}
|
|
19
|
+
return g;
|
|
20
|
+
}
|
|
21
|
+
function _(t) {
|
|
22
|
+
return typeof t == "string" && t.length > 0 && t.length < 100;
|
|
23
|
+
}
|
|
24
|
+
function k(t) {
|
|
25
|
+
const { websiteId: o, ...n } = t;
|
|
26
|
+
return n;
|
|
27
|
+
}
|
|
28
|
+
function a() {
|
|
29
|
+
return typeof window < "u" && typeof document < "u";
|
|
30
|
+
}
|
|
31
|
+
function C(t) {
|
|
32
|
+
if (!a()) return !1;
|
|
33
|
+
const o = document.getElementsByTagName("script");
|
|
34
|
+
for (let n = 0; n < o.length; n++)
|
|
35
|
+
if (o[n].src === t)
|
|
36
|
+
return !0;
|
|
37
|
+
return !1;
|
|
38
|
+
}
|
|
39
|
+
const P = /* @__PURE__ */ h({
|
|
40
|
+
__name: "BubblaVWidget",
|
|
41
|
+
props: {
|
|
42
|
+
websiteId: {},
|
|
43
|
+
apiUrl: { default: void 0 },
|
|
44
|
+
bubbleColor: { default: void 0 },
|
|
45
|
+
bubbleIconColor: { default: void 0 },
|
|
46
|
+
desktopPosition: { default: void 0 },
|
|
47
|
+
mobilePosition: { default: void 0 },
|
|
48
|
+
poweredByVisible: { type: Boolean, default: void 0 },
|
|
49
|
+
botName: { default: void 0 },
|
|
50
|
+
greetingMessage: { default: void 0 },
|
|
51
|
+
textboxPlaceholder: { default: void 0 },
|
|
52
|
+
showActionButtons: { type: Boolean, default: void 0 }
|
|
53
|
+
},
|
|
54
|
+
setup(t, { expose: o }) {
|
|
55
|
+
const n = t, i = s(!1);
|
|
56
|
+
let u = null;
|
|
57
|
+
const r = s(null);
|
|
58
|
+
function f() {
|
|
59
|
+
if (!a() || i.value)
|
|
60
|
+
return;
|
|
61
|
+
if (!_(n.websiteId)) {
|
|
62
|
+
console.warn(
|
|
63
|
+
`[BubblaV] Invalid website ID format: "${n.websiteId}". Please check your website ID in the BubblaV dashboard.`
|
|
64
|
+
);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const e = I(n.apiUrl);
|
|
68
|
+
if (C(e)) {
|
|
69
|
+
console.warn(
|
|
70
|
+
"[BubblaV] Widget script already loaded. Only one widget instance should be active."
|
|
71
|
+
), window.BubblaV && (r.value = window.BubblaV);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
i.value = !0;
|
|
75
|
+
const l = document.createElement("script");
|
|
76
|
+
l.src = e, l.async = !0, l.defer = !0, l.setAttribute("data-site-id", n.websiteId);
|
|
77
|
+
const p = k(n), v = y(p);
|
|
78
|
+
for (const [B, m] of Object.entries(v))
|
|
79
|
+
l.setAttribute(B, m);
|
|
80
|
+
l.onload = () => {
|
|
81
|
+
window.BubblaV && (r.value = window.BubblaV);
|
|
82
|
+
}, l.onerror = () => {
|
|
83
|
+
console.error(
|
|
84
|
+
`[BubblaV] Failed to load widget script from ${e}. Please check your network connection and ensure the URL is correct.`
|
|
85
|
+
), i.value = !1;
|
|
86
|
+
}, document.body.appendChild(l), u = l;
|
|
87
|
+
}
|
|
88
|
+
function b() {
|
|
89
|
+
u && u.parentNode && u.parentNode.removeChild(u), u = null, r.value = null, i.value = !1;
|
|
90
|
+
}
|
|
91
|
+
return o({
|
|
92
|
+
get open() {
|
|
93
|
+
var e;
|
|
94
|
+
return ((e = r.value) == null ? void 0 : e.open) ?? (() => !1);
|
|
95
|
+
},
|
|
96
|
+
get close() {
|
|
97
|
+
var e;
|
|
98
|
+
return ((e = r.value) == null ? void 0 : e.close) ?? (() => !1);
|
|
99
|
+
},
|
|
100
|
+
get toggle() {
|
|
101
|
+
var e;
|
|
102
|
+
return ((e = r.value) == null ? void 0 : e.toggle) ?? (() => !1);
|
|
103
|
+
},
|
|
104
|
+
get isOpen() {
|
|
105
|
+
var e;
|
|
106
|
+
return ((e = r.value) == null ? void 0 : e.isOpen) ?? (() => !1);
|
|
107
|
+
},
|
|
108
|
+
get sendMessage() {
|
|
109
|
+
var e;
|
|
110
|
+
return ((e = r.value) == null ? void 0 : e.sendMessage) ?? (() => {
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
get showGreeting() {
|
|
114
|
+
var e;
|
|
115
|
+
return ((e = r.value) == null ? void 0 : e.showGreeting) ?? (() => {
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
get hideGreeting() {
|
|
119
|
+
var e;
|
|
120
|
+
return ((e = r.value) == null ? void 0 : e.hideGreeting) ?? (() => {
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
get getConfig() {
|
|
124
|
+
var e;
|
|
125
|
+
return ((e = r.value) == null ? void 0 : e.getConfig) ?? (() => ({}));
|
|
126
|
+
},
|
|
127
|
+
get setDebug() {
|
|
128
|
+
var e;
|
|
129
|
+
return ((e = r.value) == null ? void 0 : e.setDebug) ?? (() => {
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}), d(() => {
|
|
133
|
+
f();
|
|
134
|
+
}), c(() => {
|
|
135
|
+
b();
|
|
136
|
+
}), V(() => n.websiteId, () => {
|
|
137
|
+
b(), f();
|
|
138
|
+
}), (e, l) => null;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
function A() {
|
|
142
|
+
const t = s(() => a() && window.BubblaV ? window.BubblaV : null);
|
|
143
|
+
let o = null, n = null;
|
|
144
|
+
return d(() => {
|
|
145
|
+
t.value || (o = setInterval(() => {
|
|
146
|
+
a() && window.BubblaV && (t.value = window.BubblaV, o && clearInterval(o));
|
|
147
|
+
}, 100), n = setTimeout(() => {
|
|
148
|
+
o && clearInterval(o);
|
|
149
|
+
}, 5e3));
|
|
150
|
+
}), c(() => {
|
|
151
|
+
o && clearInterval(o), n && clearTimeout(n);
|
|
152
|
+
}), t;
|
|
153
|
+
}
|
|
154
|
+
function w(t, o) {
|
|
155
|
+
const n = o;
|
|
156
|
+
d(() => {
|
|
157
|
+
const i = a() ? window.BubblaV : null;
|
|
158
|
+
i && i.on(t, n);
|
|
159
|
+
}), c(() => {
|
|
160
|
+
const i = a() ? window.BubblaV : null;
|
|
161
|
+
i && i.off(t, n);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function U() {
|
|
165
|
+
const t = s(!1);
|
|
166
|
+
return w("widget_opened", () => {
|
|
167
|
+
t.value = !0;
|
|
168
|
+
}), w("widget_closed", () => {
|
|
169
|
+
t.value = !1;
|
|
170
|
+
}), t;
|
|
171
|
+
}
|
|
172
|
+
export {
|
|
173
|
+
P as BubblaVWidget,
|
|
174
|
+
k as getConfigProps,
|
|
175
|
+
I as getWidgetScriptUrl,
|
|
176
|
+
a as isBrowser,
|
|
177
|
+
C as isScriptLoaded,
|
|
178
|
+
y as propsToDataAttributes,
|
|
179
|
+
w as useBubblaVEvent,
|
|
180
|
+
A as useBubblaVWidget,
|
|
181
|
+
U as useBubblaVWidgetState,
|
|
182
|
+
_ as validateWebsiteId
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/utils.ts","../src/BubblaVWidget.vue","../src/composables.ts"],"sourcesContent":["/**\n * Utility functions for @bubblav/ai-chatbot-vue\n */\n\nimport type { BubblaVWidgetProps } from './types';\n\nconst WIDGET_SCRIPT_URL = 'https://www.bubblav.com/widget.js';\n\n/**\n * Convert camelCase props to data-* attribute names\n */\nexport function propsToDataAttributes(props: Record<string, unknown>): Record<string, string> {\n const dataAttrs: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (value === undefined || value === null) continue;\n\n // Convert camelCase to kebab-case for data attributes\n const dataKey = 'data-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();\n dataAttrs[dataKey] = String(value);\n }\n\n return dataAttrs;\n}\n\n/**\n * Get the widget script URL\n */\nexport function getWidgetScriptUrl(apiUrl?: string): string {\n // If custom API URL provided, derive widget URL from it\n if (apiUrl && typeof window !== 'undefined') {\n try {\n const url = new URL(apiUrl, window.location.origin);\n // Replace /api/chat with /widget.js\n return url.origin + '/widget.js';\n } catch {\n return WIDGET_SCRIPT_URL;\n }\n }\n\n return WIDGET_SCRIPT_URL;\n}\n\n/**\n * Validate website ID format\n */\nexport function validateWebsiteId(websiteId: string): boolean {\n // Basic validation: should be a non-empty string\n // Format varies, so we just check it's a reasonable string\n return typeof websiteId === 'string' && websiteId.length > 0 && websiteId.length < 100;\n}\n\n/**\n * Filter props to only include widget config (exclude websiteId)\n */\nexport function getConfigProps(props: BubblaVWidgetProps): Omit<BubblaVWidgetProps, 'websiteId'> {\n const { websiteId: _websiteId, ...configProps } = props;\n return configProps;\n}\n\n/**\n * Check if we're in a browser environment\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Check if a widget script is already loaded\n */\nexport function isScriptLoaded(url: string): boolean {\n if (!isBrowser()) return false;\n\n const scripts = document.getElementsByTagName('script');\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src === url) {\n return true;\n }\n }\n return false;\n}\n","<script setup lang=\"ts\">\n/**\n * BubblaVWidget Vue Component\n */\nimport { onMounted, onUnmounted, ref, watch } from 'vue';\nimport type { BubblaVWidgetSDK } from './types';\nimport {\n propsToDataAttributes,\n getWidgetScriptUrl,\n validateWebsiteId,\n getConfigProps,\n isBrowser,\n isScriptLoaded,\n} from './utils';\n\n// Props definition\ninterface Props {\n /** Required: Your website ID from the BubblaV dashboard */\n websiteId: string;\n\n /** Optional: Custom API URL (defaults to production) */\n apiUrl?: string;\n\n /** Optional: Bubble button color (hex) */\n bubbleColor?: string;\n\n /** Optional: Bubble icon color (hex) */\n bubbleIconColor?: string;\n\n /** Optional: Desktop position */\n desktopPosition?: 'bottom-left' | 'bottom-right';\n\n /** Optional: Mobile position */\n mobilePosition?: 'bottom-left' | 'bottom-right';\n\n /** Optional: Show/hide powered by branding */\n poweredByVisible?: boolean;\n\n /** Optional: Custom bot name */\n botName?: string;\n\n /** Optional: Greeting message shown when widget opens */\n greetingMessage?: string;\n\n /** Optional: Placeholder text for input field */\n textboxPlaceholder?: string;\n\n /** Optional: Whether to show action buttons */\n showActionButtons?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n apiUrl: undefined,\n bubbleColor: undefined,\n bubbleIconColor: undefined,\n desktopPosition: undefined,\n mobilePosition: undefined,\n poweredByVisible: undefined,\n botName: undefined,\n greetingMessage: undefined,\n textboxPlaceholder: undefined,\n showActionButtons: undefined,\n});\n\n// Internal state\nconst isInitialized = ref(false);\nlet scriptElement: HTMLScriptElement | null = null;\nconst sdk = ref<BubblaVWidgetSDK | null>(null);\n\n// Initialize widget\nfunction initWidget() {\n // Skip if not in browser (SSR)\n if (!isBrowser()) {\n return;\n }\n\n // Prevent double initialization\n if (isInitialized.value) {\n return;\n }\n\n // Validate website ID\n if (!validateWebsiteId(props.websiteId)) {\n console.warn(\n `[BubblaV] Invalid website ID format: \"${props.websiteId}\". ` +\n `Please check your website ID in the BubblaV dashboard.`\n );\n return;\n }\n\n // Get script URL\n const scriptUrl = getWidgetScriptUrl(props.apiUrl);\n\n // Check if script is already loaded\n if (isScriptLoaded(scriptUrl)) {\n console.warn(\n `[BubblaV] Widget script already loaded. ` +\n `Only one widget instance should be active.`\n );\n // Use existing SDK\n if (window.BubblaV) {\n sdk.value = window.BubblaV;\n }\n return;\n }\n\n // Mark as initialized\n isInitialized.value = true;\n\n // Create script element\n const script = document.createElement('script');\n script.src = scriptUrl;\n script.async = true;\n script.defer = true;\n\n // Set data attributes\n script.setAttribute('data-site-id', props.websiteId);\n\n // Set optional config as data attributes\n const config = getConfigProps(props);\n const dataAttrs = propsToDataAttributes(config);\n for (const [key, value] of Object.entries(dataAttrs)) {\n script.setAttribute(key, value);\n }\n\n // Handle load event\n script.onload = () => {\n // SDK should be available now\n if (window.BubblaV) {\n sdk.value = window.BubblaV;\n }\n };\n\n // Handle error event\n script.onerror = () => {\n console.error(\n `[BubblaV] Failed to load widget script from ${scriptUrl}. ` +\n `Please check your network connection and ensure the URL is correct.`\n );\n isInitialized.value = false;\n };\n\n // Add script to document\n document.body.appendChild(script);\n scriptElement = script;\n}\n\n// Cleanup widget\nfunction cleanupWidget() {\n if (scriptElement && scriptElement.parentNode) {\n scriptElement.parentNode.removeChild(scriptElement);\n }\n scriptElement = null;\n sdk.value = null;\n isInitialized.value = false;\n}\n\n// Expose SDK methods\ndefineExpose({\n get open() {\n return sdk.value?.open ?? (() => false);\n },\n get close() {\n return sdk.value?.close ?? (() => false);\n },\n get toggle() {\n return sdk.value?.toggle ?? (() => false);\n },\n get isOpen() {\n return sdk.value?.isOpen ?? (() => false);\n },\n get sendMessage() {\n return sdk.value?.sendMessage ?? (() => {});\n },\n get showGreeting() {\n return sdk.value?.showGreeting ?? (() => {});\n },\n get hideGreeting() {\n return sdk.value?.hideGreeting ?? (() => {});\n },\n get getConfig() {\n return sdk.value?.getConfig ?? (() => ({}));\n },\n get setDebug() {\n return sdk.value?.setDebug ?? (() => {});\n },\n});\n\n// Initialize on mount\nonMounted(() => {\n initWidget();\n});\n\n// Cleanup on unmount\nonUnmounted(() => {\n cleanupWidget();\n});\n\n// Watch for websiteId changes (re-init if changes)\nwatch(() => props.websiteId, () => {\n cleanupWidget();\n initWidget();\n});\n</script>\n\n<template>\n <!-- Component renders nothing (script-only widget) -->\n</template>\n","/**\n * Vue composables for @bubblav/ai-chatbot-vue\n */\n\nimport { ref, onMounted, onUnmounted, type Ref } from 'vue';\nimport type { BubblaVSDK } from './types';\nimport { isBrowser } from './utils';\n\n/**\n * Composable to access the BubblaV SDK programmatically\n * Returns null until the widget is ready\n */\nexport function useBubblaVWidget(): Ref<BubblaVSDK | null> {\n const sdk = ref<BubblaVSDK | null>(() => {\n // Check if SDK is already available\n if (isBrowser() && window.BubblaV) {\n return window.BubblaV;\n }\n return null;\n });\n\n let interval: ReturnType<typeof setInterval> | null = null;\n let timeout: ReturnType<typeof setTimeout> | null = null;\n\n onMounted(() => {\n // If SDK already available, no need to set up listener\n if (sdk.value) return;\n\n // Poll for SDK availability\n interval = setInterval(() => {\n if (isBrowser() && window.BubblaV) {\n sdk.value = window.BubblaV;\n if (interval) clearInterval(interval);\n }\n }, 100);\n\n // Cleanup interval after 5 seconds\n timeout = setTimeout(() => {\n if (interval) clearInterval(interval);\n }, 5000);\n });\n\n onUnmounted(() => {\n if (interval) clearInterval(interval);\n if (timeout) clearTimeout(timeout);\n });\n\n return sdk;\n}\n\n/**\n * Composable to listen to widget events\n *\n * @example\n * ```vue\n * <script setup>\n * import { useBubblaVEvent } from '@bubblav/ai-chatbot-vue';\n *\n * useBubblaVEvent('widget_opened', () => {\n * console.log('Widget opened!');\n * });\n * </script>\n * ```\n */\nexport function useBubblaVEvent(\n eventName: string,\n callback: (...args: unknown[]) => void\n): void {\n const listener = callback;\n\n onMounted(() => {\n const sdk = isBrowser() ? window.BubblaV : null;\n if (!sdk) return;\n\n sdk.on(eventName, listener);\n });\n\n onUnmounted(() => {\n const sdk = isBrowser() ? window.BubblaV : null;\n if (!sdk) return;\n\n sdk.off(eventName, listener);\n });\n}\n\n/**\n * Composable to get widget open/closed state\n */\nexport function useBubblaVWidgetState(): Ref<boolean> {\n const isOpen = ref(false);\n\n useBubblaVEvent('widget_opened', () => {\n isOpen.value = true;\n });\n\n useBubblaVEvent('widget_closed', () => {\n isOpen.value = false;\n });\n\n return isOpen;\n}\n"],"names":["WIDGET_SCRIPT_URL","propsToDataAttributes","props","dataAttrs","key","value","dataKey","getWidgetScriptUrl","apiUrl","validateWebsiteId","websiteId","getConfigProps","_websiteId","configProps","isBrowser","isScriptLoaded","url","scripts","i","__props","isInitialized","ref","scriptElement","sdk","initWidget","scriptUrl","script","config","cleanupWidget","__expose","_a","onMounted","onUnmounted","watch","useBubblaVWidget","interval","timeout","useBubblaVEvent","eventName","callback","listener","useBubblaVWidgetState","isOpen"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bubblav/ai-chatbot-vue",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Vue component for embedding the BubblaV AI chatbot widget",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "vite build",
|
|
22
|
+
"build:watch": "vite build --watch",
|
|
23
|
+
"dev": "vite build --watch",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"type-check": "vue-tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"bubblav",
|
|
29
|
+
"chatbot",
|
|
30
|
+
"ai",
|
|
31
|
+
"widget",
|
|
32
|
+
"vue",
|
|
33
|
+
"vue3",
|
|
34
|
+
"vue-component"
|
|
35
|
+
],
|
|
36
|
+
"author": "BubblaV",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/tonnguyen/botcanchat.git",
|
|
41
|
+
"directory": "packages/ai-chatbot-vue"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"vue": "^3.3.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
48
|
+
"@vue/compiler-sfc": "^3.5.13",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vite": "^6.0.11",
|
|
51
|
+
"vite-plugin-dts": "^4.5.0",
|
|
52
|
+
"vue": "^3.5.13",
|
|
53
|
+
"vue-tsc": "^2.2.0"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18.0.0"
|
|
57
|
+
}
|
|
58
|
+
}
|