@blank-utils/llm 0.2.2 → 0.2.6

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.js CHANGED
@@ -57,10 +57,10 @@ function getAugmentedNamespace(n) {
57
57
  return n;
58
58
  var f = n.default;
59
59
  if (typeof f == "function") {
60
- var a = function a() {
60
+ var a = function a2() {
61
61
  var isInstance = false;
62
62
  try {
63
- isInstance = this instanceof a;
63
+ isInstance = this instanceof a2;
64
64
  } catch {}
65
65
  if (isInstance) {
66
66
  return Reflect.construct(f, arguments, this.constructor);
@@ -56591,7 +56591,7 @@ async function logCapabilities() {
56591
56591
  console.log(` Recommended device: ${caps.recommendedDevice}`);
56592
56592
  return caps;
56593
56593
  }
56594
- // src/backends/webllm.ts
56594
+ // src/models.ts
56595
56595
  var DEFAULT_WEBLLM_MODEL = "Phi-3.5-mini-instruct-q4f16_1-MLC";
56596
56596
  var WEBLLM_MODELS = {
56597
56597
  "llama-3.2-1b": "Llama-3.2-1B-Instruct-q4f16_1-MLC",
@@ -56623,6 +56623,21 @@ var WEBLLM_MODELS = {
56623
56623
  "hermes-3-llama-3.2-3b": "Hermes-3-Llama-3.2-3B-q4f16_1-MLC",
56624
56624
  "hermes-3-llama-3.1-8b": "Hermes-3-Llama-3.1-8B-q4f16_1-MLC"
56625
56625
  };
56626
+ var DEFAULT_TRANSFORMERS_MODEL = "onnx-community/Qwen2.5-0.5B-Instruct";
56627
+ var TRANSFORMERS_MODELS = {
56628
+ "qwen-2.5-0.5b": "onnx-community/Qwen2.5-0.5B-Instruct",
56629
+ "qwen-2.5-1.5b": "onnx-community/Qwen2.5-1.5B-Instruct",
56630
+ "qwen-2.5-coder-0.5b": "onnx-community/Qwen2.5-Coder-0.5B-Instruct",
56631
+ "qwen-2.5-coder-1.5b": "onnx-community/Qwen2.5-Coder-1.5B-Instruct",
56632
+ "qwen-3-0.6b": "onnx-community/Qwen3-0.6B-ONNX",
56633
+ "smollm2-135m": "HuggingFaceTB/SmolLM2-135M-Instruct",
56634
+ "smollm2-360m": "HuggingFaceTB/SmolLM2-360M-Instruct",
56635
+ "smollm2-1.7b": "HuggingFaceTB/SmolLM2-1.7B-Instruct",
56636
+ "phi-3-mini": "Xenova/Phi-3-mini-4k-instruct",
56637
+ tinyllama: "Xenova/TinyLlama-1.1B-Chat-v1.0"
56638
+ };
56639
+
56640
+ // src/backends/webllm.ts
56626
56641
  function resolveModelId(model) {
56627
56642
  if (model in WEBLLM_MODELS) {
56628
56643
  return WEBLLM_MODELS[model];
@@ -56710,19 +56725,6 @@ function createWebLLMProvider() {
56710
56725
  return new WebLLMProvider;
56711
56726
  }
56712
56727
  // src/backends/transformers.ts
56713
- var DEFAULT_TRANSFORMERS_MODEL = "onnx-community/Qwen2.5-0.5B-Instruct";
56714
- var TRANSFORMERS_MODELS = {
56715
- "qwen-2.5-0.5b": "onnx-community/Qwen2.5-0.5B-Instruct",
56716
- "qwen-2.5-1.5b": "onnx-community/Qwen2.5-1.5B-Instruct",
56717
- "qwen-2.5-coder-0.5b": "onnx-community/Qwen2.5-Coder-0.5B-Instruct",
56718
- "qwen-2.5-coder-1.5b": "onnx-community/Qwen2.5-Coder-1.5B-Instruct",
56719
- "qwen-3-0.6b": "onnx-community/Qwen3-0.6B-ONNX",
56720
- "smollm2-135m": "HuggingFaceTB/SmolLM2-135M-Instruct",
56721
- "smollm2-360m": "HuggingFaceTB/SmolLM2-360M-Instruct",
56722
- "smollm2-1.7b": "HuggingFaceTB/SmolLM2-1.7B-Instruct",
56723
- "phi-3-mini": "Xenova/Phi-3-mini-4k-instruct",
56724
- tinyllama: "Xenova/TinyLlama-1.1B-Chat-v1.0"
56725
- };
56726
56728
  function detectChatFormat(modelId) {
56727
56729
  const lower = modelId.toLowerCase();
56728
56730
  if (lower.includes("qwen") || lower.includes("smollm")) {
@@ -57127,13 +57129,1189 @@ async function isWebGPUSupported() {
57127
57129
  import {
57128
57130
  createContext,
57129
57131
  useContext,
57130
- useState,
57131
- useCallback,
57132
- useEffect,
57133
- useMemo,
57134
- useRef
57132
+ useState as useState3,
57133
+ useCallback as useCallback2,
57134
+ useEffect as useEffect3,
57135
+ useMemo as useMemo2,
57136
+ useRef as useRef3
57135
57137
  } from "react";
57138
+
57139
+ // src/react/components.tsx
57140
+ import { useRef as useRef2, useEffect as useEffect2, useState as useState2, useMemo } from "react";
57141
+
57142
+ // src/react/chat-input.tsx
57143
+ import { useRef, useEffect, useCallback, useState } from "react";
57136
57144
  import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
57145
+ function SendIcon() {
57146
+ return /* @__PURE__ */ jsxDEV("svg", {
57147
+ width: "14",
57148
+ height: "14",
57149
+ viewBox: "0 0 24 24",
57150
+ fill: "none",
57151
+ stroke: "currentColor",
57152
+ strokeWidth: "2",
57153
+ strokeLinecap: "round",
57154
+ strokeLinejoin: "round",
57155
+ children: [
57156
+ /* @__PURE__ */ jsxDEV("line", {
57157
+ x1: "5",
57158
+ y1: "12",
57159
+ x2: "19",
57160
+ y2: "12"
57161
+ }, undefined, false, undefined, this),
57162
+ /* @__PURE__ */ jsxDEV("polyline", {
57163
+ points: "12 5 19 12 12 19"
57164
+ }, undefined, false, undefined, this)
57165
+ ]
57166
+ }, undefined, true, undefined, this);
57167
+ }
57168
+ function StopIcon() {
57169
+ return /* @__PURE__ */ jsxDEV("svg", {
57170
+ width: "14",
57171
+ height: "14",
57172
+ viewBox: "0 0 24 24",
57173
+ fill: "currentColor",
57174
+ children: /* @__PURE__ */ jsxDEV("rect", {
57175
+ x: "6",
57176
+ y: "6",
57177
+ width: "12",
57178
+ height: "12"
57179
+ }, undefined, false, undefined, this)
57180
+ }, undefined, false, undefined, this);
57181
+ }
57182
+ function ImageIcon() {
57183
+ return /* @__PURE__ */ jsxDEV("svg", {
57184
+ width: "14",
57185
+ height: "14",
57186
+ viewBox: "0 0 24 24",
57187
+ fill: "none",
57188
+ stroke: "currentColor",
57189
+ strokeWidth: "2",
57190
+ strokeLinecap: "round",
57191
+ strokeLinejoin: "round",
57192
+ children: [
57193
+ /* @__PURE__ */ jsxDEV("rect", {
57194
+ x: "3",
57195
+ y: "3",
57196
+ width: "18",
57197
+ height: "18",
57198
+ rx: "0",
57199
+ ry: "0"
57200
+ }, undefined, false, undefined, this),
57201
+ /* @__PURE__ */ jsxDEV("circle", {
57202
+ cx: "8.5",
57203
+ cy: "8.5",
57204
+ r: "1.5"
57205
+ }, undefined, false, undefined, this),
57206
+ /* @__PURE__ */ jsxDEV("polyline", {
57207
+ points: "21 15 16 10 5 21"
57208
+ }, undefined, false, undefined, this)
57209
+ ]
57210
+ }, undefined, true, undefined, this);
57211
+ }
57212
+ function XIcon() {
57213
+ return /* @__PURE__ */ jsxDEV("svg", {
57214
+ width: "10",
57215
+ height: "10",
57216
+ viewBox: "0 0 24 24",
57217
+ fill: "none",
57218
+ stroke: "currentColor",
57219
+ strokeWidth: "3",
57220
+ strokeLinecap: "round",
57221
+ strokeLinejoin: "round",
57222
+ children: [
57223
+ /* @__PURE__ */ jsxDEV("line", {
57224
+ x1: "18",
57225
+ y1: "6",
57226
+ x2: "6",
57227
+ y2: "18"
57228
+ }, undefined, false, undefined, this),
57229
+ /* @__PURE__ */ jsxDEV("line", {
57230
+ x1: "6",
57231
+ y1: "6",
57232
+ x2: "18",
57233
+ y2: "18"
57234
+ }, undefined, false, undefined, this)
57235
+ ]
57236
+ }, undefined, true, undefined, this);
57237
+ }
57238
+ var STYLE_ID = "__llm-chat-input-styles";
57239
+ function injectStyles(theme) {
57240
+ if (typeof document === "undefined")
57241
+ return;
57242
+ const existing = document.getElementById(STYLE_ID);
57243
+ if (existing)
57244
+ existing.remove();
57245
+ const d = theme === "dark";
57246
+ const border = d ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
57247
+ const borderFocus = d ? "rgba(255,255,255,0.35)" : "rgba(0,0,0,0.35)";
57248
+ const bg2 = d ? "#000000" : "#ffffff";
57249
+ const text = d ? "#ffffff" : "#000000";
57250
+ const textMuted = d ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.3)";
57251
+ const textSecondary = d ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
57252
+ const monoFont = `ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace`;
57253
+ const sansFont = `-apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, Helvetica, Arial, sans-serif`;
57254
+ const css = `
57255
+ .llm-ci {
57256
+ display: flex;
57257
+ flex-direction: column;
57258
+ border: 1px solid ${border};
57259
+ background: ${bg2};
57260
+ padding: 12px;
57261
+ transition: border-color 0.15s;
57262
+ position: relative;
57263
+ }
57264
+ .llm-ci:focus-within {
57265
+ border-color: ${borderFocus};
57266
+ }
57267
+
57268
+ /* Attached Images */
57269
+ .llm-ci-images {
57270
+ display: flex;
57271
+ flex-wrap: wrap;
57272
+ gap: 6px;
57273
+ margin-bottom: 8px;
57274
+ }
57275
+ .llm-ci-image-preview {
57276
+ position: relative;
57277
+ width: 52px;
57278
+ height: 52px;
57279
+ overflow: hidden;
57280
+ border: 1px solid ${border};
57281
+ }
57282
+ .llm-ci-image-preview img {
57283
+ width: 100%;
57284
+ height: 100%;
57285
+ object-fit: cover;
57286
+ }
57287
+ .llm-ci-image-remove {
57288
+ position: absolute;
57289
+ top: 2px;
57290
+ right: 2px;
57291
+ width: 16px;
57292
+ height: 16px;
57293
+ background: ${d ? "rgba(0,0,0,0.7)" : "rgba(255,255,255,0.8)"};
57294
+ color: ${text};
57295
+ border: none;
57296
+ display: flex;
57297
+ align-items: center;
57298
+ justify-content: center;
57299
+ cursor: pointer;
57300
+ opacity: 0;
57301
+ transition: opacity 0.1s;
57302
+ }
57303
+ .llm-ci-image-preview:hover .llm-ci-image-remove {
57304
+ opacity: 1;
57305
+ }
57306
+
57307
+ /* Textarea */
57308
+ .llm-ci textarea {
57309
+ width: 100%;
57310
+ min-height: 24px;
57311
+ max-height: 200px;
57312
+ resize: none;
57313
+ border: none;
57314
+ outline: none;
57315
+ background: transparent;
57316
+ color: ${text};
57317
+ font-size: 14px;
57318
+ line-height: 1.5;
57319
+ padding: 0;
57320
+ margin: 0;
57321
+ font-family: ${sansFont};
57322
+ scrollbar-width: thin;
57323
+ }
57324
+ .llm-ci textarea::placeholder {
57325
+ color: ${textMuted};
57326
+ }
57327
+
57328
+ /* Toolbar */
57329
+ .llm-ci-toolbar {
57330
+ display: flex;
57331
+ align-items: center;
57332
+ justify-content: space-between;
57333
+ margin-top: 8px;
57334
+ min-height: 28px;
57335
+ }
57336
+ .llm-ci-left-actions {
57337
+ display: flex;
57338
+ align-items: center;
57339
+ gap: 2px;
57340
+ }
57341
+ .llm-ci-right-actions {
57342
+ display: flex;
57343
+ align-items: center;
57344
+ gap: 8px;
57345
+ }
57346
+
57347
+ /* Icon Buttons */
57348
+ .llm-ci-btn-icon {
57349
+ display: flex;
57350
+ align-items: center;
57351
+ justify-content: center;
57352
+ width: 28px;
57353
+ height: 28px;
57354
+ border: none;
57355
+ background: transparent;
57356
+ color: ${textSecondary};
57357
+ cursor: pointer;
57358
+ transition: color 0.1s;
57359
+ }
57360
+ .llm-ci-btn-icon:hover {
57361
+ color: ${text};
57362
+ }
57363
+
57364
+ /* Send / Stop — flat, monospace, uppercase */
57365
+ .llm-ci-send {
57366
+ display: flex;
57367
+ align-items: center;
57368
+ justify-content: center;
57369
+ height: 28px;
57370
+ padding: 0 12px;
57371
+ border: 1px solid ${border};
57372
+ font-size: 10px;
57373
+ font-weight: 400;
57374
+ font-family: ${monoFont};
57375
+ text-transform: uppercase;
57376
+ letter-spacing: 0.08em;
57377
+ cursor: pointer;
57378
+ transition: all 0.1s;
57379
+ gap: 6px;
57380
+ background: transparent;
57381
+ color: ${textSecondary};
57382
+ }
57383
+ .llm-ci-send--active {
57384
+ background: ${text};
57385
+ color: ${bg2};
57386
+ border-color: ${text};
57387
+ }
57388
+ .llm-ci-send--active:hover {
57389
+ opacity: 0.8;
57390
+ }
57391
+ .llm-ci-send--disabled {
57392
+ color: ${textMuted};
57393
+ border-color: ${d ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)"};
57394
+ cursor: not-allowed;
57395
+ }
57396
+ .llm-ci-send--stop {
57397
+ background: transparent;
57398
+ border-color: ${border};
57399
+ color: ${textSecondary};
57400
+ }
57401
+ .llm-ci-send--stop:hover {
57402
+ border-color: ${borderFocus};
57403
+ color: ${text};
57404
+ }
57405
+
57406
+ /* Model Selector Slot */
57407
+ .llm-ci-model-selector {
57408
+ margin-right: auto;
57409
+ }
57410
+
57411
+ /* Hidden file input */
57412
+ .llm-ci-file-input {
57413
+ display: none;
57414
+ }
57415
+
57416
+ /* Drag Overlay */
57417
+ .llm-ci-drag-overlay {
57418
+ position: absolute;
57419
+ inset: 0;
57420
+ background: ${d ? "rgba(0,0,0,0.9)" : "rgba(255,255,255,0.9)"};
57421
+ display: flex;
57422
+ align-items: center;
57423
+ justify-content: center;
57424
+ z-index: 10;
57425
+ border: 1px dashed ${textSecondary};
57426
+ color: ${textSecondary};
57427
+ font-size: 11px;
57428
+ font-family: ${monoFont};
57429
+ text-transform: uppercase;
57430
+ letter-spacing: 0.08em;
57431
+ }
57432
+ `;
57433
+ const style = document.createElement("style");
57434
+ style.id = STYLE_ID;
57435
+ style.textContent = css;
57436
+ document.head.appendChild(style);
57437
+ }
57438
+ function ChatInput({
57439
+ value,
57440
+ onChange,
57441
+ onSend,
57442
+ onStop,
57443
+ disabled = false,
57444
+ isGenerating = false,
57445
+ placeholder = "Type a message...",
57446
+ maxRows = 8,
57447
+ actions,
57448
+ theme = "dark",
57449
+ className,
57450
+ images = [],
57451
+ onImageAdd,
57452
+ onImageRemove,
57453
+ modelSelector
57454
+ }) {
57455
+ const textareaRef = useRef(null);
57456
+ const fileInputRef = useRef(null);
57457
+ const [isDragging, setIsDragging] = useState(false);
57458
+ useEffect(() => {
57459
+ injectStyles(theme);
57460
+ }, [theme]);
57461
+ useEffect(() => {
57462
+ const textarea = textareaRef.current;
57463
+ if (!textarea)
57464
+ return;
57465
+ textarea.style.height = "auto";
57466
+ const lineHeight = 21;
57467
+ const maxHeight = lineHeight * maxRows + 24;
57468
+ const newHeight = Math.min(textarea.scrollHeight, maxHeight);
57469
+ textarea.style.height = `${newHeight}px`;
57470
+ }, [value, maxRows]);
57471
+ const handleKeyDown = useCallback((e) => {
57472
+ if (e.key === "Enter" && !e.shiftKey) {
57473
+ e.preventDefault();
57474
+ if (!disabled && (value.trim() || images.length > 0)) {
57475
+ onSend();
57476
+ }
57477
+ }
57478
+ }, [disabled, value, images.length, onSend]);
57479
+ const processFile = useCallback((file) => {
57480
+ if (!file.type.startsWith("image/"))
57481
+ return;
57482
+ const reader = new FileReader;
57483
+ reader.onload = (e) => {
57484
+ if (e.target?.result && typeof e.target.result === "string") {
57485
+ const id2 = Math.random().toString(36).substring(7);
57486
+ onImageAdd?.({
57487
+ id: id2,
57488
+ dataUrl: e.target.result,
57489
+ file,
57490
+ name: file.name
57491
+ });
57492
+ }
57493
+ };
57494
+ reader.readAsDataURL(file);
57495
+ }, [onImageAdd]);
57496
+ const handlePaste = useCallback((e) => {
57497
+ const items = e.clipboardData.items;
57498
+ for (let i = 0;i < items.length; i++) {
57499
+ const item = items[i];
57500
+ if (item && item.type.indexOf("image") !== -1) {
57501
+ const file = item.getAsFile();
57502
+ if (file)
57503
+ processFile(file);
57504
+ }
57505
+ }
57506
+ }, [processFile]);
57507
+ const handleDrop = useCallback((e) => {
57508
+ e.preventDefault();
57509
+ setIsDragging(false);
57510
+ const files = e.dataTransfer.files;
57511
+ for (let i = 0;i < files.length; i++) {
57512
+ const file = files.item(i);
57513
+ if (file)
57514
+ processFile(file);
57515
+ }
57516
+ }, [processFile]);
57517
+ const handleFileSelect = useCallback((e) => {
57518
+ const files = e.target.files;
57519
+ if (!files)
57520
+ return;
57521
+ for (let i = 0;i < files.length; i++) {
57522
+ const file = files.item(i);
57523
+ if (file)
57524
+ processFile(file);
57525
+ }
57526
+ e.target.value = "";
57527
+ }, [processFile]);
57528
+ const hasValue = value.trim().length > 0 || images.length > 0;
57529
+ const canSend = hasValue && !disabled && !isGenerating;
57530
+ let sendClass = "llm-ci-send";
57531
+ if (isGenerating) {
57532
+ sendClass += " llm-ci-send--stop";
57533
+ } else if (canSend) {
57534
+ sendClass += " llm-ci-send--active";
57535
+ } else {
57536
+ sendClass += " llm-ci-send--disabled";
57537
+ }
57538
+ return /* @__PURE__ */ jsxDEV("div", {
57539
+ className: `llm-ci${className ? ` ${className}` : ""}`,
57540
+ onDragOver: (e) => {
57541
+ e.preventDefault();
57542
+ setIsDragging(true);
57543
+ },
57544
+ onDragLeave: () => setIsDragging(false),
57545
+ onDrop: handleDrop,
57546
+ children: [
57547
+ isDragging && /* @__PURE__ */ jsxDEV("div", {
57548
+ className: "llm-ci-drag-overlay",
57549
+ children: "[Drop image to attach]"
57550
+ }, undefined, false, undefined, this),
57551
+ /* @__PURE__ */ jsxDEV("input", {
57552
+ ref: fileInputRef,
57553
+ type: "file",
57554
+ accept: "image/*",
57555
+ multiple: true,
57556
+ className: "llm-ci-file-input",
57557
+ onChange: handleFileSelect
57558
+ }, undefined, false, undefined, this),
57559
+ images.length > 0 && /* @__PURE__ */ jsxDEV("div", {
57560
+ className: "llm-ci-images",
57561
+ children: images.map((img) => /* @__PURE__ */ jsxDEV("div", {
57562
+ className: "llm-ci-image-preview",
57563
+ children: [
57564
+ /* @__PURE__ */ jsxDEV("img", {
57565
+ src: img.dataUrl,
57566
+ alt: "attachment"
57567
+ }, undefined, false, undefined, this),
57568
+ /* @__PURE__ */ jsxDEV("button", {
57569
+ type: "button",
57570
+ className: "llm-ci-image-remove",
57571
+ onClick: () => onImageRemove?.(img.id),
57572
+ children: /* @__PURE__ */ jsxDEV(XIcon, {}, undefined, false, undefined, this)
57573
+ }, undefined, false, undefined, this)
57574
+ ]
57575
+ }, img.id, true, undefined, this))
57576
+ }, undefined, false, undefined, this),
57577
+ /* @__PURE__ */ jsxDEV("textarea", {
57578
+ ref: textareaRef,
57579
+ value,
57580
+ onChange: (e) => onChange(e.target.value),
57581
+ onKeyDown: handleKeyDown,
57582
+ onPaste: handlePaste,
57583
+ placeholder,
57584
+ disabled: disabled && !isGenerating,
57585
+ rows: 1
57586
+ }, undefined, false, undefined, this),
57587
+ /* @__PURE__ */ jsxDEV("div", {
57588
+ className: "llm-ci-toolbar",
57589
+ children: [
57590
+ /* @__PURE__ */ jsxDEV("div", {
57591
+ className: "llm-ci-left-actions",
57592
+ children: [
57593
+ modelSelector && /* @__PURE__ */ jsxDEV("div", {
57594
+ className: "llm-ci-model-selector",
57595
+ children: modelSelector
57596
+ }, undefined, false, undefined, this),
57597
+ /* @__PURE__ */ jsxDEV("button", {
57598
+ type: "button",
57599
+ className: "llm-ci-btn-icon",
57600
+ onClick: () => fileInputRef.current?.click(),
57601
+ title: "Attach image (or Ctrl+V to paste)",
57602
+ children: /* @__PURE__ */ jsxDEV(ImageIcon, {}, undefined, false, undefined, this)
57603
+ }, undefined, false, undefined, this),
57604
+ actions
57605
+ ]
57606
+ }, undefined, true, undefined, this),
57607
+ /* @__PURE__ */ jsxDEV("div", {
57608
+ className: "llm-ci-right-actions",
57609
+ children: /* @__PURE__ */ jsxDEV("button", {
57610
+ type: "button",
57611
+ className: sendClass,
57612
+ onClick: isGenerating ? onStop : onSend,
57613
+ disabled: !isGenerating && !canSend,
57614
+ "aria-label": isGenerating ? "Stop generation" : "Send message",
57615
+ children: isGenerating ? /* @__PURE__ */ jsxDEV(Fragment, {
57616
+ children: [
57617
+ /* @__PURE__ */ jsxDEV(StopIcon, {}, undefined, false, undefined, this),
57618
+ " Stop"
57619
+ ]
57620
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV(Fragment, {
57621
+ children: [
57622
+ "Send ",
57623
+ /* @__PURE__ */ jsxDEV(SendIcon, {}, undefined, false, undefined, this)
57624
+ ]
57625
+ }, undefined, true, undefined, this)
57626
+ }, undefined, false, undefined, this)
57627
+ }, undefined, false, undefined, this)
57628
+ ]
57629
+ }, undefined, true, undefined, this)
57630
+ ]
57631
+ }, undefined, true, undefined, this);
57632
+ }
57633
+
57634
+ // src/react/components.tsx
57635
+ import { Streamdown } from "streamdown";
57636
+ import { code } from "@streamdown/code";
57637
+ import { mermaid } from "@streamdown/mermaid";
57638
+ import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
57639
+ var DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant.
57640
+ - You can use full Markdown (bold, italic, headers, lists).
57641
+ - You can use Code Blocks with language syntax highlighting.
57642
+ - You can use Mermaid diagrams (\`\`\`mermaid ... \`\`\`).
57643
+ - You can use LaTeX math ($$ ... $$).`;
57644
+ var ALL_MODELS = { ...WEBLLM_MODELS, ...TRANSFORMERS_MODELS };
57645
+ function RetryIcon() {
57646
+ return /* @__PURE__ */ jsxDEV2("svg", {
57647
+ width: "14",
57648
+ height: "14",
57649
+ viewBox: "0 0 24 24",
57650
+ fill: "none",
57651
+ stroke: "currentColor",
57652
+ strokeWidth: "2",
57653
+ strokeLinecap: "round",
57654
+ strokeLinejoin: "round",
57655
+ children: [
57656
+ /* @__PURE__ */ jsxDEV2("polyline", {
57657
+ points: "23 4 23 10 17 10"
57658
+ }, undefined, false, undefined, this),
57659
+ /* @__PURE__ */ jsxDEV2("path", {
57660
+ d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10"
57661
+ }, undefined, false, undefined, this)
57662
+ ]
57663
+ }, undefined, true, undefined, this);
57664
+ }
57665
+ function ChevronDownIcon() {
57666
+ return /* @__PURE__ */ jsxDEV2("svg", {
57667
+ width: "12",
57668
+ height: "12",
57669
+ viewBox: "0 0 24 24",
57670
+ fill: "none",
57671
+ stroke: "currentColor",
57672
+ strokeWidth: "2",
57673
+ strokeLinecap: "round",
57674
+ strokeLinejoin: "round",
57675
+ children: /* @__PURE__ */ jsxDEV2("polyline", {
57676
+ points: "6 9 12 15 18 9"
57677
+ }, undefined, false, undefined, this)
57678
+ }, undefined, false, undefined, this);
57679
+ }
57680
+ var STYLE_ID2 = "__llm-chat-styles";
57681
+ function injectChatStyles(theme) {
57682
+ if (typeof document === "undefined")
57683
+ return;
57684
+ const existing = document.getElementById(STYLE_ID2);
57685
+ if (existing)
57686
+ existing.remove();
57687
+ const d = theme === "dark";
57688
+ const bg2 = d ? "#000000" : "#ffffff";
57689
+ const border = d ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
57690
+ const borderSubtle = d ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)";
57691
+ const text = d ? "#ffffff" : "#000000";
57692
+ const textSecondary = d ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
57693
+ const textTertiary = d ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.3)";
57694
+ const surfaceSubtle = d ? "rgba(255,255,255,0.04)" : "rgba(0,0,0,0.03)";
57695
+ const monoFont = `ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace`;
57696
+ const sansFont = `-apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, Helvetica, Arial, sans-serif`;
57697
+ const css = `
57698
+ .llm-chat {
57699
+ display: flex;
57700
+ flex-direction: column;
57701
+ border: 1px solid ${border};
57702
+ background: ${bg2};
57703
+ overflow: hidden;
57704
+ font-family: ${sansFont};
57705
+ color: ${text};
57706
+ }
57707
+
57708
+ /* Header */
57709
+ .llm-chat-header {
57710
+ display: flex;
57711
+ align-items: center;
57712
+ justify-content: space-between;
57713
+ padding: 12px 16px;
57714
+ border-bottom: 1px solid ${borderSubtle};
57715
+ }
57716
+
57717
+ /* Model Selector */
57718
+ .llm-chat-model-select {
57719
+ position: relative;
57720
+ }
57721
+ .llm-chat-model-btn {
57722
+ display: flex;
57723
+ align-items: center;
57724
+ gap: 6px;
57725
+ background: transparent;
57726
+ border: none;
57727
+ color: ${text};
57728
+ font-weight: 400;
57729
+ font-size: 11px;
57730
+ font-family: ${monoFont};
57731
+ text-transform: uppercase;
57732
+ letter-spacing: 0.05em;
57733
+ padding: 4px 0;
57734
+ cursor: pointer;
57735
+ transition: opacity 0.1s;
57736
+ }
57737
+ .llm-chat-model-btn:hover {
57738
+ opacity: 0.6;
57739
+ }
57740
+ .llm-chat-model-menu {
57741
+ position: absolute;
57742
+ top: 100%;
57743
+ left: 0;
57744
+ margin-top: 4px;
57745
+ width: 280px;
57746
+ max-height: 300px;
57747
+ overflow-y: auto;
57748
+ background: ${d ? "#0a0a0a" : "#fafafa"};
57749
+ border: 1px solid ${border};
57750
+ z-index: 50;
57751
+ padding: 4px 0;
57752
+ scrollbar-width: thin;
57753
+ }
57754
+ .llm-chat-model-group {
57755
+ padding: 8px 12px 4px;
57756
+ font-size: 10px;
57757
+ font-weight: 400;
57758
+ color: ${textSecondary};
57759
+ text-transform: uppercase;
57760
+ letter-spacing: 0.08em;
57761
+ font-family: ${monoFont};
57762
+ }
57763
+ .llm-chat-model-item {
57764
+ display: block;
57765
+ width: 100%;
57766
+ text-align: left;
57767
+ padding: 6px 12px;
57768
+ font-size: 12px;
57769
+ font-family: ${monoFont};
57770
+ color: ${textSecondary};
57771
+ background: transparent;
57772
+ border: none;
57773
+ cursor: pointer;
57774
+ white-space: nowrap;
57775
+ overflow: hidden;
57776
+ text-overflow: ellipsis;
57777
+ transition: color 0.1s;
57778
+ }
57779
+ .llm-chat-model-item:hover {
57780
+ color: ${text};
57781
+ }
57782
+ .llm-chat-model-item--active {
57783
+ color: ${text};
57784
+ }
57785
+
57786
+ /* Status */
57787
+ .llm-chat-status {
57788
+ display: flex;
57789
+ align-items: center;
57790
+ gap: 8px;
57791
+ font-size: 10px;
57792
+ font-family: ${monoFont};
57793
+ text-transform: uppercase;
57794
+ letter-spacing: 0.08em;
57795
+ color: ${textSecondary};
57796
+ }
57797
+ .llm-chat-dot {
57798
+ width: 5px;
57799
+ height: 5px;
57800
+ border-radius: 50%;
57801
+ }
57802
+ .llm-chat-dot--loading {
57803
+ background: ${textSecondary};
57804
+ animation: llm-pulse 1.5s infinite;
57805
+ }
57806
+ .llm-chat-dot--ready {
57807
+ background: ${text};
57808
+ }
57809
+ .llm-chat-dot--error {
57810
+ background: #ff3333;
57811
+ }
57812
+
57813
+ /* Progress */
57814
+ .llm-chat-progress {
57815
+ padding: 0 16px 8px;
57816
+ }
57817
+ .llm-chat-progress-bar {
57818
+ height: 1px;
57819
+ background: ${borderSubtle};
57820
+ overflow: hidden;
57821
+ }
57822
+ .llm-chat-progress-fill {
57823
+ height: 100%;
57824
+ background: ${text};
57825
+ transition: width 0.3s ease;
57826
+ }
57827
+ .llm-chat-progress-text {
57828
+ font-size: 10px;
57829
+ font-family: ${monoFont};
57830
+ color: ${textTertiary};
57831
+ margin-top: 4px;
57832
+ text-align: right;
57833
+ text-transform: uppercase;
57834
+ letter-spacing: 0.05em;
57835
+ }
57836
+
57837
+ /* Messages */
57838
+ .llm-chat-messages {
57839
+ flex: 1;
57840
+ overflow-y: auto;
57841
+ padding: 20px 16px;
57842
+ display: flex;
57843
+ flex-direction: column;
57844
+ gap: 24px;
57845
+ scrollbar-width: thin;
57846
+ }
57847
+
57848
+ /* Welcome */
57849
+ .llm-chat-welcome {
57850
+ display: flex;
57851
+ flex-direction: column;
57852
+ align-items: flex-start;
57853
+ justify-content: center;
57854
+ flex: 1;
57855
+ color: ${textTertiary};
57856
+ padding: 40px 0;
57857
+ }
57858
+ .llm-chat-welcome h3 {
57859
+ font-size: 11px;
57860
+ font-weight: 400;
57861
+ font-family: ${monoFont};
57862
+ text-transform: uppercase;
57863
+ letter-spacing: 0.08em;
57864
+ color: ${textSecondary};
57865
+ margin: 0 0 8px;
57866
+ }
57867
+ .llm-chat-welcome p {
57868
+ font-size: 13px;
57869
+ margin: 0;
57870
+ max-width: 360px;
57871
+ line-height: 1.6;
57872
+ color: ${textTertiary};
57873
+ }
57874
+
57875
+ /* Bubble */
57876
+ .llm-chat-bubble {
57877
+ display: flex;
57878
+ flex-direction: column;
57879
+ max-width: 100%;
57880
+ }
57881
+ .llm-chat-bubble--user {
57882
+ align-self: flex-end;
57883
+ max-width: 80%;
57884
+ }
57885
+ .llm-chat-bubble--assistant {
57886
+ align-self: flex-start;
57887
+ width: 100%;
57888
+ }
57889
+
57890
+ /* User message — flat, subtle bg, no radius */
57891
+ .llm-chat-user-content {
57892
+ padding: 10px 14px;
57893
+ font-size: 14px;
57894
+ line-height: 1.6;
57895
+ white-space: pre-wrap;
57896
+ background: ${surfaceSubtle};
57897
+ border: 1px solid ${borderSubtle};
57898
+ color: ${text};
57899
+ }
57900
+
57901
+ /* Assistant message */
57902
+ .llm-chat-assistant-content {
57903
+ font-size: 14px;
57904
+ line-height: 1.7;
57905
+ color: ${d ? "rgba(255,255,255,0.85)" : "rgba(0,0,0,0.85)"};
57906
+ }
57907
+
57908
+ /* Streamdown Overrides */
57909
+ .llm-chat-assistant-content pre {
57910
+ background: ${surfaceSubtle} !important;
57911
+ border: 1px solid ${borderSubtle} !important;
57912
+ border-radius: 0 !important;
57913
+ padding: 12px !important;
57914
+ margin: 12px 0 !important;
57915
+ }
57916
+ .llm-chat-assistant-content code {
57917
+ font-family: ${monoFont} !important;
57918
+ font-size: 13px !important;
57919
+ }
57920
+ .llm-chat-assistant-content :not(pre) > code {
57921
+ background: ${surfaceSubtle};
57922
+ border: 1px solid ${borderSubtle};
57923
+ padding: 1px 5px;
57924
+ font-size: 12.5px !important;
57925
+ }
57926
+
57927
+ /* Attachments in message */
57928
+ .llm-chat-msg-images {
57929
+ display: flex;
57930
+ flex-wrap: wrap;
57931
+ gap: 6px;
57932
+ margin-bottom: 8px;
57933
+ justify-content: flex-end;
57934
+ }
57935
+ .llm-chat-msg-img {
57936
+ width: 100px;
57937
+ height: 100px;
57938
+ object-fit: cover;
57939
+ border: 1px solid ${border};
57940
+ }
57941
+
57942
+ /* Error */
57943
+ .llm-chat-error {
57944
+ margin: 0 16px;
57945
+ padding: 10px 14px;
57946
+ border: 1px solid ${d ? "rgba(255,50,50,0.2)" : "rgba(200,0,0,0.15)"};
57947
+ display: flex;
57948
+ align-items: center;
57949
+ justify-content: space-between;
57950
+ gap: 12px;
57951
+ }
57952
+ .llm-chat-error-text {
57953
+ font-size: 12px;
57954
+ font-family: ${monoFont};
57955
+ color: ${d ? "#ff6666" : "#cc0000"};
57956
+ flex: 1;
57957
+ }
57958
+ .llm-chat-error-retry {
57959
+ display: flex;
57960
+ align-items: center;
57961
+ gap: 4px;
57962
+ padding: 4px 10px;
57963
+ border: 1px solid ${d ? "rgba(255,50,50,0.3)" : "rgba(200,0,0,0.2)"};
57964
+ background: transparent;
57965
+ color: ${d ? "#ff6666" : "#cc0000"};
57966
+ font-size: 11px;
57967
+ font-family: ${monoFont};
57968
+ text-transform: uppercase;
57969
+ letter-spacing: 0.05em;
57970
+ cursor: pointer;
57971
+ }
57972
+ .llm-chat-error-retry:hover {
57973
+ background: ${d ? "rgba(255,50,50,0.08)" : "rgba(200,0,0,0.04)"};
57974
+ }
57975
+
57976
+ /* Input Area */
57977
+ .llm-chat-input-area {
57978
+ padding: 12px 16px 16px;
57979
+ border-top: 1px solid ${borderSubtle};
57980
+ }
57981
+
57982
+ @keyframes llm-pulse {
57983
+ 0%, 100% { opacity: 1; }
57984
+ 50% { opacity: 0.2; }
57985
+ }
57986
+ `;
57987
+ const style = document.createElement("style");
57988
+ style.id = STYLE_ID2;
57989
+ style.textContent = css;
57990
+ document.head.appendChild(style);
57991
+ }
57992
+ function ModelSelector({
57993
+ currentModel,
57994
+ onSelect,
57995
+ theme
57996
+ }) {
57997
+ const [isOpen, setIsOpen] = useState2(false);
57998
+ const ref = useRef2(null);
57999
+ useEffect2(() => {
58000
+ function handleClickOutside(event) {
58001
+ if (ref.current && !ref.current.contains(event.target)) {
58002
+ setIsOpen(false);
58003
+ }
58004
+ }
58005
+ document.addEventListener("mousedown", handleClickOutside);
58006
+ return () => document.removeEventListener("mousedown", handleClickOutside);
58007
+ }, []);
58008
+ const displayModel = useMemo(() => {
58009
+ if (!currentModel)
58010
+ return "Select Model";
58011
+ const id2 = currentModel.split("/").pop() || currentModel;
58012
+ return id2.length > 25 ? id2.substring(0, 25) + "..." : id2;
58013
+ }, [currentModel]);
58014
+ return /* @__PURE__ */ jsxDEV2("div", {
58015
+ className: "llm-chat-model-select",
58016
+ ref,
58017
+ children: [
58018
+ /* @__PURE__ */ jsxDEV2("button", {
58019
+ type: "button",
58020
+ className: "llm-chat-model-btn",
58021
+ onClick: () => setIsOpen(!isOpen),
58022
+ children: [
58023
+ "[",
58024
+ displayModel,
58025
+ "] ",
58026
+ /* @__PURE__ */ jsxDEV2(ChevronDownIcon, {}, undefined, false, undefined, this)
58027
+ ]
58028
+ }, undefined, true, undefined, this),
58029
+ isOpen && /* @__PURE__ */ jsxDEV2("div", {
58030
+ className: "llm-chat-model-menu",
58031
+ children: [
58032
+ /* @__PURE__ */ jsxDEV2("div", {
58033
+ className: "llm-chat-model-group",
58034
+ children: "[WebLLM]"
58035
+ }, undefined, false, undefined, this),
58036
+ Object.entries(WEBLLM_MODELS).map(([key, value]) => /* @__PURE__ */ jsxDEV2("button", {
58037
+ className: `llm-chat-model-item ${currentModel === value ? "llm-chat-model-item--active" : ""}`,
58038
+ onClick: () => {
58039
+ onSelect(value);
58040
+ setIsOpen(false);
58041
+ },
58042
+ children: key
58043
+ }, key, false, undefined, this)),
58044
+ /* @__PURE__ */ jsxDEV2("div", {
58045
+ className: "llm-chat-model-group",
58046
+ children: "[Transformers.js]"
58047
+ }, undefined, false, undefined, this),
58048
+ Object.entries(TRANSFORMERS_MODELS).map(([key, value]) => /* @__PURE__ */ jsxDEV2("button", {
58049
+ className: `llm-chat-model-item ${currentModel === value ? "llm-chat-model-item--active" : ""}`,
58050
+ onClick: () => {
58051
+ onSelect(value);
58052
+ setIsOpen(false);
58053
+ },
58054
+ children: key
58055
+ }, key, false, undefined, this))
58056
+ ]
58057
+ }, undefined, true, undefined, this)
58058
+ ]
58059
+ }, undefined, true, undefined, this);
58060
+ }
58061
+ function Chat2({
58062
+ systemPrompt = DEFAULT_SYSTEM_PROMPT,
58063
+ placeholder = "Type a message...",
58064
+ theme = "dark",
58065
+ className,
58066
+ maxHeight = "600px",
58067
+ inputActions,
58068
+ onSend: onSendProp,
58069
+ onResponse,
58070
+ onError: onErrorProp,
58071
+ showHeader = true,
58072
+ showProgress = true,
58073
+ welcomeMessage = "Ready to assist",
58074
+ onModelChange
58075
+ }) {
58076
+ const { llm, isLoading, isReady, loadProgress, error, modelId, reload } = useLLM();
58077
+ const [messages, setMessages] = useState2([]);
58078
+ const [input, setInput] = useState2("");
58079
+ const [isGenerating, setIsGenerating] = useState2(false);
58080
+ const [streamingText, setStreamingText] = useState2("");
58081
+ const [pendingMessage, setPendingMessage] = useState2(null);
58082
+ const [images, setImages] = useState2([]);
58083
+ const messagesEndRef = useRef2(null);
58084
+ const abortRef = useRef2(false);
58085
+ const isProcessingRef = useRef2(false);
58086
+ useEffect2(() => {
58087
+ injectChatStyles(theme);
58088
+ }, [theme]);
58089
+ useEffect2(() => {
58090
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
58091
+ }, [messages, streamingText, isGenerating]);
58092
+ const generate = async (userContent, currentMessages, attachedImages = []) => {
58093
+ if (!llm || !isReady || isProcessingRef.current)
58094
+ return;
58095
+ isProcessingRef.current = true;
58096
+ const userMsg = {
58097
+ role: "user",
58098
+ content: userContent,
58099
+ images: attachedImages
58100
+ };
58101
+ setMessages((prev) => [...prev, userMsg]);
58102
+ setIsGenerating(true);
58103
+ setStreamingText("");
58104
+ abortRef.current = false;
58105
+ const apiMessages = [];
58106
+ if (systemPrompt) {
58107
+ apiMessages.push({ role: "system", content: systemPrompt });
58108
+ }
58109
+ currentMessages.forEach((m) => {
58110
+ apiMessages.push({ role: m.role, content: m.content });
58111
+ });
58112
+ apiMessages.push({ role: "user", content: userContent });
58113
+ try {
58114
+ const response = await llm.stream(apiMessages, (_token, fullText) => {
58115
+ if (abortRef.current)
58116
+ return;
58117
+ setStreamingText(fullText);
58118
+ });
58119
+ if (!abortRef.current) {
58120
+ const assistantMsg = { role: "assistant", content: response };
58121
+ setMessages((prev) => [...prev, assistantMsg]);
58122
+ setStreamingText("");
58123
+ onResponse?.(response);
58124
+ }
58125
+ } catch (err) {
58126
+ const error2 = err instanceof Error ? err : new Error(String(err));
58127
+ onErrorProp?.(error2);
58128
+ setMessages((prev) => [...prev, {
58129
+ role: "assistant",
58130
+ content: `Error: ${error2.message}`
58131
+ }]);
58132
+ } finally {
58133
+ setIsGenerating(false);
58134
+ isProcessingRef.current = false;
58135
+ }
58136
+ };
58137
+ const handleSend = () => {
58138
+ const text = input.trim();
58139
+ if (!text && images.length === 0)
58140
+ return;
58141
+ const currentImages = [...images];
58142
+ setInput("");
58143
+ setImages([]);
58144
+ onSendProp?.(text);
58145
+ if (llm && isReady) {
58146
+ generate(text, messages, currentImages);
58147
+ } else if (isLoading) {
58148
+ setPendingMessage(text);
58149
+ }
58150
+ };
58151
+ const handleStop = () => {
58152
+ abortRef.current = true;
58153
+ setIsGenerating(false);
58154
+ if (streamingText) {
58155
+ setMessages((prev) => [...prev, { role: "assistant", content: streamingText + "..." }]);
58156
+ setStreamingText("");
58157
+ }
58158
+ };
58159
+ const statusDotClass = error ? "llm-chat-dot llm-chat-dot--error" : isReady ? "llm-chat-dot llm-chat-dot--ready" : "llm-chat-dot llm-chat-dot--loading";
58160
+ const statusText = error ? "Error" : isReady ? "Ready" : isLoading ? "Loading" : "Idle";
58161
+ return /* @__PURE__ */ jsxDEV2("div", {
58162
+ className: `llm-chat${className ? ` ${className}` : ""}`,
58163
+ style: { maxHeight, height: "100%" },
58164
+ children: [
58165
+ showHeader && /* @__PURE__ */ jsxDEV2("div", {
58166
+ className: "llm-chat-header",
58167
+ children: [
58168
+ onModelChange ? /* @__PURE__ */ jsxDEV2(ModelSelector, {
58169
+ currentModel: modelId,
58170
+ onSelect: onModelChange,
58171
+ theme
58172
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2("div", {
58173
+ className: "llm-chat-model-select",
58174
+ children: /* @__PURE__ */ jsxDEV2("span", {
58175
+ style: {
58176
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
58177
+ fontSize: "11px",
58178
+ textTransform: "uppercase",
58179
+ letterSpacing: "0.05em"
58180
+ },
58181
+ children: [
58182
+ "[",
58183
+ modelId?.split("/").pop(),
58184
+ "]"
58185
+ ]
58186
+ }, undefined, true, undefined, this)
58187
+ }, undefined, false, undefined, this),
58188
+ /* @__PURE__ */ jsxDEV2("div", {
58189
+ className: "llm-chat-status",
58190
+ children: [
58191
+ /* @__PURE__ */ jsxDEV2("span", {
58192
+ children: statusText
58193
+ }, undefined, false, undefined, this),
58194
+ /* @__PURE__ */ jsxDEV2("div", {
58195
+ className: statusDotClass
58196
+ }, undefined, false, undefined, this)
58197
+ ]
58198
+ }, undefined, true, undefined, this)
58199
+ ]
58200
+ }, undefined, true, undefined, this),
58201
+ showProgress && isLoading && loadProgress && /* @__PURE__ */ jsxDEV2("div", {
58202
+ className: "llm-chat-progress",
58203
+ children: [
58204
+ /* @__PURE__ */ jsxDEV2("div", {
58205
+ className: "llm-chat-progress-bar",
58206
+ children: /* @__PURE__ */ jsxDEV2("div", {
58207
+ className: "llm-chat-progress-fill",
58208
+ style: { width: `${Math.min(100, loadProgress.progress)}%` }
58209
+ }, undefined, false, undefined, this)
58210
+ }, undefined, false, undefined, this),
58211
+ /* @__PURE__ */ jsxDEV2("div", {
58212
+ className: "llm-chat-progress-text",
58213
+ children: loadProgress.status
58214
+ }, undefined, false, undefined, this)
58215
+ ]
58216
+ }, undefined, true, undefined, this),
58217
+ error && /* @__PURE__ */ jsxDEV2("div", {
58218
+ className: "llm-chat-error",
58219
+ children: [
58220
+ /* @__PURE__ */ jsxDEV2("span", {
58221
+ className: "llm-chat-error-text",
58222
+ children: error.message
58223
+ }, undefined, false, undefined, this),
58224
+ /* @__PURE__ */ jsxDEV2("button", {
58225
+ className: "llm-chat-error-retry",
58226
+ onClick: reload,
58227
+ children: [
58228
+ /* @__PURE__ */ jsxDEV2(RetryIcon, {}, undefined, false, undefined, this),
58229
+ " Retry"
58230
+ ]
58231
+ }, undefined, true, undefined, this)
58232
+ ]
58233
+ }, undefined, true, undefined, this),
58234
+ /* @__PURE__ */ jsxDEV2("div", {
58235
+ className: "llm-chat-messages",
58236
+ children: [
58237
+ !isLoading && messages.length === 0 && !error && /* @__PURE__ */ jsxDEV2("div", {
58238
+ className: "llm-chat-welcome",
58239
+ children: [
58240
+ /* @__PURE__ */ jsxDEV2("h3", {
58241
+ children: [
58242
+ "[",
58243
+ welcomeMessage,
58244
+ "]"
58245
+ ]
58246
+ }, undefined, true, undefined, this),
58247
+ /* @__PURE__ */ jsxDEV2("p", {
58248
+ children: "Markdown, code blocks, Mermaid diagrams. Paste images with Ctrl+V."
58249
+ }, undefined, false, undefined, this)
58250
+ ]
58251
+ }, undefined, true, undefined, this),
58252
+ messages.map((msg, i) => /* @__PURE__ */ jsxDEV2("div", {
58253
+ className: `llm-chat-bubble llm-chat-bubble--${msg.role}`,
58254
+ children: msg.role === "user" ? /* @__PURE__ */ jsxDEV2(Fragment2, {
58255
+ children: [
58256
+ msg.images && msg.images.length > 0 && /* @__PURE__ */ jsxDEV2("div", {
58257
+ className: "llm-chat-msg-images",
58258
+ children: msg.images.map((img) => /* @__PURE__ */ jsxDEV2("img", {
58259
+ src: img.dataUrl,
58260
+ className: "llm-chat-msg-img",
58261
+ alt: "attachment"
58262
+ }, img.id, false, undefined, this))
58263
+ }, undefined, false, undefined, this),
58264
+ /* @__PURE__ */ jsxDEV2("div", {
58265
+ className: "llm-chat-user-content",
58266
+ children: msg.content
58267
+ }, undefined, false, undefined, this)
58268
+ ]
58269
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV2("div", {
58270
+ className: "llm-chat-assistant-content",
58271
+ children: /* @__PURE__ */ jsxDEV2(Streamdown, {
58272
+ plugins: { code, mermaid },
58273
+ children: msg.content
58274
+ }, undefined, false, undefined, this)
58275
+ }, undefined, false, undefined, this)
58276
+ }, i, false, undefined, this)),
58277
+ streamingText && /* @__PURE__ */ jsxDEV2("div", {
58278
+ className: "llm-chat-bubble llm-chat-bubble--assistant",
58279
+ children: /* @__PURE__ */ jsxDEV2("div", {
58280
+ className: "llm-chat-assistant-content",
58281
+ children: /* @__PURE__ */ jsxDEV2(Streamdown, {
58282
+ plugins: { code, mermaid },
58283
+ children: streamingText
58284
+ }, undefined, false, undefined, this)
58285
+ }, undefined, false, undefined, this)
58286
+ }, undefined, false, undefined, this),
58287
+ /* @__PURE__ */ jsxDEV2("div", {
58288
+ ref: messagesEndRef
58289
+ }, undefined, false, undefined, this)
58290
+ ]
58291
+ }, undefined, true, undefined, this),
58292
+ /* @__PURE__ */ jsxDEV2("div", {
58293
+ className: "llm-chat-input-area",
58294
+ children: /* @__PURE__ */ jsxDEV2(ChatInput, {
58295
+ value: input,
58296
+ onChange: setInput,
58297
+ onSend: handleSend,
58298
+ onStop: handleStop,
58299
+ disabled: !isReady && !isLoading,
58300
+ isGenerating,
58301
+ placeholder: isLoading ? "Model is loading..." : placeholder,
58302
+ theme,
58303
+ actions: inputActions,
58304
+ images,
58305
+ onImageAdd: (img) => setImages((prev) => [...prev, img]),
58306
+ onImageRemove: (id2) => setImages((prev) => prev.filter((img) => img.id !== id2))
58307
+ }, undefined, false, undefined, this)
58308
+ }, undefined, false, undefined, this)
58309
+ ]
58310
+ }, undefined, true, undefined, this);
58311
+ }
58312
+
58313
+ // src/react/index.tsx
58314
+ import { jsxDEV as jsxDEV3, Fragment as Fragment3 } from "react/jsx-dev-runtime";
57137
58315
  var LLMContext = createContext(null);
57138
58316
  function LLMProvider({
57139
58317
  children,
@@ -57143,14 +58321,14 @@ function LLMProvider({
57143
58321
  onError,
57144
58322
  ...config
57145
58323
  }) {
57146
- const [llm, setLLM] = useState(null);
57147
- const [isLoading, setIsLoading] = useState(false);
57148
- const [loadProgress, setLoadProgress] = useState(null);
57149
- const [error, setError] = useState(null);
57150
- const hasLoadedRef = useRef(false);
57151
- const configRef = useRef(config);
58324
+ const [llm, setLLM] = useState3(null);
58325
+ const [isLoading, setIsLoading] = useState3(false);
58326
+ const [loadProgress, setLoadProgress] = useState3(null);
58327
+ const [error, setError] = useState3(null);
58328
+ const hasLoadedRef = useRef3(false);
58329
+ const configRef = useRef3(config);
57152
58330
  configRef.current = config;
57153
- const load = useCallback(async () => {
58331
+ const load = useCallback2(async () => {
57154
58332
  if (isLoading)
57155
58333
  return;
57156
58334
  setIsLoading(true);
@@ -57175,7 +58353,7 @@ function LLMProvider({
57175
58353
  setIsLoading(false);
57176
58354
  }
57177
58355
  }, [isLoading, onLoad, onProgress, onError]);
57178
- const unload = useCallback(async () => {
58356
+ const unload = useCallback2(async () => {
57179
58357
  if (llm) {
57180
58358
  await llm.unload();
57181
58359
  setLLM(null);
@@ -57183,24 +58361,24 @@ function LLMProvider({
57183
58361
  hasLoadedRef.current = false;
57184
58362
  }
57185
58363
  }, [llm]);
57186
- const reload = useCallback(async () => {
58364
+ const reload = useCallback2(async () => {
57187
58365
  await unload();
57188
58366
  await load();
57189
58367
  }, [unload, load]);
57190
- useEffect(() => {
58368
+ useEffect3(() => {
57191
58369
  if (autoLoad && !hasLoadedRef.current && !llm && !isLoading) {
57192
58370
  hasLoadedRef.current = true;
57193
58371
  load();
57194
58372
  }
57195
58373
  }, [autoLoad, llm, isLoading, load]);
57196
- useEffect(() => {
58374
+ useEffect3(() => {
57197
58375
  return () => {
57198
58376
  if (llm) {
57199
58377
  llm.unload().catch(console.error);
57200
58378
  }
57201
58379
  };
57202
58380
  }, [llm]);
57203
- const value = useMemo(() => ({
58381
+ const value = useMemo2(() => ({
57204
58382
  llm,
57205
58383
  isLoading,
57206
58384
  isReady: llm?.isReady ?? false,
@@ -57211,7 +58389,7 @@ function LLMProvider({
57211
58389
  reload,
57212
58390
  unload
57213
58391
  }), [llm, isLoading, loadProgress, error, reload, unload]);
57214
- return /* @__PURE__ */ jsxDEV(LLMContext.Provider, {
58392
+ return /* @__PURE__ */ jsxDEV3(LLMContext.Provider, {
57215
58393
  value,
57216
58394
  children
57217
58395
  }, undefined, false, undefined, this);
@@ -57235,14 +58413,14 @@ function useChat(options = {}) {
57235
58413
  onFinish,
57236
58414
  onError
57237
58415
  } = options;
57238
- const [messages, setMessages] = useState(initialMessages);
57239
- const [input, setInput] = useState("");
57240
- const [isGenerating, setIsGenerating] = useState(false);
57241
- const [streamingText, setStreamingText] = useState("");
57242
- const [pendingMessage, setPendingMessage] = useState(null);
57243
- const abortRef = useRef(false);
57244
- const isProcessingRef = useRef(false);
57245
- const generateResponse = useCallback(async (userContent, currentMessages) => {
58416
+ const [messages, setMessages] = useState3(initialMessages);
58417
+ const [input, setInput] = useState3("");
58418
+ const [isGenerating, setIsGenerating] = useState3(false);
58419
+ const [streamingText, setStreamingText] = useState3("");
58420
+ const [pendingMessage, setPendingMessage] = useState3(null);
58421
+ const abortRef = useRef3(false);
58422
+ const isProcessingRef = useRef3(false);
58423
+ const generateResponse = useCallback2(async (userContent, currentMessages) => {
57246
58424
  if (!llm || !isReady || isProcessingRef.current) {
57247
58425
  return "";
57248
58426
  }
@@ -57284,14 +58462,14 @@ function useChat(options = {}) {
57284
58462
  isProcessingRef.current = false;
57285
58463
  }
57286
58464
  }, [llm, isReady, systemPrompt, generateOptions, onStart, onToken, onFinish, onError]);
57287
- useEffect(() => {
58465
+ useEffect3(() => {
57288
58466
  if (isReady && pendingMessage && !isProcessingRef.current) {
57289
58467
  const messageToProcess = pendingMessage;
57290
58468
  setPendingMessage(null);
57291
58469
  generateResponse(messageToProcess, messages);
57292
58470
  }
57293
58471
  }, [isReady, pendingMessage, messages, generateResponse]);
57294
- const send = useCallback(async (content) => {
58472
+ const send = useCallback2(async (content) => {
57295
58473
  const messageContent = content ?? input;
57296
58474
  if (!messageContent.trim()) {
57297
58475
  return "";
@@ -57310,7 +58488,7 @@ function useChat(options = {}) {
57310
58488
  }
57311
58489
  return "";
57312
58490
  }, [input, llm, isReady, isLoading, queueWhileLoading, messages, generateResponse]);
57313
- const stop = useCallback(() => {
58491
+ const stop = useCallback2(() => {
57314
58492
  abortRef.current = true;
57315
58493
  setIsGenerating(false);
57316
58494
  setPendingMessage(null);
@@ -57322,16 +58500,16 @@ function useChat(options = {}) {
57322
58500
  setStreamingText("");
57323
58501
  }
57324
58502
  }, [streamingText]);
57325
- const clear = useCallback(() => {
58503
+ const clear = useCallback2(() => {
57326
58504
  setMessages(initialMessages);
57327
58505
  setStreamingText("");
57328
58506
  setInput("");
57329
58507
  setPendingMessage(null);
57330
58508
  }, [initialMessages]);
57331
- const append = useCallback((message) => {
58509
+ const append = useCallback2((message) => {
57332
58510
  setMessages((prev) => [...prev, message]);
57333
58511
  }, []);
57334
- const reload = useCallback(async () => {
58512
+ const reload = useCallback2(async () => {
57335
58513
  if (messages.length === 0)
57336
58514
  return "";
57337
58515
  const lastUserIndex = messages.findLastIndex((m) => m.role === "user");
@@ -57361,10 +58539,10 @@ function useChat(options = {}) {
57361
58539
  function useStream(options = {}) {
57362
58540
  const { llm, isReady } = useLLM();
57363
58541
  const { generateOptions, onToken, onFinish, onError } = options;
57364
- const [text, setText] = useState("");
57365
- const [isStreaming, setIsStreaming] = useState(false);
57366
- const abortRef = useRef(false);
57367
- const stream = useCallback(async (input) => {
58542
+ const [text, setText] = useState3("");
58543
+ const [isStreaming, setIsStreaming] = useState3(false);
58544
+ const abortRef = useRef3(false);
58545
+ const stream = useCallback2(async (input) => {
57368
58546
  if (!llm || !isReady) {
57369
58547
  return "";
57370
58548
  }
@@ -57388,11 +58566,11 @@ function useStream(options = {}) {
57388
58566
  setIsStreaming(false);
57389
58567
  }
57390
58568
  }, [llm, isReady, generateOptions, onToken, onFinish, onError]);
57391
- const stop = useCallback(() => {
58569
+ const stop = useCallback2(() => {
57392
58570
  abortRef.current = true;
57393
58571
  setIsStreaming(false);
57394
58572
  }, []);
57395
- const clear = useCallback(() => {
58573
+ const clear = useCallback2(() => {
57396
58574
  setText("");
57397
58575
  }, []);
57398
58576
  return {
@@ -57406,9 +58584,9 @@ function useStream(options = {}) {
57406
58584
  function useCompletion(options = {}) {
57407
58585
  const { llm, isReady } = useLLM();
57408
58586
  const { generateOptions } = options;
57409
- const [completion, setCompletion] = useState("");
57410
- const [isLoading, setIsLoading] = useState(false);
57411
- const complete = useCallback(async (prompt) => {
58587
+ const [completion, setCompletion] = useState3("");
58588
+ const [isLoading, setIsLoading] = useState3(false);
58589
+ const complete = useCallback2(async (prompt) => {
57412
58590
  if (!llm || !isReady) {
57413
58591
  return "";
57414
58592
  }
@@ -57424,7 +58602,7 @@ function useCompletion(options = {}) {
57424
58602
  setIsLoading(false);
57425
58603
  }
57426
58604
  }, [llm, isReady, generateOptions]);
57427
- const clear = useCallback(() => {
58605
+ const clear = useCallback2(() => {
57428
58606
  setCompletion("");
57429
58607
  }, []);
57430
58608
  return {
@@ -57439,22 +58617,22 @@ function LLMLoading({ children, className }) {
57439
58617
  if (!isLoading)
57440
58618
  return null;
57441
58619
  if (children) {
57442
- return /* @__PURE__ */ jsxDEV("div", {
58620
+ return /* @__PURE__ */ jsxDEV3("div", {
57443
58621
  className,
57444
58622
  children
57445
58623
  }, undefined, false, undefined, this);
57446
58624
  }
57447
- return /* @__PURE__ */ jsxDEV("div", {
58625
+ return /* @__PURE__ */ jsxDEV3("div", {
57448
58626
  className,
57449
58627
  children: [
57450
- /* @__PURE__ */ jsxDEV("p", {
58628
+ /* @__PURE__ */ jsxDEV3("p", {
57451
58629
  children: [
57452
58630
  "Loading model... ",
57453
58631
  loadProgress?.progress ?? 0,
57454
58632
  "%"
57455
58633
  ]
57456
58634
  }, undefined, true, undefined, this),
57457
- /* @__PURE__ */ jsxDEV("p", {
58635
+ /* @__PURE__ */ jsxDEV3("p", {
57458
58636
  children: loadProgress?.status
57459
58637
  }, undefined, false, undefined, this)
57460
58638
  ]
@@ -57463,11 +58641,11 @@ function LLMLoading({ children, className }) {
57463
58641
  function LLMReady({ children, fallback = null }) {
57464
58642
  const { isReady, isLoading } = useLLM();
57465
58643
  if (isLoading || !isReady) {
57466
- return /* @__PURE__ */ jsxDEV(Fragment, {
58644
+ return /* @__PURE__ */ jsxDEV3(Fragment3, {
57467
58645
  children: fallback
57468
58646
  }, undefined, false, undefined, this);
57469
58647
  }
57470
- return /* @__PURE__ */ jsxDEV(Fragment, {
58648
+ return /* @__PURE__ */ jsxDEV3(Fragment3, {
57471
58649
  children
57472
58650
  }, undefined, false, undefined, this);
57473
58651
  }
@@ -57497,5 +58675,7 @@ export {
57497
58675
  LLMProvider,
57498
58676
  LLMLoading,
57499
58677
  DEFAULT_WEBLLM_MODEL,
57500
- DEFAULT_TRANSFORMERS_MODEL
58678
+ DEFAULT_TRANSFORMERS_MODEL,
58679
+ ChatInput,
58680
+ Chat2 as Chat
57501
58681
  };