| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
| |
Each host closed the running shell before it forked the new one, so an exec whose shell failed left the pane with none and later runs answered error shell gone. A shell not there is now refused up front, and every host starts the new shell first, replacing the old only once the close-on-exec pipe says it ran; a failure there is only said (restartFailed).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
spoken, so it logs no rename to the session's and back
Each host read /proc/<pid>/cwd right after forking the shell and at every
frame after; before the shell's chdir that says this process's directory,
a rename to it, then the real one a frame later, a rename back. A shell
given a directory is named by it already (newShell) and is no longer
asked at fork; and until it has printed anything, an answer naming this
process's directory is not taken.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
started answers runs
The host could not start the shell (its chdir failed), and the pane stayed
with no pty: a run written to it waited for a prompt for ever. Tty now
refuses a directory that is not there, `Tty: <dir>: no such directory`,
before making a pane; and a terminal whose shell the host could not start
(Pardes.shellFailed, from each host's spawn) answers any run with
`error shell gone`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
dissolve crossfades
- Track.launch: a move retargeted mid-way (Presentation.sync) starts at
the old move's speed along the new one's longest side (carriedLaunch,
Motion.easeFrom/speedFrom), so a second change during a slide bends the
motion instead of stopping it dead. The macOS track ABI grows the float
(pardes.h, 48 bytes; its reserved byte is named motion, as in Zig).
- A pane that slides, zooms or rises casts Lift's soft shadow at
sin(pi x progress) of its strength, on the other panes' bodies and
rails alone, under the same contrast ceiling, carried by its track.
- The GUI's PanelDissolve is a crossfade: the new cell drawn first, the
old one over it at 1 - progress, premultiplied, on the blending cell
pipeline for such groups (new cells now emitted before old ones, as the
old layers already came after the new). sRGB blend, not linear light
(documented). The tty keeps its per-cell swap.
Presentation.zig's and animation.zig's tests were not in any test root;
pardes.zig's test block now takes them in.
Tests: a retargeted move carries its speed and still lands exactly; a
moving pane's shadow is clipped off every tag, grip and its own body,
none at the start or the landing, most mid-way. GUI golden 18 changes by
the shadow (frame 6 of a slide). Gates as G6. Feel review:
.scratch/render/transitions/.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A Dump into a DumpDir it could not write, or a Restore of a file that is
not a dump, answered a ctl write with rc 0 and only a message, and the
Restore warned about unsaved panes before it ever looked at the file. The
hosts' dump write now fails the waiting 9P write with EIO (the late failure
a Save uses), naming the path and the reason in the log, and Restore parses
the file before its unsaved-panes refusal, failing with `not a pardes
dump`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |\ \ |
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
The flavours looked alike because Lift, the only motion that read them,
moves a shadow a few pixels: overshoot and wind-up were sub-pixel. By design
now: crisp leaves at full speed and never passes its mark; smooth (the
default) eases in and out in twice crisp's time, a slide arriving in 208 ms
(inside §8.1's 220); bouncy passes its mark by ~30% and settles twice;
playful winds up, passes by ~40%, and stretches and squashes, text included
(the user's choice). Small motions are exaggerated by the flavour's gain.
Motion gains launch, gain, span and norm, and ease(u)/speed(u) for moves of
fixed length. The notice drop follows ease() over its fall (alpha never
behind its position) and stays inside its pane's body past its row. Panel
tracks carry the flavour in Track.motion (the old padding byte; macOS header
name follows); opening and moving slides, zooms and rises follow it over
span x their frames; landed, a pane is exactly its target; closing keeps
its own exit. A pane past its mark never takes a neighbour's click; a
rising one stays in its box.
Tests: each flavour's lift trajectory distinct; flavour character; exact
landing bit for bit; a stretched pane's drawn cells map back; the overshoot
hit rule; the vertical clip at peak; legacy tests pinned to crisp. GUI
golden 18-mid-transition changes (the retuned smooth slide at frame 6).
Shared files: src/animation.zig, src/Presentation.zig, src/Messages.zig,
src/macos.zig, src/config.zig, src/pardes.zig tests, src/ninep/screen.zig
test, docs/effects.md, docs/config.md.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
With the line run under job control (the change before), the job running
has a group of its own, and Kill's SIGTERM to the tty's foreground group
stopped only that job: of `sleep 30; touch x` the touch still ran. For a
command pane the hosts now signal the shell's group as well, so the whole
line stops, as it did; a line typed at a prompt still loses only its
foreground job. The test forks a real pty for both halves: a background job
outliving its command and the pty's hangup, and a killed line not running on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |/ /
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
A dogfood agent ran `exit 3` through pty/run: the reader got ENOENT, and
the log went straight to del. The hosts now read a shell's exit status at
its pty's end as they do a command's; a run waiting on the line answers
`exit 3` with what it printed, the log says `exit <serial> 3` before
the pane's `del`, and an open run still stats after its pane is gone
(cat fstats its input). Pins cloud9 f35b7ed, whose stat of an open fid names
its open. Writes to a gone pane's pty/data, held open or not, fail ENOENT.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |/
|
|
|
|
|
|
| |
names the path and why
The write answered ok and the pane stayed dirty, with `save: AccessDenied` on its message row: a script saw success. The failure now says `Save <path>: <why>`, and a 9P write that waited on the save fails with EIO and that text, logged as an err record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |\ \ |
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
The user tried them live and wants them removed entirely: the builtins,
their levels, config keys and leader paths (tR, tg), their slots in the
built-in chain, their shaders (source and prebuilt), EffectCode paths, and
on macOS their scene flags (pardes.h), the Metal kernel's coordinate warps
and the Swift pointer mapping that mirrored them. Docs say so (decision 8
in docs/render-pipeline.md); config.md documents Crt's levels, Shader and
ShaderAnimation instead.
Shared files touched: macos.zig (flags). Not touched: pardes.zig,
Messages.zig, mouse.zig, gui.zig, detached/*.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
rewritten as Shadertoy files
Stage 9 of docs/render-pipeline.md. src/gui/Post.zig runs the chain over
the finished frame: each pass reads the one before, ping-pong textures
between, the last writes the window; an empty chain is the direct path.
The prefix (shaders/post/prefix.glsl) is ghostty's uniform block, name
for name and offset for offset (tested against a copy of its Uniforms, the
selection colours by their GLSL names), at SDL GPU's bindings, y down.
A user's file (Shader <path>) is compiled by glslc with compileGlsl's own
flags on a thread of its own, taken in at the next loop step; errors name
the file's own lines; a failed compile keeps the last good pipeline;
glslc missing is said once. The bundled passes compile with the build,
through the same prefix.
Crt, Ripple and Glitch are Shadertoy files, rewritten: no barrel (input is
identity, tested), everything in linear light and dithered, keyed to iTime.
scene_effects, crt.zig and crt.frag.glsl are gone; the core keeps no clock
for the chain. ShaderAnimation off|on|always drives redraw level A: the
chain alone over the retained frame (34 us CPU a redraw, measured).
Shared files touched: pardes.zig (nextWake loses the scene line,
disableSceneEffects empties the chain, two tests), builtins.zig (one switch
arm), ninep/ctl.zig (two switch arms), macos.zig (flags from the chain),
gui.zig. Not touched: Messages.zig, mouse.zig, detached/*, host_io, tty.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
mark_hover is gone
Open point 6 of docs/render-pipeline.md: notice words are Look and Exec
targets, so the hover affordance headers have is back on notices, painted
once in the notice's layer and joined into the grid from it. Deleted:
Surface.mark_hover, Cell.hover and render's per-frame reset of it, none of
which had been set since stage 5 (macOS loses its glass hover rect; its
build is ignored for now). Snapshot goldens unchanged.
Shared files touched: macos.zig (the hover bit's removal). Not touched:
pardes.zig, Messages.zig, mouse.zig, gui.zig, detached/*.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
of rows; wire v8
src/Layer.zig merges TagLayer and BodyLayer. `rows` (0 = no layer) and a
cursor at {x, y}. A tag of N rows is ONE layer of N grid rows: tagHit answers
the row as `line`, bodyHit keeps its meaning, and the per-line layer bases
(TAG_LINE_LAYER_BASE, HEADER_LINE_LAYER_BASE) are gone, not aliased.
Wire v8, the one bump: tag layers carry rows and cursor y, and the frame
carries the placed region list. v7 and v9 peers are refused in both
directions (server test over both, a new frontend test over both).
web: tag_layer_value 11 = rows, 12 = cursor y; app.mjs lays every row.
macOS: the Zig side compiles against Layer; pardes.h still sees one row
per tag layer (accepted, the macOS shell is ignored for now).
No visual change: snapshot goldens and the 17 GUI goldens byte-identical.
Shared files touched: pardes.zig, Messages.zig, gui.zig, macos.zig,
detached/client.zig, detached/server.zig, detached/wire.zig (plus web.zig,
app.mjs, edit.zig, look.zig). Not touched: mouse.zig, tagline.zig,
colors.zig, tty.zig, dump.zig, exec.zig, host_io.zig, panes.zig.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
A shell now answers Host.now, its monotonic clock in ns, and the pump
advances core animation to it: one .tick per whole 16 ms frame since core
time last stood still. The same animation therefore takes the same time at
60, 120 and 144 Hz, over ssh and after a slow frame (the GUI's re-armed
clock ran them ~25% slow at 144 Hz; the tty's post-frame sleep drifted).
nextWake() lists every core animation in one place: a frame from now while
anything moves, the end of the wait while something only waits (a message
lingering, the look-hover delay), null when idle. Shells sleep exactly that
long, and a wait is jumped to its end in one step, so an 800 ms linger
costs one frame instead of fifty. An overshoot under 1.5 ms after a step is
let go: at 60 and 120 Hz every display frame takes exactly one step (a 16 ms
frame against a 16.67 ms vsync would otherwise double-step every 24th).
The six tick drivers are gone: tty's timer thread only times the wait,
interruptibly (a newer, shorter request cuts short a sleep still timing a
longer one); the GUI's
AnimationClock, web's JS tick bank (pardes_tick now takes rAF's timestamp),
the detached server's and the board's ticks; grid mode steps a virtual
clock straight to each wake. PARDES_TEST_CLOCK, set by the snapshot
harness, gives tty and the detached server the same virtual clock.
Every stepped frame is drawn. The old pump never drew the last frame of a
fade (a .tick asks for no frame, and the fade was over by the check), so a
theme switch stopped at 9/10 of the way until the next input: theme.golden,
themesel.golden and the GUI's acme-light scene move to the theme's true
colours, and nothing else changes. The frozen previous grid is captured only
while a panel transition is chosen, not on every idle frame.
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Pure moves, no behaviour change. draw.zig holds the whole core frame in
order: render, the pane, tag, header and notice painters it calls, and the
character-effect composition (Pardes.render stays a declaration alias).
Presentation.zig is the panel presentation state as a file struct, and
animation.zig the easing curves, transition kinds, tracks and boxes, the
character effects' sources and the generic displayed-value transition, all
of which lived in layout.zig. layout.zig keeps only layout.
|
| |/ /
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The watcher woke the host a second time 60 ms after the exit by its own clock
while the grace was counted from the reap: a host busy for 10 ms missed it,
and the tag said running for ever and Kill did nothing; and under load exit 0
could land before the last output. As decided, no timer: the exit is told
once it is reaped and the pty says nothing is left (poll: no POLLIN, and no
POLLHUP, which means the end of file is on its way behind the output), else
at that end of file, checked after each chunk of output. The tty host checks
inside its step so the frame shows it. The four hosts' copies are one
host_io.takeExits/commandEof, which close a told command's pty at its end of
file (the fd and the GUI's reader leaked when the exit came first). A
finished command pane whose pty a job it left still holds is not reused, so
that job is not hung up; and the reset before a reuse is SGR 0, not DECSTR,
which ghostty's stream does not implement.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
A signal effect carried only a slot, so a Kill followed in the same step by
the pane's closing and its slot's reuse signalled the new pane's child; and a
command pane whose child had exited and been reaped kept a process group id
another process could take. The effect now carries the pane's serial, checked
as it is performed, and a host signals no command whose exit it has recorded;
the core already sends Kill only to a command it has not been told is done.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
A job left in the background (sleep 100 &) held the pty open, so the pane
stayed running and the child a zombie until the job ended; a command that
closed its terminal and ran on got its end of file at once, the host waited
100 ms for an exit, reported exit ? and hung it up. Now each command's child
is watched on a thread (waitid with WNOWAIT, so its pid stays its own until
the host reaps it), and the host tells the core the exit from that: after the
pty's end of file, so the output before the exit is in, or 50 ms after the
exit without one, a job holding the pty. The pty stays open until both, so a
command that let go of its terminal is never hung up by it. All four front
ends; a host that cannot start the watcher reads the exit at end of file as
before. Tests: host_io's for both cases, and cmdexit.snap end to end.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
shell's prompt
A middle click, an exec write or a tag word that no builtin knows was typed
into some terminal for the pane's directory, sharing whatever state that
shell was in and answering nothing, so a misspelling vanished into a shell.
Now only a line clicked at an interactive terminal's prompt is typed there.
From anywhere else it runs as a command pane: a terminal whose child is
$SHELL -c the line in the pane's directory, full emulation, which shows its
output and then exit N from the host's reaping of the child, and stays. A
finished command pane is the next command's for its directory, which runs
below what it showed after a '% line' line (acme appends to +Errors and never
clears it, util.c:213); a running one gets a second pane. Kill ends a command
pane's whole process group, the log records run and exit, exec reads back the
command pane's serial, and a line is at most 1 KB, read off the pane as the
host forks rather than carried in every spawn effect. ttyForDir's search for
a free shell is gone. The goldens of chordcut, cmdword and layout-open change
where a file's exec now opens a command pane, and ttytaken is rewritten to
exec from the terminal itself; docs/open-questions.md records the decision.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
and restores
A client that wrote Restore saw its connection cut with no answer, and could
not tell a Restore from a crash. The listener now lets the writer's answer out
before the cut, and cuts only the old editor's connections, refusing their
requests meanwhile; a client that dials during it is the new editor's and
stays. Dump logs 'dump <path>' and the restored editor's log 'restore <path>'.
Keeping connections across a Restore was weighed and left: the fids name the
old editor's panes and opens, so it would mean carrying serials and open
records into the new one, where acme's Load only adds windows. tty's Restore
also closed its shells' ptys without reaping them; it retires them now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Kill queued a signal effect for later, so the job it saw running by its marks
could have ended, and another started, before the signal went; and with job
control off the job shares the shell's process group, so the host skipped it
and Kill reported nothing. Kill now calls the host's kill_job while it holds the
turn, and when the only job is the shell's own group says 'Kill: no job to
signal', which also fails a write of Kill to ctl.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
the hangup
tty and macOS hung the pty up and called waitpid once without waiting, so a shell
still exiting, or one that ignores SIGHUP, stayed a zombie or ran on with nobody
reading it; tty also never reaped a shell that exited by itself, and its spawn
into an occupied slot closed the old pty without ending the shell. host_io's
retireShell says hangup, waits 100 ms on a thread (macOS has no host timer to
poll from), then kills and reaps. The gui's own retired list, when full, left the
slot holding the old shell and refused the next spawn into that pane; it now
hands that shell to retireShell instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
The detached server closed a pane's pty only when the shell was respawned,
hit EOF or the session shut down, so rmdir, Del or Delcol on a terminal
left its shell running with nobody to read it; the tty front end and
macOS did the same. Retiring a terminal pane now emits a close_pty
effect, which each host that runs shells answers by hanging the pty up
and ending the shell (the detached server's and the GUI's existing
retire-and-reap path, and a close plus SIGHUP in the tty and macOS
shells).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
acme keeps what edits a text in its Text (dat.h:171-190) and the window
holds a body and a tag of that type. The cursor, the selections, the modal
state and the edit-buffer undo move off Pane into Text.zig, Pane holds
them as its body, and the edit and normal-mode operations take the Text
they edit. Nothing changes in behaviour; this is the step that lets the
tag become a second Text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Not a pure move: state moves. Pardes's `pipe_seq` and `pipe_wait` become
Pipe.zig's own fields `seq` and `wait`, and Pardes embeds one as
`pipe: Pipe = .{}`. pipeRequest reads only the request in flight, so it now
takes `pipe: *const Pipe` and the four shells call
`core.pipe.pipeRequest(id)` (their Pipe imports go away again); the other
pipe functions still need the panes and keep `p: *Pardes`, writing
`p.pipe.seq/wait`. Field reads in dump.zig, the shells' tests and two test
files follow (`core.pipe_wait` becomes `core.pipe.wait`). No behaviour
change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pure move, no behaviour change: the editor half of `|` that the earlier
change put at the end of selection_pipe.zig (PendingPipe, pipeMarker,
submitPipe, pipeRequest, pipeFailed, pipeCut, pipeOutput, pipeResponse,
and the eight pipe tests with nextPipeEffect) now lives in its own
Pipe.zig, so the pipe's editor state can become Pipe's own fields next.
selection_pipe.zig goes back to exactly what it was before this series: the
native runner and the boundary values the shells hand to their workers.
The moved code names the runner's types as `selection_pipe.X` again, as it
did in pardes.zig; callers change from `selection_pipe.submitPipe(p, ..)`
to `Pipe.submitPipe(p, ..)` (pardes.zig and the four shells).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pure move, no behaviour change (acme keeps dump and load in rows.c):
dumpState, restore, initFromDump and initDump go verbatim to the end of
dump.zig, after the dump format they read and write. Inside dump.zig the
moved code's `dump.` prefix drops, so `Pane` there is the dump record; the
one editor pane it names is spelled `pardes.panes.Pane`, and its other
`panes.X` references become `pardes.panes.X` because dump.zig's own tests
use `panes` as a local name.
The methods become free functions taking `p: *Pardes`: `p.dumpState()`
becomes `dump.dumpState(p)`, `core.restore(bytes)` becomes
`dump.restore(core, bytes)` and `Pardes.initFromDump(..)` becomes
`dump.initFromDump(..)`, in pardes.zig, the shells, layout.zig,
Terminal.zig, builtins.zig and the tests (38 receiver rewrites plus the
initFromDump calls). The tag-tail restore helpers stay with the tag code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pure move, no behaviour change: handleMouse with dragUpdate, dragRelease,
seamIdxAt, reportGesture, gestureRange, dispatchPointerBuiltin,
mirrorTtySelection and chordCutPaste, the pointer hit helpers
(bodyHitForPane, sameTagCell, tagColumn, sameBodyCell), chromeTarget with
ChromeTarget, paneAt, the Drag state type with clampBorderCol/Row, the seam
test fixtures and thirteen pointer and drag tests go verbatim to mouse.zig.
The methods become free functions taking `p: *Pardes`; their 36 call sites
change from `p.handleMouse(..)` to `mouse.handleMouse(p, ..)`, including
web.zig's chromeTarget and macos.zig's paneAt. The Mouse event type stays
in pardes.zig with the rest of the event vocabulary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pure move, no behaviour change: the Message stamp helpers, LoggedMessage,
setStatus, setMessage, showMessage, dismissMessage, dismissLine,
advanceMessages, advanceLine, messagesAnimating, MessageMotion,
messageFrames, messageMotion, noticeText, noticeLife, blendRgb,
logMessage, messageLog, reportError, the notice painters (leaderText,
noticeCols, Printed, printRight), collectNotices, and the six message
tests go verbatim to Messages.zig.
The methods become free functions taking `p: *Pardes`. setStatus,
setMessage and reportError are called from ~170 places as `p.setMessage(..)`,
so Pardes keeps three declaration aliases (`pub const setMessage =
Messages.setMessage;`) and those call sites stay as they are; every other
call changes from `p.x(..)` to `Messages.x(p, ..)` (46 of them). The five
shells' `pardes.Pardes.Message` become `pardes.Messages.Message`. The
message ring's fields stay on Pardes for now; moving them into Messages is
a separate change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pure move, no behaviour change: submitPipe, pipeRequest, pipeFailed,
pipeCut, pipeOutput and pipeResponse, the PendingPipe they share,
pipeMarker, and the eight pipe tests with their nextPipeEffect helper go
verbatim to the end of selection_pipe.zig, so the whole of `|` (runner,
boundary values, prompt, request and atomic edit) is one file. Inside that
file the `selection_pipe.` prefix drops; the file doc now says it holds both
halves.
The methods become free functions taking `p: *Pardes`: the four shells'
`core.pipeRequest(id)` become `selection_pipe.pipeRequest(core, id)`, and
pardes.zig's two calls change the same way. pushUndo becomes pub because
the pipe's edit calls it; PendingPipe.deinit becomes pub for Pardes.deinit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pure move, no behaviour change: the Theme type, the curated palettes,
fold and the themes ring with its two comptime checks, themeContrast, mix,
ChromeTheme, ChromeAnimation and initial_chrome, the Pardes methods that
load theme files and switch themes (nextThemeFileGeneration,
requestThemeFile, ThemeFileRequest, themeFileRequest, failThemeFile,
loadThemeFile, finishThemeInitialization, invalidateThemeDependentRasters,
setThemeIndex), and the ten theme tests go verbatim to colors.zig.
The methods become free functions taking `p: *Pardes`; their call sites
change from `p.setThemeIndex(i)` to `colors.setThemeIndex(p, i)` (37 of
them, in pardes.zig, builtins.zig, file_watch.zig, macos.zig and two test
files). pardes.zig keeps `pub const Theme/themes/native_theme_count/
ChromeTheme = colors.X;` for the shells that name them, and sync and
enterTagEdit become pub because a moved test calls them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
build too
The tty, gui and detached shells allocate from std.start's gpa, which is a
DebugAllocator in Debug and, because we link libc, libc's malloc otherwise.
The macOS shell hardcoded std.heap.smp_allocator in every mode, so its Debug
build checked nothing it allocated outside memory.zig's subsystems.
Which allocator a release build should use was measured rather than
assumed, in ReleaseFast, with an experimental gui that chose at startup
between glibc's malloc and smp_allocator, with and without memory.zig's
stack-fallback buffers, plus an A/A pair; ten interleaved rounds, compared
round by round. The gui frame-cost harness (idle, scroll and typing in
src/pardes.zig, terminal spew, wheel-scrolling docs/design.pdf, first paint)
saw every candidate within the A/A pair's noise. Twelve rounds of the
allocation-heavy paths themselves, highlighting all of src/pardes.zig (305k
tree-sitter allocations) and pardes-pdf-bench's MuPDF renders, split them:
smp_allocator was 2 to 6% slower on MuPDF's page-sized rasters (slower in 9
to 11 of 12 rounds), no better on tree-sitter, and the stack-fallback buffers
changed nothing either way. So glibc's malloc it is, with the buffers kept.
macos.zig now takes a DebugAllocator in Debug, deinitialized in
pardes_deinit with leaks logged the way std.start treats the others', and
libc's malloc otherwise. main.zig says why init.gpa is kept.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
resize columns from their grip
Every tag band (workspace, column, pane) is centred in its row, so text sits
at one baseline offset. Anchors are inset from the column rule by that same
margin, so they are square and the band frames them on the left, top and bottom;
the pane mark moves to stay centred. When the window is not a whole number of
cells, the bands, rules, spines and scroll thumbs at the right and bottom edges
run on through the leftover pixels.
A column grip dropped short of another column's place now moves the column's
left edge, with a dashed rail preview, sharing setColumnPairWidth with the
border drag. A release still on the grip changes nothing, and a pair too
narrow for two MINW columns is left alone.
PARDES_TEST_PAD adds leftover pixels to a test-mode window and capture.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
messages
PDF highlights (hover preview, search, selection) are baked into page rasters,
and any change re-rendered the whole page with MuPDF; the TTY then re-sent it
as base64 (4.7 MB a page), the GUI as a new texture. Worse, a pointer motion
over a PDF invalidated the page even when no preview was shown, so every
motion paid that. Now:
- A raster whose baked highlight set equals the wanted one is left alone.
- A highlighted page keeps its clean rows (before highlights and tint); a
change repaints only the rows of quads that differ, running MuPDF's
highlight pass (pardes_pdf_paint_highlights) over those clean rows and
tinting them: the operations a full render performs, so the pixels are
identical. MuPDF band renders are NOT bit-identical to a whole page (edge
rows, resampled images), so they are never used to patch; the comment
claiming otherwise is corrected.
- ImagePlace.patch hands shells the changed rows; the GUI uploads just those
rows into the texture it holds.
- The TTY probes kitty shared memory (t=s) with an id vaxis never reaches and
sends rasters as a /dev/shm name when the terminal reads it; direct base64
otherwise (ssh).
- Shells that take row patches (GUI, TTY with shm) repaint a selection while
it is dragged instead of only on release.
Latency elsewhere:
- TTY: an animating frame no longer sleeps 16 ms blind; a tick thread posts
into the input queue, so input inside the frame is handled at once.
- TTY and GUI: queued pointer motions coalesce to the last.
- GUI: a skipped swapchain image re-arms the frame (3 retries); animations
still tick while nothing presents.
- Editing: the line index is carried across an edit instead of rebuilt from
a scan of the whole file per keystroke.
Messages fall into their row (ease-in; the GUI slides the band out from under
the tagline, a terminal fades it), stay until the next input as before, linger
MessageLinger ms (default 800), and dissolve (ease-out). MessageAnimation
toggles it; both are settings, in Config and startup files. The snapshot
harness pins the old behaviour. The detached server now ticks animations.
A restored terminal comes back live: the old screen and scrollback (dumped
as clean VT by ghostty's formatter, replayed at the new size; older dumps
fall back to their rendered text), a dim
"restored history" marker, then a new shell in the directory it was in.
Right-click on a line number in a file pane looks at that line (a sticky
context header's number included).
Measured with an external pty driver (TTY), an in-process fence trace
(GUI, PARDES_TEST_LATENCY), and test/pdf_pointer_bench.zig (pixel identity
against the baseline and a whole-page oracle); balanced A/A/B rounds, paired
per-round statistics.
Messages stack: each event gets its own row and its own fall, linger and
dissolve; a line keeps its row until it leaves and a new one fills the first
free row. Announcements and statuses are replaced in place, not stacked.
MessageFall, MessageDissolve and DumpDir are settings Config reports.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The editor's loop was the only thing that could answer a 9P request, which
made the editor's own syscalls through a mount of its own tree -- a Look at
/mnt/9p/pardes/<me>/anything under a `9ns --mntgen` view, a Save into it --
requests only the blocked loop could serve. The name-based refusal that
followed (ownMountSuffix) and the in-process routing of a mount of oneself
(Client.sameSession) were patches over that, and both are gone, with the
mailbox that shipped every request to the editor's thread.
One rule replaces them, `pardes.turn`: the core is single-threaded, the
editor's thread has the turn by default and gives it up in two kinds of gap
-- while it waits for input and while a step of it is out in a host syscall
-- and a cloud9 connection task takes it in those gaps to answer. `out`
counts the steps that are out, from any thread: while one is, the core reads
consistently but that step still holds pointers into it, so a request that
would change a pane (a write, a truncation, an rmdir) is parked in the
engine and retried when the turn is next given up with nothing out, and the
editor's own wake waits for the count to reach zero. It is never a write of
its own that a step waits on out there -- writes come from a shell
performing a save between steps -- so a parked request is never the
syscall's own, and making a pane or rendering a screen need not park:
every yield sits before its step's mutation, so the layout and the surface
are whole under it. A changing request that queued effects is answered
once the editor has performed them (`echo Save > exec` returns with the
file written, as acme's `put` does), and it settles the way a step does,
because without that a /log reader waited for the user's next keystroke.
Every host syscall on a user path has to give the turn up, not fs.zig's
alone: the first end-to-end run hung in `inotify_add_watch` performing the
new pane's watch effect. PDFs and images are read whole at open, so no
draw goes out into the host. The core's allocator takes its fixed buffer
through the lock-free interface, since a connection task allocates while
the editor's thread is out in a syscall that allocates too. A Restore puts
the replacement in first and releases every task waiting on the old core.
cloud9 (pinned at eb1a104) parks an open, a truncating wstat, a clunk and a
remove on `again`, not only reads and writes, and answers a parked job
whose fid was clunked without asking the backend.
Verified: test/selfmount.py runs the editor under `9ns --mntgen` and
Looks at, reads and Saves its own tree through the mount; a unit test pins
that a change parks while the editor is out mid-step and lands when it
rests, while a read is answered in the window. 9P over the Unix socket
against a tty session, same machine, Debug builds: a read of /index 278us
-> 61us, a truncating body write 1184us -> 609us, exec Save 718us -> 583us;
the gesture benchmark is unchanged (geometric mean 0.997 over 53 cells).
Also from the reviews: a notice chip over an image or PDF pane was painted
out by the picture drawn after the cells, so pictures give up the rows; in
the GUI a tree-sitter context band painted over the chip, so body layers
are emitted first; a message is one row of printable text, its 256-byte
cut never leaves half a glyph, and one wider than its pane keeps its tail
(the file name, the reason) rather than its head.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The registry sweep could delete a live socket, anywhere on the filesystem. A
reviewer reproduced it: a socket that is bound but has not reached listen(2)
answers ECONNREFUSED exactly like a dead one -- that window is every server's
startup -- and the sweep then followed the entry's symlink and unlinked
whatever absolute path it named. It now follows a target only into the
directory our own sockets live in and only to a `pardes-9p-*.sock` name, it
re-probes immediately before deleting rather than trusting a probe that is by
then several syscalls old, and a readlink that exactly filled its buffer is
treated as the truncation it is. The test grew a case for an entry whose
target is not ours: the entry goes, the file does not.
Ctrl-V in raw tty mode was a black hole when the yank register was empty --
neither typed nor forwarded -- so vim's visual block, readline's quoted-insert
and every other program's Ctrl-V simply vanished. With nothing to paste the
chord belongs to the program again.
The lone-ESC flush added earlier was dead code. vaxis already returns Escape
for a one-byte 0x1b (`Parser.parseGround` asserts `input.len == 1`), so the
carried byte it waited for can never exist; a reviewer showed a 3 ms gap and a
60 ms gap behaving identically. Removed rather than left to imply a guarantee
it never provided.
A shell whose editor is gone can start one again. Naming a live but
unreachable session made `pardes <file>` exit 1, which let a stale environment
variable lock someone out of their own editor; it falls through to an ordinary
session, as it did before the variable existed.
Also: the macOS ABI check for `pardes_topbar_pane_border_px` had been replaced
by a duplicate of the line above it; `--startup` now fails on a leak the way
every other measurement in that file does, and stops calling its maximum a p95
below twenty samples; the served README and the skill no longer tell you to
write to `data` with `>`, which truncates the whole body before the write
lands; `docs/v9fs.md` described the allocate-on-walk design that was rejected;
and `test/fs.py` keys nesting off `PARDES_PID`, so its forwarding case stops
passing only when the runner happens to be inside a live pardes.
fs-test now reaches its one documented pre-existing failure instead of dying
early. Suite 778/783 with the two known crashes.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A 9P round trip on a local Unix socket cost 9.7 ms against the SDL shell and
0.758 ms against the terminal one. The server was not slow and the wake was
not broken: instrumenting the path showed every request waking the loop early
(wakes=210, woke_early=212, timed_out=38 over 250 ticks) and being answered on
that same pass. The cost was that `pump` answers 9P at one point in a loop
that then renders and presents unconditionally, so a client's next request
landed while the main thread was blocked on the display, and each round trip
therefore cost a whole frame. The frame rate was governing something that has
nothing to do with drawing.
`Pardes.needs_frame` starts true, is set by every event except a tick with
nothing animating and a filesystem request that only reads, and is cleared
once a frame is presented. `pump` returns before render and present when it is
false and nothing is animating. An idle editor answering reads now draws
nothing at all.
9P read_fid, one RPC: gui 9.7 ms -> 0.056 ms (173x)
tty 0.758 ms -> 0.062 ms (12x)
Verified the shells still paint rather than going quiet: the rendered screen
carries the opened file, a write through 9P redraws within the frame, and
`fs-discovery-test` passes over the real wire. Suite unchanged at 778/783 with
the two pre-existing crashes.
Also from the adversarial review of the previous commits:
`pardes --tty FILE` silently discarded the file, and `--tty MISSING` silently
discarded the error pane. main.zig named the boot layout before the positional
was resolved, and naming one short-circuits `Boot.of`. The choice now happens
after the argument is known, and only when there is no file and no missing
word. macos.zig names the same layout, so the app no longer boots a different
one from the terminal and SDL shells.
`pre_close_last_pane_tail` was transcribed from the NEW default rather than the
old one, so the upgrade path it was added for did not exist: a workspace dumped
before the tagline reorder came back with the old default welded on as a custom
tail. It is now the string it claims to be.
A pane two rows tall lost its message and, worse, its prompt and the cursor
with it. The notice cap keeps the LAST notices now, because the prompt is last
and a prompt you cannot see is one you type into blind.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Pty children are exec'd with their own TERM/COLORTERM/TERM_PROGRAM instead of
inheriting a .app launch's empty environment, and ttyTaken finally answers on
darwin — libproc walks the tty's foreground process group — so Escape reaches
the child and Exec stops believing every pane sits at its prompt. The
occupancy suite runs on both platforms now.
The workspace tag row moves into the native menu bar as a Builtins menu.
-Dworkspace-tag (default off for -Dplatform=macos, on everywhere else) drives
it, and Pardes.topBarHeight replaces the TOPBAR_H constant so the core stops
reserving the row.
The view pins every variable-font axis to the file's own default (Maple Mono
came up Thin otherwise), shapes ligatures, carries per-shape pointer cursors,
and draws the look-hover affordance as refracted glass. Tag rows fill edge to
edge, with the anchor box painted back on top of that fill and its mode glyph
centred on the same square. Theme accents re-saturated across the set.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
The hand-written poll loop for Unix/TCP listeners is replaced by
cloud9.serve.Runner; requests are queued to the editor thread, which answers
them under the connection lock on each frame and retries parked reads as
before. QUIC keeps the poll path (its adapter is fd based). The detached
server no longer loses a wake that lands between frames. The firmware path
keeps driving the engine with push/step. cloud9 re-pinned.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A swipe that was still moving when the fingers lifted stopped dead. The dial
next to it has thrown properly since pardes_rotate_end, and this is that same
curve on the scroll accumulator: velocity sampled between events off the
monotonic clock, weighted toward the newest sample because a flick is decided
by how the hand was moving when it left, and a fling that ramps up from zero
at the floor rather than switching on at it.
A coast stops at the end of a document rather than spinning its remaining
velocity against the edge, which is why spendScroll now reports whether the
pane moved and why paneAt is public. Only a step that delivered a press can
report an edge — the many steps between two rows cross nothing.
Nothing suppresses AppKit's own momentum, and nothing needs to: its momentum
events are ordinary pardes_scroll calls, every one of which cancels the coast
before spending its travel, so on a real trackpad the system takes the gesture
over about a frame after the lift and the tail it ends on is below the floor.
This is the path for devices AppKit does not fling for — and the only one a
script can reach, since NSEvent phases have no public constructor.
momentum.snap asserts both halves, which is why it needs the long scrollback:
a flick with no document left proves nothing. A slow swipe is byte-identical
after the release; a hard one coasts twenty-four rows further on its own, and
a finger back on the pad stops it there.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The builtin was gated on the SDL platform, so the macOS shell never registered
it and the setting had nowhere to land. macOS already had transparency, but
only the binary kind a theme decides — a theme with no background of its own
drops the ground and the blur shows through. WindowOpacity is the graded,
theme-independent version, and the window had no way to hear about it.
pardes_window_opacity reports the percentage and acknowledges the request in
one read, beside pardes_theme_bg: an SDL surface can arrive without an alpha
channel and has to be able to refuse, while an AppKit window always composites
per pixel, so there is nothing here to refuse and no rollback to perform.
The view then paints the rule shaders/ui.frag.glsl states for the SDL shell:
backgrounds — ground, cell and band fills, and the chrome rules over them —
take the alpha, glyph ink never does, and the block cursor is exempt because
it is foreground chrome that happens to be carried in a cell background. That
exemption is why the cursor joins the colour in the background run key; it can
no longer share a fill with the cells beside it.
Two things the harness was missing fall out of testing it: it never adopted
the theme background or the opacity, so a Theme or WindowOpacity in a script
moved the core and never reached a pixel. draw-opacity records both ends of
the alpha range, which is what makes the two-sided contract assertable —
60% reads 153..255, and 0% reads 0..255 with the ink still standing.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|