feat(lab-content-engine): phase 5 frontend — чип «Связано с программой»
Реальный фронт Ф5 (ранее ошибочно считал его сделанным параллельной сессией — его не было). _loadRelated(simId) в lab-glue.js: GET /api/lab/sims/:id/related, рендерит чипы-ссылки рядом с заголовком симуляции; контейнер #sim-related создаётся динамически (без правок lab.html/CSS). Вызов из openSim (lab-init.js). Тихо прячется при отсутствии связей/ошибке. Иконка — inline SVG .ic, без эмодзи. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -971,6 +971,58 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Контент-движок, Фаза 5: чип «Связано с программой» ──────────────────
|
||||||
|
Подтягивает курикулумные связи симуляции (GET /api/lab/sims/:id/related) и
|
||||||
|
рендерит чипы-ссылки рядом с заголовком симуляции. Самодостаточно: создаёт
|
||||||
|
контейнер #sim-related динамически (без правок lab.html/CSS — меньше риск
|
||||||
|
конфликта с параллельными сессиями). Тихо прячется, если связей нет/ошибка. */
|
||||||
|
var _LAB_LINK_ICON = '<svg class="ic" viewBox="0 0 24 24" style="width:13px;height:13px;vertical-align:-2px"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>';
|
||||||
|
function _labRelEsc(s) {
|
||||||
|
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
||||||
|
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function _ensureRelatedHost() {
|
||||||
|
var host = document.getElementById('sim-related');
|
||||||
|
if (host) return host;
|
||||||
|
host = document.createElement('div');
|
||||||
|
host.id = 'sim-related';
|
||||||
|
host.style.cssText = 'display:none;align-items:center;gap:6px;flex-wrap:wrap;margin-left:14px;min-width:0';
|
||||||
|
var title = document.getElementById('sim-topbar-title');
|
||||||
|
if (title && title.parentNode) title.parentNode.insertBefore(host, title.nextSibling);
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
function _loadRelated(simId) {
|
||||||
|
var host = _ensureRelatedHost();
|
||||||
|
host.style.display = 'none';
|
||||||
|
host.innerHTML = '';
|
||||||
|
if (!window.LS || !LS.api) return;
|
||||||
|
LS.api('/api/lab/sims/' + encodeURIComponent(simId) + '/related')
|
||||||
|
.then(function (data) {
|
||||||
|
var links = (data && data.links) || {};
|
||||||
|
var all = [].concat(links.textbook || [], links.topic || [], links.kmap || [], links.question || []);
|
||||||
|
if (!all.length) return;
|
||||||
|
var chipBase = 'display:inline-flex;align-items:center;gap:4px;font-size:.72rem;padding:3px 9px;border-radius:999px;';
|
||||||
|
var html = '<span style="font-size:.68rem;font-weight:700;color:var(--text-3);text-transform:uppercase;letter-spacing:.05em">'
|
||||||
|
+ _LAB_LINK_ICON + ' Связано с программой</span>';
|
||||||
|
all.forEach(function (l) {
|
||||||
|
var label = _labRelEsc(l.label || (l.kind + ':' + l.ref_id));
|
||||||
|
if (l.href) {
|
||||||
|
html += '<a href="' + _labRelEsc(l.href) + '" title="Открыть в учебнике" style="' + chipBase
|
||||||
|
+ 'background:rgba(155,93,229,.14);color:var(--violet);text-decoration:none;border:1px solid rgba(155,93,229,.32)">' + label + '</a>';
|
||||||
|
} else {
|
||||||
|
html += '<span style="' + chipBase
|
||||||
|
+ 'background:rgba(255,255,255,.06);color:var(--text-2);border:1px solid rgba(255,255,255,.12)">' + label + '</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
host.innerHTML = html;
|
||||||
|
host.style.display = 'flex';
|
||||||
|
if (window.lucide) lucide.createIcons();
|
||||||
|
})
|
||||||
|
.catch(function () { /* нет связей или ошибка — чип просто не показываем */ });
|
||||||
|
}
|
||||||
|
window._loadRelated = _loadRelated;
|
||||||
|
|
||||||
/* ── embed mode + auto-open from ?sim= ── */
|
/* ── embed mode + auto-open from ?sim= ── */
|
||||||
const _qp = new URLSearchParams(location.search);
|
const _qp = new URLSearchParams(location.search);
|
||||||
var _embedMode = _qp.get('embed') === '1';
|
var _embedMode = _qp.get('embed') === '1';
|
||||||
|
|||||||
@@ -119,6 +119,9 @@
|
|||||||
// load theory for this sim
|
// load theory for this sim
|
||||||
loadTheory(id.includes(':') ? id.split(':')[0] : id);
|
loadTheory(id.includes(':') ? id.split(':')[0] : id);
|
||||||
|
|
||||||
|
// Фаза 5: чип «Связано с программой» (курикулумные связи симуляции).
|
||||||
|
if (typeof _loadRelated === 'function') _loadRelated(id.includes(':') ? id.split(':')[0] : id);
|
||||||
|
|
||||||
// ── Контент-движок (Фаза 1): диспетчеризация через реестр ──
|
// ── Контент-движок (Фаза 1): диспетчеризация через реестр ──
|
||||||
// Все каталожные симуляции зарегистрированы в _register-all.js.
|
// Все каталожные симуляции зарегистрированы в _register-all.js.
|
||||||
// Алиасы deep-link (magnetic/coulomb/thinlens/mirrors/refraction) нормализуем
|
// Алиасы deep-link (magnetic/coulomb/thinlens/mirrors/refraction) нормализуем
|
||||||
|
|||||||
Reference in New Issue
Block a user