@deepseek-ai/dsh-client-ui-slots 0.1.1-rc.2 → 0.1.2-alpha.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 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/ui-slots/README.md
5
- README.md: 332bd092e8a4dec5a424acccce57c3dd0d8c27d8
6
- README.zh.md: 369776ea72c6f21184c0accd183aa9b409ee867a
5
+ README.md: 7b5b55c7f640d290b569354a1d2ce1771fcbf871
6
+ README.zh.md: 3212304227048c5334695395160378c7dfb19fb3
package/README.md CHANGED
@@ -1,29 +1,82 @@
1
+ ---
2
+ description: "Slot registry pure core for the dsh web client: SlotMap declaration merging, the single register composition API, four-share props types, store seats, and the renderer install contract."
3
+ kind: "package-library"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-client-ui-slots
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Slot registry pure core, slot terminal design: SlotMap declaration merging, the single `register` composition API on SlotCore, the four-share component-props type family, the store-seat type family, and the renderer installation contract. React types only at runtime — the package is React-free and cordis-free.
10
+ ## Summary
11
+
12
+ `dsh-client-ui-slots` is the pure core of the web client's slot system: the type-level contract every UI feature composes through. One `register({ name, children?, store?, inject?, ...kind }, Component)` call contributes a component into a declared slot and, in the same breath, declares child slots, a store seat, and the registrant's business face. The component is checked at the call site against `ComposedProps` — the intersection of four shares, each derived from its single source of truth — so a wrong composition fails to compile. Chain-kind slots invert keyed routing: entries self-nominate through a pure selector instead of the dispatch site picking an `entryKey`. The package is React-free and Cordis-free at runtime (React types only); `ui-renderer` owns the engine implementation and React bindings.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Compose UI through this package whenever you write a client plugin: register a component into a slot your parent declared, or declare child slots your component renders. The four kinds cover the composition shapes — `single` (one occupant), `list` (ordered entries), `keyed` (dispatch by a key), and `chain` (entries elect themselves).
29
+
30
+ ### The four props shares
31
+
32
+ Every registered component receives props composed from four shares: the runtime share (`owner` from the parent's renderSlot call site, plus the session standard kit and global seat), the child-render share (`renderSlot` statically narrowed to the declared children keys), the store share (the declared handle's selector hook and draft-stripped actions), and the business share (inferred from the `inject` factory's return). Components reference `ComposedProps`; they never re-type a share locally.
33
+
34
+ ### Store seats
35
+
36
+ A register call may declare a store seat with `store: defineStore(...)`: `init` infers the state schema and `actions` is the complete draft-transform write set. Components read through the selector hook and write through the baked callbacks; the engine implementation of `defineStore` lives in the runtime package and satisfies the `DefineStore` contract exported here.
37
+
38
+ ### Declaration discipline
39
+
40
+ Declaring a slot is claiming it: the registering entry becomes the only entry allowed to render that key, and registering into an undeclared slot, declaring an already-declared child, mounting one shared handle under two scopes, or registering a chain without `select` throws at load. An entry's disposer collapses its declared child slots recursively — ledger rows, contributions, and store mounts die on one lifecycle axis.
41
+
42
+ -----
6
43
 
7
- One `register({ name, children?, store?, inject?, ...kind }, Component)` call contributes a component into a declared slot and, in the same breath, declares child slots (declaration = render authorization = runtime spec, one table), a store seat, and the registrant's business face. The component is checked at the call site against `ComposedProps` — the intersection of four shares, each derived from its single source of truth:
44
+ <a id="understand-the-implementation"></a>
45
+ ## Understand the implementation
8
46
 
9
- | share | type | source |
10
- |---|---|---|
11
- | runtime | `PropsRuntime<K>` | SlotMap entry: `owner` (parent's renderSlot call site) + session standard kit + global seat |
12
- | child render | `PropsRenderSlots<S>` | the register call's `children` key set (statically narrowed `renderSlot`) |
13
- | store | `PropsStore<H>` | the declared handle: `useStore` selector hook + draft-stripped `actions` |
14
- | business | `I` | inferred from the `inject` factory's return |
47
+ <details>
48
+ <summary>Implementation internals — click to expand</summary>
15
49
 
16
- Chain-kind slots invert keyed routing entries self-nominate instead of the dispatch site picking an `entryKey`: each registration carries a pure `ChainSelect` selector (plus optional ascending `priority`, ties in registration order), the first non-null return elects its entry and becomes the component's `matched` prop, and all-null falls to the owner's `renderSlotChain` fallback (`ChainRenderOpts`).
50
+ The design is one table: declaration = render authorization = runtime spec. `SlotMap` is declared empty here and merged by consumers via `declare module` augmentation, exactly like the standard-kit interfaces (`SessionStandardProps`, `GlobalStandardProps`), which the runtime package merges with real members.
17
51
 
18
- The standard-kit interfaces (`SessionStandardProps`, `GlobalStandardProps`) are declared empty here and merged by the runtime package (same declare-merge pattern as SlotMap keys). The renderer binds the runtime's session and workspace observable sources into selector hooks. Inject factory parameters derive from the declaration (`InjectParams`): session slots get `sessionId`, a declared store appends baked `actions`, nothing else — data access lives in the apply closure's ctx.
52
+ ### Registration and routing
19
53
 
20
- The store family (`defineStore` spec in / `StoreHandle<T, A>` out) types the store seat: `init` infers the state schema, `actions` is the complete draft-transform write set, `BakedActions` strips the draft parameter into the callbacks components and inject factories receive. The `defineStore` value implementation lives in the runtime package (the engine's home) and satisfies the `DefineStore` contract exported here. Engine products and the renderer host contract carry bare snapshot sources (`getSnapshot`/`subscribe`), never React hooks hook binding belongs to the render machinery; only the props-contract hook type (`SnapshotSelectorHook`) lives here.
54
+ `SlotCore` seeds the a-priori `'root'` slot at construction and enforces load-time validation. `ChainSelect` selectors run in ascending `priority` order (ties in registration order); the first non-null return elects its entry and becomes the component's `matched` prop, and all-null falls to the owner's `renderSlotChain` fallback (`ChainRenderOpts`). Each key carries a declaration epoch that advances only on declaration and collapse; `ui-renderer` uses it for `ctx.slots.inject`, independently from ordinary entry versions.
21
55
 
22
- `SlotCore` seeds the a-priori `'root'` slot at construction and enforces load-time validation (undeclared-slot registration, duplicate child declaration, one shared handle under two scopes, a chain registration without `select` — all throw at register). An entry's disposer collapses its declared child slots recursively: ledger rows, contributions, and store mounts die on one lifecycle axis. Each key also carries a declaration epoch that advances only on declaration and collapse; the runtime uses it for [`ctx.slots.inject`](../runtime/README.md#slot-declaration-injection), independently from ordinary entry versions. `renderer.ts` carries the installation contract (`SlotRenderer`, `SlotRendererHost`) plus `StaleAuthorizationError`/`SlotOwnershipError`; ui-renderer owns both the implementation and its plugin-lifecycle installation.
56
+ ### The renderer contract
23
57
 
58
+ `renderer.ts` carries the installation contract (`SlotRenderer`, `SlotRendererHost`) plus `StaleAuthorizationError`/`SlotOwnershipError`; ui-renderer owns both the implementation and its plugin-lifecycle installation. Engine products and the renderer host contract carry bare snapshot sources (`getSnapshot`/`subscribe`), never React hooks — hook binding belongs to the render machinery.
59
+
60
+ </details>
61
+
62
+ -----
63
+
64
+ <a id="further-exploration"></a>
65
+ ## Further Exploration
66
+
67
+ These pages cover the engine, the renderer, and the composition model.
68
+
69
+ - [Slot declaration injection decision](../../../.agents/notes/implemented/architecture/2026-08-05-slot-declaration-injection.md) — the lifecycle rules behind `ctx.slots.inject`.
70
+ - [ui-renderer](../ui-renderer/README.md) — the React slot renderer implementing this package's install contract.
71
+ - [Slot system standard](../../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) — the definitive composition model.
72
+ - [Web client architecture](../../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) — the loading chain and object layer this registry plugs into.
73
+
74
+ -----
75
+
76
+ <a id="model-experience"></a>
24
77
  ## Model Experience
25
78
 
26
- None, as the slot registry is browser-side UI plumbing; nothing here reaches a model request.
79
+ None, as the package is a browser-side UI plugin layer that registers nothing model-facing.
27
80
 
28
81
  #### KV Cache effect
29
82
 
@@ -31,5 +84,20 @@ None; this package neither assembles nor sends a provider request.
31
84
 
32
85
  ## Known Limitations and Deferred Work
33
86
 
87
+ <a id="known-limitations-and-deferred-work"></a>
88
+
89
+
90
+ These limits define the registry's scaling behavior and accepted type noise; they are current package constraints.
91
+
34
92
  - **`isLive` scans all records linearly** — fine at UI-plugin registration counts (tens); revisit with an entry→record backref if ledgers ever grow hot.
35
93
  - **The `__renders` phantom anchor is visible on `PropsRenderSlots`** — the same accepted noise as the type-chain design's `__accepts`: generic method signatures compare loosely across key unions, so the contravariant marker is what enforces "component key set ⊆ children declaration".
94
+
95
+ <a id="dev-note"></a>
96
+ ### Dev Note
97
+
98
+ <details>
99
+ <summary>Working context for maintainers — click to expand</summary>
100
+
101
+ None.
102
+
103
+ </details>
package/README.zh.md CHANGED
@@ -1,35 +1,103 @@
1
+ ---
2
+ description: "dsh Web 客户端的 slot 注册表纯核心:SlotMap 声明合并、单一 register 组合 API、四 share props 类型、store 席位与渲染器安装约定。"
3
+ kind: "package-library"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-client-ui-slots
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- Slot 注册表纯核心、slot 终端设计:SlotMap 声明合并、SlotCore 上唯一的 `register` 组合 API、四 share 组件 props 类型家族、store seat 类型家族,以及 renderer 安装约定。只使用 React 类型;该包不依赖 React,也不依赖 Cordis。
10
+ ## 概述
11
+
12
+ `dsh-client-ui-slots` 是 Web 客户端 slot 系统的纯核心:每个 UI 功能都经由它组合的类型级约定。一次 `register({ name, children?, store?, inject?, ...kind }, Component)` 调用会向已声明 slot 贡献一个组件,同时声明子 slot、store 席位与注册方的业务表层。组件会在调用点依据 `ComposedProps` 接受类型检查——该类型是四个 share 的交集,每个 share 都从各自的唯一真源派生——因此错误的组合在编译期就会失败。chain-kind slot 会反转键控路由:条目通过纯 selector 自行提名,而不是由分发点选择 `entryKey`。本包在运行时与 Cordis 无关(仅使用 React 类型);`ui-renderer` 拥有引擎实现与 React 绑定。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 编写客户端插件时都通过本包组合 UI:把组件注册进父级已声明的 slot,或声明组件将要渲染的子 slot。四种 kind 覆盖组合形态——`single`(单个占位者)、`list`(有序条目)、`keyed`(按键分派)与 `chain`(条目自行提名)。
29
+
30
+ ### 四个 props share
31
+
32
+ 每个已注册组件都会收到由四个 share 组合而成的 props:运行时 share(父级 renderSlot 调用点的 `owner`,加上会话标准工具包与全局席位)、child render share(静态缩窄到已声明 children key 的 `renderSlot`)、store share(已声明 handle 的 selector 钩子与移除 draft 的 actions),以及业务 share(从 `inject` factory 返回值推断)。组件引用 `ComposedProps`;它们绝不在本地重新输入任何 share。
33
+
34
+ ### Store 席位
35
+
36
+ register 调用可以用 `store: defineStore(...)` 声明 store 席位:`init` 推断状态 schema,`actions` 是完整的 draft-transform 写入集合。组件经 selector 钩子读取、经烘焙回调写入;`defineStore` 的引擎实现位于 runtime 包,并满足这里导出的 `DefineStore` 约定。
37
+
38
+ ### 声明纪律
39
+
40
+ 声明即认领:注册条目成为唯一被允许渲染该键的条目;注册未声明 slot、声明已声明过的子项、在两个 scope 下挂载同一个共享 handle、或注册缺少 `select` 的 chain,都会在加载时抛出。条目的 disposer 会递归移除其声明的子 slot——账本行、贡献与 store 挂载都随同一生命周期结束而移除。
41
+
42
+ -----
6
43
 
7
- 一次 `register({ name, children?, store?, inject?, ...kind }, Component)` 调用会向已声明 slot 贡献一个组件,同时声明子 slot(声明 = 渲染授权 = 运行时规范,三者共用一张表)、store seat 以及注册方的业务表层。组件会在调用点依据 `ComposedProps` 接受类型检查;该类型是四个 share 的交集,每个 share 都从各自的唯一真源派生:
44
+ <a id="understand-the-implementation"></a>
45
+ ## 理解实现
8
46
 
9
- | share | 类型 | 来源 |
10
- |---|---|---|
11
- | 运行时 | `PropsRuntime<K>` | SlotMap 条目:`owner`(父级 renderSlot 调用点)+ 会话标准工具包 + 全局 seat |
12
- | child render | `PropsRenderSlots<S>` | register 调用的 `children` key 集合(静态缩窄的 `renderSlot`) |
13
- | store | `PropsStore<H>` | 已声明 handle:`useStore` selector 钩子 + 移除 draft 的 `actions` |
14
- | business | `I` | 从 `inject` factory 返回值推断 |
47
+ <details>
48
+ <summary>实现细节——点击展开</summary>
15
49
 
16
- chain-kind slot 会反转键控路由:条目自行提名,而不是由分发点选择 `entryKey`。每次注册都携带一个纯 `ChainSelect` selector(另有可选的升序 `priority`,相同值按注册顺序处理);第一个非 null 返回值选中其条目,并成为组件的 `matched` prop;全部返回 null 时则使用 owner 的 `renderSlotChain` fallback(`ChainRenderOpts`)。
50
+ 设计就是一张表:声明 = 渲染授权 = 运行时规范。`SlotMap` 在这里声明为空,由消费方通过 `declare module` 增补合并,标准工具包接口(`SessionStandardProps`、`GlobalStandardProps`)也是如此,由 runtime 包以真实成员合并。
17
51
 
18
- 标准工具包接口(`SessionStandardProps`、`GlobalStandardProps`)在这里声明为空,由运行时包合并(与 SlotMap key 相同的 declare-merge 模式)。renderer 会把运行时会话和 Workspace observable source 绑定为 selector 钩子。Inject factory 参数从声明派生(`InjectParams`):会话 slot 获得 `sessionId`;声明 store 时追加 baked `actions`;没有其他参数,数据访问位于 apply 闭包的 ctx 中。
52
+ ### 注册与路由
19
53
 
20
- store 家族(输入 `defineStore` 规范/输出 `StoreHandle<T, A>`)为 store seat 建模:`init` 推断状态 schema;`actions` 是完整的 draft-transform 写入集合;`BakedActions` 移除 draft 参数,成为组件和 inject factory 收到的回调。`defineStore` 值实现位于运行时包(引擎所属位置),并满足这里导出的 `DefineStore` 约定。引擎产物与 renderer host 约定携带裸快照 source(`getSnapshot`/`subscribe`),绝不携带 React 钩子;钩子绑定属于渲染机制,只有 props 约定钩子类型(`SnapshotSelectorHook`)位于这里。
54
+ `SlotCore` 在构造时预置 `'root'` slot,并强制执行加载时验证。`ChainSelect` selector 按升序 `priority` 运行(相同值按注册顺序);第一个非 null 返回值选中其条目,并成为组件的 `matched` prop;全部返回 null 时使用 owner `renderSlotChain` fallback(`ChainRenderOpts`)。每个 key 都携带一个 declaration epoch,它只在声明与移除时递增;`ui-renderer` 将其用于 `ctx.slots.inject`,且与普通条目版本相互独立。
21
55
 
22
- `SlotCore` 在构造时预置 `'root'` slot,并强制执行加载时验证(注册未声明 slot、重复声明子项、在两个 scope 下使用同一个共享 handle、chain 注册缺少 `select`,这些情况都在 register 时抛出)。条目的 disposer 会递归移除其声明的子 slot:账本行、贡献和 store 挂载都会随同一生命周期结束而移除。每个 key 还携带一个 declaration epoch(声明代次),它只在声明与移除时递增;运行时将其用于 [`ctx.slots.inject`](../runtime/README.zh.md#slot-declaration-injection),且与普通条目版本相互独立。`renderer.ts` 携带安装约定(`SlotRenderer`、`SlotRendererHost`)以及 `StaleAuthorizationError`/`SlotOwnershipError`;ui-renderer 同时持有实现及其插件生命周期安装。
56
+ ### 渲染器约定
23
57
 
58
+ `renderer.ts` 携带安装约定(`SlotRenderer`、`SlotRendererHost`)以及 `StaleAuthorizationError`/`SlotOwnershipError`;ui-renderer 同时持有实现及其插件生命周期安装。引擎产物与渲染器宿主约定携带裸快照 source(`getSnapshot`/`subscribe`),绝不携带 React 钩子——钩子绑定属于渲染机制。
59
+
60
+ </details>
61
+
62
+ -----
63
+
64
+ <a id="further-exploration"></a>
65
+ ## 进一步探索
66
+
67
+ 以下页面覆盖引擎、渲染器与组合模型。
68
+
69
+ - [Slot 声明注入决策](../../../.agents/notes/implemented/architecture/2026-08-05-slot-declaration-injection.zh.md)——`ctx.slots.inject` 背后的生命周期规则。
70
+ - [ui-renderer](../ui-renderer/README.zh.md)——实现本包安装约定的 React slot 渲染器。
71
+ - [slot 系统标准](../../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.zh.md)——权威组合模型。
72
+ - [Web 客户端架构](../../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.zh.md)——本注册表接入的加载链与对象层。
73
+
74
+ -----
75
+
76
+ <a id="model-experience"></a>
24
77
  ## 模型体验
25
78
 
26
- 无。slot 注册表属于浏览器侧 UI 接线;这里没有任何内容进入模型请求。
79
+ 无。该包是浏览器端 UI 接线层,不注册任何面向模型的内容。
27
80
 
28
81
  #### KV Cache 影响
29
82
 
30
83
  无;该包既不组装也不发送提供方请求。
31
84
 
32
- ## 已知限制与暂缓事项
85
+ ## 已知限制与延期工作
86
+
87
+ <a id="known-limitations-and-deferred-work"></a>
88
+
89
+
90
+ 这些限制定义注册表的扩展行为与已接受的类型噪声;它们是当前包约束。
33
91
 
34
92
  - **`isLive` 会线性扫描所有记录**:在 UI 插件的注册规模(数十项)下没有问题;如果账本变得频繁访问,再使用条目→记录反向引用改进。
35
93
  - **`__renders` 幻象锚点在 `PropsRenderSlots` 上可见**:这是与类型链设计的 `__accepts` 相同且已接受的噪声;泛型方法签名在 key 联合之间比较宽松,因此必须依靠逆变标记强制执行「组件 key 集合 ⊆ children 声明」。
94
+
95
+ <a id="dev-note"></a>
96
+ ### 开发备注
97
+
98
+ <details>
99
+ <summary>维护者的工作上下文——点击展开</summary>
100
+
101
+ 无。
102
+
103
+ </details>
package/lib/index.js CHANGED
@@ -1,4 +1,12 @@
1
1
  //#region lib/types/renderer.js
2
+ /**
3
+ * Convert one standard source name to its rendered Hook prop name.
4
+ * @param name - registered fixed or keyed source name.
5
+ * @returns the `use<Name>` prop exposed to Slot components.
6
+ */
7
+ function standardHookPropName(name) {
8
+ return `use${name[0]?.toUpperCase() ?? ""}${name.slice(1)}`;
9
+ }
2
10
  /** Thrown when a retained renderSlot binding is invoked after its declaring entry was disposed. */
3
11
  var StaleAuthorizationError = class extends Error {};
4
12
  /**
@@ -420,6 +428,6 @@ var SlotCore = class {
420
428
  }
421
429
  };
422
430
  //#endregion
423
- export { SlotCore, SlotOwnershipError, StaleAuthorizationError, resolveSlotLabel };
431
+ export { SlotCore, SlotOwnershipError, StaleAuthorizationError, resolveSlotLabel, standardHookPropName };
424
432
 
425
433
  //# sourceMappingURL=index.js.map
package/lib/invariant.js CHANGED
@@ -10,7 +10,7 @@ const name = "client-ui-slots-invariant";
10
10
  const inject = ["invariants"];
11
11
  /**
12
12
  * No runtime invariant: a zero-dependency pure registry core — it emits no
13
- * cordis events itself (the runtime SlotRegistry wrapper owns the event
13
+ * cordis events itself (the `ui-renderer` SlotRegistry owns the event
14
14
  * bridge and its invariants); define/register/dispose sequencing is asserted
15
15
  * directly by this package's behavior specs.
16
16
  */
@@ -9,8 +9,8 @@
9
9
  * the augmented module, not with re-exports.
10
10
  */
11
11
  import type { ReactNode } from 'react';
12
+ import type { BoundActions, HandleOf, PropsStore, SnapshotSelectorHook, StoreDecl } from '@deepseek-ai/dsh-client-store';
12
13
  import type { HostObservable } from './renderer.ts';
13
- import type { BoundActions, HandleOf, PropsStore, SnapshotSelectorHook, StoreDecl } from './store.ts';
14
14
  export * from './store.ts';
15
15
  export * from './renderer.ts';
16
16
  /** Slot contract table. Owners extend via declaration merging; entries are {@link SlotEntryDef}. */
@@ -155,28 +155,29 @@ export type SlotInjectOf<K extends keyof SlotMap & string> = SlotMap[K] extends
155
155
  export type ScopeOf<K extends keyof SlotMap & string> = SlotMap[K]['scope'];
156
156
  /**
157
157
  * Framework standard kit delivered to every session-scope slot component.
158
- * Declared EMPTY here (zero-dependency layer): the runtime package merges the
159
- * real members (`useSession` bound to the conversation snapshot and the
160
- * framework-supplied `sessionId`) exactly as consumers merge SlotMap keys.
158
+ * Declared empty here (zero-dependency layer): `ui-session` merges the
159
+ * Session lifecycle hook, projection hook, and Session identity; domain UI
160
+ * adapters merge their own standard hooks exactly as consumers merge SlotMap keys.
161
161
  */
162
162
  export interface SessionStandardProps {
163
163
  }
164
164
  /**
165
165
  * Framework standard kit delivered to current-session-optional slots. Its
166
166
  * hooks stay callable while no session is selected and return `undefined`
167
- * until one becomes current; concrete members merge in at runtime packages.
167
+ * until one becomes current; `ui-session` and domain UI adapters merge the
168
+ * concrete members.
168
169
  */
169
170
  export interface SessionMaybeStandardProps {
170
171
  }
171
172
  /**
172
173
  * Framework standard kit delivered to EVERY slot component (the global seat).
173
- * Declared empty here; the runtime package merges the global object-layer
174
- * selector hooks that shared page composition consumes.
174
+ * Declared empty here; each owning UI adapter merges the global selector hooks
175
+ * that shared page composition consumes.
175
176
  */
176
177
  export interface GlobalStandardProps {
177
178
  }
178
179
  /**
179
- * The session id type as the runtime's SessionStandardProps merge declares it
180
+ * The session id type as `ui-session`'s SessionStandardProps merge declares it
180
181
  * (branded); falls back to `string` in programs without the merge (this
181
182
  * package's own tests).
182
183
  */
@@ -200,12 +201,14 @@ export interface RenderOpts<EntryKey extends string = string> {
200
201
  export interface ChainRenderOpts {
201
202
  /** The owner's fallback body, rendered when every entry's selector declines. */
202
203
  fallback?: ReactNode;
204
+ /** Render only the owner fallback without resolving or dispatching the chain's scope. */
205
+ fallbackOnly?: boolean;
203
206
  /**
204
207
  * Keep the fallback permanently mounted: an election hides it (wrapped,
205
208
  * display:none) instead of unmounting it, and the all-decline case shows it
206
209
  * as-is — fallback-held state (composer drafts, DOM state) survives a
207
- * takeover. Chain kind only. Sole consumer today: the
208
- * 'conversation.composer' chain.
210
+ * takeover. Chain kind only; the sole consumer is the
211
+ * `conversation.composer` chain.
209
212
  */
210
213
  overlay?: boolean;
211
214
  }
@@ -249,23 +252,17 @@ type RenderSlotFn<S extends keyof SlotMap & string> = ([ContextualKeysOf<S>] ext
249
252
  export type MatchedShare<E extends SlotEntryDef, M> = E['kind'] extends 'chain' ? {
250
253
  matched: M;
251
254
  } : object;
252
- /**
253
- * Conversation-session selector hook alias for props contracts. Wide by
254
- * default at this dependency-inverted layer; the runtime narrows at its
255
- * export outlet (`UseSession<ConversationSnapshot>`).
256
- */
257
- export type UseSession<Snap extends object = object> = SnapshotSelectorHook<Snap>;
258
- /** Props of the standard-kit SessionProvider seat (render-prop form). */
255
+ /** Props of the standard-kit SessionProvider seat. */
259
256
  export interface SessionAreaProps {
260
257
  /** No-session body (also covers a current id whose session cannot be resolved). */
261
258
  empty?: (() => ReactNode) | undefined;
262
- /** Session body; the framework remounts it per session (key=sessionId). */
263
- children: (sessionId: SessionIdOf) => ReactNode;
259
+ /** Session body; the framework remounts it per session identity. */
260
+ children: ReactNode;
264
261
  }
265
262
  /**
266
- * Framework-wired session area component. It subscribes to runtime-owned
267
- * session selection and is injected into entries that declare session-scoped
268
- * children; business code does not import it directly.
263
+ * Framework-wired session area component. `ui-session` supplies the current
264
+ * Controller binding through the renderer scope adapter; entries that declare
265
+ * session-scoped children receive this component without importing it.
269
266
  */
270
267
  export type SessionProviderComponent = (props: SessionAreaProps) => ReactNode;
271
268
  /**
@@ -1,6 +1,8 @@
1
1
  /** React-free contracts between the slot host and an installed renderer. */
2
+ import type { Context } from '@deepseek-ai/cordis';
2
3
  import type { ReactNode } from 'react';
3
- import type { SlotEntryDef, SlotSpec, StoredEntry, Translate } from './index.ts';
4
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
5
+ import type { SessionAreaProps, SlotEntryDef, SlotScope, SlotSpec, StoredEntry, Translate } from './index.ts';
4
6
  /**
5
7
  * The locale face the render machinery consumes: namespace binding plus an
6
8
  * observable revision (getSnapshot/subscribe pair — the same HostObservable
@@ -8,7 +10,7 @@ import type { SlotEntryDef, SlotSpec, StoredEntry, Translate } from './index.ts'
8
10
  * active-locale or registry change; the renderer re-derives each entry's `t`
9
11
  * from (namespace, revision), so a locale switch hands out NEW function
10
12
  * references and memoized components re-render naturally. Implemented by the
11
- * locale plugin, installed through the runtime SlotRegistry (installLocale).
13
+ * locale plugin, installed through the `ui-renderer` SlotRegistry.
12
14
  * Install before the first render that needs the seat: outlets bind their
13
15
  * revision subscription at mount, and a face appearing later has no channel
14
16
  * to notify already-mounted outlets (the locale plugin is immediately-tier
@@ -27,11 +29,14 @@ export interface LocaleFace extends HostObservable<{
27
29
  */
28
30
  bind(ns: string): Translate;
29
31
  }
30
- /** Minimal observable API for host-provided standard-kit data sources. */
31
- export interface HostObservable<T> {
32
- getSnapshot(): T;
33
- subscribe(fn: () => void): () => void;
34
- }
32
+ /** Observable currency shared by domain sources, stores, and the renderer. */
33
+ export type HostObservable<T> = ObservableSnapshot<T>;
34
+ /**
35
+ * Convert one standard source name to its rendered Hook prop name.
36
+ * @param name - registered fixed or keyed source name.
37
+ * @returns the `use<Name>` prop exposed to Slot components.
38
+ */
39
+ export declare function standardHookPropName(name: string): string;
35
40
  /**
36
41
  * Type-erased store instance face at the render boundary (the typed twin is
37
42
  * {@link StoreInstance}): a bare snapshot source plus the draft-stripped
@@ -49,42 +54,53 @@ export interface StoreInstanceLike {
49
54
  subscribe(fn: () => void): () => void;
50
55
  readonly actions: Record<string, (...params: never[]) => void>;
51
56
  }
57
+ /** Resolve one member of an open-key standard hook family. */
58
+ export type KeyedStandardSource = (key: string) => HostObservable<unknown> | undefined;
52
59
  /**
53
- * Per-session standard props resolved per session id (identity-stable per
54
- * session scope; a recreated scope yields a new info). Plugins contribute
55
- * members through the runtime `sessions.provide` contract; the render side binds
56
- * every `hooks` source into a `use<Name>` selector hook (hooks never appear
57
- * on the host contract) and spreads `props` verbatim. The runtime itself
58
- * contributes the first entry (`'session'` → `useSession`).
60
+ * Framework-neutral inputs from which the renderer materializes standard
61
+ * props. A scope adapter keeps member names present while its binding is
62
+ * absent so optional slots retain a stable Hook call order.
59
63
  */
60
- export interface SessionMaybeProvideInfo {
61
- /** Current session id, absent while the application is in no-session mode. */
62
- sessionId: string | undefined;
64
+ export interface StandardSourceBinding {
65
+ /** Scope identity; absent for root data and an optional scope with no selection. */
66
+ readonly key: string | undefined;
67
+ /** Fixed-name sources; each `name` becomes a `useName` selector Hook. */
68
+ readonly hooks: Readonly<Record<string, HostObservable<unknown> | undefined>>;
69
+ /** Open-key source families; each `name` becomes a `useName(key, selector)` Hook. */
70
+ readonly keyedHooks: Readonly<Record<string, KeyedStandardSource | undefined>>;
71
+ /** Stable plain values copied into standard props. */
72
+ readonly props: Readonly<Record<string, unknown>>;
73
+ }
74
+ /** Materialized binding for one live non-root scope. */
75
+ export interface ScopedStandardSourceBinding extends StandardSourceBinding {
76
+ readonly key: string;
77
+ readonly ctx: Context;
78
+ }
79
+ /** One installed source of bindings for a non-root Slot scope. */
80
+ export interface SlotScopeAdapter {
81
+ /** Binding that follows the current selection, including its absent projection. */
82
+ readonly current: HostObservable<StandardSourceBinding>;
63
83
  /**
64
- * Static hook roster. Each value is absent with the session; keys remain so
65
- * session-maybe entries always receive the same hook-shaped standard kit.
84
+ * Resolve an already-materialized binding.
85
+ * @param key - scope identity.
86
+ * @returns the binding, or `undefined` when the identity is unavailable.
66
87
  */
67
- hooks: Record<string, HostObservable<unknown> | undefined>;
68
- /** Static plain-member roster; values are undefined with the session. */
69
- props: Record<string, unknown>;
88
+ resolve(key: string): ScopedStandardSourceBinding | undefined;
70
89
  /**
71
- * Key-addressed projection value sources (the useProjection framework seat;
72
- * session-projection subsystem page: docs/subsystems/session-projection.md).
73
- * Unlike `hooks`, the key space is open — values
74
- * arrive from host-computed push frames so the render side binds per
75
- * resolved key instead of per static roster member. Faces are always
76
- * defined per key (absence is an `undefined` snapshot); the whole member is
77
- * absent with the session.
90
+ * Render the scope owner's area seat over the current binding. The renderer
91
+ * binds this function to the standard `SessionProvider` prop without owning
92
+ * Session selection semantics.
93
+ * @param binding - current scope binding, including its absent projection.
94
+ * @param props - render-prop body and empty branch.
95
+ * @returns rendered scope area.
78
96
  */
79
- projections?: {
80
- faceOf(key: string): HostObservable<unknown>;
81
- } | undefined;
97
+ renderArea?(binding: StandardSourceBinding, props: SessionAreaProps): ReactNode;
82
98
  }
83
- /** Definite per-session standard props resolved for strict session slots. */
84
- export interface SessionProvideInfo extends SessionMaybeProvideInfo {
85
- sessionId: string;
86
- /** Bare observable sources, keyed by hook base name ('session' → useSession). */
87
- hooks: Record<string, HostObservable<unknown>>;
99
+ /** Root standard-source contribution installed by one domain UI package. */
100
+ export interface RootStandardSourceContribution {
101
+ readonly hooks?: Readonly<Record<string, HostObservable<unknown>>>;
102
+ readonly keyedHooks?: Readonly<Record<string, KeyedStandardSource>>;
103
+ readonly props?: Readonly<Record<string, unknown>>;
88
104
  }
89
105
  /** renderSlot dispatch options at the machinery level. */
90
106
  export interface RenderOpts {
@@ -94,7 +110,7 @@ export interface RenderOpts {
94
110
  /** Opaque occurrence context consumed only by function-valued injected Hooks. */
95
111
  hookContext?: unknown;
96
112
  }
97
- /** Host API the runtime SlotRegistry presents to the installed renderer. */
113
+ /** Host API the `ui-renderer` SlotRegistry presents to its React renderer. */
98
114
  export interface SlotRendererHost {
99
115
  /**
100
116
  * Subscribe to a key's registration changes (microtask-batched).
@@ -154,28 +170,21 @@ export interface SlotRendererHost {
154
170
  * Resolve (create or return cached) the store instance for an entry's
155
171
  * declared handle under a scope key; lifecycle rides the ledger axis.
156
172
  * @param entry - entry whose declaration carries the handle.
157
- * @param scopeKey - session id for session-scope slots, undefined for root scope.
173
+ * @param scopeBinding - exact Session binding for scoped slots, undefined for root scope.
158
174
  * @returns the instance, or undefined when the entry declares no store.
159
175
  */
160
- storeOf(entry: StoredEntry, scopeKey: string | undefined): StoreInstanceLike | undefined;
161
- /** Session-side standard-kit sources. */
162
- sessions: {
163
- /** Session list source backing the useSessions standard hook. */
164
- list: HostObservable<unknown>;
165
- /**
166
- * Atomic current-session provide projection used by SessionProvider:
167
- * selection changes and provider-roster changes publish through this one
168
- * source, so a stable current id cannot strand mounted entries on an
169
- * obsolete hook/prop schema. Carries the static roster with sessionId
170
- * undefined while no current session resolves.
171
- */
172
- provideInfo: HostObservable<SessionMaybeProvideInfo>;
173
- };
174
- /** Workspace-side standard-kit sources. */
175
- workspaces: {
176
- /** Workspace list source backing the useWorkspaces standard hook. */
177
- list: HostObservable<unknown>;
178
- };
176
+ storeOf(entry: StoredEntry, scopeBinding: ScopedStandardSourceBinding | undefined): StoreInstanceLike | undefined;
177
+ /** Root standard data assembled from domain-owned contributions. */
178
+ readonly root: HostObservable<StandardSourceBinding>;
179
+ /** Monotonic source updated whenever the installed scope-adapter roster changes. */
180
+ readonly scopeRevision: HostObservable<number>;
181
+ /**
182
+ * Resolve the adapter installed for one non-root scope.
183
+ * `session` and `session-maybe` intentionally resolve the same adapter.
184
+ * @param scope - Slot scope.
185
+ * @returns adapter, or `undefined` when the composition omitted its owner.
186
+ */
187
+ scope(scope: Exclude<SlotScope, 'root'>): SlotScopeAdapter | undefined;
179
188
  /**
180
189
  * Installed locale face backing the `t` standard seat (absent until the
181
190
  * locale plugin installs one; rendering an entry that declared `locale:`
@@ -183,7 +192,7 @@ export interface SlotRendererHost {
183
192
  */
184
193
  locale?: LocaleFace | undefined;
185
194
  }
186
- /** The installation contract: runtime owns install()/renderSlot(); ui-renderer implements rendering. */
195
+ /** The installation contract between the `ui-renderer` SlotRegistry and its React renderer. */
187
196
  export interface SlotRenderer {
188
197
  /**
189
198
  * Render the root slot tree over the host API (the only ctx-level entry).
@@ -1,111 +1,3 @@
1
- /** Framework-neutral store contracts for slot registrations and the runtime engine. */
2
- /**
3
- * Typed selector hook over a snapshot source. Canonical shape for the whole
4
- * slot system (ui-renderer's engine hook is structurally identical; the
5
- * framework is the only party that ever constructs one).
6
- */
7
- export type SnapshotSelectorHook<T> = <S>(sel: (s: T) => S, eq?: (a: S, b: S) => boolean) => S;
8
- /**
9
- * Selector hook over a source that follows the current session. The hook is
10
- * always present, while its selected value is absent whenever no session is
11
- * current. This keeps hook call sites stable across no-session/session
12
- * transitions without pretending that a session snapshot exists.
13
- */
14
- export type MaybeSnapshotSelectorHook<T> = <S>(sel: (s: T) => S, eq?: (a: S, b: S) => boolean) => S | undefined;
15
- /**
16
- * Action declaration table: pure immer-draft transforms over the store state,
17
- * declared as the store's complete write set (the audit face — components can
18
- * only write through these).
19
- */
20
- export type ActionsDecl<T> = Record<string, (draft: T, ...params: any[]) => void>;
21
- /**
22
- * Draft-stripped callback form of an actions table: what components
23
- * (`props.actions`) and inject factories receive — the framework bakes the
24
- * draft parameter away by binding each action to the resolved instance.
25
- */
26
- export type BakedActions<T, A extends ActionsDecl<T>> = {
27
- [K in keyof A]: A[K] extends (draft: T, ...params: infer P) => void ? (...params: P) => void : never;
28
- };
29
- /**
30
- * Store declaration spec: initial-state factory (a lambda so every instance
31
- * gets a fresh state), optional persistence key (mechanical, framework-run),
32
- * and the actions write set.
33
- */
34
- export interface StoreSpec<T, A extends ActionsDecl<T>> {
35
- init: () => T;
36
- persist?: string;
37
- actions: A;
38
- }
39
- /**
40
- * Live engine instance: the create() product consumed by the render machinery
41
- * and by tests. A bare snapshot source plus the baked write set — no React
42
- * hook rides the engine product (the engine lives in the React-free runtime);
43
- * the render machinery binds the `useStore` hook from this source on its own
44
- * side, cached per instance. Production components and render paths never
45
- * call create() themselves — instance lifecycle is the framework's.
46
- */
47
- export interface StoreInstance<T, A extends ActionsDecl<T>> {
48
- readonly actions: BakedActions<T, A>;
49
- getSnapshot(): T;
50
- /**
51
- * Subscribe to state changes (uSES subscribe side).
52
- * @param fn - change callback.
53
- * @returns unsubscribe.
54
- */
55
- subscribe(fn: () => void): () => void;
56
- /**
57
- * Drop this instance's persisted value (no-op for non-persist specs). The
58
- * framework calls it when the owning scope dies for good — a pruned session
59
- * must not leave orphaned storage keys behind.
60
- */
61
- clearPersisted(): void;
62
- }
63
- /**
64
- * Store handle: spec + state/actions types + shared identity + instance
65
- * factory in one value. Handles are constructed in apply world (shared across
66
- * registrations of one plugin) or by the framework from a registrant's
67
- * factory (exclusive). Never export a handle at module level — module-cache
68
- * identity is a disguised singleton across plugin reloads.
69
- */
70
- export interface StoreHandle<T, A extends ActionsDecl<T>> {
71
- readonly spec: StoreSpec<T, A>;
72
- /**
73
- * Create a live engine instance (framework machinery and tests only).
74
- * @param scopeKey - session id for session-scope instances; suffixes the
75
- * persist key so per-session instances persist independently (root-scope
76
- * instances omit it).
77
- * @returns a fresh instance seeded from `spec.init()`.
78
- */
79
- create(scopeKey?: string): StoreInstance<T, A>;
80
- }
81
- /**
82
- * Exclusive-store registration form: the registrant passes the factory itself
83
- * and the framework calls it per entry x scope (no shared identity exists).
84
- */
85
- export type StoreFactory = () => StoreHandle<any, any>;
86
- /** The register `store` option position: a shared handle or an exclusive factory. */
87
- export type StoreDecl = StoreHandle<any, any> | StoreFactory;
88
- /** Normalize a store declaration to its handle type (factories yield their return). */
89
- export type HandleOf<H> = H extends () => infer R ? R : H;
90
- /**
91
- * Handle-keyed baked actions: the `actions` parameter of an inject factory
92
- * whose registration declared a store — the same baked callback set the
93
- * component receives via {@link PropsStore}.
94
- */
95
- export type BoundActions<H> = H extends StoreHandle<infer T, infer A> ? BakedActions<T, A> : never;
96
- /**
97
- * The store props share, derived from the declared handle: a typed selector
98
- * hook plus the baked write set. Components never see the instance itself
99
- * (no update/set — reads via useStore, writes via the declared actions only).
100
- */
101
- export type PropsStore<H> = H extends StoreHandle<infer T, infer A> ? {
102
- useStore: SnapshotSelectorHook<T>;
103
- actions: BakedActions<T, A>;
104
- } : object;
105
- /**
106
- * The defineStore contract (implementation lives in the runtime package,
107
- * bound to the snapshot-store engine): spec in, handle out, with T inferred
108
- * from `init` and the actions table constrained by T.
109
- */
110
- export type DefineStore = <T, A extends ActionsDecl<T>>(spec: StoreSpec<T, A>) => StoreHandle<T, A>;
1
+ /** Slot-facing re-exports of the React-free store contracts. */
2
+ export type { ActionsDecl, BakedActions, BoundActions, DefineStore, HandleOf, MaybeSnapshotSelectorHook, PropsStore, SnapshotSelectorHook, StoreDecl, StoreFactory, StoreHandle, StoreInstance, StoreSpec, } from '@deepseek-ai/dsh-client-store';
111
3
  //# sourceMappingURL=store.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-slots",
3
3
  "description": "Slot registry pure core: SlotMap declaration merging, single register composition API, four-share props types, store-seat types, renderer install seam",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -28,8 +28,9 @@
28
28
  "license": "MIT",
29
29
  "devDependencies": {
30
30
  "@types/react": "~18.3.1",
31
- "@deepseek-ai/cordis": "^4.0.1",
32
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2"
31
+ "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.2",
32
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
33
+ "@deepseek-ai/cordis": "^4.0.2"
33
34
  },
34
35
  "files": [
35
36
  "lib/index.js",
@@ -37,7 +38,6 @@
37
38
  "lib/types/**/*.d.ts"
38
39
  ],
39
40
  "peerDependencies": {
40
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
41
- "@deepseek-ai/cordis": "^4.0.1"
41
+ "@deepseek-ai/cordis": "^4.0.2"
42
42
  }
43
43
  }