mirror of
https://github.com/Gloridust/WechatOnCloud.git
synced 2026-08-30 18:21:27 -07:00
fix(dark): 撤掉不稳定的容器 portal 方案,浏览器深色回到 --force-dark-mode
实测极简 KasmVNC 容器里 portal 方案不可靠:autostart 起的 session dbus 会死 (gsettings 提交报 Connection refused),微信内核也不订阅 portal 的 color-scheme 信号, 所以微信不会变深、浏览器的"实时切换"也落空。故回到简单可靠的做法: - 浏览器(Chromium):WOC_DARK=1 时 autostart 加 --force-dark-mode(开机即深,重启实例生效)。 - 微信:本开关无效,深色请在微信「设置→通用→深色模式」手动选。 - 移除:Dockerfile 的 xdg-desktop-portal/dconf 等包、autostart 的 portal 段、woc-dark.sh、 docker.ts 的 setInstanceDark、index.ts 的在线切换循环。面板开关仅持久化 desktopDark(驱动开机明暗)。 去掉每实例常驻的 dbus/portal,也消除其对稳定性的潜在影响。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -91,9 +91,9 @@ WOC_INSTANCE_MEM_GB=
|
||||
# 说明:设备伪装是「尽力而为」,非保证不被封;详见 doc/设备伪装.md。
|
||||
WOC_SPOOF_OS=1
|
||||
|
||||
# 实例桌面深色:无需在此配置。面板顶栏右上角的主题开关(管理员)统一控制「面板 + 实例桌面」深色,
|
||||
# 设置持久化在面板数据(accounts.json),并对运行中的实例实时切换(微信内核是 Chromium,跟随容器内
|
||||
# xdg-desktop-portal 的 color-scheme 信号)。
|
||||
# 实例桌面深色:无需在此配置。面板顶栏右上角的主题开关(管理员)控制,设置持久化在面板数据。
|
||||
# 仅对**浏览器(Chromium)实例**生效(--force-dark-mode,重启实例后生效);微信不吃命令行参数、也不跟随
|
||||
# 极简容器的桌面信号,微信深色请在微信「设置→通用→深色模式」里手动选(随数据卷持久化)。
|
||||
|
||||
# ── 自愈 watchdog(应对 KasmVNC/Xvnc 长跑内存泄漏) ───────────
|
||||
# 实测 Xvnc 长跑 24h 可膨胀到 ~9GiB,原因在 KasmVNC/Xvnc 自身的 framebuffer / 软渲染 cache
|
||||
|
||||
@@ -52,22 +52,6 @@ RUN set -eux; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 深色模式支持(仅在实例环境 WOC_DARK=1 时由 autostart 启用)。微信内核是 Chromium,它判断"系统是否深色"
|
||||
# 读的是 freedesktop 桌面 portal 的 org.freedesktop.appearance→color-scheme(D-Bus 信号),而非 GTK_THEME。
|
||||
# 极简 KasmVNC 镜像默认没有 portal,故装上 xdg-desktop-portal + gtk 后端 + dconf/gsettings:autostart 据
|
||||
# WOC_DARK 跑一条 session 总线、把 color-scheme 设为 prefer-dark 并拉起 portal,微信启动即跟随系统深色。
|
||||
# 单独成层,避免动到上面缓存的安装层;关闭深色时这些服务完全不启动,零影响旧有稳定行为。
|
||||
RUN set -eux; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
dbus-x11 \
|
||||
xdg-desktop-portal \
|
||||
xdg-desktop-portal-gtk \
|
||||
dconf-service dconf-gsettings-backend \
|
||||
gsettings-desktop-schemas; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV LANG=zh_CN.UTF-8 \
|
||||
LC_ALL=zh_CN.UTF-8 \
|
||||
LIBGL_ALWAYS_SOFTWARE=1
|
||||
@@ -86,11 +70,6 @@ RUN chmod 755 /woc/woc-www-patch.sh && chmod 644 /woc/woc-ime.pl && /woc/woc-www
|
||||
COPY wechat-ctl.sh /woc/wechat-ctl.sh
|
||||
RUN chmod 755 /woc/wechat-ctl.sh
|
||||
|
||||
# 深色模式实时切换脚本:面板顶栏主题开关 → 经 docker exec 调用,改 gsettings color-scheme,
|
||||
# portal 通知微信实时重绘(见 woc-dark.sh / autostart 的 portal 段)
|
||||
COPY woc-dark.sh /woc/woc-dark.sh
|
||||
RUN chmod 755 /woc/woc-dark.sh
|
||||
|
||||
# v1.2.0 多应用:应用定义(app-defs.sh,被 autostart/app-ctl 引用)+ 通用安装/状态控制(app-ctl.sh,
|
||||
# 微信委托回 wechat-ctl.sh、其它应用各自实现)。app-defs.sh 是被 source 的、不需可执行位。
|
||||
COPY app-defs.sh app-ctl.sh /woc/
|
||||
|
||||
@@ -18,62 +18,15 @@ APP_TYPE="${WOC_APP_TYPE:-wechat}"
|
||||
. /woc/app-defs.sh
|
||||
woc_app_def "$APP_TYPE"
|
||||
|
||||
# 深色模式(freedesktop 桌面 portal 方案)。微信内核是 Chromium,它判断"系统是否深色"读的是 portal 的
|
||||
# org.freedesktop.appearance→color-scheme(D-Bus 信号),不是 GTK_THEME/settings.ini(那只影响纯 GTK chrome)。
|
||||
# 极简 KasmVNC 默认无 portal,故每个实例都常驻一套 session D-Bus + xdg-desktop-portal(gtk 后端) + dconf:
|
||||
# - 这样面板顶栏的主题开关能"远程拨开关"实时切换实例深色(面板 docker exec 改 gsettings color-scheme →
|
||||
# portal 发 SettingChanged → 微信实时重绘),无需重启实例;
|
||||
# - 初始明暗由 WOC_DARK 决定(面板把开关持久化、经容器环境下发),微信由本脚本拉起、继承这套环境,启动即跟随。
|
||||
# 这些服务空闲极轻、且与 VNC 连接完全无关(不参与重连/重启),不影响连接稳定性。总线地址固定,便于 exec 复用。
|
||||
# 整段 best-effort:任一步失败都不阻断后续启动应用。
|
||||
woc_setup_portal() {
|
||||
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/tmp/woc-run-$(id -u)}"
|
||||
mkdir -p "$XDG_RUNTIME_DIR" 2>/dev/null && chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null || true
|
||||
# 让 xdg-desktop-portal 明确选用 gtk 后端来实现 Settings(即 color-scheme 来源)
|
||||
export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:-GNOME}"
|
||||
_xcfg="${HOME:-/config}/.config/xdg-desktop-portal"
|
||||
mkdir -p "$_xcfg" 2>/dev/null || true
|
||||
printf '[preferred]\ndefault=gtk\norg.freedesktop.impl.portal.Settings=gtk\n' >"$_xcfg/portals.conf" 2>/dev/null || true
|
||||
# 注意:不要 export GTK_THEME=Adwaita:dark —— 那是静态环境变量、会让 GTK 程序恒为深色,无法跟随开关切回浅色。
|
||||
# 明暗一律走 gsettings(dconf,可被 woc-dark.sh 实时改),见下。
|
||||
# 固定地址的 session 总线:微信(本脚本拉起,继承此变量)与面板 docker exec 的 woc-dark.sh 用同一条,
|
||||
# 改的才是同一个 dconf、portal 才读得到。已在跑(socket 存在)则复用。
|
||||
export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/woc-bus"
|
||||
if [ ! -S "${XDG_RUNTIME_DIR}/woc-bus" ] && command -v dbus-daemon >/dev/null 2>&1; then
|
||||
dbus-daemon --session --address="$DBUS_SESSION_BUS_ADDRESS" --nopidfile --fork 2>/dev/null || true
|
||||
_i=0; while [ ! -S "${XDG_RUNTIME_DIR}/woc-bus" ] && [ "$_i" -lt 25 ]; do sleep 0.2; _i=$((_i + 1)); done
|
||||
# 深色模式(实例环境 WOC_DARK=1 时;由面板顶栏主题开关持久化、经容器环境下发,改后需重启实例生效)。
|
||||
# 仅浏览器(Chromium):加 --force-dark-mode 让其 UI + 网页走深色——这是极简容器里实测唯一稳的办法。
|
||||
# 微信:内核虽是 Chromium 但不吃命令行参数,也不订阅桌面 portal 的 color-scheme 信号(实测 portal 方案在
|
||||
# 极简容器里 session 总线不稳、微信也不跟随),故本开关对微信无效;微信深色请在微信「设置→通用→深色模式」
|
||||
# 里手动选(设置随数据卷持久化)。
|
||||
if { [ "${WOC_DARK:-}" = "1" ] || [ "${WOC_DARK:-}" = "true" ]; } && [ "$APP_TYPE" = "chromium" ]; then
|
||||
APP_LAUNCH="${APP_LAUNCH} --force-dark-mode --enable-features=WebContentsForceDark"
|
||||
echo "[autostart] 浏览器深色已启用(--force-dark-mode)"
|
||||
fi
|
||||
[ -S "${XDG_RUNTIME_DIR}/woc-bus" ] || { echo "[autostart] 深色:session 总线未就绪,跳过 portal"; return 0; }
|
||||
# 落一份真实总线环境,供面板 docker exec 的 woc-dark.sh 复用。关键:容器内 XDG_RUNTIME_DIR 由 base 镜像设为
|
||||
# /config/.XDG,而 docker exec 不会继承会话环境;若 woc-dark.sh 自行猜测就会连错总线,改了 dconf 也通知不到
|
||||
# 本进程拉起的 portal(实测过的坑)。故这里把实际地址写进文件,woc-dark.sh 直接 source。
|
||||
printf 'export XDG_RUNTIME_DIR=%s\nexport DBUS_SESSION_BUS_ADDRESS=%s\n' \
|
||||
"$XDG_RUNTIME_DIR" "$DBUS_SESSION_BUS_ADDRESS" >"${HOME:-/config}/.woc-dark-env" 2>/dev/null || true
|
||||
# 把环境推进总线的"激活环境",使 dbus 激活起来的 portal 能看到 DISPLAY/XDG_CURRENT_DESKTOP 等
|
||||
command -v dbus-update-activation-environment >/dev/null 2>&1 && \
|
||||
dbus-update-activation-environment --all >/dev/null 2>&1 || true
|
||||
# 初始明暗(dconf 共享后端,portal 据此对外报告;之后面板 woc-dark.sh 改这同一个键即实时切换)
|
||||
if command -v gsettings >/dev/null 2>&1; then
|
||||
if [ "${WOC_DARK:-}" = "1" ] || [ "${WOC_DARK:-}" = "true" ]; then
|
||||
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' 2>/dev/null || true
|
||||
gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita-dark' 2>/dev/null || true
|
||||
else
|
||||
gsettings set org.gnome.desktop.interface color-scheme 'default' 2>/dev/null || true
|
||||
gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita' 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
# 显式拉起 portal(gtk 后端提供 Settings;比纯 dbus 激活更稳,重复启动会自行退出、无害)
|
||||
for _b in xdg-desktop-portal-gtk xdg-desktop-portal; do
|
||||
for _d in /usr/libexec /usr/lib/xdg-desktop-portal /usr/lib; do
|
||||
if [ -x "$_d/$_b" ]; then ( "$_d/$_b" >/dev/null 2>&1 & ); break; fi
|
||||
done
|
||||
done
|
||||
echo "[autostart] 深色 portal 已就绪(初始 $([ "${WOC_DARK:-}" = "1" ] || [ "${WOC_DARK:-}" = "true" ] && echo prefer-dark || echo light))"
|
||||
}
|
||||
woc_setup_portal || true
|
||||
# 注意:不给 Chromium 加 --force-dark-mode —— 那是启动期固定开关,会让浏览器恒为深色、无法跟随主题切回浅色
|
||||
# (这正是"切浅色没反应、必须重启"的原因)。改为同微信一样纯靠 portal 的 color-scheme/gtk-theme 信号,
|
||||
# 浏览器(stock Chromium 原生支持 portal)即可实时双向跟随。
|
||||
|
||||
# 防“最小化后丢失”:本桌面(openbox)无任务栏,窗口被最小化就无处恢复 → 黑屏。
|
||||
# 看守进程:每 2s 把“被最小化(不可见)”的顶层窗口重新激活,相当于禁用最小化。对任意应用通用。
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 实时切换本实例桌面深色(由面板经 docker exec 以 app 用户身份调用:woc-dark.sh on|off)。
|
||||
# 原理:改 GNOME color-scheme(dconf 共享后端)→ 与 autostart 同一条 session 总线上的 xdg-desktop-portal
|
||||
# 收到变化、对外发 org.freedesktop.appearance→color-scheme 的 SettingChanged → 微信等 Chromium 系应用实时重绘。
|
||||
# 须与 autostart 用完全相同的 XDG_RUNTIME_DIR / 总线地址,否则改的不是同一个 dconf、portal 读不到。
|
||||
set -u
|
||||
|
||||
# 必须用与 autostart 完全相同的总线,否则改了 dconf 也通知不到那条总线上的 portal(微信/浏览器就不会实时变)。
|
||||
# autostart 已把真实 XDG_RUNTIME_DIR / DBUS_SESSION_BUS_ADDRESS 落到 /config/.woc-dark-env(docker exec 不继承
|
||||
# 会话环境,不能自行猜测——base 镜像里 XDG_RUNTIME_DIR 是 /config/.XDG 而非 /tmp/woc-run-<uid>)。
|
||||
if [ -f /config/.woc-dark-env ]; then
|
||||
# shellcheck source=/dev/null
|
||||
. /config/.woc-dark-env
|
||||
fi
|
||||
: "${XDG_RUNTIME_DIR:=/config/.XDG}"
|
||||
: "${DBUS_SESSION_BUS_ADDRESS:=unix:path=${XDG_RUNTIME_DIR}/woc-bus}"
|
||||
export XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS
|
||||
|
||||
case "${1:-}" in
|
||||
on | 1 | dark | true)
|
||||
scheme='prefer-dark'
|
||||
gtk='Adwaita-dark'
|
||||
;;
|
||||
*)
|
||||
scheme='default'
|
||||
gtk='Adwaita'
|
||||
;;
|
||||
esac
|
||||
|
||||
command -v gsettings >/dev/null 2>&1 || {
|
||||
echo "[woc-dark] 容器内无 gsettings(镜像未升级到含 portal 的版本),跳过;重启实例即用新镜像生效" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
gsettings set org.gnome.desktop.interface color-scheme "$scheme" 2>/dev/null || {
|
||||
echo "[woc-dark] 设置 color-scheme 失败(portal/总线可能未就绪),可重启实例" >&2
|
||||
exit 1
|
||||
}
|
||||
gsettings set org.gnome.desktop.interface gtk-theme "$gtk" 2>/dev/null || true
|
||||
echo "[woc-dark] color-scheme=${scheme}"
|
||||
@@ -430,13 +430,6 @@ async function execCapture(inst: Instance, cmd: string[]): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
// 实时切换运行中实例的桌面深色:以 app 用户身份执行 /woc/woc-dark.sh on|off,它经同一条 session 总线
|
||||
// 改 gsettings color-scheme → portal 发 SettingChanged → 微信等实时重绘。容器须为升级后镜像(含该脚本与
|
||||
// portal);旧镜像没有脚本会报错,调用方按"best-effort"忽略即可(重启实例后即用新镜像、自动跟随)。
|
||||
export async function setInstanceDark(inst: Instance, dark: boolean): Promise<void> {
|
||||
await execCapture(inst, ['/woc/woc-dark.sh', dark ? 'on' : 'off']);
|
||||
}
|
||||
|
||||
// 触发下载/安装(detached,立即返回,后台下载)。按实例 appType 分发:app-ctl.sh wechat → 委托回
|
||||
// wechat-ctl.sh;telegram 等各自实现。兼容旧容器(升级前镜像里没有 /woc/app-ctl.sh):有则用之,无则
|
||||
// 回退老的 wechat-ctl.sh(旧实例都是微信)。appType 取值受 instanceAppType 约束,可安全内插进 shell。
|
||||
|
||||
@@ -44,7 +44,6 @@ import {
|
||||
upgradeInstance,
|
||||
removeInstance as removeInstanceContainer,
|
||||
instanceRuntime,
|
||||
setInstanceDark,
|
||||
triggerWechat,
|
||||
wechatStatus,
|
||||
instanceTarget,
|
||||
@@ -209,29 +208,15 @@ app.get('/api/desktop-theme', async (req, reply) => {
|
||||
if (!requireAuth(req, reply)) return;
|
||||
return { dark: getDesktopDark() };
|
||||
});
|
||||
// 设置实例深色(管理员)。面板顶栏主题开关切到 深/浅 时调用:持久化 → 对所有运行中的实例实时切换
|
||||
// (docker exec woc-dark.sh)。实时切换为 best-effort:个别实例失败(如旧镜像无该脚本)不影响其余,
|
||||
// 重启该实例后即用新镜像、按持久化的初始明暗启动。
|
||||
// 设置实例深色(管理员)。面板顶栏主题开关切到 深/浅 时调用:持久化即可。它作为浏览器(Chromium)实例
|
||||
// 启动时的明暗(经 envList → WOC_DARK 下发,autostart 据此加 --force-dark-mode),故**重启实例后生效**,
|
||||
// 不做在线切换(极简容器内无稳定的桌面 portal,微信也不跟随,详见 docker/autostart 注释)。
|
||||
app.post('/api/admin/desktop-theme', async (req, reply) => {
|
||||
if (!requireAdmin(req, reply)) return;
|
||||
const dark = !!(req.body as any)?.dark;
|
||||
setDesktopDark(dark);
|
||||
let applied = 0;
|
||||
let failed = 0;
|
||||
const insts = listInstances();
|
||||
await Promise.all(
|
||||
insts.map(async (inst) => {
|
||||
try {
|
||||
if ((await instanceRuntime(inst)) !== 'running') return;
|
||||
await setInstanceDark(inst, dark);
|
||||
applied++;
|
||||
} catch {
|
||||
failed++; // 旧镜像/总线未就绪等:忽略,重启实例即生效
|
||||
}
|
||||
}),
|
||||
);
|
||||
appendPanelLog('INFO', `实例深色设为 ${dark ? '深色' : '浅色'}(实时应用 ${applied} 个,${failed} 个待重启生效)`);
|
||||
return { ok: true, dark, applied, failed };
|
||||
appendPanelLog('INFO', `实例深色设为 ${dark ? '深色' : '浅色'}(浏览器实例重启后生效)`);
|
||||
return { ok: true, dark };
|
||||
});
|
||||
|
||||
// ---------- 自助改密 ----------
|
||||
|
||||
@@ -315,7 +315,7 @@ function ThemeToggle() {
|
||||
};
|
||||
const label = mode === 'auto' ? '跟随系统' : mode === 'light' ? '亮色' : '深色';
|
||||
const hint = isAdmin
|
||||
? `主题:${label}(同时切换面板与实例桌面深色;点击循环:跟随系统 / 亮色 / 深色)`
|
||||
? `主题:${label}(面板即时换肤;浏览器实例重启后跟随。点击循环:跟随系统 / 亮色 / 深色)`
|
||||
: `主题:${label}(点击切换:跟随系统 / 亮色 / 深色)`;
|
||||
return (
|
||||
<button className="theme-toggle" onClick={cycle} title={hint} aria-label={`主题:${label}`}>
|
||||
|
||||
Reference in New Issue
Block a user