11import type { Id } from "@harness/convex-backend/convex/_generated/dataModel" ;
2+ import { useNavigate } from "@tanstack/react-router" ;
23import { ChevronDown , ChevronRight , Sparkles } from "lucide-react" ;
34import { motion } from "motion/react" ;
45import React , {
@@ -24,7 +25,7 @@ import { type DisplayMode, MessageActions } from "../message-actions";
2425import { MessageAttachments } from "../message-attachments" ;
2526import { PendingResponseIndicator } from "../pending-response-indicator" ;
2627import { RoseCurveSpinner } from "../rose-curve-spinner" ;
27- import { Avatar , AvatarFallback } from "../ui/avatar" ;
28+ import { Avatar , AvatarFallback , AvatarImage } from "../ui/avatar" ;
2829import { StreamingUsage , ThinkingBlock , ToolCallBlock } from "./message-blocks" ;
2930
3031/** Superset of stream parts and persisted Convex parts. */
@@ -258,6 +259,7 @@ export function ChatMessages({
258259 activeConversation,
259260 forkedFromConversationId,
260261 forkedFromConversationTitle,
262+ forkedFromShareToken,
261263 forkedAtMessageCount,
262264 onNavigateToConversation,
263265 isStreaming,
@@ -295,6 +297,10 @@ export function ChatMessages({
295297 } ;
296298 model ?: string ;
297299 interrupted ?: boolean ;
300+ // Author attribution snapshot for collaborator-sent messages (name +
301+ // avatar only). Absent on the owner's own messages.
302+ authorName ?: string ;
303+ authorImageUrl ?: string ;
298304 attachments ?: Array < {
299305 storageId : Id < "_storage" > ;
300306 mimeType : string ;
@@ -340,6 +346,7 @@ export function ChatMessages({
340346 | undefined ;
341347 forkedFromConversationId ?: Id < "conversations" > ;
342348 forkedFromConversationTitle ?: string ;
349+ forkedFromShareToken ?: string ;
343350 forkedAtMessageCount ?: number ;
344351 onNavigateToConversation : ( convoId : Id < "conversations" > | null ) => void ;
345352 isStreaming : boolean ;
@@ -351,6 +358,7 @@ export function ChatMessages({
351358 /** Shared view: resolve attachment URLs through the token-scoped query. */
352359 shareToken ?: string ;
353360} ) {
361+ const navigate = useNavigate ( ) ;
354362 const scrollRef = useRef < HTMLDivElement > ( null ) ;
355363 const highlightTimeoutRef = useRef < ReturnType < typeof setTimeout > > ( null ) ;
356364 // "Pinned to bottom" — when true, auto-scroll follows new content.
@@ -850,7 +858,12 @@ export function ChatMessages({
850858 : undefined
851859 }
852860 onEditPrompt = {
853- msg . role === "user" && i === lastUserMsgIdx
861+ // Editing a prompt forks-and-resends, which is owner-
862+ // centric; collaborators (shareToken set) send and
863+ // regenerate but don't rewrite the owner's prompts.
864+ msg . role === "user" &&
865+ i === lastUserMsgIdx &&
866+ ! shareToken
854867 ? ( ) => onStartEditPrompt ( msg . _id , msg . content )
855868 : undefined
856869 }
@@ -900,8 +913,16 @@ export function ChatMessages({
900913 </ div >
901914 { msg . role === "user" && (
902915 < Avatar className = "h-7 w-7 shrink-0" >
916+ { msg . authorImageUrl && (
917+ < AvatarImage
918+ src = { msg . authorImageUrl }
919+ alt = { msg . authorName ?? "User" }
920+ />
921+ ) }
903922 < AvatarFallback className = "bg-muted text-foreground text-[10px]" >
904- U
923+ { msg . authorName
924+ ? msg . authorName . charAt ( 0 ) . toUpperCase ( )
925+ : "U" }
905926 </ AvatarFallback >
906927 </ Avatar >
907928 ) }
@@ -913,11 +934,23 @@ export function ChatMessages({
913934 Branched from{ " " }
914935 < button
915936 type = "button"
916- onClick = { ( ) =>
917- onNavigateToConversation (
918- forkedFromConversationId ?? null ,
919- )
920- }
937+ onClick = { ( ) => {
938+ // A fork can ONLY come from a share link, so the
939+ // forker never owns the original — route them back to
940+ // the shared page, not an empty owner-gated /chat. (A
941+ // since-revoked link lands on the neutral "not
942+ // available" page, which is the correct UX.)
943+ if ( forkedFromShareToken ) {
944+ navigate ( {
945+ to : "/share/$token" ,
946+ params : { token : forkedFromShareToken } ,
947+ } ) ;
948+ } else {
949+ onNavigateToConversation (
950+ forkedFromConversationId ?? null ,
951+ ) ;
952+ }
953+ } }
921954 className = "font-medium text-foreground underline underline-offset-2 hover:text-foreground/80"
922955 >
923956 { forkedFromConversationTitle }
0 commit comments