mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-10-19 01:50:51 -05:00
fix(craft): animate the welcome composer into the conversation (#15726)
This commit is contained in:
1 parent
485c3f28ab
commit
9f489a1110
3 files changed
+54
-30
No files matched your search
@@ -51,6 +51,9 @@ over; the entire agentic loop runs inside the sandbox's `opencode serve`
|
||||
process, and the backend's job is to start it, stream its events, persist
|
||||
them, and know when to stop.
|
||||
|
||||
The composer moves from the welcome position to the conversation footer with
|
||||
a shared layout animation. Reduced-motion users get an immediate transition.
|
||||
|
||||
The user can interrupt a running turn at any time. Partial output stays
|
||||
visible and stays saved; sending a new message does not erase what was
|
||||
already produced. When the agent is about to do something that needs
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { motion, useReducedMotion } from "motion/react";
|
||||
import { BuildFile } from "@/app/craft/contexts/UploadFilesContext";
|
||||
import { useVideoBackgroundToggleClick } from "@/app/craft/components/video-background/useVideoBackgroundToggleClick";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
@@ -39,6 +40,7 @@ export default function BuildWelcome({
|
||||
sandboxInitializing = false,
|
||||
}: BuildWelcomeProps) {
|
||||
const t = useTranslations("craft.welcome");
|
||||
const reduceMotion: boolean | null = useReducedMotion();
|
||||
const inputBarRef = useRef<CraftInputBarHandle>(null);
|
||||
const [selectedModel, setSelectedModel] = useState<BuildLlmSelection | null>(
|
||||
null
|
||||
@@ -103,7 +105,11 @@ export default function BuildWelcome({
|
||||
</div>
|
||||
|
||||
<div className="row-start-2 w-full flex flex-col items-center">
|
||||
<div className="w-full max-w-(--app-page-main-content-width)">
|
||||
<motion.div
|
||||
layoutId="craft-composer"
|
||||
transition={{ duration: reduceMotion ? 0 : 0.25, ease: "easeOut" }}
|
||||
className="w-full max-w-(--app-page-main-content-width)"
|
||||
>
|
||||
<CraftInputBar
|
||||
ref={inputBarRef}
|
||||
onSubmit={(message, files) =>
|
||||
@@ -114,7 +120,7 @@ export default function BuildWelcome({
|
||||
sandboxInitializing={sandboxInitializing}
|
||||
disabled={!hasAnyProvider}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<div className="row-start-3 min-h-0 w-full flex flex-col items-center">
|
||||
|
||||
@@ -61,7 +61,12 @@ import { Button, Tooltip } from "@opal/components";
|
||||
import { useBuildContext } from "@/app/craft/contexts/BuildContext";
|
||||
import useScreenSize from "@/hooks/useScreenSize";
|
||||
import { cn } from "@opal/utils";
|
||||
import { motion, AnimatePresence, useReducedMotion } from "motion/react";
|
||||
import {
|
||||
motion,
|
||||
AnimatePresence,
|
||||
LayoutGroup,
|
||||
useReducedMotion,
|
||||
} from "motion/react";
|
||||
|
||||
interface BuildChatPanelProps {
|
||||
/** Session ID from URL - used to prevent welcome flash while loading */
|
||||
@@ -651,7 +656,7 @@ export default function BuildChatPanel({
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<LayoutGroup id="craft-chat">
|
||||
{/* Content wrapper - shrinks when output panel opens. Wrapped in a
|
||||
dropzone so files can be dropped anywhere in the chat area. */}
|
||||
{/* noPaste: the input bar already uploads pasted files itself. */}
|
||||
@@ -834,37 +839,47 @@ export default function BuildChatPanel({
|
||||
)}
|
||||
{/* The composer stays in view for subagents (layout consistency)
|
||||
but is disabled — replying to subagents is not supported. */}
|
||||
<CraftInputBar
|
||||
ref={inputBarRef}
|
||||
onSubmit={handleSubmit}
|
||||
isRunning={displayIsRunning}
|
||||
isInterrupting={isInterrupting}
|
||||
onInterrupt={
|
||||
hasInterruptibleTurn && !scheduledRunInFlight
|
||||
? handleInterrupt
|
||||
: undefined
|
||||
}
|
||||
disabled={
|
||||
isViewingSubagent || scheduledRunInFlight || !hasProvider
|
||||
}
|
||||
placeholder={
|
||||
isViewingSubagent
|
||||
? t("input.subagentPlaceholder")
|
||||
: scheduledRunInFlight
|
||||
? t("input.scheduledRunPlaceholder")
|
||||
: t("input.continuePlaceholder")
|
||||
}
|
||||
queuedMessages={queuedMessages}
|
||||
onQueueMessage={handleQueueMessage}
|
||||
onRemoveQueuedMessage={handleRemoveQueuedMessage}
|
||||
contextUsage={contextUsage}
|
||||
/>
|
||||
<motion.div
|
||||
layoutId="craft-composer"
|
||||
transition={{
|
||||
duration: reduceMotion ? 0 : 0.25,
|
||||
ease: "easeOut",
|
||||
}}
|
||||
>
|
||||
<CraftInputBar
|
||||
ref={inputBarRef}
|
||||
onSubmit={handleSubmit}
|
||||
isRunning={displayIsRunning}
|
||||
isInterrupting={isInterrupting}
|
||||
onInterrupt={
|
||||
hasInterruptibleTurn && !scheduledRunInFlight
|
||||
? handleInterrupt
|
||||
: undefined
|
||||
}
|
||||
disabled={
|
||||
isViewingSubagent ||
|
||||
scheduledRunInFlight ||
|
||||
!hasProvider
|
||||
}
|
||||
placeholder={
|
||||
isViewingSubagent
|
||||
? t("input.subagentPlaceholder")
|
||||
: scheduledRunInFlight
|
||||
? t("input.scheduledRunPlaceholder")
|
||||
: t("input.continuePlaceholder")
|
||||
}
|
||||
queuedMessages={queuedMessages}
|
||||
onQueueMessage={handleQueueMessage}
|
||||
onRemoveQueuedMessage={handleRemoveQueuedMessage}
|
||||
contextUsage={contextUsage}
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
</div>
|
||||
</LayoutGroup>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user