rebuild(stage-11): deliver deterministic heaven workflows

This commit is contained in:
leefer
2026-07-30 07:08:13 +08:00
parent aa3f02bd59
commit 35ae079de7
49 changed files with 7208 additions and 39 deletions
+63
View File
@@ -0,0 +1,63 @@
import { api } from "./client";
export type ReadingMode = "trend" | "fortune" | "heart";
export type HeavenReading = {
id: number;
mode: ReadingMode;
date: string;
subject_key: string;
result: Record<string, any>;
interpretation: string;
status: "pending" | "complete" | "stopped" | "error";
created_at: string;
};
export type HeavenSetup = {
date: string;
fortune: Record<string, any>;
daily_fortune: HeavenReading | null;
history: HeavenReading[];
};
export type HeavenStreamEvent = {
type: "delta" | "done" | "error";
content?: string;
message?: string;
cached?: boolean;
};
export const heavenApi = {
setup(date: string): Promise<HeavenSetup> {
return api.get(`/heaven/setup?date=${encodeURIComponent(date)}`);
},
trend(query: string, tradeDate: string, manual: Record<string, any> = {}): Promise<any> {
return api.post("/heaven/trend/load", { query, trade_date: tradeDate, manual });
},
fortune(tradeDate: string): Promise<{ reused: boolean; reading: HeavenReading }> {
return api.post("/heaven/fortune", { trade_date: tradeDate });
},
heartLine(tradeDate: string, values: number[]): Promise<any> {
return api.post("/heaven/heart/line", { trade_date: tradeDate, values });
},
completeHeart(tradeDate: string, values: number[]): Promise<any> {
return api.post("/heaven/heart/complete", {
trade_date: tradeDate,
values,
first_thought_confirmed: true,
});
},
readings(mode?: ReadingMode, date?: string): Promise<HeavenReading[]> {
const query = new URLSearchParams();
if (mode) query.set("mode", mode);
if (date) query.set("date", date);
return api.get(`/heaven/readings?${query}`);
},
remove(id: number): Promise<{ deleted: number }> {
return api.delete(`/heaven/readings/${id}`);
},
interpret(
readingId: number,
onEvent: (event: HeavenStreamEvent) => void,
signal?: AbortSignal,
): Promise<void> {
return api.stream("/heaven/interpret", { reading_id: readingId }, onEvent, signal);
},
};
@@ -0,0 +1,127 @@
.heaven-fortune-grid {
display: grid;
grid-template-columns: minmax(var(--s-320), 0.8fr) minmax(0, 1.2fr);
gap: var(--layout-gap);
}
.heaven-calendar-card,
.heaven-phrase-card {
min-height: var(--s-120);
display: flex;
align-items: center;
gap: var(--s-16);
padding: var(--s-16);
}
.heaven-date-seal {
width: var(--s-80);
height: var(--s-80);
display: grid;
place-content: center;
border: var(--s-1) solid var(--color-heaven);
color: var(--color-heaven);
text-align: center;
}
.heaven-date-seal span {
font-family: var(--font-serif);
font-size: var(--font-18);
}
.heaven-calendar-card h2,
.heaven-phrase-card h2 {
margin: var(--s-6) 0;
color: var(--color-heaven);
font-size: var(--font-18);
}
.heaven-phrase-card {
display: grid;
align-content: center;
}
.heaven-qi-layout {
display: grid;
grid-template-columns: minmax(0, 1.4fr) minmax(var(--s-320), 0.6fr);
gap: var(--layout-gap);
}
.heaven-layer-list {
display: grid;
padding: var(--s-8) var(--s-14) var(--s-14);
}
.heaven-layer-row {
min-height: var(--s-64);
display: grid;
grid-template-columns: var(--s-44) minmax(0, 1fr);
align-items: center;
gap: var(--s-12);
border-bottom: var(--s-1) solid var(--color-divider);
}
.heaven-layer-row > span {
color: var(--color-heaven);
font-family: var(--font-serif);
}
.heaven-layer-row p,
.heaven-personal p {
margin-top: var(--s-4);
color: var(--color-text-secondary);
line-height: var(--s-20);
}
.heaven-personal {
align-self: center;
padding: var(--s-16);
}
.heaven-personal h3 {
margin-top: var(--s-8);
color: var(--color-heaven);
font-size: var(--font-17);
}
.heaven-personal small {
display: block;
margin-top: var(--s-12);
color: var(--color-text-faint);
}
.heaven-industry-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: var(--s-8);
padding: 0 var(--s-14) var(--s-14);
}
.heaven-industry-grid > div {
padding: var(--s-10);
border: var(--s-1) solid var(--color-border);
border-radius: var(--control-radius);
}
.heaven-industry-grid strong {
color: var(--color-heaven);
font-family: var(--font-serif);
font-size: var(--font-17);
}
.heaven-industry-grid p {
margin-top: var(--s-6);
color: var(--color-text-secondary);
font-size: var(--font-12);
line-height: var(--s-20);
}
.heaven-fortune-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--s-16);
color: var(--color-text-faint);
font-size: var(--font-11-5);
}
@@ -0,0 +1,182 @@
.heaven-heart-toolbar {
display: flex;
justify-content: flex-end;
}
.heart-still,
.heart-breath {
min-height: var(--s-400);
display: grid;
align-content: center;
justify-items: center;
gap: var(--s-14);
padding: var(--s-24);
text-align: center;
}
.heaven-heart-mark {
width: var(--s-80);
height: var(--s-80);
display: grid;
place-items: center;
border: var(--s-1) solid var(--color-heaven);
border-radius: var(--radius-round);
color: var(--color-heaven);
font-family: var(--font-serif);
font-size: var(--font-32);
}
.heart-still p {
max-width: var(--s-480);
color: var(--color-text-secondary);
line-height: var(--s-22);
}
.heart-ripple {
position: relative;
width: var(--s-180);
height: var(--s-180);
display: grid;
place-items: center;
}
.heart-ripple i {
position: absolute;
inset: var(--s-24);
border: var(--s-1) solid var(--color-heaven);
border-radius: var(--radius-round);
opacity: var(--opacity-muted);
transition: transform 3s linear, opacity 2s linear;
}
.heart-ripple i:nth-child(2) { inset: var(--s-16); opacity: 0.42; }
.heart-ripple i:nth-child(3) { inset: var(--s-8); opacity: 0.2; }
.heart-ripple strong { color: var(--color-heaven); font-family: var(--font-serif); font-size: var(--font-32); }
.heart-ripple.is-inhale i { transform: scale(1.18); }
.heart-ripple.is-pause i { transform: scale(1.18); opacity: var(--opacity-muted); }
.heart-ripple.is-exhale i { transform: scale(0.72); opacity: 0.18; }
.heart-incense {
width: min(100%, var(--s-360));
display: grid;
gap: var(--s-8);
color: var(--color-text-secondary);
}
.heart-incense > div {
height: var(--s-2);
overflow: hidden;
background: var(--color-divider);
}
.heart-incense i {
height: 100%;
display: block;
background: var(--color-heaven);
transition: width var(--duration-fast) linear;
}
.heart-casting-layout {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(var(--s-320), 0.8fr);
gap: var(--layout-gap);
}
.heart-coins-card,
.heart-lines-card {
min-height: var(--s-360);
display: grid;
align-content: center;
gap: var(--s-20);
padding: var(--s-24);
}
.heart-coins-card {
justify-items: center;
}
.heart-coins {
display: flex;
gap: var(--s-20);
perspective: var(--s-400);
}
.heart-coin {
width: var(--s-80);
height: var(--s-80);
display: grid;
place-content: center;
border: var(--s-4) double var(--color-heaven);
border-radius: var(--radius-round);
color: var(--color-heaven);
background: var(--color-heaven-soft);
box-shadow: inset 0 0 0 var(--s-6) var(--color-surface), var(--shadow-card);
text-align: center;
transition: transform var(--duration-normal) var(--ease-standard);
}
.heart-coin.back {
transform: rotateY(180deg);
}
.heart-coin i,
.heart-coin small {
font-style: normal;
transform: inherit;
}
.heart-coin i {
font-family: var(--font-serif);
font-size: var(--font-17);
}
.heart-coins.is-casting .heart-coin {
animation: heaven-coin 0.45s ease-in-out;
}
.heart-thought {
display: grid;
justify-items: center;
gap: var(--s-12);
}
.heart-cast-line {
min-height: var(--s-44);
display: grid;
grid-template-columns: var(--s-44) var(--s-96) minmax(0, 1fr);
align-items: center;
gap: var(--s-12);
border-bottom: var(--s-1) solid var(--color-divider);
color: var(--color-text-secondary);
}
.heart-cast-line em {
grid-column: 2 / -1;
color: var(--color-text-faint);
font-style: normal;
}
.heart-cast-line.is-revealed strong {
color: var(--color-heaven);
}
.heart-result {
min-height: var(--s-360);
display: grid;
grid-template-columns: minmax(var(--s-240), 0.8fr) minmax(0, 1fr) auto;
align-items: center;
gap: var(--s-24);
padding: var(--s-24);
}
.heart-result h2 {
margin: var(--s-8) 0;
color: var(--color-heaven);
}
.heart-result p {
margin-bottom: var(--s-12);
line-height: var(--s-22);
}
+408
View File
@@ -0,0 +1,408 @@
.heaven-page {
position: relative;
min-height: 100%;
font-family: var(--font-sans);
}
:root[data-theme="dark"] .heaven-page::before {
position: fixed;
inset: var(--shell-topbar-height) 0 var(--shell-status-height) var(--shell-sidebar-width);
z-index: 0;
pointer-events: none;
content: "";
opacity: 0.12;
background-image: radial-gradient(circle, var(--color-heaven-star) var(--s-1), transparent var(--s-1));
background-size: var(--s-32) var(--s-32);
}
.heaven-page > * {
position: relative;
z-index: 1;
}
.heaven-page-header h1,
.heaven-mode-tabs strong,
.heaven-panel h2,
.heaven-panel h3 {
font-family: var(--font-serif);
}
.heaven-mode-tabs {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--s-8);
margin-bottom: var(--layout-gap);
padding: var(--s-4);
border: var(--s-1) solid var(--color-border);
border-radius: var(--card-radius);
background: var(--color-surface);
}
.heaven-mode-tabs button {
display: grid;
gap: var(--s-2);
padding: var(--s-8) var(--s-12);
border-radius: var(--control-radius);
color: var(--color-text-secondary);
background: var(--c-transparent);
text-align: left;
}
.heaven-mode-tabs button:hover,
.heaven-mode-tabs button.active {
color: var(--color-heaven);
background: var(--color-heaven-soft);
}
.heaven-mode-tabs strong {
font-size: var(--font-17);
}
.heaven-mode-tabs span {
font-size: var(--font-11-5);
}
.heaven-panel {
display: grid;
gap: var(--layout-gap);
padding-bottom: var(--s-20);
}
.heaven-trend-input {
display: flex;
align-items: end;
gap: var(--s-24);
padding: var(--s-12) var(--s-16);
}
.heaven-stock-field {
width: min(100%, var(--s-480));
}
.heaven-inline-control,
.heaven-dual-input {
display: flex;
gap: var(--s-8);
}
.heaven-inline-control .input,
.heaven-dual-input .input {
min-width: 0;
}
.heaven-current-target {
align-self: center;
color: var(--color-text-secondary);
font-size: var(--font-13);
}
.heaven-current-target strong {
color: var(--color-heaven);
font-size: inherit;
}
.heaven-awaiting {
min-height: var(--s-240);
display: grid;
align-content: center;
justify-items: center;
gap: var(--s-8);
color: var(--color-text-secondary);
text-align: center;
}
.heaven-bagua {
width: var(--s-80);
height: var(--s-80);
display: grid;
place-items: center;
border: var(--s-1) solid var(--color-heaven);
border-radius: var(--radius-round);
color: var(--color-heaven);
font-family: var(--font-serif);
font-size: var(--font-32);
animation: heaven-turn 12s linear infinite;
}
.heaven-trend-grid {
display: grid;
grid-template-columns: minmax(0, 1.25fr) minmax(var(--s-320), 0.75fr);
gap: var(--layout-gap);
}
.heaven-lines-card,
.heaven-outcome-card {
min-height: var(--s-320);
}
.heaven-lines-card .card-header,
.heaven-qi-layers .card-header {
justify-content: space-between;
}
.heaven-line-list {
display: grid;
padding: var(--s-8) var(--s-14) var(--s-14);
}
.heaven-line-row {
min-height: var(--s-40);
display: grid;
grid-template-columns: var(--s-44) var(--s-56) var(--s-80) minmax(0, 1fr) var(--s-44);
align-items: center;
gap: var(--s-8);
border-bottom: var(--s-1) solid var(--color-divider);
color: var(--color-text-secondary);
}
.heaven-line-row strong {
color: var(--color-text);
}
.heaven-line-row small {
text-align: right;
}
.heaven-line-mini,
.heaven-yao {
display: flex;
justify-content: center;
gap: 0;
}
.heaven-line-mini i,
.heaven-yao i {
width: 50%;
height: var(--s-6);
background: var(--color-heaven);
}
.heaven-line-mini:not(.yin) i + i,
.heaven-yao:not(.yin) i + i {
margin-left: calc(var(--s-1) * -1);
}
.heaven-line-mini.yin,
.heaven-yao.yin {
gap: var(--s-8);
}
.heaven-outcome-card {
display: grid;
grid-template-rows: auto 1fr auto auto;
align-items: center;
justify-items: center;
gap: var(--s-8);
padding: var(--s-16);
}
.heaven-momentum {
display: flex;
align-items: baseline;
gap: var(--s-8);
justify-self: stretch;
color: var(--color-text-secondary);
}
.heaven-momentum strong {
color: var(--color-heaven);
font-size: var(--font-32);
}
.heaven-momentum small {
margin-left: auto;
}
.heaven-hex-pair {
display: flex;
align-items: center;
justify-content: center;
gap: var(--s-24);
}
.heaven-hex-symbol {
width: var(--s-96);
display: grid;
justify-items: center;
gap: var(--s-6);
}
.heaven-hex-symbol > strong {
color: var(--color-heaven);
font-family: var(--font-serif);
font-size: var(--font-18);
}
.heaven-hex-lines {
width: var(--s-64);
display: grid;
gap: var(--s-6);
}
.heaven-change-arrow {
color: var(--color-heaven);
font-size: var(--font-24);
}
.heaven-hex-text {
color: var(--color-text-secondary);
line-height: var(--s-20);
text-align: center;
}
.heaven-not-ready {
padding: var(--s-24);
color: var(--color-text-secondary);
text-align: center;
}
.heaven-not-ready strong {
color: var(--color-heaven);
font-family: var(--font-serif);
font-size: var(--font-18);
}
.heaven-section-toggle {
width: 100%;
min-height: var(--s-40);
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--s-8) var(--s-14);
color: var(--color-text);
background: var(--c-transparent);
}
.heaven-check-list {
display: grid;
gap: var(--s-8);
padding: 0 var(--s-14) var(--s-14);
}
.heaven-check {
display: grid;
grid-template-columns: var(--s-160) minmax(0, 1fr) var(--s-80);
gap: var(--s-12);
padding: var(--s-8) var(--s-10);
border-left: var(--s-2) solid var(--color-up);
color: var(--color-text-secondary);
background: var(--color-up-soft);
}
.heaven-check.is-pass {
border-color: var(--color-down);
background: var(--color-down-soft);
}
.heaven-check small {
text-align: right;
}
.heaven-manual-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--s-12);
padding-top: var(--s-8);
border-top: var(--s-1) solid var(--color-divider);
}
.heaven-manual-grid .form-actions {
grid-column: 1 / -1;
}
.heaven-dialog-tabs {
display: flex;
gap: var(--s-4);
margin-bottom: var(--s-12);
border-bottom: var(--s-1) solid var(--color-divider);
}
.heaven-dialog-tabs button {
padding: var(--s-8) var(--s-12);
border-bottom: var(--s-2) solid var(--c-transparent);
color: var(--color-text-secondary);
background: var(--c-transparent);
}
.heaven-dialog-tabs button.active {
color: var(--color-heaven);
border-color: var(--color-heaven);
}
.heaven-loading-stage {
min-height: var(--s-480);
display: grid;
grid-template-rows: minmax(0, 1fr) auto;
gap: var(--s-8);
text-align: center;
}
.heaven-loading-stage canvas {
width: 100%;
height: 100%;
min-height: var(--s-400);
border-radius: var(--control-radius);
}
.heaven-loading-stage p {
color: var(--color-text-secondary);
font-family: var(--font-serif);
}
.heaven-answer {
min-height: var(--s-240);
color: var(--color-text);
line-height: var(--s-24);
white-space: pre-wrap;
}
.heaven-history-list {
display: grid;
gap: var(--s-8);
}
.heaven-history-list button {
display: flex;
justify-content: space-between;
gap: var(--s-12);
padding: var(--s-10) var(--s-12);
border: var(--s-1) solid var(--color-border);
border-radius: var(--control-radius);
color: var(--color-text);
background: var(--color-surface);
text-align: left;
}
@keyframes heaven-turn { to { transform: rotate(360deg); } }
@keyframes heaven-coin { 50% { transform: rotateY(180deg) translateY(calc(var(--s-8) * -1)); } }
@media (max-width: 1023px) {
:root[data-theme="dark"] .heaven-page::before { left: 0; bottom: var(--shell-mobile-nav-height); }
.heaven-mode-tabs button { text-align: center; }
.heaven-mode-tabs span { display: none; }
.heaven-trend-input { display: grid; gap: var(--s-8); }
.heaven-trend-grid,
.heaven-fortune-grid,
.heaven-qi-layout,
.heart-casting-layout,
.heart-result { grid-template-columns: minmax(0, 1fr); }
.heaven-manual-grid { grid-template-columns: minmax(0, 1fr); }
.heaven-industry-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.heaven-check { grid-template-columns: minmax(0, 1fr); }
.heaven-check small { text-align: left; }
.heart-result { justify-items: center; text-align: center; }
.heaven-loading-stage { min-height: var(--s-360); }
.heaven-loading-stage canvas { min-height: var(--s-320); }
}
@media (max-width: 430px) {
.heaven-mode-tabs { gap: var(--s-2); }
.heaven-mode-tabs button { padding-inline: var(--s-4); }
.heaven-inline-control { display: grid; }
.heaven-line-row { grid-template-columns: var(--s-40) var(--s-44) minmax(var(--s-64), 1fr) var(--s-44); }
.heaven-line-row strong { display: none; }
.heaven-hex-pair { gap: var(--s-8); }
.heaven-industry-grid { grid-template-columns: minmax(0, 1fr); }
.heart-coins { gap: var(--s-8); }
.heart-coin { width: var(--s-64); height: var(--s-64); }
}
@@ -41,6 +41,10 @@
--c-night-amber: #e2ad58;
--c-night-amber-soft: #3d3220;
--c-transparent: transparent;
--c-gold-700: #8b6b2f;
--c-gold-500: #c9a55c;
--c-gold-100: #f4ead4;
--c-night-star: #d8e6f4;
/* Primitive dimensions */
--s-1: 1px;
@@ -71,6 +75,13 @@
--s-46: 46px;
--s-56: 56px;
--s-64: 64px;
--s-80: 80px;
--s-96: 96px;
--s-120: 120px;
--s-160: 160px;
--s-180: 180px;
--s-240: 240px;
--s-480: 480px;
--s-200: 200px;
--s-260: 260px;
--s-320: 320px;
@@ -94,6 +105,8 @@
--font-15: 15px;
--font-17: 17px;
--font-18: 18px;
--font-24: 24px;
--font-32: 32px;
--weight-400: 400;
--weight-500: 500;
--weight-600: 600;
@@ -146,6 +159,10 @@
--color-warning: var(--c-amber-700);
--color-warning-soft: var(--c-amber-050);
--color-overlay: rgba(18, 20, 22, 0.46);
--color-heaven: var(--c-gold-700);
--color-heaven-bright: var(--c-gold-500);
--color-heaven-soft: var(--c-gold-100);
--color-heaven-star: var(--c-night-star);
--shadow-card: var(--shadow-card-light);
/* Component tokens */
@@ -187,5 +204,7 @@
--color-warning: var(--c-night-amber);
--color-warning-soft: var(--c-night-amber-soft);
--color-overlay: rgba(0, 0, 0, 0.64);
--color-heaven: var(--c-night-amber);
--color-heaven-soft: var(--c-night-amber-soft);
--shadow-card: var(--shadow-card-dark);
}