General styles fixes

This commit is contained in:
2026-02-09 20:31:34 +01:00
parent c5e3dd7681
commit d6a85aef2c
3 changed files with 84 additions and 43 deletions

9
package-lock.json generated
View File

@@ -55,7 +55,6 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -1430,7 +1429,6 @@
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1472,7 +1470,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -1578,7 +1575,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -1800,7 +1796,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -2487,7 +2482,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -2565,7 +2559,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2799,7 +2792,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -2921,7 +2913,6 @@
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@@ -98,6 +98,7 @@ body {
rgba(181, 234, 215, 0.3) 0%,
rgba(255, 255, 255, 0.9) 100%
);
padding: 2.5rem 2rem;
}
.preview-card {
@@ -171,19 +172,20 @@ body {
.settings-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
gap: 2rem;
}
@media (min-width: 768px) {
.settings-grid {
grid-template-columns: 1fr 1fr;
gap: 2.5rem;
}
}
.setting-item {
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: 0.75rem;
}
.setting-label {
@@ -192,7 +194,8 @@ body {
font-size: 0.95rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
gap: 0.35rem;
margin-bottom: 0.25rem;
}
.setting-hint {
@@ -203,7 +206,7 @@ body {
}
.input-field {
padding: 0.875rem 1rem;
padding: 0.875rem 1.125rem;
border: 2px solid var(--soft-gray);
border-radius: 12px;
font-size: 1rem;
@@ -211,6 +214,7 @@ body {
background: white;
color: var(--text-primary);
transition: all 0.2s ease;
margin-top: 0.25rem;
}
.input-field:focus {
@@ -222,13 +226,14 @@ body {
.slider-container {
display: flex;
align-items: center;
gap: 1rem;
gap: 1.25rem;
padding-top: 0.25rem;
}
.slider {
flex: 1;
height: 8px;
border-radius: 8px;
height: 10px;
border-radius: 10px;
background: linear-gradient(
90deg,
var(--pastel-pink) 0%,
@@ -241,46 +246,51 @@ body {
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
width: 28px;
height: 28px;
border-radius: 50%;
background: white;
border: 3px solid var(--soft-purple);
cursor: pointer;
box-shadow: 0 2px 8px rgba(92, 74, 114, 0.2);
box-shadow: 0 3px 10px rgba(92, 74, 114, 0.25);
transition: all 0.2s ease;
}
.slider::-webkit-slider-thumb:hover {
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(92, 74, 114, 0.3);
transform: scale(1.15);
box-shadow: 0 4px 14px rgba(92, 74, 114, 0.35);
}
.slider::-moz-range-thumb {
width: 24px;
height: 24px;
width: 28px;
height: 28px;
border-radius: 50%;
background: white;
border: 3px solid var(--soft-purple);
cursor: pointer;
box-shadow: 0 2px 8px rgba(92, 74, 114, 0.2);
box-shadow: 0 3px 10px rgba(92, 74, 114, 0.25);
transition: all 0.2s ease;
}
.slider::-moz-range-thumb:hover {
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(92, 74, 114, 0.3);
transform: scale(1.15);
box-shadow: 0 4px 14px rgba(92, 74, 114, 0.35);
}
.slider-value {
min-width: 40px;
padding: 0.5rem 1rem;
min-width: 48px;
padding: 0.625rem 1.125rem;
background: var(--soft-purple);
color: white;
border-radius: 12px;
font-weight: 700;
text-align: center;
font-size: 1rem;
font-size: 1.05rem;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(92, 74, 114, 0.2);
}
.textarea {
@@ -373,11 +383,21 @@ body {
.preview-container {
display: flex;
justify-content: center;
align-items: flex-start;
justify-content: flex-start;
padding: 2rem;
background: var(--soft-gray);
border-radius: 16px;
overflow-x: auto;
overflow-y: visible;
max-width: 100%;
min-height: min-content;
}
.preview-inner {
flex: 0 0 auto;
display: flex;
align-items: flex-start;
}
.preview-container svg {
@@ -385,6 +405,7 @@ body {
box-shadow:
0 8px 32px rgba(92, 74, 114, 0.12),
0 2px 8px rgba(92, 74, 114, 0.06);
flex-shrink: 0;
}
/* Mobile responsive */

View File

@@ -1,4 +1,5 @@
import React, { useState, useRef } from "react";
import "../App.css";
const DEMO_TABS = `
4 6 -5 5 -4 4 3
@@ -16,16 +17,16 @@ const HarmonicaTabGenerator = () => {
const parseInput = (text) => {
const lines = text.split("\n");
return lines.map((line) => {
// Check if line contains tab notation (numbers with optional minus signs, quotes for bends, and spaces)
// Check if line contains tab notation (numbers with optional minus signs, quotes/backticks for bends, and spaces)
const isTabLine =
/^[\s\d-'"]+$/.test(line.trim()) && line.trim().length > 0;
/^[\s\d\-'"`'']+$/.test(line.trim()) && line.trim().length > 0;
let processedContent = line;
// If it's a tab line, add + signs to positive numbers (but preserve bend notation)
if (isTabLine) {
processedContent = line.replace(
/(\s|^)(\d+)(['"]?)/g,
/(\s|^)(\d+)(['"`'']?)/g,
(match, space, number, bend) => {
return space + "+" + number + bend;
},
@@ -73,15 +74,32 @@ const HarmonicaTabGenerator = () => {
const parsedLines = parseInput(input);
const columns = calculateLayout(parsedLines);
// Calculate the width of each column based on content
const calculateColumnWidth = (column) => {
let maxLength = 0;
column.forEach((line) => {
if (line.content.trim()) {
// Adjusted for letterSpacing of 0.5 instead of 2
// Monospace at 28px is about 16.8px per char + 0.5px spacing
const charWidth = line.isTab ? 17.3 : 7;
const length = line.content.length * charWidth;
maxLength = Math.max(maxLength, length);
}
});
return Math.max(maxLength, 200); // Minimum 200px per column
};
const columnWidths = columns.map(calculateColumnWidth);
const totalContentWidth = columnWidths.reduce((sum, width) => sum + width, 0);
// SVG dimensions and styling
const lineHeight = 40;
const fontSize = 28;
const annotationFontSize = 12;
const titleFontSize = 36;
const titleHeight = 80;
const columnWidth = 400;
const padding = 40;
const columnGap = 60;
const columnGap = 80;
const maxLinesInAnyColumn = Math.max(
...columns.map((col) => {
@@ -93,9 +111,7 @@ const HarmonicaTabGenerator = () => {
);
const svgWidth =
columns.length * columnWidth +
(columns.length - 1) * columnGap +
padding * 2;
totalContentWidth + (columns.length - 1) * columnGap + padding * 2;
const svgHeight =
maxLinesInAnyColumn * lineHeight + padding * 2 + titleHeight;
@@ -166,7 +182,7 @@ const HarmonicaTabGenerator = () => {
<span className="instruction-number">2</span>
<div className="instruction-content">
<strong>Auto-formatting</strong> - Positive numbers get a + sign
automatically. Use ' or " for bends (e.g., -3', 4")
automatically. Bends: ' " ` ' (e.g., -3', 4")
</div>
</li>
<li className="instruction-item">
@@ -247,7 +263,8 @@ const HarmonicaTabGenerator = () => {
Example:
4 6 -5 5 -4 4 3
-3' 6 7 -6' 6 5
Use ' or &quot; for bends"
Add quotes after numbers for bends"
rows={12}
/>
<div className="button-group">
@@ -261,7 +278,7 @@ Use ' or &quot; for bends"
<p className="tip">
💡 <strong>Pro tip:</strong> Mix tab lines with text for
annotations. Tab lines contain only numbers, spaces, and bend
markers (' or ").
markers (' " ` ').
</p>
</div>
@@ -298,7 +315,11 @@ Use ' or &quot; for bends"
{/* Render columns */}
{columns.map((column, colIndex) => {
let yOffset = padding + titleHeight;
const xOffset = padding + colIndex * (columnWidth + columnGap);
// Calculate xOffset based on cumulative widths of previous columns
let xOffset = padding;
for (let i = 0; i < colIndex; i++) {
xOffset += columnWidths[i] + columnGap;
}
return (
<g key={colIndex}>
@@ -317,7 +338,7 @@ Use ' or &quot; for bends"
fontSize={fontSize}
fontWeight="600"
fill="#3d5a6c"
letterSpacing="2"
letterSpacing="0.5"
>
{line.content}
</text>
@@ -346,6 +367,14 @@ Use ' or &quot; for bends"
})}
</svg>
</div>
<div className="button-group">
<button onClick={exportAsPNG} className="button button-primary">
📥 Download PNG
</button>
<button onClick={exportAsSVG} className="button button-secondary">
📥 Download SVG
</button>
</div>
</div>
</div>
</div>