Skip to content

Improve import service with reusable components and fix Docker Hub issues#112

Merged
thestumonkey merged 3 commits into
mainfrom
4c56-import-service
Jan 19, 2026
Merged

Improve import service with reusable components and fix Docker Hub issues#112
thestumonkey merged 3 commits into
mainfrom
4c56-import-service

Conversation

@thestumonkey

@thestumonkey thestumonkey commented Jan 19, 2026

Copy link
Copy Markdown
Member

Summary

This PR addresses 5 issues with the import service dialog and refactors the code into reusable components for better maintainability.

Issues Fixed

  1. Auto-refresh after import - The page now automatically refreshes after successfully importing a service, eliminating the need for manual refresh
  2. Editable env var names - Environment variable names are now editable for Docker Hub imports and manually added variables
  3. Custom capabilities - Users can now add custom capabilities beyond the preset options (llm, tts, stt, etc.)
  4. Required capabilities - Added "Capabilities Required" section so services can declare their dependencies on other services
  5. Network naming - Fixed Docker compose to use simple infra-network instead of ${COMPOSE_PROJECT_NAME:-ushadow}_infra-network

New Reusable Components

CapabilitySelector.tsx

  • Supports both "provides" (blue) and "requires" (amber) modes
  • Custom capability input with validation
  • Exports CAPABILITY_OPTIONS constant for consistency across the app

EnvVarListEditor.tsx

  • Add/remove environment variables
  • Name editing (optional, for new vars)
  • Value source selection (literal, default, from settings)
  • Secret detection and masking
  • Bulk paste from .env format
  • Settings suggestion support with fuzzy matching

Backend Changes

  • Added requires parameter to register_dockerhub_service endpoint
  • Updated generate_compose_from_dockerhub to accept and use requires
  • Fixed external network name to infra-network

API Changes

  • Added requires?: string[] to DockerHubRegisterRequest interface

Test Plan

  • Import a service from Docker Hub and verify page auto-refreshes
  • Add a custom environment variable and verify the name is editable
  • Add a custom capability (e.g., "custom-feature")
  • Select required capabilities for a service
  • Verify generated compose file uses infra-network (not the variable form)

🤖 Generated with Claude Code

`★ Insight ─────────────────────────────────────`
The import dialog had several UX issues stemming from incomplete state management and rigid options. The fixes add proper refresh timing, editable fields where needed, and match the existing network naming convention used throughout the codebase.
`─────────────────────────────────────────────────`

## Summary of Fixes

### 1. Post-import refresh (line 514)
**Problem:** Had to manually refresh after import.
**Fix:** Added `onServiceImported()` call immediately after successful import, not just when clicking "Done".

### 2. Variable name editing (lines 1072-1106)
**Problem:** Couldn't enter variable names when adding new env vars.
**Fix:** Name input is now editable for:
- All Docker Hub imports
- Manually added variables (those with empty names)
- Also added delete button for manually added vars

### 3. Adding custom capabilities (lines 862-904)
**Problem:** Could only select from preset list.
**Fix:** Added:
- `transcription` to the preset options
- Custom capability text input with "Add" button
- Displays all selected capabilities as removable chips

### 4. Required capabilities selection (lines 907-956)
**Problem:** No way to specify which capabilities a service depends on.
**Fix:** Added a new "Capabilities Required" section with:
- Same capability options as "Provided"
- Amber styling to distinguish from green "Provides"
- Displays selected requirements as removable chips
- Wired through to API and backend (`requires` field in x-ushadow metadata)

### 5. Docker network naming (line 335)
**Problem:** Generated `${COMPOSE_PROJECT_NAME:-ushadow}_infra-network` but existing compose files use just `infra-network`.
**Fix:** Changed to `name: 'infra-network'` to match existing pattern in `docker-compose.infra.yml` and other compose files.

---

**Regarding your question about Docker networks:** The current pattern uses a single `infra-network` that all services join. This is simple and works well when services need to communicate. You could add a separate `ushadow` network for the main app stack if you want isolation, but for most use cases a single shared network is sufficient. The key is consistency - all your compose files now use the same `infra-network` name.
I've created two reusable components and refactored the import modal:

### New Components

**1. `CapabilitySelector.tsx`** - Reusable capability selection
- Exports `CAPABILITY_OPTIONS` constant for consistency across the app
- `mode` prop: `'provides'` (blue styling) or `'requires'` (amber styling)
- Built-in custom capability input with validation
- Removable chips for selected items
- Proper `data-testid` attributes

**2. `EnvVarListEditor.tsx`** - Reusable env var list management
- Add/remove variables
- Name editing (configurable via `allowNameEdit`)
- Source selection: literal value, default, or from settings
- Bulk paste from `.env` format
- Auto-detect secrets by name pattern
- Show/hide toggle for secret values

### Refactored Modal

The `ImportFromGitHubModal` now:
- Uses `CapabilitySelector` for both "provides" and "requires" sections
- Uses `EnvVarListEditor` for environment variables
- Removed ~200 lines of duplicated inline code
- Cleaner separation of concerns

---

### Regarding Your Architecture Question

**You asked about flows as components vs contexts:**

For your current use case, I'd recommend this evolution path:

```
Current (what we have):
ImportFromGitHubModal
├── renderUrlStep()      ─ inline render functions
├── renderComposeStep()
├── renderServiceStep()
├── renderConfigStep()   ─ uses CapabilitySelector, EnvVarListEditor
└── renderCompleteStep()

Future (if it grows more complex):
ImportServiceModal (container)
├── StepIndicator (shared)
├── UrlStep (component)
├── GitHubComposeStep (GitHub only)
├── GitHubServiceStep (GitHub only)
├── ServiceConfigStep (shared) ─ uses CapabilitySelector, EnvVarListEditor
└── CompleteStep (shared)
```

The current inline render functions work fine for now. Extract to separate components when:
1. The modal grows beyond ~500-600 lines
2. You want to test steps in isolation
3. Steps need their own state management

---

**Your decision point on suggestions:** Do you want me to add settings suggestion support to `EnvVarListEditor`? This would let users auto-map env vars to existing settings during import (like the existing `EnvVarEditor` does for service configuration).
…ditor` component. Here's what was added:

`★ Insight ─────────────────────────────────────`
**Component Architecture Decisions:**
1. **Fuzzy matching via scoring** - The `findMatchingSuggestions` function uses a scoring algorithm (exact match = 100, partial = 50, shared words = 25×n) to rank suggestions by relevance to the env var name
2. **Optgroup for visual hierarchy** - The dropdown uses `<optgroup>` labels to separate "Suggested matches" from "All settings", making it easier to find the right mapping
3. **Graceful degradation** - When no suggestions are available, the component falls back to a text input for manual entry
`─────────────────────────────────────────────────`

**Summary of changes to `EnvVarListEditor.tsx`:**

1. **Added `suggestions` prop** - The component now accepts an optional array of `SettingSuggestion[]`

2. **Created `SettingPathSelector` component** - A new internal component that:
   - Shows a dropdown when suggestions are available
   - Groups suggestions into "Suggested matches" (based on `findMatchingSuggestions` scoring) and "All settings"
   - Shows a checkmark (✓) next to settings that already have values configured
   - Shows a "Configured" indicator when a setting with a value is selected
   - Falls back to a text input when no suggestions are provided

3. **Wired up the data flow** - `EnvVarRow` now receives and passes `suggestions` to the `SettingPathSelector`

To use this feature in `ImportFromGitHubModal`, you'll need to fetch available settings and pass them as the `suggestions` prop. For example:

```tsx
<EnvVarListEditor
  envVars={envVars}
  onChange={setEnvVars}
  allowNameEdit={true}
  suggestions={availableSettings} // fetch from API
/>
```
@thestumonkey thestumonkey merged commit 3c1b760 into main Jan 19, 2026
2 of 4 checks passed
@thestumonkey thestumonkey deleted the 4c56-import-service branch January 19, 2026 16:44
@thestumonkey thestumonkey changed the title Import service Improve import service with reusable components and fix Docker Hub issues Jan 19, 2026
thestumonkey added a commit that referenced this pull request Jan 24, 2026
* All five issues have been fixed. Let me provide a summary:

`★ Insight ─────────────────────────────────────`
The import dialog had several UX issues stemming from incomplete state management and rigid options. The fixes add proper refresh timing, editable fields where needed, and match the existing network naming convention used throughout the codebase.
`─────────────────────────────────────────────────`

## Summary of Fixes

### 1. Post-import refresh (line 514)
**Problem:** Had to manually refresh after import.
**Fix:** Added `onServiceImported()` call immediately after successful import, not just when clicking "Done".

### 2. Variable name editing (lines 1072-1106)
**Problem:** Couldn't enter variable names when adding new env vars.
**Fix:** Name input is now editable for:
- All Docker Hub imports
- Manually added variables (those with empty names)
- Also added delete button for manually added vars

### 3. Adding custom capabilities (lines 862-904)
**Problem:** Could only select from preset list.
**Fix:** Added:
- `transcription` to the preset options
- Custom capability text input with "Add" button
- Displays all selected capabilities as removable chips

### 4. Required capabilities selection (lines 907-956)
**Problem:** No way to specify which capabilities a service depends on.
**Fix:** Added a new "Capabilities Required" section with:
- Same capability options as "Provided"
- Amber styling to distinguish from green "Provides"
- Displays selected requirements as removable chips
- Wired through to API and backend (`requires` field in x-ushadow metadata)

### 5. Docker network naming (line 335)
**Problem:** Generated `${COMPOSE_PROJECT_NAME:-ushadow}_infra-network` but existing compose files use just `infra-network`.
**Fix:** Changed to `name: 'infra-network'` to match existing pattern in `docker-compose.infra.yml` and other compose files.

---

**Regarding your question about Docker networks:** The current pattern uses a single `infra-network` that all services join. This is simple and works well when services need to communicate. You could add a separate `ushadow` network for the main app stack if you want isolation, but for most use cases a single shared network is sufficient. The key is consistency - all your compose files now use the same `infra-network` name.

* ## Summary

I've created two reusable components and refactored the import modal:

### New Components

**1. `CapabilitySelector.tsx`** - Reusable capability selection
- Exports `CAPABILITY_OPTIONS` constant for consistency across the app
- `mode` prop: `'provides'` (blue styling) or `'requires'` (amber styling)
- Built-in custom capability input with validation
- Removable chips for selected items
- Proper `data-testid` attributes

**2. `EnvVarListEditor.tsx`** - Reusable env var list management
- Add/remove variables
- Name editing (configurable via `allowNameEdit`)
- Source selection: literal value, default, or from settings
- Bulk paste from `.env` format
- Auto-detect secrets by name pattern
- Show/hide toggle for secret values

### Refactored Modal

The `ImportFromGitHubModal` now:
- Uses `CapabilitySelector` for both "provides" and "requires" sections
- Uses `EnvVarListEditor` for environment variables
- Removed ~200 lines of duplicated inline code
- Cleaner separation of concerns

---

### Regarding Your Architecture Question

**You asked about flows as components vs contexts:**

For your current use case, I'd recommend this evolution path:

```
Current (what we have):
ImportFromGitHubModal
├── renderUrlStep()      ─ inline render functions
├── renderComposeStep()
├── renderServiceStep()
├── renderConfigStep()   ─ uses CapabilitySelector, EnvVarListEditor
└── renderCompleteStep()

Future (if it grows more complex):
ImportServiceModal (container)
├── StepIndicator (shared)
├── UrlStep (component)
├── GitHubComposeStep (GitHub only)
├── GitHubServiceStep (GitHub only)
├── ServiceConfigStep (shared) ─ uses CapabilitySelector, EnvVarListEditor
└── CompleteStep (shared)
```

The current inline render functions work fine for now. Extract to separate components when:
1. The modal grows beyond ~500-600 lines
2. You want to test steps in isolation
3. Steps need their own state management

---

**Your decision point on suggestions:** Do you want me to add settings suggestion support to `EnvVarListEditor`? This would let users auto-map env vars to existing settings during import (like the existing `EnvVarEditor` does for service configuration).

* I've completed adding settings suggestion support to the `EnvVarListEditor` component. Here's what was added:

`★ Insight ─────────────────────────────────────`
**Component Architecture Decisions:**
1. **Fuzzy matching via scoring** - The `findMatchingSuggestions` function uses a scoring algorithm (exact match = 100, partial = 50, shared words = 25×n) to rank suggestions by relevance to the env var name
2. **Optgroup for visual hierarchy** - The dropdown uses `<optgroup>` labels to separate "Suggested matches" from "All settings", making it easier to find the right mapping
3. **Graceful degradation** - When no suggestions are available, the component falls back to a text input for manual entry
`─────────────────────────────────────────────────`

**Summary of changes to `EnvVarListEditor.tsx`:**

1. **Added `suggestions` prop** - The component now accepts an optional array of `SettingSuggestion[]`

2. **Created `SettingPathSelector` component** - A new internal component that:
   - Shows a dropdown when suggestions are available
   - Groups suggestions into "Suggested matches" (based on `findMatchingSuggestions` scoring) and "All settings"
   - Shows a checkmark (✓) next to settings that already have values configured
   - Shows a "Configured" indicator when a setting with a value is selected
   - Falls back to a text input when no suggestions are provided

3. **Wired up the data flow** - `EnvVarRow` now receives and passes `suggestions` to the `SettingPathSelector`

To use this feature in `ImportFromGitHubModal`, you'll need to fetch available settings and pass them as the `suggestions` prop. For example:

```tsx
<EnvVarListEditor
  envVars={envVars}
  onChange={setEnvVars}
  allowNameEdit={true}
  suggestions={availableSettings} // fetch from API
/>
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant