@flamingo-stack/openframe-frontend-core 0.0.704 → 0.0.705
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-MGGGBHYD.cjs → chunk-2QEUFXNS.cjs} +10 -10
- package/dist/{chunk-LDPEQGKT.cjs → chunk-3MX5S4CF.cjs} +132 -80
- package/dist/{chunk-UK62YTH6.cjs → chunk-4I45Y3DN.cjs} +2 -2
- package/dist/{chunk-AMYY3PNL.cjs → chunk-4KX6OED2.cjs} +42 -42
- package/dist/{chunk-4DQMTBMK.cjs → chunk-6NJKKO73.cjs} +11 -11
- package/dist/{chunk-JEYRXT5L.js → chunk-6SEANMOU.js} +7 -7
- package/dist/{chunk-C7XGRLT5.cjs → chunk-7LS22BZU.cjs} +6 -6
- package/dist/{chunk-2JDVL7UV.js → chunk-7VVFKJY5.js} +70 -18
- package/dist/{chunk-7JIL3WOH.cjs → chunk-A75QY6CD.cjs} +14 -14
- package/dist/{chunk-VTASHWER.js → chunk-AQWZNP6A.js} +3 -3
- package/dist/{chunk-ZQFZSFQI.js → chunk-DENB2XJC.js} +2 -2
- package/dist/{chunk-FXENTYE5.js → chunk-DW7G4KG3.js} +1 -1
- package/dist/{chunk-I7DFUQMN.js → chunk-F6AFPNDF.js} +5 -6
- package/dist/{chunk-5QJDM6UV.js → chunk-F6I6XZMN.js} +3 -3
- package/dist/{chunk-4KGXLVCF.js → chunk-FCGC42TS.js} +3 -3
- package/dist/{chunk-2G546OND.js → chunk-G5A3PSPV.js} +19 -7
- package/dist/{chunk-N3TO7JV3.cjs → chunk-HPWWNJE7.cjs} +49 -49
- package/dist/{chunk-MI3JUJ75.cjs → chunk-IHERL5V4.cjs} +21 -21
- package/dist/{chunk-XIFOBQRQ.cjs → chunk-IKTQ3ZAG.cjs} +4 -4
- package/dist/{chunk-262YQFOS.cjs → chunk-JEZKGCZI.cjs} +24 -24
- package/dist/{chunk-L3IC42IF.cjs → chunk-KOWVJD4V.cjs} +17 -18
- package/dist/{chunk-IHU5ANUY.js → chunk-MKXO4Q5P.js} +1 -1
- package/dist/{chunk-IZGDG5X6.cjs → chunk-OUY3HHW6.cjs} +57 -45
- package/dist/{chunk-WUJNYGO2.js → chunk-PDWVPI3S.js} +0 -4
- package/dist/{chunk-ERCV6HHJ.js → chunk-PN2RKPDF.js} +4 -4
- package/dist/{chunk-7LFIKJX2.js → chunk-QFXRPRFT.js} +2 -2
- package/dist/{chunk-NRLJUBCS.js → chunk-RBDP4WDE.js} +2 -2
- package/dist/{chunk-C3BWIU7R.cjs → chunk-TYWLNCNH.cjs} +1 -5
- package/dist/{chunk-XATTBO5X.js → chunk-VA5T32XA.js} +1 -1
- package/dist/{chunk-LTSTC3N5.cjs → chunk-VESKITRQ.cjs} +379 -379
- package/dist/{chunk-HFJW24JJ.cjs → chunk-WA6GUYQ4.cjs} +79 -79
- package/dist/{chunk-QVSFBSWV.js → chunk-X5MQ7JRP.js} +4 -4
- package/dist/{chunk-XJKZ4IKT.cjs → chunk-YFBM2K6X.cjs} +4 -4
- package/dist/{chunk-6WQIUQUC.js → chunk-YHM5AWTT.js} +5 -5
- package/dist/{chunk-SYXQLETR.cjs → chunk-ZSAGLQOD.cjs} +9 -9
- package/dist/{chunk-HJAYVKWQ.js → chunk-ZSAO2HBW.js} +6 -6
- package/dist/components/case-studies/index.cjs +17 -17
- package/dist/components/case-studies/index.js +5 -5
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts +8 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +5 -3
- package/dist/components/chat/index.d.ts +1 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +4 -2
- package/dist/components/chat/mingo-chat-rail.d.ts +3 -1
- package/dist/components/chat/mingo-chat-rail.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +6 -6
- package/dist/components/contact/index.js +5 -5
- package/dist/components/docs/index.cjs +9 -9
- package/dist/components/docs/index.js +8 -8
- package/dist/components/embeds/index.cjs +6 -6
- package/dist/components/embeds/index.js +5 -5
- package/dist/components/faq/index.cjs +6 -6
- package/dist/components/faq/index.js +5 -5
- package/dist/components/features/index.cjs +5 -5
- package/dist/components/features/index.js +4 -4
- package/dist/components/help-center-pages/index.cjs +81 -81
- package/dist/components/help-center-pages/index.js +16 -16
- package/dist/components/index.cjs +51 -47
- package/dist/components/index.js +19 -15
- package/dist/components/meeting-scheduler/index.cjs +32 -32
- package/dist/components/meeting-scheduler/index.js +4 -4
- package/dist/components/navigation/app-layout-side-panel.d.ts +18 -1
- package/dist/components/navigation/app-layout-side-panel.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +10 -8
- package/dist/components/navigation/index.d.ts +1 -0
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +9 -7
- package/dist/components/onboarding-guides/index.cjs +8 -8
- package/dist/components/onboarding-guides/index.js +7 -7
- package/dist/components/tickets/index.cjs +9 -9
- package/dist/components/tickets/index.js +8 -8
- package/dist/components/ui/index.cjs +7 -5
- package/dist/components/ui/index.js +6 -4
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/index.cjs +14 -10
- package/dist/index.js +12 -8
- package/package.json +1 -1
- package/src/components/chat/chat-input.tsx +5 -1
- package/src/components/chat/chat-message-enhanced.tsx +4 -3
- package/src/components/chat/embeddable-chat.tsx +64 -6
- package/src/components/chat/index.ts +1 -0
- package/src/components/chat/mingo-chat-rail.tsx +24 -7
- package/src/components/navigation/app-layout-side-panel.tsx +30 -2
- package/src/components/navigation/index.ts +1 -0
- package/src/components/platform/SoftwareSourceBadge.tsx +1 -1
- package/src/styles/app-globals.css +13 -0
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
SoftwareInfo,
|
|
10
10
|
SoftwareSourceBadge,
|
|
11
11
|
ToolBadge
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-F6AFPNDF.js";
|
|
13
13
|
import {
|
|
14
14
|
AppHeader,
|
|
15
15
|
AppLayout,
|
|
@@ -26,17 +26,18 @@ import {
|
|
|
26
26
|
NAVIGATION_SIDEBAR_WIDTH_VAR,
|
|
27
27
|
NavigationItemBadge,
|
|
28
28
|
NavigationSidebar,
|
|
29
|
+
SIDE_PANEL_FRAME_WIDTH,
|
|
29
30
|
SlidingSidebar,
|
|
30
31
|
TicketAlertsButton,
|
|
31
32
|
TopNavigation
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-G5A3PSPV.js";
|
|
33
34
|
import {
|
|
34
35
|
MobileNavigationDropdown,
|
|
35
36
|
MultiLevelNavigation,
|
|
36
37
|
StickySectionNav,
|
|
37
38
|
useSectionNavigation
|
|
38
|
-
} from "./chunk-
|
|
39
|
-
import "./chunk-
|
|
39
|
+
} from "./chunk-DENB2XJC.js";
|
|
40
|
+
import "./chunk-QFXRPRFT.js";
|
|
40
41
|
import {
|
|
41
42
|
AIEnrichButton,
|
|
42
43
|
AIEnrichSection,
|
|
@@ -665,7 +666,7 @@ import {
|
|
|
665
666
|
withFirstTouchAttribution,
|
|
666
667
|
withWalkthroughOpenParam,
|
|
667
668
|
writeDismissCookie
|
|
668
|
-
} from "./chunk-
|
|
669
|
+
} from "./chunk-FCGC42TS.js";
|
|
669
670
|
import "./chunk-LQYXMEYF.js";
|
|
670
671
|
import "./chunk-4JPLYOS6.js";
|
|
671
672
|
import {
|
|
@@ -783,7 +784,7 @@ import {
|
|
|
783
784
|
validateSchema,
|
|
784
785
|
validateVariables,
|
|
785
786
|
variablesToUrlParams
|
|
786
|
-
} from "./chunk-
|
|
787
|
+
} from "./chunk-MKXO4Q5P.js";
|
|
787
788
|
import "./chunk-HFL2F4RM.js";
|
|
788
789
|
import {
|
|
789
790
|
CONTENT_REF_GROUPS,
|
|
@@ -937,6 +938,7 @@ import {
|
|
|
937
938
|
MESSAGE_ROLE,
|
|
938
939
|
MESSAGE_TYPE,
|
|
939
940
|
MINGO_V2_RAIL_WIDTH,
|
|
941
|
+
MINGO_V2_SPLIT_WIDTH,
|
|
940
942
|
MUX_IMAGE_ORIGIN,
|
|
941
943
|
MUX_STREAM_ORIGIN,
|
|
942
944
|
MarkdownEngine,
|
|
@@ -1204,7 +1206,7 @@ import {
|
|
|
1204
1206
|
useVideoOriginPreconnect,
|
|
1205
1207
|
useVideoWarmup,
|
|
1206
1208
|
usesCanonicalStatusStyle
|
|
1207
|
-
} from "./chunk-
|
|
1209
|
+
} from "./chunk-7VVFKJY5.js";
|
|
1208
1210
|
import {
|
|
1209
1211
|
HEADING_EMOJI_RE,
|
|
1210
1212
|
HUB_HEADER_OFFSET_PX,
|
|
@@ -1246,7 +1248,7 @@ import {
|
|
|
1246
1248
|
usePrefersReducedMotionState,
|
|
1247
1249
|
useResetOnPageHidden,
|
|
1248
1250
|
useSuppressCloneFocus
|
|
1249
|
-
} from "./chunk-
|
|
1251
|
+
} from "./chunk-PDWVPI3S.js";
|
|
1250
1252
|
import "./chunk-GY4IZG22.js";
|
|
1251
1253
|
import {
|
|
1252
1254
|
AboutIcon,
|
|
@@ -2447,6 +2449,7 @@ export {
|
|
|
2447
2449
|
MESSAGE_TYPE,
|
|
2448
2450
|
MINGO_APPROVAL_REQUEST_TYPE,
|
|
2449
2451
|
MINGO_V2_RAIL_WIDTH,
|
|
2452
|
+
MINGO_V2_SPLIT_WIDTH,
|
|
2450
2453
|
MOBILE_NAV_PANEL_ID,
|
|
2451
2454
|
MUX_IMAGE_ORIGIN,
|
|
2452
2455
|
MUX_STREAM_ORIGIN,
|
|
@@ -2705,6 +2708,7 @@ export {
|
|
|
2705
2708
|
SEO_DESCRIPTION_MAX_LENGTH,
|
|
2706
2709
|
SEO_TITLE_MAX_LENGTH,
|
|
2707
2710
|
SHELL_TYPES,
|
|
2711
|
+
SIDE_PANEL_FRAME_WIDTH,
|
|
2708
2712
|
SLOT_STICKINESS,
|
|
2709
2713
|
SOCIAL_PLATFORM_ALIASES,
|
|
2710
2714
|
SOCIAL_PLATFORM_ALT_HOSTS,
|
package/package.json
CHANGED
|
@@ -631,9 +631,12 @@ const ChatInput = forwardRef<ChatInputRef, ChatInputProps>((allProps, ref) => {
|
|
|
631
631
|
<div className="relative min-w-0 flex-1">
|
|
632
632
|
{/* No chip-line override on the placeholder: it only renders while
|
|
633
633
|
the draft is EMPTY, so the editor is always on its plain line
|
|
634
|
-
box here.
|
|
634
|
+
box here. `data-editor-ghost` (here and on the preview) pins
|
|
635
|
+
both overlays to 16px on phones alongside `data-editor` — the
|
|
636
|
+
iOS focus-zoom rule in app-globals.css. */}
|
|
635
637
|
{isEmpty && !showPreview && (
|
|
636
638
|
<span
|
|
639
|
+
data-editor-ghost
|
|
637
640
|
className={cn(
|
|
638
641
|
// One line, clipped with an ellipsis (Figma 954:28455): the
|
|
639
642
|
// disabled copy is a sentence, and the panel's narrowest
|
|
@@ -659,6 +662,7 @@ const ChatInput = forwardRef<ChatInputRef, ChatInputProps>((allProps, ref) => {
|
|
|
659
662
|
editor. */}
|
|
660
663
|
{showPreview && (
|
|
661
664
|
<p
|
|
665
|
+
data-editor-ghost
|
|
662
666
|
className={cn(
|
|
663
667
|
'pointer-events-none absolute inset-x-0 top-0 m-0 select-none truncate text-ods-text-secondary text-h4',
|
|
664
668
|
hasChips && '!leading-9',
|
|
@@ -522,9 +522,10 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
|
|
|
522
522
|
// its own short line above the message (on the right for the user), never
|
|
523
523
|
// beside it: a face in the row indents every line of the message by its
|
|
524
524
|
// width. The NAME is written only where it is the information itself: a
|
|
525
|
-
// system line and a human technician.
|
|
526
|
-
//
|
|
527
|
-
|
|
525
|
+
// system line and a human technician answering. Mingo sends the admin's
|
|
526
|
+
// own turns as `admin` too; those are the user's words and draw as such.
|
|
527
|
+
// Every face carries its name for assistive tech and on hover.
|
|
528
|
+
const showName = isSystem || (authorType === 'admin' && !isUser);
|
|
528
529
|
const displayName = name || (isUser ? 'User' : assistantType === 'mingo' ? 'Mingo' : 'Fae');
|
|
529
530
|
const faceBox = 'h-5 w-5 flex-shrink-0';
|
|
530
531
|
const face: ReactNode =
|
|
@@ -131,6 +131,8 @@ const HISTORY_RAIL_WIDTH = 320;
|
|
|
131
131
|
/** The v2 chat list (`MingoChatRail`, Figma `chat-sidebar`): the narrowest the v2 chat draws. */
|
|
132
132
|
export const MINGO_V2_RAIL_WIDTH = 296;
|
|
133
133
|
const CHAT_BLOCK_MIN_WIDTH = 400;
|
|
134
|
+
/** Mingo v2: the list and the chat side by side at their narrowest. */
|
|
135
|
+
export const MINGO_V2_SPLIT_WIDTH = MINGO_V2_RAIL_WIDTH + CHAT_BLOCK_MIN_WIDTH;
|
|
134
136
|
const SPLIT_MIN_WIDTH = HISTORY_RAIL_WIDTH + CHAT_BLOCK_MIN_WIDTH;
|
|
135
137
|
|
|
136
138
|
// Desktop drawer is this fraction of the viewport width (clamped to the
|
|
@@ -364,6 +366,12 @@ export interface EmbeddableChatProps {
|
|
|
364
366
|
* the open chat, which stays open.
|
|
365
367
|
*/
|
|
366
368
|
collapseTo?: 'list' | 'column';
|
|
369
|
+
/**
|
|
370
|
+
* v2: the |← control on the chat list while the panel shows the list alone.
|
|
371
|
+
* The host widens its panel; the chat opens the conversation open last (or
|
|
372
|
+
* the newest in the list), or a new chat when there is none.
|
|
373
|
+
*/
|
|
374
|
+
onExpand?: () => void;
|
|
367
375
|
|
|
368
376
|
/** v2: the status glyph at the end of a chat's row (working / unread / approval). */
|
|
369
377
|
dialogStatusOf?: (dialog: DialogItem) => MingoDialogStatus | undefined;
|
|
@@ -1024,6 +1032,7 @@ function EmbeddableChatInner({
|
|
|
1024
1032
|
appearance = CHAT_APPEARANCE.CLASSIC,
|
|
1025
1033
|
onCollapse,
|
|
1026
1034
|
collapseTo = 'list',
|
|
1035
|
+
onExpand,
|
|
1027
1036
|
dialogStatusOf,
|
|
1028
1037
|
userDisplayName,
|
|
1029
1038
|
userAvatarUrl,
|
|
@@ -1601,18 +1610,25 @@ function EmbeddableChatInner({
|
|
|
1601
1610
|
[handleNavigationClose],
|
|
1602
1611
|
);
|
|
1603
1612
|
|
|
1613
|
+
// "Ask Mingo" about a row (a card or source chip in the thread, or a page's
|
|
1614
|
+
// `ask-ai:open-with-ref`): asks in the open conversation. Forwarded through a
|
|
1615
|
+
// ref because what it does depends on dialog state declared further down
|
|
1616
|
+
// (`askAboutRef.current` is set next to it: an archived chat is read-only).
|
|
1617
|
+
const askAboutRef = useRef<(reference: ChatRef) => void>(discussRef);
|
|
1618
|
+
const askAbout = useCallback((reference: ChatRef) => askAboutRef.current(reference), []);
|
|
1619
|
+
|
|
1604
1620
|
// Host-provided renderer for inline entity cards — routes through the
|
|
1605
1621
|
// shared dispatcher in lib's `entity-cards/dispatch.tsx`.
|
|
1606
1622
|
const renderEntityCard = useCallback(
|
|
1607
1623
|
(reference: ChatRef): React.ReactNode =>
|
|
1608
1624
|
renderChatInlineEntityCard(reference, {
|
|
1609
|
-
onDiscuss:
|
|
1625
|
+
onDiscuss: askAbout,
|
|
1610
1626
|
onDisplay: displayRef,
|
|
1611
1627
|
baseRoute: resolvedBaseRoute,
|
|
1612
1628
|
chipBasePlatform,
|
|
1613
1629
|
extras,
|
|
1614
1630
|
}),
|
|
1615
|
-
[
|
|
1631
|
+
[askAbout, displayRef, resolvedBaseRoute, chipBasePlatform, extras],
|
|
1616
1632
|
);
|
|
1617
1633
|
|
|
1618
1634
|
// Stable assistant-icon element. `<ChatMessageList>` forwards this prop to
|
|
@@ -1832,11 +1848,11 @@ function EmbeddableChatInner({
|
|
|
1832
1848
|
).detail;
|
|
1833
1849
|
if (!detail || detail.source !== source) return;
|
|
1834
1850
|
setIsOpen(true);
|
|
1835
|
-
setTimeout(() =>
|
|
1851
|
+
setTimeout(() => askAbout(detail.ref as ChatRef), 0);
|
|
1836
1852
|
};
|
|
1837
1853
|
window.addEventListener('ask-ai:open-with-ref', handler);
|
|
1838
1854
|
return () => window.removeEventListener('ask-ai:open-with-ref', handler);
|
|
1839
|
-
}, [source,
|
|
1855
|
+
}, [source, askAbout, setIsOpen]);
|
|
1840
1856
|
|
|
1841
1857
|
// Listen for plain "open chat" events (no row context). Fired by the
|
|
1842
1858
|
// header MingoAiButton. Same strict source filter as `ask-ai:open-with-ref`
|
|
@@ -1960,12 +1976,12 @@ function EmbeddableChatInner({
|
|
|
1960
1976
|
baseRoute={resolvedBaseRoute}
|
|
1961
1977
|
chipBasePlatform={chipBasePlatform}
|
|
1962
1978
|
onClose={handleNavigationClose}
|
|
1963
|
-
onDiscuss={
|
|
1979
|
+
onDiscuss={askAbout}
|
|
1964
1980
|
/>
|
|
1965
1981
|
</div>
|
|
1966
1982
|
);
|
|
1967
1983
|
},
|
|
1968
|
-
[chatLoading, messages.length, resolvedBaseRoute, chipBasePlatform, handleNavigationClose,
|
|
1984
|
+
[chatLoading, messages.length, resolvedBaseRoute, chipBasePlatform, handleNavigationClose, askAbout],
|
|
1969
1985
|
);
|
|
1970
1986
|
|
|
1971
1987
|
// Host node for in-panel Radix portals (see the body wrapper below).
|
|
@@ -2176,6 +2192,37 @@ function EmbeddableChatInner({
|
|
|
2176
2192
|
resetToNewChat();
|
|
2177
2193
|
}, [resetToNewChat]);
|
|
2178
2194
|
|
|
2195
|
+
// "Ask Mingo" from an archived chat: the conversation is read-only (the
|
|
2196
|
+
// backend refuses writes), so the question starts a new chat instead. It is
|
|
2197
|
+
// queued until the host has let go of the archived dialog: until then its
|
|
2198
|
+
// `discussRef` still writes into the dialog it has open.
|
|
2199
|
+
const queuedAskRef = useRef<ChatRef | null>(null);
|
|
2200
|
+
useEffect(() => {
|
|
2201
|
+
askAboutRef.current = reference => {
|
|
2202
|
+
if (!isViewingArchived) {
|
|
2203
|
+
discussRef(reference);
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
queuedAskRef.current = reference;
|
|
2207
|
+
if (archiveOpen) closeArchive();
|
|
2208
|
+
resetToNewChat();
|
|
2209
|
+
setComposeOpen(true);
|
|
2210
|
+
};
|
|
2211
|
+
});
|
|
2212
|
+
useEffect(() => {
|
|
2213
|
+
const reference = queuedAskRef.current;
|
|
2214
|
+
if (!reference || activeDialogId != null) return;
|
|
2215
|
+
queuedAskRef.current = null;
|
|
2216
|
+
discussRef(reference);
|
|
2217
|
+
}, [activeDialogId, discussRef]);
|
|
2218
|
+
|
|
2219
|
+
// The conversation open last, for the |← control: going back to the list
|
|
2220
|
+
// closes it, and widening the panel again should bring it back.
|
|
2221
|
+
const lastDialogIdRef = useRef<string | null>(null);
|
|
2222
|
+
useEffect(() => {
|
|
2223
|
+
if (activeDialogId && !isViewingArchived) lastDialogIdRef.current = activeDialogId;
|
|
2224
|
+
}, [activeDialogId, isViewingArchived]);
|
|
2225
|
+
|
|
2179
2226
|
// Host prefill (`prefillDraft`) — staged, not written at once. The composer
|
|
2180
2227
|
// it goes into may only mount on the commit `resetToNewChat` +
|
|
2181
2228
|
// `setComposeOpen(true)` produce (narrow layout: list → compose; or the
|
|
@@ -2654,6 +2701,17 @@ function EmbeddableChatInner({
|
|
|
2654
2701
|
<MingoChatRail
|
|
2655
2702
|
{...mingoRailProps}
|
|
2656
2703
|
onNewChat={() => setComposeOpen(true)}
|
|
2704
|
+
onExpand={
|
|
2705
|
+
onExpand &&
|
|
2706
|
+
(() => {
|
|
2707
|
+
const last = lastDialogIdRef.current;
|
|
2708
|
+
const target = last && dialogs.some(d => d.id === last) ? last : dialogs[0]?.id;
|
|
2709
|
+
if (archiveOpen) closeArchive();
|
|
2710
|
+
if (target) handleSelectDialog(target);
|
|
2711
|
+
else handleNewChat();
|
|
2712
|
+
onExpand();
|
|
2713
|
+
})
|
|
2714
|
+
}
|
|
2657
2715
|
className="w-full border-r-0 duration-200 animate-in fade-in-0"
|
|
2658
2716
|
/>
|
|
2659
2717
|
) : stackedListView ? (
|
|
@@ -4,6 +4,7 @@ import { type ReactNode, useEffect, useRef, useState } from 'react';
|
|
|
4
4
|
import { cn } from '../../utils/cn';
|
|
5
5
|
import {
|
|
6
6
|
AlertCircleIcon,
|
|
7
|
+
Arrow02LeftIcon,
|
|
7
8
|
ChatPlusIcon,
|
|
8
9
|
ChatQuestionIcon,
|
|
9
10
|
ChatsIcon,
|
|
@@ -69,6 +70,8 @@ export interface MingoChatRailProps {
|
|
|
69
70
|
/** The list failed to load with nothing cached. */
|
|
70
71
|
loadError?: boolean;
|
|
71
72
|
onRetry?: () => void;
|
|
73
|
+
/** The |← control beside the logo: widen the panel to the list and the chat. */
|
|
74
|
+
onExpand?: () => void;
|
|
72
75
|
className?: string;
|
|
73
76
|
}
|
|
74
77
|
|
|
@@ -113,6 +116,7 @@ export function MingoChatRail({
|
|
|
113
116
|
isLoadingHistory = false,
|
|
114
117
|
loadError = false,
|
|
115
118
|
onRetry,
|
|
119
|
+
onExpand,
|
|
116
120
|
className,
|
|
117
121
|
}: MingoChatRailProps) {
|
|
118
122
|
const [searchOpen, setSearchOpen] = useState(!!searchQuery?.trim());
|
|
@@ -251,13 +255,26 @@ export function MingoChatRail({
|
|
|
251
255
|
data-accent="mingo"
|
|
252
256
|
aria-label="Mingo chats"
|
|
253
257
|
logo={
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
258
|
+
<div className="flex flex-1 items-center justify-between">
|
|
259
|
+
<MingoIcon
|
|
260
|
+
color="currentColor"
|
|
261
|
+
eyesColor="var(--ods-flamingo-cyan-base)"
|
|
262
|
+
cornerColor="var(--ods-flamingo-cyan-base)"
|
|
263
|
+
className="size-6 text-ods-text-primary"
|
|
264
|
+
aria-hidden
|
|
265
|
+
/>
|
|
266
|
+
{onExpand && (
|
|
267
|
+
<Button
|
|
268
|
+
variant="transparent"
|
|
269
|
+
size="icon"
|
|
270
|
+
aria-label="Expand chat"
|
|
271
|
+
onClick={onExpand}
|
|
272
|
+
className="size-6 p-0 text-ods-text-secondary hover:text-ods-text-primary md:size-6"
|
|
273
|
+
>
|
|
274
|
+
<Arrow02LeftIcon size={24} />
|
|
275
|
+
</Button>
|
|
276
|
+
)}
|
|
277
|
+
</div>
|
|
261
278
|
}
|
|
262
279
|
items={items}
|
|
263
280
|
sectionLabel="Your Current Chats"
|
|
@@ -44,13 +44,23 @@ export interface AppLayoutSidePanelRenderState {
|
|
|
44
44
|
collapsesTo: 'column' | 'minimum' | null;
|
|
45
45
|
/** Step the panel back (see `collapsesTo`), animated. */
|
|
46
46
|
collapse: () => void;
|
|
47
|
+
/** Docked at its minimum with room for more: `expand` widens it. */
|
|
48
|
+
canExpand: boolean;
|
|
49
|
+
/** Widen the panel from its minimum to `defaultWidth` (as much as fits), animated. */
|
|
50
|
+
expand: () => void;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
export interface AppLayoutSidePanelConfig {
|
|
50
54
|
/** Panel body. Told its width so it can choose what fits. */
|
|
51
55
|
children: (state: AppLayoutSidePanelRenderState) => ReactNode;
|
|
52
|
-
/** Narrowest docked width
|
|
56
|
+
/** Narrowest docked width: the narrowest its content draws. */
|
|
53
57
|
minWidth: number;
|
|
58
|
+
/**
|
|
59
|
+
* Width it starts at until the user picks one (a first launch), and the
|
|
60
|
+
* width `expand` takes it back to from its minimum. As much of it as fits:
|
|
61
|
+
* with no room the panel stays narrower. Default `minWidth`.
|
|
62
|
+
*/
|
|
63
|
+
defaultWidth?: number;
|
|
54
64
|
/** Narrowest the content may get before the panel takes over. Default 400. */
|
|
55
65
|
minContentWidth?: number;
|
|
56
66
|
/** localStorage key for the chosen width. Without it the width lasts the session. */
|
|
@@ -75,6 +85,11 @@ export interface AppLayoutSidePanelConfig {
|
|
|
75
85
|
|
|
76
86
|
/** Inset of the docked card from the window edge and the header. */
|
|
77
87
|
export const SIDE_PANEL_INSET = 16;
|
|
88
|
+
/**
|
|
89
|
+
* The docked card's 1px border on both sides: its body is this much narrower
|
|
90
|
+
* than the panel's width. Add it to a content width to size the panel by it.
|
|
91
|
+
*/
|
|
92
|
+
export const SIDE_PANEL_FRAME_WIDTH = 2;
|
|
78
93
|
const MOBILE_QUERY = `not all and ${breakpoints.md}`;
|
|
79
94
|
const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)';
|
|
80
95
|
/** Length of the docked <-> full morph; keep in step with `duration-300` below. */
|
|
@@ -228,6 +243,8 @@ export interface AppLayoutSidePanelState extends SidePanelLayout {
|
|
|
228
243
|
endMorph: () => void;
|
|
229
244
|
collapsesTo: AppLayoutSidePanelRenderState['collapsesTo'];
|
|
230
245
|
collapse: () => void;
|
|
246
|
+
canExpand: boolean;
|
|
247
|
+
expand: () => void;
|
|
231
248
|
toggle: () => void;
|
|
232
249
|
close: () => void;
|
|
233
250
|
resize: (next: number) => void;
|
|
@@ -256,6 +273,7 @@ export function useAppLayoutSidePanel(
|
|
|
256
273
|
const minContentWidth = config?.minContentWidth ?? 400;
|
|
257
274
|
const collapsed = config?.collapsed ?? false;
|
|
258
275
|
const minimum: SidePanelSize = { width: minWidth, expanded: false };
|
|
276
|
+
const defaultWidth = Math.max(minWidth, config?.defaultWidth ?? minWidth);
|
|
259
277
|
|
|
260
278
|
const [rowWidth, setRowWidth] = useState(0);
|
|
261
279
|
useIsomorphicLayoutEffect(() => {
|
|
@@ -271,7 +289,7 @@ export function useAppLayoutSidePanel(
|
|
|
271
289
|
const reduceMotion = useMediaQuery(REDUCED_MOTION_QUERY) === true;
|
|
272
290
|
const [morph, setMorph] = useState<SidePanelMorph | null>(null);
|
|
273
291
|
const [liveWidth, setLiveWidth] = useState<number | null>(null);
|
|
274
|
-
const [storedSize, setStoredSize] = useStoredSize(config?.storageKey,
|
|
292
|
+
const [storedSize, setStoredSize] = useStoredSize(config?.storageKey, { width: defaultWidth, expanded: false });
|
|
275
293
|
const [ownIsOpen, setOwnIsOpen] = useState(false);
|
|
276
294
|
const isOpen = config?.open ?? ownIsOpen;
|
|
277
295
|
const onOpenChange = config?.onOpenChange;
|
|
@@ -340,6 +358,8 @@ export function useAppLayoutSidePanel(
|
|
|
340
358
|
let collapsesTo: AppLayoutSidePanelRenderState['collapsesTo'] = null;
|
|
341
359
|
if (layout.mode === 'full' && layout.canDock) collapsesTo = layout.dockedWidth > minWidth ? 'column' : 'minimum';
|
|
342
360
|
else if (layout.mode === 'docked' && layout.width > minWidth) collapsesTo = 'minimum';
|
|
361
|
+
const canExpand =
|
|
362
|
+
layout.mode === 'docked' && layout.width <= minWidth && defaultWidth > minWidth && layout.maxDockedWidth > minWidth;
|
|
343
363
|
|
|
344
364
|
return {
|
|
345
365
|
...layout,
|
|
@@ -362,6 +382,12 @@ export function useAppLayoutSidePanel(
|
|
|
362
382
|
setSize({ width: minWidth, expanded: false });
|
|
363
383
|
}
|
|
364
384
|
},
|
|
385
|
+
canExpand,
|
|
386
|
+
expand: () => {
|
|
387
|
+
if (!canExpand) return;
|
|
388
|
+
animate('narrow');
|
|
389
|
+
dock(defaultWidth);
|
|
390
|
+
},
|
|
365
391
|
toggle: () => setIsOpen(!isOpen),
|
|
366
392
|
close: () => setIsOpen(false),
|
|
367
393
|
resize,
|
|
@@ -586,6 +612,8 @@ export function AppLayoutSidePanel({ config, state }: AppLayoutSidePanelProps) {
|
|
|
586
612
|
close,
|
|
587
613
|
collapsesTo: state.collapsesTo,
|
|
588
614
|
collapse: state.collapse,
|
|
615
|
+
canExpand: state.canExpand,
|
|
616
|
+
expand: state.expand,
|
|
589
617
|
})}
|
|
590
618
|
</div>
|
|
591
619
|
</div>
|
|
@@ -154,6 +154,19 @@ body[data-scroll-locked][data-scroll-locked] {
|
|
|
154
154
|
transform-origin: left top;
|
|
155
155
|
transform: scale(1);
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
/* The chat composer (chat-input.tsx) is a contenteditable div, not a
|
|
159
|
+
textarea, and WebKit's focus auto-zoom keys off its font size the same
|
|
160
|
+
way. Keyed on its own data-editor rather than [contenteditable]: a global
|
|
161
|
+
match would also resize consumers' editors (CodeMirror's .cm-content) and
|
|
162
|
+
drop the composer back to 14px while disabled. Its placeholder and hover
|
|
163
|
+
preview overlay it as siblings, so they are pinned with it or the text
|
|
164
|
+
would change size on the first keystroke. Mention chips stay small only
|
|
165
|
+
because Tag sets its own text-h5. */
|
|
166
|
+
[data-editor],
|
|
167
|
+
[data-editor-ghost] {
|
|
168
|
+
font-size: 16px !important;
|
|
169
|
+
}
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
/* Shadcn/ui HSL CSS variables for Tailwind compatibility */
|