@emblemvault/hustle-react 1.2.0 → 1.4.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 +56 -0
- package/dist/browser/hustle-react.js +450 -5
- package/dist/browser/hustle-react.js.map +1 -1
- package/dist/components/index.cjs +450 -4
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +148 -2
- package/dist/components/index.d.ts +148 -2
- package/dist/components/index.js +450 -5
- package/dist/components/index.js.map +1 -1
- package/dist/index.cjs +450 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +450 -5
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.cjs +1 -1
- package/dist/plugins/index.cjs.map +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -111,6 +111,62 @@ Complete chat UI component.
|
|
|
111
111
|
/>
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
### HustleChatWidget
|
|
115
|
+
|
|
116
|
+
Floating chat widget for site-wide chatbot integration. Perfect for customer support or AI assistants that persist across page navigations.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import { HustleProvider, HustleChatWidget } from '@emblemvault/hustle-react';
|
|
120
|
+
|
|
121
|
+
// Add to your Next.js layout for site-wide availability
|
|
122
|
+
export default function RootLayout({ children }) {
|
|
123
|
+
return (
|
|
124
|
+
<html>
|
|
125
|
+
<body>
|
|
126
|
+
<HustleProvider>
|
|
127
|
+
{children}
|
|
128
|
+
<HustleChatWidget
|
|
129
|
+
config={{
|
|
130
|
+
position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
|
|
131
|
+
size: 'md', // 'sm' | 'md' | 'lg' | 'xl' | 'full'
|
|
132
|
+
title: 'Support', // Header title
|
|
133
|
+
defaultOpen: false, // Start open?
|
|
134
|
+
offset: { x: 24, y: 24 }, // Edge offset in pixels
|
|
135
|
+
storageKey: 'chat-open', // localStorage key for persistence (false to disable)
|
|
136
|
+
showBadge: true, // Show notification badge
|
|
137
|
+
badgeContent: 3, // Badge content
|
|
138
|
+
onOpen: () => {}, // Called when widget opens
|
|
139
|
+
onClose: () => {}, // Called when widget closes
|
|
140
|
+
}}
|
|
141
|
+
placeholder="How can we help?"
|
|
142
|
+
showSettings
|
|
143
|
+
/>
|
|
144
|
+
</HustleProvider>
|
|
145
|
+
</body>
|
|
146
|
+
</html>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Widget Configuration
|
|
152
|
+
|
|
153
|
+
| Option | Type | Default | Description |
|
|
154
|
+
|--------|------|---------|-------------|
|
|
155
|
+
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | `'bottom-right'` | Screen position |
|
|
156
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | `'md'` | Chat panel size |
|
|
157
|
+
| `title` | `string` | `'Chat'` | Panel header title |
|
|
158
|
+
| `defaultOpen` | `boolean` | `false` | Whether widget starts open |
|
|
159
|
+
| `offset` | `{ x: number, y: number }` | `{ x: 24, y: 24 }` | Offset from screen edge |
|
|
160
|
+
| `storageKey` | `string \| false` | `'hustle-widget-open'` | Key for persisting state |
|
|
161
|
+
| `showBadge` | `boolean` | `false` | Show notification badge |
|
|
162
|
+
| `badgeContent` | `string \| number` | - | Badge content |
|
|
163
|
+
| `launcherIcon` | `React.ReactNode` | Chat bubble | Custom launcher icon |
|
|
164
|
+
| `launcherStyle` | `React.CSSProperties` | - | Custom launcher styles |
|
|
165
|
+
| `panelStyle` | `React.CSSProperties` | - | Custom panel styles |
|
|
166
|
+
| `zIndex` | `number` | `9999` | Widget z-index |
|
|
167
|
+
| `onOpen` | `() => void` | - | Called when widget opens |
|
|
168
|
+
| `onClose` | `() => void` | - | Called when widget closes |
|
|
169
|
+
|
|
114
170
|
## Plugins
|
|
115
171
|
|
|
116
172
|
Extend the AI with custom tools.
|
|
@@ -3547,7 +3547,7 @@ var screenshotExecutor = async (args2) => {
|
|
|
3547
3547
|
if (!window.html2canvas) {
|
|
3548
3548
|
await new Promise((resolve, reject) => {
|
|
3549
3549
|
const script = document.createElement("script");
|
|
3550
|
-
script.src = "https://
|
|
3550
|
+
script.src = "https://cdn.jsdelivr.net/npm/html2canvas-pro@1.5.0/dist/html2canvas-pro.min.js";
|
|
3551
3551
|
script.onload = () => resolve();
|
|
3552
3552
|
script.onerror = () => reject(new Error("Failed to load html2canvas"));
|
|
3553
3553
|
document.head.appendChild(script);
|
|
@@ -4605,6 +4605,14 @@ var animations = `
|
|
|
4605
4605
|
text-shadow: 0 0 8px ${defaults.colors.accentPrimary};
|
|
4606
4606
|
}
|
|
4607
4607
|
}
|
|
4608
|
+
/* Hide scrollbar while maintaining scroll functionality */
|
|
4609
|
+
.hustle-hide-scrollbar {
|
|
4610
|
+
-ms-overflow-style: none; /* IE and Edge */
|
|
4611
|
+
scrollbar-width: none; /* Firefox */
|
|
4612
|
+
}
|
|
4613
|
+
.hustle-hide-scrollbar::-webkit-scrollbar {
|
|
4614
|
+
display: none; /* Chrome, Safari, Opera */
|
|
4615
|
+
}
|
|
4608
4616
|
`;
|
|
4609
4617
|
|
|
4610
4618
|
// ../../node_modules/marked/lib/marked.esm.js
|
|
@@ -14862,7 +14870,11 @@ var styles = {
|
|
|
14862
14870
|
border: "none",
|
|
14863
14871
|
borderRadius: 0,
|
|
14864
14872
|
color: tokens.colors.textTertiary,
|
|
14865
|
-
flexShrink: 0
|
|
14873
|
+
flexShrink: 0,
|
|
14874
|
+
display: "flex",
|
|
14875
|
+
alignItems: "center",
|
|
14876
|
+
justifyContent: "center",
|
|
14877
|
+
cursor: "pointer"
|
|
14866
14878
|
},
|
|
14867
14879
|
inputWrapper: {
|
|
14868
14880
|
flex: 1
|
|
@@ -15071,6 +15083,7 @@ function HustleChat({
|
|
|
15071
15083
|
placeholder = "Type a message...",
|
|
15072
15084
|
showSettings = false,
|
|
15073
15085
|
showDebug = false,
|
|
15086
|
+
hideHeader = false,
|
|
15074
15087
|
initialSystemPrompt = "",
|
|
15075
15088
|
onMessage,
|
|
15076
15089
|
onToolCall,
|
|
@@ -15083,6 +15096,7 @@ function HustleChat({
|
|
|
15083
15096
|
isLoading,
|
|
15084
15097
|
error: error2,
|
|
15085
15098
|
models,
|
|
15099
|
+
client,
|
|
15086
15100
|
chatStream,
|
|
15087
15101
|
uploadFile,
|
|
15088
15102
|
selectedModel,
|
|
@@ -15109,6 +15123,7 @@ function HustleChat({
|
|
|
15109
15123
|
const [showSettingsPanel, setShowSettingsPanel] = useState(false);
|
|
15110
15124
|
const messagesEndRef = useRef(null);
|
|
15111
15125
|
const fileInputRef = useRef(null);
|
|
15126
|
+
const messagesRef = useRef(messages);
|
|
15112
15127
|
useEffect(() => {
|
|
15113
15128
|
if (initialSystemPrompt && !systemPrompt) {
|
|
15114
15129
|
setSystemPrompt(initialSystemPrompt);
|
|
@@ -15117,6 +15132,9 @@ function HustleChat({
|
|
|
15117
15132
|
useEffect(() => {
|
|
15118
15133
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
15119
15134
|
}, [messages]);
|
|
15135
|
+
useEffect(() => {
|
|
15136
|
+
messagesRef.current = messages;
|
|
15137
|
+
}, [messages]);
|
|
15120
15138
|
const handleFileSelect = useCallback(
|
|
15121
15139
|
async (e) => {
|
|
15122
15140
|
const files = e.target.files;
|
|
@@ -15138,6 +15156,96 @@ function HustleChat({
|
|
|
15138
15156
|
const removeAttachment = useCallback((index) => {
|
|
15139
15157
|
setAttachments((prev) => prev.filter((_2, i) => i !== index));
|
|
15140
15158
|
}, []);
|
|
15159
|
+
const sendContinue = useCallback(async () => {
|
|
15160
|
+
if (isStreaming || !isReady) return;
|
|
15161
|
+
console.log("[AUTO_CONTINUE] Sending continue message...");
|
|
15162
|
+
const userMessage = {
|
|
15163
|
+
id: generateId(),
|
|
15164
|
+
role: "user",
|
|
15165
|
+
content: "continue"
|
|
15166
|
+
};
|
|
15167
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
15168
|
+
onMessage?.(userMessage);
|
|
15169
|
+
const assistantMessage = {
|
|
15170
|
+
id: generateId(),
|
|
15171
|
+
role: "assistant",
|
|
15172
|
+
content: "",
|
|
15173
|
+
isStreaming: true,
|
|
15174
|
+
toolCalls: []
|
|
15175
|
+
};
|
|
15176
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
15177
|
+
setIsStreaming(true);
|
|
15178
|
+
setCurrentToolCalls([]);
|
|
15179
|
+
try {
|
|
15180
|
+
const chatMessages = messagesRef.current.filter((m2) => !m2.isStreaming).map((m2) => ({ role: m2.role, content: m2.content }));
|
|
15181
|
+
chatMessages.push({ role: "user", content: "continue" });
|
|
15182
|
+
const stream = chatStream({
|
|
15183
|
+
messages: chatMessages,
|
|
15184
|
+
processChunks: true
|
|
15185
|
+
});
|
|
15186
|
+
let fullContent = "";
|
|
15187
|
+
const toolCallsAccumulated = [];
|
|
15188
|
+
for await (const chunk of stream) {
|
|
15189
|
+
if (chunk.type === "text") {
|
|
15190
|
+
fullContent += chunk.value;
|
|
15191
|
+
setMessages(
|
|
15192
|
+
(prev) => prev.map(
|
|
15193
|
+
(m2) => m2.id === assistantMessage.id ? { ...m2, content: fullContent } : m2
|
|
15194
|
+
)
|
|
15195
|
+
);
|
|
15196
|
+
} else if (chunk.type === "tool_call") {
|
|
15197
|
+
const toolCall = chunk.value;
|
|
15198
|
+
toolCallsAccumulated.push(toolCall);
|
|
15199
|
+
setCurrentToolCalls([...toolCallsAccumulated]);
|
|
15200
|
+
setMessages(
|
|
15201
|
+
(prev) => prev.map(
|
|
15202
|
+
(m2) => m2.id === assistantMessage.id ? { ...m2, toolCalls: [...toolCallsAccumulated] } : m2
|
|
15203
|
+
)
|
|
15204
|
+
);
|
|
15205
|
+
onToolCall?.(toolCall);
|
|
15206
|
+
} else if (chunk.type === "error") {
|
|
15207
|
+
console.error("Stream error:", chunk.value);
|
|
15208
|
+
}
|
|
15209
|
+
}
|
|
15210
|
+
const processedResponse = await stream.response;
|
|
15211
|
+
const finalContent = processedResponse?.content || fullContent || "(No response)";
|
|
15212
|
+
setMessages(
|
|
15213
|
+
(prev) => prev.map(
|
|
15214
|
+
(m2) => m2.id === assistantMessage.id ? { ...m2, isStreaming: false, content: finalContent } : m2
|
|
15215
|
+
)
|
|
15216
|
+
);
|
|
15217
|
+
onResponse?.(finalContent);
|
|
15218
|
+
} catch (err) {
|
|
15219
|
+
console.error("Continue error:", err);
|
|
15220
|
+
setMessages(
|
|
15221
|
+
(prev) => prev.map(
|
|
15222
|
+
(m2) => m2.id === assistantMessage.id ? { ...m2, isStreaming: false, content: `Error: ${err instanceof Error ? err.message : "Unknown error"}` } : m2
|
|
15223
|
+
)
|
|
15224
|
+
);
|
|
15225
|
+
} finally {
|
|
15226
|
+
setIsStreaming(false);
|
|
15227
|
+
setCurrentToolCalls([]);
|
|
15228
|
+
}
|
|
15229
|
+
}, [isStreaming, isReady, chatStream, onMessage, onToolCall, onResponse]);
|
|
15230
|
+
useEffect(() => {
|
|
15231
|
+
if (!client) return;
|
|
15232
|
+
const unsubMaxTools = client.on("max_tools_reached", (event) => {
|
|
15233
|
+
console.log(`[AUTO_CONTINUE] Max tools reached (${event.toolsExecuted}/${event.maxSteps}), auto-continuing...`);
|
|
15234
|
+
setTimeout(() => {
|
|
15235
|
+
sendContinue();
|
|
15236
|
+
}, 100);
|
|
15237
|
+
});
|
|
15238
|
+
const unsubTimeout = client.on("timeout", (event) => {
|
|
15239
|
+
console.log(`[AUTO_CONTINUE] Timeout: ${event.message}, auto-continuing...`);
|
|
15240
|
+
setTimeout(() => {
|
|
15241
|
+
sendContinue();
|
|
15242
|
+
}, 100);
|
|
15243
|
+
});
|
|
15244
|
+
return () => {
|
|
15245
|
+
unsubMaxTools();
|
|
15246
|
+
unsubTimeout();
|
|
15247
|
+
};
|
|
15248
|
+
}, [client, sendContinue]);
|
|
15141
15249
|
const sendMessage = useCallback(async () => {
|
|
15142
15250
|
const content = inputValue.trim();
|
|
15143
15251
|
if (!content || isStreaming || !isReady) return;
|
|
@@ -15234,7 +15342,7 @@ function HustleChat({
|
|
|
15234
15342
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15235
15343
|
/* @__PURE__ */ jsx("style", { children: animations }),
|
|
15236
15344
|
/* @__PURE__ */ jsxs("div", { className, style: styles.container, children: [
|
|
15237
|
-
/* @__PURE__ */ jsxs("div", { style: styles.header, children: [
|
|
15345
|
+
!hideHeader && /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
|
|
15238
15346
|
/* @__PURE__ */ jsx("h2", { style: styles.headerTitle, children: "Chat" }),
|
|
15239
15347
|
/* @__PURE__ */ jsxs("div", { style: styles.headerActions, children: [
|
|
15240
15348
|
selectedModel && /* @__PURE__ */ jsx("span", { style: { fontSize: tokens.typography.fontSizeSm, color: tokens.colors.textSecondary }, children: selectedModel.split("/").pop() }),
|
|
@@ -15409,7 +15517,7 @@ function HustleChat({
|
|
|
15409
15517
|
] })
|
|
15410
15518
|
] })
|
|
15411
15519
|
] }) }),
|
|
15412
|
-
/* @__PURE__ */ jsxs("div", { style: styles.messagesArea, children: [
|
|
15520
|
+
/* @__PURE__ */ jsxs("div", { className: "hustle-hide-scrollbar", style: styles.messagesArea, children: [
|
|
15413
15521
|
messages.length === 0 && /* @__PURE__ */ jsx("div", { style: styles.messagesEmpty, children: /* @__PURE__ */ jsx("p", { children: getPlaceholderMessage() }) }),
|
|
15414
15522
|
/* @__PURE__ */ jsx("div", { style: styles.messagesContainer, children: messages.map((message) => /* @__PURE__ */ jsx(
|
|
15415
15523
|
MessageBubble,
|
|
@@ -15536,6 +15644,343 @@ function SettingsIcon() {
|
|
|
15536
15644
|
function AttachIcon() {
|
|
15537
15645
|
return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
15538
15646
|
}
|
|
15647
|
+
var sizeConfigs = {
|
|
15648
|
+
sm: { width: "320px", height: "400px" },
|
|
15649
|
+
md: { width: "380px", height: "520px" },
|
|
15650
|
+
lg: { width: "420px", height: "600px" },
|
|
15651
|
+
xl: { width: "480px", height: "700px" },
|
|
15652
|
+
full: { width: "100vw", height: "100vh" }
|
|
15653
|
+
};
|
|
15654
|
+
var widgetStyles = {
|
|
15655
|
+
// Container for absolute positioning
|
|
15656
|
+
container: {
|
|
15657
|
+
position: "fixed",
|
|
15658
|
+
zIndex: 9999,
|
|
15659
|
+
fontFamily: tokens.typography.fontFamily
|
|
15660
|
+
},
|
|
15661
|
+
// Launcher button
|
|
15662
|
+
launcher: {
|
|
15663
|
+
width: "56px",
|
|
15664
|
+
height: "56px",
|
|
15665
|
+
borderRadius: tokens.radius.full,
|
|
15666
|
+
background: `linear-gradient(135deg, ${tokens.colors.accentPrimary} 0%, #2d7dd2 100%)`,
|
|
15667
|
+
border: "none",
|
|
15668
|
+
cursor: "pointer",
|
|
15669
|
+
display: "flex",
|
|
15670
|
+
alignItems: "center",
|
|
15671
|
+
justifyContent: "center",
|
|
15672
|
+
boxShadow: `0 4px 20px rgba(76, 154, 255, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3)`,
|
|
15673
|
+
transition: `all ${tokens.transitions.normal}`,
|
|
15674
|
+
color: tokens.colors.textInverse
|
|
15675
|
+
},
|
|
15676
|
+
launcherHover: {
|
|
15677
|
+
transform: "scale(1.05)",
|
|
15678
|
+
boxShadow: `0 6px 24px rgba(76, 154, 255, 0.5), 0 4px 12px rgba(0, 0, 0, 0.4)`
|
|
15679
|
+
},
|
|
15680
|
+
// Badge
|
|
15681
|
+
badge: {
|
|
15682
|
+
position: "absolute",
|
|
15683
|
+
top: "-4px",
|
|
15684
|
+
right: "-4px",
|
|
15685
|
+
minWidth: "20px",
|
|
15686
|
+
height: "20px",
|
|
15687
|
+
borderRadius: tokens.radius.pill,
|
|
15688
|
+
background: tokens.colors.accentError,
|
|
15689
|
+
color: tokens.colors.textInverse,
|
|
15690
|
+
fontSize: tokens.typography.fontSizeXs,
|
|
15691
|
+
fontWeight: tokens.typography.fontWeightSemibold,
|
|
15692
|
+
display: "flex",
|
|
15693
|
+
alignItems: "center",
|
|
15694
|
+
justifyContent: "center",
|
|
15695
|
+
padding: "0 6px",
|
|
15696
|
+
border: `2px solid ${tokens.colors.bgPrimary}`
|
|
15697
|
+
},
|
|
15698
|
+
// Chat panel
|
|
15699
|
+
panel: {
|
|
15700
|
+
position: "absolute",
|
|
15701
|
+
borderRadius: tokens.radius.xl,
|
|
15702
|
+
background: tokens.colors.bgSecondary,
|
|
15703
|
+
border: `1px solid ${tokens.colors.borderPrimary}`,
|
|
15704
|
+
boxShadow: `0 16px 48px rgba(0, 0, 0, 0.5)`,
|
|
15705
|
+
overflow: "hidden",
|
|
15706
|
+
display: "flex",
|
|
15707
|
+
flexDirection: "column",
|
|
15708
|
+
transition: `all ${tokens.transitions.slow}`
|
|
15709
|
+
},
|
|
15710
|
+
panelHidden: {
|
|
15711
|
+
opacity: 0,
|
|
15712
|
+
transform: "scale(0.95) translateY(10px)",
|
|
15713
|
+
pointerEvents: "none"
|
|
15714
|
+
},
|
|
15715
|
+
panelVisible: {
|
|
15716
|
+
opacity: 1,
|
|
15717
|
+
transform: "scale(1) translateY(0)"
|
|
15718
|
+
},
|
|
15719
|
+
// Full screen mode overrides
|
|
15720
|
+
panelFull: {
|
|
15721
|
+
position: "fixed",
|
|
15722
|
+
top: 0,
|
|
15723
|
+
left: 0,
|
|
15724
|
+
right: 0,
|
|
15725
|
+
bottom: 0,
|
|
15726
|
+
borderRadius: 0,
|
|
15727
|
+
width: "100vw",
|
|
15728
|
+
height: "100vh"
|
|
15729
|
+
},
|
|
15730
|
+
// Close button in panel header
|
|
15731
|
+
closeBtn: {
|
|
15732
|
+
width: "32px",
|
|
15733
|
+
height: "32px",
|
|
15734
|
+
borderRadius: tokens.radius.md,
|
|
15735
|
+
background: "transparent",
|
|
15736
|
+
border: "none",
|
|
15737
|
+
color: tokens.colors.textTertiary,
|
|
15738
|
+
cursor: "pointer",
|
|
15739
|
+
display: "flex",
|
|
15740
|
+
alignItems: "center",
|
|
15741
|
+
justifyContent: "center",
|
|
15742
|
+
transition: `all ${tokens.transitions.fast}`
|
|
15743
|
+
},
|
|
15744
|
+
closeBtnHover: {
|
|
15745
|
+
background: tokens.colors.bgTertiary,
|
|
15746
|
+
color: tokens.colors.textPrimary
|
|
15747
|
+
}
|
|
15748
|
+
};
|
|
15749
|
+
function ChatIcon() {
|
|
15750
|
+
return /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
|
|
15751
|
+
}
|
|
15752
|
+
function CloseIcon() {
|
|
15753
|
+
return /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
15754
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
15755
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
15756
|
+
] });
|
|
15757
|
+
}
|
|
15758
|
+
function MinimizeIcon() {
|
|
15759
|
+
return /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
15760
|
+
/* @__PURE__ */ jsx("polyline", { points: "4 14 10 14 10 20" }),
|
|
15761
|
+
/* @__PURE__ */ jsx("polyline", { points: "20 10 14 10 14 4" }),
|
|
15762
|
+
/* @__PURE__ */ jsx("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
|
|
15763
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
|
|
15764
|
+
] });
|
|
15765
|
+
}
|
|
15766
|
+
function HustleChatWidget({
|
|
15767
|
+
config = {},
|
|
15768
|
+
...chatProps
|
|
15769
|
+
}) {
|
|
15770
|
+
const {
|
|
15771
|
+
position = "bottom-right",
|
|
15772
|
+
size = "md",
|
|
15773
|
+
title = "Chat",
|
|
15774
|
+
defaultOpen = false,
|
|
15775
|
+
launcherIcon,
|
|
15776
|
+
offset = { x: 24, y: 24 },
|
|
15777
|
+
zIndex = 9999,
|
|
15778
|
+
showBadge = false,
|
|
15779
|
+
badgeContent,
|
|
15780
|
+
launcherStyle,
|
|
15781
|
+
panelStyle,
|
|
15782
|
+
onOpen,
|
|
15783
|
+
onClose,
|
|
15784
|
+
storageKey = "hustle-widget-open"
|
|
15785
|
+
} = config;
|
|
15786
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
15787
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
15788
|
+
const [closeHovered, setCloseHovered] = useState(false);
|
|
15789
|
+
const [mounted, setMounted] = useState(false);
|
|
15790
|
+
useEffect(() => {
|
|
15791
|
+
setMounted(true);
|
|
15792
|
+
if (storageKey && typeof window !== "undefined") {
|
|
15793
|
+
const stored = localStorage.getItem(storageKey);
|
|
15794
|
+
if (stored !== null) {
|
|
15795
|
+
setIsOpen(stored === "true");
|
|
15796
|
+
} else {
|
|
15797
|
+
setIsOpen(defaultOpen);
|
|
15798
|
+
}
|
|
15799
|
+
} else {
|
|
15800
|
+
setIsOpen(defaultOpen);
|
|
15801
|
+
}
|
|
15802
|
+
}, [defaultOpen, storageKey]);
|
|
15803
|
+
useEffect(() => {
|
|
15804
|
+
if (!mounted) return;
|
|
15805
|
+
if (storageKey && typeof window !== "undefined") {
|
|
15806
|
+
localStorage.setItem(storageKey, String(isOpen));
|
|
15807
|
+
}
|
|
15808
|
+
}, [isOpen, storageKey, mounted]);
|
|
15809
|
+
const toggle = useCallback(() => {
|
|
15810
|
+
setIsOpen((prev) => {
|
|
15811
|
+
const next = !prev;
|
|
15812
|
+
if (next) {
|
|
15813
|
+
onOpen?.();
|
|
15814
|
+
} else {
|
|
15815
|
+
onClose?.();
|
|
15816
|
+
}
|
|
15817
|
+
return next;
|
|
15818
|
+
});
|
|
15819
|
+
}, [onOpen, onClose]);
|
|
15820
|
+
const positionStyles = getPositionStyles(position, offset);
|
|
15821
|
+
const sizeConfig = sizeConfigs[size];
|
|
15822
|
+
if (!mounted) {
|
|
15823
|
+
return null;
|
|
15824
|
+
}
|
|
15825
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15826
|
+
/* @__PURE__ */ jsx("style", { children: animations }),
|
|
15827
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
15828
|
+
@keyframes hustle-widget-bounce {
|
|
15829
|
+
0%, 100% { transform: translateY(0); }
|
|
15830
|
+
50% { transform: translateY(-4px); }
|
|
15831
|
+
}
|
|
15832
|
+
` }),
|
|
15833
|
+
/* @__PURE__ */ jsxs(
|
|
15834
|
+
"div",
|
|
15835
|
+
{
|
|
15836
|
+
style: {
|
|
15837
|
+
...widgetStyles.container,
|
|
15838
|
+
...positionStyles.container,
|
|
15839
|
+
zIndex
|
|
15840
|
+
},
|
|
15841
|
+
children: [
|
|
15842
|
+
/* @__PURE__ */ jsxs(
|
|
15843
|
+
"div",
|
|
15844
|
+
{
|
|
15845
|
+
style: {
|
|
15846
|
+
...widgetStyles.panel,
|
|
15847
|
+
...positionStyles.panel,
|
|
15848
|
+
...size === "full" ? widgetStyles.panelFull : {
|
|
15849
|
+
width: sizeConfig.width,
|
|
15850
|
+
height: sizeConfig.height
|
|
15851
|
+
},
|
|
15852
|
+
...isOpen ? widgetStyles.panelVisible : widgetStyles.panelHidden,
|
|
15853
|
+
...panelStyle
|
|
15854
|
+
},
|
|
15855
|
+
children: [
|
|
15856
|
+
/* @__PURE__ */ jsxs(
|
|
15857
|
+
"div",
|
|
15858
|
+
{
|
|
15859
|
+
style: {
|
|
15860
|
+
display: "flex",
|
|
15861
|
+
alignItems: "center",
|
|
15862
|
+
justifyContent: "space-between",
|
|
15863
|
+
padding: `${tokens.spacing.md} ${tokens.spacing.lg}`,
|
|
15864
|
+
background: tokens.colors.bgPrimary,
|
|
15865
|
+
borderBottom: `1px solid ${tokens.colors.borderPrimary}`,
|
|
15866
|
+
borderRadius: `${tokens.radius.xl} ${tokens.radius.xl} 0 0`,
|
|
15867
|
+
flexShrink: 0
|
|
15868
|
+
},
|
|
15869
|
+
children: [
|
|
15870
|
+
/* @__PURE__ */ jsx(
|
|
15871
|
+
"span",
|
|
15872
|
+
{
|
|
15873
|
+
style: {
|
|
15874
|
+
fontWeight: tokens.typography.fontWeightSemibold,
|
|
15875
|
+
color: tokens.colors.textPrimary,
|
|
15876
|
+
fontSize: tokens.typography.fontSizeMd
|
|
15877
|
+
},
|
|
15878
|
+
children: title
|
|
15879
|
+
}
|
|
15880
|
+
),
|
|
15881
|
+
/* @__PURE__ */ jsx(
|
|
15882
|
+
"button",
|
|
15883
|
+
{
|
|
15884
|
+
onClick: toggle,
|
|
15885
|
+
onMouseEnter: () => setCloseHovered(true),
|
|
15886
|
+
onMouseLeave: () => setCloseHovered(false),
|
|
15887
|
+
style: {
|
|
15888
|
+
...widgetStyles.closeBtn,
|
|
15889
|
+
...closeHovered ? widgetStyles.closeBtnHover : {}
|
|
15890
|
+
},
|
|
15891
|
+
title: "Close chat",
|
|
15892
|
+
children: size === "full" ? /* @__PURE__ */ jsx(MinimizeIcon, {}) : /* @__PURE__ */ jsx(CloseIcon, {})
|
|
15893
|
+
}
|
|
15894
|
+
)
|
|
15895
|
+
]
|
|
15896
|
+
}
|
|
15897
|
+
),
|
|
15898
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ jsx(HustleChatInner, { ...chatProps }) })
|
|
15899
|
+
]
|
|
15900
|
+
}
|
|
15901
|
+
),
|
|
15902
|
+
(size !== "full" || !isOpen) && /* @__PURE__ */ jsxs(
|
|
15903
|
+
"button",
|
|
15904
|
+
{
|
|
15905
|
+
onClick: toggle,
|
|
15906
|
+
onMouseEnter: () => setIsHovered(true),
|
|
15907
|
+
onMouseLeave: () => setIsHovered(false),
|
|
15908
|
+
style: {
|
|
15909
|
+
...widgetStyles.launcher,
|
|
15910
|
+
...isHovered ? widgetStyles.launcherHover : {},
|
|
15911
|
+
...positionStyles.launcher,
|
|
15912
|
+
...launcherStyle,
|
|
15913
|
+
...isOpen ? { opacity: 0, pointerEvents: "none" } : {}
|
|
15914
|
+
},
|
|
15915
|
+
title: isOpen ? "Close chat" : "Open chat",
|
|
15916
|
+
"aria-label": isOpen ? "Close chat" : "Open chat",
|
|
15917
|
+
children: [
|
|
15918
|
+
launcherIcon || /* @__PURE__ */ jsx(ChatIcon, {}),
|
|
15919
|
+
showBadge && badgeContent && !isOpen && /* @__PURE__ */ jsx("span", { style: widgetStyles.badge, children: badgeContent })
|
|
15920
|
+
]
|
|
15921
|
+
}
|
|
15922
|
+
)
|
|
15923
|
+
]
|
|
15924
|
+
}
|
|
15925
|
+
)
|
|
15926
|
+
] });
|
|
15927
|
+
}
|
|
15928
|
+
function HustleChatInner(props) {
|
|
15929
|
+
return /* @__PURE__ */ jsx(HustleChat, { ...props, hideHeader: true });
|
|
15930
|
+
}
|
|
15931
|
+
function getPositionStyles(position, offset, isOpen, size) {
|
|
15932
|
+
const panelGap = 16;
|
|
15933
|
+
switch (position) {
|
|
15934
|
+
case "bottom-right":
|
|
15935
|
+
return {
|
|
15936
|
+
container: {
|
|
15937
|
+
bottom: `${offset.y}px`,
|
|
15938
|
+
right: `${offset.x}px`
|
|
15939
|
+
},
|
|
15940
|
+
launcher: {},
|
|
15941
|
+
panel: {
|
|
15942
|
+
bottom: `${56 + panelGap}px`,
|
|
15943
|
+
right: 0
|
|
15944
|
+
}
|
|
15945
|
+
};
|
|
15946
|
+
case "bottom-left":
|
|
15947
|
+
return {
|
|
15948
|
+
container: {
|
|
15949
|
+
bottom: `${offset.y}px`,
|
|
15950
|
+
left: `${offset.x}px`
|
|
15951
|
+
},
|
|
15952
|
+
launcher: {},
|
|
15953
|
+
panel: {
|
|
15954
|
+
bottom: `${56 + panelGap}px`,
|
|
15955
|
+
left: 0
|
|
15956
|
+
}
|
|
15957
|
+
};
|
|
15958
|
+
case "top-right":
|
|
15959
|
+
return {
|
|
15960
|
+
container: {
|
|
15961
|
+
top: `${offset.y}px`,
|
|
15962
|
+
right: `${offset.x}px`
|
|
15963
|
+
},
|
|
15964
|
+
launcher: {},
|
|
15965
|
+
panel: {
|
|
15966
|
+
top: `${56 + panelGap}px`,
|
|
15967
|
+
right: 0
|
|
15968
|
+
}
|
|
15969
|
+
};
|
|
15970
|
+
case "top-left":
|
|
15971
|
+
return {
|
|
15972
|
+
container: {
|
|
15973
|
+
top: `${offset.y}px`,
|
|
15974
|
+
left: `${offset.x}px`
|
|
15975
|
+
},
|
|
15976
|
+
launcher: {},
|
|
15977
|
+
panel: {
|
|
15978
|
+
top: `${56 + panelGap}px`,
|
|
15979
|
+
left: 0
|
|
15980
|
+
}
|
|
15981
|
+
};
|
|
15982
|
+
}
|
|
15983
|
+
}
|
|
15539
15984
|
|
|
15540
15985
|
// src/utils/index.ts
|
|
15541
15986
|
function formatFileSize(bytes) {
|
|
@@ -15564,6 +16009,6 @@ var DEFAULTS = {
|
|
|
15564
16009
|
EMBLEM_MODAL_URL: "https://emblemvault.ai/connect"
|
|
15565
16010
|
};
|
|
15566
16011
|
|
|
15567
|
-
export { DEFAULTS, HustleChat, HustleProvider, MarkdownContent, STORAGE_KEYS, availablePlugins, debounce, formatFileSize, getAvailablePlugin, hydratePlugin, migrateFunPlugin, pluginRegistry, predictionMarketPlugin, tokens, useHustle, usePlugins };
|
|
16012
|
+
export { DEFAULTS, HustleChat, HustleChatWidget, HustleProvider, MarkdownContent, STORAGE_KEYS, availablePlugins, debounce, formatFileSize, getAvailablePlugin, hydratePlugin, migrateFunPlugin, pluginRegistry, predictionMarketPlugin, tokens, useHustle, usePlugins };
|
|
15568
16013
|
//# sourceMappingURL=hustle-react.js.map
|
|
15569
16014
|
//# sourceMappingURL=hustle-react.js.map
|