When a tab contains only a single split, resize_split and toggle_split_zoom
actions now return false (not performed). This allows keybindings marked with
`performable: true` to pass the event through to the terminal program.
The performable flag causes unperformed actions to be treated as if the
binding didn't exist, so the key event is sent to the terminal instead of
being consumed.
- Add isSplit() helper to SplitTree to detect single-pane vs split state
- Update GTK resizeSplit/toggleSplitZoom to return false when single pane
- Update macOS resizeSplit/toggleSplitZoom to return Bool and check isSplit
- Add unit test for isSplit method
This never caused any known issues, but it's a bug! `increaseCapacity`
should produce a node with identical contents, just more capacity. We
were forgetting to copy over the dirty flag.
I looked back at `adjustCapacity` and it also didn't preserve the dirty
flag so presumably downstream consumers have been handling this case
manually. But, I think semantically it makes sense for
`increaseCapacity` to preserve the dirty flag.
This bug was found by AI (while I was doing another task). I fixed it
and wrote the test by hand though.
We previously wrote our new cache file into a temporary directory and
the (atomically) renamed it to the canonical cache file path. This
rename operation unfortunately only works when both files are on the
same file system, and that's not always the case (e.g. when $TMPDIR is
on its own file system).
Instead, we can use Zig's AtomicFile to safely perform this operation
inside of the cache directory.
There's a new risk of a crash leaving the temporary file around in this
directory (and not getting cleaned up like $TMPDIR-based files), but the
probability is low and those files will only be readable by the creating
user (mode 0o600).
There's a new test cash that verifies the expected AtomicFile clean up
behavior. I also switched the file-oriented tests to use testing.tmpDir
rather than using our application-level TempDir type.
Fixes#10364
---
**AI Disclosure:** I asked Claude to write the initial test case to
verify the AtomicFile cleanup behavior.
This never caused any known issues, but it's a bug! `increaseCapacity`
should produce a node with identical contents, just more capacity. We
were forgetting to copy over the dirty flag.
I looked back at `adjustCapacity` and it also didn't preserve the dirty
flag so presumably downstream consumers have been handling this case
manually. But, I think semantically it makes sense for
`increaseCapacity` to preserve the dirty flag.
This bug was found by AI (while I was doing another task). I fixed it
and wrote the test by hand though.
We previously wrote our new cache file into a temporary directory and
the (atomically) renamed it to the canonical cache file path. This
rename operation unfortunately only works when both files are on the
same file system, and that's not always the case (e.g. when $TMPDIR is
on its own file system).
Instead, we can use Zig's AtomicFile to safely perform this operation
inside of the cache directory.
There's a new risk of a crash leaving the temporary file around in this
directory (and not getting cleaned up like $TMPDIR-based files), but the
probability is low and those files will only be readable by the creating
user (mode 0o600).
There's a new test cash that verifies the expected AtomicFile clean up
behavior. I also switched the file-oriented tests to use testing.tmpDir
rather than using our application-level TempDir type.
Fixes#10352
The bug was that non-standard pages would mix the old
`growRequiredForActive` check and make our active area insufficient in
the PageList.
But, since scrollbars now require we have a cached `total_rows` that our
safety checks always verify, we can remove the old linked list traversal
and switch to some simple math in general across all page sizes.
Fixes#10352
The bug was that non-standard pages would mix the old
`growRequiredForActive` check and make our active area insufficient in
the PageList.
But, since scrollbars now require we have a cached `total_rows` that our
safety checks always verify, we can remove the old linked list traversal
and switch to some simple math in general across all page sizes.
Fixes#10258
Replaces #10284
1. `Page.Capacity` now uses smaller bit-width integers that represent a
true maximum capacity for various fields.
2. On 64-bit systems, a maxed out `Page.Capacity` (every field `maxInt`)
can be represented in an addressable allocation (total required memory
less than 64 bits). This means `Page.layout` can't overflow.
3. All `adjustCapacity` functions replaced with `increaseCapacity` which
doesn't allow specifying the resulting value, which makes it so overflow
is only possible in significantly fewer places, making it easier to
handle in general.
4. `increaseCapacity` can return a new error `OutOfSpace` which happens
when overflow is detected. This means that no valid page can accommodate
the desired capacity increase because we're already at the max. The
caller is expected to handle this.
5. Updated our resize so that the only possible error is system OOM, we
handle the new `OutOfSpace` by copying the recent reflowed row into a
new page and continuing.
A very, very high-level overview is below. The "overflow" here papers
over a bunch of details where the prior usize capacities flowed through
to Page.layout and ultimately RefCountedSet and other managed types
which then caused incorrect calculations on total memory size required.
```mermaid
flowchart TB
subgraph Before["Before: adjustCapacity"]
A1[capacity: usize] --> A2["capacity *= 2"]
A2 --> A3{Overflow?}
A3 -->|"Not detected"| A4["Massive allocation or crash"]
end
subgraph After["After: increaseCapacity"]
B1["capacity: bounded int<br/>(u16/u32)"] --> B2["capacity *= 2"]
B2 --> B3{Overflow?}
B3 -->|"OutOfSpace error"| B4["Graceful handling:<br/>move row to new page"]
B3 -->|"Success"| B5["Normal allocation"]
end
Before --> After
classDef beforeStyle fill:#3d1a1a,stroke:#ff6b6b,color:#ff6b6b
classDef afterStyle fill:#1a3d3a,stroke:#4ecdc4,color:#4ecdc4
class A1,A2,A3,A4 beforeStyle
class B1,B2,B3,B4,B5 afterStyle
```
Fixes#10282
The function `cursorChangePin` is supposed to be called anytime the
cursor page pin changes, but it itself may alter the page pin if setting
up the underlying managed memory forces a page size adjustment.
Multiple callers to this function were erroneously reusing the old page
pin value.
**AI disclosure:** I had Amp help me write the test. I eyeballed and
found the bug myself, verified it by asking Amp to write the test,
reviewed that manually, then implemented the fixes manually and got it
to pass.
Fixes#10282
The function `cursorChangePin` is supposed to be called anytime the
cursor page pin changes, but it itself may alter the page pin if setting
up the underlying managed memory forces a page size adjustment.
Multiple callers to this function were erroneously reusing the old page
pin value.
This simplifies our CI command line and makes it easier to document
expected usage (in HACKING.md).
There unfortunately isn't a way to set --checked-sourced or our default
warning level in .shellcheckrc, and our `find` command is still a bit
unwieldy, but this is still a net improvement.
This simplifies our CI command line and makes it easier to document
expected usage (in HACKING.md).
There unfortunately isn't a way to set --checked-sourced or our default
warning level in .shellcheckrc, and our `find` command is still a bit
unwieldy, but this is still a net improvement.
## Summary
- Fix literal \n appearing in window titles when running commands in zsh
## Description
The zsh shell integration was using ${(V)1} parameter expansion to set
the window title. The (V) flag converts control characters to their
visible escape sequence representations, causing commands ending with a
newline to display as command\n in the title bar.
Changed to use ${1//[[:cntrl:]]} which strips control characters
entirely, matching the behavior of the bash integration.
<img width="231" height="47" alt="image"
src="https://github.com/user-attachments/assets/356c77a1-32da-47b0-bf86-29735cc67e90"
/>
## Test
- Open Ghostty with zsh shell integration enabled
- Run a command (e.g., cargo install trunk)
---
AI Disclosure: This PR was written primarily by Claude Code. (Opus 4.5)
The zsh shell integration was using `${(V)1}` parameter expansion to set
the window title, which converts control characters to their visible
escape sequence representations. This caused commands ending with a
newline to display as `command\n` in the title bar.
Changed to use `${1//[[:cntrl:]]}` which strips control characters
entirely, matching the behavior of the bash integration.
Bumps
[namespacelabs/nscloud-setup-buildx-action](https://github.com/namespacelabs/nscloud-setup-buildx-action)
from 0.0.20 to 0.0.21.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a7e5254161"><code>a7e5254</code></a>
Adopt new wait command in action. (<a
href="https://redirect.github.com/namespacelabs/nscloud-setup-buildx-action/issues/12">#12</a>)</li>
<li>See full diff in <a
href="91c2e65377...a7e5254161">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Assuming /bin/sh is symlinked to bash, the handling of special builtin
'source' is slightly different between bash and bash-in-POSIX-mode (as a
result of being invoked through /bin/sh). Specifically errors in builtin
'source' cannot be masked with `|| true`. Compare
```
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec 11 11:00 /bin/sh -> bash
$ /bin/sh -c 'set -e ; source nofile || true; echo ok'
/bin/sh: line 1: source: nofile: file not found
$ /bin/bash -c 'set -e ; source nofile || true; echo ok'
/bin/bash: line 1: nofile: No such file or directory
ok
```
Thus ghostty from snap would not start at all when
$SNAP_USER_DATA/.last_revision does not exist causign the launcher
script to exit prematurely.