Skip to content

💄 桌面与移动三页列表统一为自适应列表行,修复窄窗口下脚本名被挤没 - #1709

Open
CodFrm wants to merge 17 commits into
mainfrom
feat/unified-list-rows
Open

💄 桌面与移动三页列表统一为自适应列表行,修复窄窗口下脚本名被挤没#1709
CodFrm wants to merge 17 commits into
mainfrom
feat/unified-list-rows

Conversation

@CodFrm

@CodFrm CodFrm commented Aug 31, 2026

Copy link
Copy Markdown
Member

Checklist / 检查清单

  • Fixes mentioned issues / 修复已提及的问题
  • Code reviewed by human / 代码通过人工检查
  • Changes tested / 已完成测试

背景

桌面三页(已安装脚本 / 回收站 / 订阅)此前各自用固定像素列的表格渲染,存在三个可复现的问题:

  1. 刚性列宽把名称列挤没。 改动前 ScriptTable.tsx 的表头与行使用固定像素列:勾选 32 + 拖拽 32 + 开关 64 + 来源 76 + 标签 100 + 应用至 140 + 更新 132 + 操作 192,合计约 660px;名称列只能吃剩余空间。内容区不足时名称被压到 0 宽。这正是 [BUG] 窗口分辨率较小时界面不完整 #1698 报告的现象——803px 窗口下脚本名一个字都看不见,而来源、标签两列在多数脚本上是半空的。
  2. 订阅页在窄窗口下整体横向滚动。 表头与行同时施加 min-w-[820px],加上侧栏 200px,窗口窄于约 1020px 时出现横向滚动条。
  3. 三页风格分叉。 每页各有一套表格 + 一套卡片网格,共六套渲染分支,行高、悬停底色、操作区位置互不一致。

本次改动

桌面行统一为「左锚 + 自适应中段 + 右锚」骨架(新增 src/pages/components/ui/list-row.tsx)。骨架只固化几何——行高 56px、左右锚区宽度、圆角、悬停与选中底色、操作区常驻半透明;槽位内容由各页自行决定,不做带 isTrash / hasSwitch 这类参数分支的共享业务行。来源与标签由固定列降级为名称下方元信息行的行内徽章,释放出的宽度给名称。

表头移除,排序迁入工具栏下拉(新增 SortMenu.tsx)。脚本列表提供默认(拖拽顺序)/ 状态 / 名称 / 更新时间,订阅页承接原表头的三个排序键,回收站默认按删除时间。选择默认以外的排序键时行拖拽不可用。表头上的全选框迁入批量操作条;订阅行原本用于承载 # 排序表头的序号列一并移除(Subscribe 实体无顺序字段,排序切换后该序号不代表任何持久语义)。

移动端从卡片网格改为统一列表行 + 原生手势(新增 src/pages/components/ui/mobile-list.tsx)。行高 64px,左滑露出快捷操作,长按或顶栏入口进入多选,点整行打开操作面板。三套卡片网格(ScriptCardGrid / TrashCardGrid / SubscribeCardGrid)随之删除。

响应式让位。 视口窄于 900px 时隐藏生效站点/权限站点槽;窄于 1160px 时行操作区从摊开的按钮组收成「运行 + 更多菜单」;窄于 768px 走移动端布局。

修复一处 main 上既有的渲染缺陷:筛选行的分界线从未显示。 筛选行与批量操作栏同处一个 h-11 overflow-hidden 容器,而收起态的 SelectionBar 虽为 h-0 仍带 1px 下边框,把筛选行整体下推 1px,使筛选行自己的 border-b 落到容器外被裁掉(实测:筛选行 rect 57..101,父容器 rect 56..100)。这条线一直写在 CSS 里但从未渲染,是「列表与上方筛选区分不开」的真正成因。让 SelectionBar 收起态不带边框即修复。分界线恢复后不再需要额外分隔手段:行间不加分隔线,筛选行改用 bg-card 与顶栏合成一条头部带,顶栏原有的 border-b 随之移除(它划在了白带中间而非底部)。回收站为同构结构一并处理;订阅页没有筛选行,顶栏边框本身就是分界,未动。

新手引导「操作」步文案在 10 个语言包里都还写着「操作右侧按钮可以打开高级筛选与切换视图模式」,视图模式切换已随本次改动移除、高级筛选并入工具栏,该句作废,已改写为实际的操作集合并补上窄窗口收进菜单的行为;移动端锚点 m-card-list 随卡片网格删除而名不副实,改为 m-script-list

跟进 e2e。 options-pages-smoke / keep-alive / utils 三处用已被移除的 view-toggle 当列表就绪信号,改用工具栏搜索框;script-management 的行内删除确认由 Popconfirm 改为 AlertDialog,选择器相应改为 [data-slot="alert-dialog-action"]

由运行时验证与一轮代码评审暴露的修正(详见「实现考虑」):左滑位移写死、多选空屏、元信息行徽章在窄窗口逐字竖排冲出行外、操作区宽度随条件按钮变化把更新时间列推得逐行参差、桌面全选框在列表顶部形成只有复选框的空行、1160px 断点处名称塌陷、回收站移动端单条操作只剩隐藏手势、左滑多行同开、aria-hidden 子树可聚焦、长按无位移容差、移动端拖拽未限制垂直轴、移动端行 memo 逐字段比较漏字段。

实现考虑

  • 共享的是几何骨架,不是业务行。 三页字段集互不相同(回收站无开关有倒计时,订阅无勾选与拖拽),共享业务行组件并用布尔参数分支会退化成堆满可选参数的适配器夹心,因此骨架只认锚位与尺寸。
  • 操作区断点取 1160px 而非 1000px。 摊开态(最多 7 个按钮 + 满配 min-w)比紧凑态多占 160px,这 160px 全部由 flex-1 的中段承担。实测在 1000px 断点下,视口从 999px 变到 1000px 时脚本名可视宽度从 253px 掉到 150px——「窗口拉宽反而更窄」。改到 1160px 后该区间恢复单调。
  • 分界线问题修根因而非补第二条线。 直接给筛选行再加一条可见分隔或给列表加底色都能掩盖症状,但真实原因是收起态元素的 1px 边框造成的 1px 位移 + 同高裁剪容器。
  • 未采用给列表加底色的做法。 设置页与 Agent 页的既有约定是 chrome 白、页面灰、内容更亮;给列表加 bg-muted 会让内容面比页面底更暗,方向相反,且该表面层级在应用别处不存在。实测暗色下列表底色会靠近 border 令牌,把分界线本身从 1.161 削弱到 1.095。
  • 右缘对齐靠固定槽位。 普通脚本没有运行按钮时保留等宽占位;宽排布给操作区定满配 min-w 并右对齐,避免条件按钮(主页/用户配置/云端)的有无把左侧的更新时间列推歪。
  • 左滑开合受控于列表。 同一时刻只允许一行滑开,否则多行会各自挂着破坏性操作块;滑开态点内容层是收起而非透传为行点击。关闭态的操作区同时置 aria-hiddeninert——只有 pointer-events-none 时键盘仍能 Tab 进这棵不播报的子树并触发「删除」。
  • 回收站移动端补了操作面板。 还原与彻底删除此前只存在于左滑手势,而脚本、订阅两页都有「点整行开面板」这条可见退路。彻底删除走列表级模态,避免面板关闭连带销毁挂在面板项上的气泡确认——同样的原因,桌面行内删除也从 Popconfirm 改成了 AlertDialog
  • 行 memo 一律按对象引用比较。 store 对任一字段变更都会为该行生成新对象引用,逐字段比较会漏掉 name / metadata / selfMetadata,而行内读的正是 i18nName(script)metadata.version,会显示过期名称或版本。
  • 回收站事件二分护栏未被削弱。 进入回收站只触发「脚本不再活跃」的反应,销毁关联数据仍只由彻底删除触发;trash_event_partition.test.tsTrashNameStyle.test.ts 的文件守卫已随文件重命名同步。
  • 翻译docs/translation.md 与各语言 terminology-<locale>.md 处理:复用各语言 common/script/editor.json 中既有的编辑/运行/停止/导出/删除/更多操作用词,不新造术语;后台脚本与定时脚本按术语规范分开表述;各 locale 原有的文档链接 href 与链接文字保持不变。

已知限制

  • 行内从脚本名到最右操作图标横跨约 845px,且行间不设分隔线,密集列表下左右关联依赖视线追踪。缩短右锚区跨度是可选后续,本次未做。
  • 移动端多选时批量操作条位于全局 BottomTabBar 之上而非替换它。替换需要在 App.tsx / layout/ 增加布局级插槽,超出本次改动范围。
  • 移动端手势验证使用合成触摸事件,未在真实触摸设备上回归。长按 500ms 时延与 10px 位移容差的手感、以及长按后浏览器是否补发 click,均未经真机确认。
  • 900px 断点在临界点仍有小幅跳变(900px 下名称 210px、803px 下 229px),是断点固有现象,未再加档位平滑。1160px 断点已消除同类跳变。
  • 三个断点(768 的 JS 断点、900 与 1160 的 CSS 断点)都对视口生效,而行的实际可用宽度是「视口 − 侧栏」,侧栏折叠会带来 144px 差异。改用容器查询更准确,本次未做。
  • 宽窄两套操作区同时挂载(另一套由 display:none 隐藏),每行多出一个 DropdownMenu.Root 与若干隐藏按钮节点。Radix 的 Content 懒挂,增量主要是 DOM 节点而非重排成本,评估为可接受。
  • 一轮代码评审提出但本次未处理的三条低危项:FaviconDots 默认 maxShow = 5 而调用处未传值,与 104px 槽位和设计意图(4 个)不符;Badge 渲染 <div> 却位于 <span> 元信息行内,属非法 HTML 嵌套;同一页存在模态与气泡两种删除二次确认形态。
  • 引导第 4 步的高亮锚点落在首行;首行若是普通脚本则不含运行按钮,文案提到的运行/停止在该行不可见。此为改动前既有行为。

建议审查重点

  • SelectionBar 收起态去掉边框后,多选栏展开/收起动画与筛选行的 1px 位移是否还有残留(该栏与筛选行共用一个 h-11 overflow-hidden 容器)。
  • 三页在 1160px 与 900px 两个断点上下的行渲染,尤其是操作区两套排布共用同一批 handler 与同一个删除确认框的部分(ScriptList/components.tsxScriptRowActionSlots)。
  • MobileSwipeRow 改为受控后,三个消费方(ScriptRowsMobile / TrashListMobile / SubscribeRowsMobile)的列表级开合状态与行 memo 的配合。
  • 拖拽排序与排序键的互斥:选择非默认排序键时 DraggableRow 应被禁用。
  • 回收站的还原/彻底删除路径,以及 trash_event_partition.test.ts 的分区断言是否仍覆盖重命名后的文件。
  • 10 个语言包中 guide.script_list_action_content 的措辞是否符合各语言的术语规范(按既有 UI 用词组织,但非母语校对)。

关联

close #1698

验证

绑定的修订:base 8cbe9832a592bbc3a2cd15a67ca9ed7bdc9d050b(origin/main),head 543c7155e72a5a48e13ed5617936c975add1c660git diff --shortstat 8cbe9832...543c7155 = 68 files changed, 3485 insertions(+), 1680 deletions(-),与 GitHub 返回的 +3485/-1680 in 68 files 一致。

CI(run 33358261813,绑定 head 543c7155):9 项检查全部通过,含 Lint、单测 2 个 shard、e2e 4 个 shard、License Compliance。

本地(在 head 543c7155 上运行):

  • pnpm lint → prettier / tsc / check:i18n / check:issue-templates 均 exit 0
  • npx vitest run src/pages/components/ui src/pages/options/routes/ScriptList src/pages/options/routes/SubscribeList → 46 files / 259 passed
  • 本机整仓 pnpm test:ci 会出现数量不定的 Test timed out in 340ms/850ms(无断言失败,每次失败集合不同),系本机负载叠加该仓较紧的超时预算;以 CI 的两个单测 shard 结果为准。

浏览器验证(加载 dist/ext 的一次性 Playwright 会话,非提交的 e2e 套件):

  • 宽度扫描 1440 / 1280 / 1160 / 1159 / 1000 / 900 / 803 / 768 / 700 / 480,每档记录脚本名可用宽度、可见操作按钮数、行高、documentElement.scrollWidthclientWidth。全程 scrollWidth == clientWidth(无横向溢出),桌面行高恒 56px、移动端恒 64px。
  • [BUG] 窗口分辨率较小时界面不完整 #1698 复现宽度 803px:以一个会被截断的长脚本名测量,名称可用宽度由改动前的 81px 增至 229px;768px 由 46px 增至 194px。名称在任何宽度下不再归零,768px 以下切移动布局后名称占整行。
  • 断点单调性:999→1000px 名称保持 253px;1159→1160px(紧凑↔摊开)同样保持 253px,可见操作按钮数由 6 跳变为 16。修复前在 1000px 处为 253px→150px。
  • 右缘对齐:各宽度下所有行的 list-row-trailing 起点横坐标逐行一致(如 1280px 下全为 816)。
  • 分界线渲染:沿 x=900 纵向扫描灰度剖面。修复前 y48..99 = 255(一路白到底,无线)、y100 = 246;修复后 y48..98 = 255y99 = 229(线)、y100 = 246。对比度:线↔头部带 1.260(亮)/ 1.272(暗),线↔列表 1.207 / 1.161。
  • 背景方案取样对比:对「仅去分隔线」「筛选行并入头部」「再给列表加 bg-muted/40」三种处理分别渲染亮暗两套并取像素测算,确认加底色方案在暗色下会把分界线从 1.161 削弱到 1.095,故未采用。
  • 25 行密度渲染:分别在有/无行间分隔线两种形态下渲染 25 条脚本的亮暗两套,用于判断密集列表的可读性。
  • 移动端(390px 视口):滑开第 1 行后再滑开第 2 行,操作区状态由 ["open","closed","closed"] 变为 ["closed","open","closed"](单开生效);滑开态点行主体后状态全为 closed 且未弹出操作面板(dialog 计数 0);关闭态操作区 inert=true
  • 菜单实际项:最简脚本为 编辑/导出/删除,含 @homepage + @cloudcat 的脚本为 编辑/脚本主页/上传到云端/导出/删除。
  • 新手引导在构建产物中自行弹出并逐步走完 6 步,每步锚点命中、卡片文案与 UserConfig 链接渲染正常。

未执行:人工代码评审;真实触摸设备回归;非中英文译文的母语校对;明暗主题的实际对比度比对仅针对上述分隔面,未覆盖全部前景色。

Screenshots / 截图

截图与录屏存放在工作树的 e2e/scratch/(该目录被 gitignore,无法随分支提交,也无法经 CLI 上传到本 PR)。需要时可由维护者手动附加,关键几张为断点两侧的 1160/1159、#1698 复现宽度 803、25 行密度下的亮暗两套、以及移动端走查录屏。

CodFrm added 17 commits August 30, 2026 12:16
表格的固定列宽合计约 660px,名称列只能吃剩下的空间,而来源、标签两列
在多数脚本上半空;操作列预留 192px 却因按钮条件渲染仍然右缘参差。

改为「左锚 + 自适应中段 + 右锚」的列表行:新增 ListRow 槽位骨架,只固化
行高、悬停/选中/禁用外观与操作槽的常驻半透明,锚区宽度由消费者给出——
脚本列表左锚有勾选/拖拽/开关,回收站只有勾选,预设宽度会立刻退化成带
消费者判别参数的适配器。骨架在本次提交内即由两页共同验证。

来源与标签降级为元信息行的行内徽章,名称区从 520px 增至约 800px。
操作区固定三槽(编辑 / 运行·停止 / 更多),槽位数与脚本类型无关,右缘
不再参差;条件性操作收进「更多」菜单。

表头移除后:排序改由工具栏 SortMenu 承载,三态循环仍复用 sort.ts 的
nextSortState(键类型放宽为泛型以供回收站共用);全选改由列表上方的常驻
控件承载,避免随表头一起消失;新手引导锚点由 col-* 改挂到行内 row-*。

视图切换与卡片视图一并移除,已持久化的 viewMode 读取时忽略,同一份偏好
里的筛选、搜索、排序照常读出。
表头的三个排序键与状态筛选迁入工具栏下拉,版本与来源徽章下沉到名称下方的
元信息行,移除表头与行上的 min-w-[820px](窄窗口不再横向滚动)。
排序三态循环改由 SortMenu 统一承担,index.tsx 不再自带一份。
新增移动端槽位骨架 mobile-list(行/左滑外壳/长按/选择模式外壳/底部操作面板),
由脚本列表、回收站、订阅三页同时消费;四个卡片组件(ScriptCardGrid /
TrashCardGrid / SubscribeCardGrid 与随之失去引用的 ScriptRowActions)删除。

- 脚本列表:行左图标两行文本、右开关;整行点击开面板;左滑编辑/删除;
  长按或顶栏入口进多选,顶栏换取消/已选数/全选,底部换批量操作条。
- 回收站:右锚区改过期倒计时,左滑还原/彻底删除,多选批量还原/彻底删除。
- 订阅:右锚区开关,左滑删除;来源、更新时间与检查更新入口移入操作面板。
- 同步两个跨层文件守卫的文件清单(trash_event_partition / TrashNameStyle)。
排序下拉缺「默认」项,三态循环要点两次才回得到自然顺序;
更多菜单缺规格列出的「导出」,改用 synchronizeClient.export([uuid])。
左滑内容位移写死 -translate-x-32(128px),订阅只有一个操作块(64px)时
会滑出一段空白;改为按操作区实际宽度位移。
列表在多选期间被清空时会停在只剩批量操作条的空屏上,改为退回普通视图。
- 左滑操作区未滑动时就盖住行右侧(挡住开关):操作区是 absolute,
  普通流的内容层压不住它,内容层补 relative。
- 移动端次行在窄屏折行把内容挤出固定行高:改为按顺序水平裁切。
- 桌面运行槽占位 size-8 与按钮 size-7 不等宽,普通脚本行的编辑按钮
  比后台脚本行左移 4px,右缘并未真正对齐。
- 桌面更新时间列 76px 装不下中文相对时间而折行,加宽并禁止换行;
  底部操作面板末项在无安全区的设备上贴住屏幕底边。
card 与 background 两个表面令牌只差 4.4%(亮)/9.5%(暗),靠底色分不开层;
border 才是这套令牌里最强的分隔手段(1.207 / 1.161)。列表容器改用 divide-y,
线只出现在行与行之间,首行上方与末行下方不画;骨架去掉圆角,使悬停底色为通栏
矩形而非圆角块。移动端行本就有分隔线,不动。

方案在设计稿 Zone 11.3 定稿(方案 C)。同区另存了方案 G(分隔线 + 加深
--background),因其需连带改 background/border/input/muted-foreground 四个
全站令牌才能成立,本轮不采用。
表头随列表化移除后,全选框被单独摆成一行,视觉上是一条只有一个复选框的空行。
改为挂在 SelectionBar 上(脚本列表与回收站共用),列表顶部直接从第一行开始。
代价是全选变两步:先勾一行让批量条展开,再点栏内全选。
来源徽章与标签徽章作为 flex 项没有 shrink-0,窗口收窄时被压到内容宽度以下,
中文逐字换行成竖排并冲出 56px 的固定行高,与相邻行文字重叠(803px 下最明显)。
徽章改为不可收缩、元信息行改为溢出裁剪,空间不足时从尾部截断,与名称行的
truncate 行为一致。回收站的徽章本来就有 shrink-0,未受影响。
close #1698 的窄窗口部分:768~900px 这一段名称只剩 46~81px,几乎读不出脚本名。
脚本列表的生效站点点与订阅页的权限图标在这个宽度下让位,名称回到 162~197px。
运行状态仍可从操作区的运行/停止图标读出;整槽同进同退,行右缘对齐不变。
之前把编辑等操作一律收进 ⋯,宽窗口下反而比 main 多绕一步。改为按宽度分两套排布:
1000px 以上摊开主页/用户配置/云端/运行/编辑/导出/删除,与 main 的常驻可发现性一致;
1000px 以下这些按钮会把脚本名挤没(#1698),换成运行槽 + ⋯ 菜单。
两套共用同一批 handler 与删除确认框,运行按钮只定义一次。
序号在 main 上的搭档是表头的 # 排序触发器;表头随列表化移除后它只剩纯行号。
Subscribe 实体没有顺序字段,排序下拉一切换就重新编号,故不代表任何持久语义。
去掉后左锚区只剩开关,与另外两页一致,并把 40px 还给订阅名。
主页/用户配置/云端三个按钮按元数据条件渲染,操作区宽度随之逐行不同,
把左侧的更新时间与站点列一起推歪(1280px 下实测相差 118px)。
给宽排布定 7 按钮满配的 min-w 并右对齐:图标仍右缘对齐,左侧各列恢复同列。
原文说「操作右侧按钮可以打开高级筛选与切换视图模式」,视图模式切换已随列表化移除、
高级筛选也已并入工具栏下拉,该句在 10 个语言包里都已失效。改写为实际的操作集合,
并补上窄窗口收进「更多操作」菜单这一行为;按 terminology-<locale>.md 复用各语言
既有的编辑/运行/停止/导出/删除/更多操作用词,后台与定时脚本分开表述而非统称后台。

移动端锚点 m-card-list 随卡片网格删除而名不副实,改为 m-script-list。
三处引用了本分支删掉的 UI:options-pages-smoke 与 keep-alive 用视图模式切换按钮
当脚本列表就绪信号,utils 的保存回退路径同样如此,该按钮已随表头列表化移除;
script-management 的行内删除确认从 Popconfirm 换成了 AlertDialog。
锚点统一改用工具栏搜索框,确认按钮改用 data-slot 选择器保持语言无关。
行操作区断点 1000 → 1160:摊开态比紧凑态多占 160px,实测 1000px 处脚本名会从
253px 直接掉到 150px,即「窗口拉宽反而更窄」;1160px 起摊开态才不会更窄。

回收站移动端补操作面板:还原与彻底删除此前只存在于左滑手势,而脚本、订阅两页
都有「点整行开面板」这条可见退路,回收站没有;彻底删除走列表级模态,避免面板
关闭连带销毁气泡确认。

左滑改为受控于列表:同一时刻只允许一行滑开;滑开态点内容层是收起而非透传为行
点击,与移动端通行心智一致,也避免紧挨删除块的误触。

关闭态操作区补 inert:原先只有 aria-hidden + pointer-events-none,键盘仍能 Tab
进这棵不播报的子树并触发「删除」。

长按加 10px 位移容差:原先任何 touchmove 都无条件取消,真机上手指自然抖动就进不了多选。

移动端拖拽补 restrictToVerticalAxis 与 accessibility,与桌面一致:横向偏移超过
滑动阈值会在重排完成的同时滑开删除块。

移动端行 memo 改为按对象引用比较:原逐字段比较漏掉 name/metadata/selfMetadata,
而行内读的正是 i18nName 与 metadata.version,会显示过期名称或版本。
筛选行与批量操作栏同处一个 h-11 overflow-hidden 容器,而收起态的 SelectionBar
虽为 h-0 仍带 1px 下边框,把筛选行整体下推 1px,导致筛选行自己的 border-b 落到
容器外被裁掉——这条分界线一直写在 CSS 里但从未渲染,是「列表与筛选没有区分」的
真正成因,改动前的 main 上同样存在。让 SelectionBar 收起态不带边框即可。

分界线恢复后不再需要额外手段:行间分隔线去掉、悬停恢复圆角;筛选行改用 bg-card
与顶栏合成一条头部带,顶栏原有的 border-b 随之移除(它划在了白带中间而非底部)。
回收站为同构结构,一并处理;订阅页没有筛选行,顶栏边框本身就是分界,不动。

未采用给列表加底色的做法:设置页与 Agent 页的既有约定是 chrome 白、页面灰、
内容更亮,给列表加 bg-muted 会让内容面比页面底更暗,方向相反;且实测暗色下
列表底色靠近 border 令牌,会把分界线本身削弱到 1.095。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] 窗口分辨率较小时界面不完整

1 participant