feat(wails): move the frontend onto v3's generated bindings

frontend/wailsjs/ is deleted and frontend/bindings/ takes its place —
a real TypeScript module tree nested by Go import path, generated by
wails3's static analyser rather than by building the app and running
it.  The @go alias absorbs the constant prefix, so a call site imports
'@go/library/library.js' and the codemod over all 93 sites was a
specifier rewrite plus splitting @go/models' namespaces into one
import per package.

The 12 SetContext bindings and the fake `context` model are gone, as
Phase 2's ServiceStartup port promised: 272 methods across 12
services, none of them plumbing.

@runtime/runtime is now a local shim (src/wails/runtime.ts) over
@wailsio/runtime, so the 22 EventsOn imports are untouched.  It
unwraps v3's WailsEvent into v2's callback shape, which is exact here:
nothing in backend/events passes more than one data argument, and v3
only packs arguments into a slice when there is more than one.

v3 tells the truth about two things v2 lied about, and that is most of
the diff.  A Go nil slice really does arrive as JSON null, and a Go
named string type really is an enum; v2 typed them as T[] and string.
utils/binding.ts states the app's actual contract — an absent list is
an empty list — once, at the boundary where it is true, and also drops
the CancellablePromise the app never cancels.  Four test fixtures
widen an enum field back to its value union.

Not done, and Phase 5's to fix: frontend/test/support/wails-fake.ts
still fakes window.go, which v3 does not have, so `make ui-test` is
broken and harness.test.ts fails to compile on EventsEmit.  That test
also asserts v2 ordering that no longer holds — v3's Events.Emit calls
the backend and does not notify in-page listeners at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDCbcCZQepnpSQYJ6SxxZm
This commit is contained in:
2026-08-14 17:48:38 -04:00
co-authored by Claude Opus 5
parent c9905fbcff
commit 162c68769f
133 changed files with 5755 additions and 5094 deletions
@@ -0,0 +1,469 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* AlbumCompleteFunc answers "is the local album with this id complete",
* i.e. do its files declare a track total the library actually has.
*/
export type AlbumCompleteFunc = any;
/**
* IndexStatus is the full index build status, exposed to the frontend.
*/
export interface IndexStatus {
"building": boolean;
"ready": boolean;
/**
* RFC3339 timestamp of last complete build
*/
"lastBuilt"?: string;
"tiers": TierStatus[] | null;
"artists": number;
"recordings": number;
"releaseGroups": number;
"totalRows": number;
}
/**
* LBSimilarArtist represents a similar artist from the
* ListenBrainz labs API.
*/
export interface LBSimilarArtist {
"artistMbid": string;
"name": string;
"score": number;
}
/**
* LBTopRecording represents a popular recording from the
* ListenBrainz popularity API.
*
* JSON tags use camelCase for Wails→frontend serialization.
* The API response uses snake_case, so we unmarshal into
* lbTopRecordingWire first, then convert.
*/
export interface LBTopRecording {
"recordingMbid": string;
"artistName": string;
"trackName": string;
"totalListenCount": number;
"caaReleaseMbid": string;
/**
* ReleaseGroupMBID is resolved from CAAReleaseMBID so a top-track
* row can link to its album page with the track highlighted.
*/
"releaseGroupMbid"?: string;
"releaseName": string;
/**
* milliseconds (from LB API)
*/
"length": number;
"inLibrary": boolean;
"localId"?: number;
}
/**
* LBTopReleaseGroup represents a popular release group from the
* ListenBrainz popularity API.
*/
export interface LBTopReleaseGroup {
"releaseGroupMbid": string;
"title": string;
"artistName": string;
"type": string;
"date": string;
"totalListenCount": number;
"caaReleaseMbid": string;
"inLibrary": boolean;
"localId"?: number;
}
/**
* LyricsResult is a single lyric-search hit, mapped from the DB layer
* into the camelCase shape the frontend consumes.
*/
export interface LyricsResult {
"recordingId": number;
"filePath": string;
"lengthMs": number;
"title": string;
"artist": string;
"album": string;
}
/**
* MBArtist is a Wails-friendly projection of a MusicBrainz artist.
*/
export interface MBArtist {
"mbid": string;
"name": string;
"sortName": string;
"englishName"?: string;
"type": string;
"country": string;
"disambiguation": string;
"score": number;
/**
* raw LB listen count (0 if unknown)
*/
"popularity": number;
"listenerCount": number;
/**
* true if the user owns music by this artist
*/
"inLibrary": boolean;
/**
* local artist row ID for navigation
*/
"localId"?: number;
}
/**
* MBRecording is a Wails-friendly projection of a MusicBrainz
* recording.
*/
export interface MBRecording {
"mbid": string;
"title": string;
"length": number;
"artistCredit": string;
/**
* for linking the artist to its detail page
*/
"artistMbid"?: string;
"score": number;
/**
* raw LB listen count (0 if unknown)
*/
"popularity": number;
"listenerCount": number;
/**
* parent release, for album navigation
*/
"caaReleaseMbid"?: string;
/**
* ReleaseGroupMBID is resolved from CAAReleaseMBID so a track can
* link to its album page with the track highlighted, matching how
* tracks behave everywhere else.
*/
"releaseGroupMbid"?: string;
/**
* album title
*/
"releaseName"?: string;
/**
* true if the user owns this recording
*/
"inLibrary": boolean;
/**
* local recording row ID
*/
"localId"?: number;
}
/**
* MBRelease is a Wails-friendly projection of a MusicBrainz release.
*/
export interface MBRelease {
"mbid": string;
"title": string;
"date": string;
"country": string;
"status": string;
"artistCredit"?: string;
"tracks"?: MBTrack[] | null;
/**
* ReleaseGroupMBID is the parent release group's MBID. Empty unless
* the lookup requested the "release-groups" include (LookupRelease
* does); used to resolve a release-level MBID (what many taggers
* write) back to the release-group MBID everything else on the
* album page is keyed by.
*/
"releaseGroupMbid"?: string;
}
/**
* MBReleaseGroup is a Wails-friendly projection of a MusicBrainz
* release group.
*/
export interface MBReleaseGroup {
"mbid": string;
"title": string;
"primaryType": string;
"secondaryTypes"?: string[] | null;
"firstReleaseDate": string;
"artistCredit": string;
/**
* for linking the artist to its detail page
*/
"artistMbid"?: string;
/**
* raw LB listen count (0 if unknown)
*/
"popularity": number;
"listenerCount": number;
/**
* true if the user owns this album
*/
"inLibrary": boolean;
/**
* local release_group row ID
*/
"localId"?: number;
}
/**
* MBSearchResult aggregates the three searchable entity types
* returned by the MusicBrainz search API.
*/
export interface MBSearchResult {
"artists"?: MBArtist[] | null;
"releaseGroups"?: MBReleaseGroup[] | null;
"recordings"?: MBRecording[] | null;
"topResults"?: TopResult[] | null;
}
/**
* MBTrack is a Wails-friendly projection of a MusicBrainz track.
*/
export interface MBTrack {
"position": number;
"discNumber": number;
"title": string;
"length": number;
"mbid": string;
"inLibrary": boolean;
"localId"?: number;
}
/**
* MusicBrainzClient wraps the musicbrainzws2 library with a local
* response cache. Every API call checks the cache first and stores
* successful responses for future hits.
*
* A proactive rate limiter gates all outgoing requests at 1 req/sec
* to avoid triggering MusicBrainz 429 responses. The underlying
* musicbrainzws2.Client still retries on 429 as a safety net, but
* the limiter should prevent most rate-limit hits.
*/
export interface MusicBrainzClient {
}
/**
* RateLimiter enforces a maximum request rate using a token bucket.
* MusicBrainz requires ≤1 request per second and rejects ALL
* requests (not just excess) when the rate is exceeded, so callers
* block proactively via Wait rather than retrying reactively.
*
* A limiter may carry a second, slower **background lane** (see
* WithBackgroundLane). A caller marked by WithBackgroundPriority is
* paced by that lane *and* yields to interactive callers: while any
* interactive Wait is outstanding, background waits do not take a
* token at all. This is what keeps a multi-thousand-request backfill
* from putting the album page the user is looking at right now behind
* hours of queued work.
*
* RateLimiter is safe for concurrent use.
*/
export interface RateLimiter {
}
/**
* Shelf is one horizontal row on the Explore page.
*
* A shelf carries albums or artists, never both: they route to
* different pages and render as different cards, and a row that is
* sometimes one and sometimes the other is two components pretending to
* be one.
*/
export interface Shelf {
"id": string;
"kind": ShelfKind;
/**
* Title is the row heading.
*/
"title": string;
/**
* Subtitle says why these are here. As on Home it is not
* decoration: without it a shelf is indistinguishable from a
* random grid.
*/
"subtitle": string;
"albums"?: MBReleaseGroup[] | null;
"artists"?: MBArtist[] | null;
}
/**
* ShelfKind identifies what a shelf is built from, so the frontend can
* pick an icon and a spec can assert on a shelf without matching
* display copy.
*/
export enum ShelfKind {
/**
* The Go zero value for the underlying type of the enum.
*/
$zero = "",
/**
* Shelf kinds.
*/
ShelfPopularAlbums = "popular-albums",
ShelfPopularArtists = "popular-artists",
ShelfMoreFromOwned = "more-from-owned",
};
/**
* ShelfPage is what Explore renders before a query.
*
* State exists because "no shelves" has three different causes here and
* the page must not present them identically — a blank panel is the bug
* this whole feature is fixing, and a blank panel that says nothing
* about why is the same bug with more code behind it.
*/
export interface ShelfPage {
"shelves": Shelf[] | null;
/**
* State is one of:
*
* "ready" — the catalog is here and the shelves are below.
* "building" — it is being fetched or built right now.
* "no-index" — there is no catalog. Search still works over
* whatever is in the index, which may be nothing;
* the page says so and points at Settings.
*/
"state": string;
}
/**
* ThumbnailRequest is a single item in a batch thumbnail request.
*/
export interface ThumbnailRequest {
"mbid": string;
"albumName": string;
"artistName": string;
}
/**
* TierStatus represents the state of a single index tier.
*/
export interface TierStatus {
"name": string;
/**
* "pending", "running", "complete", "error", "skipped"
*/
"state": string;
"total": number;
"completed": number;
"error"?: string;
/**
* Detail is a human-readable progress line for stages whose raw
* completed/total numbers say little on their own — the listens
* stream reports "42.3 / 205.1 GB · 18 MB/s · ~3h20m left" here.
*/
"detail"?: string;
}
/**
* TopResult represents a single top-result card shown above the
* categorized search lists. Computed by intent scoring after all
* reranking is complete.
*/
export interface TopResult {
/**
* "artist", "release_group", "recording"
*/
"entityType": string;
"mbid": string;
"name": string;
/**
* for tracks/albums
*/
"artistCredit"?: string;
/**
* for linking the artist subtitle
*/
"artistMbid"?: string;
"intentScore": number;
/**
* Artist-specific
* "Group", "Person"
*/
"artistType"?: string;
"country"?: string;
/**
* Album-specific
*/
"primaryType"?: string;
"year"?: string;
/**
* Track-specific. ReleaseGroupMBID is resolved (from CAAReleaseMBID)
* so a track click can open its album page with the track highlighted,
* matching how tracks behave everywhere else. ReleaseName is the album
* title used for the album page header.
*/
"length"?: number;
"caaReleaseMbid"?: string;
"releaseGroupMbid"?: string;
"releaseName"?: string;
/**
* Library status — populated from index cross-reference columns.
*/
"inLibrary": boolean;
}
/**
* TrackLyrics is the stored or freshly-fetched lyrics for one track.
* Source is "embedded" (from the file's tags / library DB), "lrclib"
* (fetched on demand), or "" when none are available.
*/
export interface TrackLyrics {
"plain": string;
"synced": string;
"instrumental": boolean;
"source": string;
}
/**
* TrackThumbnailRequest is a single item in a batch track thumbnail
* request. Either ReleaseMBID or ReleaseGroupMBID may be empty;
* the proxy tries whichever is present.
*/
export interface TrackThumbnailRequest {
/**
* stable key used in the returned map
*/
"key": string;
"releaseMbid": string;
"releaseGroupMbid": string;
"albumName": string;
"artistName": string;
}