+
+ {media ??
+ (state === 'uploading' ? (
+
+ ) : (
+
+ ))}
+
+ {(title || description) && (
+
+ {title && {title}}
+ {description && (
+
+ {description}
+
+ )}
+
+ )}
+ {children}
+ {onRemove && (
+
+
+
+ )}
+
+ );
+}
+
+ChatAttachment.displayName = 'Chat.Attachment';
diff --git a/packages/raystack/components/chat/chat-context.tsx b/packages/raystack/components/chat/chat-context.tsx
new file mode 100644
index 000000000..bfc86b28c
--- /dev/null
+++ b/packages/raystack/components/chat/chat-context.tsx
@@ -0,0 +1,73 @@
+'use client';
+
+import { createContext, useContext } from 'react';
+
+export interface ChatMessagesState {
+ /** Whether the reader is at the live edge (scrolled to the bottom). */
+ atBottom: boolean;
+ /**
+ * Ids of the registered messages currently intersecting the viewport, in
+ * document order. Only messages given a `messageId` are tracked.
+ */
+ visibleMessageIds: string[];
+}
+
+export interface ChatMessagesActions {
+ /** Scrolls the conversation to the live edge. */
+ scrollToBottom: (behavior?: ScrollBehavior) => void;
+ /** Scrolls the message registered with the given `messageId` into view. */
+ scrollToMessage: (
+ id: string,
+ options?: { behavior?: ScrollBehavior }
+ ) => void;
+}
+
+export interface ChatMessageRegistration {
+ id?: string;
+ scrollAnchor?: boolean;
+}
+
+export interface ChatMessagesRegistry {
+ register: (
+ element: HTMLElement,
+ registration: ChatMessageRegistration
+ ) => () => void;
+}
+
+export const ChatMessagesStateContext = createContext