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:
Richard ZinkandClaude Sonnet 5.5 committed 2026-11-15 08:20:52 +01:00
1 parent c08c6c7436
commit 388ca96419
31 files changed
+1518 -82

No files matched your search

+19 -3
View File
@@ -2,7 +2,7 @@ import { lazy, Suspense, useEffect, useState } from "react";
import { connectEvents, get, setUnauthorizedHandler } from "./api";
import { useT } from "./i18n";
import { navigate, useRoute, closePane, MAX_PANES } from "./router";
import { applyEvent, loadSessions, resync, setState } from "./store";
import { applyEvent, loadSessions, resync, setState, useStore } from "./store";
import type { Info } from "./types";
import { ImageViewer } from "./components/ImageViewer";
import { ProjectPage } from "./components/ProjectPage";
@@ -22,6 +22,8 @@ import { WhatsNew } from "./components/WhatsNew";
import { ShareTarget } from "./components/ShareTarget";
import { Palette } from "./components/Palette";
import { pendingShare } from "./share";
import { cacheInfo, cachedInfo, isOffline } from "./offline";
import { PublicChat } from "./components/PublicChat";
// The file manager (editor, git) is loaded on demand to keep the chat small.
const Files = lazy(() => import("./components/Files").then((m) => ({ default: m.Files })));
@@ -34,11 +36,21 @@ export function App() {
const [error, setError] = useState<string>();
const [menuOpen, setMenuOpen] = useState(false);
const [shared, setShared] = useState(pendingShare);
const offline = useStore((s) => s.offline);
useTitleActivity();
const refresh = async () => {
try {
const i = await get<Info>("/info");
let i: Info;
try {
i = await get<Info>("/info");
cacheInfo(i);
} catch (e) {
// No connection: open with the saved copy (chats are read-only).
const saved = isOffline(e) ? cachedInfo() : undefined;
if (!saved) throw e;
i = saved;
}
setState(() => ({ me: i.user, update: i.update ?? null }));
if (i.user) {
await loadSessions();
@@ -71,6 +83,7 @@ export function App() {
// Close the mobile menu on navigation.
useEffect(() => setMenuOpen(false), [route.join("/")]);
if (route[0] === "shared" && route[1]) return <PublicChat token={route[1]} />;
if (error) return <div className="empty">{error}</div>;
if (!info) return <div className="empty">{t("common.loading")}</div>;
if (!info.user)
@@ -119,7 +132,10 @@ export function App() {
<div className="app">
{menuOpen && <div className="backdrop" onClick={() => setMenuOpen(false)} />}
<Sidebar open={menuOpen} activeIds={chatIds} section={section ?? ""} />
<main className="main">{content}</main>
<main className="main">
{offline && <div className="offline-bar" role="status">{t("offline.banner")}</div>}
{content}
</main>
<WhatsNew version={info.version} />
<Palette />
<ImageViewer />