feat(assistant): контент по всем разделам, FAQ x5, поиск по платформе, умный проактив
Контент: контекстные подсказки на ВСЕ разделы (PAGE_HINTS), «Совет дня» (ротация), FAQ расширен ~10 -> ~50 записей по всем фичам. «Спроси Квантика» теперь ищет и по платформе (LS.globalSearch) рядом с FAQ. Умный проактив: weakSubject (слабый предмет по тестам, /api/assistant/context) -> «потренируемся» и daily-plan (план на сегодня из квестов и карточек к повторению). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,36 +11,57 @@ const db = require('../db/db');
|
||||
/* Корпус справки для «Спроси Квантика» (поиск по ключевым словам в ask()).
|
||||
* Это же место — контекст для будущей локальной модели (см. ask). Правьте свободно. */
|
||||
const FAQ = [
|
||||
{ id: 'clip-textbook', q: 'Как сохранить кусок учебника?',
|
||||
a: 'На странице учебника нажми «Вырезать область» (внизу), выдели фрагмент — он сохранится картинкой в «Мои материалы».',
|
||||
url: '/my-materials', keywords: ['учебник', 'вырезать', 'область', 'скриншот', 'фрагмент', 'сохранить', 'картинк', 'материал'] },
|
||||
{ id: 'materials-folders', q: 'Как разложить материалы по папкам?',
|
||||
a: 'В «Мои материалы» нажми «+ папка», затем у карточки выбери папку. Можно фильтровать по папкам и типам.',
|
||||
url: '/my-materials', keywords: ['папк', 'материал', 'коллекци', 'разложить', 'сортиров', 'фильтр'] },
|
||||
{ id: 'materials-annotate', q: 'Как рисовать поверх фото?',
|
||||
a: 'Открой материал-картинку и нажми кнопку с карандашом-линейкой — откроется редактор. Сохранение обновит ту же карточку.',
|
||||
url: '/my-materials', keywords: ['рисовать', 'аннотир', 'поверх', 'фото', 'карандаш', 'разметк', 'редактир'] },
|
||||
{ id: 'flashcards', q: 'Как работают флешкарты?',
|
||||
a: 'Создай колоду, добавь карточки (вопрос/ответ, можно картинку и формулы KaTeX). Система сама напомнит, что пора повторить.',
|
||||
url: '/flashcards', keywords: ['флешкарт', 'карточк', 'колод', 'повтор', 'память', 'katex', 'формул'] },
|
||||
{ id: 'exam-modes', q: 'Чем отличаются режимы экзамена?',
|
||||
a: 'Экзамен — как на ЦТ/ЦЭ, на время. Тренировка — с разбором после каждого ответа. Случайный — быстрый набор вопросов.',
|
||||
url: '/exam-prep', keywords: ['экзамен', 'режим', 'тренировк', 'случайн', 'цт', 'цэ', 'тест'] },
|
||||
{ id: 'board-tools', q: 'Что умеет доска?',
|
||||
a: 'Карандаш, маркер, лазер, фигуры, соединители, стикеры, текст, формулы KaTeX, таблицы, линейка и транспортир. «Выделение» двигает и поворачивает объекты.',
|
||||
url: '/board', keywords: ['доск', 'инструмент', 'рисов', 'фигур', 'линейк', 'маркер', 'whiteboard'] },
|
||||
{ id: 'pet', q: 'Зачем нужен питомец и XP?',
|
||||
a: 'Квантик растёт от твоей активности: за тесты, уроки и карточки идут XP и монеты. Серия за ежедневные занятия поднимает настроение и даёт бонусы.',
|
||||
url: '/pet', keywords: ['питомец', 'квантик', 'xp', 'опыт', 'монет', 'серия', 'streak', 'настроение', 'уровень'] },
|
||||
{ id: 'homework', q: 'Где мои домашние задания?',
|
||||
a: 'Все задания и дедлайны — в разделе «Домашние задания». Там же можно загрузить выполненную работу.',
|
||||
url: '/homework', keywords: ['домашк', 'домашн', 'задани', 'дедлайн', 'сдать', 'загрузить', 'работ'] },
|
||||
{ id: 'quick-lesson', q: 'Как создать один урок без курса?',
|
||||
a: 'В «Теории» нажми «Быстрый урок» — урок создастся в скрытом личном контейнере, его не видно в общем каталоге.',
|
||||
url: '/theory', keywords: ['урок', 'быстрый', 'без курса', 'создать', 'теори'] },
|
||||
{ id: 'lab', q: 'Как открыть симуляции?',
|
||||
a: 'В «Лаборатории» симуляции запускаются прямо в браузере — установка не нужна. Выбери предмет и опыт.',
|
||||
url: '/lab', keywords: ['лаборатори', 'симуляци', 'опыт', 'эксперимент', 'физик', 'хими'] },
|
||||
// ── Учебники / чтение ──
|
||||
{ id: 'clip-textbook', q: 'Как сохранить кусок учебника?', a: 'На странице учебника нажми «Вырезать область» (внизу), выдели фрагмент — он сохранится картинкой в «Мои материалы».', url: '/my-materials', keywords: ['учебник','вырезать','область','скриншот','фрагмент','сохранить','картинк'] },
|
||||
{ id: 'textbook-link', q: 'Как поделиться ссылкой на параграф?', a: 'Открой нужный параграф учебника и скопируй адрес из строки браузера — ссылка ведёт прямо на этот раздел.', url: '/textbooks', keywords: ['учебник','ссылк','параграф','поделит','раздел','deep'] },
|
||||
{ id: 'textbooks-where', q: 'Где учебники?', a: 'Раздел «Учебники» в меню. Внутри — главы и параграфы с теорией, формулами и задачами.', url: '/textbooks', keywords: ['учебник','теори','глава','параграф','книг'] },
|
||||
{ id: 'read-progress', q: 'Считается ли прогресс чтения?', a: 'Да — прочитанные параграфы отмечаются, прогресс по учебнику виден в самом учебнике и влияет на питомца.', url: '/textbooks', keywords: ['прогресс','чтени','прочит','отмет'] },
|
||||
// ── Тесты / экзамен ──
|
||||
{ id: 'exam-modes', q: 'Чем отличаются режимы экзамена?', a: 'Экзамен — как на ЦТ/ЦЭ, на время. Тренировка — с разбором после каждого ответа. Случайный — быстрый набор вопросов.', url: '/exam-prep/math9', keywords: ['экзамен','режим','тренировк','случайн','цт','цэ','тест'] },
|
||||
{ id: 'exam-start', q: 'Как начать тест?', a: 'Зайди в «Подготовка к экзамену», выбери тему и режим. Можно быстро стартовать тест и с дашборда.', url: '/exam-prep/math9', keywords: ['тест','начать','старт','экзамен','реши'] },
|
||||
{ id: 'exam-review', q: 'Как разобрать ошибки после теста?', a: 'После завершения теста доступен разбор: правильные ответы и решения по каждому заданию. В режиме тренировки разбор идёт сразу.', url: '/exam-prep/math9', keywords: ['ошибк','разбор','решени','ответ','проверк'] },
|
||||
{ id: 'results-where', q: 'Где мои результаты?', a: 'Последние результаты — на дашборде, история и проценты по предметам — там же в виджетах.', url: '/dashboard', keywords: ['результат','оценк','процент','истори','статистик'] },
|
||||
// ── Флешкарты ──
|
||||
{ id: 'flashcards', q: 'Как работают флешкарты?', a: 'Создай колоду, добавь карточки (вопрос/ответ, можно картинку и формулы KaTeX). Система сама напомнит, что пора повторить.', url: '/flashcards', keywords: ['флешкарт','карточк','колод','повтор','память'] },
|
||||
{ id: 'flashcards-katex', q: 'Как вставить формулу в карточку?', a: 'В редакторе карточки есть палитра KaTeX — выбирай символы или пиши LaTeX, превью покажет результат.', url: '/flashcards', keywords: ['формул','katex','latex','карточк','математ'] },
|
||||
{ id: 'flashcards-img', q: 'Можно ли картинку на карточке?', a: 'Да — при создании карточки загрузи изображение, оно отрендерится на лицевой или оборотной стороне.', url: '/flashcards', keywords: ['картинк','изображен','карточк','фото'] },
|
||||
{ id: 'flashcards-fab', q: 'Что за кнопка «Запомнить» в углу?', a: 'Это быстрый способ создать карточку из любого места платформы — плавающая кнопка справа внизу.', url: '/flashcards', keywords: ['запомнить','кнопк','быстр','карточк','fab'] },
|
||||
// ── Мои материалы ──
|
||||
{ id: 'materials-where', q: 'Что такое «Мои материалы»?', a: 'Личное хранилище: вырезки из учебника, страницы доски, заметки и рисунки. Хранятся у тебя, даже если урок удалят.', url: '/my-materials', keywords: ['материал','хранилищ','заметк','доск','вырезк'] },
|
||||
{ id: 'materials-folders', q: 'Как разложить материалы по папкам?', a: 'В «Мои материалы» нажми «+ папка», затем у карточки выбери папку. Можно фильтровать по папкам и типам.', url: '/my-materials', keywords: ['папк','материал','коллекци','разложить','сортиров','фильтр'] },
|
||||
{ id: 'materials-annotate', q: 'Как рисовать поверх фото?', a: 'Открой материал-картинку и нажми кнопку с карандашом-линейкой — откроется редактор. Сохранение обновит ту же карточку.', url: '/my-materials', keywords: ['рисовать','аннотир','поверх','фото','карандаш','разметк'] },
|
||||
{ id: 'materials-note', q: 'Как создать заметку?', a: 'В «Мои материалы» кнопка «Заметка» вверху. Заметку потом можно превратить во флешкарту.', url: '/my-materials', keywords: ['заметк','создать','текст','note'] },
|
||||
{ id: 'materials-view', q: 'Почему картинка открывается в окне?', a: 'Клик по материалу-картинке открывает её в просмотрщике на странице. Там же «Скачать» и «В новой вкладке».', url: '/my-materials', keywords: ['картинк','открыт','просмотр','окно','лайтбокс'] },
|
||||
// ── Доска / онлайн-урок ──
|
||||
{ id: 'board-tools', q: 'Что умеет доска?', a: 'Карандаш, маркер, лазер, фигуры, соединители, стикеры, текст, формулы KaTeX, таблицы, линейка и транспортир. «Выделение» двигает и поворачивает объекты.', url: '/board', keywords: ['доск','инструмент','рисов','фигур','линейк','маркер','whiteboard'] },
|
||||
{ id: 'board-save', q: 'Как сохранить доску себе?', a: 'На уроке или в архиве нажми «К себе» — страница доски сохранится в «Мои материалы». Можно сохранить и выделенную область.', url: '/my-materials', keywords: ['доск','сохранить','к себе','область','материал'] },
|
||||
{ id: 'classroom-join', q: 'Как попасть на онлайн-урок?', a: 'Когда учитель начинает урок, в меню «Онлайн-урок» появится приглашение — нажми, чтобы войти.', url: '/classroom', keywords: ['онлайн','урок','classroom','подключ','войти','вызов'] },
|
||||
{ id: 'classroom-materials', q: 'Сохранятся ли заметки с онлайн-урока?', a: 'Да — доску и заметки можно сохранить в «Мои материалы», они переживут окончание сессии.', url: '/my-materials', keywords: ['онлайн','урок','заметк','сохран','материал'] },
|
||||
// ── Теория / уроки ──
|
||||
{ id: 'theory', q: 'Что в разделе «Теория»?', a: 'Курсы и уроки с теорией и заданиями. Прогресс по урокам отслеживается.', url: '/theory', keywords: ['теори','курс','урок','обучен'] },
|
||||
{ id: 'quick-lesson', q: 'Как создать один урок без курса?', a: 'В «Теории» нажми «Быстрый урок» — урок создастся в скрытом личном контейнере, его не видно в общем каталоге.', url: '/theory', keywords: ['урок','быстрый','без курса','создать','теори'] },
|
||||
{ id: 'continue-lesson', q: 'Как продолжить с того же места?', a: 'На дашборде есть «Продолжить» с последним незаконченным уроком. Я тоже подскажу, когда есть что продолжить.', url: '/dashboard', keywords: ['продолж','урок','место','незаконч'] },
|
||||
// ── Лаборатория / игры ──
|
||||
{ id: 'lab', q: 'Как открыть симуляции?', a: 'В «Лаборатории» симуляции запускаются прямо в браузере — установка не нужна. Выбери предмет и опыт.', url: '/lab', keywords: ['лаборатори','симуляци','опыт','эксперимент','физик','хими'] },
|
||||
{ id: 'optics', q: 'Есть ли конструктор оптики?', a: 'Да — в оптической скамье есть режим «Конструктор»: собирай системы из линз, зеркал и призм.', url: '/lab', keywords: ['оптик','линз','призм','конструктор','скамья'] },
|
||||
{ id: 'games', q: 'Какие есть игры?', a: 'Кроссворд и «Виселица» по терминам предметов — закрепляют слова и понятия, дают XP.', url: '/crossword', keywords: ['игр','кроссворд','виселиц','термин','слов'] },
|
||||
// ── Питомец / геймификация ──
|
||||
{ id: 'pet', q: 'Зачем нужен питомец и XP?', a: 'Квантик растёт от твоей активности: за тесты, уроки и карточки идут XP и монеты. Серия за ежедневные занятия поднимает настроение и даёт бонусы.', url: '/pet', keywords: ['питомец','квантик','xp','опыт','монет','серия','streak','уровень'] },
|
||||
{ id: 'streak', q: 'Что такое серия (streak)?', a: 'Серия — сколько дней подряд ты занимаешься. Чем длиннее, тем счастливее питомец и больше бонусов. Пропуск дня обнуляет серию.', url: '/pet', keywords: ['серия','streak','подряд','дни','бонус'] },
|
||||
{ id: 'coins', q: 'Где потратить монеты?', a: 'В магазине на странице питомца — например, на фоны для Квантика.', url: '/pet', keywords: ['монет','магазин','купить','фон','награда'] },
|
||||
{ id: 'achievements', q: 'Где достижения?', a: 'В профиле есть вкладка «Достижения» — ачивки за активность, серии и результаты.', url: '/profile', keywords: ['достижени','ачивк','награда','бейдж'] },
|
||||
// ── Домашка / классы ──
|
||||
{ id: 'homework', q: 'Где мои домашние задания?', a: 'Все задания и дедлайны — в разделе «Домашние задания». Там же можно загрузить выполненную работу.', url: '/homework', keywords: ['домашк','домашн','задани','дедлайн','сдать','загрузить'] },
|
||||
{ id: 'join-class', q: 'Как вступить в класс?', a: 'Кнопка «Вступить в класс» в меню — введи код от учителя, и задания класса появятся у тебя.', url: '/dashboard', keywords: ['класс','вступить','код','присоедин'] },
|
||||
// ── Профиль / настройки / поиск ──
|
||||
{ id: 'search', q: 'Как искать по платформе?', a: 'Нажми «Поиск» в меню или Ctrl+K — найдёшь уроки, курсы, файлы и вопросы. Спроси меня — я тоже поищу.', url: null, keywords: ['поиск','найти','search','ctrl'] },
|
||||
{ id: 'theme', q: 'Как включить тёмную тему?', a: 'В профиле → «Настройки» → «Внешний вид». Там же звуки и анимации.', url: '/profile', keywords: ['тема','тёмн','dark','внешний','настройк'] },
|
||||
{ id: 'sounds', q: 'Как отключить звуки?', a: 'Профиль → «Настройки» → «Звуки системы»: общий выключатель, громкость и категории.', url: '/profile', keywords: ['звук','выключ','громкост','sound'] },
|
||||
{ id: 'parent', q: 'Как дать доступ родителю?', a: 'В профиле есть «Доступ для родителей» — создай ссылку, по ней родитель видит твой прогресс.', url: '/profile', keywords: ['родител','доступ','ссылк','контрол'] },
|
||||
{ id: 'bookmarks', q: 'Где мои закладки?', a: 'В профиле вкладка «Закладки» — сохранённые параграфы и страницы.', url: '/profile', keywords: ['закладк','сохран','избранн'] },
|
||||
// ── Сам помощник ──
|
||||
{ id: 'assistant-off', q: 'Как выключить помощника?', a: 'Профиль → «Настройки» → «Помощник Квантик». Можно выключить совсем или скрыть конкретные подсказки кнопкой «Не показывать».', url: '/profile', keywords: ['помощник','выключ','ассистент','подсказк','квантик','скрыть'] },
|
||||
{ id: 'assistant-tour', q: 'Как пройти тур заново?', a: 'Нажми на меня и выбери «Тур по системе» — проведу по разделам ещё раз.', url: null, keywords: ['тур','онбординг','обзор','заново'] },
|
||||
];
|
||||
|
||||
/* ── Источники проактивных подсказок (всё уже есть в БД) ──────────────── */
|
||||
@@ -101,6 +122,21 @@ function activeLesson(uid, role) {
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
|
||||
function weakSubject(uid) {
|
||||
// Предмет с наименьшим средним по завершённым тестам (минимум 2 теста).
|
||||
try {
|
||||
const row = db.prepare(`
|
||||
SELECT s.slug AS slug, s.name AS name,
|
||||
ROUND(AVG(ts.score * 100.0 / ts.total)) AS avg, COUNT(*) AS n
|
||||
FROM test_sessions ts JOIN subjects s ON s.id = ts.subject_id
|
||||
WHERE ts.user_id = ? AND ts.status = 'completed' AND ts.total > 0
|
||||
GROUP BY ts.subject_id HAVING n >= 2
|
||||
ORDER BY avg ASC LIMIT 1
|
||||
`).get(uid);
|
||||
return (row && row.avg != null && row.avg < 70) ? { slug: row.slug, name: row.name, avg: row.avg } : null;
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
|
||||
/* ── GET /api/assistant/context ───────────────────────────────────────── */
|
||||
function getContext(req, res) {
|
||||
const uid = req.user.id;
|
||||
@@ -118,6 +154,7 @@ function getContext(req, res) {
|
||||
dueCards: dueCardsCount(uid),
|
||||
homework: pendingHomework(uid),
|
||||
activeLesson: activeLesson(uid, req.user.role),
|
||||
weakSubject: weakSubject(uid),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+124
-34
@@ -34,6 +34,21 @@
|
||||
if (p === '/my-materials') return 'materials';
|
||||
if (p === '/lab') return 'lab';
|
||||
if (p === '/theory' || p.indexOf('/course') === 0 || p.indexOf('/lesson') === 0) return 'theory';
|
||||
if (p === '/textbooks') return 'textbooks';
|
||||
if (p === '/library') return 'library';
|
||||
if (p === '/knowledge-map') return 'knowledge';
|
||||
if (p === '/biochem') return 'biochem';
|
||||
if (p === '/red-book') return 'redbook';
|
||||
if (p === '/crossword') return 'crossword';
|
||||
if (p === '/hangman') return 'hangman';
|
||||
if (p === '/collection') return 'collection';
|
||||
if (p === '/analytics') return 'analytics';
|
||||
if (p === '/gradebook') return 'gradebook';
|
||||
if (p === '/lesson-history') return 'lessonhistory';
|
||||
if (p === '/question-bank') return 'qbank';
|
||||
if (p === '/classes') return 'classes';
|
||||
if (p === '/homework') return 'homework';
|
||||
if (p === '/pet') return 'pet';
|
||||
return 'other';
|
||||
}
|
||||
var PAGE = pageId();
|
||||
@@ -56,32 +71,7 @@
|
||||
/* ── каталог правил ──────────────────────────────────────────────────── */
|
||||
// scope: page | proactive | celebration. when(C) → bool. action(C) → {label,url}|null.
|
||||
var RULES = [
|
||||
// — контекстные —
|
||||
{ id: 'p-textbook', scope: 'page', cooldownDays: 14, maxShows: 2,
|
||||
when: function () { return PAGE === 'textbook'; },
|
||||
text: function () { return 'Любой кусок страницы можно вырезать картинкой в «Мои материалы» — кнопка «Вырезать область» внизу.'; },
|
||||
action: function () { return null; } },
|
||||
{ id: 'p-exam', scope: 'page', cooldownDays: 14, maxShows: 2,
|
||||
when: function () { return PAGE === 'exam'; },
|
||||
text: function () { return 'Три режима: экзамен (как на ЦТ/ЦЭ), тренировка (с разбором) и случайный. Выбирай под свою цель.'; },
|
||||
action: function () { return null; } },
|
||||
{ id: 'p-flashcards', scope: 'page', cooldownDays: 14, maxShows: 2,
|
||||
when: function () { return PAGE === 'flashcards'; },
|
||||
text: function () { return 'Формулы в карточках вводятся через KaTeX-палитру, а ещё можно добавить картинку.'; },
|
||||
action: function () { return null; } },
|
||||
{ id: 'p-materials', scope: 'page', cooldownDays: 14, maxShows: 2,
|
||||
when: function () { return PAGE === 'materials'; },
|
||||
text: function () { return 'Раскладывай материалы по папкам, а поверх фото можно рисовать — кнопка с карандашом.'; },
|
||||
action: function () { return null; } },
|
||||
{ id: 'p-lab', scope: 'page', cooldownDays: 14, maxShows: 2,
|
||||
when: function () { return PAGE === 'lab'; },
|
||||
text: function () { return 'Симуляции запускаются прямо в браузере — ничего ставить не нужно.'; },
|
||||
action: function () { return null; } },
|
||||
{ id: 'p-dashboard', scope: 'page', cooldownDays: 30, maxShows: 1,
|
||||
when: function () { return PAGE === 'dashboard'; },
|
||||
text: function () { return 'Виджеты на дашборде можно включать и переставлять под себя.'; },
|
||||
action: function () { return null; } },
|
||||
|
||||
// — контекстные подсказки генерируются из PAGE_HINTS ниже —
|
||||
// — проактивные (из реальных данных) —
|
||||
{ id: 'hw-overdue', scope: 'proactive', cooldownDays: 1, maxShows: 30,
|
||||
when: function () { return !!(SRV && SRV.homework && SRV.homework.overdue); },
|
||||
@@ -102,13 +92,34 @@
|
||||
{ id: 'streak-risk', scope: 'proactive', cooldownDays: 1, maxShows: 60,
|
||||
when: function () { return !!(PET && PET.streakCurrent >= 1 && !activeToday() && new Date().getHours() >= 18); },
|
||||
text: function () { return 'Серия ' + PET.streakCurrent + ' ' + plural(PET.streakCurrent, 'день', 'дня', 'дней') + ' под угрозой — позанимайся сегодня, чтобы не потерять.'; },
|
||||
action: function () { return { label: 'Заниматься', url: '/exam-prep' }; } },
|
||||
action: function () { return { label: 'Заниматься', url: '/exam-prep/math9' }; } },
|
||||
{ id: 'quest', scope: 'proactive', cooldownDays: 1, maxShows: 90,
|
||||
when: function () { return !!(quest(true) && new Date().getHours() >= 16); },
|
||||
text: function () { var q = quest(true); return 'Остался квест дня: «' + (q.label || 'задание') + '».'; },
|
||||
action: function () { return null; } },
|
||||
{ id: 'weak-subject', scope: 'proactive', cooldownDays: 2, maxShows: 40,
|
||||
when: function () { return !!(SRV && SRV.weakSubject); },
|
||||
text: function () { var w = SRV.weakSubject; return 'Слабее всего идёт ' + (w.name || 'предмет') + ' (' + w.avg + '%). Потренируемся?'; },
|
||||
action: function () { return { label: 'Потренироваться', url: '/exam-prep/math9' }; } },
|
||||
{ id: 'daily-plan', scope: 'proactive', cooldownDays: 1, maxShows: 120,
|
||||
when: function () { return PAGE === 'dashboard' && dailyPlan().length > 0; },
|
||||
text: function () { return 'План на сегодня: ' + dailyPlan().join(', ') + '. Начнём?'; },
|
||||
action: function () { var p = dailyPlanAction(); return p; } },
|
||||
];
|
||||
|
||||
/* План на сегодня — из дневных квестов и карточек к повторению */
|
||||
function dailyPlan() {
|
||||
var out = [];
|
||||
var qs = (PET && PET.quests) || [];
|
||||
qs.forEach(function (q) { if (!q.done && q.label) out.push(q.label.toLowerCase()); });
|
||||
if (SRV && SRV.dueCards > 0) out.push('повторить ' + SRV.dueCards + ' ' + plural(SRV.dueCards, 'карточку', 'карточки', 'карточек'));
|
||||
return out.slice(0, 3);
|
||||
}
|
||||
function dailyPlanAction() {
|
||||
if (SRV && SRV.dueCards > 0) return { label: 'Повторить карточки', url: '/flashcards' };
|
||||
return { label: 'К занятиям', url: '/exam-prep/math9' };
|
||||
}
|
||||
|
||||
function plural(n, one, few, many) {
|
||||
var m10 = n % 10, m100 = n % 100;
|
||||
if (m10 === 1 && m100 !== 11) return one;
|
||||
@@ -116,6 +127,69 @@
|
||||
return many;
|
||||
}
|
||||
|
||||
/* ── контентные подсказки по страницам (генерируются в RULES) ────────── */
|
||||
var PAGE_HINTS = {
|
||||
textbook: ['Любой кусок страницы можно вырезать картинкой в «Мои материалы» — кнопка «Вырезать область» внизу.',
|
||||
'Ссылка из адресной строки ведёт прямо на этот параграф — удобно делиться.'],
|
||||
exam: ['Три режима: экзамен (как на ЦТ/ЦЭ), тренировка (с разбором) и случайный.',
|
||||
'После теста доступен разбор: правильные ответы и решения.'],
|
||||
flashcards: ['Формулы вводятся через KaTeX-палитру, можно добавить и картинку.',
|
||||
'Система сама напоминает, какие карточки пора повторить.'],
|
||||
materials: ['Раскладывай материалы по папкам, а поверх фото можно рисовать — кнопка с карандашом.',
|
||||
'Клик по картинке открывает её в просмотрщике прямо на странице.'],
|
||||
lab: ['Симуляции запускаются прямо в браузере — ничего ставить не нужно.',
|
||||
'У оптической скамьи есть режим «Конструктор» — собирай системы из линз и зеркал.'],
|
||||
dashboard: ['Виджеты на дашборде можно включать и переставлять под себя.',
|
||||
'Блок «Активность» показывает серию и занятия за недели.'],
|
||||
textbooks: ['Внутри учебника — главы и параграфы; прочитанное отмечается и идёт в прогресс.'],
|
||||
library: ['В библиотеке — файлы и материалы от учителя; ищи по предмету.'],
|
||||
knowledge: ['Карта знаний показывает связи тем — видно, что уже освоено.'],
|
||||
biochem: ['Интерактивные модели молекул и реакций — крути и разбирай.'],
|
||||
redbook: ['Красная книга — карточки видов; листай и запоминай.'],
|
||||
crossword: ['Кроссворд по терминам предмета — отгадывай и получай XP.'],
|
||||
hangman: ['«Виселица» по терминам — угадывай слово по буквам.'],
|
||||
collection: ['Коллекция — твои собранные награды и предметы.'],
|
||||
board: ['Инструмент «выделение» двигает и поворачивает объекты; есть фигуры, формулы и линейка.',
|
||||
'Страницу доски можно сохранить в «Мои материалы».'],
|
||||
lessonhistory: ['Архив онлайн-уроков — доска и заметки сохраняются после занятия.'],
|
||||
qbank: ['Банк вопросов — основа для тестов и заданий.'],
|
||||
theory: ['Можно создать одиночный «Быстрый урок» без курса.',
|
||||
'Прогресс по урокам отслеживается — продолжай с того же места.'],
|
||||
classes: ['Создавай классы и выдавай задания; ученики входят по коду.'],
|
||||
analytics: ['Аналитика: динамика результатов и слабые темы.'],
|
||||
gradebook: ['Журнал — оценки и сдачи по заданиям класса.'],
|
||||
homework: ['Здесь все задания и дедлайны; работу можно загрузить прямо тут.'],
|
||||
pet: ['Гладь и корми Квантика, закрывай квесты — за это XP и монеты.'],
|
||||
};
|
||||
Object.keys(PAGE_HINTS).forEach(function (pg) {
|
||||
PAGE_HINTS[pg].forEach(function (text, idx) {
|
||||
RULES.push({
|
||||
id: 'p-' + pg + '-' + idx, scope: 'page', cooldownDays: 12, maxShows: 2,
|
||||
when: (function (p) { return function () { return PAGE === p; }; })(pg),
|
||||
text: (function (t) { return function () { return t; }; })(text),
|
||||
action: function () { return null; },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/* ── «Совет дня» — ротация фич-открытий (дашборд) ────────────────────── */
|
||||
var TIPS = [
|
||||
'фрагмент учебника можно сохранить картинкой в «Мои материалы».',
|
||||
'в «Теории» есть «Быстрый урок» — один урок без создания курса.',
|
||||
'поверх сохранённого фото можно рисовать пометки.',
|
||||
'формулы в карточках и на доске вводятся через KaTeX.',
|
||||
'серия за ежедневные занятия делает Квантика счастливее.',
|
||||
'поиск по платформе — Ctrl+K: уроки, курсы, файлы и вопросы.',
|
||||
'заметку из «Мои материалы» можно превратить во флешкарту.',
|
||||
'тёмную тему и звуки включишь в Профиль → Настройки.',
|
||||
];
|
||||
RULES.push({
|
||||
id: 'tip-daily', scope: 'page', cooldownDays: 1, maxShows: 200,
|
||||
when: function () { return PAGE === 'dashboard'; },
|
||||
text: function () { return 'А ты знал: ' + TIPS[(new Date().getDate()) % TIPS.length]; },
|
||||
action: function () { return null; },
|
||||
});
|
||||
|
||||
/* ── выбор подсказки ─────────────────────────────────────────────────── */
|
||||
function eligible(rule) {
|
||||
if (SUPPRESS_PAGE && rule.scope !== 'celebration') return false;
|
||||
@@ -217,6 +291,8 @@
|
||||
'.asst-ans:first-of-type{border-top:none;}',
|
||||
'.asst-ans-q{font-weight:700;color:#0F172A;margin-bottom:2px;}',
|
||||
'.asst-ans-link{display:inline-block;margin-top:4px;color:#9B5DE5;font-weight:700;font-size:.78rem;text-decoration:none;}',
|
||||
'.asst-ans-sec{font-size:.66rem;font-weight:800;color:#8a94a6;text-transform:uppercase;letter-spacing:.03em;margin:12px 0 2px;}',
|
||||
'.asst-ans-box{max-height:46vh;overflow:auto;}',
|
||||
'.asst-empty{font-size:.82rem;color:#8a94a6;padding:6px 0;}',
|
||||
// на мобиле сайдбар — выезжающая шторка, контент во всю ширину → к левому краю
|
||||
'@media(max-width:768px){.asst-root,.app-layout ~ .asst-root,.app-layout.sb-collapsed ~ .asst-root{left:12px;bottom:18px;}.asst-fab{width:48px;height:48px;}}',
|
||||
@@ -291,13 +367,27 @@
|
||||
q = (q || '').trim();
|
||||
if (q.length < 3) { box.innerHTML = ''; return; }
|
||||
box.innerHTML = '<div class="asst-empty">Ищу…</div>';
|
||||
LS.assistantAsk(q).then(function (r) {
|
||||
var ans = (r && r.answers) || [];
|
||||
if (!ans.length) { box.innerHTML = '<div class="asst-empty">Не нашёл точного ответа. Попробуй переформулировать.</div>'; return; }
|
||||
box.innerHTML = ans.map(function (a) {
|
||||
return '<div class="asst-ans"><div class="asst-ans-q">' + esc(a.q) + '</div>' + esc(a.a) +
|
||||
(a.url ? '<br><a class="asst-ans-link" href="' + esc(a.url) + '">Открыть</a>' : '') + '</div>';
|
||||
}).join('');
|
||||
Promise.all([
|
||||
LS.assistantAsk(q).catch(function () { return { answers: [] }; }),
|
||||
(LS.globalSearch ? LS.globalSearch(q, 'all', 4) : Promise.resolve({ results: [] })).catch(function () { return { results: [] }; }),
|
||||
]).then(function (res) {
|
||||
var ans = (res[0] && res[0].answers) || [];
|
||||
var found = (res[1] && res[1].results) || [];
|
||||
var html = '';
|
||||
if (ans.length) {
|
||||
html += ans.map(function (a) {
|
||||
return '<div class="asst-ans"><div class="asst-ans-q">' + esc(a.q) + '</div>' + esc(a.a) +
|
||||
(a.url ? '<br><a class="asst-ans-link" href="' + esc(a.url) + '">Открыть</a>' : '') + '</div>';
|
||||
}).join('');
|
||||
}
|
||||
if (found.length) {
|
||||
html += '<div class="asst-ans-sec">На платформе</div>';
|
||||
html += found.slice(0, 4).map(function (f) {
|
||||
return '<div class="asst-ans"><a class="asst-ans-link" style="margin-top:0" href="' + esc(f.url || '#') + '">' + esc(f.title || 'Без названия') + '</a>' +
|
||||
(f.subtitle ? ' <span style="color:#8a94a6">— ' + esc(f.subtitle) + '</span>' : '') + '</div>';
|
||||
}).join('');
|
||||
}
|
||||
box.innerHTML = html || '<div class="asst-empty">Ничего не нашёл. Попробуй переформулировать.</div>';
|
||||
}).catch(function () { box.innerHTML = '<div class="asst-empty">Не удалось получить ответ.</div>'; });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user