@flamingo-stack/openframe-frontend-core 0.0.321 → 0.0.322

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 (90) hide show
  1. package/dist/{chunk-PDOTUMJC.cjs → chunk-27UIZKPP.cjs} +31 -31
  2. package/dist/{chunk-PDOTUMJC.cjs.map → chunk-27UIZKPP.cjs.map} +1 -1
  3. package/dist/{chunk-TFCGMQPK.cjs → chunk-2JFIPRZM.cjs} +25 -25
  4. package/dist/{chunk-TFCGMQPK.cjs.map → chunk-2JFIPRZM.cjs.map} +1 -1
  5. package/dist/{chunk-DEEBJBFQ.js → chunk-2L6XJRWU.js} +2 -2
  6. package/dist/{chunk-WCRJWZUA.js → chunk-2ZMEIVHB.js} +4 -4
  7. package/dist/{chunk-TV6VD6LP.js → chunk-3CUHOXPN.js} +4 -4
  8. package/dist/{chunk-J65CQLX6.js → chunk-3NOGHXE5.js} +2 -2
  9. package/dist/{chunk-CD56IAXU.cjs → chunk-6GHEXJ6H.cjs} +5 -5
  10. package/dist/{chunk-CD56IAXU.cjs.map → chunk-6GHEXJ6H.cjs.map} +1 -1
  11. package/dist/{chunk-WCCZGPPP.cjs → chunk-AOD7KNNL.cjs} +62 -62
  12. package/dist/{chunk-WCCZGPPP.cjs.map → chunk-AOD7KNNL.cjs.map} +1 -1
  13. package/dist/{chunk-MN2WQYXS.cjs → chunk-APGXPHP2.cjs} +15 -15
  14. package/dist/{chunk-MN2WQYXS.cjs.map → chunk-APGXPHP2.cjs.map} +1 -1
  15. package/dist/{chunk-FNK67HTC.js → chunk-HYW3BYDX.js} +2 -2
  16. package/dist/{chunk-6DH2TCRH.js → chunk-KFEIMA2T.js} +2 -2
  17. package/dist/{chunk-563D7EB2.js → chunk-KJNTCTV4.js} +4 -4
  18. package/dist/{chunk-NEY347ME.cjs → chunk-M5RW5ASP.cjs} +26 -26
  19. package/dist/{chunk-NEY347ME.cjs.map → chunk-M5RW5ASP.cjs.map} +1 -1
  20. package/dist/{chunk-Y2TJ64S5.cjs → chunk-MQFGEYOG.cjs} +12 -12
  21. package/dist/{chunk-Y2TJ64S5.cjs.map → chunk-MQFGEYOG.cjs.map} +1 -1
  22. package/dist/{chunk-TPJJJ6KC.js → chunk-MQHD6CTN.js} +145 -90
  23. package/dist/chunk-MQHD6CTN.js.map +1 -0
  24. package/dist/{chunk-7M3QIYFW.cjs → chunk-NCFCQWMH.cjs} +56 -56
  25. package/dist/{chunk-7M3QIYFW.cjs.map → chunk-NCFCQWMH.cjs.map} +1 -1
  26. package/dist/{chunk-GAQAPTBU.cjs → chunk-NTEAD4ZM.cjs} +37 -37
  27. package/dist/{chunk-GAQAPTBU.cjs.map → chunk-NTEAD4ZM.cjs.map} +1 -1
  28. package/dist/{chunk-VNWWOPIY.js → chunk-PZYSR23M.js} +2 -2
  29. package/dist/{chunk-HALW7FPF.js → chunk-S3X2KEFA.js} +2 -2
  30. package/dist/{chunk-ZFER75HU.cjs → chunk-S6W5YPAB.cjs} +9 -9
  31. package/dist/{chunk-ZFER75HU.cjs.map → chunk-S6W5YPAB.cjs.map} +1 -1
  32. package/dist/{chunk-7C5G45KU.cjs → chunk-UFUOM6ZT.cjs} +732 -677
  33. package/dist/chunk-UFUOM6ZT.cjs.map +1 -0
  34. package/dist/{chunk-V4U7ASRM.js → chunk-WQ4OIM4E.js} +2 -2
  35. package/dist/{chunk-BRERE4E5.js → chunk-XF3GIE5U.js} +4 -4
  36. package/dist/{chunk-5HQIFRRE.cjs → chunk-YKIU2GXU.cjs} +7 -7
  37. package/dist/{chunk-5HQIFRRE.cjs.map → chunk-YKIU2GXU.cjs.map} +1 -1
  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/embeddable-chat.d.ts +2 -0
  41. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  42. package/dist/components/chat/index.cjs +2 -2
  43. package/dist/components/chat/index.js +1 -1
  44. package/dist/components/chat/mingo-chat-history.d.ts +11 -2
  45. package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
  46. package/dist/components/chat/mingo-welcome.d.ts +5 -1
  47. package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
  48. package/dist/components/contact/index.cjs +3 -3
  49. package/dist/components/contact/index.js +2 -2
  50. package/dist/components/docs/index.cjs +5 -5
  51. package/dist/components/docs/index.js +4 -4
  52. package/dist/components/embeds/index.cjs +3 -3
  53. package/dist/components/embeds/index.js +2 -2
  54. package/dist/components/faq/index.cjs +3 -3
  55. package/dist/components/faq/index.js +2 -2
  56. package/dist/components/features/index.cjs +2 -2
  57. package/dist/components/features/index.js +1 -1
  58. package/dist/components/help-center-pages/index.cjs +20 -20
  59. package/dist/components/help-center-pages/index.js +11 -11
  60. package/dist/components/index.cjs +120 -120
  61. package/dist/components/index.js +9 -9
  62. package/dist/components/navigation/index.cjs +2 -2
  63. package/dist/components/navigation/index.js +1 -1
  64. package/dist/components/onboarding-guides/index.cjs +5 -5
  65. package/dist/components/onboarding-guides/index.js +4 -4
  66. package/dist/components/related-content/index.cjs +3 -3
  67. package/dist/components/related-content/index.js +2 -2
  68. package/dist/components/tickets/index.cjs +5 -5
  69. package/dist/components/tickets/index.js +4 -4
  70. package/dist/components/ui/index.cjs +2 -2
  71. package/dist/components/ui/index.js +1 -1
  72. package/dist/index.cjs +2 -2
  73. package/dist/index.js +1 -1
  74. package/package.json +1 -1
  75. package/src/components/chat/embeddable-chat.tsx +15 -1
  76. package/src/components/chat/mingo-chat-history.tsx +136 -53
  77. package/src/components/chat/mingo-welcome.tsx +6 -1
  78. package/dist/chunk-7C5G45KU.cjs.map +0 -1
  79. package/dist/chunk-TPJJJ6KC.js.map +0 -1
  80. /package/dist/{chunk-DEEBJBFQ.js.map → chunk-2L6XJRWU.js.map} +0 -0
  81. /package/dist/{chunk-WCRJWZUA.js.map → chunk-2ZMEIVHB.js.map} +0 -0
  82. /package/dist/{chunk-TV6VD6LP.js.map → chunk-3CUHOXPN.js.map} +0 -0
  83. /package/dist/{chunk-J65CQLX6.js.map → chunk-3NOGHXE5.js.map} +0 -0
  84. /package/dist/{chunk-FNK67HTC.js.map → chunk-HYW3BYDX.js.map} +0 -0
  85. /package/dist/{chunk-6DH2TCRH.js.map → chunk-KFEIMA2T.js.map} +0 -0
  86. /package/dist/{chunk-563D7EB2.js.map → chunk-KJNTCTV4.js.map} +0 -0
  87. /package/dist/{chunk-VNWWOPIY.js.map → chunk-PZYSR23M.js.map} +0 -0
  88. /package/dist/{chunk-HALW7FPF.js.map → chunk-S3X2KEFA.js.map} +0 -0
  89. /package/dist/{chunk-V4U7ASRM.js.map → chunk-WQ4OIM4E.js.map} +0 -0
  90. /package/dist/{chunk-BRERE4E5.js.map → chunk-XF3GIE5U.js.map} +0 -0
@@ -2,9 +2,11 @@
2
2
 
3
3
  import * as React from 'react'
4
4
  import { cn } from '../../utils/cn'
5
+ import { useDebounce } from '../../hooks/ui/use-debounce'
5
6
  import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu'
6
7
  import { Button } from '../ui/button'
7
- import { Ellipsis01Icon } from '../icons-v2-generated'
8
+ import { Input } from '../ui/input'
9
+ import { Ellipsis01Icon, SearchIcon } from '../icons-v2-generated'
8
10
  import type { DialogItem } from './types/component.types'
9
11
 
10
12
  // =============================================================================
@@ -24,6 +26,12 @@ export interface MingoChatHistoryProps {
24
26
  /** Request archive — enables the row "Archive chat" action. The host opens
25
27
  * the Archive confirmation modal. */
26
28
  onRequestArchive?: (dialog: DialogItem) => void
29
+ /** Seed value for the search input. The host owns the search term and refetches
30
+ * the dialog list server-side; the list itself does no filtering. */
31
+ searchQuery?: string
32
+ /** Emit the (debounced) search term — enables the dialog search bar above the
33
+ * list. Omit to hide the search bar entirely. */
34
+ onSearchChange?: (query: string) => void
27
35
  /** Whether more dialogs remain (cursor pagination). */
28
36
  hasMore?: boolean
29
37
  /** True while the next page is loading. */
@@ -211,7 +219,15 @@ function MingoChatHistoryRow({
211
219
  * two layouts are mutually exclusive — swapping between them mid-load reads as
212
220
  * a flicker). Pure ODS tokens, `animate-pulse`.
213
221
  */
214
- export function MingoChatHistorySkeleton({ className }: { className?: string }) {
222
+ export function MingoChatHistorySkeleton({
223
+ className,
224
+ searchable = false,
225
+ }: {
226
+ className?: string
227
+ /** Render a search-bar placeholder above the groups — match the real list,
228
+ * which shows the search bar only when search is wired. */
229
+ searchable?: boolean
230
+ }) {
215
231
  // Mirror the real grouped list: a group label, then a bordered container of
216
232
  // `h-12` rows. `withBadge` adds the leading unread-badge square on the first
217
233
  // N rows; `widths` varies the title-bar length so the placeholder reads like
@@ -234,6 +250,15 @@ export function MingoChatHistorySkeleton({ className }: { className?: string })
234
250
  className,
235
251
  )}
236
252
  >
253
+ {/* Search-bar placeholder — mirrors the real `Input` (border, rounded,
254
+ h-11/12) with a leading icon + text bar so the loading state matches
255
+ the searchable layout. */}
256
+ {searchable && (
257
+ <div className="flex h-11 md:h-12 shrink-0 items-center gap-2 rounded-[6px] border border-ods-border bg-ods-card px-3">
258
+ <div className="size-4 md:size-6 shrink-0 animate-pulse rounded bg-ods-border" />
259
+ <div className="h-4 w-32 animate-pulse rounded bg-ods-border" />
260
+ </div>
261
+ )}
237
262
  {groups.map((group, g) => (
238
263
  <div key={g} className="flex shrink-0 flex-col gap-[var(--spacing-system-xxs)]">
239
264
  {/* Group label (Today / Yesterday). */}
@@ -261,6 +286,46 @@ export function MingoChatHistorySkeleton({ className }: { className?: string })
261
286
  )
262
287
  }
263
288
 
289
+ // =============================================================================
290
+ // Search bar
291
+ // =============================================================================
292
+
293
+ /**
294
+ * Dialog search field. Holds its own input text for snappy typing and emits
295
+ * the DEBOUNCED term via `onSearchChange` — the host owns the search state
296
+ * and refetches the dialog list server-side, so the list never
297
+ * filters locally. `initialValue` only seeds the field on mount.
298
+ */
299
+ function DialogSearchBar({
300
+ initialValue,
301
+ onSearchChange,
302
+ }: {
303
+ initialValue?: string
304
+ onSearchChange: (query: string) => void
305
+ }) {
306
+ const [value, setValue] = React.useState(initialValue ?? '')
307
+ const debounced = useDebounce(value, 300)
308
+ const lastEmitted = React.useRef(initialValue ?? '')
309
+
310
+ React.useEffect(() => {
311
+ if (debounced === lastEmitted.current) return
312
+ lastEmitted.current = debounced
313
+ onSearchChange(debounced)
314
+ }, [debounced, onSearchChange])
315
+
316
+ return (
317
+ <Input
318
+ type="text"
319
+ value={value}
320
+ onChange={(e) => setValue(e.target.value)}
321
+ placeholder="Search for Chats"
322
+ aria-label="Search chats"
323
+ startAdornment={<SearchIcon />}
324
+ className="shrink-0"
325
+ />
326
+ )
327
+ }
328
+
264
329
  // =============================================================================
265
330
  // Component
266
331
  // =============================================================================
@@ -282,12 +347,15 @@ export function MingoChatHistory({
282
347
  onSelectDialog,
283
348
  onRequestRename,
284
349
  onRequestArchive,
350
+ searchQuery,
351
+ onSearchChange,
285
352
  hasMore = false,
286
353
  isLoadingMore = false,
287
354
  onLoadMore,
288
355
  className,
289
356
  }: MingoChatHistoryProps) {
290
357
  const groups = React.useMemo(() => groupDialogs(dialogs), [dialogs])
358
+ const noSearchResults = groups.length === 0 && !!searchQuery?.trim()
291
359
 
292
360
  // Scroll-fade affordances (same pattern as MingoWelcome's greeting region).
293
361
  const scrollRef = React.useRef<HTMLDivElement>(null)
@@ -333,58 +401,73 @@ export function MingoChatHistory({
333
401
  }, [hasMore])
334
402
 
335
403
  return (
336
- <div className={cn('relative flex flex-1 min-h-0 flex-col', className)}>
337
- <div
338
- ref={scrollRef}
339
- onScroll={updateFade}
340
- className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
341
- >
342
- {groups.map((group) => (
343
- <div
344
- key={group.key}
345
- className="flex flex-col gap-[var(--spacing-system-xxs)]"
346
- >
347
- <p className="text-h5 text-ods-text-secondary">{group.label}</p>
348
- <div className="overflow-hidden rounded-md border border-ods-border">
349
- {group.items.map((dialog) => (
350
- <MingoChatHistoryRow
351
- key={dialog.id}
352
- dialog={dialog}
353
- isActive={dialog.id === activeDialogId}
354
- onSelect={onSelectDialog}
355
- onRequestRename={onRequestRename}
356
- onRequestArchive={onRequestArchive}
357
- />
358
- ))}
359
- </div>
360
- </div>
361
- ))}
362
- {hasMore ? <div ref={sentinelRef} className="h-px shrink-0" /> : null}
363
- </div>
404
+ <div className={cn('relative flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)]', className)}>
405
+ {/* Pinned search bar — stays put while the grouped list scrolls below. */}
406
+ {onSearchChange ? (
407
+ <DialogSearchBar initialValue={searchQuery} onSearchChange={onSearchChange} />
408
+ ) : null}
364
409
 
365
- {/* Scroll-fadeonly while content is hidden in that direction. */}
366
- <div
367
- aria-hidden
368
- className={cn(
369
- 'pointer-events-none absolute inset-x-0 top-0 h-12 transition-opacity duration-150',
370
- fade.top ? 'opacity-100' : 'opacity-0',
371
- )}
372
- style={{
373
- background:
374
- 'linear-gradient(0deg, transparent 0%, var(--color-bg-card) 100%)',
375
- }}
376
- />
377
- <div
378
- aria-hidden
379
- className={cn(
380
- 'pointer-events-none absolute inset-x-0 bottom-0 h-12 transition-opacity duration-150',
381
- fade.bottom ? 'opacity-100' : 'opacity-0',
382
- )}
383
- style={{
384
- background:
385
- 'linear-gradient(180deg, transparent 0%, var(--color-bg-card) 100%)',
386
- }}
387
- />
410
+ {/* Scroll region `relative` so the fades anchor here (below the search
411
+ bar), not over it. */}
412
+ <div className="relative flex flex-1 min-h-0 flex-col">
413
+ <div
414
+ ref={scrollRef}
415
+ onScroll={updateFade}
416
+ className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
417
+ >
418
+ {noSearchResults ? (
419
+ <p className="py-[var(--spacing-system-m)] text-center text-h5 text-ods-text-secondary">
420
+ No chats found
421
+ </p>
422
+ ) : (
423
+ groups.map((group) => (
424
+ <div
425
+ key={group.key}
426
+ className="flex flex-col gap-[var(--spacing-system-xxs)]"
427
+ >
428
+ <p className="text-h5 text-ods-text-secondary">{group.label}</p>
429
+ <div className="overflow-hidden rounded-md border border-ods-border">
430
+ {group.items.map((dialog) => (
431
+ <MingoChatHistoryRow
432
+ key={dialog.id}
433
+ dialog={dialog}
434
+ isActive={dialog.id === activeDialogId}
435
+ onSelect={onSelectDialog}
436
+ onRequestRename={onRequestRename}
437
+ onRequestArchive={onRequestArchive}
438
+ />
439
+ ))}
440
+ </div>
441
+ </div>
442
+ ))
443
+ )}
444
+ {hasMore ? <div ref={sentinelRef} className="h-px shrink-0" /> : null}
445
+ </div>
446
+
447
+ {/* Scroll-fade — only while content is hidden in that direction. */}
448
+ <div
449
+ aria-hidden
450
+ className={cn(
451
+ 'pointer-events-none absolute inset-x-0 top-0 h-12 transition-opacity duration-150',
452
+ fade.top ? 'opacity-100' : 'opacity-0',
453
+ )}
454
+ style={{
455
+ background:
456
+ 'linear-gradient(0deg, transparent 0%, var(--color-bg-card) 100%)',
457
+ }}
458
+ />
459
+ <div
460
+ aria-hidden
461
+ className={cn(
462
+ 'pointer-events-none absolute inset-x-0 bottom-0 h-12 transition-opacity duration-150',
463
+ fade.bottom ? 'opacity-100' : 'opacity-0',
464
+ )}
465
+ style={{
466
+ background:
467
+ 'linear-gradient(180deg, transparent 0%, var(--color-bg-card) 100%)',
468
+ }}
469
+ />
470
+ </div>
388
471
  </div>
389
472
  )
390
473
  }
@@ -92,6 +92,10 @@ export interface MingoWelcomeProps {
92
92
  loadError?: boolean
93
93
  /** Retry handler for the `loadError` state. */
94
94
  onRetry?: () => void
95
+ /** Whether the dialog history has a search bar — when true, the loading
96
+ * skeleton includes a search-bar placeholder so it matches the loaded
97
+ * layout. Ignored once `dialogHistory` is provided. */
98
+ historySearchable?: boolean
95
99
  /** When provided, renders the "Start Guide Chat" chip (the only wired
96
100
  * action — switches the host chat to Guide mode) and enables the default
97
101
  * promo notification. When omitted, both are suppressed. */
@@ -170,6 +174,7 @@ export function MingoWelcome({
170
174
  isLoadingHistory = false,
171
175
  loadError = false,
172
176
  onRetry,
177
+ historySearchable = false,
173
178
  onStartGuideChat,
174
179
  className,
175
180
  }: MingoWelcomeProps) {
@@ -287,7 +292,7 @@ export function MingoWelcome({
287
292
  )}
288
293
  </div>
289
294
  ) : isLoadingHistory ? (
290
- <MingoChatHistorySkeleton />
295
+ <MingoChatHistorySkeleton searchable={historySearchable} />
291
296
  ) : (
292
297
  <>
293
298
  {/* Scrollable region — only the greeting + grid scroll; the notification