analytica-frontend-lib 1.7.21 → 1.7.23

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 (384) hide show
  1. package/dist/AccessReportModal/index.js +3 -3
  2. package/dist/AccessReportModal/index.mjs +2 -2
  3. package/dist/ActivitiesHistory/index.js +49 -49
  4. package/dist/ActivitiesHistory/index.mjs +48 -48
  5. package/dist/ActivityCardQuestionBanks/index.js +49 -49
  6. package/dist/ActivityCardQuestionBanks/index.mjs +48 -48
  7. package/dist/ActivityCardQuestionPreview/index.js +49 -49
  8. package/dist/ActivityCardQuestionPreview/index.mjs +48 -48
  9. package/dist/ActivityCreate/ActivityCreateContent/index.js +49 -49
  10. package/dist/ActivityCreate/ActivityCreateContent/index.mjs +48 -48
  11. package/dist/ActivityCreate/index.js +49 -49
  12. package/dist/ActivityCreate/index.mjs +48 -48
  13. package/dist/ActivityDetails/index.js +49 -49
  14. package/dist/ActivityDetails/index.mjs +48 -48
  15. package/dist/ActivityFilters/index.js +49 -49
  16. package/dist/ActivityFilters/index.mjs +48 -48
  17. package/dist/ActivityPageLayout/index.js +49 -49
  18. package/dist/ActivityPageLayout/index.mjs +48 -48
  19. package/dist/ActivityPreview/index.js +49 -49
  20. package/dist/ActivityPreview/index.mjs +48 -48
  21. package/dist/AlertManager/index.js +49 -49
  22. package/dist/AlertManager/index.mjs +48 -48
  23. package/dist/AlertManagerView/index.js +3 -3
  24. package/dist/AlertManagerView/index.mjs +2 -2
  25. package/dist/AnnouncementModal/index.js +3 -3
  26. package/dist/AnnouncementModal/index.mjs +2 -2
  27. package/dist/AnnouncementsGate/index.js +4 -4
  28. package/dist/AnnouncementsGate/index.mjs +3 -3
  29. package/dist/AnswerKeyModal/index.js +3 -3
  30. package/dist/AnswerKeyModal/index.mjs +2 -2
  31. package/dist/AppHeader/index.js +6 -6
  32. package/dist/AppHeader/index.mjs +5 -5
  33. package/dist/CalendarCard/index.js +4 -4
  34. package/dist/CalendarCard/index.mjs +3 -3
  35. package/dist/Chat/index.js +3 -3
  36. package/dist/Chat/index.mjs +2 -2
  37. package/dist/CheckBox/index.d.ts.map +1 -1
  38. package/dist/CheckBox/index.js +2 -2
  39. package/dist/CheckBox/index.mjs +1 -1
  40. package/dist/CheckBoxGroup/index.js +49 -49
  41. package/dist/CheckBoxGroup/index.mjs +48 -48
  42. package/dist/ComparatorChart/ComparatorView/index.js +3 -3
  43. package/dist/ComparatorChart/ComparatorView/index.mjs +2 -2
  44. package/dist/CorrectActivityModal/index.js +7 -7
  45. package/dist/CorrectActivityModal/index.mjs +6 -6
  46. package/dist/DownloadButton/index.js +4 -4
  47. package/dist/DownloadButton/index.mjs +3 -3
  48. package/dist/DownloadModal/index.js +3 -3
  49. package/dist/DownloadModal/index.mjs +2 -2
  50. package/dist/DropdownMenu/index.js +3 -3
  51. package/dist/DropdownMenu/index.mjs +2 -2
  52. package/dist/EnemClassroomStartModal/index.d.ts.map +1 -1
  53. package/dist/EnemClassroomStartModal/index.js +7 -7
  54. package/dist/EnemClassroomStartModal/index.mjs +6 -6
  55. package/dist/EnemMoment/index.js +57 -57
  56. package/dist/EnemMoment/index.mjs +51 -51
  57. package/dist/EnemMomentStudentModal/index.js +50 -50
  58. package/dist/EnemMomentStudentModal/index.mjs +48 -48
  59. package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +49 -49
  60. package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +48 -48
  61. package/dist/ExamPageLayout/index.js +49 -49
  62. package/dist/ExamPageLayout/index.mjs +48 -48
  63. package/dist/Forum/index.js +5 -5
  64. package/dist/Forum/index.mjs +4 -4
  65. package/dist/KnowledgePerformanceCard/index.js +51 -51
  66. package/dist/KnowledgePerformanceCard/index.mjs +50 -50
  67. package/dist/LessonCardPreview/index.js +49 -49
  68. package/dist/LessonCardPreview/index.mjs +48 -48
  69. package/dist/LessonMediaSections/index.js +49 -49
  70. package/dist/LessonMediaSections/index.mjs +48 -48
  71. package/dist/LessonsCatalog/index.js +49 -49
  72. package/dist/LessonsCatalog/index.mjs +48 -48
  73. package/dist/Modal/index.d.ts.map +1 -1
  74. package/dist/Modal/index.js +2 -2
  75. package/dist/Modal/index.mjs +1 -1
  76. package/dist/MultiCheckSelect/index.js +5 -5
  77. package/dist/MultiCheckSelect/index.mjs +4 -4
  78. package/dist/MultiSearchSelect/index.js +3 -3
  79. package/dist/MultiSearchSelect/index.mjs +2 -2
  80. package/dist/MultipleChoice/index.js +3 -3
  81. package/dist/MultipleChoice/index.mjs +2 -2
  82. package/dist/NotificationCard/index.js +4 -4
  83. package/dist/NotificationCard/index.mjs +3 -3
  84. package/dist/PerformanceReportModal/index.js +3 -3
  85. package/dist/PerformanceReportModal/index.mjs +2 -2
  86. package/dist/PrintableUsersTable/index.js +49 -49
  87. package/dist/PrintableUsersTable/index.mjs +48 -48
  88. package/dist/ProgressModal/index.js +3 -3
  89. package/dist/ProgressModal/index.mjs +2 -2
  90. package/dist/QuestionsPerformanceCard/index.js +50 -50
  91. package/dist/QuestionsPerformanceCard/index.mjs +49 -49
  92. package/dist/Quiz/index.js +6 -6
  93. package/dist/Quiz/index.mjs +5 -5
  94. package/dist/RecommendedLessonCreate/index.js +49 -49
  95. package/dist/RecommendedLessonCreate/index.mjs +48 -48
  96. package/dist/RecommendedLessonDetails/index.js +7 -7
  97. package/dist/RecommendedLessonDetails/index.mjs +6 -6
  98. package/dist/RecommendedLessonsHistory/index.js +49 -49
  99. package/dist/RecommendedLessonsHistory/index.mjs +48 -48
  100. package/dist/ReportDetailModal/index.js +4 -4
  101. package/dist/ReportDetailModal/index.mjs +3 -3
  102. package/dist/RichEditor/index.js +5 -5
  103. package/dist/RichEditor/index.mjs +4 -4
  104. package/dist/{RichEditorCore-6MTYSSKS.mjs → RichEditorCore-2S5YUAOI.mjs} +5 -5
  105. package/dist/{RichEditorCore-KDARBEK4.js → RichEditorCore-N7A2445B.js} +16 -16
  106. package/dist/{RichEditorCore-KDARBEK4.js.map → RichEditorCore-N7A2445B.js.map} +1 -1
  107. package/dist/Search/index.js +4 -4
  108. package/dist/Search/index.mjs +3 -3
  109. package/dist/SendActivityModal/SendActivityModal.js +49 -49
  110. package/dist/SendActivityModal/SendActivityModal.mjs +48 -48
  111. package/dist/SendActivityModal/index.js +49 -49
  112. package/dist/SendActivityModal/index.mjs +48 -48
  113. package/dist/SendLessonModal/index.js +49 -49
  114. package/dist/SendLessonModal/index.mjs +48 -48
  115. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +49 -49
  116. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +48 -48
  117. package/dist/SimulatedPerformance/useSimulatedPerformance/index.js +5 -5
  118. package/dist/SimulatedPerformance/useSimulatedPerformance/index.mjs +4 -4
  119. package/dist/SimulatedStudentSimulationsModal/index.js +6 -6
  120. package/dist/SimulatedStudentSimulationsModal/index.mjs +5 -5
  121. package/dist/SimulationsPage/index.js +49 -49
  122. package/dist/SimulationsPage/index.mjs +48 -48
  123. package/dist/StudentLessonProgressModal/index.js +3 -3
  124. package/dist/StudentLessonProgressModal/index.mjs +2 -2
  125. package/dist/StudentPerformanceDetailsModal/index.js +5 -5
  126. package/dist/StudentPerformanceDetailsModal/index.mjs +4 -4
  127. package/dist/Support/TicketModal/index.js +3 -3
  128. package/dist/Support/TicketModal/index.mjs +2 -2
  129. package/dist/Support/index.js +4 -4
  130. package/dist/Support/index.mjs +3 -3
  131. package/dist/SupportChatComposer/index.js +3 -3
  132. package/dist/SupportChatComposer/index.mjs +2 -2
  133. package/dist/SupportChatEvent/index.js +3 -3
  134. package/dist/SupportChatEvent/index.mjs +2 -2
  135. package/dist/SupportChatMessage/index.js +3 -3
  136. package/dist/SupportChatMessage/index.mjs +2 -2
  137. package/dist/SupportChatWidget/index.js +3 -3
  138. package/dist/SupportChatWidget/index.mjs +2 -2
  139. package/dist/SupportConcludeModal/index.js +4 -4
  140. package/dist/SupportConcludeModal/index.mjs +3 -3
  141. package/dist/SupportConversationHeader/index.js +4 -4
  142. package/dist/SupportConversationHeader/index.mjs +3 -3
  143. package/dist/SupportConversationMessages/index.js +5 -5
  144. package/dist/SupportConversationMessages/index.mjs +4 -4
  145. package/dist/SupportTicketConversation/index.js +12 -12
  146. package/dist/SupportTicketConversation/index.mjs +11 -11
  147. package/dist/TableProvider/index.js +49 -49
  148. package/dist/TableProvider/index.mjs +48 -48
  149. package/dist/UnifiedDraftModelPage/index.js +49 -49
  150. package/dist/UnifiedDraftModelPage/index.mjs +48 -48
  151. package/dist/UnifiedHistoryPage/index.js +49 -49
  152. package/dist/UnifiedHistoryPage/index.mjs +48 -48
  153. package/dist/VideoPlayer/index.js +5 -5
  154. package/dist/VideoPlayer/index.mjs +4 -4
  155. package/dist/Whiteboard/index.js +3 -3
  156. package/dist/Whiteboard/index.mjs +2 -2
  157. package/dist/{chunk-B6QOBHZM.mjs → chunk-24BEZK43.mjs} +2 -2
  158. package/dist/{chunk-CYSODZGX.js → chunk-27Y6V3GM.js} +169 -66
  159. package/dist/chunk-27Y6V3GM.js.map +1 -0
  160. package/dist/{chunk-OQTXTDSK.mjs → chunk-2OJ3QSLU.mjs} +3 -3
  161. package/dist/{chunk-VDUO65VK.mjs → chunk-3EGJJDIM.mjs} +2 -2
  162. package/dist/{chunk-VDUO65VK.mjs.map → chunk-3EGJJDIM.mjs.map} +1 -1
  163. package/dist/{chunk-4OUBPEVS.mjs → chunk-3HKLGNUE.mjs} +3 -3
  164. package/dist/{chunk-YA7XLH7D.js → chunk-3MFACEZ2.js} +3 -3
  165. package/dist/{chunk-YA7XLH7D.js.map → chunk-3MFACEZ2.js.map} +1 -1
  166. package/dist/{chunk-IHBWJM4C.mjs → chunk-3Q7RJKUP.mjs} +2 -2
  167. package/dist/{chunk-PY6WD6SB.mjs → chunk-3WRB4WCD.mjs} +4 -4
  168. package/dist/{chunk-4RHCBMMP.js → chunk-46VHAL4S.js} +3 -3
  169. package/dist/{chunk-4RHCBMMP.js.map → chunk-46VHAL4S.js.map} +1 -1
  170. package/dist/{chunk-POT3ALY3.js → chunk-5DINXU6W.js} +1 -1
  171. package/dist/{chunk-POT3ALY3.js.map → chunk-5DINXU6W.js.map} +1 -1
  172. package/dist/{chunk-L52NFNFK.mjs → chunk-5VC4BK65.mjs} +3 -3
  173. package/dist/{chunk-B67RLIUM.js → chunk-72D7G6YQ.js} +3 -3
  174. package/dist/{chunk-B67RLIUM.js.map → chunk-72D7G6YQ.js.map} +1 -1
  175. package/dist/{chunk-C65DG2RI.mjs → chunk-7FSBWUOM.mjs} +2 -2
  176. package/dist/{chunk-QWMFWYII.js → chunk-7IZNLARX.js} +3 -3
  177. package/dist/{chunk-QWMFWYII.js.map → chunk-7IZNLARX.js.map} +1 -1
  178. package/dist/{chunk-ZCBT2BUZ.js → chunk-7RFHXFFV.js} +13 -13
  179. package/dist/{chunk-ZCBT2BUZ.js.map → chunk-7RFHXFFV.js.map} +1 -1
  180. package/dist/{chunk-EMWX6IKP.js → chunk-7VJP5D26.js} +13 -13
  181. package/dist/{chunk-EMWX6IKP.js.map → chunk-7VJP5D26.js.map} +1 -1
  182. package/dist/{chunk-HCQJR5CE.mjs → chunk-7ZWEBNFB.mjs} +2 -2
  183. package/dist/{chunk-3RHNJUXU.mjs → chunk-ACSEJOW6.mjs} +2 -2
  184. package/dist/{chunk-WQZHUW7C.mjs → chunk-ANFHLSDB.mjs} +3 -3
  185. package/dist/{chunk-H6NQOLQY.mjs → chunk-ANFLWCVD.mjs} +2 -2
  186. package/dist/{chunk-BKBDQXZX.js → chunk-AOYPUZRO.js} +3 -3
  187. package/dist/{chunk-BKBDQXZX.js.map → chunk-AOYPUZRO.js.map} +1 -1
  188. package/dist/{chunk-5WS6IENQ.mjs → chunk-AU2HLQDJ.mjs} +2 -2
  189. package/dist/{chunk-P6FX3NT6.mjs → chunk-AZGDHAP5.mjs} +2 -2
  190. package/dist/{chunk-NCGO7FPB.js → chunk-AZKNYREH.js} +8 -8
  191. package/dist/{chunk-NCGO7FPB.js.map → chunk-AZKNYREH.js.map} +1 -1
  192. package/dist/{chunk-R6MTPYYW.js → chunk-AZTBNERQ.js} +3 -3
  193. package/dist/{chunk-R6MTPYYW.js.map → chunk-AZTBNERQ.js.map} +1 -1
  194. package/dist/{chunk-NPB2JTYC.mjs → chunk-BO3QVQ37.mjs} +4 -4
  195. package/dist/{chunk-2WI4Q2I2.mjs → chunk-BPMWRFSI.mjs} +4 -4
  196. package/dist/{chunk-TEW33GLR.mjs → chunk-BXN3VO6I.mjs} +2 -2
  197. package/dist/{chunk-7ZB3YHF2.mjs → chunk-C6LC5LD5.mjs} +1 -1
  198. package/dist/{chunk-7ZB3YHF2.mjs.map → chunk-C6LC5LD5.mjs.map} +1 -1
  199. package/dist/{chunk-5HONWYQA.js → chunk-C7ILTLT5.js} +3 -3
  200. package/dist/{chunk-5HONWYQA.js.map → chunk-C7ILTLT5.js.map} +1 -1
  201. package/dist/{chunk-OES54VU6.mjs → chunk-CFV2OZ3P.mjs} +116 -13
  202. package/dist/chunk-CFV2OZ3P.mjs.map +1 -0
  203. package/dist/{chunk-4OVIHZ3I.js → chunk-CGXWWPEA.js} +6 -6
  204. package/dist/{chunk-4OVIHZ3I.js.map → chunk-CGXWWPEA.js.map} +1 -1
  205. package/dist/{chunk-MNU45FM3.js → chunk-COT4KW3H.js} +2 -2
  206. package/dist/{chunk-MNU45FM3.js.map → chunk-COT4KW3H.js.map} +1 -1
  207. package/dist/{chunk-JDQUXG75.js → chunk-CVDNCU6L.js} +3 -3
  208. package/dist/{chunk-JDQUXG75.js.map → chunk-CVDNCU6L.js.map} +1 -1
  209. package/dist/{chunk-HBZHWSMZ.mjs → chunk-D46PJSQ7.mjs} +13 -13
  210. package/dist/{chunk-ARYIJ4RU.js → chunk-DDORKDQO.js} +3 -3
  211. package/dist/{chunk-ARYIJ4RU.js.map → chunk-DDORKDQO.js.map} +1 -1
  212. package/dist/{chunk-7YQTDJFA.js → chunk-DKXSR2V3.js} +3 -3
  213. package/dist/{chunk-7YQTDJFA.js.map → chunk-DKXSR2V3.js.map} +1 -1
  214. package/dist/{chunk-I6EAPPBL.mjs → chunk-DZMYIOKZ.mjs} +5 -5
  215. package/dist/{chunk-SKM7NFPY.js → chunk-EPXAFJVW.js} +6 -6
  216. package/dist/{chunk-SKM7NFPY.js.map → chunk-EPXAFJVW.js.map} +1 -1
  217. package/dist/{chunk-AYYMS23L.js → chunk-ESIQ2GB4.js} +7 -7
  218. package/dist/{chunk-AYYMS23L.js.map → chunk-ESIQ2GB4.js.map} +1 -1
  219. package/dist/{chunk-NU2WNYOD.js → chunk-EW7HGUFV.js} +87 -87
  220. package/dist/{chunk-NU2WNYOD.js.map → chunk-EW7HGUFV.js.map} +1 -1
  221. package/dist/{chunk-I4QGQMXI.mjs → chunk-EXYXZFNK.mjs} +2 -2
  222. package/dist/{chunk-RBJAO57A.js → chunk-FAAR4XC5.js} +3 -3
  223. package/dist/{chunk-RBJAO57A.js.map → chunk-FAAR4XC5.js.map} +1 -1
  224. package/dist/{chunk-N2VNE3ON.js → chunk-FDTDJDZI.js} +13 -13
  225. package/dist/{chunk-N2VNE3ON.js.map → chunk-FDTDJDZI.js.map} +1 -1
  226. package/dist/{chunk-SU5Q64TW.mjs → chunk-FVCTKS33.mjs} +6 -5
  227. package/dist/chunk-FVCTKS33.mjs.map +1 -0
  228. package/dist/{chunk-AVHZHWI3.js → chunk-GBIFSFWJ.js} +15 -15
  229. package/dist/{chunk-AVHZHWI3.js.map → chunk-GBIFSFWJ.js.map} +1 -1
  230. package/dist/{chunk-KSUPPAKH.js → chunk-GIQSHTB5.js} +9 -9
  231. package/dist/{chunk-KSUPPAKH.js.map → chunk-GIQSHTB5.js.map} +1 -1
  232. package/dist/{chunk-4QBKD4JN.js → chunk-GUUJYGXF.js} +3 -3
  233. package/dist/{chunk-4QBKD4JN.js.map → chunk-GUUJYGXF.js.map} +1 -1
  234. package/dist/{chunk-M4O46VDY.mjs → chunk-GX2PKLN2.mjs} +2 -2
  235. package/dist/{chunk-5BUTINIM.mjs → chunk-HLVLSWKD.mjs} +3 -3
  236. package/dist/{chunk-FU23WOTY.js → chunk-HSV5VZ4Z.js} +6 -6
  237. package/dist/{chunk-FU23WOTY.js.map → chunk-HSV5VZ4Z.js.map} +1 -1
  238. package/dist/{chunk-MMS2ELDK.mjs → chunk-IIQCS3A2.mjs} +2 -2
  239. package/dist/{chunk-NKWLCZUZ.mjs → chunk-JGGUMHM6.mjs} +2 -2
  240. package/dist/{chunk-HT2GREFH.js → chunk-JLD46DL7.js} +11 -11
  241. package/dist/{chunk-HT2GREFH.js.map → chunk-JLD46DL7.js.map} +1 -1
  242. package/dist/{chunk-NVL5TKWD.mjs → chunk-JYFLOQYT.mjs} +2 -2
  243. package/dist/{chunk-DWGDEUEW.mjs → chunk-KG563P7F.mjs} +5 -5
  244. package/dist/{chunk-TCIC5556.mjs → chunk-KH4TEDGD.mjs} +2 -2
  245. package/dist/{chunk-23PAYKWB.js → chunk-KM27ZDHZ.js} +2 -2
  246. package/dist/{chunk-23PAYKWB.js.map → chunk-KM27ZDHZ.js.map} +1 -1
  247. package/dist/{chunk-XZUMD3CB.mjs → chunk-LODDYDTP.mjs} +2 -2
  248. package/dist/{chunk-XZUMD3CB.mjs.map → chunk-LODDYDTP.mjs.map} +1 -1
  249. package/dist/{chunk-E6CGJBID.mjs → chunk-LUHKMY56.mjs} +2 -2
  250. package/dist/{chunk-TVLEUNKE.mjs → chunk-LZP5VZCO.mjs} +2 -2
  251. package/dist/{chunk-EHNEBXS7.mjs → chunk-M6DXZ2FM.mjs} +2 -2
  252. package/dist/{chunk-B4C5DWAP.mjs → chunk-MMMJDO75.mjs} +2 -2
  253. package/dist/{chunk-AFCWMJQO.js → chunk-MOUPNP53.js} +3 -3
  254. package/dist/{chunk-AFCWMJQO.js.map → chunk-MOUPNP53.js.map} +1 -1
  255. package/dist/{chunk-36KFIZ43.js → chunk-MYPUZ6EG.js} +3 -3
  256. package/dist/{chunk-36KFIZ43.js.map → chunk-MYPUZ6EG.js.map} +1 -1
  257. package/dist/{chunk-6JHIKIPA.js → chunk-NUGLTWEA.js} +23 -23
  258. package/dist/{chunk-6JHIKIPA.js.map → chunk-NUGLTWEA.js.map} +1 -1
  259. package/dist/{chunk-KWXOYHYK.mjs → chunk-P7MORXSE.mjs} +2 -2
  260. package/dist/{chunk-YG4IZ5Q3.mjs → chunk-Q2OSEROT.mjs} +2 -2
  261. package/dist/{chunk-L5SXG6T6.mjs → chunk-QQKOHKO2.mjs} +3 -3
  262. package/dist/{chunk-SADSJ2MV.js → chunk-QVGUYPXO.js} +3 -3
  263. package/dist/{chunk-SADSJ2MV.js.map → chunk-QVGUYPXO.js.map} +1 -1
  264. package/dist/{chunk-K45PTE7F.mjs → chunk-RBKQXT73.mjs} +4 -4
  265. package/dist/{chunk-JZFISWTP.js → chunk-RIN2IN7E.js} +2 -2
  266. package/dist/{chunk-JZFISWTP.js.map → chunk-RIN2IN7E.js.map} +1 -1
  267. package/dist/{chunk-BXLAOYAG.mjs → chunk-RIUVPTG7.mjs} +2 -2
  268. package/dist/{chunk-QVB54LBS.mjs → chunk-RNZGX6MX.mjs} +3 -3
  269. package/dist/{chunk-VSVLIRO2.js → chunk-RXEU77VN.js} +15 -15
  270. package/dist/{chunk-VSVLIRO2.js.map → chunk-RXEU77VN.js.map} +1 -1
  271. package/dist/{chunk-ZRIYCU2V.js → chunk-S2BZPPAI.js} +5 -5
  272. package/dist/{chunk-ZRIYCU2V.js.map → chunk-S2BZPPAI.js.map} +1 -1
  273. package/dist/{chunk-MES2BP4T.mjs → chunk-SGL5IUCV.mjs} +2 -2
  274. package/dist/{chunk-7LS5UQCQ.mjs → chunk-STKUPDHG.mjs} +2 -2
  275. package/dist/{chunk-PPYCCT2K.js → chunk-SWDVSCQJ.js} +9 -8
  276. package/dist/chunk-SWDVSCQJ.js.map +1 -0
  277. package/dist/{chunk-JCA76TCX.mjs → chunk-SZKLJOTN.mjs} +2 -2
  278. package/dist/{chunk-F5WR6XZZ.js → chunk-T73HPYF2.js} +3 -3
  279. package/dist/{chunk-F5WR6XZZ.js.map → chunk-T73HPYF2.js.map} +1 -1
  280. package/dist/{chunk-ZB6HDDE4.js → chunk-TQO37PVW.js} +14 -14
  281. package/dist/{chunk-ZB6HDDE4.js.map → chunk-TQO37PVW.js.map} +1 -1
  282. package/dist/{chunk-VKQ5AJDT.mjs → chunk-UENZQF2Y.mjs} +2 -2
  283. package/dist/{chunk-DCG7EUBC.js → chunk-UWLW5YVB.js} +6 -6
  284. package/dist/{chunk-DCG7EUBC.js.map → chunk-UWLW5YVB.js.map} +1 -1
  285. package/dist/{chunk-37AJ2GVO.mjs → chunk-UXJIZDXQ.mjs} +2 -2
  286. package/dist/{chunk-PLLPFCN3.js → chunk-UXMDPRAI.js} +14 -14
  287. package/dist/{chunk-PLLPFCN3.js.map → chunk-UXMDPRAI.js.map} +1 -1
  288. package/dist/{chunk-7BOTKRY6.mjs → chunk-V55DG5B4.mjs} +2 -2
  289. package/dist/{chunk-YRTIDZTN.js → chunk-VHEDMUAW.js} +4 -4
  290. package/dist/{chunk-YRTIDZTN.js.map → chunk-VHEDMUAW.js.map} +1 -1
  291. package/dist/{chunk-4RD55E4R.js → chunk-VWLWSO4J.js} +3 -3
  292. package/dist/{chunk-4RD55E4R.js.map → chunk-VWLWSO4J.js.map} +1 -1
  293. package/dist/{chunk-VJQIKFMG.mjs → chunk-W3CWEZDB.mjs} +3 -3
  294. package/dist/{chunk-JM6SLU7D.js → chunk-W7NNH4VY.js} +6 -6
  295. package/dist/{chunk-JM6SLU7D.js.map → chunk-W7NNH4VY.js.map} +1 -1
  296. package/dist/{chunk-VQ7MSYIH.js → chunk-WUN5NHZE.js} +4 -4
  297. package/dist/{chunk-VQ7MSYIH.js.map → chunk-WUN5NHZE.js.map} +1 -1
  298. package/dist/{chunk-AAU7ABJN.js → chunk-WWTRSQ3X.js} +3 -3
  299. package/dist/{chunk-AAU7ABJN.js.map → chunk-WWTRSQ3X.js.map} +1 -1
  300. package/dist/{chunk-BR6D2TGF.js → chunk-WZOA2PVJ.js} +3 -3
  301. package/dist/{chunk-BR6D2TGF.js.map → chunk-WZOA2PVJ.js.map} +1 -1
  302. package/dist/{chunk-TZKR5Z4J.mjs → chunk-XJF2F36N.mjs} +2 -2
  303. package/dist/{chunk-P6ACVNQK.mjs → chunk-YD2XBEKO.mjs} +2 -2
  304. package/dist/{chunk-LMXWIPGR.js → chunk-YWPLHFAK.js} +3 -3
  305. package/dist/{chunk-LMXWIPGR.js.map → chunk-YWPLHFAK.js.map} +1 -1
  306. package/dist/{chunk-DCEWWHV5.mjs → chunk-Z3HGYPH7.mjs} +2 -2
  307. package/dist/{chunk-Q3PKW6TH.js → chunk-ZCV3HWI2.js} +6 -6
  308. package/dist/{chunk-Q3PKW6TH.js.map → chunk-ZCV3HWI2.js.map} +1 -1
  309. package/dist/{chunk-OSA3YVDZ.mjs → chunk-ZF46DCAC.mjs} +2 -2
  310. package/dist/{chunk-5T6IDXSB.js → chunk-ZO2GA23I.js} +3 -3
  311. package/dist/{chunk-5T6IDXSB.js.map → chunk-ZO2GA23I.js.map} +1 -1
  312. package/dist/{chunk-ZN3763VZ.js → chunk-ZQLW4EJB.js} +9 -9
  313. package/dist/{chunk-ZN3763VZ.js.map → chunk-ZQLW4EJB.js.map} +1 -1
  314. package/dist/{chunk-CM5R4SPF.js → chunk-ZQQJTO6T.js} +3 -3
  315. package/dist/{chunk-CM5R4SPF.js.map → chunk-ZQQJTO6T.js.map} +1 -1
  316. package/dist/{chunk-SEL4QDCO.js → chunk-ZSXSAE3D.js} +3 -3
  317. package/dist/{chunk-SEL4QDCO.js.map → chunk-ZSXSAE3D.js.map} +1 -1
  318. package/dist/components/SupportChatWidget/guestSupportChatAdapter.d.ts +6 -1
  319. package/dist/components/SupportChatWidget/guestSupportChatAdapter.d.ts.map +1 -1
  320. package/dist/hooks/useAppContent/index.js +49 -49
  321. package/dist/hooks/useAppContent/index.mjs +48 -48
  322. package/dist/hooks/useSupportChat.d.ts +2 -0
  323. package/dist/hooks/useSupportChat.d.ts.map +1 -1
  324. package/dist/index.js +49 -49
  325. package/dist/index.mjs +48 -48
  326. package/dist/types/supportChat/index.d.ts +14 -0
  327. package/dist/types/supportChat/index.d.ts.map +1 -1
  328. package/dist/types/supportChat/index.js +2 -2
  329. package/dist/types/supportChat/index.mjs +1 -1
  330. package/dist/types/supportChat.d.ts +14 -0
  331. package/dist/types/supportChat.d.ts.map +1 -1
  332. package/package.json +1 -1
  333. package/dist/chunk-CYSODZGX.js.map +0 -1
  334. package/dist/chunk-OES54VU6.mjs.map +0 -1
  335. package/dist/chunk-PPYCCT2K.js.map +0 -1
  336. package/dist/chunk-SU5Q64TW.mjs.map +0 -1
  337. /package/dist/{RichEditorCore-6MTYSSKS.mjs.map → RichEditorCore-2S5YUAOI.mjs.map} +0 -0
  338. /package/dist/{chunk-B6QOBHZM.mjs.map → chunk-24BEZK43.mjs.map} +0 -0
  339. /package/dist/{chunk-OQTXTDSK.mjs.map → chunk-2OJ3QSLU.mjs.map} +0 -0
  340. /package/dist/{chunk-4OUBPEVS.mjs.map → chunk-3HKLGNUE.mjs.map} +0 -0
  341. /package/dist/{chunk-IHBWJM4C.mjs.map → chunk-3Q7RJKUP.mjs.map} +0 -0
  342. /package/dist/{chunk-PY6WD6SB.mjs.map → chunk-3WRB4WCD.mjs.map} +0 -0
  343. /package/dist/{chunk-L52NFNFK.mjs.map → chunk-5VC4BK65.mjs.map} +0 -0
  344. /package/dist/{chunk-C65DG2RI.mjs.map → chunk-7FSBWUOM.mjs.map} +0 -0
  345. /package/dist/{chunk-HCQJR5CE.mjs.map → chunk-7ZWEBNFB.mjs.map} +0 -0
  346. /package/dist/{chunk-3RHNJUXU.mjs.map → chunk-ACSEJOW6.mjs.map} +0 -0
  347. /package/dist/{chunk-WQZHUW7C.mjs.map → chunk-ANFHLSDB.mjs.map} +0 -0
  348. /package/dist/{chunk-H6NQOLQY.mjs.map → chunk-ANFLWCVD.mjs.map} +0 -0
  349. /package/dist/{chunk-5WS6IENQ.mjs.map → chunk-AU2HLQDJ.mjs.map} +0 -0
  350. /package/dist/{chunk-P6FX3NT6.mjs.map → chunk-AZGDHAP5.mjs.map} +0 -0
  351. /package/dist/{chunk-NPB2JTYC.mjs.map → chunk-BO3QVQ37.mjs.map} +0 -0
  352. /package/dist/{chunk-2WI4Q2I2.mjs.map → chunk-BPMWRFSI.mjs.map} +0 -0
  353. /package/dist/{chunk-TEW33GLR.mjs.map → chunk-BXN3VO6I.mjs.map} +0 -0
  354. /package/dist/{chunk-HBZHWSMZ.mjs.map → chunk-D46PJSQ7.mjs.map} +0 -0
  355. /package/dist/{chunk-I6EAPPBL.mjs.map → chunk-DZMYIOKZ.mjs.map} +0 -0
  356. /package/dist/{chunk-I4QGQMXI.mjs.map → chunk-EXYXZFNK.mjs.map} +0 -0
  357. /package/dist/{chunk-M4O46VDY.mjs.map → chunk-GX2PKLN2.mjs.map} +0 -0
  358. /package/dist/{chunk-5BUTINIM.mjs.map → chunk-HLVLSWKD.mjs.map} +0 -0
  359. /package/dist/{chunk-MMS2ELDK.mjs.map → chunk-IIQCS3A2.mjs.map} +0 -0
  360. /package/dist/{chunk-NKWLCZUZ.mjs.map → chunk-JGGUMHM6.mjs.map} +0 -0
  361. /package/dist/{chunk-NVL5TKWD.mjs.map → chunk-JYFLOQYT.mjs.map} +0 -0
  362. /package/dist/{chunk-DWGDEUEW.mjs.map → chunk-KG563P7F.mjs.map} +0 -0
  363. /package/dist/{chunk-TCIC5556.mjs.map → chunk-KH4TEDGD.mjs.map} +0 -0
  364. /package/dist/{chunk-E6CGJBID.mjs.map → chunk-LUHKMY56.mjs.map} +0 -0
  365. /package/dist/{chunk-TVLEUNKE.mjs.map → chunk-LZP5VZCO.mjs.map} +0 -0
  366. /package/dist/{chunk-EHNEBXS7.mjs.map → chunk-M6DXZ2FM.mjs.map} +0 -0
  367. /package/dist/{chunk-B4C5DWAP.mjs.map → chunk-MMMJDO75.mjs.map} +0 -0
  368. /package/dist/{chunk-KWXOYHYK.mjs.map → chunk-P7MORXSE.mjs.map} +0 -0
  369. /package/dist/{chunk-YG4IZ5Q3.mjs.map → chunk-Q2OSEROT.mjs.map} +0 -0
  370. /package/dist/{chunk-L5SXG6T6.mjs.map → chunk-QQKOHKO2.mjs.map} +0 -0
  371. /package/dist/{chunk-K45PTE7F.mjs.map → chunk-RBKQXT73.mjs.map} +0 -0
  372. /package/dist/{chunk-BXLAOYAG.mjs.map → chunk-RIUVPTG7.mjs.map} +0 -0
  373. /package/dist/{chunk-QVB54LBS.mjs.map → chunk-RNZGX6MX.mjs.map} +0 -0
  374. /package/dist/{chunk-MES2BP4T.mjs.map → chunk-SGL5IUCV.mjs.map} +0 -0
  375. /package/dist/{chunk-7LS5UQCQ.mjs.map → chunk-STKUPDHG.mjs.map} +0 -0
  376. /package/dist/{chunk-JCA76TCX.mjs.map → chunk-SZKLJOTN.mjs.map} +0 -0
  377. /package/dist/{chunk-VKQ5AJDT.mjs.map → chunk-UENZQF2Y.mjs.map} +0 -0
  378. /package/dist/{chunk-37AJ2GVO.mjs.map → chunk-UXJIZDXQ.mjs.map} +0 -0
  379. /package/dist/{chunk-7BOTKRY6.mjs.map → chunk-V55DG5B4.mjs.map} +0 -0
  380. /package/dist/{chunk-VJQIKFMG.mjs.map → chunk-W3CWEZDB.mjs.map} +0 -0
  381. /package/dist/{chunk-TZKR5Z4J.mjs.map → chunk-XJF2F36N.mjs.map} +0 -0
  382. /package/dist/{chunk-P6ACVNQK.mjs.map → chunk-YD2XBEKO.mjs.map} +0 -0
  383. /package/dist/{chunk-DCEWWHV5.mjs.map → chunk-Z3HGYPH7.mjs.map} +0 -0
  384. /package/dist/{chunk-OSA3YVDZ.mjs.map → chunk-ZF46DCAC.mjs.map} +0 -0
@@ -5,10 +5,10 @@
5
5
 
6
6
 
7
7
 
8
- var _chunk7YQTDJFAjs = require('./chunk-7YQTDJFA.js');
8
+ var _chunkDKXSR2V3js = require('./chunk-DKXSR2V3.js');
9
9
 
10
10
 
11
- var _chunkJM6SLU7Djs = require('./chunk-JM6SLU7D.js');
11
+ var _chunkW7NNH4VYjs = require('./chunk-W7NNH4VY.js');
12
12
 
13
13
 
14
14
 
@@ -16,7 +16,7 @@ var _chunkBIKQB5M6js = require('./chunk-BIKQB5M6.js');
16
16
 
17
17
 
18
18
 
19
- var _chunkCM5R4SPFjs = require('./chunk-CM5R4SPF.js');
19
+ var _chunkZQQJTO6Tjs = require('./chunk-ZQQJTO6T.js');
20
20
  require('./chunk-HMIMGXGB.js');
21
21
  require('./chunk-4IASFFQ2.js');
22
22
 
@@ -31,7 +31,7 @@ require('./chunk-PMXNIJKV.js');
31
31
  var _chunkVVO3W2SMjs = require('./chunk-VVO3W2SM.js');
32
32
 
33
33
 
34
- var _chunk23PAYKWBjs = require('./chunk-23PAYKWB.js');
34
+ var _chunkKM27ZDHZjs = require('./chunk-KM27ZDHZ.js');
35
35
  require('./chunk-6V5MWTP5.js');
36
36
  require('./chunk-EBA7URWC.js');
37
37
  require('./chunk-TWT3CSRQ.js');
@@ -91,7 +91,7 @@ var VideoEmbedNode = _core.Node.create({
91
91
  default: null,
92
92
  parseHTML: (element) => {
93
93
  const iframe = findIframe(element);
94
- return iframe ? _chunk7YQTDJFAjs.parseImageWidth.call(void 0, iframe) : null;
94
+ return iframe ? _chunkDKXSR2V3js.parseImageWidth.call(void 0, iframe) : null;
95
95
  },
96
96
  renderHTML: (attributes) => typeof attributes.width === "number" ? { width: attributes.width } : {}
97
97
  }
@@ -174,7 +174,7 @@ var ResizableImage = _extensionimage2.default.extend({
174
174
  ..._optionalChain([this, 'access', _4 => _4.parent, 'optionalCall', _5 => _5()]),
175
175
  width: {
176
176
  default: null,
177
- parseHTML: (element) => _chunk7YQTDJFAjs.parseImageWidth.call(void 0, element),
177
+ parseHTML: (element) => _chunkDKXSR2V3js.parseImageWidth.call(void 0, element),
178
178
  renderHTML: (attributes) => typeof attributes.width === "number" ? { width: attributes.width } : {}
179
179
  },
180
180
  height: {
@@ -221,8 +221,8 @@ function createRichEditorExtensions(placeholder, options = {}) {
221
221
  // Corner handles only: edge handles span the whole side and read as a
222
222
  // border rather than a grip.
223
223
  directions: ["top-left", "top-right", "bottom-left", "bottom-right"],
224
- minWidth: _chunk7YQTDJFAjs.MIN_IMAGE_WIDTH,
225
- minHeight: _chunk7YQTDJFAjs.MIN_IMAGE_HEIGHT,
224
+ minWidth: _chunkDKXSR2V3js.MIN_IMAGE_WIDTH,
225
+ minHeight: _chunkDKXSR2V3js.MIN_IMAGE_HEIGHT,
226
226
  alwaysPreserveAspectRatio: true
227
227
  }
228
228
  }),
@@ -237,7 +237,7 @@ function createRichEditorExtensions(placeholder, options = {}) {
237
237
  // different width. Column layout stays with the display stylesheet.
238
238
  table: { resizable: false }
239
239
  }),
240
- _chunkJM6SLU7Djs.MathNode
240
+ _chunkW7NNH4VYjs.MathNode
241
241
  ];
242
242
  }
243
243
 
@@ -296,7 +296,7 @@ function VideoDialog({ open, onClose, onInsert }) {
296
296
  handleClose();
297
297
  };
298
298
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
299
- _chunk23PAYKWBjs.Modal_default,
299
+ _chunkKM27ZDHZjs.Modal_default,
300
300
  {
301
301
  isOpen: open,
302
302
  onClose: handleClose,
@@ -421,7 +421,7 @@ var namePastedImage = (file) => {
421
421
  type: file.type
422
422
  });
423
423
  };
424
- var isOversized = (file) => file.size > _chunk7YQTDJFAjs.MAX_IMAGE_SIZE;
424
+ var isOversized = (file) => file.size > _chunkDKXSR2V3js.MAX_IMAGE_SIZE;
425
425
  var prefersMarkup = (clipboardData) => {
426
426
  const html = clipboardData.getData("text/html");
427
427
  return html !== "" && !/<img\b/i.test(html);
@@ -493,7 +493,7 @@ var TableActionBtn = ({
493
493
  }
494
494
  );
495
495
  var NEW_TABLE = { rows: 3, cols: 3, withHeaderRow: true };
496
- var prepareContent = (content) => _chunkCM5R4SPFjs.processLatexInHtml.call(void 0, _chunkD4BRJ4ZNjs.normalizeLineBreaksInHtml.call(void 0, content || ""));
496
+ var prepareContent = (content) => _chunkZQQJTO6Tjs.processLatexInHtml.call(void 0, _chunkD4BRJ4ZNjs.normalizeLineBreaksInHtml.call(void 0, content || ""));
497
497
  var OVERSIZED_PASTE_MESSAGE = "A imagem deve ter no m\xE1ximo 5MB.";
498
498
  var GENERIC_PASTE_ERROR = "Erro ao enviar a imagem.";
499
499
  function RichEditor({
@@ -607,7 +607,7 @@ function RichEditor({
607
607
  try {
608
608
  for (const file of files) {
609
609
  const src = await upload(file);
610
- const width = await _chunk7YQTDJFAjs.resolveInsertWidth.call(void 0, src);
610
+ const width = await _chunkDKXSR2V3js.resolveInsertWidth.call(void 0, src);
611
611
  if (token !== pasteTokenRef.current) return;
612
612
  applyImage(src, "", width);
613
613
  }
@@ -984,7 +984,7 @@ function RichEditor({
984
984
  ] })
985
985
  ] }),
986
986
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
987
- _chunkCM5R4SPFjs.FormulaDialog,
987
+ _chunkZQQJTO6Tjs.FormulaDialog,
988
988
  {
989
989
  open: formulaOpen,
990
990
  onClose: () => setFormulaOpen(false),
@@ -1001,7 +1001,7 @@ function RichEditor({
1001
1001
  }
1002
1002
  ),
1003
1003
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1004
- _chunk7YQTDJFAjs.ImageDialog,
1004
+ _chunkDKXSR2V3js.ImageDialog,
1005
1005
  {
1006
1006
  open: imageOpen,
1007
1007
  onClose: () => setImageOpen(false),
@@ -1016,4 +1016,4 @@ function RichEditor({
1016
1016
 
1017
1017
 
1018
1018
  exports.RichEditor = RichEditor;
1019
- //# sourceMappingURL=RichEditorCore-KDARBEK4.js.map
1019
+ //# sourceMappingURL=RichEditorCore-N7A2445B.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/RichEditorCore-KDARBEK4.js","../src/components/RichEditor/RichEditorCore.tsx","../src/components/RichEditor/components/extensions.ts","../src/components/RichEditor/components/VideoEmbedNode.ts","../src/components/RichEditor/components/VideoDialog.tsx","../src/components/RichEditor/components/EmojiPicker.tsx","../src/components/RichEditor/components/pastedImage.ts","../src/utils/htmlColors.ts"],"names":["useEffect","jsx","jsxs","useRef"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/RichEditorCore-KDARBEK4.js","sourcesContent":[null,"import { EditorContent, useEditor, useEditorState } from '@tiptap/react';\nimport { createRichEditorExtensions } from './components/extensions';\nimport {\n normalizeLineBreaksInHtml,\n processLatexInHtml,\n} from './components/utils';\nimport 'katex/dist/katex.min.css';\nimport { TextBolderIcon } from '@phosphor-icons/react/dist/csr/TextB';\nimport { TextItalicIcon } from '@phosphor-icons/react/dist/csr/TextItalic';\nimport { TextUnderlineIcon } from '@phosphor-icons/react/dist/csr/TextUnderline';\nimport { TextStrikethroughIcon } from '@phosphor-icons/react/dist/csr/TextStrikethrough';\nimport { TextAlignLeftIcon } from '@phosphor-icons/react/dist/csr/TextAlignLeft';\nimport { TextAlignCenterIcon } from '@phosphor-icons/react/dist/csr/TextAlignCenter';\nimport { TextAlignRightIcon } from '@phosphor-icons/react/dist/csr/TextAlignRight';\nimport { TextAlignJustifyIcon } from '@phosphor-icons/react/dist/csr/TextAlignJustify';\nimport { LinkIcon } from '@phosphor-icons/react/dist/csr/Link';\nimport { ListBulletsIcon } from '@phosphor-icons/react/dist/csr/ListBullets';\nimport { ListNumbersIcon } from '@phosphor-icons/react/dist/csr/ListNumbers';\nimport { QuotesIcon } from '@phosphor-icons/react/dist/csr/Quotes';\nimport { MinusIcon } from '@phosphor-icons/react/dist/csr/Minus';\nimport { CodeIcon } from '@phosphor-icons/react/dist/csr/Code';\nimport { TextHOneIcon } from '@phosphor-icons/react/dist/csr/TextHOne';\nimport { TextHTwoIcon } from '@phosphor-icons/react/dist/csr/TextHTwo';\nimport { TextHThreeIcon } from '@phosphor-icons/react/dist/csr/TextHThree';\nimport { MathOperationsIcon } from '@phosphor-icons/react/dist/csr/MathOperations';\nimport { ImageIcon } from '@phosphor-icons/react/dist/csr/Image';\nimport { TableIcon } from '@phosphor-icons/react/dist/csr/Table';\nimport { RowsPlusTopIcon } from '@phosphor-icons/react/dist/csr/RowsPlusTop';\nimport { RowsPlusBottomIcon } from '@phosphor-icons/react/dist/csr/RowsPlusBottom';\nimport { ColumnsPlusLeftIcon } from '@phosphor-icons/react/dist/csr/ColumnsPlusLeft';\nimport { ColumnsPlusRightIcon } from '@phosphor-icons/react/dist/csr/ColumnsPlusRight';\nimport { RowsIcon } from '@phosphor-icons/react/dist/csr/Rows';\nimport { ColumnsIcon } from '@phosphor-icons/react/dist/csr/Columns';\nimport { TrashIcon } from '@phosphor-icons/react/dist/csr/Trash';\nimport { VideoIcon } from '@phosphor-icons/react/dist/csr/Video';\nimport { SmileyIcon } from '@phosphor-icons/react/dist/csr/Smiley';\nimport { useState, useRef, useEffect, ReactNode } from 'react';\nimport { FormulaDialog } from './components/FormulaDialog';\nimport { ImageDialog } from './components/ImageDialog';\nimport { VideoDialog } from './components/VideoDialog';\nimport { EmojiPicker } from './components/EmojiPicker';\nimport { createPastedImageHandler } from './components/pastedImage';\nimport { resolveInsertWidth } from './components/imageSize';\nimport { hasColorMarkup } from '../../utils/htmlColors';\nimport Button from '../Button/Button';\nimport Text from '../Text/Text';\n\ninterface ToolbarBtnProps {\n onClick: () => void;\n active?: boolean;\n title: string;\n children: ReactNode;\n}\n\nconst ToolbarBtn = ({ onClick, active, title, children }: ToolbarBtnProps) => (\n <Button\n type=\"button\"\n onClick={onClick}\n title={title}\n size=\"small\"\n className={`bg-transparent border-transparent h-7 w-7 p-0 flex items-center justify-center rounded hover:bg-background-100 ${\n active ? 'bg-background-200 text-primary-700' : 'text-text-700'\n }`}\n >\n {children}\n </Button>\n);\n\nconst Divider = () => <div className=\"w-px h-5 bg-border-200 mx-0.5\" />;\n\ninterface TableActionBtnProps {\n onClick: () => void;\n label: string;\n danger?: boolean;\n children: ReactNode;\n}\n\n/**\n * Ação da barra de tabela. Leva rótulo escrito, e não só ícone: \"linha acima\"\n * e \"coluna à esquerda\" são indistinguíveis em 16px para quem não usa o editor\n * com frequência, e esta barra só aparece de vez em quando — há espaço de sobra.\n */\nconst TableActionBtn = ({\n onClick,\n label,\n danger,\n children,\n}: TableActionBtnProps) => (\n <Button\n type=\"button\"\n onClick={onClick}\n title={label}\n size=\"small\"\n className={`bg-transparent border-transparent h-7 px-2 flex items-center gap-1 rounded text-xs whitespace-nowrap hover:bg-background-100 ${\n danger ? 'text-error-600' : 'text-text-700'\n }`}\n >\n {children}\n {label}\n </Button>\n);\n\n/** Tabela padrão de quem está montando um enunciado: cabeçalho + 2 linhas. */\nconst NEW_TABLE = { rows: 3, cols: 3, withHeaderRow: true };\n\n/**\n * Prepares stored content for the TipTap parser. Line breaks are restored first\n * so that LaTeX spans are injected into already-structured markup.\n */\nconst prepareContent = (content?: string) =>\n processLatexInHtml(normalizeLineBreaksInHtml(content || ''));\n\n/** Shown when a pasted image is bigger than the storage backend accepts. */\nconst OVERSIZED_PASTE_MESSAGE = 'A imagem deve ter no máximo 5MB.';\n\n/** Shown when the upload rejects with something that is not an `Error`. */\nconst GENERIC_PASTE_ERROR = 'Erro ao enviar a imagem.';\n\ninterface RichEditorProps {\n readonly content?: string;\n /**\n * Notificado a cada edição do autor e, na carga, quando o `content` recebido\n * trazia cor: o editor descarta a cor ao parsear e devolve aqui o HTML limpo,\n * para que salvar sem editar nada não regrave a cor. Carregar conteúdo sem cor\n * não dispara nada.\n */\n readonly onChange?: (data: { json: object; html: string }) => void;\n readonly placeholder?: string;\n /**\n * Optional callback to generate LaTeX using AI\n * If provided, the AI generation feature will be enabled in the formula dialog\n * @param description - Natural language description of the formula\n * @returns Promise resolving to the LaTeX string\n */\n readonly onGenerateLatexWithAI?: (description: string) => Promise<string>;\n /**\n * Optional callback to upload an image file and get back its public URL.\n * If provided, the file upload tab is enabled in the image dialog; otherwise\n * images can still be inserted by URL.\n * @param file - The image file selected by the user\n * @returns Promise resolving to the public URL of the uploaded image\n */\n readonly onUploadImage?: (file: File) => Promise<string>;\n /**\n * Enables pasting an image straight from the clipboard (Ctrl+V), uploading it\n * through `onUploadImage` and inserting the resulting URL. Off by default and\n * ignored without `onUploadImage`, since there would be nowhere to put the\n * file — the editor schema rejects base64 sources.\n */\n readonly allowImagePaste?: boolean;\n /**\n * Adds a \"Vídeo\" toolbar button that embeds YouTube/Vimeo players. Off by\n * default: without it, iframes in loaded or pasted HTML are dropped as before.\n */\n readonly enableVideoEmbed?: boolean;\n /** Adds an emoji picker to the toolbar. Off by default. */\n readonly enableEmoji?: boolean;\n}\n\nexport function RichEditor({\n content,\n onChange,\n placeholder = 'Digite aqui...',\n onGenerateLatexWithAI,\n onUploadImage,\n allowImagePaste = false,\n enableVideoEmbed = false,\n enableEmoji = false,\n}: RichEditorProps) {\n const [formulaOpen, setFormulaOpen] = useState(false);\n const [imageOpen, setImageOpen] = useState(false);\n const [videoOpen, setVideoOpen] = useState(false);\n const [emojiOpen, setEmojiOpen] = useState(false);\n const [isPastingImage, setIsPastingImage] = useState(false);\n const [pasteError, setPasteError] = useState('');\n const lastContentRef = useRef(content);\n /**\n * Último `content` recebido cuja cor já foi verificada.\n *\n * Separado do `lastContentRef` — que guarda o HTML que o consumidor tem em\n * mãos, e por isso muda quando devolvemos a versão limpa — para que uma nova\n * execução do efeito com o mesmo conteúdo (o editor sendo recriado, ou um\n * consumidor que ignore o `onChange` e siga mandando o HTML colorido) não\n * notifique a mesma limpeza duas vezes.\n */\n const colorCheckedRef = useRef<string | undefined>(undefined);\n /**\n * `editorProps` is captured once, when `useEditor` runs, so a handler reading\n * the props directly would keep serving the first render's values. The editor\n * calls through this ref, which the effect below repoints whenever the paste\n * configuration changes.\n */\n const pasteHandlerRef = useRef<(event: ClipboardEvent) => boolean>(\n () => false\n );\n /** Bumped on unmount so an upload still in flight knows not to insert. */\n const pasteTokenRef = useRef(0);\n\n const editor = useEditor({\n extensions: createRichEditorExtensions(placeholder, {\n videoEmbed: enableVideoEmbed,\n }),\n content: prepareContent(content),\n // External updates do not reach this callback: the sync effect below calls\n // setContent with `emitUpdate: false`.\n onUpdate: ({ editor }) => {\n const html = editor.getHTML();\n lastContentRef.current = html;\n onChange?.({\n json: editor.getJSON(),\n html,\n });\n },\n editorProps: {\n attributes: {\n class:\n 'min-h-[120px] outline-none prose prose-sm max-w-none px-4 py-3 text-text-950 focus:outline-none',\n },\n handlePaste: (_view, event) => pasteHandlerRef.current(event),\n },\n });\n\n /**\n * Estado reativo da toolbar.\n *\n * No Tiptap 3 o `useEditor` deixou de re-renderizar a cada transação, então\n * ler `editor.isActive(...)` direto no corpo do componente devolve o valor\n * congelado do último render: os botões não acendiam ao mover o cursor e a\n * barra de tabela nunca apareceria ao entrar numa célula. `useEditorState`\n * assina as transações e só re-renderiza quando um destes flags muda — bem\n * mais barato que re-renderizar a toolbar inteira a cada tecla digitada.\n */\n const toolbar = useEditorState({\n editor,\n selector: ({ editor }) => ({\n heading1: editor.isActive('heading', { level: 1 }),\n heading2: editor.isActive('heading', { level: 2 }),\n heading3: editor.isActive('heading', { level: 3 }),\n bold: editor.isActive('bold'),\n italic: editor.isActive('italic'),\n underline: editor.isActive('underline'),\n strike: editor.isActive('strike'),\n subscript: editor.isActive('subscript'),\n superscript: editor.isActive('superscript'),\n code: editor.isActive('code'),\n alignLeft: editor.isActive({ textAlign: 'left' }),\n alignCenter: editor.isActive({ textAlign: 'center' }),\n alignRight: editor.isActive({ textAlign: 'right' }),\n alignJustify: editor.isActive({ textAlign: 'justify' }),\n bulletList: editor.isActive('bulletList'),\n orderedList: editor.isActive('orderedList'),\n blockquote: editor.isActive('blockquote'),\n link: editor.isActive('link'),\n image: editor.isActive('image'),\n table: editor.isActive('table'),\n }),\n });\n\n /**\n * Devolve ao consumidor o HTML já sem cor quando o conteúdo carregado vinha\n * colorido.\n *\n * O schema descarta a cor ao parsear, então a tela aparece limpa sozinha — mas\n * o valor que o consumidor guarda continuaria sendo o HTML colorido que veio\n * da API, e salvar sem editar nada regravaria o vermelho no banco. Emitir aqui\n * alinha os dois: o próximo salvamento persiste a questão sem cor, mesmo que o\n * autor só tenha aberto e clicado em salvar.\n *\n * `emitUpdate: false` na sincronização abaixo é justamente o que torna este\n * passo necessário, e o `onChange` só dispara quando havia cor de fato —\n * qualquer outro conteúdo continua sendo carregado em silêncio, sem reescrever\n * o que já está salvo.\n * @param loaded - O HTML como chegou na prop `content`\n */\n const emitColorlessContent = (loaded: string) => {\n if (!editor || editor.isDestroyed || !hasColorMarkup(loaded)) return;\n\n const html = editor.getHTML();\n lastContentRef.current = html;\n onChange?.({ json: editor.getJSON(), html });\n };\n\n // Update editor content when prop changes externally (e.g., from loadQuestion)\n useEffect(() => {\n // `useEditor` destroys an instance whose component left the screen (e.g.\n // a Suspense boundary hiding and showing the tree) and only hands back the\n // new one on the next render. In between, `editor` is the destroyed\n // instance: its command manager is gone and `commands` would throw.\n if (!editor || editor.isDestroyed || content === undefined) return;\n\n // Na primeira renderização o conteúdo já entrou pelo `useEditor`, então só\n // o que chega depois precisa ser reaplicado.\n if (content !== lastContentRef.current) {\n editor.commands.setContent(prepareContent(content), {\n emitUpdate: false,\n });\n lastContentRef.current = content;\n }\n\n // Uma vez por conteúdo recebido, e não a cada execução do efeito.\n if (content !== colorCheckedRef.current) {\n colorCheckedRef.current = content;\n emitColorlessContent(content);\n }\n // `emitColorlessContent` é recriada a cada render e fica fora das\n // dependências de propósito: só fecha sobre o editor e o `onChange` do\n // consumidor, e reagir a eles reemitiria o mesmo conteúdo.\n }, [content, editor]);\n\n const insertFormula = (latex: string, display: boolean) => {\n if (!latex || !editor) return;\n editor\n .chain()\n .focus()\n .insertContent({\n type: 'mathInline',\n attrs: { latex, display },\n })\n .run();\n setFormulaOpen(false);\n };\n\n const applyImage = (src: string, alt: string, width?: number) => {\n if (!src || !editor) return;\n editor\n .chain()\n .focus()\n .setImage({ src, alt, ...(width ? { width } : {}) })\n .run();\n };\n\n const insertImage = (src: string, alt: string, width?: number) => {\n applyImage(src, alt, width);\n setImageOpen(false);\n };\n\n /**\n * Uploads the images taken from the clipboard and inserts them in order.\n *\n * The upload has to finish before anything reaches the document: a `blob:`\n * source would survive into the saved HTML and break as soon as the page\n * unloads, and base64 is rejected by the schema.\n * @param files - Images already named and within the size limit\n * @param upload - The consumer's upload callback\n */\n const uploadPastedImages = async (\n files: File[],\n upload: (file: File) => Promise<string>\n ) => {\n const token = pasteTokenRef.current;\n setPasteError('');\n setIsPastingImage(true);\n\n try {\n for (const file of files) {\n const src = await upload(file);\n const width = await resolveInsertWidth(src);\n // The editor may be gone by now — the user can navigate away mid-upload.\n if (token !== pasteTokenRef.current) return;\n applyImage(src, '', width);\n }\n } catch (error) {\n if (token !== pasteTokenRef.current) return;\n setPasteError(\n error instanceof Error ? error.message : GENERIC_PASTE_ERROR\n );\n } finally {\n setIsPastingImage(false);\n }\n };\n\n useEffect(() => {\n pasteHandlerRef.current = createPastedImageHandler({\n enabled: allowImagePaste,\n upload: onUploadImage,\n onImages: uploadPastedImages,\n onOversized: () => setPasteError(OVERSIZED_PASTE_MESSAGE),\n });\n // `uploadPastedImages` is rebuilt on every render and is deliberately left\n // out of the dependencies: it only closes over the editor instance and the\n // state setters, all stable once mounted.\n }, [allowImagePaste, onUploadImage, editor]);\n\n useEffect(() => {\n // Invalidates uploads still in flight, so a late resolution cannot insert\n // into an editor that no longer exists.\n return () => {\n pasteTokenRef.current += 1;\n };\n }, []);\n\n const setLink = () => {\n const url = globalThis.window.prompt('URL do link:');\n if (url === null || !editor) return;\n if (url === '') {\n editor.chain().focus().extendMarkRange('link').unsetLink().run();\n return;\n }\n editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run();\n };\n\n // `toolbar` só é nulo junto com o editor, mas o TypeScript não sabe disso.\n if (!editor || editor.isDestroyed || !toolbar) return null;\n\n // `data-analytica-rich-editor` scopes the image resize handle styles shipped\n // in the library's global stylesheet, so they cannot leak into another Tiptap\n // instance living in the consumer app.\n return (\n <div\n data-analytica-rich-editor\n className=\"border border-border-200 rounded-xl overflow-hidden bg-background\"\n >\n {/* Toolbar */}\n <div className=\"flex items-center gap-0.5 border-b border-border-200 bg-background-50 px-2 py-1.5 flex-wrap\">\n {/* Headings */}\n <ToolbarBtn\n onClick={() =>\n editor.chain().focus().toggleHeading({ level: 1 }).run()\n }\n active={toolbar.heading1}\n title=\"Título 1\"\n >\n <TextHOneIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() =>\n editor.chain().focus().toggleHeading({ level: 2 }).run()\n }\n active={toolbar.heading2}\n title=\"Título 2\"\n >\n <TextHTwoIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() =>\n editor.chain().focus().toggleHeading({ level: 3 }).run()\n }\n active={toolbar.heading3}\n title=\"Título 3\"\n >\n <TextHThreeIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Text formatting */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleBold().run()}\n active={toolbar.bold}\n title=\"Negrito (Ctrl+B)\"\n >\n <TextBolderIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleItalic().run()}\n active={toolbar.italic}\n title=\"Itálico (Ctrl+I)\"\n >\n <TextItalicIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n active={toolbar.underline}\n title=\"Sublinhado (Ctrl+U)\"\n >\n <TextUnderlineIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleStrike().run()}\n active={toolbar.strike}\n title=\"Tachado\"\n >\n <TextStrikethroughIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleSubscript().run()}\n active={toolbar.subscript}\n title=\"Subscrito\"\n >\n <span className=\"text-xs font-medium\">\n X<sub className=\"text-[8px]\">2</sub>\n </span>\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleSuperscript().run()}\n active={toolbar.superscript}\n title=\"Sobrescrito\"\n >\n <span className=\"text-xs font-medium\">\n X<sup className=\"text-[8px]\">2</sup>\n </span>\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleCode().run()}\n active={toolbar.code}\n title=\"Código inline\"\n >\n <CodeIcon size={16} />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Alignment */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('left').run()}\n active={toolbar.alignLeft}\n title=\"Alinhar à esquerda\"\n >\n <TextAlignLeftIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('center').run()}\n active={toolbar.alignCenter}\n title=\"Centralizar\"\n >\n <TextAlignCenterIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('right').run()}\n active={toolbar.alignRight}\n title=\"Alinhar à direita\"\n >\n <TextAlignRightIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('justify').run()}\n active={toolbar.alignJustify}\n title=\"Justificar\"\n >\n <TextAlignJustifyIcon size={16} />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Lists */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n active={toolbar.bulletList}\n title=\"Lista com marcadores\"\n >\n <ListBulletsIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n active={toolbar.orderedList}\n title=\"Lista numerada\"\n >\n <ListNumbersIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n active={toolbar.blockquote}\n title=\"Citação\"\n >\n <QuotesIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setHorizontalRule().run()}\n active={false}\n title=\"Linha horizontal\"\n >\n <MinusIcon size={16} />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Link */}\n <ToolbarBtn\n onClick={setLink}\n active={toolbar.link}\n title=\"Inserir link\"\n >\n <LinkIcon size={16} />\n </ToolbarBtn>\n\n {/* Image */}\n <ToolbarBtn\n onClick={() => setImageOpen(true)}\n active={toolbar.image}\n title=\"Inserir imagem\"\n >\n <ImageIcon size={16} />\n </ToolbarBtn>\n\n {enableVideoEmbed && (\n <ToolbarBtn\n onClick={() => setVideoOpen(true)}\n active={false}\n title=\"Inserir vídeo\"\n >\n <VideoIcon size={16} />\n </ToolbarBtn>\n )}\n\n {enableEmoji && (\n <EmojiPicker\n open={emojiOpen}\n onClose={() => setEmojiOpen(false)}\n onSelect={(emoji) =>\n editor.chain().focus().insertContent(emoji).run()\n }\n >\n <ToolbarBtn\n onClick={() => setEmojiOpen((open) => !open)}\n active={emojiOpen}\n title=\"Inserir emoji\"\n >\n <SmileyIcon size={16} />\n </ToolbarBtn>\n </EmojiPicker>\n )}\n\n {/* Table */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().insertTable(NEW_TABLE).run()}\n active={toolbar.table}\n title=\"Inserir tabela\"\n >\n <TableIcon size={16} />\n </ToolbarBtn>\n\n {/* Formula */}\n <Button\n type=\"button\"\n onClick={() => setFormulaOpen(true)}\n title=\"Inserir fórmula LaTeX\"\n size=\"extra-small\"\n variant=\"link\"\n >\n <MathOperationsIcon size={16} />\n LaTeX\n </Button>\n </div>\n\n {/*\n Ações de linha/coluna só existem quando o cursor está dentro de uma\n tabela, então ficam numa barra própria em vez de ocuparem a toolbar\n principal — que já quebra em duas linhas no tablet.\n */}\n {toolbar.table && (\n <div\n data-testid=\"table-toolbar\"\n className=\"flex items-center gap-0.5 border-b border-border-200 bg-background-50 px-2 py-1.5 flex-wrap\"\n >\n <TableActionBtn\n onClick={() => editor.chain().focus().addRowBefore().run()}\n label=\"Linha acima\"\n >\n <RowsPlusTopIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().addRowAfter().run()}\n label=\"Linha abaixo\"\n >\n <RowsPlusBottomIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().addColumnBefore().run()}\n label=\"Coluna à esquerda\"\n >\n <ColumnsPlusLeftIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().addColumnAfter().run()}\n label=\"Coluna à direita\"\n >\n <ColumnsPlusRightIcon size={16} />\n </TableActionBtn>\n\n <Divider />\n\n <TableActionBtn\n onClick={() => editor.chain().focus().toggleHeaderRow().run()}\n label=\"Cabeçalho\"\n >\n <TableIcon size={16} />\n </TableActionBtn>\n\n <Divider />\n\n <TableActionBtn\n onClick={() => editor.chain().focus().deleteRow().run()}\n label=\"Excluir linha\"\n danger\n >\n <RowsIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().deleteColumn().run()}\n label=\"Excluir coluna\"\n danger\n >\n <ColumnsIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().deleteTable().run()}\n label=\"Excluir tabela\"\n danger\n >\n <TrashIcon size={16} />\n </TableActionBtn>\n </div>\n )}\n\n {/* Editor */}\n <EditorContent editor={editor} />\n\n {/* Estado do envio de imagem colada e dica de LaTeX */}\n <div className=\"border-t border-border-200 px-4 py-2 bg-background-50\">\n {isPastingImage && (\n <Text size=\"xs\" color=\"text-text-600\" className=\"block mb-1\">\n Enviando imagem...\n </Text>\n )}\n {pasteError && (\n <Text size=\"xs\" color=\"text-error-600\" className=\"block mb-1\">\n {pasteError}\n </Text>\n )}\n <Text size=\"xs\" color=\"text-text-400\">\n Dica: use{' '}\n <code className=\"bg-background-200 px-1 rounded\">$fórmula$</code> para\n LaTeX inline e{' '}\n <code className=\"bg-background-200 px-1 rounded\">$$fórmula$$</code>{' '}\n para fórmula em bloco. A conversão acontece assim que você fecha os\n cifrões.\n </Text>\n </div>\n\n <FormulaDialog\n open={formulaOpen}\n onClose={() => setFormulaOpen(false)}\n onInsert={insertFormula}\n onGenerateWithAI={onGenerateLatexWithAI}\n />\n\n {enableVideoEmbed && (\n <VideoDialog\n open={videoOpen}\n onClose={() => setVideoOpen(false)}\n onInsert={(src) =>\n editor.chain().focus().setVideoEmbed({ src }).run()\n }\n />\n )}\n\n <ImageDialog\n open={imageOpen}\n onClose={() => setImageOpen(false)}\n onInsert={insertImage}\n onUploadImage={onUploadImage}\n />\n </div>\n );\n}\n","import StarterKit from '@tiptap/starter-kit';\nimport Underline from '@tiptap/extension-underline';\nimport TextAlign from '@tiptap/extension-text-align';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport Link from '@tiptap/extension-link';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Image from '@tiptap/extension-image';\nimport { TableKit } from '@tiptap/extension-table';\nimport { MathNode } from './MathNode';\nimport { VideoEmbedNode } from './VideoEmbedNode';\nimport {\n MIN_IMAGE_HEIGHT,\n MIN_IMAGE_WIDTH,\n parseImageWidth,\n} from './imageSize';\n\n/**\n * Image node with drag-to-resize handles.\n *\n * The stock extension already declares `width`/`height` attributes, but their\n * default parser keeps the raw HTML value. That breaks the resizable node view,\n * which appends `px` to whatever is stored — a legacy `width=\"400px\"` would\n * become `400pxpx`. Parsing to a number up front keeps every source consistent.\n *\n * `height` is deliberately never persisted: the editor stylesheet and\n * HtmlMathRenderer both force `height: auto`, so a stored height exists only to\n * be overridden. Worse, when `max-width` clips a too-wide image while an inline\n * height survives, the node view measures the clipped box and commits a\n * distorted aspect ratio on the next drag.\n */\nconst ResizableImage = Image.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n width: {\n default: null,\n parseHTML: (element: HTMLElement) => parseImageWidth(element),\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.width === 'number'\n ? { width: attributes.width }\n : {},\n },\n height: {\n default: null,\n parseHTML: () => null,\n rendered: false,\n },\n };\n },\n\n addNodeView() {\n const parentNodeView = this.parent?.();\n if (!parentNodeView) return null;\n\n return (props) => {\n const nodeView = parentNodeView(props);\n const container = nodeView.dom;\n\n if (container instanceof HTMLElement) {\n // The upstream node view hides the container until `load` fires. An\n // expired storage URL therefore leaves an invisible, unselectable node —\n // worse than the browser's broken-image icon. Reveal it on failure.\n const image = container.querySelector('img');\n image?.addEventListener('error', () => {\n container.style.visibility = '';\n container.style.pointerEvents = '';\n });\n }\n\n return nodeView;\n };\n },\n});\n\n/**\n * Extension list backing the RichEditor.\n *\n * Tiptap validates incoming HTML against the schema these extensions build and\n * silently discards any node it cannot match. Anything the editor must be able\n * to *load* has to be registered here, not only what it can create — omitting\n * Image is what made `<img>` tags disappear from existing content.\n *\n * That same rule is what keeps colour out of the content, and it is why\n * TextStyle/Color/Highlight are deliberately absent. The toolbar has never\n * offered a colour control, so every coloured run in the question bank arrived\n * pasted from Word, a PDF or an old ENEM export (`<span style=\"color: red\">`,\n * `<mark style=\"background-color: yellow\">`) and then survived into the saved\n * HTML, where it fights the app's own typography. Without a schema attribute\n * to land in, the declaration is dropped on the way in — on paste, on\n * drag-and-drop and when an existing question is loaded for editing — while\n * the text itself is kept. Bold, italic, alignment and the rest are unaffected:\n * they are separate marks.\n */\nexport function createRichEditorExtensions(\n placeholder: string,\n options: { videoEmbed?: boolean } = {}\n) {\n return [\n // Opt-in: without it the schema has no match for `<iframe>`, so editors\n // that never asked for video keep dropping any iframe that is pasted in.\n ...(options.videoEmbed ? [VideoEmbedNode] : []),\n StarterKit,\n Underline,\n Subscript,\n Superscript,\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Link.configure({ openOnClick: false }),\n Placeholder.configure({ placeholder }),\n ResizableImage.configure({\n inline: false,\n allowBase64: false,\n resize: {\n enabled: true,\n // Corner handles only: edge handles span the whole side and read as a\n // border rather than a grip.\n directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],\n minWidth: MIN_IMAGE_WIDTH,\n minHeight: MIN_IMAGE_HEIGHT,\n alwaysPreserveAspectRatio: true,\n },\n }),\n // Registering these nodes is what makes a table survive at all: without\n // them the schema has no match for `<table>`, so every cell collapsed into\n // a single run-on paragraph — and the next save persisted that flattened\n // text over the original table. The toolbar commands (insert, add/remove\n // row and column) come from the same kit.\n TableKit.configure({\n // Resizing is off on purpose: dragging a column persists a `colwidth`\n // measured against the editor viewport, and the student's viewport is a\n // different width. Column layout stays with the display stylesheet.\n table: { resizable: false },\n }),\n MathNode,\n ];\n}\n","import {\n Node,\n mergeAttributes,\n ResizableNodeView,\n type NodeViewRendererProps,\n} from '@tiptap/core';\nimport { isAllowedVideoEmbedSrc } from '../../../utils/videoEmbed';\nimport { parseImageWidth } from './imageSize';\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n videoEmbed: {\n /** Inserts a video player; `src` must already be a player URL */\n setVideoEmbed: (options: { src: string }) => ReturnType;\n };\n }\n}\n\n/** Smallest width a resize handle may produce, in pixels (still a usable player) */\nexport const MIN_VIDEO_WIDTH = 200;\n\n/** Attributes of the player iframe, in the editor and in the saved HTML */\nconst IFRAME_ATTRIBUTES = {\n frameborder: '0',\n allowfullscreen: 'true',\n allow:\n 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture',\n title: 'Vídeo',\n};\n\n/** The iframe of a saved embed: the element itself or the one in its wrapper */\nconst findIframe = (element: HTMLElement): HTMLElement | null =>\n element.tagName === 'IFRAME' ? element : element.querySelector('iframe');\n\n/**\n * Block node holding a YouTube/Vimeo player.\n *\n * Saved as `<div data-video-embed><iframe src=\"…\" width=\"…\"></iframe></div>`.\n * Only players from `VIDEO_EMBED_HOSTS` are parsed back: any other iframe in\n * loaded or pasted HTML is dropped by the schema.\n *\n * Resizable like the images: corner handles change the width and the 16:9\n * box follows. Only the width is stored, on the iframe `width` attribute —\n * it is the one place both sanitizers keep (the backend drops `style` on the\n * wrapper), and ArticleContent sizes the player from it. No width means the\n * player fills the line, as before.\n */\nexport const VideoEmbedNode = Node.create({\n name: 'videoEmbed',\n group: 'block',\n atom: true,\n draggable: true,\n\n addAttributes() {\n return {\n src: { default: null },\n width: {\n default: null,\n parseHTML: (element: HTMLElement) => {\n const iframe = findIframe(element);\n return iframe ? parseImageWidth(iframe) : null;\n },\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.width === 'number'\n ? { width: attributes.width }\n : {},\n },\n };\n },\n\n parseHTML() {\n const read = (element: HTMLElement) => {\n const src = findIframe(element)?.getAttribute('src') ?? null;\n return isAllowedVideoEmbedSrc(src) ? { src } : false;\n };\n return [\n { tag: 'div[data-video-embed]', getAttrs: read },\n { tag: 'iframe', getAttrs: read },\n ];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'div',\n { 'data-video-embed': '', class: 'video-embed' },\n ['iframe', mergeAttributes(HTMLAttributes, IFRAME_ATTRIBUTES)],\n ];\n },\n\n addNodeView() {\n if (typeof document === 'undefined') return null;\n\n return ({ node, getPos, editor }) => {\n const wrapper = document.createElement('div');\n wrapper.className = 'video-embed';\n wrapper.setAttribute('data-video-embed', '');\n const iframe = document.createElement('iframe');\n Object.entries(IFRAME_ATTRIBUTES).forEach(([key, value]) =>\n iframe.setAttribute(key, value)\n );\n wrapper.appendChild(iframe);\n\n /** Shows the stored source and width */\n const apply = (current: NodeViewRendererProps['node']) => {\n if (iframe.getAttribute('src') !== current.attrs.src) {\n iframe.setAttribute('src', current.attrs.src ?? '');\n }\n wrapper.style.width =\n typeof current.attrs.width === 'number'\n ? `${current.attrs.width}px`\n : '';\n };\n apply(node);\n\n return new ResizableNodeView({\n element: wrapper,\n editor,\n node,\n getPos,\n // The height follows from the 16:9 box, so only the width is set\n onResize: (width) => {\n wrapper.style.width = `${width}px`;\n },\n onCommit: (width) => {\n const pos = getPos();\n if (pos === undefined) return;\n editor\n .chain()\n .setNodeSelection(pos)\n .updateAttributes(this.name, { width: Math.round(width) })\n .run();\n },\n onUpdate: (updatedNode) => {\n if (updatedNode.type !== node.type) return false;\n apply(updatedNode);\n return true;\n },\n options: {\n // Same corner handles as the images\n directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],\n min: { width: MIN_VIDEO_WIDTH },\n preserveAspectRatio: true,\n },\n });\n };\n },\n\n addCommands() {\n return {\n setVideoEmbed:\n ({ src }) =>\n ({ commands }) =>\n isAllowedVideoEmbedSrc(src) &&\n commands.insertContent({ type: this.name, attrs: { src } }),\n };\n },\n});\n","import { useState, type ChangeEvent } from 'react';\nimport Button from '../../Button/Button';\nimport Input from '../../Input/Input';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport { toVideoEmbedUrl } from '../../../utils/videoEmbed';\n\ninterface VideoDialogProps {\n readonly open: boolean;\n readonly onClose: () => void;\n /** Receives the player URL (already converted from the pasted link) */\n readonly onInsert: (src: string) => void;\n}\n\nconst INVALID_URL = 'Use um link do YouTube ou do Vimeo.';\n\n/**\n * Asks for a YouTube or Vimeo link and hands back its player URL.\n */\nexport function VideoDialog({ open, onClose, onInsert }: VideoDialogProps) {\n const [url, setUrl] = useState('');\n const [error, setError] = useState('');\n\n const handleClose = () => {\n setUrl('');\n setError('');\n onClose();\n };\n\n const handleInsert = () => {\n const src = toVideoEmbedUrl(url);\n if (!src) {\n setError(INVALID_URL);\n return;\n }\n onInsert(src);\n handleClose();\n };\n\n return (\n <Modal\n isOpen={open}\n onClose={handleClose}\n title=\"Inserir vídeo\"\n size=\"md\"\n footer={\n <>\n <Button variant=\"outline\" onClick={handleClose}>\n Cancelar\n </Button>\n <Button\n variant=\"solid\"\n action=\"primary\"\n onClick={handleInsert}\n disabled={!url.trim()}\n >\n Inserir vídeo\n </Button>\n </>\n }\n >\n <div className=\"flex flex-col gap-2\">\n <Text weight=\"medium\" size=\"xs\" color=\"text-text-600\" className=\"block\">\n Link do vídeo\n </Text>\n <Input\n type=\"url\"\n aria-label=\"Link do vídeo\"\n value={url}\n onChange={(event: ChangeEvent<HTMLInputElement>) => {\n setUrl(event.target.value);\n setError('');\n }}\n placeholder=\"https://www.youtube.com/watch?v=...\"\n />\n <Text size=\"xs\" color=\"text-text-400\">\n Aceita links do YouTube e do Vimeo.\n </Text>\n {error && (\n <Text size=\"xs\" role=\"alert\" color=\"text-error-600\">\n {error}\n </Text>\n )}\n </div>\n </Modal>\n );\n}\n","import { useEffect, useRef, type ReactNode } from 'react';\nimport Button from '../../Button/Button';\n\n/** Emojis offered by the picker, in display order */\nexport const RICH_EDITOR_EMOJIS = [\n '😀 😃 😄 😁 😊 🙂 😉 😍',\n '🤩 😎 🤔 😅 😢 😮 🙏 👏',\n '👍 👎 👋 💪 ✅ ❌ ⚠️ ❗',\n '❓ ℹ️ 💡 📌 📎 📝 📚 📖',\n '🎓 🏫 📅 ⏰ 🔒 🔑 📱 💻',\n '🖥️ 📧 🔗 🎥 ▶️ ⭐ 🎉 🚀',\n]\n .join(' ')\n .split(' ');\n\ninterface EmojiPickerProps {\n readonly open: boolean;\n readonly onClose: () => void;\n readonly onSelect: (emoji: string) => void;\n /** The toolbar button; the popover anchors to it */\n readonly children: ReactNode;\n}\n\n/**\n * Toolbar popover with a grid of common emojis. Closes on pick, on Escape and\n * on a click outside.\n */\nexport function EmojiPicker({\n open,\n onClose,\n onSelect,\n children,\n}: EmojiPickerProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return undefined;\n const handlePointer = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) onClose();\n };\n const handleKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose();\n };\n document.addEventListener('mousedown', handlePointer);\n document.addEventListener('keydown', handleKey);\n return () => {\n document.removeEventListener('mousedown', handlePointer);\n document.removeEventListener('keydown', handleKey);\n };\n }, [open, onClose]);\n\n return (\n <div ref={rootRef} className=\"relative\">\n {children}\n {open && (\n <div\n role=\"dialog\"\n aria-label=\"Emojis\"\n className=\"absolute left-0 top-full z-20 mt-1 grid w-72 grid-cols-8 gap-1 rounded-lg border border-border-200 bg-background p-2 shadow-lg\"\n >\n {RICH_EDITOR_EMOJIS.map((emoji) => (\n <Button\n key={emoji}\n variant=\"raw\"\n aria-label={`Inserir ${emoji}`}\n onClick={() => {\n onSelect(emoji);\n onClose();\n }}\n className=\"flex h-8 w-8 items-center justify-center rounded text-lg hover:bg-background-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indicator-info\"\n >\n {emoji}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Clipboard handling for images pasted straight into the RichEditor.\n *\n * A screenshot on the clipboard arrives as a `File` on the paste event, but the\n * editor schema rejects base64 sources, so the file has to be uploaded before\n * it can be inserted. These helpers isolate the clipboard reading from the\n * upload flow in `RichEditorCore`, which keeps them testable without a real\n * ProseMirror view.\n */\n\nimport { MAX_IMAGE_SIZE } from './imageSize';\n\n/** Base name given to a clipboard image that arrives without a filename. */\nconst PASTED_IMAGE_BASENAME = 'imagem-colada';\n\n/**\n * Extension per MIME type accepted by the storage backend. Keyed by the types a\n * browser actually puts on the clipboard when copying an image.\n */\nconst EXTENSION_BY_MIME: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/bmp': 'bmp',\n 'image/svg+xml': 'svg',\n};\n\n/** Extension used when the clipboard reports an image type we do not map. */\nconst FALLBACK_EXTENSION = 'png';\n\n/**\n * Reads the image files carried by a paste event.\n * Anything that is not an image is left alone so the default paste behaviour\n * still handles it.\n * @param clipboardData - The `clipboardData` of the paste event\n * @returns The image files found, in clipboard order\n */\nexport const extractPastedImageFiles = (\n clipboardData: DataTransfer | null | undefined\n): File[] => {\n if (!clipboardData?.files) return [];\n\n return Array.from(clipboardData.files).filter((file) =>\n file.type.startsWith('image/')\n );\n};\n\n/**\n * Guarantees the file has a name before it reaches the upload.\n * Some browsers hand over a nameless blob for a screenshot, and the pre-signed\n * URL request sends `file.name` to the backend — an empty name would be\n * rejected there.\n * @param file - The image file taken from the clipboard\n * @returns The original file when it is already named, a renamed copy otherwise\n */\nexport const namePastedImage = (file: File): File => {\n if (file.name) return file;\n\n const extension = EXTENSION_BY_MIME[file.type] ?? FALLBACK_EXTENSION;\n return new File([file], `${PASTED_IMAGE_BASENAME}.${extension}`, {\n type: file.type,\n });\n};\n\nconst isOversized = (file: File) => file.size > MAX_IMAGE_SIZE;\n\n/**\n * Whether the clipboard markup should win over the image file it came with.\n *\n * Copying a range from Excel or Google Sheets puts both an HTML table and a\n * picture of it on the clipboard; taking the picture would silently turn a\n * pasteable table into a figure. Markup that carries its own `<img>` is the\n * opposite case — copying a picture from Word sends `<img src=\"file:///...\">`,\n * which would paste as a broken image, so there the file is the good source.\n * @param clipboardData - The `clipboardData` of the paste event\n * @returns True when the default paste handling should keep the markup\n */\nconst prefersMarkup = (clipboardData: DataTransfer) => {\n const html = clipboardData.getData('text/html');\n return html !== '' && !/<img\\b/i.test(html);\n};\n\ninterface PastedImageHandlerOptions {\n /** Whether the consumer opted into pasting images. */\n readonly enabled: boolean;\n /** Upload callback. Without one there is nowhere to put the file. */\n readonly upload?: (file: File) => Promise<string>;\n /** Receives the named images to upload, once the clipboard is accepted. */\n readonly onImages: (\n files: File[],\n upload: (file: File) => Promise<string>\n ) => void;\n /** Called instead of `onImages` when any image is over the size limit. */\n readonly onOversized: () => void;\n}\n\n/**\n * Builds the handler Tiptap calls on every paste.\n *\n * Returning false leaves the paste to the default handling, so text, HTML and\n * anything else keep behaving exactly as before — only a clipboard carrying\n * image files is taken over.\n * @param options - Upload wiring and the callbacks used to report the outcome\n * @returns A handler that reports whether it consumed the paste\n */\nexport const createPastedImageHandler =\n ({ enabled, upload, onImages, onOversized }: PastedImageHandlerOptions) =>\n (event: ClipboardEvent): boolean => {\n if (!enabled || !upload) return false;\n\n const clipboard = event.clipboardData;\n if (!clipboard) return false;\n\n const files = extractPastedImageFiles(clipboard);\n if (files.length === 0 || prefersMarkup(clipboard)) return false;\n\n // From here on the paste belongs to us: the default handler would drop the\n // file anyway, since the editor schema rejects base64 image sources.\n event.preventDefault();\n\n if (files.some(isOversized)) {\n onOversized();\n return true;\n }\n\n onImages(files.map(namePastedImage), upload);\n return true;\n };\n","/**\n * Detecção de cor no HTML das questões.\n *\n * O schema do RichEditor não tem atributo onde guardar cor (ver\n * `components/RichEditor/components/extensions.ts`), então toda declaração de\n * cor é descartada na leitura — o autor abre a questão e ela já aparece sem o\n * vermelho que veio da API. O que o schema não faz é avisar que descartou algo:\n * o valor em memória do formulário continua sendo o HTML colorido, e salvar sem\n * editar nada regravaria a cor no banco.\n *\n * Este módulo é o gatilho que fecha esse buraco. É deliberadamente uma\n * heurística sobre a string: basta reconhecer que havia cor para que o editor\n * devolva ao consumidor o HTML já limpo. Um falso positivo custa um\n * `onChange` extra na carga; um falso negativo só deixa a cor no valor salvo até\n * a próxima edição de verdade.\n */\n\n/** Valores de atributos `style` (aspas duplas ou simples) presentes no HTML. */\nconst STYLE_ATTRIBUTE = /style\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/gi;\n\n/**\n * Propriedades CSS que pintam texto ou fundo, ancoradas no início de uma\n * declaração para não casar com `border-color` e afins. A forma abreviada\n * `background` entra na lista porque é o que Word e Google Docs escrevem com\n * frequência no lugar de `background-color`.\n */\nconst COLOR_DECLARATION =\n /(?:^|;)\\s*(?:color|background|background-color)\\s*:/i;\n\n/**\n * Cor que vive fora do `style`: `<font color>` e `bgcolor` vêm dos exports\n * antigos do ENEM e do que se cola de um PDF; `<mark>` e `data-color` são o\n * grifo que o Highlight do Tiptap gravava junto com a cor de fundo.\n */\nconst COLOR_MARKUP = [\n /<font\\b[^>]*\\bcolor\\s*=/i,\n /<[a-z][^>]*\\bbgcolor\\s*=/i,\n /<mark\\b/i,\n /\\bdata-color\\s*=/i,\n];\n\n/**\n * Se `html` carrega alguma cor que o editor vai descartar ao carregá-lo.\n * @param html - HTML como veio da API, da geração por IA ou de um export antigo\n * @returns True quando há cor de fonte, de fundo ou grifo no conteúdo\n */\nexport function hasColorMarkup(html?: string | null): boolean {\n if (!html) return false;\n\n if (COLOR_MARKUP.some((pattern) => pattern.test(html))) return true;\n\n for (const match of html.matchAll(STYLE_ATTRIBUTE)) {\n if (COLOR_DECLARATION.test(match[1] ?? match[2] ?? '')) return true;\n }\n\n return false;\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/RichEditorCore-N7A2445B.js","../src/components/RichEditor/RichEditorCore.tsx","../src/components/RichEditor/components/extensions.ts","../src/components/RichEditor/components/VideoEmbedNode.ts","../src/components/RichEditor/components/VideoDialog.tsx","../src/components/RichEditor/components/EmojiPicker.tsx","../src/components/RichEditor/components/pastedImage.ts","../src/utils/htmlColors.ts"],"names":["useEffect","jsx","jsxs","useRef"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/RichEditorCore-N7A2445B.js","sourcesContent":[null,"import { EditorContent, useEditor, useEditorState } from '@tiptap/react';\nimport { createRichEditorExtensions } from './components/extensions';\nimport {\n normalizeLineBreaksInHtml,\n processLatexInHtml,\n} from './components/utils';\nimport 'katex/dist/katex.min.css';\nimport { TextBolderIcon } from '@phosphor-icons/react/dist/csr/TextB';\nimport { TextItalicIcon } from '@phosphor-icons/react/dist/csr/TextItalic';\nimport { TextUnderlineIcon } from '@phosphor-icons/react/dist/csr/TextUnderline';\nimport { TextStrikethroughIcon } from '@phosphor-icons/react/dist/csr/TextStrikethrough';\nimport { TextAlignLeftIcon } from '@phosphor-icons/react/dist/csr/TextAlignLeft';\nimport { TextAlignCenterIcon } from '@phosphor-icons/react/dist/csr/TextAlignCenter';\nimport { TextAlignRightIcon } from '@phosphor-icons/react/dist/csr/TextAlignRight';\nimport { TextAlignJustifyIcon } from '@phosphor-icons/react/dist/csr/TextAlignJustify';\nimport { LinkIcon } from '@phosphor-icons/react/dist/csr/Link';\nimport { ListBulletsIcon } from '@phosphor-icons/react/dist/csr/ListBullets';\nimport { ListNumbersIcon } from '@phosphor-icons/react/dist/csr/ListNumbers';\nimport { QuotesIcon } from '@phosphor-icons/react/dist/csr/Quotes';\nimport { MinusIcon } from '@phosphor-icons/react/dist/csr/Minus';\nimport { CodeIcon } from '@phosphor-icons/react/dist/csr/Code';\nimport { TextHOneIcon } from '@phosphor-icons/react/dist/csr/TextHOne';\nimport { TextHTwoIcon } from '@phosphor-icons/react/dist/csr/TextHTwo';\nimport { TextHThreeIcon } from '@phosphor-icons/react/dist/csr/TextHThree';\nimport { MathOperationsIcon } from '@phosphor-icons/react/dist/csr/MathOperations';\nimport { ImageIcon } from '@phosphor-icons/react/dist/csr/Image';\nimport { TableIcon } from '@phosphor-icons/react/dist/csr/Table';\nimport { RowsPlusTopIcon } from '@phosphor-icons/react/dist/csr/RowsPlusTop';\nimport { RowsPlusBottomIcon } from '@phosphor-icons/react/dist/csr/RowsPlusBottom';\nimport { ColumnsPlusLeftIcon } from '@phosphor-icons/react/dist/csr/ColumnsPlusLeft';\nimport { ColumnsPlusRightIcon } from '@phosphor-icons/react/dist/csr/ColumnsPlusRight';\nimport { RowsIcon } from '@phosphor-icons/react/dist/csr/Rows';\nimport { ColumnsIcon } from '@phosphor-icons/react/dist/csr/Columns';\nimport { TrashIcon } from '@phosphor-icons/react/dist/csr/Trash';\nimport { VideoIcon } from '@phosphor-icons/react/dist/csr/Video';\nimport { SmileyIcon } from '@phosphor-icons/react/dist/csr/Smiley';\nimport { useState, useRef, useEffect, ReactNode } from 'react';\nimport { FormulaDialog } from './components/FormulaDialog';\nimport { ImageDialog } from './components/ImageDialog';\nimport { VideoDialog } from './components/VideoDialog';\nimport { EmojiPicker } from './components/EmojiPicker';\nimport { createPastedImageHandler } from './components/pastedImage';\nimport { resolveInsertWidth } from './components/imageSize';\nimport { hasColorMarkup } from '../../utils/htmlColors';\nimport Button from '../Button/Button';\nimport Text from '../Text/Text';\n\ninterface ToolbarBtnProps {\n onClick: () => void;\n active?: boolean;\n title: string;\n children: ReactNode;\n}\n\nconst ToolbarBtn = ({ onClick, active, title, children }: ToolbarBtnProps) => (\n <Button\n type=\"button\"\n onClick={onClick}\n title={title}\n size=\"small\"\n className={`bg-transparent border-transparent h-7 w-7 p-0 flex items-center justify-center rounded hover:bg-background-100 ${\n active ? 'bg-background-200 text-primary-700' : 'text-text-700'\n }`}\n >\n {children}\n </Button>\n);\n\nconst Divider = () => <div className=\"w-px h-5 bg-border-200 mx-0.5\" />;\n\ninterface TableActionBtnProps {\n onClick: () => void;\n label: string;\n danger?: boolean;\n children: ReactNode;\n}\n\n/**\n * Ação da barra de tabela. Leva rótulo escrito, e não só ícone: \"linha acima\"\n * e \"coluna à esquerda\" são indistinguíveis em 16px para quem não usa o editor\n * com frequência, e esta barra só aparece de vez em quando — há espaço de sobra.\n */\nconst TableActionBtn = ({\n onClick,\n label,\n danger,\n children,\n}: TableActionBtnProps) => (\n <Button\n type=\"button\"\n onClick={onClick}\n title={label}\n size=\"small\"\n className={`bg-transparent border-transparent h-7 px-2 flex items-center gap-1 rounded text-xs whitespace-nowrap hover:bg-background-100 ${\n danger ? 'text-error-600' : 'text-text-700'\n }`}\n >\n {children}\n {label}\n </Button>\n);\n\n/** Tabela padrão de quem está montando um enunciado: cabeçalho + 2 linhas. */\nconst NEW_TABLE = { rows: 3, cols: 3, withHeaderRow: true };\n\n/**\n * Prepares stored content for the TipTap parser. Line breaks are restored first\n * so that LaTeX spans are injected into already-structured markup.\n */\nconst prepareContent = (content?: string) =>\n processLatexInHtml(normalizeLineBreaksInHtml(content || ''));\n\n/** Shown when a pasted image is bigger than the storage backend accepts. */\nconst OVERSIZED_PASTE_MESSAGE = 'A imagem deve ter no máximo 5MB.';\n\n/** Shown when the upload rejects with something that is not an `Error`. */\nconst GENERIC_PASTE_ERROR = 'Erro ao enviar a imagem.';\n\ninterface RichEditorProps {\n readonly content?: string;\n /**\n * Notificado a cada edição do autor e, na carga, quando o `content` recebido\n * trazia cor: o editor descarta a cor ao parsear e devolve aqui o HTML limpo,\n * para que salvar sem editar nada não regrave a cor. Carregar conteúdo sem cor\n * não dispara nada.\n */\n readonly onChange?: (data: { json: object; html: string }) => void;\n readonly placeholder?: string;\n /**\n * Optional callback to generate LaTeX using AI\n * If provided, the AI generation feature will be enabled in the formula dialog\n * @param description - Natural language description of the formula\n * @returns Promise resolving to the LaTeX string\n */\n readonly onGenerateLatexWithAI?: (description: string) => Promise<string>;\n /**\n * Optional callback to upload an image file and get back its public URL.\n * If provided, the file upload tab is enabled in the image dialog; otherwise\n * images can still be inserted by URL.\n * @param file - The image file selected by the user\n * @returns Promise resolving to the public URL of the uploaded image\n */\n readonly onUploadImage?: (file: File) => Promise<string>;\n /**\n * Enables pasting an image straight from the clipboard (Ctrl+V), uploading it\n * through `onUploadImage` and inserting the resulting URL. Off by default and\n * ignored without `onUploadImage`, since there would be nowhere to put the\n * file — the editor schema rejects base64 sources.\n */\n readonly allowImagePaste?: boolean;\n /**\n * Adds a \"Vídeo\" toolbar button that embeds YouTube/Vimeo players. Off by\n * default: without it, iframes in loaded or pasted HTML are dropped as before.\n */\n readonly enableVideoEmbed?: boolean;\n /** Adds an emoji picker to the toolbar. Off by default. */\n readonly enableEmoji?: boolean;\n}\n\nexport function RichEditor({\n content,\n onChange,\n placeholder = 'Digite aqui...',\n onGenerateLatexWithAI,\n onUploadImage,\n allowImagePaste = false,\n enableVideoEmbed = false,\n enableEmoji = false,\n}: RichEditorProps) {\n const [formulaOpen, setFormulaOpen] = useState(false);\n const [imageOpen, setImageOpen] = useState(false);\n const [videoOpen, setVideoOpen] = useState(false);\n const [emojiOpen, setEmojiOpen] = useState(false);\n const [isPastingImage, setIsPastingImage] = useState(false);\n const [pasteError, setPasteError] = useState('');\n const lastContentRef = useRef(content);\n /**\n * Último `content` recebido cuja cor já foi verificada.\n *\n * Separado do `lastContentRef` — que guarda o HTML que o consumidor tem em\n * mãos, e por isso muda quando devolvemos a versão limpa — para que uma nova\n * execução do efeito com o mesmo conteúdo (o editor sendo recriado, ou um\n * consumidor que ignore o `onChange` e siga mandando o HTML colorido) não\n * notifique a mesma limpeza duas vezes.\n */\n const colorCheckedRef = useRef<string | undefined>(undefined);\n /**\n * `editorProps` is captured once, when `useEditor` runs, so a handler reading\n * the props directly would keep serving the first render's values. The editor\n * calls through this ref, which the effect below repoints whenever the paste\n * configuration changes.\n */\n const pasteHandlerRef = useRef<(event: ClipboardEvent) => boolean>(\n () => false\n );\n /** Bumped on unmount so an upload still in flight knows not to insert. */\n const pasteTokenRef = useRef(0);\n\n const editor = useEditor({\n extensions: createRichEditorExtensions(placeholder, {\n videoEmbed: enableVideoEmbed,\n }),\n content: prepareContent(content),\n // External updates do not reach this callback: the sync effect below calls\n // setContent with `emitUpdate: false`.\n onUpdate: ({ editor }) => {\n const html = editor.getHTML();\n lastContentRef.current = html;\n onChange?.({\n json: editor.getJSON(),\n html,\n });\n },\n editorProps: {\n attributes: {\n class:\n 'min-h-[120px] outline-none prose prose-sm max-w-none px-4 py-3 text-text-950 focus:outline-none',\n },\n handlePaste: (_view, event) => pasteHandlerRef.current(event),\n },\n });\n\n /**\n * Estado reativo da toolbar.\n *\n * No Tiptap 3 o `useEditor` deixou de re-renderizar a cada transação, então\n * ler `editor.isActive(...)` direto no corpo do componente devolve o valor\n * congelado do último render: os botões não acendiam ao mover o cursor e a\n * barra de tabela nunca apareceria ao entrar numa célula. `useEditorState`\n * assina as transações e só re-renderiza quando um destes flags muda — bem\n * mais barato que re-renderizar a toolbar inteira a cada tecla digitada.\n */\n const toolbar = useEditorState({\n editor,\n selector: ({ editor }) => ({\n heading1: editor.isActive('heading', { level: 1 }),\n heading2: editor.isActive('heading', { level: 2 }),\n heading3: editor.isActive('heading', { level: 3 }),\n bold: editor.isActive('bold'),\n italic: editor.isActive('italic'),\n underline: editor.isActive('underline'),\n strike: editor.isActive('strike'),\n subscript: editor.isActive('subscript'),\n superscript: editor.isActive('superscript'),\n code: editor.isActive('code'),\n alignLeft: editor.isActive({ textAlign: 'left' }),\n alignCenter: editor.isActive({ textAlign: 'center' }),\n alignRight: editor.isActive({ textAlign: 'right' }),\n alignJustify: editor.isActive({ textAlign: 'justify' }),\n bulletList: editor.isActive('bulletList'),\n orderedList: editor.isActive('orderedList'),\n blockquote: editor.isActive('blockquote'),\n link: editor.isActive('link'),\n image: editor.isActive('image'),\n table: editor.isActive('table'),\n }),\n });\n\n /**\n * Devolve ao consumidor o HTML já sem cor quando o conteúdo carregado vinha\n * colorido.\n *\n * O schema descarta a cor ao parsear, então a tela aparece limpa sozinha — mas\n * o valor que o consumidor guarda continuaria sendo o HTML colorido que veio\n * da API, e salvar sem editar nada regravaria o vermelho no banco. Emitir aqui\n * alinha os dois: o próximo salvamento persiste a questão sem cor, mesmo que o\n * autor só tenha aberto e clicado em salvar.\n *\n * `emitUpdate: false` na sincronização abaixo é justamente o que torna este\n * passo necessário, e o `onChange` só dispara quando havia cor de fato —\n * qualquer outro conteúdo continua sendo carregado em silêncio, sem reescrever\n * o que já está salvo.\n * @param loaded - O HTML como chegou na prop `content`\n */\n const emitColorlessContent = (loaded: string) => {\n if (!editor || editor.isDestroyed || !hasColorMarkup(loaded)) return;\n\n const html = editor.getHTML();\n lastContentRef.current = html;\n onChange?.({ json: editor.getJSON(), html });\n };\n\n // Update editor content when prop changes externally (e.g., from loadQuestion)\n useEffect(() => {\n // `useEditor` destroys an instance whose component left the screen (e.g.\n // a Suspense boundary hiding and showing the tree) and only hands back the\n // new one on the next render. In between, `editor` is the destroyed\n // instance: its command manager is gone and `commands` would throw.\n if (!editor || editor.isDestroyed || content === undefined) return;\n\n // Na primeira renderização o conteúdo já entrou pelo `useEditor`, então só\n // o que chega depois precisa ser reaplicado.\n if (content !== lastContentRef.current) {\n editor.commands.setContent(prepareContent(content), {\n emitUpdate: false,\n });\n lastContentRef.current = content;\n }\n\n // Uma vez por conteúdo recebido, e não a cada execução do efeito.\n if (content !== colorCheckedRef.current) {\n colorCheckedRef.current = content;\n emitColorlessContent(content);\n }\n // `emitColorlessContent` é recriada a cada render e fica fora das\n // dependências de propósito: só fecha sobre o editor e o `onChange` do\n // consumidor, e reagir a eles reemitiria o mesmo conteúdo.\n }, [content, editor]);\n\n const insertFormula = (latex: string, display: boolean) => {\n if (!latex || !editor) return;\n editor\n .chain()\n .focus()\n .insertContent({\n type: 'mathInline',\n attrs: { latex, display },\n })\n .run();\n setFormulaOpen(false);\n };\n\n const applyImage = (src: string, alt: string, width?: number) => {\n if (!src || !editor) return;\n editor\n .chain()\n .focus()\n .setImage({ src, alt, ...(width ? { width } : {}) })\n .run();\n };\n\n const insertImage = (src: string, alt: string, width?: number) => {\n applyImage(src, alt, width);\n setImageOpen(false);\n };\n\n /**\n * Uploads the images taken from the clipboard and inserts them in order.\n *\n * The upload has to finish before anything reaches the document: a `blob:`\n * source would survive into the saved HTML and break as soon as the page\n * unloads, and base64 is rejected by the schema.\n * @param files - Images already named and within the size limit\n * @param upload - The consumer's upload callback\n */\n const uploadPastedImages = async (\n files: File[],\n upload: (file: File) => Promise<string>\n ) => {\n const token = pasteTokenRef.current;\n setPasteError('');\n setIsPastingImage(true);\n\n try {\n for (const file of files) {\n const src = await upload(file);\n const width = await resolveInsertWidth(src);\n // The editor may be gone by now — the user can navigate away mid-upload.\n if (token !== pasteTokenRef.current) return;\n applyImage(src, '', width);\n }\n } catch (error) {\n if (token !== pasteTokenRef.current) return;\n setPasteError(\n error instanceof Error ? error.message : GENERIC_PASTE_ERROR\n );\n } finally {\n setIsPastingImage(false);\n }\n };\n\n useEffect(() => {\n pasteHandlerRef.current = createPastedImageHandler({\n enabled: allowImagePaste,\n upload: onUploadImage,\n onImages: uploadPastedImages,\n onOversized: () => setPasteError(OVERSIZED_PASTE_MESSAGE),\n });\n // `uploadPastedImages` is rebuilt on every render and is deliberately left\n // out of the dependencies: it only closes over the editor instance and the\n // state setters, all stable once mounted.\n }, [allowImagePaste, onUploadImage, editor]);\n\n useEffect(() => {\n // Invalidates uploads still in flight, so a late resolution cannot insert\n // into an editor that no longer exists.\n return () => {\n pasteTokenRef.current += 1;\n };\n }, []);\n\n const setLink = () => {\n const url = globalThis.window.prompt('URL do link:');\n if (url === null || !editor) return;\n if (url === '') {\n editor.chain().focus().extendMarkRange('link').unsetLink().run();\n return;\n }\n editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run();\n };\n\n // `toolbar` só é nulo junto com o editor, mas o TypeScript não sabe disso.\n if (!editor || editor.isDestroyed || !toolbar) return null;\n\n // `data-analytica-rich-editor` scopes the image resize handle styles shipped\n // in the library's global stylesheet, so they cannot leak into another Tiptap\n // instance living in the consumer app.\n return (\n <div\n data-analytica-rich-editor\n className=\"border border-border-200 rounded-xl overflow-hidden bg-background\"\n >\n {/* Toolbar */}\n <div className=\"flex items-center gap-0.5 border-b border-border-200 bg-background-50 px-2 py-1.5 flex-wrap\">\n {/* Headings */}\n <ToolbarBtn\n onClick={() =>\n editor.chain().focus().toggleHeading({ level: 1 }).run()\n }\n active={toolbar.heading1}\n title=\"Título 1\"\n >\n <TextHOneIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() =>\n editor.chain().focus().toggleHeading({ level: 2 }).run()\n }\n active={toolbar.heading2}\n title=\"Título 2\"\n >\n <TextHTwoIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() =>\n editor.chain().focus().toggleHeading({ level: 3 }).run()\n }\n active={toolbar.heading3}\n title=\"Título 3\"\n >\n <TextHThreeIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Text formatting */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleBold().run()}\n active={toolbar.bold}\n title=\"Negrito (Ctrl+B)\"\n >\n <TextBolderIcon size={16} weight=\"bold\" />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleItalic().run()}\n active={toolbar.italic}\n title=\"Itálico (Ctrl+I)\"\n >\n <TextItalicIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n active={toolbar.underline}\n title=\"Sublinhado (Ctrl+U)\"\n >\n <TextUnderlineIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleStrike().run()}\n active={toolbar.strike}\n title=\"Tachado\"\n >\n <TextStrikethroughIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleSubscript().run()}\n active={toolbar.subscript}\n title=\"Subscrito\"\n >\n <span className=\"text-xs font-medium\">\n X<sub className=\"text-[8px]\">2</sub>\n </span>\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleSuperscript().run()}\n active={toolbar.superscript}\n title=\"Sobrescrito\"\n >\n <span className=\"text-xs font-medium\">\n X<sup className=\"text-[8px]\">2</sup>\n </span>\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleCode().run()}\n active={toolbar.code}\n title=\"Código inline\"\n >\n <CodeIcon size={16} />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Alignment */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('left').run()}\n active={toolbar.alignLeft}\n title=\"Alinhar à esquerda\"\n >\n <TextAlignLeftIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('center').run()}\n active={toolbar.alignCenter}\n title=\"Centralizar\"\n >\n <TextAlignCenterIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('right').run()}\n active={toolbar.alignRight}\n title=\"Alinhar à direita\"\n >\n <TextAlignRightIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setTextAlign('justify').run()}\n active={toolbar.alignJustify}\n title=\"Justificar\"\n >\n <TextAlignJustifyIcon size={16} />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Lists */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n active={toolbar.bulletList}\n title=\"Lista com marcadores\"\n >\n <ListBulletsIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n active={toolbar.orderedList}\n title=\"Lista numerada\"\n >\n <ListNumbersIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n active={toolbar.blockquote}\n title=\"Citação\"\n >\n <QuotesIcon size={16} />\n </ToolbarBtn>\n <ToolbarBtn\n onClick={() => editor.chain().focus().setHorizontalRule().run()}\n active={false}\n title=\"Linha horizontal\"\n >\n <MinusIcon size={16} />\n </ToolbarBtn>\n\n <Divider />\n\n {/* Link */}\n <ToolbarBtn\n onClick={setLink}\n active={toolbar.link}\n title=\"Inserir link\"\n >\n <LinkIcon size={16} />\n </ToolbarBtn>\n\n {/* Image */}\n <ToolbarBtn\n onClick={() => setImageOpen(true)}\n active={toolbar.image}\n title=\"Inserir imagem\"\n >\n <ImageIcon size={16} />\n </ToolbarBtn>\n\n {enableVideoEmbed && (\n <ToolbarBtn\n onClick={() => setVideoOpen(true)}\n active={false}\n title=\"Inserir vídeo\"\n >\n <VideoIcon size={16} />\n </ToolbarBtn>\n )}\n\n {enableEmoji && (\n <EmojiPicker\n open={emojiOpen}\n onClose={() => setEmojiOpen(false)}\n onSelect={(emoji) =>\n editor.chain().focus().insertContent(emoji).run()\n }\n >\n <ToolbarBtn\n onClick={() => setEmojiOpen((open) => !open)}\n active={emojiOpen}\n title=\"Inserir emoji\"\n >\n <SmileyIcon size={16} />\n </ToolbarBtn>\n </EmojiPicker>\n )}\n\n {/* Table */}\n <ToolbarBtn\n onClick={() => editor.chain().focus().insertTable(NEW_TABLE).run()}\n active={toolbar.table}\n title=\"Inserir tabela\"\n >\n <TableIcon size={16} />\n </ToolbarBtn>\n\n {/* Formula */}\n <Button\n type=\"button\"\n onClick={() => setFormulaOpen(true)}\n title=\"Inserir fórmula LaTeX\"\n size=\"extra-small\"\n variant=\"link\"\n >\n <MathOperationsIcon size={16} />\n LaTeX\n </Button>\n </div>\n\n {/*\n Ações de linha/coluna só existem quando o cursor está dentro de uma\n tabela, então ficam numa barra própria em vez de ocuparem a toolbar\n principal — que já quebra em duas linhas no tablet.\n */}\n {toolbar.table && (\n <div\n data-testid=\"table-toolbar\"\n className=\"flex items-center gap-0.5 border-b border-border-200 bg-background-50 px-2 py-1.5 flex-wrap\"\n >\n <TableActionBtn\n onClick={() => editor.chain().focus().addRowBefore().run()}\n label=\"Linha acima\"\n >\n <RowsPlusTopIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().addRowAfter().run()}\n label=\"Linha abaixo\"\n >\n <RowsPlusBottomIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().addColumnBefore().run()}\n label=\"Coluna à esquerda\"\n >\n <ColumnsPlusLeftIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().addColumnAfter().run()}\n label=\"Coluna à direita\"\n >\n <ColumnsPlusRightIcon size={16} />\n </TableActionBtn>\n\n <Divider />\n\n <TableActionBtn\n onClick={() => editor.chain().focus().toggleHeaderRow().run()}\n label=\"Cabeçalho\"\n >\n <TableIcon size={16} />\n </TableActionBtn>\n\n <Divider />\n\n <TableActionBtn\n onClick={() => editor.chain().focus().deleteRow().run()}\n label=\"Excluir linha\"\n danger\n >\n <RowsIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().deleteColumn().run()}\n label=\"Excluir coluna\"\n danger\n >\n <ColumnsIcon size={16} />\n </TableActionBtn>\n <TableActionBtn\n onClick={() => editor.chain().focus().deleteTable().run()}\n label=\"Excluir tabela\"\n danger\n >\n <TrashIcon size={16} />\n </TableActionBtn>\n </div>\n )}\n\n {/* Editor */}\n <EditorContent editor={editor} />\n\n {/* Estado do envio de imagem colada e dica de LaTeX */}\n <div className=\"border-t border-border-200 px-4 py-2 bg-background-50\">\n {isPastingImage && (\n <Text size=\"xs\" color=\"text-text-600\" className=\"block mb-1\">\n Enviando imagem...\n </Text>\n )}\n {pasteError && (\n <Text size=\"xs\" color=\"text-error-600\" className=\"block mb-1\">\n {pasteError}\n </Text>\n )}\n <Text size=\"xs\" color=\"text-text-400\">\n Dica: use{' '}\n <code className=\"bg-background-200 px-1 rounded\">$fórmula$</code> para\n LaTeX inline e{' '}\n <code className=\"bg-background-200 px-1 rounded\">$$fórmula$$</code>{' '}\n para fórmula em bloco. A conversão acontece assim que você fecha os\n cifrões.\n </Text>\n </div>\n\n <FormulaDialog\n open={formulaOpen}\n onClose={() => setFormulaOpen(false)}\n onInsert={insertFormula}\n onGenerateWithAI={onGenerateLatexWithAI}\n />\n\n {enableVideoEmbed && (\n <VideoDialog\n open={videoOpen}\n onClose={() => setVideoOpen(false)}\n onInsert={(src) =>\n editor.chain().focus().setVideoEmbed({ src }).run()\n }\n />\n )}\n\n <ImageDialog\n open={imageOpen}\n onClose={() => setImageOpen(false)}\n onInsert={insertImage}\n onUploadImage={onUploadImage}\n />\n </div>\n );\n}\n","import StarterKit from '@tiptap/starter-kit';\nimport Underline from '@tiptap/extension-underline';\nimport TextAlign from '@tiptap/extension-text-align';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport Link from '@tiptap/extension-link';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Image from '@tiptap/extension-image';\nimport { TableKit } from '@tiptap/extension-table';\nimport { MathNode } from './MathNode';\nimport { VideoEmbedNode } from './VideoEmbedNode';\nimport {\n MIN_IMAGE_HEIGHT,\n MIN_IMAGE_WIDTH,\n parseImageWidth,\n} from './imageSize';\n\n/**\n * Image node with drag-to-resize handles.\n *\n * The stock extension already declares `width`/`height` attributes, but their\n * default parser keeps the raw HTML value. That breaks the resizable node view,\n * which appends `px` to whatever is stored — a legacy `width=\"400px\"` would\n * become `400pxpx`. Parsing to a number up front keeps every source consistent.\n *\n * `height` is deliberately never persisted: the editor stylesheet and\n * HtmlMathRenderer both force `height: auto`, so a stored height exists only to\n * be overridden. Worse, when `max-width` clips a too-wide image while an inline\n * height survives, the node view measures the clipped box and commits a\n * distorted aspect ratio on the next drag.\n */\nconst ResizableImage = Image.extend({\n addAttributes() {\n return {\n ...this.parent?.(),\n width: {\n default: null,\n parseHTML: (element: HTMLElement) => parseImageWidth(element),\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.width === 'number'\n ? { width: attributes.width }\n : {},\n },\n height: {\n default: null,\n parseHTML: () => null,\n rendered: false,\n },\n };\n },\n\n addNodeView() {\n const parentNodeView = this.parent?.();\n if (!parentNodeView) return null;\n\n return (props) => {\n const nodeView = parentNodeView(props);\n const container = nodeView.dom;\n\n if (container instanceof HTMLElement) {\n // The upstream node view hides the container until `load` fires. An\n // expired storage URL therefore leaves an invisible, unselectable node —\n // worse than the browser's broken-image icon. Reveal it on failure.\n const image = container.querySelector('img');\n image?.addEventListener('error', () => {\n container.style.visibility = '';\n container.style.pointerEvents = '';\n });\n }\n\n return nodeView;\n };\n },\n});\n\n/**\n * Extension list backing the RichEditor.\n *\n * Tiptap validates incoming HTML against the schema these extensions build and\n * silently discards any node it cannot match. Anything the editor must be able\n * to *load* has to be registered here, not only what it can create — omitting\n * Image is what made `<img>` tags disappear from existing content.\n *\n * That same rule is what keeps colour out of the content, and it is why\n * TextStyle/Color/Highlight are deliberately absent. The toolbar has never\n * offered a colour control, so every coloured run in the question bank arrived\n * pasted from Word, a PDF or an old ENEM export (`<span style=\"color: red\">`,\n * `<mark style=\"background-color: yellow\">`) and then survived into the saved\n * HTML, where it fights the app's own typography. Without a schema attribute\n * to land in, the declaration is dropped on the way in — on paste, on\n * drag-and-drop and when an existing question is loaded for editing — while\n * the text itself is kept. Bold, italic, alignment and the rest are unaffected:\n * they are separate marks.\n */\nexport function createRichEditorExtensions(\n placeholder: string,\n options: { videoEmbed?: boolean } = {}\n) {\n return [\n // Opt-in: without it the schema has no match for `<iframe>`, so editors\n // that never asked for video keep dropping any iframe that is pasted in.\n ...(options.videoEmbed ? [VideoEmbedNode] : []),\n StarterKit,\n Underline,\n Subscript,\n Superscript,\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Link.configure({ openOnClick: false }),\n Placeholder.configure({ placeholder }),\n ResizableImage.configure({\n inline: false,\n allowBase64: false,\n resize: {\n enabled: true,\n // Corner handles only: edge handles span the whole side and read as a\n // border rather than a grip.\n directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],\n minWidth: MIN_IMAGE_WIDTH,\n minHeight: MIN_IMAGE_HEIGHT,\n alwaysPreserveAspectRatio: true,\n },\n }),\n // Registering these nodes is what makes a table survive at all: without\n // them the schema has no match for `<table>`, so every cell collapsed into\n // a single run-on paragraph — and the next save persisted that flattened\n // text over the original table. The toolbar commands (insert, add/remove\n // row and column) come from the same kit.\n TableKit.configure({\n // Resizing is off on purpose: dragging a column persists a `colwidth`\n // measured against the editor viewport, and the student's viewport is a\n // different width. Column layout stays with the display stylesheet.\n table: { resizable: false },\n }),\n MathNode,\n ];\n}\n","import {\n Node,\n mergeAttributes,\n ResizableNodeView,\n type NodeViewRendererProps,\n} from '@tiptap/core';\nimport { isAllowedVideoEmbedSrc } from '../../../utils/videoEmbed';\nimport { parseImageWidth } from './imageSize';\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n videoEmbed: {\n /** Inserts a video player; `src` must already be a player URL */\n setVideoEmbed: (options: { src: string }) => ReturnType;\n };\n }\n}\n\n/** Smallest width a resize handle may produce, in pixels (still a usable player) */\nexport const MIN_VIDEO_WIDTH = 200;\n\n/** Attributes of the player iframe, in the editor and in the saved HTML */\nconst IFRAME_ATTRIBUTES = {\n frameborder: '0',\n allowfullscreen: 'true',\n allow:\n 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture',\n title: 'Vídeo',\n};\n\n/** The iframe of a saved embed: the element itself or the one in its wrapper */\nconst findIframe = (element: HTMLElement): HTMLElement | null =>\n element.tagName === 'IFRAME' ? element : element.querySelector('iframe');\n\n/**\n * Block node holding a YouTube/Vimeo player.\n *\n * Saved as `<div data-video-embed><iframe src=\"…\" width=\"…\"></iframe></div>`.\n * Only players from `VIDEO_EMBED_HOSTS` are parsed back: any other iframe in\n * loaded or pasted HTML is dropped by the schema.\n *\n * Resizable like the images: corner handles change the width and the 16:9\n * box follows. Only the width is stored, on the iframe `width` attribute —\n * it is the one place both sanitizers keep (the backend drops `style` on the\n * wrapper), and ArticleContent sizes the player from it. No width means the\n * player fills the line, as before.\n */\nexport const VideoEmbedNode = Node.create({\n name: 'videoEmbed',\n group: 'block',\n atom: true,\n draggable: true,\n\n addAttributes() {\n return {\n src: { default: null },\n width: {\n default: null,\n parseHTML: (element: HTMLElement) => {\n const iframe = findIframe(element);\n return iframe ? parseImageWidth(iframe) : null;\n },\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.width === 'number'\n ? { width: attributes.width }\n : {},\n },\n };\n },\n\n parseHTML() {\n const read = (element: HTMLElement) => {\n const src = findIframe(element)?.getAttribute('src') ?? null;\n return isAllowedVideoEmbedSrc(src) ? { src } : false;\n };\n return [\n { tag: 'div[data-video-embed]', getAttrs: read },\n { tag: 'iframe', getAttrs: read },\n ];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'div',\n { 'data-video-embed': '', class: 'video-embed' },\n ['iframe', mergeAttributes(HTMLAttributes, IFRAME_ATTRIBUTES)],\n ];\n },\n\n addNodeView() {\n if (typeof document === 'undefined') return null;\n\n return ({ node, getPos, editor }) => {\n const wrapper = document.createElement('div');\n wrapper.className = 'video-embed';\n wrapper.setAttribute('data-video-embed', '');\n const iframe = document.createElement('iframe');\n Object.entries(IFRAME_ATTRIBUTES).forEach(([key, value]) =>\n iframe.setAttribute(key, value)\n );\n wrapper.appendChild(iframe);\n\n /** Shows the stored source and width */\n const apply = (current: NodeViewRendererProps['node']) => {\n if (iframe.getAttribute('src') !== current.attrs.src) {\n iframe.setAttribute('src', current.attrs.src ?? '');\n }\n wrapper.style.width =\n typeof current.attrs.width === 'number'\n ? `${current.attrs.width}px`\n : '';\n };\n apply(node);\n\n return new ResizableNodeView({\n element: wrapper,\n editor,\n node,\n getPos,\n // The height follows from the 16:9 box, so only the width is set\n onResize: (width) => {\n wrapper.style.width = `${width}px`;\n },\n onCommit: (width) => {\n const pos = getPos();\n if (pos === undefined) return;\n editor\n .chain()\n .setNodeSelection(pos)\n .updateAttributes(this.name, { width: Math.round(width) })\n .run();\n },\n onUpdate: (updatedNode) => {\n if (updatedNode.type !== node.type) return false;\n apply(updatedNode);\n return true;\n },\n options: {\n // Same corner handles as the images\n directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],\n min: { width: MIN_VIDEO_WIDTH },\n preserveAspectRatio: true,\n },\n });\n };\n },\n\n addCommands() {\n return {\n setVideoEmbed:\n ({ src }) =>\n ({ commands }) =>\n isAllowedVideoEmbedSrc(src) &&\n commands.insertContent({ type: this.name, attrs: { src } }),\n };\n },\n});\n","import { useState, type ChangeEvent } from 'react';\nimport Button from '../../Button/Button';\nimport Input from '../../Input/Input';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport { toVideoEmbedUrl } from '../../../utils/videoEmbed';\n\ninterface VideoDialogProps {\n readonly open: boolean;\n readonly onClose: () => void;\n /** Receives the player URL (already converted from the pasted link) */\n readonly onInsert: (src: string) => void;\n}\n\nconst INVALID_URL = 'Use um link do YouTube ou do Vimeo.';\n\n/**\n * Asks for a YouTube or Vimeo link and hands back its player URL.\n */\nexport function VideoDialog({ open, onClose, onInsert }: VideoDialogProps) {\n const [url, setUrl] = useState('');\n const [error, setError] = useState('');\n\n const handleClose = () => {\n setUrl('');\n setError('');\n onClose();\n };\n\n const handleInsert = () => {\n const src = toVideoEmbedUrl(url);\n if (!src) {\n setError(INVALID_URL);\n return;\n }\n onInsert(src);\n handleClose();\n };\n\n return (\n <Modal\n isOpen={open}\n onClose={handleClose}\n title=\"Inserir vídeo\"\n size=\"md\"\n footer={\n <>\n <Button variant=\"outline\" onClick={handleClose}>\n Cancelar\n </Button>\n <Button\n variant=\"solid\"\n action=\"primary\"\n onClick={handleInsert}\n disabled={!url.trim()}\n >\n Inserir vídeo\n </Button>\n </>\n }\n >\n <div className=\"flex flex-col gap-2\">\n <Text weight=\"medium\" size=\"xs\" color=\"text-text-600\" className=\"block\">\n Link do vídeo\n </Text>\n <Input\n type=\"url\"\n aria-label=\"Link do vídeo\"\n value={url}\n onChange={(event: ChangeEvent<HTMLInputElement>) => {\n setUrl(event.target.value);\n setError('');\n }}\n placeholder=\"https://www.youtube.com/watch?v=...\"\n />\n <Text size=\"xs\" color=\"text-text-400\">\n Aceita links do YouTube e do Vimeo.\n </Text>\n {error && (\n <Text size=\"xs\" role=\"alert\" color=\"text-error-600\">\n {error}\n </Text>\n )}\n </div>\n </Modal>\n );\n}\n","import { useEffect, useRef, type ReactNode } from 'react';\nimport Button from '../../Button/Button';\n\n/** Emojis offered by the picker, in display order */\nexport const RICH_EDITOR_EMOJIS = [\n '😀 😃 😄 😁 😊 🙂 😉 😍',\n '🤩 😎 🤔 😅 😢 😮 🙏 👏',\n '👍 👎 👋 💪 ✅ ❌ ⚠️ ❗',\n '❓ ℹ️ 💡 📌 📎 📝 📚 📖',\n '🎓 🏫 📅 ⏰ 🔒 🔑 📱 💻',\n '🖥️ 📧 🔗 🎥 ▶️ ⭐ 🎉 🚀',\n]\n .join(' ')\n .split(' ');\n\ninterface EmojiPickerProps {\n readonly open: boolean;\n readonly onClose: () => void;\n readonly onSelect: (emoji: string) => void;\n /** The toolbar button; the popover anchors to it */\n readonly children: ReactNode;\n}\n\n/**\n * Toolbar popover with a grid of common emojis. Closes on pick, on Escape and\n * on a click outside.\n */\nexport function EmojiPicker({\n open,\n onClose,\n onSelect,\n children,\n}: EmojiPickerProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return undefined;\n const handlePointer = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) onClose();\n };\n const handleKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose();\n };\n document.addEventListener('mousedown', handlePointer);\n document.addEventListener('keydown', handleKey);\n return () => {\n document.removeEventListener('mousedown', handlePointer);\n document.removeEventListener('keydown', handleKey);\n };\n }, [open, onClose]);\n\n return (\n <div ref={rootRef} className=\"relative\">\n {children}\n {open && (\n <div\n role=\"dialog\"\n aria-label=\"Emojis\"\n className=\"absolute left-0 top-full z-20 mt-1 grid w-72 grid-cols-8 gap-1 rounded-lg border border-border-200 bg-background p-2 shadow-lg\"\n >\n {RICH_EDITOR_EMOJIS.map((emoji) => (\n <Button\n key={emoji}\n variant=\"raw\"\n aria-label={`Inserir ${emoji}`}\n onClick={() => {\n onSelect(emoji);\n onClose();\n }}\n className=\"flex h-8 w-8 items-center justify-center rounded text-lg hover:bg-background-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indicator-info\"\n >\n {emoji}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Clipboard handling for images pasted straight into the RichEditor.\n *\n * A screenshot on the clipboard arrives as a `File` on the paste event, but the\n * editor schema rejects base64 sources, so the file has to be uploaded before\n * it can be inserted. These helpers isolate the clipboard reading from the\n * upload flow in `RichEditorCore`, which keeps them testable without a real\n * ProseMirror view.\n */\n\nimport { MAX_IMAGE_SIZE } from './imageSize';\n\n/** Base name given to a clipboard image that arrives without a filename. */\nconst PASTED_IMAGE_BASENAME = 'imagem-colada';\n\n/**\n * Extension per MIME type accepted by the storage backend. Keyed by the types a\n * browser actually puts on the clipboard when copying an image.\n */\nconst EXTENSION_BY_MIME: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/bmp': 'bmp',\n 'image/svg+xml': 'svg',\n};\n\n/** Extension used when the clipboard reports an image type we do not map. */\nconst FALLBACK_EXTENSION = 'png';\n\n/**\n * Reads the image files carried by a paste event.\n * Anything that is not an image is left alone so the default paste behaviour\n * still handles it.\n * @param clipboardData - The `clipboardData` of the paste event\n * @returns The image files found, in clipboard order\n */\nexport const extractPastedImageFiles = (\n clipboardData: DataTransfer | null | undefined\n): File[] => {\n if (!clipboardData?.files) return [];\n\n return Array.from(clipboardData.files).filter((file) =>\n file.type.startsWith('image/')\n );\n};\n\n/**\n * Guarantees the file has a name before it reaches the upload.\n * Some browsers hand over a nameless blob for a screenshot, and the pre-signed\n * URL request sends `file.name` to the backend — an empty name would be\n * rejected there.\n * @param file - The image file taken from the clipboard\n * @returns The original file when it is already named, a renamed copy otherwise\n */\nexport const namePastedImage = (file: File): File => {\n if (file.name) return file;\n\n const extension = EXTENSION_BY_MIME[file.type] ?? FALLBACK_EXTENSION;\n return new File([file], `${PASTED_IMAGE_BASENAME}.${extension}`, {\n type: file.type,\n });\n};\n\nconst isOversized = (file: File) => file.size > MAX_IMAGE_SIZE;\n\n/**\n * Whether the clipboard markup should win over the image file it came with.\n *\n * Copying a range from Excel or Google Sheets puts both an HTML table and a\n * picture of it on the clipboard; taking the picture would silently turn a\n * pasteable table into a figure. Markup that carries its own `<img>` is the\n * opposite case — copying a picture from Word sends `<img src=\"file:///...\">`,\n * which would paste as a broken image, so there the file is the good source.\n * @param clipboardData - The `clipboardData` of the paste event\n * @returns True when the default paste handling should keep the markup\n */\nconst prefersMarkup = (clipboardData: DataTransfer) => {\n const html = clipboardData.getData('text/html');\n return html !== '' && !/<img\\b/i.test(html);\n};\n\ninterface PastedImageHandlerOptions {\n /** Whether the consumer opted into pasting images. */\n readonly enabled: boolean;\n /** Upload callback. Without one there is nowhere to put the file. */\n readonly upload?: (file: File) => Promise<string>;\n /** Receives the named images to upload, once the clipboard is accepted. */\n readonly onImages: (\n files: File[],\n upload: (file: File) => Promise<string>\n ) => void;\n /** Called instead of `onImages` when any image is over the size limit. */\n readonly onOversized: () => void;\n}\n\n/**\n * Builds the handler Tiptap calls on every paste.\n *\n * Returning false leaves the paste to the default handling, so text, HTML and\n * anything else keep behaving exactly as before — only a clipboard carrying\n * image files is taken over.\n * @param options - Upload wiring and the callbacks used to report the outcome\n * @returns A handler that reports whether it consumed the paste\n */\nexport const createPastedImageHandler =\n ({ enabled, upload, onImages, onOversized }: PastedImageHandlerOptions) =>\n (event: ClipboardEvent): boolean => {\n if (!enabled || !upload) return false;\n\n const clipboard = event.clipboardData;\n if (!clipboard) return false;\n\n const files = extractPastedImageFiles(clipboard);\n if (files.length === 0 || prefersMarkup(clipboard)) return false;\n\n // From here on the paste belongs to us: the default handler would drop the\n // file anyway, since the editor schema rejects base64 image sources.\n event.preventDefault();\n\n if (files.some(isOversized)) {\n onOversized();\n return true;\n }\n\n onImages(files.map(namePastedImage), upload);\n return true;\n };\n","/**\n * Detecção de cor no HTML das questões.\n *\n * O schema do RichEditor não tem atributo onde guardar cor (ver\n * `components/RichEditor/components/extensions.ts`), então toda declaração de\n * cor é descartada na leitura — o autor abre a questão e ela já aparece sem o\n * vermelho que veio da API. O que o schema não faz é avisar que descartou algo:\n * o valor em memória do formulário continua sendo o HTML colorido, e salvar sem\n * editar nada regravaria a cor no banco.\n *\n * Este módulo é o gatilho que fecha esse buraco. É deliberadamente uma\n * heurística sobre a string: basta reconhecer que havia cor para que o editor\n * devolva ao consumidor o HTML já limpo. Um falso positivo custa um\n * `onChange` extra na carga; um falso negativo só deixa a cor no valor salvo até\n * a próxima edição de verdade.\n */\n\n/** Valores de atributos `style` (aspas duplas ou simples) presentes no HTML. */\nconst STYLE_ATTRIBUTE = /style\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/gi;\n\n/**\n * Propriedades CSS que pintam texto ou fundo, ancoradas no início de uma\n * declaração para não casar com `border-color` e afins. A forma abreviada\n * `background` entra na lista porque é o que Word e Google Docs escrevem com\n * frequência no lugar de `background-color`.\n */\nconst COLOR_DECLARATION =\n /(?:^|;)\\s*(?:color|background|background-color)\\s*:/i;\n\n/**\n * Cor que vive fora do `style`: `<font color>` e `bgcolor` vêm dos exports\n * antigos do ENEM e do que se cola de um PDF; `<mark>` e `data-color` são o\n * grifo que o Highlight do Tiptap gravava junto com a cor de fundo.\n */\nconst COLOR_MARKUP = [\n /<font\\b[^>]*\\bcolor\\s*=/i,\n /<[a-z][^>]*\\bbgcolor\\s*=/i,\n /<mark\\b/i,\n /\\bdata-color\\s*=/i,\n];\n\n/**\n * Se `html` carrega alguma cor que o editor vai descartar ao carregá-lo.\n * @param html - HTML como veio da API, da geração por IA ou de um export antigo\n * @returns True quando há cor de fonte, de fundo ou grifo no conteúdo\n */\nexport function hasColorMarkup(html?: string | null): boolean {\n if (!html) return false;\n\n if (COLOR_MARKUP.some((pattern) => pattern.test(html))) return true;\n\n for (const match of html.matchAll(STYLE_ATTRIBUTE)) {\n if (COLOR_DECLARATION.test(match[1] ?? match[2] ?? '')) return true;\n }\n\n return false;\n}\n"]}
@@ -1,12 +1,12 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
3
 
4
- var _chunkDCG7EUBCjs = require('../chunk-DCG7EUBC.js');
5
- require('../chunk-RBJAO57A.js');
4
+ var _chunkUWLW5YVBjs = require('../chunk-UWLW5YVB.js');
5
+ require('../chunk-FAAR4XC5.js');
6
6
  require('../chunk-SNUI54QS.js');
7
7
  require('../chunk-I3SR6N3L.js');
8
8
  require('../chunk-IK5AMBED.js');
9
- require('../chunk-23PAYKWB.js');
9
+ require('../chunk-KM27ZDHZ.js');
10
10
  require('../chunk-6V5MWTP5.js');
11
11
  require('../chunk-EBA7URWC.js');
12
12
  require('../chunk-TWT3CSRQ.js');
@@ -27,5 +27,5 @@ require('../chunk-YQ3JVMVB.js');
27
27
 
28
28
 
29
29
 
30
- exports.default = _chunkDCG7EUBCjs.Search_default; exports.getResultsMessage = _chunkDCG7EUBCjs.getResultsMessage;
30
+ exports.default = _chunkUWLW5YVBjs.Search_default; exports.getResultsMessage = _chunkUWLW5YVBjs.getResultsMessage;
31
31
  //# sourceMappingURL=index.js.map
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  Search_default,
3
3
  getResultsMessage
4
- } from "../chunk-JCA76TCX.mjs";
5
- import "../chunk-HCQJR5CE.mjs";
4
+ } from "../chunk-SZKLJOTN.mjs";
5
+ import "../chunk-7ZWEBNFB.mjs";
6
6
  import "../chunk-JWTNOKCG.mjs";
7
7
  import "../chunk-W46HZ2AP.mjs";
8
8
  import "../chunk-4V2UAEJP.mjs";
9
- import "../chunk-XZUMD3CB.mjs";
9
+ import "../chunk-LODDYDTP.mjs";
10
10
  import "../chunk-7TXA3GOD.mjs";
11
11
  import "../chunk-624IUGB4.mjs";
12
12
  import "../chunk-MEUREWL3.mjs";