- Store favorites in /data/pldmgr/favorites.txt on backend
- Return FAVORITES and FAVORITES_INITIALIZED in /get_config
- Parse and save FAVORITES in /set_config POST handler
- Automatically update/remove favorites entries on slot delete, replace, and legacy migration
- Sync favorites state from backend in frontend on startup and persist updates
- Include one-time migration from localStorage for existing clients
- Move repository badge to a new line under version and author
- Style with matching badge aesthetics as version pill
- Replace "Manual" badge for local/manual payloads with "Other Source"
- Remove legacy absolute bottom-right placement to prevent overlaps
- Remove frontend/src/locales git submodule and track translation files directly
- Credit JensJ56 for Dutch translation
- Move supported languages and past translator credits to TRANSLATIONS.md
- Document that translations since v0.6.0 are generated via Google Gemini AI
- Add issue reporting link for translation feedback
On clean PS5 systems with no prior apps or games installed, /user/app does not exist yet, causing direct mkdir() on /user/app/<title_id> to fail with ENOENT (errno: 2).
Use recursive mkdir_p to create the required directory tree (/user/app/<title_id>/sce_sys).
Resolves#108
- Remove startup self-update check and chainloading in main.c
- Remove self-update logic and ELF version inspection from repository.c and repository.h
- Neuter /update_check route to return status none
- Remove update banners, notifications, and settings toggle from frontend
- Exclude pldmgr from repository payload listings and block installing pldmgr as a payload
- Add launch guards in HTTP server and autoload worker preventing pldmgr self-execution
- Implement managed slot layout: repo/<id>@<srchash8> and local/<key> with meta.json sidecars
- Decouple user uploads and USB imports from repository matching and updates
- Use stable slot keys for autoload.txt, favorites, launching, and deletion
- Add strict token-based version comparison in C and JS, hiding install button when repo is older
- Support cross-source duplicate handling (keep both vs replace) with conflict modals
- Auto-refresh stale repository caches on install and retry seamlessly
- Implement one-time legacy storage migration on startup (layout version 2)
- Update CUSTOM_REPOSITORIES.md with id field specifications and comparison rules
- Remove deprecated /repository_push and /repository_install_push routes
- Add full test suite for versions, payloadStatus, helpers, and markdown
- Redesign Get Payloads grid into wide 3-column cards with icon on left, version top-right, dynamic title width, max 3-line description, and bottom-right installed checkmark
- Overhaul Payload Details panel with hero header, compact action button, author, repository link, short description, and raw GitHub README rendering with stripped images
- Implement URL hash state persistence and hierarchical history navigation for views and deep payload links (#/payload/:filename?src=:id)
- Add unit test suite for history navigation and route parsing
- Update locales submodule pointer with new detail view translations
- Port history navigation strategy from pkg-manager to prevent PS5 Circle button from exiting browser
- Seed baseline history stack with Dashboard at root entry 0
- Navigate between top-level pages via replaceState so Back always returns directly to Dashboard
- Allow deep sub-views (Sources under Settings, Move from USB under Storage) to pop back to parent views
- Intercept modal/panel closures (confirmations, downloads, logs, payload details, dashboard edit modes) on Back press
- Exit browser via window.close / history.back only when Back is pressed from Dashboard root
This addresses the remaining minor issues outlined in ISSUES.md.
- Extracted remaining hardcoded English strings into i18n locales
- Fixed stale state and implemented lazy-loaded images in PayloadDetailPanel
- Revived 'installed' payload state in StorageHub by no longer filtering them out
- Fixed event propagation bug in dashboard manage mode where clicking anywhere triggered delete
- Hardened C backend 'is_pldmgr_filename' to strictly match pldmgr prefixes, preventing false positives
This addresses the missing UI toggle for the SHOW_UPDATE_NOTIFICATIONS config key.
- Added toggle to SettingsView under Startup section
- Wired toggle state directly to the config file via /set_config
- Included i18n keys for the new settings labels
This addresses the issue where dashboard payloads lacked icons.
Instead of passing the icon through the C backend's payload list, this leverages the existing `RepositoryContext` remote catalog.
- Wrapped the root `App` in `RepositoryProvider` inside `main.jsx`.
- Added `filenameMap` to `RepositoryContext` to build a fast O(1) lookup map of all payload metadata across sources.
- Injected the map data directly into Dashboard `PayloadButton` cards.
This addresses the top two critical issues identified in ISSUES.md.
Backend C Fixes:
- Added `skip_top_level_wrapper()` and `find_next_object_start()` to `repository.c` to correctly parse payloads even when wrapped in a top-level JSON object or containing stringified braces.
- Updated `repository_check_self_update` and `repository_check_repo_update` to return full version, filename, and source metadata required by the frontend.
Frontend React Fixes:
- Updated the `App.jsx` update banner to properly consume the API contract.
- Added a functional `handleApplyUpdate` to load the downloaded self-update elf.
- Added a functional `handleInstallUpdate` to trigger repository installation when a new version is available.
- Removed heavy CSS shadows from payload icons for smoother scrolling
- Implemented dual-element approach for payload name marquee animations
- Re-labeled manage view to 'Remove Payload' when active
This commit completely redesigns how users browse, view, and install payloads.
Backend C Changes:
- Updated C structures to support multiple concurrent payload sources (config.h, repository.h).
- Modified repository.c and config.c to parse and retain multi-source JSON payloads.
- Allowed the HTTP server to serve the dynamically merged JSON sources.
Frontend React Changes:
- Converted StorageHub.jsx into a grid-based "App Shop" catalog layout.
- Replaced the modal payload view with a seamless full-page PayloadDetailPanel.jsx component for extended markdown reading and details.
- Added RepositoryContext.jsx to unify repository fetching across views.
- Styled payload cards with a dynamic marquee animation for overflowing text names (PayloadName.jsx).
- Allowed external icons, with a fallback placeholder for payloads without one.
- Polished padding, margins, and transition effects for spatial controller navigation.
- Removed duplicate versioning strings in payload titles and tweaked dashboard header text.
Introduces a visual indicator to help track payloads launched during the current session.
Backend:
- Create history_mgr.c to track the last 20 launched payload paths
- Log payloads in ps5_launcher.c upon successful launch
- Add /history_list API endpoint to expose the launch history
- Update Makefile
Frontend:
- Fetch and poll /history_list in App.jsx to maintain launch history state
- Add isLaunched prop to PayloadButton.jsx to render a checkmark indicator
Closes#44
- Update `frontend/src/locales` submodule to pull in the latest translations from Crowdin
- Add translators for newly supported languages: German, Persian, and Ukrainian to README
- Fix Crowdin badge URLs for Italian, Portuguese (Brazilian), and Russian which shifted after adding Persian
The app will now automatically detect and use the user's browser language on first load. We can safely do this since only high-quality languages (70%+ translated) are enabled anyway.
Introduces a complete localization system using i18next, enabling the application to be translated into multiple languages by the community via Crowdin.
Key additions:
- Core i18next integration with English as the default base language.
- A language selector in the Settings menu for users to easily switch languages.
- Full UI localization, including support for complex grammar rules (e.g., dynamic pluralization) and responsive text layouts.
- A dedicated `locales` git submodule to seamlessly sync community-driven translations.
- Automatic filtering of incomplete languages (under 70% translated) to ensure a high-quality user experience.
Note:
This implementation currently applies exclusively to the frontend web UI. Native PS5 system notifications (triggered by the backend C code) remain in English and are not yet covered by this localization system.
When a repository payload explicitly defines a version, use it instead of trying to guess the version out of the filename.
Closes#60
Co-authored-by: lucaszhongsj <20980720+lucaszhongsj@users.noreply.github.com>
Sets applicationCategoryType to 65536 so the app appears in the Media tab rather than the Games tab on the PS5 home screen.
Closes#62
Co-authored-by: lucaszhongsj <20980720+lucaszhongsj@users.noreply.github.com>
After suspend/resume, the listening socket becomes stale but the
watchdog only checked for IP changes — if WiFi was already off,
no restart happened and 127.0.0.1:8084 became unreachable.
Force a full MHD stop/start on SIGCONT and also when network is
lost, so loopback access always works.
- Added a dropdown to sort repository payloads by Category (default), Update Time, or Name
- Sorting by Update Time or Name automatically flattens the list (disables category grouping)
- Sort preference is persisted across sessions via `localStorage`
Resolves#47
- Added a search input to the repository view that filters payloads by name, filename, and description
- Searching automatically un-collapses all sources
- Searching automatically disables category grouping to show a flat list of matching results
- Increased MAX_SOURCES to 50 and refactored SourceEntry allocations to the heap
- Added `category` field to payload schema (defaults to "Uncategorized")
- Updated repository UI to group payloads into collapsible category sections
- Auto-expand categories if 10 or fewer payloads are available
- Updated CUSTOM_REPOSITORIES.md
Resolves#30
Fixes#42
If github.com (Sectigo ECC) or its redirects cause mbedTLS to fail
verification due to cross-signed certificate chains or unsupported
ECC curves in the PS5 SDK, curl will now catch CURLE_PEER_FAILED_VERIFICATION
and automatically retry the download without SSL verification.
This is completely safe because repository.c securely verifies the
SHA256 checksum of the downloaded ELF against the trusted payloads.json
metadata before installing it.
Older versions of curl using the mbedTLS backend require the PEM buffer
passed via CURLOPT_CAINFO_BLOB to be explicitly NUL-terminated, with the
length field including the NUL byte. Since the auto-generated
assets_cacert_pem array lacked this terminator, mbedTLS would read past
the buffer bounds, causing sporadic 'SSL peer certificate or SSH remote
key was not OK' errors depending on the memory layout.
This commit dynamically allocates a slightly larger buffer to append the
missing NUL terminator before passing it to curl, ensuring deterministic
certificate parsing across all environments.
Also bumped version to 0.3.2.
Updates the USB import flow to provide explicit buttons for 'Copy to Internal' and 'Move to Internal'. The backend route now accepts a keep_original flag to optionally skip removing the source file.
The `payload_mgr_usb_move` function was only copying the file and leaving the original on the USB drive, despite returning a 'Moved successfully' message. It now correctly deletes the source file to complete the move operation.
The big refactor in 2189ee9 accidentally removed the max_depth constraint on the recursive scanner. This caused SCAN_USB_PAYLOADS to exhaustively scan the entire drive rather than just the root folder, resulting in infinite loading states on the dashboard.
During the recent storage refactoring, `payload_mgr_check_existing` was updated to return a simplified `{"exists": true}` structure. However, the frontend (`App.jsx`) still relied on the `file_exists` and `folder_exists` boolean flags to display the overwrite confirmation modal when a duplicate payload is uploaded. Because those fields were missing, the frontend silently fell through and overwrote the files without asking.
This change restores the original JSON output format for the `/manage:check` endpoint (`file_exists`, `folder_exists`, and `folder_name`), ensuring the overwrite prompt appears again when an uploaded ELF already exists.
The sidebar's expanded/collapsed state is now saved in `localStorage`. This ensures that if the user collapses the sidebar, it stays collapsed after reloading the page or returning to the app later.
Implemented via a `useState` lazy initializer and `useEffect` hook in `App.jsx`.
- Embeds version signature dynamically into ELF
- Scans 'pldmgr' or 'payload-manager' ELFs for version on startup
- Auto-launches updated ELF without infinite loops
- Hides payload manager from UI lists to prevent accidental deletion
- Add HTTP status checks to loadPayload, handleDelete, and performUpload
- Fix autoload polling to continue backing off when sequence is inactive
- Add await to fetchRemote in StorageHub auto-refresh
- Replace index keys with stable identifiers in dynamic lists
Address code review findings:
- Validate USB paths (restrict to /mnt/usbN/, block .., require .elf/.bin)
- Sanitize /loadpayload route (resolve via known scan dirs only)
- Add filename validation to /manage:check endpoint
- Fix autoload config OOB write (swap fgets/line_count check order)
- Escape JSON strings in /log and /get_config responses
- Move json_escape to utils.c as nm_json_escape for reuse
- Replace global response_buffer with per-request malloc + MUST_FREE
- Add alloc_response_buffer helper to deduplicate OOM handling
- Fix overwrite flag being ignored in USB move
- Handle truncated repository JSON with proper error status
- Centralize CORS headers via add_cors_headers helper
- Fix dangling pointer in loadpayload resolved_path scope