From 890c42e62f7a34dc1597991b47a3268f870eea1c Mon Sep 17 00:00:00 2001 From: ReaperOfVeriod Date: Sun, 25 Oct 2026 05:36:50 +0100 Subject: [PATCH] add overlay variable diagnostics --- src/App.svelte | 22 ++++++++++++++++++++++ src/styles.css | 17 +++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/App.svelte b/src/App.svelte index 9ecb8a2..55473af 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -94,6 +94,7 @@ let overlaySavedSignature = ''; let overlayAutosaveAttemptedSignature = ''; let pendingOverlayDraft: { overlayName: string; overlaySlug: string; overlayHtml: string; overlayCss: string; overlayJavascript: string; overlayEnabled: boolean; overlayDisabledDesign: string } | null = null; + let overlayDiagnosticsOpen = false; let overlayAutosaveTimer: number | undefined; let oauthScopes: Record<'streamer' | 'bot', { connected: boolean; missing_scopes: string[]; reauthorization_required: boolean }> = { streamer: { connected: false, missing_scopes: [], reauthorization_required: false }, @@ -724,6 +725,21 @@ return JSON.stringify({ overlayName, overlaySlug, overlayHtml, overlayCss, overlayJavascript, overlayEnabled, overlayDisabledDesign }); } + function overlayVariableWarnings() { + const warnings: string[] = []; + for (const [language, source] of [['HTML', overlayHtml], ['CSS', overlayCss], ['JAVASCRIPT', overlayJavascript]] as const) { + if (source.includes('${')) { + for (const match of source.match(/\$\{([^}]*)\}/g) || []) { + const name = match.slice(2, -1); + if (!/^[A-Za-z0-9_]+$/.test(name) || name.length === 0) warnings.push(`${language}: invalid variable ${match[0]}`); + else if (name !== 'username') warnings.push(`${language}: undefined variable ${match[0]}`); + } + if (source.split('${').length - 1 !== (source.match(/\$\{[^}]*\}/g) || []).length) warnings.push(`${language}: an opening variable marker has no closing }`); + } + } + return warnings; + } + function overlayDraftCacheKey(id: string) { return `trixie.overlay.draft.${id}`; } @@ -1026,5 +1042,11 @@
A newer unsaved draft is available for this overlay.
{/if} + {#if activeView === 'Overlays' && (selectedOverlayId || overlayName) && overlayVariableWarnings().length} +
+
+ {#if overlayDiagnosticsOpen}
{#each overlayVariableWarnings() as warning}
{warning}
{/each}
{/if} +
+ {/if} diff --git a/src/styles.css b/src/styles.css index 8709992..f51adac 100644 --- a/src/styles.css +++ b/src/styles.css @@ -931,3 +931,20 @@ label { margin: 6px 0 10px 18px; border-left: 2px solid var(--ui-line-focus); } +.overlay-diagnostics-panel { + margin-top: 10px; +} +.diagnostics-toggle { + border: 0; + background: transparent; + color: var(--ui-error, #ff6b7a); + font: inherit; + letter-spacing: 0.08em; + text-transform: uppercase; +} +.diagnostics-details { + padding: 10px; + color: var(--ui-error, #ff6b7a); + font-family: var(--font-mono); + font-size: 0.8rem; +}