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.
211 lines
8.2 KiB
JavaScript
211 lines
8.2 KiB
JavaScript
// EditorShell — the dual-canvas workspace, themed by the wrapping artboard.
|
||
|
||
function TopBar({ data, presence }) {
|
||
return (
|
||
<header className="topbar">
|
||
<div className="topbar-left">
|
||
<div className="brand">
|
||
<span className="brand-name">Socrata</span>
|
||
</div>
|
||
<div className="breadcrumbs">
|
||
<span className="bc-sep">/</span>
|
||
<span className="bc-item">{data.project.scope}</span>
|
||
<span className="bc-sep">/</span>
|
||
<span className="bc-item bc-active">{data.project.name}</span>
|
||
<span className="bc-branch">
|
||
<span className="bc-branch-glyph">⎇</span> {data.project.branch}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div className="topbar-right">
|
||
<div className="sync-pill">
|
||
<span className="sync-dot" /> model in sync · {data.project.lastSync}
|
||
</div>
|
||
<div className="topbar-divider" />
|
||
<div className="avatar">MC</div>
|
||
</div>
|
||
</header>
|
||
);
|
||
}
|
||
|
||
function LeftRail({ data, focusBlockId, setFocusBlockId, density }) {
|
||
const [collapsed, setCollapsed] = React.useState(false);
|
||
const [open, setOpen] = React.useState({ outline: true, model: true, requirements: true });
|
||
const toggle = (k) => setOpen((s) => ({ ...s, [k]: !s[k] }));
|
||
|
||
if (collapsed) {
|
||
return (
|
||
<nav className="leftrail leftrail-collapsed">
|
||
<button className="rail-collapse-btn" onClick={() => setCollapsed(false)} title="Expand rail">›</button>
|
||
<div className="rail-collapsed-stack">
|
||
<span className="rail-collapsed-tag" title="Outline">OUT</span>
|
||
<span className="rail-collapsed-tag" title="Model · 6 blocks">MOD</span>
|
||
<span className="rail-collapsed-tag" title="Requirements">REQ</span>
|
||
</div>
|
||
</nav>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<nav className="leftrail">
|
||
<div className="rail-section">
|
||
<button className="rail-section-head" onClick={() => toggle("outline")}>
|
||
<span className={`rail-caret ${open.outline ? "rail-caret-open" : ""}`}>▸</span>
|
||
<span className="rail-label">Outline</span>
|
||
</button>
|
||
{open.outline && (
|
||
<ul className="rail-list">
|
||
<li className="rail-item rail-item-active">
|
||
<span className="rail-item-text">Problem framing</span>
|
||
</li>
|
||
<li className="rail-item">
|
||
<span className="rail-item-text">Constraints</span>
|
||
<span className="rail-count rail-count-req">3</span>
|
||
</li>
|
||
<li className="rail-item">
|
||
<span className="rail-item-text">Why now</span>
|
||
</li>
|
||
<li className="rail-item rail-item-muted">
|
||
<span className="rail-item-text">Hypotheses</span>
|
||
<span className="rail-count rail-count-asm">3</span>
|
||
</li>
|
||
<li className="rail-item rail-item-muted">
|
||
<span className="rail-item-text">Open questions</span>
|
||
<span className="rail-count rail-count-q">5</span>
|
||
</li>
|
||
<li className="rail-item rail-item-muted">
|
||
<span className="rail-item-text">Risks</span>
|
||
<span className="rail-count rail-count-risk">2</span>
|
||
</li>
|
||
</ul>
|
||
)}
|
||
</div>
|
||
<div className="rail-section">
|
||
<button className="rail-section-head" onClick={() => toggle("model")}>
|
||
<span className={`rail-caret ${open.model ? "rail-caret-open" : ""}`}>▸</span>
|
||
<span className="rail-label">Model</span>
|
||
</button>
|
||
{open.model && (
|
||
<ul className="rail-list rail-blocks">
|
||
{data.blocks.map((b) => (
|
||
<li
|
||
key={b.id}
|
||
className={`rail-block rail-${b.kind} ${focusBlockId === b.id ? "rail-block-active" : ""}`}
|
||
onMouseEnter={() => setFocusBlockId(b.id)}
|
||
onMouseLeave={() => setFocusBlockId(null)}
|
||
>
|
||
<span className="rail-block-glyph">
|
||
{b.kind === "constraint" ? "{}" : b.kind === "actor" ? "◐" : "▢"}
|
||
</span>
|
||
<span className="rail-block-label">{b.label}</span>
|
||
<span
|
||
className="rail-block-count"
|
||
title={`${b.properties.length} ${b.properties.length === 1 ? "property" : "properties"}`}
|
||
>
|
||
<span className="rail-block-count-glyph">·</span>
|
||
{b.properties.length}
|
||
</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
)}
|
||
</div>
|
||
<div className="rail-section">
|
||
<button className="rail-section-head" onClick={() => toggle("requirements")}>
|
||
<span className={`rail-caret ${open.requirements ? "rail-caret-open" : ""}`}>▸</span>
|
||
<span className="rail-label">Requirements</span>
|
||
</button>
|
||
{open.requirements && (
|
||
<ul className="rail-list">
|
||
<li className="rail-req"><span className="req-tag">REQ-001</span><span className="req-status req-traced" /></li>
|
||
<li className="rail-req"><span className="req-tag">REQ-002</span><span className="req-status req-traced" /></li>
|
||
<li className="rail-req"><span className="req-tag">REQ-003</span><span className="req-status req-untraced" /></li>
|
||
</ul>
|
||
)}
|
||
</div>
|
||
<button className="rail-collapse-btn rail-collapse-btn-bottom" onClick={() => setCollapsed(true)} title="Collapse rail">‹</button>
|
||
</nav>
|
||
);
|
||
}
|
||
|
||
function CanvasHeader({ title, subtitle, right }) {
|
||
return (
|
||
<div className="canvas-header">
|
||
<div>
|
||
<div className="canvas-title">{title}</div>
|
||
<div className="canvas-sub">{subtitle}</div>
|
||
</div>
|
||
<div className="canvas-header-right">{right}</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function EditorShell({ data, density, markupStyle, diagramStyle, presence }) {
|
||
const [focusBlockId, setFocusBlockId] = React.useState(null);
|
||
|
||
return (
|
||
<div className={`shell shell-density-${density} shell-presence-${presence}`}>
|
||
<TopBar data={data} presence={presence} />
|
||
<div className="shell-body">
|
||
<SocratesDock data={data} presence={presence} density={density} />
|
||
|
||
<LeftRail data={data} focusBlockId={focusBlockId} setFocusBlockId={setFocusBlockId} density={density} />
|
||
|
||
<main className="canvases">
|
||
<section className="canvas canvas-text">
|
||
<CanvasHeader
|
||
title="Narrative"
|
||
subtitle="Markup-augmented prose · synced to model"
|
||
right={null}
|
||
/>
|
||
<div className="canvas-scroll">
|
||
<TextCanvas
|
||
data={data}
|
||
density={density}
|
||
markupStyle={markupStyle}
|
||
focusBlockId={focusBlockId}
|
||
setFocusBlockId={setFocusBlockId}
|
||
/>
|
||
</div>
|
||
</section>
|
||
|
||
<div className="canvas-divider" />
|
||
|
||
<section className="canvas canvas-diagram">
|
||
<CanvasHeader
|
||
title="Model"
|
||
subtitle="SysML · 6 blocks · 6 associations · 1 constraint"
|
||
right={
|
||
<div className="canvas-actions">
|
||
<span className="canvas-mode-pill">Fit</span>
|
||
<span className="canvas-mode-pill canvas-mode-active">100%</span>
|
||
<span className="canvas-mode-pill">Layout</span>
|
||
</div>
|
||
}
|
||
/>
|
||
<div className="canvas-scroll canvas-scroll-diagram">
|
||
<DiagramCanvas data={data} density={density} variant={diagramStyle} focusBlockId={focusBlockId} onSelect={setFocusBlockId} />
|
||
<div className="diagram-legend">
|
||
<span><span className="legend-sw legend-block" /> block</span>
|
||
<span><span className="legend-sw legend-actor" /> actor</span>
|
||
<span><span className="legend-sw legend-constraint" /> constraint</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
</div>
|
||
|
||
<footer className="statusbar">
|
||
<span>Socrata · v0.4 · Phase 1</span>
|
||
<span className="status-spacer" />
|
||
<span>3 assumptions open · 2 risks tracked · 1 proposal pending</span>
|
||
<span className="status-spacer" />
|
||
<span>{data.project.owner}</span>
|
||
</footer>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
window.EditorShell = EditorShell;
|