diff --git a/shine-UI/index.html b/shine-UI/index.html
index db4c53ad..9592f39f 100644
--- a/shine-UI/index.html
+++ b/shine-UI/index.html
@@ -52,8 +52,284 @@ window.__SHINE_BUILD_HASH__ = '20260819190000';
diff --git a/shine-UI/js/app.js b/shine-UI/js/app.js
index e3d3cbc0..448485b8 100644
--- a/shine-UI/js/app.js
+++ b/shine-UI/js/app.js
@@ -5,7 +5,12 @@ import {
syncTrackedRouteHistory,
} from './router.js';
import { renderToolbar } from './components/toolbar.js';
-import { captureClientError, setClientErrorSentNotifier, setClientErrorTransport } from './services/client-error-reporter.js';
+import {
+ captureClientError,
+ getLastClientErrorPayload,
+ setClientErrorSentNotifier,
+ setClientErrorTransport,
+} from './services/client-error-reporter.js';
import { initPwaInstallPromptHandling } from './services/pwa-install-service.js';
import { initPwaPush } from './services/pwa-push-service.js';
import { initCallUiOverlay } from './services/call-ui-service.js';
@@ -208,6 +213,10 @@ setClientErrorSentNotifier((payload) => {
const isoTs = new Date(Number(payload?.clientTs || Date.now())).toISOString();
showToast(`Ошибка отправлена на сервер · ${login} · ${isoTs}`);
});
+window.__SHINE_SEND_CLIENT_ERROR_TO_SERVER__ = (payload) => authService.reportClientError(payload);
+window.__SHINE_SEND_CLIENT_ERROR__ = window.__SHINE_SEND_CLIENT_ERROR_TO_SERVER__;
+window.__SHINE_GET_LAST_CLIENT_ERROR__ = () => getLastClientErrorPayload();
+window.__SHINE_SHOW_TOAST__ = (message, kind = 'success') => showToast(message, { kind });
initPwaInstallPromptHandling();
initCallUiOverlay();
setCallDebugReporter((payload) => authService.reportClientDebug(payload));
diff --git a/shine-UI/js/services/client-error-reporter.js b/shine-UI/js/services/client-error-reporter.js
index 4bcbca74..dba6b432 100644
--- a/shine-UI/js/services/client-error-reporter.js
+++ b/shine-UI/js/services/client-error-reporter.js
@@ -6,6 +6,7 @@ let transport = null;
let transportDepth = 0;
const recentFingerprints = new Map();
let notifySent = null;
+let lastCapturedPayload = null;
function nowTs() {
return Date.now();
@@ -85,6 +86,11 @@ export function setClientErrorSentNotifier(fn) {
notifySent = typeof fn === 'function' ? fn : null;
}
+export function getLastClientErrorPayload() {
+ if (!lastCapturedPayload) return null;
+ return { ...lastCapturedPayload };
+}
+
export function isClientErrorReportingEnabled() {
try {
return localStorage.getItem(UI_ERROR_REPORTING_KEY) === '1';
@@ -104,6 +110,7 @@ export function setClientErrorReportingEnabled(enabled) {
export async function captureClientError(details = {}) {
const payload = buildPayload(details);
if (!payload.message) return false;
+ lastCapturedPayload = payload;
const fingerprint = details.dedupeKey || makeFingerprint(payload);
if (isDuplicate(fingerprint)) return false;
diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css
index 3b4021fd..84ff06b2 100644
--- a/shine-UI/styles/components.css
+++ b/shine-UI/styles/components.css
@@ -3324,6 +3324,10 @@ textarea.input {
z-index: 24;
}
+.boot-error-menu-shell {
+ z-index: 1000000;
+}
+
.modal-backdrop {
position: absolute;
inset: 0;
@@ -3341,6 +3345,16 @@ textarea.input {
box-shadow: var(--shadow);
}
+.boot-error-menu-card {
+ gap: 10px;
+}
+
+.boot-error-menu-message {
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+ line-height: 1.4;
+}
+
.network-board {
position: relative;
height: 290px;