Commit Graph

125 Commits

Author SHA1 Message Date
c77a1795a3 fix(ui): 页脚改回固定贴底(文档流方案会让页脚掉出视口)+ 内容留净空
用户实测反馈三点,经真实视口复现确认:
① /workspace 不滚动时页脚 bottom=737 > 视口 720 → 第二行(豫公网安备/无谓前路之沧桑)
   落在视口外,且页面被撑高 → '工作区整体能滑动'、'底栏只显示备案号';
② 文档流方案(v81-v83)在视口较矮或内容较多时页脚必然掉出视口,
   用户评价'不如上一版'—— 因为上一版是固定层,任何视口都可见;
③ 我此前用'距文档底=0px'当成功判据,但那只证明它是文档最后一项,
   **不能证明它在视口内可见** —— 判据再次用错。

本版:恢复 position:fixed/bottom:0(同 v76 做法),
并成对给内容容器留出 44px 底部净空避免遮挡;
固定层不参与文档高度,避免页面被撑出滚动条。
2026-09-12 19:53:47 +08:00
cb82a938f6 fix(ui): 页脚贴住页面最底 —— 消除 #app 吞掉剩余空间
实测(滚到最底时的 host.bottom / 文档高):
  /         720 / 720  → 0px ✓
  /minimal  717 / 720  → 余 3px
  /workspace 674 / 720 → **余 46px**
根因:body 为 display:flex/column,其中 #app 是 flex:1 1 auto,
吃掉全部剩余高度,使页脚只能排在 #app 之后的中途位置。
修法:body 直接含 .navi-footer-host 时令 #app 改为 flex:0 0 auto(不再伸展),
页脚用 margin-top:auto 落到最底;并清掉 minimal 内容链末尾的余量。
2026-09-12 19:38:12 +08:00
b017c3a4b8 fix(ui): 三视图页脚真正落到页面最底 + workspace 的 grid 生效
父链实测(v81,三个视图结构不同):
  /          .navi-footer[grid] < FOOTER[pb:8px] < #dashy < #app < BODY
  /minimal   .navi-footer[grid] < .navi-footer-host[flex] < BODY
  /workspace .navi-footer[flex ✗] < .navi-footer-host[flex] < BODY
问题:① / 多出 8px 空隙(来自 <footer> 的 padding-bottom);
     ② /workspace 页脚停在 y=624,下方仍有 80px 空;
     ③ /workspace 页脚未被 grid 规则命中(写成 flex),四子项挤在同一行。
改动:
 - 补 body > .navi-footer-host > .navi-footer 的 grid 规则与 2×2 显式定位
   (覆盖 host 直接挂 body 的视图);
 - body > .navi-footer-host 用 margin-top:auto 推到页面最底、不留空;
 - 首页清掉 #dashy > footer 的 padding-bottom(8px 空隙来源)。
选择器均按实测父链结构编写,非猜测。
2026-09-12 19:10:53 +08:00
a2bead912f fix(ui): 页脚回归文档流(用户要求:位于页面底部,而非悬浮于底部)
实测 v80:三视图 .navi-footer-host/.navi-footer 均 position:fixed + z-index:3,
是永远贴住视口、盖在内容之上的悬浮层(/workspace 还因此底部出血 8px)。
用户明确要求页脚应是**页面底部的内容**,滚到最底才出现,不悬在内容上。

改动:host 与 .navi-footer 一律 position:static(回到正常文档流),
取消 z-index、取消为固定页脚预留的 margin-bottom(26/40px),
pointer-events 恢复 auto(不再是浮层无需穿透),
透明/无边框/无模糊保持不变。
2026-09-12 19:00:20 +08:00
e3770cd10a fix(ui): 修 v79 页脚三行问题(隐藏分隔符仍占网格格位)
实测 v79:grid 算成 3 行(y=671/688/705),左列多出一行。
原因:.navi-footer-sep 虽设 display:none,但被更高特异性规则压过,
仍占据 grid 第 2 行格位,把「豫公网安备」挤到第 3 行。
修法:同级别选择器彻底隐藏分隔符(display:none + 绝对定位移出流外),
并用 grid-column/grid-row 显式定位:左列 1/2 行 = 两个备案号,
右列 1/2 行 = 两句座右铭;grid-auto-rows:0 防止隐式行撑高。
2026-09-12 18:54:52 +08:00
38467a1724 feat(ui): 页脚改为「左两行备案 / 右两行一言」,一言换成用户座右铭
用户要求:
① 左侧两行 = 两个备案号;右侧两行 = 一言两句;
② 一言不用随机 API(原 hitokoto + 随机兜底,观感中二且不可控),
   改为固定座右铭「不念往昔之繁华 / 无谓前路之沧桑」。

实现:
- navi.js:installDailyQuote 重写为固定文案,移除 fetch(hitokoto) 与随机 FALLBACK。
- navi.css:.navi-footer 改 CSS Grid(2 列 × 2 行 + grid-auto-flow:column),
  使「备案A / 备案B」落入左列两行、「quote1 / quote2」落入右列两行,
  一言列右对齐;隐藏原分隔符;内容框底部预留提升到 40px 以容纳两行页脚。
2026-09-12 18:51:19 +08:00
8cc94e91f0 fix(ui): 页脚文字加深 —— 备案号 #94a3b8→#475569、一言 #b6c2d2→#64748b
用户反馈「文字颜色过浅」。实测前景色与亮度:
  备案号/分隔符 rgb(148,163,184)=#94a3b8(亮度 160)
  每日一言     rgb(182,194,210)=#b6c2d2(亮度 187)
均为 11px 小字 + 透明背景(叠在浅色页面底图上),对比度偏低、观感发虚。
改为中性 slate:#475569(对白底约 8.9:1)、一言 #64748b;
并去掉一言的斜体(小字号下进一步降低可读性);hover 加深为 #1e293b + 下划线。
深色主题改用 #cbd5e1 / #94a3b8 保持可读。
2026-09-12 18:35:56 +08:00
175788e4eb fix(ui): 首页/工作台页脚对齐 —— 首页固定贴底、workspace 不再出血
复核发现(同一套判据,1280×720):
① /:.navi-footer 位于 Dashy <footer> 内、position:static、bottomPx=1092,
   页脚在文档流里 → 必须滚动才可见('备案号各视图可见'被破坏);
   且该视图无 .navi-footer-host 包裹层,结构与其他视图不一致。
② /workspace:.navi-footer-host 虽 fixed bottom:0,但 bottomPx=733 > 视口 720,
   底部约 13px 出血。

修法:
- 首页:对 footer > .navi-footer 直接固定贴底(flex row + 两端分布 + 透明一行),
  不改动 DOM 结构;
- workspace:显式 bottom:0 并限制 footer 高度上限 20px。
两视图的悬浮按钮经复核已是 idle α=0、无阴影(v75 已修),菜单在 / 上可点且真跳转。
2026-09-12 18:20:15 +08:00
38ba5584f0 fix(ui): 页脚被内容链规则压成竖排 —— 排除 .navi-footer-host/.navi-footer
几何实测(v75):.navi-footer display=flex 但 flex-direction=column,
5 个子项竖排 y=694/715/736/757/778,每日一言 h=0 且落在视口外;
host overflow=hidden。
根因:那两条 body:has(.minimal-home) > div / > div > div 规则命中 .navi-footer,
使其成为 column + overflow:hidden 的容器。
v71 的 row 规则特异性 (0,4,2) 本可压过原规则 (0,2,4),
但 v74 为修「菜单被裁剪」给该规则加了 3 个 :not(),
特异性升到 (0,5,4),反而压过了页脚规则 —— 同一个 :not() 修好菜单、踩坏页脚。
修法:把 .navi-footer-host / .navi-footer 也加入 :not() 排除
(两者均为固定定位,本就不应参与该压缩链)。
2026-09-12 18:07:07 +08:00
c299a666f6 fix(ui): 删除 v68 引入的按钮灰底(源头),不再叠加覆盖
用户反馈:两个悬浮按钮下面都有方形灰色背景,点击时背景还会高亮。
来源即本段(v68 为不再显灰而加的 rgba(255,255,255,0.92) + 描边 + 投影)。
v73 试图改成透明未生效,因为该段选择器 html[data-theme]:not(.navi-dark) .navi-*
特异性 (0,3,1) 高于 v73 的 (0,2,2) —— 又一次被特异性反超。
故直接删除本段,按钮回到既有设计:idle 透明、仅 hover/focus-visible 圆形淡染。
2026-09-12 18:05:05 +08:00
b967dd409e fix(ui): 源头修复「切换视图菜单被裁剪」—— 排除导航控件子树
真因(已定位到具体规则与行号):
 navi.css 729-730 行(v60 为修 flex 压缩链而写)
   html[data-theme] body:has(.minimal-home) > div,
   html[data-theme] body:has(.minimal-home) > div > div { … overflow: hidden !important }
 匹配了 body 下所有 div 与 div>div,因此:
   .navi-dock(body 直接子元素)、.navi-view-switch 也被设成
   overflow:hidden + display:flex + column + max-height:100%。
 菜单(.navi-view-menu)是 .navi-view-switch 的子元素,超出 52×52 盒的部分被裁剪,
 只露一条缝:像素比对证明它在渲染,但中心 elementFromPoint 命中 #dashy,
 三个选项全部点不到 → 用户观感点了没反应,残留碎片即方形灰色背景。
 先前 v73 的覆盖选择器特异性不足(0,2,2 < 该规则的 0,2,3 起),故无效。

修法:在该规则上加 :not(.navi-dock):not(.navi-view-switch):not(.navi-view-menu),
不动其它内容链行为。配套 v73 的 overflow:visible/z-index:1000 现已生效。
2026-09-12 18:03:00 +08:00
d7cada953c fix(ui): 修切换视图菜单被 overflow 裁剪('点了没反应'真因)+ 按钮去灰底板
真因(两条独立证据 + 像素证据):
- 祖先链:.navi-view-switch{overflow:hidden} → .navi-dock{overflow:hidden};
- 菜单开/关截图像素比对有变化(确实在渲染),但菜单中心的 elementFromPoint
  命中 #dashy → 超出 52×52 按钮盒的面板被裁剪,看不见也点不到;
- 用户所称方形灰色背景即被裁剪后残留的菜单面板碎片。
此前我误判它'可见',因为 getBoundingClientRect 不受裁剪影响 —— 判据已改为像素比对 + 命中测试。

改动:.navi-dock/.navi-view-switch overflow:visible;.navi-view-menu z-index:1000 +
pointer-events:auto;三个悬浮按钮去掉背景/描边/阴影(透明圆形),
仅在 hover/focus-visible 时给一层圆形淡染,点击不再出现方块高亮。
2026-09-12 18:00:28 +08:00
26b3bac329 feat(ui): 页脚左右分栏——备案号靠左并列、每日一言靠右
需求(用户):备案号并列在左侧,一言在右侧。
结构不变(备案 A / sep / 公安备案 A / sep / .navi-quote),用
.navi-quote { margin-left: auto } 把一言推到右端,其余靠左排列;
宽度改为 100%(上限 calc(100vw - 32px))以便两端分布。
隐藏紧邻一言的那条分隔符(左右分栏后不再需要)。
沿用 v71 的同特异性选择器,避免被中间重复定义覆盖。
2026-09-12 17:56:56 +08:00
0cd0d253d3 fix(ui): 页脚补宽度约束 + 更正上一提交的根因叙述
更正:上一提交称 824 行 'display: block' 压制了 flex —— 实际读该行是
  .navi-footer-host > * { pointer-events: auto }
设的是 pointer-events,与 display 无关。真实成因是 **折行**:
页脚宽度实测仅约 192px(内容需约 470px),flex-wrap 生效下折成 3~4 行,
总高约 63px(与 scrollHeight=65 吻合),第 4 行的每日一言被排到 y=743,
超出视口 720 → 不可见。
本版补 width: max-content + max-width: calc(100vw - 24px),
配合 v70 的 flex-wrap: nowrap,使四个子项落在同一行。
判据仍为 y>=0 且 bottom<=innerHeight(元素有尺寸但位于视口外不算可见)。
2026-09-12 17:47:17 +08:00
ab2e514e00 fix(ui): 页脚真凶——同特异性覆盖 .navi-footer 的 display,恢复横排一行
几何实测(v69):.navi-footer 运行时 display=block,四个子元素纵向堆叠
(y=680/701/722/743),盒高 26px 而已 bottom=720 →
每日一言在视口下方(y=743 > 720),**根本没有显示**。
此前我改高度、行高、加 !important、挪到文件末尾全部无效,
因为根因是 824 行的
  html[data-theme] body:has(.minimal-home) .navi-footer-host > * { display: block !important }
—— 特异性 (0,2,1) 高于我的 (0,1,1):同等 !important 下按特异性取胜,与顺序无关。

修法:用同特异性选择器(.navi-footer-host > .navi-footer)覆盖为
display:flex + flex-direction:row + nowrap,子项高度 auto,每日一言省略号。

判据修正(此前两次误判为成功):
不再只看 height>0,而要求 y>=0 且 bottom<=innerHeight —— 元素有尺寸但在视口外仍属不可见。
2026-09-12 17:47:01 +08:00
7f6bec43f2 fix(ui): 撤销 v68 页脚固定行盒(倒退),恢复 v67 形态,保留按钮底色改良
v68 实测为倒退:页脚 scrollHeight 65 / clientHeight 26,每日一言被挤出到高度 0
(v67 时它 13px 可见)。撤销 20px 行盒相关规则,回到 v67 页脚形态;
保留同时交付的按钮样式(0.92 中性亮面 + 描边 + 柔投影),该改动实测有效。
2026-09-12 17:45:04 +08:00
65138f7d83 fix(ui): 页脚固定 20px 行盒消除 1-2px 裁切;底部按钮改中性亮面不再显灰
- v67 遗留:.navi-footer 高 26px 但 scrollHeight 超出 clientHeight 1-2px(下缘被切)。
  改为 20/20/20 + line-height:20px + overflow:visible,单行行盒不再溢出。
- 浅色主题下 .navi-view-toggle/.navi-admin-entry 底色 rgba(255,255,255,.72)
  叠在浅背景上观感发灰 → 改 0.92 亮面 + 中性描边 + 柔投影,hover 纯白,
  focus-visible 用中性色轮廓(不引入蓝色)。深色主题规则不变。
- 视图切换保持点开弹菜单(用户明确选择),未改动其交互。
2026-09-12 17:42:51 +08:00
e84d51f747 fix(ui): 页脚最终覆盖移到文件末尾(此前被同优先级规则按级联顺序盖掉)
v66 实测:重叠已消除(-19/-83px)、底部控件全部可点(命中自身),
但页脚背景仍是 rgba(255,255,255,.55) + blur(14px)、每日一言 height:0 ——
因为 navi.css 里 .navi-footer 有多处重复定义(1016/1447/1515…),
我插在中间的规则被后面同优先级规则覆盖。

本提交把页脚最终形态(透明、无边框/模糊、一行、26px host + 20px footer)
统一追加到文件**末尾**,并给每日一言 display:inline-block + min-height:13px,
确保它参与行盒、有实际高度而不会被裁成 0。
2026-09-12 17:30:51 +08:00
cb0f8111d8 fix(ui): 页脚改为"真正一行 + 布局预留",消除遮挡与裁切(第三次,纠正前两版)
实测故障(1280×720):
  .navi-footer-host y=598 h=123 fixed;.navi-footer 209×93 半透明 + overflow:hidden;
  内容区底=701 → 与页脚重叠约 103px。那块半透明白块既是"灰色遮罩"又挡住底部点击。

与前两次的区别(前两次的错):
- v63:只改字号/背景,页脚仍 113px 高(另有规则撑高),没解决遮挡
- v64:用 height:22px + overflow:hidden 硬压 → **把内容裁掉**(每日一言高 0 不可见)

本版:
- 去掉胶囊(透明/无边框/无模糊/无圆角),内容自然排成一行 → 不靠裁切
- .navi-footer 用 inline-flex + width:max-content + max-width:calc(100vw-16px),
  每日一言 min-width:0 + ellipsis(超长才省略,不会消失)
- **在布局里预留 26px**(.item-group-container margin-bottom:26px),
  内容框不再与固定页脚重叠;内框 padding-bottom 由 96px 降为 8px
- 重新加入每日一言(按天缓存 + 3s 超时 + 内置兜底)
2026-09-12 17:27:38 +08:00
cad98c9f11 Revert "fix(ui): 备案号压成一行小字贴底,并在旁边加"每日一言"点缀"
This reverts commit 80fd9b9ffa.
2026-09-12 17:09:28 +08:00
691245122c Revert "fix(ui): 页脚强制 22px 高度上限(实测曾被撑到 113px)"
This reverts commit 4a66b2559a.
2026-09-12 17:09:28 +08:00
4a66b2559a fix(ui): 页脚强制 22px 高度上限(实测曾被撑到 113px)
上一版只改了字号/背景/边框,实测页脚高仍为 113px —— 说明另有规则在撑它,
视觉上依旧一大块。本次对 .navi-footer-host 与 .navi-footer 显式限定
height/max-height: 22px + overflow:hidden + box-sizing:border-box,
把它压成真正的一行小字(配 nowrap + ellipsis,不会显示成半截字)。
2026-09-12 16:55:45 +08:00
80fd9b9ffa fix(ui): 备案号压成一行小字贴底,并在旁边加"每日一言"点缀
用户反馈:备案区域太大、喧宾夺主,且位置不贴底。

CSS(web/navi.css):
- .navi-footer-host / .navi-footer 去掉胶囊底、玻璃模糊、边框与投影
- 字体 11px、内边距 2px 10px、单行 nowrap、居中、透明背景
- 文字色降为 muted(浅色 #94a3b8 / 深色 #7c8ea6,hover 略亮)
- 新增 .navi-quote 样式(斜体、更淡一档、超长省略;窄屏限制宽度)

JS(web/navi.js):
- 安装"每日一言":按天缓存于 localStorage,请求 hitokoto 文本接口(3s 超时),
  失败/超时用内置句子兜底;页脚尚未注入时最多轮询 20×500ms 再挂载
- 装饰性内容不影响可用性:任何失败都不抛出、不阻塞页面
2026-09-12 16:51:28 +08:00
fdfee17726 fix(ui): mini 页备案号脱离内容流固定贴底,使页框高度恒定
实测(1280×720,逐页签):
  页脚 .navi-footer-host 的 top 在 481~527 间漂移 —— 内容多的页签页脚更低,
  因为它处在内容流里;用户反馈"内容没撑满时备案号位置就不对"。

改动:
- body:has(.minimal-home) 下,.navi-footer-host 改为 position:fixed 贴视口底部
  (pointer-events 仅容器 none、子元素 auto,链接照常可点)
- 表项框底部净空 84px → 96px,给固定页脚留位,最后一行不被压住

这样内容框吃满"视口 - 顶部chrome - 固定页脚",高度与内容多少无关。
2026-09-12 16:46:19 +08:00
925fae13a4 fix(ui): 滚动区层级修正——只让表项框滚动,页签行保持固定
用户实测:滚动时页签与会一起被带走。原因是我把滚动放在 .item-group-container 上,
而 Dashy 的 DOM 里**页签行(.minimal-headings-wrap)也在该容器内**,
所以整块(页签+内容)一起滚。

本版:
- .minimal-home > .item-group-container:仅 flex 列布局 + min-height:0,**overflow:visible(不滚)**
- .minimal-home > .item-group-container > .minimal-section-inner:设为**唯一滚动区**
  (overflow-y:auto + min-height:0 + flex:1 + 底部 84px 净空)
- .minimal-headings-wrap:flex:0 0 auto,固定在上方不参与滚动
2026-09-12 16:41:32 +08:00
26924ca719 fix(ui): 修正 mini 页滚动——补 flex 链条 min-height:0,滚动交给内容框
上一版只把 body 锁成 overflow:hidden,既没提供可滚动内层,也没让框收缩,
等于把超出视口的内容"藏起来"。用户直接指出:应当让框收缩到一屏刚好撑满。

根因:Dashy 的 DOM 链条是
  .minimal-section-inner ← .item-group-container ← .minimal-home ← div ← div ← body
中间两层无名 wrapper 没有 min-height:0,flex 项无法压缩 → 容器被内容撑高,
既撑出页面(已锁住)又不产生内部滚动。

本版:
- body(:has(.minimal-home)) 锁 100dvh,并把中间两层 wrapper 也设为
  flex:1 + min-height:0 + max-height:100% + overflow:hidden(补齐链条)
- 滚动显式交给 .minimal-home > .item-group-container(flex:1 + min-height:0 +
  overflow-y:auto + padding-bottom 84px 给底部悬浮件/备案号留净空)
- .minimal-section-inner 不再自成滚动区(overflow:visible),避免双层滚动条
2026-09-12 16:18:00 +08:00
536c7e09ec fix(ui): mini 页内容框按视口自适应并在框内滚动 + 去除硬编码蓝色
两个反馈一次处理:

1) 滚动行为割裂
   实测 .minimal-section-inner 为 max-height:none / overflow:visible,
   内容多即撑高整页 → 滚动落在页面;分组内容多少不同,就出现"有的能滚有的不能"。
   改为:body(:has(.minimal-home)) 锁 100dvh 且 overflow:hidden,主体纵向 flex,
   内容外框/内框 flex:1 + min-height:0 + overflow-y:auto,滚动统一切到框内;
   底部留 84px 净空避免被悬浮件/备案号压住;仅作用于 mini 页,不影响首页与工作区。

2) 硬编码蓝色(9 处,全在 web/navi.css)
   - Dashy 主题变量 --primary/--primary-alt/--info/--footer-text-color-link/
     --nav-link-border-color-hover 原为 #38bdf8/#0ea5e9 → 改中性 slate
     (深色块用 #cbd5e1/#94a3b8 保证可读)——这是全站泛蓝的源头
   - 按钮蓝色渐变 linear-gradient(#0ea5e9,#2563eb) ×2 → 中性 slate 渐变
   - 深色页脚链接 hover #38bdf8 → #e2e8f0
2026-09-12 16:06:24 +08:00
106a178726 fix(ui): mini 页签去图标与蓝色,选中态改为中心向外扩散的高亮
按用户要求调整 /minimal 顶部页签:
- 删除页签内小图标(.section-icon/.item-icon 隐藏)——它同时是 Dashy --primary(#0ea5e9)
  蓝色最显眼的一处来源
- 选中态不再用"更实底色 + 内描边"(会读成描边/填充),改为**径向渐变**:
  中心接近纯白、向四周渐淡至透明,视觉上"从中心向外扩散";
  未选中页签显式清掉 background-image,形成"选中=有光晕"的对照,不靠颜色区分
- 文字强制回中性色(浅色 #0f172a / 未选 #334155;深色 #f1f5f9 / 未选 #a9b8cd)
  并加轻微 text-shadow,保证中心亮底上的可读性
- 深色模式同款径向高亮(冷白偏蓝的暗底亮斑),文字对比度同步
- 焦点态改为细白环(去掉会读成描边的默认 outline)
2026-09-12 15:32:45 +08:00
6e073a9201 fix(ui): mini 页顶部页签补齐玻璃质感(与搜索栏/浮动件同一配方)
问题:/minimal 顶部那排页签(.headings)与左右翻页按钮只有尺寸与圆角,
没有底色/边框/backdrop-filter,而页面其它玻璃面(dock、搜索栏、分组容器)
用的是 blur(22px) saturate(180%) + 半透明白底 + 内高光边框 → 顶部一排显得十分割裂。

改动(web/navi.css):
- .minimal-headings-row .headings 补玻璃底/边框/投影/模糊,与搜索栏同配方
- 选中态(.active / aria-selected)底色更实 + 内描边,保持可辨识
- 左右翻页按钮同样上玻璃(略小模糊),disabled 时降透明度
- 深色模式给对应的深色玻璃值(rgba(20,31,49,.78) 系),避免深色下发白刺眼
- 支持 prefers-reduced-motion

注意:本次为纯 CSS 改动,尚未做真实渲染验证(需部署后截图核对)。
2026-09-12 15:25:31 +08:00
1ac8d2a0f2 docs: 修正过时端口(8080→18080)与 statusCheck 示例值
审计发现 README 三处过时:
- 快速开始示例 docker run -p 3003:8080(容器内 nginx 已是 18080)
- 遗留 portal-nginx 配置示例里 blog 上游仍写 8080
- 配置示例里 statusCheck: true(该项已按要求关闭,示例值应同步为 false 并注明原因)
2026-09-12 15:17:42 +08:00
16707bdfab docs: 同步「常用服务」新机制(导航项点击计数)并补 nav-visit 接口
审计发现文档仍有旧描述,且新端点完全没写:
- README:分组注释/机制说明/系统分组警示/history-server 职责 仍写成泛指的"访问频次",
  容易让人以为常用服务来自浏览器历史。统一改为"导航项点击次数(nav-visits.json)",
  并明确两者数据源互相独立(常用服务不看浏览器历史)
- README 接口表 + AGENTS.md §4.2 补 POST /api/history/nav-visit 的可执行示例
- AGENTS.md 已知坑补一条:顺序不像"最常点的"时如何排查
2026-09-12 15:16:57 +08:00
b980ce6292 fix(build): 测试文件 import 改为块形式(os/filepath/testing)
main_test.go 原为单行 import "testing",补 import 的替换锚点没命中,
导致 os/filepath 未导入、history-server 编译失败(承接上一提交的坏状态)。
本次改为块形式导入,go vet 与 go test 均通过。
2026-09-12 12:42:08 +08:00
6c34970f38 feat(nav): 导航项点击频次独立统计,常用服务据此取 top-5
用户指出:导航项的访问频率应当单独统计。此前「常用服务」的排序依据是浏览器
历史的 host 访问次数(把站外浏览混进来反推),并不等于"我常点哪个服务"。

- history-server 新增 POST /api/history/nav-visit(内部 /nav-visit):
  按 host 计数并落盘 data 卷的 nav-visits.json(临时文件 + rename 原子写),
  随后立即 rebuild,使「常用服务」按新计数重排
- rebuildDashySections:候选访问量改为只认 nav-visits 计数,不再使用浏览器历史;
  无任何点击数据时保持既有分组内容不动(不空转、不误清)
- web/navi.js:点击 http(s) 导航项时用 sendBeacon 异步上报(不阻塞跳转,失败静默)
- 测试:计数递增、落盘、文件生成
2026-09-12 12:41:24 +08:00
a9845f1fba feat(nav): 常用服务改为纯访问频次 top-5,取消 Gitea 置顶
用户要求:不要固定置顶 Gitea,且每个服务都应归到准确描述其功能的分组里。

- history-server/main.go:删除「强制 Gitea/代码托管置顶」逻辑(含 isCodeHostVal
  参与的首位组装),改为按访问频次取前 5;顶部说明同步更正
- 文档:README / AGENTS.md 里"(Gitea 置顶)"的描述改为"不置顶任何服务"
- 现场分组调整:新建「代码托管」分组并把 Gitea 归入(此前它只存在于
  系统管理的 常用服务 里,等于没有功能归属)

go vet / go test 通过。
2026-09-12 12:27:35 +08:00
7c3054f14a docs: 明确 常用服务/历史访问 是系统管理分组,禁止手工加条目
用户指出:常用服务应当是按访问频次自动排序取前五,不该手工塞条目。
我部署 it-tools 时正是这么错的(把新项直接写进了 常用服务,污染频次榜)。

- AGENTS.md:写入导航条目示例前加警示(两个系统分组 + navSection 必须指向用户分组
  + 实测教训:塞进常用服务会被下次重建挤掉)
- README:配置接口章节加同样说明
现场已修正:IT Tools 移入「AI & 工具」,路由 navSection 同步改为该分组,
常用服务恢复为 4 项(系统按频次维护)。
2026-09-12 12:22:01 +08:00
e0e79c98da docs(agents): 修正 /api/item 示例(嵌套形状)+ 补 /api/item/delete(DELETE + body)
实测发现手册里两处错误(部署 it-tools 时踩到):
1. /api/item 是嵌套形状 {"sectionIndex":N,"item":{...}},
   扁平 {"name","url"} 会被拒绝并返回 "title and url required"
2. /api/item/delete 要求 **DELETE** 且 body 带 {"sectionIndex","itemIndex"},
   用 POST 会得到 "method not allowed"

同时补上"分组下标怎么取"的可执行片段,并写明:文档里的示例必须实测过,形状
不确定时先读 /api/schema 或源码里的处理函数。
2026-09-12 11:46:28 +08:00
890af22b95 fix(docs): 清除 AGENTS.md 里的真实令牌/口令/备案号(我自己写进去的),并修正端口写死
机械式 runbook 验证(照抄文档命令执行)抓到两个真问题:

1) 严重:AGENTS.md §6 脱敏自检把**真实令牌前缀、真实口令、真实备案号**当作
   "示例模式"写了进去 —— 等于把凭据与备案信息提交进仓库。
   改为特征检测(长度/字符集/格式),并排除文档自身;同时写明"不要用真实值当模式"。

2) §5 验收与 §4.3 验证把端口写死 3080:非默认端口部署(如 NAVI_ENTRY_PORT=13080)
   照抄会全部得到 000(实测)。改为 PORT=${NAVI_ENTRY_PORT:-3080} 等变量,
   并注明 NAVI_TLS=0 的部署没有跳转端口(该项 000 属预期)。

已知坑表补两条对应的现象与处理。
2026-09-12 11:17:05 +08:00
5ba7789e30 docs(readme): 架构描述改为当前实态(单容器即统一入口 + 内置认证)
README 开头与架构图还停留在"portal-nginx + portal-auth + navi"三容器时代。

- 开头一句话改为:反向代理 + 统一登录 + Dashy + SearXNG + 历史 + 管理后台
  + 路由管理全在同一容器内,容器自己就是入口(3080/8443,3081 跳转)
- 架构图重画:公网 → frps → frpc → navi 单容器,容器内按 $host 查路由表分流、
  需要登录的走 auth_request 到内置登录服务(:3005)、未知域名给自定义 404、
  上游故障给自定义 500(错误页免认证)
- 容器内部图补:nginx 内部端口 18080(对外 3080/8443)、/auth/verify 认证校验、
  gateway/*.conf 路由生成来源
- 明确标注 portal-nginx / portal-auth 已停用,仅作回滚保留
2026-09-12 11:11:28 +08:00
02b321ab51 docs: 拆分双版本教程 —— AGENTS.md(agent 照着做)+ README.md(人类)
用户要求:教程要能让 agent 跟着做,且区分 agent 版与人类版。

新增 AGENTS.md(可机械执行,不做叙述):
- §0 铁律 8 条(首装/升级不可混用、绝不重置门户口令、apply 后异步 reload、
  /admin/api 前缀、路由表整表替换、脱敏清单、不 sed -i 单文件挂载、部署串行)
- §1 场景判定(一条命令区分首装/升级)
- §2 首装:前置检查、三组命令示例、**逐步期望输出**、5 条失败处置表
- §3 升级:硬性前提(旧容器无 cookie 域就必须传 NAVI_COOKIE_DOMAIN)、命令、
  期望输出、收尾断言(含 navi-next 残留的处置)
- §4 配置:先取 /api/schema 清单,再给可直接照抄的六类写入命令 + **写完必须验证**
- §5 验收清单:五项探测及各自不符合时的含义
- §6 提交前脱敏清单(可执行命令 + 绝不可提交项 + "可配置项不得写死")
- §7 回滚 §8 已知坑(8 条,全部是本次实测踩过的)

README:顶部加"两种读者两个入口"导航,并在首装/部署两节加指向 AGENTS.md 的指引。
2026-09-12 10:50:02 +08:00
412e51d1f5 docs: 教程同步本轮全部变更(升级守卫 / 可配置项 / agent 接口)
- 升级示例补 NAVI_COOKIE_DOMAIN、NAVI_HOME_URL:旧容器没有 cookie 域且未传时
  脚本会拒绝升级(否则登录 cookie 退化 host-only,跨子域登录失效)
- 升级脚本环境变量表补齐:NAVI_COOKIE_DOMAIN / NAVI_HOME_URL / NAVI_CHECK_HOST /
  NAVI_ROTATE_PORTAL_PASSWORD,并写明**默认不改登录口令**(生效凭据是数据卷里的加盐哈希,
  改口令会让所有受保护域名立即要求新口令,属高风险,必须显式开启)
- 自定义文件表补 pages.go(文案渲染 + 卡片覆盖)与 schema.go(/api/schema 配置清单)
- 门户卡片章节补后台入口(站点信息 → 门户页服务卡片,与接口同一份数据)
- 自检域名说明:脚本内不写死任何域名,默认取路由表首条(避免脱敏改坏自检)
2026-09-12 10:47:41 +08:00
700775866a fix(deploy): 修掉远端引号嵌套导致切换中途崩溃 + 补最终名字断言
真实事故:探测域名提取用了 `tr -d '"'"'"'` 这种引号嵌套,远端 bash 报
"unexpected EOF while looking for matching `'`",脚本在"停旧容器→起新容器→
改名"的改名之前中断,容器留在 navi-next(服务正常但名字不对,
且验收里 docker inspect navi 全为空,看起来像数据丢失)。

修复:
- 改用无引号的字符类提取域名(grep -oE '[A-Za-z0-9][A-Za-z0-9.-]*\.[A-Za-z]{2,}'),
  彻底避开引号嵌套
- 收尾加断言:docker inspect navi 必须成功;若发现仍在 navi-next,
  明确提示可直接改名,而不是静默"成功"

现场已手动补齐:navi-next 完整核验(healthz/conf.yml/admin/schema 200、
入口 302/404/200/200/200、22 条路由、端口 3080/3081、门户标题与备案号完整、
env COOKIE_DOMAIN=1 明文 PASSWORD=0、数据卷凭据文件在)后改名为 navi,
回滚点 navi-prev-20260912104324(ui-v53) 保留。
2026-09-12 10:46:09 +08:00
b850a478fe fix(deploy): 入口自检改为域名无关(脱敏把探测域名换成了占位符导致误判失败)
真实部署失败原因:脱敏时把仓库里所有 example.com 换成 example.com,连
deploy/zd-host.sh 的三个入口自检探测域名也一起换了 → 自检去探测
dashy.example.com(用户路由表里不存在)→ 404 → 判定失败并回滚。

修复:
- 探测域名默认取数据卷 gateway.yml 路由表第一条的 host(或 NAVI_CHECK_HOST 显式指定),
  不再把任何具体域名写进脚本
- 已配置域名接受 200/302(门户 200、受保护 302 都算正常);
  000(未监听)/404(不在路由表)/5xx 才算失败;未知域名仍必须 404;3081 必须 302
- apply 后先轮询等待 reload 就绪再探测(异步 reload 会误判)

另修复上一轮引入的隐患:等待循环引用了脚本里不存在的 $CHECK_HOST/$ENTRY_PORT,
set -u 下会直接崩(与第一轮 HOST 未定义同类错误),本次一并纠正并改为先定义后使用。
2026-09-12 10:43:13 +08:00
dbdfe0faef fix(deploy): 默认不改门户口令 + apply 后等待监听就绪
两个问题,都由本轮真实部署暴露:

1) 口令轮换不该默认执行
   集成明文 env 清理时顺手加了"重置为强随机口令",但重置会让所有受保护域名
   (含用户访问 agent 界面的入口)立即要求新口令,用户若没读到日志就等于被锁在门外。
   而生效凭据本是数据卷里的加盐哈希(/app/data/portal-auth.json),删掉明文 env
   对登录毫无影响。→ 改为仅清理 env,轮换必须显式 NAVI_ROTATE_PORTAL_PASSWORD=1。

2) apply 触发的 nginx reload 是异步的
   本次部署因此误判失败:受保护域名探测到 404(旧配置仍在生效),脚本判定
   "应 302 实际 404" 并回滚。→ apply 之后、入口自检之前加入轮询等待就绪(最多 10s),
   与 README「接口注意事项」第 4 条同一原因。
2026-09-12 10:42:44 +08:00
2eeaabb200 fix(deploy): NAVI_COOKIE_DOMAIN/NAVI_HOME_URL 未转发到远端,升级被自家守卫拦下
现象:本地已传 NAVI_COOKIE_DOMAIN=example.com,部署仍在远端报
"缺少 PORTAL_COOKIE_DOMAIN ... "(守卫在远端执行,而远端 heredoc 是引号形式,
不会展开本地变量)→ 升级被拦,生产保持 ui-v53 未变。

修复:在 ssh 命令行显式转发这两个变量(本地展开后作为远端环境变量传入)。
实测:远端能读到传入值。
2026-09-12 10:39:59 +08:00
ff4af5fa31 fix(deploy): 必填校验插在 HOST 赋值之前,set -u 下直接崩
现象:NAVI_HOST=... bash deploy/zd-host.sh v54 直接报
line 21: HOST: unbound variable 并退出(生产未受影响,纯脚本缺陷)。

原因:上一轮加 NAVI_HOST 必填校验时,把校验插到了 STAMP 之后、HOST 赋值之前,
而脚本是 set -u,提前引用未赋值变量即中止。

修复:校验移到 HOST 赋值之后;zd-host.sh 与 zd.sh 都改。
自测:不给 NAVI_HOST → 友好提示 + 退出码 2;给了则继续执行(实测进入构建阶段)。
2026-09-12 10:38:17 +08:00
203cd05fbb docs: 接口注意事项(前缀 / 整表替换 / 必须 apply / reload 需等待)
验证过程中实际踩到的四点,写进 README 便于 agent 与人复用:
1. 管理接口前缀是 /admin/api;打根 /api/ 会拿到 200 + Dashy HTML 的假成功,
   判断是否命中要看返回体类型而非状态码
2. 路由表是整表替换,必须提交完整 {routes:[...]};清空非空表需 force:true
3. 改完必须 apply(内部 nginx -t 校验,失败保留旧配置并回显 nginx 报错)
4. apply 是异步 reload:立刻探测会得到旧结果(表现为'apply 了却 404'),
   给出轮询等待入口就绪的示例脚本
2026-09-12 10:31:46 +08:00
bb41b61ad6 fix(gateway): 路由表接口只替换路由,不再整份配置重置(agent 友好)
真实验证(干净环境练兵)抓到:POST /admin/api/gateway/routes 会把整个
GatewayConfig 反序列化为零值后保存,因此
- 只提交单个路由对象 → {"routes":0,"status":"saved"}:**静默清空整张路由表**
- 提交 {"routes":[…] } → 其余字段被重置为默认(实测入口端口 13080 → 8081,
  TLS/认证地址一并丢失),随后 apply 生成的配置与预期完全不符

对 agent 尤其危险:照文档提交一条路由就会改掉入口端口与所有其它路由。

修复:
- 只补路由表:以现有配置为基准,仅替换 Routes,其它字段原样保留
- 请求体必须是 {"routes":[...]};缺该键 → 400 并说明正确形状(不再静默变空表)
- 非空表要清空必须显式传 force:true,否则 400 拒绝
- 测试:单个对象被拒;数组提交后 listenPort/TLS/认证地址/证书路径全部保留;
  清空被拒、force 允许
- README:修正路由示例为完整数组形状,补"替换整张表"与 /admin/api 前缀说明
2026-09-12 10:28:03 +08:00
92fd32e7a9 feat(api): 全量配置项暴露为接口 + 后台增加门户卡片编辑框
用户要求:所有可配置项都应当是 API,方便 agent 直接把站点配成用户喜欢的样子。

- 新增 GET /api/schema:一次返回
  · items:全部单值配置项(站点信息、登录页/门户页文案、备案号、页脚…),
    含当前值、类型、写入口与请求体示例;**当前为空的键也会列出**,否则 agent 发现不了
  · resources:结构化资源与其真正的写入口(导航、路由表+apply、门户卡片、壁纸、
    门户口令、历史、图标上传、重载),并在 hint 里写明"路由表改完必须 apply"这类约束
- 后台「站点信息」新增「门户页服务卡片」编辑框:载入 / 保存 / 清空即回内置,
  与 /api/portal-cards 同一份数据(界面能改的 agent 都能改)
- README 新增「配置接口(agent 可直接改)」:schema 用法、写入口对照表、可直接复制的示例
- 测试:schema 必须列出关键项与写入口;卡片接口写入生效、清空返回 reset 并回落内置
2026-09-12 10:19:22 +08:00
7c2bed2f2d feat: 门户页卡片可配置 + 升级前域名校验 + 清理明文门户口令
按用户选择实施三件事:

1) 门户页服务卡片可配置(不再写死真实域名)
   - portal/index.html 卡片区加 <!--NAVI_CARDS_START/END--> 标记,内置卡片改为通用示例
   - renderPortalPage 支持数据卷 <data>/portal/cards.html 覆盖标记区间(清空即回落内置)
   - 新增 GET/POST /api/portal-cards 读写该片段
   - 测试:覆盖替换成功、空白片段回落内置
   - 仓库内真实域名由此清零

2) 升级前必须提供跨子域单点登录变量(防止"去掉写死默认值"造成 SSO 静默失效)
   - zd-host.sh 在切换前检查 PORTAL_COOKIE_DOMAIN:缺失且未传 NAVI_COOKIE_DOMAIN
     时直接拒绝升级(退出码 2)并给出正确命令;PORTAL_HOME_URL 可用 NAVI_HOME_URL 补
   - README 新增「升级已有部署前必须确认的两个变量」含失败表现与正确命令

3) 清理明文门户口令
   - 迁移旧容器 env 时丢弃 PORTAL_PASSWORD(docker inspect 可见 = 泄露面)
   - 升级后写入强随机口令并通过接口生效,部署日志只打印一次;失败不阻断升级并提示后台可改
2026-09-12 10:14:22 +08:00
b99319b38b chore(sanitize): 备案号与域名一律可配置,仓库不再预置真实域名/备案号/内网地址
用户要求:备案号与域名都应当可配置,仓库内容要脱敏。

功能(不只是替换字面量):
- 被代理页面注入的备案号改为读后台「站点信息」的 icp/gongan,**未配置就不注入**;
  原先写死具体备案号,等于每个部署者都会把别人的备案信息印到自己页面上
- 注入内容做 nginx 安全处理(去引号/反斜杠/换行/美元符),并有测试覆盖
- auth.go:PORTAL_COOKIE_DOMAIN / PORTAL_HOME_URL 不再预置域名,
  cookie 默认 host-only、首页默认相对路径 "/"
- gateway.go:默认 loginUrl 不再预置域名(受保护路由缺配置会明确报错,不静默生成跳不出去的登录页)
- 首装脚本在给了 NAVI_DOMAIN 时自动注入 PORTAL_COOKIE_DOMAIN/PORTAL_HOME_URL,
  避免"去掉写死默认值"导致跨子域单点登录失效

脱敏:内网 IP(10.0.0.10)与真实备案号清零;真实域名仅剩门户页内容 8 处(待定);
测试夹具/示例/错误页/脚本/后台占位提示全部通用化。

README:新增「跨子域单点登录需要的两个环境变量」;环境变量表改为 NAVI_HOST 必填。
测试:新增备案号注入三条(用配置、未配置不注入、危险字符清理)。
2026-09-12 10:10:52 +08:00