@flamingo-stack/openframe-frontend-core 0.0.351 → 0.0.352-snapshot.20260701170615

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.
Files changed (117) hide show
  1. package/dist/{chunk-HMUIRZ46.cjs → chunk-3B663UT5.cjs} +222 -165
  2. package/dist/chunk-3B663UT5.cjs.map +1 -0
  3. package/dist/{chunk-UQFTSFIF.cjs → chunk-5AR4XSIK.cjs} +29 -29
  4. package/dist/{chunk-UQFTSFIF.cjs.map → chunk-5AR4XSIK.cjs.map} +1 -1
  5. package/dist/{chunk-MLW25MGY.js → chunk-5Y6G2GKO.js} +3 -3
  6. package/dist/{chunk-EKU6D26E.js → chunk-776NIQQ6.js} +6 -6
  7. package/dist/{chunk-YBL3JURC.js → chunk-BAPR6IQV.js} +5 -5
  8. package/dist/{chunk-VY6AAUCK.js → chunk-CDPGUKPU.js} +2 -2
  9. package/dist/{chunk-NEIOYJIE.cjs → chunk-D4TPL6T4.cjs} +15 -15
  10. package/dist/{chunk-NEIOYJIE.cjs.map → chunk-D4TPL6T4.cjs.map} +1 -1
  11. package/dist/{chunk-YIFJJCFB.js → chunk-EQQZLZZH.js} +4 -4
  12. package/dist/{chunk-RD3HIB25.cjs → chunk-GPX5I7FT.cjs} +2 -2
  13. package/dist/{chunk-FPAF4JHM.js.map → chunk-GPX5I7FT.cjs.map} +1 -1
  14. package/dist/{chunk-46Z3O6SN.cjs → chunk-HAFY32LW.cjs} +5 -5
  15. package/dist/{chunk-46Z3O6SN.cjs.map → chunk-HAFY32LW.cjs.map} +1 -1
  16. package/dist/{chunk-VZ7TICMR.cjs → chunk-HWFFUT6G.cjs} +40 -40
  17. package/dist/{chunk-VZ7TICMR.cjs.map → chunk-HWFFUT6G.cjs.map} +1 -1
  18. package/dist/{chunk-NIRMBPFP.cjs → chunk-IVL6FGM6.cjs} +19 -19
  19. package/dist/{chunk-NIRMBPFP.cjs.map → chunk-IVL6FGM6.cjs.map} +1 -1
  20. package/dist/{chunk-FPAF4JHM.js → chunk-JGAAUUD7.js} +2 -2
  21. package/dist/chunk-JGAAUUD7.js.map +1 -0
  22. package/dist/{chunk-A6YFD2SK.js → chunk-OKGXLXDW.js} +174 -117
  23. package/dist/chunk-OKGXLXDW.js.map +1 -0
  24. package/dist/{chunk-63ISTNQW.cjs → chunk-P56AC6I5.cjs} +31 -31
  25. package/dist/{chunk-63ISTNQW.cjs.map → chunk-P56AC6I5.cjs.map} +1 -1
  26. package/dist/{chunk-4WVVMKMC.js → chunk-POLYFLWF.js} +5 -5
  27. package/dist/{chunk-JKJO7HVH.cjs → chunk-RYROZ7HM.cjs} +3 -3
  28. package/dist/{chunk-JKJO7HVH.cjs.map → chunk-RYROZ7HM.cjs.map} +1 -1
  29. package/dist/{chunk-QGBJKB6V.cjs → chunk-TWVF3QU5.cjs} +39 -39
  30. package/dist/{chunk-QGBJKB6V.cjs.map → chunk-TWVF3QU5.cjs.map} +1 -1
  31. package/dist/{chunk-KYCZ4ISB.js → chunk-U5MQW3PH.js} +2 -2
  32. package/dist/{chunk-XZVVLO4V.cjs → chunk-W3CCNLBL.cjs} +12 -12
  33. package/dist/{chunk-XZVVLO4V.cjs.map → chunk-W3CCNLBL.cjs.map} +1 -1
  34. package/dist/{chunk-DJZ64AF6.js → chunk-XJQNOT4Z.js} +2 -2
  35. package/dist/{chunk-TH3HPNMO.cjs → chunk-XKRR5GIV.cjs} +70 -70
  36. package/dist/{chunk-TH3HPNMO.cjs.map → chunk-XKRR5GIV.cjs.map} +1 -1
  37. package/dist/{chunk-JBWB3U3P.js → chunk-YGCWTL7E.js} +2 -2
  38. package/dist/{chunk-5F45BY24.js → chunk-YI5435S6.js} +3 -3
  39. package/dist/{chunk-BMQ5CBFD.cjs → chunk-YL2YXSRI.cjs} +73 -73
  40. package/dist/{chunk-BMQ5CBFD.cjs.map → chunk-YL2YXSRI.cjs.map} +1 -1
  41. package/dist/{chunk-GJZL72ON.cjs → chunk-Z22AMCZL.cjs} +7 -7
  42. package/dist/{chunk-GJZL72ON.cjs.map → chunk-Z22AMCZL.cjs.map} +1 -1
  43. package/dist/{chunk-WB2EWYSF.js → chunk-ZAKU2MOI.js} +3 -3
  44. package/dist/{chunk-Q3VK7WI2.js → chunk-ZYB2QBNU.js} +6 -6
  45. package/dist/components/case-studies/index.cjs +9 -9
  46. package/dist/components/case-studies/index.js +3 -3
  47. package/dist/components/chat/approval-batch-message.d.ts +7 -1
  48. package/dist/components/chat/approval-batch-message.d.ts.map +1 -1
  49. package/dist/components/chat/approval-request-message.d.ts.map +1 -1
  50. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  51. package/dist/components/chat/index.cjs +3 -3
  52. package/dist/components/chat/index.js +2 -2
  53. package/dist/components/chat/tool-call-blocks.d.ts.map +1 -1
  54. package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
  55. package/dist/components/chat/types/component.types.d.ts +6 -0
  56. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  57. package/dist/components/contact/index.cjs +4 -4
  58. package/dist/components/contact/index.js +3 -3
  59. package/dist/components/docs/index.cjs +6 -6
  60. package/dist/components/docs/index.js +5 -5
  61. package/dist/components/embeds/index.cjs +4 -4
  62. package/dist/components/embeds/index.js +3 -3
  63. package/dist/components/faq/index.cjs +5 -5
  64. package/dist/components/faq/index.js +4 -4
  65. package/dist/components/features/index.cjs +3 -3
  66. package/dist/components/features/index.js +2 -2
  67. package/dist/components/help-center-pages/index.cjs +22 -22
  68. package/dist/components/help-center-pages/index.js +13 -13
  69. package/dist/components/index.cjs +124 -124
  70. package/dist/components/index.js +11 -11
  71. package/dist/components/navigation/index.cjs +3 -3
  72. package/dist/components/navigation/index.js +2 -2
  73. package/dist/components/onboarding-guides/index.cjs +7 -7
  74. package/dist/components/onboarding-guides/index.js +6 -6
  75. package/dist/components/related-content/index.cjs +5 -5
  76. package/dist/components/related-content/index.js +4 -4
  77. package/dist/components/tickets/index.cjs +6 -6
  78. package/dist/components/tickets/index.js +5 -5
  79. package/dist/components/ui/index.cjs +3 -3
  80. package/dist/components/ui/index.js +2 -2
  81. package/dist/hooks/index.cjs +2 -2
  82. package/dist/hooks/index.js +1 -1
  83. package/dist/index.cjs +3 -3
  84. package/dist/index.js +2 -2
  85. package/dist/utils/index.cjs +1 -1
  86. package/dist/utils/index.cjs.map +1 -1
  87. package/dist/utils/index.js +1 -1
  88. package/dist/utils/index.js.map +1 -1
  89. package/dist/utils/platform-config.d.ts.map +1 -1
  90. package/package.json +1 -1
  91. package/src/components/chat/approval-batch-message.tsx +82 -27
  92. package/src/components/chat/approval-request-message.tsx +27 -19
  93. package/src/components/chat/chat-message-enhanced.tsx +3 -0
  94. package/src/components/chat/tool-call-blocks.tsx +17 -6
  95. package/src/components/chat/tool-execution-display.tsx +23 -8
  96. package/src/components/chat/types/component.types.ts +6 -0
  97. package/src/stories/ApprovalBatchMessage.stories.tsx +169 -0
  98. package/src/stories/ApprovalBatchMessageClient.stories.tsx +172 -0
  99. package/src/stories/ApprovalRequestMessage.stories.tsx +95 -0
  100. package/src/stories/ToolExecutionDisplay.stories.tsx +1 -1
  101. package/src/stories/ToolExecutionDisplayClient.stories.tsx +64 -0
  102. package/src/utils/platform-config.tsx +4 -1
  103. package/dist/chunk-A6YFD2SK.js.map +0 -1
  104. package/dist/chunk-HMUIRZ46.cjs.map +0 -1
  105. package/dist/chunk-RD3HIB25.cjs.map +0 -1
  106. /package/dist/{chunk-MLW25MGY.js.map → chunk-5Y6G2GKO.js.map} +0 -0
  107. /package/dist/{chunk-EKU6D26E.js.map → chunk-776NIQQ6.js.map} +0 -0
  108. /package/dist/{chunk-YBL3JURC.js.map → chunk-BAPR6IQV.js.map} +0 -0
  109. /package/dist/{chunk-VY6AAUCK.js.map → chunk-CDPGUKPU.js.map} +0 -0
  110. /package/dist/{chunk-YIFJJCFB.js.map → chunk-EQQZLZZH.js.map} +0 -0
  111. /package/dist/{chunk-4WVVMKMC.js.map → chunk-POLYFLWF.js.map} +0 -0
  112. /package/dist/{chunk-KYCZ4ISB.js.map → chunk-U5MQW3PH.js.map} +0 -0
  113. /package/dist/{chunk-DJZ64AF6.js.map → chunk-XJQNOT4Z.js.map} +0 -0
  114. /package/dist/{chunk-JBWB3U3P.js.map → chunk-YGCWTL7E.js.map} +0 -0
  115. /package/dist/{chunk-5F45BY24.js.map → chunk-YI5435S6.js.map} +0 -0
  116. /package/dist/{chunk-WB2EWYSF.js.map → chunk-ZAKU2MOI.js.map} +0 -0
  117. /package/dist/{chunk-Q3VK7WI2.js.map → chunk-ZYB2QBNU.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"platform-config.d.ts","sourceRoot":"","sources":["../../src/utils/platform-config.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,eAAO,MAAM,aAAa;;;;;;;;;;;;CAYzB,CAAC;AAGF,eAAO,MAAM,cAAc;;;;;;;;;;;;CAY1B,CAAC;AAGF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAYhC,CAAC;AAGF,eAAO,MAAM,oBAAoB;;;;;;;CAOhC,CAAC;AAGF,eAAO,MAAM,eAAe;;;;;;;CAO3B,CAAC;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAY7B,CAAC;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAY7B,CAAC;AAEF;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,iCAAiC,CAAC,eAAe,EAAE,cAAc,EAAE,GAAG,gBAAgB,EAAE,CASvG;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,qBAEnD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,UAEpD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CA2B1E;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,GAAE,MAAkB,GAAG,KAAK,CAAC,SAAS,CAyB7G"}
1
+ {"version":3,"file":"platform-config.d.ts","sourceRoot":"","sources":["../../src/utils/platform-config.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,eAAO,MAAM,aAAa;;;;;;;;;;;;CAYzB,CAAC;AAGF,eAAO,MAAM,cAAc;;;;;;;;;;;;CAY1B,CAAC;AAGF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAYhC,CAAC;AAGF,eAAO,MAAM,oBAAoB;;;;;;;CAOhC,CAAC;AAGF,eAAO,MAAM,eAAe;;;;;;;CAO3B,CAAC;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAY7B,CAAC;AAGF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;CAY7B,CAAC;AAEF;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,iCAAiC,CAAC,eAAe,EAAE,cAAc,EAAE,GAAG,gBAAgB,EAAE,CASvG;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,qBAEnD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,UAEpD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CA2B1E;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,GAAE,MAAkB,GAAG,KAAK,CAAC,SAAS,CA4B7G"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.351",
3
+ "version": "0.0.352-snapshot.20260701170615",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,6 +11,7 @@ import { ExpandChevron } from "./expand-chevron"
11
11
  import { useCollapsible } from "./hooks/use-collapsible"
12
12
  import { ArgRow, ResultBlock } from "./tool-call-blocks"
13
13
  import type {
14
+ AssistantType,
14
15
  ApprovalBatchExecutionState,
15
16
  ApprovalBatchSegment,
16
17
  PendingToolCallData,
@@ -39,19 +40,35 @@ export interface ApprovalBatchMessageProps extends React.HTMLAttributes<HTMLDivE
39
40
  * otherwise show the dots spinner forever after approval. Defaults to on.
40
41
  */
41
42
  showExecutionStatus?: boolean
43
+ /**
44
+ * Chat identity, which drives the styling variant. `'fae'` = client
45
+ * (frameless outer, bordered command box, no tool icon, no divider,
46
+ * full-text status pill); `'mingo'`/undefined = admin (original layout).
47
+ */
48
+ assistantType?: AssistantType
42
49
  }
43
50
 
44
51
  const COMMAND_BODY_KEYS = new Set<string>(COMMAND_BODY_ARG_KEYS)
45
52
 
46
- /** Terminal-status badge for a resolved approval batch (approved / rejected / cancelled). */
47
- function renderStatusTag(status: ApprovalBatchSegment["status"]) {
53
+ /**
54
+ * Terminal-status badge for a resolved approval batch (approved / rejected /
55
+ * cancelled). For CLIENT (Fae) the resolver's name is baked into the tag as a
56
+ * single full-text pill ("Approved by {name}"); ADMIN keeps the compact word
57
+ * and renders "by {name}" as a separate muted span in the footer.
58
+ */
59
+ function renderStatusTag(
60
+ status: ApprovalBatchSegment["status"],
61
+ isClient: boolean,
62
+ resolvedByName?: string | null,
63
+ ) {
64
+ const suffix = isClient && resolvedByName ? ` by ${resolvedByName}` : ""
48
65
  if (status === "approved") {
49
- return <Tag label="Approved" variant="success" icon={<CheckCircleIcon className="w-4 h-4" />} />
66
+ return <Tag label={`Approved${suffix}`} variant="success" icon={<CheckCircleIcon className="w-4 h-4" />} />
50
67
  }
51
68
  if (status === "cancelled") {
52
- return <Tag label="Cancelled" variant="grey" icon={<XmarkIcon className="w-4 h-4" />} />
69
+ return <Tag label={`Canceled${suffix}`} variant="grey" icon={<XmarkIcon className="w-4 h-4" />} />
53
70
  }
54
- return <Tag label="Rejected" variant="error" icon={<XmarkCircleIcon className="w-4 h-4" />} />
71
+ return <Tag label={`Rejected${suffix}`} variant="error" icon={<XmarkCircleIcon className="w-4 h-4" />} />
55
72
  }
56
73
 
57
74
  function getArgEntries(call: PendingToolCallData): Array<[string, unknown]> {
@@ -88,9 +105,10 @@ interface ToolCallRowProps {
88
105
  batchStatus: ApprovalBatchSegment["status"]
89
106
  execution: ApprovalBatchExecutionState | undefined
90
107
  showExecutionStatus: boolean
108
+ isClient: boolean
91
109
  }
92
110
 
93
- function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExecutionStatus }: ToolCallRowProps) {
111
+ function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExecutionStatus, isClient }: ToolCallRowProps) {
94
112
  const command = getCommandText(call)
95
113
  const args = getArgEntries(call)
96
114
  const toolType = (call.toolType as ToolType) || ("OPENFRAME" as ToolType)
@@ -99,18 +117,30 @@ function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExe
99
117
  const hasExpandableBody = args.length > 0 || (typeof result === "string" && result.length > 0)
100
118
 
101
119
  return (
102
- <div className="bg-ods-card border-b border-ods-border last:border-b-0 flex flex-col items-start w-full">
120
+ <div
121
+ className={cn(
122
+ "bg-ods-card flex flex-col items-start w-full",
123
+ // ADMIN: rows share one outer card, separated by a bottom border.
124
+ // CLIENT (Fae): each command is its own self-contained bordered box
125
+ // (Figma 1092-2804 / 1972-7925) — the outer component is frameless.
126
+ isClient
127
+ ? "border border-ods-border rounded-[6px] overflow-hidden"
128
+ : "border-b border-ods-border last:border-b-0",
129
+ )}
130
+ >
103
131
  <button
104
132
  type="button"
105
133
  onClick={onToggle}
106
- className="flex gap-2 items-start w-full p-3 cursor-pointer text-left"
134
+ className="flex gap-[var(--spacing-system-xsf)] items-start w-full p-[var(--spacing-system-sf)] cursor-pointer text-left"
107
135
  >
108
- <div className="flex items-center justify-center shrink-0 w-5 h-5">
109
- <ToolIcon toolType={toolType} size={16} />
110
- </div>
136
+ {!isClient && (
137
+ <div className="flex items-center justify-center shrink-0 w-5 h-5">
138
+ <ToolIcon toolType={toolType} size={16} />
139
+ </div>
140
+ )}
111
141
  <div
112
142
  className={cn(
113
- "flex-1 min-w-0 font-medium text-sm leading-5",
143
+ "flex-1 min-w-0 text-h6",
114
144
  expanded
115
145
  ? "text-ods-text-primary whitespace-pre-wrap break-all"
116
146
  : "text-ods-text-secondary line-clamp-2 max-h-10 break-all",
@@ -131,11 +161,16 @@ function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExe
131
161
  <div className="w-full" style={containerStyle}>
132
162
  <div ref={innerRef}>
133
163
  {hasExpandableBody && (
134
- <div className="flex flex-col gap-2 items-start w-full text-sm font-medium leading-5 p-3 bg-ods-card">
164
+ <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">
135
165
  {args.map(([key, value]) => (
136
166
  <ArgRow key={key} argKey={key} value={value} />
137
167
  ))}
138
- {result && <ResultBlock result={result} />}
168
+ {result && (
169
+ <ResultBlock
170
+ result={result}
171
+ className={args.length > 0 ? "mt-[var(--spacing-system-xsf)]" : undefined}
172
+ />
173
+ )}
139
174
  </div>
140
175
  )}
141
176
  </div>
@@ -145,9 +180,10 @@ function ToolCallRow({ call, expanded, onToggle, batchStatus, execution, showExe
145
180
  }
146
181
 
147
182
  const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProps>(
148
- ({ className, data, onApprove, onReject, status = "pending", maxBodyHeight, resolvedByName, showExecutionStatus = true, ...props }, ref) => {
183
+ ({ className, data, onApprove, onReject, status = "pending", maxBodyHeight, resolvedByName, showExecutionStatus = true, assistantType, ...props }, ref) => {
149
184
  const [expandedId, setExpandedId] = useState<string | null>(null)
150
185
  const [isProcessing, setIsProcessing] = useState(false)
186
+ const isClient = assistantType === "fae"
151
187
 
152
188
  const explanations = data.toolCalls
153
189
  .map((c) => c.toolExplanation?.trim())
@@ -177,13 +213,22 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
177
213
  <div
178
214
  ref={ref}
179
215
  className={cn(
180
- "bg-ods-card border border-ods-border rounded-md overflow-hidden flex flex-col mb-2",
216
+ "flex flex-col mb-[var(--spacing-system-xsf)]",
217
+ // ADMIN: one framed card. CLIENT (Fae): frameless container — the
218
+ // bordered command box(es) + button/status row stacked 16px apart.
219
+ isClient
220
+ ? "gap-[var(--spacing-system-mf)]"
221
+ : "bg-ods-card border border-ods-border rounded-md overflow-hidden",
181
222
  className,
182
223
  )}
183
224
  {...props}
184
225
  >
185
226
  <div
186
- className={cn("flex flex-col", maxBodyHeight != null && "overflow-y-auto overscroll-contain")}
227
+ className={cn(
228
+ "flex flex-col",
229
+ isClient && "gap-[var(--spacing-system-mf)]",
230
+ maxBodyHeight != null && "overflow-y-auto overscroll-contain",
231
+ )}
187
232
  style={maxBodyHeight != null ? { maxHeight: maxBodyHeight } : undefined}
188
233
  >
189
234
  {data.toolCalls.map((call) => (
@@ -199,14 +244,22 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
199
244
  batchStatus={status}
200
245
  execution={data.executions?.[call.toolExecutionRequestId]}
201
246
  showExecutionStatus={showExecutionStatus}
247
+ isClient={isClient}
202
248
  />
203
249
  ))}
204
250
  </div>
205
251
 
206
252
  {showFooterBlock && (
207
- <div className="bg-ods-card flex flex-col gap-2 items-start justify-center p-3">
253
+ <div
254
+ className={cn(
255
+ "flex flex-col gap-[var(--spacing-system-xsf)] items-start justify-center",
256
+ // ADMIN: padded footer inside the card, divided from the list.
257
+ // CLIENT (Fae): buttons/status sit flush outside the box.
258
+ !isClient && "bg-ods-card border-t border-ods-border p-[var(--spacing-system-sf)]",
259
+ )}
260
+ >
208
261
  {explanations.length > 0 && (
209
- <ul className="list-disc pl-5 text-sm font-medium text-ods-text-primary leading-5 w-full">
262
+ <ul className="list-disc pl-5 text-h6 text-ods-text-primary w-full">
210
263
  {explanations.map((expl, i) => (
211
264
  <li key={i}>{expl}</li>
212
265
  ))}
@@ -214,7 +267,7 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
214
267
  )}
215
268
 
216
269
  {status === "pending" ? (
217
- <div className="flex gap-2 items-center">
270
+ <div className="flex gap-[var(--spacing-system-xsf)] items-center">
218
271
  <Button
219
272
  size="small-legacy"
220
273
  variant="accent"
@@ -222,8 +275,8 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
222
275
  disabled={isProcessing}
223
276
  className={cn(
224
277
  "bg-ods-accent hover:bg-ods-accent/90",
225
- "font-mono font-medium md:!text-sm text-ods-bg uppercase tracking-[-0.28px]",
226
- "px-2 py-1 h-auto",
278
+ "text-h5 text-ods-bg",
279
+ "px-[var(--spacing-system-xsf)] h-8",
227
280
  )}
228
281
  >
229
282
  Approve
@@ -235,17 +288,19 @@ const ApprovalBatchMessage = forwardRef<HTMLDivElement, ApprovalBatchMessageProp
235
288
  disabled={isProcessing}
236
289
  className={cn(
237
290
  "bg-ods-card border-ods-border",
238
- "font-mono font-medium md:!text-sm text-ods-text-primary uppercase tracking-[-0.28px]",
239
- "hover:bg-ods-bg px-2 py-1 h-auto",
291
+ "text-h5 text-ods-text-primary",
292
+ "hover:bg-ods-bg px-[var(--spacing-system-xsf)] h-8",
240
293
  )}
241
294
  >
242
295
  Reject
243
296
  </Button>
244
297
  </div>
245
298
  ) : (
246
- <div className="flex items-center gap-2">
247
- {renderStatusTag(status)}
248
- {resolvedByName && <span className="text-h6 text-ods-text-secondary">by {resolvedByName}</span>}
299
+ <div className="flex items-center gap-[var(--spacing-system-xsf)]">
300
+ {renderStatusTag(status, isClient, resolvedByName)}
301
+ {!isClient && resolvedByName && (
302
+ <span className="text-h6 text-ods-text-secondary">by {resolvedByName}</span>
303
+ )}
249
304
  </div>
250
305
  )}
251
306
  </div>
@@ -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-1">
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="font-['DM_Sans'] text-sm text-ods-text-primary leading-5 whitespace-pre-wrap break-words">
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-1">
53
- <div className="bg-ods-bg border border-ods-border rounded-md p-3 flex gap-2 items-start max-h-32 overflow-y-auto">
54
- <code className="font-['DM_Sans'] font-medium text-sm text-ods-text-primary flex-1 leading-5 whitespace-pre-wrap break-words">
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="font-['DM_Sans'] font-medium text-sm text-ods-text-secondary leading-5 whitespace-pre-line break-words">
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
- ({ className, data, onApprove, onReject, status = 'pending', ...props }, ref) => {
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-4 mb-2 flex flex-col gap-4",
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-4 items-center">
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
- "font-mono font-medium md:!text-sm text-ods-bg uppercase tracking-[-0.28px]",
118
- "px-2 py-1 h-auto"
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
- "font-mono font-medium md:!text-sm text-ods-text-primary uppercase tracking-[-0.28px]",
131
- "hover:bg-ods-bg px-2 py-1 h-auto"
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
- <Tag
140
- label={status === 'approved' ? 'Approved' : 'Rejected'}
141
- variant={status === 'approved' ? 'success' : 'error'}
142
- icon={status === 'approved' ? <CheckCircle className="w-4 h-4" /> : <XCircle className="w-4 h-4" />}
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-1 items-start w-full">
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-1 items-start w-full">
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="bg-ods-bg border border-ods-border rounded-md p-3 w-full max-h-64 overflow-auto text-xs leading-5 text-ods-text-primary font-mono whitespace-pre">
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-1 items-start w-full", className)}>
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-1 items-start w-full", className)}>
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="bg-ods-bg border border-ods-border rounded-md p-3 w-full max-h-64 overflow-auto text-xs leading-5 text-ods-text-primary font-mono whitespace-pre">
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
- <div className="flex items-center justify-center shrink-0 w-5 h-5">
68
- <ToolIcon toolType={integratedToolType} size={16} />
69
- </div>
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-sm font-medium leading-5",
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-2 items-start w-full text-sm font-medium leading-5 p-3 bg-ods-card">
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 && <ResultBlock result={message.result} />}
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 className="flex flex-col gap-1 items-start w-full">
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 ==========