|
|
|
@@ -2007,6 +2007,55 @@
|
|
|
|
|
background: #0D0D1A;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Annotate-over-sim mode ───────────────────────────────────────────── */
|
|
|
|
|
/* Board floats above the sim panel (sim visible behind transparent canvas) */
|
|
|
|
|
.cr-board-area.annotate-active .cr-sim-panel { z-index: 1; }
|
|
|
|
|
.cr-board-area.annotate-active .cr-board-wrap {
|
|
|
|
|
z-index: 45;
|
|
|
|
|
background: transparent !important;
|
|
|
|
|
border-color: transparent !important;
|
|
|
|
|
box-shadow: none !important;
|
|
|
|
|
}
|
|
|
|
|
.cr-board-area.annotate-active .cr-board-wrap::before,
|
|
|
|
|
.cr-board-area.annotate-active .cr-board-wrap::after { display: none !important; }
|
|
|
|
|
/* Floating bar shown while annotating */
|
|
|
|
|
.cr-annotate-bar {
|
|
|
|
|
display: none;
|
|
|
|
|
position: absolute; top: 0; left: 0; right: 0; z-index: 60;
|
|
|
|
|
height: 40px; flex-shrink: 0;
|
|
|
|
|
background: rgba(10,6,22,0.92);
|
|
|
|
|
border-bottom: 1.5px solid rgba(241,91,181,0.4);
|
|
|
|
|
backdrop-filter: blur(6px);
|
|
|
|
|
align-items: center; gap: 8px; padding: 0 12px;
|
|
|
|
|
}
|
|
|
|
|
.cr-board-area.annotate-active .cr-annotate-bar { display: flex; }
|
|
|
|
|
.cr-annotate-bar-label {
|
|
|
|
|
font-size: 0.75rem; font-weight: 700; color: #F15BB5;
|
|
|
|
|
display: flex; align-items: center; gap: 5px; flex: 1;
|
|
|
|
|
}
|
|
|
|
|
.cr-annotate-bar-label svg { width: 14px; height: 14px; stroke: #F15BB5; }
|
|
|
|
|
.cr-annotate-exit {
|
|
|
|
|
padding: 4px 12px; border-radius: 7px; border: 1px solid rgba(241,91,181,0.4);
|
|
|
|
|
background: rgba(241,91,181,0.1); color: #F15BB5;
|
|
|
|
|
font-family: 'Manrope',sans-serif; font-size: 0.72rem; font-weight: 700;
|
|
|
|
|
cursor: pointer; transition: all .15s;
|
|
|
|
|
}
|
|
|
|
|
.cr-annotate-exit:hover { background: rgba(241,91,181,0.22); }
|
|
|
|
|
/* "Draw" button in sim bar */
|
|
|
|
|
.cr-sim-annotate-btn {
|
|
|
|
|
display: none;
|
|
|
|
|
padding: 3px 10px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.12);
|
|
|
|
|
background: transparent; color: rgba(255,255,255,0.5);
|
|
|
|
|
font-family: 'Manrope',sans-serif; font-size: 0.7rem; font-weight: 700;
|
|
|
|
|
cursor: pointer; transition: all .15s; align-items: center; gap: 4px;
|
|
|
|
|
margin-right: 4px;
|
|
|
|
|
}
|
|
|
|
|
.cr-sim-annotate-btn svg { width: 12px; height: 12px; flex-shrink: 0; }
|
|
|
|
|
.cr-sim-annotate-btn:hover { color: rgba(255,255,255,0.8); border-color: rgba(241,91,181,0.4); }
|
|
|
|
|
.cr-sim-annotate-btn.active { background: rgba(241,91,181,0.18); border-color: rgba(241,91,181,0.5); color: #F15BB5; }
|
|
|
|
|
/* Show draw button only for teachers when sim is open */
|
|
|
|
|
.cr-sim-panel.open .cr-sim-annotate-btn.teacher-ctrl { display: flex; }
|
|
|
|
|
|
|
|
|
|
/* ── Simulation picker modal ────────────────────────────────────────── */
|
|
|
|
|
.cr-sim-picker-overlay {
|
|
|
|
|
position: fixed; inset: 0; z-index: 200;
|
|
|
|
@@ -2202,6 +2251,11 @@
|
|
|
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18"/></svg>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="cr-sim-bar-title" id="cr-sim-bar-title">Симуляция</span>
|
|
|
|
|
<!-- Draw-over button (teacher only) -->
|
|
|
|
|
<button class="cr-sim-annotate-btn teacher-ctrl" id="cr-sim-annotate-btn" onclick="crToggleAnnotate()" title="Рисовать поверх симуляции">
|
|
|
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
|
|
|
|
Рисовать
|
|
|
|
|
</button>
|
|
|
|
|
<!-- Mode toggle (teacher only) -->
|
|
|
|
|
<div class="cr-sim-mode" id="cr-sim-mode-toggle" style="display:none">
|
|
|
|
|
<button class="cr-sim-mode-btn active" id="cr-sim-mode-demo" onclick="crSetSimMode('demo')" title="Все видят одно и то же">Демо</button>
|
|
|
|
@@ -2215,6 +2269,14 @@
|
|
|
|
|
<!-- Blocks student interaction in demo mode -->
|
|
|
|
|
<div class="cr-sim-blocker" id="cr-sim-blocker"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- Annotate-mode floating bar (shown on top of sim when drawing) -->
|
|
|
|
|
<div class="cr-annotate-bar" id="cr-annotate-bar">
|
|
|
|
|
<span class="cr-annotate-bar-label">
|
|
|
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
|
|
|
|
Режим аннотации
|
|
|
|
|
</span>
|
|
|
|
|
<button class="cr-annotate-exit" id="cr-annotate-exit-btn" onclick="crToggleAnnotate()">Вернуться к симуляции</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div><!-- /.cr-board-area -->
|
|
|
|
|
<!-- student nav: page nav + follow toggle -->
|
|
|
|
|
<div class="cr-student-nav" id="cr-student-nav" style="display:none">
|
|
|
|
@@ -3600,6 +3662,8 @@
|
|
|
|
|
}
|
|
|
|
|
} else if (data.type === 'classroom_sim_mode') {
|
|
|
|
|
if (_sessionId == data.sessionId) onSimModeChange(data.mode);
|
|
|
|
|
} else if (data.type === 'classroom_sim_annotate') {
|
|
|
|
|
if (_sessionId == data.sessionId) _crApplyAnnotate(data.active);
|
|
|
|
|
} else if (data.type === '_sse_reconnect') {
|
|
|
|
|
// SSE reconnected after a drop — re-sync all real-time state to fill the gap
|
|
|
|
|
if (_sessionId) resyncAfterReconnect();
|
|
|
|
@@ -6867,6 +6931,9 @@
|
|
|
|
|
const modeToggle = document.getElementById('cr-sim-mode-toggle');
|
|
|
|
|
const blocker = document.getElementById('cr-sim-blocker');
|
|
|
|
|
|
|
|
|
|
// Exit annotate mode when sim closes
|
|
|
|
|
if (_annotateActive) _crApplyAnnotate(false);
|
|
|
|
|
|
|
|
|
|
panel.classList.remove('open');
|
|
|
|
|
if (modeToggle) modeToggle.style.display = 'none';
|
|
|
|
|
if (blocker) blocker.classList.remove('active');
|
|
|
|
@@ -6882,6 +6949,50 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _annotateActive = false;
|
|
|
|
|
let _annotateTool = 'pencil'; // saved tool before entering annotate mode
|
|
|
|
|
|
|
|
|
|
async function crToggleAnnotate() {
|
|
|
|
|
if (!_simActive) return;
|
|
|
|
|
const isTeacher = _me && (_me.role === 'teacher' || _me.role === 'admin');
|
|
|
|
|
const newVal = !_annotateActive;
|
|
|
|
|
_crApplyAnnotate(newVal);
|
|
|
|
|
// Only teacher broadcasts to students
|
|
|
|
|
if (isTeacher && _sessionId) {
|
|
|
|
|
try {
|
|
|
|
|
await LS.post(`/api/classroom/${_sessionId}/sim/annotate`, { active: newVal });
|
|
|
|
|
} catch (e) { /* non-critical */ }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _crApplyAnnotate(active) {
|
|
|
|
|
_annotateActive = active;
|
|
|
|
|
const boardArea = document.getElementById('cr-board-area');
|
|
|
|
|
const simBtn = document.getElementById('cr-sim-annotate-btn');
|
|
|
|
|
const isTeacher = _me && (_me.role === 'teacher' || _me.role === 'admin');
|
|
|
|
|
|
|
|
|
|
boardArea?.classList.toggle('annotate-active', active);
|
|
|
|
|
if (simBtn) simBtn.classList.toggle('active', active);
|
|
|
|
|
|
|
|
|
|
if (!_wb) return;
|
|
|
|
|
_wb.setAnnotateMode(active);
|
|
|
|
|
if (active) {
|
|
|
|
|
// Remember current tool, switch to pencil for drawing
|
|
|
|
|
_annotateTool = _wb._currentTool || 'pencil';
|
|
|
|
|
if (isTeacher) {
|
|
|
|
|
_wb.setTool('pencil');
|
|
|
|
|
document.querySelectorAll('.cr-tool-btn').forEach(b => b.classList.remove('active'));
|
|
|
|
|
document.getElementById('cr-tool-pencil')?.classList.add('active');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Restore previous tool
|
|
|
|
|
if (isTeacher) {
|
|
|
|
|
_wb.setTool(_annotateTool);
|
|
|
|
|
document.getElementById(`cr-tool-${_annotateTool}`)?.classList.add('active');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function crSetSimMode(mode) {
|
|
|
|
|
if (!_sessionId) return;
|
|
|
|
|
try {
|
|
|
|
|