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
+19
-3
@@ -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 />
|
||||
|
||||
Reference in new issue
Block a user