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,19 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as Service from "./service.js";
export {
Service
};
export type {
CandidateTrack,
DuplicateCheckResult,
DuplicateTrackInfo,
FavoritesConfigProvider,
PhantomMatch,
PhantomSearchResult,
Summary,
Track,
WithTracks
} from "./models.js";
@@ -0,0 +1,103 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* CandidateTrack represents a potential library match for a
* phantom track.
*/
export interface CandidateTrack {
"FilePath": string;
"Title": string;
"Artist": string;
"Album": string;
"Duration": string;
"Score": number;
}
/**
* DuplicateCheckResult contains the outcome of checking for
* duplicate tracks in a playlist.
*/
export interface DuplicateCheckResult {
"Duplicates": DuplicateTrackInfo[] | null;
"Unique": string[] | null;
}
/**
* DuplicateTrackInfo holds metadata for a track that already
* exists in a playlist.
*/
export interface DuplicateTrackInfo {
"FilePath": string;
"Title": string;
"Artist": string;
"Album": string;
"Duration": string;
}
/**
* FavoritesConfigProvider is a narrow interface for reading and
* writing the default-playlist configuration.
*/
export type FavoritesConfigProvider = any;
/**
* PhantomMatch represents a high-confidence pairing of a phantom
* track to a library track.
*/
export interface PhantomMatch {
"PhantomPath": string;
"PhantomTitle": string;
"Candidate": CandidateTrack;
}
/**
* PhantomSearchResult contains auto-matched pairs and remaining
* unmatched phantom paths for a batch search operation.
*/
export interface PhantomSearchResult {
"AutoMatched": PhantomMatch[] | null;
"Unmatched": string[] | null;
}
/**
* Summary is a lightweight representation of a playlist for the
* picker UI.
*/
export interface Summary {
"ID": number;
"Name": string;
"CreatedAt": string;
"UpdatedAt": string;
"IsSmart": boolean;
}
/**
* Track represents a track within a playlist, including its
* metadata.
*/
export interface Track {
"ID": number;
"Position": number;
"FilePath": string;
"Title": string;
"Artist": string;
"Album": string;
"CoverArtPath": string;
"CoverArtSmall": string;
"CoverArtMedium": string;
"CoverArtLarge": string;
"Duration": string;
"Phantom": boolean;
"ArtistMBID": string;
"ReleaseGroupMBID": string;
"RecordingMBID": string;
}
/**
* WithTracks contains a playlist summary and all its tracks.
*/
export interface WithTracks {
"Summary": Summary;
"Tracks": Track[] | null;
}
@@ -0,0 +1,336 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* Service manages playlist operations.
* @module
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import { Call as $Call, CancellablePromise as $CancellablePromise } from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as library$0 from "../library/models.js";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
/**
* AddToDefaultPlaylist adds multiple tracks to the default
* playlist, skipping any that are already present.
*/
export function AddToDefaultPlaylist(filePaths: string[] | null): $CancellablePromise<void> {
return $Call.ByID(1493850977, filePaths);
}
/**
* AddTracksToPlaylist adds one or more tracks to an existing
* playlist.
*/
export function AddTracksToPlaylist(playlistID: number, filePaths: string[] | null): $CancellablePromise<void> {
return $Call.ByID(2843242868, playlistID, filePaths);
}
/**
* CreatePlaylist creates a new empty playlist with the given name.
*/
export function CreatePlaylist(name: string): $CancellablePromise<$models.Summary> {
return $Call.ByID(4080218878, name);
}
/**
* CreatePlaylistWithTracks creates a new playlist and populates
* it with tracks.
*/
export function CreatePlaylistWithTracks(name: string, filePaths: string[] | null): $CancellablePromise<$models.Summary> {
return $Call.ByID(4079964334, name, filePaths);
}
/**
* CreateSmartPlaylist creates a new smart playlist with the given
* name and JSON rule set. The rules are validated before storage.
*/
export function CreateSmartPlaylist(name: string, rulesJSON: string): $CancellablePromise<$models.Summary> {
return $Call.ByID(2020825359, name, rulesJSON);
}
/**
* DeletePlaylist deletes a playlist and its M3U8 file.
* If the deleted playlist was the default, a new default
* playlist is automatically created.
*/
export function DeletePlaylist(playlistID: number): $CancellablePromise<void> {
return $Call.ByID(1998239209, playlistID);
}
/**
* EnsureDefaultPlaylist verifies the configured default playlist
* exists in the database. If the playlist is missing or no ID
* has been configured yet, a new playlist named "Favorites" is
* created and the config is updated.
*/
export function EnsureDefaultPlaylist(): $CancellablePromise<void> {
return $Call.ByID(3280319649);
}
/**
* EvaluateSmartPlaylist loads the rule set for a smart playlist
* from the database and evaluates it against the track library,
* returning the matching tracks.
*/
export function EvaluateSmartPlaylist(playlistID: number): $CancellablePromise<library$0.Track[] | null> {
return $Call.ByID(2192251014, playlistID);
}
/**
* FindDuplicateTracksInPlaylist checks which of the given file
* paths already exist in the specified playlist. Returns metadata
* for each duplicate and a list of non-duplicate file paths.
*/
export function FindDuplicateTracksInPlaylist(playlistID: number, filePaths: string[] | null): $CancellablePromise<$models.DuplicateCheckResult> {
return $Call.ByID(57159613, playlistID, filePaths);
}
/**
* FindPhantomMatches searches the library for matches for the
* given phantom file paths. High-confidence matches are returned
* as auto-matched pairs; the rest remain in the unmatched list.
*/
export function FindPhantomMatches(playlistID: number, phantomPaths: string[] | null): $CancellablePromise<$models.PhantomSearchResult> {
return $Call.ByID(2112903901, playlistID, phantomPaths);
}
/**
* GetAllPlaylists returns all playlists ordered by most recently
* updated.
*/
export function GetAllPlaylists(): $CancellablePromise<$models.Summary[] | null> {
return $Call.ByID(1751004770);
}
/**
* GetAllPlaylistsWithTracks returns all playlists with their
* tracks in a single call, merging phantom tracks from M3U8 files.
*/
export function GetAllPlaylistsWithTracks(): $CancellablePromise<$models.WithTracks[] | null> {
return $Call.ByID(1602859466);
}
/**
* GetDefaultPlaylistInfo returns the ID and name of the default
* playlist for display in the frontend.
*/
export function GetDefaultPlaylistInfo(): $CancellablePromise<$models.Summary> {
return $Call.ByID(845855111);
}
/**
* GetDefaultPlaylistTrackPaths returns the file paths of all
* tracks in the default playlist.
*/
export function GetDefaultPlaylistTrackPaths(): $CancellablePromise<string[] | null> {
return $Call.ByID(135237414);
}
/**
* GetPhantomCandidates returns scored candidate matches for a
* single phantom track.
*/
export function GetPhantomCandidates(playlistID: number, phantomPath: string): $CancellablePromise<$models.CandidateTrack[] | null> {
return $Call.ByID(904105323, playlistID, phantomPath);
}
/**
* GetPlaylistTracks returns all tracks in a playlist with full
* metadata, merging phantom tracks from the M3U8 file.
*/
export function GetPlaylistTracks(playlistID: number): $CancellablePromise<$models.Track[] | null> {
return $Call.ByID(3997481994, playlistID);
}
/**
* GetSmartPlaylistRules returns the raw JSON rule string for an
* existing smart playlist. This is used when the user opens the
* rule editor for an existing smart playlist.
*/
export function GetSmartPlaylistRules(playlistID: number): $CancellablePromise<string> {
return $Call.ByID(997403286, playlistID);
}
/**
* GetSmartPlaylistTracks returns the persisted snapshot of a smart
* playlist as regular playlist tracks (with resolved cover art and
* phantom entries), identical to a normal playlist. If the playlist
* has never been materialized — e.g. it predates snapshot support —
* it is evaluated and stored on first access.
*/
export function GetSmartPlaylistTracks(playlistID: number): $CancellablePromise<$models.Track[] | null> {
return $Call.ByID(2517005223, playlistID);
}
/**
* ImportPlaylist imports a playlist from an external M3U/M3U8
* file. It creates a new playlist in the DB, resolves tracks
* against the library, and saves an M3U8 file.
*/
export function ImportPlaylist(filePath: string): $CancellablePromise<$models.Summary> {
return $Call.ByID(3295216919, filePath);
}
/**
* ImportPlaylists imports multiple playlists from external M3U/M3U8
* files. Each file is imported sequentially using ImportPlaylist.
* Errors from individual imports are collected; partial success is
* possible. Returns the summaries of successfully imported playlists
* and the first error encountered (if any).
*/
export function ImportPlaylists(filePaths: string[] | null): $CancellablePromise<$models.Summary[] | null> {
return $Call.ByID(2505276524, filePaths);
}
/**
* MaterializeUnmaterializedSmartPlaylists evaluates and snapshots any
* smart playlist that has never been materialized (smart_snapshot_at
* IS NULL) — e.g. playlists created before creation-time
* materialization existed. It runs once at startup and is idempotent:
* once every smart playlist has a snapshot it becomes a no-op. Errors
* on individual playlists are logged and skipped so one bad rule set
* doesn't block the rest.
*/
export function MaterializeUnmaterializedSmartPlaylists(): $CancellablePromise<void> {
return $Call.ByID(3018316369);
}
/**
* PreviewSmartPlaylist evaluates a rule set from raw JSON without
* requiring a saved playlist. This powers live preview in the rule
* editor — the frontend sends rules as they are being edited and
* receives matching tracks immediately.
*/
export function PreviewSmartPlaylist(rulesJSON: string): $CancellablePromise<library$0.Track[] | null> {
return $Call.ByID(3006325439, rulesJSON);
}
/**
* RefreshSmartPlaylist re-evaluates a smart playlist's rules against
* the current library and replaces its persisted membership in
* playlist_tracks with the result. This is the only path that
* re-evaluates a smart playlist — opening one otherwise reads the
* stored snapshot. Triggered on rule save and by the manual Refresh
* button.
*/
export function RefreshSmartPlaylist(playlistID: number): $CancellablePromise<void> {
return $Call.ByID(2225480718, playlistID);
}
/**
* RemoveFromDefaultPlaylist removes multiple tracks from the
* default playlist.
*/
export function RemoveFromDefaultPlaylist(filePaths: string[] | null): $CancellablePromise<void> {
return $Call.ByID(619411935, filePaths);
}
/**
* RemovePhantomTracks removes phantom entries from a playlist's
* M3U8 file. Since phantom tracks have no DB rows, only the
* M3U8 file is modified.
*/
export function RemovePhantomTracks(playlistID: number, phantomPaths: string[] | null): $CancellablePromise<void> {
return $Call.ByID(823798913, playlistID, phantomPaths);
}
/**
* RemoveTracksFromPlaylist removes multiple tracks from a playlist
* by their playlist_track IDs.
*/
export function RemoveTracksFromPlaylist(playlistID: number, trackIDs: number[] | null): $CancellablePromise<void> {
return $Call.ByID(2519855654, playlistID, trackIDs);
}
/**
* RenamePlaylist renames a playlist and updates its M3U8 file.
*/
export function RenamePlaylist(playlistID: number, newName: string): $CancellablePromise<void> {
return $Call.ByID(1835045264, playlistID, newName);
}
/**
* RepopulateFromM3U re-imports tracks for playlists that have zero
* playlist_tracks rows but still have a corresponding M3U8 file.
* This recovers from a FullRescan that deleted playlist tracks
* before the ON DELETE SET NULL fix was in place. Each M3U8 entry
* is resolved against the audio_files table; unresolved entries
* become phantom tracks with metadata preserved from the M3U8.
*/
export function RepopulateFromM3U(): $CancellablePromise<void> {
return $Call.ByID(3399290718);
}
/**
* ResolvePhantomTracks replaces phantom entries in a playlist
* with real library tracks. The matches map keys are phantom
* absolute paths and values are resolved absolute paths.
*/
export function ResolvePhantomTracks(playlistID: number, matches: { [_ in string]?: string } | null): $CancellablePromise<void> {
return $Call.ByID(1239473037, playlistID, matches);
}
/**
* ResolvePhantomTracksAfterScan re-links phantom playlist tracks
* whose files now exist in the library. It iterates each playlist
* that has phantoms, reads its M3U8 file, resolves each entry
* against the current audio_files table using multi-root path
* resolution, and updates matching phantom playlist_tracks. This
* handles both pre-existing phantoms (created before migration 7,
* with NULL phantom_file_path) and new ones.
*/
export function ResolvePhantomTracksAfterScan(): $CancellablePromise<void> {
return $Call.ByID(3836161100);
}
/**
* RestoreAllPlaylists restores playlist tracks from M3U8 files.
* This is called after a full library rescan to repopulate
* playlist_tracks from the surviving M3U8 files.
*/
export function RestoreAllPlaylists(): $CancellablePromise<void> {
return $Call.ByID(453794624);
}
/**
* SearchLibrary searches the entire library by a free-text query
* for manual phantom resolution.
*/
export function SearchLibrary(query: string): $CancellablePromise<$models.CandidateTrack[] | null> {
return $Call.ByID(3912116995, query);
}
/**
* SetFavoritesConfig sets the provider used to read and write
* the default-playlist configuration.
*/
export function SetFavoritesConfig(provider: $models.FavoritesConfigProvider): $CancellablePromise<void> {
return $Call.ByID(2117418507, provider);
}
/**
* ToggleDefaultPlaylistTrack adds or removes a single track
* from the default playlist. Returns true if the track is now
* in the playlist (was added), false if it was removed.
*/
export function ToggleDefaultPlaylistTrack(filePath: string): $CancellablePromise<boolean> {
return $Call.ByID(450278306, filePath);
}
/**
* UpdateSmartPlaylistRules updates the rule set for an existing
* smart playlist. Returns an error if the playlist does not exist
* or is not a smart playlist.
*/
export function UpdateSmartPlaylistRules(playlistID: number, rulesJSON: string): $CancellablePromise<void> {
return $Call.ByID(2746792411, playlistID, rulesJSON);
}