Files
homeschool/tablet-test.html
T
nmemmert 8d37038177 feat: Complete tablet UI overhaul with popup menu system and help bubble
Major Changes:
- 🪟 Popup Menu System: Converted slide-down navigation to draggable, minimizable popup window
- ❓ Help Bubble: Added contextual help system for admin dashboard (accessible via ? bubble)
- 🎨 Universal Theme Engine: All elements now properly match selected theme across all panels
- 📱 Enhanced Tablet Support: Comprehensive touch gestures, handwriting recognition, and Apple Pencil optimization
- 🖊️ Unified Write Mode: Combined handwriting and keyboard input with large canvas interface

Technical Implementation:
- Draggable popup with minimize/restore functionality and boundary constraints
- Dynamic theme switching using CSS variables throughout all components
- Help system with modal overlay and keyboard shortcuts (ESC to close)
- Responsive design optimizations for mobile, tablet, and desktop
- Comprehensive CSS overrides to eliminate hardcoded colors
- JavaScript fixes for canvas stroke colors and input styling

UI/UX Improvements:
- Clean admin dashboard without instruction clutter
- Professional popup interface with smooth animations
- Better contrast and readability across all themes (default, playground, chalkboard, galaxy, nature)
- Improved accessibility with proper focus management
- Enhanced mobile responsiveness with touch-optimized controls

Files Modified:
- Menu system: tablet-menu.js, tablet-menu.css
- Theme engine: styles.css, themes integration
- Admin interface: index.html, ui.js
- Input systems: game.js, unified-write.js, handwriting.js
- Authentication: auth.js with tablet menu cleanup
2025-10-02 14:26:04 -04:00

422 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Tablet & Handwriting Test</title>
<style>
:root {
--primary: #3b82f6;
--primary-dark: #2563eb;
--secondary: #f1f5f9;
--success: #10b981;
--error: #ef4444;
--text-primary: #1e293b;
--text-secondary: #64748b;
--border: #e2e8f0;
--bg-primary: #ffffff;
--bg-secondary: #f8fafc;
--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg-secondary);
margin: 0;
padding: 2rem;
touch-action: manipulation;
}
.container {
max-width: 800px;
margin: 0 auto;
background: var(--bg-primary);
border-radius: 20px;
padding: 2rem;
box-shadow: var(--shadow-lg);
}
h1 {
text-align: center;
color: var(--primary);
font-size: 2.5rem;
margin-bottom: 2rem;
}
.demo-section {
margin: 2rem 0;
padding: 1.5rem;
border: 2px solid var(--border);
border-radius: 12px;
}
.demo-section h2 {
color: var(--text-primary);
font-size: 1.5rem;
margin-bottom: 1rem;
}
/* Button styles */
button {
background: var(--primary);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 12px;
font-size: 1.125rem;
font-weight: 600;
cursor: pointer;
min-height: 50px;
min-width: 120px;
margin: 0.5rem;
transition: all 0.2s ease;
touch-action: manipulation;
}
button:hover, button:active {
background: var(--primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
/* Input mode toggle */
.input-mode-toggle {
display: flex;
background: var(--secondary);
border-radius: 12px;
padding: 0.5rem;
margin: 1.5rem auto;
max-width: 350px;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
}
.input-mode-toggle button {
flex: 1;
padding: 1rem 1.5rem;
background: transparent;
color: var(--text-secondary);
font-size: 1.125rem;
min-height: 50px;
margin: 0;
box-shadow: none;
}
.input-mode-toggle button.active {
background: var(--bg-primary);
color: var(--primary);
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
/* Text input */
input[type="text"] {
width: 100%;
max-width: 500px;
font-size: 1.5rem;
text-align: center;
background: white;
color: var(--text-primary);
font-weight: 500;
letter-spacing: 0.5px;
min-height: 60px;
padding: 1.25rem;
border: 3px solid var(--primary);
border-radius: 12px;
margin: 1.5rem auto;
display: block;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
}
/* Canvas styles */
#handwritingCanvas {
border: 3px solid var(--primary);
border-radius: 12px;
background: white;
cursor: crosshair;
display: block;
margin: 1rem auto;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
touch-action: none;
max-width: 100%;
}
.canvas-controls {
display: flex;
justify-content: center;
gap: 1rem;
margin: 1rem 0;
flex-wrap: wrap;
}
.canvas-controls button {
padding: 0.75rem 1.5rem;
font-size: 1rem;
min-width: 100px;
}
.clear-btn {
background: var(--error) !important;
}
.recognize-btn {
background: var(--success) !important;
}
.recognition-status {
margin: 1rem auto;
padding: 1rem;
border-radius: 8px;
text-align: center;
max-width: 400px;
}
.recognition-status.success {
background: #f0fdf4;
color: var(--success);
border: 2px solid var(--success);
}
.recognition-status.error {
background: #fef2f2;
color: var(--error);
border: 2px solid var(--error);
}
.recognized-text {
background: var(--secondary);
border-radius: 8px;
padding: 1rem;
margin: 1rem auto;
font-size: 1.25rem;
font-weight: 600;
text-align: center;
max-width: 400px;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid var(--border);
}
.recognized-text.has-content {
background: var(--bg-primary);
border-color: var(--primary);
color: var(--primary);
}
.handwriting-container {
background: var(--bg-primary);
border-radius: 16px;
padding: 2rem;
margin: 2rem auto;
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
border: 2px solid var(--border);
max-width: 700px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h1>📱 Tablet & Handwriting Test</h1>
<div class="demo-section">
<h2>🎮 Spelling Game Interface</h2>
<!-- Word Display -->
<div style="font-size: 3rem; font-weight: 700; text-align: center; padding: 2rem; margin: 2rem auto; max-width: 600px; background: linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%); border: 3px solid var(--primary); border-radius: 20px; color: var(--primary); box-shadow: var(--shadow-lg); min-height: 120px; display: flex; align-items: center; justify-content: center; letter-spacing: 2px;">
EXAMPLE
</div>
<!-- Input Mode Toggle -->
<div class="input-mode-toggle">
<button class="active" data-mode="typing" onclick="toggleMode('typing')">
⌨️ Type
</button>
<button data-mode="handwriting" onclick="toggleMode('handwriting')">
✏️ Write
</button>
</div>
<!-- Typing Input -->
<input type="text" id="typingInput" placeholder="Type the word you saw..." style="display: block;">
<!-- Handwriting Container -->
<div id="handwritingContainer" class="handwriting-container" style="display: none;">
<h3>✏️ Write the word you saw:</h3>
<!-- Canvas -->
<canvas id="handwritingCanvas" width="600" height="250" style="width: 100%; max-width: 600px; height: 250px;"></canvas>
<!-- Controls -->
<div class="canvas-controls">
<button onclick="clearCanvas()" class="clear-btn">🗑️ Clear</button>
<button onclick="recognizeText()" class="recognize-btn">🔍 Recognize</button>
</div>
<!-- Status -->
<div id="recognitionStatus" class="recognition-status" style="display: none;"></div>
<!-- Recognized Text -->
<div id="recognizedText" class="recognized-text">Draw to see recognized text here</div>
</div>
<button onclick="submitAnswer()" style="background: var(--success); font-size: 1.25rem; padding: 1.25rem 2.5rem; margin-top: 1rem;">Submit Answer</button>
</div>
<div class="demo-section">
<h2>📊 Test Results</h2>
<div id="testResults" style="padding: 1rem; background: var(--secondary); border-radius: 8px; font-family: monospace;">
Ready for testing...
</div>
</div>
</div>
<script>
// Simple handwriting canvas implementation
let canvas, ctx, isDrawing = false, strokes = [];
document.addEventListener('DOMContentLoaded', () => {
canvas = document.getElementById('handwritingCanvas');
ctx = canvas.getContext('2d');
// Set canvas properties
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.strokeStyle = '#1e293b';
ctx.lineWidth = 3;
// Mouse events
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
// Touch events
canvas.addEventListener('touchstart', handleTouch);
canvas.addEventListener('touchmove', handleTouch);
canvas.addEventListener('touchend', stopDrawing);
});
function handleTouch(e) {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const touch = e.touches[0] || e.changedTouches[0];
const mouseEvent = new MouseEvent(
e.type === 'touchstart' ? 'mousedown' :
e.type === 'touchmove' ? 'mousemove' : 'mouseup',
{
clientX: touch.clientX,
clientY: touch.clientY
}
);
if (e.type === 'touchstart') startDrawing(mouseEvent);
else if (e.type === 'touchmove') draw(mouseEvent);
else stopDrawing();
}
function startDrawing(e) {
isDrawing = true;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
ctx.beginPath();
ctx.moveTo(x, y);
}
function draw(e) {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
ctx.lineTo(x, y);
ctx.stroke();
}
function stopDrawing() {
isDrawing = false;
}
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
document.getElementById('recognizedText').textContent = 'Draw to see recognized text here';
document.getElementById('recognizedText').className = 'recognized-text';
updateTestResults('Canvas cleared');
}
function recognizeText() {
// Simple mock recognition
const mockWords = ['EXAMPLE', 'TEST', 'HELLO', 'WORLD', 'TABLET'];
const recognized = mockWords[Math.floor(Math.random() * mockWords.length)];
document.getElementById('recognizedText').textContent = recognized;
document.getElementById('recognizedText').className = 'recognized-text has-content';
updateTestResults(`Recognized: "${recognized}"`);
}
function toggleMode(mode) {
const buttons = document.querySelectorAll('.input-mode-toggle button');
const typingInput = document.getElementById('typingInput');
const handwritingContainer = document.getElementById('handwritingContainer');
buttons.forEach(btn => {
btn.classList.remove('active');
if (btn.dataset.mode === mode) {
btn.classList.add('active');
}
});
if (mode === 'typing') {
typingInput.style.display = 'block';
handwritingContainer.style.display = 'none';
updateTestResults('Switched to typing mode');
} else {
typingInput.style.display = 'none';
handwritingContainer.style.display = 'block';
updateTestResults('Switched to handwriting mode');
}
}
function submitAnswer() {
const typingInput = document.getElementById('typingInput');
const handwritingContainer = document.getElementById('handwritingContainer');
const recognizedText = document.getElementById('recognizedText');
let answer = '';
if (handwritingContainer.style.display !== 'none') {
answer = recognizedText.textContent;
if (answer === 'Draw to see recognized text here') answer = '';
} else {
answer = typingInput.value;
}
updateTestResults(`Submitted answer: "${answer}"`);
}
function updateTestResults(message) {
const results = document.getElementById('testResults');
const timestamp = new Date().toLocaleTimeString();
results.innerHTML += `[${timestamp}] ${message}<br>`;
results.scrollTop = results.scrollHeight;
}
// Initial test
updateTestResults('Tablet and handwriting test interface loaded');
updateTestResults('Try switching between typing and handwriting modes');
updateTestResults('Test drawing on the canvas when in handwriting mode');
</script>
</body>
</html>