Save worktree changes

This commit is contained in:
2026-07-25 02:29:19 +01:00
parent 8d3b7d557b
commit db9d718cd8
170 changed files with 11719 additions and 3414 deletions
@@ -59,6 +59,44 @@
return utils.clamp ? utils.clamp(value, min, max) : Math.max(min, Math.min(max, value));
}
function normalizeLockRatio(value) {
return utils.normalizeLockRatio ? utils.normalizeLockRatio(value) : String(value || '').trim().replace(/\s+/g, '');
}
function parseLockRatio(value) {
if (utils.parseLockRatio) {
return utils.parseLockRatio(value);
}
var normalized = normalizeLockRatio(value);
if (!normalized || !/^\d+:\d+$/.test(normalized)) {
return null;
}
var parts = normalized.split(':');
var width = Number(parts[0]);
var height = Number(parts[1]);
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
return null;
}
return { label: normalized, ratio: width / height };
}
function getDefaultRegionSize(regionType, lockRatio) {
if (utils.getDefaultRegionSize) {
return utils.getDefaultRegionSize(regionType, lockRatio);
}
var ratio = parseLockRatio(lockRatio);
var locked = regionType === 'image' || regionType === 'webpage' || regionType === 'html' || regionType === 'rtmp' || regionType === 'rss';
if (ratio) {
if (ratio.ratio >= 1) {
var baseWidth = locked ? 420 : 300;
return { width: Math.max(12, Math.round(baseWidth)), height: Math.max(12, Math.round(baseWidth / ratio.ratio)) };
}
var baseHeight = locked ? 300 : 240;
return { width: Math.max(12, Math.round(baseHeight * ratio.ratio)), height: Math.max(12, Math.round(baseHeight)) };
}
return { width: locked ? 420 : 300, height: locked ? 240 : 120 };
}
function getCards() {
return Array.prototype.slice.call(regionList.querySelectorAll('.region-item'));
}
@@ -126,6 +164,7 @@
label: getRegionName(card),
region_type: card.querySelector('[name="region_type[]"]').value,
font_family: card.querySelector('[name="font_family[]"]').value,
lock_ratio: normalizeLockRatio(card.querySelector('[name="region_lock_ratio[]"]').value),
x: Number(card.querySelector('[name="region_x[]"]').value || 0),
y: Number(card.querySelector('[name="region_y[]"]').value || 0),
width: Number(card.querySelector('[name="region_width[]"]').value || 0),
@@ -148,6 +187,7 @@
}
if (values.region_type !== undefined) { card.querySelector('[name="region_type[]"]').value = values.region_type; }
if (values.font_family !== undefined) { card.querySelector('[name="font_family[]"]').value = values.font_family; }
if (values.lock_ratio !== undefined) { card.querySelector('[name="region_lock_ratio[]"]').value = normalizeLockRatio(values.lock_ratio); }
if (values.x !== undefined) { card.querySelector('[name="region_x[]"]').value = Math.round(values.x); }
if (values.y !== undefined) { card.querySelector('[name="region_y[]"]').value = Math.round(values.y); }
if (values.width !== undefined) { card.querySelector('[name="region_width[]"]').value = Math.round(values.width); }
@@ -229,8 +269,50 @@
stage.style.backgroundColor = backgroundColorInput && backgroundColorInput.value ? backgroundColorInput.value : '#111111';
}
function updateRegionLockBadge(card) {
var lockChip = card.querySelector('[data-region-lock-chip]');
if (!lockChip) {
return;
}
var lockRatio = normalizeLockRatio(card.querySelector('[name="region_lock_ratio[]"]').value);
if (!lockRatio) {
lockChip.hidden = true;
lockChip.textContent = '';
return;
}
lockChip.hidden = false;
lockChip.textContent = 'Locked ' + lockRatio;
}
function getRegionLockRatio(card) {
return normalizeLockRatio(card.querySelector('[name="region_lock_ratio[]"]').value);
}
function isRegionLocked(card) {
return Boolean(getRegionLockRatio(card));
}
function syncLockedDimensions(card, changedField) {
var lockRatio = getRegionLockRatio(card);
var aspect = lockRatio ? parseLockRatio(lockRatio) : null;
if (!aspect) {
return;
}
var widthInput = card.querySelector('[name="region_width[]"]');
var heightInput = card.querySelector('[name="region_height[]"]');
var width = Math.max(1, Number(widthInput.value || 0));
var height = Math.max(1, Number(heightInput.value || 0));
if (changedField === 'width') {
heightInput.value = Math.max(1, Math.round(width / aspect.ratio));
} else if (changedField === 'height') {
widthInput.value = Math.max(1, Math.round(height * aspect.ratio));
}
}
function getRegionChipLabel(regionType) {
return regionType === 'image' ? 'Image' : regionType === 'webpage' ? 'Webpage' : regionType === 'html' ? 'HTML' : 'Text';
return regionType === 'image' ? 'Image' : regionType === 'webpage' ? 'Webpage' : regionType === 'html' ? 'HTML' : regionType === 'rtmp' ? 'RTMP' : regionType === 'rss' ? 'RSS' : 'Text';
}
function populateRegionCard(card, region) {
@@ -252,7 +334,7 @@
nameInput.value = region.region_key || region.label || '';
}
if (fontFamilyInput) {
fontFamilyInput.value = region.region_type === 'image' ? '' : (region.font_family || 'Arial');
fontFamilyInput.value = region.region_type === 'image' || region.region_type === 'rtmp' ? '' : (region.font_family || 'Arial');
}
if (regionTypeInput) {
regionTypeInput.value = region.region_type || 'text';
@@ -263,11 +345,16 @@
if (regionLabelInput) {
regionLabelInput.value = region.label || region.region_key || '';
}
var regionLockRatioInput = card.querySelector('[name="region_lock_ratio[]"]');
if (regionLockRatioInput) {
regionLockRatioInput.value = normalizeLockRatio(region.lock_ratio);
}
card.querySelector('[name="region_x[]"]').value = valueOrDefault(region.x, 80);
card.querySelector('[name="region_y[]"]').value = valueOrDefault(region.y, 80);
card.querySelector('[name="region_z[]"]').value = valueOrDefault(region.z_index, 1);
card.querySelector('[name="region_width[]"]').value = valueOrDefault(region.width, 300);
card.querySelector('[name="region_height[]"]').value = valueOrDefault(region.height, 120);
updateRegionLockBadge(card);
}
function updateRegionLabel(card) {
@@ -293,6 +380,9 @@
}
populateRegionCard(card, region);
var nameInput = card.querySelector('[name="region_name[]"]');
var lockRatioInput = card.querySelector('[name="region_lock_ratio[]"]');
var widthInput = card.querySelector('[name="region_width[]"]');
var heightInput = card.querySelector('[name="region_height[]"]');
nameInput.addEventListener('input', function () {
syncRegionIdentity(card, nameInput.value);
updateRegionLabel(card);
@@ -300,6 +390,30 @@
renderRegionSidebar();
renderOverlay();
});
if (lockRatioInput) {
lockRatioInput.addEventListener('input', function () {
updateRegionLockBadge(card);
requestOverlayRender();
});
}
if (widthInput) {
widthInput.addEventListener('input', function () {
if (isRegionLocked(card)) {
syncLockedDimensions(card, 'width');
updateRegionLockBadge(card);
}
requestOverlayRender();
});
}
if (heightInput) {
heightInput.addEventListener('input', function () {
if (isRegionLocked(card)) {
syncLockedDimensions(card, 'height');
updateRegionLockBadge(card);
}
requestOverlayRender();
});
}
card.addEventListener('click', function (event) {
if (event.target && event.target.classList && event.target.classList.contains('remove-region')) {
return;
@@ -423,15 +537,17 @@
function createDefaultRegion(type) {
var count = getCards().length + 1;
var name = 'region_' + count;
var size = getDefaultRegionSize(type, '');
return {
region_key: name,
label: name,
region_type: type,
font_family: type === 'text' || type === 'html' ? 'Arial' : '',
font_family: type === 'text' || type === 'html' || type === 'rss' || type === 'api' ? 'Arial' : '',
x: 80,
y: 80,
width: type === 'image' || type === 'webpage' || type === 'html' ? 420 : 300,
height: type === 'image' || type === 'webpage' || type === 'html' ? 240 : 120,
width: size.width,
height: size.height,
lock_ratio: '',
z_index: 1
};
}
@@ -499,15 +615,69 @@
function resizeFromHandle(index, dir, event) {
var startPoint = toCanvasPoint(event);
var startRegion = readCard(cardAt(index));
var lockRatio = normalizeLockRatio(startRegion.lock_ratio);
var aspect = lockRatio ? parseLockRatio(lockRatio) : null;
function fitFromWidth(width) {
var nextWidth = Math.max(12, Math.round(width));
return {
width: nextWidth,
height: aspect ? Math.max(12, Math.round(nextWidth / aspect.ratio)) : startRegion.height
};
}
function fitFromHeight(height) {
var nextHeight = Math.max(12, Math.round(height));
return {
width: aspect ? Math.max(12, Math.round(nextHeight * aspect.ratio)) : startRegion.width,
height: nextHeight
};
}
function moveHandler(moveEvent) {
var currentPoint = toCanvasPoint(moveEvent);
var dx = currentPoint.x - startPoint.x;
var dy = currentPoint.y - startPoint.y;
var next = { x: startRegion.x, y: startRegion.y, width: startRegion.width, height: startRegion.height };
if (dir.indexOf('w') !== -1) { next.x = startRegion.x + dx; next.width = startRegion.width - dx; }
if (dir.indexOf('e') !== -1) { next.width = startRegion.width + dx; }
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 (aspect) {
if (dir === 'e') {
var eastSize = fitFromWidth(startRegion.width + dx);
next.width = eastSize.width;
next.height = eastSize.height;
next.y = startRegion.y + Math.round((startRegion.height - next.height) / 2);
} else if (dir === 'w') {
var westSize = fitFromWidth(startRegion.width - dx);
next.width = westSize.width;
next.height = westSize.height;
next.x = startRegion.x + startRegion.width - next.width;
next.y = startRegion.y + Math.round((startRegion.height - next.height) / 2);
} else if (dir === 'n') {
var northSize = fitFromHeight(startRegion.height - dy);
next.width = northSize.width;
next.height = northSize.height;
next.x = startRegion.x + Math.round((startRegion.width - next.width) / 2);
next.y = startRegion.y + startRegion.height - next.height;
} else if (dir === 's') {
var southSize = fitFromHeight(startRegion.height + dy);
next.width = southSize.width;
next.height = southSize.height;
next.x = startRegion.x + Math.round((startRegion.width - next.width) / 2);
} else {
var useWidth = Math.abs(dx) >= Math.abs(dy * aspect.ratio);
var cornerSize = useWidth ? fitFromWidth(dir.indexOf('w') !== -1 ? startRegion.width - dx : startRegion.width + dx) : fitFromHeight(dir.indexOf('n') !== -1 ? startRegion.height - dy : startRegion.height + dy);
next.width = cornerSize.width;
next.height = cornerSize.height;
if (dir.indexOf('w') !== -1) { next.x = startRegion.x + startRegion.width - next.width; }
if (dir.indexOf('n') !== -1) { next.y = startRegion.y + startRegion.height - next.height; }
if (dir.indexOf('e') !== -1) { next.x = startRegion.x; }
if (dir.indexOf('s') !== -1) { next.y = startRegion.y; }
}
} else {
if (dir.indexOf('w') !== -1) { next.x = startRegion.x + dx; next.width = startRegion.width - dx; }
if (dir.indexOf('e') !== -1) { next.width = startRegion.width + dx; }
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 (next.width < 12) { if (dir.indexOf('w') !== -1) { next.x -= 12 - next.width; } next.width = 12; }
if (next.height < 12) { if (dir.indexOf('n') !== -1) { next.y -= 12 - next.height; } next.height = 12; }
next = clampRegion(next);
@@ -586,6 +756,12 @@
event.preventDefault();
return;
}
getCards().forEach(function (card) {
if (isRegionLocked(card)) {
syncLockedDimensions(card, 'width');
updateRegionLockBadge(card);
}
});
syncCanvasSizeSelection();
event.formData.set('regions_json', JSON.stringify(getCards().map(readCard)));
});
@@ -594,6 +770,12 @@
if (!validateRegionNames()) {
return;
}
getCards().forEach(function (card) {
if (isRegionLocked(card)) {
syncLockedDimensions(card, 'width');
updateRegionLockBadge(card);
}
});
syncCanvasSizeSelection();
regionsJsonInput.value = JSON.stringify(getCards().map(readCard));
});