|
33 | 33 | } |
34 | 34 |
|
35 | 35 |
|
| 36 | +def _safe_decode(data: bytes) -> str: |
| 37 | + """Decode bytes to string, trying common encodings.""" |
| 38 | + try: |
| 39 | + return data.decode("utf-8") |
| 40 | + except UnicodeDecodeError: |
| 41 | + pass |
| 42 | + for enc in ("gbk", "gb2312", "gb18030", "big5", "shift-jis", "euc-kr"): |
| 43 | + try: |
| 44 | + return data.decode(enc) |
| 45 | + except (UnicodeDecodeError, UnicodeEncodeError): |
| 46 | + continue |
| 47 | + return data.decode("utf-8", errors="replace") |
| 48 | + |
| 49 | + |
36 | 50 | def _strip_html(text: str) -> str: |
37 | | - """Remove HTML tags and collapse whitespace.""" |
| 51 | + """Remove HTML tags, style/script blocks, comments, and unescape entities.""" |
| 52 | + import html as _html |
| 53 | + # Remove <style> and <script> blocks (including their content) |
| 54 | + text = re.sub(r"<style[^>]*>.*?</style>", "", text, flags=re.DOTALL | re.IGNORECASE) |
| 55 | + text = re.sub(r"<script[^>]*>.*?</script>", "", text, flags=re.DOTALL | re.IGNORECASE) |
| 56 | + # Remove HTML comments |
| 57 | + text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL) |
| 58 | + # Strip remaining tags |
38 | 59 | plain = re.sub(r"<[^>]*>", "", text) |
| 60 | + # Unescape HTML entities |
| 61 | + plain = _html.unescape(plain) |
| 62 | + # Collapse whitespace |
39 | 63 | plain = re.sub(r"\s+", " ", plain) |
40 | 64 | return plain.strip() |
41 | 65 |
|
@@ -73,10 +97,10 @@ def _make_dedup_key(content: ClipboardContent) -> str: |
73 | 97 | def _build_preview(types: dict[ContentType, bytes]) -> str: |
74 | 98 | """Build a human-readable preview from clipboard content.""" |
75 | 99 | if ContentType.TEXT in types: |
76 | | - text = types[ContentType.TEXT].decode("utf-8", errors="replace") |
| 100 | + text = _safe_decode(types[ContentType.TEXT]) |
77 | 101 | return text[:200] |
78 | 102 | if ContentType.HTML in types: |
79 | | - html = types[ContentType.HTML].decode("utf-8", errors="replace") |
| 103 | + html = _safe_decode(types[ContentType.HTML]) |
80 | 104 | plain = _strip_html(html) |
81 | 105 | return plain[:200] if plain else "[HTML]" |
82 | 106 | if ContentType.IMAGE_EMF in types: |
|
0 commit comments