@blank-utils/llm 0.3.5 → 0.3.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.
Files changed (69) hide show
  1. package/dist/KaTeX_AMS-Regular-CYEKBG2K.woff +0 -0
  2. package/dist/KaTeX_AMS-Regular-JKX5W2C4.ttf +0 -0
  3. package/dist/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
  4. package/dist/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
  5. package/dist/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff +0 -0
  6. package/dist/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf +0 -0
  7. package/dist/KaTeX_Caligraphic-Regular-3LKEU76G.woff +0 -0
  8. package/dist/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf +0 -0
  9. package/dist/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
  10. package/dist/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
  11. package/dist/KaTeX_Fraktur-Bold-T4SWXBMT.woff +0 -0
  12. package/dist/KaTeX_Fraktur-Bold-WGHVTYOR.ttf +0 -0
  13. package/dist/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
  14. package/dist/KaTeX_Fraktur-Regular-5U4OPH2X.ttf +0 -0
  15. package/dist/KaTeX_Fraktur-Regular-PQMHCIK6.woff +0 -0
  16. package/dist/KaTeX_Main-Bold-2GA4IZIN.woff +0 -0
  17. package/dist/KaTeX_Main-Bold-W5FBVCZM.ttf +0 -0
  18. package/dist/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
  19. package/dist/KaTeX_Main-BoldItalic-4P4C7HJH.woff +0 -0
  20. package/dist/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
  21. package/dist/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf +0 -0
  22. package/dist/KaTeX_Main-Italic-I43T2HSR.ttf +0 -0
  23. package/dist/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
  24. package/dist/KaTeX_Main-Italic-SASNQFN2.woff +0 -0
  25. package/dist/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
  26. package/dist/KaTeX_Main-Regular-P5I74A2A.woff +0 -0
  27. package/dist/KaTeX_Main-Regular-W74P5G27.ttf +0 -0
  28. package/dist/KaTeX_Math-BoldItalic-6EBV3DK5.woff +0 -0
  29. package/dist/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
  30. package/dist/KaTeX_Math-BoldItalic-VB447A4D.ttf +0 -0
  31. package/dist/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
  32. package/dist/KaTeX_Math-Italic-KKK3USB2.woff +0 -0
  33. package/dist/KaTeX_Math-Italic-SON4MRCA.ttf +0 -0
  34. package/dist/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
  35. package/dist/KaTeX_SansSerif-Bold-STQ6RXC7.ttf +0 -0
  36. package/dist/KaTeX_SansSerif-Bold-X5M5EMOD.woff +0 -0
  37. package/dist/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
  38. package/dist/KaTeX_SansSerif-Italic-PSN4QKYX.woff +0 -0
  39. package/dist/KaTeX_SansSerif-Italic-WTBAZBGY.ttf +0 -0
  40. package/dist/KaTeX_SansSerif-Regular-2TL3USAE.ttf +0 -0
  41. package/dist/KaTeX_SansSerif-Regular-OQCII6EP.woff +0 -0
  42. package/dist/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
  43. package/dist/KaTeX_Script-Regular-72OLXYNA.ttf +0 -0
  44. package/dist/KaTeX_Script-Regular-A5IFOEBS.woff +0 -0
  45. package/dist/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
  46. package/dist/KaTeX_Size1-Regular-4HRHTS65.woff +0 -0
  47. package/dist/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
  48. package/dist/KaTeX_Size1-Regular-7K6AASVL.ttf +0 -0
  49. package/dist/KaTeX_Size2-Regular-222HN3GT.ttf +0 -0
  50. package/dist/KaTeX_Size2-Regular-K5ZHAIS6.woff +0 -0
  51. package/dist/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
  52. package/dist/KaTeX_Size3-Regular-TLFPAHDE.woff +0 -0
  53. package/dist/KaTeX_Size3-Regular-UFCO6WCA.ttf +0 -0
  54. package/dist/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
  55. package/dist/KaTeX_Size4-Regular-7PGNVPQK.ttf +0 -0
  56. package/dist/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
  57. package/dist/KaTeX_Size4-Regular-PKMWZHNC.woff +0 -0
  58. package/dist/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf +0 -0
  59. package/dist/KaTeX_Typewriter-Regular-MJMFSK64.woff +0 -0
  60. package/dist/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
  61. package/dist/{chunk-PA7F4CD6.js → chunk-NDXKOAAG.js} +148 -767
  62. package/dist/{index-BfDM8kLq.d.ts → index-fIqGfqTG.d.ts} +1 -77
  63. package/dist/index.css +1155 -0
  64. package/dist/index.d.ts +2 -2
  65. package/dist/index.js +1 -1
  66. package/dist/react/index.css +1155 -0
  67. package/dist/react/index.d.ts +1 -1
  68. package/dist/react/index.js +1 -1
  69. package/package.json +9 -3
@@ -894,227 +894,8 @@ import { useRef as useRef3, useEffect as useEffect3, useState as useState3, useM
894
894
  // src/react/chat-input.tsx
895
895
  import "react";
896
896
  import { useRef as useRef2, useEffect as useEffect2, useCallback as useCallback2, useState as useState2 } from "react";
897
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
898
- function SendIcon() {
899
- return /* @__PURE__ */ jsxs2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
900
- /* @__PURE__ */ jsx2("line", { x1: "5", y1: "12", x2: "19", y2: "12" }),
901
- /* @__PURE__ */ jsx2("polyline", { points: "12 5 19 12 12 19" })
902
- ] });
903
- }
904
- function StopIcon() {
905
- return /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("rect", { x: "6", y: "6", width: "12", height: "12" }) });
906
- }
907
- function ImageIcon() {
908
- return /* @__PURE__ */ jsxs2("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
909
- /* @__PURE__ */ jsx2("rect", { x: "3", y: "3", width: "18", height: "18", rx: "0", ry: "0" }),
910
- /* @__PURE__ */ jsx2("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
911
- /* @__PURE__ */ jsx2("polyline", { points: "21 15 16 10 5 21" })
912
- ] });
913
- }
914
- function XIcon() {
915
- return /* @__PURE__ */ jsxs2("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: [
916
- /* @__PURE__ */ jsx2("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
917
- /* @__PURE__ */ jsx2("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
918
- ] });
919
- }
920
- var STYLE_ID = "__llm-chat-input-styles";
921
- function injectStyles(theme) {
922
- if (typeof document === "undefined") return;
923
- const existing = document.getElementById(STYLE_ID);
924
- if (existing) existing.remove();
925
- const d = theme === "dark";
926
- const border = d ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
927
- const borderFocus = d ? "rgba(255,255,255,0.35)" : "rgba(0,0,0,0.35)";
928
- const bg = d ? "#000000" : "#ffffff";
929
- const text = d ? "#ffffff" : "#000000";
930
- const textMuted = d ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.3)";
931
- const textSecondary = d ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
932
- const monoFont = `ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, monospace`;
933
- const sansFont = `-apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, Helvetica, Arial, sans-serif`;
934
- const css = `
935
- .llm-ci {
936
- display: flex;
937
- flex-direction: column;
938
- border: 1px solid ${border};
939
- background: ${bg};
940
- padding: 12px;
941
- transition: border-color 0.15s;
942
- position: relative;
943
- }
944
- .llm-ci:focus-within {
945
- border-color: ${borderFocus};
946
- }
947
-
948
- /* Attached Images */
949
- .llm-ci-images {
950
- display: flex;
951
- flex-wrap: wrap;
952
- gap: 6px;
953
- margin-bottom: 8px;
954
- }
955
- .llm-ci-image-preview {
956
- position: relative;
957
- width: 52px;
958
- height: 52px;
959
- overflow: hidden;
960
- border: 1px solid ${border};
961
- }
962
- .llm-ci-image-preview img {
963
- width: 100%;
964
- height: 100%;
965
- object-fit: cover;
966
- }
967
- .llm-ci-image-remove {
968
- position: absolute;
969
- top: 2px;
970
- right: 2px;
971
- width: 16px;
972
- height: 16px;
973
- background: ${d ? "rgba(0,0,0,0.7)" : "rgba(255,255,255,0.8)"};
974
- color: ${text};
975
- border: none;
976
- display: flex;
977
- align-items: center;
978
- justify-content: center;
979
- cursor: pointer;
980
- opacity: 0;
981
- transition: opacity 0.1s;
982
- }
983
- .llm-ci-image-preview:hover .llm-ci-image-remove {
984
- opacity: 1;
985
- }
986
-
987
- /* Textarea */
988
- .llm-ci textarea {
989
- width: 100%;
990
- min-height: 24px;
991
- max-height: 200px;
992
- resize: none;
993
- border: none;
994
- outline: none;
995
- background: transparent;
996
- color: ${text};
997
- font-size: 14px;
998
- line-height: 1.5;
999
- padding: 0;
1000
- margin: 0;
1001
- font-family: ${sansFont};
1002
- scrollbar-width: thin;
1003
- }
1004
- .llm-ci textarea::placeholder {
1005
- color: ${textMuted};
1006
- }
1007
-
1008
- /* Toolbar */
1009
- .llm-ci-toolbar {
1010
- display: flex;
1011
- align-items: center;
1012
- justify-content: space-between;
1013
- margin-top: 8px;
1014
- min-height: 28px;
1015
- }
1016
- .llm-ci-left-actions {
1017
- display: flex;
1018
- align-items: center;
1019
- gap: 2px;
1020
- }
1021
- .llm-ci-right-actions {
1022
- display: flex;
1023
- align-items: center;
1024
- gap: 8px;
1025
- }
1026
-
1027
- /* Icon Buttons */
1028
- .llm-ci-btn-icon {
1029
- display: flex;
1030
- align-items: center;
1031
- justify-content: center;
1032
- width: 28px;
1033
- height: 28px;
1034
- border: none;
1035
- background: transparent;
1036
- color: ${textSecondary};
1037
- cursor: pointer;
1038
- transition: color 0.1s;
1039
- }
1040
- .llm-ci-btn-icon:hover {
1041
- color: ${text};
1042
- }
1043
-
1044
- /* Send / Stop \u2014 flat, monospace, uppercase */
1045
- .llm-ci-send {
1046
- display: flex;
1047
- align-items: center;
1048
- justify-content: center;
1049
- height: 28px;
1050
- padding: 0 12px;
1051
- border: 1px solid ${border};
1052
- font-size: 10px;
1053
- font-weight: 400;
1054
- font-family: ${monoFont};
1055
- text-transform: uppercase;
1056
- letter-spacing: 0.08em;
1057
- cursor: pointer;
1058
- transition: all 0.1s;
1059
- gap: 6px;
1060
- background: transparent;
1061
- color: ${textSecondary};
1062
- }
1063
- .llm-ci-send--active {
1064
- background: ${text};
1065
- color: ${bg};
1066
- border-color: ${text};
1067
- }
1068
- .llm-ci-send--active:hover {
1069
- opacity: 0.8;
1070
- }
1071
- .llm-ci-send--disabled {
1072
- color: ${textMuted};
1073
- border-color: ${d ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)"};
1074
- cursor: not-allowed;
1075
- }
1076
- .llm-ci-send--stop {
1077
- background: transparent;
1078
- border-color: ${border};
1079
- color: ${textSecondary};
1080
- }
1081
- .llm-ci-send--stop:hover {
1082
- border-color: ${borderFocus};
1083
- color: ${text};
1084
- }
1085
-
1086
- /* Model Selector Slot */
1087
- .llm-ci-model-selector {
1088
- margin-right: auto;
1089
- }
1090
-
1091
- /* Hidden file input */
1092
- .llm-ci-file-input {
1093
- display: none;
1094
- }
1095
-
1096
- /* Drag Overlay */
1097
- .llm-ci-drag-overlay {
1098
- position: absolute;
1099
- inset: 0;
1100
- background: ${d ? "rgba(0,0,0,0.9)" : "rgba(255,255,255,0.9)"};
1101
- display: flex;
1102
- align-items: center;
1103
- justify-content: center;
1104
- z-index: 10;
1105
- border: 1px dashed ${textSecondary};
1106
- color: ${textSecondary};
1107
- font-size: 11px;
1108
- font-family: ${monoFont};
1109
- text-transform: uppercase;
1110
- letter-spacing: 0.08em;
1111
- }
1112
- `;
1113
- const style = document.createElement("style");
1114
- style.id = STYLE_ID;
1115
- style.textContent = css;
1116
- document.head.appendChild(style);
1117
- }
897
+ import { Send, Square, ImagePlus, X } from "lucide-react";
898
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1118
899
  function ChatInput({
1119
900
  value,
1120
901
  onChange,
@@ -1122,11 +903,10 @@ function ChatInput({
1122
903
  onStop,
1123
904
  disabled = false,
1124
905
  isGenerating = false,
1125
- placeholder = "Type a message...",
906
+ placeholder = "Message...",
1126
907
  maxRows = 8,
1127
908
  actions,
1128
- theme = "dark",
1129
- className,
909
+ className = "",
1130
910
  images = [],
1131
911
  onImageAdd,
1132
912
  onImageRemove,
@@ -1135,29 +915,22 @@ function ChatInput({
1135
915
  const textareaRef = useRef2(null);
1136
916
  const fileInputRef = useRef2(null);
1137
917
  const [isDragging, setIsDragging] = useState2(false);
1138
- useEffect2(() => {
1139
- injectStyles(theme);
1140
- }, [theme]);
1141
918
  useEffect2(() => {
1142
919
  const textarea = textareaRef.current;
1143
920
  if (!textarea) return;
1144
921
  textarea.style.height = "auto";
1145
- const lineHeight = 21;
922
+ const lineHeight = 24;
1146
923
  const maxHeight = lineHeight * maxRows + 24;
1147
- const newHeight = Math.min(textarea.scrollHeight, maxHeight);
1148
- textarea.style.height = `${newHeight}px`;
924
+ textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)}px`;
1149
925
  }, [value, maxRows]);
1150
- const handleKeyDown = useCallback2(
1151
- (e) => {
1152
- if (e.key === "Enter" && !e.shiftKey) {
1153
- e.preventDefault();
1154
- if (!disabled && (value.trim() || images.length > 0)) {
1155
- onSend();
1156
- }
926
+ const handleKeyDown = useCallback2((e) => {
927
+ if (e.key === "Enter" && !e.shiftKey) {
928
+ e.preventDefault();
929
+ if (!disabled && (value.trim() || images.length > 0)) {
930
+ onSend();
1157
931
  }
1158
- },
1159
- [disabled, value, images.length, onSend]
1160
- );
932
+ }
933
+ }, [disabled, value, images.length, onSend]);
1161
934
  const processFile = useCallback2((file) => {
1162
935
  if (file.type === "application/pdf" || file.name.toLowerCase().endsWith(".pdf")) {
1163
936
  const reader2 = new FileReader();
@@ -1195,12 +968,7 @@ ${newText}` : newText);
1195
968
  reader.onload = (e) => {
1196
969
  if (e.target?.result && typeof e.target.result === "string") {
1197
970
  const id = Math.random().toString(36).substring(7);
1198
- onImageAdd?.({
1199
- id,
1200
- dataUrl: e.target.result,
1201
- file,
1202
- name: file.name
1203
- });
971
+ onImageAdd?.({ id, dataUrl: e.target.result, file, name: file.name });
1204
972
  }
1205
973
  };
1206
974
  reader.readAsDataURL(file);
@@ -1235,18 +1003,10 @@ ${newText}` : newText);
1235
1003
  }, [processFile]);
1236
1004
  const hasValue = value.trim().length > 0 || images.length > 0;
1237
1005
  const canSend = hasValue && !disabled && !isGenerating;
1238
- let sendClass = "llm-ci-send";
1239
- if (isGenerating) {
1240
- sendClass += " llm-ci-send--stop";
1241
- } else if (canSend) {
1242
- sendClass += " llm-ci-send--active";
1243
- } else {
1244
- sendClass += " llm-ci-send--disabled";
1245
- }
1246
1006
  return /* @__PURE__ */ jsxs2(
1247
1007
  "div",
1248
1008
  {
1249
- className: `llm-ci${className ? ` ${className}` : ""}`,
1009
+ className: `relative flex flex-col bg-zinc-50 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl shadow-sm focus-within:ring-2 focus-within:ring-blue-500/20 focus-within:border-blue-500 transition-all p-3 ${className}`,
1250
1010
  onDragOver: (e) => {
1251
1011
  e.preventDefault();
1252
1012
  setIsDragging(true);
@@ -1254,7 +1014,10 @@ ${newText}` : newText);
1254
1014
  onDragLeave: () => setIsDragging(false),
1255
1015
  onDrop: handleDrop,
1256
1016
  children: [
1257
- isDragging && /* @__PURE__ */ jsx2("div", { className: "llm-ci-drag-overlay", children: "[Drop image to attach]" }),
1017
+ isDragging && /* @__PURE__ */ jsxs2("div", { className: "absolute inset-0 bg-white/90 dark:bg-zinc-900/90 flex flex-col items-center justify-center z-20 border-2 border-dashed border-blue-500 rounded-2xl", children: [
1018
+ /* @__PURE__ */ jsx2(ImagePlus, { className: "w-8 h-8 text-blue-500 mb-2" }),
1019
+ /* @__PURE__ */ jsx2("span", { className: "text-blue-500 font-medium tracking-wide", children: "Drop file to attach" })
1020
+ ] }),
1258
1021
  /* @__PURE__ */ jsx2(
1259
1022
  "input",
1260
1023
  {
@@ -1262,19 +1025,19 @@ ${newText}` : newText);
1262
1025
  type: "file",
1263
1026
  accept: "image/*,.pdf",
1264
1027
  multiple: true,
1265
- className: "llm-ci-file-input",
1028
+ className: "hidden",
1266
1029
  onChange: handleFileSelect
1267
1030
  }
1268
1031
  ),
1269
- images.length > 0 && /* @__PURE__ */ jsx2("div", { className: "llm-ci-images", children: images.map((img) => /* @__PURE__ */ jsxs2("div", { className: "llm-ci-image-preview", children: [
1270
- /* @__PURE__ */ jsx2("img", { src: img.dataUrl, alt: "attachment" }),
1032
+ images.length > 0 && /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-2 mb-3 px-2", children: images.map((img) => /* @__PURE__ */ jsxs2("div", { className: "relative w-14 h-14 overflow-hidden border border-zinc-200 dark:border-zinc-700 rounded-xl group select-none", children: [
1033
+ /* @__PURE__ */ jsx2("img", { src: img.dataUrl, alt: "attachment", className: "w-full h-full object-cover transition-transform group-hover:scale-105" }),
1271
1034
  /* @__PURE__ */ jsx2(
1272
1035
  "button",
1273
1036
  {
1274
1037
  type: "button",
1275
- className: "llm-ci-image-remove",
1038
+ className: "absolute top-1 right-1 w-5 h-5 bg-black/50 hover:bg-black/80 text-white rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all backdrop-blur-sm",
1276
1039
  onClick: () => onImageRemove?.(img.id),
1277
- children: /* @__PURE__ */ jsx2(XIcon, {})
1040
+ children: /* @__PURE__ */ jsx2(X, { className: "w-3 h-3" })
1278
1041
  }
1279
1042
  )
1280
1043
  ] }, img.id)) }),
@@ -1288,39 +1051,47 @@ ${newText}` : newText);
1288
1051
  onPaste: handlePaste,
1289
1052
  placeholder,
1290
1053
  disabled: disabled && !isGenerating,
1291
- rows: 1
1054
+ rows: 1,
1055
+ className: "w-full min-h-[24px] max-h-[200px] resize-none border-none outline-none bg-transparent text-zinc-900 dark:text-zinc-100 text-[15px] leading-relaxed px-2 m-0 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 scrollbar-thin scrollbar-thumb-zinc-300 dark:scrollbar-thumb-zinc-700"
1292
1056
  }
1293
1057
  ),
1294
- /* @__PURE__ */ jsxs2("div", { className: "llm-ci-toolbar", children: [
1295
- /* @__PURE__ */ jsxs2("div", { className: "llm-ci-left-actions", children: [
1296
- modelSelector && /* @__PURE__ */ jsx2("div", { className: "llm-ci-model-selector", children: modelSelector }),
1058
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-end justify-between mt-3 px-1", children: [
1059
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-1", children: [
1060
+ modelSelector && /* @__PURE__ */ jsx2("div", { className: "mr-2", children: modelSelector }),
1297
1061
  /* @__PURE__ */ jsx2(
1298
1062
  "button",
1299
1063
  {
1300
1064
  type: "button",
1301
- className: "llm-ci-btn-icon",
1065
+ className: "flex items-center justify-center w-8 h-8 rounded-full text-zinc-500 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-zinc-100 hover:bg-zinc-200/50 dark:hover:bg-zinc-800 transition-colors",
1302
1066
  onClick: () => fileInputRef.current?.click(),
1303
- title: "Attach image (or Ctrl+V to paste)",
1304
- children: /* @__PURE__ */ jsx2(ImageIcon, {})
1067
+ title: "Attach image or PDF (Ctrl+V to paste)",
1068
+ children: /* @__PURE__ */ jsx2(ImagePlus, { className: "w-4 h-4" })
1305
1069
  }
1306
1070
  ),
1307
1071
  actions
1308
1072
  ] }),
1309
- /* @__PURE__ */ jsx2("div", { className: "llm-ci-right-actions", children: /* @__PURE__ */ jsx2(
1073
+ /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-2", children: isGenerating ? /* @__PURE__ */ jsxs2(
1310
1074
  "button",
1311
1075
  {
1312
1076
  type: "button",
1313
- className: sendClass,
1314
- onClick: isGenerating ? onStop : onSend,
1315
- disabled: !isGenerating && !canSend,
1316
- "aria-label": isGenerating ? "Stop generation" : "Send message",
1317
- children: isGenerating ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1318
- /* @__PURE__ */ jsx2(StopIcon, {}),
1077
+ className: "flex items-center justify-center h-8 px-4 rounded-full bg-zinc-200 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-300 dark:hover:bg-zinc-700 text-xs font-semibold tracking-wide transition-colors gap-1.5 cursor-pointer shadow-sm",
1078
+ onClick: onStop,
1079
+ children: [
1080
+ /* @__PURE__ */ jsx2(Square, { className: "w-3 h-3 fill-current" }),
1319
1081
  " Stop"
1320
- ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1082
+ ]
1083
+ }
1084
+ ) : /* @__PURE__ */ jsxs2(
1085
+ "button",
1086
+ {
1087
+ type: "button",
1088
+ className: `flex items-center justify-center h-8 px-4 rounded-full text-xs font-semibold tracking-wide transition-all gap-1.5 ${canSend ? "bg-black dark:bg-white text-white dark:text-black shadow-md hover:scale-[1.02] active:scale-[0.98] cursor-pointer" : "bg-zinc-200 dark:bg-zinc-800 text-zinc-400 dark:text-zinc-600 cursor-not-allowed"}`,
1089
+ onClick: onSend,
1090
+ disabled: !canSend,
1091
+ children: [
1321
1092
  "Send ",
1322
- /* @__PURE__ */ jsx2(SendIcon, {})
1323
- ] })
1093
+ /* @__PURE__ */ jsx2(Send, { className: "w-3.5 h-3.5" })
1094
+ ]
1324
1095
  }
1325
1096
  ) })
1326
1097
  ] })
@@ -1330,430 +1101,53 @@ ${newText}` : newText);
1330
1101
  }
1331
1102
 
1332
1103
  // src/react/components.tsx
1104
+ import { RotateCcw, ChevronDown } from "lucide-react";
1105
+ import { clsx } from "clsx";
1106
+ import { twMerge } from "tailwind-merge";
1333
1107
  import { Streamdown } from "streamdown";
1334
1108
  import { code } from "@streamdown/code";
1335
1109
  import { mermaid } from "@streamdown/mermaid";
1336
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1110
+ import { math } from "@streamdown/math";
1111
+ import "streamdown/styles.css";
1112
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1113
+ function cn(...inputs) {
1114
+ return twMerge(clsx(inputs));
1115
+ }
1337
1116
  var DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant.
1338
1117
  - You can use full Markdown (bold, italic, headers, lists).
1339
1118
  - You can use Code Blocks with language syntax highlighting.
1340
1119
  - You can use Mermaid diagrams (\`\`\`mermaid ... \`\`\`).
1341
1120
  - You can use LaTeX math ($$ ... $$).`;
1342
1121
  var ALL_MODELS = { ...WEBLLM_MODELS, ...TRANSFORMERS_MODELS };
1343
- function RetryIcon() {
1344
- return /* @__PURE__ */ jsxs3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1345
- /* @__PURE__ */ jsx3("polyline", { points: "23 4 23 10 17 10" }),
1346
- /* @__PURE__ */ jsx3("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
1347
- ] });
1348
- }
1349
- function ChevronDownIcon() {
1350
- return /* @__PURE__ */ jsx3("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("polyline", { points: "6 9 12 15 18 9" }) });
1351
- }
1352
- var STYLE_ID2 = "__llm-chat-styles";
1353
- function injectChatStyles(theme) {
1354
- if (typeof document === "undefined") return;
1355
- const existing = document.getElementById(STYLE_ID2);
1356
- if (existing) existing.remove();
1357
- const d = theme === "dark";
1358
- const bg = d ? "#000000" : "#ffffff";
1359
- const border = d ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
1360
- const borderSubtle = d ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)";
1361
- const text = d ? "#ffffff" : "#000000";
1362
- const textSecondary = d ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)";
1363
- const textTertiary = d ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.3)";
1364
- const surfaceSubtle = d ? "rgba(255,255,255,0.04)" : "rgba(0,0,0,0.03)";
1365
- const monoFont = `ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace`;
1366
- const sansFont = `-apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, Helvetica, Arial, sans-serif`;
1367
- const css = `
1368
- .llm-chat {
1369
- display: flex;
1370
- flex-direction: column;
1371
- border: 1px solid ${border};
1372
- background: ${bg};
1373
- overflow: hidden;
1374
- font-family: ${sansFont};
1375
- color: ${text};
1376
- }
1377
-
1378
- /* Header */
1379
- .llm-chat-header {
1380
- display: flex;
1381
- align-items: center;
1382
- justify-content: space-between;
1383
- padding: 12px 16px;
1384
- border-bottom: 1px solid ${borderSubtle};
1385
- }
1386
-
1387
- /* Model Selector */
1388
- .llm-chat-model-select {
1389
- position: relative;
1390
- }
1391
- .llm-chat-model-btn {
1392
- display: flex;
1393
- align-items: center;
1394
- gap: 6px;
1395
- background: transparent;
1396
- border: none;
1397
- color: ${text};
1398
- font-weight: 400;
1399
- font-size: 11px;
1400
- font-family: ${monoFont};
1401
- text-transform: uppercase;
1402
- letter-spacing: 0.05em;
1403
- padding: 4px 0;
1404
- cursor: pointer;
1405
- transition: opacity 0.1s;
1406
- }
1407
- .llm-chat-model-btn:hover {
1408
- opacity: 0.6;
1409
- }
1410
- .llm-chat-model-menu {
1411
- position: absolute;
1412
- top: 100%;
1413
- left: 0;
1414
- margin-top: 4px;
1415
- width: 280px;
1416
- max-height: 300px;
1417
- overflow-y: auto;
1418
- background: ${d ? "#0a0a0a" : "#fafafa"};
1419
- border: 1px solid ${border};
1420
- z-index: 50;
1421
- padding: 4px 0;
1422
- scrollbar-width: thin;
1423
- }
1424
- .llm-chat-model-group {
1425
- padding: 8px 12px 4px;
1426
- font-size: 10px;
1427
- font-weight: 400;
1428
- color: ${textSecondary};
1429
- text-transform: uppercase;
1430
- letter-spacing: 0.08em;
1431
- font-family: ${monoFont};
1432
- }
1433
- .llm-chat-model-item {
1434
- display: block;
1435
- width: 100%;
1436
- text-align: left;
1437
- padding: 6px 12px;
1438
- font-size: 12px;
1439
- font-family: ${monoFont};
1440
- color: ${textSecondary};
1441
- background: transparent;
1442
- border: none;
1443
- cursor: pointer;
1444
- white-space: nowrap;
1445
- overflow: hidden;
1446
- text-overflow: ellipsis;
1447
- transition: color 0.1s;
1448
- }
1449
- .llm-chat-model-item:hover {
1450
- color: ${text};
1451
- }
1452
- .llm-chat-model-item--active {
1453
- color: ${text};
1454
- }
1455
-
1456
- /* Status */
1457
- .llm-chat-status {
1458
- display: flex;
1459
- align-items: center;
1460
- gap: 8px;
1461
- font-size: 10px;
1462
- font-family: ${monoFont};
1463
- text-transform: uppercase;
1464
- letter-spacing: 0.08em;
1465
- color: ${textSecondary};
1466
- }
1467
- .llm-chat-dot {
1468
- width: 5px;
1469
- height: 5px;
1470
- border-radius: 50%;
1471
- }
1472
- .llm-chat-dot--loading {
1473
- background: ${textSecondary};
1474
- animation: llm-pulse 1.5s infinite;
1475
- }
1476
- .llm-chat-dot--ready {
1477
- background: ${text};
1478
- }
1479
- .llm-chat-dot--error {
1480
- background: #ff3333;
1481
- }
1482
-
1483
- /* Progress */
1484
- .llm-chat-progress {
1485
- padding: 0 16px 8px;
1486
- }
1487
- .llm-chat-progress-bar {
1488
- height: 1px;
1489
- background: ${borderSubtle};
1490
- overflow: hidden;
1491
- }
1492
- .llm-chat-progress-fill {
1493
- height: 100%;
1494
- background: ${text};
1495
- transition: width 0.3s ease;
1496
- }
1497
- .llm-chat-progress-text {
1498
- font-size: 10px;
1499
- font-family: ${monoFont};
1500
- color: ${textTertiary};
1501
- margin-top: 4px;
1502
- text-align: right;
1503
- text-transform: uppercase;
1504
- letter-spacing: 0.05em;
1505
- }
1506
-
1507
- /* Messages */
1508
- .llm-chat-messages {
1509
- flex: 1;
1510
- overflow-y: auto;
1511
- padding: 20px 16px;
1512
- display: flex;
1513
- flex-direction: column;
1514
- gap: 24px;
1515
- scrollbar-width: thin;
1516
- }
1517
-
1518
- /* Welcome */
1519
- .llm-chat-welcome {
1520
- display: flex;
1521
- flex-direction: column;
1522
- align-items: flex-start;
1523
- justify-content: center;
1524
- flex: 1;
1525
- color: ${textTertiary};
1526
- padding: 40px 0;
1527
- }
1528
- .llm-chat-welcome h3 {
1529
- font-size: 11px;
1530
- font-weight: 400;
1531
- font-family: ${monoFont};
1532
- text-transform: uppercase;
1533
- letter-spacing: 0.08em;
1534
- color: ${textSecondary};
1535
- margin: 0 0 8px;
1536
- }
1537
- .llm-chat-welcome p {
1538
- font-size: 13px;
1539
- margin: 0;
1540
- max-width: 360px;
1541
- line-height: 1.6;
1542
- color: ${textTertiary};
1543
- }
1544
-
1545
- /* Bubble */
1546
- .llm-chat-bubble {
1547
- display: flex;
1548
- flex-direction: column;
1549
- max-width: 100%;
1550
- }
1551
- .llm-chat-bubble--user {
1552
- align-self: flex-end;
1553
- max-width: 80%;
1554
- overflow-wrap: break-word;
1555
- }
1556
- .llm-chat-bubble--assistant {
1557
- align-self: flex-start;
1558
- width: 100%;
1559
- min-width: 0; /* Prevents flex children from expanding past 100% */
1560
- overflow-x: hidden;
1561
- }
1562
-
1563
- /* User message \u2014 flat, subtle bg, no radius */
1564
- .llm-chat-user-content {
1565
- padding: 10px 14px;
1566
- font-size: 14px;
1567
- line-height: 1.6;
1568
- white-space: pre-wrap;
1569
- background: ${surfaceSubtle};
1570
- border: 1px solid ${borderSubtle};
1571
- color: ${text};
1572
- }
1573
-
1574
- /* Streamdown Overrides / Markdown Styling */
1575
- .llm-chat-assistant-content {
1576
- font-size: 14px;
1577
- line-height: 1.7;
1578
- color: ${text};
1579
- word-wrap: break-word;
1580
- }
1581
- .llm-chat-assistant-content > *:first-child { margin-top: 0; }
1582
- .llm-chat-assistant-content > *:last-child { margin-bottom: 0; }
1583
-
1584
- .llm-chat-assistant-content p {
1585
- margin: 0 0 12px 0;
1586
- }
1587
-
1588
- .llm-chat-assistant-content h1,
1589
- .llm-chat-assistant-content h2,
1590
- .llm-chat-assistant-content h3,
1591
- .llm-chat-assistant-content h4 {
1592
- margin: 20px 0 10px 0;
1593
- color: ${text};
1594
- font-weight: 600;
1595
- line-height: 1.3;
1596
- }
1597
- .llm-chat-assistant-content h1 { font-size: 1.5em; }
1598
- .llm-chat-assistant-content h2 { font-size: 1.3em; }
1599
- .llm-chat-assistant-content h3 { font-size: 1.1em; }
1600
-
1601
- .llm-chat-assistant-content ul,
1602
- .llm-chat-assistant-content ol {
1603
- margin: 0 0 12px 0;
1604
- padding-left: 24px;
1605
- }
1606
- .llm-chat-assistant-content li {
1607
- margin-bottom: 4px;
1608
- }
1609
-
1610
- .llm-chat-assistant-content pre {
1611
- background: ${surfaceSubtle} !important;
1612
- border: 1px solid ${borderSubtle} !important;
1613
- border-radius: 6px !important;
1614
- padding: 12px !important;
1615
- margin: 12px 0 !important;
1616
- overflow-x: auto !important;
1617
- white-space: pre !important;
1618
- max-width: 100%;
1619
- }
1620
- .llm-chat-assistant-content code {
1621
- font-family: ${monoFont} !important;
1622
- font-size: 13px !important;
1623
- white-space: inherit;
1624
- }
1625
- .llm-chat-assistant-content :not(pre) > code {
1626
- background: ${surfaceSubtle};
1627
- border: 1px solid ${borderSubtle};
1628
- border-radius: 4px;
1629
- padding: 2px 5px;
1630
- font-size: 12.5px !important;
1631
- white-space: pre-wrap !important;
1632
- word-break: break-word;
1633
- }
1634
-
1635
- .llm-chat-assistant-content blockquote {
1636
- border-left: 3px solid ${borderSubtle};
1637
- margin: 0 0 12px 0;
1638
- padding-left: 12px;
1639
- color: ${textTertiary};
1640
- }
1641
-
1642
- .llm-chat-assistant-content a {
1643
- color: #3b82f6;
1644
- text-decoration: none;
1645
- }
1646
- .llm-chat-assistant-content a:hover {
1647
- text-decoration: underline;
1648
- }
1649
-
1650
- .llm-chat-assistant-content table {
1651
- border-collapse: collapse;
1652
- width: 100%;
1653
- margin: 12px 0;
1654
- }
1655
- .llm-chat-assistant-content th,
1656
- .llm-chat-assistant-content td {
1657
- border: 1px solid ${borderSubtle};
1658
- padding: 6px 10px;
1659
- text-align: left;
1660
- }
1661
- .llm-chat-assistant-content th {
1662
- background: ${surfaceSubtle};
1663
- font-weight: 600;
1664
- }
1665
-
1666
- /* Attachments in message */
1667
- .llm-chat-msg-images {
1668
- display: flex;
1669
- flex-wrap: wrap;
1670
- gap: 6px;
1671
- margin-bottom: 8px;
1672
- justify-content: flex-end;
1673
- }
1674
- .llm-chat-msg-img {
1675
- width: 100px;
1676
- height: 100px;
1677
- object-fit: cover;
1678
- border: 1px solid ${border};
1679
- }
1680
-
1681
- /* Error */
1682
- .llm-chat-error {
1683
- margin: 0 16px;
1684
- padding: 10px 14px;
1685
- border: 1px solid ${d ? "rgba(255,50,50,0.2)" : "rgba(200,0,0,0.15)"};
1686
- display: flex;
1687
- align-items: center;
1688
- justify-content: space-between;
1689
- gap: 12px;
1690
- }
1691
- .llm-chat-error-text {
1692
- font-size: 12px;
1693
- font-family: ${monoFont};
1694
- color: ${d ? "#ff6666" : "#cc0000"};
1695
- flex: 1;
1696
- }
1697
- .llm-chat-error-retry {
1698
- display: flex;
1699
- align-items: center;
1700
- gap: 4px;
1701
- padding: 4px 10px;
1702
- border: 1px solid ${d ? "rgba(255,50,50,0.3)" : "rgba(200,0,0,0.2)"};
1703
- background: transparent;
1704
- color: ${d ? "#ff6666" : "#cc0000"};
1705
- font-size: 11px;
1706
- font-family: ${monoFont};
1707
- text-transform: uppercase;
1708
- letter-spacing: 0.05em;
1709
- cursor: pointer;
1710
- }
1711
- .llm-chat-error-retry:hover {
1712
- background: ${d ? "rgba(255,50,50,0.08)" : "rgba(200,0,0,0.04)"};
1713
- }
1714
-
1715
- .llm-chat-input-area {
1716
- padding: 12px 16px 16px;
1717
- border-top: 1px solid ${borderSubtle};
1718
- }
1719
-
1720
- /* Streamdown Animation CSS */
1721
- @keyframes sd-fadeIn {
1722
- from { opacity: 0; }
1723
- to { opacity: 1; }
1724
- }
1725
- @keyframes sd-blurIn {
1726
- from { opacity: 0; filter: blur(4px); }
1727
- to { opacity: 1; filter: blur(0); }
1728
- }
1729
- @keyframes sd-slideUp {
1730
- from { opacity: 0; transform: translateY(4px); }
1731
- to { opacity: 1; transform: translateY(0); }
1732
- }
1733
- [data-sd-animate] {
1734
- animation: var(--sd-animation, sd-fadeIn) var(--sd-duration, 150ms)
1735
- var(--sd-easing, ease) both;
1736
- }
1737
-
1738
- @keyframes llm-pulse {
1739
- 0%, 100% { opacity: 1; }
1740
- 50% { opacity: 0.2; }
1741
- }
1742
- `;
1743
- const style = document.createElement("style");
1744
- style.id = STYLE_ID2;
1745
- style.textContent = css;
1746
- document.head.appendChild(style);
1747
- }
1748
1122
  function isVisionModel2(modelId) {
1749
1123
  if (!modelId) return false;
1750
1124
  const lower = modelId.toLowerCase();
1751
1125
  return lower.includes("vl") || lower.includes("vision") || lower.includes("moondream");
1752
1126
  }
1127
+ var markdownComponents = {
1128
+ h1: ({ children }) => /* @__PURE__ */ jsx3("h1", { className: "text-3xl font-bold tracking-tight mt-6 mb-4 text-zinc-900 dark:text-zinc-100", children }),
1129
+ h2: ({ children }) => /* @__PURE__ */ jsx3("h2", { className: "text-2xl font-semibold tracking-tight mt-6 mb-4 text-zinc-900 dark:text-zinc-100 border-b border-zinc-200 dark:border-zinc-800 pb-2", children }),
1130
+ h3: ({ children }) => /* @__PURE__ */ jsx3("h3", { className: "text-xl font-semibold tracking-tight mt-6 mb-3 text-zinc-900 dark:text-zinc-100", children }),
1131
+ h4: ({ children }) => /* @__PURE__ */ jsx3("h4", { className: "text-lg font-semibold tracking-tight mt-4 mb-2 text-zinc-900 dark:text-zinc-100", children }),
1132
+ p: ({ children }) => /* @__PURE__ */ jsx3("p", { className: "leading-7 [&:not(:first-child)]:mt-4 text-zinc-800 dark:text-zinc-300", children }),
1133
+ ul: ({ children }) => /* @__PURE__ */ jsx3("ul", { className: "my-4 ml-6 list-disc [&>li]:mt-2 text-zinc-800 dark:text-zinc-300", children }),
1134
+ ol: ({ children }) => /* @__PURE__ */ jsx3("ol", { className: "my-4 ml-6 list-decimal [&>li]:mt-2 text-zinc-800 dark:text-zinc-300", children }),
1135
+ li: ({ children }) => /* @__PURE__ */ jsx3("li", { children }),
1136
+ blockquote: ({ children }) => /* @__PURE__ */ jsx3("blockquote", { className: "mt-4 border-l-2 border-zinc-300 dark:border-zinc-700 pl-4 italic text-zinc-600 dark:text-zinc-400", children }),
1137
+ a: ({ href, children }) => /* @__PURE__ */ jsx3("a", { href, className: "font-medium text-blue-600 hover:underline dark:text-blue-400", target: "_blank", rel: "noopener noreferrer", children }),
1138
+ table: ({ children }) => /* @__PURE__ */ jsx3("div", { className: "my-4 w-full overflow-y-auto", children: /* @__PURE__ */ jsx3("table", { className: "w-full text-sm", children }) }),
1139
+ tr: ({ children }) => /* @__PURE__ */ jsx3("tr", { className: "m-0 border-t border-zinc-200 dark:border-zinc-800 p-0 even:bg-zinc-50 dark:even:bg-zinc-900/50", children }),
1140
+ th: ({ children }) => /* @__PURE__ */ jsx3("th", { className: "border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900 px-4 py-2 font-semibold text-zinc-900 dark:text-zinc-100 text-left [&[align=center]]:text-center [&[align=right]]:text-right", children }),
1141
+ td: ({ children }) => /* @__PURE__ */ jsx3("td", { className: "border border-zinc-200 dark:border-zinc-700 px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right text-zinc-700 dark:text-zinc-300", children }),
1142
+ pre: ({ children }) => /* @__PURE__ */ jsx3("pre", { className: "mt-4 mb-4 overflow-x-auto rounded-xl border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 p-4 text-[13px] leading-relaxed font-mono shadow-sm", children }),
1143
+ code: ({ children, className }) => {
1144
+ const isInline = !className;
1145
+ return /* @__PURE__ */ jsx3("code", { className: cn("font-mono text-[13px]", isInline ? "bg-zinc-100 dark:bg-zinc-800/60 rounded-md px-1.5 py-0.5 border border-zinc-200 dark:border-zinc-700 text-zinc-800 dark:text-zinc-300" : ""), children });
1146
+ }
1147
+ };
1753
1148
  function ModelSelector({
1754
1149
  currentModel,
1755
- onSelect,
1756
- theme
1150
+ onSelect
1757
1151
  }) {
1758
1152
  const [isOpen, setIsOpen] = useState3(false);
1759
1153
  const ref = useRef3(null);
@@ -1775,27 +1169,28 @@ function ModelSelector({
1775
1169
  }
1776
1170
  return label;
1777
1171
  }, [currentModel]);
1778
- return /* @__PURE__ */ jsxs3("div", { className: "llm-chat-model-select", ref, children: [
1172
+ return /* @__PURE__ */ jsxs3("div", { className: "relative", ref, children: [
1779
1173
  /* @__PURE__ */ jsxs3(
1780
1174
  "button",
1781
1175
  {
1782
1176
  type: "button",
1783
- className: "llm-chat-model-btn",
1177
+ className: "flex items-center gap-1.5 px-3 py-1.5 bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700 text-zinc-700 dark:text-zinc-300 text-xs font-medium tracking-wide rounded-full transition-colors",
1784
1178
  onClick: () => setIsOpen(!isOpen),
1785
1179
  children: [
1786
- "[",
1787
- displayModel,
1788
- "] ",
1789
- /* @__PURE__ */ jsx3(ChevronDownIcon, {})
1180
+ /* @__PURE__ */ jsx3("span", { children: displayModel }),
1181
+ /* @__PURE__ */ jsx3(ChevronDown, { className: "w-3.5 h-3.5 opacity-70" })
1790
1182
  ]
1791
1183
  }
1792
1184
  ),
1793
- isOpen && /* @__PURE__ */ jsxs3("div", { className: "llm-chat-model-menu", children: [
1794
- /* @__PURE__ */ jsx3("div", { className: "llm-chat-model-group", children: "[WebLLM]" }),
1185
+ isOpen && /* @__PURE__ */ jsxs3("div", { className: "absolute top-[calc(100%+8px)] left-0 w-72 max-h-[300px] overflow-y-auto bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl shadow-xl z-50 py-2 scrollbar-thin", children: [
1186
+ /* @__PURE__ */ jsx3("div", { className: "px-3 py-1.5 text-[10px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-wider", children: "WebLLM" }),
1795
1187
  Object.entries(WEBLLM_MODELS).map(([key, value]) => /* @__PURE__ */ jsxs3(
1796
1188
  "button",
1797
1189
  {
1798
- className: `llm-chat-model-item ${currentModel === value ? "llm-chat-model-item--active" : ""}`,
1190
+ className: cn(
1191
+ "block w-full text-left px-3 py-2 text-xs font-medium truncate transition-colors",
1192
+ currentModel === value ? "bg-blue-50 text-blue-600 dark:bg-blue-900/20 dark:text-blue-400" : "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-zinc-800/50"
1193
+ ),
1799
1194
  onClick: () => {
1800
1195
  onSelect(value);
1801
1196
  setIsOpen(false);
@@ -1803,16 +1198,19 @@ function ModelSelector({
1803
1198
  children: [
1804
1199
  key,
1805
1200
  " ",
1806
- isVisionModel2(value) ? " [VISION]" : ""
1201
+ isVisionModel2(value) && /* @__PURE__ */ jsx3("span", { className: "ml-1 px-1.5 py-0.5 rounded text-[9px] bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700", children: "VISION" })
1807
1202
  ]
1808
1203
  },
1809
1204
  key
1810
1205
  )),
1811
- /* @__PURE__ */ jsx3("div", { className: "llm-chat-model-group", children: "[Transformers.js]" }),
1206
+ /* @__PURE__ */ jsx3("div", { className: "px-3 py-1.5 text-[10px] font-bold text-zinc-400 dark:text-zinc-500 uppercase tracking-wider mt-2 border-t border-zinc-100 dark:border-zinc-800 pt-3", children: "Transformers.js" }),
1812
1207
  Object.entries(TRANSFORMERS_MODELS).map(([key, value]) => /* @__PURE__ */ jsxs3(
1813
1208
  "button",
1814
1209
  {
1815
- className: `llm-chat-model-item ${currentModel === value ? "llm-chat-model-item--active" : ""}`,
1210
+ className: cn(
1211
+ "block w-full text-left px-3 py-2 text-xs font-medium truncate transition-colors",
1212
+ currentModel === value ? "bg-blue-50 text-blue-600 dark:bg-blue-900/20 dark:text-blue-400" : "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-zinc-800/50"
1213
+ ),
1816
1214
  onClick: () => {
1817
1215
  onSelect(value);
1818
1216
  setIsOpen(false);
@@ -1820,7 +1218,7 @@ function ModelSelector({
1820
1218
  children: [
1821
1219
  key,
1822
1220
  " ",
1823
- isVisionModel2(value) ? " [VISION]" : ""
1221
+ isVisionModel2(value) && /* @__PURE__ */ jsx3("span", { className: "ml-1 px-1.5 py-0.5 rounded text-[9px] bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700", children: "VISION" })
1824
1222
  ]
1825
1223
  },
1826
1224
  key
@@ -1830,7 +1228,7 @@ function ModelSelector({
1830
1228
  }
1831
1229
  function Chat({
1832
1230
  systemPrompt = DEFAULT_SYSTEM_PROMPT,
1833
- placeholder = "Type a message...",
1231
+ placeholder = "Message...",
1834
1232
  theme = "dark",
1835
1233
  className,
1836
1234
  maxHeight = "600px",
@@ -1840,7 +1238,7 @@ function Chat({
1840
1238
  onError: onErrorProp,
1841
1239
  showHeader = true,
1842
1240
  showProgress = true,
1843
- welcomeMessage = "Ready to assist",
1241
+ welcomeMessage = "How can I help you today?",
1844
1242
  onModelChange
1845
1243
  }) {
1846
1244
  const { llm, isLoading, isReady, loadProgress, error, modelId, reload } = useLLM();
@@ -1853,9 +1251,6 @@ function Chat({
1853
1251
  const messagesEndRef = useRef3(null);
1854
1252
  const abortRef = useRef3(false);
1855
1253
  const isProcessingRef = useRef3(false);
1856
- useEffect3(() => {
1857
- injectChatStyles(theme);
1858
- }, [theme]);
1859
1254
  useEffect3(() => {
1860
1255
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1861
1256
  }, [messages, streamingText, isGenerating]);
@@ -1954,83 +1349,70 @@ function Chat({
1954
1349
  setStreamingText("");
1955
1350
  }
1956
1351
  };
1957
- 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";
1958
- const statusText = error ? "Error" : isReady ? "Ready" : isLoading ? "Loading" : "Idle";
1959
- return /* @__PURE__ */ jsxs3("div", { className: `llm-chat${className ? ` ${className}` : ""}`, style: { maxHeight, height: "100%" }, children: [
1960
- showHeader && /* @__PURE__ */ jsxs3("div", { className: "llm-chat-header", children: [
1352
+ return /* @__PURE__ */ jsxs3("div", { className: cn("flex flex-col bg-white dark:bg-[#09090b] border border-zinc-200 dark:border-zinc-800 rounded-3xl overflow-hidden shadow-sm", className), style: { maxHeight, height: "100%" }, children: [
1353
+ showHeader && /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between px-5 py-4 border-b border-zinc-100 dark:border-zinc-800/60 bg-white/50 dark:bg-zinc-900/20 backdrop-blur-md", children: [
1961
1354
  onModelChange ? /* @__PURE__ */ jsx3(
1962
1355
  ModelSelector,
1963
1356
  {
1964
1357
  currentModel: modelId,
1965
- onSelect: onModelChange,
1966
- theme
1358
+ onSelect: onModelChange
1967
1359
  }
1968
- ) : /* @__PURE__ */ jsx3("div", { className: "llm-chat-model-select", children: /* @__PURE__ */ jsxs3("span", { style: {
1969
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
1970
- fontSize: "11px",
1971
- textTransform: "uppercase",
1972
- letterSpacing: "0.05em"
1973
- }, children: [
1974
- "[",
1975
- modelId?.split("/").pop(),
1976
- "] ",
1977
- modelId && isVisionModel2(modelId) ? "[VISION]" : ""
1978
- ] }) }),
1979
- /* @__PURE__ */ jsxs3("div", { className: "llm-chat-status", children: [
1980
- /* @__PURE__ */ jsx3("span", { children: statusText }),
1981
- /* @__PURE__ */ jsx3("div", { className: statusDotClass })
1360
+ ) : /* @__PURE__ */ jsxs3("div", { className: "px-3 py-1 bg-zinc-100 dark:bg-zinc-800 rounded-full flex items-center gap-2", children: [
1361
+ /* @__PURE__ */ jsx3("span", { className: "text-xs font-semibold tracking-wide text-zinc-700 dark:text-zinc-300", children: modelId?.split("/").pop() }),
1362
+ modelId && isVisionModel2(modelId) && /* @__PURE__ */ jsx3("span", { className: "px-1.5 py-0.5 rounded text-[9px] font-bold bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400 border border-blue-200 dark:border-blue-800", children: "VISION" })
1363
+ ] }),
1364
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
1365
+ /* @__PURE__ */ jsx3("span", { className: "text-xs font-medium text-zinc-500 dark:text-zinc-400 capitalize", children: error ? "Error" : isReady ? "Ready" : isLoading ? "Loading" : "Idle" }),
1366
+ /* @__PURE__ */ jsx3("div", { className: cn(
1367
+ "w-2 h-2 rounded-full",
1368
+ error ? "bg-red-500" : isReady ? "bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)]" : "bg-zinc-300 dark:bg-zinc-700 animate-pulse"
1369
+ ) })
1982
1370
  ] })
1983
1371
  ] }),
1984
- showProgress && isLoading && loadProgress && /* @__PURE__ */ jsxs3("div", { className: "llm-chat-progress", children: [
1985
- /* @__PURE__ */ jsx3("div", { className: "llm-chat-progress-bar", children: /* @__PURE__ */ jsx3(
1372
+ showProgress && isLoading && loadProgress && /* @__PURE__ */ jsxs3("div", { className: "px-5 py-3 border-b border-zinc-100 dark:border-zinc-800 bg-zinc-50/50 dark:bg-zinc-900/30", children: [
1373
+ /* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center mb-1.5", children: [
1374
+ /* @__PURE__ */ jsx3("span", { className: "text-xs font-medium text-zinc-600 dark:text-zinc-400 truncate pr-4", children: loadProgress.status }),
1375
+ /* @__PURE__ */ jsxs3("span", { className: "text-xs font-bold text-zinc-900 dark:text-zinc-100", children: [
1376
+ Math.round(loadProgress.progress),
1377
+ "%"
1378
+ ] })
1379
+ ] }),
1380
+ /* @__PURE__ */ jsx3("div", { className: "h-1.5 w-full bg-zinc-200 dark:bg-zinc-800 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx3(
1986
1381
  "div",
1987
1382
  {
1988
- className: "llm-chat-progress-fill",
1383
+ className: "h-full bg-blue-500 dark:bg-blue-600 rounded-full transition-all duration-300 ease-out",
1989
1384
  style: { width: `${Math.min(100, loadProgress.progress)}%` }
1990
1385
  }
1991
- ) }),
1992
- /* @__PURE__ */ jsx3("div", { className: "llm-chat-progress-text", children: loadProgress.status })
1386
+ ) })
1993
1387
  ] }),
1994
- error && /* @__PURE__ */ jsxs3("div", { className: "llm-chat-error", children: [
1995
- /* @__PURE__ */ jsx3("span", { className: "llm-chat-error-text", children: error.message }),
1996
- /* @__PURE__ */ jsxs3("button", { className: "llm-chat-error-retry", onClick: reload, children: [
1997
- /* @__PURE__ */ jsx3(RetryIcon, {}),
1998
- " Retry"
1999
- ] })
2000
- ] }),
2001
- /* @__PURE__ */ jsxs3("div", { className: "llm-chat-messages", children: [
2002
- !isLoading && messages.length === 0 && !error && /* @__PURE__ */ jsxs3("div", { className: "llm-chat-welcome", children: [
2003
- /* @__PURE__ */ jsxs3("h3", { children: [
2004
- "[",
2005
- welcomeMessage,
2006
- "]"
2007
- ] }),
2008
- /* @__PURE__ */ jsx3("p", { children: "Markdown, code blocks, Mermaid diagrams. Paste images with Ctrl+V." })
2009
- ] }),
2010
- messages.map((msg, i) => /* @__PURE__ */ jsx3("div", { className: `llm-chat-bubble llm-chat-bubble--${msg.role}`, children: msg.role === "user" ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
2011
- msg.images && msg.images.length > 0 && /* @__PURE__ */ jsx3("div", { className: "llm-chat-msg-images", children: msg.images.map((img) => /* @__PURE__ */ jsx3("img", { src: img.dataUrl, className: "llm-chat-msg-img", alt: "attachment" }, img.id)) }),
2012
- /* @__PURE__ */ jsx3("div", { className: "llm-chat-user-content", style: { padding: "0px" }, children: /* @__PURE__ */ jsx3("div", { className: "llm-chat-assistant-content", style: { padding: "10px 14px" }, children: /* @__PURE__ */ jsx3(Streamdown, { plugins: { code, mermaid }, children: msg.content }) }) })
2013
- ] }) : /* @__PURE__ */ jsx3("div", { className: "llm-chat-assistant-content", children: /* @__PURE__ */ jsx3(
2014
- Streamdown,
2015
- {
2016
- plugins: { code, mermaid },
2017
- animated: true,
2018
- isAnimating: false,
2019
- children: msg.content
2020
- }
2021
- ) }) }, i)),
2022
- streamingText && /* @__PURE__ */ jsx3("div", { className: "llm-chat-bubble llm-chat-bubble--assistant", children: /* @__PURE__ */ jsx3("div", { className: "llm-chat-assistant-content", children: /* @__PURE__ */ jsx3(
2023
- Streamdown,
1388
+ error && /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mx-5 mt-4 p-3 bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900/50 rounded-xl", children: [
1389
+ /* @__PURE__ */ jsx3("span", { className: "text-sm text-red-700 dark:text-red-400 font-medium", children: error.message }),
1390
+ /* @__PURE__ */ jsxs3(
1391
+ "button",
2024
1392
  {
2025
- plugins: { code, mermaid },
2026
- animated: true,
2027
- isAnimating: isGenerating,
2028
- children: streamingText
1393
+ className: "flex items-center gap-1.5 px-3 py-1.5 bg-white dark:bg-black border border-red-200 dark:border-red-900 shadow-sm rounded-lg text-xs font-semibold text-red-700 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-950/50 transition-colors",
1394
+ onClick: reload,
1395
+ children: [
1396
+ /* @__PURE__ */ jsx3(RotateCcw, { className: "w-3.5 h-3.5" }),
1397
+ " Retry"
1398
+ ]
2029
1399
  }
2030
- ) }) }),
2031
- /* @__PURE__ */ jsx3("div", { ref: messagesEndRef })
1400
+ )
1401
+ ] }),
1402
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 overflow-y-auto p-5 flex flex-col gap-6 scrollbar-thin scrollbar-thumb-zinc-200 dark:scrollbar-thumb-zinc-800", children: [
1403
+ !isLoading && messages.length === 0 && !error && /* @__PURE__ */ jsxs3("div", { className: "flex-1 flex flex-col items-center justify-center text-center opacity-80 mt-12 mb-8 transition-opacity hover:opacity-100", children: [
1404
+ /* @__PURE__ */ jsx3("div", { className: "w-16 h-16 bg-gradient-to-tr from-blue-500 to-cyan-400 rounded-2xl shadow-xl shadow-blue-500/20 mb-6 flex items-center justify-center transform rotate-3", children: /* @__PURE__ */ jsx3("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }) }),
1405
+ /* @__PURE__ */ jsx3("h3", { className: "text-2xl font-bold tracking-tight text-zinc-900 dark:text-zinc-100 mb-2", children: welcomeMessage }),
1406
+ /* @__PURE__ */ jsx3("p", { className: "text-zinc-500 dark:text-zinc-400 max-w-sm", children: "Use Markdown, paste images (Ctrl+V), create Mermaid diagrams, and write math equations safely." })
1407
+ ] }),
1408
+ messages.map((msg, i) => /* @__PURE__ */ jsx3("div", { className: cn("flex flex-col max-w-[85%]", msg.role === "user" ? "self-end" : "self-start w-full"), children: msg.role === "user" ? /* @__PURE__ */ jsxs3("div", { className: "bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 px-5 py-3.5 rounded-[24px] rounded-br-[8px] sm:px-6 shadow-sm border border-zinc-200 dark:border-zinc-700/50", children: [
1409
+ msg.images && msg.images.length > 0 && /* @__PURE__ */ jsx3("div", { className: "flex flex-wrap gap-2 mb-3 mt-1", children: msg.images.map((img) => /* @__PURE__ */ jsx3("img", { src: img.dataUrl, className: "w-24 h-24 object-cover rounded-xl border border-zinc-200 dark:border-zinc-700 shadow-sm", alt: "attachment" }, img.id)) }),
1410
+ /* @__PURE__ */ jsx3("div", { className: "prose prose-sm dark:prose-invert max-w-none", children: /* @__PURE__ */ jsx3(Streamdown, { plugins: { code, mermaid, math }, components: markdownComponents, animated: true, isAnimating: false, children: msg.content }) })
1411
+ ] }) : /* @__PURE__ */ jsx3("div", { className: "prose prose-sm dark:prose-invert max-w-none px-2 w-full min-w-0", children: /* @__PURE__ */ jsx3(Streamdown, { plugins: { code, mermaid, math }, components: markdownComponents, animated: true, isAnimating: false, children: msg.content }) }) }, i)),
1412
+ streamingText && /* @__PURE__ */ jsx3("div", { className: "flex flex-col self-start w-full max-w-[85%]", children: /* @__PURE__ */ jsx3("div", { className: "prose prose-sm dark:prose-invert max-w-none px-2 w-full min-w-0", children: /* @__PURE__ */ jsx3(Streamdown, { plugins: { code, mermaid, math }, components: markdownComponents, animated: true, isAnimating: isGenerating, children: streamingText }) }) }),
1413
+ /* @__PURE__ */ jsx3("div", { ref: messagesEndRef, className: "h-4" })
2032
1414
  ] }),
2033
- /* @__PURE__ */ jsx3("div", { className: "llm-chat-input-area", children: /* @__PURE__ */ jsx3(
1415
+ /* @__PURE__ */ jsx3("div", { className: "p-4 bg-white/80 dark:bg-zinc-950/80 backdrop-blur-xl border-t border-zinc-100 dark:border-zinc-800/80", children: /* @__PURE__ */ jsx3(
2034
1416
  ChatInput,
2035
1417
  {
2036
1418
  value: input,
@@ -2040,7 +1422,6 @@ function Chat({
2040
1422
  disabled: !isReady && !isLoading,
2041
1423
  isGenerating,
2042
1424
  placeholder: isLoading ? "Model is loading..." : placeholder,
2043
- theme,
2044
1425
  actions: inputActions,
2045
1426
  images,
2046
1427
  onImageAdd: (img) => setImages((prev) => [...prev, img]),