diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 6051b73..51bb12b 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -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-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-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-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} diff --git a/.impeccable/design.json b/.impeccable/design.json new file mode 100644 index 0000000..b42f6cf --- /dev/null +++ b/.impeccable/design.json @@ -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": "", + "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": "Signals", + "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": "", + "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": "Bearish", + "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": "

Flow Packets

Pane content
", + "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": "", + "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." + ] + } +} diff --git a/AGENTS.md b/AGENTS.md index c3f5e63..351b68c 100644 --- a/AGENTS.md +++ b/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. - 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 - 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 were addressed or mitigated by the changes. -- The document should be stored in the `docs/turns` directory. - -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. +- 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 diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..d1f2a68 --- /dev/null +++ b/DESIGN.md @@ -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. diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 0000000..5072e04 --- /dev/null +++ b/PRODUCT.md @@ -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. diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 777505b..3232e6d 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -46,6 +46,40 @@ a { 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, input { font: inherit; @@ -88,10 +122,12 @@ input { } .terminal-brand-name { + min-width: 0; font-family: var(--font-display), sans-serif; font-size: 1.8rem; letter-spacing: 0.08em; text-transform: uppercase; + overflow-wrap: anywhere; } .terminal-nav { @@ -100,6 +136,8 @@ input { } .terminal-nav-link { + min-width: 0; + min-height: 44px; padding: 12px 14px; border: 1px solid transparent; border-radius: 10px; @@ -116,6 +154,13 @@ input { 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 { border-color: var(--border-strong); color: var(--text); @@ -212,6 +257,7 @@ input { display: flex; align-items: center; justify-content: flex-end; + flex-wrap: wrap; gap: 12px; min-width: 0; width: auto; @@ -222,6 +268,7 @@ input { display: flex; align-items: center; justify-content: flex-end; + flex-wrap: wrap; gap: 10px; min-width: 0; flex: 0 1 auto; @@ -335,6 +382,7 @@ input { .interval-button, .overlay-toggle, .drawer-close { + min-height: 36px; border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; @@ -366,6 +414,7 @@ input { display: inline-flex; align-items: center; gap: 8px; + min-width: 0; min-height: 32px; max-width: min(360px, 32vw); padding: 5px 8px 5px 10px; @@ -379,6 +428,7 @@ input { } .instrument-focus-chip span { + min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -1090,27 +1140,23 @@ h3 { } .data-table-row-classified.is-classified { - border-left: 3px solid rgba(var(--classifier-rgb), calc(0.35 + var(--classifier-intensity) * 0.45)); - padding-left: 7px; + box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.28 + var(--classifier-intensity) * 0.24)); } .data-table-row-warn, .data-table-row-severity-high, .data-table-row-direction-bearish { - border-left: 3px solid rgba(255, 107, 95, 0.58); - padding-left: 7px; + box-shadow: inset 0 0 0 1px rgba(255, 107, 95, 0.46); } .data-table-row-severity-medium, .data-table-row-direction-neutral { - border-left: 3px solid rgba(77, 163, 255, 0.46); - padding-left: 7px; + box-shadow: inset 0 0 0 1px rgba(77, 163, 255, 0.36); } .data-table-row-severity-low, .data-table-row-direction-bullish { - border-left: 3px solid rgba(37, 193, 122, 0.5); - padding-left: 7px; + box-shadow: inset 0 0 0 1px rgba(37, 193, 122, 0.38); } .data-table-options .data-table-head, @@ -1220,8 +1266,7 @@ h3 { .options-table-row.is-classified { cursor: pointer; - border-left: 3px solid rgba(var(--classifier-rgb), calc(0.35 + var(--classifier-intensity) * 0.45)); - padding-left: 7px; + box-shadow: inset 0 0 0 1px rgba(var(--classifier-rgb), calc(0.28 + var(--classifier-intensity) * 0.24)); } .options-table-row > span { @@ -1764,15 +1809,57 @@ h3 { } .terminal-rail { - position: static; + position: sticky; + top: 0; + z-index: 35; 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-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 { + grid-column: 1 / -1; 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 { - position: static; align-items: center; justify-content: flex-end; padding: 10px 16px; @@ -1833,8 +1919,60 @@ h3 { } @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 { - padding: 18px 14px 22px; + padding: 16px 10px 22px; + } + + .page-shell { + gap: 14px; + } + + .page-title { + font-size: 1.55rem; + line-height: 1.06; } .page-header, @@ -1849,6 +1987,27 @@ h3 { .terminal-pane-title-row { flex-direction: column; 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, @@ -1864,6 +2023,19 @@ h3 { 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 { width: 100%; min-width: 0; @@ -1873,10 +2045,46 @@ h3 { .terminal-input { 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 { width: 100%; + flex-direction: column; + align-items: stretch; } .flow-filter-popover { @@ -1890,11 +2098,13 @@ h3 { .flow-filter-popover-panel { position: fixed; - top: calc(var(--topbar-height) + 26px); - left: 14px; - right: 14px; + top: auto; + bottom: calc(10px + env(safe-area-inset-bottom)); + left: 10px; + right: 10px; width: auto; - max-height: min(68vh, 560px); + max-height: min(72vh, 560px); + border-radius: 16px; } .flow-filter-checkbox-grid, @@ -1908,6 +2118,39 @@ h3 { 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 { text-align: left; } @@ -1917,10 +2160,31 @@ h3 { } .drawer { - position: static; + position: fixed; + inset: auto 10px calc(10px + env(safe-area-inset-bottom)); width: auto; - max-height: none; - margin-top: 14px; + max-height: min(78vh, 640px); + 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 { diff --git a/apps/web/app/terminal.test.ts b/apps/web/app/terminal.test.ts index 20647ca..8878fd9 100644 --- a/apps/web/app/terminal.test.ts +++ b/apps/web/app/terminal.test.ts @@ -27,8 +27,10 @@ import { getTapeVirtualConfig, mergeNewestWithOverflow, normalizeAlertSeverity, + normalizeTickerFilterInput, nextFlowFilterPopoverState, isSyntheticAdminVisible, + parseTickerFilterInput, prunePinnedEntries, projectPausableTapeState, reducePausableTapeData, @@ -412,6 +414,17 @@ describe("synthetic admin visibility", () => { it("shows the internal control rail only when the public admin flag is enabled", () => { expect(isSyntheticAdminVisible("1")).toBe(true); 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"]); }); }); diff --git a/apps/web/app/terminal.tsx b/apps/web/app/terminal.tsx index e4d496e..20070fe 100644 --- a/apps/web/app/terminal.tsx +++ b/apps/web/app/terminal.tsx @@ -8,6 +8,7 @@ import { useCallback, useContext, useEffect, + useId, useLayoutEffect, useMemo, useRef, @@ -5054,6 +5055,25 @@ const formatFlowMetric = (value: number, suffix?: string): string => { 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 pathname = usePathname(); const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]); @@ -5069,13 +5089,7 @@ const useTerminalState = () => { const [filterInput, setFilterInput] = useState(""); const [flowFilters, setFlowFilters] = useState(() => buildDefaultFlowFilters()); const [chartIntervalMs, setChartIntervalMs] = useState(CANDLE_INTERVALS[0].ms); - const activeTickers = useMemo(() => { - const parts = filterInput - .split(/[,\s]+/) - .map((value) => value.trim().toUpperCase()) - .filter(Boolean); - return Array.from(new Set(parts)); - }, [filterInput]); + const activeTickers = useMemo(() => parseTickerFilterInput(filterInput), [filterInput]); const tickerSet = useMemo(() => new Set(activeTickers), [activeTickers]); const instrumentUnderlying = selectedInstrument?.underlyingId.toUpperCase() ?? null; const isOptionContractFocused = selectedInstrument?.kind === "option-contract"; @@ -8348,20 +8362,26 @@ function SyntheticControlDock() { export function TerminalAppShell({ children }: { children: ReactNode }) { const state = useTerminalState(); const pathname = usePathname(); + const tickerFieldId = useId(); + const tickerHintId = useId(); return (
+ + Skip to terminal content +
@@ -8419,7 +8454,9 @@ export function TerminalAppShell({ children }: { children: ReactNode }) {
-
{children}
+
+ {children} +
diff --git a/docs/terminal-audit-2026-05-14-0432.html b/docs/terminal-audit-2026-05-14-0432.html new file mode 100644 index 0000000..20a063a --- /dev/null +++ b/docs/terminal-audit-2026-05-14-0432.html @@ -0,0 +1,486 @@ + + + + + + Terminal Audit - 2026-05-14 04:32 + + + +
+
+

Terminal View Audit

+

+ 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. +

+
+ Overall Score: 11/20 + Rating Band: Acceptable + Severity Mix: P0 0, P1 5, P2 3, P3 1 + Generated: 2026-05-14 04:32 +
+

+ The terminal does not 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. +

+
+ +
+

Audit Health Score

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#DimensionScoreKey Finding
1Accessibility2/4Invalid nested interactive controls in options rows.
2Performance3/4Virtualization is good, but blur-heavy chrome and overlays add avoidable cost.
3Responsive Design2/4Core tables rely on large fixed minimum widths and horizontal scrolling.
4Theming2/4Token base exists, but many hard-coded colors and undefined vars bypass it.
5Anti-Patterns2/4Repeated side-stripe accents violate the stated design bans.
Total11/20Acceptable
+
+ +
+

Anti-Patterns Verdict

+

+ Pass, with caveats. +

+

+ 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. +

+
+ +
+

Executive Summary

+
    +
  • Audit Health Score: 11/20 (Acceptable)
  • +
  • Total issues found: 9
  • +
  • Severity mix: P0: 0, P1: 5, P2: 3, P3: 1
  • +
  • + Top issues: 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. +
  • +
+
+ +
+

Detailed Findings By Severity

+ +
+

[P1] Nested Interactive Controls Inside Clickable Row

+

Location: apps/web/app/terminal.tsx:7118-7135, apps/web/app/terminal.tsx:7155-7179

+

Category: Accessibility

+

Impact: Decorated option rows render as outer <button> elements containing inner contract-focus <button> elements. This is invalid HTML and can create inconsistent tab order, click handling, and screen-reader output.

+

WCAG/Standard: WCAG 4.1.2 Name, Role, Value; HTML interactive content nesting rules

+

Recommendation: 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.

+

Suggested command: $impeccable harden terminal view

+
+ +
+

[P1] Drawer Panels Are Visually Drawers, Not Accessible Dialogs

+

Location: apps/web/app/terminal.tsx:4524-4629, 4639-4737, 4747-4841, 4850-4952, close handling at 5070-5102

+

Category: Accessibility

+

Impact: The drawers close on outside click and Escape, but they lack role="dialog", aria-modal, focus entry, focus return, and trap behavior. Keyboard users can tab behind the drawer and lose context.

+

WCAG/Standard: WCAG 2.1.1 Keyboard, 2.4.3 Focus Order, 4.1.2 Name, Role, Value

+

Recommendation: Promote drawers to true modal dialogs with labelled titles, initial focus, focus containment, inert background, and focus restoration on close.

+

Suggested command: $impeccable harden terminal view

+
+ +
+

[P1] Focus Indicators Are Suppressed In Multiple Core Controls

+

Location: apps/web/app/globals.css:325-327, 413-415, 1054-1058, 1213-1218

+

Category: Accessibility

+

Impact: 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.

+

WCAG/Standard: WCAG 2.4.7 Focus Visible

+

Recommendation: Restore strong :focus-visible treatment on inputs, row buttons, and inline instrument actions using a consistent high-contrast ring or border treatment.

+

Suggested command: $impeccable harden terminal view

+
+ +
+

[P1] ARIA Table Semantics Are Incomplete

+

Location: apps/web/app/terminal.tsx:7061-7075, 7251-7259, 7348-7359, 7496-7505, 7609-7616, 7732-7739

+

Category: Accessibility

+

Impact: The app uses role="table" and role="row" but not full table semantics such as rowgroup, columnheader, and cell roles. Screen readers will get a weaker structural model than a real table or fully formed ARIA grid.

+

WCAG/Standard: WCAG 1.3.1 Info and Relationships

+

Recommendation: Prefer semantic <table> markup where possible, or complete the ARIA table structure consistently.

+

Suggested command: $impeccable harden terminal view

+
+ +
+

[P1] Narrow-Screen Experience Depends On Oversized Horizontal Tables

+

Location: apps/web/app/globals.css:967-1009, 1116-1144, 1645-1714

+

Category: Responsive Design

+

Impact: Major views keep min-width values like 1280px, 1260px, 900px, and 820px. The mobile fallback is horizontal scroll rather than structural adaptation, which increases cognitive load and makes comparison harder on phones and small tablets.

+

WCAG/Standard: Responsive design best practice

+

Recommendation: Define compact column sets, progressive disclosure, or cardless stacked row summaries under mobile breakpoints instead of preserving full desktop schema.

+

Suggested command: $impeccable adapt terminal view

+
+ +
+

[P2] Touch Targets Are Below Recommended Minimum In Key Controls

+

Location: apps/web/app/globals.css:255, 330-347, 365-379, 461-468

+

Category: Responsive Design

+

Impact: Controls and chips commonly bottom out around 32px height. That is workable on desktop, but it is tight for touch use and increases mis-taps on mobile.

+

WCAG/Standard: WCAG 2.5.5 Target Size (AAA), platform mobile guidance

+

Recommendation: Raise interactive height to at least 40px, ideally 44px, for topbar controls, focus chips, and filter triggers under touch breakpoints.

+

Suggested command: $impeccable adapt terminal view

+
+ +
+

[P2] Token Discipline Is Partial, Not Consistent

+

Location: apps/web/app/globals.css:41, 306, 321, 362, 375, 479, 502, 565, 616, 763, 1452-1470

+

Category: Theming

+

Impact: 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.

+

WCAG/Standard: Theming and system quality

+

Recommendation: Replace one-off literals with named variables, especially amber text variants, chart surface background, and severity-strip foreground colors.

+

Suggested command: $impeccable polish terminal view

+
+ +
+

[P2] Undefined CSS Variables Create Silent Theming Bugs

+

Location: apps/web/app/globals.css:398, 1186

+

Category: Theming

+

Impact: var(--text-muted) and var(--muted) are referenced but not defined in :root. Those declarations will fail and fall back to inherited color, which makes the result fragile and inconsistent.

+

WCAG/Standard: CSS correctness

+

Recommendation: Replace them with existing tokens such as --text-dim or define the missing variables explicitly.

+

Suggested command: $impeccable harden terminal view

+
+ +
+

[P2] Blur-Heavy Chrome Is Overused For Product UI

+

Location: apps/web/app/globals.css:174-176, 518-525, 1504-1506

+

Category: Performance / Anti-Pattern

+

Impact: backdrop-filter: blur(12px) and blur(18px) 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.

+

WCAG/Standard: Performance and product-design guidance

+

Recommendation: Keep blur only where separation is essential, or replace it with tonal contrast and border treatment.

+

Suggested command: $impeccable quieter terminal view

+
+ +
+

[P3] Side-Stripe Row Accents Are Repeated Across Tables

+

Location: apps/web/app/globals.css:1092-1114, 1221-1224

+

Category: Anti-Pattern

+

Impact: The interface repeatedly uses border-left: 3px 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.

+

WCAG/Standard: Design-system rule

+

Recommendation: Move semantic emphasis into full-row tinting, chips, iconography, or stronger text hierarchy instead of colored side rails.

+

Suggested command: $impeccable polish terminal view

+
+
+ +
+

Patterns And Systemic Issues

+
    +
  • Accessibility semantics are strongest at the surface level, labels and buttons exist, but weaker in composite patterns, drawers, virtualized tables, and focus handling.
  • +
  • The responsive strategy is mostly preserve desktop density and allow scrolling, not restructure the workflow for narrow screens.
  • +
  • The CSS starts from a tokenized system, then drifts into literal color values in later component rules.
  • +
  • The visual system is disciplined overall, but a few repeated product bans, side stripes and default blur, show up across multiple components.
  • +
+
+ +
+

Positive Findings

+
    +
  • The terminal has a clear, distinctive product identity without falling into meme-trader styling.
  • +
  • Virtualized list rendering is the right performance baseline for these dense live data views.
  • +
  • The top-level shell and pane structure are predictable and support fast scanning.
  • +
  • Core inputs are labelled, and many actionable rows are implemented as real buttons instead of click-only divs.
  • +
  • Color usage is generally restrained and semantically meaningful, even where implementation cleanup is still needed.
  • +
+
+ +
+

Recommended Actions

+
    +
  1. [P1] $impeccable harden terminal view: Fix nested buttons, accessible dialog behavior, focus visibility, and incomplete ARIA table semantics.
  2. +
  3. [P1] $impeccable adapt terminal view: Redesign narrow-screen table behavior and increase touch target sizes in the shell and filter controls.
  4. +
  5. [P2] $impeccable quieter terminal view: Reduce default blur and glass treatment in topbar, popover, and drawer chrome.
  6. +
  7. [P2] $impeccable polish terminal view: Normalize tokens, remove hard-coded color drift, and replace banned side-stripe accents.
  8. +
  9. [P2] $impeccable polish terminal view: Final cleanup pass once the structural fixes are in.
  10. +
+
+ + +
+
+
+ + diff --git a/docs/turns/2026-05-14-harden-terminal-view.html b/docs/turns/2026-05-14-harden-terminal-view.html new file mode 100644 index 0000000..778391c --- /dev/null +++ b/docs/turns/2026-05-14-harden-terminal-view.html @@ -0,0 +1,308 @@ + + + + + + Turn Document - Harden Terminal View + + + +
+
+

Harden Terminal View

+

+ Turn document for the terminal shell hardening pass in apps/web/app/terminal.tsx, + apps/web/app/globals.css, and apps/web/app/terminal.test.ts. +

+

+ 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. +

+
+ Generated: 2026-05-14 11:24 EDT + Tests: Passed + Web Build: Passed + Beads: islandflow-6ri closed +
+
+ +
+

Summary

+

+ 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. +

+
+ +
+

Changes Made

+
    +
  • Added a skip link targeting the main terminal content region.
  • +
  • Added primary navigation semantics with aria-label and aria-current.
  • +
  • Added visible keyboard focus treatment for nav links, shell buttons, and the instrument chip action.
  • +
  • Allowed topbar action groups to wrap instead of forcing overflow at narrower widths.
  • +
  • Added long-text hardening for the brand name and selected instrument chip.
  • +
  • Added ticker filter input normalization, uppercase handling, control-character cleanup, and length limits.
  • +
  • Added unit tests for ticker filter normalization and parsing.
  • +
+
+ +
+

Context

+

+ 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. +

+

+ The hardening pass stayed focused on shell-level reliability rather than introducing broader layout or + component refactors. +

+
+ +
+

Important Implementation Details

+
+
+

+ 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. +

+
export const normalizeTickerFilterInput = (value: string): string =>
+  value
+    .normalize("NFKC")
+    .replace(/[\u0000-\u001f\u007f]+/g, " ")
+    .replace(/,/g, ",")
+    .replace(/\s+/g, " ")
+    .toUpperCase()
+
+
+

+ 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. +

+
<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>
+
+
+
+ +
+

Validation

+
    +
  • bun test apps/web/app/terminal.test.ts passed.
  • +
  • bun --cwd=apps/web run build passed.
  • +
  • Regression coverage was added for normalization and token parsing of ticker input.
  • +
+
+ +
+

Issues, Limitations, and Mitigations

+
    +
  • + Dolt sync limitation: bd dolt pull 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. +
  • +
  • + Scope control: this pass hardened the shell only. It did not audit every downstream pane, + drawer, or popover for similar edge cases. +
  • +
  • + Workflow status: this document records the implementation and validation, but the work was not + committed or pushed as part of this turn. +
  • +
+
+ +
+

Follow-up Work

+
    +
  • No follow-up issue was created from this hardening pass beyond the completed beads item islandflow-6ri.
  • +
  • If terminal adaptation work continues, the next pass should examine small-screen drawer behavior and popover placement under dense live states.
  • +
+

+ Document created to satisfy the required turn-documentation step for implementation changes. +

+
+
+ +