diff --git a/public/flux-studio-ux.js b/public/flux-studio-ux.js index ad02d2d..e94394d 100644 --- a/public/flux-studio-ux.js +++ b/public/flux-studio-ux.js @@ -12,6 +12,18 @@ return node; }; + const setText = (node, value) => { + if (!node) return; + const normalized = String(value ?? ''); + if (node.textContent !== normalized) node.textContent = normalized; + }; + + const setData = (node, name, value) => { + if (!node) return; + const normalized = String(value ?? ''); + if (node.dataset[name] !== normalized) node.dataset[name] = normalized; + }; + const readStoredView = () => { try { const stored = localStorage.getItem(VIEW_KEY) || 'create'; @@ -36,11 +48,6 @@ }); }; - const parseNumber = value => { - const match = String(value || '').replace(/\./g, '').match(/\d+/); - return match ? Number(match[0]) : 0; - }; - function createViewButton(view, label, copy) { const button = createNode('button', 'ux-flux-view-tab'); button.type = 'button'; @@ -75,7 +82,7 @@ function setView(root, view, { persist = true, focus = false } = {}) { const normalized = VALID_VIEWS.includes(view) ? view : 'create'; - root.dataset.uxFluxView = normalized; + setData(root, 'uxFluxView', normalized); if (persist) storeView(normalized); root.querySelectorAll('[data-ux-flux-view]').forEach(button => { @@ -101,8 +108,8 @@ const target = normalized === 'create' ? root.querySelector('#flux-studio-prompt') : normalized === 'jobs' - ? root.querySelector('#flux-job-center-body, #flux-job-center-toggle') - : root.querySelector('#flux-studio-history, #flux-studio-favorites-only'); + ? root.querySelector('#flux-job-center-toggle, #flux-job-center-body') + : root.querySelector('#flux-studio-favorites-only, #flux-studio-history'); focusNode(target); } } @@ -297,15 +304,15 @@ const jobState = readJobState(root); const jobBadge = root.querySelector('[data-ux-flux-view-badge="jobs"]'); if (jobBadge) { - jobBadge.textContent = jobState.issues ? 'Problem' : jobState.active ? 'Aktiv' : jobState.hasJobs ? 'Verlauf' : ''; - jobBadge.dataset.tone = jobState.issues ? 'danger' : jobState.active ? 'working' : 'neutral'; + setText(jobBadge, jobState.issues ? 'Problem' : jobState.active ? 'Aktiv' : jobState.hasJobs ? 'Verlauf' : ''); + setData(jobBadge, 'tone', jobState.issues ? 'danger' : jobState.active ? 'working' : 'neutral'); } const libraryBadge = root.querySelector('[data-ux-flux-view-badge="library"]'); if (libraryBadge) { const count = readLibraryCount(root); - libraryBadge.textContent = count ? String(count) : ''; - libraryBadge.dataset.tone = 'neutral'; + setText(libraryBadge, count ? String(count) : ''); + setData(libraryBadge, 'tone', 'neutral'); } } @@ -316,7 +323,7 @@ const image = root.querySelector('#flux-studio-preview-image'); const hasResult = Boolean(image?.getAttribute('src')) && !image.classList.contains('hidden'); const progress = root.querySelector('#flux-studio-progress'); - const isBusy = progress && !progress.classList.contains('hidden'); + const isBusy = Boolean(progress && !progress.classList.contains('hidden')); const jobState = readJobState(root); const completed = { @@ -334,31 +341,30 @@ ? 'result' : 'profile'; - root.dataset.uxFluxCurrentStep = current; + setData(root, 'uxFluxCurrentStep', current); root.querySelectorAll('[data-ux-flux-step]').forEach(step => { const id = step.dataset.uxFluxStep; const active = id === current; step.classList.toggle('is-current', active); step.classList.toggle('is-complete', Boolean(completed[id])); step.setAttribute('aria-pressed', String(active)); - const state = step.querySelector('.ux-flux-step-state'); - if (state) state.textContent = completed[id] ? 'Erledigt' : active ? 'Jetzt' : 'Offen'; + setText(step.querySelector('.ux-flux-step-state'), completed[id] ? 'Erledigt' : active ? 'Jetzt' : 'Offen'); }); const next = root.querySelector('[data-ux-flux-next]'); if (next) { if (!completed.prompt) { - next.dataset.uxFluxNext = 'prompt'; - next.textContent = 'Prompt eingeben'; + setData(next, 'uxFluxNext', 'prompt'); + setText(next, 'Prompt eingeben'); } else if (jobState.issues || jobState.active || isBusy) { - next.dataset.uxFluxNext = 'jobs'; - next.textContent = jobState.issues ? 'Probleme prüfen' : 'Auftrag überwachen'; + setData(next, 'uxFluxNext', 'jobs'); + setText(next, jobState.issues ? 'Probleme prüfen' : 'Auftrag überwachen'); } else if (hasResult) { - next.dataset.uxFluxNext = 'result'; - next.textContent = 'Ergebnis prüfen'; + setData(next, 'uxFluxNext', 'result'); + setText(next, 'Ergebnis prüfen'); } else { - next.dataset.uxFluxNext = 'generate'; - next.textContent = 'Profil prüfen & Auftrag starten'; + setData(next, 'uxFluxNext', 'generate'); + setText(next, 'Profil prüfen & Auftrag starten'); } } @@ -424,13 +430,22 @@ }; const observer = new MutationObserver(schedule); - observer.observe(root, { + const targets = [ + root.querySelector('#flux-job-summary'), + root.querySelector('#flux-job-list'), + root.querySelector('#flux-history-page-info'), + root.querySelector('#flux-studio-history'), + root.querySelector('#flux-studio-preview-image'), + root.querySelector('#flux-studio-progress'), + root.querySelector('#flux-studio-status'), + ].filter(Boolean); + targets.forEach(target => observer.observe(target, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ['class', 'src', 'data-state', 'data-tone', 'disabled'], - }); + })); root.querySelectorAll('input,textarea,select').forEach(field => { field.addEventListener('input', schedule); field.addEventListener('change', schedule);