mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-23 08:25:57 +00:00
[codex] fix follow-up suggestions layout (#2836)
* fix follow-up suggestions layout * fix agent chat welcome layout transition --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { BotIcon, PlusSquare } from "lucide-react";
|
import { BotIcon, PlusSquare } from "lucide-react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -14,7 +14,6 @@ import { InputBox } from "@/components/workspace/input-box";
|
|||||||
import {
|
import {
|
||||||
MessageList,
|
MessageList,
|
||||||
MESSAGE_LIST_DEFAULT_PADDING_BOTTOM,
|
MESSAGE_LIST_DEFAULT_PADDING_BOTTOM,
|
||||||
MESSAGE_LIST_FOLLOWUPS_EXTRA_PADDING_BOTTOM,
|
|
||||||
} from "@/components/workspace/messages";
|
} from "@/components/workspace/messages";
|
||||||
import { ThreadContext } from "@/components/workspace/messages/context";
|
import { ThreadContext } from "@/components/workspace/messages/context";
|
||||||
import { ThreadTitle } from "@/components/workspace/thread-title";
|
import { ThreadTitle } from "@/components/workspace/thread-title";
|
||||||
@@ -34,7 +33,6 @@ import { cn } from "@/lib/utils";
|
|||||||
|
|
||||||
export default function AgentChatPage() {
|
export default function AgentChatPage() {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [showFollowups, setShowFollowups] = useState(false);
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { agent_name } = useParams<{
|
const { agent_name } = useParams<{
|
||||||
@@ -45,6 +43,10 @@ export default function AgentChatPage() {
|
|||||||
|
|
||||||
const { threadId, setThreadId, isNewThread, setIsNewThread, isMock } =
|
const { threadId, setThreadId, isNewThread, setIsNewThread, isMock } =
|
||||||
useThreadChat();
|
useThreadChat();
|
||||||
|
// `isNewThread` gates history/token-usage fetches until the backend creates
|
||||||
|
// the thread. `isWelcomeMode` controls only the centered welcome layout, so
|
||||||
|
// it can flip immediately on submit without triggering eager history loads.
|
||||||
|
const [isWelcomeMode, setIsWelcomeMode] = useState(isNewThread);
|
||||||
const [settings, setSettings] = useThreadSettings(threadId);
|
const [settings, setSettings] = useThreadSettings(threadId);
|
||||||
const [localSettings, setLocalSettings] = useLocalSettings();
|
const [localSettings, setLocalSettings] = useLocalSettings();
|
||||||
const { tokenUsageEnabled } = useModels();
|
const { tokenUsageEnabled } = useModels();
|
||||||
@@ -55,6 +57,11 @@ export default function AgentChatPage() {
|
|||||||
const backendTokenUsage = threadTokenUsageToTokenUsage(threadTokenUsage.data);
|
const backendTokenUsage = threadTokenUsageToTokenUsage(threadTokenUsage.data);
|
||||||
|
|
||||||
const { showNotification } = useNotification();
|
const { showNotification } = useNotification();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsWelcomeMode(isNewThread);
|
||||||
|
}, [isNewThread]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
thread,
|
thread,
|
||||||
pendingUsageMessages,
|
pendingUsageMessages,
|
||||||
@@ -66,6 +73,9 @@ export default function AgentChatPage() {
|
|||||||
threadId: isNewThread ? undefined : threadId,
|
threadId: isNewThread ? undefined : threadId,
|
||||||
context: { ...settings.context, agent_name: agent_name },
|
context: { ...settings.context, agent_name: agent_name },
|
||||||
isMock,
|
isMock,
|
||||||
|
onSend: () => {
|
||||||
|
setIsWelcomeMode(false);
|
||||||
|
},
|
||||||
onStart: (createdThreadId) => {
|
onStart: (createdThreadId) => {
|
||||||
setThreadId(createdThreadId);
|
setThreadId(createdThreadId);
|
||||||
setIsNewThread(false);
|
setIsNewThread(false);
|
||||||
@@ -105,13 +115,10 @@ export default function AgentChatPage() {
|
|||||||
await thread.stop();
|
await thread.stop();
|
||||||
}, [thread]);
|
}, [thread]);
|
||||||
|
|
||||||
const messageListPaddingBottom = showFollowups
|
|
||||||
? MESSAGE_LIST_DEFAULT_PADDING_BOTTOM +
|
|
||||||
MESSAGE_LIST_FOLLOWUPS_EXTRA_PADDING_BOTTOM
|
|
||||||
: undefined;
|
|
||||||
const tokenUsageInlineMode = tokenUsageEnabled
|
const tokenUsageInlineMode = tokenUsageEnabled
|
||||||
? localSettings.tokenUsage.inlineMode
|
? localSettings.tokenUsage.inlineMode
|
||||||
: "off";
|
: "off";
|
||||||
|
const hasTodos = (thread.values.todos?.length ?? 0) > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThreadContext.Provider value={{ thread }}>
|
<ThreadContext.Provider value={{ thread }}>
|
||||||
@@ -120,7 +127,7 @@ export default function AgentChatPage() {
|
|||||||
<header
|
<header
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute top-0 right-0 left-0 z-30 flex h-12 shrink-0 items-center gap-2 px-4",
|
"absolute top-0 right-0 left-0 z-30 flex h-12 shrink-0 items-center gap-2 px-4",
|
||||||
isNewThread
|
isWelcomeMode
|
||||||
? "bg-background/0 backdrop-blur-none"
|
? "bg-background/0 backdrop-blur-none"
|
||||||
: "bg-background/80 shadow-xs backdrop-blur",
|
: "bg-background/80 shadow-xs backdrop-blur",
|
||||||
)}
|
)}
|
||||||
@@ -165,12 +172,12 @@ export default function AgentChatPage() {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="flex min-h-0 max-w-full grow flex-col">
|
<main className="flex min-h-0 max-w-full grow flex-col">
|
||||||
<div className="flex size-full justify-center">
|
<div className="flex min-h-0 flex-1 justify-center">
|
||||||
<MessageList
|
<MessageList
|
||||||
className={cn("size-full", !isNewThread && "pt-10")}
|
className={cn("size-full", !isWelcomeMode && "pt-10")}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
thread={thread}
|
thread={thread}
|
||||||
paddingBottom={messageListPaddingBottom}
|
paddingBottom={MESSAGE_LIST_DEFAULT_PADDING_BOTTOM}
|
||||||
hasMoreHistory={hasMoreHistory}
|
hasMoreHistory={hasMoreHistory}
|
||||||
loadMoreHistory={loadMoreHistory}
|
loadMoreHistory={loadMoreHistory}
|
||||||
isHistoryLoading={isHistoryLoading}
|
isHistoryLoading={isHistoryLoading}
|
||||||
@@ -178,33 +185,51 @@ export default function AgentChatPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute right-0 bottom-0 left-0 z-30 flex justify-center px-4">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"right-0 bottom-0 left-0 z-30 flex justify-center px-4",
|
||||||
|
isWelcomeMode ? "absolute" : "relative shrink-0 pb-4",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative w-full",
|
"relative w-full",
|
||||||
isNewThread && "-translate-y-[calc(50vh-96px)]",
|
isWelcomeMode && "-translate-y-[calc(50vh-96px)]",
|
||||||
isNewThread
|
isWelcomeMode
|
||||||
? "max-w-(--container-width-sm)"
|
? "max-w-(--container-width-sm)"
|
||||||
: "max-w-(--container-width-md)",
|
: "max-w-(--container-width-md)",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="absolute -top-4 right-0 left-0 z-0">
|
{hasTodos && (
|
||||||
<div className="absolute right-0 bottom-0 left-0">
|
<div
|
||||||
<TodoList
|
className={cn(
|
||||||
className="bg-background/5"
|
"right-0 left-0 z-0",
|
||||||
todos={thread.values.todos ?? []}
|
isWelcomeMode ? "absolute -top-4" : "relative",
|
||||||
hidden={
|
)}
|
||||||
!thread.values.todos || thread.values.todos.length === 0
|
>
|
||||||
}
|
<div
|
||||||
/>
|
className={cn(
|
||||||
|
"right-0 bottom-0 left-0",
|
||||||
|
isWelcomeMode ? "absolute" : "relative",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<TodoList
|
||||||
|
className="bg-background/5"
|
||||||
|
todos={thread.values.todos ?? []}
|
||||||
|
hidden={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
<InputBox
|
<InputBox
|
||||||
className={cn("bg-background/5 w-full -translate-y-4")}
|
className={cn(
|
||||||
isWelcomeMode={isNewThread}
|
"bg-background/5 w-full",
|
||||||
|
isWelcomeMode && "-translate-y-4",
|
||||||
|
)}
|
||||||
|
isWelcomeMode={isWelcomeMode}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
autoFocus={isNewThread}
|
autoFocus={isWelcomeMode}
|
||||||
status={
|
status={
|
||||||
thread.error
|
thread.error
|
||||||
? "error"
|
? "error"
|
||||||
@@ -214,13 +239,12 @@ export default function AgentChatPage() {
|
|||||||
}
|
}
|
||||||
context={settings.context}
|
context={settings.context}
|
||||||
extraHeader={
|
extraHeader={
|
||||||
isNewThread && (
|
isWelcomeMode && (
|
||||||
<AgentWelcome agent={agent} agentName={agent_name} />
|
<AgentWelcome agent={agent} agentName={agent_name} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true"}
|
disabled={env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true"}
|
||||||
onContextChange={(context) => setSettings("context", context)}
|
onContextChange={(context) => setSettings("context", context)}
|
||||||
onFollowupsVisibilityChange={setShowFollowups}
|
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onStop={handleStop}
|
onStop={handleStop}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { InputBox } from "@/components/workspace/input-box";
|
|||||||
import {
|
import {
|
||||||
MessageList,
|
MessageList,
|
||||||
MESSAGE_LIST_DEFAULT_PADDING_BOTTOM,
|
MESSAGE_LIST_DEFAULT_PADDING_BOTTOM,
|
||||||
MESSAGE_LIST_FOLLOWUPS_EXTRA_PADDING_BOTTOM,
|
|
||||||
} from "@/components/workspace/messages";
|
} from "@/components/workspace/messages";
|
||||||
import { ThreadContext } from "@/components/workspace/messages/context";
|
import { ThreadContext } from "@/components/workspace/messages/context";
|
||||||
import { ThreadTitle } from "@/components/workspace/thread-title";
|
import { ThreadTitle } from "@/components/workspace/thread-title";
|
||||||
@@ -33,7 +32,6 @@ import { cn } from "@/lib/utils";
|
|||||||
|
|
||||||
export default function ChatPage() {
|
export default function ChatPage() {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [showFollowups, setShowFollowups] = useState(false);
|
|
||||||
const { threadId, setThreadId, isNewThread, setIsNewThread, isMock } =
|
const { threadId, setThreadId, isNewThread, setIsNewThread, isMock } =
|
||||||
useThreadChat();
|
useThreadChat();
|
||||||
// `isNewThread` tracks whether the backend has the thread yet — gates the
|
// `isNewThread` tracks whether the backend has the thread yet — gates the
|
||||||
@@ -119,13 +117,10 @@ export default function ChatPage() {
|
|||||||
await thread.stop();
|
await thread.stop();
|
||||||
}, [thread]);
|
}, [thread]);
|
||||||
|
|
||||||
const messageListPaddingBottom = showFollowups
|
|
||||||
? MESSAGE_LIST_DEFAULT_PADDING_BOTTOM +
|
|
||||||
MESSAGE_LIST_FOLLOWUPS_EXTRA_PADDING_BOTTOM
|
|
||||||
: undefined;
|
|
||||||
const tokenUsageInlineMode = tokenUsageEnabled
|
const tokenUsageInlineMode = tokenUsageEnabled
|
||||||
? localSettings.tokenUsage.inlineMode
|
? localSettings.tokenUsage.inlineMode
|
||||||
: "off";
|
: "off";
|
||||||
|
const hasTodos = (thread.values.todos?.length ?? 0) > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThreadContext.Provider value={{ thread, isMock }}>
|
<ThreadContext.Provider value={{ thread, isMock }}>
|
||||||
@@ -159,19 +154,24 @@ export default function ChatPage() {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main className="flex min-h-0 max-w-full grow flex-col">
|
<main className="flex min-h-0 max-w-full grow flex-col">
|
||||||
<div className="flex size-full justify-center">
|
<div className="flex min-h-0 flex-1 justify-center">
|
||||||
<MessageList
|
<MessageList
|
||||||
className={cn("size-full", !isWelcomeMode && "pt-10")}
|
className={cn("size-full", !isWelcomeMode && "pt-10")}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
thread={thread}
|
thread={thread}
|
||||||
paddingBottom={messageListPaddingBottom}
|
paddingBottom={MESSAGE_LIST_DEFAULT_PADDING_BOTTOM}
|
||||||
hasMoreHistory={hasMoreHistory}
|
hasMoreHistory={hasMoreHistory}
|
||||||
loadMoreHistory={loadMoreHistory}
|
loadMoreHistory={loadMoreHistory}
|
||||||
isHistoryLoading={isHistoryLoading}
|
isHistoryLoading={isHistoryLoading}
|
||||||
tokenUsageInlineMode={tokenUsageInlineMode}
|
tokenUsageInlineMode={tokenUsageInlineMode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute right-0 bottom-0 left-0 z-30 flex justify-center px-4">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"right-0 bottom-0 left-0 z-30 flex justify-center px-4",
|
||||||
|
isWelcomeMode ? "absolute" : "relative shrink-0 pb-4",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative w-full",
|
"relative w-full",
|
||||||
@@ -181,20 +181,33 @@ export default function ChatPage() {
|
|||||||
: "max-w-(--container-width-md)",
|
: "max-w-(--container-width-md)",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="absolute -top-4 right-0 left-0 z-0">
|
{hasTodos && (
|
||||||
<div className="absolute right-0 bottom-0 left-0">
|
<div
|
||||||
<TodoList
|
className={cn(
|
||||||
className="bg-background/5"
|
"right-0 left-0 z-0",
|
||||||
todos={thread.values.todos ?? []}
|
isWelcomeMode ? "absolute -top-4" : "relative",
|
||||||
hidden={
|
)}
|
||||||
!thread.values.todos || thread.values.todos.length === 0
|
>
|
||||||
}
|
<div
|
||||||
/>
|
className={cn(
|
||||||
|
"right-0 bottom-0 left-0",
|
||||||
|
isWelcomeMode ? "absolute" : "relative",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<TodoList
|
||||||
|
className="bg-background/5"
|
||||||
|
todos={thread.values.todos ?? []}
|
||||||
|
hidden={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
{mountedRef.current ? (
|
{mountedRef.current ? (
|
||||||
<InputBox
|
<InputBox
|
||||||
className={cn("bg-background/5 w-full -translate-y-4")}
|
className={cn(
|
||||||
|
"bg-background/5 w-full",
|
||||||
|
isWelcomeMode && "-translate-y-4",
|
||||||
|
)}
|
||||||
isWelcomeMode={isWelcomeMode}
|
isWelcomeMode={isWelcomeMode}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
autoFocus={isWelcomeMode}
|
autoFocus={isWelcomeMode}
|
||||||
@@ -216,7 +229,6 @@ export default function ChatPage() {
|
|||||||
onContextChange={(context) =>
|
onContextChange={(context) =>
|
||||||
setSettings("context", context)
|
setSettings("context", context)
|
||||||
}
|
}
|
||||||
onFollowupsVisibilityChange={setShowFollowups}
|
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onStop={handleStop}
|
onStop={handleStop}
|
||||||
/>
|
/>
|
||||||
@@ -224,7 +236,8 @@ export default function ChatPage() {
|
|||||||
<div
|
<div
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background/5 h-32 w-full -translate-y-4 rounded-2xl",
|
"bg-background/5 h-32 w-full rounded-2xl",
|
||||||
|
isWelcomeMode && "-translate-y-4",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ export function InputBox({
|
|||||||
threadId,
|
threadId,
|
||||||
initialValue,
|
initialValue,
|
||||||
onContextChange,
|
onContextChange,
|
||||||
onFollowupsVisibilityChange,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onStop,
|
onStop,
|
||||||
...props
|
...props
|
||||||
@@ -143,7 +142,6 @@ export function InputBox({
|
|||||||
reasoning_effort?: "minimal" | "low" | "medium" | "high";
|
reasoning_effort?: "minimal" | "low" | "medium" | "high";
|
||||||
},
|
},
|
||||||
) => void;
|
) => void;
|
||||||
onFollowupsVisibilityChange?: (visible: boolean) => void;
|
|
||||||
onSubmit?: (message: PromptInputMessage) => void;
|
onSubmit?: (message: PromptInputMessage) => void;
|
||||||
onStop?: () => void;
|
onStop?: () => void;
|
||||||
}) {
|
}) {
|
||||||
@@ -350,24 +348,10 @@ export function InputBox({
|
|||||||
!followupsHidden &&
|
!followupsHidden &&
|
||||||
(followupsLoading || followups.length > 0);
|
(followupsLoading || followups.length > 0);
|
||||||
|
|
||||||
const followupsVisibilityChangeRef = useRef(onFollowupsVisibilityChange);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
followupsVisibilityChangeRef.current = onFollowupsVisibilityChange;
|
|
||||||
}, [onFollowupsVisibilityChange]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
followupsVisibilityChangeRef.current?.(showFollowups);
|
|
||||||
}, [showFollowups]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
messagesRef.current = thread.messages;
|
messagesRef.current = thread.messages;
|
||||||
}, [thread.messages]);
|
}, [thread.messages]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => followupsVisibilityChangeRef.current?.(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const streaming = status === "streaming";
|
const streaming = status === "streaming";
|
||||||
const wasStreaming = wasStreamingRef.current;
|
const wasStreaming = wasStreamingRef.current;
|
||||||
@@ -442,26 +426,33 @@ export function InputBox({
|
|||||||
}, [context.model_name, disabled, isMock, status, threadId]);
|
}, [context.model_name, disabled, isMock, status, threadId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={promptRootRef} className="relative flex flex-col gap-4">
|
<div
|
||||||
|
ref={promptRootRef}
|
||||||
|
className={cn(
|
||||||
|
"relative flex flex-col",
|
||||||
|
isWelcomeMode ? "gap-4" : "gap-2",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{showFollowups && (
|
{showFollowups && (
|
||||||
<div className="flex items-center justify-center pb-2">
|
<div className="flex items-center justify-center pb-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{followupsLoading ? (
|
{followupsLoading ? (
|
||||||
<div className="text-muted-foreground bg-background/80 rounded-full border px-4 py-2 text-xs backdrop-blur-sm">
|
<div className="text-muted-foreground bg-background/80 rounded-full border px-4 py-1.5 text-xs backdrop-blur-sm">
|
||||||
{t.inputBox.followupLoading}
|
{t.inputBox.followupLoading}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Suggestions className="min-h-16 w-fit items-start">
|
<Suggestions className="w-fit items-center">
|
||||||
{followups.map((s) => (
|
{followups.map((s) => (
|
||||||
<Suggestion
|
<Suggestion
|
||||||
key={s}
|
key={s}
|
||||||
|
className="py-1.5"
|
||||||
suggestion={s}
|
suggestion={s}
|
||||||
onClick={() => handleFollowupClick(s)}
|
onClick={() => handleFollowupClick(s)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<Button
|
<Button
|
||||||
aria-label={t.common.close}
|
aria-label={t.common.close}
|
||||||
className="text-muted-foreground cursor-pointer rounded-full px-3 text-xs font-normal"
|
className="text-muted-foreground h-auto cursor-pointer rounded-full px-2.5 py-1.5 text-xs font-normal"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ import {
|
|||||||
import { MessageListSkeleton } from "./skeleton";
|
import { MessageListSkeleton } from "./skeleton";
|
||||||
import { SubtaskCard } from "./subtask-card";
|
import { SubtaskCard } from "./subtask-card";
|
||||||
|
|
||||||
export const MESSAGE_LIST_DEFAULT_PADDING_BOTTOM = 160;
|
export const MESSAGE_LIST_DEFAULT_PADDING_BOTTOM = 24;
|
||||||
export const MESSAGE_LIST_FOLLOWUPS_EXTRA_PADDING_BOTTOM = 80;
|
|
||||||
|
|
||||||
const LOAD_MORE_HISTORY_THROTTLE_MS = 1200;
|
const LOAD_MORE_HISTORY_THROTTLE_MS = 1200;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user