[eric] settings-agent: select-and-send Settings rows -> selected_setting_ids (new settings-option element type, Interface rows selectable, full send plumbing)

This commit is contained in:
ciregenz
2026-06-19 03:10:31 -07:00
parent a0af8476c2
commit 0441e8f127
7 changed files with 27 additions and 17 deletions
@@ -10,7 +10,7 @@ export interface SelectedElement {
computedStyles: Record<string, string>;
screenshot?: string;
boundingRect: { x: number; y: number; width: number; height: number };
semanticType?: 'agent-card' | 'message' | 'tool-call' | 'tool-group' | 'view-card' | 'browser-card' | 'dom-element';
semanticType?: 'agent-card' | 'message' | 'tool-call' | 'tool-group' | 'view-card' | 'browser-card' | 'settings-option' | 'dom-element';
semanticLabel?: string;
semanticData?: Record<string, any>;
}
@@ -6,7 +6,7 @@ const SELECT_ATTR = 'data-select-type';
const SELECT_ID_ATTR = 'data-select-id';
const SELECT_META_ATTR = 'data-select-meta';
const DRAG_SELECT_TYPES = ['agent-card', 'view-card', 'browser-card'] as const;
const DRAG_SELECT_TYPES = ['agent-card', 'view-card', 'browser-card', 'settings-option'] as const;
const DRAG_SELECTOR = DRAG_SELECT_TYPES.map((t) => `[${SELECT_ATTR}="${t}"]`).join(',');
export interface OverlayState {
@@ -38,6 +38,7 @@ const SEMANTIC_LABELS: Record<string, string> = {
'tool-group': 'Tool Group',
'view-card': 'View',
'browser-card': 'Browser',
'settings-option': 'Setting',
};
function findSelectableAncestor(target: Element, excludeId?: string | null): Element | null {
@@ -232,6 +232,7 @@ interface QueuedMessage {
attachedSkills?: Array<{ id: string; name: string; content: string }>;
selectedBrowserIds?: string[];
selectedAppIds?: string[];
selectedSettingIds?: string[];
}
interface AgentChatProps {
@@ -407,7 +408,7 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
// the instant you send (looked like "the chat quit when I clicked an option").
if (session?.dashboard_id) config.dashboard_id = session.dashboard_id;
dispatch(
launchAndSendFirstMessage({ draftId: id, config, prompt: msg.prompt, mode, model, images: msg.images, contextPaths: msg.contextPaths, forcedTools: msg.forcedTools, attachedSkills: msg.attachedSkills, selectedBrowserIds: msg.selectedBrowserIds, selectedAppIds: msg.selectedAppIds })
launchAndSendFirstMessage({ draftId: id, config, prompt: msg.prompt, mode, model, images: msg.images, contextPaths: msg.contextPaths, forcedTools: msg.forcedTools, attachedSkills: msg.attachedSkills, selectedBrowserIds: msg.selectedBrowserIds, selectedAppIds: msg.selectedAppIds, selectedSettingIds: msg.selectedSettingIds })
).then((action) => {
if (launchAndSendFirstMessage.fulfilled.match(action)) {
const realId = action.payload.session.id;
@@ -421,7 +422,7 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
if (msg.selectedBrowserIds?.length) {
dispatch(setGlowingBrowserCards({ browserIds: msg.selectedBrowserIds, sessionId: id, label: 'Use Browser' }));
}
dispatch(sendMessageThunk({ sessionId: id, prompt: msg.prompt, mode, model, images: msg.images, contextPaths: msg.contextPaths, forcedTools: msg.forcedTools, attachedSkills: msg.attachedSkills, selectedBrowserIds: msg.selectedBrowserIds, selectedAppIds: msg.selectedAppIds }))
dispatch(sendMessageThunk({ sessionId: id, prompt: msg.prompt, mode, model, images: msg.images, contextPaths: msg.contextPaths, forcedTools: msg.forcedTools, attachedSkills: msg.attachedSkills, selectedBrowserIds: msg.selectedBrowserIds, selectedAppIds: msg.selectedAppIds, selectedSettingIds: msg.selectedSettingIds }))
.then((action) => {
if (sendMessageThunk.rejected.match(action)) {
setAwaitingResponse(false);
@@ -850,10 +851,11 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
attachedSkills?: Array<{ id: string; name: string; content: string }>,
selectedBrowserIds?: string[],
selectedAppIds?: string[],
selectedSettingIds?: string[],
) => {
if (!id) return;
scrollToBottom();
const msg: QueuedMessage = { prompt, images, contextPaths, forcedTools, attachedSkills, selectedBrowserIds, selectedAppIds };
const msg: QueuedMessage = { prompt, images, contextPaths, forcedTools, attachedSkills, selectedBrowserIds, selectedAppIds, selectedSettingIds };
if (agentBusy) {
messageQueueRef.current.push(msg);
setQueueLength(messageQueueRef.current.length);
@@ -24,7 +24,7 @@ export type { AttachedImage, ForcedToolGroup, ChatInputHandle };
export type { AttachedSkill } from '@/app/components/editor/richEditorUtils';
interface Props {
onSend: (message: string, images?: Array<{ data: string; media_type: string }>, contextPaths?: ContextPath[], forcedTools?: string[], attachedSkills?: Array<{ id: string; name: string; content: string }>, selectedBrowserIds?: string[], selectedAppIds?: string[]) => void;
onSend: (message: string, images?: Array<{ data: string; media_type: string }>, contextPaths?: ContextPath[], forcedTools?: string[], attachedSkills?: Array<{ id: string; name: string; content: string }>, selectedBrowserIds?: string[], selectedAppIds?: string[], selectedSettingIds?: string[]) => void;
disabled?: boolean;
mode: string;
onModeChange: (mode: string) => void;
@@ -263,6 +263,9 @@ const ChatInput = forwardRef<ChatInputHandle, Props>(({ onSend, disabled, mode,
const appIds = selectedEls
.filter((el) => el.semanticType === 'view-card' && el.semanticData?.selectId)
.map((el) => el.semanticData!.selectId as string);
const settingIds = selectedEls
.filter((el) => el.semanticType === 'settings-option' && el.semanticData?.selectId)
.map((el) => el.semanticData!.selectId as string);
onSend(
trimmed,
sendImages,
@@ -271,6 +274,7 @@ const ChatInput = forwardRef<ChatInputHandle, Props>(({ onSend, disabled, mode,
sendSkills,
browserIds.length > 0 ? browserIds : undefined,
appIds.length > 0 ? appIds : undefined,
settingIds.length > 0 ? settingIds : undefined,
);
if (editor.tagName === 'TEXTAREA') (editor as unknown as HTMLTextAreaElement).value = ''; else editor.innerHTML = '';
deleteDraft(ownerId);
@@ -98,6 +98,7 @@ export function appendSelectedElements(trimmed: string, selectedEls: SelectedEle
'tool-group': 'Tool Group',
'view-card': 'App Card',
'browser-card': 'Browser Card',
'settings-option': 'Setting',
'dom-element': 'Element',
}[el.semanticType] || el.semanticType;
lines.push(`${i + 1}. [${typeLabel}] ${el.semanticLabel || ''}`);
@@ -27,7 +27,7 @@ const GeneralInterface: React.FC<{
<>
<Typography sx={{ ...sectionSx, mt: 3 }}>Interface</Typography>
<Box sx={inlineRowSx}>
<Box sx={inlineRowSx} data-select-type="settings-option" data-select-id="theme" data-select-meta={JSON.stringify({ name: 'Theme', category: 'Interface', fieldName: 'theme', description: 'Application color scheme.' })}>
<Box sx={{ mr: 3 }}>
<Typography sx={labelSx}>Theme</Typography>
<Typography sx={descSx}>Application color scheme.</Typography>
@@ -64,7 +64,7 @@ const GeneralInterface: React.FC<{
</ToggleButtonGroup>
</Box>
<Box sx={rowSx}>
<Box sx={rowSx} data-select-type="settings-option" data-select-id="zoom_sensitivity" data-select-meta={JSON.stringify({ name: 'Zoom sensitivity', category: 'Interface', fieldName: 'zoom_sensitivity', description: 'Scroll-to-zoom responsiveness.' })}>
<Typography sx={labelSx}>Zoom sensitivity</Typography>
<Typography sx={{ ...descSx, mb: 1 }}>
Scroll-to-zoom responsiveness. Lower for trackpads, higher for mouse wheels.
@@ -91,7 +91,7 @@ const GeneralInterface: React.FC<{
</Box>
</Box>
<Box sx={inlineRowSx}>
<Box sx={inlineRowSx} data-select-type="settings-option" data-select-id="new_agent_shortcut" data-select-meta={JSON.stringify({ name: 'New agent shortcut', category: 'Interface', fieldName: 'new_agent_shortcut', description: 'Keyboard shortcut to create an agent.' })}>
<Box sx={{ mr: 3 }}>
<Typography sx={labelSx}>New agent shortcut</Typography>
<Typography sx={descSx}>Keyboard shortcut to create an agent.</Typography>
@@ -149,7 +149,7 @@ const GeneralInterface: React.FC<{
</Box>
</Box>
<Box sx={inlineRowSx}>
<Box sx={inlineRowSx} data-select-type="settings-option" data-select-id="auto_select_mode_on_new_agent" data-select-meta={JSON.stringify({ name: 'Auto-enable element selection', category: 'Interface', fieldName: 'auto_select_mode_on_new_agent', description: 'Enter element selection mode when creating a new agent.' })}>
<Box sx={{ mr: 3 }}>
<Typography sx={labelSx}>Auto-enable element selection</Typography>
<Typography sx={descSx}>Automatically enter element selection mode when creating a new agent.</Typography>
@@ -164,7 +164,7 @@ const GeneralInterface: React.FC<{
/>
</Box>
<Box sx={inlineRowSx}>
<Box sx={inlineRowSx} data-select-type="settings-option" data-select-id="expand_new_chats_in_dashboard" data-select-meta={JSON.stringify({ name: 'Default agent spawn state in dashboard', category: 'Interface', fieldName: 'expand_new_chats_in_dashboard', description: 'New agents spawn expanded instead of collapsed.' })}>
<Box sx={{ mr: 3 }}>
<Typography sx={labelSx}>Default agent spawn state in dashboard</Typography>
<Typography sx={descSx}>When enabled, new agents spawn expanded instead of collapsed.</Typography>
@@ -179,7 +179,7 @@ const GeneralInterface: React.FC<{
/>
</Box>
<Box sx={inlineRowLastSx}>
<Box sx={inlineRowLastSx} data-select-type="settings-option" data-select-id="auto_reveal_sub_agents" data-select-meta={JSON.stringify({ name: 'Auto-reveal sub-agents on dashboard', category: 'Interface', fieldName: 'auto_reveal_sub_agents', description: 'Show sub-agent cards tethered to their parent on the dashboard.' })}>
<Box sx={{ mr: 3 }}>
<Typography sx={labelSx}>Auto-reveal sub-agents on dashboard</Typography>
<Typography sx={descSx}>Automatically show sub-agent cards (from CreateAgent / InvokeAgent) tethered to their parent on the dashboard.</Typography>
@@ -196,7 +196,7 @@ const GeneralInterface: React.FC<{
<Typography sx={{ ...sectionSx, mt: 3 }}>Browser</Typography>
<Box sx={rowLastSx}>
<Box sx={rowLastSx} data-select-type="settings-option" data-select-id="browser_homepage" data-select-meta={JSON.stringify({ name: 'Default homepage', category: 'Browser', fieldName: 'browser_homepage', description: 'URL loaded when opening a new browser card.' })}>
<Typography sx={labelSx}>Default homepage</Typography>
<Typography sx={{ ...descSx, mb: 1.5 }}>
URL loaded when opening a new browser card on the dashboard.
+6 -4
View File
@@ -205,6 +205,7 @@ export interface SendMessagePayload {
hidden?: boolean;
selectedBrowserIds?: string[];
selectedAppIds?: string[];
selectedSettingIds?: string[];
}
function _genOptimisticId(): string {
@@ -213,7 +214,7 @@ function _genOptimisticId(): string {
export const sendMessage = createAsyncThunk(
'agents/sendMessage',
async ({ sessionId, prompt, mode, model, provider, images, contextPaths, forcedTools, attachedSkills, hidden, selectedBrowserIds, selectedAppIds }: SendMessagePayload, { dispatch }) => {
async ({ sessionId, prompt, mode, model, provider, images, contextPaths, forcedTools, attachedSkills, hidden, selectedBrowserIds, selectedAppIds, selectedSettingIds }: SendMessagePayload, { dispatch }) => {
// Mint client id and dispatch optimistic bubble before awaiting the network; id round-trips for echo dedupe.
const clientMessageId = _genOptimisticId();
dispatch(addOptimisticMessage({
@@ -230,7 +231,7 @@ export const sendMessage = createAsyncThunk(
const res = await fetch(`${AGENTS_API}/sessions/${sessionId}/message`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt, mode, model, provider, images, context_paths: contextPaths, forced_tools: forcedTools, attached_skills: attachedSkills, hidden, selected_browser_ids: selectedBrowserIds, selected_app_output_ids: selectedAppIds, client_message_id: clientMessageId }),
body: JSON.stringify({ prompt, mode, model, provider, images, context_paths: contextPaths, forced_tools: forcedTools, attached_skills: attachedSkills, hidden, selected_browser_ids: selectedBrowserIds, selected_app_output_ids: selectedAppIds, selected_setting_ids: selectedSettingIds, client_message_id: clientMessageId }),
});
if (!res.ok) throw new Error(`send failed: ${res.status}`);
} catch (err) {
@@ -310,6 +311,7 @@ export interface LaunchAndSendPayload {
expand?: boolean;
selectedBrowserIds?: string[];
selectedAppIds?: string[];
selectedSettingIds?: string[];
}
export const fetchSession = createAsyncThunk(
@@ -333,7 +335,7 @@ export const fetchSession = createAsyncThunk(
export const launchAndSendFirstMessage = createAsyncThunk(
'agents/launchAndSendFirstMessage',
async ({ draftId, config, prompt, mode, model, provider, images, contextPaths, forcedTools, attachedSkills, selectedBrowserIds, selectedAppIds }: LaunchAndSendPayload) => {
async ({ draftId, config, prompt, mode, model, provider, images, contextPaths, forcedTools, attachedSkills, selectedBrowserIds, selectedAppIds, selectedSettingIds }: LaunchAndSendPayload) => {
const launchRes = await fetch(`${AGENTS_API}/launch`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -345,7 +347,7 @@ export const launchAndSendFirstMessage = createAsyncThunk(
await fetch(`${AGENTS_API}/sessions/${session.id}/message`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt, mode, model, provider, images, context_paths: contextPaths, forced_tools: forcedTools, attached_skills: attachedSkills, selected_browser_ids: selectedBrowserIds, selected_app_output_ids: selectedAppIds }),
body: JSON.stringify({ prompt, mode, model, provider, images, context_paths: contextPaths, forced_tools: forcedTools, attached_skills: attachedSkills, selected_browser_ids: selectedBrowserIds, selected_app_output_ids: selectedAppIds, selected_setting_ids: selectedSettingIds }),
});
const refreshRes = await fetch(`${AGENTS_API}/sessions/${session.id}`);