@hef2024/llmasaservice-ui 0.26.0 → 0.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.css CHANGED
@@ -2575,59 +2575,6 @@ button[data-pending=true]::after {
2575
2575
  .ai-agent-panel__chat-header-spacer {
2576
2576
  flex: 1;
2577
2577
  }
2578
- .ai-agent-panel__context-notification {
2579
- position: absolute;
2580
- bottom: 70px;
2581
- left: 50%;
2582
- transform: translateX(-50%);
2583
- z-index: 50;
2584
- display: flex;
2585
- align-items: center;
2586
- gap: 8px;
2587
- padding: 8px 16px;
2588
- background-color: var(--ai-agent-badge-bg);
2589
- color: var(--ai-agent-badge-text);
2590
- border-radius: 20px;
2591
- font-size: 13px;
2592
- font-weight: 500;
2593
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
2594
- animation: ai-context-notification-in 0.3s ease-out, ai-context-notification-out 0.3s ease-in 2.7s forwards;
2595
- pointer-events: none;
2596
- }
2597
- .ai-agent-panel__context-notification svg {
2598
- flex-shrink: 0;
2599
- animation: ai-sparkle-pulse 0.6s ease-in-out infinite alternate;
2600
- }
2601
- @keyframes ai-context-notification-in {
2602
- 0% {
2603
- opacity: 0;
2604
- transform: translateX(-50%) translateY(10px) scale(0.9);
2605
- }
2606
- 100% {
2607
- opacity: 1;
2608
- transform: translateX(-50%) translateY(0) scale(1);
2609
- }
2610
- }
2611
- @keyframes ai-context-notification-out {
2612
- 0% {
2613
- opacity: 1;
2614
- transform: translateX(-50%) translateY(0) scale(1);
2615
- }
2616
- 100% {
2617
- opacity: 0;
2618
- transform: translateX(-50%) translateY(10px) scale(0.9);
2619
- }
2620
- }
2621
- @keyframes ai-sparkle-pulse {
2622
- 0% {
2623
- opacity: 0.7;
2624
- transform: scale(0.9);
2625
- }
2626
- 100% {
2627
- opacity: 1;
2628
- transform: scale(1.1);
2629
- }
2630
- }
2631
2578
 
2632
2579
  /* src/AIChatPanel.css */
2633
2580
  :root {
@@ -4059,7 +4006,7 @@ button[data-pending=true]::after {
4059
4006
  align-items: center;
4060
4007
  gap: 4px;
4061
4008
  flex-shrink: 0;
4062
- margin-left: auto;
4009
+ margin-left: 0;
4063
4010
  }
4064
4011
  .ai-chat-context-pill-anchor {
4065
4012
  position: relative;
@@ -4141,8 +4088,10 @@ button[data-pending=true]::after {
4141
4088
  .ai-chat-context-pill {
4142
4089
  display: flex;
4143
4090
  align-items: center;
4144
- gap: 6px;
4145
- padding: 4px 10px;
4091
+ justify-content: center;
4092
+ gap: 0;
4093
+ min-width: 30px;
4094
+ padding: 4px 8px;
4146
4095
  background: var(--ai-chat-suggestion-bg, #f3f4f6);
4147
4096
  border: none;
4148
4097
  border-radius: 999px;
@@ -4163,6 +4112,9 @@ button[data-pending=true]::after {
4163
4112
  .ai-chat-context-pill--warning {
4164
4113
  color: #f59e0b;
4165
4114
  }
4115
+ .ai-chat-context-pill--updated {
4116
+ box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.24), 0 0 10px rgba(245, 158, 11, 0.18);
4117
+ }
4166
4118
  .ai-chat-context-pill__count {
4167
4119
  display: none;
4168
4120
  border-radius: 999px;
@@ -4172,6 +4124,43 @@ button[data-pending=true]::after {
4172
4124
  color: var(--ai-sidebar-text-muted, #6b7280);
4173
4125
  font-size: 12px;
4174
4126
  }
4127
+ .ai-chat-context-pill__icon {
4128
+ position: relative;
4129
+ display: inline-flex;
4130
+ align-items: center;
4131
+ justify-content: center;
4132
+ width: 14px;
4133
+ height: 14px;
4134
+ }
4135
+ .ai-chat-context-pill__icon .ai-chat-icon-sm {
4136
+ width: 14px;
4137
+ height: 14px;
4138
+ }
4139
+ .ai-chat-context-pill__spark {
4140
+ position: absolute;
4141
+ top: -7px;
4142
+ right: -7px;
4143
+ font-size: 10px;
4144
+ line-height: 1;
4145
+ color: #f59e0b;
4146
+ text-shadow: 0 0 6px rgba(245, 158, 11, 0.55);
4147
+ pointer-events: none;
4148
+ animation: contextPillSpark 1.6s ease-out forwards;
4149
+ }
4150
+ @keyframes contextPillSpark {
4151
+ 0% {
4152
+ opacity: 0;
4153
+ transform: scale(0.7) translateY(2px);
4154
+ }
4155
+ 24% {
4156
+ opacity: 0.95;
4157
+ transform: scale(1) translateY(0);
4158
+ }
4159
+ 100% {
4160
+ opacity: 0;
4161
+ transform: scale(1.08) translateY(-2px);
4162
+ }
4163
+ }
4175
4164
  .ai-chat-context-pill__tokens {
4176
4165
  display: none;
4177
4166
  }
@@ -4541,6 +4530,9 @@ button[data-pending=true]::after {
4541
4530
  .dark-theme .ai-chat-context-pill--active {
4542
4531
  background: var(--ai-chat-suggestion-hover-bg, #4b5563);
4543
4532
  }
4533
+ .dark-theme .ai-chat-context-pill--updated {
4534
+ box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.35), 0 0 12px rgba(245, 158, 11, 0.22);
4535
+ }
4544
4536
  .dark-theme .ai-chat-context-popover {
4545
4537
  background: var(--ai-chat-input-bg, #1f2937);
4546
4538
  border-color: var(--ai-chat-input-border, #374151);
@@ -4580,6 +4572,15 @@ button[data-pending=true]::after {
4580
4572
  }
4581
4573
  }
4582
4574
  }
4575
+ @media (prefers-reduced-motion: reduce) {
4576
+ .ai-chat-context-pill {
4577
+ transition: none;
4578
+ }
4579
+ .ai-chat-context-pill__spark {
4580
+ animation: none;
4581
+ opacity: 0.9;
4582
+ }
4583
+ }
4583
4584
  .ai-chat-error-banner {
4584
4585
  display: flex;
4585
4586
  align-items: flex-start;
package/dist/index.d.mts CHANGED
@@ -304,6 +304,7 @@ interface AIChatPanelProps {
304
304
  contextSections?: ContextSection$1[];
305
305
  totalContextTokens?: number;
306
306
  maxContextTokens?: number;
307
+ contextUpdateSignal?: number;
307
308
  enableContextDetailView?: boolean;
308
309
  disabledSectionIds?: Set<string>;
309
310
  onToggleSection?: (sectionId: string, enabled: boolean) => void;
package/dist/index.d.ts CHANGED
@@ -304,6 +304,7 @@ interface AIChatPanelProps {
304
304
  contextSections?: ContextSection$1[];
305
305
  totalContextTokens?: number;
306
306
  maxContextTokens?: number;
307
+ contextUpdateSignal?: number;
307
308
  enableContextDetailView?: boolean;
308
309
  disabledSectionIds?: Set<string>;
309
310
  onToggleSection?: (sectionId: string, enabled: boolean) => void;
package/dist/index.js CHANGED
@@ -5140,6 +5140,7 @@ var ChevronUpIcon = () => /* @__PURE__ */ import_react14.default.createElement("
5140
5140
  var AgentIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "M12 8V4H8" }), /* @__PURE__ */ import_react14.default.createElement("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M2 14h2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M20 14h2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M15 13v2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M9 13v2" }));
5141
5141
  var ToolIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "M14.7 6.3a4 4 0 0 0-5.4 5.4l-6 6a2 2 0 0 0 2.8 2.8l6-6a4 4 0 0 0 5.4-5.4l-2.1 2.1-3.3-3.3 2.6-1.6Z" }));
5142
5142
  var CheckIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("polyline", { points: "20 6 9 17 4 12" }));
5143
+ var ContextViewerIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }), /* @__PURE__ */ import_react14.default.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "16", x2: "8", y1: "13", y2: "13" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "16", x2: "8", y1: "17", y2: "17" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "10", x2: "8", y1: "9", y2: "9" }));
5143
5144
  var LLMAsAServiceLogo = () => /* @__PURE__ */ import_react14.default.createElement("svg", { width: "16", height: "16", viewBox: "0 0 72 72", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "14.0868", cy: "59.2146", rx: "7.8261", ry: "7.7854", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "24.9013", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "45.391", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "65.8813", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "35.1461", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "55.6364", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "45.391", cy: "10.3959", rx: "2.70351", ry: "2.68919", fill: "#2487D8" }));
5144
5145
  var AlertCircleIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "12", x2: "12", y1: "8", y2: "12" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" }));
5145
5146
  var CloseIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("line", { x1: "18", x2: "6", y1: "6", y2: "18" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "6", x2: "18", y1: "6", y2: "18" }));
@@ -5162,6 +5163,7 @@ var ChatInput = import_react14.default.memo(({
5162
5163
  contextSections = [],
5163
5164
  totalContextTokens = 0,
5164
5165
  maxContextTokens = 8e3,
5166
+ contextUpdateSignal = 0,
5165
5167
  enableContextDetailView = false,
5166
5168
  disabledSectionIds = /* @__PURE__ */ new Set(),
5167
5169
  onToggleSection,
@@ -5172,9 +5174,12 @@ var ChatInput = import_react14.default.memo(({
5172
5174
  const [contextViewerOpen, setContextViewerOpen] = (0, import_react14.useState)(false);
5173
5175
  const [contextViewMode, setContextViewMode] = (0, import_react14.useState)("summary");
5174
5176
  const [expandedSectionId, setExpandedSectionId] = (0, import_react14.useState)(null);
5177
+ const [showContextUpdateCue, setShowContextUpdateCue] = (0, import_react14.useState)(false);
5178
+ const [contextUpdateCueKey, setContextUpdateCueKey] = (0, import_react14.useState)(0);
5175
5179
  const textareaRef = (0, import_react14.useRef)(null);
5176
5180
  const containerRef = (0, import_react14.useRef)(null);
5177
5181
  const contextPopoverRef = (0, import_react14.useRef)(null);
5182
+ const contextUpdateCueTimeoutRef = (0, import_react14.useRef)(null);
5178
5183
  const [userInputSelections, setUserInputSelections] = (0, import_react14.useState)({});
5179
5184
  const [userInputWriteIns, setUserInputWriteIns] = (0, import_react14.useState)({});
5180
5185
  const [userInputValidationError, setUserInputValidationError] = (0, import_react14.useState)("");
@@ -5284,6 +5289,28 @@ var ChatInput = import_react14.default.memo(({
5284
5289
  return () => document.removeEventListener("mousedown", handleClickOutside);
5285
5290
  }
5286
5291
  }, [contextViewerOpen]);
5292
+ (0, import_react14.useEffect)(() => {
5293
+ if (contextUpdateSignal <= 0) {
5294
+ return;
5295
+ }
5296
+ setShowContextUpdateCue(true);
5297
+ setContextUpdateCueKey((prev) => prev + 1);
5298
+ if (contextUpdateCueTimeoutRef.current) {
5299
+ window.clearTimeout(contextUpdateCueTimeoutRef.current);
5300
+ contextUpdateCueTimeoutRef.current = null;
5301
+ }
5302
+ contextUpdateCueTimeoutRef.current = window.setTimeout(() => {
5303
+ setShowContextUpdateCue(false);
5304
+ contextUpdateCueTimeoutRef.current = null;
5305
+ }, 1800);
5306
+ }, [contextUpdateSignal]);
5307
+ (0, import_react14.useEffect)(() => {
5308
+ return () => {
5309
+ if (contextUpdateCueTimeoutRef.current) {
5310
+ window.clearTimeout(contextUpdateCueTimeoutRef.current);
5311
+ }
5312
+ };
5313
+ }, []);
5287
5314
  const formatTokens = (tokens) => {
5288
5315
  if (tokens >= 1e3) {
5289
5316
  return `${(tokens / 1e3).toFixed(1)}K`;
@@ -5539,11 +5566,12 @@ var ChatInput = import_react14.default.memo(({
5539
5566
  ), agentOptions.length === 0 && !isAgentModeActionVisible ? /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-panel__input-footer-spacer" }) : null), contextSections.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-pill-wrapper" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-pill-anchor" }, /* @__PURE__ */ import_react14.default.createElement(
5540
5567
  "button",
5541
5568
  {
5542
- className: `ai-chat-context-pill ${contextViewerOpen ? "ai-chat-context-pill--active" : ""} ${isOverLimit ? "ai-chat-context-pill--warning" : ""}`,
5569
+ className: `ai-chat-context-pill ${contextViewerOpen ? "ai-chat-context-pill--active" : ""} ${isOverLimit ? "ai-chat-context-pill--warning" : ""} ${showContextUpdateCue && !contextViewerOpen ? "ai-chat-context-pill--updated" : ""}`,
5543
5570
  onClick: (e) => {
5544
5571
  e.preventDefault();
5545
5572
  e.stopPropagation();
5546
5573
  console.log("[ContextViewer] Button clicked, current state:", contextViewerOpen);
5574
+ setShowContextUpdateCue(false);
5547
5575
  setContextViewerOpen(!contextViewerOpen);
5548
5576
  if (!contextViewerOpen) {
5549
5577
  setContextViewMode("summary");
@@ -5553,9 +5581,18 @@ var ChatInput = import_react14.default.memo(({
5553
5581
  }
5554
5582
  },
5555
5583
  type: "button",
5556
- title: "View context"
5584
+ title: "View context",
5585
+ "aria-label": "View context"
5557
5586
  },
5558
- /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-pill__label" }, "context: ", contextSections.length, " ", contextSections.length === 1 ? "section" : "sections")
5587
+ /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-pill__icon", "aria-hidden": "true" }, /* @__PURE__ */ import_react14.default.createElement(ContextViewerIcon, null), showContextUpdateCue && !contextViewerOpen && /* @__PURE__ */ import_react14.default.createElement(
5588
+ "span",
5589
+ {
5590
+ key: contextUpdateCueKey,
5591
+ className: "ai-chat-context-pill__spark",
5592
+ "aria-hidden": "true"
5593
+ },
5594
+ "*"
5595
+ ))
5559
5596
  ), contextViewerOpen && /* @__PURE__ */ import_react14.default.createElement(
5560
5597
  "div",
5561
5598
  {
@@ -5749,6 +5786,7 @@ var AIChatPanel = ({
5749
5786
  contextSections = [],
5750
5787
  totalContextTokens = 0,
5751
5788
  maxContextTokens = 8e3,
5789
+ contextUpdateSignal = 0,
5752
5790
  enableContextDetailView = false,
5753
5791
  disabledSectionIds: propDisabledSectionIds,
5754
5792
  onToggleSection: propOnToggleSection,
@@ -8931,6 +8969,7 @@ ${traceSummary}` : traceSummary;
8931
8969
  contextSections,
8932
8970
  totalContextTokens,
8933
8971
  maxContextTokens,
8972
+ contextUpdateSignal,
8934
8973
  enableContextDetailView,
8935
8974
  disabledSectionIds,
8936
8975
  onToggleSection: handleToggleSection,
@@ -9163,7 +9202,6 @@ var MessageIcon = () => /* @__PURE__ */ import_react16.default.createElement("sv
9163
9202
  var CloseIcon2 = () => /* @__PURE__ */ import_react16.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react16.default.createElement("path", { d: "M9 3L3 9M3 3l6 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }));
9164
9203
  var LoadingDotIcon = () => /* @__PURE__ */ import_react16.default.createElement("span", { className: "ai-agent-panel__loading-dot" });
9165
9204
  var SidebarIcon = () => /* @__PURE__ */ import_react16.default.createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react16.default.createElement("rect", { x: "2", y: "2", width: "12", height: "12", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }), /* @__PURE__ */ import_react16.default.createElement("path", { d: "M6 2v12", stroke: "currentColor", strokeWidth: "1.5" }));
9166
- var SparkleIcon = () => /* @__PURE__ */ import_react16.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react16.default.createElement("path", { d: "M8 1v3M8 12v3M3 8H0M16 8h-3M12.95 3.05l-2.12 2.12M5.17 10.83l-2.12 2.12M12.95 12.95l-2.12-2.12M5.17 5.17L3.05 3.05", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }), /* @__PURE__ */ import_react16.default.createElement("circle", { cx: "8", cy: "8", r: "2", fill: "currentColor" }));
9167
9205
  var normalizeConversationListPayload = (payload) => {
9168
9206
  if (!payload) return [];
9169
9207
  const conversations = Array.isArray(payload) ? payload : payload.conversations || [];
@@ -9656,6 +9694,7 @@ var ChatPanelWrapper = ({
9656
9694
  contextSections,
9657
9695
  totalContextTokens,
9658
9696
  maxContextTokens,
9697
+ contextUpdateSignal,
9659
9698
  enableContextDetailView,
9660
9699
  disabledSectionIds,
9661
9700
  onToggleSection,
@@ -9778,6 +9817,7 @@ var ChatPanelWrapper = ({
9778
9817
  contextSections,
9779
9818
  totalContextTokens,
9780
9819
  maxContextTokens,
9820
+ contextUpdateSignal,
9781
9821
  enableContextDetailView,
9782
9822
  disabledSectionIds,
9783
9823
  onToggleSection,
@@ -10129,9 +10169,8 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10129
10169
  commitConversationSelection(tempId, true);
10130
10170
  }
10131
10171
  }), [commitConversationSelection, createDraftConversation, currentAgentId]);
10132
- const [showContextNotification, setShowContextNotification] = (0, import_react16.useState)(false);
10172
+ const [contextUpdateSignal, setContextUpdateSignal] = (0, import_react16.useState)(0);
10133
10173
  const prevContextRef = (0, import_react16.useRef)(null);
10134
- const contextNotificationTimeoutRef = (0, import_react16.useRef)(null);
10135
10174
  const prevDefaultAgentRef = (0, import_react16.useRef)(null);
10136
10175
  const fetchFirstPrompt = (0, import_react16.useCallback)((conversationId, agentIdForConversation) => __async(void 0, null, function* () {
10137
10176
  var _a2, _b2;
@@ -10692,24 +10731,9 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10692
10731
  const changed = prevContextRef.current !== contextString;
10693
10732
  if (changed) {
10694
10733
  prevContextRef.current = contextString;
10695
- if (contextNotificationTimeoutRef.current) {
10696
- clearTimeout(contextNotificationTimeoutRef.current);
10697
- contextNotificationTimeoutRef.current = null;
10698
- }
10699
- setShowContextNotification(true);
10700
- contextNotificationTimeoutRef.current = setTimeout(() => {
10701
- setShowContextNotification(false);
10702
- contextNotificationTimeoutRef.current = null;
10703
- }, 3e3);
10734
+ setContextUpdateSignal((prev) => prev + 1);
10704
10735
  }
10705
10736
  }, [mergedContext.sections]);
10706
- (0, import_react16.useEffect)(() => {
10707
- return () => {
10708
- if (contextNotificationTimeoutRef.current) {
10709
- clearTimeout(contextNotificationTimeoutRef.current);
10710
- }
10711
- };
10712
- }, []);
10713
10737
  (0, import_react16.useEffect)(() => {
10714
10738
  var _a2, _b2;
10715
10739
  let foundDefaultAgent = null;
@@ -11240,7 +11264,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
11240
11264
  /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-title" }, isActive && /* @__PURE__ */ import_react16.default.createElement("span", { className: "ai-agent-panel__active-badge" }, "\u25CF"), conversationFirstPrompts[conv.conversationId] || conv.title), subtitle ? /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-subtitle" }, subtitle) : null)
11241
11265
  );
11242
11266
  }))))))),
11243
- /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ import_react16.default.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ import_react16.default.createElement(ChevronRightIcon, null) : /* @__PURE__ */ import_react16.default.createElement(ChevronLeftIcon, null)))), showContextNotification && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__context-notification" }, /* @__PURE__ */ import_react16.default.createElement(SparkleIcon, null), /* @__PURE__ */ import_react16.default.createElement("span", null, "Agent now has new context")), activeConversationsList.map((activeConv) => /* @__PURE__ */ import_react16.default.createElement(
11267
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ import_react16.default.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ import_react16.default.createElement(ChevronRightIcon, null) : /* @__PURE__ */ import_react16.default.createElement(ChevronLeftIcon, null)))), activeConversationsList.map((activeConv) => /* @__PURE__ */ import_react16.default.createElement(
11244
11268
  ChatPanelWrapper,
11245
11269
  {
11246
11270
  key: activeConv.stableKey,
@@ -11270,6 +11294,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
11270
11294
  contextSections: mergedContext.sections,
11271
11295
  totalContextTokens: filteredContext.totalTokens,
11272
11296
  maxContextTokens,
11297
+ contextUpdateSignal,
11273
11298
  enableContextDetailView,
11274
11299
  disabledSectionIds: currentDisabledSections,
11275
11300
  onToggleSection: handleContextSectionToggle,
package/dist/index.mjs CHANGED
@@ -5105,6 +5105,7 @@ var ChevronUpIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns:
5105
5105
  var AgentIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ React14.createElement("path", { d: "M12 8V4H8" }), /* @__PURE__ */ React14.createElement("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), /* @__PURE__ */ React14.createElement("path", { d: "M2 14h2" }), /* @__PURE__ */ React14.createElement("path", { d: "M20 14h2" }), /* @__PURE__ */ React14.createElement("path", { d: "M15 13v2" }), /* @__PURE__ */ React14.createElement("path", { d: "M9 13v2" }));
5106
5106
  var ToolIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ React14.createElement("path", { d: "M14.7 6.3a4 4 0 0 0-5.4 5.4l-6 6a2 2 0 0 0 2.8 2.8l6-6a4 4 0 0 0 5.4-5.4l-2.1 2.1-3.3-3.3 2.6-1.6Z" }));
5107
5107
  var CheckIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ React14.createElement("polyline", { points: "20 6 9 17 4 12" }));
5108
+ var ContextViewerIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ React14.createElement("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }), /* @__PURE__ */ React14.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React14.createElement("line", { x1: "16", x2: "8", y1: "13", y2: "13" }), /* @__PURE__ */ React14.createElement("line", { x1: "16", x2: "8", y1: "17", y2: "17" }), /* @__PURE__ */ React14.createElement("line", { x1: "10", x2: "8", y1: "9", y2: "9" }));
5108
5109
  var LLMAsAServiceLogo = () => /* @__PURE__ */ React14.createElement("svg", { width: "16", height: "16", viewBox: "0 0 72 72", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React14.createElement("ellipse", { cx: "14.0868", cy: "59.2146", rx: "7.8261", ry: "7.7854", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "24.9013", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "45.391", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "65.8813", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "35.1461", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "55.6364", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "45.391", cy: "10.3959", rx: "2.70351", ry: "2.68919", fill: "#2487D8" }));
5109
5110
  var AlertCircleIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ React14.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React14.createElement("line", { x1: "12", x2: "12", y1: "8", y2: "12" }), /* @__PURE__ */ React14.createElement("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" }));
5110
5111
  var CloseIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ React14.createElement("line", { x1: "18", x2: "6", y1: "6", y2: "18" }), /* @__PURE__ */ React14.createElement("line", { x1: "6", x2: "18", y1: "6", y2: "18" }));
@@ -5127,6 +5128,7 @@ var ChatInput = React14.memo(({
5127
5128
  contextSections = [],
5128
5129
  totalContextTokens = 0,
5129
5130
  maxContextTokens = 8e3,
5131
+ contextUpdateSignal = 0,
5130
5132
  enableContextDetailView = false,
5131
5133
  disabledSectionIds = /* @__PURE__ */ new Set(),
5132
5134
  onToggleSection,
@@ -5137,9 +5139,12 @@ var ChatInput = React14.memo(({
5137
5139
  const [contextViewerOpen, setContextViewerOpen] = useState7(false);
5138
5140
  const [contextViewMode, setContextViewMode] = useState7("summary");
5139
5141
  const [expandedSectionId, setExpandedSectionId] = useState7(null);
5142
+ const [showContextUpdateCue, setShowContextUpdateCue] = useState7(false);
5143
+ const [contextUpdateCueKey, setContextUpdateCueKey] = useState7(0);
5140
5144
  const textareaRef = useRef6(null);
5141
5145
  const containerRef = useRef6(null);
5142
5146
  const contextPopoverRef = useRef6(null);
5147
+ const contextUpdateCueTimeoutRef = useRef6(null);
5143
5148
  const [userInputSelections, setUserInputSelections] = useState7({});
5144
5149
  const [userInputWriteIns, setUserInputWriteIns] = useState7({});
5145
5150
  const [userInputValidationError, setUserInputValidationError] = useState7("");
@@ -5249,6 +5254,28 @@ var ChatInput = React14.memo(({
5249
5254
  return () => document.removeEventListener("mousedown", handleClickOutside);
5250
5255
  }
5251
5256
  }, [contextViewerOpen]);
5257
+ useEffect8(() => {
5258
+ if (contextUpdateSignal <= 0) {
5259
+ return;
5260
+ }
5261
+ setShowContextUpdateCue(true);
5262
+ setContextUpdateCueKey((prev) => prev + 1);
5263
+ if (contextUpdateCueTimeoutRef.current) {
5264
+ window.clearTimeout(contextUpdateCueTimeoutRef.current);
5265
+ contextUpdateCueTimeoutRef.current = null;
5266
+ }
5267
+ contextUpdateCueTimeoutRef.current = window.setTimeout(() => {
5268
+ setShowContextUpdateCue(false);
5269
+ contextUpdateCueTimeoutRef.current = null;
5270
+ }, 1800);
5271
+ }, [contextUpdateSignal]);
5272
+ useEffect8(() => {
5273
+ return () => {
5274
+ if (contextUpdateCueTimeoutRef.current) {
5275
+ window.clearTimeout(contextUpdateCueTimeoutRef.current);
5276
+ }
5277
+ };
5278
+ }, []);
5252
5279
  const formatTokens = (tokens) => {
5253
5280
  if (tokens >= 1e3) {
5254
5281
  return `${(tokens / 1e3).toFixed(1)}K`;
@@ -5504,11 +5531,12 @@ var ChatInput = React14.memo(({
5504
5531
  ), agentOptions.length === 0 && !isAgentModeActionVisible ? /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-panel__input-footer-spacer" }) : null), contextSections.length > 0 && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-pill-wrapper" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-pill-anchor" }, /* @__PURE__ */ React14.createElement(
5505
5532
  "button",
5506
5533
  {
5507
- className: `ai-chat-context-pill ${contextViewerOpen ? "ai-chat-context-pill--active" : ""} ${isOverLimit ? "ai-chat-context-pill--warning" : ""}`,
5534
+ className: `ai-chat-context-pill ${contextViewerOpen ? "ai-chat-context-pill--active" : ""} ${isOverLimit ? "ai-chat-context-pill--warning" : ""} ${showContextUpdateCue && !contextViewerOpen ? "ai-chat-context-pill--updated" : ""}`,
5508
5535
  onClick: (e) => {
5509
5536
  e.preventDefault();
5510
5537
  e.stopPropagation();
5511
5538
  console.log("[ContextViewer] Button clicked, current state:", contextViewerOpen);
5539
+ setShowContextUpdateCue(false);
5512
5540
  setContextViewerOpen(!contextViewerOpen);
5513
5541
  if (!contextViewerOpen) {
5514
5542
  setContextViewMode("summary");
@@ -5518,9 +5546,18 @@ var ChatInput = React14.memo(({
5518
5546
  }
5519
5547
  },
5520
5548
  type: "button",
5521
- title: "View context"
5549
+ title: "View context",
5550
+ "aria-label": "View context"
5522
5551
  },
5523
- /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-pill__label" }, "context: ", contextSections.length, " ", contextSections.length === 1 ? "section" : "sections")
5552
+ /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-pill__icon", "aria-hidden": "true" }, /* @__PURE__ */ React14.createElement(ContextViewerIcon, null), showContextUpdateCue && !contextViewerOpen && /* @__PURE__ */ React14.createElement(
5553
+ "span",
5554
+ {
5555
+ key: contextUpdateCueKey,
5556
+ className: "ai-chat-context-pill__spark",
5557
+ "aria-hidden": "true"
5558
+ },
5559
+ "*"
5560
+ ))
5524
5561
  ), contextViewerOpen && /* @__PURE__ */ React14.createElement(
5525
5562
  "div",
5526
5563
  {
@@ -5714,6 +5751,7 @@ var AIChatPanel = ({
5714
5751
  contextSections = [],
5715
5752
  totalContextTokens = 0,
5716
5753
  maxContextTokens = 8e3,
5754
+ contextUpdateSignal = 0,
5717
5755
  enableContextDetailView = false,
5718
5756
  disabledSectionIds: propDisabledSectionIds,
5719
5757
  onToggleSection: propOnToggleSection,
@@ -8896,6 +8934,7 @@ ${traceSummary}` : traceSummary;
8896
8934
  contextSections,
8897
8935
  totalContextTokens,
8898
8936
  maxContextTokens,
8937
+ contextUpdateSignal,
8899
8938
  enableContextDetailView,
8900
8939
  disabledSectionIds,
8901
8940
  onToggleSection: handleToggleSection,
@@ -9128,7 +9167,6 @@ var MessageIcon = () => /* @__PURE__ */ React15.createElement("svg", { width: "1
9128
9167
  var CloseIcon2 = () => /* @__PURE__ */ React15.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React15.createElement("path", { d: "M9 3L3 9M3 3l6 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }));
9129
9168
  var LoadingDotIcon = () => /* @__PURE__ */ React15.createElement("span", { className: "ai-agent-panel__loading-dot" });
9130
9169
  var SidebarIcon = () => /* @__PURE__ */ React15.createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React15.createElement("rect", { x: "2", y: "2", width: "12", height: "12", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }), /* @__PURE__ */ React15.createElement("path", { d: "M6 2v12", stroke: "currentColor", strokeWidth: "1.5" }));
9131
- var SparkleIcon = () => /* @__PURE__ */ React15.createElement("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React15.createElement("path", { d: "M8 1v3M8 12v3M3 8H0M16 8h-3M12.95 3.05l-2.12 2.12M5.17 10.83l-2.12 2.12M12.95 12.95l-2.12-2.12M5.17 5.17L3.05 3.05", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }), /* @__PURE__ */ React15.createElement("circle", { cx: "8", cy: "8", r: "2", fill: "currentColor" }));
9132
9170
  var normalizeConversationListPayload = (payload) => {
9133
9171
  if (!payload) return [];
9134
9172
  const conversations = Array.isArray(payload) ? payload : payload.conversations || [];
@@ -9621,6 +9659,7 @@ var ChatPanelWrapper = ({
9621
9659
  contextSections,
9622
9660
  totalContextTokens,
9623
9661
  maxContextTokens,
9662
+ contextUpdateSignal,
9624
9663
  enableContextDetailView,
9625
9664
  disabledSectionIds,
9626
9665
  onToggleSection,
@@ -9743,6 +9782,7 @@ var ChatPanelWrapper = ({
9743
9782
  contextSections,
9744
9783
  totalContextTokens,
9745
9784
  maxContextTokens,
9785
+ contextUpdateSignal,
9746
9786
  enableContextDetailView,
9747
9787
  disabledSectionIds,
9748
9788
  onToggleSection,
@@ -10094,9 +10134,8 @@ var AIAgentPanel = React15.forwardRef(({
10094
10134
  commitConversationSelection(tempId, true);
10095
10135
  }
10096
10136
  }), [commitConversationSelection, createDraftConversation, currentAgentId]);
10097
- const [showContextNotification, setShowContextNotification] = useState9(false);
10137
+ const [contextUpdateSignal, setContextUpdateSignal] = useState9(0);
10098
10138
  const prevContextRef = useRef7(null);
10099
- const contextNotificationTimeoutRef = useRef7(null);
10100
10139
  const prevDefaultAgentRef = useRef7(null);
10101
10140
  const fetchFirstPrompt = useCallback4((conversationId, agentIdForConversation) => __async(void 0, null, function* () {
10102
10141
  var _a2, _b2;
@@ -10657,24 +10696,9 @@ var AIAgentPanel = React15.forwardRef(({
10657
10696
  const changed = prevContextRef.current !== contextString;
10658
10697
  if (changed) {
10659
10698
  prevContextRef.current = contextString;
10660
- if (contextNotificationTimeoutRef.current) {
10661
- clearTimeout(contextNotificationTimeoutRef.current);
10662
- contextNotificationTimeoutRef.current = null;
10663
- }
10664
- setShowContextNotification(true);
10665
- contextNotificationTimeoutRef.current = setTimeout(() => {
10666
- setShowContextNotification(false);
10667
- contextNotificationTimeoutRef.current = null;
10668
- }, 3e3);
10699
+ setContextUpdateSignal((prev) => prev + 1);
10669
10700
  }
10670
10701
  }, [mergedContext.sections]);
10671
- useEffect10(() => {
10672
- return () => {
10673
- if (contextNotificationTimeoutRef.current) {
10674
- clearTimeout(contextNotificationTimeoutRef.current);
10675
- }
10676
- };
10677
- }, []);
10678
10702
  useEffect10(() => {
10679
10703
  var _a2, _b2;
10680
10704
  let foundDefaultAgent = null;
@@ -11205,7 +11229,7 @@ var AIAgentPanel = React15.forwardRef(({
11205
11229
  /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-title" }, isActive && /* @__PURE__ */ React15.createElement("span", { className: "ai-agent-panel__active-badge" }, "\u25CF"), conversationFirstPrompts[conv.conversationId] || conv.title), subtitle ? /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-subtitle" }, subtitle) : null)
11206
11230
  );
11207
11231
  }))))))),
11208
- /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ React15.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ React15.createElement(ChevronRightIcon, null) : /* @__PURE__ */ React15.createElement(ChevronLeftIcon, null)))), showContextNotification && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__context-notification" }, /* @__PURE__ */ React15.createElement(SparkleIcon, null), /* @__PURE__ */ React15.createElement("span", null, "Agent now has new context")), activeConversationsList.map((activeConv) => /* @__PURE__ */ React15.createElement(
11232
+ /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ React15.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ React15.createElement(ChevronRightIcon, null) : /* @__PURE__ */ React15.createElement(ChevronLeftIcon, null)))), activeConversationsList.map((activeConv) => /* @__PURE__ */ React15.createElement(
11209
11233
  ChatPanelWrapper,
11210
11234
  {
11211
11235
  key: activeConv.stableKey,
@@ -11235,6 +11259,7 @@ var AIAgentPanel = React15.forwardRef(({
11235
11259
  contextSections: mergedContext.sections,
11236
11260
  totalContextTokens: filteredContext.totalTokens,
11237
11261
  maxContextTokens,
11262
+ contextUpdateSignal,
11238
11263
  enableContextDetailView,
11239
11264
  disabledSectionIds: currentDisabledSections,
11240
11265
  onToggleSection: handleContextSectionToggle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hef2024/llmasaservice-ui",
3
- "version": "0.26.0",
3
+ "version": "0.26.2",
4
4
  "description": "Prebuilt UI components for LLMAsAService.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -1407,64 +1407,3 @@
1407
1407
  .ai-agent-panel__chat-header-spacer {
1408
1408
  flex: 1;
1409
1409
  }
1410
-
1411
- /* --------------------------------------------------------
1412
- Context Change Notification
1413
- -------------------------------------------------------- */
1414
- .ai-agent-panel__context-notification {
1415
- position: absolute;
1416
- bottom: 70px;
1417
- left: 50%;
1418
- transform: translateX(-50%);
1419
- z-index: 50;
1420
- display: flex;
1421
- align-items: center;
1422
- gap: 8px;
1423
- padding: 8px 16px;
1424
- background-color: var(--ai-agent-badge-bg);
1425
- color: var(--ai-agent-badge-text);
1426
- border-radius: 20px;
1427
- font-size: 13px;
1428
- font-weight: 500;
1429
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
1430
- animation: ai-context-notification-in 0.3s ease-out, ai-context-notification-out 0.3s ease-in 2.7s forwards;
1431
- pointer-events: none;
1432
- }
1433
-
1434
- .ai-agent-panel__context-notification svg {
1435
- flex-shrink: 0;
1436
- animation: ai-sparkle-pulse 0.6s ease-in-out infinite alternate;
1437
- }
1438
-
1439
- @keyframes ai-context-notification-in {
1440
- 0% {
1441
- opacity: 0;
1442
- transform: translateX(-50%) translateY(10px) scale(0.9);
1443
- }
1444
- 100% {
1445
- opacity: 1;
1446
- transform: translateX(-50%) translateY(0) scale(1);
1447
- }
1448
- }
1449
-
1450
- @keyframes ai-context-notification-out {
1451
- 0% {
1452
- opacity: 1;
1453
- transform: translateX(-50%) translateY(0) scale(1);
1454
- }
1455
- 100% {
1456
- opacity: 0;
1457
- transform: translateX(-50%) translateY(10px) scale(0.9);
1458
- }
1459
- }
1460
-
1461
- @keyframes ai-sparkle-pulse {
1462
- 0% {
1463
- opacity: 0.7;
1464
- transform: scale(0.9);
1465
- }
1466
- 100% {
1467
- opacity: 1;
1468
- transform: scale(1.1);
1469
- }
1470
- }
@@ -269,13 +269,6 @@ const SidebarIcon = () => (
269
269
  </svg>
270
270
  );
271
271
 
272
- const SparkleIcon = () => (
273
- <svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
274
- <path d="M8 1v3M8 12v3M3 8H0M16 8h-3M12.95 3.05l-2.12 2.12M5.17 10.83l-2.12 2.12M12.95 12.95l-2.12-2.12M5.17 5.17L3.05 3.05" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
275
- <circle cx="8" cy="8" r="2" fill="currentColor"/>
276
- </svg>
277
- );
278
-
279
272
  /**
280
273
  * AIAgentPanel - Cursor-inspired multi-agent panel
281
274
  */
@@ -955,6 +948,7 @@ interface ChatPanelWrapperProps {
955
948
  contextSections: ContextSection[];
956
949
  totalContextTokens: number;
957
950
  maxContextTokens: number;
951
+ contextUpdateSignal: number;
958
952
  enableContextDetailView: boolean;
959
953
  disabledSectionIds: Set<string>;
960
954
  onToggleSection: (sectionId: string, enabled: boolean) => void;
@@ -1017,6 +1011,7 @@ const ChatPanelWrapper = (({
1017
1011
  contextSections,
1018
1012
  totalContextTokens,
1019
1013
  maxContextTokens,
1014
+ contextUpdateSignal,
1020
1015
  enableContextDetailView,
1021
1016
  disabledSectionIds,
1022
1017
  onToggleSection,
@@ -1165,6 +1160,7 @@ const ChatPanelWrapper = (({
1165
1160
  contextSections={contextSections}
1166
1161
  totalContextTokens={totalContextTokens}
1167
1162
  maxContextTokens={maxContextTokens}
1163
+ contextUpdateSignal={contextUpdateSignal}
1168
1164
  enableContextDetailView={enableContextDetailView}
1169
1165
  disabledSectionIds={disabledSectionIds}
1170
1166
  onToggleSection={onToggleSection}
@@ -1627,10 +1623,9 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
1627
1623
  }
1628
1624
  }), [commitConversationSelection, createDraftConversation, currentAgentId]);
1629
1625
 
1630
- // Context change notification state
1631
- const [showContextNotification, setShowContextNotification] = useState(false);
1626
+ // Bumps when context changes so child components can show a subtle cue.
1627
+ const [contextUpdateSignal, setContextUpdateSignal] = useState(0);
1632
1628
  const prevContextRef = useRef<string | null>(null);
1633
- const contextNotificationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
1634
1629
 
1635
1630
  // Track previous defaultAgent to avoid re-suggesting the same agent
1636
1631
  const prevDefaultAgentRef = useRef<string | null>(null);
@@ -2373,7 +2368,7 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
2373
2368
  }, [context, sharedContextSections, pageContextSections, contextDataSources]);
2374
2369
 
2375
2370
 
2376
- // Detect context changes and show notification
2371
+ // Detect context changes and notify the context icon.
2377
2372
  useEffect(() => {
2378
2373
  // Create a stable string representation of the context for comparison
2379
2374
  const contextString = JSON.stringify(
@@ -2397,33 +2392,10 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
2397
2392
 
2398
2393
  if (changed) {
2399
2394
  prevContextRef.current = contextString;
2400
-
2401
- // Clear any existing timeout before setting a new one
2402
- if (contextNotificationTimeoutRef.current) {
2403
- clearTimeout(contextNotificationTimeoutRef.current);
2404
- contextNotificationTimeoutRef.current = null;
2405
- }
2406
-
2407
- // Show the notification
2408
- setShowContextNotification(true);
2409
-
2410
- // Auto-hide after 3 seconds
2411
- contextNotificationTimeoutRef.current = setTimeout(() => {
2412
- setShowContextNotification(false);
2413
- contextNotificationTimeoutRef.current = null;
2414
- }, 3000);
2395
+ setContextUpdateSignal((prev) => prev + 1);
2415
2396
  }
2416
2397
  }, [mergedContext.sections]);
2417
2398
 
2418
- // Separate cleanup effect for unmount only
2419
- useEffect(() => {
2420
- return () => {
2421
- if (contextNotificationTimeoutRef.current) {
2422
- clearTimeout(contextNotificationTimeoutRef.current);
2423
- }
2424
- };
2425
- }, []);
2426
-
2427
2399
  // Auto-suggest agent switch when page context includes a defaultAgent
2428
2400
  // Injects a synthetic message with [SUGGEST_AGENT:...] into the chat history
2429
2401
  useEffect(() => {
@@ -3215,14 +3187,6 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
3215
3187
  </div>
3216
3188
  )}
3217
3189
 
3218
- {/* Context change notification */}
3219
- {showContextNotification && (
3220
- <div className="ai-agent-panel__context-notification">
3221
- <SparkleIcon />
3222
- <span>Agent now has new context</span>
3223
- </div>
3224
- )}
3225
-
3226
3190
  {/* Chat panels - one per active conversation, shown/hidden via CSS */}
3227
3191
  {activeConversationsList.map((activeConv) => (
3228
3192
  <ChatPanelWrapper
@@ -3253,6 +3217,7 @@ const AIAgentPanel = React.forwardRef<AIAgentPanelHandle, AIAgentPanelProps>(({
3253
3217
  contextSections={mergedContext.sections}
3254
3218
  totalContextTokens={filteredContext.totalTokens}
3255
3219
  maxContextTokens={maxContextTokens}
3220
+ contextUpdateSignal={contextUpdateSignal}
3256
3221
  enableContextDetailView={enableContextDetailView}
3257
3222
  disabledSectionIds={currentDisabledSections}
3258
3223
  onToggleSection={handleContextSectionToggle}
@@ -1781,7 +1781,7 @@
1781
1781
  align-items: center;
1782
1782
  gap: 4px;
1783
1783
  flex-shrink: 0;
1784
- margin-left: auto;
1784
+ margin-left: 0;
1785
1785
  }
1786
1786
 
1787
1787
  .ai-chat-context-pill-anchor {
@@ -1872,8 +1872,10 @@
1872
1872
  .ai-chat-context-pill {
1873
1873
  display: flex;
1874
1874
  align-items: center;
1875
- gap: 6px;
1876
- padding: 4px 10px;
1875
+ justify-content: center;
1876
+ gap: 0;
1877
+ min-width: 30px;
1878
+ padding: 4px 8px;
1877
1879
  background: var(--ai-chat-suggestion-bg, #f3f4f6);
1878
1880
  border: none;
1879
1881
  border-radius: 999px;
@@ -1898,6 +1900,10 @@
1898
1900
  color: #f59e0b;
1899
1901
  }
1900
1902
 
1903
+ .ai-chat-context-pill--updated {
1904
+ box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.24), 0 0 10px rgba(245, 158, 11, 0.18);
1905
+ }
1906
+
1901
1907
  .ai-chat-context-pill__count {
1902
1908
  display: none;
1903
1909
  border-radius: 999px;
@@ -1909,6 +1915,47 @@
1909
1915
  font-size: 12px;
1910
1916
  }
1911
1917
 
1918
+ .ai-chat-context-pill__icon {
1919
+ position: relative;
1920
+ display: inline-flex;
1921
+ align-items: center;
1922
+ justify-content: center;
1923
+ width: 14px;
1924
+ height: 14px;
1925
+ }
1926
+
1927
+ .ai-chat-context-pill__icon .ai-chat-icon-sm {
1928
+ width: 14px;
1929
+ height: 14px;
1930
+ }
1931
+
1932
+ .ai-chat-context-pill__spark {
1933
+ position: absolute;
1934
+ top: -7px;
1935
+ right: -7px;
1936
+ font-size: 10px;
1937
+ line-height: 1;
1938
+ color: #f59e0b;
1939
+ text-shadow: 0 0 6px rgba(245, 158, 11, 0.55);
1940
+ pointer-events: none;
1941
+ animation: contextPillSpark 1.6s ease-out forwards;
1942
+ }
1943
+
1944
+ @keyframes contextPillSpark {
1945
+ 0% {
1946
+ opacity: 0;
1947
+ transform: scale(0.7) translateY(2px);
1948
+ }
1949
+ 24% {
1950
+ opacity: 0.95;
1951
+ transform: scale(1) translateY(0);
1952
+ }
1953
+ 100% {
1954
+ opacity: 0;
1955
+ transform: scale(1.08) translateY(-2px);
1956
+ }
1957
+ }
1958
+
1912
1959
  .ai-chat-context-pill__tokens {
1913
1960
  display: none;
1914
1961
  }
@@ -2352,6 +2399,10 @@
2352
2399
  background: var(--ai-chat-suggestion-hover-bg, #4b5563);
2353
2400
  }
2354
2401
 
2402
+ .dark-theme .ai-chat-context-pill--updated {
2403
+ box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.35), 0 0 12px rgba(245, 158, 11, 0.22);
2404
+ }
2405
+
2355
2406
  .dark-theme .ai-chat-context-popover {
2356
2407
  background: var(--ai-chat-input-bg, #1f2937);
2357
2408
  border-color: var(--ai-chat-input-border, #374151);
@@ -2400,6 +2451,17 @@
2400
2451
  }
2401
2452
  }
2402
2453
 
2454
+ @media (prefers-reduced-motion: reduce) {
2455
+ .ai-chat-context-pill {
2456
+ transition: none;
2457
+ }
2458
+
2459
+ .ai-chat-context-pill__spark {
2460
+ animation: none;
2461
+ opacity: 0.9;
2462
+ }
2463
+ }
2464
+
2403
2465
  /* ============================================================================
2404
2466
  Error Banner
2405
2467
  ============================================================================ */
@@ -195,6 +195,7 @@ export interface AIChatPanelProps {
195
195
  contextSections?: ContextSection[];
196
196
  totalContextTokens?: number;
197
197
  maxContextTokens?: number;
198
+ contextUpdateSignal?: number;
198
199
  enableContextDetailView?: boolean;
199
200
  disabledSectionIds?: Set<string>;
200
201
  onToggleSection?: (sectionId: string, enabled: boolean) => void;
@@ -1726,6 +1727,7 @@ interface ChatInputProps {
1726
1727
  contextSections?: ContextSection[];
1727
1728
  totalContextTokens?: number;
1728
1729
  maxContextTokens?: number;
1730
+ contextUpdateSignal?: number;
1729
1731
  enableContextDetailView?: boolean;
1730
1732
  disabledSectionIds?: Set<string>;
1731
1733
  onToggleSection?: (sectionId: string, enabled: boolean) => void;
@@ -1752,6 +1754,7 @@ const ChatInput = React.memo<ChatInputProps>(({
1752
1754
  contextSections = [],
1753
1755
  totalContextTokens = 0,
1754
1756
  maxContextTokens = 8000,
1757
+ contextUpdateSignal = 0,
1755
1758
  enableContextDetailView = false,
1756
1759
  disabledSectionIds = new Set(),
1757
1760
  onToggleSection,
@@ -1762,9 +1765,12 @@ const ChatInput = React.memo<ChatInputProps>(({
1762
1765
  const [contextViewerOpen, setContextViewerOpen] = useState(false);
1763
1766
  const [contextViewMode, setContextViewMode] = useState<'summary' | 'detail'>('summary');
1764
1767
  const [expandedSectionId, setExpandedSectionId] = useState<string | null>(null);
1768
+ const [showContextUpdateCue, setShowContextUpdateCue] = useState(false);
1769
+ const [contextUpdateCueKey, setContextUpdateCueKey] = useState(0);
1765
1770
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);
1766
1771
  const containerRef = useRef<HTMLDivElement | null>(null);
1767
1772
  const contextPopoverRef = useRef<HTMLDivElement | null>(null);
1773
+ const contextUpdateCueTimeoutRef = useRef<number | null>(null);
1768
1774
  const [userInputSelections, setUserInputSelections] = useState<Record<string, string>>({});
1769
1775
  const [userInputWriteIns, setUserInputWriteIns] = useState<Record<string, string>>({});
1770
1776
  const [userInputValidationError, setUserInputValidationError] = useState('');
@@ -1892,6 +1898,33 @@ const ChatInput = React.memo<ChatInputProps>(({
1892
1898
  }
1893
1899
  }, [contextViewerOpen]);
1894
1900
 
1901
+ useEffect(() => {
1902
+ if (contextUpdateSignal <= 0) {
1903
+ return;
1904
+ }
1905
+
1906
+ setShowContextUpdateCue(true);
1907
+ setContextUpdateCueKey((prev) => prev + 1);
1908
+
1909
+ if (contextUpdateCueTimeoutRef.current) {
1910
+ window.clearTimeout(contextUpdateCueTimeoutRef.current);
1911
+ contextUpdateCueTimeoutRef.current = null;
1912
+ }
1913
+
1914
+ contextUpdateCueTimeoutRef.current = window.setTimeout(() => {
1915
+ setShowContextUpdateCue(false);
1916
+ contextUpdateCueTimeoutRef.current = null;
1917
+ }, 1800);
1918
+ }, [contextUpdateSignal]);
1919
+
1920
+ useEffect(() => {
1921
+ return () => {
1922
+ if (contextUpdateCueTimeoutRef.current) {
1923
+ window.clearTimeout(contextUpdateCueTimeoutRef.current);
1924
+ }
1925
+ };
1926
+ }, []);
1927
+
1895
1928
  // Format tokens for display
1896
1929
  const formatTokens = (tokens: number): string => {
1897
1930
  if (tokens >= 1000) {
@@ -2251,11 +2284,12 @@ const ChatInput = React.memo<ChatInputProps>(({
2251
2284
  <div className="ai-chat-context-pill-wrapper">
2252
2285
  <div className="ai-chat-context-pill-anchor">
2253
2286
  <button
2254
- className={`ai-chat-context-pill ${contextViewerOpen ? 'ai-chat-context-pill--active' : ''} ${isOverLimit ? 'ai-chat-context-pill--warning' : ''}`}
2287
+ className={`ai-chat-context-pill ${contextViewerOpen ? 'ai-chat-context-pill--active' : ''} ${isOverLimit ? 'ai-chat-context-pill--warning' : ''} ${showContextUpdateCue && !contextViewerOpen ? 'ai-chat-context-pill--updated' : ''}`}
2255
2288
  onClick={(e) => {
2256
2289
  e.preventDefault();
2257
2290
  e.stopPropagation();
2258
2291
  console.log('[ContextViewer] Button clicked, current state:', contextViewerOpen);
2292
+ setShowContextUpdateCue(false);
2259
2293
  setContextViewerOpen(!contextViewerOpen);
2260
2294
  if (!contextViewerOpen) {
2261
2295
  setContextViewMode('summary');
@@ -2266,8 +2300,20 @@ const ChatInput = React.memo<ChatInputProps>(({
2266
2300
  }}
2267
2301
  type="button"
2268
2302
  title="View context"
2303
+ aria-label="View context"
2269
2304
  >
2270
- <span className="ai-chat-context-pill__label">context: {contextSections.length} {contextSections.length === 1 ? 'section' : 'sections'}</span>
2305
+ <span className="ai-chat-context-pill__icon" aria-hidden="true">
2306
+ <ContextViewerIcon />
2307
+ {showContextUpdateCue && !contextViewerOpen && (
2308
+ <span
2309
+ key={contextUpdateCueKey}
2310
+ className="ai-chat-context-pill__spark"
2311
+ aria-hidden="true"
2312
+ >
2313
+ *
2314
+ </span>
2315
+ )}
2316
+ </span>
2271
2317
  </button>
2272
2318
 
2273
2319
  {/* Context popover - positioned relative to context pill */}
@@ -2552,6 +2598,7 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
2552
2598
  contextSections = [],
2553
2599
  totalContextTokens = 0,
2554
2600
  maxContextTokens = 8000,
2601
+ contextUpdateSignal = 0,
2555
2602
  enableContextDetailView = false,
2556
2603
  disabledSectionIds: propDisabledSectionIds,
2557
2604
  onToggleSection: propOnToggleSection,
@@ -6969,6 +7016,7 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
6969
7016
  contextSections={contextSections}
6970
7017
  totalContextTokens={totalContextTokens}
6971
7018
  maxContextTokens={maxContextTokens}
7019
+ contextUpdateSignal={contextUpdateSignal}
6972
7020
  enableContextDetailView={enableContextDetailView}
6973
7021
  disabledSectionIds={disabledSectionIds}
6974
7022
  onToggleSection={handleToggleSection}
Binary file