@hef2024/llmasaservice-ui 0.26.1 → 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 {
@@ -4165,6 +4112,9 @@ button[data-pending=true]::after {
4165
4112
  .ai-chat-context-pill--warning {
4166
4113
  color: #f59e0b;
4167
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
+ }
4168
4118
  .ai-chat-context-pill__count {
4169
4119
  display: none;
4170
4120
  border-radius: 999px;
@@ -4175,6 +4125,7 @@ button[data-pending=true]::after {
4175
4125
  font-size: 12px;
4176
4126
  }
4177
4127
  .ai-chat-context-pill__icon {
4128
+ position: relative;
4178
4129
  display: inline-flex;
4179
4130
  align-items: center;
4180
4131
  justify-content: center;
@@ -4185,6 +4136,31 @@ button[data-pending=true]::after {
4185
4136
  width: 14px;
4186
4137
  height: 14px;
4187
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
+ }
4188
4164
  .ai-chat-context-pill__tokens {
4189
4165
  display: none;
4190
4166
  }
@@ -4554,6 +4530,9 @@ button[data-pending=true]::after {
4554
4530
  .dark-theme .ai-chat-context-pill--active {
4555
4531
  background: var(--ai-chat-suggestion-hover-bg, #4b5563);
4556
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
+ }
4557
4536
  .dark-theme .ai-chat-context-popover {
4558
4537
  background: var(--ai-chat-input-bg, #1f2937);
4559
4538
  border-color: var(--ai-chat-input-border, #374151);
@@ -4593,6 +4572,15 @@ button[data-pending=true]::after {
4593
4572
  }
4594
4573
  }
4595
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
+ }
4596
4584
  .ai-chat-error-banner {
4597
4585
  display: flex;
4598
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
@@ -5163,6 +5163,7 @@ var ChatInput = import_react14.default.memo(({
5163
5163
  contextSections = [],
5164
5164
  totalContextTokens = 0,
5165
5165
  maxContextTokens = 8e3,
5166
+ contextUpdateSignal = 0,
5166
5167
  enableContextDetailView = false,
5167
5168
  disabledSectionIds = /* @__PURE__ */ new Set(),
5168
5169
  onToggleSection,
@@ -5173,9 +5174,12 @@ var ChatInput = import_react14.default.memo(({
5173
5174
  const [contextViewerOpen, setContextViewerOpen] = (0, import_react14.useState)(false);
5174
5175
  const [contextViewMode, setContextViewMode] = (0, import_react14.useState)("summary");
5175
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);
5176
5179
  const textareaRef = (0, import_react14.useRef)(null);
5177
5180
  const containerRef = (0, import_react14.useRef)(null);
5178
5181
  const contextPopoverRef = (0, import_react14.useRef)(null);
5182
+ const contextUpdateCueTimeoutRef = (0, import_react14.useRef)(null);
5179
5183
  const [userInputSelections, setUserInputSelections] = (0, import_react14.useState)({});
5180
5184
  const [userInputWriteIns, setUserInputWriteIns] = (0, import_react14.useState)({});
5181
5185
  const [userInputValidationError, setUserInputValidationError] = (0, import_react14.useState)("");
@@ -5285,6 +5289,28 @@ var ChatInput = import_react14.default.memo(({
5285
5289
  return () => document.removeEventListener("mousedown", handleClickOutside);
5286
5290
  }
5287
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
+ }, []);
5288
5314
  const formatTokens = (tokens) => {
5289
5315
  if (tokens >= 1e3) {
5290
5316
  return `${(tokens / 1e3).toFixed(1)}K`;
@@ -5540,11 +5566,12 @@ var ChatInput = import_react14.default.memo(({
5540
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(
5541
5567
  "button",
5542
5568
  {
5543
- 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" : ""}`,
5544
5570
  onClick: (e) => {
5545
5571
  e.preventDefault();
5546
5572
  e.stopPropagation();
5547
5573
  console.log("[ContextViewer] Button clicked, current state:", contextViewerOpen);
5574
+ setShowContextUpdateCue(false);
5548
5575
  setContextViewerOpen(!contextViewerOpen);
5549
5576
  if (!contextViewerOpen) {
5550
5577
  setContextViewMode("summary");
@@ -5557,7 +5584,15 @@ var ChatInput = import_react14.default.memo(({
5557
5584
  title: "View context",
5558
5585
  "aria-label": "View context"
5559
5586
  },
5560
- /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-pill__icon", "aria-hidden": "true" }, /* @__PURE__ */ import_react14.default.createElement(ContextViewerIcon, null))
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
+ ))
5561
5596
  ), contextViewerOpen && /* @__PURE__ */ import_react14.default.createElement(
5562
5597
  "div",
5563
5598
  {
@@ -5751,6 +5786,7 @@ var AIChatPanel = ({
5751
5786
  contextSections = [],
5752
5787
  totalContextTokens = 0,
5753
5788
  maxContextTokens = 8e3,
5789
+ contextUpdateSignal = 0,
5754
5790
  enableContextDetailView = false,
5755
5791
  disabledSectionIds: propDisabledSectionIds,
5756
5792
  onToggleSection: propOnToggleSection,
@@ -8933,6 +8969,7 @@ ${traceSummary}` : traceSummary;
8933
8969
  contextSections,
8934
8970
  totalContextTokens,
8935
8971
  maxContextTokens,
8972
+ contextUpdateSignal,
8936
8973
  enableContextDetailView,
8937
8974
  disabledSectionIds,
8938
8975
  onToggleSection: handleToggleSection,
@@ -9165,7 +9202,6 @@ var MessageIcon = () => /* @__PURE__ */ import_react16.default.createElement("sv
9165
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" }));
9166
9203
  var LoadingDotIcon = () => /* @__PURE__ */ import_react16.default.createElement("span", { className: "ai-agent-panel__loading-dot" });
9167
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" }));
9168
- 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" }));
9169
9205
  var normalizeConversationListPayload = (payload) => {
9170
9206
  if (!payload) return [];
9171
9207
  const conversations = Array.isArray(payload) ? payload : payload.conversations || [];
@@ -9658,6 +9694,7 @@ var ChatPanelWrapper = ({
9658
9694
  contextSections,
9659
9695
  totalContextTokens,
9660
9696
  maxContextTokens,
9697
+ contextUpdateSignal,
9661
9698
  enableContextDetailView,
9662
9699
  disabledSectionIds,
9663
9700
  onToggleSection,
@@ -9780,6 +9817,7 @@ var ChatPanelWrapper = ({
9780
9817
  contextSections,
9781
9818
  totalContextTokens,
9782
9819
  maxContextTokens,
9820
+ contextUpdateSignal,
9783
9821
  enableContextDetailView,
9784
9822
  disabledSectionIds,
9785
9823
  onToggleSection,
@@ -10131,9 +10169,8 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10131
10169
  commitConversationSelection(tempId, true);
10132
10170
  }
10133
10171
  }), [commitConversationSelection, createDraftConversation, currentAgentId]);
10134
- const [showContextNotification, setShowContextNotification] = (0, import_react16.useState)(false);
10172
+ const [contextUpdateSignal, setContextUpdateSignal] = (0, import_react16.useState)(0);
10135
10173
  const prevContextRef = (0, import_react16.useRef)(null);
10136
- const contextNotificationTimeoutRef = (0, import_react16.useRef)(null);
10137
10174
  const prevDefaultAgentRef = (0, import_react16.useRef)(null);
10138
10175
  const fetchFirstPrompt = (0, import_react16.useCallback)((conversationId, agentIdForConversation) => __async(void 0, null, function* () {
10139
10176
  var _a2, _b2;
@@ -10694,24 +10731,9 @@ var AIAgentPanel = import_react16.default.forwardRef(({
10694
10731
  const changed = prevContextRef.current !== contextString;
10695
10732
  if (changed) {
10696
10733
  prevContextRef.current = contextString;
10697
- if (contextNotificationTimeoutRef.current) {
10698
- clearTimeout(contextNotificationTimeoutRef.current);
10699
- contextNotificationTimeoutRef.current = null;
10700
- }
10701
- setShowContextNotification(true);
10702
- contextNotificationTimeoutRef.current = setTimeout(() => {
10703
- setShowContextNotification(false);
10704
- contextNotificationTimeoutRef.current = null;
10705
- }, 3e3);
10734
+ setContextUpdateSignal((prev) => prev + 1);
10706
10735
  }
10707
10736
  }, [mergedContext.sections]);
10708
- (0, import_react16.useEffect)(() => {
10709
- return () => {
10710
- if (contextNotificationTimeoutRef.current) {
10711
- clearTimeout(contextNotificationTimeoutRef.current);
10712
- }
10713
- };
10714
- }, []);
10715
10737
  (0, import_react16.useEffect)(() => {
10716
10738
  var _a2, _b2;
10717
10739
  let foundDefaultAgent = null;
@@ -11242,7 +11264,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
11242
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)
11243
11265
  );
11244
11266
  }))))))),
11245
- /* @__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(
11246
11268
  ChatPanelWrapper,
11247
11269
  {
11248
11270
  key: activeConv.stableKey,
@@ -11272,6 +11294,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
11272
11294
  contextSections: mergedContext.sections,
11273
11295
  totalContextTokens: filteredContext.totalTokens,
11274
11296
  maxContextTokens,
11297
+ contextUpdateSignal,
11275
11298
  enableContextDetailView,
11276
11299
  disabledSectionIds: currentDisabledSections,
11277
11300
  onToggleSection: handleContextSectionToggle,
package/dist/index.mjs CHANGED
@@ -5128,6 +5128,7 @@ var ChatInput = React14.memo(({
5128
5128
  contextSections = [],
5129
5129
  totalContextTokens = 0,
5130
5130
  maxContextTokens = 8e3,
5131
+ contextUpdateSignal = 0,
5131
5132
  enableContextDetailView = false,
5132
5133
  disabledSectionIds = /* @__PURE__ */ new Set(),
5133
5134
  onToggleSection,
@@ -5138,9 +5139,12 @@ var ChatInput = React14.memo(({
5138
5139
  const [contextViewerOpen, setContextViewerOpen] = useState7(false);
5139
5140
  const [contextViewMode, setContextViewMode] = useState7("summary");
5140
5141
  const [expandedSectionId, setExpandedSectionId] = useState7(null);
5142
+ const [showContextUpdateCue, setShowContextUpdateCue] = useState7(false);
5143
+ const [contextUpdateCueKey, setContextUpdateCueKey] = useState7(0);
5141
5144
  const textareaRef = useRef6(null);
5142
5145
  const containerRef = useRef6(null);
5143
5146
  const contextPopoverRef = useRef6(null);
5147
+ const contextUpdateCueTimeoutRef = useRef6(null);
5144
5148
  const [userInputSelections, setUserInputSelections] = useState7({});
5145
5149
  const [userInputWriteIns, setUserInputWriteIns] = useState7({});
5146
5150
  const [userInputValidationError, setUserInputValidationError] = useState7("");
@@ -5250,6 +5254,28 @@ var ChatInput = React14.memo(({
5250
5254
  return () => document.removeEventListener("mousedown", handleClickOutside);
5251
5255
  }
5252
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
+ }, []);
5253
5279
  const formatTokens = (tokens) => {
5254
5280
  if (tokens >= 1e3) {
5255
5281
  return `${(tokens / 1e3).toFixed(1)}K`;
@@ -5505,11 +5531,12 @@ var ChatInput = React14.memo(({
5505
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(
5506
5532
  "button",
5507
5533
  {
5508
- 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" : ""}`,
5509
5535
  onClick: (e) => {
5510
5536
  e.preventDefault();
5511
5537
  e.stopPropagation();
5512
5538
  console.log("[ContextViewer] Button clicked, current state:", contextViewerOpen);
5539
+ setShowContextUpdateCue(false);
5513
5540
  setContextViewerOpen(!contextViewerOpen);
5514
5541
  if (!contextViewerOpen) {
5515
5542
  setContextViewMode("summary");
@@ -5522,7 +5549,15 @@ var ChatInput = React14.memo(({
5522
5549
  title: "View context",
5523
5550
  "aria-label": "View context"
5524
5551
  },
5525
- /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-pill__icon", "aria-hidden": "true" }, /* @__PURE__ */ React14.createElement(ContextViewerIcon, null))
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
+ ))
5526
5561
  ), contextViewerOpen && /* @__PURE__ */ React14.createElement(
5527
5562
  "div",
5528
5563
  {
@@ -5716,6 +5751,7 @@ var AIChatPanel = ({
5716
5751
  contextSections = [],
5717
5752
  totalContextTokens = 0,
5718
5753
  maxContextTokens = 8e3,
5754
+ contextUpdateSignal = 0,
5719
5755
  enableContextDetailView = false,
5720
5756
  disabledSectionIds: propDisabledSectionIds,
5721
5757
  onToggleSection: propOnToggleSection,
@@ -8898,6 +8934,7 @@ ${traceSummary}` : traceSummary;
8898
8934
  contextSections,
8899
8935
  totalContextTokens,
8900
8936
  maxContextTokens,
8937
+ contextUpdateSignal,
8901
8938
  enableContextDetailView,
8902
8939
  disabledSectionIds,
8903
8940
  onToggleSection: handleToggleSection,
@@ -9130,7 +9167,6 @@ var MessageIcon = () => /* @__PURE__ */ React15.createElement("svg", { width: "1
9130
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" }));
9131
9168
  var LoadingDotIcon = () => /* @__PURE__ */ React15.createElement("span", { className: "ai-agent-panel__loading-dot" });
9132
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" }));
9133
- 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" }));
9134
9170
  var normalizeConversationListPayload = (payload) => {
9135
9171
  if (!payload) return [];
9136
9172
  const conversations = Array.isArray(payload) ? payload : payload.conversations || [];
@@ -9623,6 +9659,7 @@ var ChatPanelWrapper = ({
9623
9659
  contextSections,
9624
9660
  totalContextTokens,
9625
9661
  maxContextTokens,
9662
+ contextUpdateSignal,
9626
9663
  enableContextDetailView,
9627
9664
  disabledSectionIds,
9628
9665
  onToggleSection,
@@ -9745,6 +9782,7 @@ var ChatPanelWrapper = ({
9745
9782
  contextSections,
9746
9783
  totalContextTokens,
9747
9784
  maxContextTokens,
9785
+ contextUpdateSignal,
9748
9786
  enableContextDetailView,
9749
9787
  disabledSectionIds,
9750
9788
  onToggleSection,
@@ -10096,9 +10134,8 @@ var AIAgentPanel = React15.forwardRef(({
10096
10134
  commitConversationSelection(tempId, true);
10097
10135
  }
10098
10136
  }), [commitConversationSelection, createDraftConversation, currentAgentId]);
10099
- const [showContextNotification, setShowContextNotification] = useState9(false);
10137
+ const [contextUpdateSignal, setContextUpdateSignal] = useState9(0);
10100
10138
  const prevContextRef = useRef7(null);
10101
- const contextNotificationTimeoutRef = useRef7(null);
10102
10139
  const prevDefaultAgentRef = useRef7(null);
10103
10140
  const fetchFirstPrompt = useCallback4((conversationId, agentIdForConversation) => __async(void 0, null, function* () {
10104
10141
  var _a2, _b2;
@@ -10659,24 +10696,9 @@ var AIAgentPanel = React15.forwardRef(({
10659
10696
  const changed = prevContextRef.current !== contextString;
10660
10697
  if (changed) {
10661
10698
  prevContextRef.current = contextString;
10662
- if (contextNotificationTimeoutRef.current) {
10663
- clearTimeout(contextNotificationTimeoutRef.current);
10664
- contextNotificationTimeoutRef.current = null;
10665
- }
10666
- setShowContextNotification(true);
10667
- contextNotificationTimeoutRef.current = setTimeout(() => {
10668
- setShowContextNotification(false);
10669
- contextNotificationTimeoutRef.current = null;
10670
- }, 3e3);
10699
+ setContextUpdateSignal((prev) => prev + 1);
10671
10700
  }
10672
10701
  }, [mergedContext.sections]);
10673
- useEffect10(() => {
10674
- return () => {
10675
- if (contextNotificationTimeoutRef.current) {
10676
- clearTimeout(contextNotificationTimeoutRef.current);
10677
- }
10678
- };
10679
- }, []);
10680
10702
  useEffect10(() => {
10681
10703
  var _a2, _b2;
10682
10704
  let foundDefaultAgent = null;
@@ -11207,7 +11229,7 @@ var AIAgentPanel = React15.forwardRef(({
11207
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)
11208
11230
  );
11209
11231
  }))))))),
11210
- /* @__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(
11211
11233
  ChatPanelWrapper,
11212
11234
  {
11213
11235
  key: activeConv.stableKey,
@@ -11237,6 +11259,7 @@ var AIAgentPanel = React15.forwardRef(({
11237
11259
  contextSections: mergedContext.sections,
11238
11260
  totalContextTokens: filteredContext.totalTokens,
11239
11261
  maxContextTokens,
11262
+ contextUpdateSignal,
11240
11263
  enableContextDetailView,
11241
11264
  disabledSectionIds: currentDisabledSections,
11242
11265
  onToggleSection: handleContextSectionToggle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hef2024/llmasaservice-ui",
3
- "version": "0.26.1",
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}
@@ -1900,6 +1900,10 @@
1900
1900
  color: #f59e0b;
1901
1901
  }
1902
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
+
1903
1907
  .ai-chat-context-pill__count {
1904
1908
  display: none;
1905
1909
  border-radius: 999px;
@@ -1912,6 +1916,7 @@
1912
1916
  }
1913
1917
 
1914
1918
  .ai-chat-context-pill__icon {
1919
+ position: relative;
1915
1920
  display: inline-flex;
1916
1921
  align-items: center;
1917
1922
  justify-content: center;
@@ -1924,6 +1929,33 @@
1924
1929
  height: 14px;
1925
1930
  }
1926
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
+
1927
1959
  .ai-chat-context-pill__tokens {
1928
1960
  display: none;
1929
1961
  }
@@ -2367,6 +2399,10 @@
2367
2399
  background: var(--ai-chat-suggestion-hover-bg, #4b5563);
2368
2400
  }
2369
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
+
2370
2406
  .dark-theme .ai-chat-context-popover {
2371
2407
  background: var(--ai-chat-input-bg, #1f2937);
2372
2408
  border-color: var(--ai-chat-input-border, #374151);
@@ -2415,6 +2451,17 @@
2415
2451
  }
2416
2452
  }
2417
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
+
2418
2465
  /* ============================================================================
2419
2466
  Error Banner
2420
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');
@@ -2270,6 +2304,15 @@ const ChatInput = React.memo<ChatInputProps>(({
2270
2304
  >
2271
2305
  <span className="ai-chat-context-pill__icon" aria-hidden="true">
2272
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
+ )}
2273
2316
  </span>
2274
2317
  </button>
2275
2318
 
@@ -2555,6 +2598,7 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
2555
2598
  contextSections = [],
2556
2599
  totalContextTokens = 0,
2557
2600
  maxContextTokens = 8000,
2601
+ contextUpdateSignal = 0,
2558
2602
  enableContextDetailView = false,
2559
2603
  disabledSectionIds: propDisabledSectionIds,
2560
2604
  onToggleSection: propOnToggleSection,
@@ -6972,6 +7016,7 @@ const AIChatPanel: React.FC<AIChatPanelProps> = ({
6972
7016
  contextSections={contextSections}
6973
7017
  totalContextTokens={totalContextTokens}
6974
7018
  maxContextTokens={maxContextTokens}
7019
+ contextUpdateSignal={contextUpdateSignal}
6975
7020
  enableContextDetailView={enableContextDetailView}
6976
7021
  disabledSectionIds={disabledSectionIds}
6977
7022
  onToggleSection={handleToggleSection}
Binary file