Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Guidance for any code agent working on the Architect repo. Keep this file instru
## Coding Conventions
- Favor self-documenting code; keep comments minimal and meaningful.
- Default to ASCII unless the file already uses non-ASCII.
- Always handle errors explicitly: propagate, recover, or log; do not swallow errors with bare `catch {}` / `catch unreachable` unless proven impossible.
- Run `zig fmt src/` (or `zig fmt` on touched Zig files) before wrapping up changes.
- Avoid destructive git commands and do not revert user changes.

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

![Architect Hero](docs/assets/architect-hero.png)

A Zig terminal multiplexer that displays 9 interactive terminal sessions in a 3×3 grid with smooth expand/collapse animations. Built on ghostty-vt for terminal emulation and SDL3 for rendering.
A Zig terminal multiplexer that displays a configurable grid of interactive terminal sessions (default 3×3) with smooth expand/collapse animations. Built on ghostty-vt for terminal emulation and SDL3 for rendering.

> [!WARNING]
> **This project is in early stages of development. Use at your own risk.**
Expand Down Expand Up @@ -71,7 +71,7 @@ cp -r $(brew --prefix)/Cellar/architect/*/Architect.app /Applications/
The formula will:
- Build from source using Zig
- Install all required dependencies (SDL3, SDL3_ttf)
- Create Architect.app with bundled fonts and icon
- Create Architect.app with the application icon (fonts are resolved from your system based on `config.toml`)
- After copying to /Applications, launch from Spotlight or: `open -a Architect`

### Build from Source
Expand All @@ -80,7 +80,7 @@ See [Setup](#setup) section below for building from source.

## Features

- **3×3 Terminal Grid**: Run 9 independent shell sessions simultaneously
- **Configurable Grid**: Run multiple independent shell sessions; defaults to 3×3 but rows/cols are configurable (1–12) in `config.toml`
- **Smooth Animations**: Click any terminal to smoothly expand it to full screen
- **Full-Window Scaling**: Each terminal is sized for the full window and scaled down in grid view
- **Resizable Window**: Dynamically resize the window with automatic terminal and PTY resizing
Expand Down Expand Up @@ -376,7 +376,7 @@ Architect integrates with AI coding assistants through a Unix domain socket prot
- **Per-shell env**: Each spawned shell receives `ARCHITECT_SESSION_ID` (0‑based grid index) and `ARCHITECT_NOTIFY_SOCK` (socket path) so tools inside the terminal can send status.
- **Protocol**: Send a single-line JSON object to the socket:
- `{"session":0,"state":"start"}` clears the highlight and marks the session as running.
- `{"session":0,"state":"awaiting_approval"}` turns on a pulsing yellow border in the 3×3 grid (request).
- `{"session":0,"state":"awaiting_approval"}` turns on a pulsing yellow border in the grid (request).
- `{"session":0,"state":"done"}` shows a solid green border in the grid (completion).

**Example from inside a terminal session:**
Expand Down Expand Up @@ -599,7 +599,7 @@ Download the latest release from the [releases page](https://github.com/forketyf
## Architecture

### Terminal Scaling
Each terminal session is initialized with full-window dimensions (calculated from font metrics). In grid view, these full-sized terminals are scaled down to 1/3 and rendered into grid cells, providing a "zoomed out" view of complete terminal sessions.
Each terminal session is initialized with full-window dimensions (calculated from font metrics). In grid view, these full-sized terminals are scaled down to the current grid cell size, providing a "zoomed out" view of complete terminal sessions regardless of the configured rows/cols.

### Animation System
The application uses cubic ease-in-out interpolation to smoothly transition between grid and full-screen views over 300ms. Six view modes (Grid, Expanding, Full, Collapsing, PanningLeft, PanningRight) manage the animation state, including horizontal panning for terminal switching.
Expand All @@ -613,7 +613,7 @@ The application uses cubic ease-in-out interpolation to smoothly transition betw
## Implementation Status

✅ **Fully Implemented**:
- 3×3 grid layout with 9 terminal sessions
- Configurable grid layout (defaults to 3×3) with per-cell terminal sessions
- PTY management and shell spawning
- Real-time terminal I/O
- SDL3 window and event loop with resizable window support
Expand All @@ -636,7 +636,7 @@ The application uses cubic ease-in-out interpolation to smoothly transition betw

The following features are not yet fully implemented:
- **Emoji coverage is macOS-only**: Apple Color Emoji fallback is used; other platforms may still show tofu or monochrome glyphs for emoji and complex ZWJ sequences.
- **Limited font distribution**: Only the bundled font family ships with the app today
- **Fonts must exist locally**: Architect relies on system-installed fonts; ensure your configured family is available on the host OS.
- **Limited configurability**: Keybindings are hardcoded

## License
Expand Down
17 changes: 9 additions & 8 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ Architect is a terminal multiplexer displaying 9 interactive sessions in a 3×3

## Runtime Flow

**main.zig** owns application lifetime, window sizing, PTY/session startup, configuration persistence, and the frame loop. Each frame it:
**main.zig** owns application lifetime, window sizing, PTY/session startup, configuration persistence, and the frame loop. Each iteration it:

1. Polls SDL events and scales coordinates to render space.
2. Builds a lightweight `UiHost` snapshot and lets `UiRoot` handle events first.
3. Runs remaining app logic (terminal input, resizing, keyboard shortcuts).
4. Runs `xev` loop iteration for async process exit detection.
5. Processes output from all sessions and drains async notifications.
1. Computes a wait deadline (0ms when work is pending, ~16ms during inertia, up to 500ms when idle) and blocks on `SDL_WaitEventTimeout`.
2. SDL events include user events posted by a dedicated IO thread (xev) whenever a PTY has data or a child exits.
3. Builds a lightweight `UiHost` snapshot and lets `UiRoot` handle events first.
4. Runs remaining app logic (terminal input, resizing, keyboard shortcuts).
5. Processes PTY output only for sessions marked “ready” by the IO thread, then flushes queued stdin.
6. Updates UI components and drains `UiAction` queue.
7. Advances animation state if transitioning.
8. Calls `renderer.render` for the scene, then `ui.render` for overlays, then presents.
9. Sleeps based on idle/active frame targets (~16ms active, ~50ms idle).
8. Calls `renderer.render` for the scene, then `ui.render` for overlays, then presents—only when something is dirty/animating.

The IO-facing work is isolated on a dedicated xev thread: it watches all PTY masters for readability and process exits, then posts SDL user events that wake the main loop. Access to the xev loop is serialized with a mutex when (re)registering watchers during spawns or restarts.

**Terminal resizing**
- `applyTerminalResize` updates the PTY size first, then resizes the `ghostty-vt` terminal.
Expand Down
7 changes: 7 additions & 0 deletions src/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ pub const SDL_FillSurfaceRect = c_import.SDL_FillSurfaceRect;
pub const SDL_BlitSurface = c_import.SDL_BlitSurface;
pub const SDL_GetError = c_import.SDL_GetError;
pub const SDL_PollEvent = c_import.SDL_PollEvent;
pub const SDL_WaitEventTimeout = c_import.SDL_WaitEventTimeout;
pub const SDL_PushEvent = c_import.SDL_PushEvent;
pub const SDL_RegisterEvents = c_import.SDL_RegisterEvents;
pub const SDL_Delay = c_import.SDL_Delay;
pub const SDL_StartTextInput = c_import.SDL_StartTextInput;
pub const SDL_StopTextInput = c_import.SDL_StopTextInput;
Expand All @@ -79,6 +82,7 @@ pub const SDL_EVENT_MOUSE_MOTION = c_import.SDL_EVENT_MOUSE_MOTION;
pub const SDL_EVENT_MOUSE_WHEEL = c_import.SDL_EVENT_MOUSE_WHEEL;
pub const SDL_EVENT_WINDOW_RESIZED = c_import.SDL_EVENT_WINDOW_RESIZED;
pub const SDL_EVENT_WINDOW_MOVED = c_import.SDL_EVENT_WINDOW_MOVED;
pub const SDL_EVENT_USER = c_import.SDL_EVENT_USER;
pub const SDL_EVENT_DROP_BEGIN = c_import.SDL_EVENT_DROP_BEGIN;
pub const SDL_EVENT_DROP_FILE = c_import.SDL_EVENT_DROP_FILE;
pub const SDL_EVENT_DROP_TEXT = c_import.SDL_EVENT_DROP_TEXT;
Expand All @@ -96,6 +100,9 @@ pub const SDL_SCANCODE_END = c_import.SDL_SCANCODE_END;
pub const SDL_KMOD_MODE = c_import.SDL_KMOD_MODE;
pub const SDL_SCANCODE_AC_HOME = c_import.SDL_SCANCODE_AC_HOME;
pub const SDL_SCANCODE_AC_END = c_import.SDL_SCANCODE_AC_END;
pub const SDL_DisplayID = c_import.SDL_DisplayID;
pub const SDL_GetPrimaryDisplay = c_import.SDL_GetPrimaryDisplay;
pub const SDL_GetDisplayBounds = c_import.SDL_GetDisplayBounds;

pub const SDL_SetTextureScaleMode = c_import.SDL_SetTextureScaleMode;
pub const SDL_SCALEMODE_LINEAR = c_import.SDL_SCALEMODE_LINEAR;
Expand Down
Loading