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
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@

Daily coding-agent work means juggling repos, branches, and sessions, and the current tools are chat-first. pidex is the kitchen bench: every active repo, its branch, its sessions, and what's stale, visible in one glance.

## In action

| | |
|---|---|
| ![Boot](docs/assets/workbench-boot.gif) | ![Loaded](docs/assets/workbench-loaded.gif) |
| App booting, workspaces populate | The workbench, fully loaded |

## Quick Start

You need [mise](https://mise.jdx.dev/) and the [Pi CLI](https://pi.dev/). The dashboard reads existing sessions from `~/.pi/agent/sessions`.
Expand Down Expand Up @@ -82,10 +89,17 @@ The workbench inherits its palette from the [Pi coding agent](https://pi.dev) so
Regenerate assets after a token edit:

```bash
uv run --with fonttools python scripts/build-icon.py # assets/icon, mark
uv run --with fonttools python scripts/build-screenshot.py # docs/assets/screenshot
uv run --with fonttools python scripts/build-icon.py # assets/icon, mark
uv run --with fonttools python scripts/build-logo.py # assets/logo (composable π + pidex)
uv run --with fonttools python scripts/build-banner.py # docs/assets/banner-bg
```

`docs/assets/screenshot.png` is captured from the running app. To refresh: temporarily gate a `webContents.capturePage()` call behind an env var, run `PIDEX_CAPTURE=1 mise run dev`, then revert. The banner composites the screenshot over the design-system background (`docs/assets/banner-bg.png`, built by `scripts/build-banner.py`) with the composable logo watermark, all via `magick`.

The banner background is procedural: `bg-canvas` field with the blueprint grid (96px minor + 480px major, lifted in opacity for hero scale) and a vertical gradient into `bg-deep`. Strictly rectilinear; no generated imagery.

Banner and logo use the design system font pairing: **Georgia** (serif, from `--serif` in `tokens.css`) for the wordmark and body, **Commit Mono** (from `--mono`) for the code surfaces visible in the screenshot.

## What's next

- [ ] Diff viewer with virtualised summaries
Expand Down
Binary file removed assets/icon-premium.jpg
Binary file not shown.
Binary file removed assets/icon.png
Binary file not shown.
12 changes: 0 additions & 12 deletions assets/icon.svg

This file was deleted.

Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/mark.png
Binary file not shown.
5 changes: 0 additions & 5 deletions assets/mark.svg

This file was deleted.

Binary file added docs/assets/banner-bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions docs/assets/banner-bg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions docs/assets/framed-window.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 0 additions & 59 deletions docs/assets/screenshot.svg

This file was deleted.

Binary file added docs/assets/workbench-boot.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/workbench-loaded.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dev": "mise run dev",
"build": "mise run build",
"start": "mise run start",
"lint": "oxlint src tests",
"lint": "bunx oxlint src tests",
"typecheck": "bunx tsc -p tsconfig.json --noEmit && bunx tsc -p tsconfig.main.json --noEmit && bunx tsc -p tsconfig.preload.json --noEmit",
"test": "mise run test",
"test:unit": "bunx vitest run --project unit",
Expand Down
22 changes: 3 additions & 19 deletions scripts/_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,16 @@
Token groups:
- Warm darks (window bg, panel surfaces)
- Creams (text, marks, lit facet)
- Terracotta / sunkissed (warm accents, the fluid-crest peak)
- Terracotta / sunkissed (warm accents)
- Sage / accent-blue (secondary accents)
"""

from __future__ import annotations

from fontTools.ttLib import TTFont
from fontTools.pens.svgPathPen import SVGPathPen

# --- Warm darks ---
# pi.dev: --color-warm-black
WARM_BLACK = "#13110f"
INK_BRONZE = WARM_BLACK
# pi.dev: --panel-base
PANEL = "#212730"
# pi.dev: --panel-soft-base
Expand Down Expand Up @@ -56,18 +54,4 @@
# pi.dev: --text
TEXT = "#ebe7e4"
# pi.dev: --muted
MUTED = "#9fa4ab"

DEFAULT_FONT = "/System/Library/Fonts/Supplemental/Georgia Italic.ttf"


def pi_path(font_path: str = DEFAULT_FONT) -> tuple[str, tuple[int, int, int, int], int]:
"""Return (svg_path_d, bbox, unitsPerEm) for the π glyph (U+03C0)."""
font = TTFont(font_path)
cmap = font.getBestCmap()
gname = cmap[ord("π")]
pen = SVGPathPen(font.getGlyphSet())
font.getGlyphSet()[gname].draw(pen)
glyf = font["glyf"][gname]
bbox = (glyf.xMin, glyf.yMin, glyf.xMax, glyf.yMax)
return pen.getCommands(), bbox, font["head"].unitsPerEm
MUTED = "#9fa4ab"
86 changes: 86 additions & 0 deletions scripts/build-banner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Build the README banner background using the pidex design system.

The background mirrors the app's own chrome (tokens.css base.css body
background): bg-canvas field with the blueprint grid (96px minor +
480px major, blue-tinted) and a vertical gradient into bg-deep. Shapes
are strictly rectilinear; no organic imagery. The real app screenshot
and the composable logo are laid on top of this background.

Output:
docs/assets/banner-bg.png (1600x900)

Compose the final banner with:
magick docs/assets/banner-bg.png \\
docs/assets/screenshot.png -resize 1000x -geometry +300+80 -composite \\
assets/logo.png -resize 200x -geometry +70+70 -composite \\
docs/assets/banner.png
"""

from __future__ import annotations

import shutil
import subprocess
import sys
from pathlib import Path

REPO = Path(__file__).resolve().parent.parent
OUT = REPO / "docs" / "assets"

sys.path.insert(0, str(Path(__file__).resolve().parent))
from _design import BG_DEEP, BG_CANVAS # noqa: E402

W, H = 1600, 900


def svg() -> str:
# Blueprint grid matches tokens.css (96px minor + 480px major, blue
# tint). At hero scale the token opacities would be invisible, so
# they're lifted slightly while keeping the same hue.
return f"""<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" width="{W}" height="{H}">
<defs>
<linearGradient id="vert" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="{BG_DEEP}" stop-opacity="0"/>
<stop offset="1" stop-color="{BG_DEEP}" stop-opacity="0.85"/>
</linearGradient>
<pattern id="grid_minor" width="96" height="96" patternUnits="userSpaceOnUse">
<path d="M 96 0 L 0 0 0 96" fill="none" stroke="#8aa9d4" stroke-opacity="0.18" stroke-width="1"/>
</pattern>
<pattern id="grid_major" width="480" height="480" patternUnits="userSpaceOnUse">
<path d="M 480 0 L 0 0 0 480" fill="none" stroke="#8aa9d4" stroke-opacity="0.32" stroke-width="1.75"/>
</pattern>
</defs>

<!-- base canvas -->
<rect x="0" y="0" width="{W}" height="{H}" fill="{BG_CANVAS}"/>

<!-- blueprint grid -->
<rect x="0" y="0" width="{W}" height="{H}" fill="url(#grid_minor)"/>
<rect x="0" y="0" width="{W}" height="{H}" fill="url(#grid_major)"/>

<!-- vertical gradient into bg-deep (anchors the bottom) -->
<rect x="0" y="0" width="{W}" height="{H}" fill="url(#vert)"/>
</svg>
"""


def render_png(svg: Path, png: Path, width: int) -> None:
if not shutil.which("inkscape"):
sys.exit("inkscape not found on PATH; install via brew install inkscape")
subprocess.run(
["inkscape", str(svg), "--export-type=png", "--export-filename", str(png), "--export-width", str(width)],
check=True,
)


def main() -> None:
OUT.mkdir(parents=True, exist_ok=True)
svg_path = OUT / "banner-bg.svg"
png_path = OUT / "banner-bg.png"
svg_path.write_text(svg())
render_png(svg_path, png_path, W)
print(f"wrote {svg_path}")
print(f"wrote {png_path}")


if __name__ == "__main__":
main()
Loading
Loading