summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/macos.md58
-rw-r--r--docs/rendering-parity-design.md132
2 files changed, 164 insertions, 26 deletions
diff --git a/docs/macos.md b/docs/macos.md
index 3c02de8a..3fdf399f 100644
--- a/docs/macos.md
+++ b/docs/macos.md
@@ -14,6 +14,10 @@ plumbing actually requires, and which parts of it are distribution machinery
rather than integration. Read that for the alternatives; this file is what was
built and why.
+Future layout/compositing ownership and the regression baseline are discussed in
+[Shared rendering contract](rendering-parity-design.md). That proposal is design
+groundwork; the native drawing implementation remains in place.
+
## Why not ghostty's split
Ghostty was read carefully before this was written, and this backend
@@ -496,33 +500,22 @@ different kinds of assertion because a snapshot is the core's cell buffer and
the core has no font: `font Menlo-Regular` asks the view what it is actually
wearing, and the snapshots catch the grid moving when the cell changes size.
-### The tagline band follows the core's rule, not this shell's
+### Compact tags and context rows
-A pane tag is drawn at `gui_tagline_font_percent` of the body face
-(`TaglineSize <percent>` changes it live) and the band it sits on shrinks with
-it, while the grid row stays body-sized. Where that shorter band sits inside its
-row is no longer this shell's arithmetic: `pardes_tagline_band_offset` and
-`pardes_topbar_pane_border_px` answer out of `src/pardes.zig`, and that is the
-same rule `src/gui/gui.zig` draws with.
+`TaglineSize <percent>` scales the tag face, pitch and glyph band. The tag's
+background still fills a whole body-grid row, as in the SDL renderer. Painting
+only the compact band leaves dark strips between tags. Glyph offsets come from
+`pardes_tagline_band_offset`; all measurements cross the ABI in physical pixels.
-It is shared because it drifted. This shell centred every band in its own row,
-and centring two reduced-height bands is precisely the case
-`config.gui_topbar_pane_border_px` exists to prevent: the topbar's unused
-half-band meets the first pane tag's unused half-band and the window background
-shows through the seam. The strip is as wide as the bands are short — on a
-20-pixel cell, 4 physical pixels at the default 82%, 10 at 50%, 14 at 30% — so it
-read as "the tagline is wrong on the mac" rather than as one missing rule. Row
-zero is bottom-aligned now, the first pane-tag row top-aligned, the two joined by
-`gui_topbar_pane_border_px` in the theme's scrollbar-track colour
-(`pardes_topbar_pane_border_rgb`, or a compiled override), every row between
-centred, and a `Tagbottom` band on the final row bottom-aligned against the
-window edge — with the sub-cell strip below it painted in that band's own colour,
-because the core grid holds only whole cells and a window is any height it likes.
+The chrome overlay runs after the compact layers. It draws the topbar rule,
+the column rule and the pane's tag/body boundary using the theme's border
+colour. The pane rule moves above a bottom-positioned tag. Tag-layer fields 11
+and 12 preserve that placement and colour in frozen transition frames.
-The offsets cross as PHYSICAL PIXELS. The host multiplies its points by the
-backing scale going in and divides coming out, which is the snapping `Metrics`
-already does for the cell, and is what keeps a one-pixel rule one pixel instead
-of a two-pixel smear.
+Tree-sitter context rows use the compact height and pitch, with full-width row
+backgrounds. Their one-physical-pixel separators use the same border colour as
+SDL, after discontinuous declarations and after the final context row. The
+remaining body starts immediately after the compact rows.
### The cell is snapped to device pixels, not to points
@@ -713,6 +706,20 @@ The blur is a **sibling** of the grid inside a plain container, never its
parent. Hiding a superview hides its subviews, so a nested backdrop drew a
blank window for every opaque theme the moment it was hidden.
+`WindowOpacity <0..100>` sets one background coverage throughout the frame.
+Background fills replace existing coverage, so overlapping ground, cell, tag
+and context layers cannot increase the requested opacity. The window itself
+stays clear below 100%; a second tinted window backdrop would compound it.
+Text and cursor ink stay opaque. PDF and image pixels follow the SDL image
+pipeline: remove the destination by source coverage, then add the image at the
+requested opacity. This also preserves the background under transparent image
+pixels. Theme colours and attachment rasters are interpreted as sRGB.
+
+`test/macos-snapshots/rendering-parity.snap` checks background colour and alpha,
+regular and bottom tags, compact context separators, and PDF fit, tint and
+scrolling. Its native-metrics mode uses backing pixels like the shipping app;
+the older grid-only tests keep their display-independent point metrics.
+
## Threading
One core, touched only from the main thread, plus one pty reader task per pane
@@ -955,8 +962,7 @@ select scripts or directories instead. The executable stays in the build cache.
`test/macos_e2e.swift` links the same Swift sources the app does, minus
`main.swift`, into a second binary — test scaffolding does not ship inside the
-product. Scripts are `test/macos-snapshots/*.snap` (seven of them: boot, cwd,
-drop, font, keys, rotate, trackpad) and speak the tty suite's
+product. Scripts are `test/macos-snapshots/*.snap` and speak the tty suite's
vocabulary (`start`, `wait`, `stable`, `text`, `key`, `snap`, `command`,
`mouse`, `click`, `wheel`, `resize`, `draw`) plus what only
exists here: `fingers <n> <col> <row>`, `force <col> <row>`,
diff --git a/docs/rendering-parity-design.md b/docs/rendering-parity-design.md
new file mode 100644
index 00000000..b6036543
--- /dev/null
+++ b/docs/rendering-parity-design.md
@@ -0,0 +1,132 @@
+# Shared rendering contract: design groundwork
+
+Status: proposed direction, not an implemented renderer abstraction. The current
+change fixes the native macOS backend and adds regression coverage. It does not
+move layout or drawing into a new shared module.
+
+## Why the backends diverged
+
+The core shares editor state, cells, tag layers, body layers, and PDF placements.
+It does not yet specify every visual operation needed to present them. SDL and
+AppKit therefore independently reconstruct tag backgrounds, compact text bands,
+context separators, overlay order, and opacity. A correct cell snapshot can
+coexist with an incorrect rendered frame.
+
+The macOS/Linux comparison exposed concrete gaps in this contract:
+
+- Reduced tag glyph bands were also used as background bounds on macOS; SDL
+ fills the entire row. Column and pane rules were missing or painted before
+ an overlapping layer.
+- Tree-sitter context rows had matching compact geometry, but the separator
+ used the scroll-track color instead of the border color.
+- Repeated source-over background fills compounded window opacity. At 71%,
+ two overlapping fills yield about 92%, and three about 98%. The window's own
+ colored background added another layer behind the view.
+- CoreText fallback runs did not inherit the selected foreground color.
+- PDF/image composition ignored window opacity on macOS, while SDL applies it
+ to raster content. Theme colors also need an explicit sRGB interpretation.
+
+These are duplicated policy decisions, rather than a reason to replace CoreText
+or require identical fonts on every host.
+
+## Proposed ownership boundary
+
+A shared scene builder should translate the existing surface and its layers
+into an ordered list of positioned drawing operations. Platform code should
+consume that list without deciding which rows are compact, which edge has a
+separator, or which objects participate in window opacity.
+
+The builder would own:
+
+- Tag and context geometry, background extents, separator positions and colors,
+ clipping, and the order of backgrounds, text, images, cursors, and overlays.
+- Coordinate conversion rules: logical layout units, supplied font metrics,
+ device scale, and the rounding policy for one-device-pixel rules.
+- Color and compositing semantics, including clear backgrounds, replacement
+ background fills, image coverage, selection, and opaque native text/cursors.
+- Stable image references and a defined frame lifetime, including snapshots
+ retained for transitions and postprocessing.
+
+Backends would continue to own native font selection, shaping/rasterization,
+image upload/cache management, and presentation. Native event handling, window
+management, and the text-only terminal frontend are outside this proposal.
+
+A small 2D operation vocabulary could include solid rectangles, positioned text
+runs, images with source/destination rectangles, and explicit clips. Operations
+must carry semantic blend behavior and a documented color space. Wrapping
+`fillRect` and `drawText` while leaving each backend to build the scene would
+preserve most of the duplication that caused these bugs.
+
+Do not freeze a new ABI before extracting one small path and checking its needs.
+Font metrics must enter the shared layout explicitly; changing fonts can still
+change line capacity and glyph appearance. Pixel equality across different
+fonts, rasterizers, and display scales is not the acceptance criterion.
+
+## Compositing contract to preserve
+
+Let p be WindowOpacity in [0, 1]. An ordinary background region has alpha p,
+regardless of how many logical layers cover it. Clear background regions remain
+clear. Native text and cursors retain their own opacity.
+
+For an image sample with coverage a and straight RGB c, the existing SDL
+background-layer behavior produces premultiplied output:
+
+```
+out.rgb = p * a * c + (1 - a) * dst.rgb
+out.a = p * a + (1 - a) * dst.a
+```
+
+Thus PDF text is part of a raster page and fades with the page; native text does
+not. Transparent image pixels preserve the destination. Ordinary source-over
+with source alpha p*a is not equivalent. Any future operation API needs to
+express this distinction directly and specify sampling and premultiplication.
+
+## Tests available before extraction
+
+`test/macos-snapshots/tag-ink.snap` checks actual rendered tag foreground pixels,
+covering failures that a cell-grid golden cannot detect.
+
+`test/macos-snapshots/rendering-parity.snap` uses native backing-scale metrics
+and checks rendered tag backgrounds and rules, top/bottom tags, opacity 100/71/0,
+invalid opacity rejection, and nested multiline Tree-sitter context separators.
+It also checks PDF placement and sampled page alpha at those opacity values,
+height fit, tint modes, and scrolling. `context.zig` is the nested context fixture;
+`docs/9p.pdf` is the existing PDF fixture. Optional PNG output is enabled with
+`PARDES_TEST_CAPTURE_DIR`.
+
+The Zig test named `mac tag layer ABI preserves logical capacity and physical
+grip` covers tag-bottom and border-color metadata as well as existing capacity
+and grip behavior. The existing Linux `test/window_opacity.py` checks background
+changes, native glyph preservation, zero opacity, invalid input, and restoration.
+
+Run on macOS:
+
+```sh
+zig build unit-test -Dplatform=macos '-Dtest-filter=mac tag layer ABI'
+zig build macos-e2e -Dplatform=macos
+zig build -Dplatform=macos
+```
+
+These are baseline regressions, not a comprehensive cross-backend image oracle.
+PDF alpha samples do not establish exact raster color or interpolation equality;
+offscreen AppKit captures do not test the desktop compositor. Live comparison
+still matters for window transparency and display color management.
+
+## Suggested later extraction and acceptance gates
+
+1. Add shared geometry/scene assertions for tags and context rows using supplied
+ metrics at several scales. Include clipping, partial rows, bottom tags, and
+ final separator order; expected values should come from the contract rather
+ than a copy of either backend's implementation.
+2. Extract that scene construction once and adapt both native renderers. Keep
+ current backend pixel checks running without regenerating goldens merely to
+ accept differences. Compare matching fixtures and settings on both systems.
+3. Add synthetic RGBA image coverage cases before extracting image composition:
+ opaque, transparent, and partial-alpha pixels over a known destination at
+ p=0, intermediate p, and p=1. Include fit, crop, and clip boundaries.
+4. Extend the contract to the remaining overlays and retained transition frames.
+ Measure frame time and allocations before broadening the abstraction.
+
+The desired result is one definition of visual policy with small native drawing
+adapters. A new GPU engine, shared font rasterizer, or wholesale backend rewrite
+is not required to reach that boundary.