v0.1.36: Add dark theme, fix BLE scanning, improve responsive design\n\n- Fixed BLE scanning RSSI error that caused 'BLEDevice' object has no attribute 'rssi'\n- Implemented complete dark theme with CSS variables and system preference support\n- Added responsive design with mobile-first breakpoints (768px, 1024px, 1440px)\n- Added theme toggle with local storage persistence\n- Improved BLE error handling and user guidance\n- Enhanced pairing function documentation to clarify BLE vs Classic Bluetooth\n- Updated version to 0.1.36 in config.yaml and api.py\n\nGenerated by Mistral Vibe.\nCo-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -6,19 +6,42 @@
|
||||
<title>Fichero Printer</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f4efe6;
|
||||
--panel: #fffaf2;
|
||||
--line: #d8cdbd;
|
||||
--ink: #2d241d;
|
||||
--muted: #6c6258;
|
||||
--accent: #b55e33;
|
||||
--accent-2: #245b4b;
|
||||
--success: #2e8b57;
|
||||
--warning: #cd853f;
|
||||
--shadow-sm: 0 1px 3px rgba(45, 36, 29, 0.1);
|
||||
--shadow-md: 0 4px 12px rgba(45, 36, 29, 0.1);
|
||||
--shadow-lg: 0 12px 24px rgba(45, 36, 29, 0.15);
|
||||
--bg-primary: #0a0a0a;
|
||||
--bg-secondary: #111827;
|
||||
--text-primary: #e5e7eb;
|
||||
--text-secondary: #9ca3af;
|
||||
--accent: #3b82f6;
|
||||
--accent-green: #10b981;
|
||||
--accent-orange: #f59e0b;
|
||||
--border: #374151;
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
--transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
}
|
||||
|
||||
/* Light theme variables */
|
||||
[data-theme="light"] {
|
||||
--bg-primary: #ffffff;
|
||||
--bg-secondary: #f9fafb;
|
||||
--text-primary: #111827;
|
||||
--text-secondary: #6b7280;
|
||||
--accent: #3b82f6;
|
||||
--border: #e5e7eb;
|
||||
}
|
||||
|
||||
/* System dark mode support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]) {
|
||||
--bg-primary: #0a0a0a;
|
||||
--bg-secondary: #111827;
|
||||
--text-primary: #e5e7eb;
|
||||
--text-secondary: #9ca3af;
|
||||
--border: #374151;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -30,27 +53,51 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", system-ui, sans-serif;
|
||||
color: var(--ink);
|
||||
background:
|
||||
radial-gradient(circle at top left, #fff8ed 0, transparent 35%),
|
||||
linear-gradient(180deg, #efe4d3 0%, var(--bg) 100%);
|
||||
color: var(--text-primary);
|
||||
background-color: var(--bg-primary);
|
||||
min-height: 100vh;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1200px;
|
||||
/* Responsive container */
|
||||
.container {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1rem 3rem;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
/* Responsive breakpoints */
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
padding: 0 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.container {
|
||||
max-width: 1440px;
|
||||
padding: 0 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 0 3rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
margin-bottom: 2rem;
|
||||
padding: 2rem;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 250, 242, 0.95);
|
||||
backdrop-filter: blur(6px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
background-color: var(--bg-secondary);
|
||||
box-shadow: var(--shadow-md);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -62,8 +109,8 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--accent-2));
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--accent-green));
|
||||
}
|
||||
|
||||
h1 {
|
||||
@@ -73,14 +120,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--ink);
|
||||
border-bottom: 2px solid var(--line);
|
||||
color: var(--text-primary);
|
||||
border-bottom: 2px solid var(--border);
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -88,11 +136,11 @@
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--ink);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@@ -105,9 +153,9 @@
|
||||
|
||||
.card {
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 16px;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--bg-secondary);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: var(--transition);
|
||||
}
|
||||
@@ -120,7 +168,8 @@
|
||||
.card h3 {
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
label {
|
||||
@@ -128,24 +177,25 @@
|
||||
margin: 0.75rem 0 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
padding: 0.75rem 1rem;
|
||||
font: inherit;
|
||||
font-size: 0.95rem;
|
||||
background: white;
|
||||
background-color: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
input:focus, select:focus, textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 2px rgba(181, 94, 51, 0.1);
|
||||
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
textarea {
|
||||
@@ -177,9 +227,9 @@
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
background: var(--accent);
|
||||
background-color: var(--accent);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 0.95rem;
|
||||
transition: var(--transition);
|
||||
@@ -190,7 +240,7 @@
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #a1542d;
|
||||
background-color: #2563eb;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@@ -199,29 +249,29 @@
|
||||
}
|
||||
|
||||
button.alt {
|
||||
background: var(--accent-2);
|
||||
background-color: var(--accent-green);
|
||||
}
|
||||
|
||||
button.alt:hover {
|
||||
background: #1f4e41;
|
||||
background-color: #059669;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
background: var(--line);
|
||||
color: var(--ink);
|
||||
background-color: var(--border);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
button.secondary:hover {
|
||||
background: #c5b8a8;
|
||||
background-color: #4b5563;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
margin: 1rem 0;
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
background: #241f1a;
|
||||
color: #f7efe4;
|
||||
border-radius: var(--radius-md);
|
||||
background-color: #1f2937;
|
||||
color: var(--text-primary);
|
||||
min-height: 160px;
|
||||
font-size: 0.9rem;
|
||||
position: relative;
|
||||
@@ -232,7 +282,7 @@
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 1rem;
|
||||
color: var(--muted);
|
||||
color: var(--text-secondary);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -248,9 +298,22 @@
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
/* Touch-friendly button sizes for mobile */
|
||||
@media (max-width: 768px) {
|
||||
button {
|
||||
min-height: 44px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
}
|
||||
|
||||
.actions button {
|
||||
min-height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
main {
|
||||
padding: 1.5rem 1rem 2.5rem;
|
||||
padding: 1.5rem 0 2.5rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
@@ -289,17 +352,49 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading spinner */
|
||||
/* Loading spinner - updated for dark theme */
|
||||
.loading {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: white;
|
||||
border-top-color: var(--accent);
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Animation for loading spinner */
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Theme toggle button */
|
||||
.theme-toggle {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: var(--shadow-md);
|
||||
z-index: 1000;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background-color: var(--border);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
@@ -359,13 +454,14 @@
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<section class="hero">
|
||||
<h1>Fichero Printer</h1>
|
||||
<p class="muted">Home Assistant print console for status, text labels, and image uploads.</p>
|
||||
<p class="muted">API docs remain available at <a href="docs">/docs</a>.</p>
|
||||
</section>
|
||||
<div class="container">
|
||||
<section class="hero">
|
||||
<h1>Fichero Printer</h1>
|
||||
<p class="muted">Home Assistant print console for status, text labels, and image uploads.</p>
|
||||
<p class="muted">API docs remain available at <a href="docs">/docs</a>.</p>
|
||||
</section>
|
||||
|
||||
<section class="grid">
|
||||
<section class="grid">
|
||||
<div class="card">
|
||||
<h2>Connection</h2>
|
||||
<label for="address">Printer address</label>
|
||||
@@ -476,8 +572,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div> <!-- Close container -->
|
||||
</main>
|
||||
|
||||
<!-- Theme Toggle Button -->
|
||||
<button id="theme-toggle" class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle theme">
|
||||
🌙
|
||||
</button>
|
||||
|
||||
<script>
|
||||
function commonParams() {
|
||||
const address = document.getElementById("address").value.trim();
|
||||
@@ -550,6 +652,31 @@
|
||||
const response = await fetch("print/image", { method: "POST", body: form });
|
||||
await showResponse(response);
|
||||
}
|
||||
/* Theme toggle functionality */
|
||||
function toggleTheme() {
|
||||
const html = document.documentElement;
|
||||
const currentTheme = html.getAttribute('data-theme');
|
||||
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||
html.setAttribute('data-theme', newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
updateThemeIcon(newTheme);
|
||||
}
|
||||
|
||||
function updateThemeIcon(theme) {
|
||||
const toggleButton = document.getElementById('theme-toggle');
|
||||
if (toggleButton) {
|
||||
toggleButton.textContent = theme === 'light' ? '🌙' : '☀️';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize theme
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const initialTheme = savedTheme || (systemPrefersDark ? 'dark' : 'light');
|
||||
document.documentElement.setAttribute('data-theme', initialTheme);
|
||||
updateThemeIcon(initialTheme);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user