feat(sim-builder): фаза 7 — custom-sim на доске онлайн-урока (синхрон параметров классу, аннотации)
This commit is contained in:
+26
-5
@@ -7056,14 +7056,32 @@
|
||||
const CAT_LABELS = { math:'Математика', phys:'Физика', chem:'Химия', bio:'Биология', game:'Игра' };
|
||||
|
||||
let _simPickerCat = 'all'; // active filter in picker
|
||||
// Конструктор симуляций (Фаза 7): свои + published custom-симуляции для доски.
|
||||
let _crCustomSims = null; // [{ id, cat, title, _custom:true }] — кэш списка
|
||||
|
||||
function crOpenSimPicker() {
|
||||
async function _crLoadCustomSims() {
|
||||
if (_crCustomSims) return _crCustomSims;
|
||||
try {
|
||||
const data = await LS.customSimsList();
|
||||
const rows = (data && data.sims) || [];
|
||||
_crCustomSims = rows.map(s => ({
|
||||
id: 'custom:' + s.id,
|
||||
cat: s.cat || 'phys',
|
||||
title: s.title || ('Симуляция #' + s.id),
|
||||
_custom: true,
|
||||
}));
|
||||
} catch (e) { _crCustomSims = []; }
|
||||
return _crCustomSims;
|
||||
}
|
||||
|
||||
async function crOpenSimPicker() {
|
||||
if (_simActive) {
|
||||
// If sim already open — clicking "Симуляция" closes it (teacher action)
|
||||
crTeacherCloseSim();
|
||||
return;
|
||||
}
|
||||
_simPickerCat = 'all';
|
||||
await _crLoadCustomSims();
|
||||
_crRenderSimGrid('all');
|
||||
const overlay = document.getElementById('cr-sim-picker-overlay');
|
||||
overlay.classList.add('open');
|
||||
@@ -7084,11 +7102,14 @@
|
||||
|
||||
function _crRenderSimGrid(cat) {
|
||||
const grid = document.getElementById('cr-sim-picker-grid');
|
||||
const sims = cat === 'all' ? CR_SIMS : CR_SIMS.filter(s => s.cat === cat);
|
||||
// Конструктор симуляций (Фаза 7): встроенные + свои/published custom-sims.
|
||||
const all = CR_SIMS.concat(_crCustomSims || []);
|
||||
const sims = cat === 'all' ? all : all.filter(s => s.cat === cat);
|
||||
const esc = v => String(v == null ? '' : v).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
grid.innerHTML = sims.map(s => `
|
||||
<div class="cr-sim-picker-card" onclick="crPickSim('${s.id}','${s.title.replace(/'/g,'\\\'')}')" title="${s.title}">
|
||||
<span class="cr-sim-picker-card-cat ${s.cat}">${CAT_LABELS[s.cat] || s.cat}</span>
|
||||
<span class="cr-sim-picker-card-title">${s.title}</span>
|
||||
<div class="cr-sim-picker-card" onclick="crPickSim('${String(s.id).replace(/'/g,"\\'")}','${esc(s.title).replace(/'/g,"\\'")}')" title="${esc(s.title)}">
|
||||
<span class="cr-sim-picker-card-cat ${s.cat}">${s._custom ? 'Моя' : (CAT_LABELS[s.cat] || s.cat)}</span>
|
||||
<span class="cr-sim-picker-card-title">${esc(s.title)}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
@@ -395,6 +395,45 @@ const SIMS = [
|
||||
_lastEmittedState = null;
|
||||
}
|
||||
|
||||
/* Конструктор симуляций (Фаза 7): подключить custom-sim (SimEngine-инстанс через
|
||||
адаптерный манифест real.instance()) к тому же мосту sim_state/apply_sim_state,
|
||||
что и встроенные. Состояние = { params, running } — параметры слайдеров +
|
||||
признак воспроизведения. applyState проигрывает их у ученика через setParam/
|
||||
play/pause (время жёстко не синхронится — параметры и play/pause достаточны).
|
||||
Регистрируем под ключом _autoSim ('custom:<dbid>'), т.к. обработчик
|
||||
apply_sim_state у ученика берёт _simStateRegistry[_autoSim]. */
|
||||
function _bridgeCustomSimState(real) {
|
||||
if (!_embedMode || !real || typeof real.instance !== 'function') return;
|
||||
var key = _autoSim;
|
||||
if (!key || _simStateRegistry[key]) return; // уже подключено
|
||||
function getState() {
|
||||
var inst = real.instance();
|
||||
if (!inst || !inst.params) return null;
|
||||
var p = {};
|
||||
for (var k in inst.params) {
|
||||
if (Object.prototype.hasOwnProperty.call(inst.params, k)) {
|
||||
var v = inst.params[k];
|
||||
if (typeof v === 'number' && isFinite(v)) p[k] = v;
|
||||
}
|
||||
}
|
||||
return { params: p, running: !!(inst.isRunning && inst.isRunning()) };
|
||||
}
|
||||
function applyState(st) {
|
||||
var inst = real.instance();
|
||||
if (!inst || !st) return;
|
||||
if (st.params) {
|
||||
for (var k in st.params) {
|
||||
if (Object.prototype.hasOwnProperty.call(st.params, k)) inst.setParam(k, st.params[k]);
|
||||
}
|
||||
}
|
||||
var run = !!st.running, isRun = !!(inst.isRunning && inst.isRunning());
|
||||
if (run && !isRun && inst.play) inst.play();
|
||||
else if (!run && isRun && inst.pause) inst.pause();
|
||||
}
|
||||
_registerSimState(key, getState, applyState);
|
||||
_startStateEmit(key);
|
||||
}
|
||||
|
||||
// Receive apply_sim_state from parent (students)
|
||||
window.addEventListener('message', e => {
|
||||
if (!_embedMode) return;
|
||||
@@ -683,7 +722,14 @@ const SIMS = [
|
||||
real._custom = true;
|
||||
real._customId = dbid;
|
||||
if (window.LabRegistry) window.LabRegistry.setActive(real);
|
||||
return real.open(ctx);
|
||||
var _r = real.open(ctx);
|
||||
// Конструктор симуляций (Фаза 7): синхрон параметров/play на доске
|
||||
// онлайн-урока. В embed подключаем custom-sim к общему мосту
|
||||
// sim_state/apply_sim_state — тем же каналом, что и встроенные.
|
||||
// Ключ — исходный _autoSim ('custom:<dbid>'), т.к. apply_sim_state
|
||||
// у ученика берёт _simStateRegistry[_autoSim].
|
||||
try { _bridgeCustomSimState(real); } catch (e) {}
|
||||
return _r;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user