@flamingo-stack/openframe-frontend-core 0.0.438-snapshot.20260716203356 → 0.0.438
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.
- package/dist/{chunk-HTVVXVJL.cjs → chunk-2DCP2TIF.cjs} +1490 -1966
- package/dist/chunk-2DCP2TIF.cjs.map +1 -0
- package/dist/{chunk-PBTY7AH5.js → chunk-44HNH57W.js} +4 -4
- package/dist/{chunk-G5FALXNO.js → chunk-5D5SJOLE.js} +4 -4
- package/dist/{chunk-NQQ4NLUT.js → chunk-6ETVJ5ON.js} +6101 -6577
- package/dist/chunk-6ETVJ5ON.js.map +1 -0
- package/dist/{chunk-3WJQL4DN.cjs → chunk-6R5SZPAQ.cjs} +15 -15
- package/dist/{chunk-3WJQL4DN.cjs.map → chunk-6R5SZPAQ.cjs.map} +1 -1
- package/dist/{chunk-P7PISHM5.js → chunk-A25RPWYF.js} +2 -2
- package/dist/{chunk-AUJXLVXA.cjs → chunk-B5SGPIJ4.cjs} +37 -37
- package/dist/{chunk-AUJXLVXA.cjs.map → chunk-B5SGPIJ4.cjs.map} +1 -1
- package/dist/{chunk-VWGF6IFR.cjs → chunk-CMP56RUC.cjs} +62 -62
- package/dist/{chunk-VWGF6IFR.cjs.map → chunk-CMP56RUC.cjs.map} +1 -1
- package/dist/{chunk-F7YVI54P.js → chunk-D5RTZ2WN.js} +4 -4
- package/dist/{chunk-R46QEE3R.cjs → chunk-DTYPYYKD.cjs} +59 -59
- package/dist/{chunk-R46QEE3R.cjs.map → chunk-DTYPYYKD.cjs.map} +1 -1
- package/dist/{chunk-RQQZF6G6.cjs → chunk-H2TQ44VD.cjs} +31 -31
- package/dist/{chunk-RQQZF6G6.cjs.map → chunk-H2TQ44VD.cjs.map} +1 -1
- package/dist/{chunk-7WM77CSW.js → chunk-KBFQF4AV.js} +2 -2
- package/dist/{chunk-2ELBNZJ3.js → chunk-LR6A67KZ.js} +2 -2
- package/dist/{chunk-ZMUFTGL7.cjs → chunk-PCMDRDYX.cjs} +5 -5
- package/dist/{chunk-ZMUFTGL7.cjs.map → chunk-PCMDRDYX.cjs.map} +1 -1
- package/dist/{chunk-WAGTZVQN.js → chunk-POFDZH2C.js} +4 -4
- package/dist/{chunk-EGRZHR7J.js → chunk-RTHOCS3S.js} +2 -2
- package/dist/{chunk-M3LD6DNE.cjs → chunk-SYTL2VIU.cjs} +7 -7
- package/dist/{chunk-M3LD6DNE.cjs.map → chunk-SYTL2VIU.cjs.map} +1 -1
- package/dist/{chunk-WZ6TCAYQ.cjs → chunk-VYG2CJFJ.cjs} +9 -9
- package/dist/{chunk-WZ6TCAYQ.cjs.map → chunk-VYG2CJFJ.cjs.map} +1 -1
- package/dist/{chunk-COMBYYQY.js → chunk-W2PRXXUD.js} +2 -2
- package/dist/{chunk-IAN7GUIV.cjs → chunk-WF7VGEYR.cjs} +26 -26
- package/dist/{chunk-IAN7GUIV.cjs.map → chunk-WF7VGEYR.cjs.map} +1 -1
- package/dist/{chunk-C3FH53AP.cjs → chunk-X66KRL5G.cjs} +11 -11
- package/dist/{chunk-C3FH53AP.cjs.map → chunk-X66KRL5G.cjs.map} +1 -1
- package/dist/{chunk-WPVBTVI3.js → chunk-XFSDFO6U.js} +2 -2
- package/dist/{chunk-M6QGB6DC.cjs → chunk-YKP2F2NC.cjs} +26 -26
- package/dist/{chunk-M6QGB6DC.cjs.map → chunk-YKP2F2NC.cjs.map} +1 -1
- package/dist/{chunk-PEIR7D25.js → chunk-ZEY7A3MP.js} +2 -2
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/chat-archive-page.d.ts +8 -28
- package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
- package/dist/components/chat/chat-header-icon-button.d.ts +2 -6
- package/dist/components/chat/chat-header-icon-button.d.ts.map +1 -1
- package/dist/components/chat/chat-panel-header.d.ts +9 -19
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts +6 -15
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts +0 -2
- package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -4
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +0 -2
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +3 -5
- package/dist/components/chat/mingo-chat-history.d.ts +10 -5
- package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
- package/dist/components/chat/mingo-welcome.d.ts +25 -6
- package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +128 -130
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +11 -13
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/index.cjs +2 -4
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +3 -5
- package/dist/index.cjs +2 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -5
- package/package.json +1 -1
- package/src/components/chat/chat-archive-page.tsx +73 -89
- package/src/components/chat/chat-header-icon-button.tsx +5 -12
- package/src/components/chat/chat-panel-header.tsx +29 -76
- package/src/components/chat/embeddable-chat.tsx +125 -548
- package/src/components/chat/guide-welcome.tsx +4 -4
- package/src/components/chat/hooks/use-chat-dialog-manager.ts +5 -59
- package/src/components/chat/index.ts +0 -2
- package/src/components/chat/mingo-chat-history.tsx +100 -46
- package/src/components/chat/mingo-welcome.tsx +98 -17
- package/src/stories/MingoWelcome.stories.tsx +11 -2
- package/dist/chunk-HTVVXVJL.cjs.map +0 -1
- package/dist/chunk-NQQ4NLUT.js.map +0 -1
- package/dist/components/chat/chat-header-search-field.d.ts +0 -25
- package/dist/components/chat/chat-header-search-field.d.ts.map +0 -1
- package/dist/components/chat/chat-list-empty-state.d.ts +0 -20
- package/dist/components/chat/chat-list-empty-state.d.ts.map +0 -1
- package/dist/components/chat/mingo-history-rail.d.ts +0 -54
- package/dist/components/chat/mingo-history-rail.d.ts.map +0 -1
- package/src/components/chat/chat-header-search-field.tsx +0 -144
- package/src/components/chat/chat-list-empty-state.tsx +0 -44
- package/src/components/chat/mingo-history-rail.tsx +0 -161
- /package/dist/{chunk-PBTY7AH5.js.map → chunk-44HNH57W.js.map} +0 -0
- /package/dist/{chunk-G5FALXNO.js.map → chunk-5D5SJOLE.js.map} +0 -0
- /package/dist/{chunk-P7PISHM5.js.map → chunk-A25RPWYF.js.map} +0 -0
- /package/dist/{chunk-F7YVI54P.js.map → chunk-D5RTZ2WN.js.map} +0 -0
- /package/dist/{chunk-7WM77CSW.js.map → chunk-KBFQF4AV.js.map} +0 -0
- /package/dist/{chunk-2ELBNZJ3.js.map → chunk-LR6A67KZ.js.map} +0 -0
- /package/dist/{chunk-WAGTZVQN.js.map → chunk-POFDZH2C.js.map} +0 -0
- /package/dist/{chunk-EGRZHR7J.js.map → chunk-RTHOCS3S.js.map} +0 -0
- /package/dist/{chunk-COMBYYQY.js.map → chunk-W2PRXXUD.js.map} +0 -0
- /package/dist/{chunk-WPVBTVI3.js.map → chunk-XFSDFO6U.js.map} +0 -0
- /package/dist/{chunk-PEIR7D25.js.map → chunk-ZEY7A3MP.js.map} +0 -0
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
export interface ChatHeaderSearchFieldProps {
|
|
3
|
-
/** Seeds the field on mount (the current server-side search term). */
|
|
4
|
-
initialValue?: string;
|
|
5
|
-
/** Emits the DEBOUNCED term. The host owns the query and refetches the
|
|
6
|
-
* dialog list server-side — the field never filters locally. */
|
|
7
|
-
onSearchChange: (query: string) => void;
|
|
8
|
-
/** Collapse the field back to the title. Fired on Escape and on blur while
|
|
9
|
-
* the field is empty (an accidental open closes itself). */
|
|
10
|
-
onCollapse?: () => void;
|
|
11
|
-
/** Focus the input on mount (default true — the field only mounts when the
|
|
12
|
-
* user opens search, so we drop them straight into typing). */
|
|
13
|
-
autoFocus?: boolean;
|
|
14
|
-
/** Appended to the root element (e.g. to tweak the open animation). */
|
|
15
|
-
className?: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Inline chat-header search field (Figma node 116:51217). When search is
|
|
19
|
-
* toggled on, the panel header's title area is REPLACED in place by this
|
|
20
|
-
* full-height field — a leading magnifier + a bare, borderless input — instead
|
|
21
|
-
* of dropping a separate search bar into the list body. Holds its own text for
|
|
22
|
-
* snappy typing and emits the debounced term via `onSearchChange`.
|
|
23
|
-
*/
|
|
24
|
-
export declare function ChatHeaderSearchField({ initialValue, onSearchChange, onCollapse, autoFocus, className, }: ChatHeaderSearchFieldProps): React.JSX.Element;
|
|
25
|
-
//# sourceMappingURL=chat-header-search-field.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat-header-search-field.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-header-search-field.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,WAAW,0BAA0B;IACzC,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;qEACiE;IACjE,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;iEAC6D;IAC7D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB;oEACgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,YAAY,EACZ,cAAc,EACd,UAAU,EACV,SAAgB,EAChB,SAAS,GACV,EAAE,0BAA0B,qBA2G5B"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
export interface ChatListEmptyStateProps {
|
|
3
|
-
/** Centred 24px glyph — rendered muted (`ods-text-secondary`). Pass the icon
|
|
4
|
-
* already sized (e.g. `<ChatsIcon size={24} />`). */
|
|
5
|
-
icon: React.ReactNode;
|
|
6
|
-
/** Title line (`h4`). */
|
|
7
|
-
title: React.ReactNode;
|
|
8
|
-
/** Caption line under the title (`h6`). Omitted → title alone. */
|
|
9
|
-
description?: React.ReactNode;
|
|
10
|
-
/** Appended to the root element. */
|
|
11
|
-
className?: string;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Shared empty state for the chat-list surfaces — the "Current Chats" rail and
|
|
15
|
-
* the Chat Archive page (Figma node 113:60939). A centred 24px muted glyph over
|
|
16
|
-
* a title (`h4`) + caption (`h6`), both in `ods-text-secondary`. Kept in one
|
|
17
|
-
* place so the two lists can't drift apart.
|
|
18
|
-
*/
|
|
19
|
-
export declare function ChatListEmptyState({ icon, title, description, className, }: ChatListEmptyStateProps): React.JSX.Element;
|
|
20
|
-
//# sourceMappingURL=chat-list-empty-state.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat-list-empty-state.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-list-empty-state.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,WAAW,uBAAuB;IACtC;0DACsD;IACtD,IAAI,EAAE,KAAK,CAAC,SAAS,CAAA;IACrB,yBAAyB;IACzB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,kEAAkE;IAClE,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,KAAK,EACL,WAAW,EACX,SAAS,GACV,EAAE,uBAAuB,qBAezB"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import type { DialogItem } from './types/component.types';
|
|
3
|
-
export interface MingoHistoryRailProps {
|
|
4
|
-
/** Dialogs to list, assumed already sorted newest-first by the host. */
|
|
5
|
-
dialogs: ReadonlyArray<DialogItem>;
|
|
6
|
-
/** Currently-open dialog id (highlighted). */
|
|
7
|
-
activeDialogId?: string;
|
|
8
|
-
/** Open a dialog. */
|
|
9
|
-
onSelectDialog?: (id: string) => void;
|
|
10
|
-
/** Start a fresh chat — clears the open conversation so the chat block shows
|
|
11
|
-
* the welcome. Rendered as the pinned "Start New Chat" button above the list. */
|
|
12
|
-
onNewChat?: () => void;
|
|
13
|
-
/** Show the "Start New Chat" button even when there are no chats yet. Needed
|
|
14
|
-
* for the narrow single-column list, where there's no composer to fall back
|
|
15
|
-
* on; the wide rail leaves it off (the chat block already has a composer). */
|
|
16
|
-
newChatAlways?: boolean;
|
|
17
|
-
/** Request rename — enables the row "Rename chat" action. */
|
|
18
|
-
onRequestRename?: (dialog: DialogItem) => void;
|
|
19
|
-
/** Request archive — enables the row "Archive chat" action. */
|
|
20
|
-
onRequestArchive?: (dialog: DialogItem) => void;
|
|
21
|
-
/** Current server-side search term. Drives the list's "No chats found"
|
|
22
|
-
* empty state; the search INPUT itself lives in the panel header now, not
|
|
23
|
-
* in the rail body. */
|
|
24
|
-
searchQuery?: string;
|
|
25
|
-
/** Whether more dialogs remain (cursor pagination). */
|
|
26
|
-
hasMore?: boolean;
|
|
27
|
-
/** True while the next page is loading. */
|
|
28
|
-
isLoadingMore?: boolean;
|
|
29
|
-
/** Fetch the next page. */
|
|
30
|
-
onLoadMore?: () => void;
|
|
31
|
-
/** True while the FIRST page of dialogs is still loading. Renders a skeleton
|
|
32
|
-
* so the rail doesn't flash the empty state before the list arrives. */
|
|
33
|
-
isLoadingHistory?: boolean;
|
|
34
|
-
/** The dialog-list load FAILED with nothing cached — renders an error + retry
|
|
35
|
-
* block instead of the empty state. */
|
|
36
|
-
loadError?: boolean;
|
|
37
|
-
/** Retry handler for the `loadError` state. */
|
|
38
|
-
onRetry?: () => void;
|
|
39
|
-
/** Appended to the root element. */
|
|
40
|
-
className?: string;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* MingoHistoryRail — the persistent "Current Chats" left column of the split
|
|
44
|
-
* (wide) Mingo layout (Figma 113:60931 / 113:63630).
|
|
45
|
-
*
|
|
46
|
-
* The dialog history that the stacked layout renders inline in the Mingo empty
|
|
47
|
-
* state is hoisted here into a fixed-width rail beside the chat block. On top
|
|
48
|
-
* sits a pinned "Start New Chat" button (shown once the user has chats); below
|
|
49
|
-
* it the grouped Today / Yesterday / Older list (`MingoChatHistory`). With no
|
|
50
|
-
* chats it collapses to a centred empty state; loading / error states mirror
|
|
51
|
-
* the stacked empty state so the two layouts stay consistent.
|
|
52
|
-
*/
|
|
53
|
-
export declare function MingoHistoryRail({ dialogs, activeDialogId, onSelectDialog, onNewChat, newChatAlways, onRequestRename, onRequestArchive, searchQuery, hasMore, isLoadingMore, onLoadMore, isLoadingHistory, loadError, onRetry, className, }: MingoHistoryRailProps): React.JSX.Element;
|
|
54
|
-
//# sourceMappingURL=mingo-history-rail.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mingo-history-rail.d.ts","sourceRoot":"","sources":["../../../src/components/chat/mingo-history-rail.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAMzD,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IAClC,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qBAAqB;IACrB,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC;sFACkF;IAClF,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;mFAE+E;IAC/E,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC9C,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC/C;;4BAEwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB;6EACyE;IACzE,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B;4CACwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,cAAc,EACd,cAAc,EACd,SAAS,EACT,aAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,OAAe,EACf,aAAqB,EACrB,UAAU,EACV,gBAAwB,EACxB,SAAiB,EACjB,OAAO,EACP,SAAS,GACV,EAAE,qBAAqB,qBAyEvB"}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import * as React from 'react'
|
|
4
|
-
import { cn } from '../../utils/cn'
|
|
5
|
-
import { useDebounce } from '../../hooks/ui/use-debounce'
|
|
6
|
-
import { SearchIcon } from '../icons-v2-generated'
|
|
7
|
-
|
|
8
|
-
export interface ChatHeaderSearchFieldProps {
|
|
9
|
-
/** Seeds the field on mount (the current server-side search term). */
|
|
10
|
-
initialValue?: string
|
|
11
|
-
/** Emits the DEBOUNCED term. The host owns the query and refetches the
|
|
12
|
-
* dialog list server-side — the field never filters locally. */
|
|
13
|
-
onSearchChange: (query: string) => void
|
|
14
|
-
/** Collapse the field back to the title. Fired on Escape and on blur while
|
|
15
|
-
* the field is empty (an accidental open closes itself). */
|
|
16
|
-
onCollapse?: () => void
|
|
17
|
-
/** Focus the input on mount (default true — the field only mounts when the
|
|
18
|
-
* user opens search, so we drop them straight into typing). */
|
|
19
|
-
autoFocus?: boolean
|
|
20
|
-
/** Appended to the root element (e.g. to tweak the open animation). */
|
|
21
|
-
className?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Inline chat-header search field (Figma node 116:51217). When search is
|
|
26
|
-
* toggled on, the panel header's title area is REPLACED in place by this
|
|
27
|
-
* full-height field — a leading magnifier + a bare, borderless input — instead
|
|
28
|
-
* of dropping a separate search bar into the list body. Holds its own text for
|
|
29
|
-
* snappy typing and emits the debounced term via `onSearchChange`.
|
|
30
|
-
*/
|
|
31
|
-
export function ChatHeaderSearchField({
|
|
32
|
-
initialValue,
|
|
33
|
-
onSearchChange,
|
|
34
|
-
onCollapse,
|
|
35
|
-
autoFocus = true,
|
|
36
|
-
className,
|
|
37
|
-
}: ChatHeaderSearchFieldProps) {
|
|
38
|
-
const [value, setValue] = React.useState(initialValue ?? '')
|
|
39
|
-
const debounced = useDebounce(value, 300)
|
|
40
|
-
const lastEmitted = React.useRef(initialValue ?? '')
|
|
41
|
-
|
|
42
|
-
// Exit animation: collapsing plays `animate-out` (slide/fade back toward the
|
|
43
|
-
// right) BEFORE the parent unmounts us. We stay mounted through the leave —
|
|
44
|
-
// the parent only drops the field once we call `onCollapse`, which we defer
|
|
45
|
-
// to the animation's end.
|
|
46
|
-
const [exiting, setExiting] = React.useState(false)
|
|
47
|
-
const collapsedRef = React.useRef(false)
|
|
48
|
-
|
|
49
|
-
React.useEffect(() => {
|
|
50
|
-
// Once the collapse starts, no further emits: Escape already flushed the
|
|
51
|
-
// cleared term synchronously, and a still-pending debounce of the typed
|
|
52
|
-
// text ("abc" → Escape before 300ms) must not resurrect a stale query
|
|
53
|
-
// while the field animates out.
|
|
54
|
-
if (exiting) return
|
|
55
|
-
if (debounced === lastEmitted.current) return
|
|
56
|
-
lastEmitted.current = debounced
|
|
57
|
-
onSearchChange(debounced)
|
|
58
|
-
}, [debounced, exiting, onSearchChange])
|
|
59
|
-
|
|
60
|
-
const finishCollapse = React.useCallback(() => {
|
|
61
|
-
if (collapsedRef.current) return
|
|
62
|
-
collapsedRef.current = true
|
|
63
|
-
onCollapse?.()
|
|
64
|
-
}, [onCollapse])
|
|
65
|
-
|
|
66
|
-
const beginCollapse = React.useCallback(() => {
|
|
67
|
-
setExiting(true)
|
|
68
|
-
}, [])
|
|
69
|
-
|
|
70
|
-
// Fallback for when `animationend` never fires (reduced motion, or the
|
|
71
|
-
// animation utilities are disabled) — otherwise the field would hang open.
|
|
72
|
-
React.useEffect(() => {
|
|
73
|
-
if (!exiting) return
|
|
74
|
-
const t = setTimeout(finishCollapse, 240)
|
|
75
|
-
return () => clearTimeout(t)
|
|
76
|
-
}, [exiting, finishCollapse])
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<div
|
|
80
|
-
onAnimationEnd={(e) => {
|
|
81
|
-
// Only the root's OWN leave animation collapses — ignore the child
|
|
82
|
-
// icon's animations bubbling up, and the enter animation.
|
|
83
|
-
if (e.target === e.currentTarget && exiting) finishCollapse()
|
|
84
|
-
}}
|
|
85
|
-
className={cn(
|
|
86
|
-
'flex min-w-0 flex-1 items-center gap-[var(--spacing-system-xs)] px-[var(--spacing-system-sf)]',
|
|
87
|
-
exiting
|
|
88
|
-
? // Elegant leave: sweep back out to the right + fade, holding the end
|
|
89
|
-
// frame (`fill-mode-forwards`) so it stays hidden until unmount.
|
|
90
|
-
'animate-out fade-out-0 slide-out-to-right-4 duration-200 ease-in fill-mode-forwards pointer-events-none'
|
|
91
|
-
: // Elegant open: sweep in from the right (where the magnifier toggle
|
|
92
|
-
// sat) and fade up as it takes over the title area.
|
|
93
|
-
'animate-in fade-in-0 slide-in-from-right-4 duration-200 ease-out',
|
|
94
|
-
className,
|
|
95
|
-
)}
|
|
96
|
-
>
|
|
97
|
-
{/* The magnifier eases in a touch later + scales up, so the icon reads as
|
|
98
|
-
"growing" into the field rather than hard-cutting in. */}
|
|
99
|
-
<SearchIcon
|
|
100
|
-
size={24}
|
|
101
|
-
className="shrink-0 text-ods-text-secondary animate-in fade-in-0 zoom-in-75 duration-300 ease-out"
|
|
102
|
-
/>
|
|
103
|
-
{/* biome-ignore lint/a11y/noAutofocus: the field only mounts on an
|
|
104
|
-
explicit search toggle, so focusing it is the intended behaviour. */}
|
|
105
|
-
<input
|
|
106
|
-
autoFocus={autoFocus}
|
|
107
|
-
type="text"
|
|
108
|
-
value={value}
|
|
109
|
-
disabled={exiting}
|
|
110
|
-
onChange={(e) => setValue(e.target.value)}
|
|
111
|
-
onKeyDown={(e) => {
|
|
112
|
-
if (e.key === 'Escape') {
|
|
113
|
-
e.preventDefault()
|
|
114
|
-
setValue('')
|
|
115
|
-
// Emit the cleared term immediately AND record it as the last emit,
|
|
116
|
-
// so the 300ms debounce effect doesn't fire a second, redundant
|
|
117
|
-
// `onSearchChange('')` (extra server round-trip) before we unmount.
|
|
118
|
-
lastEmitted.current = ''
|
|
119
|
-
onSearchChange('')
|
|
120
|
-
beginCollapse()
|
|
121
|
-
}
|
|
122
|
-
}}
|
|
123
|
-
onBlur={() => {
|
|
124
|
-
// An accidental open (no query typed) collapses itself; a field with
|
|
125
|
-
// an active query stays open so the filtered state stays visible.
|
|
126
|
-
if (!value.trim()) {
|
|
127
|
-
// Flush the cleared term synchronously (mirrors Escape) — the
|
|
128
|
-
// debounce effect is muted once `exiting` flips, so a just-deleted
|
|
129
|
-
// query ("abc" → "" → immediate blur) must emit its reset here or
|
|
130
|
-
// the host would keep filtering by the stale term.
|
|
131
|
-
if (lastEmitted.current !== '') {
|
|
132
|
-
lastEmitted.current = ''
|
|
133
|
-
onSearchChange('')
|
|
134
|
-
}
|
|
135
|
-
beginCollapse()
|
|
136
|
-
}
|
|
137
|
-
}}
|
|
138
|
-
placeholder="Search for Chats"
|
|
139
|
-
aria-label="Search chats"
|
|
140
|
-
className="min-w-0 flex-1 bg-transparent text-h4 text-ods-text-primary placeholder:text-ods-text-secondary focus:outline-none"
|
|
141
|
-
/>
|
|
142
|
-
</div>
|
|
143
|
-
)
|
|
144
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import * as React from 'react'
|
|
4
|
-
import { cn } from '../../utils/cn'
|
|
5
|
-
|
|
6
|
-
export interface ChatListEmptyStateProps {
|
|
7
|
-
/** Centred 24px glyph — rendered muted (`ods-text-secondary`). Pass the icon
|
|
8
|
-
* already sized (e.g. `<ChatsIcon size={24} />`). */
|
|
9
|
-
icon: React.ReactNode
|
|
10
|
-
/** Title line (`h4`). */
|
|
11
|
-
title: React.ReactNode
|
|
12
|
-
/** Caption line under the title (`h6`). Omitted → title alone. */
|
|
13
|
-
description?: React.ReactNode
|
|
14
|
-
/** Appended to the root element. */
|
|
15
|
-
className?: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Shared empty state for the chat-list surfaces — the "Current Chats" rail and
|
|
20
|
-
* the Chat Archive page (Figma node 113:60939). A centred 24px muted glyph over
|
|
21
|
-
* a title (`h4`) + caption (`h6`), both in `ods-text-secondary`. Kept in one
|
|
22
|
-
* place so the two lists can't drift apart.
|
|
23
|
-
*/
|
|
24
|
-
export function ChatListEmptyState({
|
|
25
|
-
icon,
|
|
26
|
-
title,
|
|
27
|
-
description,
|
|
28
|
-
className,
|
|
29
|
-
}: ChatListEmptyStateProps) {
|
|
30
|
-
return (
|
|
31
|
-
<div
|
|
32
|
-
className={cn(
|
|
33
|
-
'flex flex-1 min-h-0 flex-col items-center justify-center gap-[var(--spacing-system-l)] p-[var(--spacing-system-l)] text-center text-ods-text-secondary',
|
|
34
|
-
className,
|
|
35
|
-
)}
|
|
36
|
-
>
|
|
37
|
-
<span className="flex size-6 shrink-0 items-center justify-center">{icon}</span>
|
|
38
|
-
<div className="flex flex-col">
|
|
39
|
-
<p className="text-h4">{title}</p>
|
|
40
|
-
{description && <p className="text-h6">{description}</p>}
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import * as React from 'react'
|
|
4
|
-
import { cn } from '../../utils/cn'
|
|
5
|
-
import { Button } from '../ui/button'
|
|
6
|
-
import { ChatsIcon } from '../icons-v2-generated/communication/chats-icon'
|
|
7
|
-
import { PlusCircleIcon } from '../icons-v2-generated/signs-and-symbols/plus-circle-icon'
|
|
8
|
-
import { AlertCircleIcon, Refresh01RightIcon } from '../icons-v2-generated'
|
|
9
|
-
import { MingoChatHistory, MingoChatHistorySkeleton } from './mingo-chat-history'
|
|
10
|
-
import { ChatListEmptyState } from './chat-list-empty-state'
|
|
11
|
-
import type { DialogItem } from './types/component.types'
|
|
12
|
-
|
|
13
|
-
// =============================================================================
|
|
14
|
-
// Types
|
|
15
|
-
// =============================================================================
|
|
16
|
-
|
|
17
|
-
export interface MingoHistoryRailProps {
|
|
18
|
-
/** Dialogs to list, assumed already sorted newest-first by the host. */
|
|
19
|
-
dialogs: ReadonlyArray<DialogItem>
|
|
20
|
-
/** Currently-open dialog id (highlighted). */
|
|
21
|
-
activeDialogId?: string
|
|
22
|
-
/** Open a dialog. */
|
|
23
|
-
onSelectDialog?: (id: string) => void
|
|
24
|
-
/** Start a fresh chat — clears the open conversation so the chat block shows
|
|
25
|
-
* the welcome. Rendered as the pinned "Start New Chat" button above the list. */
|
|
26
|
-
onNewChat?: () => void
|
|
27
|
-
/** Show the "Start New Chat" button even when there are no chats yet. Needed
|
|
28
|
-
* for the narrow single-column list, where there's no composer to fall back
|
|
29
|
-
* on; the wide rail leaves it off (the chat block already has a composer). */
|
|
30
|
-
newChatAlways?: boolean
|
|
31
|
-
/** Request rename — enables the row "Rename chat" action. */
|
|
32
|
-
onRequestRename?: (dialog: DialogItem) => void
|
|
33
|
-
/** Request archive — enables the row "Archive chat" action. */
|
|
34
|
-
onRequestArchive?: (dialog: DialogItem) => void
|
|
35
|
-
/** Current server-side search term. Drives the list's "No chats found"
|
|
36
|
-
* empty state; the search INPUT itself lives in the panel header now, not
|
|
37
|
-
* in the rail body. */
|
|
38
|
-
searchQuery?: string
|
|
39
|
-
/** Whether more dialogs remain (cursor pagination). */
|
|
40
|
-
hasMore?: boolean
|
|
41
|
-
/** True while the next page is loading. */
|
|
42
|
-
isLoadingMore?: boolean
|
|
43
|
-
/** Fetch the next page. */
|
|
44
|
-
onLoadMore?: () => void
|
|
45
|
-
/** True while the FIRST page of dialogs is still loading. Renders a skeleton
|
|
46
|
-
* so the rail doesn't flash the empty state before the list arrives. */
|
|
47
|
-
isLoadingHistory?: boolean
|
|
48
|
-
/** The dialog-list load FAILED with nothing cached — renders an error + retry
|
|
49
|
-
* block instead of the empty state. */
|
|
50
|
-
loadError?: boolean
|
|
51
|
-
/** Retry handler for the `loadError` state. */
|
|
52
|
-
onRetry?: () => void
|
|
53
|
-
/** Appended to the root element. */
|
|
54
|
-
className?: string
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// =============================================================================
|
|
58
|
-
// Component
|
|
59
|
-
// =============================================================================
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* MingoHistoryRail — the persistent "Current Chats" left column of the split
|
|
63
|
-
* (wide) Mingo layout (Figma 113:60931 / 113:63630).
|
|
64
|
-
*
|
|
65
|
-
* The dialog history that the stacked layout renders inline in the Mingo empty
|
|
66
|
-
* state is hoisted here into a fixed-width rail beside the chat block. On top
|
|
67
|
-
* sits a pinned "Start New Chat" button (shown once the user has chats); below
|
|
68
|
-
* it the grouped Today / Yesterday / Older list (`MingoChatHistory`). With no
|
|
69
|
-
* chats it collapses to a centred empty state; loading / error states mirror
|
|
70
|
-
* the stacked empty state so the two layouts stay consistent.
|
|
71
|
-
*/
|
|
72
|
-
export function MingoHistoryRail({
|
|
73
|
-
dialogs,
|
|
74
|
-
activeDialogId,
|
|
75
|
-
onSelectDialog,
|
|
76
|
-
onNewChat,
|
|
77
|
-
newChatAlways = false,
|
|
78
|
-
onRequestRename,
|
|
79
|
-
onRequestArchive,
|
|
80
|
-
searchQuery,
|
|
81
|
-
hasMore = false,
|
|
82
|
-
isLoadingMore = false,
|
|
83
|
-
onLoadMore,
|
|
84
|
-
isLoadingHistory = false,
|
|
85
|
-
loadError = false,
|
|
86
|
-
onRetry,
|
|
87
|
-
className,
|
|
88
|
-
}: MingoHistoryRailProps) {
|
|
89
|
-
const hasSearch = !!searchQuery?.trim()
|
|
90
|
-
// The list (vs. the empty state) shows whenever there are chats OR an active
|
|
91
|
-
// search is running — a no-match query must keep the search bar mounted
|
|
92
|
-
// rather than flash the "No Current Chats" state.
|
|
93
|
-
const hasList = dialogs.length > 0 || hasSearch
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<div
|
|
97
|
-
className={cn(
|
|
98
|
-
'flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] p-[var(--spacing-system-m)]',
|
|
99
|
-
className,
|
|
100
|
-
)}
|
|
101
|
-
>
|
|
102
|
-
{/* Pinned "Start New Chat". In the wide rail it's offered only once the
|
|
103
|
-
user has chats (the chat block's composer is the empty-state entry);
|
|
104
|
-
the narrow list forces it on (`newChatAlways`) since it has no composer. */}
|
|
105
|
-
{onNewChat && (hasList || newChatAlways) && (
|
|
106
|
-
<Button
|
|
107
|
-
variant="outline"
|
|
108
|
-
fullWidth
|
|
109
|
-
leftIcon={<PlusCircleIcon size={20} />}
|
|
110
|
-
onClick={onNewChat}
|
|
111
|
-
className="shrink-0 justify-start"
|
|
112
|
-
>
|
|
113
|
-
Start New Chat
|
|
114
|
-
</Button>
|
|
115
|
-
)}
|
|
116
|
-
|
|
117
|
-
{loadError ? (
|
|
118
|
-
<div className="flex flex-1 min-h-0 flex-col items-center justify-center gap-[var(--spacing-system-m)] text-center">
|
|
119
|
-
<AlertCircleIcon className="h-8 w-8 text-ods-text-secondary shrink-0" />
|
|
120
|
-
<div className="flex flex-col gap-[var(--spacing-system-xxs)]">
|
|
121
|
-
<p className="text-h4 text-ods-text-primary">Couldn’t load your chats</p>
|
|
122
|
-
<p className="text-h6 text-ods-text-secondary">
|
|
123
|
-
Something went wrong reaching the server. Try again.
|
|
124
|
-
</p>
|
|
125
|
-
</div>
|
|
126
|
-
{onRetry && (
|
|
127
|
-
<Button
|
|
128
|
-
variant="outline"
|
|
129
|
-
size="small"
|
|
130
|
-
leftIcon={<Refresh01RightIcon />}
|
|
131
|
-
onClick={onRetry}
|
|
132
|
-
>
|
|
133
|
-
Try again
|
|
134
|
-
</Button>
|
|
135
|
-
)}
|
|
136
|
-
</div>
|
|
137
|
-
) : isLoadingHistory ? (
|
|
138
|
-
<MingoChatHistorySkeleton />
|
|
139
|
-
) : hasList ? (
|
|
140
|
-
<MingoChatHistory
|
|
141
|
-
dialogs={dialogs}
|
|
142
|
-
activeDialogId={activeDialogId}
|
|
143
|
-
onSelectDialog={onSelectDialog}
|
|
144
|
-
onRequestRename={onRequestRename}
|
|
145
|
-
onRequestArchive={onRequestArchive}
|
|
146
|
-
searchQuery={searchQuery}
|
|
147
|
-
hasMore={hasMore}
|
|
148
|
-
isLoadingMore={isLoadingMore}
|
|
149
|
-
onLoadMore={onLoadMore}
|
|
150
|
-
/>
|
|
151
|
-
) : (
|
|
152
|
-
// Figma 113:60939 — no-chats empty state.
|
|
153
|
-
<ChatListEmptyState
|
|
154
|
-
icon={<ChatsIcon size={24} />}
|
|
155
|
-
title="No Current Chats"
|
|
156
|
-
description="Previous Mingo sessions will show here"
|
|
157
|
-
/>
|
|
158
|
-
)}
|
|
159
|
-
</div>
|
|
160
|
-
)
|
|
161
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|