diff --git a/changedetectionio/blueprint/tags/__init__.py b/changedetectionio/blueprint/tags/__init__.py index 7c9fffb0..97361550 100644 --- a/changedetectionio/blueprint/tags/__init__.py +++ b/changedetectionio/blueprint/tags/__init__.py @@ -29,6 +29,7 @@ def construct_blueprint(datastore: ChangeDetectionStore): form=add_form, generate_tag_colors=processors.generate_processor_badge_colors, tag_count=tag_count, + wcag_text_color=processors.wcag_text_color, ) return output diff --git a/changedetectionio/blueprint/tags/form.py b/changedetectionio/blueprint/tags/form.py index e8b3e192..61b8bdb8 100644 --- a/changedetectionio/blueprint/tags/form.py +++ b/changedetectionio/blueprint/tags/form.py @@ -12,12 +12,9 @@ class group_restock_settings_form(restock_settings_form): overrides_watch = BooleanField('Activate for individual watches in this tag/group?', default=False) url_match_pattern = StringField('Auto-apply to watches with URLs matching', render_kw={"placeholder": "e.g. *://example.com/* or github.com/myorg"}) + tag_colour = StringField('Tag colour', default='') class SingleTag(Form): name = StringField('Tag name', [validators.InputRequired()], render_kw={"placeholder": "Name"}) save_button = SubmitField('Save', render_kw={"class": "pure-button pure-button-primary"}) - - - - diff --git a/changedetectionio/blueprint/tags/templates/edit-tag.html b/changedetectionio/blueprint/tags/templates/edit-tag.html index 03305126..ecdc0bbb 100644 --- a/changedetectionio/blueprint/tags/templates/edit-tag.html +++ b/changedetectionio/blueprint/tags/templates/edit-tag.html @@ -57,6 +57,32 @@ {% endif %} +
+ +
+ + + + +
+ {{ _('Leave unchecked to use the auto-generated colour based on the tag name.') }} +
+ diff --git a/changedetectionio/blueprint/tags/templates/groups-overview.html b/changedetectionio/blueprint/tags/templates/groups-overview.html index 2c627889..30bcf5d2 100644 --- a/changedetectionio/blueprint/tags/templates/groups-overview.html +++ b/changedetectionio/blueprint/tags/templates/groups-overview.html @@ -7,6 +7,9 @@ {%- for uuid, tag in available_tags -%} {%- if tag and tag.title -%} {%- set class_name = tag.title|sanitize_tag_class -%} +{%- if tag.get('tag_colour') -%} +.watch-tag-list.tag-{{ class_name }} { background-color: {{ tag.tag_colour }}; color: {{ wcag_text_color(tag.tag_colour) }}; } +{%- else -%} {%- set colors = generate_tag_colors(tag.title) -%} .watch-tag-list.tag-{{ class_name }} { background-color: {{ colors['light']['bg'] }}; @@ -17,6 +20,7 @@ html[data-darkmode="true"] .watch-tag-list.tag-{{ class_name }} { color: {{ colors['dark']['color'] }}; } {%- endif -%} +{%- endif -%} {%- endfor -%} diff --git a/changedetectionio/blueprint/watchlist/__init__.py b/changedetectionio/blueprint/watchlist/__init__.py index ebdfa627..ea879ac6 100644 --- a/changedetectionio/blueprint/watchlist/__init__.py +++ b/changedetectionio/blueprint/watchlist/__init__.py @@ -92,6 +92,7 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe extra_classes='has-queue' if not update_q.empty() else '', form=form, generate_tag_colors=processors.generate_processor_badge_colors, + wcag_text_color=processors.wcag_text_color, guid=datastore.data['app_guid'], has_proxies=proxy_list, hosted_sticky=os.getenv("SALTED_PASS", False) == False, diff --git a/changedetectionio/blueprint/watchlist/templates/watch-overview.html b/changedetectionio/blueprint/watchlist/templates/watch-overview.html index 55fbb9e0..70012e09 100644 --- a/changedetectionio/blueprint/watchlist/templates/watch-overview.html +++ b/changedetectionio/blueprint/watchlist/templates/watch-overview.html @@ -71,6 +71,13 @@ document.addEventListener('DOMContentLoaded', function() { {%- for uuid, tag in tags -%} {%- if tag and tag.title -%} {%- set class_name = tag.title|sanitize_tag_class -%} +{%- if tag.get('tag_colour') -%} +.button-tag.tag-{{ class_name }}, +.watch-tag-list.tag-{{ class_name }} { + background-color: {{ tag.tag_colour }}; + color: {{ wcag_text_color(tag.tag_colour) }}; +} +{%- else -%} {%- set colors = generate_tag_colors(tag.title) -%} .button-tag.tag-{{ class_name }} { background-color: {{ colors['light']['bg'] }}; @@ -92,6 +99,7 @@ html[data-darkmode="true"] .watch-tag-list.tag-{{ class_name }} { color: {{ colors['dark']['color'] }}; } {%- endif -%} +{%- endif -%} {%- endfor -%}
diff --git a/changedetectionio/processors/__init__.py b/changedetectionio/processors/__init__.py index fbb448aa..032245f3 100644 --- a/changedetectionio/processors/__init__.py +++ b/changedetectionio/processors/__init__.py @@ -341,6 +341,18 @@ def get_processor_descriptions(): return descriptions +def wcag_text_color(hex_bg: str) -> str: + """Return #000000 or #ffffff for maximum WCAG contrast against hex_bg.""" + hex_bg = hex_bg.lstrip('#') + if len(hex_bg) != 6: + return '#000000' + r, g, b = (int(hex_bg[i:i+2], 16) / 255 for i in (0, 2, 4)) + def lin(c): + return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4 + L = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b) + return '#000000' if L > 0.179 else '#ffffff' + + def generate_processor_badge_colors(processor_name): """ Generate consistent colors for a processor badge based on its name.