@flamingo-stack/openframe-frontend-core 0.0.389 → 0.0.390-snapshot.20260709094846
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/{chunk-3JQUNKWY.cjs → chunk-33PP6VR3.cjs} +15 -15
- package/dist/{chunk-3JQUNKWY.cjs.map → chunk-33PP6VR3.cjs.map} +1 -1
- package/dist/{chunk-CQXHHVMR.js → chunk-4HV6RIDE.js} +2 -2
- package/dist/{chunk-LFLFQ422.js → chunk-BY337N4T.js} +2 -2
- package/dist/{chunk-M6ODI2F5.js → chunk-CAEKZPSH.js} +2 -2
- package/dist/{chunk-JSFHTIKA.js → chunk-CPWR2BJG.js} +2 -2
- package/dist/{chunk-Q4YGG3CL.cjs → chunk-DAYWQZIW.cjs} +31 -31
- package/dist/{chunk-Q4YGG3CL.cjs.map → chunk-DAYWQZIW.cjs.map} +1 -1
- package/dist/{chunk-44Q3IXZM.js → chunk-GM5M4L7O.js} +2 -2
- package/dist/{chunk-OBKCQ7LA.cjs → chunk-GWJSQEQO.cjs} +62 -62
- package/dist/{chunk-OBKCQ7LA.cjs.map → chunk-GWJSQEQO.cjs.map} +1 -1
- package/dist/{chunk-BJPQJNSC.js → chunk-HPTHEA2A.js} +4 -4
- package/dist/{chunk-WIMXB5EU.js → chunk-KWLSVTDN.js} +4 -4
- package/dist/{chunk-7MKU2EB4.cjs → chunk-LC4VIFII.cjs} +26 -26
- package/dist/{chunk-7MKU2EB4.cjs.map → chunk-LC4VIFII.cjs.map} +1 -1
- package/dist/{chunk-KA4WXCN3.js → chunk-QHKU6473.js} +4 -4
- package/dist/{chunk-MPOP47ME.cjs → chunk-QQQW4DUI.cjs} +12 -12
- package/dist/{chunk-MPOP47ME.cjs.map → chunk-QQQW4DUI.cjs.map} +1 -1
- package/dist/{chunk-NULFIYK2.js → chunk-QXD4OZCN.js} +2 -2
- package/dist/{chunk-ZFZAHLI4.cjs → chunk-R62LSFOU.cjs} +7 -7
- package/dist/{chunk-ZFZAHLI4.cjs.map → chunk-R62LSFOU.cjs.map} +1 -1
- package/dist/{chunk-4OLIDOLO.js → chunk-SJYXTTUB.js} +2 -2
- package/dist/{chunk-EHTE3CTT.cjs → chunk-TXCE75UB.cjs} +37 -37
- package/dist/{chunk-EHTE3CTT.cjs.map → chunk-TXCE75UB.cjs.map} +1 -1
- package/dist/{chunk-2JCSORCJ.cjs → chunk-USUNV54R.cjs} +26 -26
- package/dist/{chunk-2JCSORCJ.cjs.map → chunk-USUNV54R.cjs.map} +1 -1
- package/dist/{chunk-IDWKP5H5.js → chunk-VIZUVGIY.js} +1936 -1852
- package/dist/chunk-VIZUVGIY.js.map +1 -0
- package/dist/{chunk-VXJBE5NX.cjs → chunk-VY3N2RPS.cjs} +59 -59
- package/dist/{chunk-VXJBE5NX.cjs.map → chunk-VY3N2RPS.cjs.map} +1 -1
- package/dist/{chunk-GNB5ERZP.cjs → chunk-Y4S6X7IV.cjs} +1574 -1490
- package/dist/chunk-Y4S6X7IV.cjs.map +1 -0
- package/dist/{chunk-MIEODAAV.cjs → chunk-YKQE7DDT.cjs} +9 -9
- package/dist/{chunk-MIEODAAV.cjs.map → chunk-YKQE7DDT.cjs.map} +1 -1
- package/dist/{chunk-XGOX5UQ2.js → chunk-YU2RPRI4.js} +4 -4
- package/dist/{chunk-TQZLC3BQ.cjs → chunk-ZHYT2HYT.cjs} +5 -5
- package/dist/{chunk-TQZLC3BQ.cjs.map → chunk-ZHYT2HYT.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/approval-batch-message.d.ts +13 -4
- package/dist/components/chat/approval-batch-message.d.ts.map +1 -1
- package/dist/components/chat/approval-request-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +4 -2
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +3 -1
- package/dist/components/chat/types/chat.types.d.ts +13 -0
- package/dist/components/chat/types/chat.types.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +21 -3
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/chat/types/message.types.d.ts +3 -0
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/auth/accept-invitation-form.d.ts +2 -2
- package/dist/components/features/auth/accept-invitation-form.d.ts.map +1 -1
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/features/notifications/notification-drawer.d.ts.map +1 -1
- package/dist/components/features/time-tracker/time-tracker-header-button.d.ts +5 -2
- package/dist/components/features/time-tracker/time-tracker-header-button.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +122 -120
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +11 -9
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/app-header.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-context.d.ts +34 -0
- package/dist/components/navigation/app-layout-context.d.ts.map +1 -0
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
- package/dist/components/navigation/app-layout.d.ts +2 -21
- package/dist/components/navigation/app-layout.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/index.cjs +4 -2
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +3 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/approval-batch-message.tsx +104 -88
- package/src/components/chat/approval-request-message.tsx +57 -3
- package/src/components/chat/chat-message-enhanced.tsx +5 -1
- package/src/components/chat/chat-message-list.tsx +3 -0
- package/src/components/chat/types/chat.types.ts +17 -0
- package/src/components/chat/types/component.types.ts +21 -3
- package/src/components/chat/types/message.types.ts +3 -0
- package/src/components/features/auth/accept-invitation-form.tsx +4 -4
- package/src/components/features/notifications/notification-drawer.tsx +72 -43
- package/src/components/features/time-tracker/time-tracker-header-button.tsx +46 -6
- package/src/components/navigation/app-header.tsx +13 -9
- package/src/components/navigation/app-layout-context.ts +46 -0
- package/src/components/navigation/app-layout-drawer.tsx +21 -11
- package/src/components/navigation/app-layout.tsx +17 -39
- package/src/stories/ApprovalBatchMessageClient.stories.tsx +20 -62
- package/dist/chunk-GNB5ERZP.cjs.map +0 -1
- package/dist/chunk-IDWKP5H5.js.map +0 -1
- /package/dist/{chunk-CQXHHVMR.js.map → chunk-4HV6RIDE.js.map} +0 -0
- /package/dist/{chunk-LFLFQ422.js.map → chunk-BY337N4T.js.map} +0 -0
- /package/dist/{chunk-M6ODI2F5.js.map → chunk-CAEKZPSH.js.map} +0 -0
- /package/dist/{chunk-JSFHTIKA.js.map → chunk-CPWR2BJG.js.map} +0 -0
- /package/dist/{chunk-44Q3IXZM.js.map → chunk-GM5M4L7O.js.map} +0 -0
- /package/dist/{chunk-BJPQJNSC.js.map → chunk-HPTHEA2A.js.map} +0 -0
- /package/dist/{chunk-WIMXB5EU.js.map → chunk-KWLSVTDN.js.map} +0 -0
- /package/dist/{chunk-KA4WXCN3.js.map → chunk-QHKU6473.js.map} +0 -0
- /package/dist/{chunk-NULFIYK2.js.map → chunk-QXD4OZCN.js.map} +0 -0
- /package/dist/{chunk-4OLIDOLO.js.map → chunk-SJYXTTUB.js.map} +0 -0
- /package/dist/{chunk-XGOX5UQ2.js.map → chunk-YU2RPRI4.js.map} +0 -0
package/package.json
CHANGED
|
@@ -9,8 +9,9 @@ import { ToolIcon } from "../tool-icon"
|
|
|
9
9
|
import { CheckCircleIcon, DotsLoaderIcon, XmarkCircleIcon, XmarkIcon } from "../icons-v2-generated"
|
|
10
10
|
import { ExpandChevron } from "./expand-chevron"
|
|
11
11
|
import { useCollapsible } from "./hooks/use-collapsible"
|
|
12
|
-
import { ArgRow,
|
|
12
|
+
import { ArgRow, ResultBlock } from "./tool-call-blocks"
|
|
13
13
|
import type {
|
|
14
|
+
ApprovalBlockVariant,
|
|
14
15
|
AssistantType,
|
|
15
16
|
ApprovalBatchExecutionState,
|
|
16
17
|
ApprovalBatchSegment,
|
|
@@ -18,7 +19,6 @@ import type {
|
|
|
18
19
|
} from "./types"
|
|
19
20
|
import {
|
|
20
21
|
COMMAND_BODY_ARG_KEYS,
|
|
21
|
-
getCommandBody,
|
|
22
22
|
getCommandText,
|
|
23
23
|
} from "./utils/tool-call-helpers"
|
|
24
24
|
|
|
@@ -42,11 +42,20 @@ export interface ApprovalBatchMessageProps extends React.HTMLAttributes<HTMLDivE
|
|
|
42
42
|
*/
|
|
43
43
|
showExecutionStatus?: boolean
|
|
44
44
|
/**
|
|
45
|
-
* Chat identity
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* Chat identity. Kept for prop parity with the message renderers; does NOT
|
|
46
|
+
* drive the styling anymore — use `variant` instead. An admin can be
|
|
47
|
+
* looking at a Fae dialog (tickets dialog client tab) and must still see
|
|
48
|
+
* the full admin block.
|
|
48
49
|
*/
|
|
49
50
|
assistantType?: AssistantType
|
|
51
|
+
/**
|
|
52
|
+
* Viewer variant. `'admin'` (default) = full block with command preview,
|
|
53
|
+
* expandable args/result and tool icon. `'client'` = end-client (Fae
|
|
54
|
+
* desktop app) card that shows ONLY the BE-generated title(s) plus the
|
|
55
|
+
* Approve/Reject buttons or the full-text resolved pill ("Approved by
|
|
56
|
+
* {name}") — commands and scripts are never rendered.
|
|
57
|
+
*/
|
|
58
|
+
variant?: ApprovalBlockVariant
|
|
50
59
|
/**
|
|
51
60
|
* Render the footer Approve/Reject buttons (or the resolved-status tag).
|
|
52
61
|
* Turn off when the host owns the actions row — e.g. the approval
|
|
@@ -119,45 +128,29 @@ interface ToolCallRowProps {
|
|
|
119
128
|
batchStatus: ApprovalBatchSegment["status"]
|
|
120
129
|
execution: ApprovalBatchExecutionState | undefined
|
|
121
130
|
showExecutionStatus: boolean
|
|
122
|
-
isClient: boolean
|
|
123
131
|
}
|
|
124
132
|
|
|
125
|
-
|
|
133
|
+
// ADMIN-only row: command preview header, expandable args/result. The client
|
|
134
|
+
// variant never renders tool calls — see the `variant === 'client'` branch of
|
|
135
|
+
// `<ApprovalBatchMessage>`.
|
|
136
|
+
function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExecutionStatus }: ToolCallRowProps) {
|
|
126
137
|
const command = getCommandText(call)
|
|
127
138
|
const args = getArgEntries(call)
|
|
128
139
|
const toolType = (call.toolType as ToolType) || ("OPENFRAME" as ToolType)
|
|
129
140
|
const { innerRef, containerStyle } = useCollapsible({ expanded })
|
|
130
141
|
const result = execution?.status === "done" ? execution.result : undefined
|
|
131
|
-
|
|
132
|
-
// and the raw command moves into the expanded body (Figma 1972-6100). Falls
|
|
133
|
-
// back to the command when no explanation is present. ADMIN is unchanged.
|
|
134
|
-
const explanation = call.toolExplanation?.trim()
|
|
135
|
-
const headerText = isClient && explanation ? explanation : command
|
|
136
|
-
const commandBody = isClient ? getCommandBody(call.toolCallArguments) : undefined
|
|
137
|
-
const hasExpandableBody = !!commandBody || args.length > 0 || (typeof result === "string" && result.length > 0)
|
|
142
|
+
const hasExpandableBody = args.length > 0 || (typeof result === "string" && result.length > 0)
|
|
138
143
|
|
|
139
144
|
return (
|
|
140
|
-
<div
|
|
141
|
-
className={cn(
|
|
142
|
-
"bg-ods-card flex flex-col items-start w-full",
|
|
143
|
-
// ADMIN: rows share one outer card, separated by a bottom border.
|
|
144
|
-
// CLIENT (Fae): each command is its own self-contained bordered box
|
|
145
|
-
// (Figma 1092-2804 / 1972-7925) — the outer component is frameless.
|
|
146
|
-
isClient
|
|
147
|
-
? "border border-ods-border rounded-[6px] overflow-hidden"
|
|
148
|
-
: "border-b border-ods-border last:border-b-0",
|
|
149
|
-
)}
|
|
150
|
-
>
|
|
145
|
+
<div className="bg-ods-card flex flex-col items-start w-full border-b border-ods-border last:border-b-0">
|
|
151
146
|
<button
|
|
152
147
|
type="button"
|
|
153
148
|
onClick={onToggle}
|
|
154
149
|
className="flex gap-[var(--spacing-system-xsf)] items-start w-full p-[var(--spacing-system-sf)] cursor-pointer text-left"
|
|
155
150
|
>
|
|
156
|
-
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
</div>
|
|
160
|
-
)}
|
|
151
|
+
<div className="flex items-center justify-center shrink-0 w-5 h-5">
|
|
152
|
+
<ToolIcon toolType={toolType} size={16} />
|
|
153
|
+
</div>
|
|
161
154
|
<div
|
|
162
155
|
className={cn(
|
|
163
156
|
"flex-1 min-w-0 text-h6",
|
|
@@ -166,7 +159,7 @@ function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExe
|
|
|
166
159
|
: "text-ods-text-secondary line-clamp-2 max-h-10 break-all",
|
|
167
160
|
)}
|
|
168
161
|
>
|
|
169
|
-
{
|
|
162
|
+
{command}
|
|
170
163
|
</div>
|
|
171
164
|
{showExecutionStatus && (
|
|
172
165
|
<div className="flex items-center justify-center shrink-0 w-5 h-5">
|
|
@@ -182,12 +175,6 @@ function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExe
|
|
|
182
175
|
<div ref={innerRef}>
|
|
183
176
|
{hasExpandableBody && (
|
|
184
177
|
<div className="flex flex-col gap-0 items-start w-full text-h6 px-[var(--spacing-system-sf)] pb-[var(--spacing-system-sf)] bg-ods-card">
|
|
185
|
-
{commandBody && (
|
|
186
|
-
<CommandBlock
|
|
187
|
-
command={commandBody}
|
|
188
|
-
className={args.length > 0 || result ? "mb-[var(--spacing-system-xsf)]" : undefined}
|
|
189
|
-
/>
|
|
190
|
-
)}
|
|
191
178
|
{args.map(([key, value]) => (
|
|
192
179
|
<ArgRow key={key} argKey={key} value={value} />
|
|
193
180
|
))}
|
|
@@ -206,17 +193,14 @@ function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExe
|
|
|
206
193
|
}
|
|
207
194
|
|
|
208
195
|
const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProps>(
|
|
209
|
-
({ className, data, onApprove, onReject, status = "pending", maxBodyHeight, resolvedByName, showExecutionStatus = true, assistantType, showFooterActions = true, ...props }, ref) => {
|
|
196
|
+
({ className, data, onApprove, onReject, status = "pending", maxBodyHeight, resolvedByName, showExecutionStatus = true, assistantType: _assistantType, variant = "admin", showFooterActions = true, ...props }, ref) => {
|
|
210
197
|
const [expandedId, setExpandedId] = useState<string | null>(null)
|
|
211
198
|
const [isProcessing, setIsProcessing] = useState(false)
|
|
212
|
-
const isClient =
|
|
199
|
+
const isClient = variant === "client"
|
|
213
200
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const explanations = isClient
|
|
218
|
-
? []
|
|
219
|
-
: data.toolCalls.map((c) => c.toolExplanation?.trim()).filter((s): s is string => !!s)
|
|
201
|
+
const explanations = data.toolCalls
|
|
202
|
+
.map((c) => c.toolExplanation?.trim())
|
|
203
|
+
.filter((s): s is string => !!s)
|
|
220
204
|
|
|
221
205
|
const handleApprove = async () => {
|
|
222
206
|
setIsProcessing(true)
|
|
@@ -236,6 +220,76 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
|
|
|
236
220
|
}
|
|
237
221
|
}
|
|
238
222
|
|
|
223
|
+
const actionButtons = (
|
|
224
|
+
<>
|
|
225
|
+
<Button
|
|
226
|
+
size="small-legacy"
|
|
227
|
+
variant="accent"
|
|
228
|
+
onClick={handleApprove}
|
|
229
|
+
disabled={isProcessing}
|
|
230
|
+
className={cn(
|
|
231
|
+
"bg-ods-accent hover:bg-ods-accent/90",
|
|
232
|
+
"text-h5 text-ods-bg",
|
|
233
|
+
"px-[var(--spacing-system-xsf)] h-8",
|
|
234
|
+
)}
|
|
235
|
+
>
|
|
236
|
+
Approve
|
|
237
|
+
</Button>
|
|
238
|
+
<Button
|
|
239
|
+
size="small-legacy"
|
|
240
|
+
variant="outline"
|
|
241
|
+
onClick={handleReject}
|
|
242
|
+
disabled={isProcessing}
|
|
243
|
+
className={cn(
|
|
244
|
+
"bg-ods-card border-ods-border",
|
|
245
|
+
"text-h5 text-ods-text-primary",
|
|
246
|
+
"hover:bg-ods-bg px-[var(--spacing-system-xsf)] h-8",
|
|
247
|
+
)}
|
|
248
|
+
>
|
|
249
|
+
Reject
|
|
250
|
+
</Button>
|
|
251
|
+
</>
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
// CLIENT (Fae end-user) card — Figma 203-11947 "fae-approval-block".
|
|
255
|
+
// One bordered card: BE-generated title(s) + Approve/Reject buttons or the
|
|
256
|
+
// full-text resolved pill ("Approved by {name}"). No commands, scripts,
|
|
257
|
+
// expansion or execution icons — the end client must not see them.
|
|
258
|
+
if (isClient) {
|
|
259
|
+
const titles = data.toolCalls
|
|
260
|
+
.map((c) => c.toolExplanation?.trim() || c.toolTitle?.trim())
|
|
261
|
+
.filter((s): s is string => !!s)
|
|
262
|
+
return (
|
|
263
|
+
<div
|
|
264
|
+
ref={ref}
|
|
265
|
+
className={cn(
|
|
266
|
+
"bg-ods-card border border-ods-border rounded-md p-[var(--spacing-system-mf)] mb-[var(--spacing-system-xsf)] flex flex-col gap-[var(--spacing-system-mf)]",
|
|
267
|
+
className,
|
|
268
|
+
)}
|
|
269
|
+
{...props}
|
|
270
|
+
>
|
|
271
|
+
{titles.length > 0 ? (
|
|
272
|
+
titles.map((title, i) => (
|
|
273
|
+
<p key={i} className="text-h4 text-ods-text-primary whitespace-pre-line break-words w-full">
|
|
274
|
+
{title}
|
|
275
|
+
</p>
|
|
276
|
+
))
|
|
277
|
+
) : (
|
|
278
|
+
<p className="text-h4 text-ods-text-primary w-full">Approval required</p>
|
|
279
|
+
)}
|
|
280
|
+
{showFooterActions && (status === "pending" ? (
|
|
281
|
+
<div className="flex gap-[var(--spacing-system-mf)] items-center w-full">
|
|
282
|
+
{actionButtons}
|
|
283
|
+
</div>
|
|
284
|
+
) : (
|
|
285
|
+
<div className="flex w-full">
|
|
286
|
+
<ApprovalStatusTag status={status} resolvedByName={resolvedByName} inlineResolver />
|
|
287
|
+
</div>
|
|
288
|
+
))}
|
|
289
|
+
</div>
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
239
293
|
const showFooterBlock = explanations.length > 0 || showFooterActions
|
|
240
294
|
|
|
241
295
|
return (
|
|
@@ -243,11 +297,7 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
|
|
|
243
297
|
ref={ref}
|
|
244
298
|
className={cn(
|
|
245
299
|
"flex flex-col mb-[var(--spacing-system-xsf)]",
|
|
246
|
-
|
|
247
|
-
// bordered command box(es) + button/status row stacked 16px apart.
|
|
248
|
-
isClient
|
|
249
|
-
? "gap-[var(--spacing-system-mf)]"
|
|
250
|
-
: "bg-ods-card border border-ods-border rounded-md overflow-hidden",
|
|
300
|
+
"bg-ods-card border border-ods-border rounded-md overflow-hidden",
|
|
251
301
|
className,
|
|
252
302
|
)}
|
|
253
303
|
{...props}
|
|
@@ -255,7 +305,6 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
|
|
|
255
305
|
<div
|
|
256
306
|
className={cn(
|
|
257
307
|
"flex flex-col",
|
|
258
|
-
isClient && "gap-[var(--spacing-system-mf)]",
|
|
259
308
|
maxBodyHeight != null && "overflow-y-auto overscroll-contain",
|
|
260
309
|
)}
|
|
261
310
|
style={maxBodyHeight != null ? { maxHeight: maxBodyHeight } : undefined}
|
|
@@ -273,20 +322,12 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
|
|
|
273
322
|
batchStatus={status}
|
|
274
323
|
execution={data.executions?.[call.toolExecutionRequestId]}
|
|
275
324
|
showExecutionStatus={showExecutionStatus}
|
|
276
|
-
isClient={isClient}
|
|
277
325
|
/>
|
|
278
326
|
))}
|
|
279
327
|
</div>
|
|
280
328
|
|
|
281
329
|
{showFooterBlock && (
|
|
282
|
-
<div
|
|
283
|
-
className={cn(
|
|
284
|
-
"flex flex-col gap-[var(--spacing-system-xsf)] items-start justify-center",
|
|
285
|
-
// ADMIN: padded footer inside the card, divided from the list.
|
|
286
|
-
// CLIENT (Fae): buttons/status sit flush outside the box.
|
|
287
|
-
!isClient && "bg-ods-card border-t border-ods-border p-[var(--spacing-system-sf)]",
|
|
288
|
-
)}
|
|
289
|
-
>
|
|
330
|
+
<div className="flex flex-col gap-[var(--spacing-system-xsf)] items-start justify-center bg-ods-card border-t border-ods-border p-[var(--spacing-system-sf)]">
|
|
290
331
|
{explanations.length > 0 && (
|
|
291
332
|
<ul className="list-disc pl-5 text-h6 text-ods-text-primary w-full">
|
|
292
333
|
{explanations.map((expl, i) => (
|
|
@@ -297,37 +338,12 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
|
|
|
297
338
|
|
|
298
339
|
{showFooterActions && (status === "pending" ? (
|
|
299
340
|
<div className="flex gap-[var(--spacing-system-xsf)] items-center">
|
|
300
|
-
|
|
301
|
-
size="small-legacy"
|
|
302
|
-
variant="accent"
|
|
303
|
-
onClick={handleApprove}
|
|
304
|
-
disabled={isProcessing}
|
|
305
|
-
className={cn(
|
|
306
|
-
"bg-ods-accent hover:bg-ods-accent/90",
|
|
307
|
-
"text-h5 text-ods-bg",
|
|
308
|
-
"px-[var(--spacing-system-xsf)] h-8",
|
|
309
|
-
)}
|
|
310
|
-
>
|
|
311
|
-
Approve
|
|
312
|
-
</Button>
|
|
313
|
-
<Button
|
|
314
|
-
size="small-legacy"
|
|
315
|
-
variant="outline"
|
|
316
|
-
onClick={handleReject}
|
|
317
|
-
disabled={isProcessing}
|
|
318
|
-
className={cn(
|
|
319
|
-
"bg-ods-card border-ods-border",
|
|
320
|
-
"text-h5 text-ods-text-primary",
|
|
321
|
-
"hover:bg-ods-bg px-[var(--spacing-system-xsf)] h-8",
|
|
322
|
-
)}
|
|
323
|
-
>
|
|
324
|
-
Reject
|
|
325
|
-
</Button>
|
|
341
|
+
{actionButtons}
|
|
326
342
|
</div>
|
|
327
343
|
) : (
|
|
328
344
|
<div className="flex items-center gap-[var(--spacing-system-xsf)]">
|
|
329
|
-
<ApprovalStatusTag status={status} resolvedByName={resolvedByName}
|
|
330
|
-
{
|
|
345
|
+
<ApprovalStatusTag status={status} resolvedByName={resolvedByName} />
|
|
346
|
+
{resolvedByName && (
|
|
331
347
|
<span className="text-h6 text-ods-text-secondary">by {resolvedByName}</span>
|
|
332
348
|
)}
|
|
333
349
|
</div>
|
|
@@ -5,6 +5,7 @@ import { cn } from "../../utils/cn"
|
|
|
5
5
|
import { Button } from "../ui/button"
|
|
6
6
|
import { Tag } from "../ui/tag"
|
|
7
7
|
import { Ban, CheckCircle, XCircle } from "lucide-react"
|
|
8
|
+
import { ApprovalStatusTag } from "./approval-batch-message"
|
|
8
9
|
import type { ApprovalRequestMessageProps } from "./types"
|
|
9
10
|
import type { ApprovalRequestField } from "./types/message.types"
|
|
10
11
|
|
|
@@ -78,9 +79,8 @@ function ApprovalCardBody({
|
|
|
78
79
|
|
|
79
80
|
const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessageProps>(
|
|
80
81
|
// `assistantType` is accepted for prop-parity with the batch card (so hosts
|
|
81
|
-
// can forward it uniformly); the
|
|
82
|
-
|
|
83
|
-
({ className, data, onApprove, onReject, status = 'pending', assistantType: _assistantType, ...props }, ref) => {
|
|
82
|
+
// can forward it uniformly); the viewer variant is driven by `variant`.
|
|
83
|
+
({ className, data, onApprove, onReject, status = 'pending', assistantType: _assistantType, variant = 'admin', resolvedByName, ...props }, ref) => {
|
|
84
84
|
const [isProcessing, setIsProcessing] = useState(false)
|
|
85
85
|
|
|
86
86
|
const handleApprove = async () => {
|
|
@@ -101,6 +101,60 @@ const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessage
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// CLIENT (Fae end-user) card — Figma 203-11947 "fae-approval-block".
|
|
105
|
+
// Shows ONLY the BE-generated title (`explanation`) plus the actions row
|
|
106
|
+
// or the full-text resolved pill; the raw command is never rendered.
|
|
107
|
+
if (variant === 'client') {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
ref={ref}
|
|
111
|
+
className={cn(
|
|
112
|
+
"bg-ods-card border border-ods-border rounded-md p-[var(--spacing-system-mf)] mb-[var(--spacing-system-xsf)] flex flex-col gap-[var(--spacing-system-mf)]",
|
|
113
|
+
className
|
|
114
|
+
)}
|
|
115
|
+
{...props}
|
|
116
|
+
>
|
|
117
|
+
<p className="text-h4 text-ods-text-primary whitespace-pre-line break-words w-full">
|
|
118
|
+
{data.explanation?.trim() || "Approval required"}
|
|
119
|
+
</p>
|
|
120
|
+
{status === 'pending' ? (
|
|
121
|
+
<div className="flex gap-[var(--spacing-system-mf)] items-center w-full">
|
|
122
|
+
<Button
|
|
123
|
+
size="small-legacy"
|
|
124
|
+
variant="accent"
|
|
125
|
+
onClick={handleApprove}
|
|
126
|
+
disabled={isProcessing}
|
|
127
|
+
className={cn(
|
|
128
|
+
"bg-ods-accent hover:bg-ods-accent/90",
|
|
129
|
+
"text-h5 text-ods-bg",
|
|
130
|
+
"px-[var(--spacing-system-xsf)] h-8"
|
|
131
|
+
)}
|
|
132
|
+
>
|
|
133
|
+
Approve
|
|
134
|
+
</Button>
|
|
135
|
+
<Button
|
|
136
|
+
size="small-legacy"
|
|
137
|
+
variant="outline"
|
|
138
|
+
onClick={handleReject}
|
|
139
|
+
disabled={isProcessing}
|
|
140
|
+
className={cn(
|
|
141
|
+
"bg-ods-card border-ods-border",
|
|
142
|
+
"text-h5 text-ods-text-primary",
|
|
143
|
+
"hover:bg-ods-bg px-[var(--spacing-system-xsf)] h-8"
|
|
144
|
+
)}
|
|
145
|
+
>
|
|
146
|
+
Reject
|
|
147
|
+
</Button>
|
|
148
|
+
</div>
|
|
149
|
+
) : (
|
|
150
|
+
<div className="flex w-full">
|
|
151
|
+
<ApprovalStatusTag status={status} resolvedByName={resolvedByName} inlineResolver />
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
104
158
|
return (
|
|
105
159
|
<div
|
|
106
160
|
ref={ref}
|
|
@@ -43,7 +43,7 @@ function normalizeContent(content: MessageContent): MessageSegment[] {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>(
|
|
46
|
-
({ className, role, content, name, avatar, isTyping = false, timestamp, showAvatar = true, assistantType, authorType: authorTypeProp, assistantIcon, chatRefs, contextItems, resolveContextIcon, renderContextItem, renderMention, renderEntityCard, NavLinkAnchor, ...props }, ref) => {
|
|
46
|
+
({ className, role, content, name, avatar, isTyping = false, timestamp, showAvatar = true, assistantType, approvalVariant, authorType: authorTypeProp, assistantIcon, chatRefs, contextItems, resolveContextIcon, renderContextItem, renderMention, renderEntityCard, NavLinkAnchor, ...props }, ref) => {
|
|
47
47
|
const isUser = role === 'user'
|
|
48
48
|
const isError = role === 'error'
|
|
49
49
|
const authorType = authorTypeProp ?? (isUser ? 'user' : assistantType === 'mingo' ? 'mingo' : 'fae')
|
|
@@ -546,9 +546,11 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
546
546
|
key={index}
|
|
547
547
|
data={segment.data}
|
|
548
548
|
status={segment.status}
|
|
549
|
+
resolvedByName={segment.resolvedByName}
|
|
549
550
|
onApprove={segment.onApprove}
|
|
550
551
|
onReject={segment.onReject}
|
|
551
552
|
assistantType={assistantType}
|
|
553
|
+
variant={approvalVariant}
|
|
552
554
|
/>
|
|
553
555
|
)
|
|
554
556
|
} else if (segment.type === 'approval_batch') {
|
|
@@ -561,6 +563,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
561
563
|
onApprove={segment.onApprove}
|
|
562
564
|
onReject={segment.onReject}
|
|
563
565
|
assistantType={assistantType}
|
|
566
|
+
variant={approvalVariant}
|
|
564
567
|
/>
|
|
565
568
|
)
|
|
566
569
|
} else if (segment.type === 'error') {
|
|
@@ -620,6 +623,7 @@ const MemoizedChatMessageEnhanced = memo(ChatMessageEnhanced, (prevProps, nextPr
|
|
|
620
623
|
prevProps.timestamp?.getTime() === nextProps.timestamp?.getTime() &&
|
|
621
624
|
prevProps.showAvatar === nextProps.showAvatar &&
|
|
622
625
|
prevProps.assistantType === nextProps.assistantType &&
|
|
626
|
+
prevProps.approvalVariant === nextProps.approvalVariant &&
|
|
623
627
|
prevProps.authorType === nextProps.authorType &&
|
|
624
628
|
prevProps.assistantIcon === nextProps.assistantIcon &&
|
|
625
629
|
prevProps.className === nextProps.className &&
|
|
@@ -100,6 +100,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
100
100
|
fullWidth = false,
|
|
101
101
|
contentClassName,
|
|
102
102
|
assistantType,
|
|
103
|
+
approvalVariant,
|
|
103
104
|
assistantIcon,
|
|
104
105
|
pendingApprovals,
|
|
105
106
|
hasNextPage,
|
|
@@ -535,6 +536,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
535
536
|
avatar={showAvatars ? message.avatar : null}
|
|
536
537
|
showAvatar={showAvatars}
|
|
537
538
|
assistantType={message.assistantType || assistantType}
|
|
539
|
+
approvalVariant={approvalVariant}
|
|
538
540
|
authorType={message.authorType}
|
|
539
541
|
assistantIcon={message.role !== 'user' ? assistantIcon : undefined}
|
|
540
542
|
chatRefs={message.chatRefs}
|
|
@@ -590,6 +592,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
590
592
|
timestamp={new Date()}
|
|
591
593
|
showAvatar={showAvatars}
|
|
592
594
|
assistantType={assistantType}
|
|
595
|
+
approvalVariant={approvalVariant}
|
|
593
596
|
assistantIcon={assistantIcon}
|
|
594
597
|
NavLinkAnchor={NavLinkAnchor}
|
|
595
598
|
className="py-3"
|
|
@@ -64,6 +64,23 @@ export const APPROVAL_STATUS = {
|
|
|
64
64
|
|
|
65
65
|
export type ChatApprovalStatus = typeof APPROVAL_STATUS[keyof typeof APPROVAL_STATUS]
|
|
66
66
|
|
|
67
|
+
// ========== Approval Block Variant Definitions ==========
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Who is LOOKING at an approval block — not which assistant produced it.
|
|
71
|
+
* `admin` (default) shows the full block: command/script preview, expandable
|
|
72
|
+
* args/result, tool icon. `client` (end-user Fae desktop app) shows only the
|
|
73
|
+
* BE-generated title plus Approve/Reject or the resolved-status pill — raw
|
|
74
|
+
* commands and scripts must never reach the end client. Admin surfaces that
|
|
75
|
+
* render a Fae dialog (tickets dialog client tab) keep the admin variant.
|
|
76
|
+
*/
|
|
77
|
+
export const APPROVAL_BLOCK_VARIANT = {
|
|
78
|
+
ADMIN: 'admin',
|
|
79
|
+
CLIENT: 'client',
|
|
80
|
+
} as const
|
|
81
|
+
|
|
82
|
+
export type ApprovalBlockVariant = typeof APPROVAL_BLOCK_VARIANT[keyof typeof APPROVAL_BLOCK_VARIANT]
|
|
83
|
+
|
|
67
84
|
// ========== Connection Status Definitions ==========
|
|
68
85
|
|
|
69
86
|
export const CONNECTION_STATUS = {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { ComponentType, HTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react'
|
|
6
|
-
import type { AssistantType, AuthorType, ChatApprovalStatus, ConnectionStatus } from './chat.types'
|
|
6
|
+
import type { ApprovalBlockVariant, AssistantType, AuthorType, ChatApprovalStatus, ConnectionStatus } from './chat.types'
|
|
7
7
|
import type { ApprovalRequestData, Message, MessageSegment, ToolExecutionData } from './message.types'
|
|
8
8
|
import type { ChatRef } from '../chat-ref.types'
|
|
9
9
|
import type { ChatContextItem } from './context-item.types'
|
|
@@ -119,6 +119,11 @@ export interface ChatMessageEnhancedProps extends Omit<HTMLAttributes<HTMLDivEle
|
|
|
119
119
|
name?: string
|
|
120
120
|
assistantType?: AssistantType
|
|
121
121
|
authorType?: AuthorType
|
|
122
|
+
/** Viewer variant for approval blocks in this message's segments.
|
|
123
|
+
* `'admin'` (default) = full command block; `'client'` = end-client
|
|
124
|
+
* (Fae desktop app) title-only card. Forwarded to
|
|
125
|
+
* ApprovalRequestMessage / ApprovalBatchMessage. */
|
|
126
|
+
approvalVariant?: ApprovalBlockVariant
|
|
122
127
|
assistantIcon?: React.ReactNode
|
|
123
128
|
avatar?: string | null
|
|
124
129
|
timestamp?: Date
|
|
@@ -229,6 +234,12 @@ export interface ChatMessageListProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
229
234
|
* override (custom max-w value, etc.). */
|
|
230
235
|
contentClassName?: string
|
|
231
236
|
assistantType?: AssistantType
|
|
237
|
+
/** Viewer variant for approval blocks in every rendered message (incl. the
|
|
238
|
+
* sticky pending-approvals footer). `'admin'` (default) = full command
|
|
239
|
+
* block; `'client'` = end-client (Fae desktop app) title-only card. Set to
|
|
240
|
+
* `'client'` ONLY on true end-client surfaces — admin views of a Fae
|
|
241
|
+
* dialog (tickets dialog client tab) keep the default. */
|
|
242
|
+
approvalVariant?: ApprovalBlockVariant
|
|
232
243
|
assistantIcon?: React.ReactNode
|
|
233
244
|
pendingApprovals?: MessageSegment[]
|
|
234
245
|
onApprove?: (requestId?: string) => void | Promise<void>
|
|
@@ -488,9 +499,16 @@ export interface ApprovalRequestMessageProps extends HTMLAttributes<HTMLDivEleme
|
|
|
488
499
|
onReject?: (requestId?: string) => void | Promise<void>
|
|
489
500
|
status?: ChatApprovalStatus
|
|
490
501
|
disabled?: boolean
|
|
491
|
-
/** Chat identity
|
|
492
|
-
* the
|
|
502
|
+
/** Chat identity. Accepted for parity with the batch card; does NOT drive
|
|
503
|
+
* the styling — use `variant`. */
|
|
493
504
|
assistantType?: AssistantType
|
|
505
|
+
/** Viewer variant. `'admin'` (default) = full card with the raw command;
|
|
506
|
+
* `'client'` = end-client (Fae desktop app) card with only the
|
|
507
|
+
* BE-generated title (`explanation`) + actions/status pill. */
|
|
508
|
+
variant?: ApprovalBlockVariant
|
|
509
|
+
/** Display name of the user who resolved the request; baked into the
|
|
510
|
+
* client variant's full-text status pill ("Approved by {name}"). */
|
|
511
|
+
resolvedByName?: string | null
|
|
494
512
|
}
|
|
495
513
|
|
|
496
514
|
// ========== Error Message Display Props ==========
|
|
@@ -171,6 +171,9 @@ export type ApprovalRequestSegment = {
|
|
|
171
171
|
type: 'approval_request'
|
|
172
172
|
data: ApprovalRequestData & { approvalType?: string }
|
|
173
173
|
status?: ChatApprovalStatus
|
|
174
|
+
/** Display name of the user who resolved the request; baked into the client
|
|
175
|
+
* variant's full-text status pill ("Approved by {name}"). */
|
|
176
|
+
resolvedByName?: string | null
|
|
174
177
|
onApprove?: (requestId?: string) => void | Promise<void>
|
|
175
178
|
onReject?: (requestId?: string) => void | Promise<void>
|
|
176
179
|
}
|
|
@@ -7,8 +7,8 @@ import type { AuthSsoProvider } from './sso-providers'
|
|
|
7
7
|
import { SsoProviderButtons } from './sso-providers'
|
|
8
8
|
|
|
9
9
|
export interface AcceptInvitationFormProps {
|
|
10
|
-
/** Invited email — shown read-only
|
|
11
|
-
email
|
|
10
|
+
/** Invited email — shown read-only. The field is hidden when empty/absent. */
|
|
11
|
+
email?: string
|
|
12
12
|
/** SSO providers offered to accept the invitation. */
|
|
13
13
|
ssoProviders: AuthSsoProvider[]
|
|
14
14
|
onSsoClick: (provider: AuthSsoProvider) => void
|
|
@@ -53,8 +53,8 @@ export function AcceptInvitationForm({
|
|
|
53
53
|
<p className="text-h4 text-ods-text-secondary">{subtitle}</p>
|
|
54
54
|
</div>
|
|
55
55
|
|
|
56
|
-
{/* Invited email — read-only */}
|
|
57
|
-
<Input type="email" label={emailLabel} value={email} disabled
|
|
56
|
+
{/* Invited email — read-only; hidden when the backend didn't provide it */}
|
|
57
|
+
{email && <Input type="email" label={emailLabel} value={email} disabled />}
|
|
58
58
|
|
|
59
59
|
{/* SSO providers */}
|
|
60
60
|
<SsoProviderButtons
|