Layout, Tool-Tipps und Feinheiten bei der Eingabe und der Trainingszeit-Darstellung verbessert
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
4cbc98f0a9
commit
10031abb05
13 changed files with 127 additions and 20 deletions
13
HILFE.md
13
HILFE.md
|
|
@ -2,7 +2,16 @@
|
|||
|
||||
Willkommen beim Typewriter Trainer! Diese Hilfe erklärt alle Funktionen und gibt Tipps für effektives Tipptraining.
|
||||
|
||||

|
||||

|
||||
|
||||
<!--
|
||||
<div class="mt-4 text-center">
|
||||
<img src="static/images/Quertz_10_Finger_Layout.jpg"
|
||||
alt="Deutsches Tastatur-Layout"
|
||||
style="max-width: 600px;">
|
||||
<p class="mt-2 text-muted">Standard-Deutsches Tastaturlayout (QWERTZ)</p>
|
||||
</div>
|
||||
-->
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -422,6 +431,8 @@ Der Text scrollt **automatisch**, sodass:
|
|||
- 🚀 **Produktivität**: Bis zu 3x schneller tippen
|
||||
- 🧠 **Weniger Ermüdung**: Ergonomischer für Ihre Hände
|
||||
|
||||
---
|
||||
|
||||
**Viel Erfolg beim Training!** 💪
|
||||
|
||||
---
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# 🎹 Typewriter Tutor - Adaptiver Tipptrainer
|
||||
# Typewriter Tutor - Adaptiver Tipptrainer
|
||||
|
||||
**Version:** v0.3.0 _(siehe [VERSION](VERSION) Datei)_
|
||||
**Version:** v0.4.0 _(siehe [VERSION](VERSION) Datei)_
|
||||
|
||||
Ein moderner, webbasierter Tipptrainer mit **adaptivem Metronom** und umfangreichen Statistiken. Das Programm passt sich automatisch Ihrer Tippgeschwindigkeit an und hilft Ihnen, das Zehnfingerschreiben systematisch zu erlernen.
|
||||
|
||||
|
|
@ -369,7 +369,12 @@ Contributions sind willkommen! Bitte:
|
|||
|
||||
> **Hinweis:** Die aktuelle Versionsnummer wird zentral in der [VERSION](VERSION) Datei verwaltet.
|
||||
|
||||
### Version 0.3.0 (Aktuell)
|
||||
### Version 0.4.0 (Aktuell)
|
||||
- ✅ **Landing Page**: Begrüßungsseite mit Übersicht über Funktionen und direktem Zugang zum Training
|
||||
- ✅ **Theme-Integration**: Dark/Light Mode Toggle auch auf der Landing Page
|
||||
- ✅ **Verbesserte Navigation**: Zentrale Einstiegsseite für neue Nutzer
|
||||
|
||||
### Version 0.3.0
|
||||
- ✅ **Maximale Metronom-Geschwindigkeit**: Konfigurierbar (Standard: 100 BPM) zur Vermeidung von Überforderung
|
||||
- ✅ **Präzisions-Anzeige**: Positive Darstellung (Präzision statt Fehlerrate) für motivierendes Feedback
|
||||
- ✅ **Persistente Settings**: Einstellungen werden in Datenbank gespeichert (nicht nur Session)
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.4.0
|
||||
v0.4.5
|
||||
|
|
|
|||
|
|
@ -120,6 +120,22 @@ body {
|
|||
color: #a0a0a0; /* Hellgrau für Sonderzeichen */
|
||||
}
|
||||
|
||||
/* Korrekt eingegebene Whitespace-Zeichen in dezent grau-grün (Dark Mode) */
|
||||
[data-bs-theme="dark"] .highlight-correct.highlight-space,
|
||||
[data-bs-theme="dark"] .highlight-correct.highlight-tab,
|
||||
[data-bs-theme="dark"] .highlight-correct.highlight-newline {
|
||||
color: #7fc49a;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Falsch eingegebene Whitespace-Zeichen in rot (Dark Mode) */
|
||||
[data-bs-theme="dark"] .highlight-incorrect.highlight-space,
|
||||
[data-bs-theme="dark"] .highlight-incorrect.highlight-tab,
|
||||
[data-bs-theme="dark"] .highlight-incorrect.highlight-newline {
|
||||
color: #f87171;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] .form-control {
|
||||
background-color: #2d2d2d;
|
||||
color: #e0e0e0;
|
||||
|
|
@ -309,13 +325,29 @@ body {
|
|||
max-width: 900px;
|
||||
}
|
||||
|
||||
.highlight-space,
|
||||
.highlight-tab,
|
||||
.highlight-space,
|
||||
.highlight-tab,
|
||||
.highlight-newline {
|
||||
color: #888;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Korrekt eingegebene Whitespace-Zeichen in dezent grau-grün */
|
||||
.highlight-correct.highlight-space,
|
||||
.highlight-correct.highlight-tab,
|
||||
.highlight-correct.highlight-newline {
|
||||
color: #6ca87f;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Falsch eingegebene Whitespace-Zeichen in rot */
|
||||
.highlight-incorrect.highlight-space,
|
||||
.highlight-incorrect.highlight-tab,
|
||||
.highlight-incorrect.highlight-newline {
|
||||
color: #dc3545;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
BIN
static/images/typing_hands.jpg
Normal file
BIN
static/images/typing_hands.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 202 KiB |
|
|
@ -33,6 +33,9 @@ class TypewriterApp {
|
|||
this.metronomeMode = 'automatic';
|
||||
this.targetErrorRate = 5;
|
||||
this.speedDisplay = 'zpm';
|
||||
|
||||
// Time display update interval
|
||||
this.timeDisplayInterval = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,6 +73,9 @@ class TypewriterApp {
|
|||
this.metronomePlayer.start();
|
||||
}
|
||||
|
||||
// Start time display update interval (updates every second)
|
||||
this.startTimeDisplayUpdate();
|
||||
|
||||
console.log('Typewriter Tutor initialized successfully');
|
||||
}
|
||||
|
||||
|
|
@ -315,6 +321,35 @@ class TypewriterApp {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the time display update interval
|
||||
* Updates every second to show running time
|
||||
*/
|
||||
startTimeDisplayUpdate() {
|
||||
// Clear any existing interval
|
||||
if (this.timeDisplayInterval) {
|
||||
clearInterval(this.timeDisplayInterval);
|
||||
}
|
||||
|
||||
// Update every second
|
||||
this.timeDisplayInterval = setInterval(() => {
|
||||
// Only update if not paused
|
||||
if (!this.sessionManager.isPaused) {
|
||||
this.updateTrainingTimeDisplay();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the time display update interval
|
||||
*/
|
||||
stopTimeDisplayUpdate() {
|
||||
if (this.timeDisplayInterval) {
|
||||
clearInterval(this.timeDisplayInterval);
|
||||
this.timeDisplayInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update text display
|
||||
* @param {boolean} isKeystroke - Whether this is triggered by a keystroke
|
||||
|
|
@ -490,6 +525,9 @@ class TypewriterApp {
|
|||
* End session
|
||||
*/
|
||||
async endSession() {
|
||||
// Stop time display update
|
||||
this.stopTimeDisplayUpdate();
|
||||
|
||||
try {
|
||||
const settings = {
|
||||
metronome_enabled: this.metronomePlayer.enabled,
|
||||
|
|
|
|||
|
|
@ -81,11 +81,26 @@ export class TextDisplayManager {
|
|||
const escapedChar = this.escapeHtml(char);
|
||||
|
||||
if (char === ' ') {
|
||||
return `<span class="${className} highlight-space">·</span>`;
|
||||
if (type === 'incorrect' && incorrectChar !== null) {
|
||||
const escapedIncorrectChar = this.escapeHtml(incorrectChar);
|
||||
return `<span class="${className} highlight-space incorrect-char-container">·<span class="incorrect-char-overlay">${escapedIncorrectChar}</span></span>`;
|
||||
} else {
|
||||
return `<span class="${className} highlight-space">·</span>`;
|
||||
}
|
||||
} else if (char === '\t') {
|
||||
return `<span class="${className} highlight-tab">→</span>`;
|
||||
if (type === 'incorrect' && incorrectChar !== null) {
|
||||
const escapedIncorrectChar = this.escapeHtml(incorrectChar);
|
||||
return `<span class="${className} highlight-tab incorrect-char-container">→<span class="incorrect-char-overlay">${escapedIncorrectChar}</span></span>`;
|
||||
} else {
|
||||
return `<span class="${className} highlight-tab">→</span>`;
|
||||
}
|
||||
} else if (char === '\n') {
|
||||
return `<span class="${className} highlight-newline">↵</span><br>`;
|
||||
if (type === 'incorrect' && incorrectChar !== null) {
|
||||
const escapedIncorrectChar = this.escapeHtml(incorrectChar);
|
||||
return `<span class="${className} highlight-newline incorrect-char-container">↵<span class="incorrect-char-overlay">${escapedIncorrectChar}</span></span><br>`;
|
||||
} else {
|
||||
return `<span class="${className} highlight-newline">↵</span><br>`;
|
||||
}
|
||||
} else {
|
||||
if (type === 'incorrect' && incorrectChar !== null) {
|
||||
const escapedIncorrectChar = this.escapeHtml(incorrectChar);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Hilfe - Typewriter Trainer</title>
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}">
|
||||
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}" sizes="any">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Anleitung</h2>
|
||||
<div id="theme-toggle" style="cursor: pointer; position: relative;">
|
||||
<div id="theme-toggle" style="cursor: pointer; position: relative;" title="Theme wechseln">
|
||||
<i data-lucide="sun" id="theme-toggle-sun" style="width: 24px; height: 24px;"></i>
|
||||
<i data-lucide="moon" id="theme-toggle-moon" style="width: 24px; height: 24px; display: none;"></i>
|
||||
</div>
|
||||
|
|
@ -112,6 +112,7 @@
|
|||
{{ hilfe_html|safe }}
|
||||
|
||||
<!-- Deutsche Tastatur Grafik -->
|
||||
<!--
|
||||
<div class="mt-4 text-center">
|
||||
<h3>Deutsches Tastatur-Layout</h3>
|
||||
<img src="{{ url_for('static', filename='images/Quertz_10_Finger_Layout.jpg') }}"
|
||||
|
|
@ -119,7 +120,8 @@
|
|||
class="img-fluid mt-2"
|
||||
style="max-width: 600px;">
|
||||
<p class="mt-2 text-muted">Standard-Deutsches Tastaturlayout (QWERTZ)</p>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,25 +23,27 @@
|
|||
<button id="settings-toggle"
|
||||
style="width: 24px; height: 24px; cursor: pointer; border: none; background: none; padding: 0; color: white;"
|
||||
aria-label="Einstellungen öffnen"
|
||||
title="Einstellungen"
|
||||
type="button">
|
||||
<i data-lucide="settings" style="width: 24px; height: 24px;" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button id="theme-toggle"
|
||||
style="width: 24px; height: 24px; cursor: pointer; position: relative; border: none; background: none; padding: 0; color: white;"
|
||||
aria-label="Zwischen hellem und dunklem Design wechseln"
|
||||
title="Theme wechseln"
|
||||
type="button">
|
||||
<i data-lucide="sun" id="theme-toggle-sun" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;" aria-hidden="true"></i>
|
||||
<i data-lucide="moon" id="theme-toggle-moon" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div id="lock-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;">
|
||||
<div id="lock-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;" title="Fehler blockiert an/aus">
|
||||
<i data-lucide="lock" id="lock-closed" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;"></i>
|
||||
<i data-lucide="unlock" id="lock-open" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;"></i>
|
||||
</div>
|
||||
<div id="volume-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;">
|
||||
<div id="volume-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;" title="Metronom-Ton an/aus">
|
||||
<i data-lucide="volume-2" id="volume-on" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;"></i>
|
||||
<i data-lucide="volume-x" id="volume-off" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;"></i>
|
||||
</div>
|
||||
<div id="play-pause-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;">
|
||||
<div id="play-pause-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;" title="Eingabe pausieren/fortsetzen">
|
||||
<i data-lucide="pause" id="play-pause-pause" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;"></i>
|
||||
<i data-lucide="play" id="play-pause-play" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;"></i>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Einstellungen</h2>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div id="theme-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;">
|
||||
<div id="theme-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;" title="Theme wechseln">
|
||||
<i data-lucide="sun" id="theme-toggle-sun" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;"></i>
|
||||
<i data-lucide="moon" id="theme-toggle-moon" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;"></i>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@
|
|||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h2 class="mb-0">Lektionsstatistik</h2>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div id="theme-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;">
|
||||
<div id="theme-toggle" style="width: 24px; height: 24px; cursor: pointer; position: relative;" title="Theme wechseln">
|
||||
<i data-lucide="sun" id="theme-toggle-sun" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0;"></i>
|
||||
<i data-lucide="moon" id="theme-toggle-moon" style="width: 24px; height: 24px; position: absolute; top: 0; left: 0; display: none;"></i>
|
||||
</div>
|
||||
<div onclick="window.location.href='/train'" style="width: 24px; height: 24px; cursor: pointer; display: flex; align-items: center; justify-content: center;" aria-label="Zurück zum Training">
|
||||
<div onclick="window.location.href='/train'" style="width: 24px; height: 24px; cursor: pointer; display: flex; align-items: center; justify-content: center;" aria-label="Zurück zum Training" title="Zurück zum Training">
|
||||
<i data-lucide="undo-2" style="width: 24px; height: 24px;"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -79,7 +79,9 @@
|
|||
|
||||
<!-- Logo -->
|
||||
<div class="logo-icon">
|
||||
🎹
|
||||
<img src="{{ url_for('static', filename='images/typing_hands.jpg') }}"
|
||||
alt="Typing Hands"
|
||||
style="max-width: 200px; height: auto; border-radius: 8px;">
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue