matterbridge 3.3.7-dev-20251102-c85d574 → 3.3.7-dev-20251104-7c779b9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -34,6 +34,8 @@ Advantages:
34
34
 
35
35
  ### Added
36
36
 
37
+ - [matterbridge]: Added a first check for plugin existence (docker pull) and reinstall it before parsing the plugin.
38
+
37
39
  ### Changed
38
40
 
39
41
  - [frontend]: Bumped `frontend` version to 3.3.1.
@@ -41,6 +43,8 @@ Advantages:
41
43
 
42
44
  ### Fixed
43
45
 
46
+ - [frontend]: Fixed route fallback and cross platform path failing randomly with node prefix.
47
+
44
48
  <a href="https://www.buymeacoffee.com/luligugithub">
45
49
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
46
50
  </a>
package/dist/frontend.js CHANGED
@@ -135,7 +135,7 @@ export class Frontend extends EventEmitter {
135
135
  const upload = multer.default({ dest: uploadDir });
136
136
  const express = await import('express');
137
137
  this.expressApp = express.default();
138
- this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
138
+ this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend', 'build')));
139
139
  this.log.debug(`Creating WebSocketServer...`);
140
140
  const ws = await import('ws');
141
141
  this.webSocketServer = new ws.WebSocketServer({ noServer: true });
@@ -254,23 +254,23 @@ export class Frontend extends EventEmitter {
254
254
  let passphrase;
255
255
  let httpsServerOptions = {};
256
256
  const fs = await import('node:fs');
257
- if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
257
+ if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
258
258
  try {
259
- pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
260
- this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
259
+ pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
260
+ this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
261
261
  }
262
262
  catch (error) {
263
- this.log.error(`Error reading p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}: ${error}`);
263
+ this.log.error(`Error reading p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}: ${error}`);
264
264
  this.emit('server_error', error);
265
265
  return;
266
266
  }
267
267
  try {
268
- passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
268
+ passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
269
269
  passphrase = passphrase.trim();
270
- this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
270
+ this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
271
271
  }
272
272
  catch (error) {
273
- this.log.error(`Error reading p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}: ${error}`);
273
+ this.log.error(`Error reading p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}: ${error}`);
274
274
  this.emit('server_error', error);
275
275
  return;
276
276
  }
@@ -278,30 +278,30 @@ export class Frontend extends EventEmitter {
278
278
  }
279
279
  else {
280
280
  try {
281
- cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
282
- this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
281
+ cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
282
+ this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
283
283
  }
284
284
  catch (error) {
285
- this.log.error(`Error reading certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}: ${error}`);
285
+ this.log.error(`Error reading certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}: ${error}`);
286
286
  this.emit('server_error', error);
287
287
  return;
288
288
  }
289
289
  try {
290
- key = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/key.pem'), 'utf8');
291
- this.log.info(`Loaded key file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/key.pem')}`);
290
+ key = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'key.pem'), 'utf8');
291
+ this.log.info(`Loaded key file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'key.pem')}`);
292
292
  }
293
293
  catch (error) {
294
- this.log.error(`Error reading key file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/key.pem')}: ${error}`);
294
+ this.log.error(`Error reading key file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'key.pem')}: ${error}`);
295
295
  this.emit('server_error', error);
296
296
  return;
297
297
  }
298
298
  try {
299
- ca = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem'), 'utf8');
299
+ ca = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'ca.pem'), 'utf8');
300
300
  fullChain = `${cert}\n${ca}`;
301
- this.log.info(`Loaded CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')}`);
301
+ this.log.info(`Loaded CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'ca.pem')}`);
302
302
  }
303
303
  catch (error) {
304
- this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
304
+ this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'ca.pem')} not loaded: ${error}`);
305
305
  }
306
306
  httpsServerOptions = { cert: fullChain ?? cert, key, ca };
307
307
  }
@@ -700,10 +700,11 @@ export class Frontend extends EventEmitter {
700
700
  }
701
701
  });
702
702
  this.expressApp.use((req, res) => {
703
- this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
704
- res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
703
+ const filePath = path.resolve(this.matterbridge.rootDirectory, 'frontend', 'build');
704
+ this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
705
+ res.sendFile('index.html', { root: filePath });
705
706
  });
706
- this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
707
+ this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend', 'build')}${UNDERLINEOFF}${rs}`);
707
708
  }
708
709
  async stop() {
709
710
  this.log.debug('Stopping the frontend...');
@@ -2,7 +2,7 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
2
  console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import { promises as fs } from 'node:fs';
5
+ import fs from 'node:fs';
6
6
  import EventEmitter from 'node:events';
7
7
  import { inspect } from 'node:util';
8
8
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE, or } from 'node-ansi-logger';
@@ -28,6 +28,7 @@ import { bridge } from './matterbridgeDeviceTypes.js';
28
28
  import { Frontend } from './frontend.js';
29
29
  import { addVirtualDevices } from './helpers.js';
30
30
  import { BroadcastServer } from './broadcastServer.js';
31
+ import { inspectError } from './utils/error.js';
31
32
  export class Matterbridge extends EventEmitter {
32
33
  systemInformation = {
33
34
  interfaceName: '',
@@ -238,8 +239,8 @@ export class Matterbridge extends EventEmitter {
238
239
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
239
240
  const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
240
241
  try {
241
- await fs.access(pairingFilePath, fs.constants.R_OK);
242
- const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
242
+ await fs.promises.access(pairingFilePath, fs.constants.R_OK);
243
+ const pairingFileContent = await fs.promises.readFile(pairingFilePath, 'utf8');
243
244
  const pairingFileJson = JSON.parse(pairingFileContent);
244
245
  if (isValidNumber(pairingFileJson.vendorId)) {
245
246
  this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
@@ -459,9 +460,8 @@ export class Matterbridge extends EventEmitter {
459
460
  await this.plugins.loadFromStorage();
460
461
  this.devices.logLevel = this.log.logLevel;
461
462
  for (const plugin of this.plugins) {
462
- const packageJson = await this.plugins.parse(plugin);
463
- if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
464
- this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
463
+ if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
464
+ this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
465
465
  try {
466
466
  const { spawnCommand } = await import('./utils/spawn.js');
467
467
  await spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose'], 'install', plugin.name);
@@ -469,9 +469,16 @@ export class Matterbridge extends EventEmitter {
469
469
  plugin.error = false;
470
470
  }
471
471
  catch (error) {
472
+ inspectError(this.log, `Error installing plugin ${plg}${plugin.name}${er}. The plugin is disabled.`, error);
472
473
  plugin.error = true;
473
474
  plugin.enabled = false;
474
- this.log.error(`Error installing plugin ${plg}${plugin.name}${er}. The plugin is disabled.`, error instanceof Error ? error.message : error);
475
+ continue;
476
+ }
477
+ if ((await this.plugins.parse(plugin)) === null) {
478
+ this.log.error(`Error parsing plugin ${plg}${plugin.name}${er}. The plugin is disabled.`);
479
+ plugin.error = true;
480
+ plugin.enabled = false;
481
+ continue;
475
482
  }
476
483
  }
477
484
  this.log.debug(`Creating node storage context for plugin ${plg}${plugin.name}${db}`);
@@ -817,7 +824,7 @@ export class Matterbridge extends EventEmitter {
817
824
  }
818
825
  }
819
826
  this.log.debug(`Reading matterbridge package.json...`);
820
- const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
827
+ const packageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
821
828
  this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
822
829
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
823
830
  if (this.nodeContext)
@@ -827,7 +834,7 @@ export class Matterbridge extends EventEmitter {
827
834
  this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
828
835
  this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
829
836
  this.log.debug(`Reading frontend package.json...`);
830
- const frontendPackageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
837
+ const frontendPackageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
831
838
  this.frontendVersion = frontendPackageJson.version;
832
839
  this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
833
840
  const currentDir = process.cwd();
@@ -1052,10 +1059,10 @@ export class Matterbridge extends EventEmitter {
1052
1059
  try {
1053
1060
  const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
1054
1061
  this.log.info(`Removing matter storage directory: ${dir}`);
1055
- await fs.rm(dir, { recursive: true });
1062
+ await fs.promises.rm(dir, { recursive: true });
1056
1063
  const backup = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup');
1057
1064
  this.log.info(`Removing matter storage backup directory: ${backup}`);
1058
- await fs.rm(backup, { recursive: true });
1065
+ await fs.promises.rm(backup, { recursive: true });
1059
1066
  }
1060
1067
  catch (error) {
1061
1068
  if (error instanceof Error && error.code !== 'ENOENT') {
@@ -1065,10 +1072,10 @@ export class Matterbridge extends EventEmitter {
1065
1072
  try {
1066
1073
  const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
1067
1074
  this.log.info(`Removing matterbridge storage directory: ${dir}`);
1068
- await fs.rm(dir, { recursive: true });
1075
+ await fs.promises.rm(dir, { recursive: true });
1069
1076
  const backup = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup');
1070
1077
  this.log.info(`Removing matterbridge storage backup directory: ${backup}`);
1071
- await fs.rm(backup, { recursive: true });
1078
+ await fs.promises.rm(backup, { recursive: true });
1072
1079
  }
1073
1080
  catch (error) {
1074
1081
  if (error instanceof Error && error.code !== 'ENOENT') {
@@ -1 +1 @@
1
- body{font-family:Roboto,sans-serif}[frontend-theme=classic]{--main-bg-color: #c4c2c2;--main-text-color: black;--main-grey-color: #616161;--main-light-color: #959595;--main-icon-color: #4d4d4d;--main-log-color: var(--main-text-color);--main-button-color: #ffffff;--main-button-bg-color: var(--primary-color);--main-menu-color: #26292d;--main-menu-bg-color: #e2e2e2;--main-menu-hover-color: #959595;--main-label-color: var(--main-grey-color);--primary-color: #009a00;--secondary-color: #92771f;--header-bg-color: var(--primary-color);--header-text-color: white;--footer-bg-color: var(--div-bg-color);--footer-text-color: var(--div-text-color);--ttip-bg-color: #555;--ttip-text-color: #fff;--table-border-color: #ddd;--table-text-color: black;--table-even-bg-color: #bdbdbd;--table-odd-bg-color: var(--div-bg-color);--table-hover-bg-color: #5f8c9e;--table-selected-bg-color: #5f8c9e;--div-bg-color: #adadad;--div-text-color: black;--div-shadow-color: #888;--div-border-color: rgb(139, 139, 139);--div-border-radius: 0px;--div-title-bg-color: var(--div-bg-color);--div-title-text-color: black;background-color:var(--main-bg-color);color:var(--main-text-color);font-family:Roboto,Helvetica,Arial,sans-serif}[frontend-theme=dark]{--main-bg-color: #26292d;--main-text-color: #ffffff;--main-grey-color: #616161;--main-light-color: #959595;--main-icon-color: var(--main-light-color);--main-log-color: var(--main-light-color);--main-button-color: #ffffff;--main-button-bg-color: var(--primary-color);--main-menu-color: var(--main-light-color);--main-menu-bg-color: var(--main-bg-color);--main-menu-hover-color: var(--div-bg-color);--main-label-color: var(--main-grey-color);--primary-color: #1976d2;--secondary-color: #a58827;--header-bg-color: var(--div-bg-color);--header-text-color: var(--primary-color);--footer-bg-color: var(--div-bg-color);--footer-text-color: var(--div-text-color);--ttip-bg-color: #555;--ttip-text-color: #fff;--table-border-color: var(--div-bg-color);--table-text-color: var(--main-light-color);--table-even-bg-color: var(--div-bg-color);--table-odd-bg-color: var(--div-bg-color);--table-hover-bg-color: var(--main-bg-color);--table-selected-bg-color: var(--main-bg-color);--div-bg-color: #1b1d21;--div-text-color: var(--main-light-color);--div-shadow-color: #34373d;--div-border-color: #1b1d21;--div-border-radius: 5px;--div-title-bg-color: #1b1d21;--div-title-text-color: var(--primary-color);background-color:var(--main-bg-color);color:var(--main-text-color);font-family:Roboto,Helvetica,Arial,sans-serif}[frontend-theme=light]{--main-bg-color: #f0f0f0;--main-text-color: #212121;--main-grey-color: #616161;--main-light-color: #363636;--main-icon-color: #7a7a7a;--main-log-color: var(--main-light-color);--main-button-color: #ffffff;--main-button-bg-color: var(--primary-color);--main-menu-color: var(--main-text-color);--main-menu-bg-color: var(--div-bg-color);--main-menu-hover-color: #85c0d8;--main-label-color: var(--main-grey-color);--primary-color: #2196f3;--secondary-color: #a58827;--header-bg-color: var(--div-bg-color);--header-text-color: var(--div-text-color);--footer-bg-color: var(--div-bg-color);--footer-text-color: var(--div-text-color);--ttip-bg-color: #555;--ttip-text-color: #fff;--table-border-color: var(--div-bg-color);--table-text-color: var(--main-light-color);--table-even-bg-color: var(--div-bg-color);--table-odd-bg-color: var(--div-bg-color);--table-hover-bg-color: #85c0d8;--table-selected-bg-color: #85c0d8;--div-bg-color: #ffffff;--div-text-color: #212121;--div-shadow-color: #bfbfbf;--div-border-color: var(--div-bg-color);--div-border-radius: 5px;--div-title-bg-color: var(--div-bg-color);--div-title-text-color: var(--div-text-color);background-color:var(--main-bg-color);color:var(--main-text-color);font-family:Roboto,Helvetica,Arial,sans-serif}::-webkit-scrollbar{width:10px}::-webkit-scrollbar-thumb{background:var(--primary-color);border-radius:5px}::-webkit-scrollbar-thumb:hover{background:var(--primary-color);border-radius:5px}::-webkit-scrollbar-track{background:"inherit"}html,.thin-scroll{scrollbar-width:thin;scrollbar-color:var(--primary-color) var(--div-bg-color)}.thin-scroll::-webkit-scrollbar{width:5px;height:5px}.thin-scroll::-webkit-scrollbar-thumb{background:var(--primary-color);border-radius:5px}.tooltip-container{position:relative;display:inline-block;z-index:10}.tooltip-text{visibility:hidden;background-color:var(--ttip-bg-color);color:var(--ttip-text-color);text-align:center;padding:5px;border-radius:6px;position:absolute;z-index:10;bottom:calc(100% + 10px);left:50%;margin-left:-60px;opacity:0;transition:opacity .3s;font-size:12px}.tooltip-container:hover{cursor:pointer;z-index:10}.tooltip-container:hover .tooltip-text{visibility:visible;opacity:1;z-index:10}.status-enabled{background-color:green;color:#fff;padding:.2rem;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-disabled{background-color:red;color:#fff;padding:.2rem;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-information{background-color:#9e9e9e;color:#fff;padding:2px 10px;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-warning{background-color:#e9db18;color:#000;padding:2px 10px;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-sponsor{background-color:#b6409c;color:#fff;padding:2px 10px;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-blue{background-color:#5f8c9e;color:#fff;padding:.2rem;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.header{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:20px;margin:0;padding:0;height:40px}.sub-header{flex:0 0 auto;display:flex;flex-direction:row;align-items:center;gap:20px;margin:0;padding:0;height:40px}nav{display:flex;align-items:center;gap:10px}.nav-link{margin:0;font-size:20px;text-decoration:none;color:var(--main-icon-color);transition:color .3s ease}.nav-link:hover{color:var(--primary-color)}table{border-collapse:collapse;width:100%;table-layout:auto}thead{position:sticky;top:0;border:1px solid var(--table-border-color);z-index:10}thead th{border:1px solid var(--table-border-color);padding:5px 10px;color:var(--header-text-color);background:var(--header-bg-color);text-align:left;z-index:10}tbody td{border:1px solid var(--table-border-color);margin:0;padding:5px 10px;text-align:left;font-size:14px}tbody tr:hover{color:var(--table-text-color);background-color:var(--table-hover-bg-color)}.table-content-even{color:var(--table-text-color);background-color:var(--table-even-bg-color)}.table-content-odd{color:var(--table-text-color);background-color:var(--table-odd-bg-color)}.table-content-selected{color:var(--table-text-color);background-color:var(--table-selected-bg-color)}h3{margin:0}.MbfScreen{display:flex;flex-direction:column;width:calc(100vw - 40px);height:calc(100vh - 40px);gap:20px;margin:0;padding:20px;background-color:var(--main-bg-color)}.MbfPageDiv{display:flex;flex-direction:column;height:100%;width:100%;margin:0;padding:0;gap:20px}.MbfWindowDiv{display:flex;flex-direction:column;box-shadow:5px 5px 10px var(--div-shadow-color);border:1px solid var(--table-border-color);border-radius:var(--div-border-radius);box-sizing:border-box;background-color:var(--div-bg-color)}.MbfWindowDivTable{display:flex;flex-direction:column;flex:1 1 auto;margin:-1px;padding:0;gap:0;overflow:auto;display:block}.MbfWindowHeaderFooterIcons{display:flex;flex-direction:row;margin:0;padding:0 10px;gap:10px}.MbfWindowHeader{display:flex;flex-direction:row;align-items:center;width:100%;border-bottom:1px solid var(--table-border-color);color:var(--header-text-color);background-color:var(--header-bg-color);margin:0;padding:0;box-sizing:border-box}.MbfWindowHeaderText{color:var(--header-text-color);font-weight:700;margin:0;padding:5px 10px}.MbfWindowFooter{display:flex;flex-direction:row;align-items:center;justify-content:center;color:var(--footer-text-color);background-color:var(--footer-bg-color);margin:0;padding:0}.MbfWindowFooterText{color:var(--footer-text-color);background-color:var(--footer-bg-color);font-weight:700;text-align:center;margin:0;padding:5px 10px}.MbfWindowBody{display:flex;flex:1 1 auto;margin:0;padding:10px;gap:10px}.MbfWindowBodyColumn{display:flex;flex-direction:column;flex:1 1 auto;width:100%;margin:0;padding:10px 0;gap:0px;overflow:auto}.MbfWindowBodyRow{display:flex;flex-direction:row;flex:1 1 auto;height:100%;margin:0;padding:0 10px;gap:0px;overflow:auto}.configSubmitButton{display:flex;flex-direction:row;justify-content:center;width:auto;margin:20px}.configSubmitButton button{width:auto}@media(max-width:1300px){.MbfScreen{width:1300px;height:1024px}.xxxheader{flex-direction:column;align-items:start;justify-content:start}.xxxsub-header{align-items:start;justify-content:start}}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
1
+ body{font-family:Roboto,sans-serif}[frontend-theme=classic]{--main-bg-color: #c4c2c2;--main-text-color: black;--main-grey-color: #616161;--main-light-color: #959595;--main-icon-color: #4d4d4d;--main-log-color: var(--main-text-color);--main-button-color: #ffffff;--main-button-bg-color: var(--primary-color);--main-menu-color: #26292d;--main-menu-bg-color: #e2e2e2;--main-menu-hover-color: #959595;--main-label-color: var(--main-grey-color);--primary-color: #009a00;--secondary-color: #92771f;--header-bg-color: var(--primary-color);--header-text-color: white;--footer-bg-color: var(--div-bg-color);--footer-text-color: var(--div-text-color);--ttip-bg-color: #555;--ttip-text-color: #fff;--table-border-color: #ddd;--table-text-color: black;--table-even-bg-color: #bdbdbd;--table-odd-bg-color: var(--div-bg-color);--table-hover-bg-color: #5f8c9e;--table-selected-bg-color: #5f8c9e;--div-bg-color: #adadad;--div-text-color: black;--div-shadow-color: #888;--div-border-color: rgb(139, 139, 139);--div-border-radius: 0px;--div-title-bg-color: var(--div-bg-color);--div-title-text-color: black;background-color:var(--main-bg-color);color:var(--main-text-color);font-family:Roboto,Helvetica,Arial,sans-serif}[frontend-theme=dark]{--main-bg-color: #26292d;--main-text-color: #ffffff;--main-grey-color: #616161;--main-light-color: #959595;--main-icon-color: var(--main-light-color);--main-log-color: var(--main-light-color);--main-button-color: #ffffff;--main-button-bg-color: var(--primary-color);--main-menu-color: var(--main-light-color);--main-menu-bg-color: var(--main-bg-color);--main-menu-hover-color: var(--div-bg-color);--main-label-color: var(--main-grey-color);--primary-color: #1976d2;--secondary-color: #a58827;--header-bg-color: var(--div-bg-color);--header-text-color: var(--primary-color);--footer-bg-color: var(--div-bg-color);--footer-text-color: var(--div-text-color);--ttip-bg-color: #555;--ttip-text-color: #fff;--table-border-color: var(--div-bg-color);--table-text-color: var(--main-light-color);--table-even-bg-color: var(--div-bg-color);--table-odd-bg-color: var(--div-bg-color);--table-hover-bg-color: var(--main-bg-color);--table-selected-bg-color: var(--main-bg-color);--div-bg-color: #1b1d21;--div-text-color: var(--main-light-color);--div-shadow-color: #34373d;--div-border-color: #1b1d21;--div-border-radius: 5px;--div-title-bg-color: #1b1d21;--div-title-text-color: var(--primary-color);background-color:var(--main-bg-color);color:var(--main-text-color);font-family:Roboto,Helvetica,Arial,sans-serif}[frontend-theme=light]{--main-bg-color: #f0f0f0;--main-text-color: #212121;--main-grey-color: #616161;--main-light-color: #363636;--main-icon-color: #7a7a7a;--main-log-color: var(--main-light-color);--main-button-color: #ffffff;--main-button-bg-color: var(--primary-color);--main-menu-color: var(--main-text-color);--main-menu-bg-color: var(--div-bg-color);--main-menu-hover-color: #85c0d8;--main-label-color: var(--main-grey-color);--primary-color: #2196f3;--secondary-color: #a58827;--header-bg-color: var(--div-bg-color);--header-text-color: var(--div-text-color);--footer-bg-color: var(--div-bg-color);--footer-text-color: var(--div-text-color);--ttip-bg-color: #555;--ttip-text-color: #fff;--table-border-color: var(--div-bg-color);--table-text-color: var(--main-light-color);--table-even-bg-color: var(--div-bg-color);--table-odd-bg-color: var(--div-bg-color);--table-hover-bg-color: #85c0d8;--table-selected-bg-color: #85c0d8;--div-bg-color: #ffffff;--div-text-color: #212121;--div-shadow-color: #bfbfbf;--div-border-color: var(--div-bg-color);--div-border-radius: 5px;--div-title-bg-color: var(--div-bg-color);--div-title-text-color: var(--div-text-color);background-color:var(--main-bg-color);color:var(--main-text-color);font-family:Roboto,Helvetica,Arial,sans-serif}::-webkit-scrollbar{width:10px}::-webkit-scrollbar-thumb{background:var(--primary-color);border-radius:5px}::-webkit-scrollbar-thumb:hover{background:var(--primary-color);border-radius:5px}::-webkit-scrollbar-track{background:"inherit"}html,.thin-scroll{scrollbar-width:thin;scrollbar-color:var(--primary-color) var(--div-bg-color)}.thin-scroll::-webkit-scrollbar{width:5px;height:5px}.thin-scroll::-webkit-scrollbar-thumb{background:var(--primary-color);border-radius:5px}.tooltip-container{position:relative;display:inline-block;z-index:10}.tooltip-text{visibility:hidden;background-color:var(--ttip-bg-color);color:var(--ttip-text-color);text-align:center;padding:5px;border-radius:6px;position:absolute;z-index:10;bottom:calc(100% + 10px);left:50%;margin-left:-60px;opacity:0;transition:opacity .3s;font-size:12px}.tooltip-container:hover{cursor:pointer;z-index:10}.tooltip-container:hover .tooltip-text{visibility:visible;opacity:1;z-index:10}.status-enabled{background-color:green;color:#fff;padding:.2rem;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-disabled{background-color:red;color:#fff;padding:.2rem;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-information{background-color:#9e9e9e;color:#fff;padding:2px 10px;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-warning{background-color:#e9db18;color:#000;padding:2px 10px;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-sponsor{background-color:#b6409c;color:#fff;padding:2px 10px;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.status-blue{background-color:#5f8c9e;color:#fff;padding:.2rem;border-radius:.25rem;text-align:center;font-size:12px;cursor:pointer;box-shadow:2px 2px 2px #0003}.header{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:20px;margin:0;padding:0;height:40px}.sub-header{flex:0 0 auto;display:flex;flex-direction:row;align-items:center;gap:20px;margin:0;padding:0;height:40px}nav{display:flex;align-items:center;gap:10px}.nav-link{margin:0;font-size:20px;text-decoration:none;color:var(--main-icon-color);transition:color .3s ease}.nav-link:hover{color:var(--primary-color)}table{border-collapse:collapse;width:100%;table-layout:auto}thead{position:sticky;top:0;border:1px solid var(--table-border-color);z-index:10}thead th{border:1px solid var(--table-border-color);padding:5px 10px;color:var(--header-text-color);background:var(--header-bg-color);text-align:left;z-index:10}tbody td{border:1px solid var(--table-border-color);margin:0;padding:5px 10px;text-align:left;font-size:14px}tbody tr:hover{color:var(--table-text-color);background-color:var(--table-hover-bg-color)}.table-content-even{color:var(--table-text-color);background-color:var(--table-even-bg-color)}.table-content-odd{color:var(--table-text-color);background-color:var(--table-odd-bg-color)}.table-content-selected{color:var(--table-text-color);background-color:var(--table-selected-bg-color)}h3{margin:0}.MbfScreen{display:flex;flex-direction:column;width:calc(100vw - 40px);height:calc(100vh - 40px);gap:20px;margin:0;padding:20px;background-color:var(--main-bg-color)}.MbfPageDiv{display:flex;flex-direction:column;height:100%;width:100%;margin:0;padding:0;gap:20px}.MbfWindowDiv{display:flex;flex-direction:column;box-shadow:5px 5px 10px var(--div-shadow-color);border:1px solid var(--table-border-color);border-radius:var(--div-border-radius);box-sizing:border-box;background-color:var(--div-bg-color)}.MbfWindowDivTable{display:block;flex:1 1 auto;overflow:auto;margin:-1px;padding:0;gap:0}.MbfWindowHeaderFooterIcons{display:flex;flex-direction:row;margin:0;padding:0 10px;gap:10px}.MbfWindowHeader{display:flex;flex-direction:row;align-items:center;width:100%;border-bottom:1px solid var(--table-border-color);color:var(--header-text-color);background-color:var(--header-bg-color);margin:0;padding:0;box-sizing:border-box}.MbfWindowHeaderText{color:var(--header-text-color);font-weight:700;margin:0;padding:5px 10px}.MbfWindowFooter{display:flex;flex-direction:row;align-items:center;justify-content:center;color:var(--footer-text-color);background-color:var(--footer-bg-color);margin:0;padding:0}.MbfWindowFooterText{color:var(--footer-text-color);background-color:var(--footer-bg-color);font-weight:700;text-align:center;margin:0;padding:5px 10px}.MbfWindowBody{display:flex;flex:1 1 auto;margin:0;padding:10px;gap:10px}.MbfWindowBodyColumn{display:flex;flex-direction:column;flex:1 1 auto;width:100%;margin:0;padding:10px 0;gap:0px;overflow:auto}.MbfWindowBodyRow{display:flex;flex-direction:row;flex:1 1 auto;height:100%;margin:0;padding:0 10px;gap:0px;overflow:auto}.configSubmitButton{display:flex;flex-direction:row;justify-content:center;width:auto;margin:20px}.configSubmitButton button{width:auto}@media(max-width:1300px){.MbfScreen{width:1300px;height:1024px}.xxxheader{flex-direction:column;align-items:start;justify-content:start}.xxxsub-header{align-items:start;justify-content:start}}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}