Built with

React 19 Core

The desktop UI is React 19 inside Tauri webviews. Tabs replace a router; Zustand picks the active surface.

main.tsx checks getCurrentWindow().label and mounts either App, MiniPlayer, or NotifyOverlayApp. Same React 19 + StrictMode entry for each webview label.

App.tsx is the shell: sidebar nav sets activeTab in Zustand (downloader, explorer, media, player, settings). AnimatePresence swaps the major views. Explorer is special: the tab body is mostly a positioned host div because the child webview paints on top.

Heavy feature UIs are split components. Downloader logic sits in useDownloaderView.ts with UI in DownloaderView.tsx and DownloadJobQueuePanel.tsx. Player splits outer PlayerView (nullable playingFile guard) from inner PlayerViewWithFile so hooks never run without a file. Settings uses nested collapsible trees for SponsorBlock and playback prefs.

Mini player is a separate bundle with duplicated playback UI state (progress, hover, layout mode). It listens for Tauri events instead of subscribing to the main store. Fast refresh works under npm run tauri dev the same as plain Vite.

In the repo


window.addEventListener("beforeunload", clearRuforgeNotificationDismissTimers);
if (import.meta.hot) {
  import.meta.hot.dispose(() => {
    clearRuforgeNotificationDismissTimers();
  });
}

const rootEl = document.getElementById("root") as HTMLElement;
const label = getCurrentWindow().label;

syncBootNavMode();

  useEffect(() => {
    if (navMode === "music" || shellBlocked) return;
    void (async () => {
      try {
        await invoke("eval_in_webview", {
          label: MUSIC_EXPLORE_WEBVIEW_LABEL,
          script: EXPLORER_PAUSE_MEDIA_SCRIPT,
        });
      } catch {
        /* not mounted */
      }
      const wv = await getEmbeddedExplorerWebview(MUSIC_EXPLORE_WEBVIEW_LABEL);
      if (wv) {
        try {
          await wv.hide();
        } catch {
          /* ok */
        }
      }
    })();
  }, [navMode, shellBlocked]);

  useEffect(() => {
    if (activeTab !== "explorer" || shellBlocked) return;
    let alive = true;
    const tick = async () => {
      try {
        const u = await invoke<string>("get_embedded_explorer_webview_url");
        if (alive) setLastExplorerUrl(u);
      } catch {
        /* Embedded explorer webview not mounted yet */
      }
    };
    void tick();
    const id = window.setInterval(() => void tick(), 800);
    return () => {
      alive = false;
      clearInterval(id);
    };
  }, [activeTab, shellBlocked, setLastExplorerUrl]);

  useEffect(() => {
    if (activeTab !== "settings" || shellBlocked) {
      settingsTabMorph.set(0);
      setSettingsMorphAmount(0);
      setSettingsScrollable(false);
      return;
    }
              </div>
            </div>
          </motion.div>
        )}
      </div>

      {!audioOnly && (
        <SlidingCommentsDrawer
          isOpen={commentsPanelOpen}

Where it shows up

  • main.tsx webview label routing
  • App.tsx tab shell, window chrome, event listeners
  • PlayerView.tsx, MediaView.tsx, DownloaderView.tsx, SettingsView.tsx
  • MiniPlayer.tsx second-window playback UI