From cd9f2d5efa26a325ca24cdf3c184d0f49cfb0710 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 12 Jun 2026 23:19:43 +0300 Subject: [PATCH] =?UTF-8?q?feat(flashcards):=20=D0=BA=D0=BB=D0=B8=D0=BA-?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F-=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=BC=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=BE=20=D0=B4=D1=83=D0=B1=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=B5+=D0=BF=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Карточка по умолчанию показывает ОТРИСОВАННЫЙ текст (формулы KaTeX красиво), клик → textarea с сырым LaTeX для правки, blur → сохранение + переотрисовка. Никакого дублирования (как в Anki). Кнопка «ƒₓ Формула» синхронизирует отрисовку после вставки (модалка снимает фокус с поля → fcEndEdit). Co-Authored-By: Claude Opus 4.8 --- frontend/flashcards.html | 64 ++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/frontend/flashcards.html b/frontend/flashcards.html index f942af7..32e5fef 100644 --- a/frontend/flashcards.html +++ b/frontend/flashcards.html @@ -139,9 +139,12 @@ .card-textarea { width: 100%; border: none; outline: none; resize: none; background: transparent; font-family: 'Manrope', sans-serif; font-size: .88rem; color: var(--text); min-height: 48px; line-height: 1.5; padding: 0; } - .fc-math-preview { display: none; margin-top: 6px; padding: 6px 9px; font-size: .85rem; line-height: 1.5; - color: var(--text); background: rgba(155,93,229,.05); border: 1px solid rgba(155,93,229,.14); - border-radius: 8px; overflow-x: auto; } + /* Карточка по умолчанию — отрисованный текст (с KaTeX). Клик → редактирование. */ + .card-display { min-height: 48px; line-height: 1.5; font-size: .88rem; color: var(--text); + cursor: text; padding: 2px 4px; margin: 0 -4px; border-radius: 7px; + transition: background .12s; overflow-x: auto; white-space: pre-wrap; word-break: break-word; } + .card-display:hover { background: rgba(155,93,229,.06); } + .card-display.fc-empty { color: var(--text-3); font-style: italic; } .card-actions { display: flex; flex-direction: column; gap: 0; border-left: 1px solid var(--border); } .card-act-btn { padding: 0 14px; height: 100%; flex: 1; border: none; background: none; cursor: pointer; color: var(--text-3); transition: .15s; display: flex; @@ -828,11 +831,10 @@ function renderCardList() { Вопрос - -
+ onblur="fcEndEdit(this)">${esc(c.front)} ${imgRowHtml(c, 'front')}
@@ -841,11 +843,10 @@ function renderCardList() { Ответ - -
+ onblur="fcEndEdit(this)">${esc(c.back)} ${imgRowHtml(c, 'back')}
@@ -855,20 +856,37 @@ function renderCardList() {
`).join(''); - // Превью формул KaTeX под каждым полем (textarea сырой LaTeX не рендерит) - list.querySelectorAll('.card-textarea').forEach(fcMathPreview); + // Отрисовать карточки (KaTeX). Правка — по клику (textarea), как в Anki. + list.querySelectorAll('.card-display').forEach(fcRenderDisplay); if (!q) bindCardDrag(); } -/* Рендер KaTeX-превью под полем карточки, если в тексте есть формула. */ -function fcMathPreview(ta) { - const side = ta.closest('.card-side'); - const prev = side && side.querySelector('.fc-math-preview'); - if (!prev) return; - const txt = ta.value || ''; - if (!/\$|\\\(|\\\[/.test(txt)) { prev.style.display = 'none'; prev.innerHTML = ''; return; } - prev.innerHTML = mathHtmlFC(txt); - prev.style.display = 'block'; +/* Показать отрисованный текст карточки (или плейсхолдер, если пусто). */ +function fcRenderDisplay(disp) { + const side = disp.closest('.card-side'); + const ta = side && side.querySelector('.card-textarea'); + const text = ta ? ta.value : ''; + if (text && text.trim()) { disp.innerHTML = mathHtmlFC(text); disp.classList.remove('fc-empty'); } + else { disp.textContent = disp.dataset.ph || ''; disp.classList.add('fc-empty'); } +} +/* Клик по отрисованной карточке → редактирование (textarea с сырым LaTeX). */ +function fcStartEdit(disp) { + const side = disp.closest('.card-side'); + const ta = side && side.querySelector('.card-textarea'); + if (!ta) return; + disp.style.display = 'none'; + ta.style.display = ''; + ta.focus(); + const v = ta.value; try { ta.setSelectionRange(v.length, v.length); } catch (e) {} +} +/* Конец редактирования (blur / после вставки формулы): сохранить + отрисовать. */ +function fcEndEdit(ta) { + const id = +ta.dataset.cid, side = ta.dataset.side; + if (id && side) saveCard(id, side, ta.value); + const sideEl = ta.closest('.card-side'); + const disp = sideEl && sideEl.querySelector('.card-display'); + ta.style.display = 'none'; + if (disp) { disp.style.display = ''; fcRenderDisplay(disp); } } /* ── drag-reorder карточек (только без активного фильтра) ── */ @@ -1411,6 +1429,8 @@ function fxInsert() { ta.dispatchEvent(new Event('input', { bubbles: true })); ta.dispatchEvent(new Event('change', { bubbles: true })); ta.focus(); + // Поле карточки скрылось при открытии модалки (blur) — сохранить и переотрисовать. + if (ta.classList && ta.classList.contains('card-textarea')) fcEndEdit(ta); } closeModal('modal-formula'); }