Commit Graph
30 Commits
Author SHA1 Message Date
im0o 5bd4e8d609 合并规则页(task/rules-page)
# Conflicts:
#	web/api.ts
2026-08-11 20:08:44 +08:00
im0o 8026c432e7 合并配置页(task/config-page)
# Conflicts:
#	web/api.ts
2026-08-11 20:08:17 +08:00
im0o 5c39f666dc 合并仪表盘页(task/dashboard) 2026-08-11 20:07:22 +08:00
im0o 76460c7074 合并请求页(task/requests) 2026-08-11 20:07:07 +08:00
im0o 5b6ea8469f 合并连接页(task/connections) 2026-08-11 20:07:07 +08:00
im0o fdf3bfb659 合并设置页(task/settings) 2026-08-11 20:07:07 +08:00
im0o dd100cfc97 web/api.ts 行尾归一回 LF
第一版提交把整个文件以 CRLF 写进了 blob,内容与 main 相比只是追加,
但每一行都会在后续分支的合并里制造整文件冲突。
2026-08-11 20:06:54 +08:00
im0o 9e87517a4e 规则页:全局分流规则的编辑与规则组管理
新增 RulesPage.vue 和规则、规则组、ini 导入三个对话框,纯逻辑放在
shared/rules.ts。拆分规则字符串复用 vendor 的 splitRuleFields——引号和
嵌套括号里的逗号不会被当成字段分隔;目标之后的选项段原样拼回,改一条
规则不会丢掉它的 no-resolve 尾巴。规则组的缓存路径显式生成:不写 path
时内核按 url 哈希自己算一个,那个路径这里算不出来。
2026-08-11 18:53:21 +08:00
im0o 0eef680fba 配置页:订阅的增删改和立刻更新
写配置的纯逻辑在 shared/providers.ts。删除前 subscriptionReferences 同时查
直接 use 和经订阅副本(共用缓存文件 path)的间接引用,有被引用的就拦下并列出
策略组。订阅不允许改名:名字被 use: 和副本引用,同步所有引用不如删了重建。

页面还没挂进 App.vue 的导航,由集成者统一加。
2026-08-11 18:50:13 +08:00
im0o 0eb8d05ed8 新增请求页:实时显示内核日志流
流式接口不能走 api.ts 的 request()(它会把整个响应读完),页面自己
fetch 加 stream.ts 按行读。解析和追加上限放在 web/logs.ts 里,因为
「脏行跳过」「超上限丢最旧的」是点名要测的行为,埋在组件里测不到。

收到的行先进缓冲、每 200ms 批量刷进列表;暂停就是不刷但照收,
缓冲也设上限,恢复时补上。级别是建连时定的,切换即重连。
2026-08-11 18:49:59 +08:00
im0o c9a0b87af4 连接页:每秒轮询的连接表格,能按入站看、能断开
/connections 没有流式版本,只能全量轮询;断开是运行时动作,不走
落盘那条保护链路。两个容易做反的地方都收在 shared/connections.ts:
chains 数组第一个是最终出口,显示要倒过来;没有连接时 connections
字段是 null 而不是空数组。虚拟滚动用 @tanstack/vue-virtual,ADR 0006
留的两个例外之一,它只有 virtual-core 一个运行时依赖。
2026-08-11 18:48:55 +08:00
im0o 97c3943403 新增仪表盘页:流量曲线、流量与内存数字、配置概览、内核状态
流量曲线手画 SVG polyline 而不引图表库:60 个点两条线,一个图表库几百 KB,
决策记录 0006 留的例外用在这里不划算。/traffic 和 /memory 是永不结束的流,
api.request() 会把整个响应读完等于挂死,所以新增 api.openStream() 返回响应体,
交给 web/stream.ts 的 readLines 逐行读,离开页面中止 fetch 断流。
导航入口要在 App.vue 里挂,按约定留给集成者统一改。
2026-08-11 18:36:00 +08:00
im0o fb1dd401b2 设置页:外观、连接、关于、备份还原
主题用 <html> 的 data-theme 标记显式选择,tokens.css 新增两组选择器块,
权重压过 prefers-color-scheme;选「跟随系统」则不标记,交回媒体查询,
系统主题切换时界面仍跟着变。
顺带把 InboundForm 和 TokenGate 里硬编码的 #fff 换成 var(--c-bg):
深色主题的强调色是浅青,白字压在上面对比度不够,深色下会看不清。
2026-08-11 18:35:04 +08:00
im0o 6c8a1c4e0e 并行开发前的清场:白名单、读流模块、流量格式化
任务书承诺这些在分发前已存在。/traffic /logs 补进读白名单(在流式清单
里却不在读白名单里,仪表盘和请求页一开工就 403),PUT /providers/rules
补进写白名单(规则页的立刻更新)。web/stream.ts 把分块流缝成完整的行,
分块边界和行边界无关是它存在的全部理由;shared/format.ts 是连接页和
仪表盘共用的流量数字格式化。
2026-08-11 18:02:21 +08:00
im0o d028e63a74 路由页双列转单列改按内容区宽度判断,阈值等效提高
原来按视口 900px 切换,侧栏收拢与否会让同一视口下的内容区差出一百多
像素,且用户实测切换时右列已经太窄。改成容器查询,内容区 900px 以下
转单列,等效视口阈值提高到约 1100-1160。
2026-08-11 17:52:00 +08:00
im0o 4e504e9003 切 tab 时容器高度也做过渡
三个 tab 的成员数不同,内容淡切时高度直接跳。复用换入站那套锁高度、
量目标、过渡、交还的钩子,挂到 tab 的 Transition 上;tab 切换仍然不触发
自动滚动。
2026-08-11 17:08:33 +08:00
im0o 58d317afce 链式代理段出现消失不再抖动
抖动有两个来源。主要的是 .block + .block 给的 24px 上外边距在高度折叠
动画之外,出现和消失的瞬间各跳一下,折叠钩子现在把 margin-top 和高度
一起做插值。次要的是整页高度跨过视口阈值时滚动条出现消失让内容横移,
用 scrollbar-gutter 把滚动条位置常驻留出来。
2026-08-11 17:04:11 +08:00
im0o 272a0931a1 文本输入的焦点态统一成描边变色
原来三个组件各写一份、筛选输入框哪份都没有,落到全局的外圈 outline,
和订阅选择器长得不一样。规则上收到 tokens.css 一处,组件里的重复定义
删掉。按钮保留外圈焦点环,那是键盘可达性。
2026-08-11 17:03:11 +08:00
im0o 8bc97eb47a 卡片 foot 的空白处点击透传为选择
foot 属于卡片,点了没反应像是卡片坏了。延迟按钮 stop 掉冒泡,测延迟
不会连带换线;禁用的卡片照样不可点,光标跟着语义走。
2026-08-11 16:57:28 +08:00
im0o d41012a0ab 自动滚动改为折叠进场时预量最终高度;侧栏导航按钮间距加大
原来在 nextTick 量高度,此时折叠动画刚从 0 起步,配置展开量到 0 不滚,
链式代理量到半截滚一半。enter 钩子触发时元素刚插入、还是自然高度,
在把高度压到 0 之前先量,得到的就是折叠结束后的位置,滚动和折叠同时
开跑。触发点也从开关挪进 foldEnter,两个包装函数随之删掉。
2026-08-11 16:51:49 +08:00
im0o 0ddefd0d5d 侧栏收拢时导航按钮跟着缩窄;配置区高度折叠动画;侧栏不再牵动页面内容
导航按钮宽度写死 159px 而不是 100%,百分比到像素之间没法插值,收拢时
按钮平滑缩成 36px 的方形贴住图标,高亮块不再被裁切出血。配置表单和
链式代理段的出现消失改成高度折叠(量出目标高度做插值,margin 换成
padding 否则那一截在动画之外瞬跳)。路由页「新增」按钮的文字不再跟着
侧栏密度隐藏:侧栏收拢是侧栏自己的事。
2026-08-11 16:42:43 +08:00
im0o 5a04968e17 详情切换动画与侧栏折叠动画
详情换选中:内容 out-in 淡切,容器高度从旧值滑到新值。auto 高度不可
插值,做法是离开前钉住当前高度,新内容进场时量出目标值过渡过去,结束
后清掉内联样式交还给内容。

侧栏折叠改成裁切式:只过渡外层宽度,内层内容按展开宽度定死、被 overflow
裁掉,文字用透明度淡出(比宽度动画快,先消失再收边,不露半截字)。原来
用 display: none 藏文字加居中图标,动画中途会重排跳位。折叠宽度取 60px
是因为图标中心恰好在 30px,收拢后天然居中,全程不用挪任何东西;折叠
按钮钉在右下角,跟着边缘走。
2026-08-11 16:34:20 +08:00
im0o de3a4c9bcd 路由页动效与细节一批
基调是简单快速:hover 颜色 0.15s 过渡(全局规则,只过渡颜色不碰布局)、
对话框入场淡入加 6px 上移、tab 下划线改成共享的一条并在切换时滑动、tab
内容 out-in 淡切、配置折叠箭头居右随展开旋转 90 度、订阅下拉 0.12s 出入场。
不引 animate.css:这些效果各自十几行 CSS,引库只多不少。

配置展开和拨开链式代理时,若详情底部长出窗口则平滑滚进视野;tab 切换
不触发,来回切时页面跟着跳只会晕。二次确认对话框点遮罩可关闭,内边距
挪进内层,否则点在 dialog 自己的 padding 上也会被当成遮罩。新增入站的
表单不做遮罩关闭:误点会丢掉填了一半的内容。对话框标题改为「新增路由」
「编辑路由」。select 换自绘箭头,原生箭头贴边且位置调不了。
2026-08-11 16:23:03 +08:00
im0o a44f410074 删掉出站模式的写方向
界面上做「出站模式三选一」的计划经讨论废弃:全局档等价于既有的专属组,
直连留在卡片里是即时动作,规则档的真实需求(内网豁免、按站点分流)属于
之后的附加规则功能。outboundToProxyField 自此确定没有调用方。读方向保留,
手写配置里的 DIRECT/GLOBAL 仍要认得和显示。
2026-08-11 16:03:08 +08:00
im0o 4da501ded6 给落盘保护链路补上单元测试
全项目最关键的一条链路此前只有手动脚本在守。跑在一个假内核上,覆盖
在线路径的八种情形:正常保存、基准过时、内容未变、自检断引用、内核
拒绝后还原、恢复备份、路径穿越、备份轮换。mihomo -t 那条离线路径需要
真内核二进制,测不到,离线行为仍靠人工验证。
2026-08-11 14:41:56 +08:00
im0o a4d08892f7 路由页一轮交互细化:草稿预览、预选、若干修正
上半部分收敛成一条规则:草稿和已保存一致时显示内核现状;不一致时显示
按草稿保存后的样子,将移除的画删除线、新增的虚线描边,即将被移除的当前
选中用危险色警示。点击语义只看卡片:现在就存在的成员点了立刻生效,保存
后才加入的点了是预选,保存成功后再作为运行时动作选上。预览的集合运算在
web/member-preview.ts,组件里只留接线。

其余:订阅选择器重写成可搜索多选(原平铺标签订阅一多就是一面墙);删除
入站换成页面内确认对话框;列表右列解析到最终出口并标注链式;「高级配置」
更名「配置」;表单重置改按内容比较(按引用的话任何一次运行时刷新都会冲掉
未保存的草稿,watch 传返回数组的 getter 同样触发这个问题);保存后先刷
运行时再换配置数据,避免卡片区闪空。
2026-08-11 14:39:47 +08:00
im0o 6bedce215c SELECT 组的点名成员 AUTO 排第一,不再静默回退到直连
内核给没有选中记录的组默认用第一个成员,选中的线路被订阅刷新删掉时也
静默回退到第一个成员(selector.go:103-112)。之前第一位是 DIRECT,这两种
情况都会让一路入站在无人察觉时变成直连,对按端口定向出站的场景是泄露。

另加 sharedPeers():「共用」标记只在真有别的入站落在同一个组上时才该出现,
只有自己在用时它是噪音。
2026-08-11 14:39:26 +08:00
im0o 17f8b3f3fa 保存链路:内核在线时跳过 mihomo -t,离线时写盘不再回滚
实测 mihomo -t 占整条保存链路九成以上耗时(4-9.4 秒),而 PUT /configs
自带同一套解析、失败时什么都不应用,配合既有的还原备份等于校验,重载
只要几十到几百毫秒。代价是写盘到还原之间的短暂窗口里磁盘上存在一份
可能有错的配置。

顺带修正离线行为:原来重载连不上会把刚写好的文件还原掉,但内核起不来
时用这个界面修配置正是核心场景,现在离线走 mihomo -t 校验后写盘不重载。
2026-08-11 14:39:09 +08:00
im0o 49038ce5f9 术语表和校验脚本跟上订阅副本的 file 实现
订阅副本改成 type: file 后,认亲靠 path 而不是 url,脚本里解构已不存在的
url 字段会拿到 undefined;术语表补上 0007 引入的专属组、链式代理、入口组、
订阅副本四个词条,并移除已被取代的筛选组。
2026-08-11 14:38:51 +08:00
im0o e9e2eb555d 每路入站配一组专属策略组,换线不再改配置文件
原本换一条线要改 config.yaml、跑校验、等内核重载,几秒起步;而换线恰恰是这个工具
最高频的动作,把它放在最重的路径上方向就错了。现在每一路配一个 select 组加 AUTO、
FALLBACK 两个,入站的 proxy 永久指向 select,换线只是调一次内核接口。这样做的前提是
选中不会丢:内核把它写进 cache 文件,重启后由 patchSelectGroup 恢复,而
profile.store-selected 默认为真。

链式代理靠给来源订阅复制一份声明、挂上 override.dialer-proxy 实现,因为这个字段写在
策略组上内核只会打条日志然后忽略。副本是 type: file 读原订阅已经落盘的缓存,而不是
自己再拉一次:自己拉的话订阅地址一不可达副本就是空的,那一路入站会一条线都不剩,
表现是端口还在监听却没有任何出口。

顺带把 shared/outbound-chain.ts 里「只有 Selector 接受手动选择」那条断言删了,它是
错的(内核检查的是 SelectAble 接口,URLTest 和 Fallback 也实现了)。界面仍然只让
Selector 可点,但理由换成了另外两种的手动指定会被它们自己撤销。

完整取舍见工作区决策记录 0007。
2026-08-11 03:10:38 +08:00