@runtypelabs/persona 4.12.0 → 4.13.0
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/dist/codegen.cjs +1 -1
- package/dist/codegen.js +1 -1
- package/dist/index.cjs +45 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +34 -34
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +45 -45
- package/dist/index.js.map +1 -1
- package/dist/theme-editor-preview.cjs +44 -44
- package/dist/theme-editor-preview.js +33 -33
- package/package.json +1 -1
- package/src/ui.scroll-additive.test.ts +365 -0
- package/src/ui.scroll.test.ts +19 -6
- package/src/ui.ts +115 -28
- package/src/utils/auto-follow.test.ts +38 -0
- package/src/utils/auto-follow.ts +35 -0
|
@@ -6,10 +6,12 @@ import {
|
|
|
6
6
|
createFollowStateController,
|
|
7
7
|
getScrollBottomOffset,
|
|
8
8
|
hasSelectionWithin,
|
|
9
|
+
isAttentionWorthyMessage,
|
|
9
10
|
isElementNearBottom,
|
|
10
11
|
resolveFollowStateFromScroll,
|
|
11
12
|
resolveFollowStateFromWheel
|
|
12
13
|
} from "./auto-follow";
|
|
14
|
+
import type { AgentWidgetMessage } from "../types";
|
|
13
15
|
|
|
14
16
|
describe("auto-follow utilities", () => {
|
|
15
17
|
it("tracks pause and resume state", () => {
|
|
@@ -199,3 +201,39 @@ describe("auto-follow utilities", () => {
|
|
|
199
201
|
).toBe("resume");
|
|
200
202
|
});
|
|
201
203
|
});
|
|
204
|
+
|
|
205
|
+
describe("isAttentionWorthyMessage", () => {
|
|
206
|
+
const msg = (over: Partial<AgentWidgetMessage>): AgentWidgetMessage => ({
|
|
207
|
+
id: "m",
|
|
208
|
+
role: "assistant",
|
|
209
|
+
content: "",
|
|
210
|
+
createdAt: "2026-07-28T00:00:00.000Z",
|
|
211
|
+
...over,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("treats prose, artifacts/components, images and approvals as output", () => {
|
|
215
|
+
expect(isAttentionWorthyMessage(msg({ content: "Here is the answer." }))).toBe(true);
|
|
216
|
+
// Artifact / component blocks carry their payload in rawContent with an
|
|
217
|
+
// empty `content` — the case a text-only test silently drops.
|
|
218
|
+
expect(
|
|
219
|
+
isAttentionWorthyMessage(msg({ rawContent: '{"component":"Chart","props":{}}' }))
|
|
220
|
+
).toBe(true);
|
|
221
|
+
expect(
|
|
222
|
+
isAttentionWorthyMessage(
|
|
223
|
+
msg({ contentParts: [{ type: "image", image: "data:image/png;base64,x" }] as never })
|
|
224
|
+
)
|
|
225
|
+
).toBe(true);
|
|
226
|
+
// Blocked on the reader deciding: must never sit off-screen.
|
|
227
|
+
expect(isAttentionWorthyMessage(msg({ variant: "approval" }))).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("treats tool calls and reasoning as progress chrome", () => {
|
|
231
|
+
expect(isAttentionWorthyMessage(msg({ variant: "tool", content: "searching" }))).toBe(false);
|
|
232
|
+
expect(isAttentionWorthyMessage(msg({ variant: "reasoning", content: "thinking" }))).toBe(false);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("skips the empty streaming placeholder and non-assistant roles", () => {
|
|
236
|
+
expect(isAttentionWorthyMessage(msg({ content: " " }))).toBe(false);
|
|
237
|
+
expect(isAttentionWorthyMessage(msg({ role: "user", content: "hi" }))).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
});
|
package/src/utils/auto-follow.ts
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
import type { AgentWidgetMessage } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Is this message *output a human came for*, as opposed to progress chrome?
|
|
5
|
+
*
|
|
6
|
+
* This is the classifier behind anchor-top's "first unread" positioning: the
|
|
7
|
+
* transcript anchors to the first attention-worthy block of a response and
|
|
8
|
+
* leaves everything else to scroll past underneath. Two categories:
|
|
9
|
+
*
|
|
10
|
+
* - **Chrome** — tool calls and reasoning. Rendered so the reader *can* audit
|
|
11
|
+
* the work, never positioned to. A response that opens with six tool calls
|
|
12
|
+
* should not park the reader on the sixth one.
|
|
13
|
+
* - **Attention-worthy** — anything that carries the answer: prose, an
|
|
14
|
+
* artifact or component block (charts, forms, documents — these carry their
|
|
15
|
+
* payload in `rawContent` with an EMPTY `content`, so testing text alone
|
|
16
|
+
* silently drops them), multi-modal parts (images), and approval requests
|
|
17
|
+
* (which are the strongest case of all: the turn is blocked on the reader
|
|
18
|
+
* deciding, so it must never sit off-screen).
|
|
19
|
+
*
|
|
20
|
+
* The empty streaming placeholder — an assistant bubble created at
|
|
21
|
+
* `text_start` before its first token — is deliberately NOT attention-worthy,
|
|
22
|
+
* so the anchor waits for content rather than jumping to a blank bubble.
|
|
23
|
+
*/
|
|
24
|
+
export function isAttentionWorthyMessage(message: AgentWidgetMessage): boolean {
|
|
25
|
+
if (message.role !== "assistant") return false;
|
|
26
|
+
if (message.variant === "tool" || message.variant === "reasoning") return false;
|
|
27
|
+
// Blocked on the reader: attention-worthy even with no text of its own.
|
|
28
|
+
if (message.variant === "approval") return true;
|
|
29
|
+
return Boolean(
|
|
30
|
+
message.content?.trim() ||
|
|
31
|
+
message.rawContent?.trim() ||
|
|
32
|
+
message.contentParts?.length
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
1
36
|
export type FollowStateAction = "none" | "pause" | "resume";
|
|
2
37
|
|
|
3
38
|
export type FollowStateController = {
|