Files
chrome-use/packages/dashboard/src/store/activity.ts
T
Chris Tatectategithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Stefan SmiljkoviczhanbaxuyongliangxuyongliangThomas Kosiewski
f9174513c2 dashboard (#1034)
* dashboard

* fix: re-apply download behavior on recording context (#1019)

* fix: re-apply download behavior on recording context

record start creates a new browser context via Target.createBrowserContext.
Browser.setDownloadBehavior called at launch only applies to the default
context, so downloads in the recording context are silently dropped.

Fix:
1. Store download_path on BrowserManager (from LaunchOptions)
2. After creating the recording context, call Browser.setDownloadBehavior
   with the new browserContextId

This ensures downloads work during recording.

Fixes #1018

* fix: add download_path to third BrowserManager constructor (auto_connect_cdp)

* fix: reap zombie Chrome process and fast-detect crash for auto-restart (#1023)

When Chrome crashes (e.g. SIGTRAP from CHECK() assertion), the daemon
now:

1. Reaps the zombie immediately via a SIGCHLD handler in the event loop
   that calls waitpid(-1, WNOHANG)
2. Detects the crash instantly on the next command via a non-blocking
   try_wait() check (has_process_exited), avoiding the 3-second CDP
   timeout that is_connection_alive() would incur
3. Auto-relaunches Chrome transparently for the caller

Fixes #1017

Co-authored-by: ctate <366502+ctate@users.noreply.github.com>

* fix: route keyboard type through text input (#1014)

* fix: handle --clear flag in console command (#1015)

The console and errors commands parsed --clear from CLI args but the
action handlers silently ignored the flag. The handlers did not accept
the cmd parameter so they had no way to read the clear field.

Changes:
- Add clear_console() method to EventTracker in network.rs
- Update handle_console to accept cmd, read the clear field, and clear
  the buffer when --clear is passed (returns {cleared: true})
- Update call site in execute_command to pass cmd

Co-authored-by: xuyongliang <yongliang.xyl@alibaba-inc.com>

* chore: patch release - ### Bug Fixes

- **Re-apply download behavior on r... (#1025)

* Add runtime stream enable/disable/status commands (#951)

* Add runtime stream management commands

* Run rustfmt and satisfy clippy

* Fix stream disable cleanup semantics

* Format stream disable regression tests

* fix: retain radio/checkbox elements in compact snapshot tree (#1008)

compact_tree() checked for "[ref=" to identify lines worth keeping, but
radio and checkbox elements render as e.g. [checked=false, ref=e1] where
the "[" opens before "checked=", not "ref=". Dropping the leading bracket
so the check is just "ref=" fixes the match for all elements with refs.

Fixes #1006

Co-authored-by: ctate <366502+ctate@users.noreply.github.com>

* chore: version packages (#1027)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fixes

* dashboard

* fixes

* remove observe

* fmt

* fixes

* fixes

* jotai

* fmt

* upload dashboard

---------

Co-authored-by: Stefan Smiljkovic <stefan@vanila.io>
Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Co-authored-by: zhanba <c5e1856@gmail.com>
Co-authored-by: xuyongliang <478439790@qq.com>
Co-authored-by: xuyongliang <yongliang.xyl@alibaba-inc.com>
Co-authored-by: Thomas Kosiewski <thoma471@googlemail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-26 08:43:35 -07:00

121 lines
3.6 KiB
TypeScript

"use client";
import { atom } from "jotai";
import { useEffect } from "react";
import { useAtomValue, useSetAtom } from "jotai/react";
import type { ActivityEvent } from "@/types";
import { streamEventsAtom } from "@/store/stream";
import { activeSessionNameAtom } from "@/store/sessions";
const PERSIST_KEY = "ab-persist-activity";
const MAX_PERSISTED = 500;
function activityStorageKey(session: string) {
return `ab-activity-${session}`;
}
function loadPersistedEvents(session: string): ActivityEvent[] {
try {
const raw = localStorage.getItem(activityStorageKey(session));
if (!raw) return [];
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch {
return [];
}
}
function savePersistedEvents(session: string, events: ActivityEvent[]) {
try {
const capped = events.slice(-MAX_PERSISTED);
localStorage.setItem(activityStorageKey(session), JSON.stringify(capped));
} catch {
// Storage full or unavailable
}
}
function clearPersistedEvents(session: string) {
try {
localStorage.removeItem(activityStorageKey(session));
} catch {
// Ignore
}
}
// ---------------------------------------------------------------------------
// Primitive atoms
// ---------------------------------------------------------------------------
export const persistActivityAtom = atom(
typeof window !== "undefined"
? localStorage.getItem(PERSIST_KEY) === "true"
: false,
);
export const restoredEventsAtom = atom<ActivityEvent[]>([]);
// ---------------------------------------------------------------------------
// Derived atoms
// ---------------------------------------------------------------------------
export const combinedEventsAtom = atom((get) => {
const persist = get(persistActivityAtom);
const restored = get(restoredEventsAtom);
const streamEvents = get(streamEventsAtom);
if (persist && restored.length > 0) {
return [...restored, ...streamEvents].slice(-MAX_PERSISTED);
}
return streamEvents;
});
// ---------------------------------------------------------------------------
// Action atoms
// ---------------------------------------------------------------------------
export const togglePersistAtom = atom(null, (get, set) => {
const next = !get(persistActivityAtom);
set(persistActivityAtom, next);
localStorage.setItem(PERSIST_KEY, String(next));
if (!next) {
const session = get(activeSessionNameAtom);
if (session) clearPersistedEvents(session);
set(restoredEventsAtom, []);
}
});
export const clearActivityAtom = atom(null, (get, set) => {
set(streamEventsAtom, []);
set(restoredEventsAtom, []);
const session = get(activeSessionNameAtom);
if (session) clearPersistedEvents(session);
});
// ---------------------------------------------------------------------------
// Sync hook -- call once to keep localStorage in sync with atoms
// ---------------------------------------------------------------------------
export function useActivitySync() {
const persist = useAtomValue(persistActivityAtom);
const session = useAtomValue(activeSessionNameAtom);
const combinedEvents = useAtomValue(combinedEventsAtom);
const setRestored = useSetAtom(restoredEventsAtom);
// Load persisted events when session changes
useEffect(() => {
if (persist && session) {
setRestored(loadPersistedEvents(session));
} else {
setRestored([]);
}
}, [persist, session, setRestored]);
// Save combined events to localStorage when persist is on
useEffect(() => {
if (persist && session && combinedEvents.length > 0) {
savePersistedEvents(session, combinedEvents);
}
}, [persist, session, combinedEvents]);
}