diff --git a/src/renderer/components/WorkflowEditor.tsx b/src/renderer/components/WorkflowEditor.tsx index 43501a1..8b283b5 100644 --- a/src/renderer/components/WorkflowEditor.tsx +++ b/src/renderer/components/WorkflowEditor.tsx @@ -1,5 +1,5 @@ import { Fragment, useCallback, useMemo, useState } from 'react'; -import { AlertCircle, CheckCircle, ChevronLeft, ChevronRight, Info, Trash2 } from 'lucide-react'; +import { AlertCircle, CheckCircle, ChevronLeft, ChevronRight, Info, Plus, Trash2, X } from 'lucide-react'; import type { ModelDefinition } from '@shared/domain/models'; import type { @@ -11,6 +11,7 @@ import type { WorkflowEdge, AgentNodeConfig, SubWorkflowConfig, + WorkflowStateScope, } from '@shared/domain/workflow'; import { validateWorkflowDefinition } from '@shared/domain/workflow'; import { createId } from '@shared/utils/ids'; @@ -514,6 +515,71 @@ export function WorkflowEditor({ /* ── Settings panel below canvas ───────────────────────────── */ +function StateScopeInitialValues({ + initialValues, + onChange, +}: { + initialValues: Record; + onChange: (values: Record) => void; +}) { + const entries = Object.entries(initialValues); + + return ( +
+
+ Initial Values + +
+ {entries.map(([key, value]) => ( +
+ { + const next = { ...initialValues }; + const val = next[key]; + delete next[key]; + next[e.target.value] = val; + onChange(next); + }} + placeholder="key" + value={key} + /> + { + onChange({ ...initialValues, [key]: e.target.value }); + }} + placeholder="value" + value={typeof value === 'string' ? value : JSON.stringify(value) ?? ''} + /> + +
+ ))} +
+ ); +} + function WorkflowSettingsPanel({ workflow, onChange, @@ -599,6 +665,126 @@ function WorkflowSettingsPanel({ + + {/* Telemetry */} +
+
+
OpenTelemetry
+

Export telemetry data via OpenTelemetry

+
+ +
+ +
+
+
Filter Sensitive Data
+

Redact sensitive information from telemetry

+
+ +
+ + {/* State Scopes */} +
+
+ + State Scopes + + +
+ + {(workflow.settings.stateScopes ?? []).map((scope, idx) => ( +
+
+ { + const scopes = [...(workflow.settings.stateScopes ?? [])]; + scopes[idx] = { ...scopes[idx], name: e.target.value }; + onChange({ ...workflow, settings: { ...workflow.settings, stateScopes: scopes } }); + }} + placeholder="Scope name" + value={scope.name} + /> + +
+ { + const scopes = [...(workflow.settings.stateScopes ?? [])]; + scopes[idx] = { ...scopes[idx], description: e.target.value }; + onChange({ ...workflow, settings: { ...workflow.settings, stateScopes: scopes } }); + }} + placeholder="Description (optional)" + value={scope.description ?? ''} + /> + { + const scopes = [...(workflow.settings.stateScopes ?? [])]; + scopes[idx] = { ...scopes[idx], initialValues }; + onChange({ ...workflow, settings: { ...workflow.settings, stateScopes: scopes } }); + }} + /> +
+ ))} +
); diff --git a/src/renderer/components/workflow/CodeExecutorInspector.tsx b/src/renderer/components/workflow/CodeExecutorInspector.tsx new file mode 100644 index 0000000..cce5065 --- /dev/null +++ b/src/renderer/components/workflow/CodeExecutorInspector.tsx @@ -0,0 +1,174 @@ +import { useCallback } from 'react'; +import { AlertCircle, Code, Trash2 } from 'lucide-react'; + +import type { + CodeExecutorConfig, + WorkflowNode, + WorkflowNodeConfig, + WorkflowValidationIssue, +} from '@shared/domain/workflow'; + +interface CodeExecutorInspectorProps { + node: WorkflowNode; + validationIssues?: WorkflowValidationIssue[]; + onNodeChange: (nodeId: string, patch: Partial) => void; + onNodeConfigChange: (nodeId: string, config: WorkflowNodeConfig) => void; + onNodeRemove: (nodeId: string) => void; +} + +function InputField({ + label, + value, + onChange, + multiline, + placeholder, +}: { + label: string; + value: string; + onChange: (value: string) => void; + multiline?: boolean; + placeholder?: string; +}) { + const base = + '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 focus:border-[var(--color-accent)]/50'; + return ( +