Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,20 @@
- **验证方式**:`cargo test -p one-core --lib`(`popup_window::reuse_contract_tests`(`every_popup_intercepts_the_native_close_route`:两类弹窗都要装关闭路线、且这条路线在 `reuse_key` 分支之外;`popup_windows_are_hidden_and_never_destroyed`:`close_window_for_reuse` 里只允许剩一处 `window.remove_window();`(`ClosePlan::Destroy`,即「不是弹窗」与没开保护的 `Ok(false)`);`close_plan` 的 `Err(_)` 必须是 `ClosePlan::Retain`、`close_window_after_save` 必须在 `Retained` 时 `push_notification`;`a_failed_hide_keeps_the_window_instead_of_destroying_it`(注入隐藏失败)、`save_flows_close_through_the_save_aware_funnel`、`save_flows_flip_the_form_into_its_saved_state`、`one_shot_popups_are_registered_as_parked`)、`popup_lifecycle::tests`)、`cargo check -p main -p db_view -p extension_view --all-targets`、`cargo test -p main --bin navop setting_tab`。计数判据:复用键弹窗首次打开 `live_windows` +1 后不再增长、关闭后 `live_sessions` 回落;`opened_windows` 随开关次数上涨说明复用没命中;**一次性弹窗的 `live_windows` 随打开次数上涨是预期行为**(停放,见 `docs/macos-memory-investigation.md` §10.7),但它的**绝对量**由 `docs/macos-window-reuse-acceptance.md` §5.1 的资源预算卡住(超预算就暂停往新窗口类型接复用,而不是把结论按「能解释」放过去)。**本机无 Touch Bar,崩溃无法本地复现**,行为层面必须真机验证,自动化只能靠 `include_str!` 源码契约守住「关闭路径必须走会话卸载」。注意这类契约对格式敏感:`popup_window_tests.rs` 用 `find("struct PopupWindowContent")` 切片,改结构体可见性或字段会让它的 `expect` 直接 panic,锚点要跟着改。
- **适用范围**:`crates/core/src/{window_close,popup_window,popup_lifecycle}.rs`、`crates/core/src/popup_window_tests.rs`,以及所有 `open_reusable_popup_window` 调用点(更新对话框、代理设置、表导入/导出、SQL 运行/转储、比较窗口、离线扩展包对话框)。

- **标题**:剪贴板 TSV 必须成对转义,否则「复制整行后粘贴丢字段」(issue #355)
- **触发信号**:复制一行/一块表格数据,粘贴后列错位或少了若干字段,而剪贴板里看着数据是全的;典型诱因是某个字段里含 `\t`、`\n`、`\r` 或 `"`(多行文本、JSON、带引号的备注)。
- **根因 / 约束**:可编辑表格(`one_ui::edit_table`)与结果集(`db_view::table_data`)共用同一套剪贴板格式「`\t` 分列、`\n` 分行」,但两侧各自手写:复制侧 `join("\t")` 原样写值,粘贴侧 `text.lines()` + `split('\t')` 裸切。值本身含分隔符时,复制那一刻就已经在剪贴板上被切成多个字段,粘贴侧无从还原——字段被挤到旁边的列或下一行,观感就是「丢了字段」。CSV 导出那条路早有 RFC 4180 引号转义,说明剪贴板链路只是没跟上。
- **正确做法**:编解码收敛到 `one_ui::edit_table::tsv`(`escape_tsv_field` / `encode_tsv_row(s)` / `parse_tsv_rows`),四个读写点全走它:`EditTableState::action_copy` / `action_paste`(Ctrl+C/V)与 `CopyFormatter::format_tsv` / `results_delegate` 右键粘贴。规则:字段含 `\t`/`\n`/`\r`/`"` 才加引号并双写内部 `"`(普通值保持原样,粘到 Excel 也是标准格式);**只有整段文本含 `\t`、换行或 `""` 时才按引号规则解转义**,这样从别处复制单值(JSON 片段、`C:\new\test` 这类路径)仍是原样写入,不会被误当引号字段抹掉引号。`\r\n` 算一个换行、末尾换行不补空行,与旧的 `str::lines()` 行为对齐(否则「复制一行」粘回来会多一行)。已知遗留:值恰为字面量 `\N` 与 SQL NULL 的标记仍无法区分,粘贴侧没有 NULL 语义,这次**没有**动它。
- **验证方式**:`cargo test -p one-ui --lib tsv`(编解码单测:往返、引号/换行/制表符/回车/空值,加 `clipboard_sites_use_the_shared_codec` 契约测)、`cargo test -p db_view --lib copy_format`(`clipboard_tsv_round_trip_is_lossless` 走真实 `CopyFormatter::format(Tsv, ..)` → `parse_tsv_rows`:按旧规则先跑会精确复现「字段含制表符 → 4 列」「字段含换行 → 多一行且值错位」,改完转绿;`table_data_clipboard_sites_use_the_shared_codec` 用 `include_str!` 钉住两个右侧读写点不许退回 `split('\t')`)。回归面:`cargo test -p db_view --lib`(884)、`cargo test -p one-ui --lib`(114)、`cargo clippy -p one-ui -p db_view --all-targets`、`cargo check -p main`。
- **适用范围**:`crates/one_ui/src/edit_table/{tsv.rs,tsv_tests.rs,state.rs,mod.rs}`、`crates/db_view/src/table_data/{copy_format.rs,results_delegate.rs}`;任何新增「写/读表格剪贴板」的调用点都必须走这套编解码,不要各自 `join("\t")` / `split('\t')`(`results_delegate` 的「复制列名」是给 SQL 用的,故意不转义)。

- **标题**:拖拽分隔条的抓取区必须骑在边界两侧,细线别被 block 布局丢到盒子起点(issue #333)
- **触发信号**:某条拖拽分隔条「很难拉」——光标要像素级对准某几个像素才变形,稍微偏一点就退回默认状态;或者给分隔条容器写了 `justify_center` / `items_center`,细线却跑到盒子起点/顶端。
- **根因 / 约束**:这类站点把热区整条缩在边界一侧。`one_ui::resize_handle` 的 `HandlePlacement::Left`/`Right` 形态是 `w(1px)` + 单侧 `padding`(taffy 是 border-box,实测热区只有 4px),且 `left(px(1.))` / `right(px(1.))` 让细线不在边界上;`redis_view` 值表表头是 6px 整条缩在左列右缘里。从另一侧靠近时完全没有反馈。另一个更隐蔽的坑:gpui 的容器默认是 `display: block`(`Style::default().display = Block`,`div()` 不自动 flex),此时 `justify_content` / `align_items` **静默失效**,子元素被 block 布局丢到内容起点——`justify_center` 看着写了、实际没生效,必须显式 `.flex()`。
- **正确做法**:热区宽度取「分隔线 + 两侧各 `edge_padding`」(`geometry::Resize::hit_area()`),用负边距跨到边界另一侧(`right_0() + mr(-pad)` / `left_0() + ml(-pad)` / `top_0() + mt(-pad)`),细线再用 `justify_center` / `items_center` 留在原位(实测 `right_0().mr(-4).w(9)` ⇒ 盒子 `[边界-5, 边界+4]`、线仍在 `[边界-1, 边界]`,视觉零变化)。相邻单元格紧贴的表头不能靠单个元素跨列:右侧单元格可能带 `overflow_hidden`,跨列的绝对定位会被裁掉,而且后画的兄弟节点先拿到鼠标 ⇒ 由左右两个单元格各渲染一半(各 4px、都留在自己单元格内),合计 8px 跨边界。`render_*_resize_handles` 要返回 `Vec<AnyElement>` 给调用方 `.children(...)`,不能套一层尺寸为零的 wrapper(`top_0`/`bottom_0` 按直接父节点解析,套一层热区高度会变 0)。
- **验证方式**:`cargo test -p one-ui --lib resize_handle_tests`、`cargo test -p redis_view --lib value_table_columns_resize_tests`(在 `#[gpui::test]` 里用 `debug_bounds` 量热区和细线的真实 bounds:边界两侧各铺够、细线停在边界、从边界左右按下都能拖动同一列;含变异验证:负边距改成 0、抓取区退回单边 6px、去掉右列那一半,对应用例都转红)。回归面:`cargo test -p one-ui --lib`(117)、`cargo test -p redis_view --lib`(77)、`cargo clippy -p one-ui -p redis_view --all-targets`、`cargo check -p main --all-targets`。
- **适用范围**:`crates/one_ui/src/resize_handle.rs`(面板分隔条:SQL 结果面板、数据库/Mongo/Redis 侧边栏、终端工具坞共用)、`crates/redis_view/src/value_table_columns.rs`、`crates/db_view/src/{database_table_columns,table_designer_tab}.rs`、`crates/db_view/src/table_data/cell_preview_host.rs`。**故意没改的两处**(不是漏改):`crates/core/src/tab_container.rs` 的侧边栏手柄已经是 9px(`theme_geometry().resize.hit_area()`),它挂在带 `overflow_hidden` 的面板上,跨出去的那半会被裁掉,面板内侧 9px 已经够宽;`crates/terminal_view/src/view/command_bar/render.rs` 的命令栏手柄是 6px 通栏、不锚定某条边界、两侧对称,加高会吃掉终端内容和输入框的点击。要动这两处得先把手柄搬到不裁剪的父层,属于结构改动。

### 执行原则

1. 先澄清,再实现;先缩小边界,再扩展范围。
Expand Down
Loading
Loading