Baseline: full tree snapshot after git repository loss
The previous .git directory was found gutted (only info/ remained), so history is unrecoverable locally. This commit restores version control with the tree exactly as it stands: the working app, the preview-tracks launcher on the artist hero, the scrollable-track-previews design bundle (docs + approved mockups), and the Phase 0 validation artifacts.
This commit is contained in:
commit
a667b4b28e
161 files changed
+53170
No files matched your search
+154
@@ -0,0 +1,154 @@
|
||||
// Thin wrapper over Tauri IPC so components never import tauri directly.
|
||||
import { invoke as tauriInvoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { mockInvoke } from "./mockLibrary.js";
|
||||
|
||||
// mockInvoke is inert (returns undefined) unless localStorage.mockLibrary === "1".
|
||||
const invoke = (cmd, args) => mockInvoke(cmd, args) ?? tauriInvoke(cmd, args);
|
||||
|
||||
// Artist-page payloads are keyed by a Deezer id all the way through the
|
||||
// backend. Reject a mismatched response at the IPC boundary so a provider,
|
||||
// cache, or future command regression cannot populate another artist's page.
|
||||
// String comparison deliberately accepts route-string ids and numeric Tauri
|
||||
// ids as the same identity without coercing the u64 argument sent to Rust.
|
||||
const artistIdKey = (value) => (value == null ? "" : String(value).trim());
|
||||
const sameArtistId = (left, right) => {
|
||||
const leftKey = artistIdKey(left);
|
||||
return !!leftKey && leftKey === artistIdKey(right);
|
||||
};
|
||||
|
||||
const checkedArtistPayload = (command, artistId) =>
|
||||
invoke(command, { artistId }).then((payload) => {
|
||||
if (!sameArtistId(payload?.artist?.id, artistId)) {
|
||||
throw new Error(`${command} returned a mismatched artist identity`);
|
||||
}
|
||||
return payload;
|
||||
});
|
||||
|
||||
const checkedArtistIdentity = (artistId, artist) =>
|
||||
invoke("catalog_artist_identity", { artistId, artist }).then((payload) => {
|
||||
if (!sameArtistId(payload?.artist_id ?? payload?.artistId, artistId)) {
|
||||
throw new Error("catalog_artist_identity returned a mismatched artist identity");
|
||||
}
|
||||
return payload;
|
||||
});
|
||||
|
||||
const checkedAlbumPayload = (albumId) =>
|
||||
invoke("catalog_album", { albumId }).then((payload) => {
|
||||
if (!sameArtistId(payload?.album?.id, albumId)) {
|
||||
throw new Error("catalog_album returned a mismatched album identity");
|
||||
}
|
||||
return payload;
|
||||
});
|
||||
|
||||
export const appStatus = () => invoke("app_status");
|
||||
export const getCredentials = () => invoke("get_credentials");
|
||||
export const connectAndLogin = (username, password, remember) =>
|
||||
invoke("connect_and_login", { username, password, remember });
|
||||
export const connectStored = () => invoke("connect_stored");
|
||||
export const disconnect = () => invoke("disconnect");
|
||||
export const startSearch = (query) => invoke("start_search", { query });
|
||||
export const excludedSearchPhrases = () => invoke("excluded_search_phrases");
|
||||
export const stopSearch = (token) => invoke("stop_search", { token });
|
||||
export const downloadFile = (username, path, size) =>
|
||||
invoke("download_file", { username, path, size });
|
||||
export const copyStreamToFolder = (files, folder) =>
|
||||
invoke("copy_stream_to_folder", { files, folder });
|
||||
export const saveToLibrary = (files) => invoke("save_to_library", { files });
|
||||
export const cancelDownload = (id) => invoke("cancel_download", { id });
|
||||
export const showInFolder = (path) => invoke("show_in_folder", { path });
|
||||
export const refreshArtistArt = () => invoke("refresh_artist_art");
|
||||
export const playStream = (username, path, size, duration) =>
|
||||
invoke("play_stream", { username, path, size, duration });
|
||||
export const playLocal = (path, duration) => invoke("play_local", { path, duration });
|
||||
export const preloadLocal = (path, duration) => invoke("preload_local", { path, duration });
|
||||
export const playerControl = (action, value, rate) =>
|
||||
invoke("player_control", { action, value: value ?? null, rate: rate ?? null });
|
||||
export const waveform = (path, buckets) => invoke("waveform", { path, buckets });
|
||||
export const libraryAlbums = () => invoke("library_albums");
|
||||
export const albumTracks = (albumId) => invoke("album_tracks", { albumId });
|
||||
export const recordPlay = (artist, title) => invoke("record_play", { artist, title });
|
||||
export const addPlaySeconds = (playId, secs) => invoke("add_play_seconds", { playId, secs });
|
||||
export const artistPlayStats = (artist) => invoke("artist_play_stats", { artist });
|
||||
// The WebView blocks target="_blank"; external links open via the backend.
|
||||
export const openExternal = (url) => invoke("open_external", { url });
|
||||
export const albumArt = (path) => invoke("album_art", { path });
|
||||
export const refreshAlbumArt = (albumId) => invoke("refresh_album_art", { albumId });
|
||||
export const refreshAllAlbumArt = () => invoke("refresh_all_album_art");
|
||||
export const getSettings = () => invoke("get_settings");
|
||||
export const setSettings = (patch) => invoke("set_settings", patch);
|
||||
export const forgetCredentials = () => invoke("forget_credentials");
|
||||
export const pickFolder = () => invoke("pick_folder");
|
||||
export const listPlaylists = () => invoke("list_playlists");
|
||||
export const createPlaylist = (name) => invoke("create_playlist", { name });
|
||||
export const deletePlaylist = (id) => invoke("delete_playlist", { id });
|
||||
export const playlistTracks = (id) => invoke("playlist_tracks", { id });
|
||||
export const addToPlaylist = (playlistId, tracks) =>
|
||||
invoke("add_to_playlist", { playlistId, tracks });
|
||||
export const removeFromPlaylist = (playlistId, position) =>
|
||||
invoke("remove_from_playlist", { playlistId, position });
|
||||
export const catalogSearch = (query) => invoke("catalog_search", { query });
|
||||
export const catalogArtist = (artistId) => checkedArtistPayload("catalog_artist", artistId);
|
||||
export const catalogArtistArt = (artistId) =>
|
||||
checkedArtistPayload("catalog_artist_art", artistId);
|
||||
export const catalogArtistLogos = (artistId) => invoke("catalog_artist_logos", { artistId });
|
||||
// Deferred MusicBrainz pass (socials + MB-only releases). Rate-limited at
|
||||
// 1 req/s upstream, so it is fetched after the page renders, never before.
|
||||
export const catalogArtistSocials = (artistId) =>
|
||||
invoke("catalog_artist_socials", { artistId });
|
||||
export const catalogArtistBio = (artistId, artist) =>
|
||||
invoke("catalog_artist_bio", { artistId: artistId ?? null, artist });
|
||||
export const catalogArtistIdentity = (artistId, artist) =>
|
||||
checkedArtistIdentity(artistId, artist);
|
||||
export const catalogCachedCoverLogo = (artistId) =>
|
||||
invoke("catalog_cached_cover_logo", { artistId });
|
||||
export const catalogCoverLogo = (artistId, artist, covers) =>
|
||||
invoke("catalog_cover_logo", { artistId, artist, covers });
|
||||
export const catalogArtistHeroSeed = (artistId) =>
|
||||
invoke("catalog_artist_hero_seed", { artistId });
|
||||
export const catalogArtistOverview = (artistId) =>
|
||||
checkedArtistPayload("catalog_artist_overview", artistId);
|
||||
export const pickArtistHero = (artistId) => invoke("pick_artist_hero", { artistId });
|
||||
export const clearArtistHero = (artistId) => invoke("clear_artist_hero", { artistId });
|
||||
export const catalogRelated = (artistId) => invoke("catalog_related", { artistId });
|
||||
export const catalogAlbum = (albumId) => checkedAlbumPayload(albumId);
|
||||
export const catalogAlbumInfo = (artistId, artist, title) =>
|
||||
invoke("catalog_album_info", { artistId: artistId ?? null, artist, title });
|
||||
export const catalogArt = (url) => invoke("catalog_art", { url });
|
||||
export const catalogCover = (artistId, artist, title, cover) =>
|
||||
invoke("catalog_cover", { artistId: artistId ?? null, artist, title, cover });
|
||||
export const invalidateCatalogCover = (artistId, artist, title, cover) =>
|
||||
invoke("invalidate_catalog_cover", { artistId: artistId ?? null, artist, title, cover });
|
||||
export const catalogHeroPreview = (url) => invoke("catalog_hero_preview", { url });
|
||||
export const catalogHeroGpuSource = (url, plan = null) =>
|
||||
invoke("catalog_hero_gpu_source", { url, plan });
|
||||
export const catalogCachedHeroArt = (url) => invoke("catalog_cached_hero_art", { url });
|
||||
export const catalogHeroArt = (url, plan = null) =>
|
||||
invoke("catalog_hero_art", { url, plan });
|
||||
export const preloadStream = (username, path, size) =>
|
||||
invoke("preload_stream", { username, path, size });
|
||||
export const preloadCachedStream = (username, path, size, duration) =>
|
||||
invoke("preload_cached_stream", { username, path, size, duration });
|
||||
export const streamWaveform = (username, path, size, buckets) =>
|
||||
invoke("stream_waveform", { username, path, size, buckets });
|
||||
|
||||
// Window chrome (the app draws its own titlebar; decorations are off).
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
export const win = {
|
||||
minimize: () => getCurrentWindow().minimize(),
|
||||
toggleMaximize: () => getCurrentWindow().toggleMaximize(),
|
||||
close: () => getCurrentWindow().close(),
|
||||
isMinimized: () => getCurrentWindow().isMinimized(),
|
||||
isFocused: () => getCurrentWindow().isFocused(),
|
||||
isMaximized: () => getCurrentWindow().isMaximized(),
|
||||
// returns Promise<unlisten>
|
||||
onResized: (handler) => getCurrentWindow().onResized(handler),
|
||||
onFocusChanged: (handler) => getCurrentWindow().onFocusChanged(handler),
|
||||
};
|
||||
|
||||
// Subscribe to a backend event; returns an unlisten function.
|
||||
// No-ops outside Tauri (e.g. plain-browser preview).
|
||||
export function onEvent(name, handler) {
|
||||
const un = listen(name, (e) => handler(e.payload)).catch(() => () => {});
|
||||
return () => un.then((f) => f());
|
||||
}
|
||||
Reference in new issue
Block a user