sql-cli 1.69.2

SQL query tool for CSV/JSON with both interactive TUI and non-interactive CLI modes - perfect for exploration and automation
Documentation
# State Hierarchy Design Decision

## The Question
Should viewport/cursor locks be tracked as separate states or sub-states in our shadow state manager?

## Current Situation

### What We Have
- **ViewportManager** owns cursor lock and viewport lock state
- **Shadow State** tracks high-level modes (Command, Results, Search, etc.)
- Locks only make sense in Results mode

### The Options

## Option 1: Sub-states within Results Mode
```rust
enum AppState {
    Command,
    Results(ResultsSubState),
    Search { search_type: SearchType },
    // ...
}

enum ResultsSubState {
    Normal,
    CursorLocked,
    ViewportLocked,
    BothLocked,
}
```

**Pros:**
- Accurately represents that locks are Results-specific
- Single source of truth for application state
- Clear state hierarchy

**Cons:**
- Duplicates state that ViewportManager already tracks
- More complex state transitions
- Risk of synchronization issues

## Option 2: Orthogonal State Dimensions
```rust
struct ShadowState {
    mode: AppState,
    cursor_lock: bool,
    viewport_lock: bool,
}
```

**Pros:**
- Simpler, flatter structure
- Easier to track independent toggles
- No duplication with ViewportManager

**Cons:**
- Loses the semantic connection that locks are Results-specific
- Multiple pieces of state to coordinate

## Option 3: Observer Pattern Only (Current)
Keep locks in ViewportManager, shadow state just observes major modes.

**Pros:**
- No duplication
- Clear ownership (ViewportManager owns navigation state)
- Simple shadow state

**Cons:**
- Shadow state doesn't see full picture
- Can't track state combinations like "Results with cursor locked"

## Recommendation

For now, **Option 3** (current approach) is probably best because:

1. **Shadow state is temporary** - It's a learning tool before centralized state management
2. **Avoid premature abstraction** - We're still learning the patterns
3. **Single responsibility** - ViewportManager owns navigation, shadow observes modes

However, we should **log lock changes** for learning:

```rust
// In toggle_cursor_lock action handler
info!(target: "shadow_state", "Cursor lock toggled: {} (in {:?} mode)", 
      is_locked, self.buffer().get_mode());
```

## Future Centralized State

When we move to centralized state management, consider:

```rust
struct ApplicationState {
    mode: AppMode,
    navigation: NavigationState,
    search: SearchState,
    // ...
}

struct NavigationState {
    cursor_position: (usize, usize),
    viewport_offset: (usize, usize),
    cursor_locked: bool,
    viewport_locked: bool,
    // ...
}
```

This gives clear ownership while maintaining accessibility.

## Decision Log

- **Current**: Keep locks in ViewportManager, log changes for learning
- **Future**: Move to hierarchical state with clear domain boundaries
- **Rationale**: Avoid duplication while learning actual usage patterns