@hef2024/llmasaservice-ui 0.22.9 → 0.22.11

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.
@@ -272,3 +272,5 @@ For questions or issues:
272
272
 
273
273
  The controlled collapse state feature is now fully implemented, documented, and tested. It follows React best practices, maintains backward compatibility, and provides a flexible solution for persisting panel state across page navigations.
274
274
 
275
+
276
+
package/dist/index.css CHANGED
@@ -997,12 +997,12 @@ button[data-pending=true]::after {
997
997
  --ai-panel-collapsed-width: 48px;
998
998
  --ai-panel-border-radius: 0;
999
999
  --ai-panel-transition: 0.2s ease;
1000
- --ai-sidebar-bg: #f8f9fa;
1000
+ --ai-sidebar-bg: #ffffff;
1001
1001
  --ai-sidebar-border: #e5e7eb;
1002
- --ai-sidebar-text: #374151;
1002
+ --ai-sidebar-text: #111827;
1003
1003
  --ai-sidebar-text-muted: #6b7280;
1004
- --ai-sidebar-hover: #f3f4f6;
1005
- --ai-sidebar-active: #e5e7eb;
1004
+ --ai-sidebar-hover: #f9fafb;
1005
+ --ai-sidebar-active: #f3f4f6;
1006
1006
  --ai-header-bg: #ffffff;
1007
1007
  --ai-header-border: #e5e7eb;
1008
1008
  --ai-header-height: 52px;
@@ -1011,13 +1011,13 @@ button[data-pending=true]::after {
1011
1011
  --ai-agent-selector-bg: #ffffff;
1012
1012
  --ai-agent-selector-border: #e5e7eb;
1013
1013
  --ai-conversation-bg: transparent;
1014
- --ai-conversation-hover: rgba(0, 0, 0, 0.04);
1015
- --ai-conversation-active: rgba(0, 0, 0, 0.08);
1014
+ --ai-conversation-hover: rgba(0, 0, 0, 0.03);
1015
+ --ai-conversation-active: rgba(0, 0, 0, 0.05);
1016
1016
  --ai-conversation-title-color: #1f2937;
1017
1017
  --ai-conversation-preview-color: #6b7280;
1018
1018
  --ai-conversation-meta-color: #9ca3af;
1019
- --ai-group-label-color: #9ca3af;
1020
- --ai-group-label-size: 0.75rem;
1019
+ --ai-group-label-color: #6b7280;
1020
+ --ai-group-label-size: 0.6875rem;
1021
1021
  --ai-button-primary-bg: #3b82f6;
1022
1022
  --ai-button-primary-text: #ffffff;
1023
1023
  --ai-button-primary-hover: #2563eb;
@@ -1025,18 +1025,18 @@ button[data-pending=true]::after {
1025
1025
  --ai-button-secondary-text: #374151;
1026
1026
  --ai-button-secondary-border: #d1d5db;
1027
1027
  --ai-button-secondary-hover: #e5e7eb;
1028
- --ai-button-ghost-hover: rgba(0, 0, 0, 0.06);
1028
+ --ai-button-ghost-hover: #f3f4f6;
1029
1029
  --ai-button-destructive-bg: #ef4444;
1030
1030
  --ai-button-destructive-text: #ffffff;
1031
1031
  --ai-button-destructive-hover: #dc2626;
1032
- --ai-button-radius: 6px;
1032
+ --ai-button-radius: 4px;
1033
1033
  --ai-input-bg: #ffffff;
1034
1034
  --ai-input-border: #d1d5db;
1035
1035
  --ai-input-text: #1f2937;
1036
1036
  --ai-input-placeholder: #9ca3af;
1037
1037
  --ai-input-focus-border: #3b82f6;
1038
1038
  --ai-input-focus-ring: rgba(59, 130, 246, 0.2);
1039
- --ai-input-radius: 6px;
1039
+ --ai-input-radius: 4px;
1040
1040
  --ai-select-bg: #ffffff;
1041
1041
  --ai-select-border: #d1d5db;
1042
1042
  --ai-select-text: #1f2937;
@@ -1067,21 +1067,21 @@ button[data-pending=true]::after {
1067
1067
  --ai-chat-bg: #ffffff;
1068
1068
  }
1069
1069
  .dark-theme {
1070
- --ai-sidebar-bg: #1f2937;
1071
- --ai-sidebar-border: #374151;
1070
+ --ai-sidebar-bg: #111827;
1071
+ --ai-sidebar-border: #1f2937;
1072
1072
  --ai-sidebar-text: #f3f4f6;
1073
1073
  --ai-sidebar-text-muted: #9ca3af;
1074
- --ai-sidebar-hover: #374151;
1075
- --ai-sidebar-active: #4b5563;
1076
- --ai-header-bg: #111827;
1077
- --ai-header-border: #374151;
1074
+ --ai-sidebar-hover: #1f2937;
1075
+ --ai-sidebar-active: #374151;
1076
+ --ai-header-bg: #0d1117;
1077
+ --ai-header-border: #1f2937;
1078
1078
  --ai-agent-badge-bg: #1e3a5f;
1079
1079
  --ai-agent-badge-text: #60a5fa;
1080
1080
  --ai-agent-selector-bg: #1f2937;
1081
1081
  --ai-agent-selector-border: #374151;
1082
- --ai-conversation-hover: rgba(255, 255, 255, 0.04);
1082
+ --ai-conversation-hover: rgba(255, 255, 255, 0.05);
1083
1083
  --ai-conversation-active: rgba(255, 255, 255, 0.08);
1084
- --ai-conversation-title-color: #f3f4f6;
1084
+ --ai-conversation-title-color: #e5e7eb;
1085
1085
  --ai-conversation-preview-color: #9ca3af;
1086
1086
  --ai-conversation-meta-color: #6b7280;
1087
1087
  --ai-group-label-color: #6b7280;
@@ -1092,7 +1092,7 @@ button[data-pending=true]::after {
1092
1092
  --ai-button-secondary-text: #f3f4f6;
1093
1093
  --ai-button-secondary-border: #4b5563;
1094
1094
  --ai-button-secondary-hover: #4b5563;
1095
- --ai-button-ghost-hover: rgba(255, 255, 255, 0.08);
1095
+ --ai-button-ghost-hover: #1f2937;
1096
1096
  --ai-input-bg: #1f2937;
1097
1097
  --ai-input-border: #374151;
1098
1098
  --ai-input-text: #f3f4f6;
@@ -1286,7 +1286,7 @@ button[data-pending=true]::after {
1286
1286
  .ai-agent-panel__conversations {
1287
1287
  flex: 1;
1288
1288
  overflow-y: auto;
1289
- padding: 8px;
1289
+ padding: 8px 0;
1290
1290
  }
1291
1291
  .ai-agent-panel__empty {
1292
1292
  display: flex;
@@ -1312,15 +1312,15 @@ button[data-pending=true]::after {
1312
1312
  opacity: 0.7;
1313
1313
  }
1314
1314
  .ai-agent-panel__group {
1315
- margin-bottom: 16px;
1315
+ margin-bottom: 24px;
1316
1316
  }
1317
1317
  .ai-agent-panel__group-label {
1318
1318
  font-size: var(--ai-group-label-size);
1319
- font-weight: 600;
1319
+ font-weight: 400;
1320
1320
  color: var(--ai-group-label-color);
1321
1321
  text-transform: uppercase;
1322
- letter-spacing: 0.05em;
1323
- padding: 8px 8px 4px;
1322
+ letter-spacing: 0.1em;
1323
+ padding: 8px 12px 4px;
1324
1324
  }
1325
1325
  .ai-agent-panel__group-label--clickable {
1326
1326
  display: flex;
@@ -1328,10 +1328,10 @@ button[data-pending=true]::after {
1328
1328
  justify-content: space-between;
1329
1329
  cursor: pointer;
1330
1330
  user-select: none;
1331
- transition: color 0.15s;
1331
+ transition: opacity 0.15s;
1332
1332
  }
1333
1333
  .ai-agent-panel__group-label--clickable:hover {
1334
- color: var(--ai-sidebar-text);
1334
+ opacity: 0.7;
1335
1335
  }
1336
1336
  .ai-agent-panel__group-chevron {
1337
1337
  font-size: 8px;
@@ -1344,10 +1344,11 @@ button[data-pending=true]::after {
1344
1344
  .ai-agent-panel__conversation {
1345
1345
  display: flex;
1346
1346
  align-items: flex-start;
1347
- padding: 4px 8px;
1348
- border-radius: 6px;
1347
+ padding: 6px 12px;
1348
+ margin: 0;
1349
+ border-radius: 0;
1349
1350
  cursor: pointer;
1350
- transition: background-color 0.15s;
1351
+ transition: background-color 0.1s ease;
1351
1352
  gap: 8px;
1352
1353
  }
1353
1354
  .ai-agent-panel__conversation:hover {
@@ -1362,12 +1363,12 @@ button[data-pending=true]::after {
1362
1363
  }
1363
1364
  .ai-agent-panel__conversation-title {
1364
1365
  font-weight: 400;
1365
- font-size: 11px;
1366
- color: var(--ai-conversation-preview-color);
1366
+ font-size: 13px;
1367
+ color: var(--ai-conversation-title-color);
1367
1368
  white-space: nowrap;
1368
1369
  overflow: hidden;
1369
1370
  text-overflow: ellipsis;
1370
- margin-bottom: 2px;
1371
+ line-height: 1.5;
1371
1372
  }
1372
1373
  .ai-agent-panel__conversation-preview {
1373
1374
  font-size: 11px;
@@ -1908,22 +1909,25 @@ button[data-pending=true]::after {
1908
1909
  }
1909
1910
  }
1910
1911
  .ai-agent-panel__group--active {
1911
- background-color: var(--ai-conversation-active);
1912
- border-radius: 6px;
1913
- margin-bottom: 8px;
1914
- padding: 4px;
1912
+ margin-bottom: 24px;
1915
1913
  }
1916
1914
  .ai-agent-panel__group--active .ai-agent-panel__group-label {
1917
- color: var(--ai-button-primary-bg);
1918
- font-weight: 600;
1915
+ color: var(--ai-group-label-color);
1916
+ font-weight: 400;
1917
+ padding: 8px 12px 4px;
1919
1918
  }
1920
1919
  .ai-agent-panel__conversation--active-item {
1921
1920
  display: flex;
1922
1921
  align-items: center;
1923
1922
  justify-content: space-between;
1924
- background-color: var(--ai-sidebar-bg);
1925
- border-radius: 4px;
1926
- margin: 2px 0;
1923
+ background-color: transparent;
1924
+ padding: 6px 12px;
1925
+ margin: 0;
1926
+ transition: background-color 0.1s ease;
1927
+ cursor: pointer;
1928
+ }
1929
+ .ai-agent-panel__conversation--active-item:hover {
1930
+ background-color: var(--ai-conversation-hover);
1927
1931
  }
1928
1932
  .ai-agent-panel__conversation--active-item .ai-agent-panel__conversation-content {
1929
1933
  flex: 1;
@@ -1935,8 +1939,7 @@ button[data-pending=true]::after {
1935
1939
  gap: 6px;
1936
1940
  }
1937
1941
  .ai-agent-panel__conversation--current {
1938
- background-color: var(--ai-conversation-active) !important;
1939
- border-left: 2px solid var(--ai-button-primary-bg);
1942
+ background-color: rgba(59, 130, 246, 0.08) !important;
1940
1943
  }
1941
1944
  .ai-agent-panel__conversation--in-active {
1942
1945
  opacity: 0.7;
@@ -1972,8 +1975,9 @@ button[data-pending=true]::after {
1972
1975
  }
1973
1976
  .ai-agent-panel__active-badge {
1974
1977
  color: var(--ai-button-primary-bg);
1975
- font-size: 8px;
1976
- margin-right: 2px;
1978
+ font-size: 7px;
1979
+ margin-right: 4px;
1980
+ opacity: 0.8;
1977
1981
  }
1978
1982
  .ai-agent-panel__loading-dot {
1979
1983
  display: inline-block;
@@ -1996,7 +2000,7 @@ button[data-pending=true]::after {
1996
2000
  .ai-agent-panel__group-divider {
1997
2001
  height: 1px;
1998
2002
  background-color: var(--ai-sidebar-border);
1999
- margin: 12px 8px;
2003
+ margin: 20px 0;
2000
2004
  }
2001
2005
  .ai-agent-panel__chat-wrapper {
2002
2006
  position: absolute;
package/dist/index.js CHANGED
@@ -3865,6 +3865,7 @@ var AIChatPanel = ({
3865
3865
  const [copiedCallId, setCopiedCallId] = (0, import_react12.useState)(null);
3866
3866
  const [feedbackCallId, setFeedbackCallId] = (0, import_react12.useState)(null);
3867
3867
  const [error, setError] = (0, import_react12.useState)(null);
3868
+ const lastProcessedErrorRef = (0, import_react12.useRef)(null);
3868
3869
  const [emailSent, setEmailSent] = (0, import_react12.useState)(false);
3869
3870
  const [isToolInfoModalOpen, setIsToolInfoModalOpen] = (0, import_react12.useState)(false);
3870
3871
  const [toolInfoData, setToolInfoData] = (0, import_react12.useState)(null);
@@ -4369,6 +4370,7 @@ var AIChatPanel = ({
4369
4370
  setThinkingBlocks([]);
4370
4371
  setCurrentThinkingIndex(0);
4371
4372
  setError(null);
4373
+ lastProcessedErrorRef.current = null;
4372
4374
  setUserHasScrolled(false);
4373
4375
  prevResponseLengthRef.current = 0;
4374
4376
  setResponse("");
@@ -4442,31 +4444,58 @@ var AIChatPanel = ({
4442
4444
  // onComplete
4443
4445
  (errorMsg) => {
4444
4446
  console.log("[AIChatPanel] Error callback triggered:", errorMsg);
4445
- if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
4447
+ const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
4448
+ if (isAbortError) {
4449
+ console.log("[AIChatPanel] Request was aborted by user");
4450
+ if (promptKey) {
4451
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4452
+ [promptKey]: {
4453
+ content: "Response canceled",
4454
+ callId: lastCallId || ""
4455
+ }
4456
+ }));
4457
+ }
4458
+ } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
4446
4459
  setError({
4447
4460
  message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
4448
4461
  code: "413"
4449
4462
  });
4463
+ if (promptKey) {
4464
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4465
+ [promptKey]: {
4466
+ content: `Error: ${errorMsg}`,
4467
+ callId: lastCallId || ""
4468
+ }
4469
+ }));
4470
+ }
4450
4471
  } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
4451
4472
  setError({
4452
4473
  message: "Network error. Please check your connection and try again.",
4453
4474
  code: "NETWORK_ERROR"
4454
4475
  });
4476
+ if (promptKey) {
4477
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4478
+ [promptKey]: {
4479
+ content: `Error: ${errorMsg}`,
4480
+ callId: lastCallId || ""
4481
+ }
4482
+ }));
4483
+ }
4455
4484
  } else {
4456
4485
  setError({
4457
4486
  message: errorMsg,
4458
4487
  code: "UNKNOWN_ERROR"
4459
4488
  });
4489
+ if (promptKey) {
4490
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4491
+ [promptKey]: {
4492
+ content: `Error: ${errorMsg}`,
4493
+ callId: lastCallId || ""
4494
+ }
4495
+ }));
4496
+ }
4460
4497
  }
4461
4498
  setIsLoading(false);
4462
- if (promptKey) {
4463
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4464
- [promptKey]: {
4465
- content: `Error: ${errorMsg}`,
4466
- callId: lastCallId || ""
4467
- }
4468
- }));
4469
- }
4470
4499
  }
4471
4500
  );
4472
4501
  setLastMessages(messagesAndHistory);
@@ -4656,33 +4685,57 @@ var AIChatPanel = ({
4656
4685
  }, [followOnPrompt, continueChat]);
4657
4686
  (0, import_react12.useEffect)(() => {
4658
4687
  if (llmError && llmError.trim()) {
4688
+ if (lastProcessedErrorRef.current === llmError) {
4689
+ console.log("[AIChatPanel] Skipping duplicate error:", llmError);
4690
+ return;
4691
+ }
4659
4692
  console.log("[AIChatPanel] Error detected:", llmError);
4693
+ lastProcessedErrorRef.current = llmError;
4660
4694
  const errorMessage = llmError;
4661
- if (errorMessage.includes("413") || errorMessage.toLowerCase().includes("content too large")) {
4695
+ const isAbortError = errorMessage.toLowerCase().includes("abort") || errorMessage.toLowerCase().includes("canceled") || errorMessage.toLowerCase().includes("cancelled");
4696
+ if (isAbortError) {
4697
+ console.log("[AIChatPanel] Request was aborted by user (useEffect)");
4698
+ } else if (errorMessage.includes("413") || errorMessage.toLowerCase().includes("content too large")) {
4662
4699
  setError({
4663
4700
  message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
4664
4701
  code: "413"
4665
4702
  });
4703
+ if (lastKey) {
4704
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4705
+ [lastKey]: {
4706
+ content: `Error: ${errorMessage}`,
4707
+ callId: lastCallId || ""
4708
+ }
4709
+ }));
4710
+ }
4666
4711
  } else if (errorMessage.toLowerCase().includes("network error") || errorMessage.toLowerCase().includes("fetch")) {
4667
4712
  setError({
4668
4713
  message: "Network error. Please check your connection and try again.",
4669
4714
  code: "NETWORK_ERROR"
4670
4715
  });
4716
+ if (lastKey) {
4717
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4718
+ [lastKey]: {
4719
+ content: `Error: ${errorMessage}`,
4720
+ callId: lastCallId || ""
4721
+ }
4722
+ }));
4723
+ }
4671
4724
  } else {
4672
4725
  setError({
4673
4726
  message: errorMessage,
4674
4727
  code: "UNKNOWN_ERROR"
4675
4728
  });
4729
+ if (lastKey) {
4730
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4731
+ [lastKey]: {
4732
+ content: `Error: ${errorMessage}`,
4733
+ callId: lastCallId || ""
4734
+ }
4735
+ }));
4736
+ }
4676
4737
  }
4677
4738
  setIsLoading(false);
4678
- if (lastKey) {
4679
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4680
- [lastKey]: {
4681
- content: `Error: ${errorMessage}`,
4682
- callId: lastCallId || ""
4683
- }
4684
- }));
4685
- }
4686
4739
  }
4687
4740
  }, [llmError, lastKey, lastCallId]);
4688
4741
  (0, import_react12.useEffect)(() => {
package/dist/index.mjs CHANGED
@@ -3832,6 +3832,7 @@ var AIChatPanel = ({
3832
3832
  const [copiedCallId, setCopiedCallId] = useState6(null);
3833
3833
  const [feedbackCallId, setFeedbackCallId] = useState6(null);
3834
3834
  const [error, setError] = useState6(null);
3835
+ const lastProcessedErrorRef = useRef5(null);
3835
3836
  const [emailSent, setEmailSent] = useState6(false);
3836
3837
  const [isToolInfoModalOpen, setIsToolInfoModalOpen] = useState6(false);
3837
3838
  const [toolInfoData, setToolInfoData] = useState6(null);
@@ -4336,6 +4337,7 @@ var AIChatPanel = ({
4336
4337
  setThinkingBlocks([]);
4337
4338
  setCurrentThinkingIndex(0);
4338
4339
  setError(null);
4340
+ lastProcessedErrorRef.current = null;
4339
4341
  setUserHasScrolled(false);
4340
4342
  prevResponseLengthRef.current = 0;
4341
4343
  setResponse("");
@@ -4409,31 +4411,58 @@ var AIChatPanel = ({
4409
4411
  // onComplete
4410
4412
  (errorMsg) => {
4411
4413
  console.log("[AIChatPanel] Error callback triggered:", errorMsg);
4412
- if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
4414
+ const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
4415
+ if (isAbortError) {
4416
+ console.log("[AIChatPanel] Request was aborted by user");
4417
+ if (promptKey) {
4418
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4419
+ [promptKey]: {
4420
+ content: "Response canceled",
4421
+ callId: lastCallId || ""
4422
+ }
4423
+ }));
4424
+ }
4425
+ } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
4413
4426
  setError({
4414
4427
  message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
4415
4428
  code: "413"
4416
4429
  });
4430
+ if (promptKey) {
4431
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4432
+ [promptKey]: {
4433
+ content: `Error: ${errorMsg}`,
4434
+ callId: lastCallId || ""
4435
+ }
4436
+ }));
4437
+ }
4417
4438
  } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
4418
4439
  setError({
4419
4440
  message: "Network error. Please check your connection and try again.",
4420
4441
  code: "NETWORK_ERROR"
4421
4442
  });
4443
+ if (promptKey) {
4444
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4445
+ [promptKey]: {
4446
+ content: `Error: ${errorMsg}`,
4447
+ callId: lastCallId || ""
4448
+ }
4449
+ }));
4450
+ }
4422
4451
  } else {
4423
4452
  setError({
4424
4453
  message: errorMsg,
4425
4454
  code: "UNKNOWN_ERROR"
4426
4455
  });
4456
+ if (promptKey) {
4457
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4458
+ [promptKey]: {
4459
+ content: `Error: ${errorMsg}`,
4460
+ callId: lastCallId || ""
4461
+ }
4462
+ }));
4463
+ }
4427
4464
  }
4428
4465
  setIsLoading(false);
4429
- if (promptKey) {
4430
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4431
- [promptKey]: {
4432
- content: `Error: ${errorMsg}`,
4433
- callId: lastCallId || ""
4434
- }
4435
- }));
4436
- }
4437
4466
  }
4438
4467
  );
4439
4468
  setLastMessages(messagesAndHistory);
@@ -4623,33 +4652,57 @@ var AIChatPanel = ({
4623
4652
  }, [followOnPrompt, continueChat]);
4624
4653
  useEffect7(() => {
4625
4654
  if (llmError && llmError.trim()) {
4655
+ if (lastProcessedErrorRef.current === llmError) {
4656
+ console.log("[AIChatPanel] Skipping duplicate error:", llmError);
4657
+ return;
4658
+ }
4626
4659
  console.log("[AIChatPanel] Error detected:", llmError);
4660
+ lastProcessedErrorRef.current = llmError;
4627
4661
  const errorMessage = llmError;
4628
- if (errorMessage.includes("413") || errorMessage.toLowerCase().includes("content too large")) {
4662
+ const isAbortError = errorMessage.toLowerCase().includes("abort") || errorMessage.toLowerCase().includes("canceled") || errorMessage.toLowerCase().includes("cancelled");
4663
+ if (isAbortError) {
4664
+ console.log("[AIChatPanel] Request was aborted by user (useEffect)");
4665
+ } else if (errorMessage.includes("413") || errorMessage.toLowerCase().includes("content too large")) {
4629
4666
  setError({
4630
4667
  message: "The context is too large to process. Please start a new conversation or reduce the amount of context.",
4631
4668
  code: "413"
4632
4669
  });
4670
+ if (lastKey) {
4671
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4672
+ [lastKey]: {
4673
+ content: `Error: ${errorMessage}`,
4674
+ callId: lastCallId || ""
4675
+ }
4676
+ }));
4677
+ }
4633
4678
  } else if (errorMessage.toLowerCase().includes("network error") || errorMessage.toLowerCase().includes("fetch")) {
4634
4679
  setError({
4635
4680
  message: "Network error. Please check your connection and try again.",
4636
4681
  code: "NETWORK_ERROR"
4637
4682
  });
4683
+ if (lastKey) {
4684
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4685
+ [lastKey]: {
4686
+ content: `Error: ${errorMessage}`,
4687
+ callId: lastCallId || ""
4688
+ }
4689
+ }));
4690
+ }
4638
4691
  } else {
4639
4692
  setError({
4640
4693
  message: errorMessage,
4641
4694
  code: "UNKNOWN_ERROR"
4642
4695
  });
4696
+ if (lastKey) {
4697
+ setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4698
+ [lastKey]: {
4699
+ content: `Error: ${errorMessage}`,
4700
+ callId: lastCallId || ""
4701
+ }
4702
+ }));
4703
+ }
4643
4704
  }
4644
4705
  setIsLoading(false);
4645
- if (lastKey) {
4646
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4647
- [lastKey]: {
4648
- content: `Error: ${errorMessage}`,
4649
- callId: lastCallId || ""
4650
- }
4651
- }));
4652
- }
4653
4706
  }
4654
4707
  }, [llmError, lastKey, lastCallId]);
4655
4708
  useEffect7(() => {
@@ -145,3 +145,5 @@ For complete documentation, see:
145
145
  - [Examples](../examples/controlled-collapse-example.tsx)
146
146
  - [Demo App](../examples/demo-app/src/App.tsx)
147
147
 
148
+
149
+
@@ -649,3 +649,5 @@ const handleCollapsedChange = (isCollapsed: boolean) => {
649
649
  - [Examples](../examples/controlled-collapse-example.tsx)
650
650
  - [Demo App](../examples/demo-app/src/App.tsx)
651
651
 
652
+
653
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hef2024/llmasaservice-ui",
3
- "version": "0.22.9",
3
+ "version": "0.22.11",
4
4
  "description": "Prebuilt UI components for LLMAsAService.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -17,12 +17,12 @@
17
17
  --ai-panel-transition: 0.2s ease;
18
18
 
19
19
  /* Sidebar Colors */
20
- --ai-sidebar-bg: #f8f9fa;
20
+ --ai-sidebar-bg: #ffffff;
21
21
  --ai-sidebar-border: #e5e7eb;
22
- --ai-sidebar-text: #374151;
22
+ --ai-sidebar-text: #111827;
23
23
  --ai-sidebar-text-muted: #6b7280;
24
- --ai-sidebar-hover: #f3f4f6;
25
- --ai-sidebar-active: #e5e7eb;
24
+ --ai-sidebar-hover: #f9fafb;
25
+ --ai-sidebar-active: #f3f4f6;
26
26
 
27
27
  /* Header */
28
28
  --ai-header-bg: #ffffff;
@@ -37,15 +37,15 @@
37
37
 
38
38
  /* Conversation List */
39
39
  --ai-conversation-bg: transparent;
40
- --ai-conversation-hover: rgba(0, 0, 0, 0.04);
41
- --ai-conversation-active: rgba(0, 0, 0, 0.08);
40
+ --ai-conversation-hover: rgba(0, 0, 0, 0.03);
41
+ --ai-conversation-active: rgba(0, 0, 0, 0.05);
42
42
  --ai-conversation-title-color: #1f2937;
43
43
  --ai-conversation-preview-color: #6b7280;
44
44
  --ai-conversation-meta-color: #9ca3af;
45
45
 
46
46
  /* Group Labels */
47
- --ai-group-label-color: #9ca3af;
48
- --ai-group-label-size: 0.75rem;
47
+ --ai-group-label-color: #6b7280;
48
+ --ai-group-label-size: 0.6875rem;
49
49
 
50
50
  /* Buttons */
51
51
  --ai-button-primary-bg: #3b82f6;
@@ -55,11 +55,11 @@
55
55
  --ai-button-secondary-text: #374151;
56
56
  --ai-button-secondary-border: #d1d5db;
57
57
  --ai-button-secondary-hover: #e5e7eb;
58
- --ai-button-ghost-hover: rgba(0, 0, 0, 0.06);
58
+ --ai-button-ghost-hover: #f3f4f6;
59
59
  --ai-button-destructive-bg: #ef4444;
60
60
  --ai-button-destructive-text: #ffffff;
61
61
  --ai-button-destructive-hover: #dc2626;
62
- --ai-button-radius: 6px;
62
+ --ai-button-radius: 4px;
63
63
 
64
64
  /* Input */
65
65
  --ai-input-bg: #ffffff;
@@ -68,7 +68,7 @@
68
68
  --ai-input-placeholder: #9ca3af;
69
69
  --ai-input-focus-border: #3b82f6;
70
70
  --ai-input-focus-ring: rgba(59, 130, 246, 0.2);
71
- --ai-input-radius: 6px;
71
+ --ai-input-radius: 4px;
72
72
 
73
73
  /* Select */
74
74
  --ai-select-bg: #ffffff;
@@ -119,24 +119,24 @@
119
119
  CSS Variables - Dark Theme
120
120
  -------------------------------------------------------- */
121
121
  .dark-theme {
122
- --ai-sidebar-bg: #1f2937;
123
- --ai-sidebar-border: #374151;
122
+ --ai-sidebar-bg: #111827;
123
+ --ai-sidebar-border: #1f2937;
124
124
  --ai-sidebar-text: #f3f4f6;
125
125
  --ai-sidebar-text-muted: #9ca3af;
126
- --ai-sidebar-hover: #374151;
127
- --ai-sidebar-active: #4b5563;
126
+ --ai-sidebar-hover: #1f2937;
127
+ --ai-sidebar-active: #374151;
128
128
 
129
- --ai-header-bg: #111827;
130
- --ai-header-border: #374151;
129
+ --ai-header-bg: #0d1117;
130
+ --ai-header-border: #1f2937;
131
131
 
132
132
  --ai-agent-badge-bg: #1e3a5f;
133
133
  --ai-agent-badge-text: #60a5fa;
134
134
  --ai-agent-selector-bg: #1f2937;
135
135
  --ai-agent-selector-border: #374151;
136
136
 
137
- --ai-conversation-hover: rgba(255, 255, 255, 0.04);
137
+ --ai-conversation-hover: rgba(255, 255, 255, 0.05);
138
138
  --ai-conversation-active: rgba(255, 255, 255, 0.08);
139
- --ai-conversation-title-color: #f3f4f6;
139
+ --ai-conversation-title-color: #e5e7eb;
140
140
  --ai-conversation-preview-color: #9ca3af;
141
141
  --ai-conversation-meta-color: #6b7280;
142
142
 
@@ -149,7 +149,7 @@
149
149
  --ai-button-secondary-text: #f3f4f6;
150
150
  --ai-button-secondary-border: #4b5563;
151
151
  --ai-button-secondary-hover: #4b5563;
152
- --ai-button-ghost-hover: rgba(255, 255, 255, 0.08);
152
+ --ai-button-ghost-hover: #1f2937;
153
153
 
154
154
  --ai-input-bg: #1f2937;
155
155
  --ai-input-border: #374151;
@@ -395,7 +395,7 @@
395
395
  .ai-agent-panel__conversations {
396
396
  flex: 1;
397
397
  overflow-y: auto;
398
- padding: 8px;
398
+ padding: 8px 0;
399
399
  }
400
400
 
401
401
  .ai-agent-panel__empty {
@@ -427,16 +427,16 @@
427
427
 
428
428
  /* Groups */
429
429
  .ai-agent-panel__group {
430
- margin-bottom: 16px;
430
+ margin-bottom: 24px;
431
431
  }
432
432
 
433
433
  .ai-agent-panel__group-label {
434
434
  font-size: var(--ai-group-label-size);
435
- font-weight: 600;
435
+ font-weight: 400;
436
436
  color: var(--ai-group-label-color);
437
437
  text-transform: uppercase;
438
- letter-spacing: 0.05em;
439
- padding: 8px 8px 4px;
438
+ letter-spacing: 0.1em;
439
+ padding: 8px 12px 4px;
440
440
  }
441
441
 
442
442
  .ai-agent-panel__group-label--clickable {
@@ -445,11 +445,11 @@
445
445
  justify-content: space-between;
446
446
  cursor: pointer;
447
447
  user-select: none;
448
- transition: color 0.15s;
448
+ transition: opacity 0.15s;
449
449
  }
450
450
 
451
451
  .ai-agent-panel__group-label--clickable:hover {
452
- color: var(--ai-sidebar-text);
452
+ opacity: 0.7;
453
453
  }
454
454
 
455
455
  .ai-agent-panel__group-chevron {
@@ -466,10 +466,11 @@
466
466
  .ai-agent-panel__conversation {
467
467
  display: flex;
468
468
  align-items: flex-start;
469
- padding: 4px 8px;
470
- border-radius: 6px;
469
+ padding: 6px 12px;
470
+ margin: 0;
471
+ border-radius: 0;
471
472
  cursor: pointer;
472
- transition: background-color 0.15s;
473
+ transition: background-color 0.1s ease;
473
474
  gap: 8px;
474
475
  }
475
476
 
@@ -488,12 +489,12 @@
488
489
 
489
490
  .ai-agent-panel__conversation-title {
490
491
  font-weight: 400;
491
- font-size: 11px;
492
- color: var(--ai-conversation-preview-color);
492
+ font-size: 13px;
493
+ color: var(--ai-conversation-title-color);
493
494
  white-space: nowrap;
494
495
  overflow: hidden;
495
496
  text-overflow: ellipsis;
496
- margin-bottom: 2px;
497
+ line-height: 1.5;
497
498
  }
498
499
 
499
500
  .ai-agent-panel__conversation-preview {
@@ -1169,24 +1170,28 @@
1169
1170
  -------------------------------------------------------- */
1170
1171
 
1171
1172
  .ai-agent-panel__group--active {
1172
- background-color: var(--ai-conversation-active);
1173
- border-radius: 6px;
1174
- margin-bottom: 8px;
1175
- padding: 4px;
1173
+ margin-bottom: 24px;
1176
1174
  }
1177
1175
 
1178
1176
  .ai-agent-panel__group--active .ai-agent-panel__group-label {
1179
- color: var(--ai-button-primary-bg);
1180
- font-weight: 600;
1177
+ color: var(--ai-group-label-color);
1178
+ font-weight: 400;
1179
+ padding: 8px 12px 4px;
1181
1180
  }
1182
1181
 
1183
1182
  .ai-agent-panel__conversation--active-item {
1184
1183
  display: flex;
1185
1184
  align-items: center;
1186
1185
  justify-content: space-between;
1187
- background-color: var(--ai-sidebar-bg);
1188
- border-radius: 4px;
1189
- margin: 2px 0;
1186
+ background-color: transparent;
1187
+ padding: 6px 12px;
1188
+ margin: 0;
1189
+ transition: background-color 0.1s ease;
1190
+ cursor: pointer;
1191
+ }
1192
+
1193
+ .ai-agent-panel__conversation--active-item:hover {
1194
+ background-color: var(--ai-conversation-hover);
1190
1195
  }
1191
1196
 
1192
1197
  .ai-agent-panel__conversation--active-item .ai-agent-panel__conversation-content {
@@ -1201,8 +1206,7 @@
1201
1206
  }
1202
1207
 
1203
1208
  .ai-agent-panel__conversation--current {
1204
- background-color: var(--ai-conversation-active) !important;
1205
- border-left: 2px solid var(--ai-button-primary-bg);
1209
+ background-color: rgba(59, 130, 246, 0.08) !important;
1206
1210
  }
1207
1211
 
1208
1212
  .ai-agent-panel__conversation--in-active {
@@ -1243,8 +1247,9 @@
1243
1247
  /* Active badge for conversations in history */
1244
1248
  .ai-agent-panel__active-badge {
1245
1249
  color: var(--ai-button-primary-bg);
1246
- font-size: 8px;
1247
- margin-right: 2px;
1250
+ font-size: 7px;
1251
+ margin-right: 4px;
1252
+ opacity: 0.8;
1248
1253
  }
1249
1254
 
1250
1255
  /* Loading dot indicator */
@@ -1272,7 +1277,7 @@
1272
1277
  .ai-agent-panel__group-divider {
1273
1278
  height: 1px;
1274
1279
  background-color: var(--ai-sidebar-border);
1275
- margin: 12px 8px;
1280
+ margin: 20px 0;
1276
1281
  }
1277
1282
 
1278
1283
  /* Chat wrapper for multi-conversation support */
@@ -2352,4 +2352,4 @@
2352
2352
 
2353
2353
  .dark-theme .ai-chat-email-edit-button:hover {
2354
2354
  background-color: #374151;
2355
- }
2355
+ }
@@ -797,6 +797,7 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
797
797
  const [copiedCallId, setCopiedCallId] = useState<string | null>(null);
798
798
  const [feedbackCallId, setFeedbackCallId] = useState<{ callId: string; type: 'up' | 'down' } | null>(null);
799
799
  const [error, setError] = useState<{ message: string; code?: string } | null>(null);
800
+ const lastProcessedErrorRef = useRef<string | null>(null);
800
801
 
801
802
  // Email & Save state
802
803
  const [emailSent, setEmailSent] = useState(false);
@@ -1520,6 +1521,7 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1520
1521
 
1521
1522
  // Clear any previous errors
1522
1523
  setError(null);
1524
+ lastProcessedErrorRef.current = null; // Allow new errors to be processed
1523
1525
 
1524
1526
  // Reset scroll tracking for new message - enable auto-scroll
1525
1527
  setUserHasScrolled(false);
@@ -1632,12 +1634,44 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1632
1634
  // Error callback - handle errors immediately
1633
1635
  console.log('[AIChatPanel] Error callback triggered:', errorMsg);
1634
1636
 
1637
+ // Check if this is a user-initiated abort
1638
+ const isAbortError = errorMsg.toLowerCase().includes('abort') ||
1639
+ errorMsg.toLowerCase().includes('canceled') ||
1640
+ errorMsg.toLowerCase().includes('cancelled');
1641
+
1642
+ if (isAbortError) {
1643
+ // User canceled the request - don't show error banner
1644
+ console.log('[AIChatPanel] Request was aborted by user');
1645
+ // Don't set error state - no red banner
1646
+
1647
+ // Update history to show cancellation
1648
+ if (promptKey) {
1649
+ setHistory((prev) => ({
1650
+ ...prev,
1651
+ [promptKey]: {
1652
+ content: 'Response canceled',
1653
+ callId: lastCallId || '',
1654
+ },
1655
+ }));
1656
+ }
1657
+ }
1635
1658
  // Detect 413 Content Too Large error
1636
- if (errorMsg.includes('413') || errorMsg.toLowerCase().includes('content too large')) {
1659
+ else if (errorMsg.includes('413') || errorMsg.toLowerCase().includes('content too large')) {
1637
1660
  setError({
1638
1661
  message: 'The context is too large to process. Please start a new conversation or reduce the amount of context.',
1639
1662
  code: '413',
1640
1663
  });
1664
+
1665
+ // Update history to show error
1666
+ if (promptKey) {
1667
+ setHistory((prev) => ({
1668
+ ...prev,
1669
+ [promptKey]: {
1670
+ content: `Error: ${errorMsg}`,
1671
+ callId: lastCallId || '',
1672
+ },
1673
+ }));
1674
+ }
1641
1675
  }
1642
1676
  // Detect other network errors
1643
1677
  else if (errorMsg.toLowerCase().includes('network error') || errorMsg.toLowerCase().includes('fetch')) {
@@ -1645,6 +1679,17 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1645
1679
  message: 'Network error. Please check your connection and try again.',
1646
1680
  code: 'NETWORK_ERROR',
1647
1681
  });
1682
+
1683
+ // Update history to show error
1684
+ if (promptKey) {
1685
+ setHistory((prev) => ({
1686
+ ...prev,
1687
+ [promptKey]: {
1688
+ content: `Error: ${errorMsg}`,
1689
+ callId: lastCallId || '',
1690
+ },
1691
+ }));
1692
+ }
1648
1693
  }
1649
1694
  // Generic error
1650
1695
  else {
@@ -1652,21 +1697,21 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1652
1697
  message: errorMsg,
1653
1698
  code: 'UNKNOWN_ERROR',
1654
1699
  });
1700
+
1701
+ // Update history to show error
1702
+ if (promptKey) {
1703
+ setHistory((prev) => ({
1704
+ ...prev,
1705
+ [promptKey]: {
1706
+ content: `Error: ${errorMsg}`,
1707
+ callId: lastCallId || '',
1708
+ },
1709
+ }));
1710
+ }
1655
1711
  }
1656
1712
 
1657
1713
  // Reset loading state
1658
1714
  setIsLoading(false);
1659
-
1660
- // Update history to show error
1661
- if (promptKey) {
1662
- setHistory((prev) => ({
1663
- ...prev,
1664
- [promptKey]: {
1665
- content: `Error: ${errorMsg}`,
1666
- callId: lastCallId || '',
1667
- },
1668
- }));
1669
- }
1670
1715
  }
1671
1716
  );
1672
1717
 
@@ -1959,17 +2004,49 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1959
2004
  // Monitor for errors from useLLM hook
1960
2005
  useEffect(() => {
1961
2006
  if (llmError && llmError.trim()) {
2007
+ // Skip if we've already processed this exact error
2008
+ if (lastProcessedErrorRef.current === llmError) {
2009
+ console.log('[AIChatPanel] Skipping duplicate error:', llmError);
2010
+ return;
2011
+ }
2012
+
1962
2013
  console.log('[AIChatPanel] Error detected:', llmError);
2014
+ lastProcessedErrorRef.current = llmError;
1963
2015
 
1964
2016
  // Parse error message to detect specific error types
1965
2017
  const errorMessage = llmError;
1966
2018
 
2019
+ // Check if this is a user-initiated abort
2020
+ const isAbortError = errorMessage.toLowerCase().includes('abort') ||
2021
+ errorMessage.toLowerCase().includes('canceled') ||
2022
+ errorMessage.toLowerCase().includes('cancelled');
2023
+
2024
+ if (isAbortError) {
2025
+ // User canceled the request - don't show error banner
2026
+ console.log('[AIChatPanel] Request was aborted by user (useEffect)');
2027
+ // Don't set error state - no red banner
2028
+
2029
+ // Don't update history here - the error callback in send() already handled it
2030
+ // with the correct promptKey. Updating here with lastKey can affect the wrong entry
2031
+ // if the user has already submitted a new prompt.
2032
+ }
1967
2033
  // Detect 413 Content Too Large error
1968
- if (errorMessage.includes('413') || errorMessage.toLowerCase().includes('content too large')) {
2034
+ else if (errorMessage.includes('413') || errorMessage.toLowerCase().includes('content too large')) {
1969
2035
  setError({
1970
2036
  message: 'The context is too large to process. Please start a new conversation or reduce the amount of context.',
1971
2037
  code: '413',
1972
2038
  });
2039
+
2040
+ // Update history to show error
2041
+ if (lastKey) {
2042
+ setHistory((prev) => ({
2043
+ ...prev,
2044
+ [lastKey]: {
2045
+ content: `Error: ${errorMessage}`,
2046
+ callId: lastCallId || '',
2047
+ },
2048
+ }));
2049
+ }
1973
2050
  }
1974
2051
  // Detect other network errors
1975
2052
  else if (errorMessage.toLowerCase().includes('network error') || errorMessage.toLowerCase().includes('fetch')) {
@@ -1977,6 +2054,17 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1977
2054
  message: 'Network error. Please check your connection and try again.',
1978
2055
  code: 'NETWORK_ERROR',
1979
2056
  });
2057
+
2058
+ // Update history to show error
2059
+ if (lastKey) {
2060
+ setHistory((prev) => ({
2061
+ ...prev,
2062
+ [lastKey]: {
2063
+ content: `Error: ${errorMessage}`,
2064
+ callId: lastCallId || '',
2065
+ },
2066
+ }));
2067
+ }
1980
2068
  }
1981
2069
  // Generic error
1982
2070
  else {
@@ -1984,21 +2072,21 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
1984
2072
  message: errorMessage,
1985
2073
  code: 'UNKNOWN_ERROR',
1986
2074
  });
2075
+
2076
+ // Update history to show error
2077
+ if (lastKey) {
2078
+ setHistory((prev) => ({
2079
+ ...prev,
2080
+ [lastKey]: {
2081
+ content: `Error: ${errorMessage}`,
2082
+ callId: lastCallId || '',
2083
+ },
2084
+ }));
2085
+ }
1987
2086
  }
1988
2087
 
1989
2088
  // Reset loading state
1990
2089
  setIsLoading(false);
1991
-
1992
- // Update history to show error
1993
- if (lastKey) {
1994
- setHistory((prev) => ({
1995
- ...prev,
1996
- [lastKey]: {
1997
- content: `Error: ${errorMessage}`,
1998
- callId: lastCallId || '',
1999
- },
2000
- }));
2001
- }
2002
2090
  }
2003
2091
  }, [llmError, lastKey, lastCallId]);
2004
2092