Обновил добавление обновлений

This commit is contained in:
2026-06-22 18:25:04 +03:00
parent 24c10ea5ca
commit b0fe3ad5a4
3 changed files with 253 additions and 80 deletions

103
app.py
View File

@@ -1958,12 +1958,107 @@ def _format_update_date(value: str | None) -> str:
return value
def _paragraphs_html(value: str | None) -> str:
lines = _split_lines(value)
def _inline_update_markup(value: str | None) -> str:
"""Безопасное мини-форматирование для текста обновлений.
Поддерживаем в админке:
- `F5` или `1` — выделенная клавиша/команда;
- **важный текст** — зелёное выделение.
Остальной текст обязательно экранируется, чтобы не сломать HTML.
"""
source = _clean_update_text(value)
if not source:
return ""
token_re = re.compile(r"(`[^`]+`|\*\*.+?\*\*)")
result: list[str] = []
last_pos = 0
for match in token_re.finditer(source):
result.append(html.escape(source[last_pos:match.start()], quote=True))
token = match.group(0)
if token.startswith("`") and token.endswith("`"):
inner = token[1:-1].strip()
if inner:
result.append(f'<kbd class="update-key">{html.escape(inner, quote=True)}</kbd>')
elif token.startswith("**") and token.endswith("**"):
inner = token[2:-2].strip()
if inner:
result.append(f'<span class="update-highlight">{html.escape(inner, quote=True)}</span>')
last_pos = match.end()
result.append(html.escape(source[last_pos:], quote=True))
return "".join(result)
def _flush_update_list(buffer: list[str], list_type: str) -> str:
if not buffer:
return ""
tag = "ol" if list_type == "numbered" else "ul"
items = "\n".join(f" <li>{item}</li>" for item in buffer)
return (
f' <{tag} class="update-pretty-list update-pretty-list--{list_type}">\n'
f"{items}\n"
f" </{tag}>"
)
def _rich_update_text_html(value: str | None) -> str:
"""Текст обновления с красивыми списками и мини-выделениями.
Поддерживаемые форматы в textarea:
1. Первый пункт -> красивый нумерованный список
- Первый пункт -> красивый маркированный список
`F5` -> выделенная клавиша/команда
**важно** -> выделенное слово/фраза
"""
raw_lines = [line.strip() for line in (value or "").splitlines()]
lines = [line for line in raw_lines if line]
if not lines:
return "<p>Описание не заполнено.</p>"
return "\n".join(f"<p>{_escape_text(line)}</p>" for line in lines)
html_parts: list[str] = []
list_buffer: list[str] = []
current_list_type = ""
def close_list() -> None:
nonlocal list_buffer, current_list_type
if list_buffer:
html_parts.append(_flush_update_list(list_buffer, current_list_type))
list_buffer = []
current_list_type = ""
for line in lines:
numbered_match = re.match(r"^\d+[\.)]\s+(.+)$", line)
bullet_match = re.match(r"^[-*•]\s+(.+)$", line)
if numbered_match:
if current_list_type and current_list_type != "numbered":
close_list()
current_list_type = "numbered"
list_buffer.append(_inline_update_markup(numbered_match.group(1)))
continue
if bullet_match:
if current_list_type and current_list_type != "bullet":
close_list()
current_list_type = "bullet"
list_buffer.append(_inline_update_markup(bullet_match.group(1)))
continue
close_list()
html_parts.append(f"<p>{_inline_update_markup(line)}</p>")
close_list()
return "\n".join(html_parts)
def _paragraphs_html(value: str | None) -> str:
# Оставлено как совместимый алиас: теперь описание умеет списки, клавиши и выделения.
return _rich_update_text_html(value)
def _normalize_update_image_path(value: str | None) -> str:
@@ -2012,7 +2107,7 @@ def _build_update_block(
actions = _split_lines(operator_actions)
if actions:
actions_html = "\n".join(f"<li>{_escape_text(action)}</li>" for action in actions)
actions_html = "\n".join(f"<li>{_inline_update_markup(action)}</li>" for action in actions)
else:
actions_html = "<li>Ознакомиться с обновлением перед работой с матчем.</li>"