mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-09 15:58:34 +00:00
1d91c8fbf4e22d48fe2ff0130784fadfe2062853
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2edbeae02c |
Remove unused code and assets
Remove unused code, assets, and stale config entries. |
||
|
|
fe7a867f38 | Improve terminal tab switching stability | ||
|
|
037b85bd66 |
fix(ui): smooth work-tab chrome transitions and split-pane autocomplete
Replace immersive instant-switch with animated active chrome theme sync so top tabs match terminal sessions immediately on tab click, and clamp autocomplete popups to the active pane so they stay anchored to the cursor in split workspaces. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
4b5993cad6 | fix host sidebar behavior for editor tabs (#1348) | ||
|
|
36e5779d94 |
perf(terminal): reduce terminal tab-switch and layout jank (#1321)
* 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> |
||
|
|
86a815ad46 |
[codex] Optimize terminal tab switching (#1003)
* Optimize terminal tab switching * Reduce themed tab switch repaint work |
||
|
|
b9e9a0d59c |
feat(editor): promote SFTP text editor into top-level tabs (#631) (#808)
* chore: ignore local .worktrees/ directory Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): editorTabStore scaffold with single-tab ops Implements the EditorTabStore class singleton (matching activeTabStore pattern) with updateContent, markSaved, setWordWrap, setSavingState, close, and subscribe. Includes useSyncExternalStore hooks and 6 passing unit tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): editorTabStore promoteFromModal with per-session path dedup * feat(editor): confirmCloseBySession for session teardown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(sftp): writeTextFileByConnection for pane-agnostic saves Adds a new `writeTextFileByConnection(connectionId, expectedHostId, filePath, content, filenameEncoding?)` method to `useSftpExternalOperations` that looks up the SFTP pane by connection ID (with a hostId safety check) instead of the left/right-side coupling used by `writeTextFile`. Threads the existing `getPaneByConnectionId` callback through the call site and re-exports the new method via `SftpStateApi`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(editor): editorSftpBridge singleton for out-of-React saves Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(editor): extract TextEditorPane from TextEditorModal Lift Monaco editor body + toolbar + theme sync + paste fallback into a pure TextEditorPane component. Adds sftp.editor.maximize i18n key to en.ts and zh-CN.ts locale files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(editor): drop unused getLanguageId import in TextEditorPane Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(editor): TextEditorModal delegates to TextEditorPane Replace the monolithic modal (560 lines including full Monaco setup) with a thin Dialog shell (~150 lines) that owns content/saving/saveError/ languageId state, save orchestration, and dirty-check on close, then delegates all editor chrome to <TextEditorPane chrome="modal" />. Exports TextEditorModalSnapshot for the optional onPromoteToTab callback so callers can later wire tab promotion (Task 12) without breaking the existing interface — the new prop is optional and existing callers (SftpOverlays.tsx) are source-compatible with zero changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(editor): include fileName and wordWrap in TextEditorModalSnapshot Task 12 will populate the promoted tab with these fields, so the snapshot must carry them from the modal at maximize time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): UnsavedChangesDialog three-button confirm * fix(editor): resolve UnsavedChangesDialog re-entrance and unmount leaks - Re-entrance: if prompt() is called while a prior prompt is still pending, cancel the prior one so its caller doesn't hang forever. - Unmount: resolve any in-flight prompt as "cancel" in the effect cleanup so awaiters don't leak when the provider unmounts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): TextEditorTabView tab-form shell Add TextEditorTabView component that binds an editorTabStore entry to TextEditorPane, with CSS display:none toggling for inactive tabs so the Monaco instance persists across tab switches. Also adds setLanguage public method to EditorTabStore (lands Task 15's intent early — Task 15 can be a no-op). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(editor): read live store state in TextEditorTabView handlers React state snapshot lags the store by a microtask. Closing over `tab` meant a keystroke between Monaco's onChange and a Ctrl+S would write stale content and mark a stale baseline. Read via editorTabStore.getTab at call time instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): dispatch editor:* tab ids in App and activeTabStore - Add EDITOR_PREFIX, isEditorTabId, toEditorTabId, fromEditorTabId helpers - Add useIsEditorTabActive hook to activeTabStore - Update useIsTerminalLayerVisible to exclude editor tabs - Import useEditorTabs and TextEditorTabView into App.tsx - Append editor tab ids (editor:<id>) to allTabs in hotkey handler - Mount TextEditorTabView per editorTab with CSS visibility toggling - Add editorTabs to executeHotkeyAction useCallback dependency array Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(editor): render editor tabs in TopTabs with icon/dirty/tooltip - Add `fromEditorTabId`, `isEditorTabId` imports to TopTabs.tsx - Add `FileCode`, `FileText` icons; use FileCode for code-like extensions - Extend `TopTabsProps` with `editorTabs`, `onRequestCloseEditorTab`, `hostById` - Build `editorTabMap` for O(1) lookup; add `editor` branch in `orderedTabItems` - Render editor tab chrome matching terminal tab style: file icon, dirty dot (●), filename with disambiguation suffix for duplicate filenames, close button - In App.tsx: add stub `handleRequestCloseEditorTab`, `orderedTabsWithEditors`, pass new props to `<TopTabs>` Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(editor): hoist editor-tab code-extension regex and use onSelectTab - Move CODE_EXTENSIONS_RE to module scope so it isn't recompiled per render. - Call onSelectTab(tabId) for consistency with other tab types, instead of reaching into activeTabStore directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): maximize modal to tab and dirty-confirm tab close Wire onPromoteToTab from TextEditorModal through SftpOverlays and useSftpViewFileOps so clicking the maximize button snapshots editor state into editorTabStore and activates the new editor tab. Replace the stub handleRequestCloseEditorTab in App.tsx with a real dirty-confirm flow using UnsavedChangesProvider render-prop: clean tabs close immediately, dirty tabs prompt save/discard/cancel, and save routes through editorSftpBridge with markSaved on success. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(editor): register SFTP bridge and gate session close on dirty editor tabs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(editor): make onDisconnect async so host-picker waits for dirty check The session-close dirty gate added in Task 13 made onDisconnect async, but the host-picker in SftpPaneDialogs still called it synchronously before kicking off onConnect — a fire-and-forget that raced past the dirty prompt and let unsaved editor tabs slip through. Propagate the Promise return type through SftpPaneCallbacks / SftpPaneDialogs / useSftpViewPaneActionsResult and await it at the host-picker call sites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): block app quit while editor tabs are dirty Add a before-quit IPC guard that asks the renderer whether any editor tab has unsaved changes. If dirty tabs exist, preventDefault() blocks the quit and a warning toast is shown. The app quits normally once editors are clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(editor): add 5s timeout fallback to quit-guard IPC check If the renderer crashes or throws before reporting back, the quitGuard would stay busy forever and the app could not be quit. Fall back to force-quit after 5 s if no reply arrives. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(editor): quit-guard uses quitConfirmed flag to prevent re-entry loop The prior flow reset quitGuardChannelBusy before calling app.quit(), which on macOS re-fires before-quit and re-entered the dirty check with the flag cleared — creating an infinite IPC loop. Introduce a separate quitConfirmed flag that commits to quitting before app.quit() fires, so the re-entry takes the fast path. Also extract QUIT_GUARD_TIMEOUT_MS and clarify that a concurrent quit while a check is in flight is swallowed (preventDefault) rather than letting the second event through. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(editor): use absolute inset-0 for tab panel and add sr-only DialogTitle Two bugs surfaced during the first dev-server smoke test: 1. Editor tab content was blank because TextEditorTabView used only className="h-full", while its sibling panels (VaultView, SftpView, TerminalLayerMount, LogView) all fill their flex-1 parent via `absolute inset-0`. In normal flow the editor tab collapsed to zero height. Match the sibling convention. 2. Radix printed an accessibility warning because the Task 7 refactor pulled the DialogTitle out of DialogContent and into the Pane header (now a plain span). Add a visually hidden DialogTitle that mirrors the filename, so screen readers have a title without showing it twice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(editor): raise tab panel z-index to 20 so it sits above TerminalLayer TerminalLayer's root is visibility:hidden when the active tab is an editor tab, but its inner panels set `absolute inset-0 z-10` on their own and those still paint. Without an explicit z on the editor tab panel, TerminalLayer's inner bg-background div was covering the Monaco content, producing a blank screen. Also add bg-background to the wrapper so the editor tab paints an opaque surface (matches the pattern VaultViewContainer / TerminalLayer follow). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): show host label and remote path next to filename in tab header The editor tab form previously only showed the bare filename in its header, which is ambiguous when the same filename is open against multiple hosts. Add an optional subtitle prop on TextEditorPane and populate it from the tab form with `<hostLabel>:<remotePath>` rendered in muted text beside the filename. The modal keeps its existing filename-only header. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(editor): bridge supports multiple useSftpState instances useSftpState is instantiated in both the top-level SftpView and the terminal's SftpSidePanel, each owning its own pane registry. The editor bridge previously stored only one writer, so maximizing a file opened from the terminal side panel registered nothing (bridge was owned by SftpView which may never have mounted) and save failed with "bridge not registered". Change the bridge to track a Set of writers and dispatch by trying each until one owns the connectionId (signalled by its specific "connection no longer available" error). Add registerEditorSftpWriterScoped that returns an unregister fn so each instance's cleanup removes only its own entry. Register in both SftpView and SftpSidePanel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(editor): Cmd+W closes editor tab + terminal close forces tab close Two behaviors added after user feedback from dev-server smoke-test: 1. Cmd/Ctrl+W (the closeTab hotkey) previously did nothing on editor tabs because executeHotkeyAction had no branch for editor:* ids. Add one that reaches into the UnsavedChangesProvider render-prop's close flow via a ref, routing through the existing dirty-confirm path. 2. Closing a terminal tab unmounts its SftpSidePanel which destroys the useSftpState instance that owned the connection. Any editor tab promoted from that panel would then be stuck — bridge gone, save channel dead. On SftpSidePanel unmount, gather the connection ids it owned and call a new editorTabStore.forceCloseBySessions to drop matching editor tabs. Dirty state is dropped because the user closed the terminal knowing the file was open — there is no save channel left anyway. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(editor): Cmd/Ctrl+W works when focus is inside Monaco Monaco's internal key-event dispatcher swallows keydown before the capture-phase handler on the Pane's root div can see it, so the global hotkey dispatcher never got the chance to close the editor tab when the editor had focus. Register a Monaco editor command for the close-tab keybinding and route it through a handleCloseRef — mirrors the same pattern used for Cmd/Ctrl+S. Also drop the modal-only guard in the capture-phase handler so the outer-chrome path works in tab mode too. TextEditorTabView now receives an onRequestClose(tabId) prop that App.tsx wires via the render-prop-exposed handleRequestCloseEditorTabRef, same mechanism as the hotkey-dispatcher path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(editor): fall back to Vaults when forceCloseBySessions removes the active tab Closing a terminal tab triggers SftpSidePanel unmount which force-closes its editor tabs. If the editor tab being removed happened to be the active tab (user maximized → then closed the owning terminal from another path), the app ended up on a stale activeTabId with no selected tab and blank content. Inside forceCloseBySessions, if the active tab was one of the removed editor ids, redirect to 'vault'. Picking a more sophisticated neighbor would need the full orderedTabs list which isn't reachable from this layer; Vaults is always valid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
40d80fe535 |
perf: comprehensive UI and state management optimization (#539)
* perf: comprehensive performance optimization across UI and state management - Replace Array.find/includes with Map/Set lookups for O(1) access in hot paths - Add requestAnimationFrame throttling to all mousemove resize handlers - Remove redundant forceUpdate + useSyncExternalStore double subscription - Extract terminal search decoration config to module-level constant - Pause server stats polling and resize handlers for hidden terminals - Add timer cleanup for useEffect/useLayoutEffect with setTimeout - Use useEffectEvent to stabilize effect callbacks and reduce effect re-runs - Use useDeferredValue for QuickSwitcher search input - Batch activeTabStore notifications with microtask coalescing - Memoize sessionLogConfig and activityTrackedSessions to prevent child re-renders - Use ref pattern for stable onTerminalDataCapture callback - Skip TerminalLayer pre-warming when no sessions or workspaces exist Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: flush final resize value before canceling RAF Apply the last computed size synchronously on mouseup/cleanup before canceling the pending requestAnimationFrame. This prevents the final drag delta from being dropped when mouseup fires before the queued frame executes. Addresses review feedback from codex on all 3 RAF-throttled resize handlers: split resize, side panel resize, and SFTP column resize. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: initialize lastClientXRef on resize start to prevent click-collapse Without initialization, a click on the resize handle without dragging would use lastClientXRef=0, computing a large negative diff and collapsing the column to minimum width. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert useDeferredValue for QuickSwitcher search useDeferredValue can lag behind the actual input, causing quickResults to reflect a stale query when the user types fast and presses Enter. This is a correctness regression - the selected item may not match the user's intent. The host list is typically small (<200), so synchronous filtering is fast enough without deferral. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore runtime activity guard to prevent stale badge on tab switch The pre-filtered activityTrackedSessions reduces subscriptions for disconnected sessions, but removing the runtime shouldMarkSessionActivity check introduced a race: between tab switch and effect re-subscription, old listeners could mark the newly-focused session as unread. Restore the activeTabIdRef.current guard inside the callback as a safety net. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: defer initialConnectDoneRef flag until auto-connect executes Moving the flag inside the setTimeout callback prevents it from being set when the timer is canceled by cleanup. Previously, if the effect re-ran before the setTimeout(0) fired, the timer was cleared but the ref was already true, permanently skipping the initial local connect. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: capture resizingRef fields before setState updater Destructure field/startX/startWidth from the ref upfront so the functional updater closure never reads resizingRef.current after it may have been cleared by handleResizeEnd. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove activeTabId from activityTrackedSessions to stabilize subscriptions Depending on activeTabId caused subscriptions to tear down and recreate on every tab switch, resetting the ChunkedEscapeFilter mid-sequence and producing false unread badges. The runtime guard via activeTabIdRef already handles the active-tab check, so pre-filtering only needs to exclude disconnected sessions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fetch server stats immediately when tab becomes visible again Use hasFetchedRef to distinguish first connect (2s delay for connection stabilization) from tab resume (immediate fetch). Prevents showing stale CPU/memory data for 2 seconds after switching back to a terminal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore cold-start prewarm and reset network stats on tab resume 1. Revert shouldPrewarm guard - TerminalLayer should always prewarm after 1.2s regardless of session/workspace count, as the purpose is to hide lazy-load latency before the user opens their first terminal. 2. Reset netRxSpeed/netTxSpeed to 0 when resuming a hidden terminal tab. The backend computes network throughput as a delta from the previous sample, so the first fetch after a long hidden interval would show artificially low throughput averaged over the gap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: reset hasFetchedRef on disconnect and preserve built-in theme precedence 1. Clear hasFetchedRef when connection drops so reconnects get the 2s stabilization delay before first stats fetch. 2. Reverse theme merge order in themeById Map so built-in themes are written last and take precedence over custom themes with duplicate IDs, matching the original find() semantics and other resolution sites (customThemeStore.getThemeById, Terminal.tsx). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: also clear per-interface network speeds on tab resume Reset rxSpeed/txSpeed on each netInterfaces entry in addition to the aggregate values, so the network hovercard doesn't show stale throughput while waiting for the first fresh poll after resume. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: reset capture ref on retry and skip warmup for established connections 1. Reset terminalDataCapturedRef in handleRetry() so log capture works for retried sessions (retry doesn't change sessionId, so the effect that resets the ref never re-runs). 2. Track connection start time to skip the 2s warmup delay when a tab becomes visible for a connection that was already established while hidden. Only apply the warmup for truly fresh connections (<2s old). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: prevent overlapping stats requests and track connection time while hidden 1. Add fetchInFlightRef guard to prevent concurrent getServerStats requests that could race and corrupt baseline CPU/network data. 2. Move connectedAtRef initialization before the isVisible check so connections that complete while the tab is hidden record their start time. This ensures the warmup delay is correctly skipped when the tab becomes visible for an already-stable connection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: reset fetchInFlightRef on disconnect to unblock reconnect stats A pending getServerStats request from a previous connection could keep fetchInFlightRef set, causing the reconnected session's initial fetch to be skipped until the old request timed out. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: clear fetchInFlightRef when tab becomes hidden Ensures the resume fetch isn't blocked by an in-flight request from the previous visible cycle. Any stale response from the old request will be quickly overwritten by the fresh immediate fetch on resume. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use generation counter to invalidate stale stats responses Replace fetchInFlightRef with a generation counter that increments on each fetch. Stale responses from before a hide/show cycle are discarded by comparing the captured generation against the current value, fully preventing pre-hide requests from overwriting zeroed network stats. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: increment fetch generation on effect setup to invalidate in-flight requests The generation was only incremented inside fetchStats, but the resume setTimeout hadn't fired yet when old responses arrived. Incrementing at effect setup time ensures any pre-hide in-flight request is immediately stale, preventing it from overwriting zeroed network stats. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
dbc3a8a29b |
Improves tab drag UX and updates Electron for stability
Prevents drop indicator flickering during tab reordering and ensures reliable visual feedback. Defers tab state notifications to avoid setState warnings in render phase. Optimizes terminal performance by disabling transparency and custom glyphs. Suppresses noisy DevTools autofill errors in Electron. Upgrades Electron and related dependencies for compatibility and stability. |
||
|
|
b8c221d112 |
Organize and normalize imports across project
Reorder and normalize import statements, group icon imports, and add missing utility imports (cn). Fix export/newline formatting and minor type import tweaks. Add ESLint scripts to package.json and update the lockfile. |
||
|
|
1a7d9e7639 |
Improves performance and removes debug logging
Reduces unnecessary re-renders in terminal and workspace components by optimizing host object creation and separating visibility effects. Speeds up SSH connection issue detection and lowers latency via adjusted keepalive and cipher settings. Removes debug console logs to clean up output and improve efficiency. |
||
|
|
a47f9ddc8c |
Improves performance and memoization of known hosts UI
Optimizes rendering of known hosts management by introducing memoization and limiting rendered items for large datasets. Refactors callback and state management in several components to reduce unnecessary re-renders and improve responsiveness. Enhances code structure for maintainability and future scalability, and adds debug logs to aid profiling and diagnostics. |
||
|
|
c29fc418f9 |
Refactor state management for active tabs and optimize component rendering
- Introduced `activeTabStore` to manage active tab state with fine-grained subscriptions. - Replaced direct prop passing of `isActive` with hooks (`useIsVaultActive`, `useIsSftpActive`) in components to determine active state. - Memoized components (e.g., `SftpView`, `VaultView`, `TerminalLayer`, `TopTabs`) to prevent unnecessary re-renders. - Updated various components to use `useCallback` and `useMemo` for stable references and performance improvements. - Removed redundant state and props, simplifying the component structure. - Disabled React.StrictMode in index.tsx for performance testing. |