Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9b93804
Initial version with the drop down menu
VeithMetro Nov 19, 2025
e1e9b3b
Make the menu update after each state change
VeithMetro Nov 19, 2025
4ab5d03
Reload the page after activating or deactivating bridge link
VeithMetro Nov 19, 2025
0a68c30
Formatting changes
VeithMetro Nov 19, 2025
e92b5dc
Make sure that any callsign for the composite plugin will work, as we…
VeithMetro Dec 1, 2025
a7fe86d
Intercept each API call and add a prefix to calls from composite Thun…
VeithMetro Dec 1, 2025
b4edc1a
Clean everything up, make sure one nesting layer is allowed and you n…
VeithMetro Dec 1, 2025
83309f4
Clean the formatting and comments
VeithMetro Dec 1, 2025
297e088
Make sure that active UI of the plugin survives the page reload
VeithMetro Dec 1, 2025
68365bd
Thunder instances as buttons on the top of the page instead of a drop…
VeithMetro Dec 3, 2025
ae9239c
Fix a bug with inproper caching between Thunder instances
VeithMetro Dec 3, 2025
0871444
Slight formatting changes
VeithMetro Dec 3, 2025
dc935c6
Update some comments
VeithMetro Dec 3, 2025
4151b3c
Fix the Cross-site scripting vulnerability found by the scanning tools
VeithMetro Dec 3, 2025
c713913
Try once again to fix the code scanning issues
VeithMetro Dec 3, 2025
bc24ca5
Build header using DOM methods to avoid innerHTML XSS concerns
VeithMetro Dec 3, 2025
66b22af
Take care of one other innerHTML
VeithMetro Dec 3, 2025
3ac3ea6
Replace all innerHTML in Controller.js with DOM methods
VeithMetro Dec 3, 2025
bf675be
Sanitaze the data and fix one more innerHTML in application.js
VeithMetro Dec 3, 2025
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
2 changes: 1 addition & 1 deletion dist/bundle.js

Large diffs are not rendered by default.

48 changes: 44 additions & 4 deletions src/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ html {
user-select: none;
z-index: 1000;
background-color: rgba(0,0,0,0.1);
display: flex;
align-items: center;
}

.touch .header img {
Expand Down Expand Up @@ -633,10 +635,8 @@ div#hide-notifications:hover {

.desktop .header img {
height: 30px;
width: 30px;
margin-top: 15px;
margin-left: 35px;
margin-bottom: 10px;
width: auto;
margin-left: 15px;
}

@media screen and (min-width: 960px) {
Expand Down Expand Up @@ -882,3 +882,43 @@ input[type=checkbox] {
-moz-transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out;
-webkit-transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out;
}

.instance-buttons {
display: flex;
gap: 8px;
margin-left: 20px;
overflow-x: auto;
max-width: calc(100vw - 250px);
scrollbar-width: thin;
}

.instance-buttons::-webkit-scrollbar {
height: 4px;
}

.instance-buttons::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
}

.instance-button {
background-color: #444;
border: 1px solid #555;
border-radius: 4px;
color: #ccc;
padding: 6px 14px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
}

.instance-button:hover {
background-color: #555;
color: #fff;
}

.instance-button.active {
background-color: #0078d4;
border-color: #0078d4;
color: #fff;
}
27 changes: 20 additions & 7 deletions src/js/core/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,30 @@ function init(host){

/** (global) renders a plugin in the main div */
function showPlugin(callsign) {
if (plugins[ callsign ] === undefined)
// Extract base callsign for plugin lookup (e.g., "DeviceInfo" from "BridgeLink1/DeviceInfo")
const delimiter = '/';
const lastDelimiterIndex = callsign.lastIndexOf(delimiter);
const baseCallsign = lastDelimiterIndex !== -1 ? callsign.substring(lastDelimiterIndex + 1) : callsign;
const prefix = lastDelimiterIndex !== -1 ? callsign.substring(0, lastDelimiterIndex) : null;

if (plugins[ baseCallsign ] === undefined)
return;

if (activePlugin !== undefined && plugins[ activePlugin ] !== undefined)
plugins[ activePlugin ].close();
if (activePlugin !== undefined) {
// Get base callsign for the currently active plugin
const activeLastDelimiter = activePlugin.lastIndexOf(delimiter);
const activeBaseCallsign = activeLastDelimiter !== -1 ? activePlugin.substring(activeLastDelimiter + 1) : activePlugin;
if (plugins[ activeBaseCallsign ] !== undefined)
plugins[ activeBaseCallsign ].close();
}

document.getElementById('main').innerHTML = '';
plugins[ callsign ].render();
activePlugin = callsign;
window.localStorage.setItem('lastActivePlugin', callsign);
};

// Set the active prefix on the API so all subsequent calls use it
api.setActivePrefix(prefix);

plugins[ baseCallsign ].render();
}

/** (global) refresh current active plugin */
function renderCurrentPlugin() {
Expand Down
40 changes: 37 additions & 3 deletions src/js/core/wpeApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export default class WpeApi {
this.t = ThunderJS({ 'host' : this.host[0], 'port': this.host[1] });

this.socketListeners = {};

// Active prefix for composite plugin support (e.g., "BridgeLink1")
this.activePrefix = null;

// might use this later if the requests are getting to slow with the jsonrpc -> rest fallback.
this.servicesAvailableInJsonRPC = [
Expand All @@ -61,6 +64,34 @@ export default class WpeApi {
];
};

/**
* Set the active prefix for API calls.
* When set, all plugin calls will be prefixed (e.g., "MessageControl" becomes "BridgeLink1/MessageControl").
* @param {string|null} prefix - The prefix to apply, or null for local Thunder
*/
setActivePrefix(prefix) {
this.activePrefix = prefix;
}

/**
* Get the prefixed plugin name for API calls.
* @param {string} pluginName - The base plugin name
* @returns {string} The prefixed plugin name (or original if no prefix set)
*/
getPrefixedPlugin(pluginName) {
// Don't prefix if no active prefix
if (!this.activePrefix) {
return pluginName;
}

// Don't double-prefix if the plugin already has a prefix
if (pluginName.includes('/')) {
return pluginName;
}

return this.activePrefix + '/' + pluginName;
}

handleRequest(method, URL, body, callback) {
var self = this;

Expand Down Expand Up @@ -109,11 +140,14 @@ export default class WpeApi {

// Compatibility method to deal with transitioning APIs and older version of Thunder
// note: This assumes the WebSocket to jsonrpc will fail.
req(rest, jsonrpc) {
req(rest, jsonrpc, options = {}) {
return new Promise( (resolve, reject) => {
if (jsonrpc) {
console.debug(`<JSON> ${jsonrpc.plugin }.1.${jsonrpc.method}`, jsonrpc.params ? jsonrpc.params : '');
this.t.call(jsonrpc.plugin, jsonrpc.method, jsonrpc.params)
// Apply active prefix to plugin name unless skipPrefix is set
// skipPrefix is used for infrastructure calls with absolute paths
const prefixedPlugin = options.skipPrefix ? jsonrpc.plugin : this.getPrefixedPlugin(jsonrpc.plugin);
console.debug(`<JSON> ${prefixedPlugin}.1.${jsonrpc.method}`, jsonrpc.params ? jsonrpc.params : '');
this.t.call(prefixedPlugin, jsonrpc.method, jsonrpc.params)
.then( result => {
resolve(result);
}).catch( error => {
Expand Down
Loading
Loading