@allbluecn/web-app 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (949) hide show
  1. package/package.json +29 -180
  2. package/src/__tests__/components/reference-inline.test.tsx +1 -1
  3. package/src/__tests__/setup.ts +1 -1
  4. package/src/__tests__/tag-badge.test.tsx +1 -1
  5. package/src/components/agents/schemas/agent-form.ts +1 -1
  6. package/src/components/animated-icons/layout-panel-top.tsx +1 -1
  7. package/src/components/auth/auth-layout.tsx +1 -1
  8. package/src/components/billing/__tests__/use-resource.test.tsx +1 -1
  9. package/src/components/billing/checkout-redirect.ts +1 -1
  10. package/src/components/billing/feature-gate.tsx +1 -1
  11. package/src/components/billing/subscription-provider.tsx +2 -2
  12. package/src/components/billing/use-feature.ts +1 -1
  13. package/src/components/billing/use-resource.ts +1 -1
  14. package/src/components/changelog/changelog-page.tsx +1 -1
  15. package/src/components/chat/ai-chat-history/utils/group-conversations.ts +1 -1
  16. package/src/components/chat/ai-citations/utils.ts +1 -1
  17. package/src/components/command/command-palette.tsx +1 -1
  18. package/src/components/forms/hooks/use-form-submit.ts +1 -1
  19. package/src/components/forms/schemas/common.ts +1 -1
  20. package/src/components/knowledge/editor/doc-header.tsx +4 -4
  21. package/src/components/knowledge/editor/knowledge-doc-editor.tsx +1 -1
  22. package/src/components/knowledge-public/knowledge-card.tsx +2 -2
  23. package/src/components/knowledge-public/knowledge-form-dialog.tsx +1 -1
  24. package/src/components/knowledge-public/sharers-avatar-group.tsx +1 -1
  25. package/src/components/layout/sidebar-03/__tests__/nav-workspace.test.tsx +1 -1
  26. package/src/components/layout/sidebar-03/nav-main.tsx +1 -1
  27. package/src/components/layout/sidebar-03/nav-notifications.tsx +1 -1
  28. package/src/components/layout/sidebar-03/user-menu.tsx +2 -2
  29. package/src/components/magicui/animated-theme-toggler.tsx +1 -1
  30. package/src/components/magicui/grid-pattern.tsx +1 -1
  31. package/src/components/mindmap/mindmap-doc-editor.tsx +4 -4
  32. package/src/components/notifications/settings-notifications.tsx +2 -2
  33. package/src/components/notifications/team-notifications.tsx +1 -1
  34. package/src/components/project/invite-member-dialog.tsx +1 -1
  35. package/src/components/project/project-activity-list.tsx +2 -2
  36. package/src/components/project/project-member-manager.tsx +1 -1
  37. package/src/components/project/schemas/create-project.ts +1 -1
  38. package/src/components/prototype/block/index.ts +22 -0
  39. package/src/components/prototype/component/Button.tsx +49 -0
  40. package/src/components/prototype/component/Card.tsx +49 -0
  41. package/src/components/prototype/component/GenericPrototype.tsx +42 -0
  42. package/src/components/prototype/component/Image.tsx +51 -0
  43. package/src/components/prototype/component/Input.tsx +45 -0
  44. package/src/components/prototype/component/Text.tsx +43 -0
  45. package/src/components/prototype/component/index.ts +102 -0
  46. package/src/components/prototype/component-preview.tsx +137 -0
  47. package/src/components/prototype/imported/.gitkeep +0 -0
  48. package/src/components/prototype/imported-registry.ts +25 -0
  49. package/src/components/prototype/index.ts +42 -0
  50. package/src/components/prototype/registry.ts +40 -0
  51. package/src/components/prototype/template/index.ts +22 -0
  52. package/src/components/prototype/types.ts +96 -0
  53. package/src/components/prototype/utils.ts +20 -0
  54. package/src/components/prototype/vite-env.d.ts +6 -0
  55. package/src/components/settings/language-selector.tsx +1 -1
  56. package/src/components/settings/personal-team/account/avatar/notion-avatar.tsx +2 -2
  57. package/src/components/settings/personal-team/team/team-member-list.tsx +1 -1
  58. package/src/components/share/prd/prd-share-client.tsx +1 -1
  59. package/src/components/share/prd/share-annotation-overlay.tsx +1 -1
  60. package/src/components/share/prd/share-comment-panel.tsx +1 -1
  61. package/src/components/share/prd/share-comment-review-panel.tsx +1 -1
  62. package/src/components/share/prd/share-directory-tree.tsx +1 -1
  63. package/src/components/share/prd/share-doc-view.tsx +1 -1
  64. package/src/components/share/prd/share-layout.tsx +1 -1
  65. package/src/components/share/prd/share-nav-bar.tsx +1 -1
  66. package/src/components/share/prd/share-password-gate.tsx +1 -1
  67. package/src/components/share/prd/share-prototype-view.tsx +1 -1
  68. package/src/components/share/prd/share-review-panel.tsx +1 -1
  69. package/src/components/share/prd/share-sse-listener.tsx +1 -1
  70. package/src/components/share/prd/share-theme-toggle.tsx +1 -1
  71. package/src/components/share/prd/version-share-client.tsx +1 -1
  72. package/src/components/shared/component-preview.tsx +1 -1
  73. package/src/components/shared/language-switcher.tsx +1 -1
  74. package/src/components/shared/name-confirm-dialog.tsx +1 -1
  75. package/src/components/shared/nav-float-button.tsx +1 -1
  76. package/src/components/shared/placeholder-page.tsx +1 -1
  77. package/src/components/story/columns.tsx +1 -1
  78. package/src/components/story/create-story-dialog.tsx +2 -2
  79. package/src/components/story/detail/__tests__/activity-timeline.test.tsx +1 -1
  80. package/src/components/story/detail/activity-timeline.tsx +1 -1
  81. package/src/components/story/detail/comment-item.tsx +1 -1
  82. package/src/components/story/detail/story-detail-body.tsx +2 -2
  83. package/src/components/story/display/__tests__/display-dropdown.test.tsx +1 -1
  84. package/src/components/story/display/group-resolver.tsx +1 -1
  85. package/src/components/story/filters/applied-filters.tsx +1 -1
  86. package/src/components/story/infinite-scroll-sentinel.tsx +1 -1
  87. package/src/components/story/inline-editors/assignee-editor.tsx +1 -1
  88. package/src/components/story/schemas/story-create.ts +1 -1
  89. package/src/components/story/views/kanban-card.tsx +1 -1
  90. package/src/components/story/views/view-types.ts +1 -1
  91. package/src/components/tiptap/__tests__/ai-extension.test.ts +120 -0
  92. package/src/components/tiptap/extension/ai-extension.ts +408 -0
  93. package/src/components/tiptap/extension/code-block-ellipsis-input-extension.ts +32 -0
  94. package/src/components/tiptap/extension/fullwidth-blockquote-extension.ts +31 -0
  95. package/src/components/tiptap/extension/indent-extension.ts +500 -0
  96. package/src/components/tiptap/extension/list-normalization-extension.ts +129 -0
  97. package/src/components/tiptap/extension/node-alignment-extension.ts +302 -0
  98. package/src/components/tiptap/extension/node-background-extension.ts +167 -0
  99. package/src/components/tiptap/extension/triple-click-block-selection-extension.ts +66 -0
  100. package/src/components/tiptap/extension/ui-state-extension.ts +114 -0
  101. package/src/components/tiptap/extensions/local-file/insert-local-file.ts +35 -0
  102. package/src/components/tiptap/extensions/local-file/local-file-node.ts +67 -0
  103. package/src/components/tiptap/global.d.ts +12 -0
  104. package/src/components/tiptap/hooks/use-composed-ref.ts +63 -0
  105. package/src/components/tiptap/hooks/use-cursor-visibility.ts +87 -0
  106. package/src/components/tiptap/hooks/use-element-rect.ts +179 -0
  107. package/src/components/tiptap/hooks/use-floating-element.ts +144 -0
  108. package/src/components/tiptap/hooks/use-floating-toolbar-visibility.ts +140 -0
  109. package/src/components/tiptap/hooks/use-is-breakpoint.ts +53 -0
  110. package/src/components/tiptap/hooks/use-isomorphic-layout-effect.ts +27 -0
  111. package/src/components/tiptap/hooks/use-menu-navigation.ts +212 -0
  112. package/src/components/tiptap/hooks/use-on-click-outside.ts +167 -0
  113. package/src/components/tiptap/hooks/use-tiptap-editor.ts +87 -0
  114. package/src/components/tiptap/hooks/use-ui-editor-state.ts +47 -0
  115. package/src/components/tiptap/hooks/use-window-size.ts +108 -0
  116. package/src/components/tiptap/icons/add-col-left-icon.tsx +47 -0
  117. package/src/components/tiptap/icons/add-col-right-icon.tsx +47 -0
  118. package/src/components/tiptap/icons/add-row-bottom-icon.tsx +47 -0
  119. package/src/components/tiptap/icons/add-row-top-icon.tsx +47 -0
  120. package/src/components/tiptap/icons/ai-sparkles-icon.tsx +49 -0
  121. package/src/components/tiptap/icons/align-bottom-icon.tsx +45 -0
  122. package/src/components/tiptap/icons/align-center-icon.tsx +55 -0
  123. package/src/components/tiptap/icons/align-center-vertical-icon.tsx +51 -0
  124. package/src/components/tiptap/icons/align-end-vertical-icon.tsx +51 -0
  125. package/src/components/tiptap/icons/align-justify-icon.tsx +55 -0
  126. package/src/components/tiptap/icons/align-left-icon.tsx +55 -0
  127. package/src/components/tiptap/icons/align-middle-icon.tsx +72 -0
  128. package/src/components/tiptap/icons/align-right-icon.tsx +55 -0
  129. package/src/components/tiptap/icons/align-start-vertical-icon.tsx +49 -0
  130. package/src/components/tiptap/icons/align-top-icon.tsx +45 -0
  131. package/src/components/tiptap/icons/alignment-icon.tsx +89 -0
  132. package/src/components/tiptap/icons/arrow-down-a-z-icon.tsx +51 -0
  133. package/src/components/tiptap/icons/arrow-down-icon.tsx +41 -0
  134. package/src/components/tiptap/icons/arrow-down-to-line-icon.tsx +45 -0
  135. package/src/components/tiptap/icons/arrow-down-z-a-icon.tsx +51 -0
  136. package/src/components/tiptap/icons/arrow-left-icon.tsx +41 -0
  137. package/src/components/tiptap/icons/arrow-right-icon.tsx +43 -0
  138. package/src/components/tiptap/icons/arrow-up-icon.tsx +43 -0
  139. package/src/components/tiptap/icons/at-sign-icon.tsx +43 -0
  140. package/src/components/tiptap/icons/ban-icon.tsx +43 -0
  141. package/src/components/tiptap/icons/blockquote-icon.tsx +61 -0
  142. package/src/components/tiptap/icons/bold-icon.tsx +43 -0
  143. package/src/components/tiptap/icons/check-ai-icon.tsx +49 -0
  144. package/src/components/tiptap/icons/check-icon.tsx +43 -0
  145. package/src/components/tiptap/icons/chevron-down-icon.tsx +43 -0
  146. package/src/components/tiptap/icons/chevron-right-icon.tsx +43 -0
  147. package/src/components/tiptap/icons/clipboard-icon.tsx +41 -0
  148. package/src/components/tiptap/icons/close-icon.tsx +41 -0
  149. package/src/components/tiptap/icons/code-block-icon.tsx +55 -0
  150. package/src/components/tiptap/icons/code2-icon.tsx +49 -0
  151. package/src/components/tiptap/icons/complete-sentence-icon.tsx +61 -0
  152. package/src/components/tiptap/icons/copy-icon.tsx +49 -0
  153. package/src/components/tiptap/icons/corner-down-left-icon.tsx +43 -0
  154. package/src/components/tiptap/icons/external-link-icon.tsx +45 -0
  155. package/src/components/tiptap/icons/grip-4-icon.tsx +41 -0
  156. package/src/components/tiptap/icons/grip-vertical-icon.tsx +61 -0
  157. package/src/components/tiptap/icons/heading-five-icon.tsx +45 -0
  158. package/src/components/tiptap/icons/heading-four-icon.tsx +45 -0
  159. package/src/components/tiptap/icons/heading-one-icon.tsx +45 -0
  160. package/src/components/tiptap/icons/heading-six-icon.tsx +47 -0
  161. package/src/components/tiptap/icons/heading-three-icon.tsx +53 -0
  162. package/src/components/tiptap/icons/heading-two-icon.tsx +45 -0
  163. package/src/components/tiptap/icons/highlighter-icon.tsx +43 -0
  164. package/src/components/tiptap/icons/image-caption-icon.tsx +55 -0
  165. package/src/components/tiptap/icons/image-icon.tsx +43 -0
  166. package/src/components/tiptap/icons/image-plus-icon.tsx +43 -0
  167. package/src/components/tiptap/icons/indent-decrease-icon.tsx +57 -0
  168. package/src/components/tiptap/icons/indent-increase-icon.tsx +57 -0
  169. package/src/components/tiptap/icons/italic-icon.tsx +41 -0
  170. package/src/components/tiptap/icons/languages-icon.tsx +51 -0
  171. package/src/components/tiptap/icons/link-icon.tsx +45 -0
  172. package/src/components/tiptap/icons/list-icon.tsx +73 -0
  173. package/src/components/tiptap/icons/list-indented-icon.tsx +73 -0
  174. package/src/components/tiptap/icons/list-ordered-icon.tsx +73 -0
  175. package/src/components/tiptap/icons/list-todo-icon.tsx +67 -0
  176. package/src/components/tiptap/icons/message-square-icon.tsx +43 -0
  177. package/src/components/tiptap/icons/message-square-plus-icon.tsx +49 -0
  178. package/src/components/tiptap/icons/mic-ai-icon.tsx +51 -0
  179. package/src/components/tiptap/icons/minus-icon.tsx +43 -0
  180. package/src/components/tiptap/icons/moon-star-icon.tsx +47 -0
  181. package/src/components/tiptap/icons/more-vertical-icon.tsx +55 -0
  182. package/src/components/tiptap/icons/move-horizontal-icon.tsx +41 -0
  183. package/src/components/tiptap/icons/network-icon.tsx +48 -0
  184. package/src/components/tiptap/icons/paint-bucket-icon.tsx +49 -0
  185. package/src/components/tiptap/icons/plus-icon.tsx +41 -0
  186. package/src/components/tiptap/icons/plus-small-icon.tsx +41 -0
  187. package/src/components/tiptap/icons/redo2-icon.tsx +43 -0
  188. package/src/components/tiptap/icons/refresh-ai-icon.tsx +51 -0
  189. package/src/components/tiptap/icons/refresh-ccw-icon.tsx +45 -0
  190. package/src/components/tiptap/icons/repeat-2-icon.tsx +43 -0
  191. package/src/components/tiptap/icons/rotate-ccw-icon.tsx +41 -0
  192. package/src/components/tiptap/icons/simplify-2-icon.tsx +41 -0
  193. package/src/components/tiptap/icons/smile-ai-icon.tsx +55 -0
  194. package/src/components/tiptap/icons/smile-plus-icon.tsx +43 -0
  195. package/src/components/tiptap/icons/square-x-icon.tsx +43 -0
  196. package/src/components/tiptap/icons/stop-circle-2-icon.tsx +43 -0
  197. package/src/components/tiptap/icons/strike-icon.tsx +45 -0
  198. package/src/components/tiptap/icons/subscript-icon.tsx +55 -0
  199. package/src/components/tiptap/icons/summarize-text-icon.tsx +53 -0
  200. package/src/components/tiptap/icons/sun-icon.tsx +75 -0
  201. package/src/components/tiptap/icons/superscript-icon.tsx +55 -0
  202. package/src/components/tiptap/icons/table-cell-merge-icon.tsx +61 -0
  203. package/src/components/tiptap/icons/table-cell-split-icon.tsx +61 -0
  204. package/src/components/tiptap/icons/table-column-icon.tsx +43 -0
  205. package/src/components/tiptap/icons/table-header-column-icon.tsx +45 -0
  206. package/src/components/tiptap/icons/table-header-row-icon.tsx +43 -0
  207. package/src/components/tiptap/icons/table-icon.tsx +43 -0
  208. package/src/components/tiptap/icons/table-row-icon.tsx +43 -0
  209. package/src/components/tiptap/icons/text-color-small-icon.tsx +43 -0
  210. package/src/components/tiptap/icons/text-extend-icon.tsx +53 -0
  211. package/src/components/tiptap/icons/text-reduce-icon.tsx +49 -0
  212. package/src/components/tiptap/icons/trash-icon.tsx +43 -0
  213. package/src/components/tiptap/icons/type-icon.tsx +41 -0
  214. package/src/components/tiptap/icons/underline-icon.tsx +43 -0
  215. package/src/components/tiptap/icons/undo2-icon.tsx +43 -0
  216. package/src/components/tiptap/icons/x-icon.tsx +41 -0
  217. package/src/components/tiptap/index.ts +1 -0
  218. package/src/components/tiptap/lib/advanced-utils.ts +388 -0
  219. package/src/components/tiptap/lib/ai-types.ts +41 -0
  220. package/src/components/tiptap/lib/collab-utils.ts +147 -0
  221. package/src/components/tiptap/lib/datetime.ts +1 -0
  222. package/src/components/tiptap/lib/fullwidth.ts +44 -0
  223. package/src/components/tiptap/lib/pacer.ts +95 -0
  224. package/src/components/tiptap/lib/utils/attr.ts +67 -0
  225. package/src/components/tiptap/lib/utils/constants.ts +46 -0
  226. package/src/components/tiptap/lib/utils/math.ts +23 -0
  227. package/src/components/tiptap/lib/utils/node.ts +136 -0
  228. package/src/components/tiptap/lib/utils/platform.ts +77 -0
  229. package/src/components/tiptap/lib/utils/schema.ts +75 -0
  230. package/src/components/tiptap/lib/utils/selection.ts +218 -0
  231. package/src/components/tiptap/lib/utils/url.ts +122 -0
  232. package/src/components/tiptap/lib/utils.ts +57 -0
  233. package/src/components/tiptap/mindmap/lib/__tests__/default-mindmap-data.test.ts +58 -0
  234. package/src/components/tiptap/mindmap/lib/default-mindmap-data.ts +36 -0
  235. package/src/components/tiptap/mindmap/mind-map.tsx +579 -0
  236. package/src/components/tiptap/mindmap/mindmap-fullscreen-dialog.tsx +52 -0
  237. package/src/components/tiptap/node/annotation-node/annotation-extension.ts +47 -0
  238. package/src/components/tiptap/node/annotation-node/annotation-node-view.tsx +185 -0
  239. package/src/components/tiptap/node/annotation-node/annotation-node.scss +77 -0
  240. package/src/components/tiptap/node/annotation-node/index.ts +2 -0
  241. package/src/components/tiptap/node/blockquote-node/blockquote-node.scss +37 -0
  242. package/src/components/tiptap/node/code-block-node/code-block-node.scss +54 -0
  243. package/src/components/tiptap/node/doc-card-extension.tsx +226 -0
  244. package/src/components/tiptap/node/heading-node/heading-node.scss +45 -0
  245. package/src/components/tiptap/node/horizontal-rule-node/horizontal-rule-node-extension.ts +31 -0
  246. package/src/components/tiptap/node/horizontal-rule-node/horizontal-rule-node.scss +25 -0
  247. package/src/components/tiptap/node/image-node/image-node-extension.ts +253 -0
  248. package/src/components/tiptap/node/image-node/image-node-floating.tsx +65 -0
  249. package/src/components/tiptap/node/image-node/image-node-view.scss +95 -0
  250. package/src/components/tiptap/node/image-node/image-node-view.tsx +363 -0
  251. package/src/components/tiptap/node/image-node/image-node.scss +35 -0
  252. package/src/components/tiptap/node/image-upload-node/image-upload-node-extension.ts +179 -0
  253. package/src/components/tiptap/node/image-upload-node/image-upload-node.scss +249 -0
  254. package/src/components/tiptap/node/image-upload-node/image-upload-node.tsx +570 -0
  255. package/src/components/tiptap/node/image-upload-node/index.tsx +18 -0
  256. package/src/components/tiptap/node/inline-node-selection.ts +42 -0
  257. package/src/components/tiptap/node/list-node/list-node.scss +208 -0
  258. package/src/components/tiptap/node/mention-extension.ts +163 -0
  259. package/src/components/tiptap/node/mindmap-node/mindmap-extension.ts +87 -0
  260. package/src/components/tiptap/node/mindmap-node/mindmap-node-view.tsx +61 -0
  261. package/src/components/tiptap/node/paragraph-node/paragraph-node.scss +1 -0
  262. package/src/components/tiptap/node/table-node/extensions/table-handle/helpers/create-image.ts +303 -0
  263. package/src/components/tiptap/node/table-node/extensions/table-handle/index.ts +19 -0
  264. package/src/components/tiptap/node/table-node/extensions/table-handle/table-handle-plugin.ts +863 -0
  265. package/src/components/tiptap/node/table-node/extensions/table-handle/table-handle.ts +67 -0
  266. package/src/components/tiptap/node/table-node/extensions/table-node-extension.ts +264 -0
  267. package/src/components/tiptap/node/table-node/hooks/use-table-handle-state.ts +88 -0
  268. package/src/components/tiptap/node/table-node/lib/tiptap-table-utils.ts +1306 -0
  269. package/src/components/tiptap/node/table-node/styles/prosemirror-table.scss +48 -0
  270. package/src/components/tiptap/node/table-node/styles/table-node.scss +211 -0
  271. package/src/components/tiptap/node/table-node/ui/table-add-row-column-button/index.tsx +19 -0
  272. package/src/components/tiptap/node/table-node/ui/table-add-row-column-button/table-add-row-column-button.tsx +117 -0
  273. package/src/components/tiptap/node/table-node/ui/table-add-row-column-button/use-table-add-row-column.ts +365 -0
  274. package/src/components/tiptap/node/table-node/ui/table-align-cell-button/index.tsx +19 -0
  275. package/src/components/tiptap/node/table-node/ui/table-align-cell-button/table-align-cell-button.tsx +144 -0
  276. package/src/components/tiptap/node/table-node/ui/table-align-cell-button/use-table-align-cell.ts +565 -0
  277. package/src/components/tiptap/node/table-node/ui/table-alignment-menu/index.tsx +18 -0
  278. package/src/components/tiptap/node/table-node/ui/table-alignment-menu/table-alignment-menu.tsx +177 -0
  279. package/src/components/tiptap/node/table-node/ui/table-cell-handle-menu/index.tsx +18 -0
  280. package/src/components/tiptap/node/table-node/ui/table-cell-handle-menu/table-cell-handle-menu.scss +121 -0
  281. package/src/components/tiptap/node/table-node/ui/table-cell-handle-menu/table-cell-handle-menu.tsx +227 -0
  282. package/src/components/tiptap/node/table-node/ui/table-clear-row-column-content-button/index.tsx +19 -0
  283. package/src/components/tiptap/node/table-node/ui/table-clear-row-column-content-button/table-clear-row-column-content-button.tsx +116 -0
  284. package/src/components/tiptap/node/table-node/ui/table-clear-row-column-content-button/use-table-clear-row-column-content.ts +487 -0
  285. package/src/components/tiptap/node/table-node/ui/table-delete-row-column-button/index.tsx +19 -0
  286. package/src/components/tiptap/node/table-node/ui/table-delete-row-column-button/table-delete-row-column-button.tsx +115 -0
  287. package/src/components/tiptap/node/table-node/ui/table-delete-row-column-button/use-table-delete-row-column.ts +283 -0
  288. package/src/components/tiptap/node/table-node/ui/table-duplicate-row-column-button/index.tsx +19 -0
  289. package/src/components/tiptap/node/table-node/ui/table-duplicate-row-column-button/table-duplicate-row-column-button.tsx +107 -0
  290. package/src/components/tiptap/node/table-node/ui/table-duplicate-row-column-button/use-table-duplicate-row-column.ts +424 -0
  291. package/src/components/tiptap/node/table-node/ui/table-extend-row-column-button/index.tsx +19 -0
  292. package/src/components/tiptap/node/table-node/ui/table-extend-row-column-button/table-extend-row-column-button.scss +36 -0
  293. package/src/components/tiptap/node/table-node/ui/table-extend-row-column-button/table-extend-row-column-button.tsx +272 -0
  294. package/src/components/tiptap/node/table-node/ui/table-extend-row-column-button/use-table-extend-row-column.ts +135 -0
  295. package/src/components/tiptap/node/table-node/ui/table-fit-to-width-button/index.tsx +19 -0
  296. package/src/components/tiptap/node/table-node/ui/table-fit-to-width-button/table-fit-to-width-button.tsx +113 -0
  297. package/src/components/tiptap/node/table-node/ui/table-fit-to-width-button/use-table-fit-to-width.ts +259 -0
  298. package/src/components/tiptap/node/table-node/ui/table-handle/index.tsx +19 -0
  299. package/src/components/tiptap/node/table-node/ui/table-handle/table-handle.tsx +179 -0
  300. package/src/components/tiptap/node/table-node/ui/table-handle/use-table-handle-positioning.ts +272 -0
  301. package/src/components/tiptap/node/table-node/ui/table-handle-menu/index.tsx +18 -0
  302. package/src/components/tiptap/node/table-node/ui/table-handle-menu/table-handle-menu.scss +54 -0
  303. package/src/components/tiptap/node/table-node/ui/table-handle-menu/table-handle-menu.tsx +765 -0
  304. package/src/components/tiptap/node/table-node/ui/table-header-row-column-button/index.tsx +19 -0
  305. package/src/components/tiptap/node/table-node/ui/table-header-row-column-button/table-header-row-column-button.tsx +114 -0
  306. package/src/components/tiptap/node/table-node/ui/table-header-row-column-button/use-table-header-row-column.ts +280 -0
  307. package/src/components/tiptap/node/table-node/ui/table-merge-split-cell-button/index.tsx +19 -0
  308. package/src/components/tiptap/node/table-node/ui/table-merge-split-cell-button/table-merge-split-cell-button.tsx +140 -0
  309. package/src/components/tiptap/node/table-node/ui/table-merge-split-cell-button/use-table-merge-split-cell.ts +305 -0
  310. package/src/components/tiptap/node/table-node/ui/table-move-row-column-button/index.tsx +19 -0
  311. package/src/components/tiptap/node/table-node/ui/table-move-row-column-button/table-move-row-column-button.tsx +138 -0
  312. package/src/components/tiptap/node/table-node/ui/table-move-row-column-button/use-table-move-row-column.ts +498 -0
  313. package/src/components/tiptap/node/table-node/ui/table-selection-overlay/index.tsx +18 -0
  314. package/src/components/tiptap/node/table-node/ui/table-selection-overlay/table-selection-overlay.tsx +590 -0
  315. package/src/components/tiptap/node/table-node/ui/table-selection-overlay/use-resize-overlay.ts +94 -0
  316. package/src/components/tiptap/node/table-node/ui/table-sort-row-column-button/index.tsx +19 -0
  317. package/src/components/tiptap/node/table-node/ui/table-sort-row-column-button/table-sort-row-column-button.tsx +115 -0
  318. package/src/components/tiptap/node/table-node/ui/table-sort-row-column-button/use-table-sort-row-column.ts +495 -0
  319. package/src/components/tiptap/node/table-node/ui/table-trigger-button/index.tsx +20 -0
  320. package/src/components/tiptap/node/table-node/ui/table-trigger-button/table-grid-selector.scss +98 -0
  321. package/src/components/tiptap/node/table-node/ui/table-trigger-button/table-grid-selector.tsx +236 -0
  322. package/src/components/tiptap/node/table-node/ui/table-trigger-button/table-trigger-button.tsx +147 -0
  323. package/src/components/tiptap/node/table-node/ui/table-trigger-button/use-table-trigger.ts +196 -0
  324. package/src/components/tiptap/node/toc-node/context/toc-context.tsx +214 -0
  325. package/src/components/tiptap/node/toc-node/extensions/toc-node-extension.ts +172 -0
  326. package/src/components/tiptap/node/toc-node/index.ts +20 -0
  327. package/src/components/tiptap/node/toc-node/toc-node.scss +105 -0
  328. package/src/components/tiptap/node/toc-node/toc-node.tsx +131 -0
  329. package/src/components/tiptap/node/toc-node/ui/toc-show-title-button/index.tsx +19 -0
  330. package/src/components/tiptap/node/toc-node/ui/toc-show-title-button/toc-show-title-button.tsx +113 -0
  331. package/src/components/tiptap/node/toc-node/ui/toc-show-title-button/toc-show-title.ts +228 -0
  332. package/src/components/tiptap/node/toc-node/ui/toc-sidebar/index.tsx +18 -0
  333. package/src/components/tiptap/node/toc-node/ui/toc-sidebar/toc-sidebar.scss +265 -0
  334. package/src/components/tiptap/node/toc-node/ui/toc-sidebar/toc-sidebar.tsx +272 -0
  335. package/src/components/tiptap/notion-like/data/content.json +1658 -0
  336. package/src/components/tiptap/notion-like/index.ts +2 -0
  337. package/src/components/tiptap/notion-like/notion-like-editor-header.scss +29 -0
  338. package/src/components/tiptap/notion-like/notion-like-editor-header.tsx +91 -0
  339. package/src/components/tiptap/notion-like/notion-like-editor-mobile-toolbar.tsx +704 -0
  340. package/src/components/tiptap/notion-like/notion-like-editor-theme-toggle.tsx +66 -0
  341. package/src/components/tiptap/notion-like/notion-like-editor-toolbar-floating.tsx +435 -0
  342. package/src/components/tiptap/notion-like/notion-like-editor.scss +181 -0
  343. package/src/components/tiptap/notion-like/notion-like-editor.tsx +589 -0
  344. package/src/components/tiptap/notion-like/setup-error-message/index.tsx +19 -0
  345. package/src/components/tiptap/notion-like/setup-error-message/setup-error-message.scss +108 -0
  346. package/src/components/tiptap/notion-like/setup-error-message/setup-error-message.tsx +116 -0
  347. package/src/components/tiptap/ui/ai-ask-button/ai-ask-button.tsx +124 -0
  348. package/src/components/tiptap/ui/ai-ask-button/index.tsx +19 -0
  349. package/src/components/tiptap/ui/ai-ask-button/use-ai-ask.ts +192 -0
  350. package/src/components/tiptap/ui/ai-menu/ai-menu-actions/ai-menu-actions.scss +11 -0
  351. package/src/components/tiptap/ui/ai-menu/ai-menu-actions/ai-menu-actions.tsx +103 -0
  352. package/src/components/tiptap/ui/ai-menu/ai-menu-actions/index.tsx +18 -0
  353. package/src/components/tiptap/ui/ai-menu/ai-menu-hooks.ts +325 -0
  354. package/src/components/tiptap/ui/ai-menu/ai-menu-input/ai-menu-input-hooks.ts +61 -0
  355. package/src/components/tiptap/ui/ai-menu/ai-menu-input/ai-menu-input-types.ts +32 -0
  356. package/src/components/tiptap/ui/ai-menu/ai-menu-input/ai-menu-input.scss +114 -0
  357. package/src/components/tiptap/ui/ai-menu/ai-menu-input/ai-menu-input.tsx +269 -0
  358. package/src/components/tiptap/ui/ai-menu/ai-menu-input/index.tsx +20 -0
  359. package/src/components/tiptap/ui/ai-menu/ai-menu-items/ai-menu-items-constants.ts +63 -0
  360. package/src/components/tiptap/ui/ai-menu/ai-menu-items/ai-menu-items-types.ts +64 -0
  361. package/src/components/tiptap/ui/ai-menu/ai-menu-items/ai-menu-items.tsx +381 -0
  362. package/src/components/tiptap/ui/ai-menu/ai-menu-items/index.tsx +20 -0
  363. package/src/components/tiptap/ui/ai-menu/ai-menu-items/language-selection-submenu.tsx +140 -0
  364. package/src/components/tiptap/ui/ai-menu/ai-menu-items/menu-action-renderer.tsx +105 -0
  365. package/src/components/tiptap/ui/ai-menu/ai-menu-items/tone-selection-submenu.tsx +125 -0
  366. package/src/components/tiptap/ui/ai-menu/ai-menu-types.ts +42 -0
  367. package/src/components/tiptap/ui/ai-menu/ai-menu-utils.ts +255 -0
  368. package/src/components/tiptap/ui/ai-menu/ai-menu.scss +101 -0
  369. package/src/components/tiptap/ui/ai-menu/ai-menu.tsx +427 -0
  370. package/src/components/tiptap/ui/ai-menu/index.tsx +25 -0
  371. package/src/components/tiptap/ui/blockquote-button/blockquote-button.tsx +143 -0
  372. package/src/components/tiptap/ui/blockquote-button/index.tsx +19 -0
  373. package/src/components/tiptap/ui/blockquote-button/use-blockquote.ts +290 -0
  374. package/src/components/tiptap/ui/code-block-button/code-block-button.tsx +143 -0
  375. package/src/components/tiptap/ui/code-block-button/index.tsx +19 -0
  376. package/src/components/tiptap/ui/code-block-button/use-code-block.ts +300 -0
  377. package/src/components/tiptap/ui/color-highlight-button/color-highlight-button.scss +49 -0
  378. package/src/components/tiptap/ui/color-highlight-button/color-highlight-button.tsx +188 -0
  379. package/src/components/tiptap/ui/color-highlight-button/index.tsx +19 -0
  380. package/src/components/tiptap/ui/color-highlight-button/use-color-highlight.ts +397 -0
  381. package/src/components/tiptap/ui/color-highlight-popover/color-highlight-popover.tsx +251 -0
  382. package/src/components/tiptap/ui/color-highlight-popover/index.tsx +18 -0
  383. package/src/components/tiptap/ui/color-menu/color-menu.tsx +228 -0
  384. package/src/components/tiptap/ui/color-menu/index.tsx +18 -0
  385. package/src/components/tiptap/ui/color-text-button/color-text-button.scss +52 -0
  386. package/src/components/tiptap/ui/color-text-button/color-text-button.tsx +168 -0
  387. package/src/components/tiptap/ui/color-text-button/index.tsx +19 -0
  388. package/src/components/tiptap/ui/color-text-button/use-color-text.ts +303 -0
  389. package/src/components/tiptap/ui/color-text-popover/color-text-popover.scss +40 -0
  390. package/src/components/tiptap/ui/color-text-popover/color-text-popover.tsx +471 -0
  391. package/src/components/tiptap/ui/color-text-popover/index.tsx +19 -0
  392. package/src/components/tiptap/ui/color-text-popover/use-color-text-popover.ts +295 -0
  393. package/src/components/tiptap/ui/copy-anchor-link-button/copy-anchor-link-button.tsx +136 -0
  394. package/src/components/tiptap/ui/copy-anchor-link-button/index.tsx +20 -0
  395. package/src/components/tiptap/ui/copy-anchor-link-button/use-copy-anchor-link.ts +282 -0
  396. package/src/components/tiptap/ui/copy-anchor-link-button/use-scroll-to-hash.ts +155 -0
  397. package/src/components/tiptap/ui/copy-to-clipboard-button/copy-to-clipboard-button.tsx +134 -0
  398. package/src/components/tiptap/ui/copy-to-clipboard-button/index.tsx +19 -0
  399. package/src/components/tiptap/ui/copy-to-clipboard-button/use-copy-to-clipboard.ts +275 -0
  400. package/src/components/tiptap/ui/delete-node-button/delete-node-button.tsx +132 -0
  401. package/src/components/tiptap/ui/delete-node-button/index.tsx +19 -0
  402. package/src/components/tiptap/ui/delete-node-button/use-delete-node.ts +259 -0
  403. package/src/components/tiptap/ui/doc-reference-floating/doc-reference-floating.tsx +120 -0
  404. package/src/components/tiptap/ui/doc-reference-interaction/doc-reference-interaction.tsx +209 -0
  405. package/src/components/tiptap/ui/drag-context-menu/drag-context-menu-types.ts +47 -0
  406. package/src/components/tiptap/ui/drag-context-menu/drag-context-menu.scss +15 -0
  407. package/src/components/tiptap/ui/drag-context-menu/drag-context-menu.tsx +558 -0
  408. package/src/components/tiptap/ui/drag-context-menu/index.tsx +19 -0
  409. package/src/components/tiptap/ui/duplicate-button/duplicate-button.tsx +132 -0
  410. package/src/components/tiptap/ui/duplicate-button/index.tsx +19 -0
  411. package/src/components/tiptap/ui/duplicate-button/use-duplicate.ts +240 -0
  412. package/src/components/tiptap/ui/emoji-dropdown-menu/emoji-dropdown-menu.tsx +124 -0
  413. package/src/components/tiptap/ui/emoji-dropdown-menu/index.tsx +18 -0
  414. package/src/components/tiptap/ui/emoji-menu/emoji-menu-utils.ts +53 -0
  415. package/src/components/tiptap/ui/emoji-menu/emoji-menu.scss +28 -0
  416. package/src/components/tiptap/ui/emoji-menu/emoji-menu.tsx +166 -0
  417. package/src/components/tiptap/ui/emoji-menu/index.tsx +19 -0
  418. package/src/components/tiptap/ui/emoji-trigger-button/emoji-trigger-button.tsx +146 -0
  419. package/src/components/tiptap/ui/emoji-trigger-button/index.tsx +19 -0
  420. package/src/components/tiptap/ui/emoji-trigger-button/use-emoji-trigger.ts +338 -0
  421. package/src/components/tiptap/ui/heading-button/heading-button.tsx +142 -0
  422. package/src/components/tiptap/ui/heading-button/index.tsx +19 -0
  423. package/src/components/tiptap/ui/heading-button/use-heading.ts +374 -0
  424. package/src/components/tiptap/ui/image-align-button/image-align-button.tsx +154 -0
  425. package/src/components/tiptap/ui/image-align-button/index.tsx +19 -0
  426. package/src/components/tiptap/ui/image-align-button/use-image-align.ts +313 -0
  427. package/src/components/tiptap/ui/image-caption-button/image-caption-button.tsx +109 -0
  428. package/src/components/tiptap/ui/image-caption-button/index.tsx +19 -0
  429. package/src/components/tiptap/ui/image-caption-button/use-image-caption.ts +232 -0
  430. package/src/components/tiptap/ui/image-download-button/image-download-button.tsx +141 -0
  431. package/src/components/tiptap/ui/image-download-button/index.tsx +19 -0
  432. package/src/components/tiptap/ui/image-download-button/use-image-download.ts +384 -0
  433. package/src/components/tiptap/ui/image-upload-button/image-upload-button.tsx +148 -0
  434. package/src/components/tiptap/ui/image-upload-button/index.tsx +19 -0
  435. package/src/components/tiptap/ui/image-upload-button/use-image-upload.ts +214 -0
  436. package/src/components/tiptap/ui/improve-dropdown/improve-dropdown.tsx +311 -0
  437. package/src/components/tiptap/ui/improve-dropdown/index.tsx +19 -0
  438. package/src/components/tiptap/ui/improve-dropdown/use-improve-dropdown.ts +294 -0
  439. package/src/components/tiptap/ui/indent-button/indent-button.tsx +148 -0
  440. package/src/components/tiptap/ui/indent-button/index.tsx +19 -0
  441. package/src/components/tiptap/ui/indent-button/use-indent.ts +217 -0
  442. package/src/components/tiptap/ui/link-popover/index.tsx +19 -0
  443. package/src/components/tiptap/ui/link-popover/link-popover.scss +16 -0
  444. package/src/components/tiptap/ui/link-popover/link-popover.tsx +333 -0
  445. package/src/components/tiptap/ui/link-popover/use-link-popover.ts +316 -0
  446. package/src/components/tiptap/ui/list-button/index.tsx +19 -0
  447. package/src/components/tiptap/ui/list-button/list-button.tsx +138 -0
  448. package/src/components/tiptap/ui/list-button/use-list.ts +370 -0
  449. package/src/components/tiptap/ui/mark-button/index.tsx +19 -0
  450. package/src/components/tiptap/ui/mark-button/mark-button.tsx +138 -0
  451. package/src/components/tiptap/ui/mark-button/use-mark.ts +247 -0
  452. package/src/components/tiptap/ui/mention-dropdown-menu/index.tsx +18 -0
  453. package/src/components/tiptap/ui/mention-dropdown-menu/mention-dropdown-menu.tsx +194 -0
  454. package/src/components/tiptap/ui/mention-trigger-button/index.tsx +19 -0
  455. package/src/components/tiptap/ui/mention-trigger-button/mention-trigger-button.tsx +137 -0
  456. package/src/components/tiptap/ui/mention-trigger-button/use-mention-trigger.ts +362 -0
  457. package/src/components/tiptap/ui/move-node-button/index.tsx +19 -0
  458. package/src/components/tiptap/ui/move-node-button/move-node-button.tsx +138 -0
  459. package/src/components/tiptap/ui/move-node-button/use-move-node.ts +230 -0
  460. package/src/components/tiptap/ui/reference-dropdown-menu/index.tsx +18 -0
  461. package/src/components/tiptap/ui/reference-dropdown-menu/reference-dropdown-menu.tsx +231 -0
  462. package/src/components/tiptap/ui/reset-all-formatting-button/index.tsx +19 -0
  463. package/src/components/tiptap/ui/reset-all-formatting-button/reset-all-formatting-button.tsx +144 -0
  464. package/src/components/tiptap/ui/reset-all-formatting-button/use-reset-all-formatting.ts +279 -0
  465. package/src/components/tiptap/ui/slash-command-trigger-button/index.tsx +19 -0
  466. package/src/components/tiptap/ui/slash-command-trigger-button/slash-command-trigger-button.tsx +143 -0
  467. package/src/components/tiptap/ui/slash-command-trigger-button/use-slash-command-trigger.ts +305 -0
  468. package/src/components/tiptap/ui/slash-dropdown-menu/index.tsx +19 -0
  469. package/src/components/tiptap/ui/slash-dropdown-menu/slash-dropdown-menu.scss +33 -0
  470. package/src/components/tiptap/ui/slash-dropdown-menu/slash-dropdown-menu.tsx +222 -0
  471. package/src/components/tiptap/ui/slash-dropdown-menu/use-slash-dropdown-menu.ts +498 -0
  472. package/src/components/tiptap/ui/text-align-button/index.tsx +19 -0
  473. package/src/components/tiptap/ui/text-align-button/text-align-button.tsx +160 -0
  474. package/src/components/tiptap/ui/text-align-button/use-text-align.ts +248 -0
  475. package/src/components/tiptap/ui/text-button/index.tsx +19 -0
  476. package/src/components/tiptap/ui/text-button/text-button.tsx +135 -0
  477. package/src/components/tiptap/ui/text-button/use-text.ts +308 -0
  478. package/src/components/tiptap/ui/turn-into-dropdown/index.tsx +19 -0
  479. package/src/components/tiptap/ui/turn-into-dropdown/turn-into-dropdown.tsx +243 -0
  480. package/src/components/tiptap/ui/turn-into-dropdown/use-turn-into-dropdown.ts +294 -0
  481. package/src/components/tiptap/ui/undo-redo-button/index.tsx +19 -0
  482. package/src/components/tiptap/ui/undo-redo-button/undo-redo-button.tsx +141 -0
  483. package/src/components/tiptap/ui/undo-redo-button/use-undo-redo.ts +207 -0
  484. package/src/components/tiptap/ui-primitive/avatar/avatar.scss +118 -0
  485. package/src/components/tiptap/ui-primitive/avatar/avatar.tsx +255 -0
  486. package/src/components/tiptap/ui-primitive/avatar/index.tsx +18 -0
  487. package/src/components/tiptap/ui-primitive/badge/badge-colors.scss +395 -0
  488. package/src/components/tiptap/ui-primitive/badge/badge-group.scss +16 -0
  489. package/src/components/tiptap/ui-primitive/badge/badge.scss +99 -0
  490. package/src/components/tiptap/ui-primitive/badge/badge.tsx +62 -0
  491. package/src/components/tiptap/ui-primitive/badge/index.tsx +18 -0
  492. package/src/components/tiptap/ui-primitive/button/button-colors.scss +429 -0
  493. package/src/components/tiptap/ui-primitive/button/button.scss +314 -0
  494. package/src/components/tiptap/ui-primitive/button/button.tsx +119 -0
  495. package/src/components/tiptap/ui-primitive/button/index.tsx +18 -0
  496. package/src/components/tiptap/ui-primitive/button-group/button-group.scss +79 -0
  497. package/src/components/tiptap/ui-primitive/button-group/button-group.tsx +89 -0
  498. package/src/components/tiptap/ui-primitive/button-group/index.tsx +18 -0
  499. package/src/components/tiptap/ui-primitive/card/card.scss +74 -0
  500. package/src/components/tiptap/ui-primitive/card/card.tsx +95 -0
  501. package/src/components/tiptap/ui-primitive/card/index.tsx +18 -0
  502. package/src/components/tiptap/ui-primitive/combobox/combobox.scss +90 -0
  503. package/src/components/tiptap/ui-primitive/combobox/combobox.tsx +231 -0
  504. package/src/components/tiptap/ui-primitive/combobox/index.tsx +18 -0
  505. package/src/components/tiptap/ui-primitive/dropdown-menu/dropdown-menu.scss +152 -0
  506. package/src/components/tiptap/ui-primitive/dropdown-menu/dropdown-menu.tsx +280 -0
  507. package/src/components/tiptap/ui-primitive/dropdown-menu/index.tsx +18 -0
  508. package/src/components/tiptap/ui-primitive/input/index.tsx +18 -0
  509. package/src/components/tiptap/ui-primitive/input/input.scss +41 -0
  510. package/src/components/tiptap/ui-primitive/input/input.tsx +33 -0
  511. package/src/components/tiptap/ui-primitive/input-group/index.tsx +18 -0
  512. package/src/components/tiptap/ui-primitive/input-group/input-group.scss +225 -0
  513. package/src/components/tiptap/ui-primitive/input-group/input-group.tsx +152 -0
  514. package/src/components/tiptap/ui-primitive/label/index.tsx +18 -0
  515. package/src/components/tiptap/ui-primitive/label/label.scss +20 -0
  516. package/src/components/tiptap/ui-primitive/label/label.tsx +34 -0
  517. package/src/components/tiptap/ui-primitive/menu/index.tsx +22 -0
  518. package/src/components/tiptap/ui-primitive/menu/menu-context.ts +35 -0
  519. package/src/components/tiptap/ui-primitive/menu/menu-hooks.ts +118 -0
  520. package/src/components/tiptap/ui-primitive/menu/menu-types.ts +75 -0
  521. package/src/components/tiptap/ui-primitive/menu/menu-utils.ts +80 -0
  522. package/src/components/tiptap/ui-primitive/menu/menu.scss +68 -0
  523. package/src/components/tiptap/ui-primitive/menu/menu.tsx +327 -0
  524. package/src/components/tiptap/ui-primitive/popover/index.tsx +18 -0
  525. package/src/components/tiptap/ui-primitive/popover/popover.scss +64 -0
  526. package/src/components/tiptap/ui-primitive/popover/popover.tsx +53 -0
  527. package/src/components/tiptap/ui-primitive/separator/index.tsx +18 -0
  528. package/src/components/tiptap/ui-primitive/separator/separator.scss +23 -0
  529. package/src/components/tiptap/ui-primitive/separator/separator.tsx +46 -0
  530. package/src/components/tiptap/ui-primitive/sidebar/index.tsx +18 -0
  531. package/src/components/tiptap/ui-primitive/sidebar/sidebar.scss +389 -0
  532. package/src/components/tiptap/ui-primitive/sidebar/sidebar.tsx +633 -0
  533. package/src/components/tiptap/ui-primitive/spacer/index.tsx +18 -0
  534. package/src/components/tiptap/ui-primitive/spacer/spacer.tsx +40 -0
  535. package/src/components/tiptap/ui-primitive/textarea/index.tsx +18 -0
  536. package/src/components/tiptap/ui-primitive/textarea/textarea.scss +42 -0
  537. package/src/components/tiptap/ui-primitive/textarea/textarea.tsx +32 -0
  538. package/src/components/tiptap/ui-primitive/textarea-autosize/index.tsx +18 -0
  539. package/src/components/tiptap/ui-primitive/textarea-autosize/textarea-autosize.tsx +34 -0
  540. package/src/components/tiptap/ui-primitive/toolbar/index.tsx +18 -0
  541. package/src/components/tiptap/ui-primitive/toolbar/toolbar.scss +98 -0
  542. package/src/components/tiptap/ui-primitive/toolbar/toolbar.tsx +139 -0
  543. package/src/components/tiptap/ui-primitive/tooltip/index.tsx +18 -0
  544. package/src/components/tiptap/ui-primitive/tooltip/tooltip.scss +43 -0
  545. package/src/components/tiptap/ui-primitive/tooltip/tooltip.tsx +257 -0
  546. package/src/components/tiptap/ui-utils/floating-element/floating-element-utils.ts +51 -0
  547. package/src/components/tiptap/ui-utils/floating-element/floating-element.tsx +397 -0
  548. package/src/components/tiptap/ui-utils/floating-element/index.tsx +19 -0
  549. package/src/components/tiptap/ui-utils/suggestion-menu/index.tsx +20 -0
  550. package/src/components/tiptap/ui-utils/suggestion-menu/suggestion-menu-fullwidth.ts +48 -0
  551. package/src/components/tiptap/ui-utils/suggestion-menu/suggestion-menu-types.ts +119 -0
  552. package/src/components/tiptap/ui-utils/suggestion-menu/suggestion-menu-utils.ts +107 -0
  553. package/src/components/tiptap/ui-utils/suggestion-menu/suggestion-menu.tsx +367 -0
  554. package/src/components/tiptap/virtual.d.ts +17 -0
  555. package/src/components/ui/alert-dialog.tsx +1 -1
  556. package/src/components/ui/badge.tsx +1 -1
  557. package/src/components/ui/button.tsx +1 -1
  558. package/src/components/ui/card.tsx +1 -1
  559. package/src/components/ui/checkbox.tsx +1 -1
  560. package/src/components/ui/context-menu.tsx +1 -1
  561. package/src/components/ui/dialog.tsx +1 -1
  562. package/src/components/ui/dropdown-menu.tsx +1 -1
  563. package/src/components/ui/empty.tsx +1 -1
  564. package/src/components/ui/input.tsx +1 -1
  565. package/src/components/ui/label.tsx +1 -1
  566. package/src/components/ui/popover.tsx +1 -1
  567. package/src/components/ui/resizable.tsx +1 -1
  568. package/src/components/ui/select.tsx +1 -1
  569. package/src/components/ui/sidebar.tsx +1 -1
  570. package/src/components/ui/skeleton.tsx +1 -1
  571. package/src/components/ui/sonner.tsx +1 -1
  572. package/src/components/ui/spinner.tsx +1 -1
  573. package/src/components/ui/switch.tsx +1 -1
  574. package/src/components/ui/textarea.tsx +1 -1
  575. package/src/components/ui/tooltip.tsx +1 -1
  576. package/src/components/ui-shared/ai-command-button.tsx +36 -0
  577. package/src/components/ui-shared/ai-stream-text.tsx +37 -0
  578. package/src/components/ui-shared/alert-dialog.tsx +216 -0
  579. package/src/components/ui-shared/animated-theme-toggler.tsx +272 -0
  580. package/src/components/ui-shared/auth-client.ts +115 -0
  581. package/src/components/ui-shared/auth-layout.tsx +66 -0
  582. package/src/components/ui-shared/avatar.tsx +129 -0
  583. package/src/components/ui-shared/badge.tsx +66 -0
  584. package/src/components/ui-shared/button.tsx +84 -0
  585. package/src/components/ui-shared/card.tsx +120 -0
  586. package/src/components/ui-shared/checkbox.tsx +50 -0
  587. package/src/components/ui-shared/context-menu.tsx +280 -0
  588. package/src/components/ui-shared/dialog.tsx +183 -0
  589. package/src/components/ui-shared/dropdown-menu.tsx +286 -0
  590. package/src/components/ui-shared/empty.tsx +121 -0
  591. package/src/components/ui-shared/grid-pattern.tsx +87 -0
  592. package/src/components/ui-shared/index.ts +23 -0
  593. package/src/components/ui-shared/input.tsx +36 -0
  594. package/src/components/ui-shared/item.tsx +213 -0
  595. package/src/components/ui-shared/kbd.tsx +33 -0
  596. package/src/components/ui-shared/label.tsx +39 -0
  597. package/src/components/ui-shared/layout-panel-top.tsx +160 -0
  598. package/src/components/ui-shared/name-confirm-dialog.tsx +153 -0
  599. package/src/components/ui-shared/nav-float-button.tsx +301 -0
  600. package/src/components/ui-shared/notion-avatar.tsx +126 -0
  601. package/src/components/ui-shared/placeholder-page.tsx +42 -0
  602. package/src/components/ui-shared/popover.tsx +106 -0
  603. package/src/components/ui-shared/reference-inline.tsx +53 -0
  604. package/src/components/ui-shared/reference-search.tsx +52 -0
  605. package/src/components/ui-shared/resizable.tsx +67 -0
  606. package/src/components/ui-shared/select.tsx +207 -0
  607. package/src/components/ui-shared/separator.tsx +45 -0
  608. package/src/components/ui-shared/sheet.tsx +164 -0
  609. package/src/components/ui-shared/sidebar.tsx +708 -0
  610. package/src/components/ui-shared/skeleton.tsx +30 -0
  611. package/src/components/ui-shared/sonner.tsx +37 -0
  612. package/src/components/ui-shared/switch.tsx +48 -0
  613. package/src/components/ui-shared/textarea.tsx +35 -0
  614. package/src/components/ui-shared/timezone-provider.tsx +55 -0
  615. package/src/components/ui-shared/tooltip.tsx +72 -0
  616. package/src/components/ui-shared/use-mobile.ts +36 -0
  617. package/src/components/ui-shared/user-avatar.tsx +83 -0
  618. package/src/components/ui-shared/utils.ts +6 -0
  619. package/src/components/workspace/schemas/create-workspace.ts +1 -1
  620. package/src/hooks/use-entity-subscription.ts +1 -1
  621. package/src/hooks/use-notifications.ts +2 -2
  622. package/src/i18n/__tests__/detect.test.ts +48 -0
  623. package/src/i18n/__tests__/integration.test.tsx +32 -0
  624. package/src/i18n/__tests__/server.test.ts +33 -0
  625. package/src/i18n/client.ts +32 -0
  626. package/src/i18n/config.ts +114 -18
  627. package/src/i18n/detect.ts +43 -0
  628. package/src/i18n/locales/en/agents.json +134 -0
  629. package/src/i18n/locales/en/apps.json +6 -0
  630. package/src/i18n/locales/en/auth.json +90 -0
  631. package/src/i18n/locales/en/billing.json +78 -0
  632. package/src/i18n/locales/en/chat.json +194 -0
  633. package/src/i18n/locales/en/common.json +242 -0
  634. package/src/i18n/locales/en/dashboard.json +38 -0
  635. package/src/i18n/locales/en/design.json +70 -0
  636. package/src/i18n/locales/en/desktop.json +35 -0
  637. package/src/i18n/locales/en/display.json +37 -0
  638. package/src/i18n/locales/en/editor.json +304 -0
  639. package/src/i18n/locales/en/enterprise.json +1 -0
  640. package/src/i18n/locales/en/invite.json +56 -0
  641. package/src/i18n/locales/en/iterations.json +92 -0
  642. package/src/i18n/locales/en/knowledge.json +462 -0
  643. package/src/i18n/locales/en/mindmap.json +28 -0
  644. package/src/i18n/locales/en/notifications.json +79 -0
  645. package/src/i18n/locales/en/prd.json +51 -0
  646. package/src/i18n/locales/en/projects.json +140 -0
  647. package/src/i18n/locales/en/settings.json +428 -0
  648. package/src/i18n/locales/en/share.json +20 -0
  649. package/src/i18n/locales/en/story.json +923 -0
  650. package/src/i18n/locales/en/tags.json +42 -0
  651. package/src/i18n/locales/en/workspace.json +90 -0
  652. package/src/i18n/locales/zh/agents.json +134 -0
  653. package/src/i18n/locales/zh/apps.json +6 -0
  654. package/src/i18n/locales/zh/auth.json +90 -0
  655. package/src/i18n/locales/zh/billing.json +78 -0
  656. package/src/i18n/locales/zh/chat.json +194 -0
  657. package/src/i18n/locales/zh/common.json +242 -0
  658. package/src/i18n/locales/zh/dashboard.json +38 -0
  659. package/src/i18n/locales/zh/design.json +70 -0
  660. package/src/i18n/locales/zh/desktop.json +35 -0
  661. package/src/i18n/locales/zh/display.json +37 -0
  662. package/src/i18n/locales/zh/editor.json +304 -0
  663. package/src/i18n/locales/zh/enterprise.json +1 -0
  664. package/src/i18n/locales/zh/invite.json +56 -0
  665. package/src/i18n/locales/zh/iterations.json +92 -0
  666. package/src/i18n/locales/zh/knowledge.json +462 -0
  667. package/src/i18n/locales/zh/mindmap.json +28 -0
  668. package/src/i18n/locales/zh/notifications.json +79 -0
  669. package/src/i18n/locales/zh/prd.json +51 -0
  670. package/src/i18n/locales/zh/projects.json +140 -0
  671. package/src/i18n/locales/zh/settings.json +428 -0
  672. package/src/i18n/locales/zh/share.json +20 -0
  673. package/src/i18n/locales/zh/story.json +923 -0
  674. package/src/i18n/locales/zh/tags.json +42 -0
  675. package/src/i18n/locales/zh/workspace.json +90 -0
  676. package/src/i18n/server.ts +18 -0
  677. package/src/i18n/types.ts +5 -0
  678. package/src/lib/__tests__/ai-adapter.test.ts +5 -5
  679. package/src/lib/__tests__/cli-detector.test.ts +6 -6
  680. package/src/lib/__tests__/embedding.test.ts +1 -1
  681. package/src/lib/__tests__/module-registry.test.ts +1 -1
  682. package/src/lib/__tests__/provider-router.test.ts +2 -2
  683. package/src/lib/__tests__/rag.test.ts +1 -1
  684. package/src/lib/ai/__tests__/story-prompt.test.ts +1 -1
  685. package/src/lib/annotation-template.ts +33 -1
  686. package/src/lib/auth/auth-client.ts +1 -1
  687. package/src/lib/auth/auth.config.ts +1 -1
  688. package/src/lib/changelog/sdk-versions.ts +1 -8
  689. package/src/lib/collections/index.ts +1 -1
  690. package/src/lib/collections/knowledge/knowledge-bases-collection.ts +1 -1
  691. package/src/lib/collections/reference/bookmark-categories-collection.ts +1 -1
  692. package/src/lib/collections/reference/labels-collection.ts +1 -1
  693. package/src/lib/collections/reference/projects-collection.ts +1 -1
  694. package/src/lib/collections/reference/users-brief-collection.ts +1 -1
  695. package/src/lib/collections/reference/workspaces-collection.ts +1 -1
  696. package/src/lib/comment-utils.ts +47 -1
  697. package/src/lib/crypto/api-key-cipher.ts +1 -1
  698. package/src/lib/datetime.ts +130 -7
  699. package/src/lib/outline-utils.ts +57 -1
  700. package/src/lib/pacer.ts +96 -11
  701. package/src/lib/prd/__tests__/stack-layout.test.ts +1 -1
  702. package/src/lib/prd/stack-layout.ts +53 -1
  703. package/src/lib/prd-tree-utils.ts +94 -1
  704. package/src/lib/prototype-registry.ts +24 -1
  705. package/src/lib/query-client-seam.test.ts +19 -0
  706. package/src/lib/query-client-seam.ts +14 -0
  707. package/src/lib/rag.ts +1 -1
  708. package/src/lib/restore-version.ts +1 -1
  709. package/src/lib/router-context.ts +1 -1
  710. package/src/lib/share-events.ts +78 -1
  711. package/src/lib/sidebar-utils.ts +21 -1
  712. package/src/lib/sse-transport.ts +1 -1
  713. package/src/lib/stores/prd-canvas-store.ts +69 -1
  714. package/src/lib/tag-colors.ts +35 -1
  715. package/src/lib/time-format.ts +50 -1
  716. package/src/lib/utils.ts +1 -1
  717. package/src/plugins/core/context.ts +2 -0
  718. package/src/plugins/core/index.ts +1 -0
  719. package/src/plugins/core/inspiration/index.ts +17 -0
  720. package/src/plugins/core/inspiration/pages/inspiration-page.tsx +8 -0
  721. package/src/plugins/core/inspiration/routes.ts +12 -0
  722. package/src/plugins/core/inspiration/ui.tsx +28 -0
  723. package/src/plugins/core/prd/components/agent-input.tsx +193 -0
  724. package/src/plugins/core/prd/components/component-annotation-overlay/AnnotationOverlay.tsx +113 -0
  725. package/src/plugins/core/prd/components/component-editor/__tests__/field-utils.test.ts +93 -0
  726. package/src/plugins/core/prd/components/component-editor/collapsible-section.tsx +44 -0
  727. package/src/plugins/core/prd/components/component-editor/content-section.tsx +63 -0
  728. package/src/plugins/core/prd/components/component-editor/field-utils.ts +56 -0
  729. package/src/plugins/core/prd/components/component-editor/position-section.tsx +39 -0
  730. package/src/plugins/core/prd/components/component-editor/style-fields.tsx +187 -0
  731. package/src/plugins/core/prd/components/component-editor/use-field.ts +53 -0
  732. package/src/plugins/core/prd/components/component-thumbnail.tsx +109 -0
  733. package/src/plugins/core/prd/components/dialog/create-prd-dialog.tsx +148 -0
  734. package/src/plugins/core/prd/components/dialog/prd-detail-client.tsx +102 -0
  735. package/src/plugins/core/prd/components/element-context.ts +29 -0
  736. package/src/plugins/core/prd/components/index.ts +19 -0
  737. package/src/plugins/core/prd/components/page-version-panel/components/create-version-dialog.tsx +84 -0
  738. package/src/plugins/core/prd/components/page-version-panel/components/delete-version-dialog.tsx +61 -0
  739. package/src/plugins/core/prd/components/page-version-panel/components/edit-version-dialog.tsx +82 -0
  740. package/src/plugins/core/prd/components/page-version-panel/constants.tsx +23 -0
  741. package/src/plugins/core/prd/components/page-version-panel/types.ts +30 -0
  742. package/src/plugins/core/prd/components/page-version-panel.tsx +1100 -0
  743. package/src/plugins/core/prd/components/page-version-tree.tsx +308 -0
  744. package/src/plugins/core/prd/components/prd-agent-panel.tsx +429 -0
  745. package/src/plugins/core/prd/components/prd-comment-review-panel.tsx +227 -0
  746. package/src/plugins/core/prd/components/prd-component-editor-content.tsx +90 -0
  747. package/src/plugins/core/prd/components/prd-dashboard.tsx +302 -0
  748. package/src/plugins/core/prd/components/prd-doc-panel.tsx +116 -0
  749. package/src/plugins/core/prd/components/prd-editor-layout.tsx +466 -0
  750. package/src/plugins/core/prd/components/prd-filter-bar.tsx +103 -0
  751. package/src/plugins/core/prd/components/prd-left-sidebar.tsx +473 -0
  752. package/src/plugins/core/prd/components/prd-navigation.tsx +287 -0
  753. package/src/plugins/core/prd/components/prd-outline-tab.tsx +61 -0
  754. package/src/plugins/core/prd/components/prd-prototype-panel.tsx +142 -0
  755. package/src/plugins/core/prd/components/prd-right-sidebar.tsx +209 -0
  756. package/src/plugins/core/prd/components/prd-share-panel/components/single-version-share-card.tsx +201 -0
  757. package/src/plugins/core/prd/components/prd-share-panel/constants.ts +22 -0
  758. package/src/plugins/core/prd/components/prd-share-panel/types.ts +21 -0
  759. package/src/plugins/core/prd/components/prd-share-panel/utils.ts +30 -0
  760. package/src/plugins/core/prd/components/prd-share-panel.tsx +567 -0
  761. package/src/plugins/core/prd/components/prd-tree-page-tab.tsx +572 -0
  762. package/src/plugins/core/prd/components/prd-tree-reducer.ts +96 -0
  763. package/src/plugins/core/prd/components/prototype/IconPrototype.tsx +81 -0
  764. package/src/plugins/core/prd/components/prototype/PrototypeCanvas.tsx +411 -0
  765. package/src/plugins/core/prd/components/prototype/PrototypeElement.tsx +168 -0
  766. package/src/plugins/core/prd/components/prototype/ResizeHandles.tsx +95 -0
  767. package/src/plugins/core/prd/components/prototype/icon-components.ts +57 -0
  768. package/src/plugins/core/prd/components/prototype/imported/.gitkeep +0 -0
  769. package/src/plugins/core/prd/components/prototype/index.ts +31 -0
  770. package/src/plugins/core/prd/components/restore-version-dialog.tsx +76 -0
  771. package/src/plugins/core/prd/components/schemas/create-prd.ts +26 -0
  772. package/src/plugins/core/prd/components/share/prd-share-client.tsx +118 -0
  773. package/src/plugins/core/prd/components/share/share-annotation-overlay.tsx +122 -0
  774. package/src/plugins/core/prd/components/share/share-comment-panel.tsx +257 -0
  775. package/src/plugins/core/prd/components/share/share-comment-review-panel.tsx +75 -0
  776. package/src/plugins/core/prd/components/share/share-directory-tree.tsx +93 -0
  777. package/src/plugins/core/prd/components/share/share-doc-view.tsx +96 -0
  778. package/src/plugins/core/prd/components/share/share-layout.tsx +171 -0
  779. package/src/plugins/core/prd/components/share/share-nav-bar.tsx +72 -0
  780. package/src/plugins/core/prd/components/share/share-password-gate.tsx +90 -0
  781. package/src/plugins/core/prd/components/share/share-prototype-view.tsx +92 -0
  782. package/src/plugins/core/prd/components/share/share-review-panel.tsx +93 -0
  783. package/src/plugins/core/prd/components/share/share-sse-listener.tsx +76 -0
  784. package/src/plugins/core/prd/components/share/share-theme-toggle.tsx +57 -0
  785. package/src/plugins/core/prd/components/share/version-share-client.tsx +123 -0
  786. package/src/plugins/core/prd/components/tiptap/node/paragraph-node/paragraph-node.scss +286 -0
  787. package/src/plugins/core/prd/icon-picker-bridge.ts +41 -0
  788. package/src/plugins/core/prd/index.ts +40 -0
  789. package/src/plugins/core/prd/lib/ai/annotation-prompt.ts +66 -0
  790. package/src/plugins/core/prd/lib/restore-version.ts +53 -0
  791. package/src/plugins/core/prd/pages/prd-detail-page.tsx +88 -0
  792. package/src/plugins/core/prd/pages/prd-layout.tsx +28 -0
  793. package/src/plugins/core/prd/pages/share-prd-page.tsx +69 -0
  794. package/src/plugins/core/prd/prd-nav.tsx +92 -0
  795. package/src/plugins/core/prd/routes.tsx +103 -0
  796. package/src/plugins/core/prd/serverFns/ai/agent-conversations.ts +124 -0
  797. package/src/plugins/core/prd/serverFns/ai/fill-annotation.ts +80 -0
  798. package/src/plugins/core/prd/serverFns/ai/generate-prototype.ts +121 -0
  799. package/src/plugins/core/prd/serverFns/ai/providers.ts +528 -0
  800. package/src/plugins/core/prd/serverFns/comments.ts +109 -0
  801. package/src/plugins/core/prd/serverFns/components.ts +37 -0
  802. package/src/plugins/core/prd/serverFns/detail.ts +49 -0
  803. package/src/plugins/core/prd/serverFns/index.ts +14 -0
  804. package/src/plugins/core/prd/serverFns/page-versions.ts +137 -0
  805. package/src/plugins/core/prd/serverFns/pages.ts +164 -0
  806. package/src/plugins/core/prd/serverFns/prd.ts +164 -0
  807. package/src/plugins/core/prd/serverFns/reviews.ts +87 -0
  808. package/src/plugins/core/prd/serverFns/share/verify-password.ts +75 -0
  809. package/src/plugins/core/prd/serverFns/share/versions.ts +112 -0
  810. package/src/plugins/core/prd/serverFns/share.ts +119 -0
  811. package/src/plugins/core/prd/serverFns/shares-password.ts +189 -0
  812. package/src/plugins/core/prd/serverFns/shares.ts +336 -0
  813. package/src/plugins/core/prd/serverFns/versions.ts +277 -0
  814. package/src/plugins/core/prd/ui.ts +24 -0
  815. package/src/plugins/core/server/api-key-cipher.ts +61 -0
  816. package/src/plugins/core/server/rate-limit.ts +111 -0
  817. package/src/plugins/core/tags/__tests__/tags.test.ts +367 -0
  818. package/src/plugins/core/tags/index.ts +39 -0
  819. package/src/plugins/core/tags/nav-tag.tsx +137 -0
  820. package/src/plugins/core/tags/pages/tag-detail-page.tsx +31 -0
  821. package/src/plugins/core/tags/pages/tag-settings-page.tsx +34 -0
  822. package/src/plugins/core/tags/pages/tags-index-page.tsx +34 -0
  823. package/src/plugins/core/tags/routes.ts +65 -0
  824. package/src/plugins/core/tags/serverFns.ts +122 -0
  825. package/src/plugins/core/tags/tag-badge.tsx +57 -0
  826. package/src/plugins/core/tags/tag-color-classes.ts +61 -0
  827. package/src/plugins/core/tags/tag-color-picker.tsx +56 -0
  828. package/src/plugins/core/tags/tag-components-index.ts +23 -0
  829. package/src/plugins/core/tags/tag-manager.tsx +212 -0
  830. package/src/plugins/core/tags/tag-selector.tsx +169 -0
  831. package/src/plugins/core/tags/tags-collection.ts +40 -0
  832. package/src/plugins/core/tags/ui.ts +24 -0
  833. package/src/plugins/plugins.gen.ts +3 -3
  834. package/src/plugins/ui.gen.ts +1 -1
  835. package/src/providers/index.tsx +3 -3
  836. package/src/providers/timezone-provider.tsx +1 -1
  837. package/src/router.tsx +2 -2
  838. package/src/routes/__root.tsx +2 -2
  839. package/src/routes/_login/schemas/-auth.ts +1 -1
  840. package/src/routes/_nav/changelog/route.lazy.tsx +1 -1
  841. package/src/routes/_nav/changelog/route.tsx +1 -1
  842. package/src/routes/_nav/dashboard/-components/avatar-group.tsx +2 -2
  843. package/src/routes/_nav/dashboard/route.tsx +1 -1
  844. package/src/routes/_nav/inspiration/route.tsx +1 -1
  845. package/src/routes/_nav/notifications/route.tsx +1 -1
  846. package/src/routes/_nav/prd/$id/route.tsx +1 -1
  847. package/src/routes/_nav/prd/index.tsx +1 -1
  848. package/src/routes/_nav/prd/route.tsx +1 -1
  849. package/src/routes/_nav/projects/$projectId/index.lazy.tsx +1 -1
  850. package/src/routes/_nav/route.tsx +1 -1
  851. package/src/routes/_nav/settings/account/route.tsx +1 -1
  852. package/src/routes/_nav/settings/route.tsx +1 -1
  853. package/src/routes/_nav/tags/$id/route.tsx +1 -1
  854. package/src/routes/_nav/tags/index.tsx +1 -1
  855. package/src/routes/_nav/tags/route.tsx +1 -1
  856. package/src/routes/_nav/tags/settings/route.tsx +1 -1
  857. package/src/routes/api/auth/desktop-callback.ts +1 -1
  858. package/src/routes/api/chat.ts +2 -2
  859. package/src/routes/api/notifications/stream.ts +1 -1
  860. package/src/routes/api/story-ai.ts +1 -1
  861. package/src/routes/invite/$token/route.lazy.tsx +1 -1
  862. package/src/routes/server.ai-stream.ts +1 -1
  863. package/src/routes/share/$versionId/route.lazy.tsx +1 -1
  864. package/src/routes/share/prd/$prdId/route.tsx +1 -1
  865. package/src/server/__tests__/agent-client-graph.test.ts +8 -5
  866. package/src/server/__tests__/resource-registry.test.ts +1 -1
  867. package/src/server/ai-gateway/__tests__/conformance.test.ts +43 -0
  868. package/src/server/ai-gateway/__tests__/embedding-request.test.ts +84 -0
  869. package/src/server/ai-gateway/__tests__/embedding.test.ts +42 -0
  870. package/src/server/ai-gateway/__tests__/interrupt-store.test.ts +539 -0
  871. package/src/server/ai-gateway/__tests__/knowledge-scope.test.ts +207 -0
  872. package/src/server/ai-gateway/__tests__/label-suggest.test.ts +113 -0
  873. package/src/server/ai-gateway/__tests__/message-store.test.ts +197 -0
  874. package/src/server/ai-gateway/__tests__/rerank.test.ts +128 -0
  875. package/src/server/ai-gateway/__tests__/run-store.test.ts +378 -0
  876. package/src/server/ai-gateway/__tests__/runtime.test.ts +58 -0
  877. package/src/server/ai-gateway/__tests__/story-prompt.test.ts +34 -0
  878. package/src/server/ai-gateway/__tests__/tools-select.test.ts +103 -0
  879. package/src/server/ai-gateway/__tests__/unified-router.test.ts +124 -0
  880. package/src/server/ai-gateway/adapter.ts +255 -0
  881. package/src/server/ai-gateway/chat-handler.ts +238 -0
  882. package/src/server/ai-gateway/chat-stream-builder.ts +91 -0
  883. package/src/server/ai-gateway/connectivity-check.ts +148 -0
  884. package/src/server/ai-gateway/embedding.ts +67 -0
  885. package/src/server/ai-gateway/index.ts +111 -0
  886. package/src/server/ai-gateway/label-suggest.ts +126 -0
  887. package/src/server/ai-gateway/middlewares/index.ts +19 -0
  888. package/src/server/ai-gateway/middlewares/logger.ts +35 -0
  889. package/src/server/ai-gateway/middlewares/rate-limit.ts +40 -0
  890. package/src/server/ai-gateway/model-fetch.ts +168 -0
  891. package/src/server/ai-gateway/persistence/index.ts +36 -0
  892. package/src/server/ai-gateway/persistence/interrupt-store.ts +154 -0
  893. package/src/server/ai-gateway/persistence/message-store.ts +124 -0
  894. package/src/server/ai-gateway/persistence/run-store.ts +120 -0
  895. package/src/server/ai-gateway/ports.ts +71 -0
  896. package/src/server/ai-gateway/pro-services.ts +25 -0
  897. package/src/server/ai-gateway/provider-router.ts +238 -0
  898. package/src/server/ai-gateway/provider-tools.ts +42 -0
  899. package/src/server/ai-gateway/rerank.ts +63 -0
  900. package/src/server/ai-gateway/runtime.ts +42 -0
  901. package/src/server/ai-gateway/story-handler.ts +171 -0
  902. package/src/server/ai-gateway/story-prompt.ts +68 -0
  903. package/src/server/ai-gateway/tools/definitions.ts +99 -0
  904. package/src/server/ai-gateway/tools/delete-project.ts +39 -0
  905. package/src/server/ai-gateway/tools/index.ts +31 -0
  906. package/src/server/ai-gateway/tools/knowledge-scope.ts +56 -0
  907. package/src/server/ai-gateway/tools/select.ts +53 -0
  908. package/src/server/ai-gateway/tools/server.ts +198 -0
  909. package/src/server/ai-gateway/tools.ts +7 -0
  910. package/src/server/ai-gateway/types.ts +42 -0
  911. package/src/server/ai-gateway/unified-router.ts +180 -0
  912. package/src/server/ai-ports.ts +2 -2
  913. package/src/server/entitlements-token-client.ts +1 -1
  914. package/src/server/entitlements-token.ts +1 -1
  915. package/src/server/entitlements.ts +1 -1
  916. package/src/server/middlewares/rate-limit-server-fn.ts +1 -1
  917. package/src/server/notifications/event-bus.ts +1 -1
  918. package/src/server/notifications/notification-service.ts +3 -3
  919. package/src/server/resources.ts +1 -1
  920. package/src/server/serverFns/__tests__/knowledge-index.test.ts +1 -1
  921. package/src/server/serverFns/agents/agent-crud.ts +1 -1
  922. package/src/server/serverFns/agents/resolve-overrides.ts +2 -2
  923. package/src/server/serverFns/ai/agent-conversations.ts +1 -1
  924. package/src/server/serverFns/ai/fill-annotation.ts +1 -1
  925. package/src/server/serverFns/ai/generate-prototype.ts +1 -1
  926. package/src/server/serverFns/ai/providers.ts +1 -1
  927. package/src/server/serverFns/auth/session.ts +1 -1
  928. package/src/server/serverFns/billing.ts +1 -1
  929. package/src/server/serverFns/changelog/fetch.ts +1 -1
  930. package/src/server/serverFns/comments.ts +1 -1
  931. package/src/server/serverFns/components.ts +1 -1
  932. package/src/server/serverFns/knowledge-index-utils.ts +1 -1
  933. package/src/server/serverFns/locale.ts +1 -1
  934. package/src/server/serverFns/notifications.ts +1 -1
  935. package/src/server/serverFns/reviews.ts +1 -1
  936. package/src/server/serverFns/share/verify-password.ts +1 -1
  937. package/src/server/serverFns/share/versions.ts +1 -1
  938. package/src/server/serverFns/story/__tests__/label-suggest-fn.test.ts +2 -2
  939. package/src/server/serverFns/story/story-crud/label-suggest-core.ts +1 -1
  940. package/src/server/serverFns/translation.ts +1 -1
  941. package/src/shared/ai-cli/adapter.ts +59 -0
  942. package/src/shared/ai-cli/cli-detector.ts +64 -0
  943. package/src/start.ts +1 -1
  944. package/vite.shared.ts +1 -5
  945. package/src/lib/i18n/client.ts +0 -1
  946. package/src/lib/i18n/config.ts +0 -1
  947. package/src/lib/i18n/detect.ts +0 -1
  948. package/src/lib/i18n/server.ts +0 -1
  949. package/src/lib/i18n/types.ts +0 -1
@@ -0,0 +1,1306 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import type { Editor } from "@tiptap/react"
19
+ import type { Node } from "@tiptap/pm/model"
20
+ import type { Command } from "@tiptap/pm/state"
21
+ import { Selection, type EditorState, type Transaction } from "@tiptap/pm/state"
22
+ import type { FindNodeResult, Rect } from "@tiptap/pm/tables"
23
+ import {
24
+ TableMap,
25
+ CellSelection,
26
+ findTable,
27
+ selectedRect,
28
+ cellAround,
29
+ selectionCell,
30
+ isInTable,
31
+ } from "@tiptap/pm/tables"
32
+ import { Mapping } from "@tiptap/pm/transform"
33
+
34
+ export const RESIZE_MIN_WIDTH = 35
35
+ export const EMPTY_CELL_WIDTH = 120
36
+ export const EMPTY_CELL_HEIGHT = 40
37
+
38
+ export type Orientation = "row" | "column"
39
+ export interface CellInfo extends FindNodeResult {
40
+ row: number
41
+ column: number
42
+ }
43
+
44
+ export type CellCoordinates = {
45
+ row: number
46
+ col: number
47
+ }
48
+
49
+ export type SelectionReturnMode = "state" | "transaction" | "dispatch"
50
+
51
+ export type BaseSelectionOptions = { mode?: SelectionReturnMode }
52
+ export type DispatchSelectionOptions = {
53
+ mode: "dispatch"
54
+ dispatch: (tr: Transaction) => void
55
+ }
56
+ export type TransactionSelectionOptions = { mode: "transaction" }
57
+ export type StateSelectionOptions = { mode?: "state" }
58
+
59
+ export type TableInfo = {
60
+ map: TableMap
61
+ } & FindNodeResult
62
+
63
+ // ============================================================================
64
+ // HELPER CONSTANTS & UTILITIES
65
+ // ============================================================================
66
+
67
+ const EMPTY_CELLS_RESULT = { cells: [], mergedCells: [] }
68
+
69
+ export function isHTMLElement(n: unknown): n is HTMLElement {
70
+ return n instanceof HTMLElement
71
+ }
72
+
73
+ export type DomCellAroundResult =
74
+ | {
75
+ type: "cell"
76
+ domNode: HTMLElement
77
+ tbodyNode: HTMLTableSectionElement | null
78
+ }
79
+ | {
80
+ type: "wrapper"
81
+ domNode: HTMLElement
82
+ tbodyNode: HTMLTableSectionElement | null
83
+ }
84
+
85
+ export function safeClosest<T extends Element>(
86
+ start: Element | null,
87
+ selector: string
88
+ ): T | null {
89
+ return (start?.closest?.(selector) as T | null) ?? null
90
+ }
91
+
92
+ /**
93
+ * Walk up from an element until we find a TD/TH or the table wrapper.
94
+ * Returns the found element plus its tbody (if present).
95
+ */
96
+ export function domCellAround(
97
+ target: Element
98
+ ): DomCellAroundResult | undefined {
99
+ let current: Element | null = target
100
+
101
+ while (
102
+ current &&
103
+ current.tagName !== "TD" &&
104
+ current.tagName !== "TH" &&
105
+ !current.classList.contains("tableWrapper")
106
+ ) {
107
+ if (current.classList.contains("ProseMirror")) return undefined
108
+ current = isHTMLElement(current.parentNode)
109
+ ? (current.parentNode as Element)
110
+ : null
111
+ }
112
+
113
+ if (!current) return undefined
114
+
115
+ if (current.tagName === "TD" || current.tagName === "TH") {
116
+ return {
117
+ type: "cell",
118
+ domNode: current as HTMLElement,
119
+ tbodyNode: safeClosest<HTMLTableSectionElement>(current, "tbody"),
120
+ }
121
+ }
122
+
123
+ return {
124
+ type: "wrapper",
125
+ domNode: current as HTMLElement,
126
+ tbodyNode: (current as HTMLElement).querySelector("tbody"),
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Clamps a value between min and max bounds
132
+ */
133
+ export function clamp(value: number, min: number, max: number): number {
134
+ return Math.max(min, Math.min(value, max))
135
+ }
136
+
137
+ /**
138
+ * Validates if row/col indices are within table bounds
139
+ */
140
+ function isWithinBounds(row: number, col: number, map: TableMap): boolean {
141
+ return row >= 0 && row < map.height && col >= 0 && col < map.width
142
+ }
143
+
144
+ /**
145
+ * Resolves the index for a row or column based on current selection or provided value
146
+ */
147
+ function resolveOrientationIndex(
148
+ state: EditorState,
149
+ table: TableInfo,
150
+ orientation: Orientation,
151
+ providedIndex?: number
152
+ ): number | null {
153
+ if (typeof providedIndex === "number") {
154
+ return providedIndex
155
+ }
156
+
157
+ if (state.selection instanceof CellSelection) {
158
+ const rect = selectedRect(state)
159
+ return orientation === "row" ? rect.top : rect.left
160
+ }
161
+
162
+ const $cell = cellAround(state.selection.$anchor) ?? selectionCell(state)
163
+ if (!$cell) return null
164
+
165
+ const rel = $cell.pos - table.start
166
+ const rect = table.map.findCell(rel)
167
+ return orientation === "row" ? rect.top : rect.left
168
+ }
169
+
170
+ /**
171
+ * Creates a CellInfo object from position data
172
+ */
173
+ function createCellInfo(
174
+ row: number,
175
+ column: number,
176
+ cellPos: number,
177
+ cellNode: Node
178
+ ): CellInfo {
179
+ return {
180
+ row,
181
+ column,
182
+ pos: cellPos,
183
+ node: cellNode,
184
+ start: cellPos + 1,
185
+ depth: cellNode ? cellNode.content.size : 0,
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Checks if a cell is merged (has colspan or rowspan > 1)
191
+ */
192
+ export function isCellMerged(node: Node | null): boolean {
193
+ if (!node) return false
194
+ const colspan = node.attrs.colspan ?? 1
195
+ const rowspan = node.attrs.rowspan ?? 1
196
+ return colspan > 1 || rowspan > 1
197
+ }
198
+
199
+ /**
200
+ * Generic function to collect cells along a row or column
201
+ */
202
+ function collectCells(
203
+ editor: Editor | null,
204
+ orientation: Orientation,
205
+ index?: number,
206
+ tablePos?: number
207
+ ): { cells: CellInfo[]; mergedCells: CellInfo[] } {
208
+ if (!editor) return EMPTY_CELLS_RESULT
209
+
210
+ const { state } = editor
211
+ const table = getTable(editor, tablePos)
212
+ if (!table) return EMPTY_CELLS_RESULT
213
+
214
+ const tableStart = table.start
215
+ const tableNode = table.node
216
+ const map = table.map
217
+
218
+ const resolvedIndex = resolveOrientationIndex(
219
+ state,
220
+ table,
221
+ orientation,
222
+ index
223
+ )
224
+ if (resolvedIndex === null) return EMPTY_CELLS_RESULT
225
+
226
+ // Bounds check
227
+ const maxIndex = orientation === "row" ? map.height : map.width
228
+ if (resolvedIndex < 0 || resolvedIndex >= maxIndex) {
229
+ return EMPTY_CELLS_RESULT
230
+ }
231
+
232
+ const cells: CellInfo[] = []
233
+ const mergedCells: CellInfo[] = []
234
+ const seenMerged = new Set<number>()
235
+
236
+ const iterationCount = orientation === "row" ? map.width : map.height
237
+
238
+ for (let i = 0; i < iterationCount; i++) {
239
+ const row = orientation === "row" ? resolvedIndex : i
240
+ const col = orientation === "row" ? i : resolvedIndex
241
+ const cellIndex = row * map.width + col
242
+ const mapCell = map.map[cellIndex]
243
+
244
+ if (mapCell === undefined) continue
245
+
246
+ const cellPos = tableStart + mapCell
247
+ const cellNode = tableNode.nodeAt(mapCell)
248
+ if (!cellNode) continue
249
+
250
+ const cell = createCellInfo(row, col, cellPos, cellNode)
251
+
252
+ if (isCellMerged(cellNode)) {
253
+ if (!seenMerged.has(cellPos)) {
254
+ mergedCells.push(cell)
255
+ seenMerged.add(cellPos)
256
+ }
257
+ }
258
+
259
+ cells.push(cell)
260
+ }
261
+
262
+ return { cells, mergedCells }
263
+ }
264
+
265
+ /**
266
+ * Generic function to count empty cells from the end of a row or column
267
+ */
268
+ function countEmptyCellsFromEnd(
269
+ editor: Editor,
270
+ tablePos: number,
271
+ orientation: Orientation
272
+ ): number {
273
+ const table = getTable(editor, tablePos)
274
+ if (!table) return 0
275
+
276
+ const { doc } = editor.state
277
+ const maxIndex = orientation === "row" ? table.map.height : table.map.width
278
+
279
+ let emptyCount = 0
280
+ for (let idx = maxIndex - 1; idx >= 0; idx--) {
281
+ const seen = new Set<number>()
282
+ let isLineEmpty = true
283
+
284
+ const iterationCount =
285
+ orientation === "row" ? table.map.width : table.map.height
286
+
287
+ for (let i = 0; i < iterationCount; i++) {
288
+ const row = orientation === "row" ? idx : i
289
+ const col = orientation === "row" ? i : idx
290
+ const rel = table.map.positionAt(row, col, table.node)
291
+
292
+ if (seen.has(rel)) continue
293
+ seen.add(rel)
294
+
295
+ const abs = tablePos + 1 + rel
296
+ const cell = doc.nodeAt(abs)
297
+ if (!cell) continue
298
+
299
+ if (!isCellEmpty(cell)) {
300
+ isLineEmpty = false
301
+ break
302
+ }
303
+ }
304
+
305
+ if (isLineEmpty) emptyCount++
306
+ else break
307
+ }
308
+
309
+ return emptyCount
310
+ }
311
+
312
+ /**
313
+ * Get information about the table at the current selection or a specific position.
314
+ *
315
+ * If `tablePos` is provided, it looks for a table at that exact position.
316
+ * Otherwise, it finds the nearest table containing the current selection.
317
+ *
318
+ * Returns an object with:
319
+ * - `node`: the table node
320
+ * - `pos`: the position of the table in the document
321
+ * - `start`: the position just after the table node (where its content starts)
322
+ * - `map`: the `TableMap` for layout info (rows, columns, spans)
323
+ *
324
+ * If no table is found, returns null.
325
+ */
326
+ export function getTable(editor: Editor | null, tablePos?: number) {
327
+ if (!editor) return null
328
+
329
+ let table: any = null
330
+
331
+ if (typeof tablePos === "number") {
332
+ const tableNode = editor.state.doc.nodeAt(tablePos)
333
+ if (tableNode?.type.name === "table") {
334
+ table = {
335
+ node: tableNode,
336
+ pos: tablePos,
337
+ start: tablePos + 1,
338
+ depth: editor.state.doc.resolve(tablePos).depth,
339
+ }
340
+ }
341
+ }
342
+
343
+ if (!table) {
344
+ const { state } = editor
345
+ const $from = state.doc.resolve(state.selection.from)
346
+ table = findTable($from) as any
347
+ }
348
+
349
+ if (!table) return null
350
+
351
+ const tableMap = TableMap.get(table.node) as any
352
+ if (!tableMap) return null
353
+
354
+ return { ...table, map: tableMap }
355
+ }
356
+
357
+ /**
358
+ * Checks if the current text selection is inside a table cell.
359
+ * @param state - The editor state to check
360
+ * @returns true if the selection is inside a table cell; false otherwise
361
+ */
362
+ export function isSelectionInCell(state: EditorState): boolean {
363
+ const { selection } = state
364
+ const $from = selection.$from
365
+
366
+ for (let depth = $from.depth; depth > 0; depth--) {
367
+ const node = $from.node(depth)
368
+ if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
369
+ return true
370
+ }
371
+ }
372
+
373
+ return false
374
+ }
375
+
376
+ /**
377
+ * Cells overlap a rectangle if any of the cells in the rectangle are merged
378
+ * with cells outside the rectangle.
379
+ */
380
+ export function cellsOverlapRectangle(
381
+ { width, height, map }: TableMap,
382
+ rect: Rect
383
+ ) {
384
+ let indexTop = rect.top * width + rect.left,
385
+ indexLeft = indexTop
386
+ let indexBottom = (rect.bottom - 1) * width + rect.left,
387
+ indexRight = indexTop + (rect.right - rect.left - 1)
388
+ for (let i = rect.top; i < rect.bottom; i++) {
389
+ if (
390
+ (rect.left > 0 && map[indexLeft] == map[indexLeft - 1]) ||
391
+ (rect.right < width && map[indexRight] == map[indexRight + 1])
392
+ )
393
+ return true
394
+ indexLeft += width
395
+ indexRight += width
396
+ }
397
+ for (let i = rect.left; i < rect.right; i++) {
398
+ if (
399
+ (rect.top > 0 && map[indexTop] == map[indexTop - width]) ||
400
+ (rect.bottom < height && map[indexBottom] == map[indexBottom + width])
401
+ )
402
+ return true
403
+ indexTop++
404
+ indexBottom++
405
+ }
406
+ return false
407
+ }
408
+
409
+ /**
410
+ * Runs a function while preserving the editor's selection.
411
+ * @param editor The Tiptap editor instance
412
+ * @param fn The function to run
413
+ * @returns True if the selection was successfully restored, false otherwise
414
+ */
415
+ export function runPreservingCursor(editor: Editor, fn: () => void): boolean {
416
+ const view = editor.view
417
+ const startSel = view.state.selection
418
+ const bookmark = startSel.getBookmark()
419
+
420
+ const mapping = new Mapping()
421
+ const originalDispatch = view.dispatch
422
+
423
+ view.dispatch = (tr) => {
424
+ mapping.appendMapping(tr.mapping)
425
+ originalDispatch(tr)
426
+ }
427
+
428
+ try {
429
+ fn()
430
+ } finally {
431
+ view.dispatch = originalDispatch
432
+ }
433
+
434
+ try {
435
+ const sel = bookmark.map(mapping).resolve(view.state.doc)
436
+ view.dispatch(view.state.tr.setSelection(sel))
437
+ return true
438
+ } catch {
439
+ // Fallback: if the exact spot vanished (e.g., cell deleted),
440
+ // go to the nearest valid position.
441
+ const mappedPos = mapping.map(startSel.from, -1)
442
+ const clamped = clamp(mappedPos, 0, view.state.doc.content.size)
443
+ const near = Selection.near(view.state.doc.resolve(clamped), -1)
444
+ view.dispatch(view.state.tr.setSelection(near))
445
+ return false
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Determines whether a table cell is effectively empty.
451
+ *
452
+ * A cell is considered empty when:
453
+ * - it has no children, or
454
+ * - it contains only whitespace text, or
455
+ * - it contains no text and no non-text leaf nodes (images, embeds, etc.)
456
+ *
457
+ * Early-outs as soon as any meaningful content is found.
458
+ *
459
+ * @param cellNode - The table cell node to check
460
+ * @returns true if the cell is empty; false otherwise
461
+ */
462
+ export function isCellEmpty(cellNode: Node): boolean {
463
+ if (cellNode.childCount === 0) return true
464
+
465
+ let isEmpty = true
466
+ cellNode.descendants((n) => {
467
+ if (n.isText && n.text?.trim()) {
468
+ isEmpty = false
469
+ return false
470
+ }
471
+ if (n.isLeaf && !n.isText) {
472
+ isEmpty = false
473
+ return false
474
+ }
475
+ return true
476
+ })
477
+
478
+ return isEmpty
479
+ }
480
+
481
+ /**
482
+ * Determine if the current selection is a full row or column selection.
483
+ *
484
+ * If the selection is a `CellSelection` that spans an entire row or column,
485
+ * returns an object indicating the type and index:
486
+ * - `{ type: "row", index: number }` for full row selections
487
+ * - `{ type: "column", index: number }` for full column selections
488
+ *
489
+ * If the selection is not a full row/column, or if no table is found, returns null.
490
+ */
491
+ export function getTableSelectionType(
492
+ editor: Editor | null,
493
+ index?: number,
494
+ orientation?: Orientation,
495
+ tablePos?: number
496
+ ): { orientation: Orientation; index: number } | null {
497
+ if (typeof index === "number" && orientation) {
498
+ return { orientation, index }
499
+ }
500
+
501
+ if (!editor) return null
502
+
503
+ const { state } = editor
504
+
505
+ const table = getTable(editor, tablePos)
506
+ if (!table) return null
507
+
508
+ if (state.selection instanceof CellSelection) {
509
+ const rect = selectedRect(state)
510
+ const width = rect.right - rect.left
511
+ const height = rect.bottom - rect.top
512
+
513
+ if (height === 1 && width >= 1) {
514
+ return { orientation: "row", index: rect.top }
515
+ }
516
+
517
+ if (width === 1 && height >= 1) {
518
+ return { orientation: "column", index: rect.left }
519
+ }
520
+
521
+ return null
522
+ }
523
+
524
+ return null
525
+ }
526
+
527
+ /**
528
+ * Get all cells (and unique merged cells) in the selected row or column.
529
+ *
530
+ * - If `index` is provided, uses that row/column index.
531
+ * - If omitted, uses the first selected row/column based on current selection.
532
+ *
533
+ * Returns an object with:
534
+ * - `cells`: all cells in the row/column
535
+ * - `mergedCells`: only the unique cells that have rowspan/colspan > 1
536
+ *
537
+ * If no valid selection or index is found, returns empty arrays.
538
+ */
539
+ export function getRowOrColumnCells(
540
+ editor: Editor | null,
541
+ index?: number,
542
+ orientation?: Orientation,
543
+ tablePos?: number
544
+ ): {
545
+ cells: CellInfo[]
546
+ mergedCells: CellInfo[]
547
+ index?: number
548
+ orientation?: Orientation
549
+ tablePos?: number
550
+ } {
551
+ const emptyResult = {
552
+ cells: [],
553
+ mergedCells: [],
554
+ index: undefined,
555
+ orientation: undefined,
556
+ tablePos: undefined,
557
+ }
558
+
559
+ if (!editor) {
560
+ return emptyResult
561
+ }
562
+
563
+ if (
564
+ typeof index !== "number" &&
565
+ !(editor.state.selection instanceof CellSelection)
566
+ ) {
567
+ return emptyResult
568
+ }
569
+
570
+ let finalIndex = index
571
+ let finalOrientation = orientation
572
+
573
+ if (
574
+ typeof finalIndex !== "number" ||
575
+ !finalOrientation ||
576
+ !["row", "column"].includes(finalOrientation)
577
+ ) {
578
+ const selectionType = getTableSelectionType(editor)
579
+ if (!selectionType) return emptyResult
580
+
581
+ finalIndex = selectionType.index
582
+ finalOrientation = selectionType.orientation
583
+ }
584
+
585
+ const result = collectCells(editor, finalOrientation, finalIndex, tablePos)
586
+ return { ...result, index: finalIndex, orientation: finalOrientation }
587
+ }
588
+
589
+ /**
590
+ * Collect cells (and unique merged cells) from a specific row.
591
+ * - If `rowIndex` is provided, scans that row.
592
+ * - If omitted, uses the first (topmost) selected row based on the current selection.
593
+ */
594
+ export function getRowCells(
595
+ editor: Editor | null,
596
+ rowIndex?: number,
597
+ tablePos?: number
598
+ ): { cells: CellInfo[]; mergedCells: CellInfo[] } {
599
+ return collectCells(editor, "row", rowIndex, tablePos)
600
+ }
601
+
602
+ /**
603
+ * Collect cells (and unique merged cells) from the current table.
604
+ * - If `columnIndex` is provided, scans that column.
605
+ * - If omitted, uses the first (leftmost) selected column based on the current selection.
606
+ */
607
+ export function getColumnCells(
608
+ editor: Editor | null,
609
+ columnIndex?: number,
610
+ tablePos?: number
611
+ ): { cells: CellInfo[]; mergedCells: CellInfo[] } {
612
+ return collectCells(editor, "column", columnIndex, tablePos)
613
+ }
614
+
615
+ /**
616
+ * After moving a row or column, update the selection to the moved item.
617
+ *
618
+ * This ensures that after a move operation, the selection remains on the
619
+ * moved row or column, providing better user feedback.
620
+ *
621
+ * @param editor - The editor instance
622
+ * @param orientation - "row" or "column" indicating what was moved
623
+ * @param newIndex - The new index of the moved row/column
624
+ * @param tablePos - Optional position of the table in the document
625
+ */
626
+ export function updateSelectionAfterAction(
627
+ editor: Editor,
628
+ orientation: Orientation,
629
+ newIndex: number,
630
+ tablePos?: number
631
+ ): void {
632
+ try {
633
+ const table = getTable(editor, tablePos)
634
+ if (!table) return
635
+
636
+ const { state } = editor
637
+ const { map } = table
638
+
639
+ if (orientation === "row") {
640
+ if (newIndex >= 0 && newIndex < map.height) {
641
+ const startCol = 0
642
+ const endCol = map.width - 1
643
+
644
+ const startCellPos =
645
+ table.start + map.positionAt(newIndex, startCol, table.node)
646
+ const endCellPos =
647
+ table.start + map.positionAt(newIndex, endCol, table.node)
648
+
649
+ const $start = state.doc.resolve(startCellPos)
650
+ const $end = state.doc.resolve(endCellPos)
651
+
652
+ const newSelection = CellSelection.create(
653
+ state.doc,
654
+ $start.pos,
655
+ $end.pos
656
+ )
657
+ const tr = state.tr.setSelection(newSelection)
658
+ editor.view.dispatch(tr)
659
+ }
660
+ } else if (orientation === "column") {
661
+ if (newIndex >= 0 && newIndex < map.width) {
662
+ const startRow = 0
663
+ const endRow = map.height - 1
664
+
665
+ const startCellPos =
666
+ table.start + map.positionAt(startRow, newIndex, table.node)
667
+ const endCellPos =
668
+ table.start + map.positionAt(endRow, newIndex, table.node)
669
+
670
+ const $start = state.doc.resolve(startCellPos)
671
+ const $end = state.doc.resolve(endCellPos)
672
+
673
+ const newSelection = CellSelection.create(
674
+ state.doc,
675
+ $start.pos,
676
+ $end.pos
677
+ )
678
+ const tr = state.tr.setSelection(newSelection)
679
+ editor.view.dispatch(tr)
680
+ }
681
+ }
682
+ } catch (error) {
683
+ console.warn("Failed to update selection after move:", error)
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Returns a command that sets the given attributes to the given values,
689
+ * and is only available when the currently selected cell doesn't
690
+ * already have those attributes set to those values.
691
+ *
692
+ * @public
693
+ */
694
+ export function setCellAttr(attrs: Record<string, unknown>): Command
695
+ export function setCellAttr(name: string, value: unknown): Command
696
+ export function setCellAttr(
697
+ nameOrAttrs: string | Record<string, unknown>,
698
+ value?: unknown
699
+ ): Command {
700
+ return function (state, dispatch) {
701
+ if (!isInTable(state)) return false
702
+ const $cell = selectionCell(state)
703
+
704
+ const attrs =
705
+ typeof nameOrAttrs === "string" ? { [nameOrAttrs]: value } : nameOrAttrs
706
+
707
+ if (dispatch) {
708
+ const tr = state.tr
709
+ if (state.selection instanceof CellSelection) {
710
+ state.selection.forEachCell((node, pos) => {
711
+ const needsUpdate = Object.entries(attrs).some(
712
+ ([name, val]) => node.attrs[name] !== val
713
+ )
714
+
715
+ if (needsUpdate) {
716
+ tr.setNodeMarkup(pos, null, {
717
+ ...node.attrs,
718
+ ...attrs,
719
+ })
720
+ }
721
+ })
722
+ } else {
723
+ const needsUpdate = Object.entries(attrs).some(
724
+ ([name, val]) => $cell.nodeAfter!.attrs[name] !== val
725
+ )
726
+
727
+ if (needsUpdate) {
728
+ tr.setNodeMarkup($cell.pos, null, {
729
+ ...$cell.nodeAfter!.attrs,
730
+ ...attrs,
731
+ })
732
+ }
733
+ }
734
+ dispatch(tr)
735
+ }
736
+ return true
737
+ }
738
+ }
739
+
740
+ /**
741
+ * Counts how many consecutive empty rows exist at the bottom of a given table.
742
+ *
743
+ * This function:
744
+ * - Locates the exact table in the document via reference matching
745
+ * - Iterates from the last visual row upward
746
+ * - Deduplicates cells per row using `TableMap` (merged cells can repeat positions)
747
+ * - Treats a row as empty only if all its unique cells are empty by `isCellEmpty`
748
+ *
749
+ * @param editor - The editor whose document contains the table
750
+ * @param target - The table node instance to analyze (must be the same reference as in the doc)
751
+ * @returns The number of trailing empty rows (0 if table not found)
752
+ */
753
+ export function countEmptyRowsFromEnd(
754
+ editor: Editor,
755
+ tablePos: number
756
+ ): number {
757
+ return countEmptyCellsFromEnd(editor, tablePos, "row")
758
+ }
759
+
760
+ /**
761
+ * Counts how many consecutive empty columns exist at the right edge of a given table.
762
+ *
763
+ * Similar to `countEmptyRowsFromEnd`, but scans by columns:
764
+ * - Iterates from the last visual column leftward
765
+ * - Deduplicates per-column cells using `TableMap`
766
+ * - A column is empty only if all unique cells in that column are empty
767
+ *
768
+ * @param editor - The editor whose document contains the table
769
+ * @param target - The table node instance to analyze (must be the same reference as in the doc)
770
+ * @returns The number of trailing empty columns (0 if table not found)
771
+ */
772
+ export function countEmptyColumnsFromEnd(
773
+ editor: Editor,
774
+ tablePos: number
775
+ ): number {
776
+ return countEmptyCellsFromEnd(editor, tablePos, "column")
777
+ }
778
+
779
+ /**
780
+ * Rounds a number with a symmetric "dead-zone" around integer boundaries,
781
+ * which makes drag/resize UX feel less jittery near thresholds.
782
+ *
783
+ * For example, with `margin = 0.3`:
784
+ * - values < n + 0.3 snap down to `n`
785
+ * - values > n + 0.7 snap up to `n + 1`
786
+ * - values in [n + 0.3, n + 0.7] fall back to `Math.round`
787
+ *
788
+ * @param num - The floating value to round
789
+ * @param margin - Half-width of the dead-zone around integer boundaries (default 0.3)
790
+ * @returns The rounded value using the dead-zone heuristic
791
+ */
792
+ export function marginRound(num: number, margin = 0.3): number {
793
+ const floor = Math.floor(num)
794
+ const ceil = Math.ceil(num)
795
+ const lowerBound = floor + margin
796
+ const upperBound = ceil - margin
797
+
798
+ if (num < lowerBound) return floor
799
+ if (num > upperBound) return ceil
800
+ return Math.round(num)
801
+ }
802
+
803
+ /**
804
+ * Compares two DOMRect objects for equality.
805
+ *
806
+ * Treats `undefined` as a valid state, where two `undefined` rects are equal,
807
+ * and `undefined` is not equal to any defined rect.
808
+ *
809
+ * @param a - The first DOMRect or undefined
810
+ * @param b - The second DOMRect or undefined
811
+ * @returns true if both rects are equal or both are undefined; false otherwise
812
+ */
813
+ export function rectEq(a?: DOMRect | null, b?: DOMRect | null): boolean {
814
+ if (!a && !b) return true
815
+ if (!a || !b) return false
816
+ return (
817
+ a.left === b.left &&
818
+ a.top === b.top &&
819
+ a.width === b.width &&
820
+ a.height === b.height
821
+ )
822
+ }
823
+
824
+ /**
825
+ * Applies the transaction based on the specified mode
826
+ */
827
+ function applySelectionWithMode(
828
+ state: EditorState,
829
+ transaction: Transaction,
830
+ options: BaseSelectionOptions | DispatchSelectionOptions
831
+ ): EditorState | Transaction | void {
832
+ const mode: SelectionReturnMode = options.mode ?? "state"
833
+
834
+ switch (mode) {
835
+ case "dispatch": {
836
+ const dispatchOptions = options as DispatchSelectionOptions
837
+ if (typeof dispatchOptions.dispatch === "function") {
838
+ dispatchOptions.dispatch(transaction)
839
+ }
840
+ return
841
+ }
842
+
843
+ case "transaction":
844
+ return transaction
845
+
846
+ default: // "state"
847
+ return state.apply(transaction)
848
+ }
849
+ }
850
+
851
+ /**
852
+ * Create or apply a `CellSelection` inside a table.
853
+ *
854
+ * Depending on the `mode` option, this helper behaves differently:
855
+ *
856
+ * - `"state"` (default) → Returns a new `EditorState` with the selection applied.
857
+ * - `"transaction"` → Returns a `Transaction` with the selection set, without applying it.
858
+ * - `"dispatch"` → Immediately calls `dispatch(tr)` with the new selection.
859
+ *
860
+ * This allows you to reuse the same helper in commands, tests, or utilities
861
+ * without duplicating logic.
862
+ *
863
+ * Example:
864
+ * ```ts
865
+ * // Get new state
866
+ * const nextState = createTableCellSelection(state, tablePosition, { row: 1, col: 1 }, { row: 2, col: 3 })
867
+ *
868
+ * // Get transaction only
869
+ * const tr = createTableCellSelection(state, tablePosition, { row: 0, col: 0 }, { row: 0, col: 2 }, { mode: "transaction" })
870
+ *
871
+ * // Dispatch directly
872
+ * createTableCellSelection(state, tablePosition, { row: 1, col: 1 }, { row: 3, col: 2 }, { mode: "dispatch", dispatch })
873
+ * ```
874
+ */
875
+ export function createTableCellSelection(
876
+ state: EditorState,
877
+ tablePosition: number,
878
+ startCell: CellCoordinates,
879
+ endCell?: CellCoordinates,
880
+ options?: StateSelectionOptions
881
+ ): EditorState
882
+ export function createTableCellSelection(
883
+ state: EditorState,
884
+ tablePosition: number,
885
+ startCell: CellCoordinates,
886
+ endCell: CellCoordinates | undefined,
887
+ options: TransactionSelectionOptions
888
+ ): Transaction
889
+ export function createTableCellSelection(
890
+ state: EditorState,
891
+ tablePosition: number,
892
+ startCell: CellCoordinates,
893
+ endCell: CellCoordinates | undefined,
894
+ options: DispatchSelectionOptions
895
+ ): void
896
+
897
+ export function createTableCellSelection(
898
+ state: EditorState,
899
+ tablePosition: number,
900
+ startCell: CellCoordinates,
901
+ endCell: CellCoordinates = startCell,
902
+ options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" }
903
+ ): EditorState | Transaction | void {
904
+ const startCellPosition = getCellPosition(state, tablePosition, startCell)
905
+ const endCellPosition = getCellPosition(state, tablePosition, endCell)
906
+
907
+ if (!startCellPosition || !endCellPosition) {
908
+ return
909
+ }
910
+
911
+ const transaction = state.tr.setSelection(
912
+ new CellSelection(startCellPosition, endCellPosition)
913
+ )
914
+
915
+ return applySelectionWithMode(state, transaction, options)
916
+ }
917
+
918
+ /**
919
+ * Get the position of a cell inside a table by relative row/col indices.
920
+ * Returns the position *before* the cell, which is what `CellSelection` expects.
921
+ */
922
+ export function getCellPosition(
923
+ state: EditorState,
924
+ tablePosition: number,
925
+ cellCoordinates: CellCoordinates
926
+ ) {
927
+ const resolvedTablePosition = state.doc.resolve(tablePosition)
928
+ const resolvedRowPosition = state.doc.resolve(
929
+ resolvedTablePosition.posAtIndex(cellCoordinates.row) + 1
930
+ )
931
+ const resolvedColPosition = state.doc.resolve(
932
+ resolvedRowPosition.posAtIndex(cellCoordinates.col)
933
+ )
934
+
935
+ const $cell = cellAround(resolvedColPosition)
936
+ if (!$cell) return null
937
+
938
+ return resolvedColPosition
939
+ }
940
+
941
+ /**
942
+ * Selects table cells by their (row, col) coordinates.
943
+ *
944
+ * This function can be used in three modes:
945
+ * - `"state"` (default) → Returns a new `EditorState` with the selection applied, or null if failed.
946
+ * - `"transaction"` → Returns a `Transaction` with the selection set, or null if failed.
947
+ * - `"dispatch"` → Immediately dispatches the selection and returns boolean success status.
948
+ *
949
+ * @param editor - The editor instance
950
+ * @param tablePos - Position of the table in the document
951
+ * @param coords - Array of {row, col} coordinates to select
952
+ * @param options - Mode and dispatch options
953
+ */
954
+ export function selectCellsByCoords(
955
+ editor: Editor | null,
956
+ tablePos: number,
957
+ coords: { row: number; col: number }[],
958
+ options?: StateSelectionOptions
959
+ ): EditorState
960
+ export function selectCellsByCoords(
961
+ editor: Editor | null,
962
+ tablePos: number,
963
+ coords: { row: number; col: number }[],
964
+ options: TransactionSelectionOptions
965
+ ): Transaction
966
+ export function selectCellsByCoords(
967
+ editor: Editor | null,
968
+ tablePos: number,
969
+ coords: { row: number; col: number }[],
970
+ options: DispatchSelectionOptions
971
+ ): void
972
+ export function selectCellsByCoords(
973
+ editor: Editor | null,
974
+ tablePos: number,
975
+ coords: { row: number; col: number }[],
976
+ options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" }
977
+ ): EditorState | Transaction | void {
978
+ if (!editor) return
979
+
980
+ const table = getTable(editor, tablePos)
981
+ if (!table) return
982
+
983
+ const { state } = editor
984
+ const tableMap = table.map
985
+
986
+ const cleanedCoords = coords
987
+ .map((coord) => ({
988
+ row: clamp(coord.row, 0, tableMap.height - 1),
989
+ col: clamp(coord.col, 0, tableMap.width - 1),
990
+ }))
991
+ .filter((coord) => isWithinBounds(coord.row, coord.col, tableMap))
992
+
993
+ if (cleanedCoords.length === 0) {
994
+ return
995
+ }
996
+
997
+ // --- Find the smallest rectangle that contains all our coordinates ---
998
+ const allRows = cleanedCoords.map((coord) => coord.row)
999
+ const topRow = Math.min(...allRows)
1000
+ const bottomRow = Math.max(...allRows)
1001
+
1002
+ const allCols = cleanedCoords.map((coord) => coord.col)
1003
+ const leftCol = Math.min(...allCols)
1004
+ const rightCol = Math.max(...allCols)
1005
+
1006
+ // --- Convert visual coordinates to document positions ---
1007
+ // Use TableMap.map array directly to handle merged cells correctly
1008
+ const getCellPositionFromMap = (row: number, col: number): number | null => {
1009
+ // TableMap.map is a flat array where each entry represents a cell
1010
+ // For merged cells, the same offset appears multiple times
1011
+ const cellOffset = tableMap.map[row * tableMap.width + col]
1012
+ if (cellOffset === undefined) return null
1013
+
1014
+ // Convert the relative offset to an absolute position in the document
1015
+ // tablePos + 1 skips the table opening tag
1016
+ return tablePos + 1 + cellOffset
1017
+ }
1018
+
1019
+ // Anchor = where the selection starts (top-left of bounding box)
1020
+ const anchorPosition = getCellPositionFromMap(topRow, leftCol)
1021
+ if (anchorPosition === null) return
1022
+
1023
+ // Head = where the selection ends (usually bottom-right of bounding box)
1024
+ let headPosition = getCellPositionFromMap(bottomRow, rightCol)
1025
+ if (headPosition === null) return
1026
+
1027
+ // --- Handle edge case with merged cells ---
1028
+ // If anchor and head point to the same cell, we need to find a different head
1029
+ // This happens when selecting a single merged cell or when all coords point to one cell
1030
+ if (headPosition === anchorPosition) {
1031
+ let foundDifferentCell = false
1032
+
1033
+ // Search backwards from bottom-right to find a cell with a different position
1034
+ for (let row = bottomRow; row >= topRow && !foundDifferentCell; row--) {
1035
+ for (let col = rightCol; col >= leftCol && !foundDifferentCell; col--) {
1036
+ const candidatePosition = getCellPositionFromMap(row, col)
1037
+
1038
+ if (
1039
+ candidatePosition !== null &&
1040
+ candidatePosition !== anchorPosition
1041
+ ) {
1042
+ headPosition = candidatePosition
1043
+ foundDifferentCell = true
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+
1049
+ try {
1050
+ const anchorRef = state.doc.resolve(anchorPosition)
1051
+ const headRef = state.doc.resolve(headPosition)
1052
+
1053
+ const cellSelection = new CellSelection(anchorRef, headRef)
1054
+ const transaction = state.tr.setSelection(cellSelection)
1055
+
1056
+ return applySelectionWithMode(state, transaction, options)
1057
+ } catch (error) {
1058
+ console.error("Failed to create cell selection:", error)
1059
+ return
1060
+ }
1061
+ }
1062
+
1063
+ /**
1064
+ * Select the cell at (row, col) using `cellAround` to respect merged cells.
1065
+ *
1066
+ * @param editor Tiptap editor
1067
+ * @param row Row index (0-based)
1068
+ * @param col Column index (0-based)
1069
+ * @param tablePos Optional absolute position of the table node
1070
+ * @param dispatch Optional dispatch; defaults to editor.view.dispatch
1071
+ */
1072
+ export function selectCellAt({
1073
+ editor,
1074
+ row,
1075
+ col,
1076
+ tablePos,
1077
+ dispatch,
1078
+ }: {
1079
+ editor: Editor | null
1080
+ row: number
1081
+ col: number
1082
+ tablePos?: number
1083
+ dispatch?: (tr: Transaction) => void
1084
+ }): boolean {
1085
+ if (!editor) return false
1086
+
1087
+ const { state, view } = editor
1088
+ const found = getTable(editor, tablePos)
1089
+ if (!found) return false
1090
+
1091
+ // Bounds check
1092
+ if (!isWithinBounds(row, col, found.map)) {
1093
+ return false
1094
+ }
1095
+
1096
+ const relCellPos = found.map.positionAt(row, col, found.node)
1097
+ const absCellPos = found.start + relCellPos
1098
+
1099
+ const $abs = state.doc.resolve(absCellPos)
1100
+ const $cell = cellAround($abs)
1101
+ const cellPos = $cell ? $cell.pos : absCellPos
1102
+
1103
+ const sel = CellSelection.create(state.doc, cellPos)
1104
+
1105
+ const doDispatch = dispatch ?? view?.dispatch
1106
+ if (!doDispatch) return false
1107
+
1108
+ doDispatch(state.tr.setSelection(sel))
1109
+ return true
1110
+ }
1111
+
1112
+ /**
1113
+ * Selects a boundary cell of the table based on orientation.
1114
+ *
1115
+ * For row orientation, selects the bottom-left cell of the table.
1116
+ * For column orientation, selects the top-right cell of the table.
1117
+ *
1118
+ * This function accounts for merged cells to ensure the correct cell is selected.
1119
+ *
1120
+ * @param editor The Tiptap editor instance
1121
+ * @param tableNode The table node
1122
+ * @param tablePos The position of the table node in the document
1123
+ * @param orientation "row" to select bottom-left, "column" to select top-right
1124
+ * @returns true if the selection was successful; false otherwise
1125
+ */
1126
+ export function selectLastCell(
1127
+ editor: Editor,
1128
+ tableNode: Node,
1129
+ tablePos: number,
1130
+ orientation: Orientation
1131
+ ) {
1132
+ const map = TableMap.get(tableNode)
1133
+ const isRow = orientation === "row"
1134
+
1135
+ // For rows, select bottom-left cell; for columns, select top-right cell
1136
+ const row = isRow ? map.height - 1 : 0
1137
+ const col = isRow ? 0 : map.width - 1
1138
+
1139
+ // Calculate the index in the table map
1140
+ const index = row * map.width + col
1141
+
1142
+ // Get the actual cell position from the map (handles merged cells)
1143
+ const cellPos = map.map[index]
1144
+ if (!cellPos && cellPos !== 0) {
1145
+ console.warn("selectLastCell: cell position not found in map", {
1146
+ index,
1147
+ row,
1148
+ col,
1149
+ map,
1150
+ })
1151
+ return false
1152
+ }
1153
+
1154
+ // Find the row and column of the actual cell
1155
+ const cellIndex = map.map.indexOf(cellPos)
1156
+ const actualRow = cellIndex >= 0 ? Math.floor(cellIndex / map.width) : 0
1157
+ const actualCol = cellIndex >= 0 ? cellIndex % map.width : 0
1158
+
1159
+ return selectCellAt({
1160
+ editor,
1161
+ row: actualRow,
1162
+ col: actualCol,
1163
+ tablePos,
1164
+ dispatch: editor.view.dispatch.bind(editor.view),
1165
+ })
1166
+ }
1167
+
1168
+ /**
1169
+ * Get all (row, col) coordinates for a given row or column index.
1170
+ *
1171
+ * - If `orientation` is "row", returns all columns in that row.
1172
+ * - If `orientation` is "column", returns all rows in that column.
1173
+ *
1174
+ * Returns null if:
1175
+ * - the editor or table is not found
1176
+ * - the index is out of bounds
1177
+ *
1178
+ * @param editor The Tiptap editor instance
1179
+ * @param index The row or column index (0-based)
1180
+ * @param orientation "row" to get row coordinates, "column" for column coordinates
1181
+ * @param tablePos Optional position of the table node in the document
1182
+ * @returns Array of {row, col} objects or null if invalid
1183
+ */
1184
+ export function getIndexCoordinates({
1185
+ editor,
1186
+ index,
1187
+ orientation,
1188
+ tablePos,
1189
+ }: {
1190
+ editor: Editor | null
1191
+ index: number
1192
+ orientation?: Orientation
1193
+ tablePos?: number
1194
+ }): { row: number; col: number }[] | null {
1195
+ if (!editor) return null
1196
+
1197
+ const table = getTable(editor, tablePos)
1198
+ if (!table) return null
1199
+
1200
+ const { map } = table
1201
+ const { width, height } = map
1202
+
1203
+ if (index < 0) return null
1204
+ if (orientation === "row" && index >= height) return null
1205
+ if (orientation === "column" && index >= width) return null
1206
+
1207
+ return orientation === "row"
1208
+ ? Array.from({ length: map.width }, (_, col) => ({ row: index, col }))
1209
+ : Array.from({ length: map.height }, (_, row) => ({ row, col: index }))
1210
+ }
1211
+
1212
+ /**
1213
+ * Given a DOM cell element, find its (row, col) indices within the table.
1214
+ *
1215
+ * This function:
1216
+ * - Locates the nearest ancestor table element
1217
+ * - Uses the editor's document model to resolve the cell's position
1218
+ * - Traverses up the node hierarchy to find the corresponding table cell node
1219
+ * - Uses `TableMap` to translate the cell's position into (row, col) indices
1220
+ *
1221
+ * Returns null if:
1222
+ * - the table or cell cannot be found in the editor's document
1223
+ * - any error occurs during position resolution
1224
+ *
1225
+ * @param cell The HTMLTableCellElement (td or th)
1226
+ * @param tableNode The table node in the ProseMirror document
1227
+ * @param editor The Tiptap editor instance
1228
+ * @returns An object with { rowIndex, colIndex } or null if not found
1229
+ */
1230
+ export function getCellIndicesFromDOM(
1231
+ cell: HTMLTableCellElement,
1232
+ tableNode: Node | null,
1233
+ editor: Editor
1234
+ ): { rowIndex: number; colIndex: number } | null {
1235
+ if (!tableNode) return null
1236
+
1237
+ try {
1238
+ const cellPos = editor.view.posAtDOM(cell, 0)
1239
+ const $cellPos = editor.view.state.doc.resolve(cellPos)
1240
+
1241
+ for (let d = $cellPos.depth; d > 0; d--) {
1242
+ const node = $cellPos.node(d)
1243
+ if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
1244
+ const tableMap = TableMap.get(tableNode)
1245
+ const cellNodePos = $cellPos.before(d)
1246
+ const tableStart = $cellPos.start(d - 2)
1247
+ const cellOffset = cellNodePos - tableStart
1248
+ const cellIndex = tableMap.map.indexOf(cellOffset)
1249
+
1250
+ return {
1251
+ rowIndex: Math.floor(cellIndex / tableMap.width),
1252
+ colIndex: cellIndex % tableMap.width,
1253
+ }
1254
+ }
1255
+ }
1256
+ } catch (error) {
1257
+ console.warn("Could not get cell position:", error)
1258
+ }
1259
+ return null
1260
+ }
1261
+
1262
+ /**
1263
+ * Given a DOM element inside a table, find the corresponding table node and its position.
1264
+ *
1265
+ * This function:
1266
+ * - Locates the nearest ancestor table element
1267
+ * - Uses the editor's document model to resolve the table's position
1268
+ * - Traverses up the node hierarchy to find the corresponding table node
1269
+ *
1270
+ * Returns null if:
1271
+ * - the table cannot be found in the editor's document
1272
+ * - any error occurs during position resolution
1273
+ *
1274
+ * @param tableElement The HTMLTableElement or an element inside it
1275
+ * @param editor The Tiptap editor instance
1276
+ * @returns An object with { node: tableNode, pos: tablePos } or null if not found
1277
+ */
1278
+ export function getTableFromDOM(
1279
+ tableElement: HTMLElement,
1280
+ editor: Editor
1281
+ ): { node: Node; pos: number } | null {
1282
+ try {
1283
+ const pos = editor.view.posAtDOM(tableElement, 0)
1284
+ const $pos = editor.view.state.doc.resolve(pos)
1285
+
1286
+ for (let d = $pos.depth; d >= 0; d--) {
1287
+ const node = $pos.node(d)
1288
+ if (isTableNode(node)) {
1289
+ return { node, pos: d === 0 ? 0 : $pos.before(d) }
1290
+ }
1291
+ }
1292
+ } catch (error) {
1293
+ console.warn("Could not get table from DOM:", error)
1294
+ }
1295
+ return null
1296
+ }
1297
+
1298
+ /**
1299
+ * Checks if a node is a table node
1300
+ */
1301
+ export function isTableNode(node: Node | null | undefined): node is Node {
1302
+ return (
1303
+ !!node &&
1304
+ (node.type.name === "table" || node.type.spec.tableRole === "table")
1305
+ )
1306
+ }