{{ message }}
Feature: Let custom modules extend the panel via manifests without editing stock PB sources#3720
Merged
gmt2001 merged 53 commits intoMay 17, 2026
Merged
Conversation
The OAuth and Setup HTTP handlers previously confined their served-from check to <execution_path>/web, while isValidPathWebAuth already understands assets can also live under the Docker _data volume. This commit factors that dual-root check into a new PathValidator.isPathUnderExecutionOrDockerWeb helper and adopts it in both handlers, so panel assets dropped into PhantomBot_data/web are served correctly in Docker installs.
Walks <execution_path>/web/panel/custom/ (and the Docker _data equivalent), parses every <moduleId>/manifest.json, validates and merges nav and cards entries, and serves the result as a single JSON document for the panel to consume. Invalid manifests are warn-logged and skipped so a single broken module cannot break the panel. Also adopts PathValidator.isPathUnderExecutionOrDockerWeb in HTTPPanelAndYTHandler for the same Docker _data web root behavior as the OAuth and Setup handlers.
On panel login, fetches /panel/custom-manifests.json and appends the nav entries into the Extra, Alerts, Giveaways, and Audio submenus declared by their manifests. Inserts a one-time 'CUSTOM' divider above the first community-contributed link in each affected section, with styles injected on demand so a panel with no manifests installed renders no extra UI at all. Switches the [data-folder] click binding in ajaxLoader.js to a delegated handler so the runtime-injected links route correctly through $.loadPage.
Adds a mount point and an injectCustomCards trigger to games.html so the manifest loader's already-shipped card-rendering helpers (buildCardElement, wireCardToggles, wireCardSettings, loadInitialCardStates) actually fire on this page. Cards are appended below the stock cards under a one-time 'Community modules' divider; toggle and cog button render only when the manifest declares scriptPath / settingsFolder respectively.
Fixes an issue caused by the custom cards where disabling/reenabling the custom module doesn't sync with the toggle state of the card (custom card toggle remains in a disabled state regardless of actual state).
Panel-settings-saved when manifest omits wsEvent, pbCustomCardSettingsSaved jQuery + CustomEvent, dispatchBotWsAfterCustomCardSave, $mount.empty(), namespaced toggle handlers, lookupModulesTableValue / modulesDbValueIsEnabled, and initial toggle state from modules.
…hat or bot panel load
… now functions as originally intended
…m/mcawful/PhantomBot into feature/custom-module-enhancement
Collapses four over-engineered manifest features in CustomPanelManifestCollector to keep custom-module authoring straightforward.
Brings the panel-side render/save/details flows in line with the trimmed Java validator contract.
gmt2001
requested changes
May 17, 2026
gmt2001
left a comment
Member
There was a problem hiding this comment.
Prefer using let in all JS var declarations to strictly control scope and prevent global object creation
…m/mcawful/PhantomBot into feature/custom-module-enhancement
Sartharon
reviewed
May 17, 2026
Sartharon
reviewed
May 17, 2026
Sartharon
reviewed
May 17, 2026
Contributor
Additionally: refactor checkIfClientCacheMatches to accept a caller-supplied ETag and share If-None-Match parsing with static files.
Discovered that `If-None-Match` can return string with our without quotations, added logic to normalize by stripping quotations
Contributor
Author
I have some slight integration, but I would like to grow it out. If you want, I could look into integrating that with this PR. |
Contributor
gmt2001
approved these changes
May 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Brief description of the intended change
Purpose: Give custom and community modules a supported way to extend the web panel—sidebar links in the
extra,alerts,giveaways, oraudiosubmenus (via eachnavitem’ssection), Games cards, and optional settings / “more info” UI—by shipping amanifest.jsonnext to their usual Rhino scripts and panel pages, instead of editing PhantomBot’s stock panel sources. The bot collects, validates, and serves those manifests to the browser so installs (including Docker with a data volume) can pick up panel contributions safely after a refresh or reload.New files
PhantomBot/source/com/mcawful/CustomPanelManifestCollector.java
web/panel/custom/**/manifest.json, validate and mergenav/cards, warn on bad input — the panel must not trust raw manifests; this is the single server-side gate for schema, safety rules, deduplication, and merge order before any JSON is served.PhantomBot/source/com/mcawful/CustomPanelManifestCache.java
PhantomBot/javascript-source/core/customScripts.js
scripts/custom/(and lang),!reloadcustom, andinitReadyreplay for newly loaded scripts; invoked silently after manifest fetch from the panel — after a manifest refresh, new or updated Rhino modules should register commands without a full bot restart, and the panel can trigger that path safely.PhantomBot/resources/web/panel/js/utils/customPanelManifestLoader.js
custom-manifests.json, ownwindow.__pbCustomPanel__,initCustomPanelNav, shared injected CSS (nav, modals, checkbox layout, community card flex row);ensureStylesInjectedskips creating a second#pb-custom-panel-stylesif one already exists — one place to load merged manifests, share constants/helpers across nav/cards/modals, inject small shared styles (including a stable Games card grid), and avoid duplicate<style>tags on SPA-style navigation.PhantomBot/resources/web/panel/js/utils/customPanelNav.js
navinto the sidebar (dividers / sections) — stockindex.htmlcannot list unknown modules; this injects links only into sidebar sections that already expose a submenu mount.PhantomBot/resources/web/panel/js/utils/customPanelCards.js
cardson the Games page; toggles, settings, details; addpb-custom-cards-mounton the mount row — community cards need the same enable/disable and settings patterns (along with a new info pattern) as stock games without hand-authored HTML per module.PhantomBot/resources/web/panel/js/utils/customPanelSettingsModal.js
PhantomBot/resources/web/panel/js/utils/customPanelDetailsModal.js
PhantomBot/docs/guides/content/developerdocs/custommodules.md
manifest.json, paths, and behavior overview — contributors need a canonical contract (paths,sectionvalues, field types) separate from scattered README fragments.PhantomBot/docs/guides/content/moduleguides/addingcustommodules.md
Modified files
PhantomBot/source/tv/phantombot/httpserver/HTTPPanelAndYTHandler.java
/panel/custom-manifests.jsonfrom the collector/cache (with 304 when unchanged) — the browser must load merged manifests through the same authenticated panel HTTP stack as the rest of the UI, with cache semantics.PhantomBot/source/com/gmt2001/PathValidator.java
isPathUnderExecutionOrDockerWeb(and related checks) so./webaccess is valid when custom content lives under the Docker data volume — Docker images symlinkweb/panel/.../customintoPhantomBot_data; checks that only allow paths under the JAR layout would wrongly 403 legitimate panel files.PhantomBot/source/tv/phantombot/httpserver/HttpSetupHandler.java
PathValidator.isPathUnderExecutionOrDockerWebinstead ofReflect.GetExecutionPath()prefix checks for./webauth paths — web-auth path validation must match where custom panel pages and assets actually live in Docker and bare-metal layouts.PhantomBot/source/tv/phantombot/httpserver/HTTPOAuthHandler.java
PathValidator-based web root check asHttpSetupHandler— OAuth-related static./webfetches use the same path rules; leaving one handler on the old check would be an inconsistent hole.PhantomBot/source/com/gmt2001/RollbarProvider.java
com.mcawfulinAPP_PACKAGESso stack traces from new classes are treated as first-party for Rollbar grouping — new collector/cache code lives undercom.mcawful; without this, reports may be misclassified or filtered versus core PhantomBot packages.PhantomBot/resources/web/panel/index.html
customPanel*.jsin dependency order — the loader must run before modals, nav, and cards; order is part of the public contract between those scripts.PhantomBot/resources/web/panel/js/index.js
initCustomPanelNav()after panel websocket auth — nav injection should run only when the panel session is real (socket ready), avoiding empty mounts or duplicate work on the login shell.PhantomBot/resources/web/panel/js/utils/ajaxLoader.js
[data-folder]clicks ondocumentso dynamically injected custom nav links load pages — manifest nav<a>elements are added after the initial$(function () { ... })binding; direct binding on[data-folder]never sees them.PhantomBot/resources/web/panel/pages/games/games.html
#pb-panel-games-custom-cardsmount andinjectCustomCards('games', …)— stock Games markup has no mount point for third-party cards; this is the minimal hook that keeps community cards on the same page as built-in games.PhantomBot/resources/docker-entrypoint.sh
mkdir -pforweb/panel/pages/customandweb/panel/js/pages/customunderPhantomBot_data— fresh data volumes may lack those directories; the bot should start clean even before the user copies any custom files.PhantomBot/Dockerfile
web/panel/custom,pages/custom, andjs/pages/custominto the data volume layout (alongside existing Docker steps) — custom panel files must persist on the volume and be visible under/opt/PhantomBot/web/...at runtime, matching how other mutableweboverlays are handled.PhantomBot/docs/guides/content/developerdocs/toc.json
PhantomBot/docs/guides/content/moduleguides/toc.json