@genesislcap/ai-assistant 14.431.0 → 14.432.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 +136 -4
- package/dist/ai-assistant.d.ts +55 -2
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -1
- package/dist/dts/config/config.d.ts +20 -0
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +22 -1
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/types/ai-chat-widget.d.ts +12 -1
- package/dist/dts/types/ai-chat-widget.d.ts.map +1 -1
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +6 -0
- package/dist/esm/main/main.js +59 -12
- package/dist/esm/main/main.template.js +30 -29
- package/package.json +16 -16
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +6 -0
- package/src/config/config.ts +21 -0
- package/src/main/main.template.ts +47 -42
- package/src/main/main.ts +52 -9
- package/src/types/ai-chat-widget.ts +12 -1
|
@@ -963,7 +963,7 @@
|
|
|
963
963
|
{
|
|
964
964
|
"kind": "Interface",
|
|
965
965
|
"canonicalReference": "@genesislcap/ai-assistant!AiChatWidget:interface",
|
|
966
|
-
"docComment": "/**\n * Interface that AI inline interaction components can implement. The `AiChatInteractionWrapper` always sets `data` and `
|
|
966
|
+
"docComment": "/**\n * Interface that AI inline interaction components can implement. The `AiChatInteractionWrapper` always sets `data`, `resolved`, and `shouldAutoFocus` on the rendered element.\n *\n * @beta\n */\n",
|
|
967
967
|
"excerptTokens": [
|
|
968
968
|
{
|
|
969
969
|
"kind": "Content",
|
|
@@ -1033,6 +1033,33 @@
|
|
|
1033
1033
|
"startIndex": 1,
|
|
1034
1034
|
"endIndex": 3
|
|
1035
1035
|
}
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
"kind": "PropertySignature",
|
|
1039
|
+
"canonicalReference": "@genesislcap/ai-assistant!AiChatWidget#shouldAutoFocus:member",
|
|
1040
|
+
"docComment": "/**\n * True when the widget is being mounted fresh and the assistant currently has the user's engagement (last pointer/focus interaction was inside the assistant). Widgets with input fields should use this to decide whether to call `.focus()` on mount. False when: - the user has interacted with something outside the assistant, or - this widget has already been resolved (we're re-rendering history due to a layout-lifecycle remount, dock/undock, etc.).\n */\n",
|
|
1041
|
+
"excerptTokens": [
|
|
1042
|
+
{
|
|
1043
|
+
"kind": "Content",
|
|
1044
|
+
"text": "shouldAutoFocus?: "
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
"kind": "Content",
|
|
1048
|
+
"text": "boolean"
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
"kind": "Content",
|
|
1052
|
+
"text": ";"
|
|
1053
|
+
}
|
|
1054
|
+
],
|
|
1055
|
+
"isReadonly": false,
|
|
1056
|
+
"isOptional": true,
|
|
1057
|
+
"releaseTag": "Beta",
|
|
1058
|
+
"name": "shouldAutoFocus",
|
|
1059
|
+
"propertyTypeTokenRange": {
|
|
1060
|
+
"startIndex": 1,
|
|
1061
|
+
"endIndex": 2
|
|
1062
|
+
}
|
|
1036
1063
|
}
|
|
1037
1064
|
],
|
|
1038
1065
|
"extendsTokenRanges": []
|
|
@@ -2638,6 +2665,32 @@
|
|
|
2638
2665
|
"endIndex": 7
|
|
2639
2666
|
}
|
|
2640
2667
|
},
|
|
2668
|
+
{
|
|
2669
|
+
"kind": "TypeAlias",
|
|
2670
|
+
"canonicalReference": "@genesislcap/ai-assistant!ChatInputDuringExecutionMode:type",
|
|
2671
|
+
"docComment": "/**\n * Controls how the main chat input area behaves while this agent is executing (i.e. while `state === 'loading'` — covers both LLM thinking and pending widget interactions).\n *\n * - `'disabled'` (default): input stays visible but disabled. - `'hidden'`: the entire input row (attach button, textarea, send button) is hidden. Useful for agents whose interaction widgets contain their own input affordances and where a second disabled chat input is redundant or confusing — typically long-running agents (planners) where one transition in/out is less jarring than mid-run flicker.\n *\n * @beta\n */\n",
|
|
2672
|
+
"excerptTokens": [
|
|
2673
|
+
{
|
|
2674
|
+
"kind": "Content",
|
|
2675
|
+
"text": "export type ChatInputDuringExecutionMode = "
|
|
2676
|
+
},
|
|
2677
|
+
{
|
|
2678
|
+
"kind": "Content",
|
|
2679
|
+
"text": "'disabled' | 'hidden'"
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
"kind": "Content",
|
|
2683
|
+
"text": ";"
|
|
2684
|
+
}
|
|
2685
|
+
],
|
|
2686
|
+
"fileUrlPath": "src/config/config.ts",
|
|
2687
|
+
"releaseTag": "Beta",
|
|
2688
|
+
"name": "ChatInputDuringExecutionMode",
|
|
2689
|
+
"typeTokenRange": {
|
|
2690
|
+
"startIndex": 1,
|
|
2691
|
+
"endIndex": 2
|
|
2692
|
+
}
|
|
2693
|
+
},
|
|
2641
2694
|
{
|
|
2642
2695
|
"kind": "Function",
|
|
2643
2696
|
"canonicalReference": "@genesislcap/ai-assistant!createToolFold:function(1)",
|
|
@@ -3229,6 +3282,37 @@
|
|
|
3229
3282
|
"isAbstract": false,
|
|
3230
3283
|
"name": "chatConfigChanged"
|
|
3231
3284
|
},
|
|
3285
|
+
{
|
|
3286
|
+
"kind": "Property",
|
|
3287
|
+
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#chatInputEl:member",
|
|
3288
|
+
"docComment": "/**\n * Bound to the rapid-text-area chat input via the template ref.\n */\n",
|
|
3289
|
+
"excerptTokens": [
|
|
3290
|
+
{
|
|
3291
|
+
"kind": "Content",
|
|
3292
|
+
"text": "chatInputEl?: "
|
|
3293
|
+
},
|
|
3294
|
+
{
|
|
3295
|
+
"kind": "Reference",
|
|
3296
|
+
"text": "HTMLElement",
|
|
3297
|
+
"canonicalReference": "!HTMLElement:interface"
|
|
3298
|
+
},
|
|
3299
|
+
{
|
|
3300
|
+
"kind": "Content",
|
|
3301
|
+
"text": ";"
|
|
3302
|
+
}
|
|
3303
|
+
],
|
|
3304
|
+
"isReadonly": false,
|
|
3305
|
+
"isOptional": true,
|
|
3306
|
+
"releaseTag": "Beta",
|
|
3307
|
+
"name": "chatInputEl",
|
|
3308
|
+
"propertyTypeTokenRange": {
|
|
3309
|
+
"startIndex": 1,
|
|
3310
|
+
"endIndex": 2
|
|
3311
|
+
},
|
|
3312
|
+
"isStatic": false,
|
|
3313
|
+
"isProtected": false,
|
|
3314
|
+
"isAbstract": false
|
|
3315
|
+
},
|
|
3232
3316
|
{
|
|
3233
3317
|
"kind": "Method",
|
|
3234
3318
|
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#connectedCallback:member(1)",
|
|
@@ -3563,7 +3647,16 @@
|
|
|
3563
3647
|
},
|
|
3564
3648
|
{
|
|
3565
3649
|
"kind": "Content",
|
|
3566
|
-
"text": "[];\n
|
|
3650
|
+
"text": "[];\n chatInputDuringExecution?: import(\"../config/config\")."
|
|
3651
|
+
},
|
|
3652
|
+
{
|
|
3653
|
+
"kind": "Reference",
|
|
3654
|
+
"text": "ChatInputDuringExecutionMode",
|
|
3655
|
+
"canonicalReference": "@genesislcap/ai-assistant!ChatInputDuringExecutionMode:type"
|
|
3656
|
+
},
|
|
3657
|
+
{
|
|
3658
|
+
"kind": "Content",
|
|
3659
|
+
"text": ";\n } | {\n toolDefinitions: import(\"../utils/tool-fold\")."
|
|
3567
3660
|
},
|
|
3568
3661
|
{
|
|
3569
3662
|
"kind": "Reference",
|
|
@@ -3590,7 +3683,16 @@
|
|
|
3590
3683
|
},
|
|
3591
3684
|
{
|
|
3592
3685
|
"kind": "Content",
|
|
3593
|
-
"text": "[];\n
|
|
3686
|
+
"text": "[];\n chatInputDuringExecution?: import(\"../config/config\")."
|
|
3687
|
+
},
|
|
3688
|
+
{
|
|
3689
|
+
"kind": "Reference",
|
|
3690
|
+
"text": "ChatInputDuringExecutionMode",
|
|
3691
|
+
"canonicalReference": "@genesislcap/ai-assistant!ChatInputDuringExecutionMode:type"
|
|
3692
|
+
},
|
|
3693
|
+
{
|
|
3694
|
+
"kind": "Content",
|
|
3695
|
+
"text": ";\n })[];\n activeSystemPrompt: string;\n activePrimerHistory: "
|
|
3594
3696
|
},
|
|
3595
3697
|
{
|
|
3596
3698
|
"kind": "Reference",
|
|
@@ -3609,7 +3711,7 @@
|
|
|
3609
3711
|
"isStatic": false,
|
|
3610
3712
|
"returnTypeTokenRange": {
|
|
3611
3713
|
"startIndex": 1,
|
|
3612
|
-
"endIndex":
|
|
3714
|
+
"endIndex": 22
|
|
3613
3715
|
},
|
|
3614
3716
|
"releaseTag": "Beta",
|
|
3615
3717
|
"isProtected": false,
|
|
@@ -3947,6 +4049,36 @@
|
|
|
3947
4049
|
"isProtected": false,
|
|
3948
4050
|
"isAbstract": false
|
|
3949
4051
|
},
|
|
4052
|
+
{
|
|
4053
|
+
"kind": "Property",
|
|
4054
|
+
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#isEngaged:member",
|
|
4055
|
+
"docComment": "/**\n * True when the user's last interaction (pointer or keyboard focus) was inside the assistant. Defaults to true so the assistant claims focus on first mount. Auto-focus features (chat input on idle, widget inputs on mount) gate on this to avoid stealing focus from elsewhere on the page.\n */\n",
|
|
4056
|
+
"excerptTokens": [
|
|
4057
|
+
{
|
|
4058
|
+
"kind": "Content",
|
|
4059
|
+
"text": "isEngaged: "
|
|
4060
|
+
},
|
|
4061
|
+
{
|
|
4062
|
+
"kind": "Content",
|
|
4063
|
+
"text": "boolean"
|
|
4064
|
+
},
|
|
4065
|
+
{
|
|
4066
|
+
"kind": "Content",
|
|
4067
|
+
"text": ";"
|
|
4068
|
+
}
|
|
4069
|
+
],
|
|
4070
|
+
"isReadonly": false,
|
|
4071
|
+
"isOptional": false,
|
|
4072
|
+
"releaseTag": "Beta",
|
|
4073
|
+
"name": "isEngaged",
|
|
4074
|
+
"propertyTypeTokenRange": {
|
|
4075
|
+
"startIndex": 1,
|
|
4076
|
+
"endIndex": 2
|
|
4077
|
+
},
|
|
4078
|
+
"isStatic": false,
|
|
4079
|
+
"isProtected": false,
|
|
4080
|
+
"isAbstract": false
|
|
4081
|
+
},
|
|
3950
4082
|
{
|
|
3951
4083
|
"kind": "Property",
|
|
3952
4084
|
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#liveSubAgentName:member",
|
package/dist/ai-assistant.d.ts
CHANGED
|
@@ -159,7 +159,8 @@ export declare class AiChatMarkdown extends GenesisElement {
|
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
161
|
* Interface that AI inline interaction components can implement.
|
|
162
|
-
* The `AiChatInteractionWrapper` always sets `data
|
|
162
|
+
* The `AiChatInteractionWrapper` always sets `data`, `resolved`, and
|
|
163
|
+
* `shouldAutoFocus` on the rendered element.
|
|
163
164
|
*
|
|
164
165
|
* @beta
|
|
165
166
|
*/
|
|
@@ -167,6 +168,16 @@ export declare interface AiChatWidget {
|
|
|
167
168
|
data: unknown;
|
|
168
169
|
/** The result when the interaction has been resolved. Truthy = resolved; value contains status/payload for display. */
|
|
169
170
|
resolved?: InteractionResult<unknown>;
|
|
171
|
+
/**
|
|
172
|
+
* True when the widget is being mounted fresh and the assistant currently has
|
|
173
|
+
* the user's engagement (last pointer/focus interaction was inside the
|
|
174
|
+
* assistant). Widgets with input fields should use this to decide whether to
|
|
175
|
+
* call `.focus()` on mount. False when:
|
|
176
|
+
* - the user has interacted with something outside the assistant, or
|
|
177
|
+
* - this widget has already been resolved (we're re-rendering history due
|
|
178
|
+
* to a layout-lifecycle remount, dock/undock, etc.).
|
|
179
|
+
*/
|
|
180
|
+
shouldAutoFocus?: boolean;
|
|
170
181
|
}
|
|
171
182
|
|
|
172
183
|
/**
|
|
@@ -294,6 +305,11 @@ declare interface BaseAgentConfig {
|
|
|
294
305
|
* Sub-agents available to this agent's tool handlers via `requestSubAgent`.
|
|
295
306
|
*/
|
|
296
307
|
subAgents?: AgentConfig[];
|
|
308
|
+
/**
|
|
309
|
+
* How the main chat input area behaves while this agent is executing.
|
|
310
|
+
* Defaults to `'disabled'`. See {@link ChatInputDuringExecutionMode}.
|
|
311
|
+
*/
|
|
312
|
+
chatInputDuringExecution?: ChatInputDuringExecutionMode;
|
|
297
313
|
}
|
|
298
314
|
|
|
299
315
|
/**
|
|
@@ -433,6 +449,22 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
|
|
|
433
449
|
*/
|
|
434
450
|
export declare type ChatHistoryUpdatedEvent = CustomEvent<ReadonlyArray<ChatMessage>>;
|
|
435
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Controls how the main chat input area behaves while this agent is executing
|
|
454
|
+
* (i.e. while `state === 'loading'` — covers both LLM thinking and pending
|
|
455
|
+
* widget interactions).
|
|
456
|
+
*
|
|
457
|
+
* - `'disabled'` (default): input stays visible but disabled.
|
|
458
|
+
* - `'hidden'`: the entire input row (attach button, textarea, send button) is
|
|
459
|
+
* hidden. Useful for agents whose interaction widgets contain their own
|
|
460
|
+
* input affordances and where a second disabled chat input is redundant or
|
|
461
|
+
* confusing — typically long-running agents (planners) where one transition
|
|
462
|
+
* in/out is less jarring than mid-run flicker.
|
|
463
|
+
*
|
|
464
|
+
* @beta
|
|
465
|
+
*/
|
|
466
|
+
export declare type ChatInputDuringExecutionMode = 'disabled' | 'hidden';
|
|
467
|
+
|
|
436
468
|
/**
|
|
437
469
|
* Creates a tool fold — a facade that hides a group of related tools behind a single
|
|
438
470
|
* named entry, revealing them progressively when the model invokes the facade.
|
|
@@ -581,6 +613,16 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
581
613
|
set messages(value: ChatMessage[]);
|
|
582
614
|
get state(): AiAssistantState;
|
|
583
615
|
set state(value: AiAssistantState);
|
|
616
|
+
/**
|
|
617
|
+
* Focus the main chat input if the user is still engaged with the assistant.
|
|
618
|
+
* Deferred to the next frame so any conditional re-render (e.g. unhiding the
|
|
619
|
+
* input row when an agent's `chatInputDuringExecution: 'hidden'` execution
|
|
620
|
+
* ends) has committed before we call `.focus()`.
|
|
621
|
+
*
|
|
622
|
+
* `rapid-text-area` does not delegate focus to its inner native `<textarea>`,
|
|
623
|
+
* so we drill into its shadow root and focus the textarea directly.
|
|
624
|
+
*/
|
|
625
|
+
private maybeAutoFocusChatInput;
|
|
584
626
|
get activeAgent(): Omit<AgentConfig, 'toolHandlers'> | undefined;
|
|
585
627
|
set activeAgent(value: AgentConfig | undefined);
|
|
586
628
|
get suggestionsState(): SuggestionsState;
|
|
@@ -627,6 +669,16 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
627
669
|
private _driverAgentsKey?;
|
|
628
670
|
/** Bound to the messages container via the template ref — assigned by FAST before connectedCallback logic runs. */
|
|
629
671
|
messagesEl?: HTMLElement;
|
|
672
|
+
/** Bound to the rapid-text-area chat input via the template ref. */
|
|
673
|
+
chatInputEl?: HTMLElement;
|
|
674
|
+
/**
|
|
675
|
+
* True when the user's last interaction (pointer or keyboard focus) was inside
|
|
676
|
+
* the assistant. Defaults to true so the assistant claims focus on first mount.
|
|
677
|
+
* Auto-focus features (chat input on idle, widget inputs on mount) gate on this
|
|
678
|
+
* to avoid stealing focus from elsewhere on the page.
|
|
679
|
+
*/
|
|
680
|
+
isEngaged: boolean;
|
|
681
|
+
private _handleGlobalInteraction;
|
|
630
682
|
/** True when the user has intentionally scrolled away from the bottom — suppresses auto-scroll. */
|
|
631
683
|
private _userScrolledAway;
|
|
632
684
|
private _scrollListener?;
|
|
@@ -718,6 +770,7 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
718
770
|
systemPrompt?: string;
|
|
719
771
|
primerHistory?: ChatMessage[];
|
|
720
772
|
subAgents?: AgentConfig[];
|
|
773
|
+
chatInputDuringExecution?: ChatInputDuringExecutionMode;
|
|
721
774
|
} | {
|
|
722
775
|
toolDefinitions: ToolTreeNode[];
|
|
723
776
|
toolHandlers: any;
|
|
@@ -727,6 +780,7 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
727
780
|
systemPrompt?: string;
|
|
728
781
|
primerHistory?: ChatMessage[];
|
|
729
782
|
subAgents?: AgentConfig[];
|
|
783
|
+
chatInputDuringExecution?: ChatInputDuringExecutionMode;
|
|
730
784
|
})[];
|
|
731
785
|
activeSystemPrompt: string;
|
|
732
786
|
activePrimerHistory: ChatMessage[];
|
|
@@ -746,7 +800,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
746
800
|
handleSuggestionClick(suggestion: string): void;
|
|
747
801
|
private fetchSuggestions;
|
|
748
802
|
private send;
|
|
749
|
-
private restoreFocusIfAppropriate;
|
|
750
803
|
onChatHeaderMouseDown(e: MouseEvent): void;
|
|
751
804
|
handleInteractionCompleted(e: Event): void;
|
|
752
805
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-interaction-wrapper.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAiB,cAAc,EAAc,MAAM,uBAAuB,CAAC;AAKlF,MAAM,MAAM,wBAAwB,GAAG;IACrC,uBAAuB,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACjF,CAAC;AAEF;;;;;GAKG;AACH,qBAKa,wBAAyB,SAAQ,cAAc;IAC1D,gBAAgB;IACJ,aAAa,EAAE,MAAM,CAAM;IACvC,gBAAgB;IACJ,IAAI,EAAE,GAAG,CAAC;IACtB,gBAAgB;IACJ,aAAa,EAAE,MAAM,CAAM;IACvC,6GAA6G;IACjG,QAAQ,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAa;IAEzE,gBAAgB;IAChB,SAAS,EAAG,WAAW,CAAC;IAExB,iBAAiB;IAKjB,oBAAoB;IAIpB,eAAe;IAOf,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"chat-interaction-wrapper.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAiB,cAAc,EAAc,MAAM,uBAAuB,CAAC;AAKlF,MAAM,MAAM,wBAAwB,GAAG;IACrC,uBAAuB,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACjF,CAAC;AAEF;;;;;GAKG;AACH,qBAKa,wBAAyB,SAAQ,cAAc;IAC1D,gBAAgB;IACJ,aAAa,EAAE,MAAM,CAAM;IACvC,gBAAgB;IACJ,IAAI,EAAE,GAAG,CAAC;IACtB,gBAAgB;IACJ,aAAa,EAAE,MAAM,CAAM;IACvC,6GAA6G;IACjG,QAAQ,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAa;IAEzE,gBAAgB;IAChB,SAAS,EAAG,WAAW,CAAC;IAExB,iBAAiB;IAKjB,oBAAoB;IAIpB,eAAe;IAOf,OAAO,CAAC,eAAe;CAoDxB"}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import type { ChatMessage, ChatToolDefinition, ChatToolHandlers } from '@genesislcap/foundation-ai';
|
|
2
|
+
/**
|
|
3
|
+
* Controls how the main chat input area behaves while this agent is executing
|
|
4
|
+
* (i.e. while `state === 'loading'` — covers both LLM thinking and pending
|
|
5
|
+
* widget interactions).
|
|
6
|
+
*
|
|
7
|
+
* - `'disabled'` (default): input stays visible but disabled.
|
|
8
|
+
* - `'hidden'`: the entire input row (attach button, textarea, send button) is
|
|
9
|
+
* hidden. Useful for agents whose interaction widgets contain their own
|
|
10
|
+
* input affordances and where a second disabled chat input is redundant or
|
|
11
|
+
* confusing — typically long-running agents (planners) where one transition
|
|
12
|
+
* in/out is less jarring than mid-run flicker.
|
|
13
|
+
*
|
|
14
|
+
* @beta
|
|
15
|
+
*/
|
|
16
|
+
export type ChatInputDuringExecutionMode = 'disabled' | 'hidden';
|
|
2
17
|
interface BaseAgentConfig {
|
|
3
18
|
/**
|
|
4
19
|
* Display name shown in the chat header when this agent is active.
|
|
@@ -25,6 +40,11 @@ interface BaseAgentConfig {
|
|
|
25
40
|
* Sub-agents available to this agent's tool handlers via `requestSubAgent`.
|
|
26
41
|
*/
|
|
27
42
|
subAgents?: AgentConfig[];
|
|
43
|
+
/**
|
|
44
|
+
* How the main chat input area behaves while this agent is executing.
|
|
45
|
+
* Defaults to `'disabled'`. See {@link ChatInputDuringExecutionMode}.
|
|
46
|
+
*/
|
|
47
|
+
chatInputDuringExecution?: ChatInputDuringExecutionMode;
|
|
28
48
|
}
|
|
29
49
|
/**
|
|
30
50
|
* Configuration for a specialist agent.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEpG,UAAU,eAAe;IACvB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,eAAe,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACvC;;OAEG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;OAGG;IACH,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;IAC9B;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEpG;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,4BAA4B,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEjE,UAAU,eAAe;IACvB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,eAAe,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACvC;;OAEG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC;;;OAGG;IACH,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;IAC9B;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,4BAA4B,CAAC;CACzD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D;;OAEG;IACH,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,qBAAqB,GAAG,mBAAmB,CAAC;AAEtE;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,WAAW,CAAC,KAAK,CAAC,CAAC,SAAS,WAAW,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAErE"}
|
package/dist/dts/main/main.d.ts
CHANGED
|
@@ -44,6 +44,16 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
44
44
|
set messages(value: ChatMessage[]);
|
|
45
45
|
get state(): AiAssistantState;
|
|
46
46
|
set state(value: AiAssistantState);
|
|
47
|
+
/**
|
|
48
|
+
* Focus the main chat input if the user is still engaged with the assistant.
|
|
49
|
+
* Deferred to the next frame so any conditional re-render (e.g. unhiding the
|
|
50
|
+
* input row when an agent's `chatInputDuringExecution: 'hidden'` execution
|
|
51
|
+
* ends) has committed before we call `.focus()`.
|
|
52
|
+
*
|
|
53
|
+
* `rapid-text-area` does not delegate focus to its inner native `<textarea>`,
|
|
54
|
+
* so we drill into its shadow root and focus the textarea directly.
|
|
55
|
+
*/
|
|
56
|
+
private maybeAutoFocusChatInput;
|
|
47
57
|
get activeAgent(): Omit<AgentConfig, 'toolHandlers'> | undefined;
|
|
48
58
|
set activeAgent(value: AgentConfig | undefined);
|
|
49
59
|
get suggestionsState(): SuggestionsState;
|
|
@@ -90,6 +100,16 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
90
100
|
private _driverAgentsKey?;
|
|
91
101
|
/** Bound to the messages container via the template ref — assigned by FAST before connectedCallback logic runs. */
|
|
92
102
|
messagesEl?: HTMLElement;
|
|
103
|
+
/** Bound to the rapid-text-area chat input via the template ref. */
|
|
104
|
+
chatInputEl?: HTMLElement;
|
|
105
|
+
/**
|
|
106
|
+
* True when the user's last interaction (pointer or keyboard focus) was inside
|
|
107
|
+
* the assistant. Defaults to true so the assistant claims focus on first mount.
|
|
108
|
+
* Auto-focus features (chat input on idle, widget inputs on mount) gate on this
|
|
109
|
+
* to avoid stealing focus from elsewhere on the page.
|
|
110
|
+
*/
|
|
111
|
+
isEngaged: boolean;
|
|
112
|
+
private _handleGlobalInteraction;
|
|
93
113
|
/** True when the user has intentionally scrolled away from the bottom — suppresses auto-scroll. */
|
|
94
114
|
private _userScrolledAway;
|
|
95
115
|
private _scrollListener?;
|
|
@@ -181,6 +201,7 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
181
201
|
systemPrompt?: string;
|
|
182
202
|
primerHistory?: ChatMessage[];
|
|
183
203
|
subAgents?: AgentConfig[];
|
|
204
|
+
chatInputDuringExecution?: import("../config/config").ChatInputDuringExecutionMode;
|
|
184
205
|
} | {
|
|
185
206
|
toolDefinitions: import("../utils/tool-fold").ToolTreeNode[];
|
|
186
207
|
toolHandlers: any;
|
|
@@ -190,6 +211,7 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
190
211
|
systemPrompt?: string;
|
|
191
212
|
primerHistory?: ChatMessage[];
|
|
192
213
|
subAgents?: AgentConfig[];
|
|
214
|
+
chatInputDuringExecution?: import("../config/config").ChatInputDuringExecutionMode;
|
|
193
215
|
})[];
|
|
194
216
|
activeSystemPrompt: string;
|
|
195
217
|
activePrimerHistory: ChatMessage[];
|
|
@@ -209,7 +231,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
209
231
|
handleSuggestionClick(suggestion: string): void;
|
|
210
232
|
private fetchSuggestions;
|
|
211
233
|
private send;
|
|
212
|
-
private restoreFocusIfAppropriate;
|
|
213
234
|
onChatHeaderMouseDown(e: MouseEvent): void;
|
|
214
235
|
handleInteractionCompleted(e: Event): void;
|
|
215
236
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/main/main.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE1F,OAAO,EAGL,cAAc,EAIf,MAAM,uBAAuB,CAAC;AAU/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AASpD,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EACjB,MAAM,cAAc,CAAC;AA6BtB;;;;;;;;;;;;;GAaG;AACH,qBAOa,qBAAsB,SAAQ,cAAc;IAC3C,UAAU,EAAG,UAAU,CAAC;IAExB,kBAAkB,EAAE,MAAM,CAAW;IACZ,WAAW,EAAE,MAAM,CAAuB;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC5C,WAAW,EAAE,MAAM,CAA0B;IACrD;;;;;OAKG;IACiC,UAAU,CAAC,EAAE,UAAU,CAAC;IAC5D;;;OAGG;IACS,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,UAAU,CAAM;IAC5B,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;IAC9C,0EAA0E;IACrB,UAAU,UAAS;IAIxE,OAAO,CAAC,WAAW,CAAC,CAAqB;IAEzC,IAAI,QAAQ,IAAI,WAAW,EAAE,CAE5B;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,EAGhC;IAED,IAAI,KAAK,IAAI,gBAAgB,CAE5B;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/main/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.template.d.ts","sourceRoot":"","sources":["../../../src/main/main.template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAA2B,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAiIpD,gBAAgB;AAChB,eAAO,MAAM,6BAA6B,GACxC,oBAAoB,MAAM,KACzB,YAAY,CAAC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"main.template.d.ts","sourceRoot":"","sources":["../../../src/main/main.template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAA2B,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAiIpD,gBAAgB;AAChB,eAAO,MAAM,6BAA6B,GACxC,oBAAoB,MAAM,KACzB,YAAY,CAAC,qBAAqB,CA+ZpC,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { InteractionResult } from '@genesislcap/foundation-ai';
|
|
2
2
|
/**
|
|
3
3
|
* Interface that AI inline interaction components can implement.
|
|
4
|
-
* The `AiChatInteractionWrapper` always sets `data
|
|
4
|
+
* The `AiChatInteractionWrapper` always sets `data`, `resolved`, and
|
|
5
|
+
* `shouldAutoFocus` on the rendered element.
|
|
5
6
|
*
|
|
6
7
|
* @beta
|
|
7
8
|
*/
|
|
@@ -9,5 +10,15 @@ export interface AiChatWidget {
|
|
|
9
10
|
data: unknown;
|
|
10
11
|
/** The result when the interaction has been resolved. Truthy = resolved; value contains status/payload for display. */
|
|
11
12
|
resolved?: InteractionResult<unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* True when the widget is being mounted fresh and the assistant currently has
|
|
15
|
+
* the user's engagement (last pointer/focus interaction was inside the
|
|
16
|
+
* assistant). Widgets with input fields should use this to decide whether to
|
|
17
|
+
* call `.focus()` on mount. False when:
|
|
18
|
+
* - the user has interacted with something outside the assistant, or
|
|
19
|
+
* - this widget has already been resolved (we're re-rendering history due
|
|
20
|
+
* to a layout-lifecycle remount, dock/undock, etc.).
|
|
21
|
+
*/
|
|
22
|
+
shouldAutoFocus?: boolean;
|
|
12
23
|
}
|
|
13
24
|
//# sourceMappingURL=ai-chat-widget.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-chat-widget.d.ts","sourceRoot":"","sources":["../../../src/types/ai-chat-widget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE
|
|
1
|
+
{"version":3,"file":"ai-chat-widget.d.ts","sourceRoot":"","sources":["../../../src/types/ai-chat-widget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,uHAAuH;IACvH,QAAQ,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACtC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B"}
|
|
@@ -52,6 +52,12 @@ let AiChatInteractionWrapper = class AiChatInteractionWrapper extends GenesisEle
|
|
|
52
52
|
element.data = this.data;
|
|
53
53
|
}
|
|
54
54
|
element.resolved = this.resolved;
|
|
55
|
+
// Pierce the wrapper's shadow boundary to read engagement from the
|
|
56
|
+
// assistant host. False if the widget is already resolved — re-renders
|
|
57
|
+
// from history (layout remount, dock/undock) must never steal focus.
|
|
58
|
+
const root = this.getRootNode();
|
|
59
|
+
const host = root instanceof ShadowRoot ? root.host : null;
|
|
60
|
+
element.shouldAutoFocus = !this.resolved && !!(host === null || host === void 0 ? void 0 : host.isEngaged);
|
|
55
61
|
// Handle completion from the inner component.
|
|
56
62
|
// Guard against re-emission if the interaction was already resolved
|
|
57
63
|
// (e.g. auto-resolve components fire on connectedCallback when rendering history).
|
package/dist/esm/main/main.js
CHANGED
|
@@ -91,6 +91,19 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
91
91
|
/** Whether the splash overlay is currently showing (no messages and showSplash is enabled). Reflected as a boolean attribute on the host. */
|
|
92
92
|
this.showingSplash = false;
|
|
93
93
|
this.haloStartPublished = false;
|
|
94
|
+
/**
|
|
95
|
+
* True when the user's last interaction (pointer or keyboard focus) was inside
|
|
96
|
+
* the assistant. Defaults to true so the assistant claims focus on first mount.
|
|
97
|
+
* Auto-focus features (chat input on idle, widget inputs on mount) gate on this
|
|
98
|
+
* to avoid stealing focus from elsewhere on the page.
|
|
99
|
+
*/
|
|
100
|
+
this.isEngaged = true;
|
|
101
|
+
this._handleGlobalInteraction = (e) => {
|
|
102
|
+
const next = e.composedPath().includes(this);
|
|
103
|
+
if (next === this.isEngaged)
|
|
104
|
+
return;
|
|
105
|
+
this.isEngaged = next;
|
|
106
|
+
};
|
|
94
107
|
/** True when the user has intentionally scrolled away from the bottom — suppresses auto-scroll. */
|
|
95
108
|
this._userScrolledAway = false;
|
|
96
109
|
this.showHalo = 'no';
|
|
@@ -109,9 +122,35 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
109
122
|
return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.state) !== null && _b !== void 0 ? _b : 'idle';
|
|
110
123
|
}
|
|
111
124
|
set state(value) {
|
|
112
|
-
var _a;
|
|
113
|
-
(_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.
|
|
125
|
+
var _a, _b;
|
|
126
|
+
const prev = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.state;
|
|
127
|
+
(_b = this._sessionRef) === null || _b === void 0 ? void 0 : _b.actions.aiAssistant.setState(value);
|
|
114
128
|
this.syncShowHalo();
|
|
129
|
+
// When the agent finishes (loading → !loading) the input row reappears (or
|
|
130
|
+
// becomes enabled) — refocus it so the user can type immediately, but only
|
|
131
|
+
// if they haven't moved on to something else on the page.
|
|
132
|
+
if (prev === 'loading' && value !== 'loading') {
|
|
133
|
+
this.maybeAutoFocusChatInput();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Focus the main chat input if the user is still engaged with the assistant.
|
|
138
|
+
* Deferred to the next frame so any conditional re-render (e.g. unhiding the
|
|
139
|
+
* input row when an agent's `chatInputDuringExecution: 'hidden'` execution
|
|
140
|
+
* ends) has committed before we call `.focus()`.
|
|
141
|
+
*
|
|
142
|
+
* `rapid-text-area` does not delegate focus to its inner native `<textarea>`,
|
|
143
|
+
* so we drill into its shadow root and focus the textarea directly.
|
|
144
|
+
*/
|
|
145
|
+
maybeAutoFocusChatInput() {
|
|
146
|
+
if (!this.isEngaged)
|
|
147
|
+
return;
|
|
148
|
+
requestAnimationFrame(() => {
|
|
149
|
+
var _a, _b, _c;
|
|
150
|
+
if (!this.isEngaged || !this.isConnected)
|
|
151
|
+
return;
|
|
152
|
+
(_c = (_b = (_a = this.chatInputEl) === null || _a === void 0 ? void 0 : _a.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('textarea')) === null || _c === void 0 ? void 0 : _c.focus();
|
|
153
|
+
});
|
|
115
154
|
}
|
|
116
155
|
get activeAgent() {
|
|
117
156
|
var _a;
|
|
@@ -474,6 +513,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
474
513
|
this.stopLoadingTimer();
|
|
475
514
|
this.syncShowHalo();
|
|
476
515
|
this.fetchSuggestions();
|
|
516
|
+
this.maybeAutoFocusChatInput();
|
|
477
517
|
(_b = this._executionCompletionUnsub) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
478
518
|
this._executionCompletionUnsub = undefined;
|
|
479
519
|
}
|
|
@@ -489,6 +529,14 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
489
529
|
};
|
|
490
530
|
this.messagesEl.addEventListener('scroll', this._scrollListener);
|
|
491
531
|
}
|
|
532
|
+
// Track whether the user's most recent interaction was inside this assistant.
|
|
533
|
+
// Capture phase + composedPath() pierces shadow boundaries reliably.
|
|
534
|
+
document.addEventListener('pointerdown', this._handleGlobalInteraction, true);
|
|
535
|
+
document.addEventListener('focusin', this._handleGlobalInteraction, true);
|
|
536
|
+
// Initial focus on mount when idle (skips during a mid-lifecycle reattach
|
|
537
|
+
// where state is loading and the input may be hidden or disabled).
|
|
538
|
+
if (this.state !== 'loading')
|
|
539
|
+
this.maybeAutoFocusChatInput();
|
|
492
540
|
logger.debug('FoundationAiAssistant connected');
|
|
493
541
|
}
|
|
494
542
|
disconnectedCallback() {
|
|
@@ -506,6 +554,8 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
506
554
|
}
|
|
507
555
|
this._scrollListener = undefined;
|
|
508
556
|
this._userScrolledAway = false;
|
|
557
|
+
document.removeEventListener('pointerdown', this._handleGlobalInteraction, true);
|
|
558
|
+
document.removeEventListener('focusin', this._handleGlobalInteraction, true);
|
|
509
559
|
// Clear local references only — driver and store stay in their registries.
|
|
510
560
|
this.driver = undefined;
|
|
511
561
|
this._sessionRef = undefined;
|
|
@@ -869,19 +919,13 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
869
919
|
capturedSessionRef === null || capturedSessionRef === void 0 ? void 0 : capturedSessionRef.actions.aiAssistant.setState('idle');
|
|
870
920
|
this.syncShowHalo();
|
|
871
921
|
this.fetchSuggestions();
|
|
872
|
-
|
|
922
|
+
// setState was dispatched directly via the captured ref (not via the
|
|
923
|
+
// state setter) so the setter's auto-focus path doesn't fire here. Call
|
|
924
|
+
// it explicitly so the chat input refocuses after the run completes.
|
|
925
|
+
this.maybeAutoFocusChatInput();
|
|
873
926
|
}
|
|
874
927
|
});
|
|
875
928
|
}
|
|
876
|
-
restoreFocusIfAppropriate() {
|
|
877
|
-
const active = document.activeElement;
|
|
878
|
-
if (active !== document.body && active !== this)
|
|
879
|
-
return;
|
|
880
|
-
requestAnimationFrame(() => {
|
|
881
|
-
var _a, _b;
|
|
882
|
-
(_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.chat-input')) === null || _b === void 0 ? void 0 : _b.focus();
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
929
|
onChatHeaderMouseDown(e) {
|
|
886
930
|
if (this.popoutMode === 'collapse')
|
|
887
931
|
return;
|
|
@@ -951,6 +995,9 @@ __decorate([
|
|
|
951
995
|
__decorate([
|
|
952
996
|
observable
|
|
953
997
|
], FoundationAiAssistant.prototype, "showingSplash", void 0);
|
|
998
|
+
__decorate([
|
|
999
|
+
observable
|
|
1000
|
+
], FoundationAiAssistant.prototype, "isEngaged", void 0);
|
|
954
1001
|
__decorate([
|
|
955
1002
|
observable
|
|
956
1003
|
], FoundationAiAssistant.prototype, "showHalo", void 0);
|
|
@@ -376,33 +376,33 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
376
376
|
`)}
|
|
377
377
|
</div>
|
|
378
378
|
`)}
|
|
379
|
-
|
|
380
379
|
${when((x) => { var _a; return ((_a = x.chatConfig.suggestions) === null || _a === void 0 ? void 0 : _a.behavior) !== 'never'; }, html `
|
|
381
380
|
<chat-suggestions
|
|
382
381
|
:state="${(x) => x.suggestionsState}"
|
|
383
382
|
@suggestion-clicked="${(x, c) => x.handleSuggestionClick(c.event.detail)}"
|
|
384
383
|
></chat-suggestions>
|
|
385
384
|
`)}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
385
|
+
${when((x) => { var _a; return !(x.state === 'loading' && ((_a = x.activeAgent) === null || _a === void 0 ? void 0 : _a.chatInputDuringExecution) === 'hidden'); }, html `
|
|
386
|
+
<div class="input-row" part="input-row">
|
|
387
|
+
${when((x) => { var _a; return !!((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.acceptedFiles); }, html `
|
|
388
|
+
<${buttonTag}
|
|
389
|
+
class="attach-button"
|
|
390
|
+
part="attach-button"
|
|
391
|
+
appearance="stealth"
|
|
392
|
+
title=${(x) => { var _a; return `Attach file (${(_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.acceptedFiles})`; }}
|
|
393
|
+
?disabled=${(x) => x.state === 'loading'}
|
|
394
|
+
@click=${(x) => x.triggerFileInput()}
|
|
395
|
+
><${iconTag} name="paperclip"></${iconTag}></${buttonTag}>
|
|
396
|
+
`)}
|
|
397
|
+
<${textareaTag}
|
|
398
|
+
${ref('chatInputEl')}
|
|
399
|
+
class="chat-input"
|
|
400
|
+
part="input"
|
|
401
|
+
placeholder=${(x) => x.placeholder}
|
|
402
|
+
:value=${(x) => x.inputValue}
|
|
394
403
|
?disabled=${(x) => x.state === 'loading'}
|
|
395
|
-
@
|
|
396
|
-
|
|
397
|
-
`)}
|
|
398
|
-
<${textareaTag}
|
|
399
|
-
class="chat-input"
|
|
400
|
-
part="input"
|
|
401
|
-
placeholder=${(x) => x.placeholder}
|
|
402
|
-
:value=${(x) => x.inputValue}
|
|
403
|
-
?disabled=${(x) => x.state === 'loading'}
|
|
404
|
-
@input=${(x, c) => (x.inputValue = c.event.target.value)}
|
|
405
|
-
@keydown=${(x, c) => {
|
|
404
|
+
@input=${(x, c) => (x.inputValue = c.event.target.value)}
|
|
405
|
+
@keydown=${(x, c) => {
|
|
406
406
|
if (c.event.key === 'Enter' &&
|
|
407
407
|
!c.event.shiftKey) {
|
|
408
408
|
c.event.preventDefault();
|
|
@@ -411,18 +411,19 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
411
411
|
}
|
|
412
412
|
return true;
|
|
413
413
|
}}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
414
|
+
></${textareaTag}>
|
|
415
|
+
<${buttonTag}
|
|
416
|
+
class="send-button"
|
|
417
|
+
part="send-button"
|
|
418
|
+
?disabled=${(x) => x.state === 'loading' || (!x.inputValue.trim() && !x.attachments.length)}
|
|
419
|
+
@click=${(x) => x.handleSendClick()}
|
|
420
|
+
>Send</${buttonTag}>
|
|
421
|
+
</div>
|
|
422
|
+
`)}
|
|
422
423
|
<ai-halo-overlay
|
|
423
424
|
part="halo-overlay"
|
|
424
425
|
?active=${(x) => x.showHalo !== 'no' && x.enabledAnimations.includes('halo')}
|
|
425
|
-
:speed="${(x) =>
|
|
426
|
+
:speed="${(x) => x.showHalo === 'orchestrating' ? HALO_SPEED_ORCHESTRATING : HALO_SPEED_DEFAULT}"
|
|
426
427
|
direction="${(x) => (x.showHalo === 'orchestrating' ? 'ccw' : 'cw')}"
|
|
427
428
|
:borderSize="${(x) => (x.showHalo === 'orchestrating' ? 2 : HALO_BORDER_SIZE_DEFAULT)}"
|
|
428
429
|
></ai-halo-overlay>
|
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.432.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.432.0",
|
|
68
|
+
"@genesislcap/genx": "14.432.0",
|
|
69
|
+
"@genesislcap/rollup-builder": "14.432.0",
|
|
70
|
+
"@genesislcap/ts-builder": "14.432.0",
|
|
71
|
+
"@genesislcap/uvu-playwright-builder": "14.432.0",
|
|
72
|
+
"@genesislcap/vite-builder": "14.432.0",
|
|
73
|
+
"@genesislcap/webpack-builder": "14.432.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.432.0",
|
|
79
|
+
"@genesislcap/foundation-logger": "14.432.0",
|
|
80
|
+
"@genesislcap/foundation-redux": "14.432.0",
|
|
81
|
+
"@genesislcap/foundation-ui": "14.432.0",
|
|
82
|
+
"@genesislcap/foundation-utils": "14.432.0",
|
|
83
|
+
"@genesislcap/rapid-design-system": "14.432.0",
|
|
84
|
+
"@genesislcap/web-core": "14.432.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": "60d929edef90648c5566f3721748aa7dfada8030"
|
|
97
97
|
}
|
|
@@ -72,6 +72,12 @@ export class AiChatInteractionWrapper extends GenesisElement {
|
|
|
72
72
|
element.data = this.data;
|
|
73
73
|
}
|
|
74
74
|
element.resolved = this.resolved;
|
|
75
|
+
// Pierce the wrapper's shadow boundary to read engagement from the
|
|
76
|
+
// assistant host. False if the widget is already resolved — re-renders
|
|
77
|
+
// from history (layout remount, dock/undock) must never steal focus.
|
|
78
|
+
const root = this.getRootNode();
|
|
79
|
+
const host = root instanceof ShadowRoot ? (root.host as { isEngaged?: boolean }) : null;
|
|
80
|
+
element.shouldAutoFocus = !this.resolved && !!host?.isEngaged;
|
|
75
81
|
|
|
76
82
|
// Handle completion from the inner component.
|
|
77
83
|
// Guard against re-emission if the interaction was already resolved
|
package/src/config/config.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { ChatMessage, ChatToolDefinition, ChatToolHandlers } from '@genesislcap/foundation-ai';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Controls how the main chat input area behaves while this agent is executing
|
|
5
|
+
* (i.e. while `state === 'loading'` — covers both LLM thinking and pending
|
|
6
|
+
* widget interactions).
|
|
7
|
+
*
|
|
8
|
+
* - `'disabled'` (default): input stays visible but disabled.
|
|
9
|
+
* - `'hidden'`: the entire input row (attach button, textarea, send button) is
|
|
10
|
+
* hidden. Useful for agents whose interaction widgets contain their own
|
|
11
|
+
* input affordances and where a second disabled chat input is redundant or
|
|
12
|
+
* confusing — typically long-running agents (planners) where one transition
|
|
13
|
+
* in/out is less jarring than mid-run flicker.
|
|
14
|
+
*
|
|
15
|
+
* @beta
|
|
16
|
+
*/
|
|
17
|
+
export type ChatInputDuringExecutionMode = 'disabled' | 'hidden';
|
|
18
|
+
|
|
3
19
|
interface BaseAgentConfig {
|
|
4
20
|
/**
|
|
5
21
|
* Display name shown in the chat header when this agent is active.
|
|
@@ -26,6 +42,11 @@ interface BaseAgentConfig {
|
|
|
26
42
|
* Sub-agents available to this agent's tool handlers via `requestSubAgent`.
|
|
27
43
|
*/
|
|
28
44
|
subAgents?: AgentConfig[];
|
|
45
|
+
/**
|
|
46
|
+
* How the main chat input area behaves while this agent is executing.
|
|
47
|
+
* Defaults to `'disabled'`. See {@link ChatInputDuringExecutionMode}.
|
|
48
|
+
*/
|
|
49
|
+
chatInputDuringExecution?: ChatInputDuringExecutionMode;
|
|
29
50
|
}
|
|
30
51
|
|
|
31
52
|
/**
|
|
@@ -499,7 +499,6 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
499
499
|
</div>
|
|
500
500
|
`,
|
|
501
501
|
)}
|
|
502
|
-
|
|
503
502
|
${when(
|
|
504
503
|
(x) => x.chatConfig.suggestions?.behavior !== 'never',
|
|
505
504
|
html<FoundationAiAssistant>`
|
|
@@ -510,51 +509,57 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
510
509
|
></chat-suggestions>
|
|
511
510
|
`,
|
|
512
511
|
)}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
512
|
+
${when(
|
|
513
|
+
(x) => !(x.state === 'loading' && x.activeAgent?.chatInputDuringExecution === 'hidden'),
|
|
514
|
+
html<FoundationAiAssistant>`
|
|
515
|
+
<div class="input-row" part="input-row">
|
|
516
|
+
${when(
|
|
517
|
+
(x) => !!x.chatConfig.ui?.acceptedFiles,
|
|
518
|
+
html<FoundationAiAssistant>`
|
|
519
|
+
<${buttonTag}
|
|
520
|
+
class="attach-button"
|
|
521
|
+
part="attach-button"
|
|
522
|
+
appearance="stealth"
|
|
523
|
+
title=${(x) => `Attach file (${x.chatConfig.ui?.acceptedFiles})`}
|
|
524
|
+
?disabled=${(x) => x.state === 'loading'}
|
|
525
|
+
@click=${(x) => x.triggerFileInput()}
|
|
526
|
+
><${iconTag} name="paperclip"></${iconTag}></${buttonTag}>
|
|
527
|
+
`,
|
|
528
|
+
)}
|
|
529
|
+
<${textareaTag}
|
|
530
|
+
${ref('chatInputEl')}
|
|
531
|
+
class="chat-input"
|
|
532
|
+
part="input"
|
|
533
|
+
placeholder=${(x) => x.placeholder}
|
|
534
|
+
:value=${(x) => x.inputValue}
|
|
523
535
|
?disabled=${(x) => x.state === 'loading'}
|
|
524
|
-
@
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
x.handleSendClick()
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
></${textareaTag}>
|
|
547
|
-
<${buttonTag}
|
|
548
|
-
class="send-button"
|
|
549
|
-
part="send-button"
|
|
550
|
-
?disabled=${(x) => x.state === 'loading' || (!x.inputValue.trim() && !x.attachments.length)}
|
|
551
|
-
@click=${(x) => x.handleSendClick()}
|
|
552
|
-
>Send</${buttonTag}>
|
|
553
|
-
</div>
|
|
536
|
+
@input=${(x, c) => (x.inputValue = (c.event.target as any).value)}
|
|
537
|
+
@keydown=${(x, c) => {
|
|
538
|
+
if (
|
|
539
|
+
(c.event as KeyboardEvent).key === 'Enter' &&
|
|
540
|
+
!(c.event as KeyboardEvent).shiftKey
|
|
541
|
+
) {
|
|
542
|
+
c.event.preventDefault();
|
|
543
|
+
x.handleSendClick();
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
return true;
|
|
547
|
+
}}
|
|
548
|
+
></${textareaTag}>
|
|
549
|
+
<${buttonTag}
|
|
550
|
+
class="send-button"
|
|
551
|
+
part="send-button"
|
|
552
|
+
?disabled=${(x) => x.state === 'loading' || (!x.inputValue.trim() && !x.attachments.length)}
|
|
553
|
+
@click=${(x) => x.handleSendClick()}
|
|
554
|
+
>Send</${buttonTag}>
|
|
555
|
+
</div>
|
|
556
|
+
`,
|
|
557
|
+
)}
|
|
554
558
|
<ai-halo-overlay
|
|
555
559
|
part="halo-overlay"
|
|
556
560
|
?active=${(x) => x.showHalo !== 'no' && x.enabledAnimations.includes('halo')}
|
|
557
|
-
:speed="${(x) =>
|
|
561
|
+
:speed="${(x) =>
|
|
562
|
+
x.showHalo === 'orchestrating' ? HALO_SPEED_ORCHESTRATING : HALO_SPEED_DEFAULT}"
|
|
558
563
|
direction="${(x) => (x.showHalo === 'orchestrating' ? 'ccw' : 'cw')}"
|
|
559
564
|
:borderSize="${(x) => (x.showHalo === 'orchestrating' ? 2 : HALO_BORDER_SIZE_DEFAULT)}"
|
|
560
565
|
></ai-halo-overlay>
|
package/src/main/main.ts
CHANGED
|
@@ -145,8 +145,32 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
145
145
|
return this._sessionRef?.store.aiAssistant.state ?? 'idle';
|
|
146
146
|
}
|
|
147
147
|
set state(value: AiAssistantState) {
|
|
148
|
+
const prev = this._sessionRef?.store.aiAssistant.state;
|
|
148
149
|
this._sessionRef?.actions.aiAssistant.setState(value);
|
|
149
150
|
this.syncShowHalo();
|
|
151
|
+
// When the agent finishes (loading → !loading) the input row reappears (or
|
|
152
|
+
// becomes enabled) — refocus it so the user can type immediately, but only
|
|
153
|
+
// if they haven't moved on to something else on the page.
|
|
154
|
+
if (prev === 'loading' && value !== 'loading') {
|
|
155
|
+
this.maybeAutoFocusChatInput();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Focus the main chat input if the user is still engaged with the assistant.
|
|
161
|
+
* Deferred to the next frame so any conditional re-render (e.g. unhiding the
|
|
162
|
+
* input row when an agent's `chatInputDuringExecution: 'hidden'` execution
|
|
163
|
+
* ends) has committed before we call `.focus()`.
|
|
164
|
+
*
|
|
165
|
+
* `rapid-text-area` does not delegate focus to its inner native `<textarea>`,
|
|
166
|
+
* so we drill into its shadow root and focus the textarea directly.
|
|
167
|
+
*/
|
|
168
|
+
private maybeAutoFocusChatInput() {
|
|
169
|
+
if (!this.isEngaged) return;
|
|
170
|
+
requestAnimationFrame(() => {
|
|
171
|
+
if (!this.isEngaged || !this.isConnected) return;
|
|
172
|
+
this.chatInputEl?.shadowRoot?.querySelector<HTMLTextAreaElement>('textarea')?.focus();
|
|
173
|
+
});
|
|
150
174
|
}
|
|
151
175
|
|
|
152
176
|
get activeAgent(): Omit<AgentConfig, 'toolHandlers'> | undefined {
|
|
@@ -257,6 +281,20 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
257
281
|
private _driverAgentsKey?: string;
|
|
258
282
|
/** Bound to the messages container via the template ref — assigned by FAST before connectedCallback logic runs. */
|
|
259
283
|
messagesEl?: HTMLElement;
|
|
284
|
+
/** Bound to the rapid-text-area chat input via the template ref. */
|
|
285
|
+
chatInputEl?: HTMLElement;
|
|
286
|
+
/**
|
|
287
|
+
* True when the user's last interaction (pointer or keyboard focus) was inside
|
|
288
|
+
* the assistant. Defaults to true so the assistant claims focus on first mount.
|
|
289
|
+
* Auto-focus features (chat input on idle, widget inputs on mount) gate on this
|
|
290
|
+
* to avoid stealing focus from elsewhere on the page.
|
|
291
|
+
*/
|
|
292
|
+
@observable isEngaged: boolean = true;
|
|
293
|
+
private _handleGlobalInteraction = (e: Event) => {
|
|
294
|
+
const next = e.composedPath().includes(this);
|
|
295
|
+
if (next === this.isEngaged) return;
|
|
296
|
+
this.isEngaged = next;
|
|
297
|
+
};
|
|
260
298
|
/** True when the user has intentionally scrolled away from the bottom — suppresses auto-scroll. */
|
|
261
299
|
private _userScrolledAway = false;
|
|
262
300
|
private _scrollListener?: () => void;
|
|
@@ -553,6 +591,7 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
553
591
|
this.stopLoadingTimer();
|
|
554
592
|
this.syncShowHalo();
|
|
555
593
|
this.fetchSuggestions();
|
|
594
|
+
this.maybeAutoFocusChatInput();
|
|
556
595
|
this._executionCompletionUnsub?.();
|
|
557
596
|
this._executionCompletionUnsub = undefined;
|
|
558
597
|
}
|
|
@@ -569,6 +608,13 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
569
608
|
};
|
|
570
609
|
this.messagesEl.addEventListener('scroll', this._scrollListener);
|
|
571
610
|
}
|
|
611
|
+
// Track whether the user's most recent interaction was inside this assistant.
|
|
612
|
+
// Capture phase + composedPath() pierces shadow boundaries reliably.
|
|
613
|
+
document.addEventListener('pointerdown', this._handleGlobalInteraction, true);
|
|
614
|
+
document.addEventListener('focusin', this._handleGlobalInteraction, true);
|
|
615
|
+
// Initial focus on mount when idle (skips during a mid-lifecycle reattach
|
|
616
|
+
// where state is loading and the input may be hidden or disabled).
|
|
617
|
+
if (this.state !== 'loading') this.maybeAutoFocusChatInput();
|
|
572
618
|
logger.debug('FoundationAiAssistant connected');
|
|
573
619
|
}
|
|
574
620
|
|
|
@@ -586,6 +632,8 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
586
632
|
}
|
|
587
633
|
this._scrollListener = undefined;
|
|
588
634
|
this._userScrolledAway = false;
|
|
635
|
+
document.removeEventListener('pointerdown', this._handleGlobalInteraction, true);
|
|
636
|
+
document.removeEventListener('focusin', this._handleGlobalInteraction, true);
|
|
589
637
|
// Clear local references only — driver and store stay in their registries.
|
|
590
638
|
this.driver = undefined;
|
|
591
639
|
this._sessionRef = undefined;
|
|
@@ -972,18 +1020,13 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
972
1020
|
capturedSessionRef?.actions.aiAssistant.setState('idle');
|
|
973
1021
|
this.syncShowHalo();
|
|
974
1022
|
this.fetchSuggestions();
|
|
975
|
-
|
|
1023
|
+
// setState was dispatched directly via the captured ref (not via the
|
|
1024
|
+
// state setter) so the setter's auto-focus path doesn't fire here. Call
|
|
1025
|
+
// it explicitly so the chat input refocuses after the run completes.
|
|
1026
|
+
this.maybeAutoFocusChatInput();
|
|
976
1027
|
}
|
|
977
1028
|
}
|
|
978
1029
|
|
|
979
|
-
private restoreFocusIfAppropriate() {
|
|
980
|
-
const active = document.activeElement;
|
|
981
|
-
if (active !== document.body && active !== this) return;
|
|
982
|
-
requestAnimationFrame(() => {
|
|
983
|
-
this.shadowRoot?.querySelector<HTMLElement>('.chat-input')?.focus();
|
|
984
|
-
});
|
|
985
|
-
}
|
|
986
|
-
|
|
987
1030
|
onChatHeaderMouseDown(e: MouseEvent) {
|
|
988
1031
|
if (this.popoutMode === 'collapse') return;
|
|
989
1032
|
e.preventDefault();
|
|
@@ -2,7 +2,8 @@ import type { InteractionResult } from '@genesislcap/foundation-ai';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Interface that AI inline interaction components can implement.
|
|
5
|
-
* The `AiChatInteractionWrapper` always sets `data
|
|
5
|
+
* The `AiChatInteractionWrapper` always sets `data`, `resolved`, and
|
|
6
|
+
* `shouldAutoFocus` on the rendered element.
|
|
6
7
|
*
|
|
7
8
|
* @beta
|
|
8
9
|
*/
|
|
@@ -10,4 +11,14 @@ export interface AiChatWidget {
|
|
|
10
11
|
data: unknown;
|
|
11
12
|
/** The result when the interaction has been resolved. Truthy = resolved; value contains status/payload for display. */
|
|
12
13
|
resolved?: InteractionResult<unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* True when the widget is being mounted fresh and the assistant currently has
|
|
16
|
+
* the user's engagement (last pointer/focus interaction was inside the
|
|
17
|
+
* assistant). Widgets with input fields should use this to decide whether to
|
|
18
|
+
* call `.focus()` on mount. False when:
|
|
19
|
+
* - the user has interacted with something outside the assistant, or
|
|
20
|
+
* - this widget has already been resolved (we're re-rendering history due
|
|
21
|
+
* to a layout-lifecycle remount, dock/undock, etc.).
|
|
22
|
+
*/
|
|
23
|
+
shouldAutoFocus?: boolean;
|
|
13
24
|
}
|