@admin-layout/tailwind-chat-pro 12.2.5-alpha.2 → 12.2.5-alpha.22

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 (46) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/lib/components/messages-container-ui/MessagesContainerUI.d.ts +2 -2
  3. package/lib/components/messages-container-ui/MessagesContainerUI.d.ts.map +1 -1
  4. package/lib/components/messages-container-ui/ModernMessageGroup.d.ts +3 -0
  5. package/lib/components/messages-container-ui/ModernMessageGroup.d.ts.map +1 -1
  6. package/lib/components/messages-container-ui/ModernMessageGroup.js +75 -19
  7. package/lib/components/messages-container-ui/ModernMessageGroup.js.map +1 -1
  8. package/lib/components/messages-container-ui/PlanModeView.d.ts +2 -2
  9. package/lib/components/messages-container-ui/PlanModeView.d.ts.map +1 -1
  10. package/lib/components/messages-container-ui/PlanModeView.js +13 -4
  11. package/lib/components/messages-container-ui/PlanModeView.js.map +1 -1
  12. package/lib/components/messages-container-ui/index.d.ts +1 -1
  13. package/lib/components/messages-container-ui/index.d.ts.map +1 -1
  14. package/lib/components/messages-container-ui/markdownTables.d.ts +25 -0
  15. package/lib/components/messages-container-ui/markdownTables.d.ts.map +1 -0
  16. package/lib/components/messages-container-ui/markdownTables.js +103 -0
  17. package/lib/components/messages-container-ui/markdownTables.js.map +1 -0
  18. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.d.ts +5 -1
  19. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.d.ts.map +1 -1
  20. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.js +6 -0
  21. package/lib/components/messages-container-ui/messenger-slot-fill-name-enum.js.map +1 -1
  22. package/lib/components/messages-container-ui/modernStreaming.d.ts +2 -1
  23. package/lib/components/messages-container-ui/modernStreaming.d.ts.map +1 -1
  24. package/lib/components/messages-container-ui/modernStreaming.js +26 -9
  25. package/lib/components/messages-container-ui/modernStreaming.js.map +1 -1
  26. package/lib/components/messages-container-ui/types.d.ts +7 -0
  27. package/lib/components/messages-container-ui/types.d.ts.map +1 -1
  28. package/lib/components/messages-container-ui/useBubbleEntrance.d.ts +12 -0
  29. package/lib/components/messages-container-ui/useBubbleEntrance.d.ts.map +1 -0
  30. package/lib/components/messages-container-ui/useBubbleEntrance.js +36 -0
  31. package/lib/components/messages-container-ui/useBubbleEntrance.js.map +1 -0
  32. package/package.json +9 -10
  33. package/src/__tests__/stubs/common-graphql.ts +2 -0
  34. package/src/__tests__/stubs/common.ts +5 -0
  35. package/src/__tests__/stubs/components-pro.ts +2 -0
  36. package/src/components/messages-container-ui/MessagesContainerUI.tsx +2 -2
  37. package/src/components/messages-container-ui/ModernMessageGroup.tsx +88 -23
  38. package/src/components/messages-container-ui/PlanModeView.tsx +26 -18
  39. package/src/components/messages-container-ui/__tests__/markdownTables.test.tsx +94 -0
  40. package/src/components/messages-container-ui/index.ts +7 -1
  41. package/src/components/messages-container-ui/markdownTables.ts +108 -0
  42. package/src/components/messages-container-ui/messenger-slot-fill-name-enum.ts +6 -0
  43. package/src/components/messages-container-ui/modernStreaming.tsx +20 -6
  44. package/src/components/messages-container-ui/types.ts +8 -0
  45. package/src/components/messages-container-ui/useBubbleEntrance.ts +31 -0
  46. package/vitest.config.ts +17 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Markdown tables in a chat message: every table sits in its own box that scrolls sideways, and
3
+ * nothing inside a table may break a word to make it fit.
4
+ *
5
+ * A message's prose wraps anywhere (`overflow-wrap: anywhere`), so a long path or URL never
6
+ * pushes the chat wider. That rule also counts every character as a break when a table sizes its
7
+ * columns, so a table in a narrow pane squeezed its number columns to a few characters and split
8
+ * "2,000" into "2,00" / "0". Inside a table a line now breaks only where the text has a space,
9
+ * and a table still too wide for the message scrolls in its box instead.
10
+ */
11
+ /** The box every rendered markdown table sits in. */
12
+ export declare const TABLE_SCROLL_CLASS = "llm-table-scroll";
13
+ /**
14
+ * Put each `<table>` of rendered HTML in a {@link TABLE_SCROLL_CLASS} box. Every opening tag gets
15
+ * a box and every closing tag closes it, so well-formed HTML stays balanced (nested tables too).
16
+ * Escaped markup, such as a table inside a code block, is text and is left alone.
17
+ */
18
+ export declare function wrapTables(html: string): string;
19
+ /**
20
+ * The box, the no-split rule and the scroll cue, for both the assistant's reply and the person's
21
+ * own bubble. `.llm-table-scroll table *` outranks the bubbles' wrap-anywhere rules
22
+ * (`.user-message *`, the reply's bubble), which is what keeps a number whole.
23
+ */
24
+ export declare const TABLE_STYLES = "\n .llm-table-scroll {\n max-width: 100%;\n overflow-x: auto;\n }\n\n /* The box carries the spacing: a scroll box never collapses its child's margins, so a\n table's own (prose gives it 1em) would show as an empty band inside the box. */\n .llm-table-scroll > table {\n margin-top: 0;\n margin-bottom: 0;\n }\n\n .llm-table-scroll table,\n .llm-table-scroll table * {\n word-break: normal;\n overflow-wrap: normal;\n }\n\n .llm-table-scroll th,\n .llm-table-scroll td {\n font-variant-numeric: tabular-nums;\n }\n\n /* A cut-off column alone reads as a finished table (or as a number split in two), so a side\n that has more columns to scroll to carries a soft shadow. Both shadows are pinned to the\n box's edges (sticky, in the table's grid cell) and fade with the scroll position; a table\n that fits has no scroll timeline, so neither shows. Where the browser cannot tie an\n animation to scrolling, the box scrolls without the cue. */\n @supports (animation-timeline: scroll()) {\n .llm-table-scroll {\n display: grid;\n scroll-timeline: --llm-table-scroll inline;\n }\n\n .llm-table-scroll > table,\n .llm-table-scroll::before,\n .llm-table-scroll::after {\n grid-area: 1 / 1;\n }\n\n .llm-table-scroll::before,\n .llm-table-scroll::after {\n content: '';\n position: sticky;\n z-index: 1;\n width: 0.875rem;\n pointer-events: none;\n opacity: 0;\n }\n\n .llm-table-scroll::before {\n left: 0;\n justify-self: start;\n background: linear-gradient(to right, var(--llm-table-scroll-shadow, rgba(18, 25, 38, 0.14)), transparent);\n animation: llm-table-scroll-start linear both;\n animation-timeline: --llm-table-scroll;\n }\n\n .llm-table-scroll::after {\n right: 0;\n justify-self: end;\n background: linear-gradient(to left, var(--llm-table-scroll-shadow, rgba(18, 25, 38, 0.14)), transparent);\n animation: llm-table-scroll-end linear both;\n animation-timeline: --llm-table-scroll;\n }\n }\n\n @keyframes llm-table-scroll-start {\n 0% { opacity: 0; }\n 6%, 100% { opacity: 1; }\n }\n\n @keyframes llm-table-scroll-end {\n 0%, 94% { opacity: 1; }\n 100% { opacity: 0; }\n }\n";
25
+ //# sourceMappingURL=markdownTables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownTables.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/markdownTables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,qBAAqB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,q/EA6ExB,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Markdown tables in a chat message: every table sits in its own box that scrolls sideways, and
3
+ * nothing inside a table may break a word to make it fit.
4
+ *
5
+ * A message's prose wraps anywhere (`overflow-wrap: anywhere`), so a long path or URL never
6
+ * pushes the chat wider. That rule also counts every character as a break when a table sizes its
7
+ * columns, so a table in a narrow pane squeezed its number columns to a few characters and split
8
+ * "2,000" into "2,00" / "0". Inside a table a line now breaks only where the text has a space,
9
+ * and a table still too wide for the message scrolls in its box instead.
10
+ */
11
+ /** The box every rendered markdown table sits in. */
12
+ const TABLE_SCROLL_CLASS = 'llm-table-scroll';
13
+ /**
14
+ * Put each `<table>` of rendered HTML in a {@link TABLE_SCROLL_CLASS} box. Every opening tag gets
15
+ * a box and every closing tag closes it, so well-formed HTML stays balanced (nested tables too).
16
+ * Escaped markup, such as a table inside a code block, is text and is left alone.
17
+ */
18
+ function wrapTables(html) {
19
+ return html.replace(/<table\b[^>]*>/gi, open => `<div class="${TABLE_SCROLL_CLASS}">${open}`).replace(/<\/table\s*>/gi, close => `${close}</div>`);
20
+ }
21
+ /**
22
+ * The box, the no-split rule and the scroll cue, for both the assistant's reply and the person's
23
+ * own bubble. `.llm-table-scroll table *` outranks the bubbles' wrap-anywhere rules
24
+ * (`.user-message *`, the reply's bubble), which is what keeps a number whole.
25
+ */
26
+ const TABLE_STYLES = `
27
+ .${TABLE_SCROLL_CLASS} {
28
+ max-width: 100%;
29
+ overflow-x: auto;
30
+ }
31
+
32
+ /* The box carries the spacing: a scroll box never collapses its child's margins, so a
33
+ table's own (prose gives it 1em) would show as an empty band inside the box. */
34
+ .${TABLE_SCROLL_CLASS} > table {
35
+ margin-top: 0;
36
+ margin-bottom: 0;
37
+ }
38
+
39
+ .${TABLE_SCROLL_CLASS} table,
40
+ .${TABLE_SCROLL_CLASS} table * {
41
+ word-break: normal;
42
+ overflow-wrap: normal;
43
+ }
44
+
45
+ .${TABLE_SCROLL_CLASS} th,
46
+ .${TABLE_SCROLL_CLASS} td {
47
+ font-variant-numeric: tabular-nums;
48
+ }
49
+
50
+ /* A cut-off column alone reads as a finished table (or as a number split in two), so a side
51
+ that has more columns to scroll to carries a soft shadow. Both shadows are pinned to the
52
+ box's edges (sticky, in the table's grid cell) and fade with the scroll position; a table
53
+ that fits has no scroll timeline, so neither shows. Where the browser cannot tie an
54
+ animation to scrolling, the box scrolls without the cue. */
55
+ @supports (animation-timeline: scroll()) {
56
+ .${TABLE_SCROLL_CLASS} {
57
+ display: grid;
58
+ scroll-timeline: --llm-table-scroll inline;
59
+ }
60
+
61
+ .${TABLE_SCROLL_CLASS} > table,
62
+ .${TABLE_SCROLL_CLASS}::before,
63
+ .${TABLE_SCROLL_CLASS}::after {
64
+ grid-area: 1 / 1;
65
+ }
66
+
67
+ .${TABLE_SCROLL_CLASS}::before,
68
+ .${TABLE_SCROLL_CLASS}::after {
69
+ content: '';
70
+ position: sticky;
71
+ z-index: 1;
72
+ width: 0.875rem;
73
+ pointer-events: none;
74
+ opacity: 0;
75
+ }
76
+
77
+ .${TABLE_SCROLL_CLASS}::before {
78
+ left: 0;
79
+ justify-self: start;
80
+ background: linear-gradient(to right, var(--llm-table-scroll-shadow, rgba(18, 25, 38, 0.14)), transparent);
81
+ animation: llm-table-scroll-start linear both;
82
+ animation-timeline: --llm-table-scroll;
83
+ }
84
+
85
+ .${TABLE_SCROLL_CLASS}::after {
86
+ right: 0;
87
+ justify-self: end;
88
+ background: linear-gradient(to left, var(--llm-table-scroll-shadow, rgba(18, 25, 38, 0.14)), transparent);
89
+ animation: llm-table-scroll-end linear both;
90
+ animation-timeline: --llm-table-scroll;
91
+ }
92
+ }
93
+
94
+ @keyframes llm-table-scroll-start {
95
+ 0% { opacity: 0; }
96
+ 6%, 100% { opacity: 1; }
97
+ }
98
+
99
+ @keyframes llm-table-scroll-end {
100
+ 0%, 94% { opacity: 1; }
101
+ 100% { opacity: 0; }
102
+ }
103
+ `;export{TABLE_SCROLL_CLASS,TABLE_STYLES,wrapTables};//# sourceMappingURL=markdownTables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownTables.js","sources":["../../../src/components/messages-container-ui/markdownTables.ts"],"sourcesContent":[null],"names":[],"mappings":"AAAA;;;;;;;;;AASG;AAEH;AACO,MAAM,kBAAkB,GAAG;AAElC;;;;AAIG;AACG,SAAU,UAAU,CAAC,IAAY,EAAA;AACnC,EAAA,OAAA,IAAO,CAAA,OAAI,CAAA,kBAAA,EAAA,IAAA,IAAA,CAAA,YAAA,EAAA,kBAAA,CAAA,EAAA,EAAA,IAAA,CAAA,CAAA,CAAA,CAAA,OAAA,CAAA,gBAAA,EAAA,KAAA,IAAA,CAAA,EAAA,KAAA,CAAA,MAAA,CAAA,CAAA;AACN;AACA;AACT;AAEA;;;;AAIG,KAAA,EAAA,kBAAA,CAAA;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAmCW,kBAAkB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -11,6 +11,10 @@ export declare enum MessengerSlotFillNameEnum {
11
11
  MEDIA_NAME = "media",
12
12
  WORKFLOW_NAME = "workflow",
13
13
  CONNECTOR_NAME = "connector",
14
- GENERIC_NAME = "generic"
14
+ GENERIC_NAME = "generic",
15
+ SHOPPING_NAME = "shopping",
16
+ MOVIES_NAME = "movies",
17
+ JOBS_NAME = "jobs",
18
+ RESUME_NAME = "resume"
15
19
  }
16
20
  //# sourceMappingURL=messenger-slot-fill-name-enum.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"messenger-slot-fill-name-enum.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/messenger-slot-fill-name-enum.ts"],"names":[],"mappings":"AAAA,oBAAY,yBAAyB;IACjC,mBAAmB,wBAAwB;IAC3C,2BAA2B,gCAAgC;IAC3D,aAAa,kBAAkB;IAC/B,YAAY,YAAY;IACxB,aAAa,aAAa;IAC1B,cAAc,cAAc;IAC5B,gBAAgB,gBAAgB;IAChC,YAAY,YAAY;IACxB,WAAW,WAAW;IACtB,UAAU,UAAU;IACpB,aAAa,aAAa;IAC1B,cAAc,cAAc;IAC5B,YAAY,YAAY;CAC3B"}
1
+ {"version":3,"file":"messenger-slot-fill-name-enum.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/messenger-slot-fill-name-enum.ts"],"names":[],"mappings":"AAAA,oBAAY,yBAAyB;IACjC,mBAAmB,wBAAwB;IAC3C,2BAA2B,gCAAgC;IAC3D,aAAa,kBAAkB;IAC/B,YAAY,YAAY;IACxB,aAAa,aAAa;IAC1B,cAAc,cAAc;IAC5B,gBAAgB,gBAAgB;IAChC,YAAY,YAAY;IACxB,WAAW,WAAW;IACtB,UAAU,UAAU;IACpB,aAAa,aAAa;IAC1B,cAAc,cAAc;IAC5B,YAAY,YAAY;IAGxB,aAAa,aAAa;IAC1B,WAAW,WAAW;IACtB,SAAS,SAAS;IAClB,WAAW,WAAW;CACzB"}
@@ -13,4 +13,10 @@ var MessengerSlotFillNameEnum;
13
13
  MessengerSlotFillNameEnum["WORKFLOW_NAME"] = "workflow";
14
14
  MessengerSlotFillNameEnum["CONNECTOR_NAME"] = "connector";
15
15
  MessengerSlotFillNameEnum["GENERIC_NAME"] = "generic";
16
+ // Result cards registered by ai-chat-ext (MessageResults): a row of product, showtime or
17
+ // role cards, and the single resume document card.
18
+ MessengerSlotFillNameEnum["SHOPPING_NAME"] = "shopping";
19
+ MessengerSlotFillNameEnum["MOVIES_NAME"] = "movies";
20
+ MessengerSlotFillNameEnum["JOBS_NAME"] = "jobs";
21
+ MessengerSlotFillNameEnum["RESUME_NAME"] = "resume";
16
22
  })(MessengerSlotFillNameEnum || (MessengerSlotFillNameEnum = {}));export{MessengerSlotFillNameEnum};//# sourceMappingURL=messenger-slot-fill-name-enum.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"messenger-slot-fill-name-enum.js","sources":["../../../src/components/messages-container-ui/messenger-slot-fill-name-enum.ts"],"sourcesContent":[null],"names":[],"mappings":"IAAY;AAAZ,CAAA,UAAY,yBAAyB,EAAA;AACjC,EAAA,yBAAA,CAAA,qBAAA,CAAA,GAAA,qBAAA;AACA,EAAA,yBAAA,CAAA,6BAAA,CAAA,GAAA,6BAAA;AACA,EAAA,yBAAA,CAAA,eAAA,CAAA,GAAA,eAAA;AACA,EAAA,yBAAA,CAAA,cAAA,CAAA,GAAA,SAAA;AACA,EAAA,yBAAA,CAAA,eAAA,CAAA,GAAA,UAAA;AACA,EAAA,yBAAA,CAAA,gBAAA,CAAA,GAAA,WAAA;AACA,EAAA,yBAAA,CAAA,kBAAA,CAAA,GAAA,aAAA;AACA,EAAA,yBAAA,CAAA,cAAA,CAAA,GAAA,SAAA;AACA,EAAA,yBAAA,CAAA,aAAA,CAAA,GAAA,QAAA;AACA,EAAA,yBAAA,CAAA,YAAA,CAAA,GAAA,OAAA;AACA,EAAA,yBAAA,CAAA,eAAA,CAAA,GAAA,UAAA;AACA,EAAA,yBAAA,CAAA,gBAAA,CAAA,GAAA,WAAA;AACA,EAAA,yBAAA,CAAA,cAAA,CAAA,GAAA,SAAA;AACJ,CAAC,EAdW,yBAAyB,KAAzB,yBAAyB,GAAA,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"messenger-slot-fill-name-enum.js","sources":["../../../src/components/messages-container-ui/messenger-slot-fill-name-enum.ts"],"sourcesContent":[null],"names":[],"mappings":"IAAY;AAAZ,CAAA,UAAY,yBAAyB,EAAA;AACjC,EAAA,yBAAA,CAAA,qBAAA,CAAA,GAAA,qBAAA;AACA,EAAA,yBAAA,CAAA,6BAAA,CAAA,GAAA,6BAAA;AACA,EAAA,yBAAA,CAAA,eAAA,CAAA,GAAA,eAAA;AACA,EAAA,yBAAA,CAAA,cAAA,CAAA,GAAA,SAAA;AACA,EAAA,yBAAA,CAAA,eAAA,CAAA,GAAA,UAAA;AACA,EAAA,yBAAA,CAAA,gBAAA,CAAA,GAAA,WAAA;AACA,EAAA,yBAAA,CAAA,kBAAA,CAAA,GAAA,aAAA;AACA,EAAA,yBAAA,CAAA,cAAA,CAAA,GAAA,SAAA;AACA,EAAA,yBAAA,CAAA,aAAA,CAAA,GAAA,QAAA;AACA,EAAA,yBAAA,CAAA,YAAA,CAAA,GAAA,OAAA;AACA,EAAA,yBAAA,CAAA,eAAA,CAAA,GAAA,UAAA;AACA,EAAA,yBAAA,CAAA,gBAAA,CAAA,GAAA,WAAA;AACA,EAAA,yBAAA,CAAA,cAAA,CAAA,GAAA,SAAA;;;AAGA,EAAA,yBAAA,CAAA,eAAA,CAAA,GAAA,UAAA;AACA,EAAA,yBAAA,CAAA,aAAA,CAAA,GAAA,QAAA;AACA,EAAA,yBAAA,CAAA,WAAA,CAAA,GAAA,MAAA;AACA,EAAA,yBAAA,CAAA,aAAA,CAAA,GAAA,QAAA;AACJ,CAAC,EApBW,yBAAyB,KAAzB,yBAAyB,GAAA,EAAA,CAAA,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import type { StreamingRenderOptions } from './types';
2
3
  /**
3
4
  * Renders in-flight streaming assistant content with the rich Modern renderer:
4
5
  * raw-code content goes through {@link CodeFormatter}, everything else through
@@ -7,5 +8,5 @@ import React from 'react';
7
8
  * Pass this as `MessagesContainerUI` / `PlanModeView`'s `renderStreamingContent` to keep
8
9
  * markdown / code rendering while streaming (the base default is plain text).
9
10
  */
10
- export declare function renderModernStreamingContent(content: string, showCopyDownloadOnCodeBlock?: boolean): React.ReactNode;
11
+ export declare function renderModernStreamingContent(content: string, opts?: StreamingRenderOptions): React.ReactNode;
11
12
  //# sourceMappingURL=modernStreaming.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"modernStreaming.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/modernStreaming.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,2BAA2B,UAAO,GAAG,KAAK,CAAC,SAAS,CAMjH"}
1
+ {"version":3,"file":"modernStreaming.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/modernStreaming.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,sBAA2B,GAAG,KAAK,CAAC,SAAS,CAmBhH"}
@@ -1,4 +1,4 @@
1
- import {jsx}from'react/jsx-runtime';import {detectContentType,CodeFormatter,FormattedMessageContent}from'./ModernMessageGroup.js';/**
1
+ import {jsx}from'react/jsx-runtime';import {detectContentType,CodeFormatter,FormattedMessageContent,assistantFrameClass}from'./ModernMessageGroup.js';/**
2
2
  * Renders in-flight streaming assistant content with the rich Modern renderer:
3
3
  * raw-code content goes through {@link CodeFormatter}, everything else through
4
4
  * {@link FormattedMessageContent} (markdown + embedded code blocks).
@@ -6,12 +6,29 @@ import {jsx}from'react/jsx-runtime';import {detectContentType,CodeFormatter,Form
6
6
  * Pass this as `MessagesContainerUI` / `PlanModeView`'s `renderStreamingContent` to keep
7
7
  * markdown / code rendering while streaming (the base default is plain text).
8
8
  */
9
- function renderModernStreamingContent(content, showCopyDownloadOnCodeBlock = true) {
10
- return detectContentType(content) === 'code' ? jsx(CodeFormatter, {
11
- content: content,
12
- showCopyDownload: showCopyDownloadOnCodeBlock
13
- }) : jsx(FormattedMessageContent, {
14
- content: content,
15
- showCopyDownloadOnCodeBlock: showCopyDownloadOnCodeBlock
16
- });
9
+ function renderModernStreamingContent(content, opts = {}) {
10
+ const {
11
+ isDesktopView = false,
12
+ isSmallScreen = false,
13
+ showCopyDownloadOnCodeBlock = true
14
+ } = opts;
15
+ return (
16
+ // Same frame as ModernMessageGroup's assistant post, so nothing restyles when the stream lands.
17
+ jsx("div", {
18
+ className: `group rounded-lg ${assistantFrameClass(isDesktopView, isSmallScreen)}`,
19
+ children: jsx("div", {
20
+ className: "min-w-0 overflow-hidden py-2",
21
+ children: jsx("div", {
22
+ className: "min-w-0 relative max-w-full w-full text-foreground",
23
+ children: detectContentType(content) === 'code' ? jsx(CodeFormatter, {
24
+ content: content,
25
+ showCopyDownload: showCopyDownloadOnCodeBlock
26
+ }) : jsx(FormattedMessageContent, {
27
+ content: content,
28
+ showCopyDownloadOnCodeBlock: showCopyDownloadOnCodeBlock
29
+ })
30
+ })
31
+ })
32
+ })
33
+ );
17
34
  }export{renderModernStreamingContent};//# sourceMappingURL=modernStreaming.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"modernStreaming.js","sources":["../../../src/components/messages-container-ui/modernStreaming.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":"kIAGA;;;;;;;AAOG;SACa,4BAA4B,CAAC,OAAe,EAAE,2BAA2B,GAAG,IAAI,EAAA;AAC5F,EAAA,OAAA,yBAAyB,CAAA,KAAQ,SAAYA,iBACxC,EAAA;AAIT,IAAC,OAAA,EAAA,OAAA;;;;;;"}
1
+ {"version":3,"file":"modernStreaming.js","sources":["../../../src/components/messages-container-ui/modernStreaming.tsx"],"sourcesContent":[null],"names":[],"mappings":"sJAIA;;;;;;;AAOG;SACa,4BAA4B,CAAC,OAAe,EAAE,OAA+B,EAAE,EAAA;AAC3F,EAAA,MAAA;IACA,aAAO,GAAA,KAAA;IACH,aAAA,GAAA,KAAA;AACA,IAAA,2BAAgB,GAAA;AAexB,GAAC,GAAA,IAAA;;;;;;;;;;;;;;;;;;;;"}
@@ -11,6 +11,7 @@ export interface PlanMessage {
11
11
  createdAt?: Date | string;
12
12
  /** Optional: pass through to the injected message-group renderer (propsConfiguration, props, files, etc.) */
13
13
  propsConfiguration?: any;
14
+ /** `props.animateEntry: true` plays the just-sent entrance on a user bubble (see useBubbleEntrance). */
14
15
  props?: any;
15
16
  files?: any;
16
17
  author?: any;
@@ -60,4 +61,10 @@ export interface MessageGroupRenderProps {
60
61
  }
61
62
  /** The injectable message-group renderer. Defaults to a plain-text bubble renderer. */
62
63
  export type MessageGroupComponentType = React.ComponentType<MessageGroupRenderProps>;
64
+ /** What `renderStreamingContent` gets to frame the stream like the post it becomes. */
65
+ export interface StreamingRenderOptions {
66
+ isDesktopView?: boolean;
67
+ isSmallScreen?: boolean;
68
+ showCopyDownloadOnCodeBlock?: boolean;
69
+ }
63
70
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;;;GAIG;AAEH,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC1B,6GAA6G;IAC7G,kBAAkB,CAAC,EAAE,GAAG,CAAC;IACzB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,yDAAyD;AACzD,MAAM,WAAW,6BAA6B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yGAAyG;IACzG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACpC,iFAAiF;IACjF,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,WAAW,EAAE,GAAG,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,uFAAuF;AACvF,MAAM,MAAM,yBAAyB,GAAG,KAAK,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B;;;;GAIG;AAEH,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC1B,6GAA6G;IAC7G,kBAAkB,CAAC,EAAE,GAAG,CAAC;IACzB,wGAAwG;IACxG,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,yDAAyD;AACzD,MAAM,WAAW,6BAA6B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yGAAyG;IACzG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACpC,iFAAiF;IACjF,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,WAAW,EAAE,GAAG,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,uFAAuF;AACvF,MAAM,MAAM,yBAAyB,GAAG,KAAK,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC;AAErF,uFAAuF;AACvF,MAAM,WAAW,sBAAsB;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACzC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The entrance a message plays when the user has just sent it (a post with
3
+ * `props.animateEntry`). History, chat switches and re-renders never animate:
4
+ * the host flags only the bubble it just appended, and the decision is taken
5
+ * once at mount so the flag going away later cannot cut or replay it.
6
+ *
7
+ * Transform and opacity only, so it runs on the compositor and never moves the
8
+ * layout around it. Runs before paint, so the bubble never flashes in place
9
+ * first. Reduced-motion users get the bubble with no motion.
10
+ */
11
+ export declare function useBubbleEntrance<T extends HTMLElement>(requested: boolean): import("react").RefObject<T>;
12
+ //# sourceMappingURL=useBubbleEntrance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBubbleEntrance.d.ts","sourceRoot":"","sources":["../../../src/components/messages-container-ui/useBubbleEntrance.ts"],"names":[],"mappings":"AASA;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EAAE,SAAS,EAAE,OAAO,gCAW1E"}
@@ -0,0 +1,36 @@
1
+ import {useRef,useState,useLayoutEffect}from'react';/** Rises out of the composer toward its tail corner; ease-out so it lands softly. */
2
+ const KEYFRAMES = [{
3
+ opacity: 0,
4
+ transform: 'translateY(12px) scale(0.96)',
5
+ transformOrigin: 'right bottom'
6
+ }, {
7
+ opacity: 1,
8
+ transform: 'translateY(0) scale(1)',
9
+ transformOrigin: 'right bottom'
10
+ }];
11
+ const TIMING = {
12
+ duration: 320,
13
+ easing: 'cubic-bezier(0.22, 1, 0.36, 1)'
14
+ };
15
+ /**
16
+ * The entrance a message plays when the user has just sent it (a post with
17
+ * `props.animateEntry`). History, chat switches and re-renders never animate:
18
+ * the host flags only the bubble it just appended, and the decision is taken
19
+ * once at mount so the flag going away later cannot cut or replay it.
20
+ *
21
+ * Transform and opacity only, so it runs on the compositor and never moves the
22
+ * layout around it. Runs before paint, so the bubble never flashes in place
23
+ * first. Reduced-motion users get the bubble with no motion.
24
+ */
25
+ function useBubbleEntrance(requested) {
26
+ const ref = useRef(null);
27
+ const [play] = useState(requested);
28
+ useLayoutEffect(() => {
29
+ const el = ref.current;
30
+ if (!play || !el || typeof el.animate !== 'function') return undefined;
31
+ if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return undefined;
32
+ const animation = el.animate(KEYFRAMES, TIMING);
33
+ return () => animation.cancel();
34
+ }, [play]);
35
+ return ref;
36
+ }export{useBubbleEntrance};//# sourceMappingURL=useBubbleEntrance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBubbleEntrance.js","sources":["../../../src/components/messages-container-ui/useBubbleEntrance.ts"],"sourcesContent":[null],"names":[],"mappings":"oDAEA;AACA,MAAM,SAAS,GAAe,CAAA;SACxB,EAAA,CAAA;WACA,EAAO,8BAAgB;EAC3B,eAAA,EAAA;AACF,CAAA,EAAA;AAEA,EAAA,OAAA,EAAA,CAAA;;;;;;;;;AASG;AACH;AACI;;;AAGI;;AACsD;;AACe,SAAA,iBAAiB,CAAA,SAAA,EAAA;QACtF,GAAA,GAAM,MAAA,CAAA,IAAS,CAAG;AAClB,EAAA,MAAA,CAAA,IAAA,CAAA,qBAAuB,CAAA;AAC3B,EAAA,eAAW,CAAA,MAAA;AACX,IAAA,MAAA,KAAW,GAAA,CAAA,OAAA;AACf,IAAC,IAAA,CAAA,IAAA,IAAA,CAAA,EAAA,IAAA,OAAA,EAAA,CAAA,OAAA,KAAA,UAAA,EAAA,OAAA,SAAA;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@admin-layout/tailwind-chat-pro",
3
- "version": "12.2.5-alpha.2",
3
+ "version": "12.2.5-alpha.22",
4
4
  "description": "Customizable base chat / messages container UI (plan mode). The message renderer is injectable, so consumers plug in their own message-group component.",
5
5
  "license": "ISC",
6
6
  "author": "CDMBase LLC",
@@ -13,15 +13,13 @@
13
13
  "build:clean": "rimraf lib",
14
14
  "build:lib": "rollup -c rollup.config.mjs",
15
15
  "build:lib:watch": "yarn build:lib -- --watch",
16
- "jest": "./node_modules/.bin/jest",
17
16
  "prepublish": "yarn build",
18
- "test": "cross-env ENV_FILE=../../config/test/test.env jest",
19
- "test:debug": "npm test -- --runInBand",
20
- "test:watch": "npm test -- --watch",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
21
19
  "watch": "yarn build:lib:watch"
22
20
  },
23
21
  "dependencies": {
24
- "@admin-layout/tailwind-ui": "12.2.5-alpha.2",
22
+ "@admin-layout/tailwind-ui": "12.2.5-alpha.22",
25
23
  "clsx": "^2.1.1",
26
24
  "date-fns": "^4.1.0",
27
25
  "marked": "7.0.5",
@@ -30,12 +28,13 @@
30
28
  "remark-gfm": "^4.0.1"
31
29
  },
32
30
  "devDependencies": {
33
- "common": "12.2.5-alpha.2"
31
+ "common": "12.2.5-alpha.2",
32
+ "vitest": "^3.0.4"
34
33
  },
35
34
  "peerDependencies": {
36
- "@admin-layout/tailwind-ui": "12.2.5-alpha.0",
35
+ "@admin-layout/tailwind-ui": "12.2.5-alpha.15",
37
36
  "@common-stack/components-pro": "*",
38
- "common": "12.2.5-alpha.0",
37
+ "common": "12.2.5-alpha.2",
39
38
  "react": ">=18"
40
39
  },
41
40
  "publishConfig": {
@@ -44,5 +43,5 @@
44
43
  "typescript": {
45
44
  "definition": "lib/index.d.ts"
46
45
  },
47
- "gitHead": "5ccfbf6ff024031ed38926197c3aef7b3f4dc8ee"
46
+ "gitHead": "d8db495e828063c09a588df4e27936a00dce3293"
48
47
  }
@@ -0,0 +1,2 @@
1
+ /** Test stand-in for `common/graphql`: the one hook the renderer's file list calls, with no data. */
2
+ export const useFileUrlQuery = (): { data: undefined; loading: boolean } => ({ data: undefined, loading: false });
@@ -0,0 +1,5 @@
1
+ /** Test stand-in for the generated `common` package: only what the renderer reads at runtime. */
2
+ export enum AiAgentMessageRole {
3
+ Assistant = 'ASSISTANT',
4
+ User = 'USER',
5
+ }
@@ -0,0 +1,2 @@
1
+ /** Test stand-in for `@common-stack/components-pro`: a slot with nothing plugged in. */
2
+ export const Slot = (): null => null;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { PlanModeView } from './PlanModeView';
4
- import type { PlanMessage, MessageGroupComponentType } from './types';
4
+ import type { PlanMessage, MessageGroupComponentType, StreamingRenderOptions } from './types';
5
5
  import type { PlanModeViewInputToolbarProps } from './PlanModeView';
6
6
 
7
7
  export interface MessagesContainerUIProps {
@@ -80,7 +80,7 @@ export interface MessagesContainerUIProps {
80
80
  */
81
81
  MessageGroupComponent?: MessageGroupComponentType;
82
82
  /** Optional: how to render the in-flight streaming assistant content (default: plain text). */
83
- renderStreamingContent?: (content: string) => React.ReactNode;
83
+ renderStreamingContent?: (content: string, opts?: StreamingRenderOptions) => React.ReactNode;
84
84
  /** Optional: override how a PlanMessage is mapped to the post shape handed to the renderer. */
85
85
  mapMessageToPost?: (msg: PlanMessage, currentUser: MessagesContainerUIProps['currentUser']) => any;
86
86
  }