Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: openpi-e2e
name: pidex-e2e
path: |
/tmp/pidex-*.png
test-results/
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
**A desktop workbench for the Pi coding agent.**
**Workspace-first, not chat-first.**

[Quick Start](#quick-start) · [Architecture](docs/openpi-architecture.md) · [Design tokens](#design-tokens) · [Docs](docs/openpi-project-brief.md)
[Quick Start](#quick-start) · [Architecture](docs/pidex-architecture.md) · [Design tokens](#design-tokens) · [Docs](docs/pidex-project-brief.md)

[![CI](https://github.com/echohello-dev/pidex/actions/workflows/ci.yml/badge.svg)](https://github.com/echohello-dev/pidex/actions/workflows/ci.yml)
[![Electron 41](https://img.shields.io/badge/electron-41-blue)](https://www.electronjs.org/)
Expand Down Expand Up @@ -37,7 +37,7 @@ For a production build: `mise run build` then `mise run start`.
┌────────────────────────────────────────┐
│ Renderer (React 19) │ ← dashboard, session tabs, timeline
└───────────────────┬────────────────────┘
│ typed IPC (window.openpi)
│ typed IPC (window.pidex)
┌───────────────────▼────────────────────┐
│ Main process (Electron) │ ← workspace registry, event normalization
└───────────────────┬────────────────────┘
Expand All @@ -47,13 +47,13 @@ For a production build: `mise run build` then `mise run start`.
└────────────────────────────────────────┘
```

Pi runs out-of-process over RPC so the UI survives a runtime crash. Pretext handles text measurement so virtualized lists stay smooth in long sessions. Full detail in [docs/openpi-architecture.md](docs/openpi-architecture.md).
Pi runs out-of-process over RPC so the UI survives a runtime crash. Pretext handles text measurement so virtualized lists stay smooth in long sessions. Full detail in [docs/pidex-architecture.md](docs/pidex-architecture.md).

## Built on

| Layer | Choice |
|---|---|
| Shell | Electron 41, context-isolated, typed `window.openpi` preload bridge |
| Shell | Electron 41, context-isolated, typed `window.pidex` preload bridge |
| UI | React 19.2, Vite 8 with HMR |
| Text | [`@chenglou/pretext`](https://github.com/chenglou/pretext), DOM-free |
| Language | TypeScript 6 |
Expand Down Expand Up @@ -96,9 +96,9 @@ uv run --with fonttools python scripts/build-screenshot.py # docs/assets/scree

| Doc | What it covers |
|---|---|
| [docs/openpi-project-brief.md](docs/openpi-project-brief.md) | Thesis, pain points, positioning, MVP slice |
| [docs/openpi-architecture.md](docs/openpi-architecture.md) | Process boundaries, IPC schema, SDK vs RPC |
| [docs/openpi-pretext-deep-dive.md](docs/openpi-pretext-deep-dive.md) | Why DOM-free text measurement |
| [docs/pidex-project-brief.md](docs/pidex-project-brief.md) | Thesis, pain points, positioning, MVP slice |
| [docs/pidex-architecture.md](docs/pidex-architecture.md) | Process boundaries, IPC schema, SDK vs RPC |
| [docs/pidex-pretext-deep-dive.md](docs/pidex-pretext-deep-dive.md) | Why DOM-free text measurement |

## License

Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0002-pierre-diffs-renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
- **Status**: Accepted (2026-07-19)
- **Context**: The workbench surfaces code changes from agent runs (file-level diffs from `bash`/`edit`/`write` tools). The change surface needs a renderer that handles large files, performs syntax highlighting consistently with code blocks, and supports future merge-conflict UI. The repo's research note (`2026-06-26 - Spanner Layer - Search, Syntax, Markdown, Diff.md`) ranked candidates: `@pierre/diffs`, `react-diff-viewer`, `react-virtualized-diff`, raw `jsdiff`. Pierre was the recommended primary — same engine used by Claude Code, Codex, Cursor, and OpenChamber — and pairs naturally with `shiki` for syntax highlighting. The earlier "Pidex - Pi Ecosystem Technology Stack Research" note (`2026-05-31`) flagged that `openchamber` already uses `@pierre/diffs 1.1.0-beta.13`.

- **Decision**: Adopt `@pierre/diffs` as the primary diff engine. Wrap its React `<PatchDiff>` component in `<PierreDiff>` to provide a small API (`oldFile`, `newFile`, `filename`) and a unified-patch generator. Use `theme: 'pierre-dark'` to match the OpenPi palette.
- **Decision**: Adopt `@pierre/diffs` as the primary diff engine. Wrap its React `<PatchDiff>` component in `<PierreDiff>` to provide a small API (`oldFile`, `newFile`, `filename`) and a unified-patch generator. Use `theme: 'pierre-dark'` to match the pidex palette.

- **Consequences**:
- Diff renderer scales to 10k+ lines via per-line virtualisation in Shadow DOM.
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0003-pretext-measurement.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ADR 0003 — Use @chenglou/pretext for text measurement only

- **Status**: Accepted (2026-07-19)
- **Context**: The workbench renders long session timelines (10k+ messages in long sessions). The renderer needs a way to know row heights for the status-bar measurement counter and, eventually, for virtualised row sizing before mounting DOM. `@chenglou/pretext` (Cheng Lou) provides Canvas-2D-based text measurement that is ~300× faster than DOM-based measurement for re-layout. The original OpenPi architecture (`docs/openpi-architecture.md`) called for Pretext specifically as a "layout dispatch" primitive.
- **Context**: The workbench renders long session timelines (10k+ messages in long sessions). The renderer needs a way to know row heights for the status-bar measurement counter and, eventually, for virtualised row sizing before mounting DOM. `@chenglou/pretext` (Cheng Lou) provides Canvas-2D-based text measurement that is ~300× faster than DOM-based measurement for re-layout. The original pidex architecture (`docs/pidex-architecture.md`) called for Pretext specifically as a "layout dispatch" primitive.

- **Decision**: Use `@chenglou/pretext` strictly for text measurement — row counting for the status bar today, and (future) row heights for virtualised lists. Do not use it for glyph rendering; rely on `markdown-it` + browser text layout for that.

Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0005-oxlint.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ADR 0005 — Use oxlint as the lint tool

- **Status**: Accepted (2026-07-19)
- **Context**: The repo had no lint gate. ESLint is the default but slow to install and configure, and many of its plugins don't work cleanly with React 19 + Vitest. The OpenPi project has a single-language (TypeScript) codebase and needs correctness guarantees, not style debates.
- **Context**: The repo had no lint gate. ESLint is the default but slow to install and configure, and many of its plugins don't work cleanly with React 19 + Vitest. The pidex project has a single-language (TypeScript) codebase and needs correctness guarantees, not style debates.

- **Decision**: Adopt `oxlint` for CI lint. Configure it via `.oxlintrc.json` with the `correctness` category as error and the rest allowed. Style formatting is left to manual conventions.

Expand Down
4 changes: 2 additions & 2 deletions docs/adr/0006-pi-runtime-rpc.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# ADR 0006 — Pi runtime integration via JSONL RPC

- **Status**: Accepted (2026-07-19)
- **Context**: OpenPi is an Electron workbench for the Pi coding agent. Pi supports four runtime modes (`interactive`, `print/JSON`, `RPC`, `SDK`). The renderer needs streaming events and prompt submission. Embedding the SDK in-process bloats the renderer with Node-only dependencies; interactive mode is TUI-only. RPC mode is the documented headless protocol — JSON lines over stdin/stdout, `prompt`/`get_state`/`get_messages`/`get_commands` commands, `agent_start`/`message_update`/`tool_execution_*` events.
- **Context**: pidex is an Electron workbench for the Pi coding agent. Pi supports four runtime modes (`interactive`, `print/JSON`, `RPC`, `SDK`). The renderer needs streaming events and prompt submission. Embedding the SDK in-process bloats the renderer with Node-only dependencies; interactive mode is TUI-only. RPC mode is the documented headless protocol — JSON lines over stdin/stdout, `prompt`/`get_state`/`get_messages`/`get_commands` commands, `agent_start`/`message_update`/`tool_execution_*` events.

- **Decision**: Spawn `pi --mode rpc` from the Electron main process per active session tab. Line-buffer stdout on `\n` only (no `readline` — the protocol forbids U+2028/U+2029 splitting). Forward events to the renderer over `webContents.send`. Re-emit Pi errors as `openpi:session:exit`. Validate `sessionFile` is under `~/.pi/agent/sessions` before resume.
- **Decision**: Spawn `pi --mode rpc` from the Electron main process per active session tab. Line-buffer stdout on `\n` only (no `readline` — the protocol forbids U+2028/U+2029 splitting). Forward events to the renderer over `webContents.send`. Re-emit Pi errors as `pidex:session:exit`. Validate `sessionFile` is under `~/.pi/agent/sessions` before resume.

- **Consequences**:
- Strong process boundary; renderer never sees Node APIs beyond the typed bridge.
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0007-native-tab-bar.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ADR 0007 — Native tab bar with hidden-inset title bar

- **Status**: Accepted (2026-07-19)
- **Context**: The workbench has a workspace/session model with multiple open sessions. macOS users expect Chrome-style native tabs (`NSWindow tabbingMode`) OR a custom drag region with native traffic-light controls. The original OpenPi brief calls for "few clicks, fast switching, high awareness" and a "workspace-first" mental model — both benefit from a tab strip at the top of the window.
- **Context**: The workbench has a workspace/session model with multiple open sessions. macOS users expect Chrome-style native tabs (`NSWindow tabbingMode`) OR a custom drag region with native traffic-light controls. The original pidex brief calls for "few clicks, fast switching, high awareness" and a "workspace-first" mental model — both benefit from a tab strip at the top of the window.

- **Decision**: Use `titleBarStyle: 'hiddenInset'` on the `BrowserWindow`. Reserve `84px` of left padding on the renderer tab strip to clear the macOS traffic lights at the inset position. Mark the tab strip with `-webkit-app-region: drag` so the user can drag the window by the strip; mark every interactive child with `no-drag` so buttons remain clickable. Render the tab strip in-app with custom styles.

Expand Down
4 changes: 2 additions & 2 deletions docs/adr/0008-pi-dev-design-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
- **Status**: Accepted (2026-07-19)
- **Context**: The workbench is a desktop wrapper around the Pi coding agent. The Pi product (pi.dev) has its own distinctive visual language: deep ink canvas `#0d1116`, moonstone text, tidal-blue accent, terracotta rust, Departure Mono labels, Commit Mono code, bracketed buttons `[ ACTION ]`, corner-bracketed figure frames, lowercase `›` and `$` shell prompts. Building a separate design system would feel disjointed.

- **Decision**: Mirror the pi.dev design system in OpenPi. Bundle `Commit Mono` (OFL) and `Departure Mono` (OFL) locally; fall back to system serifs/mono for the licensed Plantin MT Pro. Tokens live in `src/renderer/design-system/tokens.css` and components in `components.css`.
- **Decision**: Mirror the pi.dev design system in pidex. Bundle `Commit Mono` (OFL) and `Departure Mono` (OFL) locally; fall back to system serifs/mono for the licensed Plantin MT Pro. Tokens live in `src/renderer/design-system/tokens.css` and components in `components.css`.

- **Consequences**:
- Screenshots from OpenPi feel like native Pi product surfaces.
- Screenshots from pidex feel like native Pi product surfaces.
- Designers familiar with Pi can transfer work without re-learning a design system.
- Token names match Pi's published names (`--bg-deep`, `--accent`, `--panel`) — easy to grep and update.
- Local font assets ship ~850KB; acceptable for desktop.
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0009-calver-releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
- Predictable, sortable, machine-friendly tags.
- No "is this a breaking change" ceremony — every push to main tags a release.
- Dropped `release-please` (semver, PR-based) for a small workflow that just calls `gh release create --generate-notes`.
- Inconsistent with Pi CLI semver — keep documentation clear that OpenPi tags are CalVer.
- Inconsistent with Pi CLI semver — keep documentation clear that pidex tags are CalVer.
- If we ever publish a public API, we revisit and add semver alongside.
2 changes: 1 addition & 1 deletion docs/adr/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Architecture Decision Records

ADRs document significant architectural choices made in OpenPi.
ADRs document significant architectural choices made in pidex.

Each ADR follows this shape:

Expand Down
40 changes: 20 additions & 20 deletions docs/openpi-architecture.md → docs/pidex-architecture.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# OpenPi Architecture Proposal
# pidex Architecture Proposal

This document captures the first-pass architecture for an Electron desktop app that wraps the Pi coding agent runtime and stays responsive under long sessions, diffs, and workspace switching.

Expand Down Expand Up @@ -55,7 +55,7 @@ Owns the narrow, typed bridge between renderer and main.

Suggested responsibilities:

- Expose a minimal `window.openpi` API
- Expose a minimal `window.pidex` API
- Keep IPC channels explicit and typed
- Prevent renderer code from touching Node APIs directly

Expand Down Expand Up @@ -101,28 +101,28 @@ The IPC layer should be event-driven, typed, and intentionally small.

| Channel | Direction | Purpose |
|---|---|---|
| `openpi:workspace/open` | renderer -> main | Open or focus a workspace |
| `openpi:workspace/refresh` | renderer -> main | Re-read repo and session state |
| `openpi:session/start` | renderer -> main | Start a new Pi session |
| `openpi:session/resume` | renderer -> main | Resume an existing session |
| `openpi:session/fork` | renderer -> main | Fork a session branch |
| `openpi:agent/send` | renderer -> main | Send a prompt or command to Pi |
| `openpi:diff/open` | renderer -> main | Open a file diff or patch view |
| `openpi:file/open` | renderer -> main | Open a file in the workspace |
| `openpi:git/checkout` | renderer -> main | Switch branches or worktrees |
| `pidex:workspace/open` | renderer -> main | Open or focus a workspace |
| `pidex:workspace/refresh` | renderer -> main | Re-read repo and session state |
| `pidex:session/start` | renderer -> main | Start a new Pi session |
| `pidex:session/resume` | renderer -> main | Resume an existing session |
| `pidex:session/fork` | renderer -> main | Fork a session branch |
| `pidex:agent/send` | renderer -> main | Send a prompt or command to Pi |
| `pidex:diff/open` | renderer -> main | Open a file diff or patch view |
| `pidex:file/open` | renderer -> main | Open a file in the workspace |
| `pidex:git/checkout` | renderer -> main | Switch branches or worktrees |

### Event channels

| Channel | Direction | Purpose |
|---|---|---|
| `openpi:workspace/state` | main -> renderer | Current workspace snapshot |
| `openpi:session/state` | main -> renderer | Session lifecycle and metadata |
| `openpi:agent/event` | main -> renderer | Normalized runtime events |
| `openpi:agent/token` | main -> renderer | Streamed text deltas |
| `openpi:agent/tool` | main -> renderer | Tool call start / progress / result |
| `openpi:git/state` | main -> renderer | Repo, branch, dirty, commit state |
| `openpi:fs/change` | main -> renderer | File tree or watch updates |
| `openpi:ui/notify` | main -> renderer | Toasts, errors, approvals, prompts |
| `pidex:workspace/state` | main -> renderer | Current workspace snapshot |
| `pidex:session/state` | main -> renderer | Session lifecycle and metadata |
| `pidex:agent/event` | main -> renderer | Normalized runtime events |
| `pidex:agent/token` | main -> renderer | Streamed text deltas |
| `pidex:agent/tool` | main -> renderer | Tool call start / progress / result |
| `pidex:git/state` | main -> renderer | Repo, branch, dirty, commit state |
| `pidex:fs/change` | main -> renderer | File tree or watch updates |
| `pidex:ui/notify` | main -> renderer | Toasts, errors, approvals, prompts |

### Core payload shapes

Expand Down Expand Up @@ -187,7 +187,7 @@ type AgentEvent =
- Use **Pi SDK** for the first implementation if the priority is rich integration and fastest product iteration.
- Use **Pi RPC** if the priority is runtime isolation, restartability, and keeping the Electron main process lightweight.

### Practical choice for OpenPi
### Practical choice for pidex

Start with **SDK** if:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Pretext Deep-Dive for OpenPi
# Pretext Deep-Dive for pidex

## What Pretext Actually Is

Expand All @@ -16,7 +16,7 @@ Package name is `@chenglou/pretext`. The npm registry has it under this scoped n

##Core API

Pretext serves two use cases. Both matter for OpenPi.
Pretext serves two use cases. Both matter for pidex.

### Use case 1: Measure a paragraph's height without the DOM

Expand Down Expand Up @@ -167,11 +167,11 @@ Designed to be narrow: `white-space: normal` only, caller-owned extraWidth for p
- On every render frame
- Inside a React render function

## Caveats That Matter for OpenPi
## Caveats That Matter for pidex

1. **Font loading**: Fonts must be loaded before `prepare()`. Not a problem in Electron since you ship fonts locally.

2. **`system-ui` is unsafe on macOS** for layout() accuracy. Use a named font. OpenPi should bundle Inter and JetBrains Mono.
2. **`system-ui` is unsafe on macOS** for layout() accuracy. Use a named font. pidex should bundle Inter and JetBrains Mono.

3. **Not a bidi layout engine**. Segment widths are canvas widths for line breaking only. Not precise enough for custom Arabic or mixed-direction text reconstruction. Adequate for most coding-tool UI.

Expand All @@ -181,7 +181,7 @@ Designed to be narrow: `white-space: normal` only, caller-owned extraWidth for p

6. **Version note**: The API changed significantly between early 2026 previews and the current npm release. The current API uses: `prepare(text, font)` returning opaque handle, `layout(prepared, maxWidth, lineHeight)` with `lineHeight` as a layout-time input (not in prepare).

## Where Pretext Fits in OpenPi
## Where Pretext Fits in pidex

### High-impact areas

Expand Down Expand Up @@ -248,19 +248,19 @@ Key point: **this lives in a view-model layer, not in React render**. Computed o
| DOM `getBoundingClientRect` | ~0ms (skip prepare) | ~1ms+ | Yes | No | Ground truth (but slow) |
| Heuristic/guesswork | ~0ms | ~0ms | No | N/A | Unreliable (causes jank) |

For OpenPi's workload (thousands of messages, dozens of workspaces, frequent updates), the one-time prepare cost pays off immediately. The alternative — repeated DOM reads on scroll, resize, or data update — compounds into the exact kind of lag that OpenPi is trying to eliminate.
For pidex's workload (thousands of messages, dozens of workspaces, frequent updates), the one-time prepare cost pays off immediately. The alternative — repeated DOM reads on scroll, resize, or data update — compounds into the exact kind of lag that pidex is trying to eliminate.

## What Pretext Enables That CSS Can't

Pretext uniquely enables **shrinkwrap text measurement**: finding the minimum container width that fits a paragraph without changing line count. CSS has `fit-content` which gives the widest line's width, but no equivalent for "find the narrowest width that still produces exactly N lines". Pretext's `walkLineRanges()` + binary search gives you this in pure arithmetic.

In OpenPi, this means:
In pidex, this means:
- Workspace status cards that size to content without wasted space
- Tool output cards that fit text exactly, even with variable-length content
- Balanced text layout for session summaries
- Multiline "shrinkwrap" that has been missing from the web

## Recommended Implementation Order for OpenPi
## Recommended Implementation Order for pidex

1. Session timeline cards (most text, most updates)
2. Workspace summary list
Expand Down
Loading
Loading