Fix ArrowDown import
This commit is contained in:
@@ -71,6 +71,7 @@ function classifyTurnIntent(message: string): TurnIntent {
|
||||
|
||||
// High-agency directives
|
||||
if (
|
||||
/^(yes|yep|yeah|yup|sure|ok|okay|kk|k|please|fix it)\b/.test(m) ||
|
||||
/(keep going|continue|build it|do it|go ahead|proceed|autonomous)/.test(m)
|
||||
)
|
||||
return "autonomous";
|
||||
@@ -134,7 +135,7 @@ function classifyTurnIntent(message: string): TurnIntent {
|
||||
m,
|
||||
) ||
|
||||
// Acknowledgements / pleasantries
|
||||
/^(thanks|thank you|ty|ok|okay|kk|k|yes|yep|yeah|yup|no|nope|nah|sure|cool|nice|great|awesome|perfect|got it|sounds good|will do|nvm|never mind)\b/.test(
|
||||
/^(thanks|thank you|ty|no|nope|nah|cool|nice|great|awesome|perfect|got it|sounds good|will do|nvm|never mind)\b/.test(
|
||||
m,
|
||||
) ||
|
||||
/(what'?s up|how are you|how'?s it going|good to see you)/.test(m) ||
|
||||
|
||||
@@ -640,7 +640,7 @@ function Timeline({ entries, isActiveStream }: { entries: TimelineEntry[], isAct
|
||||
{items.map((item, i) => {
|
||||
const isLast = i === items.length - 1;
|
||||
if (item.kind === "thought") {
|
||||
return <TimelineText key={i} text={item.text} isStreaming={isActiveStream && isLast} />;
|
||||
return <TimelineThought key={i} text={item.text} isStreaming={isActiveStream && isLast} />;
|
||||
}
|
||||
if (item.kind === "text") {
|
||||
return <TimelineText key={i} text={item.text} isStreaming={isActiveStream && isLast} />;
|
||||
@@ -665,6 +665,92 @@ function Timeline({ entries, isActiveStream }: { entries: TimelineEntry[], isAct
|
||||
* bubble so each round of multi-tool-loop output reads as a discrete
|
||||
* step instead of concatenating into a wall of text.
|
||||
*/
|
||||
|
||||
function TimelineThought({ text, isStreaming }: { text: string; isStreaming?: boolean }) {
|
||||
const [expanded, setExpanded] = React.useState(true);
|
||||
const textLenRef = React.useRef(text.length);
|
||||
|
||||
React.useEffect(() => {
|
||||
// If not streaming, auto-collapse after a short delay so the user isn't stuck with huge thinking blocks
|
||||
if (!isStreaming) {
|
||||
const t = setTimeout(() => setExpanded(false), 500);
|
||||
return () => clearTimeout(t);
|
||||
}
|
||||
}, [isStreaming]);
|
||||
|
||||
const proseWrap: React.CSSProperties = {
|
||||
overflowWrap: "anywhere",
|
||||
wordBreak: "break-word",
|
||||
minWidth: 0,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
margin: "12px 0",
|
||||
fontFamily: "var(--font-inter),ui-sans-serif,sans-serif",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
background: "#f4f4f5",
|
||||
border: "1px solid #e4e4e7",
|
||||
borderRadius: "999px",
|
||||
padding: "4px 10px",
|
||||
cursor: "pointer",
|
||||
fontSize: "0.75rem",
|
||||
color: "#52525b",
|
||||
fontWeight: 500,
|
||||
transition: "all 0.2s ease",
|
||||
boxShadow: "0 1px 2px rgba(0,0,0,0.02)",
|
||||
}}
|
||||
>
|
||||
<Sparkles
|
||||
size={14}
|
||||
className={isStreaming ? "animate-pulse" : ""}
|
||||
style={{ color: "#8b5cf6" }}
|
||||
/>
|
||||
<span className={isStreaming ? "animate-pulse" : ""} style={{ transition: "opacity 0.2s ease" }}>
|
||||
{isStreaming ? "Thinking..." : "Thought Process"}
|
||||
</span>
|
||||
<ChevronRight
|
||||
size={14}
|
||||
style={{
|
||||
transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
|
||||
transition: "transform 0.2s ease",
|
||||
color: "#a1a1aa"
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
{expanded && (
|
||||
<div
|
||||
style={{
|
||||
marginTop: 8,
|
||||
padding: "10px 14px",
|
||||
borderLeft: "2px solid #c4b5fd",
|
||||
fontSize: "0.85rem",
|
||||
color: "#52525b",
|
||||
background: "linear-gradient(to right, #f5f3ff 0%, transparent 100%)",
|
||||
borderRadius: "0 8px 8px 0",
|
||||
...proseWrap,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={proseWrap}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderMarkdown(stripRawToolLogs(text)) + (isStreaming ? `<span class="animate-pulse" style="display:inline-block; width:6px; height:13px; background-color:#9ca3af; vertical-align:-1px; margin-left:2px; border-radius:1px;"></span>` : ""),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TimelineText({ text, isStreaming }: { text: string; isStreaming?: boolean }) {
|
||||
const proseWrap: React.CSSProperties = {
|
||||
overflowWrap: "anywhere",
|
||||
@@ -1060,6 +1146,7 @@ export function ChatPanel({
|
||||
.catch(() => {});
|
||||
}, [projectId, workspace, status]);
|
||||
const [sending, setSending] = useState(false);
|
||||
const [showScrollButton, setShowScrollButton] = useState(false);
|
||||
const [currentPhaseLabel, setCurrentPhaseLabel] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
@@ -1890,6 +1977,11 @@ export function ChatPanel({
|
||||
|
||||
{/* Messages */}
|
||||
<div
|
||||
onScroll={(e) => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
||||
const distanceToBottom = scrollHeight - scrollTop - clientHeight;
|
||||
setShowScrollButton(distanceToBottom > 150);
|
||||
}}
|
||||
style={{
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
@@ -2119,6 +2211,35 @@ export function ChatPanel({
|
||||
|
||||
|
||||
|
||||
{/* Scroll to bottom button */}
|
||||
{showScrollButton && (
|
||||
<button
|
||||
onClick={scrollToBottom}
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: "calc(100% + 12px)",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
background: "#ffffff",
|
||||
border: "1px solid #e4e4e7",
|
||||
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)",
|
||||
borderRadius: "50%",
|
||||
width: 32,
|
||||
height: 32,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
zIndex: 50,
|
||||
color: "#52525b",
|
||||
transition: "all 0.2s ease",
|
||||
}}
|
||||
aria-label="Scroll to bottom"
|
||||
>
|
||||
<ArrowDown size={16} />
|
||||
</button>
|
||||
)}
|
||||
|
||||
<ProjectPreviewChatInputWrap unifiedShell={unifiedProjectShell}>
|
||||
{(selectToggle) => (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user