diff --git a/.kiro/docs/architecture-en.md b/.kiro/docs/architecture-en.md index 6747501..727b9bc 100644 --- a/.kiro/docs/architecture-en.md +++ b/.kiro/docs/architecture-en.md @@ -163,10 +163,29 @@ unic init --force # Overwrite existing config file |---------|---------|--------| | EC2 | SSM Session Manager (connect to EC2 instances) | โœ… Implemented | | VPC | VPC Browser (VPCs โ†’ subnets โ†’ available IPs) | โœ… Implemented | -| RDS | ListDBInstances | ๐Ÿšง Coming Soon | +| RDS | RDS Browser (list, detail, start/stop/failover) | โœ… Implemented | | Route53 | ListHostedZones | ๐Ÿšง Coming Soon | | IAM | ListUsers | ๐Ÿšง Coming Soon | +## Planned Improvements + +### M5 โ€” UI Beautification (Charmbracelet Ecosystem) + +- **File extraction**: Split `internal/app/app.go` (~1700 lines) into `styles.go`, `views.go`, `commands.go`, `filter.go` +- **bubbles components**: Add `bubbles/textinput` (filter input), `bubbles/spinner` (loading), `bubbles/table` (context picker) +- **Enhanced styles**: Bordered list views, full-width status bar, consistent label alignment, styled help bar +- Dependency: `github.com/charmbracelet/bubbles` + +### M6 โ€” Search/Filter for Long Lists + +- **Fuzzy matching**: Replace `strings.Contains` with `sahilm/fuzzy` for scored fuzzy search +- **Match highlighting**: Bold + orange on matched characters in list items +- **Universal filter**: Add "/" filter to all list views (VPC list and subnet list currently missing) +- **Unified architecture**: `Filterable` interface + generic `applyFuzzyFilter[T]()` to eliminate per-screen duplication +- Dependency: `github.com/sahilm/fuzzy` + +See `PLAN.md` for full milestone details and implementation order. + ## New Feature Addition Checklist 1. `internal/domain/model.go` โ†’ Add `AwsService` and `FeatureKind` string constants diff --git a/.kiro/docs/architecture.md b/.kiro/docs/architecture.md index de5274b..f209e25 100644 --- a/.kiro/docs/architecture.md +++ b/.kiro/docs/architecture.md @@ -163,10 +163,29 @@ unic init --force # ๊ธฐ์กด ์„ค์ • ํŒŒ์ผ ๋ฎ์–ด์“ฐ๊ธฐ |--------|------|------| | EC2 | SSM Session Manager (EC2 ์ธ์Šคํ„ด์Šค ์ ‘์†) | โœ… ๊ตฌํ˜„ ์™„๋ฃŒ | | VPC | VPC Browser (VPC โ†’ ์„œ๋ธŒ๋„ท โ†’ ๊ฐ€์šฉ IP) | โœ… ๊ตฌํ˜„ ์™„๋ฃŒ | -| RDS | ListDBInstances | ๐Ÿšง Coming Soon | +| RDS | RDS Browser (๋ชฉ๋ก, ์ƒ์„ธ, ์‹œ์ž‘/์ค‘์ง€/์žฅ์• ์กฐ์น˜) | โœ… ๊ตฌํ˜„ ์™„๋ฃŒ | | Route53 | ListHostedZones | ๐Ÿšง Coming Soon | | IAM | ListUsers | ๐Ÿšง Coming Soon | +## ๊ณ„ํš๋œ ๊ฐœ์„  ์‚ฌํ•ญ + +### M5 โ€” UI ๊ฐœ์„  (Charmbracelet ์ƒํƒœ๊ณ„) + +- **ํŒŒ์ผ ๋ถ„๋ฆฌ**: `internal/app/app.go` (~1700์ค„)๋ฅผ `styles.go`, `views.go`, `commands.go`, `filter.go`๋กœ ๋ถ„๋ฆฌ +- **bubbles ์ปดํฌ๋„ŒํŠธ**: `bubbles/textinput` (ํ•„ํ„ฐ ์ž…๋ ฅ), `bubbles/spinner` (๋กœ๋”ฉ), `bubbles/table` (์ปจํ…์ŠคํŠธ ์„ ํƒ๊ธฐ) ์ถ”๊ฐ€ +- **์Šคํƒ€์ผ ๊ฐ•ํ™”**: ํ…Œ๋‘๋ฆฌ๊ฐ€ ์žˆ๋Š” ๋ชฉ๋ก ๋ทฐ, ์ „์ฒด ๋„ˆ๋น„ ์ƒํƒœ ๋ฐ”, ์ผ๊ด€๋œ ๋ ˆ์ด๋ธ” ์ •๋ ฌ, ์Šคํƒ€์ผ ์ ์šฉ๋œ ๋„์›€๋ง ๋ฐ” +- ์˜์กด์„ฑ: `github.com/charmbracelet/bubbles` + +### M6 โ€” ๊ธด ๋ชฉ๋ก ๊ฒ€์ƒ‰/ํ•„ํ„ฐ + +- **ํผ์ง€ ๋งค์นญ**: `strings.Contains`๋ฅผ `sahilm/fuzzy`๋กœ ๊ต์ฒดํ•˜์—ฌ ์ ์ˆ˜ ๊ธฐ๋ฐ˜ ํผ์ง€ ๊ฒ€์ƒ‰ ๊ตฌํ˜„ +- **๋งค์นญ ํ•˜์ด๋ผ์ดํŠธ**: ๋งค์นญ๋œ ๋ฌธ์ž์— ๊ตต์€์ฒด + ์ฃผํ™ฉ์ƒ‰ ์Šคํƒ€์ผ ์ ์šฉ +- **์ „์ฒด ํ•„ํ„ฐ ์ง€์›**: ๋ชจ๋“  ๋ชฉ๋ก ๋ทฐ์— "/" ํ•„ํ„ฐ ์ถ”๊ฐ€ (ํ˜„์žฌ VPC ๋ชฉ๋ก, ์„œ๋ธŒ๋„ท ๋ชฉ๋ก์— ๋ฏธ์ง€์›) +- **ํ†ตํ•ฉ ์•„ํ‚คํ…์ฒ˜**: `Filterable` ์ธํ„ฐํŽ˜์ด์Šค + ์ œ๋„ค๋ฆญ `applyFuzzyFilter[T]()` ๋กœ ํ™”๋ฉด๋ณ„ ์ค‘๋ณต ์ œ๊ฑฐ +- ์˜์กด์„ฑ: `github.com/sahilm/fuzzy` + +์ž์„ธํ•œ ๋งˆ์ผ์Šคํ†ค ๋ฐ ๊ตฌํ˜„ ์ˆœ์„œ๋Š” `PLAN.md` ์ฐธ์กฐ. + ## ์ƒˆ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์ฒดํฌ๋ฆฌ์ŠคํŠธ 1. `internal/domain/model.go` โ†’ `AwsService`, `FeatureKind` ๋ฌธ์ž์—ด ์ƒ์ˆ˜ ์ถ”๊ฐ€ diff --git a/.kiro/steering/project-overview-en.md b/.kiro/steering/project-overview-en.md index dffe335..fcfd5c0 100644 --- a/.kiro/steering/project-overview-en.md +++ b/.kiro/steering/project-overview-en.md @@ -9,9 +9,10 @@ UNIC is a Go-based TUI (Terminal User Interface) tool for browsing and managing ## Tech Stack - Language: Go (1.22+) -- TUI Framework: Bubbletea + Lipgloss + Bubbles +- TUI Framework: Bubbletea + Lipgloss + Bubbles (planned: bubbles/textinput, bubbles/spinner, bubbles/table) +- Search: sahilm/fuzzy (planned โ€” M6 fuzzy search/filter) - CLI Parser: Cobra -- AWS SDK: aws-sdk-go-v2 (ec2, ssm, sts) +- AWS SDK: aws-sdk-go-v2 (ec2, rds, ssm, sts, sso, ssooidc) - Config: gopkg.in/yaml.v3 (YAML-based) - Concurrency: goroutines + errgroup - Error Handling: fmt.Errorf wrapping / standard errors package diff --git a/.kiro/steering/project-overview.md b/.kiro/steering/project-overview.md index 2d74654..5c48d9e 100644 --- a/.kiro/steering/project-overview.md +++ b/.kiro/steering/project-overview.md @@ -9,9 +9,10 @@ UNIC์€ Go ๊ธฐ๋ฐ˜ TUI(Terminal User Interface) ๋„๊ตฌ๋กœ, AWS ๋ฆฌ์†Œ์Šค๋ฅผ ํƒ ## ๊ธฐ์ˆ  ์Šคํƒ - ์–ธ์–ด: Go (1.22+) -- TUI ํ”„๋ ˆ์ž„์›Œํฌ: Bubbletea + Lipgloss + Bubbles +- TUI ํ”„๋ ˆ์ž„์›Œํฌ: Bubbletea + Lipgloss + Bubbles (๊ณ„ํš: bubbles/textinput, bubbles/spinner, bubbles/table) +- ๊ฒ€์ƒ‰: sahilm/fuzzy (๊ณ„ํš โ€” M6 ํผ์ง€ ๊ฒ€์ƒ‰/ํ•„ํ„ฐ) - CLI ํŒŒ์„œ: Cobra -- AWS SDK: aws-sdk-go-v2 (ec2, ssm, sts) +- AWS SDK: aws-sdk-go-v2 (ec2, rds, ssm, sts, sso, ssooidc) - ์„ค์ • ํŒŒ์ผ: gopkg.in/yaml.v3 (YAML ๊ธฐ๋ฐ˜) - ๋™์‹œ์„ฑ: goroutines + errgroup - ์—๋Ÿฌ ์ฒ˜๋ฆฌ: fmt.Errorf ๋ž˜ํ•‘ / ํ‘œ์ค€ errors ํŒจํ‚ค์ง€ diff --git a/PLAN.md b/PLAN.md index 8877c03..f68726f 100644 --- a/PLAN.md +++ b/PLAN.md @@ -159,9 +159,9 @@ unic/ **M4.1 โ€” UX** - Keyboard shortcut help screen (`?` key) -- Fuzzy search/filter on all list views -- Loading spinners for async operations - Color theme (respect terminal colors via Lipgloss) +- ~~Fuzzy search/filter on all list views~~ โ†’ moved to M6 +- ~~Loading spinners for async operations~~ โ†’ moved to M5.3 **M4.2 โ€” Error Handling & Logging** - Structured error messages with actionable hints @@ -176,6 +176,66 @@ unic/ --- +### M5 โ€” UI Beautification (Charmbracelet Ecosystem) + +Prerequisite: `go get github.com/charmbracelet/bubbles` + +**M5.1 โ€” File Extraction (no behavior change)** +- Split `internal/app/app.go` (~1700 lines) into focused files: + - `styles.go` โ€” all lipgloss style vars + new styles + - `views.go` โ€” all `view*()` methods + `renderStatusBar()` + - `commands.go` โ€” all `tea.Cmd` functions (`load*`, `execute*`, `poll*`, etc.) + - `filter.go` โ€” filter logic (`applyFilter`, `applyRDSFilter`, `applyIPFilter`) +- `app.go` retains: Model struct, `New()`, `Init()`, `Update()`, and all `update*()` methods + +**M5.2 โ€” bubbles/textinput for Filter** +- Replace manual character-by-character filter input with a single shared `textinput.Model` +- On "/" key โ†’ `filterTI.Focus()`, on esc/enter โ†’ `filterTI.Blur()` +- Check `filterTI.Focused()` instead of `filterActive` booleans +- Remove: `filterInput`, `filterActive`, `rdsFilter`, `rdsFilterActive`, `ipFilter`, `ipFilterActive` + +**M5.3 โ€” bubbles/spinner for Loading** +- Replace static `"Loading..."` with animated spinner (`spinner.MiniDot`) +- Start spinner tick on entering `screenLoading`, ignore ticks elsewhere + +**M5.4 โ€” bubbles/table for Context Picker** +- Convert manual column-aligned context picker to `bubbles/table` +- Columns: Name, Region, Auth, Current (*) +- Keep "a" key shortcut for adding contexts + +**M5.5 โ€” Enhanced Lipgloss Styles** +- Status bar: full-width via `lipgloss.Width(m.width)`, left/right split with `JoinHorizontal` +- List views: wrap content in `lipgloss.RoundedBorder()` box +- Detail views: consistent label alignment with `lipgloss.NewStyle().Width(14)` +- Help bar: consistent `helpStyle` across all screens + +--- + +### M6 โ€” Search/Filter for Long Lists + +Prerequisite: `go get github.com/sahilm/fuzzy` + +**M6.1 โ€” Fuzzy Matching** +- Replace `strings.Contains` filter logic with `sahilm/fuzzy` scored fuzzy matching +- Match against `DisplayTitle()` for both filtering and highlighting +- Sort results by score descending + +**M6.2 โ€” Match Highlighting** +- Highlight matched characters with bold + orange (color 214) style +- Use match indices from fuzzy library to render per-character styling + +**M6.3 โ€” Filter on All List Views** +- Add "/" filter to VPC list and subnet list (currently missing) +- All filterable items already implement `FilterText()` and `DisplayTitle()` + +**M6.4 โ€” Unified Filter Architecture** +- Define `Filterable` interface: `FilterText() string`, `DisplayTitle() string` +- Implement generic `applyFuzzyFilter[T Filterable](items []T, query string) []filterMatch[T]` +- Store `matchIndices [][]int` parallel to filtered slices for highlighting +- Eliminate per-screen filter state duplication + +--- + ## Implementation Order ``` @@ -184,12 +244,20 @@ M1.1 โ†’ M1.2 โ†’ M2.1 โ†’ M2.2 โ†’ M2.3 โ†’ M2.4 M3.1 ~ M3.8 (independent, any order) โ†“ M4.1 โ†’ M4.2 โ†’ M4.3 + โ†“ + M5.1 โ†’ M5.2 ~ M5.5 + โ†“ + M6.1 ~ M6.4 ``` -- M1 is complete; M2 is partially complete (SSO, credential, assume role done; Okta deferred) +Note: M5.2 (textinput) provides the foundation for M6 (enhanced search) โ€” they converge naturally. + +- M1 is complete; M2 is deferred (relying on AWS SDK default credential chain) +- M2.1 (Context-based auth with SSO, credential, assume-role) is complete - M3 services are independent of each other, build in any order - M3.1 (VPC), M3.2 (RDS), and M3.4 (SSM Sessions) are complete - M4.3 (Distribution) is partially done (GoReleaser + GitHub Actions) +- M5 and M6 can begin independently of remaining M3/M4 work ---