Extends scripts/audit-css.mjs to also report hardcoded hex colors
outside CSS custom-property defs (`--name: #xxx;`) and outside
`var(--token, #fallback)` token-default fallbacks (which are explicitly
allowed per repo design-system rules).
Initial run reports 4 violations — all intentional pre-cascade body
defaults in :root and html.dark that must be literal hex (no var()) so
the page renders correct colors before the cascade resolves the custom
props. Annotated each with an explanatory comment; values intentionally
mirror --foreground / --background. Future drift surfaces immediately.
UI audit doc §5 #10 updated to reflect the sweep is done and the 4 known
violations are documented.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Now that @bytelyst/ui@0.1.6 ships <CardButton>, replace the local
`.card-button` className shim (added to layout-fixes.css §25 in the
prior commit) with the real primitive.
Conversions:
- components/StrategyWizard.tsx — risk style (3 cards) + hours pickers
- views/SimpleView.tsx — new-buy + manage-holding plan cards
- tabs/MyStrategiesTab.tsx — diagnostic accordion toggle
Removes layout-fixes.css §25 (.card-button) — no longer needed.
Updates docs/ui/UI_AUDIT.md Pattern A to reflect that the design-system
fix has shipped.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds web/scripts/audit-css.mjs — surfaces classes that appear in many
rules (likely targets of style drift) and per-class !important counts
(specificity-fight indicators). Implements the script suggested by
docs/ui/UI_AUDIT.md §5 #7 / Pattern G.
Run: pnpm --filter @bytelyst/trading-web run audit:css
(or: cd web && npm run audit:css)
Initial run on src/index.css + src/App.css + src/layout-fixes.css
exposes the top hotspots:
- .positions-tab 22 rules, 26 !important
- .trade-plans-page 20 rules, 25 !important
- .history-tab 17 rules, 22 !important
- .trading-sidebar-logo 9 rules, 20 !important
These are the targets for the future Pattern G consolidation pass.
The script supports --threshold N and --json for tooling.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds a local ESLint plugin (web/eslint-local/) with three custom rules
implementing the preventive guardrails from docs/ui/UI_AUDIT.md §5:
- bytelyst-trading/truncate-needs-title
flags JSX elements using Tailwind 'truncate' / 'line-clamp-*' /
'text-ellipsis' without a paired title= or aria-label= (Pattern E)
- bytelyst-trading/grid-needs-minmax
flags gridTemplateColumns string values with bare Nfr tracks not
wrapped in minmax(0, ...). Catches both literal and template-string
forms; verifies *every* fr is wrapped, not just one (Pattern F)
- bytelyst-trading/no-button-with-stacked-children
flags <Button> from @bytelyst/ui wrapping 2+ block children. The
Button primitive applies whitespace-nowrap + fixed h-{size} which
collapses stacked content; recommends native <button class="card-button">
(Pattern A)
All wired into eslint.config.js as 'warn' (not error) so existing code
isn't broken; new violations show up immediately.
Also fixes the two bare-Nfr grids the new rule caught:
- components/strategy/CodeStrategyEditor.tsx :270 — repeat(5, 1fr)
- views/ScreenerView.tsx :142 — '100px 1fr 90px ...'
eslint src/ now reports zero bytelyst-trading/* warnings.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The Quick Actions in the AI Trading Copilot floating modal were broken:
- Always 2 columns regardless of viewport (cards overflowed at narrow widths).
- Used <Button> from primitives which is inline-flex internally; the two
inline-block <span> children for title and prompt overlapped.
- Prompt was truncated client-side with .slice(0, 55)+'...' instead of
proper CSS line-clamp, causing inconsistent display.
- No min-width: 0 on grid/card/text so content could push the card past
the modal width (460px).
Refactored:
- Replaced <Button> with native <button> (cleaner default styling base).
- New CSS classes copilot-quick-action-card, -title, -prompt with explicit
flex-direction: column so title and description stack reliably.
- Responsive grid: single column below 480px, 2 columns above. The modal
is 460px wide on desktop and shrinks to viewport-32 on smaller screens,
so this prevents card overflow on narrow phones.
- Title: single-line truncate with ellipsis, full text exposed via title
attribute on the button (tooltip + a11y).
- Prompt: -webkit-line-clamp: 2 (no JS slice). Wraps cleanly on long
prompts. word-break + overflow-wrap as guards.
- Card: min-height 64px so they align in a row without text-length
jumping. Hover, active, and focus-visible states for keyboard a11y.
- Body container gets bottom padding so the last card is reachable above
the composer input.
Container changes:
- .copilot-body class added to messages scroll container; bottom padding
ensures composer doesn't crowd content.
- The modal shell (existing) already uses display:flex; flex-direction:
column; overflow:hidden so header stays top, body scrolls, composer
stays bottom — preserved unchanged.
No breaking changes to chat behavior, sendMessage flow, or quick action
prompts.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Alert banner (top of every page when critical events exist):
- Refactored from a single anchor with inline spans into a structured
wrapper + inner row with a real CTA link.
- Inner row uses flex-wrap with max-width: 1280px container so the text
never overflows the viewport.
- Text takes flex: 1 1 auto with min-width: 0 + overflow-wrap: anywhere
so long messages wrap cleanly. CTA stays shrink-0 so "Go to Admin Panel"
is always visible.
- On viewports ≤560px the text wraps to its own row above the CTA.
- CTA links directly to /settings?section=Admin%20Panel (the URL the
Settings router actually consumes), not just /settings.
- Singular/plural ("issue" vs "issues") fixed for n=1.
- Softer styling: card+destructive color-mix instead of bright red,
no shadow, normal weight, no uppercase.
Sidebar logo / brand block (was clipped):
- Two competing CSS rules (one made it a 44×44 icon-only grid, the
other a 56px-tall flex row). The grid one was clipping the brand text.
Now an explicit flex row with min-height: 60px, gap: 12px.
- Icon: shrink-0 22×22.
- Brand wrapper: min-width: 0, two stacked lines with line-height: 1.2/1.3.
- "ByteLyst" and "Trading OS" never clip vertically; they ellipsis-truncate
if the sidebar is somehow constrained horizontally.
- Below 1279px (tablet sidebar): logo block recenters at 52px tall to fit
the icon-only narrow sidebar.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The Plans page (/plans) had multiple known overflow / clipping bugs:
1. .saved-setup-card { overflow: hidden } clipped the right-side action
buttons (Edit, Delete) on cards with long content.
2. .saved-setup-meta-grid chips used white-space: nowrap + overflow: hidden
+ text-overflow: ellipsis, which truncated each chip awkwardly instead
of wrapping. Long Trade ID and Order ID badges blew out the card.
3. .saved-setup-topline used a CSS Grid with auto column for actions, so
actions could push the title block off-screen at narrow widths.
4. .saved-setup-timeline used 5 fixed-min-width columns that caused
horizontal overflow on cards <440px wide.
5. The outer two-column grid forced minmax(380px, ...) on the saved-setups
column which squeezed the form on 1280–1440px screens.
6. The critical alert banner used a loud high-contrast red background and
uppercase letterspaced text — visually inconsistent with the rest of
the app.
Fixes applied in web/src/layout-fixes.css (extends existing layout fixes):
- saved-setup-card → overflow visible, never clip children.
- saved-setup-topline → flexbox with flex-wrap, title flexes to 320px
before actions wrap below to a new row.
- saved-setup-actions → flex-wrap with right-justified items.
- saved-setup-meta-grid chips → real wrapping (white-space: normal,
overflow visible, max-width 100%, line-height tightened, padding
expanded slightly for readability).
- Trade-id badges → break-all so long IDs wrap inside their pill.
- saved-setup-timeline → overflow-x: auto so progression scrolls
inside the card on narrow widths instead of pushing the layout.
- Outer grid → single column below 1440px, two-column 1.15fr/0.85fr
above. Form gets adequate width on common laptop sizes.
- Alert banner → soft tinted background using card+destructive mix,
normal weight, no uppercase, integrated with the app shell aesthetic.
- saved-setups-header, asset-row, form sections → flex-wrap + min-width: 0
so nothing forces horizontal scroll.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds web/src/layout-fixes.css imported once from main.tsx — surgical
overrides applied with !important so they win against the existing
3000+ line index.css without rewriting it.
Issues fixed:
1. Modals/popovers/dropdowns clipped by .dashboard-main { overflow: hidden }
→ now overflow: visible. Stacking context guard for [role=dialog].
2. Right panel (308px fixed) covers main content on laptop/tablet
→ 260px below 1280px, hidden below 1024px.
3. Tables extending off-screen
→ .dashboard-content table wrapped with display:block + overflow-x:auto
so they scroll inside their column. Also exposes .scroll-x utility.
4. Header search/indices push each other off-screen
→ flex-wrap on .trading-header, search shrinks to 240–360px range,
indices wrap with smaller column gap on narrow.
5. Long unbreakable strings (commit SHAs, URLs) escaping containers
→ overflow-wrap: anywhere + word-break: break-word + pre-wrap on <pre>.
6. Sidebar 76px doesn't collapse on mobile
→ 56px below 768px with reduced content padding.
Bumps @bytelyst/devops to ^0.1.3 (responsive panel) in backend + web.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The DevOps panel lives inside Settings, but typing /devops directly hit
the catch-all NotFoundView. Add a Navigate redirect so the shorthand URL
works.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Backend:
- /api/devops/info now requires admin role (was: any authenticated user).
Exposes env keys, dep checks, and socket counts — admin-only by design.
- New /api/devops/version (public, no auth) returns build SHA/branch/image
for ops/CI rollback verification.
- Dep checks: live ping for Cosmos (trading_users) and platform-service.
- Service version read dynamically via readServiceVersion(import.meta.url)
— no more hardcoded '0.1.0'.
- extra: socketIoConnections + tradingApiUrl for runtime debugging.
- saveCurrentUserProfile no longer accepts client-supplied role —
prevents drift with platform JWT (which is authoritative).
Web:
- DevOps tab is now admin-only (gated behind isAdmin like Bot Config and
Admin Panel). Both the section list and content render are guarded.
- Service version baked into bundle via Vite `define` (__WEB_SERVICE_VERSION__)
read from web/package.json — no more hardcoded VERSION constant.
- Bumps @bytelyst/devops dep to ^0.1.2.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Backend:
- Fix role drift: /api/me/profile now returns JWT role authoritatively (was reading
drifting role from trading_users). PATCH strips client-supplied role.
- Add /api/devops/info endpoint backed by @bytelyst/devops/server.
- Dockerfile: bake BYTELYST_COMMIT_SHA / BYTELYST_BUILT_AT / etc. as build args.
Web:
- Migrate from vendor/ + .pnpmfile.cjs to Gitea npm registry (consistency with backend).
- Replace file: refs in web/package.json with semver ranges resolved from Gitea.
- Drop vendor/bytelyst/* tree and .pnpmfile.cjs.
- Add DevOpsTab in Settings using @bytelyst/devops/ui (tabbed: Build/Runtime/Config/Deps/Raw).
- Vite alias: restrict @bytelyst/* catch-all to single-segment names so subpath
imports (@bytelyst/devops/ui) resolve via package exports map.
- Bake BYTELYST_* metadata into the bundle as VITE_BYTELYST_* env.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The base image approach was reverted for notes and clock due to workspace
complexity. The trading repo was still using the base image approach
which is causing build failures. Reverting to the vendor approach
which works reliably for the monorepo structure.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Update Dockerfiles to use bytelyst-common-base-backend and bytelyst-common-base-web
images instead of installing @bytelyst/* packages via vendor directory.
Benefits:
- Smaller final images (~50MB vs ~250MB)
- Faster builds (base image cached)
- Consistent package versions across products
- No need for vendor/ directory maintenance
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Fixed run-e2e.sh to use numeric 0 instead of 'unknown' for test counts
- This resolves JSON parsing error when tests don't run
- Disabled webServer in playwright.config.ts due to Vite -p flag issue
- Tests now require manually starting dev server: cd web && pnpm dev -- --port 3050
- Updated comments in config with instructions for running tests
- Created e2e/alert-positioning.spec.ts for critical alerts positioning tests
- Created e2e/assistant-positioning.spec.ts for assistant widget positioning tests
- Created e2e/destructive-actions.spec.ts for destructive actions confirmation tests
- Created e2e/feedback.spec.ts for save/delete/update feedback tests
- Created e2e/page-states.spec.ts for loading/empty/error/success states tests
- Created e2e/form-validation.spec.ts for form validation tests
- Created e2e/keyboard-navigation.spec.ts for keyboard navigation tests
- Created scripts/tests/run-e2e.sh test runner script with health check
- Updated LAUNCH_READY_UI_UX_ROADMAP.md checklist - all items complete
- All testing infrastructure complete (CI integration replaced with local test runner)
- Installed Playwright and Storybook packages
- Created playwright.config.ts with viewport matrix and browser configurations
- Installed Playwright chromium browser
- Created e2e/viewport-matrix.spec.ts for viewport matrix testing
- Created e2e/horizontal-overflow.spec.ts for horizontal overflow testing
- Added test scripts to package.json (test:e2e, test:e2e:ui, test:e2e:viewport, test:e2e:overflow)
- Updated LAUNCH_READY_UI_UX_ROADMAP.md checklist with testing infrastructure status
- Replace .stat-chip with Badge in PresetMarketplace, BacktestTab, MembershipTab
- Replace .saved-setup-id-chip with Badge in SimpleView
- Replace .screener-sector-chip with Badge in ScreenerView
- Replace .health-pill with Badge in TradeProfileManager
- Remove CSS definitions for one-off classes from index.css
- All components now use shared Badge from product adapter
- Verify: audit:ui (0 findings), audit:ui:strict (0 findings), typecheck, build