diff --git a/frontend/src/app/components/editor/ElementSelectionContext.tsx b/frontend/src/app/components/editor/ElementSelectionContext.tsx index d3a77373..41a34888 100644 --- a/frontend/src/app/components/editor/ElementSelectionContext.tsx +++ b/frontend/src/app/components/editor/ElementSelectionContext.tsx @@ -10,7 +10,7 @@ export interface SelectedElement { computedStyles: Record; 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; } diff --git a/frontend/src/app/components/editor/useDomElementSelector.ts b/frontend/src/app/components/editor/useDomElementSelector.ts index bf2df2fe..75a165c2 100644 --- a/frontend/src/app/components/editor/useDomElementSelector.ts +++ b/frontend/src/app/components/editor/useDomElementSelector.ts @@ -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 = { 'tool-group': 'Tool Group', 'view-card': 'View', 'browser-card': 'Browser', + 'settings-option': 'Setting', }; function findSelectableAncestor(target: Element, excludeId?: string | null): Element | null { diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx index a621b423..9c1768c8 100644 --- a/frontend/src/app/pages/AgentChat/AgentChat.tsx +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -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 = ({ 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 = ({ 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 = ({ 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); diff --git a/frontend/src/app/pages/AgentChat/ChatInput.tsx b/frontend/src/app/pages/AgentChat/ChatInput.tsx index ecd3c509..7ee8026b 100644 --- a/frontend/src/app/pages/AgentChat/ChatInput.tsx +++ b/frontend/src/app/pages/AgentChat/ChatInput.tsx @@ -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(({ 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(({ 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); diff --git a/frontend/src/app/pages/AgentChat/ChatInput/sendHelpers.ts b/frontend/src/app/pages/AgentChat/ChatInput/sendHelpers.ts index 34288810..573c8fd2 100644 --- a/frontend/src/app/pages/AgentChat/ChatInput/sendHelpers.ts +++ b/frontend/src/app/pages/AgentChat/ChatInput/sendHelpers.ts @@ -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 || ''}`); diff --git a/frontend/src/app/pages/Settings/sections/general/GeneralInterface.tsx b/frontend/src/app/pages/Settings/sections/general/GeneralInterface.tsx index cae89ff1..bab1a19c 100644 --- a/frontend/src/app/pages/Settings/sections/general/GeneralInterface.tsx +++ b/frontend/src/app/pages/Settings/sections/general/GeneralInterface.tsx @@ -27,7 +27,7 @@ const GeneralInterface: React.FC<{ <> Interface - + Theme Application color scheme. @@ -64,7 +64,7 @@ const GeneralInterface: React.FC<{ - + Zoom sensitivity Scroll-to-zoom responsiveness. Lower for trackpads, higher for mouse wheels. @@ -91,7 +91,7 @@ const GeneralInterface: React.FC<{ - + New agent shortcut Keyboard shortcut to create an agent. @@ -149,7 +149,7 @@ const GeneralInterface: React.FC<{ - + Auto-enable element selection Automatically enter element selection mode when creating a new agent. @@ -164,7 +164,7 @@ const GeneralInterface: React.FC<{ /> - + Default agent spawn state in dashboard When enabled, new agents spawn expanded instead of collapsed. @@ -179,7 +179,7 @@ const GeneralInterface: React.FC<{ /> - + Auto-reveal sub-agents on dashboard Automatically show sub-agent cards (from CreateAgent / InvokeAgent) tethered to their parent on the dashboard. @@ -196,7 +196,7 @@ const GeneralInterface: React.FC<{ Browser - + Default homepage URL loaded when opening a new browser card on the dashboard. diff --git a/frontend/src/shared/state/agentsSlice.ts b/frontend/src/shared/state/agentsSlice.ts index 07a1c4ce..0592abe5 100644 --- a/frontend/src/shared/state/agentsSlice.ts +++ b/frontend/src/shared/state/agentsSlice.ts @@ -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}`);