diff --git a/src/app/utils/rs-polyfill.util.spec.ts b/src/app/utils/rs-polyfill.util.spec.ts index 829cad4a2..711c05680 100644 --- a/src/app/utils/rs-polyfill.util.spec.ts +++ b/src/app/utils/rs-polyfill.util.spec.ts @@ -1,6 +1,24 @@ import { RsPolyfillUtil } from './rs-polyfill.util'; describe('RsPolyfillUtil', () => { + it('adds an idempotent native SVG inversion filter for iOS night mode', () => { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + const addFilter = (RsPolyfillUtil as unknown as { + addNightModeImageFilter: (svg: SVGSVGElement) => void; + }).addNightModeImageFilter.bind(RsPolyfillUtil); + + addFilter(svg); + addFilter(svg); + + const filters = svg.querySelectorAll('#mekbay-night-image-invert'); + expect(filters.length).toBe(1); + expect(filters[0].parentElement?.localName).toBe('defs'); + expect(filters[0].getAttribute('color-interpolation-filters')).toBe('sRGB'); + expect(filters[0].querySelector('feFuncR')?.getAttribute('tableValues')).toBe('1 0'); + expect(filters[0].querySelector('feFuncG')?.getAttribute('tableValues')).toBe('1 0'); + expect(filters[0].querySelector('feFuncB')?.getAttribute('tableValues')).toBe('1 0'); + }); + it('injects fluff at outer root coordinates across a nested SVG viewport', () => { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const parent = document.createElementNS('http://www.w3.org/2000/svg', 'g'); diff --git a/src/app/utils/rs-polyfill.util.ts b/src/app/utils/rs-polyfill.util.ts index 1e71e6927..320833dd3 100644 --- a/src/app/utils/rs-polyfill.util.ts +++ b/src/app/utils/rs-polyfill.util.ts @@ -118,6 +118,7 @@ export class RsPolyfillUtil { */ public static addMissingClasses(forceUnit: CBTForceUnit, svg: SVGSVGElement): void { const unit = forceUnit.getUnit(); + this.addNightModeImageFilter(svg); if (unit.type !== 'Mek') { this.addCriticalLocs(svg); } @@ -139,6 +140,33 @@ export class RsPolyfillUtil { this.addCriticalSectionsButtons(unit, svg) } + /** Adds a native SVG inversion filter for iOS WebKit, which ignores CSS invert() on SVG images. */ + private static addNightModeImageFilter(svg: SVGSVGElement): void { + const filterId = 'mekbay-night-image-invert'; + if (svg.getElementById(filterId)) return; + + let defs = Array.from(svg.children) + .find(element => element.localName === 'defs') as SVGDefsElement | undefined; + if (!defs) { + defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); + svg.insertBefore(defs, svg.firstChild); + } + + const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter'); + filter.setAttribute('id', filterId); + filter.setAttribute('color-interpolation-filters', 'sRGB'); + + const componentTransfer = document.createElementNS('http://www.w3.org/2000/svg', 'feComponentTransfer'); + for (const channel of ['R', 'G', 'B']) { + const fn = document.createElementNS('http://www.w3.org/2000/svg', `feFunc${channel}`); + fn.setAttribute('type', 'table'); + fn.setAttribute('tableValues', '1 0'); + componentTransfer.appendChild(fn); + } + filter.appendChild(componentTransfer); + defs.appendChild(filter); + } + public static syncConditionButtons(forceUnit: CBTForceUnit, svg: SVGSVGElement): void { this.addConditionsButtons(forceUnit, svg); } diff --git a/src/styles.scss b/src/styles.scss index 48677b676..ececba7a0 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -2520,7 +2520,7 @@ hr.divider { image { transform: none; - filter: invert(1); + filter: url("#mekbay-night-image-invert"); } #fluff-image-fo {