@deepseek-ai/dsh-client-ui-session 0.1.6-alpha.1 → 0.1.6-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 +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +232 -72
- package/lib/types/client/index.d.ts +77 -20
- package/lib/types/client/session-provider.d.ts +2 -2
- package/package.json +9 -6
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-session/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 4fff368e58d7b422ba6948be78e543c31f5cef59
|
|
6
|
+
README.zh.md: db37539b59231bfef50f3bdcc33aeae94e881a44
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ English | [中文](README.zh.md)
|
|
|
8
8
|
|
|
9
9
|
## Summary
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Use this package to expose Session catalog, retain information, and unified UI status through standard Slot hooks. It materializes per-`SessionBinding` hooks and props, while `SessionProvider` can inherit an outer binding or bind an explicit `SessionReference`. It owns process-local pending-interaction and completion-reminder policy without owning Controller transport, history, or references.
|
|
12
12
|
|
|
13
13
|
## Table of Contents
|
|
14
14
|
|
package/README.zh.md
CHANGED
|
@@ -8,7 +8,7 @@ kind: "package-reference"
|
|
|
8
8
|
|
|
9
9
|
## 概述
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
使用本包可通过标准 Slot 钩子公开 Session catalog、retain 信息与统一 UI 状态。它按 `SessionBinding` 物化钩子和 prop,而 `SessionProvider` 可以继承外围 binding 或绑定显式 `SessionReference`。它拥有进程本地的 pending-interaction 与完成提醒策略,但不拥有 Controller transport、历史或 reference。
|
|
12
12
|
|
|
13
13
|
## 目录
|
|
14
14
|
|
package/lib/client.js
CHANGED
|
@@ -8,19 +8,79 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
|
-
|
|
11
|
+
//#region ../../util/values/src/index.ts
|
|
12
|
+
/**
|
|
13
|
+
* Weak-key lookup with a strongly retained iterable set of associated values.
|
|
14
|
+
*
|
|
15
|
+
* Each value must belong to only one key. The container performs no automatic
|
|
16
|
+
* cleanup; owners delete associations or clear the container at lifecycle end.
|
|
17
|
+
*/
|
|
18
|
+
var WeakMapWithValues = class {
|
|
19
|
+
keys = /* @__PURE__ */ new WeakMap();
|
|
20
|
+
valueSet = /* @__PURE__ */ new Set();
|
|
21
|
+
/** Live strongly retained values in insertion order. */
|
|
22
|
+
values = this.valueSet;
|
|
23
|
+
/**
|
|
24
|
+
* Read the value associated with a key.
|
|
25
|
+
* @param key - weakly held lookup key.
|
|
26
|
+
* @returns the associated value, or absence.
|
|
27
|
+
*/
|
|
28
|
+
get(key) {
|
|
29
|
+
return this.keys.get(key);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Test whether a key has an association.
|
|
33
|
+
* @param key - weakly held lookup key.
|
|
34
|
+
* @returns whether the key is present.
|
|
35
|
+
*/
|
|
36
|
+
has(key) {
|
|
37
|
+
return this.keys.has(key);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Associate one key with one caller-unique value.
|
|
41
|
+
* @param key - weakly held lookup key.
|
|
42
|
+
* @param value - strongly retained value that belongs to no other key.
|
|
43
|
+
* @returns this container.
|
|
44
|
+
*/
|
|
45
|
+
set(key, value) {
|
|
46
|
+
if (this.keys.has(key)) {
|
|
47
|
+
const previous = this.keys.get(key);
|
|
48
|
+
if (previous === value) return this;
|
|
49
|
+
this.valueSet.delete(previous);
|
|
50
|
+
}
|
|
51
|
+
this.keys.set(key, value);
|
|
52
|
+
this.valueSet.add(value);
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Remove one association and its strongly retained value.
|
|
57
|
+
* @param key - weakly held lookup key.
|
|
58
|
+
* @returns whether an association was removed.
|
|
59
|
+
*/
|
|
60
|
+
delete(key) {
|
|
61
|
+
if (!this.keys.has(key)) return false;
|
|
62
|
+
const value = this.keys.get(key);
|
|
63
|
+
const deleted = this.keys.delete(key);
|
|
64
|
+
this.valueSet.delete(value);
|
|
65
|
+
return deleted;
|
|
66
|
+
}
|
|
67
|
+
/** Remove every association and strongly retained value. */
|
|
68
|
+
clear() {
|
|
69
|
+
this.keys = /* @__PURE__ */ new WeakMap();
|
|
70
|
+
this.valueSet.clear();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
12
74
|
//#region lib/types/client/session-provider.js
|
|
13
|
-
/** Session-owned rendering semantics for the standard SessionProvider seat. */
|
|
14
75
|
/**
|
|
15
76
|
* Render the selected Session body or its empty branch.
|
|
16
77
|
* @param binding - current Session scope binding.
|
|
17
78
|
* @param props - standard Session area render props.
|
|
18
|
-
* @returns the selected Session subtree
|
|
79
|
+
* @returns the selected Session subtree.
|
|
19
80
|
*/
|
|
20
81
|
function renderSessionArea(binding, { empty, children }) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return (0, react_jsx_runtime.jsx)(react.Fragment, { children }, sessionId);
|
|
82
|
+
if (binding.key === void 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: empty?.() ?? null });
|
|
83
|
+
return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
|
|
24
84
|
}
|
|
25
85
|
//#endregion
|
|
26
86
|
//#region lib/types/client/index.js
|
|
@@ -72,20 +132,25 @@ window.__ModuleLoader__.load({
|
|
|
72
132
|
var UiSession = class extends _deepseek_ai_cordis.Service {
|
|
73
133
|
sessions;
|
|
74
134
|
descriptors = [BUILTIN_SOURCE];
|
|
75
|
-
bindings =
|
|
135
|
+
bindings = new WeakMapWithValues();
|
|
76
136
|
absent;
|
|
77
|
-
|
|
78
|
-
currentListeners = /* @__PURE__ */ new Set();
|
|
137
|
+
current;
|
|
79
138
|
pendingDomains = [];
|
|
80
139
|
pendingSnapshot = /* @__PURE__ */ new Map();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
140
|
+
running = /* @__PURE__ */ new Map();
|
|
141
|
+
completionUnread = /* @__PURE__ */ new Set();
|
|
142
|
+
statusSnapshot = /* @__PURE__ */ new Map();
|
|
143
|
+
statusListeners = /* @__PURE__ */ new Set();
|
|
144
|
+
mainRetainId;
|
|
145
|
+
disposeMainRetain = () => {};
|
|
146
|
+
active = true;
|
|
147
|
+
/** Root source combining running, pending-interaction, and completion-reminder facts. */
|
|
148
|
+
sessionStatus = {
|
|
149
|
+
getSnapshot: () => this.statusSnapshot,
|
|
85
150
|
subscribe: (listener) => {
|
|
86
|
-
this.
|
|
151
|
+
this.statusListeners.add(listener);
|
|
87
152
|
return () => {
|
|
88
|
-
this.
|
|
153
|
+
this.statusListeners.delete(listener);
|
|
89
154
|
};
|
|
90
155
|
}
|
|
91
156
|
};
|
|
@@ -98,34 +163,51 @@ window.__ModuleLoader__.load({
|
|
|
98
163
|
constructor(ctx, sessions) {
|
|
99
164
|
super(ctx, "uiSession");
|
|
100
165
|
this.sessions = sessions;
|
|
101
|
-
this.absent = this.materializeAbsent();
|
|
102
|
-
this.
|
|
166
|
+
this.absent = createBindingSource(this.materializeAbsent());
|
|
167
|
+
this.current = createBindingSource(this.absent.value);
|
|
103
168
|
this.adapter = {
|
|
104
|
-
current:
|
|
105
|
-
|
|
106
|
-
subscribe: (listener) => {
|
|
107
|
-
this.currentListeners.add(listener);
|
|
108
|
-
return () => {
|
|
109
|
-
this.currentListeners.delete(listener);
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
resolve: (key) => this.resolve(key),
|
|
169
|
+
current: this.current,
|
|
170
|
+
bindingSource: (target) => this.bindingSource(target),
|
|
114
171
|
renderArea: renderSessionArea
|
|
115
172
|
};
|
|
116
173
|
ctx.effect(() => {
|
|
117
174
|
const disposeList = sessions.list.subscribe(() => {
|
|
118
|
-
this.
|
|
175
|
+
this.publishMain();
|
|
119
176
|
});
|
|
177
|
+
const disposeStatus = sessions.list.subscribe(() => {
|
|
178
|
+
this.reconcileStatus();
|
|
179
|
+
});
|
|
180
|
+
const disposeRemoteStatus = ctx.remote.$on("api-session/status", (sessionId, running) => {
|
|
181
|
+
this.observeRunning(sessionId, running);
|
|
182
|
+
});
|
|
183
|
+
this.publishMain();
|
|
184
|
+
this.reconcileStatus();
|
|
120
185
|
return () => {
|
|
186
|
+
this.active = false;
|
|
121
187
|
disposeList();
|
|
122
|
-
|
|
188
|
+
disposeStatus();
|
|
189
|
+
disposeRemoteStatus();
|
|
190
|
+
this.disposeMainRetain();
|
|
191
|
+
const records = [...this.bindings.values];
|
|
123
192
|
this.bindings.clear();
|
|
124
193
|
for (const record of records) record.release();
|
|
125
194
|
};
|
|
126
195
|
}, "ui-session: Session binding projection");
|
|
127
196
|
}
|
|
128
197
|
/**
|
|
198
|
+
* Resolve a stable renderer source for an owned Session reference or explicit absence.
|
|
199
|
+
* @param reference - active reference supplied by the Provider owner, or absence.
|
|
200
|
+
* @returns the binding source, which falls back to the absent projection when its generation ends.
|
|
201
|
+
* @throws when the reference does not belong to the active Controller generation.
|
|
202
|
+
*/
|
|
203
|
+
bindingSource(reference) {
|
|
204
|
+
if (!this.active) return this.absent;
|
|
205
|
+
if (reference === void 0) return this.absent;
|
|
206
|
+
const owner = reference.binding;
|
|
207
|
+
if (this.sessions.binding(reference.sessionId) !== owner) throw new Error("ui-session: Session reference is not active in this Controller");
|
|
208
|
+
return this.sourceFor(owner);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
129
211
|
* Register one Session-scoped standard-source contribution.
|
|
130
212
|
* @param descriptor - static member roster and per-binding resolver.
|
|
131
213
|
* @returns disposer owned by the caller's Cordis fiber.
|
|
@@ -177,38 +259,42 @@ window.__ModuleLoader__.load({
|
|
|
177
259
|
}
|
|
178
260
|
rebuildBindings() {
|
|
179
261
|
const absent = this.materializeAbsent();
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
this.
|
|
189
|
-
this.bindings = bindings;
|
|
190
|
-
for (const record of previous.values()) record.release();
|
|
191
|
-
this.publishCurrent();
|
|
262
|
+
const updates = [...this.bindings.values].map((record) => ({
|
|
263
|
+
source: record.source,
|
|
264
|
+
value: this.materialize(record.owner)
|
|
265
|
+
}));
|
|
266
|
+
this.absent.value = absent;
|
|
267
|
+
for (const { source, value } of updates) source.value = value;
|
|
268
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.absent.listeners, "[ui-session] absent binding");
|
|
269
|
+
for (const { source } of updates) (0, _deepseek_ai_dsh_client_store.notifySubscribers)(source.listeners, "[ui-session] Session binding");
|
|
270
|
+
this.publishMain();
|
|
192
271
|
}
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
const cached = this.bindings.get(key);
|
|
197
|
-
if (cached?.owner === owner) return cached.value;
|
|
272
|
+
sourceFor(owner) {
|
|
273
|
+
const cached = this.bindings.get(owner);
|
|
274
|
+
if (cached !== void 0) return cached.source;
|
|
198
275
|
const record = this.createMaterializedBinding(owner);
|
|
199
|
-
this.bindings.set(
|
|
200
|
-
|
|
201
|
-
return record.value;
|
|
276
|
+
this.bindings.set(owner, record);
|
|
277
|
+
return record.source;
|
|
202
278
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
279
|
+
publishMain() {
|
|
280
|
+
if (!this.active) return;
|
|
281
|
+
const byId = this.sessions.list.getSnapshot().byId;
|
|
282
|
+
const currentId = this.current.value.key;
|
|
283
|
+
const nextId = currentId !== void 0 && (this.sessions.retainInfo(currentId).getSnapshot().retainedBy.mainView ?? 0) > 0 ? currentId : Object.values(byId).find((candidate) => (candidate.retainedBy.mainView ?? 0) > 0)?.id;
|
|
284
|
+
this.watchMainRetention(nextId);
|
|
285
|
+
const owner = nextId === void 0 ? void 0 : this.sessions.binding(nextId);
|
|
286
|
+
const value = owner === void 0 ? this.absent.value : this.sourceFor(owner).value;
|
|
287
|
+
if (this.current.value === value) return;
|
|
288
|
+
this.current.value = value;
|
|
289
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.current.listeners, "[ui-session] main binding");
|
|
206
290
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.
|
|
211
|
-
|
|
291
|
+
watchMainRetention(sessionId) {
|
|
292
|
+
if (sessionId === this.mainRetainId) return;
|
|
293
|
+
this.disposeMainRetain();
|
|
294
|
+
this.mainRetainId = sessionId;
|
|
295
|
+
this.disposeMainRetain = sessionId === void 0 ? () => {} : this.sessions.retainInfo(sessionId).subscribe(() => {
|
|
296
|
+
this.publishMain();
|
|
297
|
+
});
|
|
212
298
|
}
|
|
213
299
|
publishPendingInteractions() {
|
|
214
300
|
const next = /* @__PURE__ */ new Map();
|
|
@@ -223,20 +309,67 @@ window.__ModuleLoader__.load({
|
|
|
223
309
|
const projected = new Map([...next].map(([sessionId, value]) => [sessionId, value.interaction]));
|
|
224
310
|
if (samePendingInteractions(this.pendingSnapshot, projected)) return;
|
|
225
311
|
this.pendingSnapshot = projected;
|
|
226
|
-
|
|
312
|
+
this.publishStatus();
|
|
313
|
+
}
|
|
314
|
+
observeRunning(sessionId, running) {
|
|
315
|
+
const previous = this.running.get(sessionId);
|
|
316
|
+
const beforeBaseline = this.sessions.list.getSnapshot().phase === "pending";
|
|
317
|
+
this.running.set(sessionId, running);
|
|
318
|
+
if (running) this.completionUnread.delete(sessionId);
|
|
319
|
+
else if ((previous === true || previous === void 0 && beforeBaseline) && !this.isMain(sessionId)) this.completionUnread.add(sessionId);
|
|
320
|
+
this.publishStatus();
|
|
321
|
+
}
|
|
322
|
+
reconcileStatus() {
|
|
323
|
+
const list = this.sessions.list.getSnapshot();
|
|
324
|
+
const present = new Set(Object.keys(list.byId));
|
|
325
|
+
for (const id of present) {
|
|
326
|
+
const row = list.byId[id];
|
|
327
|
+
if (row === void 0) continue;
|
|
328
|
+
const previous = this.running.get(id);
|
|
329
|
+
if (previous === void 0) this.running.set(id, row.running);
|
|
330
|
+
else if (previous !== row.running) this.observeRunning(id, row.running);
|
|
331
|
+
if ((row.retainedBy.mainView ?? 0) > 0) this.completionUnread.delete(id);
|
|
332
|
+
}
|
|
333
|
+
if (list.phase === "ready") for (const id of this.running.keys()) {
|
|
334
|
+
if (present.has(id)) continue;
|
|
335
|
+
this.running.delete(id);
|
|
336
|
+
this.completionUnread.delete(id);
|
|
337
|
+
}
|
|
338
|
+
this.publishStatus();
|
|
339
|
+
}
|
|
340
|
+
isMain(sessionId) {
|
|
341
|
+
return (this.sessions.list.getSnapshot().byId[sessionId]?.retainedBy.mainView ?? 0) > 0;
|
|
342
|
+
}
|
|
343
|
+
publishStatus() {
|
|
344
|
+
const ids = new Set([
|
|
345
|
+
...Object.keys(this.sessions.list.getSnapshot().byId),
|
|
346
|
+
...this.running.keys(),
|
|
347
|
+
...this.pendingSnapshot.keys(),
|
|
348
|
+
...this.completionUnread
|
|
349
|
+
]);
|
|
350
|
+
const next = /* @__PURE__ */ new Map();
|
|
351
|
+
for (const id of ids) next.set(id, {
|
|
352
|
+
running: this.running.get(id),
|
|
353
|
+
pendingInteraction: this.pendingSnapshot.get(id),
|
|
354
|
+
completionUnread: this.completionUnread.has(id)
|
|
355
|
+
});
|
|
356
|
+
if (sameSessionStatus(this.statusSnapshot, next)) return;
|
|
357
|
+
this.statusSnapshot = next;
|
|
358
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.statusListeners, "[ui-session] Session status");
|
|
227
359
|
}
|
|
228
360
|
createMaterializedBinding(owner) {
|
|
229
361
|
const value = this.materialize(owner);
|
|
362
|
+
this.ctx.slots.bindStoreScope(value);
|
|
363
|
+
const source = createBindingSource(value);
|
|
230
364
|
const releaseEffect = owner.ctx.effect(() => () => {
|
|
231
|
-
if (this.bindings.get(owner
|
|
232
|
-
this.
|
|
233
|
-
|
|
234
|
-
this.
|
|
235
|
-
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(this.currentListeners, "[ui-session] current binding");
|
|
365
|
+
if (this.bindings.get(owner) === record) this.bindings.delete(owner);
|
|
366
|
+
source.value = this.absent.value;
|
|
367
|
+
(0, _deepseek_ai_dsh_client_store.notifySubscribers)(source.listeners, "[ui-session] Session binding");
|
|
368
|
+
this.publishMain();
|
|
236
369
|
}, `ui-session: binding ${owner.sessionId}`);
|
|
237
370
|
const record = {
|
|
238
371
|
owner,
|
|
239
|
-
|
|
372
|
+
source,
|
|
240
373
|
release: () => {
|
|
241
374
|
releaseEffect();
|
|
242
375
|
}
|
|
@@ -255,15 +388,13 @@ window.__ModuleLoader__.load({
|
|
|
255
388
|
copyDeclared("keyed hook", keyedHooks, descriptor.keyedHooks, contribution.keyedHooks, finalProps);
|
|
256
389
|
copyDeclared("prop", props, descriptor.props, contribution.props, finalProps);
|
|
257
390
|
}
|
|
258
|
-
|
|
391
|
+
return {
|
|
259
392
|
key: binding.sessionId,
|
|
260
393
|
ctx: binding.ctx,
|
|
261
394
|
hooks,
|
|
262
395
|
keyedHooks,
|
|
263
396
|
props
|
|
264
397
|
};
|
|
265
|
-
this.ctx.slots.bindStoreScope(value);
|
|
266
|
-
return value;
|
|
267
398
|
}
|
|
268
399
|
materializeAbsent() {
|
|
269
400
|
const hooks = {};
|
|
@@ -283,6 +414,20 @@ window.__ModuleLoader__.load({
|
|
|
283
414
|
};
|
|
284
415
|
}
|
|
285
416
|
};
|
|
417
|
+
function createBindingSource(value) {
|
|
418
|
+
const source = {
|
|
419
|
+
value,
|
|
420
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
421
|
+
getSnapshot: () => source.value,
|
|
422
|
+
subscribe: (listener) => {
|
|
423
|
+
source.listeners.add(listener);
|
|
424
|
+
return () => {
|
|
425
|
+
source.listeners.delete(listener);
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
return source;
|
|
430
|
+
}
|
|
286
431
|
function validateContribution(descriptor, contribution) {
|
|
287
432
|
rejectUndeclared("hook", descriptor.hooks, contribution.hooks);
|
|
288
433
|
rejectUndeclared("keyed hook", descriptor.keyedHooks, contribution.keyedHooks);
|
|
@@ -311,19 +456,34 @@ window.__ModuleLoader__.load({
|
|
|
311
456
|
finalProps.add(propName);
|
|
312
457
|
}
|
|
313
458
|
/** Required Controller and renderer services. */
|
|
314
|
-
const inject = [
|
|
459
|
+
const inject = [
|
|
460
|
+
"sessions",
|
|
461
|
+
"slots",
|
|
462
|
+
"remote"
|
|
463
|
+
];
|
|
315
464
|
/**
|
|
316
465
|
* Install the Session root source and scoped adapter.
|
|
317
466
|
* @param ctx - Client Cordis context.
|
|
318
467
|
*/
|
|
319
468
|
function apply(ctx) {
|
|
320
469
|
const service = new UiSession(ctx, ctx.sessions);
|
|
321
|
-
ctx.slots.provideRoot({
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
470
|
+
ctx.slots.provideRoot({
|
|
471
|
+
hooks: {
|
|
472
|
+
sessions: ctx.sessions.list,
|
|
473
|
+
sessionStatus: service.sessionStatus
|
|
474
|
+
},
|
|
475
|
+
keyedHooks: { sessionRetainInfo: (key) => ctx.sessions.retainInfo(key) }
|
|
476
|
+
});
|
|
325
477
|
ctx.slots.installScope("session", service.adapter);
|
|
326
478
|
}
|
|
479
|
+
function sameSessionStatus(left, right) {
|
|
480
|
+
if (left.size !== right.size) return false;
|
|
481
|
+
for (const [id, status] of left) {
|
|
482
|
+
const candidate = right.get(id);
|
|
483
|
+
if (candidate === void 0 || candidate.running !== status.running || candidate.pendingInteraction !== status.pendingInteraction || candidate.completionUnread !== status.completionUnread) return false;
|
|
484
|
+
}
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
327
487
|
function samePendingInteractions(left, right) {
|
|
328
488
|
if (left.size !== right.size) return false;
|
|
329
489
|
for (const [sessionId, interaction] of left) if (right.get(sessionId) !== interaction) return false;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Session Controller adapter for React selector hooks and Slot scope data. */
|
|
2
2
|
import { Service, type Context } from '@deepseek-ai/cordis';
|
|
3
|
-
import type { ISessions, SessionBinding, SessionListState, SessionSnapshot, UseProjection } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
3
|
+
import type { ISessions, SessionBinding, SessionListState, SessionReference, SessionRetainInfo, SessionSnapshot, UseProjection } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
4
4
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
5
|
-
import type { HostObservable, KeyedStandardSource, MaybeSnapshotSelectorHook, SlotScopeAdapter, SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
|
|
5
|
+
import type { HostObservable, KeyedStandardSource, MaybeSnapshotSelectorHook, SlotScopeAdapter, SnapshotSelectorHook, StandardSourceBinding } from '@deepseek-ai/dsh-client-ui-slots';
|
|
6
6
|
/** Selector hook over the Session Controller list and current selection. */
|
|
7
7
|
export type UseSessions = SnapshotSelectorHook<SessionListState>;
|
|
8
8
|
/** Selector hook over one Session's lifecycle and control state. */
|
|
@@ -18,25 +18,61 @@ export interface SessionPendingInteractionBase {
|
|
|
18
18
|
/** Session whose UI can answer this interaction. */
|
|
19
19
|
readonly sessionId: SessionId;
|
|
20
20
|
}
|
|
21
|
-
/** Declaration-merged
|
|
21
|
+
/** Declaration-merged map of domain keys to their pending-interaction values. */
|
|
22
22
|
export interface SessionPendingInteractionMap {
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
24
|
+
/** Union of every pending-interaction value contributed by the assembled Client. */
|
|
25
25
|
export type SessionPendingInteraction = [
|
|
26
26
|
keyof SessionPendingInteractionMap
|
|
27
27
|
] extends [never] ? SessionPendingInteractionBase : SessionPendingInteractionMap[keyof SessionPendingInteractionMap];
|
|
28
|
-
/**
|
|
29
|
-
export
|
|
30
|
-
/**
|
|
31
|
-
|
|
28
|
+
/** Independent UI status facts for one Session identity. */
|
|
29
|
+
export interface SessionStatus {
|
|
30
|
+
/** Latest known running state; absent until a baseline or event establishes it. */
|
|
31
|
+
readonly running: boolean | undefined;
|
|
32
|
+
/** Highest-precedence domain request currently awaiting user interaction. */
|
|
33
|
+
readonly pendingInteraction: SessionPendingInteraction | undefined;
|
|
34
|
+
/** Whether an observed stop outside the main view still needs acknowledgement. */
|
|
35
|
+
readonly completionUnread: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Current UI status indexed by Session identity. */
|
|
38
|
+
export type SessionStatusSnapshot = ReadonlyMap<SessionId, SessionStatus>;
|
|
39
|
+
/** Selector hook over the unified Session UI status snapshot. */
|
|
40
|
+
export type UseSessionStatus = SnapshotSelectorHook<SessionStatusSnapshot>;
|
|
41
|
+
/** Selector hook for explicit or surrounding-Provider Session reference counts. */
|
|
42
|
+
export interface UseSessionRetainInfo {
|
|
43
|
+
/**
|
|
44
|
+
* Read the complete retain information for an explicit Session identity.
|
|
45
|
+
* @param sessionId - Session identity to inspect without retaining it.
|
|
46
|
+
* @returns current local reference counts, or absence while the source is unavailable.
|
|
47
|
+
*/
|
|
48
|
+
(sessionId: SessionId): SessionRetainInfo | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Select from the retain information for an explicit Session identity.
|
|
51
|
+
* @param sessionId - Session identity to inspect without retaining it.
|
|
52
|
+
* @param selector - projection over the current value.
|
|
53
|
+
* @param equal - optional selected-value equality.
|
|
54
|
+
* @returns selected value.
|
|
55
|
+
*/
|
|
56
|
+
<Selected>(sessionId: SessionId, selector: (value: SessionRetainInfo | undefined) => Selected, equal?: (left: Selected, right: Selected) => boolean): Selected;
|
|
57
|
+
/**
|
|
58
|
+
* Select from the surrounding Provider's Session retain information.
|
|
59
|
+
* @param selector - projection receiving absence outside a Session binding.
|
|
60
|
+
* @param equal - optional selected-value equality.
|
|
61
|
+
* @returns selected value.
|
|
62
|
+
*/
|
|
63
|
+
<Selected>(selector: (value: SessionRetainInfo | undefined) => Selected, equal?: (left: Selected, right: Selected) => boolean): Selected;
|
|
64
|
+
}
|
|
32
65
|
/** Publish one pending interaction and define how plugin teardown delegates it. */
|
|
33
66
|
export type PendingInteractionPublisher<T extends SessionPendingInteractionBase> = (interaction: T, delegate: () => Promise<void>) => () => void;
|
|
34
67
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
68
|
+
interface SlotScopeTargetMap {
|
|
69
|
+
session: SessionReference;
|
|
70
|
+
}
|
|
35
71
|
interface GlobalStandardProps {
|
|
36
72
|
/** Session list and current selection. */
|
|
37
73
|
useSessions: UseSessions;
|
|
38
|
-
|
|
39
|
-
|
|
74
|
+
useSessionStatus: UseSessionStatus;
|
|
75
|
+
useSessionRetainInfo: UseSessionRetainInfo;
|
|
40
76
|
}
|
|
41
77
|
interface SessionStandardProps {
|
|
42
78
|
/** Current Session lifecycle and control state. */
|
|
@@ -55,6 +91,11 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
55
91
|
useProjection: UseProjection;
|
|
56
92
|
}
|
|
57
93
|
}
|
|
94
|
+
declare module '@deepseek-ai/dsh-api-session-controller/client' {
|
|
95
|
+
interface SessionReferenceSourceMap {
|
|
96
|
+
mainView: unknown;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
58
99
|
declare module '@deepseek-ai/cordis' {
|
|
59
100
|
interface Context {
|
|
60
101
|
/** Session Controller adapter and session-scoped source registry. */
|
|
@@ -85,15 +126,20 @@ export interface SessionSourceDescriptor<Hooks extends SessionSourceRoster = Ses
|
|
|
85
126
|
export declare class UiSession extends Service {
|
|
86
127
|
private readonly sessions;
|
|
87
128
|
private readonly descriptors;
|
|
88
|
-
private bindings;
|
|
89
|
-
private absent;
|
|
90
|
-
private
|
|
91
|
-
private readonly currentListeners;
|
|
129
|
+
private readonly bindings;
|
|
130
|
+
private readonly absent;
|
|
131
|
+
private readonly current;
|
|
92
132
|
private readonly pendingDomains;
|
|
93
133
|
private pendingSnapshot;
|
|
94
|
-
private readonly
|
|
95
|
-
|
|
96
|
-
|
|
134
|
+
private readonly running;
|
|
135
|
+
private readonly completionUnread;
|
|
136
|
+
private statusSnapshot;
|
|
137
|
+
private readonly statusListeners;
|
|
138
|
+
private mainRetainId;
|
|
139
|
+
private disposeMainRetain;
|
|
140
|
+
private active;
|
|
141
|
+
/** Root source combining running, pending-interaction, and completion-reminder facts. */
|
|
142
|
+
readonly sessionStatus: HostObservable<SessionStatusSnapshot>;
|
|
97
143
|
/** Renderer-facing adapter for `session` and `session-maybe` scopes. */
|
|
98
144
|
readonly adapter: SlotScopeAdapter;
|
|
99
145
|
/**
|
|
@@ -101,6 +147,13 @@ export declare class UiSession extends Service {
|
|
|
101
147
|
* @param sessions - Controller-owned Session object layer.
|
|
102
148
|
*/
|
|
103
149
|
constructor(ctx: Context, sessions: ISessions);
|
|
150
|
+
/**
|
|
151
|
+
* Resolve a stable renderer source for an owned Session reference or explicit absence.
|
|
152
|
+
* @param reference - active reference supplied by the Provider owner, or absence.
|
|
153
|
+
* @returns the binding source, which falls back to the absent projection when its generation ends.
|
|
154
|
+
* @throws when the reference does not belong to the active Controller generation.
|
|
155
|
+
*/
|
|
156
|
+
bindingSource(reference: SessionReference | undefined): HostObservable<StandardSourceBinding>;
|
|
104
157
|
/**
|
|
105
158
|
* Register one Session-scoped standard-source contribution.
|
|
106
159
|
* @param descriptor - static member roster and per-binding resolver.
|
|
@@ -116,10 +169,14 @@ export declare class UiSession extends Service {
|
|
|
116
169
|
*/
|
|
117
170
|
registerPendingInteraction<T extends SessionPendingInteractionBase>(precedence: (interaction: T) => number): PendingInteractionPublisher<T>;
|
|
118
171
|
private rebuildBindings;
|
|
119
|
-
private
|
|
120
|
-
private
|
|
121
|
-
private
|
|
172
|
+
private sourceFor;
|
|
173
|
+
private publishMain;
|
|
174
|
+
private watchMainRetention;
|
|
122
175
|
private publishPendingInteractions;
|
|
176
|
+
private observeRunning;
|
|
177
|
+
private reconcileStatus;
|
|
178
|
+
private isMain;
|
|
179
|
+
private publishStatus;
|
|
123
180
|
private createMaterializedBinding;
|
|
124
181
|
private materialize;
|
|
125
182
|
private materializeAbsent;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Session-owned rendering semantics for the standard SessionProvider seat. */
|
|
2
|
-
import {
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
3
|
import type { SessionAreaProps, StandardSourceBinding } from '@deepseek-ai/dsh-client-ui-slots';
|
|
4
4
|
/**
|
|
5
5
|
* Render the selected Session body or its empty branch.
|
|
6
6
|
* @param binding - current Session scope binding.
|
|
7
7
|
* @param props - standard Session area render props.
|
|
8
|
-
* @returns the selected Session subtree
|
|
8
|
+
* @returns the selected Session subtree.
|
|
9
9
|
*/
|
|
10
10
|
export declare function renderSessionArea(binding: StandardSourceBinding, { empty, children }: SessionAreaProps): ReactNode;
|
|
11
11
|
//# sourceMappingURL=session-provider.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-session",
|
|
3
3
|
"description": "Session Controller adapter for React and session-scoped slots",
|
|
4
|
-
"version": "0.1.6-alpha.
|
|
4
|
+
"version": "0.1.6-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"dsh": {
|
|
29
29
|
"client": {
|
|
30
30
|
"inject": [
|
|
31
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
31
32
|
"@deepseek-ai/dsh-api-session-controller",
|
|
32
33
|
"@deepseek-ai/dsh-client-ui-renderer"
|
|
33
34
|
],
|
|
@@ -42,11 +43,13 @@
|
|
|
42
43
|
"@types/react": "~18.3.1",
|
|
43
44
|
"react": "^18.2.0",
|
|
44
45
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
45
|
-
"@deepseek-ai/dsh-api-
|
|
46
|
-
"@deepseek-ai/dsh-client-store": "^0.1.6-alpha.
|
|
47
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.
|
|
48
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.
|
|
49
|
-
"@deepseek-ai/dsh-session": "^0.1.6-alpha.
|
|
46
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-client-store": "^0.1.6-alpha.2",
|
|
48
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-util-values": "^0.1.6-alpha.2"
|
|
50
53
|
},
|
|
51
54
|
"files": [
|
|
52
55
|
"lib/index.js",
|