@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 +41 -53
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +45 -22
- package/dist/index.mjs +45 -22
- package/package.json +1 -1
- package/src/AIAgentPanel.css +0 -61
- package/src/AIAgentPanel.tsx +8 -43
- package/src/AIChatPanel.css +47 -0
- package/src/AIChatPanel.tsx +46 -1
- package/hef2024-llmasaservice-ui-0.25.3.tgz +0 -0
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 [
|
|
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
|
-
|
|
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)))),
|
|
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 [
|
|
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
|
-
|
|
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)))),
|
|
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
package/src/AIAgentPanel.css
CHANGED
|
@@ -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
|
-
}
|
package/src/AIAgentPanel.tsx
CHANGED
|
@@ -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
|
-
//
|
|
1631
|
-
const [
|
|
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
|
|
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}
|
package/src/AIChatPanel.css
CHANGED
|
@@ -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
|
============================================================================ */
|
package/src/AIChatPanel.tsx
CHANGED
|
@@ -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
|