iobroker.eos-admin 7.9.37 → 7.9.39

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.
@@ -3321,7 +3321,7 @@ html.eos-app #eos-assist-root {
3321
3321
  }
3322
3322
 
3323
3323
 
3324
- /* === NexoWatt EOS v37: native adapter configuration safe mode =============
3324
+ /* === NexoWatt EOS v38: native adapter configuration safe mode =============
3325
3325
  Custom adapter configuration pages (React/HTML/jsonConfig) must be fully
3326
3326
  controlled by the adapter itself. EOS shell decoration is disabled inside the
3327
3327
  content area so buttons such as "Gerät hinzufügen" and "Gerät bearbeiten" stay
@@ -3372,100 +3372,157 @@ html.eos-app.eos-adapter-config-surface #app-paper .eos-protected-adapter-row {
3372
3372
  pointer-events: auto !important;
3373
3373
  }
3374
3374
 
3375
- /* === NexoWatt EOS v37: notification close compatibility ==================
3376
- Toasts/snackbars/alerts must stay above EOS decoration and their close
3377
- actions must remain clickable on all routes. */
3375
+
3376
+ /* === NexoWatt EOS v38: native popup/dialog compatibility ==================
3377
+ EOS decoration must never block native Admin dialogs, adapter install search,
3378
+ autocomplete poppers, menus or adapter-owned configuration dialogs. */
3379
+ html.eos-app .MuiDialog-root,
3380
+ html.eos-app .MuiModal-root,
3381
+ html.eos-app .MuiPopover-root,
3382
+ html.eos-app .MuiPopper-root,
3383
+ html.eos-app .MuiMenu-root,
3384
+ html.eos-app .MuiAutocomplete-popper,
3385
+ html.eos-app [role="dialog"],
3386
+ html.eos-app [role="listbox"],
3387
+ html.eos-app [role="menu"] {
3388
+ pointer-events: auto !important;
3389
+ }
3390
+ html.eos-app .MuiDialog-paper,
3391
+ html.eos-app .MuiPaper-root[role="dialog"],
3392
+ html.eos-app .MuiPopover-paper,
3393
+ html.eos-app .MuiMenu-paper,
3394
+ html.eos-app .MuiAutocomplete-paper,
3395
+ html.eos-app .MuiAutocomplete-listbox,
3396
+ html.eos-app .MuiList-root[role="listbox"] {
3397
+ pointer-events: auto !important;
3398
+ user-select: auto !important;
3399
+ }
3400
+ html.eos-app .MuiAutocomplete-popper,
3401
+ html.eos-app .MuiPopper-root,
3402
+ html.eos-app .MuiPopover-root,
3403
+ html.eos-app .MuiMenu-root {
3404
+ z-index: 6500 !important;
3405
+ }
3406
+ html.eos-app .MuiDialog-root button,
3407
+ html.eos-app .MuiDialog-root [role="button"],
3408
+ html.eos-app .MuiDialog-root a,
3409
+ html.eos-app .MuiModal-root button,
3410
+ html.eos-app .MuiModal-root [role="button"],
3411
+ html.eos-app .MuiModal-root a,
3412
+ html.eos-app .MuiPopover-root button,
3413
+ html.eos-app .MuiPopover-root [role="button"],
3414
+ html.eos-app .MuiPopover-root a,
3415
+ html.eos-app .MuiPopper-root button,
3416
+ html.eos-app .MuiPopper-root [role="button"],
3417
+ html.eos-app .MuiPopper-root a,
3418
+ html.eos-app .MuiMenu-root button,
3419
+ html.eos-app .MuiMenu-root [role="button"],
3420
+ html.eos-app .MuiMenu-root a,
3421
+ html.eos-app [role="listbox"] [role="option"],
3422
+ html.eos-app .MuiAutocomplete-option {
3423
+ pointer-events: auto !important;
3424
+ visibility: visible !important;
3425
+ }
3426
+
3427
+ /* v38: snackbars/toasts are clickable, but generic dialogs are not modified. */
3378
3428
  html.eos-app .MuiSnackbar-root,
3379
3429
  html.eos-app .SnackbarItem-root,
3380
3430
  html.eos-app .SnackbarItem-wrappedRoot,
3381
3431
  html.eos-app .notistack-Snackbar,
3382
3432
  html.eos-app .Toastify__toast-container,
3383
- html.eos-app .Toastify__toast,
3384
- html.eos-app [role="alert"],
3385
- html.eos-app .MuiAlert-root {
3433
+ html.eos-app .Toastify__toast {
3386
3434
  pointer-events: auto !important;
3387
3435
  z-index: 5200 !important;
3388
3436
  }
3389
3437
  html.eos-app .MuiSnackbar-root button,
3390
3438
  html.eos-app .SnackbarItem-root button,
3391
3439
  html.eos-app .notistack-Snackbar button,
3392
- html.eos-app .Toastify__toast button,
3393
- html.eos-app .MuiAlert-root button,
3394
- html.eos-app [role="alert"] button,
3395
- html.eos-app button[aria-label="close"],
3396
- html.eos-app button[aria-label="Close"],
3397
- html.eos-app button[title="Schließen"],
3398
- html.eos-app button[title="Close"] {
3440
+ html.eos-app .Toastify__toast button {
3399
3441
  pointer-events: auto !important;
3400
- opacity: 1 !important;
3401
3442
  visibility: visible !important;
3402
- z-index: 5201 !important;
3443
+ opacity: 1 !important;
3403
3444
  }
3404
- html.eos-app .MuiSnackbar-root svg,
3405
- html.eos-app .SnackbarItem-root svg,
3406
- html.eos-app .MuiAlert-root svg,
3407
- html.eos-app [role="alert"] svg {
3445
+
3446
+ /* v38: never allow decorative EOS layers to sit above native modals/popups. */
3447
+ html.eos-app .eos-top-toolbar::before,
3448
+ html.eos-app .eos-top-toolbar::after,
3449
+ html.eos-app .eos-brand-badge::before,
3450
+ html.eos-app .eos-brand-badge::after,
3451
+ html.eos-app .eos-panel::before,
3452
+ html.eos-app .eos-panel::after {
3408
3453
  pointer-events: none !important;
3409
3454
  }
3410
3455
 
3411
- /* v37: BackItUp-safe mode. EOS keeps delete/stop controls protected in the UI,
3412
- but does not force adapter-object ACLs for runtime adapters by default. */
3413
- html.eos-app .eos-backitup-safe-note { color: rgba(226,245,255,.78); }
3414
3456
 
3415
- /* v37: native Admin notifications must always be closable. */
3416
- html.eos-app .MuiSnackbar-root,
3417
- html.eos-app .MuiAlert-root,
3418
- html.eos-app .MuiSnackbarContent-root,
3419
- html.eos-app [role="alert"],
3420
- html.eos-app .Toastify__toast,
3421
- html.eos-app .notistack-Snackbar,
3422
- html.eos-app .eos-notification-safe {
3457
+ /* === NexoWatt EOS v39: Objects/datapoints native state writing compatibility === */
3458
+ html.eos-app.eos-objects-surface .eos-object-value-cell,
3459
+ html.eos-objects-surface .eos-object-value-cell {
3423
3460
  pointer-events: auto !important;
3424
- z-index: 5200 !important;
3461
+ min-height: 30px !important;
3462
+ border-radius: 8px !important;
3463
+ transition: background .12s ease, box-shadow .12s ease, outline-color .12s ease;
3425
3464
  }
3426
- html.eos-app .MuiSnackbar-root button,
3427
- html.eos-app .MuiAlert-root button,
3428
- html.eos-app .MuiSnackbarContent-root button,
3429
- html.eos-app [role="alert"] button,
3430
- html.eos-app .Toastify__toast button,
3431
- html.eos-app .notistack-Snackbar button,
3432
- html.eos-app .eos-notification-safe button,
3433
- html.eos-app .eos-notification-safe [role="button"],
3434
- html.eos-app .eos-notification-safe a,
3435
- html.eos-app .eos-notification-safe .MuiIconButton-root {
3436
- pointer-events: auto !important;
3437
- visibility: visible !important;
3438
- opacity: 1 !important;
3465
+ html.eos-app.eos-objects-surface .eos-object-value-cell.eos-object-value-writable,
3466
+ html.eos-objects-surface .eos-object-value-cell.eos-object-value-writable {
3467
+ cursor: pointer !important;
3439
3468
  }
3440
- html.eos-app .eos-notification-safe .eos-protected-delete-control,
3441
- html.eos-app .eos-notification-safe .eos-security-hidden-delete {
3442
- display: inline-flex !important;
3469
+ html.eos-app.eos-objects-surface .eos-object-value-cell.eos-object-value-writable:hover,
3470
+ html.eos-objects-surface .eos-object-value-cell.eos-object-value-writable:hover,
3471
+ html.eos-app.eos-objects-surface .eos-object-value-cell.eos-object-value-hover,
3472
+ html.eos-objects-surface .eos-object-value-cell.eos-object-value-hover {
3473
+ background: rgba(0, 255, 136, .14) !important;
3474
+ box-shadow: inset 0 0 0 1px rgba(0, 255, 136, .32), 0 0 12px rgba(0, 255, 136, .10) !important;
3475
+ }
3476
+ html.eos-app.eos-objects-surface .eos-object-value-cell .admin-button,
3477
+ html.eos-objects-surface .eos-object-value-cell .admin-button,
3478
+ html.eos-app.eos-objects-surface .eos-object-value-cell button,
3479
+ html.eos-objects-surface .eos-object-value-cell button {
3443
3480
  pointer-events: auto !important;
3481
+ cursor: pointer !important;
3444
3482
  }
3445
-
3446
-
3447
- /* v37 notification dialog safety: notification dialogs must stay fully native/clickable. */
3448
- html.eos-app .eos-notification-dialog-root,
3449
- html.eos-app .MuiModal-root:has(#notifications-dialog-close),
3450
- html.eos-app .MuiDialog-root:has(#notifications-dialog-close) {
3483
+ html.eos-app.eos-objects-surface .MuiDialog-root,
3484
+ html.eos-app.eos-objects-surface .MuiModal-root,
3485
+ html.eos-app.eos-objects-surface .MuiPopover-root,
3486
+ html.eos-app.eos-objects-surface .MuiPopper-root,
3487
+ html.eos-app.eos-objects-surface [role="dialog"],
3488
+ html.eos-app.eos-objects-surface [role="listbox"],
3489
+ html.eos-app.eos-objects-surface [role="menu"],
3490
+ html.eos-objects-surface .MuiDialog-root,
3491
+ html.eos-objects-surface .MuiModal-root,
3492
+ html.eos-objects-surface .MuiPopover-root,
3493
+ html.eos-objects-surface .MuiPopper-root,
3494
+ html.eos-objects-surface [role="dialog"],
3495
+ html.eos-objects-surface [role="listbox"],
3496
+ html.eos-objects-surface [role="menu"] {
3451
3497
  pointer-events: auto !important;
3452
- z-index: 4300 !important;
3453
3498
  }
3454
- html.eos-app .eos-notification-dialog,
3455
- html.eos-app .MuiDialog-paper:has(#notifications-dialog-close) {
3499
+ html.eos-app.eos-objects-surface .MuiDialog-root button,
3500
+ html.eos-app.eos-objects-surface .MuiDialog-root [role="button"],
3501
+ html.eos-app.eos-objects-surface .MuiDialog-root input,
3502
+ html.eos-app.eos-objects-surface .MuiDialog-root textarea,
3503
+ html.eos-app.eos-objects-surface .MuiDialog-root select,
3504
+ html.eos-app.eos-objects-surface .MuiMenuItem-root,
3505
+ html.eos-app.eos-objects-surface .MuiAutocomplete-option,
3506
+ html.eos-objects-surface .MuiDialog-root button,
3507
+ html.eos-objects-surface .MuiDialog-root [role="button"],
3508
+ html.eos-objects-surface .MuiDialog-root input,
3509
+ html.eos-objects-surface .MuiDialog-root textarea,
3510
+ html.eos-objects-surface .MuiDialog-root select,
3511
+ html.eos-objects-surface .MuiMenuItem-root,
3512
+ html.eos-objects-surface .MuiAutocomplete-option {
3456
3513
  pointer-events: auto !important;
3457
- z-index: 4301 !important;
3458
3514
  }
3459
- html.eos-app .eos-notification-dialog button,
3460
- html.eos-app .eos-notification-dialog [role="button"],
3461
- html.eos-app .MuiDialog-paper:has(#notifications-dialog-close) button,
3462
- html.eos-app .MuiDialog-paper:has(#notifications-dialog-close) [role="button"] {
3463
- pointer-events: auto !important;
3464
- visibility: visible !important;
3515
+ html.eos-app.eos-objects-surface .MuiTooltip-popper,
3516
+ html.eos-objects-surface .MuiTooltip-popper {
3517
+ z-index: 9000 !important;
3518
+ pointer-events: none !important;
3465
3519
  }
3466
-
3467
- /* v37: security text must never show mojibake-like fragments after runtime repair. */
3468
- html.eos-app .eos-security-admin-only-field,
3469
- html.eos-app .eos-settings-dialog {
3470
- unicode-bidi: plaintext;
3520
+ /* Do not let EOS floating help cover the right action column of the object table. */
3521
+ html.eos-app.eos-objects-surface .eos-assist-root,
3522
+ html.eos-app.eos-objects-surface #eos-assist-root,
3523
+ html.eos-objects-surface .eos-assist-root,
3524
+ html.eos-objects-surface #eos-assist-root {
3525
+ right: 150px !important;
3526
+ bottom: 26px !important;
3527
+ z-index: 1200 !important;
3471
3528
  }
@@ -31,7 +31,7 @@
31
31
  rel="stylesheet"
32
32
  href="css/leaflet.css"
33
33
  />
34
- <link rel="stylesheet" href="./css/eos-branding.css?v=37" />
34
+ <link rel="stylesheet" href="./css/eos-branding.css?v=39" />
35
35
  <link
36
36
  rel="manifest"
37
37
  href="manifest.json"
@@ -154,9 +154,10 @@
154
154
  <script type="module" crossorigin src="./assets/index-CQZugZ1z.js"></script>
155
155
  <link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
156
156
  <link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
157
- <script defer src="./js/eos-branding.js?v=37"></script>
158
- <script defer src="./js/eos-security-ui.js?v=37"></script>
159
- <script defer src="./js/eos-assistant.js?v=37"></script>
157
+ <script defer src="./js/eos-branding.js?v=39"></script>
158
+ <script defer src="./js/eos-security-ui.js?v=39"></script>
159
+ <script defer src="./js/eos-assistant.js?v=39"></script>
160
+ <script defer src="./js/eos-objects-state-tools.js?v=39"></script>
160
161
  </head>
161
162
  <body>
162
163
  <noscript>You need to enable JavaScript to run this app.</noscript>
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- window.NEXOWATT_EOS_UI_VERSION = 'v37-notification-backitup-security-text-fix';
4
+ window.NEXOWATT_EOS_UI_VERSION = 'v39-object-state-tools';
5
5
 
6
6
  const BRAND = 'NexoWatt EOS';
7
7
  const EOS_MEANING = 'Energy Operation System';
@@ -405,24 +405,50 @@
405
405
 
406
406
 
407
407
  const releaseNotificationControls = () => safe(() => {
408
- // v37: notification/snackbar close buttons belong to the native Admin UI.
409
- // They must never be disabled or covered by EOS security/layout layers.
410
- document.querySelectorAll('.MuiSnackbar-root, .MuiAlert-root, .MuiSnackbarContent-root, [role="alert"], .Toastify__toast, .notistack-Snackbar').forEach(box => {
408
+ // v38: Keep native snackbar/toast close buttons clickable without touching dialogs,
409
+ // poppers, menus or adapter-owned configuration surfaces. Earlier broad selectors
410
+ // changed generic dialogs and broke React click handlers.
411
+ const roots = [
412
+ '.MuiSnackbar-root',
413
+ '.SnackbarItem-root',
414
+ '.SnackbarItem-wrappedRoot',
415
+ '.notistack-Snackbar',
416
+ '.Toastify__toast-container',
417
+ '.Toastify__toast'
418
+ ].join(',');
419
+ document.querySelectorAll(roots).forEach(box => {
420
+ if (box.closest('.MuiDialog-root,.MuiModal-root,.MuiPopover-root,.MuiPopper-root,.MuiMenu-root,[role="dialog"]')) return;
411
421
  box.classList.add('eos-notification-safe');
412
422
  box.style.pointerEvents = 'auto';
413
- box.querySelectorAll('button, [role="button"], a, .MuiIconButton-root, svg').forEach(control => {
423
+ box.querySelectorAll('button, [role="button"], a, .MuiIconButton-root').forEach(control => {
414
424
  control.classList.remove('eos-protected-delete-control', 'eos-security-hidden-delete');
415
- control.removeAttribute('disabled');
416
- control.removeAttribute('aria-disabled');
417
- if ('disabled' in control) control.disabled = false;
425
+ // Do not remove disabled states globally. Only restore pointer handling.
418
426
  control.style.pointerEvents = 'auto';
419
- control.style.display = '';
420
427
  control.style.visibility = '';
421
428
  control.style.opacity = '';
422
429
  });
423
430
  });
424
431
  });
425
432
 
433
+ const ensurePopupCompatibility = () => safe(() => {
434
+ // v38: All native Admin dialogs, adapter install/autocomplete poppers, menus and
435
+ // adapter-owned modals must stay above EOS decorative layers and keep their React
436
+ // event handlers untouched.
437
+ const selectors = [
438
+ '.MuiDialog-root', '.MuiModal-root', '.MuiPopover-root', '.MuiPopper-root',
439
+ '.MuiMenu-root', '.MuiAutocomplete-popper', '.MuiAutocomplete-listbox',
440
+ '[role="dialog"]', '[role="listbox"]', '[role="menu"]'
441
+ ].join(',');
442
+ document.querySelectorAll(selectors).forEach(el => {
443
+ el.classList.add('eos-native-popup-safe');
444
+ if (el.style) {
445
+ el.style.pointerEvents = 'auto';
446
+ const isFloating = el.matches('.MuiPopover-root,.MuiPopper-root,.MuiMenu-root,.MuiAutocomplete-popper,[role="listbox"],[role="menu"]');
447
+ if (isFloating && !el.closest('.MuiDialog-paper')) el.style.zIndex = '6500';
448
+ }
449
+ });
450
+ });
451
+
426
452
  const protectDeleteDialogs = () => {
427
453
  if (isAdminUser() || state.securityPolicy.restrictProtectedAdapterControls === false) return;
428
454
  const protectedAdapters = state.securityPolicy.protectedAdapters || [];
@@ -836,7 +862,8 @@
836
862
  }
837
863
  patchDrawerHeader(document.querySelector('.MuiDrawer-paper'));
838
864
  hideNativeLogoutNav();
839
- patchNotifications();
865
+ releaseNotificationControls();
866
+ ensurePopupCompatibility();
840
867
  removeLogoutButton();
841
868
  });
842
869
 
@@ -1033,23 +1060,10 @@
1033
1060
 
1034
1061
 
1035
1062
  const patchNotifications = () => safe(() => {
1036
- const selectors = [
1037
- '.MuiSnackbar-root', '.SnackbarItem-root', '.SnackbarItem-wrappedRoot', '.notistack-Snackbar',
1038
- '.Toastify__toast-container', '.Toastify__toast', '.MuiAlert-root', '[role="alert"]'
1039
- ];
1040
- document.querySelectorAll(selectors.join(',')).forEach(node => {
1041
- node.classList.add('eos-notification-surface');
1042
- if (node.style) {
1043
- node.style.pointerEvents = 'auto';
1044
- if (!node.closest('.MuiDialog-root')) node.style.zIndex = '5200';
1045
- }
1046
- node.querySelectorAll('button,[role="button"],a').forEach(control => {
1047
- control.classList.add('eos-notification-action');
1048
- control.style.pointerEvents = 'auto';
1049
- control.style.visibility = 'visible';
1050
- control.style.opacity = '1';
1051
- });
1052
- });
1063
+ // Kept for compatibility with older calls. v38 intentionally scopes this to
1064
+ // snackbar/toast surfaces only; no dialogs, popovers or adapter config controls.
1065
+ releaseNotificationControls();
1066
+ ensurePopupCompatibility();
1053
1067
  });
1054
1068
 
1055
1069
  const applyNavCompactPreference = () => safe(() => {
@@ -1355,10 +1369,11 @@
1355
1369
  ensureRightsHelper();
1356
1370
  ensurePermissionPresets();
1357
1371
  ensureSettingsDialogClasses();
1358
- ensureNotificationDialogClasses();
1372
+ ensurePopupCompatibility();
1359
1373
  hideNativeLogoutNav();
1360
1374
  hideOfficialNexoWattRepoWarning();
1361
- patchNotifications();
1375
+ releaseNotificationControls();
1376
+ ensurePopupCompatibility();
1362
1377
  applySecurityUiGuard();
1363
1378
  if (isAdapterConfigSurface()) {
1364
1379
  // Adapter-owned configuration pages must not be rebranded or structurally patched.
@@ -1394,10 +1409,11 @@
1394
1409
  ensureRightsHelper();
1395
1410
  ensurePermissionPresets();
1396
1411
  ensureSettingsDialogClasses();
1397
- ensureNotificationDialogClasses();
1412
+ ensurePopupCompatibility();
1398
1413
  hideNativeLogoutNav();
1399
1414
  hideOfficialNexoWattRepoWarning();
1400
- patchNotifications();
1415
+ releaseNotificationControls();
1416
+ ensurePopupCompatibility();
1401
1417
  applySecurityUiGuard();
1402
1418
  for (const scope of scopes.slice(0, 80)) {
1403
1419
  if (!scope || !scope.isConnected) continue;
@@ -1474,21 +1490,3 @@
1474
1490
  window.addEventListener('load', () => scheduleFullPatch(0), { once: true });
1475
1491
  window.addEventListener('hashchange', () => scheduleFullPatch(0));
1476
1492
  })();
1477
-
1478
-
1479
- // v37 eos notification close compatibility: never let EOS overlays block native notification dialogs.
1480
- (() => {
1481
- const normalize = value => String(value || '').replace(/\s+/g, ' ').trim();
1482
- document.addEventListener('click', event => {
1483
- const target = event.target?.closest?.('button, [role="button"], a, .MuiButtonBase-root, .MuiIconButton-root');
1484
- if (!target) return;
1485
- const dialog = target.closest?.('.eos-notification-dialog, .MuiDialog-paper, [role="dialog"]');
1486
- if (!dialog || !/benachrichtigungen|notifications|acknowledge|bestätigen|schließen|close/i.test(dialog.textContent || '')) return;
1487
- const label = normalize(`${target.textContent || ''} ${target.getAttribute?.('aria-label') || ''} ${target.getAttribute?.('title') || ''}`);
1488
- if (/schließen|close|bestätigen|acknowledge/i.test(label)) {
1489
- target.style.pointerEvents = 'auto';
1490
- // Do not prevent React handlers; only stop EOS-specific bubbling side effects.
1491
- event.stopPropagation();
1492
- }
1493
- }, true);
1494
- })();
@@ -0,0 +1,167 @@
1
+ (() => {
2
+ 'use strict';
3
+
4
+ window.NEXOWATT_EOS_OBJECTS_STATE_TOOLS_VERSION = 'v39-object-state-tools';
5
+
6
+ const ACTIVE_CLASS = 'eos-objects-surface';
7
+ const safe = fn => { try { return fn(); } catch (e) { return undefined; } };
8
+
9
+ const isObjectsRoute = () => {
10
+ const hash = String(window.location.hash || '');
11
+ if (/#tab-objects/.test(hash)) return true;
12
+ const url = String(window.location.href || '');
13
+ if (/[?&]tab=objects/.test(url)) return true;
14
+ return false;
15
+ };
16
+
17
+ const setSurfaceState = () => {
18
+ const active = isObjectsRoute();
19
+ document.documentElement.classList.toggle(ACTIVE_CLASS, active);
20
+ document.body?.classList.toggle(ACTIVE_CLASS, active);
21
+ return active;
22
+ };
23
+
24
+ const interactiveSelector = [
25
+ 'button',
26
+ '[role="button"]',
27
+ 'a[href]',
28
+ 'input',
29
+ 'select',
30
+ 'textarea',
31
+ '.MuiButton-root',
32
+ '.MuiIconButton-root',
33
+ '.MuiCheckbox-root',
34
+ '.MuiSwitch-root',
35
+ '.MuiSwitch-switchBase',
36
+ '.MuiMenuItem-root',
37
+ '.MuiAutocomplete-option',
38
+ '.MuiSelect-select',
39
+ '.admin-button',
40
+ '.copyButton'
41
+ ].join(',');
42
+
43
+ const nativeLayerSelector = [
44
+ '.MuiDialog-root',
45
+ '.MuiModal-root',
46
+ '.MuiPopover-root',
47
+ '.MuiPopper-root',
48
+ '.MuiTooltip-popper',
49
+ '[role="dialog"]',
50
+ '[role="listbox"]',
51
+ '[role="menu"]',
52
+ '[role="tooltip"]'
53
+ ].join(',');
54
+
55
+ const releaseNativeControls = root => safe(() => {
56
+ if (!setSurfaceState()) return;
57
+ const base = root && root.nodeType ? root : document;
58
+ const nodes = [];
59
+ if (base.nodeType === Node.ELEMENT_NODE && base.matches?.(interactiveSelector)) nodes.push(base);
60
+ nodes.push(...Array.from(base.querySelectorAll?.(interactiveSelector) || []));
61
+ nodes.forEach(el => {
62
+ if (!el || el.closest?.('#eos-assist-root,.eos-assist-root')) return;
63
+ el.classList?.remove('eos-security-hidden-delete', 'eos-protected-delete-control', 'eos-disabled-by-security');
64
+ el.removeAttribute?.('data-eos-security-blocked');
65
+ el.removeAttribute?.('aria-disabled');
66
+ el.style.pointerEvents = 'auto';
67
+ if (el.style.visibility === 'hidden') el.style.visibility = 'visible';
68
+ if (el.style.display === 'none' && !el.classList?.contains('eos-native-logout-hidden')) el.style.display = '';
69
+ });
70
+ const layers = [];
71
+ if (base.nodeType === Node.ELEMENT_NODE && base.matches?.(nativeLayerSelector)) layers.push(base);
72
+ layers.push(...Array.from(base.querySelectorAll?.(nativeLayerSelector) || []));
73
+ layers.forEach(el => {
74
+ if (!el) return;
75
+ el.style.pointerEvents = 'auto';
76
+ });
77
+ });
78
+
79
+ const formatAge = ts => {
80
+ const n = Number(ts);
81
+ if (!Number.isFinite(n) || n <= 0) return 'unbekannt';
82
+ try { return new Date(n).toLocaleString(); } catch { return String(n); }
83
+ };
84
+
85
+ const annotateValueCells = root => safe(() => {
86
+ if (!setSurfaceState()) return;
87
+ const base = root && root.nodeType ? root : document;
88
+ const cells = [];
89
+ if (base.nodeType === Node.ELEMENT_NODE && base.matches?.('.eos-object-value-cell')) cells.push(base);
90
+ cells.push(...Array.from(base.querySelectorAll?.('.eos-object-value-cell') || []));
91
+ cells.forEach(cell => {
92
+ const id = cell.getAttribute('data-eos-object-value-cell') || '';
93
+ const writable = cell.getAttribute('data-eos-object-writable') === '1';
94
+ const visibleValue = (cell.textContent || '').trim().replace(/\s+/g, ' ') || '(leer)';
95
+ cell.style.pointerEvents = 'auto';
96
+ cell.style.cursor = writable ? 'pointer' : 'default';
97
+ cell.classList.toggle('eos-object-value-writable', writable);
98
+ cell.classList.toggle('eos-object-value-readonly', !writable);
99
+ cell.setAttribute('data-eos-current-visible-value', visibleValue);
100
+ const hint = writable
101
+ ? 'Klicken: Wert schreiben / Button sofort testen'
102
+ : 'Nur lesbarer Wert';
103
+ cell.setAttribute('title', [
104
+ id ? `ID: ${id}` : null,
105
+ `Aktueller Wert: ${visibleValue}`,
106
+ `Status: ${writable ? 'beschreibbar' : 'nur lesbar'}`,
107
+ `Letzte Anzeige-Aktualisierung: ${formatAge(Date.now())}`,
108
+ hint,
109
+ 'Zusätzliche Statusdetails zeigt der native EOS/ioBroker-Hover an.'
110
+ ].filter(Boolean).join('\n'));
111
+ });
112
+ });
113
+
114
+ const handlePointerAssist = event => {
115
+ if (!setSurfaceState()) return;
116
+ const cell = event.target?.closest?.('.eos-object-value-cell');
117
+ if (!cell) return;
118
+ if (cell.getAttribute('data-eos-object-writable') === '1') {
119
+ cell.classList.add('eos-object-value-hover');
120
+ }
121
+ };
122
+ const handlePointerLeave = event => {
123
+ const cell = event.target?.closest?.('.eos-object-value-cell');
124
+ cell?.classList.remove('eos-object-value-hover');
125
+ };
126
+
127
+ const run = root => {
128
+ if (!setSurfaceState()) return;
129
+ releaseNativeControls(root);
130
+ annotateValueCells(root);
131
+ };
132
+
133
+ window.addEventListener('hashchange', () => setTimeout(() => run(document), 30));
134
+ window.addEventListener('popstate', () => setTimeout(() => run(document), 30));
135
+ document.addEventListener('mouseover', handlePointerAssist, true);
136
+ document.addEventListener('mouseout', handlePointerLeave, true);
137
+ document.addEventListener('click', event => {
138
+ if (!setSurfaceState()) return;
139
+ const nativeInteractive = event.target?.closest?.(interactiveSelector + ',' + nativeLayerSelector + ',.eos-object-value-cell');
140
+ if (nativeInteractive) {
141
+ // Intentionally do not stop propagation. This keeps the native ObjectBrowser write dialog and button states working.
142
+ releaseNativeControls(nativeInteractive);
143
+ }
144
+ }, false);
145
+
146
+ const observer = new MutationObserver(records => {
147
+ if (!setSurfaceState()) return;
148
+ let roots = [];
149
+ for (const rec of records) {
150
+ rec.addedNodes?.forEach(node => {
151
+ if (node.nodeType === Node.ELEMENT_NODE) roots.push(node);
152
+ });
153
+ }
154
+ if (!roots.length) roots = [document];
155
+ window.requestAnimationFrame?.(() => roots.forEach(run)) || roots.forEach(run);
156
+ });
157
+
158
+ const start = () => {
159
+ setSurfaceState();
160
+ run(document);
161
+ observer.observe(document.documentElement, { childList: true, subtree: true });
162
+ window.setInterval(() => run(document), 2500);
163
+ };
164
+
165
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once: true });
166
+ else start();
167
+ })();
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- window.NEXOWATT_EOS_RUNTIME_FIXES_VERSION = 'v37-notification-backitup-security-text-fix';
4
+ window.NEXOWATT_EOS_RUNTIME_FIXES_VERSION = 'v39-object-state-tools';
5
5
 
6
6
  const MOJIBAKE_MAP = new Map(Object.entries({
7
7
  'dürfen': 'dürfen', 'Dürfen': 'Dürfen',
@@ -78,9 +78,9 @@
78
78
 
79
79
  const isNotificationSurface = el => !!el?.closest?.([
80
80
  '.MuiSnackbar-root',
81
- '.MuiAlert-root',
82
- '[role="alert"]',
83
- '[role="status"]',
81
+ '.SnackbarItem-root',
82
+ '.SnackbarItem-wrappedRoot',
83
+ '.notistack-Snackbar',
84
84
  '.Toastify__toast',
85
85
  '.eos-notification-safe'
86
86
  ].join(','));
@@ -107,9 +107,9 @@
107
107
  const base = root && root.nodeType ? root : document;
108
108
  const surfaces = Array.from(base.querySelectorAll?.([
109
109
  '.MuiSnackbar-root',
110
- '.MuiAlert-root',
111
- '[role="alert"]',
112
- '[role="status"]',
110
+ '.SnackbarItem-root',
111
+ '.SnackbarItem-wrappedRoot',
112
+ '.notistack-Snackbar',
113
113
  '.Toastify__toast'
114
114
  ].join(',')) || []);
115
115
  if (base.nodeType === Node.ELEMENT_NODE && isNotificationSurface(base)) surfaces.push(base);
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v37-security-text-polish';
4
+ const VERSION = 'v39-object-state-tools';
5
5
  const LEGACY_ADMIN = 'admin';
6
6
  const LEGACY_ADMIN_INSTANCE = 'admin.0';
7
7
  const ASSET_BASE = (() => {
@@ -246,18 +246,19 @@
246
246
  const isAdapterConfigSurface = () => document.documentElement.classList.contains('eos-adapter-config-surface') || /Instanzeinstellungen:|Instance settings:|Geräteliste|Gerät hinzufügen|Gerät bearbeiten/i.test(document.body?.textContent || '');
247
247
 
248
248
  const releaseNotificationControls = () => {
249
- // Never block notification/toast close actions. These controls are owned by
250
- // the native Admin UI and must remain clickable regardless of EOS security rules.
251
- document.querySelectorAll('.MuiSnackbar-root, .MuiAlert-root, .MuiSnackbarContent-root, [role="alert"], .Toastify__toast, .notistack-Snackbar').forEach(box => {
249
+ // v38: security UI must not modify generic dialogs or adapter popups.
250
+ // Only snackbar/toast surfaces are normalized for clickability.
251
+ const roots = [
252
+ '.MuiSnackbar-root', '.SnackbarItem-root', '.SnackbarItem-wrappedRoot',
253
+ '.notistack-Snackbar', '.Toastify__toast-container', '.Toastify__toast'
254
+ ].join(',');
255
+ document.querySelectorAll(roots).forEach(box => {
256
+ if (box.closest('.MuiDialog-root,.MuiModal-root,.MuiPopover-root,.MuiPopper-root,.MuiMenu-root,[role="dialog"]')) return;
252
257
  box.classList.add('eos-notification-safe');
253
258
  box.style.pointerEvents = 'auto';
254
259
  box.querySelectorAll('button, [role="button"], a, .MuiIconButton-root').forEach(control => {
255
260
  control.classList.remove('eos-protected-delete-control', 'eos-security-hidden-delete');
256
- control.removeAttribute('disabled');
257
- control.removeAttribute('aria-disabled');
258
- if ('disabled' in control) control.disabled = false;
259
261
  control.style.pointerEvents = 'auto';
260
- control.style.display = '';
261
262
  control.style.visibility = '';
262
263
  });
263
264
  });
@@ -301,6 +302,8 @@
301
302
  document.addEventListener('click', event => {
302
303
  const target = event.target?.closest?.('button,[role="button"],a,[role="menuitem"],.MuiMenuItem-root');
303
304
  if (!target || isAdminUser() || isAdapterConfigSurface()) return;
305
+ // Native Admin dialogs, install/autocomplete poppers and adapter-owned menus must stay untouched.
306
+ if (target.closest?.('.MuiDialog-root,.MuiModal-root,.MuiPopover-root,.MuiPopper-root,.MuiMenu-root,.MuiAutocomplete-popper,[role="dialog"],[role="listbox"],[role="menu"]')) return;
304
307
  const label = normalizeFlat(`${target.textContent || ''} ${target.getAttribute?.('title') || ''} ${target.getAttribute?.('aria-label') || ''}`);
305
308
  if (/loschen|delete|remove|deinstall|uninstall/.test(label)) {
306
309
  target.classList.add('eos-security-hidden-delete');