@ttt-productions/chat-react 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +31 -0
  2. package/dist/context/ChatNameResolverContext.d.ts +37 -0
  3. package/dist/context/ChatNameResolverContext.d.ts.map +1 -0
  4. package/dist/context/ChatNameResolverContext.js +44 -0
  5. package/dist/context/ChatNameResolverContext.js.map +1 -0
  6. package/dist/firestore/queries.d.ts +5 -0
  7. package/dist/firestore/queries.d.ts.map +1 -0
  8. package/dist/firestore/queries.js +16 -0
  9. package/dist/firestore/queries.js.map +1 -0
  10. package/dist/hooks/useChatMessages.d.ts +12 -0
  11. package/dist/hooks/useChatMessages.d.ts.map +1 -0
  12. package/dist/hooks/useChatMessages.js +139 -0
  13. package/dist/hooks/useChatMessages.js.map +1 -0
  14. package/dist/hooks/useChatThreadAccess.d.ts +8 -0
  15. package/dist/hooks/useChatThreadAccess.d.ts.map +1 -0
  16. package/dist/hooks/useChatThreadAccess.js +19 -0
  17. package/dist/hooks/useChatThreadAccess.js.map +1 -0
  18. package/dist/index.d.ts +18 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +13 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/mentions/MentionAutocomplete.d.ts +22 -0
  23. package/dist/mentions/MentionAutocomplete.d.ts.map +1 -0
  24. package/dist/mentions/MentionAutocomplete.js +21 -0
  25. package/dist/mentions/MentionAutocomplete.js.map +1 -0
  26. package/dist/mentions/MessageText.d.ts +27 -0
  27. package/dist/mentions/MessageText.d.ts.map +1 -0
  28. package/dist/mentions/MessageText.js +20 -0
  29. package/dist/mentions/MessageText.js.map +1 -0
  30. package/dist/mentions/use-mention-autocomplete.d.ts +82 -0
  31. package/dist/mentions/use-mention-autocomplete.d.ts.map +1 -0
  32. package/dist/mentions/use-mention-autocomplete.js +323 -0
  33. package/dist/mentions/use-mention-autocomplete.js.map +1 -0
  34. package/dist/types.d.ts +117 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +5 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/ui/ChatShell.d.ts +23 -0
  39. package/dist/ui/ChatShell.d.ts.map +1 -0
  40. package/dist/ui/ChatShell.js +22 -0
  41. package/dist/ui/ChatShell.js.map +1 -0
  42. package/dist/ui/Composer.d.ts +24 -0
  43. package/dist/ui/Composer.d.ts.map +1 -0
  44. package/dist/ui/Composer.js +140 -0
  45. package/dist/ui/Composer.js.map +1 -0
  46. package/dist/ui/MessageItemDefault.d.ts +11 -0
  47. package/dist/ui/MessageItemDefault.d.ts.map +1 -0
  48. package/dist/ui/MessageItemDefault.js +51 -0
  49. package/dist/ui/MessageItemDefault.js.map +1 -0
  50. package/dist/ui/MessageList.d.ts +18 -0
  51. package/dist/ui/MessageList.d.ts.map +1 -0
  52. package/dist/ui/MessageList.js +90 -0
  53. package/dist/ui/MessageList.js.map +1 -0
  54. package/dist/ui/menus.d.ts +12 -0
  55. package/dist/ui/menus.d.ts.map +1 -0
  56. package/dist/ui/menus.js +12 -0
  57. package/dist/ui/menus.js.map +1 -0
  58. package/package.json +78 -0
  59. package/src/styles/chat.css +153 -0
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @ttt-productions/chat-react
2
+
3
+ Chat **React UI** for TTT Productions apps: chat shell, composer, message list,
4
+ attachment UI, the generic mention autocomplete UI, the name-resolver context,
5
+ and the realtime-newest-window + infinite-older hooks.
6
+
7
+ Built on the pure [`@ttt-productions/chat-core`](../chat-core) package
8
+ (contracts, mention parser/serializer, grouping). A non-React consumer (a Cloud
9
+ Function, a script, a future native/TV client) installs `chat-core` and pulls
10
+ in none of this frontend tree.
11
+
12
+ ## Entry points
13
+
14
+ - `.` — React components, hooks, the Firebase-client adapter config types
15
+ (`ChatCoreConfig`, `ChatAttachmentConfig`, `ChatUploadAdapter`,
16
+ `ChatMentionConfig`), and the React render types (`MessageRenderer`,
17
+ `RenderableMentionProvider`, `MentionResultRenderer`).
18
+ - `./styles` — chat CSS. Import once in your app layout:
19
+ `import "@ttt-productions/chat-react/styles";`
20
+
21
+ ## Peers
22
+
23
+ `react`, `react-dom`, `firebase`, `@tanstack/react-query`, and `lucide-react`
24
+ are optional peers — provided by the consuming app.
25
+
26
+ ## Boundary
27
+
28
+ `chat-react` does not import `ttt-core`, does not hardcode TTT origins, and does
29
+ not build TTT storage paths. Consumers pass a chat upload adapter with opaque
30
+ origin id, upload path builder, and optional metadata builder. TTT mention
31
+ kinds, permissions, provider lists, routing, and search live in TTT code.
@@ -0,0 +1,37 @@
1
+ import * as React from "react";
2
+ import type { ChatNameResolver, ChatPrewarmSenders } from "@ttt-productions/chat-core";
3
+ type ChatNameResolverContextValue = {
4
+ resolveName: ChatNameResolver;
5
+ prewarm?: ChatPrewarmSenders;
6
+ };
7
+ export type ChatNameResolverProviderProps = {
8
+ resolveName: ChatNameResolver;
9
+ prewarm?: ChatPrewarmSenders;
10
+ children: React.ReactNode;
11
+ };
12
+ /**
13
+ * Wraps any chat-core consumer (typically the whole app shell) and provides
14
+ * a name resolver. Required for any tree that renders <ChatShell>, <MessageList>,
15
+ * or <MessageItemDefault>.
16
+ */
17
+ export declare function ChatNameResolverProvider(props: ChatNameResolverProviderProps): import("react/jsx-runtime").JSX.Element;
18
+ /**
19
+ * Strict hook — throws if no provider is wrapped. Used inside chat-core's
20
+ * own renderers (MessageItemDefault, ReplyQuote) so a missing setup surfaces
21
+ * immediately on first render rather than silently rendering "User".
22
+ */
23
+ export declare function useChatNameResolver(): ChatNameResolverContextValue;
24
+ /**
25
+ * Optional variant — returns null instead of throwing when no provider is
26
+ * wrapped. Used internally by useChatMessages so headless callers (tests,
27
+ * data-only consumers that don't render UI) can still use the hook.
28
+ */
29
+ export declare function useOptionalChatNameResolver(): ChatNameResolverContextValue | null;
30
+ /**
31
+ * Convenience hook for renderers. Returns a resolved string — never null —
32
+ * by falling back to "User" when the cache hasn't filled yet. Re-renders
33
+ * automatically once the upstream cache settles.
34
+ */
35
+ export declare function useResolvedSenderName(senderId: string): string;
36
+ export {};
37
+ //# sourceMappingURL=ChatNameResolverContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatNameResolverContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatNameResolverContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEvF,KAAK,4BAA4B,GAAG;IAClC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B,CAAC;AAIF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,WAAW,EAAE,gBAAgB,CAAC;IAC9B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,2CAQ5E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,4BAA4B,CAQlE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,4BAA4B,GAAG,IAAI,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG9D"}
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ const ChatNameResolverContext = React.createContext(null);
5
+ /**
6
+ * Wraps any chat-core consumer (typically the whole app shell) and provides
7
+ * a name resolver. Required for any tree that renders <ChatShell>, <MessageList>,
8
+ * or <MessageItemDefault>.
9
+ */
10
+ export function ChatNameResolverProvider(props) {
11
+ const { resolveName, prewarm, children } = props;
12
+ const value = React.useMemo(() => ({ resolveName, prewarm }), [resolveName, prewarm]);
13
+ return (_jsx(ChatNameResolverContext.Provider, { value: value, children: children }));
14
+ }
15
+ /**
16
+ * Strict hook — throws if no provider is wrapped. Used inside chat-core's
17
+ * own renderers (MessageItemDefault, ReplyQuote) so a missing setup surfaces
18
+ * immediately on first render rather than silently rendering "User".
19
+ */
20
+ export function useChatNameResolver() {
21
+ const ctx = React.useContext(ChatNameResolverContext);
22
+ if (!ctx) {
23
+ throw new Error("[chat-core] ChatNameResolverProvider is required. Wrap your ChatShell tree with <ChatNameResolverProvider>.");
24
+ }
25
+ return ctx;
26
+ }
27
+ /**
28
+ * Optional variant — returns null instead of throwing when no provider is
29
+ * wrapped. Used internally by useChatMessages so headless callers (tests,
30
+ * data-only consumers that don't render UI) can still use the hook.
31
+ */
32
+ export function useOptionalChatNameResolver() {
33
+ return React.useContext(ChatNameResolverContext);
34
+ }
35
+ /**
36
+ * Convenience hook for renderers. Returns a resolved string — never null —
37
+ * by falling back to "User" when the cache hasn't filled yet. Re-renders
38
+ * automatically once the upstream cache settles.
39
+ */
40
+ export function useResolvedSenderName(senderId) {
41
+ const { resolveName } = useChatNameResolver();
42
+ return resolveName(senderId) ?? "User";
43
+ }
44
+ //# sourceMappingURL=ChatNameResolverContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatNameResolverContext.js","sourceRoot":"","sources":["../../src/context/ChatNameResolverContext.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,MAAM,uBAAuB,GAAG,KAAK,CAAC,aAAa,CAAsC,IAAI,CAAC,CAAC;AAQ/F;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAoC;IAC3E,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IACtF,OAAO,CACL,KAAC,uBAAuB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAC3C,QAAQ,GACwB,CACpC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,6GAA6G,CAC9G,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO,KAAK,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,EAAE,WAAW,EAAE,GAAG,mBAAmB,EAAE,CAAC;IAC9C,OAAO,WAAW,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC;AACzC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Firestore, Query, DocumentData } from "firebase/firestore";
2
+ export declare function threadDocPath(chatCollectionPath: string | string[], threadId: string): string[];
3
+ export declare function messagesColPath(chatCollectionPath: string | string[], threadId: string, messagesSubcollection?: string): [string, string, ...string[]];
4
+ export declare function newestWindowQuery(db: Firestore, chatCollectionPath: string | string[], threadId: string, createdAtField: string, pageSize: number, messagesSubcollection?: string): Query<DocumentData>;
5
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/firestore/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOzE,wBAAgB,aAAa,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE/F;AAED,wBAAgB,eAAe,CAC7B,kBAAkB,EAAE,MAAM,GAAG,MAAM,EAAE,EACrC,QAAQ,EAAE,MAAM,EAChB,qBAAqB,SAAa,GACjC,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAG/B;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,SAAS,EACb,kBAAkB,EAAE,MAAM,GAAG,MAAM,EAAE,EACrC,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,qBAAqB,SAAa,GACjC,KAAK,CAAC,YAAY,CAAC,CAOrB"}
@@ -0,0 +1,16 @@
1
+ import { collection, query, orderBy, limit } from "firebase/firestore";
2
+ function normalizeSegments(path) {
3
+ return Array.isArray(path) ? path : [path];
4
+ }
5
+ export function threadDocPath(chatCollectionPath, threadId) {
6
+ return [...normalizeSegments(chatCollectionPath), threadId];
7
+ }
8
+ export function messagesColPath(chatCollectionPath, threadId, messagesSubcollection = "messages") {
9
+ const segments = [...normalizeSegments(chatCollectionPath), threadId, messagesSubcollection];
10
+ return segments;
11
+ }
12
+ export function newestWindowQuery(db, chatCollectionPath, threadId, createdAtField, pageSize, messagesSubcollection = "messages") {
13
+ const path = messagesColPath(chatCollectionPath, threadId, messagesSubcollection);
14
+ return query(collection(db, path[0], ...path.slice(1)), orderBy(createdAtField, "desc"), limit(pageSize));
15
+ }
16
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/firestore/queries.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEvE,SAAS,iBAAiB,CAAC,IAAuB;IAChD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,kBAAqC,EAAE,QAAgB;IACnF,OAAO,CAAC,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,kBAAqC,EACrC,QAAgB,EAChB,qBAAqB,GAAG,UAAU;IAElC,MAAM,QAAQ,GAAG,CAAC,GAAG,iBAAiB,CAAC,kBAAkB,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;IAC7F,OAAO,QAAyC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,EAAa,EACb,kBAAqC,EACrC,QAAgB,EAChB,cAAsB,EACtB,QAAgB,EAChB,qBAAqB,GAAG,UAAU;IAElC,MAAM,IAAI,GAAG,eAAe,CAAC,kBAAkB,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;IAClF,OAAO,KAAK,CACV,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EACzC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,EAC/B,KAAK,CAAC,QAAQ,CAAC,CAChB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ChatMessageV1 } from "@ttt-productions/chat-core";
2
+ import type { ChatCoreConfig } from "../types.js";
3
+ export type UseChatMessagesResult = {
4
+ allowed: boolean;
5
+ isInitialLoading: boolean;
6
+ messages: ChatMessageV1[];
7
+ fetchOlder: () => Promise<void>;
8
+ hasOlder: boolean;
9
+ isFetchingOlder: boolean;
10
+ };
11
+ export declare function useChatMessages(config: ChatCoreConfig): UseChatMessagesResult;
12
+ //# sourceMappingURL=useChatMessages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatMessages.d.ts","sourceRoot":"","sources":["../../src/hooks/useChatMessages.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAclD,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAsDF,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,qBAAqB,CA4I7E"}
@@ -0,0 +1,139 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { collection, getDocs, onSnapshot, orderBy, query, startAfter, limit, } from "firebase/firestore";
4
+ import { useInfiniteQuery } from "@tanstack/react-query";
5
+ import { toMillis } from "@ttt-productions/firebase-helpers";
6
+ import { canAccessThread } from "./useChatThreadAccess.js";
7
+ import { messagesColPath, newestWindowQuery } from "../firestore/queries.js";
8
+ import { useOptionalChatNameResolver } from "../context/ChatNameResolverContext.js";
9
+ // Safety limit to prevent memory exhaustion
10
+ const MAX_PAGE_SIZE = 100;
11
+ function mapMsg(doc, threadId) {
12
+ const d = doc.data();
13
+ const createdAt = toMillis(d.createdAt);
14
+ if (!createdAt) {
15
+ console.error('[useChatMessages] Message missing valid createdAt:', doc.id, d.createdAt);
16
+ }
17
+ // replyTo: the new shape uses senderId, not senderUsername. Tolerate older
18
+ // docs that still have senderUsername — drop the name and keep what we can.
19
+ const replyTo = d.replyTo
20
+ ? {
21
+ messageId: d.replyTo.messageId,
22
+ senderId: d.replyTo.senderId ?? "",
23
+ messagePreview: d.replyTo.messagePreview,
24
+ }
25
+ : undefined;
26
+ return {
27
+ messageId: doc.id,
28
+ threadId,
29
+ createdAt: createdAt || 0,
30
+ senderId: d.senderId,
31
+ text: d.text ?? "",
32
+ type: d.type,
33
+ attachment: d.attachment ?? undefined,
34
+ replyTo,
35
+ isSystemMessage: d.isSystemMessage ?? undefined,
36
+ meta: d.meta,
37
+ };
38
+ }
39
+ function dedupeAndSortAsc(all) {
40
+ const m = new Map();
41
+ for (const x of all)
42
+ m.set(x.messageId, x);
43
+ return Array.from(m.values()).sort((a, b) => a.createdAt - b.createdAt);
44
+ }
45
+ function messagesCol(db, chatCollectionPath, threadId, messagesSubcollection) {
46
+ const path = messagesColPath(chatCollectionPath, threadId, messagesSubcollection);
47
+ return collection(db, path[0], ...path.slice(1));
48
+ }
49
+ export function useChatMessages(config) {
50
+ const { db, chatCollectionPath, messagesSubcollection = "messages", threadId, pageSize: requestedPageSize = 20, currentUserId, isAdmin, accessMode, threadAllowedUserIds, createdAtField = "createdAt", } = config;
51
+ const pageSize = Math.min(requestedPageSize, MAX_PAGE_SIZE);
52
+ if (requestedPageSize > MAX_PAGE_SIZE) {
53
+ console.warn(`[useChatMessages] Requested pageSize ${requestedPageSize} exceeds maximum ${MAX_PAGE_SIZE}. Using ${MAX_PAGE_SIZE}.`);
54
+ }
55
+ const pathKey = JSON.stringify(Array.isArray(chatCollectionPath) ? chatCollectionPath : [chatCollectionPath]);
56
+ const allowed = React.useMemo(() => canAccessThread({
57
+ accessMode,
58
+ isAdmin,
59
+ currentUserId,
60
+ allowedUserIds: threadAllowedUserIds,
61
+ }), [accessMode, isAdmin, currentUserId, threadAllowedUserIds]);
62
+ const [newest, setNewest] = React.useState({
63
+ ready: false,
64
+ newestDesc: [],
65
+ oldestDocInWindow: null,
66
+ });
67
+ React.useEffect(() => {
68
+ if (!allowed)
69
+ return;
70
+ const q = newestWindowQuery(db, chatCollectionPath, threadId, createdAtField, pageSize, messagesSubcollection);
71
+ const unsub = onSnapshot(q, (snap) => {
72
+ const docs = snap.docs;
73
+ const mapped = docs.map((d) => mapMsg(d, threadId));
74
+ setNewest({
75
+ ready: true,
76
+ newestDesc: mapped,
77
+ oldestDocInWindow: docs[docs.length - 1] ?? null,
78
+ });
79
+ });
80
+ return () => unsub();
81
+ }, [allowed, db, pathKey, threadId, createdAtField, pageSize, messagesSubcollection]);
82
+ const older = useInfiniteQuery({
83
+ queryKey: ["chat-core", "older", pathKey, threadId, messagesSubcollection, pageSize],
84
+ enabled: allowed && newest.ready,
85
+ initialPageParam: undefined,
86
+ queryFn: async ({ pageParam }) => {
87
+ const baseCol = messagesCol(db, chatCollectionPath, threadId, messagesSubcollection);
88
+ const cursor = pageParam ?? (newest.oldestDocInWindow ?? undefined);
89
+ if (!cursor) {
90
+ return {
91
+ itemsDesc: [],
92
+ nextCursor: undefined,
93
+ };
94
+ }
95
+ const q = query(baseCol, orderBy(createdAtField, "desc"), startAfter(cursor), limit(pageSize));
96
+ const snap = await getDocs(q);
97
+ const docs = snap.docs;
98
+ const itemsDesc = docs.map((d) => mapMsg(d, threadId));
99
+ const nextCursor = docs.length === pageSize ? docs[docs.length - 1] : undefined;
100
+ return { itemsDesc, nextCursor };
101
+ },
102
+ getNextPageParam: (last) => last.nextCursor,
103
+ });
104
+ const fetchOlder = React.useCallback(async () => {
105
+ await older.fetchNextPage();
106
+ }, [older.fetchNextPage]);
107
+ const messages = React.useMemo(() => {
108
+ if (!allowed)
109
+ return [];
110
+ const olderDesc = (older.data?.pages ?? []).flatMap((p) => p.itemsDesc);
111
+ return dedupeAndSortAsc([...olderDesc, ...newest.newestDesc]);
112
+ }, [allowed, older.data, newest.newestDesc]);
113
+ // Pre-warm name cache for all visible senders (and reply-to senders).
114
+ // Optional resolver: silent no-op if no provider is wrapped.
115
+ const resolverCtx = useOptionalChatNameResolver();
116
+ const senderIds = React.useMemo(() => {
117
+ const set = new Set();
118
+ for (const m of messages) {
119
+ set.add(m.senderId);
120
+ if (m.replyTo?.senderId)
121
+ set.add(m.replyTo.senderId);
122
+ }
123
+ return Array.from(set);
124
+ }, [messages]);
125
+ React.useEffect(() => {
126
+ if (resolverCtx?.prewarm && senderIds.length > 0) {
127
+ resolverCtx.prewarm(senderIds);
128
+ }
129
+ }, [resolverCtx, senderIds]);
130
+ return {
131
+ allowed,
132
+ isInitialLoading: allowed ? !newest.ready : false,
133
+ messages,
134
+ fetchOlder,
135
+ hasOlder: Boolean(older.hasNextPage),
136
+ isFetchingOlder: older.isFetchingNextPage,
137
+ };
138
+ }
139
+ //# sourceMappingURL=useChatMessages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatMessages.js","sourceRoot":"","sources":["../../src/hooks/useChatMessages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ChatAccessMode } from "@ttt-productions/chat-core";
2
+ export declare function canAccessThread(args: {
3
+ accessMode: ChatAccessMode;
4
+ isAdmin: boolean;
5
+ currentUserId: string;
6
+ allowedUserIds?: string[];
7
+ }): boolean;
8
+ //# sourceMappingURL=useChatThreadAccess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatThreadAccess.d.ts","sourceRoot":"","sources":["../../src/hooks/useChatThreadAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,GAAG,OAAO,CAkBV"}
@@ -0,0 +1,19 @@
1
+ export function canAccessThread(args) {
2
+ const { accessMode, isAdmin, currentUserId, allowedUserIds } = args;
3
+ // Admin always bypasses access checks (admin moderation surfaces).
4
+ if (isAdmin)
5
+ return true;
6
+ // Unauthenticated users never have access regardless of mode.
7
+ if (!currentUserId)
8
+ return false;
9
+ if (accessMode === "firestore-rules") {
10
+ // Defer to Firestore rules. If rules deny, onSnapshot will surface
11
+ // permission-denied, but chat-core does not pre-deny.
12
+ return true;
13
+ }
14
+ // accessMode === "explicit-allowlist"
15
+ if (!allowedUserIds)
16
+ return false;
17
+ return allowedUserIds.includes(currentUserId);
18
+ }
19
+ //# sourceMappingURL=useChatThreadAccess.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatThreadAccess.js","sourceRoot":"","sources":["../../src/hooks/useChatThreadAccess.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,IAK/B;IACC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAEpE,mEAAmE;IACnE,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IAEzB,8DAA8D;IAC9D,IAAI,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC;IAEjC,IAAI,UAAU,KAAK,iBAAiB,EAAE,CAAC;QACrC,mEAAmE;QACnE,sDAAsD;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sCAAsC;IACtC,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAClC,OAAO,cAAc,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,18 @@
1
+ export type { ChatCoreConfig, ChatAttachmentConfig, ChatUploadAdapter, ChatMentionConfig, MessageRenderer, MessageRendererRegistry, RenderableMentionProvider, MentionResultRenderer, } from "./types.js";
2
+ export type { ChatNameResolverProviderProps } from "./context/ChatNameResolverContext.js";
3
+ export { ChatNameResolverProvider, useChatNameResolver, useOptionalChatNameResolver, useResolvedSenderName, } from "./context/ChatNameResolverContext.js";
4
+ export type { UseChatMessagesResult } from "./hooks/useChatMessages.js";
5
+ export { useChatMessages } from "./hooks/useChatMessages.js";
6
+ export { canAccessThread } from "./hooks/useChatThreadAccess.js";
7
+ export type { ChatShellProps } from "./ui/ChatShell.js";
8
+ export { ChatShell } from "./ui/ChatShell.js";
9
+ export { MessageList } from "./ui/MessageList.js";
10
+ export type { ComposerProps } from "./ui/Composer.js";
11
+ export { Composer } from "./ui/Composer.js";
12
+ export type { MessageItemDefaultProps } from "./ui/MessageItemDefault.js";
13
+ export { MessageItemDefault } from "./ui/MessageItemDefault.js";
14
+ export { MessageActions, ThreadActions } from "./ui/menus.js";
15
+ export { MessageText, type MessageTextProps } from "./mentions/MessageText.js";
16
+ export { MentionAutocomplete, type MentionAutocompleteProps, } from "./mentions/MentionAutocomplete.js";
17
+ export { useMentionAutocomplete, type UseMentionAutocompleteArgs, type UseMentionAutocompleteResult, type AutocompleteState, type AutocompleteResultGroup, } from "./mentions/use-mention-autocomplete.js";
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAG9C,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,YAAY,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,GAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,sBAAsB,EACtB,KAAK,0BAA0B,EAC/B,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,wCAAwC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ export { ChatNameResolverProvider, useChatNameResolver, useOptionalChatNameResolver, useResolvedSenderName, } from "./context/ChatNameResolverContext.js";
3
+ export { useChatMessages } from "./hooks/useChatMessages.js";
4
+ export { canAccessThread } from "./hooks/useChatThreadAccess.js";
5
+ export { ChatShell } from "./ui/ChatShell.js";
6
+ export { MessageList } from "./ui/MessageList.js";
7
+ export { Composer } from "./ui/Composer.js";
8
+ export { MessageItemDefault } from "./ui/MessageItemDefault.js";
9
+ export { MessageActions, ThreadActions } from "./ui/menus.js";
10
+ export { MessageText } from "./mentions/MessageText.js";
11
+ export { MentionAutocomplete, } from "./mentions/MentionAutocomplete.js";
12
+ export { useMentionAutocomplete, } from "./mentions/use-mention-autocomplete.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAsBb,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAI9C,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAIjE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAyB,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EACL,mBAAmB,GAEpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,sBAAsB,GAKvB,MAAM,wCAAwC,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Visual autocomplete dropdown for the mention system.
3
+ *
4
+ * Pure presentation — driven entirely by `state` from `useMentionAutocomplete`.
5
+ * The consumer is expected to absolutely-position this near the textarea (or
6
+ * use its caret position via a wrapping container). chat-core does not
7
+ * compute textarea-caret coordinates; that's the consumer's job.
8
+ *
9
+ * Selecting a result fires `onSelect(ref)`. Hovering / arrow-keying updates
10
+ * the highlight via `onHighlight(index)`.
11
+ */
12
+ import type { AutocompleteState } from './use-mention-autocomplete.js';
13
+ import type { MentionRef } from '@ttt-productions/chat-core';
14
+ export interface MentionAutocompleteProps<TKind extends string = string> {
15
+ state: AutocompleteState<TKind>;
16
+ onSelect: (ref: MentionRef<TKind>) => void;
17
+ onHighlight?: (flatIndex: number) => void;
18
+ /** Class name applied to the outer container. */
19
+ className?: string;
20
+ }
21
+ export declare function MentionAutocomplete<TKind extends string = string>(props: MentionAutocompleteProps<TKind>): import("react/jsx-runtime").JSX.Element | null;
22
+ //# sourceMappingURL=MentionAutocomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MentionAutocomplete.d.ts","sourceRoot":"","sources":["../../src/mentions/MentionAutocomplete.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAG7D,MAAM,WAAW,wBAAwB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACrE,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAChC,QAAQ,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAC3C,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAC/D,KAAK,EAAE,wBAAwB,CAAC,KAAK,CAAC,kDAoEvC"}
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from '@ttt-productions/ui-core';
4
+ export function MentionAutocomplete(props) {
5
+ const { state, onSelect, onHighlight, className } = props;
6
+ if (!state.open)
7
+ return null;
8
+ const anyResults = state.flatResults.length > 0;
9
+ const anyLoading = state.groups.some((g) => g.loading);
10
+ let flatIndex = 0;
11
+ return (_jsxs("div", { role: "listbox", "aria-label": "Mentions", className: cn('chat-mention-autocomplete', 'rounded-md border bg-popover text-popover-foreground shadow-md', 'max-h-72 overflow-y-auto min-w-[16rem]', className), children: [!anyResults && anyLoading && (_jsx("div", { className: "px-3 py-2 text-xs text-muted-foreground", children: "Searching\u2026" })), !anyResults && !anyLoading && (_jsx("div", { className: "px-3 py-2 text-xs text-muted-foreground", children: state.showingRecent ? 'No recent mentions' : 'No matches' })), state.groups.map((group) => (_jsxs("div", { className: "chat-mention-group", children: [_jsx("div", { className: "px-3 pt-2 pb-1 text-xs font-semibold text-muted-foreground", children: state.showingRecent ? `Recent ${group.label}` : group.label }), group.loading && group.results.length === 0 && (_jsx("div", { className: "px-3 pb-2 text-xs text-muted-foreground", children: "Loading\u2026" })), _jsx("ul", { children: group.results.map((ref) => {
12
+ const myIndex = flatIndex;
13
+ flatIndex += 1;
14
+ const highlighted = myIndex === state.highlightedIndex;
15
+ return (_jsx("li", { children: _jsx("button", { type: "button", role: "option", "aria-selected": highlighted, className: cn('w-full text-left px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground', highlighted && 'bg-accent text-accent-foreground'), onMouseEnter: () => onHighlight?.(myIndex), onClick: (e) => {
16
+ e.preventDefault();
17
+ onSelect(ref);
18
+ }, children: group.renderResult ? group.renderResult(ref) : _jsxs("span", { children: ["@", ref.displayText] }) }) }, `${ref.kind}:${ref.id}`));
19
+ }) })] }, group.kind)))] }));
20
+ }
21
+ //# sourceMappingURL=MentionAutocomplete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MentionAutocomplete.js","sourceRoot":"","sources":["../../src/mentions/MentionAutocomplete.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAgBb,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAU9C,MAAM,UAAU,mBAAmB,CACjC,KAAsC;IAEtC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC1D,IAAI,CAAC,KAAK,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAE7B,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAEvD,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,OAAO,CACL,eACE,IAAI,EAAC,SAAS,gBACH,UAAU,EACrB,SAAS,EAAE,EAAE,CACX,2BAA2B,EAC3B,gEAAgE,EAChE,wCAAwC,EACxC,SAAS,CACV,aAEA,CAAC,UAAU,IAAI,UAAU,IAAI,CAC5B,cAAK,SAAS,EAAC,yCAAyC,gCAAiB,CAC1E,EACA,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,CAC7B,cAAK,SAAS,EAAC,yCAAyC,YACrD,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,YAAY,GACtD,CACP,EACA,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAC3B,eAAsB,SAAS,EAAC,oBAAoB,aAClD,cAAK,SAAS,EAAC,4DAA4D,YACxE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,GACxD,EACL,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAC9C,cAAK,SAAS,EAAC,yCAAyC,8BAAe,CACxE,EACD,uBACG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;4BACzB,MAAM,OAAO,GAAG,SAAS,CAAC;4BAC1B,SAAS,IAAI,CAAC,CAAC;4BACf,MAAM,WAAW,GAAG,OAAO,KAAK,KAAK,CAAC,gBAAgB,CAAC;4BACvD,OAAO,CACL,uBACE,iBACE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,QAAQ,mBACE,WAAW,EAC1B,SAAS,EAAE,EAAE,CACX,iFAAiF,EACjF,WAAW,IAAI,kCAAkC,CAClD,EACD,YAAY,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,EAC1C,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;wCACb,CAAC,CAAC,cAAc,EAAE,CAAC;wCACnB,QAAQ,CAAC,GAAG,CAAC,CAAC;oCAChB,CAAC,YAEA,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,gCAAQ,GAAG,CAAC,WAAW,IAAQ,GACxE,IAhBF,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,EAAE,CAiB3B,CACN,CAAC;wBACJ,CAAC,CAAC,GACC,KAjCG,KAAK,CAAC,IAAI,CAkCd,CACP,CAAC,IACE,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Renders message text with inline mention chips.
3
+ *
4
+ * Walks the parser output and renders each segment: text segments as
5
+ * `<span>`, mention segments via the consumer-supplied `renderMention`
6
+ * callback (or a default chip when omitted).
7
+ *
8
+ * Pure, no context. Safe to use inside MessageItem renderers, ReplyQuote
9
+ * previews, or any other text surface that should honor mention tokens.
10
+ */
11
+ import type { ReactNode } from 'react';
12
+ import type { MentionRef } from '@ttt-productions/chat-core';
13
+ export interface MessageTextProps<TKind extends string = string> {
14
+ /** Raw message text containing optional `@[kind:id|displayText]` tokens. */
15
+ text: string;
16
+ /**
17
+ * Custom mention renderer. When omitted, mentions render as a
18
+ * `<span class="chat-mention-chip">@{displayText}</span>` chip — consumers
19
+ * style via CSS. To make mentions clickable / linked / role-aware, supply
20
+ * a renderMention.
21
+ */
22
+ renderMention?: (ref: MentionRef<TKind>) => ReactNode;
23
+ /** Optional className applied to the wrapping `<span>`. */
24
+ className?: string;
25
+ }
26
+ export declare function MessageText<TKind extends string = string>(props: MessageTextProps<TKind>): import("react/jsx-runtime").JSX.Element;
27
+ //# sourceMappingURL=MessageText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageText.d.ts","sourceRoot":"","sources":["../../src/mentions/MessageText.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAG7D,MAAM,WAAW,gBAAgB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IAC7D,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC;IACtD,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,gBAAgB,CAAC,KAAK,CAAC,2CAiBxF"}
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import { parseMentionTokens } from '@ttt-productions/chat-core';
4
+ function DefaultMentionChip({ ref }) {
5
+ return (_jsxs("span", { className: "chat-mention-chip", "data-mention-kind": ref.kind, "data-mention-id": ref.id, children: ["@", ref.displayText] }));
6
+ }
7
+ export function MessageText(props) {
8
+ const { text, renderMention, className } = props;
9
+ const segments = parseMentionTokens(text);
10
+ return (_jsx("span", { className: className, children: segments.map((seg, i) => {
11
+ if (seg.type === 'text') {
12
+ return _jsx("span", { children: seg.text }, i);
13
+ }
14
+ if (renderMention) {
15
+ return _jsx("span", { children: renderMention(seg.ref) }, i);
16
+ }
17
+ return _jsx(DefaultMentionChip, { ref: seg.ref }, i);
18
+ }) }));
19
+ }
20
+ //# sourceMappingURL=MessageText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageText.js","sourceRoot":"","sources":["../../src/mentions/MessageText.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAeb,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAgBhE,SAAS,kBAAkB,CAAuB,EAAE,GAAG,EAA8B;IACnF,OAAO,CACL,gBAAM,SAAS,EAAC,mBAAmB,uBAAoB,GAAG,CAAC,IAAI,qBAAmB,GAAG,CAAC,EAAE,kBACpF,GAAG,CAAC,WAAW,IACZ,CACR,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAgC,KAA8B;IACvF,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACjD,MAAM,QAAQ,GAAG,kBAAkB,CAAQ,IAAI,CAAC,CAAC;IAEjD,OAAO,CACL,eAAM,SAAS,EAAE,SAAS,YACvB,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YACvB,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACxB,OAAO,yBAAe,GAAG,CAAC,IAAI,IAAZ,CAAC,CAAmB,CAAC;YACzC,CAAC;YACD,IAAI,aAAa,EAAE,CAAC;gBAClB,OAAO,yBAAe,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAA1B,CAAC,CAAiC,CAAC;YACvD,CAAC;YACD,OAAO,KAAC,kBAAkB,IAAS,GAAG,EAAE,GAAG,CAAC,GAAG,IAAf,CAAC,CAAkB,CAAC;QACtD,CAAC,CAAC,GACG,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Composer-side mention autocomplete state hook.
3
+ *
4
+ * Detects when the user types the trigger character (default `@`) inside the
5
+ * provided textarea, captures the live query (the text between the trigger
6
+ * and the cursor), debounces searches across all configured providers, and
7
+ * tracks the anchor positions of inserted mentions so the composer can
8
+ * convert display text back to wire tokens on send.
9
+ *
10
+ * Returns an `AutocompleteState` value tailored for the `<MentionAutocomplete>`
11
+ * UI component, plus an `insertMention` action and a `getValueWithTokens`
12
+ * accessor the composer calls on send.
13
+ *
14
+ * Generic over `TKind` and `TContext`; chat-core inspects neither.
15
+ */
16
+ import { type RefObject } from 'react';
17
+ import type { MentionAnchor, MentionRef, RecentMentionsAdapter } from '@ttt-productions/chat-core';
18
+ import type { RenderableMentionProvider } from '../types.js';
19
+ export type AutocompleteResultGroup<TKind extends string = string> = {
20
+ kind: TKind;
21
+ label: string;
22
+ results: MentionRef<TKind>[];
23
+ /** True if `search` is in flight for this provider. */
24
+ loading: boolean;
25
+ /** Provider-supplied custom renderer for individual results, if any. */
26
+ renderResult?: (ref: MentionRef<TKind>) => import('react').ReactNode;
27
+ };
28
+ export type AutocompleteState<TKind extends string = string> = {
29
+ /** True iff the dropdown should be visible. */
30
+ open: boolean;
31
+ /** Current query text (everything after the trigger up to the cursor). */
32
+ query: string;
33
+ /** Results grouped by provider, in the order the consumer passed providers. */
34
+ groups: AutocompleteResultGroup<TKind>[];
35
+ /** When true, groups are recent picks (`getRecent`) rather than search results. */
36
+ showingRecent: boolean;
37
+ /** Index of the currently highlighted result, flattened across all groups. */
38
+ highlightedIndex: number;
39
+ /** Flat array of all results in display order. Used internally for keyboard nav. */
40
+ flatResults: MentionRef<TKind>[];
41
+ };
42
+ export interface UseMentionAutocompleteArgs<TKind extends string, TContext> {
43
+ textareaRef: RefObject<HTMLTextAreaElement | null>;
44
+ /** Current textarea value (controlled). */
45
+ value: string;
46
+ /** Setter for the textarea value. */
47
+ onChange: (next: string) => void;
48
+ /** Providers in display order. Empty list disables the system entirely. */
49
+ providers: RenderableMentionProvider<TKind, TContext>[];
50
+ /** Consumer-controlled context passed to each provider's `search`. */
51
+ context: TContext;
52
+ /** Optional recent-mentions adapter. */
53
+ recent?: RecentMentionsAdapter<TKind>;
54
+ /** Trigger character. Defaults to `'@'`. Must be exactly one character. */
55
+ trigger?: string;
56
+ /**
57
+ * Minimum query length to fire `search`. Below this, recent picks are shown
58
+ * (if `recent` is supplied) or the dropdown stays empty. Default: 0 (search
59
+ * fires immediately on trigger).
60
+ */
61
+ minQueryLength?: number;
62
+ /** Debounce window in ms for search calls. Default: 200. */
63
+ searchDebounceMs?: number;
64
+ }
65
+ export interface UseMentionAutocompleteResult<TKind extends string = string> {
66
+ state: AutocompleteState<TKind>;
67
+ /** Handle keydown from the textarea — returns true if the event was handled. */
68
+ handleKeyDown: (event: import('react').KeyboardEvent<HTMLTextAreaElement>) => boolean;
69
+ /** Programmatically insert a mention at the trigger position (called by the autocomplete UI). */
70
+ insertMention: (ref: MentionRef<TKind>) => void;
71
+ /**
72
+ * Returns the current textarea value with all tracked mention anchors
73
+ * substituted back to wire tokens. Call this from the composer's send path.
74
+ */
75
+ getValueWithTokens: () => string;
76
+ /** Anchor list — exposed for tests and consumers that need the raw positions. */
77
+ anchors: MentionAnchor<TKind>[];
78
+ /** Close the dropdown imperatively (e.g. on send). */
79
+ close: () => void;
80
+ }
81
+ export declare function useMentionAutocomplete<TKind extends string, TContext>(args: UseMentionAutocompleteArgs<TKind, TContext>): UseMentionAutocompleteResult<TKind>;
82
+ //# sourceMappingURL=use-mention-autocomplete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-mention-autocomplete.d.ts","sourceRoot":"","sources":["../../src/mentions/use-mention-autocomplete.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1F,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,qBAAqB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAE7D,MAAM,MAAM,uBAAuB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI;IACnE,IAAI,EAAE,KAAK,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7B,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,OAAO,OAAO,EAAE,SAAS,CAAC;CACtE,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,IAAI;IAC7D,+CAA+C;IAC/C,IAAI,EAAE,OAAO,CAAC;IACd,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,MAAM,EAAE,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;IACzC,mFAAmF;IACnF,aAAa,EAAE,OAAO,CAAC;IACvB,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,WAAW,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,WAAW,0BAA0B,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ;IACxE,WAAW,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACnD,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,2EAA2E;IAC3E,SAAS,EAAE,yBAAyB,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;IACxD,sEAAsE;IACtE,OAAO,EAAE,QAAQ,CAAC;IAClB,wCAAwC;IACxC,MAAM,CAAC,EAAE,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACtC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACzE,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAChC,gFAAgF;IAChF,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,EAAE,aAAa,CAAC,mBAAmB,CAAC,KAAK,OAAO,CAAC;IACtF,iGAAiG;IACjG,aAAa,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,kBAAkB,EAAE,MAAM,MAAM,CAAC;IACjC,iFAAiF;IACjF,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;IAChC,sDAAsD;IACtD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAMD,wBAAgB,sBAAsB,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ,EACnE,IAAI,EAAE,0BAA0B,CAAC,KAAK,EAAE,QAAQ,CAAC,GAChD,4BAA4B,CAAC,KAAK,CAAC,CAgVrC"}