Skip to content
Draft
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

**What makes Jacare special:**
- 🎮 **All-in-one solution** – Browse, search, download ROMs without switching between tools
- 🎯 **Big Picture Mode** – Controller-friendly full-screen interface for TVs and couch gaming
- ⏸️ **Persistent download management** – Pause and resume downloads seamlessly, even after closing and reopening the application
- 🎨 **Customizable themes** – Personalize your experience with a variety of themes to suit your preferences
- ⚡ **Ultra-responsive web-based UI** – Enjoy a fast, smooth interface accessible through your browser or desktop app
Expand All @@ -19,9 +20,11 @@
> Want details?
> - 📚 **Developer guide:** Head to [`docs/README.md`](docs/README.md) for the full technical rundown.
> - 😀 **Friendly guide:** Open [`docs/user/README.md`](docs/user/README.md) for a non-technical walkthrough.
> - 🎮 **Big Picture Mode:** Check out [`docs/BIG_PICTURE_MODE.md`](docs/BIG_PICTURE_MODE.md) for controller setup and usage.

## Why Jacare? 🐊
- **One app for everything:** Browse, enrich, and launch ROMs without juggling separate tools.
- **Couch gaming ready:** Big Picture Mode transforms your PC into a console-like experience with full controller support.
- **Local-first with cloud search:** Metadata is pulled from [Crocdb](https://api.crocdb.net) while your collection, cache, and settings remain on disk.
- **Built for speed:** Background jobs, SSE updates, and caching cut down on repetitive scraping.
- **Works online or offline:** Cached search and entry data keep your library usable even when you lose connectivity.
Expand Down
4 changes: 4 additions & 0 deletions apps/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import DownloadsPage from "./pages/DownloadsPage";
import SettingsPage from "./pages/SettingsPage";
import GameDetailPage from "./pages/GameDetailPage";
import LibraryItemDetailPage from "./pages/LibraryItemDetailPage";
import BigPictureModePage from "./pages/BigPictureModePage";
import BigPictureDownloadsPage from "./pages/BigPictureDownloadsPage";
import { WelcomeView, shouldShowWelcome } from "./components/WelcomeView";
// useMemo imported above

Expand All @@ -24,6 +26,8 @@ function AppRoutes() {
<Route path="/queue" element={<QueuePage />} />
<Route path="/downloads" element={<DownloadsPage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/big-picture" element={<BigPictureModePage />} />
<Route path="/big-picture/downloads" element={<BigPictureDownloadsPage />} />
<Route path="/game/:slug" element={<GameDetailPage />} />
</Routes>
{state?.backgroundLocation && (
Expand Down
169 changes: 169 additions & 0 deletions apps/web/src/components/OnScreenKeyboard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
.onscreen-keyboard {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--card);
border-top: 2px solid var(--border);
padding: 24px;
z-index: 1000;
box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.3);
}

.keyboard-input-wrapper {
margin-bottom: 20px;
}

.keyboard-input {
background: var(--bg);
border: 2px solid var(--border);
border-radius: 8px;
padding: 16px 20px;
font-size: 24px;
font-family: "IBM Plex Sans", monospace;
color: var(--ink);
min-height: 60px;
display: flex;
align-items: center;
position: relative;
}

.keyboard-placeholder {
color: var(--ink-muted);
opacity: 0.5;
}

.keyboard-cursor {
display: inline-block;
width: 2px;
height: 28px;
background: var(--accent);
margin-left: 2px;
animation: blink 1s infinite;
}

@keyframes blink {
0%, 49% { opacity: 1; }
50%, 100% { opacity: 0; }
}

.keyboard-keys {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}

.keyboard-row {
display: flex;
justify-content: center;
gap: 8px;
}

.keyboard-key {
min-width: 60px;
height: 60px;
background: var(--bg);
border: 2px solid var(--border);
border-radius: 8px;
font-size: 20px;
font-weight: 600;
color: var(--ink);
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
}

.keyboard-key:hover {
background: var(--bg-alt);
border-color: var(--accent);
transform: translateY(-2px);
}

.keyboard-key:active {
transform: translateY(0);
background: var(--accent);
color: var(--bg);
}

.keyboard-key.focused {
border-color: var(--accent);
border-width: 3px;
background: var(--accent);
color: var(--bg);
box-shadow: 0 0 16px var(--accent);
}

.keyboard-key.special {
min-width: 80px;
font-size: 16px;
}

.keyboard-key.space {
min-width: 200px;
}

.keyboard-key.submit {
background: var(--accent);
color: var(--bg);
border-color: var(--accent);
}

.keyboard-key.submit:hover {
background: var(--accent-hover);
border-color: var(--accent-hover);
}

.keyboard-key.active {
background: var(--accent);
color: var(--bg);
}

.keyboard-hints {
display: flex;
justify-content: center;
gap: 24px;
font-size: 14px;
color: var(--ink-muted);
opacity: 0.7;
}

.keyboard-hints span {
padding: 4px 12px;
background: var(--bg);
border-radius: 4px;
}

/* Big Picture Mode specific overrides */
[data-big-picture="true"] .onscreen-keyboard {
padding: 32px;
}

[data-big-picture="true"] .keyboard-input {
font-size: 32px;
padding: 20px 24px;
min-height: 80px;
}

[data-big-picture="true"] .keyboard-key {
min-width: 70px;
height: 70px;
font-size: 24px;
}

[data-big-picture="true"] .keyboard-key.special {
min-width: 100px;
font-size: 18px;
}

[data-big-picture="true"] .keyboard-key.space {
min-width: 280px;
}

[data-big-picture="true"] .keyboard-hints {
font-size: 16px;
gap: 32px;
}
Loading
Loading