@flamingo-stack/openframe-frontend-core 0.0.462 → 0.0.463

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 (102) hide show
  1. package/dist/{chunk-WSPRJARS.js → chunk-2ZDVWIAX.js} +3715 -3593
  2. package/dist/chunk-2ZDVWIAX.js.map +1 -0
  3. package/dist/{chunk-4KKY7ZUY.js → chunk-4RQZ5X6Y.js} +4 -4
  4. package/dist/{chunk-RMYT3QCX.cjs → chunk-52NND23E.cjs} +62 -62
  5. package/dist/{chunk-RMYT3QCX.cjs.map → chunk-52NND23E.cjs.map} +1 -1
  6. package/dist/{chunk-XM3WRNYR.cjs → chunk-5ME74JNR.cjs} +59 -59
  7. package/dist/{chunk-XM3WRNYR.cjs.map → chunk-5ME74JNR.cjs.map} +1 -1
  8. package/dist/{chunk-FRXDTBA2.js → chunk-CIWVOQKW.js} +2 -2
  9. package/dist/{chunk-JOW3AEZZ.cjs → chunk-E6OKPI77.cjs} +15 -15
  10. package/dist/{chunk-JOW3AEZZ.cjs.map → chunk-E6OKPI77.cjs.map} +1 -1
  11. package/dist/{chunk-RQJGUEQF.cjs → chunk-G2GSHNSJ.cjs} +5 -5
  12. package/dist/{chunk-RQJGUEQF.cjs.map → chunk-G2GSHNSJ.cjs.map} +1 -1
  13. package/dist/{chunk-66XFCYRE.js → chunk-GHSSMGZL.js} +2 -2
  14. package/dist/{chunk-WUPS5YYG.js → chunk-IDMH7CZO.js} +2 -2
  15. package/dist/{chunk-JCCVQ3PN.cjs → chunk-IRVMOG4M.cjs} +935 -813
  16. package/dist/chunk-IRVMOG4M.cjs.map +1 -0
  17. package/dist/{chunk-HJWJLRSU.cjs → chunk-KFND7IRU.cjs} +26 -26
  18. package/dist/{chunk-HJWJLRSU.cjs.map → chunk-KFND7IRU.cjs.map} +1 -1
  19. package/dist/{chunk-DNUF7SJD.cjs → chunk-ME6PX3N3.cjs} +31 -31
  20. package/dist/{chunk-DNUF7SJD.cjs.map → chunk-ME6PX3N3.cjs.map} +1 -1
  21. package/dist/{chunk-ZV3T47FO.js → chunk-NXCDJV2N.js} +2 -2
  22. package/dist/{chunk-GGPVPGIB.js → chunk-OURYLIMA.js} +4 -4
  23. package/dist/{chunk-BN5T3OP6.cjs → chunk-Q5QILCAZ.cjs} +9 -9
  24. package/dist/{chunk-BN5T3OP6.cjs.map → chunk-Q5QILCAZ.cjs.map} +1 -1
  25. package/dist/{chunk-N27ZOEDU.js → chunk-QMJALAKE.js} +2 -2
  26. package/dist/{chunk-OYYGSA3W.js → chunk-RQG32QZS.js} +2 -2
  27. package/dist/{chunk-WL4QJ7BD.cjs → chunk-VHR5BOCQ.cjs} +26 -26
  28. package/dist/{chunk-WL4QJ7BD.cjs.map → chunk-VHR5BOCQ.cjs.map} +1 -1
  29. package/dist/{chunk-4KD6NQDK.cjs → chunk-VUDXSYY7.cjs} +37 -37
  30. package/dist/{chunk-4KD6NQDK.cjs.map → chunk-VUDXSYY7.cjs.map} +1 -1
  31. package/dist/{chunk-LE7N2KEE.cjs → chunk-WDN6RDXY.cjs} +7 -7
  32. package/dist/{chunk-LE7N2KEE.cjs.map → chunk-WDN6RDXY.cjs.map} +1 -1
  33. package/dist/{chunk-MBHZJTZC.js → chunk-XEQCEYTI.js} +4 -4
  34. package/dist/{chunk-CARCB5BE.cjs → chunk-YMRAEKRR.cjs} +11 -11
  35. package/dist/{chunk-CARCB5BE.cjs.map → chunk-YMRAEKRR.cjs.map} +1 -1
  36. package/dist/{chunk-QO2YQ7V4.js → chunk-YQI5NNA2.js} +4 -4
  37. package/dist/{chunk-ZST5WNYI.js → chunk-YROSJGPY.js} +2 -2
  38. package/dist/components/case-studies/index.cjs +8 -8
  39. package/dist/components/case-studies/index.js +2 -2
  40. package/dist/components/chat/chat-panel-header.d.ts +9 -1
  41. package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
  42. package/dist/components/chat/embeddable-chat.d.ts +8 -0
  43. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  44. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  45. package/dist/components/chat/index.cjs +2 -2
  46. package/dist/components/chat/index.js +1 -1
  47. package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
  48. package/dist/components/chat/mingo-history-rail.d.ts +11 -1
  49. package/dist/components/chat/mingo-history-rail.d.ts.map +1 -1
  50. package/dist/components/chat/types/component.types.d.ts +8 -0
  51. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  52. package/dist/components/chat/types/unified-chat-state.types.d.ts +7 -0
  53. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  54. package/dist/components/contact/index.cjs +3 -3
  55. package/dist/components/contact/index.js +2 -2
  56. package/dist/components/docs/index.cjs +5 -5
  57. package/dist/components/docs/index.js +4 -4
  58. package/dist/components/embeds/index.cjs +3 -3
  59. package/dist/components/embeds/index.js +2 -2
  60. package/dist/components/faq/index.cjs +3 -3
  61. package/dist/components/faq/index.js +2 -2
  62. package/dist/components/features/index.cjs +2 -2
  63. package/dist/components/features/index.js +1 -1
  64. package/dist/components/help-center-pages/index.cjs +20 -20
  65. package/dist/components/help-center-pages/index.js +11 -11
  66. package/dist/components/index.cjs +129 -129
  67. package/dist/components/index.js +9 -9
  68. package/dist/components/navigation/index.cjs +2 -2
  69. package/dist/components/navigation/index.js +1 -1
  70. package/dist/components/onboarding-guides/index.cjs +5 -5
  71. package/dist/components/onboarding-guides/index.js +4 -4
  72. package/dist/components/related-content/index.cjs +3 -3
  73. package/dist/components/related-content/index.js +2 -2
  74. package/dist/components/tickets/index.cjs +5 -5
  75. package/dist/components/tickets/index.js +4 -4
  76. package/dist/components/ui/index.cjs +2 -2
  77. package/dist/components/ui/index.js +1 -1
  78. package/dist/components/ui/tab-selector.d.ts.map +1 -1
  79. package/dist/index.cjs +2 -2
  80. package/dist/index.js +1 -1
  81. package/package.json +1 -1
  82. package/src/components/chat/chat-panel-header.tsx +21 -1
  83. package/src/components/chat/embeddable-chat.tsx +51 -4
  84. package/src/components/chat/hooks/use-unified-chat.ts +9 -0
  85. package/src/components/chat/mingo-chat-history.tsx +85 -37
  86. package/src/components/chat/mingo-history-rail.tsx +31 -0
  87. package/src/components/chat/types/component.types.ts +8 -0
  88. package/src/components/chat/types/unified-chat-state.types.ts +9 -0
  89. package/src/components/ui/tab-selector.tsx +7 -4
  90. package/dist/chunk-JCCVQ3PN.cjs.map +0 -1
  91. package/dist/chunk-WSPRJARS.js.map +0 -1
  92. /package/dist/{chunk-4KKY7ZUY.js.map → chunk-4RQZ5X6Y.js.map} +0 -0
  93. /package/dist/{chunk-FRXDTBA2.js.map → chunk-CIWVOQKW.js.map} +0 -0
  94. /package/dist/{chunk-66XFCYRE.js.map → chunk-GHSSMGZL.js.map} +0 -0
  95. /package/dist/{chunk-WUPS5YYG.js.map → chunk-IDMH7CZO.js.map} +0 -0
  96. /package/dist/{chunk-ZV3T47FO.js.map → chunk-NXCDJV2N.js.map} +0 -0
  97. /package/dist/{chunk-GGPVPGIB.js.map → chunk-OURYLIMA.js.map} +0 -0
  98. /package/dist/{chunk-N27ZOEDU.js.map → chunk-QMJALAKE.js.map} +0 -0
  99. /package/dist/{chunk-OYYGSA3W.js.map → chunk-RQG32QZS.js.map} +0 -0
  100. /package/dist/{chunk-MBHZJTZC.js.map → chunk-XEQCEYTI.js.map} +0 -0
  101. /package/dist/{chunk-QO2YQ7V4.js.map → chunk-YQI5NNA2.js.map} +0 -0
  102. /package/dist/{chunk-ZST5WNYI.js.map → chunk-YROSJGPY.js.map} +0 -0
@@ -5,6 +5,7 @@ import { cn } from '../../utils/cn'
5
5
  import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu'
6
6
  import { Button } from '../ui/button'
7
7
  import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
8
+ import { SquareAvatar } from '../ui/square-avatar'
8
9
  import { Ellipsis01Icon, SearchXmarkIcon } from '../icons-v2-generated'
9
10
  import { ChatListEmptyState } from './chat-list-empty-state'
10
11
  import type { DialogItem } from './types/component.types'
@@ -107,6 +108,8 @@ function MingoChatHistoryRow({
107
108
  const title = dialog.title || 'Untitled Chat'
108
109
  const unread = dialog.unreadMessagesCount ?? 0
109
110
  const hasMenu = !!onRequestRename || !!onRequestArchive
111
+ const owner = dialog.owner
112
+ const hasAvatar = !!(owner?.name || owner?.avatarUrl)
110
113
  // Keep the `⋯` visible while its menu is open — once Radix moves focus into
111
114
  // the portalled content the row loses hover/focus-within.
112
115
  const [menuOpen, setMenuOpen] = React.useState(false)
@@ -173,46 +176,82 @@ function MingoChatHistoryRow({
173
176
  {title}
174
177
  </span>
175
178
  </div>
176
- {hasMenu ? (
177
- // Sibling of the padded content (not inside it): sits at the item's
178
- // right edge, free of the content's left padding. Revealed on row
179
- // hover/focus; kept visible while its menu is open. Hidden on mobile
180
- // it's a hover affordance (no hover on touch).
179
+ {hasMenu || hasAvatar ? (
180
+ // Trailing 48px cell (Figma 113:63224): the owner avatar sits centred
181
+ // by default; hovering the row (or opening the menu) swaps it for the
182
+ // `⋯` actions button IN PLACE, so the title never reflows. Without an
183
+ // avatar the cell exists only for the hover menu, so it collapses on
184
+ // mobile exactly like the old hover-only gutter (no hover on touch).
181
185
  <span
182
- onClick={(e) => e.stopPropagation()}
183
186
  className={cn(
184
- // `self-stretch` so the wrapper fills the row height — gives the
185
- // button's `h-full` a definite parent to resolve against (under the
186
- // row's `items-center` the wrapper would otherwise collapse to its
187
- // content and `h-full` on the button would no-op).
188
- 'self-stretch shrink-0 transition-opacity max-md:hidden',
189
- menuOpen
190
- ? 'opacity-100'
191
- : 'opacity-0 group-hover/row:opacity-100 focus-within:opacity-100',
187
+ 'relative w-12 self-stretch shrink-0',
188
+ !hasAvatar && 'max-md:hidden',
192
189
  )}
193
190
  >
194
- <ActionsMenuDropdown
195
- triggerAriaLabel="Chat actions"
196
- groups={[{ items: menuItems }]}
197
- open={menuOpen}
198
- onOpenChange={setMenuOpen}
199
- // Don't return focus (and its ring) to the `⋯` trigger on close.
200
- onCloseAutoFocus={(e) => e.preventDefault()}
201
- customTrigger={
202
- <Button
203
- variant="transparent"
204
- size="icon"
205
- aria-label="Chat actions"
206
- // Square, grey icon → white on hover. `h-full` matches the row
207
- // height exactly (no breakpoint poke from `size="icon"`'s
208
- // responsive `md:h-12`); `w-9` keeps it compact; `p-0` drops the
209
- // icon-size padding. tailwind-merge so these win over defaults.
210
- className="h-full md:h-full rounded-none p-0 text-ods-text-secondary hover:text-ods-text-primary"
211
- >
212
- <Ellipsis01Icon />
213
- </Button>
214
- }
215
- />
191
+ {hasMenu ? (
192
+ // Revealed on row hover/its own focus; kept visible while the menu
193
+ // is open. Hidden on mobile — it's a hover affordance. `peer/menu`
194
+ // so the avatar layer (a later sibling) can fade when the trigger
195
+ // holds keyboard focus.
196
+ <span
197
+ onClick={(e) => e.stopPropagation()}
198
+ className={cn(
199
+ 'peer/menu absolute inset-0 transition-opacity max-md:hidden',
200
+ menuOpen
201
+ ? 'opacity-100'
202
+ : 'opacity-0 group-hover/row:opacity-100 focus-within:opacity-100',
203
+ )}
204
+ >
205
+ <ActionsMenuDropdown
206
+ triggerAriaLabel="Chat actions"
207
+ groups={[{ items: menuItems }]}
208
+ open={menuOpen}
209
+ onOpenChange={setMenuOpen}
210
+ // Don't return focus (and its ring) to the `⋯` trigger on close.
211
+ onCloseAutoFocus={(e) => e.preventDefault()}
212
+ customTrigger={
213
+ <Button
214
+ variant="transparent"
215
+ size="icon"
216
+ aria-label="Chat actions"
217
+ // Square, grey icon → white on hover. `h-full`/`w-full` fill
218
+ // the 48px cell exactly (no breakpoint poke from
219
+ // `size="icon"`'s responsive `md:h-12`); `p-0` drops the
220
+ // icon-size padding. tailwind-merge so these win over
221
+ // defaults.
222
+ className="h-full md:h-full w-full rounded-none p-0 text-ods-text-secondary hover:text-ods-text-primary"
223
+ >
224
+ <Ellipsis01Icon />
225
+ </Button>
226
+ }
227
+ />
228
+ </span>
229
+ ) : null}
230
+ {hasAvatar ? (
231
+ // Avatar layer — clicks fall through to row selection (it covers
232
+ // the cell whenever the menu trigger is faded out). On desktop it
233
+ // fades for the menu swap; on mobile it's always shown.
234
+ <span
235
+ onClick={() => onSelect?.(dialog.id)}
236
+ title={owner?.name || undefined}
237
+ className={cn(
238
+ 'absolute inset-0 flex cursor-pointer items-center justify-center transition-opacity',
239
+ hasMenu &&
240
+ (menuOpen
241
+ ? 'opacity-0 pointer-events-none'
242
+ : 'md:group-hover/row:opacity-0 md:group-hover/row:pointer-events-none md:peer-[:focus-within]/menu:opacity-0 md:peer-[:focus-within]/menu:pointer-events-none'),
243
+ )}
244
+ >
245
+ <SquareAvatar
246
+ variant="round"
247
+ sizePx={24}
248
+ src={owner?.avatarUrl || undefined}
249
+ alt={owner?.name || undefined}
250
+ fallback={owner?.name || undefined}
251
+ initialsClassName="text-[10px] text-ods-text-secondary"
252
+ />
253
+ </span>
254
+ ) : null}
216
255
  </span>
217
256
  ) : null}
218
257
  </div>
@@ -316,6 +355,7 @@ export function MingoChatHistory({
316
355
  onLoadMoreRef.current = onLoadMore
317
356
  const isLoadingMoreRef = React.useRef(isLoadingMore)
318
357
  isLoadingMoreRef.current = isLoadingMore
358
+ const dialogCount = dialogs.length
319
359
  React.useEffect(() => {
320
360
  const root = scrollRef.current
321
361
  const sentinel = sentinelRef.current
@@ -331,7 +371,15 @@ export function MingoChatHistory({
331
371
  )
332
372
  io.observe(sentinel)
333
373
  return () => io.disconnect()
334
- }, [hasMore])
374
+ // `dialogCount`/`isLoadingMore` deps re-arm the observer after every page
375
+ // lands: `observe()` re-reports the CURRENT intersection, so a sentinel
376
+ // that stays in view (short host-filtered list — e.g. the "My Chats"
377
+ // scope keeps a handful of rows out of a 20-row page) keeps chaining
378
+ // loads until the viewport fills or `hasMore` flips. A continuously
379
+ // visible sentinel never re-crosses the threshold, so the previous
380
+ // [hasMore]-only observer fired exactly once per mount and the list
381
+ // grew by a single page per drawer open.
382
+ }, [hasMore, isLoadingMore, dialogCount])
335
383
 
336
384
  return (
337
385
  <div className={cn('relative flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)]', className)}>
@@ -3,6 +3,7 @@
3
3
  import * as React from 'react'
4
4
  import { cn } from '../../utils/cn'
5
5
  import { Button } from '../ui/button'
6
+ import { TabSelector } from '../ui/tab-selector'
6
7
  import { ChatsIcon } from '../icons-v2-generated/communication/chats-icon'
7
8
  import { PlusCircleIcon } from '../icons-v2-generated/signs-and-symbols/plus-circle-icon'
8
9
  import { AlertCircleIcon, Refresh01RightIcon } from '../icons-v2-generated'
@@ -14,6 +15,10 @@ import type { DialogItem } from './types/component.types'
14
15
  // Types
15
16
  // =============================================================================
16
17
 
18
+ /** Ownership scope of the rail's dialog list — the current user's chats only,
19
+ * or every admin's chats in the tenant. */
20
+ export type MingoHistoryScope = 'my' | 'all'
21
+
17
22
  export interface MingoHistoryRailProps {
18
23
  /** Dialogs to list, assumed already sorted newest-first by the host. */
19
24
  dialogs: ReadonlyArray<DialogItem>
@@ -32,6 +37,13 @@ export interface MingoHistoryRailProps {
32
37
  onRequestRename?: (dialog: DialogItem) => void
33
38
  /** Request archive — enables the row "Archive chat" action. */
34
39
  onRequestArchive?: (dialog: DialogItem) => void
40
+ /** Ownership scope shown as a two-state "My Chats / All Chats" selector
41
+ * between "Start New Chat" and the list. Rendered only when BOTH `scope`
42
+ * and `onScopeChange` are provided; the host owns the state and refilters
43
+ * the `dialogs` it passes in. */
44
+ scope?: MingoHistoryScope
45
+ /** Scope selector change handler — see `scope`. */
46
+ onScopeChange?: (scope: MingoHistoryScope) => void
35
47
  /** Current server-side search term. Drives the list's "No chats found"
36
48
  * empty state; the search INPUT itself lives in the panel header now, not
37
49
  * in the rail body. */
@@ -77,6 +89,8 @@ export function MingoHistoryRail({
77
89
  newChatAlways = false,
78
90
  onRequestRename,
79
91
  onRequestArchive,
92
+ scope,
93
+ onScopeChange,
80
94
  searchQuery,
81
95
  hasMore = false,
82
96
  isLoadingMore = false,
@@ -114,6 +128,23 @@ export function MingoHistoryRail({
114
128
  </Button>
115
129
  )}
116
130
 
131
+ {/* Two-state ownership scope — "My Chats" / "All Chats". Stays visible in
132
+ the empty state too: an empty "My Chats" list must still offer the
133
+ switch to "All Chats" (host filters, so the empty state can be
134
+ scope-induced). Hidden while the load failed — retry is the only
135
+ useful action there. */}
136
+ {scope && onScopeChange && !loadError ? (
137
+ <TabSelector
138
+ className="shrink-0"
139
+ value={scope}
140
+ onValueChange={(value) => onScopeChange(value as MingoHistoryScope)}
141
+ items={[
142
+ { id: 'my', label: 'My Chats' },
143
+ { id: 'all', label: 'All Chats' },
144
+ ]}
145
+ />
146
+ ) : null}
147
+
117
148
  {loadError ? (
118
149
  <div className="flex flex-1 min-h-0 flex-col items-center justify-center gap-[var(--spacing-system-m)] text-center">
119
150
  <AlertCircleIcon className="h-8 w-8 text-ods-text-secondary shrink-0" />
@@ -608,6 +608,14 @@ export interface DialogItem {
608
608
  timestamp?: Date | string
609
609
  isActive?: boolean
610
610
  unreadMessagesCount?: number
611
+ /** Dialog owner (creator) — drives the trailing avatar in the chat-history
612
+ * rows (Figma 113:63224). Omit to render the row without an avatar. */
613
+ owner?: {
614
+ /** Full display name — initials fallback + `title` tooltip. */
615
+ name?: string | null
616
+ /** Absolute avatar URL; falls back to initials when absent or failing. */
617
+ avatarUrl?: string | null
618
+ }
611
619
  }
612
620
 
613
621
  // ========== Chat Sidebar Props ==========
@@ -369,6 +369,15 @@ export interface UnifiedChatState {
369
369
  /** Fetch the next page of dialogs. No-op when `hasMoreDialogs` is false. */
370
370
  loadMoreDialogs: () => Promise<void>
371
371
 
372
+ /** Ownership scope of `dialogs` — `'my'` (current user's chats) or `'all'`
373
+ * (every admin's). Optional: adapters without ownership data omit it and
374
+ * the "My Chats / All Chats" selector stays hidden. The HOST owns the
375
+ * filtering — `dialogs` must already reflect this scope. */
376
+ dialogScope?: 'my' | 'all'
377
+
378
+ /** Switch the ownership scope — see `dialogScope`. */
379
+ setDialogScope?: (scope: 'my' | 'all') => void
380
+
372
381
  /** Whether more historical messages remain in the active dialog. */
373
382
  hasMoreMessages: boolean
374
383
 
@@ -67,15 +67,18 @@ export function TabSelector({
67
67
  onClick={() => onValueChange(item.id)}
68
68
  aria-label={!item.label ? item.ariaLabel : undefined}
69
69
  className={cn(
70
- 'flex flex-1 items-center justify-center gap-2 rounded-xs p-2 text-h4 transition-colors duration-200 whitespace-nowrap',
70
+ // Bold on BOTH states a medium↔bold flip between the active
71
+ // and inactive tabs read as inconsistent weight (and nudged
72
+ // label widths on every switch).
73
+ 'flex flex-1 items-center justify-center gap-2 rounded-xs p-2 text-h4 font-bold transition-colors duration-200 whitespace-nowrap',
71
74
  isDisabled
72
75
  ? isActive
73
- ? 'cursor-not-allowed bg-ods-bg-surface text-ods-text-secondary [font-weight:700]'
76
+ ? 'cursor-not-allowed bg-ods-bg-surface text-ods-text-secondary'
74
77
  : 'cursor-not-allowed bg-transparent text-ods-text-secondary'
75
78
  : isActive
76
79
  ? variant === 'primary'
77
- ? 'bg-ods-accent text-ods-text-on-accent cursor-default [font-weight:700]'
78
- : 'bg-ods-bg-surface text-ods-text-primary cursor-default [font-weight:700]'
80
+ ? 'bg-ods-accent text-ods-text-on-accent cursor-default'
81
+ : 'bg-ods-bg-surface text-ods-text-primary cursor-default'
79
82
  : 'bg-transparent text-ods-text-primary hover:bg-ods-bg-hover cursor-pointer',
80
83
  )}
81
84
  >