Reachy_Mini / docs /APP_ICON_CONVENTION.md
tfrere's picture
tfrere HF Staff
feat(api): publish iconUrl on /api/js-apps from Space siblings
07a94f1
|
Raw
History Blame Contribute Delete
4.78 kB

App icon convention

Status: convention v1 Audience: authors shipping a Reachy Mini app to the Hugging Face Hub Implemented by: reachy-mini-website catalog server (this repo) + reachy_mini_mobile_app, reachy_mini_desktop_app Source of truth: server/index.jsfindIconUrl()

This document specifies how a Reachy Mini app declares a custom icon. Apps that don't follow it keep working - the surface falls back to the front-matter emoji: glyph, which is the existing behaviour.


1. The convention in three lines

To ship a custom icon for your Reachy Mini app:

  1. Commit icon.svg (preferred) or icon.png at the root of your Hugging Face Space repository.
  2. That's it. Within ~5 minutes (the catalog cache TTL) the mobile shell, the desktop app and the website surface your icon automatically, replacing the README front-matter emoji.
  3. If both files are present, icon.svg wins.

No README change required. No tag to add. No PR to file against this repo. The catalog server scans the file list once per refresh and publishes a resolved URL on the app entry; every client consumes it.


2. Why a file convention and not cardData.thumbnail

HF Spaces support a thumbnail: field in README front-matter, but:

  • thumbnail is full-bleed marketing artwork (typically 1200x630), not a square avatar. Scaling it to a 22 px or 44 px tile produces muddy thumbnails.
  • We want app authors to ship a dedicated, optimised glyph they control without learning the HF metadata schema.
  • SVG support means the icon scales cleanly across every mount point (rail tile, pinned grid, iframe header) from a single asset.

thumbnail: keeps its existing role (banner artwork on the Space's HF page) and is not consulted by this resolution path.


3. Format & dimension recommendations

Property Recommended Hard requirement
Format icon.svg (vector) icon.svg or icon.png
Aspect ratio 1:1 (square) Renderers crop with object-fit: contain, but non-square icons render with letterboxing - prefer a true square
Min PNG size 256x256 None enforced. PNGs below 64x64 will look soft on the pinned grid (44 px on retina ≈ 88 effective px)
Background Transparent OR solid colour None - your call. Renderers don't add their own plate, so an icon with no background renders directly on the tile colour
Padding Bake ~10% inner padding into the asset None - but icons that bleed edge-to-edge will touch the tile's rounded corners
Light/dark variants Single asset that works on both None - if you must, ship two SVGs and use prefers-color-scheme inside the SVG via CSS

Style notes

  • Iconic, not photographic. A solid filled silhouette reads at 22 px; a screenshot doesn't.
  • High contrast against background.paper. The mobile app paints the tile background with the surface colour (very light grey on light, near-black on dark). A pure white icon disappears on light.
  • No drop shadow baked into the asset. The renderer doesn't add one either, and a baked shadow won't scale across sizes.

4. How resolution works (for the curious)

  1. The catalog server calls https://huggingface.co/api/spaces?filter=reachy_mini&full=true. With full=true, the HF Hub returns siblings: [{ rfilename: ... }] for every Space - the complete file list.

  2. For each app, findIconUrl() (in server/index.js) scans the list for root-level filenames matching ICON_CANDIDATES in order (icon.svgicon.png).

  3. The first match becomes:

    https://huggingface.co/spaces/<author>/<repo>/resolve/main/<filename>
    

    resolve/main/ (not raw/main/) so LFS pointers follow through transparently and the Content-Type is set from the extension, which <img> needs.

  4. The URL is published on the app entry as a top-level iconUrl field. null when neither candidate exists.

  5. Clients (reachy_mini_mobile_app, reachy_mini_desktop_app) read iconUrl and render an <img> when present, falling back to the front-matter emoji otherwise. A runtime image load failure re-falls-back to the emoji without a refresh.

The whole resolution path is server-side, behind the 5-minute catalog cache. Adding 100 more apps adds zero per-client probes.


5. Adding new icon formats

If you need to support a new format (say, icon.webp), edit ICON_CANDIDATES in server/index.js:

const ICON_CANDIDATES = ['icon.svg', 'icon.png', 'icon.webp'];

Order matters - the first hit wins, so put the preferred format first. Bumping the catalog cache (POST /api/js-apps/refresh-categories or just wait 5 minutes) picks up the new resolution rule.