diff --git a/changedetectionio/processors/image_ssim_diff/templates/image_ssim_diff/diff.html b/changedetectionio/processors/image_ssim_diff/templates/image_ssim_diff/diff.html index fbfac7c3..cac6d31c 100644 --- a/changedetectionio/processors/image_ssim_diff/templates/image_ssim_diff/diff.html +++ b/changedetectionio/processors/image_ssim_diff/templates/image_ssim_diff/diff.html @@ -1,182 +1,8 @@ {% extends 'base.html' %} {% from '_helpers.html' import render_field, render_checkbox_field, render_button %} + {% block content %} - - +
@@ -186,7 +12,7 @@
-

Screenshot Comparison (Fast)

+

Screenshot Comparison (Fast)

{% if versions|length >= 2 %}
@@ -232,19 +58,19 @@

Interactive Comparison

-
+
Drag slider to compare Previous ({{ from_version|format_timestamp_timeago }}) vs Current ({{ to_version|format_timestamp_timeago }})
- - Previous screenshot + +
+ Previous screenshot +
- - Current screenshot + +
+ Current screenshot +
@@ -276,11 +106,11 @@

Difference Visualization

-
+
Red = Changed Pixels
{% if comparison_data and comparison_data.get('history') and comparison_data.history|length > 1 %} -
+

Comparison History

-

Recent comparison results (last {{ comparison_data.history|length }} checks)

+

Recent comparison results (last {{ comparison_data.history|length }} checks)

@@ -314,9 +144,9 @@ @@ -363,6 +193,42 @@ function downloadImage(imageId, filename) { URL.revokeObjectURL(url); }, 100); } + +/** + * Synchronize comparison slider width with diff image width + * This ensures both panels display images at the same max-width + */ +function syncComparisonWidth() { + const diffImage = document.getElementById('diff-image'); + const comparisonContainer = document.getElementById('comparison-container'); + + if (!diffImage || !comparisonContainer) return; + + // Wait for diff image to load to get its actual rendered width + if (diffImage.complete) { + applyWidth(); + } else { + diffImage.addEventListener('load', applyWidth); + } + + function applyWidth() { + const diffImageWidth = diffImage.offsetWidth; + if (diffImageWidth > 0) { + comparisonContainer.style.maxWidth = diffImageWidth + 'px'; + comparisonContainer.style.margin = '0 auto'; + } + } +} + +// Run on page load +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', syncComparisonWidth); +} else { + syncComparisonWidth(); +} + +// Re-sync on window resize +window.addEventListener('resize', syncComparisonWidth); diff --git a/changedetectionio/static/styles/scss/diff-image.scss b/changedetectionio/static/styles/scss/diff-image.scss new file mode 100644 index 00000000..35c05201 --- /dev/null +++ b/changedetectionio/static/styles/scss/diff-image.scss @@ -0,0 +1,259 @@ +/** + * Image Comparison Diff Styles + * Styles for the interactive image comparison slider and screenshot diff visualization + */ + +.comparison-score { + padding: 1em; + background: var(--color-table-stripe); + border-radius: 4px; + margin: 1em 0; + border: 1px solid var(--color-border-table-cell); + color: var(--color-text); +} + +.change-detected { + color: #d32f2f; + font-weight: bold; +} + +.no-change { + color: #388e3c; + font-weight: bold; +} + +.comparison-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1em; + margin: 1em 1em; + + @media (max-width: 1200px) { + grid-template-columns: 1fr; + } +} + +/* Interactive Image Comparison Slider */ +.image-comparison { + position: relative; + width: 100%; + overflow: hidden; + border: 1px solid var(--color-border-table-cell); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + user-select: none; + + img { + display: block; + width: 100%; + height: auto; + max-width: 100%; + border: none; + box-shadow: none; + } +} + +/* Image wrappers with checkered background */ +.comparison-image-wrapper { + position: relative; + width: 100%; + display: flex; + align-items: flex-start; + justify-content: center; + /* Very light checkered background pattern */ + background-color: var(--color-background); + background-image: + linear-gradient(45deg, var(--color-table-stripe) 25%, transparent 25%), + linear-gradient(-45deg, var(--color-table-stripe) 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, var(--color-table-stripe) 75%), + linear-gradient(-45deg, transparent 75%, var(--color-table-stripe) 75%); + background-size: 20px 20px; + background-position: 0 0, 0 10px, 10px -10px, -10px 0px; +} + +.comparison-after { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + clip-path: inset(0 0 0 50%); +} + +.comparison-slider { + position: absolute; + top: 0; + left: 50%; + width: 4px; + height: 100%; + background: #0078e7; + cursor: ew-resize; + transform: translateX(-2px); + z-index: 10; +} + +.comparison-handle { + position: absolute; + top: 50%; + left: 50%; + width: 48px; + height: 48px; + background: #0078e7; + border: 3px solid white; + border-radius: 50%; + transform: translate(-50%, -50%); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + display: flex; + align-items: center; + justify-content: center; + cursor: ew-resize; + transition: top 0.1s ease-out; + + &::after { + content: '⇄'; + color: white; + font-size: 24px; + font-weight: bold; + pointer-events: none; + } +} + +.comparison-labels { + position: absolute; + top: 10px; + width: 100%; + display: flex; + justify-content: space-between; + padding: 0 0px; + z-index: 5; + pointer-events: none; +} + +.comparison-label { + background: rgba(0, 0, 0, 0.7); + color: white; + padding: 0.5em 1em; + border-radius: 4px; + font-size: 0.9em; + font-weight: bold; +} + +.screenshot-panel { + text-align: center; + background: var(--color-background); + border: 1px solid var(--color-border-table-cell); + border-radius: 4px; + padding: 1em; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + + h3 { + margin: 0 0 1em 0; + font-size: 1.1em; + color: var(--color-text); + border-bottom: 2px solid var(--color-background-button-primary); + padding-bottom: 0.5em; + } + + &.diff h3 { + border-bottom-color: #d32f2f; + } + + img { + max-width: 100%; + height: auto; + border: 1px solid var(--color-border-table-cell); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } +} + +.version-selector { + display: inline-block; + margin: 0 0.5em; + + label { + font-weight: bold; + margin-right: 0.5em; + color: var(--color-text); + } +} + +#settings { + background: var(--color-background); + padding: 1.5em; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + margin-bottom: 2em; + border: 1px solid var(--color-border-table-cell); + + h2 { + margin-top: 0; + color: var(--color-text); + } +} + +.diff-fieldset { + border: none; + padding: 0; + margin: 0; +} + +.edit-link { + float: right; + margin-top: -0.5em; +} + +.comparison-description { + color: var(--color-text-input-description); + font-size: 0.9em; + margin-bottom: 1em; +} + +.download-link { + color: var(--color-link); + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 0.3em; + font-size: 0.85em; + + &:hover { + text-decoration: underline; + } +} + +.diff-section-header { + color: #d32f2f; + font-size: 0.9em; + margin-bottom: 1em; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + gap: 1em; +} + +.comparison-history-section { + margin-top: 3em; + padding: 1em; + background: var(--color-background); + border: 1px solid var(--color-border-table-cell); + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + + h3 { + color: var(--color-text); + } + + p { + color: var(--color-text-input-description); + font-size: 0.9em; + } +} + +.history-changed-yes { + color: #d32f2f; + font-weight: bold; +} + +.history-changed-no { + color: #388e3c; +}
{{ entry.method }} {% if entry.changed %} - Yes + Yes {% else %} - No + No {% endif %}