feat: sound system — 12 new sounds + navigation category; dashboard FAB widget button

Sounds:
- UI: modal_open, modal_close, tab_switch, delete
- Navigation (new category): page_enter, section_reveal
- Classroom: timer_warning, wb_clear, file_shared
- Gamification: challenge_complete, daily_login
- Quiz: time_up, quiz_bonus

Dashboard:
- Widget configurator moved from header to fixed FAB (bottom-right)
  no longer pushed off-screen by wide sidebar

Profile settings:
- Added Navigation category toggle
- Expanded preview section: 12 test buttons covering all categories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-04-14 20:26:04 +03:00
parent 89ba25cd20
commit f3c9ab860e
3 changed files with 169 additions and 45 deletions
+31 -34
View File
@@ -1167,39 +1167,37 @@
/* Heatmap popup always right-anchored */ /* Heatmap popup always right-anchored */
.hm-day-popup { right: 10px !important; left: auto !important; top: auto !important; } .hm-day-popup { right: 10px !important; left: auto !important; top: auto !important; }
} }
/* ── Widget configurator ── */ /* ── Widget configurator FAB ── */
.dash-cfg-btn { .dash-cfg-fab {
margin-left: auto; flex-shrink: 0; display: none; /* shown for students via JS */
display: none; /* shown only for students via JS */ position: fixed; bottom: 88px; right: 20px; z-index: 80;
align-items: center; gap: 6px; width: 44px; height: 44px; border-radius: 50%;
padding: 6px 12px; border-radius: 8px; background: #9B5DE5; border: none; cursor: pointer;
background: rgba(155,93,229,0.13); border: 1px solid rgba(155,93,229,0.3); box-shadow: 0 4px 16px rgba(155,93,229,0.45);
color: #9B5DE5; font-size: 0.78rem; font-weight: 600; cursor: pointer; align-items: center; justify-content: center;
transition: background .15s; transition: transform .15s, box-shadow .15s;
} }
.dash-cfg-btn:hover { background: rgba(155,93,229,0.22); } .dash-cfg-fab:hover { transform: scale(1.08); box-shadow: 0 6px 24px rgba(155,93,229,0.55); }
.dash-cfg-btn svg { width: 14px; height: 14px; flex-shrink: 0; } .dash-cfg-fab svg { width: 20px; height: 20px; color: #fff; flex-shrink: 0; }
.dash-cfg-panel { .dash-cfg-panel {
display: none; position: absolute; top: calc(100% + 6px); right: 0; display: none; position: fixed; bottom: 140px; right: 20px;
background: #1e1b2e; border: 1px solid rgba(155,93,229,0.3); background: #1e1b2e; border: 1px solid rgba(155,93,229,0.3);
border-radius: 12px; padding: 12px; min-width: 210px; z-index: 50; border-radius: 14px; padding: 14px; min-width: 220px; z-index: 81;
box-shadow: 0 8px 24px rgba(0,0,0,.35); box-shadow: 0 8px 32px rgba(0,0,0,.45);
} }
.dash-cfg-panel.open { display: block; } .dash-cfg-panel.open { display: block; }
.dash-cfg-title { .dash-cfg-title {
font-size: 0.72rem; font-weight: 700; color: #9B5DE5; font-size: 0.72rem; font-weight: 700; color: #9B5DE5;
text-transform: uppercase; letter-spacing: .5px; margin-bottom: 8px; text-transform: uppercase; letter-spacing: .5px; margin-bottom: 10px;
} }
.dash-cfg-row { .dash-cfg-row {
display: flex; align-items: center; justify-content: space-between; display: flex; align-items: center; justify-content: space-between;
padding: 6px 4px; border-radius: 6px; cursor: pointer; padding: 7px 4px; border-radius: 6px; cursor: pointer;
} }
.dash-cfg-row:hover { background: rgba(255,255,255,.05); } .dash-cfg-row:hover { background: rgba(255,255,255,.05); }
.dash-cfg-row label { .dash-cfg-row label { font-size: 0.83rem; color: #e2e8f0; cursor: pointer; flex: 1; }
font-size: 0.82rem; color: #e2e8f0; cursor: pointer; flex: 1;
}
.dash-cfg-row input[type=checkbox] { accent-color: #9B5DE5; width: 15px; height: 15px; cursor: pointer; } .dash-cfg-row input[type=checkbox] { accent-color: #9B5DE5; width: 15px; height: 15px; cursor: pointer; }
.dash-cfg-wrap { position: relative; } .dash-cfg-wrap { display: contents; }
</style> </style>
<script src="https://cdn.jsdelivr.net/npm/lucide@0.469.0/dist/umd/lucide.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lucide@0.469.0/dist/umd/lucide.min.js"></script>
</head> </head>
@@ -1222,20 +1220,19 @@
<div class="stat-ring" id="sr-streak"></div> <div class="stat-ring" id="sr-streak"></div>
<div class="stat-ring" id="sr-pending"></div> <div class="stat-ring" id="sr-pending"></div>
</div> </div>
<div class="dash-cfg-wrap" id="dash-cfg-wrap"> </div>
<button class="dash-cfg-btn" id="dash-cfg-btn" onclick="toggleDashCfg(event)" title="Настроить виджеты">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg> <!-- Widget configurator FAB (fixed, outside header flow) -->
Виджеты <button class="dash-cfg-fab" id="dash-cfg-btn" onclick="toggleDashCfg(event)" title="Настроить виджеты">
</button> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
<div class="dash-cfg-panel" id="dash-cfg-panel"> </button>
<div class="dash-cfg-title">Показывать виджеты</div> <div class="dash-cfg-panel" id="dash-cfg-panel">
<div class="dash-cfg-row" onclick="toggleDashWidget('lb-section',this)"><label>Рейтинг</label><input type="checkbox" data-widget="lb-section" checked></div> <div class="dash-cfg-title">Показывать виджеты</div>
<div class="dash-cfg-row" onclick="toggleDashWidget('ch-section',this)"><label>Испытания недели</label><input type="checkbox" data-widget="ch-section" checked></div> <div class="dash-cfg-row" onclick="toggleDashWidget('lb-section',this)"><label>Рейтинг</label><input type="checkbox" data-widget="lb-section" checked></div>
<div class="dash-cfg-row" onclick="toggleDashWidget('stats-section',this)"><label>Статистика</label><input type="checkbox" data-widget="stats-section" checked></div> <div class="dash-cfg-row" onclick="toggleDashWidget('ch-section',this)"><label>Испытания недели</label><input type="checkbox" data-widget="ch-section" checked></div>
<div class="dash-cfg-row" onclick="toggleDashWidget('w-my-subs',this)"><label>Мои сдачи</label><input type="checkbox" data-widget="w-my-subs" checked></div> <div class="dash-cfg-row" onclick="toggleDashWidget('stats-section',this)"><label>Статистика</label><input type="checkbox" data-widget="stats-section" checked></div>
<div class="dash-cfg-row" onclick="toggleDashWidget('w-theory-progress',this)"><label>Теория</label><input type="checkbox" data-widget="w-theory-progress" checked></div> <div class="dash-cfg-row" onclick="toggleDashWidget('w-my-subs',this)"><label>Мои сдачи</label><input type="checkbox" data-widget="w-my-subs" checked></div>
</div> <div class="dash-cfg-row" onclick="toggleDashWidget('w-theory-progress',this)"><label>Теория</label><input type="checkbox" data-widget="w-theory-progress" checked></div>
</div>
</div> </div>
<div class="container"> <div class="container">
+56 -10
View File
@@ -819,7 +819,7 @@
<div class="p-card-icon"><i data-lucide="volume-2" style="width:15px;height:15px"></i></div> <div class="p-card-icon"><i data-lucide="volume-2" style="width:15px;height:15px"></i></div>
<div> <div>
<div class="p-card-title">Звуки системы</div> <div class="p-card-title">Звуки системы</div>
<div class="p-card-sub">Уведомления, геймификация, classroom, квизы</div> <div class="p-card-sub">Синтезированные — не требуют загрузки файлов</div>
</div> </div>
</div> </div>
@@ -854,7 +854,7 @@
<div class="pref-row"> <div class="pref-row">
<div class="pref-row-info"> <div class="pref-row-info">
<div class="pref-row-label">Интерфейс</div> <div class="pref-row-label">Интерфейс</div>
<div class="pref-row-desc">Клики, успех, ошибки, уведомления</div> <div class="pref-row-desc">Клики, модальные окна, удаление, уведомления</div>
</div> </div>
<label class="pref-toggle"> <label class="pref-toggle">
<input type="checkbox" id="pref-sfx-ui" onchange="prefSfxCat('ui',this.checked)"> <input type="checkbox" id="pref-sfx-ui" onchange="prefSfxCat('ui',this.checked)">
@@ -862,11 +862,23 @@
</label> </label>
</div> </div>
<!-- Navigation sounds -->
<div class="pref-row">
<div class="pref-row-info">
<div class="pref-row-label">Навигация</div>
<div class="pref-row-desc">Переходы между страницами и секциями</div>
</div>
<label class="pref-toggle">
<input type="checkbox" id="pref-sfx-navigation" onchange="prefSfxCat('navigation',this.checked)">
<span class="pref-toggle-track"></span>
</label>
</div>
<!-- Classroom sounds --> <!-- Classroom sounds -->
<div class="pref-row"> <div class="pref-row">
<div class="pref-row-info"> <div class="pref-row-info">
<div class="pref-row-label">Classroom</div> <div class="pref-row-label">Classroom</div>
<div class="pref-row-desc">Урок, участники, чат, рука, рисование</div> <div class="pref-row-desc">Урок, участники, таймер, доска, файлы</div>
</div> </div>
<label class="pref-toggle"> <label class="pref-toggle">
<input type="checkbox" id="pref-sfx-classroom" onchange="prefSfxCat('classroom',this.checked)"> <input type="checkbox" id="pref-sfx-classroom" onchange="prefSfxCat('classroom',this.checked)">
@@ -878,7 +890,7 @@
<div class="pref-row"> <div class="pref-row">
<div class="pref-row-info"> <div class="pref-row-info">
<div class="pref-row-label">Геймификация</div> <div class="pref-row-label">Геймификация</div>
<div class="pref-row-desc">XP, уровень, достижения, монеты</div> <div class="pref-row-desc">XP, уровень, достижения, испытания, вход</div>
</div> </div>
<label class="pref-toggle"> <label class="pref-toggle">
<input type="checkbox" id="pref-sfx-gamification" onchange="prefSfxCat('gamification',this.checked)"> <input type="checkbox" id="pref-sfx-gamification" onchange="prefSfxCat('gamification',this.checked)">
@@ -890,7 +902,7 @@
<div class="pref-row"> <div class="pref-row">
<div class="pref-row-info"> <div class="pref-row-info">
<div class="pref-row-label">Квизы</div> <div class="pref-row-label">Квизы</div>
<div class="pref-row-desc">Старт вопроса, правильно, неправильно</div> <div class="pref-row-desc">Старт, правильно/неправильно, таймер, бонус</div>
</div> </div>
<label class="pref-toggle"> <label class="pref-toggle">
<input type="checkbox" id="pref-sfx-quiz" onchange="prefSfxCat('quiz',this.checked)"> <input type="checkbox" id="pref-sfx-quiz" onchange="prefSfxCat('quiz',this.checked)">
@@ -898,23 +910,56 @@
</label> </label>
</div> </div>
<!-- Test button --> <!-- Preview buttons -->
<div style="margin-top:14px;display:flex;gap:8px;flex-wrap:wrap"> <div class="pref-section-label" style="margin-top:14px">Прослушать</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px">
<button class="pref-test-btn" onclick="prefSfxTest('notification')"> <button class="pref-test-btn" onclick="prefSfxTest('notification')">
<i data-lucide="bell" style="width:12px;height:12px;vertical-align:-2px"></i> <i data-lucide="bell" style="width:12px;height:12px;vertical-align:-2px"></i>
Уведомление Уведомление
</button> </button>
<button class="pref-test-btn" onclick="prefSfxTest('success')">
<i data-lucide="check-circle" style="width:12px;height:12px;vertical-align:-2px"></i>
Успех
</button>
<button class="pref-test-btn" onclick="prefSfxTest('error')">
<i data-lucide="x-circle" style="width:12px;height:12px;vertical-align:-2px"></i>
Ошибка
</button>
<button class="pref-test-btn" onclick="prefSfxTest('modal_open')">
<i data-lucide="layout" style="width:12px;height:12px;vertical-align:-2px"></i>
Модал
</button>
<button class="pref-test-btn" onclick="prefSfxTest('page_enter')">
<i data-lucide="arrow-right" style="width:12px;height:12px;vertical-align:-2px"></i>
Навигация
</button>
<button class="pref-test-btn" onclick="prefSfxTest('lesson_start')">
<i data-lucide="play" style="width:12px;height:12px;vertical-align:-2px"></i>
Урок
</button>
<button class="pref-test-btn" onclick="prefSfxTest('timer_warning')">
<i data-lucide="clock" style="width:12px;height:12px;vertical-align:-2px"></i>
Таймер
</button>
<button class="pref-test-btn" onclick="prefSfxTest('achievement')"> <button class="pref-test-btn" onclick="prefSfxTest('achievement')">
<i data-lucide="trophy" style="width:12px;height:12px;vertical-align:-2px"></i> <i data-lucide="trophy" style="width:12px;height:12px;vertical-align:-2px"></i>
Ачивка Ачивка
</button> </button>
<button class="pref-test-btn" onclick="prefSfxTest('challenge_complete')">
<i data-lucide="target" style="width:12px;height:12px;vertical-align:-2px"></i>
Испытание
</button>
<button class="pref-test-btn" onclick="prefSfxTest('level_up')"> <button class="pref-test-btn" onclick="prefSfxTest('level_up')">
<i data-lucide="zap" style="width:12px;height:12px;vertical-align:-2px"></i> <i data-lucide="zap" style="width:12px;height:12px;vertical-align:-2px"></i>
Уровень Уровень
</button> </button>
<button class="pref-test-btn" onclick="prefSfxTest('lesson_start')"> <button class="pref-test-btn" onclick="prefSfxTest('quiz_correct')">
<i data-lucide="play" style="width:12px;height:12px;vertical-align:-2px"></i> <i data-lucide="check" style="width:12px;height:12px;vertical-align:-2px"></i>
Урок Правильно
</button>
<button class="pref-test-btn" onclick="prefSfxTest('time_up')">
<i data-lucide="timer-off" style="width:12px;height:12px;vertical-align:-2px"></i>
Время вышло
</button> </button>
</div> </div>
</div> </div>
@@ -1537,6 +1582,7 @@
const setChk = (id, v) => { const el = document.getElementById(id); if (el) el.checked = v; }; const setChk = (id, v) => { const el = document.getElementById(id); if (el) el.checked = v; };
setChk('pref-sfx-enabled', sfx.enabled); setChk('pref-sfx-enabled', sfx.enabled);
setChk('pref-sfx-ui', sfx.prefs.ui); setChk('pref-sfx-ui', sfx.prefs.ui);
setChk('pref-sfx-navigation', sfx.prefs.navigation !== false);
setChk('pref-sfx-classroom', sfx.prefs.classroom); setChk('pref-sfx-classroom', sfx.prefs.classroom);
setChk('pref-sfx-gamification', sfx.prefs.gamification); setChk('pref-sfx-gamification', sfx.prefs.gamification);
setChk('pref-sfx-quiz', sfx.prefs.quiz); setChk('pref-sfx-quiz', sfx.prefs.quiz);
+82 -1
View File
@@ -134,6 +134,36 @@
_tone(880, 80, 'sine', 0.13, 5, 50); _tone(880, 80, 'sine', 0.13, 5, 50);
setTimeout(function () { _tone(1108, 120, 'sine', 0.15, 5, 80); }, 90); setTimeout(function () { _tone(1108, 120, 'sine', 0.15, 5, 80); }, 90);
}, },
modal_open: function () {
// Soft sweep in — airy whoosh up
_sweep(300, 520, 180, 'sine', 0.09);
setTimeout(function () { _tone(660, 100, 'sine', 0.08, 5, 70); }, 160);
},
modal_close: function () {
// Sweep down — gentle dismiss
_sweep(520, 300, 150, 'sine', 0.08);
},
tab_switch: function () {
// Double-click feel
_tone(900, 35, 'sine', 0.09, 2, 25);
setTimeout(function () { _tone(1100, 35, 'sine', 0.09, 2, 25); }, 45);
},
delete: function () {
// Descending soft thud
_sweep(400, 180, 120, 'triangle', 0.10);
_noise(80, 0.04);
},
// ── Navigation ────────────────────────────────────────────────────────
page_enter: function () {
// Subtle arrival chime
_tone(784, 80, 'sine', 0.08, 5, 55);
setTimeout(function () { _tone(1047, 120, 'sine', 0.07, 5, 80); }, 75);
},
section_reveal: function () {
// Very quiet rising whoosh
_sweep(250, 420, 200, 'sine', 0.07);
},
// ── Classroom ───────────────────────────────────────────────────────── // ── Classroom ─────────────────────────────────────────────────────────
hand_raise: function () { hand_raise: function () {
@@ -173,6 +203,23 @@
setTimeout(function () { _tone(330, 160, 'sine', 0.14, 10, 100); }, 140); setTimeout(function () { _tone(330, 160, 'sine', 0.14, 10, 100); }, 140);
setTimeout(function () { _tone(261, 350, 'sine', 0.13, 10, 220); }, 280); setTimeout(function () { _tone(261, 350, 'sine', 0.13, 10, 220); }, 280);
}, },
timer_warning: function () {
// Urgent triple beep — last 10 seconds
_tone(880, 60, 'square', 0.12, 2, 40);
setTimeout(function () { _tone(880, 60, 'square', 0.13, 2, 40); }, 200);
setTimeout(function () { _tone(1108, 100, 'square', 0.15, 2, 70); }, 400);
},
wb_clear: function () {
// Erasing noise sweep
_noise(220, 0.08);
_sweep(600, 200, 220, 'triangle', 0.06);
},
file_shared: function () {
// Paper rustle + chime
_noise(80, 0.05);
setTimeout(function () { _tone(784, 100, 'sine', 0.12, 5, 70); }, 60);
setTimeout(function () { _tone(1047, 120, 'sine', 0.10, 5, 80); }, 160);
},
// ── Gamification ────────────────────────────────────────────────────── // ── Gamification ──────────────────────────────────────────────────────
xp_gain: function () { xp_gain: function () {
@@ -218,6 +265,21 @@
setTimeout(function () { _tone(659, 80, 'sine', 0.14, 5, 55); }, 80); setTimeout(function () { _tone(659, 80, 'sine', 0.14, 5, 55); }, 80);
setTimeout(function () { _tone(784, 160, 'sine', 0.16, 5, 100); }, 160); setTimeout(function () { _tone(784, 160, 'sine', 0.16, 5, 100); }, 160);
}, },
challenge_complete: function () {
// Heroic: E4-G4-B4-E5 arpeggio
_tone(330, 140, 'sine', 0.15, 8, 90);
setTimeout(function () { _tone(392, 140, 'sine', 0.16, 8, 90); }, 130);
setTimeout(function () { _tone(494, 140, 'sine', 0.17, 8, 90); }, 260);
setTimeout(function () { _tone(659, 400, 'sine', 0.20, 8, 260); }, 390);
// Sparkle on top
setTimeout(function () { _tone(1319, 200, 'triangle', 0.07); }, 450);
},
daily_login: function () {
// Morning chime: G-A-B
_tone(392, 120, 'sine', 0.12, 8, 80);
setTimeout(function () { _tone(440, 120, 'sine', 0.13, 8, 80); }, 110);
setTimeout(function () { _tone(494, 250, 'sine', 0.15, 8, 160); }, 220);
},
// ── Quiz ────────────────────────────────────────────────────────────── // ── Quiz ──────────────────────────────────────────────────────────────
quiz_start: function () { quiz_start: function () {
@@ -244,6 +306,20 @@
quiz_tick: function () { quiz_tick: function () {
_tone(660, 40, 'square', 0.08, 2, 25); _tone(660, 40, 'square', 0.08, 2, 25);
}, },
time_up: function () {
// Buzzer — descending sawtooth alarm
_tone(440, 120, 'sawtooth', 0.14, 3, 80);
_noise(120, 0.06);
setTimeout(function () { _tone(330, 160, 'sawtooth', 0.12, 3, 110); }, 110);
setTimeout(function () { _tone(220, 220, 'sawtooth', 0.10, 3, 160); }, 240);
},
quiz_bonus: function () {
// Golden sparkle — high twinkling
_tone(1047, 60, 'sine', 0.14, 2, 45);
setTimeout(function () { _tone(1319, 60, 'sine', 0.16, 2, 45); }, 65);
setTimeout(function () { _tone(1568, 60, 'sine', 0.17, 2, 45); }, 130);
setTimeout(function () { _tone(2093, 180, 'sine', 0.18, 2, 120); }, 195);
},
}; };
// ── Persistence ─────────────────────────────────────────────────────────── // ── Persistence ───────────────────────────────────────────────────────────
@@ -272,6 +348,7 @@
volume : 0.75, volume : 0.75,
prefs : { prefs : {
ui : true, ui : true,
navigation : true,
classroom : true, classroom : true,
gamification : true, gamification : true,
quiz : true, quiz : true,
@@ -280,14 +357,18 @@
// Category map — which prefs key each sound belongs to // Category map — which prefs key each sound belongs to
_cats: { _cats: {
click: 'ui', success: 'ui', error: 'ui', notification: 'ui', click: 'ui', success: 'ui', error: 'ui', notification: 'ui',
modal_open: 'ui', modal_close: 'ui', tab_switch: 'ui', delete: 'ui',
page_enter: 'navigation', section_reveal: 'navigation',
hand_raise: 'classroom', chat_message: 'classroom', hand_raise: 'classroom', chat_message: 'classroom',
user_joined: 'classroom', user_left: 'classroom', user_joined: 'classroom', user_left: 'classroom',
muted: 'classroom', draw_permitted: 'classroom', muted: 'classroom', draw_permitted: 'classroom',
lesson_start: 'classroom', lesson_end: 'classroom', lesson_start: 'classroom', lesson_end: 'classroom',
timer_warning: 'classroom', wb_clear: 'classroom', file_shared: 'classroom',
xp_gain: 'gamification', level_up: 'gamification', xp_gain: 'gamification', level_up: 'gamification',
achievement: 'gamification', coin: 'gamification', streak: 'gamification', achievement: 'gamification', coin: 'gamification', streak: 'gamification',
challenge_complete: 'gamification', daily_login: 'gamification',
quiz_start: 'quiz', quiz_correct: 'quiz', quiz_wrong: 'quiz', quiz_start: 'quiz', quiz_correct: 'quiz', quiz_wrong: 'quiz',
quiz_end: 'quiz', quiz_tick: 'quiz', quiz_end: 'quiz', quiz_tick: 'quiz', time_up: 'quiz', quiz_bonus: 'quiz',
}, },
play: function (name) { play: function (name) {