106 lines
4.6 KiB
JavaScript
106 lines
4.6 KiB
JavaScript
// Archive screen — main listing view
|
|
const ArchiveScreen = ({ documents, onOpen, query, setQuery }) => {
|
|
const filtered = query ? documents.filter(d => d.title.toLowerCase().includes(query.toLowerCase()) || d.excerpt.toLowerCase().includes(query.toLowerCase())) : documents;
|
|
|
|
return (
|
|
<>
|
|
<Topbar crumbs={["Archive", "All documents"]}>
|
|
<Button kind="secondary">Import</Button>
|
|
<Button kind="primary">New note</Button>
|
|
</Topbar>
|
|
|
|
<div className="page">
|
|
<div className="page-hero">
|
|
<div className="eyebrow">§ Archive · {documents.length} documents · updated today</div>
|
|
<h1>A quiet place to keep <em>what you are reading</em>.</h1>
|
|
<p className="lead">Every document annotated. Every question remembered. Search the thing you half-recall, the way you actually remember it.</p>
|
|
</div>
|
|
|
|
<FloatingSearch onFocus={() => {}} />
|
|
|
|
<div className="section-head">
|
|
<h2>Recently <em>filed</em>.</h2>
|
|
<span className="count">§ {filtered.length} results</span>
|
|
</div>
|
|
|
|
<div className="archive-list">
|
|
{filtered.map(d => <ArchiveItem key={d.id} item={d} onOpen={onOpen} />)}
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
// Reader screen — doc + ask panel
|
|
const ReaderScreen = ({ doc, onBack }) => {
|
|
const [input, setInput] = React.useState("");
|
|
const [loading, setLoading] = React.useState(false);
|
|
const [thread, setThread] = React.useState([
|
|
{ who: "fenja", body: "This essay returns to the metaphor of the mill three times — once as labor, once as inheritance, once as grief. The terracotta highlights above mark those returns." }
|
|
]);
|
|
|
|
const onSend = () => {
|
|
if (!input.trim()) return;
|
|
const q = input;
|
|
setThread(t => [...t, { who: "user", body: q }]);
|
|
setInput("");
|
|
setLoading(true);
|
|
setTimeout(() => {
|
|
setThread(t => [...t, {
|
|
who: "fenja",
|
|
body: "The mill appears first on page two, as the giantesses' labor — then, in the third section, reframed as a family inheritance passed reluctantly.",
|
|
cite: "§ pp. 2, 7"
|
|
}]);
|
|
setLoading(false);
|
|
}, 1400);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Topbar crumbs={["Archive", "Essays", doc.title]}>
|
|
<IconButton name="bookmark" label="Save"/>
|
|
<IconButton name="star" label="Star"/>
|
|
<Button kind="tertiary" onClick={onBack}>← Back to archive</Button>
|
|
</Topbar>
|
|
|
|
<div className="page">
|
|
<div className="reader">
|
|
<div className="reader-main">
|
|
<h1 className="doc-title">{doc.title}</h1>
|
|
<div className="doc-meta">{doc.meta} · {doc.collection}</div>
|
|
|
|
<p>
|
|
The mill does not stop, and the giantesses do not sleep. <span className="highlight">Fenja and Menja grind what the king asks them to grind</span>, and for a long time this is gold, and for a short time this is an army, and then — the ship founders — it is salt, forever, at the bottom of the sea.
|
|
</p>
|
|
<p>
|
|
What the old poem understands and what the new ones rarely do is that the tool and the hand are the same <span className="highlight">patient object</span>. The mill is not separate from the women who turn it. The archive is not separate from the reader who returns, and returns, and annotates in the margin the third time through.
|
|
</p>
|
|
<p>
|
|
This is the case I want to make for slowness. Not as a posture — the internet has enough postures — but as an epistemic stance. The things worth knowing are the things you come back to.
|
|
</p>
|
|
</div>
|
|
|
|
<aside className="ask-pane">
|
|
<div className="eyebrow">Ask this document</div>
|
|
<h3>Reading alongside you, <em>Fenja</em>.</h3>
|
|
<div className="thread">
|
|
{thread.map((m, i) => (
|
|
<div key={i} className={`msg ${m.who}`}>
|
|
<div className="who">{m.who === "user" ? "You" : "Fenja"}</div>
|
|
<div className="body">
|
|
{m.body}
|
|
{m.cite && <><br/><span className="cite">{m.cite}</span></>}
|
|
</div>
|
|
</div>
|
|
))}
|
|
{loading && <div className="msg fenja"><div className="who">Fenja</div><div className="body" style={{display:'flex',alignItems:'center',gap:10}}><RuneSpinner/> <em>Reading…</em></div></div>}
|
|
</div>
|
|
<Composer value={input} onChange={setInput} onSend={onSend} loading={loading}/>
|
|
</aside>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
Object.assign(window, { ArchiveScreen, ReaderScreen });
|