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