Add interactive tutorial system for calibration and device cards
Some checks failed
Validate / validate (push) Failing after 8s

Generic tutorial engine supports absolute (modal) and fixed (viewport)
positioning modes with spotlight backdrop, pulsing ring, and tooltip.
Calibration tutorial covers LED count, corner, direction, offset, span,
test, and toggle inputs. Device tutorial walks through card controls.
Auto-triggers on first calibration open and first device add.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 04:51:56 +03:00
parent fa322ee0ce
commit 2a085e63a0
5 changed files with 526 additions and 11 deletions

View File

@@ -206,7 +206,7 @@ section {
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
padding: 12px 20px 20px;
position: relative;
transition: transform 0.2s, box-shadow 0.2s;
}
@@ -215,6 +215,31 @@ section {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.card-tutorial-btn {
position: absolute;
bottom: 10px;
right: 10px;
background: none;
border: 1.5px solid var(--border-color);
color: var(--text-muted, #777);
font-size: 0.7rem;
font-weight: bold;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 50%;
transition: color 0.2s, background 0.2s, border-color 0.2s;
padding: 0;
}
.card-tutorial-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
}
.card-remove-btn {
position: absolute;
top: 10px;
@@ -242,7 +267,7 @@ section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
margin-bottom: 10px;
padding-right: 30px;
}
@@ -1368,3 +1393,202 @@ input:-webkit-autofill:focus {
}
}
/* Tutorial System */
.tutorial-trigger-btn {
width: 28px;
height: 28px;
border-radius: 50%;
border: 2px solid var(--primary-color);
background: transparent;
color: var(--primary-color);
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
margin-left: auto;
margin-right: 8px;
flex-shrink: 0;
}
.tutorial-trigger-btn:hover {
background: var(--primary-color);
color: white;
}
#calibration-modal .modal-body {
position: relative;
}
.tutorial-overlay {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
pointer-events: none;
}
.tutorial-overlay.active {
display: block;
pointer-events: auto;
}
.tutorial-backdrop {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
transition: clip-path 0.3s ease;
}
.tutorial-ring {
position: absolute;
border: 2px solid var(--primary-color);
border-radius: 6px;
pointer-events: none;
transition: all 0.3s ease;
animation: tutorial-pulse 2s infinite;
}
@keyframes tutorial-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.6); }
50% { box-shadow: 0 0 0 6px rgba(76, 175, 80, 0); }
}
.tutorial-tooltip {
position: absolute;
width: 260px;
background: var(--card-bg);
border: 2px solid var(--primary-color);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
z-index: 102;
pointer-events: auto;
animation: tutorial-tooltip-in 0.25s ease-out;
}
@keyframes tutorial-tooltip-in {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
.tutorial-tooltip-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid var(--border-color);
}
.tutorial-step-counter {
font-size: 0.8rem;
font-weight: 600;
color: var(--primary-color);
}
.tutorial-close-btn {
background: none;
border: none;
color: #777;
font-size: 1.3rem;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 4px;
transition: color 0.2s, background 0.2s;
padding: 0;
line-height: 1;
}
.tutorial-close-btn:hover {
color: var(--text-color);
background: rgba(128, 128, 128, 0.15);
}
.tutorial-tooltip-text {
margin: 0;
padding: 12px;
line-height: 1.5;
color: var(--text-color);
font-size: 0.9rem;
}
.tutorial-tooltip-nav {
display: flex;
gap: 6px;
padding: 8px 12px;
border-top: 1px solid var(--border-color);
}
.tutorial-prev-btn,
.tutorial-next-btn {
flex: 1;
padding: 6px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: opacity 0.2s;
}
.tutorial-prev-btn {
background: var(--border-color);
color: var(--text-color);
}
.tutorial-next-btn {
background: var(--primary-color);
color: white;
}
.tutorial-prev-btn:hover:not(:disabled),
.tutorial-next-btn:hover:not(:disabled) {
opacity: 0.85;
}
.tutorial-prev-btn:disabled,
.tutorial-next-btn:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.tutorial-target {
position: relative;
z-index: 101 !important;
}
/* Fixed (viewport-level) tutorial overlay for device cards */
.tutorial-overlay-fixed {
position: fixed;
z-index: 10000;
}
.tutorial-overlay-fixed .tutorial-backdrop {
position: fixed;
}
.tutorial-overlay-fixed .tutorial-ring {
position: fixed;
}
.tutorial-overlay-fixed .tutorial-tooltip {
position: absolute;
z-index: 10002;
animation: none;
opacity: 1;
}
/* target z-index for fixed overlay is set inline via JS (target is outside overlay DOM) */