Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions apps/web/components/chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,23 @@ export function ChatSidebar({
)

const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
if (e.key === "Enter" && !e.shiftKey && !isMobile) {
e.preventDefault()
handleSend()
}
}

// When the user stops generation before any assistant response arrives,
// remove the dangling user message so it isn't duplicated on the next send.
const handleStop = useCallback(() => {
stop()
setMessages((prev) => {
const last = prev[prev.length - 1]
if (last?.role === "user") return prev.slice(0, -1)
return prev
})
}, [stop, setMessages])

const handleCopyMessage = useCallback((messageId: string, text: string) => {
analytics.chatMessageCopied({ message_id: messageId })
navigator.clipboard.writeText(text)
Expand Down Expand Up @@ -1137,7 +1148,7 @@ export function ChatSidebar({
value={input}
onChange={(e) => setInput(e.target.value)}
onSend={handleSend}
onStop={stop}
onStop={handleStop}
onKeyDown={handleKeyDown}
isResponding={isResponding}
activeStatus={
Expand Down
40 changes: 37 additions & 3 deletions apps/web/components/highlights-card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
"use client"

import { useState, useCallback, useRef, useEffect } from "react"
import {
useState,
useCallback,
useRef,
useEffect,
useLayoutEffect,
} from "react"
import { cn } from "@lib/utils"
import { dmSansClassName } from "@/lib/fonts"
import {
Expand Down Expand Up @@ -75,6 +81,9 @@ export function HighlightsCard({
const [activeIndex, setActiveIndex] = useState(0)
const [isReplyOpen, setIsReplyOpen] = useState(false)
const [replyText, setReplyText] = useState("")
const [isExpanded, setIsExpanded] = useState(false)
const [isClamped, setIsClamped] = useState(false)
const contentRef = useRef<HTMLDivElement>(null)
const replyInputRef = useRef<HTMLInputElement>(null)

const currentItem = items[activeIndex]
Expand All @@ -87,18 +96,28 @@ export function HighlightsCard({
useEffect(() => {
setIsReplyOpen(false)
setReplyText("")
setIsExpanded(false)
}, [items])

// biome-ignore lint/correctness/useExhaustiveDependencies: re-run when item or expansion changes to detect clamping
useLayoutEffect(() => {
const el = contentRef.current
if (!el) return
setIsClamped(el.scrollHeight > el.clientHeight)
}, [currentItem, isExpanded])

const handlePrev = useCallback(() => {
setActiveIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1))
setIsReplyOpen(false)
setReplyText("")
setIsExpanded(false)
}, [items.length])

const handleNext = useCallback(() => {
setActiveIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0))
setIsReplyOpen(false)
setReplyText("")
setIsExpanded(false)
}, [items.length])

const handleChatClick = useCallback(() => {
Expand Down Expand Up @@ -259,12 +278,27 @@ export function HighlightsCard({
</div>

<div id="highlights-body" className="flex flex-col gap-1.5">
<p className="text-[12px] font-semibold text-fg-primary leading-tight truncate">
<p className="text-[12px] font-semibold text-fg-primary leading-tight">
{currentItem.title}
</p>
<div className="text-[12px] text-fg-primary leading-normal line-clamp-5">
<div
ref={contentRef}
className={cn(
"text-[12px] text-fg-primary leading-normal",
!isExpanded && "line-clamp-5",
)}
>
{renderContent(currentItem.content, currentItem.format)}
</div>
{(isClamped || isExpanded) && (
<button
type="button"
onClick={() => setIsExpanded((v) => !v)}
className="self-start text-[11px] text-fg-subtle hover:text-fg-primary transition-colors cursor-pointer"
>
{isExpanded ? "Show less" : "Show more"}
</button>
)}
</div>

{isReplyOpen && (
Expand Down
Loading