2026-08-22 01:20:14 +03:00
|
|
|
import React, { useState } from "react";
|
2026-08-22 00:19:49 +03:00
|
|
|
import type { SessionSummary } from "../../../src/shared/protocol.ts";
|
|
|
|
|
import { GraphPlaceholder } from "../components/GraphPlaceholder.tsx";
|
|
|
|
|
|
|
|
|
|
interface HomeProps {
|
|
|
|
|
sessions: SessionSummary[];
|
2026-08-22 00:57:16 +03:00
|
|
|
onStart: (topic: string) => Promise<void>;
|
|
|
|
|
onOpen: (file: string) => void;
|
2026-08-22 00:19:49 +03:00
|
|
|
onRefresh: () => void;
|
2026-08-22 00:57:16 +03:00
|
|
|
error: string | null;
|
2026-08-22 00:19:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatDate(iso: string): string {
|
|
|
|
|
if (!iso) return "";
|
|
|
|
|
try {
|
|
|
|
|
return new Date(iso).toLocaleString(undefined, {
|
|
|
|
|
month: "short",
|
|
|
|
|
day: "numeric",
|
|
|
|
|
hour: "2-digit",
|
|
|
|
|
minute: "2-digit",
|
|
|
|
|
});
|
|
|
|
|
} catch {
|
|
|
|
|
return iso;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-22 00:57:16 +03:00
|
|
|
export function Home({ sessions, onStart, onOpen, onRefresh, error }: HomeProps) {
|
2026-08-22 00:19:49 +03:00
|
|
|
const [topic, setTopic] = useState("");
|
|
|
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
|
|
|
|
|
|
const submit = async () => {
|
|
|
|
|
const t = topic.trim();
|
|
|
|
|
if (!t || busy) return;
|
|
|
|
|
setBusy(true);
|
|
|
|
|
try {
|
|
|
|
|
await onStart(t);
|
|
|
|
|
} finally {
|
|
|
|
|
setBusy(false);
|
|
|
|
|
setTopic("");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="home">
|
|
|
|
|
<aside className="sidebar">
|
|
|
|
|
<div className="sidebar-head">
|
|
|
|
|
<span>Sessions</span>
|
|
|
|
|
<button className="icon-btn" title="Refresh" onClick={onRefresh}>
|
|
|
|
|
↻
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="session-list">
|
|
|
|
|
{sessions.length === 0 && <div className="empty">No sessions yet</div>}
|
|
|
|
|
{sessions.map((s) => (
|
2026-08-22 00:57:16 +03:00
|
|
|
<button key={s.id} className="session-item" onClick={() => onOpen(s.file)}>
|
2026-08-22 00:19:49 +03:00
|
|
|
<div className="session-name">{s.name || s.preview || "Untitled"}</div>
|
|
|
|
|
<div className="session-meta">
|
|
|
|
|
{formatDate(s.updatedAt)} · {s.messageCount} msg{s.messageCount === 1 ? "" : "s"}
|
|
|
|
|
</div>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</aside>
|
|
|
|
|
<main className="home-main">
|
|
|
|
|
<GraphPlaceholder />
|
|
|
|
|
<div className="new-session">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="What do you want to learn?"
|
|
|
|
|
value={topic}
|
|
|
|
|
disabled={busy}
|
|
|
|
|
onChange={(e) => setTopic(e.target.value)}
|
|
|
|
|
onKeyDown={(e) => {
|
|
|
|
|
if (e.key === "Enter") void submit();
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<button onClick={() => void submit()} disabled={busy || !topic.trim()}>
|
2026-08-22 00:57:16 +03:00
|
|
|
{busy ? "Starting…" : "Start"}
|
2026-08-22 00:19:49 +03:00
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-08-22 00:57:16 +03:00
|
|
|
{error && <div className="error-banner">{error}</div>}
|
2026-08-22 00:19:49 +03:00
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|