Commit 3c954be
authored
v0.3.1: Fix detail view scrolling + comprehensive test suite (#5)
* test: add comprehensive UI integration test suite
Add integration tests for core TUI user flows, increasing UI package
coverage from 0% to 10.3%. Tests validate:
- Initial application load and model creation
- Search functionality with various query patterns
- Cursor navigation (arrows, home/end, page up/down)
- View transitions (list ↔ detail, help toggle)
- Filter mode switching (All/Discover/Ready/Installed)
- Window resize responsiveness
- Plugin selection logic
Uses Bubbletea's message-passing model to simulate user input
without requiring an actual terminal. Each test is independent
with isolated test data.
This addresses the critical test coverage gap identified in the
v0.3.0 post-release audit, providing a foundation for safe
refactoring of high-complexity UI functions.
* docs: add development section with Go 1.24 requirements
Add comprehensive development documentation including:
- Go 1.24+ prerequisite (matches go.mod requirement)
- Test running commands with coverage options
- Code formatting instructions
- Linter setup with version matching guidance
Addresses audit finding: Tooling version mismatch between locally
installed golangci-lint/staticcheck (built with Go 1.23) and project
requirement (Go 1.24). CI already uses correct version via runtime
installation.
Note: CI workflows already properly configured:
- ci.yml uses go-version-file: go.mod (auto-detects 1.24)
- release.yml uses go-version: '1.24' (hardcoded)
- golangci-lint@v1.64.8 installed at runtime with Go 1.24
This documentation helps developers avoid local tooling issues.
* feat: add static GitHub stats with fallback mechanism
Add static GitHub stats snapshot to PopularMarketplace struct as
fallback when cache is empty. This provides immediate stats on first
run without requiring API calls.
Changes:
- Add StaticStats field to PopularMarketplace struct
- Fetch and hardcode current GitHub stats for all 12 marketplaces
(Stars, Forks, OpenIssues as of 2025-12-30)
- Update LoadMarketplaceItems() to use static stats as fallback
- Expand hardcoded list from 8 to 12 marketplaces (matches registry)
- Remove 3 TODO comments for background GitHub stats loading
Stats Snapshot:
- claude-code (anthropics): 49,810 stars
- anthropic-agent-skills: 29,937 stars
- wshobson-agents: 23,895 stars
- claude-mem: 9,585 stars
- mag-claude-plugins: 190 stars
- dev-gom-plugins: 41 stars
- feedmob-claude-plugins: 2 stars
- docker-plugins: 11 stars
- ccplugins-marketplace: 10 stars
Benefits:
- Users see stats immediately on first run (no API wait)
- Reduces GitHub API rate limit pressure
- Cache still updates naturally (24h TTL)
- Future background loading can overlay fresh data
Defers background auto-update feature to Phase 5 (Polish).
Resolves Phase 1.3 of post-v0.3.0 audit plan.
* test: add comprehensive search package test suite
Add exhaustive table-driven tests for fuzzy search functionality,
achieving 98.1% code coverage (exceeded 60% target).
Test Coverage:
- Search() function with 11 test cases
- Empty query (returns all, sorted by installed then name)
- Exact name matches (case insensitive)
- Partial name matches
- Fuzzy name matching
- Keyword matching (exact and partial)
- Category matching
- Description matching
- Multi-word queries
- Special characters and Unicode
- No matches (empty results)
- Sorting logic with 3 test suites
- Installed plugins prioritized
- Score-based ranking
- Alphabetical tie-breaking
- scorePlugin() with 11 scoring scenarios
- Exact match: +100
- Partial contains: +70
- Fuzzy match: +0-50 (scaled)
- Keyword exact: +30
- Keyword partial: +20
- Category: +15
- Description: +25
- Installed boost: +5
- Accumulation of multiple matches
- Zero score for no matches
- PluginSearchSource fuzzy.Source implementation
- Len() returns count
- String() builds searchable content
- Edge cases
- Empty plugin list
- Whitespace queries
- Plugins with missing fields
- Unicode and special characters
Coverage: 0% → 98.1%
Tests: 35 test cases, all passing
Completes Phase 2.1 of post-v0.3.0 audit roadmap.
* test: add comprehensive plugin package test suite
Add exhaustive table-driven tests for Plugin struct and methods,
achieving 100% code coverage (exceeded 40% target).
Test Coverage:
- FullName() with 4 test cases
- Standard format: "name@marketplace"
- Edge cases: empty name, empty marketplace
- Special characters and hyphens
- InstallCommand() with 3 test cases
- Standard format: "/plugin install name@marketplace"
- Special characters in name/marketplace
- Discoverable vs installed plugins
- FilterValue() with 5 test cases
- Name + description concatenation
- Empty field handling
- Special characters and Unicode
- Title() with 3 test cases
- Name passthrough
- Empty and whitespace handling
- AuthorName() with 6 test cases
- Priority: Name → Company → "Unknown"
- All combinations of set/unset values
- Whitespace handling
- GitHubURL() with 7 test cases
- Standard URL construction
- Source path normalization (remove leading ./)
- Empty source defaults to plugins/{name}
- Dot source handling
- Empty marketplace repo returns empty
- Nested paths and non-https URLs
- Plugin struct creation and field access
- Author struct creation and defaults
Coverage: 0% → 100%
Tests: 37 test cases across 8 test suites, all passing
Completes Phase 2.2 of post-v0.3.0 audit roadmap.
* test: add marketplace package tests (29% → 41% coverage)
Add comprehensive tests for GitHub stats, cache, refresh, and
discovery functions, boosting marketplace coverage by 11.7%.
New Test Files:
- github_stats_test.go (11 test cases)
- extractOwnerRepo URL parsing (10 scenarios)
- SaveStatsToCache/LoadStatsFromCache with TTL
- Cache file permissions (0600)
- Cache directory permissions (0700)
- Invalid name rejection (path traversal)
- Expired cache handling (24h TTL)
- GitHubStats/GitHubStatsCacheEntry structs
- refresh_test.go (3 test cases)
- ClearCache with existing cache
- ClearCache with non-existent cache
- Error handling for cache dir failures
- discovery_test.go (7 test cases)
- PopularMarketplaces list validation
- Required fields verification
- Static stats presence and sanity checks
- No duplicate marketplace names
- Repo URL format validation
- PopularMarketplace struct creation
Coverage: 29.3% → 41.0% (+11.7%)
Tests: 21 new test cases, all passing
Progress toward Phase 2.3 target (50%+).
Partial completion of post-v0.3.0 audit roadmap Phase 2.
* fix: add scrollable viewport to plugin detail view
Fix UX bug where long plugin descriptions were cut off with no way
to scroll. Implement sticky header/footer with scrollable content
viewport, following the exact pattern from the working help menu.
Changes:
- Add detailViewport field to Model struct
- Split detailView() into 3 functions:
- generateDetailHeader() - sticky top (name, badge, separator)
- generateDetailContent() - scrollable middle (details, description, keywords)
- generateDetailFooter() - sticky bottom (install instructions + key bindings)
- Rewrite detailView() to use viewport pattern (mirror helpView structure)
- Add renderDetailScrollbar() (copy of renderHelpScrollbar)
- Initialize detailViewport in WindowSizeMsg handler
- Width: contentWidth (dynamic)
- Height: windowHeight - 12 (conservative overhead)
- Recalculates on window resize
- Handle scroll keys in handleDetailKeys default case
- Pass unhandled keys to viewport.Update(msg)
- Enables up/down/pgup/pgdown scrolling
- Set viewport content when entering detail view
- Content set on Enter key press
- Scroll position reset to top (GotoTop)
Pattern Validation:
✅ Follows working help menu implementation
✅ Bubbletea viewport best practices (Context7 validated)
✅ Header/footer stay static as specified
✅ Content scrolls with visual scrollbar on right
✅ Window resize recalculates viewport height
✅ All tests passing (UI: 10.3% coverage maintained)
Fixes production bug reported in v0.3.0 where "whimsy-injector"
plugin description was truncated with no scroll mechanism.
One-shot implementation (no iteration needed).
* fix: adjust viewport overhead and move install instructions to scrollable content
* fix: enable mouse wheel scrolling and reduce bottom spacing
Two fixes for detail view viewport:
1. Enable mouse wheel scrolling
- Add tea.MouseMsg handler in main Update() function
- Pass mouse events to detailViewport and helpViewport
- Enables scroll wheel support in both views
- Arrow keys already worked via handleDetailKeys default case
2. Reduce excess bottom spacing
- Reduce overhead from 13 to 9 (match help menu exactly)
- Overhead breakdown: header(2) + footer(1) + box border(2) +
box padding(2) + buffer(2) = 9
- Makes viewport fit more snugly when content is short
- Matches help menu calculation for consistency
Both issues resolved. User should now have:
- ✅ Sticky header/footer
- ✅ Scrollable content with visual scrollbar
- ✅ Arrow key scrolling
- ✅ Mouse wheel scrolling
- ✅ No excess bottom space
- ✅ Responsive window resize1 parent d4c1fc1 commit 3c954be
11 files changed
Lines changed: 2137 additions & 28 deletions
File tree
- internal
- marketplace
- plugin
- search
- ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| 177 | + | |
| 178 | + | |
177 | 179 | | |
178 | 180 | | |
179 | 181 | | |
180 | 182 | | |
181 | 183 | | |
182 | 184 | | |
183 | 185 | | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
184 | 212 | | |
185 | 213 | | |
186 | 214 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
29 | | - | |
| 30 | + | |
| 31 | + | |
30 | 32 | | |
31 | 33 | | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
| 38 | + | |
36 | 39 | | |
37 | 40 | | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
41 | 44 | | |
| 45 | + | |
42 | 46 | | |
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
46 | 50 | | |
47 | 51 | | |
| 52 | + | |
48 | 53 | | |
49 | 54 | | |
50 | 55 | | |
51 | 56 | | |
52 | 57 | | |
53 | 58 | | |
| 59 | + | |
54 | 60 | | |
55 | 61 | | |
56 | 62 | | |
57 | 63 | | |
58 | 64 | | |
59 | 65 | | |
| 66 | + | |
60 | 67 | | |
61 | 68 | | |
62 | 69 | | |
63 | 70 | | |
64 | 71 | | |
65 | 72 | | |
| 73 | + | |
66 | 74 | | |
67 | 75 | | |
68 | 76 | | |
69 | 77 | | |
70 | 78 | | |
71 | 79 | | |
| 80 | + | |
72 | 81 | | |
73 | 82 | | |
74 | 83 | | |
75 | 84 | | |
76 | 85 | | |
77 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
78 | 116 | | |
79 | 117 | | |
80 | 118 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
0 commit comments