feat: add timestamp and message version to prompt (#944) (#959)

### Summary

This PR implements a timestamp feature for messages in the prompt
window, responding to feature request #944.

**What this does:**
- Prefixes each sent message with a timestamp and message version.
### Screenshot

<img width="530" height="116" alt="image"
src="https://github.com/user-attachments/assets/62a86890-b120-42dd-ab48-8eeb4515a292"
/>

---------

Co-authored-by: Will Chen <willchen90@gmail.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
This commit is contained in:
Adeniji Adekunle James
2025-08-19 06:27:19 +01:00
committed by GitHub
parent d535db6251
commit 0cdd13dcbe
2 changed files with 64 additions and 2 deletions

View File

@@ -5,7 +5,12 @@ import {
} from "./DyadMarkdownParser"; } from "./DyadMarkdownParser";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useStreamChat } from "@/hooks/useStreamChat"; import { useStreamChat } from "@/hooks/useStreamChat";
import { CheckCircle, XCircle } from "lucide-react"; import { CheckCircle, XCircle, Clock, GitCommit } from "lucide-react";
import { formatDistanceToNow, format } from "date-fns";
import { useVersions } from "@/hooks/useVersions";
import { useAtomValue } from "jotai";
import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { useMemo } from "react";
interface ChatMessageProps { interface ChatMessageProps {
message: Message; message: Message;
@@ -14,13 +19,46 @@ interface ChatMessageProps {
const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => { const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
const { isStreaming } = useStreamChat(); const { isStreaming } = useStreamChat();
const appId = useAtomValue(selectedAppIdAtom);
const { versions: liveVersions } = useVersions(appId);
// Find the version that was active when this message was sent
const messageVersion = useMemo(() => {
if (
message.role === "assistant" &&
message.commitHash &&
liveVersions.length
) {
return (
liveVersions.find(
(version) =>
message.commitHash &&
version.oid.slice(0, 7) === message.commitHash.slice(0, 7),
) || null
);
}
return null;
}, [message.commitHash, message.role, liveVersions]);
// Format the message timestamp
const formatTimestamp = (timestamp: string | Date) => {
const now = new Date();
const messageTime = new Date(timestamp);
const diffInHours =
(now.getTime() - messageTime.getTime()) / (1000 * 60 * 60);
if (diffInHours < 24) {
return formatDistanceToNow(messageTime, { addSuffix: true });
} else {
return format(messageTime, "MMM d, yyyy 'at' h:mm a");
}
};
return ( return (
<div <div
className={`flex ${ className={`flex ${
message.role === "assistant" ? "justify-start" : "justify-end" message.role === "assistant" ? "justify-start" : "justify-end"
}`} }`}
> >
<div className={`mt-2 w-full max-w-3xl mx-auto`}> <div className={`mt-2 w-full max-w-3xl mx-auto group`}>
<div <div
className={`rounded-lg p-2 ${ className={`rounded-lg p-2 ${
message.role === "assistant" ? "" : "ml-24 bg-(--sidebar-accent)" message.role === "assistant" ? "" : "ml-24 bg-(--sidebar-accent)"
@@ -101,6 +139,29 @@ const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
</div> </div>
)} )}
</div> </div>
{/* Timestamp and commit info for assistant messages - only visible on hover */}
{message.role === "assistant" && message.createdAt && (
<div className="mt-1 flex items-center justify-start space-x-2 text-xs text-gray-500 dark:text-gray-400 ">
<div className="flex items-center space-x-1">
<Clock className="h-3 w-3" />
<span>{formatTimestamp(message.createdAt)}</span>
</div>
{messageVersion && messageVersion.message && (
<div className="flex items-center space-x-1">
<GitCommit className="h-3 w-3" />
{messageVersion && messageVersion.message && (
<span className="max-w-70 truncate font-medium">
{
messageVersion.message
.replace(/^\[dyad\]\s*/i, "")
.split("\n")[0]
}
</span>
)}
</div>
)}
</div>
)}
</div> </div>
</div> </div>
); );

View File

@@ -67,6 +67,7 @@ export interface Message {
approvalState?: "approved" | "rejected" | null; approvalState?: "approved" | "rejected" | null;
commitHash?: string | null; commitHash?: string | null;
dbTimestamp?: string | null; dbTimestamp?: string | null;
createdAt?: Date | string;
} }
export interface Chat { export interface Chat {