From 6b0d5563477ac4b6189e2e03fa85563fa2803b32 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Sat, 30 May 2026 17:18:06 +0300 Subject: [PATCH] =?UTF-8?q?feat(lab-content-engine):=20phase=205=20fronten?= =?UTF-8?q?d=20=E2=80=94=20=D1=87=D0=B8=D0=BF=20=C2=AB=D0=A1=D0=B2=D1=8F?= =?UTF-8?q?=D0=B7=D0=B0=D0=BD=D0=BE=20=D1=81=20=D0=BF=D1=80=D0=BE=D0=B3?= =?UTF-8?q?=D1=80=D0=B0=D0=BC=D0=BC=D0=BE=D0=B9=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реальный фронт Ф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) --- frontend/js/labs/lab-glue.js | 52 ++++++++++++++++++++++++++++++++++++ frontend/js/labs/lab-init.js | 3 +++ 2 files changed, 55 insertions(+) diff --git a/frontend/js/labs/lab-glue.js b/frontend/js/labs/lab-glue.js index ef572a9..3317a05 100644 --- a/frontend/js/labs/lab-glue.js +++ b/frontend/js/labs/lab-glue.js @@ -971,6 +971,58 @@ }); } + /* ── Контент-движок, Фаза 5: чип «Связано с программой» ────────────────── + Подтягивает курикулумные связи симуляции (GET /api/lab/sims/:id/related) и + рендерит чипы-ссылки рядом с заголовком симуляции. Самодостаточно: создаёт + контейнер #sim-related динамически (без правок lab.html/CSS — меньше риск + конфликта с параллельными сессиями). Тихо прячется, если связей нет/ошибка. */ + var _LAB_LINK_ICON = ''; + 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 = '' + + _LAB_LINK_ICON + ' Связано с программой'; + all.forEach(function (l) { + var label = _labRelEsc(l.label || (l.kind + ':' + l.ref_id)); + if (l.href) { + html += '' + label + ''; + } else { + html += '' + label + ''; + } + }); + host.innerHTML = html; + host.style.display = 'flex'; + if (window.lucide) lucide.createIcons(); + }) + .catch(function () { /* нет связей или ошибка — чип просто не показываем */ }); + } + window._loadRelated = _loadRelated; + /* ── embed mode + auto-open from ?sim= ── */ const _qp = new URLSearchParams(location.search); var _embedMode = _qp.get('embed') === '1'; diff --git a/frontend/js/labs/lab-init.js b/frontend/js/labs/lab-init.js index 3e9d5d1..b48303a 100644 --- a/frontend/js/labs/lab-init.js +++ b/frontend/js/labs/lab-init.js @@ -119,6 +119,9 @@ // load theory for this sim loadTheory(id.includes(':') ? id.split(':')[0] : id); + // Фаза 5: чип «Связано с программой» (курикулумные связи симуляции). + if (typeof _loadRelated === 'function') _loadRelated(id.includes(':') ? id.split(':')[0] : id); + // ── Контент-движок (Фаза 1): диспетчеризация через реестр ── // Все каталожные симуляции зарегистрированы в _register-all.js. // Алиасы deep-link (magnetic/coulomb/thinlens/mirrors/refraction) нормализуем