@flamingo-stack/openframe-frontend-core 0.0.663 → 0.0.664-2366.7030.1

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 (135) hide show
  1. package/dist/{chunk-HK7R7HSW.cjs → chunk-3SS37SNM.cjs} +10 -10
  2. package/dist/{chunk-HK7R7HSW.cjs.map → chunk-3SS37SNM.cjs.map} +1 -1
  3. package/dist/{chunk-BASKR3Z4.cjs → chunk-5R4QMTML.cjs} +8 -4
  4. package/dist/chunk-5R4QMTML.cjs.map +1 -0
  5. package/dist/{chunk-B5DZ37OG.cjs → chunk-6FD7ZEBY.cjs} +248 -248
  6. package/dist/{chunk-B5DZ37OG.cjs.map → chunk-6FD7ZEBY.cjs.map} +1 -1
  7. package/dist/{chunk-ARMNWO4M.cjs → chunk-6W5N23LC.cjs} +38 -38
  8. package/dist/{chunk-ARMNWO4M.cjs.map → chunk-6W5N23LC.cjs.map} +1 -1
  9. package/dist/{chunk-EPV6XTB5.js → chunk-77WMNQTE.js} +3 -3
  10. package/dist/{chunk-B2LSPZFR.js → chunk-BKCPXDOE.js} +3 -3
  11. package/dist/{chunk-IXLJIH6U.js → chunk-BPKEDLGR.js} +7 -7
  12. package/dist/{chunk-OZTH6T4W.js → chunk-CJ5CFXYX.js} +2 -2
  13. package/dist/{chunk-PW5QV4FH.js → chunk-CQJXOBWK.js} +5 -5
  14. package/dist/{chunk-WB32HEBY.js → chunk-DNFC6RYF.js} +4 -4
  15. package/dist/{chunk-ISPIEV7D.cjs → chunk-EMJAT6IK.cjs} +44 -44
  16. package/dist/{chunk-ISPIEV7D.cjs.map → chunk-EMJAT6IK.cjs.map} +1 -1
  17. package/dist/{chunk-K56K55H2.cjs → chunk-FGQSSFP4.cjs} +10 -10
  18. package/dist/{chunk-K56K55H2.cjs.map → chunk-FGQSSFP4.cjs.map} +1 -1
  19. package/dist/{chunk-WDE7ETA5.cjs → chunk-FJNGXGJW.cjs} +5 -5
  20. package/dist/{chunk-WDE7ETA5.cjs.map → chunk-FJNGXGJW.cjs.map} +1 -1
  21. package/dist/{chunk-MRBXYWEX.js → chunk-GDTFJ5QN.js} +2 -2
  22. package/dist/{chunk-A3L5AJNX.js → chunk-JHAS6Y7P.js} +6 -6
  23. package/dist/{chunk-4IQC3VKQ.cjs → chunk-JHH7NETU.cjs} +5 -5
  24. package/dist/{chunk-4IQC3VKQ.cjs.map → chunk-JHH7NETU.cjs.map} +1 -1
  25. package/dist/{chunk-XCA5HIVS.cjs → chunk-KBUYG5N6.cjs} +33 -33
  26. package/dist/{chunk-XCA5HIVS.cjs.map → chunk-KBUYG5N6.cjs.map} +1 -1
  27. package/dist/{chunk-SAM7XUKM.cjs → chunk-L4ZNA3NX.cjs} +3 -3
  28. package/dist/{chunk-SAM7XUKM.cjs.map → chunk-L4ZNA3NX.cjs.map} +1 -1
  29. package/dist/{chunk-67DMHSF7.cjs → chunk-LCYYTWRF.cjs} +7 -7
  30. package/dist/{chunk-67DMHSF7.cjs.map → chunk-LCYYTWRF.cjs.map} +1 -1
  31. package/dist/{chunk-4AAPEV7A.cjs → chunk-LTW2GWJ2.cjs} +25 -25
  32. package/dist/{chunk-4AAPEV7A.cjs.map → chunk-LTW2GWJ2.cjs.map} +1 -1
  33. package/dist/{chunk-UGR37VMX.cjs → chunk-LXL3PZE2.cjs} +12 -12
  34. package/dist/{chunk-UGR37VMX.cjs.map → chunk-LXL3PZE2.cjs.map} +1 -1
  35. package/dist/{chunk-TXKAFOSY.js → chunk-MBLACUCE.js} +5 -5
  36. package/dist/{chunk-BSETJKSW.cjs → chunk-MNUR6PAJ.cjs} +105 -68
  37. package/dist/chunk-MNUR6PAJ.cjs.map +1 -0
  38. package/dist/{chunk-JOBTZDZZ.js → chunk-O3GB7JFL.js} +4 -4
  39. package/dist/{chunk-J75M5PKR.js → chunk-OLCI3UA5.js} +7 -7
  40. package/dist/{chunk-RP4TSHMY.js → chunk-OMTLCMQB.js} +768 -731
  41. package/dist/chunk-OMTLCMQB.js.map +1 -0
  42. package/dist/{chunk-6IU4FW4G.js → chunk-QR3NUFA5.js} +2 -2
  43. package/dist/{chunk-RZ4UO3OK.js → chunk-RM4S53GB.js} +2 -2
  44. package/dist/{chunk-SHUKF2HE.js → chunk-RT5JNTCN.js} +4 -4
  45. package/dist/{chunk-KU7HL52F.js → chunk-VQZXJIPY.js} +8 -4
  46. package/dist/chunk-VQZXJIPY.js.map +1 -0
  47. package/dist/{chunk-XWQOOMT2.cjs → chunk-X32X52VN.cjs} +75 -75
  48. package/dist/{chunk-XWQOOMT2.cjs.map → chunk-X32X52VN.cjs.map} +1 -1
  49. package/dist/{chunk-KPXGWKFF.cjs → chunk-XCACXPCM.cjs} +22 -22
  50. package/dist/{chunk-KPXGWKFF.cjs.map → chunk-XCACXPCM.cjs.map} +1 -1
  51. package/dist/{chunk-2MRQ3XK2.js → chunk-YRMKUDOE.js} +5 -5
  52. package/dist/{chunk-UZMPTJ5B.js → chunk-Z2MJPTIB.js} +3 -3
  53. package/dist/{chunk-KYOOTRYM.cjs → chunk-ZGPST26T.cjs} +51 -51
  54. package/dist/{chunk-KYOOTRYM.cjs.map → chunk-ZGPST26T.cjs.map} +1 -1
  55. package/dist/{chunk-RDYUPNDO.cjs → chunk-ZMZUF6FX.cjs} +3 -3
  56. package/dist/{chunk-RDYUPNDO.cjs.map → chunk-ZMZUF6FX.cjs.map} +1 -1
  57. package/dist/components/case-studies/index.cjs +20 -20
  58. package/dist/components/case-studies/index.js +5 -5
  59. package/dist/components/chat/chat-dialog-menu-items.d.ts +15 -0
  60. package/dist/components/chat/chat-dialog-menu-items.d.ts.map +1 -0
  61. package/dist/components/chat/chat-panel-header-mobile.d.ts +1 -1
  62. package/dist/components/chat/chat-panel-header-mobile.d.ts.map +1 -1
  63. package/dist/components/chat/chat-panel-header.d.ts +6 -4
  64. package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
  65. package/dist/components/chat/embeddable-chat.d.ts +2 -1
  66. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  67. package/dist/components/chat/index.cjs +3 -3
  68. package/dist/components/chat/index.js +2 -2
  69. package/dist/components/chat/mingo-chat-history.d.ts +6 -4
  70. package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
  71. package/dist/components/chat/mingo-history-rail.d.ts +6 -4
  72. package/dist/components/chat/mingo-history-rail.d.ts.map +1 -1
  73. package/dist/components/chat/types/unified-chat-state.types.d.ts +9 -3
  74. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  75. package/dist/components/contact/index.cjs +6 -6
  76. package/dist/components/contact/index.js +5 -5
  77. package/dist/components/docs/index.cjs +9 -9
  78. package/dist/components/docs/index.js +8 -8
  79. package/dist/components/embeds/index.cjs +6 -6
  80. package/dist/components/embeds/index.js +5 -5
  81. package/dist/components/faq/index.cjs +6 -6
  82. package/dist/components/faq/index.js +5 -5
  83. package/dist/components/features/index.cjs +5 -5
  84. package/dist/components/features/index.js +4 -4
  85. package/dist/components/help-center-pages/index.cjs +25 -25
  86. package/dist/components/help-center-pages/index.js +16 -16
  87. package/dist/components/icons/empty-vendor-icon.d.ts.map +1 -1
  88. package/dist/components/icons/index.cjs +2 -2
  89. package/dist/components/icons/index.js +1 -1
  90. package/dist/components/index.cjs +42 -42
  91. package/dist/components/index.js +15 -15
  92. package/dist/components/meeting-scheduler/index.cjs +28 -28
  93. package/dist/components/meeting-scheduler/index.js +4 -4
  94. package/dist/components/navigation/index.cjs +8 -8
  95. package/dist/components/navigation/index.js +7 -7
  96. package/dist/components/onboarding-guides/index.cjs +8 -8
  97. package/dist/components/onboarding-guides/index.js +7 -7
  98. package/dist/components/tickets/index.cjs +9 -9
  99. package/dist/components/tickets/index.js +8 -8
  100. package/dist/components/ui/index.cjs +5 -5
  101. package/dist/components/ui/index.js +4 -4
  102. package/dist/hooks/index.cjs +3 -3
  103. package/dist/hooks/index.js +2 -2
  104. package/dist/index.cjs +9 -9
  105. package/dist/index.js +8 -8
  106. package/package.json +1 -1
  107. package/src/components/chat/__tests__/chat-dialog-menu-items.test.tsx +39 -0
  108. package/src/components/chat/chat-dialog-menu-items.tsx +51 -0
  109. package/src/components/chat/chat-panel-header-mobile.tsx +7 -7
  110. package/src/components/chat/chat-panel-header.tsx +11 -10
  111. package/src/components/chat/embeddable-chat.tsx +17 -7
  112. package/src/components/chat/mingo-chat-history.tsx +18 -22
  113. package/src/components/chat/mingo-history-rail.tsx +7 -3
  114. package/src/components/chat/types/unified-chat-state.types.ts +9 -3
  115. package/src/components/icons/empty-vendor-icon.tsx +10 -4
  116. package/dist/chunk-BASKR3Z4.cjs.map +0 -1
  117. package/dist/chunk-BSETJKSW.cjs.map +0 -1
  118. package/dist/chunk-KU7HL52F.js.map +0 -1
  119. package/dist/chunk-RP4TSHMY.js.map +0 -1
  120. /package/dist/{chunk-EPV6XTB5.js.map → chunk-77WMNQTE.js.map} +0 -0
  121. /package/dist/{chunk-B2LSPZFR.js.map → chunk-BKCPXDOE.js.map} +0 -0
  122. /package/dist/{chunk-IXLJIH6U.js.map → chunk-BPKEDLGR.js.map} +0 -0
  123. /package/dist/{chunk-OZTH6T4W.js.map → chunk-CJ5CFXYX.js.map} +0 -0
  124. /package/dist/{chunk-PW5QV4FH.js.map → chunk-CQJXOBWK.js.map} +0 -0
  125. /package/dist/{chunk-WB32HEBY.js.map → chunk-DNFC6RYF.js.map} +0 -0
  126. /package/dist/{chunk-MRBXYWEX.js.map → chunk-GDTFJ5QN.js.map} +0 -0
  127. /package/dist/{chunk-A3L5AJNX.js.map → chunk-JHAS6Y7P.js.map} +0 -0
  128. /package/dist/{chunk-TXKAFOSY.js.map → chunk-MBLACUCE.js.map} +0 -0
  129. /package/dist/{chunk-JOBTZDZZ.js.map → chunk-O3GB7JFL.js.map} +0 -0
  130. /package/dist/{chunk-J75M5PKR.js.map → chunk-OLCI3UA5.js.map} +0 -0
  131. /package/dist/{chunk-6IU4FW4G.js.map → chunk-QR3NUFA5.js.map} +0 -0
  132. /package/dist/{chunk-RZ4UO3OK.js.map → chunk-RM4S53GB.js.map} +0 -0
  133. /package/dist/{chunk-SHUKF2HE.js.map → chunk-RT5JNTCN.js.map} +0 -0
  134. /package/dist/{chunk-2MRQ3XK2.js.map → chunk-YRMKUDOE.js.map} +0 -0
  135. /package/dist/{chunk-UZMPTJ5B.js.map → chunk-Z2MJPTIB.js.map} +0 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `chatDialogMenuItems` — the one ⋯ menu shared by every conversation surface.
3
+ *
4
+ * Pins the design order (link, rename, compact, archive) and that an action
5
+ * appears only when its handler is wired, so no surface advertises something
6
+ * the host has not implemented.
7
+ */
8
+
9
+ import { describe, expect, it, vi } from 'vitest';
10
+
11
+ import { chatDialogMenuItems } from '../chat-dialog-menu-items';
12
+
13
+ describe('chatDialogMenuItems', () => {
14
+ it('lists every wired action in design order, each with an icon', () => {
15
+ const items = chatDialogMenuItems({
16
+ onArchive: vi.fn(),
17
+ onCompact: vi.fn(),
18
+ onRename: vi.fn(),
19
+ onCopyLink: vi.fn(),
20
+ });
21
+
22
+ expect(items.map(item => item.label)).toEqual([
23
+ 'Copy Chat Link',
24
+ 'Rename Chat',
25
+ 'Compact Chat Memory',
26
+ 'Archive Chat',
27
+ ]);
28
+ expect(items.every(item => item.icon !== undefined)).toBe(true);
29
+ });
30
+
31
+ it('omits actions without a handler', () => {
32
+ const onCompact = vi.fn();
33
+ const items = chatDialogMenuItems({ onCompact });
34
+
35
+ expect(items.map(item => item.id)).toEqual(['compact']);
36
+ items[0].onClick?.();
37
+ expect(onCompact).toHaveBeenCalledOnce();
38
+ });
39
+ });
@@ -0,0 +1,51 @@
1
+ import { BoxArchiveIcon, Link01HorizontalIcon, PackageCheckIcon, PenEditIcon } from '../icons-v2-generated';
2
+ import type { ActionsMenuItem } from '../ui/actions-menu';
3
+
4
+ export interface ChatDialogMenuActions {
5
+ onCopyLink?: () => void;
6
+ onRename?: () => void;
7
+ onCompact?: () => void;
8
+ onArchive?: () => void;
9
+ }
10
+
11
+ export const chatMenuIconClassName = 'h-full w-full text-ods-text-secondary';
12
+
13
+ /**
14
+ * The per-conversation ⋯ menu — one list for the desktop header, the mobile
15
+ * header, the split header and every history row, so the four can't drift in
16
+ * order, wording or icons. Each action appears only when its handler is set.
17
+ */
18
+ export function chatDialogMenuItems({
19
+ onCopyLink,
20
+ onRename,
21
+ onCompact,
22
+ onArchive,
23
+ }: ChatDialogMenuActions): ActionsMenuItem[] {
24
+ const items: (ActionsMenuItem | undefined)[] = [
25
+ onCopyLink && {
26
+ id: 'copy-link',
27
+ label: 'Copy Chat Link',
28
+ icon: <Link01HorizontalIcon className={chatMenuIconClassName} />,
29
+ onClick: onCopyLink,
30
+ },
31
+ onRename && {
32
+ id: 'rename',
33
+ label: 'Rename Chat',
34
+ icon: <PenEditIcon className={chatMenuIconClassName} />,
35
+ onClick: onRename,
36
+ },
37
+ onCompact && {
38
+ id: 'compact',
39
+ label: 'Compact Chat Memory',
40
+ icon: <PackageCheckIcon className={chatMenuIconClassName} />,
41
+ onClick: onCompact,
42
+ },
43
+ onArchive && {
44
+ id: 'archive',
45
+ label: 'Archive Chat',
46
+ icon: <BoxArchiveIcon className={chatMenuIconClassName} />,
47
+ onClick: onArchive,
48
+ },
49
+ ];
50
+ return items.filter((item): item is ActionsMenuItem => !!item);
51
+ }
@@ -4,6 +4,7 @@ import { cn } from '../../utils/cn';
4
4
  import { Chevron02LeftIcon, ClockHistoryIcon, XmarkIcon } from '../icons-v2-generated';
5
5
  import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu';
6
6
  import { Button } from '../ui/button';
7
+ import { chatDialogMenuItems, chatMenuIconClassName } from './chat-dialog-menu-items';
7
8
  import type { ChatPanelHeaderProps } from './chat-panel-header';
8
9
 
9
10
  export interface ChatPanelHeaderMobileProps extends ChatPanelHeaderProps {
@@ -38,6 +39,7 @@ export function ChatPanelHeaderMobile({
38
39
  onRestore,
39
40
  onRename,
40
41
  onArchive,
42
+ onCompact,
41
43
  onCopyLink,
42
44
  onOpenArchive,
43
45
  className,
@@ -49,15 +51,13 @@ export function ChatPanelHeaderMobile({
49
51
  !showBack &&
50
52
  onOpenArchive && {
51
53
  id: 'open-archive',
52
- label: 'Chat archive',
53
- icon: <ClockHistoryIcon className="h-full w-full" />,
54
+ label: 'Chat Archive',
55
+ icon: <ClockHistoryIcon className={chatMenuIconClassName} />,
54
56
  onClick: onOpenArchive,
55
57
  },
56
- isArchivedView && onRestore && { id: 'unarchive', label: 'Unarchive chat', onClick: onRestore },
57
- !isArchivedView && onCopyLink && { id: 'copy-link', label: 'Copy chat link', onClick: onCopyLink },
58
- !isArchivedView && onRename && { id: 'rename', label: 'Rename chat', onClick: onRename },
59
- !isArchivedView && onArchive && { id: 'archive', label: 'Archive chat', onClick: onArchive },
60
- ].filter(Boolean) as ActionsMenuItem[];
58
+ isArchivedView && onRestore && { id: 'unarchive', label: 'Unarchive Chat', onClick: onRestore },
59
+ ...(isArchivedView ? [] : chatDialogMenuItems({ onCopyLink, onRename, onCompact, onArchive })),
60
+ ].filter((item): item is ActionsMenuItem => !!item);
61
61
 
62
62
  return (
63
63
  <div
@@ -10,8 +10,9 @@ import {
10
10
  SearchIcon,
11
11
  } from '../icons-v2-generated';
12
12
  import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon';
13
- import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu';
13
+ import { ActionsMenuDropdown } from '../ui/actions-menu';
14
14
  import { SquareAvatar } from '../ui/square-avatar';
15
+ import { chatDialogMenuItems } from './chat-dialog-menu-items';
15
16
  import { ChatHeaderIconButton } from './chat-header-icon-button';
16
17
  import { ChatHeaderSearchField } from './chat-header-search-field';
17
18
  import { ChatPanelHeaderMobile } from './chat-panel-header-mobile';
@@ -41,11 +42,13 @@ export interface ChatPanelHeaderProps {
41
42
  onClose: () => void;
42
43
  /** Restore/unarchive — renders the refresh button (archived view only). */
43
44
  onRestore?: () => void;
44
- /** Rename — adds the "Rename chat" item to the ⋯ menu. */
45
+ /** Rename — adds the "Rename Chat" item to the ⋯ menu. */
45
46
  onRename?: () => void;
46
- /** Archive — adds the "Archive chat" item to the ⋯ menu. */
47
+ /** Archive — adds the "Archive Chat" item to the ⋯ menu. */
47
48
  onArchive?: () => void;
48
- /** Copy a shareable link to the open conversation — adds the "Copy chat link"
49
+ /** Compact — adds the "Compact Chat Memory" item to the ⋯ menu. */
50
+ onCompact?: () => void;
51
+ /** Copy a shareable link to the open conversation — adds the "Copy Chat Link"
49
52
  * item to the ⋯ menu. The host owns the URL and the copy. */
50
53
  onCopyLink?: () => void;
51
54
  /** Open the Chat Archive page — renders the clock button (list view only). */
@@ -87,6 +90,7 @@ export function ChatPanelHeader({
87
90
  onRestore,
88
91
  onRename,
89
92
  onArchive,
93
+ onCompact,
90
94
  onCopyLink,
91
95
  onOpenArchive,
92
96
  compact = false,
@@ -97,12 +101,8 @@ export function ChatPanelHeader({
97
101
  }: ChatPanelHeaderProps) {
98
102
  // Search open (with a wired handler) swaps the title for the inline field.
99
103
  const searchInline = searchActive && !!onSearchChange;
100
- // Desktop ⋯ menu (active, non-archived conversation only) — rename / archive.
101
- const menuItems = [
102
- onCopyLink && { id: 'copy-link', label: 'Copy chat link', onClick: onCopyLink },
103
- onRename && { id: 'rename', label: 'Rename chat', onClick: onRename },
104
- onArchive && { id: 'archive', label: 'Archive chat', onClick: onArchive },
105
- ].filter(Boolean) as ActionsMenuItem[];
104
+ // Desktop ⋯ menu (active, non-archived conversation only).
105
+ const menuItems = chatDialogMenuItems({ onCopyLink, onRename, onCompact, onArchive });
106
106
 
107
107
  return (
108
108
  <>
@@ -122,6 +122,7 @@ export function ChatPanelHeader({
122
122
  onRestore={onRestore}
123
123
  onRename={onRename}
124
124
  onArchive={onArchive}
125
+ onCompact={onCompact}
125
126
  onCopyLink={onCopyLink}
126
127
  onOpenArchive={onOpenArchive}
127
128
  />
@@ -54,7 +54,7 @@ import {
54
54
  } from '../icons-v2-generated';
55
55
  import { Chevron02LeftIcon } from '../icons-v2-generated/arrows/chevron-02-left-icon';
56
56
  import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon';
57
- import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu';
57
+ import { ActionsMenuDropdown } from '../ui/actions-menu';
58
58
  import { Button } from '../ui/button';
59
59
  import { Drawer, DrawerContent } from '../ui/drawer';
60
60
  import { HoverDropdown, type HoverDropdownItem } from '../ui/hover-dropdown';
@@ -63,6 +63,7 @@ import { SquareAvatar } from '../ui/square-avatar';
63
63
  import { ChatArchivePage } from './chat-archive-page';
64
64
  import { ChatAttachmentChipStrip } from './chat-attachment-bar';
65
65
  import { ChatComposer } from './chat-composer';
66
+ import { chatDialogMenuItems } from './chat-dialog-menu-items';
66
67
  import { ChatHeaderIconButton } from './chat-header-icon-button';
67
68
  import { ChatHeaderSearchField } from './chat-header-search-field';
68
69
  import { ChatMessageList } from './chat-message-list';
@@ -248,8 +249,9 @@ export interface EmbeddableChatProps {
248
249
  *
249
250
  * Same shape an adapter reports through `UnifiedChatState.dialogCapabilities`
250
251
  * (`ChatDialogCapabilities`) — one type, whether the list is host-owned or
251
- * adapter-owned. `onCopyLink` adds "Copy chat link" to the header ⋯ menu and
252
+ * adapter-owned. `onCopyLink` adds "Copy Chat Link" to the header ⋯ menu and
252
253
  * every row menu; the host owns the URL shape and the clipboard write.
254
+ * `compactDialog` adds "Compact Chat Memory" to the same menus.
253
255
  */
254
256
  mingoDialogCapabilities?: ChatDialogCapabilities;
255
257
 
@@ -2023,6 +2025,10 @@ function EmbeddableChatInner({
2023
2025
  activeDialogId && activeDialog && mingoCaps.canArchive ? () => setArchiveTarget(activeDialog) : undefined;
2024
2026
  const headerOnCopyLink =
2025
2027
  activeDialogId && activeDialog && mingoCaps.onCopyLink ? () => mingoCaps.onCopyLink?.(activeDialog) : undefined;
2028
+ const headerOnCompact =
2029
+ activeDialogId && activeDialog && mingoCaps.compactDialog
2030
+ ? () => mingoCaps.compactDialog?.(activeDialog)
2031
+ : undefined;
2026
2032
  const headerOnOpenArchive = fetchArchivedDialogs ? openArchive : undefined;
2027
2033
 
2028
2034
  // Header person (sub-line + 32px avatar, Figma 113:63273): the dialog OWNER
@@ -2110,11 +2116,12 @@ function EmbeddableChatInner({
2110
2116
  );
2111
2117
 
2112
2118
  // Desktop split header ⋯ menu (active, non-archived conversation only).
2113
- const splitHeaderMenuItems = [
2114
- headerOnCopyLink && { id: 'copy-link', label: 'Copy chat link', onClick: headerOnCopyLink },
2115
- headerOnRename && { id: 'rename', label: 'Rename chat', onClick: headerOnRename },
2116
- headerOnArchive && { id: 'archive', label: 'Archive chat', onClick: headerOnArchive },
2117
- ].filter(Boolean) as ActionsMenuItem[];
2119
+ const splitHeaderMenuItems = chatDialogMenuItems({
2120
+ onCopyLink: headerOnCopyLink,
2121
+ onRename: headerOnRename,
2122
+ onCompact: headerOnCompact,
2123
+ onArchive: headerOnArchive,
2124
+ });
2118
2125
 
2119
2126
  // Narrow (single-column) header. The Mingo empty state splits into a "Current
2120
2127
  // Chats" list header (search + archive, no back) and a "New Chat" compose
@@ -2158,6 +2165,7 @@ function EmbeddableChatInner({
2158
2165
  onRestore: headerOnRestore,
2159
2166
  onRename: headerOnRename,
2160
2167
  onArchive: headerOnArchive,
2168
+ onCompact: headerOnCompact,
2161
2169
  onCopyLink: headerOnCopyLink,
2162
2170
  onOpenArchive: headerOnOpenArchive,
2163
2171
  };
@@ -2389,6 +2397,7 @@ function EmbeddableChatInner({
2389
2397
  onRequestRename={mingoCaps.canRename ? setRenameTarget : undefined}
2390
2398
  onRequestArchive={mingoCaps.canArchive ? setArchiveTarget : undefined}
2391
2399
  onRequestCopyLink={mingoCaps.onCopyLink}
2400
+ onRequestCompact={mingoCaps.compactDialog}
2392
2401
  scope={dialogScope}
2393
2402
  onScopeChange={setDialogScope}
2394
2403
  searchQuery={mingoCaps.searchQuery}
@@ -2418,6 +2427,7 @@ function EmbeddableChatInner({
2418
2427
  onRequestRename={mingoCaps.canRename ? setRenameTarget : undefined}
2419
2428
  onRequestArchive={mingoCaps.canArchive ? setArchiveTarget : undefined}
2420
2429
  onRequestCopyLink={mingoCaps.onCopyLink}
2430
+ onRequestCompact={mingoCaps.compactDialog}
2421
2431
  scope={dialogScope}
2422
2432
  onScopeChange={setDialogScope}
2423
2433
  searchQuery={mingoCaps.searchQuery}
@@ -3,10 +3,11 @@
3
3
  import { useState, useMemo, useRef, useEffect } from 'react';
4
4
  import { cn } from '../../utils/cn';
5
5
  import { Ellipsis01Icon, SearchXmarkIcon } from '../icons-v2-generated';
6
- import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu';
6
+ import { ActionsMenuDropdown } from '../ui/actions-menu';
7
7
  import { Button } from '../ui/button';
8
8
  import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade';
9
9
  import { SquareAvatar } from '../ui/square-avatar';
10
+ import { chatDialogMenuItems } from './chat-dialog-menu-items';
10
11
  import { ChatListEmptyState } from './chat-list-empty-state';
11
12
  import type { DialogItem } from './types/component.types';
12
13
 
@@ -21,16 +22,18 @@ export interface MingoChatHistoryProps {
21
22
  activeDialogId?: string;
22
23
  /** Open a dialog. */
23
24
  onSelectDialog?: (id: string) => void;
24
- /** Request rename — enables the row "Rename chat" action. The host opens
25
+ /** Request rename — enables the row "Rename Chat" action. The host opens
25
26
  * the Rename modal; the list does no inline editing. */
26
27
  onRequestRename?: (dialog: DialogItem) => void;
27
- /** Request archive — enables the row "Archive chat" action. The host opens
28
+ /** Request archive — enables the row "Archive Chat" action. The host opens
28
29
  * the Archive confirmation modal. */
29
30
  onRequestArchive?: (dialog: DialogItem) => void;
30
- /** Request a shareable link — enables the row "Copy chat link" action. The host
31
+ /** Request a shareable link — enables the row "Copy Chat Link" action. The host
31
32
  * owns the URL shape and the copy itself (the list knows neither the app's
32
33
  * routes nor whether a clipboard is available). */
33
34
  onRequestCopyLink?: (dialog: DialogItem) => void;
35
+ /** Request context compaction — enables the row "Compact Chat Memory" action. */
36
+ onRequestCompact?: (dialog: DialogItem) => void;
34
37
  /** Current server-side search term. Drives the "No chats found" empty state;
35
38
  * the search INPUT lives in the panel header, not in this list. The host
36
39
  * owns the term and refetches server-side — the list does no filtering. */
@@ -93,6 +96,7 @@ interface RowProps {
93
96
  onSelect?: (id: string) => void;
94
97
  onRequestRename?: (dialog: DialogItem) => void;
95
98
  onRequestCopyLink?: (dialog: DialogItem) => void;
99
+ onRequestCompact?: (dialog: DialogItem) => void;
96
100
  onRequestArchive?: (dialog: DialogItem) => void;
97
101
  }
98
102
 
@@ -102,34 +106,24 @@ function MingoChatHistoryRow({
102
106
  onSelect,
103
107
  onRequestCopyLink,
104
108
  onRequestRename,
109
+ onRequestCompact,
105
110
  onRequestArchive,
106
111
  }: RowProps) {
107
112
  const title = dialog.title || 'Untitled Chat';
108
113
  const unread = dialog.unreadMessagesCount ?? 0;
109
- const hasMenu = !!onRequestRename || !!onRequestArchive || !!onRequestCopyLink;
114
+ const hasMenu = !!onRequestRename || !!onRequestArchive || !!onRequestCopyLink || !!onRequestCompact;
110
115
  const owner = dialog.owner;
111
116
  const hasAvatar = !!(owner?.name || owner?.avatarUrl);
112
117
  // Keep the `⋯` visible while its menu is open — once Radix moves focus into
113
118
  // the portalled content the row loses hover/focus-within.
114
119
  const [menuOpen, setMenuOpen] = useState(false);
115
120
 
116
- const menuItems = [
117
- onRequestCopyLink && {
118
- id: 'copy-link',
119
- label: 'Copy chat link',
120
- onClick: () => onRequestCopyLink(dialog),
121
- },
122
- onRequestRename && {
123
- id: 'rename',
124
- label: 'Rename chat',
125
- onClick: () => onRequestRename(dialog),
126
- },
127
- onRequestArchive && {
128
- id: 'archive',
129
- label: 'Archive chat',
130
- onClick: () => onRequestArchive(dialog),
131
- },
132
- ].filter(Boolean) as ActionsMenuItem[];
121
+ const menuItems = chatDialogMenuItems({
122
+ onCopyLink: onRequestCopyLink && (() => onRequestCopyLink(dialog)),
123
+ onRename: onRequestRename && (() => onRequestRename(dialog)),
124
+ onCompact: onRequestCompact && (() => onRequestCompact(dialog)),
125
+ onArchive: onRequestArchive && (() => onRequestArchive(dialog)),
126
+ });
133
127
 
134
128
  // The item is the full-width row (inside the group's bordered box). Padding
135
129
  // lives on the inner content button (badge + title) only — the `⋯` is a
@@ -317,6 +311,7 @@ export function MingoChatHistory({
317
311
  onSelectDialog,
318
312
  onRequestCopyLink,
319
313
  onRequestRename,
314
+ onRequestCompact,
320
315
  onRequestArchive,
321
316
  searchQuery,
322
317
  hasMore = false,
@@ -400,6 +395,7 @@ export function MingoChatHistory({
400
395
  onSelect={onSelectDialog}
401
396
  onRequestCopyLink={onRequestCopyLink}
402
397
  onRequestRename={onRequestRename}
398
+ onRequestCompact={onRequestCompact}
403
399
  onRequestArchive={onRequestArchive}
404
400
  />
405
401
  ))}
@@ -29,12 +29,14 @@ export interface MingoHistoryRailProps {
29
29
  * the welcome. Rendered as the pinned "Start New Chat" button above the list,
30
30
  * in every state (including the no-chats empty state). */
31
31
  onNewChat?: () => void;
32
- /** Request rename — enables the row "Rename chat" action. */
32
+ /** Request rename — enables the row "Rename Chat" action. */
33
33
  onRequestRename?: (dialog: DialogItem) => void;
34
- /** Request archive — enables the row "Archive chat" action. */
34
+ /** Request archive — enables the row "Archive Chat" action. */
35
35
  onRequestArchive?: (dialog: DialogItem) => void;
36
- /** Request a shareable link — enables the row "Copy chat link" action. */
36
+ /** Request a shareable link — enables the row "Copy Chat Link" action. */
37
37
  onRequestCopyLink?: (dialog: DialogItem) => void;
38
+ /** Request context compaction — enables the row "Compact Chat Memory" action. */
39
+ onRequestCompact?: (dialog: DialogItem) => void;
38
40
  /** Ownership scope shown as a two-state "My Chats / All Chats" selector
39
41
  * between "Start New Chat" and the list. Rendered only when BOTH `scope`
40
42
  * and `onScopeChange` are provided; the host owns the state and refilters
@@ -88,6 +90,7 @@ export function MingoHistoryRail({
88
90
  onRequestRename,
89
91
  onRequestArchive,
90
92
  onRequestCopyLink,
93
+ onRequestCompact,
91
94
  scope,
92
95
  onScopeChange,
93
96
  searchQuery,
@@ -169,6 +172,7 @@ export function MingoHistoryRail({
169
172
  onRequestRename={onRequestRename}
170
173
  onRequestArchive={onRequestArchive}
171
174
  onRequestCopyLink={onRequestCopyLink}
175
+ onRequestCompact={onRequestCompact}
172
176
  searchQuery={searchQuery}
173
177
  hasMore={hasMore}
174
178
  isLoadingMore={isLoadingMore}
@@ -49,9 +49,9 @@ export interface FetchDialogsResult {
49
49
  * or adapter-owned (`UnifiedChatState.dialogCapabilities`).
50
50
  */
51
51
  export interface ChatDialogCapabilities {
52
- /** Show "Rename chat" in the row ⋯ menu and the conversation header. */
52
+ /** Show "Rename Chat" in the row ⋯ menu and the conversation header. */
53
53
  canRename?: boolean;
54
- /** Show "Archive chat" in the row ⋯ menu and the conversation header. */
54
+ /** Show "Archive Chat" in the row ⋯ menu and the conversation header. */
55
55
  canArchive?: boolean;
56
56
  /** Pages archived dialogs — presence gates the archive page + header button. */
57
57
  fetchArchivedDialogs?: (params: FetchDialogsParams) => Promise<FetchDialogsResult>;
@@ -61,8 +61,14 @@ export interface ChatDialogCapabilities {
61
61
  searchQuery?: string;
62
62
  /** Presence wires the header magnifier + the rail's search field. */
63
63
  onSearchChange?: (query: string) => void;
64
- /** "Copy chat link" — the owner of the URL shape + clipboard write. */
64
+ /** "Copy Chat Link" — the owner of the URL shape + clipboard write. */
65
65
  onCopyLink?: (dialog: DialogItem) => void;
66
+ /**
67
+ * "Compact Chat Memory" — summarizes the dialog's AI context on demand. The
68
+ * host owns the request and its feedback; the compaction itself streams into
69
+ * the thread as the usual context-compaction message.
70
+ */
71
+ compactDialog?: (dialog: DialogItem) => void;
66
72
  /**
67
73
  * When the list has settled EMPTY and is unsearched, land on the composer
68
74
  * instead of an empty "Current Chats" screen.
@@ -1,9 +1,15 @@
1
+ import { useId } from 'react';
2
+
1
3
  interface EmptyVendorIconProps {
2
4
  className?: string;
3
5
  }
4
6
 
5
7
  /** @deprecated Use icons from icons-v2-generated instead. */
6
8
  export function EmptyVendorIcon({ className }: EmptyVendorIconProps) {
9
+ const uniqueId = useId();
10
+ const patternId = `pattern0_1643_33816_${uniqueId}`;
11
+ const imageId = `image0_1643_33816_${uniqueId}`;
12
+
7
13
  return (
8
14
  <svg
9
15
  width="24"
@@ -16,14 +22,14 @@ export function EmptyVendorIcon({ className }: EmptyVendorIconProps) {
16
22
  >
17
23
  <path
18
24
  d="M0 4C0 1.79086 1.79086 0 4 0H20C22.2091 0 24 1.79086 24 4V20C24 22.2091 22.2091 24 20 24H4C1.79086 24 0 22.2091 0 20V4Z"
19
- fill="url(#pattern0_1643_33816)"
25
+ fill={`url(#${patternId})`}
20
26
  />
21
27
  <defs>
22
- <pattern id="pattern0_1643_33816" patternContentUnits="objectBoundingBox" width="1" height="1">
23
- <use xlinkHref="#image0_1643_33816" transform="scale(0.0104167)" />
28
+ <pattern id={patternId} patternContentUnits="objectBoundingBox" width="1" height="1">
29
+ <use xlinkHref={`#${imageId}`} transform="scale(0.0104167)" />
24
30
  </pattern>
25
31
  <image
26
- id="image0_1643_33816"
32
+ id={imageId}
27
33
  width="96"
28
34
  height="96"
29
35
  preserveAspectRatio="none"