@jackuait/blok 0.10.3 → 0.10.5

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 (286) hide show
  1. package/dist/blok.mjs +2 -2
  2. package/dist/chunks/{blok-3wc3aInM.mjs → blok-DSP5zC_s.mjs} +2374 -2159
  3. package/dist/chunks/{constants-Bp622jic.mjs → constants-B0Tns0mh.mjs} +318 -227
  4. package/dist/chunks/{i18next-loader-CDnSPae_.mjs → i18next-loader-D_v0EmDP.mjs} +1 -1
  5. package/dist/chunks/{lightweight-i18n-DZmo8dAI.mjs → lightweight-i18n-DTYoSr_o.mjs} +21 -0
  6. package/dist/{messages-bkGniiaz.mjs → chunks/messages--X-ZzYSb.mjs} +24 -3
  7. package/dist/chunks/{messages-DrfRYiM32.mjs → messages--tSqMtkx2.mjs} +24 -3
  8. package/dist/chunks/{messages-CW35K1pq.mjs → messages-3w1e16T2.mjs} +25 -4
  9. package/dist/chunks/{messages-BHMiK51R.mjs → messages-B5BaA6AV.mjs} +24 -3
  10. package/dist/{messages-DKHbt-7l2.mjs → chunks/messages-B5ZVSxG4.mjs} +25 -4
  11. package/dist/chunks/{messages-kGmxkeFH.mjs → messages-B9PUHq4t.mjs} +24 -3
  12. package/dist/chunks/{messages-BlxwW7M6.mjs → messages-BAxCMQgm.mjs} +24 -3
  13. package/dist/chunks/{messages-BRAoJpOu.mjs → messages-BDhWRN6Y.mjs} +25 -4
  14. package/dist/{messages-CdEASHDp2.mjs → chunks/messages-BGpx2ecP2.mjs} +24 -3
  15. package/dist/{messages-C6ONf71u2.mjs → chunks/messages-BHUQs6402.mjs} +24 -3
  16. package/dist/{messages-BrOWqNCu2.mjs → chunks/messages-BIXCvkbG.mjs} +24 -3
  17. package/dist/chunks/{messages-DMoERagV2.mjs → messages-BKeL1zk-2.mjs} +24 -3
  18. package/dist/chunks/{messages-DjvaiALg2.mjs → messages-Bedied602.mjs} +24 -3
  19. package/dist/chunks/{messages-oMc7qugU2.mjs → messages-Bg8uBETk2.mjs} +24 -3
  20. package/dist/{messages-CRNogopy2.mjs → chunks/messages-Blg1X6YL.mjs} +24 -3
  21. package/dist/chunks/{messages-euM2m3wQ.mjs → messages-BmDMK9Dg.mjs} +24 -3
  22. package/dist/{messages-D3rwCtKn.mjs → chunks/messages-BmDw1fZF2.mjs} +24 -3
  23. package/dist/{messages-qfvXgPpu2.mjs → chunks/messages-BqKIunt0.mjs} +24 -3
  24. package/dist/chunks/{messages-BfAcUavP.mjs → messages-BszGK0ox.mjs} +26 -5
  25. package/dist/{messages-BckDk9aq2.mjs → chunks/messages-Buwm8xrX2.mjs} +24 -3
  26. package/dist/chunks/{messages-CC_noR8y.mjs → messages-BvoElEE_.mjs} +24 -3
  27. package/dist/chunks/{messages-zt6zdYWh.mjs → messages-ByHXyVi6.mjs} +23 -2
  28. package/dist/chunks/{messages-DBMaLL8b2.mjs → messages-C9HPruJj2.mjs} +24 -3
  29. package/dist/{messages-DQORja0D.mjs → chunks/messages-CDtmVcBc.mjs} +26 -5
  30. package/dist/chunks/{messages-wl8YrvGG.mjs → messages-CEWKcK-Q.mjs} +23 -2
  31. package/dist/chunks/{messages-CZSlfnkO2.mjs → messages-CRLvJfNU2.mjs} +22 -1
  32. package/dist/chunks/{messages-BRoa9tGl.mjs → messages-CXjSY5Fy.mjs} +24 -3
  33. package/dist/{messages-BesJaI6A.mjs → chunks/messages-CdWS0hvw.mjs} +29 -8
  34. package/dist/{messages-JNrYldAa2.mjs → chunks/messages-Cic0Y1z6.mjs} +24 -3
  35. package/dist/chunks/{messages-CTCe595D2.mjs → messages-CmGixnBb2.mjs} +24 -3
  36. package/dist/chunks/{messages-JSQjKQ8I.mjs → messages-CrCalcxK.mjs} +24 -3
  37. package/dist/chunks/{messages-DUr9WAkD.mjs → messages-D3wlt9eE.mjs} +22 -1
  38. package/dist/{messages-CdduYw-q.mjs → chunks/messages-D5NWb-B9.mjs} +23 -2
  39. package/dist/chunks/{messages-CRF7nNrO.mjs → messages-D5lY22F4.mjs} +24 -3
  40. package/dist/{messages-_PLyRfVw.mjs → chunks/messages-DGYjioKM.mjs} +26 -5
  41. package/dist/{messages-Che99vKP.mjs → chunks/messages-DIH2U0ZD.mjs} +24 -3
  42. package/dist/chunks/{messages-BJ-vT1SU2.mjs → messages-DIUegdwv2.mjs} +24 -3
  43. package/dist/chunks/{messages-BgM91Lxm2.mjs → messages-DKZHsfXy2.mjs} +25 -4
  44. package/dist/chunks/{messages-1_6UkKLS.mjs → messages-DKjUcEfs.mjs} +24 -3
  45. package/dist/{messages-C1vc5584.mjs → chunks/messages-DOPK-Xif2.mjs} +24 -3
  46. package/dist/chunks/{messages-DB_-5Xln.mjs → messages-DSMFg80t.mjs} +25 -4
  47. package/dist/{messages-A96tMxeU.mjs → chunks/messages-DT6eM0I22.mjs} +22 -1
  48. package/dist/chunks/{messages-DUeiPraX.mjs → messages-DZTQIS9F.mjs} +24 -3
  49. package/dist/{messages-apA6BStA.mjs → chunks/messages-DalLfJNC.mjs} +24 -3
  50. package/dist/chunks/{messages-QilfinOn2.mjs → messages-DggaAu362.mjs} +24 -3
  51. package/dist/chunks/{messages-CxxyR4vY.mjs → messages-DgoJDjHm.mjs} +24 -3
  52. package/dist/chunks/{messages-ElIGUi0O2.mjs → messages-DhgEjOjw2.mjs} +25 -4
  53. package/dist/chunks/{messages-sDdNf8O9.mjs → messages-Dk-d3mmM.mjs} +22 -1
  54. package/dist/{messages-BE_z-zrb.mjs → chunks/messages-DlTG06-j.mjs} +25 -4
  55. package/dist/chunks/{messages-C0IFfhnp.mjs → messages-Dle6Hv30.mjs} +22 -1
  56. package/dist/{messages-DBhvm8NK.mjs → chunks/messages-DoD9yWMu2.mjs} +22 -1
  57. package/dist/{messages-D0lLw9KM.mjs → chunks/messages-DpnhbDoK2.mjs} +24 -3
  58. package/dist/{messages-DODrhcop.mjs → chunks/messages-DqGv-lj6.mjs} +24 -3
  59. package/dist/{messages-CZbcxlZt2.mjs → chunks/messages-DslnYTHU2.mjs} +24 -3
  60. package/dist/chunks/{messages-Du2BffA7.mjs → messages-IjEU7XAq.mjs} +24 -3
  61. package/dist/chunks/{messages-BK8Cp2d0.mjs → messages-JRjsu_bC.mjs} +24 -3
  62. package/dist/chunks/{messages-CszmHAvQ.mjs → messages-PkO_43Eb.mjs} +24 -3
  63. package/dist/chunks/{messages-CQBo3lmL2.mjs → messages-W2zpf0Mu2.mjs} +24 -3
  64. package/dist/{messages-Do7Xjy0n.mjs → chunks/messages-adEATsLK.mjs} +23 -2
  65. package/dist/{messages-E8NjqzWq2.mjs → chunks/messages-b8XGiuAa2.mjs} +23 -2
  66. package/dist/{messages-CisR4PNV.mjs → chunks/messages-bteFpBer.mjs} +24 -3
  67. package/dist/chunks/{messages-DVr1sqfI2.mjs → messages-cR48x2Aq2.mjs} +24 -3
  68. package/dist/chunks/{messages-4Ck88DYZ2.mjs → messages-f-YBthdd2.mjs} +24 -3
  69. package/dist/{messages-Xc0KUbYl.mjs → chunks/messages-fUpUvsRN.mjs} +25 -4
  70. package/dist/{messages-Be_2RHZD.mjs → chunks/messages-gNK1EolD.mjs} +25 -4
  71. package/dist/{messages-DfFZ6Yj5.mjs → chunks/messages-vqR3RAU4.mjs} +24 -3
  72. package/dist/{messages-BbJ7ZXY8.mjs → chunks/messages-wSX_4fK1.mjs} +24 -3
  73. package/dist/chunks/{tools-BC1jRfoS.mjs → tools-KZmhOKEo.mjs} +3407 -1299
  74. package/dist/full.mjs +10 -10
  75. package/dist/locales.mjs +88 -67
  76. package/dist/{chunks/messages-D22e9h7V2.mjs → messages--zgIPYtb.mjs} +22 -1
  77. package/dist/{chunks/messages-EDMC5ukV.mjs → messages-4jULpVvw.mjs} +24 -3
  78. package/dist/{messages-DK6dA0O2.mjs → messages-AbwSlRf_.mjs} +26 -5
  79. package/dist/{messages-Ddq3Ce3E2.mjs → messages-B-dl7Pwb2.mjs} +25 -4
  80. package/dist/{chunks/messages-BAlZjPcl.mjs → messages-B3rCRhb1.mjs} +24 -3
  81. package/dist/{chunks/messages-DSrdy9Nw2.mjs → messages-B73L8nKc2.mjs} +24 -3
  82. package/dist/{messages-ClGvlFcH2.mjs → messages-BAjsD2Vm2.mjs} +24 -3
  83. package/dist/{chunks/messages-BbEW9bQz.mjs → messages-BBodSb5B2.mjs} +24 -3
  84. package/dist/{messages-DopaMHC42.mjs → messages-BTpn6H9x2.mjs} +22 -1
  85. package/dist/{messages-D0005ti32.mjs → messages-BWb0GjTj2.mjs} +24 -3
  86. package/dist/{messages-Q5sQeVap2.mjs → messages-BbCgnri12.mjs} +24 -3
  87. package/dist/{chunks/messages-BeGZqQwz.mjs → messages-Bfon_Y3q.mjs} +25 -4
  88. package/dist/{messages-CTTmWn4Y2.mjs → messages-BhOtow9J2.mjs} +24 -3
  89. package/dist/{messages-98nQiC7t2.mjs → messages-BoB7m0uu2.mjs} +25 -4
  90. package/dist/{messages-CT-Kdas6.mjs → messages-BsOZPjeg.mjs} +24 -3
  91. package/dist/{messages-D6VIFnSW.mjs → messages-BuzKUYPV.mjs} +24 -3
  92. package/dist/{chunks/messages-DEBy3nuJ2.mjs → messages-BzOKMdMQ.mjs} +22 -1
  93. package/dist/{messages-DOGbHYv-2.mjs → messages-C2H3PeQW2.mjs} +24 -3
  94. package/dist/{chunks/messages-DxHh0O8j2.mjs → messages-C5KNzSTk.mjs} +24 -3
  95. package/dist/{chunks/messages-eFd4YYzt.mjs → messages-CIDYjE6l.mjs} +24 -3
  96. package/dist/{messages-Dqu4aX9s.mjs → messages-CJmyF5ye.mjs} +23 -2
  97. package/dist/{messages-qbKjjvgd2.mjs → messages-CTwiYb2a2.mjs} +22 -1
  98. package/dist/{chunks/messages-CPBN4zWc.mjs → messages-CUHoJaDO.mjs} +24 -3
  99. package/dist/{messages-D05jqBIa2.mjs → messages-CVeuqUjQ2.mjs} +24 -3
  100. package/dist/{chunks/messages-BMD37y3q2.mjs → messages-CW75b4sz.mjs} +24 -3
  101. package/dist/{chunks/messages-CD_MnBln.mjs → messages-CXd7YeWV.mjs} +24 -3
  102. package/dist/{messages-DVL0KZE5.mjs → messages-CjlYkunD.mjs} +24 -3
  103. package/dist/{chunks/messages-BKN3YVIj.mjs → messages-CrGx64yd.mjs} +25 -4
  104. package/dist/{messages-BK_LsgY4.mjs → messages-Ct0jbgHd.mjs} +24 -3
  105. package/dist/{messages-BmH2cQHQ.mjs → messages-CtKrCYD1.mjs} +24 -3
  106. package/dist/{messages-neGD3WGq.mjs → messages-Cu2eLjfJ.mjs} +24 -3
  107. package/dist/{messages-Brd5R-da2.mjs → messages-CvwQ-fK_2.mjs} +24 -3
  108. package/dist/{messages-uwK7ktqk.mjs → messages-Cy6UYN9b.mjs} +22 -1
  109. package/dist/{chunks/messages-ChK7v1PV.mjs → messages-D01vnfA9.mjs} +24 -3
  110. package/dist/{messages-DpwMKDV0.mjs → messages-D0BAV45W.mjs} +24 -3
  111. package/dist/{messages-LMaR2_bE.mjs → messages-D5LGpdga.mjs} +24 -3
  112. package/dist/{messages-BcVB3osF.mjs → messages-DATK4ftv.mjs} +25 -4
  113. package/dist/{chunks/messages-Bz0-KNEB.mjs → messages-DEl77Urv2.mjs} +25 -4
  114. package/dist/{messages-DSmxJWju2.mjs → messages-DHNjM2nv2.mjs} +22 -1
  115. package/dist/{messages-DYuD5-rO.mjs → messages-DMW8b_sO.mjs} +24 -3
  116. package/dist/{chunks/messages-DPzHD51Y.mjs → messages-DNmgANKr.mjs} +23 -2
  117. package/dist/{chunks/messages-C1S9ztpF.mjs → messages-DQaa3cEM.mjs} +25 -4
  118. package/dist/{chunks/messages-a07QVz8U.mjs → messages-DSlD4bV9.mjs} +24 -3
  119. package/dist/{messages-DM4Gjc9h.mjs → messages-DTzbkXFt.mjs} +24 -3
  120. package/dist/{messages-LYJbLq_F.mjs → messages-DbZihxtV.mjs} +24 -3
  121. package/dist/{messages-C_Qn9SbQ.mjs → messages-DeE8fyKQ.mjs} +24 -3
  122. package/dist/{chunks/messages-Q7-4ZJLB2.mjs → messages-Dg-TpO2S2.mjs} +24 -3
  123. package/dist/{messages-BiTMwiKH.mjs → messages-DgP82LzF.mjs} +23 -2
  124. package/dist/{chunks/messages-QMOmwcZb.mjs → messages-Dh3Vt7WC.mjs} +23 -2
  125. package/dist/{messages-C0GSBBCo2.mjs → messages-DiVVyZD22.mjs} +25 -4
  126. package/dist/{chunks/messages-BONyZroH.mjs → messages-DpRnGjoe.mjs} +26 -5
  127. package/dist/{chunks/messages-8Ld7P_9j2.mjs → messages-DqZmrFaa2.mjs} +24 -3
  128. package/dist/{chunks/messages-CxiURE2X.mjs → messages-Dra9Fz5Q.mjs} +26 -5
  129. package/dist/{chunks/messages-Clku7Cf-2.mjs → messages-DuPBRgEF2.mjs} +23 -2
  130. package/dist/{messages-53w0fPZS2.mjs → messages-DuxEyAY-2.mjs} +25 -4
  131. package/dist/{chunks/messages-D7dx_6k8.mjs → messages-FmJD6lHR2.mjs} +24 -3
  132. package/dist/{chunks/messages-DTN1XGll.mjs → messages-ONielGl92.mjs} +24 -3
  133. package/dist/{chunks/messages-CIfUm1Oa.mjs → messages-RzgUqCGb2.mjs} +24 -3
  134. package/dist/{messages-DpJGbx3q.mjs → messages-U22AKGKs.mjs} +24 -3
  135. package/dist/{messages-2ZWBTerL.mjs → messages-XufeeRfO.mjs} +24 -3
  136. package/dist/{messages-CnuH-BZK2.mjs → messages-fprUm7SO2.mjs} +24 -3
  137. package/dist/{messages-C7lJg8fy2.mjs → messages-gFNdz3Tn2.mjs} +24 -3
  138. package/dist/{chunks/messages-C15z2r5U.mjs → messages-h_n5GBm6.mjs} +29 -8
  139. package/dist/{chunks/messages-CvANwuht2.mjs → messages-oOogIpsL2.mjs} +24 -3
  140. package/dist/{chunks/messages-DtoId_bw2.mjs → messages-otwg3KcK.mjs} +24 -3
  141. package/dist/{messages-D81w6AmW.mjs → messages-rDDoBMWb.mjs} +24 -3
  142. package/dist/{messages-Dnd5YSWv.mjs → messages-trQPxZIu.mjs} +24 -3
  143. package/dist/react.mjs +2 -2
  144. package/dist/tools.mjs +3 -3
  145. package/dist/vendor.LICENSE.txt +135 -0
  146. package/package.json +2 -1
  147. package/src/blok.ts +103 -0
  148. package/src/components/block/index.ts +21 -2
  149. package/src/components/block-tunes/block-tune-copy-link.ts +82 -0
  150. package/src/components/i18n/locales/am/messages.json +24 -3
  151. package/src/components/i18n/locales/ar/messages.json +24 -3
  152. package/src/components/i18n/locales/az/messages.json +24 -3
  153. package/src/components/i18n/locales/bg/messages.json +23 -2
  154. package/src/components/i18n/locales/bn/messages.json +24 -3
  155. package/src/components/i18n/locales/bs/messages.json +26 -5
  156. package/src/components/i18n/locales/cs/messages.json +25 -4
  157. package/src/components/i18n/locales/da/messages.json +25 -4
  158. package/src/components/i18n/locales/de/messages.json +24 -3
  159. package/src/components/i18n/locales/dv/messages.json +24 -3
  160. package/src/components/i18n/locales/el/messages.json +24 -3
  161. package/src/components/i18n/locales/en/messages.json +21 -0
  162. package/src/components/i18n/locales/es/messages.json +25 -4
  163. package/src/components/i18n/locales/et/messages.json +23 -2
  164. package/src/components/i18n/locales/fa/messages.json +24 -3
  165. package/src/components/i18n/locales/fi/messages.json +24 -3
  166. package/src/components/i18n/locales/fil/messages.json +29 -8
  167. package/src/components/i18n/locales/fr/messages.json +24 -3
  168. package/src/components/i18n/locales/gu/messages.json +24 -3
  169. package/src/components/i18n/locales/he/messages.json +24 -3
  170. package/src/components/i18n/locales/hi/messages.json +24 -3
  171. package/src/components/i18n/locales/hr/messages.json +26 -5
  172. package/src/components/i18n/locales/hu/messages.json +25 -4
  173. package/src/components/i18n/locales/hy/messages.json +24 -3
  174. package/src/components/i18n/locales/id/messages.json +23 -2
  175. package/src/components/i18n/locales/it/messages.json +26 -5
  176. package/src/components/i18n/locales/ja/messages.json +24 -3
  177. package/src/components/i18n/locales/ka/messages.json +24 -3
  178. package/src/components/i18n/locales/km/messages.json +22 -1
  179. package/src/components/i18n/locales/kn/messages.json +24 -3
  180. package/src/components/i18n/locales/ko/messages.json +24 -3
  181. package/src/components/i18n/locales/ku/messages.json +24 -3
  182. package/src/components/i18n/locales/lo/messages.json +22 -1
  183. package/src/components/i18n/locales/lt/messages.json +24 -3
  184. package/src/components/i18n/locales/lv/messages.json +24 -3
  185. package/src/components/i18n/locales/mk/messages.json +24 -3
  186. package/src/components/i18n/locales/ml/messages.json +24 -3
  187. package/src/components/i18n/locales/mn/messages.json +24 -3
  188. package/src/components/i18n/locales/mr/messages.json +24 -3
  189. package/src/components/i18n/locales/ms/messages.json +22 -1
  190. package/src/components/i18n/locales/my/messages.json +24 -3
  191. package/src/components/i18n/locales/ne/messages.json +24 -3
  192. package/src/components/i18n/locales/nl/messages.json +24 -3
  193. package/src/components/i18n/locales/no/messages.json +25 -4
  194. package/src/components/i18n/locales/pa/messages.json +24 -3
  195. package/src/components/i18n/locales/pl/messages.json +25 -4
  196. package/src/components/i18n/locales/ps/messages.json +24 -3
  197. package/src/components/i18n/locales/pt/messages.json +24 -3
  198. package/src/components/i18n/locales/ro/messages.json +24 -3
  199. package/src/components/i18n/locales/ru/messages.json +23 -2
  200. package/src/components/i18n/locales/sd/messages.json +24 -3
  201. package/src/components/i18n/locales/si/messages.json +24 -3
  202. package/src/components/i18n/locales/sk/messages.json +25 -4
  203. package/src/components/i18n/locales/sl/messages.json +25 -4
  204. package/src/components/i18n/locales/sq/messages.json +25 -4
  205. package/src/components/i18n/locales/sr/messages.json +24 -3
  206. package/src/components/i18n/locales/sv/messages.json +24 -3
  207. package/src/components/i18n/locales/sw/messages.json +22 -1
  208. package/src/components/i18n/locales/ta/messages.json +24 -3
  209. package/src/components/i18n/locales/te/messages.json +24 -3
  210. package/src/components/i18n/locales/th/messages.json +22 -1
  211. package/src/components/i18n/locales/tr/messages.json +24 -3
  212. package/src/components/i18n/locales/ug/messages.json +24 -3
  213. package/src/components/i18n/locales/uk/messages.json +23 -2
  214. package/src/components/i18n/locales/ur/messages.json +24 -3
  215. package/src/components/i18n/locales/vi/messages.json +22 -1
  216. package/src/components/i18n/locales/yi/messages.json +24 -3
  217. package/src/components/i18n/locales/zh/messages.json +24 -3
  218. package/src/components/icons/index.ts +65 -0
  219. package/src/components/inline-tools/inline-tool-bold.ts +10 -0
  220. package/src/components/inline-tools/inline-tool-code.ts +35 -1
  221. package/src/components/inline-tools/inline-tool-italic.ts +37 -1
  222. package/src/components/inline-tools/inline-tool-strikethrough.ts +35 -1
  223. package/src/components/inline-tools/inline-tool-underline.ts +35 -1
  224. package/src/components/inline-tools/services/bold-normalization-pass.ts +2 -4
  225. package/src/components/inline-tools/utils/formatting-range-utils.ts +83 -0
  226. package/src/components/modules/api/tools.ts +19 -0
  227. package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +7 -1
  228. package/src/components/modules/blockManager/blockManager.ts +22 -0
  229. package/src/components/modules/blockManager/event-binder.ts +12 -1
  230. package/src/components/modules/blockManager/factory.ts +4 -0
  231. package/src/components/modules/blockManager/types.ts +4 -0
  232. package/src/components/modules/blockManager/yjs-sync.ts +16 -2
  233. package/src/components/modules/paste/google-docs-preprocessor.ts +49 -3
  234. package/src/components/modules/paste/handlers/table-cells-handler.ts +12 -2
  235. package/src/components/modules/paste/index.ts +8 -4
  236. package/src/components/modules/paste/types.ts +2 -0
  237. package/src/components/modules/renderer.ts +22 -2
  238. package/src/components/modules/saver.ts +19 -1
  239. package/src/components/modules/themeManager.ts +3 -1
  240. package/src/components/modules/toolbar/blockSettings.ts +51 -1
  241. package/src/components/modules/toolbar/inline/index.ts +0 -3
  242. package/src/components/modules/tools.ts +5 -0
  243. package/src/components/modules/ui.ts +1 -0
  244. package/src/components/modules/uiControllers/controllers/keyboard.ts +79 -0
  245. package/src/components/modules/uiControllers/controllers/selection.ts +14 -2
  246. package/src/components/modules/yjs/document-store.ts +22 -0
  247. package/src/components/modules/yjs/index.ts +10 -0
  248. package/src/components/modules/yjs/serializer.ts +20 -0
  249. package/src/components/ui/toolbox.ts +0 -1
  250. package/src/components/utils/id-generator.ts +11 -0
  251. package/src/components/utils/key-icon.ts +187 -0
  252. package/src/components/utils/popover/components/hint/hint.ts +3 -1
  253. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +18 -5
  254. package/src/components/utils/popover/popover-abstract.ts +45 -0
  255. package/src/components/utils/popover/popover-desktop.ts +1 -0
  256. package/src/components/utils/popover/popover-position.ts +4 -2
  257. package/src/components/utils/popover/popover.const.ts +2 -0
  258. package/src/components/utils.ts +1 -0
  259. package/src/styles/main.css +1272 -0
  260. package/src/tools/code/index.ts +4 -0
  261. package/src/tools/database/database-backend-sync.ts +132 -0
  262. package/src/tools/database/database-board-view.ts +410 -0
  263. package/src/tools/database/database-card-drag.ts +306 -0
  264. package/src/tools/database/database-card-drawer.ts +546 -0
  265. package/src/tools/database/database-column-controls.ts +141 -0
  266. package/src/tools/database/database-column-drag.ts +262 -0
  267. package/src/tools/database/database-keyboard.ts +35 -0
  268. package/src/tools/database/database-list-row-drag.ts +245 -0
  269. package/src/tools/database/database-list-view.ts +333 -0
  270. package/src/tools/database/database-model.ts +214 -0
  271. package/src/tools/database/database-property-type-popover.ts +108 -0
  272. package/src/tools/database/database-tab-bar.ts +558 -0
  273. package/src/tools/database/database-view-popover.ts +129 -0
  274. package/src/tools/database/database-view-renderer.ts +25 -0
  275. package/src/tools/database/database-view.ts +269 -0
  276. package/src/tools/database/index.ts +1223 -0
  277. package/src/tools/database/types.ts +168 -0
  278. package/src/tools/database-row/index.ts +74 -0
  279. package/src/tools/index.ts +4 -0
  280. package/types/api/tools.d.ts +18 -0
  281. package/types/configs/blok-config.d.ts +27 -0
  282. package/types/data-formats/output-data.d.ts +13 -0
  283. package/types/index.d.ts +17 -0
  284. package/types/tools/database.d.ts +152 -0
  285. package/types/tools-entry.d.ts +7 -4
  286. package/types/utils/popover/popover.d.ts +6 -0
@@ -18,13 +18,15 @@
18
18
  "toolNames.numberedList": "נומעריקע ליסטע",
19
19
  "toolNames.todoList": "צו-טאָן ליסטע",
20
20
  "toolNames.toggleList": "אויפֿקלאַפּ ליסטע",
21
+ "toolNames.board": "טאָוול",
22
+ "toolNames.database": "דאַטאַבאַנק",
21
23
  "tools.link.addLink": "צולייגן לינק",
22
24
  "tools.link.invalidLink": "אומגילטיקער לינק",
23
25
  "tools.marker.textColor": "טעקסט",
24
26
  "tools.marker.background": "הינטערגרונט",
25
27
  "tools.marker.default": "שטאַנדאַרד",
26
- "tools.colorPicker.defaultSwatchLabel": "{default} {mode}",
27
- "tools.colorPicker.colorSwatchLabel": "{color} {mode}",
28
+ "tools.colorPicker.defaultSwatchLabel": "{mode} {default}",
29
+ "tools.colorPicker.colorSwatchLabel": "{mode} {color}",
28
30
  "tools.colorPicker.color.gray": "גרוי",
29
31
  "tools.colorPicker.color.brown": "ברוין",
30
32
  "tools.colorPicker.color.orange": "אָראַנזש",
@@ -71,6 +73,9 @@
71
73
  "tools.table.clickToAddColumn": "קליק צו צולייגן אַ נײַע שפּאַלט",
72
74
  "tools.table.dragToAddRemoveColumns": "שלעפּ צו צולייגן אָדער אויסמעקן שפּאַלטן",
73
75
  "blockSettings.delete": "אויסמעקן",
76
+ "blockSettings.copyLink": "קאָפּירן לינק צום בלאָק",
77
+ "blockSettings.lastEdited": "לעצט רעדאַקטירט",
78
+ "blockSettings.lastEditedBy": "לעצט רעדאַקטירט דורך {name}",
74
79
  "a11y.dragHandle": "שלעפּ צו באַוועגן בלאָק אָדער קליק פֿאַר מעניו",
75
80
  "a11y.dragHandleRole": "שלעפּ האַנטל",
76
81
  "a11y.dragStarted": "שלעפּט בלאָק",
@@ -143,6 +148,22 @@
143
148
  "tools.callout.emojiCategoryObjects": "זאַכן",
144
149
  "tools.callout.emojiCategorySymbols": "סימבאָלן",
145
150
  "tools.callout.emojiCategoryFlags": "פֿאָנען",
151
+ "tools.database.addCard": "צוגעבן קאַרטל",
152
+ "tools.database.newPage": "נייע בלאט",
153
+ "tools.database.addColumn": "צוגעבן שפּאַלט",
154
+ "tools.database.cardTitlePlaceholder": "ליידיק בלאַט",
155
+ "tools.database.columnTitlePlaceholder": "שפּאַלט",
156
+ "tools.database.deleteCard": "אויסמעקן קאַרטל",
157
+ "tools.database.editCardTitle": "רעדאַקטירן טיטל",
158
+ "tools.database.cardMenuLabel": "קאַרטל אָפּציעס",
159
+ "tools.database.deleteColumn": "אויסמעקן שפּאַלט",
160
+ "tools.database.emptyColumn": "קיין קארטן",
161
+ "tools.database.rowTitlePlaceholder": "ליידיק בלאַט",
162
+ "tools.database.addRow": "צוגעבן שורה",
163
+ "tools.database.newRow": "נייע שורה",
164
+ "tools.database.openRow": "עפֿענען שורה",
165
+ "tools.database.deleteRow": "אויסמעקן שורה",
166
+ "tools.database.renameColumn": "באַנענען שפּאַלט",
146
167
  "tools.callout.color": "קאָליר",
147
168
  "tools.callout.colorDefault": "שטאַנדאַרד",
148
169
  "tools.callout.colorGray": "גרוי",
@@ -184,5 +205,5 @@
184
205
  "searchTerms.code": "קאָד",
185
206
  "searchTerms.snippet": "שניצל",
186
207
  "searchTerms.program": "פּראָגראַם",
187
- "searchTerms.pre": "pre"
208
+ "searchTerms.pre": "פֿאָרמאַטירט"
188
209
  }
@@ -18,13 +18,15 @@
18
18
  "toolNames.numberedList": "有序列表",
19
19
  "toolNames.todoList": "清单",
20
20
  "toolNames.toggleList": "折叠列表",
21
+ "toolNames.board": "看板",
22
+ "toolNames.database": "数据库",
21
23
  "tools.link.addLink": "添加链接",
22
24
  "tools.link.invalidLink": "链接无效",
23
25
  "tools.marker.textColor": "文字",
24
26
  "tools.marker.background": "背景",
25
27
  "tools.marker.default": "默认",
26
- "tools.colorPicker.defaultSwatchLabel": "{default} {mode}",
27
- "tools.colorPicker.colorSwatchLabel": "{color} {mode}",
28
+ "tools.colorPicker.defaultSwatchLabel": "{mode} {default}",
29
+ "tools.colorPicker.colorSwatchLabel": "{mode} {color}",
28
30
  "tools.colorPicker.color.gray": "灰色",
29
31
  "tools.colorPicker.color.brown": "棕色",
30
32
  "tools.colorPicker.color.orange": "橙色",
@@ -53,6 +55,9 @@
53
55
  "tools.toggle.ariaLabelCollapse": "折叠",
54
56
  "tools.toggle.ariaLabelExpand": "展开",
55
57
  "blockSettings.delete": "删除",
58
+ "blockSettings.copyLink": "复制块链接",
59
+ "blockSettings.lastEdited": "上次编辑",
60
+ "blockSettings.lastEditedBy": "{name} 上次编辑",
56
61
  "a11y.dragHandle": "拖动移动或点击打开菜单",
57
62
  "a11y.dragHandleRole": "拖动控件",
58
63
  "a11y.dragStarted": "正在拖动内容块",
@@ -143,6 +148,22 @@
143
148
  "tools.callout.emojiCategoryObjects": "物品",
144
149
  "tools.callout.emojiCategorySymbols": "符号",
145
150
  "tools.callout.emojiCategoryFlags": "旗帜",
151
+ "tools.database.addCard": "添加卡片",
152
+ "tools.database.newPage": "新页面",
153
+ "tools.database.addColumn": "添加列",
154
+ "tools.database.cardTitlePlaceholder": "空白页面",
155
+ "tools.database.columnTitlePlaceholder": "列",
156
+ "tools.database.deleteCard": "删除卡片",
157
+ "tools.database.editCardTitle": "编辑标题",
158
+ "tools.database.cardMenuLabel": "卡片选项",
159
+ "tools.database.deleteColumn": "删除列",
160
+ "tools.database.emptyColumn": "没有卡片",
161
+ "tools.database.rowTitlePlaceholder": "空白页面",
162
+ "tools.database.addRow": "添加行",
163
+ "tools.database.newRow": "新行",
164
+ "tools.database.openRow": "打开行",
165
+ "tools.database.deleteRow": "删除行",
166
+ "tools.database.renameColumn": "重命名列",
146
167
  "tools.callout.color": "颜色",
147
168
  "tools.callout.colorDefault": "默认",
148
169
  "tools.callout.colorGray": "灰色",
@@ -184,5 +205,5 @@
184
205
  "searchTerms.code": "代码",
185
206
  "searchTerms.snippet": "片段",
186
207
  "searchTerms.program": "程序",
187
- "searchTerms.pre": "pre"
208
+ "searchTerms.pre": "预格式化"
188
209
  }
@@ -399,6 +399,13 @@ export const IconCopy = `
399
399
  </svg>
400
400
  `;
401
401
 
402
+ // Pencil/Rename icon
403
+ export const IconPencil = `
404
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
405
+ <path d="M12.5 5.5l2 2M4.5 13.5l8-8 2 2-8 8H4.5v-2z" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
406
+ </svg>
407
+ `;
408
+
402
409
  // Trash/Delete icon
403
410
  export const IconTrash = `
404
411
  <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -409,6 +416,15 @@ export const IconTrash = `
409
416
  </svg>
410
417
  `;
411
418
 
419
+ // Horizontal dots / more options icon
420
+ export const IconDotsHorizontal = `
421
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
422
+ <circle cx="5" cy="10" r="1.25" fill="currentColor"/>
423
+ <circle cx="10" cy="10" r="1.25" fill="currentColor"/>
424
+ <circle cx="15" cy="10" r="1.25" fill="currentColor"/>
425
+ </svg>
426
+ `;
427
+
412
428
  // Toggle Heading 1 icon (H1 with toggle arrow)
413
429
  export const IconToggleH1 = `
414
430
  <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -592,6 +608,55 @@ export const IconEmojiFlag = `
592
608
  </svg>
593
609
  `;
594
610
 
611
+ export const IconDatabase = `
612
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
613
+ <ellipse cx="10" cy="5.5" rx="6" ry="2.5" stroke="currentColor" stroke-width="1.25"/>
614
+ <path d="M4 5.5v4c0 1.38 2.69 2.5 6 2.5s6-1.12 6-2.5v-4" stroke="currentColor" stroke-width="1.25"/>
615
+ <path d="M4 9.5v4c0 1.38 2.69 2.5 6 2.5s6-1.12 6-2.5v-4" stroke="currentColor" stroke-width="1.25"/>
616
+ </svg>
617
+ `;
618
+
619
+ export const IconBoard = `
620
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
621
+ <rect x="3" y="4" width="14" height="12" rx="1.5" stroke="currentColor" stroke-width="1.25"/>
622
+ <path d="M7.5 4v12M12.5 4v12" stroke="currentColor" stroke-width="1.25"/>
623
+ <rect x="4.5" y="6" width="1.5" height="2" rx="0.5" fill="currentColor"/>
624
+ <rect x="4.5" y="9" width="1.5" height="2" rx="0.5" fill="currentColor"/>
625
+ <rect x="9" y="6" width="1.5" height="2" rx="0.5" fill="currentColor"/>
626
+ <rect x="14" y="6" width="1.5" height="2" rx="0.5" fill="currentColor"/>
627
+ </svg>
628
+ `;
629
+
630
+ export const IconGallery = `
631
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
632
+ <rect x="3" y="4" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
633
+ <rect x="11" y="4" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
634
+ <rect x="3" y="11" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
635
+ <rect x="11" y="11" width="6" height="5" rx="1" stroke="currentColor" stroke-width="1.25"/>
636
+ </svg>
637
+ `;
638
+
639
+ export const IconList = `
640
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
641
+ <circle cx="4.5" cy="6.5" r="1" fill="currentColor"/>
642
+ <circle cx="4.5" cy="10" r="1" fill="currentColor"/>
643
+ <circle cx="4.5" cy="13.5" r="1" fill="currentColor"/>
644
+ <path d="M7.5 6.5h9M7.5 10h9M7.5 13.5h9" stroke="currentColor" stroke-width="1.25" stroke-linecap="round"/>
645
+ </svg>
646
+ `;
647
+
648
+ // Calendar icon (date property type)
649
+ export const IconCalendar = `
650
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
651
+ <rect x="3" y="4.5" width="14" height="12" rx="1.5" stroke="currentColor" stroke-width="1.25"/>
652
+ <path d="M3 8.5h14" stroke="currentColor" stroke-width="1.25" stroke-linecap="round"/>
653
+ <path d="M7 3v3M13 3v3" stroke="currentColor" stroke-width="1.25" stroke-linecap="round"/>
654
+ <circle cx="7" cy="12" r="1" fill="currentColor"/>
655
+ <circle cx="10" cy="12" r="1" fill="currentColor"/>
656
+ <circle cx="13" cy="12" r="1" fill="currentColor"/>
657
+ </svg>
658
+ `;
659
+
595
660
  // Merge cells icon (two cells merging into one)
596
661
  export const IconMergeCells = `
597
662
  <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -16,6 +16,7 @@ import {
16
16
  import {
17
17
  isRangeFormatted,
18
18
  collectFormattingAncestors,
19
+ extendRangeToTrailingWhitespace,
19
20
  } from './utils/formatting-range-utils';
20
21
 
21
22
  /**
@@ -212,6 +213,7 @@ export class BoldInlineTool implements InlineTool {
212
213
  * @param range - The Range object containing the selection to wrap
213
214
  */
214
215
  private wrapWithBold(range: Range): void {
216
+ extendRangeToTrailingWhitespace(range);
215
217
  const html = this.getRangeHtmlWithoutBold(range);
216
218
  const insertedRange = this.replaceRangeWithHtml(range, `<strong>${html}</strong>`);
217
219
  const selection = window.getSelection();
@@ -308,8 +310,16 @@ export class BoldInlineTool implements InlineTool {
308
310
  BoldNormalizationPass.normalizeAroundSelection(selection);
309
311
 
310
312
  boldAncestors.forEach((element) => {
313
+ if (!element.isConnected) {
314
+ return;
315
+ }
316
+
311
317
  if (isElementEmpty(element)) {
312
318
  element.remove();
319
+ } else if (element.textContent.trim().length === 0) {
320
+ // Element contains only whitespace — unwrap it to preserve the whitespace
321
+ // as plain text while removing the bold formatting
322
+ this.unwrapElement(element);
313
323
  }
314
324
  });
315
325
 
@@ -6,6 +6,7 @@ import {
6
6
  isRangeFormatted,
7
7
  findFormattingAncestor,
8
8
  collectFormattingAncestors,
9
+ extendRangeToTrailingWhitespace,
9
10
  } from './utils/formatting-range-utils';
10
11
 
11
12
  /**
@@ -168,13 +169,46 @@ export class CodeInlineTool implements InlineTool {
168
169
  * @param range - The Range object containing the selection to wrap
169
170
  */
170
171
  private wrapWithCode(range: Range): void {
172
+ extendRangeToTrailingWhitespace(range);
171
173
  const html = this.getRangeHtmlWithoutCode(range);
172
174
  const insertedRange = this.replaceRangeWithHtml(range, `<code>${html}</code>`);
173
175
  const selection = window.getSelection();
174
176
 
175
177
  if (selection && insertedRange) {
178
+ const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
179
+ const newRange = document.createRange();
180
+
181
+ if (wrappedElement) {
182
+ this.normalizeNbspInElement(wrappedElement);
183
+ newRange.selectNodeContents(wrappedElement);
184
+ } else {
185
+ newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
186
+ newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
187
+ }
188
+
176
189
  selection.removeAllRanges();
177
- selection.addRange(insertedRange);
190
+ selection.addRange(newRange);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element
196
+ * @param element - The element to normalize
197
+ */
198
+ private normalizeNbspInElement(element: HTMLElement): void {
199
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
200
+
201
+ while (true) {
202
+ const node = walker.nextNode();
203
+
204
+ if (node === null) {
205
+ break;
206
+ }
207
+ if (!node.textContent?.includes('\u00A0')) {
208
+ continue;
209
+ }
210
+
211
+ node.textContent = node.textContent.replace(/\u00A0/g, ' ');
178
212
  }
179
213
  }
180
214
 
@@ -7,6 +7,7 @@ import {
7
7
  findFormattingAncestor,
8
8
  hasFormattingAncestor,
9
9
  collectFormattingAncestors,
10
+ extendRangeToTrailingWhitespace,
10
11
  } from './utils/formatting-range-utils';
11
12
 
12
13
  /**
@@ -172,13 +173,48 @@ export class ItalicInlineTool implements InlineTool {
172
173
  * @param range - The Range object containing the selection to wrap
173
174
  */
174
175
  private wrapWithItalic(range: Range): void {
176
+ extendRangeToTrailingWhitespace(range);
175
177
  const html = this.getRangeHtmlWithoutItalic(range);
176
178
  const insertedRange = this.replaceRangeWithHtml(range, `<i>${html}</i>`);
177
179
  const selection = window.getSelection();
178
180
 
179
181
  if (selection && insertedRange) {
182
+ const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
183
+ const newRange = document.createRange();
184
+
185
+ if (wrappedElement) {
186
+ this.normalizeNbspInElement(wrappedElement);
187
+ newRange.selectNodeContents(wrappedElement);
188
+ } else {
189
+ newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
190
+ newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
191
+ }
192
+
180
193
  selection.removeAllRanges();
181
- selection.addRange(insertedRange);
194
+ selection.addRange(newRange);
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element.
200
+ * The browser's contenteditable engine will re-insert nbsp where needed for rendering
201
+ * (e.g. trailing spaces, consecutive spaces) on the next input event.
202
+ * @param element - The element to normalize
203
+ */
204
+ private normalizeNbspInElement(element: HTMLElement): void {
205
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
206
+
207
+ while (true) {
208
+ const node = walker.nextNode();
209
+
210
+ if (node === null) {
211
+ break;
212
+ }
213
+ if (!node.textContent?.includes('\u00A0')) {
214
+ continue;
215
+ }
216
+
217
+ node.textContent = node.textContent.replace(/\u00A0/g, ' ');
182
218
  }
183
219
  }
184
220
 
@@ -7,6 +7,7 @@ import {
7
7
  findFormattingAncestor,
8
8
  hasFormattingAncestor,
9
9
  collectFormattingAncestors,
10
+ extendRangeToTrailingWhitespace,
10
11
  } from './utils/formatting-range-utils';
11
12
 
12
13
  /**
@@ -169,13 +170,46 @@ export class StrikethroughInlineTool implements InlineTool {
169
170
  * @param range - The Range object containing the selection to wrap
170
171
  */
171
172
  private wrapWithStrikethrough(range: Range): void {
173
+ extendRangeToTrailingWhitespace(range);
172
174
  const html = this.getRangeHtmlWithoutStrikethrough(range);
173
175
  const insertedRange = this.replaceRangeWithHtml(range, `<s>${html}</s>`);
174
176
  const selection = window.getSelection();
175
177
 
176
178
  if (selection && insertedRange) {
179
+ const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
180
+ const newRange = document.createRange();
181
+
182
+ if (wrappedElement) {
183
+ this.normalizeNbspInElement(wrappedElement);
184
+ newRange.selectNodeContents(wrappedElement);
185
+ } else {
186
+ newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
187
+ newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
188
+ }
189
+
177
190
  selection.removeAllRanges();
178
- selection.addRange(insertedRange);
191
+ selection.addRange(newRange);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element
197
+ * @param element - The element to normalize
198
+ */
199
+ private normalizeNbspInElement(element: HTMLElement): void {
200
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
201
+
202
+ while (true) {
203
+ const node = walker.nextNode();
204
+
205
+ if (node === null) {
206
+ break;
207
+ }
208
+ if (!node.textContent?.includes('\u00A0')) {
209
+ continue;
210
+ }
211
+
212
+ node.textContent = node.textContent.replace(/\u00A0/g, ' ');
179
213
  }
180
214
  }
181
215
 
@@ -7,6 +7,7 @@ import {
7
7
  findFormattingAncestor,
8
8
  hasFormattingAncestor,
9
9
  collectFormattingAncestors,
10
+ extendRangeToTrailingWhitespace,
10
11
  } from './utils/formatting-range-utils';
11
12
 
12
13
  /**
@@ -169,13 +170,46 @@ export class UnderlineInlineTool implements InlineTool {
169
170
  * @param range - The Range object containing the selection to wrap
170
171
  */
171
172
  private wrapWithUnderline(range: Range): void {
173
+ extendRangeToTrailingWhitespace(range);
172
174
  const html = this.getRangeHtmlWithoutUnderline(range);
173
175
  const insertedRange = this.replaceRangeWithHtml(range, `<u>${html}</u>`);
174
176
  const selection = window.getSelection();
175
177
 
176
178
  if (selection && insertedRange) {
179
+ const wrappedElement = insertedRange.startContainer.childNodes[insertedRange.startOffset] as HTMLElement | undefined;
180
+ const newRange = document.createRange();
181
+
182
+ if (wrappedElement) {
183
+ this.normalizeNbspInElement(wrappedElement);
184
+ newRange.selectNodeContents(wrappedElement);
185
+ } else {
186
+ newRange.setStart(insertedRange.startContainer, insertedRange.startOffset);
187
+ newRange.setEnd(insertedRange.endContainer, insertedRange.endOffset);
188
+ }
189
+
177
190
  selection.removeAllRanges();
178
- selection.addRange(insertedRange);
191
+ selection.addRange(newRange);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Replace non-breaking spaces (\u00A0) with regular spaces in all text nodes of an element
197
+ * @param element - The element to normalize
198
+ */
199
+ private normalizeNbspInElement(element: HTMLElement): void {
200
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
201
+
202
+ while (true) {
203
+ const node = walker.nextNode();
204
+
205
+ if (node === null) {
206
+ break;
207
+ }
208
+ if (!node.textContent?.includes('\u00A0')) {
209
+ continue;
210
+ }
211
+
212
+ node.textContent = node.textContent.replace(/\u00A0/g, ' ');
179
213
  }
180
214
  }
181
215
 
@@ -201,7 +201,7 @@ export class BoldNormalizationPass {
201
201
  }
202
202
 
203
203
  /**
204
- * Replace non-breaking spaces with regular spaces in a text node
204
+ * Replace non-breaking spaces with regular spaces in a text node.
205
205
  * @param textNode - The text node to process
206
206
  */
207
207
  private replaceNbspInTextNode(textNode: Text): void {
@@ -211,10 +211,8 @@ export class BoldNormalizationPass {
211
211
  return;
212
212
  }
213
213
 
214
- const normalizedText = text.replace(/\u00A0/g, ' ');
215
-
216
214
  // eslint-disable-next-line no-param-reassign
217
- textNode.textContent = normalizedText;
215
+ textNode.textContent = text.replace(/\u00A0/g, ' ');
218
216
  }
219
217
 
220
218
  /**
@@ -132,6 +132,89 @@ export const isRangeFormatted = (
132
132
  return textNodes.every((textNode) => hasFormattingAncestor(textNode, predicate));
133
133
  };
134
134
 
135
+ /**
136
+ * Walk down the `lastChild` chain of `node` and return the deepest last
137
+ * descendant that is a text node, or `null` if none exists.
138
+ */
139
+ const findDeepestLastTextNode = (node: Node): Text | null => {
140
+ const last = node.lastChild;
141
+
142
+ if (last === null) {
143
+ return node.nodeType === Node.TEXT_NODE ? (node as Text) : null;
144
+ }
145
+
146
+ return findDeepestLastTextNode(last);
147
+ };
148
+
149
+ /**
150
+ * Extend the range to include any trailing whitespace characters that browsers
151
+ * exclude from selections (e.g. Ctrl+A on loaded text stops before trailing spaces).
152
+ *
153
+ * When text is loaded via innerHTML (e.g. `"hello "`), browsers keep trailing
154
+ * regular spaces (char 32) as-is in the text node, but Ctrl+A / selectAll
155
+ * in Chromium and WebKit places the range end *before* those trailing spaces.
156
+ * This means `range.cloneContents()` omits them, so formatting operations
157
+ * (bold, italic, etc.) silently drop any trailing whitespace.
158
+ *
159
+ * By contrast, text typed by the user is stored internally with a non-breaking
160
+ * space (`\u00A0`) which is always included in the selection.
161
+ *
162
+ * This function mutates the range in place to cover those excluded trailing
163
+ * spaces, so downstream formatting can include them.
164
+ *
165
+ * Two browser behaviours are handled:
166
+ * 1. `endContainer` is a text node — the range ends mid-text, so check whether
167
+ * the remaining characters are all whitespace and extend if so.
168
+ * 2. `endContainer` is an element node — Ctrl+A / selectAll in Chromium and
169
+ * WebKit places the range end on the element itself with `endOffset` equal
170
+ * to the child count (i.e. after all children). In this case the last child
171
+ * text node may still have trailing whitespace that was excluded; walk to
172
+ * the deepest last text node and extend the range to its end if it ends with
173
+ * only whitespace after the current offset (or fully consists of whitespace).
174
+ *
175
+ * @param range - The range to extend (mutated in place)
176
+ */
177
+ export const extendRangeToTrailingWhitespace = (range: Range): void => {
178
+ const endContainer = range.endContainer;
179
+
180
+ if (endContainer.nodeType === Node.TEXT_NODE) {
181
+ const text = endContainer.textContent ?? '';
182
+ const endOffset = range.endOffset;
183
+
184
+ if (endOffset >= text.length) {
185
+ return;
186
+ }
187
+
188
+ // Check whether all characters between endOffset and end of text node are whitespace
189
+ const trailingSlice = text.slice(endOffset);
190
+
191
+ if (trailingSlice.length > 0 && trailingSlice.trim().length === 0) {
192
+ range.setEnd(endContainer, text.length);
193
+ }
194
+
195
+ return;
196
+ }
197
+
198
+ // endContainer is an element node (the common case for Ctrl+A on loaded content).
199
+ // Walk to the deepest last text node within it to find any trailing whitespace.
200
+ const lastTextNode = findDeepestLastTextNode(endContainer);
201
+
202
+ if (lastTextNode === null) {
203
+ return;
204
+ }
205
+
206
+ const text = lastTextNode.textContent ?? '';
207
+
208
+ if (text.length === 0) {
209
+ return;
210
+ }
211
+
212
+ // Extend range if the text ends with one or more whitespace characters.
213
+ if (/\s+$/.test(text)) {
214
+ range.setEnd(lastTextNode, text.length);
215
+ }
216
+ };
217
+
135
218
  /**
136
219
  * Collect all unique formatting ancestors within a range
137
220
  * @param range - The range to search within
@@ -11,6 +11,25 @@ export class ToolsAPI extends Module {
11
11
  public get methods(): ToolsAPIInterface {
12
12
  return {
13
13
  getBlockTools: () => Array.from(this.Blok.Tools.blockTools.values()),
14
+ getToolsConfig: () => {
15
+ const result: ReturnType<ToolsAPIInterface['getToolsConfig']> = {
16
+ tools: this.config.tools,
17
+ };
18
+
19
+ if (this.config.inlineToolbar !== undefined) {
20
+ result.inlineToolbar = this.config.inlineToolbar;
21
+ }
22
+
23
+ if (this.config.tunes !== undefined) {
24
+ result.tunes = this.config.tunes;
25
+ }
26
+
27
+ if (this.config.theme !== undefined) {
28
+ result.theme = this.config.theme;
29
+ }
30
+
31
+ return result;
32
+ },
14
33
  };
15
34
  }
16
35
  }
@@ -94,7 +94,13 @@ export class KeyboardNavigation extends BlockEventComposer {
94
94
  const isNavigated = event.shiftKey ? Caret.navigatePrevious(true) : Caret.navigateNext(true);
95
95
 
96
96
  /**
97
- * If we have next Block/input to focus, then focus it. Otherwise, leave native Tab behaviour
97
+ * Prevent default Tab behaviour only when navigation occurred within the
98
+ * editor. When navigateNext/navigatePrevious returns false (no next/prev
99
+ * block), allow the browser default so focus can leave the editor to
100
+ * external elements (e.g. inputs after the editor).
101
+ *
102
+ * Nested editors (e.g. database card drawer) are safe because the table
103
+ * cell guard above returns early, and those editors handle Tab themselves.
98
104
  */
99
105
  if (isNavigated) {
100
106
  event.preventDefault();
@@ -300,6 +300,17 @@ export class BlockManager extends Module {
300
300
  eventsDispatcher: this.eventsDispatcher,
301
301
  getBlockIndex: (block) => this.repository.getBlockIndex(block),
302
302
  onBlockMutated: this.blockDidMutated.bind(this),
303
+ shouldHandleEvent: (event: Event) => {
304
+ const target = event.target;
305
+
306
+ if (target instanceof Element) {
307
+ const closestEditor = target.closest('[data-blok-testid="blok-editor"]');
308
+
309
+ return closestEditor === null || closestEditor === this.Blok.UI.nodes.wrapper;
310
+ }
311
+
312
+ return true;
313
+ },
303
314
  });
304
315
 
305
316
  // Initialize factory
@@ -438,6 +449,8 @@ export class BlockManager extends Module {
438
449
  parentId?: string;
439
450
  contentIds?: string[];
440
451
  bindEventsImmediately?: boolean;
452
+ lastEditedAt?: number;
453
+ lastEditedBy?: string | null;
441
454
  }): Block {
442
455
  return this.factory.composeBlock(options);
443
456
  }
@@ -1086,6 +1099,11 @@ export class BlockManager extends Module {
1086
1099
  // Also skip if a pointer drag is active — the browser can mutate contenteditable DOM across
1087
1100
  // cell boundaries during a drag, and we must not write that corrupted state to Yjs.
1088
1101
  if (mutationType === BlockChangedMutationType && !this.yjsSync.isSyncingFromYjs && !this._isPointerDragActive) {
1102
+ // eslint-disable-next-line no-param-reassign
1103
+ block.lastEditedAt = Date.now();
1104
+ // eslint-disable-next-line no-param-reassign
1105
+ block.lastEditedBy = this.config.user?.name ?? null;
1106
+
1089
1107
  void this.syncBlockDataToYjs(block);
1090
1108
  }
1091
1109
 
@@ -1140,5 +1158,9 @@ export class BlockManager extends Module {
1140
1158
  for (const [key, value] of Object.entries(savedData.data)) {
1141
1159
  this.Blok.YjsManager.updateBlockData(block.id, key, value);
1142
1160
  }
1161
+
1162
+ if (block.lastEditedAt !== undefined) {
1163
+ this.Blok.YjsManager.updateBlockMetadata(block.id, block.lastEditedAt, block.lastEditedBy);
1164
+ }
1143
1165
  }
1144
1166
  }