mirror of
https://github.com/D-Ogi/WatermarkRemover-AI.git
synced 2026-10-21 22:22:35 -05:00
Merge pull request #95: Prevent model status from overlapping the footer
This commit is contained in:
commit
08504163a4
3 files changed
+84
-22
No files matched your search
+58
-13
@@ -24,11 +24,23 @@ failures = []
|
||||
|
||||
|
||||
|
||||
def check_sidebar_layout(window):
|
||||
"""Measure actual rendered controls and tooltip bounds across all themes/languages."""
|
||||
window.resize(800, 600)
|
||||
def check_theme_layout(window, width, height):
|
||||
"""Measure controls, tooltips and status-bar bounds across themes and languages."""
|
||||
window.resize(width, height)
|
||||
window.evaluate_js("window.appInstance.modelsOpen = true")
|
||||
deadline = time.monotonic() + 10
|
||||
while not window.evaluate_js("document.querySelector('.model-dialog').getBoundingClientRect().height > 0"):
|
||||
assert time.monotonic() < deadline, "Model dialog did not become visible"
|
||||
time.sleep(.05)
|
||||
window.evaluate_js("""window.sidebarProbe = null; (async () => {
|
||||
const app = window.appInstance, rows = [];
|
||||
// Measure final theme styles, not an intermediate animated color.
|
||||
const motion = document.createElement('style');
|
||||
motion.textContent = '*, *::before, *::after { transition: none !important; animation: none !important; }';
|
||||
document.head.appendChild(motion);
|
||||
const originalGpu = app.systemInfo.gpu, originalStatus = app.models.status;
|
||||
app.systemInfo.gpu = 'NVIDIA GeForce RTX 4090 Laptop GPU';
|
||||
app.models.status = 'downloading';
|
||||
for (const language of app.availableLanguages) {
|
||||
app.t = await loadLanguage(language.id);
|
||||
await Alpine.nextTick();
|
||||
@@ -70,17 +82,48 @@ def check_sidebar_layout(window):
|
||||
return (Math.max(a, b) + .05) / (Math.min(a, b) + .05);
|
||||
};
|
||||
const background = getComputedStyle(panel).backgroundColor;
|
||||
const textContrast = [...panel.querySelectorAll('h2, p')]
|
||||
.map(node => contrast(getComputedStyle(node).color, background));
|
||||
const buttonContrast = [...panel.querySelectorAll('button')]
|
||||
// Hidden error/progress messages are not rendered; WebKit may
|
||||
// retain their previous computed colors until they become visible.
|
||||
const visible = node => node.getBoundingClientRect().height > 0
|
||||
&& node.getClientRects().length && node.textContent.trim();
|
||||
const textStyles = [...panel.querySelectorAll('h2, p')].filter(visible)
|
||||
.map(node => ({text: node.textContent, color: getComputedStyle(node).color}));
|
||||
const textContrast = textStyles.map(style => contrast(style.color, background));
|
||||
const buttonContrast = [...panel.querySelectorAll('button')].filter(visible)
|
||||
.map(node => { const style = getComputedStyle(node);
|
||||
return contrast(style.color, style.backgroundColor); });
|
||||
rows.push({theme: theme.id, language: language.id,
|
||||
const gpu = document.querySelector('[x-text^="systemInfo.gpu"]');
|
||||
const footer = gpu.parentElement;
|
||||
const button = document.querySelector('#models-button');
|
||||
const frame = document.querySelector('main').getBoundingClientRect();
|
||||
const contains = (outer, inner) => inner.left >= outer.left - 1
|
||||
&& inner.right <= outer.right + 1 && inner.top >= outer.top - 1
|
||||
&& inner.bottom <= outer.bottom + 1;
|
||||
const statusBounds = footer.getBoundingClientRect();
|
||||
const buttonBounds = button.getBoundingClientRect();
|
||||
const gpuBounds = gpu.getBoundingClientRect();
|
||||
const overlap = Math.min(buttonBounds.right, gpuBounds.right)
|
||||
- Math.max(buttonBounds.left, gpuBounds.left) > 1
|
||||
&& Math.min(buttonBounds.bottom, gpuBounds.bottom)
|
||||
- Math.max(buttonBounds.top, gpuBounds.top) > 1;
|
||||
const statusVisible = contains(frame, statusBounds)
|
||||
&& contains(statusBounds, buttonBounds) && contains(statusBounds, gpuBounds)
|
||||
&& !overlap && footer.scrollWidth <= footer.clientWidth + 1
|
||||
&& gpu.scrollWidth <= gpu.clientWidth + 1
|
||||
&& button.scrollWidth <= button.clientWidth + 1;
|
||||
rows.push({theme: theme.id, language: language.id, statusVisible,
|
||||
statusGeometry: {frame: frame.toJSON(), footer: statusBounds.toJSON(),
|
||||
button: buttonBounds.toJSON(), gpu: gpuBounds.toJSON(), overlap,
|
||||
scroll: footer.scrollWidth, client: footer.clientWidth},
|
||||
contrast: Math.min(...textContrast, ...buttonContrast),
|
||||
modelColors: {background, textStyles, isOpen: app.modelsOpen, display: getComputedStyle(panel.parentElement).display},
|
||||
client: aside.clientWidth, scroll: aside.scrollWidth,
|
||||
outside, hintsInside: hints.every(Boolean)});
|
||||
}
|
||||
}
|
||||
app.systemInfo.gpu = originalGpu;
|
||||
app.models.status = originalStatus;
|
||||
motion.remove();
|
||||
return rows;
|
||||
})().then(rows => { window.sidebarProbe = {rows}; })
|
||||
.catch(error => { window.sidebarProbe = {error: String(error)}; });""")
|
||||
@@ -97,8 +140,9 @@ def check_sidebar_layout(window):
|
||||
for row in results:
|
||||
assert row['scroll'] <= row['client'] + 1, row
|
||||
assert not row['outside'] and row['hintsInside'], row
|
||||
assert row['contrast'] >= 4.5, row
|
||||
print('THEME PASS', len(results), 'theme/language combinations; minimum model-panel contrast',
|
||||
assert row['contrast'] is not None and row['contrast'] >= 4.5, row
|
||||
assert row['statusVisible'], row
|
||||
print('THEME PASS', f'{width}x{height}', len(results), 'theme/language combinations; minimum model-panel contrast',
|
||||
round(min(row['contrast'] for row in results), 2), flush=True)
|
||||
|
||||
|
||||
@@ -139,7 +183,8 @@ def exercise(window, api):
|
||||
break
|
||||
time.sleep(.1)
|
||||
assert window.evaluate_js("window.savedProbe?.lang") == 'en'
|
||||
check_sidebar_layout(window)
|
||||
check_theme_layout(window, 800, 600)
|
||||
check_theme_layout(window, 1000, 800)
|
||||
print('DESKTOP PASS', json.dumps(state), flush=True)
|
||||
except BaseException as exc:
|
||||
failures.append(repr(exc))
|
||||
@@ -158,9 +203,9 @@ def watchdog():
|
||||
|
||||
|
||||
threading.Thread(target=watchdog, daemon=True).start()
|
||||
# WebKit can defer network page loading for an entirely hidden native window.
|
||||
# Exercise its normal visible startup on the macOS CI desktop.
|
||||
remwmgui.main(exercise, hidden=sys.platform != "darwin")
|
||||
# Native engines can defer viewport sizing for an entirely hidden window.
|
||||
# Measure the real visible desktop layout (Linux CI supplies Xvfb).
|
||||
remwmgui.main(exercise)
|
||||
finished.set()
|
||||
check_config.cleanup()
|
||||
if failures:
|
||||
|
||||
+7
-9
@@ -277,7 +277,7 @@
|
||||
</aside>
|
||||
|
||||
<!-- RIGHT CONTENT (Preview & Logs) -->
|
||||
<div class="flex-1 min-w-[400px] flex flex-col relative bg-black/40 backdrop-blur-sm overflow-hidden">
|
||||
<div class="flex-1 min-w-0 flex flex-col relative bg-black/40 backdrop-blur-sm overflow-hidden">
|
||||
|
||||
<!-- MARQUEE BAR -->
|
||||
<div class="bg-neon-pink text-black font-bold text-xs py-1 overflow-hidden whitespace-nowrap border-b border-neon-pink">
|
||||
@@ -341,8 +341,8 @@
|
||||
</div>
|
||||
|
||||
<!-- STATUS BAR -->
|
||||
<div class="bg-black/60 backdrop-blur-md border-t border-white/10 p-2 px-4 flex justify-between items-center text-[10px] font-mono text-gray-500 uppercase">
|
||||
<div class="flex gap-4">
|
||||
<div class="status-bar bg-black/60 backdrop-blur-md border-t border-white/10 p-2 px-4 text-[10px] font-mono text-gray-500 uppercase">
|
||||
<div class="status-details">
|
||||
<span class="flex items-center gap-1">
|
||||
<span class="w-2 h-2 rounded-full" :class="systemInfo.cuda ? 'bg-neon-green' : 'bg-red-500'"></span>
|
||||
<span x-text="t.status_bar?.cuda || 'CUDA'"></span>: <span x-text="systemInfo.cuda ? (t.status_bar?.on || 'ON') : (t.status_bar?.off || 'OFF')"></span>
|
||||
@@ -353,7 +353,10 @@
|
||||
</span>
|
||||
<span><span x-text="t.status_bar?.ram || 'RAM'"></span>: <span x-text="systemInfo.ram + '%'"></span></span>
|
||||
</div>
|
||||
<div x-text="systemInfo.gpu || (t.status_bar?.cpu_mode || 'CPU Mode')"></div>
|
||||
<div class="status-device" x-text="systemInfo.gpu || (t.status_bar?.cpu_mode || 'CPU Mode')"></div>
|
||||
<button id="models-button" @click="modelsOpen = true" class="glass-panel px-3 py-2 text-xs text-neon-cyan rounded">
|
||||
Models: <span x-text="models.status"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- PROGRESS OVERLAY (Visible when processing) -->
|
||||
@@ -466,11 +469,6 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="fixed bottom-3 right-3 z-50">
|
||||
<button id="models-button" @click="modelsOpen = true" class="glass-panel px-3 py-2 text-xs text-neon-cyan rounded">
|
||||
Models: <span x-text="models.status"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="bridgeError" class="fixed top-2 left-2 right-2 z-50 bg-card border border-error p-3 text-sm" x-text="bridgeError"></div>
|
||||
<div x-show="modelsOpen" x-cloak class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-6">
|
||||
<section class="glass-panel model-dialog w-full max-w-xl p-6 rounded-xl" role="dialog" aria-label="AI models">
|
||||
|
||||
@@ -1094,3 +1094,22 @@ body .glass-panel.model-dialog h2 { color: var(--model-accent) !important; }
|
||||
color: var(--model-ink) !important;
|
||||
border: 1px solid var(--model-accent) !important;
|
||||
}
|
||||
|
||||
|
||||
/* Keep model readiness inside the status bar, including inset window themes. */
|
||||
.status-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.status-details {
|
||||
display: flex;
|
||||
flex: 1 1 100%;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 16px;
|
||||
}
|
||||
.status-details > span { max-width: 100%; flex-wrap: wrap; }
|
||||
.status-device { flex: 1 1 160px; min-width: 0; overflow-wrap: anywhere; }
|
||||
#models-button { flex: 0 0 auto; max-width: 100%; margin-left: auto; }
|
||||
Reference in new issue
Block a user