@flamingo-stack/openframe-frontend-core 0.0.351 → 0.0.352
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-DJZ64AF6.js → chunk-3QWEET5C.js} +2 -2
- package/dist/{chunk-VZ7TICMR.cjs → chunk-3SYTPXCP.cjs} +37 -37
- package/dist/{chunk-VZ7TICMR.cjs.map → chunk-3SYTPXCP.cjs.map} +1 -1
- package/dist/{chunk-63ISTNQW.cjs → chunk-42PSJKMY.cjs} +25 -25
- package/dist/{chunk-63ISTNQW.cjs.map → chunk-42PSJKMY.cjs.map} +1 -1
- package/dist/{chunk-YBL3JURC.js → chunk-66D3JL6J.js} +4 -4
- package/dist/{chunk-VY6AAUCK.js → chunk-7CTSKYAV.js} +2 -2
- package/dist/{chunk-QGBJKB6V.cjs → chunk-DY4Y7QQB.cjs} +31 -31
- package/dist/{chunk-QGBJKB6V.cjs.map → chunk-DY4Y7QQB.cjs.map} +1 -1
- package/dist/{chunk-46Z3O6SN.cjs → chunk-ED72ZQV7.cjs} +5 -5
- package/dist/{chunk-46Z3O6SN.cjs.map → chunk-ED72ZQV7.cjs.map} +1 -1
- package/dist/{chunk-MLW25MGY.js → chunk-FQ4VGHPO.js} +2 -2
- package/dist/{chunk-GJZL72ON.cjs → chunk-FU5KF6MA.cjs} +7 -7
- package/dist/{chunk-GJZL72ON.cjs.map → chunk-FU5KF6MA.cjs.map} +1 -1
- package/dist/{chunk-WB2EWYSF.js → chunk-GGCOLMNS.js} +2 -2
- package/dist/{chunk-NEIOYJIE.cjs → chunk-HPVOWKIO.cjs} +15 -15
- package/dist/{chunk-NEIOYJIE.cjs.map → chunk-HPVOWKIO.cjs.map} +1 -1
- package/dist/{chunk-A6YFD2SK.js → chunk-HSZJGPVA.js} +173 -116
- package/dist/chunk-HSZJGPVA.js.map +1 -0
- package/dist/{chunk-NIRMBPFP.cjs → chunk-IHXJKIFM.cjs} +12 -12
- package/dist/{chunk-NIRMBPFP.cjs.map → chunk-IHXJKIFM.cjs.map} +1 -1
- package/dist/{chunk-5F45BY24.js → chunk-IM5HXLFT.js} +2 -2
- package/dist/{chunk-JBWB3U3P.js → chunk-JQGLBAGS.js} +2 -2
- package/dist/{chunk-BMQ5CBFD.cjs → chunk-K2NJ5VHQ.cjs} +62 -62
- package/dist/{chunk-BMQ5CBFD.cjs.map → chunk-K2NJ5VHQ.cjs.map} +1 -1
- package/dist/{chunk-Q3VK7WI2.js → chunk-M247JWGY.js} +4 -4
- package/dist/{chunk-4WVVMKMC.js → chunk-N7BREY67.js} +4 -4
- package/dist/{chunk-YIFJJCFB.js → chunk-ONMA32BY.js} +2 -2
- package/dist/{chunk-HMUIRZ46.cjs → chunk-QP7TLRLH.cjs} +172 -115
- package/dist/chunk-QP7TLRLH.cjs.map +1 -0
- package/dist/{chunk-UQFTSFIF.cjs → chunk-RTKRXPPG.cjs} +26 -26
- package/dist/{chunk-UQFTSFIF.cjs.map → chunk-RTKRXPPG.cjs.map} +1 -1
- package/dist/{chunk-TH3HPNMO.cjs → chunk-T2ARJIH6.cjs} +56 -56
- package/dist/{chunk-TH3HPNMO.cjs.map → chunk-T2ARJIH6.cjs.map} +1 -1
- package/dist/{chunk-EKU6D26E.js → chunk-TM455OW5.js} +4 -4
- package/dist/{chunk-XZVVLO4V.cjs → chunk-ZRLPXSZC.cjs} +9 -9
- package/dist/{chunk-XZVVLO4V.cjs.map → chunk-ZRLPXSZC.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 +7 -1
- 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/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/tool-call-blocks.d.ts.map +1 -1
- package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +6 -0
- package/dist/components/chat/types/component.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/index.cjs +2 -2
- package/dist/components/features/index.js +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 +120 -120
- package/dist/components/index.js +9 -9
- 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 +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/chat/approval-batch-message.tsx +82 -27
- package/src/components/chat/approval-request-message.tsx +27 -19
- package/src/components/chat/chat-message-enhanced.tsx +3 -0
- package/src/components/chat/tool-call-blocks.tsx +17 -6
- package/src/components/chat/tool-execution-display.tsx +23 -8
- package/src/components/chat/types/component.types.ts +6 -0
- package/src/stories/ApprovalBatchMessage.stories.tsx +169 -0
- package/src/stories/ApprovalBatchMessageClient.stories.tsx +172 -0
- package/src/stories/ApprovalRequestMessage.stories.tsx +95 -0
- package/src/stories/ToolExecutionDisplay.stories.tsx +1 -1
- package/src/stories/ToolExecutionDisplayClient.stories.tsx +64 -0
- package/dist/chunk-A6YFD2SK.js.map +0 -1
- package/dist/chunk-HMUIRZ46.cjs.map +0 -1
- /package/dist/{chunk-DJZ64AF6.js.map → chunk-3QWEET5C.js.map} +0 -0
- /package/dist/{chunk-YBL3JURC.js.map → chunk-66D3JL6J.js.map} +0 -0
- /package/dist/{chunk-VY6AAUCK.js.map → chunk-7CTSKYAV.js.map} +0 -0
- /package/dist/{chunk-MLW25MGY.js.map → chunk-FQ4VGHPO.js.map} +0 -0
- /package/dist/{chunk-WB2EWYSF.js.map → chunk-GGCOLMNS.js.map} +0 -0
- /package/dist/{chunk-5F45BY24.js.map → chunk-IM5HXLFT.js.map} +0 -0
- /package/dist/{chunk-JBWB3U3P.js.map → chunk-JQGLBAGS.js.map} +0 -0
- /package/dist/{chunk-Q3VK7WI2.js.map → chunk-M247JWGY.js.map} +0 -0
- /package/dist/{chunk-4WVVMKMC.js.map → chunk-N7BREY67.js.map} +0 -0
- /package/dist/{chunk-YIFJJCFB.js.map → chunk-ONMA32BY.js.map} +0 -0
- /package/dist/{chunk-EKU6D26E.js.map → chunk-TM455OW5.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import { forwardRef, useState } from "react"
|
|
|
4
4
|
import { cn } from "../../utils/cn"
|
|
5
5
|
import { Button } from "../ui/button"
|
|
6
6
|
import { Tag } from "../ui/tag"
|
|
7
|
-
import { CheckCircle, XCircle } from "lucide-react"
|
|
7
|
+
import { Ban, CheckCircle, XCircle } from "lucide-react"
|
|
8
8
|
import type { ApprovalRequestMessageProps } from "./types"
|
|
9
9
|
import type { ApprovalRequestField } from "./types/message.types"
|
|
10
10
|
|
|
@@ -18,13 +18,16 @@ import type { ApprovalRequestField } from "./types/message.types"
|
|
|
18
18
|
*/
|
|
19
19
|
function ApprovalFieldList({ fields }: { fields: ApprovalRequestField[] }) {
|
|
20
20
|
return (
|
|
21
|
-
<dl className="flex flex-col gap-2.5 mt-
|
|
21
|
+
<dl className="flex flex-col gap-2.5 mt-[var(--spacing-system-xxs)]">
|
|
22
22
|
{fields.map((f, i) => (
|
|
23
23
|
<div key={i} className="flex flex-col gap-0.5">
|
|
24
|
+
{/* NOTE: 11px DM Sans small-caps label has no ODS composite token
|
|
25
|
+
(h5 is Azeret Mono, h6 is 14px) — flagged for a future caption
|
|
26
|
+
token; kept as-is rather than forcing a mismatched h-class. */}
|
|
24
27
|
<dt className="font-['DM_Sans'] font-semibold text-[11px] uppercase tracking-wide text-ods-text-tertiary leading-4">
|
|
25
28
|
{f.label}
|
|
26
29
|
</dt>
|
|
27
|
-
<dd className="
|
|
30
|
+
<dd className="text-h6 text-ods-text-primary whitespace-pre-wrap break-words">
|
|
28
31
|
{f.value}
|
|
29
32
|
</dd>
|
|
30
33
|
</div>
|
|
@@ -49,9 +52,9 @@ function ApprovalCardBody({
|
|
|
49
52
|
data: ApprovalRequestMessageProps['data']
|
|
50
53
|
}) {
|
|
51
54
|
return (
|
|
52
|
-
<div className="flex flex-col gap-
|
|
53
|
-
<div className="bg-ods-bg border border-ods-border rounded-md p-
|
|
54
|
-
<code className="
|
|
55
|
+
<div className="flex flex-col gap-[var(--spacing-system-xxs)]">
|
|
56
|
+
<div className="bg-ods-bg border border-ods-border rounded-md p-[var(--spacing-system-sf)] flex gap-[var(--spacing-system-xsf)] items-start max-h-32 overflow-y-auto">
|
|
57
|
+
<code className="text-h6 text-ods-text-primary flex-1 whitespace-pre-wrap break-words">
|
|
55
58
|
{data.command}
|
|
56
59
|
</code>
|
|
57
60
|
{data.icon && (
|
|
@@ -64,7 +67,7 @@ function ApprovalCardBody({
|
|
|
64
67
|
<ApprovalFieldList fields={data.fields} />
|
|
65
68
|
) : (
|
|
66
69
|
data.explanation && (
|
|
67
|
-
<p className="
|
|
70
|
+
<p className="text-h6 text-ods-text-secondary whitespace-pre-line break-words">
|
|
68
71
|
{data.explanation}
|
|
69
72
|
</p>
|
|
70
73
|
)
|
|
@@ -74,7 +77,10 @@ function ApprovalCardBody({
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessageProps>(
|
|
77
|
-
|
|
80
|
+
// `assistantType` is accepted for prop-parity with the batch card (so hosts
|
|
81
|
+
// can forward it uniformly); the legacy single-command card has no
|
|
82
|
+
// icon/divider to vary, so it renders identically for admin and client today.
|
|
83
|
+
({ className, data, onApprove, onReject, status = 'pending', assistantType: _assistantType, ...props }, ref) => {
|
|
78
84
|
const [isProcessing, setIsProcessing] = useState(false)
|
|
79
85
|
|
|
80
86
|
const handleApprove = async () => {
|
|
@@ -99,14 +105,14 @@ const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessage
|
|
|
99
105
|
<div
|
|
100
106
|
ref={ref}
|
|
101
107
|
className={cn(
|
|
102
|
-
"bg-ods-card border border-ods-border rounded-md p-
|
|
108
|
+
"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)]",
|
|
103
109
|
className
|
|
104
110
|
)}
|
|
105
111
|
{...props}
|
|
106
112
|
>
|
|
107
113
|
<ApprovalCardBody data={data} />
|
|
108
114
|
{status === 'pending' ? (
|
|
109
|
-
<div className="flex gap-
|
|
115
|
+
<div className="flex gap-[var(--spacing-system-mf)] items-center">
|
|
110
116
|
<Button
|
|
111
117
|
size="small-legacy"
|
|
112
118
|
variant="accent"
|
|
@@ -114,8 +120,8 @@ const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessage
|
|
|
114
120
|
disabled={isProcessing}
|
|
115
121
|
className={cn(
|
|
116
122
|
"bg-ods-accent hover:bg-ods-accent/90",
|
|
117
|
-
"
|
|
118
|
-
"px-
|
|
123
|
+
"text-h5 text-ods-bg",
|
|
124
|
+
"px-[var(--spacing-system-xsf)] h-8"
|
|
119
125
|
)}
|
|
120
126
|
>
|
|
121
127
|
Approve
|
|
@@ -127,8 +133,8 @@ const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessage
|
|
|
127
133
|
disabled={isProcessing}
|
|
128
134
|
className={cn(
|
|
129
135
|
"bg-ods-card border-ods-border",
|
|
130
|
-
"
|
|
131
|
-
"hover:bg-ods-bg px-
|
|
136
|
+
"text-h5 text-ods-text-primary",
|
|
137
|
+
"hover:bg-ods-bg px-[var(--spacing-system-xsf)] h-8"
|
|
132
138
|
)}
|
|
133
139
|
>
|
|
134
140
|
Reject
|
|
@@ -136,11 +142,13 @@ const ApprovalRequestMessage = forwardRef<HTMLDivElement, ApprovalRequestMessage
|
|
|
136
142
|
</div>
|
|
137
143
|
) : (
|
|
138
144
|
<div className="flex">
|
|
139
|
-
|
|
140
|
-
label=
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
{status === 'approved' ? (
|
|
146
|
+
<Tag label="Approved" variant="success" icon={<CheckCircle className="w-4 h-4" />} />
|
|
147
|
+
) : status === 'cancelled' ? (
|
|
148
|
+
<Tag label="Canceled" variant="grey" icon={<Ban className="w-4 h-4" />} />
|
|
149
|
+
) : (
|
|
150
|
+
<Tag label="Rejected" variant="error" icon={<XCircle className="w-4 h-4" />} />
|
|
151
|
+
)}
|
|
144
152
|
</div>
|
|
145
153
|
)}
|
|
146
154
|
</div>
|
|
@@ -537,6 +537,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
537
537
|
<ToolExecutionDisplay
|
|
538
538
|
key={index}
|
|
539
539
|
message={segment.data}
|
|
540
|
+
assistantType={assistantType}
|
|
540
541
|
/>
|
|
541
542
|
)
|
|
542
543
|
} else if (segment.type === 'approval_request') {
|
|
@@ -547,6 +548,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
547
548
|
status={segment.status}
|
|
548
549
|
onApprove={segment.onApprove}
|
|
549
550
|
onReject={segment.onReject}
|
|
551
|
+
assistantType={assistantType}
|
|
550
552
|
/>
|
|
551
553
|
)
|
|
552
554
|
} else if (segment.type === 'approval_batch') {
|
|
@@ -558,6 +560,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
558
560
|
resolvedByName={segment.resolvedByName}
|
|
559
561
|
onApprove={segment.onApprove}
|
|
560
562
|
onReject={segment.onReject}
|
|
563
|
+
assistantType={assistantType}
|
|
561
564
|
/>
|
|
562
565
|
)
|
|
563
566
|
} else if (segment.type === 'error') {
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
import { cn } from "../../utils/cn"
|
|
4
4
|
import { formatToolArgValue, formatToolResult } from "./utils/tool-call-helpers"
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Monospace code body shared by ArgRow / ResultBlock.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: ODS exposes no mono/code typography token (only the composite
|
|
10
|
+
* `text-h1`–`text-h6` DM Sans / Azeret-Mono headings), so the 12px code size
|
|
11
|
+
* stays as `text-xs` here. Flagged for a future `text-code` token rather than
|
|
12
|
+
* forcing an h-class that would change the intended density.
|
|
13
|
+
*/
|
|
14
|
+
const CODE_PRE_CLASS =
|
|
15
|
+
"bg-ods-bg border border-ods-border rounded-md p-[var(--spacing-system-sf)] w-full max-h-64 overflow-auto text-xs leading-5 text-ods-text-primary font-mono whitespace-pre"
|
|
16
|
+
|
|
6
17
|
/**
|
|
7
18
|
* Single arg row: inline `key: value` or labeled `<pre>` block.
|
|
8
19
|
* Shared between ApprovalBatchMessage and ToolExecutionDisplay so both
|
|
@@ -13,7 +24,7 @@ export function ArgRow({ argKey, value }: { argKey: string; value: unknown }) {
|
|
|
13
24
|
|
|
14
25
|
if (formatted.kind === "inline") {
|
|
15
26
|
return (
|
|
16
|
-
<div className="flex gap-
|
|
27
|
+
<div className="flex gap-[var(--spacing-system-xxs)] items-start w-full">
|
|
17
28
|
<span className="shrink-0 text-ods-text-secondary">{argKey}:</span>
|
|
18
29
|
<span className="flex-1 min-w-0 text-ods-text-primary break-all">{formatted.text}</span>
|
|
19
30
|
</div>
|
|
@@ -21,9 +32,9 @@ export function ArgRow({ argKey, value }: { argKey: string; value: unknown }) {
|
|
|
21
32
|
}
|
|
22
33
|
|
|
23
34
|
return (
|
|
24
|
-
<div className="flex flex-col gap-
|
|
35
|
+
<div className="flex flex-col gap-[var(--spacing-system-xxs)] items-start w-full">
|
|
25
36
|
<span className="text-ods-text-secondary">{argKey}:</span>
|
|
26
|
-
<pre className=
|
|
37
|
+
<pre className={CODE_PRE_CLASS}>
|
|
27
38
|
<code>{formatted.text}</code>
|
|
28
39
|
</pre>
|
|
29
40
|
</div>
|
|
@@ -40,7 +51,7 @@ export function ResultBlock({ result, className }: { result: string | undefined
|
|
|
40
51
|
|
|
41
52
|
if (formatted.kind === "inline") {
|
|
42
53
|
return (
|
|
43
|
-
<div className={cn("flex flex-col gap-
|
|
54
|
+
<div className={cn("flex flex-col gap-[var(--spacing-system-xxs)] items-start w-full", className)}>
|
|
44
55
|
<span className="text-ods-text-secondary">Result:</span>
|
|
45
56
|
<span className="text-ods-text-primary break-all">{formatted.text}</span>
|
|
46
57
|
</div>
|
|
@@ -48,9 +59,9 @@ export function ResultBlock({ result, className }: { result: string | undefined
|
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
return (
|
|
51
|
-
<div className={cn("flex flex-col gap-
|
|
62
|
+
<div className={cn("flex flex-col gap-[var(--spacing-system-xxs)] items-start w-full", className)}>
|
|
52
63
|
<span className="text-ods-text-secondary">Result:</span>
|
|
53
|
-
<pre className=
|
|
64
|
+
<pre className={CODE_PRE_CLASS}>
|
|
54
65
|
<code>{formatted.text}</code>
|
|
55
66
|
</pre>
|
|
56
67
|
</div>
|
|
@@ -15,9 +15,10 @@ import type { ToolExecutionDisplayProps } from "./types"
|
|
|
15
15
|
const COMMAND_BODY_KEYS = new Set<string>(COMMAND_BODY_ARG_KEYS)
|
|
16
16
|
|
|
17
17
|
const ToolExecutionDisplay = forwardRef<HTMLDivElement, ToolExecutionDisplayProps>(
|
|
18
|
-
({ className, message, ...props }, ref) => {
|
|
18
|
+
({ className, message, assistantType, ...props }, ref) => {
|
|
19
19
|
const [expanded, setExpanded] = useState(false)
|
|
20
20
|
const { innerRef, containerStyle } = useCollapsible({ expanded })
|
|
21
|
+
const isClient = assistantType === "fae"
|
|
21
22
|
|
|
22
23
|
const isExecuting = message.type === "EXECUTING_TOOL"
|
|
23
24
|
const isExecuted = message.type === "EXECUTED_TOOL"
|
|
@@ -54,6 +55,8 @@ const ToolExecutionDisplay = forwardRef<HTMLDivElement, ToolExecutionDisplayProp
|
|
|
54
55
|
<div
|
|
55
56
|
ref={ref}
|
|
56
57
|
className={cn(
|
|
58
|
+
// The command running block keeps its bordered box in both chats
|
|
59
|
+
// (Figma 1972-6109). CLIENT (Fae) only drops the tool icon below.
|
|
57
60
|
"bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full flex flex-col",
|
|
58
61
|
className,
|
|
59
62
|
)}
|
|
@@ -64,12 +67,14 @@ const ToolExecutionDisplay = forwardRef<HTMLDivElement, ToolExecutionDisplayProp
|
|
|
64
67
|
className="flex gap-[var(--spacing-system-xs)] items-start p-[var(--spacing-system-s)] cursor-pointer text-left w-full"
|
|
65
68
|
onClick={() => setExpanded((prev) => !prev)}
|
|
66
69
|
>
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
+
{!isClient && (
|
|
71
|
+
<div className="flex items-center justify-center shrink-0 w-5 h-5">
|
|
72
|
+
<ToolIcon toolType={integratedToolType} size={16} />
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
70
75
|
<div
|
|
71
76
|
className={cn(
|
|
72
|
-
"flex-1 min-w-0 text-
|
|
77
|
+
"flex-1 min-w-0 text-h6",
|
|
73
78
|
expanded
|
|
74
79
|
? "text-ods-text-primary whitespace-pre-wrap break-all"
|
|
75
80
|
: "text-ods-text-secondary line-clamp-2 max-h-10 break-all",
|
|
@@ -86,13 +91,23 @@ const ToolExecutionDisplay = forwardRef<HTMLDivElement, ToolExecutionDisplayProp
|
|
|
86
91
|
<div className="w-full" style={containerStyle}>
|
|
87
92
|
<div ref={innerRef}>
|
|
88
93
|
{hasBody && (
|
|
89
|
-
<div className="flex flex-col gap-
|
|
94
|
+
<div className="flex flex-col gap-0 items-start w-full text-h6 p-[var(--spacing-system-sf)] bg-ods-card">
|
|
90
95
|
{argEntries.map(([key, value]) => (
|
|
91
96
|
<ArgRow key={key} argKey={key} value={value} />
|
|
92
97
|
))}
|
|
93
|
-
{hasResult &&
|
|
98
|
+
{hasResult && (
|
|
99
|
+
<ResultBlock
|
|
100
|
+
result={message.result}
|
|
101
|
+
className={argEntries.length > 0 ? "mt-[var(--spacing-system-xsf)]" : undefined}
|
|
102
|
+
/>
|
|
103
|
+
)}
|
|
94
104
|
{isExecuting && (
|
|
95
|
-
<div
|
|
105
|
+
<div
|
|
106
|
+
className={cn(
|
|
107
|
+
"flex flex-col gap-[var(--spacing-system-xxs)] items-start w-full",
|
|
108
|
+
argEntries.length > 0 && "mt-[var(--spacing-system-xsf)]",
|
|
109
|
+
)}
|
|
110
|
+
>
|
|
96
111
|
<span className="text-ods-text-secondary">Result:</span>
|
|
97
112
|
<DotsLoaderIcon size={16} className="text-ods-text-secondary" />
|
|
98
113
|
</div>
|
|
@@ -461,6 +461,9 @@ export interface ChatTypingIndicatorProps extends HTMLAttributes<HTMLDivElement>
|
|
|
461
461
|
|
|
462
462
|
export interface ToolExecutionDisplayProps extends HTMLAttributes<HTMLDivElement> {
|
|
463
463
|
message: ToolExecutionData
|
|
464
|
+
/** Chat identity. `'fae'` (client) hides the tool icon; `'mingo'`/undefined
|
|
465
|
+
* keep the admin layout. */
|
|
466
|
+
assistantType?: AssistantType
|
|
464
467
|
}
|
|
465
468
|
|
|
466
469
|
// ========== Approval Request Message Props ==========
|
|
@@ -471,6 +474,9 @@ export interface ApprovalRequestMessageProps extends HTMLAttributes<HTMLDivEleme
|
|
|
471
474
|
onReject?: (requestId?: string) => void | Promise<void>
|
|
472
475
|
status?: ChatApprovalStatus
|
|
473
476
|
disabled?: boolean
|
|
477
|
+
/** Chat identity; drives the CLIENT (Fae) styling. Accepted for parity with
|
|
478
|
+
* the batch card. `'fae'` = client, `'mingo'`/undefined = admin. */
|
|
479
|
+
assistantType?: AssistantType
|
|
474
480
|
}
|
|
475
481
|
|
|
476
482
|
// ========== Error Message Display Props ==========
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
import { ApprovalBatchMessage } from "../components/chat/approval-batch-message";
|
|
5
|
+
import type {
|
|
6
|
+
ApprovalBatchData,
|
|
7
|
+
ApprovalBatchExecutionState,
|
|
8
|
+
PendingToolCallData,
|
|
9
|
+
} from "../components/chat/types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The "Command Block" from Figma (open-design-system, node 1092-2807).
|
|
13
|
+
*
|
|
14
|
+
* A single command/tool call the AI wants to run, rendered as an approvable
|
|
15
|
+
* card: a command title line, an expandable body with the call arguments
|
|
16
|
+
* (RunAsUser, timeoutSeconds, …) and — once executed — a Result block. The
|
|
17
|
+
* footer flips between Approve/Reject buttons (pending) and a resolved status
|
|
18
|
+
* tag ("Approved / Rejected / Cancelled by {name}").
|
|
19
|
+
*
|
|
20
|
+
* Click a row to expand/collapse its body.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const LONG_COMMAND = `Get-CimInstance Win32_Process |
|
|
24
|
+
Select-Object ProcessId, Name, Path, CommandLine |
|
|
25
|
+
Where-Object { $_.Path -like 'C:\\Users\\Public\\*' }`;
|
|
26
|
+
|
|
27
|
+
const RESULT = `pid | name | path | cmdline
|
|
28
|
+
4821 | suspicious.exe | C:\\Users\\Public\\suspicious.exe | suspicious.exe --hidden --connect 185.220.101.45`;
|
|
29
|
+
|
|
30
|
+
const REQUEST_ID = "req-1";
|
|
31
|
+
|
|
32
|
+
const toolCall: PendingToolCallData = {
|
|
33
|
+
toolExecutionRequestId: REQUEST_ID,
|
|
34
|
+
toolName: "run_script",
|
|
35
|
+
toolTitle: "Run Script",
|
|
36
|
+
toolExplanation:
|
|
37
|
+
"Runs a diagnostic script on the endpoint to inspect running processes.",
|
|
38
|
+
toolType: "TACTICAL_RMM",
|
|
39
|
+
requiresApproval: true,
|
|
40
|
+
approvalType: "ADMIN",
|
|
41
|
+
toolCallArguments: {
|
|
42
|
+
command: LONG_COMMAND,
|
|
43
|
+
RunAsUser: "False",
|
|
44
|
+
timeoutSeconds: 60,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const makeData = (
|
|
49
|
+
executions?: Record<string, ApprovalBatchExecutionState>,
|
|
50
|
+
): ApprovalBatchData => ({
|
|
51
|
+
approvalRequestId: "batch-1",
|
|
52
|
+
approvalType: "ADMIN",
|
|
53
|
+
toolCalls: [toolCall],
|
|
54
|
+
executions,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const executingData = makeData({ [REQUEST_ID]: { status: "executing" } });
|
|
58
|
+
const doneData = makeData({
|
|
59
|
+
[REQUEST_ID]: { status: "done", result: RESULT, success: true },
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const constrainedDecorator =
|
|
63
|
+
(width?: number) => (Story: React.ComponentType) => (
|
|
64
|
+
<div style={{ maxWidth: width ?? 400, background: "var(--color-bg)" }}>
|
|
65
|
+
<Story />
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const meta = {
|
|
70
|
+
title: "Chat/Admin/ApprovalBatchMessage",
|
|
71
|
+
component: ApprovalBatchMessage,
|
|
72
|
+
tags: ["autodocs"],
|
|
73
|
+
parameters: {
|
|
74
|
+
docs: {
|
|
75
|
+
description: {
|
|
76
|
+
component:
|
|
77
|
+
"Command Block (Figma node 1092-2807). Approvable tool-call card with pending / approved / rejected / cancelled states, an expandable body of call arguments, and a Result block after execution.",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
decorators: [constrainedDecorator()],
|
|
82
|
+
} satisfies Meta<typeof ApprovalBatchMessage>;
|
|
83
|
+
|
|
84
|
+
export default meta;
|
|
85
|
+
type Story = StoryObj<typeof meta>;
|
|
86
|
+
|
|
87
|
+
/** Pending — Approve / Reject buttons shown. Click the row to expand args. */
|
|
88
|
+
export const Pending: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
data: makeData(),
|
|
91
|
+
status: "pending",
|
|
92
|
+
onApprove: () => {},
|
|
93
|
+
onReject: () => {},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/** Approved and still executing — grey loader on the row, no result yet. */
|
|
98
|
+
export const ApprovedExecuting: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
data: executingData,
|
|
101
|
+
status: "approved",
|
|
102
|
+
resolvedByName: "Michael Johnson",
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** Approved and finished — green check + expandable Result block. */
|
|
107
|
+
export const ApprovedDone: Story = {
|
|
108
|
+
args: {
|
|
109
|
+
data: doneData,
|
|
110
|
+
status: "approved",
|
|
111
|
+
resolvedByName: "Michael Johnson",
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** Rejected by a user — red status tag, no execution. */
|
|
116
|
+
export const Rejected: Story = {
|
|
117
|
+
args: {
|
|
118
|
+
data: makeData(),
|
|
119
|
+
status: "rejected",
|
|
120
|
+
resolvedByName: "Michael Johnson",
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** Cancelled — grey status tag, no execution. */
|
|
125
|
+
export const Cancelled: Story = {
|
|
126
|
+
args: {
|
|
127
|
+
data: makeData(),
|
|
128
|
+
status: "cancelled",
|
|
129
|
+
resolvedByName: "Michael Johnson",
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* All states side by side — mirrors the Figma "Command Blocks" board so the
|
|
135
|
+
* full set can be reviewed at a glance.
|
|
136
|
+
*/
|
|
137
|
+
export const AllStates: Story = {
|
|
138
|
+
decorators: [constrainedDecorator(420)],
|
|
139
|
+
render: () => (
|
|
140
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
|
141
|
+
<ApprovalBatchMessage
|
|
142
|
+
data={makeData()}
|
|
143
|
+
status="pending"
|
|
144
|
+
onApprove={() => {}}
|
|
145
|
+
onReject={() => {}}
|
|
146
|
+
/>
|
|
147
|
+
<ApprovalBatchMessage
|
|
148
|
+
data={executingData}
|
|
149
|
+
status="approved"
|
|
150
|
+
resolvedByName="Michael Johnson"
|
|
151
|
+
/>
|
|
152
|
+
<ApprovalBatchMessage
|
|
153
|
+
data={doneData}
|
|
154
|
+
status="approved"
|
|
155
|
+
resolvedByName="Michael Johnson"
|
|
156
|
+
/>
|
|
157
|
+
<ApprovalBatchMessage
|
|
158
|
+
data={makeData()}
|
|
159
|
+
status="rejected"
|
|
160
|
+
resolvedByName="Michael Johnson"
|
|
161
|
+
/>
|
|
162
|
+
<ApprovalBatchMessage
|
|
163
|
+
data={makeData()}
|
|
164
|
+
status="cancelled"
|
|
165
|
+
resolvedByName="Michael Johnson"
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
),
|
|
169
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
import { ApprovalBatchMessage } from "../components/chat/approval-batch-message";
|
|
5
|
+
import type {
|
|
6
|
+
ApprovalBatchData,
|
|
7
|
+
ApprovalBatchExecutionState,
|
|
8
|
+
PendingToolCallData,
|
|
9
|
+
} from "../components/chat/types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* CLIENT (Fae) variant of the Command Block — Figma node 1092-2807.
|
|
13
|
+
*
|
|
14
|
+
* Same component and props as the Admin card, driven by `assistantType="fae"`.
|
|
15
|
+
* Differences vs Admin: no tool icon next to the command, no footer divider,
|
|
16
|
+
* and a single full-text status pill ("Approved by Michael Johnson") instead
|
|
17
|
+
* of a compact tag + separate "by {name}".
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const LONG_COMMAND = `Get-CimInstance Win32_Process |
|
|
21
|
+
Select-Object ProcessId, Name, Path, CommandLine |
|
|
22
|
+
Where-Object { $_.Path -like 'C:\\Users\\Public\\*' }`;
|
|
23
|
+
|
|
24
|
+
const RESULT = `pid | name | path | cmdline
|
|
25
|
+
4821 | suspicious.exe | C:\\Users\\Public\\suspicious.exe | suspicious.exe --hidden --connect 185.220.101.45`;
|
|
26
|
+
|
|
27
|
+
const REQUEST_ID = "req-1";
|
|
28
|
+
|
|
29
|
+
const toolCall: PendingToolCallData = {
|
|
30
|
+
toolExecutionRequestId: REQUEST_ID,
|
|
31
|
+
toolName: "run_script",
|
|
32
|
+
toolTitle: "Run Script",
|
|
33
|
+
toolType: "TACTICAL_RMM",
|
|
34
|
+
requiresApproval: true,
|
|
35
|
+
approvalType: "CLIENT",
|
|
36
|
+
toolCallArguments: {
|
|
37
|
+
command: LONG_COMMAND,
|
|
38
|
+
RunAsUser: "False",
|
|
39
|
+
timeoutSeconds: 60,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const makeData = (
|
|
44
|
+
executions?: Record<string, ApprovalBatchExecutionState>,
|
|
45
|
+
): ApprovalBatchData => ({
|
|
46
|
+
approvalRequestId: "batch-1",
|
|
47
|
+
approvalType: "CLIENT",
|
|
48
|
+
toolCalls: [toolCall],
|
|
49
|
+
executions,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const executingData = makeData({ [REQUEST_ID]: { status: "executing" } });
|
|
53
|
+
const doneData = makeData({
|
|
54
|
+
[REQUEST_ID]: { status: "done", result: RESULT, success: true },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The assistant's message text that precedes the command block in the real
|
|
59
|
+
* chat (Figma "Command description text."). It is NOT part of the component —
|
|
60
|
+
* shown here only so the story matches the Figma frame.
|
|
61
|
+
*/
|
|
62
|
+
const CommandHeading = () => (
|
|
63
|
+
<p className="text-h4 text-ods-text-primary mb-[var(--spacing-system-xxs)]">
|
|
64
|
+
Command description text.
|
|
65
|
+
</p>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const constrainedDecorator =
|
|
69
|
+
(width?: number) => (Story: React.ComponentType) => (
|
|
70
|
+
<div style={{ maxWidth: width ?? 400, background: "var(--color-bg)" }}>
|
|
71
|
+
<CommandHeading />
|
|
72
|
+
<Story />
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const plainDecorator =
|
|
77
|
+
(width?: number) => (Story: React.ComponentType) => (
|
|
78
|
+
<div style={{ maxWidth: width ?? 400, background: "var(--color-bg)" }}>
|
|
79
|
+
<Story />
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const meta = {
|
|
84
|
+
title: "Chat/Client/ApprovalBatchMessage",
|
|
85
|
+
component: ApprovalBatchMessage,
|
|
86
|
+
tags: ["autodocs"],
|
|
87
|
+
args: { assistantType: "fae" },
|
|
88
|
+
parameters: {
|
|
89
|
+
docs: {
|
|
90
|
+
description: {
|
|
91
|
+
component:
|
|
92
|
+
"CLIENT (Fae) Command Block — no tool icon, no divider, full-text status pill. Same component/props as Admin, gated by assistantType='fae' (Figma 1092-2807).",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
decorators: [constrainedDecorator()],
|
|
97
|
+
} satisfies Meta<typeof ApprovalBatchMessage>;
|
|
98
|
+
|
|
99
|
+
export default meta;
|
|
100
|
+
type Story = StoryObj<typeof meta>;
|
|
101
|
+
|
|
102
|
+
/** Pending — Approve / Reject buttons. Click the row to expand args. */
|
|
103
|
+
export const Pending: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
data: makeData(),
|
|
106
|
+
status: "pending",
|
|
107
|
+
onApprove: () => {},
|
|
108
|
+
onReject: () => {},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/** Approved and still executing — grey loader on the row, no result yet. */
|
|
113
|
+
export const ApprovedExecuting: Story = {
|
|
114
|
+
args: {
|
|
115
|
+
data: executingData,
|
|
116
|
+
status: "approved",
|
|
117
|
+
resolvedByName: "Michael Johnson",
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/** Approved and finished — green check + expandable Result block. */
|
|
122
|
+
export const ApprovedDone: Story = {
|
|
123
|
+
args: {
|
|
124
|
+
data: doneData,
|
|
125
|
+
status: "approved",
|
|
126
|
+
resolvedByName: "Michael Johnson",
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** Rejected by a user — full-text red status pill. */
|
|
131
|
+
export const Rejected: Story = {
|
|
132
|
+
args: {
|
|
133
|
+
data: makeData(),
|
|
134
|
+
status: "rejected",
|
|
135
|
+
resolvedByName: "Michael Johnson",
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/** Cancelled — full-text grey status pill. */
|
|
140
|
+
export const Cancelled: Story = {
|
|
141
|
+
args: {
|
|
142
|
+
data: makeData(),
|
|
143
|
+
status: "cancelled",
|
|
144
|
+
resolvedByName: "Michael Johnson",
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* All states side by side — mirrors the Figma CLIENT "Command Blocks" board.
|
|
150
|
+
*/
|
|
151
|
+
export const AllStates: Story = {
|
|
152
|
+
decorators: [plainDecorator(420)],
|
|
153
|
+
render: () => {
|
|
154
|
+
const states = [
|
|
155
|
+
{ data: makeData(), status: "pending" as const, onApprove: () => {}, onReject: () => {} },
|
|
156
|
+
{ data: executingData, status: "approved" as const, resolvedByName: "Michael Johnson" },
|
|
157
|
+
{ data: doneData, status: "approved" as const, resolvedByName: "Michael Johnson" },
|
|
158
|
+
{ data: makeData(), status: "rejected" as const, resolvedByName: "Michael Johnson" },
|
|
159
|
+
{ data: makeData(), status: "cancelled" as const, resolvedByName: "Michael Johnson" },
|
|
160
|
+
];
|
|
161
|
+
return (
|
|
162
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 32 }}>
|
|
163
|
+
{states.map((s, i) => (
|
|
164
|
+
<div key={i}>
|
|
165
|
+
<CommandHeading />
|
|
166
|
+
<ApprovalBatchMessage assistantType="fae" {...s} />
|
|
167
|
+
</div>
|
|
168
|
+
))}
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
};
|