diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-21 00:58:15 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-21 00:58:15 +0800 |
| commit | 7b29dbff7e43e87741476f47df17ad1582b16546 (patch) | |
| tree | 4c9d0832b4e4edd77e59e9cb0a88abf5e729a9f9 /public/styles.css | |
| parent | 47add3088d50f12c5f48591f26c53cdb59d8283b (diff) | |
| download | somhairles-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.css | 692 |
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; + } +} |
