feat(frontend): support static website demo mode (#3170)

* feat(frontend): support static website demo mode

* fix(frontend): render html artifact previews from blob content

* chore(frontend): apply pre-commit formatting

* fix(frontend): address static demo PR review comments

* Update the release information of DeerFlow

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
JeffJiang
2026-05-23 00:10:56 +08:00
committed by GitHub
parent 66d6a6a4e8
commit b103d1a7f5
21 changed files with 477 additions and 59 deletions
+17 -3
View File
@@ -10,6 +10,8 @@ import React, {
type ReactNode,
} from "react";
import { isStaticWebsiteOnly } from "../static-mode";
import { type User, buildLoginUrl } from "./types";
// Re-export for consumers
@@ -46,6 +48,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
const [isLoading, setIsLoading] = useState(false);
const router = useRouter();
const pathname = usePathname();
const staticMode = isStaticWebsiteOnly();
const isAuthenticated = user !== null;
@@ -54,6 +57,8 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
* Used when initialUser might be stale (e.g., after tab was inactive)
*/
const refreshUser = useCallback(async () => {
if (staticMode) return;
try {
setIsLoading(true);
const res = await fetch("/api/v1/auth/me", {
@@ -77,7 +82,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
} finally {
setIsLoading(false);
}
}, [pathname, router]);
}, [staticMode, pathname, router]);
/**
* Logout - call FastAPI logout endpoint and clear local state
@@ -87,6 +92,11 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
// Immediately clear local state to prevent UI flicker
setUser(null);
if (staticMode) {
router.push("/");
return;
}
try {
await fetch("/api/v1/auth/logout", {
method: "POST",
@@ -99,7 +109,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
// Redirect to home page
router.push("/");
}, [router]);
}, [staticMode, router]);
/**
* Handle visibility change - refresh user when tab becomes visible again.
@@ -108,6 +118,8 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
const lastCheckRef = React.useRef(0);
useEffect(() => {
if (staticMode) return;
const handleVisibilityChange = () => {
if (document.visibilityState !== "visible" || user === null) return;
const now = Date.now();
@@ -120,7 +132,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) {
return () => {
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
}, [user, refreshUser]);
}, [staticMode, user, refreshUser]);
const value: AuthContextType = {
user,
@@ -155,6 +167,8 @@ export function useRequireAuth(): AuthContextType {
const pathname = usePathname();
useEffect(() => {
if (isStaticWebsiteOnly()) return;
// Only redirect if we're sure user is not authenticated (not just loading)
if (!auth.isLoading && !auth.isAuthenticated) {
router.push(buildLoginUrl(pathname || "/workspace"));