iobroker.eos-admin 7.10.0 → 7.10.2

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.
Files changed (65) hide show
  1. package/INSTALL_TEST_V7.10.2.md +22 -0
  2. package/MERGE_UPDATE_README_V7.10.2.md +11 -0
  3. package/NEXOWATT_EOS_BUILD_INFO.json +19 -21
  4. package/NEXOWATT_EOS_PREBUILT_MANIFEST.json +1410 -0
  5. package/PUBLISH_STABLE_V7.10.2.md +21 -0
  6. package/README.md +16 -6
  7. package/README_STABILITY_V7.10.2.md +20 -0
  8. package/RELEASE_ACCEPTANCE_V7.10.2.md +13 -0
  9. package/RELEASE_NOTES_V7.10.2.md +11 -0
  10. package/adminWww/assets/Intro-DkwRiz1n-v84.js +1 -1
  11. package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
  12. package/adminWww/css/eos-ems-overview.css +5 -3
  13. package/adminWww/css/nexowatt-native-shell.css +85 -413
  14. package/adminWww/index.html +20 -21
  15. package/adminWww/js/eos-account-management.js +28 -19
  16. package/adminWww/js/eos-assistant.js +1 -1
  17. package/adminWww/js/eos-ems-overview.js +1 -1
  18. package/adminWww/js/eos-role-bootstrap.js +17 -80
  19. package/adminWww/js/eos-role-ui.js +28 -4
  20. package/adminWww/js/nexowatt-native-shell.js +1 -1
  21. package/adminWww/static/js/nexowatt-stable-login.js +43 -70
  22. package/adminWww/static/js/nexowatt-stable-v7101.js +27 -0
  23. package/build/lib/eosAutoUpdate.js +418 -20
  24. package/build/lib/eosAutoUpdate.js.map +1 -0
  25. package/build/lib/eosRequestSecurity.js +4 -1
  26. package/build/lib/eosRequestSecurity.js.map +1 -0
  27. package/build/lib/web.js +161 -254
  28. package/build/lib/web.js.map +1 -1
  29. package/build/main.js +39 -67
  30. package/io-package.json +72 -7
  31. package/package.json +24 -25
  32. package/tools/nexowatt-account-management-selftest.cjs +15 -19
  33. package/tools/nexowatt-account-password-flow-selftest.cjs +15 -0
  34. package/tools/nexowatt-apply-ems-overview.cjs +4 -2
  35. package/tools/nexowatt-auto-update-selftest.cjs +144 -32
  36. package/tools/nexowatt-backend-runtime-selftest.cjs +147 -0
  37. package/tools/nexowatt-clean-legacy-runtime.cjs +1 -1
  38. package/tools/nexowatt-ems-overview-selftest.cjs +1 -1
  39. package/tools/nexowatt-ensure-release-defaults.cjs +1 -1
  40. package/tools/nexowatt-login-layout-selftest.cjs +9 -13
  41. package/tools/nexowatt-merge-update-selftest.cjs +7 -1
  42. package/tools/nexowatt-modern-ui-selftest.cjs +10 -46
  43. package/tools/nexowatt-password-write-selftest.cjs +46 -16
  44. package/tools/nexowatt-prebuilt-release-selftest.cjs +292 -0
  45. package/tools/nexowatt-release-defaults-selftest.cjs +1 -1
  46. package/tools/nexowatt-runtime-cleanup-selftest.cjs +6 -8
  47. package/tools/nexowatt-stable-v7102-selftest.cjs +51 -0
  48. package/tools/nexowatt-sync-release-version.cjs +43 -5
  49. package/tools/nexowatt-ui-overview-runtime-selftest.cjs +2 -3
  50. package/tools/nexowatt-validate-package.cjs +54 -29
  51. package/tools/nexowatt-version-sync-selftest.cjs +18 -1
  52. package/INSTALL_TEST_V7.10.0.md +0 -11
  53. package/MERGE_UPDATE_README_V7.10.0.md +0 -5
  54. package/PUBLISH_STABLE_V7.10.0.md +0 -15
  55. package/README_STABILITY_V7.10.0.md +0 -3
  56. package/RELEASE_ACCEPTANCE_V7.10.0.md +0 -11
  57. package/RELEASE_NOTES_V7.10.0.md +0 -17
  58. package/adminWww/js/eos-release-watch.js +0 -77
  59. package/adminWww/static/js/nexowatt-stable-v7100.js +0 -111
  60. package/build/lib/eosPassword.js +0 -72
  61. package/build/lib/nexowattStableAuth.js +0 -74
  62. package/src/lib/eosAutoUpdate.ts +0 -320
  63. package/tools/nexowatt-cache-update-selftest.cjs +0 -12
  64. package/tools/nexowatt-request-security-selftest.cjs +0 -11
  65. package/tools/nexowatt-stable-v7100-selftest.cjs +0 -17
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v7100-account-management-password-write';
4
+ const VERSION = 'v98-account-management-real-password-write';
5
5
  const script = document.currentScript || document.querySelector('script[src*="eos-account-management.js"]');
6
6
  const base = new URL('../', script?.src || window.location.href);
7
7
  const abort = new AbortController();
@@ -19,11 +19,10 @@
19
19
  introInstaller: 'Du kannst Endkundenzugänge zurücksetzen. Admin- und Installateurzugänge bleiben NexoWatt Service vorbehalten.',
20
20
  loading: 'Zugänge werden geladen …', empty: 'Keine passenden Konten gefunden.', close: 'Schließen', refresh: 'Aktualisieren',
21
21
  installer: 'Installateur', enduser: 'Endkunde / Gast', active: 'Passwort aktiv', first: 'Erstanmeldung offen', disabled: 'Deaktiviert',
22
- reset: 'Passwort zurücksetzen', resetting: 'Wird zurückgesetzt …', resetConfirm: name => `Soll der Zugang „${name}“ wirklich zurückgesetzt werden? Danach gilt einmalig das Startpasswort „nexowatt“ und anschließend muss ein persönliches Passwort vergeben werden.`,
23
- resetDone: name => `Der Zugang „${name}“ wurde im EOS-Benutzerkonto auf „nexowatt“ zurückgesetzt. Nach der nächsten Anmeldung muss sofort ein persönliches Passwort vergeben werden.`,
24
- resetFailed: 'Der Zugang konnte nicht zurückgesetzt werden.', lastSet: 'Letzte Passwortvergabe', lastReset: 'Letzter Reset', by: 'durch',
22
+ reset: 'Passwort zurücksetzen', resetting: 'Wird zurückgesetzt …', resetConfirm: name => `Soll der Zugang „${name}“ wirklich zurückgesetzt werden? Das bisherige Passwort wird ungültig. Danach gilt einmalig das Startpasswort „nexowatt“ und beim nächsten Login muss sofort ein persönliches Passwort vergeben werden.`,
23
+ resetDone: name => `Der Zugang „${name}“ ist zurückgesetzt. Einmalig mit dem Startpasswort „nexowatt“ anmelden und anschließend direkt ein persönliches Passwort festlegen.`,
24
+ resetFailed: 'Der Zugang konnte nicht zurückgesetzt werden.', permissionError: 'Für dieses Konto ist das Zurücksetzen nicht erlaubt.', originError: 'Die Sicherheitsprüfung der Anfrage ist fehlgeschlagen. Seite mit Strg+F5 neu laden.', disabledError: 'Das Konto ist deaktiviert und kann nicht zurückgesetzt werden.', notFoundError: 'Das Konto wurde nicht gefunden.', writeError: 'Das neue Startpasswort konnte nicht in das Benutzerkonto geschrieben werden.', lastSet: 'Letzte Passwortvergabe', lastReset: 'Letzter Reset', by: 'durch',
25
25
  security: 'Admin/Service darf Installateur und Endkunde zurücksetzen. Installateure dürfen ausschließlich Endkunden zurücksetzen.',
26
- errors: { permissionError: 'Die Admin-/Installateurberechtigung konnte nicht bestätigt werden. Bitte neu anmelden.', invalidRequest: 'Die Sicherheitsbestätigung fehlt. Bitte die Seite neu laden.', invalidRequestOrigin: 'Die Sitzung oder Adresse hat sich geändert. Bitte die Seite neu laden.', invalidTarget: 'Dieses Konto darf nicht über EOS zurückgesetzt werden.', accountDisabled: 'Das Konto ist deaktiviert.', accountNotFound: 'Das Benutzerkonto wurde nicht gefunden.', passwordApiUnavailable: 'Die EOS-Passwortverwaltung ist nicht verfügbar.', passwordNotPersisted: 'Das Startpasswort wurde nicht gespeichert.', passwordVerificationFailed: 'Das Startpasswort wurde geschrieben, aber nicht als gültige Anmeldung bestätigt.', passwordMetadataNotPersisted: 'Der Erstanmeldungsstatus wurde nicht gespeichert.', accountResetFailed: 'Das Konto konnte serverseitig nicht zurückgesetzt werden.' },
27
26
  },
28
27
  en: {
29
28
  launcher: 'Accounts', titleAdmin: 'Accounts & roles', titleInstaller: 'End-user accounts',
@@ -31,11 +30,10 @@
31
30
  introInstaller: 'You may reset end-user accounts. Admin and installer accounts remain reserved for NexoWatt Service.',
32
31
  loading: 'Loading accounts …', empty: 'No matching accounts found.', close: 'Close', refresh: 'Refresh',
33
32
  installer: 'Installer', enduser: 'End user / Guest', active: 'Password active', first: 'First activation open', disabled: 'Disabled',
34
- reset: 'Reset password', resetting: 'Resetting …', resetConfirm: name => `Reset account “${name}”? The one-time initial password will be “nexowatt”; a personal password must then be created immediately.`,
35
- resetDone: name => `Account “${name}” was reset in the EOS user account to “nexowatt”. The next sign-in requires an immediate personal password change.`,
36
- resetFailed: 'The account could not be reset.', lastSet: 'Last password setup', lastReset: 'Last reset', by: 'by',
33
+ reset: 'Reset password', resetting: 'Resetting …', resetConfirm: name => `Reset account “${name}”? The current password becomes invalid. Sign in once with the initial password “nexowatt”, then create a personal password immediately.`,
34
+ resetDone: name => `Account “${name}” was reset. Sign in once with “nexowatt” and then create a personal password.`,
35
+ resetFailed: 'The account could not be reset.', permissionError: 'Reset is not permitted for this account.', originError: 'The request security check failed. Reload the page with Ctrl+F5.', disabledError: 'The account is disabled and cannot be reset.', notFoundError: 'The account was not found.', writeError: 'The initial password could not be written to the user account.', lastSet: 'Last password setup', lastReset: 'Last reset', by: 'by',
37
36
  security: 'Admin/Service may reset installer and end-user accounts. Installers may reset end-user accounts only.',
38
- errors: { permissionError: 'Admin/Installer permission could not be confirmed. Sign in again.', invalidRequest: 'The security confirmation is missing. Reload the page.', invalidRequestOrigin: 'The session or address changed. Reload the page.', invalidTarget: 'This account cannot be reset through EOS.', accountDisabled: 'The account is disabled.', accountNotFound: 'The user account was not found.', passwordApiUnavailable: 'The EOS password service is unavailable.', passwordNotPersisted: 'The initial password was not saved.', passwordVerificationFailed: 'The initial password was written but could not be verified.', passwordMetadataNotPersisted: 'The first-login status was not saved.', accountResetFailed: 'The account could not be reset on the server.' },
39
37
  },
40
38
  nl: {
41
39
  launcher: 'Toegangen', titleAdmin: 'Toegangen & rollen', titleInstaller: 'Eindgebruikerstoegangen',
@@ -43,15 +41,26 @@
43
41
  introInstaller: 'Je kunt eindgebruikersaccounts resetten. Admin- en installateurstoegang blijft voor NexoWatt Service.',
44
42
  loading: 'Toegangen worden geladen …', empty: 'Geen passende accounts gevonden.', close: 'Sluiten', refresh: 'Vernieuwen',
45
43
  installer: 'Installateur', enduser: 'Eindgebruiker / Guest', active: 'Wachtwoord actief', first: 'Eerste activering open', disabled: 'Uitgeschakeld',
46
- reset: 'Wachtwoord resetten', resetting: 'Wordt gereset …', resetConfirm: name => `Account „${name}“ resetten? Eenmalig geldt het startwachtwoord „nexowatt“, waarna direct een persoonlijk wachtwoord moet worden ingesteld.`,
47
- resetDone: name => `Account „${name}“ is in het EOS-gebruikersaccount teruggezet op „nexowatt“. Bij de volgende aanmelding moet direct een persoonlijk wachtwoord worden ingesteld.`,
48
- resetFailed: 'Het account kon niet worden gereset.', lastSet: 'Laatste wachtwoordinstelling', lastReset: 'Laatste reset', by: 'door',
44
+ reset: 'Wachtwoord resetten', resetting: 'Wordt gereset …', resetConfirm: name => `Account „${name}“ resetten? Het huidige wachtwoord wordt ongeldig. Meld één keer aan met het startwachtwoord „nexowatt“ en stel daarna direct een persoonlijk wachtwoord in.`,
45
+ resetDone: name => `Account „${name}“ is gereset. Meld één keer aan met „nexowatt“ en stel daarna een persoonlijk wachtwoord in.`,
46
+ resetFailed: 'Het account kon niet worden gereset.', permissionError: 'Resetten is voor dit account niet toegestaan.', originError: 'De veiligheidscontrole van het verzoek is mislukt. Herlaad de pagina met Ctrl+F5.', disabledError: 'Het account is uitgeschakeld en kan niet worden gereset.', notFoundError: 'Het account werd niet gevonden.', writeError: 'Het startwachtwoord kon niet naar het gebruikersaccount worden geschreven.', lastSet: 'Laatste wachtwoordinstelling', lastReset: 'Laatste reset', by: 'door',
49
47
  security: 'Admin/service mag installateur- en eindgebruikersaccounts resetten. Installateurs alleen eindgebruikers.',
50
- errors: { permissionError: 'De Admin-/Installateurrechten konden niet worden bevestigd. Meld opnieuw aan.', invalidRequest: 'De beveiligingsbevestiging ontbreekt. Herlaad de pagina.', invalidRequestOrigin: 'De sessie of het adres is gewijzigd. Herlaad de pagina.', invalidTarget: 'Dit account kan niet via EOS worden gereset.', accountDisabled: 'Het account is uitgeschakeld.', accountNotFound: 'Het gebruikersaccount is niet gevonden.', passwordApiUnavailable: 'De EOS-wachtwoordservice is niet beschikbaar.', passwordNotPersisted: 'Het startwachtwoord is niet opgeslagen.', passwordVerificationFailed: 'Het startwachtwoord is geschreven maar kon niet worden geverifieerd.', passwordMetadataNotPersisted: 'De eerste-aanmeldstatus is niet opgeslagen.', accountResetFailed: 'Het account kon server-side niet worden gereset.' },
51
48
  },
52
49
  };
53
50
  const t = () => i18n[language()];
54
- const errorText = code => t().errors?.[String(code || '')] || String(code || t().resetFailed);
51
+ const accountErrorText = code => {
52
+ const text = t();
53
+ return ({
54
+ permissionError: text.permissionError,
55
+ invalidRequestOrigin: text.originError,
56
+ accountDisabled: text.disabledError,
57
+ accountNotFound: text.notFoundError,
58
+ passwordApiUnavailable: text.writeError,
59
+ passwordWriteFailed: text.writeError,
60
+ passwordVerificationFailed: text.writeError,
61
+ userObjectUnavailableAfterPasswordChange: text.writeError,
62
+ })[String(code || '')] || text.resetFailed;
63
+ };
55
64
  const roleFromPolicy = policy => {
56
65
  const raw = normalize(policy?.role || '');
57
66
  if (policy?.isAdmin || policy?.isEosAdminGroup || /admin|service/.test(raw)) return 'admin';
@@ -148,17 +157,17 @@
148
157
  reset.disabled = true; reset.textContent = t().resetting; setStatus('', '');
149
158
  try {
150
159
  const response = await fetch(new URL('nexowatt/account/reset', base).href, {
151
- method: 'POST', credentials: 'same-origin', cache: 'no-store',
160
+ method: 'POST', credentials: 'include', cache: 'no-store',
152
161
  headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-Account-Reset': '1' },
153
162
  body: JSON.stringify({ user: account.id }),
154
163
  });
155
164
  const payload = await response.json().catch(() => ({}));
156
- if (!response.ok || payload.error) throw new Error(payload.error || t().resetFailed);
165
+ if (!response.ok || payload.error) throw new Error(accountErrorText(payload.error));
157
166
  setStatus(t().resetDone(label), 'success');
158
167
  await loadAccounts();
159
168
  } catch (error) {
160
169
  reset.disabled = false; reset.textContent = t().reset;
161
- setStatus(errorText(error?.message), 'error');
170
+ setStatus(error?.message || t().resetFailed, 'error');
162
171
  }
163
172
  });
164
173
  actions.appendChild(reset);
@@ -180,12 +189,12 @@
180
189
  if (!state.overlay) return;
181
190
  setStatus(t().loading, '');
182
191
  try {
183
- const response = await fetch(new URL('nexowatt/account/manage', base).href, { credentials: 'same-origin', cache: 'no-store', headers: { Accept: 'application/json' } });
192
+ const response = await fetch(new URL('nexowatt/account/manage', base).href, { credentials: 'include', cache: 'no-store', headers: { Accept: 'application/json' } });
184
193
  const data = await response.json().catch(() => ({}));
185
194
  if (!response.ok || data.error) throw new Error(data.error || t().resetFailed);
186
195
  renderAccounts(data); setStatus('', '');
187
196
  } catch (error) {
188
- setStatus(errorText(error?.message), 'error');
197
+ setStatus(error?.message || t().resetFailed, 'error');
189
198
  }
190
199
  };
191
200
 
@@ -1,6 +1,6 @@
1
1
  (() => {
2
2
  'use strict';
3
- // NexoWatt EOS stable 7.10.0: EOS Assist remains disabled until the
3
+ // NexoWatt EOS stable 7.9.97: EOS Assist remains disabled until the
4
4
  // read-only system assistant is rebuilt and accepted separately.
5
5
  window.NEXOWATT_EOS_ASSIST_DISABLED = true;
6
6
  document.documentElement.classList.add('eos-assist-disabled');
@@ -1,6 +1,6 @@
1
1
  (() => {
2
2
  'use strict';
3
- const VERSION = 'v7100-nexowatt-ems-overview-runtime';
3
+ const VERSION = 'v98-nexowatt-ems-overview-runtime';
4
4
  const CONTRACT = 'nexowatt-ems-overview-v1';
5
5
  const ROOT_ID = 'eos-ems-overview-runtime';
6
6
  const POLL_MS = 5000;
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v7100-first-login-password-persisted';
4
+ const VERSION = 'v7101-standard-password-native-overview';
5
5
  const script = document.currentScript || document.querySelector('script[src*="eos-role-bootstrap.js"]');
6
6
  const entry = script?.dataset?.eosEntry || '';
7
7
  let launched = false;
@@ -80,7 +80,6 @@
80
80
  required: 'Bitte beide Passwortfelder ausfüllen.', mismatch: 'Die Passwörter stimmen nicht überein.',
81
81
  length: min => `Das Passwort muss mindestens ${min} Zeichen lang sein.`, complexity: 'Groß-/Kleinbuchstabe, Zahl und Sonderzeichen sind erforderlich.',
82
82
  easy: 'Das Passwort ist zu leicht oder enthält den Kontonamen.', generic: 'Das Passwort konnte nicht gespeichert werden.', success: 'Passwort gespeichert. Die Anmeldung wird neu gestartet …',
83
- backendErrors: { invalidRequest: 'Die Sicherheitsbestätigung fehlt. Bitte erneut anmelden.', invalidRequestOrigin: 'Die Sitzung oder aufgerufene Adresse hat sich geändert. Bitte erneut anmelden.', passwordApiUnavailable: 'Die ioBroker-Passwortverwaltung ist nicht verfügbar.', passwordNotPersisted: 'Das Passwort wurde vom ioBroker-Controller nicht gespeichert. Bitte Systemlog prüfen.', passwordVerificationFailed: 'Das neue Passwort wurde geschrieben, aber nicht als gültige Anmeldung bestätigt.', passwordMetadataNotPersisted: 'Das Passwort wurde gespeichert, aber der Erstanmeldungsstatus konnte nicht abgeschlossen werden.', passwordSetupFailed: 'Das Passwort konnte serverseitig nicht gespeichert werden. Bitte das EOS-Systemlog prüfen.', passwordSetupTokenExpired: 'Die sichere Passwortfreigabe ist abgelaufen. Bitte erneut anmelden.', passwordSetupTokenMismatch: 'Die Passwortfreigabe gehört nicht zu dieser Sitzung. Bitte erneut anmelden.' },
84
83
  securityTitle: 'Zugriff wird geprüft', securityIntro: 'Die sichere Anmeldung wird gerade geprüft. Die Oberfläche bleibt gesperrt, bis die Berechtigungen eindeutig geladen wurden.',
85
84
  securityRetry: 'Jetzt erneut prüfen', securityLogout: 'Zur Anmeldung', securityAuto: seconds => `Automatischer neuer Versuch in ${seconds} Sekunden …`,
86
85
  claimLauncher: 'Erstanmeldung', claimTitle: 'Konto erstmals aktivieren',
@@ -100,7 +99,6 @@
100
99
  required: 'Please complete both password fields.', mismatch: 'The passwords do not match.',
101
100
  length: min => `The password must contain at least ${min} characters.`, complexity: 'Upper/lower case, a number and a special character are required.',
102
101
  easy: 'The password is too easy or contains the account name.', generic: 'The password could not be saved.', success: 'Password saved. Restarting sign-in …',
103
- backendErrors: { invalidRequest: 'The security confirmation is missing. Sign in again.', invalidRequestOrigin: 'The session or address changed. Sign in again.', passwordApiUnavailable: 'The ioBroker password service is unavailable.', passwordNotPersisted: 'The ioBroker controller did not persist the password. Check the system log.', passwordVerificationFailed: 'The new password was written but could not be verified as a valid login.', passwordMetadataNotPersisted: 'The password was saved, but the first-login status could not be completed.', passwordSetupFailed: 'The password could not be saved on the server. Check the EOS system log.', passwordSetupTokenExpired: 'The secure password setup ticket expired. Sign in again.', passwordSetupTokenMismatch: 'The password setup ticket does not belong to this session. Sign in again.' },
104
102
  securityTitle: 'Checking access', securityIntro: 'The secure sign-in is being verified. The interface stays locked until the permissions are loaded unambiguously.',
105
103
  securityRetry: 'Check again now', securityLogout: 'Return to sign-in', securityAuto: seconds => `Retrying automatically in ${seconds} seconds …`,
106
104
  claimLauncher: 'First sign-in', claimTitle: 'Activate account for the first time',
@@ -120,7 +118,6 @@
120
118
  required: 'Vul beide wachtwoordvelden in.', mismatch: 'De wachtwoorden zijn niet gelijk.',
121
119
  length: min => `Het wachtwoord moet minimaal ${min} tekens bevatten.`, complexity: 'Hoofdletter, kleine letter, cijfer en speciaal teken zijn verplicht.',
122
120
  easy: 'Het wachtwoord is te eenvoudig of bevat de accountnaam.', generic: 'Het wachtwoord kon niet worden opgeslagen.', success: 'Wachtwoord opgeslagen. De aanmelding wordt opnieuw gestart …',
123
- backendErrors: { invalidRequest: 'De beveiligingsbevestiging ontbreekt. Meld opnieuw aan.', invalidRequestOrigin: 'De sessie of het adres is gewijzigd. Meld opnieuw aan.', passwordApiUnavailable: 'De ioBroker-wachtwoordservice is niet beschikbaar.', passwordNotPersisted: 'De ioBroker-controller heeft het wachtwoord niet opgeslagen. Controleer het systeemlogboek.', passwordVerificationFailed: 'Het nieuwe wachtwoord is geschreven maar kon niet als geldige aanmelding worden bevestigd.', passwordMetadataNotPersisted: 'Het wachtwoord is opgeslagen, maar de eerste-aanmeldstatus kon niet worden afgerond.', passwordSetupFailed: 'Het wachtwoord kon server-side niet worden opgeslagen. Controleer het EOS-systeemlogboek.', passwordSetupTokenExpired: 'De beveiligde wachtwoordtoestemming is verlopen. Meld opnieuw aan.', passwordSetupTokenMismatch: 'De wachtwoordtoestemming hoort niet bij deze sessie. Meld opnieuw aan.' },
124
121
  securityTitle: 'Toegang wordt gecontroleerd', securityIntro: 'De veilige aanmelding wordt gecontroleerd. De interface blijft vergrendeld totdat de rechten eenduidig zijn geladen.',
125
122
  securityRetry: 'Nu opnieuw controleren', securityLogout: 'Terug naar aanmelden', securityAuto: seconds => `Automatisch opnieuw proberen over ${seconds} seconden …`,
126
123
  claimLauncher: 'Eerste aanmelding', claimTitle: 'Account voor het eerst activeren',
@@ -133,32 +130,11 @@
133
130
  },
134
131
  };
135
132
 
136
- const waitForPasswordSocket = async (timeoutMs = 10000) => {
137
- const started = Date.now();
138
- while (Date.now() - started < timeoutMs) {
139
- const socket = window.NEXOWATT_EOS_ADMIN_SOCKET || window.NEXOWATT_EOS_SOCKET;
140
- if (socket && typeof socket.changePassword === 'function') return socket;
141
- await new Promise(resolve => window.setTimeout(resolve, 120));
142
- }
143
- return null;
144
- };
145
-
146
- const postWithTimeout = async (url, options, timeoutMs = 25000) => {
147
- const controller = new AbortController();
148
- const timer = window.setTimeout(() => controller.abort(), timeoutMs);
149
- try {
150
- return await fetch(url, { ...options, signal: controller.signal });
151
- } finally {
152
- window.clearTimeout(timer);
153
- }
154
- };
155
-
156
133
  const showFirstLoginPassword = (policy, base) => {
157
134
  const t = translations[language()];
158
135
  const passwordSetup = policy?.passwordSetup || {};
159
136
  const minLength = Number(passwordSetup.minLength || 10);
160
137
  const userName = String(passwordSetup.userName || policy?.user || '').replace(/^system\.user\./, '') || 'user';
161
- const setupToken = String(passwordSetup.setupToken || '');
162
138
  const endpoint = new URL('nexowatt/account/first-password', base).href;
163
139
  const logoutUrl = new URL('logout', base).href;
164
140
  window.NEXOWATT_EOS_PASSWORD_SETUP_ACTIVE = true;
@@ -215,49 +191,21 @@
215
191
  if (!/[a-z]/.test(password.value) || !/[A-Z]/.test(password.value) || !/\d/.test(password.value) || !/[^A-Za-z0-9]/.test(password.value)) return setStatus(t.complexity, 'error');
216
192
  submit.disabled = true; submit.textContent = t.saving; setStatus('', '');
217
193
  try {
218
- const socket = await waitForPasswordSocket();
219
- let passwordAlreadyWritten = false;
220
- if (socket) {
221
- try {
222
- // Use the same native path as the working ioBroker user editor.
223
- await socket.changePassword(`system.user.${userName}`, password.value);
224
- passwordAlreadyWritten = true;
225
- } catch (socketError) {
226
- console.warn('[NexoWatt EOS] Native password write failed; using server fallback:', socketError);
227
- }
228
- }
229
- const response = await postWithTimeout(endpoint, {
230
- method: 'POST', credentials: 'same-origin', cache: 'no-store',
231
- headers: {
232
- 'Content-Type': 'application/json',
233
- Accept: 'application/json',
234
- 'X-NexoWatt-EOS-First-Login': '1',
235
- ...(setupToken ? { 'X-NexoWatt-EOS-Password-Setup-Token': setupToken } : {}),
236
- },
237
- body: JSON.stringify({
238
- password: password.value,
239
- passwordRepeat: repeat.value,
240
- setupToken,
241
- passwordAlreadyWritten,
242
- }),
194
+ const response = await fetch(endpoint, {
195
+ method: 'POST', credentials: 'include', cache: 'no-store',
196
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-First-Login': '1' },
197
+ body: JSON.stringify({ password: password.value, passwordRepeat: repeat.value }),
243
198
  });
244
199
  const data = await response.json().catch(() => ({}));
245
200
  if (!response.ok || data.error) {
246
- const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy, ...(t.backendErrors || {}) };
201
+ const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy, passwordSetupNotAllowed: t.generic, invalidRequestOrigin: t.generic, passwordApiUnavailable: t.generic, passwordWriteFailed: t.generic, passwordVerificationFailed: t.generic, userObjectUnavailableAfterPasswordChange: t.generic };
247
202
  throw new Error(messages[data.error] || t.generic);
248
203
  }
249
204
  setStatus(t.success, 'success');
250
205
  window.setTimeout(() => window.location.replace(logoutUrl), 650);
251
206
  } catch (error) {
252
207
  submit.disabled = false; submit.textContent = t.submit;
253
- const message = error?.name === 'AbortError'
254
- ? (language() === 'de'
255
- ? 'Das Speichern hat zu lange gedauert. Bitte erneut versuchen.'
256
- : language() === 'nl'
257
- ? 'Opslaan duurde te lang. Probeer opnieuw.'
258
- : 'Saving timed out. Please try again.')
259
- : error?.message || t.generic;
260
- setStatus(message, 'error');
208
+ setStatus(error?.message || t.generic, 'error');
261
209
  }
262
210
  });
263
211
  window.setTimeout(() => password.focus(), 0);
@@ -317,13 +265,13 @@
317
265
  submit.disabled = true; submit.textContent = t.saving; setStatus('', '');
318
266
  try {
319
267
  const response = await fetch(endpoint, {
320
- method: 'POST', credentials: 'same-origin', cache: 'no-store',
268
+ method: 'POST', credentials: 'include', cache: 'no-store',
321
269
  headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-Passwordless-Password': '1' },
322
270
  body: JSON.stringify({ password: password.value, passwordRepeat: repeat.value }),
323
271
  });
324
272
  const data = await response.json().catch(() => ({}));
325
273
  if (!response.ok || data.error) {
326
- const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy, claimExpired: t.claimExpired, ...(t.backendErrors || {}) };
274
+ const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy, claimExpired: t.claimExpired };
327
275
  throw new Error(messages[data.error] || t.generic);
328
276
  }
329
277
  overlay.querySelector('.eos-activation-steps span:nth-of-type(2)')?.classList.add('done');
@@ -367,17 +315,9 @@
367
315
  const loginElements = () => {
368
316
  const username = document.querySelector('input#username,input[name="username"]');
369
317
  const password = document.querySelector('input#password,input[name="password"]');
370
- const form = username?.closest?.('form') || password?.closest?.('form');
371
- const card = form?.closest?.('.MuiPaper-root')
372
- || username?.closest?.('.MuiPaper-root')
373
- || password?.closest?.('.MuiPaper-root')
374
- || form
375
- || username?.closest?.('main,section')
376
- || password?.closest?.('main,section');
377
- const submit = form?.querySelector?.('button[type="submit"]')
378
- || card?.querySelector?.('button[type="submit"]')
379
- || Array.from(card?.querySelectorAll?.('button') || []).find(button => /anmelden|login|sign in|aanmelden/.test(normalize(button.textContent || '')));
380
- return { username, password, form, card, submit };
318
+ const card = username?.closest?.('.MuiPaper-root,form,main,section') || password?.closest?.('.MuiPaper-root,form,main,section');
319
+ const submit = card?.querySelector?.('button[type="submit"]') || Array.from(card?.querySelectorAll?.('button') || []).find(button => /anmelden|login|sign in|aanmelden/.test(normalize(button.textContent || '')));
320
+ return { username, password, card, submit };
381
321
  };
382
322
  const statusNode = card => card?.querySelector?.('.eos-login-first-status');
383
323
  const directChildWithin = (ancestor, node) => {
@@ -533,7 +473,7 @@
533
473
  setStatus(card, t.claimChecking, '');
534
474
  try {
535
475
  const response = await fetch(new URL('nexowatt/account/passwordless-claim', base).href, {
536
- method: 'POST', credentials: 'same-origin', cache: 'no-store',
476
+ method: 'POST', credentials: 'include', cache: 'no-store',
537
477
  headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-Passwordless-Claim': '1' },
538
478
  body: JSON.stringify({ user: account }),
539
479
  });
@@ -560,7 +500,7 @@
560
500
  const { username, password, card, submit } = loginElements();
561
501
  if (!username || !password || !card) return;
562
502
  card.classList.add('eos-login-card-modern');
563
- // v7100 normal-login first activation: do not add role buttons or a second panel.
503
+ // v98 compact normal-login first activation: do not add role buttons or a second panel.
564
504
  // The account is entered in the existing Loginname field; a blank password is accepted only
565
505
  // after the backend confirms that this exact managed account is eligible.
566
506
  card.querySelectorAll('.eos-login-role-selector').forEach(element => element.remove());
@@ -685,7 +625,6 @@
685
625
  const isLoginRequest = /(?:^|[?&])login(?:[=&]|$)/i.test(window.location.search || '');
686
626
  if (isLoginRequest) {
687
627
  launch();
688
- installIntegratedFirstLogin(base);
689
628
  return;
690
629
  }
691
630
 
@@ -694,7 +633,7 @@
694
633
  const timeout = window.setTimeout(() => controller.abort(), 8000);
695
634
 
696
635
  window.NEXOWATT_EOS_BOOTSTRAP_READY = fetch(contextUrl, {
697
- credentials: 'same-origin', cache: 'no-store', headers: { Accept: 'application/json' }, signal: controller.signal,
636
+ credentials: 'include', cache: 'no-store', headers: { Accept: 'application/json' }, signal: controller.signal,
698
637
  })
699
638
  .then(response => { if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); })
700
639
  .then(policy => {
@@ -710,10 +649,6 @@
710
649
  window.NEXOWATT_EOS_ACCESS_ROLE = role;
711
650
  try { window.sessionStorage.removeItem('NEXOWATT_EOS_SECURITY_RETRY_COUNT'); } catch (_) { /* ignore */ }
712
651
  lockExpertMode(role);
713
- if (role !== 'admin' && (resolved.mustChangePassword || resolved.passwordSetup?.required)) {
714
- showFirstLoginPassword(resolved, base);
715
- return resolved;
716
- }
717
652
  const route = currentRoute();
718
653
  if (!isAllowed(role, route)) navigate(defaultTab(role));
719
654
  launch();
@@ -727,6 +662,8 @@
727
662
  })
728
663
  .finally(() => window.clearTimeout(timeout));
729
664
 
665
+ window.NEXOWATT_EOS_STANDARD_PASSWORD_MODE = true;
666
+ document.documentElement.classList.remove('eos-first-login-active');
730
667
  window.NEXOWATT_EOS_ROLE_BOOTSTRAP = Object.freeze({
731
668
  version: VERSION, getRole: () => window.NEXOWATT_EOS_ACCESS_ROLE || 'unknown',
732
669
  getPolicy: () => window.NEXOWATT_EOS_BOOTSTRAP_POLICY || null, isAllowed, defaultTab,
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v7100-native-overview-ems-live-reserve-filter';
4
+ const VERSION = 'v7101-native-overview-standard-users';
5
5
  window.NEXOWATT_EOS_ROLE_UI_VERSION = VERSION;
6
6
 
7
7
  const state = {
@@ -65,7 +65,7 @@
65
65
  if (role === 'admin') return true;
66
66
  if (isOfficialReserveTab(route)) return false;
67
67
  if (role === 'installer') return route === 'tab-intro' || route === 'tab-users' || !isInstallerDenied(route, label);
68
- if (role === 'enduser') return isEndUserTab(route, label);
68
+ if (role === 'enduser') return route === 'tab-users' || isEndUserTab(route, label);
69
69
  return false;
70
70
  };
71
71
  const defaultTab = () => 'tab-intro';
@@ -226,7 +226,7 @@
226
226
  ['Datenpunkte', 'Messwerte prüfen und freigegebene Werte schreiben', 'tab-objects', 'datapoints'],
227
227
  ['Struktur', 'Räume und Funktionen für Smart Home zuordnen', 'tab-enums', 'structure'],
228
228
  ['Geräte', 'Geräteintegration und Inbetriebnahme öffnen', 'tab-devicemanager', 'devices'],
229
- ['Zugänge & Rechte', 'Endkundenpasswörter sicher zurücksetzen', 'tab-users', 'rights'],
229
+ ['Zugänge & Rechte', 'Passwörter im nativen Benutzereditor verwalten', 'tab-users', 'rights'],
230
230
  ['Basis-Einstellungen', 'Standort, Sprache und Anlagenparameter', 'basic-settings', 'settings'],
231
231
  ...(backupTab ? [['NexoWatt Sicherung', 'Sicherung und Wiederherstellung des EOS-Systems', backupTab, 'backup']] : []),
232
232
  ['EOS Cockpit', 'Energie-, Lade- und Gebäudesteuerung öffnen', uiTab, 'eos'],
@@ -277,8 +277,31 @@
277
277
  if (!admin) button.setAttribute('aria-hidden', 'true'); else button.removeAttribute('aria-hidden');
278
278
  });
279
279
  };
280
+ const filterNativeUsersPage = () => safe(() => {
281
+ if (currentRoute() !== 'tab-users' || state.role === 'admin') return;
282
+ const paper = appPaper(); if (!paper) return;
283
+ const policyUser = String(state.policy?.user || state.policy?.userId || window.NEXOWATT_EOS_BOOTSTRAP_POLICY?.user || '').replace(/^system\.user\./, '').toLowerCase();
284
+ const allowed = state.role === 'installer'
285
+ ? new Set(['installer', 'user', 'guest', policyUser].filter(Boolean))
286
+ : new Set([policyUser || (state.role === 'enduser' ? 'user' : '')].filter(Boolean));
287
+ paper.querySelectorAll('.MuiCard-root,[role="row"],li').forEach(row => {
288
+ const text = normalize(row.textContent || '');
289
+ const match = text.match(/system user ([a-z0-9_.@-]+)/) || text.match(/system\.user\.([a-z0-9_.@-]+)/);
290
+ if (!match) return;
291
+ row.classList.toggle('eos-role-hidden', !allowed.has(match[1]));
292
+ });
293
+ paper.querySelectorAll('button').forEach(button => {
294
+ const title = normalize(button.getAttribute('aria-label') || button.getAttribute('title') || button.textContent || '');
295
+ if (/add user|add group|delete|remove|benutzer hinzufugen|gruppe hinzufugen|loschen/.test(title)) {
296
+ button.classList.add('eos-role-hidden'); button.disabled = true;
297
+ }
298
+ });
299
+ paper.querySelectorAll('[data-eos-user-role-filter]').forEach(node => node.removeAttribute('data-eos-user-role-filter'));
300
+ paper.setAttribute('data-eos-user-role-filter', state.role);
301
+ });
302
+
280
303
  const ensureSafeOverview = () => safe(() => {
281
- // 7.10.0: never cover the real Admin Intro with a second tile surface.
304
+ // 7.9.97: never cover the real Admin Intro with a second tile surface.
282
305
  // Installer and end-user use the native card component; security remains
283
306
  // enforced by route/instance filters and backend permissions.
284
307
  removeSafeOverview();
@@ -415,6 +438,7 @@
415
438
  applySensitiveDialogPolicy();
416
439
  redirectIfNeeded();
417
440
  ensureSafeOverview();
441
+ filterNativeUsersPage();
418
442
  hideOfficialReserveSurfaces();
419
443
  ensureReserveObserver();
420
444
  suppressPermissionErrors();
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v7100-nexowatt-native-shell-native-overview';
4
+ const VERSION = 'v7101-nexowatt-native-shell-native-overview';
5
5
  const previous = window.NEXOWATT_NATIVE_SHELL;
6
6
  if (previous?.version === VERSION) return;
7
7
  previous?.destroy?.();
@@ -1,72 +1,45 @@
1
- /* NexoWatt EOS stable login runtime 7.10.0 */
1
+ /* NexoWatt EOS stable runtime v98 */
2
2
  (() => {
3
- 'use strict';
4
- const VERSION = 'v7100-clean-login';
5
- const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
6
- const ASSIST_TEXT = /EOS\s*Assist/i;
7
- const PASSWORDLESS_TEXT = /Erstanmeldung\s+ohne\s+Passwort|first\s+sign-?in\s+without\s+password/i;
8
- const isLogin = () => /(?:\?|&)login(?:[=&]|$)/i.test(location.search) || /\/login(?:[/?#]|$)/i.test(location.pathname + location.hash);
9
- const visible = element => !!element && element.getClientRects().length > 0;
10
- const findInput = kind => {
11
- const inputs = [...document.querySelectorAll('input')].filter(visible);
12
- if (kind === 'password') return inputs.find(input => input.type === 'password');
13
- return inputs.find(input => input.type !== 'password' && /user|login|name/i.test(`${input.name} ${input.id} ${input.autocomplete} ${input.getAttribute('aria-label') || ''}`)) || inputs.find(input => input.type !== 'password');
14
- };
15
- const findSubmit = () => [...document.querySelectorAll('button,input[type="submit"]')].find(element => visible(element) && (/anmelden|login|sign in/i.test((element.textContent || element.value || '').trim()) || element.type === 'submit'));
16
- const normalizeUser = value => String(value || '').trim().toLowerCase().replace(/^system\.user\./, '');
17
- const normalizeGeometry = () => {
18
- document.querySelectorAll('[data-nw-login-card],[data-nw-login-shell]').forEach(element => {
19
- if (!element.classList.contains('MuiPaper-root') && !element.classList.contains('eos-login-card-modern')) {
20
- element.removeAttribute('data-nw-login-card');
21
- element.removeAttribute('data-nw-login-shell');
22
- }
23
- });
24
- for (const input of [findInput('user'), findInput('password')].filter(Boolean)) {
25
- const control = input.closest('.MuiFormControl-root');
26
- const label = control?.querySelector('.MuiInputLabel-root');
27
- if (control && label) {
28
- const localized = String(label.textContent || input.getAttribute('aria-label') || '').trim();
29
- if (localized) control.dataset.eosLoginLabel = localized;
30
- label.classList.add('MuiInputLabel-shrink');
31
- label.dataset.shrink = 'true';
32
- label.removeAttribute('aria-hidden');
33
- }
34
- }
35
- };
36
- const sync = () => {
37
- if (!isLogin() && !document.querySelector('input#username,input[name="username"]')) return;
38
- document.documentElement.classList.add('nw-eos-login-v7100');
39
- document.body?.classList.add('nw-eos-login-v7100');
40
- normalizeGeometry();
41
- const username = findInput('user');
42
- const password = findInput('password');
43
- const submit = findSubmit();
44
- if (submit && MANAGED_USERS.has(normalizeUser(username?.value)) && !String(password?.value || '')) {
45
- submit.disabled = true;
46
- submit.setAttribute('aria-disabled', 'true');
47
- }
48
- };
49
- const hideLegacyUi = () => {
50
- for (const element of document.querySelectorAll('button,a,[role="button"],div,span')) {
51
- const value = (element.textContent || '').trim();
52
- if (!value || value.length > 100) continue;
53
- if (ASSIST_TEXT.test(value) || PASSWORDLESS_TEXT.test(value)) {
54
- const target = element.closest('button,a,[role="button"],[data-eos-assist],.eos-assist') || element;
55
- target.style.setProperty('display', 'none', 'important');
56
- target.setAttribute('aria-hidden', 'true');
57
- }
58
- }
59
- };
60
- const apply = () => { sync(); hideLegacyUi(); };
61
- let scheduled = false;
62
- const schedule = () => {
63
- if (scheduled) return;
64
- scheduled = true;
65
- requestAnimationFrame(() => { scheduled = false; apply(); });
66
- };
67
- new MutationObserver(schedule).observe(document.documentElement, { subtree: true, childList: true });
68
- document.addEventListener('input', schedule, true);
69
- document.addEventListener('change', schedule, true);
70
- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', apply, { once: true }); else apply();
71
- window.NEXOWATT_EOS_STABLE_LOGIN = Object.freeze({ version: VERSION, refresh: apply });
3
+ 'use strict';
4
+ const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
5
+ const ASSIST_TEXT = /EOS\s*Assist/i;
6
+ const PASSWORDLESS_TEXT = /Erstanmeldung\s+ohne\s+Passwort|first\s+sign-?in\s+without\s+password/i;
7
+ const isLogin = () => /(?:\?|&)login(?:[=&]|$)/i.test(location.search) || /\/login(?:[/?#]|$)/i.test(location.pathname + location.hash);
8
+ const visible = element => !!element && element.getClientRects().length > 0;
9
+ const findInput = kind => {
10
+ const inputs = [...document.querySelectorAll('input')].filter(visible);
11
+ if (kind === 'password') return inputs.find(input => input.type === 'password');
12
+ return inputs.find(input => input.type !== 'password' && /user|login|name/i.test(`${input.name} ${input.id} ${input.autocomplete} ${input.getAttribute('aria-label') || ''}`)) || inputs.find(input => input.type !== 'password');
13
+ };
14
+ const findSubmit = () => [...document.querySelectorAll('button,input[type="submit"]')].find(element => visible(element) && (/anmelden|login|sign in/i.test((element.textContent || element.value || '').trim()) || element.type === 'submit'));
15
+ const sync = () => {
16
+ if (!isLogin()) return;
17
+ document.documentElement.classList.add('nw-eos-login-v98');
18
+ document.body?.classList.add('nw-eos-login-v98');
19
+ const username = findInput('user');
20
+ const password = findInput('password');
21
+ const submit = findSubmit();
22
+ const account = String(username?.value || '').trim().toLowerCase().replace(/^system\.user\./, '');
23
+ if (submit && MANAGED_USERS.has(account) && !String(password?.value || '')) {
24
+ submit.disabled = true;
25
+ submit.setAttribute('aria-disabled', 'true');
26
+ }
27
+ };
28
+ const hideLegacyUi = () => {
29
+ for (const element of document.querySelectorAll('button,a,[role="button"],div,span')) {
30
+ const value = (element.textContent || '').trim();
31
+ if (!value || value.length > 100) continue;
32
+ if (ASSIST_TEXT.test(value) || PASSWORDLESS_TEXT.test(value)) {
33
+ const target = element.closest('button,a,[role="button"],[data-eos-assist],.eos-assist') || element;
34
+ target.style.setProperty('display', 'none', 'important');
35
+ target.setAttribute('aria-hidden', 'true');
36
+ }
37
+ }
38
+ };
39
+ const apply = () => { sync(); hideLegacyUi(); };
40
+ const observer = new MutationObserver(() => requestAnimationFrame(apply));
41
+ observer.observe(document.documentElement, { subtree: true, childList: true });
42
+ document.addEventListener('input', apply, true);
43
+ document.addEventListener('change', apply, true);
44
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', apply, { once: true }); else apply();
72
45
  })();
@@ -0,0 +1,27 @@
1
+ (() => {
2
+ 'use strict';
3
+ const VERSION = 'v7101-scroll-standard-password';
4
+ const ensureScroll = () => {
5
+ const root = document.documentElement;
6
+ const paper = document.getElementById('app-paper');
7
+ if (!paper || !root.classList.contains('eos-app') || !root.classList.contains('eos-route-intro')) return;
8
+ paper.dataset.nexowattScrollable = 'true';
9
+ paper.style.setProperty('overflow-x','hidden','important');
10
+ paper.style.setProperty('overflow-y','scroll','important');
11
+ paper.style.setProperty('height','calc(100dvh - var(--nx-content-top) - 12px)','important');
12
+ paper.style.setProperty('max-height','calc(100dvh - var(--nx-content-top) - 12px)','important');
13
+ paper.style.setProperty('min-height','0','important');
14
+ };
15
+ const apply = () => {
16
+ document.documentElement.classList.add('eos-assist-disabled');
17
+ document.documentElement.classList.remove('eos-first-login-active');
18
+ document.querySelectorAll('.eos-assist-root,.eos-assist-header-root,[data-eos-assist-root],.eos-passwordless-launcher,.eos-first-login-overlay')
19
+ .forEach(element => element.remove());
20
+ ensureScroll();
21
+ };
22
+ let scheduled=false;
23
+ const schedule=()=>{if(scheduled)return;scheduled=true;requestAnimationFrame(()=>{scheduled=false;apply();});};
24
+ const start=()=>{apply();new MutationObserver(schedule).observe(document.documentElement,{subtree:true,childList:true});window.addEventListener('hashchange',schedule);window.addEventListener('resize',schedule,{passive:true});};
25
+ document.readyState==='loading'?document.addEventListener('DOMContentLoaded',start,{once:true}):start();
26
+ window.NEXOWATT_EOS_STABLE_V7101=Object.freeze({version:VERSION,refresh:apply,ensureScroll});
27
+ })();