From 8c0539a6f0f910ed500b51149d28e57154dfa73b Mon Sep 17 00:00:00 2001
From: ThreeFish
6 views · 101 commands · 11 settings · 372 unit tests
+6 views · 101 commands · 11 settings · 375 unit tests
## Features @@ -78,7 +78,7 @@ See the [Engineering Plan](./docs/architecture/engineering-plan.md), the [Implem pnpm install # install dependencies pnpm run compile # type-check + lint + build pnpm run watch # watch build (press F5 to launch the Extension Host debugger) -pnpm run test:unit # unit tests (pure engine logic, Vitest, 324 cases) +pnpm run test:unit # unit tests (pure engine logic, Vitest, 375 cases) pnpm run test:integration # integration tests (@vscode/test-electron) pnpm run package # production build pnpm dlx @vscode/vsce package # package the .vsix diff --git a/docs/.agents/issue.md b/docs/.agents/issue.md index 31de2b1..628942c 100644 --- a/docs/.agents/issue.md +++ b/docs/.agents/issue.md @@ -98,7 +98,7 @@ - **表因**:用户反馈 Worktrees 视图展开后无法继续缩小(截图中仍占大片空白),要求「所有视图可拖到任意高度、取消最小高度限制」。 - **根因**:侧边栏每个视图面板(`Pane`)的最小体高由 VS Code 核心**硬编码 = 120px**(竖直方向;构造函数 `this._minimumBodySize = ... orientation === HORIZONTAL ? 200 : 120`,见 `src/vs/base/browser/ui/splitview/paneview.ts`),加 22px 标题栏,展开态最小 ≈ **142px**,该值经 `minimumSize` 直接驱动 SplitView 拖拽分隔条下限。`WebviewViewPane extends ViewPane` **未覆写** `minimumBodySize`,故本扩展 2 个 webview(Commit/Graph)与 4 个 tree(Branches/Stash/Shelf/Worktrees)视图**共用同一 142px 下限**。允许扩展为活动栏容器内视图指定固定/最小/最大高度的官方特性请求 [microsoft/vscode#123715](https://github.com/microsoft/vscode/issues/123715) 已被**关闭为 not planned / out-of-scope**,从未新增任何 API 或 `package.json` 贡献点。扩展运行于独立进程,拿不到工作台面板对象,`minimumBodySize` setter 仅核心 `ViewPaneContainer` 调用;注入 CSS 亦无效(`.pane-body{min-height:0}` 改不动 JS 层用于夹取拖拽下限的 `minimumSize`)。 -- **处理方式**:该限制**无法经扩展解除**,采用受支持的折中缓解——在 `package.json` `contributes.views` 调初始布局默认值:次要视图 Stash/Shelf 设 `visibility:"collapsed"`(默认仅 22px 标题栏、点击即展开),Worktrees 保持 `visible`(仅以 `initialSize` 权重收窄),全部视图加 `initialSize`(Commit 3 / Graph 3 / Branches 2 / 其余 1,类 CSS flex 的高度权重)。两字段经 `src/vs/workbench/api/browser/viewsExtensionPoint.ts` 的 `viewDescriptor` schema 确认可用;`initialSize` **仅当「同一扩展同时拥有视图与视图容器」时生效**(本扩展拥有 `hyper-git` 容器与全部视图,条件满足)。 +- **处理方式**:该限制**无法经扩展解除**,采用受支持的折中缓解——在 `package.json` `contributes.views` 调初始布局默认值:次要视图 Stash/Shelf 设 `visibility:"collapsed"`(默认仅 22px 标题栏、点击即展开),Worktrees 保持 `visible`(仅以 `initialSize` 权重收窄),全部视图加 `initialSize`(Commit 3 / Graph 3 / Branches 2 / 其余 1,类 CSS flex 的高度权重)。两字段经 `src/vs/workbench/api/browser/viewsExtensionPoint.ts` 的 `viewDescriptor` schema 确认可用;`initialSize` **仅当「同一扩展同时拥有视图与视图容器」时生效**(本扩展拥有 `hyper-git` 容器与全部视图,条件满足)。**注**:此处枚举系 v0.0.12 时点缓解快照——自 #16 起默认布局改为 Stash/Shelf `hidden`、Commit/Worktrees `collapsed`、顺序重排为 Commit·Branches·Graph·Worktrees,**当前默认布局以 #16 为准**。 - **后续防范**:① VS Code 侧边栏视图存在约 **142px 硬性最小展开高度**,无法经扩展降低——遇「任意高度 / 无最小高度」类诉求应直接引 #123715(not planned)说明平台边界,**勿承诺实现**;判断「webview 内容 CSS `min-height`」与「外层面板最小高度」是两回事。② `visibility` / `initialSize` **只影响初始状态**(「用户手动折叠/移动/隐藏过后即不再生效」)——老用户需命令面板「View: Reset View Locations」或右键容器图标「Reset Location」才采用新默认;实机验证须用**干净 profile 或先重置**以规避持久化布局。③ 想让展开视图更紧凑,只能靠「减少常驻视图数(默认折叠)+ 权重」,而非解除下限。 - **同类问题影响**:所有向活动栏/侧边栏容器贡献 TreeView/WebviewView 且希望自定义或取消视图高度的扩展;凡把「webview 内容 `min-height` CSS」误认为能改变外层面板最小高度的实现。 @@ -131,4 +131,12 @@ - **后续防范**:① 为差异的「缺失端」构造 URI 时,**一律用 git 空树 ref**,不要对不存在对象取 `'HEAD'` / `${hash}^` 这类具名 ref——它们只在旧版 VS Code(容错 readFile)上侥幸可用,新版必抛 FileNotFound。② VS Code git 扩展内部行为(如 readFile 容错性)随版本变化,复用其 `toGitUri` 时须确认跨 `engines.vscode` 下限到当前主线的兼容矩阵;空树 ref 是少数有契约保障的「稳定回空」途径。③ webview 的 `data-*` 属性应承载**数据**(机器可用的稳定 key/路径),展示串(含 `"old → new"` 这类人为拼接)只放在可见标签文本里——二者混用会导致 `joinPath` 之类以数据为输入的下游崩溃。④ 逐文件级语义(status)须端到端透传到决策点(host 命令),勿用提交级布尔(`hasParent`)模糊替代——后者无法区分单文件是 A/D/M/R。⑤ 测试断言「差异已打开」时勿按标签计数(VS Code `{preview:true}` 会复用预览槽替换而非新增),应先 `closeAllEditors` 再按差异标题(含文件名)匹配标签。 - **同类问题影响**:所有消费 vscode.git `toGitUri` 自建差异打开逻辑的扩展,凡为缺失端取具名 ref 的均在新版 VS Code 复现;凡 webview `data-path` 复用展示串(含分隔符)的实现均会在路径拼接处崩溃;以及任何「逐文件操作」误用「提交级 / 全局级」标志判定单文件形态的设计。 +## #16 面板视图默认布局调整(Stash/Shelf 默认隐藏 · Commit/Worktrees 默认折叠 · 顺序重排) + +- **表因**:用户(附 VS Code「视图显隐」右键菜单截图)要求——Stash / Shelf **默认不显示**、仅在用户勾选后出现;其余视图显示顺序为 **Commit → Branches → Graph → Worktrees**;且 Commit 与 Worktrees **默认折叠**。 +- **根因**:非缺陷,系默认布局的 UX 决策落地。视图的显隐 / 顺序 / 初始尺寸**纯由 `package.json` `contributes.views` 声明式驱动**(`src/` 无任何代码断言或依赖,两处 `.focus` 在折叠/可见态均正常),可零代码达成。此前 #12 缓解取值(Stash/Shelf `collapsed`、Worktrees/Commit/Graph/Branches `visible`)与本诉求不符,需重排数组并改写 `visibility`。 +- **处理方式**:重排 `contributes.views["hyper-git"]` 数组为 `commit → branches → log → worktrees → stash → shelf → changesBadge`(容器内顺序取声明顺序);`visibility` 改写为 Commit/Worktrees=`collapsed`、Branches/Graph=`visible`、**Stash/Shelf=`hidden`**(VS Code `src/vs/workbench/api/browser/viewsExtensionPoint.ts` 的 `viewDescriptor` schema 确认 `visibility` 枚举为 `['visible','hidden','collapsed']`,`hidden` 映射 `hideByDefault:true`——不入容器但「可经视图菜单发现/勾选」,正对附图未勾选态);`initialSize` 权重与 `changesBadge`(`when:false`,恒末位)不变。命令 / 协议 / 菜单 / `viewsWelcome` 均按 id 匹配、与顺序无关,零破坏。新增 [`tests/unit/views-layout.test.ts`](../../tests/unit/views-layout.test.ts) 声明式护栏锁定顺序 + 各视图 `visibility` + `changesBadge` 的 `when:false`。 +- **后续防范**:① `visibility` / 顺序 / `initialSize` **仅影响全新安装 / 干净 profile 的初始态**(复用 #12 结论,不重述)——老用户须「View: Reset View Locations」或右键容器「Reset Location」方生效,实机验证须用干净 profile。② `visibility:"hidden"` ≠ `when:"false"`:前者「默认隐藏但用户可经视图菜单勾选恢复」(Stash/Shelf),后者「恒不渲染」(changesBadge 角标承载专用)——诉求「默认不显示但用户可自行开启」**必须**用 `hidden`,误用 `when:false` 会致用户无法启用。③ 容器**内**视图相对顺序可控(声明顺序驱动),但容器相对内置页签的顺序不可控(见 #13),勿承诺后者。④ 三态语义以 VS Code schema 为权威,勿据「本仓此前只用过 `visible`/`collapsed`」臆断 `hidden` 不存在。 +- **同类问题影响**:所有以 `contributes.views` 声明默认布局的 VS Code 扩展;凡将「默认隐藏但可恢复」误用 `when:false`(致用户无法启用)或反向混淆的实现;以及把「仅初始态生效」误当「持久强制」而困惑于老用户升级后不生效的排障。 + diff --git a/docs/i18n/zh-CN/README.md b/docs/i18n/zh-CN/README.md index 67883e3..7387354 100644 --- a/docs/i18n/zh-CN/README.md +++ b/docs/i18n/zh-CN/README.md @@ -16,7 +16,7 @@ English · 简体中文 -6 视图 · 101 命令 · 11 配置项 · 372 单元测试
+6 视图 · 101 命令 · 11 配置项 · 375 单元测试
## 核心能力 @@ -78,7 +78,7 @@ pnpm install # 安装依赖 pnpm run compile # 类型检查 + lint + 构建 pnpm run watch # 监听构建(F5 启动 Extension Host 调试) -pnpm run test:unit # 单元测试(engine 纯逻辑,Vitest,324 例) +pnpm run test:unit # 单元测试(engine 纯逻辑,Vitest,375 例) pnpm run test:integration # 集成测试(@vscode/test-electron) pnpm run package # 生产构建 pnpm dlx @vscode/vsce package # 打包 .vsix diff --git a/package.json b/package.json index 01a087d..d2eded3 100644 --- a/package.json +++ b/package.json @@ -62,9 +62,15 @@ "id": "hyperGit.commit", "name": "Commit", "type": "webview", - "visibility": "visible", + "visibility": "collapsed", "initialSize": 3 }, + { + "id": "hyperGit.branches", + "name": "Branches", + "visibility": "visible", + "initialSize": 2 + }, { "id": "hyperGit.log", "name": "Graph", @@ -73,27 +79,21 @@ "initialSize": 3 }, { - "id": "hyperGit.branches", - "name": "Branches", - "visibility": "visible", - "initialSize": 2 + "id": "hyperGit.worktrees", + "name": "Worktrees", + "visibility": "collapsed", + "initialSize": 1 }, { "id": "hyperGit.stash", "name": "Stash", - "visibility": "collapsed", + "visibility": "hidden", "initialSize": 1 }, { "id": "hyperGit.shelf", "name": "Shelf", - "visibility": "collapsed", - "initialSize": 1 - }, - { - "id": "hyperGit.worktrees", - "name": "Worktrees", - "visibility": "visible", + "visibility": "hidden", "initialSize": 1 }, { diff --git a/tests/unit/views-layout.test.ts b/tests/unit/views-layout.test.ts new file mode 100644 index 0000000..a4a6ab2 --- /dev/null +++ b/tests/unit/views-layout.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect } from 'vitest'; +import pkg from '../../package.json'; + +/** + * 面板视图默认布局护栏:`contributes.views["hyper-git"]` 的声明顺序与各视图 + * `visibility` 纯由 package.json 驱动(`src/` 无代码断言/依赖),一旦被误改, + * 用户可见的默认排序与显隐即随之漂移。此处锁定「设计基线」,呼应本仓 + * `--topo-order` 回归护栏文化。语义见 docs/.agents/issue.md #16(决策)/ #12(平台约束)。 + */ + +/** VS Code view descriptor 的最小子集(仅取本护栏关心的字段)。 */ +interface ViewDescriptor { + id: string; + visibility?: string; + when?: string; +} + +const views = pkg.contributes.views['hyper-git'] as unknown as ViewDescriptor[]; +/** 用户可见视图 = 排除 `when:false` 的角标承载视图(changesBadge,永不渲染)。 */ +const userViews = views.filter((v) => v.when !== 'false'); + +describe('Hyper Git 面板视图默认布局(package.json 声明式护栏)', () => { + it('用户可见视图声明顺序固定为 Commit·Branches·Graph·Worktrees·Stash·Shelf', () => { + expect(userViews.map((v) => v.id)).toEqual([ + 'hyperGit.commit', + 'hyperGit.branches', + 'hyperGit.log', + 'hyperGit.worktrees', + 'hyperGit.stash', + 'hyperGit.shelf', + ]); + }); + + it('各视图默认 visibility 符合设计(Commit/Worktrees 折叠、Branches/Graph 展开、Stash/Shelf 隐藏)', () => { + const visibilityById = Object.fromEntries(userViews.map((v) => [v.id, v.visibility])); + expect(visibilityById).toEqual({ + 'hyperGit.commit': 'collapsed', + 'hyperGit.branches': 'visible', + 'hyperGit.log': 'visible', + 'hyperGit.worktrees': 'collapsed', + 'hyperGit.stash': 'hidden', + 'hyperGit.shelf': 'hidden', + }); + }); + + it('changesBadge 为 when:false 的隐藏角标承载视图,且恒定末位', () => { + const badge = views.find((v) => v.id === 'hyperGit.changesBadge'); + expect(badge?.when).toBe('false'); + expect(badge?.visibility).toBeUndefined(); + expect(views[views.length - 1].id).toBe('hyperGit.changesBadge'); + }); +});