Commit Graph

1568 Commits

Author SHA1 Message Date
Fusion
701feb8ea5 feat(FN-2845): streamline node routing configuration in dashboard
- Add a reusable ProjectNodeSelector component and hook RoutingTab to it for per-task node overrides
- Move default node and unavailable-node policy controls into the dedicated Node Routing settings section
- Add descriptive routing guidance text and project-level note in SettingsModal
- Expand dashboard tests for routing tab behavior and node routing settings UX
2026-04-28 14:25:56 -07:00
Fusion
8df19490f0 fix(FN-2867): simplify remote access settings activation flow
- Remove the separate "Activate Provider" action from SettingsModal and rely on save-only provider configuration
- Ensure saving remote settings enables the selected active provider and keeps provider flags consistent
- Seed remoteAccess defaults in remote settings routes when project settings are missing instead of returning conflicts
- Expand dashboard tests to cover first-use/default remote settings behavior and updated provider lifecycle expectations
2026-04-28 14:25:56 -07:00
Fusion
35e4f53524 feat(FN-2873): align settings header refresh action
- Move the check-for-updates refresh button into the settings header actions group for consistent placement
- Keep update check behavior unchanged, including loading state, disabled state, and aria labeling
- Set the refresh icon size explicitly to match surrounding header controls
- Simplify mobile CSS by removing obsolete settings-update-check wrap overrides
2026-04-28 14:25:56 -07:00
Fusion
4f6763bc2a feat(FN-2875): add done-task elapsed time ceiling rounding
- Add formatElapsedDurationDone to render done task elapsed time with ceiling-based minute/hour/day rounding
- Keep in-progress time indicator logic on existing floor-based formatElapsedDuration behavior
- Route done-card time indicator rendering through the new done-specific formatter
- Expand TaskCard tests to cover done rounding semantics and preserve in-progress rounding expectations
2026-04-28 14:25:56 -07:00
Fusion
78836b1969 fix(FN-2883): reset merge state when re-entering in-progress
- Reset merge metadata, verification counters, and workflow results when tasks move from in-review/done back to in-progress
- Reopen verification-related steps (or the last step fallback) so re-verification runs from a pending state
- Add execute-time guard to clear stale mergeDetails on in-progress tasks before continuing
- Prevent resumeOrphaned fast-path recovery when completed in-progress tasks still carry merge metadata
- Add targeted executor and TaskForm tests covering FN-2883 regression paths
2026-04-28 14:25:56 -07:00
Fusion
2a26d61b2b feat(FN-2724): add interactive task routing controls
- Replace the static routing panel with a dedicated Routing tab that shows effective node, source, and policy details
- Add per-task node override selection with optimistic save/rollback, clear override action, and in-progress lock messaging
- Tighten PATCH /tasks/:id nodeId validation (empty/non-string rejection) while preserving node override conflict enforcement
- Update dashboard route and modal tests, including new RoutingTab coverage and tab-order assertions
2026-04-28 14:25:56 -07:00
Fusion
9d2a86c2c0 feat(FN-2874): add failed-task retry action to dashboard cards
- Wire onRetryTask from App through Board/Column/WorktreeGroup/ListView into TaskCard
- Add a Retry button with loading/disabled state to failed task error boxes in TaskCard
- Update failed-card styles to use design tokens and add retry button visual states
- Add TaskCard tests covering retry visibility, callback invocation, loading, and error toast behavior
- Add a patch changeset for @runfusion/fusion documenting the dashboard retry shortcut
2026-04-28 14:25:56 -07:00
Fusion
5ab5e5467e fix(FN-2872): reduce blocked-task log noise and clarify routing details
- Add a Node Routing section in TaskDetailModal showing override, effective node/source, unavailable-node policy, and blocking reason
- Remove duplicate blocked-state heartbeat log output when the blocked reason has not changed
- Silence runtime onBlocked logging to avoid repeated blocked-task log spam
2026-04-28 14:25:56 -07:00
gsxdsm
8e33f3039d feat(FN-2864): merge fusion/fn-2864 2026-04-28 14:25:56 -07:00
Fusion
07bed274b5 test(FN-2863): expand quick chat default-model recovery coverage
- Assert default-model auto-selection initializes a model session with project-scoped context
- Verify quick chat input stays disabled until default-model session bootstrap completes
- Add parity coverage for default-model initialization when agents are present
2026-04-28 14:25:56 -07:00
Fusion
6f06214ce6 fix(FN-2863): retry quick chat session init for default model
- Allow session initialization to retry when the same target key is selected but no active session exists
- Gate duplicate-init short-circuit behind activeSession/sessionsLoading so model-mode startup can recover
- Expand the effect dependency list to track session readiness inputs used by the retry guard
2026-04-28 14:25:56 -07:00
Fusion
279d584072 feat(FN-2723): show node health in dashboard routing selectors
- Add node status indicators and labels to task creation, quick entry, task form, and settings node selectors
- Surface selected node health in list bulk-edit controls with status dot styling for online/connecting/offline/error states
- Update dashboard component CSS and shared styles for consistent node status presentation
- Include changeset documenting unavailable-node routing policy and default node behavior
2026-04-28 14:25:56 -07:00
Fusion
25ffd1ae84 fix(FN-2861): harden planning session recovery and retry UX
- Add planning and subtask retry routes with session-lock checks and proper API error mapping
- Improve planning session execution with timeout/abort handling, stop-generation support, and resilient stream catch-up behavior
- Update Planning Mode modal to handle reconnects, retry-from-error flow, stop action, and cross-tab session state synchronization
- Expand dashboard tests for planning routes, planning session behavior, and PlanningModeModal retry/error coverage
2026-04-28 14:25:55 -07:00
gsxdsm
54f4fd540e feat(FN-2862): merge fusion/fn-2862 2026-04-28 14:25:55 -07:00
Fusion
bf9273abae fix(FN-2859): reduce background AI pill height in executor footer
- Tighten background task indicator pill padding to reduce vertical footprint
- Lower line-height to keep icon/text alignment after the size reduction
- Reduce mobile min-height so the footer pill no longer appears oversized
2026-04-28 14:25:55 -07:00
Fusion
aed253e380 test(FN-2733): expand memory dreams regression coverage
- Add ProjectEngine memory dreams wiring tests for startup ordering, settings-change resync, and unrelated-setting no-op behavior
- Cover degraded-mode startup and settings-update failure paths to ensure sync errors are logged without stopping the engine
- Extend MemoryView Dream Now tests for success, failure toast handling, and disabled loading-state behavior
2026-04-28 14:25:55 -07:00
Fusion
71ad9a8232 feat(FN-2848): add manual memory dream API trigger
- Add POST /api/memory/dream route that runs project and agent dream processing with AI prompt execution
- Expose triggerMemoryDreams() in dashboard legacy API client for direct dream invocation
- Switch SettingsModal and useMemoryData Dream Now actions from automation lookup to the new endpoint
- Update dashboard route, hook, and settings modal tests to cover success and error handling
- Add a patch changeset for @runfusion/fusion documenting the new endpoint and client helper
2026-04-28 14:25:55 -07:00
Fusion
f503ba1b27 feat(FN-2843): auto-select quick chat default model
- Extend /models API responses to include optional defaultProvider/defaultModelId from global settings, including empty and error paths
- Update QuickChatFAB to prefer the configured default model on load and fall back to first-model selection only when no agents exist
- Move quick chat model tag/title-wrap inline styles into QuickChatFAB.css using dashboard design tokens
- Add QuickChatFAB tests covering default-model auto-selection with and without agents and legacy behavior when no default is configured
2026-04-28 14:25:55 -07:00
Fusion
234a819338 feat(FN-2837): add infusion.ai links to update notices
- Add a Learn more link to the update-available banner alongside release notes
- Refactor SettingsModal update-result rendering into a shared helper and include the infusion.ai link when updates exist
- Add dedicated SettingsModal link styling with hover and focus-visible states using dashboard tokens
- Update banner and settings tests to assert the new Learn more link behavior
- Add a patch changeset for @runfusion/fusion documenting the update notice link addition
2026-04-28 14:25:55 -07:00
Fusion
73bd1de238 feat(FN-2842): polish workflow result expand toggle styling
- Add dedicated styles for the workflow result expand icon button with token-based sizing and alignment
- Add hover and focus-visible states for clearer interaction feedback and keyboard accessibility
- Increase mobile touch target sizing for both workflow edit and expand toggles using design tokens
2026-04-28 14:25:55 -07:00
Fusion
95f582f8ae fix(FN-2838): prevent mobile zoom on dashboard form inputs
- Add global `touch-action: manipulation` on html/body to reduce gesture-triggered zoom
- Set mobile input font-size to 16px for terminal, nodes, and scripts modal form fields
- Extend mobile CSS regression tests to assert the global touch-action rule
2026-04-28 14:25:55 -07:00
Fusion
1c96fee50f feat(FN-2732): add manual dream processing actions in dashboard
- Extend useMemoryData with triggerDreamNow and dreamRunning state by locating and invoking the Memory Dreams automation
- Add Dream Now actions to MemoryView and SettingsModal memory settings with loading UI, success/error toasts, and file refresh in MemoryView
- Expand hook and component tests to cover dream-trigger visibility and execution flows in both views
- Update terminal mobile keyboard layout CSS contract expectations for the current mobile modal width and min-height rules
2026-04-28 14:25:55 -07:00
Fusion
02ae48b0d6 feat(FN-2721): enforce node override guard across task workflows
- Add a core node-override-guard module, export it from @fusion/core, and enforce conflicts in store updates
- Add API route and CLI extension safeguards so nodeId override updates are blocked when ownership would conflict
- Wire node override routing and validation through dashboard quick-create, list, modal, settings, and task form/detail surfaces
- Add focused unit and integration tests for core guard logic, workflow routes, and dashboard node override UX
2026-04-28 14:25:55 -07:00
Fusion
4ee7f6b2b8 chore(release): v0.7.1
Version bump via changesets.
2026-04-28 14:25:55 -07:00
gsxdsm
187fc798c0 fix(dashboard): mobile polish, modal layout fixes, keyboard-aware terminal + add 0.7.1 changeset
- WorkflowStepManager: fix React error #310 ("Rendered more hooks than during
  the previous render") that broke the workflow steps panel from loading.
  `useOverlayDismiss` was being called after `if (!isOpen) return null`, so
  the hook count differed between open/closed renders. Move the hook above
  the early return.
- ModelOnboardingModal: API-key input + Save button now span the full card
  width on mobile via negative inline margins that bleed past the card's
  horizontal padding, so the form sits flush to the card edges instead of
  picking up a chunky left indent from the icon-row's flex start position.
- TerminalModal: same desktop-min-width-pinning bug as the onboarding modal.
  Reset min-width/min-height to 0 on mobile with `!important` so persisted
  desktop sizes from useModalResizePersist cannot re-pin the modal at 480×320
  on smaller phones. Also add `!important` to the keyboard-overlap height/
  max-height so the visual-viewport adjustment wins against the new
  fullscreen rule — without this the terminal stayed at 100dvh while the
  mobile keyboard covered the bottom of the screen.
- Add 0.7.1 changeset covering the full mobile polish + paperclip CLI parity
  + plugin runtime registry fallback + SCHEMA_VERSION bump series.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:35:48 -07:00
gsxdsm
4cb6ac4ef8 fix(dashboard): portal NewAgentDialog so it escapes the AgentsView stacking context
On mobile the dialog was rendering with its top hidden behind the in-page
\"Agents\" header. The dialog wasn't actually positioned wrong — its overlay
is `position: fixed; inset: 0`. The problem was that NewAgentDialog
rendered as a child of `.agents-view` (which has `overflow: hidden` and
`position: relative`), and an ancestor stacking context combined with the
relative+overflow parent prevented the fixed overlay from escaping above
the page header in the painter's order.

Render the dialog through `createPortal(..., document.body)` so the
overlay attaches at the document root, escaping every parent stacking
context. This is the standard React modal pattern.

All 9639 dashboard tests still pass — React Testing Library queries
traverse portals transparently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:23:45 -07:00
gsxdsm
0719fc0d1c fix(dashboard): mobile polish — single-row api key input, NewAgentDialog safe-area + scroll
ModelOnboardingModal:
- API-key row was forced to flex-direction: column on mobile, stacking the
  input above the Save button and doubling the vertical space inside the
  already-cramped provider card. Switch to flex-direction: row with the
  input growing (flex: 1, left-aligned) and Save shrunk to its label
  (right-aligned). Tighten input/button min-height to 32px and shrink the
  button's inline padding.
- Tighten surrounding form: gap → --space-xs, label font-size → 11px with
  no margin-bottom, helper paragraphs → 11px / line-height 1.35.

NewAgentDialog:
- Top of the dialog was cut off under the iOS notch / status bar. Added
  env(safe-area-inset-top) to the mobile header padding-top, and
  env(safe-area-inset-bottom) to the footer padding-bottom so the home
  indicator doesn't cover the action buttons.
- Body wasn't scrolling because the flex child kept its default
  min-height: auto, making the dialog grow past the viewport and never
  trigger overflow-y: auto. Added min-height: 0 + flex: 1 1 auto on the
  body, and flex-shrink: 0 on the header/footer/steps so only the body
  gives up height.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:18:31 -07:00
gsxdsm
09434aa409 fix(dashboard): compact onboarding provider cards on mobile
Previous mobile rule forced .onboarding-provider-card to flex-direction:
column with --space-lg padding, so the icon, body, and actions each took
their own full-width row and every card rendered ~140px tall on a phone.

Keep the icon inline beside the name + description (the row layout the
desktop already uses, just tighter), shrink the icon container to
list-item-bullet size, drop name/description font sizes a notch, and rely
on the existing flex-wrap so the API-key form / action buttons still drop
to their own row underneath. Also evenly split simple action rows (Connect
/ Skip) across the full width for easier tap targets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:15:09 -07:00
gsxdsm
a97dfdd213 fix(dashboard): mobile layout polish for onboarding/new-agent modals + settings auth gutter
- ModelOnboardingModal: the desktop base set min-width: 640px / min-height:
  480px and the mobile media query only reset max-width — but min-width
  always wins over max-width in size resolution, so the modal stayed pinned
  at 640px and overflowed any phone narrower than that. Compounding it,
  useModalResizePersist writes inline width/height from saved desktop
  sessions (e.g. 1100px), and inline styles beat the media query. Reset
  min-width/min-height to 0 in the mobile rule and force fullscreen
  (100vw/100dvh) with !important so the persisted desktop size cannot
  re-pin the modal off-screen. Also disable resize on mobile.

- NewAgentDialog: had no mobile media query at all. Added a max-width: 768px
  block that drops the overlay padding, fills 100vw/100dvh, removes the
  border-radius, tightens header/footer/steps padding, locks body scroll
  with overscroll-behavior: contain + -webkit-overflow-scrolling: touch
  (so iOS doesn't bubble the scroll up and trap users above the footer),
  and stacks the footer buttons full-width. Also switched the desktop
  max-height from 100vh to 100dvh so iOS Safari's collapsing URL bar
  doesn't push the footer under the address bar.

- SettingsModal auth panel: reduced .auth-panel-body padding-inline from
  --space-xl (24px) to --space-md (12px) so each provider card gets more
  horizontal room — the cards are dense enough that the wider gutter
  wasted visible width without any visual benefit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:14:21 -07:00
gsxdsm
505e8733e7 chore(dashboard): UI polish — Hermes logo, settings header heights, simplify agent card actions
- Use bundled Nous Research SVG (inverted) on the Hermes runtime card
  instead of the generic ProviderIcon.
- Lock GitHub-star and Help buttons in the settings header to a uniform
  26px height so the differing icon sizes don't misalign them.
- Drop the inline Delete button from the idle/terminated agent-card
  states — deletion stays available from the agent detail view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:27:41 -07:00
gsxdsm
a3102080c4 feat(paperclip-runtime): route Paperclip calls through paperclipai CLI in Local CLI mode
The Paperclip runtime card's Local CLI tab previously only derived an apiUrl
from the local config and then made HTTP calls itself, so the Test action and
the company/agent pickers ignored the user's onboarded CLI auth context.

Add CLI-backed variants for every Paperclip call that has a `paperclipai`
counterpart, and route through them when transport=cli — both from the
settings card and from the runtime adapter's prompt path.

- New plugin functions spawning `paperclipai … --json`:
  * probePaperclipViaCli, listCompaniesViaCli, listCompanyAgentsViaCli
    (settings card test + pickers)
  * createIssueViaCli, getIssueViaCli, agentsMeViaCli (runtime hot path)
- New dashboard routes: /providers/paperclip/cli-status, /cli-companies,
  /cli-agents (read-only façades over the plugin's CLI helpers)
- PaperclipRuntimeCard: branches on transport=cli to use the cli-* fetchers
- PaperclipRuntimeAdapter: stores transport on the session and routes
  createIssue/getIssue + identity derivation through CLI variants in CLI mode;
  raises a clear error when agentId is unset in CLI mode (paperclipai has no
  /agents/me equivalent)
- getIssueComments / wakeAgent / getRunEvents stay on HTTP (no matching
  paperclipai subcommands) and continue to use the apiKey discovered from
  the local paperclipai config, so CLI mode still works end-to-end
- Tests: 9 new paperclip-client tests covering each CLI variant + 5 new
  adapter tests for the Local-CLI transport branch (64/64 plugin tests pass)
- Update routes.test for the existing BUNDLED_PLUGIN_RUNTIMES fallback so
  the bundled hermes/openclaw/paperclip entries are expected alongside
  installed plugins

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:06:20 -07:00
Fusion
35db935feb feat(FN-2718): add unavailable node policy setting and validation
- Define and export unavailable node policy types in core settings interfaces
- Add project-level unavailable node policy default and runtime validation helper
- Add core unit coverage for unavailable node policy parsing and acceptance cases
- Guard Paperclip mint requests to include companyId only when available for type-safe payloads
- Document unavailable node policy in the settings reference
2026-04-27 22:40:08 -07:00
Fusion
6e65786bc5 fix(plugins): re-export probe symbols + declare plugin deps in dashboard
- Hermes / OpenClaw plugin index.ts now re-export `probeHermesBinary` /
  `probeOpenClawBinary` and their status types so the dashboard's
  `runtime-provider-probes.ts` façade can import them via the public
  package entry instead of deep paths.
- Dashboard `package.json` adds `@fusion-plugin-examples/hermes-runtime`,
  `…/openclaw-runtime`, `…/paperclip-runtime` as workspace deps so
  pnpm symlinks them into `packages/dashboard/node_modules/`. Without
  these, the new probe imports failed with "Cannot find module" during
  `pnpm typecheck`.

This clears 6 of the 9 outstanding typecheck errors. The remaining 3 are
in the in-flight Hermes plugin rewrite (runtime-adapter still imports
from a deleted `./pi-module.js`; the new `index.ts` calls a factory
with the wrong arg type) and should be resolved by the same change set
that landed the rewrite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:17:54 -07:00
gsxdsm
5159cc2a68 test(dashboard): align CSS-contract + onboarding tests with current contracts
Two long-standing test failures, root-caused and fixed.

terminal-mobile-keyboard-layout.test.ts (2 tests)
  CSS structure changed: the desktop terminal modal moved its initial
  width/height into companion `:not([style*="width"])` /
  `:not([style*="height"])` rules so persisted resize values can win
  over the default. The tests still searched the base block. Updated
  the helpers to extract from each rule explicitly, asserting:
    width: min(1800px, …)            in the :not([style*="width"]) rule
    max-width: calc(100vw - …)       in the base rule
    85vh                             somewhere in the :not([style*="height"]) rule
    min-height + max-height: calc(100dvh - 40px)  in the base rule
  Same constraints, just reflecting the current selector layout.

ModelOnboardingModal.test.tsx (2 tests)
  Component refactor (commit c94e9be31) made the "what GitHub unlocks"
  feature list conditional on `!isGitHubReady`, so the test that mocks
  `authenticated: true` no longer sees "Import issues as tasks". And
  the gh-CLI optional explanation copy was rewritten to "OAuth from
  the dashboard is optional" instead of "OAuth integration in
  Settings → Authentication is optional". Updated:
    - "shows connected state": assert the new connected-state
      sentence ("GitHub is connected — issue imports …") instead of
      the now-hidden feature list line.
    - "explains gh CLI auth": use getAllByText for the two-place
      "GitHub CLI is already authenticated" copy and match the
      current "OAuth from the dashboard is optional" sentence.
  Also adds the missing `afterEach` import to silence its TS error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:16:45 -07:00
gsxdsm
93d2dfdebf fix(dashboard): scale stats values to fit metric box without overflow
Token counts on the task detail Stats panel could overflow the metric
box for large values (e.g. 10-digit comma-separated totals). Switched
each metric to a container-query inline-size context and gave the
value a fluid font-size of `clamp(13px, 13cqw, 28px)` so it grows to
fill the box up to a 28px cap and shrinks down to 13px on narrow grid
columns. Combined with `min-width: 0`, `overflow-wrap: anywhere`, and
the metric's `overflow: hidden`, long values now stay within the box
and short values render as large as they can without dominating.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:06:06 -07:00
gsxdsm
c94e9be31c fix(dashboard): wider/resizable onboarding modal + GitHub CLI-aware messaging
- ModelOnboardingModal: native CSS resize with persisted size via
  useModalResizePersist; default ~1100px wide; modal scrolls inside
  the content area instead of clipping.
- Provider cards flex-wrap so the API-key form drops to a full-width
  row instead of being squished into a fixed side column. Connected
  badge no longer stretches the full body width.
- Step changes now reset content scrollTop so each page lands at the
  top.
- GitHub step: prominent GitHub mark via ProviderIcon; when gh CLI is
  authenticated the intro reads as 'you are all set', primary CTA
  becomes 'Continue with gh CLI auth', and a secondary
  'Connect OAuth (optional)' button is offered.
- CustomModelDropdown: highlight init runs once per open session, and
  filter changes reset highlight to top + scrollTop=0, fixing the
  scroll-fight when filtering models.
- TUI dashboard: at >=150 cols, Stats panel sits to the left of Logs;
  bottom row drops to Utilities + Settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:05:45 -07:00
gsxdsm
3031d05a68 fix(dashboard): clear all 661 test-file type errors
Production typecheck (tsconfig.json + tsconfig.app.json) was already
clean, but a third config that includes test files surfaced 661 errors
across 60+ test files — accumulated drift between mock fixtures and
production types. Six parallel typescript-pro agents fixed every one
without touching production code.

Per-scope before/after (errors → 0):
  ChatView                                                     183
  Mailbox + Agent suite (5 files)                              156
  Task / Modal suite (6 files)                                 127
  App + small components (12 files)                             96
  Hooks + api/auth (8 files)                                    48
  Long tail (32 files)                                          51
  -----------------------------------------------------------------
  Total                                                        661

Major fix categories:
- Untyped state objects inferring `never[]` / `null` literals (root
  cause of ~120 errors in ChatView alone — added a single
  `UseChatReturn` annotation)
- Mock objects missing fields that became required: `WorkflowStep.mode`,
  `ChatMessage.thinkingOutput / metadata`, `ChatSession.projectId`,
  `Task.log`, `ProjectHealth` fields, `PtyTerminalSessionInfo.createdAt`,
  `Agent.metadata`, `InboxResponse.total`, etc.
- Mock objects with stale fields that no longer exist:
  `AgentBudgetStatus.budgetPeriod`, `truncated` on log responses,
  `OutboxResponse.unreadCount`, `MergeResult.source/target/details`
- Modal props that became required (e.g. `PlanningModeModal.onTasksCreated`)
- String literals not in narrowed unions (`Column`, `WorkflowStepPhase`,
  `InsightStatus`, `AgentLogType`, etc.)
- `querySelector` returning `Element` cast to `HTMLElement` for
  `@testing-library/react`'s `within()`
- Vitest mock typing: `.mock.calls` access needing `vi.mocked(...)`,
  zero-param tuple handling, generic `vi.fn(() => [])` inferring
  `never[]`

Helpers introduced in test files (no shared infra):
- `makeSettings(overrides)` in ModelSelectorTab.test.tsx
- `makePromptOverrides(overrides)` in AgentPromptsManager.test.tsx
- `FileBrowserTestOverrides` type alias in FileBrowser.test.tsx
- `makeInboxResponse / makeOutboxResponse` in MailboxView.test.tsx

Verification:
- tsc -p tsconfig.json:        exit 0
- tsc -p tsconfig.app.json:    exit 0
- tsc -p tsconfig.test-check.json (new — includes test files): exit 0
- vitest run:                  9639 / 9641 (2 pre-existing failures
                               in terminal-mobile-keyboard-layout.test.ts
                               unrelated to this work; verified via
                               `git stash` + run on clean HEAD)

Adds packages/dashboard/tsconfig.test-check.json to keep this regression
guard available locally — same as tsconfig.app.json minus the test
exclude.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:59:52 -07:00
gsxdsm
156caf8a3c fix(dashboard): card timer matches stats panel + resizable Changes diff modal
Two related dashboard fixes.

1. Card timer mismatch: the board card timer chip showed only workflow
   runtime (e.g. <1m on FN-2716) while the task detail Stats panel
   reported "Total execution time" of 7m+ for the same task. Cause —
   the slim board listing strips `task.log` to keep payloads small, so
   the card's client-side `[timing]` log scan returned 0. Now the slim
   path aggregates `[timing] … in <N>ms` durations server-side into a
   new `task.timedExecutionMs` field before stripping the log; the
   card prefers this aggregate, falling back to the client scan when
   the full log is loaded (TaskDetailModal). Wire payload stays slim.

2. View Changes diff modal: defaulted to `90vw × 80vh` and was not
   user-resizable. Switched to `min(95vw, 2200px) × min(90vh, ...)`
   default with `resize: both`, persisted via useModalResizePersist
   (`fusion:changes-diff-modal-size`). Mobile keeps fullscreen layout.
   Overlay dismiss switched to the shared `useOverlayDismiss` hook so
   resize-drags that release on the overlay don't close the modal.

Updated the diff modal's regression tests to match the new constraint
shape (still asserts max-height clamps to viewport via calc()).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:55:46 -07:00
gsxdsm
b196ab42b4 fix(dashboard): no resize-drag dismiss across all resizable modals + Claude CLI in Authenticated group
When a user drags the native CSS resize grip from inside a modal and
releases the mouse over the overlay, the synthesised click event
targets the common ancestor (the overlay) — fooling the existing
e.target === e.currentTarget dismiss check. Audited every modal with
`resize: both` and switched them to a shared mousedown→mouseup tracking
pattern (new useOverlayDismiss hook) so dismiss only fires when both
events land on the overlay.

Modals fixed: TaskDetail, Settings, FileBrowser, GitHubImport,
GitManager, ScheduledTasks, Scripts, WorkflowStepManager. (Terminal
and AgentDetail were already fixed in 95566795e; PlanningModeModal
already had the right pattern inline.)

Also: in Settings → Authentication, the "Anthropic via Claude CLI"
card now lives inside the Authenticated group when authenticated and
the Available group otherwise, instead of floating at the top.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:36:24 -07:00
gsxdsm
95566795ea fix(dashboard): resizable terminal + agent detail modals, no resize-drag dismiss
Terminal modal and agent detail view are now resizable via the native
CSS grip with sizes persisted per-modal in localStorage. Mobile keeps
fullscreen layout (resize disabled, !important overrides any persisted
desktop dimensions).

Both modals previously dismissed when a drag-resize started inside the
modal but released over the overlay — the synthesised click event
targets the common ancestor (overlay), tripping the e.target ===
e.currentTarget dismiss check. Switched to mousedown→mouseup tracking
so dismiss only fires when both events land on the overlay.

Terminal additionally observes its own pixel box via ResizeObserver
and refits xterm on every grip drag — `resize: both` doesn't emit
window resize.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:30:05 -07:00
gsxdsm
fc4c42c1ca feat(dashboard): add fireworksai and qwen-ai provider id aliases
Two more brand-id spellings observed in the wild that previously fell
through to the generic Cpu glyph:
  fireworksai → FireworksIcon (joins fireworks / fireworks-ai)
  qwen-ai     → QwenIcon       (joins qwen / qwen-coder / alibaba / tongyi)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:25:10 -07:00
gsxdsm
3f25a08538 feat(dashboard): add Cerebras / Groq / Vercel icons + cover all pi-ai catalog providers
Audited the pi-ai built-in model catalog provider IDs against the
ProviderIcon registry and filled every remaining gap.

New icons:
  --provider-cerebras    (#f1592a)  → CerebrasIcon (concentric arcs)
  --provider-groq        (#f55036)  → GroqIcon     (lightning bolt)
  --provider-vercel      (text)     → VercelIcon   (triangle wordmark)

New aliases routed to existing icons:
  minimax-cn               → MiniMaxIcon
  azure-openai-responses   → AzureIcon
  google-gemini-cli        → GeminiIcon
  google-generative-ai     → GeminiIcon
  opencode-go              → OpencodeIcon
  vercel-ai-gateway        → VercelIcon

After this commit every provider id emitted by `@mariozechner/pi-ai`'s
register-builtins (anthropic, openai, openai-codex, google,
google-vertex, google-gemini-cli, google-antigravity,
amazon-bedrock, mistral, openrouter, fireworks, cerebras, groq,
huggingface, kimi-coding, minimax, minimax-cn, zai, xai, opencode,
opencode-go, github-copilot, azure-openai-responses,
vercel-ai-gateway) plus the user-curated set (qwen, lm-studio,
hugging-face, alibaba/tongyi, hf, lmstudio…) renders a brand icon
instead of falling back to the generic Cpu glyph.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:22:42 -07:00
gsxdsm
1f5a57ad4e feat(dashboard): add Qwen, LM Studio, Hugging Face, Mistral, Azure, Fireworks logos + Gemini aliases
Adds six new provider icons rendered as inline SVG, each backed by a new
brand token in styles.css:
  --provider-qwen          (Qwen / Tongyi)
  --provider-lmstudio      (LM Studio)
  --provider-huggingface   (Hugging Face)
  --provider-mistral       (Mistral AI)
  --provider-azure         (Microsoft Azure / Azure OpenAI)
  --provider-fireworks     (Fireworks AI)

Aliases registered:
  qwen / qwen-coder / alibaba / tongyi
  lmstudio / lm-studio
  huggingface / hugging-face / hf
  mistral / mistral-ai
  azure / azure-openai
  fireworks / fireworks-ai

Also extends the Gemini icon to cover every Google-product alias the
catalog throws at it: google-vertex, vertex, google-cloud-code,
cloud-code, antigravity (in addition to the existing google,
gemini, google-antigravity).

Tests cover each new mapping (and confirm the Gemini alias group all
resolves to the gemini icon with --provider-gemini color).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:07:32 -07:00
gsxdsm
8f95e742e5 fix(dashboard): onboarding step buttons + tighter auth provider spacing
- ModelOnboardingModal: completed/skipped step indicators render as
  <button> so they're clickable for review. Without explicit reset,
  browsers gave them their default white-ish background. Added
  background: transparent + border/padding/font-family resets to
  .model-onboarding-step-indicator so it inherits cleanly from the
  surrounding card on every theme.
- SettingsModal authentication: tightened spacing around providers —
  group margin 16→10, group label margin 8→4, provider card margin
  8→4, header padding 12 16 → 8 12. Less wasted space between rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:59:37 -07:00
gsxdsm
1c7eaf3e85 fix(dashboard): mobile rendering for resizable modals
The Settings, Workflow Steps, Automations (and friends) modals all carried
desktop `min-width: 480-520px` constraints from the resize work. On phones
that pushed them off-screen, and the overlay's 10vh top padding plus a
fractional `height: 80vh` left awkward gaps top and bottom.

Apply the same `@media (max-width: 768px)` full-screen-sheet treatment
that File Browser already had to every resize-aware modal: drop the
overlay padding, set width/height to `100vw`/`100dvh`, zero out the
min-* / max-* constraints, and disable `resize` (touchscreens can't grab
the corner grip anyway).

Also tightened the existing File Browser and GitHub Import overrides to
match (`min-width: 0`, `min-height: 0`, `resize: none`).

For TaskDetailModal the mobile rules go inside the existing first
`@media (max-width: 768px)` block to keep the FN-1331 detail-body
padding regression test happy (it captures from the first 768px media
query greedily).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:10:24 -07:00
gsxdsm
61ea16e31f feat(dashboard,cli): update-check frequency, GitHub star button, more resizable modals
Update-check frequency (end-to-end)
- Add `updateCheckFrequency: "manual" | "on-startup" | "daily" | "weekly"`
  to GlobalSettings (default: "daily").
- Backend `performUpdateCheck` honors the frequency: TTL = day/week,
  `manual` returns cache or empty without hitting npm, `on-startup`
  refreshes once per process lifetime then serves cache. `/refresh`
  route forces network regardless.
- Settings → Updates surfaces a working `<select>` for the cadence,
  disabled when auto-checks are off entirely.
- Tests: 4 new cases covering ttlForFrequency mapping, weekly window,
  manual semantics, on-startup once-per-process behavior.

TUI update notice
- Read cached update result synchronously on TUI startup. When an
  update is available, render a yellow notice line on the splash and
  a colored ● next to the version in the status bar.

Settings modal header polish
- Add "Star on GitHub" pill (icon + Star + cached star count from the
  GitHub API, 1h localStorage TTL) and "Help" button (opens project
  Discussions). Both link to the Runfusion/Fusion repo.
- Star button auto-hides after the user clicks it (intent = star),
  tracked in localStorage `fusion:github-star-clicked`.
- Settings → General gets a "Show Star on GitHub button" checkbox so
  users can hide it preemptively. New global setting
  `showGitHubStarButton: boolean` (default true) gates rendering.

More resizable modals
- Task Detail modal: 85vh default, resize: both, persisted via
  useModalResizePersist (key `fusion:task-detail-modal-size`).
- Quick Chat FAB: full 8-direction resize (4 corners + 4 edges) via
  pointer-event handlers; persisted to
  `fusion:quick-chat-size-<projectId>`. Each handle has the right
  cursor + role="separator" for accessibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:55:15 -07:00
gsxdsm
8a88e23b9c feat(dashboard): UI polish pass — resizable modals, themed scrollbars, settings overhaul
Modal resize + size persistence
- Extract `useModalResizePersist` hook (ResizeObserver + localStorage),
  apply to Files, Git Manager, GitHub Import, Workflow Steps, Automations
  (ScheduledTasks), and Settings modals. Each gets `resize: both`, sane
  min/max constraints, and a unique storage key.
- Bump default heights so the modals feel less cramped (Git: 92vh,
  Workflow/Automation: 80vh, Settings: 80vh / 1100px).

Scrollbar theme
- Add a global `*::-webkit-scrollbar*` + `scrollbar-color` rule in
  styles.css so chat, document, system stats, file browser, usage
  indicator, etc. inherit the theme. Existing per-component overrides
  (.board, .column-body, .settings-sidebar, planning modal) still win.

Document view
- Collapse "Show hidden" toggle and search input onto the same row as
  the Project Files / Task Documents segmented control. Stack again
  below 768px.

Mailbox / Todos
- Match Todos header treatment to the Mailbox header (typography,
  padding, border).
- Add top spacing above Mailbox Inbox/Outbox/Agents tab bar so the
  vertical gaps balance.

Settings
- Wider, resizable, persisted Settings modal.
- Project Models description and Authentication panel get proper
  horizontal padding.
- Reorder project sidebar so "General" is first.
- Plugins page: clean margins, integrate refresh button, exclude
  bundled runtimes from the "Installed Plugins" list (they were
  appearing twice — once erroring, once in their own section).
- New "Updates" panel with auto-check toggle + "Check now" button
  (frequency control noted as needing a backend schema field).

Background sessions
- Fix stale "AI N" / planning-icon badge: `handleDeleted` in
  `useBackgroundSessions` now writes a tombstone, advances the
  timestamp guard, and broadcasts completion so the cross-tab
  sync store stops resurrecting the deleted session on the next
  merge tick. Added regression test.
- Re-fetch list on SSE reconnect so terminal events fired during a
  network blip don't get permanently lost.

System stats
- Refresh button uses correct single class (was getting both `btn`
  and `btn-icon`, which conflicted on padding/border).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:26:07 -07:00
gsxdsm
d73f227cc6 feat(dashboard): redesign Planning modal as two-pane session manager
- Sidebar lists all planning sessions for the project (any status) with
  inline status badges, relative timestamps, "+ New session", and an
  inline delete confirm. Mobile collapses to a single pane with a back
  chevron in the header.
- Closing the modal (X / Escape / overlay) no longer cancels the server
  session — it stays in the list, resumable. Only an explicit Delete
  cancels + removes. Removes the now-redundant minimize button.
- Add re-sync on reopen so a session whose terminal SSE event was missed
  doesn't stay stuck on a stale loading view; previously only a hard
  reload recovered.
- Persist user-chosen modal width/height across opens via localStorage,
  driven by a ResizeObserver on the modal element.
- Theme the modal scrollbars to match the rest of the app.
- Banner dismiss only hides the banner now — it must not delete the
  underlying session, since sessions are first-class in the new sidebar.
- Refresh background sessions list on SSE reconnect so the footer "AI N"
  pill never gets stuck on tombstoned sessions after a network blip.
- Drop selection + broadcast completion on Create Task / Create Tasks
  so the footer count drops in lockstep instead of waiting on SSE.
- Auto-scroll the AI thinking output as new tokens stream in (only when
  already pinned to the tail).
- Guard overlay-click dismissal with a mousedown-on-overlay check so
  releasing a resize drag outside the modal doesn't close it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:58:35 -07:00
Fusion
9eef425525 fix(dashboard): card timer shows total execution = timed events + workflow runtime
The task card timer chip previously fell back through several metrics
(timed duration → workflow runtime → wallclock), so cards showed only a
subset of execution time. For FN-2714 this rendered <1m on the card while
the stats tab reported >2m of workflow runtime.

The chip now reports the sum of [timing]-tagged log events and workflow
step runtime (matching the new "Total execution time" metric in the stats
panel), with live elapsed for in-progress workflow steps. When neither
metric is recorded, the chip is hidden rather than falling back to
wallclock.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:05:50 -07:00
Fusion
3e43477dc8 fix(FN-2714): restore task timer chip to footer metadata row
- Move TaskCard time indicator rendering from the header back into the footer metadata row
- Render timer and files-changed metadata together in the shared footer row layout
- Update timer chip CSS to stay right-aligned with margin-left:auto and prevent shrinking
- Update TaskCard and mobile board tests to assert footer placement and alignment behavior
2026-04-27 12:45:53 -07:00