@stapel/chat-react 0.1.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 (195) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/MODULE.md +118 -0
  3. package/README.md +126 -0
  4. package/dist/api/chatApi.d.ts +49 -0
  5. package/dist/api/chatApi.d.ts.map +1 -0
  6. package/dist/api/chatApi.js +46 -0
  7. package/dist/api/chatApi.js.map +1 -0
  8. package/dist/api/extensions.d.ts +19 -0
  9. package/dist/api/extensions.d.ts.map +1 -0
  10. package/dist/api/extensions.js +19 -0
  11. package/dist/api/extensions.js.map +1 -0
  12. package/dist/api/generated/schema.d.ts +547 -0
  13. package/dist/api/generated/schema.d.ts.map +1 -0
  14. package/dist/api/generated/schema.js +2 -0
  15. package/dist/api/generated/schema.js.map +1 -0
  16. package/dist/api/types.d.ts +75 -0
  17. package/dist/api/types.d.ts.map +1 -0
  18. package/dist/api/types.js +2 -0
  19. package/dist/api/types.js.map +1 -0
  20. package/dist/default/ConversationListPanel.d.ts +22 -0
  21. package/dist/default/ConversationListPanel.d.ts.map +1 -0
  22. package/dist/default/ConversationListPanel.js +49 -0
  23. package/dist/default/ConversationListPanel.js.map +1 -0
  24. package/dist/default/ConversationThreadPanel.d.ts +21 -0
  25. package/dist/default/ConversationThreadPanel.d.ts.map +1 -0
  26. package/dist/default/ConversationThreadPanel.js +51 -0
  27. package/dist/default/ConversationThreadPanel.js.map +1 -0
  28. package/dist/default/ErrorAlert.d.ts +8 -0
  29. package/dist/default/ErrorAlert.d.ts.map +1 -0
  30. package/dist/default/ErrorAlert.js +26 -0
  31. package/dist/default/ErrorAlert.js.map +1 -0
  32. package/dist/default/StartChatButton.d.ts +17 -0
  33. package/dist/default/StartChatButton.d.ts.map +1 -0
  34. package/dist/default/StartChatButton.js +18 -0
  35. package/dist/default/StartChatButton.js.map +1 -0
  36. package/dist/default/index.d.ts +21 -0
  37. package/dist/default/index.d.ts.map +1 -0
  38. package/dist/default/index.js +18 -0
  39. package/dist/default/index.js.map +1 -0
  40. package/dist/flows/errors.d.ts +12 -0
  41. package/dist/flows/errors.d.ts.map +1 -0
  42. package/dist/flows/errors.js +13 -0
  43. package/dist/flows/errors.js.map +1 -0
  44. package/dist/flows/freshness.d.ts +69 -0
  45. package/dist/flows/freshness.d.ts.map +1 -0
  46. package/dist/flows/freshness.js +227 -0
  47. package/dist/flows/freshness.js.map +1 -0
  48. package/dist/flows/registry.d.ts +26 -0
  49. package/dist/flows/registry.d.ts.map +1 -0
  50. package/dist/flows/registry.js +24 -0
  51. package/dist/flows/registry.js.map +1 -0
  52. package/dist/headless/ChatProvider.d.ts +22 -0
  53. package/dist/headless/ChatProvider.d.ts.map +1 -0
  54. package/dist/headless/ChatProvider.js +18 -0
  55. package/dist/headless/ChatProvider.js.map +1 -0
  56. package/dist/headless/ConversationList.d.ts +51 -0
  57. package/dist/headless/ConversationList.d.ts.map +1 -0
  58. package/dist/headless/ConversationList.js +47 -0
  59. package/dist/headless/ConversationList.js.map +1 -0
  60. package/dist/headless/ConversationThread.d.ts +50 -0
  61. package/dist/headless/ConversationThread.d.ts.map +1 -0
  62. package/dist/headless/ConversationThread.js +64 -0
  63. package/dist/headless/ConversationThread.js.map +1 -0
  64. package/dist/headless/MessageComposer.d.ts +39 -0
  65. package/dist/headless/MessageComposer.d.ts.map +1 -0
  66. package/dist/headless/MessageComposer.js +45 -0
  67. package/dist/headless/MessageComposer.js.map +1 -0
  68. package/dist/headless/StartDirectChat.d.ts +40 -0
  69. package/dist/headless/StartDirectChat.d.ts.map +1 -0
  70. package/dist/headless/StartDirectChat.js +46 -0
  71. package/dist/headless/StartDirectChat.js.map +1 -0
  72. package/dist/i18n/errorsMap.d.ts +10 -0
  73. package/dist/i18n/errorsMap.d.ts.map +1 -0
  74. package/dist/i18n/errorsMap.js +20 -0
  75. package/dist/i18n/errorsMap.js.map +1 -0
  76. package/dist/i18n/es.d.ts +23 -0
  77. package/dist/i18n/es.d.ts.map +1 -0
  78. package/dist/i18n/es.js +75 -0
  79. package/dist/i18n/es.js.map +1 -0
  80. package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
  81. package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
  82. package/dist/i18n/generated/errors.es.gen.js +58 -0
  83. package/dist/i18n/generated/errors.es.gen.js.map +1 -0
  84. package/dist/i18n/generated/errors.gen.d.ts +359 -0
  85. package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
  86. package/dist/i18n/generated/errors.gen.js +183 -0
  87. package/dist/i18n/generated/errors.gen.js.map +1 -0
  88. package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
  89. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
  90. package/dist/i18n/generated/errors.ru.gen.js +58 -0
  91. package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
  92. package/dist/i18n/keys.d.ts +78 -0
  93. package/dist/i18n/keys.d.ts.map +1 -0
  94. package/dist/i18n/keys.js +130 -0
  95. package/dist/i18n/keys.js.map +1 -0
  96. package/dist/i18n/ru.d.ts +28 -0
  97. package/dist/i18n/ru.d.ts.map +1 -0
  98. package/dist/i18n/ru.js +80 -0
  99. package/dist/i18n/ru.js.map +1 -0
  100. package/dist/index.d.ts +61 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +55 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/model/context.d.ts +11 -0
  105. package/dist/model/context.d.ts.map +1 -0
  106. package/dist/model/context.js +16 -0
  107. package/dist/model/context.js.map +1 -0
  108. package/dist/model/limits.d.ts +12 -0
  109. package/dist/model/limits.d.ts.map +1 -0
  110. package/dist/model/limits.js +12 -0
  111. package/dist/model/limits.js.map +1 -0
  112. package/dist/model/mutations.d.ts +66 -0
  113. package/dist/model/mutations.d.ts.map +1 -0
  114. package/dist/model/mutations.js +156 -0
  115. package/dist/model/mutations.js.map +1 -0
  116. package/dist/model/queries.d.ts +33 -0
  117. package/dist/model/queries.d.ts.map +1 -0
  118. package/dist/model/queries.js +93 -0
  119. package/dist/model/queries.js.map +1 -0
  120. package/dist/model/queryKeys.d.ts +8 -0
  121. package/dist/model/queryKeys.d.ts.map +1 -0
  122. package/dist/model/queryKeys.js +26 -0
  123. package/dist/model/queryKeys.js.map +1 -0
  124. package/dist/model/readMarker.d.ts +25 -0
  125. package/dist/model/readMarker.d.ts.map +1 -0
  126. package/dist/model/readMarker.js +31 -0
  127. package/dist/model/readMarker.js.map +1 -0
  128. package/dist/model/runtime.d.ts +50 -0
  129. package/dist/model/runtime.d.ts.map +1 -0
  130. package/dist/model/runtime.js +27 -0
  131. package/dist/model/runtime.js.map +1 -0
  132. package/dist/model/threadWindow.d.ts +95 -0
  133. package/dist/model/threadWindow.d.ts.map +1 -0
  134. package/dist/model/threadWindow.js +100 -0
  135. package/dist/model/threadWindow.js.map +1 -0
  136. package/dist/nav/manifest.d.ts +23 -0
  137. package/dist/nav/manifest.d.ts.map +1 -0
  138. package/dist/nav/manifest.js +15 -0
  139. package/dist/nav/manifest.js.map +1 -0
  140. package/dist/realtime/chatSocket.d.ts +93 -0
  141. package/dist/realtime/chatSocket.d.ts.map +1 -0
  142. package/dist/realtime/chatSocket.js +199 -0
  143. package/dist/realtime/chatSocket.js.map +1 -0
  144. package/dist/realtime/frames.d.ts +123 -0
  145. package/dist/realtime/frames.d.ts.map +1 -0
  146. package/dist/realtime/frames.js +123 -0
  147. package/dist/realtime/frames.js.map +1 -0
  148. package/dist/realtime/streams.d.ts +49 -0
  149. package/dist/realtime/streams.d.ts.map +1 -0
  150. package/dist/realtime/streams.js +52 -0
  151. package/dist/realtime/streams.js.map +1 -0
  152. package/llms.txt +89 -0
  153. package/manifest.json +733 -0
  154. package/nav-manifest.json +25 -0
  155. package/package.json +114 -0
  156. package/src/analytics/generated/events.json +7 -0
  157. package/src/api/chatApi.ts +142 -0
  158. package/src/api/extensions.ts +18 -0
  159. package/src/api/generated/schema.ts +549 -0
  160. package/src/api/types.ts +87 -0
  161. package/src/default/ConversationListPanel.tsx +163 -0
  162. package/src/default/ConversationThreadPanel.tsx +245 -0
  163. package/src/default/ErrorAlert.tsx +43 -0
  164. package/src/default/StartChatButton.tsx +78 -0
  165. package/src/default/index.ts +20 -0
  166. package/src/flows/errors.ts +16 -0
  167. package/src/flows/freshness.ts +315 -0
  168. package/src/flows/registry.ts +36 -0
  169. package/src/headless/ChatProvider.tsx +23 -0
  170. package/src/headless/ConversationList.tsx +92 -0
  171. package/src/headless/ConversationThread.tsx +107 -0
  172. package/src/headless/MessageComposer.tsx +86 -0
  173. package/src/headless/StartDirectChat.tsx +75 -0
  174. package/src/i18n/errorsMap.ts +31 -0
  175. package/src/i18n/es.ts +95 -0
  176. package/src/i18n/generated/errors.es.gen.ts +64 -0
  177. package/src/i18n/generated/errors.gen.ts +214 -0
  178. package/src/i18n/generated/errors.json +408 -0
  179. package/src/i18n/generated/errors.ru.gen.ts +64 -0
  180. package/src/i18n/keys.ts +152 -0
  181. package/src/i18n/ru.ts +98 -0
  182. package/src/index.ts +189 -0
  183. package/src/model/context.tsx +25 -0
  184. package/src/model/limits.ts +11 -0
  185. package/src/model/mutations.ts +208 -0
  186. package/src/model/queries.ts +120 -0
  187. package/src/model/queryKeys.ts +32 -0
  188. package/src/model/readMarker.ts +32 -0
  189. package/src/model/runtime.ts +90 -0
  190. package/src/model/threadWindow.ts +173 -0
  191. package/src/nav/manifest.ts +36 -0
  192. package/src/realtime/chatSocket.ts +304 -0
  193. package/src/realtime/frames.ts +229 -0
  194. package/src/realtime/streams.ts +85 -0
  195. package/tsconfig.json +26 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `<ConversationListPanel/>` — the default skin for the inbox, and the screen
3
+ * this pair's nav manifest mounts (`chat.conversations`, member surface).
4
+ *
5
+ * Built entirely on the headless `<ConversationList>`: this file makes visual
6
+ * decisions and nothing else.
7
+ */
8
+ import type { ReactElement } from "react";
9
+ export interface ConversationListPanelProps {
10
+ /** Page size for the underlying list. */
11
+ limit?: number;
12
+ /**
13
+ * Where a row leads, as an href — the SSR-friendly half. A storefront
14
+ * renders real links so a conversation is right-clickable and indexable by
15
+ * the browser's own history, not only by a click handler.
16
+ */
17
+ openHref?: (conversationId: string) => string;
18
+ /** Where a row leads, in a SPA. Used when `openHref` is not given. */
19
+ onOpen?: (conversationId: string) => void;
20
+ }
21
+ export declare function ConversationListPanel(props?: ConversationListPanelProps): ReactElement;
22
+ //# sourceMappingURL=ConversationListPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationListPanel.d.ts","sourceRoot":"","sources":["../../src/default/ConversationListPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAc1C,MAAM,WAAW,0BAA0B;IACzC,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,sEAAsE;IACtE,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAaD,wBAAgB,qBAAqB,CACnC,KAAK,GAAE,0BAA+B,GACrC,YAAY,CAmHd"}
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Badge, Button, Card, Empty, List, Space, Spin, Typography } from "antd";
3
+ import { matchList, useErrorDisplay, useI18n, useT } from "@stapel/core";
4
+ import { ConversationList } from "../headless/ConversationList.js";
5
+ import { CHAT_I18N_KEYS } from "../i18n/keys.js";
6
+ import { ErrorAlert } from "./ErrorAlert.js";
7
+ const KIND_KEYS = {
8
+ direct: CHAT_I18N_KEYS.kindDirect,
9
+ group: CHAT_I18N_KEYS.kindGroup,
10
+ support: CHAT_I18N_KEYS.kindSupport,
11
+ };
12
+ function relativeTime(locale, iso) {
13
+ // A timestamp is data, not copy: `Intl` localizes it from the host's own
14
+ // locale, so it needs no i18n key and cannot go stale in a catalogue.
15
+ const parsed = Date.parse(iso);
16
+ if (Number.isNaN(parsed))
17
+ return "";
18
+ return new Intl.DateTimeFormat(locale, {
19
+ dateStyle: "short",
20
+ timeStyle: "short",
21
+ }).format(parsed);
22
+ }
23
+ export function ConversationListPanel(props = {}) {
24
+ const t = useT();
25
+ const { locale } = useI18n();
26
+ // Never the raw `.message` — for a response with no error envelope that is
27
+ // the transport's own "Request failed with status 500".
28
+ const errorDisplay = useErrorDisplay(CHAT_I18N_KEYS.unknownError);
29
+ const { openHref, onOpen } = props;
30
+ const renderRow = (row) => {
31
+ const kindKey = KIND_KEYS[row.kind] ?? CHAT_I18N_KEYS.kindDirect;
32
+ const title = t(kindKey);
33
+ return (_jsx(List.Item, { "data-testid": "chat-conversation-row", extra: row.unread_count > 0 ? (_jsx(Badge, { count: row.unread_count,
34
+ // The badge is a number; the accessible sentence is the key.
35
+ title: t(CHAT_I18N_KEYS.listUnread, { count: row.unread_count }) })) : null, children: _jsx(List.Item.Meta, { title: openHref ? (_jsx(Typography.Link, { href: openHref(row.id), children: title })) : onOpen ? (_jsx(Button, { type: "link", style: { padding: 0 }, onClick: () => onOpen(row.id), "data-analytics": "none", "data-analytics-reason": "navigation into a thread \u2014 the host app wraps this with its own tracked(); pairs carry no @stapel/analytics runtime dependency by architecture", children: title })) : (
36
+ // Neither route given: a title, not a control that goes
37
+ // nowhere. A dead affordance is worse than none.
38
+ _jsx(Typography.Text, { strong: true, children: title })), description: _jsx(Typography.Text, { type: "secondary", children: relativeTime(locale, row.updated_at) }) }) }));
39
+ };
40
+ return (_jsx(ConversationList, { ...(props.limit !== undefined ? { limit: props.limit } : {}), children: ({ state, hasNextPage, isFetchingNextPage, fetchNextPage, refetch }) => (_jsxs(Card, { "data-testid": "chat-conversation-list", children: [_jsx(Typography.Title, { level: 4, style: { marginTop: 0 }, children: t(CHAT_I18N_KEYS.listTitle) }), matchList(state, {
41
+ loading: () => _jsx(Spin, { style: { marginTop: 16 } }),
42
+ // One arm owns the failure; the empty copy is unreachable from
43
+ // here, so an outage can never render as "no conversations yet".
44
+ failed: (error) => (_jsxs("div", { style: { marginTop: 16 }, "data-testid": "chat-conversation-list-error", children: [_jsx(ErrorAlert, { error: errorDisplay(error) }), _jsx(Button, { style: { marginTop: 12 }, onClick: refetch, "data-analytics": "none", "data-analytics-reason": "recovery affordance for a failed read \u2014 host app wraps with its own tracked()", children: t(CHAT_I18N_KEYS.listRetry) })] })),
45
+ empty: () => (_jsx(Empty, { style: { marginTop: 16 }, "data-testid": "chat-conversation-list-empty", description: t(CHAT_I18N_KEYS.listEmpty) })),
46
+ ready: (rows) => (_jsxs(Space, { direction: "vertical", style: { width: "100%" }, children: [_jsx(List, { style: { marginTop: 16 }, dataSource: [...rows], rowKey: (row) => row.id, renderItem: renderRow }), hasNextPage ? (_jsx(Button, { loading: isFetchingNextPage, onClick: () => fetchNextPage(), "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: t(CHAT_I18N_KEYS.listLoadMore) })) : (_jsx(Typography.Text, { type: "secondary", children: t(CHAT_I18N_KEYS.listEnd) }))] })),
47
+ })] })) }));
48
+ }
49
+ //# sourceMappingURL=ConversationListPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationListPanel.js","sourceRoot":"","sources":["../../src/default/ConversationListPanel.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `<ConversationThreadPanel/>` — the default skin for one thread: the messages,
3
+ * the "show earlier" control, a live/polling indicator, and the composer.
4
+ *
5
+ * The transport indicator is the ONLY place a person is told how freshness
6
+ * arrives, and it is a label, never a behaviour: nothing else on this screen
7
+ * branches on it. That is the seam holding — the same component renders a
8
+ * socket-fed thread and a polled one.
9
+ */
10
+ import type { ReactElement } from "react";
11
+ export interface ConversationThreadPanelProps {
12
+ conversationId: string;
13
+ /** The reader, when the host knows them — used only to align their own
14
+ * lines. Absent, every message is rendered the same way rather than
15
+ * guessed at. */
16
+ viewerId?: string | null;
17
+ limit?: number;
18
+ maxLength?: number;
19
+ }
20
+ export declare function ConversationThreadPanel(props: ConversationThreadPanelProps): ReactElement;
21
+ //# sourceMappingURL=ConversationThreadPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationThreadPanel.d.ts","sourceRoot":"","sources":["../../src/default/ConversationThreadPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,OAAO,CAAC;AAsBzD,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB;;qBAEiB;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAwHD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,GAClC,YAAY,CAmFd"}
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Card, Empty, Flex, Input, Space, Spin, Tag, Typography } from "antd";
3
+ import { matchList, useActionGate, useErrorDisplay, useI18n, useT, } from "@stapel/core";
4
+ import { ConversationThread } from "../headless/ConversationThread.js";
5
+ import { MessageComposer } from "../headless/MessageComposer.js";
6
+ import { CHAT_I18N_KEYS } from "../i18n/keys.js";
7
+ import { ErrorAlert } from "./ErrorAlert.js";
8
+ const TRANSPORT_KEYS = {
9
+ socket: CHAT_I18N_KEYS.transportLive,
10
+ polling: CHAT_I18N_KEYS.transportPolling,
11
+ idle: CHAT_I18N_KEYS.transportIdle,
12
+ };
13
+ function MessageRow(props) {
14
+ const t = useT();
15
+ const { message, viewerId, locale } = props;
16
+ const isSystem = message.kind === "system";
17
+ const isOwn = !isSystem && viewerId != null && message.sender_id === viewerId;
18
+ const bubble = {
19
+ alignSelf: isOwn ? "flex-end" : "flex-start",
20
+ maxWidth: "42rem",
21
+ };
22
+ const stamp = Number.isNaN(Date.parse(message.created_at))
23
+ ? ""
24
+ : new Intl.DateTimeFormat(locale, { timeStyle: "short" }).format(Date.parse(message.created_at));
25
+ return (_jsxs("div", { style: bubble, "data-testid": "chat-message", "data-seq": message.seq, children: [isSystem ? (_jsx(Typography.Text, { type: "secondary", children: t(CHAT_I18N_KEYS.threadSystem) })) : null, _jsx(Typography.Paragraph, { style: { marginBottom: 0 }, children: message.body }), _jsx(Typography.Text, { type: "secondary", style: { fontSize: 12 }, children: stamp })] }));
26
+ }
27
+ function Composer(props) {
28
+ const t = useT();
29
+ const errorDisplay = useErrorDisplay(CHAT_I18N_KEYS.unknownError);
30
+ return (_jsx(MessageComposer, { conversationId: props.conversationId, ...(props.maxLength !== undefined ? { maxLength: props.maxLength } : {}), children: ({ value, setValue, availability, isSending, error, send, maxLength, length }) => (_jsx(ComposerBody, { value: value, setValue: setValue, isSending: isSending, send: send, maxLength: maxLength, length: length, availability: availability, errorNode: _jsx(ErrorAlert, { error: errorDisplay(error) }), t: t })) }));
31
+ }
32
+ /** Split out so `useActionGate` is called from a component, not a callback. */
33
+ function ComposerBody(props) {
34
+ const gate = useActionGate(props.availability);
35
+ const { t } = props;
36
+ return (_jsxs(Space, { direction: "vertical", style: { width: "100%" }, children: [props.errorNode, _jsx(Input.TextArea, { value: props.value, onChange: (event) => props.setValue(event.target.value), placeholder: t(CHAT_I18N_KEYS.composerPlaceholder), autoSize: { minRows: 2, maxRows: 6 }, "data-testid": "chat-composer-input" }), _jsxs(Flex, { align: "center", gap: 12, wrap: "wrap", children: [_jsx(Button, { type: "primary", disabled: gate.disabled, loading: props.isSending, onClick: props.send, "data-testid": "chat-composer-send", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: props.isSending
37
+ ? t(CHAT_I18N_KEYS.composerSending)
38
+ : t(CHAT_I18N_KEYS.composerSend) }), gate.reason ? (_jsx(Typography.Text, { type: "secondary", "data-testid": "chat-composer-blocked", children: gate.reason })) : null, _jsx(Typography.Text, { type: "secondary", style: { marginLeft: "auto" }, children: `${props.length}/${props.maxLength}` })] })] }));
39
+ }
40
+ export function ConversationThreadPanel(props) {
41
+ const t = useT();
42
+ const { locale } = useI18n();
43
+ const errorDisplay = useErrorDisplay(CHAT_I18N_KEYS.unknownError);
44
+ return (_jsx(ConversationThread, { conversationId: props.conversationId, ...(props.limit !== undefined ? { limit: props.limit } : {}), children: ({ state, hasOlder, isLoadingOlder, loadOlder, refetch, transport }) => (_jsxs(Card, { "data-testid": "chat-thread", children: [_jsxs(Flex, { justify: "space-between", align: "center", style: { marginBottom: 12 }, children: [_jsx(Typography.Title, { level: 4, style: { margin: 0 }, children: t(CHAT_I18N_KEYS.listTitle) }), _jsx(Tag, { "data-testid": "chat-transport", "data-transport": transport, children: t(TRANSPORT_KEYS[transport]) })] }), matchList(state, {
45
+ loading: () => _jsx(Spin, {}),
46
+ failed: (error) => (_jsxs("div", { "data-testid": "chat-thread-error", children: [_jsx(ErrorAlert, { error: errorDisplay(error) }), _jsx(Button, { style: { marginTop: 12 }, onClick: refetch, "data-analytics": "none", "data-analytics-reason": "recovery affordance for a failed read \u2014 host app wraps with its own tracked()", children: t(CHAT_I18N_KEYS.threadRetry) })] })),
47
+ empty: () => (_jsx(Empty, { "data-testid": "chat-thread-empty", description: t(CHAT_I18N_KEYS.threadEmpty) })),
48
+ ready: (messages) => (_jsxs(Space, { direction: "vertical", style: { width: "100%" }, children: [hasOlder ? (_jsx(Button, { loading: isLoadingOlder, onClick: loadOlder, "data-testid": "chat-thread-older", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: t(CHAT_I18N_KEYS.threadLoadOlder) })) : (_jsx(Typography.Text, { type: "secondary", children: t(CHAT_I18N_KEYS.threadBeginning) })), _jsx(Flex, { vertical: true, gap: 8, style: { width: "100%" }, children: messages.map((message) => (_jsx(MessageRow, { message: message, viewerId: props.viewerId, locale: locale }, message.id))) })] })),
49
+ }), _jsx("div", { style: { marginTop: 16 }, children: _jsx(Composer, { conversationId: props.conversationId, maxLength: props.maxLength }) })] })) }));
50
+ }
51
+ //# sourceMappingURL=ConversationThreadPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationThreadPanel.js","sourceRoot":"","sources":["../../src/default/ConversationThreadPanel.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { CSSProperties, ReactElement } from "react";
2
+ import type { FlowErrorDisplay } from "@stapel/core";
3
+ export declare function ErrorAlert(props: {
4
+ error: FlowErrorDisplay | undefined;
5
+ style?: CSSProperties | undefined;
6
+ testId?: string | undefined;
7
+ }): ReactElement | null;
8
+ //# sourceMappingURL=ErrorAlert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorAlert.d.ts","sourceRoot":"","sources":["../../src/default/ErrorAlert.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,GAAG,YAAY,GAAG,IAAI,CAqBtB"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * The one error surface this pair's default skins render, so the split copy
4
+ * core produces reaches a screen the same way everywhere.
5
+ *
6
+ * Core's `describeFlowError` splits a failure into the sentence a person reads
7
+ * and the technical detail a support agent quotes. The split is only worth
8
+ * anything if a skin renders both halves at their own weights: `message` at
9
+ * normal weight, `detail` muted and small, where an eye skips it and a support
10
+ * agent finds it.
11
+ *
12
+ * `undefined` in — including a `detail` core left `undefined` because there
13
+ * was nothing worth quoting — renders nothing rather than an empty muted line.
14
+ */
15
+ import { Alert, Typography } from "antd";
16
+ export function ErrorAlert(props) {
17
+ const { error } = props;
18
+ if (!error)
19
+ return null;
20
+ return (_jsx(Alert, { type: "error", showIcon: true, ...(props.style ? { style: props.style } : {}), ...(props.testId ? { "data-testid": props.testId } : {}), message: error.message, ...(error.detail
21
+ ? {
22
+ description: (_jsx(Typography.Text, { type: "secondary", style: { fontSize: 12 }, children: error.detail })),
23
+ }
24
+ : {}) }));
25
+ }
26
+ //# sourceMappingURL=ErrorAlert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorAlert.js","sourceRoot":"","sources":["../../src/default/ErrorAlert.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIzC,MAAM,UAAU,UAAU,CAAC,KAI1B;IACC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACxB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,CACL,KAAC,KAAK,IACJ,IAAI,EAAC,OAAO,EACZ,QAAQ,WACJ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC3C,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EACzD,OAAO,EAAE,KAAK,CAAC,OAAO,KAClB,CAAC,KAAK,CAAC,MAAM;YACf,CAAC,CAAC;gBACE,WAAW,EAAE,CACX,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YACtD,KAAK,CAAC,MAAM,GACG,CACnB;aACF;YACH,CAAC,CAAC,EAAE,CAAC,GACP,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `<StartChatButton/>` — "message the seller" on a listing card, skinned.
3
+ *
4
+ * The control is either pressable or switched off WITH a sentence beside it
5
+ * (no seller on the listing; the listing is your own). It never renders as a
6
+ * grey rectangle with no explanation.
7
+ */
8
+ import type { ReactElement } from "react";
9
+ import type { Conversation } from "../api/types.js";
10
+ export interface StartChatButtonProps {
11
+ sellerId: string | null | undefined;
12
+ viewerId?: string | null;
13
+ onOpened?: (conversation: Conversation) => void;
14
+ block?: boolean;
15
+ }
16
+ export declare function StartChatButton(props: StartChatButtonProps): ReactElement;
17
+ //# sourceMappingURL=StartChatButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StartChatButton.d.ts","sourceRoot":"","sources":["../../src/default/StartChatButton.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAI1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKpD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAsCD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,CAkBzE"}
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Space, Typography } from "antd";
3
+ import { useActionGate, useErrorDisplay, useT } from "@stapel/core";
4
+ import { StartDirectChat } from "../headless/StartDirectChat.js";
5
+ import { CHAT_I18N_KEYS } from "../i18n/keys.js";
6
+ import { ErrorAlert } from "./ErrorAlert.js";
7
+ function StartChatBody(props) {
8
+ const t = useT();
9
+ const gate = useActionGate(props.availability);
10
+ const errorDisplay = useErrorDisplay(CHAT_I18N_KEYS.unknownError);
11
+ return (_jsxs(Space, { direction: "vertical", style: { width: props.block ? "100%" : undefined }, children: [_jsx(Button, { type: "primary", block: props.block ?? false, disabled: gate.disabled, loading: props.isStarting, onClick: props.start, "data-testid": "chat-start-button", "data-analytics": "none", "data-analytics-reason": "business action \u2014 host app wraps with its own tracked()", children: props.isStarting
12
+ ? t(CHAT_I18N_KEYS.startStarting)
13
+ : t(CHAT_I18N_KEYS.startButton) }), gate.reason ? (_jsx(Typography.Text, { type: "secondary", "data-testid": "chat-start-blocked", children: gate.reason })) : null, _jsx(ErrorAlert, { error: errorDisplay(props.error) })] }));
14
+ }
15
+ export function StartChatButton(props) {
16
+ return (_jsx(StartDirectChat, { sellerId: props.sellerId, ...(props.viewerId !== undefined ? { viewerId: props.viewerId } : {}), ...(props.onOpened !== undefined ? { onOpened: props.onOpened } : {}), children: ({ availability, isStarting, error, start }) => (_jsx(StartChatBody, { availability: availability, isStarting: isStarting, error: error, start: start, block: props.block })) }));
17
+ }
18
+ //# sourceMappingURL=StartChatButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StartChatButton.js","sourceRoot":"","sources":["../../src/default/StartChatButton.tsx"],"names":[],"mappings":";AAQA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAS7C,SAAS,aAAa,CAAC,KAMtB;IACC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,eAAe,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IAClE,OAAO,CACL,MAAC,KAAK,IAAC,SAAS,EAAC,UAAU,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,aAC5E,KAAC,MAAM,IACL,IAAI,EAAC,SAAS,EACd,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK,EAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,OAAO,EAAE,KAAK,CAAC,UAAU,EACzB,OAAO,EAAE,KAAK,CAAC,KAAK,iBACR,mBAAmB,oBAChB,MAAM,2BACC,8DAAyD,YAE9E,KAAK,CAAC,UAAU;oBACf,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC;oBACjC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,GAC1B,EACR,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CACb,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,iBAAa,oBAAoB,YAC/D,IAAI,CAAC,MAAM,GACI,CACnB,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,UAAU,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,GAAI,IAC1C,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,OAAO,CACL,KAAC,eAAe,IACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,KACpB,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAClE,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAErE,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAC/C,KAAC,aAAa,IACZ,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,CAAC,KAAK,GAClB,CACH,GACe,CACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `@stapel/chat-react/default` — the opt-in antd skin for this pair (mirrors
3
+ * auth-react's `/default` split, §54): a separate entry point so consumers who
4
+ * bring their own visuals never pull `antd` into their bundle; importing this
5
+ * subpath is the opt-in.
6
+ *
7
+ * ```tsx
8
+ * import {
9
+ * ConversationListPanel,
10
+ * ConversationThreadPanel,
11
+ * StartChatButton,
12
+ * } from "@stapel/chat-react/default";
13
+ * ```
14
+ */
15
+ export { ConversationListPanel } from "./ConversationListPanel.js";
16
+ export type { ConversationListPanelProps } from "./ConversationListPanel.js";
17
+ export { ConversationThreadPanel } from "./ConversationThreadPanel.js";
18
+ export type { ConversationThreadPanelProps } from "./ConversationThreadPanel.js";
19
+ export { StartChatButton } from "./StartChatButton.js";
20
+ export type { StartChatButtonProps } from "./StartChatButton.js";
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@stapel/chat-react/default` — the opt-in antd skin for this pair (mirrors
3
+ * auth-react's `/default` split, §54): a separate entry point so consumers who
4
+ * bring their own visuals never pull `antd` into their bundle; importing this
5
+ * subpath is the opt-in.
6
+ *
7
+ * ```tsx
8
+ * import {
9
+ * ConversationListPanel,
10
+ * ConversationThreadPanel,
11
+ * StartChatButton,
12
+ * } from "@stapel/chat-react/default";
13
+ * ```
14
+ */
15
+ export { ConversationListPanel } from "./ConversationListPanel.js";
16
+ export { ConversationThreadPanel } from "./ConversationThreadPanel.js";
17
+ export { StartChatButton } from "./StartChatButton.js";
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/default/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { FlowError } from "@stapel/core";
2
+ export type { FlowError } from "@stapel/core";
3
+ export { isErrorCode } from "@stapel/core";
4
+ /**
5
+ * Fold any thrown value into a {@link FlowError} using this pair's own
6
+ * module-scoped fallback key (`chat.error.unknown`, an en string in
7
+ * `chatI18nBundleEn`) so a non-`StapelApiError` fault still renders real copy
8
+ * rather than a raw key. The primitive lives in `@stapel/core`
9
+ * (frontend-core-architecture §4b); this wrapper only pins the fallback.
10
+ */
11
+ export declare function toFlowError(error: unknown): FlowError;
12
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/flows/errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAErD"}
@@ -0,0 +1,13 @@
1
+ import { toFlowError as coreToFlowError } from "@stapel/core";
2
+ export { isErrorCode } from "@stapel/core";
3
+ /**
4
+ * Fold any thrown value into a {@link FlowError} using this pair's own
5
+ * module-scoped fallback key (`chat.error.unknown`, an en string in
6
+ * `chatI18nBundleEn`) so a non-`StapelApiError` fault still renders real copy
7
+ * rather than a raw key. The primitive lives in `@stapel/core`
8
+ * (frontend-core-architecture §4b); this wrapper only pins the fallback.
9
+ */
10
+ export function toFlowError(error) {
11
+ return coreToFlowError(error, "chat.error.unknown");
12
+ }
13
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/flows/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,cAAc,CAAC;AAI9D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,eAAe,CAAC,KAAK,EAAE,oBAAoB,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,69 @@
1
+ import type { QueryKey } from "@tanstack/react-query";
2
+ import type { ChatConnectionState, ChatSocketRefusal } from "../realtime/chatSocket.js";
3
+ import type { ChatStreamKey } from "../realtime/streams.js";
4
+ /**
5
+ * What a transport reports upward. One vocabulary for both, so the mapping
6
+ * function does not care where the news came from:
7
+ *
8
+ * - `message` — a specific message landed (socket only),
9
+ * - `resync` — the stream gave up on incremental delivery; re-read the
10
+ * journal (socket `error{resync}`),
11
+ * - `tick` — a scheduled "check for news" (polling), or a catch-up after a
12
+ * reconnect.
13
+ */
14
+ export type ChatSignal = {
15
+ readonly kind: "message";
16
+ readonly conversationId: string;
17
+ readonly seq: number;
18
+ } | {
19
+ readonly kind: "resync";
20
+ readonly conversationId: string;
21
+ } | {
22
+ readonly kind: "tick";
23
+ };
24
+ /** Signal → the query keys it makes stale. The whole contract with the UI. */
25
+ export type ChatSignalKeyMap = (signal: ChatSignal) => readonly QueryKey[];
26
+ /** Which transport is actually carrying freshness right now. */
27
+ export type ChatTransport = "socket" | "polling" | "idle";
28
+ export interface ChatFreshnessOptions {
29
+ /**
30
+ * Polling period (ms) used whenever the socket is not carrying the stream.
31
+ * `0` (the default) means no polling — a stream that is only ever live.
32
+ * The storefront spec's periods: an open thread 3000, the inbox 15000.
33
+ */
34
+ readonly fallbackRefetchInterval?: number;
35
+ /** Off entirely — an unauthenticated visitor, an unmounted screen. */
36
+ readonly enabled?: boolean;
37
+ /**
38
+ * Whether the SOCKET half may open (default `true`). Polling is unaffected.
39
+ *
40
+ * A resumable stream must not be subscribed before its cursor exists:
41
+ * `hello{last_seq: 0}` asks the server to replay the whole thread over the
42
+ * socket, which the store would then discard and re-read by REST. So
43
+ * `<ConversationThread>` holds the socket back until the window is loaded,
44
+ * while polling stays on — polling is also how a FAILED first read
45
+ * recovers, and switching that off with the socket would leave a broken
46
+ * thread broken until someone pressed something.
47
+ *
48
+ * The substrate is expected to subsume this: a stream that owns its own
49
+ * cursor knows when it has one.
50
+ */
51
+ readonly socketEnabled?: boolean;
52
+ }
53
+ export interface ChatFreshness {
54
+ readonly transport: ChatTransport;
55
+ readonly connection: ChatConnectionState;
56
+ /** Why the socket will not come back, when it will not. */
57
+ readonly refusal: ChatSocketRefusal | undefined;
58
+ /** Check for news right now (a pull-to-refresh, a regained focus). */
59
+ pollNow(): void;
60
+ }
61
+ /**
62
+ * The polling periods the storefront spec fixes (§3.6): an open thread 3 s,
63
+ * the inbox 15 s. They live here, beside the transport that uses them, rather
64
+ * than in the components — the numbers are a property of the seam.
65
+ */
66
+ export declare const THREAD_INTERVAL_MS = 3000;
67
+ export declare const CONVERSATION_LIST_INTERVAL_MS = 15000;
68
+ export declare function useChatFreshness(stream: ChatStreamKey, mapToQueryKeys: ChatSignalKeyMap, options?: ChatFreshnessOptions): ChatFreshness;
69
+ //# sourceMappingURL=freshness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"freshness.d.ts","sourceRoot":"","sources":["../../src/flows/freshness.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAMtD,OAAO,KAAK,EACV,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9B,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,UAAU,KAAK,SAAS,QAAQ,EAAE,CAAC;AAE3E,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC;IACzC,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAChD,sEAAsE;IACtE,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AACxC,eAAO,MAAM,6BAA6B,QAAS,CAAC;AAiBpD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EACrB,cAAc,EAAE,gBAAgB,EAChC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,aAAa,CAiLf"}