@ozwell/react 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 +207 -0
- package/dist/index.cjs +394 -0
- package/dist/index.d.cts +282 -0
- package/dist/index.d.ts +282 -0
- package/dist/index.js +366 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# @ozwell/react
|
|
2
|
+
|
|
3
|
+
React components for Ozwell chat widget.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @ozwell/react
|
|
9
|
+
# or
|
|
10
|
+
yarn add @ozwell/react
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @ozwell/react
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { OzwellChat } from '@ozwell/react';
|
|
19
|
+
|
|
20
|
+
function App() {
|
|
21
|
+
return (
|
|
22
|
+
<OzwellChat
|
|
23
|
+
apiKey="agnt_key-your-agent-key"
|
|
24
|
+
tools={[
|
|
25
|
+
{
|
|
26
|
+
type: 'function',
|
|
27
|
+
function: {
|
|
28
|
+
name: 'get_user_info',
|
|
29
|
+
description: 'Get current user information',
|
|
30
|
+
parameters: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {},
|
|
33
|
+
required: []
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This package loads the hosted iframe widget and authenticates with an agent key.
|
|
44
|
+
Sign in to [Ozwell Manager](https://ozwellconsole.os.mieweb.org) to create an agent.
|
|
45
|
+
For another deployment, set `widgetUrl` to its `/widget/frame/` URL.
|
|
46
|
+
See the [React integration guide](https://docs.ozwell.ai/frontend/react) for credential
|
|
47
|
+
safety, a complete Vite + MIE UI Hello World example, and troubleshooting.
|
|
48
|
+
|
|
49
|
+
## Feature Support
|
|
50
|
+
|
|
51
|
+
### Currently Available
|
|
52
|
+
- Agent-key authentication (`apiKey` prop)
|
|
53
|
+
- Custom endpoints (`endpoint` prop)
|
|
54
|
+
- Model selection (`model` prop)
|
|
55
|
+
- MCP tool/function calling (`tools` prop, `onToolCall` callback)
|
|
56
|
+
- System prompts (`system` prop)
|
|
57
|
+
- Welcome messages (`welcomeMessage` prop)
|
|
58
|
+
- Debug mode (`debug` prop)
|
|
59
|
+
- OpenAI API compatibility (`openaiApiKey` prop)
|
|
60
|
+
- Custom headers (`headers` prop)
|
|
61
|
+
- Auto-open on AI reply (`autoOpenOnReply` prop)
|
|
62
|
+
- Lifecycle callbacks (`onReady`, `onOpen`, `onClose`)
|
|
63
|
+
- Error callback for loader and mount errors (`onError` - partial)
|
|
64
|
+
|
|
65
|
+
### Coming Soon
|
|
66
|
+
- Agent management (`agentId` prop)
|
|
67
|
+
- Theme customization (`theme`, `primaryColor` props)
|
|
68
|
+
- Position control (`position` prop)
|
|
69
|
+
- Auto-open behavior (`autoOpen` prop)
|
|
70
|
+
- User share callbacks (`onUserShare` - requires widget support)
|
|
71
|
+
- Full error callback support (`onError` - additional error types)
|
|
72
|
+
|
|
73
|
+
## Using Tools (MCP Function Calling)
|
|
74
|
+
|
|
75
|
+
The React package supports MCP (Model Context Protocol) tools for function calling. Here's how to define and handle tools:
|
|
76
|
+
|
|
77
|
+
### Defining Tools
|
|
78
|
+
|
|
79
|
+
Tools are defined using the OpenAI-compatible format and passed via the `tools` prop:
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { OzwellChat } from '@ozwell/react';
|
|
83
|
+
import type { OzwellTool } from '@ozwell/react';
|
|
84
|
+
|
|
85
|
+
const tools: OzwellTool[] = [
|
|
86
|
+
{
|
|
87
|
+
type: 'function',
|
|
88
|
+
function: {
|
|
89
|
+
name: 'get_form_data',
|
|
90
|
+
description: 'Retrieves current user information including name, address, and zip code',
|
|
91
|
+
parameters: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {},
|
|
94
|
+
required: []
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'function',
|
|
100
|
+
function: {
|
|
101
|
+
name: 'update_form_data',
|
|
102
|
+
description: 'Updates user profile information',
|
|
103
|
+
parameters: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
properties: {
|
|
106
|
+
name: { type: 'string', description: 'The new name value' },
|
|
107
|
+
address: { type: 'string', description: 'The new address value' },
|
|
108
|
+
zipCode: { type: 'string', description: 'The new zip code value' }
|
|
109
|
+
},
|
|
110
|
+
required: []
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
function App() {
|
|
117
|
+
return (
|
|
118
|
+
<OzwellChat
|
|
119
|
+
endpoint="http://localhost:3000/v1/chat/completions"
|
|
120
|
+
tools={tools}
|
|
121
|
+
debug={true}
|
|
122
|
+
system="You are a helpful assistant for managing user profile information."
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Handling Tool Calls
|
|
129
|
+
|
|
130
|
+
Use the `onToolCall` prop to handle tool calls with a simple callback:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import React, { useState } from 'react';
|
|
134
|
+
import { OzwellChat } from '@ozwell/react';
|
|
135
|
+
|
|
136
|
+
function App() {
|
|
137
|
+
const [formData, setFormData] = useState({
|
|
138
|
+
name: '',
|
|
139
|
+
address: '',
|
|
140
|
+
zipCode: ''
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Define your tool handlers
|
|
144
|
+
const toolHandlers: Record<string, (args: Record<string, unknown>) => unknown> = {
|
|
145
|
+
get_form_data: () => formData,
|
|
146
|
+
|
|
147
|
+
update_form_data: (args) => {
|
|
148
|
+
setFormData(prev => ({
|
|
149
|
+
name: (args.name as string) ?? prev.name,
|
|
150
|
+
address: (args.address as string) ?? prev.address,
|
|
151
|
+
zipCode: (args.zipCode as string) ?? prev.zipCode
|
|
152
|
+
}));
|
|
153
|
+
return { success: true, updated: args };
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<OzwellChat
|
|
159
|
+
endpoint="/v1/chat/completions"
|
|
160
|
+
tools={tools}
|
|
161
|
+
onToolCall={(tool, args, sendResult) => {
|
|
162
|
+
const handler = toolHandlers[tool];
|
|
163
|
+
if (handler) {
|
|
164
|
+
const result = handler(args);
|
|
165
|
+
sendResult(result);
|
|
166
|
+
} else {
|
|
167
|
+
sendResult({ error: `Unknown tool: ${tool}` });
|
|
168
|
+
}
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The `onToolCall` callback receives:
|
|
176
|
+
|
|
177
|
+
- `tool` - The name of the tool being called
|
|
178
|
+
- `args` - The arguments passed to the tool
|
|
179
|
+
- `sendResult` - A function to send the result back to the AI
|
|
180
|
+
|
|
181
|
+
This handles all the postMessage complexity internally, so you just focus on your tool logic.
|
|
182
|
+
|
|
183
|
+
## Documentation
|
|
184
|
+
|
|
185
|
+
For full documentation, see [React Integration Guide](../../docs/frontend/react.md).
|
|
186
|
+
|
|
187
|
+
> **Note:** The documentation describes the planned API including `apiKey` and `agentId` props. The current implementation supports `endpoint` and `tools` configuration matching the vanilla JS widget.
|
|
188
|
+
|
|
189
|
+
## Development
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Install dependencies
|
|
193
|
+
npm install
|
|
194
|
+
|
|
195
|
+
# Build the package
|
|
196
|
+
npm run build
|
|
197
|
+
|
|
198
|
+
# Development mode (watch)
|
|
199
|
+
npm run dev
|
|
200
|
+
|
|
201
|
+
# Type checking
|
|
202
|
+
npm run lint
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
Apache-2.0
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
OzwellChat: () => OzwellChat,
|
|
24
|
+
default: () => OzwellChat_default,
|
|
25
|
+
useOzwell: () => useOzwell
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/OzwellChat.tsx
|
|
30
|
+
var import_react = require("react");
|
|
31
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
function OzwellChat(props) {
|
|
33
|
+
const {
|
|
34
|
+
// Layout
|
|
35
|
+
width = 360,
|
|
36
|
+
height = 420,
|
|
37
|
+
containerId,
|
|
38
|
+
defaultUI = true,
|
|
39
|
+
// Configuration
|
|
40
|
+
endpoint,
|
|
41
|
+
model,
|
|
42
|
+
system,
|
|
43
|
+
welcomeMessage,
|
|
44
|
+
placeholder,
|
|
45
|
+
title,
|
|
46
|
+
tools,
|
|
47
|
+
debug,
|
|
48
|
+
openaiApiKey,
|
|
49
|
+
headers,
|
|
50
|
+
widgetUrl,
|
|
51
|
+
autoOpenOnReply,
|
|
52
|
+
apiKey,
|
|
53
|
+
// Future props (not yet implemented in vanilla widget)
|
|
54
|
+
// These are accepted but ignored until backend support is added
|
|
55
|
+
agentId,
|
|
56
|
+
theme: _theme,
|
|
57
|
+
// Prefix with _ to indicate intentionally unused
|
|
58
|
+
position: _position,
|
|
59
|
+
primaryColor: _primaryColor,
|
|
60
|
+
autoOpen: _autoOpen,
|
|
61
|
+
// Callbacks
|
|
62
|
+
onReady,
|
|
63
|
+
onOpen,
|
|
64
|
+
onClose,
|
|
65
|
+
onToolCall,
|
|
66
|
+
onUserShare,
|
|
67
|
+
onError,
|
|
68
|
+
// React-specific
|
|
69
|
+
children
|
|
70
|
+
} = props;
|
|
71
|
+
const [scriptStatus, setScriptStatus] = (0, import_react.useState)("idle");
|
|
72
|
+
const [isWidgetReady, setIsWidgetReady] = (0, import_react.useState)(false);
|
|
73
|
+
const instanceContainerId = (0, import_react.useRef)(
|
|
74
|
+
containerId || `ozwell-container-${Math.random().toString(36).slice(2, 11)}`
|
|
75
|
+
);
|
|
76
|
+
(0, import_react.useEffect)(() => {
|
|
77
|
+
if (window.OzwellChat) {
|
|
78
|
+
setScriptStatus("ready");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const handleLoad = () => setScriptStatus("ready");
|
|
82
|
+
const handleError = () => {
|
|
83
|
+
setScriptStatus("error");
|
|
84
|
+
console.error("[OzwellChat] Failed to load ozwell-loader.js");
|
|
85
|
+
onError?.({
|
|
86
|
+
code: "SCRIPT_LOAD_ERROR",
|
|
87
|
+
message: "Failed to load Ozwell widget. Check the widget host URL and network access."
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const existingScript = document.querySelector(
|
|
91
|
+
'script[data-ozwell-loader], script[src*="ozwell-loader.js"]'
|
|
92
|
+
);
|
|
93
|
+
if (existingScript) {
|
|
94
|
+
setScriptStatus("loading");
|
|
95
|
+
existingScript.addEventListener("load", handleLoad);
|
|
96
|
+
existingScript.addEventListener("error", handleError);
|
|
97
|
+
return () => {
|
|
98
|
+
existingScript.removeEventListener("load", handleLoad);
|
|
99
|
+
existingScript.removeEventListener("error", handleError);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
window.OzwellChatConfig = {
|
|
103
|
+
...window.OzwellChatConfig,
|
|
104
|
+
autoMount: false
|
|
105
|
+
};
|
|
106
|
+
setScriptStatus("loading");
|
|
107
|
+
const scriptSrc = new URL(
|
|
108
|
+
"/widget",
|
|
109
|
+
widgetUrl ? new URL(widgetUrl, window.location.href) : "https://ozwellapi.os.mieweb.org"
|
|
110
|
+
).href;
|
|
111
|
+
const script = document.createElement("script");
|
|
112
|
+
script.dataset.ozwellLoader = "true";
|
|
113
|
+
script.src = scriptSrc;
|
|
114
|
+
script.async = true;
|
|
115
|
+
script.addEventListener("load", handleLoad);
|
|
116
|
+
script.addEventListener("error", handleError);
|
|
117
|
+
document.head.appendChild(script);
|
|
118
|
+
return () => {
|
|
119
|
+
script.removeEventListener("load", handleLoad);
|
|
120
|
+
script.removeEventListener("error", handleError);
|
|
121
|
+
};
|
|
122
|
+
}, [widgetUrl]);
|
|
123
|
+
(0, import_react.useEffect)(() => {
|
|
124
|
+
if (scriptStatus !== "ready" || !window.OzwellChat) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const config = {
|
|
128
|
+
// Core config
|
|
129
|
+
endpoint,
|
|
130
|
+
model,
|
|
131
|
+
system,
|
|
132
|
+
welcomeMessage,
|
|
133
|
+
placeholder,
|
|
134
|
+
title,
|
|
135
|
+
tools,
|
|
136
|
+
debug,
|
|
137
|
+
openaiApiKey,
|
|
138
|
+
headers,
|
|
139
|
+
widgetUrl,
|
|
140
|
+
apiKey,
|
|
141
|
+
// Layout config
|
|
142
|
+
defaultUI,
|
|
143
|
+
autoMount: false,
|
|
144
|
+
// Prevent auto-mount, we'll mount manually
|
|
145
|
+
autoOpenOnReply,
|
|
146
|
+
agentId
|
|
147
|
+
};
|
|
148
|
+
if (!defaultUI) {
|
|
149
|
+
config.containerId = instanceContainerId.current;
|
|
150
|
+
}
|
|
151
|
+
const cleanConfig = Object.fromEntries(
|
|
152
|
+
Object.entries(config).filter(([, value]) => value !== void 0)
|
|
153
|
+
);
|
|
154
|
+
window.OzwellChatConfig = cleanConfig;
|
|
155
|
+
try {
|
|
156
|
+
const mountOptions = {};
|
|
157
|
+
if (!defaultUI) {
|
|
158
|
+
mountOptions.containerId = instanceContainerId.current;
|
|
159
|
+
}
|
|
160
|
+
if (width) {
|
|
161
|
+
mountOptions.width = typeof width === "string" ? parseInt(width) : width;
|
|
162
|
+
}
|
|
163
|
+
if (height) {
|
|
164
|
+
mountOptions.height = typeof height === "string" ? parseInt(height) : height;
|
|
165
|
+
}
|
|
166
|
+
if (window.OzwellChat.iframe) {
|
|
167
|
+
window.OzwellChat.configure(cleanConfig);
|
|
168
|
+
} else {
|
|
169
|
+
window.OzwellChat.mount(mountOptions);
|
|
170
|
+
}
|
|
171
|
+
window.OzwellChat.ready().then(() => {
|
|
172
|
+
setIsWidgetReady(true);
|
|
173
|
+
onReady?.();
|
|
174
|
+
});
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error("[OzwellChat] Failed to mount widget:", error);
|
|
177
|
+
onError?.({
|
|
178
|
+
code: "MOUNT_ERROR",
|
|
179
|
+
message: "Failed to mount Ozwell widget",
|
|
180
|
+
details: error
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}, [
|
|
184
|
+
scriptStatus,
|
|
185
|
+
endpoint,
|
|
186
|
+
model,
|
|
187
|
+
system,
|
|
188
|
+
welcomeMessage,
|
|
189
|
+
placeholder,
|
|
190
|
+
title,
|
|
191
|
+
tools,
|
|
192
|
+
debug,
|
|
193
|
+
openaiApiKey,
|
|
194
|
+
headers,
|
|
195
|
+
widgetUrl,
|
|
196
|
+
defaultUI,
|
|
197
|
+
autoOpenOnReply,
|
|
198
|
+
width,
|
|
199
|
+
height,
|
|
200
|
+
apiKey,
|
|
201
|
+
agentId,
|
|
202
|
+
onReady,
|
|
203
|
+
onError
|
|
204
|
+
]);
|
|
205
|
+
(0, import_react.useEffect)(() => {
|
|
206
|
+
if (!isWidgetReady) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const handleMessage = (event) => {
|
|
210
|
+
const iframe = window.OzwellChat?.iframe;
|
|
211
|
+
if (!iframe || event.source !== iframe.contentWindow || event.origin !== new URL(iframe.src, window.location.href).origin) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const data = event.data;
|
|
215
|
+
if (!data || typeof data !== "object" || data.source !== "ozwell-chat-widget") {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
switch (data.type) {
|
|
219
|
+
case "closed":
|
|
220
|
+
onClose?.();
|
|
221
|
+
break;
|
|
222
|
+
case "opened":
|
|
223
|
+
onOpen?.();
|
|
224
|
+
break;
|
|
225
|
+
case "user-share":
|
|
226
|
+
onUserShare?.(data.payload);
|
|
227
|
+
break;
|
|
228
|
+
case "error":
|
|
229
|
+
onError?.(data.payload);
|
|
230
|
+
break;
|
|
231
|
+
case "tool_call":
|
|
232
|
+
if (onToolCall) {
|
|
233
|
+
const { tool, tool_call_id, payload: args } = data;
|
|
234
|
+
const sendResult = (result) => {
|
|
235
|
+
const iframe2 = window.OzwellChat?.iframe;
|
|
236
|
+
if (iframe2?.contentWindow) {
|
|
237
|
+
const targetOrigin = iframe2.src ? new URL(iframe2.src).origin : "*";
|
|
238
|
+
iframe2.contentWindow.postMessage(
|
|
239
|
+
{
|
|
240
|
+
source: "ozwell-chat-parent",
|
|
241
|
+
type: "tool_result",
|
|
242
|
+
tool_call_id,
|
|
243
|
+
result
|
|
244
|
+
},
|
|
245
|
+
targetOrigin
|
|
246
|
+
);
|
|
247
|
+
} else {
|
|
248
|
+
console.error("[OzwellChat] Could not find widget iframe to send tool result");
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
onToolCall(tool, args || {}, sendResult);
|
|
252
|
+
}
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
window.addEventListener("message", handleMessage);
|
|
257
|
+
return () => {
|
|
258
|
+
window.removeEventListener("message", handleMessage);
|
|
259
|
+
};
|
|
260
|
+
}, [isWidgetReady, onClose, onOpen, onUserShare, onError, onToolCall]);
|
|
261
|
+
(0, import_react.useEffect)(() => {
|
|
262
|
+
const handleToolCall = (event) => {
|
|
263
|
+
const { name, arguments: args, respond, error } = event.detail;
|
|
264
|
+
if (!onToolCall) {
|
|
265
|
+
error(`No handler configured for tool "${name}".`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
const outcome = onToolCall(name, args || {}, respond);
|
|
270
|
+
Promise.resolve(outcome).catch(() => error(`Tool "${name}" failed.`));
|
|
271
|
+
} catch {
|
|
272
|
+
error(`Tool "${name}" failed.`);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
document.addEventListener("ozwell-tool-call", handleToolCall);
|
|
276
|
+
return () => document.removeEventListener("ozwell-tool-call", handleToolCall);
|
|
277
|
+
}, [onToolCall]);
|
|
278
|
+
if (defaultUI) {
|
|
279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
280
|
+
}
|
|
281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
282
|
+
"div",
|
|
283
|
+
{
|
|
284
|
+
id: instanceContainerId.current,
|
|
285
|
+
style: {
|
|
286
|
+
width: typeof width === "number" ? `${width}px` : width,
|
|
287
|
+
height: typeof height === "number" ? `${height}px` : height
|
|
288
|
+
},
|
|
289
|
+
children
|
|
290
|
+
}
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
var OzwellChat_default = OzwellChat;
|
|
294
|
+
|
|
295
|
+
// src/useOzwell.ts
|
|
296
|
+
var import_react2 = require("react");
|
|
297
|
+
function useOzwell() {
|
|
298
|
+
const [isReady, setIsReady] = (0, import_react2.useState)(false);
|
|
299
|
+
const [isOpen, setIsOpen] = (0, import_react2.useState)(false);
|
|
300
|
+
const [hasUnread, setHasUnread] = (0, import_react2.useState)(false);
|
|
301
|
+
const [iframe, setIframe] = (0, import_react2.useState)(null);
|
|
302
|
+
(0, import_react2.useEffect)(() => {
|
|
303
|
+
if (window.OzwellChat) {
|
|
304
|
+
setIsReady(true);
|
|
305
|
+
setIframe(window.OzwellChat.iframe);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const handleReady = () => {
|
|
309
|
+
setIsReady(true);
|
|
310
|
+
setIframe(window.OzwellChat?.iframe || null);
|
|
311
|
+
};
|
|
312
|
+
document.addEventListener("ozwell-chat-ready", handleReady);
|
|
313
|
+
return () => {
|
|
314
|
+
document.removeEventListener("ozwell-chat-ready", handleReady);
|
|
315
|
+
};
|
|
316
|
+
}, []);
|
|
317
|
+
(0, import_react2.useEffect)(() => {
|
|
318
|
+
if (!isReady) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const handleMessage = (event) => {
|
|
322
|
+
const widgetIframe = window.OzwellChat?.iframe;
|
|
323
|
+
if (widgetIframe && event.source !== widgetIframe.contentWindow) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const data = event.data;
|
|
327
|
+
if (!data || typeof data !== "object" || data.source !== "ozwell-chat-widget") {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (data.type === "opened") {
|
|
331
|
+
setIsOpen(true);
|
|
332
|
+
setHasUnread(false);
|
|
333
|
+
} else if (data.type === "closed") {
|
|
334
|
+
setIsOpen(false);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
const handleUnread = () => {
|
|
338
|
+
setHasUnread(true);
|
|
339
|
+
};
|
|
340
|
+
window.addEventListener("message", handleMessage);
|
|
341
|
+
document.addEventListener("ozwell-chat-unread", handleUnread);
|
|
342
|
+
return () => {
|
|
343
|
+
window.removeEventListener("message", handleMessage);
|
|
344
|
+
document.removeEventListener("ozwell-chat-unread", handleUnread);
|
|
345
|
+
};
|
|
346
|
+
}, [isReady]);
|
|
347
|
+
const open = (0, import_react2.useCallback)(() => {
|
|
348
|
+
if (!isReady) {
|
|
349
|
+
console.warn("[useOzwell] Widget not ready yet");
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
window.OzwellChat?.open?.();
|
|
353
|
+
}, [isReady]);
|
|
354
|
+
const close = (0, import_react2.useCallback)(() => {
|
|
355
|
+
if (!isReady) {
|
|
356
|
+
console.warn("[useOzwell] Widget not ready yet");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
window.OzwellChat?.close?.();
|
|
360
|
+
}, [isReady]);
|
|
361
|
+
const toggle = (0, import_react2.useCallback)(() => {
|
|
362
|
+
if (isOpen) {
|
|
363
|
+
close();
|
|
364
|
+
} else {
|
|
365
|
+
open();
|
|
366
|
+
}
|
|
367
|
+
}, [isOpen, open, close]);
|
|
368
|
+
const sendMessage = (0, import_react2.useCallback)((content) => {
|
|
369
|
+
if (!isReady) {
|
|
370
|
+
console.warn("[useOzwell] Widget not ready yet");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (!content || typeof content !== "string") {
|
|
374
|
+
console.warn("[useOzwell] sendMessage requires a non-empty string");
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
console.warn("[useOzwell] sendMessage() not yet implemented in vanilla widget");
|
|
378
|
+
}, [isReady]);
|
|
379
|
+
return {
|
|
380
|
+
isReady,
|
|
381
|
+
isOpen,
|
|
382
|
+
hasUnread,
|
|
383
|
+
open,
|
|
384
|
+
close,
|
|
385
|
+
toggle,
|
|
386
|
+
sendMessage,
|
|
387
|
+
iframe
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
391
|
+
0 && (module.exports = {
|
|
392
|
+
OzwellChat,
|
|
393
|
+
useOzwell
|
|
394
|
+
});
|