After sending on mobile the iOS Safari focus retention shifts
visualViewport.offsetTop, leaving the composer near the top of the
screen. Translate the chat thread by that offset so it stays pinned
above the keyboard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Inline grouped tool-call rendering in chat surfaces and update summary logic for clearer aggregation
- Refine chat, quick FAB, settings, and agents UI styles/behavior with matching test coverage updates
- Extend dashboard TUI state/controller flow with status and shortcut improvements
- Update engine and dashboard route handling related to agent runs, settings memory sync, and remote access adapters
Fusion-Task-Id: FN-2926
- Add attachment-first chat compose flow with paperclip picker, drag-and-drop, and paste support in ChatView input
- Render pending attachment preview chips with per-file remove actions and drag-over visual affordance
- Display sent message attachments with image/file presentation and accessible focus/interaction styling
- Update ChatView tests and icon mocks to cover attachment-only sends, file selection, and preview behavior
Fusion-Task-Id: FN-2911
- Remove btn-icon styling from ChatView render toggle controls and apply dedicated borderless toggle styling
- Update ChatView and QuickChatFAB behavior so quick chat streaming state is preserved when resuming sessions
- Add regression tests for QuickChatFAB and useQuickChat to cover resume and streaming-state handling
Fusion-Task-Id: FN-2906
- Add a shared useMobileKeyboard hook that tracks visualViewport overlap on mobile browsers
- Apply keyboard overlap CSS variables to QuickChatFAB so fullscreen mobile panels resize above the virtual keyboard
- Apply the same keyboard-aware sizing to ChatView mobile thread layout and auto-scroll messages when keyboard opens
- Expand ChatView, QuickChatFAB, and hook test coverage for mobile keyboard detection and viewport-resize behavior
- Track plain-text render mode per assistant message (including streaming output) in ChatView state
- Add inline mobile-only Eye/EyeOff toggle buttons on assistant bubbles to switch markdown vs plain text per message
- Hide the header markdown/plain toggle on mobile and polish inline toggle sizing, focus, and hover styles in ChatView.css
- Extend ChatView tests with mobile viewport coverage for toggle rendering, per-message isolation, round-trip toggling, and mobile CSS contract checks
- ChatView mobile: hide the top sidebar header's "New Chat" button on
<=768px and show the existing footer button as a pinned full-width
action with safe-area padding. Both buttons existed in JSX; only
CSS was needed.
- styles.css: comments compose/edit textareas use var(--surface) so
they remain visible against the task detail modal's --card panel
(in light theme --bg and --card both resolve to #ffffff).
- Includes resolved-conflict changes from fusion/fn-2469 in
ModelOnboardingModal and SettingsModal (and the related test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ChatView mobile: add @media (max-width: 768px) so the session-list
sidebar goes full-width and the thread is hidden when the sidebar
is visible (and vice-versa). The component already had the state,
back-button (ChevronLeft), and visibility toggling — only the CSS
was missing.
- TaskComments compose/edit textareas now use var(--surface) for
background. The base .spec-editor-feedback uses var(--bg), which
in light theme resolves to #ffffff — the same as the task detail
modal's --card panel — making the comment box invisible.
- ChatView CSS is now imported eagerly from App.tsx so it bundles
into the main CSS file. Previously the lazy ChatView JS chunk
loaded its CSS via an async <link> tag, producing a brief flash
of unstyled chat UI on first render.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split app/styles.css from ~40k lines down to ~4.5k. Created 56 co-located
component CSS files in app/components/, each imported by its owning .tsx.
The remainder of styles.css holds genuinely global rules (design tokens,
.btn/.card/.modal/.form-input primitives, cross-component @media overrides).
- Lazy-load 13 heavy views (AgentsView, RoadmapsView, NodesView, etc.) via
React.lazy + Suspense; prefetch all chunks on idle so first navigation is
instant. Initial JS bundle: 1.58 MB → 1.16 MB (-26%). Initial CSS bundle:
635 kB → 471 kB (-26%); the rest splits into 13 per-view chunks.
- Add app/test/cssFixture.ts exposing loadAllAppCss() + loadAllAppCssBaseOnly()
so CSS regression tests load the full per-component bundle (mirroring Vite
source order). Migrate 30+ tests off direct readFileSync('../styles.css').
- Enable test.css: { include: [/.+/] } in vitest.config.ts so component CSS
imports actually inject styles in jsdom (fixes getComputedStyle assertions).
- Add ESLint rule (no-restricted-syntax) banning direct styles.css reads in
dashboard test files; points at loadAllAppCss() instead.
- Restore lost utility classes (.text-muted, .text-secondary, .text-dim,
.form-input) and rescue dropped chat tool-call rules into QuickChatFAB.css.
- Mobile fixes along the way: scroll containment for view containers
(min-height:0 + -webkit-overflow-scrolling), QuickChatFAB full-screen on
mobile (with safe-area-inset for iOS home bar), AgentsView single-row
header layout, ActivityLogModal close button on right, model-combobox
z-index above the mobile quick-chat panel.
- Bug fix: SkillsView toggle was display:none which hid the input from the
accessibility tree; replaced with the visually-hidden pattern so screen
readers + getByRole still find the checkbox.
- Bug fix: standalone Delete button in TaskDetailModal for triage-column
tasks (Actions dropdown is hidden in triage state, so previously no way
to delete a freshly-created task without status change first).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>