Chore: Add project memory and complete shared architecture analysis

- .claude/memory.md: Complete project architecture snapshot (268 lines)
- Enables instant context restoration via /remember
- Move list-table-shared-architecture.md to done/
This commit is contained in:
phranck
2026-02-07 16:43:00 +01:00
parent fb011e2e1e
commit 3cbbe3a6f7
2 changed files with 179 additions and 131 deletions
@@ -0,0 +1,179 @@
# List & Table: Shared Architecture Analysis
## Completed
2026-02-07
## Preface
This analysis identifies the shared architecture between List and Table before implementation: both need focus management, keyboard navigation (Up/Down/Home/End), selection binding, scrolling, and item state rendering. Navigation logic and selection state are identical; rendering differs (List = vertical stack, Table = grid). Extract shared components (handlers, helpers, state managers) to eliminate duplication while letting each component specialize in layout.
## Context / Problem
Both List and Table need focus management, item navigation, selection binding, scrolling, and keyboard handlers. Without careful planning, the implementations will duplicate effort and diverge in behavior.
## Specification / Goal
Analyze shared patterns between List and Table to establish common architecture before implementing either component.
## Existing Patterns Analysis
### 1. Box.swift Pattern
- Composite View that delegates to `.border()` modifier
- Uses `body` property (not Renderable directly)
- Optional parameters with environment defaults
- Equatable conformance for caching
### 2. Focus.swift Pattern
- `Focusable` protocol requires AnyObject (class-based handlers)
- Handler returns `true` from `handleKeyEvent()` to consume events
- FocusManager dispatches to focused element first, then handles Tab/arrows
- Section-based navigation with `context.activeFocusSectionID`
### 3. RadioButtonGroupHandler Pattern (Primary Blueprint)
- Handler class with `focusedIndex` persisted via StateStorage
- Separate navigation index vs selection binding
- Wrap-around navigation (last to first)
- Consumes directional keys even when not acting
- `onFocusLost()` resets focusedIndex to selected item
### 4. StateStorage Pattern
- `StateKey(identity, propertyIndex)` for unique identification
- `stateStorage.storage(for:default:)` returns StateBox
- `stateStorage.markActive(identity)` prevents garbage collection
- StateBox mutations trigger re-render automatically
### 5. ContainerView Pattern
- Two-struct pattern: public View + private `_Core` Renderable
- Width calculation: render content first, then container
- Context width reduction: subtract 2 for borders
- Focus indicator consumption via `context.focusIndicatorColor`
## Architecture Decision
**Option A Selected**: List as View, Table as View, shared `ItemListHandler` class
Rationale:
- Follows existing RadioButtonGroupHandler pattern
- Handler encapsulates all navigation logic
- List and Table differ only in rendering
- No need for complex protocol hierarchies
## Shared Components Design
### 1. ItemListHandler (Focusable class)
```swift
final class ItemListHandler: Focusable {
let focusID: String
var focusedIndex: Int = 0
var scrollOffset: Int = 0
var itemCount: Int = 0
var viewportHeight: Int = 10
var canBeFocused: Bool = true
var onSelect: ((Int) -> Void)?
func handleKeyEvent(_ event: KeyEvent) -> Bool
func onFocusLost()
func ensureFocusedInView()
}
```
Navigation keys:
| Key | Action |
|-----|--------|
| Up | focusedIndex -= 1 (wrap to end) |
| Down | focusedIndex += 1 (wrap to start) |
| Home | focusedIndex = 0 |
| End | focusedIndex = itemCount - 1 |
| PageUp | focusedIndex -= viewportHeight |
| PageDown | focusedIndex += viewportHeight |
| Enter/Space | onSelect?(focusedIndex) |
### 2. Item Rendering States
Four visual states based on focus and selection:
| State | Style |
|-------|-------|
| Focused + Selected | Pulsing accent, bold |
| Focused only | Accent color (navigation cursor) |
| Selected only | Dimmed accent |
| Neither | Default foreground |
Helper function in `ItemStateRenderer`:
```swift
func renderItem(
content: String,
isFocused: Bool,
isSelected: Bool,
pulsePhase: Double,
palette: Palette
) -> String
```
### 3. Scroll Management
Auto-scroll when focus moves out of visible window:
```swift
func ensureFocusedInView() {
if focusedIndex < scrollOffset {
scrollOffset = focusedIndex
} else if focusedIndex >= scrollOffset + viewportHeight {
scrollOffset = focusedIndex - viewportHeight + 1
}
}
```
Scroll indicators: Show up/down arrows when content extends beyond viewport.
### 4. Container Reuse
Both List and Table wrap content in ContainerView via `.border()` modifier:
- Optional title
- Border style from appearance
- Padding handled by modifier chain
## File Structure
```
Sources/TUIkit/
├── Focus/
│ ├── ItemListHandler.swift # Shared navigation handler
│ └── ItemStateRenderer.swift # Shared item rendering
├── Views/
│ ├── List.swift # List view (uses ItemListHandler)
│ └── Table.swift # Table view (uses ItemListHandler)
```
## Keyboard Navigation Model
```
┌─────────────────────────────────────────────────────────────┐
│ FocusManager │
│ dispatchKeyEvent() → focused.handleKeyEvent() │
│ ↓ │
│ ItemListHandler.handleKeyEvent() │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Up/Down/Home/End/PgUp/PgDown: Update focusedIndex │ │
│ │ Enter/Space: Call onSelect │ │
│ │ Return true (consume event) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Tab: FocusManager handles (next section/element) │
└─────────────────────────────────────────────────────────────┘
```
## Implementation Order
1. **ItemListHandler.swift**: Navigation logic, Focusable conformance
2. **ItemStateRenderer.swift**: Focus/selection visual states
3. **List.swift**: Simple vertical list using shared components
4. **Table.swift**: Grid layout with column alignment (future)
## Checklist
- [x] Architecture decisions documented
- [x] Shared patterns identified
- [x] Reference provided for List & Table implementation
- [x] No code changes required (analysis only)
@@ -1,131 +0,0 @@
# List & Table: Shared Architecture Analysis
## Preface
This analysis identifies the shared architecture between List and Table before implementation: both need focus management, keyboard navigation (Up/Down/Home/End), selection binding, scrolling, and item state rendering. Navigation logic and selection state are identical; rendering differs (List = vertical stack, Table = grid). Extract shared components (handlers, helpers, state managers) to eliminate duplication while letting each component specialize in layout.
## Similarities (Redundancy Risk)
### Core Concepts
- **Focus Management**: Both need to track which item is focused (keyboard navigation)
- **Item Focus State**: Up/Down arrows, Home/End, Page Up/Down to navigate items
- **Selection**: Binding to track selected item(s)
- **Scrolling/Viewport**: Both have visible area vs total content
- **Keyboard Handlers**: Up/Down, Enter/Space, Page Up/Down, Home/End
- **Item Rendering with State**: Each item can be focused/selected (visual diff)
### Visual Elements
- **Container**: Border, optional title, padding
- **Focus Indicator**: Visual marker on focused item (background color, bar, etc.)
- **Selection Indicator**: Visual marker on selected item(s)
- **Scroll Indicators**: Up/Down arrows when not at boundaries
### Handler Logic
- Both need `Focusable` interface (for focus manager)
- Both need `StateStorage` for persistence across renders
- Navigation logic (focusUp, focusDown, etc.) is identical
- Scroll offset management is identical
## Differences
### List
- **Layout**: Vertical stack of items (simple)
- **Selection**: Usually single item
- **Item Content**: Arbitrary Views
### Table
- **Layout**: Grid with columns and rows
- **Selection**: Row-based (but multiple columns in display)
- **Column Alignment**: ANSI-aware padding per column
- **Item Content**: Structured (column values, not arbitrary Views)
## Shared Architecture Needed
### 1. Base Focusable Item Handler
Extract common navigation logic into reusable class:
```
FocusableItemListHandler:
- focusedIndex
- scrollOffset
- viewportHeight
- rowCount
- focusUp/Down/Home/End/Page Up/Down
- ensureFocusedInView()
```
### 2. Selection State
Shared pattern for tracking selected item:
```
SelectionState<SelectionValue>:
- binding: Binding<SelectionValue>?
- select(index, value)
- isSelected(index, value) -> Bool
```
### 3. Container & Border Rendering
Both need border + optional title + padding.
Possibly extract into shared utility or reuse existing `ContainerView`.
### 4. View Modifiers (Extensions)
Both should support:
- `.foregroundColor()` (via Environment)
- `.disabled()`
- `.focusable()` (custom?)
- `.padding()` (via modifiers)
### 5. Focus Indicator & Item State Rendering
Common pattern:
```
func renderItemWithState(
content: String,
isFocused: Bool,
isSelected: Bool,
palette: Palette
) -> String
```
## Implementation Strategy
### Phase 1: Extract Common Handler
Create `FocusableItemListBase` or similar with shared navigation logic.
Both List and Table create instances and reuse.
### Phase 2: Shared Selection State
Extract `SelectionStateManager<T>` for consistent selection binding.
### Phase 3: Architecture Decision
- **Option A**: List as View, Table as View, both use private _ListCore/_TableCore Views
- **Option B**: Single generic base (e.g., `_FocusableItemList`) that List and Table wrap
- **Option C**: Trait-based design (protocols for Focus, Selection, Rendering)
## Context / Problem
Both List and Table need focus management, item navigation, selection binding, scrolling, and keyboard handlers. Without careful planning, the implementations will duplicate effort and diverge in behavior.
## Specification / Goal
Analyze shared patterns between List and Table to establish common architecture before implementing either component.
## Design
### Shared Concepts Analysis (No Implementation)
Just analyze for reference before implementing either List or Table. Areas to investigate:
1. How Box handles modifiers
2. How existing focus management works
3. How Focusable interface is used elsewhere
4. What StateStorage patterns already exist
5. Container rendering patterns
Then design List & Table to maximize code reuse.
## Implementation Plan
This is an analysis document only. No implementation tasks.
## Checklist
- [ ] Architecture decisions documented
- [ ] Shared patterns identified
- [ ] Reference provided for List & Table implementation
- [ ] No code changes required