Files
Netcatty/application/state/useScriptExecution.ts
T
bincxz da831c053f perf(terminal): harden store isolation after multi-agent review
Address correctness and residual jank found in review:

- Expand pane session equality for connect/startup fields (pendingInitialCwd,
  startupCommand, pending scripts, serial/local shell, etc.).
- Cache coding-CLI provider in the signal controller so memo-skipped
  TerminalLayer renders cannot clear titles via a stale sessionsRef.
- Heal terminalCwdStore drift when the renderer ref already has the path.
- Publish shell history synchronously on write/load and via useLayoutEffect.
- Move script runs to scriptRunsStore; Scripts slot subscribes so automation
  ticks no longer re-render TerminalLayerInner.
- TerminalLayerView equal uses stable side-panel keys only (cwd/theme live
  via sidePanelLiveStore, not chrome re-renders).
- Compare terminalFontFamilyId / session log props; stabilize HostTreeView
  callbacks and end-session-drag handler.
2026-07-30 16:32:42 +08:00

77 lines
2.1 KiB
TypeScript

import { useCallback, useEffect, useSyncExternalStore } from 'react';
import type { ScriptRunParams } from '@/types/global/netcatty-bridge-script.d.ts';
import { netcattyBridge } from '@/infrastructure/services/netcattyBridge.ts';
import {
getScriptRunsSnapshot,
publishScriptRunsSnapshot,
subscribeScriptRuns,
} from './scriptRunsStore.ts';
let scriptRunsBridgeBound = false;
function ensureScriptRunsBridgeBound(): void {
if (scriptRunsBridgeBound) return;
if (typeof window === 'undefined') return;
const bridge = netcattyBridge.get();
if (!bridge?.scriptGetRuns) return;
scriptRunsBridgeBound = true;
bridge.scriptGetRuns()
.then((runs) => {
publishScriptRunsSnapshot(runs);
})
.catch(() => {});
bridge.onScriptRunsUpdated?.(({ runs: nextRuns }) => {
publishScriptRunsSnapshot(nextRuns);
});
}
/**
* Script run state is externalized so TerminalLayer can avoid re-rendering on
* every automation log tick. Call sites that need the list should subscribe
* via this hook (or scriptRunsStore directly).
*/
export function useScriptExecution() {
useEffect(() => {
ensureScriptRunsBridgeBound();
}, []);
const runs = useSyncExternalStore(
subscribeScriptRuns,
getScriptRunsSnapshot,
getScriptRunsSnapshot,
);
const runScript = useCallback(async (params: ScriptRunParams) => {
const bridge = netcattyBridge.get();
if (!bridge?.scriptRun) {
throw new Error('Script bridge unavailable');
}
return bridge.scriptRun(params);
}, []);
const stopRun = useCallback(async (runId: string) => {
await netcattyBridge.get()?.scriptStop?.(runId);
}, []);
const pauseRun = useCallback(async (runId: string) => {
await netcattyBridge.get()?.scriptPause?.(runId);
}, []);
const resumeRun = useCallback(async (runId: string) => {
await netcattyBridge.get()?.scriptResume?.(runId);
}, []);
const getRunsForSession = useCallback((sessionId: string) => {
return getScriptRunsSnapshot().filter((run) => run.sessionId === sessionId);
}, []);
return {
runs,
runScript,
stopRun,
pauseRun,
resumeRun,
getRunsForSession,
};
}