feat(materials): Фаза 3 (часть 2) — источник «Учебник»
Сервер инжектит в /textbook/<slug> плавающую кнопку «В мои материалы» (js/textbook-clip.js + material-save.js рядом с deep-link). Сохраняет текущий § как ссылку /textbook/<slug>#sec-<id> (заголовок = название §, источник = глава). Скрыта в classroom-embed и для неавторизованных. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -424,7 +424,11 @@ const EMBED_INJECT = `
|
||||
|
||||
// Always injected (plain + embed): deep-link helper so /textbook/<slug>#sec-pN
|
||||
// actually opens § N. Without it the page ignores the hash and shows §1.
|
||||
const DEEPLINK_INJECT = `\n<script defer src="/js/textbook-deeplink.js"></script>\n`;
|
||||
const DEEPLINK_INJECT = `
|
||||
<script defer src="/js/textbook-deeplink.js"></script>
|
||||
<script defer src="/js/material-save.js"></script>
|
||||
<script defer src="/js/textbook-clip.js"></script>
|
||||
`;
|
||||
|
||||
function _renderTextbook(filePath, slug, embed) {
|
||||
let stat; try { stat = fs.statSync(filePath); } catch { return null; }
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
/* textbook-clip.js — floating «В мои материалы» button on textbook pages.
|
||||
* Injected by the server into /textbook/<slug>. Saves the currently open
|
||||
* paragraph as a link (/textbook/<slug>#sec-<id>) into the student's
|
||||
* personal materials. Reuses MaterialSave (material-save.js) + LS (api.js).
|
||||
* Hidden inside the classroom embed (iframe) to avoid clutter. */
|
||||
(function () {
|
||||
if (window.parent !== window) return; // skip in classroom embed
|
||||
if (!window.LS || !LS.getToken || !LS.getToken()) return; // only for logged-in users
|
||||
|
||||
function chapterTitle() {
|
||||
return (document.title || 'Учебник').replace(/\s*[—|].*$/, '').replace(/\s*·\s*LearnSpace.*$/i, '').trim() || 'Учебник';
|
||||
}
|
||||
function sectionTitle() {
|
||||
const h = document.querySelector('.sec.active .sec-h');
|
||||
if (h && h.textContent.trim()) return h.textContent.trim();
|
||||
const n = document.querySelector('.psel-card.active .psel-name');
|
||||
if (n && n.textContent.trim()) return n.textContent.trim();
|
||||
return (document.title || 'Тема').split('·').pop().trim() || 'Тема';
|
||||
}
|
||||
function activeId() {
|
||||
const c = document.querySelector('.psel-card.active');
|
||||
return c && c.dataset ? c.dataset.id : null;
|
||||
}
|
||||
|
||||
function save(btn) {
|
||||
if (!window.MaterialSave) { if (LS.toast) LS.toast('Модуль не загружен', 'error'); return; }
|
||||
const slug = location.pathname.replace(/^\/textbook\//, '').replace(/\/+$/, '');
|
||||
if (!slug) return;
|
||||
const id = activeId();
|
||||
const hash = id ? '#sec-' + id : (location.hash || '');
|
||||
MaterialSave.link({ title: sectionTitle(), url: '/textbook/' + slug + hash, sourceTitle: chapterTitle() }, btn);
|
||||
}
|
||||
|
||||
function build() {
|
||||
if (document.getElementById('__ls_clip') || !document.body) return;
|
||||
const btn = document.createElement('button');
|
||||
btn.id = '__ls_clip';
|
||||
btn.type = 'button';
|
||||
btn.title = 'Сохранить эту тему в «Мои материалы»';
|
||||
btn.innerHTML = '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg><span>В мои материалы</span>';
|
||||
btn.style.cssText = 'position:fixed;right:18px;bottom:18px;z-index:9000;display:inline-flex;align-items:center;gap:7px;padding:10px 14px;border-radius:99px;border:none;background:#8b5cf6;color:#fff;font:600 13px/1 Inter,system-ui,sans-serif;cursor:pointer;box-shadow:0 6px 20px rgba(139,92,246,.4)';
|
||||
btn.addEventListener('click', function () { save(btn); });
|
||||
document.body.appendChild(btn);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function () { setTimeout(build, 300); });
|
||||
else setTimeout(build, 300);
|
||||
})();
|
||||
Reference in New Issue
Block a user