@deepseek-ai/dsh-client-web 0.1.7-alpha.2 → 0.1.7-rc.2
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/README.i18n.yaml +60 -5
- package/README.md +5 -1
- package/README.zh.md +5 -1
- package/lib/base.css +30 -20
- package/lib/index.js +250 -0
- package/lib/types/boot.d.ts +1 -0
- package/lib/types/window-drag/recall.d.ts +25 -0
- package/lib/types/window-drag/regions.d.ts +71 -0
- package/package.json +8 -8
package/README.i18n.yaml
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
|
-
# Bilingual-pair consistency record (docs/i18n/README.md):
|
|
2
|
-
#
|
|
3
|
-
#
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/web/README.md
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/:
|
|
6
|
+
en: bd6e5fc50f07893f
|
|
7
|
+
zh: 0ee146674eb01770
|
|
8
|
+
/deepseek-ai-dsh-client-web:
|
|
9
|
+
en: 8423614617b7bbf1
|
|
10
|
+
zh: c99647136c800173
|
|
11
|
+
/deepseek-ai-dsh-client-web/summary:
|
|
12
|
+
en: 646bd0a246789400
|
|
13
|
+
zh: d875dc0d37f46338
|
|
14
|
+
/deepseek-ai-dsh-client-web/table-of-contents:
|
|
15
|
+
en: d152484eb41ac6b4
|
|
16
|
+
zh: 09388d293f9be9cb
|
|
17
|
+
/deepseek-ai-dsh-client-web/use-this-package:
|
|
18
|
+
en: 0c8b98946feedb0f
|
|
19
|
+
zh: ae10ae7b9ecbf89c
|
|
20
|
+
/deepseek-ai-dsh-client-web/use-this-package/what-boot-looks-like:
|
|
21
|
+
en: f3b01aa878764e5c
|
|
22
|
+
zh: f02eab65d441bfe0
|
|
23
|
+
/deepseek-ai-dsh-client-web/use-this-package/the-boot-page:
|
|
24
|
+
en: add354591107b80d
|
|
25
|
+
zh: 41136254a00d785e
|
|
26
|
+
/deepseek-ai-dsh-client-web/use-this-package/the-shared-module-table:
|
|
27
|
+
en: 8380cd70b861a3a8
|
|
28
|
+
zh: 5a331deb7ad8fca1
|
|
29
|
+
/deepseek-ai-dsh-client-web/use-this-package/configuration:
|
|
30
|
+
en: 942959cb40ee31a1
|
|
31
|
+
zh: 9c86ac2cf6a13976
|
|
32
|
+
/deepseek-ai-dsh-client-web/understand-the-implementation:
|
|
33
|
+
en: 9556ca6438410146
|
|
34
|
+
zh: 8a60572cb4caa04a
|
|
35
|
+
/deepseek-ai-dsh-client-web/understand-the-implementation/design-concept:
|
|
36
|
+
en: e18ec3ae51dcdc3f
|
|
37
|
+
zh: e454da2038979aec
|
|
38
|
+
/deepseek-ai-dsh-client-web/understand-the-implementation/two-stage-boot:
|
|
39
|
+
en: fb3cab79c1505627
|
|
40
|
+
zh: 875897afe593aeb3
|
|
41
|
+
/deepseek-ai-dsh-client-web/understand-the-implementation/boot-page-mechanics:
|
|
42
|
+
en: 83f9195138da9a82
|
|
43
|
+
zh: 135d7da735fb870a
|
|
44
|
+
/deepseek-ai-dsh-client-web/understand-the-implementation/source-map:
|
|
45
|
+
en: 1fe37d3730368e32
|
|
46
|
+
zh: 74c2d6980ce82e21
|
|
47
|
+
/deepseek-ai-dsh-client-web/further-exploration:
|
|
48
|
+
en: f2da70a33bba9086
|
|
49
|
+
zh: a775f6fe309150ea
|
|
50
|
+
/deepseek-ai-dsh-client-web/model-experience:
|
|
51
|
+
en: ec17334dcce181f3
|
|
52
|
+
zh: bc8bc6e25ecbf9e8
|
|
53
|
+
/deepseek-ai-dsh-client-web/model-experience/kv-cache-effect:
|
|
54
|
+
en: ca75c51c89c2c9b0
|
|
55
|
+
zh: de54f3467ef3b9be
|
|
56
|
+
/deepseek-ai-dsh-client-web/known-limitations-and-deferred-work:
|
|
57
|
+
en: f498ba05d4ea96d5
|
|
58
|
+
zh: c3e57dd8ca9b5bda
|
|
59
|
+
/deepseek-ai-dsh-client-web/known-limitations-and-deferred-work/dev-note:
|
|
60
|
+
en: 80ab3d00f7aac4de
|
|
61
|
+
zh: 3c08f30322413856
|
package/README.md
CHANGED
|
@@ -52,6 +52,8 @@ The package accepts no plugin config of its own; the generated [configuration ca
|
|
|
52
52
|
<a id="understand-the-implementation"></a>
|
|
53
53
|
## Understand the implementation
|
|
54
54
|
|
|
55
|
+
Menus use the shared `MenuSurface` material, including the macOS backing for background blur; custom content follows the [menu rules](../../../docs/web-styling.md#component-rules).
|
|
56
|
+
|
|
55
57
|
<details>
|
|
56
58
|
<summary>Implementation internals — click to expand</summary>
|
|
57
59
|
|
|
@@ -76,12 +78,14 @@ The boot kernel delegates manifest entry creation to Client Modules so live grap
|
|
|
76
78
|
| File | Role |
|
|
77
79
|
|---|---|
|
|
78
80
|
| [`src/index.ts`](src/index.ts) | Library entry: `AppWebEntry`, `getStaticModules`, platform tables |
|
|
79
|
-
| [`src/boot.ts`](src/boot.ts) | `AppWebEntry`: module stage, boot page, immediate-tier prefetch, then `bootClient` + `mountClient` |
|
|
81
|
+
| [`src/boot.ts`](src/boot.ts) | `AppWebEntry`: module stage, boot page, immediate-tier prefetch, window drag-rect watcher install, then `bootClient` + `mountClient` |
|
|
80
82
|
| [`src/boot-client.ts`](src/boot-client.ts) | `bootClient` / `assertEntriesActive`: Loader mount, one entry per manifest row, activation audit |
|
|
81
83
|
| [`src/mount.ts`](src/mount.ts) | `mountClient`: renderer handoff through a `uiRenderer` dependency fiber |
|
|
82
84
|
| [`src/boot-page.ts`](src/boot-page.ts) | Framework-free boot page: spinner, per-entry status, failure rendering |
|
|
83
85
|
| [`src/platform.ts`](src/platform.ts) | `PLATFORM_MODULES` / `PRELOADED_CLIENT_EXTERNALS`: the implicit external baseline |
|
|
84
86
|
| [`src/seed.ts`](src/seed.ts) | Static module table handed to the loader at boot |
|
|
87
|
+
| [`src/window-drag/regions.ts`](src/window-drag/regions.ts) | The darwin app-region composition model, and the interactive selector `base.css` subtracts |
|
|
88
|
+
| [`src/window-drag/recall.ts`](src/window-drag/recall.ts) | The shell's one window drag-rect watcher (electron/electron#32341): measure the marked rows per frame and pulse the recall mark while they move |
|
|
85
89
|
|
|
86
90
|
</details>
|
|
87
91
|
|
package/README.zh.md
CHANGED
|
@@ -52,6 +52,8 @@ kind: "package-library"
|
|
|
52
52
|
<a id="understand-the-implementation"></a>
|
|
53
53
|
## 理解实现
|
|
54
54
|
|
|
55
|
+
菜单采用共享 `MenuSurface` 材质,包括用于背景模糊的 macOS 底层;自定义内容遵循[菜单规则](../../../docs/web-styling.zh.md#component-rules)。
|
|
56
|
+
|
|
55
57
|
<details>
|
|
56
58
|
<summary>实现细节——点击展开</summary>
|
|
57
59
|
|
|
@@ -76,12 +78,14 @@ kind: "package-library"
|
|
|
76
78
|
| 文件 | 职责 |
|
|
77
79
|
|---|---|
|
|
78
80
|
| [`src/index.ts`](src/index.ts) | 库入口:`AppWebEntry`、`getStaticModules`、平台表 |
|
|
79
|
-
| [`src/boot.ts`](src/boot.ts) | `AppWebEntry`:模块阶段、启动页、immediately
|
|
81
|
+
| [`src/boot.ts`](src/boot.ts) | `AppWebEntry`:模块阶段、启动页、immediately 层级预取、安装窗口拖拽矩形 watcher,随后调用 `bootClient` + `mountClient` |
|
|
80
82
|
| [`src/boot-client.ts`](src/boot-client.ts) | `bootClient` / `assertEntriesActive`:挂载 Loader、每个 manifest 行一个 entry、激活审计 |
|
|
81
83
|
| [`src/mount.ts`](src/mount.ts) | `mountClient`:经 `uiRenderer` 依赖 fiber 完成渲染器交接 |
|
|
82
84
|
| [`src/boot-page.ts`](src/boot-page.ts) | 无框架启动页:spinner、逐 entry 状态、失败渲染 |
|
|
83
85
|
| [`src/platform.ts`](src/platform.ts) | `PLATFORM_MODULES` / `PRELOADED_CLIENT_EXTERNALS`:隐式 external 基座 |
|
|
84
86
|
| [`src/seed.ts`](src/seed.ts) | 启动时交给 loader 的静态模块表 |
|
|
87
|
+
| [`src/window-drag/regions.ts`](src/window-drag/regions.ts) | darwin app-region 组合模型,以及 `base.css` 减除的交互元素选择器 |
|
|
88
|
+
| [`src/window-drag/recall.ts`](src/window-drag/recall.ts) | 外壳唯一的窗口拖拽矩形 watcher(electron/electron#32341):逐帧测量被打标行,并在它们移动期间脉冲 recall 标记 |
|
|
85
89
|
|
|
86
90
|
</details>
|
|
87
91
|
|
package/lib/base.css
CHANGED
|
@@ -39,36 +39,46 @@ html[data-platform='darwin'] body {
|
|
|
39
39
|
background: transparent;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/*
|
|
43
|
-
against the root surface, which vibrancy leaves transparent, so the blur in
|
|
44
|
-
--dsw-menu-backdrop-filter renders as nothing and half-opaque menu fills
|
|
45
|
-
let underlying text bleed through. Compensate with near-opaque fills on the
|
|
46
|
-
same design-platform hues (light 248 249 250 / dark 48 49 54). */
|
|
42
|
+
/* Menu backings paint below page content but above the native vibrancy. */
|
|
47
43
|
html[data-platform='darwin'] body {
|
|
48
|
-
|
|
44
|
+
isolation: isolate;
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
/* macOS desktop: the one drag declaration. A chrome row marks itself in markup
|
|
48
|
+
(`data-window-drag`) and its own box becomes its draggable geometry, so no fixed
|
|
49
|
+
band has to match any row's height; the ui-theme app-region gate's CHROME_ROWS
|
|
50
|
+
manifest is the list of rows that must carry the mark. Electron composes
|
|
51
|
+
app-regions from geometry, not hit testing, so a surface covering a row would
|
|
52
|
+
otherwise drag the window instead of receiving clicks; every covering overlay
|
|
53
|
+
portals to document.body beside #root, which also puts it after every drag row
|
|
54
|
+
in document order, and one rule subtracts them all. */
|
|
55
|
+
html[data-platform='darwin'] [data-window-drag] {
|
|
56
|
+
-webkit-app-region: drag;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* One-frame app-region pulse that forces Electron to recollect the window's drag
|
|
60
|
+
rects (electron#32341): a change to a computed app-region value is what dirties
|
|
61
|
+
them. The shell's window drag watcher (window-drag/recall.ts) sets this
|
|
62
|
+
attribute on the body for each frame in which a marked row's box moved, and
|
|
63
|
+
clears it on the frame that finds the geometry settled. The box it subtracts
|
|
64
|
+
is the body's own: the row marks inside still declare drag and win over it in
|
|
65
|
+
document order, so the pulse changes nothing about the composed surface — it
|
|
66
|
+
only makes Electron collect it again. */
|
|
67
|
+
html[data-platform='darwin'] [data-window-drag-recall] {
|
|
68
|
+
-webkit-app-region: no-drag;
|
|
53
69
|
}
|
|
54
70
|
|
|
55
|
-
/* macOS desktop: the app frame publishes window drag bands, and Electron
|
|
56
|
-
composes app-regions from geometry, not hit testing — a surface covering a
|
|
57
|
-
band would otherwise drag the window instead of receiving clicks. Every
|
|
58
|
-
overlay portals to document.body beside #root, so one rule subtracts them
|
|
59
|
-
all; drag surfaces all live inside #root. */
|
|
60
71
|
html[data-platform='darwin'] body > :not(#root) {
|
|
61
72
|
-webkit-app-region: no-drag;
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
/* Inside #root, interactive controls and raised surfaces (dialogs, menus)
|
|
65
|
-
subtract themselves from any drag
|
|
66
|
-
Electron composes app-regions from geometry in document order, so
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
custom widgets replaces per-component no-drag opt-outs. */
|
|
76
|
+
subtract themselves from any drag row they overlap, wherever they render.
|
|
77
|
+
Electron composes app-regions from geometry in document order, so a drag rule
|
|
78
|
+
must sit on a chrome row that precedes the overlays covering it — never on a
|
|
79
|
+
content container, which would override every overlay mounted earlier in the
|
|
80
|
+
DOM. One selector on the native elements, ARIA roles, and tabindexed custom
|
|
81
|
+
widgets replaces per-component no-drag opt-outs. */
|
|
72
82
|
html[data-platform='darwin'] :is(
|
|
73
83
|
button, a, input, select, textarea, summary, [contenteditable='true'], [tabindex],
|
|
74
84
|
[role='dialog'], [role='alertdialog'], [role='menu'], [role='listbox'], [role='tooltip'],
|
package/lib/index.js
CHANGED
|
@@ -220,6 +220,252 @@ function getStaticModules() {
|
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
222
|
//#endregion
|
|
223
|
+
//#region lib/types/window-drag/regions.js
|
|
224
|
+
/**
|
|
225
|
+
* The window drag-region contract for the macOS desktop shell: Electron hands
|
|
226
|
+
* the page's `-webkit-app-region` boxes to the native window, which hit-tests
|
|
227
|
+
* them by geometry in DOM order, ignoring stacking. The property does not inherit:
|
|
228
|
+
* Blink collects one box per element whose own computed value is not `none`,
|
|
229
|
+
* skipping subtrees that are not visible, and the window applies them in that
|
|
230
|
+
* order: `drag` adds geometry, `no-drag` removes it. The composition is therefore
|
|
231
|
+
* equivalent to "the last matching box decides" — a point is draggable when the
|
|
232
|
+
* last collected box containing it is `drag`.
|
|
233
|
+
*
|
|
234
|
+
* This module is the executable statement of that rule. Production CSS authors
|
|
235
|
+
* the boxes; tests and the browser coverage scenario both decide points through
|
|
236
|
+
* this module so a claim about coverage has one meaning.
|
|
237
|
+
*/
|
|
238
|
+
/**
|
|
239
|
+
* The attribute a chrome row puts on the element that owns its window drag.
|
|
240
|
+
* ui-web `base.css` turns the mark into the one darwin drag rule, so the row's
|
|
241
|
+
* own box is the window's draggable geometry.
|
|
242
|
+
*/
|
|
243
|
+
const DRAG_MARK = "data-window-drag";
|
|
244
|
+
/**
|
|
245
|
+
* The attribute the shell sets for one frame to make Electron recollect the
|
|
246
|
+
* window's drag rects (electron#32341). While it is set, ui-web `base.css`
|
|
247
|
+
* subtracts the marked box; the row marks inside it still declare drag and win in
|
|
248
|
+
* document order, so the mark's only effect is that app-region values changed.
|
|
249
|
+
*/
|
|
250
|
+
const RECALL_MARK = "data-window-drag-recall";
|
|
251
|
+
[
|
|
252
|
+
"button",
|
|
253
|
+
"a",
|
|
254
|
+
"input",
|
|
255
|
+
"select",
|
|
256
|
+
"textarea",
|
|
257
|
+
"summary",
|
|
258
|
+
"[contenteditable='true']",
|
|
259
|
+
"[tabindex]",
|
|
260
|
+
"[role='dialog']",
|
|
261
|
+
"[role='alertdialog']",
|
|
262
|
+
"[role='menu']",
|
|
263
|
+
"[role='listbox']",
|
|
264
|
+
"[role='tooltip']",
|
|
265
|
+
"[role='button']",
|
|
266
|
+
"[role='link']",
|
|
267
|
+
"[role='tab']",
|
|
268
|
+
"[role='menuitem']",
|
|
269
|
+
"[role='menuitemcheckbox']",
|
|
270
|
+
"[role='menuitemradio']",
|
|
271
|
+
"[role='option']",
|
|
272
|
+
"[role='checkbox']",
|
|
273
|
+
"[role='radio']",
|
|
274
|
+
"[role='switch']",
|
|
275
|
+
"[role='slider']",
|
|
276
|
+
"[role='combobox']",
|
|
277
|
+
"[role='textbox']"
|
|
278
|
+
].join(", ");
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region lib/types/window-drag/recall.js
|
|
281
|
+
/**
|
|
282
|
+
* Window drag recollection for the macOS desktop shell. Electron rebuilds the
|
|
283
|
+
* window's `-webkit-app-region` rects only when a style pass changes a computed
|
|
284
|
+
* app-region value (electron#32341), and Blink skips hidden boxes when it collects
|
|
285
|
+
* them, so a chrome row that appears, moves, or disappears without such a change
|
|
286
|
+
* leaves the native window holding the previous geometry: a press inside the row
|
|
287
|
+
* drags from where it used to be, and a row that slid under the pointer keeps its
|
|
288
|
+
* controls unreachable or its blank runs undraggable. This module owns the one
|
|
289
|
+
* watcher that closes that gap, so no chrome row has to know the trap.
|
|
290
|
+
*
|
|
291
|
+
* A row's viewport box is the measurement: every row in this composition shows and
|
|
292
|
+
* hides by mounting, unmounting, or moving, so a changed box is the signal. The
|
|
293
|
+
* watcher starts on a DOM change that touches a marked row — inside it, on it, on
|
|
294
|
+
* an ancestor that holds one, on a container it lives in, or adding or removing one
|
|
295
|
+
* — on a marked row's box resizing, or on a transition or animation starting on an
|
|
296
|
+
* element that holds one, and then measures every marked row once per frame for as
|
|
297
|
+
* long as any box keeps changing. Each frame that changed sets the recall mark, and
|
|
298
|
+
* the frame that finds the same geometry clears it and stops once the short grace
|
|
299
|
+
* window below has passed: that clear is the collection the steady state comes from.
|
|
300
|
+
* @module @deepseek-ai/dsh-client-web/src/window-drag/recall
|
|
301
|
+
*/
|
|
302
|
+
/** The selector of an element that owns a window drag region. */
|
|
303
|
+
const ROW_SELECTOR = `[${DRAG_MARK}]`;
|
|
304
|
+
/**
|
|
305
|
+
* Quiet frames a report tolerates before the loop stops again. A CSS transition's
|
|
306
|
+
* first frame still reports the box's from-value, so a report that arrives before
|
|
307
|
+
* the surface starts moving must not end the loop on its first unchanged sample.
|
|
308
|
+
*/
|
|
309
|
+
const GRACE_FRAMES = 2;
|
|
310
|
+
/** The event types that mark a box starting or finishing a transition or animation. */
|
|
311
|
+
const MOTION_EVENTS = [
|
|
312
|
+
"transitionstart",
|
|
313
|
+
"transitionend",
|
|
314
|
+
"animationstart",
|
|
315
|
+
"animationend"
|
|
316
|
+
];
|
|
317
|
+
/**
|
|
318
|
+
* Watch the window's marked drag rows and pulse the recall mark whenever their
|
|
319
|
+
* geometry can have moved. Installs nothing outside the darwin platform, where no
|
|
320
|
+
* app-region rule exists.
|
|
321
|
+
* @param options - the document to watch and the two scheduling seams.
|
|
322
|
+
* @returns a disposer that stops watching, drops any pending frame, and clears the mark.
|
|
323
|
+
*/
|
|
324
|
+
function installWindowDragRecall(options) {
|
|
325
|
+
const doc = options.document;
|
|
326
|
+
if (doc.documentElement.dataset.platform !== "darwin") return () => {};
|
|
327
|
+
const scheduleFrame = options.scheduleFrame ?? defaultScheduleFrame;
|
|
328
|
+
const watchBox = options.watchBox ?? defaultWatchBox;
|
|
329
|
+
/** The last frame's measurement per row, the baseline a change is read against. */
|
|
330
|
+
let geometry = /* @__PURE__ */ new Map();
|
|
331
|
+
/** One box watcher per row currently rendered. */
|
|
332
|
+
const boxes = /* @__PURE__ */ new Map();
|
|
333
|
+
let scheduled = false;
|
|
334
|
+
let disposed = false;
|
|
335
|
+
let cancelPending;
|
|
336
|
+
/** Quiet frames left before the loop may stop; refreshed by every report. */
|
|
337
|
+
let grace = 0;
|
|
338
|
+
/**
|
|
339
|
+
* Measure every marked row and pulse when any box differs from the last frame's.
|
|
340
|
+
* Runs once per frame while the surface keeps moving.
|
|
341
|
+
*/
|
|
342
|
+
const measure = () => {
|
|
343
|
+
doc.body.removeAttribute(RECALL_MARK);
|
|
344
|
+
const rows = Array.from(doc.querySelectorAll(ROW_SELECTOR));
|
|
345
|
+
watchBoxes(rows);
|
|
346
|
+
const next = new Map(rows.map((row) => [row, readBox(row)]));
|
|
347
|
+
const moved = rows.length !== geometry.size || rows.some((row) => geometry.get(row) !== next.get(row));
|
|
348
|
+
geometry = next;
|
|
349
|
+
if (moved) {
|
|
350
|
+
doc.body.setAttribute(RECALL_MARK, "");
|
|
351
|
+
grace = 0;
|
|
352
|
+
schedule();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (grace === 0) return;
|
|
356
|
+
grace -= 1;
|
|
357
|
+
schedule();
|
|
358
|
+
};
|
|
359
|
+
/** Report that the surface may be moving, opening the grace window again. */
|
|
360
|
+
const arm = () => {
|
|
361
|
+
grace = GRACE_FRAMES;
|
|
362
|
+
schedule();
|
|
363
|
+
};
|
|
364
|
+
/** Schedule one frame, keeping at most one pending. */
|
|
365
|
+
const schedule = () => {
|
|
366
|
+
if (scheduled) return;
|
|
367
|
+
scheduled = true;
|
|
368
|
+
cancelPending = scheduleFrame(() => {
|
|
369
|
+
scheduled = false;
|
|
370
|
+
cancelPending = void 0;
|
|
371
|
+
if (disposed) return;
|
|
372
|
+
measure();
|
|
373
|
+
});
|
|
374
|
+
};
|
|
375
|
+
/** Keep one box watcher per live row, re-watching when its frame moves or unmounts. */
|
|
376
|
+
const watchBoxes = (rows) => {
|
|
377
|
+
for (const [row, dispose] of boxes) {
|
|
378
|
+
if (rows.includes(row)) continue;
|
|
379
|
+
dispose();
|
|
380
|
+
boxes.delete(row);
|
|
381
|
+
}
|
|
382
|
+
for (const row of rows) {
|
|
383
|
+
if (boxes.has(row)) continue;
|
|
384
|
+
boxes.set(row, watchBox(row, arm));
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
/**
|
|
388
|
+
* Whether a motion event can be moving a marked row: the transitioning element is
|
|
389
|
+
* one, holds one, or lives inside one.
|
|
390
|
+
* @param target - the event target to classify.
|
|
391
|
+
* @returns true when the drag surface has to be re-measured.
|
|
392
|
+
*/
|
|
393
|
+
const movesRows = (target) => {
|
|
394
|
+
if (!(target instanceof Element)) return false;
|
|
395
|
+
return target.closest(ROW_SELECTOR) !== null || target.querySelector(ROW_SELECTOR) !== null;
|
|
396
|
+
};
|
|
397
|
+
/** Re-arm for a transition or animation that can be sliding a marked row. */
|
|
398
|
+
const onMotion = (event) => {
|
|
399
|
+
if (movesRows(event.target)) arm();
|
|
400
|
+
};
|
|
401
|
+
const observer = new MutationObserver((records) => {
|
|
402
|
+
if (records.some((record) => touchesRows(record, geometry.keys()))) arm();
|
|
403
|
+
});
|
|
404
|
+
observer.observe(doc.body, {
|
|
405
|
+
subtree: true,
|
|
406
|
+
childList: true,
|
|
407
|
+
attributes: true,
|
|
408
|
+
characterData: true
|
|
409
|
+
});
|
|
410
|
+
for (const type of MOTION_EVENTS) doc.addEventListener(type, onMotion, true);
|
|
411
|
+
arm();
|
|
412
|
+
return () => {
|
|
413
|
+
disposed = true;
|
|
414
|
+
observer.disconnect();
|
|
415
|
+
for (const type of MOTION_EVENTS) doc.removeEventListener(type, onMotion, true);
|
|
416
|
+
if (cancelPending !== void 0) cancelPending();
|
|
417
|
+
for (const dispose of boxes.values()) dispose();
|
|
418
|
+
boxes.clear();
|
|
419
|
+
geometry.clear();
|
|
420
|
+
doc.body.removeAttribute(RECALL_MARK);
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
/** The box a row owns this frame, in viewport pixels. */
|
|
424
|
+
function readBox(row) {
|
|
425
|
+
const rect = row.getBoundingClientRect();
|
|
426
|
+
return `${rect.x},${rect.y},${rect.width},${rect.height}`;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Whether one DOM change can have moved the drag surface: a child list change in a
|
|
430
|
+
* container that holds a marked row, or an attribute or text change that lands on a
|
|
431
|
+
* marked row, inside one, or on an element holding one (a panel's open flag, a
|
|
432
|
+
* column's width). A child list change in a container without a marked row is left
|
|
433
|
+
* alone — a row whose box grows because its content did is a resize, which the box
|
|
434
|
+
* watcher reports — and so is a mounted overlay, whose own app-region value is a
|
|
435
|
+
* change of its own.
|
|
436
|
+
* @param record - the mutation record to classify.
|
|
437
|
+
* @param rows - the rows the last frame measured.
|
|
438
|
+
* @returns true when the drag surface has to be re-measured.
|
|
439
|
+
*/
|
|
440
|
+
function touchesRows(record, rows) {
|
|
441
|
+
if (record.attributeName === "data-window-drag-recall") return false;
|
|
442
|
+
for (const node of [...record.addedNodes, ...record.removedNodes]) if (node instanceof Element && (node.matches(ROW_SELECTOR) || node.querySelector(ROW_SELECTOR) !== null)) return true;
|
|
443
|
+
const target = record.target instanceof Element ? record.target : record.target.parentElement;
|
|
444
|
+
/* v8 ignore next -- the observer watches body's subtree, so a text target always has a parent element. */
|
|
445
|
+
if (target === null) return false;
|
|
446
|
+
if (record.type === "childList") {
|
|
447
|
+
for (const row of rows) if (target.contains(row)) return true;
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
return target.closest(ROW_SELECTOR) !== null || target.querySelector(ROW_SELECTOR) !== null;
|
|
451
|
+
}
|
|
452
|
+
/** Schedule one frame through the browser's animation frame queue. */
|
|
453
|
+
function defaultScheduleFrame(frame) {
|
|
454
|
+
const handle = requestAnimationFrame(frame);
|
|
455
|
+
return () => {
|
|
456
|
+
cancelAnimationFrame(handle);
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
/** Watch one row's border box where the document has a `ResizeObserver`. */
|
|
460
|
+
function defaultWatchBox(row, changed) {
|
|
461
|
+
if (typeof ResizeObserver !== "function") return () => {};
|
|
462
|
+
const observer = new ResizeObserver(changed);
|
|
463
|
+
observer.observe(row);
|
|
464
|
+
return () => {
|
|
465
|
+
observer.disconnect();
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
//#endregion
|
|
223
469
|
//#region lib/types/boot.js
|
|
224
470
|
/**
|
|
225
471
|
* Web boot kernel. It owns only the module system, Cordis loader, and a
|
|
@@ -234,6 +480,7 @@ var AppWebEntry = class {
|
|
|
234
480
|
seams;
|
|
235
481
|
page;
|
|
236
482
|
ctx;
|
|
483
|
+
stopDragRecall;
|
|
237
484
|
modules;
|
|
238
485
|
manifest;
|
|
239
486
|
/**
|
|
@@ -279,6 +526,7 @@ var AppWebEntry = class {
|
|
|
279
526
|
if (onFailure === void 0 || state !== "failed") this.page.setState(name, state);
|
|
280
527
|
}
|
|
281
528
|
});
|
|
529
|
+
this.stopDragRecall = installWindowDragRecall({ document: this.container.ownerDocument });
|
|
282
530
|
await mountClient(ctx, this.container);
|
|
283
531
|
} catch (reason) {
|
|
284
532
|
console.error(reason);
|
|
@@ -288,6 +536,8 @@ var AppWebEntry = class {
|
|
|
288
536
|
}
|
|
289
537
|
/** Dispose the client plugin tree and whichever page owns the mount point. */
|
|
290
538
|
async dispose() {
|
|
539
|
+
this.stopDragRecall?.();
|
|
540
|
+
this.stopDragRecall = void 0;
|
|
291
541
|
const ctx = this.ctx;
|
|
292
542
|
this.ctx = void 0;
|
|
293
543
|
if (ctx !== void 0) await ctx.fiber.dispose();
|
package/lib/types/boot.d.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** How the shell installs the one window drag watcher. */
|
|
2
|
+
export interface WindowDragRecallOptions {
|
|
3
|
+
/** Document whose marked rows are watched. */
|
|
4
|
+
readonly document: Document;
|
|
5
|
+
/**
|
|
6
|
+
* Schedules one frame callback and returns its canceller. The callback runs
|
|
7
|
+
* asynchronously, as `requestAnimationFrame` does.
|
|
8
|
+
*/
|
|
9
|
+
readonly scheduleFrame?: (frame: () => void) => () => void;
|
|
10
|
+
/**
|
|
11
|
+
* Watches one row's border box and calls `changed` whenever it resizes, returning
|
|
12
|
+
* a disposer. Defaults to a `ResizeObserver`; a document without one watches no
|
|
13
|
+
* boxes and relies on the DOM changes alone.
|
|
14
|
+
*/
|
|
15
|
+
readonly watchBox?: (row: Element, changed: () => void) => () => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Watch the window's marked drag rows and pulse the recall mark whenever their
|
|
19
|
+
* geometry can have moved. Installs nothing outside the darwin platform, where no
|
|
20
|
+
* app-region rule exists.
|
|
21
|
+
* @param options - the document to watch and the two scheduling seams.
|
|
22
|
+
* @returns a disposer that stops watching, drops any pending frame, and clears the mark.
|
|
23
|
+
*/
|
|
24
|
+
export declare function installWindowDragRecall(options: WindowDragRecallOptions): () => void;
|
|
25
|
+
//# sourceMappingURL=recall.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The window drag-region contract for the macOS desktop shell: Electron hands
|
|
3
|
+
* the page's `-webkit-app-region` boxes to the native window, which hit-tests
|
|
4
|
+
* them by geometry in DOM order, ignoring stacking. The property does not inherit:
|
|
5
|
+
* Blink collects one box per element whose own computed value is not `none`,
|
|
6
|
+
* skipping subtrees that are not visible, and the window applies them in that
|
|
7
|
+
* order: `drag` adds geometry, `no-drag` removes it. The composition is therefore
|
|
8
|
+
* equivalent to "the last matching box decides" — a point is draggable when the
|
|
9
|
+
* last collected box containing it is `drag`.
|
|
10
|
+
*
|
|
11
|
+
* This module is the executable statement of that rule. Production CSS authors
|
|
12
|
+
* the boxes; tests and the browser coverage scenario both decide points through
|
|
13
|
+
* this module so a claim about coverage has one meaning.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* One collected app-region box, in DOM order.
|
|
17
|
+
*
|
|
18
|
+
* Coordinates are viewport CSS pixels, the space `getBoundingClientRect`
|
|
19
|
+
* reports, so collection and composition never rescale.
|
|
20
|
+
*/
|
|
21
|
+
export interface RegionRect {
|
|
22
|
+
/** Left edge, viewport CSS pixels. */
|
|
23
|
+
readonly x: number;
|
|
24
|
+
/** Top edge, viewport CSS pixels. */
|
|
25
|
+
readonly y: number;
|
|
26
|
+
/** Box width in CSS pixels. */
|
|
27
|
+
readonly width: number;
|
|
28
|
+
/** Box height in CSS pixels. */
|
|
29
|
+
readonly height: number;
|
|
30
|
+
/** Whether this box declares `drag` (true) or `no-drag` (false). */
|
|
31
|
+
readonly draggable: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The attribute a chrome row puts on the element that owns its window drag.
|
|
35
|
+
* ui-web `base.css` turns the mark into the one darwin drag rule, so the row's
|
|
36
|
+
* own box is the window's draggable geometry.
|
|
37
|
+
*/
|
|
38
|
+
export declare const DRAG_MARK = "data-window-drag";
|
|
39
|
+
/**
|
|
40
|
+
* The attribute the shell sets for one frame to make Electron recollect the
|
|
41
|
+
* window's drag rects (electron#32341). While it is set, ui-web `base.css`
|
|
42
|
+
* subtracts the marked box; the row marks inside it still declare drag and win in
|
|
43
|
+
* document order, so the mark's only effect is that app-region values changed.
|
|
44
|
+
*/
|
|
45
|
+
export declare const RECALL_MARK = "data-window-drag-recall";
|
|
46
|
+
/**
|
|
47
|
+
* The interactive-element selector that subtracts a control from any drag row it
|
|
48
|
+
* overlaps. ui-web `base.css` declares it for the darwin platform; this constant
|
|
49
|
+
* is the single source both that sheet and its contract spec compare against, so
|
|
50
|
+
* the two cannot drift.
|
|
51
|
+
*/
|
|
52
|
+
export declare const INTERACTIVE_SELECTOR: string;
|
|
53
|
+
/**
|
|
54
|
+
* Whether a box contains a viewport point. Edges belong to the box, matching
|
|
55
|
+
* the half-open rectangles the window hit test uses.
|
|
56
|
+
* @param rect - the collected box.
|
|
57
|
+
* @param x - viewport x in CSS pixels.
|
|
58
|
+
* @param y - viewport y in CSS pixels.
|
|
59
|
+
* @returns true when the point lies inside the box.
|
|
60
|
+
*/
|
|
61
|
+
export declare function containsPoint(rect: RegionRect, x: number, y: number): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Decide whether the window would drag when pressed at a point.
|
|
64
|
+
* @param regions - collected boxes in DOM order.
|
|
65
|
+
* @param x - viewport x in CSS pixels.
|
|
66
|
+
* @param y - viewport y in CSS pixels.
|
|
67
|
+
* @returns true when the last box containing the point is a drag box; false
|
|
68
|
+
* when it is a no-drag box or no box contains the point.
|
|
69
|
+
*/
|
|
70
|
+
export declare function isDraggableAt(regions: readonly RegionRect[], x: number, y: number): boolean;
|
|
71
|
+
//# sourceMappingURL=regions.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-web",
|
|
3
3
|
"description": "Web boot kernel: static module table, Cordis loader, framework-free boot page, and UI-renderer handoff",
|
|
4
|
-
"version": "0.1.7-
|
|
4
|
+
"version": "0.1.7-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
"react": "^18.2.0",
|
|
33
33
|
"react-dom": "^18.2.0",
|
|
34
34
|
"typescript": "^6.0.3",
|
|
35
|
-
"@deepseek-ai/dsh-client-modules": "0.1.7-alpha.2",
|
|
36
35
|
"@deepseek-ai/cordis-plugin-loader": "~1.0.5",
|
|
37
|
-
"@deepseek-ai/dsh-client-
|
|
38
|
-
"@deepseek-ai/dsh-client-
|
|
39
|
-
"@deepseek-ai/dsh-client-ui-
|
|
40
|
-
"@deepseek-ai/dsh-client-ui-
|
|
41
|
-
"@deepseek-ai/dsh-client-ui-
|
|
36
|
+
"@deepseek-ai/dsh-client-modules": "0.1.7-rc.2",
|
|
37
|
+
"@deepseek-ai/dsh-client-store": "0.1.7-rc.2",
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-dockkit": "0.1.7-rc.2",
|
|
39
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.2",
|
|
40
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2",
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2",
|
|
42
42
|
"@deepseek-ai/cordis": "~4.0.4",
|
|
43
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.7-
|
|
43
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.7-rc.2"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@deepseek-ai/cordis": "~4.0.4"
|