@vertesia/ui 0.80.0-dev.20251121 → 0.80.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 (277) hide show
  1. package/README.md +105 -0
  2. package/lib/esm/core/components/MenuList.js +2 -5
  3. package/lib/esm/core/components/MenuList.js.map +1 -1
  4. package/lib/esm/core/components/MessageBox.js +1 -1
  5. package/lib/esm/core/components/MessageBox.js.map +1 -1
  6. package/lib/esm/core/components/shadcn/dialog.js +16 -2
  7. package/lib/esm/core/components/shadcn/dialog.js.map +1 -1
  8. package/lib/esm/core/components/shadcn/filters/filter/SelectFilter.js +6 -9
  9. package/lib/esm/core/components/shadcn/filters/filter/SelectFilter.js.map +1 -1
  10. package/lib/esm/core/components/shadcn/filters/filterBar.js +1 -1
  11. package/lib/esm/core/components/shadcn/filters/filterBar.js.map +1 -1
  12. package/lib/esm/core/components/shadcn/selectBox.js +23 -5
  13. package/lib/esm/core/components/shadcn/selectBox.js.map +1 -1
  14. package/lib/esm/core/components/shadcn/tabs.js +3 -3
  15. package/lib/esm/core/components/shadcn/tabs.js.map +1 -1
  16. package/lib/esm/env/index.js +3 -0
  17. package/lib/esm/env/index.js.map +1 -1
  18. package/lib/esm/features/agent/chat/AgentChart.js +184 -0
  19. package/lib/esm/features/agent/chat/AgentChart.js.map +1 -0
  20. package/lib/esm/features/agent/chat/ModernAgentConversation.js +87 -10
  21. package/lib/esm/features/agent/chat/ModernAgentConversation.js.map +1 -1
  22. package/lib/esm/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +6 -2
  23. package/lib/esm/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  24. package/lib/esm/features/agent/chat/ModernAgentOutput/Header.js +4 -4
  25. package/lib/esm/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  26. package/lib/esm/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.js +4 -1
  27. package/lib/esm/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.js.map +1 -1
  28. package/lib/esm/features/agent/chat/ModernAgentOutput/MessageInput.js +12 -4
  29. package/lib/esm/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
  30. package/lib/esm/features/agent/chat/ModernAgentOutput/MessageItem.js +60 -56
  31. package/lib/esm/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  32. package/lib/esm/features/agent/chat/index.js +1 -0
  33. package/lib/esm/features/agent/chat/index.js.map +1 -1
  34. package/lib/esm/features/agent/createChartTool.js +354 -0
  35. package/lib/esm/features/agent/createChartTool.js.map +1 -0
  36. package/lib/esm/features/agent/examples.js +295 -0
  37. package/lib/esm/features/agent/examples.js.map +1 -0
  38. package/lib/esm/features/agent/index.js +2 -0
  39. package/lib/esm/features/agent/index.js.map +1 -1
  40. package/lib/esm/features/agent/visualization.js +165 -0
  41. package/lib/esm/features/agent/visualization.js.map +1 -0
  42. package/lib/esm/features/facets/CollectionsFacetsNav.js +5 -1
  43. package/lib/esm/features/facets/CollectionsFacetsNav.js.map +1 -1
  44. package/lib/esm/features/index.js +1 -0
  45. package/lib/esm/features/index.js.map +1 -1
  46. package/lib/esm/features/layout/GenericPageNavHeader.js +14 -4
  47. package/lib/esm/features/layout/GenericPageNavHeader.js.map +1 -1
  48. package/lib/esm/features/magic-pdf/AnnotatedImageSlider.js +268 -0
  49. package/lib/esm/features/magic-pdf/AnnotatedImageSlider.js.map +1 -0
  50. package/lib/esm/features/magic-pdf/DownloadPopover.js +11 -11
  51. package/lib/esm/features/magic-pdf/DownloadPopover.js.map +1 -1
  52. package/lib/esm/features/magic-pdf/ExtractedContentView.js +77 -0
  53. package/lib/esm/features/magic-pdf/ExtractedContentView.js.map +1 -0
  54. package/lib/esm/features/magic-pdf/MagicPdfProvider.js +242 -0
  55. package/lib/esm/features/magic-pdf/MagicPdfProvider.js.map +1 -0
  56. package/lib/esm/features/magic-pdf/MagicPdfView.js +41 -47
  57. package/lib/esm/features/magic-pdf/MagicPdfView.js.map +1 -1
  58. package/lib/esm/features/pdf-viewer/PdfPageRenderer.js +261 -0
  59. package/lib/esm/features/pdf-viewer/PdfPageRenderer.js.map +1 -0
  60. package/lib/esm/features/pdf-viewer/PdfPageSlider.js +276 -0
  61. package/lib/esm/features/pdf-viewer/PdfPageSlider.js.map +1 -0
  62. package/lib/esm/features/pdf-viewer/SimplePdfViewer.js +71 -0
  63. package/lib/esm/features/pdf-viewer/SimplePdfViewer.js.map +1 -0
  64. package/lib/esm/features/pdf-viewer/index.js +4 -0
  65. package/lib/esm/features/pdf-viewer/index.js.map +1 -0
  66. package/lib/esm/features/store/collections/EditCollectionView.js +3 -5
  67. package/lib/esm/features/store/collections/EditCollectionView.js.map +1 -1
  68. package/lib/esm/features/store/collections/SharedPropsEditor.js +1 -2
  69. package/lib/esm/features/store/collections/SharedPropsEditor.js.map +1 -1
  70. package/lib/esm/features/store/objects/DocumentSearchResults.js +0 -7
  71. package/lib/esm/features/store/objects/DocumentSearchResults.js.map +1 -1
  72. package/lib/esm/features/store/objects/components/ContentOverview.js +273 -83
  73. package/lib/esm/features/store/objects/components/ContentOverview.js.map +1 -1
  74. package/lib/esm/features/store/objects/components/useContentPanelHooks.js +153 -0
  75. package/lib/esm/features/store/objects/components/useContentPanelHooks.js.map +1 -0
  76. package/lib/esm/features/store/objects/layout/DocumentTableColumn.js +3 -3
  77. package/lib/esm/features/store/objects/layout/DocumentTableColumn.js.map +1 -1
  78. package/lib/esm/features/store/objects/layout/renderers.js +13 -0
  79. package/lib/esm/features/store/objects/layout/renderers.js.map +1 -1
  80. package/lib/esm/features/utils/index.js +2 -0
  81. package/lib/esm/features/utils/index.js.map +1 -1
  82. package/lib/esm/features/utils/mimeType.js +8 -0
  83. package/lib/esm/features/utils/mimeType.js.map +1 -1
  84. package/lib/esm/features/utils/print.js +181 -0
  85. package/lib/esm/features/utils/print.js.map +1 -0
  86. package/lib/esm/features/utils/workflowStatus.js +43 -0
  87. package/lib/esm/features/utils/workflowStatus.js.map +1 -0
  88. package/lib/esm/router/HistoryNavigator.js +22 -2
  89. package/lib/esm/router/HistoryNavigator.js.map +1 -1
  90. package/lib/esm/shell/login/UserInfo.js +2 -1
  91. package/lib/esm/shell/login/UserInfo.js.map +1 -1
  92. package/lib/esm/shell/login/UserSessionMenu.js +7 -1
  93. package/lib/esm/shell/login/UserSessionMenu.js.map +1 -1
  94. package/lib/esm/widgets/form/Form.js +6 -2
  95. package/lib/esm/widgets/form/Form.js.map +1 -1
  96. package/lib/esm/widgets/markdown/MarkdownRenderer.js +226 -4
  97. package/lib/esm/widgets/markdown/MarkdownRenderer.js.map +1 -1
  98. package/lib/esm/widgets/schema-editor/ManagedSchema.js +0 -3
  99. package/lib/esm/widgets/schema-editor/ManagedSchema.js.map +1 -1
  100. package/lib/esm/widgets/schema-editor/json-schema4-utils.js +1 -1
  101. package/lib/esm/widgets/schema-editor/json-schema4-utils.js.map +1 -1
  102. package/lib/esm/widgets/xml-viewer/components/XMLViewer.js +18 -9
  103. package/lib/esm/widgets/xml-viewer/components/XMLViewer.js.map +1 -1
  104. package/lib/esm/widgets/xml-viewer/constants/index.js +10 -0
  105. package/lib/esm/widgets/xml-viewer/constants/index.js.map +1 -1
  106. package/lib/tsconfig.tsbuildinfo +1 -1
  107. package/lib/types/core/components/MessageBox.d.ts.map +1 -1
  108. package/lib/types/core/components/shadcn/dialog.d.ts +2 -1
  109. package/lib/types/core/components/shadcn/dialog.d.ts.map +1 -1
  110. package/lib/types/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  111. package/lib/types/core/components/shadcn/selectBox.d.ts +5 -1
  112. package/lib/types/core/components/shadcn/selectBox.d.ts.map +1 -1
  113. package/lib/types/core/components/shadcn/tabs.d.ts +3 -1
  114. package/lib/types/core/components/shadcn/tabs.d.ts.map +1 -1
  115. package/lib/types/env/index.d.ts +2 -0
  116. package/lib/types/env/index.d.ts.map +1 -1
  117. package/lib/types/features/agent/chat/AgentChart.d.ts +48 -0
  118. package/lib/types/features/agent/chat/AgentChart.d.ts.map +1 -0
  119. package/lib/types/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  120. package/lib/types/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -2
  121. package/lib/types/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  122. package/lib/types/features/agent/chat/ModernAgentOutput/Header.d.ts +2 -1
  123. package/lib/types/features/agent/chat/ModernAgentOutput/Header.d.ts.map +1 -1
  124. package/lib/types/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.d.ts +4 -2
  125. package/lib/types/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.d.ts.map +1 -1
  126. package/lib/types/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +2 -4
  127. package/lib/types/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
  128. package/lib/types/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  129. package/lib/types/features/agent/chat/index.d.ts +1 -0
  130. package/lib/types/features/agent/chat/index.d.ts.map +1 -1
  131. package/lib/types/features/agent/createChartTool.d.ts +178 -0
  132. package/lib/types/features/agent/createChartTool.d.ts.map +1 -0
  133. package/lib/types/features/agent/examples.d.ts +59 -0
  134. package/lib/types/features/agent/examples.d.ts.map +1 -0
  135. package/lib/types/features/agent/index.d.ts +2 -0
  136. package/lib/types/features/agent/index.d.ts.map +1 -1
  137. package/lib/types/features/agent/visualization.d.ts +95 -0
  138. package/lib/types/features/agent/visualization.d.ts.map +1 -0
  139. package/lib/types/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
  140. package/lib/types/features/index.d.ts +1 -0
  141. package/lib/types/features/index.d.ts.map +1 -1
  142. package/lib/types/features/layout/GenericPageNavHeader.d.ts.map +1 -1
  143. package/lib/types/features/magic-pdf/AnnotatedImageSlider.d.ts +13 -0
  144. package/lib/types/features/magic-pdf/AnnotatedImageSlider.d.ts.map +1 -0
  145. package/lib/types/features/magic-pdf/DownloadPopover.d.ts.map +1 -1
  146. package/lib/types/features/magic-pdf/ExtractedContentView.d.ts +8 -0
  147. package/lib/types/features/magic-pdf/ExtractedContentView.d.ts.map +1 -0
  148. package/lib/types/features/magic-pdf/MagicPdfProvider.d.ts +58 -0
  149. package/lib/types/features/magic-pdf/MagicPdfProvider.d.ts.map +1 -0
  150. package/lib/types/features/magic-pdf/MagicPdfView.d.ts +1 -1
  151. package/lib/types/features/magic-pdf/MagicPdfView.d.ts.map +1 -1
  152. package/lib/types/features/pdf-viewer/PdfPageRenderer.d.ts +83 -0
  153. package/lib/types/features/pdf-viewer/PdfPageRenderer.d.ts.map +1 -0
  154. package/lib/types/features/pdf-viewer/PdfPageSlider.d.ts +29 -0
  155. package/lib/types/features/pdf-viewer/PdfPageSlider.d.ts.map +1 -0
  156. package/lib/types/features/pdf-viewer/SimplePdfViewer.d.ts +19 -0
  157. package/lib/types/features/pdf-viewer/SimplePdfViewer.d.ts.map +1 -0
  158. package/lib/types/features/pdf-viewer/index.d.ts +4 -0
  159. package/lib/types/features/pdf-viewer/index.d.ts.map +1 -0
  160. package/lib/types/features/store/collections/EditCollectionView.d.ts.map +1 -1
  161. package/lib/types/features/store/collections/SharedPropsEditor.d.ts.map +1 -1
  162. package/lib/types/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
  163. package/lib/types/features/store/objects/components/ContentOverview.d.ts.map +1 -1
  164. package/lib/types/features/store/objects/components/useContentPanelHooks.d.ts +30 -0
  165. package/lib/types/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -0
  166. package/lib/types/features/store/objects/layout/renderers.d.ts.map +1 -1
  167. package/lib/types/features/utils/index.d.ts +2 -0
  168. package/lib/types/features/utils/index.d.ts.map +1 -1
  169. package/lib/types/features/utils/mimeType.d.ts +1 -0
  170. package/lib/types/features/utils/mimeType.d.ts.map +1 -1
  171. package/lib/types/features/utils/print.d.ts +10 -0
  172. package/lib/types/features/utils/print.d.ts.map +1 -0
  173. package/lib/types/features/utils/workflowStatus.d.ts +10 -0
  174. package/lib/types/features/utils/workflowStatus.d.ts.map +1 -0
  175. package/lib/types/router/HistoryNavigator.d.ts +3 -0
  176. package/lib/types/router/HistoryNavigator.d.ts.map +1 -1
  177. package/lib/types/shell/login/UserInfo.d.ts.map +1 -1
  178. package/lib/types/shell/login/UserSessionMenu.d.ts.map +1 -1
  179. package/lib/types/widgets/form/Form.d.ts.map +1 -1
  180. package/lib/types/widgets/markdown/MarkdownRenderer.d.ts +5 -1
  181. package/lib/types/widgets/markdown/MarkdownRenderer.d.ts.map +1 -1
  182. package/lib/types/widgets/schema-editor/ManagedSchema.d.ts.map +1 -1
  183. package/lib/types/widgets/xml-viewer/components/XMLViewer.d.ts.map +1 -1
  184. package/lib/types/widgets/xml-viewer/constants/index.d.ts +10 -0
  185. package/lib/types/widgets/xml-viewer/constants/index.d.ts.map +1 -1
  186. package/lib/vertesia-ui-core.js +1 -1
  187. package/lib/vertesia-ui-core.js.map +1 -1
  188. package/lib/vertesia-ui-env.js +1 -1
  189. package/lib/vertesia-ui-env.js.map +1 -1
  190. package/lib/vertesia-ui-features.js +1 -1
  191. package/lib/vertesia-ui-features.js.map +1 -1
  192. package/lib/vertesia-ui-layout.js +1 -1
  193. package/lib/vertesia-ui-layout.js.map +1 -1
  194. package/lib/vertesia-ui-router.js +1 -1
  195. package/lib/vertesia-ui-router.js.map +1 -1
  196. package/lib/vertesia-ui-session.js +1 -1
  197. package/lib/vertesia-ui-session.js.map +1 -1
  198. package/lib/vertesia-ui-shell.js +1 -1
  199. package/lib/vertesia-ui-shell.js.map +1 -1
  200. package/lib/vertesia-ui-widgets.js +1 -1
  201. package/lib/vertesia-ui-widgets.js.map +1 -1
  202. package/package.json +11 -8
  203. package/src/core/components/MenuList.tsx +3 -6
  204. package/src/core/components/MessageBox.tsx +7 -2
  205. package/src/core/components/SelectBox.tsx +1 -1
  206. package/src/core/components/shadcn/dialog.tsx +19 -1
  207. package/src/core/components/shadcn/filters/filter/SelectFilter.tsx +31 -31
  208. package/src/core/components/shadcn/filters/filterBar.tsx +1 -0
  209. package/src/core/components/shadcn/selectBox.tsx +32 -6
  210. package/src/core/components/shadcn/tabs.tsx +3 -2
  211. package/src/env/index.ts +5 -0
  212. package/src/features/agent/CHART_INSTRUCTIONS.md +228 -0
  213. package/src/features/agent/chat/AgentChart.tsx +601 -0
  214. package/src/features/agent/chat/ModernAgentConversation.tsx +123 -11
  215. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +8 -2
  216. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +12 -2
  217. package/src/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.tsx +6 -1
  218. package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +15 -10
  219. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +122 -87
  220. package/src/features/agent/chat/index.ts +1 -0
  221. package/src/features/agent/createChartTool.ts +364 -0
  222. package/src/features/agent/examples.ts +321 -0
  223. package/src/features/agent/index.ts +2 -0
  224. package/src/features/agent/visualization.ts +227 -0
  225. package/src/features/facets/CollectionsFacetsNav.tsx +5 -1
  226. package/src/features/index.ts +1 -0
  227. package/src/features/layout/GenericPageNavHeader.tsx +15 -4
  228. package/src/features/magic-pdf/AnnotatedImageSlider.tsx +482 -0
  229. package/src/features/magic-pdf/DownloadPopover.tsx +45 -40
  230. package/src/features/magic-pdf/ExtractedContentView.tsx +132 -0
  231. package/src/features/magic-pdf/MagicPdfProvider.tsx +297 -0
  232. package/src/features/magic-pdf/MagicPdfView.tsx +184 -91
  233. package/src/features/pdf-viewer/PdfPageRenderer.tsx +612 -0
  234. package/src/features/pdf-viewer/PdfPageSlider.tsx +473 -0
  235. package/src/features/pdf-viewer/SimplePdfViewer.tsx +142 -0
  236. package/src/features/pdf-viewer/index.ts +3 -0
  237. package/src/features/store/collections/EditCollectionView.tsx +3 -5
  238. package/src/features/store/collections/SharedPropsEditor.tsx +1 -2
  239. package/src/features/store/objects/DocumentSearchResults.tsx +0 -7
  240. package/src/features/store/objects/components/ContentOverview.tsx +677 -210
  241. package/src/features/store/objects/components/useContentPanelHooks.ts +169 -0
  242. package/src/features/store/objects/layout/DocumentTableColumn.tsx +3 -3
  243. package/src/features/store/objects/layout/knowledge.md +1 -0
  244. package/src/features/store/objects/layout/renderers.tsx +25 -0
  245. package/src/features/utils/index.ts +3 -1
  246. package/src/features/utils/mimeType.ts +10 -1
  247. package/src/features/utils/print.ts +189 -0
  248. package/src/features/utils/workflowStatus.ts +44 -0
  249. package/src/router/HistoryNavigator.ts +30 -2
  250. package/src/shell/login/UserInfo.tsx +2 -0
  251. package/src/shell/login/UserSessionMenu.tsx +12 -1
  252. package/src/widgets/form/Form.tsx +8 -3
  253. package/src/widgets/markdown/MarkdownRenderer.tsx +350 -6
  254. package/src/widgets/schema-editor/ManagedSchema.ts +0 -3
  255. package/src/widgets/schema-editor/json-schema4-utils.ts +1 -1
  256. package/src/widgets/xml-viewer/components/XMLViewer.tsx +22 -10
  257. package/src/widgets/xml-viewer/constants/index.ts +11 -0
  258. package/lib/esm/features/magic-pdf/PageSlider.js +0 -70
  259. package/lib/esm/features/magic-pdf/PageSlider.js.map +0 -1
  260. package/lib/esm/features/magic-pdf/PdfPageProvider.js +0 -188
  261. package/lib/esm/features/magic-pdf/PdfPageProvider.js.map +0 -1
  262. package/lib/esm/features/magic-pdf/TextPageView.js +0 -62
  263. package/lib/esm/features/magic-pdf/TextPageView.js.map +0 -1
  264. package/lib/esm/features/magic-pdf/useResizeOnDrag.js +0 -34
  265. package/lib/esm/features/magic-pdf/useResizeOnDrag.js.map +0 -1
  266. package/lib/types/features/magic-pdf/PageSlider.d.ts +0 -9
  267. package/lib/types/features/magic-pdf/PageSlider.d.ts.map +0 -1
  268. package/lib/types/features/magic-pdf/PdfPageProvider.d.ts +0 -39
  269. package/lib/types/features/magic-pdf/PdfPageProvider.d.ts.map +0 -1
  270. package/lib/types/features/magic-pdf/TextPageView.d.ts +0 -8
  271. package/lib/types/features/magic-pdf/TextPageView.d.ts.map +0 -1
  272. package/lib/types/features/magic-pdf/useResizeOnDrag.d.ts +0 -9
  273. package/lib/types/features/magic-pdf/useResizeOnDrag.d.ts.map +0 -1
  274. package/src/features/magic-pdf/PageSlider.tsx +0 -142
  275. package/src/features/magic-pdf/PdfPageProvider.tsx +0 -310
  276. package/src/features/magic-pdf/TextPageView.tsx +0 -91
  277. package/src/features/magic-pdf/useResizeOnDrag.ts +0 -42
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/ui",
3
- "version": "0.80.0-dev.20251121",
3
+ "version": "0.80.0",
4
4
  "description": "Vertesia UI components and and hooks",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -44,26 +44,29 @@
44
44
  "fast-xml-parser": "^5.2.3",
45
45
  "firebase": "^10.12.2",
46
46
  "framer-motion": "^12.23.12",
47
+ "html-to-image": "^1.11.13",
47
48
  "json-schema": "^0.4.0",
48
49
  "jwt-decode": "^4.0.0",
49
50
  "lodash-es": "^4.17.21",
50
51
  "lucide-react": "^0.511.0",
51
52
  "monaco-editor": "^0.52.2",
52
53
  "motion": "^12.12.1",
53
- "react": "^19.1.0",
54
+ "react": "^19.2.3",
54
55
  "react-calendar": "^6.0.0",
55
56
  "react-date-picker": "^11.0.0",
56
- "react-dom": "^19.1.0",
57
+ "react-dom": "^19.2.3",
57
58
  "react-error-boundary": "^6.0.0",
58
59
  "react-markdown": "^10.1.0",
60
+ "react-pdf": "^10.2.0",
59
61
  "react-resizable-panels": "^3.0.6",
62
+ "recharts": "^3.5.0",
60
63
  "remark-gfm": "^4.0.1",
61
64
  "tailwind-merge": "^3.3.0",
62
65
  "ts-md5": "^1.3.1",
63
66
  "unist-util-visit": "^5.0.0",
64
- "@vertesia/client": "0.80.0-dev.20251121",
65
- "@vertesia/json": "0.80.0-dev.20251121",
66
- "@vertesia/common": "0.80.0-dev.20251121"
67
+ "@vertesia/client": "0.80.0",
68
+ "@vertesia/json": "0.80.0",
69
+ "@vertesia/common": "0.80.0"
67
70
  },
68
71
  "devDependencies": {
69
72
  "@eslint/js": "^9.27.0",
@@ -73,8 +76,8 @@
73
76
  "@types/json-schema": "^7.0.15",
74
77
  "@types/lodash-es": "^4.17.12",
75
78
  "@types/node": "^22.15.21",
76
- "@types/react": "^19.1.0",
77
- "@types/react-dom": "^19.1.1",
79
+ "@types/react": "^19.2.3",
80
+ "@types/react-dom": "^19.2.3",
78
81
  "eslint": "^9.27.0",
79
82
  "eslint-import-resolver-typescript": "^4.4.4",
80
83
  "eslint-plugin-import": "^2.32.0",
@@ -7,7 +7,7 @@ interface MenuListProps {
7
7
  }
8
8
  export function MenuList({ className, children }: MenuListProps) {
9
9
  return (
10
- <ul className={`${className} space-y-1 flex flex-col items-start dark:px-2`}>
10
+ <ul className={`${className} space-y-1 flex flex-col items-start`}>
11
11
  {children}
12
12
  </ul>
13
13
  )
@@ -21,11 +21,8 @@ interface MenuListItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
21
21
  const MenuListItem = forwardRef<HTMLAnchorElement, MenuListItemProps>(function _MenuListItem(props, ref) {
22
22
  const { current, children, className, href = '#', onClick, ...others } = props;
23
23
  return (
24
- <li className={clsx(className, current ?
25
- 'bg-gray-50 text-indigo-600'
26
- :
27
- 'text-gray-700 dark:dark:text-slate-300 hover:text-indigo-600 hover:bg-gray-50 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:border dark:border-transparent dark:hover:border-slate-50',
28
- 'w-full rounded-md p-2 pl-3 text-sm leading-6 font-semibold')}>
24
+ <li className={clsx(className, current ? 'bg-muted' : '',
25
+ 'w-full p-2 pl-3 text-sm leading-6 font-semibold hover:bg-muted')}>
29
26
  <a ref={ref} href={href} onClick={(e) => {
30
27
  if (onClick) {
31
28
  e.preventDefault();
@@ -74,7 +74,13 @@ export function MessageBox({ icon, status, title, children, className }: Message
74
74
  }
75
75
 
76
76
  export function ErrorBox({ title, className, children }: { title: string, className?:string, children: React.ReactNode }) {
77
- return <MessageBox status="error" title={title} className={className}><pre>{children}</pre></MessageBox>
77
+ return (
78
+ <MessageBox status="error" title={title} className={className}>
79
+ <pre className="whitespace-pre-wrap break-words">
80
+ {children}
81
+ </pre>
82
+ </MessageBox>
83
+ );
78
84
  }
79
85
 
80
86
  export function InfoBox({ title, className, children }: { title: string, className?:string, children: React.ReactNode }) {
@@ -93,4 +99,3 @@ export function DefaultBox({ title, className, children }: { title: string, clas
93
99
  return <MessageBox status="default" title={title} className={className}>{children}</MessageBox>
94
100
  }
95
101
 
96
-
@@ -185,4 +185,4 @@ export function ListOption<T>({ option, optionLabel, onClick, className }: { opt
185
185
  </>
186
186
  )}
187
187
  </ListboxOption>)
188
- }
188
+ }
@@ -16,6 +16,7 @@ interface ModalProps {
16
16
  className?: string;
17
17
  allowOverflow?: boolean;
18
18
  disableCloseOnClickOutside?: boolean;
19
+ size?: "sm" | "md" | "lg" | "xl";
19
20
  }
20
21
  const ModalContext = createContext<boolean>(false)
21
22
  export function useIsInModal() {
@@ -34,12 +35,27 @@ export function VModal({
34
35
  noCloseButton = false,
35
36
  allowOverflow = false,
36
37
  disableCloseOnClickOutside = false,
38
+ size = "md",
37
39
  }: ModalProps) {
38
40
  const handleOpenChange = (open: boolean) => {
39
41
  if (!open) {
40
42
  onClose();
41
43
  }
42
44
  };
45
+ function getSizeClasses() {
46
+ switch (size) {
47
+ case "sm":
48
+ return "max-w-[20vw]";
49
+ case "md":
50
+ return "max-w-[40vw]";
51
+ case "lg":
52
+ return "max-w-[60vw]";
53
+ case "xl":
54
+ return "max-w-[80vw]";
55
+ default:
56
+ return "max-w-[40vw]";
57
+ }
58
+ }
43
59
 
44
60
  return (
45
61
  <Dialog
@@ -49,6 +65,7 @@ export function VModal({
49
65
  handleOpenChange(open);
50
66
  }
51
67
  }}
68
+
52
69
  >
53
70
  {allowOverflow && <DialogOverlay className="z-50 fixed inset-0 bg-black/80" />}
54
71
  <VisuallyHidden>
@@ -57,7 +74,8 @@ export function VModal({
57
74
  <DialogContent
58
75
  className={cn(
59
76
  "min-h-20 p-4",
60
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] border bg-background shadow-lg duration-200 sm:rounded-lg",
77
+ "fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] border bg-background shadow-lg duration-200 sm:rounded-lg",
78
+ getSizeClasses(),
61
79
  className
62
80
  )}
63
81
  >
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from "react";
2
- import { CommandItem, CommandEmpty } from "../../command";
2
+ import { CommandItem } from "../../command";
3
3
  import { Button } from "../../button";
4
4
  import { Filter, FilterGroup, FilterGroupOption, FilterOption } from "../types";
5
5
  import { DynamicLabel } from "../DynamicLabel";
@@ -52,12 +52,8 @@ export default function SelectFilter({
52
52
  }
53
53
 
54
54
  const options = getFilteredOptions(selectedView);
55
- const selectedGroup = filterGroups.find(g => g.name === selectedView);
56
-
57
- if (options.length === 0) {
58
- return <CommandEmpty>No matching options</CommandEmpty>;
59
- }
60
55
 
56
+ const selectedGroup = filterGroups.find(g => g.name === selectedView);
61
57
  const groupTitle = selectedGroup?.placeholder || selectedGroup?.name;
62
58
 
63
59
  const handleApply = () => {
@@ -117,35 +113,39 @@ export default function SelectFilter({
117
113
  };
118
114
 
119
115
  return (
120
- <>
121
- <div className="flex items-center p-1.5 text-xs text-muted">
116
+ <div className="flex flex-col h-full">
117
+ <div className="flex items-center p-1.5 text-xs text-muted shrink-0">
122
118
  <span>{groupTitle}</span>
123
119
  </div>
124
- <div className="max-h-50 overflow-y-auto">
125
- {options.map((option: FilterGroupOption) => {
126
- const isSelected = selectedOptions.some(opt => opt.value === option.value);
120
+ <div className="flex-1 overflow-hidden min-h-0">
121
+ <div className="max-h-[200px] overflow-y-auto">
122
+ {options.length > 0 && (
123
+ options.map((option: FilterGroupOption) => {
124
+ const isSelected = selectedOptions.some(opt => opt.value === option.value);
127
125
 
128
- return (
129
- <CommandItem
130
- key={option.value || `option-${Math.random()}`}
131
- className={`group flex gap-2 items-center w-full hover:bg-muted ${selectedGroup?.multiple && isSelected ? 'bg-muted' : ''
132
- }`}
133
- onSelect={() => handleOptionToggle(option)}
134
- >
135
- <DynamicLabel
136
- value={option.value || ''}
137
- labelRenderer={option.labelRenderer || selectedGroup?.labelRenderer}
138
- fallbackLabel={option.label}
139
- />
140
- {selectedGroup?.multiple && isSelected && (
141
- <span className="ml-auto text-xs text-success">✓</span>
142
- )}
143
- </CommandItem>
144
- );
145
- })}
126
+ return (
127
+ <CommandItem
128
+ key={option.value || `option-${Math.random()}`}
129
+ className={`group flex gap-2 items-center w-full hover:bg-muted ${selectedGroup?.multiple && isSelected ? 'bg-muted' : ''
130
+ }`}
131
+ onSelect={() => handleOptionToggle(option)}
132
+ >
133
+ <DynamicLabel
134
+ value={option.value || ''}
135
+ labelRenderer={option.labelRenderer || selectedGroup?.labelRenderer}
136
+ fallbackLabel={option.label}
137
+ />
138
+ {selectedGroup?.multiple && isSelected && (
139
+ <span className="ml-auto text-xs text-success">✓</span>
140
+ )}
141
+ </CommandItem>
142
+ );
143
+ })
144
+ )}
145
+ </div>
146
146
  </div>
147
147
  {selectedGroup?.multiple && (
148
- <div className="p-2 border-t">
148
+ <div className="p-2 border-t shrink-0">
149
149
  <div className="flex gap-2 justify-end">
150
150
  <Button variant="ghost" size="sm" onClick={handleClose}>
151
151
  Cancel
@@ -156,6 +156,6 @@ export default function SelectFilter({
156
156
  </div>
157
157
  </div>
158
158
  )}
159
- </>
159
+ </div>
160
160
  );
161
161
  }
@@ -330,6 +330,7 @@ const FilterBtn = ({ className }: { className?: string }) => {
330
330
  )
331
331
  }
332
332
  <CommandList>
333
+ <CommandEmpty>No matching filters</CommandEmpty>
333
334
  <CommandGroup>
334
335
  {!selectedView ? getAvailableFilterGroups() : renderFilterOptions()}
335
336
  </CommandGroup>
@@ -1,12 +1,13 @@
1
1
  import clsx from 'clsx';
2
2
  import { isEqual } from 'lodash-es';
3
- import { Check, ChevronsUpDown, SearchIcon, SquarePlus, X } from 'lucide-react';
4
- import { useState, useEffect, useRef } from 'react';
3
+ import { AlertTriangle, Check, ChevronsUpDown, SearchIcon, SquarePlus, X } from 'lucide-react';
4
+ import { useState, useEffect, useRef, useMemo } from 'react';
5
5
 
6
6
  import { Popover, PopoverContent, PopoverTrigger, PopoverClose } from './popover';
7
7
  import { Command, CommandEmpty, CommandGroup, CommandItem, CommandList } from './command';
8
8
  import { Input } from './input';
9
9
  import { Button } from '@vertesia/ui/core';
10
+ import { VTooltip } from './tooltip';
10
11
 
11
12
  export interface VSelectBoxBaseProps<T> {
12
13
  options: T[] | undefined;
@@ -25,6 +26,10 @@ export interface VSelectBoxBaseProps<T> {
25
26
  isClearable?: boolean;
26
27
  border?: boolean;
27
28
  inline?: boolean;
29
+ /** Show warning when value is not in options list (default: true) */
30
+ warnOnMissingValue?: boolean;
31
+ /** Custom warning message when value is not in options */
32
+ missingValueWarning?: string;
28
33
  }
29
34
 
30
35
  interface VSelectBoxSingleProps<T> extends VSelectBoxBaseProps<T> {
@@ -41,12 +46,27 @@ interface VSelectBoxMultipleProps<T> extends VSelectBoxBaseProps<T> {
41
46
 
42
47
  type VSelectBoxProps<T> = VSelectBoxSingleProps<T> | VSelectBoxMultipleProps<T>;
43
48
 
44
- export function VSelectBox<T = any>({ options, optionLabel, value, onChange, addNew, addNewLabel, disabled, filterBy, label, placeholder, className, popupClass, isClearable, border = true, multiple = false, by, inline = false }: Readonly<VSelectBoxProps<T>>) {
49
+ export function VSelectBox<T = any>({ options, optionLabel, value, onChange, addNew, addNewLabel, disabled, filterBy, label, placeholder, className, popupClass, isClearable, border = true, multiple = false, by, inline = false, warnOnMissingValue = true, missingValueWarning = "Value not in options list, may not be valid" }: Readonly<VSelectBoxProps<T>>) {
45
50
  const triggerRef = useRef<HTMLDivElement>(null);
46
51
  const [open, setOpen] = useState(false);
47
52
  const [width, setWidth] = useState<number>(0);
48
53
  const [filterValue, setFilterValue] = useState('');
49
54
 
55
+ // Check if value is in options list (for single select only)
56
+ const isMissingValue = useMemo(() => {
57
+ if (!warnOnMissingValue || multiple || value == null || !options) return false;
58
+ // Use the isOptionsEqual helper which respects the 'by' comparator
59
+ return !options.some(opt => {
60
+ if (typeof by === 'string') {
61
+ return (opt as any)[by] === (value as any)[by];
62
+ } else if (typeof by === 'function') {
63
+ return by(opt, value as T);
64
+ } else {
65
+ return isEqual(opt, value);
66
+ }
67
+ });
68
+ }, [warnOnMissingValue, multiple, value, options, by]);
69
+
50
70
  useEffect(() => {
51
71
  const element = triggerRef.current;
52
72
  if (!element) {
@@ -185,7 +205,7 @@ export function VSelectBox<T = any>({ options, optionLabel, value, onChange, add
185
205
  <Command className="overflow-hidden">
186
206
  <CommandList className={inline ? "max-h-full overflow-y-auto" : "max-h-[200px] overflow-y-auto"}>
187
207
  <CommandEmpty>No result found.</CommandEmpty>
188
- <CommandGroup className="overflow-visible">
208
+ <CommandGroup>
189
209
  {filteredOptions?.map((opt, index) => {
190
210
  const isSelected = multiple
191
211
  ? isOptionSelected(opt, Array.isArray(value) ? value : [])
@@ -255,7 +275,7 @@ export function VSelectBox<T = any>({ options, optionLabel, value, onChange, add
255
275
  onClick={handleTriggerClick}
256
276
  className={clsx(
257
277
  className,
258
- border && 'border border-border',
278
+ border && (isMissingValue ? 'border border-destructive' : 'border border-border'),
259
279
  'flex flex-row gap-2 items-center justify-between p-2 rounded-md group relative [&:hover_.clear-button]:opacity-100',
260
280
  !disabled ? "cursor-pointer hover:bg-muted" : "cursor-not-allowed text-muted",
261
281
  )}
@@ -268,13 +288,19 @@ export function VSelectBox<T = any>({ options, optionLabel, value, onChange, add
268
288
  )}
269
289
  >
270
290
  {label && <div className='w-full text-left text-xs font-semibold'>{label}</div>}
271
- <div className={clsx('w-full text-left', !disabled && '')}>
291
+ <div className={clsx('w-full text-left min-h-6', !disabled && '', isMissingValue && 'text-destructive')}>
292
+ {isMissingValue && (
293
+ <VTooltip description={missingValueWarning} placement="top" asChild>
294
+ <AlertTriangle className="inline-block size-4 mr-1 -mt-0.5 cursor-help" />
295
+ </VTooltip>
296
+ )}
272
297
  {multiple ? renderMultipleValue() : renderSingleValue()}
273
298
  </div>
274
299
  </div>
275
300
  <div className="flex items-center gap-1 group">
276
301
  {isClearable && value && (Array.isArray(value) ? value.length > 0 : true) && (
277
302
  <Button variant={"link"} size={"icon"}
303
+ disabled={disabled}
278
304
  alt="Clear selection"
279
305
  onClick={(e) => {
280
306
  e.stopPropagation();
@@ -194,7 +194,7 @@ const VTabsBar = ({ className }: { className?: string }) => {
194
194
  );
195
195
  };
196
196
 
197
- const VTabsPanel = () => {
197
+ const VTabsPanel = ({ className }: { className?: string }) => {
198
198
  const { tabs } = React.useContext(TabsContext);
199
199
 
200
200
  if (!tabs) return null;
@@ -202,7 +202,7 @@ const VTabsPanel = () => {
202
202
  return (
203
203
  <>
204
204
  {tabs.map((tab) => (
205
- <TabsContent key={tab.name} value={tab.name}>
205
+ <TabsContent key={tab.name} value={tab.name} className={className}>
206
206
  {tab.content}
207
207
  </TabsContent>
208
208
  ))}
@@ -285,6 +285,7 @@ const TabsContent = React.forwardRef<
285
285
  ref={ref}
286
286
  className={cn(
287
287
  "focus-visible:outline-none",
288
+ "flex-1 overflow-y-auto min-h-0 pt-2",
288
289
  className
289
290
  )}
290
291
  {...props}
package/src/env/index.ts CHANGED
@@ -6,6 +6,7 @@ import { AuthTokenPayload } from "@vertesia/common";
6
6
  export interface EnvProps {
7
7
  name: string; // the app name
8
8
  version: string,
9
+ sdkVersion?: string, // the @vertesia/ui package version
9
10
  isLocalDev: boolean,
10
11
  isDocker: boolean,
11
12
  type: "production" | "staging" | "preview" | "development" | string,
@@ -53,6 +54,10 @@ export class VertesiaEnvironment implements Readonly<EnvProps> {
53
54
  return this.prop("version");
54
55
  }
55
56
 
57
+ get sdkVersion() {
58
+ return this._props?.sdkVersion;
59
+ }
60
+
56
61
  get name() {
57
62
  return this.prop("name");
58
63
  }
@@ -0,0 +1,228 @@
1
+ # Chart Creation Instructions for AI Agents
2
+
3
+ This document provides instructions for AI agents on how to create interactive charts that will be rendered in the UI.
4
+
5
+ ## Overview
6
+
7
+ You can create charts by including a special markdown code block with the language `chart`. The UI will automatically detect and render these as interactive Recharts visualizations.
8
+
9
+ ## Basic Format
10
+
11
+ ```markdown
12
+ \`\`\`chart
13
+ {
14
+ "version": "1.0",
15
+ "library": "recharts",
16
+ "chart": "bar" | "line" | "composed",
17
+ "title": "Chart Title",
18
+ "description": "Optional description",
19
+ "data": [ /* array of data objects */ ],
20
+ "xKey": "fieldName",
21
+ "series": [ /* array of series configurations */ ],
22
+ "yAxis": { /* optional axis labels */ },
23
+ "options": { /* optional chart options */ }
24
+ }
25
+ \`\`\`
26
+ ```
27
+
28
+ ## Chart Types
29
+
30
+ ### 1. Bar Chart
31
+ Used for comparing values across categories.
32
+
33
+ ```chart
34
+ {
35
+ "version": "1.0",
36
+ "chart": "bar",
37
+ "title": "Revenue by Quarter",
38
+ "data": [
39
+ { "quarter": "Q1", "revenue": 100000 },
40
+ { "quarter": "Q2", "revenue": 150000 },
41
+ { "quarter": "Q3", "revenue": 120000 }
42
+ ],
43
+ "xKey": "quarter",
44
+ "series": [
45
+ { "key": "revenue", "label": "Revenue", "type": "bar" }
46
+ ]
47
+ }
48
+ ```
49
+
50
+ ### 2. Line Chart
51
+ Used for showing trends over time.
52
+
53
+ ```chart
54
+ {
55
+ "version": "1.0",
56
+ "chart": "line",
57
+ "title": "Performance Over Time",
58
+ "data": [
59
+ { "period": "2024-Q1", "tvpi": 1.2, "dpi": 0.3 },
60
+ { "period": "2024-Q2", "tvpi": 1.5, "dpi": 0.5 },
61
+ { "period": "2024-Q3", "tvpi": 1.8, "dpi": 0.7 }
62
+ ],
63
+ "xKey": "period",
64
+ "series": [
65
+ { "key": "tvpi", "label": "TVPI", "color": "#4f46e5", "dot": false },
66
+ { "key": "dpi", "label": "DPI", "color": "#16a34a", "dot": false }
67
+ ],
68
+ "yAxis": {
69
+ "left": { "label": "Multiple (x)" }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ### 3. Composed Chart
75
+ Used for mixing bar and line charts (e.g., bars for cashflows, line for net).
76
+
77
+ ```chart
78
+ {
79
+ "version": "1.0",
80
+ "chart": "composed",
81
+ "title": "Cashflow Timeline",
82
+ "data": [
83
+ { "period": "2024-Q1", "calls": 1000000, "distributions": 500000, "netCashflow": -500000 },
84
+ { "period": "2024-Q2", "calls": 800000, "distributions": 1200000, "netCashflow": 400000 }
85
+ ],
86
+ "xKey": "period",
87
+ "series": [
88
+ { "key": "calls", "label": "Calls", "type": "bar", "color": "#ef4444" },
89
+ { "key": "distributions", "label": "Distributions", "type": "bar", "color": "#22c55e" },
90
+ { "key": "netCashflow", "label": "Net CF", "type": "line", "color": "#0ea5e9", "yAxisId": "right", "dot": false }
91
+ ],
92
+ "yAxis": {
93
+ "left": { "label": "Amount" },
94
+ "right": { "label": "Net" }
95
+ },
96
+ "options": {
97
+ "referenceZero": true
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Series Configuration
103
+
104
+ Each series can have:
105
+
106
+ - `key`: (required) The field name in the data
107
+ - `label`: Display name for the legend
108
+ - `type`: "bar" or "line" (for composed charts)
109
+ - `color`: Hex color code (e.g., "#4f46e5")
110
+ - `yAxisId`: "left" (default) or "right" for dual Y-axis
111
+ - `stackId`: Group bars for stacking
112
+ - `dot`: true/false - show dots on line charts
113
+
114
+ ## Options
115
+
116
+ - `stacked`: true/false - Stack bars in bar charts
117
+ - `referenceZero`: true/false - Show a reference line at y=0
118
+ - `collapsible`: true/false - Allow users to collapse the chart (default: true)
119
+ - `collapseInitially`: true/false - Start in collapsed state
120
+
121
+ ## Number Formatting
122
+
123
+ Numbers are automatically formatted:
124
+ - 1,000 → 1K
125
+ - 1,000,000 → 1M
126
+ - 1,000,000,000 → 1B
127
+
128
+ ## Pre-built Chart Builders (TypeScript)
129
+
130
+ If you're writing TypeScript code, you can use these helper functions:
131
+
132
+ ```typescript
133
+ import {
134
+ buildCashflowComposedChart,
135
+ buildPerformanceLineChart,
136
+ buildScenarioComparisonBarChart,
137
+ toChartMarkdown
138
+ } from '@vertesia/ui/features';
139
+
140
+ // Example: Create a cashflow chart
141
+ const chart = buildCashflowComposedChart({
142
+ title: 'Fund Cashflow',
143
+ rows: [
144
+ { period: '2024-Q1', calls: 1000000, distributions: 500000 },
145
+ { period: '2024-Q2', calls: 800000, distributions: 1200000 }
146
+ ]
147
+ });
148
+
149
+ // Convert to markdown
150
+ const markdown = toChartMarkdown(chart);
151
+ ```
152
+
153
+ ### Available Builders
154
+
155
+ 1. `buildCashflowComposedChart` - Calls/Distributions + Net CF
156
+ 2. `buildPerformanceLineChart` - TVPI/DPI/RVPI J-curve
157
+ 3. `buildScenarioComparisonBarChart` - Side-by-side comparison
158
+ 4. `buildScenarioDeltaPercentChart` - % change visualization
159
+ 5. `buildTvpiOverlayChart` - TVPI comparison between scenarios
160
+ 6. `buildNavLineChart` - NAV over time
161
+
162
+ ## Best Practices
163
+
164
+ 1. **Use appropriate chart types**:
165
+ - Bar charts for comparisons
166
+ - Line charts for trends
167
+ - Composed charts for mixed metrics
168
+
169
+ 2. **Keep data concise**:
170
+ - Limit to 20-30 data points for readability
171
+ - Use aggregation for large datasets
172
+
173
+ 3. **Color consistency**:
174
+ - Use the default color palette for consistency
175
+ - Red (#ef4444) for negative/costs
176
+ - Green (#22c55e) for positive/gains
177
+ - Blue (#0ea5e9) for neutral metrics
178
+
179
+ 4. **Add context**:
180
+ - Always include a descriptive title
181
+ - Add a description when the chart needs explanation
182
+ - Use axis labels for clarity
183
+
184
+ 5. **Dual Y-axis**:
185
+ - Use when comparing metrics with different scales
186
+ - Example: Amount (left) vs Count (right)
187
+
188
+ ## Example: Complete Workflow
189
+
190
+ When a user asks "Show me the fund performance":
191
+
192
+ 1. Fetch or calculate the data
193
+ 2. Format it as a chart spec
194
+ 3. Include it in your response:
195
+
196
+ ```markdown
197
+ Here's the fund performance over time:
198
+
199
+ \`\`\`chart
200
+ {
201
+ "version": "1.0",
202
+ "chart": "line",
203
+ "title": "Fund Performance",
204
+ "data": [
205
+ { "period": "2024-Q1", "tvpi": 1.2 },
206
+ { "period": "2024-Q2", "tvpi": 1.5 }
207
+ ],
208
+ "xKey": "period",
209
+ "series": [
210
+ { "key": "tvpi", "label": "TVPI", "color": "#4f46e5" }
211
+ ]
212
+ }
213
+ \`\`\`
214
+
215
+ The fund shows strong performance with TVPI increasing from 1.2x to 1.5x.
216
+ ```
217
+
218
+ ## Common Pitfalls
219
+
220
+ ❌ **Don't** include line breaks in the JSON
221
+ ❌ **Don't** use single quotes in JSON (use double quotes)
222
+ ❌ **Don't** forget to specify the chart type
223
+ ❌ **Don't** mix up xKey with data field names
224
+
225
+ ✅ **Do** validate your JSON before including it
226
+ ✅ **Do** include descriptive labels
227
+ ✅ **Do** test with sample data first
228
+ ✅ **Do** provide context around the chart