docs(picker): record the pwsh-first DPI-aware Windows picker fix

README pairs document the pwsh-preferred adapter and the PowerShell 7
requirement for the modern dialog; the 2026-07-27 picker note's Windows
adapter fact is updated in place, and a new bug-fix note records the
defect, the fallback decision, and the DPI awareness rationale.
This commit is contained in:
Huanqi Cao
2026-08-02 00:08:58 +08:00
parent a92ffff10a
commit 5c51589665
9 changed files with 68 additions and 8 deletions

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-08-01-windows-picker-pwsh-dpi.md
2026-08-01-windows-picker-pwsh-dpi.md: 2c90821be3e4800d624cb2f54dcd6661756784bc
2026-08-01-windows-picker-pwsh-dpi.zh.md: ff8a535af396ed5ede51cbd3c69c1944d28ce95e

View File

@@ -0,0 +1,26 @@
# Agent Note: Windows directory picker prefers pwsh and forces DPI awareness
Status: implemented
English | [中文](2026-08-01-windows-picker-pwsh-dpi.zh.md)
## Problem
The Windows branch of the native directory picker spawned Windows PowerShell 5.1's `FolderBrowserDialog`, which .NET Framework hardwires to the legacy `SHBrowseForFolder` tree dialog: no address bar, search, or quick access. The same process is DPI-unaware (`powershell.exe` declares no DPI awareness), so on scaled displays Windows renders the dialog at 96 DPI and bitmap-stretches it — blurry text and soft edges. Both defects were visible at once on any display above 100 % scaling.
## Decision
The win32 branch in `packages/host/directory-picker-native` now spawns `pwsh.exe` (PowerShell 7) first and falls back to `powershell.exe` (Windows PowerShell 5.1) only when pwsh is missing (`ENOENT`), mirroring the Zenity→KDialog fallback. PowerShell 7's WinForms `FolderBrowserDialog` supports `AutoUpgradeEnabled` (added in .NET Core 3.0, absent from .NET Framework) and renders the modern Explorer-style folder picker. Both runtimes execute the identical script, which calls `SetProcessDPIAware()` (user32) before any window exists, so the dialog is system-DPI-aware no matter which host serves it. `-STA` stays explicit for both, and the fallback keeps the seam's cancellation/failure contract (`null` on cancel, a retryable error otherwise). The host-boundary, RPC trust, and cancellation decisions stay with the [picker feature note](../feature/2026-07-27-native-workspace-directory-picker.md).
## Alternatives considered
- **Require PowerShell 7.** Rejected: pwsh is not a Windows built-in, so machines without it would lose the only workspace-creation route; the 5.1 fallback keeps the dialog functional, and DPI is corrected there too.
- **Import `resolvePwshPath` from `dsh-pwsh-local`.** Rejected for this change: a host GUI package importing from a bash-executor package is a cross-seam coupling, and PATH-based `execFile` resolution plus `ENOENT` fallback already covers the practical installs (Program Files, Store aliases); single-source resolution remains a follow-up if the two consumers drift.
- **Set DPI awareness in the harness process.** Rejected: DPI awareness is per-process, and the dialog lives in a spawned child that inherits nothing from the parent's absent declaration.
- **Per-monitor v2 (`SetProcessDpiAwarenessContext`).** Deferred: system-aware is the ceiling .NET Framework WinForms supports, the shell dialog handles per-monitor rendering itself on modern Windows, and one call keeps both runtimes on a single code path.
## Consequences
- Machines with PowerShell 7 get the modern folder picker; 5.1-only machines keep the legacy tree — now sharp — and the package README's Known Limitations documents the gap.
- No new packages or runtime dependencies; the fallback reuses the existing `ENOENT` classification and abort propagation.
- The command boundary (`DirectoryPickerRunner`) pins the spawn order and script content in unit tests; real dialog rendering remains a manual Windows check, as before.

View File

@@ -0,0 +1,26 @@
# Agent Note: Windows 目录选择器优先 pwsh 并强制 DPI awareness
Status: implemented
[English](2026-08-01-windows-picker-pwsh-dpi.md) | 中文
## 问题
原生目录选择器的 Windows 分支原先启动 Windows PowerShell 5.1 的 `FolderBrowserDialog`,而 .NET Framework 将其硬编码为旧版 `SHBrowseForFolder` 树形对话框:没有地址栏、搜索或快速访问。同一进程又是 DPI-unaware 的(`powershell.exe` 未声明任何 DPI awareness因此在缩放显示器上Windows 会以 96 DPI 渲染该对话框再位图拉伸——文字模糊、边缘发虚。任何超过 100% 缩放的显示器上,两个缺陷同时可见。
## 决策
`packages/host/directory-picker-native` 的 win32 分支现在先启动 `pwsh.exe`PowerShell 7仅当 pwsh 缺失(`ENOENT`)时才回退到 `powershell.exe`Windows PowerShell 5.1),与 Zenity→KDialog 的回退方式一致。PowerShell 7 的 WinForms `FolderBrowserDialog` 支持 `AutoUpgradeEnabled`.NET Core 3.0 加入;.NET Framework 没有),呈现现代资源管理器风格文件夹选择器。两个运行时执行完全相同的脚本,脚本在任何窗口存在前调用 `SetProcessDPIAware()`user32因此无论由哪个宿主服务对话框都系统 DPI aware。两个运行时都显式保留 `-STA`;回退维持 seam 的取消/失败契约(取消返回 `null`其余为可重试错误。宿主边界、RPC 信任与取消决策仍归[选择器功能 Note](../feature/2026-07-27-native-workspace-directory-picker.md)所有。
## 考虑过的替代方案
- **强制要求 PowerShell 7。** 否决pwsh 并非 Windows 内置没有它的机器将失去唯一的工作区创建路径5.1 回退保持对话框可用,且 DPI 在那里同样被修正。
- **从 `dsh-pwsh-local` 导入 `resolvePwshPath`。** 本变更否决host GUI 包依赖 bash 执行器包是跨 seam 耦合PATH 上的 `execFile` 解析加 `ENOENT` 回退已覆盖实际安装形态Program Files、Store 别名);若两个消费者日后漂移,单一来源解析留作后续。
- **在 harness 进程内设置 DPI awareness。** 否决DPI awareness 是进程级的,而对话框位于派生的子进程中,不会继承父进程缺失的声明。
- **Per-monitor v2`SetProcessDpiAwarenessContext`)。** 暂缓system-aware 是 .NET Framework WinForms 的上限,现代 Windows 中 shell 对话框自身处理 per-monitor 渲染,且一次调用让两个运行时共用一条代码路径。
## 后果
- 装有 PowerShell 7 的机器获得现代文件夹选择器;只有 5.1 的机器保留旧版树——但现在清晰了——包 README 的已知限制记录了该差距。
- 无新增包或运行时依赖;回退复用既有的 `ENOENT` 分类与中止传播。
- 命令边界(`DirectoryPickerRunner`)在单元测试中固定启动顺序与脚本内容;真实对话框渲染仍与以前一样属于手动 Windows 检查。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-27-native-workspace-directory-picker.md
2026-07-27-native-workspace-directory-picker.md: 98f9dc9bed5358e816d4324462d5ea7657f9007f
2026-07-27-native-workspace-directory-picker.zh.md: ca765778fae734fd47a05652aea7021328ed4ab6
2026-07-27-native-workspace-directory-picker.md: c18b4263d4e97290d69ac229e7423558bdb4c3b1
2026-07-27-native-workspace-directory-picker.zh.md: 7267516d7eea4b3cfecc2ca8f18305896eaffede

View File

@@ -30,7 +30,7 @@ The native dialog RPC is accepted only from a loopback socket with same-origin b
Platform adapters invoke native tools without a shell:
- macOS: `osascript` and the system folder chooser.
- Windows: PowerShell in STA mode and `FolderBrowserDialog`.
- Windows: `pwsh` (PowerShell 7) in STA mode with a Windows PowerShell 5.1 fallback, always DPI-aware ([picker fix](../bug-fix/2026-08-01-windows-picker-pwsh-dpi.md)).
- Linux: `zenity`, with `kdialog` as a fallback when Zenity is unavailable.
## Alternatives considered

View File

@@ -30,7 +30,7 @@ Status: implemented
平台适配器不经 shell直接调用原生工具
- macOS`osascript` 和系统文件夹选择器。
- Windows采用 STA 模式的 PowerShell `FolderBrowserDialog`
- Windows采用 STA 模式的 `pwsh`PowerShell 7并以 Windows PowerShell 5.1 回退,且始终 DPI aware见[选择器修复](../bug-fix/2026-08-01-windows-picker-pwsh-dpi.md)
- Linux使用 `zenity`Zenity 不可用时回退到 `kdialog`
## 考虑过的替代方案

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/host/directory-picker-native/README.md
README.md: 0b54c651d4f5382021d0f8832ab4f1146b7652c8
README.zh.md: e5ac2762a691a16a7e6d9d6dd9aefc70a59dcd4f
README.md: ab4326fed886e9bb2fa550ae9865550eed7c2583
README.zh.md: cb2e067d340df1696e1b1195ec99f6d63509cc20

View File

@@ -2,7 +2,7 @@
English | [中文](README.zh.md)
The **native-OS-chooser backend** of the [directory-picker seam](../directory-picker/README.md): `NativeDirectoryPicker` registers `ctx.directoryPicker` with the `native` capability, whose `pick(signal)` opens one native chooser per call and resolves the chosen absolute path (`null` on cancel). Platform tools run without a shell: `osascript` on macOS, an STA PowerShell `FolderBrowserDialog` on Windows, and Zenity with a KDialog fallback on Linux; the caller's abort terminates the native process. Only viable when the operator sits at the host's display — remote deployments compose [`-browse`](../directory-picker-browse/README.md) instead. The command boundary (`DirectoryPickerRunner`) and platform facts are injectable for deterministic tests. The shared no-shell subprocess runner lives in [`dsh-native-command`](../../util/native-command/README.md).
The **native-OS-chooser backend** of the [directory-picker seam](../directory-picker/README.md): `NativeDirectoryPicker` registers `ctx.directoryPicker` with the `native` capability, whose `pick(signal)` opens one native chooser per call and resolves the chosen absolute path (`null` on cancel). Platform tools run without a shell: `osascript` on macOS, `pwsh` (PowerShell 7) with a Windows PowerShell 5.1 fallback on Windows — the dialog script opts the process into system DPI awareness — and Zenity with a KDialog fallback on Linux; the caller's abort terminates the native process. Only viable when the operator sits at the host's display — remote deployments compose [`-browse`](../directory-picker-browse/README.md) instead. The command boundary (`DirectoryPickerRunner`) and platform facts are injectable for deterministic tests. The shared no-shell subprocess runner lives in [`dsh-native-command`](../../util/native-command/README.md).
**Dual-face package**: the browser half (`./client`) registers a renderless flow occupant into [ui-workspace's](../../client/ui-workspace/README.md) two directory-flow holes — each `open` request drives `host.pickDirectory` and reports the one outcome (picked path / cancel / failure) through the hole's owner conversation. One cordis.yml row therefore composes both sides of the native interaction; the client carries no capability-kind branching, and mounting a second flow package fails at load (the holes are `single` kind).
@@ -17,3 +17,4 @@ None; this package neither assembles nor sends a provider request.
## Known Limitations and Deferred Work
- **Linux requires desktop tooling** — with neither Zenity nor KDialog installed, `pick` rejects with an actionable error; it does not fall back to a typed-path prompt (the browse backend is that fallback at the composition level).
- **Windows needs PowerShell 7 for the modern picker** — `pwsh` renders the Explorer-style folder dialog; a machine with only Windows PowerShell 5.1 falls back to the legacy folder tree, DPI-corrected but not the modern UI.

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文
[目录选择 seam](../directory-picker/README.md) 的**原生 OS 选择器后端**`NativeDirectoryPicker``native` 能力注册 `ctx.directoryPicker`,其 `pick(signal)` 每次调用打开一个原生选择器并解析出所选绝对路径(取消时为 `null`)。平台工具不经 shell 调用macOS 使用 `osascript`Windows 使用以 STA 模式运行的 PowerShell `FolderBrowserDialog`Linux 使用 Zenity 并以 KDialog 回退;调用方的中止信号会终止原生进程。只有操作者坐在宿主屏幕前时才可用——远程部署应组合 [`-browse`](../directory-picker-browse/README.md)。命令边界(`DirectoryPickerRunner`)与平台事实可注入,便于确定性测试。共享的免 shell 子进程运行器位于 [`dsh-native-command`](../../util/native-command/README.md)。
[目录选择 seam](../directory-picker/README.md) 的**原生 OS 选择器后端**`NativeDirectoryPicker``native` 能力注册 `ctx.directoryPicker`,其 `pick(signal)` 每次调用打开一个原生选择器并解析出所选绝对路径(取消时为 `null`)。平台工具不经 shell 调用macOS 使用 `osascript`Windows 使用 `pwsh`PowerShell 7并以 Windows PowerShell 5.1 回退——对话框脚本会把进程设为系统 DPI aware——Linux 使用 Zenity 并以 KDialog 回退;调用方的中止信号会终止原生进程。只有操作者坐在宿主屏幕前时才可用——远程部署应组合 [`-browse`](../directory-picker-browse/README.md)。命令边界(`DirectoryPickerRunner`)与平台事实可注入,便于确定性测试。共享的免 shell 子进程运行器位于 [`dsh-native-command`](../../util/native-command/README.md)。
**双面包**browser half`./client`)向 [ui-workspace](../../client/ui-workspace/README.md) 的两个目录流洞注册一个无渲染的流程占用者——每次 `open` 请求驱动 `host.pickDirectory`,并经洞的 owner 会话上报唯一结果(所选路径/取消/失败)。因此一行 cordis.yml 同时组合原生交互的两侧client 侧不含任何能力 kind 分支,挂载第二个流程包会在加载期失败(洞为 `single` kind
@@ -17,3 +17,4 @@
## 已知限制与延期工作
- **Linux 依赖桌面工具**——Zenity 与 KDialog 均未安装时,`pick` 以包含解决建议的错误拒绝;它不会回退为手输路径提示(组合层面的回退是 browse 后端)。
- **Windows 需要 PowerShell 7 才能使用现代选择器**——`pwsh` 呈现资源管理器风格的文件夹对话框;只有 Windows PowerShell 5.1 的机器会回退到旧版文件夹树DPI 已修正,但界面不是现代的。