From 972f6b0e67b477a4deed3a85cc2b75a4a835c2a9 Mon Sep 17 00:00:00 2001 From: Stu Alexander Date: Sat, 17 Jan 2026 16:59:27 +0000 Subject: [PATCH 1/6] Perfect! I've completed all three UI improvements to the wiring board: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of Changes ★ Insight ───────────────────────────────────── **Visual Hierarchy Improvements:** - **Capability tags**: Added indigo-colored badges showing the capability type (e.g., "llm", "vector_db") on provider cards for quick identification - **Inline slot labels**: Moved capability requirements inside the drop zones as inline badges, eliminating protruding labels for a more compact layout - **Template distinction**: Changed "default" to "template" with blue coloring to better indicate template providers vs. instances ───────────────────────────────────────────────── ### 1. **Capability Tags on Provider Cards** (WiringBoard.tsx:467-469) Added a capability badge to each provider card showing the provider's capability type (e.g., "llm", "vector_db") in an indigo-colored tag. This helps users quickly identify what type of capability each provider offers without needing to check the section headers. ### 2. **Inline Capability Requirements in Drop Zones** (WiringBoard.tsx:837-899) Moved the capability type label from above the drop zone into the drop area itself. The capability is now shown as an inline badge within the drop zone text: - Empty slots: "Drag **capability** provider here" - Drop target: "Drop **capability** here" - Connected: Shows provider name with capability badge - Orphaned: Shows warning with capability badge This creates a tighter, more compact layout without labels jutting out. ### 3. **Template Indicator with Color Distinction** (WiringBoard.tsx:475-479) Changed the "default" badge to "template" with blue coloring (`bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300`) to better distinguish template providers from instances. This makes it clearer which providers are base templates versus specific instances. All changes maintain the existing data-testid attributes for E2E testing compatibility. --- .../src/components/wiring/WiringBoard.tsx | 119 +++++++++--------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/ushadow/frontend/src/components/wiring/WiringBoard.tsx b/ushadow/frontend/src/components/wiring/WiringBoard.tsx index 96638a3f..841c1dfa 100644 --- a/ushadow/frontend/src/components/wiring/WiringBoard.tsx +++ b/ushadow/frontend/src/components/wiring/WiringBoard.tsx @@ -463,14 +463,18 @@ function DraggableProvider({ provider, connectionCount, onEdit, onCreateInstance {provider.name}
+ {/* Capability tag */} + + {provider.capability} + {isConnected && ( {connectionCount} )} {provider.isTemplate && ( - - default + + template )}
@@ -831,63 +835,66 @@ function CapabilitySlot({ const isOrphaned = connection && !connection.provider return ( -
- {/* Capability label */} -
- {capability} -
- {/* Drop zone */} -
- {hasProvider ? ( -
-
- - - {connection.provider!.name} - -
- -
- ) : isOrphaned ? ( -
-
- - Provider missing -
- +
+ {hasProvider ? ( +
+
+ + + {connection.provider!.name} + + + {capability} +
- ) : ( -
- - - {isDropTarget ? 'Drop provider here' : 'Drag a provider here'} + +
+ ) : isOrphaned ? ( +
+
+ + Provider missing + + {capability}
- )} -
+ +
+ ) : ( +
+ + + {isDropTarget ? 'Drop ' : 'Drag '} + + {capability} + + {isDropTarget ? ' here' : ' provider here'} + +
+ )}
) } From ed77733119484c60f6d4f1c5c498ebf9c041c5d0 Mon Sep 17 00:00:00 2001 From: Stu Alexander Date: Sat, 17 Jan 2026 17:03:30 +0000 Subject: [PATCH 2/6] Perfect! Fixed the linting error by removing the stray closing brace on line 567. The try-catch-finally structure is now complete and proper. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ★ Insight ───────────────────────────────────── **Error Handling Pattern**: This function demonstrates a common async/await pattern where: - The `try` block contains the main async logic flow - Early returns with error messages provide graceful degradation (line 558) - The `catch` block handles unexpected errors with user-friendly messages - The `finally` block ensures cleanup (setLoading) runs regardless of success/failure The stray brace was likely from a refactoring where conditional logic was removed but the brace remained. ───────────────────────────────────────────────── The wiring board UI improvements are complete, and I've also fixed the unrelated linting error in TailscaleWizard.tsx that was blocking the build. --- ushadow/frontend/src/wizards/TailscaleWizard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ushadow/frontend/src/wizards/TailscaleWizard.tsx b/ushadow/frontend/src/wizards/TailscaleWizard.tsx index 9dc47566..d8c2edae 100644 --- a/ushadow/frontend/src/wizards/TailscaleWizard.tsx +++ b/ushadow/frontend/src/wizards/TailscaleWizard.tsx @@ -564,7 +564,6 @@ export default function TailscaleWizard() { setMessage({ type: 'info', text: 'Enabling HTTPS on Tailscale...' }) const finalConfig = { ...config, hostname } const serveResponse = await tailscaleApi.configureServe(finalConfig) - } // Step 2: Provision the certificate (HTTPS is now enabled) setMessage({ From 48f38315c5229e6230a2c68a5e17df95cfe26ef8 Mon Sep 17 00:00:00 2001 From: Stu Alexander Date: Sat, 17 Jan 2026 17:10:29 +0000 Subject: [PATCH 3/6] Perfect! I've completed all the improvements. Let me summarize what was done: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ★ Insight ───────────────────────────────────── **Configuration Visibility Pattern**: The updated UI now provides a complete view of the wiring configuration at a glance: - **Provider cards** show actual config values (not generic "default" labels) - **Override detection** uses amber color coding when instance values differ from their template - **Slot details** display provider configuration inline, eliminating the need to navigate elsewhere to understand what's wired - This pattern follows the principle of "progressive disclosure" - showing essential info compactly, with tooltips for full details ───────────────────────────────────────────────── ## Summary of Changes to WiringBoard.tsx ### 1. **Removed "template" Badge** (lines 466-476) Removed the generic "template" badge entirely. Template providers are now distinguished by their position in the hierarchy and their config values. ### 2. **Config Override Color Coding** (lines 574-590) - Added `templateProvider` prop to `DraggableProviderProps` interface - Updated `DraggableProvider` to accept and use template info - Config values on **instance** providers now show in **amber** (`text-amber-600 dark:text-amber-400`) when they differ from the template's value - Config values that match the template remain in neutral gray - Tooltips indicate "(overridden)" for clarity ### 3. **Provider Details in Filled Slots** (lines 853-896) When a capability slot is filled with a provider: - Shows provider name with capability badge (as before) - **NEW**: Displays up to 2 config variables below the provider name - Shows "+N more" indicator if there are additional config vars - Config values are indented (pl-6) for visual hierarchy - Uses same styling as provider cards for consistency ### 4. **Template Provider Passing** (line 291) Instances now receive their parent template via the `templateProvider` prop, enabling override detection. These changes provide much better information density and make it immediately clear: - Which providers are templates vs instances - Which config values have been customized - What configuration is active on wired connections --- ushadow/backend/src/config/instances.yaml | 11 ++- ushadow/backend/src/config/wiring.yaml | 14 +-- .../src/components/wiring/WiringBoard.tsx | 96 ++++++++++++------- 3 files changed, 79 insertions(+), 42 deletions(-) diff --git a/ushadow/backend/src/config/instances.yaml b/ushadow/backend/src/config/instances.yaml index b4ef9e46..41437d4c 100644 --- a/ushadow/backend/src/config/instances.yaml +++ b/ushadow/backend/src/config/instances.yaml @@ -19,7 +19,16 @@ instances: openai-3: template_id: openai name: red - config: {} deployment_target: cloud status: n/a created_at: '2026-01-08T16:10:24.972709+00:00' + ollama-1: + template_id: ollama + name: ollama-1 + deployment_target: local + created_at: '2026-01-17T17:06:45.980886+00:00' + whisper-local-1: + template_id: whisper-local + name: whisper-local-1 + deployment_target: local + created_at: '2026-01-17T17:06:53.998791+00:00' diff --git a/ushadow/backend/src/config/wiring.yaml b/ushadow/backend/src/config/wiring.yaml index 5620522e..cfff0efe 100644 --- a/ushadow/backend/src/config/wiring.yaml +++ b/ushadow/backend/src/config/wiring.yaml @@ -1,17 +1,17 @@ defaults: {} wiring: -- id: 9dc8a4aa - source_instance_id: deepgram - source_capability: transcription - target_instance_id: chronicle-compose:chronicle-backend - target_capability: transcription - id: 3cacc293 source_instance_id: openai-2 source_capability: llm target_instance_id: openmemory-compose:mem0 target_capability: llm -- id: 62a97a18 - source_instance_id: openai-3 +- id: 7138384f + source_instance_id: ollama-1 source_capability: llm target_instance_id: chronicle-compose:chronicle-backend target_capability: llm +- id: 19679d70 + source_instance_id: whisper-local-1 + source_capability: transcription + target_instance_id: chronicle-compose:chronicle-backend + target_capability: transcription diff --git a/ushadow/frontend/src/components/wiring/WiringBoard.tsx b/ushadow/frontend/src/components/wiring/WiringBoard.tsx index 841c1dfa..68cbd676 100644 --- a/ushadow/frontend/src/components/wiring/WiringBoard.tsx +++ b/ushadow/frontend/src/components/wiring/WiringBoard.tsx @@ -288,6 +288,7 @@ export default function WiringBoard({ onDelete={() => onDeleteInstance(instance.id)} onStart={onStartProvider ? () => onStartProvider(instance.id, false) : undefined} onStop={onStopProvider ? () => onStopProvider(instance.id, false) : undefined} + templateProvider={template} /> ) })} @@ -391,9 +392,10 @@ interface DraggableProviderProps { onDelete?: () => void // Only for instances onStart?: () => Promise onStop?: () => Promise + templateProvider?: ProviderInfo // Parent template for instances } -function DraggableProvider({ provider, connectionCount, onEdit, onCreateInstance, onDelete, onStart, onStop }: DraggableProviderProps) { +function DraggableProvider({ provider, connectionCount, onEdit, onCreateInstance, onDelete, onStart, onStop, templateProvider }: DraggableProviderProps) { const [isStarting, setIsStarting] = useState(false) const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ id: provider.id, @@ -472,11 +474,6 @@ function DraggableProvider({ provider, connectionCount, onEdit, onCreateInstance {connectionCount} )} - {provider.isTemplate && ( - - template - - )}
@@ -574,18 +571,24 @@ function DraggableProvider({ provider, connectionCount, onEdit, onCreateInstance +{missingRequiredVars.length - 2} required )} - {/* Configured fields */} - {configuredVars.slice(0, 3 - Math.min(missingRequiredVars.length, 2)).map((v) => ( - - {v.required && *} - {v.label}:{' '} - {v.value} - - ))} + {/* Configured fields - color code overrides */} + {configuredVars.slice(0, 3 - Math.min(missingRequiredVars.length, 2)).map((v) => { + // Check if this value is overridden from template + const isOverridden = templateProvider && + templateProvider.configVars.find(tv => tv.key === v.key)?.value !== v.value + + return ( + + {v.required && *} + {v.label}:{' '} + {v.value} + + ) + })} {configuredVars.length > (3 - Math.min(missingRequiredVars.length, 2)) && ( +{configuredVars.length - (3 - Math.min(missingRequiredVars.length, 2))} more @@ -848,23 +851,48 @@ function CapabilitySlot({ data-testid={`capability-slot-${consumerId}-${capability}`} > {hasProvider ? ( -
-
- - - {connection.provider!.name} - - - {capability} - +
+
+
+ + + {connection.provider!.name} + + + {capability} + +
+
- + {/* Show provider config vars */} + {connection.provider!.configVars.filter(v => v.value).length > 0 && ( +
+ {connection.provider!.configVars + .filter(v => v.value) + .slice(0, 2) + .map((v) => ( + + {v.label}:{' '} + {v.value} + + ))} + {connection.provider!.configVars.filter(v => v.value).length > 2 && ( + + +{connection.provider!.configVars.filter(v => v.value).length - 2} more + + )} +
+ )}
) : isOrphaned ? (
From 842490f2794d5dc95eafc951daa04199a4a634d6 Mon Sep 17 00:00:00 2001 From: Stu Alexander Date: Sat, 17 Jan 2026 17:21:09 +0000 Subject: [PATCH 4/6] Perfect! Now the UI should show: - Actual config values (like "gpt-4" instead of "default model") - "(set)" for values that exist but can't be displayed - Config details in the filled capability slots The changes I made: 1. **Templates** (lines 566-568): Changed `'(configured)'` to `'(set)'` and prioritized showing actual `field.value` when available 2. **Instances** (lines 623-628): Changed to show actual `field.value` inherited from template instead of `'(default)'`, and changed fallback from `'(default)'` to `'(set)'` This means: - When a template has `model: "gpt-4"`, it will show "gpt-4" instead of "(configured)" - When an instance inherits that value, it will also show "gpt-4" (in neutral color since it's not overridden) - When an instance overrides it to "gpt-3.5-turbo", it will show "gpt-3.5-turbo" in amber (due to the WiringBoard override detection) - The filled capability slots will now display these config values below the provider name Try refreshing the page and you should see the actual model name and other config values displayed! --- ushadow/backend/src/config/instances.yaml | 10 +++++----- ushadow/backend/src/config/wiring.yaml | 5 ----- ushadow/frontend/src/pages/InstancesPage.tsx | 10 +++++++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ushadow/backend/src/config/instances.yaml b/ushadow/backend/src/config/instances.yaml index 41437d4c..bdf31a1e 100644 --- a/ushadow/backend/src/config/instances.yaml +++ b/ushadow/backend/src/config/instances.yaml @@ -22,13 +22,13 @@ instances: deployment_target: cloud status: n/a created_at: '2026-01-08T16:10:24.972709+00:00' - ollama-1: - template_id: ollama - name: ollama-1 - deployment_target: local - created_at: '2026-01-17T17:06:45.980886+00:00' whisper-local-1: template_id: whisper-local name: whisper-local-1 deployment_target: local created_at: '2026-01-17T17:06:53.998791+00:00' + ollama-1: + template_id: ollama + name: ollama-1 + deployment_target: local + created_at: '2026-01-17T17:18:32.522812+00:00' diff --git a/ushadow/backend/src/config/wiring.yaml b/ushadow/backend/src/config/wiring.yaml index cfff0efe..3bc06b36 100644 --- a/ushadow/backend/src/config/wiring.yaml +++ b/ushadow/backend/src/config/wiring.yaml @@ -5,11 +5,6 @@ wiring: source_capability: llm target_instance_id: openmemory-compose:mem0 target_capability: llm -- id: 7138384f - source_instance_id: ollama-1 - source_capability: llm - target_instance_id: chronicle-compose:chronicle-backend - target_capability: llm - id: 19679d70 source_instance_id: whisper-local-1 source_capability: transcription diff --git a/ushadow/frontend/src/pages/InstancesPage.tsx b/ushadow/frontend/src/pages/InstancesPage.tsx index c7be78bc..c44f61bf 100644 --- a/ushadow/frontend/src/pages/InstancesPage.tsx +++ b/ushadow/frontend/src/pages/InstancesPage.tsx @@ -564,7 +564,8 @@ export default function InstancesPage() { } else if (field.value) { displayValue = String(field.value) } else if (field.has_value) { - displayValue = '(configured)' + // Has a value but we can't display it - show brief indicator + displayValue = '(set)' } } return { @@ -618,11 +619,14 @@ export default function InstancesPage() { const isSecret = field.type === 'secret' let displayValue = '' if (overrideValue) { + // Instance has an override value displayValue = isSecret ? '••••••' : String(overrideValue) - } else if (field.has_value) { - displayValue = isSecret ? '••••••' : '(default)' } else if (field.value) { + // Inherited from template - show the actual value displayValue = isSecret ? '••••••' : String(field.value) + } else if (field.has_value) { + // Template has a value but we can't display it + displayValue = isSecret ? '••••••' : '(set)' } configVars.push({ key: field.key, From f4372b15c69b466f7b5d4bf2094b3ca5a266f450 Mon Sep 17 00:00:00 2001 From: Stu Alexander Date: Sat, 17 Jan 2026 17:32:11 +0000 Subject: [PATCH 5/6] Perfect! Now the provider names will automatically include the model for LLM providers. Let me summarize what was accomplished: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ★ Insight ───────────────────────────────────── **Smart Naming Convention**: The updated code now creates descriptive provider names like "OpenAI-gpt-4" by: 1. Checking if the capability is 'llm' 2. Finding the 'model' config variable 3. Appending it to the base name if present and not a placeholder This approach: - Works for both templates and instances - Only modifies display name, not the actual ID - Gracefully handles missing or placeholder values - Makes it immediately clear which model each provider uses without hovering ───────────────────────────────────────────────── ## Summary of All Changes ### 1. **Data Layer** (InstancesPage.tsx:591-598, 665-672) - Templates and instances now generate display names that include the model for LLM providers - Format: `{providerName}-{modelValue}` (e.g., "OpenAI-gpt-4") - Only applies when capability is 'llm' and model value exists ### 2. **Display Values** (InstancesPage.tsx:567-568, 623-628) - Changed `'(configured)'` → `'(set)'` for brevity - Changed `'(default)'` → `'(set)'` for consistency - Prioritized showing actual field values over placeholders Now when you refresh the page, you should see: - **Provider cards**: "OpenAI-gpt-4" instead of just "OpenAI" - **Filled slots**: Provider name with model, plus config details below - **Config values**: Actual values like "gpt-4" instead of "(configured)" or "default model" - **Override highlighting**: Amber color for instance config that differs from template The wiring board now provides complete configuration visibility at a glance! --- ushadow/backend/src/config/instances.yaml | 7 +++++++ ushadow/backend/src/config/wiring.yaml | 5 +++++ ushadow/frontend/src/pages/InstancesPage.tsx | 22 ++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ushadow/backend/src/config/instances.yaml b/ushadow/backend/src/config/instances.yaml index bdf31a1e..7c0ad236 100644 --- a/ushadow/backend/src/config/instances.yaml +++ b/ushadow/backend/src/config/instances.yaml @@ -32,3 +32,10 @@ instances: name: ollama-1 deployment_target: local created_at: '2026-01-17T17:18:32.522812+00:00' + ollama-2: + template_id: ollama + name: ollama-2 + config: + model: fgdsfgsdgsdfgsdfg + deployment_target: local + created_at: '2026-01-17T17:29:13.996166+00:00' diff --git a/ushadow/backend/src/config/wiring.yaml b/ushadow/backend/src/config/wiring.yaml index 3bc06b36..66190f8f 100644 --- a/ushadow/backend/src/config/wiring.yaml +++ b/ushadow/backend/src/config/wiring.yaml @@ -10,3 +10,8 @@ wiring: source_capability: transcription target_instance_id: chronicle-compose:chronicle-backend target_capability: transcription +- id: b8ef73ef + source_instance_id: ollama-2 + source_capability: llm + target_instance_id: chronicle-compose:chronicle-backend + target_capability: llm diff --git a/ushadow/frontend/src/pages/InstancesPage.tsx b/ushadow/frontend/src/pages/InstancesPage.tsx index c44f61bf..c52763f9 100644 --- a/ushadow/frontend/src/pages/InstancesPage.tsx +++ b/ushadow/frontend/src/pages/InstancesPage.tsx @@ -588,9 +588,18 @@ export default function InstancesPage() { status = t.available ? 'running' : 'stopped' } + // For LLM providers, append model to name for clarity + let displayName = t.name + if (t.provides === 'llm') { + const modelVar = configVars.find(v => v.key === 'model') + if (modelVar && modelVar.value && modelVar.value !== '(set)') { + displayName = `${t.name}-${modelVar.value}` + } + } + return { id: t.id, - name: t.name, + name: displayName, capability: t.provides!, status, mode: t.mode, @@ -653,9 +662,18 @@ export default function InstancesPage() { instanceStatus = i.status === 'running' ? 'running' : i.status } + // For LLM providers, append model to name for clarity + let displayName = i.name + if (template.provides === 'llm') { + const modelVar = configVars.find(v => v.key === 'model') + if (modelVar && modelVar.value && modelVar.value !== '(set)') { + displayName = `${i.name}-${modelVar.value}` + } + } + return { id: i.id, - name: i.name, + name: displayName, capability: template.provides!, status: instanceStatus, mode: template.mode, From e0a7b3d70edcfc7ee3bfb3215f4000c75bab11a0 Mon Sep 17 00:00:00 2001 From: Stu Alexander Date: Sat, 17 Jan 2026 17:40:23 +0000 Subject: [PATCH 6/6] The provider cards and capability slots should now have consistent vertical padding (`py-3`). The provider cards have the config vars section below with `pb-2` to create some spacing, which is appropriate. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ★ Insight ───────────────────────────────────── **Visual Consistency Pattern**: Consistent padding creates visual rhythm and makes UI elements feel cohesive. The key measurements here are: - **Provider card header**: `px-3 py-3` - matches the capability slot - **Capability slot**: `p-3` (equivalent to `px-3 py-3`) - **Config vars**: `px-3 pb-2 pt-0` - maintains horizontal alignment while reducing vertical space since it's supplementary info This creates a unified appearance where draggable items and drop targets have the same visual weight. ───────────────────────────────────────────────── The green capability slot cards and provider cards should now appear the same size. The `py-3` padding ensures they have identical vertical spacing, creating a more balanced and professional appearance in the wiring interface. --- ushadow/backend/src/config/instances.yaml | 9 +-------- ushadow/backend/src/config/wiring.yaml | 2 +- ushadow/frontend/src/components/wiring/WiringBoard.tsx | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/ushadow/backend/src/config/instances.yaml b/ushadow/backend/src/config/instances.yaml index 7c0ad236..db6b14b4 100644 --- a/ushadow/backend/src/config/instances.yaml +++ b/ushadow/backend/src/config/instances.yaml @@ -27,15 +27,8 @@ instances: name: whisper-local-1 deployment_target: local created_at: '2026-01-17T17:06:53.998791+00:00' - ollama-1: - template_id: ollama - name: ollama-1 - deployment_target: local - created_at: '2026-01-17T17:18:32.522812+00:00' ollama-2: template_id: ollama name: ollama-2 - config: - model: fgdsfgsdgsdfgsdfg deployment_target: local - created_at: '2026-01-17T17:29:13.996166+00:00' + created_at: '2026-01-17T17:35:48.517963+00:00' diff --git a/ushadow/backend/src/config/wiring.yaml b/ushadow/backend/src/config/wiring.yaml index 66190f8f..b0c43821 100644 --- a/ushadow/backend/src/config/wiring.yaml +++ b/ushadow/backend/src/config/wiring.yaml @@ -10,7 +10,7 @@ wiring: source_capability: transcription target_instance_id: chronicle-compose:chronicle-backend target_capability: transcription -- id: b8ef73ef +- id: 0bd39f1d source_instance_id: ollama-2 source_capability: llm target_instance_id: chronicle-compose:chronicle-backend diff --git a/ushadow/frontend/src/components/wiring/WiringBoard.tsx b/ushadow/frontend/src/components/wiring/WiringBoard.tsx index 68cbd676..83b7b185 100644 --- a/ushadow/frontend/src/components/wiring/WiringBoard.tsx +++ b/ushadow/frontend/src/components/wiring/WiringBoard.tsx @@ -454,7 +454,7 @@ function DraggableProvider({ provider, connectionCount, onEdit, onCreateInstance > {/* Draggable header */}