summaryrefslogtreecommitdiff
path: root/public/styles.css
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 00:58:15 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 00:58:15 +0800
commit7b29dbff7e43e87741476f47df17ad1582b16546 (patch)
tree4c9d0832b4e4edd77e59e9cb0a88abf5e729a9f9 /public/styles.css
parent47add3088d50f12c5f48591f26c53cdb59d8283b (diff)
downloadsomhairles-dream-fsharp-7b29dbff7e43e87741476f47df17ad1582b16546.tar.gz
feat(pipeline): add runnable artifact pipeline increment
[Change Nature] - This commit adds the first runnable increment of the F# artifact pipeline: CLI, server, frontend bundle, stage fixture, and tests. It is feature work, not a bug fix. [New Capability] - CLI `run` executes the three-step Blender fixture through the process bridge and writes GLBs, step logs, events, and a manifest; `verify` validates a manifest without Blender. - Server exposes run start, run-scoped SSE, artifact manifest/file routes, and serves the Fable frontend from its build output. - Frontend starts runs, follows run-scoped SSE progress, and links the artifact manifest on completion. [Implementation] - Shared layer holds domain, run state, replay, and artifact contracts; Modeling holds the pipeline, 120s bridge timeout, and result-file protocol; stage/blender/fixture.py owns geometry in Python (bpy owns geometry, F# owns orchestration). - Fable bundle is regenerated into public/ and copied by the server project; solution includes all projects with warnings-as-errors. - README documents build, Fable, server, interpreter/env, fixture limitations, and current verification status. [Impact] - Verified: 40/40 solution tests pass; Release build 0 warnings/errors; real CLI run with bpy 5.0.1 produced 3 GLBs, 3 logs, and a verify-clean manifest; isolated-port server smoke flow succeeded. - Not yet verified: browser end-to-end behavior and production fidelity. Artifacts and caches are gitignored.
Diffstat (limited to 'public/styles.css')
-rw-r--r--public/styles.css692
1 files changed, 692 insertions, 0 deletions
diff --git a/public/styles.css b/public/styles.css
new file mode 100644
index 0000000..0ee02ef
--- /dev/null
+++ b/public/styles.css
@@ -0,0 +1,692 @@
+:root {
+ color-scheme: dark;
+ --ink: #080b0d;
+ --panel: #11171a;
+ --panel-raised: #161e21;
+ --line: #344146;
+ --line-bright: #627177;
+ --ivory: #f0eadb;
+ --muted: #9aa6a8;
+ --gold: #d7a948;
+ --gold-soft: #f0c976;
+ --mint: #a7c7c0;
+ --green: #76c39a;
+ --red: #dc827b;
+ --shadow: rgba(0, 0, 0, 0.28);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html {
+ min-width: 320px;
+ background: var(--ink);
+}
+
+body {
+ min-width: 320px;
+ margin: 0;
+ background: var(--ink);
+ color: var(--ivory);
+ font-family: "Fira Sans", "Segoe UI", sans-serif;
+ font-size: 16px;
+ line-height: 1.5;
+}
+
+button,
+body,
+input {
+ font: inherit;
+}
+
+button {
+ cursor: pointer;
+}
+
+button:focus-visible {
+ outline: 2px solid var(--gold-soft);
+ outline-offset: 3px;
+}
+
+.shell {
+ width: min(1480px, calc(100% - 48px));
+ margin: 0 auto;
+ padding: 34px 0 24px;
+}
+
+.masthead,
+.mission-strip,
+.panel-heading,
+.label-row,
+.timeline-header,
+.timeline-footer,
+.footer-note {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.masthead {
+ gap: 24px;
+ padding-bottom: 30px;
+ border-bottom: 1px solid var(--line);
+}
+
+.brand-lockup h1,
+.panel-heading h2,
+.timeline-header h2 {
+ margin: 0;
+ font-weight: 500;
+ letter-spacing: -0.025em;
+}
+
+.brand-lockup h1 {
+ font-size: clamp(2rem, 4vw, 3.7rem);
+ line-height: 1;
+}
+
+.panel-heading h2,
+.timeline-header h2 {
+ font-size: 1.12rem;
+}
+
+.eyebrow,
+.connection-readout,
+.event-count,
+.mesh-label,
+.replay-state,
+.timeline-version,
+.timeline-current,
+.footer-note,
+.axis-readout,
+.viewport-note {
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ letter-spacing: 0.07em;
+ text-transform: uppercase;
+}
+
+.eyebrow {
+ display: block;
+ margin-bottom: 7px;
+ color: var(--muted);
+ font-size: 0.66rem;
+ letter-spacing: 0.16em;
+}
+
+.connection-readout {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ color: var(--mint);
+ font-size: 0.72rem;
+ white-space: nowrap;
+}
+
+.signal-mark,
+.status-dot {
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--gold);
+ box-shadow: 0 0 0 4px rgba(215, 169, 72, 0.12);
+}
+
+.mission-strip {
+ display: grid;
+ grid-template-columns: 1fr 1fr 2fr;
+ gap: 1px;
+ margin: 20px 0;
+ border: 1px solid var(--line);
+ background: var(--line);
+}
+
+.mission-strip > div {
+ min-height: 76px;
+ padding: 15px 18px;
+ background: var(--panel);
+}
+
+.mission-strip strong {
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.83rem;
+ font-weight: 500;
+ letter-spacing: 0.08em;
+}
+
+.mission-note {
+ text-align: right;
+}
+
+.workspace {
+ display: grid;
+ grid-template-columns: minmax(200px, 0.72fr) minmax(360px, 1.8fr) minmax(220px, 0.82fr);
+ gap: 12px;
+}
+
+.panel {
+ border: 1px solid var(--line);
+ background: var(--panel);
+ box-shadow: 0 16px 36px var(--shadow);
+}
+
+.telemetry-panel,
+.event-panel {
+ min-height: 440px;
+ padding: 20px;
+}
+
+.panel-heading {
+ align-items: flex-start;
+ gap: 16px;
+ padding-bottom: 18px;
+ border-bottom: 1px solid var(--line);
+}
+
+.status-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 5px 9px;
+ border: 1px solid var(--line-bright);
+ color: var(--gold-soft);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.68rem;
+ white-space: nowrap;
+}
+
+.status-chip .status-dot {
+ width: 6px;
+ height: 6px;
+ box-shadow: none;
+}
+
+.status-running .status-dot,
+.status-succeeded .status-dot {
+ background: var(--green);
+}
+
+.status-failed .status-dot,
+.status-stale .status-dot {
+ background: var(--red);
+}
+
+.status-failed,
+.status-stale {
+ color: var(--red);
+}
+
+.status-message {
+ min-height: 56px;
+ margin: 22px 0 28px;
+ color: var(--ivory);
+ font-size: 1.25rem;
+ line-height: 1.35;
+}
+
+.is-hidden {
+ display: none !important;
+}
+
+.run-controls {
+ display: grid;
+ gap: 14px;
+ margin: 0 0 22px;
+ padding: 14px 0 20px;
+ border-top: 1px solid var(--line);
+ border-bottom: 1px solid var(--line);
+}
+
+.control-grid {
+ display: grid;
+ gap: 9px;
+}
+
+.control-grid label,
+.render-toggle {
+ display: grid;
+ gap: 4px;
+ color: var(--muted);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.62rem;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+
+.control-grid input {
+ width: 100%;
+ min-height: 31px;
+ padding: 5px 7px;
+ border: 1px solid var(--line-bright);
+ border-radius: 0;
+ background: var(--panel-raised);
+ color: var(--ivory);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.68rem;
+}
+
+.control-grid input:focus-visible {
+ outline: 2px solid var(--gold-soft);
+ outline-offset: 2px;
+}
+
+.control-actions {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+}
+
+.render-toggle {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ letter-spacing: 0;
+ text-transform: none;
+}
+
+.render-toggle input {
+ accent-color: var(--gold);
+}
+
+.run-start-button,
+.artifact-link {
+ min-height: 34px;
+ padding: 7px 9px;
+ border: 1px solid var(--gold);
+ border-radius: 0;
+ background: transparent;
+ color: var(--gold-soft);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.62rem;
+ text-decoration: none;
+ transition: background 180ms ease, color 180ms ease;
+}
+
+.run-start-button:hover,
+.artifact-link:hover {
+ background: var(--gold);
+ color: var(--ink);
+}
+
+.artifact-link {
+ justify-self: start;
+}
+
+.version-block {
+ padding: 16px 0 22px;
+ border-top: 1px solid var(--line);
+ border-bottom: 1px solid var(--line);
+}
+
+.label-row {
+ color: var(--muted);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.68rem;
+}
+
+.label-row strong {
+ color: var(--gold-soft);
+ font-weight: 500;
+}
+
+.progress-track {
+ height: 4px;
+ margin-top: 14px;
+ background: #293236;
+}
+
+.progress-fill {
+ display: block;
+ height: 100%;
+ background: var(--gold);
+ transition: width 220ms ease;
+}
+
+.telemetry-list {
+ margin: 0;
+}
+
+.telemetry-list > div {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 13px 0;
+ border-bottom: 1px solid #253035;
+}
+
+.telemetry-list dt {
+ color: var(--muted);
+ font-size: 0.76rem;
+}
+
+.telemetry-list dd {
+ max-width: 60%;
+ margin: 0;
+ color: var(--ivory);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.69rem;
+ text-align: right;
+ word-break: break-word;
+}
+
+.viewport-panel {
+ min-height: 440px;
+ padding: 20px 20px 14px;
+}
+
+.viewport-heading {
+ align-items: center;
+}
+
+.mesh-label {
+ color: var(--gold-soft);
+ font-size: 0.68rem;
+ text-align: right;
+}
+
+.viewport-frame {
+ position: relative;
+ height: 370px;
+ margin-top: 16px;
+ overflow: hidden;
+ border: 1px solid var(--line);
+ background: #0b1012;
+}
+
+.viewport-frame::after {
+ position: absolute;
+ inset: 16px;
+ border: 1px solid rgba(167, 199, 192, 0.13);
+ content: "";
+ pointer-events: none;
+}
+
+#viewport-canvas {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+.axis-readout,
+.viewport-note {
+ position: absolute;
+ z-index: 2;
+ color: var(--muted);
+ font-size: 0.6rem;
+ pointer-events: none;
+}
+
+.axis-readout {
+ right: 18px;
+ bottom: 16px;
+}
+
+.viewport-note {
+ left: 18px;
+ bottom: 16px;
+ color: var(--gold-soft);
+}
+
+.viewport-fallback .viewport-frame {
+ display: grid;
+ place-items: center;
+}
+
+.event-count {
+ color: var(--green);
+ font-size: 0.65rem;
+}
+
+.event-list {
+ display: grid;
+ gap: 0;
+ margin: 22px 0 0;
+ padding: 0;
+ list-style: none;
+}
+
+.event-item {
+ display: grid;
+ grid-template-columns: 32px 1fr;
+ gap: 10px;
+ padding: 15px 0;
+ border-bottom: 1px solid #253035;
+}
+
+.event-item-active {
+ border-left: 2px solid var(--gold);
+ padding-left: 10px;
+}
+
+.event-index {
+ color: var(--gold-soft);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.68rem;
+}
+
+.event-item strong,
+.event-item span {
+ display: block;
+}
+
+.event-item strong {
+ font-size: 0.82rem;
+ font-weight: 500;
+}
+
+.event-item div > span {
+ margin-top: 4px;
+ color: var(--muted);
+ font-size: 0.72rem;
+}
+
+.timeline {
+ margin-top: 12px;
+ padding: 20px;
+}
+
+.timeline-header {
+ align-items: flex-end;
+ gap: 16px;
+ padding-bottom: 18px;
+ border-bottom: 1px solid var(--line);
+}
+
+.replay-state {
+ color: var(--muted);
+ font-size: 0.65rem;
+ text-align: right;
+}
+
+.timeline-track {
+ position: relative;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 10px;
+ padding: 30px 0 24px;
+}
+
+.timeline-line {
+ position: absolute;
+ top: 35px;
+ right: 8%;
+ left: 8%;
+ height: 1px;
+ background: var(--line-bright);
+}
+
+.timeline-step {
+ position: relative;
+ display: grid;
+ justify-items: center;
+ gap: 7px;
+ color: var(--muted);
+ text-align: center;
+}
+
+.timeline-dot {
+ z-index: 1;
+ width: 11px;
+ height: 11px;
+ border: 2px solid var(--line-bright);
+ background: var(--panel);
+}
+
+.timeline-step-complete .timeline-dot {
+ border-color: var(--gold);
+ background: var(--gold);
+}
+
+.timeline-version {
+ color: var(--gold-soft);
+ font-size: 0.68rem;
+}
+
+.timeline-label {
+ font-size: 0.78rem;
+}
+
+.timeline-footer {
+ padding-top: 14px;
+ border-top: 1px solid var(--line);
+ color: var(--ivory);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.68rem;
+}
+
+.replay-button {
+ min-height: 44px;
+ padding: 8px 16px;
+ border: 1px solid var(--gold);
+ border-radius: 0;
+ background: transparent;
+ color: var(--gold-soft);
+ font-family: "Fira Code", "SFMono-Regular", Consolas, monospace;
+ font-size: 0.68rem;
+ transition: background 180ms ease, color 180ms ease;
+}
+
+.replay-button:hover {
+ background: var(--gold);
+ color: var(--ink);
+}
+
+.footer-note {
+ padding-top: 18px;
+ color: #687579;
+ font-size: 0.59rem;
+}
+
+@media (max-width: 1080px) {
+ .workspace {
+ grid-template-columns: minmax(190px, 0.7fr) minmax(360px, 1.3fr);
+ }
+
+ .event-panel {
+ grid-column: 1 / -1;
+ min-height: 0;
+ }
+
+ .event-list {
+ grid-template-columns: repeat(3, 1fr);
+ gap: 16px;
+ }
+
+ .event-item {
+ border-bottom: 0;
+ border-top: 1px solid #253035;
+ }
+}
+
+@media (max-width: 700px) {
+ .shell {
+ width: min(100% - 28px, 560px);
+ padding-top: 20px;
+ }
+
+ .masthead {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 18px;
+ padding-bottom: 22px;
+ }
+
+ .connection-readout {
+ font-size: 0.64rem;
+ }
+
+ .mission-strip {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .mission-note {
+ grid-column: 1 / -1;
+ text-align: left;
+ }
+
+ .workspace {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .telemetry-panel,
+ .event-panel,
+ .viewport-panel {
+ min-height: 0;
+ }
+
+ .viewport-frame {
+ height: 320px;
+ }
+
+ .event-list {
+ grid-template-columns: 1fr;
+ }
+
+ .event-item {
+ border-bottom: 1px solid #253035;
+ }
+
+ .timeline-header,
+ .timeline-footer {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .timeline-footer {
+ gap: 14px;
+ }
+
+ .control-actions {
+ align-items: stretch;
+ flex-direction: column;
+ }
+
+ .run-start-button {
+ width: 100%;
+ }
+
+ .replay-button {
+ width: 100%;
+ }
+
+ .footer-note {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 6px;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ scroll-behavior: auto !important;
+ transition-duration: 0.01ms !important;
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ }
+}