Redesign home command deck #13
10 changed files with 1386 additions and 3 deletions
|
|
@ -1,3 +1,4 @@
|
|||
{"_type":"issue","id":"islandflow-2op","title":"[bug] Desktop app unclickable and no live data in hosted shell","description":"## Summary\\nDesktop Electron shell appears fully non-interactive (clicks do not work) and no live market data reaches the UI.\\n\\n## Why this matters\\nDesktop wrapper is currently unusable for core workflow and blocks users from validating market streams outside browser.\\n\\n## Scope\\nReproduce issue locally, identify root cause(s) in Electron shell and frontend integration, implement fix, and validate interactivity + data flow end-to-end.\\n\\n## Acceptance Criteria\\n- Desktop app responds to pointer interactions (navigation/actions clickable)\\n- Live data stream connects and updates UI in desktop mode\\n- Regression coverage or guardrails added where practical\\n- Findings and validation documented","status":"in_progress","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-24T04:23:55Z","created_by":"dirtydishes","updated_at":"2026-05-24T04:23:57Z","started_at":"2026-05-24T04:23:57Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-jad","title":"Sync docs pages workflow fix to github mirror","description":"GitHub is still running an older docs Pages workflow with configure-pages because github/main is behind forgejo/main. Push the already-fixed workflow commit to the GitHub mirror so Actions runs the gh-pages branch deployment flow instead.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-23T22:27:46Z","created_by":"dirtydishes","updated_at":"2026-05-23T22:28:24Z","started_at":"2026-05-23T22:28:10Z","closed_at":"2026-05-23T22:28:24Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-bc7","title":"Fix docs Pages workflow configure-pages failure","description":"Replace the current docs Pages deployment flow so workflow runs succeed even when configure-pages cannot read or enable the site. Keep published docs target behavior for dirtydishes.github.io/islandflow/docs.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-23T22:23:28Z","created_by":"dirtydishes","updated_at":"2026-05-23T22:25:19Z","started_at":"2026-05-23T22:23:31Z","closed_at":"2026-05-23T22:25:19Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-3o0","title":"address forgejo issue #10 security dependency cves","description":"Track remediation for Forgejo issue #10 (2026-05-23 security CVE triage): upgrade dependency chain to resolve tar/ws/postcss/tmp advisories, validate with bun audit and relevant quality gates.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-23T16:59:34Z","created_by":"dirtydishes","updated_at":"2026-05-23T17:03:06Z","started_at":"2026-05-23T16:59:38Z","closed_at":"2026-05-23T17:03:06Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
@ -23,6 +24,7 @@
|
|||
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-4xb","title":"Create dashboard structure mock routes","description":"Prototype four alternate islandflow dashboard structures at /mock1 through /mock4 based on the supplied reference so the main dashboard direction can be evaluated live.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-28T08:30:33Z","created_by":"dirtydishes","updated_at":"2026-05-28T08:38:35Z","started_at":"2026-05-28T08:30:39Z","closed_at":"2026-05-28T08:38:35Z","close_reason":"Added four dashboard mock routes, documented the implementation, and validated build/tests plus route responses.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-1gq","title":"Set up Forgejo-native CI baseline","description":"Create a Forgejo-native CI workflow under .forgejo/workflows that runs the existing fast, high-signal validation checks on pull requests, pushes to main, and manual dispatch. Document the runner label expectations, scope of the job, and manual rerun path in repository docs. Keep heavier container/integration work out of the initial PR gate.","status":"closed","priority":2,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-24T00:31:55Z","created_by":"dirtydishes","updated_at":"2026-05-24T00:36:03Z","closed_at":"2026-05-24T00:36:03Z","close_reason":"Implemented a Forgejo-native CI baseline under .forgejo/workflows, documented runner expectations in the README, and synced the docker workspace snapshot so the fast validate path passes.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-7ez","title":"rename tape to options and replace web rail with drawer shell","description":"Implement the web and desktop route transition from /tape to /options, keep /tape as a compatibility redirect, replace the persistent web rail with a shared sticky header plus overlay drawer, and update validation/docs to match.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-23T23:30:06Z","created_by":"dirtydishes","updated_at":"2026-05-23T23:38:59Z","started_at":"2026-05-23T23:30:24Z","closed_at":"2026-05-23T23:38:59Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-hoh","title":"clarify turn-doc exemptions and ambiguity rule","description":"Update AGENTS.md turn documentation rules so minor/trivial checklist takes precedence, ambiguous cases require user check-in, and completion rule applies only when turn docs are required.","status":"closed","priority":2,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-23T23:02:10Z","created_by":"dirtydishes","updated_at":"2026-05-23T23:02:30Z","closed_at":"2026-05-23T23:02:30Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
@ -72,7 +74,7 @@
|
|||
{"_type":"issue","id":"islandflow-dod","title":"Publish terminal audit to GitHub Pages","description":"Why this issue exists and what needs to be done: publish the generated terminal audit HTML to dirtydishes.github.io at /terminal-audit.html so it can be shared publicly.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:39:45Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:42:59Z","started_at":"2026-05-14T08:40:02Z","closed_at":"2026-05-14T08:42:59Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-dxu","title":"Document terminal audit findings as HTML","description":"Why this issue exists and what needs to be done: capture the completed terminal view audit findings in a user-readable HTML document under docs/ with the full score summary and all detailed findings preserved.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:32:22Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:34:57Z","started_at":"2026-05-14T08:32:30Z","closed_at":"2026-05-14T08:34:57Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-a50","title":"Add HTML plan docs for synthetic tape redesign","description":"Create two HTML planning docs under plans/: one straightforward end-user readable version and one more polished impeccable-style version, both covering the hosted synthetic tape redesign with summary, scope, affected services, UI notes, rollout, tests, and the full detailed implementation plan.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T02:47:44Z","created_by":"dirtydishes","updated_at":"2026-05-14T02:53:11Z","started_at":"2026-05-14T02:47:48Z","closed_at":"2026-05-14T02:53:11Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-932","title":"Desktop follow-up native features","description":"Track deferred native desktop features after the thin hosted-wrapper v1 lands: notifications, keyboard shortcuts, local preferences storage, remembered window state, signed/notarized macOS distribution, auto-update evaluation, and optional local frontend bundling.\n","status":"open","priority":2,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-13T13:20:12Z","created_by":"dirtydishes","updated_at":"2026-05-13T13:20:12Z","dependencies":[{"issue_id":"islandflow-932","depends_on_id":"islandflow-9ug","type":"discovered-from","created_at":"2026-05-13T09:20:12Z","created_by":"auto-import","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-932","title":"Desktop follow-up native features","description":"Track deferred native desktop features after the thin hosted-wrapper v1 lands: notifications, keyboard shortcuts, local preferences storage, remembered window state, signed/notarized macOS distribution, auto-update evaluation, and optional local frontend bundling.\n","status":"in_progress","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-13T13:20:12Z","created_by":"dirtydishes","updated_at":"2026-05-24T04:23:46Z","started_at":"2026-05-24T04:23:46Z","dependencies":[{"issue_id":"islandflow-932","depends_on_id":"islandflow-9ug","type":"discovered-from","created_at":"2026-05-13T09:20:12Z","created_by":"auto-import","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-vbk","title":"Remove deprecated Alpaca key-pair auth","description":"Remove legacy Alpaca key-pair authentication support and keep ALPACA_API_KEY as the only supported auth method across options/equities ingest and docs.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:19:51Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:21:10Z","started_at":"2026-05-05T07:19:54Z","closed_at":"2026-05-05T07:21:10Z","close_reason":"Removed key-pair auth and kept ALPACA_API_KEY only","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-h47","title":"Support single-token Alpaca auth","description":"Support single-token Alpaca authentication across ingest adapters using ALPACA_API_KEY with fallback to ALPACA_KEY_ID/ALPACA_SECRET_KEY, and document env usage.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:12:22Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:13:54Z","started_at":"2026-05-05T07:12:25Z","closed_at":"2026-05-05T07:13:54Z","close_reason":"Added ALPACA_API_KEY support with key-pair fallback","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-neu","title":"Add Alpha Vantage event calendar provider","description":"Add an Alpha Vantage earnings-calendar provider to services/refdata that fetches CSV, normalizes entries, writes the JSON cache consumed by compute, and documents the required env variables.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:00:31Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:02:30Z","started_at":"2026-05-05T07:00:37Z","closed_at":"2026-05-05T07:02:30Z","close_reason":"Added Alpha Vantage event-calendar provider","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
@ -97,3 +99,4 @@
|
|||
{"_type":"issue","id":"islandflow-zsy","title":"Expose Forgejo SSH on a direct DNS hostname","description":"git.deltaisland.io currently resolves through Cloudflare's proxy, so SSH on port 2222 does not complete even though the Forgejo container is listening on the host. If SSH-based git/beads workflows are desired, add a DNS-only hostname (or adjust the existing record) that points directly at the server for Forgejo SSH.","status":"open","priority":3,"issue_type":"task","created_at":"2026-05-17T10:34:06Z","created_by":"delta","updated_at":"2026-05-17T10:34:06Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-38p","title":"Add native deployment unit templates and rollback helpers","description":"The deploy helper now supports --runtime native, but the repo still relies on operator-managed systemd units and manual rollback. Add checked-in native deployment templates or provisioning guidance for the expected units, and consider lightweight rollback/smoke-test helpers once the host-native path is exercised on the real VPS.","status":"open","priority":3,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-15T23:46:42Z","created_by":"dirtydishes","updated_at":"2026-05-15T23:46:42Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-575","title":"Document smart-money event calendar env","description":"Document smart-money event-calendar environment configuration in env examples and README.\n","status":"closed","priority":3,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T06:57:14Z","created_by":"dirtydishes","updated_at":"2026-05-05T06:57:57Z","started_at":"2026-05-05T06:57:17Z","closed_at":"2026-05-05T06:57:57Z","close_reason":"Documented event-calendar env variables","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-1tu","title":"Publish 2026-05-24 standup summary","description":"Why this issue exists and what needs to be done\n\nCreate the daily standup summary for git activity on 2026-05-24, grounded in commits and touched files, then store the HTML report in docs/general.","status":"closed","priority":4,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-25T13:02:56Z","created_by":"dirtydishes","updated_at":"2026-05-25T13:04:31Z","closed_at":"2026-05-25T13:04:31Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
|
|||
458
apps/web/app/dashboard-mocks.tsx
Normal file
458
apps/web/app/dashboard-mocks.tsx
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
import Link from "next/link";
|
||||
import type { CSSProperties, ReactNode } from "react";
|
||||
|
||||
type MockVariant = "mock1" | "mock2" | "mock3" | "mock4";
|
||||
|
||||
type DashboardMockProps = {
|
||||
variant: MockVariant;
|
||||
};
|
||||
|
||||
const variants: Record<
|
||||
MockVariant,
|
||||
{
|
||||
title: string;
|
||||
premise: string;
|
||||
mode: string;
|
||||
layout: string;
|
||||
}
|
||||
> = {
|
||||
mock1: {
|
||||
title: "Command Deck",
|
||||
premise: "Closest to the reference: left navigation, ticker ribbon, dense evidence panes, replay rail.",
|
||||
mode: "Dense ops",
|
||||
layout: "classic"
|
||||
},
|
||||
mock2: {
|
||||
title: "Investigation Stack",
|
||||
premise: "A calmer analyst layout with the selected symbol story in the center and context wrapped around it.",
|
||||
mode: "Forensic",
|
||||
layout: "focus"
|
||||
},
|
||||
mock3: {
|
||||
title: "Signal Wall",
|
||||
premise: "Prioritizes alert triage and cross-symbol scanning before a user drills into price action.",
|
||||
mode: "Triage",
|
||||
layout: "signals"
|
||||
},
|
||||
mock4: {
|
||||
title: "Replay Lab",
|
||||
premise: "A replay-first structure with timeline, event tape, and causality context always visible.",
|
||||
mode: "Replay",
|
||||
layout: "replay"
|
||||
}
|
||||
};
|
||||
|
||||
const tickers = [
|
||||
["SPY", "529.18", "+0.23%", "up"],
|
||||
["QQQ", "452.47", "+0.31%", "up"],
|
||||
["AAPL", "194.88", "+1.22%", "up"],
|
||||
["NVDA", "120.19", "-0.41%", "down"],
|
||||
["TSLA", "180.72", "+0.72%", "up"],
|
||||
["AMZN", "186.31", "+0.35%", "up"],
|
||||
["IWM", "205.41", "+0.21%", "up"]
|
||||
];
|
||||
|
||||
const optionRows = [
|
||||
["2m", "AAPL", "May 17", "195 C", "5,240", "$2.31M", "Sweep", "Bullish"],
|
||||
["3m", "AAPL", "Jun 21", "200 C", "6,800", "$1.87M", "Block", "Bullish"],
|
||||
["4m", "NVDA", "May 24", "120 C", "9,150", "$2.01M", "Split", "Bullish"],
|
||||
["5m", "TSLA", "Jul 19", "205 C", "10,000", "$3.45M", "Block", "Bullish"],
|
||||
["6m", "AMZN", "May 17", "185 P", "4,500", "$1.20M", "Sweep", "Bearish"],
|
||||
["7m", "IWM", "Jun 21", "207 C", "3,100", "$712K", "Sweep", "Bullish"],
|
||||
["8m", "AAPL", "May 24", "197.5 C", "7,600", "$2.01M", "Block", "Bullish"]
|
||||
];
|
||||
|
||||
const signals = [
|
||||
["09:41:10", "Dark Flow Sweep", "AAPL", "$4.32M", "Bullish"],
|
||||
["09:40:58", "Unusual Options Activity", "NVDA", "$2.01M", "Bullish"],
|
||||
["09:40:21", "News Catalyst", "AAPL", "AI update", "News"],
|
||||
["09:39:47", "Classifier Hit: Momentum", "TSLA", "91%", "Bullish"],
|
||||
["09:39:12", "Large Block Trade", "AMZN", "$3.67M", "Bearish"]
|
||||
];
|
||||
|
||||
const feedHealth = [
|
||||
["OPRA Options", "Healthy", "120ms", "2,341"],
|
||||
["CBOE Quotes", "Healthy", "85ms", "1,987"],
|
||||
["Nasdaq TotalView", "Healthy", "92ms", "3,102"],
|
||||
["NYSE Pillar", "Degraded", "412ms", "932"],
|
||||
["News", "Healthy", "1.2s", "12"],
|
||||
["Dark Pool", "Healthy", "1.0s", "421"]
|
||||
];
|
||||
|
||||
const darkFlow = [
|
||||
["09:41:05", "AAPL", "Buy", "25,000", "$4.87M", "Sweep"],
|
||||
["09:40:51", "AAPL", "Buy", "18,500", "$3.60M", "Sweep"],
|
||||
["09:40:35", "AAPL", "Sell", "30,000", "$5.84M", "Block"],
|
||||
["09:39:59", "AAPL", "Buy", "12,000", "$2.34M", "Sweep"],
|
||||
["09:38:47", "AAPL", "Sell", "21,000", "$4.09M", "Block"]
|
||||
];
|
||||
|
||||
const variantOrder: MockVariant[] = ["mock1", "mock2", "mock3", "mock4"];
|
||||
|
||||
export function DashboardMock({ variant }: DashboardMockProps) {
|
||||
const config = variants[variant];
|
||||
|
||||
return (
|
||||
<section className={`mock-terminal mock-terminal-${config.layout}`} aria-labelledby="mock-title">
|
||||
<MockHeader config={config} active={variant} />
|
||||
<TickerRail />
|
||||
{variant === "mock1" ? <ClassicLayout /> : null}
|
||||
{variant === "mock2" ? <FocusLayout /> : null}
|
||||
{variant === "mock3" ? <SignalLayout /> : null}
|
||||
{variant === "mock4" ? <ReplayLayout /> : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function MockHeader({
|
||||
config,
|
||||
active
|
||||
}: {
|
||||
config: (typeof variants)[MockVariant];
|
||||
active: MockVariant;
|
||||
}) {
|
||||
return (
|
||||
<header className="mock-header">
|
||||
<div className="mock-brand-lockup">
|
||||
<span className="mock-mark" aria-hidden="true" />
|
||||
<div>
|
||||
<span className="mock-brand">islandflow</span>
|
||||
<h1 id="mock-title">{config.title}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<p>{config.premise}</p>
|
||||
<div className="mock-header-tools">
|
||||
<span className="mock-live-dot">Live</span>
|
||||
<span className="mock-system">NATS 3ms / US-EAST-1</span>
|
||||
<span className="mock-clock">09:41:23 ET</span>
|
||||
<span className="mock-mode">{config.mode}</span>
|
||||
</div>
|
||||
<nav className="mock-switcher" aria-label="Mock variants">
|
||||
{variantOrder.map((item, index) => (
|
||||
<Link
|
||||
aria-current={item === active ? "page" : undefined}
|
||||
className={item === active ? "is-active" : ""}
|
||||
href={`/${item}`}
|
||||
key={item}
|
||||
>
|
||||
Mock {index + 1}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
function TickerRail() {
|
||||
return (
|
||||
<div className="mock-ticker-rail" aria-label="Live symbol ticker">
|
||||
<div className="mock-ticker-track">
|
||||
{[...tickers, ...tickers].map(([symbol, price, move, direction], index) => (
|
||||
<article className="mock-ticker-card" key={`${symbol}-${index}`}>
|
||||
<div>
|
||||
<strong>{symbol}</strong>
|
||||
<span>{price}</span>
|
||||
</div>
|
||||
<span className={`mock-move is-${direction}`}>{move}</span>
|
||||
<Sparkline direction={direction} />
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ClassicLayout() {
|
||||
return (
|
||||
<div className="mock-dashboard-grid mock-grid-classic">
|
||||
<OptionTape />
|
||||
<ChartPanel />
|
||||
<SignalPanel />
|
||||
<FeedHealth />
|
||||
<DarkFlow />
|
||||
<EventContext />
|
||||
<ReplayRail compact />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FocusLayout() {
|
||||
return (
|
||||
<div className="mock-dashboard-grid mock-grid-focus">
|
||||
<SymbolBrief />
|
||||
<ChartPanel />
|
||||
<EventContext />
|
||||
<OptionTape condensed />
|
||||
<SignalPanel />
|
||||
<DarkFlow />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SignalLayout() {
|
||||
return (
|
||||
<div className="mock-dashboard-grid mock-grid-signals">
|
||||
<SignalPanel hero />
|
||||
<OptionTape />
|
||||
<ChartPanel compact />
|
||||
<FeedHealth />
|
||||
<EventContext />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ReplayLayout() {
|
||||
return (
|
||||
<div className="mock-dashboard-grid mock-grid-replay">
|
||||
<ReplayRail />
|
||||
<ChartPanel />
|
||||
<EventContext />
|
||||
<OptionTape condensed />
|
||||
<SignalPanel />
|
||||
<DarkFlow />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Panel({
|
||||
title,
|
||||
meta,
|
||||
className = "",
|
||||
children
|
||||
}: {
|
||||
title: string;
|
||||
meta?: string;
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className={`mock-panel ${className}`} aria-label={title}>
|
||||
<div className="mock-panel-head">
|
||||
<h2>{title}</h2>
|
||||
{meta ? <span>{meta}</span> : null}
|
||||
</div>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function OptionTape({ condensed = false }: { condensed?: boolean }) {
|
||||
const rows = condensed ? optionRows.slice(0, 5) : optionRows;
|
||||
|
||||
return (
|
||||
<Panel title="Option Flow Tape" meta="250+ shown" className="mock-option-tape">
|
||||
<div className="mock-table mock-table-options">
|
||||
<div className="mock-table-row mock-table-head">
|
||||
<span>Time</span>
|
||||
<span>Symbol</span>
|
||||
<span>Exp</span>
|
||||
<span>Strike</span>
|
||||
<span>Size</span>
|
||||
<span>Prem</span>
|
||||
<span>Type</span>
|
||||
<span>Score</span>
|
||||
</div>
|
||||
{rows.map((row) => (
|
||||
<div className="mock-table-row" key={`${row[0]}-${row[1]}-${row[3]}`}>
|
||||
{row.map((cell, index) => (
|
||||
<span
|
||||
className={
|
||||
index === 6
|
||||
? "mock-pill is-info"
|
||||
: index === 7
|
||||
? `mock-pill ${cell === "Bearish" ? "is-bearish" : "is-bullish"}`
|
||||
: ""
|
||||
}
|
||||
key={cell}
|
||||
>
|
||||
{cell}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function ChartPanel({ compact = false }: { compact?: boolean }) {
|
||||
return (
|
||||
<Panel title="AAPL | Price & Flow" meta="1m / 5m / 15m" className={compact ? "mock-chart is-compact" : "mock-chart"}>
|
||||
<div className="mock-chart-meta">
|
||||
<strong>194.88</strong>
|
||||
<span className="mock-move is-up">+2.34 (+1.22%)</span>
|
||||
</div>
|
||||
<div className="mock-candle-field" aria-hidden="true">
|
||||
{Array.from({ length: 58 }).map((_, index) => (
|
||||
<span
|
||||
className={index % 7 === 0 || index % 11 === 0 ? "is-red" : "is-green"}
|
||||
key={index}
|
||||
style={{ "--height": `${18 + ((index * 17) % 62)}%` } as CSSProperties}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="mock-volume-field" aria-hidden="true">
|
||||
{Array.from({ length: 42 }).map((_, index) => (
|
||||
<span
|
||||
className={index % 6 === 0 ? "is-red" : "is-green"}
|
||||
key={index}
|
||||
style={{ "--height": `${14 + ((index * 23) % 68)}%` } as CSSProperties}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function SignalPanel({ hero = false }: { hero?: boolean }) {
|
||||
return (
|
||||
<Panel title="Signals & Alerts" meta="All / Signals / System" className={hero ? "mock-signals is-hero" : "mock-signals"}>
|
||||
<div className="mock-signal-list">
|
||||
{signals.map(([time, title, symbol, value, tag]) => (
|
||||
<article className="mock-signal-item" key={`${time}-${title}`}>
|
||||
<time>{time}</time>
|
||||
<div>
|
||||
<strong>{title}</strong>
|
||||
<span>{symbol} / {value}</span>
|
||||
</div>
|
||||
<span className={`mock-pill ${tag === "Bearish" ? "is-bearish" : tag === "News" ? "is-news" : "is-bullish"}`}>
|
||||
{tag}
|
||||
</span>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function FeedHealth() {
|
||||
return (
|
||||
<Panel title="Feed Health" meta="Live checks" className="mock-feed">
|
||||
<div className="mock-table mock-table-feed">
|
||||
{feedHealth.map(([feed, status, lag, rate]) => (
|
||||
<div className="mock-table-row" key={feed}>
|
||||
<span>{feed}</span>
|
||||
<span className={`mock-pill ${status === "Degraded" ? "is-warning" : "is-bullish"}`}>{status}</span>
|
||||
<span>{lag}</span>
|
||||
<span>{rate}/s</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function DarkFlow() {
|
||||
return (
|
||||
<Panel title="Dark Flow" meta="Equity prints" className="mock-dark-flow">
|
||||
<div className="mock-table mock-table-dark">
|
||||
{darkFlow.map(([time, symbol, side, size, notional, type]) => (
|
||||
<div className="mock-table-row" key={`${time}-${side}-${size}`}>
|
||||
<span>{time}</span>
|
||||
<strong>{symbol}</strong>
|
||||
<span className={`mock-pill ${side === "Sell" ? "is-bearish" : "is-bullish"}`}>{side}</span>
|
||||
<span>{size}</span>
|
||||
<span>{notional}</span>
|
||||
<span>{type}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function EventContext() {
|
||||
return (
|
||||
<Panel title="Event Context" meta="Window: 15m" className="mock-context">
|
||||
<div className="mock-event-layout">
|
||||
<ol className="mock-timeline">
|
||||
{signals.slice(0, 4).map(([time, title, symbol]) => (
|
||||
<li key={`${time}-${title}`}>
|
||||
<time>{time}</time>
|
||||
<strong>{title}</strong>
|
||||
<span>{symbol} evidence linked</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<div className="mock-detail">
|
||||
<h3>Why it fired</h3>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>Type</dt>
|
||||
<dd>Dark Flow Sweep</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Premium</dt>
|
||||
<dd>$4.32M</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Venue</dt>
|
||||
<dd>Off-exchange</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Tags</dt>
|
||||
<dd>Bullish / Sweep / Call</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function ReplayRail({ compact = false }: { compact?: boolean }) {
|
||||
return (
|
||||
<Panel title="Replay" meta="May 16, 2024" className={compact ? "mock-replay is-compact" : "mock-replay"}>
|
||||
<div className="mock-replay-controls">
|
||||
<button type="button">Prev</button>
|
||||
<button type="button">Pause</button>
|
||||
<button type="button">Next</button>
|
||||
<span>32x</span>
|
||||
</div>
|
||||
<div className="mock-replay-track">
|
||||
<span className="mock-replay-window" />
|
||||
<span className="mock-replay-now" />
|
||||
</div>
|
||||
<div className="mock-replay-times">
|
||||
<span>09:00</span>
|
||||
<strong>09:41:23 / Live</strong>
|
||||
<span>10:15</span>
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function SymbolBrief() {
|
||||
return (
|
||||
<Panel title="AAPL Evidence Brief" meta="Focused symbol" className="mock-symbol-brief">
|
||||
<div className="mock-brief-price">
|
||||
<strong>194.88</strong>
|
||||
<span className="mock-move is-up">+1.22%</span>
|
||||
</div>
|
||||
<p>
|
||||
Dark sweep pressure aligns with short-window momentum and a fresh news catalyst. Context confidence is high, but
|
||||
the largest block remains off-exchange and should be checked against next print behavior.
|
||||
</p>
|
||||
<div className="mock-brief-tags">
|
||||
<span className="mock-pill is-bullish">Bullish</span>
|
||||
<span className="mock-pill is-info">Sweep</span>
|
||||
<span className="mock-pill is-news">News linked</span>
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
function Sparkline({ direction }: { direction: string }) {
|
||||
return (
|
||||
<svg className="mock-sparkline" viewBox="0 0 96 28" role="img" aria-label={`${direction} sparkline`}>
|
||||
<polyline
|
||||
fill="none"
|
||||
points={
|
||||
direction === "down"
|
||||
? "0,8 9,12 18,10 27,17 36,14 45,21 54,18 63,23 72,19 81,24 96,20"
|
||||
: "0,22 9,18 18,20 27,13 36,15 45,9 54,12 63,6 72,10 81,4 96,7"
|
||||
}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -2362,3 +2362,711 @@ h3 {
|
|||
border-radius: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.mock-terminal {
|
||||
min-height: calc(100vh - var(--topbar-height));
|
||||
padding: 18px;
|
||||
color: var(--text);
|
||||
background:
|
||||
linear-gradient(180deg, oklch(0.18 0.018 238 / 0.8), transparent 220px),
|
||||
linear-gradient(135deg, oklch(0.12 0.015 230), oklch(0.1 0.012 255));
|
||||
}
|
||||
|
||||
.mock-header {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 0.8fr) minmax(280px, 1.2fr) auto;
|
||||
gap: 14px;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mock-brand-lockup {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
}
|
||||
|
||||
.mock-mark {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 9px;
|
||||
background:
|
||||
linear-gradient(135deg, oklch(0.68 0.14 246), oklch(0.68 0.12 164)),
|
||||
var(--blue-soft);
|
||||
box-shadow: inset 0 0 0 1px oklch(0.94 0.02 240 / 0.24);
|
||||
}
|
||||
|
||||
.mock-brand {
|
||||
display: block;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.74rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.mock-header h1 {
|
||||
margin: 2px 0 0;
|
||||
font-family: var(--font-display), sans-serif;
|
||||
font-size: 1.28rem;
|
||||
line-height: 1.08;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.mock-header p {
|
||||
max-width: 72ch;
|
||||
margin: 0;
|
||||
color: var(--text-dim);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.mock-header-tools,
|
||||
.mock-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.mock-header-tools span,
|
||||
.mock-switcher a {
|
||||
min-height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 6px 9px;
|
||||
background: oklch(0.97 0.008 250 / 0.035);
|
||||
color: var(--text-dim);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mock-live-dot {
|
||||
color: var(--green) !important;
|
||||
background: var(--green-soft) !important;
|
||||
}
|
||||
|
||||
.mock-mode,
|
||||
.mock-switcher a.is-active {
|
||||
color: var(--accent) !important;
|
||||
border-color: var(--border-strong) !important;
|
||||
background: var(--accent-soft) !important;
|
||||
}
|
||||
|
||||
.mock-switcher {
|
||||
grid-column: 1 / -1;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.mock-ticker-rail {
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: oklch(0.13 0.015 245 / 0.94);
|
||||
}
|
||||
|
||||
.mock-ticker-track {
|
||||
display: flex;
|
||||
width: max-content;
|
||||
gap: 8px;
|
||||
padding: 7px;
|
||||
animation: mockTicker 42s linear infinite;
|
||||
}
|
||||
|
||||
.mock-ticker-card {
|
||||
width: 176px;
|
||||
min-height: 48px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 7px;
|
||||
align-items: center;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(180deg, oklch(0.18 0.017 244), oklch(0.14 0.014 244));
|
||||
}
|
||||
|
||||
.mock-ticker-card div {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.mock-ticker-card strong,
|
||||
.mock-table strong {
|
||||
font-family: var(--font-mono), monospace;
|
||||
}
|
||||
|
||||
.mock-ticker-card span {
|
||||
color: var(--text-dim);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.mock-sparkline {
|
||||
grid-column: 1 / -1;
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.mock-sparkline polyline {
|
||||
stroke: var(--green);
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mock-ticker-card:has(.is-down) .mock-sparkline polyline {
|
||||
stroke: var(--red);
|
||||
}
|
||||
|
||||
.mock-dashboard-grid {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mock-grid-classic {
|
||||
grid-template-columns: minmax(420px, 1.18fr) minmax(420px, 1.48fr) minmax(320px, 0.95fr);
|
||||
grid-template-areas:
|
||||
"tape chart signals"
|
||||
"feed dark context"
|
||||
"replay replay replay";
|
||||
}
|
||||
|
||||
.mock-grid-focus {
|
||||
grid-template-columns: minmax(280px, 0.78fr) minmax(480px, 1.45fr) minmax(360px, 0.95fr);
|
||||
grid-template-areas:
|
||||
"brief chart context"
|
||||
"tape chart context"
|
||||
"signals dark context";
|
||||
}
|
||||
|
||||
.mock-grid-signals {
|
||||
grid-template-columns: minmax(360px, 0.92fr) minmax(440px, 1.15fr) minmax(360px, 0.9fr);
|
||||
grid-template-areas:
|
||||
"signals tape chart"
|
||||
"signals tape feed"
|
||||
"context context context";
|
||||
}
|
||||
|
||||
.mock-grid-replay {
|
||||
grid-template-columns: minmax(340px, 0.95fr) minmax(460px, 1.25fr) minmax(360px, 0.9fr);
|
||||
grid-template-areas:
|
||||
"replay replay replay"
|
||||
"tape chart context"
|
||||
"signals dark context";
|
||||
}
|
||||
|
||||
.mock-panel {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(180deg, oklch(0.18 0.016 246 / 0.98), oklch(0.135 0.014 246 / 0.98));
|
||||
}
|
||||
|
||||
.mock-panel-head {
|
||||
min-height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.mock-panel-head h2 {
|
||||
margin: 0;
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mock-panel-head span {
|
||||
color: var(--text-faint);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
|
||||
.mock-option-tape {
|
||||
grid-area: tape;
|
||||
}
|
||||
|
||||
.mock-chart {
|
||||
grid-area: chart;
|
||||
}
|
||||
|
||||
.mock-signals {
|
||||
grid-area: signals;
|
||||
}
|
||||
|
||||
.mock-feed {
|
||||
grid-area: feed;
|
||||
}
|
||||
|
||||
.mock-dark-flow {
|
||||
grid-area: dark;
|
||||
}
|
||||
|
||||
.mock-context {
|
||||
grid-area: context;
|
||||
}
|
||||
|
||||
.mock-replay {
|
||||
grid-area: replay;
|
||||
}
|
||||
|
||||
.mock-symbol-brief {
|
||||
grid-area: brief;
|
||||
}
|
||||
|
||||
.mock-table {
|
||||
display: grid;
|
||||
padding: 6px 10px 10px;
|
||||
}
|
||||
|
||||
.mock-table-row {
|
||||
min-height: 36px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid oklch(0.72 0.012 250 / 0.09);
|
||||
color: var(--text-dim);
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.mock-table-row:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.mock-table-head {
|
||||
min-height: 30px;
|
||||
color: var(--text-faint);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.64rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mock-table-options .mock-table-row {
|
||||
grid-template-columns: 42px 58px 70px 64px 68px 72px 68px 76px;
|
||||
}
|
||||
|
||||
.mock-table-feed .mock-table-row {
|
||||
grid-template-columns: minmax(110px, 1fr) 86px 58px 70px;
|
||||
}
|
||||
|
||||
.mock-table-dark .mock-table-row {
|
||||
grid-template-columns: 72px 56px 64px 74px 78px 64px;
|
||||
}
|
||||
|
||||
.mock-pill {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 22px;
|
||||
padding: 3px 7px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.64rem;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.mock-pill.is-bullish {
|
||||
color: var(--green);
|
||||
background: var(--green-soft);
|
||||
}
|
||||
|
||||
.mock-pill.is-bearish {
|
||||
color: var(--red);
|
||||
background: var(--red-soft);
|
||||
}
|
||||
|
||||
.mock-pill.is-info,
|
||||
.mock-pill.is-news {
|
||||
color: var(--blue);
|
||||
background: var(--blue-soft);
|
||||
}
|
||||
|
||||
.mock-pill.is-warning {
|
||||
color: var(--accent);
|
||||
background: var(--accent-soft);
|
||||
}
|
||||
|
||||
.mock-move {
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.mock-move.is-up {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.mock-move.is-down {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
.mock-chart {
|
||||
min-height: 326px;
|
||||
}
|
||||
|
||||
.mock-chart.is-compact {
|
||||
min-height: 240px;
|
||||
}
|
||||
|
||||
.mock-chart-meta {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
padding: 10px 12px 0;
|
||||
}
|
||||
|
||||
.mock-chart-meta strong,
|
||||
.mock-brief-price strong {
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.mock-candle-field {
|
||||
position: relative;
|
||||
height: 190px;
|
||||
margin: 8px 12px 0;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: 4px;
|
||||
padding: 12px 0;
|
||||
border-top: 1px solid oklch(0.72 0.012 250 / 0.08);
|
||||
border-bottom: 1px solid oklch(0.72 0.012 250 / 0.08);
|
||||
background:
|
||||
repeating-linear-gradient(0deg, transparent 0 38px, oklch(0.72 0.012 250 / 0.08) 39px 40px),
|
||||
linear-gradient(180deg, oklch(0.16 0.018 246), oklch(0.12 0.014 246));
|
||||
}
|
||||
|
||||
.mock-chart.is-compact .mock-candle-field {
|
||||
height: 126px;
|
||||
}
|
||||
|
||||
.mock-candle-field span {
|
||||
width: 5px;
|
||||
height: var(--height);
|
||||
min-height: 18px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mock-candle-field .is-green,
|
||||
.mock-volume-field .is-green {
|
||||
background: var(--green);
|
||||
}
|
||||
|
||||
.mock-candle-field .is-red,
|
||||
.mock-volume-field .is-red {
|
||||
background: var(--red);
|
||||
}
|
||||
|
||||
.mock-volume-field {
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: 5px;
|
||||
padding: 9px 12px 12px;
|
||||
}
|
||||
|
||||
.mock-chart.is-compact .mock-volume-field {
|
||||
height: 54px;
|
||||
}
|
||||
|
||||
.mock-volume-field span {
|
||||
width: 7px;
|
||||
height: var(--height);
|
||||
min-height: 8px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.mock-signal-list {
|
||||
display: grid;
|
||||
padding: 6px 10px 10px;
|
||||
}
|
||||
|
||||
.mock-signal-item {
|
||||
min-height: 58px;
|
||||
display: grid;
|
||||
grid-template-columns: 70px minmax(0, 1fr) auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid oklch(0.72 0.012 250 / 0.09);
|
||||
}
|
||||
|
||||
.mock-signal-item:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.mock-signal-item time,
|
||||
.mock-timeline time {
|
||||
color: var(--text-faint);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.mock-signal-item div {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.mock-signal-item strong {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.mock-signal-item span:not(.mock-pill) {
|
||||
color: var(--text-dim);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.mock-signals.is-hero .mock-signal-item {
|
||||
min-height: 74px;
|
||||
}
|
||||
|
||||
.mock-event-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(220px, 0.75fr);
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mock-timeline {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.mock-timeline li {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
padding: 9px;
|
||||
border: 1px solid oklch(0.72 0.012 250 / 0.1);
|
||||
border-radius: 8px;
|
||||
background: oklch(0.97 0.008 250 / 0.028);
|
||||
}
|
||||
|
||||
.mock-timeline strong {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.mock-timeline span,
|
||||
.mock-detail dd,
|
||||
.mock-symbol-brief p {
|
||||
color: var(--text-dim);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.mock-detail {
|
||||
padding: 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: oklch(0.12 0.014 246 / 0.72);
|
||||
}
|
||||
|
||||
.mock-detail h3 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
.mock-detail dl {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mock-detail div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mock-detail dt {
|
||||
color: var(--text-faint);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.65rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mock-detail dd {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mock-replay {
|
||||
min-height: 112px;
|
||||
}
|
||||
|
||||
.mock-replay-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px 0;
|
||||
}
|
||||
|
||||
.mock-replay-controls button {
|
||||
min-height: 30px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-soft);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mock-replay-controls span {
|
||||
color: var(--text-faint);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.mock-replay-track {
|
||||
position: relative;
|
||||
height: 26px;
|
||||
margin: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background:
|
||||
repeating-linear-gradient(90deg, transparent 0 22px, oklch(0.72 0.012 250 / 0.18) 23px 24px),
|
||||
oklch(0.11 0.014 246);
|
||||
}
|
||||
|
||||
.mock-replay-window {
|
||||
position: absolute;
|
||||
inset: 6px 28% 6px 42%;
|
||||
border-radius: 999px;
|
||||
background: var(--blue);
|
||||
}
|
||||
|
||||
.mock-replay-now {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
bottom: 2px;
|
||||
left: 62%;
|
||||
width: 3px;
|
||||
border-radius: 999px;
|
||||
background: var(--green);
|
||||
}
|
||||
|
||||
.mock-replay-times {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 12px 12px;
|
||||
color: var(--text-faint);
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
|
||||
.mock-replay-times strong {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.mock-symbol-brief {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.mock-brief-price,
|
||||
.mock-brief-tags {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 12px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.mock-symbol-brief p {
|
||||
margin: 12px 12px 0;
|
||||
}
|
||||
|
||||
@keyframes mockTicker {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.mock-ticker-track {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.mock-header {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mock-header-tools,
|
||||
.mock-switcher {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.mock-grid-classic,
|
||||
.mock-grid-focus,
|
||||
.mock-grid-signals,
|
||||
.mock-grid-replay {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"replay"
|
||||
"brief"
|
||||
"signals"
|
||||
"chart"
|
||||
"tape"
|
||||
"context"
|
||||
"feed"
|
||||
"dark";
|
||||
}
|
||||
|
||||
.mock-grid-classic {
|
||||
grid-template-areas:
|
||||
"tape"
|
||||
"chart"
|
||||
"signals"
|
||||
"feed"
|
||||
"dark"
|
||||
"context"
|
||||
"replay";
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.mock-terminal {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.mock-table {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.mock-table-row {
|
||||
width: max-content;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.mock-event-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mock-signal-item {
|
||||
grid-template-columns: 62px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.mock-signal-item .mock-pill {
|
||||
grid-column: 2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const mono = IBM_Plex_Mono({
|
|||
});
|
||||
|
||||
export const metadata = {
|
||||
title: "Islandflow Terminal",
|
||||
title: "islandflow terminal",
|
||||
description: "Realtime options flow and off-exchange analysis terminal"
|
||||
};
|
||||
|
||||
|
|
|
|||
7
apps/web/app/mock1/page.tsx
Normal file
7
apps/web/app/mock1/page.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { DashboardMock } from "../dashboard-mocks";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default function Mock1Page() {
|
||||
return <DashboardMock variant="mock1" />;
|
||||
}
|
||||
7
apps/web/app/mock2/page.tsx
Normal file
7
apps/web/app/mock2/page.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { DashboardMock } from "../dashboard-mocks";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default function Mock2Page() {
|
||||
return <DashboardMock variant="mock2" />;
|
||||
}
|
||||
7
apps/web/app/mock3/page.tsx
Normal file
7
apps/web/app/mock3/page.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { DashboardMock } from "../dashboard-mocks";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default function Mock3Page() {
|
||||
return <DashboardMock variant="mock3" />;
|
||||
}
|
||||
7
apps/web/app/mock4/page.tsx
Normal file
7
apps/web/app/mock4/page.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { DashboardMock } from "../dashboard-mocks";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default function Mock4Page() {
|
||||
return <DashboardMock variant="mock4" />;
|
||||
}
|
||||
|
|
@ -8958,7 +8958,7 @@ export function TerminalAppShell({ children }: { children: ReactNode }) {
|
|||
<div className="terminal-drawer-head">
|
||||
<div className="terminal-brand">
|
||||
<span className="terminal-brand-kicker">IF</span>
|
||||
<span className="terminal-brand-name">Islandflow</span>
|
||||
<span className="terminal-brand-name">islandflow</span>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Close navigation drawer"
|
||||
|
|
|
|||
186
docs/turns/2026-05-28-dashboard-mock-routes.html
Normal file
186
docs/turns/2026-05-28-dashboard-mock-routes.html
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue