@flamingo-stack/openframe-frontend-core 0.0.685-snapshot.20260929164409 → 0.0.685

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 (170) hide show
  1. package/dist/{chunk-DL67S7H4.cjs → chunk-5LJZPJHX.cjs} +59 -59
  2. package/dist/{chunk-DL67S7H4.cjs.map → chunk-5LJZPJHX.cjs.map} +1 -1
  3. package/dist/{chunk-IZ3WKG62.js → chunk-65KTXRBY.js} +6 -6
  4. package/dist/{chunk-IBKMIJ7Y.js → chunk-7XF63THA.js} +6 -6
  5. package/dist/{chunk-SI77HF4U.cjs → chunk-A3CSQB4A.cjs} +805 -1247
  6. package/dist/chunk-A3CSQB4A.cjs.map +1 -0
  7. package/dist/{chunk-5MUR43QD.cjs → chunk-B25CF3PO.cjs} +30 -30
  8. package/dist/{chunk-5MUR43QD.cjs.map → chunk-B25CF3PO.cjs.map} +1 -1
  9. package/dist/{chunk-32KBPEF7.js → chunk-B32YLOYQ.js} +4 -4
  10. package/dist/{chunk-OBLED7JY.cjs → chunk-B5N3AHPG.cjs} +81 -81
  11. package/dist/{chunk-OBLED7JY.cjs.map → chunk-B5N3AHPG.cjs.map} +1 -1
  12. package/dist/{chunk-QSRUYHTW.js → chunk-BZ2XOEHW.js} +3 -3
  13. package/dist/{chunk-XATA4OVO.cjs → chunk-C3B36OYZ.cjs} +3 -3
  14. package/dist/{chunk-XATA4OVO.cjs.map → chunk-C3B36OYZ.cjs.map} +1 -1
  15. package/dist/{chunk-V26TKJ4Z.cjs → chunk-CMQXPD5C.cjs} +28 -28
  16. package/dist/{chunk-V26TKJ4Z.cjs.map → chunk-CMQXPD5C.cjs.map} +1 -1
  17. package/dist/{chunk-QNWQQ4RT.cjs → chunk-DQOQ3KTP.cjs} +73 -78
  18. package/dist/chunk-DQOQ3KTP.cjs.map +1 -0
  19. package/dist/{chunk-5HVGOIAE.cjs → chunk-DWXWEGGO.cjs} +22 -22
  20. package/dist/{chunk-5HVGOIAE.cjs.map → chunk-DWXWEGGO.cjs.map} +1 -1
  21. package/dist/{chunk-6CMH53MP.cjs → chunk-EFBMNVSH.cjs} +15 -15
  22. package/dist/{chunk-6CMH53MP.cjs.map → chunk-EFBMNVSH.cjs.map} +1 -1
  23. package/dist/{chunk-DDFYKF3B.js → chunk-GHLGXFNU.js} +3 -3
  24. package/dist/{chunk-VDV7K5TD.js → chunk-GXV6UEVY.js} +3 -3
  25. package/dist/{chunk-AEAFQTVR.cjs → chunk-HMXUOLZR.cjs} +20 -20
  26. package/dist/{chunk-AEAFQTVR.cjs.map → chunk-HMXUOLZR.cjs.map} +1 -1
  27. package/dist/{chunk-2OD2PPNO.cjs → chunk-K2HZG4RM.cjs} +32 -32
  28. package/dist/{chunk-2OD2PPNO.cjs.map → chunk-K2HZG4RM.cjs.map} +1 -1
  29. package/dist/{chunk-JNDHXL2D.cjs → chunk-M4HYIXP6.cjs} +383 -384
  30. package/dist/chunk-M4HYIXP6.cjs.map +1 -0
  31. package/dist/{chunk-KTWJKCUB.js → chunk-N4Z6X5BV.js} +5 -5
  32. package/dist/{chunk-T2RHI676.js → chunk-O7LOZW4R.js} +5 -5
  33. package/dist/{chunk-ZHYYGIAI.cjs → chunk-P2ASEPA2.cjs} +6 -6
  34. package/dist/{chunk-ZHYYGIAI.cjs.map → chunk-P2ASEPA2.cjs.map} +1 -1
  35. package/dist/{chunk-CHRDLXXX.js → chunk-PSQKD6ZN.js} +3 -3
  36. package/dist/{chunk-GSJR33UP.cjs → chunk-QHLJSB2J.cjs} +12 -12
  37. package/dist/{chunk-GSJR33UP.cjs.map → chunk-QHLJSB2J.cjs.map} +1 -1
  38. package/dist/{chunk-CFL3EW4V.cjs → chunk-T4DUTP5C.cjs} +5 -5
  39. package/dist/{chunk-CFL3EW4V.cjs.map → chunk-T4DUTP5C.cjs.map} +1 -1
  40. package/dist/{chunk-22RSJ57Z.js → chunk-T6KWNJDW.js} +465 -470
  41. package/dist/chunk-T6KWNJDW.js.map +1 -0
  42. package/dist/{chunk-2FGFB2HY.cjs → chunk-TJI5V5BR.cjs} +7 -7
  43. package/dist/{chunk-2FGFB2HY.cjs.map → chunk-TJI5V5BR.cjs.map} +1 -1
  44. package/dist/{chunk-HLEXYTOE.js → chunk-TUG7IT6H.js} +2 -2
  45. package/dist/{chunk-DWCVA6XT.cjs → chunk-U3IXG7TR.cjs} +33 -33
  46. package/dist/{chunk-DWCVA6XT.cjs.map → chunk-U3IXG7TR.cjs.map} +1 -1
  47. package/dist/{chunk-WJ5XN4AL.js → chunk-UT333EOT.js} +4 -4
  48. package/dist/{chunk-OTV4W3YD.js → chunk-VDPCK437.js} +5 -6
  49. package/dist/{chunk-OTV4W3YD.js.map → chunk-VDPCK437.js.map} +1 -1
  50. package/dist/{chunk-5GK7TU2Q.js → chunk-WJK2EETW.js} +2 -2
  51. package/dist/{chunk-YPXXXX6R.js → chunk-XGX6JSEU.js} +1644 -2086
  52. package/dist/chunk-XGX6JSEU.js.map +1 -0
  53. package/dist/{chunk-XODE4YTG.js → chunk-XW7MUIW4.js} +3 -3
  54. package/dist/{chunk-N74MQ4HQ.js → chunk-Y7ZYOQNB.js} +3 -3
  55. package/dist/{chunk-2NUB5EMG.cjs → chunk-YCTVLN47.cjs} +10 -10
  56. package/dist/{chunk-2NUB5EMG.cjs.map → chunk-YCTVLN47.cjs.map} +1 -1
  57. package/dist/{chunk-TILC52ZF.js → chunk-ZF4Z36G4.js} +7 -7
  58. package/dist/components/case-studies/index.cjs +16 -16
  59. package/dist/components/case-studies/index.js +4 -4
  60. package/dist/components/chat/ai-assistant-info.d.ts.map +1 -1
  61. package/dist/components/chat/approval-request-message.d.ts.map +1 -1
  62. package/dist/components/chat/chat-input.d.ts.map +1 -1
  63. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  64. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  65. package/dist/components/chat/error-message-display.d.ts.map +1 -1
  66. package/dist/components/chat/index.cjs +3 -21
  67. package/dist/components/chat/index.cjs.map +1 -1
  68. package/dist/components/chat/index.d.ts +0 -6
  69. package/dist/components/chat/index.d.ts.map +1 -1
  70. package/dist/components/chat/index.js +2 -20
  71. package/dist/components/chat/msp-organization-card.d.ts +1 -7
  72. package/dist/components/chat/msp-organization-card.d.ts.map +1 -1
  73. package/dist/components/chat/types/chat.types.d.ts +0 -13
  74. package/dist/components/chat/types/chat.types.d.ts.map +1 -1
  75. package/dist/components/chat/types/component.types.d.ts +1 -20
  76. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  77. package/dist/components/contact/index.cjs +5 -5
  78. package/dist/components/contact/index.js +4 -4
  79. package/dist/components/docs/index.cjs +8 -8
  80. package/dist/components/docs/index.js +7 -7
  81. package/dist/components/embeds/index.cjs +5 -5
  82. package/dist/components/embeds/index.js +4 -4
  83. package/dist/components/faq/index.cjs +5 -5
  84. package/dist/components/faq/index.js +4 -4
  85. package/dist/components/features/index.cjs +4 -4
  86. package/dist/components/features/index.js +3 -3
  87. package/dist/components/help-center-pages/index.cjs +91 -91
  88. package/dist/components/help-center-pages/index.js +15 -15
  89. package/dist/components/index.cjs +55 -73
  90. package/dist/components/index.cjs.map +1 -1
  91. package/dist/components/index.js +15 -33
  92. package/dist/components/index.js.map +1 -1
  93. package/dist/components/meeting-scheduler/index.cjs +41 -41
  94. package/dist/components/meeting-scheduler/index.js +3 -3
  95. package/dist/components/navigation/index.cjs +7 -7
  96. package/dist/components/navigation/index.js +6 -6
  97. package/dist/components/onboarding-guides/index.cjs +7 -7
  98. package/dist/components/onboarding-guides/index.js +6 -6
  99. package/dist/components/related-content/index.cjs +3 -3
  100. package/dist/components/related-content/index.js +2 -2
  101. package/dist/components/tickets/index.cjs +8 -8
  102. package/dist/components/tickets/index.js +7 -7
  103. package/dist/components/ui/avatar-stack.d.ts +1 -5
  104. package/dist/components/ui/avatar-stack.d.ts.map +1 -1
  105. package/dist/components/ui/index.cjs +4 -22
  106. package/dist/components/ui/index.cjs.map +1 -1
  107. package/dist/components/ui/index.js +3 -21
  108. package/dist/components/ui/reopen-ticket-request-modal.d.ts +1 -4
  109. package/dist/components/ui/reopen-ticket-request-modal.d.ts.map +1 -1
  110. package/dist/index.cjs +9 -27
  111. package/dist/index.cjs.map +1 -1
  112. package/dist/index.js +7 -25
  113. package/dist/index.js.map +1 -1
  114. package/package.json +1 -1
  115. package/src/components/chat/ai-assistant-info.tsx +1 -7
  116. package/src/components/chat/approval-request-message.tsx +1 -6
  117. package/src/components/chat/chat-input.tsx +1 -26
  118. package/src/components/chat/chat-message-enhanced.tsx +142 -180
  119. package/src/components/chat/chat-message-list.tsx +164 -169
  120. package/src/components/chat/error-message-display.tsx +3 -21
  121. package/src/components/chat/index.ts +0 -6
  122. package/src/components/chat/msp-organization-card.tsx +0 -35
  123. package/src/components/chat/types/chat.types.ts +0 -15
  124. package/src/components/chat/types/component.types.ts +0 -20
  125. package/src/components/ui/avatar-stack.tsx +5 -15
  126. package/src/components/ui/reopen-ticket-request-modal.tsx +2 -6
  127. package/src/styles/ods-colors.css +0 -10
  128. package/src/styles/ods-effects.css +0 -13
  129. package/dist/chunk-22RSJ57Z.js.map +0 -1
  130. package/dist/chunk-JNDHXL2D.cjs.map +0 -1
  131. package/dist/chunk-QNWQQ4RT.cjs.map +0 -1
  132. package/dist/chunk-SI77HF4U.cjs.map +0 -1
  133. package/dist/chunk-YPXXXX6R.js.map +0 -1
  134. package/dist/components/chat/chat-appearance-context.d.ts +0 -9
  135. package/dist/components/chat/chat-appearance-context.d.ts.map +0 -1
  136. package/dist/components/chat/chat-nav-sidebar.d.ts +0 -50
  137. package/dist/components/chat/chat-nav-sidebar.d.ts.map +0 -1
  138. package/dist/components/chat/chat-top-navigation.d.ts +0 -29
  139. package/dist/components/chat/chat-top-navigation.d.ts.map +0 -1
  140. package/dist/components/chat/onboarding-carousel.d.ts +0 -29
  141. package/dist/components/chat/onboarding-carousel.d.ts.map +0 -1
  142. package/dist/components/chat/system-event-message.d.ts +0 -14
  143. package/dist/components/chat/system-event-message.d.ts.map +0 -1
  144. package/src/components/chat/__tests__/chat-appearance.test.tsx +0 -79
  145. package/src/components/chat/__tests__/onboarding-carousel.test.tsx +0 -56
  146. package/src/components/chat/chat-appearance-context.tsx +0 -15
  147. package/src/components/chat/chat-nav-sidebar.tsx +0 -163
  148. package/src/components/chat/chat-top-navigation.tsx +0 -109
  149. package/src/components/chat/onboarding-carousel.tsx +0 -162
  150. package/src/components/chat/system-event-message.tsx +0 -54
  151. package/src/components/ui/__tests__/avatar-stack.test.tsx +0 -25
  152. package/src/stories/FaeLayoutV2.stories.tsx +0 -150
  153. package/src/stories/FaeOnboardingV2.stories.tsx +0 -68
  154. package/src/stories/FaeThreadV2.stories.tsx +0 -192
  155. package/src/stories/fae-brand-decorator.tsx +0 -28
  156. /package/dist/{chunk-IZ3WKG62.js.map → chunk-65KTXRBY.js.map} +0 -0
  157. /package/dist/{chunk-IBKMIJ7Y.js.map → chunk-7XF63THA.js.map} +0 -0
  158. /package/dist/{chunk-32KBPEF7.js.map → chunk-B32YLOYQ.js.map} +0 -0
  159. /package/dist/{chunk-QSRUYHTW.js.map → chunk-BZ2XOEHW.js.map} +0 -0
  160. /package/dist/{chunk-DDFYKF3B.js.map → chunk-GHLGXFNU.js.map} +0 -0
  161. /package/dist/{chunk-VDV7K5TD.js.map → chunk-GXV6UEVY.js.map} +0 -0
  162. /package/dist/{chunk-KTWJKCUB.js.map → chunk-N4Z6X5BV.js.map} +0 -0
  163. /package/dist/{chunk-T2RHI676.js.map → chunk-O7LOZW4R.js.map} +0 -0
  164. /package/dist/{chunk-CHRDLXXX.js.map → chunk-PSQKD6ZN.js.map} +0 -0
  165. /package/dist/{chunk-HLEXYTOE.js.map → chunk-TUG7IT6H.js.map} +0 -0
  166. /package/dist/{chunk-WJ5XN4AL.js.map → chunk-UT333EOT.js.map} +0 -0
  167. /package/dist/{chunk-5GK7TU2Q.js.map → chunk-WJK2EETW.js.map} +0 -0
  168. /package/dist/{chunk-XODE4YTG.js.map → chunk-XW7MUIW4.js.map} +0 -0
  169. /package/dist/{chunk-N74MQ4HQ.js.map → chunk-Y7ZYOQNB.js.map} +0 -0
  170. /package/dist/{chunk-TILC52ZF.js.map → chunk-ZF4Z36G4.js.map} +0 -0
@@ -16,12 +16,10 @@ import {
16
16
  import { renderToStaticMarkup } from 'react-dom/server';
17
17
  import { cn } from '../../utils/cn';
18
18
  import { Send01Icon, StopCircleIcon } from '../icons-v2-generated';
19
- import { AvatarStack } from '../ui/avatar-stack';
20
19
  import { Tag } from '../ui/tag';
21
20
  import { ChatTypingIndicator } from './chat-typing-indicator';
22
21
  import { SlashCommandSuggestions } from './slash-command-suggestions';
23
22
  import type { ChatInputProps, ChatInputRef, MentionMeta, SlashCommandSummary } from './types';
24
- import { CHAT_APPEARANCE } from './types/chat.types';
25
23
 
26
24
  /** SHARED with `lib/config/slash-commands-config.ts` AND the chat-route slash
27
25
  * dispatch parser. Keep all three in sync. */
@@ -166,9 +164,6 @@ const ChatInput = forwardRef<ChatInputRef, ChatInputProps>((allProps, ref) => {
166
164
  onStop,
167
165
  sending = false,
168
166
  awaitingResponse = false,
169
- awaitingTeam,
170
- awaitingTeamCount,
171
- appearance = CHAT_APPEARANCE.CLASSIC,
172
167
  placeholder = 'Enter your Request...',
173
168
  reserveAvatarOffset: _reserveAvatarOffset,
174
169
  disabled = false,
@@ -565,31 +560,11 @@ const ChatInput = forwardRef<ChatInputRef, ChatInputProps>((allProps, ref) => {
565
560
  className,
566
561
  )}
567
562
  >
568
- {awaitingResponse && appearance === CHAT_APPEARANCE.CLASSIC ? (
563
+ {awaitingResponse ? (
569
564
  <div className="relative flex items-center justify-center gap-[var(--spacing-system-xs)] rounded-md border border-ods-border bg-ods-card px-[var(--spacing-system-s)] py-[var(--spacing-system-s)] transition-colors">
570
565
  <ChatTypingIndicator size="sm" dotClassName="bg-ods-text-primary" />
571
566
  <p className="text-ods-text-secondary text-h4">Waiting for Technician Response</p>
572
567
  </div>
573
- ) : awaitingResponse ? (
574
- // Hand-off bar (fae chat v2): who has the conversation now, and that a
575
- // reply is on its way. Same 48px height as the editor row it replaces,
576
- // so the thread above does not jump when the chat is handed off.
577
- <div
578
- role="status"
579
- className="flex min-h-11 w-full items-center justify-center gap-[var(--spacing-system-xs)] rounded-md border border-ods-border bg-ods-bg p-[var(--spacing-system-sf)] md:min-h-12"
580
- >
581
- {awaitingTeam && awaitingTeam.length > 0 && (
582
- <AvatarStack
583
- people={awaitingTeam}
584
- total={awaitingTeamCount}
585
- size="xs"
586
- ringClassName="ring-ods-bg"
587
- label="Technicians"
588
- />
589
- )}
590
- <p className="min-w-0 truncate text-ods-text-primary text-h4">Handed off to your technical support team</p>
591
- <ChatTypingIndicator size="sm" dotClassName="bg-ods-text-secondary" />
592
- </div>
593
568
  ) : (
594
569
  <div className="relative">
595
570
  <SlashCommandSuggestions
@@ -11,7 +11,6 @@ import { SquareAvatar } from '../ui/square-avatar';
11
11
  import { ApprovalBatchMessage } from './approval-batch-message';
12
12
  import { ApprovalRequestMessage } from './approval-request-message';
13
13
  import { AskDisplay } from './ask-display';
14
- import { ChatAppearanceContext, useChatAppearance } from './chat-appearance-context';
15
14
  import { ChatContextChipStrip } from './chat-context-picker';
16
15
  import type { ChatRef } from './chat-ref.types';
17
16
  import { ContextCompactionDisplay } from './context-compaction-display';
@@ -21,13 +20,11 @@ import { EscalationOfferMessage } from './escalation-offer-message';
21
20
  import { remarkCardLinks } from './remark-card-links';
22
21
  import { remarkMentionChips } from './remark-mention-chips';
23
22
  import { remarkStripCitations } from './remark-strip-citations';
24
- import { SystemEventMessage } from './system-event-message';
25
23
  import { ThinkingDisplay } from './thinking-display';
26
24
  import { TicketEscalatedMessage } from './ticket-escalated-message';
27
25
  import { TicketEventMessage } from './ticket-event-message';
28
26
  import { ToolExecutionDisplay } from './tool-execution-display';
29
27
  import type { AskSegment, MessageSegment, MessageContent, ChatMessageEnhancedProps } from './types';
30
- import { CHAT_APPEARANCE } from './types/chat.types';
31
28
 
32
29
  /** Inline `@marker:id` mention token in the message body (sibling of the
33
30
  * `[card://]` grammar) — used to filter out items rendered inline from the
@@ -97,7 +94,6 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
97
94
  showAvatar = true,
98
95
  assistantType,
99
96
  approvalVariant,
100
- appearance: appearanceProp,
101
97
  authorType: authorTypeProp,
102
98
  assistantIcon,
103
99
  contextItems,
@@ -114,9 +110,6 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
114
110
  ) => {
115
111
  const isUser = role === 'user';
116
112
  const isError = role === 'error';
117
- const inheritedAppearance = useChatAppearance();
118
- const appearance = appearanceProp ?? inheritedAppearance;
119
- const isV2 = appearance === CHAT_APPEARANCE.V2;
120
113
  const authorType = authorTypeProp ?? (isUser ? 'user' : assistantType === 'mingo' ? 'mingo' : 'fae');
121
114
 
122
115
  // Inline-card rendering uses a HOST-PROVIDED `renderEntityCard` function
@@ -525,8 +518,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
525
518
  // (passing pre-joined initials like "PS" would collapse to one letter,
526
519
  // since getFirstLastInitials treats it as a single word).
527
520
  fallback: displayName,
528
- // v2 (fae chat v2): a 24px face that sits on the name's line height.
529
- size: isV2 ? ('xs' as const) : ('sm' as const),
521
+ size: 'sm' as const,
530
522
  variant: 'round' as const,
531
523
  // User avatar: compact 20×20 with 2px padding and a subtle gray fill
532
524
  // (`bg-ods-card`) so the `border-ods-border` ring stays visible — the
@@ -544,32 +536,9 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
544
536
 
545
537
  const isSystem = authorType === 'system';
546
538
 
547
- // v2 draws a system line (e.g. a technician joining) as an in-thread
548
- // receipt card rather than as an author row with no body.
549
- if (isV2 && isSystem && name) {
550
- return (
551
- <div ref={ref} className={cn('relative py-[calc(var(--spacing-system-m)/2)]', className)} {...props}>
552
- <ChatAppearanceContext.Provider value={appearance}>
553
- <SystemEventMessage text={name} timestamp={timestamp} />
554
- </ChatAppearanceContext.Provider>
555
- </div>
556
- );
557
- }
558
-
559
539
  return (
560
- <ChatAppearanceContext.Provider value={appearance}>
561
- <div
562
- ref={ref}
563
- className={cn(
564
- 'relative',
565
- // v2 spaces messages 16px apart (spacing-system-m), split across the
566
- // two neighbours' padding so blocks outside a row keep the rhythm.
567
- isV2 ? 'py-[calc(var(--spacing-system-m)/2)]' : 'py-[var(--spacing-system-s)]',
568
- className,
569
- )}
570
- {...props}
571
- >
572
- {/* Message Content — full panel width.
540
+ <div ref={ref} className={cn('relative py-[var(--spacing-system-s)]', className)} {...props}>
541
+ {/* Message Content — full panel width.
573
542
  Avatar is INLINE in the name row below (2025-2026 chat
574
543
  pattern — Claude.ai, ChatGPT, Gemini, Perplexity).
575
544
  Legacy hanging-avatar layout (`absolute -left-16`) wasted
@@ -583,8 +552,8 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
583
552
  layout per drawer-resize frame in Safari / the desktop WKWebView
584
553
  vs ~9ms as grid (Chrome: ~2ms either way). Same gap and stretch,
585
554
  no margin collapsing — visually identical. */}
586
- <div className="grid min-w-0 grid-cols-1 gap-[var(--spacing-system-xxs)]">
587
- {/* Avatar + Name + Timestamp Row.
555
+ <div className="grid min-w-0 grid-cols-1 gap-[var(--spacing-system-xxs)]">
556
+ {/* Avatar + Name + Timestamp Row.
588
557
  Sizing rationale (per design-token measurements):
589
558
  - Name uses `text-h3` = 14px mobile / 18px desktop.
590
559
  - Avatar uses `SquareAvatar size="sm"` = 32px — the
@@ -600,161 +569,155 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
600
569
  inside via `flex items-center justify-center` — they
601
570
  should be sized at ~50-60% of the wrapper (h-4 w-4 =
602
571
  16px works well for a 32px circle). */}
603
- <div
604
- className={cn(
605
- 'flex items-center',
606
- isV2 ? 'gap-[var(--spacing-system-xxs)]' : 'gap-[var(--spacing-system-xs)]',
607
- )}
608
- >
609
- {/* Avatar rules:
572
+ <div className="flex items-center gap-[var(--spacing-system-xs)]">
573
+ {/* Avatar rules:
610
574
  - Assistant/Fae always show an avatar — host brand icon when no
611
575
  image is supplied, else the filled SquareAvatar.
612
576
  - User shows the SquareAvatar ONLY when an avatar image actually
613
577
  arrived. With no user avatar we hide the block entirely (just
614
578
  the name), instead of an initials placeholder. TEMPORARY —
615
579
  restore the user placeholder when user avatars ship. */}
616
- {showAvatar &&
617
- !isSystem &&
618
- !(isUser && !avatar) &&
619
- (!isUser && assistantIcon && !avatar ? (
620
- // Host-supplied brand icon (e.g. Mingo): render it directly,
621
- // no filled pill — the icon carries its own brand accent.
622
- <div className="flex flex-shrink-0 items-center justify-center">{assistantIcon}</div>
623
- ) : (
624
- <SquareAvatar {...avatarProps} />
625
- ))}
626
- <span
627
- className={cn(
628
- 'flex-1 !font-mono !font-medium text-h3',
629
- authorType === 'system'
580
+ {showAvatar &&
581
+ !isSystem &&
582
+ !(isUser && !avatar) &&
583
+ (!isUser && assistantIcon && !avatar ? (
584
+ // Host-supplied brand icon (e.g. Mingo): render it directly,
585
+ // no filled pill — the icon carries its own brand accent.
586
+ <div className="flex flex-shrink-0 items-center justify-center">{assistantIcon}</div>
587
+ ) : (
588
+ <SquareAvatar {...avatarProps} />
589
+ ))}
590
+ <span
591
+ className={cn(
592
+ 'flex-1 !font-mono !font-medium text-h3',
593
+ authorType === 'system'
594
+ ? 'text-ods-open-yellow'
595
+ : authorType === 'admin'
630
596
  ? 'text-ods-open-yellow'
631
- : authorType === 'admin'
632
- ? 'text-ods-open-yellow'
633
- : authorType === 'mingo'
634
- ? 'text-ods-flamingo-cyan'
635
- : authorType === 'fae'
636
- ? 'text-ods-flamingo-pink'
637
- : 'text-ods-text-secondary',
638
- )}
639
- >
640
- {name || (isUser ? 'User' : assistantType === 'mingo' ? 'Mingo' : 'Fae')}
641
- {!isSystem && !isV2 && ':'}
642
- </span>
643
- {timestamp && (
644
- <span className="shrink-0 whitespace-nowrap text-ods-text-secondary text-h6">
645
- {formatMessageTimestamp(timestamp)}
646
- </span>
597
+ : authorType === 'mingo'
598
+ ? 'text-ods-flamingo-cyan'
599
+ : authorType === 'fae'
600
+ ? 'text-ods-flamingo-pink'
601
+ : 'text-ods-text-secondary',
647
602
  )}
648
- </div>
649
-
650
- {/* Message segments — hidden for system messages without content */}
651
- {(!isSystem || segments.length > 0) && (
652
- <div className="grid grid-cols-1 gap-2">
653
- {segments.map((segment, index) => {
654
- // The engine's streaming path (atomic-block memoization +
655
- // fence tail-completion + aria-live) applies ONLY to the
656
- // actively streaming segment: last segment of a message that
657
- // is still typing. On completion `isTyping` flips false and
658
- // the engine does one authoritative whole-document parse.
659
- const segmentIsStreaming = index === segments.length - 1 && !!isTyping;
660
- if (segment.type === 'text') {
661
- return (
662
- <div
663
- key={index}
664
- className={cn(
665
- 'w-full min-w-0 break-words text-h4',
666
- isError ? 'text-ods-error' : 'text-ods-text-primary',
667
- )}
668
- >
669
- {renderSegmentBody(index, segment.text, segmentIsStreaming)}
670
- </div>
671
- );
672
- } else if (segment.type === 'ask') {
673
- // Only the run's head draws — the tail segments are pages of
674
- // the card already rendered above them.
675
- const run = askRuns.get(index);
676
- if (!run) return null;
677
- return <AskDisplay key={index} cards={run} onSelect={onAskSelect} />;
678
- } else if (segment.type === 'tool_execution') {
679
- return (
680
- <ToolExecutionDisplay
681
- key={index}
682
- message={segment.data}
683
- assistantType={assistantType}
684
- variant={approvalVariant}
685
- />
686
- );
687
- } else if (segment.type === 'approval_request') {
688
- return (
689
- <ApprovalRequestMessage
690
- key={index}
691
- data={segment.data}
692
- status={segment.status}
693
- resolvedByName={segment.resolvedByName}
694
- onApprove={segment.onApprove}
695
- onReject={segment.onReject}
696
- assistantType={assistantType}
697
- variant={approvalVariant}
698
- />
699
- );
700
- } else if (segment.type === 'approval_batch') {
701
- return (
702
- <ApprovalBatchMessage
703
- key={index}
704
- data={segment.data}
705
- status={segment.status}
706
- resolvedByName={segment.resolvedByName}
707
- onApprove={segment.onApprove}
708
- onReject={segment.onReject}
709
- assistantType={assistantType}
710
- variant={approvalVariant}
711
- />
712
- );
713
- } else if (segment.type === 'escalation_offer') {
714
- return (
715
- <EscalationOfferMessage
716
- key={index}
717
- data={segment.data}
718
- status={segment.status}
719
- resolvedByName={segment.resolvedByName}
720
- onApprove={segment.onApprove}
721
- onReject={segment.onReject}
722
- />
723
- );
724
- } else if (segment.type === 'ticket_escalated') {
725
- return <TicketEscalatedMessage key={index} data={segment.data} timestamp={timestamp} />;
726
- } else if (segment.type === 'ticket_event') {
727
- // The card's own event time; the bubble timestamp is the
728
- // turn's FIRST row and lags every later lifecycle event.
729
- return (
730
- <TicketEventMessage key={index} data={segment.data} timestamp={segment.occurredAt ?? timestamp} />
731
- );
732
- } else if (segment.type === 'error') {
733
- return <ErrorMessageDisplay key={index} title={segment.title} details={segment.details} />;
734
- } else if (segment.type === 'context_compaction') {
735
- return <ContextCompactionDisplay key={index} status={segment.status} />;
736
- } else if (segment.type === 'thinking') {
737
- const isStreaming = index === segments.length - 1 && isTyping;
738
- return <ThinkingDisplay key={index} text={segment.text} isStreaming={isStreaming} />;
739
- }
740
- return null;
741
- })}
742
- </div>
603
+ >
604
+ {name || (isUser ? 'User' : assistantType === 'mingo' ? 'Mingo' : 'Fae')}
605
+ {!isSystem && ':'}
606
+ </span>
607
+ {timestamp && (
608
+ <span className="shrink-0 whitespace-nowrap text-ods-text-secondary text-h6">
609
+ {formatMessageTimestamp(timestamp)}
610
+ </span>
743
611
  )}
612
+ </div>
744
613
 
745
- {/* Attached entity-context chips (user bubbles). Read-only — no
614
+ {/* Message segments — hidden for system messages without content */}
615
+ {(!isSystem || segments.length > 0) && (
616
+ <div className="grid grid-cols-1 gap-2">
617
+ {segments.map((segment, index) => {
618
+ // The engine's streaming path (atomic-block memoization +
619
+ // fence tail-completion + aria-live) applies ONLY to the
620
+ // actively streaming segment: last segment of a message that
621
+ // is still typing. On completion `isTyping` flips false and
622
+ // the engine does one authoritative whole-document parse.
623
+ const segmentIsStreaming = index === segments.length - 1 && !!isTyping;
624
+ if (segment.type === 'text') {
625
+ return (
626
+ <div
627
+ key={index}
628
+ className={cn(
629
+ 'w-full min-w-0 break-words text-h4',
630
+ isError ? 'text-ods-error' : 'text-ods-text-primary',
631
+ )}
632
+ >
633
+ {renderSegmentBody(index, segment.text, segmentIsStreaming)}
634
+ </div>
635
+ );
636
+ } else if (segment.type === 'ask') {
637
+ // Only the run's head draws — the tail segments are pages of
638
+ // the card already rendered above them.
639
+ const run = askRuns.get(index);
640
+ if (!run) return null;
641
+ return <AskDisplay key={index} cards={run} onSelect={onAskSelect} />;
642
+ } else if (segment.type === 'tool_execution') {
643
+ return (
644
+ <ToolExecutionDisplay
645
+ key={index}
646
+ message={segment.data}
647
+ assistantType={assistantType}
648
+ variant={approvalVariant}
649
+ />
650
+ );
651
+ } else if (segment.type === 'approval_request') {
652
+ return (
653
+ <ApprovalRequestMessage
654
+ key={index}
655
+ data={segment.data}
656
+ status={segment.status}
657
+ resolvedByName={segment.resolvedByName}
658
+ onApprove={segment.onApprove}
659
+ onReject={segment.onReject}
660
+ assistantType={assistantType}
661
+ variant={approvalVariant}
662
+ />
663
+ );
664
+ } else if (segment.type === 'approval_batch') {
665
+ return (
666
+ <ApprovalBatchMessage
667
+ key={index}
668
+ data={segment.data}
669
+ status={segment.status}
670
+ resolvedByName={segment.resolvedByName}
671
+ onApprove={segment.onApprove}
672
+ onReject={segment.onReject}
673
+ assistantType={assistantType}
674
+ variant={approvalVariant}
675
+ />
676
+ );
677
+ } else if (segment.type === 'escalation_offer') {
678
+ return (
679
+ <EscalationOfferMessage
680
+ key={index}
681
+ data={segment.data}
682
+ status={segment.status}
683
+ resolvedByName={segment.resolvedByName}
684
+ onApprove={segment.onApprove}
685
+ onReject={segment.onReject}
686
+ />
687
+ );
688
+ } else if (segment.type === 'ticket_escalated') {
689
+ return <TicketEscalatedMessage key={index} data={segment.data} timestamp={timestamp} />;
690
+ } else if (segment.type === 'ticket_event') {
691
+ // The card's own event time; the bubble timestamp is the
692
+ // turn's FIRST row and lags every later lifecycle event.
693
+ return (
694
+ <TicketEventMessage key={index} data={segment.data} timestamp={segment.occurredAt ?? timestamp} />
695
+ );
696
+ } else if (segment.type === 'error') {
697
+ return <ErrorMessageDisplay key={index} title={segment.title} details={segment.details} />;
698
+ } else if (segment.type === 'context_compaction') {
699
+ return <ContextCompactionDisplay key={index} status={segment.status} />;
700
+ } else if (segment.type === 'thinking') {
701
+ const isStreaming = index === segments.length - 1 && isTyping;
702
+ return <ThinkingDisplay key={index} text={segment.text} isStreaming={isStreaming} />;
703
+ }
704
+ return null;
705
+ })}
706
+ </div>
707
+ )}
708
+
709
+ {/* Attached entity-context chips (user bubbles). Read-only — no
746
710
  remove affordance once the message is sent (Figma 31:28709). */}
747
- {stripContextItems && stripContextItems.length > 0 && (
748
- <ChatContextChipStrip
749
- items={stripContextItems}
750
- resolveIcon={resolveContextIcon}
751
- renderItem={renderContextItem}
752
- className="mt-2"
753
- />
754
- )}
755
- </div>
711
+ {stripContextItems && stripContextItems.length > 0 && (
712
+ <ChatContextChipStrip
713
+ items={stripContextItems}
714
+ resolveIcon={resolveContextIcon}
715
+ renderItem={renderContextItem}
716
+ className="mt-2"
717
+ />
718
+ )}
756
719
  </div>
757
- </ChatAppearanceContext.Provider>
720
+ </div>
758
721
  );
759
722
  },
760
723
  );
@@ -772,7 +735,6 @@ const MemoizedChatMessageEnhanced = memo(ChatMessageEnhanced, (prevProps, nextPr
772
735
  prevProps.showAvatar === nextProps.showAvatar &&
773
736
  prevProps.assistantType === nextProps.assistantType &&
774
737
  prevProps.approvalVariant === nextProps.approvalVariant &&
775
- prevProps.appearance === nextProps.appearance &&
776
738
  prevProps.authorType === nextProps.authorType &&
777
739
  prevProps.assistantIcon === nextProps.assistantIcon &&
778
740
  prevProps.className === nextProps.className &&