From 38d82d2ac6812b11b2bafa40e4e1bf03faa6757c Mon Sep 17 00:00:00 2001 From: Mark Rapson Date: Wed, 5 Aug 2026 19:51:54 +0100 Subject: [PATCH] Fix QR background gradient handling --- CHANGELOG.md | 6 +++++ package.json | 2 +- src/data/qr-code.js | 15 +++---------- src/web/public/js/regions/type/qr-code.js | 12 +++++++--- test/qr-code.test.js | 27 +++++++++++++++++++++++ 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ed658..27fa2ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## 2.5.7 - 2026-08-05 + +### Fixed + +- QR template backgrounds now use a single solid color only, and the editor no longer exposes a background gradient mode. + ## 2.5.6 - 2026-08-05 ### Added diff --git a/package.json b/package.json index 2c5db7f..8de5a2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pulse-signage", - "version": "2.5.6", + "version": "2.5.7", "private": false, "description": "Pulse Signage application with MySQL and media storage", "repository": { diff --git a/src/data/qr-code.js b/src/data/qr-code.js index ebe7520..908bb25 100644 --- a/src/data/qr-code.js +++ b/src/data/qr-code.js @@ -312,14 +312,6 @@ function buildQrStylingOptions(source) { { offset: 1, color: normalizeQrStyleColor(qrStyle.qr_corners_dot_gradient_color_2, '#ffffff') } ] } : null; - const backgroundGradient = qrStyle.qr_background_color_mode === 'gradient' ? { - type: String(qrStyle.qr_background_gradient_type || 'linear').trim() || 'linear', - rotation: normalizeQrStyleNumber(qrStyle.qr_background_gradient_rotation, 0, undefined, undefined), - colorStops: [ - { offset: 0, color: normalizeQrStyleColor(qrStyle.qr_background_gradient_color_1, normalizeQrStyleColor(qrStyle.qr_background_color, '#ffffff')) }, - { offset: 1, color: normalizeQrStyleColor(qrStyle.qr_background_gradient_color_2, '#ffffff') } - ] - } : null; return { width: QR_PNG_WIDTH, @@ -344,8 +336,7 @@ function buildQrStylingOptions(source) { gradient: cornersDotGradient || undefined }, backgroundOptions: { - color: qrStyle.qr_background_transparent ? 'transparent' : normalizeQrStyleColor(qrStyle.qr_background_color, '#ffffff'), - gradient: backgroundGradient || undefined + color: qrStyle.qr_background_transparent ? 'transparent' : normalizeQrStyleColor(qrStyle.qr_background_color, '#ffffff') }, image: String(qrStyle.qr_image || '').trim() || undefined, imageOptions: { @@ -491,7 +482,7 @@ async function buildQrCodeContent(value, options) { qr_border_radius: Number.isFinite(Number(source.qr_border_radius)) ? Math.max(0, Math.round(Number(source.qr_border_radius))) : undefined, qr_background_color: String(source.qr_background_color === undefined || source.qr_background_color === null ? '' : source.qr_background_color).trim() || undefined, qr_background_transparent: hasBooleanField('qr_background_transparent') ? Boolean(source.qr_background_transparent) : undefined, - qr_background_color_mode: String(source.qr_background_color_mode === undefined || source.qr_background_color_mode === null ? '' : source.qr_background_color_mode).trim() || undefined, + qr_background_color_mode: 'single', qr_background_gradient_type: String(source.qr_background_gradient_type === undefined || source.qr_background_gradient_type === null ? '' : source.qr_background_gradient_type).trim() || undefined, qr_background_gradient_rotation: normalizeNumber(source.qr_background_gradient_rotation), qr_background_gradient_color_1: normalizeHex(source.qr_background_gradient_color_1), @@ -533,7 +524,7 @@ async function buildQrCodeContent(value, options) { } }); - content.qr_background_use_gradient = content.qr_background_gradient_type && content.qr_background_gradient_type !== 'none'; + content.qr_background_use_gradient = false; content.qr_dots_use_gradient = content.qr_dots_gradient_type && content.qr_dots_gradient_type !== 'none'; content.qr_corners_square_use_gradient = content.qr_corners_square_gradient_type && content.qr_corners_square_gradient_type !== 'none'; content.qr_corners_dot_use_gradient = content.qr_corners_dot_gradient_type && content.qr_corners_dot_gradient_type !== 'none'; diff --git a/src/web/public/js/regions/type/qr-code.js b/src/web/public/js/regions/type/qr-code.js index 3479e03..626c283 100644 --- a/src/web/public/js/regions/type/qr-code.js +++ b/src/web/public/js/regions/type/qr-code.js @@ -251,6 +251,7 @@ qr_margin: normalizeMarginValue(source.qr_margin, defaultQrOptions.margin), qr_background_color: normalizeHexColorValue(source.qr_background_color, '#ffffff'), qr_background_transparent: normalizeBooleanValue(source.qr_background_transparent, false), + qr_background_color_mode: 'single', qr_background_gradient_type: normalizeGradientTypeValue(source.qr_background_gradient_type, 'linear'), qr_background_gradient_rotation: normalizeGradientRotationValue(source.qr_background_gradient_rotation, 0), qr_background_gradient_color_1: normalizeHexColorValue(source.qr_background_gradient_color_1, '#ffffff'), @@ -729,7 +730,6 @@ var dotsGradient = style.qr_dots_color_mode === 'gradient' ? buildGradientOptions('qr_dots', style) : null; var cornersSquareGradient = style.qr_corners_square_color_mode === 'gradient' ? buildGradientOptions('qr_corners_square', style) : null; var cornersDotGradient = style.qr_corners_dot_color_mode === 'gradient' ? buildGradientOptions('qr_corners_dot', style) : null; - var backgroundGradient = style.qr_background_color_mode === 'gradient' ? buildGradientOptions('qr_background', style) : null; return { width: defaultQrOptions.width, height: defaultQrOptions.height, @@ -753,8 +753,7 @@ gradient: cornersDotGradient || undefined }, backgroundOptions: { - color: style.qr_background_transparent ? 'transparent' : style.qr_background_color, - gradient: backgroundGradient || undefined + color: style.qr_background_transparent ? 'transparent' : style.qr_background_color }, image: style.qr_image || undefined, imageOptions: { @@ -818,6 +817,13 @@ function renderColorModeSection(accordionId, sectionKey, prefix, regionId, label, style, singleControlsHtml, gradientLabel, fallbackColor, currentOpen, styleSelectHtml) { var mode = normalizeColorModeValue(style[prefix + '_color_mode'], 'single'); + if (prefix === 'qr_background') { + return renderAccordionSection(accordionId, sectionKey, label, '' + + '
' + + '
' + singleControlsHtml + '
' + + styleSelectHtml + + '
', currentOpen); + } var colorModeSelect = '' + '
' + '' + diff --git a/test/qr-code.test.js b/test/qr-code.test.js index bf33a89..de895c7 100644 --- a/test/qr-code.test.js +++ b/test/qr-code.test.js @@ -18,6 +18,33 @@ test('createStyledQrCodeSvg renders a styled qr svg for non-empty values', async assert.match(svg, /]/); }); +test('createStyledQrCodeSvg keeps the qr background solid when gradient fields are provided', async () => { + const svg = await createStyledQrCodeSvg({ + value: 'https://example.com', + qr_background_color_mode: 'gradient', + qr_background_gradient_type: 'radial', + qr_background_gradient_color_1: '#ff0000', + qr_background_gradient_color_2: '#0000ff' + }); + + assert.equal(typeof svg, 'string'); + assert.match(svg, /]/); + assert.doesNotMatch(svg, / { + const content = await buildQrCodeContent({ + value: 'https://example.com', + qr_background_color_mode: 'gradient', + qr_background_gradient_type: 'radial', + qr_background_gradient_color_1: '#ff0000', + qr_background_gradient_color_2: '#0000ff' + }); + + assert.equal(content.qr_background_color_mode, 'single'); + assert.equal(content.qr_background_use_gradient, false); +}); + test('createStyledQrCodeDataUrl renders a styled qr data url for non-empty values', async () => { const dataUrl = await createStyledQrCodeDataUrl('https://example.com');