@theoremjs/web 0.3.4-next.375.ga2d1878 → 0.3.4-next.379.gf91c8b4
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.md +1 -1
- package/client/chat.d.ts +7 -3
- package/client/chat.js +13 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -176,7 +176,7 @@ await chat.submit();
|
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
- `getState()` returns a `ChatState`. It is a new object after each change, and the same object between changes. So you can compare it by reference, as React's `useSyncExternalStore` does.
|
|
179
|
-
- `subscribe(listener)` calls the listener once for each group of changes, after the code that made them has run. It returns the function that unsubscribes.
|
|
179
|
+
- `subscribe(listener)` calls the listener once for each group of changes, after the code that made them has run. It returns the function that unsubscribes. Until the first subscribe or send, the chat asks nothing of the host. Then it reads a resumed conversation that came without its `conversationView`, and answers a restored page gate as unanswered.
|
|
180
180
|
- `update(options)` gives the controller your latest options, such as a new `context`, `slots`, or `pageTools`. Each turn reads the options as they are when it is sent. `conversation`, `conversationView`, and `initialText` are read only when the controller is created.
|
|
181
181
|
- Each method is bound to the controller. Pass it as a callback as it is.
|
|
182
182
|
|
package/client/chat.d.ts
CHANGED
|
@@ -28,8 +28,8 @@ export type ChatOptions = {
|
|
|
28
28
|
onConversationChange?: (ref: string) => void;
|
|
29
29
|
/**
|
|
30
30
|
* The value chosen for each of the profile's `inputs.slots`. Sent with every turn. A slot you
|
|
31
|
-
* name here is fixed to that value; one you leave out is
|
|
32
|
-
* its first allowed value.
|
|
31
|
+
* name here is fixed to that value; one you leave out is the visitor's to pick (the chat's
|
|
32
|
+
* `pickSlot`), starting at its first allowed value.
|
|
33
33
|
*/
|
|
34
34
|
slots?: Record<string, string>;
|
|
35
35
|
/** Told each time the visitor picks a slot, with every slot's value. */
|
|
@@ -94,7 +94,11 @@ export type ChatUpdate<T> = T | ((prev: T) => T);
|
|
|
94
94
|
*/
|
|
95
95
|
export type ChatController = {
|
|
96
96
|
getState(): ChatState;
|
|
97
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Called after each change, once per batch of changes. Returns the unsubscribe. Until the first
|
|
99
|
+
* subscribe or send, the chat asks nothing of the host: a conversation resumed without its
|
|
100
|
+
* `conversationView` is read then, and a restored page gate answered then.
|
|
101
|
+
*/
|
|
98
102
|
subscribe(listener: () => void): () => void;
|
|
99
103
|
/** The latest options. `conversation`, `conversationView` and `initialText` are read only at creation. */
|
|
100
104
|
update(options: ChatOptions): void;
|
package/client/chat.js
CHANGED
|
@@ -91,6 +91,8 @@ class Chat {
|
|
|
91
91
|
#delivery = null;
|
|
92
92
|
#picked = {};
|
|
93
93
|
#resumed = false;
|
|
94
|
+
// why: A chat no one uses yet (one React's strict mode builds and drops) asks nothing of the host.
|
|
95
|
+
#live = false;
|
|
94
96
|
#defaultsFrom;
|
|
95
97
|
#answeredPage = null;
|
|
96
98
|
#pageToolsWarned;
|
|
@@ -124,6 +126,10 @@ class Chat {
|
|
|
124
126
|
getState = () => this.#state;
|
|
125
127
|
subscribe = (listener) => {
|
|
126
128
|
this.#listeners.add(listener);
|
|
129
|
+
if (!this.#live) {
|
|
130
|
+
this.#live = true;
|
|
131
|
+
this.#settle();
|
|
132
|
+
}
|
|
127
133
|
return () => this.#listeners.delete(listener);
|
|
128
134
|
};
|
|
129
135
|
update = (options) => {
|
|
@@ -377,11 +383,13 @@ class Chat {
|
|
|
377
383
|
#resume(iface) {
|
|
378
384
|
if (this.#resumed)
|
|
379
385
|
return;
|
|
380
|
-
this.#resumed = true;
|
|
381
386
|
const ref = this.#conversation;
|
|
387
|
+
const shown = this.#options.conversationView;
|
|
388
|
+
if (ref !== undefined && !shown && !this.#live)
|
|
389
|
+
return;
|
|
390
|
+
this.#resumed = true;
|
|
382
391
|
if (ref === undefined || !keepsConversation(iface))
|
|
383
392
|
return;
|
|
384
|
-
const shown = this.#options.conversationView;
|
|
385
393
|
if (shown) {
|
|
386
394
|
this.#draw(shown, this.#state.session, []);
|
|
387
395
|
return;
|
|
@@ -399,6 +407,8 @@ class Chat {
|
|
|
399
407
|
* invariant: A restored page gate cannot authorize callback execution. Only active dispatch grants do.
|
|
400
408
|
*/
|
|
401
409
|
#answerPageGate() {
|
|
410
|
+
if (!this.#live)
|
|
411
|
+
return;
|
|
402
412
|
const gated = this.#state.phase === 'gated' ? this.#state.session.gatedTool : null;
|
|
403
413
|
if (gated?.gateKind !== 'page' || !gated.operationId)
|
|
404
414
|
return;
|
|
@@ -613,6 +623,7 @@ class Chat {
|
|
|
613
623
|
const { iface } = this.#options;
|
|
614
624
|
if (!iface || this.#busy)
|
|
615
625
|
return;
|
|
626
|
+
this.#live = true;
|
|
616
627
|
const { message } = options;
|
|
617
628
|
const priorDelivery = this.#delivery;
|
|
618
629
|
const abort = options.turn ? new AbortController() : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theoremjs/web",
|
|
3
|
-
"version": "0.3.4-next.
|
|
3
|
+
"version": "0.3.4-next.379.gf91c8b4",
|
|
4
4
|
"description": "Theorem on the web platform, with no UI framework: Web-standard server handlers that serve a profile, and the browser client that talks to them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,6 @@
|
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"zod": "^4.6.5",
|
|
28
|
-
"@theoremjs/agents": "^0.3.4-next.
|
|
28
|
+
"@theoremjs/agents": "^0.3.4-next.379.gf91c8b4"
|
|
29
29
|
}
|
|
30
30
|
}
|