diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 763a622..657f1c3 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -7,7 +7,6 @@ import { ChatPane } from '@renderer/components/ChatPane'; import { CommandPalette } from '@renderer/components/CommandPalette'; import { DiscoveredToolingModal } from '@renderer/components/DiscoveredToolingModal'; import { KeyboardShortcutsPanel } from '@renderer/components/KeyboardShortcutsPanel'; -import { NewSessionModal } from '@renderer/components/NewSessionModal'; import { ProjectSettingsPanel } from '@renderer/components/ProjectSettingsPanel'; import { BookmarksPanel } from '@renderer/components/BookmarksPanel'; import { SessionSearchPanel } from '@renderer/components/SessionSearchPanel'; @@ -39,47 +38,20 @@ import { useTheme, useSidecarCapabilities } from '@renderer/hooks/useAppHooks'; import { buildAvailableModelCatalog, findModel, - normalizePatternModels, + normalizeWorkflowModels, resolveReasoningEffort, } from '@shared/domain/models'; -import { createDefaultToolApprovalPolicy } from '@shared/domain/approval'; import { listPendingDiscoveredMcpServers } from '@shared/domain/discoveredTooling'; -import { syncPatternGraph, type PatternDefinition } from '@shared/domain/pattern'; +import { type ReasoningEffort, type WorkflowDefinition } from '@shared/domain/workflow'; import { isScratchpadProject, SCRATCHPAD_PROJECT_ID } from '@shared/domain/project'; import type { ProjectGitFileReference } from '@shared/domain/project'; import { applySessionModelConfig } from '@shared/domain/session'; import type { AppearanceTheme, LspProfileDefinition, McpServerDefinition } from '@shared/domain/tooling'; -import type { WorkflowDefinition } from '@shared/domain/workflow'; import type { WorkspaceAgentDefinition } from '@shared/domain/workspaceAgent'; import type { WorkspaceState } from '@shared/domain/workspace'; import type { UpdateStatus } from '@shared/contracts/ipc'; import { createId, nowIso } from '@shared/utils/ids'; -function createDraftPattern(defaultModelId: string, defaultReasoningEffort: PatternDefinition['agents'][0]['reasoningEffort']): PatternDefinition { - const timestamp = nowIso(); - return syncPatternGraph({ - id: createId('custom-pattern'), - name: 'New Pattern', - description: '', - mode: 'single', - availability: 'available', - maxIterations: 1, - approvalPolicy: createDefaultToolApprovalPolicy(), - agents: [ - { - id: createId('agent'), - name: 'Primary Agent', - description: 'General-purpose assistant.', - instructions: 'You are a helpful coding assistant working inside the selected project.', - model: defaultModelId, - reasoningEffort: defaultReasoningEffort, - }, - ], - createdAt: timestamp, - updatedAt: timestamp, - }); -} - function createDraftMcpServer(): McpServerDefinition { const timestamp = nowIso(); return { @@ -122,7 +94,7 @@ function createDraftWorkspaceAgent(defaultModelId: string): WorkspaceAgentDefini }; } -function createDraftWorkflow(defaultModelId: string, defaultReasoningEffort: PatternDefinition['agents'][0]['reasoningEffort']): WorkflowDefinition { +function createDraftWorkflow(defaultModelId: string, defaultReasoningEffort?: ReasoningEffort): WorkflowDefinition { const timestamp = nowIso(); const startId = createId('wf-start'); const agentId = createId('wf-agent'); @@ -181,7 +153,6 @@ export default function App() { const [settingsSection, setSettingsSection] = useState(); const [updateStatus, setUpdateStatus] = useState({ state: 'idle' }); const [projectSettingsId, setProjectSettingsId] = useState(); - const [newSessionProjectId, setNewSessionProjectId] = useState(); const [showDiscoveryModal, setShowDiscoveryModal] = useState(false); const [commandPaletteOpen, setCommandPaletteOpen] = useState(false); const [showShortcuts, setShowShortcuts] = useState(false); @@ -286,23 +257,23 @@ export default function App() { () => buildAvailableModelCatalog(sidecarCapabilities?.models), [sidecarCapabilities?.models], ); - const patternForSession = useMemo(() => { + const workflowForSession = useMemo(() => { if (!selectedSession) { return undefined; } - const basePattern = workspace?.patterns.find((pattern) => pattern.id === selectedSession.patternId); - if (!basePattern) { + const baseWorkflow = workspace?.workflows.find((workflow) => workflow.id === selectedSession.workflowId); + if (!baseWorkflow) { return undefined; } - const patternWithSessionConfig = + const workflowWithSessionConfig = projectForSession && selectedSession.sessionModelConfig - ? applySessionModelConfig(basePattern, selectedSession) - : basePattern; + ? applySessionModelConfig(baseWorkflow, selectedSession) + : baseWorkflow; - return normalizePatternModels(patternWithSessionConfig, availableModels); - }, [availableModels, projectForSession, selectedSession, workspace?.patterns]); + return normalizeWorkflowModels(workflowWithSessionConfig, availableModels); + }, [availableModels, projectForSession, selectedSession, workspace?.workflows]); const activityForSession = useMemo( () => (selectedSession ? sessionActivities[selectedSession.id] : undefined), [selectedSession, sessionActivities], @@ -361,8 +332,6 @@ export default function App() { commandPaletteOpenRef.current = commandPaletteOpen; const projectSettingsIdRef = useRef(projectSettingsId); projectSettingsIdRef.current = projectSettingsId; - const newSessionProjectIdRef = useRef(newSessionProjectId); - newSessionProjectIdRef.current = newSessionProjectId; // ── Global keyboard shortcuts ── useEffect(() => { @@ -431,11 +400,6 @@ export default function App() { setShowSettings(false); return; } - if (newSessionProjectIdRef.current) { - e.preventDefault(); - setNewSessionProjectId(undefined); - return; - } // If nothing is open, cancel a running turn on the selected session if (ws) { @@ -460,7 +424,10 @@ export default function App() { ws.selectedProjectId ?? ws.projects.find((p) => !isScratchpadProject(p))?.id; if (defaultProjectId) { - setNewSessionProjectId(defaultProjectId); + const defaultWorkflow = ws.workflows.find((w) => w.isFavorite) ?? ws.workflows[0]; + if (defaultWorkflow) { + void api.createSession({ projectId: defaultProjectId, workflowId: defaultWorkflow.id }); + } } } return; @@ -584,17 +551,17 @@ export default function App() { const handleCreateScratchpad = useCallback(() => { if (!workspace) return; - const singlePatterns = workspace.patterns - .filter((p) => p.mode === 'single' && p.availability !== 'unavailable') + const singleWorkflows = workspace.workflows + .filter((w) => (w.settings.orchestrationMode ?? 'single') === 'single') .sort((a, b) => { if (a.isFavorite && !b.isFavorite) return -1; if (!a.isFavorite && b.isFavorite) return 1; return 0; }); - const defaultPattern = singlePatterns[0]; - if (defaultPattern) { - void api.createSession({ projectId: SCRATCHPAD_PROJECT_ID, patternId: defaultPattern.id }); + const defaultWorkflow = singleWorkflows[0]; + if (defaultWorkflow) { + void api.createSession({ projectId: SCRATCHPAD_PROJECT_ID, workflowId: defaultWorkflow.id }); } }, [api, workspace]); @@ -645,7 +612,7 @@ export default function App() { ); - } else if (selectedSession && patternForSession && projectForSession) { + } else if (selectedSession && workflowForSession && projectForSession) { content = ( api.sendSessionMessage({ @@ -715,7 +682,7 @@ export default function App() { mcpProbingServerIds={workspace.mcpProbingServerIds} onTerminalToggle={handleTerminalToggle} onGitToggle={!isScratchpadProject(selectedSession.projectId) ? handleGitToggle : undefined} - pattern={patternForSession} + workflow={workflowForSession} project={projectForSession} runtimeTools={sidecarCapabilities?.runtimeTools} session={selectedSession} @@ -734,7 +701,7 @@ export default function App() { onDiscard={handleDiscardRunChanges} onJumpToMessage={jumpToMessage} onOpenCommitComposer={handleOpenCommitComposer} - pattern={patternForSession} + workflow={workflowForSession} session={selectedSession} sessionRequestUsage={requestUsageForSession} turnEvents={turnEventsForSession} @@ -765,22 +732,11 @@ export default function App() { onDeleteMcpServer={async (id) => { await api.deleteMcpServer(id); }} - onDeletePattern={async (id) => { - await api.deletePattern(id); - }} onDeleteWorkflow={async (id) => { await api.deleteWorkflow(id); }} onNewLspProfile={createDraftLspProfile} onNewMcpServer={createDraftMcpServer} - onNewPattern={() => { - const defaultModel = availableModels[0] ?? findModel('gpt-5.4', availableModels) ?? findModel('gpt-5.4'); - - return createDraftPattern( - defaultModel?.id ?? 'gpt-5.4', - resolveReasoningEffort(defaultModel, 'high'), - ); - }} onNewWorkflow={() => { const defaultModel = availableModels[0] ?? findModel('gpt-5.4', availableModels) ?? findModel('gpt-5.4'); return createDraftWorkflow( @@ -795,9 +751,6 @@ export default function App() { onSaveMcpServer={async (server) => { await api.saveMcpServer({ server }); }} - onSavePattern={async (pattern) => { - await api.savePattern({ pattern }); - }} onSaveWorkflow={async (workflow) => { await api.saveWorkflow({ workflow }); }} @@ -826,15 +779,11 @@ export default function App() { setSessionActivities({}); setShowSettings(false); }} - patterns={workspace.patterns} workflows={workspace.workflows} workflowTemplates={workspace.workflowTemplates} onCreateWorkflowFromTemplate={async (templateId, name) => { await api.createWorkflowFromTemplate({ templateId, options: name ? { name } : undefined }); }} - onUpgradePatternToWorkflow={async (patternId) => { - await api.upgradePatternToWorkflow({ patternId, options: { save: true } }); - }} sidecarCapabilities={sidecarCapabilities} theme={workspace.settings.theme} toolingSettings={workspace.settings.tooling} @@ -884,7 +833,10 @@ export default function App() { onAddProject={() => void api.addProject()} onCreateScratchpad={() => handleCreateScratchpad()} onNewProjectSession={(projectId) => { - setNewSessionProjectId(projectId); + const defaultWorkflow = workspace.workflows.find((w) => w.isFavorite) ?? workspace.workflows[0]; + if (defaultWorkflow) { + void api.createSession({ projectId, workflowId: defaultWorkflow.id }); + } }} onOpenSettings={() => setShowSettings(true)} onOpenProjectSettings={(projectId) => setProjectSettingsId(projectId)} @@ -920,22 +872,6 @@ export default function App() { } /> - {newSessionProjectId && ( - setNewSessionProjectId(undefined)} - onCreate={(projectId, patternId) => { - setNewSessionProjectId(undefined); - void api.createSession({ projectId, patternId }); - }} - onTogglePatternFavorite={(patternId, isFavorite) => { - void api.setPatternFavorite({ patternId, isFavorite }); - }} - patterns={workspace.patterns} - projects={workspace.projects} - /> - )} - {showDiscoveryModal && ( setShowDiscoveryModal(false)} @@ -987,7 +923,10 @@ export default function App() { void api.selectProject(projectId); }} onNewSession={(projectId) => { - setNewSessionProjectId(projectId); + const defaultWorkflow = workspace.workflows.find((w) => w.isFavorite) ?? workspace.workflows[0]; + if (defaultWorkflow) { + void api.createSession({ projectId, workflowId: defaultWorkflow.id }); + } }} onCreateScratchpad={handleCreateScratchpad} onOpenSettings={() => setShowSettings(true)} diff --git a/src/renderer/components/ActivityPanel.tsx b/src/renderer/components/ActivityPanel.tsx index dad54f5..4638775 100644 --- a/src/renderer/components/ActivityPanel.tsx +++ b/src/renderer/components/ActivityPanel.tsx @@ -17,20 +17,19 @@ import { } from '@renderer/lib/sessionActivity'; import { RunTimeline } from '@renderer/components/RunTimeline'; import { inferProvider } from '@shared/domain/models'; -import type { OrchestrationMode, PatternAgentDefinition, PatternDefinition } from '@shared/domain/pattern'; +import { resolveWorkflowAgentNodes, type AgentNodeConfig, type WorkflowDefinition, type WorkflowOrchestrationMode } from '@shared/domain/workflow'; import type { ProjectGitFileReference } from '@shared/domain/project'; import type { SessionRecord } from '@shared/domain/session'; import { ProviderIcon } from './ProviderIcons'; /* ── Mode accent colours ───────────────────────────────────── */ -const modeAccent: Record = { +const modeAccent: Record = { single: { dot: 'bg-[#245CF9]', bar: 'bg-[#245CF9] opacity-60', label: 'text-[#245CF9]' }, sequential: { dot: 'bg-[var(--color-status-warning)]', bar: 'bg-[var(--color-status-warning)] opacity-60', label: 'text-[var(--color-status-warning)]' }, concurrent: { dot: 'bg-[var(--color-status-success)]', bar: 'bg-[var(--color-status-success)] opacity-60', label: 'text-[var(--color-status-success)]' }, handoff: { dot: 'bg-[var(--color-accent-sky)]', bar: 'bg-[var(--color-accent-sky)] opacity-60', label: 'text-[var(--color-accent-sky)]' }, 'group-chat': { dot: 'bg-[var(--color-accent-purple)]', bar: 'bg-[var(--color-accent-purple)] opacity-60', label: 'text-[var(--color-accent-purple)]' }, - magentic: { dot: 'bg-[var(--color-text-muted)]', bar: 'bg-[var(--color-text-muted)] opacity-60', label: 'text-[var(--color-text-muted)]' }, }; /* ── Helpers ───────────────────────────────────────────────── */ @@ -50,13 +49,12 @@ function formatEffort(effort: string | undefined): string | undefined { return labels[effort] ?? effort; } -const modeLabels: Record = { +const modeLabels: Record = { single: 'Single agent', sequential: 'Sequential', concurrent: 'Concurrent', handoff: 'Handoff', 'group-chat': 'Group chat', - magentic: 'Magentic', }; /* ── Section header ────────────────────────────────────────── */ @@ -79,8 +77,8 @@ function AgentRow({ agentUsage, }: { row: AgentActivityRow; - agent?: PatternAgentDefinition; - accent: (typeof modeAccent)[OrchestrationMode]; + agent?: AgentNodeConfig; + accent: (typeof modeAccent)[WorkflowOrchestrationMode]; isLast: boolean; agentUsage?: AgentUsageAccumulator; }) { @@ -213,7 +211,7 @@ interface ActivityPanelProps { onJumpToMessage?: (messageId: string) => void; onDiscard?: (sessionId: string, runId: string, files?: ProjectGitFileReference[]) => Promise; onOpenCommitComposer?: () => void; - pattern: PatternDefinition; + workflow: WorkflowDefinition; session: SessionRecord; sessionRequestUsage?: SessionRequestUsageState; turnEvents?: TurnEventLog; @@ -224,21 +222,29 @@ export function ActivityPanel({ onJumpToMessage, onDiscard, onOpenCommitComposer, - pattern, + workflow, session, sessionRequestUsage, turnEvents, }: ActivityPanelProps) { + const workflowAgents = useMemo( + () => resolveWorkflowAgentNodes(workflow) + .map((n) => n.config) + .filter((c): c is AgentNodeConfig => c.kind === 'agent'), + [workflow], + ); + const workflowMode = workflow.settings.orchestrationMode ?? 'single'; + const activityRows = useMemo( - () => buildAgentActivityRows(activity, pattern.agents), - [activity, pattern.agents], + () => buildAgentActivityRows(activity, workflowAgents), + [activity, workflowAgents], ); const isBusy = session.status === 'running'; const hasPendingApproval = session.pendingApproval?.status === 'pending'; const queuedCount = (session.pendingApprovalQueue ?? []).filter((a) => a.status === 'pending').length; const totalApprovalCount = (hasPendingApproval ? 1 : 0) + queuedCount; - const accent = modeAccent[pattern.mode] ?? modeAccent.single; + const accent = modeAccent[workflowMode] ?? modeAccent.single; return (
@@ -272,7 +278,7 @@ export function ActivityPanel({ {activityRows.length} - {modeLabels[pattern.mode]} + {modeLabels[workflowMode]} @@ -285,7 +291,7 @@ export function ActivityPanel({ return ( ; toolingSettings: WorkspaceToolingSettings; @@ -85,7 +85,7 @@ interface ChatPaneProps { export function ChatPane({ project, - pattern, + workflow, session, availableModels, toolingSettings, @@ -184,8 +184,15 @@ export function ChatPane({ const interactionMode: InteractionMode = session.interactionMode ?? 'interactive'; const isPlanMode = interactionMode === 'plan'; const isScratchpad = isScratchpadProject(project); - const isSingleAgent = pattern.agents.length === 1; - const primaryAgent = pattern.agents[0]; + const workflowAgents = useMemo( + () => resolveWorkflowAgentNodes(workflow) + .map((n) => n.config) + .filter((c): c is AgentNodeConfig => c.kind === 'agent'), + [workflow], + ); + const workflowMode = workflow.settings.orchestrationMode ?? 'single'; + const isSingleAgent = workflowAgents.length === 1; + const primaryAgent = workflowAgents[0]; const selectedModel = primaryAgent ? findModel(primaryAgent.model, availableModels) : undefined; const supportedEfforts = getSupportedReasoningEfforts(selectedModel); const sessionReasoningEffort = resolveReasoningEffort(selectedModel, primaryAgent?.reasoningEffort); @@ -223,7 +230,7 @@ export function ChatPane({ const mcpServers = toolingSettings.mcpServers; const lspProfiles = toolingSettings.lspProfiles; const hasConfigurableTools = mcpServers.length > 0 || lspProfiles.length > 0; - const hasToolCallApproval = pattern.approvalPolicy?.rules.some((r) => r.kind === 'tool-call') ?? false; + const hasToolCallApproval = workflow.settings.approvalPolicy?.rules.some((r) => r.kind === 'tool-call') ?? false; const approvalTools = useMemo( () => listApprovalToolDefinitions(toolingSettings, runtimeTools), [runtimeTools, toolingSettings], @@ -233,9 +240,9 @@ export function ChatPane({ () => new Set( isApprovalOverridden ? session.approvalSettings!.autoApprovedToolNames - : pattern.approvalPolicy?.autoApprovedToolNames ?? [], + : workflow.settings.approvalPolicy?.autoApprovedToolNames ?? [], ), - [isApprovalOverridden, session.approvalSettings, pattern.approvalPolicy], + [isApprovalOverridden, session.approvalSettings, workflow.settings.approvalPolicy], ); const effectiveAutoApprovedCount = useMemo(() => { const groups = groupApprovalToolsByProvider(approvalTools, toolingSettings); @@ -366,8 +373,8 @@ export function ChatPane({

{session.title}

{isScratchpad - ? `Scratchpad · ${pattern.name}` - : `${project.name} · ${pattern.name} · ${pattern.mode}`} + ? `Scratchpad · ${workflow.name}` + : `${project.name} · ${workflow.name} · ${workflowMode}`} {!isScratchpad && project.git?.status === 'ready' && (() => { const git = project.git; const tipLines: string[] = [git.branch ?? git.head?.shortHash ?? 'HEAD']; @@ -442,11 +449,11 @@ export function ChatPane({ {isScratchpad ? ( <> Scratchpad is ready for ad-hoc questions using{' '} - {pattern.name} + {workflow.name} ) : ( <> - Using {pattern.name} in{' '} + Using {workflow.name} in{' '} {project.name} )} diff --git a/src/renderer/components/NewSessionModal.tsx b/src/renderer/components/NewSessionModal.tsx deleted file mode 100644 index da095f8..0000000 --- a/src/renderer/components/NewSessionModal.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { Star, X } from 'lucide-react'; - -import type { PatternDefinition } from '@shared/domain/pattern'; -import { isScratchpadProject, type ProjectRecord } from '@shared/domain/project'; - -interface NewSessionModalProps { - projects: ProjectRecord[]; - patterns: PatternDefinition[]; - defaultProjectId?: string; - onClose: () => void; - onCreate: (projectId: string, patternId: string) => void; - onTogglePatternFavorite?: (patternId: string, isFavorite: boolean) => void; -} - -export function NewSessionModal({ - projects, - patterns, - defaultProjectId, - onClose, - onCreate, - onTogglePatternFavorite, -}: NewSessionModalProps) { - const userProjects = useMemo( - () => projects.filter((p) => !isScratchpadProject(p)), - [projects], - ); - const [projectId, setProjectId] = useState(defaultProjectId ?? userProjects[0]?.id ?? ''); - const availablePatterns = useMemo( - () => - patterns - .filter((pattern) => pattern.availability !== 'unavailable') - .sort((a, b) => { - if (a.isFavorite && !b.isFavorite) return -1; - if (!a.isFavorite && b.isFavorite) return 1; - return 0; - }), - [patterns], - ); - const [patternId, setPatternId] = useState(availablePatterns[0]?.id ?? ''); - - useEffect(() => { - if (!availablePatterns.some((pattern) => pattern.id === patternId)) { - setPatternId(availablePatterns[0]?.id ?? ''); - } - }, [availablePatterns, patternId]); - - const handleKeyDown = useCallback((e: KeyboardEvent) => { - if (e.key === 'Escape') onClose(); - }, [onClose]); - - useEffect(() => { - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, [handleKeyDown]); - - const canCreate = projectId && patternId; - - return ( -

-
- {/* Header */} -
-

New Session

- -
- - {/* Body */} -
- - - -
- - {/* Footer */} -
- - -
-
-
- ); -} diff --git a/src/renderer/components/PatternEditor.tsx b/src/renderer/components/PatternEditor.tsx deleted file mode 100644 index 0f43b7c..0000000 --- a/src/renderer/components/PatternEditor.tsx +++ /dev/null @@ -1,751 +0,0 @@ -import { useState } from 'react'; -import { - AlertCircle, - ArrowLeftRight, - CheckCircle, - ChevronDown, - ChevronLeft, - GitFork, - Library, - Link2, - ListOrdered, - Lock, - MessageSquare, - Plus, - ShieldCheck, - Trash2, - Unlink, - Users, - type LucideIcon, -} from 'lucide-react'; - -import type { ApprovalCheckpointKind, ApprovalPolicy } from '@shared/domain/approval'; -import type { ModelDefinition } from '@shared/domain/models'; -import { - addAgentToGraph, - removeAgentFromGraph, - resolvePatternGraph, - syncPatternGraph, - validatePatternDefinition, - type OrchestrationMode, - type PatternDefinition, - type PatternAgentDefinition, - type PatternGraph, -} from '@shared/domain/pattern'; -import { - listApprovalToolDefinitions, - type ApprovalToolDefinition, - type ApprovalToolKind, - type RuntimeToolDefinition, - type WorkspaceToolingSettings, -} from '@shared/domain/tooling'; -import type { WorkspaceAgentDefinition } from '@shared/domain/workspaceAgent'; - -import { useClickOutside } from '@renderer/hooks/useClickOutside'; -import { ToggleSwitch } from '@renderer/components/ui'; -import { PatternGraphCanvas } from './pattern-graph/PatternGraphCanvas'; -import { PatternGraphInspector } from './pattern-graph/PatternGraphInspector'; - -interface PatternEditorProps { - availableModels: ReadonlyArray; - pattern: PatternDefinition; - isBuiltin: boolean; - toolingSettings: WorkspaceToolingSettings; - runtimeTools?: ReadonlyArray; - workspaceAgents: WorkspaceAgentDefinition[]; - onSaveWorkspaceAgent: (agent: WorkspaceAgentDefinition) => Promise; - onChange: (pattern: PatternDefinition) => void; - onDelete?: () => void; - onSave: () => void; - onBack: () => void; -} - -interface ModeInfo { - icon: LucideIcon; - label: string; - description: string; -} - -const modeInfo: Record = { - single: { - icon: MessageSquare, - label: 'Single', - description: 'Direct conversation with one agent', - }, - sequential: { - icon: ListOrdered, - label: 'Sequential', - description: 'Agents process in order, each refining the result', - }, - concurrent: { - icon: GitFork, - label: 'Concurrent', - description: 'Multiple agents respond in parallel', - }, - handoff: { - icon: ArrowLeftRight, - label: 'Handoff', - description: 'Triage agent routes to specialists', - }, - 'group-chat': { - icon: Users, - label: 'Group Chat', - description: 'Agents iterate in managed round-robin', - }, - magentic: { - icon: Lock, - label: 'Magentic', - description: 'Not yet available in .NET', - }, -}; - -function InputField({ - label, - value, - onChange, - multiline, - placeholder, -}: { - label: string; - value: string; - onChange: (value: string) => void; - multiline?: boolean; - placeholder?: string; -}) { - const baseClasses = - 'w-full rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-1)] px-3 py-2 text-[13px] text-[var(--color-text-primary)] placeholder-[var(--color-text-muted)] outline-none transition-all duration-200 focus:border-[var(--color-accent)]/50'; - return ( -