addasd
All checks were successful
Deploy / deploy-production (push) Has been skipped
Deploy / deploy-staging (push) Successful in 5s

This commit is contained in:
2026-05-06 00:05:42 +02:00
parent 47757675c2
commit fbfcf50b67
2 changed files with 98 additions and 9 deletions

View File

@@ -1429,7 +1429,7 @@ body.has-modal-open {
overflow: hidden; overflow: hidden;
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: 24px; border-radius: 24px;
background: var(--surface); background: var(--surface-strong);
box-shadow: 0 28px 80px rgba(1, 22, 32, 0.24); box-shadow: 0 28px 80px rgba(1, 22, 32, 0.24);
} }
@@ -1452,10 +1452,18 @@ body.has-modal-open {
} }
.nexus-debug-popup__head h2 { .nexus-debug-popup__head h2 {
margin: 8px 0 0; margin: 4px 0 0;
font-size: 1.2rem; font-size: 1.2rem;
} }
.nexus-debug-popup__eyebrow {
color: var(--muted);
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.nexus-debug-popup__toolbar { .nexus-debug-popup__toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -1488,7 +1496,7 @@ body.has-modal-open {
.nexus-debug-entry { .nexus-debug-entry {
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: 16px; border-radius: 16px;
background: color-mix(in srgb, var(--surface) 92%, var(--brand-accent-2) 8%); background: var(--surface-strong);
} }
.nexus-debug-entry summary { .nexus-debug-entry summary {
@@ -1505,12 +1513,23 @@ body.has-modal-open {
display: none; display: none;
} }
.nexus-debug-entry__title {
display: grid;
gap: 4px;
min-width: 0;
flex: 1 1 auto;
}
.nexus-debug-entry__meta { .nexus-debug-entry__meta {
color: var(--muted); color: var(--muted);
font-size: 0.82rem; font-size: 0.82rem;
text-align: right; text-align: right;
} }
.nexus-debug-entry__copy {
flex: 0 0 auto;
}
.nexus-debug-entry pre { .nexus-debug-entry pre {
margin: 0; margin: 0;
padding: 0 14px 14px; padding: 0 14px 14px;
@@ -1550,6 +1569,10 @@ body.has-modal-open {
.nexus-debug-entry__meta { .nexus-debug-entry__meta {
text-align: left; text-align: left;
} }
.nexus-debug-entry__copy {
width: 100%;
}
} }
.module-box-title { .module-box-title {

View File

@@ -229,16 +229,17 @@ window.NexusModal = (() => {
<section class="nexus-debug-popup" aria-hidden="true"> <section class="nexus-debug-popup" aria-hidden="true">
<div class="nexus-debug-popup__head"> <div class="nexus-debug-popup__head">
<div> <div>
<div class="pill">Debug</div> <div class="nexus-debug-popup__eyebrow">Nexus Debug</div>
<h2>Nexus Debug</h2> <h2>Nexus Debug</h2>
</div> </div>
<button type="button" class="module-button module-button--ghost module-button--small" data-debug-close>Schließen</button> <button type="button" class="module-button module-button--secondary module-button--small" data-debug-close>Schließen</button>
</div> </div>
<div class="nexus-debug-popup__toolbar"> <div class="nexus-debug-popup__toolbar">
<div class="muted">Projektweiter Debug-Stream der aktuellen Admin-Sitzung.</div> <div class="muted">Projektweiter Debug-Stream der aktuellen Admin-Sitzung.</div>
<div class="nexus-debug-popup__actions"> <div class="nexus-debug-popup__actions">
<button type="button" class="module-button module-button--ghost module-button--small" data-debug-reload>Neu laden</button> <button type="button" class="module-button module-button--secondary module-button--small" data-debug-reload>Neu laden</button>
<button type="button" class="module-button module-button--ghost module-button--small" data-debug-clear>Leeren</button> <button type="button" class="module-button module-button--secondary module-button--small" data-debug-copy>Kopieren</button>
<button type="button" class="module-button module-button--secondary module-button--small" data-debug-clear>Leeren</button>
</div> </div>
</div> </div>
<div class="nexus-debug-popup__body" data-debug-list></div> <div class="nexus-debug-popup__body" data-debug-list></div>
@@ -250,6 +251,7 @@ window.NexusModal = (() => {
const popup = root.querySelector('.nexus-debug-popup'); const popup = root.querySelector('.nexus-debug-popup');
const closeButton = root.querySelector('[data-debug-close]'); const closeButton = root.querySelector('[data-debug-close]');
const reloadButton = root.querySelector('[data-debug-reload]'); const reloadButton = root.querySelector('[data-debug-reload]');
const copyButton = root.querySelector('[data-debug-copy]');
const clearButton = root.querySelector('[data-debug-clear]'); const clearButton = root.querySelector('[data-debug-clear]');
const list = root.querySelector('[data-debug-list]'); const list = root.querySelector('[data-debug-list]');
@@ -289,8 +291,11 @@ window.NexusModal = (() => {
return ` return `
<details class="nexus-debug-entry"${index === 0 ? ' open' : ''}> <details class="nexus-debug-entry"${index === 0 ? ' open' : ''}>
<summary> <summary>
<strong>${escapeHtml(title)}</strong> <span class="nexus-debug-entry__title">
<span class="nexus-debug-entry__meta">${escapeHtml(entry.source)} · ${escapeHtml(entry.at)}</span> <strong>${escapeHtml(title)}</strong>
<span class="nexus-debug-entry__meta">${escapeHtml(entry.source)} · ${escapeHtml(entry.at)}</span>
</span>
<button type="button" class="module-button module-button--secondary module-button--small nexus-debug-entry__copy" data-entry-copy="${escapeHtml(entry.id)}">Kopieren</button>
</summary> </summary>
<pre>${escapeHtml(payloadText)}</pre> <pre>${escapeHtml(payloadText)}</pre>
</details> </details>
@@ -305,6 +310,36 @@ window.NexusModal = (() => {
.replaceAll('"', '&quot;') .replaceAll('"', '&quot;')
.replaceAll("'", '&#039;'); .replaceAll("'", '&#039;');
const copyText = async (text, successLabel, errorLabel) => {
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(text);
} else {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', 'readonly');
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
appendEntry({
source: 'nexus',
type: 'debug.copy.success',
label: successLabel,
});
} catch (error) {
appendEntry({
source: 'nexus',
type: 'debug.copy.error',
label: errorLabel,
message: error && error.message ? error.message : 'Kopieren fehlgeschlagen.',
});
}
};
const openPopup = () => { const openPopup = () => {
popup?.classList.add('is-open'); popup?.classList.add('is-open');
popup?.setAttribute('aria-hidden', 'false'); popup?.setAttribute('aria-hidden', 'false');
@@ -357,6 +392,23 @@ window.NexusModal = (() => {
renderEntries(); renderEntries();
}; };
const copyAllEntries = async () => {
const content = entries
.slice()
.reverse()
.map((entry) => JSON.stringify(entry.payload, null, 2))
.join('\n\n');
if (!content) {
appendEntry({
source: 'nexus',
type: 'debug.copy.empty',
label: 'Keine Debug-Einträge zum Kopieren',
});
return;
}
await copyText(content, 'Debug-Einträge kopiert', 'Debug-Einträge konnten nicht kopiert werden');
};
const previousListener = typeof debugBus.listener === 'function' ? debugBus.listener : null; const previousListener = typeof debugBus.listener === 'function' ? debugBus.listener : null;
debugBus.listener = (entry) => { debugBus.listener = (entry) => {
if (previousListener) { if (previousListener) {
@@ -374,7 +426,21 @@ window.NexusModal = (() => {
}); });
closeButton?.addEventListener('click', closePopup); closeButton?.addEventListener('click', closePopup);
reloadButton?.addEventListener('click', reloadEntries); reloadButton?.addEventListener('click', reloadEntries);
copyButton?.addEventListener('click', copyAllEntries);
clearButton?.addEventListener('click', clearEntries); clearButton?.addEventListener('click', clearEntries);
list?.addEventListener('click', (event) => {
const button = event.target instanceof HTMLElement ? event.target.closest('[data-entry-copy]') : null;
if (!button) {
return;
}
const entryId = button.getAttribute('data-entry-copy') || '';
const entry = entries.find((item) => String(item.id) === entryId);
if (!entry) {
return;
}
const content = JSON.stringify(entry.payload, null, 2);
copyText(content, `Eintrag ${entry.type || entry.id} kopiert`, `Eintrag ${entry.type || entry.id} konnte nicht kopiert werden`);
});
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') { if (event.key === 'Escape') {