Files
TestingWebApp/flask_app/app/templates/tests/editor.html
T
Константин Лебединский 1ea83aa6b4 testingwebapp fixes, weeek tasks 2948-2958
2026-05-04 21:29:23 +05:00

534 lines
32 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}{{ content.test.title }} — редактор{% endblock %}
{% block content %}
<div id="editor-root"
class="space-y-4 sm:space-y-5 pb-24 test-detail-page test-detail-page--with-fixed-actions"
data-test-id="{{ test_id }}"
data-initial='{{ content | tojson | safe }}'>
<div id="editor-gen-toast" class="editor-gen-toast" role="status" aria-live="polite" hidden></div>
<section class="cabinet-brick cabinet-brick--hero hero-brick">
<a href="{{ url_for('tests.tests_list_page') }}" class="link-back">К тестам</a>
<textarea id="test-title" maxlength="200" rows="1" placeholder="Название теста"
class="hero-brick__title font-headline"></textarea>
<textarea id="test-description" rows="2" placeholder="Краткое описание (необязательно)"
class="hero-brick__desc"></textarea>
<div class="hero-brick__meta-row">
<span>Автор: <b id="intro-author">Вы</b></span>
<span class="hero-brick__sep">·</span>
<span>Обновлён: <span id="intro-updated"></span></span>
<span class="hero-brick__sep">·</span>
<span>Версия <span id="intro-version"></span></span>
</div>
<div class="hero-brick__divider"></div>
<div class="hero-brick__meta-row">
<span>Порог зачёта: <b id="threshold-mirror"></b>%</span>
<span class="hero-brick__sep">·</span>
<span>Вопросов: <b id="q-count">0</b></span>
<span class="hero-brick__sep">·</span>
<span>Время: <b id="editor-hero-time-val"></b></span>
<span class="hero-brick__sep">·</span>
<span>Результат: <b id="editor-hero-result-val"></b></span>
<span class="hero-brick__sep">·</span>
<span>Подсказки: <b id="editor-hero-hints-val"></b></span>
<span class="hero-brick__sep">·</span>
<span id="chain-active-display">Активна в каталоге</span>
</div>
</section>
{# ── Версии ───────────────────────────────────────────────────── #}
<details class="cabinet-disclosure cabinet-brick">
<summary class="cabinet-disclosure__summary">
<span class="cabinet-disclosure__summary-text">
<span class="cabinet-disclosure__summary-title font-headline">Версии</span>
<span class="cabinet-disclosure__summary-sub">История изменений; можно переключить активную версию</span>
</span>
</summary>
<div class="cabinet-disclosure__body">
<ul id="versions-list" class="version-card-list"></ul>
</div>
</details>
<details class="cabinet-disclosure cabinet-brick">
<summary class="cabinet-disclosure__summary">
<span class="cabinet-disclosure__summary-text">
<span class="cabinet-disclosure__summary-title font-headline">Параметры теста</span>
<span class="cabinet-disclosure__summary-sub">Порог, таймер, режим результата, подсказки и шаблон структуры вопросов</span>
</span>
</summary>
<div class="cabinet-disclosure__body">
<div class="settings-grid">
<label class="settings-row">
<span class="settings-row__label">Проходной балл, %</span>
<input id="test-threshold" type="number" min="0" max="100" step="1" inputmode="numeric"
class="settings-row__input" />
</label>
<label class="settings-row">
<span class="settings-row__label">
Таймер, минут
<span class="settings-row__hint">0 или пусто — без ограничения</span>
</span>
<input id="test-time-limit" type="number" min="0" max="600" step="1" inputmode="numeric"
class="settings-row__input" placeholder="—" />
</label>
<fieldset class="settings-row settings-row--block">
<legend class="settings-row__label">Когда показывать результат</legend>
<label class="settings-radio">
<input type="radio" name="result-mode" value="end" />
<span>В конце теста</span>
</label>
<label class="settings-radio">
<input type="radio" name="result-mode" value="immediate" />
<span>Сразу после ответа</span>
</label>
</fieldset>
<label class="settings-row settings-row--toggle" id="test-hints-row" style="display:none;">
<span class="settings-row__label">
Показывать подсказку после ответа
<span class="settings-row__hint">Краткое объяснение под вариантами ответа</span>
</span>
<input id="test-hints-enabled" type="checkbox" />
</label>
<div class="settings-row settings-row--block">
<span class="settings-row__label">Шаблон структуры вопросов</span>
<p class="settings-row__hint" style="margin-bottom:0.5rem;">
Задаёт сетку для автосборки пустых вопросов и для генерации через ИИ. При смене чисел список вопросов пересобирается (с подтверждением, если уже есть текст).
</p>
<div class="flex flex-wrap items-end gap-3">
<label class="block">
<span class="form-label">Вопросов</span>
<input id="ai-q-count" type="number" min="1" max="30" step="1" value="7"
class="form-input" style="width:90px;" />
</label>
<label class="block">
<span class="form-label">Вариантов</span>
<input id="ai-o-count" type="number" min="2" max="8" step="1" value="3"
class="form-input" style="width:90px;" />
</label>
</div>
<div class="template-multi-row mt-3">
<label class="inline-flex items-start gap-2 min-h-9 w-full">
<input id="template-global-multi" type="checkbox"
class="rounded border-ink-300 text-brand-600 focus:ring-brand-500 mt-0.5 shrink-0" />
<span class="text-sm leading-snug">Несколько правильных ответов (все вопросы)</span>
</label>
</div>
<div class="template-correct-range-row mt-2 flex flex-wrap items-end gap-3">
<label class="block">
<span class="form-label">Правильных: от</span>
<input id="template-min-correct" type="number" min="1" max="8" step="1" value="1"
class="form-input" style="width:90px;" />
</label>
<label class="block">
<span class="form-label">до</span>
<input id="template-max-correct" type="number" min="1" max="8" step="1" value="1"
class="form-input" style="width:90px;" />
</label>
</div>
</div>
<div class="settings-row settings-row--block" style="padding-top:0.75rem; border-top:1px solid var(--outline-variant); margin-top:0.25rem;">
<span class="settings-row__label">Видимость в каталоге</span>
<p class="settings-row__hint" style="margin-bottom:0.5rem;">Скрытые тесты не показываются в общем списке; ссылку по-прежнему можно открыть.</p>
<button id="btn-toggle-visibility" class="btn btn-ghost btn--sm" type="button">Скрыть из списка</button>
</div>
</div>
</div>
</details>
<details class="cabinet-disclosure cabinet-brick">
<summary class="cabinet-disclosure__summary">
<span class="cabinet-disclosure__summary-text">
<span class="cabinet-disclosure__summary-title font-headline">Инструменты генерации</span>
<span class="cabinet-disclosure__summary-sub">Генерация через ИИ, проверка, улучшение и импорт документа</span>
</span>
</summary>
<div class="cabinet-disclosure__body">
<section class="rounded-2xl bg-brand-50/60 border border-brand-100 test-detail-ai-panel editor-generation-panel">
{# ── Заполнить через ИИ по теме ──────────────────────────── #}
<div class="question-editor-block question-editor-block--first">
<h3 class="test-detail-subsection__title">Заполнить через ИИ</h3>
<label class="block">
<span class="form-label">Тема / промпт</span>
<textarea id="ai-topic" rows="1" class="form-input"
placeholder="Например: охрана труда на производстве"
style="resize:none; overflow:hidden; font-family:inherit;"></textarea>
</label>
<label class="mt-2 inline-flex items-start gap-2 text-sm text-ink-700 cursor-pointer select-none">
<input type="checkbox" id="ai-keep-title" class="mt-1 rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
<span>Не менять название теста в редакторе после генерации (оставить текущее)</span>
</label>
<div class="mt-2">
<button id="ai-generate-test"
class="btn btn-ghost" type="button" style="min-height:43px;">
Сгенерировать вопросы (ИИ)
</button>
</div>
<ul class="editor-generation-panel__notes mt-2 text-xs text-ink-500 space-y-1 list-disc pl-4 leading-snug">
<li>Если в параметрах включены подсказки — после генерации будут созданы и тексты подсказок (черновик сохранится автоматически).</li>
<li>Если уже есть версия с заполненным тестом — после «Сгенерировать» и сохранения весь прежний текст останется в старой версии в блоке «Версии».</li>
</ul>
</div>
{# ── Проверить и улучшить ─────────────────────────────────── #}
<div class="question-editor-block">
<h3 class="test-detail-subsection__title">Проверить и улучшить</h3>
<p class="text-xs text-ink-500 leading-snug mb-2">
<span class="font-medium text-ink-600">Проверить тест</span> — ИИ прочитает вопросы и варианты и вернёт краткий вердикт и список замечаний; черновик не меняется.
</p>
<p class="text-xs text-ink-500 leading-snug mb-3">
<span class="font-medium text-ink-600">Предложить улучшение</span> — ИИ предложит правки по каждому вопросу (было → стало); вы отметите, что применить к черновику.
</p>
<div class="flex flex-col sm:flex-row sm:flex-wrap sm:items-end gap-2">
<label class="flex flex-col gap-1 min-w-[12rem] flex-1">
<span class="text-xs font-medium text-ink-600">Область улучшения</span>
<select id="ai-improve-focus"
class="form-input text-sm py-2 rounded-lg border border-ink-300 bg-white">
<option value="all">Всё: вопросы и варианты</option>
<option value="questions">Только формулировки вопросов</option>
<option value="distractors">Только неверные варианты (дистракторы)</option>
<option value="options">Все варианты ответа (без смены верности)</option>
</select>
</label>
<div class="flex flex-wrap gap-2">
<button id="ai-check"
class="btn btn-ghost" type="button" style="min-height:43px;">
<span class="material-symbols-outlined text-base" style="vertical-align:-3px;">fact_check</span>
Проверить тест
</button>
<button id="ai-improve"
class="btn btn-ghost" type="button" style="min-height:43px;">
<span class="material-symbols-outlined text-base" style="vertical-align:-3px;">auto_fix_high</span>
Предложить улучшение
</button>
</div>
</div>
</div>
{# ── Документ в вопросы ──────────────────────────────────── #}
<div class="question-editor-block test-detail-subsection test-detail-subsection--import">
<h3 class="test-detail-subsection__title">Документ в вопросы</h3>
<p class="text-xs text-ink-500 leading-snug mb-2 text-center">
<span class="font-medium text-ink-600">Сгенерировать из документа</span> — из файла извлекается текст; ИИ составляет вопросы по содержанию и шаблону из «Параметров» (число вопросов, вариантов, несколько верных и т.д.), с учётом поля «Пожелания», если оно заполнено. Перед заменой откроется предпросмотр: «Применить» подставит черновик вместо текущих вопросов; дальше сохраните тест на сервер — подсказки и версии ведут себя так же, как при генерации по теме.
</p>
<label id="ai-import-dropzone"
class="import-dropzone mt-2 flex flex-col w-full items-center justify-center gap-1
px-4 py-5 rounded-xl bg-white border-2 border-dashed border-ink-300/70
hover:border-brand-400 hover:bg-brand-50/40 cursor-pointer transition-colors">
<span class="material-symbols-outlined text-2xl text-brand-400">upload_file</span>
<span id="ai-import-dropzone-label" class="text-sm font-medium text-ink-700 text-center block w-full">Перетащите файлы сюда или нажмите</span>
<span class="text-xs text-ink-400 text-center block w-full">PDF, DOCX, TXT, MD · до 5 за раз · до 16 МБ · повторный выбор добавляет к уже загруженным; полный сброс — «Сбросить загрузку».</span>
<input id="ai-import-file" type="file" accept=".pdf,.docx,.txt,.md" multiple class="hidden" />
</label>
<div class="mt-2 flex flex-wrap gap-2">
<button type="button" id="ai-import-clear" class="btn btn-ghost btn--sm text-sm" style="min-height:36px;">
Сбросить загрузку
</button>
</div>
<label class="block mt-3">
<span class="form-label">Пожелания по содержанию <span class="text-ink-400 font-normal">(необязательно)</span></span>
<textarea id="doc-user-hint" rows="1"
class="form-input mt-1"
placeholder="Например: акцент на разделе 3, не делать вопросы про даты"
style="resize:none; overflow:hidden; font-family:inherit;"></textarea>
</label>
<button id="doc-generate-btn"
class="btn btn-ghost mt-2" type="button" style="min-height:43px;">
<span class="material-symbols-outlined text-base" style="vertical-align:-3px;">auto_awesome</span>
Сгенерировать из документа
</button>
</div>
{# ── Модалка результата импорта документа ─────────────────── #}
<dialog id="import-modal" class="save-modal import-modal">
<div class="save-modal__inner import-modal__inner">
<h3 id="import-modal-title" class="import-modal__title font-headline"></h3>
<div id="import-modal-body" class="import-modal__body"></div>
<div id="import-modal-actions" class="import-modal__actions flex gap-2 justify-end flex-wrap"></div>
</div>
</dialog>
</section>
</div>
</details>
<details class="cabinet-disclosure cabinet-brick" open>
<summary class="cabinet-disclosure__summary">
<span class="cabinet-disclosure__summary-text">
<span class="cabinet-disclosure__summary-title font-headline">Вопросы</span>
<span class="cabinet-disclosure__summary-sub">Тексты, варианты ответов и подсказки по каждому вопросу</span>
</span>
</summary>
<div class="cabinet-disclosure__body">
<section class="mt-1">
<div class="flex items-center gap-2">
<h2 class="font-semibold text-ink-900">Вопросы (<span id="q-count-mirror">0</span>)</h2>
</div>
<ol id="questions" class="mt-3 space-y-4"></ol>
<div class="mt-3 flex justify-center flex-wrap gap-2">
<button id="add-question"
class="inline-flex items-center gap-1 px-3 py-2 rounded-lg
bg-white border border-ink-300/60 hover:border-brand-300 text-sm min-h-10
btn btn-ghost question-editor__add-question">
<span class="material-symbols-outlined text-base">add</span>
<span>Добавить вопрос</span>
</button>
<button id="add-question-ai" type="button"
class="inline-flex items-center gap-1 px-3 py-2 rounded-lg
bg-brand-50 border border-brand-200 hover:border-brand-400 text-sm min-h-10
btn btn-ghost question-editor__add-question-ai"
title="Добавляет пустой блок и вызывает ИИ для одного нового вопроса с вариантами">
<span class="material-symbols-outlined text-base">auto_awesome</span>
<span>Новый вопрос (ИИ)</span>
</button>
</div>
</section>
</div>
</details>
{# Прохождения перенесены на /stats. Назначения перенесены на /assignments #}
</div>
{# ── Sticky-footer: «Цепочка активна» + «Сохранить» ────────────── #}
<div class="fixed bottom-0 inset-x-0 z-30 bg-white/95 backdrop-blur border-t border-ink-300/60
pb-[env(safe-area-inset-bottom)]">
<div class="mx-auto max-w-2xl px-4 py-3
flex items-center justify-between gap-3">
<div id="intro-fork-banner" class="callout callout--warning text-xs sm:text-sm"
data-fork-risk="{{ '1' if content.test.hasForkRisk else '0' }}"
style="display:none; margin:0; padding:0.4rem 0.6rem; flex:1 1 0; min-width:0; white-space:normal; word-break:break-word; line-height:1.25;">
При сохранении будет создана новая версия теста.
</div>
<div class="flex items-center gap-2 ml-auto shrink-0">
<button id="btn-cancel"
class="inline-flex px-3 py-2 rounded-lg text-ink-700 hover:bg-ink-100 text-sm btn btn-ghost">
Отмена
</button>
<button id="save-draft"
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg
bg-brand-600 hover:bg-brand-700 text-white font-medium min-h-11 btn btn-primary">
<span class="material-symbols-outlined text-base">save</span>
Сохранить
</button>
</div>
</div>
<p id="save-status" class="mx-auto max-w-2xl px-4 pb-2 text-xs text-ink-500"></p>
</div>
{# ── Шаблон вопроса ─────────────────────────────────────────────── #}
<template id="tpl-question">
<li class="relative rounded-xl bg-white border border-ink-300/60 p-4 sm:p-5 q-item" draggable="true">
{# Оверлей загрузки AI #}
<div class="q-ai-overlay hidden absolute inset-0 rounded-xl z-10
bg-white/80 backdrop-blur-[2px] flex flex-col items-center justify-center gap-2">
<span class="q-ai-spinner inline-block w-7 h-7 rounded-full
border-[3px] border-brand-200 border-t-brand-600 animate-spin"></span>
<span class="text-xs text-ink-500 font-medium">Генерирую…</span>
</div>
{# Шапка карточки вопроса #}
<div class="flex items-center justify-between gap-2">
<span class="inline-flex items-center gap-1">
<button class="q-drag p-2 rounded hover:bg-ink-100 min-w-10 min-h-10 cursor-grab"
title="Перетащить" aria-label="Перетащить" type="button">
<span class="material-symbols-outlined text-base">drag_indicator</span>
</button>
<span class="inline-flex items-center px-2 py-0.5 rounded-md
bg-brand-50 text-brand-700 text-xs font-medium q-num">Вопрос #</span>
</span>
<div class="flex items-center gap-0.5">
<button class="q-clear p-2 rounded hover:bg-ink-100 min-w-10 min-h-10 text-ink-400"
title="Очистить вопрос" aria-label="Очистить вопрос" type="button">
<span class="material-symbols-outlined text-base">backspace</span>
</button>
<button class="q-up p-2 rounded hover:bg-ink-100 min-w-10 min-h-10"
title="Выше" aria-label="Поднять выше">
<span class="material-symbols-outlined text-base">arrow_upward</span>
</button>
<button class="q-down p-2 rounded hover:bg-ink-100 min-w-10 min-h-10"
title="Ниже" aria-label="Опустить ниже">
<span class="material-symbols-outlined text-base">arrow_downward</span>
</button>
<button class="q-delete p-2 rounded hover:bg-red-50 text-red-600 min-w-10 min-h-10"
title="Удалить" aria-label="Удалить вопрос">
<span class="material-symbols-outlined text-base">delete</span>
</button>
</div>
</div>
<div class="mt-2 relative">
<textarea class="q-text w-full rounded-lg border border-ink-300 px-3 py-2
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
rows="1" placeholder="Формулировка вопроса" maxlength="500"
style="resize:none; overflow:hidden; font-family:inherit;"></textarea>
<span class="q-text-counter absolute bottom-1.5 right-2 text-xs text-ink-400 pointer-events-none select-none"></span>
</div>
<div class="mt-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 text-sm">
<label class="inline-flex items-center gap-2 min-h-9">
<input type="checkbox"
class="q-multi rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
<span>Несколько правильных ответов</span>
</label>
<button class="q-ai btn btn-ghost btn--sm q-ai-btn" style="font-size:0.75rem; padding:0.3rem 0.7rem;">
<span class="material-symbols-outlined q-ai-icon" style="font-size:0.9rem; vertical-align:-2px;">auto_fix_high</span>
<span class="q-ai-label">Сгенерировать</span>
</button>
</div>
<p class="mt-4 mb-2 text-xs text-ink-400 font-medium">Отметьте правильные варианты</p>
<ul class="q-options space-y-2"></ul>
<div class="mt-3 flex items-center gap-3">
<button class="q-add-option inline-flex items-center gap-1 px-2 py-2 rounded
text-sm text-brand-700 hover:bg-brand-50 min-h-10 btn btn-ghost btn--sm">
<span class="material-symbols-outlined text-base">add</span>
<span class="q-add-option-label">Добавить вариант</span>
</button>
<span class="q-options-count text-xs text-ink-400"></span>
</div>
<div class="mt-4 pt-3 border-t border-ink-200/80 q-hint-block">
<label class="block">
<span class="text-xs text-ink-600 font-medium mb-1 block">Подсказка при прохождении</span>
<textarea class="q-hint form-input w-full rounded-lg border border-ink-300 px-3 py-2 text-sm
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
rows="2" maxlength="8000"
placeholder="Краткий текст подсказки (если в параметрах теста включены подсказки и режим «Сразу после ответа»)"
style="resize:none; overflow:hidden; font-family:inherit;"></textarea>
</label>
<p class="mt-1.5 text-xs text-ink-400 leading-snug">Необязательно. Показывается участнику во всплывающем окне после ответа на вопрос.</p>
</div>
</li>
</template>
{# ── Шаблон варианта ────────────────────────────────────────────── #}
<template id="tpl-option">
<li class="flex items-start gap-2 opt-item">
{# Чекбокс «Правильный» — выровнен по первой строке textarea #}
<label class="shrink-0 w-10 inline-flex items-center justify-center cursor-pointer
rounded hover:bg-ink-100 pt-1.5" style="min-height:2.5rem;" title="Правильный ответ">
<input type="checkbox"
class="opt-correct w-5 h-5 rounded border-ink-300 text-brand-600 focus:ring-brand-500" />
</label>
<div class="opt-text-wrap relative flex-1 min-w-0 self-start">
<textarea rows="1"
class="opt-text w-full rounded-lg border border-ink-300 px-3 py-2
focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20"
placeholder="Вариант ответа"
style="resize:none; overflow:hidden; font-family:inherit; line-height:1.55;"></textarea>
<div class="opt-ai-overlay hidden absolute inset-0 rounded-lg z-[5]
bg-white/85 backdrop-blur-[2px] flex flex-col items-center justify-center gap-1
border border-ink-200/80 shadow-sm">
<span class="inline-block w-6 h-6 rounded-full
border-[3px] border-brand-200 border-t-brand-600 animate-spin"></span>
<span class="text-xs text-ink-600 font-medium px-2 text-center leading-snug">Улучшаю…</span>
</div>
</div>
<button type="button" class="opt-ai shrink-0 inline-flex items-center justify-center
rounded hover:bg-brand-50 text-brand-700 px-1.5 pt-1.5"
style="min-height:2.5rem; min-width:2.25rem;"
title="Улучшить только этот вариант (ИИ)" aria-label="Улучшить вариант ИИ">
<span class="material-symbols-outlined text-base">auto_fix_high</span>
</button>
<button class="opt-delete shrink-0 w-10 inline-flex items-center justify-center
rounded hover:bg-red-50 text-red-600 pt-1.5"
style="min-height:2.5rem;"
title="Удалить" aria-label="Удалить вариант">
<span class="material-symbols-outlined text-base">close</span>
</button>
</li>
</template>
{# ── Модалка успешного сохранения (компактная, сверху) ──────────── #}
<dialog id="save-modal" class="save-modal">
<div class="save-modal__inner">
<h3 class="text-base font-semibold mb-1" id="save-modal-title">Сохранено</h3>
<p id="save-modal-msg" class="text-sm text-ink-700">Изменения сохранены.</p>
<div class="mt-4 flex items-center justify-end gap-2">
<button id="save-modal-stay" type="button"
class="px-3 py-2 rounded-lg bg-ink-100 hover:bg-ink-200 text-sm btn btn-ghost">
К редактору
</button>
<button id="save-modal-go" type="button"
class="px-3 py-2 rounded-lg bg-brand-600 hover:bg-brand-700 text-white text-sm btn btn-primary">
К каталогу
</button>
</div>
</div>
</dialog>
{# ── Выбор режима ИИ для непустого вопроса (улучшить / дистракторы) ── #}
<dialog id="dlg-q-ai-mode" class="save-modal" style="max-width: 24rem; width: calc(100% - 2rem);">
<div class="save-modal__inner">
<h3 class="text-base font-semibold text-ink-900 mb-1">Что сделать с вопросом?</h3>
<p class="text-xs text-ink-500 mb-3">Выберите, что должен сделать ИИ.</p>
<div class="flex flex-col gap-2">
<button type="button" id="q-ai-mode-distractors"
class="px-3 py-2.5 rounded-lg border border-ink-200 text-left text-sm
hover:bg-brand-50 hover:border-brand-200 transition-colors">
<span class="font-medium text-ink-800">Добавить дистракторы</span>
<span class="block text-ink-500 text-xs mt-0.5">Заполнить пустые поля вариантов</span>
</button>
<button type="button" id="q-ai-mode-question"
class="px-3 py-2.5 rounded-lg border border-ink-200 text-left text-sm
hover:bg-brand-50 hover:border-brand-200 transition-colors">
<span class="font-medium text-ink-800">Улучшить только вопрос</span>
<span class="block text-ink-500 text-xs mt-0.5">Переформулировать текст вопроса</span>
</button>
<button type="button" id="q-ai-mode-options"
class="px-3 py-2.5 rounded-lg border border-ink-200 text-left text-sm
hover:bg-brand-50 hover:border-brand-200 transition-colors">
<span class="font-medium text-ink-800">Улучшить только варианты</span>
<span class="block text-ink-500 text-xs mt-0.5">Тексты ответов, без смены верности</span>
</button>
</div>
<div class="mt-4 flex justify-end">
<button type="button" id="q-ai-mode-cancel"
class="px-3 py-2 rounded-lg text-ink-600 hover:bg-ink-100 text-sm">
Отмена
</button>
</div>
</div>
</dialog>
{# ── Модалка результата AI-проверки/улучшения (fullscreen на мобиле) ── #}
<dialog id="ai-modal"
class="m-0 p-0 w-full h-full sm:h-auto sm:max-w-3xl sm:w-full sm:max-h-[90vh]
sm:rounded-2xl sm:m-auto bg-white backdrop:bg-black/50">
<div class="flex flex-col h-full sm:max-h-[90vh]">
<div class="flex items-center justify-between gap-3 px-4 sm:px-5 py-3 border-b border-ink-300/60">
<h3 id="ai-modal-title" class="text-lg font-semibold truncate">AI</h3>
<button id="ai-modal-close"
class="p-2 rounded hover:bg-ink-100 min-w-10 min-h-10"
aria-label="Закрыть">
<span class="material-symbols-outlined">close</span>
</button>
</div>
<div id="ai-modal-body" class="flex-1 overflow-y-auto px-4 sm:px-5 py-4"></div>
<div id="ai-modal-actions"
class="px-4 sm:px-5 py-3 border-t border-ink-300/60
flex items-center justify-end gap-2 flex-wrap
pb-[max(env(safe-area-inset-bottom),0.75rem)]"></div>
</div>
</dialog>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/editor.js') }}"></script>
{% endblock %}