Files
Socrates/apps/web/components/seed/SeedScreen.tsx
dtoro a0566ce64c MVP M1–M3: visual port, TipTap text editor, React Flow diagram
apps/web — Next.js 16 + TypeScript + React 19 (no Tailwind)

M1 Visual port
- Manuscript theme + base styles ported verbatim from design-source
- 100dvh layout with internal scroll regions (rail / narrative / dock thread)
- TopBar, LeftRail (collapsible sections + collapsed strip), CanvasHeader,
  StatusBar, EditorShell composing the dual-canvas
- SocratesDock (subtle/default/prominent) with bubbles + numbered options
- Sigil (Σ + laurel) used across editor and seed screens
- SeedScreen with emerging-seed rail, mini-graph, confidence bar, thread,
  typing indicator, input row
- Routes: / (homepage), /editor/[projectId], /seed
- Aristotle fixture in lib/fixtures (ported from data.js)

M2 Real text editor
- TipTap (StarterKit + custom Chip atom inline node + ReactNodeViewRenderer)
- Slash-menu insertion via @tiptap/suggestion with arrow / number-key shortcuts
- ChipFocusContext bridges narrative ↔ diagram hover/selection across the
  TipTap render boundary
- fixtureToDoc converts the fixture narrative → ProseMirror JSON

M3 Real diagram
- React Flow (@xyflow/react) custom node (block / actor / constraint / system)
  matching prototype's softened look
- Custom edge with bezier path + label renderer (association / composition /
  constraint variants)
- Drag-create from a left-side palette via HTML5 DnD + screenToFlowPosition
- NodeInspector panel: edit kind / label / properties (or expression for
  constraints); delete cascades to incident edges
- Bidirectional focus highlighting between chips (narrative) and blocks
  (diagram)
- Removed redundant legend (stereotype labels on nodes serve same purpose)

State for M3 lives in component memory; persistence + bidirectional sync land
in M4–M5.
2026-04-28 22:54:36 +02:00

186 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Seed onboarding screen — two-column layout: emerging-seed rail (left) + Socrates conversation (right).
// Ported from docs/design-source/socrata/project/seed-screen.jsx.
import { Sigil } from "../socrates/Sigil";
interface SeedField {
key: string;
label: string;
value: string;
inferred?: boolean;
}
interface ThreadTurn {
who: "socrates" | "user";
text: string;
}
const fields: SeedField[] = [
{
key: "problem",
label: "Problem",
value: "First-year STEM students disengage in the long tail between lectures and office hours; a self-efficacy gap forms quickly.",
},
{
key: "user",
label: "Target user",
value: "Undergraduates at large public universities, weeks 310 of an intro course.",
},
{
key: "outcome",
label: "Desired outcome",
value: "Students re-engage with material via a low-stakes thinking partner — without producing solutions.",
},
{
key: "hypothesis",
label: "Initial hypothesis",
value: "Students will adopt a tool that explicitly refuses to solve their homework.",
inferred: true,
},
{
key: "constraint",
label: "Constraint",
value: "FERPA tenancy, P50 < 1.2s.",
inferred: true,
},
];
const thread: ThreadTurn[] = [
{
who: "socrates",
text: "Welcome. I'm Socrates. Before we model anything, let me understand what you're really proposing. In one sentence — what is the smallest, most honest version of the problem?",
},
{
who: "user",
text: "Students disengage between lectures because they have nobody to think with at 11pm.",
},
{
who: "socrates",
text: "Good. Two follow-ups. First, who specifically — and why now? Second, when you say 'think with', do you mean a tutor that explains, or a partner that asks? These are quite different products.",
},
{
who: "user",
text: "Public-university undergrads, weeks 310. A partner that asks. The market is saturated with explainers.",
},
{
who: "socrates",
text: "Then the central tension is restraint: a tool that holds its tongue. Most LLM products are rewarded for being helpful. Yours will be rewarded for being patient. Should I draft this as a Constraint on the model — refusal_policy : single-valued — and surface it for your review?",
},
];
export function SeedScreen() {
return (
<div className="seed-screen">
<header className="seed-top">
<div className="seed-top-left">
<Sigil size={28} />
<span className="seed-brand">Socrata</span>
<span className="seed-pip">·</span>
<span className="seed-step">Seed · forming</span>
</div>
<div className="seed-top-right">
<span className="seed-mode-pill seed-mode-active">Interview</span>
<span className="seed-mode-pill">Form</span>
</div>
</header>
<div className="seed-body">
{/* Left: emerging seed */}
<section className="seed-left">
<div className="seed-section-label">Emerging seed</div>
<div className="seed-fields">
{fields.map(f => (
<div key={f.key} className={`seed-field ${f.inferred ? "seed-field-inferred" : ""}`}>
<div className="seed-field-label">
{f.label}
{f.inferred && <span className="seed-conf">inferred · 0.74</span>}
</div>
<div className="seed-field-value">{f.value}</div>
</div>
))}
</div>
<div className="seed-section-label seed-section-label-2">Initial model · drafting</div>
<div className="seed-mini-graph">
<div className="mini-block mini-block-1">
<span className="mini-stereo">«block»</span>
<span className="mini-name">Student</span>
<span className="mini-prop">self_efficacy</span>
</div>
<div className="mini-edge" />
<div className="mini-block mini-block-2 mini-block-focus">
<span className="mini-stereo">«block»</span>
<span className="mini-name">Aristotle</span>
<span className="mini-prop">refusal_policy</span>
<span className="mini-prop">interaction_style</span>
</div>
<div className="mini-edge mini-edge-down" />
<div className="mini-block mini-block-3">
<span className="mini-stereo">«constraint»</span>
<span className="mini-name">FERPA boundary</span>
</div>
</div>
<div className="seed-confidence">
<div className="seed-confidence-row">
<span>Model confidence</span>
<span>0.62</span>
</div>
<div className="seed-confidence-bar">
<div className="seed-confidence-fill" style={{ width: "62%" }} />
</div>
<div className="seed-confidence-hint">
Three more clarifying questions should bring this above 0.80.
</div>
</div>
</section>
{/* Right: Socrates conversation */}
<section className="seed-right">
<div className="seed-thread">
{thread.map((m, i) => (
<div key={i} className={`seed-bubble seed-bubble-${m.who}`}>
{m.who === "socrates" && (
<div className="seed-bubble-avatar">
<Sigil size={32} />
</div>
)}
<div className="seed-bubble-body">
<div className="seed-bubble-who">{m.who === "socrates" ? "Socrates" : "You"}</div>
<div className="seed-bubble-text">{m.text}</div>
</div>
</div>
))}
<div className="seed-bubble seed-bubble-socrates seed-bubble-typing">
<div className="seed-bubble-avatar">
<Sigil size={32} />
</div>
<div className="seed-bubble-body">
<div className="seed-bubble-who">Socrates</div>
<div className="seed-typing">
<span /><span /><span /> drafting next question
</div>
</div>
</div>
</div>
<div className="seed-input-row">
<div className="seed-input">
<span className="seed-input-prompt"></span>
<span className="seed-input-text">
Public-university undergrads, weeks 310. A partner that asks. The market is saturated with explainers.
</span>
<span className="seed-input-caret" />
</div>
<div className="seed-input-actions">
<button className="seed-btn" type="button">Save draft</button>
<button className="seed-btn seed-btn-primary" type="button">Send · </button>
</div>
</div>
</section>
</div>
</div>
);
}