From 00e749091269c30032cbd1df4d78ef08f0219228 Mon Sep 17 00:00:00 2001 From: aramesh-site deploy Date: Tue, 6 Oct 2026 12:19:34 +0000 Subject: [PATCH] =?UTF-8?q?fix(blog):=20=D9=86=D9=82=D9=84=E2=80=8C=D9=82?= =?UTF-8?q?=D9=88=D9=84=20=D8=AF=D8=A7=D8=AE=D9=84=20=D8=A2=DB=8C=D8=AA?= =?UTF-8?q?=D9=85=20=D9=81=D9=87=D8=B1=D8=B3=D8=AA=20=D8=A8=DB=8C=D8=B1?= =?UTF-8?q?=D9=88=D9=86=20=D9=85=DB=8C=E2=80=8C=D8=A2=DB=8C=D8=AF=20?= =?UTF-8?q?=E2=80=94=20=D8=AE=D8=B7=20=D9=85=D8=B3=DB=8C=20=D9=87=D9=85?= =?UTF-8?q?=D9=87=D9=94=20=D9=86=D9=82=D9=84=E2=80=8C=D9=82=D9=88=D9=84?= =?UTF-8?q?=E2=80=8C=D9=87=D8=A7=20=DB=8C=DA=A9=E2=80=8C=D8=AE=D8=B7=20ali?= =?UTF-8?q?gn=20=D9=85=DB=8C=E2=80=8C=D8=B4=D9=88=D8=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/sanitize.py | 61 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/backend/app/sanitize.py b/backend/app/sanitize.py index bd86fb3..a479cfb 100644 --- a/backend/app/sanitize.py +++ b/backend/app/sanitize.py @@ -44,6 +44,64 @@ def _is_junk_paragraph(node: Any) -> bool: ) +def _lift_lists_flat(node: Any) -> list[Any]: + """گره را به دنباله‌ای از گره‌های نرمال تبدیل می‌کند — نقل‌قول‌های داخل + آیتم فهرست به سطح خود فهرست می‌آیند تا خط مسی همهٔ نقل‌قول‌ها یک‌خط شود.""" + if not isinstance(node, dict): + return [node] + flat_content: list[Any] = [] + for child in node.get("content") or []: + flat_content.extend(_lift_lists_flat(child)) + node["content"] = flat_content + + if node.get("type") not in ("bulletList", "orderedList"): + return [node] + + rebuilt: list[Any] = [] + pending_items: list[Any] = [] + for li in node.get("content") or []: + if not isinstance(li, dict) or li.get("type") != "listItem": + pending_items.append(li) + continue + parts: list[list[Any]] = [[]] + for child in li.get("content") or []: + if isinstance(child, dict) and child.get("type") == "blockquote": + parts.append(child) + parts.append([]) + else: + parts[-1].append(child) + if len(parts) == 1: + pending_items.append(li) + continue + if parts[0]: + li["content"] = parts[0] + pending_items.append(li) + for part in parts[1:]: + if isinstance(part, dict) and part.get("type") == "blockquote": + if pending_items: + rebuilt.append({**node, "content": pending_items}) + pending_items = [] + rebuilt.append(part) + else: + pending_items.extend(part) + if pending_items: + rebuilt.append({**node, "content": pending_items}) + return rebuilt + + +def _lift_doc(doc: Any) -> Any: + lifted = _lift_lists_flat(doc) + if len(lifted) == 1 and isinstance(lifted[0], dict) and lifted[0].get("type") == "doc": + return lifted[0] + content: list[Any] = [] + for part in lifted: + if isinstance(part, dict) and part.get("type") == "doc": + content.extend(part.get("content") or []) + else: + content.append(part) + return {"type": "doc", "content": content} + + def _walk(node: Any) -> Any: if isinstance(node, list): return [_walk(item) for item in node] @@ -73,4 +131,5 @@ def sanitize_doc(doc: Any) -> Any: """سند TipTap را درجا پاک می‌کند (سایر ساختارها دست‌نخورده برمی‌گردند).""" if not isinstance(doc, dict): return doc - return _walk(doc) + walked = _walk(doc) + return _lift_doc(walked)