Commit Graph

19 Commits

Author SHA1 Message Date
86aced0c4a fix(ui): 滑动切换只淡出页框内元素(页签行不动)+ 页签行跟随选中项
用户反馈两点,均已实测定位:
① 整框(含顶部页签行)跟着淡出淡入 —— 不该动页签。
   原实现把 opacity 打在 .item-group-container 上(它同时包含页签行与内容)。
   改为只对 .minimal-section-inner.selected > *(.section-items + .minimal-widget-wrap)
   做 keyframes 淡出/淡入;框体与页签行保持不动。
   用 keyframes 而非 transition:这些子元素自带 `transition: all`,改 transition 会
   覆盖它们原有的 hover 过渡。
② 页签行不联动 —— 实测 .minimal-headings-row 可横向滚动
   (scrollWidth 1306 > clientWidth 828)但 scrollLeft 恒为 0,
   切到第 7/8 个分组时选中页签落在视口外(x=1374..1532 > 1280),
   用户看不出切换发生。新增 swipeRevealTab() 把选中项滚进可视区(居中,reduced-motion 下瞬时),
   手势切换与手动点页签都会触发。
另:边界回弹提示也改为只动页框内容,不再带动页签行。
2026-09-12 22:06:26 +08:00
f27f46099c fix(ui): 修正滑动手势的两个确凿缺陷(触屏不切换 / 触控板方向反)
v89 实测 12/16,且已测出事件确实到达(触摸 touch+pointer 各 7 个、wheel deltaX=40 共 4 次),
证明不是"事件没到",而是逻辑把结果吃掉了:

① 防误触拦截器把自己吞了:滑动后 400ms 内抑制 click(防误开卡片),
   而切换手势发的正是 tabs[j].click() → 被自己拦掉 → 表现为"滑了没反应"。
   修法:程序化切换 click 期间置 swipeSelfClick 放行,并清掉本次抑制窗口。
② 触控板方向反了:两指向左滑时浏览器给 deltaX > 0(内容右移),
   原实现映射成"上一个",在第一个分组会被 clamp → 看起来完全没反应。
   修法:deltaX > 0 → 下一个分组。
2026-09-12 21:59:11 +08:00
3deffc1a77 feat(ui): 极简页支持左右滑动切换分组(触屏/触控板/鼠标拖拽)
需求:极简页的内容框此前只有竖向滚动手势,加入横滑切换"页面"(分组)。

实现(web/navi.js 新增 bindTabSwipe + web/navi.css v89):
 · 契约:.minimal-headings-row > .minimal-section-heading.headings(当前项 .selected),
   切换 = 对该元素派发真实 click(实测有效,不触碰 Dashy 内部状态)。
 · 三种输入:pointer 事件覆盖触屏与鼠标拖拽;wheel 的 deltaX 累积覆盖触控板双指横滑。
 · 判定:|dx| >= 56 且 |dx| > 1.5|dy| 才切换;先判 |dy| > |dx| 则直接让给竖向滚动。
 · 边界:clamp —— 到头不动,给一次轻微回弹提示(prefers-reduced-motion 时改为淡闪)。
 · 动画:纯淡入淡出(不位移),并遵守 prefers-reduced-motion。
 · 防误触:横拖超过 20px 后吞掉紧随的一次 click,避免误开卡片链接。
 · 不抢横滑:手势起点若落在可横向滚动的容器内、或输入框内,直接放行。
 · 可访问性:键盘 ←/→ 也能切换;touch-action 用 pan-y pinch-zoom(保留双指缩放)。
 · 自检:匹配不到标签时在 <html> 上置 data-navi-swipe="unavailable"(不刷控制台)。
2026-09-12 21:54:35 +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
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
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
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
44e2792f0e fix(theme): 深色下主页底色与右下悬浮件仍不生效
现象:主页大面积没变深;右下角仍有一块灰色方块。

根因:
1. conf.yml 的玻璃主题用 html[data-theme] body 设了亮色背景,特异度与
   html.navi-dark body 相同(0,1,1),而 customCss 由 Dashy 运行时注入、排在
   navi.css 之后 —— 平局时后者胜出,所以页面底色没被改掉。
2. 右下那块是我们自己的悬浮件,但此前只逐个类名列举,漏掉的类名就保持原样。

改法:
- navi.js 把 navi-dark 同时打到 <html> 与 <body>(并在 DOMContentLoaded 再应用一次)
- navi.css 用 html.navi-dark body.navi-dark 提高特异度到 (0,2,1),压过 customCss,
  与注入顺序无关
- 兜住 Dashy 外层容器(#dashy/.home/.workspace/.minimal-home)与原生页脚
- 用 [class^="navi-"]/[class*=" navi-"] 整体兜住我们注入的悬浮件,避免再漏类名
2026-09-11 23:38:56 +08:00
b1e4367937 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),与注入顺序无关

需要目视确认配色层次(变量值只能看着调)。
2026-09-11 23:31:27 +08:00
a8ce6ad759 feat(theme): 导航页恢复自动深浅色(跟随系统),保留隐藏原生设置入口
用户反馈:现在没有自动深/浅色。实测原因有两层:
1. Dashy 原生的日/夜切换按钮在设置面板里,而该面板按要求被隐藏;
2. navi.css 没有任何 prefers-color-scheme 分支,且多处写死亮色。

本提交按"我们自己接管"实现(不去赌 Dashy 压缩后的主题存储键):
- navi.js:默认跟 prefers-color-scheme;手动切换过以 localStorage.navi-theme 为准;
  系统偏好变化实时跟随;给 <html> 设 data-theme 与 .navi-dark,并设 color-scheme。
  Dashy 挂载后可能覆写 data-theme,用短暂定时对齐 + 属性观察器收口
  (仅在"与期望不一致"时回写,避免自激循环)。
- navi.css:深色下覆盖页面底色与文字,以及我们自己注入的悬浮控件
  (搜索条/收边控件/页脚/工作空间工具条与弹窗/迷你视图标签条)。
  选择器带 body 以压过主题注入的 customCss。

需要人工目视确认观感(深色下的玻璃层次只能靠眼睛调)。
2026-09-11 23:18:54 +08:00
26a0b15014 perf(web)+fix(admin): 撤掉卡片层模糊、补隐藏动画、后台窄屏适配、收边两段式
1) 卡顿:撤掉"重复且参与动画"的卡片层 backdrop-filter
   实测首页 37 个元素带模糊(24 张卡片 + 6 个分组容器 + 顶栏 + 悬浮件)。
   模糊要对背景逐帧重采样,而卡片又参与逐张淡入与悬停位移,叠加就是卡顿。
   现在只保留分组容器的玻璃感与悬浮件(搜索栏因宽/圆角/内边距一直在变也去掉)。
   实测:/ 从 37 → 12,/minimal 从 15 → 4。
   注意:选择器必须写成 html[data-theme] body a.item —— 主题的 customCss 由
   Dashy 运行时注入,同权重 + 同为 !important 时靠文档顺序决胜,写在 navi.css
   里的规则会输,必须提高特异性。

2) 隐藏/出现补动画
   ≤380px 让位隐藏按钮组:加上 opacity+位移过渡,并把 visibility 延迟到淡出
   结束再切(否则瞬间消失、看不到过渡)。弹层关闭先加 .closing 淡出再摘节点。
   降动效模式下不再用 transform:none 抹掉"收边"的位移,避免隐藏失效。

3) 工作空间收边改为"第一次交互只滑出"
   收起态点一下只滑出、不触发(否则贴边的按钮会被直接点中,比如想看搜索框
   结果把管理后台打开了);第二次点击才生效。判据是"元素是否真的露在视口内",
   所以鼠标悬停已展开时一次点击即生效。6 秒无操作或 Esc 自动收回。

4) 后台窄屏:分组行原本是内联 flex + 固定 max-width,窄屏既不能换行也不能
   收缩,输入框顶破卡片。改用类 + ≤560px 适配(竖排、整行占满、按钮右对齐、
   字号 16px 防 iOS 聚焦缩放)。实测 390px:scrollWidth == innerWidth、
   零横向溢出。
2026-09-11 20:13:44 +08:00
76a6a83e7d feat(web): 不支持内嵌时明确弹层;修掉被 promise 吞掉的 ReferenceError
1) 点不支持在工作区显示的应用 → 弹出提示卡片
   标题「该应用不支持在工作区显示」+ 域名 + 服务器返回的原因 +
   「在新标签页打开 / 知道了」两个按钮;面板里同时保留同样的说明。
   之前只在面板里显示一行字,用户点了之后缺少明确反馈。

2) 修 ReferenceError: openUrl is not defined(线上真实故障)
   setWorkspaceState 第 4 个参数早先从 openUrl 改名为 note 时,blocked
   分支漏改,于是每次被拒内嵌都在函数内部抛异常,异常沿 promise 链被吞,
   导致后面的弹层根本没有执行、面板提示也只渲染到一半(URL 行和按钮缺失)。
   教训:验收必须采集 Runtime.exceptionThrown,并且断言交互元素真的存在;
   只断言"状态=blocked + 提示文字前缀"会恰好掩盖这类错误。
2026-09-11 19:52:19 +08:00
8ba5e5117b feat(web): 工作空间内嵌网页 + 控件收边 + 页脚去重 + 窄屏不再重叠
1) 工作空间真的在中间打开网页
   Dashy 的面板就是 iframe,但它的 launchApp() 只在 target 不是 newtab 时
   才把 URL 灌进 iframe;而且侧栏项目大多没有 per-item target,Dashy 走的是
   Vue 直接改 iframe.src 的路径 —— 根本不经过 window.open,导致判定逻辑被
   绕过(实测:Gitea 仍被内嵌、空白)。现在两条路径统一进 verifyAndShow():
   先探测再决定内嵌还是明确提示;被拒时还会清空 iframe 的 src。

2) 控件在工作空间收边
   中间嵌的是别人的应用,常驻控件会遮挡。搜索球收左边缘、右下角两个按钮
   组成 dock 整组收右边缘、备案号收下边缘,各露 14px(备案号 13px),
   鼠标移入或点击、聚焦时滑出展开。只在工作空间生效。

3) 修 iframe 撑高页面 77px
   Dashy 的 .web-content iframe 是 position:absolute,包含块是 #dashy 而非
   .web-content,外层 overflow:hidden 拦不住它,它按 #dashy 高度撑到 814px
   (164+814=977 正好等于页面 scrollHeight)。补 position:static 后归零。

4) 首页不再出现两个备案号
   Vue 异步挂载,navi.js 可能先补了一份页脚,之后 Dashy 的页脚才出现;
   原来的守卫只判断 .navi-footer 是否存在,不会回收自己早先注入的那份。
   新增 dashyFooterNode() 区分两者,发现 Dashy 的就把我们补的移除。

5) 窄屏展开搜索框不再压住右下按钮组
   手机端展开宽度是 calc(100% - 86px),而按钮组占最后 114px → 重叠 38px。
   改为按按钮组真实占位预留(≥600px: min(560px, 100% - 158px);
   ≤599px: calc(100% - 132px));≤380px 时输入框会窄到不可用,改为
   临时隐藏按钮组(用 visibility 而非 transform,因为 prefers-reduced-motion
   会强制 transform:none)。
2026-09-11 19:04:38 +08:00
da196abcb5 feat(web): 隐藏原生设置、圆形视图切换、工作空间改列表、三视图标题统一
用户提出的四条,都基于实测数据而不是猜测:

1) 隐藏 Dashy 原生设置入口
   顶栏的 .config-launcher(齿轮+铅笔+调色板)与我们的 /admin/ 重复,
   连同 .options-trigger/.settings-outer 一起隐藏。

2) 视图切换图标改成圆形,紧贴自定义入口左侧
   原生切换按钮是侧栏底部的方形图标(.switch-view-buttons),已隐藏;
   改为右下角圆形悬浮按钮 .navi-view-switch,点开是「首页/极简/工作空间」
   菜单并标出当前视图。
   顺带修掉 2px 错位:管理入口是内容盒(48+2px 边框=50),视图按钮是
   边框盒(48),并排时差 2px;给管理入口补 box-sizing: border-box。

3) 工作空间「点了没反应」
   读 Dashy 自己的 Workspace 组件(assets/Workspace-*.js)得到结论:
   右侧面板就是一个 iframe,launchApp() 只有 target 不是 newtab 才把 URL
   灌进面板;我们全局 openingMethod: newtab,所以每次点击都走 window.open
   分支,this.url 恒为空 → 面板永远空白。
   就算灌进去也没用:Gitea 实测 X-Frame-Options: sameorigin 拒绝被嵌
   (Refused to display … in a frame)。
   所以改成:点左侧分组 → 右侧列出该分组应用卡片(数据取自
   /admin/api/sections),不依赖目标站点是否允许 iframe,首页仍保持新标签页。

4) 三视图标题割裂
   实测 /minimal 是 48px 居中 -1.92px,而 / 与 /workspace 是顶栏里 40px
   左对齐、无字距。现在三个视图共用同一条规则(clamp(1.75rem,4vw,2.75rem)、
   700、-0.035em、居中;移动端 clamp(1.6rem,8vw,2.2rem)),副标题也居中置灰。

另外:工作空间面板原本用 position: fixed 钉在视口中间(1364×654,上距顶栏
7px、下距搜索栏 8px),把页面切成「顶栏/大方块/底部」三段;改为正常文档流,
内容多时页面自然滚动。
2026-09-11 18:17:23 +08:00
80b0e5f51e feat(web): 搜索框收起态 + admin 入口 + 修正多余滚动
- 搜索栏默认收起为左下角放大镜悬浮球(52/56px),点它展开成输入框,
  输入框内左侧仍是同一个放大镜,点它收起;Esc 也可收起
- Dashy 页面补右下角 /admin/ 悬浮入口(原先没有任何管理入口)
- 短页面不再凭空可滚动:body 留白 104->84px,并给 #dashy 扣掉同样高度
  (Dashy 的 #dashy 本已撑满 100vh,叠加留白就多出约 134px 滚动)
- 补的备案号页脚改为固定在搜索栏正上方,不再参与文档流撑高页面
- 弹层打开时搜索栏与管理入口一并让位
2026-09-11 17:03:33 +08:00
9eb8214fab fix(web): 搜索栏改为底部悬浮,补极简视图页脚与整体动效
- 搜索栏 position:fixed 悬浮在视口下侧,任何视图/滚动位置都可见;
  原先是流式跟在页脚之后,手机上要滚到底才看得到
- 极简视图(/minimal)与工作空间不渲染 Dashy 页脚,备案号整段消失;
  navi.js 从只读 pageinfo 取回页脚补一份(DOMParser,不用 innerHTML)
- 空分组不再渲染成空玻璃条(:not(:has(a.item))),修掉 mini 页底部一排空框
- 图标统一 56/48px 容器 + object-fit:contain,修 24px 与 56px 混排
- 备案号页脚改为胶囊样式
- 新增动效层:视图/卡片淡入、搜索栏滑入、悬停位移,全部只动
  transform/opacity 并遵循 prefers-reduced-motion
2026-09-11 16:25:26 +08:00
ff0e6418f8 feat(nav): auto-detect browser extension and offer one-click install
导航页现在会自动检测浏览历史插件是否已安装:

- 扩展在用户配置的服务地址上动态注册 content script
  (Chrome: chrome.scripting.registerContentScripts,
   Firefox: browser.contentScripts.register),
  于 document_start 给 <html> 打上 data-navi-ext 标记,
  不在其他站点运行。
- history-server 始终把「安装浏览历史插件」放在「历史访问」第一条
  (入口 url=/extension/install.html),启动时也会重建一次,
  不再需要等下一次同步。
- web/navi.js 读到标记后隐藏该入口(连 .item-wrapper 一起隐藏,
  避免留下空网格位)。
- 新增 /extension/install.html:自动识别浏览器,给出对应安装包与步骤;
  支持在 data 卷放 extension-store.json 配置商店链接,
  有商店链接时显示真正的「一键安装」。
- 段落名匹配同时兼容中英文(常用服务/Favorites、历史访问/History),
  修掉了公开示例 conf.yml 用英文名时重建逻辑完全失效的问题。
- 扩展升到 1.1.0(新增 scripting 权限与 detect.js)。

Note: Chrome/Edge 不允许从网站直接安装非商店扩展,
因此自托管场景下只能下载后手动加载;发布到商店后才会出现一键安装。
2026-09-11 10:41:10 +08:00
7e49d2b51c feat: HomeDash - unified self-hosted navigation portal
Single-container navigation service combining:
- Dashy dashboard + SearXNG metasearch + history sync API + admin WebUI
- Go services for history/admin APIs, nginx as unified entry
- Browser extensions (Chrome MV3 / Firefox MV2) with 30s batched sync
- Production guide: portal-nginx unified auth reverse proxy + webui4frpc/frp tunnel
- Fully sanitized (no internal IPs/domains/credentials)
2026-09-08 22:25:00 +08:00