@deepseek-ai/dsh-client-web 0.1.6-alpha.1 → 0.1.7-alpha.1
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 +2 -2
- package/README.md +4 -0
- package/README.zh.md +4 -0
- package/lib/apply-injections.js +40 -0
- package/lib/base.css +47 -0
- package/lib/boot-page.module.css +1 -3
- package/lib/index.js +47 -10
- package/lib/types/apply-injections.d.ts +14 -0
- package/lib/types/boot.d.ts +3 -2
- package/lib/types/index.d.ts +1 -0
- package/package.json +17 -11
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
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
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 2cedd1748973e13ad1b9a3b0ef89c0101d4ffb09
|
|
6
|
+
README.zh.md: 882c338339b725f82aae4d931ab85a6d14650b42
|
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ English | [中文](README.zh.md)
|
|
|
27
27
|
|
|
28
28
|
Use it when you assemble the browser application: `apps/web`'s Vite entry runs `new AppWebEntry(container).run()` against the mount point, and the boot page carries the user through activation. Ordinary browser callers pass no options. A pre-injected page transport is the default ahead of the `seams` override: when `globalThis.__DSH_TRANSPORT__` carries `loadBundle`, the module stage adopts it as the bundle transport and skips the immediate-tier HTTP prefetch, while explicit `seams` still win (for example jsdom tests, where external `<script>` execution cannot reach the page context).
|
|
29
29
|
|
|
30
|
+
Static application pages install `__DSH_BOOT_READY__` before the entry runs. The boot page renders immediately while `run()` waits; the page owner applies the Host rows with `applyIndexInjections` (also exported from `./injections`) and resolves the deferred after all scripts finish. A rejected deferred renders a boot failure unless the caller supplies `run(onFailure)` to present the error externally while retaining the loading page. Desktop uses this callback to request native recovery. Desktop and WebWorker share the injection interpreter; server-side `tapIndex` HTML transforms apply only to served documents.
|
|
31
|
+
|
|
30
32
|
The shell base styles apply automatic CJK/Latin spacing to ordinary content in supporting browsers. Semantic code and terminal, diff, read, and search output containers retain literal source spacing and column alignment; browsers without `text-autospace` support ignore both declarations.
|
|
31
33
|
|
|
32
34
|
### What boot looks like
|
|
@@ -67,6 +69,8 @@ The kernel owns exactly three things: the module system, the Cordis Loader, and
|
|
|
67
69
|
|
|
68
70
|
The boot page is plain DOM with local CSS whose fallback fonts and colors match the theme tokens that arrive during loading. `internal/status` events drive one spinner node and per-entry labels; hydration preserves the node and animation phase through the application commit, and `fail()` renders the thrown reason. React mounting, slot rendering, and assembly live in `ui-renderer`; `ui-layout` owns the assembled browser-title projection.
|
|
69
71
|
|
|
72
|
+
The boot kernel delegates manifest entry creation to Client Modules so live graph synchronization owns the same entry identities after startup. The initial activation audit remains strict; later page-local failures appear in Settings → Plugins → Plugin list.
|
|
73
|
+
|
|
70
74
|
### Source map
|
|
71
75
|
|
|
72
76
|
| File | Role |
|
package/README.zh.md
CHANGED
|
@@ -27,6 +27,8 @@ kind: "package-library"
|
|
|
27
27
|
|
|
28
28
|
组装浏览器应用时使用它:`apps/web` 的 Vite 入口对挂载点运行 `new AppWebEntry(container).run()`,启动页会在激活过程中向用户展示进度。普通浏览器调用方不传任何选项。默认使用预注入的页面传输,除非提供 `seams` 覆盖:当 `globalThis.__DSH_TRANSPORT__` 携带 `loadBundle` 时,模块阶段将其采纳为 bundle 传输并跳过 `immediately` 层级的 HTTP 预取,而显式 `seams` 仍然优先(例如外部 `<script>` 执行无法到达页面上下文的 jsdom 测试)。
|
|
29
29
|
|
|
30
|
+
静态应用页面在入口运行前安装 `__DSH_BOOT_READY__`。`run()` 等待期间会立即显示启动页;页面所有者通过 `applyIndexInjections`(也从 `./injections` 导出)应用 Host 注入项,并在所有脚本完成后兑现延迟对象。延迟对象拒绝时显示启动失败;若调用方提供 `run(onFailure)`,则由外部呈现错误并保留加载页。Desktop 使用该回调请求原生恢复。Desktop 与 WebWorker 共享注入解释器;服务端 `tapIndex` HTML 转换仅适用于服务端提供的文档。
|
|
31
|
+
|
|
30
32
|
外壳基础样式会在支持的浏览器中为普通内容自动添加中西文间距。语义化代码以及终端、diff、读取和搜索输出容器会保留源码中的原始间距和列对齐;不支持 `text-autospace` 的浏览器会忽略这两项声明。
|
|
31
33
|
|
|
32
34
|
### 启动过程是怎样的
|
|
@@ -67,6 +69,8 @@ kind: "package-library"
|
|
|
67
69
|
|
|
68
70
|
启动页是原生 DOM 加本地 CSS,其回退字体与颜色匹配加载期间到达的主题 token。`internal/status` 事件驱动一个 spinner 节点与逐 entry 标签;hydrate 会保留该节点与动画相位直到应用提交,`fail()` 渲染抛出的原因。React 挂载、slot 渲染与应用组装位于 `ui-renderer`;`ui-layout` 拥有组装后的浏览器标题投影。
|
|
69
71
|
|
|
72
|
+
启动内核把清单条目创建交给 Client Modules,使启动后的动态图同步继续持有相同的条目身份。初始激活审计仍然严格;后续页面本地失败显示在「设置 → 插件 → 插件列表」。
|
|
73
|
+
|
|
70
74
|
### 源码地图
|
|
71
75
|
|
|
72
76
|
| 文件 | 职责 |
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region lib/types/apply-injections.js
|
|
2
|
+
function assertNever(row) {
|
|
3
|
+
throw new Error(`web boot: unknown index injection row ${JSON.stringify(row)}`);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Execute every row in table order.
|
|
7
|
+
* @param rows - Injection table from the boot payload.
|
|
8
|
+
* @param loadScript - Executes one script-src row through the page owner's asset transport.
|
|
9
|
+
*/
|
|
10
|
+
async function applyIndexInjections(rows, loadScript) {
|
|
11
|
+
for (const row of rows) switch (row.kind) {
|
|
12
|
+
case "global":
|
|
13
|
+
globalThis[row.name] = row.value;
|
|
14
|
+
break;
|
|
15
|
+
case "script": {
|
|
16
|
+
const el = document.createElement("script");
|
|
17
|
+
el.textContent = row.text;
|
|
18
|
+
(row.placement === "head" ? document.head : document.body).append(el);
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
case "script-src":
|
|
22
|
+
await loadScript(row.src);
|
|
23
|
+
break;
|
|
24
|
+
case "script-preload": break;
|
|
25
|
+
case "style": {
|
|
26
|
+
const el = document.createElement("style");
|
|
27
|
+
el.textContent = row.text;
|
|
28
|
+
document.head.append(el);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case "html":
|
|
32
|
+
(row.placement === "head" ? document.head : document.body).insertAdjacentHTML("beforeend", row.html);
|
|
33
|
+
break;
|
|
34
|
+
default: assertNever(row);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { applyIndexInjections };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=apply-injections.js.map
|
package/lib/base.css
CHANGED
|
@@ -32,6 +32,53 @@ body {
|
|
|
32
32
|
text-autospace: normal;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/* macOS desktop (the Electron preload sets data-platform): the window's
|
|
36
|
+
sidebar vibrancy shows only through a transparent page background. */
|
|
37
|
+
html[data-platform='darwin'],
|
|
38
|
+
html[data-platform='darwin'] body {
|
|
39
|
+
background: transparent;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* The transparent window disables backdrop-filter: Chromium composites it
|
|
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). */
|
|
47
|
+
html[data-platform='darwin'] body {
|
|
48
|
+
--dsw-specific-menu: rgba(248, 249, 250, 0.94);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
html[data-platform='darwin'] body[data-ds-dark-theme] {
|
|
52
|
+
--dsw-specific-menu: rgba(48, 49, 54, 0.94);
|
|
53
|
+
}
|
|
54
|
+
|
|
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
|
+
html[data-platform='darwin'] body > :not(#root) {
|
|
61
|
+
-webkit-app-region: no-drag;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Inside #root, interactive controls and raised surfaces (dialogs, menus)
|
|
65
|
+
subtract themselves from any drag band they overlap, wherever they render.
|
|
66
|
+
Electron composes app-regions from geometry in document order, so this works
|
|
67
|
+
only because the bands are declared before all content (AppFrame's first
|
|
68
|
+
child; the sidebar rows that precede the column's overlay slots) — a drag
|
|
69
|
+
rule on a content container would override every overlay mounted earlier in
|
|
70
|
+
the DOM. One selector on the native elements, ARIA roles, and tabindexed
|
|
71
|
+
custom widgets replaces per-component no-drag opt-outs. */
|
|
72
|
+
html[data-platform='darwin'] :is(
|
|
73
|
+
button, a, input, select, textarea, summary, [contenteditable='true'], [tabindex],
|
|
74
|
+
[role='dialog'], [role='alertdialog'], [role='menu'], [role='listbox'], [role='tooltip'],
|
|
75
|
+
[role='button'], [role='link'], [role='tab'], [role='menuitem'], [role='menuitemcheckbox'],
|
|
76
|
+
[role='menuitemradio'], [role='option'], [role='checkbox'], [role='radio'], [role='switch'],
|
|
77
|
+
[role='slider'], [role='combobox'], [role='textbox']
|
|
78
|
+
) {
|
|
79
|
+
-webkit-app-region: no-drag;
|
|
80
|
+
}
|
|
81
|
+
|
|
35
82
|
code,
|
|
36
83
|
pre,
|
|
37
84
|
[data-diff],
|
package/lib/boot-page.module.css
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* The framework-free boot page cannot depend on theme delivery succeeding. */
|
|
2
2
|
|
|
3
3
|
.boot {
|
|
4
|
-
--dsh-boot-bg: #fff;
|
|
5
4
|
--dsh-boot-label-primary: #0f1115;
|
|
6
5
|
--dsh-boot-label-secondary: #61666b;
|
|
7
6
|
--dsh-boot-label-tertiary: #81858c;
|
|
@@ -11,11 +10,10 @@
|
|
|
11
10
|
height: 100%;
|
|
12
11
|
display: grid;
|
|
13
12
|
place-items: center;
|
|
14
|
-
background: var(--dsw-alias-bg-base, var(--dsh-boot-bg));
|
|
13
|
+
background: var(--dsw-alias-bg-base, var(--dsh-boot-bg, Canvas));
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
:global(body[data-ds-dark-theme]) .boot {
|
|
18
|
-
--dsh-boot-bg: #151517;
|
|
19
17
|
--dsh-boot-label-primary: #f9fafb;
|
|
20
18
|
--dsh-boot-label-secondary: #cfd3d6;
|
|
21
19
|
--dsh-boot-label-tertiary: #adb2b8;
|
package/lib/index.js
CHANGED
|
@@ -56,11 +56,9 @@ async function bootClient(options) {
|
|
|
56
56
|
onEntryState?.(entry.options.name, STATE_LABELS[entry.fiber.state]);
|
|
57
57
|
});
|
|
58
58
|
const rows = manifest.plugins.map((row) => row.id);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (loader.resolve(id).fiber === void 0) onEntryState?.(name, "failed");
|
|
63
|
-
}));
|
|
59
|
+
for (const name of rows) onEntryState?.(name, "loading");
|
|
60
|
+
await options.modules.entries.start(loader, manifest);
|
|
61
|
+
for (const entry of loader.entries()) if (entry.fiber === void 0) onEntryState?.(entry.options.name, "failed");
|
|
64
62
|
await loader.await();
|
|
65
63
|
assertEntriesActive(ctx);
|
|
66
64
|
}
|
|
@@ -247,9 +245,10 @@ var AppWebEntry = class {
|
|
|
247
245
|
/**
|
|
248
246
|
* Load and activate every client entry, then hand the mount point to the
|
|
249
247
|
* UI renderer. Plugin failures remain visible on the boot page.
|
|
250
|
-
* @
|
|
248
|
+
* @param onFailure - Optional carrier-owned fatal presentation; keeps the boot page visible.
|
|
249
|
+
* @returns Resolves after application mount or failure reporting.
|
|
251
250
|
*/
|
|
252
|
-
async run() {
|
|
251
|
+
async run(onFailure) {
|
|
253
252
|
try {
|
|
254
253
|
await globalThis.__DSH_BOOT_READY__?.promise;
|
|
255
254
|
const win = globalThis;
|
|
@@ -273,13 +272,14 @@ var AppWebEntry = class {
|
|
|
273
272
|
modules: this.modules,
|
|
274
273
|
manifest: this.manifest,
|
|
275
274
|
onEntryState: (name, state) => {
|
|
276
|
-
this.page.setState(name, state);
|
|
275
|
+
if (onFailure === void 0 || state !== "failed") this.page.setState(name, state);
|
|
277
276
|
}
|
|
278
277
|
});
|
|
279
278
|
await mountClient(ctx, this.container);
|
|
280
279
|
} catch (reason) {
|
|
281
280
|
console.error(reason);
|
|
282
|
-
|
|
281
|
+
if (onFailure !== void 0) onFailure(reason);
|
|
282
|
+
else this.page.fail(reason instanceof Error ? reason.message : String(reason));
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
/** Dispose the client plugin tree and whichever page owns the mount point. */
|
|
@@ -316,6 +316,43 @@ const PLATFORM_MODULES = [
|
|
|
316
316
|
/** Client-bundle specifiers whose factories the parser preloads before the shell starts. */
|
|
317
317
|
const PRELOADED_CLIENT_EXTERNALS = [];
|
|
318
318
|
//#endregion
|
|
319
|
-
|
|
319
|
+
//#region lib/types/apply-injections.js
|
|
320
|
+
function assertNever(row) {
|
|
321
|
+
throw new Error(`web boot: unknown index injection row ${JSON.stringify(row)}`);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Execute every row in table order.
|
|
325
|
+
* @param rows - Injection table from the boot payload.
|
|
326
|
+
* @param loadScript - Executes one script-src row through the page owner's asset transport.
|
|
327
|
+
*/
|
|
328
|
+
async function applyIndexInjections(rows, loadScript) {
|
|
329
|
+
for (const row of rows) switch (row.kind) {
|
|
330
|
+
case "global":
|
|
331
|
+
globalThis[row.name] = row.value;
|
|
332
|
+
break;
|
|
333
|
+
case "script": {
|
|
334
|
+
const el = document.createElement("script");
|
|
335
|
+
el.textContent = row.text;
|
|
336
|
+
(row.placement === "head" ? document.head : document.body).append(el);
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
case "script-src":
|
|
340
|
+
await loadScript(row.src);
|
|
341
|
+
break;
|
|
342
|
+
case "script-preload": break;
|
|
343
|
+
case "style": {
|
|
344
|
+
const el = document.createElement("style");
|
|
345
|
+
el.textContent = row.text;
|
|
346
|
+
document.head.append(el);
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
case "html":
|
|
350
|
+
(row.placement === "head" ? document.head : document.body).insertAdjacentHTML("beforeend", row.html);
|
|
351
|
+
break;
|
|
352
|
+
default: assertNever(row);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
export { AppWebEntry, PLATFORM_MODULES, PRELOADED_CLIENT_EXTERNALS, applyIndexInjections, getStaticModules };
|
|
320
357
|
|
|
321
358
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-side interpreter for the structured index injection table. The served
|
|
3
|
+
* form renders the same rows into index.html text; a static worker page has
|
|
4
|
+
* no served HTML, so it executes the table directly. Rows execute strictly in
|
|
5
|
+
* table order, so a global row lands before the scripts that read it.
|
|
6
|
+
*/
|
|
7
|
+
import type { IndexInjection } from '@deepseek-ai/dsh-host-webserver';
|
|
8
|
+
/**
|
|
9
|
+
* Execute every row in table order.
|
|
10
|
+
* @param rows - Injection table from the boot payload.
|
|
11
|
+
* @param loadScript - Executes one script-src row through the page owner's asset transport.
|
|
12
|
+
*/
|
|
13
|
+
export declare function applyIndexInjections(rows: readonly IndexInjection[], loadScript: (src: string) => Promise<void>): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=apply-injections.d.ts.map
|
package/lib/types/boot.d.ts
CHANGED
|
@@ -19,9 +19,10 @@ export declare class AppWebEntry {
|
|
|
19
19
|
/**
|
|
20
20
|
* Load and activate every client entry, then hand the mount point to the
|
|
21
21
|
* UI renderer. Plugin failures remain visible on the boot page.
|
|
22
|
-
* @
|
|
22
|
+
* @param onFailure - Optional carrier-owned fatal presentation; keeps the boot page visible.
|
|
23
|
+
* @returns Resolves after application mount or failure reporting.
|
|
23
24
|
*/
|
|
24
|
-
run(): Promise<void>;
|
|
25
|
+
run(onFailure?: (reason: unknown) => void): Promise<void>;
|
|
25
26
|
/** Dispose the client plugin tree and whichever page owns the mount point. */
|
|
26
27
|
dispose(): Promise<void>;
|
|
27
28
|
/** Prefetch stage-one bundles and their dynamic requests before concurrent plugin imports. */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -8,4 +8,5 @@
|
|
|
8
8
|
export { AppWebEntry, type BootSeams } from './boot.ts';
|
|
9
9
|
export { getStaticModules } from './seed.ts';
|
|
10
10
|
export { PLATFORM_MODULES, PRELOADED_CLIENT_EXTERNALS, type PlatformModule } from './platform.ts';
|
|
11
|
+
export { applyIndexInjections } from './apply-injections.ts';
|
|
11
12
|
//# sourceMappingURL=index.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.
|
|
4
|
+
"version": "0.1.7-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
"default": "./lib/index.js"
|
|
20
20
|
},
|
|
21
21
|
"./src/*": "./src/*",
|
|
22
|
-
"./package.json": "./package.json"
|
|
22
|
+
"./package.json": "./package.json",
|
|
23
|
+
"./injections": {
|
|
24
|
+
"types": "./lib/types/apply-injections.d.ts",
|
|
25
|
+
"default": "./lib/apply-injections.js"
|
|
26
|
+
}
|
|
23
27
|
},
|
|
24
28
|
"license": "MIT",
|
|
25
29
|
"devDependencies": {
|
|
@@ -28,21 +32,23 @@
|
|
|
28
32
|
"react": "^18.2.0",
|
|
29
33
|
"react-dom": "^18.2.0",
|
|
30
34
|
"typescript": "^6.0.3",
|
|
31
|
-
"@deepseek-ai/
|
|
32
|
-
"@deepseek-ai/dsh-client-
|
|
33
|
-
"@deepseek-ai/dsh-client-ui-
|
|
34
|
-
"@deepseek-ai/dsh-client-ui-
|
|
35
|
-
"@deepseek-ai/
|
|
36
|
-
"@deepseek-ai/
|
|
37
|
-
"@deepseek-ai/dsh-client-
|
|
38
|
-
"@deepseek-ai/
|
|
35
|
+
"@deepseek-ai/dsh-client-modules": "^0.1.7-alpha.1",
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-dockkit": "^0.1.7-alpha.1",
|
|
37
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
|
|
39
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
40
|
+
"@deepseek-ai/cordis-plugin-loader": "^1.0.4",
|
|
41
|
+
"@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
|
|
42
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.7-alpha.1",
|
|
43
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1"
|
|
39
44
|
},
|
|
40
45
|
"peerDependencies": {
|
|
41
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
46
|
+
"@deepseek-ai/cordis": "^4.0.3"
|
|
42
47
|
},
|
|
43
48
|
"files": [
|
|
44
49
|
"lib/index.js",
|
|
45
50
|
"lib/**/*.css",
|
|
51
|
+
"lib/apply-injections.js",
|
|
46
52
|
"lib/types/**/*.d.ts"
|
|
47
53
|
]
|
|
48
54
|
}
|