@theoremjs/web 0.3.4-next.377.g4dbec64 → 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 +5 -4
- package/client/chat.js +9 -3
- 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. */
|
|
@@ -95,8 +95,9 @@ export type ChatUpdate<T> = T | ((prev: T) => T);
|
|
|
95
95
|
export type ChatController = {
|
|
96
96
|
getState(): ChatState;
|
|
97
97
|
/**
|
|
98
|
-
* Called after each change, once per batch of changes. Returns the unsubscribe.
|
|
99
|
-
*
|
|
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.
|
|
100
101
|
*/
|
|
101
102
|
subscribe(listener: () => void): () => void;
|
|
102
103
|
/** The latest options. `conversation`, `conversationView` and `initialText` are read only at creation. */
|
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,8 +126,10 @@ class Chat {
|
|
|
124
126
|
getState = () => this.#state;
|
|
125
127
|
subscribe = (listener) => {
|
|
126
128
|
this.#listeners.add(listener);
|
|
127
|
-
if (this.#
|
|
129
|
+
if (!this.#live) {
|
|
130
|
+
this.#live = true;
|
|
128
131
|
this.#settle();
|
|
132
|
+
}
|
|
129
133
|
return () => this.#listeners.delete(listener);
|
|
130
134
|
};
|
|
131
135
|
update = (options) => {
|
|
@@ -381,8 +385,7 @@ class Chat {
|
|
|
381
385
|
return;
|
|
382
386
|
const ref = this.#conversation;
|
|
383
387
|
const shown = this.#options.conversationView;
|
|
384
|
-
|
|
385
|
-
if (ref !== undefined && !shown && this.#listeners.size === 0)
|
|
388
|
+
if (ref !== undefined && !shown && !this.#live)
|
|
386
389
|
return;
|
|
387
390
|
this.#resumed = true;
|
|
388
391
|
if (ref === undefined || !keepsConversation(iface))
|
|
@@ -404,6 +407,8 @@ class Chat {
|
|
|
404
407
|
* invariant: A restored page gate cannot authorize callback execution. Only active dispatch grants do.
|
|
405
408
|
*/
|
|
406
409
|
#answerPageGate() {
|
|
410
|
+
if (!this.#live)
|
|
411
|
+
return;
|
|
407
412
|
const gated = this.#state.phase === 'gated' ? this.#state.session.gatedTool : null;
|
|
408
413
|
if (gated?.gateKind !== 'page' || !gated.operationId)
|
|
409
414
|
return;
|
|
@@ -618,6 +623,7 @@ class Chat {
|
|
|
618
623
|
const { iface } = this.#options;
|
|
619
624
|
if (!iface || this.#busy)
|
|
620
625
|
return;
|
|
626
|
+
this.#live = true;
|
|
621
627
|
const { message } = options;
|
|
622
628
|
const priorDelivery = this.#delivery;
|
|
623
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
|
}
|