Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 11 additions & 43 deletions src/web-ui/src/flow_chat/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ import { resolveSessionRelationship } from '../utils/sessionMetadata';
import { isProjectedSessionEmpty } from '../utils/flowChatTurnIdentity';
import {
DEFAULT_CHAT_INPUT_MODE_CONFIG_PATH,
agentExecutionTier,
canSwitchSessionMainAgent,
isChatInputActionVisibleForTarget,
normalizeUserDefaultChatInputModeId,
Expand Down Expand Up @@ -181,9 +180,7 @@ import { useRealtimeVoiceCallActive } from './voice/RealtimeVoiceCallContext';
import { useComposerVoiceInput } from './voice/useComposerVoiceInput';
import { expandWidgetPromptReferenceTokens } from '@/tools/generative-widget/widgetPromptReference';
import {
createAdditionalModePromptReferenceToken,
expandAdditionalModePromptReferenceTokens,
type AdditionalModePromptReferenceId,
} from '../utils/additionalModePromptReference';
import {
composerPresentationContexts,
Expand Down Expand Up @@ -262,15 +259,11 @@ export interface ChatInputProps {
registration?: ChatInputRegistration;
}

type ChatInputAdditionalModeSelection =
| { kind: 'skill'; skillName: string }
| { kind: 'additional-mode'; modeId: AdditionalModePromptReferenceId };

interface ChatInputAdditionalModeItem {
id: string;
label: string;
title: string;
selection: ChatInputAdditionalModeSelection;
skillName: string;
}

type SlashActionItem = {
Expand Down Expand Up @@ -1152,9 +1145,6 @@ export const ChatInput: React.FC<ChatInputProps> = ({
[activeSessionMode, currentMode, isAcpTargetSession, isAssistantWorkspace],
);
const canSwitchModes = chatInputModePolicy.canSwitchModes && !isSubagentInputTarget;
// Other Agents sit on the Standard execution surface. Minimal and Ultimate
// keep their deliberately fixed capability sets.
const showStandardExecutionOptions = agentExecutionTier(currentMode) === 'balanced';
const selectedHarnessProfile = resolveSelectedComposerExecutionLevel({
currentMode,
});
Expand Down Expand Up @@ -1440,12 +1430,10 @@ export const ChatInput: React.FC<ChatInputProps> = ({
: [],
[canUseSkillsForTarget, resolvedModeSkills],
);
const showReviewAdditionalMode = canLaunchReview && canSwitchModes && showStandardExecutionOptions;
const showAdditionalModes = quickSkillShortcuts.length > 0 || showReviewAdditionalMode;
const boostMenuLayoutRevision = [
...quickSkillShortcuts.map(shortcut => shortcut.id),
showReviewAdditionalMode ? 'review' : '',
].filter(Boolean).join('|');
const showAdditionalModes = quickSkillShortcuts.length > 0;
const boostMenuLayoutRevision = quickSkillShortcuts
.map(shortcut => shortcut.id)
.join('|');
const boostMenuLayout = useAnchoredPopoverPosition({
open: modeState.dropdownOpen,
anchorRef: boostTriggerRef,
Expand Down Expand Up @@ -5255,38 +5243,18 @@ export const ChatInput: React.FC<ChatInputProps> = ({
insertInlineReferenceIntoInput(createSkillPromptReferenceToken(skillName));
}, [insertInlineReferenceIntoInput]);

const insertAdditionalModeIntoInput = useCallback((modeId: AdditionalModePromptReferenceId) => {
insertInlineReferenceIntoInput(createAdditionalModePromptReferenceToken(modeId));
}, [insertInlineReferenceIntoInput]);

const additionalModeItems = useMemo<ChatInputAdditionalModeItem[]>(() => [
...quickSkillShortcuts.map(shortcut => ({
id: shortcut.id,
label: shortcut.label,
title: shortcut.skill.description || shortcut.label,
selection: {
kind: 'skill' as const,
skillName: shortcut.skill.name,
},
skillName: shortcut.skill.name,
})),
...(showReviewAdditionalMode
? [{
id: 'review',
label: t('chatInput.agents.review.name'),
title: t('chatInput.agents.review.name'),
selection: { kind: 'additional-mode' as const, modeId: 'review' as const },
}]
: []),
], [quickSkillShortcuts, showReviewAdditionalMode, t]);

const selectAdditionalMode = useCallback((selection: ChatInputAdditionalModeSelection) => {
if (selection.kind === 'skill') {
insertSkillIntoInput(selection.skillName);
return;
}
], [quickSkillShortcuts]);

insertAdditionalModeIntoInput(selection.modeId);
}, [insertAdditionalModeIntoInput, insertSkillIntoInput]);
const selectAdditionalMode = useCallback((skillName: string) => {
insertSkillIntoInput(skillName);
}, [insertSkillIntoInput]);

const handleBoostPickImage = useCallback(
(e: React.MouseEvent) => {
Expand Down Expand Up @@ -6098,7 +6066,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
leading={<Icon name="spark" size="xs" aria-hidden />}
onClick={event => {
event.stopPropagation();
selectAdditionalMode(item.selection);
selectAdditionalMode(item.skillName);
}}
>
{item.label}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,19 +248,17 @@ describe('composer context track layout', () => {
expect(chatInput).toContain('!isMultiLine && executionLevelPolicy.userConfigurable ? (');
});

it('groups additional modes in one second-level menu immediately after Harness', () => {
it('groups quick skill modes in one second-level menu immediately after Harness', () => {
const chatInput = readLocalFile('ChatInput.tsx');
const menuHarnessIndex = chatInput.indexOf('presentation="menu-item"');
const additionalModesIndex = chatInput.indexOf("label={t('chatInput.boostAdditionalModes')}");
const quickSkillsIndex = chatInput.indexOf('quickSkillShortcuts.map(shortcut => (');
const reviewDefinitionIndex = chatInput.indexOf("id: 'review',");
const additionalModeItemsIndex = chatInput.indexOf('additionalModeItems.map(item => (');
const contextIndex = chatInput.indexOf('onClick={handleBoostOpenAtContext}');

expect(menuHarnessIndex).toBeGreaterThan(-1);
expect(additionalModesIndex).toBeGreaterThan(menuHarnessIndex);
expect(quickSkillsIndex).toBeGreaterThan(-1);
expect(reviewDefinitionIndex).toBeGreaterThan(quickSkillsIndex);
expect(additionalModeItemsIndex).toBeGreaterThan(additionalModesIndex);
expect(contextIndex).toBeGreaterThan(additionalModeItemsIndex);
expect(chatInput).toContain('additionalModeItems.map(item => (');
Expand All @@ -275,8 +273,9 @@ describe('composer context track layout', () => {
);
expect(chatInput).toContain('layoutRevision: boostMenuLayoutRevision');
expect(chatInput).toContain('skillName: shortcut.skill.name');
expect(chatInput).toContain('selectAdditionalMode(item.selection)');
expect(chatInput).toContain('insertAdditionalModeIntoInput(selection.modeId)');
expect(chatInput).toContain('selectAdditionalMode(item.skillName)');
expect(chatInput).not.toContain('showReviewAdditionalMode');
expect(chatInput).not.toContain("modeId: 'review' as const");
expect(chatInput).not.toContain("selectSlashCommandAction('review')");
});

Expand Down
Loading