Allow one-pixel template regions
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m23s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s

This commit is contained in:
lzstealth committed 2026-10-19 17:55:56 +01:00
1 parent 8d75b12354
commit 821593dd8f
9 files changed
+41 -35

No files matched your search

+6
View File
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 2.15.7 - 2026-10-19
### Fixed
- Template regions can now be sized down to one canvas pixel.
## 2.15.6 - 2026-10-18 ## 2.15.6 - 2026-10-18
### Changed ### Changed
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pulse-signage-player", "name": "pulse-signage-player",
"version": "2.15.6", "version": "2.15.7",
"private": false, "private": false,
"description": "Pulse Signage player application bundle", "description": "Pulse Signage player application bundle",
"engines": { "engines": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pulse-signage-web", "name": "pulse-signage-web",
"version": "2.15.6", "version": "2.15.7",
"private": false, "private": false,
"description": "Pulse Signage web and bridge application bundle", "description": "Pulse Signage web and bridge application bundle",
"engines": { "engines": {
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "pulse-signage", "name": "pulse-signage",
"version": "2.15.6", "version": "2.15.7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pulse-signage", "name": "pulse-signage",
"version": "2.15.6", "version": "2.15.7",
"dependencies": { "dependencies": {
"@sparticuz/chromium": "^149.0.0", "@sparticuz/chromium": "^149.0.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pulse-signage", "name": "pulse-signage",
"version": "2.15.6", "version": "2.15.7",
"private": false, "private": false,
"description": "Pulse Signage application with MySQL and media storage", "description": "Pulse Signage application with MySQL and media storage",
"engines": { "engines": {
@@ -56,15 +56,15 @@
if (ratio.ratio >= 1) { if (ratio.ratio >= 1) {
var baseWidth = locked ? 420 : 300; var baseWidth = locked ? 420 : 300;
return { return {
width: Math.max(12, Math.round(baseWidth)), width: Math.max(1, Math.round(baseWidth)),
height: Math.max(12, Math.round(baseWidth / ratio.ratio)) height: Math.max(1, Math.round(baseWidth / ratio.ratio))
}; };
} }
var baseHeight = locked ? 300 : 240; var baseHeight = locked ? 300 : 240;
return { return {
width: Math.max(12, Math.round(baseHeight * ratio.ratio)), width: Math.max(1, Math.round(baseHeight * ratio.ratio)),
height: Math.max(12, Math.round(baseHeight)) height: Math.max(1, Math.round(baseHeight))
}; };
} }
@@ -297,7 +297,7 @@
function clampRegion(region, size, minSize) { function clampRegion(region, size, minSize) {
var bounds = size || { width: 1920, height: 1080 }; var bounds = size || { width: 1920, height: 1080 };
var minimum = minSize || 12; var minimum = minSize || 1;
var x = clamp(region.x, 0, bounds.width - minimum); var x = clamp(region.x, 0, bounds.width - minimum);
var y = clamp(region.y, 0, bounds.height - minimum); var y = clamp(region.y, 0, bounds.height - minimum);
var width = Math.max(minimum, region.width); var width = Math.max(minimum, region.width);
@@ -140,10 +140,10 @@
if (ratio) { if (ratio) {
if (ratio.ratio >= 1) { if (ratio.ratio >= 1) {
var baseWidth = locked ? 420 : 300; var baseWidth = locked ? 420 : 300;
return { width: Math.max(12, Math.round(baseWidth)), height: Math.max(12, Math.round(baseWidth / ratio.ratio)) }; return { width: Math.max(1, Math.round(baseWidth)), height: Math.max(1, Math.round(baseWidth / ratio.ratio)) };
} }
var baseHeight = locked ? 300 : 240; var baseHeight = locked ? 300 : 240;
return { width: Math.max(12, Math.round(baseHeight * ratio.ratio)), height: Math.max(12, Math.round(baseHeight)) }; return { width: Math.max(1, Math.round(baseHeight * ratio.ratio)), height: Math.max(1, Math.round(baseHeight)) };
} }
return { width: locked ? 420 : 300, height: locked ? 240 : 120 }; return { width: locked ? 420 : 300, height: locked ? 240 : 120 };
} }
@@ -1015,7 +1015,7 @@
function fitRegionToCanvas(region, aspect) { function fitRegionToCanvas(region, aspect) {
var size = getCanvasSize(); var size = getCanvasSize();
var minSize = 12; var minSize = 1;
var next = { var next = {
x: Math.round(Number(region && region.x !== undefined ? region.x : 0)), x: Math.round(Number(region && region.x !== undefined ? region.x : 0)),
y: Math.round(Number(region && region.y !== undefined ? region.y : 0)), y: Math.round(Number(region && region.y !== undefined ? region.y : 0)),
@@ -1075,8 +1075,8 @@
var next = { var next = {
x: current.x, x: current.x,
y: current.y, y: current.y,
width: Math.max(12, Math.round(current.width || 0)), width: Math.max(1, Math.round(current.width || 0)),
height: Math.max(12, Math.round(current.height || 0)) height: Math.max(1, Math.round(current.height || 0))
}; };
var lockRatio = normalizeLockRatio(current.lock_ratio); var lockRatio = normalizeLockRatio(current.lock_ratio);
var aspect = lockRatio ? parseLockRatio(lockRatio) : null; var aspect = lockRatio ? parseLockRatio(lockRatio) : null;
@@ -1084,20 +1084,20 @@
if (aspect) { if (aspect) {
if (changedField === 'height') { if (changedField === 'height') {
next.width = Math.max(12, Math.round(next.height * aspect.ratio)); next.width = Math.max(1, Math.round(next.height * aspect.ratio));
} else { } else {
next.height = Math.max(12, Math.round(next.width / aspect.ratio)); next.height = Math.max(1, Math.round(next.width / aspect.ratio));
} }
if (next.width > size.width || next.height > size.height) { if (next.width > size.width || next.height > size.height) {
var scale = Math.min(size.width / Math.max(1, next.width), size.height / Math.max(1, next.height), 1); var scale = Math.min(size.width / Math.max(1, next.width), size.height / Math.max(1, next.height), 1);
if (scale < 1) { if (scale < 1) {
next.width = Math.max(12, Math.round(next.width * scale)); next.width = Math.max(1, Math.round(next.width * scale));
next.height = Math.max(12, Math.round(next.height * scale)); next.height = Math.max(1, Math.round(next.height * scale));
if (changedField === 'height') { if (changedField === 'height') {
next.width = Math.max(12, Math.round(next.height * aspect.ratio)); next.width = Math.max(1, Math.round(next.height * aspect.ratio));
} else { } else {
next.height = Math.max(12, Math.round(next.width / aspect.ratio)); next.height = Math.max(1, Math.round(next.width / aspect.ratio));
} }
} }
} }
@@ -1237,9 +1237,9 @@
var current = readCard(card); var current = readCard(card);
var aspect = parseLockRatio(current.lock_ratio); var aspect = parseLockRatio(current.lock_ratio);
if (aspect && changedField === 'width' && current.width > 0) { if (aspect && changedField === 'width' && current.width > 0) {
writeCard(card, { height: Math.max(12, Math.round(current.width / aspect.ratio)) }); writeCard(card, { height: Math.max(1, Math.round(current.width / aspect.ratio)) });
} else if (aspect && changedField === 'height' && current.height > 0) { } else if (aspect && changedField === 'height' && current.height > 0) {
writeCard(card, { width: Math.max(12, Math.round(current.height * aspect.ratio)) }); writeCard(card, { width: Math.max(1, Math.round(current.height * aspect.ratio)) });
} }
} else { } else {
normalizeRegionCard(card, changedField); normalizeRegionCard(card, changedField);
@@ -1589,7 +1589,7 @@
function clampRegion(region) { function clampRegion(region) {
var size = getCanvasSize(); var size = getCanvasSize();
var minSize = 12; var minSize = 1;
var x = clamp(region.x, 0, size.width - minSize); var x = clamp(region.x, 0, size.width - minSize);
var y = clamp(region.y, 0, size.height - minSize); var y = clamp(region.y, 0, size.height - minSize);
var width = Math.max(minSize, region.width); var width = Math.max(minSize, region.width);
@@ -1619,7 +1619,7 @@
draft.end = toCanvasPoint(upEvent); draft.end = toCanvasPoint(upEvent);
var width = Math.abs(draft.end.x - draft.start.x); var width = Math.abs(draft.end.x - draft.start.x);
var height = Math.abs(draft.end.y - draft.start.y); var height = Math.abs(draft.end.y - draft.start.y);
if (width >= 8 && height >= 8) { if (width >= 1 && height >= 1) {
var region = fitRegionToCanvas({ x: Math.min(draft.start.x, draft.end.x), y: Math.min(draft.start.y, draft.end.y), width: width, height: height }); var region = fitRegionToCanvas({ x: Math.min(draft.start.x, draft.end.x), y: Math.min(draft.start.y, draft.end.y), width: width, height: height });
var name = getNextDefaultRegionName('text'); var name = getNextDefaultRegionName('text');
addRegion({ region_key: name, label: name, region_type: 'text', x: region.x, y: region.y, width: region.width, height: region.height, z_index: 1 }); addRegion({ region_key: name, label: name, region_type: 'text', x: region.x, y: region.y, width: region.width, height: region.height, z_index: 1 });
@@ -1671,7 +1671,7 @@
var lockRatio = normalizeLockRatio(startRegion.lock_ratio); var lockRatio = normalizeLockRatio(startRegion.lock_ratio);
var aspect = lockRatio ? parseLockRatio(lockRatio) : null; var aspect = lockRatio ? parseLockRatio(lockRatio) : null;
var canvasSize = getCanvasSize(); var canvasSize = getCanvasSize();
var minSize = 12; var minSize = 1;
function clampResizeDelta(dx, dy) { function clampResizeDelta(dx, dy) {
if (dir.indexOf('w') !== -1) { if (dir.indexOf('w') !== -1) {
@@ -1690,17 +1690,17 @@
} }
function fitFromWidth(width) { function fitFromWidth(width) {
var nextWidth = Math.max(12, Math.round(width)); var nextWidth = Math.max(1, Math.round(width));
return { return {
width: nextWidth, width: nextWidth,
height: aspect ? Math.max(12, Math.round(nextWidth / aspect.ratio)) : startRegion.height height: aspect ? Math.max(1, Math.round(nextWidth / aspect.ratio)) : startRegion.height
}; };
} }
function fitFromHeight(height) { function fitFromHeight(height) {
var nextHeight = Math.max(12, Math.round(height)); var nextHeight = Math.max(1, Math.round(height));
return { return {
width: aspect ? Math.max(12, Math.round(nextHeight * aspect.ratio)) : startRegion.width, width: aspect ? Math.max(1, Math.round(nextHeight * aspect.ratio)) : startRegion.width,
height: nextHeight height: nextHeight
}; };
} }
@@ -1752,8 +1752,8 @@
if (dir.indexOf('n') !== -1) { next.y = startRegion.y + dy; next.height = startRegion.height - dy; } if (dir.indexOf('n') !== -1) { next.y = startRegion.y + dy; next.height = startRegion.height - dy; }
if (dir.indexOf('s') !== -1) { next.height = startRegion.height + dy; } if (dir.indexOf('s') !== -1) { next.height = startRegion.height + dy; }
} }
if (next.width < 12) { if (dir.indexOf('w') !== -1) { next.x -= 12 - next.width; } next.width = 12; } if (next.width < 1) { if (dir.indexOf('w') !== -1) { next.x -= 1 - next.width; } next.width = 1; }
if (next.height < 12) { if (dir.indexOf('n') !== -1) { next.y -= 12 - next.height; } next.height = 12; } if (next.height < 1) { if (dir.indexOf('n') !== -1) { next.y -= 1 - next.height; } next.height = 1; }
next = fitRegionToCanvas(next, aspect); next = fitRegionToCanvas(next, aspect);
writeCard(cardAt(index), { x: next.x, y: next.y, width: next.width, height: next.height }); writeCard(cardAt(index), { x: next.x, y: next.y, width: next.width, height: next.height });
markTemplateFormDirty(); markTemplateFormDirty();
+2 -2
View File
@@ -174,8 +174,8 @@
<label>Z-Index<input class="form-control" type="number" name="region_z[]" min="1" step="1" value="1" required /></label> <label>Z-Index<input class="form-control" type="number" name="region_z[]" min="1" step="1" value="1" required /></label>
</div> </div>
<div class="region-field-grid region-field-grid--size"> <div class="region-field-grid region-field-grid--size">
<label>Width<input class="form-control" type="number" name="region_width[]" value="300" required /></label> <label>Width<input class="form-control" type="number" name="region_width[]" min="1" step="1" value="300" required /></label>
<label>Height<input class="form-control" type="number" name="region_height[]" value="120" required /></label> <label>Height<input class="form-control" type="number" name="region_height[]" min="1" step="1" value="120" required /></label>
<label>Lock ratio<input class="form-control" type="text" name="region_lock_ratio[]" value="" maxlength="20" data-limit-text-length placeholder="Ratio, e.g. 16:9" /></label> <label>Lock ratio<input class="form-control" type="text" name="region_lock_ratio[]" value="" maxlength="20" data-limit-text-length placeholder="Ratio, e.g. 16:9" /></label>
<label>Remove<button type="button" class="btn btn-outline-danger w-100" data-region-remove-button{{#if isEdit}} disabled{{/if}}>Remove</button></label> <label>Remove<button type="button" class="btn btn-outline-danger w-100" data-region-remove-button{{#if isEdit}} disabled{{/if}}>Remove</button></label>
</div> </div>
+1 -1
View File
@@ -443,7 +443,7 @@ test('region dimensions allow clearing and typing below the minimum until commit
assert.equal(harness.rawValue(field), value); assert.equal(harness.rawValue(field), value);
} }
harness.edit(field, '1', 'change'); harness.edit(field, '1', 'change');
assert.equal(harness.value(field), 12); assert.equal(harness.value(field), 1);
harness.edit(field, '9999', 'change'); harness.edit(field, '9999', 'change');
assert.equal(harness.value(field), field === 'width' ? 1920 : 1080); assert.equal(harness.value(field), field === 'width' ? 1920 : 1080);
} }