"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { AppShell } from "@/components/AppShell"; import { ErrorState, LoadingState } from "@/components/StateBlocks"; import { getRunWalk } from "@/lib/api/client"; import type { FlowLedgerWalkResponse, RawRecord } from "@/lib/api/types"; import { asRecord, text } from "@/lib/flow-ledger/format"; import { WalkTree } from "@/features/walk/WalkTree"; export function RunWalkPage({ runId }: { runId: string }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const load = useCallback(async () => { setLoading(true); setError(null); try { const debug = new URLSearchParams(window.location.search).get("debug") === "1"; setData(await getRunWalk(runId, debug)); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }, [runId]); useEffect(() => { void load(); }, [load]); const summary = asRecord(data?.summary); const actions = useMemo( () => (data?.actions || []).filter((a): a is RawRecord => typeof a === "object" && a !== null), [data?.actions] ); const queryLabels = useMemo(() => { const raw = asRecord(summary.query_labels); const out: Record = {}; for (const key of Object.keys(raw)) out[key] = text(raw[key], ""); return out; }, [summary]); const budget = useMemo(() => budgetStats(summary), [summary]); return ( {loading ? : null} {error ? : null} {data && !loading && !error ? ( <>
游走预算 本 run 总览
{budget.map((stat) => ( {stat.value} {stat.label} ))}
) : null}
); } function fmtUsed(used: unknown, cap: unknown): string { const u = Number(used) || 0; if (cap === null || cap === undefined || cap === "") return String(u); return `${u} / ${Number(cap) || 0}`; } function budgetStats(summary: RawRecord): { label: string; value: string; tone?: string }[] { return [ { label: "游走到的视频", value: String(Number(summary.walk_video_total) || 0), tone: "go" }, { label: "标签→新搜索", value: fmtUsed(summary.tag_used, summary.tag_cap) }, { label: "作者→作品", value: fmtUsed(summary.author_used, summary.author_cap) }, { label: "最深层数", value: fmtUsed(summary.max_depth_reached, summary.max_depth_cap) } ]; }