# Config Registry & Widget Layout Picker Specification

## Overview

Create a unified **Config Registry** system where configuration objects (Layouts, Themes, Profiles, Plugins, etc.) register themselves with the `/config` command. Each config object defines:
- Its data model
- Save location (config.json, ~/.kollabor-cli/layouts/, etc.)
- UI widgets for editing
- Discovery pattern (file scan, registry, etc.)

## Problem Statement

Currently `/config` is a single modal with hardcoded sections. Adding new config types requires:
1. Modifying `ConfigWidgetDefinitions.get_config_modal_definition()`
2. Hardcoding save logic to main config.json
3. No way to save to external files (layouts/, themes/, etc.)

## Goals

1. **Config Registry**: Central registry where config objects register themselves
2. **Config Object Picker**: `/config` shows dropdown to select config object
3. **Flexible Save Locations**: `config_path` routing determines save target
4. **Preserve Existing Discovery**: Keep layout/theme/widget discovery patterns
5. **Extensibility**: New config types register without modifying core code

## Architecture

### Config Registry

```python
class ConfigObjectType(ABC):
    """Base class for all config object types."""

    # Registration metadata
    id: str                    # "layouts", "themes", "profiles"
    name: str                  # "Layouts", "Themes", "Profiles"
    description: str           # "Widget layout profiles"
    icon: str                  # "[LAY]"
    sort_order: int            # For ordering in picker

    # Data model
    data_model: Type           # StatusLayout, Theme, LLMProfile, etc.

    # Storage
    location: ConfigLocation   # Determines where files are saved

    # UI
    widgets: List[WidgetDef]   # Widget definitions for editing

    # Discovery
    discovery_type: DiscoveryType
    discovery_pattern: str     # Glob pattern, registry key, etc.

    @abstractmethod
    def list(self) -> List[ConfigItem]: ...
    @abstractmethod
    def load(self, name: str) -> data_model: ...
    @abstractmethod
    def save(self, name: str, data: data_model) -> bool: ...
    @abstractmethod
    def delete(self, name: str) -> bool: ...
    @abstractmethod
    def get_widgets(self, item_name: str = None) -> List[WidgetDef]: ...

@dataclass
class ConfigItem:
    """A discoverable config item (e.g., a layout file)."""
    name: str
    description: str
    bundled: bool              # True if shipped with app
    modified: Optional[datetime]
    metadata: Dict[str, Any]   # row_count, widget_count, etc.

class ConfigLocation(Enum):
    """Where config data is stored."""
    MAIN_CONFIG = "main"           # ~/.kollabor-cli/config.json
    LAYOUTS_DIR = "layouts"         # ~/.kollabor-cli/layouts/
    THEMES_DIR = "themes"           # ~/.kollabor-cli/themes/
    PROFILES_DIR = "profiles"       # ~/.kollabor-cli/profiles/
    PLUGINS_DIR = "plugins"         # ~/.kollabor-cli/plugins/
    CUSTOM = "custom"               # Custom path

class DiscoveryType(Enum):
    """How config items are discovered."""
    FILE_SCAN = "file_scan"         # Scan directory for *.json files
    REGISTRY = "registry"           # Query internal registry
    API = "api"                     # External API call
    STATIC = "static"               # Hardcoded list
```

### Config Registry

```python
class ConfigRegistry:
    """Central registry for config object types.

    Config types register at startup via register_type().
    The /config command queries this registry to build the picker.
    """

    def __init__(self):
        self._types: Dict[str, ConfigObjectType] = {}

    def register_type(self, config_type: ConfigObjectType) -> None:
        """Register a config object type."""
        self._types[config_type.id] = config_type

    def get_type(self, id: str) -> Optional[ConfigObjectType]:
        """Get a registered config type."""
        return self._types.get(id)

    def list_types(self) -> List[ConfigObjectType]:
        """Get all registered types, sorted by sort_order."""
        return sorted(self._types.values(), key=lambda t: t.sort_order)

    def get_type_for_path(self, config_path: str) -> Optional[ConfigObjectType]:
        """Find the config type that handles this config_path.

        Example:
            "widgets.layout.current" -> LayoutsConfigType
            "ui.theme.selected" -> ThemesConfigType
        """
        # Longest prefix match wins
        for type_id, config_type in self._types.items():
            if config_path.startswith(type_id):
                return config_type
        return None
```

### Registered Config Types

```python
# 1. Layouts Config Type
class LayoutsConfigType(ConfigObjectType):
    id = "widgets.layout"
    name = "Layouts"
    description = "Status widget layout profiles"
    icon = "[LAY]"
    sort_order = 10
    data_model = StatusLayout
    location = ConfigLocation.LAYOUTS_DIR
    discovery_type = DiscoveryType.FILE_SCAN
    discovery_pattern = "~/.kollabor-cli/layouts/*.json"

    def list(self) -> List[ConfigItem]:
        layouts_dir = Path.home() / ".kollabor-cli" / "layouts"
        items = []
        for json_file in layouts_dir.glob("*.json"):
            layout = StatusLayout.from_dict(json.loads(json_file.read_text()))
            items.append(ConfigItem(
                name=json_file.stem,
                description=layout.get("description", f"{len(layout.rows)} rows"),
                bundled=json_file.stat().st_mtime < BUNDLED_THRESHOLD,
                # ...
            ))
        return items

    def get_widgets(self, item_name: str = None) -> List[WidgetDef]:
        """Get widgets for editing layouts.

        If item_name is None, show layout picker.
        If item_name is set, show layout editor (rows, widgets).
        """
        if item_name is None:
            return [
                WidgetDef(
                    type="dropdown",
                    label="Active Layout",
                    config_path="widgets.layout.current",
                    options=self._list_layout_names(),
                    help="Select active layout"
                ),
                WidgetDef(
                    type="button",
                    label="Create New Layout",
                    action="create_layout",
                    help="Create a new layout from current configuration"
                ),
            ]
        else:
            # Layout editor widgets
            return [
                # Row 1 widgets, Row 2 widgets, etc.
            ]

# 2. Themes Config Type
class ThemesConfigType(ConfigObjectType):
    id = "ui.theme"
    name = "Themes"
    description = "Color theme selection and customization"
    icon = "[THR]"
    sort_order = 20
    data_model = Theme
    location = ConfigLocation.THEMES_DIR
    discovery_type = DiscoveryType.FILE_SCAN
    discovery_pattern = "~/.kollabor-cli/themes/*.json"

    def get_widgets(self, item_name: str = None) -> List[WidgetDef]:
        return [
            WidgetDef(
                type="dropdown",
                label="Active Theme",
                config_path="ui.theme.selected",
                options=self._list_theme_names(),
                help="Select color theme"
            ),
            WidgetDef(
                type="color_picker",
                label="Primary Color",
                config_path="ui.theme.custom.primary",
                help="Customize primary color"
            ),
            # ... more color pickers
        ]

# 3. Profiles Config Type (uses existing ProfileManager)
class ProfilesConfigType(ConfigObjectType):
    id = "kollabor.llm.profiles"
    name = "Profiles"
    description = "LLM provider and model profiles"
    icon = "[PRF]"
    sort_order = 30
    data_model = LLMProfile
    location = ConfigLocation.MAIN_CONFIG  # Stored in config.json
    discovery_type = DiscoveryType.REGISTRY
    discovery_pattern = "config.kollabor.llm.profiles"

    def get_widgets(self, item_name: str = None) -> List[WidgetDef]:
        return [
            WidgetDef(
                type="dropdown",
                label="Active Profile",
                config_path="kollabor.llm.active_profile",
                options=self._list_profile_names(),
                help="Select active LLM profile"
            ),
            WidgetDef(
                type="button",
                label="Edit Profiles",
                action="edit_profiles",
                help="Open profile editor"
            ),
        ]

# 4. Plugins Config Type
class PluginsConfigType(ConfigObjectType):
    id = "plugins"
    name = "Plugins"
    description = "Plugin configuration and discovery"
    icon = "[PLG]"
    sort_order = 40
    data_model = dict
    location = ConfigLocation.MAIN_CONFIG
    discovery_type = DiscoveryType.FILE_SCAN
    discovery_pattern = "plugins/*_plugin.py"

    def get_widgets(self, item_name: str = None) -> List[WidgetDef]:
        # Discover plugins and create enable/disable widgets
        widgets = []
        for plugin in self._discover_plugins():
            widgets.append(WidgetDef(
                type="checkbox",
                label=plugin.display_name,
                config_path=f"plugins.{plugin.id}.enabled",
                help=plugin.description
            ))
        return widgets
```

### Registration

Config types register at application startup:

```python
# In kollabor/application.py or kollabor/config/__init__.py

def register_config_types(registry: ConfigRegistry) -> None:
    """Register all config object types."""

    registry.register_type(LayoutsConfigType())
    registry.register_type(ThemesConfigType())
    registry.register_type(ProfilesConfigType())
    registry.register_type(PluginsConfigType())

    # Plugins can register their own config types
    for plugin in plugin_factory.get_all_instances():
        if hasattr(plugin, 'get_config_type'):
            registry.register_type(plugin.get_config_type())
```

## UI: /config Command

### Main Picker View

```
+==============================================================================+
|  CONFIGURATION                                              Ctrl+S save | Esc |
+==============================================================================+
|                                                                              |
|  Select configuration object:                                                |
|  ┌──────────────────────────────────────────────────────────────────────┐  |
|  | > Layouts          [LAY]  Widget layout profiles                    |  |
|  |   Themes           [THR]  Color theme selection                      |  |
|  |   Profiles         [PRF]  LLM provider and model profiles            |  |
|  |   Plugins          [PLG]  Plugin configuration                       |  |
|  └──────────────────────────────────────────────────────────────────────┘  |
|                                                                              |
|  Layouts                                                                      |
|  ┌──────────────────────────────────────────────────────────────────────┐  |
|  |   Widget layout profiles stored in ~/.kollabor-cli/layouts/          |  |
|  |                                                                       |  |
|  |   > [default]        6 rows, 18 widgets   Default layout            |  |
|  |     [developer]      3 rows, 8 widgets    Dev-focused               |  |
|  |     [compact]        1 row, 4 widgets     Minimal                   |  |
|  |     [testing]        2 rows, 5 widgets    Test layout (custom)      |  |
|  |     [+ new]          Create new layout...                            |  |
|  └──────────────────────────────────────────────────────────────────────┘  |
|                                                                              |
+------------------------------------------------------------------------------+
|  ↑↓/Tab navigate • Enter select • Esc exit                                   |
+------------------------------------------------------------------------------+
```

### Layout Editor View (after selecting Layouts → default)

```
+==============================================================================+
|  CONFIGURATION > Layouts > default                               Ctrl+S save |
+==============================================================================+
|                                                                              |
|  ACTIVE LAYOUT: default                                                       |
|  ┌──────────────────────────────────────────────────────────────────────┐  |
|  |   Row 1: [endpoint] [model] [status] [stats] [+]                     |  |
|  |   Row 2: [agent] [skills] [tasks] [+]                                |  |
|  |   Row 3: [tmux] [bg-tasks] [+] (hidden)                              |  |
|  |   Row 4: [cwd] [+]                                                   |  |
|  |   ---                                                                |  |
|  |   [+ Add Row]                                                        |  |
|  └──────────────────────────────────────────────────────────────────────┘  |
|                                                                              |
|  Row 1 Widgets                                                                |
|  ┌──────────────────────────────────────────────────────────────────────┐  |
|  |   > [endpoint]         API endpoint display                [remove]  |  |
|  |     [model]            Current LLM model                    [remove]  |  |
|  |     [status]           Connection status                      [remove]  |  |
|  |     [stats]            Token/message stats                     [remove]  |  |
|  |     [+ Add Widget]     Add widget to this row                            |  |
|  └──────────────────────────────────────────────────────────────────────┘  |
|                                                                              |
+------------------------------------------------------------------------------+
|  Tab: Layout Picker • ↑↓: navigate • Enter: edit • d: delete • Esc: back       |
+------------------------------------------------------------------------------+
```

### Theme Editor View

```
+==============================================================================+
|  CONFIGURATION > Themes > lime                                   Ctrl+S save |
+==============================================================================+
|                                                                              |
|  ACTIVE THEME: lime                                                           |
|  ┌──────────────────────────────────────────────────────────────────────┐  |
|  |   Preview                                                             |  |
|  |   ┌────────────────────────────────────────────────────────────────┐  |  |
|  |   │ lime theme preview with actual colors...                       │  |  |
|  |   └────────────────────────────────────────────────────────────────┘  |  |
|  └──────────────────────────────────────────────────────────────────────┘  |
|                                                                              |
|  Colors                                                                       |
|  ┌──────────────────────────────────────────────────────────────────────┐  |
|  |   Primary    [█▓▓▓▓▓▓▓▓]                                                |  |
|  |   Secondary  [█▓▓▓▓▓▓▓▓]                                                |  |
|  |   Response   [█▓▓▓▓▓▓▓▓]                                                |  |
|  |   Text       [█▓▓▓▓▓▓▓▓]                                                |  |
|  └──────────────────────────────────────────────────────────────────────┘  |
|                                                                              |
+------------------------------------------------------------------------------+
|  Tab: Theme Picker • ↑↓: navigate • Enter: edit • Esc: back                    |
+------------------------------------------------------------------------------+
```

## Save Location Routing

Config paths determine save location based on registry routing:

```python
class ConfigService:
    """Extended config service with routing."""

    def __init__(self, config_registry: ConfigRegistry):
        self._registry = config_registry

    def set(self, path: str, value: Any) -> bool:
        """Set a config value, routing to appropriate save location."""
        # Find the config type that handles this path
        config_type = self._registry.get_type_for_path(path)

        if config_type and config_type.location != ConfigLocation.MAIN_CONFIG:
            # Save to external file
            return self._save_to_external(config_type, path, value)
        else:
            # Save to main config (existing behavior)
            return self._config_manager.set(path, value)

    def _save_to_external(self, config_type: ConfigObjectType, path: str, value: Any) -> bool:
        """Save config to external file based on config type."""
        if config_type.location == ConfigLocation.LAYOUTS_DIR:
            layout_name = path.split(".")[-1]  # Extract name from path
            return config_type.save(layout_name, value)
        elif config_type.location == ConfigLocation.THEMES_DIR:
            theme_name = path.split(".")[-1]
            return config_type.save(theme_name, value)
        # etc.
```

## Existing Discovery Patterns (Preserved)

### Layout Discovery

Already implemented in `kollabor/utils/config_utils.py:_initialize_layouts_directory()`:
- Scan `~/.kollabor-cli/layouts/*.json`
- Copy bundled layouts from `bundles/layouts/` on first run
- Preserved in new system via `LayoutsConfigType.discovery_type = FILE_SCAN`

### Theme Discovery

Already implemented in `ConfigWidgetDefinitions.get_available_themes()`:
- Scan `~/.kollabor-cli/themes/*.json`
- Merge with built-in THEMES
- Preserved in new system via `ThemesConfigType.discovery_type = FILE_SCAN`

### Widget Discovery

Already implemented in `StatusWidgetRegistry`:
- Core widgets registered in `kollabor/io/status/core_widgets.py`
- Plugin widgets can register via `registry.register()`
- Preserved for /widgets command

## File Structure

```
kollabor/
  config/
    registry.py              # NEW: ConfigRegistry, ConfigObjectType base
    types/
      __init__.py
      layouts.py             # NEW: LayoutsConfigType
      themes.py              # NEW: ThemesConfigType
      profiles.py            # NEW: ProfilesConfigType (wraps ProfileManager)
      plugins.py             # NEW: PluginsConfigType
  ui/
    config_picker.py         # NEW: /config fullscreen with picker
    config_widgets.py        # MODIFY: Remove modal def, keep widget helpers
  commands/
    system_commands/
      handlers/
        config.py            # MODIFY: Use ConfigRegistry for /config
plugins/
  fullscreen/
    widgets_plugin.py        # NEW: /widgets fullscreen (from status_setup_plugin.py)
```

## Implementation Plan

### Phase 1: Core Registry
- [ ] Create `ConfigRegistry` class in `kollabor/config/registry.py`
- [ ] Create `ConfigObjectType` base class
- [ ] Create `ConfigLocation`, `DiscoveryType` enums
- [ ] Create `ConfigItem` dataclass

### Phase 2: Config Types
- [ ] Create `LayoutsConfigType` in `kollabor/config/types/layouts.py`
- [ ] Create `ThemesConfigType` in `kollabor/config/types/themes.py`
- [ ] Create `ProfilesConfigType` in `kollabor/config/types/profiles.py`
- [ ] Create `PluginsConfigType` in `kollabor/config/types/plugins.py`

### Phase 3: Save Routing
- [ ] Extend `ConfigService` with `ConfigRegistry` routing
- [ ] Implement `_save_to_external()` method
- [ ] Add load routing for external files

### Phase 4: /config UI
- [ ] Create `config_picker.py` with picker UI
- [ ] Integrate `ConfigRegistry` for type listing
- [ ] Implement navigation between types and items
- [ ] Update `system.py` handler to use new picker

### Phase 5: /widgets Command
- [ ] Rename `status_setup_plugin.py` to `widgets_plugin.py`
- [ ] Update command to `/widgets` (aliases: `/w`)
- [ ] Integrate with `LayoutsConfigType` for layout management
- [ ] Add Layout Profile panel for layout switching

### Phase 6: Testing Layout
- [ ] Create `bundles/layouts/testing.json` with test widgets
- [ ] Update test specs to use `/widgets load testing`
- [ ] Document testing workflow

## Testing Layout

```json
// bundles/layouts/testing.json
{
    "version": 1,
    "description": "Test layout with toggle and slider widgets",
    "tags": ["testing", "development"],
    "rows": [
        {
            "id": 1,
            "visible": true,
            "widgets": [
                {"id": "cwd", "width": {"type": "auto"}},
                {"id": "profile", "width": {"type": "auto"}},
                {"id": "model", "width": {"type": "auto"}}
            ]
        },
        {
            "id": 2,
            "visible": true,
            "widgets": [
                {"id": "test-toggle", "width": {"type": "fixed", "value": 10}},
                {"id": "test-slider", "width": {"type": "fixed", "value": 15}}
            ]
        }
    ]
}
```

## Command Reference

### /config (picker)
```
/config                    # Open config picker
/config layouts            # Jump to layouts config
/config themes             # Jump to themes config
```

### /widgets (layout manager)
```
/widgets                   # Open widget layout manager
/w                         # Short alias
/w load testing            # Load testing layout
/w save my-test            # Save current layout as "my-test"
/w delete my-work          # Delete custom layout "my-work"
/w reset                   # Reset to default
```

## Migration Notes

1. **Existing `get_config_widgets()`**: Plugins can still implement this for backward compatibility
2. **Config file locations**: No changes to existing file locations
3. **Status layout key**: Still `status_layout` in config.json for active layout
4. **Theme storage**: Still in config.json for active theme name
5. **Profile storage**: Still in config.json for profiles

## Security

- Config files are JSON only - no code execution
- External file saves restricted to `~/.kollabor-cli/` subdirectories
- Path traversal protection in file names
- Bundled configs cannot be deleted (protected flag)
