feat(status-ui): improve concurrent session workflows

Add in-place session renewal, searchable model selection, clearer progress feedback, tool-result previews, and per-session presentation state.

Keep commands, errors, loading, and lifecycle indicators attached to their owning runtime so parallel tabs cannot interfere.
This commit is contained in:
2026-08-25 14:56:42 +02:00
parent 9f2c840ae7
commit 166b065ad4
20 changed files with 2211 additions and 430 deletions
+41 -22
View File
@@ -29,28 +29,46 @@ export function useWorkspace() {
const disposed = useRef(true);
const refreshGeneration = useRef(0);
const loadSnapshot = useCallback(async (runtimeId: string) => {
if (disposed.current) return;
const generation = (loadGeneration.current[runtimeId] ?? 0) + 1;
loadGeneration.current[runtimeId] = generation;
dispatch({ type: "snapshotRequested", runtimeId, generation });
try {
const snapshot = await invoke<RuntimeSnapshot>(
"get_session_runtime_snapshot",
{ runtimeId },
);
if (!disposed.current)
dispatch({ type: "snapshotLoaded", runtimeId, generation, snapshot });
} catch (error) {
if (!disposed.current)
dispatch({
type: "snapshotFailed",
runtimeId,
generation,
error: String(error),
});
}
}, []);
const loadSnapshot = useCallback(
async (runtimeId: string, options: { throwOnError?: boolean } = {}) => {
if (disposed.current) return false;
const generation = (loadGeneration.current[runtimeId] ?? 0) + 1;
loadGeneration.current[runtimeId] = generation;
dispatch({ type: "snapshotRequested", runtimeId, generation });
try {
const snapshot = await invoke<RuntimeSnapshot>(
"get_session_runtime_snapshot",
{ runtimeId },
);
if (!disposed.current)
dispatch({ type: "snapshotLoaded", runtimeId, generation, snapshot });
return true;
} catch (error) {
if (!disposed.current)
dispatch({
type: "snapshotFailed",
runtimeId,
generation,
error: String(error),
});
if (options.throwOnError) throw error;
return false;
}
},
[],
);
const snapshotRetryKey = Object.values(state.runtimesById)
.flatMap((runtime) =>
runtime.snapshotRetry ? [runtime.summary.runtimeId] : [],
)
.sort((left, right) => left.localeCompare(right))
.join("\n");
useEffect(() => {
if (!snapshotRetryKey) return;
for (const runtimeId of snapshotRetryKey.split("\n"))
void loadSnapshot(runtimeId);
}, [snapshotRetryKey, loadSnapshot]);
const scheduleSnapshot = useCallback(
(runtimeId: string) => {
@@ -177,6 +195,7 @@ export function useWorkspace() {
"runtime_opened",
"runtime_closed",
"runtime_recovered",
"runtime_renewed",
"runtime_restore_failed",
].includes(eventType ?? "")
) {