new web ract

This commit is contained in:
jze9
2026-05-15 03:31:28 +05:00
parent de78624495
commit 2335497226
58 changed files with 7397 additions and 406 deletions

View File

@@ -1,9 +1,41 @@
import re
import flet as ft
import designer as d
import api_client as api
import theme_manager
def _html_to_markdown(html: str) -> str:
"""Convert TipTap HTML output to Markdown for ft.Markdown rendering."""
t = html or ""
# Headings
t = re.sub(r"<h([1-6])[^>]*>(.*?)</h\1>",
lambda m: "#" * int(m[1]) + " " + m[2] + "\n\n",
t, flags=re.DOTALL | re.IGNORECASE)
# Bold / italic
t = re.sub(r"<(strong|b)[^>]*>(.*?)</\1>", r"**\2**", t, flags=re.DOTALL | re.IGNORECASE)
t = re.sub(r"<(em|i)[^>]*>(.*?)</\1>", r"*\2*", t, flags=re.DOTALL | re.IGNORECASE)
# Links
t = re.sub(r'<a[^>]+href=["\']([^"\']+)["\'][^>]*>(.*?)</a>',
r"[\2](\1)", t, flags=re.DOTALL | re.IGNORECASE)
# Images
t = re.sub(r'<img[^>]+src=["\']([^"\']+)["\'][^>]*>', r"![](\1)", t, flags=re.IGNORECASE)
# Lists
t = re.sub(r"<li[^>]*>(.*?)</li>", r"- \1\n", t, flags=re.DOTALL | re.IGNORECASE)
t = re.sub(r"</?[uo]l[^>]*>", "\n", t, flags=re.IGNORECASE)
# Paragraphs / line breaks
t = re.sub(r"<br\s*/?>", "\n", t, flags=re.IGNORECASE)
t = re.sub(r"<p[^>]*>(.*?)</p>", r"\1\n\n", t, flags=re.DOTALL | re.IGNORECASE)
# Strip remaining tags
t = re.sub(r"<[^>]+>", "", t)
# HTML entities
t = (t.replace("&nbsp;", " ").replace("&amp;", "&")
.replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", '"'))
# Collapse excessive blank lines
t = re.sub(r"\n{3,}", "\n\n", t)
return t.strip()
class NewsDetailView:
def __init__(self, page: ft.Page, slug: str):
self.page = page
@@ -159,7 +191,7 @@ class NewsDetailView:
controls.append(
ft.Markdown(
value=a.get("content", ""),
value=_html_to_markdown(a.get("content", "")),
selectable=True,
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
on_tap_link=lambda e: self.page.launch_url(e.data),
@@ -167,6 +199,26 @@ class NewsDetailView:
)
)
if a.get("source_url"):
source_url = a["source_url"]
controls.append(
ft.Container(
content=ft.Row([
ft.Icon(ft.icons.OPEN_IN_NEW, size=15, color=d.PRIMARY),
ft.Text("Источник:", size=13, color=d.TEXT_SECONDARY),
ft.TextButton(
source_url,
style=ft.ButtonStyle(color=d.PRIMARY),
on_click=lambda e, url=source_url: self.page.launch_url(url),
),
], spacing=6, vertical_alignment=ft.CrossAxisAlignment.CENTER, wrap=True),
bgcolor=ft.colors.with_opacity(0.04, d.PRIMARY),
border_radius=8,
padding=12,
border=ft.border.all(1, ft.colors.with_opacity(0.15, d.PRIMARY)),
)
)
controls.append(ft.Divider(height=32, color="transparent"))
controls.append(
ft.TextButton(