feat(terminal): replace ghostty-web with an in-repo libghostty-vt adapter

The terminal ran on the ghostty-web npm package plus a hand-written patch,
and every rendering bug (recycled rows, duplicated reflow fragments, prompt
artifacts) had to be worked around from outside. The emulator now is the
official libghostty-vt C ABI compiled to WebAssembly, driven by a browser
adapter ported from T3 Code (MIT, notice in LICENSE-T3CODE) and owned in
packages/ui/src/lib/ghostty. The artifact is reproducible with
scripts/build-libghostty-wasm.sh, including a workaround for Zig 0.15.2 on
macOS 27 SDKs.

On top of the port: one WASM instance per page with every tab kept mounted
and hidden tabs paused; history replayed at the PTY size it was drawn for;
shells spawned only after the first fitted grid so zsh never prints the
PROMPT_SP marker; box drawing, block elements and Powerline arrows drawn
procedurally to the exact cell so TUI borders and block logos have no gaps
between rows; a software-rasterized canvas so Gecko renders every tab's text
with the same smoothing; the symbols-only Nerd Font bundled instead of a CDN
fetch; touch selection and scrolling driven through the surface API; a copy
button in the tab strip for touch hosts; localized aria labels.

Testing: bun tests run the real WASM (reflow, palette, replay isolation,
recycled rows, box glyph geometry); viewport and view tests use a surface
double; verified in Chromium and Zen (windowed and headless) for crisp text,
new tabs, panel reopen, resize and box glyph rendering; package type-check,
oxlint/eslint on new files, web build.
This commit is contained in:
Bohdan Triapitsyn
2026-09-07 12:18:22 +03:00
parent 3132d1361a
commit 39fa8c1917
61 changed files with 6678 additions and 990 deletions
+4
View File
@@ -8,6 +8,9 @@ export const dict = {
...linearIssuePickerI18n.tr,
...linearPanelI18n.tr,
'terminalView.actions.attachSelection': 'Seçili çıktıyı ekle',
'terminalView.actions.copySelection': 'Seçili çıktıyı kopyala',
'terminalView.toast.selectionCopied': 'Çıktı kopyalandı',
'terminalView.toast.copyFailed': 'Kopyalama başarısız',
'terminalView.actions.restart': 'Terminali yeniden başlat',
'chat.message.terminalContext': '{terminal}, {start}-{end}. satırlar',
'chat.chatInput.terminalContext': '{terminal}, {start}-{end}. satırlar',
@@ -1809,6 +1812,7 @@ export const dict = {
'terminalView.tabs.closeTabTitle': 'Sekmeyi kapat',
'terminalView.tabs.newTabTitle': 'Yeni sekme',
'terminalView.viewport.inputAria': 'Terminal girişi',
'terminalView.viewport.scrollbarAria': 'Terminal geçmişi',
'directoryExplorerDialog.title': 'Proje dizini ekle',
'directoryExplorerDialog.description': 'Proje olarak eklemek için bir klasör seçin.',
'directoryExplorerDialog.toggle.showHidden': 'Gizli dosyaları göster',