Commit Graph
176 Commits
Author SHA1 Message Date
bincxz 2e51ff8b54 fix: route reassigned shortcut from editor 2026-08-23 23:11:59 +08:00
bincxz 88387b9c2b fix: respect close-tab binding in editor 2026-08-23 22:56:36 +08:00
bincxz 4ff20453f2 fix: preserve Cmd+W focus and settings behavior 2026-08-23 22:51:42 +08:00
bincxz 13b1bf506e fix: honor disabled close-tab shortcut on macOS 2026-08-23 22:42:29 +08:00
bincxz 8191f0b67b fix: keep focus inside the magnified pane 2026-08-21 17:23:44 +08:00
bincxz 2897a18f1e feat: magnify the active split pane 2026-08-21 17:15:25 +08:00
bincxz 95dd8b62c4 revert: replace pane focus implementation 2026-08-21 16:25:24 +08:00
bincxz e70f60f270 fix: complete pane focus interactions 2026-08-20 17:44:17 +08:00
bincxz 5db74fbb2d feat: add focus mode for split panes 2026-08-20 17:11:56 +08:00
bincxz e6c46f8198 fix(app-lock): defer protected actions until unlock 2026-08-18 20:45:29 +08:00
bincxz 0ac30b5049 fix(app-lock): harden lock boundaries 2026-08-18 17:24:46 +08:00
bincxzandCursor d422a27715 Merge origin/main into feature/app-lock-screen
Reconcile the app-lock feature with main's refactored renderer
architecture (publishers/hosts/AppShell) and main-process changes:

- index.tsx/App.tsx: AppLockGate keeps ownership of useSettingsState and
  useAppLockState; new AppLockRuntimePublisher publishes the runtime via
  appRuntimeBridge; SettingsPublisher now binds the gate-owned settings.
- AppSideEffects: defer deep links, tray port-forward toggles, global
  hotkeys and window close commands while locked; flush on unlock.
- main.cjs: keep fresh-session re-lock before main's reusable-window
  fast path; before-quit guard delegates to handleBeforeQuit with main's
  async plugin-shutdown commitQuit and pending-update cancellation.
- globalShortcutBridge: keep locked-state tray redaction on top of
  main's deferred tray panel show logic.
- preload: adopt main's lastPayload replay for terminal popup config
  (superset of the gate-delay replay fix).
- vite config: keep dev CSP plugin, adopt main's warn-only stale
  optimize-dep handling; electron-builder: keep Windows Hello helper
  extraResource with main's CLI-controlled arch targets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 16:30:30 +08:00
netcatty-bot bbc1a08848 fix: address Codex review on PR #2965 2026-08-13 09:40:27 +00:00
netcatty-bot e51d223a0e fix(#2963): automated Cursor CLI fix 2026-08-13 09:16:11 +00:00
陈大猫 e38b372d1f Merge pull request #2897 from yintian710/main
feat(vault): 添加动态组路径支持用于自动化脚本
2026-08-12 22:24:12 +08:00
yintian 27dc1c62af feat(vault): 添加组变更提交功能以支持原子操作
- 在多个组件中引入 commitVaultGroupMutation 函数
- 更新 useHostTreeInlineGroupActions 钩子以使用新的提交机制
- 修改 vault 组件以传递新的组变更提交函数
- 实现组重命名和删除操作的统一提交逻辑
- 添加相关测试用例验证组变更提交功能
- 更新类型定义以包含新的组变更状态管理接口
2026-08-12 20:37:10 +08:00
bincxz e2ad0f526e fix(workspace): complete host drop paths 2026-08-12 11:44:35 +08:00
netcatty-bot 10c399f47b fix(#2916): automated Cursor CLI fix 2026-08-12 02:13:50 +00:00
陈大猫 8e53ec829f Merge pull request #2906 from binaricat/codex/fix-issue-2865-terminal-font-shortcuts
[Bug] Keep Ctrl+=/-/0 scoped to terminal font size
2026-08-12 00:32:02 +08:00
bincxz df045bf144 Revert "fix: address Codex review on PR #2906"
This reverts commit a1b7f3708a.
2026-08-11 23:57:57 +08:00
bincxz 195a23e380 Revert "fix: honor configured terminal shortcuts"
This reverts commit a5bf1765d4.
2026-08-11 23:57:57 +08:00
bincxz a5bf1765d4 fix: honor configured terminal shortcuts 2026-08-11 23:03:14 +08:00
netcatty-bot a1b7f3708a fix: address Codex review on PR #2906 2026-08-11 14:47:01 +00:00
bincxz 105107755b fix: route terminal font shortcuts by focus 2026-08-11 22:14:06 +08:00
netcatty-bot 57e11822f2 fix: address Codex review on PR #2904 2026-08-11 12:39:55 +00:00
bincxz a2ff82187b fix(tabs): follow custom shortcut bindings for previews 2026-08-11 19:42:10 +08:00
netcatty-bot b89607dbb0 fix(#2898): automated Cursor CLI fix 2026-08-11 09:55:39 +00:00
netcatty-bot b9702b043a fix(#2886): automated Cursor CLI fix 2026-08-11 06:27:49 +00:00
5dcba28621 feat(scripts): add expand/collapse all and delete-selected toolbar actions (#2828)
* fix(#2826): automated Cursor CLI fix

* fix(scripts): confirm bulk delete and clear host bindings

Scripts side-panel mass-delete skipped confirmation and, when
onSnippetsChange was wired, filtered snippets without clearing host
login/connect script bindings. Confirm first, always dispatch the shared
delete event, and apply deletes via deleteSelectedSnippetsFromVault.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(scripts): harden delete handler against stale vault state

Read snippets/hosts from refs in the shared delete listener so rapid
confirms cannot resurrect already-removed items, and clear pending bulk
delete when the scripts panel hides so the confirm dialog does not
resurface after a mid-prompt hide.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(scripts): keep delete refs current within the same tick

Update snippetsRef/hostsRef immediately after applying a delete so a
same-tick second event cannot rebuild vault state from the pre-delete
snapshot and resurrect removed snippets or host bindings.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: address Codex review on PR #2828

* fix(scripts): hoist bulk-delete confirm outside toolbar popover

Portalled Dialog focus dismissed the compact Scripts popover and cleared
pendingDeleteIds before confirm. Own the confirm in TerminalToolbar and
defer via onBulkDeleteRequest when nested in that popover.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: serialize snippet saves with bulk-delete vault lock

updateSnippets wrote STORAGE_KEY_SNIPPETS without withVaultImportLock, so a
concurrent edit could land between bulk-delete's snapshot read and journal
commit and be discarded. Queue ordinary snippet writes on the shared lock and
include them in waitForPendingVaultWrites.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: rebase queued snippet saves onto latest vault snapshot

Locking updateSnippets alone still wrote a pre-delete full array, so a
popup bulk-delete could be resurrected (or a storage-event version bump
could cancel the local edit). Rebase under the vault lock and supersede
only newer local writers via snippetsWriteOwnerRef.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

* fix: address Codex review on PR #2828

---------

Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-08-10 10:20:55 +08:00
3490b0673d fix: StrictMode 双发清理 + 笔记页卡顿优化 (#2810)
* Cursor: Apply local changes for cloud agent

* fix(terminal): stop first SSH fail under StrictMode remount

Defer backend boot past the double-invoke, cancel superseded vault init,
gate start on vault hydration, and harden hotkey/attach cleanup so the
first connect no longer races a stale close or empty keychain.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* perf: finish StrictMode mega-subscriber plan (accent isolation + docs)

Stabilize published terminal themes across custom-accent drag, apply accent
at chrome/log leaves and CSS injection only, land Phase 0 design/plan docs,
and lock Host-bag / Bugbot-clean contracts for the isolation checklist.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* docs: add StrictMode mega-subscriber design and plan artifacts

Force-add Phase 0 spec/plan under docs/superpowers (gitignored locally)
so the delivery branch carries the approved design and plan copy.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: clear remaining StrictMode doubles and notes editor jank

Gate every AI send and reject concurrent runTurn, latch tray connect flush
and settings persistMounted remounts, and stop notes content drafts from
rebuilding MDXEditor/shell on each keystroke.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* perf: gate hidden notes panel subscribe and skip host-link annotate on keystrokes

Hidden terminal notes mounts keep a live snapshot read but stop listening to
publishes. Host-link DOM annotation follows hosts/mode (preview MutationObserver)
instead of every markdown value change.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: latch SSH transport idle TTL notify under StrictMode

Avoid duplicate main-process notifySettingsChanged on effect double-invoke
when the idle TTL value has not changed.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* perf: freeze inactive vault notes against hosts churn and prefetch MDXEditor

Gate the retained VaultNotesSection notesStore subscription while hidden,
ignore hosts identity in its memo compare when inactive, and warm the lazy
InlineMarkdownEditor chunk when notes become active.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: clear remaining AI StrictMode double-fire hazards

Latch Ask-AI terminal selection on requestId before draft mutation, hoist
Codex App Server IPC bridge to App (ref-counted), and make Catty
requestApproval join an existing waiter for the same toolCallId.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: remount-safe AI send latch and skip stale MCP approval IPC

Replace the component sendInFlightRef with a module key latch so StrictMode
panel remount cannot reopen a mid-flight send, and no-op resolveApproval when
the pending entry is already drained.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* test: allow gated useNotesStore in VaultNotesSection memo contract

The retained notes section now passes enabled:isActive so hidden mounts do
not subscribe; update the VaultView memo source assertion accordingly.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: keep note drafts across mode toggle and external note publishes

Flush ref-only content before edit/preview remount, and skip prop→editor
setMarkdown while a local draft diverges from the last synced value.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* chore: strip StrictMode sermon comments after Bugbot/Comment Sicko pass

Keep effect-order and public-export contracts; drop remount/perf narration
that the latches and tests already encode.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: remount-safe Ask-AI terminal selection latch

Move consumed requestIds to a module Set so StrictMode remount cannot
append the same selection chip twice before parent state clears.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* chore: drop remount latch comment

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-08-07 14:38:24 +00:00
陈大猫andCursor 70607cb26d perf: StrictMode + Host-island mega-subscriber elimination (#2809)
Split vault/session/settings into publishers and store-backed Hosts so hot
fields no longer rebuild the whole shell, and harden Terminal boot / startup
effects for StrictMode double-mounts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 14:37:37 +00:00
cc5ad3be16 perf: isolate Terminal hot props, cut memory/jank, and repay architecture debt (#2790)
* refactor: migrate architecture-debt hooks into application/state

Move SFTP Wave A stores/hooks and related vault/AI/terminal/systemManager
hooks out of components into application/state, update importers, and keep
thin deprecated re-exports at the old paths.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* refactor(sftp): align local bookmarks store API with global bookmarks

Export subscribe/get/set/rehydrate helpers from localSftpBookmarks to match
the globalSftpBookmarks external-store pattern.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(electron): reduce baseline memory via spellcheck/GPU/prewarm guards

Disable spellcheck on BrowserWindows and the default session, drop spare
renderers, gate aggressive GPU switches behind NETCATTY_COMPAT_GPU, and make
settings prewarm opt-in via NETCATTY_PREWARM_SETTINGS.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* Isolate notes and customAccent from TerminalLayer hot props

Route vault notes through notesStore (shellHistoryStore pattern) and
app accent through appearanceChromeStore so note edits and accent-drag
ticks no longer rebuild TerminalLayer. Notes/AI slots and Terminal leaves
subscribe locally; VaultView and open-note callbacks stay unchanged.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(ui): cut unnecessary AppView/VaultView/settings re-renders

Stabilize TopTabs close handler, memo AppHostTreeLayer, exclude accent from
settingsVersion, ignore connectionLogs off the logs section, share lastConnected
host refs, and keep rename dialog drafts local so chrome domain stays stable.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* chore: package size, dead code, and consolidation cleanup

Remove unused direct deps, exclude Vite-bundled renderer packages from
electron-builder, delete high-confidence dead code/no-op transfer warm
wiring, consolidate cloud provider IDs and KnownHost/SFTP helpers, and
land UI/AGENTS quick wins (rAF text batching, notes draft debounce,
streaming plain text, tempDirBridge, diagnostics adapter, External MCP
setters).

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: stabilize rAF text batching and diagnostics tests

Polyfill requestAnimationFrame for Node, flush pending text before
steer buffering, bypass rAF while steer is in flight, and stub
global localStorage for AI panel diagnostics adapter tests.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: correct import paths after application/state hook migrations

Migrated vault/AI/system hooks still used components/-relative imports;
point them at repo-root domain/components/types/lib instead.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: address Bugbot/Codex regressions from perf cleanup

- Keep blank Enter from closing RenameDraftDialog; only reset rename state after a non-empty submit
- Flush note drafts before vault mutations so pending title/body edits are not overwritten
- Re-apply appearanceChromeStore accent on appearanceTheme in Terminal, and keep layer themes unaccented

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(notes): flush draft before markdown export paths

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: keep editor-close hotkey ref fresh during render

Assign handleRequestCloseEditorTabRef in render instead of useEffect so
Cmd/Ctrl+W never hits the App stub before the effect runs.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: keep accent in chrome themes without TerminalLayer thrash

Restore accent in globalAppearance for chrome/injection consumers.
Keep resolveFocusedAppearance identity stable across accent drag via
accent ref, while Terminal leaf still re-applies appearanceChromeStore.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: keep compose chrome accent live and merge note renames

Subscribe theme-panel focusedAppearance to appearanceChromeStore so
compose-bar chrome updates during color-picker drag even when
resolveFocusedAppearance identity stays stable. Tree renames now merge
title into the post-flush note instead of replacing from a stale row.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: clear terminal accent when switching back to theme mode

Re-resolve appearanceTheme by catalog id before applyCustomAccent so a
stale baked custom cursor/selection cannot stick after accentMode returns
to theme while TerminalLayer memo ignores accent props.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: register unsaved prompt singleton during render

AppView close handlers call promptUnsavedChanges outside the old
render-prop. Assign the singleton during render so Cmd/Ctrl+W and tab
close never hit the pre-effect null window that auto-cancels.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: keep vault import hooks UI-free and flush notes on teardown

Move ImportOptions into application/state and inject a notifier so
useVaultImportHandlers no longer imports components. Notes drafts also
flush on pagehide/beforeunload when Electron tears down without React
unmount.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: move AI terminal session context builders into domain

buildAITerminalSessionInfo and AIPanelContext now live under domain/ so
application hooks no longer import TerminalLayerSupport and create a
UI → application → UI cycle.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: drop duplicate AIPanelContext type from TerminalLayerSupport

The type is re-exported from domain; the local definition conflicted.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: keep migrated state hooks free of UI-layer imports

Move aiChatStreamingSupport into infrastructure/ai and poll list helpers
into domain/systemManager so useAIChatStreaming / useSystemManager no
longer import from components. Leave thin re-exports at the old paths.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: move remaining migrated-hook deps out of components

Host-key verification types/helpers now live in domain/, and SFTP column
layout types live in application/state/sftp/columnLayout. Application
hooks no longer import those definitions from the UI layer.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix: clear lint blockers and flush notes when panels hide

Move AI panel diagnostics persistence into application/state so
components stop importing localStorageAdapter. Flush note drafts when
retained NotesManager mounts become inactive, and silence related lint
warnings in theme/host-key helpers.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(notes): publish notesStore sync on vault note updates

updateNotes/updateNoteGroups now publishNotesSnapshot immediately so
AI/notes consumers do not briefly read a stale catalog after a hide
flush. Flush inactive drafts in useLayoutEffect before paint.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-08-07 14:40:38 +08:00
205d1591e8 feat(terminal): add disconnect/reconnect on workspace split status bar (#2769) (#2771)
* feat(terminal): add disconnect/reconnect controls on session status bar

Workspace panes could not reach tab-level reconnect. Expose disconnect
(without closing the pane) and reconnect beside the host info actions so
split sessions stay operable. Closes #2769.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): abort in-flight boot on status-bar disconnect

Disconnect keeps the pane mounted, so cancel's unmount cleanup never runs.
Flip isBootActiveRef (and isCancelling) so a late startSSH/startMosh attach
cannot resurrect the session after the user disconnects mid-connect.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): ignore stale start UI after status-bar disconnect

When disconnect aborts an in-flight connect, starter catch paths must not
reopen auth or repopulate errors. Gate SSH/telnet/mosh/ET/local/serial
failure UI on isTerminalBootActive and harden the boot effect awaits.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): hide disconnect/reconnect on compact popup terminals

Owned command popups use compactToolbar with a one-shot startup command.
Exposing reconnect there can re-run that command; keep the controls on
normal tabs/workspace panes only.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): use boot epoch so reconnect cannot revive aborted starts

Disconnect alone flipping isBootActiveRef is not enough: an immediate
reconnect re-arms boot, and a late startSSH from the aborted attempt can
still attach. Capture a boot epoch per attempt and ignore stale attaches/UI.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): do not let stale attach abort a newer reconnect

tryAttach failure for an expired boot epoch must close the orphan session
only. Calling abortSessionStartAfterUnmount() would force disconnected on
a reconnect that already re-armed boot.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): reject late host-key prompts after disconnect

Disconnect can land before pendingHostKeyRequestId is set. Auto-deny
onHostKeyVerification when boot is inactive or status is disconnected
so approval UI cannot reopen on an aborted pane.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): cancel wake and MFA when status-bar disconnects

Invalidate hibernation reconnect-wake tokens on disconnect so a late wake
cannot re-arm boot. Reject/cancel keyboard-interactive prompts for
disconnected panes and clear any already-queued MFA for that session.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): ignore stale SSH catch UI and password fallback

Check boot attempt currency before resetting reconnect UI state on
SSH start failure, and before launching key→password fallback after
disconnect so a replacement reconnect is not interrupted.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): guard stale SSH progress and host-key prompts

Ignore chain-progress callbacks from superseded boot attempts, and
thread bootEpoch through SSH host-key verification so a late prompt
from an aborted start cannot overwrite a replacement reconnect.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): correlate MFA and jump host-keys with boot epoch

Forward bootEpoch through jump-host verification and keyboard-interactive
prompts, and reject superseded MFA when the retained tab has already
started a replacement reconnect.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): keep replacement SSH session alive across stale starts

Skip closeSession for superseded starts while a newer boot still owns
the shared sessionId, and avoid clearing MFA wait state before the
attempt-freshness guard on SSH success.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): own shared sessionIds by boot epoch

Claim SSH/local registry slots with bootEpoch so a stale start cannot
overwrite a replacement, and make closeSession no-op when the payload
epoch does not match the live session owner.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): claim bootEpoch for all reconnectable backends

Pass bootEpoch through Telnet/Mosh/ET/serial starts, claim registry slots
the same way as SSH/local, and dispose displaced owners when a newer
boot replaces the shared sessionId.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): protect preload listeners and ET artifacts on displace

Defer epoch-scoped closeSession listener teardown until main confirms
ownership, and remove ET external-auth temp artifacts when a newer boot
displaces the previous registry owner.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): clean rejected Mosh starts and keep replacement listeners

Kill superseded Mosh/ET PTYs and auth temp files when claimSessionSlot
rejects, and never clear shared preload listeners on epoch-scoped closes.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): settle superseded boots and stop displaced log streams

Reject Telnet/SSH claim failures through Promise reject/failReuse instead of
uncaught EventEmitter/.then throws, and stop a displaced session's log stream
so a logging-disabled reconnect cannot append into the old file.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): keep woken runtime when disconnect cancels hibernate wake

Disconnect invalidates in-flight hibernated reconnects with keep mode so a
successful wake that already cleared hibernatedRef is not disposed into a
dead pane that can never reconnect. Unmount still disposes orphan runtimes.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): bind reused SSH shellPid only to the claimed session

Late PID discovery after Copy Tab reuse must not write onto a newer
bootEpoch that already replaced the shared sessionId registry slot.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): await plugin cancel cleanup and clear boot epochs on teardown

Track plugin start/cancel promises so Disconnect→Reconnect waits until the
external session finishes unregistering, and stop unmount teardown from
republishing boot epochs into the process-wide map.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): cancel SSH passphrase prompts on disconnect

Register a pending-boot AbortSignal for SSH starts, abort it (and session-
scoped passphrase requests) from closeSession even before a registry slot
exists, and filter/drop late passphrase modals by boot epoch / disconnect.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): close owned boot epoch and scope passphrase cancels

Capture the pre-bump epoch for disconnect/teardown closeSession so live
transports are not skipped by epoch mismatch, cancel only passphrase prompts
at or below that epoch, and revalidate boot currency after async passphrase
lookups before queuing a modal.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): tag Telnet auto-login and Mosh ready with bootEpoch

Emit bootEpoch on telnet auto-login and mosh:ready events so overlapping
Disconnect→Reconnect boots ignore cross-delivered callbacks meant for an
older shared sessionId.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): update disconnect assertion and cancel pending serial opens

Align the disconnect source regression with invalidateBootEpochForClose, and
abort in-flight serialPort.open attempts via pending boot AbortSignal so
Disconnect→Reconnect cannot leave a stale open racing the replacement.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

* fix(terminal): keep flow-pause leases on skipped epoch closes

Worker close:await previously cleared pause ownership in finally even when
the worker returned skipped for an epoch mismatch, dropping the live
replacement's lease. Only clear on non-skipped closes; epoch-scoped
fire-and-forget close similarly defers to onSessionClosed.

Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
2026-08-06 15:47:20 +08:00
陈大猫andCursor 8bbead1261 fix(sftp): serialize write OPENs and harden late/supersede cleanup (#2750)
* fix(sftp): keep cancel shared write OPEN drain gated on callback

Cancel settle must not force-complete sharedWriteOpenDrain — only channel
error does — so stage cleanup cannot race a late truncating OPEN after #2720.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(sftp): bound cancel OPEN drain and unlink late truncates

Restore cancel drain force-complete so stalled shared OPENs cannot hang
the lease, and best-effort unlink late truncating "w" opens after close so
force-complete cannot leave a recreated stage orphan.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(sftp): only unlink generated stages on cancel OPEN

Gate cancel-time truncating OPEN unlink on generatedStagePath so in-place
and symlink destinations keep their final path; cover with a late-OPEN test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(sftp): unlink late staged OPEN after channel-error settle

Late truncating generated-stage OPENs after settle must unlink even when
transfer.cancelled is false, so channel-error force-complete cannot leave
a recreated .netcatty-upload orphan.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(sftp): skip late OPEN unlink when same-id retry owns stage

After shared write OPEN drain force-complete, a late truncating "w"
callback still closed and unlinked generated stages. Resumable retries
reuse `.netcatty-<transferId>.part`, so a stale callback could delete
the new attempt's stage. Gate unlink on activeTransfers ownership
(generated stages + truncating only), matching the in-place final
target guard.

* fix(sftp): only skip late OPEN unlink for reusable resume stages

Same-id retry ownership must not suppress unlink for every generated
stage path. Non-resumable uploads use unique .netcatty-upload-* paths;
a stale late OPEN on those must still clean orphans. Only deterministic
.netcatty-<transferId>.part resume stages are protected.

* fix(sftp): gate late OPEN unlink skip on actual staged path

Only suppress late generated-stage unlink when the same-id retry's
active.stagedRemote.path matches. Do not treat resumable+targetPath or
path shape alone as ownership for in-place retries.

* fix(sftp): compare late OPEN stage paths in the same representation

OPEN filePath may be a session-encoded Buffer while stagedRemote.path is the
logical string, so strict equality failed and could unlink a same-id retry
stage. Normalize via encodePathForSession / Buffer equals before ownership skip.

* fix(sftp): gate truncating shared OPEN against same-path retries

Serialize truncating shared-write OPENs per remote path so a same-id
retry cannot issue OPEN "w" while a prior attempt is still unsettled.
When a drain force-complete already released the gate and a late stale
OPEN re-truncates a retry-owned resume stage, invalidate that retry
instead of allowing a sparse promote. Keep late-OPEN unlink skip for
retry-owned stages.

* fix(sftp): bound best-effort unlink so cancel cannot hang

Shared/sudo cancel after OPEN "w" waited on unlink before finishCancel.
A dead channel that never invokes the unlink callback left the transfer
and its session lease active indefinitely. Cap unlink at the same 2s
grace used for shared-write drain force-complete.

* fix(sftp): make truncating shared OPEN path-gate waits cancelable

A same-path upload queued behind a prior truncating OPEN could keep its
lease/admission slot forever if cancelled before runOpen. Race the gate
wait with transfer.abort and release the gate + drain on cancel.

* fix(sftp): scope truncating OPEN path gates per SFTP session

A module-global path-only key serialized unrelated hosts that shared the
same remote pathname. Include transfer/sftp session identity in the gate
key so only same-endpoint opens wait on each other.

* fix(sftp): refuse stage promote after stale OPEN truncation

A late force-completed OPEN w from a prior same-id attempt can zero the
deterministic stage after concurrent write finishes. Latch
staleOpenTruncatedStage through assertCanPromote and uploadFileConcurrent
so promotion cannot rename a corrupt stage.

* fix(sftp): keep path gate chained when canceling a queued OPEN wait

Releasing a cancelled waiter's gate immediately cleared the barrier while
the prior truncating OPEN was still in flight. Chain the waiter's release
to waitForPrior so later same-path attempts stay serialized.

* fix(sftp): hold reusable-stage path gate past OPEN force-complete

Force-completing drain no longer frees the path gate for generated stages
immediately. Same-id retries stay serialized until the late OPEN settles
(or a 10s hard cap) so promotion cannot race a still-processing OPEN w.

* test(sftp): wait for generated-stage path gate hold on retry OPEN

After force-complete, generated stages keep the path gate for 10s more.
Align the late-OPEN retry test wait with that hold.

* fix(sftp): hold truncating OPEN path gates for in-place targets too

In-place shared OPEN force-complete no longer frees the path gate
immediately, and late OPEN invalidation applies to final-path retries so
a stale truncating OPEN cannot wipe an in-flight same-id rewrite.

* fix(sftp): keep path gate held until OPEN settles (no hard cap)

Remove the post-force-complete gate timeout so a different transferId
cannot reopen the same target while a stale truncating OPEN is pending.

* test(sftp): accept normal {transferId,totalBytes} retry success

startTransferNow returns transferId/totalBytes on success, not success:true.

* fix(sftp): reject path-gate waits when the SFTP channel errors

Queued same-path OPEN waiters now observe channel error while waiting on
the prior gate, so a dead session cannot pin lease/admission forever.

* fix(sftp): treat superseded OPEN as cancelled, not failed

Late OPEN rejections after a same-id retry must not broadcast a terminal
failed event that marks the live retry task failed in transfer center.

* fix(sftp): emit cancelled channel for superseded OPEN failures

Worker fan-out only maps transfer:error as cancelled for cancel messages.
Superseded attempts now use transfer:cancelled so retries are not failed.

* fix(sftp): silence superseded OPEN terminal events for live retries

When a same-id retry already owns activeTransfers, do not emit cancelled
or failed for the stale superseded attempt or the transfer center sticks.

* fix(sftp): release path gate on OPEN force-complete for dead channels

When the OPEN callback never arrives, free same-path waiters after drain
force-complete so later uploads do not hang forever on the gate.

* fix(sftp): hold path gate until OPEN settles; 30s dead-channel grace

Force-complete no longer frees the gate immediately. Late OPEN release
keeps retries serialized; a 30s grace frees dead channels.

* fix(sftp): never force-release path gate on OPEN drain force-complete

A dead-channel force-complete must free the transfer/lease, but keep the
path gate held until the OPEN callback actually settles. Force-releasing
lets a same-path retry write then get wiped by a late truncating OPEN.

* fix(sftp): skip upload fallback when OPEN is superseded by a retry

A same-id retry can already own the transfer while the old OPEN callback
still rejects. Mark that rejection noTransferFallback so we never truncate
the shared stage via prepareUploadFallbackCheckpoint under the retry.

* fix(sftp): bound late OPEN close before releasing path gate

After drain force-complete, a late truncating OPEN must still close the
handle before releasing the path gate — but a dead channel may never
invoke CLOSE. Race close with a 2s timeout so same-path uploads cannot
wait forever on pathGate.waitForPrior.

* chore: re-trigger Codex review automation

* fix(sftp): always suppress terminal events for superseded OPEN attempts

Superseded means this attempt lost ownership. Do not require the retry to
still own activeTransfers — if the retry already completed, emitting
cancelled for the same transferId rewrites a completed transfer.

* chore: re-trigger Codex for path-gate head

* chore: nudge Codex automation after cancelled run

* fix(sftp): keep SFTP leases when suppressing superseded attempts

Superseded OPEN cleanup must not release transferId leases — a same-id
retry shares that lease set entry and would lose its hold if we release.

* fix(sftp): do not return result.error for superseded transfer attempts

Renderer directory ops treat any startStreamTransfer error as failure.
Superseded same-id OPEN attempts should return a non-error marker so the
live retry is not marked failed.

* fix(sftp): bound CLOSE on pre-settle cancel OPEN path

Reuse the 2s close timeout for cancel-before-settle so a dead channel
cannot leave the path gate held after finishCancel never runs.

* fix(sftp): hold path gate until unlink callback settles

Remove the 2s force-complete on late shared OPEN unlink so a delayed
server unlink cannot race a same-id retry that already reopened the stage.

* fix(sftp): path-gate r+ resume OPENs; release only unused superseded leases

Serialize all shared write opens including resume r+. On superseded, release
only SFTP leases not also held by the live same-id retry.

* fix(sftp): serialize shared and isolated OPENs by host+path

Path gates keyed only by SFTP object let an isolated-channel retry race a
stale shared truncating OPEN on the same host path. Prefer targetHostId so
both channels wait on the same gate.

* fix(sftp): path-gate isolated writes; revalidate checkpoint after gate

Gate all write OPENs including isolated channels so a shared stale OPEN "w"
cannot race. After waiting on the path gate, re-stat the stage and shrink
the resume checkpoint (and flags) before issuing r+/w.

* fix(sftp): dynamic truncating flags; cross-id stale OPEN; bound unlink

Resolve r+/w at OPEN/unlink time so afterPathGate flag getters still unlink
generated stages on cancel. Invalidate any same-path active transfer (not only
same-id) when a late truncating OPEN lands, and force checkpoint restart when
the stage was wiped before OPEN accepted. Bound best-effort unlink so a dead
channel cannot hang cancel settle.

* fix(sftp): clear staleOpenTruncatedStage after gated full restart

When a late truncating OPEN zeros the stage while a retry waits on the path
gate, afterPathGate restarts from offset zero. Consume the stale flag there so
a successful full rewrite is not failed by the post-upload guard.

* fix(sftp): keep cancel active during post-gate checkpoint revalidation

After the path gate opens, afterPathGate may stat a remote stage. Keep the
transfer abort hook and channel-error listener live for that await so cancel
or a dead channel can still release the path gate and lease.

* fix(sftp): hold path gate for isolated write OPENs until settle

Isolated truncating OPENs previously released the host/path gate on cancel
as soon as sftp.end() was called, allowing a same-path retry to race a still
in-flight OPEN that can truncate the stage without invalidation. Keep the gate
until the OPEN callback (or a bounded force-release after end), and run late
OPEN invalidation for isolated channels too.

* fix(sftp): close late shared read OPEN handles after settle

Restores close of late shared/sudo OPEN "r" handles after cancel or channel
error settle. Write-only gate invalidation remains for isolated late OPENs.

* fix(sftp): copy host IDs onto transfer for path-gate keys

dedicatedTransferResume passes targetHostId with a new targetSftpId. Keep
those host IDs on the transfer object so sharedWriteOpenSessionKey stays
host-scoped across session replacement instead of falling back to sftpId.

* fix(sftp): do not complete directory children on superseded invoke

When a same-id retry steals ownership mid-OPEN, the old startStreamTransfer
returns { superseded: true }. Wait for the live owner's terminal task status
instead of treating that result as a successful child completion.

* fix(sftp): pass host IDs on background restart; scope stale-OPEN invalidation

Background agent resume now includes source/target host IDs so path gates stay
host-scoped after openSftpForSession. Stale OPEN invalidation requires a
matching host or sftp session instead of treating missing host as a wildcard.

* fix(sftp): do not unlink stage after same-transfer fastPut fallback

When concurrent isolated OPEN dies without a callback and upload falls back
to fastPut on the same generated stage, a late OPEN must not unlink that
stage. Skip unlink when the same transfer already accepted an OPEN, owns
stagedRemote, or moved to a fastPut/stream strategy.

* fix(sftp): narrow fastPut unlink skip; handle superseded in all callers

Only suppress late-stage unlink when the same transfer moved to
fastPut/stream — ordinary cancel still unlinks. Background restart and
dedicated resume wait for the live same-id owner instead of treating
superseded invokes as completed.

* fix(sftp): never force-release isolated write path gates

Hold host/path gates for isolated truncating OPENs until the OPEN callback
settles, matching shared-channel behavior. Force-releasing after sftp.end()
allowed a late OPEN to wipe a completed same-path destination with no live
transfer left to invalidate.

* fix(sftp): hold path gate until late unlink settles

Transfer cancel can settle after a 2s unlink grace so dead channels do not
hang invokes, but the host/path gate stays held until the unlink callback
actually fires so a delayed unlink cannot delete a same-path retry stage.

* fix(sftp): wait for live owner without fixed superseded deadline

Superseded invokes now wait only on the live same-id transfer's terminal
status (or cancel). A 30-minute timeout could mark a still-running long
transfer failed and stick that status after the owner later completed.

* fix(sftp): classify superseded before cancelled cleanup

When a cancelled attempt loses ownership to a same-id retry mid-OPEN, the
error is Transfer superseded while cancelled is still true. Handle supersede
first so cancel cleanup cannot delete the retry stage, free its leases, or
emit a terminal cancelled event for the live owner.

* fix(sftp): wait for pending write OPEN before fastPut fallback

Expose pendingWriteOpenPathGate on the transfer so non-resumable concurrent
isolated failures wait for their own truncating OPEN to settle before
fastPut rewrites the same stage.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 09:16:59 +08:00
4adbfb8817 feat(tabs): add edit host to session tab and quick switcher menus (#2729)
* fix(#2725): automated Cursor CLI fix

* fix(ui): keep Quick Switcher open for host context menu clicks

Portaled ContextMenuContent lives outside containerRef, so outside mousedown was closing the switcher before Connect/Edit host could run.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com>
Co-authored-by: bincxz <16399091+binaricat@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 13:26:36 +08:00
陈大猫 f395b7c9e9 fix: allow deleting global command history entries (#2706)
Add single-entry delete on the Global history side panel, remove the
matching host autocomplete record when no duplicate row remains, and
keep the change set intentionally slim (no multi-window journal/sequence
hardening).

Fixes #2701
2026-08-05 10:58:22 +08:00
陈大猫andCursor 735c047a09 [Bug] Fix host connect script queue selection (#2705) (#2716)
* fix(hosts): keep draft connect scripts before onConnect promote

Host details queue editing pruned non-onConnect scripts immediately, so
default manual automation scripts could be selected but never appeared
in the queue. Keep an editable draft queue and only prune missing IDs
until save promotes triggers.

Closes #2705

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): promote persisted manual connect-queue scripts on save

Editable queue retention left already-saved manual script IDs unpromoted
when reopen/save compared only newly added IDs. Promote every queued
script that still needs onConnect + host targeting.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(hosts): drop unused HostDetailsPanel snippet imports

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): do not demote global scripts during queue save sync

Promote-on-save must leave targetsAllHosts onConnect scripts unchanged
when they appear in a host connectScriptIds list.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(hosts): cover global connect scripts during queue save sync

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): promote connect-queue scripts on new host save

New hosts have no initialData, so queue promote-on-save was skipped and
manual scripts stayed non-onConnect after the first save.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): harden connect-queue promote against Codex review

Preserve global scope when promoting manuals, skip re-promoting scripts
demoted while the host editor is open, sync peer host queues for retained
targets, and keep test fixtures ASCII-only.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): persist peer connect queues with edited host save

Peer queue sync via onHostsChange was overwritten by onSave rebuilding
from a stale hosts snapshot. Write peers+edited host together and make
save paths apply functional host updates.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): keep connect queues across hydration and concurrent edits

Skip queue rewrite while snippets are still empty, and do not promote
entries whose non-onConnect trigger changed while the host editor stayed
open.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): skip connect-queue rewrite before snippets hydrate

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): preserve concurrent target removals and merge peer deltas

Drop stale queue entries when targets were unlinked while editing, and
apply peer connectScriptIds updates via functional host state merges.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): hydrate connect-queue baseline and append peer deltas

Capture the first hydrated snippet catalog as the editor baseline, and
merge peer queue updates by appending only newly queued script IDs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): snapshot opening queue and keep peer updates host-safe

Use the editor-opening connect queue as sync previous IDs, and never
overwrite the edited host inside peer delta merges.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): clear deleted queues after hydrate and honor manual unlinks

Treat a hydrated empty snippet catalog as ready for queue reconciliation,
and drop concurrently untargeted non-onConnect queue entries on save.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(hosts): clear loginScriptId when connect queue is emptied

sanitizeHost reconstitutes connectScriptIds from loginScriptId when the
explicit queue is absent, so clearing the last queued script must also
drop the legacy field.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 09:53:37 +08:00
bincxz 8a9f08aa28 fix(ui): remove top-tabs local terminal fallback button
The title-bar terminal icon only appeared on Vault/SFTP and vanished on
terminal surfaces with the host tree open. Drop the redundant control;
local shell remains on the host-tree toolbar and quick switcher.
2026-07-31 10:50:00 +08:00
bincxz 39dd111ff7 fix(sftp): address Codex review on pause/resume reconnect
- Soft resume identity: size + mtime (+ local head sample), not size-only,
  so same-size in-place rewrites are rejected without full-file SHA-256.
- resumeQueuedTransfer returns lifecycleEpoch so soft-resume does not invent
  a higher epoch and drop live progress.
- Ignore left-/right- SFTP pane connection ids as openSftpForSession targets.
- Pass resumeHosts (vault + ephemeral) separately from vault hosts used by tray.
2026-07-31 01:16:31 +08:00
bincxz 8991d1faaf fix(sftp): repair pause/resume soft-control and hard reconnect reuse
Pause then resume was flaky: soft resume cleared lifecycle epochs so stale
paused events re-applied, soft failures always rehomed to dedicated-resume
(panel queue vanished, "reconnecting" then stuck paused), and concurrent
range soft-drain only waited 1.5s before "still finishing".

Soft resume now stamps bridge lifecycle epochs, only hard-reconnects when
the live stream is gone, dedicated resume reuses terminal/parked transports
(including ephemeral hosts), and deferred sparse settle runs in the
background with a longer resume wait for in-flight ranges.
2026-07-31 01:00:49 +08:00
bincxz 57af825706 perf(app): cut remaining top-tab switch jank on the renderer path
Stop AppView from re-subscribing to activeTabId so shell composition no
longer rebuilds on every top-tab click; move plugin keybindings and host
editor surface visibility into leaf subscribers. Patch session-restore
activeTabId without re-serializing every session on tab switches, defer
chrome theme CSS work with fingerprint short-circuit + rAF, let terminal
workspace chrome read active workspace from the live store, apply TopTabs
presentation per tab, and keep hibernate wake chunked/idle with leaner
focus scheduling.
2026-07-30 22:33:31 +08:00
陈大猫 8a28f42570 Merge pull request #2628 from binaricat/fix/app-terminal-domain-theme-runtime-deps
perf(app): land shell domain isolation + fix themeRuntime domain thrash
2026-07-30 22:04:49 +08:00
bincxz 987e64734d perf(app): isolate editor presence list and AI sibling session thrash
App/TopTabs consume useEditorTabChromeList so content keystrokes no longer
rebuild shell domains; dirty dots use useEditorTabDirty. AI side panels keep
the full sessions list for fuzzy history but areEqual only on exact-scope
session object refs so sibling stream ticks skip retained panels.
2026-07-30 19:30:41 +08:00
bincxz 653d67d39a perf(app): cut settings/log thrash and scope tab activity subscriptions
Stop appTerminalDomain from depending on the whole settings bag and stabilize
hotkey/capture callbacks via refs so opacity/settings/log writes do not rebuild
the terminal domain. Gate hidden History shellHistory subscriptions, use a
stable empty quick-switcher list, and move TopTabs activity dots onto per-tab
store snapshots so sibling activity no longer re-renders the whole tab strip.
2026-07-30 19:11:40 +08:00
bincxz 4c24a0bd88 merge: resolve main conflicts and host tree local-shell fallback
Merge main into the PR branch and keep the top-tabs local terminal control as a
fallback when the host-tree toolbar is disabled, collapsed, or not on a work
surface (Codex review).
2026-07-30 18:04:41 +08:00
bincxz 6fe22dd137 feat(terminal): make host tree toolbar customizable
Use the shared show/collapse/hide layout for the host-list toolbar, drop the
redundant top-tabs local-terminal button, and switch the local shell control to
the borderless Terminal glyph as the primary one-click entry.
2026-07-30 17:58:35 +08:00
bincxz f94c72cf55 perf(app): stop themeRuntime bag identity thrashing App terminal domain
useThemeRuntime now returns a memoized bag with setters-stable callbacks, and
App domain memos depend on clear/pick/resolve members instead of the whole
runtime object so unrelated App re-renders no longer rebuild appTerminalDomain.
2026-07-30 17:45:19 +08:00
bincxz f4ecb6d1a1 perf(app): isolate App shell domains, TopTabs titles, AI contexts, Vault history
Split the main-window mega bag into domain-scoped AppView slices so Vault,
terminal, chrome, dialogs, and mounts only re-render when their slice identity
changes. Drive tab titles/coding-CLI icons through sessionPresentationStore
(store-only live updates + orphan map overlay), retain presentation-stable
sessions for domain memos, stabilize AI panel context Maps with structural
equal/retain, and keep shell history on shellHistoryStore so Vault host lists
ignore history-only churn.
2026-07-30 17:28:16 +08:00
bincxz c239f30653 perf(terminal): cut TerminalLayer re-renders from history, cwd, and title churn
Decouple high-frequency UI state from TerminalLayer's memo boundary so
npm run dev is less dominated by full-layer reconciliation.

- Publish shell history through shellHistoryStore; History side panel
  subscribes directly instead of receiving shellHistory props.
- Track terminal CWD in terminalCwdStore so OSC 7 updates do not setState
  TerminalLayerInner; TabBridge updates side-panel live snapshots only.
- Move remote-history ownership into the History side-panel slot.
- Treat sessions as equal for pane/layer memo when only dynamicTitle or
  codingCliProviderId changes (TopTabs still sees full session updates).
- Stabilize AppView terminal handlers; thin AI vault catalog props when
  the AI panel is not visible; memo HostTreeView.
2026-07-30 16:10:08 +08:00
bincxz 34ee6a7ecb fix(terminal): bound inherited cwd probes 2026-07-30 10:43:48 +08:00