harden terminal view, add $impeccable design docs, update AGENTS.md
This commit is contained in:
parent
1089174264
commit
9644e9ceef
10 changed files with 1716 additions and 42 deletions
|
|
@ -8,6 +8,8 @@
|
||||||
{"_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-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-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-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-1f5","title":"Adapt terminal view for responsive use","description":"Improve the terminal view so it remains usable across desktop, tablet, and small-screen contexts without hiding core workflow functionality.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T22:22:18Z","created_by":"dirtydishes","updated_at":"2026-05-14T22:25:22Z","started_at":"2026-05-14T22:22:25Z","closed_at":"2026-05-14T22:25:22Z","close_reason":"Terminal view adapted for responsive and touch-first contexts; tests and web build passed.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
|
{"_type":"issue","id":"islandflow-uhi","title":"Publish terminal turn document to GitHub Pages","description":"Why: the completed turn document should be reachable on the user's GitHub Pages site. What: determine the GitHub Pages publishing path for dirtydishes.github.io, place the terminal hardening turn document at a stable HTML URL, validate the file location, and update beads status for the publishing work.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T22:15:23Z","created_by":"dirtydishes","updated_at":"2026-05-14T22:17:39Z","started_at":"2026-05-14T22:15:34Z","closed_at":"2026-05-14T22:17:39Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"_type":"issue","id":"islandflow-6ri","title":"Harden terminal shell view","description":"Why: the terminal shell needs production hardening for focus visibility, long labels, and ticker entry edge cases so the main workflow remains stable under constrained widths and imperfect input. What: tighten shell semantics and input handling, prevent overflow in the top bar and rail, and add regression tests for the ticker filter normalization path.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:56:45Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:58:46Z","started_at":"2026-05-14T08:56:53Z","closed_at":"2026-05-14T08:58:46Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
{"_type":"issue","id":"islandflow-6ri","title":"Harden terminal shell view","description":"Why: the terminal shell needs production hardening for focus visibility, long labels, and ticker entry edge cases so the main workflow remains stable under constrained widths and imperfect input. What: tighten shell semantics and input handling, prevent overflow in the top bar and rail, and add regression tests for the ticker filter normalization path.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-14T08:56:45Z","created_by":"dirtydishes","updated_at":"2026-05-14T08:58:46Z","started_at":"2026-05-14T08:56:53Z","closed_at":"2026-05-14T08:58:46Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
{"_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-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-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}
|
||||||
|
|
|
||||||
210
.impeccable/design.json
Normal file
210
.impeccable/design.json
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 2,
|
||||||
|
"generatedAt": "2026-05-14T08:06:45Z",
|
||||||
|
"title": "Design System: Islandflow Terminal",
|
||||||
|
"extensions": {
|
||||||
|
"colorMeta": {
|
||||||
|
"bg-core": {
|
||||||
|
"role": "neutral",
|
||||||
|
"displayName": "Command Black",
|
||||||
|
"canonical": "#06080b",
|
||||||
|
"tonalRamp": ["#050608", "#0b0f14", "#111821", "#1a2430", "#263445", "#394a5f", "#6f8095", "#dce3ea"]
|
||||||
|
},
|
||||||
|
"bg-pane": {
|
||||||
|
"role": "neutral",
|
||||||
|
"displayName": "Panel Graphite",
|
||||||
|
"canonical": "#111820",
|
||||||
|
"tonalRamp": ["#0b0f14", "#121922", "#1a2430", "#253444", "#314658", "#4a6076", "#7b90a5", "#dbe3ec"]
|
||||||
|
},
|
||||||
|
"text-primary": {
|
||||||
|
"role": "neutral",
|
||||||
|
"displayName": "Data Ink",
|
||||||
|
"canonical": "#e6edf4",
|
||||||
|
"tonalRamp": ["#1c232b", "#2a333e", "#3a4654", "#4f6072", "#6a7f93", "#92a4b5", "#bcc8d3", "#e6edf4"]
|
||||||
|
},
|
||||||
|
"signal-amber": {
|
||||||
|
"role": "primary",
|
||||||
|
"displayName": "Signal Amber",
|
||||||
|
"canonical": "#f5a623",
|
||||||
|
"tonalRamp": ["#2f1f06", "#5b3c0b", "#865913", "#b5761a", "#d89220", "#f5a623", "#f8c069", "#fce3bc"]
|
||||||
|
},
|
||||||
|
"confirm-green": {
|
||||||
|
"role": "tertiary",
|
||||||
|
"displayName": "Confirm Green",
|
||||||
|
"canonical": "#25c17a",
|
||||||
|
"tonalRamp": ["#062716", "#0d4b2a", "#13703f", "#1a9554", "#20ae6a", "#25c17a", "#6ed5a6", "#c9f1df"]
|
||||||
|
},
|
||||||
|
"risk-red": {
|
||||||
|
"role": "tertiary",
|
||||||
|
"displayName": "Risk Red",
|
||||||
|
"canonical": "#ff6b5f",
|
||||||
|
"tonalRamp": ["#320d0a", "#611914", "#91261f", "#bf362f", "#e04f48", "#ff6b5f", "#ff9b93", "#ffd9d5"]
|
||||||
|
},
|
||||||
|
"info-blue": {
|
||||||
|
"role": "secondary",
|
||||||
|
"displayName": "Info Blue",
|
||||||
|
"canonical": "#4da3ff",
|
||||||
|
"tonalRamp": ["#0a1f33", "#143c61", "#1f5a8f", "#2b78bd", "#3a91e0", "#4da3ff", "#8cc4ff", "#d8ebff"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"typographyMeta": {
|
||||||
|
"display": {
|
||||||
|
"displayName": "Display",
|
||||||
|
"purpose": "Primary wayfinding headers and route-level titles."
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"displayName": "Body",
|
||||||
|
"purpose": "Default transactional and descriptive copy in panes and controls."
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"displayName": "Label/Mono",
|
||||||
|
"purpose": "Data labels, numeric cells, chips, and compact control text."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shadows": [
|
||||||
|
{
|
||||||
|
"name": "overlay-lift",
|
||||||
|
"value": "0 24px 60px rgba(0, 0, 0, 0.42)",
|
||||||
|
"purpose": "Filter popover separation from live content."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "drawer-lift",
|
||||||
|
"value": "0 24px 70px rgba(0, 0, 0, 0.5)",
|
||||||
|
"purpose": "Right-side detail drawer emphasis."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tooltip-lift",
|
||||||
|
"value": "0 16px 40px rgba(0, 0, 0, 0.45)",
|
||||||
|
"purpose": "Transient metadata tooltip depth."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"motion": [
|
||||||
|
{
|
||||||
|
"name": "fast-state",
|
||||||
|
"value": "150ms ease",
|
||||||
|
"purpose": "Button and hover state transitions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "focus-rail",
|
||||||
|
"value": "160ms ease",
|
||||||
|
"purpose": "Input underline and glow transitions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "count-reveal",
|
||||||
|
"value": "180ms ease",
|
||||||
|
"purpose": "Missed counter width/position reveal."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"breakpoints": [
|
||||||
|
{
|
||||||
|
"name": "lg",
|
||||||
|
"value": "1180px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "md",
|
||||||
|
"value": "980px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sm",
|
||||||
|
"value": "720px"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"name": "Terminal Action Button",
|
||||||
|
"kind": "button",
|
||||||
|
"refersTo": "button-base",
|
||||||
|
"description": "Default compact control for tape actions and utility toggles.",
|
||||||
|
"html": "<button class=\"ds-btn\">Pause Tape</button>",
|
||||||
|
"css": ".ds-btn { border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; padding: 8px 10px; background: rgba(255,255,255,0.03); color: #e6edf4; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer; transition: border-color 150ms ease, background 150ms ease, color 150ms ease; } .ds-btn:hover { border-color: rgba(255,177,48,0.35); background: rgba(245,166,35,0.08); color: #ffd89a; } .ds-btn:focus-visible { outline: none; border-color: rgba(255,177,48,0.45); box-shadow: 0 0 0 2px rgba(245,166,35,0.2); } .ds-btn:active { background: rgba(245,166,35,0.12); }"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Rail Navigation Link",
|
||||||
|
"kind": "nav",
|
||||||
|
"refersTo": "nav-link",
|
||||||
|
"description": "Primary route selector in the left terminal rail.",
|
||||||
|
"html": "<a class=\"ds-nav-link\" href=\"#\">Signals</a>",
|
||||||
|
"css": ".ds-nav-link { display: inline-block; padding: 12px 14px; border: 1px solid transparent; border-radius: 10px; color: #90a0b2; background: transparent; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; text-decoration: none; transition: border-color 150ms ease, background 150ms ease, color 150ms ease; } .ds-nav-link:hover { border-color: rgba(255,255,255,0.08); background: rgba(255,255,255,0.03); color: #e6edf4; } .ds-nav-link:focus-visible { outline: none; border-color: rgba(255,177,48,0.35); } .ds-nav-link.ds-nav-link-active { border-color: rgba(255,177,48,0.35); background: linear-gradient(90deg, rgba(245,166,35,0.12), rgba(245,166,35,0.04)); color: #e6edf4; }"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Filter Underline Input",
|
||||||
|
"kind": "input",
|
||||||
|
"refersTo": "pane-surface",
|
||||||
|
"description": "Global tape filter field with amber under-rail focus behavior.",
|
||||||
|
"html": "<label class=\"ds-filter\"><span class=\"ds-filter-label\">Contract Filter</span><span class=\"ds-filter-line\"><input class=\"ds-filter-input\" placeholder=\"Type symbol, contract, or expiry\" /></span></label>",
|
||||||
|
"css": ".ds-filter { display: inline-flex; flex-direction: column; gap: 4px; min-width: 260px; } .ds-filter-label { color: #6e7b8c; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; } .ds-filter-line { position: relative; display: block; padding-bottom: 6px; } .ds-filter-line::before { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: linear-gradient(90deg, rgba(245,166,35,0.88), rgba(245,166,35,0.14)); } .ds-filter-line::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: linear-gradient(90deg, rgba(255,216,154,0.98), rgba(245,166,35,0.92)); transform: scaleX(0.2); transform-origin: left center; opacity: 0; transition: transform 160ms ease, opacity 160ms ease, box-shadow 160ms ease; } .ds-filter-input { width: 100%; border: 0; background: transparent; color: #e6edf4; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.92rem; font-weight: 600; letter-spacing: 0.01em; } .ds-filter-input::placeholder { color: rgba(193,203,224,0.58); font-size: 0.86rem; } .ds-filter-input:focus-visible { outline: none; } .ds-filter:focus-within .ds-filter-label { color: #ffd89a; } .ds-filter:focus-within .ds-filter-line::after { transform: scaleX(1); opacity: 1; box-shadow: 0 0 18px rgba(245,166,35,0.34); }"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Semantic Status Chip",
|
||||||
|
"kind": "chip",
|
||||||
|
"refersTo": "status-chip",
|
||||||
|
"description": "Pill used for direction, severity, and condition tags with explicit label text.",
|
||||||
|
"html": "<span class=\"ds-chip ds-chip-risk\">Bearish</span>",
|
||||||
|
"css": ".ds-chip { display: inline-flex; align-items: center; padding: 3px 8px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.08); font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; } .ds-chip-neutral { background: rgba(77,163,255,0.14); border-color: rgba(77,163,255,0.26); color: #bddcff; } .ds-chip-good { background: rgba(37,193,122,0.12); border-color: rgba(37,193,122,0.34); color: #98f0c0; } .ds-chip-risk { background: rgba(255,107,95,0.14); border-color: rgba(255,107,95,0.34); color: #ffc3bd; }"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Terminal Pane",
|
||||||
|
"kind": "card",
|
||||||
|
"refersTo": "pane-surface",
|
||||||
|
"description": "Default data region container for tape, alerts, and chart modules.",
|
||||||
|
"html": "<section class=\"ds-pane\"><header class=\"ds-pane-head\"><h3 class=\"ds-pane-title\">Flow Packets</h3><button class=\"ds-btn-mini\">Refresh</button></header><div class=\"ds-pane-body\">Pane content</div></section>",
|
||||||
|
"css": ".ds-pane { border: 1px solid rgba(255,255,255,0.08); border-radius: 14px; background: linear-gradient(180deg, rgba(255,255,255,0.03), transparent 40%), #111820; color: #e6edf4; overflow: hidden; } .ds-pane-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 18px; border-bottom: 1px solid rgba(255,255,255,0.08); background: rgba(255,255,255,0.02); } .ds-pane-title { margin: 0; font-family: var(--font-display, Quantico, sans-serif); font-size: 1rem; letter-spacing: 0.08em; text-transform: uppercase; } .ds-pane-body { padding: 16px 18px 18px; } .ds-btn-mini { border: 1px solid rgba(255,255,255,0.08); border-radius: 8px; padding: 8px 10px; background: rgba(255,255,255,0.03); color: #e6edf4; font-family: var(--font-mono, 'IBM Plex Mono', monospace); font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer; transition: border-color 150ms ease, background 150ms ease; } .ds-btn-mini:hover { border-color: rgba(255,177,48,0.35); background: rgba(245,166,35,0.08); }"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Flow Filter Popover Surface",
|
||||||
|
"kind": "custom",
|
||||||
|
"refersTo": "pane-surface",
|
||||||
|
"description": "Floating filter inspector with dedicated overlay elevation.",
|
||||||
|
"html": "<aside class=\"ds-popover\"><h4 class=\"ds-popover-title\">Flow Filters</h4><p class=\"ds-popover-copy\">Changes apply immediately.</p></aside>",
|
||||||
|
"css": ".ds-popover { width: min(420px, 90vw); border: 1px solid rgba(245,166,35,0.24); border-radius: 18px; padding: 16px; background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02)), rgba(11,16,22,0.92); box-shadow: 0 24px 60px rgba(0,0,0,0.42), inset 0 1px 0 rgba(255,255,255,0.04); backdrop-filter: blur(18px); color: #e6edf4; } .ds-popover-title { margin: 0; font-family: var(--font-display, Quantico, sans-serif); font-size: 0.9rem; letter-spacing: 0.12em; text-transform: uppercase; } .ds-popover-copy { margin: 6px 0 0; color: #90a0b2; font-family: var(--font-sans, 'IBM Plex Sans', sans-serif); font-size: 0.78rem; }"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"narrative": {
|
||||||
|
"northStar": "The Evidence Console",
|
||||||
|
"overview": "Islandflow's interface behaves like an investigation instrument, not a presentation layer. The system is tuned for fast read accuracy under volatility: hierarchy is built from contrast, casing, and spacing cadence rather than decorative effects.\n\nThe visual atmosphere is dark and controlled, with amber used as a directional signal rather than ambient decoration. Surfaces are compact and information-dense, but each zone is explicit about purpose so the user can move from detection to validation without losing context.\n\nThis system explicitly rejects the anti-references in PRODUCT.md: no meme-stock hype aesthetics, no generic SaaS card fog, and no Bloomberg cosplay density unless density is earning its keep with decision value.",
|
||||||
|
"keyCharacteristics": [
|
||||||
|
"Operational contrast over ornamental contrast.",
|
||||||
|
"Dense layout with stable rhythm.",
|
||||||
|
"Accent color treated as scarce signal.",
|
||||||
|
"Monospace-assisted precision for time, numeric, and status data.",
|
||||||
|
"Readability preserved during bursty live updates."
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"name": "The Signal Scarcity Rule",
|
||||||
|
"body": "Amber is a control and attention signal, not a wash. Keep it concentrated on actions, state edges, and critical counters.",
|
||||||
|
"section": "colors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "The Semantic Color Rule",
|
||||||
|
"body": "Red and green never stand alone for meaning. Every directional or severity cue must include text, shape, or positional confirmation.",
|
||||||
|
"section": "colors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "The Instrument Label Rule",
|
||||||
|
"body": "Labels are short, uppercase, and spaced. They identify system state fast, without narrative phrasing.",
|
||||||
|
"section": "typography"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "The Flat-By-Default Rule",
|
||||||
|
"body": "If a surface is not floating over active workflow content, it does not get shadow lift.",
|
||||||
|
"section": "elevation"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dos": [
|
||||||
|
"Do keep status and direction semantic with both color and text labels (`severity-high`, `direction-bullish`, explicit words).",
|
||||||
|
"Do preserve compact control density (`8px-12px` padding range) so investigation actions stay within a short scan path.",
|
||||||
|
"Do use amber as a sparse decision signal for active controls, focus rails, and key counters.",
|
||||||
|
"Do keep overlays visually separated with dedicated shadow roles while leaving primary panes flat.",
|
||||||
|
"Do design live updates to avoid flashing, excessive animation, and layout shifts during high-volume periods."
|
||||||
|
],
|
||||||
|
"donts": [
|
||||||
|
"Don't make Islandflow feel like a meme-stock or finfluencer trading app with hype, gamification, urgency theater, or promotional calls to action.",
|
||||||
|
"Don't make Islandflow feel like a generic SaaS analytics dashboard with decorative gradients, vague card stacks, and non-actionable vanity metrics.",
|
||||||
|
"Don't make Islandflow feel like Bloomberg-style visual density used as aesthetic cosplay instead of as a genuinely useful information structure.",
|
||||||
|
"Don't rely on red/green alone for directional meaning or severity.",
|
||||||
|
"Don't use colored side-stripe accents on rows/cards as the primary signifier; use complete semantic chips and labels instead."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
106
AGENTS.md
106
AGENTS.md
|
|
@ -69,17 +69,103 @@ Working style that avoids common problems here:
|
||||||
- Keep `.env` aligned with `.env.example`; adapters default to synthetic modes for local development.
|
- Keep `.env` aligned with `.env.example`; adapters default to synthetic modes for local development.
|
||||||
- Dev runners persist child PID state in `.tmp/`; if a previous run crashed, restart via the standard `bun run dev*` commands so stale processes are cleaned up.
|
- Dev runners persist child PID state in `.tmp/`; if a previous run crashed, restart via the standard `bun run dev*` commands so stale processes are cleaned up.
|
||||||
|
|
||||||
Always do the following when you finish a task and make a commit:
|
## Required Turn Documentation
|
||||||
|
|
||||||
|
At the end of every completed implementation task, before final handoff, create a user-readable HTML document describing the work.
|
||||||
|
|
||||||
|
This documentation is mandatory whenever code, configuration, tests, or project files were changed.
|
||||||
|
|
||||||
|
### Location
|
||||||
|
|
||||||
|
Save the document in:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/turns/
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a clear timestamped filename:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/turns/YYYY-MM-DD-short-task-name.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/turns/2026-05-14-add-market-replay-controls.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Format
|
||||||
|
|
||||||
|
Use the impeccable skill to structure the document as clean, readable HTML.
|
||||||
|
|
||||||
|
If the impeccable skill is unavailable, still create a well-structured standalone HTML file with:
|
||||||
|
|
||||||
|
- A concise summary at the top
|
||||||
|
- A detailed explanation of what changed
|
||||||
|
- Relevant context or background
|
||||||
|
- Specific code snippets or examples when helpful
|
||||||
|
- Issues, limitations, tradeoffs, or mitigations
|
||||||
|
- Validation performed, including tests, builds, linters, or manual checks
|
||||||
|
- Any remaining follow-up work, with corresponding Beads issue IDs when applicable
|
||||||
|
|
||||||
|
### Required Sections
|
||||||
|
|
||||||
|
Each turn document must include these sections:
|
||||||
|
|
||||||
|
1. **Summary**
|
||||||
|
2. **Changes Made**
|
||||||
|
3. **Context**
|
||||||
|
4. **Important Implementation Details**
|
||||||
|
5. **Validation**
|
||||||
|
6. **Issues, Limitations, and Mitigations**
|
||||||
|
7. **Follow-up Work**
|
||||||
|
|
||||||
|
### Completion Rule
|
||||||
|
|
||||||
|
A task is not complete until:
|
||||||
|
|
||||||
|
1. The Beads workflow is updated
|
||||||
|
2. The turn document is created in `docs/turns`
|
||||||
|
3. Relevant quality gates have passed or failures are documented
|
||||||
|
4. Changes are committed
|
||||||
|
5. `bd dolt push` succeeds
|
||||||
|
6. `git push` succeeds
|
||||||
|
7. `git status` shows the branch is up to date with origin
|
||||||
|
|
||||||
|
For trivial changes, the document may be brief, but it must still exist and clearly explain what changed and how it was validated.
|
||||||
|
|
||||||
|
## Plan Mode Documentation
|
||||||
|
|
||||||
|
When working in plan mode, do not modify implementation files.
|
||||||
|
|
||||||
|
At the end of plan mode, provide a concise summary of the plan and ask the user whether they want to proceed with implementation.
|
||||||
|
|
||||||
|
If the user asks to save the plan, create a user-readable HTML plan document in:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/plans/
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a clear timestamped filename:
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/plans/YYYY-MM-DD-short-plan-name.html
|
||||||
|
```
|
||||||
|
|
||||||
|
The plan document should be labeled clearly as a plan and should include:
|
||||||
|
|
||||||
|
1. **Plan Summary**
|
||||||
|
2. **Goals**
|
||||||
|
3. **Proposed Changes**
|
||||||
|
4. **Relevant Context**
|
||||||
|
5. **Implementation Steps**
|
||||||
|
6. **Risks, Limitations, and Mitigations**
|
||||||
|
7. **Open Questions**
|
||||||
|
|
||||||
|
Always do the following when you finish a task, finish the beads workflow and and make a commit:
|
||||||
- Document the changes in a user-readable format
|
- Document the changes in a user-readable format
|
||||||
- Use the impeccable skill to structure the document as HTML
|
- Use the impeccable skill to structure the document as HTML
|
||||||
- Create a clear, concise summary of the changes at the top, followed by a detailed description of the changes, including any relevant context or background as well as specific code snippets or examples.
|
- Create a clear, concise summary of the changes at the top, followed by a detailed description of the changes, including any relevant context or background as well as specific code snippets or examples.
|
||||||
- Note any relevant issues or limitations that were addressed or mitigated by the changes.
|
- Note any relevant issues or limitations that were addressed or mitigated by the changes.
|
||||||
- The document should be stored in the `docs/turns` directory.
|
- The HTML file should be stored in the `docs/turns` directory. It should include the current date and time, as well as a brief explanation of changes. e.g. docs/turns/YYYY-MM-DD-{description}.html
|
||||||
|
|
||||||
Always do the following when you finish a task and make a commit:
|
|
||||||
- Give a conscise summary of the plan and the changes made.
|
|
||||||
- Use the impeccable skill to structure the document as HTML
|
|
||||||
- Create a clear, concise summary of the changes at the top, followed by a detailed description of the changes, including any relevant context or background as well as specific code snippets or examples.
|
|
||||||
- Note any relevant issues or limitations that would be addressed or mitigated by the changes.
|
|
||||||
- The document should be stored in the `docs/plans` directory.
|
|
||||||
- It should be labeled as a plan with a brief description of the changes.
|
|
||||||
|
|
|
||||||
230
DESIGN.md
Normal file
230
DESIGN.md
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
---
|
||||||
|
name: Islandflow Terminal
|
||||||
|
description: Evidence-linked market intelligence terminal for real-time and replay investigation
|
||||||
|
colors:
|
||||||
|
bg-core: "#06080b"
|
||||||
|
bg-elevated: "#0b1016"
|
||||||
|
bg-pane: "#111820"
|
||||||
|
bg-pane-2: "#0d141b"
|
||||||
|
bg-soft: "#ffffff08"
|
||||||
|
border-subtle: "#ffffff14"
|
||||||
|
border-accent: "#ffb13059"
|
||||||
|
text-primary: "#e6edf4"
|
||||||
|
text-dim: "#90a0b2"
|
||||||
|
text-faint: "#6e7b8c"
|
||||||
|
signal-amber: "#f5a623"
|
||||||
|
signal-amber-soft: "#f5a6231f"
|
||||||
|
confirm-green: "#25c17a"
|
||||||
|
confirm-green-soft: "#25c17a1f"
|
||||||
|
risk-red: "#ff6b5f"
|
||||||
|
risk-red-soft: "#ff6b5f24"
|
||||||
|
info-blue: "#4da3ff"
|
||||||
|
info-blue-soft: "#4da3ff24"
|
||||||
|
typography:
|
||||||
|
display:
|
||||||
|
fontFamily: "Quantico, sans-serif"
|
||||||
|
fontSize: "clamp(2rem, 3vw, 2.8rem)"
|
||||||
|
fontWeight: 700
|
||||||
|
lineHeight: 1.05
|
||||||
|
letterSpacing: "0.08em"
|
||||||
|
body:
|
||||||
|
fontFamily: "IBM Plex Sans, sans-serif"
|
||||||
|
fontSize: "0.92rem"
|
||||||
|
fontWeight: 400
|
||||||
|
lineHeight: 1.45
|
||||||
|
label:
|
||||||
|
fontFamily: "IBM Plex Mono, monospace"
|
||||||
|
fontSize: "0.72rem"
|
||||||
|
fontWeight: 600
|
||||||
|
lineHeight: 1.2
|
||||||
|
letterSpacing: "0.12em"
|
||||||
|
rounded:
|
||||||
|
sm: "8px"
|
||||||
|
md: "10px"
|
||||||
|
lg: "12px"
|
||||||
|
xl: "14px"
|
||||||
|
pill: "999px"
|
||||||
|
spacing:
|
||||||
|
xs: "4px"
|
||||||
|
sm: "8px"
|
||||||
|
md: "12px"
|
||||||
|
lg: "16px"
|
||||||
|
xl: "24px"
|
||||||
|
components:
|
||||||
|
button-base:
|
||||||
|
backgroundColor: "{colors.bg-soft}"
|
||||||
|
textColor: "{colors.text-primary}"
|
||||||
|
typography: "{typography.label}"
|
||||||
|
rounded: "{rounded.sm}"
|
||||||
|
padding: "8px 10px"
|
||||||
|
button-active:
|
||||||
|
backgroundColor: "{colors.signal-amber-soft}"
|
||||||
|
textColor: "{colors.signal-amber}"
|
||||||
|
typography: "{typography.label}"
|
||||||
|
rounded: "{rounded.sm}"
|
||||||
|
padding: "8px 10px"
|
||||||
|
nav-link:
|
||||||
|
backgroundColor: "{colors.bg-core}"
|
||||||
|
textColor: "{colors.text-dim}"
|
||||||
|
typography: "{typography.label}"
|
||||||
|
rounded: "{rounded.md}"
|
||||||
|
padding: "12px 14px"
|
||||||
|
nav-link-active:
|
||||||
|
backgroundColor: "{colors.signal-amber-soft}"
|
||||||
|
textColor: "{colors.text-primary}"
|
||||||
|
typography: "{typography.label}"
|
||||||
|
rounded: "{rounded.md}"
|
||||||
|
padding: "12px 14px"
|
||||||
|
pane-surface:
|
||||||
|
backgroundColor: "{colors.bg-pane}"
|
||||||
|
textColor: "{colors.text-primary}"
|
||||||
|
rounded: "{rounded.xl}"
|
||||||
|
padding: "16px 18px"
|
||||||
|
status-chip:
|
||||||
|
backgroundColor: "{colors.bg-soft}"
|
||||||
|
textColor: "{colors.text-primary}"
|
||||||
|
typography: "{typography.label}"
|
||||||
|
rounded: "{rounded.pill}"
|
||||||
|
padding: "3px 8px"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Design System: Islandflow Terminal
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
**Creative North Star: "The Evidence Console"**
|
||||||
|
|
||||||
|
Islandflow's interface behaves like an investigation instrument, not a presentation layer. The system is tuned for fast read accuracy under volatility: hierarchy is built from contrast, casing, and spacing cadence rather than decorative effects.
|
||||||
|
|
||||||
|
The visual atmosphere is dark and controlled, with amber used as a directional signal rather than ambient decoration. Surfaces are compact and information-dense, but each zone is explicit about purpose so the user can move from detection to validation without losing context.
|
||||||
|
|
||||||
|
This system explicitly rejects the anti-references in PRODUCT.md: no meme-stock hype aesthetics, no generic SaaS card fog, and no Bloomberg cosplay density unless density is earning its keep with decision value.
|
||||||
|
|
||||||
|
**Key Characteristics:**
|
||||||
|
- Operational contrast over ornamental contrast.
|
||||||
|
- Dense layout with stable rhythm.
|
||||||
|
- Accent color treated as scarce signal.
|
||||||
|
- Monospace-assisted precision for time, numeric, and status data.
|
||||||
|
- Readability preserved during bursty live updates.
|
||||||
|
|
||||||
|
## Colors
|
||||||
|
|
||||||
|
The palette is operational and role-first: neutral cold surfaces carry most of the interface, with amber, green, red, and blue reserved for state and meaning.
|
||||||
|
|
||||||
|
### Primary
|
||||||
|
|
||||||
|
- **Signal Amber** (`#f5a623`): active controls, focus rails, status emphasis, and live interaction highlights.
|
||||||
|
|
||||||
|
### Secondary
|
||||||
|
|
||||||
|
- **Info Blue** (`#4da3ff`): replay states, neutral directional tags, and non-critical positive context.
|
||||||
|
|
||||||
|
### Tertiary
|
||||||
|
|
||||||
|
- **Confirm Green** (`#25c17a`): healthy connectivity and positive directional markers.
|
||||||
|
- **Risk Red** (`#ff6b5f`): stale/disconnected/error states and bearish risk markers.
|
||||||
|
|
||||||
|
### Neutral
|
||||||
|
|
||||||
|
- **Command Black** (`#06080b`): base shell and deepest background.
|
||||||
|
- **Panel Graphite** (`#111820`): primary container surfaces.
|
||||||
|
- **Elevation Slate** (`#0b1016`): raised or overlay-adjacent planes.
|
||||||
|
- **Data Ink** (`#e6edf4`): default text on dark surfaces.
|
||||||
|
- **Support Ink** (`#90a0b2`): secondary labels and metadata.
|
||||||
|
- **Trace Ink** (`#6e7b8c`): tertiary labels and low-priority framing.
|
||||||
|
|
||||||
|
### Named Rules
|
||||||
|
|
||||||
|
**The Signal Scarcity Rule.** Amber is a control and attention signal, not a wash. Keep it concentrated on actions, state edges, and critical counters.
|
||||||
|
|
||||||
|
**The Semantic Color Rule.** Red and green never stand alone for meaning. Every directional or severity cue must include text, shape, or positional confirmation.
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
**Display Font:** Quantico (fallback: sans-serif)
|
||||||
|
**Body Font:** IBM Plex Sans (fallback: sans-serif)
|
||||||
|
**Label/Mono Font:** IBM Plex Mono (fallback: monospace)
|
||||||
|
|
||||||
|
**Character:** The pairing is technical and composed. Quantico provides assertive waypoint headings, IBM Plex Sans keeps body copy readable, and IBM Plex Mono anchors temporal/numeric trust.
|
||||||
|
|
||||||
|
### Hierarchy
|
||||||
|
|
||||||
|
- **Display** (700, `clamp(2rem, 3vw, 2.8rem)`, 1.05): page-level and major section titles.
|
||||||
|
- **Headline** (700, `1.8rem`, 1.1): rail brand mark and high-salience panel titles.
|
||||||
|
- **Title** (600, `1rem`, 1.2): pane headings and focused section labels.
|
||||||
|
- **Body** (400, `0.92rem`, 1.45): default transactional and descriptive copy.
|
||||||
|
- **Label** (600, `0.72rem`, `0.12em`, uppercase): controls, chips, table headers, and instrumentation micro-labels.
|
||||||
|
|
||||||
|
### Named Rules
|
||||||
|
|
||||||
|
**The Instrument Label Rule.** Labels are short, uppercase, and spaced. They identify system state fast, without narrative phrasing.
|
||||||
|
|
||||||
|
## Elevation
|
||||||
|
|
||||||
|
The system is flat by default. Depth is primarily tonal (background and border deltas), with shadows reserved for overlays that require separation from live data.
|
||||||
|
|
||||||
|
### Shadow Vocabulary
|
||||||
|
|
||||||
|
- **Overlay Lift** (`0 24px 60px rgba(0, 0, 0, 0.42)`): filter popovers and floating control surfaces.
|
||||||
|
- **Drawer Lift** (`0 24px 70px rgba(0, 0, 0, 0.5)`): detail drawers and deep inspection layers.
|
||||||
|
- **Tooltip Lift** (`0 16px 40px rgba(0, 0, 0, 0.45)`): short-lived contextual tooltips.
|
||||||
|
|
||||||
|
### Named Rules
|
||||||
|
|
||||||
|
**The Flat-By-Default Rule.** If a surface is not floating over active workflow content, it does not get shadow lift.
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### Buttons
|
||||||
|
|
||||||
|
- **Shape:** compact rounded rectangle (`8px radius`) for standard controls, pill (`999px`) for segment toggles.
|
||||||
|
- **Primary:** subtle dark fill with bordered edge (`1px`, `rgba(255,255,255,0.08)`), label typography in uppercase mono (`0.72rem`).
|
||||||
|
- **Active State:** amber-tinted gradient/fill (`rgba(245,166,35,0.18 -> 0.08)`), stronger border and warmer text.
|
||||||
|
- **Focus/Interaction:** no bounce effects; state transitions stay short (`~150-180ms`) with opacity/color emphasis.
|
||||||
|
|
||||||
|
### Chips
|
||||||
|
|
||||||
|
- **Style:** pill chips (`999px`) with thin border and semantic soft fill.
|
||||||
|
- **State:** direction/severity/status chips map to green/red/blue semantic channels with text labels always present.
|
||||||
|
|
||||||
|
### Cards / Containers
|
||||||
|
|
||||||
|
- **Corner Style:** medium-soft corners (`12px` or `14px`) depending on container prominence.
|
||||||
|
- **Background:** layered dark surfaces (`#111820`, `#0d141b`) with restrained top-to-bottom sheen.
|
||||||
|
- **Shadow Strategy:** no default card shadow; only overlays and floating inspectors use lift shadows.
|
||||||
|
- **Border:** subtle perimeter lines (`rgba(255,255,255,0.08)` baseline).
|
||||||
|
- **Internal Padding:** primarily `16px-18px` with tighter inner rhythm (`8px-12px`) for controls.
|
||||||
|
|
||||||
|
### Inputs / Fields
|
||||||
|
|
||||||
|
- **Style:** mostly transparent text fields with underlined focus rails for global filter/search workflows.
|
||||||
|
- **Focus:** amber underline amplification and glow, paired with brighter field text.
|
||||||
|
- **Error/Disabled:** disabled uses opacity reduction; error state should be paired with label text, not color only.
|
||||||
|
|
||||||
|
### Navigation
|
||||||
|
|
||||||
|
- **Style:** rail links in uppercase label typography with `10px` radius and low-contrast base fill.
|
||||||
|
- **Hover/Active:** hover introduces border + subtle fill; active introduces amber-tinted background and stronger contrast.
|
||||||
|
- **Mobile Treatment:** rail collapses to top flow, controls stack vertically under `720px` while preserving full-width hit targets.
|
||||||
|
|
||||||
|
### Signature Component
|
||||||
|
|
||||||
|
- **Virtualized Data Tables:** fixed-height row lanes (`36px` and `44px` families), mono numeric columns, semantic row tinting, and stable scroll performance for live bursts.
|
||||||
|
|
||||||
|
## Do's and Don'ts
|
||||||
|
|
||||||
|
### Do:
|
||||||
|
|
||||||
|
- **Do** keep status and direction semantic with both color and text labels (`severity-high`, `direction-bullish`, explicit words).
|
||||||
|
- **Do** preserve compact control density (`8px-12px` padding range) so investigation actions stay within a short scan path.
|
||||||
|
- **Do** use amber as a sparse decision signal for active controls, focus rails, and key counters.
|
||||||
|
- **Do** keep overlays visually separated with dedicated shadow roles while leaving primary panes flat.
|
||||||
|
- **Do** design live updates to avoid flashing, excessive animation, and layout shifts during high-volume periods.
|
||||||
|
|
||||||
|
### Don't:
|
||||||
|
|
||||||
|
- **Don't** make Islandflow feel like a meme-stock or finfluencer trading app with hype, gamification, urgency theater, or promotional calls to action.
|
||||||
|
- **Don't** make Islandflow feel like a generic SaaS analytics dashboard with decorative gradients, vague card stacks, and non-actionable vanity metrics.
|
||||||
|
- **Don't** make Islandflow feel like Bloomberg-style visual density used as aesthetic cosplay instead of as a genuinely useful information structure.
|
||||||
|
- **Don't** rely on red/green alone for directional meaning or severity.
|
||||||
|
- **Don't** use colored side-stripe accents on rows/cards as the primary signifier; use complete semantic chips and labels instead.
|
||||||
38
PRODUCT.md
Normal file
38
PRODUCT.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Product
|
||||||
|
|
||||||
|
## Register
|
||||||
|
|
||||||
|
product
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
|
Islandflow is for serious individual traders and researchers working in live market conditions. They use real-time options flow, equity prints, inferred dark/off-exchange signals, and deterministic replay to investigate market behavior under pressure, where speed and confidence both matter.
|
||||||
|
|
||||||
|
## Product Purpose
|
||||||
|
|
||||||
|
Islandflow exists to help users quickly decide whether unusual market activity is meaningful, explainable, and actionable. The product should surface evidence fast enough to support real-time decisions, while preserving enough context and traceability to trust or dismiss a signal with confidence.
|
||||||
|
|
||||||
|
## Brand Personality
|
||||||
|
|
||||||
|
Precise, composed, forensic (with tactical tone when needed). The interface should feel like an instrument panel: utility-first, calm under load, and trustworthy. Brand voice should appear in orientation moments, empty states, onboarding, and high-level framing, while core workflows prioritize clarity, speed, and evidence.
|
||||||
|
|
||||||
|
## Anti-references
|
||||||
|
|
||||||
|
- Meme-stock or finfluencer-style trading apps that rely on hype, gamification, urgency theater, or promotional calls to action.
|
||||||
|
- Generic SaaS analytics dashboards with decorative gradients, vague card stacks, and non-actionable vanity metrics.
|
||||||
|
- Bloomberg-style visual density used as aesthetic cosplay instead of as a genuinely useful information structure.
|
||||||
|
|
||||||
|
## Design Principles
|
||||||
|
|
||||||
|
- Evidence before impression: every important signal should be explainable with clear supporting context.
|
||||||
|
- Utility over theater: visual choices must improve legibility, prioritization, and decision speed.
|
||||||
|
- Composure under volatility: interactions and layouts should remain stable and readable during bursts of market activity.
|
||||||
|
- Trust through precision: labels, states, and data semantics should be explicit, unambiguous, and internally consistent.
|
||||||
|
- Workflow-first framing: the interface should support investigative flow from detection to validation to action.
|
||||||
|
|
||||||
|
## Accessibility & Inclusion
|
||||||
|
|
||||||
|
- Target WCAG AA contrast at minimum across all core interfaces.
|
||||||
|
- Support reduced motion preferences, especially for live ticks, pulses, chart transitions, and alert animations.
|
||||||
|
- Never rely on red/green color alone for directionality or status; pair with text, icons, shape, and/or position.
|
||||||
|
- Keep real-time updates readable by avoiding flashing effects, excessive animation, and layout shifts during high-volume periods.
|
||||||
|
|
@ -46,6 +46,40 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-link {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 12px;
|
||||||
|
z-index: 40;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1px solid rgba(255, 216, 154, 0.44);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(7, 10, 14, 0.98);
|
||||||
|
color: #ffe2aa;
|
||||||
|
font-family: var(--font-mono), monospace;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transform: translateY(-160%);
|
||||||
|
transition: transform 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-link:focus-visible {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
button,
|
button,
|
||||||
input {
|
input {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
|
|
@ -88,10 +122,12 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-brand-name {
|
.terminal-brand-name {
|
||||||
|
min-width: 0;
|
||||||
font-family: var(--font-display), sans-serif;
|
font-family: var(--font-display), sans-serif;
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-nav {
|
.terminal-nav {
|
||||||
|
|
@ -100,6 +136,8 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-nav-link {
|
.terminal-nav-link {
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 44px;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
@ -116,6 +154,13 @@ input {
|
||||||
background: var(--bg-soft);
|
background: var(--bg-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal-nav-link:focus-visible,
|
||||||
|
.terminal-button:focus-visible,
|
||||||
|
.instrument-focus-chip button:focus-visible {
|
||||||
|
outline: 2px solid rgba(255, 216, 154, 0.88);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-nav-link-active {
|
.terminal-nav-link-active {
|
||||||
border-color: var(--border-strong);
|
border-color: var(--border-strong);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
|
|
@ -212,6 +257,7 @@ input {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
|
@ -222,6 +268,7 @@ input {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
|
|
@ -335,6 +382,7 @@ input {
|
||||||
.interval-button,
|
.interval-button,
|
||||||
.overlay-toggle,
|
.overlay-toggle,
|
||||||
.drawer-close {
|
.drawer-close {
|
||||||
|
min-height: 36px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
|
|
@ -366,6 +414,7 @@ input {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
max-width: min(360px, 32vw);
|
max-width: min(360px, 32vw);
|
||||||
padding: 5px 8px 5px 10px;
|
padding: 5px 8px 5px 10px;
|
||||||
|
|
@ -379,6 +428,7 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
.instrument-focus-chip span {
|
.instrument-focus-chip span {
|
||||||
|
min-width: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -1090,27 +1140,23 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table-row-classified.is-classified {
|
.data-table-row-classified.is-classified {
|
||||||
border-left: 3px solid rgba(var(--classifier-rgb), calc(0.35 + var(--classifier-intensity) * 0.45));
|
box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.28 + var(--classifier-intensity) * 0.24));
|
||||||
padding-left: 7px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table-row-warn,
|
.data-table-row-warn,
|
||||||
.data-table-row-severity-high,
|
.data-table-row-severity-high,
|
||||||
.data-table-row-direction-bearish {
|
.data-table-row-direction-bearish {
|
||||||
border-left: 3px solid rgba(255, 107, 95, 0.58);
|
box-shadow: inset 0 0 0 1px rgba(255, 107, 95, 0.46);
|
||||||
padding-left: 7px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table-row-severity-medium,
|
.data-table-row-severity-medium,
|
||||||
.data-table-row-direction-neutral {
|
.data-table-row-direction-neutral {
|
||||||
border-left: 3px solid rgba(77, 163, 255, 0.46);
|
box-shadow: inset 0 0 0 1px rgba(77, 163, 255, 0.36);
|
||||||
padding-left: 7px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table-row-severity-low,
|
.data-table-row-severity-low,
|
||||||
.data-table-row-direction-bullish {
|
.data-table-row-direction-bullish {
|
||||||
border-left: 3px solid rgba(37, 193, 122, 0.5);
|
box-shadow: inset 0 0 0 1px rgba(37, 193, 122, 0.38);
|
||||||
padding-left: 7px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table-options .data-table-head,
|
.data-table-options .data-table-head,
|
||||||
|
|
@ -1220,8 +1266,7 @@ h3 {
|
||||||
|
|
||||||
.options-table-row.is-classified {
|
.options-table-row.is-classified {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-left: 3px solid rgba(var(--classifier-rgb), calc(0.35 + var(--classifier-intensity) * 0.45));
|
box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.28 + var(--classifier-intensity) * 0.24));
|
||||||
padding-left: 7px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.options-table-row > span {
|
.options-table-row > span {
|
||||||
|
|
@ -1764,15 +1809,57 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-rail {
|
.terminal-rail {
|
||||||
position: static;
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 35;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(170px, auto) minmax(0, 1fr);
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px 18px;
|
||||||
|
padding: 14px 16px;
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal-brand {
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-brand-name {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-nav {
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-nav-link {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.shell-metrics {
|
.shell-metrics {
|
||||||
|
grid-column: 1 / -1;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(136px, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-metric {
|
||||||
|
min-width: 136px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-topbar {
|
||||||
|
position: static;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1811,7 +1898,6 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-topbar {
|
.terminal-topbar {
|
||||||
position: static;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
|
|
@ -1833,8 +1919,60 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
|
.terminal-shell {
|
||||||
|
background-size: 24px 24px, 24px 24px, 100% 100%, auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-rail {
|
||||||
|
position: static;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-brand {
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-brand-kicker {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-brand-name {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-nav {
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-nav-link {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-metrics {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-metric {
|
||||||
|
flex: 0 0 156px;
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-content {
|
.terminal-content {
|
||||||
padding: 18px 14px 22px;
|
padding: 16px 10px 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-shell {
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 1.55rem;
|
||||||
|
line-height: 1.06;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header,
|
.page-header,
|
||||||
|
|
@ -1849,6 +1987,27 @@ h3 {
|
||||||
.terminal-pane-title-row {
|
.terminal-pane-title-row {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-topbar {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 30;
|
||||||
|
padding: 12px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-button,
|
||||||
|
.mode-button,
|
||||||
|
.filter-clear,
|
||||||
|
.jump-button,
|
||||||
|
.pause-button,
|
||||||
|
.interval-button,
|
||||||
|
.overlay-toggle,
|
||||||
|
.drawer-close,
|
||||||
|
.contract-filter-button,
|
||||||
|
.filter-chip {
|
||||||
|
min-height: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-topbar-actions,
|
.terminal-topbar-actions,
|
||||||
|
|
@ -1864,6 +2023,19 @@ h3 {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal-topbar-mode .terminal-button,
|
||||||
|
.terminal-topbar-controls > .terminal-button,
|
||||||
|
.page-actions > .terminal-button,
|
||||||
|
.page-actions > .flow-filter-popover {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instrument-focus-chip {
|
||||||
|
max-width: none;
|
||||||
|
min-height: 44px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-filter {
|
.terminal-filter {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
@ -1873,10 +2045,46 @@ h3 {
|
||||||
|
|
||||||
.terminal-input {
|
.terminal-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-height: 38px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-pane {
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-pane-head,
|
||||||
|
.terminal-pane-body {
|
||||||
|
padding: 14px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-pane-actions,
|
||||||
|
.card-controls,
|
||||||
|
.chart-controls,
|
||||||
|
.tape-controls {
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tape-controls button {
|
||||||
|
flex: 1 1 112px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-inline {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
row-gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-inline-counter {
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-actions {
|
.page-actions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-filter-popover {
|
.flow-filter-popover {
|
||||||
|
|
@ -1890,11 +2098,13 @@ h3 {
|
||||||
|
|
||||||
.flow-filter-popover-panel {
|
.flow-filter-popover-panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: calc(var(--topbar-height) + 26px);
|
top: auto;
|
||||||
left: 14px;
|
bottom: calc(10px + env(safe-area-inset-bottom));
|
||||||
right: 14px;
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
width: auto;
|
width: auto;
|
||||||
max-height: min(68vh, 560px);
|
max-height: min(72vh, 560px);
|
||||||
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-filter-checkbox-grid,
|
.flow-filter-checkbox-grid,
|
||||||
|
|
@ -1908,6 +2118,39 @@ h3 {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-table-wrap {
|
||||||
|
margin-inline: -12px;
|
||||||
|
border-radius: 0;
|
||||||
|
scroll-snap-type: x proximity;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
min-width: 860px;
|
||||||
|
scroll-snap-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table-options,
|
||||||
|
.data-table-flow {
|
||||||
|
min-width: 1080px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table-head,
|
||||||
|
.data-table-row {
|
||||||
|
padding-inline: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table-row-options,
|
||||||
|
.data-table-row-equities {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table-row-flow,
|
||||||
|
.data-table-row-alerts,
|
||||||
|
.data-table-row-classifier,
|
||||||
|
.data-table-row-dark {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
@ -1917,10 +2160,31 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer {
|
.drawer {
|
||||||
position: static;
|
position: fixed;
|
||||||
|
inset: auto 10px calc(10px + env(safe-area-inset-bottom));
|
||||||
width: auto;
|
width: auto;
|
||||||
max-height: none;
|
max-height: min(78vh, 640px);
|
||||||
margin-top: 14px;
|
margin-top: 0;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
.terminal-content {
|
||||||
|
padding-inline: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-pane-head,
|
||||||
|
.terminal-pane-body {
|
||||||
|
padding-inline: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell-metric {
|
||||||
|
flex-basis: 142px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table-wrap {
|
||||||
|
margin-inline: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.synthetic-control-gear {
|
.synthetic-control-gear {
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,10 @@ import {
|
||||||
getTapeVirtualConfig,
|
getTapeVirtualConfig,
|
||||||
mergeNewestWithOverflow,
|
mergeNewestWithOverflow,
|
||||||
normalizeAlertSeverity,
|
normalizeAlertSeverity,
|
||||||
|
normalizeTickerFilterInput,
|
||||||
nextFlowFilterPopoverState,
|
nextFlowFilterPopoverState,
|
||||||
isSyntheticAdminVisible,
|
isSyntheticAdminVisible,
|
||||||
|
parseTickerFilterInput,
|
||||||
prunePinnedEntries,
|
prunePinnedEntries,
|
||||||
projectPausableTapeState,
|
projectPausableTapeState,
|
||||||
reducePausableTapeData,
|
reducePausableTapeData,
|
||||||
|
|
@ -412,6 +414,17 @@ describe("synthetic admin visibility", () => {
|
||||||
it("shows the internal control rail only when the public admin flag is enabled", () => {
|
it("shows the internal control rail only when the public admin flag is enabled", () => {
|
||||||
expect(isSyntheticAdminVisible("1")).toBe(true);
|
expect(isSyntheticAdminVisible("1")).toBe(true);
|
||||||
expect(isSyntheticAdminVisible("0")).toBe(false);
|
expect(isSyntheticAdminVisible("0")).toBe(false);
|
||||||
|
expect(isSyntheticAdminVisible(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("ticker filter helpers", () => {
|
||||||
|
it("normalizes pasted ticker input into a stable terminal format", () => {
|
||||||
|
expect(normalizeTickerFilterInput(" spy,\n nvda\u0000 aapl ")).toBe(" SPY, NVDA AAPL ");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses, uppercases, and deduplicates ticker tokens", () => {
|
||||||
|
expect(parseTickerFilterInput("spy, nvda spy\nqqq")).toEqual(["SPY", "NVDA", "QQQ"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useId,
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
|
|
@ -5054,6 +5055,25 @@ const formatFlowMetric = (value: number, suffix?: string): string => {
|
||||||
return value.toLocaleString();
|
return value.toLocaleString();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TICKER_FILTER_INPUT_MAX_LENGTH = 120;
|
||||||
|
|
||||||
|
export const normalizeTickerFilterInput = (value: string): string =>
|
||||||
|
value
|
||||||
|
.normalize("NFKC")
|
||||||
|
.replace(/[\u0000-\u001f\u007f]+/g, " ")
|
||||||
|
.replace(/,/g, ",")
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.toUpperCase()
|
||||||
|
.slice(0, TICKER_FILTER_INPUT_MAX_LENGTH);
|
||||||
|
|
||||||
|
export const parseTickerFilterInput = (value: string): string[] => {
|
||||||
|
const parts = normalizeTickerFilterInput(value)
|
||||||
|
.split(/[,\s]+/)
|
||||||
|
.map((part) => part.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
return Array.from(new Set(parts));
|
||||||
|
};
|
||||||
|
|
||||||
const useTerminalState = () => {
|
const useTerminalState = () => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]);
|
const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]);
|
||||||
|
|
@ -5069,13 +5089,7 @@ const useTerminalState = () => {
|
||||||
const [filterInput, setFilterInput] = useState<string>("");
|
const [filterInput, setFilterInput] = useState<string>("");
|
||||||
const [flowFilters, setFlowFilters] = useState<OptionFlowFilters>(() => buildDefaultFlowFilters());
|
const [flowFilters, setFlowFilters] = useState<OptionFlowFilters>(() => buildDefaultFlowFilters());
|
||||||
const [chartIntervalMs, setChartIntervalMs] = useState<number>(CANDLE_INTERVALS[0].ms);
|
const [chartIntervalMs, setChartIntervalMs] = useState<number>(CANDLE_INTERVALS[0].ms);
|
||||||
const activeTickers = useMemo(() => {
|
const activeTickers = useMemo(() => parseTickerFilterInput(filterInput), [filterInput]);
|
||||||
const parts = filterInput
|
|
||||||
.split(/[,\s]+/)
|
|
||||||
.map((value) => value.trim().toUpperCase())
|
|
||||||
.filter(Boolean);
|
|
||||||
return Array.from(new Set(parts));
|
|
||||||
}, [filterInput]);
|
|
||||||
const tickerSet = useMemo(() => new Set(activeTickers), [activeTickers]);
|
const tickerSet = useMemo(() => new Set(activeTickers), [activeTickers]);
|
||||||
const instrumentUnderlying = selectedInstrument?.underlyingId.toUpperCase() ?? null;
|
const instrumentUnderlying = selectedInstrument?.underlyingId.toUpperCase() ?? null;
|
||||||
const isOptionContractFocused = selectedInstrument?.kind === "option-contract";
|
const isOptionContractFocused = selectedInstrument?.kind === "option-contract";
|
||||||
|
|
@ -8348,20 +8362,26 @@ function SyntheticControlDock() {
|
||||||
export function TerminalAppShell({ children }: { children: ReactNode }) {
|
export function TerminalAppShell({ children }: { children: ReactNode }) {
|
||||||
const state = useTerminalState();
|
const state = useTerminalState();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const tickerFieldId = useId();
|
||||||
|
const tickerHintId = useId();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TerminalContext.Provider value={state}>
|
<TerminalContext.Provider value={state}>
|
||||||
<div className="terminal-shell">
|
<div className="terminal-shell">
|
||||||
|
<a className="skip-link" href="#terminal-content">
|
||||||
|
Skip to terminal content
|
||||||
|
</a>
|
||||||
<aside className="terminal-rail">
|
<aside className="terminal-rail">
|
||||||
<div className="terminal-brand">
|
<div className="terminal-brand">
|
||||||
<span className="terminal-brand-kicker">IF</span>
|
<span className="terminal-brand-kicker">IF</span>
|
||||||
<span className="terminal-brand-name">Islandflow</span>
|
<span className="terminal-brand-name">Islandflow</span>
|
||||||
</div>
|
</div>
|
||||||
<nav className="terminal-nav">
|
<nav aria-label="Primary" className="terminal-nav">
|
||||||
{NAV_ITEMS.map((item) => {
|
{NAV_ITEMS.map((item) => {
|
||||||
const active = pathname === item.href;
|
const active = pathname === item.href;
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
aria-current={active ? "page" : undefined}
|
||||||
className={`terminal-nav-link${active ? " terminal-nav-link-active" : ""}`}
|
className={`terminal-nav-link${active ? " terminal-nav-link-active" : ""}`}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
key={item.href}
|
key={item.href}
|
||||||
|
|
@ -8387,31 +8407,46 @@ export function TerminalAppShell({ children }: { children: ReactNode }) {
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
<label className="terminal-filter">
|
<label className="terminal-filter">
|
||||||
<span className="terminal-filter-label">Ticker</span>
|
<span className="terminal-filter-label" id={tickerHintId}>
|
||||||
|
Ticker
|
||||||
|
</span>
|
||||||
<span className="terminal-filter-field">
|
<span className="terminal-filter-field">
|
||||||
<input
|
<input
|
||||||
|
id={tickerFieldId}
|
||||||
|
aria-describedby={tickerHintId}
|
||||||
|
autoCapitalize="characters"
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
className="terminal-input"
|
className="terminal-input"
|
||||||
value={state.filterInput}
|
value={state.filterInput}
|
||||||
onChange={(event) => state.setFilterInput(event.target.value)}
|
inputMode="text"
|
||||||
|
maxLength={TICKER_FILTER_INPUT_MAX_LENGTH}
|
||||||
|
name="ticker-filter"
|
||||||
|
onChange={(event) => state.setFilterInput(normalizeTickerFilterInput(event.target.value))}
|
||||||
placeholder="SPY, NVDA, AAPL"
|
placeholder="SPY, NVDA, AAPL"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
|
aria-label="Clear ticker filter"
|
||||||
className="terminal-button"
|
className="terminal-button"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => state.setFilterInput("")}
|
onClick={() => state.setFilterInput("")}
|
||||||
disabled={state.filterInput.trim().length === 0}
|
disabled={state.filterInput.trim().length === 0}
|
||||||
|
title="Clear ticker filter"
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="terminal-topbar-mode">
|
<div className="terminal-topbar-mode">
|
||||||
<button
|
<button
|
||||||
|
aria-label={state.mode === "live" ? "Switch to replay mode" : "Switch to live mode"}
|
||||||
|
aria-pressed={state.mode !== "live"}
|
||||||
className="terminal-button terminal-button-primary"
|
className="terminal-button terminal-button-primary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={state.toggleMode}
|
onClick={state.toggleMode}
|
||||||
|
title={state.mode === "live" ? "Switch to replay mode" : "Switch to live mode"}
|
||||||
>
|
>
|
||||||
{state.mode === "live" ? "Replay" : "Live"}
|
{state.mode === "live" ? "Replay" : "Live"}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -8419,7 +8454,9 @@ export function TerminalAppShell({ children }: { children: ReactNode }) {
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="terminal-content">{children}</main>
|
<main className="terminal-content" id="terminal-content">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SyntheticControlDock />
|
<SyntheticControlDock />
|
||||||
|
|
|
||||||
486
docs/terminal-audit-2026-05-14-0432.html
Normal file
486
docs/terminal-audit-2026-05-14-0432.html
Normal file
|
|
@ -0,0 +1,486 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Terminal Audit - 2026-05-14 04:32</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #0b1016;
|
||||||
|
--panel: #111820;
|
||||||
|
--panel-2: #0d141b;
|
||||||
|
--border: rgba(255, 255, 255, 0.1);
|
||||||
|
--text: #e6edf4;
|
||||||
|
--text-dim: #90a0b2;
|
||||||
|
--text-faint: #6e7b8c;
|
||||||
|
--accent: #f5a623;
|
||||||
|
--accent-soft: rgba(245, 166, 35, 0.14);
|
||||||
|
--green: #25c17a;
|
||||||
|
--green-soft: rgba(37, 193, 122, 0.16);
|
||||||
|
--red: #ff6b5f;
|
||||||
|
--red-soft: rgba(255, 107, 95, 0.16);
|
||||||
|
--blue: #4da3ff;
|
||||||
|
--blue-soft: rgba(77, 163, 255, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Inter, "IBM Plex Sans", Arial, sans-serif;
|
||||||
|
background: linear-gradient(180deg, #081017 0%, #05070a 100%);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: min(1100px, calc(100vw - 32px));
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 32px 0 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
p,
|
||||||
|
ul,
|
||||||
|
ol,
|
||||||
|
table {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3 {
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
li,
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
line-height: 1.55;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lede {
|
||||||
|
color: var(--text-dim);
|
||||||
|
max-width: 76ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
padding: 24px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 16px;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.02));
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
margin: 14px 0 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-badge,
|
||||||
|
.pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-badge {
|
||||||
|
border-color: rgba(245, 166, 35, 0.35);
|
||||||
|
background: var(--accent-soft);
|
||||||
|
color: #ffe2aa;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill-good {
|
||||||
|
border-color: rgba(37, 193, 122, 0.35);
|
||||||
|
background: var(--green-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill-warn {
|
||||||
|
border-color: rgba(245, 166, 35, 0.35);
|
||||||
|
background: var(--accent-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill-risk {
|
||||||
|
border-color: rgba(255, 107, 95, 0.35);
|
||||||
|
background: var(--red-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
padding: 22px 24px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--panel);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subpanel {
|
||||||
|
padding: 18px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: var(--panel-2);
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.94rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 12px 10px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
color: var(--text-dim);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: "IBM Plex Mono", Menlo, monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
color: #ffe2aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding {
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding-top: 18px;
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finding:first-of-type {
|
||||||
|
border-top: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
color: var(--text-dim);
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta strong {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
color: var(--text-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
.verdict-pass {
|
||||||
|
color: #bfe7cf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-note {
|
||||||
|
color: var(--text-dim);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
main {
|
||||||
|
width: min(100vw - 20px, 1100px);
|
||||||
|
padding: 20px 0 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary,
|
||||||
|
.panel {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 10px 8px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<section class="summary">
|
||||||
|
<h1>Terminal View Audit</h1>
|
||||||
|
<p class="lede">
|
||||||
|
Audit report for the Islandflow terminal view, formatted for handoff and review. This preserves the
|
||||||
|
full findings set: scorecard, anti-pattern verdict, executive summary, detailed issues, systemic
|
||||||
|
patterns, positive findings, and recommended follow-up commands.
|
||||||
|
</p>
|
||||||
|
<div class="score-row">
|
||||||
|
<span class="score-badge">Overall Score: 11/20</span>
|
||||||
|
<span class="pill pill-warn">Rating Band: Acceptable</span>
|
||||||
|
<span class="pill pill-risk">Severity Mix: P0 0, P1 5, P2 3, P3 1</span>
|
||||||
|
<span class="pill">Generated: 2026-05-14 04:32</span>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
The terminal does <strong>not</strong> read as generic AI-generated UI overall. It has a coherent
|
||||||
|
instrument-panel identity, consistent density, and restrained accent use. The biggest problems are
|
||||||
|
implementation quality issues: invalid nested interactive controls, inaccessible drawer behavior,
|
||||||
|
weak focus treatment, mobile layouts that depend on horizontal scrolling, token drift, and repeated
|
||||||
|
banned side-stripe accents.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Audit Health Score</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Dimension</th>
|
||||||
|
<th>Score</th>
|
||||||
|
<th>Key Finding</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td>Accessibility</td>
|
||||||
|
<td>2/4</td>
|
||||||
|
<td>Invalid nested interactive controls in options rows.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td>Performance</td>
|
||||||
|
<td>3/4</td>
|
||||||
|
<td>Virtualization is good, but blur-heavy chrome and overlays add avoidable cost.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>3</td>
|
||||||
|
<td>Responsive Design</td>
|
||||||
|
<td>2/4</td>
|
||||||
|
<td>Core tables rely on large fixed minimum widths and horizontal scrolling.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>4</td>
|
||||||
|
<td>Theming</td>
|
||||||
|
<td>2/4</td>
|
||||||
|
<td>Token base exists, but many hard-coded colors and undefined vars bypass it.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>5</td>
|
||||||
|
<td>Anti-Patterns</td>
|
||||||
|
<td>2/4</td>
|
||||||
|
<td>Repeated side-stripe accents violate the stated design bans.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Total</strong></td>
|
||||||
|
<td></td>
|
||||||
|
<td><strong>11/20</strong></td>
|
||||||
|
<td><strong>Acceptable</strong></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Anti-Patterns Verdict</h2>
|
||||||
|
<p class="verdict-pass">
|
||||||
|
Pass, with caveats.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The terminal does not look AI-generated overall. It has a coherent instrument-panel identity,
|
||||||
|
consistent density, and restrained accent use. The main tells are implementation-level:
|
||||||
|
banned side-stripe accents on live rows, decorative blur-heavy chrome, and some product-UI
|
||||||
|
typography choices that drift toward display styling.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Executive Summary</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Audit Health Score:</strong> 11/20 (Acceptable)</li>
|
||||||
|
<li><strong>Total issues found:</strong> 9</li>
|
||||||
|
<li><strong>Severity mix:</strong> P0: 0, P1: 5, P2: 3, P3: 1</li>
|
||||||
|
<li>
|
||||||
|
<strong>Top issues:</strong> nested buttons inside clickable options rows, drawers that are not true
|
||||||
|
accessible dialogs, suppressed focus indicators, mobile dependence on oversized horizontal tables,
|
||||||
|
and repeated banned side-stripe row styling.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Detailed Findings By Severity</h2>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P1] Nested Interactive Controls Inside Clickable Row</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/terminal.tsx:7118-7135</code>, <code>apps/web/app/terminal.tsx:7155-7179</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Accessibility</p>
|
||||||
|
<p><strong>Impact:</strong> Decorated option rows render as outer <code><button></code> elements containing inner contract-focus <code><button></code> elements. This is invalid HTML and can create inconsistent tab order, click handling, and screen-reader output.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> WCAG 4.1.2 Name, Role, Value; HTML interactive content nesting rules</p>
|
||||||
|
<p><strong>Recommendation:</strong> Split row selection and contract focus into non-nested controls. Use a non-button row container with one explicit action button, or keep the row as the only button and turn inner controls into non-interactive text.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P1] Drawer Panels Are Visually Drawers, Not Accessible Dialogs</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/terminal.tsx:4524-4629</code>, <code>4639-4737</code>, <code>4747-4841</code>, <code>4850-4952</code>, close handling at <code>5070-5102</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Accessibility</p>
|
||||||
|
<p><strong>Impact:</strong> The drawers close on outside click and <code>Escape</code>, but they lack <code>role="dialog"</code>, <code>aria-modal</code>, focus entry, focus return, and trap behavior. Keyboard users can tab behind the drawer and lose context.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> WCAG 2.1.1 Keyboard, 2.4.3 Focus Order, 4.1.2 Name, Role, Value</p>
|
||||||
|
<p><strong>Recommendation:</strong> Promote drawers to true modal dialogs with labelled titles, initial focus, focus containment, inert background, and focus restoration on close.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P1] Focus Indicators Are Suppressed In Multiple Core Controls</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:325-327</code>, <code>413-415</code>, <code>1054-1058</code>, <code>1213-1218</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Accessibility</p>
|
||||||
|
<p><strong>Impact:</strong> Several controls explicitly remove the browser outline. Some surfaces get only a subtle background shift, which is weaker than a reliable visible focus ring, especially in dense data views.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> WCAG 2.4.7 Focus Visible</p>
|
||||||
|
<p><strong>Recommendation:</strong> Restore strong <code>:focus-visible</code> treatment on inputs, row buttons, and inline instrument actions using a consistent high-contrast ring or border treatment.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P1] ARIA Table Semantics Are Incomplete</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/terminal.tsx:7061-7075</code>, <code>7251-7259</code>, <code>7348-7359</code>, <code>7496-7505</code>, <code>7609-7616</code>, <code>7732-7739</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Accessibility</p>
|
||||||
|
<p><strong>Impact:</strong> The app uses <code>role="table"</code> and <code>role="row"</code> but not full table semantics such as <code>rowgroup</code>, <code>columnheader</code>, and cell roles. Screen readers will get a weaker structural model than a real table or fully formed ARIA grid.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> WCAG 1.3.1 Info and Relationships</p>
|
||||||
|
<p><strong>Recommendation:</strong> Prefer semantic <code><table></code> markup where possible, or complete the ARIA table structure consistently.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P1] Narrow-Screen Experience Depends On Oversized Horizontal Tables</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:967-1009</code>, <code>1116-1144</code>, <code>1645-1714</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Responsive Design</p>
|
||||||
|
<p><strong>Impact:</strong> Major views keep <code>min-width</code> values like <code>1280px</code>, <code>1260px</code>, <code>900px</code>, and <code>820px</code>. The mobile fallback is horizontal scroll rather than structural adaptation, which increases cognitive load and makes comparison harder on phones and small tablets.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> Responsive design best practice</p>
|
||||||
|
<p><strong>Recommendation:</strong> Define compact column sets, progressive disclosure, or cardless stacked row summaries under mobile breakpoints instead of preserving full desktop schema.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable adapt terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P2] Touch Targets Are Below Recommended Minimum In Key Controls</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:255</code>, <code>330-347</code>, <code>365-379</code>, <code>461-468</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Responsive Design</p>
|
||||||
|
<p><strong>Impact:</strong> Controls and chips commonly bottom out around <code>32px</code> height. That is workable on desktop, but it is tight for touch use and increases mis-taps on mobile.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> WCAG 2.5.5 Target Size (AAA), platform mobile guidance</p>
|
||||||
|
<p><strong>Recommendation:</strong> Raise interactive height to at least <code>40px</code>, ideally <code>44px</code>, for topbar controls, focus chips, and filter triggers under touch breakpoints.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable adapt terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P2] Token Discipline Is Partial, Not Consistent</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:41</code>, <code>306</code>, <code>321</code>, <code>362</code>, <code>375</code>, <code>479</code>, <code>502</code>, <code>565</code>, <code>616</code>, <code>763</code>, <code>1452-1470</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Theming</p>
|
||||||
|
<p><strong>Impact:</strong> The file starts with a clear token layer, but many later rules bypass it with hard-coded hex values. That makes palette evolution and future theme work harder.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> Theming and system quality</p>
|
||||||
|
<p><strong>Recommendation:</strong> Replace one-off literals with named variables, especially amber text variants, chart surface background, and severity-strip foreground colors.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable polish terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P2] Undefined CSS Variables Create Silent Theming Bugs</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:398</code>, <code>1186</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Theming</p>
|
||||||
|
<p><strong>Impact:</strong> <code>var(--text-muted)</code> and <code>var(--muted)</code> are referenced but not defined in <code>:root</code>. Those declarations will fail and fall back to inherited color, which makes the result fragile and inconsistent.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> CSS correctness</p>
|
||||||
|
<p><strong>Recommendation:</strong> Replace them with existing tokens such as <code>--text-dim</code> or define the missing variables explicitly.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable harden terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P2] Blur-Heavy Chrome Is Overused For Product UI</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:174-176</code>, <code>518-525</code>, <code>1504-1506</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Performance / Anti-Pattern</p>
|
||||||
|
<p><strong>Impact:</strong> <code>backdrop-filter: blur(12px)</code> and <code>blur(18px)</code> on persistent UI surfaces add cost and push the product UI slightly toward decorative glass treatment, which the design rules explicitly warn against as a default.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> Performance and product-design guidance</p>
|
||||||
|
<p><strong>Recommendation:</strong> Keep blur only where separation is essential, or replace it with tonal contrast and border treatment.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable quieter terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="finding">
|
||||||
|
<h3>[P3] Side-Stripe Row Accents Are Repeated Across Tables</h3>
|
||||||
|
<p class="meta"><strong>Location:</strong> <code>apps/web/app/globals.css:1092-1114</code>, <code>1221-1224</code></p>
|
||||||
|
<p class="meta"><strong>Category:</strong> Anti-Pattern</p>
|
||||||
|
<p><strong>Impact:</strong> The interface repeatedly uses <code>border-left: 3px</code> to communicate severity, direction, and classifier state. That is one of the skill's explicit banned patterns and makes rows feel more template-like than intentional.</p>
|
||||||
|
<p><strong>WCAG/Standard:</strong> Design-system rule</p>
|
||||||
|
<p><strong>Recommendation:</strong> Move semantic emphasis into full-row tinting, chips, iconography, or stronger text hierarchy instead of colored side rails.</p>
|
||||||
|
<p><strong>Suggested command:</strong> <code>$impeccable polish terminal view</code></p>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Patterns And Systemic Issues</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Accessibility semantics are strongest at the surface level, labels and buttons exist, but weaker in composite patterns, drawers, virtualized tables, and focus handling.</li>
|
||||||
|
<li>The responsive strategy is mostly preserve desktop density and allow scrolling, not restructure the workflow for narrow screens.</li>
|
||||||
|
<li>The CSS starts from a tokenized system, then drifts into literal color values in later component rules.</li>
|
||||||
|
<li>The visual system is disciplined overall, but a few repeated product bans, side stripes and default blur, show up across multiple components.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Positive Findings</h2>
|
||||||
|
<ul>
|
||||||
|
<li>The terminal has a clear, distinctive product identity without falling into meme-trader styling.</li>
|
||||||
|
<li>Virtualized list rendering is the right performance baseline for these dense live data views.</li>
|
||||||
|
<li>The top-level shell and pane structure are predictable and support fast scanning.</li>
|
||||||
|
<li>Core inputs are labelled, and many actionable rows are implemented as real buttons instead of click-only divs.</li>
|
||||||
|
<li>Color usage is generally restrained and semantically meaningful, even where implementation cleanup is still needed.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Recommended Actions</h2>
|
||||||
|
<ol>
|
||||||
|
<li><strong>[P1] <code>$impeccable harden terminal view</code>:</strong> Fix nested buttons, accessible dialog behavior, focus visibility, and incomplete ARIA table semantics.</li>
|
||||||
|
<li><strong>[P1] <code>$impeccable adapt terminal view</code>:</strong> Redesign narrow-screen table behavior and increase touch target sizes in the shell and filter controls.</li>
|
||||||
|
<li><strong>[P2] <code>$impeccable quieter terminal view</code>:</strong> Reduce default blur and glass treatment in topbar, popover, and drawer chrome.</li>
|
||||||
|
<li><strong>[P2] <code>$impeccable polish terminal view</code>:</strong> Normalize tokens, remove hard-coded color drift, and replace banned side-stripe accents.</li>
|
||||||
|
<li><strong>[P2] <code>$impeccable polish terminal view</code>:</strong> Final cleanup pass once the structural fixes are in.</li>
|
||||||
|
</ol>
|
||||||
|
<div class="subpanel">
|
||||||
|
<p class="footer-note">
|
||||||
|
You can ask me to run these one at a time, all at once, or in any order you prefer.
|
||||||
|
</p>
|
||||||
|
<p class="footer-note">
|
||||||
|
Re-run <code>$impeccable audit</code> after fixes to see the score improve.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
308
docs/turns/2026-05-14-harden-terminal-view.html
Normal file
308
docs/turns/2026-05-14-harden-terminal-view.html
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Turn Document - Harden Terminal View</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
--bg: #06080b;
|
||||||
|
--panel: #111820;
|
||||||
|
--panel-2: #0d141b;
|
||||||
|
--border: rgba(255, 255, 255, 0.1);
|
||||||
|
--text: #e6edf4;
|
||||||
|
--text-dim: #90a0b2;
|
||||||
|
--text-faint: #6e7b8c;
|
||||||
|
--accent: #f5a623;
|
||||||
|
--accent-soft: rgba(245, 166, 35, 0.14);
|
||||||
|
--green-soft: rgba(37, 193, 122, 0.16);
|
||||||
|
--red-soft: rgba(255, 107, 95, 0.16);
|
||||||
|
--blue-soft: rgba(77, 163, 255, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "IBM Plex Sans", Inter, Arial, sans-serif;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top left, rgba(245, 166, 35, 0.08), transparent 24%),
|
||||||
|
linear-gradient(180deg, #081017 0%, #05070a 100%);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: min(1040px, calc(100vw - 32px));
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 28px 0 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
p,
|
||||||
|
ul,
|
||||||
|
pre {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
font-family: Quantico, "IBM Plex Sans", sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: clamp(1.8rem, 3vw, 2.5rem);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
li {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary,
|
||||||
|
.panel {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 16px;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.02));
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
padding: 22px 24px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
background: var(--panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lede,
|
||||||
|
.meta,
|
||||||
|
.note {
|
||||||
|
color: var(--text-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 16px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill-accent {
|
||||||
|
border-color: rgba(245, 166, 35, 0.36);
|
||||||
|
background: var(--accent-soft);
|
||||||
|
color: #ffe2aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill-good {
|
||||||
|
background: var(--green-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill-info {
|
||||||
|
background: var(--blue-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill-risk {
|
||||||
|
background: var(--red-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 18px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li + li {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
pre {
|
||||||
|
font-family: "IBM Plex Mono", Menlo, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: #ffe2aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--panel-2);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-col {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
main {
|
||||||
|
width: min(100vw - 20px, 1040px);
|
||||||
|
padding: 20px 0 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary,
|
||||||
|
.panel {
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-col {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<section class="summary">
|
||||||
|
<h1>Harden Terminal View</h1>
|
||||||
|
<p class="lede">
|
||||||
|
Turn document for the terminal shell hardening pass in <code>apps/web/app/terminal.tsx</code>,
|
||||||
|
<code>apps/web/app/globals.css</code>, and <code>apps/web/app/terminal.test.ts</code>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The work focused on production resilience in the main terminal shell: keyboard access, focus visibility,
|
||||||
|
long-text behavior, topbar wrapping, and ticker filter normalization for pasted or malformed input.
|
||||||
|
</p>
|
||||||
|
<div class="meta-row">
|
||||||
|
<span class="pill pill-accent">Generated: 2026-05-14 11:24 EDT</span>
|
||||||
|
<span class="pill pill-good">Tests: Passed</span>
|
||||||
|
<span class="pill pill-good">Web Build: Passed</span>
|
||||||
|
<span class="pill pill-info">Beads: islandflow-6ri closed</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Summary</h2>
|
||||||
|
<p>
|
||||||
|
The terminal shell now behaves more predictably under constrained widths and less-perfect input. The
|
||||||
|
changes stay small and local, but improve accessibility and reduce UI breakage risk in the top-level
|
||||||
|
workflow.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Changes Made</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Added a skip link targeting the main terminal content region.</li>
|
||||||
|
<li>Added primary navigation semantics with <code>aria-label</code> and <code>aria-current</code>.</li>
|
||||||
|
<li>Added visible keyboard focus treatment for nav links, shell buttons, and the instrument chip action.</li>
|
||||||
|
<li>Allowed topbar action groups to wrap instead of forcing overflow at narrower widths.</li>
|
||||||
|
<li>Added long-text hardening for the brand name and selected instrument chip.</li>
|
||||||
|
<li>Added ticker filter input normalization, uppercase handling, control-character cleanup, and length limits.</li>
|
||||||
|
<li>Added unit tests for ticker filter normalization and parsing.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Context</h2>
|
||||||
|
<p>
|
||||||
|
Islandflow's terminal is an evidence-first product surface used under time pressure. The shell is not a
|
||||||
|
decorative wrapper. It controls navigation, global filter state, and mode switching, so failures here can
|
||||||
|
degrade every route at once.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The hardening pass stayed focused on shell-level reliability rather than introducing broader layout or
|
||||||
|
component refactors.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Important Implementation Details</h2>
|
||||||
|
<div class="two-col">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
The ticker filter path now normalizes paste-heavy input before it reaches state-dependent parsing.
|
||||||
|
This covers full-width commas, repeated whitespace, control characters, and casing drift.
|
||||||
|
</p>
|
||||||
|
<pre><code>export const normalizeTickerFilterInput = (value: string): string =>
|
||||||
|
value
|
||||||
|
.normalize("NFKC")
|
||||||
|
.replace(/[\u0000-\u001f\u007f]+/g, " ")
|
||||||
|
.replace(/,/g, ",")
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.toUpperCase()</code></pre>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Shell semantics were strengthened without changing the route structure. The new skip link and current-page
|
||||||
|
annotation improve keyboard and assistive navigation while staying visually quiet during normal use.
|
||||||
|
</p>
|
||||||
|
<pre><code><a class="skip-link" href="#terminal-content">Skip to terminal content</a>
|
||||||
|
|
||||||
|
<nav aria-label="Primary" className="terminal-nav">
|
||||||
|
<Link aria-current={active ? "page" : undefined} ... />
|
||||||
|
</nav></code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Validation</h2>
|
||||||
|
<ul>
|
||||||
|
<li><code>bun test apps/web/app/terminal.test.ts</code> passed.</li>
|
||||||
|
<li><code>bun --cwd=apps/web run build</code> passed.</li>
|
||||||
|
<li>Regression coverage was added for normalization and token parsing of ticker input.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Issues, Limitations, and Mitigations</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<strong>Dolt sync limitation:</strong> <code>bd dolt pull</code> failed earlier in the session because no Dolt
|
||||||
|
remote is configured in this workspace. The code work continued, but beads remote sync was not available.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Scope control:</strong> this pass hardened the shell only. It did not audit every downstream pane,
|
||||||
|
drawer, or popover for similar edge cases.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Workflow status:</strong> this document records the implementation and validation, but the work was not
|
||||||
|
committed or pushed as part of this turn.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel">
|
||||||
|
<h2>Follow-up Work</h2>
|
||||||
|
<ul>
|
||||||
|
<li>No follow-up issue was created from this hardening pass beyond the completed beads item <code>islandflow-6ri</code>.</li>
|
||||||
|
<li>If terminal adaptation work continues, the next pass should examine small-screen drawer behavior and popover placement under dense live states.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="note">
|
||||||
|
Document created to satisfy the required turn-documentation step for implementation changes.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue