@agent-native/toolkit 0.217.0 → 0.219.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 (206) hide show
  1. package/agent-native.eject.json +21 -0
  2. package/dist/app/agent-page/AgentContextTab.d.ts.map +1 -1
  3. package/dist/app/agent-page/AgentContextTab.js +1 -0
  4. package/dist/app/agent-page/AgentContextTab.js.map +1 -1
  5. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  6. package/dist/app/agentkit/react/components.js +1 -1
  7. package/dist/app/agentkit/react/components.js.map +1 -1
  8. package/dist/app/agentkit/react/context.d.ts +4 -1
  9. package/dist/app/agentkit/react/context.d.ts.map +1 -1
  10. package/dist/app/agentkit/react/context.js +3 -1
  11. package/dist/app/agentkit/react/context.js.map +1 -1
  12. package/dist/app/agentkit/react/data-chart-layout.d.ts +30 -0
  13. package/dist/app/agentkit/react/data-chart-layout.d.ts.map +1 -0
  14. package/dist/app/agentkit/react/data-chart-layout.js +2 -0
  15. package/dist/app/agentkit/react/data-chart-layout.js.map +1 -0
  16. package/dist/app/agentkit/react/index.d.ts +1 -0
  17. package/dist/app/agentkit/react/index.d.ts.map +1 -1
  18. package/dist/app/agentkit/react/index.js +1 -0
  19. package/dist/app/agentkit/react/index.js.map +1 -1
  20. package/dist/app/agentkit/react/root.d.ts +3 -1
  21. package/dist/app/agentkit/react/root.d.ts.map +1 -1
  22. package/dist/app/agentkit/react/root.js +2 -2
  23. package/dist/app/agentkit/react/root.js.map +1 -1
  24. package/dist/app/agentkit/react/styles.css +31 -6
  25. package/dist/app/agentkit/react/transcript-layout.d.ts +2 -0
  26. package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -1
  27. package/dist/app/agentkit/react/transcript-layout.js.map +1 -1
  28. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  29. package/dist/app/chat/AgentKitAssistantChat.js +2 -2
  30. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  31. package/dist/app/chat/AgentPanel.js +6 -6
  32. package/dist/app/chat/AgentPanel.js.map +1 -1
  33. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  34. package/dist/app/chat/MultiTabAssistantChat.js +53 -95
  35. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  36. package/dist/app/chat/agentkit-chat/action-widget.d.ts.map +1 -1
  37. package/dist/app/chat/agentkit-chat/action-widget.js +4 -1
  38. package/dist/app/chat/agentkit-chat/action-widget.js.map +1 -1
  39. package/dist/app/chat/agentkit-chat/rail.d.ts +1 -1
  40. package/dist/app/chat/agentkit-chat/rail.d.ts.map +1 -1
  41. package/dist/app/chat/agentkit-chat/rail.js +1 -1
  42. package/dist/app/chat/agentkit-chat/rail.js.map +1 -1
  43. package/dist/app/chat/chat/index.d.ts +2 -1
  44. package/dist/app/chat/chat/index.d.ts.map +1 -1
  45. package/dist/app/chat/chat/index.js +1 -1
  46. package/dist/app/chat/chat/index.js.map +1 -1
  47. package/dist/app/chat/chat/surface-types.d.ts +3 -0
  48. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  49. package/dist/app/chat/chat/surface-types.js.map +1 -1
  50. package/dist/app/chat/chat/tool-call-display.d.ts +5 -1
  51. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  52. package/dist/app/chat/chat/tool-call-display.js +6 -4
  53. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  54. package/dist/app/chat/chat/tool-render-registry.d.ts +4 -0
  55. package/dist/app/chat/chat/tool-render-registry.d.ts.map +1 -1
  56. package/dist/app/chat/chat/tool-render-registry.js.map +1 -1
  57. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts +3 -1
  58. package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
  59. package/dist/app/chat/chat/widgets/DataChartRenderer.js +116 -26
  60. package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
  61. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -1
  62. package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
  63. package/dist/app/chat/chat/widgets/DataChartWidget.js +21 -2
  64. package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
  65. package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts +3 -1
  66. package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts.map +1 -1
  67. package/dist/app/chat/chat/widgets/DataInsightsWidget.js +2 -2
  68. package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
  69. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +1 -1
  70. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -1
  71. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +3 -1
  72. package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -1
  73. package/dist/app/chat/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
  74. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
  75. package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
  76. package/dist/app/chat/chat-first/chat-history.d.ts +13 -2
  77. package/dist/app/chat/chat-first/chat-history.d.ts.map +1 -1
  78. package/dist/app/chat/chat-first/chat-history.js +24 -3
  79. package/dist/app/chat/chat-first/chat-history.js.map +1 -1
  80. package/dist/app/chat/conversation/AgentConversation.d.ts.map +1 -1
  81. package/dist/app/chat/conversation/AgentConversation.js +6 -1
  82. package/dist/app/chat/conversation/AgentConversation.js.map +1 -1
  83. package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts +6 -0
  84. package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts.map +1 -0
  85. package/dist/app/chat/ownership-history/ChatOwnerAvatar.js +9 -0
  86. package/dist/app/chat/ownership-history/ChatOwnerAvatar.js.map +1 -0
  87. package/dist/app/chat/ownership-history/index.d.ts +11 -0
  88. package/dist/app/chat/ownership-history/index.d.ts.map +1 -0
  89. package/dist/app/chat/ownership-history/index.js +22 -0
  90. package/dist/app/chat/ownership-history/index.js.map +1 -0
  91. package/dist/app/chat/ownership-history/rows.d.ts +27 -0
  92. package/dist/app/chat/ownership-history/rows.d.ts.map +1 -0
  93. package/dist/app/chat/ownership-history/rows.js +101 -0
  94. package/dist/app/chat/ownership-history/rows.js.map +1 -0
  95. package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts +52 -0
  96. package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts.map +1 -0
  97. package/dist/app/chat/ownership-history/useOwnershipChatHistory.js +497 -0
  98. package/dist/app/chat/ownership-history/useOwnershipChatHistory.js.map +1 -0
  99. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  100. package/dist/app/i18n/catalogs/ar-SA.js +3 -0
  101. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  102. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  103. package/dist/app/i18n/catalogs/de-DE.js +3 -0
  104. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  105. package/dist/app/i18n/catalogs/en-US.d.ts +3 -0
  106. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  107. package/dist/app/i18n/catalogs/en-US.js +3 -0
  108. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  109. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  110. package/dist/app/i18n/catalogs/es-ES.js +3 -0
  111. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  112. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  113. package/dist/app/i18n/catalogs/fr-FR.js +3 -0
  114. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  115. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  116. package/dist/app/i18n/catalogs/hi-IN.js +3 -0
  117. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  118. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  119. package/dist/app/i18n/catalogs/ja-JP.js +3 -0
  120. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  121. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  122. package/dist/app/i18n/catalogs/ko-KR.js +3 -0
  123. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  124. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  125. package/dist/app/i18n/catalogs/pt-BR.js +3 -0
  126. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  127. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  128. package/dist/app/i18n/catalogs/zh-CN.js +3 -0
  129. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  130. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  131. package/dist/app/i18n/catalogs/zh-TW.js +3 -0
  132. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  133. package/dist/app/shared/CommandMenu.d.ts +2 -0
  134. package/dist/app/shared/CommandMenu.d.ts.map +1 -1
  135. package/dist/app/shared/CommandMenu.js +11 -6
  136. package/dist/app/shared/CommandMenu.js.map +1 -1
  137. package/dist/app/sharing/ShareButton.d.ts.map +1 -1
  138. package/dist/app/sharing/ShareButton.js +9 -8
  139. package/dist/app/sharing/ShareButton.js.map +1 -1
  140. package/dist/chat-history/ChatHistoryList.d.ts +16 -3
  141. package/dist/chat-history/ChatHistoryList.d.ts.map +1 -1
  142. package/dist/chat-history/ChatHistoryList.js +25 -15
  143. package/dist/chat-history/ChatHistoryList.js.map +1 -1
  144. package/dist/chat-history.css +109 -9
  145. package/dist/composer/TiptapComposer.js +1 -1
  146. package/dist/composer/TiptapComposer.js.map +1 -1
  147. package/package.json +10 -3
  148. package/src/app/agent-page/AgentContextTab.spec.ts +50 -2
  149. package/src/app/agent-page/AgentContextTab.tsx +1 -0
  150. package/src/app/agentkit/react/components.interaction.spec.tsx +2 -0
  151. package/src/app/agentkit/react/components.spec.tsx +11 -6
  152. package/src/app/agentkit/react/components.tsx +3 -0
  153. package/src/app/agentkit/react/context.tsx +6 -0
  154. package/src/app/agentkit/react/data-chart-layout.ts +25 -0
  155. package/src/app/agentkit/react/index.ts +1 -0
  156. package/src/app/agentkit/react/root.tsx +4 -0
  157. package/src/app/agentkit/react/styles.css +31 -6
  158. package/src/app/agentkit/react/transcript-layout.ts +2 -0
  159. package/src/app/chat/AgentKitAssistantChat.spec.tsx +201 -27
  160. package/src/app/chat/AgentKitAssistantChat.tsx +3 -0
  161. package/src/app/chat/AgentPanel.tsx +8 -8
  162. package/src/app/chat/MultiTabAssistantChat.spec.tsx +270 -3
  163. package/src/app/chat/MultiTabAssistantChat.tsx +81 -158
  164. package/src/app/chat/agentkit-chat/action-widget.spec.tsx +62 -0
  165. package/src/app/chat/agentkit-chat/action-widget.tsx +4 -0
  166. package/src/app/chat/agentkit-chat/rail.ts +11 -0
  167. package/src/app/chat/chat/index.ts +10 -0
  168. package/src/app/chat/chat/surface-types.ts +3 -0
  169. package/src/app/chat/chat/tool-call-display.spec.tsx +29 -0
  170. package/src/app/chat/chat/tool-call-display.tsx +17 -1
  171. package/src/app/chat/chat/tool-render-registry.tsx +5 -0
  172. package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +377 -17
  173. package/src/app/chat/chat/widgets/DataChartRenderer.tsx +232 -63
  174. package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +67 -0
  175. package/src/app/chat/chat/widgets/DataChartWidget.tsx +39 -5
  176. package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +13 -2
  177. package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +6 -4
  178. package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -1
  179. package/src/app/chat/chat-first/chat-history.spec.tsx +79 -0
  180. package/src/app/chat/chat-first/chat-history.tsx +75 -22
  181. package/src/app/chat/conversation/AgentConversation.spec.tsx +51 -1
  182. package/src/app/chat/conversation/AgentConversation.tsx +11 -2
  183. package/src/app/chat/ownership-history/ChatOwnerAvatar.tsx +27 -0
  184. package/src/app/chat/ownership-history/index.tsx +67 -0
  185. package/src/app/chat/ownership-history/ownership-history.spec.tsx +3146 -0
  186. package/src/app/chat/ownership-history/rows.ts +156 -0
  187. package/src/app/chat/ownership-history/useOwnershipChatHistory.ts +709 -0
  188. package/src/app/i18n/catalogs/ar-SA.ts +3 -0
  189. package/src/app/i18n/catalogs/de-DE.ts +3 -0
  190. package/src/app/i18n/catalogs/en-US.ts +3 -0
  191. package/src/app/i18n/catalogs/es-ES.ts +3 -0
  192. package/src/app/i18n/catalogs/fr-FR.ts +3 -0
  193. package/src/app/i18n/catalogs/hi-IN.ts +3 -0
  194. package/src/app/i18n/catalogs/ja-JP.ts +3 -0
  195. package/src/app/i18n/catalogs/ko-KR.ts +3 -0
  196. package/src/app/i18n/catalogs/pt-BR.ts +3 -0
  197. package/src/app/i18n/catalogs/zh-CN.ts +3 -0
  198. package/src/app/i18n/catalogs/zh-TW.ts +3 -0
  199. package/src/app/shared/CommandMenu.spec.tsx +175 -6
  200. package/src/app/shared/CommandMenu.tsx +24 -7
  201. package/src/app/sharing/ShareButton.tsx +12 -10
  202. package/src/chat-history/ChatHistoryList.spec.tsx +51 -0
  203. package/src/chat-history/ChatHistoryList.tsx +87 -26
  204. package/src/chat-history/ChatHistoryRailSections.spec.tsx +12 -4
  205. package/src/chat-history.css +109 -9
  206. package/src/composer/TiptapComposer.tsx +1 -1
@@ -59,6 +59,302 @@ describe("DataChartRenderer theme colors", () => {
59
59
  expect(markup).toContain('stroke="hsl(var(--chart-grid, var(--border)))"');
60
60
  });
61
61
 
62
+ it("applies app chart sizing, plot insets, typography, and integer tick labels", () => {
63
+ const markup = renderToStaticMarkup(
64
+ <DataChartRenderer
65
+ chart={{
66
+ type: "line",
67
+ xKey: "day",
68
+ series: [{ key: "value", label: "Value" }],
69
+ data: [
70
+ { day: "First", value: 0 },
71
+ { day: "Last", value: 1 },
72
+ ],
73
+ valueAxisTickInterval: 0.1,
74
+ }}
75
+ layout={{
76
+ plotInsets: { top: 6, right: 8, bottom: 29 },
77
+ labelFontFamily: "IBM Plex Sans",
78
+ labelFontSize: 14,
79
+ labelTone: "foreground",
80
+ }}
81
+ />,
82
+ );
83
+
84
+ expect(markup).toContain('font-family="IBM Plex Sans"');
85
+ expect(markup).toContain('font-size="14"');
86
+ expect(markup).toMatch(
87
+ /<text[^>]*fill="hsl\(var\(--foreground\)\)"[^>]*>First<\/text>/,
88
+ );
89
+ expect(markup).toMatch(/<line x1="[^"]+" x2="632" y1="6" y2="6"/);
90
+ expect(markup).toMatch(/<line x1="[^"]+" x2="632" y1="331" y2="331"/);
91
+ expect(markup).toContain(">1</text>");
92
+ expect(markup).not.toContain(">1.0</text>");
93
+ });
94
+
95
+ it("preserves one-decimal ticks without an app layout override", () => {
96
+ const markup = renderToStaticMarkup(
97
+ <DataChartRenderer
98
+ chart={{
99
+ type: "line",
100
+ xKey: "day",
101
+ series: [{ key: "value", label: "Value" }],
102
+ data: [
103
+ { day: "First", value: 0 },
104
+ { day: "Last", value: 1 },
105
+ ],
106
+ valueAxisTickInterval: 0.1,
107
+ }}
108
+ />,
109
+ );
110
+
111
+ expect(markup).toContain(">0.0</text>");
112
+ expect(markup).toContain(">1.0</text>");
113
+ });
114
+
115
+ it("keeps positive value labels outside horizontal bars with a right inset", () => {
116
+ const markup = renderToStaticMarkup(
117
+ <DataChartRenderer
118
+ chart={{
119
+ type: "bar",
120
+ layout: "horizontal",
121
+ xKey: "day",
122
+ series: [{ key: "value", label: "Value" }],
123
+ data: [{ day: "Monday", value: 3 }],
124
+ showValueLabels: true,
125
+ valueAxisTickInterval: 0.2,
126
+ }}
127
+ layout={{ plotInsets: { right: 8 } }}
128
+ />,
129
+ );
130
+
131
+ const plotRightEdge = Number(
132
+ markup.match(/<line x1="[\d.]+" x2="([\d.]+)" y1="14" y2="326"/)?.[1],
133
+ );
134
+ expect(plotRightEdge).toBeLessThanOrEqual(632);
135
+ expect(markup).toContain('fill="hsl(var(--muted-foreground))"');
136
+ const maximumBarValueLabel = markup.match(
137
+ /<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="start">3<\/text>/,
138
+ );
139
+ expect(maximumBarValueLabel).not.toBeNull();
140
+ expect(Number(maximumBarValueLabel?.[1])).toBeGreaterThan(plotRightEdge);
141
+ expect(Number(maximumBarValueLabel?.[1]) + 8).toBeLessThanOrEqual(640);
142
+ });
143
+
144
+ it("rejects inset combinations that leave no positive plot area", () => {
145
+ const renderWithInsets = (plotInsets: { right?: number; top?: number }) =>
146
+ renderToStaticMarkup(
147
+ <DataChartRenderer
148
+ chart={{
149
+ type: "line",
150
+ xKey: "day",
151
+ series: [{ key: "value", label: "Value" }],
152
+ data: [{ day: "Monday", value: 1 }],
153
+ }}
154
+ layout={{ plotInsets }}
155
+ />,
156
+ );
157
+
158
+ expect(() => renderWithInsets({ right: 600 })).toThrow(
159
+ "positive chart plot area",
160
+ );
161
+ expect(() => renderWithInsets({ top: 400 })).toThrow(
162
+ "positive chart plot area",
163
+ );
164
+ });
165
+
166
+ it("keeps a vertical bar chart's final category label inside the SVG", () => {
167
+ const markup = renderToStaticMarkup(
168
+ <DataChartRenderer
169
+ chart={{
170
+ type: "bar",
171
+ xKey: "day",
172
+ series: [{ key: "value", label: "Value" }],
173
+ data: Array.from({ length: 10 }, (_, index) => ({
174
+ day: index === 9 ? "Last category label" : `Day ${index}`,
175
+ value: index,
176
+ })),
177
+ }}
178
+ layout={{ plotInsets: { right: 8 } }}
179
+ />,
180
+ );
181
+ const finalCategory = markup.match(
182
+ /<text[^>]*x="([\d.]+)" y="348"[^>]*text-anchor="end">Last category\.\.\.<\/text>/,
183
+ );
184
+
185
+ expect(finalCategory).not.toBeNull();
186
+ expect(Number(finalCategory?.[1])).toBeLessThanOrEqual(632);
187
+ });
188
+
189
+ it("keeps a long first category label inside the SVG", () => {
190
+ const markup = renderToStaticMarkup(
191
+ <DataChartRenderer
192
+ chart={{
193
+ type: "line",
194
+ xKey: "day",
195
+ series: [{ key: "value", label: "Value" }],
196
+ data: [
197
+ { day: "First category label", value: 0 },
198
+ { day: "Last", value: 1 },
199
+ ],
200
+ }}
201
+ />,
202
+ );
203
+
204
+ expect(markup).toMatch(
205
+ /<text[^>]*x="46" y="348"[^>]*text-anchor="start">First categor\.\.\.<\/text>/,
206
+ );
207
+ });
208
+
209
+ it("keeps top vertical-bar value labels above the bar at a zero inset", () => {
210
+ const markup = renderToStaticMarkup(
211
+ <DataChartRenderer
212
+ chart={{
213
+ type: "bar",
214
+ xKey: "day",
215
+ series: [{ key: "value", label: "Value" }],
216
+ data: [{ day: "Monday", value: 1 }],
217
+ showValueLabels: true,
218
+ }}
219
+ layout={{ plotInsets: { top: 0 } }}
220
+ />,
221
+ );
222
+ const valueLabel = markup.match(
223
+ /<text(?=[^>]*data-value-label="true")(?=[^>]*>1<\/text>)[^>]*y="([\d.]+)"[^>]*>/,
224
+ );
225
+ const bar = markup.match(/<rect[^>]*y="([\d.]+)"/);
226
+
227
+ expect(valueLabel).not.toBeNull();
228
+ expect(Number(valueLabel?.[1])).toBeGreaterThanOrEqual(11);
229
+ expect(bar).not.toBeNull();
230
+ expect(Number(bar?.[1]) - Number(valueLabel?.[1])).toBeGreaterThanOrEqual(
231
+ 5,
232
+ );
233
+ });
234
+
235
+ it("keeps final vertical-bar value labels inside the SVG at a small right inset", () => {
236
+ const markup = renderToStaticMarkup(
237
+ <DataChartRenderer
238
+ chart={{
239
+ type: "bar",
240
+ xKey: "day",
241
+ series: [{ key: "value", label: "Value" }],
242
+ data: Array.from({ length: 60 }, (_, index) => ({
243
+ day: `Day ${index}`,
244
+ value: index === 59 ? 1_234_567 : 0,
245
+ })),
246
+ showValueLabels: true,
247
+ }}
248
+ layout={{ plotInsets: { right: 8 } }}
249
+ />,
250
+ );
251
+ const valueLabels = Array.from(
252
+ markup.matchAll(
253
+ /<text(?=[^>]*data-value-label="true")([^>]*)>([^<]+)<\/text>/g,
254
+ ),
255
+ );
256
+ const finalValueLabel = valueLabels.at(-1);
257
+ const finalLabelX = Number(
258
+ finalValueLabel?.[1].match(/\bx="([\d.]+)"/)?.[1],
259
+ );
260
+ const finalLabelWidth =
261
+ Array.from(finalValueLabel?.[2] ?? "").length * 11 * 0.65;
262
+
263
+ expect(finalValueLabel).toBeDefined();
264
+ expect(finalLabelX + finalLabelWidth / 2).toBeLessThanOrEqual(640);
265
+ });
266
+
267
+ it("preserves the top value-axis label when the configured top inset is zero", () => {
268
+ const markup = renderToStaticMarkup(
269
+ <DataChartRenderer
270
+ chart={{
271
+ type: "line",
272
+ xKey: "day",
273
+ series: [{ key: "value", label: "Value" }],
274
+ data: [
275
+ { day: "Monday", value: 0 },
276
+ { day: "Tuesday", value: 1 },
277
+ ],
278
+ valueAxisTickInterval: 0.5,
279
+ }}
280
+ layout={{ labelFontSize: 12, plotInsets: { top: 0 } }}
281
+ />,
282
+ );
283
+ const topTick = markup.match(
284
+ /<text(?=[^>]*data-value-axis-tick="true")[^>]*y="([\d.]+)"[^>]*>1<\/text>/,
285
+ );
286
+
287
+ expect(topTick).not.toBeNull();
288
+ expect(Number(topTick?.[1])).toBeGreaterThanOrEqual(12);
289
+ });
290
+
291
+ it("keeps horizontal axis tick labels inside the chart at zero edge insets", () => {
292
+ const markup = renderToStaticMarkup(
293
+ <DataChartRenderer
294
+ chart={{
295
+ type: "bar",
296
+ layout: "horizontal",
297
+ xKey: "day",
298
+ series: [{ key: "value", label: "Value" }],
299
+ data: [{ day: "Monday", value: 3 }],
300
+ valueAxisTickInterval: 1,
301
+ xAxisTitle: "Value",
302
+ }}
303
+ layout={{ plotInsets: { right: 0, bottom: 0 } }}
304
+ />,
305
+ );
306
+ const finalTick = Array.from(
307
+ markup.matchAll(
308
+ /<text[^>]*x="([\d.]+)" y="([\d.]+)"[^>]*>([^<]+)<\/text>/g,
309
+ ),
310
+ )
311
+ .filter((match) => match[3] === "3")
312
+ .slice(-1)[0];
313
+
314
+ expect(finalTick).toBeDefined();
315
+ expect(Number(finalTick?.[1])).toBeLessThan(640);
316
+ expect(Number(finalTick?.[1]) + 4).toBeLessThanOrEqual(640);
317
+ expect(Number(finalTick?.[2])).toBeLessThan(340);
318
+ });
319
+
320
+ it("keeps vertical axis ticks and category labels clear at a zero bottom inset", () => {
321
+ const markup = renderToStaticMarkup(
322
+ <DataChartRenderer
323
+ chart={{
324
+ type: "line",
325
+ xKey: "day",
326
+ series: [{ key: "value", label: "Value" }],
327
+ data: [
328
+ { day: "Monday", value: 0 },
329
+ { day: "Tuesday", value: 1 },
330
+ ],
331
+ valueAxisTickInterval: 0.5,
332
+ }}
333
+ layout={{ labelFontSize: 12, plotInsets: { bottom: 0 } }}
334
+ />,
335
+ );
336
+ const minimumTick = markup.match(
337
+ /<text(?=[^>]*data-value-axis-tick="true")(?=[^>]*>0<\/text>)[^>]*y="([\d.]+)"/,
338
+ );
339
+ const categoryLabel = markup.match(
340
+ /<text[^>]*y="([\d.]+)"[^>]*>Tuesday<\/text>/,
341
+ );
342
+ const plotBaseline = Math.max(
343
+ ...Array.from(
344
+ markup.matchAll(/<line[^>]*y1="([\d.]+)" y2="([\d.]+)"/g),
345
+ (match) =>
346
+ match[1] === match[2] ? Number(match[1]) : Number.NEGATIVE_INFINITY,
347
+ ),
348
+ );
349
+
350
+ expect(minimumTick).not.toBeNull();
351
+ expect(Number(minimumTick?.[1]) + 3).toBeLessThanOrEqual(360);
352
+ expect(categoryLabel).not.toBeNull();
353
+ expect(Number(categoryLabel?.[1]) - plotBaseline).toBeGreaterThanOrEqual(
354
+ 12,
355
+ );
356
+ });
357
+
62
358
  it("uses the requested chart content height", () => {
63
359
  const markup = renderToStaticMarkup(
64
360
  <DataChartRenderer chart={{ ...chart, contentHeight: 400 }} />,
@@ -217,7 +513,7 @@ describe("DataChartRenderer theme colors", () => {
217
513
  expect(numericLabels.at(-1)).toBe("99");
218
514
  const tickLabelPositions = Array.from(
219
515
  markup.matchAll(
220
- /<text x="([\d.]+)"[^>]*text-anchor="middle">\d+<\/text>/g,
516
+ /<text[^>]*x="([\d.]+)"[^>]*text-anchor="middle">\d+<\/text>/g,
221
517
  ),
222
518
  (match) => Number(match[1]),
223
519
  );
@@ -290,7 +586,7 @@ describe("DataChartRenderer theme colors", () => {
290
586
  );
291
587
  const valueTickLabels = Array.from(
292
588
  markup.matchAll(
293
- /<text x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
589
+ /<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
294
590
  ),
295
591
  (match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
296
592
  );
@@ -321,7 +617,7 @@ describe("DataChartRenderer theme colors", () => {
321
617
  );
322
618
  const valueTickLabels = Array.from(
323
619
  markup.matchAll(
324
- /<text x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
620
+ /<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
325
621
  ),
326
622
  (match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
327
623
  );
@@ -344,7 +640,7 @@ describe("DataChartRenderer theme colors", () => {
344
640
  );
345
641
  const valueTickLabels = Array.from(
346
642
  markup.matchAll(
347
- /<text x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
643
+ /<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
348
644
  ),
349
645
  (match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
350
646
  );
@@ -379,7 +675,7 @@ describe("DataChartRenderer theme colors", () => {
379
675
  );
380
676
  const valueLabels = Array.from(
381
677
  markup.matchAll(
382
- /<text x="([\d.-]+)" y="([\d.]+)"[^>]*font-size="24"[^>]*text-anchor="(start|end)">([^<]+)<\/text>/g,
678
+ /<text[^>]*x="([\d.-]+)" y="([\d.]+)"[^>]*font-size="24"[^>]*text-anchor="(start|end)">([^<]+)<\/text>/g,
383
679
  ),
384
680
  (match) => ({
385
681
  x: Number(match[1]),
@@ -426,7 +722,9 @@ describe("DataChartRenderer theme colors", () => {
426
722
  />,
427
723
  );
428
724
  const tickLabels = Array.from(
429
- markup.matchAll(/<text[^>]*text-anchor="end">([^<]+)<\/text>/g),
725
+ markup.matchAll(
726
+ /<text(?=[^>]*data-value-axis-tick="true")[^>]*>([^<]+)<\/text>/g,
727
+ ),
430
728
  (match) => match[1],
431
729
  );
432
730
 
@@ -454,7 +752,9 @@ describe("DataChartRenderer theme colors", () => {
454
752
  />,
455
753
  );
456
754
  const tickLabels = Array.from(
457
- markup.matchAll(/<text[^>]*text-anchor="end">([^<]+)<\/text>/g),
755
+ markup.matchAll(
756
+ /<text(?=[^>]*data-value-axis-tick="true")[^>]*>([^<]+)<\/text>/g,
757
+ ),
458
758
  (match) => match[1],
459
759
  );
460
760
 
@@ -466,6 +766,40 @@ describe("DataChartRenderer theme colors", () => {
466
766
  ]);
467
767
  });
468
768
 
769
+ it("keeps integer data labels compact while axis ticks retain interval precision", () => {
770
+ const markup = renderToStaticMarkup(
771
+ <DataChartRenderer
772
+ chart={{
773
+ type: "line",
774
+ xKey: "day",
775
+ series: [{ key: "value", label: "Value" }],
776
+ data: [
777
+ { day: "Monday", value: 0 },
778
+ { day: "Tuesday", value: 7 },
779
+ ],
780
+ showValueLabels: true,
781
+ valueAxisTickInterval: 0.5,
782
+ }}
783
+ />,
784
+ );
785
+ const axisLabels = Array.from(
786
+ markup.matchAll(
787
+ /<text(?=[^>]*data-value-axis-tick="true")[^>]*>([^<]+)<\/text>/g,
788
+ ),
789
+ (match) => match[1],
790
+ );
791
+ const valueLabels = Array.from(
792
+ markup.matchAll(
793
+ /<text(?=[^>]*data-value-label="true")[^>]*>([^<]+)<\/text>/g,
794
+ ),
795
+ (match) => match[1],
796
+ );
797
+
798
+ expect(axisLabels).toContain("7.0");
799
+ expect(valueLabels).toContain("7");
800
+ expect(valueLabels).not.toContain("7.0");
801
+ });
802
+
469
803
  it("compacts wide vertical ticks while keeping them clear of the axis title", () => {
470
804
  const markup = renderToStaticMarkup(
471
805
  <DataChartRenderer
@@ -484,7 +818,7 @@ describe("DataChartRenderer theme colors", () => {
484
818
  );
485
819
  const tickLabels = Array.from(
486
820
  markup.matchAll(
487
- /<text x="([\d.]+)" y="([\d.]+)"[^>]*text-anchor="end">([^<]+)<\/text>/g,
821
+ /<text[^>]*x="([\d.]+)" y="([\d.]+)"[^>]*text-anchor="end">([^<]+)<\/text>/g,
488
822
  ),
489
823
  (match) => ({
490
824
  x: Number(match[1]),
@@ -503,7 +837,7 @@ describe("DataChartRenderer theme colors", () => {
503
837
  Number(plotBounds?.[2]) - Number(plotBounds?.[1]),
504
838
  ).toBeGreaterThanOrEqual(32);
505
839
  const title = markup.match(
506
- /<text x="([\d.]+)" y="[\d.]+" transform="rotate\(-90 [\d.]+ [\d.]+\)"[^>]*font-size="([\d.]+)"[^>]*>Revenue<\/text>/,
840
+ /<text[^>]*x="([\d.]+)" y="[\d.]+" transform="rotate\(-90 [\d.]+ [\d.]+\)"[^>]*font-size="([\d.]+)"[^>]*>Revenue<\/text>/,
507
841
  );
508
842
  expect(title).not.toBeNull();
509
843
  const titleRight = Number(title?.[1]) + Number(title?.[2]) / 2;
@@ -563,7 +897,7 @@ describe("DataChartRenderer theme colors", () => {
563
897
  );
564
898
  const horizontalLabels = Array.from(
565
899
  horizontalMarkup.matchAll(
566
- /<text x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
900
+ /<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
567
901
  ),
568
902
  (match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
569
903
  );
@@ -602,7 +936,7 @@ describe("DataChartRenderer theme colors", () => {
602
936
  );
603
937
  const verticalLabels = Array.from(
604
938
  verticalMarkup.matchAll(
605
- /<text x="[\d.]+" y="([\d.]+)"[^>]*text-anchor="end">([^<]+)<\/text>/g,
939
+ /<text[^>]*x="[\d.]+" y="([\d.]+)"[^>]*text-anchor="end">([^<]+)<\/text>/g,
606
940
  ),
607
941
  (match) => ({ y: Number(match[1]), label: match[2] ?? "" }),
608
942
  ).sort((first, second) => first.y - second.y);
@@ -619,6 +953,32 @@ describe("DataChartRenderer theme colors", () => {
619
953
  ).toBe(true);
620
954
  });
621
955
 
956
+ it("keeps line value labels inside the canvas with no bottom inset", () => {
957
+ const markup = renderToStaticMarkup(
958
+ <DataChartRenderer
959
+ chart={{
960
+ type: "line",
961
+ xKey: "day",
962
+ series: [{ key: "value", label: "Value" }],
963
+ data: [
964
+ { day: "First", value: 0 },
965
+ { day: "Last", value: 1 },
966
+ ],
967
+ showValueLabels: true,
968
+ }}
969
+ layout={{ plotInsets: { bottom: 0 } }}
970
+ />,
971
+ );
972
+ const minimumValueLabelY = Array.from(
973
+ markup.matchAll(
974
+ /<text(?=[^>]*data-value-label="true")(?=[^>]*>0<\/text>)[^>]*y="([\d.]+)"[^>]*>/g,
975
+ ),
976
+ (match) => Number(match[1]),
977
+ ).filter((y) => y < 360);
978
+
979
+ expect(minimumValueLabelY.some((y) => y <= 356)).toBe(true);
980
+ });
981
+
622
982
  it("reserves room for custom decimal value-axis labels", () => {
623
983
  const markup = renderToStaticMarkup(
624
984
  <DataChartRenderer
@@ -632,7 +992,7 @@ describe("DataChartRenderer theme colors", () => {
632
992
  />,
633
993
  );
634
994
  const tickLabels = Array.from(
635
- markup.matchAll(/<text x="([\d.]+)"[^>]*>(0\.000[123])<\/text>/g),
995
+ markup.matchAll(/<text[^>]*x="([\d.]+)"[^>]*>(0\.000[123])<\/text>/g),
636
996
  (match) => ({ x: Number(match[1]), label: match[2] }),
637
997
  );
638
998
 
@@ -752,10 +1112,10 @@ describe("DataChartRenderer theme colors", () => {
752
1112
  />,
753
1113
  );
754
1114
  const categoryLabel = markup.match(
755
- /<text x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="10" text-anchor="end">A very long c\.\.\.<\/text>/,
1115
+ /<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="10" text-anchor="end">A very long c\.\.\.<\/text>/,
756
1116
  );
757
1117
  const largestValueLabel = markup.match(
758
- /<text x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="start">123,456<\/text>/,
1118
+ /<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="start">123,456<\/text>/,
759
1119
  );
760
1120
 
761
1121
  expect(categoryLabel).not.toBeNull();
@@ -779,10 +1139,10 @@ describe("DataChartRenderer theme colors", () => {
779
1139
  );
780
1140
  const negativeBarX = markup.match(/<rect x="([\d.]+)"/);
781
1141
  const negativeBarLabel = markup.match(
782
- /<text x="([\d.]+)"[^>]*text-anchor="end">-123,456<\/text>/,
1142
+ /<text[^>]*x="([\d.]+)"[^>]*text-anchor="end">-123,456<\/text>/,
783
1143
  );
784
1144
  const categoryLabel = markup.match(
785
- /<text x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" text-anchor="end">Monday<\/text>/,
1145
+ /<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" text-anchor="end">Monday<\/text>/,
786
1146
  );
787
1147
 
788
1148
  expect(negativeBarX).not.toBeNull();
@@ -839,7 +1199,7 @@ describe("DataChartRenderer theme colors", () => {
839
1199
  />,
840
1200
  );
841
1201
  const finalValueLabel = markup.match(
842
- /<text x="([\d.]+)" y="[\d.]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="end">123,456,789<\/text>/,
1202
+ /<text[^>]*x="([\d.]+)" y="[\d.]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="end">123,456,789<\/text>/,
843
1203
  );
844
1204
 
845
1205
  expect(finalValueLabel).not.toBeNull();