fix(gpui): sync settings resets and pane gutters
This commit is contained in:
1 parent
4852049495
commit
e41b3c8e7c
4 files changed
+110
-25
No files matched your search
+14
-2
@@ -8,11 +8,23 @@ Every release entry is provided in English and Simplified Chinese.
|
||||
|
||||
### English
|
||||
|
||||
No unreleased changes yet.
|
||||
#### Fixed
|
||||
- **Common formulas such as `$E=mc^2$` and `$n!$` are recognized without reinterpreting shell variables, prompts, prices, or `sed`/`grep` BRE groups as mathematics.** Inline delimiters may cross terminal soft wraps but no longer join unrelated hard lines.
|
||||
- **Resetting a settings drop-down immediately restores its displayed default value** instead of updating the saved setting while leaving the old selection visible.
|
||||
- **Rounded terminal panes no longer expose an outline or a gap along their top edge** in either light or dark themes; the shell paint now follows the pane's actual left, right, bottom, and zero-width top gutters.
|
||||
|
||||
#### Improved
|
||||
- **Terminal TeX rendering now detects formulas from terminal content instead of a recognized AI process name**, so block formulas remain available through WSL, SSH, and full-screen agent interfaces. Standard `$...$`, `$$...$$`, `\(...\)`, and `\[...\]` forms, Markdown-damaged bare delimiters, compact fraction arguments such as `\frac13`, dimmed reasoning output, cell backgrounds, and overlapping persisted formulas are handled more reliably.
|
||||
|
||||
### 简体中文
|
||||
|
||||
暂无未发布改动。
|
||||
#### 修复
|
||||
- **`$E=mc^2$`、`$n!$` 等常见公式可以被正确识别,同时不会把 shell 变量、提示符、价格或 `sed`/`grep` 的 BRE 分组误当成数学公式。** 行内定界符可以跨终端软换行,但不会再连接互不相关的真实换行。
|
||||
- **还原设置下拉框后会立即显示默认选项**,不再出现设置值已经写回、界面却仍停留在旧选项的情况。
|
||||
- **浅色与深色主题下,圆角终端 pane 四周不再露出线框或顶部缝隙**;壳层绘制现在严格采用 pane 真实的左、右、下卡缝以及零宽度上卡缝。
|
||||
|
||||
#### 改进
|
||||
- **终端 TeX 渲染改为根据终端内容识别公式,不再依赖识别 AI 进程名称**,因此块级公式可继续用于 WSL、SSH 和全屏 Agent 界面。标准 `$...$`、`$$...$$`、`\(...\)`、`\[...\]`、Markdown 损坏后的裸定界符、`\frac13` 等紧凑分式参数、DIM 推理输出、单元格背景和持久化公式重叠都能得到更可靠的处理。
|
||||
|
||||
## 1.3.1 - 2026-08-25
|
||||
|
||||
|
||||
@@ -392,7 +392,10 @@ pub struct SettingsPane {
|
||||
about_logo: Arc<Image>,
|
||||
about_update: AboutUpdateState,
|
||||
about_update_seq: u64,
|
||||
selects: Vec<(&'static str, SharedSelect)>,
|
||||
/// 每项还带着自己的 `values` 表:`SelectState` 只认索引,而从代码侧
|
||||
/// 改设置(还原默认值、命令面板切换)时手里只有配置文件记号,没有
|
||||
/// 这张表就没法把闭框的选中项拉回去。
|
||||
selects: Vec<(&'static str, SharedSelect, &'static [&'static str])>,
|
||||
shell_select: SharedShellSelect,
|
||||
/// 外观页背景色:闭态 combobox + 开态 SV/色相/色板/hex 浮层(旧壳
|
||||
/// `SettingsDropdown::BackgroundColor`,不是组件库 ColorPicker)。
|
||||
@@ -489,7 +492,7 @@ pub struct SettingsPane {
|
||||
impl SettingsPane {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let runtime = RuntimeSettings::load();
|
||||
let mut selects: Vec<(&'static str, SharedSelect)> = Vec::new();
|
||||
let mut selects: Vec<(&'static str, SharedSelect, &'static [&'static str])> = Vec::new();
|
||||
let mut subscriptions = Vec::new();
|
||||
|
||||
let mut add_select = |key: &'static str,
|
||||
@@ -523,7 +526,7 @@ impl SettingsPane {
|
||||
}
|
||||
},
|
||||
));
|
||||
selects.push((key, select));
|
||||
selects.push((key, select, values));
|
||||
};
|
||||
|
||||
let cursor_current =
|
||||
@@ -1285,7 +1288,34 @@ impl SettingsPane {
|
||||
}
|
||||
|
||||
pub(super) fn select_of(&self, key: &str) -> Option<SharedSelect> {
|
||||
self.selects.iter().find(|(k, _)| *k == key).map(|(_, entity)| entity.clone())
|
||||
self.selects.iter().find(|(k, _, _)| *k == key).map(|(_, entity, _)| entity.clone())
|
||||
}
|
||||
|
||||
/// 从代码侧改掉某项设置后,把闭框的选中项拉回新值。
|
||||
///
|
||||
/// `SelectState` 自己持有选中索引,而 [`Self::persist`] 只写设置文件和
|
||||
/// `runtime`——两者之间没有任何联系。少了这一步,"还原为默认值"会把值
|
||||
/// 真的写回去、撤销图标也如期消失,但闭框仍显示旧标签,看上去就是
|
||||
/// 撤销按钮没反应。
|
||||
///
|
||||
/// 值不在 `values` 里(光标形状的"未设置"写空串)时落到第 0 项,与
|
||||
/// `add_select` 建初始索引时的同一条回落规则一致。
|
||||
fn sync_select(
|
||||
&mut self,
|
||||
key: &str,
|
||||
value: &str,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some((_, select, values)) = self.selects.iter().find(|(k, _, _)| *k == key) else {
|
||||
return;
|
||||
};
|
||||
let row = values.iter().position(|candidate| *candidate == value).unwrap_or(0);
|
||||
// `set_selected_index` 只改索引和 `final_selected_index`,不发
|
||||
// `Confirm`,所以不会绕回订阅里再 persist 一次。
|
||||
select.clone().update(cx, |state, cx| {
|
||||
state.set_selected_index(Some(IndexPath::default().row(row)), window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn select_row(
|
||||
@@ -1551,8 +1581,11 @@ impl SettingsPane {
|
||||
label,
|
||||
desc,
|
||||
dirty,
|
||||
move |this, _, cx| {
|
||||
move |this, window, cx| {
|
||||
this.persist(&[(key, factory.clone())], cx);
|
||||
// 开关行读 `runtime`,notify 就够;下拉框自己存索引,
|
||||
// 必须显式拉回,否则撤销只改了值不改显示。
|
||||
this.sync_select(key, &factory, window, cx);
|
||||
},
|
||||
control,
|
||||
cx,
|
||||
|
||||
@@ -211,30 +211,51 @@ pub fn card_content_bg(cx: &App) -> Hsla {
|
||||
///
|
||||
/// GPUI 没有凹圆角 primitive,因此四角按物理像素扫描圆外区域;卡本身的
|
||||
/// 抗锯齿圆角仍是唯一可见边,壳色不会侵入卡内形成第二次 alpha 叠加。
|
||||
pub fn paint_shell_around_card(bounds: Bounds<Pixels>, window: &mut Window, cx: &App) {
|
||||
///
|
||||
/// `insets` 必须是调用方**真实**的卡缝,四边分别给——不能在这里假设对称。
|
||||
/// 终端卡的上边距是零(08-26 裁定:侧栏 / 终端卡 / 右侧抽屉三列顶边都贴
|
||||
/// chrome 下沿),而左右和底部是 8px。这里若按四边 8px 推算,卡矩形整体
|
||||
/// 下移 8px:左右两条壳色带从 `y+8` 起画,卡真正的上两个圆角却在 `y`,
|
||||
/// 于是 `y..y+8` 里圆角外侧那两块三角没有任何人覆盖——深色主题下露的是
|
||||
/// 深色不易察觉,浅色主题下直接露出一圈白边和一道顶部白缝。
|
||||
pub fn paint_shell_around_card(
|
||||
bounds: Bounds<Pixels>,
|
||||
insets: gpui::Edges<f32>,
|
||||
window: &mut Window,
|
||||
cx: &App,
|
||||
) {
|
||||
let color = cx.theme().background;
|
||||
let width = f32::from(bounds.size.width);
|
||||
let height = f32::from(bounds.size.height);
|
||||
let inset = 8.0_f32.min(width * 0.5).min(height * 0.5);
|
||||
if width <= 0.0 || height <= 0.0 || inset <= 0.0 {
|
||||
if width <= 0.0 || height <= 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 逐边钳制,且后一边扣掉前一边已占的空间:窄窗口下两侧卡缝相加超过
|
||||
// 容器宽度时,卡矩形不能退化成负尺寸。
|
||||
let top = insets.top.clamp(0.0, height);
|
||||
let bottom = insets.bottom.clamp(0.0, height - top);
|
||||
let left = insets.left.clamp(0.0, width);
|
||||
let right = insets.right.clamp(0.0, width - left);
|
||||
|
||||
let x = f32::from(bounds.origin.x);
|
||||
let y = f32::from(bounds.origin.y);
|
||||
let card_w = (width - inset * 2.0).max(0.0);
|
||||
let card_h = (height - inset * 2.0).max(0.0);
|
||||
let card_x = x + left;
|
||||
let card_y = y + top;
|
||||
let card_w = (width - left - right).max(0.0);
|
||||
let card_h = (height - top - bottom).max(0.0);
|
||||
let paint = |window: &mut Window, x: f32, y: f32, w: f32, h: f32| {
|
||||
if w > 0.0 && h > 0.0 {
|
||||
window.paint_quad(fill(Bounds::new(point(px(x), px(y)), size(px(w), px(h))), color));
|
||||
}
|
||||
};
|
||||
|
||||
// 四条带互不重叠,每个像素只承受一次壳色 alpha。
|
||||
paint(window, x, y, width, inset);
|
||||
paint(window, x, y + height - inset, width, inset);
|
||||
paint(window, x, y + inset, inset, card_h);
|
||||
paint(window, x + width - inset, y + inset, inset, card_h);
|
||||
// 四条带互不重叠,每个像素只承受一次壳色 alpha。某一边卡缝为零时
|
||||
// 对应的带宽度为零,`paint` 直接跳过。
|
||||
paint(window, x, y, width, top);
|
||||
paint(window, x, y + height - bottom, width, bottom);
|
||||
paint(window, x, card_y, left, card_h);
|
||||
paint(window, x + width - right, card_y, right, card_h);
|
||||
|
||||
let radius = crate::display::UI_SHELL_RADIUS_LOGICAL.min(card_w * 0.5).min(card_h * 0.5);
|
||||
if radius <= 0.0 {
|
||||
@@ -242,8 +263,6 @@ pub fn paint_shell_around_card(bounds: Bounds<Pixels>, window: &mut Window, cx:
|
||||
}
|
||||
let step = 1.0 / window.scale_factor().max(1.0);
|
||||
let rows = (radius / step).ceil() as usize;
|
||||
let card_x = x + inset;
|
||||
let card_y = y + inset;
|
||||
let card_right = card_x + card_w;
|
||||
let card_bottom = card_y + card_h;
|
||||
for row in 0..rows {
|
||||
|
||||
@@ -110,6 +110,11 @@ const PALETTE_KEY_CONTEXT: &str = "NebulaCommandPalette";
|
||||
const SIDEBAR_RESIZE_VISUAL_OFFSET: f32 = 8.0;
|
||||
const SIDEBAR_RESIZE_HANDLE_WIDTH: f32 = 6.0;
|
||||
|
||||
/// 终端卡的左 / 右 / 下卡缝(上边为零,见卡容器处的裁定)。布局的 padding
|
||||
/// 与 `paint_shell_around_card` 的壳色带必须读同一个数——两边各写一份
|
||||
/// 字面量就是那圈白边的来源。
|
||||
const CARD_GUTTER: f32 = 8.0;
|
||||
|
||||
/// 工作区静态默认绑定的 combo 集([`init`] 的镜像)。撤销已失效的自定义
|
||||
/// 注入时要排除:gpui 的 NoAction 打在静态默认键上会误杀基础功能。
|
||||
const STATIC_DEFAULT_COMBOS: &[&str] = &[
|
||||
@@ -4530,9 +4535,13 @@ impl Render for NebulaWorkspace {
|
||||
.child(
|
||||
// 终端卡(一体化外壳):唯一的结构分界。圆角与旧壳卡
|
||||
// 同源(UI_SHELL_RADIUS_LOGICAL=14),无描边——融合靠
|
||||
// 壳色包围圆角卡本身,不靠线框。侧栏模式四边保留旧壳
|
||||
// 8px 卡缝;顶部 tab 模式取消上边距,让 tab 底边与正文
|
||||
// 直接相接,左右和底部卡缝不变。
|
||||
// 壳色包围圆角卡本身,不靠线框。
|
||||
//
|
||||
// 上边距一律为零:侧栏 / 终端卡 / 右侧抽屉三列的顶边都贴
|
||||
// chrome 下沿(用户 08-26 裁定「pane 和左侧 tab 抬到和文件树
|
||||
// 顶部一致」)。左右和底部保留旧壳 8px 卡缝,抽屉在场时那条
|
||||
// 右卡缝就是它和终端之间的呼吸位,不再归零——归零后的观感是
|
||||
// 文件树压在终端上。
|
||||
div()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
@@ -4541,16 +4550,28 @@ impl Render for NebulaWorkspace {
|
||||
gpui::canvas(
|
||||
|_, _, _| (),
|
||||
|bounds, _, window, cx| {
|
||||
// 卡缝必须和下面的 `px_2` / `pb_2` 逐边
|
||||
// 对上:上边距为零是上面那条裁定,壳色
|
||||
// 若按四边对称推算,卡的上两个圆角外侧
|
||||
// 会漏覆盖,浅色主题下露出一圈白边。
|
||||
crate::gpui_shell::theme::paint_shell_around_card(
|
||||
bounds, window, cx,
|
||||
bounds,
|
||||
gpui::Edges {
|
||||
top: 0.0,
|
||||
right: CARD_GUTTER,
|
||||
bottom: CARD_GUTTER,
|
||||
left: CARD_GUTTER,
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
},
|
||||
)
|
||||
.absolute()
|
||||
.inset_0(),
|
||||
)
|
||||
.when(top_tabs, |card| card.px_2().pb(px(8.0)))
|
||||
.when(!top_tabs, |card| card.p_2())
|
||||
.px(px(CARD_GUTTER))
|
||||
.pb(px(CARD_GUTTER))
|
||||
.child(
|
||||
div()
|
||||
.size_full()
|
||||
|
||||
Reference in new issue
Block a user