block / block/trailblaze

TrailRunner: show app icons (Android) / favicons (web) next to each target

Open
#200 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Kotlin
Stars
310
Forks
28
Avg merge
7h 45m
Merged PRs (30d)
8

Description

## Summary

TrailRunner's target list (the web UI served on `:52525`) shows targets as text-only rows. On a fresh workspace — or any workspace where the author hasn't wired up custom visuals — the list reads as an undifferentiated wall of names with no glyphs, which makes the first-run experience feel empty and makes targets harder to scan/discover at a glance.

Proposal: render a small **icon per target** next to its name — the **Android launcher icon** for app targets and the **site favicon** for web targets. A spike showed both are cheaply obtainable with CLI-only tooling (no browser, no extra services beyond public favicon endpoints), and sample icons rendered cleanly.

## The problem

- The target list has no per-target visual, so an empty/sparse workspace looks like nothing is there.
- Scanning a longer list is slower without recognizable glyphs (app icon / favicon are the fastest "which one is this" cue).
- There's no lightweight way for an author to attach a glyph today.

## Proposed design

Two complementary mechanisms, so the common case needs **zero per-target config**:

1. **Optional `icon:` field on the trailmap `target:` block** — a sibling to `display_name`, holding a workspace-relative path:
```yaml
target:
display_name: Example App
icon: assets/icons/android_com.example.app.png
```
It threads through `trailblaze check`/generation into the resolved target manifest (`AppTargetYamlConfig`) so the TrailRunner UI can render it. The bundled-config generator already passes unknown target fields through into the generated `targets/.yaml`, so once the field is on the model it carries end-to-end.

2. **Convention fallback (no config)** — when `icon:` is absent, the UI resolves by convention against a well-known assets folder:
- Android target → `assets/icons/android_.png`
- Web target → derive the host from the start/base URL → `assets/icons/favicon_.png`

These match the exact filenames the extraction step produces, so **just populating `assets/icons/` fills the empty first-run state** with no per-target authoring. Explicit `icon:` overrides convention when a target wants a custom glyph.

## How the icons are obtained (spike-validated, CLI-only)

**Android app icons — `adb` + `aapt`:**
- Pull `base.apk` (`adb shell pm path ` → `adb pull`).
- Ask `aapt dump badging` which resource is the launcher icon per density; take the highest-density PNG (`application-icon-640` / xxxhdpi, falling back to xxhdpi).
- **Adaptive icons** (`application-icon-*` points at a `.xml`): decode the adaptive XML to find the ``/`` drawable IDs (`aapt dump xmltree`), map IDs → real raster files via the resource table (`aapt dump --values resources`), then alpha-composite foreground over background. Note: real APKs use obfuscated resource paths and non-`ic_launcher` names, so reading the manifest-declared icon via `aapt` is more reliable than globbing `res/mipmap*/ic_launcher*`; and some foreground layers are vector-only (anydpi) — fall back to the mipmap raster foreground in that case.

**Web favicons — public services, no browser:**
- Google s2: `https://www.google.com/s2/favicons?domain=&sz=128` (most reliable; returns whatever size the site publishes).
- DuckDuckGo: `https://icons.duckduckgo.com/ip3/.ico` (sometimes higher-res than s2; may return `.ico` → re-save as PNG).
- Direct `/favicon.ico` and homepage `` scraping are unreliable — some sites bot-block and return an HTML challenge instead of an image. Some sites only publish a 16×16 favicon; usable as a tiny glyph but soft when upscaled.

The spike rendered 3 Android icons (including two adaptive-composited) and 4 favicons, all verified as valid PNGs.

## Where it surfaces

The TrailRunner target list in the web UI on `:52525` — a small icon rendered beside each target's `display_name`. Convention resolution + the assets folder means an author can drop icons in and immediately see the list populate.

## Suggested scope / phasing

- **Model/schema (small, self-contained):** add the optional `icon:` field to the authored target block and the resolved target manifest, thread it through resolution; add a pure resolver helper for the convention filename (`android_.png` / `favicon_.png`) + explicit-override precedence. Unit-testable, no UI dependency.
- **UI wiring:** TrailRunner renders the resolved/convention icon path next to each target (Compose/web-UI change).
- **Optional tooling follow-up:** a helper command to populate `assets/icons/` for a workspace's targets (the `aapt`/favicon steps above), so the convention folder fills itself.

Happy to iterate on the field name, the assets-folder location, and the host-derivation rule for web targets.

Contributor guide

Open the contributing guide

Research direction

Start with the target model/schema around AppTargetYamlConfig and the trailblaze check/generation path; then inspect the TrailRunner target list served on :52525. Use the assets/icons convention and explicit icon override as the acceptance boundary, with tests for resolver precedence and both target filename forms.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
frontend, mobile
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.