From fdf41cfcbc5e9b1517e1dee85cf7120ad43902bf Mon Sep 17 00:00:00 2001 From: Rosie Val Date: Tue, 28 Jul 2026 18:41:36 +0100 Subject: [PATCH] GhostWipe report: chronological order, mobile layout, Ruby branding Messages within each channel now sort oldest-first (history()/purge() return newest-first, which read backwards). Added a real mobile layout: below 700px the sidebar stacks above the message view and its channel list becomes a horizontally-scrolling chip strip instead of a cramped vertical column. Report title/footer and the Discord log embeds now carry Ruby branding alongside the GhostWipe module name. Co-Authored-By: Claude Sonnet 5 --- ghostwipe/ghostwipe.py | 7 +++++-- ghostwipe/report.py | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/ghostwipe/ghostwipe.py b/ghostwipe/ghostwipe.py index c8746d2..0e63cf5 100644 --- a/ghostwipe/ghostwipe.py +++ b/ghostwipe/ghostwipe.py @@ -309,6 +309,9 @@ class GhostWipe(commands.Cog): "messages": [], } + # channel.history()/purge() return newest-first; the report reads far + # more naturally in actual conversation order. + collected.sort(key=lambda m: m.created_at) messages = [self._serialize_message(m) for m in collected] return { "id": str(channel.id), @@ -541,7 +544,7 @@ class GhostWipe(commands.Cog): if top: breakdown = "\n".join(f"**{c['name']}** — {c['message_count']}" for c in top) embed.add_field(name="Per-Channel Breakdown", value=breakdown, inline=False) - embed.set_footer(text=f"GhostWipe v{self.__version__}") + embed.set_footer(text=f"Ruby · GhostWipe v{self.__version__}") file_obj = None if conf["attach_html_report"]: @@ -903,7 +906,7 @@ class GhostWipe(commands.Cog): f"{humanize_number(stats.get('total_messages_deleted', 0))} messages deleted", inline=False, ) - embed.set_footer(text=f"GhostWipe v{self.__version__}") + embed.set_footer(text=f"Ruby · GhostWipe v{self.__version__}") await ctx.send(embed=embed) @ghostwipe.command(name="history") diff --git a/ghostwipe/report.py b/ghostwipe/report.py index bd96d98..ac8860f 100644 --- a/ghostwipe/report.py +++ b/ghostwipe/report.py @@ -33,6 +33,7 @@ STYLE = r""" --danger: #f04747; --warn: #faa61a; --ok: #3ba55d; + --ruby: #e0115f; --radius: 10px; --shadow: 0 8px 24px rgba(0,0,0,0.35); } @@ -171,12 +172,42 @@ html, body { padding: 8px 20px; border-top: 1px solid var(--border); color: var(--text-faint); font-size: 11px; text-align: center; flex-shrink: 0; } +.brand-ruby { color: var(--ruby); font-weight: 700; } ::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 6px; } ::-webkit-scrollbar-track { background: transparent; } -@media (max-width: 720px) { - #sidebar { width: 200px; } - #header .stats { gap: 12px; } +@media (max-width: 700px) { + html, body { font-size: 15px; } + #header { padding: 12px 14px; gap: 10px; } + .avatar, .avatar-fallback { width: 40px; height: 40px; } + #header .username { font-size: 14px; } + #header .id-block { min-width: 0; } + #header .stats { order: 3; width: 100%; margin-left: 0; justify-content: space-around; gap: 10px; } + #header .stat .num { font-size: 16px; } + #header .meta-row { order: 4; } + + /* Stack sidebar above main content, and turn the channel list into a + horizontally-scrolling strip of chips instead of a tall vertical list — + the classic mobile pattern for a channel/tab picker. */ + #body { flex-direction: column; } + #sidebar { + width: 100%; max-height: none; + border-right: none; border-bottom: 1px solid var(--border); + } + #sidebar .sidebar-title { padding: 10px 14px 4px; } + #channel-list { + display: flex; flex-direction: row; flex-wrap: nowrap; + overflow-x: auto; -webkit-overflow-scrolling: touch; + gap: 6px; padding: 4px 10px; + } + .ch-item { flex: 0 0 auto; margin-bottom: 0; } + .ch-item .ch-name { white-space: nowrap; } + #search-wrap { padding: 8px 10px; } + + #main { width: 100%; } + .panel-header { padding: 10px 14px; } + .panel-body { padding: 12px 14px; } + .attachment-img { max-width: 100%; } } """ @@ -398,7 +429,7 @@ def generate_report_html( main_content = '
Nothing was deleted for this event.
' sidebar_content = '
No channels affected.
' - title = f"GhostWipe Report — {html_lib.escape(member_name)}" + title = f"Ruby — GhostWipe Report — {html_lib.escape(member_name)}" doc = f""" @@ -442,7 +473,7 @@ def generate_report_html( {main_content} - +