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:
@@ -1,5 +1,5 @@
|
||||
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||
import type { library } from '@go/models';
|
||||
import type * as library from '@go/library/models.js';
|
||||
import { libraryStore } from '../library-store';
|
||||
|
||||
type ViewName = 'tracks' | 'albums' | 'artists' | 'genres';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||
import type { playlist } from '@go/models';
|
||||
import type * as playlist from '@go/playlist/models.js';
|
||||
import { playlistStore } from '../playlist-store';
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,8 +19,8 @@ import {
|
||||
StartDownload,
|
||||
TestProvider,
|
||||
UpdateProvider,
|
||||
} from '@go/download/Service';
|
||||
import type { download } from '@go/models';
|
||||
} from '@go/download/service.js';
|
||||
import type * as download from '@go/download/models.js';
|
||||
import { Events } from '../events';
|
||||
|
||||
export type DownloadCandidate = download.Candidate;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ToggleDefaultPlaylistTrack,
|
||||
AddToDefaultPlaylist,
|
||||
RemoveFromDefaultPlaylist,
|
||||
} from '@go/playlist/Service';
|
||||
} from '@go/playlist/service.js';
|
||||
import {
|
||||
GetFavoritesIconStyle,
|
||||
GetFavoritesPlaylistID,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
SetFavoritesIconStyle,
|
||||
SetFavoritesPlaylistID,
|
||||
SetPinDefaultPlaylist,
|
||||
} from '@go/config/Config';
|
||||
} from '@go/config/config.js';
|
||||
import { Events } from '../events';
|
||||
import { describeError } from '@utils/describe-error';
|
||||
import { notificationStore } from './notification-store';
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
CancelJob,
|
||||
DismissJob,
|
||||
ClearFinishedJobs,
|
||||
} from '@go/jobs/Service';
|
||||
import type { jobs } from '@go/models';
|
||||
} from '@go/jobs/service.js';
|
||||
import type * as jobs from '@go/jobs/models.js';
|
||||
import { Events } from '../events';
|
||||
|
||||
export type Job = jobs.Job;
|
||||
|
||||
@@ -11,8 +11,9 @@ import {
|
||||
GetAllGenresWithCountsByLibrary,
|
||||
GetAlbumsByArtistByLibrary,
|
||||
GetAllLibrariesWithTrackCounts,
|
||||
} from '@go/library/Library';
|
||||
import type { library } from '@go/models';
|
||||
} from '@go/library/library.js';
|
||||
import type * as library from '@go/library/models.js';
|
||||
import { list } from '@utils/binding';
|
||||
import { Events } from '../events';
|
||||
|
||||
type ViewName = 'tracks' | 'albums' | 'artists' | 'genres';
|
||||
@@ -231,7 +232,7 @@ class LibraryStore {
|
||||
|
||||
return this.track(
|
||||
'tracks',
|
||||
id !== null ? GetAllTracksByLibrary(id) : GetAllTracks(),
|
||||
list(id !== null ? GetAllTracksByLibrary(id) : GetAllTracks()),
|
||||
(tracks) => {
|
||||
this.tracks = tracks;
|
||||
},
|
||||
@@ -252,7 +253,7 @@ class LibraryStore {
|
||||
|
||||
return this.track(
|
||||
'albums',
|
||||
id !== null ? GetAllAlbumsByLibrary(id) : GetAllAlbums(),
|
||||
list(id !== null ? GetAllAlbumsByLibrary(id) : GetAllAlbums()),
|
||||
(albums) => {
|
||||
this.albums = albums;
|
||||
},
|
||||
@@ -273,7 +274,7 @@ class LibraryStore {
|
||||
|
||||
return this.track(
|
||||
'artists',
|
||||
id !== null ? GetAllArtistsByLibrary(id) : GetAllArtists(),
|
||||
list(id !== null ? GetAllArtistsByLibrary(id) : GetAllArtists()),
|
||||
(artists) => {
|
||||
this.artists = artists;
|
||||
},
|
||||
@@ -294,9 +295,11 @@ class LibraryStore {
|
||||
|
||||
return this.track(
|
||||
'genres',
|
||||
id !== null
|
||||
? GetAllGenresWithCountsByLibrary(id)
|
||||
: GetAllGenresWithCounts(),
|
||||
list(
|
||||
id !== null
|
||||
? GetAllGenresWithCountsByLibrary(id)
|
||||
: GetAllGenresWithCounts(),
|
||||
),
|
||||
(genres) => {
|
||||
this.genres = genres;
|
||||
},
|
||||
@@ -309,9 +312,11 @@ class LibraryStore {
|
||||
): Promise<library.Album[]> {
|
||||
const id = this.selectedLibraryIdValue;
|
||||
|
||||
return id !== null
|
||||
? GetAlbumsByArtistByLibrary(artistID, id)
|
||||
: GetAlbumsByArtist(artistID);
|
||||
return list(
|
||||
id !== null
|
||||
? GetAlbumsByArtistByLibrary(artistID, id)
|
||||
: GetAlbumsByArtist(artistID),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,8 +415,7 @@ class LibraryStore {
|
||||
return this.libraries;
|
||||
}
|
||||
|
||||
const libs =
|
||||
await GetAllLibrariesWithTrackCounts();
|
||||
const libs = await list(GetAllLibrariesWithTrackCounts());
|
||||
|
||||
this.libraries = libs;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventsOn } from '@runtime/runtime';
|
||||
import { Events } from '../events';
|
||||
import * as Player from '@go/player/Player';
|
||||
import * as Player from '@go/player/player.js';
|
||||
import { notificationStore } from './notification-store';
|
||||
|
||||
/** The region `<inline-notice>` renders these in: the player bar. */
|
||||
|
||||
@@ -3,8 +3,8 @@ import {
|
||||
GetAllPlaylists,
|
||||
GetAllPlaylistsWithTracks,
|
||||
GetPlaylistTracks,
|
||||
} from '@go/playlist/Service';
|
||||
import type { playlist } from '@go/models';
|
||||
} from '@go/playlist/service.js';
|
||||
import type * as playlist from '@go/playlist/models.js';
|
||||
import { Events } from '../events';
|
||||
|
||||
type Subscriber = () => void;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventsOn } from '@runtime/runtime';
|
||||
import { Events } from '../events';
|
||||
import * as Queue from '@go/queue/Queue';
|
||||
import * as Queue from '@go/queue/queue.js';
|
||||
|
||||
// Types
|
||||
export interface QueueTrack {
|
||||
|
||||
@@ -4,8 +4,9 @@ import {
|
||||
SetShortcut,
|
||||
SetShortcuts,
|
||||
ResetShortcuts,
|
||||
} from '@go/config/Config';
|
||||
} from '@go/config/config.js';
|
||||
import { Events } from '../events';
|
||||
import { dictByName } from '@utils/binding';
|
||||
|
||||
export interface ShortcutsState {
|
||||
bindings: Map<string, string>; // action → key combo
|
||||
@@ -56,7 +57,7 @@ class ShortcutsStore {
|
||||
|
||||
private async loadFromBackend(): Promise<void> {
|
||||
try {
|
||||
const raw = await GetShortcuts();
|
||||
const raw = await dictByName(GetShortcuts());
|
||||
this.state = {
|
||||
bindings: new Map(Object.entries(raw)),
|
||||
loaded: true,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
GetThemeBackgroundShade,
|
||||
SetThemeAccentColor,
|
||||
SetThemeBackgroundShade,
|
||||
} from '@go/config/Config';
|
||||
} from '@go/config/config.js';
|
||||
import { Events } from '../events';
|
||||
|
||||
export type BackgroundShade = 'darker' | 'dark' | 'light';
|
||||
|
||||
@@ -2,10 +2,11 @@ import { EventsOn } from '@runtime/runtime';
|
||||
import {
|
||||
GetTrackListColumns,
|
||||
SetTrackListColumns,
|
||||
} from '@go/config/Config';
|
||||
import { tracklist } from '@go/models';
|
||||
} from '@go/config/config.js';
|
||||
import * as tracklist from '@go/tracklist/models.js';
|
||||
import { Events } from '../events';
|
||||
import { DEFAULT_COLUMN_IDS } from '@components/track-list/columns';
|
||||
import { list } from '@utils/binding';
|
||||
|
||||
export interface TrackListState {
|
||||
/** Ordered list of visible column IDs. */
|
||||
@@ -47,12 +48,10 @@ class TrackListStore {
|
||||
|
||||
private async loadFromBackend(): Promise<void> {
|
||||
try {
|
||||
const columns = await GetTrackListColumns();
|
||||
const columns = await list(GetTrackListColumns());
|
||||
|
||||
this.update({
|
||||
columnIds: columns.map(
|
||||
(c: tracklist.Column) => c.id,
|
||||
),
|
||||
columnIds: columns.map((c) => c.id),
|
||||
});
|
||||
} catch {
|
||||
// Use defaults on failure.
|
||||
@@ -72,12 +71,9 @@ class TrackListStore {
|
||||
// ===============================================================
|
||||
|
||||
async setColumns(columnIds: string[]): Promise<void> {
|
||||
const columns = columnIds.map((id) => {
|
||||
const col = new tracklist.Column();
|
||||
col.id = id;
|
||||
|
||||
return col;
|
||||
});
|
||||
const columns: tracklist.Column[] = columnIds.map((id) => ({
|
||||
id: id as tracklist.ColumnID,
|
||||
}));
|
||||
|
||||
await SetTrackListColumns(columns);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user