drawboard-microservice 1.0.7 → 1.0.9

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 (365) hide show
  1. package/dist/index.css +1 -0
  2. package/dist/index.js +41234 -0
  3. package/package.json +14 -4
  4. package/dist-app/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  5. package/dist-app/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  6. package/dist-app/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  7. package/dist-app/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  8. package/dist-app/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  9. package/dist-app/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  10. package/dist-app/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  11. package/dist-app/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  12. package/dist-app/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  13. package/dist-app/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  14. package/dist-app/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  15. package/dist-app/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  16. package/dist-app/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  17. package/dist-app/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  18. package/dist-app/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  19. package/dist-app/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  20. package/dist-app/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  21. package/dist-app/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  22. package/dist-app/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  23. package/dist-app/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  24. package/dist-app/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  25. package/dist-app/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  26. package/dist-app/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  27. package/dist-app/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  28. package/dist-app/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  29. package/dist-app/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  30. package/dist-app/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  31. package/dist-app/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  32. package/dist-app/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  33. package/dist-app/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  34. package/dist-app/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  35. package/dist-app/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  36. package/dist-app/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  37. package/dist-app/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  38. package/dist-app/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  39. package/dist-app/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  40. package/dist-app/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  41. package/dist-app/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  42. package/dist-app/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  43. package/dist-app/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  44. package/dist-app/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  45. package/dist-app/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  46. package/dist-app/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  47. package/dist-app/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  48. package/dist-app/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  49. package/dist-app/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  50. package/dist-app/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  51. package/dist-app/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  52. package/dist-app/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  53. package/dist-app/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  54. package/dist-app/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  55. package/dist-app/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  56. package/dist-app/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  57. package/dist-app/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  58. package/dist-app/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  59. package/dist-app/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  60. package/dist-app/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  61. package/dist-app/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  62. package/dist-app/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  63. package/dist-app/assets/main-Dm0gV1xN.js +0 -307
  64. package/dist-app/assets/main-Dm0gV1xN.js.map +0 -1
  65. package/dist-app/assets/main-uvzSCXM2.css +0 -1
  66. package/dist-app/index.html +0 -13
  67. package/dockerfile +0 -14
  68. package/eslint.config.js +0 -23
  69. package/index.html +0 -12
  70. package/public/icon-align-64-default.png +0 -0
  71. package/public/icon-align-justify-64-active.png +0 -0
  72. package/public/icon-align-justify-64-default 1.png +0 -0
  73. package/public/icon-align-justify-64-default.png +0 -0
  74. package/public/icon-align-justify-64-hover.png +0 -0
  75. package/public/icon-align-left-64-active.png +0 -0
  76. package/public/icon-align-left-64-default 1.png +0 -0
  77. package/public/icon-align-left-64-default.png +0 -0
  78. package/public/icon-align-left-64-hover.png +0 -0
  79. package/public/icon-align-right-64-active.png +0 -0
  80. package/public/icon-align-right-64-default.png +0 -0
  81. package/public/icon-align-right-64-hover.png +0 -0
  82. package/public/icon-bold-64-active.png +0 -0
  83. package/public/icon-bold-64-default 1.png +0 -0
  84. package/public/icon-bold-64-default.png +0 -0
  85. package/public/icon-bold-64-hover.png +0 -0
  86. package/public/icon-broom-64-active.png +0 -0
  87. package/public/icon-broom-64-default.png +0 -0
  88. package/public/icon-broom-64-hover.png +0 -0
  89. package/public/icon-cancel-64-active.png +0 -0
  90. package/public/icon-cancel-64-default.png +0 -0
  91. package/public/icon-cancel-64-hover.png +0 -0
  92. package/public/icon-change-64-active.png +0 -0
  93. package/public/icon-change-64-default.png +0 -0
  94. package/public/icon-change-64-hover.png +0 -0
  95. package/public/icon-circle-64-active.png +0 -0
  96. package/public/icon-circle-64-default.png +0 -0
  97. package/public/icon-circle-64-hover.png +0 -0
  98. package/public/icon-close-64-active.png +0 -0
  99. package/public/icon-close-64-default.png +0 -0
  100. package/public/icon-close-64-hover.png +0 -0
  101. package/public/icon-cursor-64-active.png +0 -0
  102. package/public/icon-cursor-64-default.png +0 -0
  103. package/public/icon-cursor-64-hover.png +0 -0
  104. package/public/icon-delete-trash-64-active.png +0 -0
  105. package/public/icon-delete-trash-64-default.png +0 -0
  106. package/public/icon-delete-trash-64-hover.png +0 -0
  107. package/public/icon-done-64-active.png +0 -0
  108. package/public/icon-done-64-default.png +0 -0
  109. package/public/icon-done-64-hover.png +0 -0
  110. package/public/icon-edit-text-file-64-active.png +0 -0
  111. package/public/icon-edit-text-file-64-default 1.png +0 -0
  112. package/public/icon-edit-text-file-64-default.png +0 -0
  113. package/public/icon-edit-text-file-64-hover.png +0 -0
  114. package/public/icon-eraser-64-active.png +0 -0
  115. package/public/icon-eraser-64-default.png +0 -0
  116. package/public/icon-eraser-64-hover.png +0 -0
  117. package/public/icon-font-style-64-active.png +0 -0
  118. package/public/icon-font-style-64-default.png +0 -0
  119. package/public/icon-font-style-64-hover.png +0 -0
  120. package/public/icon-formula-fx-64-active.png +0 -0
  121. package/public/icon-formula-fx-64-default.png +0 -0
  122. package/public/icon-formula-fx-64-hover.png +0 -0
  123. package/public/icon-highlighter-64-active.png +0 -0
  124. package/public/icon-highlighter-64-default.png +0 -0
  125. package/public/icon-highlighter-64-hover.png +0 -0
  126. package/public/icon-installing-64-active.png +0 -0
  127. package/public/icon-installing-64-default.png +0 -0
  128. package/public/icon-installing-64-hover.png +0 -0
  129. package/public/icon-italic-64-active.png +0 -0
  130. package/public/icon-italic-64-default 1.png +0 -0
  131. package/public/icon-italic-64-default.png +0 -0
  132. package/public/icon-italic-64-hover.png +0 -0
  133. package/public/icon-line-64-active.png +0 -0
  134. package/public/icon-line-64-default.png +0 -0
  135. package/public/icon-line-64-hover.png +0 -0
  136. package/public/icon-pen-64-active.png +0 -0
  137. package/public/icon-pen-64-default.png +0 -0
  138. package/public/icon-pen-64-hover.png +0 -0
  139. package/public/icon-pin-64-active.png +0 -0
  140. package/public/icon-pin-64-default.png +0 -0
  141. package/public/icon-pin-64-hover.png +0 -0
  142. package/public/icon-redo-64-active.png +0 -0
  143. package/public/icon-redo-64-default.png +0 -0
  144. package/public/icon-redo-64-hover.png +0 -0
  145. package/public/icon-return-64-active.png +0 -0
  146. package/public/icon-return-64-default.png +0 -0
  147. package/public/icon-return-64-hover.png +0 -0
  148. package/public/icon-select-active.png +0 -0
  149. package/public/icon-select-default.png +0 -0
  150. package/public/icon-select-hover.png +0 -0
  151. package/public/icon-square-64-active.png +0 -0
  152. package/public/icon-square-64-default.png +0 -0
  153. package/public/icon-square-64-hover.png +0 -0
  154. package/public/icon-strikethrough-64-active.png +0 -0
  155. package/public/icon-strikethrough-64-default 1.png +0 -0
  156. package/public/icon-strikethrough-64-default.png +0 -0
  157. package/public/icon-strikethrough-64-hover.png +0 -0
  158. package/public/icon-text-64-active.png +0 -0
  159. package/public/icon-text-64-default.png +0 -0
  160. package/public/icon-text-64-hover.png +0 -0
  161. package/public/icon-u-turn-to-right-64-active.png +0 -0
  162. package/public/icon-u-turn-to-right-64-default.png +0 -0
  163. package/public/icon-u-turn-to-right-64-hover.png +0 -0
  164. package/public/icon-under-64-active.png +0 -0
  165. package/public/icon-under-64-default 1.png +0 -0
  166. package/public/icon-under-64-default.png +0 -0
  167. package/public/icon-under-64-hover.png +0 -0
  168. package/src/App.css +0 -887
  169. package/src/App.tsx +0 -1468
  170. package/src/assets/icon-broom-64-active.png +0 -0
  171. package/src/assets/icon-broom-64-default.png +0 -0
  172. package/src/assets/icon-broom-64-hover.png +0 -0
  173. package/src/assets/icon-cancel-64-active.png +0 -0
  174. package/src/assets/icon-cancel-64-default.png +0 -0
  175. package/src/assets/icon-cancel-64-hover.png +0 -0
  176. package/src/assets/icon-change-64-active.png +0 -0
  177. package/src/assets/icon-change-64-default.png +0 -0
  178. package/src/assets/icon-change-64-hover.png +0 -0
  179. package/src/assets/icon-circle-64-active.png +0 -0
  180. package/src/assets/icon-circle-64-default.png +0 -0
  181. package/src/assets/icon-circle-64-hover.png +0 -0
  182. package/src/assets/icon-cursor-64-active.png +0 -0
  183. package/src/assets/icon-cursor-64-default.png +0 -0
  184. package/src/assets/icon-cursor-64-hover.png +0 -0
  185. package/src/assets/icon-delete-trash-64-active.png +0 -0
  186. package/src/assets/icon-delete-trash-64-default.png +0 -0
  187. package/src/assets/icon-delete-trash-64-hover.png +0 -0
  188. package/src/assets/icon-eraser-64-active.png +0 -0
  189. package/src/assets/icon-eraser-64-default.png +0 -0
  190. package/src/assets/icon-eraser-64-hover.png +0 -0
  191. package/src/assets/icon-font-style-64-active.png +0 -0
  192. package/src/assets/icon-font-style-64-default.png +0 -0
  193. package/src/assets/icon-font-style-64-hover.png +0 -0
  194. package/src/assets/icon-formula-fx-64-active.png +0 -0
  195. package/src/assets/icon-formula-fx-64-default.png +0 -0
  196. package/src/assets/icon-formula-fx-64-hover.png +0 -0
  197. package/src/assets/icon-highlighter-64-active.png +0 -0
  198. package/src/assets/icon-highlighter-64-default.png +0 -0
  199. package/src/assets/icon-highlighter-64-hover.png +0 -0
  200. package/src/assets/icon-installing-64-active.png +0 -0
  201. package/src/assets/icon-installing-64-default.png +0 -0
  202. package/src/assets/icon-installing-64-hover.png +0 -0
  203. package/src/assets/icon-line-64-active.png +0 -0
  204. package/src/assets/icon-line-64-default.png +0 -0
  205. package/src/assets/icon-line-64-hover.png +0 -0
  206. package/src/assets/icon-minus-64-active.png +0 -0
  207. package/src/assets/icon-minus-64-default.png +0 -0
  208. package/src/assets/icon-minus-64-hover.png +0 -0
  209. package/src/assets/icon-pen-64-active.png +0 -0
  210. package/src/assets/icon-pen-64-default.png +0 -0
  211. package/src/assets/icon-pen-64-hover.png +0 -0
  212. package/src/assets/icon-pin-64-active.png +0 -0
  213. package/src/assets/icon-pin-64-default.png +0 -0
  214. package/src/assets/icon-pin-64-hover.png +0 -0
  215. package/src/assets/icon-plus-64-active.png +0 -0
  216. package/src/assets/icon-plus-64-default.png +0 -0
  217. package/src/assets/icon-plus-64-hover.png +0 -0
  218. package/src/assets/icon-return-64-active.png +0 -0
  219. package/src/assets/icon-return-64-default.png +0 -0
  220. package/src/assets/icon-return-64-hover.png +0 -0
  221. package/src/assets/icon-select-active.png +0 -0
  222. package/src/assets/icon-select-default.png +0 -0
  223. package/src/assets/icon-select-hover.png +0 -0
  224. package/src/assets/icon-square-64-active.png +0 -0
  225. package/src/assets/icon-square-64-default.png +0 -0
  226. package/src/assets/icon-square-64-hover.png +0 -0
  227. package/src/assets/icon-text-64-active.png +0 -0
  228. package/src/assets/icon-text-64-default.png +0 -0
  229. package/src/assets/icon-text-64-hover.png +0 -0
  230. package/src/assets/icon-u-turn-to-right-64-active.png +0 -0
  231. package/src/assets/icon-u-turn-to-right-64-default.png +0 -0
  232. package/src/assets/icon-u-turn-to-right-64-hover.png +0 -0
  233. package/src/components/BrushToolbar.tsx +0 -72
  234. package/src/components/LatexEditor.tsx +0 -247
  235. package/src/components/LatexToolbar.tsx +0 -68
  236. package/src/components/OCRPreview.tsx +0 -234
  237. package/src/components/OCRToolbar.tsx +0 -46
  238. package/src/components/RangeToolbar.tsx +0 -33
  239. package/src/components/SelDelToolbar.tsx +0 -52
  240. package/src/components/TextEditor.tsx +0 -38
  241. package/src/components/TextToolbar.tsx +0 -271
  242. package/src/components/ToolsToolbar.tsx +0 -114
  243. package/src/constants/fonts.ts +0 -14
  244. package/src/constants/latexCategories.ts +0 -11
  245. package/src/constants/latexSymbols.ts +0 -38
  246. package/src/hooks/useDrawingHandlers.ts +0 -676
  247. package/src/hooks/useDrawingState.ts +0 -59
  248. package/src/hooks/useHistory.ts +0 -52
  249. package/src/hooks/useKeyboard.ts +0 -96
  250. package/src/hooks/useLatexSymbols.ts +0 -79
  251. package/src/hooks/useOutsideClick.ts +0 -25
  252. package/src/hooks/useTextEditing.ts +0 -131
  253. package/src/hooks/useTextFormatting.ts +0 -153
  254. package/src/hooks/useWebSocket.ts +0 -202
  255. package/src/index.ts +0 -4
  256. package/src/main.tsx +0 -18
  257. package/src/services/api.ts +0 -81
  258. package/src/services/statsService.ts +0 -165
  259. package/src/services/widgetBridge.ts +0 -73
  260. package/src/types.ts +0 -80
  261. package/src/utils/colorUtils.ts +0 -6
  262. package/src/utils/latexUtils.ts +0 -48
  263. package/src/utils/shapeUtils.ts +0 -151
  264. package/tsconfig.app.json +0 -28
  265. package/tsconfig.json +0 -14
  266. package/tsconfig.node.json +0 -26
  267. package/vite.config.ts +0 -85
  268. /package/{dist-app → dist}/icon-align-64-default.png +0 -0
  269. /package/{dist-app → dist}/icon-align-justify-64-active.png +0 -0
  270. /package/{dist-app → dist}/icon-align-justify-64-default 1.png +0 -0
  271. /package/{dist-app → dist}/icon-align-justify-64-default.png +0 -0
  272. /package/{dist-app → dist}/icon-align-justify-64-hover.png +0 -0
  273. /package/{dist-app → dist}/icon-align-left-64-active.png +0 -0
  274. /package/{dist-app → dist}/icon-align-left-64-default 1.png +0 -0
  275. /package/{dist-app → dist}/icon-align-left-64-default.png +0 -0
  276. /package/{dist-app → dist}/icon-align-left-64-hover.png +0 -0
  277. /package/{dist-app → dist}/icon-align-right-64-active.png +0 -0
  278. /package/{dist-app → dist}/icon-align-right-64-default.png +0 -0
  279. /package/{dist-app → dist}/icon-align-right-64-hover.png +0 -0
  280. /package/{dist-app → dist}/icon-bold-64-active.png +0 -0
  281. /package/{dist-app → dist}/icon-bold-64-default 1.png +0 -0
  282. /package/{dist-app → dist}/icon-bold-64-default.png +0 -0
  283. /package/{dist-app → dist}/icon-bold-64-hover.png +0 -0
  284. /package/{dist-app → dist}/icon-broom-64-active.png +0 -0
  285. /package/{dist-app → dist}/icon-broom-64-default.png +0 -0
  286. /package/{dist-app → dist}/icon-broom-64-hover.png +0 -0
  287. /package/{dist-app → dist}/icon-cancel-64-active.png +0 -0
  288. /package/{dist-app → dist}/icon-cancel-64-default.png +0 -0
  289. /package/{dist-app → dist}/icon-cancel-64-hover.png +0 -0
  290. /package/{dist-app → dist}/icon-change-64-active.png +0 -0
  291. /package/{dist-app → dist}/icon-change-64-default.png +0 -0
  292. /package/{dist-app → dist}/icon-change-64-hover.png +0 -0
  293. /package/{dist-app → dist}/icon-circle-64-active.png +0 -0
  294. /package/{dist-app → dist}/icon-circle-64-default.png +0 -0
  295. /package/{dist-app → dist}/icon-circle-64-hover.png +0 -0
  296. /package/{dist-app → dist}/icon-close-64-active.png +0 -0
  297. /package/{dist-app → dist}/icon-close-64-default.png +0 -0
  298. /package/{dist-app → dist}/icon-close-64-hover.png +0 -0
  299. /package/{dist-app → dist}/icon-cursor-64-active.png +0 -0
  300. /package/{dist-app → dist}/icon-cursor-64-default.png +0 -0
  301. /package/{dist-app → dist}/icon-cursor-64-hover.png +0 -0
  302. /package/{dist-app → dist}/icon-delete-trash-64-active.png +0 -0
  303. /package/{dist-app → dist}/icon-delete-trash-64-default.png +0 -0
  304. /package/{dist-app → dist}/icon-delete-trash-64-hover.png +0 -0
  305. /package/{dist-app → dist}/icon-done-64-active.png +0 -0
  306. /package/{dist-app → dist}/icon-done-64-default.png +0 -0
  307. /package/{dist-app → dist}/icon-done-64-hover.png +0 -0
  308. /package/{dist-app → dist}/icon-edit-text-file-64-active.png +0 -0
  309. /package/{dist-app → dist}/icon-edit-text-file-64-default 1.png +0 -0
  310. /package/{dist-app → dist}/icon-edit-text-file-64-default.png +0 -0
  311. /package/{dist-app → dist}/icon-edit-text-file-64-hover.png +0 -0
  312. /package/{dist-app → dist}/icon-eraser-64-active.png +0 -0
  313. /package/{dist-app → dist}/icon-eraser-64-default.png +0 -0
  314. /package/{dist-app → dist}/icon-eraser-64-hover.png +0 -0
  315. /package/{dist-app → dist}/icon-font-style-64-active.png +0 -0
  316. /package/{dist-app → dist}/icon-font-style-64-default.png +0 -0
  317. /package/{dist-app → dist}/icon-font-style-64-hover.png +0 -0
  318. /package/{dist-app → dist}/icon-formula-fx-64-active.png +0 -0
  319. /package/{dist-app → dist}/icon-formula-fx-64-default.png +0 -0
  320. /package/{dist-app → dist}/icon-formula-fx-64-hover.png +0 -0
  321. /package/{dist-app → dist}/icon-highlighter-64-active.png +0 -0
  322. /package/{dist-app → dist}/icon-highlighter-64-default.png +0 -0
  323. /package/{dist-app → dist}/icon-highlighter-64-hover.png +0 -0
  324. /package/{dist-app → dist}/icon-installing-64-active.png +0 -0
  325. /package/{dist-app → dist}/icon-installing-64-default.png +0 -0
  326. /package/{dist-app → dist}/icon-installing-64-hover.png +0 -0
  327. /package/{dist-app → dist}/icon-italic-64-active.png +0 -0
  328. /package/{dist-app → dist}/icon-italic-64-default 1.png +0 -0
  329. /package/{dist-app → dist}/icon-italic-64-default.png +0 -0
  330. /package/{dist-app → dist}/icon-italic-64-hover.png +0 -0
  331. /package/{dist-app → dist}/icon-line-64-active.png +0 -0
  332. /package/{dist-app → dist}/icon-line-64-default.png +0 -0
  333. /package/{dist-app → dist}/icon-line-64-hover.png +0 -0
  334. /package/{dist-app → dist}/icon-pen-64-active.png +0 -0
  335. /package/{dist-app → dist}/icon-pen-64-default.png +0 -0
  336. /package/{dist-app → dist}/icon-pen-64-hover.png +0 -0
  337. /package/{dist-app → dist}/icon-pin-64-active.png +0 -0
  338. /package/{dist-app → dist}/icon-pin-64-default.png +0 -0
  339. /package/{dist-app → dist}/icon-pin-64-hover.png +0 -0
  340. /package/{dist-app → dist}/icon-redo-64-active.png +0 -0
  341. /package/{dist-app → dist}/icon-redo-64-default.png +0 -0
  342. /package/{dist-app → dist}/icon-redo-64-hover.png +0 -0
  343. /package/{dist-app → dist}/icon-return-64-active.png +0 -0
  344. /package/{dist-app → dist}/icon-return-64-default.png +0 -0
  345. /package/{dist-app → dist}/icon-return-64-hover.png +0 -0
  346. /package/{dist-app → dist}/icon-select-active.png +0 -0
  347. /package/{dist-app → dist}/icon-select-default.png +0 -0
  348. /package/{dist-app → dist}/icon-select-hover.png +0 -0
  349. /package/{dist-app → dist}/icon-square-64-active.png +0 -0
  350. /package/{dist-app → dist}/icon-square-64-default.png +0 -0
  351. /package/{dist-app → dist}/icon-square-64-hover.png +0 -0
  352. /package/{dist-app → dist}/icon-strikethrough-64-active.png +0 -0
  353. /package/{dist-app → dist}/icon-strikethrough-64-default 1.png +0 -0
  354. /package/{dist-app → dist}/icon-strikethrough-64-default.png +0 -0
  355. /package/{dist-app → dist}/icon-strikethrough-64-hover.png +0 -0
  356. /package/{dist-app → dist}/icon-text-64-active.png +0 -0
  357. /package/{dist-app → dist}/icon-text-64-default.png +0 -0
  358. /package/{dist-app → dist}/icon-text-64-hover.png +0 -0
  359. /package/{dist-app → dist}/icon-u-turn-to-right-64-active.png +0 -0
  360. /package/{dist-app → dist}/icon-u-turn-to-right-64-default.png +0 -0
  361. /package/{dist-app → dist}/icon-u-turn-to-right-64-hover.png +0 -0
  362. /package/{dist-app → dist}/icon-under-64-active.png +0 -0
  363. /package/{dist-app → dist}/icon-under-64-default 1.png +0 -0
  364. /package/{dist-app → dist}/icon-under-64-default.png +0 -0
  365. /package/{dist-app → dist}/icon-under-64-hover.png +0 -0
@@ -1,202 +0,0 @@
1
- import { useEffect, useRef, useCallback, useState, useMemo } from 'react';
2
- import type { Shape } from '../types';
3
-
4
- const WS_BASE_URL =
5
- import.meta.env.VITE_PUBLIC_WS_URL || 'ws://localhost:8000';
6
-
7
- export interface WebSocketMessage {
8
- type: 'init' | 'update' | 'clear' | 'undo' | 'shapes';
9
- data?: any;
10
- userId?: string;
11
- }
12
-
13
- export interface CanvasData {
14
- shapes: Shape[];
15
- config?: any;
16
- history?: any[];
17
- }
18
-
19
- export const useWebSocket = (
20
- boardId: string,
21
- onCanvasUpdate: (data: CanvasData) => void,
22
- userId: string = crypto.randomUUID()
23
- ) => {
24
- const wsRef = useRef<WebSocket | null>(null);
25
- const onCanvasUpdateRef = useRef(onCanvasUpdate);
26
- const reconnectTimerRef = useRef<number | null>(null);
27
- const reconnectAttempts = useRef(0);
28
-
29
- // ⬇️ Используем useRef для стабильных значений
30
- const boardIdRef = useRef(boardId);
31
- const userIdRef = useRef(userId);
32
-
33
- const MAX_RECONNECT_ATTEMPTS = 5;
34
- const RECONNECT_DELAY = 1500;
35
-
36
- const [isConnected, setIsConnected] = useState(false);
37
-
38
- // Обновляем refs при изменении пропсов
39
- useEffect(() => {
40
- boardIdRef.current = boardId;
41
- }, [boardId]);
42
-
43
- useEffect(() => {
44
- userIdRef.current = userId;
45
- }, [userId]);
46
-
47
- // Всегда актуальный callback
48
- useEffect(() => {
49
- onCanvasUpdateRef.current = onCanvasUpdate;
50
- }, [onCanvasUpdate]);
51
-
52
- // ⬇️ connect больше не зависит от изменяющихся пропсов
53
- const connect = useCallback(() => {
54
- if (wsRef.current?.readyState === WebSocket.OPEN) {
55
- console.log('[WS] Already connected, skipping');
56
- return;
57
- }
58
-
59
- // Закрываем существующее соединение
60
- if (wsRef.current) {
61
- wsRef.current.close();
62
- wsRef.current = null;
63
- }
64
-
65
- const ws = new WebSocket(
66
- `${WS_BASE_URL}/ws/canvas/${boardIdRef.current}/`
67
- );
68
-
69
- wsRef.current = ws;
70
-
71
- ws.onopen = () => {
72
- console.log('[WS] Connected to board:', boardIdRef.current);
73
- setIsConnected(true);
74
- reconnectAttempts.current = 0;
75
-
76
- // Запрашиваем начальное состояние
77
- ws.send(JSON.stringify({
78
- type: 'init',
79
- userId: userIdRef.current
80
- }));
81
- };
82
-
83
- ws.onmessage = (event) => {
84
- try {
85
- const message: WebSocketMessage = JSON.parse(event.data);
86
-
87
- if (message.userId === userIdRef.current) return;
88
-
89
- switch (message.type) {
90
- case 'init':
91
- if (message.data) {
92
- onCanvasUpdateRef.current(message.data);
93
- }
94
- break;
95
-
96
- case 'update':
97
- case 'shapes':
98
- case 'undo':
99
- if (message.data) {
100
- onCanvasUpdateRef.current(message.data);
101
- }
102
- break;
103
-
104
- case 'clear':
105
- onCanvasUpdateRef.current({ shapes: [], config: {}, history: [] });
106
- break;
107
-
108
- default:
109
- console.warn('[WS] Unknown message type:', message.type);
110
- }
111
- } catch (e) {
112
- console.error('[WS] Message parse error:', e);
113
- }
114
- };
115
-
116
- ws.onerror = (e) => {
117
- console.error('[WS] Connection error:', e);
118
- };
119
-
120
- ws.onclose = (event) => {
121
- console.log('[WS] Disconnected, code:', event.code, 'reason:', event.reason);
122
- setIsConnected(false);
123
- wsRef.current = null;
124
-
125
- // Не переподключаемся при нормальном закрытии
126
- if (event.code === 1000) return;
127
-
128
- if (reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {
129
- reconnectAttempts.current += 1;
130
- console.log(`[WS] Reconnecting in ${RECONNECT_DELAY * reconnectAttempts.current}ms (attempt ${reconnectAttempts.current})`);
131
-
132
- reconnectTimerRef.current = window.setTimeout(() => {
133
- connect();
134
- }, RECONNECT_DELAY * reconnectAttempts.current);
135
- } else {
136
- console.error('[WS] Max reconnection attempts reached');
137
- }
138
- };
139
- }, []); // ⬅️ Пустой массив зависимостей - функция создается один раз
140
-
141
- const disconnect = useCallback(() => {
142
- // Очищаем таймер переподключения
143
- if (reconnectTimerRef.current) {
144
- clearTimeout(reconnectTimerRef.current);
145
- reconnectTimerRef.current = null;
146
- }
147
-
148
- // Закрываем соединение с кодом 1000 (нормальное закрытие)
149
- if (wsRef.current) {
150
- wsRef.current.close(1000, 'Client disconnect');
151
- wsRef.current = null;
152
- setIsConnected(false);
153
- }
154
- }, []);
155
-
156
- const sendMessage = useCallback((message: WebSocketMessage) => {
157
- if (wsRef.current?.readyState === WebSocket.OPEN) {
158
- wsRef.current.send(
159
- JSON.stringify({ ...message, userId: userIdRef.current })
160
- );
161
- } else {
162
- console.warn('[WS] Cannot send message - connection not open');
163
- }
164
- }, []);
165
-
166
- const sendShapesUpdate = useCallback(
167
- (shapes: Shape[]) => {
168
- sendMessage({
169
- type: 'update',
170
- data: { shapes },
171
- });
172
- },
173
- [sendMessage]
174
- );
175
-
176
- const sendClear = useCallback(() => {
177
- sendMessage({ type: 'clear' });
178
- }, [sendMessage]);
179
-
180
- const sendUndo = useCallback(() => {
181
- sendMessage({ type: 'undo' });
182
- }, [sendMessage]);
183
-
184
- // ⬇️ Ключевое исправление - эффект зависит только от boardId
185
- useEffect(() => {
186
- console.log('[WS] Setting up connection for board:', boardId);
187
- connect();
188
-
189
- return () => {
190
- console.log('[WS] Cleaning up connection for board:', boardId);
191
- disconnect();
192
- };
193
- }, [boardId]); // ⬅️ Только boardId вызывает переподключение
194
-
195
- // Мемоизируем возвращаемый объект, чтобы не вызывать перерендеры
196
- return useMemo(() => ({
197
- sendShapesUpdate,
198
- sendClear,
199
- sendUndo,
200
- isConnected,
201
- }), [sendShapesUpdate, sendClear, sendUndo, isConnected]);
202
- };
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- // Экспортируем компоненты, которые хотим сделать публичными
2
- export { default as DrawingApp } from './App';
3
- export { getInfo } from './services/widgetBridge';
4
- // Добавьте другие экспорты по необходимости
package/src/main.tsx DELETED
@@ -1,18 +0,0 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom/client';
3
- import { getInfo } from './services/widgetBridge';
4
- import DrawingApp from './App.tsx';
5
-
6
- // 👇 Делаем доступным из консоли и iframe
7
- (window as any).getInfo = getInfo;
8
-
9
-
10
- const root = ReactDOM.createRoot(
11
- document.getElementById('root') as HTMLElement
12
- );
13
-
14
- root.render(
15
- <React.StrictMode>
16
- <DrawingApp />
17
- </React.StrictMode>
18
- );
@@ -1,81 +0,0 @@
1
- // API service for sending canvas commands to backend
2
- // Added for backend data sending logic
3
-
4
- const API_BASE_URL = 'http://localhost:8000'; // Adjust to your backend URL
5
-
6
- export interface ApiResponse {
7
- success: boolean;
8
- message?: string;
9
- data?: any;
10
- }
11
-
12
- export const sendCanvasCommand = async (
13
- boardId: string,
14
- action: 'clear' | 'undo' | 'update',
15
- data?: any
16
- ): Promise<ApiResponse> => {
17
- try {
18
- const url = `${API_BASE_URL}/api/canvas/${boardId}/${action}/`;
19
-
20
- const response = await fetch(url, {
21
- method: 'POST',
22
- headers: {
23
- 'Content-Type': 'application/json',
24
- },
25
- body: JSON.stringify(data || {}),
26
- });
27
-
28
- if (!response.ok) {
29
- throw new Error(`HTTP error! status: ${response.status}`);
30
- }
31
-
32
- const result = await response.json();
33
- return result;
34
- } catch (error) {
35
- console.error('API Error:', error);
36
- return { success: false, message: error instanceof Error ? error.message : 'Unknown error' };
37
- }
38
- };
39
-
40
- // OCR функция для отправки изображения
41
- export const sendOcrImage = async (imageData: string): Promise<ApiResponse> => {
42
- try {
43
- const url = `${API_BASE_URL}/api/ocr/latex/`;
44
-
45
- const response = await fetch(url, {
46
- method: 'POST',
47
- headers: {
48
- 'Content-Type': 'application/json',
49
- },
50
- body: JSON.stringify({ image_data: imageData }),
51
- });
52
-
53
- if (!response.ok) {
54
- throw new Error(`HTTP error! status: ${response.status}`);
55
- }
56
-
57
- const result = await response.json();
58
- return result;
59
- } catch (error) {
60
- console.error('OCR API Error:', error);
61
- return { success: false, message: error instanceof Error ? error.message : 'Unknown error' };
62
- }
63
- };
64
-
65
- // Specific functions for each action
66
- export const clearCanvas = async (boardId: string): Promise<ApiResponse> => {
67
- return sendCanvasCommand(boardId, 'clear');
68
- };
69
-
70
- export const undoAction = async (boardId: string): Promise<ApiResponse> => {
71
- return sendCanvasCommand(boardId, 'undo');
72
- };
73
-
74
- export const updateCanvasData = async (boardId: string, data: { shapes: any[], config?: any, history?: any[] }): Promise<ApiResponse> => {
75
- return sendCanvasCommand(boardId, 'update', data);
76
- };
77
-
78
- // Legacy function for backward compatibility
79
- export const updateShapes = async (boardId: string, shapes: any[]): Promise<ApiResponse> => {
80
- return updateCanvasData(boardId, { shapes });
81
- };
@@ -1,165 +0,0 @@
1
- // @ts-nocheck
2
- import type { CanvasData } from '../hooks/useWebSocket';
3
-
4
- export interface MetricsData {
5
- shapesCount: number;
6
- toolsUsage: Record<string, number>;
7
- lastUpdated: string;
8
- sessionDuration?: number;
9
- boardId: string;
10
- widgetId: number | null;
11
- }
12
-
13
- export interface WidgetConfig {
14
- shapes: any[];
15
- config: any;
16
- lastModified: string;
17
- version: string;
18
- }
19
-
20
- class StatsService {
21
- private baseURL = 'https://statservice.example.com';
22
- private moduleToken: string | null = null;
23
- private rateLimitCache = new Map<string, { count: number; resetTime: number }>();
24
- private readonly RATE_LIMIT_WINDOW = 60 * 60 * 1000; // 60 minutes
25
- private readonly MAX_REQUESTS = 12;
26
-
27
- constructor() {
28
- this.loadToken();
29
- }
30
-
31
- private loadToken() {
32
- const savedToken = localStorage.getItem('moduleToken');
33
- if (savedToken) {
34
- this.moduleToken = savedToken;
35
- }
36
- }
37
-
38
- private saveToken(token: string) {
39
- this.moduleToken = token;
40
- localStorage.setItem('moduleToken', token);
41
- }
42
-
43
- async createModule(moduleName: string): Promise<{
44
- moduleId: number;
45
- moduleName: string;
46
- token: string;
47
- tokenExpired: string;
48
- }> {
49
- try {
50
- const response = await fetch(`${this.baseURL}/api/stats/module/create`, {
51
- method: 'POST',
52
- headers: {
53
- 'Content-Type': 'application/json',
54
- },
55
- body: JSON.stringify({ moduleName })
56
- });
57
-
58
- if (!response.ok) {
59
- throw new Error(`Failed to create module: ${response.statusText}`);
60
- }
61
-
62
- const data = await response.json();
63
- this.saveToken(data.token);
64
- return data;
65
- } catch (error) {
66
- console.error('Error creating module:', error);
67
- throw error;
68
- }
69
- }
70
-
71
- private checkRateLimit(): boolean {
72
- const now = Date.now();
73
- const moduleKey = this.moduleToken || 'anonymous';
74
-
75
- const limitInfo = this.rateLimitCache.get(moduleKey);
76
-
77
- if (!limitInfo) {
78
- this.rateLimitCache.set(moduleKey, {
79
- count: 1,
80
- resetTime: now + this.RATE_LIMIT_WINDOW
81
- });
82
- return true;
83
- }
84
-
85
- if (now > limitInfo.resetTime) {
86
- this.rateLimitCache.set(moduleKey, {
87
- count: 1,
88
- resetTime: now + this.RATE_LIMIT_WINDOW
89
- });
90
- return true;
91
- }
92
-
93
- if (limitInfo.count >= this.MAX_REQUESTS) {
94
- return false;
95
- }
96
-
97
- limitInfo.count++;
98
- return true;
99
- }
100
-
101
- async sendMetrics(metrics: MetricsData): Promise<{ moduleId: number; metricId: number }> {
102
- if (!this.moduleToken) {
103
- throw new Error('Module token not set. Please create module first.');
104
- }
105
-
106
- if (!this.checkRateLimit()) {
107
- throw new Error('Rate limit exceeded. Please try again later.');
108
- }
109
-
110
- try {
111
- const response = await fetch(`${this.baseURL}/api/stats/module/metrics`, {
112
- method: 'PUT',
113
- headers: {
114
- 'X-Module-Token': this.moduleToken,
115
- 'Content-Type': 'application/json',
116
- },
117
- body: JSON.stringify(metrics)
118
- });
119
-
120
- if (response.status === 429) {
121
- const retryAfter = response.headers.get('Retry-After');
122
- throw new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds`);
123
- }
124
-
125
- if (!response.ok) {
126
- throw new Error(`Failed to send metrics: ${response.statusText}`);
127
- }
128
-
129
- return await response.json();
130
- } catch (error) {
131
- console.error('Error sending metrics:', error);
132
- throw error;
133
- }
134
- }
135
-
136
- async getMetrics(): Promise<any> {
137
- if (!this.moduleToken) {
138
- throw new Error('Module token not set');
139
- }
140
-
141
- try {
142
- const response = await fetch(`${this.baseURL}/api/stats/module/metrics`, {
143
- method: 'GET',
144
- headers: {
145
- 'X-Module-Token': this.moduleToken,
146
- }
147
- });
148
-
149
- if (response.status === 404) {
150
- return null;
151
- }
152
-
153
- if (!response.ok) {
154
- throw new Error(`Failed to get metrics: ${response.statusText}`);
155
- }
156
-
157
- return await response.json();
158
- } catch (error) {
159
- console.error('Error getting metrics:', error);
160
- throw error;
161
- }
162
- }
163
- }
164
-
165
- export const statsService = new StatsService();
@@ -1,73 +0,0 @@
1
- // widgetBridge.ts
2
- export interface WidgetInitPayload {
3
- widgetId: number;
4
- userId: number;
5
- role: string;
6
- config: any;
7
- board: {
8
- id: number;
9
- name: string;
10
- parentId: number;
11
- };
12
- }
13
-
14
- let widgetContext: WidgetInitPayload | null = null;
15
- let onInitializedCallbacks: Array<(payload: WidgetInitPayload) => void> = [];
16
-
17
- export const getInfo = (payload: WidgetInitPayload) => {
18
- if (!payload?.board?.id || typeof payload.board.id !== 'number') {
19
- throw new Error('Invalid board.id');
20
- }
21
-
22
- if (typeof payload.widgetId !== 'number') {
23
- throw new Error('Invalid widgetId');
24
- }
25
-
26
- widgetContext = payload;
27
-
28
- console.log('[Widget] Initialized with:', payload);
29
-
30
- // Уведомляем всех подписчиков
31
- onInitializedCallbacks.forEach(callback => callback(payload));
32
-
33
- return {
34
- widgetId: payload.widgetId,
35
- boardId: String(payload.board.id),
36
- userId: payload.userId,
37
- role: payload.role,
38
- config: payload.config,
39
- };
40
- };
41
-
42
- export const getWidgetContext = () => {
43
- if (!widgetContext) {
44
- throw new Error('Widget not initialized. Call getInfo first.');
45
- }
46
- return widgetContext;
47
- };
48
-
49
- // Подписка на инициализацию виджета
50
- export const onWidgetInitialized = (callback: (payload: WidgetInitPayload) => void) => {
51
- onInitializedCallbacks.push(callback);
52
-
53
- // Если виджет уже инициализирован, вызываем callback сразу
54
- if (widgetContext) {
55
- callback(widgetContext);
56
- }
57
-
58
- // Функция для отписки
59
- return () => {
60
- const index = onInitializedCallbacks.indexOf(callback);
61
- if (index > -1) {
62
- onInitializedCallbacks.splice(index, 1);
63
- }
64
- };
65
- };
66
-
67
- // Получение только boardId (для обратной совместимости)
68
- export const getBoardId = (): string => {
69
- if (!widgetContext) {
70
- throw new Error('Widget not initialized');
71
- }
72
- return String(widgetContext.board.id);
73
- };
package/src/types.ts DELETED
@@ -1,80 +0,0 @@
1
- export type ShapeType = 'rectangle' | 'ellipse' | 'line' | 'path' | 'text' | 'latex' | 'highlighter';
2
- export type ToolMode =
3
- | 'select'
4
- | 'rectangle'
5
- | 'ellipse'
6
- | 'line'
7
- | 'pencil'
8
- | 'eraser'
9
- | 'text'
10
- | 'latex'
11
- | 'highlighter'
12
- | 'ocr-selection';
13
- export type AnchorType = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | null;
14
- export type TextAlign = 'left' | 'center' | 'right';
15
-
16
- export interface Shape {
17
- id: string;
18
- type: ShapeType;
19
- x: number;
20
- y: number;
21
- width: number;
22
- height: number;
23
- stroke: string;
24
- strokeWidth: number;
25
- fill?: string;
26
- dash?: number[];
27
- points?: number[];
28
- opacity?: number;
29
- isSelected?: boolean;
30
- text?: string;
31
- latex?: string;
32
- fontSize?: number;
33
- fontFamily?: string;
34
- textAlign?: TextAlign;
35
- isEditing?: boolean;
36
- fontWeight?: 'normal' | 'bold';
37
- fontStyle?: 'normal' | 'italic';
38
- textDecoration?: 'none' | 'underline' | 'line-through' | 'underline line-through';
39
- isLatex?: boolean;
40
- latexRendered?: string;
41
- scaleX?: number;
42
- scaleY?: number;
43
- rotation?: number;
44
- }
45
-
46
- export interface DrawingState {
47
- isDrawing: boolean;
48
- startX: number;
49
- startY: number;
50
- currentShape: Partial<Shape> | null;
51
- }
52
-
53
- export interface TransformState {
54
- isTransforming: boolean;
55
- shapeId: string | null;
56
- startWidth: number;
57
- startHeight: number;
58
- startX: number;
59
- startY: number;
60
- startMouseX: number;
61
- startMouseY: number;
62
- anchor: AnchorType;
63
- originalPoints?: number[];
64
- originalBbox?: { x: number, y: number, width: number, height: number };
65
- startFontSize?: number;
66
- }
67
-
68
- export interface LatexSymbol {
69
- name: string;
70
- latex: string;
71
- description: string;
72
- category: 'fraction' | 'root' | 'superscript' | 'subscript' | 'brackets' | 'operators' | 'symbols';
73
- placeholder?: string;
74
- }
75
-
76
- export interface LatexCategory {
77
- id: string;
78
- name: string;
79
- icon: string;
80
- }
@@ -1,6 +0,0 @@
1
- export const hexToRgba = (hex: string, opacity: number): string => {
2
- const r = parseInt(hex.slice(1, 3), 16);
3
- const g = parseInt(hex.slice(3, 5), 16);
4
- const b = parseInt(hex.slice(5, 7), 16);
5
- return `rgba(${r}, ${g}, ${b}, ${opacity})`;
6
- };
@@ -1,48 +0,0 @@
1
- // @ts-nocheck
2
- import katex from 'katex';
3
-
4
- export const renderLatexToHtml = (latex: string, fontSize: number = 20): string => {
5
- try {
6
- return katex.renderToString(latex, {
7
- throwOnError: false,
8
- displayMode: false,
9
- output: 'html',
10
- strict: false,
11
- fontSize: `${fontSize}px`
12
- });
13
- } catch (error) {
14
- console.error('LaTeX rendering error:', error);
15
- return `<span style="color: red;">LaTeX error: ${latex}</span>`;
16
- }
17
- };
18
-
19
- export const measureLatexSize = (latex: string, fontSize: number): { width: number, height: number } => {
20
- const container = document.createElement('div');
21
- container.style.position = 'absolute';
22
- container.style.visibility = 'hidden';
23
- container.style.display = 'inline-block';
24
- container.style.fontSize = `${fontSize}px`;
25
- container.style.padding = '0';
26
- container.style.margin = '0';
27
- container.style.lineHeight = '1';
28
- document.body.appendChild(container);
29
-
30
- try {
31
- katex.render(latex, container, {
32
- throwOnError: false,
33
- displayMode: false,
34
- output: 'html',
35
- strict: false
36
- });
37
-
38
- const width = container.offsetWidth + 20;
39
- const height = container.offsetHeight + 10;
40
-
41
- return { width, height };
42
- } catch (error) {
43
- console.error('LaTeX measurement error:', error);
44
- return { width: 200, height: 50 };
45
- } finally {
46
- document.body.removeChild(container);
47
- }
48
- };