@genesislcap/ai-assistant 14.458.3 → 14.459.0
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/ai-assistant.api.json +37 -10
- package/dist/ai-assistant.d.ts +46 -4
- package/dist/dts/components/flowing-waves-indicator.d.ts +32 -0
- package/dist/dts/components/flowing-waves-indicator.d.ts.map +1 -0
- package/dist/dts/components/plasma-orb-indicator.d.ts +22 -0
- package/dist/dts/components/plasma-orb-indicator.d.ts.map +1 -0
- package/dist/dts/components/waves-indicator.d.ts +30 -0
- package/dist/dts/components/waves-indicator.d.ts.map +1 -0
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/main/main.types.d.ts +44 -4
- package/dist/dts/main/main.types.d.ts.map +1 -1
- package/dist/dts/utils/animation-exclusivity.d.ts +23 -0
- package/dist/dts/utils/animation-exclusivity.d.ts.map +1 -0
- package/dist/dts/utils/animation-exclusivity.test.d.ts +2 -0
- package/dist/dts/utils/animation-exclusivity.test.d.ts.map +1 -0
- package/dist/esm/components/flowing-waves-indicator.js +222 -0
- package/dist/esm/components/plasma-orb-indicator.js +280 -0
- package/dist/esm/components/waves-indicator.js +189 -0
- package/dist/esm/main/main.js +20 -9
- package/dist/esm/main/main.styles.js +40 -6
- package/dist/esm/main/main.template.js +58 -17
- package/dist/esm/main/main.types.js +46 -3
- package/dist/esm/utils/animation-exclusivity.js +33 -0
- package/dist/esm/utils/animation-exclusivity.test.js +52 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/src/components/flowing-waves-indicator.ts +260 -0
- package/src/components/plasma-orb-indicator.ts +281 -0
- package/src/components/waves-indicator.ts +221 -0
- package/src/main/main.styles.ts +40 -6
- package/src/main/main.template.ts +60 -18
- package/src/main/main.ts +24 -8
- package/src/main/main.types.ts +56 -5
- package/src/utils/animation-exclusivity.test.ts +72 -0
- package/src/utils/animation-exclusivity.ts +40 -0
package/dist/esm/main/main.js
CHANGED
|
@@ -33,21 +33,25 @@ import { AiChatBubble } from '../components/chat-bubble/chat-bubble';
|
|
|
33
33
|
import { ChatDriver } from '../components/chat-driver/chat-driver';
|
|
34
34
|
import { AiChatInteractionWrapper } from '../components/chat-interaction-wrapper/chat-interaction-wrapper';
|
|
35
35
|
import { AiChatMarkdown } from '../components/chat-markdown/chat-markdown';
|
|
36
|
+
import { AiFlowingWavesIndicator } from '../components/flowing-waves-indicator';
|
|
36
37
|
import { AiHaloOverlay } from '../components/halo-overlay';
|
|
37
38
|
import { OrchestratingDriver } from '../components/orchestrating-driver/orchestrating-driver';
|
|
39
|
+
import { AiPlasmaOrbIndicator } from '../components/plasma-orb-indicator';
|
|
40
|
+
import { AiWavesIndicator } from '../components/waves-indicator';
|
|
38
41
|
import { recordMetaEvent, getMetaEvents, DEBUG_LOG_README, } from '../state/debug-event-log';
|
|
39
42
|
import { getOrCreateDriver, getDriver, deleteDriver } from '../state/driver-registry';
|
|
40
43
|
import { getSessionStore, hasSessionStore } from '../state/session-store';
|
|
41
44
|
import { AI_COLOUR_AMBER, AI_COLOUR_CYAN, AI_COLOUR_PINK, AI_COLOUR_VIOLET, } from '../styles/ai-colours';
|
|
42
45
|
import { ChatSuggestions } from '../suggestions/chat-suggestions';
|
|
43
46
|
import { AnimatedPanelToggle } from '../utils/animated-panel-toggle';
|
|
47
|
+
import { resolveExclusiveLoadingStyle } from '../utils/animation-exclusivity';
|
|
44
48
|
import { logger } from '../utils/logger';
|
|
45
49
|
import { filterVisibleMessages, trailingInteractionRow } from '../utils/message-partition';
|
|
46
50
|
import { sumCosts } from '../utils/sum-costs';
|
|
47
51
|
import { expandToolTree } from '../utils/tool-fold';
|
|
48
52
|
import { styles } from './main.styles';
|
|
49
53
|
import { FoundationAiAssistantTemplate } from './main.template';
|
|
50
|
-
import {
|
|
54
|
+
import { DEFAULT_ANIMATIONS } from './main.types';
|
|
51
55
|
/** Context window sizes (in tokens) for known models. */
|
|
52
56
|
/**
|
|
53
57
|
* Pin tint palette, cycled by agent position. Matches the four brand colours
|
|
@@ -90,7 +94,7 @@ const COMPOSER_MAX_HEIGHT_PX = 400;
|
|
|
90
94
|
/** Keep at least this much of the message list visible while growing the composer. */
|
|
91
95
|
const COMPOSER_MIN_MESSAGES_PX = 80;
|
|
92
96
|
// Register supporting components when the main component module is imported.
|
|
93
|
-
avoidTreeShaking(AiChatMarkdown, AiChatInteractionWrapper, AiHaloOverlay, AiChatBubble, AiActivityHalo, ChatSuggestions, AgentPicker);
|
|
97
|
+
avoidTreeShaking(AiChatMarkdown, AiChatInteractionWrapper, AiHaloOverlay, AiWavesIndicator, AiFlowingWavesIndicator, AiPlasmaOrbIndicator, AiChatBubble, AiActivityHalo, ChatSuggestions, AgentPicker);
|
|
94
98
|
/**
|
|
95
99
|
* Recursively strips non-serializable fields from an agent before storing in Redux:
|
|
96
100
|
* - `toolHandlers` (functions),
|
|
@@ -538,7 +542,9 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
538
542
|
return;
|
|
539
543
|
}
|
|
540
544
|
const last = this.messages[this.messages.length - 1];
|
|
541
|
-
|
|
545
|
+
// Hide only while a pending interaction blocks on the user; a resolved
|
|
546
|
+
// interaction means the assistant is computing again (keep the halo).
|
|
547
|
+
if ((last === null || last === void 0 ? void 0 : last.interaction) && !last.interaction.resolved) {
|
|
542
548
|
this.showHalo = 'no';
|
|
543
549
|
}
|
|
544
550
|
else if (this.showHalo !== 'orchestrating') {
|
|
@@ -911,8 +917,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
911
917
|
this.showToolCalls = ui.showToolCalls === true;
|
|
912
918
|
this.showThinkingSteps = ui.showThinkingSteps === true;
|
|
913
919
|
this.showAgentSwitchIndicator = ui.showAgentSwitchIndicator === true;
|
|
914
|
-
this.enabledAnimations =
|
|
915
|
-
(_c = (_b = ui.animations) === null || _b === void 0 ? void 0 : _b.enabled) !== null && _c !== void 0 ? _c : (ui.animations ? [...ALL_ANIMATIONS] : []);
|
|
920
|
+
this.enabledAnimations = resolveExclusiveLoadingStyle((_c = (_b = ui.animations) === null || _b === void 0 ? void 0 : _b.enabled) !== null && _c !== void 0 ? _c : (ui.animations ? [...DEFAULT_ANIMATIONS] : []));
|
|
916
921
|
const defaultAgent = (_d = this.chatConfig.picker) === null || _d === void 0 ? void 0 : _d.defaultAgent;
|
|
917
922
|
if (defaultAgent && ((_e = this.agents) !== null && _e !== void 0 ? _e : []).some((a) => a.name === defaultAgent)) {
|
|
918
923
|
this.pinnedAgentName = defaultAgent;
|
|
@@ -1078,10 +1083,14 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
1078
1083
|
// waiting for the user, not computing.
|
|
1079
1084
|
if (this.busy) {
|
|
1080
1085
|
const last = this.messages[this.messages.length - 1];
|
|
1081
|
-
|
|
1086
|
+
// Only a *pending* interaction means the assistant is blocked waiting on
|
|
1087
|
+
// the user. Once it's resolved — or for any normal step — the assistant is
|
|
1088
|
+
// computing again, so the indicator should resume (e.g. while it works out
|
|
1089
|
+
// the next planning question after the user answers a widget).
|
|
1090
|
+
if ((last === null || last === void 0 ? void 0 : last.interaction) && !last.interaction.resolved) {
|
|
1082
1091
|
this.stopLoadingTimer();
|
|
1083
1092
|
}
|
|
1084
|
-
else
|
|
1093
|
+
else {
|
|
1085
1094
|
this.startLoadingTimer();
|
|
1086
1095
|
}
|
|
1087
1096
|
}
|
|
@@ -1334,7 +1343,9 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
1334
1343
|
this.showAgentSwitchIndicator = !this.showAgentSwitchIndicator;
|
|
1335
1344
|
}
|
|
1336
1345
|
setEnabledAnimations(animations) {
|
|
1337
|
-
|
|
1346
|
+
// The dots and waves loading styles are mutually exclusive — enabling one
|
|
1347
|
+
// disables the other (see resolveExclusiveLoadingStyle).
|
|
1348
|
+
this.enabledAnimations = resolveExclusiveLoadingStyle(animations, this.enabledAnimations);
|
|
1338
1349
|
}
|
|
1339
1350
|
getDebugLog() {
|
|
1340
1351
|
var _a, _b, _c, _d, _e, _f, _j, _k, _l, _m, _o, _p, _q;
|
|
@@ -1822,7 +1833,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
1822
1833
|
FoundationAiAssistant.SCROLL_BOTTOM_THRESHOLD_PX = 50;
|
|
1823
1834
|
/** Context-usage percentage that fires the one-shot `context.threshold-crossed` event. */
|
|
1824
1835
|
FoundationAiAssistant.CONTEXT_USAGE_WARN_PERCENT = 80;
|
|
1825
|
-
FoundationAiAssistant.DEFAULT_LOADING_DELAY_S =
|
|
1836
|
+
FoundationAiAssistant.DEFAULT_LOADING_DELAY_S = 0;
|
|
1826
1837
|
FoundationAiAssistant.DEFAULT_SUGGESTION_COUNT = 3;
|
|
1827
1838
|
FoundationAiAssistant.MS_PER_SECOND = 1000;
|
|
1828
1839
|
__decorate([
|
|
@@ -95,14 +95,22 @@ export const styles = css `
|
|
|
95
95
|
animation: settings-slide-out 0.2s ease-in forwards;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
width:
|
|
98
|
+
/* Collapsed control footprint stays compact (100px)... */
|
|
99
|
+
rapid-categorized-multiselect::part(root) {
|
|
100
|
+
min-width: 0;
|
|
101
|
+
width: 100px;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
/* ...while the dropdown panel, being absolutely positioned, can be wider
|
|
105
|
+
(200px) without affecting the root's layout footprint. The control sits on
|
|
106
|
+
a different side of the settings panel depending on the container width
|
|
107
|
+
(see the layout rules below), so the dropdown must anchor to whichever side
|
|
108
|
+
keeps it inside the panel. Default (narrow) layout puts the control on the
|
|
109
|
+
LEFT, so the dropdown grows rightward. */
|
|
110
|
+
rapid-categorized-multiselect::part(options) {
|
|
111
|
+
width: 200px;
|
|
112
|
+
left: 0;
|
|
113
|
+
right: auto;
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
.settings-panel > [part='toggle-tool-calls'] {
|
|
@@ -121,6 +129,7 @@ export const styles = css `
|
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
.settings-panel > [part='download-button'] {
|
|
132
|
+
width: fit-content;
|
|
124
133
|
grid-column: 2;
|
|
125
134
|
grid-row: 2;
|
|
126
135
|
}
|
|
@@ -170,6 +179,13 @@ export const styles = css `
|
|
|
170
179
|
.settings-panel > .settings-animations {
|
|
171
180
|
grid-row: 2;
|
|
172
181
|
}
|
|
182
|
+
|
|
183
|
+
/* Control now sits on the RIGHT (justify-self: end / margin-left: auto in
|
|
184
|
+
the wider layouts), so the dropdown grows leftward to stay in the panel. */
|
|
185
|
+
rapid-categorized-multiselect::part(options) {
|
|
186
|
+
left: auto;
|
|
187
|
+
right: 0;
|
|
188
|
+
}
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
@container (min-width: 750px) {
|
|
@@ -803,6 +819,24 @@ export const styles = css `
|
|
|
803
819
|
}
|
|
804
820
|
}
|
|
805
821
|
|
|
822
|
+
.thinking-waves,
|
|
823
|
+
.thinking-flowing-waves,
|
|
824
|
+
.thinking-plasma {
|
|
825
|
+
display: flex;
|
|
826
|
+
flex-direction: column;
|
|
827
|
+
align-items: center;
|
|
828
|
+
align-self: center;
|
|
829
|
+
gap: 6px;
|
|
830
|
+
padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.thinking-caption {
|
|
834
|
+
font-size: var(--type-ramp-minus-1-font-size, 12px);
|
|
835
|
+
line-height: var(--type-ramp-minus-1-line-height, 16px);
|
|
836
|
+
color: var(--neutral-foreground-rest);
|
|
837
|
+
opacity: 70%;
|
|
838
|
+
}
|
|
839
|
+
|
|
806
840
|
.attachment-chips {
|
|
807
841
|
display: flex;
|
|
808
842
|
flex-wrap: wrap;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { isChatToolCallUnknown } from '@genesislcap/foundation-ai';
|
|
18
18
|
import { classNames, html, ref, repeat, when } from '@genesislcap/web-core';
|
|
19
|
-
import { ANIMATION_DEFS } from './main.types';
|
|
19
|
+
import { ANIMATION_DEFS, LOADING_STYLE_ANIMATIONS } from './main.types';
|
|
20
20
|
function unknownToolPayload(tc) {
|
|
21
21
|
if (!isChatToolCallUnknown(tc))
|
|
22
22
|
return '';
|
|
@@ -31,18 +31,16 @@ function unknownToolPayload(tc) {
|
|
|
31
31
|
}
|
|
32
32
|
return lines.join('\n');
|
|
33
33
|
}
|
|
34
|
-
const animationItemRenderer = (option) => html `
|
|
35
|
-
<span part="option-label" title="${() => option.tooltip}">${() => option.label}</span>
|
|
36
|
-
`;
|
|
37
34
|
const HALO_SPEED_DEFAULT = 1.5;
|
|
38
35
|
const HALO_SPEED_ORCHESTRATING = 0.4;
|
|
39
36
|
const HALO_BORDER_SIZE_DEFAULT = 3;
|
|
40
37
|
/** Decimal places shown for the running session cost (USD). 4 ≈ $0.0001 resolution. */
|
|
41
38
|
const SESSION_COST_DECIMALS = 4;
|
|
42
39
|
const animationOptions = Object.entries(ANIMATION_DEFS).map(([value, def]) => ({
|
|
40
|
+
type: def.category,
|
|
43
41
|
value,
|
|
44
42
|
label: def.label,
|
|
45
|
-
|
|
43
|
+
description: def.description,
|
|
46
44
|
}));
|
|
47
45
|
// Avatar markup is owned by the component (`assistantIconSafe` / `userIconSafe`),
|
|
48
46
|
// which holds the sanitized SVG string for the default or any consumer override
|
|
@@ -136,12 +134,61 @@ const liveSubAgentTraceTemplate = html `
|
|
|
136
134
|
${repeat((x) => x.liveSubAgentTrace.filter((m) => m.role !== 'user'), subAgentMessageRowTemplate)}
|
|
137
135
|
</div>
|
|
138
136
|
`;
|
|
137
|
+
// The interchangeable loading indicators. These MUST be stable module-level
|
|
138
|
+
// instances: the binding that selects between them reads `enabledAnimations`,
|
|
139
|
+
// which is backed by the redux store proxy and re-evaluates on every change to
|
|
140
|
+
// the aiAssistant slice (i.e. every new message, including hidden tool-call and
|
|
141
|
+
// thinking-step messages). Returning a fresh `html` instance from that binding
|
|
142
|
+
// would make FAST tear down and rebuild the indicator DOM each time, restarting
|
|
143
|
+
// the CSS animations and rAF loops. Stable references let FAST reuse the
|
|
144
|
+
// existing view so the animation runs uninterrupted.
|
|
145
|
+
const thinkingDotsTemplate = html `
|
|
146
|
+
<div class="thinking-dots">
|
|
147
|
+
<div class="dot dot-1"></div>
|
|
148
|
+
<div class="dot dot-2"></div>
|
|
149
|
+
<div class="dot dot-3"></div>
|
|
150
|
+
<div class="dot dot-4"></div>
|
|
151
|
+
</div>
|
|
152
|
+
`;
|
|
153
|
+
const thinkingWavesTemplate = html `
|
|
154
|
+
<div class="thinking-waves" part="thinking-waves">
|
|
155
|
+
<ai-waves-indicator></ai-waves-indicator>
|
|
156
|
+
<span class="thinking-caption">Thinking...</span>
|
|
157
|
+
</div>
|
|
158
|
+
`;
|
|
159
|
+
const thinkingFlowingWavesTemplate = html `
|
|
160
|
+
<div class="thinking-flowing-waves" part="thinking-flowing-waves">
|
|
161
|
+
<ai-flowing-waves-indicator></ai-flowing-waves-indicator>
|
|
162
|
+
<span class="thinking-caption">Thinking...</span>
|
|
163
|
+
</div>
|
|
164
|
+
`;
|
|
165
|
+
const thinkingPlasmaTemplate = html `
|
|
166
|
+
<div class="thinking-plasma" part="thinking-plasma">
|
|
167
|
+
<ai-plasma-orb-indicator></ai-plasma-orb-indicator>
|
|
168
|
+
<span class="thinking-caption">Thinking...</span>
|
|
169
|
+
</div>
|
|
170
|
+
`;
|
|
171
|
+
/**
|
|
172
|
+
* Picks the loading indicator for the currently enabled style, falling back to
|
|
173
|
+
* the dots (also the default when no animations config is supplied). Returns a
|
|
174
|
+
* stable template instance per style — see the note above.
|
|
175
|
+
*/
|
|
176
|
+
const selectThinkingTemplate = (x) => {
|
|
177
|
+
const enabled = x.enabledAnimations;
|
|
178
|
+
if (enabled.includes('waves'))
|
|
179
|
+
return thinkingWavesTemplate;
|
|
180
|
+
if (enabled.includes('flowingWaves'))
|
|
181
|
+
return thinkingFlowingWavesTemplate;
|
|
182
|
+
if (enabled.includes('plasma'))
|
|
183
|
+
return thinkingPlasmaTemplate;
|
|
184
|
+
return thinkingDotsTemplate;
|
|
185
|
+
};
|
|
139
186
|
// ─── Public factory ───────────────────────────────────────────────────────────
|
|
140
187
|
/** @internal */
|
|
141
188
|
export const FoundationAiAssistantTemplate = (designSystemPrefix) => {
|
|
142
189
|
const buttonTag = `${designSystemPrefix}-button`;
|
|
143
190
|
const switchTag = `${designSystemPrefix}-switch`;
|
|
144
|
-
const
|
|
191
|
+
const categorizedMultiselectTag = `${designSystemPrefix}-categorized-multiselect`;
|
|
145
192
|
const textareaTag = `${designSystemPrefix}-text-area`;
|
|
146
193
|
const iconTag = `${designSystemPrefix}-icon`;
|
|
147
194
|
const progressTag = `${designSystemPrefix}-progress`;
|
|
@@ -356,15 +403,13 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
356
403
|
${when((x) => { var _a, _b; return ((_b = (_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.animations) === null || _b === void 0 ? void 0 : _b.userConfigurable) === true; }, html `
|
|
357
404
|
<div class="settings-animations">
|
|
358
405
|
<span class="settings-label">Animations</span>
|
|
359
|
-
<${
|
|
406
|
+
<${categorizedMultiselectTag}
|
|
360
407
|
part="toggle-animations"
|
|
361
408
|
:selectedOptions=${(x) => x.enabledAnimations}
|
|
362
409
|
:options=${() => animationOptions}
|
|
363
|
-
:itemRenderer=${() => animationItemRenderer}
|
|
364
410
|
@selectionChange=${(x, c) => x.setEnabledAnimations(c.event.detail)}
|
|
365
411
|
search="false"
|
|
366
|
-
|
|
367
|
-
></${multiselectTag}>
|
|
412
|
+
></${categorizedMultiselectTag}>
|
|
368
413
|
</div>
|
|
369
414
|
`)}
|
|
370
415
|
</div>
|
|
@@ -453,7 +498,8 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
453
498
|
${when((x) => {
|
|
454
499
|
var _a;
|
|
455
500
|
return x.showLoadingIndicator &&
|
|
456
|
-
(((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.animations) == null ||
|
|
501
|
+
(((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.animations) == null ||
|
|
502
|
+
LOADING_STYLE_ANIMATIONS.some((s) => x.enabledAnimations.includes(s)));
|
|
457
503
|
}, html `
|
|
458
504
|
<div class="message-row ai" part="thinking">
|
|
459
505
|
<div class="avatar">
|
|
@@ -465,12 +511,7 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
465
511
|
<span class="avatar-icon" :innerHTML="${() => x.assistantIconSafe}"></span>
|
|
466
512
|
`}
|
|
467
513
|
</div>
|
|
468
|
-
|
|
469
|
-
<div class="dot dot-1"></div>
|
|
470
|
-
<div class="dot dot-2"></div>
|
|
471
|
-
<div class="dot dot-3"></div>
|
|
472
|
-
<div class="dot dot-4"></div>
|
|
473
|
-
</div>
|
|
514
|
+
${(x) => selectThinkingTemplate(x)}
|
|
474
515
|
</div>
|
|
475
516
|
`)}
|
|
476
517
|
</div>
|
|
@@ -2,16 +2,39 @@
|
|
|
2
2
|
* Registry of all available animations with their display metadata.
|
|
3
3
|
* Adding an entry here automatically extends the {@link AiAssistantAnimation} type.
|
|
4
4
|
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* `loading` (dots), `waves`, `flowingWaves` and `plasma` are interchangeable
|
|
7
|
+
* styles of the same "is the assistant working" indicator and are therefore
|
|
8
|
+
* grouped under the same {@link AiAssistantAnimationDef.category}. They are
|
|
9
|
+
* mutually exclusive — see `LOADING_STYLE_ANIMATIONS`.
|
|
10
|
+
*
|
|
5
11
|
* @beta
|
|
6
12
|
*/
|
|
7
13
|
export const ANIMATION_DEFS = {
|
|
8
14
|
loading: {
|
|
9
|
-
label: '
|
|
10
|
-
|
|
15
|
+
label: 'Dots',
|
|
16
|
+
description: 'Shows pulsing dots while the assistant is generating a response.',
|
|
17
|
+
category: 'Loading style',
|
|
18
|
+
},
|
|
19
|
+
waves: {
|
|
20
|
+
label: 'Waves',
|
|
21
|
+
description: 'Shows glowing sine waves inside a circle while the assistant is generating a response.',
|
|
22
|
+
category: 'Loading style',
|
|
23
|
+
},
|
|
24
|
+
flowingWaves: {
|
|
25
|
+
label: 'Flowing waves',
|
|
26
|
+
description: 'Shows coloured waves rising and settling from a line while the assistant is generating a response.',
|
|
27
|
+
category: 'Loading style',
|
|
28
|
+
},
|
|
29
|
+
plasma: {
|
|
30
|
+
label: 'Plasma orb',
|
|
31
|
+
description: 'Shows a glowing plasma sphere with drifting energy while the assistant is generating a response.',
|
|
32
|
+
category: 'Loading style',
|
|
11
33
|
},
|
|
12
34
|
halo: {
|
|
13
35
|
label: 'Halo',
|
|
14
|
-
|
|
36
|
+
description: 'Displays a glowing halo around the assistant avatar while a response is streaming.',
|
|
37
|
+
category: 'Effects',
|
|
15
38
|
},
|
|
16
39
|
};
|
|
17
40
|
/**
|
|
@@ -20,3 +43,23 @@ export const ANIMATION_DEFS = {
|
|
|
20
43
|
* @internal
|
|
21
44
|
*/
|
|
22
45
|
export const ALL_ANIMATIONS = Object.keys(ANIMATION_DEFS);
|
|
46
|
+
/**
|
|
47
|
+
* The interchangeable "assistant is working" loading-indicator styles. At most
|
|
48
|
+
* one of these may be enabled at a time — enabling one disables the other.
|
|
49
|
+
*
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export const LOADING_STYLE_ANIMATIONS = [
|
|
53
|
+
'loading',
|
|
54
|
+
'waves',
|
|
55
|
+
'flowingWaves',
|
|
56
|
+
'plasma',
|
|
57
|
+
];
|
|
58
|
+
/**
|
|
59
|
+
* Animations enabled by default when a consumer opts into the animations
|
|
60
|
+
* feature without specifying an explicit `enabled` list. Keeps the dots loading
|
|
61
|
+
* style (the long-standing default); the waves style is opt-in.
|
|
62
|
+
*
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export const DEFAULT_ANIMATIONS = ['loading', 'halo'];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { LOADING_STYLE_ANIMATIONS } from '../main/main.types';
|
|
2
|
+
/**
|
|
3
|
+
* Enforces that at most one loading-indicator style (dots vs. waves) is enabled
|
|
4
|
+
* at a time. The two are alternative presentations of the same "assistant is
|
|
5
|
+
* working" state, so selecting one must deselect the other.
|
|
6
|
+
*
|
|
7
|
+
* The settings control is a multiselect (checkboxes), which permits selecting
|
|
8
|
+
* both; this resolver makes the loading-style group behave like a radio group
|
|
9
|
+
* by dropping the previously-selected style whenever a new one is added.
|
|
10
|
+
*
|
|
11
|
+
* @param next - The freshly-selected animation list (e.g. emitted by the
|
|
12
|
+
* multiselect, or read from consumer config).
|
|
13
|
+
* @param previous - The animation list in effect before this change. Used to
|
|
14
|
+
* work out which loading style was just added so the other can be dropped.
|
|
15
|
+
* Pass `[]` when resolving an initial/config value with no prior state.
|
|
16
|
+
* @returns `next` with at most one loading style retained. When both are
|
|
17
|
+
* present and neither is newly added (e.g. a misconfigured `enabled` list),
|
|
18
|
+
* the first entry of `LOADING_STYLE_ANIMATIONS` (dots) wins.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export function resolveExclusiveLoadingStyle(next, previous = []) {
|
|
23
|
+
var _a;
|
|
24
|
+
const selectedStyles = LOADING_STYLE_ANIMATIONS.filter((style) => next.includes(style));
|
|
25
|
+
if (selectedStyles.length <= 1) {
|
|
26
|
+
return next;
|
|
27
|
+
}
|
|
28
|
+
// Both styles selected — keep whichever was just added (absent from
|
|
29
|
+
// `previous`); fall back to the first declared style on ambiguity.
|
|
30
|
+
const justAdded = (_a = selectedStyles.find((style) => !previous.includes(style))) !== null && _a !== void 0 ? _a : selectedStyles[0];
|
|
31
|
+
return next.filter((animation) => animation === justAdded ||
|
|
32
|
+
!LOADING_STYLE_ANIMATIONS.includes(animation));
|
|
33
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
2
|
+
import { resolveExclusiveLoadingStyle } from './animation-exclusivity';
|
|
3
|
+
const suite = createLogicSuite('resolveExclusiveLoadingStyle');
|
|
4
|
+
suite('leaves a selection with only the dots loading style untouched', () => {
|
|
5
|
+
const next = ['loading', 'halo'];
|
|
6
|
+
assert.equal(resolveExclusiveLoadingStyle(next, ['halo']), ['loading', 'halo']);
|
|
7
|
+
});
|
|
8
|
+
suite('leaves a selection with only the waves loading style untouched', () => {
|
|
9
|
+
const next = ['waves', 'halo'];
|
|
10
|
+
assert.equal(resolveExclusiveLoadingStyle(next, ['halo']), ['waves', 'halo']);
|
|
11
|
+
});
|
|
12
|
+
suite('leaves a selection with no loading style untouched', () => {
|
|
13
|
+
assert.equal(resolveExclusiveLoadingStyle(['halo'], []), ['halo']);
|
|
14
|
+
});
|
|
15
|
+
suite('drops dots when waves was just added on top of dots', () => {
|
|
16
|
+
// Previously dots was on; user ticks waves → waves wins, dots removed.
|
|
17
|
+
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], ['loading', 'halo']);
|
|
18
|
+
assert.equal(result, ['waves', 'halo']);
|
|
19
|
+
});
|
|
20
|
+
suite('drops waves when dots was just added on top of waves', () => {
|
|
21
|
+
// Previously waves was on; user ticks dots → dots wins, waves removed.
|
|
22
|
+
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], ['waves', 'halo']);
|
|
23
|
+
assert.equal(result, ['loading', 'halo']);
|
|
24
|
+
});
|
|
25
|
+
suite('preserves the order of the surviving selection', () => {
|
|
26
|
+
const result = resolveExclusiveLoadingStyle(['halo', 'loading', 'waves'], ['halo', 'loading']);
|
|
27
|
+
// waves was just added → loading dropped; halo and waves keep their order.
|
|
28
|
+
assert.equal(result, ['halo', 'waves']);
|
|
29
|
+
});
|
|
30
|
+
suite('falls back to dots when both are present with no prior state (e.g. bad config)', () => {
|
|
31
|
+
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], []);
|
|
32
|
+
assert.equal(result, ['loading', 'halo']);
|
|
33
|
+
});
|
|
34
|
+
suite('falls back to dots when both were already present (neither newly added)', () => {
|
|
35
|
+
const result = resolveExclusiveLoadingStyle(['loading', 'waves'], ['loading', 'waves', 'halo']);
|
|
36
|
+
assert.equal(result, ['loading']);
|
|
37
|
+
});
|
|
38
|
+
suite('handles an empty selection', () => {
|
|
39
|
+
assert.equal(resolveExclusiveLoadingStyle([], ['loading']), []);
|
|
40
|
+
});
|
|
41
|
+
suite('switching between any two loading styles keeps only the newly added one', () => {
|
|
42
|
+
// flowingWaves was on; user picks plasma → plasma wins, flowingWaves dropped.
|
|
43
|
+
assert.equal(resolveExclusiveLoadingStyle(['flowingWaves', 'plasma', 'halo'], ['flowingWaves', 'halo']), ['plasma', 'halo']);
|
|
44
|
+
// plasma was on; user picks the dots → dots win, plasma dropped.
|
|
45
|
+
assert.equal(resolveExclusiveLoadingStyle(['plasma', 'loading'], ['plasma']), ['loading']);
|
|
46
|
+
});
|
|
47
|
+
suite('drops all but the newly added style when several are somehow selected', () => {
|
|
48
|
+
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'flowingWaves', 'plasma'], ['loading', 'waves', 'plasma']);
|
|
49
|
+
// flowingWaves is the only one absent from `previous`, so it wins.
|
|
50
|
+
assert.equal(result, ['flowingWaves']);
|
|
51
|
+
});
|
|
52
|
+
suite.run();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/halo-overlay.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/align-event-globals.ts","../src/components/chat-driver/chat-driver.test.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/config/config.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/config/validate-providers.test.ts","../src/config/validate-providers.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/state/ai-assistant-slice.ts","../src/state/debug-event-log.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/message-partition.test.ts","../src/utils/message-partition.ts","../src/utils/sum-costs.test.ts","../src/utils/sum-costs.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
|
|
1
|
+
{"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/flowing-waves-indicator.ts","../src/components/halo-overlay.ts","../src/components/plasma-orb-indicator.ts","../src/components/waves-indicator.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/align-event-globals.ts","../src/components/chat-driver/chat-driver.test.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/config/config.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/config/validate-providers.test.ts","../src/config/validate-providers.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/state/ai-assistant-slice.ts","../src/state/debug-event-log.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/animation-exclusivity.test.ts","../src/utils/animation-exclusivity.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/message-partition.test.ts","../src/utils/message-partition.ts","../src/utils/sum-costs.test.ts","../src/utils/sum-costs.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/ai-assistant",
|
|
3
3
|
"description": "Genesis AI Assistant micro-frontend",
|
|
4
|
-
"version": "14.
|
|
4
|
+
"version": "14.459.0",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -64,24 +64,24 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@genesislcap/foundation-testing": "14.
|
|
68
|
-
"@genesislcap/genx": "14.
|
|
69
|
-
"@genesislcap/rollup-builder": "14.
|
|
70
|
-
"@genesislcap/ts-builder": "14.
|
|
71
|
-
"@genesislcap/uvu-playwright-builder": "14.
|
|
72
|
-
"@genesislcap/vite-builder": "14.
|
|
73
|
-
"@genesislcap/webpack-builder": "14.
|
|
67
|
+
"@genesislcap/foundation-testing": "14.459.0",
|
|
68
|
+
"@genesislcap/genx": "14.459.0",
|
|
69
|
+
"@genesislcap/rollup-builder": "14.459.0",
|
|
70
|
+
"@genesislcap/ts-builder": "14.459.0",
|
|
71
|
+
"@genesislcap/uvu-playwright-builder": "14.459.0",
|
|
72
|
+
"@genesislcap/vite-builder": "14.459.0",
|
|
73
|
+
"@genesislcap/webpack-builder": "14.459.0",
|
|
74
74
|
"@types/dompurify": "^3.0.5",
|
|
75
75
|
"@types/marked": "^5.0.2"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@genesislcap/foundation-ai": "14.
|
|
79
|
-
"@genesislcap/foundation-logger": "14.
|
|
80
|
-
"@genesislcap/foundation-redux": "14.
|
|
81
|
-
"@genesislcap/foundation-ui": "14.
|
|
82
|
-
"@genesislcap/foundation-utils": "14.
|
|
83
|
-
"@genesislcap/rapid-design-system": "14.
|
|
84
|
-
"@genesislcap/web-core": "14.
|
|
78
|
+
"@genesislcap/foundation-ai": "14.459.0",
|
|
79
|
+
"@genesislcap/foundation-logger": "14.459.0",
|
|
80
|
+
"@genesislcap/foundation-redux": "14.459.0",
|
|
81
|
+
"@genesislcap/foundation-ui": "14.459.0",
|
|
82
|
+
"@genesislcap/foundation-utils": "14.459.0",
|
|
83
|
+
"@genesislcap/rapid-design-system": "14.459.0",
|
|
84
|
+
"@genesislcap/web-core": "14.459.0",
|
|
85
85
|
"dompurify": "^3.3.1",
|
|
86
86
|
"marked": "^17.0.3"
|
|
87
87
|
},
|
|
@@ -93,5 +93,5 @@
|
|
|
93
93
|
"publishConfig": {
|
|
94
94
|
"access": "public"
|
|
95
95
|
},
|
|
96
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "b501804b11ff99945c7c6d2afbe7a101e1bb735a"
|
|
97
97
|
}
|