Skip to content

feat(dashboard): federated hub view for Skills page#4144

Merged
houko merged 7 commits intomainfrom
feat/skills-redesign
Apr 30, 2026
Merged

feat(dashboard): federated hub view for Skills page#4144
houko merged 7 commits intomainfrom
feat/skills-redesign

Conversation

@houko
Copy link
Copy Markdown
Contributor

@houko houko commented Apr 30, 2026

Summary

Reorganises the Skills browse UX around the four federated registries (FangHub / SkillHub / ClawHub / ClawHub-CN). Inspired by the design canvas at librefang-design/dashboard/project/app/catalog.jsx.

The hidden 4-option `` in the page header is replaced by a horizontal SkillHubBar with per-hub health dots, glyphs, and live counts, plus an All hubs pill that aggregates results across every configured hub. What's new `lib/skillHubs.ts` — single source of truth for hub metadata (id, display name, glyph, color, domain, one-liner, CLI install template). `components/SkillHubBar.tsx` — `SkillHubBar`, `HubBadge` (origin stamp on every card), `SkillHubHeadline` (domain + tagline tile shown when one hub is selected), `SkillInstallCommand` (copy-to-clipboard CLI block, ready for the detail drawer in a follow-up). `pages/SkillsPage.tsx`: `source: MarketplaceSource` → `hubFilter: "all" | MarketplaceSource`. Default lands on "all" so first-time visitors see every hub. Per-hub items pulled into their own memo and merged when `hubFilter === "all"`. Search now applies to every hub. Remote hubs (ClawHub / ClawHub-CN) are still gated behind a search keyword in "all" mode so we don't fan out a wide network query on every page mount. Hub status (`fetching` / `error` / `live`) feeds the health dots, derived from the existing React Query state — no new polling. `SkillCard` gained a `hubBadge` prop; the grid stamps each card with its origin so the unified view stays self-explanatory. What's preserved Tab bar (Installed / Browse), CategoryChips, hand selector, evolve drawers, install / uninstall flows, detail modals, marketplace stats — all unchanged. UI surface only, no mutation logic moved. What's deferred Wiring `SkillInstallCommand` into the detail drawer (currently exported but not yet used). Replacing the Browse / Installed tabs with the canvas's `All / Installed / Available` filter — touches the install / uninstall flows; best done once with testing instead of in this PR. Test plan [x] `pnpm run typecheck` clean. [x] `pnpm run build` succeeds. [ ] Manual: visit /skills with no daemon — "all hubs" shows FangHub items, ClawHub queries dormant. [ ] Manual: type a keyword — remote hubs fire, counts populate, health dots reflect each hub independently. [ ] Manual: click each hub pill — narrow to that hub, headline tile appears. [ ] Manual: install a skill from each hub — flow unchanged.

houko added 2 commits April 30, 2026 15:35
Reorganises the Skills browse experience around the four federated
registries (FangHub / SkillHub / ClawHub / ClawHub-CN) instead of a
hidden 4-option `<select>` in the page header. Inspired by the design
canvas at `librefang-design/dashboard/project/app/catalog.jsx`.

What's new
- `lib/skillHubs.ts` — single source of truth for hub metadata: id,
  display name, glyph, color, domain, one-line description, and CLI
  install command template.
- `components/SkillHubBar.tsx` — pure presentational pieces shared by
  the page: `SkillHubBar` (horizontal pill row with health dot + per-hub
  count + "All hubs" pill), `HubBadge` (origin stamp on every card),
  `SkillHubHeadline` (domain + tagline tile when a single hub is
  selected), `SkillInstallCommand` (copy-to-clipboard CLI block, ready
  for the detail drawer in a follow-up).
- `pages/SkillsPage.tsx`:
  - `source: MarketplaceSource` → `hubFilter: "all" | MarketplaceSource`.
    Default lands on "all" so first-time visitors see every hub at once.
  - Per-hub items pulled into their own memo (`fanghubItems`,
    `clawhubItems`, `clawhubCnItems`, `skillhubItems`) and merged when
    `hubFilter === "all"`. Search now applies to every hub instead of
    being suppressed for FangHub. Remote hubs (ClawHub / ClawHub-CN)
    are still gated behind a search keyword in "all" mode so we don't
    fan out a wide network query on every page mount.
  - Hub status (`fetching` / `error` / `live`) feeds health dots in the
    source bar, derived from the existing React Query state — no new
    polling.
  - `SkillCard` gained a `hubBadge` prop. The browse grid stamps each
    card with its origin so the "all hubs" view stays self-explanatory.
  - PageHeader's hub `<select>` is gone — replaced by the bar in the
    page body.

What's preserved
- Tab bar (Installed / Browse), CategoryChips, hand selector, evolve
  drawers, install/uninstall flows, detail modals, marketplace stats.
  This commit is UI surface area only — no mutation logic moved.

What's deferred
- `SkillInstallCommand` is exported but not yet wired into the detail
  drawer. The drawer copy ("Install via FangHub / SkillHub / …") still
  routes through the existing per-hub mutations.
- Replacing the Browse / Installed tabs with the design canvas's
  `All / Installed / Available` install filter is a separate change —
  it touches the install/uninstall flows and is best done once with
  testing instead of as a slip-stream.

Verification
- `pnpm run typecheck` clean.
- `pnpm run build` succeeds.
Two near-identical `HubFilter` types existed: one local in SkillsPage,
one re-exported from `components/SkillHubBar`. Reuse the imported one
so the `as SkillHubFilter` / `as HubFilter` casts at the call sites
disappear.

No behavior change. typecheck clean.
- Hub-source ribbon now lives in the absolute top-right corner of the
  card, lifted out of the header row.
- Icon switched to hub-color tinted 36×36 rounded tile (browse) /
  success-tinted (installed). Uses inline style for the hub color so
  every hub gets its own distinct accent without a tailwind safelist.
- Header collapses name + verified/installed pill on one row, with a
  font-mono meta line ("skill · author · v0.4.2") below — matches the
  design canvas density.
- Stats (downloads / stars / tools / updated) and the Install button
  share one inline row instead of stacking, freeing vertical space in
  dense grids.
- Removed the duplicate "View detail" button on installed cards (the
  card body itself is clickable) and the duplicate Installed badge in
  the action slot when the header already carries it.
- Restored the `onViewDetail` destructure that was lost in the layout
  rewrite (runtime ReferenceError "onViewDetail is not defined").

Reachable visual diff via vite HMR — typecheck clean.
@github-actions github-actions Bot added no-rust-required This task does not require Rust knowledge size/L 250-999 lines changed and removed no-rust-required This task does not require Rust knowledge labels Apr 30, 2026
Two regressions from the previous SkillCard rewrite:

- A 1px gradient accent bar still sat at the top of every card despite
  not being in the design canvas. The `accentClass` plumbing it relied
  on is removed; cards now render as a single panel under the abs hub
  badge.
- Default `hubFilter = "all"` collided with the search-required gate
  for remote hubs, leaving the page empty until the user typed
  something. Switched the default to `"fanghub"` so first paint shows
  the always-warm local registry; switching to "all" still exists for
  cross-hub search.

UI surface only — typecheck clean.
@houko houko force-pushed the feat/skills-redesign branch from 5787c00 to 584b5ce Compare April 30, 2026 08:10
@github-actions github-actions Bot added the no-rust-required This task does not require Rust knowledge label Apr 30, 2026
Co-authored-by: houko <12625278+houko@users.noreply.github.com>
@github-actions github-actions Bot removed the no-rust-required This task does not require Rust knowledge label Apr 30, 2026
The Skills page redesign exposes hub-card slots for version, author,
and tags but the FangHub registry endpoint always reported them as
`null` / `[]` — so the cards stayed visually empty even when the
on-disk SKILL.md frontmatter actually carried those fields.

Extend `parse_skill_md_frontmatter` to read the existing optional
keys instead of dropping them on the floor:

  ---
  name: ansible
  description: "..."
  version: 0.5.1            ← now surfaced
  author: librefang         ← now surfaced
  tags: [devops, infra]     ← now surfaced
  ---

Old SKILL.md files that only carry `name` + `description` keep
working: missing keys parse to `None` / `[]`, the JSON payload still
serializes the same shape (`null` / `[]`) the dashboard already
handles.

Also strips surrounding quotes on YAML scalar values so
`description: "Foo"` no longer renders as `"Foo"` in the UI — minor
visual fix that came along for free with the rewrite.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying librefang with  Cloudflare Pages  Cloudflare Pages

Latest commit: f972cba
Status: ✅  Deploy successful!
Preview URL: https://f83f41a3.librefang.pages.dev
Branch Preview URL: https://feat-skills-redesign.librefang.pages.dev

View logs

@github-actions github-actions Bot added size/XL 1000+ lines changed and removed size/L 250-999 lines changed labels Apr 30, 2026
CI Quality (clippy -D warnings) flagged the new `parse_yaml_inline_list`
helper for wrapping `strip_yaml_value` in a redundant closure inside
the `.map(...)` call. Use the function reference directly.

  Run: https://github.com/librefang/librefang/actions/runs/25160443347
@houko houko merged commit 9a8201b into main Apr 30, 2026
23 checks passed
@houko houko deleted the feat/skills-redesign branch April 30, 2026 11:16
@houko houko mentioned this pull request May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL 1000+ lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant