@ttt-productions/chat-react 0.13.2 → 0.13.4

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 (69) hide show
  1. package/README.md +13 -11
  2. package/dist/context/ChatNameResolverContext.d.ts +3 -3
  3. package/dist/context/ChatNameResolverContext.js +3 -3
  4. package/dist/context/ChatNameResolverContext.js.map +1 -1
  5. package/dist/hooks/useChatMessages.d.ts.map +1 -1
  6. package/dist/hooks/useChatMessages.js +4 -13
  7. package/dist/hooks/useChatMessages.js.map +1 -1
  8. package/dist/index.d.ts +2 -4
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -3
  11. package/dist/index.js.map +1 -1
  12. package/dist/realtime/channel-client.d.ts +21 -4
  13. package/dist/realtime/channel-client.d.ts.map +1 -1
  14. package/dist/realtime/channel-client.js +109 -12
  15. package/dist/realtime/channel-client.js.map +1 -1
  16. package/dist/realtime/diagnostics.d.ts +7 -0
  17. package/dist/realtime/diagnostics.d.ts.map +1 -1
  18. package/dist/realtime/diagnostics.js +7 -0
  19. package/dist/realtime/diagnostics.js.map +1 -1
  20. package/dist/realtime/inbox-client.d.ts.map +1 -1
  21. package/dist/realtime/inbox-client.js +29 -8
  22. package/dist/realtime/inbox-client.js.map +1 -1
  23. package/dist/realtime/map.d.ts +3 -5
  24. package/dist/realtime/map.d.ts.map +1 -1
  25. package/dist/realtime/map.js +3 -18
  26. package/dist/realtime/map.js.map +1 -1
  27. package/dist/realtime/shared.d.ts +21 -0
  28. package/dist/realtime/shared.d.ts.map +1 -1
  29. package/dist/realtime/shared.js +22 -0
  30. package/dist/realtime/shared.js.map +1 -1
  31. package/dist/realtime/useRealtimeChatMessages.d.ts +1 -4
  32. package/dist/realtime/useRealtimeChatMessages.d.ts.map +1 -1
  33. package/dist/realtime/useRealtimeChatMessages.js +1 -3
  34. package/dist/realtime/useRealtimeChatMessages.js.map +1 -1
  35. package/dist/realtime/wire.d.ts +6 -5
  36. package/dist/realtime/wire.d.ts.map +1 -1
  37. package/dist/realtime/wire.js.map +1 -1
  38. package/dist/types.d.ts +1 -52
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/types.js.map +1 -1
  41. package/dist/ui/ChatShell.d.ts +2 -2
  42. package/dist/ui/ChatShell.d.ts.map +1 -1
  43. package/dist/ui/ChatShell.js +6 -10
  44. package/dist/ui/ChatShell.js.map +1 -1
  45. package/dist/ui/Composer.d.ts +7 -6
  46. package/dist/ui/Composer.d.ts.map +1 -1
  47. package/dist/ui/Composer.js +2 -18
  48. package/dist/ui/Composer.js.map +1 -1
  49. package/dist/ui/MessageItemDefault.d.ts.map +1 -1
  50. package/dist/ui/MessageItemDefault.js +7 -12
  51. package/dist/ui/MessageItemDefault.js.map +1 -1
  52. package/dist/ui/MessageText.d.ts +19 -0
  53. package/dist/ui/MessageText.d.ts.map +1 -0
  54. package/dist/ui/MessageText.js +7 -0
  55. package/dist/ui/MessageText.js.map +1 -0
  56. package/package.json +6 -6
  57. package/src/styles/chat.css +0 -40
  58. package/dist/mentions/MentionAutocomplete.d.ts +0 -22
  59. package/dist/mentions/MentionAutocomplete.d.ts.map +0 -1
  60. package/dist/mentions/MentionAutocomplete.js +0 -21
  61. package/dist/mentions/MentionAutocomplete.js.map +0 -1
  62. package/dist/mentions/MessageText.d.ts +0 -27
  63. package/dist/mentions/MessageText.d.ts.map +0 -1
  64. package/dist/mentions/MessageText.js +0 -20
  65. package/dist/mentions/MessageText.js.map +0 -1
  66. package/dist/mentions/use-mention-autocomplete.d.ts +0 -82
  67. package/dist/mentions/use-mention-autocomplete.d.ts.map +0 -1
  68. package/dist/mentions/use-mention-autocomplete.js +0 -323
  69. package/dist/mentions/use-mention-autocomplete.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItemDefault.js","sourceRoot":"","sources":["../../src/ui/MessageItemDefault.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport type { ChatMessageV1, ModerationHandlers } from \"@ttt-productions/chat-core\";\nimport { MessageText } from \"../mentions/MessageText.js\";\nimport { cn } from \"@ttt-productions/ui-core\";\nimport { Button } from \"@ttt-productions/ui-core/react\";\nimport { Loader2, AlertTriangle } from \"lucide-react\";\nimport { MessageActions } from \"./menus.js\";\nimport { useResolvedSenderName } from \"../context/ChatNameResolverContext.js\";\n\n// A chat message renders TEXT (plus its reply quote and send-state rows). Files\n// live in the conversation's Conversation Files surface, never in the timeline —\n// there is no attachment body here.\n\n// ============================================\n// Correlated send-rejection copy + retry policy\n// ============================================\n\n// User-facing copy per correlated `send-rejected` code (stamped on meta.sendFailureCode\n// by the realtime channel client). A failed send with no code (a transport/exhausted\n// reconnect failure) falls back to the neutral \"Couldn't send\".\nconst SEND_FAILURE_COPY: Record<string, string> = {\n \"blocked-word\": \"Message contains blocked language\",\n \"archived\": \"This channel is read-only\",\n \"deleted\": \"This channel is read-only\",\n \"membership-pending\": \"Chat is still preparing\",\n \"wordlist-unavailable\": \"Chat safety check is temporarily unavailable\",\n \"flood\": \"Please wait before sending again\",\n \"slow-mode\": \"Please wait before sending again\",\n};\n\nfunction sendFailureCopy(code: string | null): string {\n return (code && SEND_FAILURE_COPY[code]) || \"Couldn't send\";\n}\n\n// Retry policy comes from meta.sendRetryable, stamped by the channel client from\n// the wire contract's canonical retryability table (chat-schemas enforces the\n// frame's `retryable` matches the table, so this is the ONE source of truth —\n// never a second hardcoded code list here, which would have to move in lockstep\n// with the contract). `false` = terminal (an unchanged resend is guaranteed to\n// fail — no Retry action); `true` or ABSENT (a code-less transport/exhausted\n// reconnect failure) keeps Retry, reusing the ORIGINAL clientMessageId.\nfunction isRetryableFailure(meta: Record<string, unknown> | undefined): boolean {\n return meta?.sendRetryable !== false;\n}\n\n// ============================================\n// Reply-to quote\n// ============================================\n\nfunction ReplyQuote({ replyTo }: { replyTo: NonNullable<ChatMessageV1[\"replyTo\"]> }) {\n const replyName = useResolvedSenderName(replyTo.senderId);\n return (\n <div className=\"chat-reply-quote\">\n <span className=\"chat-reply-quote-sender\">{replyName}</span>\n <span className=\"chat-reply-quote-preview\"><MessageText text={replyTo.messagePreview} /></span>\n </div>\n );\n}\n\n// ============================================\n// System message\n// ============================================\n\n// System lines are the conversation's action record (agreed / retracted / offer updated /\n// finalized…), so each carries its time — same hour:minute format as the message bubbles.\nfunction SystemMessage({ m }: { m: ChatMessageV1 }) {\n return (\n <div className=\"chat-system-message\">\n {m.text && <span>{m.text}</span>}\n <span className=\"chat-system-message-time\">\n {new Date(m.createdAt).toLocaleTimeString([], { hour: \"numeric\", minute: \"2-digit\" })}\n </span>\n </div>\n );\n}\n\n// ============================================\n// Default message item\n// ============================================\n\nexport type MessageItemDefaultProps = {\n m: ChatMessageV1;\n currentUserId: string;\n isAdmin: boolean;\n handlers?: ModerationHandlers;\n\n // Grouping\n isContinuation?: boolean;\n\n // Sender interaction\n onSenderClick?: (senderId: string, displayName: string) => void;\n\n /** Retry a failed realtime send by its original clientMessageId (wired by\n * ChatShell on the realtime transport; absent on firestore). Enables the\n * retry affordance on a `meta.sendFailed` bubble. */\n onRetrySend?: (clientMessageId: string) => void;\n};\n\nexport function MessageItemDefault(props: MessageItemDefaultProps) {\n const { m, currentUserId, isAdmin, handlers, isContinuation, onSenderClick, onRetrySend } = props;\n const senderName = useResolvedSenderName(m.senderId);\n\n // System messages render differently\n if (m.isSystemMessage) {\n return <SystemMessage m={m} />;\n }\n\n const mine = m.senderId === currentUserId;\n\n // Realtime optimistic-send lifecycle (meta is stamped by the channel client):\n // an un-acked echo renders subtly pending; one past the retry/age cap renders a\n // visibly failed bubble with a retry affordance (same clientMessageId — the DO\n // dedups, so a retry can never double-send).\n const sendFailed = m.meta?.sendFailed === true;\n const sendPending = m.meta?.optimistic === true && !sendFailed;\n const clientMessageId =\n typeof m.meta?.clientMessageId === \"string\" ? m.meta.clientMessageId : null;\n // Correlated rejection reason (from the `send-rejected` frame), if any — drives\n // both the failure copy and whether a Retry affordance is offered.\n const sendFailureCode =\n typeof m.meta?.sendFailureCode === \"string\" ? m.meta.sendFailureCode : null;\n const showRetry = sendFailed && isRetryableFailure(m.meta);\n\n return (\n <div\n className={cn(\n \"flex flex-col w-fit max-w-[85%]\",\n mine ? \"ml-auto items-end\" : \"mr-auto items-start\",\n isContinuation ? \"chat-continuation-gap\" : \"chat-group-gap\"\n )}\n >\n <div\n className={cn(\n \"chat-bubble\",\n mine ? \"chat-bubble--mine\" : \"chat-bubble--theirs\",\n sendPending && \"chat-bubble--pending\",\n sendFailed && \"chat-bubble--failed\"\n )}\n >\n {!isContinuation && (\n <div className=\"flex items-center gap-2 text-xs opacity-80 mb-1\">\n <span\n className={cn(\"font-medium\", onSenderClick && \"cursor-pointer hover:underline\")}\n onClick={onSenderClick ? () => onSenderClick(m.senderId, senderName) : undefined}\n role={onSenderClick ? \"button\" : undefined}\n tabIndex={onSenderClick ? 0 : undefined}\n onKeyDown={onSenderClick ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") onSenderClick(m.senderId, senderName);\n } : undefined}\n >\n {senderName}\n </span>\n <span>·</span>\n <span>{new Date(m.createdAt).toLocaleTimeString([], { hour: \"numeric\", minute: \"2-digit\" })}</span>\n </div>\n )}\n\n {m.replyTo && <ReplyQuote replyTo={m.replyTo} />}\n\n {m.text && (\n <p className=\"text-sm whitespace-pre-wrap\">\n <MessageText text={m.text} />\n </p>\n )}\n </div>\n\n {/* Send-state status rows render regardless of grouping — a pending/failed\n send must never be indistinguishable from a delivered one. */}\n {sendPending && (\n <div className=\"chat-send-status mt-0.5\" role=\"status\" aria-live=\"polite\">\n <Loader2 className=\"h-3 w-3 shrink-0 animate-spin\" />\n <span>Sending…</span>\n </div>\n )}\n {sendFailed && (\n <div className=\"chat-send-status chat-send-status--failed mt-0.5\" role=\"alert\">\n <AlertTriangle className=\"h-3 w-3 shrink-0\" />\n <span>{sendFailureCopy(sendFailureCode)}</span>\n {showRetry && onRetrySend && clientMessageId && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-5 px-1.5 text-[0.6875rem]\"\n onClick={() => onRetrySend(clientMessageId)}\n >\n Retry\n </Button>\n )}\n </div>\n )}\n\n {!isContinuation && (\n <div className=\"mt-1\">\n <MessageActions messageId={m.messageId} isAdmin={isAdmin} handlers={handlers} />\n </div>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"MessageItemDefault.js","sourceRoot":"","sources":["../../src/ui/MessageItemDefault.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport type { ChatMessageV1, ModerationHandlers } from \"@ttt-productions/chat-core\";\nimport { MessageText } from \"./MessageText.js\";\nimport { cn } from \"@ttt-productions/ui-core\";\nimport { Button } from \"@ttt-productions/ui-core/react\";\nimport { Loader2, AlertTriangle } from \"lucide-react\";\nimport { MessageActions } from \"./menus.js\";\nimport { useResolvedSenderName } from \"../context/ChatNameResolverContext.js\";\n\n// A chat message renders TEXT (plus its send-state rows). Files live in the\n// conversation's Conversation Files surface, never in the timeline — there is no\n// attachment body here. There is no reply-quote body either: no chat surface has\n// an authoring affordance for replying to a specific message, so a quote\n// renderer would be unreachable machinery (DJ ruling 2026-07-29).\n\n// ============================================\n// Correlated send-rejection copy + retry policy\n// ============================================\n\n// User-facing copy per correlated `send-rejected` code (stamped on meta.sendFailureCode\n// by the realtime channel client). A failed send with no code (a transport/exhausted\n// reconnect failure) falls back to the neutral \"Couldn't send\".\nconst SEND_FAILURE_COPY: Record<string, string> = {\n \"blocked-word\": \"Message contains blocked language\",\n \"archived\": \"This channel is read-only\",\n \"deleted\": \"This channel is read-only\",\n \"membership-pending\": \"Chat is still preparing\",\n \"wordlist-unavailable\": \"Chat safety check is temporarily unavailable\",\n \"flood\": \"Please wait before sending again\",\n \"slow-mode\": \"Please wait before sending again\",\n};\n\nfunction sendFailureCopy(code: string | null): string {\n return (code && SEND_FAILURE_COPY[code]) || \"Couldn't send\";\n}\n\n// Retry policy comes from meta.sendRetryable, stamped by the channel client from\n// the wire contract's canonical retryability table (chat-schemas enforces the\n// frame's `retryable` matches the table, so this is the ONE source of truth —\n// never a second hardcoded code list here, which would have to move in lockstep\n// with the contract). `false` = terminal (an unchanged resend is guaranteed to\n// fail — no Retry action); `true` or ABSENT (a code-less transport/exhausted\n// reconnect failure) keeps Retry, reusing the ORIGINAL clientMessageId.\nfunction isRetryableFailure(meta: Record<string, unknown> | undefined): boolean {\n return meta?.sendRetryable !== false;\n}\n\n// ============================================\n// System message\n// ============================================\n\n// System lines are the conversation's action record (agreed / retracted / offer updated /\n// finalized…), so each carries its time — same hour:minute format as the message bubbles.\nfunction SystemMessage({ m }: { m: ChatMessageV1 }) {\n return (\n <div className=\"chat-system-message\">\n {m.text && <span>{m.text}</span>}\n <span className=\"chat-system-message-time\">\n {new Date(m.createdAt).toLocaleTimeString([], { hour: \"numeric\", minute: \"2-digit\" })}\n </span>\n </div>\n );\n}\n\n// ============================================\n// Default message item\n// ============================================\n\nexport type MessageItemDefaultProps = {\n m: ChatMessageV1;\n currentUserId: string;\n isAdmin: boolean;\n handlers?: ModerationHandlers;\n\n // Grouping\n isContinuation?: boolean;\n\n // Sender interaction\n onSenderClick?: (senderId: string, displayName: string) => void;\n\n /** Retry a failed realtime send by its original clientMessageId (wired by\n * ChatShell on the realtime transport; absent on firestore). Enables the\n * retry affordance on a `meta.sendFailed` bubble. */\n onRetrySend?: (clientMessageId: string) => void;\n};\n\nexport function MessageItemDefault(props: MessageItemDefaultProps) {\n const { m, currentUserId, isAdmin, handlers, isContinuation, onSenderClick, onRetrySend } = props;\n const senderName = useResolvedSenderName(m.senderId);\n\n // System messages render differently\n if (m.isSystemMessage) {\n return <SystemMessage m={m} />;\n }\n\n const mine = m.senderId === currentUserId;\n\n // Realtime optimistic-send lifecycle (meta is stamped by the channel client):\n // an un-acked echo renders subtly pending; one past the retry/age cap renders a\n // visibly failed bubble with a retry affordance (same clientMessageId — the DO\n // dedups, so a retry can never double-send).\n const sendFailed = m.meta?.sendFailed === true;\n const sendPending = m.meta?.optimistic === true && !sendFailed;\n const clientMessageId =\n typeof m.meta?.clientMessageId === \"string\" ? m.meta.clientMessageId : null;\n // Correlated rejection reason (from the `send-rejected` frame), if any — drives\n // both the failure copy and whether a Retry affordance is offered.\n const sendFailureCode =\n typeof m.meta?.sendFailureCode === \"string\" ? m.meta.sendFailureCode : null;\n const showRetry = sendFailed && isRetryableFailure(m.meta);\n\n return (\n <div\n className={cn(\n \"flex flex-col w-fit max-w-[85%]\",\n mine ? \"ml-auto items-end\" : \"mr-auto items-start\",\n isContinuation ? \"chat-continuation-gap\" : \"chat-group-gap\"\n )}\n >\n <div\n className={cn(\n \"chat-bubble\",\n mine ? \"chat-bubble--mine\" : \"chat-bubble--theirs\",\n sendPending && \"chat-bubble--pending\",\n sendFailed && \"chat-bubble--failed\"\n )}\n >\n {!isContinuation && (\n <div className=\"flex items-center gap-2 text-xs opacity-80 mb-1\">\n <span\n className={cn(\"font-medium\", onSenderClick && \"cursor-pointer hover:underline\")}\n onClick={onSenderClick ? () => onSenderClick(m.senderId, senderName) : undefined}\n role={onSenderClick ? \"button\" : undefined}\n tabIndex={onSenderClick ? 0 : undefined}\n onKeyDown={onSenderClick ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") onSenderClick(m.senderId, senderName);\n } : undefined}\n >\n {senderName}\n </span>\n <span>·</span>\n <span>{new Date(m.createdAt).toLocaleTimeString([], { hour: \"numeric\", minute: \"2-digit\" })}</span>\n </div>\n )}\n\n {m.text && (\n <p className=\"text-sm whitespace-pre-wrap\">\n <MessageText text={m.text} />\n </p>\n )}\n </div>\n\n {/* Send-state status rows render regardless of grouping — a pending/failed\n send must never be indistinguishable from a delivered one. */}\n {sendPending && (\n <div className=\"chat-send-status mt-0.5\" role=\"status\" aria-live=\"polite\">\n <Loader2 className=\"h-3 w-3 shrink-0 animate-spin\" />\n <span>Sending…</span>\n </div>\n )}\n {sendFailed && (\n <div className=\"chat-send-status chat-send-status--failed mt-0.5\" role=\"alert\">\n <AlertTriangle className=\"h-3 w-3 shrink-0\" />\n <span>{sendFailureCopy(sendFailureCode)}</span>\n {showRetry && onRetrySend && clientMessageId && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-5 px-1.5 text-[0.6875rem]\"\n onClick={() => onRetrySend(clientMessageId)}\n >\n Retry\n </Button>\n )}\n </div>\n )}\n\n {!isContinuation && (\n <div className=\"mt-1\">\n <MessageActions messageId={m.messageId} isAdmin={isAdmin} handlers={handlers} />\n </div>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Renders message text.
3
+ *
4
+ * Chat text is PLAIN text — there is no inline token grammar, no mention
5
+ * parsing, and no rich-text pass. The component exists so every chat text
6
+ * surface (message bubbles) renders through one place with one wrapper element
7
+ * and one optional className.
8
+ *
9
+ * Pure, no context. Safe to use inside MessageItem renderers or any other chat
10
+ * text surface.
11
+ */
12
+ export interface MessageTextProps {
13
+ /** Raw message text. Rendered verbatim. */
14
+ text: string;
15
+ /** Optional className applied to the wrapping `<span>`. */
16
+ className?: string;
17
+ }
18
+ export declare function MessageText(props: MessageTextProps): import("react").JSX.Element;
19
+ //# sourceMappingURL=MessageText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageText.d.ts","sourceRoot":"","sources":["../../src/ui/MessageText.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAGlD"}
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ export function MessageText(props) {
4
+ const { text, className } = props;
5
+ return _jsx("span", { className: className, children: text });
6
+ }
7
+ //# sourceMappingURL=MessageText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageText.js","sourceRoot":"","sources":["../../src/ui/MessageText.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAqBb,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAClC,OAAO,eAAM,SAAS,EAAE,SAAS,YAAG,IAAI,GAAQ,CAAC;AACnD,CAAC","sourcesContent":["'use client';\n\n/**\n * Renders message text.\n *\n * Chat text is PLAIN text — there is no inline token grammar, no mention\n * parsing, and no rich-text pass. The component exists so every chat text\n * surface (message bubbles) renders through one place with one wrapper element\n * and one optional className.\n *\n * Pure, no context. Safe to use inside MessageItem renderers or any other chat\n * text surface.\n */\n\nexport interface MessageTextProps {\n /** Raw message text. Rendered verbatim. */\n text: string;\n /** Optional className applied to the wrapping `<span>`. */\n className?: string;\n}\n\nexport function MessageText(props: MessageTextProps) {\n const { text, className } = props;\n return <span className={className}>{text}</span>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/chat-react",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
4
4
  "description": "Chat React UI, hooks, and Firebase-client adapter for TTT Productions apps (realtime newest window + infinite older pagination)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -60,13 +60,13 @@
60
60
  }
61
61
  },
62
62
  "dependencies": {
63
- "@ttt-productions/chat-core": "^0.11.0",
64
- "@ttt-productions/chat-schemas": "^0.10.0",
63
+ "@ttt-productions/chat-core": "^0.11.1",
64
+ "@ttt-productions/chat-schemas": "^0.10.1",
65
65
  "@ttt-productions/realtime-core": "^0.1.3",
66
- "@ttt-productions/ui-core": "^0.11.10",
67
- "@ttt-productions/upload-ui": "^0.7.5",
66
+ "@ttt-productions/ui-core": "^0.11.12",
67
+ "@ttt-productions/upload-ui": "^0.7.8",
68
68
  "@ttt-productions/mobile-core": "^0.9.1",
69
- "@ttt-productions/firebase-helpers": "^0.12.3"
69
+ "@ttt-productions/firebase-helpers": "^0.12.5"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@ttt-productions/query-core": "^0.14.1",
@@ -78,32 +78,6 @@
78
78
  white-space: nowrap;
79
79
  }
80
80
 
81
- /* --- Reply-to quote --- */
82
- .chat-reply-quote {
83
- display: flex;
84
- flex-direction: column;
85
- gap: 0.125rem;
86
- padding: 0.375rem 0.5rem;
87
- margin-bottom: 0.375rem;
88
- border-left: 2px solid hsl(var(--border));
89
- border-radius: 0.125rem;
90
- background-color: hsl(var(--muted) / 0.5);
91
- font-size: 0.75rem;
92
- }
93
-
94
- .chat-reply-quote-sender {
95
- font-weight: 600;
96
- color: hsl(var(--foreground));
97
- }
98
-
99
- .chat-reply-quote-preview {
100
- color: hsl(var(--muted-foreground));
101
- overflow: hidden;
102
- text-overflow: ellipsis;
103
- white-space: nowrap;
104
- max-width: 280px;
105
- }
106
-
107
81
  /* --- Composer --- */
108
82
  .chat-composer {
109
83
  display: flex;
@@ -111,17 +85,3 @@
111
85
  gap: 0.5rem;
112
86
  width: 100%;
113
87
  }
114
-
115
- /* Mention system — default chip styling. Consumers override via custom
116
- renderMention or by targeting .chat-mention-chip directly. */
117
- .chat-mention-chip {
118
- color: hsl(var(--primary));
119
- font-weight: 500;
120
- cursor: pointer;
121
- }
122
- .chat-mention-chip:hover {
123
- text-decoration: underline;
124
- }
125
- .chat-mention-autocomplete {
126
- /* container styles applied by ui-core via Tailwind classes in the component */
127
- }
@@ -1,22 +0,0 @@
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.Element | null;
22
- //# sourceMappingURL=MentionAutocomplete.d.ts.map
@@ -1 +0,0 @@
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,sCAoEvC"}
@@ -1,21 +0,0 @@
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
@@ -1 +0,0 @@
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","sourcesContent":["'use client';\n\n/**\n * Visual autocomplete dropdown for the mention system.\n *\n * Pure presentation — driven entirely by `state` from `useMentionAutocomplete`.\n * The consumer is expected to absolutely-position this near the textarea (or\n * use its caret position via a wrapping container). chat-core does not\n * compute textarea-caret coordinates; that's the consumer's job.\n *\n * Selecting a result fires `onSelect(ref)`. Hovering / arrow-keying updates\n * the highlight via `onHighlight(index)`.\n */\n\nimport type { AutocompleteState } from './use-mention-autocomplete.js';\nimport type { MentionRef } from '@ttt-productions/chat-core';\nimport { cn } from '@ttt-productions/ui-core';\n\nexport interface MentionAutocompleteProps<TKind extends string = string> {\n state: AutocompleteState<TKind>;\n onSelect: (ref: MentionRef<TKind>) => void;\n onHighlight?: (flatIndex: number) => void;\n /** Class name applied to the outer container. */\n className?: string;\n}\n\nexport function MentionAutocomplete<TKind extends string = string>(\n props: MentionAutocompleteProps<TKind>,\n) {\n const { state, onSelect, onHighlight, className } = props;\n if (!state.open) return null;\n\n const anyResults = state.flatResults.length > 0;\n const anyLoading = state.groups.some((g) => g.loading);\n\n let flatIndex = 0;\n\n return (\n <div\n role=\"listbox\"\n aria-label=\"Mentions\"\n className={cn(\n 'chat-mention-autocomplete',\n 'rounded-md border bg-popover text-popover-foreground shadow-md',\n 'max-h-72 overflow-y-auto min-w-[16rem]',\n className,\n )}\n >\n {!anyResults && anyLoading && (\n <div className=\"px-3 py-2 text-xs text-muted-foreground\">Searching…</div>\n )}\n {!anyResults && !anyLoading && (\n <div className=\"px-3 py-2 text-xs text-muted-foreground\">\n {state.showingRecent ? 'No recent mentions' : 'No matches'}\n </div>\n )}\n {state.groups.map((group) => (\n <div key={group.kind} className=\"chat-mention-group\">\n <div className=\"px-3 pt-2 pb-1 text-xs font-semibold text-muted-foreground\">\n {state.showingRecent ? `Recent ${group.label}` : group.label}\n </div>\n {group.loading && group.results.length === 0 && (\n <div className=\"px-3 pb-2 text-xs text-muted-foreground\">Loading…</div>\n )}\n <ul>\n {group.results.map((ref) => {\n const myIndex = flatIndex;\n flatIndex += 1;\n const highlighted = myIndex === state.highlightedIndex;\n return (\n <li key={`${ref.kind}:${ref.id}`}>\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={highlighted}\n className={cn(\n 'w-full text-left px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground',\n highlighted && 'bg-accent text-accent-foreground',\n )}\n onMouseEnter={() => onHighlight?.(myIndex)}\n onClick={(e) => {\n e.preventDefault();\n onSelect(ref);\n }}\n >\n {group.renderResult ? group.renderResult(ref) : <span>@{ref.displayText}</span>}\n </button>\n </li>\n );\n })}\n </ul>\n </div>\n ))}\n </div>\n );\n}\n"]}
@@ -1,27 +0,0 @@
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.Element;
27
- //# sourceMappingURL=MessageText.d.ts.map
@@ -1 +0,0 @@
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,+BAiBxF"}
@@ -1,20 +0,0 @@
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
@@ -1 +0,0 @@
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","sourcesContent":["'use client';\n\n/**\n * Renders message text with inline mention chips.\n *\n * Walks the parser output and renders each segment: text segments as\n * `<span>`, mention segments via the consumer-supplied `renderMention`\n * callback (or a default chip when omitted).\n *\n * Pure, no context. Safe to use inside MessageItem renderers, ReplyQuote\n * previews, or any other text surface that should honor mention tokens.\n */\n\nimport type { ReactNode } from 'react';\nimport type { MentionRef } from '@ttt-productions/chat-core';\nimport { parseMentionTokens } from '@ttt-productions/chat-core';\n\nexport interface MessageTextProps<TKind extends string = string> {\n /** Raw message text containing optional `@[kind:id|displayText]` tokens. */\n text: string;\n /**\n * Custom mention renderer. When omitted, mentions render as a\n * `<span class=\"chat-mention-chip\">@{displayText}</span>` chip — consumers\n * style via CSS. To make mentions clickable / linked / role-aware, supply\n * a renderMention.\n */\n renderMention?: (ref: MentionRef<TKind>) => ReactNode;\n /** Optional className applied to the wrapping `<span>`. */\n className?: string;\n}\n\nfunction DefaultMentionChip<TKind extends string>({ ref }: { ref: MentionRef<TKind> }) {\n return (\n <span className=\"chat-mention-chip\" data-mention-kind={ref.kind} data-mention-id={ref.id}>\n @{ref.displayText}\n </span>\n );\n}\n\nexport function MessageText<TKind extends string = string>(props: MessageTextProps<TKind>) {\n const { text, renderMention, className } = props;\n const segments = parseMentionTokens<TKind>(text);\n\n return (\n <span className={className}>\n {segments.map((seg, i) => {\n if (seg.type === 'text') {\n return <span key={i}>{seg.text}</span>;\n }\n if (renderMention) {\n return <span key={i}>{renderMention(seg.ref)}</span>;\n }\n return <DefaultMentionChip key={i} ref={seg.ref} />;\n })}\n </span>\n );\n}\n"]}
@@ -1,82 +0,0 @@
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
@@ -1 +0,0 @@
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"}