Commit Graph
72 Commits
Author SHA1 Message Date
l0ng-ai f9560dc30b chore(release): v0.13.0 v0.13.0 2026-07-13 19:33:29 +08:00
l0ng-aiandl0ng-ai e1d502a3f2 fix(ci): format code and platform-gate the ctrl glyph in keymap test (#67)
cargo fmt over app.rs/pane.rs/settings.rs/keymap.rs (rustfmt CI job).

key_chords_splits_a_sequence_into_keycap_groups hard-coded ⌃ for the
ctrl modifier, which only renders that way on macOS; elsewhere key_tokens
maps ctrl to "Ctrl", so the test failed on the Linux and Windows runners.
Gate the expected glyph behind target_os like the existing SECONDARY and
SHIFT constants.

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-13 16:14:21 +08:00
ayamir f25667cc1d feat(links): add SSH loopback forwarding (#58)
Detect foreground SSH sessions in the daemon and cache the active remote context per pane; open Cmd-clicked loopback URLs through daemon-owned ssh -N -L local forwards over a ControlMaster socket; add settings controls to enable SSH loopback forwarding and view/close active forwards. Kept behind an explicit ssh_loopback_forward setting; the daemon validates the pane's foreground process is a plain SSH session and rejects unsafe invocations. Includes a follow-up hardening commit rejecting option-like ssh targets (leading '-') to close a local argument-injection gap.
2026-07-13 16:02:02 +08:00
l0ng-ai 2443a951c0 fix(ui): trim the first tab's left gap flush to the traffic-light reserve (#62)
Drop the tab strip's leading pl_2 to pl_0 so the first chip starts flush against the title bar's left reserve (80px traffic-light on macOS, 12px elsewhere) instead of adding an extra 8px gutter. Right side and per-chip text inset unchanged.
2026-07-13 15:35:50 +08:00
l0ng-ai 9a4e97c0e2 fix(ui): remove the active-pane corner indicator dot (#63)
The focused pane is already distinguished by fading inactive panes (opacity 0.55) when a tab is split, so the corner dot was redundant. The focused flag still drives the fade.
2026-07-13 15:35:45 +08:00
l0ng-ai e71efed007 feat(keybindings): editable shortcuts, pane/tab actions, tmux preset (#65)
Implements issue #61's tmux-like input model in three layers — editable shortcuts (Settings → Keybindings), directional pane focus/resize/swap + relative tab nav + Activate Tab 1-9, and a tmux prefix preset — without parsing ~/.tmux.conf and without changing zero-config defaults. Closes #61.
2026-07-13 15:28:36 +08:00
l0ng-ai f77d62a918 chore(release): v0.12.0 v0.12.0 2026-07-13 11:03:47 +08:00
4b29b5967f fix(ui): stop the title-bar strip from clipping the Windows close button (#60)
On Windows the tab strip reserved only 100px on the right for the native
window controls, but gpui-component's TitleBar lays out 12px of left
padding plus three 34px caption tiles (─ ▢ ✕ = 102px) beside the strip —
114px in all. The 14px shortfall let the strip overrun the bar and shove
the ✕ off the rounded corner once the overflow "⋯" was pinned to the
strip's right edge. Reserve the full 114px so the strip's right edge meets
the controls and the "⋯" keeps its inset.

Co-authored-by: thomas <thomas@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 10:53:21 +08:00
l0ng-aiandl0ng-ai b5beba6d80 feat(release): ship a Linux AppImage alongside the tarball (#55)
* feat(release): ship a Linux AppImage alongside the tarball

The Linux release was a bare, dynamically-linked binary built on
ubuntu-latest, so it only reliably ran on Ubuntu — Fedora/Arch users hit
missing/mismatched runtime libs. Add an AppImage that bundles the
x11/wayland/xkb/fontconfig/freetype libs so it launches across distros.

- bundle-appimage.sh: linuxdeploy populates an AppDir + deps, completions
  go beside the binary (usr/bin/completions, matching signature.rs's
  current_exe lookup), appimagetool packs it. Runs FUSE-less on CI.
- release.yml: new "Package Linux AppImage" step after the tarball,
  libfuse2/file added to the Linux deps, *.AppImage added to the upload
  list. The AppImage step avoids `rm -rf dist` so the tarball survives.
- README (en + zh): recommend the AppImage, keep the tarball as the bare
  fallback.

Note: glibc is not bundled, so ubuntu-latest still sets the glibc floor.

* fix(release): downscale AppImage icon to a resolution linuxdeploy accepts

linuxdeploy rejected the 1024x1024 app-icon.png (its valid list tops out at
512). Resize to 256x256 with ImageMagick's convert (added to the Linux apt
deps) before handing the icon to linuxdeploy.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-13 10:43:26 +08:00
l0ng-aiandl0ng-ai 78a6f00ec8 feat(settings): redesign theme picker with a slide-in panel (#56)
* feat(settings): redesign theme picker with a slide-in panel

Redesign the Appearance page's theme UI:

- Replace the inline theme gallery with a compact "Current theme" card
  (preview + name + light/dark) that opens a searchable theme picker in
  a right-hand panel; applying a theme keeps the panel open so several
  looks can be tried in a row.
- Add `theme_panel_open` / `theme_search` to `SettingsState`, plus
  toggle/close helpers and an `active_settings_mut` accessor.
- Share one `theme_preview` between the card and the panel cards; keep
  the existing bar-based preview shape.
- Group the custom-theme controls (duplicate / color editor / open
  folder) under the Theme section instead of stranding them at the foot
  of the page, and drop the near-black `.primary()` fill on the
  Duplicate button so it fits the soft, mostly-outline sheet.

* style: rustfmt theme panel border_color

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-13 10:38:45 +08:00
l0ng-aiandl0ng-ai 0fdfcf127a feat(ui): add a title-bar overflow menu for palette and settings (#57)
* feat(ui): add a title-bar overflow menu for palette and settings

The command palette and settings panel were reachable only by keyboard
(⌘P / ⌘,) — no on-screen affordance existed, so the app leaned entirely
on shortcuts for its two most useful non-terminal entry points.

Add a ghost "⋯" button on the title bar's otherwise-empty right edge
(same 30px tile rhythm as the "+"), opening a small dropdown:

  - Command Palette  ⌘P
  - Settings…        ⌘,

Both rows dispatch the real gpui action, so a click and the shortcut
share one path and each row auto-renders its keybinding hint. The menu
is hidden while the settings tab is active (both entries are redundant
there). On Windows/Linux the window controls sit on the right, so the
button gets extra right padding to read as a menu, not a fourth control.

* fix(ui): keep the title-bar overflow menu from drifting into the corner

The '⋯' is pinned to the right edge of a strip that used '.w_full()', but
the title bar sizes its content by intrinsic width, so 'w_full' never
tracked the window. Shrinking the window left the strip's right edge —
and the pinned '⋯' — lagging behind, sliding the button into the rounded
corner where it clipped and read cramped.

Derive the strip width from the live viewport instead, mirroring the cap
already used for the chip row, so the right edge tracks the window at
every size and the '⋯' holds its original tight 8px inset on resize.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-13 10:37:29 +08:00
l0ng-aiandl0ng-ai 4c2082235d revert(branding): restore original terminal-window logo (#59)
The #54 branding refresh replaced the original line-art logo with a
heavier Slate-tile + gradient-ring mark. Restore the original assets
(rounded terminal window frame, title-bar divider, orange cursor block)
across svg/png/icns/ico, reverting to the pre-#54 versions.

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-13 10:37:24 +08:00
l0ng-ai 90cae6c731 chore(release): v0.11.0 v0.11.0 2026-07-12 19:52:26 +08:00
l0ng-ai c4bee13d83 feat(theme): file-based themes, in-app editor, and a UI/branding refresh (#54)
Replace compiled-in presets + colors.*/ansi_colors.* overrides with a file-based theme system: a serializable seed (bg/fg/accent/cursor/selection + ANSI-16, optional gradient/image/opacity/blur) with all chrome derived, light/dark inferred from luminance behind a WCAG guard, built-ins + user YAML + on-the-fly iTerm2 import via a hot-reloaded registry, and an in-app duplicate-to-edit color editor. Also: prompt-editor shift-click/word-drag selection and cross-platform word keys, ghostty-style tab labels, flat menu highlights, a redesigned app icon, the Background Service -> Daemon rename, and a gated TTY7_PROFILE build-timing probe.
2026-07-12 19:51:07 +08:00
l0ng-ai a50300f0ff chore(release): v0.10.0 v0.10.0 2026-07-11 19:14:19 +08:00
l0ng-aiandl0ng-ai 21b7f82392 feat(completion): execute dynamic generators for live candidates (#52)
* feat(completion): execute dynamic generators for live candidates

The completion engine consumed Fig specs' static shape but never ran
their dynamic generators, so positions whose candidates come from the
live system — ssh hosts, git branches — fell through to filesystem
path completion (#51: ssh <Tab> listed the cwd).

Local-only by design: the pure engine returns each pending script, the
view runs it on the background executor (/bin/sh -c in the session
cwd, 800ms timeout, kill-on-drop, 256KiB stdout cap, 5s TTL cache) and
merges the parsed lines into the open menu, generation-tagged so a
result can't outlive its session. A per-script parser registry ports
the specs' dropped postProcess transforms (git markers, docker
{{json .}}, package.json scripts, …); unmatched scripts default to
one-candidate-per-line, and hopeless outputs are suppressed rather
than inserted as garbage.

ssh/scp/sftp/rsync specs gain host generators reading ~/.ssh/config
(Include-aware, wildcard patterns skipped) and known_hosts (hashed
entries skipped, [host]:port unwrapped).

Fixes #51

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>

* docs(comments): describe borrowed conventions directly

Prior-art name-drops in comments aged poorly as the implementations
diverged; keep the behavioral rationale, drop the product citations.

* fix(completion): make generator execution CI-portable

Linux: sh -c may fork the command instead of exec'ing it, so killing
only the shell on timeout left a grandchild holding the stdout pipe —
the reader (and the caller) then blocked until the grandchild exited
on its own. Spawn the child as its own process-group leader and kill
the group; the timeout test now forces the fork case (trailing true)
so the group-kill is what's actually proven.

Windows: generator scripts are POSIX sh + awk, so the execution path
now compiles to no-suggestions there instead of failing at runtime on
a missing /bin/sh; the process-spawning tests are Unix-only to match.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-11 19:12:10 +08:00
l0ng-aiandl0ng-ai aae33ede23 feat(links): let Cmd+click open directories (#50)
Follow-up to #49: iTerm2-style semantic paths — an existing directory in
the row text links like a file does, and the system opener (open /
xdg-open / explorer) already handles directories natively, so detection
is the only change. Bare paths only: a token carrying a :line suffix
still requires a file, so localhost:8080 can't link just because a
directory named localhost exists in the cwd.

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-11 17:32:26 +08:00
ayamirandl0ng-ai 63a05b256e feat(links): add file path links (#49)
* feat(links): add file path links

* fix(links): reset the cached link modifier on activation flips, cover all tabs

The per-pane link_modifier_down cache was refreshed only for the active
tab's leaves, and never on window (de)activation — so releasing Cmd after
a mouse tab-switch, or during Cmd-Tab/Spotlight (the release lands in
whatever app is key by then), left panes stuck at true. A stale true makes
a plain unmodified left click open links and steals clicks from
mouse-tracking TUIs.

Route the refresh through a helper that walks every tab, and treat the
window-activation flip as a release, exactly like the badge dismissal
right next to it. Also reword the search.rs docs that still described the
now test-only url_at as the production entry point.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-11 14:32:51 +08:00
l0ng-ai 75c4fed62f docs(readme): drop the header screenshot 2026-07-11 10:24:37 +08:00
l0ng-ai 0ee126a7fd docs(readme): restructure around Why/Features, cut prose and boilerplate (#48)
* feat(view): explain a dead Ctrl+R instead of failing silently (#46)

When shell integration never engages in a pane — typically because a
figterm-style PTY shim (kiro-cli-term, qterm) exec'd over the shell and
swallowed its OSC 133 reports — the whole command-editor overlay is
absent by design, and Ctrl+R used to fall through to the raw PTY with no
hint of why the history menu didn't appear.

Now that raw-path Ctrl+R raises a one-shot, per-pane notice (floating
bottom-right) saying integration hasn't engaged, refined off-thread with
the daemon's foreground-process name when it matches a known shim: the
wrapper is the culprit worth naming, since "install integration" advice
would mislead — the hooks are installed, something between the shell and
tty7 is eating their output. The chord still reaches the PTY, so the
shell's own reverse-i-search keeps working as the fallback.

Guards keep it honest: silent inside an 8s startup grace window (slow rc
files legitimately haven't reported yet), on the alt screen, or once
integration has engaged (a running foreground command is then the
obvious reason); retracted if a slow shell engages late; dismissed by
the next keystroke or a 15s timeout.

* docs(readme): restructure around Why/Features, cut prose and boilerplate

Replace the prose About section with a four-point Why tty7 list, split
Features into prompt vs window groups written as one-line benefit
bullets, drop emoji section headers, fold acknowledgements/contributing/
license into a one-line footer, and keep zh-CN in sync throughout.
2026-07-11 10:22:54 +08:00
l0ng-ai 5e7a3240ad chore(tooling): add repomix config for AI-friendly repo packing 2026-07-10 19:05:12 +08:00
l0ng-ai 3cc5d3ee0f chore(docs): drop the openwiki directory 2026-07-10 18:59:06 +08:00
l0ng-aiandClaude Fable 5 bf9499260f chore(release): v0.9.0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.9.0
2026-07-10 18:30:05 +08:00
7401fd59df feat(history): Ctrl+R fuzzy search menu with run metadata (#45)
* feat(history): Ctrl+R fuzzy search menu with run metadata

Ctrl+R grows from the single-line reverse-i-search into a browsable
menu of ranked candidates floating beside the prompt:

- Matching is fuzzy (src/terminal/fuzzy.rs, a dependency-free
  affine-gap aligner: word-boundary and consecutive-run bonuses, gap
  penalties; space-separated query terms must all match), blended with
  the existing frecency scores so a command you run constantly — or
  ran in this directory — outranks an equally-good textual match.
- An empty query lists the whole history by frecency, so bare Ctrl+R
  is a "recent & relevant" browser. Matched characters highlight in
  the rows; Ctrl+R/Down and Ctrl+S/Up move the selection, Enter loads
  the line into the editor, Cmd+Enter runs it outright. The classic
  (reverse-i-search) prompt line stays.
- History records now carry run metadata: new lines are
  <ts>\t<exit>\t<cwd>\t<command>, written when the command finishes
  (zsh INC_APPEND_HISTORY_TIME-style) so the exit code sniffed from
  OSC 133;D lands in the record; older formats still parse, and
  zsh/bash HISTFILE timestamps carry over when seeding. The menu shows
  "ran 3h ago" and a red x-badge on commands whose last run failed.
- RemoteTerminal exposes prompt_seq/last_exit_code so the view can
  tell a fresh post-command prompt report from the stale pre-submit
  state even when 1 Hz polling misses a fast command's running window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style: cargo fmt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:28:42 +08:00
l0ng-aiandl0ng-ai 2c3b13fc99 fix(links): balance parens/brackets in URL detection (#44)
ASCII () and [] are legal URL characters, so the URL token scan let an
orphan closer glue the surrounding prose onto the link:
'(https://github.com/l0ng-ai/tty7/pull/43)(Fixes' detected everything
through '(Fixes' as one URL. Meanwhile trim_trailing_punct stripped a
trailing ')' unconditionally, so a balanced Wikipedia-style
'…_(programming_language)' lost its real closing paren.

Treat closers by balance instead, both ways:
- cut the URL at the first ')' or ']' with no matching opener inside it
  (the '(' before the scheme is prose, dropped with the prefix), and
- keep a trailing ')' / ']' whose opener sits inside the URL, stripping
  only unmatched ones (full-width closers can't appear in a URL at all
  and stay unconditional).

The www. fallback re-trims after dropping leading wrappers, since
'(www.x)' now keeps its ')' through the first, balance-aware trim.
IPv6 '[::1]' hosts survive the same way.

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-10 14:37:07 +08:00
l0ng-aiandl0ng-ai cf59fc67fc fix(daemon): reap a live-but-unreachable daemon instead of stranding it (#43)
A replaced daemon used to linger forever: both takeover paths (ensure_running
on a failed connect, restart after the Shutdown wait) would unlink the endpoint
and spawn a fresh daemon without checking whether the old process was still
alive. A daemon that couldn't be stopped — a binary predating ClientMsg::Shutdown,
or a wedged teardown — survived unreachable, still holding every pane's PTY and
children. That is exactly how 14 panes (11 live sessions) got silently stranded
across an app update.

Now the daemon records its pid in <config>/daemon.pid after bind, and both
takeover paths reap the recorded daemon before claiming the endpoint:
SIGTERM first — handled by a new sigwait thread that tears down like Shutdown,
giving every pane's child its SIGHUP grace — then SIGKILL if it won't die.
The pidfile is never trusted blindly: the pid must be alive and its executable
basename must match our own, so a crashed daemon's stale pidfile (pid possibly
recycled) is cleared, not killed. Windows reaps via the existing winproc
helpers, descendants-first, mirroring the pane hangup order.

Fixes #42

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-10 14:36:44 +08:00
l0ng-ai c514df8e5a chore(release): v0.8.0 v0.8.0 2026-07-10 11:48:38 +08:00
l0ng-aiandl0ng-ai e1a552ec99 feat(clipboard): add copy-on-select (#40)
Selecting with the mouse — drag, double-click word, triple-click line,
over terminal output or the prompt's command editor — copies the
selection the moment the gesture ends, no Cmd+C needed. Opt-in via
Settings -> Terminal -> Clipboard (config key copy_on_select), off by
default so a stray selection never overwrites the clipboard.

Closes #34

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-10 11:42:25 +08:00
l0ng-aiandl0ng-ai 388fffeaaf fix(hints): dismiss held-modifier tab badges on window activation flip (#41)
* fix(hints): dismiss held-modifier tab badges on window activation flip

The badges were dismissed only by ModifiersChanged (release) or a real
keypress. Deactivating mid-hold — cmd-tabbing away, Spotlight, a click
into another app — delivers the modifier release to whatever app is key
by then, so this window never saw it and the badges stuck on until some
later keypress; mouse-only use left them up forever.

Dismiss on every window-activation flip via observe_window_activation.
Flipping on *both* directions also cancels a reveal scheduled just
before the switch, so the timer can't pop badges up in a window the
user already left.

Tested with a headless gpui harness: Tty7App now builds through a
with_session seam (a zero-tab session restores the home page, spawning
no terminal/daemon), the test activates the window for real, simulates
the bare-secondary hold, then deactivates and asserts the badges (and a
pending reveal) are gone. Session's save/load test takes a shared file
lock so parallel tests can't clobber the pinned session.json.

* style(hints): satisfy rustfmt in gpui test harness

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-10 11:29:17 +08:00
l0ng-aiandl0ng-ai 3cd9dee454 feat(selection): auto-scroll scrollback when dragging past the pane edge (#39)
Dragging a selection above the top (or below the bottom) of the pane
used to pin at the visible edge: pos_to_cell clamps the row and
on_select_update never moves the display offset, so scrollback content
just out of view was unreachable by mouse selection.

Now a left-drag that crosses the pane's vertical edge arms a repeating
50ms task that scrolls the display and re-anchors the selection's
moving end to the edge row, so the scroll continues while the pointer
holds still past the edge (mouse-move events alone stop the moment the
hand does). Speed scales with the overshoot distance — 1 line/tick when
grazing the edge up to a capped 8 lines/tick (~160 lines/s) — and the
task stops when the pointer re-enters, the drag ends, or a newer task
supersedes it (epoch guard, so a quick leave/re-enter/leave can't
double the speed). On the alt screen scroll_display no-ops (no
scrollback), leaving TUIs untouched.

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-10 10:57:54 +08:00
l0ng-ai 9b03f006b1 chore(release): v0.7.0 v0.7.0 2026-07-10 09:23:28 +08:00
ayamirandl0ng-ai 8a65ca9cf8 feat(settings): add font ligature toggle (#38)
Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-10 09:16:02 +08:00
ayamir eeb8eed6ba feat(theme): support ANSI color overrides (#37) 2026-07-10 09:05:10 +08:00
ayamir 6b202bffc6 feat(input): support <C-l> to clear screen (#36) 2026-07-10 09:04:42 +08:00
l0ng-ai 315dd85831 chore(release): v0.6.2
Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
v0.6.2
2026-07-08 22:36:43 +08:00
l0ng-ai b21e299fba fix(windows): close a pane when its shell exits on its own (#30)
Windows: a shell that exits on its own (exit / Ctrl-D / crash) left the pane
wedged open because ConPTY's output pipe never EOFs on a natural child exit.
Add a Windows-only exit monitor that waits on the child handle directly and
reports through a shared, run-once DeathReporter latch — the same Exited /
on_dead path the reader's EOF drives on Unix. Unix behavior is unchanged.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-08 22:31:57 +08:00
l0ng-aiandl0ng-ai 59a4c9d859 fix(terminal): stop severing wide Nerd Font prompt glyphs (#17) (#31)
* fix(terminal): give solo prompt glyphs a two-cell paint window

Nerd Font prompt icons and the multiline `➜`/`❯` rendered sliced off on
the right (#17). tty7 paints any single-column glyph clipped to exactly
one cell, but a symbol face routinely draws ink past that cell: a
non-Mono Nerd Font sets a one-cell *advance* on its icons yet fills up to
~1.9 cells (measured across Hasklug / Meslo / JetBrainsMono NF), and the
OS cascade serves a proportional `➜` the same way when nothing in the
font list covers it. The per-cell content mask then severed the overflow
— the incomplete icons and cut-off arrow in the report.

Advance is no signal here (it reads one cell for those overflowing
icons), so widen every solo glyph's clip to two cells. A glyph that
already fits is untouched — it has no ink to spill — while a symbol that
overflows renders whole, bleeding into a trailing blank the way iTerm2
and Terminal.app do with non-Mono faces; the two-cell bound keeps a
pathological face from smearing a lone glyph across the row. Batched
Run/Wide segments keep their exact column-span clip, and Mono Nerd Fonts
are pixel-identical. Pairs with the native powerline separators from #19.

Verified in-app on Apple Silicon with a non-Mono "Hasklug Nerd Font"
primary: icons and a powerline prompt that were severed now render whole,
with no change to ASCII, CJK, or the Mono-font path.

* test(terminal): cover both clip-width branches, not just the solo constant

Fold the solo/batched clip-width decision into one pure `seg_clip_width`
so the branch itself is testable (per PR review): the test now asserts a
batched Run/Wide segment clips to its exact column span while a solo
glyph gets the two-cell window, instead of only checking the standalone
constant. No behavior change.

---------

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 22:10:49 +08:00
l0ng-aiandl0ng-ai c6e071362a fix(menu): soften context-menu and dropdown row hover to match the palette (#24)
gpui-component's MenuItemElement highlights the hovered/selected row from
`tokens.accent` + `accent_foreground`. tty7's theme never overrode those, so
menu rows fell back to the stock saturated accent — a hard highlight out of
step with the app's soft mix-based palette.

Point the accent tokens (and the plain `accent`/`accent_foreground` fields the
input completion / code-action popovers read) at `list_active` (mix 0.17), the
same soft fill the command palette uses for its selected row, and keep the
hover text at `foreground` so it stays legible on the low-contrast fill. Right-
click menu, "+" dropdown and command palette now share one hover language.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 18:58:57 +08:00
l0ng-aiandl0ng-ai 86857f8cb2 fix(shell): keep zsh plugin managers out of the throwaway ZDOTDIR (#29)
Opening a new tab or split stalled for seconds while a zsh plugin manager
reinstalled itself from scratch. tty7 launches zsh through a per-pane throwaway
ZDOTDIR so it can layer its OSC 133 shell integration on top of the user's
config, but it left ZDOTDIR aimed at that empty temp dir for the whole shell
lifetime. Tools that resolve their own state via ${ZDOTDIR:-$HOME} — Zim
(.zimrc/.zim), oh-my-zsh, compinit's .zcompdump — therefore looked inside the
empty throwaway dir and rebuilt on every pane: Zim redownloads/recompiles every
module (the ~3s stall and "modules/…: Installed" spam of #15), compinit rewrites
its dump into the temp dir, etc.

Each redirector now stashes our dir, points ZDOTDIR at the user's real config
dir while their startup file runs, then restores ours so zsh still reaches the
next redirector; the integration body restores the real dir for the live
session via a one-shot precmd hook. When the user never set a ZDOTDIR we unset
it (rather than force $HOME) while sourcing, so their file sees exactly what a
real launch gives it and the `: ${ZDOTDIR:=~/.config/zsh}` relocate idiom still
fires. .zshenv recaptures a user-relocated ZDOTDIR so the classic tiny-~/.zshenv
layout keeps both its config and tty7's integration (previously it silently lost
the integration).

Verified end-to-end against real zsh for three layouts (config in $HOME,
unconditional relocate, and `-z`-guarded relocate): a second pane now hits the
plugin manager's cache instead of reinstalling, and runtime ZDOTDIR resolves to
the real dir. Adds three unit tests covering the redirector ordering, the
.zshenv recapture, and the runtime restore hook.

Fixes #15

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 18:52:35 +08:00
l0ng-ai 0ee67092d5 chore(release): v0.6.1
Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
v0.6.1
2026-07-08 17:43:37 +08:00
l0ng-ai c66a7fa014 fix(daemon): keep the local line editor out of remote ssh sessions (#28)
A remote shell that emits its own OSC 133 prompt marks — fish 4.x over
ssh, most visibly, which ships them on by default — made the daemon
report at_prompt=true, so tty7 engaged its local line editor and Tab ran
completion against the local filesystem instead of reaching the remote
shell.

The shell tty7 spawned only emits its OSC 133 marks while it is itself
the PTY's foreground process group (idle at its prompt). Reject a prompt
mark whenever a foreground command (ssh, a TUI, a nested shell) owns the
PTY — comparing the PTY's foreground pgid against the shell pid — so
keystrokes pass raw to whatever is really reading them. The normal local
case (shell idle ⇒ it is the foreground group) is untouched.

Fixes #26.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-08 17:41:22 +08:00
l0ng-ai bc3c1d599c chore(release): v0.6.0
Promote the [Unreleased] changelog to [0.6.0]: the "+" shell picker and the
Windows PowerShell 7 default, plus this cycle's fixes — native powerline
separators + bundled Hack fallback (#17), URL detection stopping at full-width
brackets (#20), PATH-probed shell detection for brew/nix installs (#18), and
stale-daemon restart-and-retry on upgrade (#22). Bump the crate version
0.5.0 → 0.6.0 so the built binary reports its real version to the update check.
v0.6.0
2026-07-08 13:36:56 +08:00
yanyaoerandyanyao 1c5a38652d fix(daemon): recover from stale shell daemon (#22)
Co-authored-by: yanyao <yanyao@xiaomi.com>
2026-07-08 13:30:47 +08:00
l0ng-aiandl0ng-ai f9b8f3b175 fix(shell): detect PATH-installed shells missing from /etc/shells (#23)
The Unix shell picker only enumerated $SHELL and /etc/shells, but
Homebrew and nix don't register what they install there (Homebrew only
suggests adding fish to /etc/shells, and few users do). A brew-installed
fish therefore never appeared in the "+" dropdown, even though the app
can spawn and shell-integrate it fine once selected.

Probe a curated set of well-known shells (fish, nu, pwsh, elvish, xonsh)
on PATH as a catch-all, after the /etc/shells entries. Registered shells
keep their /etc/shells paths via the existing basename dedupe; only the
unregistered leftovers are picked up from PATH. Uses the login-shell-
enriched PATH so Dock launches see Homebrew's prefix too.

Closes #18

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 13:28:13 +08:00
l0ng-aiandl0ng-ai 6e4492b983 fix(terminal): stop bare-URL detection at full-width brackets glued after the link (#20)
A URL followed by a full-width parenthetical with no ASCII space, e.g.
`…/pull/343(fix/… → dev)`, leaked the bracket text into the link: the
whitespace split ran past the URL and trailing-punctuation trimming only
touches the token's end. URL characters are all ASCII (RFC 3986), so after
locating the scheme, truncate at the first non-URL char — a CJK character,
full-width bracket, arrow or emoji — which marks where the link ends. Interior
ASCII parens (Wikipedia-style URLs) are preserved. Adds regression tests.

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 13:28:08 +08:00
l0ng-aiandl0ng-ai d39199987f fix(terminal): draw powerline separators natively, anchor bundled Hack in font fallbacks (#19)
Prompt themes (powerlevel10k, oh-my-posh, oh-my-zsh) render their
segment separators and status glyphs through whatever face the font
fallback chain resolves, and two failure modes followed (#17):

- Powerline separators (U+E0B0-E0BE) resolved from a fallback face
  render at *that face's* advance, not the cell width — narrow wedges
  that no longer meet their segment backgrounds, or tofu with no Nerd
  Font installed at all.
- A glyph nothing in the configured chain covers falls through to the
  OS cascade, which can serve a proportional glyph wider than the
  cell; paint_glyphs' per-cell clip then truncates it — the severed
  U+279C arrow in the report (pixel forensics: the cut is a zero-
  antialiasing vertical edge exactly one cell after the glyph origin).

Fix, the approach Warp (stretchable SVGs) and kitty (programmatic
glyphs) settled on:

- The eight solid powerline separators — sharp triangles E0B0/E0B2,
  round caps E0B4/E0B6, slants E0B8/BA/BC/BE — are now drawn as gpui
  fill paths sized to the exact cell: pixel-perfect for every
  font/size/line-height combination, no font dependency at all. The
  thin/outline variants stay on the font path (hairline strokes can't
  be a fill, and the bundled Hack covers the common ones).
- fallback_chain() appends the bundled "Hack" to every font fallback
  list, so prompt symbols (U+279C, U+276F, box drawing, the sharp
  wedges) resolve against a monospace-fitting face before ever
  reaching the OS cascade lottery.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF

Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
2026-07-08 13:27:59 +08:00
l0ng-ai bf9410d903 Merge pull request #14 from l0ng-ai/feat/macos-option-as-alt
feat(input): treat Option as Alt/Meta on macOS behind a setting
2026-07-08 10:52:50 +08:00
l0ng-ai 20b590310a Merge pull request #16 from l0ng-ai/feat/shell-picker
feat(shell): detect installed shells, pick one from the "+" dropdown
2026-07-08 10:52:24 +08:00
l0ng-ai 92ce3ca3c1 fix(shell): notify after the shell probe lands so the dropdown refreshes
The background detect_shells() update set detected_shells without
cx.notify(), so an idle window kept rendering the pre-probe (empty)
list until something else forced a redraw. Flagged by CodeRabbit.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-07 23:43:34 +08:00
l0ng-ai cdb383d6ce feat(shell): detect installed shells, pick one from the "+" dropdown
The "+" button now opens a shell picker listing every shell found on
this machine — the login shell plus /etc/shells on Unix; PowerShell 7,
Windows PowerShell, cmd, Git Bash and WSL distributions on Windows — so
opening a tab in a different shell no longer requires hand-editing
config.json. The default entry leads the menu (Cmd+T still opens a
default tab in one keystroke), and splits inherit the pane's shell.

The Windows default shell now prefers PowerShell 7 when installed,
probed the way Warp does it (Program Files x64/x86/ARM, the Microsoft
Store shim, scoop, dotnet tools, then PATH), falling back to the
Windows PowerShell that ships with the OS.

Spawn precedence in the daemon is now: explicit per-spawn pick >
config.json `shell` > platform default. Wire compat is preserved across
GUI/daemon version skew: a default spawn keeps the legacy SPAWN frame
byte-for-byte, so an old daemon still serves a new GUI; only an
explicit pick uses the new SPAWN_SHELL kind, locked by a round-trip +
legacy-frame test.

Claude-Session: https://claude.ai/code/session_01ABey161AUxhgmJC3PRoYtF
2026-07-07 23:31:54 +08:00