kanha-ai 0.1.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/dist/index.cjs +573 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +57 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +566 -0
- package/dist/index.js.map +1 -0
- package/dist/widget.d.ts +65 -0
- package/dist/widget.js +414 -0
- package/dist/widget.js.map +1 -0
- package/package.json +54 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var ReactMarkdown = require('react-markdown');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
10
|
+
|
|
11
|
+
// src/KanhaBot.tsx
|
|
12
|
+
var MODEL_LIB_PREFIX = "https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/web-llm-models/v0_2_80";
|
|
13
|
+
var WASM_BY_SIZE = {
|
|
14
|
+
small: `${MODEL_LIB_PREFIX}/Qwen3-0.6B-q4f16_1-ctx4k_cs1k-webgpu.wasm`,
|
|
15
|
+
medium: `${MODEL_LIB_PREFIX}/Qwen3-1.7B-q4f16_1-ctx4k_cs1k-webgpu.wasm`,
|
|
16
|
+
large: `${MODEL_LIB_PREFIX}/Qwen3-4B-q4f16_1-ctx4k_cs1k-webgpu.wasm`
|
|
17
|
+
};
|
|
18
|
+
function resolveModelLib(config) {
|
|
19
|
+
if (config.modelLib) return config.modelLib;
|
|
20
|
+
const size = config.modelSize ?? "small";
|
|
21
|
+
const lib = WASM_BY_SIZE[size];
|
|
22
|
+
if (!lib) throw new Error(`Unknown model size "${size}". Provide modelLib explicitly.`);
|
|
23
|
+
return lib;
|
|
24
|
+
}
|
|
25
|
+
function toWebLLMModelUrl(modelUrl) {
|
|
26
|
+
let url = modelUrl.endsWith("/") ? modelUrl.slice(0, -1) : modelUrl;
|
|
27
|
+
if (!url.includes("/resolve/")) {
|
|
28
|
+
url += "/resolve/v1";
|
|
29
|
+
}
|
|
30
|
+
return url;
|
|
31
|
+
}
|
|
32
|
+
async function checkWebGPU() {
|
|
33
|
+
try {
|
|
34
|
+
const nav = navigator;
|
|
35
|
+
if (!nav.gpu) return false;
|
|
36
|
+
const adapter = await nav.gpu.requestAdapter();
|
|
37
|
+
if (!adapter) return false;
|
|
38
|
+
const device = await adapter.requestDevice();
|
|
39
|
+
return !!device;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function stripThinkTokens(text) {
|
|
45
|
+
let cleaned = text.replace(/<think>[\s\S]*?<\/think>/g, "");
|
|
46
|
+
cleaned = cleaned.replace(/<think>[\s\S]*$/g, "");
|
|
47
|
+
return cleaned.trimStart();
|
|
48
|
+
}
|
|
49
|
+
function useKanhaChat(config) {
|
|
50
|
+
const [messages, setMessages] = react.useState([]);
|
|
51
|
+
const [input, setInput] = react.useState("");
|
|
52
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
53
|
+
const [isThinking, setIsThinking] = react.useState(false);
|
|
54
|
+
const [mode, setMode] = react.useState("detecting");
|
|
55
|
+
const [loadProgress, setLoadProgress] = react.useState(0);
|
|
56
|
+
const [error, setError] = react.useState(null);
|
|
57
|
+
const engineRef = react.useRef(null);
|
|
58
|
+
const configRef = react.useRef(config);
|
|
59
|
+
configRef.current = config;
|
|
60
|
+
const updateAssistantMessage = react.useCallback((text) => {
|
|
61
|
+
const cleaned = stripThinkTokens(text);
|
|
62
|
+
if (!cleaned) {
|
|
63
|
+
setIsThinking(true);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
setIsThinking(false);
|
|
67
|
+
setMessages((prev) => {
|
|
68
|
+
const msgs = [...prev];
|
|
69
|
+
if (msgs[msgs.length - 1]?.role === "assistant") {
|
|
70
|
+
msgs[msgs.length - 1] = { role: "assistant", content: cleaned };
|
|
71
|
+
} else {
|
|
72
|
+
msgs.push({ role: "assistant", content: cleaned });
|
|
73
|
+
}
|
|
74
|
+
return msgs;
|
|
75
|
+
});
|
|
76
|
+
}, []);
|
|
77
|
+
react.useEffect(() => {
|
|
78
|
+
if (typeof window === "undefined") return;
|
|
79
|
+
let cancelled = false;
|
|
80
|
+
const init = async () => {
|
|
81
|
+
setMode("detecting");
|
|
82
|
+
const hasWebGPU = await checkWebGPU();
|
|
83
|
+
if (!hasWebGPU) {
|
|
84
|
+
setMode("error");
|
|
85
|
+
setError("WebGPU is not supported in this browser. Try Chrome 113+ or Edge 113+.");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (cancelled) return;
|
|
89
|
+
setMode("loading");
|
|
90
|
+
try {
|
|
91
|
+
const webllm = await import('@mlc-ai/web-llm');
|
|
92
|
+
const modelId = "kanha-custom-model";
|
|
93
|
+
const modelLib = resolveModelLib(configRef.current);
|
|
94
|
+
const modelUrl = toWebLLMModelUrl(configRef.current.modelUrl);
|
|
95
|
+
const engine = await webllm.CreateMLCEngine(modelId, {
|
|
96
|
+
appConfig: {
|
|
97
|
+
model_list: [
|
|
98
|
+
{
|
|
99
|
+
model: modelUrl,
|
|
100
|
+
model_id: modelId,
|
|
101
|
+
model_lib: modelLib,
|
|
102
|
+
overrides: {
|
|
103
|
+
context_window_size: configRef.current.contextWindowSize ?? 4096
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
initProgressCallback: (report) => {
|
|
109
|
+
if (!cancelled) setLoadProgress(Math.round(report.progress * 100));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (cancelled) return;
|
|
113
|
+
engineRef.current = engine;
|
|
114
|
+
setMode("ready");
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (cancelled) return;
|
|
117
|
+
setMode("error");
|
|
118
|
+
setError(
|
|
119
|
+
err instanceof Error ? err.message : "Failed to load AI model."
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
init();
|
|
124
|
+
return () => {
|
|
125
|
+
cancelled = true;
|
|
126
|
+
};
|
|
127
|
+
}, [config.modelUrl, config.modelLib, config.modelSize]);
|
|
128
|
+
const send = react.useCallback(async () => {
|
|
129
|
+
if (!input.trim() || mode !== "ready" || isLoading || !engineRef.current) return;
|
|
130
|
+
const userMsg = { role: "user", content: input.trim() };
|
|
131
|
+
const allMessages = [...messages, userMsg];
|
|
132
|
+
setMessages(allMessages);
|
|
133
|
+
setInput("");
|
|
134
|
+
setIsLoading(true);
|
|
135
|
+
setIsThinking(true);
|
|
136
|
+
setError(null);
|
|
137
|
+
try {
|
|
138
|
+
const systemMessages = configRef.current.systemPrompt ? [{ role: "system", content: configRef.current.systemPrompt }] : [];
|
|
139
|
+
const completion = await engineRef.current.chat.completions.create({
|
|
140
|
+
messages: [...systemMessages, ...allMessages],
|
|
141
|
+
temperature: configRef.current.temperature ?? 0.7,
|
|
142
|
+
max_tokens: configRef.current.maxTokens ?? 1024,
|
|
143
|
+
stream: true
|
|
144
|
+
});
|
|
145
|
+
let text = "";
|
|
146
|
+
for await (const chunk of completion) {
|
|
147
|
+
const delta = chunk.choices[0]?.delta?.content || "";
|
|
148
|
+
text += delta;
|
|
149
|
+
updateAssistantMessage(text);
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
setError("Failed to generate response. Please try again.");
|
|
153
|
+
setMessages((prev) => [
|
|
154
|
+
...prev,
|
|
155
|
+
{ role: "assistant", content: "Sorry, I encountered an error. Please try again." }
|
|
156
|
+
]);
|
|
157
|
+
} finally {
|
|
158
|
+
setIsLoading(false);
|
|
159
|
+
setIsThinking(false);
|
|
160
|
+
}
|
|
161
|
+
}, [input, mode, isLoading, messages, updateAssistantMessage]);
|
|
162
|
+
const clear = react.useCallback(() => {
|
|
163
|
+
setMessages([]);
|
|
164
|
+
setError(null);
|
|
165
|
+
}, []);
|
|
166
|
+
return {
|
|
167
|
+
messages,
|
|
168
|
+
input,
|
|
169
|
+
setInput,
|
|
170
|
+
isLoading,
|
|
171
|
+
isThinking,
|
|
172
|
+
mode,
|
|
173
|
+
loadProgress,
|
|
174
|
+
error,
|
|
175
|
+
send,
|
|
176
|
+
clear
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/styles.ts
|
|
181
|
+
function fabStyle(primaryColor, position) {
|
|
182
|
+
return {
|
|
183
|
+
position: "fixed",
|
|
184
|
+
bottom: 24,
|
|
185
|
+
[position === "bottom-right" ? "right" : "left"]: 24,
|
|
186
|
+
width: 56,
|
|
187
|
+
height: 56,
|
|
188
|
+
borderRadius: "50%",
|
|
189
|
+
background: primaryColor,
|
|
190
|
+
color: "#fff",
|
|
191
|
+
border: "none",
|
|
192
|
+
cursor: "pointer",
|
|
193
|
+
display: "flex",
|
|
194
|
+
alignItems: "center",
|
|
195
|
+
justifyContent: "center",
|
|
196
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
197
|
+
zIndex: 9999,
|
|
198
|
+
transition: "transform 0.15s ease, box-shadow 0.15s ease"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function panelStyle(position) {
|
|
202
|
+
return {
|
|
203
|
+
position: "fixed",
|
|
204
|
+
bottom: 96,
|
|
205
|
+
[position === "bottom-right" ? "right" : "left"]: 24,
|
|
206
|
+
width: 384,
|
|
207
|
+
maxWidth: "calc(100vw - 48px)",
|
|
208
|
+
maxHeight: "calc(100vh - 120px)",
|
|
209
|
+
borderRadius: 16,
|
|
210
|
+
overflow: "hidden",
|
|
211
|
+
display: "flex",
|
|
212
|
+
flexDirection: "column",
|
|
213
|
+
boxShadow: "0 8px 30px rgba(0,0,0,0.12)",
|
|
214
|
+
border: "1px solid #e5e7eb",
|
|
215
|
+
background: "#ffffff",
|
|
216
|
+
zIndex: 9999,
|
|
217
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
var headerStyle = {
|
|
221
|
+
padding: "16px",
|
|
222
|
+
color: "#fff",
|
|
223
|
+
display: "flex",
|
|
224
|
+
alignItems: "center",
|
|
225
|
+
justifyContent: "space-between"
|
|
226
|
+
};
|
|
227
|
+
var headerTitleStyle = {
|
|
228
|
+
fontWeight: 600,
|
|
229
|
+
fontSize: 15,
|
|
230
|
+
margin: 0
|
|
231
|
+
};
|
|
232
|
+
var headerSubtitleStyle = {
|
|
233
|
+
fontSize: 12,
|
|
234
|
+
opacity: 0.85,
|
|
235
|
+
margin: "4px 0 0"
|
|
236
|
+
};
|
|
237
|
+
var messagesContainerStyle = {
|
|
238
|
+
flex: 1,
|
|
239
|
+
overflowY: "auto",
|
|
240
|
+
padding: 16,
|
|
241
|
+
display: "flex",
|
|
242
|
+
flexDirection: "column",
|
|
243
|
+
gap: 12,
|
|
244
|
+
minHeight: 300,
|
|
245
|
+
maxHeight: 384,
|
|
246
|
+
background: "#fafafa"
|
|
247
|
+
};
|
|
248
|
+
var emptyStateStyle = {
|
|
249
|
+
display: "flex",
|
|
250
|
+
flexDirection: "column",
|
|
251
|
+
alignItems: "center",
|
|
252
|
+
justifyContent: "center",
|
|
253
|
+
height: "100%",
|
|
254
|
+
textAlign: "center",
|
|
255
|
+
color: "#6b7280",
|
|
256
|
+
fontSize: 14,
|
|
257
|
+
padding: 16
|
|
258
|
+
};
|
|
259
|
+
function userBubbleStyle(primaryColor) {
|
|
260
|
+
return {
|
|
261
|
+
maxWidth: "80%",
|
|
262
|
+
padding: "8px 14px",
|
|
263
|
+
borderRadius: 16,
|
|
264
|
+
background: primaryColor,
|
|
265
|
+
color: "#fff",
|
|
266
|
+
fontSize: 14,
|
|
267
|
+
lineHeight: 1.5,
|
|
268
|
+
alignSelf: "flex-end",
|
|
269
|
+
wordBreak: "break-word"
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
var assistantBubbleStyle = {
|
|
273
|
+
maxWidth: "80%",
|
|
274
|
+
padding: "8px 14px",
|
|
275
|
+
borderRadius: 16,
|
|
276
|
+
background: "#fff",
|
|
277
|
+
color: "#1f2937",
|
|
278
|
+
fontSize: 14,
|
|
279
|
+
lineHeight: 1.5,
|
|
280
|
+
border: "1px solid #e5e7eb",
|
|
281
|
+
alignSelf: "flex-start",
|
|
282
|
+
wordBreak: "break-word"
|
|
283
|
+
};
|
|
284
|
+
var inputBarStyle = {
|
|
285
|
+
display: "flex",
|
|
286
|
+
gap: 8,
|
|
287
|
+
padding: "12px 16px",
|
|
288
|
+
borderTop: "1px solid #e5e7eb",
|
|
289
|
+
background: "#fff"
|
|
290
|
+
};
|
|
291
|
+
var textareaStyle = {
|
|
292
|
+
flex: 1,
|
|
293
|
+
padding: "8px 12px",
|
|
294
|
+
border: "1px solid #e5e7eb",
|
|
295
|
+
borderRadius: 10,
|
|
296
|
+
fontSize: 14,
|
|
297
|
+
resize: "none",
|
|
298
|
+
overflow: "hidden",
|
|
299
|
+
outline: "none",
|
|
300
|
+
fontFamily: "inherit",
|
|
301
|
+
minHeight: 40,
|
|
302
|
+
maxHeight: 120,
|
|
303
|
+
lineHeight: 1.5
|
|
304
|
+
};
|
|
305
|
+
function sendButtonStyle(primaryColor) {
|
|
306
|
+
return {
|
|
307
|
+
padding: "8px 14px",
|
|
308
|
+
background: primaryColor,
|
|
309
|
+
color: "#fff",
|
|
310
|
+
border: "none",
|
|
311
|
+
borderRadius: 10,
|
|
312
|
+
cursor: "pointer",
|
|
313
|
+
display: "flex",
|
|
314
|
+
alignItems: "center",
|
|
315
|
+
justifyContent: "center",
|
|
316
|
+
flexShrink: 0,
|
|
317
|
+
transition: "opacity 0.15s ease"
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
var iconButtonStyle = {
|
|
321
|
+
background: "none",
|
|
322
|
+
border: "none",
|
|
323
|
+
color: "inherit",
|
|
324
|
+
cursor: "pointer",
|
|
325
|
+
padding: 4,
|
|
326
|
+
borderRadius: 8,
|
|
327
|
+
display: "flex",
|
|
328
|
+
alignItems: "center",
|
|
329
|
+
justifyContent: "center",
|
|
330
|
+
opacity: 0.8,
|
|
331
|
+
transition: "opacity 0.15s ease"
|
|
332
|
+
};
|
|
333
|
+
function suggestionButtonStyle(primaryColor) {
|
|
334
|
+
return {
|
|
335
|
+
fontSize: 13,
|
|
336
|
+
padding: "8px 12px",
|
|
337
|
+
borderRadius: 10,
|
|
338
|
+
border: "1px solid #e5e7eb",
|
|
339
|
+
background: "#fff",
|
|
340
|
+
color: "#374151",
|
|
341
|
+
cursor: "pointer",
|
|
342
|
+
textAlign: "left",
|
|
343
|
+
width: "100%",
|
|
344
|
+
transition: "border-color 0.15s ease"
|
|
345
|
+
// hover handled inline
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
var progressBarContainer = {
|
|
349
|
+
width: "60%",
|
|
350
|
+
height: 4,
|
|
351
|
+
borderRadius: 2,
|
|
352
|
+
background: "rgba(255,255,255,0.3)",
|
|
353
|
+
marginTop: 8,
|
|
354
|
+
overflow: "hidden"
|
|
355
|
+
};
|
|
356
|
+
function progressBarFill(progress, primaryColor) {
|
|
357
|
+
return {
|
|
358
|
+
width: `${progress}%`,
|
|
359
|
+
height: "100%",
|
|
360
|
+
borderRadius: 2,
|
|
361
|
+
background: primaryColor,
|
|
362
|
+
transition: "width 0.3s ease"
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
var ChatIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.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" }) });
|
|
366
|
+
var CloseIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
367
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
368
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
369
|
+
] });
|
|
370
|
+
var SendIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
371
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
372
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
373
|
+
] });
|
|
374
|
+
var TrashIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
375
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }),
|
|
376
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
377
|
+
] });
|
|
378
|
+
var Spinner = ({ size = 18 }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", style: { animation: "kanha-spin 1s linear infinite" }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
|
|
379
|
+
var spinKeyframes = `@keyframes kanha-spin { to { transform: rotate(360deg); } }`;
|
|
380
|
+
var bounceKeyframes = `
|
|
381
|
+
@keyframes kanha-bounce {
|
|
382
|
+
0%, 80%, 100% { transform: translateY(0); }
|
|
383
|
+
40% { transform: translateY(-4px); }
|
|
384
|
+
}`;
|
|
385
|
+
function ThinkingDots() {
|
|
386
|
+
const dotStyle = (delay) => ({
|
|
387
|
+
width: 6,
|
|
388
|
+
height: 6,
|
|
389
|
+
borderRadius: "50%",
|
|
390
|
+
background: "#9ca3af",
|
|
391
|
+
animation: `kanha-bounce 1.2s ease-in-out ${delay}ms infinite`
|
|
392
|
+
});
|
|
393
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", gap: 3, alignItems: "center" }, children: [
|
|
394
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: dotStyle(0) }),
|
|
395
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: dotStyle(150) }),
|
|
396
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: dotStyle(300) }),
|
|
397
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#9ca3af", marginLeft: 6 }, children: "Thinking" })
|
|
398
|
+
] });
|
|
399
|
+
}
|
|
400
|
+
function KanhaBot({
|
|
401
|
+
modelUrl,
|
|
402
|
+
modelLib,
|
|
403
|
+
modelSize,
|
|
404
|
+
systemPrompt,
|
|
405
|
+
temperature,
|
|
406
|
+
maxTokens,
|
|
407
|
+
contextWindowSize,
|
|
408
|
+
botName = "AI Assistant",
|
|
409
|
+
welcomeMessage = "Ask me anything!",
|
|
410
|
+
suggestions = [],
|
|
411
|
+
theme = {}
|
|
412
|
+
}) {
|
|
413
|
+
const primaryColor = theme.primaryColor ?? "#0d9488";
|
|
414
|
+
const position = theme.position ?? "bottom-right";
|
|
415
|
+
const {
|
|
416
|
+
messages,
|
|
417
|
+
input,
|
|
418
|
+
setInput,
|
|
419
|
+
isLoading,
|
|
420
|
+
isThinking,
|
|
421
|
+
mode,
|
|
422
|
+
loadProgress,
|
|
423
|
+
error,
|
|
424
|
+
send,
|
|
425
|
+
clear
|
|
426
|
+
} = useKanhaChat({
|
|
427
|
+
modelUrl,
|
|
428
|
+
modelLib,
|
|
429
|
+
modelSize,
|
|
430
|
+
systemPrompt,
|
|
431
|
+
temperature,
|
|
432
|
+
maxTokens,
|
|
433
|
+
contextWindowSize
|
|
434
|
+
});
|
|
435
|
+
const [isOpen, setIsOpen] = react.useState(false);
|
|
436
|
+
const messagesEndRef = react.useRef(null);
|
|
437
|
+
const textareaRef = react.useRef(null);
|
|
438
|
+
react.useEffect(() => {
|
|
439
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
440
|
+
}, [messages]);
|
|
441
|
+
react.useEffect(() => {
|
|
442
|
+
if (textareaRef.current) {
|
|
443
|
+
textareaRef.current.style.height = "auto";
|
|
444
|
+
textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 120) + "px";
|
|
445
|
+
}
|
|
446
|
+
}, [input]);
|
|
447
|
+
const handleKeyPress = (e) => {
|
|
448
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
449
|
+
e.preventDefault();
|
|
450
|
+
send();
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
const isReady = mode === "ready";
|
|
454
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
455
|
+
/* @__PURE__ */ jsxRuntime.jsxs("style", { children: [
|
|
456
|
+
spinKeyframes,
|
|
457
|
+
bounceKeyframes
|
|
458
|
+
] }),
|
|
459
|
+
isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle(position), children: [
|
|
460
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...headerStyle, background: primaryColor }, children: [
|
|
461
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
462
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: headerTitleStyle, children: botName }),
|
|
463
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: headerSubtitleStyle, children: isReady ? welcomeMessage : mode === "loading" ? `Loading AI model (${loadProgress}%)` : mode === "error" ? "Failed to load" : "Detecting capabilities..." }),
|
|
464
|
+
mode === "loading" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: progressBarContainer, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: progressBarFill(loadProgress, "#fff") }) })
|
|
465
|
+
] }),
|
|
466
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 4 }, children: [
|
|
467
|
+
messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: clear, style: iconButtonStyle, "aria-label": "Clear chat", children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {}) }),
|
|
468
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setIsOpen(false), style: iconButtonStyle, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) })
|
|
469
|
+
] })
|
|
470
|
+
] }),
|
|
471
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "10px 16px", background: "#fef2f2", borderBottom: "1px solid #fecaca", fontSize: 13, color: "#b91c1c" }, children: error }),
|
|
472
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: messagesContainerStyle, children: messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: emptyStateStyle, children: isReady ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
473
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChatIcon, {}),
|
|
474
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { marginTop: 12, fontWeight: 500, color: "#374151" }, children: welcomeMessage }),
|
|
475
|
+
suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6, width: "100%", marginTop: 16 }, children: suggestions.map((text) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
476
|
+
"button",
|
|
477
|
+
{
|
|
478
|
+
onClick: () => {
|
|
479
|
+
setInput(text);
|
|
480
|
+
setTimeout(send, 0);
|
|
481
|
+
},
|
|
482
|
+
style: suggestionButtonStyle(),
|
|
483
|
+
onMouseEnter: (e) => {
|
|
484
|
+
e.target.style.borderColor = primaryColor;
|
|
485
|
+
},
|
|
486
|
+
onMouseLeave: (e) => {
|
|
487
|
+
e.target.style.borderColor = "#e5e7eb";
|
|
488
|
+
},
|
|
489
|
+
children: text
|
|
490
|
+
},
|
|
491
|
+
text
|
|
492
|
+
)) })
|
|
493
|
+
] }) : mode === "loading" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
494
|
+
/* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 32 }),
|
|
495
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: { marginTop: 8 }, children: [
|
|
496
|
+
"Loading AI model (",
|
|
497
|
+
loadProgress,
|
|
498
|
+
"%)"
|
|
499
|
+
] }),
|
|
500
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 12, marginTop: 4 }, children: "First load takes about a minute" })
|
|
501
|
+
] }) : mode === "error" ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: error }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
502
|
+
/* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 32 }),
|
|
503
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { marginTop: 8 }, children: "Detecting browser capabilities..." })
|
|
504
|
+
] }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
505
|
+
messages.map((msg, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
506
|
+
"div",
|
|
507
|
+
{
|
|
508
|
+
style: {
|
|
509
|
+
display: "flex",
|
|
510
|
+
justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
|
|
511
|
+
},
|
|
512
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: msg.role === "user" ? userBubbleStyle(primaryColor) : assistantBubbleStyle, children: msg.role === "assistant" ? /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown__default.default, { children: msg.content }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { whiteSpace: "pre-wrap" }, children: msg.content }) })
|
|
513
|
+
},
|
|
514
|
+
i
|
|
515
|
+
)),
|
|
516
|
+
isLoading && (isThinking || messages[messages.length - 1]?.role !== "assistant") && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "flex-start" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: assistantBubbleStyle, children: isThinking ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingDots, {}) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 16 }) }) }),
|
|
517
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
|
|
518
|
+
] }) }),
|
|
519
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: inputBarStyle, children: [
|
|
520
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
521
|
+
"textarea",
|
|
522
|
+
{
|
|
523
|
+
ref: textareaRef,
|
|
524
|
+
value: input,
|
|
525
|
+
onChange: (e) => setInput(e.target.value),
|
|
526
|
+
onKeyDown: handleKeyPress,
|
|
527
|
+
placeholder: isReady ? "Type your message..." : "Waiting for model...",
|
|
528
|
+
disabled: !isReady || isLoading,
|
|
529
|
+
rows: 1,
|
|
530
|
+
style: {
|
|
531
|
+
...textareaStyle,
|
|
532
|
+
opacity: !isReady ? 0.5 : 1
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
),
|
|
536
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
537
|
+
"button",
|
|
538
|
+
{
|
|
539
|
+
onClick: send,
|
|
540
|
+
disabled: !isReady || isLoading || !input.trim(),
|
|
541
|
+
style: {
|
|
542
|
+
...sendButtonStyle(primaryColor),
|
|
543
|
+
opacity: !isReady || isLoading || !input.trim() ? 0.5 : 1,
|
|
544
|
+
cursor: !isReady || isLoading || !input.trim() ? "not-allowed" : "pointer"
|
|
545
|
+
},
|
|
546
|
+
"aria-label": "Send message",
|
|
547
|
+
children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 18 }) : /* @__PURE__ */ jsxRuntime.jsx(SendIcon, {})
|
|
548
|
+
}
|
|
549
|
+
)
|
|
550
|
+
] })
|
|
551
|
+
] }),
|
|
552
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
553
|
+
"button",
|
|
554
|
+
{
|
|
555
|
+
onClick: () => setIsOpen(!isOpen),
|
|
556
|
+
style: fabStyle(primaryColor, position),
|
|
557
|
+
"aria-label": "Toggle chat",
|
|
558
|
+
onMouseEnter: (e) => {
|
|
559
|
+
e.target.style.transform = "scale(1.05)";
|
|
560
|
+
},
|
|
561
|
+
onMouseLeave: (e) => {
|
|
562
|
+
e.target.style.transform = "scale(1)";
|
|
563
|
+
},
|
|
564
|
+
children: isOpen ? /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, {})
|
|
565
|
+
}
|
|
566
|
+
)
|
|
567
|
+
] });
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
exports.KanhaBot = KanhaBot;
|
|
571
|
+
exports.useKanhaChat = useKanhaChat;
|
|
572
|
+
//# sourceMappingURL=index.cjs.map
|
|
573
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/use-kanha-chat.ts","../src/styles.ts","../src/KanhaBot.tsx"],"names":["useState","useRef","useCallback","useEffect","jsx","jsxs","Fragment","ReactMarkdown"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { Message, InferenceMode, KanhaChatConfig, KanhaChatReturn } from \"./types\";\n\nconst MODEL_LIB_PREFIX =\n \"https://raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/web-llm-models/v0_2_80\";\n\nconst WASM_BY_SIZE: Record<string, string> = {\n small: `${MODEL_LIB_PREFIX}/Qwen3-0.6B-q4f16_1-ctx4k_cs1k-webgpu.wasm`,\n medium: `${MODEL_LIB_PREFIX}/Qwen3-1.7B-q4f16_1-ctx4k_cs1k-webgpu.wasm`,\n large: `${MODEL_LIB_PREFIX}/Qwen3-4B-q4f16_1-ctx4k_cs1k-webgpu.wasm`,\n};\n\nfunction resolveModelLib(config: KanhaChatConfig): string {\n if (config.modelLib) return config.modelLib;\n const size = config.modelSize ?? \"small\";\n const lib = WASM_BY_SIZE[size];\n if (!lib) throw new Error(`Unknown model size \"${size}\". Provide modelLib explicitly.`);\n return lib;\n}\n\n/**\n * Converts a storage URL into a format web-llm's cleanModelUrl won't mangle.\n * web-llm checks for /resolve/ in the URL — if missing, it appends /resolve/main/.\n * We append /resolve/v1/ so the URL passes through untouched.\n * Files must be stored at {modelUrl}/resolve/v1/{filename} in storage.\n */\nfunction toWebLLMModelUrl(modelUrl: string): string {\n let url = modelUrl.endsWith(\"/\") ? modelUrl.slice(0, -1) : modelUrl;\n if (!url.includes(\"/resolve/\")) {\n url += \"/resolve/v1\";\n }\n return url;\n}\n\nasync function checkWebGPU(): Promise<boolean> {\n try {\n const nav = navigator as Navigator & { gpu?: { requestAdapter(): Promise<unknown> } };\n if (!nav.gpu) return false;\n const adapter = (await nav.gpu.requestAdapter()) as { requestDevice(): Promise<unknown> } | null;\n if (!adapter) return false;\n const device = await adapter.requestDevice();\n return !!device;\n } catch {\n return false;\n }\n}\n\nfunction stripThinkTokens(text: string): string {\n let cleaned = text.replace(/<think>[\\s\\S]*?<\\/think>/g, \"\");\n cleaned = cleaned.replace(/<think>[\\s\\S]*$/g, \"\");\n return cleaned.trimStart();\n}\n\nexport function useKanhaChat(config: KanhaChatConfig): KanhaChatReturn {\n const [messages, setMessages] = useState<Message[]>([]);\n const [input, setInput] = useState(\"\");\n const [isLoading, setIsLoading] = useState(false);\n const [isThinking, setIsThinking] = useState(false);\n const [mode, setMode] = useState<InferenceMode>(\"detecting\");\n const [loadProgress, setLoadProgress] = useState(0);\n const [error, setError] = useState<string | null>(null);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const engineRef = useRef<any>(null);\n const configRef = useRef(config);\n configRef.current = config;\n\n const updateAssistantMessage = useCallback((text: string) => {\n const cleaned = stripThinkTokens(text);\n if (!cleaned) {\n setIsThinking(true);\n return;\n }\n setIsThinking(false);\n setMessages((prev) => {\n const msgs = [...prev];\n if (msgs[msgs.length - 1]?.role === \"assistant\") {\n msgs[msgs.length - 1] = { role: \"assistant\", content: cleaned };\n } else {\n msgs.push({ role: \"assistant\", content: cleaned });\n }\n return msgs;\n });\n }, []);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n\n let cancelled = false;\n\n const init = async () => {\n setMode(\"detecting\");\n\n const hasWebGPU = await checkWebGPU();\n if (!hasWebGPU) {\n setMode(\"error\");\n setError(\"WebGPU is not supported in this browser. Try Chrome 113+ or Edge 113+.\");\n return;\n }\n\n if (cancelled) return;\n setMode(\"loading\");\n\n try {\n const webllm = await import(\"@mlc-ai/web-llm\");\n const modelId = \"kanha-custom-model\";\n const modelLib = resolveModelLib(configRef.current);\n const modelUrl = toWebLLMModelUrl(configRef.current.modelUrl);\n\n const engine = await webllm.CreateMLCEngine(modelId, {\n appConfig: {\n model_list: [\n {\n model: modelUrl,\n model_id: modelId,\n model_lib: modelLib,\n overrides: {\n context_window_size: configRef.current.contextWindowSize ?? 4096,\n },\n },\n ],\n },\n initProgressCallback: (report: { progress: number }) => {\n if (!cancelled) setLoadProgress(Math.round(report.progress * 100));\n },\n });\n\n if (cancelled) return;\n engineRef.current = engine;\n setMode(\"ready\");\n } catch (err) {\n if (cancelled) return;\n setMode(\"error\");\n setError(\n err instanceof Error ? err.message : \"Failed to load AI model.\"\n );\n }\n };\n\n init();\n return () => { cancelled = true; };\n }, [config.modelUrl, config.modelLib, config.modelSize]);\n\n const send = useCallback(async () => {\n if (!input.trim() || mode !== \"ready\" || isLoading || !engineRef.current) return;\n\n const userMsg: Message = { role: \"user\", content: input.trim() };\n const allMessages = [...messages, userMsg];\n setMessages(allMessages);\n setInput(\"\");\n setIsLoading(true);\n setIsThinking(true);\n setError(null);\n\n try {\n const systemMessages: Message[] = configRef.current.systemPrompt\n ? [{ role: \"system\", content: configRef.current.systemPrompt }]\n : [];\n\n const completion = await engineRef.current.chat.completions.create({\n messages: [...systemMessages, ...allMessages],\n temperature: configRef.current.temperature ?? 0.7,\n max_tokens: configRef.current.maxTokens ?? 1024,\n stream: true,\n });\n\n let text = \"\";\n for await (const chunk of completion) {\n const delta = chunk.choices[0]?.delta?.content || \"\";\n text += delta;\n updateAssistantMessage(text);\n }\n } catch {\n setError(\"Failed to generate response. Please try again.\");\n setMessages((prev) => [\n ...prev,\n { role: \"assistant\", content: \"Sorry, I encountered an error. Please try again.\" },\n ]);\n } finally {\n setIsLoading(false);\n setIsThinking(false);\n }\n }, [input, mode, isLoading, messages, updateAssistantMessage]);\n\n const clear = useCallback(() => {\n setMessages([]);\n setError(null);\n }, []);\n\n return {\n messages,\n input,\n setInput,\n isLoading,\n isThinking,\n mode,\n loadProgress,\n error,\n send,\n clear,\n };\n}\n","import type { CSSProperties } from \"react\";\n\nexport function fabStyle(\n primaryColor: string,\n position: \"bottom-right\" | \"bottom-left\",\n): CSSProperties {\n return {\n position: \"fixed\",\n bottom: 24,\n [position === \"bottom-right\" ? \"right\" : \"left\"]: 24,\n width: 56,\n height: 56,\n borderRadius: \"50%\",\n background: primaryColor,\n color: \"#fff\",\n border: \"none\",\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\n zIndex: 9999,\n transition: \"transform 0.15s ease, box-shadow 0.15s ease\",\n };\n}\n\nexport function panelStyle(position: \"bottom-right\" | \"bottom-left\"): CSSProperties {\n return {\n position: \"fixed\",\n bottom: 96,\n [position === \"bottom-right\" ? \"right\" : \"left\"]: 24,\n width: 384,\n maxWidth: \"calc(100vw - 48px)\",\n maxHeight: \"calc(100vh - 120px)\",\n borderRadius: 16,\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n boxShadow: \"0 8px 30px rgba(0,0,0,0.12)\",\n border: \"1px solid #e5e7eb\",\n background: \"#ffffff\",\n zIndex: 9999,\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n };\n}\n\nexport const headerStyle: CSSProperties = {\n padding: \"16px\",\n color: \"#fff\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n};\n\nexport const headerTitleStyle: CSSProperties = {\n fontWeight: 600,\n fontSize: 15,\n margin: 0,\n};\n\nexport const headerSubtitleStyle: CSSProperties = {\n fontSize: 12,\n opacity: 0.85,\n margin: \"4px 0 0\",\n};\n\nexport const messagesContainerStyle: CSSProperties = {\n flex: 1,\n overflowY: \"auto\",\n padding: 16,\n display: \"flex\",\n flexDirection: \"column\",\n gap: 12,\n minHeight: 300,\n maxHeight: 384,\n background: \"#fafafa\",\n};\n\nexport const emptyStateStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n textAlign: \"center\",\n color: \"#6b7280\",\n fontSize: 14,\n padding: 16,\n};\n\nexport function userBubbleStyle(primaryColor: string): CSSProperties {\n return {\n maxWidth: \"80%\",\n padding: \"8px 14px\",\n borderRadius: 16,\n background: primaryColor,\n color: \"#fff\",\n fontSize: 14,\n lineHeight: 1.5,\n alignSelf: \"flex-end\",\n wordBreak: \"break-word\",\n };\n}\n\nexport const assistantBubbleStyle: CSSProperties = {\n maxWidth: \"80%\",\n padding: \"8px 14px\",\n borderRadius: 16,\n background: \"#fff\",\n color: \"#1f2937\",\n fontSize: 14,\n lineHeight: 1.5,\n border: \"1px solid #e5e7eb\",\n alignSelf: \"flex-start\",\n wordBreak: \"break-word\",\n};\n\nexport const inputBarStyle: CSSProperties = {\n display: \"flex\",\n gap: 8,\n padding: \"12px 16px\",\n borderTop: \"1px solid #e5e7eb\",\n background: \"#fff\",\n};\n\nexport const textareaStyle: CSSProperties = {\n flex: 1,\n padding: \"8px 12px\",\n border: \"1px solid #e5e7eb\",\n borderRadius: 10,\n fontSize: 14,\n resize: \"none\",\n overflow: \"hidden\",\n outline: \"none\",\n fontFamily: \"inherit\",\n minHeight: 40,\n maxHeight: 120,\n lineHeight: 1.5,\n};\n\nexport function sendButtonStyle(primaryColor: string): CSSProperties {\n return {\n padding: \"8px 14px\",\n background: primaryColor,\n color: \"#fff\",\n border: \"none\",\n borderRadius: 10,\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n transition: \"opacity 0.15s ease\",\n };\n}\n\nexport const iconButtonStyle: CSSProperties = {\n background: \"none\",\n border: \"none\",\n color: \"inherit\",\n cursor: \"pointer\",\n padding: 4,\n borderRadius: 8,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n opacity: 0.8,\n transition: \"opacity 0.15s ease\",\n};\n\nexport function suggestionButtonStyle(primaryColor: string): CSSProperties {\n return {\n fontSize: 13,\n padding: \"8px 12px\",\n borderRadius: 10,\n border: \"1px solid #e5e7eb\",\n background: \"#fff\",\n color: \"#374151\",\n cursor: \"pointer\",\n textAlign: \"left\" as const,\n width: \"100%\",\n transition: \"border-color 0.15s ease\",\n // hover handled inline\n };\n}\n\nexport const progressBarContainer: CSSProperties = {\n width: \"60%\",\n height: 4,\n borderRadius: 2,\n background: \"rgba(255,255,255,0.3)\",\n marginTop: 8,\n overflow: \"hidden\",\n};\n\nexport function progressBarFill(progress: number, primaryColor: string): CSSProperties {\n return {\n width: `${progress}%`,\n height: \"100%\",\n borderRadius: 2,\n background: primaryColor,\n transition: \"width 0.3s ease\",\n };\n}\n","import {\n useState,\n useEffect,\n useRef,\n type KeyboardEvent,\n type CSSProperties,\n} from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport { useKanhaChat } from \"./use-kanha-chat\";\nimport type { KanhaBotProps } from \"./types\";\nimport * as s from \"./styles\";\n\n// Inline SVG icons (no icon library dependency)\nconst ChatIcon = () => (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n);\n\nconst CloseIcon = () => (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" /><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\nconst SendIcon = () => (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\" /><polygon points=\"22 2 15 22 11 13 2 9 22 2\" />\n </svg>\n);\n\nconst TrashIcon = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\" /><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </svg>\n);\n\nconst Spinner = ({ size = 18 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" style={{ animation: \"kanha-spin 1s linear infinite\" }}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n);\n\nconst spinKeyframes = `@keyframes kanha-spin { to { transform: rotate(360deg); } }`;\nconst bounceKeyframes = `\n@keyframes kanha-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-4px); }\n}`;\n\nfunction ThinkingDots() {\n const dotStyle = (delay: number): CSSProperties => ({\n width: 6,\n height: 6,\n borderRadius: \"50%\",\n background: \"#9ca3af\",\n animation: `kanha-bounce 1.2s ease-in-out ${delay}ms infinite`,\n });\n return (\n <span style={{ display: \"flex\", gap: 3, alignItems: \"center\" }}>\n <span style={dotStyle(0)} />\n <span style={dotStyle(150)} />\n <span style={dotStyle(300)} />\n <span style={{ fontSize: 12, color: \"#9ca3af\", marginLeft: 6 }}>Thinking</span>\n </span>\n );\n}\n\nexport function KanhaBot({\n modelUrl,\n modelLib,\n modelSize,\n systemPrompt,\n temperature,\n maxTokens,\n contextWindowSize,\n botName = \"AI Assistant\",\n welcomeMessage = \"Ask me anything!\",\n suggestions = [],\n theme = {},\n}: KanhaBotProps) {\n const primaryColor = theme.primaryColor ?? \"#0d9488\";\n const position = theme.position ?? \"bottom-right\";\n\n const {\n messages,\n input,\n setInput,\n isLoading,\n isThinking,\n mode,\n loadProgress,\n error,\n send,\n clear,\n } = useKanhaChat({\n modelUrl,\n modelLib,\n modelSize,\n systemPrompt,\n temperature,\n maxTokens,\n contextWindowSize,\n });\n\n const [isOpen, setIsOpen] = useState(false);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }, [messages]);\n\n useEffect(() => {\n if (textareaRef.current) {\n textareaRef.current.style.height = \"auto\";\n textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 120) + \"px\";\n }\n }, [input]);\n\n const handleKeyPress = (e: KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n send();\n }\n };\n\n const isReady = mode === \"ready\";\n\n return (\n <>\n <style>{spinKeyframes}{bounceKeyframes}</style>\n\n {isOpen && (\n <div style={s.panelStyle(position)}>\n {/* Header */}\n <div style={{ ...s.headerStyle, background: primaryColor }}>\n <div>\n <p style={s.headerTitleStyle}>{botName}</p>\n <p style={s.headerSubtitleStyle}>\n {isReady\n ? welcomeMessage\n : mode === \"loading\"\n ? `Loading AI model (${loadProgress}%)`\n : mode === \"error\"\n ? \"Failed to load\"\n : \"Detecting capabilities...\"}\n </p>\n {mode === \"loading\" && (\n <div style={s.progressBarContainer}>\n <div style={s.progressBarFill(loadProgress, \"#fff\")} />\n </div>\n )}\n </div>\n <div style={{ display: \"flex\", gap: 4 }}>\n {messages.length > 0 && (\n <button onClick={clear} style={s.iconButtonStyle} aria-label=\"Clear chat\">\n <TrashIcon />\n </button>\n )}\n <button onClick={() => setIsOpen(false)} style={s.iconButtonStyle} aria-label=\"Close\">\n <CloseIcon />\n </button>\n </div>\n </div>\n\n {/* Error banner */}\n {error && (\n <div style={{ padding: \"10px 16px\", background: \"#fef2f2\", borderBottom: \"1px solid #fecaca\", fontSize: 13, color: \"#b91c1c\" }}>\n {error}\n </div>\n )}\n\n {/* Messages */}\n <div style={s.messagesContainerStyle}>\n {messages.length === 0 ? (\n <div style={s.emptyStateStyle}>\n {isReady ? (\n <>\n <ChatIcon />\n <p style={{ marginTop: 12, fontWeight: 500, color: \"#374151\" }}>\n {welcomeMessage}\n </p>\n {suggestions.length > 0 && (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6, width: \"100%\", marginTop: 16 }}>\n {suggestions.map((text) => (\n <button\n key={text}\n onClick={() => { setInput(text); setTimeout(send, 0); }}\n style={s.suggestionButtonStyle(primaryColor)}\n onMouseEnter={(e) => { (e.target as HTMLElement).style.borderColor = primaryColor; }}\n onMouseLeave={(e) => { (e.target as HTMLElement).style.borderColor = \"#e5e7eb\"; }}\n >\n {text}\n </button>\n ))}\n </div>\n )}\n </>\n ) : mode === \"loading\" ? (\n <>\n <Spinner size={32} />\n <p style={{ marginTop: 8 }}>Loading AI model ({loadProgress}%)</p>\n <p style={{ fontSize: 12, marginTop: 4 }}>First load takes about a minute</p>\n </>\n ) : mode === \"error\" ? (\n <p>{error}</p>\n ) : (\n <>\n <Spinner size={32} />\n <p style={{ marginTop: 8 }}>Detecting browser capabilities...</p>\n </>\n )}\n </div>\n ) : (\n <>\n {messages.map((msg, i) => (\n <div\n key={i}\n style={{\n display: \"flex\",\n justifyContent: msg.role === \"user\" ? \"flex-end\" : \"flex-start\",\n }}\n >\n <div style={msg.role === \"user\" ? s.userBubbleStyle(primaryColor) : s.assistantBubbleStyle}>\n {msg.role === \"assistant\" ? (\n <ReactMarkdown>{msg.content}</ReactMarkdown>\n ) : (\n <span style={{ whiteSpace: \"pre-wrap\" }}>{msg.content}</span>\n )}\n </div>\n </div>\n ))}\n {isLoading && (isThinking || messages[messages.length - 1]?.role !== \"assistant\") && (\n <div style={{ display: \"flex\", justifyContent: \"flex-start\" }}>\n <div style={s.assistantBubbleStyle}>\n {isThinking ? <ThinkingDots /> : <Spinner size={16} />}\n </div>\n </div>\n )}\n <div ref={messagesEndRef} />\n </>\n )}\n </div>\n\n {/* Input */}\n <div style={s.inputBarStyle}>\n <textarea\n ref={textareaRef}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyPress}\n placeholder={isReady ? \"Type your message...\" : \"Waiting for model...\"}\n disabled={!isReady || isLoading}\n rows={1}\n style={{\n ...s.textareaStyle,\n opacity: !isReady ? 0.5 : 1,\n }}\n />\n <button\n onClick={send}\n disabled={!isReady || isLoading || !input.trim()}\n style={{\n ...s.sendButtonStyle(primaryColor),\n opacity: !isReady || isLoading || !input.trim() ? 0.5 : 1,\n cursor: !isReady || isLoading || !input.trim() ? \"not-allowed\" : \"pointer\",\n }}\n aria-label=\"Send message\"\n >\n {isLoading ? <Spinner size={18} /> : <SendIcon />}\n </button>\n </div>\n </div>\n )}\n\n {/* FAB */}\n <button\n onClick={() => setIsOpen(!isOpen)}\n style={s.fabStyle(primaryColor, position)}\n aria-label=\"Toggle chat\"\n onMouseEnter={(e) => { (e.target as HTMLElement).style.transform = \"scale(1.05)\"; }}\n onMouseLeave={(e) => { (e.target as HTMLElement).style.transform = \"scale(1)\"; }}\n >\n {isOpen ? <CloseIcon /> : <ChatIcon />}\n </button>\n </>\n );\n}\n"]}
|