feat(quick-pane): promote active runtime

This commit is contained in:
2026-08-28 12:07:50 +02:00
parent 0a318f5ab6
commit b44b1be4b7
19 changed files with 1298 additions and 126 deletions
+48 -1
View File
@@ -28,6 +28,9 @@ export function useWorkspace() {
const legacyMigrationPending = useRef(false);
const disposed = useRef(true);
const refreshGeneration = useRef(0);
const workspaceReady = useRef(false);
const pendingPromotedRuntime = useRef<string | undefined>(undefined);
const consumingPromotion = useRef(false);
const loadSnapshot = useCallback(
async (runtimeId: string, options: { throwOnError?: boolean } = {}) => {
@@ -150,10 +153,44 @@ export function useWorkspace() {
[loadSnapshot],
);
const consumePromotedRuntime = useCallback(() => {
if (!workspaceReady.current || consumingPromotion.current) return;
consumingPromotion.current = true;
void (async () => {
while (pendingPromotedRuntime.current && !disposed.current) {
const runtimeId = pendingPromotedRuntime.current;
pendingPromotedRuntime.current = undefined;
await refresh();
if (disposed.current) return;
dispatch({ type: "selectRuntime", runtimeId });
await loadSnapshot(runtimeId);
if (disposed.current) return;
await invoke("consume_pending_promoted_runtime", { runtimeId });
}
})()
.catch(() => {})
.finally(() => {
consumingPromotion.current = false;
if (pendingPromotedRuntime.current) consumePromotedRuntime();
});
}, [loadSnapshot, refresh]);
useEffect(() => {
disposed.current = false;
workspaceReady.current = false;
let stop: (() => void) | undefined;
let stopNewRuntime: (() => void) | undefined;
let stopPromotedRuntime: (() => void) | undefined;
const promotedListenerReady = listen<{ runtimeId: string }>(
"workspace-promoted-runtime",
(message) => {
pendingPromotedRuntime.current = message.payload.runtimeId;
consumePromotedRuntime();
},
).then((unlisten) => {
if (disposed.current) unlisten();
else stopPromotedRuntime = unlisten;
});
void listen<{
phase: "starting" | "ready" | "error";
runtimeId?: string;
@@ -197,6 +234,7 @@ export function useWorkspace() {
"runtime_recovered",
"runtime_renewed",
"runtime_restore_failed",
"runtime_promoted",
].includes(eventType ?? "")
) {
void refresh();
@@ -215,10 +253,17 @@ export function useWorkspace() {
});
void (async () => {
try {
await promotedListenerReady;
const loaded = await loadPresentation();
if (disposed.current) return;
await refresh(loaded);
if (disposed.current) return;
workspaceReady.current = true;
const runtimeId = await invoke<string | null>(
"get_pending_promoted_runtime",
).catch(() => null);
if (runtimeId) pendingPromotedRuntime.current = runtimeId;
consumePromotedRuntime();
initialized.current = true;
} catch (error) {
if (!disposed.current)
@@ -243,9 +288,11 @@ export function useWorkspace() {
document.addEventListener("visibilitychange", visibility);
return () => {
disposed.current = true;
workspaceReady.current = false;
refreshGeneration.current += 1;
stop?.();
stopNewRuntime?.();
stopPromotedRuntime?.();
void invoke("unsubscribe_workspace").catch(() => {});
window.removeEventListener("focus", visibility);
window.removeEventListener("blur", visibility);
@@ -253,7 +300,7 @@ export function useWorkspace() {
Object.values(timers.current).forEach(clearTimeout);
void appWindow;
};
}, [refresh, scheduleSnapshot]);
}, [consumePromotedRuntime, refresh, scheduleSnapshot]);
useEffect(() => {
if (!initialized.current) return;