@ai-matrx/messaging 0.3.0 → 0.4.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.
package/dist/react.d.cts CHANGED
@@ -828,6 +828,17 @@ interface MessageActionRenderer<TPayload = unknown> {
828
828
  type ReferenceRenderer = ComponentType<{
829
829
  reference: MatrxReference;
830
830
  }>;
831
+ /**
832
+ * A host's renderer for a raw ```matrx fence — the body between the backticks.
833
+ *
834
+ * This package understands one fence dialect: its own. A platform's fences are
835
+ * its platform's business (Matrx's are `__kind` directive shells resolved
836
+ * through a kind registry), and only the app can draw them. Give us this and
837
+ * EVERY fence in a message goes to you, parsed or not.
838
+ */
839
+ type FenceRenderer = ComponentType<{
840
+ body: string;
841
+ }>;
831
842
  /**
832
843
  * App chrome wrapped around ONE message bubble or ONE conversation row.
833
844
  *
@@ -858,6 +869,7 @@ interface MessagingHost {
858
869
  /** Host surfaces for action kinds whose answer is a card, not a chip. */
859
870
  readonly actionRenderers: ReadonlyMap<string, MessageActionRenderer>;
860
871
  readonly renderReference: ReferenceRenderer | null;
872
+ readonly renderFence: FenceRenderer | null;
861
873
  readonly wrapMessage: ComponentType<MessageWrapperProps> | null;
862
874
  readonly wrapConversationRow: ComponentType<ConversationRowWrapperProps> | null;
863
875
  }
@@ -889,6 +901,8 @@ interface MessagingProviderProps {
889
901
  onOpenReference?: ((reference: MatrxReference) => void) | undefined;
890
902
  /** Draw references with the app's own renderer instead of the package card. */
891
903
  renderReference?: ReferenceRenderer | undefined;
904
+ /** Draw ```matrx fences with the app's own renderer — every fence, parsed or not. */
905
+ renderFence?: FenceRenderer | undefined;
892
906
  /** App chrome (a right-click menu, a drop target) around each message bubble. */
893
907
  wrapMessage?: ComponentType<MessageWrapperProps> | undefined;
894
908
  /** App chrome around each conversation row. */
@@ -1077,11 +1091,6 @@ declare function Avatar(props: {
1077
1091
  declare function DeliveryTick(props: {
1078
1092
  state: DeliveryState;
1079
1093
  }): React.ReactElement | null;
1080
- /**
1081
- * A reference card. NO DEAD ENDS: when the host wired `onOpenReference` it is a
1082
- * button that opens; when it did not, it renders as a labeled, non-interactive
1083
- * card with a title explaining why — never a button that does nothing.
1084
- */
1085
1094
  declare function ReferenceCard(props: {
1086
1095
  reference: MatrxReference;
1087
1096
  onOpen?: ((reference: MatrxReference) => void) | null;
@@ -1363,6 +1372,20 @@ type TextSegment = {
1363
1372
  } | {
1364
1373
  readonly type: "reference";
1365
1374
  readonly reference: MatrxReference;
1375
+ }
1376
+ /**
1377
+ * A ```matrx fence this package could not resolve into references.
1378
+ *
1379
+ * It is NOT dropped. Platforms carry richer fence dialects than this
1380
+ * package's own array shape — Matrx's is a `__kind` directive shell whose
1381
+ * items are typed per noun, resolvable only by the app's kind registry — and
1382
+ * a fence silently deleted on render is a message that lost a paragraph
1383
+ * between the sender and the reader. The host draws it (`renderFence`), or
1384
+ * the package draws an honest inert card. Never a code block of JSON.
1385
+ */
1386
+ | {
1387
+ readonly type: "fence";
1388
+ readonly body: string;
1366
1389
  };
1367
1390
  /** Every reference a message carries, from both transports, de-duplicated. */
1368
1391
  declare function extractReferences(content: string, structured?: readonly MatrxReference[]): readonly MatrxReference[];
package/dist/react.d.ts CHANGED
@@ -828,6 +828,17 @@ interface MessageActionRenderer<TPayload = unknown> {
828
828
  type ReferenceRenderer = ComponentType<{
829
829
  reference: MatrxReference;
830
830
  }>;
831
+ /**
832
+ * A host's renderer for a raw ```matrx fence — the body between the backticks.
833
+ *
834
+ * This package understands one fence dialect: its own. A platform's fences are
835
+ * its platform's business (Matrx's are `__kind` directive shells resolved
836
+ * through a kind registry), and only the app can draw them. Give us this and
837
+ * EVERY fence in a message goes to you, parsed or not.
838
+ */
839
+ type FenceRenderer = ComponentType<{
840
+ body: string;
841
+ }>;
831
842
  /**
832
843
  * App chrome wrapped around ONE message bubble or ONE conversation row.
833
844
  *
@@ -858,6 +869,7 @@ interface MessagingHost {
858
869
  /** Host surfaces for action kinds whose answer is a card, not a chip. */
859
870
  readonly actionRenderers: ReadonlyMap<string, MessageActionRenderer>;
860
871
  readonly renderReference: ReferenceRenderer | null;
872
+ readonly renderFence: FenceRenderer | null;
861
873
  readonly wrapMessage: ComponentType<MessageWrapperProps> | null;
862
874
  readonly wrapConversationRow: ComponentType<ConversationRowWrapperProps> | null;
863
875
  }
@@ -889,6 +901,8 @@ interface MessagingProviderProps {
889
901
  onOpenReference?: ((reference: MatrxReference) => void) | undefined;
890
902
  /** Draw references with the app's own renderer instead of the package card. */
891
903
  renderReference?: ReferenceRenderer | undefined;
904
+ /** Draw ```matrx fences with the app's own renderer — every fence, parsed or not. */
905
+ renderFence?: FenceRenderer | undefined;
892
906
  /** App chrome (a right-click menu, a drop target) around each message bubble. */
893
907
  wrapMessage?: ComponentType<MessageWrapperProps> | undefined;
894
908
  /** App chrome around each conversation row. */
@@ -1077,11 +1091,6 @@ declare function Avatar(props: {
1077
1091
  declare function DeliveryTick(props: {
1078
1092
  state: DeliveryState;
1079
1093
  }): React.ReactElement | null;
1080
- /**
1081
- * A reference card. NO DEAD ENDS: when the host wired `onOpenReference` it is a
1082
- * button that opens; when it did not, it renders as a labeled, non-interactive
1083
- * card with a title explaining why — never a button that does nothing.
1084
- */
1085
1094
  declare function ReferenceCard(props: {
1086
1095
  reference: MatrxReference;
1087
1096
  onOpen?: ((reference: MatrxReference) => void) | null;
@@ -1363,6 +1372,20 @@ type TextSegment = {
1363
1372
  } | {
1364
1373
  readonly type: "reference";
1365
1374
  readonly reference: MatrxReference;
1375
+ }
1376
+ /**
1377
+ * A ```matrx fence this package could not resolve into references.
1378
+ *
1379
+ * It is NOT dropped. Platforms carry richer fence dialects than this
1380
+ * package's own array shape — Matrx's is a `__kind` directive shell whose
1381
+ * items are typed per noun, resolvable only by the app's kind registry — and
1382
+ * a fence silently deleted on render is a message that lost a paragraph
1383
+ * between the sender and the reader. The host draws it (`renderFence`), or
1384
+ * the package draws an honest inert card. Never a code block of JSON.
1385
+ */
1386
+ | {
1387
+ readonly type: "fence";
1388
+ readonly body: string;
1366
1389
  };
1367
1390
  /** Every reference a message carries, from both transports, de-duplicated. */
1368
1391
  declare function extractReferences(content: string, structured?: readonly MatrxReference[]): readonly MatrxReference[];
package/dist/react.js CHANGED
@@ -222,21 +222,32 @@ function splitText(content) {
222
222
  if (start > cursor) {
223
223
  segments.push({ type: "text", value: content.slice(cursor, start) });
224
224
  }
225
- parseFenceBody(match[1] ?? "").forEach((reference) => {
226
- segments.push({ type: "reference", reference });
227
- });
225
+ const body = match[1] ?? "";
226
+ const references = parseFenceBody(body);
227
+ if (references.length === 0) {
228
+ segments.push({ type: "fence", body });
229
+ } else {
230
+ references.forEach((reference) => {
231
+ segments.push({ type: "reference", reference });
232
+ });
233
+ }
228
234
  cursor = start + match[0].length;
229
235
  }
230
236
  if (cursor < content.length) {
231
237
  segments.push({ type: "text", value: content.slice(cursor) });
232
238
  }
233
239
  return segments.filter(
234
- (segment) => segment.type === "reference" || segment.value.trim().length > 0
240
+ (segment) => segment.type !== "text" || segment.value.trim().length > 0
235
241
  );
236
242
  }
237
243
  function summarizeText(content, maxLength = 140) {
238
244
  const parts = splitText(content).map(
239
- (segment) => segment.type === "text" ? segment.value : segment.reference.label
245
+ (segment) => segment.type === "text" ? segment.value : segment.type === "reference" ? segment.reference.label : (
246
+ // A fence we cannot read still SAYS something. Collapsing it to
247
+ // nothing is how a reference-only message becomes an inbox row that
248
+ // reads "No messages yet" — a screen telling a lie.
249
+ "Reference"
250
+ )
240
251
  );
241
252
  const flattened = parts.join(" ").replace(/\s+/g, " ").trim();
242
253
  if (flattened.length <= maxLength) return flattened;
@@ -1794,6 +1805,7 @@ function MessagingRuntime(props) {
1794
1805
  return map;
1795
1806
  }, [renderers]);
1796
1807
  const renderReference = props.renderReference ?? null;
1808
+ const renderFence = props.renderFence ?? null;
1797
1809
  const wrapMessage = props.wrapMessage ?? null;
1798
1810
  const wrapConversationRow = props.wrapConversationRow ?? null;
1799
1811
  const host = useMemo(() => {
@@ -1806,10 +1818,20 @@ function MessagingRuntime(props) {
1806
1818
  openReference: referenceRef.current ?? null,
1807
1819
  actionRenderers: rendererMap,
1808
1820
  renderReference,
1821
+ renderFence,
1809
1822
  wrapMessage,
1810
1823
  wrapConversationRow
1811
1824
  };
1812
- }, [engine, actionRegistry, ai, rendererMap, renderReference, wrapMessage, wrapConversationRow]);
1825
+ }, [
1826
+ engine,
1827
+ actionRegistry,
1828
+ ai,
1829
+ rendererMap,
1830
+ renderReference,
1831
+ renderFence,
1832
+ wrapMessage,
1833
+ wrapConversationRow
1834
+ ]);
1813
1835
  const MessagingContext = messagingContext();
1814
1836
  return /* @__PURE__ */ jsx(MessagingContext.Provider, { value: host, children });
1815
1837
  }
@@ -2400,6 +2422,20 @@ function DeliveryTick(props) {
2400
2422
  return null;
2401
2423
  }
2402
2424
  }
2425
+ function UnreadableReference() {
2426
+ return /* @__PURE__ */ jsxs(
2427
+ "span",
2428
+ {
2429
+ className: "mx-msg__reference mx-msg__reference--inert",
2430
+ title: "This message names something in a reference format this app has not wired. Pass renderFence to <MessagingProvider> to render it.",
2431
+ children: [
2432
+ /* @__PURE__ */ jsx3(LinkIcon, {}),
2433
+ /* @__PURE__ */ jsx3("span", { className: "mx-msg__reference-type", children: "reference" }),
2434
+ "Reference"
2435
+ ]
2436
+ }
2437
+ );
2438
+ }
2403
2439
  function ReferenceCard(props) {
2404
2440
  const { reference, onOpen } = props;
2405
2441
  const href = reference.href;
@@ -2764,6 +2800,7 @@ function MessageBubble(props) {
2764
2800
  const { message, isMine } = props;
2765
2801
  const host = useMessagingHost();
2766
2802
  const HostReference = host?.renderReference ?? null;
2803
+ const HostFence = host?.renderFence ?? null;
2767
2804
  const MessageChrome = host?.wrapMessage ?? null;
2768
2805
  if (message.deletedAt !== null) {
2769
2806
  return /* @__PURE__ */ jsx4("div", { className: "mx-msg__bubble mx-msg__bubble--deleted", children: "Message deleted" });
@@ -2778,7 +2815,7 @@ function MessageBubble(props) {
2778
2815
  const bubble = /* @__PURE__ */ jsxs2(Fragment, { children: [
2779
2816
  /* @__PURE__ */ jsxs2("div", { className: classes, children: [
2780
2817
  splitText(message.content).map(
2781
- (segment, index) => segment.type === "text" ? /* @__PURE__ */ jsx4("span", { children: segment.value }, index) : HostReference !== null ? /* @__PURE__ */ jsx4(
2818
+ (segment, index) => segment.type === "text" ? /* @__PURE__ */ jsx4("span", { children: segment.value }, index) : segment.type === "fence" ? HostFence !== null ? /* @__PURE__ */ jsx4(HostFence, { body: segment.body }, `fence:${index}`) : /* @__PURE__ */ jsx4(UnreadableReference, {}, `fence:${index}`) : HostReference !== null ? /* @__PURE__ */ jsx4(
2782
2819
  HostReference,
2783
2820
  {
2784
2821
  reference: segment.reference