Sets up the Socrata project repo with: docs/ — strategy and design documents - idea.md: full product vision - implementation-plan.md: Phase 0 + Phase 1 MVP plan - phase-0-validation.md: 2-week validation experiment strategy - phase-0-plan.md: concrete Phase 0 build plan - phase-0-results.md: Phase 0 gate outcome — GO for MVP - sysml-modeling.md: metamodel + SE discipline + validation rules - socrates.md: agent character, surfaces, modes, prompts, lifecycle - sync.md: bidirectional text↔diagram sync engineering - design-source/: HTML/CSS/JS handoff bundle from Claude Design phase-0/ — validated harness (CLI, no UI, no DB) - LM Studio (local OpenAI-compatible) generation + detection + judge - PlantUML rendering for SysML model visualization - 10-seed corpus (8 working + 2 holdouts) - 5 corpus runs with iteration history in reports/ - Final gate: 10/10 pass, mean 4.32/5, holdouts validated Phase 1 MVP scope and milestones documented in implementation-plan.md.
115 lines
4.6 KiB
JavaScript
115 lines
4.6 KiB
JavaScript
// SocratesDock — character-forward AI presence. Sigil + thread + active proposal.
|
||
|
||
function SocratesSigil({ size = 44, mood = "thinking" }) {
|
||
// A hand-drawn Greek-feeling monogram (Σ inside a softened laurel).
|
||
return (
|
||
<div className="sigil" style={{ width: size, height: size }}>
|
||
<svg viewBox="0 0 64 64" width={size} height={size}>
|
||
<defs>
|
||
<radialGradient id="sigilGlow" cx="50%" cy="50%" r="50%">
|
||
<stop offset="0%" stopColor="var(--accent-soft)" stopOpacity="0.55" />
|
||
<stop offset="100%" stopColor="var(--accent-soft)" stopOpacity="0" />
|
||
</radialGradient>
|
||
</defs>
|
||
<circle cx="32" cy="32" r="30" fill="url(#sigilGlow)" />
|
||
<circle cx="32" cy="32" r="22" fill="var(--surface-2)" stroke="var(--accent)" strokeWidth="1.25" />
|
||
{/* Laurel hint */}
|
||
<path d="M 12 32 Q 18 18 32 14" fill="none" stroke="var(--accent)" strokeWidth="1" opacity="0.45" />
|
||
<path d="M 52 32 Q 46 46 32 50" fill="none" stroke="var(--accent)" strokeWidth="1" opacity="0.45" />
|
||
{/* Σ */}
|
||
<text x="32" y="40" textAnchor="middle" fontSize="22"
|
||
fontFamily="var(--font-display)" fontWeight="600" fill="var(--accent)">
|
||
Σ
|
||
</text>
|
||
{mood === "thinking" && (
|
||
<circle cx="50" cy="50" r="3" fill="var(--accent)" className="sigil-pulse" />
|
||
)}
|
||
</svg>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function SocratesDock({ data, presence, density }) {
|
||
const compact = density === "compact";
|
||
if (presence === "subtle") {
|
||
return (
|
||
<div className="dock dock-subtle">
|
||
<SocratesSigil size={36} />
|
||
<div className="dock-subtle-count">3</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Build a richer thread that ends with a numbered-options prompt from
|
||
// Socrates, so the user can see how chat-driven choice surfaces.
|
||
const threadWithOptions = [
|
||
...data.socratesThread,
|
||
{
|
||
who: "socrates",
|
||
text: "Given the context-switch decision, three things follow. Pick where to go next:",
|
||
options: [
|
||
{ n: 1, label: "Draft the constraint", sub: "no cross-Assignment retrieval" },
|
||
{ n: 2, label: "Revisit scope_window", sub: "single-valued vs. multi-valued" },
|
||
{ n: 3, label: "Park it", sub: "I'll come back later" },
|
||
],
|
||
},
|
||
];
|
||
|
||
return (
|
||
<aside className={`dock ${presence === "prominent" ? "dock-prominent" : "dock-default"}`}>
|
||
<header className="dock-header">
|
||
<SocratesSigil size={28} />
|
||
<div className="dock-header-text">
|
||
<div className="dock-name">Socrates</div>
|
||
<div className="dock-status">
|
||
<span className="dock-dot" /> 2 open threads
|
||
</div>
|
||
</div>
|
||
<button className="dock-header-action" title="New thread">+</button>
|
||
</header>
|
||
|
||
<section className="dock-thread-wrap">
|
||
<div className="dock-section-label dock-section-label-inline">Active thread · Aristotle</div>
|
||
<div className="dock-thread">
|
||
{threadWithOptions.map((m, i) => (
|
||
<div key={i} className={`bubble bubble-${m.who}`}>
|
||
{m.who === "socrates" && <span className="bubble-sigil">Σ</span>}
|
||
<span className="bubble-body">
|
||
<span className="bubble-text">{m.text}</span>
|
||
{m.options && (
|
||
<div className="bubble-options">
|
||
{m.options.map((o) => (
|
||
<button key={o.n} className="bubble-option" type="button">
|
||
<span className="bubble-option-num">{o.n}</span>
|
||
<span className="bubble-option-text">
|
||
<span className="bubble-option-label">{o.label}</span>
|
||
<span className="bubble-option-sub">{o.sub}</span>
|
||
</span>
|
||
<span className="bubble-option-key">{o.n}</span>
|
||
</button>
|
||
))}
|
||
<div className="bubble-options-hint">
|
||
Press <kbd>1</kbd>–<kbd>3</kbd>, or type a reply
|
||
</div>
|
||
</div>
|
||
)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<footer className="dock-input-wrap">
|
||
<div className="dock-input">
|
||
<span className="dock-input-prompt">›</span>
|
||
<span className="dock-input-placeholder">Reply to Socrates…</span>
|
||
<span className="dock-input-shortcut">⌘↵</span>
|
||
</div>
|
||
</footer>
|
||
</aside>
|
||
);
|
||
}
|
||
|
||
window.SocratesDock = SocratesDock;
|
||
window.SocratesSigil = SocratesSigil;
|