④段整套同步(六节 + P/S/F/J + 模板)+ open-design 移到技能根
一、④段整套同步(用户选候选 A)
- 新增 `references/stage-proto-doc/assets/产品文档模板.html`(16 KB/247 行,从备份拷、转 LF)。
- ④段 SKILL.md 换成备份 15:28 那版,并按模板口径改齐:
· 「产品文档框架」从「四块+一附录」改成**六节** —— 页面结构/功能清单/使用场景/操作流程/
跨页与界面状态/待确认与偏离;与模板同构,⛔ 不从空白页重写结构。
· 编号从 R/S/F 改成 **P 页面/S 场景/F 功能/J 二级功能**,各自唯一、按展示顺序通排。
· 保留旧版有价值的要求:每项功能补齐 做什么/入口在哪/输入边界/异常与恢复;重点功能三处标红;
横向流程图每步写清页面/功能/操作/完成判据。
· 完成标准「七节」→「六节」,自查清单同步换掉「需求」那批条目,补模板与配图两条。
· 路径 `designs/<项目>/` → `docs/pm/<项目>/ui/`(2 处)。
· 补回本机那条「每份产出文档开头都要有一张图」的引用(指向主 skill,段内不复述)。
- 主 skill 同步:四段结构表④行、④段沿革、画图小节(④段画页面结构图 + 用户路径)。
二、open-design 移到技能根(用户选候选 A)
- `git mv product-planning/references/stage-delivery/assets/open-design → open-design/`(4178 文件)。
- `.gitignore` 白名单加 `!/open-design/` —— 否则移出去就不再纳入版本控制。
- 引用改 11 处:路径形态 `assets/open-design/...` → `<技能根>/open-design/...`;
「随段携带的自带件」改称「同根技能」;「路径怎么找」那条改成自带件(段内相对路径)与
同根技能(`<技能根>/<技能名>/`)两类分开写。
- open-design 自己的 ATTRIBUTION.md 两处同步(它现在与 oil-ui-pro 同层)。
- 副作用(有益):命名闸门读数由 2063 降到 225 —— 此前它把 open-design 里 919 个第三方 md
当成规则一起扫了;现在只扫本包 23 个 md。已确认④段的 H1 与 4a/4b/4c 子步仍在检查范围内。
三、验收
- check_naming.py --ws E:/ProgramData/AIProject/contentm_agent = 通过 225 / 失败 0 / 提示 27。
- 活文件里 `assets/open-design` 残留 0;工作区 CRLF 文件 0。
This commit is contained in:
1 parent
fa0a9026c8
commit
5afeb6b78a
4184 files changed
+326
-70
No files matched your search
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: trading-analysis-dashboard-template
|
||||
description: |
|
||||
Professional trading analysis dashboard template (single-file HTML) with
|
||||
light/dark theme switch, dense market panels, chart interactions, demo/live
|
||||
playback, and command palette behavior.
|
||||
Use when users ask for a Wall-Street-style analytics terminal, trading cockpit,
|
||||
or high-tech financial dashboard template with realistic data layout.
|
||||
triggers:
|
||||
- "trading analysis dashboard template"
|
||||
- "wall street dashboard template"
|
||||
- "financial terminal template"
|
||||
- "trading cockpit template"
|
||||
- "交易分析面板模板"
|
||||
- "华尔街风格看板"
|
||||
- "高科技金融 dashboard 模板"
|
||||
od:
|
||||
mode: template
|
||||
platform: desktop
|
||||
scenario: live-artifacts
|
||||
preview:
|
||||
type: html
|
||||
entry: index.html
|
||||
reload: debounce-100
|
||||
design_system:
|
||||
requires: true
|
||||
sections: [color, typography, layout, components]
|
||||
outputs:
|
||||
primary: index.html
|
||||
secondary:
|
||||
- template.html
|
||||
- example.html
|
||||
example_prompt: "Create a Wall-Street-grade trading analysis dashboard template with a left rail, risk cockpit, market charts, live/demo mode, and realistic dense data. Keep it single-file HTML."
|
||||
capabilities_required:
|
||||
- file_write
|
||||
---
|
||||
|
||||
# Trading Analysis Dashboard Template
|
||||
|
||||
Produce a premium, data-dense, Wall-Street style trading dashboard as a self-contained HTML artifact.
|
||||
|
||||
## Resource map
|
||||
|
||||
```text
|
||||
trading-analysis-dashboard-template/
|
||||
├── SKILL.md
|
||||
├── assets/
|
||||
│ └── template.html
|
||||
├── references/
|
||||
│ └── checklist.md
|
||||
└── example.html
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Read active `DESIGN.md`, then map typography/color/layout into CSS variables.
|
||||
2. Copy `assets/template.html` to `index.html`.
|
||||
3. Personalize headings, instrument names, and numeric labels to the user brief.
|
||||
4. Preserve interaction fidelity:
|
||||
- Light/Dark mode switch
|
||||
- Live/Demo mode
|
||||
- Chart hover crosshair and tooltip
|
||||
- Click-to-focus chart (floating modal style)
|
||||
- Keyboard command palette (`/`)
|
||||
5. Keep output single-file HTML (inline CSS + inline JS, no framework dependency).
|
||||
6. Keep placeholders honest (`—` or neutral labels) where real numbers are unknown.
|
||||
7. Validate against `references/checklist.md` before emitting.
|
||||
|
||||
## Output contract
|
||||
|
||||
One sentence before artifact, then:
|
||||
|
||||
```xml
|
||||
<artifact identifier="trading-analysis-dashboard" type="text/html" title="Trading Analysis Dashboard">
|
||||
<!doctype html>
|
||||
<html>...</html>
|
||||
</artifact>
|
||||
```
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Trading Analysis Dashboard</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#0a1220;--panel:#0f1b31;--panel2:#13233e;--line:#294161;--text:#edf3ff;--muted:#94a9c7;
|
||||
--accent:#43b6ff;--accent2:#8f8dff;--good:#4be2ad;--bad:#ff7f9f;--warn:#ffd777;--chip:#162843;
|
||||
}
|
||||
html[data-theme="light"]{
|
||||
--bg:#eaf0f8;--panel:#ffffff;--panel2:#f4f8ff;--line:#d6e0ee;--text:#0d1730;--muted:#5f7392;
|
||||
--accent:#1d87cf;--accent2:#7365ff;--good:#1d9b72;--bad:#c1435f;--warn:#ac7a00;--chip:#edf3fb;
|
||||
}
|
||||
*{box-sizing:border-box} html,body{height:100%}
|
||||
body{margin:0;font-family:Inter,system-ui,-apple-system,Segoe UI,sans-serif;background:radial-gradient(1000px 400px at 15% -10%,rgba(77,153,255,.18),transparent),var(--bg);color:var(--text);}
|
||||
.app{display:grid;grid-template-columns:230px 1fr;height:100%}
|
||||
.side{border-right:1px solid var(--line);padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--panel) 90%,transparent),color-mix(in srgb,var(--panel2) 80%,transparent))}
|
||||
.brand{font-weight:800;letter-spacing:.14em;font-size:13px;margin:8px 8px 14px}
|
||||
.nav{display:grid;gap:6px}.nav button{border:0;text-align:left;padding:10px;border-radius:10px;background:transparent;color:var(--muted);font-weight:600;cursor:pointer}
|
||||
.nav button.active,.nav button:hover{background:var(--chip);color:var(--text)}
|
||||
.main{padding:14px;overflow:auto}
|
||||
.top{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap}
|
||||
.status{display:flex;gap:8px;align-items:center;padding:7px 10px;border:1px solid var(--line);border-radius:999px;background:var(--chip);font-size:11px;color:var(--muted)}
|
||||
.dot{width:8px;height:8px;border-radius:999px;background:var(--good)}
|
||||
.controls{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
||||
.seg{display:inline-flex;border:1px solid var(--line);background:var(--chip);border-radius:999px;padding:4px}
|
||||
.seg button{border:0;background:transparent;color:var(--muted);font-size:11px;font-weight:700;padding:7px 10px;border-radius:999px;cursor:pointer}
|
||||
.seg button.active{background:var(--panel);color:var(--text)}
|
||||
.chip{border:1px solid var(--line);background:var(--chip);color:var(--text);padding:7px 10px;border-radius:8px;font-size:11px;font-weight:700;cursor:pointer}
|
||||
.danger{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 35%,var(--line))}
|
||||
.hero{margin-top:10px;display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:10px}
|
||||
.card{border:1px solid var(--line);background:linear-gradient(140deg,color-mix(in srgb,var(--panel) 96%,transparent),color-mix(in srgb,var(--panel2) 94%,transparent));border-radius:14px;padding:12px;position:relative;overflow:hidden}
|
||||
.k{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
|
||||
.v{margin-top:4px;font-size:24px;font-weight:800;font-family:"JetBrains Mono",ui-monospace,monospace}
|
||||
.layout{display:grid;grid-template-columns:1.4fr 1fr;gap:10px;margin-top:10px}
|
||||
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||||
table{width:100%;border-collapse:collapse;font-size:12px} th,td{padding:8px;border-bottom:1px solid var(--line);text-align:left}
|
||||
th{font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
|
||||
.badge{font-size:10px;padding:2px 6px;border-radius:999px;border:1px solid var(--line)}
|
||||
.good{color:var(--good)} .bad{color:var(--bad)}
|
||||
.chart-shell{position:relative}
|
||||
.chart{margin-top:8px;height:200px;border:1px solid var(--line);border-radius:10px;background:color-mix(in srgb,var(--chip) 50%,transparent)}
|
||||
.chart svg{width:100%;height:100%}
|
||||
.chart svg .axis-text{font:600 9px "JetBrains Mono",ui-monospace,monospace;fill:var(--muted);letter-spacing:.04em}
|
||||
.chart svg .axis-title{font:700 9px Inter,system-ui,sans-serif;fill:var(--muted);letter-spacing:.08em;text-transform:uppercase}
|
||||
.legend{display:flex;flex-wrap:wrap;gap:10px 14px;margin-top:6px;padding:0 4px;font-size:10px;color:var(--muted);letter-spacing:.04em}
|
||||
.legend .lg{display:inline-flex;align-items:center;gap:6px;font-weight:700}
|
||||
.legend .lg::before{content:"";width:10px;height:2px;background:var(--lg-c,var(--accent));border-radius:2px;display:inline-block}
|
||||
.cursor{position:absolute;inset:0;pointer-events:none;opacity:0}
|
||||
.chart-shell.active .cursor{opacity:1}
|
||||
.cursor .vx,.cursor .hy{position:absolute;background:color-mix(in srgb,var(--accent) 80%,transparent)}
|
||||
.cursor .vx{width:1px;top:0;bottom:0;left:50%}
|
||||
.cursor .hy{height:1px;left:0;right:0;top:50%;background:color-mix(in srgb,var(--accent2) 70%,transparent)}
|
||||
.cursor .lb{position:absolute;right:8px;top:8px;background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:4px 6px;font:700 10px "JetBrains Mono",monospace}
|
||||
.flow{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:10px}
|
||||
.step{border:1px solid var(--line);border-radius:10px;background:var(--chip);padding:10px}
|
||||
.step h4{margin:0 0 5px;font-size:11px;letter-spacing:.06em;text-transform:uppercase}
|
||||
.step p{margin:0;font-size:11px;color:var(--muted)}
|
||||
.risk{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:10px}
|
||||
.bar{height:6px;border-radius:999px;background:color-mix(in srgb,var(--line) 80%,transparent);overflow:hidden;margin-top:6px}
|
||||
.bar>i{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--accent2));width:var(--w,50%)}
|
||||
.palette{position:fixed;inset:0;background:rgba(5,10,20,.45);backdrop-filter:blur(6px);display:none;align-items:flex-start;justify-content:center;padding-top:15vh;z-index:40}
|
||||
.palette.open{display:flex}
|
||||
.palette-card{width:min(680px,92vw);border:1px solid var(--line);border-radius:12px;background:var(--panel);overflow:hidden}
|
||||
.palette input{width:100%;border:0;border-bottom:1px solid var(--line);padding:12px;background:var(--chip);color:var(--text);font:12px "JetBrains Mono",monospace}
|
||||
.palette button{width:100%;border:0;border-bottom:1px solid var(--line);padding:10px 12px;background:transparent;color:var(--text);text-align:left;cursor:pointer}
|
||||
.focus{position:fixed;inset:0;background:rgba(4,8,16,.6);backdrop-filter:blur(4px);display:none;z-index:30}
|
||||
.focus.show{display:block}
|
||||
.chart-shell.floating{position:fixed!important;z-index:31!important;box-shadow:0 30px 90px rgba(0,0,0,.45)!important}
|
||||
@media (max-width:1150px){.hero,.risk,.flow,.layout,.grid2{grid-template-columns:1fr}.app{grid-template-columns:84px 1fr}.brand span,.nav button span{display:none}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="focus" id="focus"></div>
|
||||
<div class="app">
|
||||
<aside class="side">
|
||||
<div class="brand"><span>PROMETHEUS</span></div>
|
||||
<nav class="nav">
|
||||
<button><span>Home</span></button><button><span>Trade</span></button><button class="active"><span>Analyze</span></button><button><span>Performance</span></button><button><span>Develop</span></button>
|
||||
</nav>
|
||||
</aside>
|
||||
<main class="main">
|
||||
<div class="top">
|
||||
<div class="status"><span class="dot"></span><span>Market Open · NYSE · Source: Cboe One · Latency: <b id="latency">12ms</b></span></div>
|
||||
<div class="controls">
|
||||
<div class="seg" id="mode"><button data-mode="live" class="active">LIVE</button><button data-mode="demo">DEMO</button></div>
|
||||
<select class="chip" id="speed"><option value="0.5">0.5x</option><option value="1" selected>1x</option><option value="2">2x</option></select>
|
||||
<select class="chip" id="scenario"><option value="risk-on" selected>Risk-on</option><option value="vol-crush">Vol Crush</option><option value="panic">Panic</option></select>
|
||||
<button class="chip danger" id="kill">KILL SWITCH</button>
|
||||
<div class="seg" id="theme"><button data-theme="dark" class="active">Dark</button><button data-theme="light">Light</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero">
|
||||
<div class="card"><div class="k">Alpha Pulse</div><div class="v" id="alpha">LONG BIAS · 0.78</div><div class="k" id="alpha-sub">Expected Move +1.9% · Horizon 2h</div></div>
|
||||
<div class="card"><div class="k">Confidence</div><div class="v" id="conf">78.00%</div></div>
|
||||
<div class="card"><div class="k">Risk Budget</div><div class="v" id="risk-budget">$42,500.00</div></div>
|
||||
<div class="card"><div class="k">Execution Window</div><div class="v">09:30-11:30 ET</div></div>
|
||||
</div>
|
||||
|
||||
<section class="layout">
|
||||
<article class="card">
|
||||
<div class="k">Option Chain</div>
|
||||
<table>
|
||||
<thead><tr><th>Time</th><th>Symbol</th><th>Contract</th><th>Premium</th><th>Sweep</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="orow" data-strike="160"><td>07:46:01</td><td>SPY</td><td>375 8/18</td><td>$44,751</td><td><span class="badge good">YES</span></td></tr>
|
||||
<tr class="orow" data-strike="155"><td>07:46:02</td><td>SPY</td><td>370 8/18</td><td>$38,905</td><td><span class="badge">NO</span></td></tr>
|
||||
<tr class="orow" data-strike="150"><td>07:46:03</td><td>QQQ</td><td>452 8/25</td><td>$52,204</td><td><span class="badge good">YES</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flow">
|
||||
<div class="step"><h4>Signal</h4><p id="s1">Flow consensus LONG</p></div>
|
||||
<div class="step"><h4>Risk Check</h4><p id="s2">Exposure within limit</p></div>
|
||||
<div class="step"><h4>Order</h4><p id="s3">AAPL BUY 200 @ 221.30</p></div>
|
||||
<div class="step"><h4>Fill</h4><p id="s4">Waiting...</p></div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="grid2">
|
||||
<div class="card chart-shell">
|
||||
<div class="k">Option Greeks</div>
|
||||
<div class="chart chart-interactive">
|
||||
<svg viewBox="0 0 400 200" role="img" aria-label="Option Greeks: Delta and Gamma vs Strike">
|
||||
<title>Option Greeks vs Strike</title>
|
||||
<g stroke="var(--line)"><line x1="30" y1="170" x2="390" y2="170"/><line x1="30" y1="20" x2="30" y2="170"/></g>
|
||||
<g class="axis-text" text-anchor="middle">
|
||||
<text x="40" y="186">140</text>
|
||||
<text x="120" y="186">145</text>
|
||||
<text x="200" y="186">150</text>
|
||||
<text x="300" y="186">155</text>
|
||||
<text x="380" y="186">160</text>
|
||||
</g>
|
||||
<g class="axis-text" text-anchor="end">
|
||||
<text x="26" y="44">1.0</text>
|
||||
<text x="26" y="96">0.5</text>
|
||||
<text x="26" y="148">0.0</text>
|
||||
<text x="26" y="174">−0.5</text>
|
||||
</g>
|
||||
<text class="axis-title" x="210" y="198" text-anchor="middle">Strike ($)</text>
|
||||
<text class="axis-title" x="-100" y="12" transform="rotate(-90)" text-anchor="middle">Sensitivity (Δ / Γ)</text>
|
||||
<polyline fill="none" stroke="#9b79f4" stroke-width="3" points="40,40 120,62 200,92 300,122 380,146"/>
|
||||
<polyline fill="none" stroke="#67d59b" stroke-width="3" points="40,128 120,124 200,118 300,129 380,126"/>
|
||||
<circle class="link-target strike-150" cx="200" cy="92" r="4" fill="#9b79f4"/>
|
||||
<circle class="link-target strike-155" cx="300" cy="122" r="4" fill="#9b79f4"/>
|
||||
<circle class="link-target strike-160" cx="380" cy="146" r="4" fill="#9b79f4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="legend"><span class="lg" style="--lg-c:#9b79f4">Delta (Δ)</span><span class="lg" style="--lg-c:#67d59b">Gamma (Γ)</span></div>
|
||||
<div class="cursor"><span class="vx"></span><span class="hy"></span><span class="lb">x:0 y:0</span></div>
|
||||
</div>
|
||||
<div class="card chart-shell">
|
||||
<div class="k">Cumulative PnL</div>
|
||||
<div class="chart chart-interactive">
|
||||
<svg viewBox="0 0 400 200" role="img" aria-label="Cumulative PnL by session time">
|
||||
<title>Cumulative PnL by Session Time</title>
|
||||
<g stroke="var(--line)"><line x1="30" y1="170" x2="390" y2="170"/><line x1="30" y1="20" x2="30" y2="170"/></g>
|
||||
<g class="axis-text" text-anchor="middle">
|
||||
<text x="30" y="186">09:30</text>
|
||||
<text x="120" y="186">11:00</text>
|
||||
<text x="210" y="186">12:30</text>
|
||||
<text x="300" y="186">14:00</text>
|
||||
<text x="390" y="186">16:00</text>
|
||||
</g>
|
||||
<g class="axis-text" text-anchor="end">
|
||||
<text x="26" y="62">$24k</text>
|
||||
<text x="26" y="100">$16k</text>
|
||||
<text x="26" y="138">$8k</text>
|
||||
<text x="26" y="174">$0</text>
|
||||
</g>
|
||||
<text class="axis-title" x="210" y="198" text-anchor="middle">Session Time (ET)</text>
|
||||
<text class="axis-title" x="-100" y="12" transform="rotate(-90)" text-anchor="middle">Equity ($, USD)</text>
|
||||
<polyline id="eq" fill="none" stroke="#47cf93" stroke-width="3" points="30,150 90,142 150,130 210,118 270,95 330,78 390,62"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="legend"><span class="lg" style="--lg-c:#47cf93">Equity Curve ($)</span></div>
|
||||
<div class="cursor"><span class="vx"></span><span class="hy"></span><span class="lb">x:0 y:0</span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="k">Risk Cockpit</div>
|
||||
<div class="risk">
|
||||
<div><div class="k">VaR</div><div class="v" id="var">$12,450.00</div><div class="bar"><i style="--w:64%"></i></div></div>
|
||||
<div><div class="k">Beta</div><div class="v" id="beta">1.18</div><div class="bar"><i style="--w:58%"></i></div></div>
|
||||
<div><div class="k">Exposure</div><div class="v" id="expo">68.20%</div><div class="bar"><i style="--w:68%"></i></div></div>
|
||||
<div><div class="k">Max DD</div><div class="v" id="dd">2.40%</div><div class="bar"><i style="--w:24%"></i></div></div>
|
||||
<div><div class="k">Concentration</div><div class="v" id="conc">41.80%</div><div class="bar"><i style="--w:42%"></i></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card"><div class="k">Demo Story</div><div class="v" id="story">Idle</div></div>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div class="palette" id="palette">
|
||||
<div class="palette-card">
|
||||
<input id="cmd" value="/" />
|
||||
<button data-cmd="mode demo">mode demo</button>
|
||||
<button data-cmd="scenario panic">scenario panic</button>
|
||||
<button data-cmd="theme light">theme light</button>
|
||||
<button data-cmd="focus chart">focus chart</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const $ = (s, p = document) => p.querySelector(s);
|
||||
const $$ = (s, p = document) => [...p.querySelectorAll(s)];
|
||||
const focus = $("#focus");
|
||||
let floating = null, placeholder = null, demoTimer = null, stepTimer = null;
|
||||
const n = (v, t) => t === "usd" ? `$${Number(v).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2})}` : t === "pct" ? `${Number(v).toFixed(2)}%` : Number(v).toFixed(2);
|
||||
const clearTimers = () => { if (demoTimer) clearInterval(demoTimer); if (stepTimer) clearTimeout(stepTimer); demoTimer = null; stepTimer = null; };
|
||||
|
||||
// Storage access can throw SecurityError inside sandboxed iframes that allow
|
||||
// scripts but not same-origin (OpenDesign's preview), so persistence is
|
||||
// strictly best-effort and must never abort initialization.
|
||||
const safeGetTheme = () => { try { return localStorage.getItem("od-trading-theme"); } catch { return null; } };
|
||||
const safeSetTheme = (t) => { try { localStorage.setItem("od-trading-theme", t); } catch { /* sandbox: persistence unavailable */ } };
|
||||
const setTheme = (t) => { document.documentElement.setAttribute("data-theme", t); safeSetTheme(t); $$("#theme button").forEach(b => b.classList.toggle("active", b.dataset.theme === t)); };
|
||||
const savedTheme = safeGetTheme(); if (savedTheme) setTheme(savedTheme);
|
||||
$$("#theme button").forEach(b => b.onclick = () => setTheme(b.dataset.theme));
|
||||
|
||||
const updateTick = () => {
|
||||
const speed = Number($("#speed").value || 1), scenario = $("#scenario").value;
|
||||
const sf = scenario === "panic" ? -1.4 : scenario === "vol-crush" ? .4 : 1, j = () => (Math.random() - .5);
|
||||
$("#latency").textContent = `${Math.max(8, Math.round(12 + j() * 8 * speed))}ms`;
|
||||
$("#alpha").textContent = `${scenario === "panic" ? "DEFENSIVE" : "LONG BIAS"} · ${(0.78 + j() * 0.08 * speed).toFixed(2)}`;
|
||||
$("#alpha-sub").textContent = `Expected Move ${scenario === "panic" ? "-" : "+"}${(1.1 + Math.random() * 1.6).toFixed(1)}% · Horizon 2h`;
|
||||
$("#conf").textContent = n(78 + j() * 5 * speed, "pct");
|
||||
$("#risk-budget").textContent = n(42500 + j() * 1800 * speed, "usd");
|
||||
$("#var").textContent = n(12450 + j() * 500 * speed, "usd");
|
||||
$("#expo").textContent = n(68.2 + j() * 2.8 * speed, "pct");
|
||||
const xs = [30,90,150,210,270,330,390], ys = [150,142,130,118,95,78,62].map(v => Math.max(48, Math.min(170, v + j() * 14 * speed * (scenario === "panic" ? 1.6 : 1))));
|
||||
$("#eq").setAttribute("points", xs.map((x,i)=>`${x},${ys[i].toFixed(1)}`).join(" "));
|
||||
};
|
||||
|
||||
const demoStory = () => {
|
||||
const steps = [
|
||||
["1/5 Signal spike detected", () => $("#s1").textContent = "Flow consensus LONG · TRIGGERED"],
|
||||
["2/5 Risk checks passed", () => $("#s2").textContent = "Exposure within limit · PASS"],
|
||||
["3/5 Order routed", () => $("#s3").textContent = "Routing AAPL BUY 200..."],
|
||||
["4/5 Fill confirmed", () => $("#s4").textContent = "Filled @ 221.31 · -0.2bp"],
|
||||
["5/5 PnL + telemetry updated", updateTick]
|
||||
];
|
||||
let i = 0; clearTimers();
|
||||
demoTimer = setInterval(updateTick, Math.max(700, 2200 / Number($("#speed").value || 1)));
|
||||
const run = () => { $("#story").textContent = steps[i][0]; steps[i][1](); i = (i + 1) % steps.length; stepTimer = setTimeout(run, 6000 / Number($("#speed").value || 1)); };
|
||||
run();
|
||||
};
|
||||
|
||||
$$("#mode button").forEach(b => b.onclick = () => {
|
||||
$$("#mode button").forEach(x => x.classList.toggle("active", x === b));
|
||||
if (b.dataset.mode === "demo") demoStory(); else { clearTimers(); $("#story").textContent = "Idle"; }
|
||||
});
|
||||
$("#speed").onchange = () => $("#mode .active")?.dataset.mode === "demo" && demoStory();
|
||||
$("#scenario").onchange = updateTick;
|
||||
$("#kill").onclick = () => { if (!confirm("Engage kill switch?")) return; clearTimers(); $("#s1").textContent = "HALTED"; $("#s2").textContent = "BLOCK"; $("#s3").textContent = "Orders cancelled"; $("#s4").textContent = "Exposure set to 0%"; $("#story").textContent = "Risk halt engaged"; };
|
||||
|
||||
const clearLink = () => { $$(".orow").forEach(r => r.classList.remove("active")); $$(".link-target").forEach(n => n.classList.remove("active")); };
|
||||
$$(".orow").forEach(r => { r.onmouseenter = () => { clearLink(); r.classList.add("active"); $$(".strike-" + r.dataset.strike).forEach(n => n.classList.add("active")); }; r.onmouseleave = clearLink; });
|
||||
|
||||
$$(".chart-shell").forEach(shell => {
|
||||
const cv = $(".cursor", shell), vx = $(".vx", shell), hy = $(".hy", shell), lb = $(".lb", shell);
|
||||
shell.onmousemove = (e) => { const rect = shell.getBoundingClientRect(), x = ((e.clientX-rect.left)/rect.width)*100, y = ((e.clientY-rect.top)/rect.height)*100; shell.classList.add("active"); vx.style.left = `${x}%`; hy.style.top = `${y}%`; lb.textContent = `x:${x.toFixed(1)} y:${(100-y).toFixed(1)}`; };
|
||||
shell.onmouseleave = () => shell.classList.remove("active");
|
||||
shell.onclick = () => {
|
||||
if (floating === shell) { placeholder.parentNode.insertBefore(shell, placeholder); placeholder.remove(); placeholder = null; floating = null; shell.classList.remove("floating"); shell.style = ""; focus.classList.remove("show"); document.body.style.overflow = ""; return; }
|
||||
if (floating) floating.click();
|
||||
const rect = shell.getBoundingClientRect(); placeholder = document.createElement("div"); placeholder.style.width = rect.width + "px"; placeholder.style.height = rect.height + "px"; shell.parentNode.insertBefore(placeholder, shell);
|
||||
floating = shell; shell.classList.add("floating"); shell.style.top = Math.max(24, (innerHeight - rect.height * 1.25) / 2) + "px"; shell.style.left = Math.max(24, (innerWidth - rect.width * 1.25) / 2) + "px"; shell.style.width = Math.min(innerWidth - 48, rect.width * 1.25) + "px"; shell.style.height = Math.min(innerHeight - 48, rect.height * 1.25) + "px"; document.body.appendChild(shell); focus.classList.add("show"); document.body.style.overflow = "hidden";
|
||||
};
|
||||
});
|
||||
focus.onclick = () => floating && floating.click();
|
||||
|
||||
const palette = $("#palette"), cmd = $("#cmd");
|
||||
const exec = (q) => { q = q.toLowerCase(); if (q.includes("theme dark")) setTheme("dark"); if (q.includes("theme light")) setTheme("light"); if (q.includes("mode demo")) $('#mode button[data-mode="demo"]').click(); if (q.includes("mode live")) $('#mode button[data-mode="live"]').click(); if (q.includes("scenario panic")) { $("#scenario").value = "panic"; updateTick(); } if (q.includes("focus chart")) $(".chart-shell")?.click(); palette.classList.remove("open"); };
|
||||
document.addEventListener("keydown", (e) => { if (e.key === "/" && !palette.classList.contains("open")) { e.preventDefault(); palette.classList.add("open"); cmd.focus(); cmd.value = "/"; } else if (e.key === "Escape") { palette.classList.remove("open"); if (floating) floating.click(); } });
|
||||
cmd.onkeydown = (e) => e.key === "Enter" && exec(cmd.value);
|
||||
$$("#palette button").forEach(b => b.onclick = () => exec(b.dataset.cmd || ""));
|
||||
palette.onclick = (e) => e.target === palette && palette.classList.remove("open");
|
||||
|
||||
$("#mode button[data-mode='demo']").click();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Trading Analysis Dashboard Template Example</title>
|
||||
<style>html,body{margin:0;height:100%;background:#0a1220}iframe{width:100%;height:100%;border:0;display:block}</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Iframe-only shell (recvqholN2wcep): the daemon's sandboxed preview
|
||||
route unwraps a body that is EXACTLY one iframe and serves the inner
|
||||
template directly (see iframeOnlyHtmlShellTarget in
|
||||
apps/daemon/src/routes/plugins/assets.ts). The previous wrapper added a
|
||||
header strip above the iframe, which defeated the unwrap — the nested
|
||||
iframe was then blocked by the preview CSP (default-src 'none', no
|
||||
frame-src), so both the live preview and the baked gallery poster
|
||||
rendered a gray broken frame. Keep this file a bare iframe shell, like
|
||||
flowai-live-dashboard-template. -->
|
||||
<iframe src="./assets/template.html" title="Trading Analysis Dashboard Template Example"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Trading Analysis Dashboard Template Checklist
|
||||
|
||||
## P0 (must pass before emitting `<artifact>`)
|
||||
|
||||
- [ ] Single-file HTML output (`<!doctype html>`, inline CSS/JS, no build step).
|
||||
- [ ] Dense trading layout exists: left sidebar + top status + multi-panel body.
|
||||
- [ ] Light/Dark switch works and persists in `localStorage`.
|
||||
- [ ] Live/Demo mode switch works (demo changes at least one KPI and one chart state).
|
||||
- [ ] At least 2 chart regions include:
|
||||
- [ ] axis labels (x and y)
|
||||
- [ ] units / scale hints (e.g., `$`, `%`, `Δ`)
|
||||
- [ ] chart legends naming each plotted series
|
||||
- [ ] At least one chart supports hover crosshair + tooltip-like readout.
|
||||
- [ ] At least one chart supports click-to-focus (floating/overlay) and close.
|
||||
- [ ] Risk cockpit panel shows exposure-style metrics (VaR/Beta/DD or equivalent).
|
||||
- [ ] Trading workflow panel exists (`Signal -> Risk -> Order -> Fill` semantics).
|
||||
- [ ] Placeholder values are honest (`—` if unknown), no fabricated marketing claims.
|
||||
|
||||
## P1 (quality bar)
|
||||
|
||||
- [ ] Typography hierarchy is explicit (display / title / body / mono number styles).
|
||||
- [ ] Spacing rhythm is consistent (8px-ish system or similar).
|
||||
- [ ] Professional financial tone (no toy-style icon overload).
|
||||
- [ ] Theme contrast is readable under both light and dark.
|
||||
- [ ] Motion effects are subtle and non-blocking.
|
||||
|
||||
## P2 (polish)
|
||||
|
||||
- [ ] Command palette (`/`) shortcuts included.
|
||||
- [ ] Demo story sequence (step progression) is visible.
|
||||
- [ ] Tables include state badges (good/neutral/risk).
|
||||
- [ ] Dashboard feels presentation-ready without external assets.
|
||||
Reference in new issue
Block a user