From 4128b96be7c9a71c0d7530554d7d14dedc2c7b7f Mon Sep 17 00:00:00 2001 From: Max Prokopp Date: Tue, 20 Jan 2026 15:41:36 +0100 Subject: [PATCH 01/11] enhanced message pill instead of action pill --- apps/desktop/src/renderer/Canvas.tsx | 14 ++- .../src/renderer/assets/microphone.svg | 10 ++ .../src/renderer/components/ActionPill.css | 52 +++++++- .../src/renderer/components/ActionPill.tsx | 59 ++++++++- .../src/renderer/components/MessagePill.css | 114 ++++++++++++++++++ .../src/renderer/components/MessagePill.tsx | 57 +++++++++ 6 files changed, 293 insertions(+), 13 deletions(-) create mode 100644 apps/desktop/src/renderer/assets/microphone.svg create mode 100644 apps/desktop/src/renderer/components/MessagePill.css create mode 100644 apps/desktop/src/renderer/components/MessagePill.tsx diff --git a/apps/desktop/src/renderer/Canvas.tsx b/apps/desktop/src/renderer/Canvas.tsx index a123bd28..396b5b34 100644 --- a/apps/desktop/src/renderer/Canvas.tsx +++ b/apps/desktop/src/renderer/Canvas.tsx @@ -23,6 +23,7 @@ import './Canvas.css'; import type { AgentNodeData } from '@agent-orchestrator/shared'; import { createDefaultAgentTitle } from '@agent-orchestrator/shared'; import { ActionPill } from './components/ActionPill'; +import { MessagePill } from './components/MessagePill'; import AssistantMessageNode from './components/AssistantMessageNode'; import { type CommandAction, CommandPalette } from './components/CommandPalette'; import ConversationNode from './components/ConversationNode'; @@ -230,15 +231,15 @@ function CanvasFlow() { const nodeAgentId = nodeData?.agentId as string | undefined; if (nodeAgentId === agentId) { - // Add blue border and shadow to matching node (using command palette blue) + // Add turquoise border and shadow to matching node (lighter, more poppy turquoise) const currentStyle = node.style || {}; return { ...node, style: { ...currentStyle, - border: '2px solid #4a9eff', + border: '2px solid #4db8c9', borderRadius: '12px', - boxShadow: '0 0 32px 8px rgba(74, 158, 255, 0.6)', + boxShadow: '0 0 32px 8px rgba(77, 184, 201, 0.5)', }, }; } else { @@ -248,10 +249,10 @@ function CanvasFlow() { string, unknown >; - // Only remove if it's our highlight (check if it's the blue border/shadow) + // Only remove if it's our highlight (check if it's the turquoise border/shadow) if ( - (border as string)?.includes('#4a9eff') || - (boxShadow as string)?.includes('rgba(74, 158, 255') + (border as string)?.includes('#4db8c9') || + (boxShadow as string)?.includes('rgba(77, 184, 201') ) { return { ...node, style: restStyle }; } @@ -2944,6 +2945,7 @@ function CanvasFlow() { )} + {/* Linear Issue Details Modal */} {selectedIssueId && ( diff --git a/apps/desktop/src/renderer/assets/microphone.svg b/apps/desktop/src/renderer/assets/microphone.svg new file mode 100644 index 00000000..63e9bfea --- /dev/null +++ b/apps/desktop/src/renderer/assets/microphone.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/desktop/src/renderer/components/ActionPill.css b/apps/desktop/src/renderer/components/ActionPill.css index cdf330e8..cadb1503 100644 --- a/apps/desktop/src/renderer/components/ActionPill.css +++ b/apps/desktop/src/renderer/components/ActionPill.css @@ -1,3 +1,47 @@ +/* Action Pill Container - Always expanded, light blue-gray, offset up */ +.action-pill { + width: 380px !important; /* 10% narrower than 400px */ + bottom: 46px !important; /* Adjust this value to change Y position (higher = more up, lower = more down) */ + background: #e2edee !important; + border-color: #d0dde0 !important; +} + +.action-pill:not(.square) { + height: 60px !important; + display: flex !important; + align-items: flex-start !important; /* Align content to top */ + justify-content: center !important; /* Center horizontally */ + padding-top: 6px !important; /* Add padding from top */ + } + +.action-pill.cursor-pointer:hover:not(.square) { + background: #d5e5e7 !important; /* Slightly darker on hover */ + border-color: #c5d5d8 !important; + transform: translateX(-50%) scale(1.02); +} + +.action-pill.square { + height: 500px !important; + background: var(--color-bg-primary) !important; /* Restore normal background when expanded */ + border-color: var(--color-border) !important; +} + +.action-pill .pill-text { + color: #358d9d !important; + font-weight: normal; + font-size: 13px !important; + font-family: "SF UI Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + height: auto !important; /* Don't stretch to full height */ + padding-top: 0 !important; /* Remove padding since parent has it */ + display: block !important; /* Change from flex to block so parent flex controls alignment */ + align-items: unset !important; /* Remove flex alignment */ + justify-content: unset !important; /* Remove flex justification */ +} + +.action-pill.square .pill-text { + color: var(--color-text-primary) !important; /* Restore normal text color when expanded */ +} + .action-pill-list { position: absolute; top: 24px; @@ -32,8 +76,8 @@ } .action-pill-card.highlighted { - border: 2px solid #4a9eff; - box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.1); + border: 2px solid #358d9d; + box-shadow: 0 0 0 2px rgba(53, 141, 157, 0.15); } .action-pill-card + .action-pill-card { @@ -91,6 +135,7 @@ border: none; color: #0b1020; font-weight: 600; + font-size: 13px; border-radius: 6px; padding: 6px 12px; cursor: pointer; @@ -116,7 +161,7 @@ .action-pill-command, .action-pill-path { font-family: "Monaco", "Menlo", "Courier New", monospace; - font-size: 12px; + font-size: 13px; color: var(--color-text-secondary); } @@ -131,6 +176,7 @@ border-radius: 6px; padding: 6px 12px; font-weight: 600; + font-size: 13px; cursor: pointer; border: 1px solid transparent; } diff --git a/apps/desktop/src/renderer/components/ActionPill.tsx b/apps/desktop/src/renderer/components/ActionPill.tsx index d2a97429..54585570 100644 --- a/apps/desktop/src/renderer/components/ActionPill.tsx +++ b/apps/desktop/src/renderer/components/ActionPill.tsx @@ -10,6 +10,57 @@ import './ActionPill.css'; const DEFAULT_LABEL = 'Actions pending'; +/** + * Translate tool names to human-readable descriptions + * Based on translations used in AgentChatView + */ +function translateToolName(toolName: string): string { + const lowerName = toolName.toLowerCase(); + + // Shell/command execution + if (lowerName === 'bash' || lowerName === 'shell' || lowerName === 'terminal' || lowerName === 'exec') { + return 'Executing command'; + } + + // File read operations + if (lowerName === 'read' || lowerName === 'cat' || lowerName === 'head' || lowerName === 'tail') { + return 'Read file'; + } + + // File write operations + if (lowerName === 'write' || lowerName === 'write_file' || lowerName === 'edit' || lowerName === 'touch') { + return 'Write file'; + } + + // File search operations (from AgentChatView: "Scanning the code") + if (lowerName === 'grep' || lowerName === 'search') { + return 'Scan code'; + } + + // File gathering (from AgentChatView: "Gathering files") + if (lowerName === 'glob' || lowerName === 'find') { + return 'Gather files'; + } + + // Web operations + if (lowerName.includes('web') || lowerName.includes('fetch') || lowerName.includes('http')) { + return 'Fetch from web'; + } + + // Code intelligence + if (lowerName.includes('lsp') || lowerName === 'definition' || lowerName === 'reference') { + return 'Code analysis'; + } + + // MCP tools + if (lowerName.startsWith('mcp')) { + return 'External tool'; + } + + // Default: return original if no translation found + return toolName; +} + export function ActionPill() { const [actions, setActions] = useState(() => agentActionStore.getAllActions()); const [isExpanded, setIsExpanded] = useState(false); @@ -249,10 +300,10 @@ export function ActionPill() {
{!isSquare ? ( @@ -330,7 +381,7 @@ export function ActionPill() {
{agentLabel}
- {approvalAction.toolName} + {translateToolName(approvalAction.toolName)} {approvalAction.command && ( {approvalAction.command} )} diff --git a/apps/desktop/src/renderer/components/MessagePill.css b/apps/desktop/src/renderer/components/MessagePill.css new file mode 100644 index 00000000..3d98694b --- /dev/null +++ b/apps/desktop/src/renderer/components/MessagePill.css @@ -0,0 +1,114 @@ +/* Message Pill */ +.message-pill { + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + z-index: 30; + background: var(--color-bg-primary); + border: 1px solid var(--color-border); + box-shadow: 0 0 12px rgba(0, 0, 0, 0.2); + border-radius: 30px; + width: 400px; + height: 60px; + overflow: hidden; + transition: all 0.3s ease-in-out; +} + +/* Input Area */ +.message-pill-input-area { + display: flex; + align-items: center; + gap: 8px; + padding: 0 16px; + height: 100%; + background: transparent; +} + +.message-pill-input { + flex: 1; + background: transparent; + border: none; + border-radius: 0; + padding: 10px 16px; + color: var(--color-text-primary); + font-size: 13px; + resize: none; + min-height: 20px; + max-height: 100px; + font-family: inherit; +} + +.message-pill-input:focus { + outline: none; +} + +.message-pill-input:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.message-pill-input::placeholder { + color: var(--text-muted, #888); +} + +.message-pill-mic-button { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + background: transparent; + color: var(--color-text-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + flex-shrink: 0; + transition: color 0.15s; +} + +.message-pill-mic-button:hover { + background: transparent; + color: var(--color-text-secondary); +} + +.message-pill-mic-icon { + width: 16px; + height: 16px; + display: block; + color: inherit; +} + +.message-pill-send-button { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + background: #000000; + color: var(--color-text-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + flex-shrink: 0; + transition: background 0.15s; +} + +.message-pill-send-button:hover:not(:disabled) { + background: #000000; + opacity: 0.8; +} + +.message-pill-send-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.message-pill-send-icon { + font-size: 16px; + line-height: 1; + font-weight: 500; + color: #ffffff; +} diff --git a/apps/desktop/src/renderer/components/MessagePill.tsx b/apps/desktop/src/renderer/components/MessagePill.tsx new file mode 100644 index 00000000..4bc31a4b --- /dev/null +++ b/apps/desktop/src/renderer/components/MessagePill.tsx @@ -0,0 +1,57 @@ +import { useCallback, useState } from 'react'; +import './MessagePill.css'; + +export function MessagePill() { + const [inputValue, setInputValue] = useState(''); + + const handleSend = useCallback(() => { + if (!inputValue.trim()) return; + + // TODO: Implement send functionality + console.log('Sending message:', inputValue); + setInputValue(''); + }, [inputValue]); + + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }, [handleSend]); + + return ( +
+
+