Stats history with year filter, folding and XLSX/CSV export; snippets, read-only links, commit suggestions, weekly review, offline copies, server speech-to-text, planning model, job reports; fal.ai health fix
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c08c6c7436
commit
388ca96419
31 files changed
+1518
-82
No files matched your search
+28
-4
@@ -1,3 +1,4 @@
|
||||
import { cacheChat, cacheSessions, cachedChat, cachedSessions, isOffline } from "./offline";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { get } from "./api";
|
||||
import type {
|
||||
@@ -29,6 +30,8 @@ export interface State {
|
||||
/** Newer release (admins). */
|
||||
update: { version: string; url: string; notes: string } | null;
|
||||
connected: boolean;
|
||||
/** Showing a saved copy: the server cannot be reached. */
|
||||
offline: boolean;
|
||||
sessions: Session[];
|
||||
/** Includes subagent sessions loaded on demand. */
|
||||
sessionById: Record<string, Session>;
|
||||
@@ -60,6 +63,7 @@ let state: State = {
|
||||
me: null,
|
||||
update: null,
|
||||
connected: false,
|
||||
offline: false,
|
||||
sessions: [],
|
||||
sessionById: {},
|
||||
messages: {},
|
||||
@@ -236,7 +240,17 @@ export function dismissError(sessionId: string, id: number) {
|
||||
}
|
||||
|
||||
export async function loadSessions() {
|
||||
const list = await get<Session[]>("/sessions");
|
||||
let list: Session[];
|
||||
try {
|
||||
list = await get<Session[]>("/sessions");
|
||||
cacheSessions(list);
|
||||
if (getState().offline) setState(() => ({ offline: false }));
|
||||
} catch (e) {
|
||||
if (!isOffline(e)) throw e;
|
||||
list = cachedSessions().map((x) => ({ ...x, busy: false }));
|
||||
setState(() => ({ offline: true }));
|
||||
if (list.length === 0) throw e;
|
||||
}
|
||||
setState((s) => ({
|
||||
sessions: list,
|
||||
sessionById: { ...s.sessionById, ...Object.fromEntries(list.map((x) => [x.id, x])) },
|
||||
@@ -250,9 +264,19 @@ export const PAGE = 200;
|
||||
type Page = { messages: Message[]; has_older: boolean; has_newer: boolean };
|
||||
|
||||
export async function loadSession(id: string) {
|
||||
const data = await get<{ session: Session; messages: Message[]; busy: boolean; can_write: boolean; queue: QueueItem[]; step?: any; has_older?: boolean; has_newer?: boolean }>(
|
||||
`/sessions/${id}?limit=${PAGE}`,
|
||||
);
|
||||
type Loaded = { session: Session; messages: Message[]; busy: boolean; can_write: boolean; queue: QueueItem[]; step?: any; has_older?: boolean; has_newer?: boolean };
|
||||
let data: Loaded;
|
||||
try {
|
||||
data = await get<Loaded>(`/sessions/${id}?limit=${PAGE}`);
|
||||
cacheChat(data.session, data.messages);
|
||||
if (getState().offline) setState(() => ({ offline: false }));
|
||||
} catch (e) {
|
||||
const copy = isOffline(e) ? cachedChat(id) : undefined;
|
||||
if (!copy) throw e;
|
||||
// No connection: show the saved copy; new messages wait in the outbox.
|
||||
data = { session: copy.session, messages: copy.messages, busy: false, can_write: true, queue: [], has_older: true };
|
||||
setState(() => ({ offline: true }));
|
||||
}
|
||||
setState((s) => ({
|
||||
windows: { ...s.windows, [id]: { older: !!data.has_older, newer: !!data.has_newer } },
|
||||
...upsertSession(s, data.session),
|
||||
|
||||
Reference in new issue
Block a user