xertica-ui 2.1.2 → 2.1.3

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 (402) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -1
  3. package/assets/xertica-logo.svg +37 -37
  4. package/assets/xertica-x-logo.svg +20 -20
  5. package/bin/cli.ts +1 -1
  6. package/bin/generate-tokens.ts +13 -7
  7. package/components/assistant/index.ts +6 -6
  8. package/components/assistant/xertica-assistant/index.ts +2 -0
  9. package/components/assistant/xertica-assistant/use-assistant.ts +564 -0
  10. package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
  11. package/components/assistant/xertica-assistant/xertica-assistant.tsx +183 -474
  12. package/components/blocks/card-patterns/ActivityCard.tsx +72 -72
  13. package/components/blocks/card-patterns/FeatureCard.tsx +100 -100
  14. package/components/blocks/card-patterns/NotificationCard.tsx +127 -127
  15. package/components/blocks/card-patterns/ProfileCard.tsx +84 -84
  16. package/components/blocks/card-patterns/ProjectCard.tsx +89 -89
  17. package/components/blocks/card-patterns/QuickActionCard.tsx +62 -62
  18. package/components/blocks/card-patterns/card-patterns.stories.tsx +445 -445
  19. package/components/blocks/card-patterns/index.ts +17 -17
  20. package/components/blocks/index.ts +1 -1
  21. package/components/brand/index.ts +6 -6
  22. package/components/brand/xertica-provider/XerticaProvider.tsx +48 -48
  23. package/components/brand/xertica-provider/xertica-provider.mdx +61 -61
  24. package/components/brand/xertica-provider/xertica-provider.test.tsx +52 -52
  25. package/components/examples/MapExamples.tsx +282 -282
  26. package/components/examples/SimpleFilterableMap.tsx +191 -191
  27. package/components/examples/index.ts +51 -51
  28. package/components/figma/ImageWithFallback.tsx +27 -27
  29. package/components/hooks/index.ts +7 -7
  30. package/components/index.ts +5 -5
  31. package/components/layout/header/header.test.tsx +8 -8
  32. package/components/layout/header/header.tsx +4 -4
  33. package/components/layout/index.ts +2 -2
  34. package/components/layout/sidebar/index.ts +2 -0
  35. package/components/layout/sidebar/sidebar.mdx +1 -1
  36. package/components/layout/sidebar/sidebar.stories.tsx +160 -8
  37. package/components/layout/sidebar/sidebar.test.tsx +11 -11
  38. package/components/layout/sidebar/sidebar.tsx +610 -471
  39. package/components/layout/sidebar/use-sidebar.ts +113 -0
  40. package/components/media/FloatingMediaWrapper.tsx +11 -11
  41. package/components/media/audio-player/AudioPlayer.tsx +22 -22
  42. package/components/media/index.ts +3 -3
  43. package/components/pages/home-content/HomeContent.tsx +7 -7
  44. package/components/pages/home-content/home-content.mdx +62 -62
  45. package/components/pages/home-page/HomePage.stories.tsx +39 -39
  46. package/components/pages/home-page/HomePage.tsx +1 -1
  47. package/components/pages/home-page/home-page.mdx +53 -53
  48. package/components/pages/index.ts +8 -8
  49. package/components/pages/template-content/TemplateContent.tsx +5 -5
  50. package/components/pages/template-content/template-content.mdx +61 -61
  51. package/components/pages/template-page/TemplatePage.stories.tsx +39 -39
  52. package/components/pages/template-page/TemplatePage.tsx +5 -5
  53. package/components/pages/template-page/template-page.mdx +53 -53
  54. package/components/public-api-smoke.test.tsx +52 -52
  55. package/components/shared/assistant-utils.ts +43 -43
  56. package/components/shared/layout-constants.ts +1 -1
  57. package/components/ui/accordion/accordion.mdx +8 -8
  58. package/components/ui/accordion/accordion.stories.tsx +53 -53
  59. package/components/ui/alert/alert.mdx +8 -8
  60. package/components/ui/alert-dialog/alert-dialog.mdx +8 -8
  61. package/components/ui/aspect-ratio/aspect-ratio.mdx +8 -8
  62. package/components/ui/assistant-chart/assistant-chart.mdx +8 -8
  63. package/components/ui/avatar/avatar.mdx +8 -8
  64. package/components/ui/badge/badge.mdx +8 -8
  65. package/components/ui/badge/badge.tsx +9 -9
  66. package/components/ui/breadcrumb/breadcrumb.mdx +8 -8
  67. package/components/ui/button/button.mdx +8 -8
  68. package/components/ui/calendar/calendar.mdx +8 -8
  69. package/components/ui/calendar/calendar.tsx +258 -258
  70. package/components/ui/card/card.mdx +8 -8
  71. package/components/ui/card/card.stories.tsx +245 -245
  72. package/components/ui/carousel/carousel.mdx +8 -8
  73. package/components/ui/chart/chart.mdx +8 -8
  74. package/components/ui/chart/chart.stories.tsx +1303 -344
  75. package/components/ui/chart/chart.test.tsx +154 -154
  76. package/components/ui/chart/chart.tsx +2367 -1494
  77. package/components/ui/checkbox/checkbox.mdx +8 -8
  78. package/components/ui/checkbox/checkbox.stories.tsx +20 -20
  79. package/components/ui/collapsible/collapsible.mdx +8 -8
  80. package/components/ui/command/command.mdx +8 -8
  81. package/components/ui/context-menu/context-menu.mdx +8 -8
  82. package/components/ui/dialog/dialog.mdx +8 -8
  83. package/components/ui/drawer/drawer.mdx +8 -8
  84. package/components/ui/dropdown-menu/dropdown-menu.mdx +8 -8
  85. package/components/ui/empty/empty.mdx +8 -8
  86. package/components/ui/file-upload/file-upload.mdx +8 -8
  87. package/components/ui/file-upload/file-upload.stories.tsx +100 -0
  88. package/components/ui/file-upload/file-upload.tsx +14 -74
  89. package/components/ui/file-upload/index.ts +1 -0
  90. package/components/ui/file-upload/use-file-upload.ts +181 -0
  91. package/components/ui/google-maps-loader/google-maps-loader.tsx +2 -2
  92. package/components/ui/hover-card/hover-card.mdx +8 -8
  93. package/components/ui/input/input.mdx +8 -8
  94. package/components/ui/input-otp/input-otp.mdx +8 -8
  95. package/components/ui/input-otp/input-otp.stories.tsx +6 -6
  96. package/components/ui/label/label.mdx +8 -8
  97. package/components/ui/map/map.mdx +8 -8
  98. package/components/ui/map/map.stories.tsx +51 -51
  99. package/components/ui/map/map.tsx +2 -2
  100. package/components/ui/menubar/menubar.mdx +8 -8
  101. package/components/ui/navigation-menu/navigation-menu.mdx +8 -8
  102. package/components/ui/notification-badge/notification-badge.mdx +8 -8
  103. package/components/ui/pagination/index.ts +2 -0
  104. package/components/ui/pagination/pagination.mdx +8 -8
  105. package/components/ui/pagination/pagination.stories.tsx +94 -0
  106. package/components/ui/pagination/use-pagination.ts +194 -0
  107. package/components/ui/popover/popover.mdx +8 -8
  108. package/components/ui/progress/progress.mdx +8 -8
  109. package/components/ui/progress/progress.tsx +68 -68
  110. package/components/ui/radio-group/radio-group.mdx +8 -8
  111. package/components/ui/rating/rating.mdx +8 -8
  112. package/components/ui/resizable/resizable.mdx +8 -8
  113. package/components/ui/rich-text-editor/index.ts +2 -0
  114. package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
  115. package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
  116. package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
  117. package/components/ui/route-map/route-map.mdx +8 -8
  118. package/components/ui/route-map/route-map.stories.tsx +54 -54
  119. package/components/ui/route-map/route-map.tsx +2 -2
  120. package/components/ui/scroll-area/scroll-area.mdx +8 -8
  121. package/components/ui/search/search.mdx +8 -8
  122. package/components/ui/select/select.mdx +8 -8
  123. package/components/ui/select/select.stories.tsx +9 -9
  124. package/components/ui/separator/separator.mdx +8 -8
  125. package/components/ui/sheet/sheet.mdx +8 -8
  126. package/components/ui/sheet/sheet.stories.tsx +95 -95
  127. package/components/ui/simple-map/simple-map.mdx +8 -8
  128. package/components/ui/simple-map/simple-map.stories.tsx +48 -48
  129. package/components/ui/skeleton/skeleton.mdx +8 -8
  130. package/components/ui/slider/slider.mdx +8 -8
  131. package/components/ui/sonner/sonner.mdx +8 -8
  132. package/components/ui/stats-card/stats-card.mdx +8 -8
  133. package/components/ui/stepper/index.ts +3 -1
  134. package/components/ui/stepper/stepper.mdx +8 -8
  135. package/components/ui/stepper/stepper.stories.tsx +116 -0
  136. package/components/ui/stepper/stepper.tsx +4 -4
  137. package/components/ui/stepper/use-stepper.ts +137 -0
  138. package/components/ui/switch/switch.mdx +8 -8
  139. package/components/ui/switch/switch.stories.tsx +20 -20
  140. package/components/ui/table/table.mdx +8 -8
  141. package/components/ui/tabs/tabs.mdx +8 -8
  142. package/components/ui/tabs/tabs.stories.tsx +26 -26
  143. package/components/ui/textarea/textarea.mdx +8 -8
  144. package/components/ui/timeline/timeline.mdx +8 -8
  145. package/components/ui/toggle/toggle.mdx +8 -8
  146. package/components/ui/toggle-group/toggle-group.mdx +8 -8
  147. package/components/ui/tooltip/tooltip.mdx +8 -8
  148. package/components/ui/tree-view/index.ts +4 -1
  149. package/components/ui/tree-view/tree-view.mdx +8 -8
  150. package/components/ui/tree-view/tree-view.stories.tsx +110 -4
  151. package/components/ui/tree-view/tree-view.tsx +17 -125
  152. package/components/ui/tree-view/use-tree-view.ts +229 -0
  153. package/components.json +1512 -1512
  154. package/contexts/ApiKeyContext.tsx +72 -72
  155. package/contexts/BrandColorsContext.tsx +26 -26
  156. package/contexts/LanguageContext.tsx +13 -13
  157. package/contexts/LayoutContext.test.tsx +11 -11
  158. package/contexts/LayoutContext.tsx +29 -29
  159. package/contexts/ThemeContext.tsx +26 -26
  160. package/contexts/theme-data.ts +4 -4
  161. package/dist/{AssistantChart-CVzmmhx4.js → AssistantChart-C_hwFRRr.js} +4 -4
  162. package/dist/{AssistantChart-BAx9VQvb.cjs → AssistantChart-CldVCVDe.cjs} +311 -94
  163. package/dist/{AssistantChart-CVko2A1W.js → AssistantChart-Cu3m7RBo.js} +314 -97
  164. package/dist/{AssistantChart-9w31gdAb.cjs → AssistantChart-DoZCyS5r.cjs} +4 -4
  165. package/dist/AudioPlayer-B1lt5cPl.cjs +989 -0
  166. package/dist/AudioPlayer-BZ7bibzU.cjs +982 -0
  167. package/dist/AudioPlayer-C12BjQBV.cjs +997 -0
  168. package/dist/AudioPlayer-DMcG_c7L.js +990 -0
  169. package/dist/AudioPlayer-DcFKRJE_.js +998 -0
  170. package/dist/AudioPlayer-e8LfNoqO.js +983 -0
  171. package/dist/{xertica-assistant-Qp3ydksa.cjs → CodeBlock-7TTgmdGG.cjs} +263 -51
  172. package/dist/{xertica-assistant-gnCJdcZY.js → CodeBlock-BeSt1h5P.js} +219 -7
  173. package/dist/CodeBlock-BgfYL_rD.cjs +2094 -0
  174. package/dist/CodeBlock-BlcqlA9M.cjs +2094 -0
  175. package/dist/CodeBlock-Bnmeu5ez.cjs +2094 -0
  176. package/dist/CodeBlock-BtfPlbAI.js +2078 -0
  177. package/dist/CodeBlock-CIySIuYr.js +2078 -0
  178. package/dist/CodeBlock-CuPtUM-7.cjs +2094 -0
  179. package/dist/CodeBlock-D6ffWXgc.js +2078 -0
  180. package/dist/CodeBlock-D8dcwbit.cjs +2094 -0
  181. package/dist/CodeBlock-DMZrFnlw.cjs +2094 -0
  182. package/dist/CodeBlock-DlBehYN8.js +2078 -0
  183. package/dist/CodeBlock-DnYNI8rQ.js +2078 -0
  184. package/dist/CodeBlock-DvKWbSnE.cjs +2094 -0
  185. package/dist/CodeBlock-DwMCfkFY.js +2078 -0
  186. package/dist/CodeBlock-Dy6CNYyj.js +2078 -0
  187. package/dist/CodeBlock-U1pPOQI7.cjs +2094 -0
  188. package/dist/CodeBlock-f_GpNhEB.js +2078 -0
  189. package/dist/CodeBlock-oB6u8nI1.js +2078 -0
  190. package/dist/CodeBlock-tZC31B73.cjs +2094 -0
  191. package/dist/ImageWithFallback-CGtidP6B.cjs +4542 -0
  192. package/dist/ImageWithFallback-lsg3pdFg.js +4508 -0
  193. package/dist/LanguageSelector-B5YfbHra.js +231 -0
  194. package/dist/LanguageSelector-D6uacAIM.cjs +230 -0
  195. package/dist/LayoutContext-BAql6ZRY.js +97 -0
  196. package/dist/LayoutContext-BvK-ggDa.cjs +96 -0
  197. package/dist/ThemeContext-BoH4NLfN.js +734 -0
  198. package/dist/{ThemeContext-BbBNoFTG.js → ThemeContext-C2EwAPDt.js} +2 -2
  199. package/dist/ThemeContext-r69W20Xg.cjs +733 -0
  200. package/dist/{ThemeContext-Cmr8Ex8H.cjs → ThemeContext-vTjumZeM.cjs} +2 -2
  201. package/dist/{VerifyEmailPage-BE-L9mB7.js → VerifyEmailPage-C0c2e5n0.js} +7 -7
  202. package/dist/{VerifyEmailPage-DMBh4NM9.cjs → VerifyEmailPage-CYXtbKi3.cjs} +1 -1
  203. package/dist/{VerifyEmailPage-CR7kb5df.cjs → VerifyEmailPage-Cwi3kbol.cjs} +7 -7
  204. package/dist/{VerifyEmailPage-CdYPSJoO.js → VerifyEmailPage-DvMLZgFt.js} +1 -1
  205. package/dist/XerticaOrbe-KL1RBHzw.cjs +1354 -0
  206. package/dist/XerticaOrbe-zwS1p2a8.js +1355 -0
  207. package/dist/XerticaProvider-6btlAlzc.js +17 -0
  208. package/dist/XerticaProvider-BNoNOxQ5.cjs +16 -0
  209. package/dist/XerticaProvider-BlY2limY.cjs +38 -0
  210. package/dist/{XerticaProvider-BITjgC5p.js → XerticaProvider-CEoWMTxu.js} +2 -2
  211. package/dist/{XerticaProvider-By8q3Roe.cjs → XerticaProvider-CllrbMEJ.cjs} +2 -2
  212. package/dist/XerticaProvider-DDuiIcKo.js +39 -0
  213. package/dist/XerticaProvider-cI9hSs27.cjs +38 -0
  214. package/dist/XerticaProvider-hSwhNQex.js +39 -0
  215. package/dist/{alert-dialog-yckpaOpy.cjs → alert-dialog-DSKByiKZ.cjs} +3 -3
  216. package/dist/{alert-dialog-iDe5VE5o.js → alert-dialog-s-vmNkJ_.js} +3 -3
  217. package/dist/assistant.cjs.js +2 -1
  218. package/dist/assistant.es.js +3 -2
  219. package/dist/brand.cjs.js +1 -1
  220. package/dist/brand.es.js +1 -1
  221. package/dist/breadcrumb-CqJ7bHY5.js +161 -0
  222. package/dist/breadcrumb-m9Hb2_XN.cjs +177 -0
  223. package/dist/cli.js +14 -8
  224. package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
  225. package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +126 -0
  226. package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +7 -2
  227. package/dist/components/blocks/audio-player/AudioPlayer.d.ts +35 -0
  228. package/dist/components/blocks/audio-player/index.d.ts +1 -0
  229. package/dist/components/blocks/document-editor/DocumentEditor.d.ts +26 -0
  230. package/dist/components/blocks/document-editor/index.d.ts +1 -0
  231. package/dist/components/blocks/podcast-player/PodcastPlayer.d.ts +41 -0
  232. package/dist/components/blocks/podcast-player/index.d.ts +1 -0
  233. package/dist/components/layout/sidebar/index.d.ts +2 -0
  234. package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
  235. package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
  236. package/dist/components/shared/layout-constants.d.ts +1 -1
  237. package/dist/components/ui/button/button.d.ts +1 -1
  238. package/dist/components/ui/chart/chart.d.ts +162 -5
  239. package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
  240. package/dist/components/ui/file-upload/index.d.ts +1 -0
  241. package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
  242. package/dist/components/ui/pagination/index.d.ts +2 -0
  243. package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
  244. package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
  245. package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
  246. package/dist/components/ui/stepper/index.d.ts +3 -1
  247. package/dist/components/ui/stepper/stepper.d.ts +2 -2
  248. package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
  249. package/dist/components/ui/tree-view/index.d.ts +4 -1
  250. package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
  251. package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
  252. package/dist/dropdown-menu-BDB5CmQs.cjs +247 -0
  253. package/dist/dropdown-menu-DQidbKBD.js +231 -0
  254. package/dist/google-maps-loader-BFWp6VPd.js +287 -0
  255. package/dist/{google-maps-loader-t2IlYBzw.js → google-maps-loader-CTYySAun.js} +4 -0
  256. package/dist/{google-maps-loader-BqsYL48U.cjs → google-maps-loader-Y-QkD-Li.cjs} +5 -0
  257. package/dist/google-maps-loader-eS3uQ5TA.cjs +287 -0
  258. package/dist/header-Cgy6vYPk.cjs +731 -0
  259. package/dist/header-DRlT4jgI.js +715 -0
  260. package/dist/header-Dux00SI4.cjs +731 -0
  261. package/dist/header-EkGKXPsD.js +715 -0
  262. package/dist/header-WfEywpyc.cjs +731 -0
  263. package/dist/header-tifNQn2U.js +715 -0
  264. package/dist/hooks.cjs.js +1 -1
  265. package/dist/hooks.es.js +1 -1
  266. package/dist/index-BhapVLVj.js +8 -0
  267. package/dist/{index-D3RLKRAs.cjs → index-COtD8bRW.cjs} +1 -1
  268. package/dist/index-D6fxYEY8.cjs +7 -0
  269. package/dist/index-DW5tYe26.js +8 -0
  270. package/dist/index.cjs.js +19 -8
  271. package/dist/index.es.js +39 -28
  272. package/dist/index.umd.js +1043 -470
  273. package/dist/input-2R4loU86.js +127 -0
  274. package/dist/input-DWANSKGb.cjs +145 -0
  275. package/dist/layout.cjs.js +89 -1
  276. package/dist/layout.es.js +90 -2
  277. package/dist/pages.cjs.js +1 -1
  278. package/dist/pages.es.js +1 -1
  279. package/dist/rich-text-editor-0mraWT5y.cjs +2376 -0
  280. package/dist/rich-text-editor-B6jMRLzk.cjs +1939 -0
  281. package/dist/rich-text-editor-B8_oYcIR.js +1730 -0
  282. package/dist/rich-text-editor-B9UbSXNb.js +1203 -0
  283. package/dist/rich-text-editor-BYuRBNBU.js +2373 -0
  284. package/dist/rich-text-editor-Bb9pySTs.cjs +2374 -0
  285. package/dist/rich-text-editor-BcL6L3cm.cjs +2374 -0
  286. package/dist/rich-text-editor-BoVZYtTs.cjs +2391 -0
  287. package/dist/rich-text-editor-CPV1lEPH.cjs +1748 -0
  288. package/dist/rich-text-editor-CoKqbCtu.cjs +1799 -0
  289. package/dist/rich-text-editor-Cw56T_mB.js +2356 -0
  290. package/dist/rich-text-editor-Cyt8qs2b.js +1921 -0
  291. package/dist/rich-text-editor-D6H84OcX.cjs +1220 -0
  292. package/dist/rich-text-editor-D76gD-QI.js +2328 -0
  293. package/dist/rich-text-editor-DKkokOnA.js +1781 -0
  294. package/dist/rich-text-editor-DNsdpN64.cjs +2359 -0
  295. package/dist/rich-text-editor-DfG8bCyY.js +2358 -0
  296. package/dist/rich-text-editor-DgF8s7xW.js +2949 -0
  297. package/dist/rich-text-editor-Dxjw31Z4.js +2341 -0
  298. package/dist/rich-text-editor-DzP0Epmb.js +2356 -0
  299. package/dist/rich-text-editor-mWoaSCE4.cjs +2966 -0
  300. package/dist/rich-text-editor-skplNlBM.cjs +2345 -0
  301. package/dist/select-Bkbr0f-Z.cjs +162 -0
  302. package/dist/select-CvIVdX2n.js +145 -0
  303. package/dist/sidebar-CK_0ZQHj.cjs +803 -0
  304. package/dist/sidebar-CUuOvYhK.js +787 -0
  305. package/dist/slider-Bc5Hd0y1.js +56 -0
  306. package/dist/slider-N7hFFj6X.cjs +73 -0
  307. package/dist/tooltip-Ded96neP.cjs +137 -0
  308. package/dist/tooltip-HDOoD2-0.js +120 -0
  309. package/dist/ui.cjs.js +14 -4
  310. package/dist/ui.es.js +26 -16
  311. package/dist/use-mobile-B0hNy_Y6.cjs +4303 -0
  312. package/dist/use-mobile-BXuYROXM.js +4202 -0
  313. package/dist/use-mobile-Bbd51ASU.cjs +4392 -0
  314. package/dist/use-mobile-Bk6CX-TC.js +4359 -0
  315. package/dist/use-mobile-BvYdisLP.js +4202 -0
  316. package/dist/use-mobile-BzuxjzNX.cjs +4392 -0
  317. package/dist/use-mobile-CG2-SdXV.cjs +4235 -0
  318. package/dist/use-mobile-CKb5pqTs.js +4269 -0
  319. package/dist/use-mobile-CYuAuGDl.js +4202 -0
  320. package/dist/use-mobile-CaENcqm-.js +4508 -0
  321. package/dist/use-mobile-CbrYgJGJ.js +4203 -0
  322. package/dist/use-mobile-DMOvImGQ.cjs +4542 -0
  323. package/dist/use-mobile-DRB3BQgD.cjs +4235 -0
  324. package/dist/use-mobile-DZvv7QMR.js +4359 -0
  325. package/dist/use-mobile-DdI_TXam.cjs +4235 -0
  326. package/dist/use-mobile-DlceKf8a.js +4359 -0
  327. package/dist/use-mobile-DsOnow1o.cjs +4236 -0
  328. package/dist/use-mobile-Kcj6jSnK.cjs +4392 -0
  329. package/dist/use-mobile-bnKcua_i.js +4202 -0
  330. package/dist/use-mobile-ncXBeE2z.cjs +4235 -0
  331. package/dist/{xertica-assistant-B1IaHXnB.cjs → xertica-assistant-dyP7KHM5.cjs} +533 -392
  332. package/dist/{xertica-assistant-DPsESB6t.js → xertica-assistant-yX1CFBBo.js} +535 -394
  333. package/dist/xertica-ui.css +2 -2
  334. package/docs/ai-usage.md +28 -10
  335. package/docs/architecture.md +76 -0
  336. package/docs/components/assistant.md +159 -0
  337. package/docs/components/calendar.md +154 -154
  338. package/docs/components/card-patterns.md +337 -337
  339. package/docs/components/card.md +235 -235
  340. package/docs/components/chart.md +354 -39
  341. package/docs/components/file-upload.md +119 -2
  342. package/docs/components/map.md +84 -84
  343. package/docs/components/pagination.md +187 -0
  344. package/docs/components/rich-text-editor.md +164 -0
  345. package/docs/components/sidebar.md +153 -4
  346. package/docs/components/stepper.md +157 -12
  347. package/docs/components/tree-view.md +164 -6
  348. package/docs/components/xertica-provider.md +24 -24
  349. package/docs/decision-tree.md +287 -287
  350. package/docs/getting-started.md +1 -1
  351. package/docs/guidelines.md +14 -8
  352. package/docs/layout.md +2 -2
  353. package/docs/llms.md +4 -4
  354. package/guidelines/Guidelines.md +252 -250
  355. package/hooks/useTheme.ts +3 -3
  356. package/imports/Podcast.tsx +388 -388
  357. package/imports/XerticaAi.tsx +45 -45
  358. package/imports/XerticaX.tsx +19 -19
  359. package/imports/svg-aueiaqngck.ts +11 -11
  360. package/imports/svg-v9krss1ozd.ts +16 -16
  361. package/imports/svg-vhrdofe3qe.ts +5 -5
  362. package/llms-compact.txt +327 -327
  363. package/llms.txt +160 -160
  364. package/package.json +203 -203
  365. package/styles/xertica/app-overrides/chat.css +61 -61
  366. package/styles/xertica/app-overrides/scrollbar.css +33 -33
  367. package/styles/xertica/integrations/google-maps.css +76 -76
  368. package/styles/xertica/integrations/sonner.css +73 -73
  369. package/styles/xertica/tokens.css +41 -12
  370. package/templates/CLAUDE.md +182 -172
  371. package/templates/guidelines/Guidelines.md +325 -313
  372. package/templates/package.json +3 -3
  373. package/templates/src/features/auth/index.ts +4 -4
  374. package/templates/src/features/auth/ui/AuthPageShell.tsx +34 -34
  375. package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +70 -70
  376. package/templates/src/features/auth/ui/LoginContent.tsx +90 -90
  377. package/templates/src/features/auth/ui/ResetPasswordContent.tsx +151 -151
  378. package/templates/src/features/auth/ui/SocialLoginButtons.tsx +59 -59
  379. package/templates/src/features/auth/ui/VerifyEmailContent.tsx +82 -82
  380. package/templates/src/features/home/index.ts +1 -1
  381. package/templates/src/features/home/ui/HomeContent.tsx +100 -100
  382. package/templates/src/features/template/index.ts +5 -5
  383. package/templates/src/features/template/ui/CrudTemplate.tsx +3 -3
  384. package/templates/src/features/template/ui/DashboardTemplate.tsx +3 -3
  385. package/templates/src/features/template/ui/FormTemplate.tsx +120 -120
  386. package/templates/src/features/template/ui/LoginTemplate.tsx +3 -3
  387. package/templates/src/pages/AssistantPage.tsx +328 -328
  388. package/templates/src/pages/ForgotPasswordPage.tsx +6 -6
  389. package/templates/src/pages/HomePage.tsx +57 -57
  390. package/templates/src/pages/LoginPage.tsx +10 -10
  391. package/templates/src/pages/ResetPasswordPage.tsx +6 -6
  392. package/templates/src/pages/TemplatePage.tsx +30 -30
  393. package/templates/src/pages/VerifyEmailPage.tsx +6 -6
  394. package/templates/src/shared/config/navigation.ts +20 -20
  395. package/templates/src/shared/lib/auth.ts +20 -20
  396. package/templates/src/shared/types/auth.ts +3 -3
  397. package/templates/src/styles/xertica/tokens.css +39 -10
  398. package/templates/tsconfig.json +5 -5
  399. package/utils/gemini.ts +140 -140
  400. package/dist/VerifyEmailPage-Bae2cBXT.cjs +0 -2827
  401. package/dist/VerifyEmailPage-CbgjOF0v.js +0 -2828
  402. package/dist/index-CkTUgOwX.js +0 -8
@@ -0,0 +1,194 @@
1
+ import { useState, useCallback, useMemo } from 'react';
2
+
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // Types
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+
7
+ /** A page item rendered in the pagination bar. */
8
+ export type PaginationPageItem =
9
+ | { type: 'page'; page: number }
10
+ | { type: 'ellipsis'; key: string };
11
+
12
+ export interface UsePaginationProps {
13
+ /** Total number of items in the data set. */
14
+ totalItems: number;
15
+ /** Number of items per page. @default 10 */
16
+ pageSize?: number;
17
+ /** Initial page (1-indexed). @default 1 */
18
+ initialPage?: number;
19
+ /**
20
+ * Controlled current page (1-indexed).
21
+ * When provided, page state is managed externally via `onPageChange`.
22
+ */
23
+ page?: number;
24
+ /** Called whenever the page changes. */
25
+ onPageChange?: (page: number) => void;
26
+ /**
27
+ * Number of page buttons to show on each side of the current page.
28
+ * @default 1
29
+ */
30
+ siblingCount?: number;
31
+ }
32
+
33
+ export interface UsePaginationReturn {
34
+ // ── State ─────────────────────────────────────────────────────────────────
35
+ /** The currently active page (1-indexed). */
36
+ currentPage: number;
37
+ /** Total number of pages. */
38
+ totalPages: number;
39
+ /** Index of the first item on the current page (0-indexed, useful for slicing). */
40
+ startIndex: number;
41
+ /** Index of the last item on the current page (exclusive, useful for slicing). */
42
+ endIndex: number;
43
+
44
+ // ── Flags ─────────────────────────────────────────────────────────────────
45
+ /** Whether there is a previous page available. */
46
+ canGoPrev: boolean;
47
+ /** Whether there is a next page available. */
48
+ canGoNext: boolean;
49
+ /** Whether the current page is the first page. */
50
+ isFirstPage: boolean;
51
+ /** Whether the current page is the last page. */
52
+ isLastPage: boolean;
53
+
54
+ // ── Computed ──────────────────────────────────────────────────────────────
55
+ /**
56
+ * Ordered list of items to render in the pagination bar.
57
+ * Each item is either a `{ type: 'page', page: number }` or
58
+ * `{ type: 'ellipsis', key: string }`.
59
+ */
60
+ items: PaginationPageItem[];
61
+
62
+ // ── Handlers ──────────────────────────────────────────────────────────────
63
+ /** Navigate to a specific page (1-indexed, clamped to valid range). */
64
+ goTo: (page: number) => void;
65
+ /** Navigate to the next page (no-op if already on the last page). */
66
+ next: () => void;
67
+ /** Navigate to the previous page (no-op if already on the first page). */
68
+ prev: () => void;
69
+ /** Navigate to the first page. */
70
+ first: () => void;
71
+ /** Navigate to the last page. */
72
+ last: () => void;
73
+ }
74
+
75
+ // ─────────────────────────────────────────────────────────────────────────────
76
+ // Hook
77
+ // ─────────────────────────────────────────────────────────────────────────────
78
+
79
+ /**
80
+ * Headless hook for pagination logic.
81
+ *
82
+ * @description
83
+ * Computes the current page, total pages, slice indices, and the ordered list
84
+ * of page items (numbers + ellipses) to render. Supports both controlled and
85
+ * uncontrolled modes. Pair with any custom pagination UI or with the
86
+ * `<Pagination>` visual components.
87
+ *
88
+ * @example
89
+ * ```tsx
90
+ * const { currentPage, totalPages, items, next, prev, goTo, canGoPrev, canGoNext } =
91
+ * usePagination({ totalItems: 200, pageSize: 20 });
92
+ * ```
93
+ */
94
+ export function usePagination({
95
+ totalItems,
96
+ pageSize = 10,
97
+ initialPage = 1,
98
+ page: controlledPage,
99
+ onPageChange,
100
+ siblingCount = 1,
101
+ }: UsePaginationProps): UsePaginationReturn {
102
+ const [internalPage, setInternalPage] = useState(initialPage);
103
+
104
+ const isControlled = controlledPage !== undefined;
105
+ const currentPage = isControlled ? controlledPage! : internalPage;
106
+
107
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
108
+
109
+ const setPage = useCallback(
110
+ (p: number) => {
111
+ const clamped = Math.min(Math.max(1, p), totalPages);
112
+ if (!isControlled) setInternalPage(clamped);
113
+ onPageChange?.(clamped);
114
+ },
115
+ [isControlled, onPageChange, totalPages]
116
+ );
117
+
118
+ // ── Derived ───────────────────────────────────────────────────────────────
119
+
120
+ const startIndex = (currentPage - 1) * pageSize;
121
+ const endIndex = Math.min(startIndex + pageSize, totalItems);
122
+ const canGoPrev = currentPage > 1;
123
+ const canGoNext = currentPage < totalPages;
124
+ const isFirstPage = currentPage === 1;
125
+ const isLastPage = currentPage === totalPages;
126
+
127
+ // ── Page items (numbers + ellipses) ───────────────────────────────────────
128
+
129
+ const items = useMemo((): PaginationPageItem[] => {
130
+ // Always show first, last, current ± siblingCount
131
+ const range = (start: number, end: number): number[] =>
132
+ Array.from({ length: end - start + 1 }, (_, i) => start + i);
133
+
134
+ const leftSibling = Math.max(currentPage - siblingCount, 1);
135
+ const rightSibling = Math.min(currentPage + siblingCount, totalPages);
136
+
137
+ const showLeftEllipsis = leftSibling > 2;
138
+ const showRightEllipsis = rightSibling < totalPages - 1;
139
+
140
+ const result: PaginationPageItem[] = [];
141
+
142
+ // First page
143
+ result.push({ type: 'page', page: 1 });
144
+
145
+ if (showLeftEllipsis) {
146
+ result.push({ type: 'ellipsis', key: 'ellipsis-left' });
147
+ } else if (leftSibling === 2) {
148
+ result.push({ type: 'page', page: 2 });
149
+ }
150
+
151
+ // Sibling range (excluding first and last)
152
+ for (const p of range(Math.max(leftSibling, 2), Math.min(rightSibling, totalPages - 1))) {
153
+ result.push({ type: 'page', page: p });
154
+ }
155
+
156
+ if (showRightEllipsis) {
157
+ result.push({ type: 'ellipsis', key: 'ellipsis-right' });
158
+ } else if (rightSibling === totalPages - 1) {
159
+ result.push({ type: 'page', page: totalPages - 1 });
160
+ }
161
+
162
+ // Last page (only if more than 1 page)
163
+ if (totalPages > 1) {
164
+ result.push({ type: 'page', page: totalPages });
165
+ }
166
+
167
+ return result;
168
+ }, [currentPage, siblingCount, totalPages]);
169
+
170
+ // ── Handlers ──────────────────────────────────────────────────────────────
171
+
172
+ const goTo = useCallback((p: number) => setPage(p), [setPage]);
173
+ const next = useCallback(() => setPage(currentPage + 1), [currentPage, setPage]);
174
+ const prev = useCallback(() => setPage(currentPage - 1), [currentPage, setPage]);
175
+ const first = useCallback(() => setPage(1), [setPage]);
176
+ const last = useCallback(() => setPage(totalPages), [setPage, totalPages]);
177
+
178
+ return {
179
+ currentPage,
180
+ totalPages,
181
+ startIndex,
182
+ endIndex,
183
+ canGoPrev,
184
+ canGoNext,
185
+ isFirstPage,
186
+ isLastPage,
187
+ items,
188
+ goTo,
189
+ next,
190
+ prev,
191
+ first,
192
+ last,
193
+ };
194
+ }
@@ -10,14 +10,14 @@ import * as PopoverStories from './popover.stories';
10
10
 
11
11
  <Primary />
12
12
 
13
- ---
14
-
15
- ## Variations
16
-
17
- <Stories />
18
-
19
- ---
20
-
13
+ ---
14
+
15
+ ## Variations
16
+
17
+ <Stories />
18
+
19
+ ---
20
+
21
21
  ## AI Best Practices
22
22
 
23
23
  > [!IMPORTANT]
@@ -10,14 +10,14 @@ import * as ProgressStories from './progress.stories';
10
10
 
11
11
  <Primary />
12
12
 
13
- ---
14
-
15
- ## Variations
16
-
17
- <Stories />
18
-
19
- ---
20
-
13
+ ---
14
+
15
+ ## Variations
16
+
17
+ <Stories />
18
+
19
+ ---
20
+
21
21
  ## AI Best Practices
22
22
 
23
23
  > [!IMPORTANT]
@@ -1,68 +1,68 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import * as ProgressPrimitive from "@radix-ui/react-progress";
5
-
6
- import { cn } from "../../shared/utils";
7
-
8
- type ProgressVariant = "default" | "success" | "info" | "warning" | "destructive";
9
-
10
- const trackVariants: Record<ProgressVariant, string> = {
11
- default: "bg-primary/20",
12
- success: "bg-success/20",
13
- info: "bg-info/20",
14
- warning: "bg-warning/20",
15
- destructive: "bg-destructive/20",
16
- };
17
-
18
- const indicatorVariants: Record<ProgressVariant, string> = {
19
- default: "bg-primary",
20
- success: "bg-success",
21
- info: "bg-info",
22
- warning: "bg-warning",
23
- destructive: "bg-destructive",
24
- };
25
-
26
- /**
27
- * Numeric progress bar from 0 to 100.
28
- *
29
- * @description
30
- * Indicates deterministic task progress (file upload %, form completion steps, etc.).
31
- * Built on Radix UI Progress with smooth CSS transition.
32
- *
33
- * @ai-rules
34
- * 1. Control progress via the `value` prop (e.g., `value={66}`).
35
- * 2. Use `variant` to communicate semantic state: `success`, `info`, `warning`, `destructive`.
36
- * 3. For indeterminate / infinite loading, use `<Loader2 className="animate-spin" />` — not `<Progress>`.
37
- * 4. Available variants: `default`, `success`, `info`, `warning`, `destructive`.
38
- */
39
- function Progress({
40
- className,
41
- value,
42
- variant = "default",
43
- ...props
44
- }: React.ComponentProps<typeof ProgressPrimitive.Root> & {
45
- variant?: ProgressVariant;
46
- }) {
47
- return (
48
- <ProgressPrimitive.Root
49
- data-slot="progress"
50
- aria-label={props["aria-label"] || "Progresso"}
51
- className={cn(
52
- "relative h-2 w-full overflow-hidden rounded-full",
53
- trackVariants[variant],
54
- className,
55
- )}
56
- {...props}
57
- >
58
- <ProgressPrimitive.Indicator
59
- data-slot="progress-indicator"
60
- className={cn("h-full w-full flex-1 transition-all", indicatorVariants[variant])}
61
- style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
62
- />
63
- </ProgressPrimitive.Root>
64
- );
65
- }
66
-
67
- export { Progress };
68
- export type { ProgressVariant };
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ProgressPrimitive from "@radix-ui/react-progress";
5
+
6
+ import { cn } from "../../shared/utils";
7
+
8
+ type ProgressVariant = "default" | "success" | "info" | "warning" | "destructive";
9
+
10
+ const trackVariants: Record<ProgressVariant, string> = {
11
+ default: "bg-primary/20",
12
+ success: "bg-success/20",
13
+ info: "bg-info/20",
14
+ warning: "bg-warning/20",
15
+ destructive: "bg-destructive/20",
16
+ };
17
+
18
+ const indicatorVariants: Record<ProgressVariant, string> = {
19
+ default: "bg-primary",
20
+ success: "bg-success",
21
+ info: "bg-info",
22
+ warning: "bg-warning",
23
+ destructive: "bg-destructive",
24
+ };
25
+
26
+ /**
27
+ * Numeric progress bar from 0 to 100.
28
+ *
29
+ * @description
30
+ * Indicates deterministic task progress (file upload %, form completion steps, etc.).
31
+ * Built on Radix UI Progress with smooth CSS transition.
32
+ *
33
+ * @ai-rules
34
+ * 1. Control progress via the `value` prop (e.g., `value={66}`).
35
+ * 2. Use `variant` to communicate semantic state: `success`, `info`, `warning`, `destructive`.
36
+ * 3. For indeterminate / infinite loading, use `<Loader2 className="animate-spin" />` — not `<Progress>`.
37
+ * 4. Available variants: `default`, `success`, `info`, `warning`, `destructive`.
38
+ */
39
+ function Progress({
40
+ className,
41
+ value,
42
+ variant = "default",
43
+ ...props
44
+ }: React.ComponentProps<typeof ProgressPrimitive.Root> & {
45
+ variant?: ProgressVariant;
46
+ }) {
47
+ return (
48
+ <ProgressPrimitive.Root
49
+ data-slot="progress"
50
+ aria-label={props["aria-label"] || "Progresso"}
51
+ className={cn(
52
+ "relative h-2 w-full overflow-hidden rounded-full",
53
+ trackVariants[variant],
54
+ className,
55
+ )}
56
+ {...props}
57
+ >
58
+ <ProgressPrimitive.Indicator
59
+ data-slot="progress-indicator"
60
+ className={cn("h-full w-full flex-1 transition-all", indicatorVariants[variant])}
61
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
62
+ />
63
+ </ProgressPrimitive.Root>
64
+ );
65
+ }
66
+
67
+ export { Progress };
68
+ export type { ProgressVariant };
@@ -10,14 +10,14 @@ import * as RadioGroupStories from './radio-group.stories';
10
10
 
11
11
  <Primary />
12
12
 
13
- ---
14
-
15
- ## Variations
16
-
17
- <Stories />
18
-
19
- ---
20
-
13
+ ---
14
+
15
+ ## Variations
16
+
17
+ <Stories />
18
+
19
+ ---
20
+
21
21
  ## AI Best Practices
22
22
 
23
23
  > [!IMPORTANT]
@@ -10,14 +10,14 @@ import * as RatingStories from './rating.stories';
10
10
 
11
11
  <Primary />
12
12
 
13
- ---
14
-
15
- ## Variations
16
-
17
- <Stories />
18
-
19
- ---
20
-
13
+ ---
14
+
15
+ ## Variations
16
+
17
+ <Stories />
18
+
19
+ ---
20
+
21
21
  ## AI Best Practices
22
22
 
23
23
  > [!IMPORTANT]
@@ -10,14 +10,14 @@ import * as ResizableStories from './resizable.stories';
10
10
 
11
11
  <Primary />
12
12
 
13
- ---
14
-
15
- ## Variations
16
-
17
- <Stories />
18
-
19
- ---
20
-
13
+ ---
14
+
15
+ ## Variations
16
+
17
+ <Stories />
18
+
19
+ ---
20
+
21
21
  ## AI Best Practices
22
22
 
23
23
  > [!IMPORTANT]
@@ -1 +1,3 @@
1
1
  export * from './rich-text-editor';
2
+ export { useRichTextEditor } from './use-rich-text-editor';
3
+ export type { UseRichTextEditorProps, UseRichTextEditorReturn, ActiveFormats } from './use-rich-text-editor';
@@ -1,7 +1,8 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { RichTextEditor } from './rich-text-editor';
3
+ import { useRichTextEditor } from './use-rich-text-editor';
3
4
  import { Button } from '../button';
4
- import { Check, Save } from 'lucide-react';
5
+ import { Check, Bold, Italic, Underline, Link, Unlink, Search } from 'lucide-react';
5
6
  import React, { useState } from 'react';
6
7
 
7
8
  const meta: Meta<typeof RichTextEditor> = {
@@ -132,3 +133,130 @@ export const Empty: Story = {
132
133
  ),
133
134
  };
134
135
 
136
+ /**
137
+ * **Headless Hook** — demonstrates `useRichTextEditor()` with a fully custom UI.
138
+ *
139
+ * The hook manages all editor state (active formats, search, link popover,
140
+ * word/character counts) while you supply your own markup and toolbar.
141
+ * This story renders a minimal custom toolbar + `contenteditable` div
142
+ * without using `<RichTextEditor>` at all.
143
+ */
144
+ export const HeadlessHook: Story = {
145
+ render: () => {
146
+ const [value, setValue] = useState(
147
+ '<p>Edit this text using the <strong>custom toolbar</strong> below.</p>'
148
+ );
149
+
150
+ const {
151
+ editorRef,
152
+ linkInputRef,
153
+ activeFormats,
154
+ isLinkOpen,
155
+ linkUrl,
156
+ setLinkUrl,
157
+ wordCount,
158
+ characterCount,
159
+ execCommand,
160
+ handleInput,
161
+ updateActiveFormats,
162
+ handleCreateLink,
163
+ handleUnlink,
164
+ onLinkPopoverOpenChange,
165
+ } = useRichTextEditor({ value, onChange: setValue });
166
+
167
+ const btnClass = (active: boolean) =>
168
+ `px-2 py-1 rounded text-sm border transition-colors ${active
169
+ ? 'bg-primary text-primary-foreground border-primary'
170
+ : 'bg-background text-foreground border-border hover:bg-muted'
171
+ }`;
172
+
173
+ return (
174
+ <div className="w-full max-w-2xl mx-auto p-6 space-y-4">
175
+ <div className="text-sm text-muted-foreground font-medium">
176
+ Custom headless editor — built with <code className="bg-muted px-1 rounded">useRichTextEditor()</code>
177
+ </div>
178
+
179
+ {/* Custom toolbar */}
180
+ <div className="flex items-center gap-1 p-2 border border-border rounded-lg bg-muted/30 flex-wrap">
181
+ <button
182
+ className={btnClass(activeFormats.bold)}
183
+ onMouseDown={(e) => { e.preventDefault(); execCommand('bold'); }}
184
+ title="Bold"
185
+ >
186
+ <Bold className="w-4 h-4" />
187
+ </button>
188
+ <button
189
+ className={btnClass(activeFormats.italic)}
190
+ onMouseDown={(e) => { e.preventDefault(); execCommand('italic'); }}
191
+ title="Italic"
192
+ >
193
+ <Italic className="w-4 h-4" />
194
+ </button>
195
+ <button
196
+ className={btnClass(activeFormats.underline)}
197
+ onMouseDown={(e) => { e.preventDefault(); execCommand('underline'); }}
198
+ title="Underline"
199
+ >
200
+ <Underline className="w-4 h-4" />
201
+ </button>
202
+
203
+ <div className="w-px h-5 bg-border mx-1" />
204
+
205
+ {/* Link controls */}
206
+ <div className="relative flex items-center gap-1">
207
+ <button
208
+ className={btnClass(isLinkOpen || activeFormats.link)}
209
+ onMouseDown={(e) => { e.preventDefault(); onLinkPopoverOpenChange(!isLinkOpen); }}
210
+ title="Insert link"
211
+ >
212
+ <Link className="w-4 h-4" />
213
+ </button>
214
+ {activeFormats.link && (
215
+ <button
216
+ className={btnClass(false)}
217
+ onMouseDown={(e) => { e.preventDefault(); handleUnlink(); }}
218
+ title="Remove link"
219
+ >
220
+ <Unlink className="w-4 h-4" />
221
+ </button>
222
+ )}
223
+ {isLinkOpen && (
224
+ <div className="absolute top-full left-0 mt-1 z-50 bg-popover border border-border rounded-lg shadow-lg p-3 flex gap-2 w-72">
225
+ <input
226
+ ref={linkInputRef}
227
+ type="url"
228
+ value={linkUrl}
229
+ onChange={(e) => setLinkUrl(e.target.value)}
230
+ placeholder="https://example.com"
231
+ className="flex-1 text-sm border border-border rounded px-2 py-1 bg-background"
232
+ onKeyDown={(e) => { if (e.key === 'Enter') handleCreateLink(); }}
233
+ />
234
+ <Button size="sm" onClick={handleCreateLink}>Apply</Button>
235
+ </div>
236
+ )}
237
+ </div>
238
+ </div>
239
+
240
+ {/* Custom contenteditable editor */}
241
+ <div
242
+ ref={editorRef}
243
+ contentEditable
244
+ suppressContentEditableWarning
245
+ onInput={handleInput}
246
+ onKeyUp={updateActiveFormats}
247
+ onMouseUp={updateActiveFormats}
248
+ className="min-h-[200px] p-4 border border-border rounded-lg bg-background focus:outline-none focus:ring-2 focus:ring-ring text-sm"
249
+ style={{ lineHeight: 1.6 }}
250
+ dangerouslySetInnerHTML={{ __html: value }}
251
+ />
252
+
253
+ {/* Custom footer with counts */}
254
+ <div className="flex items-center justify-between text-xs text-muted-foreground px-1">
255
+ <span>Words: <strong>{wordCount}</strong></span>
256
+ <span>Characters: <strong>{characterCount}</strong></span>
257
+ </div>
258
+ </div>
259
+ );
260
+ },
261
+ };
262
+