Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified crates/rompatch-gui/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified crates/rompatch-gui/icons/128x128@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified crates/rompatch-gui/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions crates/rompatch-gui/icons/cartridge-source.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added crates/rompatch-gui/icons/icon-source.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added crates/rompatch-gui/icons/icon.icns
Binary file not shown.
Binary file modified crates/rompatch-gui/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions crates/rompatch-gui/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,19 @@ pub fn library_reveal(app: AppHandle, entry_id: String, target: RevealTarget) ->
library::reveal_in_finder(&path)
}

#[tauri::command]
pub fn library_reveal_rom(app: AppHandle, rom_hash: String) -> GuiResult<()> {
let root = resolve_root(&app)?;
let path = library::rom_path_for(&root, &rom_hash)?;
if !path.exists() {
return Err(GuiError::Library(format!(
"file no longer exists: {}",
path.display()
)));
}
library::reveal_in_finder(&path)
}

#[tauri::command]
pub fn library_delete_entry(app: AppHandle, entry_id: String) -> GuiResult<()> {
let root = resolve_root(&app)?;
Expand Down
1 change: 1 addition & 0 deletions crates/rompatch-gui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub fn run() {
commands::library_verify,
commands::library_reapply,
commands::library_reveal,
commands::library_reveal_rom,
commands::library_delete_entry,
commands::library_delete_rom,
commands::library_export,
Expand Down
4 changes: 2 additions & 2 deletions crates/rompatch-gui/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"windows": [
{
"title": "Rom Library",
"width": 900,
"height": 680,
"width": 1035,
"height": 780,
"minWidth": 720,
"minHeight": 520,
"resizable": true,
Expand Down
28 changes: 26 additions & 2 deletions crates/rompatch-gui/ui/index.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
<!doctype html>
<html lang="en" class="dark">
<html lang="en" data-theme="dark" data-accent="amber">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Rom Library</title>
<script>
// Resolve theme + accent before React mounts so the first paint
// matches the user's preference. Keep this in sync with
// src/lib/theme.ts (same storage keys + resolver rules).
(function () {
try {
var t = localStorage.getItem('rom.theme');
var resolved =
t === 'light' || t === 'dark'
? t
: window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
document.documentElement.setAttribute('data-theme', resolved);
var a = localStorage.getItem('rom.accent');
if (a === 'amber' || a === 'sage' || a === 'coral' || a === 'indigo') {
document.documentElement.setAttribute('data-accent', a);
}
} catch (_) {
/* localStorage may be disabled; keep the SSR defaults. */
}
})();
</script>
</head>
<body class="bg-bg text-fg antialiased">
<body class="bg-bg text-text antialiased">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
Expand Down
3 changes: 3 additions & 0 deletions crates/rompatch-gui/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@fontsource/geist": "5.2.8",
"@fontsource/instrument-serif": "5.2.8",
"@fontsource/jetbrains-mono": "5.2.8",
"@tauri-apps/api": "2.11.0",
"@tauri-apps/plugin-dialog": "2.7.1",
"@tauri-apps/plugin-process": "2.3.1",
Expand Down
24 changes: 24 additions & 0 deletions crates/rompatch-gui/ui/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 71 additions & 3 deletions crates/rompatch-gui/ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { getCurrentWindow } from '@tauri-apps/api/window';
import { ApplyPanel } from './components/ApplyPanel';
import { BrowsePage } from './components/BrowsePage';
import { LibraryPage } from './components/LibraryPage';
import { PreferencesModal } from './components/PreferencesModal';
import { RomDetail } from './components/RomDetail';
import { Sidebar } from './components/Sidebar';
import type { Page } from './components/Sidebar';
import { SidebarToggle } from './components/SidebarToggle';
import { ToastProvider } from './components/Toast';
import { UpdateBanner } from './components/UpdateBanner';
import {
LibraryCtx,
useLibraryProviderValue,
} from './lib/library';
import { useAccent, useTheme } from './lib/theme';

// Wire up window dragging for elements marked with data-tauri-drag-region.
// Tauri ships a built-in handler, but it can miss elements that are nested
Expand Down Expand Up @@ -41,12 +49,51 @@ function useWindowDrag() {
export function App() {
const [sidebarOpen, setSidebarOpen] = useState(true);
const [page, setPage] = useState<Page>('library');
const [prefsOpen, setPrefsOpen] = useState(false);
const [openRomId, setOpenRomId] = useState<string | null>(null);
const [openPatchId, setOpenPatchId] = useState<string | null>(null);

// Theme + accent hooks apply `data-theme` / `data-accent` to <html>
// and stay in sync with system pref while in auto mode. We hold onto
// the setters here so the Preferences modal can drive them.
const { theme, setTheme } = useTheme();
const { accent, setAccent } = useAccent();
useWindowDrag();

// Shared library state — Sidebar count + LibraryPage list read from
// the same fetch + refresh callback.
const library = useLibraryProviderValue();

const openRom = useMemo(() => {
if (!openRomId || library.state.status !== 'ready') return null;
return library.state.roms.find((r) => r.id === openRomId) ?? null;
}, [openRomId, library.state]);

const openPatch = useMemo(() => {
if (!openRom || !openPatchId) return null;
return openRom.patches.find((p) => p.id === openPatchId) ?? null;
}, [openRom, openPatchId]);

// Close the slide-over when switching primary views.
useEffect(() => {
setOpenRomId(null);
setOpenPatchId(null);
}, [page]);

function handleNavigate(romId: string, patchId: string | null) {
setOpenRomId(romId);
setOpenPatchId(patchId);
}

function handleCloseDetail() {
setOpenRomId(null);
setOpenPatchId(null);
}

return (
<ToastProvider>
<div className="flex h-full bg-bg text-fg font-sans overflow-hidden relative">
<LibraryCtx.Provider value={library}>
<div className="flex h-full bg-bg text-text font-sans overflow-hidden relative">
<SidebarToggle
open={sidebarOpen}
onToggle={() => setSidebarOpen((v) => !v)}
Expand All @@ -55,13 +102,34 @@ export function App() {
open={sidebarOpen}
currentPage={page}
onPageChange={setPage}
onOpenPreferences={() => setPrefsOpen(true)}
/>
<main className="flex-1 flex flex-col overflow-hidden relative">
<div data-tauri-drag-region className="h-12 shrink-0" />
<UpdateBanner />
{page === 'library' ? <LibraryPage /> : <ApplyPanel />}
{page === 'library' && <LibraryPage onOpen={handleNavigate} />}
{page === 'patcher' && <ApplyPanel />}
{page === 'browse' && <BrowsePage />}
{openRom && (
<RomDetail
rom={openRom}
patch={openPatch}
onClose={handleCloseDetail}
onNavigate={handleNavigate}
onRefresh={library.refresh}
/>
)}
</main>
</div>
<PreferencesModal
open={prefsOpen}
onClose={() => setPrefsOpen(false)}
theme={theme}
setTheme={setTheme}
accent={accent}
setAccent={setAccent}
/>
</LibraryCtx.Provider>
</ToastProvider>
);
}
Loading
Loading