feat(logs): Kompass-Dial für Kurs- und Windeingabe

Ersetzt Textfelder für MgK, rwK und Wind durch einen mobilen Kompass-Ring, normalisiert Kurswinkel beim Speichern und führt Vitest mit Regressionstests für html lang ein.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-31 11:08:36 +02:00
parent 34c7d2d65c
commit 9e03fcda0a
13 changed files with 1330 additions and 37 deletions
+170
View File
@@ -154,6 +154,176 @@ select.input-text {
user-select: none;
}
.course-dial-section {
grid-column: 1 / -1;
}
.course-dial-tabs {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.course-dial-tab {
flex: 1;
padding: 8px 12px;
border-radius: 8px;
border: 1px solid var(--app-border-subtle);
background: var(--app-btn-secondary-bg);
color: var(--app-text-muted);
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.course-dial-tab.is-active {
background: var(--app-accent-bg);
border-color: var(--app-accent-border);
color: var(--app-accent-light);
}
.course-dial-tab:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.course-dial {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 100%;
max-width: 260px;
margin: 0 auto;
}
.course-dial--sm {
max-width: 220px;
}
.course-dial--disabled {
opacity: 0.65;
pointer-events: none;
}
.course-dial__step-toolbar {
display: flex;
gap: 6px;
width: 100%;
}
.course-dial__step-btn {
flex: 1;
padding: 6px 8px;
border-radius: 6px;
border: 1px solid var(--app-border-subtle);
background: var(--app-surface-alt);
color: var(--app-text-muted);
font-size: 12px;
font-weight: 600;
cursor: pointer;
}
.course-dial__step-btn.is-active {
border-color: var(--app-accent-border);
background: var(--app-accent-bg);
color: var(--app-accent-light);
}
.course-dial__ring-wrap {
width: 100%;
touch-action: none;
}
.course-dial__svg {
width: 100%;
height: auto;
display: block;
cursor: pointer;
user-select: none;
}
.course-dial__track {
fill: none;
stroke: var(--app-border);
stroke-width: 2;
}
.course-dial__tick {
stroke: var(--app-text-subtle);
stroke-width: 1.5;
}
.course-dial__label {
fill: var(--app-text-muted);
font-size: 9px;
font-weight: 600;
font-variant-numeric: tabular-nums;
}
.course-dial__needle line {
stroke: var(--app-accent-light);
stroke-width: 3;
stroke-linecap: round;
}
.course-dial__needle circle {
fill: var(--app-accent-light);
}
.course-dial__center {
fill: var(--app-text);
font-size: 15px;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.course-dial__hint {
margin: 0;
font-size: 12px;
color: var(--app-text-muted);
text-align: center;
line-height: 1.4;
}
.course-dial__input {
width: 100%;
text-align: center;
font-variant-numeric: tabular-nums;
}
.course-dial__mode-toggle {
border: none;
background: none;
color: var(--app-accent-light);
font-size: 13px;
font-weight: 500;
cursor: pointer;
text-decoration: underline;
padding: 4px;
}
@media (prefers-reduced-motion: reduce) {
.course-dial__needle {
transition: none;
}
}
@media (max-width: 640px) {
.course-dial {
max-width: min(72vw, 220px);
}
.course-dial--sm {
max-width: min(68vw, 200px);
}
.course-dial__label {
font-size: 8px;
}
}
.themed-select {
position: relative;
width: 100%;