@nanisoft/prism-ui 0.16.0 → 1.0.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/.tsbuildinfo +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts +120 -190
- package/dist/blocks/activity-feed-01/activity-feed-01.d.ts.map +1 -1
- package/dist/blocks/activity-feed-01/activity-feed-01.js +107 -97
- package/dist/blocks/activity-feed-01/index.d.ts +1 -1
- package/dist/blocks/activity-feed-01/index.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.d.ts +86 -53
- package/dist/blocks/auth-form-01/auth-form.d.ts.map +1 -1
- package/dist/blocks/auth-form-01/auth-form.js +95 -31
- package/dist/blocks/auth-form-01/index.d.ts +1 -1
- package/dist/blocks/auth-form-01/index.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.d.ts +28 -0
- package/dist/blocks/calendar-01/calendar.d.ts.map +1 -1
- package/dist/blocks/calendar-01/calendar.js +15 -1
- package/dist/blocks/card-index-01/card-index.d.ts +225 -0
- package/dist/blocks/card-index-01/card-index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/card-index.js +154 -0
- package/dist/blocks/card-index-01/index.d.ts +3 -0
- package/dist/blocks/card-index-01/index.d.ts.map +1 -0
- package/dist/blocks/card-index-01/index.js +1 -0
- package/dist/blocks/chart-card-01/chart-card-01.d.ts +23 -21
- package/dist/blocks/chart-card-01/chart-card-01.d.ts.map +1 -1
- package/dist/blocks/chart-card-01/chart-card-01.js +46 -1
- package/dist/blocks/chart-card-01/index.d.ts +1 -1
- package/dist/blocks/chart-card-01/index.d.ts.map +1 -1
- package/dist/blocks/chart-group-01/chart-group-01.d.ts +3 -3
- package/dist/blocks/chart-group-01/chart-group-01.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.d.ts +30 -82
- package/dist/blocks/contact-01/contact.d.ts.map +1 -1
- package/dist/blocks/contact-01/contact.js +59 -30
- package/dist/blocks/contact-01/index.d.ts +1 -1
- package/dist/blocks/contact-01/index.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.d.ts +26 -71
- package/dist/blocks/dashboard-01/dashboard-01.d.ts.map +1 -1
- package/dist/blocks/dashboard-01/dashboard-01.js +34 -23
- package/dist/blocks/dashboard-01/index.d.ts +1 -1
- package/dist/blocks/dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.d.ts +276 -59
- package/dist/blocks/data-table-01/data-table.d.ts.map +1 -1
- package/dist/blocks/data-table-01/data-table.js +217 -35
- package/dist/blocks/data-table-01/index.d.ts +1 -1
- package/dist/blocks/data-table-01/index.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.d.ts +23 -2
- package/dist/blocks/directory-01/directory-01.d.ts.map +1 -1
- package/dist/blocks/directory-01/directory-01.js +3 -3
- package/dist/blocks/empty-state-01/empty-state.d.ts +60 -21
- package/dist/blocks/empty-state-01/empty-state.d.ts.map +1 -1
- package/dist/blocks/empty-state-01/empty-state.js +52 -17
- package/dist/blocks/index-detail-01/index-detail.d.ts +85 -0
- package/dist/blocks/index-detail-01/index-detail.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index-detail.js +58 -0
- package/dist/blocks/index-detail-01/index.d.ts +3 -0
- package/dist/blocks/index-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/index-detail-01/index.js +1 -0
- package/dist/blocks/index.d.ts +20 -8
- package/dist/blocks/index.d.ts.map +1 -1
- package/dist/blocks/index.js +7 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts +18 -0
- package/dist/blocks/issue-detail-01/issue-detail.d.ts.map +1 -1
- package/dist/blocks/issue-detail-01/issue-detail.js +18 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts +3 -0
- package/dist/blocks/nothing-chosen-01/index.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/index.js +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts +119 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.d.ts.map +1 -0
- package/dist/blocks/nothing-chosen-01/nothing-chosen.js +79 -0
- package/dist/blocks/project-dashboard-01/index.d.ts +1 -1
- package/dist/blocks/project-dashboard-01/index.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts +21 -27
- package/dist/blocks/project-dashboard-01/project-dashboard.d.ts.map +1 -1
- package/dist/blocks/project-dashboard-01/project-dashboard.js +45 -5
- package/dist/blocks/provisioning-01/index.d.ts +1 -1
- package/dist/blocks/provisioning-01/index.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.d.ts +9 -97
- package/dist/blocks/provisioning-01/provisioning.d.ts.map +1 -1
- package/dist/blocks/provisioning-01/provisioning.js +26 -13
- package/dist/blocks/record-detail-01/index.d.ts +3 -0
- package/dist/blocks/record-detail-01/index.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/index.js +1 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts +239 -0
- package/dist/blocks/record-detail-01/record-detail.d.ts.map +1 -0
- package/dist/blocks/record-detail-01/record-detail.js +150 -0
- package/dist/blocks/record-form-01/index.d.ts +3 -0
- package/dist/blocks/record-form-01/index.d.ts.map +1 -0
- package/dist/blocks/record-form-01/index.js +1 -0
- package/dist/blocks/record-form-01/record-form.d.ts +148 -0
- package/dist/blocks/record-form-01/record-form.d.ts.map +1 -0
- package/dist/blocks/record-form-01/record-form.js +107 -0
- package/dist/blocks/record-grid-01/index.d.ts +3 -0
- package/dist/blocks/record-grid-01/index.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/index.js +1 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts +145 -0
- package/dist/blocks/record-grid-01/record-grid.d.ts.map +1 -0
- package/dist/blocks/record-grid-01/record-grid.js +114 -0
- package/dist/blocks/record-wizard-01/index.d.ts +3 -0
- package/dist/blocks/record-wizard-01/index.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/index.js +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts +152 -0
- package/dist/blocks/record-wizard-01/record-wizard.d.ts.map +1 -0
- package/dist/blocks/record-wizard-01/record-wizard.js +144 -0
- package/dist/blocks/settings-members-01/settings-members.d.ts +2 -1
- package/dist/blocks/settings-members-01/settings-members.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts +1 -1
- package/dist/blocks/settings-panel-01/index.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.d.ts +165 -67
- package/dist/blocks/settings-panel-01/settings-panel.d.ts.map +1 -1
- package/dist/blocks/settings-panel-01/settings-panel.js +92 -33
- package/dist/blocks/signup-01/index.d.ts +1 -1
- package/dist/blocks/signup-01/index.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.d.ts +10 -84
- package/dist/blocks/signup-01/signup-01.d.ts.map +1 -1
- package/dist/blocks/signup-01/signup-01.js +41 -23
- package/dist/blocks/stats-01/index.d.ts +5 -5
- package/dist/blocks/stats-01/index.d.ts.map +1 -1
- package/dist/blocks/stats-01/index.js +4 -4
- package/dist/blocks/stats-01/stats.d.ts +49 -14
- package/dist/blocks/stats-01/stats.d.ts.map +1 -1
- package/dist/blocks/stats-01/stats.js +65 -15
- package/dist/blocks/trend-01/index.d.ts +1 -1
- package/dist/blocks/trend-01/index.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.d.ts +26 -73
- package/dist/blocks/trend-01/trend.d.ts.map +1 -1
- package/dist/blocks/trend-01/trend.js +47 -30
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +93 -3
- package/dist/components/ui/metric.d.ts +4 -2
- package/dist/components/ui/metric.d.ts.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/lib/field-render.d.ts +107 -0
- package/dist/lib/field-render.d.ts.map +1 -0
- package/dist/lib/field-render.js +251 -0
- package/dist/lib/spec.d.ts +609 -0
- package/dist/lib/spec.d.ts.map +1 -0
- package/dist/lib/spec.js +1 -0
- package/dist/live/index.d.ts +2 -0
- package/dist/live/index.d.ts.map +1 -1
- package/dist/live/index.js +6 -0
- package/dist/live/message-thread-01/index.d.ts +3 -0
- package/dist/live/message-thread-01/index.d.ts.map +1 -0
- package/dist/live/message-thread-01/index.js +1 -0
- package/dist/live/message-thread-01/message-thread.d.ts +282 -0
- package/dist/live/message-thread-01/message-thread.d.ts.map +1 -0
- package/dist/live/message-thread-01/message-thread.js +221 -0
- package/dist/live/run-stream-01/run-stream.d.ts +2 -1
- package/dist/live/run-stream-01/run-stream.d.ts.map +1 -1
- package/dist/live/run-stream-01/run-stream.js +1 -7
- package/dist/live/status.d.ts +27 -0
- package/dist/live/status.d.ts.map +1 -0
- package/dist/live/status.js +15 -0
- package/dist/live/tool-call-row.d.ts +26 -0
- package/dist/live/tool-call-row.d.ts.map +1 -0
- package/dist/live/tool-call-row.js +60 -0
- package/dist/live/tool-ledger-01/tool-ledger.d.ts.map +1 -1
- package/dist/live/tool-ledger-01/tool-ledger.js +2 -42
- package/dist/pages/contact-page/index.d.ts +7 -7
- package/dist/pages/contact-page/index.d.ts.map +1 -1
- package/dist/pages/contact-page/index.js +17 -5
- package/dist/pages/docs-shell/docs-shell.d.ts +42 -30
- package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -1
- package/dist/pages/docs-shell/docs-shell.js +70 -48
- package/dist/pages/document-page/document-page.d.ts +146 -0
- package/dist/pages/document-page/document-page.d.ts.map +1 -0
- package/dist/pages/document-page/document-page.js +80 -0
- package/dist/pages/document-page/index.d.ts +3 -0
- package/dist/pages/document-page/index.d.ts.map +1 -0
- package/dist/pages/document-page/index.js +1 -0
- package/dist/pages/index.d.ts +2 -0
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +6 -0
- package/dist/styles.css +59 -3
- package/package.json +7 -2
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type RunStatus } from '../status.js';
|
|
3
|
+
import type { ToolCall } from '../tool-ledger-01/tool-ledger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Who a message is from, as a closed set.
|
|
6
|
+
*
|
|
7
|
+
* The four are the ones a conversation actually has, and they are closed for the
|
|
8
|
+
* reason `RunEventRole` is: a sender is a slot for a mark and an ink, and a
|
|
9
|
+
* free-text sender would be a string with no rendering behind it. The **words**
|
|
10
|
+
* are still the caller's, in `senderLabel`. Prism owns which sender looks like
|
|
11
|
+
* what, and ships none of the labels.
|
|
12
|
+
*/
|
|
13
|
+
export type MessageSender = 'user' | 'assistant' | 'system' | 'tool';
|
|
14
|
+
/** A run of text, in the consumer's words. */
|
|
15
|
+
export type MessageTextPart = {
|
|
16
|
+
kind: 'text';
|
|
17
|
+
/** What the sender said. */
|
|
18
|
+
text: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A reasoning block: the model's own working, shown collapsed.
|
|
22
|
+
*
|
|
23
|
+
* `label` is required because a disclosure whose summary has no words is announced
|
|
24
|
+
* as a disclosure and nothing else, and the word on it is the consumer's.
|
|
25
|
+
*/
|
|
26
|
+
export type MessageReasoningPart = {
|
|
27
|
+
kind: 'reasoning';
|
|
28
|
+
/** The words on the disclosure's summary. */
|
|
29
|
+
label: string;
|
|
30
|
+
/** The reasoning itself. */
|
|
31
|
+
text: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* A tool call inside a message, drawn through the ledger's own row.
|
|
35
|
+
*
|
|
36
|
+
* The call is a `ToolCall`, which is the shape `ToolLedger01` already receives, so
|
|
37
|
+
* a tool call is drawn the same way in a message as in the ledger and there is no
|
|
38
|
+
* second tool-call shape.
|
|
39
|
+
*/
|
|
40
|
+
export type MessageToolPart = {
|
|
41
|
+
kind: 'tool';
|
|
42
|
+
/** The call, in the vocabulary `ToolLedger01` already publishes. */
|
|
43
|
+
call: ToolCall;
|
|
44
|
+
};
|
|
45
|
+
/** A citation: a labelled destination for a claim in the message. */
|
|
46
|
+
export type MessageCitationPart = {
|
|
47
|
+
kind: 'citation';
|
|
48
|
+
/** The words on the link, required because a link with no name announces as a link. */
|
|
49
|
+
label: string;
|
|
50
|
+
/** Where it goes. */
|
|
51
|
+
href: string;
|
|
52
|
+
/** Anything the label cannot hold: a page, a line, a quote. */
|
|
53
|
+
detail?: string;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* An attachment in the thread.
|
|
57
|
+
*
|
|
58
|
+
* `preview` is the consumer's own node, because the media an attachment shows is
|
|
59
|
+
* drawn by Items this package already ships rather than by this surface. It is
|
|
60
|
+
* placed above the name and never inside the link, so a preview that holds a
|
|
61
|
+
* control cannot nest one inside the anchor.
|
|
62
|
+
*/
|
|
63
|
+
export type MessageAttachmentPart = {
|
|
64
|
+
kind: 'attachment';
|
|
65
|
+
/** The name a reader recognises it by. */
|
|
66
|
+
name: string;
|
|
67
|
+
/** Where the attachment lives, absent for one that is only named. */
|
|
68
|
+
href?: string;
|
|
69
|
+
/** The consumer's own rendering of the attachment's contents. */
|
|
70
|
+
preview?: ReactNode;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* One part of a message, as the surface receives it.
|
|
74
|
+
*
|
|
75
|
+
* The five are the ones every surveyed stream converges on, and the set is closed
|
|
76
|
+
* because a part is a slot for a rendering rather than a free-text kind. Several
|
|
77
|
+
* tool calls fit in one message without a second key, which is the reason the
|
|
78
|
+
* content shape is a message with ordered parts rather than a flat event list.
|
|
79
|
+
*/
|
|
80
|
+
export type MessagePart = MessageTextPart | MessageReasoningPart | MessageToolPart | MessageCitationPart | MessageAttachmentPart;
|
|
81
|
+
/**
|
|
82
|
+
* One message, as the surface receives it.
|
|
83
|
+
*
|
|
84
|
+
* A message has a stable identity, a sender, an optional metadata node and an
|
|
85
|
+
* ordered list of parts. The `id` is stable across deliveries, so a transport that
|
|
86
|
+
* reports the same message again as more parts arrive updates one message rather
|
|
87
|
+
* than appending a second. `at` is the consumer's own epoch milliseconds and the
|
|
88
|
+
* only value the surface orders by.
|
|
89
|
+
*/
|
|
90
|
+
export type ChatMessage = {
|
|
91
|
+
/** Stable across deliveries, so a re-reported message updates in place. */
|
|
92
|
+
id: string;
|
|
93
|
+
/** Who sent it. See `MessageSender`. */
|
|
94
|
+
sender: MessageSender;
|
|
95
|
+
/** The sender's own name for the reader, absent for a surface that draws only the mark. */
|
|
96
|
+
senderLabel?: string;
|
|
97
|
+
/** Epoch milliseconds, used for order. */
|
|
98
|
+
at: number;
|
|
99
|
+
/**
|
|
100
|
+
* The caller's own per-message metadata, drawn in the message's header.
|
|
101
|
+
*
|
|
102
|
+
* A message carries one node of the caller's choosing beside the sender and the
|
|
103
|
+
* time: the model a provider stated for the turn, a token count it reported, and
|
|
104
|
+
* the cost or the latency the caller derived. It is a node rather than a set of
|
|
105
|
+
* Prism fields because the last two are the consumer's own derivation, which the
|
|
106
|
+
* conversation decision in `DESIGN.md` rules: no provider read publishes a cost
|
|
107
|
+
* or a latency, so the surface draws only what the caller hands it and states no
|
|
108
|
+
* model, no count and no figure for itself. The parts are content a reader reads
|
|
109
|
+
* as the message; this is metadata about the message and sits above its parts.
|
|
110
|
+
*
|
|
111
|
+
* It is the same `meta` shape `ItemEntry` takes, read at the message.
|
|
112
|
+
*/
|
|
113
|
+
meta?: ReactNode;
|
|
114
|
+
/** The message's parts, in the order they are drawn and never re-sorted. */
|
|
115
|
+
parts: readonly MessagePart[];
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* How the surface receives messages.
|
|
119
|
+
*
|
|
120
|
+
* The consumer owns the socket, the transport and the persistence, so this is a
|
|
121
|
+
* function it supplies rather than a connection Prism opens. It is called once on
|
|
122
|
+
* mount and must return an unsubscribe, which is what makes a WebSocket, an
|
|
123
|
+
* `EventSource`, a polling timer or a test's own array all the same shape to the
|
|
124
|
+
* surface. Nothing here knows which one it got.
|
|
125
|
+
*/
|
|
126
|
+
export type MessageSubscribe = (emit: (message: ChatMessage) => void) => () => void;
|
|
127
|
+
export type MessageThread01Props = {
|
|
128
|
+
/**
|
|
129
|
+
* Called once to begin receiving, and again whenever `resubscribeKey` changes.
|
|
130
|
+
* Must return an unsubscribe; the surface calls it on unmount and before
|
|
131
|
+
* re-subscribing, so a consumer that returns nothing leaks its connection.
|
|
132
|
+
*/
|
|
133
|
+
subscribe: MessageSubscribe;
|
|
134
|
+
/**
|
|
135
|
+
* The messages already known when this thread is opened, so a surface opened
|
|
136
|
+
* mid-thread is not blank. Oldest first.
|
|
137
|
+
*
|
|
138
|
+
* Read when the thread is opened, and again whenever `resubscribeKey` changes. It
|
|
139
|
+
* is **not** read on every render: a prop that looked like the current message
|
|
140
|
+
* list and quietly seeded only once would be a trap, and a prop that re-seeded on
|
|
141
|
+
* every render would discard the stream. This seeds, and the stream takes over.
|
|
142
|
+
*
|
|
143
|
+
* It is also the whole of the resumption seam: the durable buffer that outlives
|
|
144
|
+
* a mount is the consumer's, and a surface opened after a reconnect is seeded
|
|
145
|
+
* from the consumer's own record rather than from anything Prism kept.
|
|
146
|
+
*/
|
|
147
|
+
initial?: readonly ChatMessage[];
|
|
148
|
+
/**
|
|
149
|
+
* Which thread this surface is showing, and the reset lever.
|
|
150
|
+
*
|
|
151
|
+
* Changing it **clears the thread and reseeds it from `initial`**, as well as
|
|
152
|
+
* tearing the subscription down and opening a new one. A thread is identified by
|
|
153
|
+
* whatever the consumer already uses to identify it, so a second thread in the
|
|
154
|
+
* same surface is a new subscription rather than two consumers of one, and it is
|
|
155
|
+
* a new thread rather than the old messages with the new ones appended.
|
|
156
|
+
*/
|
|
157
|
+
resubscribeKey?: string | number;
|
|
158
|
+
/** Where the thread has got to. The tiers are the `live` Kind's own. */
|
|
159
|
+
status?: RunStatus;
|
|
160
|
+
/** The word beside the status. Required when a status is passed. */
|
|
161
|
+
statusLabel?: string;
|
|
162
|
+
/** Optional label above the surface's heading. See the No-Default-Eyebrow Rule. */
|
|
163
|
+
eyebrow?: string;
|
|
164
|
+
/** The surface's heading. Omit it for one composed under its own. */
|
|
165
|
+
title?: string;
|
|
166
|
+
/** One or two sentences under the heading. */
|
|
167
|
+
description?: string;
|
|
168
|
+
/** What the surface says when it holds no messages yet. */
|
|
169
|
+
emptyLabel?: string;
|
|
170
|
+
/**
|
|
171
|
+
* The accessible name of the scrolling region.
|
|
172
|
+
*
|
|
173
|
+
* Required, because `ScrollArea` requires it and the reason is the right one: a
|
|
174
|
+
* scroll region with no name is announced as a group of focusable nothing, and a
|
|
175
|
+
* keyboard reader who tabs into it cannot tell what is in it or what is past the
|
|
176
|
+
* edge. The name is the consumer's word, as every other word here is.
|
|
177
|
+
*/
|
|
178
|
+
messagesLabel: string;
|
|
179
|
+
/** Announced to assistive technology as messages arrive. */
|
|
180
|
+
liveLabel?: string;
|
|
181
|
+
/** How many messages to keep. Older messages fall off the top. @defaultValue 200 */
|
|
182
|
+
limit?: number;
|
|
183
|
+
/**
|
|
184
|
+
* Whether the surface opens paused.
|
|
185
|
+
*
|
|
186
|
+
* Read when the thread opens, and the control below owns it from the first press
|
|
187
|
+
* after that. It pauses the **surface** and never the run: the subscription stays
|
|
188
|
+
* open, because the connection is the consumer's and they may be sharing it with
|
|
189
|
+
* a `RunStream01` beside it.
|
|
190
|
+
*/
|
|
191
|
+
paused?: boolean;
|
|
192
|
+
/** Layout only. Changing a Prism-owned visual property from here is prohibited. */
|
|
193
|
+
className?: string;
|
|
194
|
+
} & ({
|
|
195
|
+
pauseControl?: false;
|
|
196
|
+
pauseLabel?: never;
|
|
197
|
+
resumeLabel?: never;
|
|
198
|
+
} | {
|
|
199
|
+
pauseControl: true;
|
|
200
|
+
pauseLabel: string;
|
|
201
|
+
resumeLabel: string;
|
|
202
|
+
});
|
|
203
|
+
/**
|
|
204
|
+
* The message thread of a conversation, receiving messages as they arrive.
|
|
205
|
+
*
|
|
206
|
+
* **This is the `live` Kind's second family, and it is the second client surface in
|
|
207
|
+
* the package beside `RunStream01`.** A conversation's content changes over time
|
|
208
|
+
* without a navigation event, which is the definition of the Kind, and a chat that
|
|
209
|
+
* cannot stream is not a chat. So this surface owns a subscription and the bounded
|
|
210
|
+
* window behind it, exactly as the run log does.
|
|
211
|
+
*
|
|
212
|
+
* **Prism owns the surface, the consumer owns the transport.** `subscribe` is a
|
|
213
|
+
* function the consumer supplies and a function it tears down. There is no socket
|
|
214
|
+
* in this file, no endpoint, no retry policy, no persistence and no provider to
|
|
215
|
+
* mount, so a WebSocket, an `EventSource`, a polling timer and a test's own array
|
|
216
|
+
* are all the same shape to this surface. A consumer that already has a connection
|
|
217
|
+
* passes it in, and one that does not has not acquired a client runtime by
|
|
218
|
+
* importing this.
|
|
219
|
+
*
|
|
220
|
+
* **The content is a message list with ordered parts, and neither an event list nor
|
|
221
|
+
* a turn list is taken.** A message is an artifact a reader reads and addresses,
|
|
222
|
+
* where an occurrence is not, so the run's flat `RunEvent` stays the run log's own
|
|
223
|
+
* vocabulary. A turn is a derived grouping of a request and its answers, the
|
|
224
|
+
* transport does not preserve it as a unit, and the order of a stream after a
|
|
225
|
+
* reconnect is unsettled, so a surface that grouped by turn would impose a
|
|
226
|
+
* structure the consumer's data may not have. The message and part vocabulary lives
|
|
227
|
+
* here, beside `RunEvent` and `ToolCall`, and is deliberately not a member of the
|
|
228
|
+
* shared specification module.
|
|
229
|
+
*
|
|
230
|
+
* **Per-message metadata is the caller's own node.** The model a provider stated
|
|
231
|
+
* for a turn, a token count it reported, and the cost and the latency a consumer
|
|
232
|
+
* derived are metadata about a message rather than content a reader reads as it, so
|
|
233
|
+
* a message carries an optional `meta` the surface draws in its header beside the
|
|
234
|
+
* sender and the time. It is a node, and deliberately not a set of Prism fields:
|
|
235
|
+
* cost and latency are the consumer's own derivation, because no provider read
|
|
236
|
+
* publishes either, so the surface draws what it was handed and states no figure
|
|
237
|
+
* for itself. This is the `ItemEntry.meta` shape read at the message.
|
|
238
|
+
*
|
|
239
|
+
* **The four things the Kind already names are reused rather than answered twice.**
|
|
240
|
+
* A tool-call part is drawn through `ToolLedger01`'s own row, so there is no second
|
|
241
|
+
* tool-call shape. The status tiers are the ones `RunStatus` already publishes. The
|
|
242
|
+
* run controls are `PromptComposer`'s send, stop, retry and attach, which the
|
|
243
|
+
* consumer composes beside this surface rather than this surface drawing a second
|
|
244
|
+
* set of them. The one surface it does not reuse is `RunStream01`, because a run's
|
|
245
|
+
* log is a sequence of occurrences and a conversation is a sequence of messages.
|
|
246
|
+
*
|
|
247
|
+
* **The surface draws no control it cannot act on.** The only control in it is the
|
|
248
|
+
* pause control, which pauses this surface and never the run, and which a consumer
|
|
249
|
+
* opts into with `pauseControl`. Send, stop, retry and attach are not here: they are
|
|
250
|
+
* `PromptComposer`'s, and a settled thread composed outside the live module reaches
|
|
251
|
+
* a decision through a `CtaLink` with its required `href` or a slot the consumer
|
|
252
|
+
* fills.
|
|
253
|
+
*
|
|
254
|
+
* **Prism holds a bounded in-memory window and promises no resumption.** The
|
|
255
|
+
* durable buffer that outlives a mount is the consumer's, because Prism ships no
|
|
256
|
+
* permanent client runtime and cannot assume a server framework. The seam is
|
|
257
|
+
* `initial` and `resubscribeKey`: a surface opened mid-thread is seeded from the
|
|
258
|
+
* consumer's own record, a reconnect is a new key over a fresh seed, and the
|
|
259
|
+
* surface promises no resumption so it cannot fail to keep one.
|
|
260
|
+
*
|
|
261
|
+
* **Messages are kept sorted by `at`, not by arrival.** A transport decides when a
|
|
262
|
+
* message is delivered, and a reconnect can deliver an older message after a newer
|
|
263
|
+
* one. Ties break on `id`, so the order is total and the same thread renders the
|
|
264
|
+
* same way every time. The same `id` is also the same message, so a transport that
|
|
265
|
+
* reports it again as more parts arrive updates one message rather than adding a
|
|
266
|
+
* second.
|
|
267
|
+
*
|
|
268
|
+
* **New messages are announced once, politely, and the thread itself is not a live
|
|
269
|
+
* region.** Announcing a growing list would make a screen reader read the whole
|
|
270
|
+
* thread again on every arrival, which is the failure a live region on a list
|
|
271
|
+
* guarantees. `LiveRegion` carries the newest message's first readable part alone,
|
|
272
|
+
* and the thread is an ordinary scrollable list a reader moves through at their own
|
|
273
|
+
* pace.
|
|
274
|
+
*
|
|
275
|
+
* `limit` bounds what is held, because a long conversation produces thousands of
|
|
276
|
+
* messages and a surface that grows without bound eventually stops repainting.
|
|
277
|
+
* Older messages fall off the top, which is the direction a reader wants: the
|
|
278
|
+
* thread's beginning is the part they can scroll back to in the consumer's store.
|
|
279
|
+
*/
|
|
280
|
+
export declare function MessageThread01({ subscribe, initial, resubscribeKey, status, statusLabel, eyebrow, title, description, emptyLabel, messagesLabel, liveLabel, limit, paused, pauseControl, pauseLabel, resumeLabel, className, }: MessageThread01Props): import("react").JSX.Element;
|
|
281
|
+
export default MessageThread01;
|
|
282
|
+
//# sourceMappingURL=message-thread.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-thread.d.ts","sourceRoot":"","sources":["../../../src/live/message-thread-01/message-thread.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAOnE,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,WAAW,CAAA;AAEtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAA;AAE7D;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEpE,8CAA8C;AAC9C,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,WAAW,CAAA;IACjB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,IAAI,EAAE,QAAQ,CAAA;CACf,CAAA;AAED,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,UAAU,CAAA;IAChB,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAA;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,YAAY,CAAA;IAClB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,oBAAoB,GACpB,eAAe,GACf,mBAAmB,GACnB,qBAAqB,CAAA;AAEzB;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAA;IACV,wCAAwC;IACxC,MAAM,EAAE,aAAa,CAAA;IACrB,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAA;IACV;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,4EAA4E;IAC5E,KAAK,EAAE,SAAS,WAAW,EAAE,CAAA;CAC9B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;AAEnF,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;OAIG;IACH,SAAS,EAAE,gBAAgB,CAAA;IAC3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;IAChC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC,wEAAwE;IACxE,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;OAOG;IACH,aAAa,EAAE,MAAM,CAAA;IACrB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,CACA;IAAE,YAAY,CAAC,EAAE,KAAK,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAA;CAAE,GACjE;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAClE,CAAA;AAiED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,OAAO,EACP,cAAc,EACd,MAAM,EACN,WAAW,EACX,OAAO,EACP,KAAK,EACL,WAAW,EACX,UAAU,EACV,aAAa,EACb,SAAS,EACT,KAAW,EACX,MAAM,EACN,YAAY,EACZ,UAAU,EACV,WAAW,EACX,SAAS,GACV,EAAE,oBAAoB,+BA4KtB;AA+DD,eAAe,eAAe,CAAA"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { Button } from '../../components/ui/button.js';
|
|
5
|
+
import { LiveRegion } from '../../components/ui/live-region.js';
|
|
6
|
+
import { ScrollArea } from '../../components/ui/scroll-area.js';
|
|
7
|
+
import { headingSizeClass } from '../../components/ui/section.js';
|
|
8
|
+
import { cn } from '../../lib/utils.js';
|
|
9
|
+
import { STATUS_INK } from '../status.js';
|
|
10
|
+
import { ToolCallRow } from '../tool-call-row.js';
|
|
11
|
+
/** The ink each sender is stated in. */
|
|
12
|
+
const SENDER_INK = {
|
|
13
|
+
user: 'text-foreground',
|
|
14
|
+
assistant: 'text-foreground',
|
|
15
|
+
system: 'text-muted-foreground',
|
|
16
|
+
tool: 'text-muted-foreground',
|
|
17
|
+
};
|
|
18
|
+
/** The mark each sender draws, so the four are told apart without relying on colour. */
|
|
19
|
+
const SENDER_MARK = {
|
|
20
|
+
user: 'u',
|
|
21
|
+
assistant: 'a',
|
|
22
|
+
system: 's',
|
|
23
|
+
tool: 't',
|
|
24
|
+
};
|
|
25
|
+
/** The clock a message reads, in UTC, because the surface holds no locale to format in. */
|
|
26
|
+
const clockOf = (at) => new Date(at).toISOString().slice(11, 19);
|
|
27
|
+
/** The one source of truth for order, so a late message with an earlier `at` sorts there. */
|
|
28
|
+
const byTimeThenId = (a, b) => a.at - b.at || a.id.localeCompare(b.id);
|
|
29
|
+
/** The seed a thread opens with, already sorted and trimmed to the window. */
|
|
30
|
+
function seedOf(messages, limit) {
|
|
31
|
+
const sorted = [...messages].sort(byTimeThenId);
|
|
32
|
+
return sorted.length > limit ? sorted.slice(sorted.length - limit) : sorted;
|
|
33
|
+
}
|
|
34
|
+
/** One message merged into the thread, and the window applied. */
|
|
35
|
+
function merge(messages, message, limit) {
|
|
36
|
+
const at = messages.findIndex((current) => current.id === message.id);
|
|
37
|
+
const next = at === -1
|
|
38
|
+
? [...messages, message]
|
|
39
|
+
: messages.map((current, index) => (index === at ? message : current));
|
|
40
|
+
next.sort(byTimeThenId);
|
|
41
|
+
return next.length > limit ? next.slice(next.length - limit) : next;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The one line the live region announces for an arriving message.
|
|
45
|
+
*
|
|
46
|
+
* The region carries a single short string rather than the message, because a live
|
|
47
|
+
* region around a growing list makes a screen reader read the whole thread again
|
|
48
|
+
* on every arrival. The string is the message's own first readable part, in the
|
|
49
|
+
* consumer's words: the first text, reasoning, tool name, citation or attachment
|
|
50
|
+
* name it carries.
|
|
51
|
+
*/
|
|
52
|
+
function announcementOf(message) {
|
|
53
|
+
for (const part of message.parts) {
|
|
54
|
+
if (part.kind === 'text' || part.kind === 'reasoning')
|
|
55
|
+
return part.text;
|
|
56
|
+
if (part.kind === 'tool')
|
|
57
|
+
return part.call.name;
|
|
58
|
+
if (part.kind === 'citation')
|
|
59
|
+
return part.label;
|
|
60
|
+
if (part.kind === 'attachment')
|
|
61
|
+
return part.name;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The message thread of a conversation, receiving messages as they arrive.
|
|
67
|
+
*
|
|
68
|
+
* **This is the `live` Kind's second family, and it is the second client surface in
|
|
69
|
+
* the package beside `RunStream01`.** A conversation's content changes over time
|
|
70
|
+
* without a navigation event, which is the definition of the Kind, and a chat that
|
|
71
|
+
* cannot stream is not a chat. So this surface owns a subscription and the bounded
|
|
72
|
+
* window behind it, exactly as the run log does.
|
|
73
|
+
*
|
|
74
|
+
* **Prism owns the surface, the consumer owns the transport.** `subscribe` is a
|
|
75
|
+
* function the consumer supplies and a function it tears down. There is no socket
|
|
76
|
+
* in this file, no endpoint, no retry policy, no persistence and no provider to
|
|
77
|
+
* mount, so a WebSocket, an `EventSource`, a polling timer and a test's own array
|
|
78
|
+
* are all the same shape to this surface. A consumer that already has a connection
|
|
79
|
+
* passes it in, and one that does not has not acquired a client runtime by
|
|
80
|
+
* importing this.
|
|
81
|
+
*
|
|
82
|
+
* **The content is a message list with ordered parts, and neither an event list nor
|
|
83
|
+
* a turn list is taken.** A message is an artifact a reader reads and addresses,
|
|
84
|
+
* where an occurrence is not, so the run's flat `RunEvent` stays the run log's own
|
|
85
|
+
* vocabulary. A turn is a derived grouping of a request and its answers, the
|
|
86
|
+
* transport does not preserve it as a unit, and the order of a stream after a
|
|
87
|
+
* reconnect is unsettled, so a surface that grouped by turn would impose a
|
|
88
|
+
* structure the consumer's data may not have. The message and part vocabulary lives
|
|
89
|
+
* here, beside `RunEvent` and `ToolCall`, and is deliberately not a member of the
|
|
90
|
+
* shared specification module.
|
|
91
|
+
*
|
|
92
|
+
* **Per-message metadata is the caller's own node.** The model a provider stated
|
|
93
|
+
* for a turn, a token count it reported, and the cost and the latency a consumer
|
|
94
|
+
* derived are metadata about a message rather than content a reader reads as it, so
|
|
95
|
+
* a message carries an optional `meta` the surface draws in its header beside the
|
|
96
|
+
* sender and the time. It is a node, and deliberately not a set of Prism fields:
|
|
97
|
+
* cost and latency are the consumer's own derivation, because no provider read
|
|
98
|
+
* publishes either, so the surface draws what it was handed and states no figure
|
|
99
|
+
* for itself. This is the `ItemEntry.meta` shape read at the message.
|
|
100
|
+
*
|
|
101
|
+
* **The four things the Kind already names are reused rather than answered twice.**
|
|
102
|
+
* A tool-call part is drawn through `ToolLedger01`'s own row, so there is no second
|
|
103
|
+
* tool-call shape. The status tiers are the ones `RunStatus` already publishes. The
|
|
104
|
+
* run controls are `PromptComposer`'s send, stop, retry and attach, which the
|
|
105
|
+
* consumer composes beside this surface rather than this surface drawing a second
|
|
106
|
+
* set of them. The one surface it does not reuse is `RunStream01`, because a run's
|
|
107
|
+
* log is a sequence of occurrences and a conversation is a sequence of messages.
|
|
108
|
+
*
|
|
109
|
+
* **The surface draws no control it cannot act on.** The only control in it is the
|
|
110
|
+
* pause control, which pauses this surface and never the run, and which a consumer
|
|
111
|
+
* opts into with `pauseControl`. Send, stop, retry and attach are not here: they are
|
|
112
|
+
* `PromptComposer`'s, and a settled thread composed outside the live module reaches
|
|
113
|
+
* a decision through a `CtaLink` with its required `href` or a slot the consumer
|
|
114
|
+
* fills.
|
|
115
|
+
*
|
|
116
|
+
* **Prism holds a bounded in-memory window and promises no resumption.** The
|
|
117
|
+
* durable buffer that outlives a mount is the consumer's, because Prism ships no
|
|
118
|
+
* permanent client runtime and cannot assume a server framework. The seam is
|
|
119
|
+
* `initial` and `resubscribeKey`: a surface opened mid-thread is seeded from the
|
|
120
|
+
* consumer's own record, a reconnect is a new key over a fresh seed, and the
|
|
121
|
+
* surface promises no resumption so it cannot fail to keep one.
|
|
122
|
+
*
|
|
123
|
+
* **Messages are kept sorted by `at`, not by arrival.** A transport decides when a
|
|
124
|
+
* message is delivered, and a reconnect can deliver an older message after a newer
|
|
125
|
+
* one. Ties break on `id`, so the order is total and the same thread renders the
|
|
126
|
+
* same way every time. The same `id` is also the same message, so a transport that
|
|
127
|
+
* reports it again as more parts arrive updates one message rather than adding a
|
|
128
|
+
* second.
|
|
129
|
+
*
|
|
130
|
+
* **New messages are announced once, politely, and the thread itself is not a live
|
|
131
|
+
* region.** Announcing a growing list would make a screen reader read the whole
|
|
132
|
+
* thread again on every arrival, which is the failure a live region on a list
|
|
133
|
+
* guarantees. `LiveRegion` carries the newest message's first readable part alone,
|
|
134
|
+
* and the thread is an ordinary scrollable list a reader moves through at their own
|
|
135
|
+
* pace.
|
|
136
|
+
*
|
|
137
|
+
* `limit` bounds what is held, because a long conversation produces thousands of
|
|
138
|
+
* messages and a surface that grows without bound eventually stops repainting.
|
|
139
|
+
* Older messages fall off the top, which is the direction a reader wants: the
|
|
140
|
+
* thread's beginning is the part they can scroll back to in the consumer's store.
|
|
141
|
+
*/
|
|
142
|
+
export function MessageThread01({ subscribe, initial, resubscribeKey, status, statusLabel, eyebrow, title, description, emptyLabel, messagesLabel, liveLabel, limit = 200, paused, pauseControl, pauseLabel, resumeLabel, className, }) {
|
|
143
|
+
/**
|
|
144
|
+
* The thread, held against the key that produced it.
|
|
145
|
+
*
|
|
146
|
+
* Adjusting state during render when the key changes is React's documented
|
|
147
|
+
* pattern for "a prop changed, start over", and it is the right one here: the
|
|
148
|
+
* alternative is an effect, and an effect that clears the thread runs *after* a
|
|
149
|
+
* render, so the surface would paint one frame holding the previous thread's
|
|
150
|
+
* messages under the new thread's heading.
|
|
151
|
+
*/
|
|
152
|
+
const [newest, setNewest] = useState(undefined);
|
|
153
|
+
const [thread, setThread] = useState(() => ({
|
|
154
|
+
key: resubscribeKey,
|
|
155
|
+
messages: seedOf(initial ?? [], limit),
|
|
156
|
+
}));
|
|
157
|
+
if (thread.key !== resubscribeKey) {
|
|
158
|
+
setThread({ key: resubscribeKey, messages: seedOf(initial ?? [], limit) });
|
|
159
|
+
// The announcement is cleared with the thread it announced, so a thread switch
|
|
160
|
+
// does not leave the previous thread's last message in the live region saying so.
|
|
161
|
+
setNewest(undefined);
|
|
162
|
+
}
|
|
163
|
+
const messages = thread.messages;
|
|
164
|
+
/**
|
|
165
|
+
* Whether this surface is holding still, kept in a ref as well as in state.
|
|
166
|
+
*
|
|
167
|
+
* The ref is what the arrival path reads, and it is why the subscription effect
|
|
168
|
+
* does not list the pause among its dependencies: an effect that depended on the
|
|
169
|
+
* pause would tear the consumer's connection down and open a new one every time a
|
|
170
|
+
* reader pressed the control. The button writes both, so the value the effect
|
|
171
|
+
* reads is the value the reader set.
|
|
172
|
+
*/
|
|
173
|
+
const [held, setHeldState] = useState(paused === true);
|
|
174
|
+
const heldRef = useRef(held);
|
|
175
|
+
const setHeld = (next) => {
|
|
176
|
+
heldRef.current = next;
|
|
177
|
+
setHeldState(next);
|
|
178
|
+
};
|
|
179
|
+
// A ref rather than state, so the effect does not re-subscribe because a message
|
|
180
|
+
// arrived. An effect that depended on the value it writes re-runs itself, and the
|
|
181
|
+
// surface would open a new subscription per message.
|
|
182
|
+
const emit = useRef(() => { });
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
emit.current = (message) => {
|
|
185
|
+
// Read through the ref, so a paused surface decides without re-subscribing.
|
|
186
|
+
if (heldRef.current)
|
|
187
|
+
return;
|
|
188
|
+
setThread((current) => ({
|
|
189
|
+
...current,
|
|
190
|
+
messages: merge(current.messages, message, limit),
|
|
191
|
+
}));
|
|
192
|
+
setNewest(announcementOf(message));
|
|
193
|
+
};
|
|
194
|
+
const stop = subscribe(emit.current);
|
|
195
|
+
return () => {
|
|
196
|
+
// Calling an unsubscribe the consumer did not return would throw during
|
|
197
|
+
// unmount, so a missing one is stepped over and the leak is the consumer's to
|
|
198
|
+
// fix rather than a crash in someone else's teardown.
|
|
199
|
+
if (typeof stop === 'function')
|
|
200
|
+
stop();
|
|
201
|
+
};
|
|
202
|
+
}, [subscribe, resubscribeKey, limit]);
|
|
203
|
+
return (_jsxs("section", { "data-slot": "message-thread", className: cn('border-border flex flex-col gap-4 rounded-lg border p-4', className), children: [LiveRegion({ politeness: 'polite', label: liveLabel, children: newest }), title !== undefined || eyebrow !== undefined || description !== undefined ? (_jsxs("header", { className: "flex flex-col gap-1", children: [eyebrow ? (_jsx("span", { className: "text-muted-foreground text-xs font-medium tracking-wide uppercase", children: eyebrow })) : null, title ? (_jsx("h2", { className: cn('font-semibold tracking-tight text-balance', headingSizeClass('h2')), children: title })) : null, description ? (_jsx("p", { className: "text-muted-foreground text-pretty text-sm", children: description })) : null] })) : null, status !== undefined ? (_jsx("p", { "data-slot": "thread-status", className: cn('font-mono text-xs', STATUS_INK[status]), children: statusLabel })) : null, pauseControl === true ? (_jsx("div", { "data-slot": "thread-bar", className: "flex flex-wrap items-center gap-3", children: _jsx(Button, { "data-slot": "thread-pause", type: "button", variant: "outline", size: "sm", "aria-pressed": held, className: "ml-auto", onClick: () => setHeld(!heldRef.current), children: held ? resumeLabel : pauseLabel }) })) : null, messages.length === 0 ? (_jsx("p", { "data-slot": "message-thread-empty", className: "text-muted-foreground text-sm", children: emptyLabel })) : (_jsx(ScrollArea, { label: messagesLabel, className: "max-h-96", children: _jsx("ol", { "data-slot": "thread-messages", className: "flex flex-col gap-4", children: messages.map((message) => (_jsxs("li", { "data-slot": "thread-message", "data-sender": message.sender, className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-baseline gap-x-2", children: [_jsx("span", { "aria-hidden": "true", className: cn('font-mono text-xs', SENDER_INK[message.sender]), children: SENDER_MARK[message.sender] }), message.senderLabel ? (_jsx("span", { className: "text-foreground text-xs font-medium", children: message.senderLabel })) : null, _jsx("time", { dateTime: new Date(message.at).toISOString(), className: "text-muted-foreground font-mono text-xs tabular-nums", children: clockOf(message.at) }), message.meta === undefined ? null : (_jsx("span", { "data-slot": "thread-message-meta", className: "text-muted-foreground text-xs", children: message.meta }))] }), _jsx("ol", { "data-slot": "message-parts", className: "flex flex-col gap-2", children: message.parts.map((part, index) => (_jsx("li", { "data-slot": "message-part", "data-kind": part.kind, className: "text-sm", children: renderPart(part) }, `${message.id}-${index}`))) })] }, message.id))) }) }))] }));
|
|
204
|
+
}
|
|
205
|
+
/** One part, drawn by its kind. */
|
|
206
|
+
function renderPart(part) {
|
|
207
|
+
if (part.kind === 'text') {
|
|
208
|
+
return _jsx("p", { className: "text-foreground text-pretty text-sm", children: part.text });
|
|
209
|
+
}
|
|
210
|
+
if (part.kind === 'reasoning') {
|
|
211
|
+
return (_jsxs("details", { "data-slot": "message-reasoning", className: "text-muted-foreground text-sm", children: [_jsx("summary", { className: "cursor-pointer text-xs font-medium", children: part.label }), _jsx("p", { className: "text-muted-foreground text-pretty text-sm", children: part.text })] }));
|
|
212
|
+
}
|
|
213
|
+
if (part.kind === 'tool') {
|
|
214
|
+
return (_jsx("div", { className: "border-border flex flex-col gap-1 rounded-md border p-2", children: _jsx(ToolCallRow, { call: part.call, openedAt: part.call.at }) }));
|
|
215
|
+
}
|
|
216
|
+
if (part.kind === 'citation') {
|
|
217
|
+
return (_jsxs("span", { className: "flex flex-col gap-0.5", children: [_jsx("a", { "data-slot": "message-citation", href: part.href, className: "text-foreground self-start rounded-sm text-sm underline-offset-4 hover:underline", children: part.label }), part.detail ? (_jsx("span", { className: "text-muted-foreground text-pretty text-xs", children: part.detail })) : null] }));
|
|
218
|
+
}
|
|
219
|
+
return (_jsxs("span", { "data-slot": "message-attachment", className: "border-border flex flex-col gap-1 rounded-md border p-2", children: [part.preview ? _jsx("span", { className: "block", children: part.preview }) : null, part.href ? (_jsx("a", { "data-slot": "message-attachment-link", href: part.href, className: "text-foreground self-start rounded-sm text-sm underline-offset-4 hover:underline", children: part.name })) : (_jsx("span", { className: "text-foreground text-sm", children: part.name }))] }));
|
|
220
|
+
}
|
|
221
|
+
export default MessageThread01;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RunStatus } from '../status.js';
|
|
1
2
|
/**
|
|
2
3
|
* Who produced an event, as a closed set.
|
|
3
4
|
*
|
|
@@ -21,7 +22,7 @@ export type RunEvent = {
|
|
|
21
22
|
detail?: string;
|
|
22
23
|
};
|
|
23
24
|
/** Where a run has got to. The tiers are Prism's; the words beside them are the caller's. */
|
|
24
|
-
export type RunStatus
|
|
25
|
+
export type { RunStatus };
|
|
25
26
|
/**
|
|
26
27
|
* How the surface receives events.
|
|
27
28
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/live/run-stream-01/run-stream.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/live/run-stream-01/run-stream.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,WAAW,CAAA;AAEtD;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEhE,6CAA6C;AAC7C,MAAM,MAAM,QAAQ,GAAG;IACrB,2FAA2F;IAC3F,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,YAAY,CAAA;IAClB,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAA;IACV,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,6FAA6F;AAC7F,YAAY,EAAE,SAAS,EAAE,CAAA;AAEzB;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,KAAK,MAAM,IAAI,CAAA;AAE1E,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;OAIG;IACH,SAAS,EAAE,YAAY,CAAA;IACvB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC7B,gCAAgC;IAChC,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;OAOG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,cAAc,EACd,OAAO,EACP,MAAM,EACN,WAAW,EACX,OAAO,EACP,KAAK,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,SAAS,EACT,KAAW,EACX,SAAS,GACV,EAAE,gBAAgB,+BA0IlB;AAED,eAAe,WAAW,CAAA"}
|
|
@@ -5,6 +5,7 @@ import { LiveRegion } from '../../components/ui/live-region.js';
|
|
|
5
5
|
import { ScrollArea } from '../../components/ui/scroll-area.js';
|
|
6
6
|
import { headingSizeClass } from '../../components/ui/section.js';
|
|
7
7
|
import { cn } from '../../lib/utils.js';
|
|
8
|
+
import { STATUS_INK } from '../status.js';
|
|
8
9
|
/** The ink each role is stated in, and the tick beside it. */
|
|
9
10
|
const ROLE_INK = {
|
|
10
11
|
agent: 'text-foreground',
|
|
@@ -19,13 +20,6 @@ const ROLE_MARK = {
|
|
|
19
20
|
system: '·',
|
|
20
21
|
error: '×',
|
|
21
22
|
};
|
|
22
|
-
const STATUS_INK = {
|
|
23
|
-
queued: 'text-muted-foreground',
|
|
24
|
-
running: 'text-foreground',
|
|
25
|
-
waiting: 'text-warning',
|
|
26
|
-
done: 'text-success',
|
|
27
|
-
failed: 'text-destructive',
|
|
28
|
-
};
|
|
29
23
|
/** The one source of truth for order, so a late event with an earlier `at` sorts there. */
|
|
30
24
|
const byTimeThenId = (a, b) => a.at - b.at || a.id.localeCompare(b.id);
|
|
31
25
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a live surface has got to, as the Kind's one closed set of tiers.
|
|
3
|
+
*
|
|
4
|
+
* A run and a conversation are two families of the same Kind, and a reader
|
|
5
|
+
* watching either one asks the same question: is this queued, running, waiting,
|
|
6
|
+
* done or failed. The tiers are therefore declared once, here, and every live
|
|
7
|
+
* surface draws its state from them rather than minting a vocabulary of its own.
|
|
8
|
+
* A second status word invented for a conversation would be a second answer to a
|
|
9
|
+
* question this set already answers, and two answers that agree today are a
|
|
10
|
+
* catalogue drifting a second list.
|
|
11
|
+
*
|
|
12
|
+
* The **words** beside a tier are still the consumer's: Prism owns which tier
|
|
13
|
+
* looks like what, and ships none of the labels. The ink below is the whole of
|
|
14
|
+
* what the Kind states about a tier, and it is stated once so the run log and the
|
|
15
|
+
* message thread cannot disagree about what a failure looks like.
|
|
16
|
+
*/
|
|
17
|
+
export type RunStatus = 'queued' | 'running' | 'waiting' | 'done' | 'failed';
|
|
18
|
+
/**
|
|
19
|
+
* The ink each tier is stated in.
|
|
20
|
+
*
|
|
21
|
+
* The five roles are the ones an agent surface actually reports, and the ink is
|
|
22
|
+
* the one Prism-owned visual fact about a tier. `waiting` and `failed` are the
|
|
23
|
+
* two that carry a colour a reader acts on, which is why they are the two that
|
|
24
|
+
* are not the foreground or the muted ink.
|
|
25
|
+
*/
|
|
26
|
+
export declare const STATUS_INK: Record<RunStatus, string>;
|
|
27
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/live/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAE5E;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMhD,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ink each tier is stated in.
|
|
3
|
+
*
|
|
4
|
+
* The five roles are the ones an agent surface actually reports, and the ink is
|
|
5
|
+
* the one Prism-owned visual fact about a tier. `waiting` and `failed` are the
|
|
6
|
+
* two that carry a colour a reader acts on, which is why they are the two that
|
|
7
|
+
* are not the foreground or the muted ink.
|
|
8
|
+
*/
|
|
9
|
+
export const STATUS_INK = {
|
|
10
|
+
queued: 'text-muted-foreground',
|
|
11
|
+
running: 'text-foreground',
|
|
12
|
+
waiting: 'text-warning',
|
|
13
|
+
done: 'text-success',
|
|
14
|
+
failed: 'text-destructive',
|
|
15
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ToolCall } from './tool-ledger-01/tool-ledger.js';
|
|
2
|
+
/** The clock a row reads, in UTC, because the surface holds no locale to format in. */
|
|
3
|
+
export declare const clockOf: (at: number) => string;
|
|
4
|
+
/**
|
|
5
|
+
* One tool call, drawn as the ledger draws it.
|
|
6
|
+
*
|
|
7
|
+
* **This is the tool-call row the `live` Kind owns, and it is shared rather than
|
|
8
|
+
* copied.** `ToolLedger01` draws one row per call in its ledger, and a
|
|
9
|
+
* conversation's tool-call part draws the same row inside a message, so the two
|
|
10
|
+
* surfaces cannot drift into two shapes for one call. `ToolLedger01` renders this
|
|
11
|
+
* inside its own `<li>`; the message thread renders it inside a part. The
|
|
12
|
+
* Component owns no `<li>`, so it can sit in either list without nesting one.
|
|
13
|
+
*
|
|
14
|
+
* **The duration is the difference between two sightings of the same call, and
|
|
15
|
+
* only when there are two.** `openedAt` is the moment the surface first saw the
|
|
16
|
+
* call's `id`; a call seen once has no second timestamp and draws no duration,
|
|
17
|
+
* because a zero is a claim that a call took no time. The sentence is the
|
|
18
|
+
* consumer's, on `call.durationLabel`, and the arithmetic is one subtraction of
|
|
19
|
+
* the consumer's own `at` values. There is no clock in this file.
|
|
20
|
+
*/
|
|
21
|
+
export declare function ToolCallRow({ call, openedAt }: {
|
|
22
|
+
call: ToolCall;
|
|
23
|
+
openedAt: number;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
25
|
+
export default ToolCallRow;
|
|
26
|
+
//# sourceMappingURL=tool-call-row.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-call-row.d.ts","sourceRoot":"","sources":["../../src/live/tool-call-row.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,8BAA8B,CAAA;AAgC3E,uFAAuF;AACvF,eAAO,MAAM,OAAO,GAAI,IAAI,MAAM,KAAG,MAAkD,CAAA;AAEvF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,+BAqFnF;AAED,eAAe,WAAW,CAAA"}
|