mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-11-10 16:21:22 +00:00
Several everyday interactions re-rendered the entire shell (AppView, TopTabs,
TerminalLayer and every pane) even though nothing they show had changed:
- Clicking or focusing a split pane always replaced the workspaces array
(twice per click: onClick + onFocusCapture), cascading through the whole
app and re-saving the session restore snapshot. setWorkspaceFocusedSession
and moveFocusInWorkspace now keep the same array when focus is unchanged
(domain/workspace.ts setWorkspaceFocusedSessionId).
- Every window focus/visibility change resynced app lock and produced a new
runtime state object even when nothing changed, re-rendering the provider
tree. Equal states keep the current object and useAppLockState's result is
memoized.
- Quick switcher keystrokes went through AppSideEffects state and the
app-local UI store, re-rendering AppSideEffects, every host island, AppShell,
AppView and TopTabs per character. QuickSwitcherOverlay now owns the query
and host results; the overlay unmounts on close, which resets the query
exactly where setQuickSearch('') used to.
- Pressing/releasing Ctrl/Cmd (every ⌘C/⌘V/Ctrl+C) re-rendered the whole tab
bar to toggle shortcut number badges. The held flag now lives in a small
store read only by each tab's TabIconSlot.
- AppView passed fresh inline callbacks that defeated TopTabs' memo comparator
and every open editor tab; TextEditorTabView is now memoized. TopTabs
memoizes per-workspace session lists and hoists the root tab icons.
- Workspace drag previews only update state when the drop hint actually moves.
React commits / render time for 10 repetitions in the real app (profiling
build, 2 split panes, 300 hosts), main -> this change:
window focus 42 / 17.1 ms -> 2 / 0.4 ms
press/release Cmd 50 / 41.9 ms -> 22 / 6.6 ms
click focused pane 42 / 69.6 ms -> 3 / 0.4 ms
quick switcher typing 41 / 61.3 ms -> 16 / 34.8 ms
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
2.0 KiB
TypeScript
49 lines
2.0 KiB
TypeScript
import React, { Suspense, lazy, useMemo, useState } from 'react';
|
|
|
|
import type { QuickSwitcherProps } from '../../components/QuickSwitcher';
|
|
import { LazyLoadBoundary } from '../../components/ui/lazy-load-boundary';
|
|
import { getHostSearchMatch } from '../../lib/searchMatcher';
|
|
import type { Host } from '../../types';
|
|
|
|
const LazyQuickSwitcher = lazy(() =>
|
|
import('../../components/QuickSwitcher').then((m) => ({ default: m.QuickSwitcher })),
|
|
);
|
|
|
|
/** Hosts matching the quick switcher query, best match first. */
|
|
export function filterQuickSwitcherHosts(hosts: Host[], query: string): Host[] {
|
|
const term = query.trim();
|
|
if (!term) return hosts;
|
|
return hosts
|
|
.map((host) => ({ host, match: getHostSearchMatch(term, host) }))
|
|
.filter((entry) => entry.match.matched)
|
|
.sort((left, right) => {
|
|
if (left.match.score !== right.match.score) {
|
|
return right.match.score - left.match.score;
|
|
}
|
|
return left.host.label.localeCompare(right.host.label);
|
|
})
|
|
.map((entry) => entry.host);
|
|
}
|
|
|
|
export type QuickSwitcherOverlayProps =
|
|
Omit<QuickSwitcherProps, 'query' | 'results' | 'onQueryChange'> & { hosts: Host[] };
|
|
|
|
/**
|
|
* Owns the quick switcher query. It used to live in AppSideEffects and travel
|
|
* through the app-local UI store, so every keystroke re-rendered the whole
|
|
* shell (AppSideEffects, every host island, AppShell, AppView and TopTabs).
|
|
* The overlay is only mounted while the switcher is open, so closing it resets
|
|
* the query just like the old `setQuickSearch('')` calls did.
|
|
*/
|
|
export function QuickSwitcherOverlay({ hosts, ...props }: QuickSwitcherOverlayProps) {
|
|
const [query, setQuery] = useState('');
|
|
const results = useMemo(() => filterQuickSwitcherHosts(hosts, query), [hosts, query]);
|
|
return (
|
|
<LazyLoadBoundary name="Quick switcher" resetKey={query}>
|
|
<Suspense fallback={null}>
|
|
<LazyQuickSwitcher {...props} query={query} results={results} onQueryChange={setQuery} />
|
|
</Suspense>
|
|
</LazyLoadBoundary>
|
|
);
|
|
}
|