@sparkelf/dsh-patch-composer-popover-boundaries 0.2.0-rc.2 → 0.2.0-rc.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "file": "./patches/composer-popover-boundaries.patch",
9
9
  "id": "composer-popover-center-boundaries",
10
10
  "target": {
11
- "baseRevision": "0a15e36e7f82b6ed45af6fa9759f29b40dcd965d",
11
+ "baseRevision": "ddefc45fbc7f8e46dd73185e68295696d1297887",
12
12
  "kind": "dsh-source",
13
13
  "paths": [
14
14
  "apps/web/tests/",
@@ -35,5 +35,5 @@
35
35
  "url": "git+https://github.com/SparkElf/deepseek-harness-plus.git"
36
36
  },
37
37
  "type": "module",
38
- "version": "0.2.0-rc.2"
38
+ "version": "0.2.0-rc.20"
39
39
  }
@@ -1,8 +1,8 @@
1
1
  diff --git a/apps/web/tests/access-confirmation.e2e.ts b/apps/web/tests/access-confirmation.e2e.ts
2
- index cd64d98110..f513b1114a 100644
2
+ index 2b2ba0e..bd61e45 100644
3
3
  --- a/apps/web/tests/access-confirmation.e2e.ts
4
4
  +++ b/apps/web/tests/access-confirmation.e2e.ts
5
- @@ -53,6 +53,12 @@ describe('web e2e: Full access confirmation', () => {
5
+ @@ -150,6 +150,12 @@ describe('web e2e: experimental Auto and Full access confirmation', () => {
6
6
  expect(await access.getAttribute('aria-label')).toBe('访问模式,当前:工作区内修改')
7
7
 
8
8
  await access.click()
@@ -16,7 +16,7 @@ index cd64d98110..f513b1114a 100644
16
16
  const dialog = page.getByRole('dialog', { name: '确认启用完全权限?' })
17
17
  await dialog.waitFor({ timeout: 10_000 })
18
18
  diff --git a/apps/web/tests/default-model.e2e.ts b/apps/web/tests/default-model.e2e.ts
19
- index 304bdb97f6..0e820ee757 100644
19
+ index 304bdb9..0e820ee 100644
20
20
  --- a/apps/web/tests/default-model.e2e.ts
21
21
  +++ b/apps/web/tests/default-model.e2e.ts
22
22
  @@ -105,6 +105,12 @@ describe('web e2e: the composer model switch is the default for later sessions',
@@ -32,44 +32,11 @@ index 304bdb97f6..0e820ee757 100644
32
32
  await page.getByRole('menuitem', { name: /模型/ }).click()
33
33
  await page.getByRole('menuitemradio', { name: 'Acme Large' }).click()
34
34
 
35
- diff --git a/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx b/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx
36
- index 1736fd8..efd2496 100644
37
- --- a/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx
38
- +++ b/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx
39
- @@ -1,4 +1,4 @@
40
- -import { useEffect, useState } from 'react'
41
- +import { useEffect, useRef, useState } from 'react'
42
- import type { ReactNode } from 'react'
43
- import clsx from 'clsx'
44
- import {
45
- @@ -100,6 +100,8 @@ export function PermissionSelect({
46
- const catalog = usePermissionCatalog(state => state.value)
47
- const [pick, setPick] = useState<string | null>(null)
48
- const [open, setOpen] = useState(false)
49
- + const triggerRef = useRef<HTMLButtonElement | null>(null)
50
- + const boundaryRef = useRef<HTMLElement | null>(null)
51
- const [confirmation, setConfirmation] = useState<string | null>(null)
52
- const [acknowledged, setAcknowledged] = useState(false)
53
-
54
- @@ -192,8 +194,13 @@ export function PermissionSelect({
55
- onClose={() => { setOpen(false) }}
56
- side="top"
57
- portal
58
- + boundaryRef={boundaryRef}
59
- anchor={
60
- <button
61
- + ref={(node) => {
62
- + triggerRef.current = node
63
- + boundaryRef.current = node?.closest<HTMLElement>('[data-dsh-center-col]') ?? null
64
- + }}
65
- type="button"
66
- className={css.trigger}
67
- aria-label={t('mode', { name: currentAccessibleLabel })}
68
35
  diff --git a/packages/client/ui-layout/README.md b/packages/client/ui-layout/README.md
69
- index 4fe19fb9ec..976a4dad8c 100644
36
+ index 582c5fb..bc4dc41 100644
70
37
  --- a/packages/client/ui-layout/README.md
71
38
  +++ b/packages/client/ui-layout/README.md
72
- @@ -43,7 +43,7 @@ The presenter consumes resolved theme snapshots and projects them onto the docum
39
+ @@ -45,7 +45,7 @@ The presenter consumes resolved theme snapshots and projects them onto the docum
73
40
 
74
41
  `selectPanel(id)` checks the live `main` registry before changing selection; an absent key throws and leaves the current panel intact. `beginNavigation()` returns an abort signal for an asynchronous UI navigation. A later call, a valid panel selection (including repeated selection), or layout disposal aborts that signal without cancelling underlying Session creation. Consumers check the signal before committing navigation or moving drafts.
75
42
 
@@ -79,7 +46,7 @@ index 4fe19fb9ec..976a4dad8c 100644
79
46
  </details>
80
47
 
81
48
  diff --git a/packages/client/ui-layout/src/client/AppFrame.tsx b/packages/client/ui-layout/src/client/AppFrame.tsx
82
- index 05bb4b9494..c156255193 100644
49
+ index 323318f..86a33a3 100644
83
50
  --- a/packages/client/ui-layout/src/client/AppFrame.tsx
84
51
  +++ b/packages/client/ui-layout/src/client/AppFrame.tsx
85
52
  @@ -33,7 +33,7 @@ export type AppFrameProps =
@@ -92,7 +59,7 @@ index 05bb4b9494..c156255193 100644
92
59
 
93
60
  /** Subscribe to the main key without subscribing the column frame to each panel id. */
94
61
  diff --git a/packages/client/ui-model-selection/src/client/ModelSelect.module.css b/packages/client/ui-model-selection/src/client/ModelSelect.module.css
95
- index b2cb715287..968d000bc5 100644
62
+ index b2cb715..968d000 100644
96
63
  --- a/packages/client/ui-model-selection/src/client/ModelSelect.module.css
97
64
  +++ b/packages/client/ui-model-selection/src/client/ModelSelect.module.css
98
65
  @@ -104,7 +104,9 @@
@@ -107,10 +74,10 @@ index b2cb715287..968d000bc5 100644
107
74
  min-width: min(240px, calc(100vw - 32px));
108
75
  max-width: min(420px, calc(100vw - 32px));
109
76
  diff --git a/packages/client/ui-model-selection/src/client/ModelSelect.tsx b/packages/client/ui-model-selection/src/client/ModelSelect.tsx
110
- index e9fc0195ef..637ecb5601 100644
77
+ index b5a55f5..7252e44 100644
111
78
  --- a/packages/client/ui-model-selection/src/client/ModelSelect.tsx
112
79
  +++ b/packages/client/ui-model-selection/src/client/ModelSelect.tsx
113
- @@ -137,13 +137,22 @@ export function ModelSelect(
80
+ @@ -167,13 +167,22 @@ export function ModelSelect(
114
81
  const rect = triggerRef.current?.getBoundingClientRect()
115
82
  if (rect === undefined) return
116
83
  const MARGIN = 12
@@ -137,10 +104,10 @@ index e9fc0195ef..637ecb5601 100644
137
104
  // First run measures the hidden pre-render (same commit as `open`), so
138
105
  // the card lands placed before anything paints.
139
106
  diff --git a/packages/client/ui-model-selection/tests/model-select.client.spec.tsx b/packages/client/ui-model-selection/tests/model-select.client.spec.tsx
140
- index 3b8c92d8bf..53b1f03c39 100644
107
+ index 4a5ee43..a70e88d 100644
141
108
  --- a/packages/client/ui-model-selection/tests/model-select.client.spec.tsx
142
109
  +++ b/packages/client/ui-model-selection/tests/model-select.client.spec.tsx
143
- @@ -206,23 +206,28 @@ describe('ModelSelect reasoning effort', () => {
110
+ @@ -213,23 +213,28 @@ describe('ModelSelect reasoning effort', () => {
144
111
  Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, get: () => 200 })
145
112
  Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, get: () => 300 })
146
113
  try {
@@ -150,7 +117,7 @@ index 3b8c92d8bf..53b1f03c39 100644
150
117
  available
151
118
  directory={createSnapshotStore(state())}
152
119
  load={vi.fn()}
153
- select={vi.fn().mockResolvedValue(true)}
120
+ select={vi.fn().mockResolvedValue({ ok: true, value: undefined })}
154
121
  t={t}
155
122
  - />)
156
123
  + /></div>)
@@ -174,34 +141,67 @@ index 3b8c92d8bf..53b1f03c39 100644
174
141
  expect(menu.style.top).toBe('12px')
175
142
  // Interactions inside the trigger subtree or the portaled card stay open.
176
143
  fireEvent.mouseDown(menu)
144
+ diff --git a/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx b/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx
145
+ index 1736fd8..efd2496 100644
146
+ --- a/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx
147
+ +++ b/packages/client/ui-permission-presets/src/client/PermissionSelect.tsx
148
+ @@ -1,4 +1,4 @@
149
+ -import { useEffect, useState } from 'react'
150
+ +import { useEffect, useRef, useState } from 'react'
151
+ import type { ReactNode } from 'react'
152
+ import clsx from 'clsx'
153
+ import {
154
+ @@ -100,6 +100,8 @@ export function PermissionSelect({
155
+ const catalog = usePermissionCatalog(state => state.value)
156
+ const [pick, setPick] = useState<string | null>(null)
157
+ const [open, setOpen] = useState(false)
158
+ + const triggerRef = useRef<HTMLButtonElement | null>(null)
159
+ + const boundaryRef = useRef<HTMLElement | null>(null)
160
+ const [confirmation, setConfirmation] = useState<string | null>(null)
161
+ const [acknowledged, setAcknowledged] = useState(false)
162
+
163
+ @@ -192,8 +194,13 @@ export function PermissionSelect({
164
+ onClose={() => { setOpen(false) }}
165
+ side="top"
166
+ portal
167
+ + boundaryRef={boundaryRef}
168
+ anchor={
169
+ <button
170
+ + ref={(node) => {
171
+ + triggerRef.current = node
172
+ + boundaryRef.current = node?.closest<HTMLElement>('[data-dsh-center-col]') ?? null
173
+ + }}
174
+ type="button"
175
+ className={css.trigger}
176
+ aria-label={t('mode', { name: currentAccessibleLabel })}
177
177
  diff --git a/packages/client/ui-primitives/README.md b/packages/client/ui-primitives/README.md
178
- index 1919e923c6..fe35b2a1f4 100644
178
+ index cefa67e..915e570 100644
179
179
  --- a/packages/client/ui-primitives/README.md
180
180
  +++ b/packages/client/ui-primitives/README.md
181
- @@ -39,7 +39,7 @@ Check this table before writing a control in a feature package. A plugin cannot
182
- | `Button` | Clickable action; `variant` selects `primary`, `ghost`, `outline`, or `toolbar`. |
181
+ @@ -40,7 +40,7 @@ Check this table before writing a control in a feature package. A plugin cannot
183
182
  | `Switch` | Two-state toggle, 36×20. `label` is required, so the control cannot ship unnamed. |
183
+ | `Checkbox` | Labeled native checkbox with controlled state, keyboard interaction, and disabled styling; the caller supplies localized `label` text. |
184
184
  | `Input` | Single-line text entry for search boxes and inline forms. |
185
- -| `Menu` | Dropdown of items, separators, and group labels, with nested submenus. |
186
- +| `Menu` | Dropdown of items, separators, and group labels, with nested submenus; portaled menus may intersect viewport clamping with an owner boundary. |
185
+ -| `Menu` | Dropdown of items, separators, and group labels, with nested submenus. While open, ↑/↓ (with Home and End) walk the list, Tab settles the focused row, and Escape or Shift+Tab close back to the anchor; selecting a row also returns the keyboard to the anchor unless the owner moved it itself. Only a keyboard on the anchor or inside the list is intercepted, and `autoFocus` decides solely whether opening focuses the first row. |
186
+ +| `Menu` | Dropdown of items, separators, and group labels, with nested submenus. While open, ↑/↓ (with Home and End) walk the list, Tab settles the focused row, and Escape or Shift+Tab close back to the anchor; selecting a row also returns the keyboard to the anchor unless the owner moved it itself. Only a keyboard on the anchor or inside the list is intercepted, and `autoFocus` decides solely whether opening focuses the first row. Portaled menus may intersect viewport clamping with an owner boundary. |
187
187
  | `Pill` | Selectable capsule button for view switchers and filters; takes `active` and `onClick`. |
188
188
  | `Tag` | Read-only capsule badge; `tone` selects one of eight palettes. |
189
189
  | `StateDot` | Status mark: `done`, `warning`, `ongoing`, `error`, or `idle`. `aria-hidden`, so the render site owns the name. |
190
190
  diff --git a/packages/client/ui-primitives/README.zh.md b/packages/client/ui-primitives/README.zh.md
191
- index e0c90fd14a..31011edb72 100644
191
+ index 3c069d0..39eeb5f 100644
192
192
  --- a/packages/client/ui-primitives/README.zh.md
193
193
  +++ b/packages/client/ui-primitives/README.zh.md
194
- @@ -39,7 +39,7 @@ kind: "package-library"
195
- | `Button` | 可点击操作;`variant` 选择 `primary`、`ghost`、`outline` 或 `toolbar`。 |
194
+ @@ -40,7 +40,7 @@ kind: "package-library"
196
195
  | `Switch` | 36×20 的双态开关。`label` 必填,控件不可能在没有名称的情况下发布。 |
196
+ | `Checkbox` | 带标签的原生复选框,支持受控状态、键盘交互和禁用样式;调用方提供本地化的 `label` 文本。 |
197
197
  | `Input` | 单行文本输入,用于搜索框与行内表单。 |
198
- -| `Menu` | 由条目、分隔线与分组标题构成的下拉菜单,支持嵌套子菜单。 |
199
- +| `Menu` | 由条目、分隔线与分组标题构成的下拉菜单,支持嵌套子菜单;portal菜单可将视口clamp与owner boundary取交集。 |
198
+ -| `Menu` | 由条目、分隔线与分组标题构成的下拉菜单,支持嵌套子菜单。打开期间 `↑`/`↓`(以及 Home、End)在列表中走位,Tab 选定聚焦行,Escape 或 Shift+Tab 关闭并把焦点还给锚点;选定一行同样把键盘还给锚点——除非拥有者自己移动了焦点。只拦截位于锚点或列表内的键盘,`autoFocus` 仅决定打开时是否聚焦首行。 |
199
+ +| `Menu` | 由条目、分隔线与分组标题构成的下拉菜单,支持嵌套子菜单。打开期间 `↑`/`↓`(以及 Home、End)在列表中走位,Tab 选定聚焦行,Escape 或 Shift+Tab 关闭并把焦点还给锚点;选定一行同样把键盘还给锚点——除非拥有者自己移动了焦点。只拦截位于锚点或列表内的键盘,`autoFocus` 仅决定打开时是否聚焦首行。portal菜单可将视口clamp与owner boundary取交集。 |
200
200
  | `Pill` | 可选中的胶囊按钮,用于视图切换与筛选器;接受 `active` 与 `onClick`。 |
201
201
  | `Tag` | 只读胶囊徽章;`tone` 选择八种配色之一。 |
202
202
  | `StateDot` | 状态标记:`done`、`warning`、`ongoing`、`error` 或 `idle`。它是 `aria-hidden` 的,名称由渲染点提供。 |
203
203
  diff --git a/packages/client/ui-primitives/src/Menu.tsx b/packages/client/ui-primitives/src/Menu.tsx
204
- index db8b5588fe..cff56df8dc 100644
204
+ index bb04912..1e1e1c7 100644
205
205
  --- a/packages/client/ui-primitives/src/Menu.tsx
206
206
  +++ b/packages/client/ui-primitives/src/Menu.tsx
207
207
  @@ -1,5 +1,5 @@
@@ -211,7 +211,7 @@ index db8b5588fe..cff56df8dc 100644
211
211
  import { createPortal } from 'react-dom'
212
212
  import clsx from 'clsx'
213
213
  import { IconCheckOutline16 } from './icons/index.tsx'
214
- @@ -70,6 +70,7 @@ const MEASURE_STYLE: CSSProperties = { visibility: 'hidden', left: 0, top: 0 }
214
+ @@ -75,6 +75,7 @@ const MEASURE_STYLE: CSSProperties = { visibility: 'hidden', left: 0, top: 0 }
215
215
  * gap and a brief overshoot survivable; coming back cancels the close.
216
216
  * @param props.dense - reduce vertical row spacing without changing the standard typography or card width.
217
217
  * @param props.compact - use reduced menu typography and spacing.
@@ -219,7 +219,7 @@ index db8b5588fe..cff56df8dc 100644
219
219
  * @param props.getAnchorRect - portal mode only: supply the anchor rect
220
220
  * directly (e.g. from a host-owned trigger button) instead of measuring the
221
221
  * Menu's own wrapper span. Required when the wrapper isn't itself laid out at
222
- @@ -84,7 +85,7 @@ const MEASURE_STYLE: CSSProperties = { visibility: 'hidden', left: 0, top: 0 }
222
+ @@ -89,7 +90,7 @@ const MEASURE_STYLE: CSSProperties = { visibility: 'hidden', left: 0, top: 0 }
223
223
  * crowds the cell).
224
224
  * @returns anchor wrapper with the conditional list.
225
225
  */
@@ -228,7 +228,7 @@ index db8b5588fe..cff56df8dc 100644
228
228
  open: boolean
229
229
  autoFocus?: boolean
230
230
  anchor: ReactNode
231
- @@ -102,6 +103,7 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
231
+ @@ -107,6 +108,7 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
232
232
  compact?: boolean
233
233
  selection?: 'check' | 'fill'
234
234
  getAnchorRect?: () => DOMRect | null
@@ -236,7 +236,7 @@ index db8b5588fe..cff56df8dc 100644
236
236
  className?: string | undefined
237
237
  }) {
238
238
  const rootRef = useRef<HTMLSpanElement>(null)
239
- @@ -129,8 +131,13 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
239
+ @@ -174,8 +176,13 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
240
240
  const MARGIN = 12
241
241
  const vw = window.innerWidth
242
242
  const vh = window.innerHeight
@@ -250,7 +250,7 @@ index db8b5588fe..cff56df8dc 100644
250
250
  const lh = listEl?.offsetHeight ?? 0
251
251
 
252
252
  let x: number
253
- @@ -142,14 +149,18 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
253
+ @@ -187,14 +194,18 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
254
254
  x = r.left
255
255
  y = side === 'bottom' ? r.bottom + 4 : r.top - lh - 4
256
256
  } else {
@@ -272,20 +272,20 @@ index db8b5588fe..cff56df8dc 100644
272
272
  }
273
273
  // First run measures the hidden pre-render (same commit as `open`), so
274
274
  // end/top alignment and clamping use real dimensions before anything
275
- @@ -161,7 +172,7 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
275
+ @@ -206,7 +217,7 @@ export function Menu({ open, anchor, items, selectedId, selectedIds, onSelect, o
276
276
  window.removeEventListener('scroll', place, true)
277
277
  window.removeEventListener('resize', place)
278
278
  }
279
279
  - }, [open, portal, align, side, getAnchorRect])
280
280
  + }, [open, portal, align, side, getAnchorRect, boundaryRef])
281
281
 
282
- useEffect(() => {
283
- if (open && autoFocus) listRef.current?.querySelector<HTMLButtonElement>('button:not(:disabled)')?.focus()
282
+ // Opening remembers where the keyboard was, so closing can hand it back to
283
+ // that control — an anchor wrapping several (a split button) cannot be asked
284
284
  diff --git a/packages/client/ui-primitives/tests/atoms.client.spec.tsx b/packages/client/ui-primitives/tests/atoms.client.spec.tsx
285
- index 70649d0874..c768b406ff 100644
285
+ index a2f6b70..458c825 100644
286
286
  --- a/packages/client/ui-primitives/tests/atoms.client.spec.tsx
287
287
  +++ b/packages/client/ui-primitives/tests/atoms.client.spec.tsx
288
- @@ -337,6 +337,32 @@ describe('Menu', () => {
288
+ @@ -416,6 +416,32 @@ describe('Menu', () => {
289
289
  expect(menu.style.top).toBe('132px')
290
290
  })
291
291