Skip to content

Commit 0f9f458

Browse files
committed
Apply Apple dark-mode skin to the logviewer
Add a custom logviewer build (deploy/oracle/logviewer) that layers an Apple dark-mode stylesheet (SF Pro, layered greys, frosted nav, system accent colors) onto the upstream image. The Dockerfile injects applemode.css as the last stylesheet so the upstream message-rendering templates are untouched. Point the compose logviewer service at the build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013vqQJQAud7rmfykwWKJACY
1 parent 8b69708 commit 0f9f458

4 files changed

Lines changed: 214 additions & 2 deletions

File tree

deploy/oracle/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ inbound ports are exposed.
88
What you get:
99
- `bot` — built from this repo's `Dockerfile`, so your fork's changes are
1010
included and the image is native to the VM's architecture.
11-
- `logviewer` — the web UI for closed-thread log links.
11+
- `logviewer` — the web UI for closed-thread log links, built from
12+
`./logviewer` with an **Apple dark-mode skin** layered on the upstream image.
1213
- `cloudflared` — a Cloudflare Tunnel that serves the logviewer at your domain
1314
over HTTPS, with no open ports on the VM.
15+
- `authproxy` + `caddy` — Discord OAuth2 role gate in front of the logviewer.
1416
- No database container — the bot and logviewer both point at your current
1517
`CONNECTION_URI`.
1618

@@ -123,6 +125,14 @@ so two instances don't run against the same database at once.
123125

124126
---
125127

128+
## Customising the logviewer look
129+
130+
The logviewer UI is restyled by `logviewer/static/css/applemode.css` (an Apple
131+
dark-mode skin using SF Pro). `logviewer/Dockerfile` builds on the upstream
132+
image and injects that stylesheet as the last `<link>` in the templates, so the
133+
bot's message-rendering logic is untouched — only the appearance changes. Edit
134+
the CSS and re-run `docker compose up -d --build` to iterate on the design.
135+
126136
## Updating later
127137

128138
```bash

deploy/oracle/docker-compose.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ services:
2020
- .env
2121

2222
logviewer:
23-
image: ghcr.io/modmail-dev/logviewer:master
23+
# Custom image: upstream logviewer + Apple dark-mode skin (see ./logviewer).
24+
build:
25+
context: ./logviewer
26+
image: modmail-logviewer-apple:latest
2427
container_name: modmail-logviewer
2528
restart: always
2629
env_file:

deploy/oracle/logviewer/Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Custom logviewer image: upstream logviewer + an Apple dark-mode skin.
2+
#
3+
# We keep the upstream app and all of its message-rendering templates untouched,
4+
# and only overlay one extra stylesheet (applemode.css) that overrides the look.
5+
# The stylesheet is injected as the LAST <link> in each template so it wins the
6+
# CSS cascade — no template logic is modified.
7+
FROM ghcr.io/modmail-dev/logviewer:master
8+
9+
COPY static/css/applemode.css /logviewer/static/css/applemode.css
10+
11+
# Append the Apple stylesheet right before </head> in both page templates.
12+
RUN sed -i 's#</head># <link href="/static/css/applemode.css" type="text/css" rel="stylesheet" media="screen,projection" />\n</head>#' \
13+
/logviewer/templates/base.html \
14+
/logviewer/templates/logbase.html
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/* ============================================================================
2+
Apple dark-mode skin for the Modmail logviewer.
3+
Loaded last, so it overrides Materialize + the logviewer's own stylesheets.
4+
Targets the existing template classes; no markup/logic is changed.
5+
========================================================================== */
6+
7+
/* SF Pro (real SF Pro on Apple devices via -apple-system; webfont elsewhere). */
8+
@import url('https://fonts.cdnfonts.com/css/sf-pro-display');
9+
10+
:root {
11+
--bg: #000000;
12+
--elev1: #1c1c1e;
13+
--elev2: #2c2c2e;
14+
--elev3: #3a3a3c;
15+
--label: #ffffff;
16+
--label2: rgba(235,235,245,.60);
17+
--label3: rgba(235,235,245,.30);
18+
--sep: rgba(84,84,88,.65);
19+
--blue: #0A84FF;
20+
--green: #30D158;
21+
--orange: #FF9F0A;
22+
--red: #FF453A;
23+
--purple: #BF5AF2;
24+
--font: 'SF Pro Display','SF Pro Text',-apple-system,BlinkMacSystemFont,
25+
'Inter','Helvetica Neue',system-ui,sans-serif;
26+
}
27+
28+
html, body {
29+
background: var(--bg) !important;
30+
color: var(--label) !important;
31+
font-family: var(--font) !important;
32+
letter-spacing: -.01em;
33+
}
34+
* { -webkit-font-smoothing: antialiased; }
35+
::selection { background: rgba(10,132,255,.35); }
36+
a { color: var(--blue); }
37+
38+
/* ---------- Navbar (frosted glass) ---------- */
39+
.navbar-fixed nav, nav, nav.lighten-1 {
40+
background: rgba(28,28,30,.72) !important;
41+
-webkit-backdrop-filter: saturate(180%) blur(20px);
42+
backdrop-filter: saturate(180%) blur(20px);
43+
box-shadow: none !important;
44+
border-bottom: .5px solid var(--sep);
45+
}
46+
nav .brand-logo i, #dash { color: var(--blue) !important; }
47+
48+
/* ---------- Splash card (index + not_found) ---------- */
49+
.container .card-panel, #main-card {
50+
background: var(--elev1) !important;
51+
border: .5px solid var(--sep);
52+
border-radius: 22px !important;
53+
box-shadow: 0 20px 60px rgba(0,0,0,.6) !important;
54+
padding: 56px 44px !important;
55+
}
56+
#dashbots, h1#dashbots, .header#dashbots {
57+
font-family: var(--font) !important;
58+
font-weight: 700 !important;
59+
font-size: 52px !important;
60+
letter-spacing: -.03em !important;
61+
background: linear-gradient(180deg,#fff,#c7c7cc);
62+
-webkit-background-clip: text; background-clip: text;
63+
color: transparent !important;
64+
}
65+
.grey-text { color: var(--label2) !important; }
66+
67+
.my-button {
68+
background: var(--elev2) !important;
69+
color: var(--label) !important;
70+
border: .5px solid var(--sep);
71+
border-radius: 12px !important;
72+
box-shadow: none !important;
73+
text-transform: none !important;
74+
font-weight: 500;
75+
letter-spacing: 0;
76+
margin: 6px;
77+
transition: background .15s, transform .1s;
78+
}
79+
.my-button:hover { background: var(--elev3) !important; }
80+
.my-button:active { transform: scale(.97); }
81+
.row.center a.my-button:first-child { /* lead button -> system blue */
82+
background: var(--blue) !important;
83+
border-color: transparent;
84+
color: #fff !important;
85+
}
86+
87+
/* ---------- Log page: metadata header ---------- */
88+
.entry { max-width: 860px; margin: 0 auto; padding: 0 16px 90px; }
89+
.info {
90+
background: var(--elev1);
91+
border: .5px solid var(--sep);
92+
border-radius: 18px;
93+
padding: 22px 24px;
94+
display: flex; gap: 18px; align-items: center;
95+
margin: 18px 0;
96+
box-shadow: 0 10px 30px rgba(0,0,0,.4);
97+
}
98+
.info__metadata { flex: 1; min-width: 0; }
99+
.info__guild-icon {
100+
width: 64px; height: 64px; border-radius: 50%;
101+
border: .5px solid var(--sep);
102+
}
103+
.info__guild-name { font-size: 21px; font-weight: 600; color: var(--label); }
104+
.info__channel-topic { color: var(--label2); font-size: 14px; margin-top: 4px; }
105+
.info__channel-topic b { color: var(--label); font-weight: 600; }
106+
.info__channel-message-count {
107+
color: var(--label3); font-size: 12px; margin-top: 12px;
108+
text-transform: uppercase; letter-spacing: .04em; font-weight: 600;
109+
}
110+
111+
/* ---------- Log page: chat ---------- */
112+
.chatlog {
113+
background: var(--elev1);
114+
border: .5px solid var(--sep);
115+
border-radius: 18px;
116+
overflow: hidden;
117+
max-width: 860px; margin: 0 auto;
118+
box-shadow: 0 10px 30px rgba(0,0,0,.4);
119+
}
120+
.chatlog__message-group {
121+
display: flex; gap: 14px;
122+
padding: 14px 20px;
123+
margin: 0;
124+
border-top: .5px solid var(--sep);
125+
transition: background .12s;
126+
}
127+
.chatlog__message-group:first-child { border-top: none; }
128+
.chatlog__message-group:hover, .active_hover:hover { background: rgba(255,255,255,.03); }
129+
.perma_hover { background: rgba(10,132,255,.12) !important; }
130+
131+
.chatlog__author-avatar {
132+
width: 40px; height: 40px; border-radius: 50%;
133+
border: .5px solid var(--sep);
134+
}
135+
.chatlog__messages { display: block; flex: 1; min-width: 0; }
136+
.chatlog__author-name { font-weight: 600; font-size: 15px; color: var(--label); }
137+
.chatlog__timestamp { color: var(--label3); font-size: 12px; margin-left: 8px; }
138+
.chatlog__content { font-size: 15px; line-height: 1.46; color: var(--label); margin-top: 3px; }
139+
.chatlog__edited-timestamp { color: var(--label3); font-size: 11px; margin-left: 6px; }
140+
141+
/* role / type tags */
142+
.mod-tag, .internal-tag, .system-tag {
143+
font-size: 10px; font-weight: 700;
144+
padding: 2px 7px; border-radius: 6px;
145+
text-transform: uppercase; letter-spacing: .03em;
146+
margin-left: 8px; vertical-align: middle;
147+
}
148+
.mod-tag { background: rgba(10,132,255,.18); color: var(--blue); }
149+
.internal-tag { background: rgba(255,159,10,.18); color: var(--orange); }
150+
.system-tag { background: rgba(142,142,147,.22); color: var(--label2); }
151+
.anonymous .mod-tag { background: rgba(191,90,242,.18); color: var(--purple); }
152+
153+
/* internal notes get a subtle amber wash + coloured name */
154+
.internal.chatlog__message-group { background: rgba(255,159,10,.07); }
155+
.internal .chatlog__author-name { color: var(--orange); }
156+
157+
/* attachments */
158+
.chatlog__attachment { margin-top: 10px; }
159+
.chatlog__attachment-thumbnail {
160+
max-width: 300px; border-radius: 12px; border: .5px solid var(--sep);
161+
}
162+
.chatlog__attachment a { color: var(--blue); }
163+
164+
/* code blocks */
165+
pre, .pre--multiline {
166+
background: #0d0d10 !important;
167+
border: .5px solid var(--sep);
168+
border-radius: 12px;
169+
}
170+
.pre--inline { background: var(--elev2); border-radius: 6px; padding: 1px 5px; }
171+
172+
/* ---------- Internal-messages toggle (Materialize switch -> iOS toggle) ---- */
173+
.switch label .lever {
174+
background: var(--elev3);
175+
width: 42px; height: 26px; border-radius: 20px; margin: 0 8px;
176+
}
177+
.switch label .lever:after {
178+
width: 22px; height: 22px; top: 2px; left: 2px;
179+
background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.4);
180+
}
181+
.switch label input[type=checkbox]:checked + .lever { background: var(--green); }
182+
.switch label input[type=checkbox]:checked + .lever:after { left: 18px; background: #fff; }
183+
184+
/* tooltips */
185+
.tooltiptext { background: var(--elev3) !important; color: var(--label) !important; border-radius: 8px; }

0 commit comments

Comments
 (0)