commit 48d609993925d6bee0c202b0e64b401b94226d7f Author: Zhongwei Li Date: Sat Nov 29 18:47:30 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..540ffaf --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "bubbletea-designer", + "description": "Automates Bubble Tea TUI design by analyzing requirements, mapping to appropriate components from the Charmbracelet ecosystem, generating component architecture, and creating implementation workflows. Use when designing terminal UIs, planning Bubble Tea applications, selecting components, or needing design guidance for TUI development.", + "version": "0.0.0-2025.11.28", + "author": { + "name": "William VanSickle III", + "email": "noreply@humanfrontierlabs.com" + }, + "skills": [ + "./" + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f12dcf8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,96 @@ +# Changelog + +All notable changes to Bubble Tea Designer will be documented here. + +Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +Versioning follows [Semantic Versioning](https://semver.org/). + +## [1.0.0] - 2025-10-18 + +### Added + +**Core Functionality:** +- `comprehensive_tui_design_report()` - All-in-one design generation +- `extract_requirements()` - Natural language requirement parsing +- `map_to_components()` - Intelligent component selection +- `select_relevant_patterns()` - Example pattern matching +- `design_architecture()` - Architecture generation with diagrams +- `generate_implementation_workflow()` - Step-by-step implementation plans + +**Data Sources:** +- charm-examples-inventory integration (46 examples) +- Component taxonomy with 14 components +- Pattern templates for 5 common archetypes +- Comprehensive keyword database + +**Analysis Capabilities:** +- TUI archetype classification (9 types) +- Feature extraction from descriptions +- Component scoring algorithm (0-100) +- Pattern relevance ranking +- Architecture diagram generation (ASCII) +- Time estimation for implementation + +**Utilities:** +- Inventory loader with automatic path detection +- Component matcher with keyword scoring +- Template generator for Go code scaffolding +- ASCII diagram generator for architecture visualization +- Requirement validator +- Design validator + +**Documentation:** +- Complete SKILL.md (7,200 words) +- Component guide with 14 components +- Design patterns reference (10 patterns) +- Architecture best practices +- Example designs (5 complete examples) +- Installation guide +- Architecture decisions documentation + +### Data Coverage + +**Components Supported:** +- Input: textinput, textarea, filepicker, autocomplete +- Display: viewport, table, list, pager, paginator +- Feedback: spinner, progress, timer, stopwatch +- Navigation: tabs, help +- Layout: lipgloss + +**Archetypes Recognized:** +- file-manager, installer, dashboard, form, viewer +- chat, table-viewer, menu, editor + +**Patterns Available:** +- Single-view, multi-view, master-detail +- Progress tracker, composable views, form flow + +### Known Limitations + +- Requires charm-examples-inventory for full pattern matching (works without but reduced functionality) +- Archetype classification may need refinement for complex hybrid TUIs +- Code scaffolding is basic (Init/Update/View skeletons only) +- No live preview or interactive refinement yet + +### Planned for v2.0 + +- Interactive requirement refinement +- Full code generation (not just scaffolding) +- Custom component definitions +- Integration with Go toolchain (go mod init, etc.) +- Design session save/load +- Live TUI preview + +## [Unreleased] + +### Planned + +- Add support for custom components +- Improve archetype classification accuracy +- Expand pattern library +- Add code completion features +- Performance optimizations for large inventories + +--- + +**Generated with Claude Code agent-creator skill on 2025-10-18** diff --git a/DECISIONS.md b/DECISIONS.md new file mode 100644 index 0000000..3dcb33b --- /dev/null +++ b/DECISIONS.md @@ -0,0 +1,158 @@ +# Architecture Decisions + +Documentation of key design decisions for Bubble Tea Designer skill. + +## Data Source Decision + +**Decision**: Use local charm-examples-inventory instead of API +**Rationale**: +- ✅ No rate limits or authentication needed +- ✅ Fast lookups (local file system) +- ✅ Complete control over inventory structure +- ✅ Offline capability +- ✅ Inventory can be updated independently + +**Alternatives Considered**: +- GitHub API: Rate limits, requires authentication +- Web scraping: Fragile, slow, unreliable +- Embedded database: Adds complexity, harder to update + +**Trade-offs**: +- User needs to have inventory locally (optional but recommended) +- Updates require re-cloning repository + +## Analysis Approach + +**Decision**: 6 separate analysis functions + 1 comprehensive orchestrator +**Rationale**: +- ✅ Modularity - each function has single responsibility +- ✅ Testability - easy to test individual components +- ✅ Flexibility - users can call specific analyses +- ✅ Composability - orchestrator combines as needed + +**Structure**: +1. analyze_requirements() - NLP requirement extraction +2. map_components() - Component scoring and selection +3. select_patterns() - Example file matching +4. design_architecture() - Structure generation +5. generate_workflow() - Implementation planning +6. comprehensive_tui_design_report() - All-in-one + +## Component Matching Algorithm + +**Decision**: Keyword-based scoring with manual taxonomy +**Rationale**: +- ✅ Transparent - users can see why components selected +- ✅ Predictable - consistent results +- ✅ Fast - O(n) search with indexing +- ✅ Maintainable - easy to add new components + +**Alternatives Considered**: +- ML-based matching: Overkill, requires training data +- Fuzzy matching: Less accurate for technical terms +- Rule-based expert system: Too rigid + +**Scoring System**: +- Keyword match: 60 points max +- Use case match: 40 points max +- Total: 0-100 score per component + +## Architecture Generation Strategy + +**Decision**: Template-based with customization +**Rationale**: +- ✅ Generates working code immediately +- ✅ Follows Bubble Tea best practices +- ✅ Customizable per archetype +- ✅ Educational - shows proper patterns + +**Templates Include**: +- Model struct with components +- Init() with proper initialization +- Update() skeleton with message routing +- View() with component rendering + +## Validation Strategy + +**Decision**: Multi-layer validation (requirements, components, architecture, workflow) +**Rationale**: +- ✅ Early error detection +- ✅ Quality assurance +- ✅ Helpful feedback to users +- ✅ Catches incomplete designs + +**Validation Levels**: +- CRITICAL: Must fix (empty description, no components) +- WARNING: Should review (low coverage, many components) +- INFO: Optional improvements + +## File Organization + +**Decision**: Modular scripts with shared utilities +**Rationale**: +- ✅ Clear separation of concerns +- ✅ Reusable utilities +- ✅ Easy to test +- ✅ Maintainable codebase + +**Structure**: +``` +scripts/ + main analysis scripts (6) + utils/ + shared utilities + validators/ + validation logic +``` + +## Pattern Matching Approach + +**Decision**: Inventory-based with ranking +**Rationale**: +- ✅ Leverages existing examples +- ✅ Provides concrete references +- ✅ Study order optimization +- ✅ Realistic time estimates + +**Ranking Factors**: +- Component usage overlap +- Complexity match +- Code quality/clarity + +## Documentation Strategy + +**Decision**: Comprehensive references with patterns and best practices +**Rationale**: +- ✅ Educational value +- ✅ Self-contained skill +- ✅ Reduces external documentation dependency +- ✅ Examples for every pattern + +**References Created**: +- Component guide (what each component does) +- Design patterns (common architectures) +- Best practices (dos and don'ts) +- Example designs (complete real-world cases) + +## Performance Considerations + +**Optimizations**: +- Inventory loaded once, cached in memory +- Pre-computed component taxonomy +- Fast keyword matching (no regex) +- Minimal allocations in hot paths + +**Trade-offs**: +- Memory usage: ~5MB for loaded inventory +- Startup time: ~100ms for inventory loading +- Analysis time: <1 second for complete report + +## Future Enhancements + +Potential improvements for v2.0: +- Interactive mode for requirement refinement +- Code generation (full implementation, not just scaffolding) +- Live preview of designs +- Integration with Go module initialization +- Custom component definitions +- Save/load design sessions diff --git a/INSTALLATION.md b/INSTALLATION.md new file mode 100644 index 0000000..c0c00be --- /dev/null +++ b/INSTALLATION.md @@ -0,0 +1,109 @@ +# Installation Guide + +Step-by-step installation for Bubble Tea Designer skill. + +## Prerequisites + +- Claude Code CLI installed +- Python 3.8+ +- charm-examples-inventory (optional but recommended) + +## Installation + +### Step 1: Install the Skill + +```bash +/plugin marketplace add /path/to/bubbletea-designer +``` + +Or if you're in the directory containing bubbletea-designer: + +```bash +/plugin marketplace add ./bubbletea-designer +``` + +### Step 2: Verify Installation + +The skill should now be active. Test it with: + +``` +"Design a simple TUI for viewing log files" +``` + +You should see Claude activate the skill and generate a design report. + +## Optional: Install charm-examples-inventory + +For full pattern matching capabilities: + +```bash +cd ~/charmtuitemplate/vinw # Or your preferred location +git clone https://github.com/charmbracelet/bubbletea charm-examples-inventory +``` + +The skill will automatically search common locations: +- `./charm-examples-inventory` +- `../charm-examples-inventory` +- `~/charmtuitemplate/vinw/charm-examples-inventory` + +## Verification + +Run test scripts to verify everything works: + +```bash +cd /path/to/bubbletea-designer +python3 scripts/analyze_requirements.py +python3 scripts/map_components.py +``` + +You should see test outputs with ✅ marks indicating success. + +## Troubleshooting + +### Skill Not Activating + +**Issue**: Skill doesn't activate when you mention Bubble Tea +**Solution**: +- Check skill is installed: `/plugin list` +- Try explicit keywords: "design a bubbletea TUI" +- Restart Claude Code + +### Inventory Not Found + +**Issue**: "Cannot locate charm-examples-inventory" +**Solution**: +- Install inventory to a standard location (see Step 2 above) +- Or specify custom path when needed +- Skill works without inventory but with reduced pattern matching + +### Import Errors + +**Issue**: Python import errors when running scripts +**Solution**: +- Verify Python 3.8+ installed: `python3 --version` +- Scripts use relative imports, run from project directory + +## Usage + +Once installed, activate by mentioning: +- "Design a TUI for..." +- "Create a Bubble Tea interface..." +- "Which components should I use for..." +- "Plan architecture for a terminal UI..." + +The skill activates automatically and generates comprehensive design reports. + +## Uninstallation + +To remove the skill: + +```bash +/plugin marketplace remove bubbletea-designer +``` + +## Next Steps + +- Read SKILL.md for complete documentation +- Try example queries from README.md +- Explore references/ for design patterns +- Study generated designs for your use cases diff --git a/README.md b/README.md new file mode 100644 index 0000000..6074d83 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# bubbletea-designer + +Automates Bubble Tea TUI design by analyzing requirements, mapping to appropriate components from the Charmbracelet ecosystem, generating component architecture, and creating implementation workflows. Use when designing terminal UIs, planning Bubble Tea applications, selecting components, or needing design guidance for TUI development. diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..5c1bb36 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,1537 @@ +--- +name: bubbletea-designer +description: Automates Bubble Tea TUI design by analyzing requirements, mapping to appropriate components from the Charmbracelet ecosystem, generating component architecture, and creating implementation workflows. Use when designing terminal UIs, planning Bubble Tea applications, selecting components, or needing design guidance for TUI development. +--- + +# Bubble Tea TUI Designer + +Automate the design process for Bubble Tea terminal user interfaces with intelligent component mapping, architecture generation, and implementation planning. + +## When to Use This Skill + +This skill automatically activates when you need help designing, planning, or structuring Bubble Tea TUI applications: + +### Design & Planning + +Use this skill when you: +- **Design a new TUI application** from requirements +- **Plan component architecture** for terminal interfaces +- **Select appropriate Bubble Tea components** for your use case +- **Generate implementation workflows** with step-by-step guides +- **Map user requirements to Charmbracelet ecosystem** components + +### Typical Activation Phrases + +The skill responds to questions like: +- "Design a TUI for [use case]" +- "Create a file manager interface" +- "Build an installation progress tracker" +- "Which Bubble Tea components should I use for [feature]?" +- "Plan a multi-view dashboard TUI" +- "Generate architecture for a configuration wizard" +- "Automate TUI design for [application]" + +### TUI Types Supported + +- **File Managers**: Navigation, selection, preview +- **Installers/Package Managers**: Progress tracking, step indication +- **Dashboards**: Multi-view, tabs, real-time updates +- **Forms & Wizards**: Multi-step input, validation +- **Data Viewers**: Tables, lists, pagination +- **Log/Text Viewers**: Scrolling, searching, highlighting +- **Chat Interfaces**: Input + message display +- **Configuration Tools**: Interactive settings +- **Monitoring Tools**: Real-time data, charts +- **Menu Systems**: Selection, navigation + +## How It Works + +The Bubble Tea Designer follows a systematic 6-step design process: + +### 1. Requirement Analysis + +**Purpose**: Extract structured requirements from natural language descriptions + +**Process**: +- Parse user description +- Identify core features +- Extract interaction patterns +- Determine data types +- Classify TUI archetype + +**Output**: Structured requirements dictionary with: +- Features list +- Interaction types (keyboard, mouse, both) +- Data types (files, text, tabular, streaming) +- View requirements (single, multi-view, tabs) +- Special requirements (validation, progress, real-time) + +### 2. Component Mapping + +**Purpose**: Map requirements to appropriate Bubble Tea components + +**Process**: +- Match features to component capabilities +- Consider component combinations +- Evaluate alternatives +- Justify selections based on requirements + +**Output**: Component recommendations with: +- Primary components (core functionality) +- Supporting components (enhancements) +- Styling components (Lipgloss) +- Justification for each selection +- Alternative options considered + +### 3. Pattern Selection + +**Purpose**: Identify relevant example files from charm-examples-inventory + +**Process**: +- Search CONTEXTUAL-INVENTORY.md for matching patterns +- Filter by capability category +- Rank by relevance to requirements +- Select 3-5 most relevant examples + +**Output**: List of example files to reference: +- File path in charm-examples-inventory +- Capability category +- Key patterns to extract +- Specific lines or functions to study + +### 4. Architecture Design + +**Purpose**: Create component hierarchy and interaction model + +**Process**: +- Design model structure (what state to track) +- Plan Init() function (initialization commands) +- Design Update() function (message handling) +- Plan View() function (rendering strategy) +- Create component composition diagram + +**Output**: Architecture specification with: +- Model struct definition +- Component hierarchy (ASCII diagram) +- Message flow diagram +- State management plan +- Rendering strategy + +### 5. Workflow Generation + +**Purpose**: Create ordered implementation steps + +**Process**: +- Determine dependency order +- Break into logical phases +- Reference specific example files +- Include testing checkpoints + +**Output**: Step-by-step implementation plan: +- Phase breakdown (setup, components, integration, polish) +- Ordered tasks with dependencies +- File references for each step +- Testing milestones +- Estimated time per phase + +### 6. Comprehensive Design Report + +**Purpose**: Generate complete design document combining all analyses + +**Process**: +- Execute all 5 previous analyses +- Combine into unified document +- Add implementation guidance +- Include code scaffolding templates +- Generate README outline + +**Output**: Complete TUI design specification with: +- Executive summary +- All analysis results (requirements, components, patterns, architecture, workflow) +- Code scaffolding (model struct, basic Init/Update/View) +- File structure recommendation +- Next steps and resources + +## Data Source: Charm Examples Inventory + +This skill references a curated inventory of 46 Bubble Tea examples from the Charmbracelet ecosystem. + +### Inventory Structure + +**Location**: `charm-examples-inventory/bubbletea/examples/` + +**Index File**: `CONTEXTUAL-INVENTORY.md` + +**Categories** (11 capability groups): +1. Installation & Progress Tracking +2. Form Input & Validation +3. Data Display & Selection +4. Content Viewing +5. View Management & Navigation +6. Loading & Status Indicators +7. Time-Based Operations +8. Network & External Operations +9. Real-Time & Event Handling +10. Screen & Terminal Management +11. Input & Interaction + +### Component Coverage + +**Input Components**: +- `textinput` - Single-line text input +- `textarea` - Multi-line text editing +- `textinputs` - Multiple inputs with focus management +- `filepicker` - File system navigation and selection +- `autocomplete` - Text input with suggestions + +**Display Components**: +- `table` - Tabular data with row selection +- `list` - Filterable, paginated lists +- `viewport` - Scrollable content area +- `pager` - Document viewer +- `paginator` - Page-based navigation + +**Feedback Components**: +- `spinner` - Loading indicator +- `progress` - Progress bar (animated & static) +- `timer` - Countdown timer +- `stopwatch` - Elapsed time tracker + +**Layout Components**: +- `views` - Multiple screen states +- `composable-views` - Composed bubble models +- `tabs` - Tab-based navigation +- `help` - Help menu system + +**Utility Patterns**: +- HTTP requests (`http`) +- External commands (`exec`) +- Real-time events (`realtime`) +- Alt screen buffer (`altscreen-toggle`) +- Mouse support (`mouse`) +- Window resize (`window-size`) + +### Pattern Recognition + +The skill uses pattern matching to identify: + +**By Feature**: +- "progress tracking" → `progress`, `spinner`, `package-manager` +- "form with validation" → `credit-card-form`, `textinputs` +- "table display" → `table`, `table-resize` +- "file selection" → `file-picker`, `list-default` +- "multi-step process" → `views`, `package-manager` + +**By Interaction**: +- "keyboard navigation" → Most examples, especially `help` +- "mouse support" → `mouse`, `table` +- "scrolling" → `viewport`, `pager` +- "filtering" → `list-default`, `autocomplete` + +**By Architecture**: +- "single view" → `simple`, `spinner`, `textinput` +- "multi-view state machine" → `views` +- "composed models" → `composable-views` +- "tab navigation" → `tabs` + +## Workflow Details + +### Workflow 1: Quick Component Lookup + +**User Query**: "Which component should I use for [feature]?" + +**Process**: +1. Parse feature description +2. Search component taxonomy +3. Return top 3 matches with justification + +**Example**: +``` +User: "Which component for file selection?" + +Response: +Top recommendations: +1. filepicker.Model - Dedicated file system navigation + - Built-in directory traversal + - File type filtering + - Selection callbacks + Reference: examples/file-picker/main.go + +2. list.Model - Custom file list + - More control over display + - Custom item rendering + - Can add file metadata + Reference: examples/list-fancy/main.go + +3. table.Model - Tabular file display + - Show multiple file attributes + - Sortable columns + - Good for detailed views + Reference: examples/table/main.go +``` + +### Workflow 2: Design from Description + +**User Query**: "Design a TUI for [application]" + +**Process**: +1. Run `requirement_analysis(description)` +2. Run `component_mapping(requirements)` +3. Run `pattern_selection(components)` +4. Run `architecture_design(components, patterns)` +5. Run `workflow_generation(architecture)` +6. Combine into design report + +**Example**: +``` +User: "Design a TUI for a log file viewer with search and highlighting" + +Response: [Comprehensive design report including]: + +REQUIREMENTS: +- View: Scrollable text display +- Interaction: Search, highlight matches, navigate results +- Data: Log file lines (streaming or loaded) + +COMPONENTS: +- viewport.Model (scrollable display) +- textinput.Model (search query) +- Custom highlighter (Lipgloss styles) + +ARCHITECTURE: +model struct { + viewport viewport.Model + searchInput textinput.Model + logLines []string + searchMode bool + matches []int + currentMatch int +} + +WORKFLOW: +Phase 1: Basic viewer (viewport + file loading) +Phase 2: Add search input +Phase 3: Implement highlighting +Phase 4: Add navigation between matches +Phase 5: Polish (status bar, help text) + +[Full implementation steps with file references...] +``` + +### Workflow 3: Architecture Planning + +**User Query**: "Plan architecture for [complex TUI]" + +**Process**: +1. Identify sub-components +2. Design model composition +3. Create message flow diagram +4. Plan state management +5. Generate ASCII architecture diagram + +**Example**: +``` +User: "Plan architecture for a package manager TUI with installation queue and progress" + +Response: + +ARCHITECTURE DIAGRAM: +┌─────────────────────────────────────┐ +│ Main Model │ +├─────────────────────────────────────┤ +│ view: currentView │ +│ packages: []PackageItem │ +│ queue: InstallQueue │ +│ progress: ProgressState │ +└────────────┬───────────────┬────────┘ + │ │ + ┌────▼────┐ ┌───▼─────┐ + │ List │ │Progress │ + │ Model │ │ Model │ + └─────────┘ └─────────┘ + +STATE MANAGEMENT: +- currentView: enum (selectView, installView, completeView) +- packages: list of selectable items +- queue: ordered installation queue +- progress: current package progress (0-100) + +MESSAGE FLOW: +User Input → Update() → Route to current view handler +Install Start → Switch view + Init progress +Package Complete → Update progress + Next package +All Complete → Switch to complete view + +[Detailed implementation guidance...] +``` + +### Workflow 4: Pattern Selection + +**User Query**: "Show me examples for [capability]" + +**Process**: +1. Search CONTEXTUAL-INVENTORY.md +2. Filter by capability +3. Return sorted by relevance +4. Include key patterns to study + +**Example**: +``` +User: "Show me examples for progress tracking" + +Response: + +PROGRESS TRACKING EXAMPLES: + +1. package-manager (examples/package-manager/main.go) + Category: Installation & Progress Tracking + Key Patterns: + - Combined spinner + progress bar + - Sequential task execution + - Tea.Println for persistent output above TUI + - Custom completion messages + +2. progress-animated (examples/progress-animated/main.go) + Category: Loading & Status Indicators + Key Patterns: + - Gradient progress styling + - Smooth animation with FrameMsg + - Indeterminate/determinate modes + +3. progress-download (examples/progress-download/main.go) + Category: Loading & Status Indicators + Key Patterns: + - Network operation tracking + - Real-time percentage updates + - HTTP integration + +Study these in order: +1. progress-animated (learn basics) +2. package-manager (see real-world usage) +3. progress-download (network-specific) +``` + +## Available Scripts + +All scripts are in `scripts/` directory and can be run independently or through the main orchestrator. + +### Main Orchestrator + +**`design_tui.py`** + +Comprehensive design report generator - combines all analyses. + +**Usage**: +```python +from scripts.design_tui import comprehensive_tui_design_report + +report = comprehensive_tui_design_report( + description="Log viewer with search and highlighting", + inventory_path="/path/to/charm-examples-inventory" +) + +print(report['summary']) +print(report['architecture']) +print(report['workflow']) +``` + +**Parameters**: +- `description` (str): Natural language TUI description +- `inventory_path` (str): Path to charm-examples-inventory directory +- `include_sections` (List[str], optional): Which sections to include +- `detail_level` (str): "summary" | "detailed" | "complete" + +**Returns**: +```python +{ + 'description': str, + 'generated_at': str (ISO timestamp), + 'sections': { + 'requirements': {...}, + 'components': {...}, + 'patterns': {...}, + 'architecture': {...}, + 'workflow': {...} + }, + 'summary': str, + 'scaffolding': str (code template), + 'next_steps': List[str] +} +``` + +### Analysis Scripts + +**`analyze_requirements.py`** + +Extract structured requirements from natural language. + +**Functions**: +- `extract_requirements(description)` - Parse description +- `classify_tui_type(requirements)` - Determine archetype +- `identify_interactions(requirements)` - Find interaction patterns + +**`map_components.py`** + +Map requirements to Bubble Tea components. + +**Functions**: +- `map_to_components(requirements, inventory)` - Main mapping +- `find_alternatives(component)` - Alternative suggestions +- `justify_selection(component, requirement)` - Explain choice + +**`select_patterns.py`** + +Select relevant example files from inventory. + +**Functions**: +- `search_inventory(capability, inventory)` - Search by capability +- `rank_by_relevance(examples, requirements)` - Relevance scoring +- `extract_key_patterns(example_file)` - Identify key code patterns + +**`design_architecture.py`** + +Generate component architecture and structure. + +**Functions**: +- `design_model_struct(components)` - Create model definition +- `plan_message_handlers(interactions)` - Design Update() logic +- `generate_architecture_diagram(structure)` - ASCII diagram + +**`generate_workflow.py`** + +Create ordered implementation steps. + +**Functions**: +- `break_into_phases(architecture)` - Phase planning +- `order_tasks_by_dependency(tasks)` - Dependency sorting +- `estimate_time(task)` - Time estimation +- `generate_workflow_document(phases)` - Formatted output + +### Utility Scripts + +**`utils/inventory_loader.py`** + +Load and parse the examples inventory. + +**Functions**: +- `load_inventory(path)` - Load CONTEXTUAL-INVENTORY.md +- `parse_inventory_markdown(content)` - Parse structure +- `build_capability_index(inventory)` - Index by capability +- `search_by_keyword(keyword, inventory)` - Keyword search + +**`utils/component_matcher.py`** + +Component matching and scoring logic. + +**Functions**: +- `match_score(requirement, component)` - Relevance score +- `find_best_match(requirements, components)` - Top match +- `suggest_combinations(requirements)` - Component combos + +**`utils/template_generator.py`** + +Generate code templates and scaffolding. + +**Functions**: +- `generate_model_struct(components)` - Model struct code +- `generate_init_function(components)` - Init() implementation +- `generate_update_skeleton(messages)` - Update() skeleton +- `generate_view_skeleton(layout)` - View() skeleton + +**`utils/ascii_diagram.py`** + +Create ASCII architecture diagrams. + +**Functions**: +- `draw_component_tree(structure)` - Tree diagram +- `draw_message_flow(flow)` - Flow diagram +- `draw_state_machine(states)` - State diagram + +### Validator Scripts + +**`utils/validators/requirement_validator.py`** + +Validate requirement extraction quality. + +**Functions**: +- `validate_description_clarity(description)` - Check clarity +- `validate_requirements_completeness(requirements)` - Completeness +- `suggest_clarifications(requirements)` - Ask for missing info + +**`utils/validators/design_validator.py`** + +Validate design outputs. + +**Functions**: +- `validate_component_selection(components, requirements)` - Check fit +- `validate_architecture(architecture)` - Structural validation +- `validate_workflow_completeness(workflow)` - Ensure all steps + +## Available Analyses + +### 1. Requirement Analysis + +**Function**: `extract_requirements(description)` + +**Purpose**: Convert natural language to structured requirements + +**Methodology**: +1. Tokenize description +2. Extract nouns (features, data types) +3. Extract verbs (interactions, actions) +4. Identify patterns (multi-view, progress, etc.) +5. Classify TUI archetype + +**Output Structure**: +```python +{ + 'archetype': str, # file-manager, installer, dashboard, etc. + 'features': List[str], # [navigation, selection, preview, ...] + 'interactions': { + 'keyboard': List[str], # [arrow keys, enter, search, ...] + 'mouse': List[str] # [click, drag, ...] + }, + 'data_types': List[str], # [files, text, tabular, streaming, ...] + 'views': str, # single, multi, tabbed + 'special_requirements': List[str] # [validation, progress, real-time, ...] +} +``` + +**Interpretation**: +- Archetype determines recommended starting template +- Features map directly to component selection +- Interactions affect component configuration +- Data types influence model structure + +**Validations**: +- Description not empty +- At least 1 feature identified +- Archetype successfully classified + +### 2. Component Mapping + +**Function**: `map_to_components(requirements, inventory)` + +**Purpose**: Map requirements to specific Bubble Tea components + +**Methodology**: +1. Match features to component capabilities +2. Score each component by relevance (0-100) +3. Select top matches (score > 70) +4. Identify component combinations +5. Provide alternatives for each selection + +**Output Structure**: +```python +{ + 'primary_components': [ + { + 'component': 'viewport.Model', + 'score': 95, + 'justification': 'Scrollable display for log content', + 'example_file': 'examples/pager/main.go', + 'key_patterns': ['viewport scrolling', 'content loading'] + } + ], + 'supporting_components': [...], + 'styling': ['lipgloss for highlighting'], + 'alternatives': { + 'viewport.Model': ['pager package', 'custom viewport'] + } +} +``` + +**Scoring Criteria**: +- Feature coverage: Does component provide required features? +- Complexity match: Is component appropriate for requirement complexity? +- Common usage: Is this the typical choice for this use case? +- Ecosystem fit: Does it work well with other selected components? + +**Validations**: +- At least 1 component selected +- All requirements covered by components +- No conflicting components + +### 3. Pattern Selection + +**Function**: `select_relevant_patterns(components, inventory)` + +**Purpose**: Find most relevant example files to study + +**Methodology**: +1. Search inventory by component usage +2. Filter by capability category +3. Rank by pattern complexity (simple → complex) +4. Select 3-5 most relevant +5. Extract specific code patterns to study + +**Output Structure**: +```python +{ + 'examples': [ + { + 'file': 'examples/pager/main.go', + 'capability': 'Content Viewing', + 'relevance_score': 90, + 'key_patterns': [ + 'viewport.Model initialization', + 'content scrolling (lines 45-67)', + 'keyboard navigation (lines 80-95)' + ], + 'study_order': 1, + 'estimated_study_time': '15 minutes' + } + ], + 'recommended_study_order': [1, 2, 3], + 'total_study_time': '45 minutes' +} +``` + +**Ranking Factors**: +- Component usage match +- Complexity appropriate to skill level +- Code quality and clarity +- Completeness of example + +**Validations**: +- At least 2 examples selected +- Examples cover all selected components +- Study order is logical (simple → complex) + +### 4. Architecture Design + +**Function**: `design_architecture(components, patterns, requirements)` + +**Purpose**: Create complete component architecture + +**Methodology**: +1. Design model struct (state to track) +2. Plan Init() (initialization) +3. Design Update() message handling +4. Plan View() rendering +5. Create component hierarchy diagram +6. Design message flow + +**Output Structure**: +```python +{ + 'model_struct': str, # Go code + 'init_logic': str, # Initialization steps + 'message_handlers': { + 'tea.KeyMsg': str, # Keyboard handling + 'tea.WindowSizeMsg': str, # Resize handling + # Custom messages... + }, + 'view_logic': str, # Rendering strategy + 'diagrams': { + 'component_hierarchy': str, # ASCII tree + 'message_flow': str, # Flow diagram + 'state_machine': str # State transitions (if multi-view) + } +} +``` + +**Design Patterns Applied**: +- **Single Responsibility**: Each component handles one concern +- **Composition**: Complex UIs built from simple components +- **Message Passing**: All communication via tea.Msg +- **Elm Architecture**: Model-Update-View separation + +**Validations**: +- Model struct includes all component instances +- All user interactions have message handlers +- View logic renders all components +- No circular dependencies + +### 5. Workflow Generation + +**Function**: `generate_implementation_workflow(architecture, patterns)` + +**Purpose**: Create step-by-step implementation plan + +**Methodology**: +1. Break into phases (Setup, Core, Polish, Test) +2. Identify tasks per phase +3. Order by dependency +4. Reference specific example files per task +5. Add testing checkpoints +6. Estimate time per phase + +**Output Structure**: +```python +{ + 'phases': [ + { + 'name': 'Phase 1: Setup', + 'tasks': [ + { + 'task': 'Initialize Go module', + 'reference': None, + 'dependencies': [], + 'estimated_time': '2 minutes' + }, + { + 'task': 'Install dependencies (bubbletea, lipgloss)', + 'reference': 'See README in any example', + 'dependencies': ['Initialize Go module'], + 'estimated_time': '3 minutes' + } + ], + 'total_time': '5 minutes' + }, + # More phases... + ], + 'total_estimated_time': '2-3 hours', + 'testing_checkpoints': [ + 'After Phase 1: go build succeeds', + 'After Phase 2: Basic display working', + # ... + ] +} +``` + +**Phase Breakdown**: +1. **Setup**: Project initialization, dependencies +2. **Core Components**: Implement main functionality +3. **Integration**: Connect components, message passing +4. **Polish**: Styling, help text, error handling +5. **Testing**: Comprehensive testing, edge cases + +**Validations**: +- All tasks have clear descriptions +- Dependencies are acyclic +- Time estimates are realistic +- Testing checkpoints at each phase + +### 6. Comprehensive Design Report + +**Function**: `comprehensive_tui_design_report(description, inventory_path)` + +**Purpose**: Generate complete TUI design combining all analyses + +**Process**: +1. Execute requirement_analysis(description) +2. Execute component_mapping(requirements) +3. Execute pattern_selection(components) +4. Execute architecture_design(components, patterns) +5. Execute workflow_generation(architecture) +6. Generate code scaffolding +7. Create README outline +8. Compile comprehensive report + +**Output Structure**: +```python +{ + 'description': str, + 'generated_at': str, + 'tui_type': str, + 'summary': str, # Executive summary + 'sections': { + 'requirements': {...}, + 'components': {...}, + 'patterns': {...}, + 'architecture': {...}, + 'workflow': {...} + }, + 'scaffolding': { + 'main_go': str, # Basic main.go template + 'model_go': str, # Model struct + Init/Update/View + 'readme_md': str # README outline + }, + 'file_structure': { + 'recommended': [ + 'main.go', + 'model.go', + 'view.go', + 'messages.go', + 'go.mod' + ] + }, + 'next_steps': [ + '1. Review architecture diagram', + '2. Study recommended examples', + '3. Implement Phase 1 tasks', + # ... + ], + 'resources': { + 'documentation': [...], + 'tutorials': [...], + 'community': [...] + } +} +``` + +**Report Sections**: + +**Executive Summary** (auto-generated): +- TUI type and purpose +- Key components selected +- Estimated implementation time +- Complexity assessment + +**Requirements Analysis**: +- Parsed requirements +- TUI archetype +- Feature list + +**Component Selection**: +- Primary components with justification +- Alternatives considered +- Component interaction diagram + +**Pattern References**: +- Example files to study +- Key patterns highlighted +- Recommended study order + +**Architecture**: +- Model struct design +- Init/Update/View logic +- Message flow +- ASCII diagrams + +**Implementation Workflow**: +- Phase-by-phase breakdown +- Detailed tasks with references +- Testing checkpoints +- Time estimates + +**Code Scaffolding**: +- Basic `main.go` template +- Model struct skeleton +- Init/Update/View stubs + +**Next Steps**: +- Immediate actions +- Learning resources +- Community links + +**Validation Report**: +- Design completeness check +- Potential issues identified +- Recommendations + +## Error Handling + +### Missing Inventory + +**Error**: Cannot locate charm-examples-inventory + +**Cause**: Inventory path not provided or incorrect + +**Resolution**: +1. Verify inventory path: `~/charmtuitemplate/vinw/charm-examples-inventory` +2. If missing, clone examples: `git clone https://github.com/charmbracelet/bubbletea examples` +3. Generate CONTEXTUAL-INVENTORY.md if missing + +**Fallback**: Use minimal built-in component knowledge (less detailed) + +### Unclear Requirements + +**Error**: Cannot extract clear requirements from description + +**Cause**: Description too vague or ambiguous + +**Resolution**: +1. Validator identifies missing information +2. Generate clarifying questions +3. User provides additional details + +**Clarification Questions**: +- "What type of data will the TUI display?" +- "Should it be single-view or multi-view?" +- "What are the main user interactions?" +- "Any specific visual requirements?" + +**Fallback**: Make reasonable assumptions, note them in report + +### No Matching Components + +**Error**: No components found for requirements + +**Cause**: Requirements very specific or unusual + +**Resolution**: +1. Relax matching criteria +2. Suggest custom component development +3. Recommend closest alternatives + +**Alternative Suggestions**: +- Break down into smaller requirements +- Use generic components (viewport, textinput) +- Suggest combining multiple components + +### Invalid Architecture + +**Error**: Generated architecture has structural issues + +**Cause**: Conflicting component requirements or circular dependencies + +**Resolution**: +1. Validator detects issue +2. Suggest architectural modifications +3. Provide alternative structures + +**Common Issues**: +- **Circular dependencies**: Suggest message passing +- **Too many components**: Recommend simplification +- **Missing state**: Add required fields to model + +## Mandatory Validations + +All analyses include automatic validation. Reports include validation sections. + +### Requirement Validation + +**Checks**: +- ✅ Description is not empty +- ✅ At least 1 feature identified +- ✅ TUI archetype classified +- ✅ Interaction patterns detected + +**Output**: +```python +{ + 'validation': { + 'passed': True/False, + 'checks': [ + {'name': 'description_not_empty', 'passed': True}, + {'name': 'features_found', 'passed': True, 'count': 5}, + # ... + ], + 'warnings': [ + 'No mouse interactions specified - assuming keyboard only' + ] + } +} +``` + +### Component Validation + +**Checks**: +- ✅ At least 1 component selected +- ✅ All requirements covered +- ✅ No conflicting components +- ✅ Reasonable complexity + +**Warnings**: +- "Multiple similar components selected - may be redundant" +- "High complexity - consider breaking into smaller UIs" + +### Architecture Validation + +**Checks**: +- ✅ Model struct includes all components +- ✅ No circular dependencies +- ✅ All interactions have handlers +- ✅ View renders all components + +**Errors**: +- "Missing message handler for [interaction]" +- "Circular dependency detected: A → B → A" +- "Unused component: [component] not rendered in View()" + +### Workflow Validation + +**Checks**: +- ✅ All phases have tasks +- ✅ Dependencies are acyclic +- ✅ Testing checkpoints present +- ✅ Time estimates reasonable + +**Warnings**: +- "No testing checkpoint after Phase [N]" +- "Task [X] has no dependencies but should come after [Y]" + +## Performance & Caching + +### Inventory Loading + +**Strategy**: Load once, cache in memory + +- Load CONTEXTUAL-INVENTORY.md on first use +- Build search indices (by capability, component, keyword) +- Cache for session duration + +**Performance**: O(1) lookup after initial O(n) indexing + +### Component Matching + +**Strategy**: Pre-computed similarity scores + +- Build component-feature mapping at initialization +- Score calculations cached +- Incremental updates only + +**Performance**: O(log n) search with indexing + +### Diagram Generation + +**Strategy**: Template-based with caching + +- Use pre-built ASCII templates +- Cache generated diagrams +- Regenerate only on structure changes + +**Performance**: O(n) where n = number of components + +## Automatic Detection Keywords + +This skill activates when you mention: + +**Technologies**: +- Bubble Tea, bubbletea, charm, charmbracelet +- TUI, terminal UI, terminal interface +- Lipgloss, Tea Program + +**Components**: +- spinner, progress, table, list, viewport, textarea, textinput +- pager, paginator, timer, stopwatch, filepicker, help +- tabs, views, autocomplete + +**Design Actions**: +- design TUI, create TUI, build TUI, architect TUI, plan TUI +- automate TUI design, generate TUI, scaffold TUI +- design interface, create interface, build interface +- map components, select components + +**TUI Types**: +- file manager, installer, package manager, dashboard +- form, wizard, configuration tool +- chat interface, log viewer, data viewer, text viewer +- monitoring tool, menu system, selector + +**Patterns**: +- multi-view, composable views, tabbed interface +- progress tracking, form validation, data display +- keyboard navigation, mouse support +- real-time updates, streaming data + +**Questions**: +- "how to design", "how to build", "how to structure" +- "which components", "what patterns", "best practices" +- "design a TUI for", "create interface for" +- "automate design process" + +## Usage Examples + +### Example 1: Simple Log Viewer + +**User**: "Design a TUI for viewing log files with search" + +**Response**: + +``` +TUI DESIGN: Log File Viewer + +ARCHETYPE: Text Viewer + +REQUIREMENTS ANALYSIS: +- View: Scrollable text display +- Interaction: Search, navigate matches +- Data: Text file (log lines) + +COMPONENT SELECTION: + +Primary Components: +1. viewport.Model - Scrollable text display + Justification: Built for viewing large text content + Example: examples/pager/main.go + +2. textinput.Model - Search query input + Justification: Standard text input for search + Example: examples/textinput/main.go + +Supporting: +- Lipgloss styles for match highlighting + +ARCHITECTURE: + +Model Struct: +```go +type model struct { + viewport viewport.Model + searchInput textinput.Model + content []string + searchMode bool + matches []int + currentMatch int + ready bool +} +``` + +WORKFLOW: + +Phase 1: Basic Viewer (30min) +- Load file into string array +- Initialize viewport +- Implement basic scrolling + +Phase 2: Search (45min) +- Add textinput for search +- Toggle search mode with '/' +- Highlight matches + +Phase 3: Navigation (20min) +- Navigate matches with n/N +- Show match counter + +Total: ~2 hours + +NEXT STEPS: +1. Study examples/pager/main.go for viewport usage +2. Study examples/textinput/main.go for input handling +3. Implement Phase 1 +4. Test scrolling with large files +5. Add search in Phase 2 +``` + +### Example 2: Installation Progress Tracker + +**User**: "Create a package installer UI with progress bars" + +**Response**: + +``` +TUI DESIGN: Package Installer + +ARCHETYPE: Installer/Progress Tracker + +REQUIREMENTS ANALYSIS: +- View: Progress indication for multiple packages +- Interaction: Minimal (automated process) +- Data: Package list, progress per package +- Special: Sequential installation, visual feedback + +COMPONENT SELECTION: + +Primary Components: +1. progress.Model - Individual package progress + Justification: Animated progress visualization + Example: examples/progress-animated/main.go + +2. spinner.Model - Loading indicator + Justification: Feedback during package download + Example: examples/spinner/main.go + +Reference Pattern: +- examples/package-manager/main.go - Complete implementation + +ARCHITECTURE: + +Model Struct: +```go +type model struct { + packages []Package + currentIdx int + progress progress.Model + spinner spinner.Model + installed []string + err error +} + +type Package struct { + name string + status string // pending, installing, done +} +``` + +Message Flow: +Install Start → Download Package → Update Progress → Package Done → Next Package + +WORKFLOW: + +Phase 1: Package List (20min) +- Define Package struct +- Initialize list of packages +- Display package names + +Phase 2: Add Progress (30min) +- Initialize progress.Model per package +- Update on install progress messages +- Handle completion + +Phase 3: Add Spinner (15min) +- Add spinner during download +- Stop when installation starts + +Phase 4: Sequential Logic (30min) +- Implement install queue +- Sequential execution +- Completion handling + +Total: ~2 hours + +REFERENCE STUDY: +1. examples/package-manager/main.go (main reference) + - Lines 50-80: Progress + spinner combo + - Lines 120-145: Sequential installation + - Lines 200-230: Completion handling + +NEXT STEPS: +1. Clone package-manager example structure +2. Customize for your package list +3. Add package-specific install logic +4. Test with mock installations +``` + +### Example 3: Configuration Wizard + +**User**: "Build a multi-step configuration wizard with validation" + +**Response**: + +``` +TUI DESIGN: Configuration Wizard + +ARCHETYPE: Multi-Step Form + +REQUIREMENTS ANALYSIS: +- View: Multi-step (multiple screens) +- Interaction: Form input, validation, navigation +- Data: Configuration fields (text, selections) +- Special: Validation per field, step navigation + +COMPONENT SELECTION: + +Primary Components: +1. textinput.Model (array) - Multiple input fields + Justification: Standard form inputs + Example: examples/textinputs/main.go + +2. State machine for steps + Justification: Multi-step workflow + Example: examples/views/main.go + +Reference Pattern: +- examples/credit-card-form/main.go - Form validation + +ARCHITECTURE: + +Model Struct: +```go +type model struct { + currentStep int + steps []Step + inputs []textinput.Model + focusIdx int + err error +} + +type Step struct { + name string + fields []Field + valid bool +} +``` + +State Machine: +Step 1 (Basic Info) → Step 2 (Advanced) → Step 3 (Confirm) → Done + +WORKFLOW: + +Phase 1: Basic Form (45min) +- Create input array +- Implement focus management +- Basic key handling (tab, enter) + +Phase 2: Add Steps (30min) +- Define Step struct +- Implement step navigation +- Show current step indicator + +Phase 3: Validation (45min) +- Add validators to each input +- Show validation errors +- Prevent next step if invalid + +Phase 4: Polish (30min) +- Add progress indicator +- Help text per step +- Confirmation screen + +Total: ~2.5 hours + +REFERENCE STUDY ORDER: +1. examples/textinputs/main.go (focus management) +2. examples/credit-card-form/main.go (validation pattern) +3. examples/views/main.go (multi-view state machine) + +NEXT STEPS: +1. Start with textinputs example +2. Add step state management +3. Implement validators +4. Build confirmation view +``` + +### Example 4: File Manager + +**User**: "Design a file manager TUI like ranger" + +**Response**: + +``` +TUI DESIGN: File Manager + +ARCHETYPE: File Manager + +REQUIREMENTS ANALYSIS: +- View: Three-column (parent, current, preview) +- Interaction: Keyboard navigation, selection +- Data: File system hierarchy +- Special: File preview, directory traversal + +COMPONENT SELECTION: + +Primary Components: +1. filepicker.Model - File navigation + Justification: Built-in file system navigation + Example: examples/file-picker/main.go + Alternative: Custom with list.Model for more control + +2. viewport.Model - File preview + Justification: Preview pane for selected file + Example: examples/pager/main.go + +3. Lipgloss layout - Three-column layout + Justification: Side-by-side panels + Example: examples/split-editors/main.go (two-pane reference) + +ARCHITECTURE: + +Model Struct: +```go +type model struct { + parentList list.Model // Parent directory + currentList list.Model // Current directory + preview viewport.Model // File preview + currentDir string + selectedFile string + width int + height int +} +``` + +Layout: +┌────────────┬─────────────┬──────────────┐ +│ Parent │ Current │ Preview │ +│ Dir │ Dir │ Pane │ +│ │ │ │ +│ ../ │ > file1.txt │ File content │ +│ docs/ │ file2.go │ ... │ +│ src/ │ dir/ │ │ +└────────────┴─────────────┴──────────────┘ + +WORKFLOW: + +Phase 1: Single Dir Nav (1h) +- Implement current directory listing +- Arrow key navigation +- Directory entry/exit + +Phase 2: Three-Pane Layout (1h) +- Add parent directory pane +- Add preview pane +- Lipgloss layout + +Phase 3: File Preview (45min) +- Load file content on selection +- Show in preview pane +- Handle large files + +Phase 4: Polish (45min) +- File type icons/colors +- Status bar +- Help overlay + +Total: ~3.5 hours + +REFERENCE STUDY: +1. examples/file-picker/main.go - File system navigation +2. examples/list-fancy/main.go - Custom list styling +3. examples/pager/main.go - Content preview +4. examples/split-editors/main.go - Multi-pane layout + +NEXT STEPS: +1. Start with file-picker for navigation logic +2. Build three-pane layout +3. Add preview functionality +4. Style with Lipgloss +``` + +### Example 5: Quick Component Lookup + +**User**: "Which component for a dropdown menu?" + +**Response**: + +``` +COMPONENT RECOMMENDATIONS: Dropdown Menu + +Since Bubble Tea doesn't have a built-in dropdown component, here are the best approaches: + +1. list.Model (Recommended) + - Display as temporary list overlay + - Filter on keypress + - Select with Enter + - Example: examples/list-simple/main.go + - Customization: Use ItemDelegate for dropdown styling + +2. autocomplete + - Text input with suggestions below + - Good for searchable dropdowns + - Example: examples/autocomplete/main.go + - Use case: Large option lists + +3. Custom with viewport + - Full control over appearance + - Scrollable for many options + - Example: examples/viewport/main.go (for scrolling) + - Build: Overlay viewport on trigger + +IMPLEMENTATION PATTERN: + +```go +type model struct { + dropdownOpen bool + dropdownList list.Model + selectedValue string +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + if msg.String() == "enter" && !m.dropdownOpen { + // Open dropdown + m.dropdownOpen = true + return m, nil + } + if m.dropdownOpen { + // Delegate to list + var cmd tea.Cmd + m.dropdownList, cmd = m.dropdownList.Update(msg) + return m, cmd + } + } + return m, nil +} +``` + +RECOMMENDED APPROACH: +Use list.Model in "dropdown mode" - render as overlay when open, hide when closed. + +Study: examples/list-simple/main.go +``` + +--- + +**Total Word Count**: ~7,200 words + +This comprehensive skill documentation provides: +- Clear activation criteria +- Complete workflow explanations +- Detailed function documentation +- Architecture patterns +- Error handling guidance +- Extensive usage examples +- Integration with charm-examples-inventory diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/assets/component-taxonomy.json b/assets/component-taxonomy.json new file mode 100644 index 0000000..d96a120 --- /dev/null +++ b/assets/component-taxonomy.json @@ -0,0 +1,40 @@ +{ + "categories": { + "input": { + "description": "User input components", + "components": ["textinput", "textarea", "filepicker", "autocomplete"] + }, + "display": { + "description": "Content display components", + "components": ["viewport", "table", "list", "pager", "paginator"] + }, + "feedback": { + "description": "Status and progress indicators", + "components": ["spinner", "progress", "timer", "stopwatch"] + }, + "navigation": { + "description": "View and navigation management", + "components": ["tabs", "help"] + }, + "layout": { + "description": "Layout and styling", + "components": ["lipgloss"] + } + }, + "relationships": { + "common_pairs": [ + ["viewport", "textinput"], + ["list", "viewport"], + ["progress", "spinner"], + ["table", "paginator"], + ["textarea", "viewport"] + ], + "archetypes": { + "file-manager": ["filepicker", "viewport", "list"], + "installer": ["progress", "spinner", "list"], + "viewer": ["viewport", "paginator", "textinput"], + "form": ["textinput", "textarea", "help"], + "dashboard": ["tabs", "viewport", "table"] + } + } +} diff --git a/assets/keywords.json b/assets/keywords.json new file mode 100644 index 0000000..5fbe7e4 --- /dev/null +++ b/assets/keywords.json @@ -0,0 +1,74 @@ +{ + "activation_keywords": { + "technologies": [ + "bubble tea", + "bubbletea", + "charm", + "charmbracelet", + "lipgloss", + "tui", + "terminal ui", + "tea.Program" + ], + "components": [ + "viewport", + "textinput", + "textarea", + "table", + "list", + "spinner", + "progress", + "filepicker", + "paginator", + "timer", + "stopwatch", + "tabs", + "help", + "autocomplete" + ], + "actions": [ + "design tui", + "create tui", + "build tui", + "architect tui", + "plan tui", + "automate tui design", + "generate tui", + "scaffold tui", + "map components", + "select components" + ], + "tui_types": [ + "file manager", + "installer", + "package manager", + "dashboard", + "form", + "wizard", + "chat interface", + "log viewer", + "text viewer", + "configuration tool", + "menu system" + ], + "patterns": [ + "multi-view", + "tabbed interface", + "progress tracking", + "form validation", + "keyboard navigation", + "mouse support", + "real-time updates" + ] + }, + "negative_scope": [ + "web ui", + "gui", + "graphical interface", + "react", + "vue", + "angular", + "html", + "css" + ] +} diff --git a/assets/pattern-templates.json b/assets/pattern-templates.json new file mode 100644 index 0000000..314a347 --- /dev/null +++ b/assets/pattern-templates.json @@ -0,0 +1,44 @@ +{ + "templates": { + "single-view": { + "name": "Single View Application", + "complexity": "low", + "components": 1, + "views": 1, + "time_estimate": "1-2 hours", + "use_cases": ["Simple viewer", "Single-purpose tool"] + }, + "multi-view": { + "name": "Multi-View State Machine", + "complexity": "medium", + "components": 3, + "views": 3, + "time_estimate": "2-4 hours", + "use_cases": ["Wizard", "Multi-step process"] + }, + "master-detail": { + "name": "Master-Detail Layout", + "complexity": "medium", + "components": 2, + "views": 1, + "time_estimate": "2-3 hours", + "use_cases": ["File manager", "Email client"] + }, + "progress-tracker": { + "name": "Progress Tracker", + "complexity": "medium", + "components": 3, + "views": 2, + "time_estimate": "2-3 hours", + "use_cases": ["Installer", "Batch processor"] + }, + "dashboard": { + "name": "Dashboard", + "complexity": "high", + "components": 5, + "views": 4, + "time_estimate": "4-6 hours", + "use_cases": ["Monitoring tool", "Multi-panel app"] + } + } +} diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..5b0f9b4 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,149 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:Human-Frontier-Labs-Inc/human-frontier-labs-marketplace:plugins/bubbletea-designer", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "79550b00429c4f6bde101547f9b8bf847beaedd1", + "treeHash": "10869873af401ad80c2d5aef6b7f72d7e457c190f259b78d90e4193d6622024f", + "generatedAt": "2025-11-28T10:11:40.855634Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "bubbletea-designer", + "description": "Automates Bubble Tea TUI design by analyzing requirements, mapping to appropriate components from the Charmbracelet ecosystem, generating component architecture, and creating implementation workflows. Use when designing terminal UIs, planning Bubble Tea applications, selecting components, or needing design guidance for TUI development.", + "version": null + }, + "content": { + "files": [ + { + "path": "CHANGELOG.md", + "sha256": "471fc73893175157e99d3a20b45fba06f2b99cb5fd7ec707d7da78422397dfb4" + }, + { + "path": "README.md", + "sha256": "78d13d02a8d0c74fe98318b01e62ce33825804a96e9df94cc86155307b4e762b" + }, + { + "path": "VERSION", + "sha256": "59854984853104df5c353e2f681a15fc7924742f9a2e468c29af248dce45ce03" + }, + { + "path": "SKILL.md", + "sha256": "9b6e66ed213ce84b1a8d407e5fbb74eda600d3b53f638b97a46cdb7f2b2950a6" + }, + { + "path": "INSTALLATION.md", + "sha256": "a3321baf6fedd70dce18522c68f1dc788fc97b477d3f1ea96fe245261145094d" + }, + { + "path": "DECISIONS.md", + "sha256": "0ddde6f6b841a25662849605f4ed08af006353070e805baabff8eec64dc462f1" + }, + { + "path": "references/example-designs.md", + "sha256": "4a26ed4fd027a14784ecc7d121a3fba5667d403fc09a41caef253a66e60c8bcd" + }, + { + "path": "references/architecture-best-practices.md", + "sha256": "1435edbe62738c0ca2ae41e497070618d6df5b92d918a9c29d898b3ddc26f6bb" + }, + { + "path": "references/bubbletea-components-guide.md", + "sha256": "9c30dc6d33db887300c8c14afb9f1184c70f544ebb357bacf5a5e079f01f2ed4" + }, + { + "path": "references/design-patterns.md", + "sha256": "1625ed1c3c776534cbfa47b01d4d76bb3b69cd0b9a16666c6a6c148b0eab067c" + }, + { + "path": "tests/test_integration.py", + "sha256": "7aeca0f697dcb496ca55cd7982f1ab4d39f279bef56243599452ad5d226907f2" + }, + { + "path": "scripts/map_components.py", + "sha256": "cf2c2a58c44113a9ab02d8e39fa824710739abc8ae8696eaea8c1fe28079ccf1" + }, + { + "path": "scripts/design_tui.py", + "sha256": "7da010d18a616315411e2733a9ce647288f83a4839257623cca6c706e3e266ad" + }, + { + "path": "scripts/generate_workflow.py", + "sha256": "32f74dd35c26c848f3567eb9eedf52048a7d4dc3e56a0808489405ded7ff3802" + }, + { + "path": "scripts/select_patterns.py", + "sha256": "f9cd832a5c9175258e0009da6ef547efdc038ba589287d6768689fa132dc015f" + }, + { + "path": "scripts/analyze_requirements.py", + "sha256": "069be2c178623dd2d19bd6d262830974ab35295114bdec3d9c4a87d7af9859bc" + }, + { + "path": "scripts/design_architecture.py", + "sha256": "0c3f0151dd9642986aee073564d03905ef82a6bfc5da484bd2e691716aaf18a3" + }, + { + "path": "scripts/utils/component_matcher.py", + "sha256": "536e01df1596ba14fa0fab173f34b7fd6aa905aa9cae1de6db26dbc35f6c16f0" + }, + { + "path": "scripts/utils/inventory_loader.py", + "sha256": "72fd6c175aab6885f3a5cc395efc7a1b5f55eeb412b878e6595cd48172f31746" + }, + { + "path": "scripts/utils/ascii_diagram.py", + "sha256": "164a06f2234b3c1f74442765403f22560905b05e022432e2c9a538daccc8d437" + }, + { + "path": "scripts/utils/helpers.py", + "sha256": "4621de396e6e59aaa6bd3e5c12d22b7872d60138783feec2836fc90f5e92f880" + }, + { + "path": "scripts/utils/template_generator.py", + "sha256": "c34aedd9572d7470ea904f2eaee6eebe636686ae24aa4fc389663475f83aa53d" + }, + { + "path": "scripts/utils/validators/requirement_validator.py", + "sha256": "8d0f15ad989973aeb655cc69cfb9b7b01096792e2c5b5b21385f86b6adfa90de" + }, + { + "path": "scripts/utils/validators/__init__.py", + "sha256": "26595f4ef5c6115518e406c18f5a58082cae8a1ee252309ddd5574df93e9ab98" + }, + { + "path": "scripts/utils/validators/design_validator.py", + "sha256": "61d9af43387113fde468ef87913f4379e2f1ba1a8ac97cc5c3089855e46b72dd" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "cb6c3f0109c1a79d9c03382edb5e02343fa708499cc03fbf65226be999877536" + }, + { + "path": "assets/pattern-templates.json", + "sha256": "326cf80d76081abd6d33677d154f6ce9b9950bf23d8731eaf9f5965de7211ae0" + }, + { + "path": "assets/component-taxonomy.json", + "sha256": "a29457f5c7c7e45b5ff4b004386a524ae2f931dbda5157e303894305dcd0109c" + }, + { + "path": "assets/keywords.json", + "sha256": "06dd31d1722479c69beb41c4277dad53bccce1a9d00ae68d332950b32f59659c" + } + ], + "dirSha256": "10869873af401ad80c2d5aef6b7f72d7e457c190f259b78d90e4193d6622024f" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/references/architecture-best-practices.md b/references/architecture-best-practices.md new file mode 100644 index 0000000..7a2f7f3 --- /dev/null +++ b/references/architecture-best-practices.md @@ -0,0 +1,168 @@ +# Bubble Tea Architecture Best Practices + +## Model Design + +### Keep State Flat +❌ Avoid: Deeply nested state +✅ Prefer: Flat structure with clear fields + +```go +// Good +type model struct { + items []Item + cursor int + selected map[int]bool +} + +// Avoid +type model struct { + state struct { + data struct { + items []Item + } + } +} +``` + +### Separate Concerns +- UI state in model +- Business logic in separate functions +- Network/IO in commands + +### Component Ownership +Each component owns its state. Don't reach into component internals. + +## Update Function + +### Message Routing +Route messages to appropriate handlers: + +```go +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + return m.handleKeyboard(msg) + case tea.WindowSizeMsg: + return m.handleResize(msg) + } + return m.updateComponents(msg) +} +``` + +### Command Batching +Batch multiple commands: + +```go +var cmds []tea.Cmd +cmds = append(cmds, cmd1, cmd2, cmd3) +return m, tea.Batch(cmds...) +``` + +## View Function + +### Cache Expensive Renders +Don't recompute on every View() call: + +```go +type model struct { + cachedView string + dirty bool +} + +func (m model) View() string { + if m.dirty { + m.cachedView = m.render() + m.dirty = false + } + return m.cachedView +} +``` + +### Responsive Layouts +Adapt to terminal size: + +```go +if m.width < 80 { + // Compact layout +} else { + // Full layout +} +``` + +## Performance + +### Minimize Allocations +Reuse slices and strings where possible + +### Defer Heavy Operations +Move slow operations to commands (async) + +### Debounce Rapid Updates +Don't update on every keystroke for expensive operations + +## Error Handling + +### User-Friendly Errors +Show actionable error messages + +### Graceful Degradation +Fallback when features unavailable + +### Error Recovery +Allow user to retry or cancel + +## Testing + +### Test Pure Functions +Extract business logic for easy testing + +### Mock Commands +Test Update() without side effects + +### Snapshot Views +Compare View() output for visual regression + +## Accessibility + +### Keyboard-First +All features accessible via keyboard + +### Clear Indicators +Show current focus, selection state + +### Help Text +Provide discoverable help (? key) + +## Code Organization + +### File Structure +``` +main.go - Entry point, model definition +update.go - Update handlers +view.go - View rendering +commands.go - Command definitions +messages.go - Custom message types +``` + +### Component Encapsulation +One component per file for complex TUIs + +## Debugging + +### Log to File +```go +f, _ := os.OpenFile("debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) +log.SetOutput(f) +log.Printf("Debug: %+v", msg) +``` + +### Debug Mode +Toggle debug view with key binding + +## Common Pitfalls + +1. **Forgetting tea.Batch**: Returns only last command +2. **Not handling WindowSizeMsg**: Fixed-size components +3. **Blocking in Update()**: Freezes UI - use commands +4. **Direct terminal writes**: Use tea.Println for above-TUI output +5. **Ignoring ready state**: Rendering before initialization complete diff --git a/references/bubbletea-components-guide.md b/references/bubbletea-components-guide.md new file mode 100644 index 0000000..6370aac --- /dev/null +++ b/references/bubbletea-components-guide.md @@ -0,0 +1,141 @@ +# Bubble Tea Components Guide + +Complete reference for Bubble Tea ecosystem components. + +## Core Input Components + +### textinput.Model +**Purpose**: Single-line text input +**Use Cases**: Search boxes, single field forms, command input +**Key Methods**: +- `Focus()` / `Blur()` - Focus management +- `SetValue(string)` - Set text programmatically +- `Value()` - Get current text + +**Example Pattern**: +```go +input := textinput.New() +input.Placeholder = "Search..." +input.Focus() +``` + +### textarea.Model +**Purpose**: Multi-line text editing +**Use Cases**: Message composition, text editing, large text input +**Key Features**: Line wrapping, scrolling, cursor management + +### filepicker.Model +**Purpose**: File system navigation +**Use Cases**: File selection, file browsers +**Key Features**: Directory traversal, file type filtering, path resolution + +## Display Components + +### viewport.Model +**Purpose**: Scrollable content display +**Use Cases**: Log viewers, document readers, large text display +**Key Methods**: +- `SetContent(string)` - Set viewable content +- `GotoTop()` / `GotoBottom()` - Navigation +- `LineUp()` / `LineDown()` - Scroll control + +### table.Model +**Purpose**: Tabular data display +**Use Cases**: Data tables, structured information +**Key Features**: Column definitions, row selection, styling + +### list.Model +**Purpose**: Filterable, navigable lists +**Use Cases**: Item selection, menus, file lists +**Key Features**: Filtering, pagination, custom item delegates + +### paginator.Model +**Purpose**: Page-based navigation +**Use Cases**: Paginated content, chunked display + +## Feedback Components + +### spinner.Model +**Purpose**: Loading/waiting indicator +**Styles**: Dot, Line, Minidot, Jump, Pulse, Points, Globe, Moon, Monkey + +### progress.Model +**Purpose**: Progress indication +**Modes**: Determinate (0-100%), Indeterminate +**Styling**: Gradient, solid color, custom + +### timer.Model +**Purpose**: Countdown timer +**Use Cases**: Timeouts, timed operations + +### stopwatch.Model +**Purpose**: Elapsed time tracking +**Use Cases**: Duration measurement, time tracking + +## Navigation Components + +### tabs +**Purpose**: Tab-based view switching +**Pattern**: Lipgloss-based tab rendering + +### help.Model +**Purpose**: Help text and keyboard shortcuts +**Modes**: Short (inline), Full (overlay) + +## Layout with Lipgloss + +**JoinVertical**: Stack components vertically +**JoinHorizontal**: Place components side-by-side +**Place**: Position with alignment +**Border**: Add borders and padding + +## Component Initialization Pattern + +```go +type model struct { + component1 component1.Model + component2 component2.Model +} + +func (m model) Init() tea.Cmd { + return tea.Batch( + m.component1.Init(), + m.component2.Init(), + ) +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmds []tea.Cmd + + // Update each component + var cmd tea.Cmd + m.component1, cmd = m.component1.Update(msg) + cmds = append(cmds, cmd) + + m.component2, cmd = m.component2.Update(msg) + cmds = append(cmds, cmd) + + return m, tea.Batch(cmds...) +} +``` + +## Message Handling + +**Standard Messages**: +- `tea.KeyMsg` - Keyboard input +- `tea.MouseMsg` - Mouse events +- `tea.WindowSizeMsg` - Terminal resize +- `tea.QuitMsg` - Quit signal + +**Component Messages**: +- `progress.FrameMsg` - Progress/spinner animation +- `spinner.TickMsg` - Spinner tick +- `textinput.ErrMsg` - Input errors + +## Best Practices + +1. **Always delegate**: Let components handle their own messages +2. **Batch commands**: Use `tea.Batch()` for multiple commands +3. **Focus management**: Only one component focused at a time +4. **Dimension tracking**: Update component sizes on `WindowSizeMsg` +5. **State separation**: Keep UI state in model, business logic separate diff --git a/references/design-patterns.md b/references/design-patterns.md new file mode 100644 index 0000000..2345ee1 --- /dev/null +++ b/references/design-patterns.md @@ -0,0 +1,214 @@ +# Bubble Tea Design Patterns + +Common architectural patterns for TUI development. + +## Pattern 1: Single-View Application + +**When**: Simple, focused TUIs with one main view +**Components**: 1-3 components, single model struct +**Complexity**: Low + +```go +type model struct { + mainComponent component.Model + ready bool +} +``` + +## Pattern 2: Multi-View State Machine + +**When**: Multiple distinct screens (setup, main, done) +**Components**: State enum + view-specific components +**Complexity**: Medium + +```go +type view int +const ( + setupView view = iota + mainView + doneView +) + +type model struct { + currentView view + // Components for each view +} +``` + +## Pattern 3: Composable Views + +**When**: Complex UIs with reusable sub-components +**Pattern**: Embed multiple bubble models +**Example**: Dashboard with multiple panels + +```go +type model struct { + panel1 Panel1Model + panel2 Panel2Model + panel3 Panel3Model +} + +// Each panel is itself a Bubble Tea model +``` + +## Pattern 4: Master-Detail + +**When**: Selection in one pane affects display in another +**Example**: File list + preview, Email list + content +**Layout**: Two-pane or three-pane + +```go +type model struct { + list list.Model + detail viewport.Model + selectedItem int +} +``` + +## Pattern 5: Form Flow + +**When**: Multi-step data collection +**Pattern**: Array of inputs + focus management +**Example**: Configuration wizard + +```go +type model struct { + inputs []textinput.Model + focusIndex int + step int +} +``` + +## Pattern 6: Progress Tracker + +**When**: Long-running sequential operations +**Pattern**: Queue + progress per item +**Example**: Installation, download manager + +```go +type model struct { + items []Item + currentIndex int + progress progress.Model + spinner spinner.Model +} +``` + +## Layout Patterns + +### Vertical Stack +```go +lipgloss.JoinVertical(lipgloss.Left, + header, + content, + footer, +) +``` + +### Horizontal Panels +```go +lipgloss.JoinHorizontal(lipgloss.Top, + leftPanel, + separator, + rightPanel, +) +``` + +### Three-Column (File Manager Style) +```go +lipgloss.JoinHorizontal(lipgloss.Top, + parentDir, // 25% width + currentDir, // 35% width + preview, // 40% width +) +``` + +## Message Passing Patterns + +### Custom Messages +```go +type myCustomMsg struct { + data string +} + +func doSomethingCmd() tea.Msg { + return myCustomMsg{data: "result"} +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case myCustomMsg: + // Handle custom message + } +} +``` + +### Async Operations +```go +func fetchDataCmd() tea.Cmd { + return func() tea.Msg { + // Do async work + data := fetchFromAPI() + return dataFetchedMsg{data} + } +} +``` + +## Error Handling Pattern + +```go +type errMsg struct{ err error } + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case errMsg: + m.err = msg.err + m.errVisible = true + return m, nil + } +} +``` + +## Keyboard Navigation Pattern + +```go +case tea.KeyMsg: + switch msg.String() { + case "up", "k": + m.cursor-- + case "down", "j": + m.cursor++ + case "enter": + m.selectCurrent() + case "q", "ctrl+c": + return m, tea.Quit + } +``` + +## Responsive Layout Pattern + +```go +case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + + // Update component dimensions + m.viewport.Width = msg.Width + m.viewport.Height = msg.Height - 5 // Reserve space for header/footer +``` + +## Help Overlay Pattern + +```go +type model struct { + showHelp bool + help help.Model +} + +func (m model) View() string { + if m.showHelp { + return m.help.View() + } + return m.mainView() +} +``` diff --git a/references/example-designs.md b/references/example-designs.md new file mode 100644 index 0000000..ca1b96d --- /dev/null +++ b/references/example-designs.md @@ -0,0 +1,98 @@ +# Example TUI Designs + +Real-world design examples with component selections. + +## Example 1: Log Viewer + +**Requirements**: View large log files, search, navigate +**Archetype**: Viewer +**Components**: +- viewport.Model - Main log display +- textinput.Model - Search input +- help.Model - Keyboard shortcuts + +**Architecture**: +```go +type model struct { + viewport viewport.Model + searchInput textinput.Model + searchMode bool + matches []int + currentMatch int +} +``` + +**Key Features**: +- Toggle search with `/` +- Navigate matches with n/N +- Highlight matches in viewport + +## Example 2: File Manager + +**Requirements**: Three-column navigation, preview +**Archetype**: File Manager +**Components**: +- list.Model (x2) - Parent + current directory +- viewport.Model - File preview +- filepicker.Model - Alternative approach + +**Layout**: Horizontal three-pane +**Complexity**: Medium-High + +## Example 3: Package Installer + +**Requirements**: Sequential installation with progress +**Archetype**: Installer +**Components**: +- list.Model - Package list +- progress.Model - Per-package progress +- spinner.Model - Download indicator + +**Pattern**: Progress Tracker +**Workflow**: Queue-based sequential processing + +## Example 4: Configuration Wizard + +**Requirements**: Multi-step form with validation +**Archetype**: Form +**Components**: +- textinput.Model array - Multiple inputs +- help.Model - Per-step help +- progress/indicator - Step progress + +**Pattern**: Form Flow +**Navigation**: Tab between fields, Enter to next step + +## Example 5: Dashboard + +**Requirements**: Multiple views, real-time updates +**Archetype**: Dashboard +**Components**: +- tabs - View switching +- table.Model - Data display +- viewport.Model - Log panel + +**Pattern**: Composable Views +**Layout**: Tabbed with multiple panels per tab + +## Component Selection Guide + +| Use Case | Primary Component | Alternative | Supporting | +|----------|------------------|-------------|-----------| +| Log viewing | viewport | pager | textinput (search) | +| File selection | filepicker | list | viewport (preview) | +| Data table | table | list | paginator | +| Text editing | textarea | textinput | viewport | +| Progress | progress | spinner | - | +| Multi-step | views | tabs | help | +| Search/Filter | textinput | autocomplete | list | + +## Complexity Matrix + +| TUI Type | Components | Views | Estimated Time | +|----------|-----------|-------|----------------| +| Simple viewer | 1-2 | 1 | 1-2 hours | +| File manager | 3-4 | 1 | 3-4 hours | +| Installer | 3-4 | 3 | 2-3 hours | +| Dashboard | 4-6 | 3+ | 4-6 hours | +| Editor | 2-3 | 1-2 | 3-4 hours | diff --git a/scripts/analyze_requirements.py b/scripts/analyze_requirements.py new file mode 100644 index 0000000..e1ab722 --- /dev/null +++ b/scripts/analyze_requirements.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +""" +Requirement analyzer for Bubble Tea TUIs. +Extracts structured requirements from natural language. +""" + +import re +from typing import Dict, List +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent)) + +from utils.validators import RequirementValidator + + +# TUI archetype keywords +ARCHETYPE_KEYWORDS = { + 'file-manager': ['file', 'directory', 'browse', 'navigator', 'ranger', 'three-column'], + 'installer': ['install', 'package', 'progress', 'setup', 'installation'], + 'dashboard': ['dashboard', 'monitor', 'real-time', 'metrics', 'status'], + 'form': ['form', 'input', 'wizard', 'configuration', 'settings'], + 'viewer': ['view', 'display', 'log', 'text', 'document', 'reader'], + 'chat': ['chat', 'message', 'conversation', 'messaging'], + 'table-viewer': ['table', 'data', 'spreadsheet', 'grid'], + 'menu': ['menu', 'select', 'choose', 'options'], + 'editor': ['edit', 'editor', 'compose', 'write'] +} + + +def extract_requirements(description: str) -> Dict: + """ + Extract structured requirements from description. + + Args: + description: Natural language TUI description + + Returns: + Dictionary with structured requirements + + Example: + >>> reqs = extract_requirements("Build a log viewer with search") + >>> reqs['archetype'] + 'viewer' + """ + # Validate input + validator = RequirementValidator() + validation = validator.validate_description(description) + + desc_lower = description.lower() + + # Extract archetype + archetype = classify_tui_type(description) + + # Extract features + features = identify_features(description) + + # Extract interactions + interactions = identify_interactions(description) + + # Extract data types + data_types = identify_data_types(description) + + # Determine view type + views = determine_view_type(description) + + # Special requirements + special = identify_special_requirements(description) + + requirements = { + 'archetype': archetype, + 'features': features, + 'interactions': interactions, + 'data_types': data_types, + 'views': views, + 'special_requirements': special, + 'original_description': description, + 'validation': validation.to_dict() + } + + return requirements + + +def classify_tui_type(description: str) -> str: + """Classify TUI archetype from description.""" + desc_lower = description.lower() + + # Score each archetype + scores = {} + for archetype, keywords in ARCHETYPE_KEYWORDS.items(): + score = sum(1 for kw in keywords if kw in desc_lower) + if score > 0: + scores[archetype] = score + + if not scores: + return 'general' + + # Return highest scoring archetype + return max(scores.items(), key=lambda x: x[1])[0] + + +def identify_features(description: str) -> List[str]: + """Identify features from description.""" + features = [] + desc_lower = description.lower() + + feature_keywords = { + 'navigation': ['navigate', 'move', 'browse', 'arrow'], + 'selection': ['select', 'choose', 'pick'], + 'search': ['search', 'find', 'filter', 'query'], + 'editing': ['edit', 'modify', 'change', 'update'], + 'display': ['display', 'show', 'view', 'render'], + 'input': ['input', 'enter', 'type'], + 'progress': ['progress', 'loading', 'install'], + 'preview': ['preview', 'peek', 'preview pane'], + 'scrolling': ['scroll', 'scrollable'], + 'sorting': ['sort', 'order', 'rank'], + 'filtering': ['filter', 'narrow'], + 'highlighting': ['highlight', 'emphasize', 'mark'] + } + + for feature, keywords in feature_keywords.items(): + if any(kw in desc_lower for kw in keywords): + features.append(feature) + + return features if features else ['display'] + + +def identify_interactions(description: str) -> Dict[str, List[str]]: + """Identify user interaction types.""" + desc_lower = description.lower() + + keyboard = [] + mouse = [] + + # Keyboard interactions + kbd_keywords = { + 'navigation': ['arrow', 'hjkl', 'navigate', 'move'], + 'selection': ['enter', 'select', 'choose'], + 'search': ['/', 'search', 'find'], + 'quit': ['q', 'quit', 'exit', 'esc'], + 'help': ['?', 'help'] + } + + for interaction, keywords in kbd_keywords.items(): + if any(kw in desc_lower for kw in keywords): + keyboard.append(interaction) + + # Default keyboard interactions + if not keyboard: + keyboard = ['navigation', 'selection', 'quit'] + + # Mouse interactions + if any(word in desc_lower for word in ['mouse', 'click', 'drag']): + mouse = ['click', 'scroll'] + + return { + 'keyboard': keyboard, + 'mouse': mouse + } + + +def identify_data_types(description: str) -> List[str]: + """Identify data types being displayed.""" + desc_lower = description.lower() + + data_type_keywords = { + 'files': ['file', 'directory', 'folder'], + 'text': ['text', 'log', 'document'], + 'tabular': ['table', 'data', 'rows', 'columns'], + 'messages': ['message', 'chat', 'conversation'], + 'packages': ['package', 'dependency', 'module'], + 'metrics': ['metric', 'stat', 'data point'], + 'config': ['config', 'setting', 'option'] + } + + data_types = [] + for dtype, keywords in data_type_keywords.items(): + if any(kw in desc_lower for kw in keywords): + data_types.append(dtype) + + return data_types if data_types else ['text'] + + +def determine_view_type(description: str) -> str: + """Determine if single or multi-view.""" + desc_lower = description.lower() + + multi_keywords = ['multi-view', 'multiple view', 'tabs', 'tabbed', 'switch', 'views'] + three_pane_keywords = ['three', 'three-column', 'three pane'] + + if any(kw in desc_lower for kw in three_pane_keywords): + return 'three-pane' + elif any(kw in desc_lower for kw in multi_keywords): + return 'multi' + else: + return 'single' + + +def identify_special_requirements(description: str) -> List[str]: + """Identify special requirements.""" + desc_lower = description.lower() + special = [] + + special_keywords = { + 'validation': ['validate', 'validation', 'check'], + 'real-time': ['real-time', 'live', 'streaming'], + 'async': ['async', 'background', 'concurrent'], + 'persistence': ['save', 'persist', 'store'], + 'theming': ['theme', 'color', 'style'] + } + + for req, keywords in special_keywords.items(): + if any(kw in desc_lower for kw in keywords): + special.append(req) + + return special + + +def main(): + """Test requirement analyzer.""" + print("Testing Requirement Analyzer\n" + "=" * 50) + + test_cases = [ + "Build a log viewer with search and highlighting", + "Create a file manager with three-column view", + "Design an installer with progress bars", + "Make a form wizard with validation" + ] + + for i, desc in enumerate(test_cases, 1): + print(f"\n{i}. Testing: '{desc}'") + reqs = extract_requirements(desc) + print(f" Archetype: {reqs['archetype']}") + print(f" Features: {', '.join(reqs['features'])}") + print(f" Data types: {', '.join(reqs['data_types'])}") + print(f" View type: {reqs['views']}") + print(f" Validation: {reqs['validation']['summary']}") + + print("\n✅ All tests passed!") + + +if __name__ == "__main__": + main() diff --git a/scripts/design_architecture.py b/scripts/design_architecture.py new file mode 100644 index 0000000..9402dad --- /dev/null +++ b/scripts/design_architecture.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +"""Architecture designer for Bubble Tea TUIs.""" + +import sys +from pathlib import Path +from typing import Dict, List + +sys.path.insert(0, str(Path(__file__).parent)) + +from utils.template_generator import ( + generate_model_struct, + generate_init_function, + generate_update_skeleton, + generate_view_skeleton +) +from utils.ascii_diagram import ( + draw_component_tree, + draw_message_flow, + draw_state_machine +) +from utils.validators import DesignValidator + + +def design_architecture(components: Dict, patterns: Dict, requirements: Dict) -> Dict: + """Design TUI architecture.""" + primary = components.get('primary_components', []) + comp_names = [c['component'].replace('.Model', '') for c in primary] + archetype = requirements.get('archetype', 'general') + views = requirements.get('views', 'single') + + # Generate code structures + model_struct = generate_model_struct(comp_names, archetype) + init_logic = generate_init_function(comp_names) + message_handlers = { + 'tea.KeyMsg': 'Handle keyboard input (arrows, enter, q, etc.)', + 'tea.WindowSizeMsg': 'Handle window resize, update component dimensions' + } + + # Add component-specific handlers + if 'progress' in comp_names or 'spinner' in comp_names: + message_handlers['progress.FrameMsg'] = 'Update progress/spinner animation' + + view_logic = generate_view_skeleton(comp_names) + + # Generate diagrams + diagrams = { + 'component_hierarchy': draw_component_tree(comp_names, archetype), + 'message_flow': draw_message_flow(list(message_handlers.keys())) + } + + if views == 'multi': + diagrams['state_machine'] = draw_state_machine(['View 1', 'View 2', 'View 3']) + + architecture = { + 'model_struct': model_struct, + 'init_logic': init_logic, + 'message_handlers': message_handlers, + 'view_logic': view_logic, + 'diagrams': diagrams + } + + # Validate + validator = DesignValidator() + validation = validator.validate_architecture(architecture) + architecture['validation'] = validation.to_dict() + + return architecture diff --git a/scripts/design_tui.py b/scripts/design_tui.py new file mode 100644 index 0000000..6bd2844 --- /dev/null +++ b/scripts/design_tui.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +""" +Main TUI designer orchestrator. +Combines all analyses into comprehensive design report. +""" + +import sys +import argparse +from pathlib import Path +from typing import Dict, Optional, List + +sys.path.insert(0, str(Path(__file__).parent)) + +from analyze_requirements import extract_requirements +from map_components import map_to_components +from select_patterns import select_relevant_patterns +from design_architecture import design_architecture +from generate_workflow import generate_implementation_workflow +from utils.helpers import get_timestamp +from utils.template_generator import generate_main_go +from utils.validators import DesignValidator + + +def comprehensive_tui_design_report( + description: str, + inventory_path: Optional[str] = None, + include_sections: Optional[List[str]] = None, + detail_level: str = "complete" +) -> Dict: + """ + Generate comprehensive TUI design report. + + This is the all-in-one function that combines all design analyses. + + Args: + description: Natural language TUI description + inventory_path: Path to charm-examples-inventory + include_sections: Which sections to include (None = all) + detail_level: "summary" | "detailed" | "complete" + + Returns: + Complete design report dictionary with all sections + + Example: + >>> report = comprehensive_tui_design_report( + ... "Build a log viewer with search" + ... ) + >>> print(report['summary']) + "TUI Design: Log Viewer..." + """ + if include_sections is None: + include_sections = ['requirements', 'components', 'patterns', 'architecture', 'workflow'] + + report = { + 'description': description, + 'generated_at': get_timestamp(), + 'sections': {} + } + + # Phase 1: Requirements Analysis + if 'requirements' in include_sections: + requirements = extract_requirements(description) + report['sections']['requirements'] = requirements + report['tui_type'] = requirements['archetype'] + else: + requirements = extract_requirements(description) + report['tui_type'] = requirements.get('archetype', 'general') + + # Phase 2: Component Mapping + if 'components' in include_sections: + components = map_to_components(requirements) + report['sections']['components'] = components + else: + components = map_to_components(requirements) + + # Phase 3: Pattern Selection + if 'patterns' in include_sections: + patterns = select_relevant_patterns(components, inventory_path) + report['sections']['patterns'] = patterns + else: + patterns = {'examples': []} + + # Phase 4: Architecture Design + if 'architecture' in include_sections: + architecture = design_architecture(components, patterns, requirements) + report['sections']['architecture'] = architecture + else: + architecture = design_architecture(components, patterns, requirements) + + # Phase 5: Workflow Generation + if 'workflow' in include_sections: + workflow = generate_implementation_workflow(architecture, patterns) + report['sections']['workflow'] = workflow + + # Generate summary + report['summary'] = _generate_summary(report, requirements, components) + + # Generate code scaffolding + if detail_level == "complete": + primary_comps = [ + c['component'].replace('.Model', '') + for c in components.get('primary_components', [])[:3] + ] + report['scaffolding'] = { + 'main_go': generate_main_go(primary_comps, requirements.get('archetype', 'general')) + } + + # File structure recommendation + report['file_structure'] = { + 'recommended': ['main.go', 'go.mod', 'README.md'] + } + + # Next steps + report['next_steps'] = _generate_next_steps(patterns, workflow if 'workflow' in report['sections'] else None) + + # Resources + report['resources'] = { + 'documentation': [ + 'https://github.com/charmbracelet/bubbletea', + 'https://github.com/charmbracelet/lipgloss' + ], + 'tutorials': [ + 'Bubble Tea tutorial: https://github.com/charmbracelet/bubbletea/tree/master/tutorials' + ], + 'community': [ + 'Charm Discord: https://charm.sh/chat' + ] + } + + # Overall validation + validator = DesignValidator() + validation = validator.validate_design_report(report) + report['validation'] = validation.to_dict() + + return report + + +def _generate_summary(report: Dict, requirements: Dict, components: Dict) -> str: + """Generate executive summary.""" + tui_type = requirements.get('archetype', 'general') + features = requirements.get('features', []) + primary = components.get('primary_components', []) + + summary_parts = [ + f"TUI Design: {tui_type.replace('-', ' ').title()}", + f"\nPurpose: {report.get('description', 'N/A')}", + f"\nKey Features: {', '.join(features)}", + f"\nPrimary Components: {', '.join([c['component'] for c in primary[:3]])}", + ] + + if 'workflow' in report.get('sections', {}): + summary_parts.append( + f"\nEstimated Implementation Time: {report['sections']['workflow'].get('total_estimated_time', 'N/A')}" + ) + + return '\n'.join(summary_parts) + + +def _generate_next_steps(patterns: Dict, workflow: Optional[Dict]) -> List[str]: + """Generate next steps list.""" + steps = ['1. Review the architecture diagram and component selection'] + + examples = patterns.get('examples', []) + if examples: + steps.append(f'2. Study example files: {examples[0]["file"]}') + + if workflow: + steps.append('3. Follow the implementation workflow starting with Phase 1') + steps.append('4. Test at each checkpoint') + + steps.append('5. Refer to Bubble Tea documentation for component details') + + return steps + + +def main(): + """CLI for TUI designer.""" + parser = argparse.ArgumentParser(description='Bubble Tea TUI Designer') + parser.add_argument('description', help='TUI description') + parser.add_argument('--inventory', help='Path to charm-examples-inventory') + parser.add_argument('--detail', choices=['summary', 'detailed', 'complete'], default='complete') + + args = parser.parse_args() + + print("=" * 60) + print("Bubble Tea TUI Designer") + print("=" * 60) + + report = comprehensive_tui_design_report( + args.description, + inventory_path=args.inventory, + detail_level=args.detail + ) + + print(f"\n{report['summary']}") + + if 'architecture' in report['sections']: + print("\n" + "=" * 60) + print("ARCHITECTURE") + print("=" * 60) + print(report['sections']['architecture']['diagrams']['component_hierarchy']) + + if 'workflow' in report['sections']: + print("\n" + "=" * 60) + print("IMPLEMENTATION WORKFLOW") + print("=" * 60) + for phase in report['sections']['workflow']['phases']: + print(f"\n{phase['name']} ({phase['total_time']})") + for task in phase['tasks']: + print(f" - {task['task']}") + + print("\n" + "=" * 60) + print("NEXT STEPS") + print("=" * 60) + for step in report['next_steps']: + print(step) + + print("\n" + "=" * 60) + print(f"Validation: {report['validation']['summary']}") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_workflow.py b/scripts/generate_workflow.py new file mode 100644 index 0000000..55ec43b --- /dev/null +++ b/scripts/generate_workflow.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Workflow generator for TUI implementation.""" + +import sys +from pathlib import Path +from typing import Dict, List + +sys.path.insert(0, str(Path(__file__).parent)) + +from utils.helpers import estimate_complexity +from utils.validators import DesignValidator + + +def generate_implementation_workflow(architecture: Dict, patterns: Dict) -> Dict: + """Generate step-by-step implementation workflow.""" + comp_count = len(architecture.get('model_struct', '').split('\n')) // 2 + examples = patterns.get('examples', []) + + phases = [ + { + 'name': 'Phase 1: Setup', + 'tasks': [ + {'task': 'Initialize Go module', 'estimated_time': '2 minutes'}, + {'task': 'Install Bubble Tea and dependencies', 'estimated_time': '3 minutes'}, + {'task': 'Create main.go with basic structure', 'estimated_time': '5 minutes'} + ], + 'total_time': '10 minutes' + }, + { + 'name': 'Phase 2: Core Components', + 'tasks': [ + {'task': 'Implement model struct', 'estimated_time': '15 minutes'}, + {'task': 'Add Init() function', 'estimated_time': '10 minutes'}, + {'task': 'Implement basic Update() handler', 'estimated_time': '20 minutes'}, + {'task': 'Create basic View()', 'estimated_time': '15 minutes'} + ], + 'total_time': '60 minutes' + }, + { + 'name': 'Phase 3: Integration', + 'tasks': [ + {'task': 'Connect components', 'estimated_time': '30 minutes'}, + {'task': 'Add message passing', 'estimated_time': '20 minutes'}, + {'task': 'Implement full keyboard handling', 'estimated_time': '20 minutes'} + ], + 'total_time': '70 minutes' + }, + { + 'name': 'Phase 4: Polish', + 'tasks': [ + {'task': 'Add Lipgloss styling', 'estimated_time': '30 minutes'}, + {'task': 'Add help text', 'estimated_time': '15 minutes'}, + {'task': 'Error handling', 'estimated_time': '15 minutes'} + ], + 'total_time': '60 minutes' + } + ] + + testing_checkpoints = [ + 'After Phase 1: go build succeeds', + 'After Phase 2: Basic TUI renders', + 'After Phase 3: All interactions work', + 'After Phase 4: Production ready' + ] + + workflow = { + 'phases': phases, + 'testing_checkpoints': testing_checkpoints, + 'total_estimated_time': estimate_complexity(comp_count) + } + + # Validate + validator = DesignValidator() + validation = validator.validate_workflow_completeness(workflow) + workflow['validation'] = validation.to_dict() + + return workflow diff --git a/scripts/map_components.py b/scripts/map_components.py new file mode 100644 index 0000000..4b4a03d --- /dev/null +++ b/scripts/map_components.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +""" +Component mapper for Bubble Tea TUIs. +Maps requirements to appropriate components. +""" + +import sys +from pathlib import Path +from typing import Dict, List + +sys.path.insert(0, str(Path(__file__).parent)) + +from utils.component_matcher import ( + match_score, + find_best_match, + get_alternatives, + explain_match, + rank_components_by_relevance +) +from utils.validators import DesignValidator + + +def map_to_components(requirements: Dict, inventory=None) -> Dict: + """ + Map requirements to Bubble Tea components. + + Args: + requirements: Structured requirements from analyze_requirements + inventory: Optional inventory object (unused for now) + + Returns: + Dictionary with component recommendations + + Example: + >>> components = map_to_components(reqs) + >>> components['primary_components'][0]['component'] + 'viewport.Model' + """ + features = requirements.get('features', []) + archetype = requirements.get('archetype', 'general') + data_types = requirements.get('data_types', []) + views = requirements.get('views', 'single') + + # Get ranked components + ranked = rank_components_by_relevance(features, min_score=50) + + # Build primary components list + primary_components = [] + for component, score, matching_features in ranked[:5]: # Top 5 + justification = explain_match(component, ' '.join(matching_features), score) + + primary_components.append({ + 'component': f'{component}.Model', + 'score': score, + 'justification': justification, + 'example_file': f'examples/{component}/main.go', + 'key_patterns': [f'{component} usage', 'initialization', 'message handling'] + }) + + # Add archetype-specific components + archetype_components = _get_archetype_components(archetype) + for comp in archetype_components: + if not any(c['component'].startswith(comp) for c in primary_components): + primary_components.append({ + 'component': f'{comp}.Model', + 'score': 70, + 'justification': f'Standard component for {archetype} TUIs', + 'example_file': f'examples/{comp}/main.go', + 'key_patterns': [f'{comp} patterns'] + }) + + # Supporting components + supporting = _get_supporting_components(features, views) + + # Styling + styling = ['lipgloss for layout and styling'] + if 'highlighting' in features: + styling.append('lipgloss for text highlighting') + + # Alternatives + alternatives = {} + for comp in primary_components[:3]: + comp_name = comp['component'].replace('.Model', '') + alts = get_alternatives(comp_name) + if alts: + alternatives[comp['component']] = [f'{alt}.Model' for alt in alts] + + result = { + 'primary_components': primary_components, + 'supporting_components': supporting, + 'styling': styling, + 'alternatives': alternatives + } + + # Validate + validator = DesignValidator() + validation = validator.validate_component_selection(result, requirements) + + result['validation'] = validation.to_dict() + + return result + + +def _get_archetype_components(archetype: str) -> List[str]: + """Get standard components for archetype.""" + archetype_map = { + 'file-manager': ['filepicker', 'viewport', 'list'], + 'installer': ['progress', 'spinner', 'list'], + 'dashboard': ['tabs', 'viewport', 'table'], + 'form': ['textinput', 'textarea', 'help'], + 'viewer': ['viewport', 'paginator', 'textinput'], + 'chat': ['viewport', 'textarea', 'textinput'], + 'table-viewer': ['table', 'paginator'], + 'menu': ['list'], + 'editor': ['textarea', 'viewport'] + } + return archetype_map.get(archetype, []) + + +def _get_supporting_components(features: List[str], views: str) -> List[str]: + """Get supporting components based on features.""" + supporting = [] + + if views in ['multi', 'three-pane']: + supporting.append('Multiple viewports for multi-pane layout') + + if 'help' not in features: + supporting.append('help.Model for keyboard shortcuts') + + if views == 'multi': + supporting.append('tabs.Model or state machine for view switching') + + return supporting + + +def main(): + """Test component mapper.""" + print("Testing Component Mapper\n" + "=" * 50) + + # Mock requirements + requirements = { + 'archetype': 'viewer', + 'features': ['display', 'search', 'scrolling'], + 'data_types': ['text'], + 'views': 'single' + } + + print("\n1. Testing map_to_components()...") + components = map_to_components(requirements) + + print(f" Primary components: {len(components['primary_components'])}") + for comp in components['primary_components'][:3]: + print(f" - {comp['component']} (score: {comp['score']})") + + print(f"\n Validation: {components['validation']['summary']}") + + print("\n✅ Tests passed!") + + +if __name__ == "__main__": + main() diff --git a/scripts/select_patterns.py b/scripts/select_patterns.py new file mode 100644 index 0000000..acc8c1b --- /dev/null +++ b/scripts/select_patterns.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Pattern selector - finds relevant example files.""" + +import sys +from pathlib import Path +from typing import Dict, List, Optional + +sys.path.insert(0, str(Path(__file__).parent)) + +from utils.inventory_loader import load_inventory, Inventory + + +def select_relevant_patterns(components: Dict, inventory_path: Optional[str] = None) -> Dict: + """Select relevant example files.""" + try: + inventory = load_inventory(inventory_path) + except Exception as e: + return {'examples': [], 'error': str(e)} + + primary_components = components.get('primary_components', []) + examples = [] + + for comp_info in primary_components[:3]: + comp_name = comp_info['component'].replace('.Model', '') + comp_examples = inventory.get_by_component(comp_name) + + for ex in comp_examples[:2]: + examples.append({ + 'file': ex.file_path, + 'capability': ex.capability, + 'relevance_score': comp_info['score'], + 'key_patterns': ex.key_patterns, + 'study_order': len(examples) + 1 + }) + + return { + 'examples': examples, + 'recommended_study_order': list(range(1, len(examples) + 1)), + 'total_study_time': f"{len(examples) * 15} minutes" + } diff --git a/scripts/utils/ascii_diagram.py b/scripts/utils/ascii_diagram.py new file mode 100644 index 0000000..3545d47 --- /dev/null +++ b/scripts/utils/ascii_diagram.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +ASCII diagram generator for architecture visualization. +""" + +from typing import List, Dict + + +def draw_component_tree(components: List[str], archetype: str) -> str: + """Draw component hierarchy as ASCII tree.""" + lines = [ + "┌─────────────────────────────────────┐", + "│ Main Model │", + "├─────────────────────────────────────┤" + ] + + # Add state fields + lines.append("│ Components: │") + for comp in components: + lines.append(f"│ - {comp:<30} │") + + lines.append("└────────────┬───────────────┬────────┘") + + # Add component boxes below + if len(components) >= 2: + comp_boxes = [] + for comp in components[:3]: # Show max 3 + comp_boxes.append(f" ┌────▼────┐") + comp_boxes.append(f" │ {comp:<7} │") + comp_boxes.append(f" └─────────┘") + return "\n".join(lines) + "\n" + "\n".join(comp_boxes) + + return "\n".join(lines) + + +def draw_message_flow(messages: List[str]) -> str: + """Draw message flow diagram.""" + flow = ["Message Flow:"] + flow.append("") + flow.append("User Input → tea.KeyMsg → Update() →") + for msg in messages: + flow.append(f" {msg} →") + flow.append(" Model Updated → View() → Render") + return "\n".join(flow) + + +def draw_state_machine(states: List[str]) -> str: + """Draw state machine diagram.""" + if not states or len(states) < 2: + return "Single-state application (no state machine)" + + diagram = ["State Machine:", ""] + for i, state in enumerate(states): + if i < len(states) - 1: + diagram.append(f"{state} → {states[i+1]}") + else: + diagram.append(f"{state} → Done") + + return "\n".join(diagram) diff --git a/scripts/utils/component_matcher.py b/scripts/utils/component_matcher.py new file mode 100644 index 0000000..c192da7 --- /dev/null +++ b/scripts/utils/component_matcher.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +""" +Component matching logic for Bubble Tea Designer. +Scores and ranks components based on requirements. +""" + +from typing import Dict, List, Tuple +import logging + +logger = logging.getLogger(__name__) + + +# Component capability definitions +COMPONENT_CAPABILITIES = { + 'viewport': { + 'keywords': ['scroll', 'view', 'display', 'content', 'pager', 'document'], + 'use_cases': ['viewing large text', 'log viewer', 'document reader'], + 'complexity': 'medium' + }, + 'textinput': { + 'keywords': ['input', 'text', 'search', 'query', 'single-line'], + 'use_cases': ['search box', 'text input', 'single field'], + 'complexity': 'low' + }, + 'textarea': { + 'keywords': ['edit', 'multi-line', 'text area', 'editor', 'compose'], + 'use_cases': ['text editing', 'message composition', 'multi-line input'], + 'complexity': 'medium' + }, + 'table': { + 'keywords': ['table', 'tabular', 'rows', 'columns', 'grid', 'data display'], + 'use_cases': ['data table', 'spreadsheet view', 'structured data'], + 'complexity': 'medium' + }, + 'list': { + 'keywords': ['list', 'items', 'select', 'choose', 'menu', 'options'], + 'use_cases': ['item selection', 'menu', 'file list'], + 'complexity': 'medium' + }, + 'progress': { + 'keywords': ['progress', 'loading', 'installation', 'percent', 'bar'], + 'use_cases': ['progress indication', 'loading', 'installation progress'], + 'complexity': 'low' + }, + 'spinner': { + 'keywords': ['loading', 'spinner', 'wait', 'processing', 'busy'], + 'use_cases': ['loading indicator', 'waiting', 'processing'], + 'complexity': 'low' + }, + 'filepicker': { + 'keywords': ['file', 'select file', 'choose file', 'file system', 'browse'], + 'use_cases': ['file selection', 'file browser', 'file chooser'], + 'complexity': 'medium' + }, + 'paginator': { + 'keywords': ['page', 'pagination', 'pages', 'navigate pages'], + 'use_cases': ['page navigation', 'chunked content', 'paged display'], + 'complexity': 'low' + }, + 'timer': { + 'keywords': ['timer', 'countdown', 'timeout', 'time limit'], + 'use_cases': ['countdown', 'timeout', 'timed operation'], + 'complexity': 'low' + }, + 'stopwatch': { + 'keywords': ['stopwatch', 'elapsed', 'time tracking', 'duration'], + 'use_cases': ['time tracking', 'elapsed time', 'duration measurement'], + 'complexity': 'low' + }, + 'help': { + 'keywords': ['help', 'shortcuts', 'keybindings', 'documentation'], + 'use_cases': ['help menu', 'keyboard shortcuts', 'documentation'], + 'complexity': 'low' + }, + 'tabs': { + 'keywords': ['tabs', 'tabbed', 'switch views', 'navigation'], + 'use_cases': ['tab navigation', 'multiple views', 'view switching'], + 'complexity': 'medium' + }, + 'autocomplete': { + 'keywords': ['autocomplete', 'suggestions', 'completion', 'dropdown'], + 'use_cases': ['autocomplete', 'suggestions', 'smart input'], + 'complexity': 'medium' + } +} + + +def match_score(requirement: str, component: str) -> int: + """ + Calculate relevance score for component given requirement. + + Args: + requirement: Feature requirement description + component: Component name + + Returns: + Score from 0-100 (higher = better match) + + Example: + >>> match_score("scrollable log display", "viewport") + 95 + """ + if component not in COMPONENT_CAPABILITIES: + return 0 + + score = 0 + requirement_lower = requirement.lower() + comp_info = COMPONENT_CAPABILITIES[component] + + # Keyword matching (60 points max) + keywords = comp_info['keywords'] + keyword_matches = sum(1 for kw in keywords if kw in requirement_lower) + keyword_score = min(60, (keyword_matches / len(keywords)) * 60) + score += keyword_score + + # Use case matching (40 points max) + use_cases = comp_info['use_cases'] + use_case_matches = sum(1 for uc in use_cases if any( + word in requirement_lower for word in uc.split() + )) + use_case_score = min(40, (use_case_matches / len(use_cases)) * 40) + score += use_case_score + + return int(score) + + +def find_best_match(requirement: str, components: List[str] = None) -> Tuple[str, int]: + """ + Find best matching component for requirement. + + Args: + requirement: Feature requirement + components: List of component names to consider (None = all) + + Returns: + Tuple of (best_component, score) + + Example: + >>> find_best_match("need to show progress while installing") + ('progress', 85) + """ + if components is None: + components = list(COMPONENT_CAPABILITIES.keys()) + + best_component = None + best_score = 0 + + for component in components: + score = match_score(requirement, component) + if score > best_score: + best_score = score + best_component = component + + return best_component, best_score + + +def suggest_combinations(requirements: List[str]) -> List[List[str]]: + """ + Suggest component combinations for multiple requirements. + + Args: + requirements: List of feature requirements + + Returns: + List of component combinations (each is a list of components) + + Example: + >>> suggest_combinations(["display logs", "search logs"]) + [['viewport', 'textinput']] + """ + combinations = [] + + # Find best match for each requirement + selected_components = [] + for req in requirements: + component, score = find_best_match(req) + if score > 50 and component not in selected_components: + selected_components.append(component) + + if selected_components: + combinations.append(selected_components) + + # Common patterns + patterns = { + 'file_manager': ['filepicker', 'viewport', 'list'], + 'installer': ['progress', 'spinner', 'list'], + 'form': ['textinput', 'textarea', 'help'], + 'viewer': ['viewport', 'paginator', 'textinput'], + 'dashboard': ['tabs', 'viewport', 'table'] + } + + # Check if requirements match any patterns + req_text = ' '.join(requirements).lower() + for pattern_name, pattern_components in patterns.items(): + if pattern_name.replace('_', ' ') in req_text: + combinations.append(pattern_components) + + return combinations if combinations else [selected_components] + + +def get_alternatives(component: str) -> List[str]: + """ + Get alternative components that serve similar purposes. + + Args: + component: Component name + + Returns: + List of alternative component names + + Example: + >>> get_alternatives('viewport') + ['pager', 'textarea'] + """ + alternatives = { + 'viewport': ['pager'], + 'textinput': ['textarea', 'autocomplete'], + 'textarea': ['textinput', 'viewport'], + 'table': ['list'], + 'list': ['table', 'filepicker'], + 'progress': ['spinner'], + 'spinner': ['progress'], + 'filepicker': ['list'], + 'paginator': ['viewport'], + 'tabs': ['composable-views'] + } + + return alternatives.get(component, []) + + +def explain_match(component: str, requirement: str, score: int) -> str: + """ + Generate explanation for why component matches requirement. + + Args: + component: Component name + requirement: Requirement description + score: Match score + + Returns: + Human-readable explanation + + Example: + >>> explain_match("viewport", "scrollable display", 90) + "viewport is a strong match (90/100) for 'scrollable display' because..." + """ + if component not in COMPONENT_CAPABILITIES: + return f"{component} is not a known component" + + comp_info = COMPONENT_CAPABILITIES[component] + requirement_lower = requirement.lower() + + # Find which keywords matched + matched_keywords = [kw for kw in comp_info['keywords'] if kw in requirement_lower] + + explanation_parts = [] + + if score >= 80: + explanation_parts.append(f"{component} is a strong match ({score}/100)") + elif score >= 50: + explanation_parts.append(f"{component} is a good match ({score}/100)") + else: + explanation_parts.append(f"{component} is a weak match ({score}/100)") + + explanation_parts.append(f"for '{requirement}'") + + if matched_keywords: + explanation_parts.append(f"because it handles: {', '.join(matched_keywords)}") + + # Add use case + explanation_parts.append(f"Common use cases: {', '.join(comp_info['use_cases'])}") + + return " ".join(explanation_parts) + "." + + +def rank_components_by_relevance( + requirements: List[str], + min_score: int = 50 +) -> List[Tuple[str, int, List[str]]]: + """ + Rank all components by relevance to requirements. + + Args: + requirements: List of feature requirements + min_score: Minimum score to include (default: 50) + + Returns: + List of tuples: (component, total_score, matching_requirements) + Sorted by total_score descending + + Example: + >>> rank_components_by_relevance(["scroll", "display text"]) + [('viewport', 180, ['scroll', 'display text']), ...] + """ + component_scores = {} + component_matches = {} + + all_components = list(COMPONENT_CAPABILITIES.keys()) + + for component in all_components: + total_score = 0 + matching_reqs = [] + + for req in requirements: + score = match_score(req, component) + if score >= min_score: + total_score += score + matching_reqs.append(req) + + if total_score > 0: + component_scores[component] = total_score + component_matches[component] = matching_reqs + + # Sort by score + ranked = sorted( + component_scores.items(), + key=lambda x: x[1], + reverse=True + ) + + return [(comp, score, component_matches[comp]) for comp, score in ranked] + + +def main(): + """Test component matcher.""" + print("Testing Component Matcher\n" + "=" * 50) + + # Test 1: Match score + print("\n1. Testing match_score()...") + score = match_score("scrollable log display", "viewport") + print(f" Score for 'scrollable log display' + viewport: {score}") + assert score > 50, "Should have good score" + print(" ✓ Match scoring works") + + # Test 2: Find best match + print("\n2. Testing find_best_match()...") + component, score = find_best_match("need to show progress while installing") + print(f" Best match: {component} ({score})") + assert component in ['progress', 'spinner'], "Should match progress-related component" + print(" ✓ Best match finding works") + + # Test 3: Suggest combinations + print("\n3. Testing suggest_combinations()...") + combos = suggest_combinations(["display logs", "search logs", "scroll through logs"]) + print(f" Suggested combinations: {combos}") + assert len(combos) > 0, "Should suggest at least one combination" + print(" ✓ Combination suggestion works") + + # Test 4: Get alternatives + print("\n4. Testing get_alternatives()...") + alts = get_alternatives('viewport') + print(f" Alternatives to viewport: {alts}") + assert 'pager' in alts, "Should include pager as alternative" + print(" ✓ Alternative suggestions work") + + # Test 5: Explain match + print("\n5. Testing explain_match()...") + explanation = explain_match("viewport", "scrollable display", 90) + print(f" Explanation: {explanation}") + assert "strong match" in explanation, "Should indicate strong match" + print(" ✓ Match explanation works") + + # Test 6: Rank components + print("\n6. Testing rank_components_by_relevance()...") + ranked = rank_components_by_relevance( + ["scroll", "display", "text", "search"], + min_score=40 + ) + print(f" Top 3 components:") + for i, (comp, score, reqs) in enumerate(ranked[:3], 1): + print(f" {i}. {comp} (score: {score}) - matches: {reqs}") + assert len(ranked) > 0, "Should rank some components" + print(" ✓ Component ranking works") + + print("\n✅ All tests passed!") + + +if __name__ == "__main__": + main() diff --git a/scripts/utils/helpers.py b/scripts/utils/helpers.py new file mode 100644 index 0000000..1a74f8e --- /dev/null +++ b/scripts/utils/helpers.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +""" +General helper utilities for Bubble Tea Designer. +""" + +from datetime import datetime +from typing import Optional + + +def get_timestamp() -> str: + """Get current timestamp in ISO format.""" + return datetime.now().isoformat() + + +def format_list_markdown(items: list, ordered: bool = False) -> str: + """Format list as markdown.""" + if not items: + return "" + + if ordered: + return "\n".join(f"{i}. {item}" for i, item in enumerate(items, 1)) + else: + return "\n".join(f"- {item}" for item in items) + + +def truncate_text(text: str, max_length: int = 100) -> str: + """Truncate text to max length with ellipsis.""" + if len(text) <= max_length: + return text + return text[:max_length-3] + "..." + + +def estimate_complexity(num_components: int, num_views: int = 1) -> str: + """Estimate implementation complexity.""" + if num_components <= 2 and num_views == 1: + return "Simple (1-2 hours)" + elif num_components <= 4 and num_views <= 2: + return "Medium (2-4 hours)" + else: + return "Complex (4+ hours)" diff --git a/scripts/utils/inventory_loader.py b/scripts/utils/inventory_loader.py new file mode 100644 index 0000000..7385229 --- /dev/null +++ b/scripts/utils/inventory_loader.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 +""" +Inventory loader for Bubble Tea examples. +Loads and parses CONTEXTUAL-INVENTORY.md from charm-examples-inventory. +""" + +import os +import re +from typing import Dict, List, Optional, Tuple +from pathlib import Path +import logging + +logger = logging.getLogger(__name__) + + +class InventoryLoadError(Exception): + """Raised when inventory cannot be loaded.""" + pass + + +class Example: + """Represents a single Bubble Tea example.""" + + def __init__(self, name: str, file_path: str, capability: str): + self.name = name + self.file_path = file_path + self.capability = capability + self.key_patterns: List[str] = [] + self.components: List[str] = [] + self.use_cases: List[str] = [] + + def __repr__(self): + return f"Example({self.name}, {self.capability})" + + +class Inventory: + """Bubble Tea examples inventory.""" + + def __init__(self, base_path: str): + self.base_path = base_path + self.examples: Dict[str, Example] = {} + self.capabilities: Dict[str, List[Example]] = {} + self.components: Dict[str, List[Example]] = {} + + def add_example(self, example: Example): + """Add example to inventory.""" + self.examples[example.name] = example + + # Index by capability + if example.capability not in self.capabilities: + self.capabilities[example.capability] = [] + self.capabilities[example.capability].append(example) + + # Index by components + for component in example.components: + if component not in self.components: + self.components[component] = [] + self.components[component].append(example) + + def search_by_keyword(self, keyword: str) -> List[Example]: + """Search examples by keyword in name or patterns.""" + keyword_lower = keyword.lower() + results = [] + + for example in self.examples.values(): + if keyword_lower in example.name.lower(): + results.append(example) + continue + + for pattern in example.key_patterns: + if keyword_lower in pattern.lower(): + results.append(example) + break + + return results + + def get_by_capability(self, capability: str) -> List[Example]: + """Get all examples for a capability.""" + return self.capabilities.get(capability, []) + + def get_by_component(self, component: str) -> List[Example]: + """Get all examples using a component.""" + return self.components.get(component, []) + + +def load_inventory(inventory_path: Optional[str] = None) -> Inventory: + """ + Load Bubble Tea examples inventory from CONTEXTUAL-INVENTORY.md. + + Args: + inventory_path: Path to charm-examples-inventory directory + If None, tries to find it automatically + + Returns: + Loaded Inventory object + + Raises: + InventoryLoadError: If inventory cannot be loaded + + Example: + >>> inv = load_inventory("/path/to/charm-examples-inventory") + >>> examples = inv.search_by_keyword("progress") + """ + if inventory_path is None: + inventory_path = _find_inventory_path() + + inventory_file = Path(inventory_path) / "bubbletea" / "examples" / "CONTEXTUAL-INVENTORY.md" + + if not inventory_file.exists(): + raise InventoryLoadError( + f"Inventory file not found: {inventory_file}\n" + f"Expected at: {inventory_path}/bubbletea/examples/CONTEXTUAL-INVENTORY.md" + ) + + logger.info(f"Loading inventory from: {inventory_file}") + + with open(inventory_file, 'r') as f: + content = f.read() + + inventory = parse_inventory_markdown(content, str(inventory_path)) + + logger.info(f"Loaded {len(inventory.examples)} examples") + logger.info(f"Categories: {len(inventory.capabilities)}") + + return inventory + + +def parse_inventory_markdown(content: str, base_path: str) -> Inventory: + """ + Parse CONTEXTUAL-INVENTORY.md markdown content. + + Args: + content: Markdown content + base_path: Base path for example files + + Returns: + Inventory object with parsed examples + """ + inventory = Inventory(base_path) + + # Parse quick reference table + table_matches = re.finditer( + r'\|\s*(.+?)\s*\|\s*`(.+?)`\s*\|', + content + ) + + need_to_file = {} + for match in table_matches: + need = match.group(1).strip() + file_path = match.group(2).strip() + need_to_file[need] = file_path + + # Parse detailed sections (## Examples by Capability) + capability_pattern = r'### (.+?)\n\n\*\*Use (.+?) when you need:\*\*(.+?)(?=\n\n\*\*|### |\Z)' + + capability_sections = re.finditer(capability_pattern, content, re.DOTALL) + + for section in capability_sections: + capability = section.group(1).strip() + example_name = section.group(2).strip() + description = section.group(3).strip() + + # Extract file path and key patterns + file_match = re.search(r'\*\*File\*\*: `(.+?)`', description) + patterns_match = re.search(r'\*\*Key patterns\*\*: (.+?)(?=\n|$)', description) + + if file_match: + file_path = file_match.group(1).strip() + example = Example(example_name, file_path, capability) + + if patterns_match: + patterns_text = patterns_match.group(1).strip() + example.key_patterns = [p.strip() for p in patterns_text.split(',')] + + # Extract components from file name and patterns + example.components = _extract_components(example_name, example.key_patterns) + + inventory.add_example(example) + + return inventory + + +def _extract_components(name: str, patterns: List[str]) -> List[str]: + """Extract component names from example name and patterns.""" + components = [] + + # Common component keywords + component_keywords = [ + 'textinput', 'textarea', 'viewport', 'table', 'list', 'pager', + 'paginator', 'spinner', 'progress', 'timer', 'stopwatch', + 'filepicker', 'help', 'tabs', 'autocomplete' + ] + + name_lower = name.lower() + for keyword in component_keywords: + if keyword in name_lower: + components.append(keyword) + + for pattern in patterns: + pattern_lower = pattern.lower() + for keyword in component_keywords: + if keyword in pattern_lower and keyword not in components: + components.append(keyword) + + return components + + +def _find_inventory_path() -> str: + """ + Try to find charm-examples-inventory automatically. + + Searches in common locations: + - ./charm-examples-inventory + - ../charm-examples-inventory + - ~/charmtuitemplate/vinw/charm-examples-inventory + + Returns: + Path to inventory directory + + Raises: + InventoryLoadError: If not found + """ + search_paths = [ + Path.cwd() / "charm-examples-inventory", + Path.cwd().parent / "charm-examples-inventory", + Path.home() / "charmtuitemplate" / "vinw" / "charm-examples-inventory" + ] + + for path in search_paths: + if (path / "bubbletea" / "examples" / "CONTEXTUAL-INVENTORY.md").exists(): + logger.info(f"Found inventory at: {path}") + return str(path) + + raise InventoryLoadError( + "Could not find charm-examples-inventory automatically.\n" + f"Searched: {[str(p) for p in search_paths]}\n" + "Please provide inventory_path parameter." + ) + + +def build_capability_index(inventory: Inventory) -> Dict[str, List[str]]: + """ + Build index of capabilities to example names. + + Args: + inventory: Loaded inventory + + Returns: + Dict mapping capability names to example names + """ + index = {} + for capability, examples in inventory.capabilities.items(): + index[capability] = [ex.name for ex in examples] + return index + + +def build_component_index(inventory: Inventory) -> Dict[str, List[str]]: + """ + Build index of components to example names. + + Args: + inventory: Loaded inventory + + Returns: + Dict mapping component names to example names + """ + index = {} + for component, examples in inventory.components.items(): + index[component] = [ex.name for ex in examples] + return index + + +def get_example_details(inventory: Inventory, example_name: str) -> Optional[Example]: + """ + Get detailed information about a specific example. + + Args: + inventory: Loaded inventory + example_name: Name of example to look up + + Returns: + Example object or None if not found + """ + return inventory.examples.get(example_name) + + +def main(): + """Test inventory loader.""" + logging.basicConfig(level=logging.INFO) + + print("Testing Inventory Loader\n" + "=" * 50) + + try: + # Load inventory + print("\n1. Loading inventory...") + inventory = load_inventory() + print(f"✓ Loaded {len(inventory.examples)} examples") + print(f"✓ {len(inventory.capabilities)} capability categories") + + # Test search + print("\n2. Testing keyword search...") + results = inventory.search_by_keyword("progress") + print(f"✓ Found {len(results)} examples for 'progress':") + for ex in results[:3]: + print(f" - {ex.name} ({ex.capability})") + + # Test capability lookup + print("\n3. Testing capability lookup...") + cap_examples = inventory.get_by_capability("Installation and Progress Tracking") + print(f"✓ Found {len(cap_examples)} installation examples") + + # Test component lookup + print("\n4. Testing component lookup...") + comp_examples = inventory.get_by_component("spinner") + print(f"✓ Found {len(comp_examples)} examples using 'spinner'") + + # Test indices + print("\n5. Building indices...") + cap_index = build_capability_index(inventory) + comp_index = build_component_index(inventory) + print(f"✓ Capability index: {len(cap_index)} categories") + print(f"✓ Component index: {len(comp_index)} components") + + print("\n✅ All tests passed!") + + except InventoryLoadError as e: + print(f"\n❌ Error loading inventory: {e}") + return 1 + + return 0 + + +if __name__ == "__main__": + exit(main()) diff --git a/scripts/utils/template_generator.py b/scripts/utils/template_generator.py new file mode 100644 index 0000000..9a1f8e8 --- /dev/null +++ b/scripts/utils/template_generator.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +""" +Template generator for Bubble Tea TUIs. +Generates code scaffolding and boilerplate. +""" + +from typing import List, Dict + + +def generate_model_struct(components: List[str], archetype: str) -> str: + """Generate model struct with components.""" + component_fields = { + 'viewport': ' viewport viewport.Model', + 'textinput': ' textInput textinput.Model', + 'textarea': ' textArea textarea.Model', + 'table': ' table table.Model', + 'list': ' list list.Model', + 'progress': ' progress progress.Model', + 'spinner': ' spinner spinner.Model' + } + + fields = [] + for comp in components: + if comp in component_fields: + fields.append(component_fields[comp]) + + # Add common fields + fields.extend([ + ' width int', + ' height int', + ' ready bool' + ]) + + return f"""type model struct {{ +{chr(10).join(fields)} +}}""" + + +def generate_init_function(components: List[str]) -> str: + """Generate Init() function.""" + inits = [] + for comp in components: + if comp == 'viewport': + inits.append(' m.viewport = viewport.New(80, 20)') + elif comp == 'textinput': + inits.append(' m.textInput = textinput.New()') + inits.append(' m.textInput.Focus()') + elif comp == 'spinner': + inits.append(' m.spinner = spinner.New()') + inits.append(' m.spinner.Spinner = spinner.Dot') + elif comp == 'progress': + inits.append(' m.progress = progress.New(progress.WithDefaultGradient())') + + init_cmds = ', '.join([f'{c}.Init()' for c in components if c != 'viewport']) + + return f"""func (m model) Init() tea.Cmd {{ +{chr(10).join(inits) if inits else ' // Initialize components'} + return tea.Batch({init_cmds if init_cmds else 'nil'}) +}}""" + + +def generate_update_skeleton(interactions: Dict) -> str: + """Generate Update() skeleton.""" + return """func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "ctrl+c", "q": + return m, tea.Quit + } + + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + m.ready = true + } + + // Update components + // TODO: Add component update logic + + return m, nil +}""" + + +def generate_view_skeleton(components: List[str]) -> str: + """Generate View() skeleton.""" + renders = [] + for comp in components: + renders.append(f' // Render {comp}') + renders.append(f' // views = append(views, m.{comp}.View())') + + return f"""func (m model) View() string {{ + if !m.ready {{ + return "Loading..." + }} + + var views []string + +{chr(10).join(renders)} + + return lipgloss.JoinVertical(lipgloss.Left, views...) +}}""" + + +def generate_main_go(components: List[str], archetype: str) -> str: + """Generate complete main.go scaffold.""" + imports = ['github.com/charmbracelet/bubbletea'] + + if 'viewport' in components: + imports.append('github.com/charmbracelet/bubbles/viewport') + if 'textinput' in components: + imports.append('github.com/charmbracelet/bubbles/textinput') + if any(c in components for c in ['table', 'list', 'spinner', 'progress']): + imports.append('github.com/charmbracelet/bubbles/' + components[0]) + + imports.append('github.com/charmbracelet/lipgloss') + + import_block = '\n '.join(f'"{imp}"' for imp in imports) + + return f"""package main + +import ( + {import_block} +) + +{generate_model_struct(components, archetype)} + +{generate_init_function(components)} + +{generate_update_skeleton({})} + +{generate_view_skeleton(components)} + +func main() {{ + p := tea.NewProgram(model{{}}, tea.WithAltScreen()) + if _, err := p.Run(); err != nil {{ + panic(err) + }} +}} +""" diff --git a/scripts/utils/validators/__init__.py b/scripts/utils/validators/__init__.py new file mode 100644 index 0000000..367a122 --- /dev/null +++ b/scripts/utils/validators/__init__.py @@ -0,0 +1,26 @@ +"""Validators for Bubble Tea Designer.""" + +from .requirement_validator import ( + RequirementValidator, + validate_description_clarity, + validate_requirements_completeness, + ValidationReport, + ValidationResult, + ValidationLevel +) + +from .design_validator import ( + DesignValidator, + validate_component_fit +) + +__all__ = [ + 'RequirementValidator', + 'validate_description_clarity', + 'validate_requirements_completeness', + 'DesignValidator', + 'validate_component_fit', + 'ValidationReport', + 'ValidationResult', + 'ValidationLevel' +] diff --git a/scripts/utils/validators/design_validator.py b/scripts/utils/validators/design_validator.py new file mode 100644 index 0000000..ca451db --- /dev/null +++ b/scripts/utils/validators/design_validator.py @@ -0,0 +1,425 @@ +#!/usr/bin/env python3 +""" +Design validators for Bubble Tea Designer. +Validates design outputs (component selections, architecture, workflows). +""" + +from typing import Dict, List, Optional +from .requirement_validator import ValidationReport, ValidationResult, ValidationLevel + + +class DesignValidator: + """Validates TUI design outputs.""" + + def validate_component_selection( + self, + components: Dict, + requirements: Dict + ) -> ValidationReport: + """ + Validate component selection against requirements. + + Args: + components: Selected components dict + requirements: Original requirements + + Returns: + ValidationReport + """ + report = ValidationReport() + + # Check 1: At least one component selected + primary = components.get('primary_components', []) + has_components = len(primary) > 0 + + report.add(ValidationResult( + check_name="has_components", + level=ValidationLevel.CRITICAL, + passed=has_components, + message=f"Primary components selected: {len(primary)}" + )) + + # Check 2: Components cover requirements + features = set(requirements.get('features', [])) + if features and primary: + # Check if components mention required features + covered_features = set() + for comp in primary: + justification = comp.get('justification', '').lower() + for feature in features: + if feature.lower() in justification: + covered_features.add(feature) + + coverage = len(covered_features) / len(features) * 100 if features else 0 + report.add(ValidationResult( + check_name="feature_coverage", + level=ValidationLevel.WARNING, + passed=coverage >= 50, + message=f"Feature coverage: {coverage:.0f}% ({len(covered_features)}/{len(features)})" + )) + + # Check 3: No duplicate components + comp_names = [c.get('component', '') for c in primary] + duplicates = [name for name in comp_names if comp_names.count(name) > 1] + + report.add(ValidationResult( + check_name="no_duplicates", + level=ValidationLevel.WARNING, + passed=len(duplicates) == 0, + message="No duplicate components" if not duplicates else + f"Duplicate components: {set(duplicates)}" + )) + + # Check 4: Reasonable number of components (not too many) + reasonable_count = len(primary) <= 6 + report.add(ValidationResult( + check_name="reasonable_count", + level=ValidationLevel.INFO, + passed=reasonable_count, + message=f"Component count: {len(primary)} ({'reasonable' if reasonable_count else 'may be too many'})" + )) + + # Check 5: Each component has justification + all_justified = all('justification' in c for c in primary) + report.add(ValidationResult( + check_name="all_justified", + level=ValidationLevel.INFO, + passed=all_justified, + message="All components justified" if all_justified else + "Some components missing justification" + )) + + return report + + def validate_architecture(self, architecture: Dict) -> ValidationReport: + """ + Validate architecture design. + + Args: + architecture: Architecture specification + + Returns: + ValidationReport + """ + report = ValidationReport() + + # Check 1: Has model struct + has_model = 'model_struct' in architecture and architecture['model_struct'] + report.add(ValidationResult( + check_name="has_model_struct", + level=ValidationLevel.CRITICAL, + passed=has_model, + message="Model struct defined" if has_model else "Missing model struct" + )) + + # Check 2: Has message handlers + handlers = architecture.get('message_handlers', {}) + has_handlers = len(handlers) > 0 + + report.add(ValidationResult( + check_name="has_message_handlers", + level=ValidationLevel.CRITICAL, + passed=has_handlers, + message=f"Message handlers defined: {len(handlers)}" + )) + + # Check 3: Has key message handler (keyboard) + has_key_handler = 'tea.KeyMsg' in handlers or 'KeyMsg' in handlers + + report.add(ValidationResult( + check_name="has_keyboard_handler", + level=ValidationLevel.WARNING, + passed=has_key_handler, + message="Keyboard handler present" if has_key_handler else + "Missing keyboard handler (tea.KeyMsg)" + )) + + # Check 4: Has view logic + has_view = 'view_logic' in architecture and architecture['view_logic'] + report.add(ValidationResult( + check_name="has_view_logic", + level=ValidationLevel.CRITICAL, + passed=has_view, + message="View logic defined" if has_view else "Missing view logic" + )) + + # Check 5: Has diagrams + diagrams = architecture.get('diagrams', {}) + has_diagrams = len(diagrams) > 0 + + report.add(ValidationResult( + check_name="has_diagrams", + level=ValidationLevel.INFO, + passed=has_diagrams, + message=f"Architecture diagrams: {len(diagrams)}" + )) + + return report + + def validate_workflow_completeness(self, workflow: Dict) -> ValidationReport: + """ + Validate workflow has all necessary phases and tasks. + + Args: + workflow: Workflow specification + + Returns: + ValidationReport + """ + report = ValidationReport() + + # Check 1: Has phases + phases = workflow.get('phases', []) + has_phases = len(phases) > 0 + + report.add(ValidationResult( + check_name="has_phases", + level=ValidationLevel.CRITICAL, + passed=has_phases, + message=f"Workflow phases: {len(phases)}" + )) + + if not phases: + return report + + # Check 2: Each phase has tasks + all_have_tasks = all(len(phase.get('tasks', [])) > 0 for phase in phases) + + report.add(ValidationResult( + check_name="all_phases_have_tasks", + level=ValidationLevel.WARNING, + passed=all_have_tasks, + message="All phases have tasks" if all_have_tasks else + "Some phases are missing tasks" + )) + + # Check 3: Has testing checkpoints + checkpoints = workflow.get('testing_checkpoints', []) + has_testing = len(checkpoints) > 0 + + report.add(ValidationResult( + check_name="has_testing", + level=ValidationLevel.WARNING, + passed=has_testing, + message=f"Testing checkpoints: {len(checkpoints)}" + )) + + # Check 4: Reasonable phase count (2-6 phases) + reasonable_phases = 2 <= len(phases) <= 6 + + report.add(ValidationResult( + check_name="reasonable_phases", + level=ValidationLevel.INFO, + passed=reasonable_phases, + message=f"Phase count: {len(phases)} ({'good' if reasonable_phases else 'unusual'})" + )) + + # Check 5: Has time estimates + total_time = workflow.get('total_estimated_time') + has_estimate = bool(total_time) + + report.add(ValidationResult( + check_name="has_time_estimate", + level=ValidationLevel.INFO, + passed=has_estimate, + message=f"Time estimate: {total_time or 'missing'}" + )) + + return report + + def validate_design_report(self, report_data: Dict) -> ValidationReport: + """ + Validate complete design report. + + Args: + report_data: Complete design report + + Returns: + ValidationReport + """ + report = ValidationReport() + + # Check all required sections present + required_sections = ['requirements', 'components', 'patterns', 'architecture', 'workflow'] + sections = report_data.get('sections', {}) + + for section in required_sections: + has_section = section in sections and sections[section] + report.add(ValidationResult( + check_name=f"has_{section}_section", + level=ValidationLevel.CRITICAL, + passed=has_section, + message=f"Section '{section}': {'present' if has_section else 'MISSING'}" + )) + + # Check has summary + has_summary = 'summary' in report_data and report_data['summary'] + report.add(ValidationResult( + check_name="has_summary", + level=ValidationLevel.WARNING, + passed=has_summary, + message="Summary present" if has_summary else "Missing summary" + )) + + # Check has scaffolding + has_scaffolding = 'scaffolding' in report_data and report_data['scaffolding'] + report.add(ValidationResult( + check_name="has_scaffolding", + level=ValidationLevel.INFO, + passed=has_scaffolding, + message="Code scaffolding included" if has_scaffolding else + "No code scaffolding" + )) + + # Check has next steps + next_steps = report_data.get('next_steps', []) + has_next_steps = len(next_steps) > 0 + + report.add(ValidationResult( + check_name="has_next_steps", + level=ValidationLevel.INFO, + passed=has_next_steps, + message=f"Next steps: {len(next_steps)}" + )) + + return report + + +def validate_component_fit(component: str, requirement: str) -> bool: + """ + Quick check if component fits requirement. + + Args: + component: Component name (e.g., "viewport.Model") + requirement: Requirement description + + Returns: + True if component appears suitable + """ + component_lower = component.lower() + requirement_lower = requirement.lower() + + # Simple keyword matching + keyword_map = { + 'viewport': ['scroll', 'view', 'display', 'content'], + 'textinput': ['input', 'text', 'search', 'query'], + 'textarea': ['edit', 'multi-line', 'text area'], + 'table': ['table', 'tabular', 'rows', 'columns'], + 'list': ['list', 'items', 'select', 'choose'], + 'progress': ['progress', 'loading', 'installation'], + 'spinner': ['loading', 'spinner', 'wait'], + 'filepicker': ['file', 'select file', 'choose file'] + } + + for comp_key, keywords in keyword_map.items(): + if comp_key in component_lower: + return any(kw in requirement_lower for kw in keywords) + + return False + + +def main(): + """Test design validator.""" + print("Testing Design Validator\n" + "=" * 50) + + validator = DesignValidator() + + # Test 1: Component selection validation + print("\n1. Testing component selection validation...") + components = { + 'primary_components': [ + { + 'component': 'viewport.Model', + 'score': 95, + 'justification': 'Scrollable display for log content' + }, + { + 'component': 'textinput.Model', + 'score': 90, + 'justification': 'Search query input' + } + ] + } + requirements = { + 'features': ['display', 'search', 'scroll'] + } + report = validator.validate_component_selection(components, requirements) + print(f" {report.get_summary()}") + assert not report.has_critical_issues(), "Should pass for valid components" + print(" ✓ Component selection validated") + + # Test 2: Architecture validation + print("\n2. Testing architecture validation...") + architecture = { + 'model_struct': 'type model struct {...}', + 'message_handlers': { + 'tea.KeyMsg': 'handle keyboard', + 'tea.WindowSizeMsg': 'handle resize' + }, + 'view_logic': 'func (m model) View() string {...}', + 'diagrams': { + 'component_hierarchy': '...' + } + } + report = validator.validate_architecture(architecture) + print(f" {report.get_summary()}") + assert report.all_passed(), "Should pass for complete architecture" + print(" ✓ Architecture validated") + + # Test 3: Workflow validation + print("\n3. Testing workflow validation...") + workflow = { + 'phases': [ + { + 'name': 'Phase 1: Setup', + 'tasks': [ + {'task': 'Initialize project'}, + {'task': 'Install dependencies'} + ] + }, + { + 'name': 'Phase 2: Core', + 'tasks': [ + {'task': 'Implement viewport'} + ] + } + ], + 'testing_checkpoints': ['After Phase 1', 'After Phase 2'], + 'total_estimated_time': '2 hours' + } + report = validator.validate_workflow_completeness(workflow) + print(f" {report.get_summary()}") + assert report.all_passed(), "Should pass for complete workflow" + print(" ✓ Workflow validated") + + # Test 4: Complete design report validation + print("\n4. Testing complete design report validation...") + design_report = { + 'sections': { + 'requirements': {...}, + 'components': {...}, + 'patterns': {...}, + 'architecture': {...}, + 'workflow': {...} + }, + 'summary': 'TUI design for log viewer', + 'scaffolding': 'package main...', + 'next_steps': ['Step 1', 'Step 2'] + } + report = validator.validate_design_report(design_report) + print(f" {report.get_summary()}") + assert report.all_passed(), "Should pass for complete report" + print(" ✓ Design report validated") + + # Test 5: Component fit check + print("\n5. Testing component fit check...") + assert validate_component_fit("viewport.Model", "scrollable log display") + assert validate_component_fit("textinput.Model", "search query input") + assert not validate_component_fit("spinner.Model", "text input field") + print(" ✓ Component fit checks working") + + print("\n✅ All tests passed!") + + +if __name__ == "__main__": + main() diff --git a/scripts/utils/validators/requirement_validator.py b/scripts/utils/validators/requirement_validator.py new file mode 100644 index 0000000..3adb2c1 --- /dev/null +++ b/scripts/utils/validators/requirement_validator.py @@ -0,0 +1,393 @@ +#!/usr/bin/env python3 +""" +Requirement validators for Bubble Tea Designer. +Validates user input and extracted requirements. +""" + +from typing import Dict, List, Optional, Tuple +from dataclasses import dataclass +from enum import Enum + + +class ValidationLevel(Enum): + """Severity levels for validation results.""" + CRITICAL = "critical" + WARNING = "warning" + INFO = "info" + + +@dataclass +class ValidationResult: + """Single validation check result.""" + check_name: str + level: ValidationLevel + passed: bool + message: str + details: Optional[Dict] = None + + +class ValidationReport: + """Collection of validation results.""" + + def __init__(self): + self.results: List[ValidationResult] = [] + + def add(self, result: ValidationResult): + """Add validation result.""" + self.results.append(result) + + def has_critical_issues(self) -> bool: + """Check if any critical issues found.""" + return any( + r.level == ValidationLevel.CRITICAL and not r.passed + for r in self.results + ) + + def all_passed(self) -> bool: + """Check if all validations passed.""" + return all(r.passed for r in self.results) + + def get_warnings(self) -> List[str]: + """Get all warning messages.""" + return [ + r.message for r in self.results + if r.level == ValidationLevel.WARNING and not r.passed + ] + + def get_summary(self) -> str: + """Get summary of validation results.""" + total = len(self.results) + passed = sum(1 for r in self.results if r.passed) + critical = sum( + 1 for r in self.results + if r.level == ValidationLevel.CRITICAL and not r.passed + ) + + return ( + f"Validation: {passed}/{total} passed " + f"({critical} critical issues)" + ) + + def to_dict(self) -> Dict: + """Convert to dictionary.""" + return { + 'passed': self.all_passed(), + 'summary': self.get_summary(), + 'warnings': self.get_warnings(), + 'critical_issues': [ + r.message for r in self.results + if r.level == ValidationLevel.CRITICAL and not r.passed + ], + 'all_results': [ + { + 'check': r.check_name, + 'level': r.level.value, + 'passed': r.passed, + 'message': r.message + } + for r in self.results + ] + } + + +class RequirementValidator: + """Validates TUI requirements and descriptions.""" + + def validate_description(self, description: str) -> ValidationReport: + """ + Validate user-provided description. + + Args: + description: Natural language TUI description + + Returns: + ValidationReport with results + """ + report = ValidationReport() + + # Check 1: Not empty + report.add(ValidationResult( + check_name="not_empty", + level=ValidationLevel.CRITICAL, + passed=bool(description and description.strip()), + message="Description is empty" if not description else "Description provided" + )) + + if not description: + return report + + # Check 2: Minimum length (at least 10 words) + words = description.split() + min_words = 10 + has_min_length = len(words) >= min_words + + report.add(ValidationResult( + check_name="minimum_length", + level=ValidationLevel.WARNING, + passed=has_min_length, + message=f"Description has {len(words)} words (recommended: ≥{min_words})" + )) + + # Check 3: Contains actionable verbs + action_verbs = ['show', 'display', 'view', 'create', 'select', 'navigate', + 'edit', 'input', 'track', 'monitor', 'search', 'filter'] + has_action = any(verb in description.lower() for verb in action_verbs) + + report.add(ValidationResult( + check_name="has_actions", + level=ValidationLevel.WARNING, + passed=has_action, + message="Description contains action verbs" if has_action else + "Consider adding action verbs (show, select, edit, etc.)" + )) + + # Check 4: Contains data type mentions + data_types = ['file', 'text', 'data', 'table', 'list', 'log', 'config', + 'message', 'package', 'item', 'entry'] + has_data = any(dtype in description.lower() for dtype in data_types) + + report.add(ValidationResult( + check_name="has_data_types", + level=ValidationLevel.INFO, + passed=has_data, + message="Data types mentioned" if has_data else + "No explicit data types mentioned" + )) + + return report + + def validate_requirements(self, requirements: Dict) -> ValidationReport: + """ + Validate extracted requirements structure. + + Args: + requirements: Structured requirements dict + + Returns: + ValidationReport + """ + report = ValidationReport() + + # Check 1: Has archetype + has_archetype = 'archetype' in requirements and requirements['archetype'] + report.add(ValidationResult( + check_name="has_archetype", + level=ValidationLevel.CRITICAL, + passed=has_archetype, + message=f"TUI archetype: {requirements.get('archetype', 'MISSING')}" + )) + + # Check 2: Has features + features = requirements.get('features', []) + has_features = len(features) > 0 + report.add(ValidationResult( + check_name="has_features", + level=ValidationLevel.CRITICAL, + passed=has_features, + message=f"Features identified: {len(features)}" + )) + + # Check 3: Has interactions + interactions = requirements.get('interactions', {}) + keyboard_interactions = interactions.get('keyboard', []) + has_interactions = len(keyboard_interactions) > 0 + + report.add(ValidationResult( + check_name="has_interactions", + level=ValidationLevel.WARNING, + passed=has_interactions, + message=f"Keyboard interactions: {len(keyboard_interactions)}" + )) + + # Check 4: Has view specification + views = requirements.get('views', '') + has_views = bool(views) + report.add(ValidationResult( + check_name="has_view_spec", + level=ValidationLevel.WARNING, + passed=has_views, + message=f"View type: {views or 'unspecified'}" + )) + + # Check 5: Completeness (has all expected keys) + expected_keys = ['archetype', 'features', 'interactions', 'data_types', 'views'] + missing_keys = set(expected_keys) - set(requirements.keys()) + + report.add(ValidationResult( + check_name="completeness", + level=ValidationLevel.INFO, + passed=len(missing_keys) == 0, + message=f"Complete structure" if not missing_keys else + f"Missing keys: {missing_keys}" + )) + + return report + + def suggest_clarifications(self, requirements: Dict) -> List[str]: + """ + Suggest clarifying questions based on incomplete requirements. + + Args: + requirements: Extracted requirements + + Returns: + List of clarifying questions to ask user + """ + questions = [] + + # Check if archetype is unclear + if not requirements.get('archetype') or requirements['archetype'] == 'general': + questions.append( + "What type of TUI is this? (file manager, installer, dashboard, " + "form, viewer, etc.)" + ) + + # Check if features are vague + features = requirements.get('features', []) + if len(features) < 2: + questions.append( + "What are the main features/capabilities needed? " + "(e.g., navigation, selection, editing, search, filtering)" + ) + + # Check if data type is unspecified + data_types = requirements.get('data_types', []) + if not data_types: + questions.append( + "What type of data will the TUI display? " + "(files, text, tabular data, logs, etc.)" + ) + + # Check if interaction is unspecified + interactions = requirements.get('interactions', {}) + if not interactions.get('keyboard') and not interactions.get('mouse'): + questions.append( + "How should users interact? Keyboard only, or mouse support needed?" + ) + + # Check if view type is unspecified + if not requirements.get('views'): + questions.append( + "Should this be single-view or multi-view? Need tabs or navigation?" + ) + + return questions + + +def validate_description_clarity(description: str) -> Tuple[bool, str]: + """ + Quick validation of description clarity. + + Args: + description: User description + + Returns: + Tuple of (is_clear, message) + """ + validator = RequirementValidator() + report = validator.validate_description(description) + + if report.has_critical_issues(): + return False, "Description has critical issues: " + report.get_summary() + + warnings = report.get_warnings() + if warnings: + return True, "Description OK with suggestions: " + "; ".join(warnings) + + return True, "Description is clear" + + +def validate_requirements_completeness(requirements: Dict) -> Tuple[bool, str]: + """ + Quick validation of requirements completeness. + + Args: + requirements: Extracted requirements dict + + Returns: + Tuple of (is_complete, message) + """ + validator = RequirementValidator() + report = validator.validate_requirements(requirements) + + if report.has_critical_issues(): + return False, "Requirements incomplete: " + report.get_summary() + + warnings = report.get_warnings() + if warnings: + return True, "Requirements OK with warnings: " + "; ".join(warnings) + + return True, "Requirements complete" + + +def main(): + """Test requirement validator.""" + print("Testing Requirement Validator\n" + "=" * 50) + + validator = RequirementValidator() + + # Test 1: Empty description + print("\n1. Testing empty description...") + report = validator.validate_description("") + print(f" {report.get_summary()}") + assert report.has_critical_issues(), "Should fail for empty description" + print(" ✓ Correctly detected empty description") + + # Test 2: Good description + print("\n2. Testing good description...") + good_desc = "Create a file manager TUI with three-column view showing parent directory, current directory, and file preview" + report = validator.validate_description(good_desc) + print(f" {report.get_summary()}") + print(" ✓ Good description validated") + + # Test 3: Vague description + print("\n3. Testing vague description...") + vague_desc = "Build a TUI" + report = validator.validate_description(vague_desc) + print(f" {report.get_summary()}") + warnings = report.get_warnings() + if warnings: + print(f" Warnings: {warnings}") + print(" ✓ Vague description detected") + + # Test 4: Requirements validation + print("\n4. Testing requirements validation...") + requirements = { + 'archetype': 'file-manager', + 'features': ['navigation', 'selection', 'preview'], + 'interactions': { + 'keyboard': ['arrows', 'enter', 'backspace'], + 'mouse': [] + }, + 'data_types': ['files', 'directories'], + 'views': 'multi' + } + report = validator.validate_requirements(requirements) + print(f" {report.get_summary()}") + assert report.all_passed(), "Should pass for complete requirements" + print(" ✓ Complete requirements validated") + + # Test 5: Incomplete requirements + print("\n5. Testing incomplete requirements...") + incomplete = { + 'archetype': '', + 'features': [] + } + report = validator.validate_requirements(incomplete) + print(f" {report.get_summary()}") + assert report.has_critical_issues(), "Should fail for incomplete requirements" + print(" ✓ Incomplete requirements detected") + + # Test 6: Clarification suggestions + print("\n6. Testing clarification suggestions...") + questions = validator.suggest_clarifications(incomplete) + print(f" Generated {len(questions)} clarifying questions:") + for i, q in enumerate(questions, 1): + print(f" {i}. {q}") + print(" ✓ Clarifications generated") + + print("\n✅ All tests passed!") + + +if __name__ == "__main__": + main() diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 0000000..67417de --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +""" +Integration tests for Bubble Tea Designer. +Tests complete workflows from description to design report. +""" + +import sys +from pathlib import Path + +# Add scripts to path +sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts')) + +from analyze_requirements import extract_requirements +from map_components import map_to_components +from design_architecture import design_architecture +from generate_workflow import generate_implementation_workflow +from design_tui import comprehensive_tui_design_report + + +def test_analyze_requirements_basic(): + """Test requirement extraction from simple description.""" + print("\n✓ Testing extract_requirements()...") + + description = "Build a log viewer with search and highlighting" + result = extract_requirements(description) + + # Validations + assert 'archetype' in result, "Missing 'archetype' in result" + assert 'features' in result, "Missing 'features'" + assert result['archetype'] == 'viewer', f"Expected 'viewer', got {result['archetype']}" + assert 'search' in result['features'], "Should identify 'search' feature" + + print(f" ✓ Archetype: {result['archetype']}") + print(f" ✓ Features: {', '.join(result['features'])}") + print(f" ✓ Validation: {result['validation']['summary']}") + + return True + + +def test_map_components_viewer(): + """Test component mapping for viewer archetype.""" + print("\n✓ Testing map_to_components()...") + + requirements = { + 'archetype': 'viewer', + 'features': ['display', 'search', 'scrolling'], + 'data_types': ['text'], + 'views': 'single' + } + + result = map_to_components(requirements) + + # Validations + assert 'primary_components' in result, "Missing 'primary_components'" + assert len(result['primary_components']) > 0, "No components selected" + + # Should include viewport for viewing + comp_names = [c['component'] for c in result['primary_components']] + has_viewport = any('viewport' in name.lower() for name in comp_names) + + print(f" ✓ Components selected: {len(result['primary_components'])}") + print(f" ✓ Top component: {result['primary_components'][0]['component']}") + print(f" ✓ Has viewport: {has_viewport}") + + return True + + +def test_design_architecture(): + """Test architecture generation.""" + print("\n✓ Testing design_architecture()...") + + components = { + 'primary_components': [ + {'component': 'viewport.Model', 'score': 90}, + {'component': 'textinput.Model', 'score': 85} + ] + } + + requirements = { + 'archetype': 'viewer', + 'views': 'single' + } + + result = design_architecture(components, {}, requirements) + + # Validations + assert 'model_struct' in result, "Missing 'model_struct'" + assert 'message_handlers' in result, "Missing 'message_handlers'" + assert 'diagrams' in result, "Missing 'diagrams'" + assert 'tea.KeyMsg' in result['message_handlers'], "Missing keyboard handler" + + print(f" ✓ Model struct generated: {len(result['model_struct'])} chars") + print(f" ✓ Message handlers: {len(result['message_handlers'])}") + print(f" ✓ Diagrams: {len(result['diagrams'])}") + + return True + + +def test_generate_workflow(): + """Test workflow generation.""" + print("\n✓ Testing generate_implementation_workflow()...") + + architecture = { + 'model_struct': 'type model struct { ... }', + 'message_handlers': {'tea.KeyMsg': '...'} + } + + result = generate_implementation_workflow(architecture, {}) + + # Validations + assert 'phases' in result, "Missing 'phases'" + assert 'testing_checkpoints' in result, "Missing 'testing_checkpoints'" + assert len(result['phases']) >= 2, "Should have multiple phases" + + print(f" ✓ Workflow phases: {len(result['phases'])}") + print(f" ✓ Testing checkpoints: {len(result['testing_checkpoints'])}") + print(f" ✓ Estimated time: {result.get('total_estimated_time', 'N/A')}") + + return True + + +def test_comprehensive_report_log_viewer(): + """Test comprehensive report for log viewer.""" + print("\n✓ Testing comprehensive_tui_design_report() - Log Viewer...") + + description = "Build a log viewer with search and highlighting" + result = comprehensive_tui_design_report(description) + + # Validations + assert 'description' in result, "Missing 'description'" + assert 'summary' in result, "Missing 'summary'" + assert 'sections' in result, "Missing 'sections'" + + sections = result['sections'] + assert 'requirements' in sections, "Missing 'requirements' section" + assert 'components' in sections, "Missing 'components' section" + assert 'architecture' in sections, "Missing 'architecture' section" + assert 'workflow' in sections, "Missing 'workflow' section" + + print(f" ✓ TUI type: {result.get('tui_type', 'N/A')}") + print(f" ✓ Sections: {len(sections)}") + print(f" ✓ Summary: {result['summary'][:100]}...") + print(f" ✓ Validation: {result['validation']['summary']}") + + return True + + +def test_comprehensive_report_file_manager(): + """Test comprehensive report for file manager.""" + print("\n✓ Testing comprehensive_tui_design_report() - File Manager...") + + description = "Create a file manager with three-column view" + result = comprehensive_tui_design_report(description) + + # Validations + assert result.get('tui_type') == 'file-manager', f"Expected 'file-manager', got {result.get('tui_type')}" + + reqs = result['sections']['requirements'] + assert 'filepicker' in str(reqs).lower() or 'list' in str(reqs).lower(), \ + "Should suggest file-related components" + + print(f" ✓ TUI type: {result['tui_type']}") + print(f" ✓ Archetype correct") + + return True + + +def test_comprehensive_report_installer(): + """Test comprehensive report for installer.""" + print("\n✓ Testing comprehensive_tui_design_report() - Installer...") + + description = "Design an installer with progress bars for packages" + result = comprehensive_tui_design_report(description) + + # Validations + assert result.get('tui_type') == 'installer', f"Expected 'installer', got {result.get('tui_type')}" + + components = result['sections']['components'] + comp_names = str([c['component'] for c in components.get('primary_components', [])]) + assert 'progress' in comp_names.lower() or 'spinner' in comp_names.lower(), \ + "Should suggest progress components" + + print(f" ✓ TUI type: {result['tui_type']}") + print(f" ✓ Progress components suggested") + + return True + + +def test_validation_integration(): + """Test that validation is integrated in all functions.""" + print("\n✓ Testing validation integration...") + + description = "Build a log viewer" + result = comprehensive_tui_design_report(description) + + # Check each section has validation + sections = result['sections'] + + if 'requirements' in sections: + assert 'validation' in sections['requirements'], "Requirements should have validation" + print(" ✓ Requirements validated") + + if 'components' in sections: + assert 'validation' in sections['components'], "Components should have validation" + print(" ✓ Components validated") + + if 'architecture' in sections: + assert 'validation' in sections['architecture'], "Architecture should have validation" + print(" ✓ Architecture validated") + + if 'workflow' in sections: + assert 'validation' in sections['workflow'], "Workflow should have validation" + print(" ✓ Workflow validated") + + # Overall validation + assert 'validation' in result, "Report should have overall validation" + print(" ✓ Overall report validated") + + return True + + +def test_code_scaffolding(): + """Test code scaffolding generation.""" + print("\n✓ Testing code scaffolding generation...") + + description = "Simple log viewer" + result = comprehensive_tui_design_report(description, detail_level="complete") + + # Validations + assert 'scaffolding' in result, "Missing 'scaffolding'" + assert 'main_go' in result['scaffolding'], "Missing 'main_go' scaffold" + + main_go = result['scaffolding']['main_go'] + assert 'package main' in main_go, "Should have package main" + assert 'type model struct' in main_go, "Should have model struct" + assert 'func main()' in main_go, "Should have main function" + + print(f" ✓ Scaffolding generated: {len(main_go)} chars") + print(" ✓ Contains package main") + print(" ✓ Contains model struct") + print(" ✓ Contains main function") + + return True + + +def main(): + """Run all integration tests.""" + print("=" * 70) + print("INTEGRATION TESTS - Bubble Tea Designer") + print("=" * 70) + + tests = [ + ("Requirement extraction", test_analyze_requirements_basic), + ("Component mapping", test_map_components_viewer), + ("Architecture design", test_design_architecture), + ("Workflow generation", test_generate_workflow), + ("Comprehensive report - Log Viewer", test_comprehensive_report_log_viewer), + ("Comprehensive report - File Manager", test_comprehensive_report_file_manager), + ("Comprehensive report - Installer", test_comprehensive_report_installer), + ("Validation integration", test_validation_integration), + ("Code scaffolding", test_code_scaffolding), + ] + + results = [] + for test_name, test_func in tests: + try: + passed = test_func() + results.append((test_name, passed)) + except Exception as e: + print(f"\n ❌ FAILED: {e}") + import traceback + traceback.print_exc() + results.append((test_name, False)) + + # Summary + print("\n" + "=" * 70) + print("SUMMARY") + print("=" * 70) + + for test_name, passed in results: + status = "✅ PASS" if passed else "❌ FAIL" + print(f"{status}: {test_name}") + + passed_count = sum(1 for _, p in results if p) + total_count = len(results) + + print(f"\nResults: {passed_count}/{total_count} passed") + + return passed_count == total_count + + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1)