Fix Gitea Issue #1: Text-Generator Verbesserungen

- Rahmenform Standard auf 'Rechteck' gesetzt (statt 'Keiner')
- Pfeile werden nun korrekt im SVG-Bereich dargestellt (viewBox + Padding)
- Numerische Eingabefelder neben allen Slidern hinzugefügt
- Text-Positionierung im Rahmen ermöglicht (horizontal + vertikal)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
architeur
2025-12-14 21:51:20 +01:00
parent d707c5001d
commit acfd23ec57
5 changed files with 265 additions and 51 deletions

View File

@@ -16,6 +16,8 @@ const TextGenState = {
paddingRight: 10,
paddingBottom: 10,
paddingLeft: 10,
textAlignX: 'center',
textAlignY: 'center',
lineStyle: 'solid',
lineWeight: 2,
arrow: 'none',
@@ -38,6 +40,8 @@ const TextGenState = {
paddingRight: 10,
paddingBottom: 10,
paddingLeft: 10,
textAlignX: 'center',
textAlignY: 'center',
lineStyle: 'solid',
lineWeight: 2,
arrow: 'none',

View File

@@ -183,25 +183,49 @@ var SvgGenerator = {
var color = options.color;
var lines = options.lines;
var lineHeight = options.lineHeight;
var alignX = options.alignX || 'center';
var alignY = options.alignY || 'center';
var frameWidth = options.frameWidth || 0;
var frameHeight = options.frameHeight || 0;
var group = draw.group();
var totalHeight = lines.length * lineHeight;
var startY = y - (totalHeight / 2) + (fontSize * 0.8);
var startY;
// Vertikale Ausrichtung
switch(alignY) {
case 'top':
startY = y - frameHeight / 2 + fontSize * 0.8 + 5;
break;
case 'bottom':
startY = y + frameHeight / 2 - totalHeight + fontSize * 0.8 - 5;
break;
default: // center
startY = y - (totalHeight / 2) + (fontSize * 0.8);
}
// Text-Anchor fuer horizontale Ausrichtung
var anchor = alignX === 'left' ? 'start' : (alignX === 'right' ? 'end' : 'middle');
var textX = x;
if (alignX === 'left') {
textX = x - frameWidth / 2 + 5;
} else if (alignX === 'right') {
textX = x + frameWidth / 2 - 5;
}
for (var i = 0; i < lines.length; i++) {
var lineY = startY + (i * lineHeight);
var lineText = lines[i] || ' ';
// Verwende plain SVG text element fuer bessere Kontrolle
var textEl = draw.plain(lineText)
.font({
family: 'Arial, sans-serif',
size: fontSize,
anchor: 'middle'
anchor: anchor
})
.fill(color)
.attr('x', x)
.attr('x', textX)
.attr('y', lineY)
.attr('text-anchor', 'middle');
.attr('text-anchor', anchor);
group.add(textEl);
}
return group;
@@ -224,13 +248,13 @@ var SvgGenerator = {
if (s.arrow !== 'none') {
var angleRad = Math.abs(s.arrowAngle) * Math.PI / 180;
var bendFactor = s.arrowBend / 100;
var effectiveLength = s.arrowLength * (1 - bendFactor);
var angledPart = s.arrowLength * (1 - bendFactor);
// Hauptrichtung: volle Laenge + Spitze + Puffer
arrowSpace = s.arrowLength + s.arrowTipLength + 20;
// Hauptrichtung: volle Laenge + Spitze + grosszuegiger Puffer
arrowSpace = s.arrowLength + s.arrowTipLength + s.arrowSize + 25;
// Seitliche Abweichung durch Winkel
arrowSideSpace = Math.abs(Math.sin(angleRad) * effectiveLength) + s.arrowSize + 10;
// Seitliche Abweichung durch Winkel (beruecksichtige Pfeilspitze)
arrowSideSpace = Math.abs(Math.sin(angleRad) * angledPart) + s.arrowSize + s.arrowTipLength + 15;
}
var svgWidth = minWidth;
@@ -262,7 +286,14 @@ var SvgGenerator = {
break;
}
var draw = SVG().size(svgWidth, svgHeight);
// Puffer um alle Elemente
var padding = 5;
svgWidth += padding * 2;
svgHeight += padding * 2;
offsetX += padding;
offsetY += padding;
var draw = SVG().size(svgWidth, svgHeight).viewbox(0, 0, svgWidth, svgHeight);
var frameX = offsetX;
var frameY = offsetY;
@@ -295,7 +326,11 @@ var SvgGenerator = {
this.createText(draw, s.text, textCx, textCy, {
fontSize: s.fontSize, color: s.textColor,
lines: textMetrics.lines, lineHeight: textMetrics.lineHeight
lines: textMetrics.lines, lineHeight: textMetrics.lineHeight,
alignX: s.textAlignX || 'center',
alignY: s.textAlignY || 'center',
frameWidth: minWidth,
frameHeight: minHeight
});
return draw.svg();
@@ -353,11 +388,11 @@ var SvgGenerator = {
break;
}
// Stelle sicher, dass alle Werte positiv sind
width = Math.max(width, 80);
height = Math.max(height, 60);
// Stelle sicher, dass alle Werte positiv sind und genuegend Platz haben
width = Math.max(width, 100);
height = Math.max(height, 80);
var draw = SVG().size(width, height);
var draw = SVG().size(width, height).viewbox(0, 0, width, height);
// Simuliere einen Rahmen-Punkt als Startpunkt
var frameRect = {

View File

@@ -20,6 +20,7 @@ var UiBindings = {
// Text & Farben
e.textInput = document.getElementById('customText');
e.fontSizeInput = document.getElementById('fontSize');
e.fontSizeNum = document.getElementById('fontSizeNum');
e.fontSizeValue = document.getElementById('fontSizeValue');
e.textColorInput = document.getElementById('textColor');
e.textColorValue = document.getElementById('textColorValue');
@@ -28,11 +29,13 @@ var UiBindings = {
// Rahmen
e.frameScaleInput = document.getElementById('frameScale');
e.frameScaleNum = document.getElementById('frameScaleNum');
e.frameScaleValue = document.getElementById('frameScaleValue');
e.frameScaleRow = document.getElementById('frameScaleRow');
// Padding
e.paddingAllInput = document.getElementById('paddingAll');
e.paddingAllNum = document.getElementById('paddingAllNum');
e.paddingAllValue = document.getElementById('paddingAllValue');
e.paddingTopInput = document.getElementById('paddingTop');
e.paddingTopValue = document.getElementById('paddingTopValue');
@@ -47,14 +50,19 @@ var UiBindings = {
// Pfeil
e.arrowLengthInput = document.getElementById('arrowLength');
e.arrowLengthNum = document.getElementById('arrowLengthNum');
e.arrowLengthValue = document.getElementById('arrowLengthValue');
e.arrowAngleInput = document.getElementById('arrowAngle');
e.arrowAngleNum = document.getElementById('arrowAngleNum');
e.arrowAngleValue = document.getElementById('arrowAngleValue');
e.arrowBendInput = document.getElementById('arrowBend');
e.arrowBendNum = document.getElementById('arrowBendNum');
e.arrowBendValue = document.getElementById('arrowBendValue');
e.arrowSizeInput = document.getElementById('arrowSize');
e.arrowSizeNum = document.getElementById('arrowSizeNum');
e.arrowSizeValue = document.getElementById('arrowSizeValue');
e.arrowTipLengthInput = document.getElementById('arrowTipLength');
e.arrowTipLengthNum = document.getElementById('arrowTipLengthNum');
e.arrowTipLengthValue = document.getElementById('arrowTipLengthValue');
e.arrowDetailsRow = document.getElementById('arrowDetailsRow');
e.arrowDetailsRow2 = document.getElementById('arrowDetailsRow2');
@@ -63,6 +71,11 @@ var UiBindings = {
e.textPreview = document.getElementById('textPreview');
e.standaloneArrowPreview = document.getElementById('standaloneArrowPreview');
// Text-Ausrichtung
e.textAlignRow = document.getElementById('textAlignRow');
e.alignXButtons = document.querySelectorAll('[data-align-x]');
e.alignYButtons = document.querySelectorAll('[data-align-y]');
// Buttons
e.shapeButtons = document.querySelectorAll('.shape-btn');
e.arrowButtons = document.querySelectorAll('.arrow-btn');
@@ -83,12 +96,23 @@ var UiBindings = {
});
}
// Schriftgroesse
// Schriftgroesse - Slider und Number-Input synchronisieren
if (e.fontSizeInput) {
e.fontSizeInput.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value);
state.set('fontSize', val);
if (e.fontSizeValue) e.fontSizeValue.textContent = val + 'px';
if (e.fontSizeNum) e.fontSizeNum.value = val;
self.updatePreview();
});
}
if (e.fontSizeNum) {
e.fontSizeNum.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value) || 8;
val = Math.max(8, Math.min(48, val));
state.set('fontSize', val);
if (e.fontSizeInput) e.fontSizeInput.value = val;
if (e.fontSizeValue) e.fontSizeValue.textContent = val + 'px';
self.updatePreview();
});
}
@@ -109,37 +133,55 @@ var UiBindings = {
});
}
// Rahmen-Skalierung
// Rahmen-Skalierung - Slider und Number-Input synchronisieren
if (e.frameScaleInput) {
e.frameScaleInput.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value);
state.set('frameScale', val);
if (e.frameScaleValue) e.frameScaleValue.textContent = val + '%';
if (e.frameScaleNum) e.frameScaleNum.value = val;
self.updatePreview();
});
}
if (e.frameScaleNum) {
e.frameScaleNum.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value) || 50;
val = Math.max(50, Math.min(200, val));
state.set('frameScale', val);
if (e.frameScaleInput) e.frameScaleInput.value = val;
if (e.frameScaleValue) e.frameScaleValue.textContent = val + '%';
self.updatePreview();
});
}
// Gesamt-Padding
// Gesamt-Padding - Slider und Number-Input synchronisieren
var syncAllPadding = function(val) {
state.setMultiple({
paddingTop: val, paddingRight: val, paddingBottom: val, paddingLeft: val
});
if (e.paddingAllValue) e.paddingAllValue.textContent = val + 'px';
if (e.paddingAllNum) e.paddingAllNum.value = val;
if (e.paddingAllInput) e.paddingAllInput.value = val;
if (e.paddingTopInput) e.paddingTopInput.value = val;
if (e.paddingTopValue) e.paddingTopValue.textContent = val + 'px';
if (e.paddingRightInput) e.paddingRightInput.value = val;
if (e.paddingRightValue) e.paddingRightValue.textContent = val + 'px';
if (e.paddingBottomInput) e.paddingBottomInput.value = val;
if (e.paddingBottomValue) e.paddingBottomValue.textContent = val + 'px';
if (e.paddingLeftInput) e.paddingLeftInput.value = val;
if (e.paddingLeftValue) e.paddingLeftValue.textContent = val + 'px';
self.updatePreview();
};
if (e.paddingAllInput) {
e.paddingAllInput.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value);
state.setMultiple({
paddingTop: val,
paddingRight: val,
paddingBottom: val,
paddingLeft: val
});
if (e.paddingAllValue) e.paddingAllValue.textContent = val + 'px';
// Einzelne Slider synchronisieren
if (e.paddingTopInput) e.paddingTopInput.value = val;
if (e.paddingTopValue) e.paddingTopValue.textContent = val + 'px';
if (e.paddingRightInput) e.paddingRightInput.value = val;
if (e.paddingRightValue) e.paddingRightValue.textContent = val + 'px';
if (e.paddingBottomInput) e.paddingBottomInput.value = val;
if (e.paddingBottomValue) e.paddingBottomValue.textContent = val + 'px';
if (e.paddingLeftInput) e.paddingLeftInput.value = val;
if (e.paddingLeftValue) e.paddingLeftValue.textContent = val + 'px';
self.updatePreview();
syncAllPadding(parseInt(ev.target.value));
});
}
if (e.paddingAllNum) {
e.paddingAllNum.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value) || 0;
val = Math.max(0, Math.min(50, val));
syncAllPadding(val);
});
}
@@ -198,12 +240,34 @@ var UiBindings = {
self.updatePreview();
});
});
// Align-X-Buttons (horizontal)
e.alignXButtons.forEach(function(btn) {
btn.addEventListener('click', function() {
e.alignXButtons.forEach(function(b) { b.classList.remove('active'); });
btn.classList.add('active');
state.set('textAlignX', btn.dataset.alignX);
self.updatePreview();
});
});
// Align-Y-Buttons (vertikal)
e.alignYButtons.forEach(function(btn) {
btn.addEventListener('click', function() {
e.alignYButtons.forEach(function(b) { b.classList.remove('active'); });
btn.classList.add('active');
state.set('textAlignY', btn.dataset.alignY);
self.updatePreview();
});
});
},
bindSlider: function(inputKey, valueKey, stateKey, unit) {
var self = this;
var input = this.elements[inputKey];
var valueEl = this.elements[valueKey];
var numKey = inputKey.replace('Input', 'Num');
var numInput = this.elements[numKey];
var state = window.TextGenState;
if (input) {
@@ -211,6 +275,22 @@ var UiBindings = {
var val = parseInt(ev.target.value);
state.set(stateKey, val);
if (valueEl) valueEl.textContent = val + unit;
if (numInput) numInput.value = val;
self.updatePreview();
if (stateKey.indexOf('arrow') === 0) {
self.updateStandaloneArrowPreview();
}
});
}
if (numInput) {
var min = input ? parseInt(input.min) : 0;
var max = input ? parseInt(input.max) : 100;
numInput.addEventListener('input', function(ev) {
var val = parseInt(ev.target.value) || min;
val = Math.max(min, Math.min(max, val));
state.set(stateKey, val);
if (input) input.value = val;
if (valueEl) valueEl.textContent = val + unit;
self.updatePreview();
if (stateKey.indexOf('arrow') === 0) {
self.updateStandaloneArrowPreview();
@@ -241,14 +321,17 @@ var UiBindings = {
if (e.textInput) e.textInput.value = s.text;
if (e.fontSizeInput) e.fontSizeInput.value = s.fontSize;
if (e.fontSizeNum) e.fontSizeNum.value = s.fontSize;
if (e.fontSizeValue) e.fontSizeValue.textContent = s.fontSize + 'px';
if (e.textColorInput) e.textColorInput.value = s.textColor;
if (e.frameColorInput) e.frameColorInput.value = s.frameColor;
if (e.frameScaleInput) e.frameScaleInput.value = s.frameScale;
if (e.frameScaleNum) e.frameScaleNum.value = s.frameScale;
if (e.frameScaleValue) e.frameScaleValue.textContent = s.frameScale + '%';
// Padding
if (e.paddingAllInput) e.paddingAllInput.value = s.paddingTop;
if (e.paddingAllNum) e.paddingAllNum.value = s.paddingTop;
if (e.paddingAllValue) e.paddingAllValue.textContent = s.paddingTop + 'px';
if (e.paddingTopInput) e.paddingTopInput.value = s.paddingTop;
if (e.paddingTopValue) e.paddingTopValue.textContent = s.paddingTop + 'px';
@@ -261,14 +344,19 @@ var UiBindings = {
// Pfeil
if (e.arrowLengthInput) e.arrowLengthInput.value = s.arrowLength;
if (e.arrowLengthNum) e.arrowLengthNum.value = s.arrowLength;
if (e.arrowLengthValue) e.arrowLengthValue.textContent = s.arrowLength + 'px';
if (e.arrowAngleInput) e.arrowAngleInput.value = s.arrowAngle;
if (e.arrowAngleNum) e.arrowAngleNum.value = s.arrowAngle;
if (e.arrowAngleValue) e.arrowAngleValue.textContent = s.arrowAngle + '\u00B0';
if (e.arrowBendInput) e.arrowBendInput.value = s.arrowBend;
if (e.arrowBendNum) e.arrowBendNum.value = s.arrowBend;
if (e.arrowBendValue) e.arrowBendValue.textContent = s.arrowBend + '%';
if (e.arrowSizeInput) e.arrowSizeInput.value = s.arrowSize;
if (e.arrowSizeNum) e.arrowSizeNum.value = s.arrowSize;
if (e.arrowSizeValue) e.arrowSizeValue.textContent = s.arrowSize + 'px';
if (e.arrowTipLengthInput) e.arrowTipLengthInput.value = s.arrowTipLength;
if (e.arrowTipLengthNum) e.arrowTipLengthNum.value = s.arrowTipLength;
if (e.arrowTipLengthValue) e.arrowTipLengthValue.textContent = s.arrowTipLength + 'px';
// Buttons aktivieren
@@ -276,6 +364,8 @@ var UiBindings = {
this.activateButton(e.arrowButtons, 'arrow', s.arrow);
this.activateButton(e.lineStyleButtons, 'style', s.lineStyle);
this.activateButtonByValue(e.lineWeightButtons, 'weight', s.lineWeight);
this.activateButton(e.alignXButtons, 'alignX', s.textAlignX);
this.activateButton(e.alignYButtons, 'alignY', s.textAlignY);
},
activateButton: function(buttons, dataAttr, value) {
@@ -305,6 +395,7 @@ var UiBindings = {
if (e.frameScaleRow) e.frameScaleRow.style.display = hasShape ? 'flex' : 'none';
if (e.paddingAllRow) e.paddingAllRow.style.display = hasShape ? 'flex' : 'none';
if (e.paddingRow) e.paddingRow.style.display = hasShape ? 'flex' : 'none';
if (e.textAlignRow) e.textAlignRow.style.display = hasShape ? 'flex' : 'none';
if (e.arrowDetailsRow) e.arrowDetailsRow.style.display = hasArrow ? 'flex' : 'none';
if (e.arrowDetailsRow2) e.arrowDetailsRow2.style.display = hasArrow ? 'flex' : 'none';
},