Files
TUIkit/.github/copilot-instructions.md
phranck 6c1bda94bf Chore: Consolidate plans directory and standardize AI tool instructions
- Move all plans from plans/ to .claude/plans/ (41 files)
- Add open plans listing to whats-next.md for visibility
- Update docs pipeline (update-plans-data.ts) for new paths
- Rewrite .github/copilot-instructions.md with Swift 6.0 constraint
- Add CONTRIBUTING.md, PR template, AGENTS.md, .cursor/rules, .windsurfrules
- Update global references (~/.claude/CLAUDE.md, project-structure.md)
2026-02-13 16:18:45 +01:00

126 lines
4.3 KiB
Markdown

# Copilot Instructions for TUIkit
## Hard Constraints (non-negotiable)
- **Swift 6.0 only**: `swift-tools-version: 6.0`. Never use features that require a newer compiler.
- **Cross-platform**: Must build and run on both macOS and Linux. CI tests both (`macos-15` + `swift:6.0` container).
- **CI must pass**: All tests and linting must pass before merge.
## Project
TUIkit is a SwiftUI-like framework for building Terminal User Interfaces in pure Swift: no ncurses or C dependencies.
## Build, Test & Lint
```bash
# Build
swift build
# Run all tests (1037+ tests, Swift Testing framework)
swift test
# Run a single test suite
swift test --filter <TestSuiteName>
# Lint
swiftlint
# Format (configured but not enforced in CI)
swift-format format -i -r Sources Tests
```
## Architecture
### View System
TUIkit uses a dual rendering system:
1. **Composite views**: Implement `body` to compose other views. The renderer recurses into `body`.
2. **Primitive views**: Conform to `Renderable` and produce a `FrameBuffer` directly. Set `body: Never`.
The `renderToBuffer(_:context:)` function checks `Renderable` first, then falls back to `body`.
### View Architecture Rules
- Every **public** control must be a `View` with a real `body: some View`
- The `body` must return actual Views (not `Never`, not `fatalError()`)
- `Renderable` is only for leaf nodes (`Text`, `Spacer`, `Divider`) and private `_*Core` views
- All modifiers must propagate through the entire View hierarchy
- Environment values must flow down automatically
**The `_*Core` pattern:**
```swift
// Public View: real body, environment flows through
public struct MyControl<Content: View>: View {
let content: Content
public var body: some View {
_MyControlCore(content: content)
}
}
// Private Core: Renderable for terminal-specific rendering
private struct _MyControlCore<Content: View>: View, Renderable {
let content: Content
var body: Never { fatalError() }
func renderToBuffer(context: RenderContext) -> FrameBuffer { ... }
}
```
Prefer pure composition (combining existing Views + modifiers) over `_*Core` + `Renderable`.
### Key Components
- **`FrameBuffer`**: 2D grid of styled cells representing terminal output
- **`RenderContext`**: Carries layout constraints, environment values, and `TUIContext`
- **`TUIContext`**: Central DI container for lifecycle, key events, preferences, state storage
- **`ViewIdentity`**: Structural identity path for `@State` persistence across renders
### Directory Structure
```
Sources/TUIkit/
├── App/ App lifecycle, Scene, WindowGroup
├── Core/ View protocol, ViewBuilder, TupleViews
├── Environment/ EnvironmentValues, @Environment
├── State/ @State, StateStorage, @AppStorage
├── Rendering/ FrameBuffer, Renderable, Terminal, ANSIRenderer
├── Modifiers/ Border, Frame, Padding, Overlay, Lifecycle
├── Views/ Text, Stacks, Button, Menu, Alert, Dialog, etc.
├── Focus/ FocusManager, focus sections
├── Styling/ Color, Palette, Theme
└── StatusBar/ StatusBar, StatusBarItem
```
## SwiftUI API Parity (non-negotiable)
Public APIs **must** match SwiftUI signatures exactly unless terminal constraints require deviation.
| Aspect | Requirement |
|--------|-------------|
| Parameter names | Exact (`isPresented`, not `isVisible`) |
| Parameter order | Exact (title, binding, actions, message) |
| Parameter types | Match closely (ViewBuilder closures, not pre-built values) |
| Trailing closures | `@ViewBuilder () -> T`, not `String` |
**Before implementing any SwiftUI-equivalent API:** Look up the exact SwiftUI signature first.
## General Rules
- **No singletons**: All state flows through the Environment system
- **Search the codebase** for similar patterns before implementing anything new
- **Consolidate and reuse** before adding new functions or types
- **Never merge PRs autonomously**: Stop after creating, let the user merge
## Testing
- Uses Swift Testing framework (`@Test`, `#expect`, `@Suite`)
- Tests run in parallel
- Test files mirror source structure in `Tests/TUIkitTests/`
## Code Style
- Line length: 140 characters (warning), 200 (error)
- 4-space indentation
- Trailing commas in multi-line collections
- See `.swiftlint.yml` and `.swift-format` for full configuration