feat(dashboard): federated hub view for Skills page#4144
Merged
Conversation
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.
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.
5787c00 to
584b5ce
Compare
Co-authored-by: houko <12625278+houko@users.noreply.github.com>
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.
Deploying librefang with
|
| Latest commit: |
f972cba
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://f83f41a3.librefang.pages.dev |
| Branch Preview URL: | https://feat-skills-redesign.librefang.pages.dev |
Merged
3 tasks
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.