fix(theme): 深色改为整套主题变量覆盖,不再改写 data-theme
用户反馈"深色区域有的方形、有的根本没变深"。根因有两层,都是我的做法错了: 1. navi.js 会把 <html data-theme> 改成 "dark"。Dashy 的配色变量是按主题名 作用域输出的(本站主题 jianf-glass),改掉它 → 整套主题变量失配 → 有的区域完全没变深。 2. navi.css 之前只覆盖"页面底色 + 我们自己的悬浮件",Dashy 自己的卡片/侧栏/ 头部仍走原主题 → 与深底色拼在一起,就成了直角圆角混杂。 改法: - navi.js 只切 <html class="navi-dark">(并设 color-scheme),绝不碰 data-theme - navi.css 用 html.navi-dark 一次性覆盖 jianf-glass 的全部配色变量(47 个: 背景/卡片分组/条目文字/侧栏/头部导航/搜索框/设置面板/弹层/滚动条/语义色), Dashy 自身界面随之整体一致变深;形状由元素自身样式决定,不再混杂 - 选择器 html.navi-dark 特异度(0,1,1) 高于 [data-theme=…](0,1,0),与注入顺序无关 需要目视确认配色层次(变量值只能看着调)。
This commit is contained in:
148
deploy/zd-host.sh
Normal file
148
deploy/zd-host.sh
Normal file
@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bash
|
||||
# HomeDash host 模式部署(切换成"单容器即入口"之后使用)
|
||||
#
|
||||
# 为什么不能用 deploy/zd.sh:
|
||||
# zd.sh 靠"改门户 nginx 的上游端口 + 双端口轮换"实现零停机,前提是入口
|
||||
# (portal-nginx)独立于应用容器。切换之后入口就是 navi 容器本身,
|
||||
# host 网络下端口即入口,没有可轮换的第二组端口 —— 那套脚本会失效。
|
||||
#
|
||||
# 本脚本的策略(用户已明确接受数秒闪断):
|
||||
# 构建 → 上传 → 停旧容器 → 起新容器(host 网络)→ 全面校验 → 改名
|
||||
# 任一步失败:删掉新容器、把旧容器改名回去并启动(自动回滚)
|
||||
#
|
||||
# 用法: deploy/zd-host.sh <version>
|
||||
# 环境变量: NAVI_HOST / NAVI_SSH_PASS / NAVI_DATA_DIR / NAVI_CERT_DIR / NAVI_BASE_IMAGE
|
||||
set -euo pipefail
|
||||
|
||||
VER="${1:?用法: deploy/zd-host.sh <version>}"
|
||||
TAG="homedash:ui-${VER}"
|
||||
STAMP="$(date +%Y%m%d%H%M%S)"
|
||||
HOST="${NAVI_HOST:-admin@10.0.0.10}"
|
||||
SSH_PASS="${NAVI_SSH_PASS:-}"
|
||||
DATA_DIR="${NAVI_DATA_DIR:-/vol1/docker/navi-data}"
|
||||
CERT_DIR="${NAVI_CERT_DIR:-/vol1/docker/cert}"
|
||||
BASE_IMAGE="${NAVI_BASE_IMAGE:-jianf-navi:latest}"
|
||||
|
||||
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CTX=/tmp/homedash-ctx
|
||||
TAR="/tmp/homedash-${VER}.tar.gz"
|
||||
REMOTE_TAR="/tmp/homedash-${VER}-${STAMP}.tar.gz"
|
||||
|
||||
SSH_OPTS=(-o StrictHostKeyChecking=no)
|
||||
if [ -n "$SSH_PASS" ]; then
|
||||
SSH=(sshpass -p "$SSH_PASS" ssh "${SSH_OPTS[@]}" "$HOST")
|
||||
SCP=(sshpass -p "$SSH_PASS" scp -q "${SSH_OPTS[@]}")
|
||||
else
|
||||
SSH=(ssh "${SSH_OPTS[@]}" "$HOST")
|
||||
SCP=(scp -q "${SSH_OPTS[@]}")
|
||||
fi
|
||||
|
||||
echo "== 1/3 本地构建 ${TAG}(源码 ${SRC})=="
|
||||
cd "$SRC"
|
||||
node --check web/navi.js
|
||||
(cd admin-server && gofmt -l . && go test ./... >/dev/null)
|
||||
(cd history-server && gofmt -l . && go test ./... >/dev/null)
|
||||
rm -rf "$CTX"; mkdir -p "$CTX/runtime" "$CTX/web/extension" "$CTX/admin" "$CTX/portal"
|
||||
(cd history-server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o "$CTX/runtime/history-server" .)
|
||||
(cd admin-server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o "$CTX/runtime/admin-server" .)
|
||||
cp nginx.conf supervisord.conf searxng-settings.yml conf.example.yml "$CTX/"
|
||||
cp admin/index.html "$CTX/admin/index.html"
|
||||
cp -a portal/. "$CTX/portal/"
|
||||
cp web/extension/install.html "$CTX/web/extension/install.html"
|
||||
cp web/navi.js web/navi.css web/patch-index.py "$CTX/web/"
|
||||
cat > "$CTX/Dockerfile" <<EOF
|
||||
FROM ${BASE_IMAGE}
|
||||
COPY runtime/history-server /usr/local/bin/history-server
|
||||
COPY runtime/admin-server /usr/local/bin/admin-server
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY searxng-settings.yml /etc/searxng/settings.yml
|
||||
COPY conf.example.yml /app/defaults/conf.yml
|
||||
COPY admin/index.html /app/admin/index.html
|
||||
COPY portal /app/portal
|
||||
COPY web/extension/install.html /app/dashy/extension/install.html
|
||||
COPY web/navi.js /app/dashy/navi.js
|
||||
COPY web/navi.css /app/dashy/navi.css
|
||||
COPY web/patch-index.py /tmp/patch-index.py
|
||||
RUN python3 /tmp/patch-index.py /app/dashy/index.html
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=3 \\
|
||||
CMD curl -fs http://localhost:18080/healthz || exit 1
|
||||
EOF
|
||||
DOCKER_BUILDKIT=0 docker build -q -t "$TAG" "$CTX"
|
||||
docker save "$TAG" | gzip -1 > "$TAR"
|
||||
echo " 镜像与包就绪: $(ls -sh "$TAR" | awk '{print $1}')"
|
||||
|
||||
echo "== 2/3 上传 =="
|
||||
"${SCP[@]}" "$TAR" "$HOST:$REMOTE_TAR"
|
||||
|
||||
echo "== 3/3 远端切换(接受数秒闪断,失败自动回滚)=="
|
||||
"${SSH[@]}" "TAG='$TAG' STAMP='$STAMP' REMOTE_TAR='$REMOTE_TAR' \
|
||||
DATA_DIR='$DATA_DIR' CERT_DIR='$CERT_DIR' bash -s" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
|
||||
# 前置检查:必须已经是 host 模式的单容器入口,否则用错脚本会出事
|
||||
NET=$(docker inspect navi --format '{{.HostConfig.NetworkMode}}' 2>/dev/null || echo none)
|
||||
if [ "$NET" != "host" ]; then
|
||||
echo "!! navi 目前是 '$NET' 网络:本脚本只用于切换后的 host 模式单容器" >&2
|
||||
echo " 切换前请用 deploy/zd.sh;或先执行正式切换。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OLD_ID=$(docker inspect navi --format '{{.Id}}')
|
||||
docker inspect navi --format '{{range .Config.Env}}{{println .}}{{end}}' \
|
||||
| grep -E '^(NAVI_ADMIN_TOKEN|NAVI_SYNC_TOKEN|SEARXNG_BASE_URL|PORTAL_|DEBIAN_FRONTEND)' > "/tmp/navi-host-${STAMP}.env" || true
|
||||
chmod 600 "/tmp/navi-host-${STAMP}.env"
|
||||
OLD_IMAGE=$(docker inspect navi --format '{{.Config.Image}}')
|
||||
|
||||
PREV="navi-prev-${STAMP}"
|
||||
fail() {
|
||||
rc=$?
|
||||
echo "!! 失败(rc=$rc)→ 回滚到 ${OLD_IMAGE}" >&2
|
||||
docker rm -f navi-next >/dev/null 2>&1 || true
|
||||
if ! docker inspect "$PREV" >/dev/null 2>&1; then
|
||||
# 还没改名就把原容器起回来
|
||||
docker start navi >/dev/null 2>&1 || true
|
||||
else
|
||||
docker rename "$PREV" navi >/dev/null 2>&1 || true
|
||||
docker start navi >/dev/null 2>&1 || true
|
||||
fi
|
||||
sleep 2
|
||||
echo " 回滚后状态: $(docker inspect navi --format '{{.State.Status}} {{.Config.Image}}' 2>/dev/null)" >&2
|
||||
exit $rc
|
||||
}
|
||||
trap fail ERR
|
||||
|
||||
gzip -dc "$REMOTE_TAR" | docker load >/dev/null
|
||||
|
||||
echo " 停旧容器(闪断开始)"
|
||||
docker stop navi >/dev/null
|
||||
docker rename navi "$PREV"
|
||||
|
||||
docker run -d --name navi-next --restart unless-stopped --network host \
|
||||
-v "${DATA_DIR}:/app/data" -v "${CERT_DIR}:/etc/nginx/certs:ro" \
|
||||
--env-file "/tmp/navi-host-${STAMP}.env" "homedash:ui-${VER}" >/dev/null
|
||||
|
||||
w200() { for _ in $(seq 1 60); do [ "$(curl -s -o /dev/null -w '%{http_code}' -m 3 "$1" 2>/dev/null || true)" = "200" ] && { echo " ready $1"; return 0; }; sleep 2; done; echo " TIMEOUT $1" >&2; return 1; }
|
||||
w200 "http://127.0.0.1:18080/healthz"
|
||||
w200 "http://127.0.0.1:18080/conf.yml"
|
||||
w200 "http://127.0.0.1:18080/admin"
|
||||
|
||||
# 入口自检:受保护域名应 302、公开域名应非 5xx、未知域名应 404(自定义页)
|
||||
CODE=$(curl -sk -o /dev/null -w '%{http_code}' -m 8 --resolve dashy.example.com:3080:127.0.0.1 https://dashy.example.com:3080/conf.yml)
|
||||
[ "$CODE" = "302" ] || { echo "!! 受保护域名应 302,实际 $CODE" >&2; false; }
|
||||
CODE=$(curl -sk -o /dev/null -w '%{http_code}' -m 8 --resolve nobody.test:3080:127.0.0.1 https://nobody.test:3080/)
|
||||
[ "$CODE" = "404" ] || { echo "!! 未知域名应 404,实际 $CODE" >&2; false; }
|
||||
CODE=$(curl -s -o /dev/null -w '%{http_code}' -m 8 -H 'Host: dashy.example.com' http://127.0.0.1:3081/)
|
||||
[ "$CODE" = "302" ] || { echo "!! HTTP 入口应 302 跳转,实际 $CODE" >&2; false; }
|
||||
echo " 入口自检通过(3080/3081)"
|
||||
|
||||
HIST=$(curl -s "http://127.0.0.1:18080/admin/api/history" | grep -o '"url"' | wc -l)
|
||||
echo " 历史条数 ${HIST}(同步接口只累加不删除;与切换前对比由人工确认)"
|
||||
|
||||
docker rename navi-next navi
|
||||
trap - ERR
|
||||
docker rm -f "$PREV" >/dev/null 2>&1 || true
|
||||
rm -f "$REMOTE_TAR"
|
||||
echo "DEPLOY_OK tag=homedash:ui-${VER} mode=host health=$(docker inspect navi --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}')"
|
||||
echo "闪断结束(旧容器已删除;镜像 ${OLD_IMAGE} 仍可用于手动回滚)"
|
||||
REMOTE
|
||||
112
web/navi.css
112
web/navi.css
@ -1350,47 +1350,95 @@ html[data-theme] .navi-footer a {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 深色模式 ──
|
||||
触发条件:<html data-theme="dark">(由 navi.js 按系统偏好设置)。
|
||||
这里只覆盖两类东西:① 页面底色/文字;② 我们自己注入的悬浮控件
|
||||
(原生 Dashy 卡片的观感由主题提供,不在这里重复定义)。
|
||||
选择器都带 body 以压过主题注入的 customCss(同优先级时后者更具体)。 */
|
||||
html[data-theme="dark"] body {
|
||||
/* ── 深色模式(整套变量覆盖)──
|
||||
触发:<html class="navi-dark">(由 navi.js 按系统偏好切换,不改写 data-theme)。
|
||||
|
||||
为什么必须整变量覆盖、而不是按元素打补丁:
|
||||
Dashy 的全部配色都来自主题变量(本站主题 jianf-glass,共 48 个),
|
||||
只改页面底色和几个元素会造成"底色黑了、卡片还是亮的、圆角直角混杂"。
|
||||
这里把同一批变量整体换成深色值,Dashy 自己的卡片/侧栏/头部/弹层随之一致变深,
|
||||
形状(圆角)由元素自身样式决定,不受影响。
|
||||
|
||||
选择器用 html.navi-dark(0,1,1)高于 Dashy 的 [data-theme=...](0,1,0),
|
||||
因此与注入顺序无关,稳定生效。 */
|
||||
html.navi-dark {
|
||||
color-scheme: dark !important;
|
||||
--background: #0b1120;
|
||||
--background-darker: #070d18;
|
||||
--code-editor-background: #0f172a;
|
||||
--code-editor-color: #e2e8f0;
|
||||
--config-code-background: #0f172a;
|
||||
--config-code-color: #e2e8f0;
|
||||
--config-settings-background: rgba(15, 23, 42, 0.92);
|
||||
--config-settings-color: #cbd5e1;
|
||||
--context-menu-background: rgba(15, 23, 42, 0.96);
|
||||
--context-menu-color: #e2e8f0;
|
||||
--danger: #f87171;
|
||||
--footer-text-color: #94a3b8;
|
||||
--footer-text-color-link: #38bdf8;
|
||||
--heading-text-color: #f1f5f9;
|
||||
--highlight-background: #1e40af;
|
||||
--highlight-color: #e2e8f0;
|
||||
--info: #38bdf8;
|
||||
--item-group-background: rgba(17, 25, 40, 0.62);
|
||||
--item-group-heading-text-color: #e8eefc;
|
||||
--item-group-heading-text-color-hover: #7dd3fc;
|
||||
--item-group-outer-background: rgba(17, 25, 40, 0.5);
|
||||
--item-text-color: #dbe4f3;
|
||||
--nav-link-background-color: rgba(30, 41, 59, 0.55);
|
||||
--nav-link-background-color-hover: rgba(30, 41, 59, 0.85);
|
||||
--nav-link-border-color: rgba(148, 163, 184, 0.25);
|
||||
--nav-link-border-color-hover: #38bdf8;
|
||||
--nav-link-text-color: #cbd5e1;
|
||||
--nav-link-text-color-hover: #7dd3fc;
|
||||
--neutral: #94a3b8;
|
||||
--primary: #38bdf8;
|
||||
--primary-alt: #0ea5e9;
|
||||
--scroll-bar-background: #0f172a;
|
||||
--scroll-bar-color: #334155;
|
||||
--search-container-background: rgba(15, 23, 42, 0.6);
|
||||
--search-field-background: rgba(15, 23, 42, 0.8);
|
||||
--settings-background: rgba(15, 23, 42, 0.75);
|
||||
--settings-text-color: #cbd5e1;
|
||||
--side-bar-background: rgba(15, 23, 42, 0.72);
|
||||
--side-bar-color: #cbd5e1;
|
||||
--status-check-tooltip-background: rgba(15, 23, 42, 0.92);
|
||||
--status-check-tooltip-color: #e2e8f0;
|
||||
--success: #4ade80;
|
||||
--toast-background: rgba(15, 23, 42, 0.92);
|
||||
--toast-color: #e2e8f0;
|
||||
--warning: #fbbf24;
|
||||
--welcome-popup-background: rgba(15, 23, 42, 0.88);
|
||||
--welcome-popup-text-color: #e2e8f0;
|
||||
}
|
||||
html.navi-dark body {
|
||||
background-color: #0b1120 !important;
|
||||
color: #e8eefc !important;
|
||||
}
|
||||
html[data-theme="dark"] body .navi-search-bar,
|
||||
html[data-theme="dark"] body .navi-dock,
|
||||
html[data-theme="dark"] body .navi-footer-host,
|
||||
html[data-theme="dark"] body .navi-ws-toolbar,
|
||||
html[data-theme="dark"] body .navi-ws-modal {
|
||||
background: rgba(17, 25, 40, 0.78) !important;
|
||||
/* 我们自己的悬浮件(不在 Dashy 变量体系内,单独给深色) */
|
||||
html.navi-dark body .navi-search-bar,
|
||||
html.navi-dark body .navi-dock,
|
||||
html.navi-dark body .navi-footer-host,
|
||||
html.navi-dark body .navi-ws-toolbar,
|
||||
html.navi-dark body .navi-ws-modal,
|
||||
html.navi-dark body .navi-mini-tabs {
|
||||
background: rgba(17, 25, 40, 0.8) !important;
|
||||
border-color: rgba(148, 163, 184, 0.22) !important;
|
||||
color: #e8eefc !important;
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45) !important;
|
||||
}
|
||||
html[data-theme="dark"] body .navi-search-bar input,
|
||||
html[data-theme="dark"] body .navi-search-bar .navi-search-input {
|
||||
background: rgba(15, 23, 42, 0.6) !important;
|
||||
html.navi-dark body .navi-search-bar input,
|
||||
html.navi-dark body .navi-search-bar .navi-search-input {
|
||||
background: rgba(15, 23, 42, 0.7) !important;
|
||||
color: #e8eefc !important;
|
||||
border-color: rgba(148, 163, 184, 0.25) !important;
|
||||
}
|
||||
html[data-theme="dark"] body .navi-search-bar input::placeholder { color: #94a3b8 !important; }
|
||||
html[data-theme="dark"] body .navi-footer,
|
||||
html[data-theme="dark"] body .navi-footer a,
|
||||
html[data-theme="dark"] body .navi-footer .navi-footer-sep { color: #94a3b8 !important; }
|
||||
html[data-theme="dark"] body .navi-footer a:hover { color: #60a5fa !important; }
|
||||
html[data-theme="dark"] body .navi-dock button,
|
||||
html[data-theme="dark"] body .navi-dock a {
|
||||
background: rgba(30, 41, 59, 0.72) !important;
|
||||
html.navi-dark body .navi-footer,
|
||||
html.navi-dark body .navi-footer a,
|
||||
html.navi-dark body .navi-footer .navi-footer-sep { color: #94a3b8 !important; }
|
||||
html.navi-dark body .navi-footer a:hover { color: #38bdf8 !important; }
|
||||
html.navi-dark body .navi-dock button,
|
||||
html.navi-dark body .navi-dock a {
|
||||
background: rgba(30, 41, 59, 0.75) !important;
|
||||
color: #cbd5e1 !important;
|
||||
border-color: rgba(148, 163, 184, 0.22) !important;
|
||||
}
|
||||
html[data-theme="dark"] body .navi-dock button:hover,
|
||||
html[data-theme="dark"] body .navi-dock a:hover { color: #93bbfd !important; }
|
||||
html[data-theme="dark"] body .minimal-section-inner,
|
||||
html[data-theme="dark"] body div.item-wrapper {
|
||||
background: rgba(17, 25, 40, 0.6) !important;
|
||||
border-color: rgba(148, 163, 184, 0.18) !important;
|
||||
}
|
||||
html[data-theme="dark"] body .navi-mini-tabs { background: rgba(11, 17, 32, 0.85) !important; }
|
||||
|
||||
@ -662,10 +662,12 @@
|
||||
function apply() {
|
||||
var dark = wantDark();
|
||||
var root = document.documentElement;
|
||||
var name = dark ? 'dark' : 'light';
|
||||
if (root.getAttribute('data-theme') !== name) root.setAttribute('data-theme', name);
|
||||
// 注意:绝不能改写 data-theme —— Dashy 按主题名输出变量作用域
|
||||
// (本站主题是 jianf-glass),改掉它会让整套主题变量失配,
|
||||
// 表现就是"有的地方没变深、有的地方直角圆角混杂"。
|
||||
// 我们只加自己的类,深色变量在 navi.css 里整套覆盖。
|
||||
root.classList.toggle('navi-dark', dark);
|
||||
root.style.colorScheme = name;
|
||||
root.style.colorScheme = dark ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
apply();
|
||||
|
||||
Reference in New Issue
Block a user