Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,82 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

Ships the Cross-Project Dashboard — the killer feature for developers
with dozens of projects. Turns RunHQ from a per-service manager into a
bird's-eye view that answers "which ones are out of date? which have
uncommitted work? which are hogging resources?" without visiting each
project individually.

### Features

- **overview:** cross-project dashboard closing the full
[#32](https://github.com/erdembas/runhq/issues/32) scope —
Git Status Matrix, Resource Heatmap, Last Activity Tracker,
Dependency Outdatedness, Security Alerts, and Filter & Sort all in
one screen.
- **overview:** two-phase aggregator in `runhq-core::overview`. The
fast path (git status, resource samples, staleness, tags) returns in
tens of ms; the opt-in slow path runs `npm outdated` / `cargo
outdated` / `npm audit` / `cargo audit` in parallel with per-command
timeouts and memoises results for 5 minutes, so re-opening the
dashboard or flipping filters never re-spawns scans.
- **overview:** `ProjectDetailDrawer` — a per-project "triage
cockpit". Severity- and bump-tinted tabs, a **triage rail** where
tiles double as both count summary *and* filter, multi-select with
a sticky bulk bar that copies all selected upgrade commands as a
shell script, in-drawer rescan with scan-freshness indicator, and
an "Open in…" overflow submenu listing detected editors with a
Finder/Explorer fallback. Advisory rows keep the external-link
button persistently visible because reading the GHSA write-up is
the primary triage action, not a secondary one.
- **overview:** dashboard filter bar restructured into three logical
chambers — **Organize** (Group / Sort dropdowns) · **Git** (All /
Dirty / Clean / Ahead / Behind / No upstream) · **Attention**
(Stale / Risk / Outdated). Each chamber is a single flex-child so
its label stays glued to its pills; chambers are separated by a
vertical divider plus a larger `gap-x-5`, so the row reads as three
distinct units instead of a flat stream of controls.
- **overview:** WorstOffenders band surfaces the top N projects
weighted by CVE severity × outdated majors; chips are clickable and
jump straight to the relevant drawer tab.
- **overview:** resource heatmap sorts the running fleet by
RAM / CPU, non-running projects pushed to the bottom so they never
wedge between hot ones.
- **ui:** auto-hiding macOS-style scrollbars (visible only during
scroll), global `cursor: pointer` on interactive elements, floating
drawer (margin + radius) scoped to the content area so the sidebar
rail stays visible underneath.

### Deferred

- **Auto-execute upgrade / CVE-fix commands** — intentionally held
off. A one-click `npm i pkg@latest` can pull breaking majors, shift
peer deps, rewrite the lockfile, and burn minutes with no obvious
rollback; responsibility for that decision normally lives in CI,
tests, and review. The current *copy-as-script* pattern already
captures ~90% of the value with zero risk surface. If ever
revisited, the preferred shape is **"Run in RunHQ terminal"**: open
the embedded terminal with `cwd` set and the command pre-filled but
*not submitted* — the user sees the exact line and hits Enter
themselves. See ROADMAP.md §1 for the full reasoning.

### Removed

- **dashboard:** the two 4-up stat grids at the top of the dashboard
(Running / Starting / Stopped / Failed and CVE / Outdated / Stale /
Dirty). Each one duplicated information already present in the
page header summary, the filter bar chips, and the WorstOffenders
band — on a quiet day they burned ~200px of vertical real estate
to display mostly zeros, pushing the actual project cards below the
fold. Their filter affordances survived intact in the filter bar
chips, which are tighter and more honest about being *filters*.
- **components:** orphaned `StatTile` / `AttentionTile` components
and their barrel export.
- **components:** old `ProjectDashboard` modal replaced by the
integrated dashboard + drawer flow.

## [0.6.0](https://github.com/erdembas/runhq/compare/v0.5.1...v0.6.0) (2026-04-23)


Expand Down
34 changes: 26 additions & 8 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The overarching goal: transform RunHQ from a **service manager** into a **projec

## 1. Cross-Project Dashboard

**Priority:** High | **Effort:** Medium | **Status:** Planned
**Priority:** High | **Effort:** Medium | **Status:** Shipped (feature/32)

The killer feature for developers with dozens of projects. Today, each service card is isolated — there is no way to see the big picture across all projects at once.

Expand All @@ -21,6 +21,24 @@ The killer feature for developers with dozens of projects. Today, each service c
- **Security Alerts** — Surface `npm audit`, `cargo audit`, and equivalent results across all projects in one view.
- **Filter & Sort** — Filter by status (dirty, stale, running), runtime (node, rust, go), category, or custom tags. Sort by last activity, resource usage, name.

### Delivered

- `runhq-core::overview` — two-phase aggregator: fast path (git, resources, staleness, tags) and opt-in slow path (`npm outdated` / `cargo outdated` / `npm audit` / `cargo audit`) in parallel with per-command timeouts and a 5-minute memoised cache.
- Dashboard with filter bar (status, runtime, tags), group / sort dropdowns, resource heatmap, and a worst-offenders panel whose chips jump straight to the relevant drawer tab.
- **ProjectDetailDrawer** ("triage cockpit") — severity / bump tiles that double as filters, hover-reveal row actions, sticky bulk bar with multi-select + copy-as-script, in-drawer rescan with scan-freshness indicator, and an overflow menu whose "Open in…" submenu lists detected editors and falls back to Finder/Explorer.
- Auto-hiding macOS-style scrollbars, global `cursor: pointer` on interactive elements, floating drawer (margin + radius) scoped to the content area so the sidebar rail stays visible.

### Deferred — Auto-execute upgrade / CVE-fix commands

Considered but intentionally held off (see discussion on feature/32):

- **Risk surface**: a one-click `npm i pkg@latest` can pull a breaking major, shift peer deps, rewrite the lockfile, and burn minutes of wall time with no obvious rollback. Even with user confirmation, the app would be assuming responsibility for a decision that normally rides on CI, tests, and review.
- **Current pattern is already 90% of the value**: the drawer emits a ready-to-paste upgrade command per row and a `Copy as script` for the full selection. The user owns the paste into their terminal where their existing safety net (branch, tests, commit hooks) still applies.
- **If we ever revisit**, the preferred shape is _not_ a background "run and hope" execute. It's:
- **"Run in RunHQ terminal" (pre-filled, not submitted)** — open the embedded terminal (`LogPanel`) with `cwd` set and the command typed in, but require the user's Enter. Zero-surprise: the user sees the exact line before it runs.
- **Dry-run preflight** where the package manager supports it (`npm install --dry-run`, `cargo update --dry-run`) before the real invocation, so the diff / plan is surfaced first.
- **Per-runtime opt-in** — enable per-project, never as a global default.

### Why

When you have 20+ projects, answering "which ones are out of date?", "which have uncommitted work?", "which are hogging resources?" requires visiting each one individually. A bird's-eye view eliminates that.
Expand Down Expand Up @@ -226,13 +244,13 @@ Currently, logs exist only in memory (ring buffers). Restarting the app clears e

The suggested implementation sequence, balancing impact and dependencies:

| Phase | Features | Rationale |
| ----------- | ---------------------------------------- | --------------------------------------------------------------------------------------------- |
| **Phase 1** | Cross-Project Dashboard, Bulk Operations | Highest impact, lowest friction. Transform RunHQ from per-service to cross-project awareness. |
| **Phase 2** | Quick .env Editor | High daily value, relatively self-contained. |
| **Phase 3** | Internal Browser, Git Diff Viewer | Rich UI features that require new embedded components. |
| **Phase 4** | Service Health Checks, Log Persistence | Infrastructure improvements that other features can build on. |
| **Phase 5** | Workspace Snapshots, CLI Interface | Polish and reach — snapshots for convenience, CLI for new audiences. |
| Phase | Features | Rationale |
| ----------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| **Phase 1** | ~~Cross-Project Dashboard~~ (shipped), Bulk Operations | Highest impact, lowest friction. Transform RunHQ from per-service to cross-project awareness. |
| **Phase 2** | Quick .env Editor | High daily value, relatively self-contained. |
| **Phase 3** | Internal Browser, Git Diff Viewer | Rich UI features that require new embedded components. |
| **Phase 4** | Service Health Checks, Log Persistence | Infrastructure improvements that other features can build on. |
| **Phase 5** | Workspace Snapshots, CLI Interface | Polish and reach — snapshots for convenience, CLI for new audiences. |

---

Expand Down
25 changes: 25 additions & 0 deletions apps/desktop/src-tauri/src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use runhq_core::editors::{self, DetectedEditor};
use runhq_core::error::{AppError, AppResult};
use runhq_core::git::{self, GitStatus};
use runhq_core::logs::LogLine;
use runhq_core::overview::{self, DependencyScanResult, OverviewSummary};
use runhq_core::paths;
use runhq_core::ports::{self, ListeningPort};
use runhq_core::process::ServiceStatus;
Expand Down Expand Up @@ -406,6 +407,28 @@ pub fn stop_stack(id: String, state: State<'_, AppState>) -> AppResult<StackStat
})
}

// ---- Overview -------------------------------------------------------------

#[tauri::command]
pub async fn get_project_overview(
stale_threshold_days: Option<i64>,
state: State<'_, AppState>,
) -> AppResult<OverviewSummary> {
let threshold = stale_threshold_days.unwrap_or(30);
overview::gather_overview(&state.store, &state.supervisor, threshold).await
}

/// Run the heavy per-project dependency/audit scans. Separated from
/// [`get_project_overview`] so the dashboard opens instantly and the user
/// can opt in to the expensive work with a button.
#[tauri::command]
pub async fn scan_project_dependencies(
force: Option<bool>,
state: State<'_, AppState>,
) -> AppResult<DependencyScanResult> {
overview::gather_dependency_scan(&state.store, force.unwrap_or(false)).await
}

// ---- Timeline -------------------------------------------------------------

#[tauri::command]
Expand All @@ -414,6 +437,7 @@ pub fn record_timeline_event(
service_id: Option<String>,
service_name: Option<String>,
description: String,
run_id: Option<String>,
state: State<'_, AppState>,
) -> AppResult<()> {
let db_path = state
Expand Down Expand Up @@ -443,6 +467,7 @@ pub fn record_timeline_event(
service_id.as_deref(),
service_name.as_deref(),
&description,
run_id.as_deref(),
)
}

Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,8 @@ pub fn run() {
ipc::git_stash_pop,
ipc::git_undo_last_commit,
ipc::git_amend_commit_message,
ipc::get_project_overview,
ipc::scan_project_dependencies,
ipc::record_timeline_event,
ipc::get_timeline,
ipc::get_daily_summary,
Expand Down
Loading
Loading