* feat: auto-poll Docker capabilities while Docker tab is active
When the Docker tab is visible and hasDocker is not yet true,
poll refreshCapabilities() at the process refresh interval.
Stop polling once hasDocker becomes true, or when switching
to a different tab.
* fix: use resolvedTab instead of activeTab for Docker auto-poll condition
The auto-poll useEffect condition used activeTab, which stays stale
when Docker becomes unavailable. Changed to resolvedTab which reflects
the actual displayed tab. Also updated the dep array.
* fix: replace setInterval with setTimeout recursion in Docker tab probe
Replace setInterval-based polling with setTimeout recursion in the Docker
tab capability probe effect. This ensures the next probe only starts after
the previous one finishes, avoiding overlapping probes when SSH timeout
exceeds the polling interval.
- Add dockerPollTimerRef to track the timeout handle
- Use async pollOnce() that awaits refreshCapabilities() before scheduling next
- Use cancelled flag in cleanup to prevent scheduling after unmount
- Keep same dependency array for correctness
* fix: stabilize docker poll timer by using useRef for refreshCapabilities
refreshCapabilities() can return a new reference on every render, causing
the useEffect to re-run on every render — cleanup cancels the polling timer,
then the effect immediately calls pollOnce(), effectively bypassing the
configured timeout interval.
Fix: store refreshCapabilities in a useRef (refreshRef), use
refreshRef.current() inside pollOnce(), and replace refreshCapabilities
with refreshRef in the useEffect dependency array.
Closes #PR1456 Codex P2 review item.
* fix: delay auto-poll first probe by one interval to avoid overlap with tab-switch probe
When switching to the Docker tab, two mechanisms were triggering probes:
1. tab-switch effect (line 67-76): immediate probe via refreshCapabilities()
2. auto-poll effect: pollOnce() executing immediately on mount
This caused duplicate probes that waste SSH channel resources.
Fix: pollOnce() no longer fires on mount. Instead, the effect schedules the
first probe with setTimeout(pollOnce, capabilitiesTtlMs), so the first probe
happens after one full interval. Subsequent probes continue at interval pace
via the setTimeout recursion in pollOnce itself.
The tab-switch effect still fires the immediate probe (the correct one),
so responsiveness on tab switch is preserved.
* fix: reset cancelledRef in effect body to prevent permanent stalling of Docker polling
The cancelledRef was set to true in the cleanup function when dependencies
changed, but never reset when the effect re-ran. This caused pollOnce to
always early-return on subsequent timer ticks, permanently halting
Docker capability probing after the first dependency change.
* fix(system-manager): replace cancelledRef with closure variables for per-effect cancellation
Each effect generation now has its own and closure
variables instead of shared / . This
prevents stale probes from surviving cleanup when the panel hides and
re-shows (Codex P2 review).
* fix: wrap refreshCapabilities in try/catch to keep polling on exception
If refreshCapabilities throws (instead of returning {success: false}),
the await would exit pollOnce without scheduling the next setTimeout,
silently killing Docker auto-detection polling.
* fix: add in-flight probe guard to prevent tab-switch and auto-poll concurrent probes
Add probingRef to track whether a capabilities probe is already in-flight.
- Tab-switch effect for Docker branch checks probingRef before starting a new probe
- Auto-poll pollOnce checks probingRef at entry and sets/clears it around the actual probe
- Tmux branch left unchanged as it has no auto-poll overlap risk
* fix: re-schedule next poll timer when probe is in-flight
When probingRef.current is true (tab-switch probe still running),
pollOnce was returning early without scheduling the next timer,
causing auto-poll to stop permanently afterward.
Now it schedules the next poll within the interval and returns,
so the polling loop keeps running until a slot where no probe is
active.
* fix: convert comments to ASCII-only English
- Line 105: translate Chinese comment to 'probe is in-flight, reschedule for next cycle'
- Line 113: replace em dash (U+2014) with ASCII dash
* feat: session inline rename, closeSession shortcut, pane zoom
* fix: sidebar inline rename with local state
* fix: add sessionDisplayName to terminalPropsAreEqual comparator
The Terminal component is wrapped with React.memo(…, terminalPropsAreEqual),
but the comparator was missing a check for sessionDisplayName. After renaming
a session, the pane title bar would show the old name until some other prop
changed and triggered a re-render.
Add prev.sessionDisplayName === next.sessionDisplayName to the comparator
so that display name changes cause the Terminal to re-render immediately.
* fix: add onStartSessionRename to TerminalLayerWorkspaceSection ctx destructuring and TerminalPanesHost props
* fix: add toggleWorkspaceViewMode to executeHotkeyActionImpl destructuring
The togglePaneZoom handler calls toggleWorkspaceViewMode() but it
wasn't destructured from getCtx(), causing a ReferenceError at runtime.
* fix: restore truncated ctx object in TerminalView render call
The TerminalView ctx object literal on line 1265 was truncated to
'showSele...' due to an editing tool truncation bug, causing
Parsing error: ',' expected on npm run lint / tsc --noEmit.
Restored the missing fields from the base commit:
showSelectionAIAction, snippets, status, statusDotTone, sudoHintRef,
sudoHintText: t("terminal.sudoHint.pressEnter"), t, termRef,
terminalBackend, terminalContextActions, terminalCwdTracker,
terminalPreviewVars, terminalSettings, timeLeft, toast, zmodem
Kept the PR's new additions (isVisible, onRename, sessionDisplayName)
intact.
* fix: add toggleWorkspaceViewMode to executeHotkeyAction context and add terminal.menu.rename translations
- Add toggleWorkspaceViewMode to the context getter in executeHotkeyAction (App.tsx)
- Add terminal.menu.rename translation for en (Rename), zh-CN (重命名), ru (Переименовать)
* fix: validate focusedSessionId before closing in closeSession hotkey
When closeSession hotkey fires, workspace.focusedSessionId may reference
a session that was already closed by another trigger (e.g., mouse click
on tab close button). Collect alive session IDs from the workspace root
and fall back to the first living pane if the stored focusedSessionId
is stale.
* fix(auto-poll): check useSessionCapabilities probing state in pollOnce
When auto-poll timer fires before the initial probe (from
useSessionCapabilities) completes, probingRef.current is still false
because the initial probe doesn't set it — causing a second overlapping
probe.
Add check so that any in-flight probe from any path
(initial/auto-poll/tab-switch) prevents auto-poll overlap.
PR #1459
* fix: address remaining Codex review issues
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: add detach session from workspace with toolbar button and context menu
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: use customName in pane header display name for renamed sessions
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: refine workspace terminal detach interactions
* fix: preserve workspace detach tab ordering
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds a Settings > AI switch to hide the automatic Add to Conversation button on terminal selection while keeping the context menu action available.
Closes#1397
- Remove outdated SFTP upload message and replace it with ZMODEM-specific messages in English, Russian, and Chinese locales.
- Add a new function to handle ZMODEM drag-and-drop uploads in the terminal backend.
- Update terminal components to support ZMODEM drag-and-drop functionality.
- Enhance error handling for file uploads and provide user feedback for no files to upload.
- Introduce tests to verify ZMODEM upload behavior and fallback to SFTP for network devices.
Hide timestamp controls in popup shell windows and keep the terminal host sidebar width while root pages are shown so returning to terminal tabs does not replay the open animation.
Expose a quick Activity icon on SSH Linux sessions so users can jump directly to the system manager side panel from the terminal header.
Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce workspace-aware System side panel with remote process/tmux/Docker management, terminal popup for interactive attach, capability warmup, review-hardened IPC, performance optimizations, toast action errors, and SSH channel recovery on reconnect.
* feat(terminal): add remote command history side panel
Read remote shell history over SSH/ET/Mosh exec channels, browse it in a virtualized side panel with search, paste, and save-as-snippet actions. Closes#1381.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): expand command detail inline below selected row
Move the detail strip from a fixed slot above the list into the row
immediately below the clicked entry so expansion reads top-to-bottom.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): filter Netcatty AI PTY commands from remote history
Drop shell history lines containing the __NCMCP_ marker so AI exec noise
does not clutter the command history panel.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): tighten detail strip and add run action
Size the expanded row to its content, add a run-in-terminal button, and
use clearer snippet icon/tooltip for save-as-snippet.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): address review findings before merge
Key cache by host+session, retry Mosh pending reads, and clamp virtual
list scroll position when filtered items shrink.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Restore resize when macOS App Nap or GPU eviction leaves xterm stale after
switching away, by refitting on visibility/focus/fullscreen and fixing the
ResizeObserver race with async xterm boot.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the compose bar inside the terminal workspace so SFTP side panels stay full height, refit xterm when the bar toggles, and remeasure split-pane geometry when side panels open or close.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(terminal): resolve bold font weight without document.fonts.check false positives
Chromium reports unavailable bold weights as available, so xterm tried to rasterize weight 700 while the bundled JetBrains Mono fallback only ships 400/500/600. Bold glyphs then rendered as black blocks on Linux local terminals (fixes#1364).
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: drop unused primaryFontFamily from terminal effects context
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix terminal drag-and-drop uploads so they target the active terminal cwd and avoid fallback home/login-shell cwd when the active cwd cannot be confirmed.
When pasting (Ctrl+V / right-click paste) in a local terminal,
if the clipboard contains files, insert their paths instead of
doing nothing.
- New hook useTerminalFilePaste: capture-phase paste listener
on terminal container, reads clipboard files via Electron bridge,
formats paths (spaces quoted, deduped), writes to session
- Updated useTerminalContextActions: right-click paste checks
clipboard files first, falls back to text paste
- New terminalHelpers.extractRootPathsFromClipboardFiles
- Tests: 9 unit tests for path extraction logic
- Verified via headless Chromium integration test (15 tests)
- Build: npm run build ✅, npm test ✅ (1899 pass)
* perf(terminal): smooth layout drags and faster tab switching
Defer xterm refit during split, sidebar, and host-tree drags while keeping pane containers in sync with live layout measurements. Refactor TerminalLayer into focused sections with TabBridge/memo optimizations and add the terminal host tree sidebar.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(terminal): keep side panels alive and guard session attach races
Prevent terminal boot unmount from leaking backend sessions, keep SFTP/scripts/theme/AI state when switching side tabs, and defer heavy SFTP UI mount so first entry stays responsive.
Co-authored-by: Cursor <cursoragent@cursor.com>
* perf(terminal): reduce tab switch jank
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Rework sudo password autofill from auto-fill to a Tabby-style hint + Enter to confirm. When a sudo command is armed and a password prompt appears, show a dimmed inline hint instead of sending the password; Enter pastes the saved password and submits, any other key dismisses it.
Confirmation removes the credential-leak class (nothing is sent without the user pressing Enter at a visible hint), so detection is relaxed to a broad match (Ubuntu/PAM bare "Password:", "[sudo] password for…", localized prompts) and the per-host toggle is removed — always available when the host has a saved password.
Safety guards:
- don't arm when the hint can't render (no overlay) so Enter isn't silently intercepted;
- swallow Escape/Backspace so the byte never reaches the no-echo prompt;
- clear the pending hint once output moves past the prompt (sudo timeout/failure/returns to shell) so a later Enter can't leak the password to the shell.
Implementation ~140 lines; full suite green; manually verified on a real Linux host.
Replace the command-rewriting scheme (inject sudo -p marker, sanitize the echo, Ctrl-U retype) with passive observation: arm a short window on a sudo command and fill the password when a real sudo prompt appears.
- Fixes the Ubuntu/PAM no-fill, the cursor jump below the prompt, and the typed-vs-autocomplete discrepancy from #1281.
- Detection requires the [sudo] tag, or a whole-line bare "Password:" / "密码:"; prefixed prompts (mysql -p "Enter password:", ssh "x@h's password:", psql "Password for user x:") are rejected so the sudo password can't leak to a child program when sudo's creds are warm.
- Disarms when a non-sudo command follows, so a stale window can't fill a later prompt.
Implementation: 322 -> ~140 lines.
Duplicating an SSH tab ("Copy Tab") used to open a brand-new SSH
connection, forcing a second MFA prompt on hosts with multi-factor auth.
Like Tabby's session multiplexing, open a new shell *channel* on the
source tab's already-authenticated connection instead, so the duplicate
reuses the existing transport and skips key exchange + authentication.
- copySession records the source session id (reuseConnectionFromSessionId)
for connected, non-mosh SSH sessions; it is threaded down to the SSH
bridge as options.sourceSessionId.
- startSession.cjs gains a reuse path that opens conn.shell() on the
source connection. The shell wiring is extracted into a shared
setupShellSession helper used by both the fresh and reuse paths.
- Connection lifecycle is reference-counted via a new sshConnectionPool
module (mirrors Tabby ref/unref/destroy): the shared transport + jump
host chain are torn down only when the last channel closes, so closing
a copy — or the original while a copy is open — never kills siblings.
terminalBridge.closeSession routes SSH teardown through the same release.
- Reuse falls back to a fresh connection when the source is gone, and is
skipped for X11 hosts (X11 is negotiated per channel).
Tests: sshConnectionPool refcount unit tests, bridge reuse integration
tests (reuse vs fresh, sibling survival, X11 skip, fallback), and
terminalBridge close lifecycle tests.
Refs #1204
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ET configuration section in HostDetailsAdvancedSections (etPort,
etTerminalPath). Add ET option to ProtocolSelectDialog. Wire ET session
creation in createTerminalSessionStarters with proxy and multi-jump
validation. Add ET badges and labels in Terminal, TerminalToolbar,
TerminalConnectionDialog, and TerminalLayerSupport. Propagate ET
settings through GroupDetailsPanel, GroupSshSettingsSection, and
VaultView.
* fix(terminal): use client OS for local-shell autocomplete clear sequence (#1112)
Synthetic fallback Host in TerminalLayer hardcoded os: 'linux', so the
'host.os || navigator.platform check' expression in Terminal.tsx never
ran the client-OS detection branch for local shells. Result: autocomplete
emitted Ctrl-U (\x15) for line-clear on Windows local PowerShell/cmd,
where it's rendered literally as ^U instead of erasing the input — the
popup live-preview, fuzzy-accept, and snippet-accept paths all leak it.
Fix both ends: Terminal.tsx prefers client OS detection for local
protocol via the existing detectLocalOs helper; TerminalLayer.tsx
populates the synthetic host's os field from detectLocalOs too, so
other host.os readers (notably the server-stats gate in
Terminal.tsx:642) also stop treating local Windows as Linux.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(terminal): keep non-local fallback host on 'linux'
The TerminalLayer fallback path also triggers for unsaved serial sessions
and orphaned remote sessions (where the saved host was deleted while the
session lived on). Terminal.tsx trusts host.os for non-local protocols,
so tagging the fallback with the Windows client OS would push POSIX
remote/serial shells onto the Windows autocomplete/path-completion
branch. Gate the client-OS detection on protocol === 'local'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(autocomplete): add snippet completion source
* feat(autocomplete): merge snippets at the command position
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(autocomplete): place snippet tests where the test runner finds them
The npm test glob covers components/terminal/*.test.ts but not the
autocomplete/ subdirectory, so the snippet tests added in the previous two
commits weren't actually running in the suite. Move them up to
components/terminal/ (the existing convention for autocomplete tests) with
corrected import paths; the engine snippet cases go in a separate
completionEngineSnippets.test.ts to avoid colliding with the existing
completionEngine.test.ts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(autocomplete): snippet ghost-exclusion, preview skip, and accept path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(autocomplete): wire snippets into the terminal + preview snippet command
* fix(autocomplete): show only label in snippet popup row; keep snippet over colliding history
The popup row for a snippet now omits the inline command echo — the full
command lives in the detail preview only, matching the "label-only row"
design. The completion engine pushes snippet suggestions without the early
seen-text skip so that when a snippet's label collides with a history
entry's text, the higher-scored snippet survives the final dedup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(autocomplete): broadcast snippet command so popup acceptance mirrors peers
In broadcast mode, accepting a snippet from the autocomplete popup cleared
peer input (the line-clear keystrokes flow through the broadcast-aware path)
but never sent the command, since executeSnippetCommand wrote only to the
active session. Broadcast the normalized snippet data (matching the snippet
shortkey path) so peers receive both the clear and the command, keeping all
sessions in sync.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(autocomplete): broadcast wrapped snippet bytes to preserve noAutoRun
Broadcasting the raw normalized command sent un-wrapped newlines to peers,
so a multi-line noAutoRun snippet was pasted-but-not-run on the active
session yet executed line-by-line on broadcast peers (handleBroadcastInput
writes bytes directly without re-wrapping). Broadcast the exact bytes the
active session receives instead — bracketed-paste wrapping plus the auto-run
\r — so peers mirror the active session and noAutoRun is preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The autocomplete hook (useState) lived in Terminal, so every suggestion / selection / live-preview update re-rendered the whole ~2775-line Terminal component. Move the hook and its popup into a dedicated <TerminalAutocomplete> component so those frequent state updates re-render only that small subtree.
The hook's handlers are surfaced back to Terminal via refs (the same refs already used to wire the xterm runtime), and the component is mounted unconditionally so the hook keeps recording command history and intercepting completion keys for the session's lifetime. No behavior change intended.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* perf(terminal): bound the connection log with a chunk ring buffer
The connection log kept the last 1,000,000 chars via `log += chunk; log = log.slice(-MAX)`. Once a session emits more than that, the slice flattens a ~1M-char string on every subsequent output chunk — on the render thread, for each echoed keystroke included — on long/busy sessions.
Replace the string with a small chunk-queue ring buffer that trims only the boundary chunk (amortized O(chunk) append) and materializes the full string once on read. Behavior is unchanged: it still retains exactly the last MAX_CONNECTION_LOG_DATA_CHARS characters.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* perf(terminal): coalesce connection log into bounded blocks (O(1) trim)
The first cut used one array entry per append and trimmed with chunks.shift(). For interactive output (many tiny chunks) the array grows toward the cap in entries, so once full, shift() reindexes ~N elements on every append — O(appends) per chunk, no better than the slice it replaced.
Coalesce appends into a small, bounded set of fixed-size blocks (~maxChars/blockSize). New data fills an open tail that seals into a block at blockSize; trimming only drops/slices a handful of blocks. Adds segmentCount() and a test asserting the segment count stays bounded across many tiny appends.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
"Use Option as Meta key" was read into `altIsMeta` but only applied to the
mouse alt-click options (`altClickMovesCursor`). xterm.js's `macOptionIsMeta`
— the option that actually makes Option emit ESC-prefixed (Meta) sequences —
was never set, so on macOS Option kept producing layout characters (ƒ, ∫, …)
and readline/zle word shortcuts (Alt+f, Alt+b, Alt+Backspace) were dead.
Extract the altAsMeta→xterm mapping into one tested helper used by both the
terminal init path (createXTermRuntime) and the live settings sync
(Terminal.tsx) so the two can't drift again.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat #1064: add buildUploadPlan for rz overwrite/skip/cancel resolution
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat #1064: handle remote filename conflicts in rz handleUpload
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat #1064: SSH exec probe + remove for rz upload conflicts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat #1064: IPC for rz overwrite-conflict prompt
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat #1064: renderer prompt for rz overwrite conflicts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix#1064: repair sshBridge test mock (ipcMain.on) and i18n the overwrite dialog
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix#1064: make upload plan index-based to preserve per-file decisions
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hidden tabs stay mounted off-screen (visibility:hidden) so each keeps a
live WebGL context. Creating another terminal's WebGL context — or the GPU
dropping a non-composited off-screen canvas — leaves the hidden terminals'
drawing buffers corrupted ("花屏"). This reproduces on both Windows and
macOS: opening 2 tabs garbles the 1st, opening 3 garbles the 1st and 2nd,
while the just-created (visible) one is always fine. The DOM renderer is
immune because it uses real DOM nodes.
A window resize recovers the display because it triggers a full repaint
(clearTextureAtlas + RenderService._renderRows). A tab switch did not:
the visibility effect only calls safeFit, which early-returns when the
pane's dimensions are unchanged, so no redraw happened.
Perform the same recovery a resize does when a tab becomes visible:
clear the texture atlas (no-op on the DOM renderer) and synchronously
repaint every row. Verified against xterm core that _renderRows draws
unconditionally, independent of dimension changes or dirty-row tracking.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Heavy full-screen TUIs (claude code / gemini cli / opencode), font changes,
and device pixel ratio changes can leave xterm.js's WebGL glyph texture atlas
in a corrupted state that persists for the life of the terminal — users see
persistent "garbled / 花屏" output that only clears when a brand-new terminal
is opened (most often on Windows with display scaling / multi-monitor setups).
Clear the texture atlas so glyphs re-rasterize at the correct scale instead of
forcing users to reopen the terminal:
- Add watchDevicePixelRatio() helper (TDD, unit-tested) that re-registers a
matchMedia listener across DPI changes and fires a repair callback.
- Wire it into createXTermRuntime: on devicePixelRatio change, clear the atlas
and refit; also clear the atlas on reflow (term.onResize). Watcher is torn
down on dispose.
- Expose clearTextureAtlas() on XTermRuntime and call it after font changes in
Terminal.tsx (xterm.js #3280). All calls are no-ops under the DOM renderer.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(terminal): separate prompt after unterminated command output
Add a display-layer prompt line break handler so recognized shell prompts move to the next visual line when the final command output line is not newline terminated.
Also add a terminal setting to toggle the behavior, sync support, i18n copy, and focused tests for prompt insertion.
* fix review issue
* Fix prompt cache initialization
* Serialize terminal output writes for prompt breaks
* Keep terminal status lines ordered with output
* Fix prompt arming without command callback
* Keep prompt display breaks out of session logs
* Avoid prompt breaks for output suffix matches
---------
Co-authored-by: yuzifu <yuzifu@TB16PGen5.Info>
Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
The copy-host-address, broadcast and focus-mode buttons sit on the
per-host statusbar directly under the top tab bar. With the default
top-side tooltip placement, hovering any of them paints the tooltip
on top of the tab title above (the visible "Copy host address …"
covering "Rainyun-114.66.26.174" in the bug report screenshot).
Drop the tooltips on the bottom side instead, matching the
HoverCardContent panels already used for the CPU/Memory/Disk stats
buttons on the same bar.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix#954: unify Tooltip styling + replace native selects
Replace native HTML title= tooltips and native <select> dropdowns
with the existing Radix-based Tooltip / Select components so they
share the app's rounded styling, theme tokens and i18n pipeline.
Adds a global TooltipProvider in AppWithProviders so every
descendant Tooltip works without a per-file Provider wrapper.
Scope (driven by the issue #954 examples and "全部都处理" follow-up):
- TerminalLayer toolbar: Add Terminal / Split View / SFTP / Scripts
/ Theme / AI Chat / Move panel / Close panel.
- TopTabs middle bar: quick switcher, more tabs, AI assistant, theme
toggle, settings; window-control buttons (min/max/close), tray
close and hotkey reset/disable have their native title dropped per
the user's explicit opt-out ("可以不用Tooltip,直接全局禁用
原生title 属性").
- AI panels: AIChatSidePanel session history / new chat / delete,
ConversationExport, AgentSelector, ChatInput attach / expand /
permission, ModelSelector, ProviderCard, ai-elements/tool-call.
- SFTP: SftpSidePanel header, SftpBreadcrumb, SftpFileRow,
SftpPaneToolbar, SftpTabBar, SftpTransferQueue.
- Settings: SettingsPage close, SettingsAppearanceTab theme/accent
swatches, SettingsFileAssociationsTab edit/remove, SettingsSystemTab
crash-log paths and global hotkey reset.
- Host vault: HostDetailsPanel (clear / suggestions / show-password /
key path / browse key), GroupDetailsPanel, KnownHostsManager,
ConnectionLogsManager, KeychainManager, SyncStatusButton,
CloudSyncSettings, LogView, QuickSwitcher, ScriptsSidePanel,
Terminal status bar copy-host + broadcast/focus, ZmodemProgressIndicator.
- Terminal subcomponents: HostKeywordHighlightPopover, TerminalComposeBar,
TerminalConnectionDialog, TerminalSearchBar.
- Editor: TextEditorPane (subtitle, search, wrap, promote-to-tab).
- TrayPanel session rows and port-forwarding rows.
Native <select> migrated to custom Select component:
- SerialConnectModal (data bits, stop bits, parity, flow control)
- SerialHostDetailsPanel (same four fields)
- HostDetailsPanel backspace behavior
- GroupDetailsPanel backspace behavior
- SettingsTerminalTab local shell picker
- terminal/ThemeSidePanel font weight
Hardcoded English strings extracted to i18n. New keys for both
en and zh-CN: terminal.layer.*, topTabs.*, ai.chat.* (sessionHistory,
attach, collapse, expand, enableAgent), zmodem.*, settings.shortcuts.
resetToDefault. Inline help text on SnippetsManager package-name input
removed because the same hint is already shown in a visible <p> below
the input.
Existing per-file <TooltipProvider> wrappers (SnippetsManager,
ScriptsSidePanel, SelectHostPanel, RuleCard, HostDetailsPanel proxy
section) are left in place — they nest harmlessly under the global
provider and stay self-sufficient for component tests.
Tests:
- tsc clean for changed files (pre-existing repo-wide errors
unrelated to this PR).
- All 802 tests pass (3 skipped pre-existing).
- HostDetailsPanel.proxyProfile.test and TextEditorPane.test
updated to wrap with TooltipProvider, matching the runtime
context now needed by the migrated components.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix#954: wrap Settings + Tray windows with TooltipProvider
Settings and the tray panel mount as separate Electron windows with
their own React root in index.tsx, so they do not inherit the global
TooltipProvider added under AppWithProviders. After the unified
Tooltip migration, any settings tab that used a Tooltip (Appearance,
Application, FileAssociations, System, Shortcuts, Terminal, AI
ProviderCard, AI ModelSelector) — and TrayPanel — threw
"Tooltip must be used within TooltipProvider" and rendered nothing.
Wrap both branches with TooltipProvider at the same level as
ToastProvider in index.tsx.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>