@fuma-content/studio 0.1.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 (413) hide show
  1. package/dist/bin.d.mts +1 -0
  2. package/dist/bin.mjs +90 -0
  3. package/dist/index.d.mts +13 -0
  4. package/dist/index.mjs +66 -0
  5. package/dist/mdx/actions.mjs +34 -0
  6. package/dist/mdx/client.mjs +167 -0
  7. package/dist/package.mjs +6 -0
  8. package/dist/src/components/code-editor/mdx.lazy.mjs +19 -0
  9. package/dist/src/components/code-editor/mdx.mjs +44 -0
  10. package/dist/src/components/code-editor/yaml.lazy.mjs +22 -0
  11. package/dist/src/components/code-editor/yaml.mjs +63 -0
  12. package/dist/src/components/editor/editor-base-kit.mjs +41 -0
  13. package/dist/src/components/editor/editor-kit.mjs +73 -0
  14. package/dist/src/components/editor/md.mjs +42 -0
  15. package/dist/src/components/editor/plugins/ai-kit.mjs +68 -0
  16. package/dist/src/components/editor/plugins/align-base-kit.mjs +28 -0
  17. package/dist/src/components/editor/plugins/align-kit.mjs +30 -0
  18. package/dist/src/components/editor/plugins/autoformat-kit.mjs +214 -0
  19. package/dist/src/components/editor/plugins/basic-blocks-base-kit.mjs +22 -0
  20. package/dist/src/components/editor/plugins/basic-blocks-kit.mjs +51 -0
  21. package/dist/src/components/editor/plugins/basic-marks-base-kit.mjs +20 -0
  22. package/dist/src/components/editor/plugins/basic-marks-kit.mjs +28 -0
  23. package/dist/src/components/editor/plugins/block-menu-kit.mjs +11 -0
  24. package/dist/src/components/editor/plugins/block-placeholder-kit.mjs +14 -0
  25. package/dist/src/components/editor/plugins/block-selection-kit.mjs +48 -0
  26. package/dist/src/components/editor/plugins/callout-base-kit.mjs +8 -0
  27. package/dist/src/components/editor/plugins/callout-kit.mjs +10 -0
  28. package/dist/src/components/editor/plugins/code-block-base-kit.mjs +16 -0
  29. package/dist/src/components/editor/plugins/code-block-kit.mjs +19 -0
  30. package/dist/src/components/editor/plugins/column-base-kit.mjs +8 -0
  31. package/dist/src/components/editor/plugins/column-kit.mjs +10 -0
  32. package/dist/src/components/editor/plugins/comment-base-kit.mjs +8 -0
  33. package/dist/src/components/editor/plugins/comment-kit.mjs +53 -0
  34. package/dist/src/components/editor/plugins/copilot-kit.mjs +49 -0
  35. package/dist/src/components/editor/plugins/cursor-overlay-kit.mjs +11 -0
  36. package/dist/src/components/editor/plugins/date-base-kit.mjs +8 -0
  37. package/dist/src/components/editor/plugins/date-kit.mjs +10 -0
  38. package/dist/src/components/editor/plugins/discussion-kit.mjs +94 -0
  39. package/dist/src/components/editor/plugins/dnd-kit.mjs +31 -0
  40. package/dist/src/components/editor/plugins/emoji-kit.mjs +11 -0
  41. package/dist/src/components/editor/plugins/exit-break-kit.mjs +12 -0
  42. package/dist/src/components/editor/plugins/fixed-toolbar-kit.mjs +15 -0
  43. package/dist/src/components/editor/plugins/floating-toolbar-kit.mjs +15 -0
  44. package/dist/src/components/editor/plugins/font-base-kit.mjs +14 -0
  45. package/dist/src/components/editor/plugins/font-kit.mjs +19 -0
  46. package/dist/src/components/editor/plugins/indent-base-kit.mjs +17 -0
  47. package/dist/src/components/editor/plugins/indent-kit.mjs +20 -0
  48. package/dist/src/components/editor/plugins/line-height-base-kit.mjs +20 -0
  49. package/dist/src/components/editor/plugins/line-height-kit.mjs +22 -0
  50. package/dist/src/components/editor/plugins/link-base-kit.mjs +8 -0
  51. package/dist/src/components/editor/plugins/link-kit.mjs +15 -0
  52. package/dist/src/components/editor/plugins/list-base-kit.mjs +19 -0
  53. package/dist/src/components/editor/plugins/list-kit.mjs +22 -0
  54. package/dist/src/components/editor/plugins/markdown-kit.mjs +17 -0
  55. package/dist/src/components/editor/plugins/math-base-kit.mjs +8 -0
  56. package/dist/src/components/editor/plugins/math-kit.mjs +10 -0
  57. package/dist/src/components/editor/plugins/media-base-kit.mjs +23 -0
  58. package/dist/src/components/editor/plugins/media-kit.mjs +37 -0
  59. package/dist/src/components/editor/plugins/slash-kit.mjs +11 -0
  60. package/dist/src/components/editor/plugins/suggestion-base-kit.mjs +8 -0
  61. package/dist/src/components/editor/plugins/suggestion-kit.mjs +50 -0
  62. package/dist/src/components/editor/plugins/table-base-kit.mjs +13 -0
  63. package/dist/src/components/editor/plugins/table-kit.mjs +15 -0
  64. package/dist/src/components/editor/transforms.mjs +125 -0
  65. package/dist/src/components/editor/ui/ai-chat-editor.mjs +21 -0
  66. package/dist/src/components/editor/ui/ai-menu.mjs +488 -0
  67. package/dist/src/components/editor/ui/ai-node.mjs +24 -0
  68. package/dist/src/components/editor/ui/ai-toolbar-button.mjs +23 -0
  69. package/dist/src/components/editor/ui/align-toolbar-button.mjs +71 -0
  70. package/dist/src/components/editor/ui/block-context-menu.mjs +130 -0
  71. package/dist/src/components/editor/ui/block-discussion.mjs +200 -0
  72. package/dist/src/components/editor/ui/block-draggable.mjs +265 -0
  73. package/dist/src/components/editor/ui/block-list-static.mjs +48 -0
  74. package/dist/src/components/editor/ui/block-list.mjs +47 -0
  75. package/dist/src/components/editor/ui/block-selection.mjs +28 -0
  76. package/dist/src/components/editor/ui/block-suggestion.mjs +319 -0
  77. package/dist/src/components/editor/ui/blockquote-node-static.mjs +14 -0
  78. package/dist/src/components/editor/ui/blockquote-node.mjs +16 -0
  79. package/dist/src/components/editor/ui/callout-node-static.mjs +29 -0
  80. package/dist/src/components/editor/ui/callout-node.mjs +50 -0
  81. package/dist/src/components/editor/ui/caption.mjs +39 -0
  82. package/dist/src/components/editor/ui/code-block-node-static.mjs +30 -0
  83. package/dist/src/components/editor/ui/code-block-node.mjs +121 -0
  84. package/dist/src/components/editor/ui/code-node-static.mjs +15 -0
  85. package/dist/src/components/editor/ui/code-node.mjs +17 -0
  86. package/dist/src/components/editor/ui/column-node-static.mjs +32 -0
  87. package/dist/src/components/editor/ui/column-node.mjs +229 -0
  88. package/dist/src/components/editor/ui/comment-node-static.mjs +14 -0
  89. package/dist/src/components/editor/ui/comment-node.mjs +33 -0
  90. package/dist/src/components/editor/ui/comment-toolbar-button.mjs +23 -0
  91. package/dist/src/components/editor/ui/comment.mjs +422 -0
  92. package/dist/src/components/editor/ui/cursor-overlay.mjs +36 -0
  93. package/dist/src/components/editor/ui/date-node-static.mjs +32 -0
  94. package/dist/src/components/editor/ui/date-node.mjs +60 -0
  95. package/dist/src/components/editor/ui/editor-static.mjs +31 -0
  96. package/dist/src/components/editor/ui/editor.mjs +60 -0
  97. package/dist/src/components/editor/ui/emoji-node.mjs +46 -0
  98. package/dist/src/components/editor/ui/emoji-toolbar-button.mjs +415 -0
  99. package/dist/src/components/editor/ui/equation-node-static.mjs +65 -0
  100. package/dist/src/components/editor/ui/equation-node.mjs +159 -0
  101. package/dist/src/components/editor/ui/equation-toolbar-button.mjs +23 -0
  102. package/dist/src/components/editor/ui/fixed-toolbar-buttons.mjs +109 -0
  103. package/dist/src/components/editor/ui/fixed-toolbar.mjs +16 -0
  104. package/dist/src/components/editor/ui/floating-toolbar-buttons.mjs +54 -0
  105. package/dist/src/components/editor/ui/floating-toolbar.mjs +51 -0
  106. package/dist/src/components/editor/ui/font-color-toolbar-button.mjs +637 -0
  107. package/dist/src/components/editor/ui/font-size-toolbar-button.mjs +83 -0
  108. package/dist/src/components/editor/ui/ghost-text.mjs +22 -0
  109. package/dist/src/components/editor/ui/heading-node-static.mjs +60 -0
  110. package/dist/src/components/editor/ui/heading-node.mjs +62 -0
  111. package/dist/src/components/editor/ui/highlight-node-static.mjs +15 -0
  112. package/dist/src/components/editor/ui/highlight-node.mjs +17 -0
  113. package/dist/src/components/editor/ui/history-toolbar-button.mjs +35 -0
  114. package/dist/src/components/editor/ui/hr-node-static.mjs +18 -0
  115. package/dist/src/components/editor/ui/hr-node.mjs +23 -0
  116. package/dist/src/components/editor/ui/indent-toolbar-button.mjs +29 -0
  117. package/dist/src/components/editor/ui/inline-combobox.mjs +148 -0
  118. package/dist/src/components/editor/ui/insert-toolbar-button.mjs +199 -0
  119. package/dist/src/components/editor/ui/kbd-node-static.mjs +15 -0
  120. package/dist/src/components/editor/ui/kbd-node.mjs +17 -0
  121. package/dist/src/components/editor/ui/line-height-toolbar-button.mjs +56 -0
  122. package/dist/src/components/editor/ui/link-node-static.mjs +20 -0
  123. package/dist/src/components/editor/ui/link-node.mjs +28 -0
  124. package/dist/src/components/editor/ui/link-toolbar-button.mjs +21 -0
  125. package/dist/src/components/editor/ui/link-toolbar.mjs +141 -0
  126. package/dist/src/components/editor/ui/list-toolbar-button.mjs +131 -0
  127. package/dist/src/components/editor/ui/mark-toolbar-button.mjs +20 -0
  128. package/dist/src/components/editor/ui/media-image-node-static.mjs +32 -0
  129. package/dist/src/components/editor/ui/media-image-node.mjs +64 -0
  130. package/dist/src/components/editor/ui/media-placeholder-node.mjs +184 -0
  131. package/dist/src/components/editor/ui/media-preview-dialog.mjs +113 -0
  132. package/dist/src/components/editor/ui/media-toolbar-button.mjs +156 -0
  133. package/dist/src/components/editor/ui/media-toolbar.mjs +80 -0
  134. package/dist/src/components/editor/ui/media-upload-toast.mjs +39 -0
  135. package/dist/src/components/editor/ui/media-video-node-static.mjs +27 -0
  136. package/dist/src/components/editor/ui/mode-toolbar-button.mjs +112 -0
  137. package/dist/src/components/editor/ui/paragraph-node-static.mjs +15 -0
  138. package/dist/src/components/editor/ui/paragraph-node.mjs +17 -0
  139. package/dist/src/components/editor/ui/resize-handle.mjs +43 -0
  140. package/dist/src/components/editor/ui/slash-node.mjs +228 -0
  141. package/dist/src/components/editor/ui/suggestion-node-static.mjs +24 -0
  142. package/dist/src/components/editor/ui/suggestion-node.mjs +111 -0
  143. package/dist/src/components/editor/ui/suggestion-toolbar-button.mjs +24 -0
  144. package/dist/src/components/editor/ui/table-icons.mjs +1310 -0
  145. package/dist/src/components/editor/ui/table-node-static.mjs +68 -0
  146. package/dist/src/components/editor/ui/table-node.mjs +375 -0
  147. package/dist/src/components/editor/ui/table-toolbar-button.mjs +190 -0
  148. package/dist/src/components/editor/ui/turn-into-toolbar-button.mjs +168 -0
  149. package/dist/src/components/editor/use-chat.mjs +80 -0
  150. package/dist/src/components/json-schema-editor/client.mjs +46 -0
  151. package/dist/src/components/json-schema-editor/components/inputs.mjs +438 -0
  152. package/dist/src/components/json-schema-editor/get-default-values.mjs +23 -0
  153. package/dist/src/components/json-schema-editor/schema.mjs +131 -0
  154. package/dist/src/components/json-schema-editor/utils/merge-schema.mjs +134 -0
  155. package/dist/src/components/json-schema-editor/utils/schema-to-string.mjs +53 -0
  156. package/dist/src/components/ui/alert-dialog.mjs +90 -0
  157. package/dist/src/components/ui/avatar.mjs +32 -0
  158. package/dist/src/components/ui/button.mjs +48 -0
  159. package/dist/src/components/ui/calendar.mjs +108 -0
  160. package/dist/src/components/ui/checkbox.mjs +23 -0
  161. package/dist/src/components/ui/combobox.mjs +53 -0
  162. package/dist/src/components/ui/command.mjs +42 -0
  163. package/dist/src/components/ui/context-menu.mjs +67 -0
  164. package/dist/src/components/ui/dialog.mjs +9 -0
  165. package/dist/src/components/ui/dropdown-menu.mjs +122 -0
  166. package/dist/src/components/ui/input-group.mjs +31 -0
  167. package/dist/src/components/ui/input.mjs +15 -0
  168. package/dist/src/components/ui/label.mjs +19 -0
  169. package/dist/src/components/ui/popover.mjs +37 -0
  170. package/dist/src/components/ui/select.mjs +80 -0
  171. package/dist/src/components/ui/separator.mjs +19 -0
  172. package/dist/src/components/ui/spinner.mjs +16 -0
  173. package/dist/src/components/ui/tabs.mjs +39 -0
  174. package/dist/src/components/ui/textarea.mjs +14 -0
  175. package/dist/src/components/ui/toolbar.mjs +183 -0
  176. package/dist/src/components/ui/tooltip.mjs +38 -0
  177. package/dist/src/hooks/editor/use-is-creator.mjs +19 -0
  178. package/dist/src/hooks/use-upload-file.mjs +73 -0
  179. package/dist/src/lib/config.mjs +38 -0
  180. package/dist/src/lib/data/store.d.mts +15 -0
  181. package/dist/src/lib/lowlight.mjs +7 -0
  182. package/dist/src/lib/utils/deep-equal.mjs +18 -0
  183. package/dist/src/lib/utils/remove-undefined.mjs +18 -0
  184. package/dist/src/lib/utils.mjs +10 -0
  185. package/dist/types.d.mts +50 -0
  186. package/next.config.ts +23 -0
  187. package/package.json +131 -0
  188. package/postcss.config.mjs +7 -0
  189. package/src/app/(dashboard)/collection/[name]/[...slug]/page.client.tsx +26 -0
  190. package/src/app/(dashboard)/collection/[name]/[...slug]/page.tsx +17 -0
  191. package/src/app/(dashboard)/collection/[name]/page.client.tsx +82 -0
  192. package/src/app/(dashboard)/collection/[name]/page.tsx +11 -0
  193. package/src/app/(dashboard)/layout.tsx +34 -0
  194. package/src/app/(dashboard)/page.tsx +38 -0
  195. package/src/app/api/ai/command/prompts.ts +313 -0
  196. package/src/app/api/ai/command/route.ts +203 -0
  197. package/src/app/api/ai/command/utils.ts +242 -0
  198. package/src/app/api/ai/copilot/route.ts +33 -0
  199. package/src/app/api/uploadthing/route.ts +5 -0
  200. package/src/app/codeblock/LICENSE +21 -0
  201. package/src/app/codeblock/styles.css +289 -0
  202. package/src/app/favicon.ico +0 -0
  203. package/src/app/globals.css +223 -0
  204. package/src/app/layout.client.tsx +15 -0
  205. package/src/app/layout.tsx +30 -0
  206. package/src/components/app-sidebar.tsx +162 -0
  207. package/src/components/code-editor/mdx.lazy.tsx +18 -0
  208. package/src/components/code-editor/mdx.tsx +48 -0
  209. package/src/components/code-editor/yaml.lazy.tsx +20 -0
  210. package/src/components/code-editor/yaml.tsx +71 -0
  211. package/src/components/collection/actions.tsx +41 -0
  212. package/src/components/collection/client.tsx +63 -0
  213. package/src/components/collection/context.tsx +24 -0
  214. package/src/components/collection/document/actions.tsx +83 -0
  215. package/src/components/editor/editor-base-kit.tsx +37 -0
  216. package/src/components/editor/editor-kit.tsx +87 -0
  217. package/src/components/editor/md.tsx +60 -0
  218. package/src/components/editor/plugins/ai-kit.tsx +102 -0
  219. package/src/components/editor/plugins/align-base-kit.tsx +16 -0
  220. package/src/components/editor/plugins/align-kit.tsx +18 -0
  221. package/src/components/editor/plugins/autoformat-kit.tsx +236 -0
  222. package/src/components/editor/plugins/basic-blocks-base-kit.tsx +34 -0
  223. package/src/components/editor/plugins/basic-blocks-kit.tsx +87 -0
  224. package/src/components/editor/plugins/basic-marks-base-kit.tsx +26 -0
  225. package/src/components/editor/plugins/basic-marks-kit.tsx +40 -0
  226. package/src/components/editor/plugins/basic-nodes-kit.tsx +6 -0
  227. package/src/components/editor/plugins/block-menu-kit.tsx +14 -0
  228. package/src/components/editor/plugins/block-placeholder-kit.tsx +17 -0
  229. package/src/components/editor/plugins/block-selection-kit.tsx +54 -0
  230. package/src/components/editor/plugins/callout-base-kit.tsx +5 -0
  231. package/src/components/editor/plugins/callout-kit.tsx +7 -0
  232. package/src/components/editor/plugins/code-block-base-kit.tsx +16 -0
  233. package/src/components/editor/plugins/code-block-kit.tsx +19 -0
  234. package/src/components/editor/plugins/column-base-kit.tsx +11 -0
  235. package/src/components/editor/plugins/column-kit.tsx +10 -0
  236. package/src/components/editor/plugins/comment-base-kit.tsx +5 -0
  237. package/src/components/editor/plugins/comment-kit.tsx +92 -0
  238. package/src/components/editor/plugins/copilot-kit.tsx +70 -0
  239. package/src/components/editor/plugins/cursor-overlay-kit.tsx +13 -0
  240. package/src/components/editor/plugins/date-base-kit.tsx +5 -0
  241. package/src/components/editor/plugins/date-kit.tsx +7 -0
  242. package/src/components/editor/plugins/discussion-kit.tsx +142 -0
  243. package/src/components/editor/plugins/dnd-kit.tsx +25 -0
  244. package/src/components/editor/plugins/emoji-kit.tsx +13 -0
  245. package/src/components/editor/plugins/exit-break-kit.tsx +12 -0
  246. package/src/components/editor/plugins/fixed-toolbar-kit.tsx +19 -0
  247. package/src/components/editor/plugins/floating-toolbar-kit.tsx +19 -0
  248. package/src/components/editor/plugins/font-base-kit.tsx +20 -0
  249. package/src/components/editor/plugins/font-kit.tsx +29 -0
  250. package/src/components/editor/plugins/indent-base-kit.tsx +13 -0
  251. package/src/components/editor/plugins/indent-kit.tsx +22 -0
  252. package/src/components/editor/plugins/line-height-base-kit.tsx +14 -0
  253. package/src/components/editor/plugins/line-height-kit.tsx +16 -0
  254. package/src/components/editor/plugins/link-base-kit.tsx +5 -0
  255. package/src/components/editor/plugins/link-kit.tsx +15 -0
  256. package/src/components/editor/plugins/list-base-kit.tsx +17 -0
  257. package/src/components/editor/plugins/list-kit.tsx +26 -0
  258. package/src/components/editor/plugins/markdown-kit.tsx +13 -0
  259. package/src/components/editor/plugins/math-base-kit.tsx +11 -0
  260. package/src/components/editor/plugins/math-kit.tsx +10 -0
  261. package/src/components/editor/plugins/media-base-kit.tsx +25 -0
  262. package/src/components/editor/plugins/media-kit.tsx +28 -0
  263. package/src/components/editor/plugins/slash-kit.tsx +18 -0
  264. package/src/components/editor/plugins/suggestion-base-kit.tsx +5 -0
  265. package/src/components/editor/plugins/suggestion-kit.tsx +83 -0
  266. package/src/components/editor/plugins/table-base-kit.tsx +20 -0
  267. package/src/components/editor/plugins/table-kit.tsx +22 -0
  268. package/src/components/editor/settings-dialog.tsx +418 -0
  269. package/src/components/editor/transforms.ts +193 -0
  270. package/src/components/editor/types.ts +149 -0
  271. package/src/components/editor/ui/ai-chat-editor.tsx +20 -0
  272. package/src/components/editor/ui/ai-menu.tsx +663 -0
  273. package/src/components/editor/ui/ai-node.tsx +40 -0
  274. package/src/components/editor/ui/ai-toolbar-button.tsx +23 -0
  275. package/src/components/editor/ui/align-toolbar-button.tsx +78 -0
  276. package/src/components/editor/ui/block-context-menu.tsx +154 -0
  277. package/src/components/editor/ui/block-discussion.tsx +318 -0
  278. package/src/components/editor/ui/block-draggable.tsx +464 -0
  279. package/src/components/editor/ui/block-list-static.tsx +75 -0
  280. package/src/components/editor/ui/block-list.tsx +72 -0
  281. package/src/components/editor/ui/block-selection.tsx +37 -0
  282. package/src/components/editor/ui/block-suggestion.tsx +438 -0
  283. package/src/components/editor/ui/blockquote-node-static.tsx +5 -0
  284. package/src/components/editor/ui/blockquote-node.tsx +7 -0
  285. package/src/components/editor/ui/callout-node-static.tsx +30 -0
  286. package/src/components/editor/ui/callout-node.tsx +64 -0
  287. package/src/components/editor/ui/caption.tsx +55 -0
  288. package/src/components/editor/ui/code-block-node-static.tsx +33 -0
  289. package/src/components/editor/ui/code-block-node.tsx +161 -0
  290. package/src/components/editor/ui/code-node-static.tsx +15 -0
  291. package/src/components/editor/ui/code-node.tsx +17 -0
  292. package/src/components/editor/ui/column-node-static.tsx +27 -0
  293. package/src/components/editor/ui/column-node.tsx +285 -0
  294. package/src/components/editor/ui/comment-node-static.tsx +12 -0
  295. package/src/components/editor/ui/comment-node.tsx +43 -0
  296. package/src/components/editor/ui/comment-toolbar-button.tsx +24 -0
  297. package/src/components/editor/ui/comment.tsx +577 -0
  298. package/src/components/editor/ui/cursor-overlay.tsx +66 -0
  299. package/src/components/editor/ui/date-node-static.tsx +45 -0
  300. package/src/components/editor/ui/date-node.tsx +86 -0
  301. package/src/components/editor/ui/editor-static.tsx +47 -0
  302. package/src/components/editor/ui/editor.tsx +120 -0
  303. package/src/components/editor/ui/emoji-node.tsx +65 -0
  304. package/src/components/editor/ui/emoji-toolbar-button.tsx +582 -0
  305. package/src/components/editor/ui/equation-node-static.tsx +94 -0
  306. package/src/components/editor/ui/equation-node.tsx +218 -0
  307. package/src/components/editor/ui/equation-toolbar-button.tsx +24 -0
  308. package/src/components/editor/ui/fixed-toolbar-buttons.tsx +135 -0
  309. package/src/components/editor/ui/fixed-toolbar.tsx +17 -0
  310. package/src/components/editor/ui/floating-toolbar-buttons.tsx +72 -0
  311. package/src/components/editor/ui/floating-toolbar.tsx +76 -0
  312. package/src/components/editor/ui/font-color-toolbar-button.tsx +804 -0
  313. package/src/components/editor/ui/font-size-toolbar-button.tsx +102 -0
  314. package/src/components/editor/ui/ghost-text.tsx +27 -0
  315. package/src/components/editor/ui/heading-node-static.tsx +54 -0
  316. package/src/components/editor/ui/heading-node.tsx +54 -0
  317. package/src/components/editor/ui/highlight-node-static.tsx +11 -0
  318. package/src/components/editor/ui/highlight-node.tsx +13 -0
  319. package/src/components/editor/ui/history-toolbar-button.tsx +41 -0
  320. package/src/components/editor/ui/hr-node-static.tsx +16 -0
  321. package/src/components/editor/ui/hr-node.tsx +28 -0
  322. package/src/components/editor/ui/indent-toolbar-button.tsx +27 -0
  323. package/src/components/editor/ui/inline-combobox.tsx +251 -0
  324. package/src/components/editor/ui/insert-toolbar-button.tsx +242 -0
  325. package/src/components/editor/ui/kbd-node-static.tsx +15 -0
  326. package/src/components/editor/ui/kbd-node.tsx +17 -0
  327. package/src/components/editor/ui/line-height-toolbar-button.tsx +66 -0
  328. package/src/components/editor/ui/link-node-static.tsx +21 -0
  329. package/src/components/editor/ui/link-node.tsx +37 -0
  330. package/src/components/editor/ui/link-toolbar-button.tsx +18 -0
  331. package/src/components/editor/ui/link-toolbar.tsx +196 -0
  332. package/src/components/editor/ui/list-toolbar-button.tsx +195 -0
  333. package/src/components/editor/ui/mark-toolbar-button.tsx +20 -0
  334. package/src/components/editor/ui/media-image-node-static.tsx +33 -0
  335. package/src/components/editor/ui/media-image-node.tsx +75 -0
  336. package/src/components/editor/ui/media-placeholder-node.tsx +235 -0
  337. package/src/components/editor/ui/media-preview-dialog.tsx +145 -0
  338. package/src/components/editor/ui/media-toolbar-button.tsx +202 -0
  339. package/src/components/editor/ui/media-toolbar.tsx +100 -0
  340. package/src/components/editor/ui/media-upload-toast.tsx +59 -0
  341. package/src/components/editor/ui/media-video-node-static.tsx +23 -0
  342. package/src/components/editor/ui/mode-toolbar-button.tsx +121 -0
  343. package/src/components/editor/ui/paragraph-node-static.tsx +13 -0
  344. package/src/components/editor/ui/paragraph-node.tsx +15 -0
  345. package/src/components/editor/ui/resize-handle.tsx +79 -0
  346. package/src/components/editor/ui/slash-node.tsx +274 -0
  347. package/src/components/editor/ui/suggestion-node-static.tsx +30 -0
  348. package/src/components/editor/ui/suggestion-node.tsx +157 -0
  349. package/src/components/editor/ui/suggestion-toolbar-button.tsx +25 -0
  350. package/src/components/editor/ui/table-icons.tsx +685 -0
  351. package/src/components/editor/ui/table-node-static.tsx +86 -0
  352. package/src/components/editor/ui/table-node.tsx +569 -0
  353. package/src/components/editor/ui/table-toolbar-button.tsx +260 -0
  354. package/src/components/editor/ui/turn-into-toolbar-button.tsx +176 -0
  355. package/src/components/editor/use-chat.ts +120 -0
  356. package/src/components/icons/logo.tsx +37 -0
  357. package/src/components/json-schema-editor/client.tsx +55 -0
  358. package/src/components/json-schema-editor/components/inputs.tsx +612 -0
  359. package/src/components/json-schema-editor/get-default-values.ts +29 -0
  360. package/src/components/json-schema-editor/schema.tsx +206 -0
  361. package/src/components/json-schema-editor/utils/merge-schema.ts +167 -0
  362. package/src/components/json-schema-editor/utils/schema-to-string.ts +87 -0
  363. package/src/components/nav-user.tsx +102 -0
  364. package/src/components/site-header.tsx +13 -0
  365. package/src/components/ui/alert-dialog.tsx +182 -0
  366. package/src/components/ui/avatar.tsx +96 -0
  367. package/src/components/ui/badge.tsx +46 -0
  368. package/src/components/ui/button.tsx +65 -0
  369. package/src/components/ui/calendar.tsx +186 -0
  370. package/src/components/ui/checkbox.tsx +29 -0
  371. package/src/components/ui/combobox.tsx +267 -0
  372. package/src/components/ui/command.tsx +180 -0
  373. package/src/components/ui/context-menu.tsx +161 -0
  374. package/src/components/ui/dialog.tsx +142 -0
  375. package/src/components/ui/drawer.tsx +120 -0
  376. package/src/components/ui/dropdown-menu.tsx +239 -0
  377. package/src/components/ui/field.tsx +224 -0
  378. package/src/components/ui/input-group.tsx +144 -0
  379. package/src/components/ui/input.tsx +19 -0
  380. package/src/components/ui/label.tsx +18 -0
  381. package/src/components/ui/popover.tsx +76 -0
  382. package/src/components/ui/select.tsx +182 -0
  383. package/src/components/ui/separator.tsx +28 -0
  384. package/src/components/ui/sheet.tsx +130 -0
  385. package/src/components/ui/sidebar.tsx +608 -0
  386. package/src/components/ui/sonner.tsx +45 -0
  387. package/src/components/ui/spinner.tsx +15 -0
  388. package/src/components/ui/table.tsx +89 -0
  389. package/src/components/ui/tabs.tsx +60 -0
  390. package/src/components/ui/textarea.tsx +18 -0
  391. package/src/components/ui/toggle-group.tsx +86 -0
  392. package/src/components/ui/toggle.tsx +45 -0
  393. package/src/components/ui/toolbar.tsx +365 -0
  394. package/src/components/ui/tooltip.tsx +57 -0
  395. package/src/hooks/editor/use-is-creator.ts +21 -0
  396. package/src/hooks/use-mobile.ts +19 -0
  397. package/src/hooks/use-mounted.ts +11 -0
  398. package/src/hooks/use-upload-file.ts +121 -0
  399. package/src/lib/ai/markdown-joiner-transform.ts +235 -0
  400. package/src/lib/config.ts +60 -0
  401. package/src/lib/data/actions.ts +51 -0
  402. package/src/lib/data/boundary.client.tsx +22 -0
  403. package/src/lib/data/boundary.tsx +22 -0
  404. package/src/lib/data/query.ts +9 -0
  405. package/src/lib/data/store.ts +52 -0
  406. package/src/lib/lowlight.ts +3 -0
  407. package/src/lib/uploadthing.ts +19 -0
  408. package/src/lib/utils/deep-equal.ts +37 -0
  409. package/src/lib/utils/remove-undefined.ts +21 -0
  410. package/src/lib/utils/urls.ts +6 -0
  411. package/src/lib/utils.ts +6 -0
  412. package/tsconfig.build.json +24 -0
  413. package/tsconfig.json +37 -0
@@ -0,0 +1,57 @@
1
+ "use client";
2
+
3
+ import type * as React from "react";
4
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function TooltipProvider({
9
+ delayDuration = 0,
10
+ ...props
11
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
12
+ return (
13
+ <TooltipPrimitive.Provider
14
+ data-slot="tooltip-provider"
15
+ delayDuration={delayDuration}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22
+ return (
23
+ <TooltipProvider>
24
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
+ </TooltipProvider>
26
+ );
27
+ }
28
+
29
+ function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
30
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
31
+ }
32
+
33
+ function TooltipContent({
34
+ className,
35
+ sideOffset = 0,
36
+ children,
37
+ ...props
38
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
39
+ return (
40
+ <TooltipPrimitive.Portal>
41
+ <TooltipPrimitive.Content
42
+ data-slot="tooltip-content"
43
+ sideOffset={sideOffset}
44
+ className={cn(
45
+ "data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:fade-out-0 data-closed:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-2xl bg-foreground px-3 py-1.5 text-background text-xs data-[state=delayed-open]:animate-in data-closed:animate-out data-open:animate-in **:data-[slot=kbd]:rounded-4xl",
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
52
+ </TooltipPrimitive.Content>
53
+ </TooltipPrimitive.Portal>
54
+ );
55
+ }
56
+
57
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
@@ -0,0 +1,21 @@
1
+ import type { TElement } from "platejs";
2
+ import { useMemo } from "react";
3
+ import { useEditor } from "@/components/editor/editor-kit";
4
+
5
+ /**
6
+ * Check if current user is the creator of this element (for Yjs collaboration)
7
+ */
8
+ export function useIsCreator(element: TElement) {
9
+ const editor = useEditor();
10
+
11
+ return useMemo(() => {
12
+ const elementUserId =
13
+ "userId" in element && typeof element.userId === "string" ? element.userId : undefined;
14
+ const currentUserId = editor.meta.userId;
15
+
16
+ // If no userId (backwards compatibility or non-Yjs), allow
17
+ if (!elementUserId) return true;
18
+
19
+ return elementUserId === currentUserId;
20
+ }, [editor.meta.userId, element]);
21
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+
3
+ const MOBILE_BREAKPOINT = 768;
4
+
5
+ export function useIsMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
+ };
13
+ mql.addEventListener("change", onChange);
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
15
+ return () => mql.removeEventListener("change", onChange);
16
+ }, []);
17
+
18
+ return !!isMobile;
19
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+
3
+ export function useMounted() {
4
+ const [mounted, setMounted] = React.useState(false);
5
+
6
+ React.useEffect(() => {
7
+ setMounted(true);
8
+ }, []);
9
+
10
+ return mounted;
11
+ }
@@ -0,0 +1,121 @@
1
+ import * as React from "react";
2
+
3
+ import type { OurFileRouter } from "@/lib/uploadthing";
4
+ import type { ClientUploadedFileData, UploadFilesOptions } from "uploadthing/types";
5
+
6
+ import { generateReactHelpers } from "@uploadthing/react";
7
+ import { toast } from "sonner";
8
+ import { z } from "zod";
9
+
10
+ export type UploadedFile<T = unknown> = ClientUploadedFileData<T>;
11
+
12
+ interface UseUploadFileProps extends Pick<
13
+ UploadFilesOptions<OurFileRouter["editorUploader"]>,
14
+ "headers" | "onUploadBegin" | "onUploadProgress" | "skipPolling"
15
+ > {
16
+ onUploadComplete?: (file: UploadedFile) => void;
17
+ onUploadError?: (error: unknown) => void;
18
+ }
19
+
20
+ export function useUploadFile({
21
+ onUploadComplete,
22
+ onUploadError,
23
+ ...props
24
+ }: UseUploadFileProps = {}) {
25
+ const [uploadedFile, setUploadedFile] = React.useState<UploadedFile>();
26
+ const [uploadingFile, setUploadingFile] = React.useState<File>();
27
+ const [progress, setProgress] = React.useState<number>(0);
28
+ const [isUploading, setIsUploading] = React.useState(false);
29
+
30
+ async function uploadThing(file: File) {
31
+ setIsUploading(true);
32
+ setUploadingFile(file);
33
+
34
+ try {
35
+ const res = await uploadFiles("editorUploader", {
36
+ ...props,
37
+ files: [file],
38
+ onUploadProgress: ({ progress }) => {
39
+ setProgress(Math.min(progress, 100));
40
+ },
41
+ });
42
+
43
+ setUploadedFile(res[0]);
44
+
45
+ onUploadComplete?.(res[0]);
46
+
47
+ return uploadedFile;
48
+ } catch (error) {
49
+ const errorMessage = getErrorMessage(error);
50
+
51
+ const message =
52
+ errorMessage.length > 0 ? errorMessage : "Something went wrong, please try again later.";
53
+
54
+ toast.error(message);
55
+
56
+ onUploadError?.(error);
57
+
58
+ // Mock upload for unauthenticated users
59
+ // toast.info('User not logged in. Mocking upload process.');
60
+ const mockUploadedFile = {
61
+ key: "mock-key-0",
62
+ appUrl: `https://mock-app-url.com/${file.name}`,
63
+ name: file.name,
64
+ size: file.size,
65
+ type: file.type,
66
+ url: URL.createObjectURL(file),
67
+ } as UploadedFile;
68
+
69
+ // Simulate upload progress
70
+ let progress = 0;
71
+
72
+ const simulateProgress = async () => {
73
+ while (progress < 100) {
74
+ await new Promise((resolve) => setTimeout(resolve, 50));
75
+ progress += 2;
76
+ setProgress(Math.min(progress, 100));
77
+ }
78
+ };
79
+
80
+ await simulateProgress();
81
+
82
+ setUploadedFile(mockUploadedFile);
83
+
84
+ return mockUploadedFile;
85
+ } finally {
86
+ setProgress(0);
87
+ setIsUploading(false);
88
+ setUploadingFile(undefined);
89
+ }
90
+ }
91
+
92
+ return {
93
+ isUploading,
94
+ progress,
95
+ uploadedFile,
96
+ uploadFile: uploadThing,
97
+ uploadingFile,
98
+ };
99
+ }
100
+
101
+ export const { uploadFiles, useUploadThing } = generateReactHelpers<OurFileRouter>();
102
+
103
+ export function getErrorMessage(err: unknown) {
104
+ const unknownError = "Something went wrong, please try again later.";
105
+
106
+ if (err instanceof z.ZodError) {
107
+ const errors = err.issues.map((issue) => issue.message);
108
+
109
+ return errors.join("\n");
110
+ }
111
+ if (err instanceof Error) {
112
+ return err.message;
113
+ }
114
+ return unknownError;
115
+ }
116
+
117
+ export function showErrorToast(err: unknown) {
118
+ const errorMessage = getErrorMessage(err);
119
+
120
+ return toast.error(errorMessage);
121
+ }
@@ -0,0 +1,235 @@
1
+ import type { TextStreamPart, ToolSet } from "ai";
2
+
3
+ /**
4
+ * Transform chunks like [**,bold,**] to [**bold**] make the md deserializer
5
+ * happy.
6
+ *
7
+ * @experimental
8
+ */
9
+ export const markdownJoinerTransform =
10
+ <TOOLS extends ToolSet>() =>
11
+ () => {
12
+ const joiner = new MarkdownJoiner();
13
+ let lastTextDeltaId: string | undefined;
14
+ let textStreamEnded = false;
15
+
16
+ return new TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>({
17
+ async flush(controller) {
18
+ // Only flush if we haven't seen text-end yet
19
+ if (!textStreamEnded) {
20
+ const remaining = joiner.flush();
21
+ if (remaining && lastTextDeltaId) {
22
+ controller.enqueue({
23
+ id: lastTextDeltaId,
24
+ text: remaining,
25
+ type: "text-delta",
26
+ } as TextStreamPart<TOOLS>);
27
+ }
28
+ }
29
+ },
30
+ async transform(chunk, controller) {
31
+ if (chunk.type === "text-delta") {
32
+ lastTextDeltaId = chunk.id;
33
+ const processedText = joiner.processText(chunk.text);
34
+ if (processedText) {
35
+ controller.enqueue({
36
+ ...chunk,
37
+ text: processedText,
38
+ });
39
+ await delay(joiner.delayInMs);
40
+ }
41
+ } else if (chunk.type === "text-end") {
42
+ // Flush any remaining buffer before text-end
43
+ const remaining = joiner.flush();
44
+ if (remaining && lastTextDeltaId) {
45
+ controller.enqueue({
46
+ id: lastTextDeltaId,
47
+ text: remaining,
48
+ type: "text-delta",
49
+ } as TextStreamPart<TOOLS>);
50
+ }
51
+ textStreamEnded = true;
52
+ controller.enqueue(chunk);
53
+ } else {
54
+ controller.enqueue(chunk);
55
+ }
56
+ },
57
+ });
58
+ };
59
+
60
+ const DEFAULT_DELAY_IN_MS = 10;
61
+ const NEST_BLOCK_DELAY_IN_MS = 100;
62
+
63
+ const BOLD_PATTERN = /\*\*.*?\*\*/;
64
+ const CODE_LINE_PATTERN = /```[^\s]+/;
65
+ const LINK_PATTERN = /^\[.*?\]\(.*?\)$/;
66
+ const UNORDERED_LIST_PATTERN = /^[*-]\s+.+/;
67
+ const TODO_LIST_PATTERN = /^[*-]\s+\[[ xX]\]\s+.+/;
68
+ const ORDERED_LIST_PATTERN = /^\d+\.\s+.+/;
69
+ const MDX_TAG_PATTERN = /<([A-Za-z][A-Za-z0-9\-_]*)>/;
70
+ const DIGIT_PATTERN = /^[0-9]$/;
71
+
72
+ export class MarkdownJoiner {
73
+ delayInMs = DEFAULT_DELAY_IN_MS;
74
+
75
+ private buffer = "";
76
+ private documentCharacterCount = 0;
77
+ private isBuffering = false;
78
+ private streamingCodeBlock = false;
79
+ private streamingLargeDocument = false;
80
+ private streamingTable = false;
81
+
82
+ private clearBuffer(): void {
83
+ this.buffer = "";
84
+ this.isBuffering = false;
85
+ }
86
+ private isCompleteBold(): boolean {
87
+ return BOLD_PATTERN.test(this.buffer);
88
+ }
89
+
90
+ private isCompleteCodeBlockEnd(): boolean {
91
+ return this.buffer.trimEnd() === "```";
92
+ }
93
+
94
+ private isCompleteCodeBlockStart(): boolean {
95
+ return CODE_LINE_PATTERN.test(this.buffer);
96
+ }
97
+
98
+ private isCompleteLink(): boolean {
99
+ return LINK_PATTERN.test(this.buffer);
100
+ }
101
+
102
+ private isCompleteList(): boolean {
103
+ if (UNORDERED_LIST_PATTERN.test(this.buffer) && this.buffer.includes("["))
104
+ return TODO_LIST_PATTERN.test(this.buffer);
105
+
106
+ return (
107
+ UNORDERED_LIST_PATTERN.test(this.buffer) ||
108
+ ORDERED_LIST_PATTERN.test(this.buffer) ||
109
+ TODO_LIST_PATTERN.test(this.buffer)
110
+ );
111
+ }
112
+
113
+ private isCompleteMdxTag(): boolean {
114
+ return MDX_TAG_PATTERN.test(this.buffer);
115
+ }
116
+
117
+ private isCompleteTableStart(): boolean {
118
+ return this.buffer.startsWith("|") && this.buffer.endsWith("|");
119
+ }
120
+
121
+ private isFalsePositive(char: string): boolean {
122
+ // when link is not complete, even if ths buffer is more than 30 characters, it is not a false positive
123
+ if (this.buffer.startsWith("[") && this.buffer.includes("http")) {
124
+ return false;
125
+ }
126
+
127
+ return char === "\n" || this.buffer.length > 30;
128
+ }
129
+
130
+ private isLargeDocumentStart(): boolean {
131
+ return this.documentCharacterCount > 2500;
132
+ }
133
+
134
+ private isListStartChar(char: string): boolean {
135
+ return char === "-" || char === "*" || DIGIT_PATTERN.test(char);
136
+ }
137
+
138
+ private isTableExisted(): boolean {
139
+ return this.buffer.length > 10 && !this.buffer.includes("|");
140
+ }
141
+
142
+ flush(): string {
143
+ const remaining = this.buffer;
144
+ this.clearBuffer();
145
+ return remaining;
146
+ }
147
+
148
+ processText(text: string): string {
149
+ let output = "";
150
+
151
+ for (const char of text) {
152
+ if (this.streamingCodeBlock || this.streamingTable || this.streamingLargeDocument) {
153
+ this.buffer += char;
154
+
155
+ if (char === "\n") {
156
+ output += this.buffer;
157
+ this.clearBuffer();
158
+ }
159
+
160
+ if (this.isCompleteCodeBlockEnd() && this.streamingCodeBlock) {
161
+ this.streamingCodeBlock = false;
162
+ this.delayInMs = DEFAULT_DELAY_IN_MS;
163
+
164
+ output += this.buffer;
165
+ this.clearBuffer();
166
+ }
167
+
168
+ if (this.isTableExisted() && this.streamingTable) {
169
+ this.streamingTable = false;
170
+ this.delayInMs = DEFAULT_DELAY_IN_MS;
171
+
172
+ output += this.buffer;
173
+ this.clearBuffer();
174
+ }
175
+ } else if (this.isBuffering) {
176
+ this.buffer += char;
177
+
178
+ if (this.isCompleteCodeBlockStart()) {
179
+ this.delayInMs = NEST_BLOCK_DELAY_IN_MS;
180
+ this.streamingCodeBlock = true;
181
+ continue;
182
+ }
183
+
184
+ if (this.isCompleteTableStart()) {
185
+ this.delayInMs = NEST_BLOCK_DELAY_IN_MS;
186
+ this.streamingTable = true;
187
+ continue;
188
+ }
189
+
190
+ if (this.isLargeDocumentStart()) {
191
+ this.delayInMs = NEST_BLOCK_DELAY_IN_MS;
192
+ this.streamingLargeDocument = true;
193
+ continue;
194
+ }
195
+
196
+ if (
197
+ this.isCompleteBold() ||
198
+ this.isCompleteMdxTag() ||
199
+ this.isCompleteList() ||
200
+ this.isCompleteLink()
201
+ ) {
202
+ output += this.buffer;
203
+ this.clearBuffer();
204
+ } else if (this.isFalsePositive(char)) {
205
+ // False positive - flush buffer as raw text
206
+ output += this.buffer;
207
+ this.clearBuffer();
208
+ }
209
+ // Check if we should start buffering
210
+ } else if (
211
+ char === "*" ||
212
+ char === "<" ||
213
+ char === "`" ||
214
+ char === "|" ||
215
+ char === "[" ||
216
+ this.isListStartChar(char)
217
+ ) {
218
+ this.buffer = char;
219
+ this.isBuffering = true;
220
+ } else {
221
+ // Pass through character directly
222
+ output += char;
223
+ }
224
+ }
225
+
226
+ this.documentCharacterCount += text.length;
227
+ return output;
228
+ }
229
+ }
230
+
231
+ async function delay(delayInMs?: number | null): Promise<void> {
232
+ return delayInMs == null
233
+ ? Promise.resolve()
234
+ : new Promise((resolve) => setTimeout(resolve, delayInMs));
235
+ }
@@ -0,0 +1,60 @@
1
+ import type { StudioDocument } from "lib/types";
2
+ import { type CollectionWithHandler, Core } from "fuma-content";
3
+ import { Collection, CollectionHandlers } from "fuma-content/collections";
4
+
5
+ let core: Promise<Core>;
6
+
7
+ export async function getCore(): Promise<Core> {
8
+ core ??= (async () => {
9
+ const core = new Core({
10
+ outDir: ".content",
11
+ configPath: process.env.STUDIO_CONFIG,
12
+ cwd: process.env.STUDIO_PARENT_DIR,
13
+ });
14
+
15
+ await core.init({
16
+ config: await import("virtual:content.config"),
17
+ });
18
+
19
+ return core;
20
+ })();
21
+
22
+ return core;
23
+ }
24
+
25
+ export async function requireCollection<Handler extends keyof CollectionHandlers = never>(
26
+ collectionId: string,
27
+ handlers: Handler[],
28
+ ): Promise<CollectionWithHandler<Handler>> {
29
+ const core = await getCore();
30
+ const collection = core.getCollection(collectionId);
31
+ if (!collection) throw new Error(`Missing Collection ${collectionId}`);
32
+
33
+ if (hasHandler(collection, handlers)) {
34
+ return collection;
35
+ }
36
+
37
+ throw new Error(`Missing ${handlers.join(", ")} handlers for ${collectionId}`);
38
+ }
39
+
40
+ export async function requireDocument<Doc extends StudioDocument = StudioDocument>(
41
+ collectionId: string,
42
+ documentId: string,
43
+ ): Promise<{ collection: CollectionWithHandler<"studio">; document: Doc }> {
44
+ const collection = await requireCollection(collectionId, ["studio"]);
45
+ const document: Doc | undefined = await collection.handlers.studio.getDocument(documentId);
46
+ if (!document) throw new Error(`Missing Document ${documentId}`);
47
+
48
+ return { collection: collection as CollectionWithHandler<"studio">, document };
49
+ }
50
+
51
+ export function hasHandler<Handler extends keyof CollectionHandlers = never>(
52
+ collection: Collection,
53
+ handlers: Handler[],
54
+ ): collection is CollectionWithHandler<Handler> {
55
+ for (const handler of handlers) {
56
+ if (collection.handlers[handler] === undefined) return false;
57
+ }
58
+
59
+ return true;
60
+ }
@@ -0,0 +1,51 @@
1
+ "use server";
2
+
3
+ import { getCore, requireDocument } from "../config";
4
+ import type { CollectionItem, DocumentItem } from "./store";
5
+
6
+ export async function getCollectionItems(): Promise<CollectionItem[]> {
7
+ const core = await getCore();
8
+ return core.getCollections(true).map<CollectionItem>((collection) => ({
9
+ id: collection.name,
10
+ name: collection.name,
11
+ badge: collection.typeInfo.id,
12
+ handlers: Object.fromEntries(Object.keys(collection.handlers).map((k) => [k, null])),
13
+ }));
14
+ }
15
+
16
+ export async function getDocumentItems(): Promise<DocumentItem[]> {
17
+ const core = await getCore();
18
+ const items = await Promise.all(
19
+ core.getCollections(true).map(async (collection) => {
20
+ const handler = collection.handlers.studio;
21
+
22
+ if (handler) {
23
+ const docs = await handler.getDocuments();
24
+ const supportDelete = handler.actions?.deleteDocument !== undefined;
25
+
26
+ return docs.map<DocumentItem>((doc) => ({
27
+ name: doc.name,
28
+ id: doc.id,
29
+ collectionId: collection.name,
30
+ permissions: {
31
+ delete: supportDelete,
32
+ },
33
+ }));
34
+ }
35
+
36
+ return [];
37
+ }),
38
+ );
39
+
40
+ return items.flat();
41
+ }
42
+
43
+ export async function deleteDocumentAction(documentId: string, collectionId: string) {
44
+ const { collection, document } = await requireDocument(collectionId, documentId);
45
+ const { deleteDocument } = collection.handlers.studio.actions ?? {};
46
+ if (deleteDocument) {
47
+ await deleteDocument({ collection, document });
48
+ } else {
49
+ throw new Error(`collection ${collectionId} does not support the delete operation.`);
50
+ }
51
+ }
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { Spinner } from "@/components/ui/spinner";
3
+ import { useMounted } from "@/hooks/use-mounted";
4
+ import { Suspense, type ReactNode } from "react";
5
+
6
+ export function BoundaryClient({ children }: { children: ReactNode }) {
7
+ const mounted = useMounted();
8
+ const fallback = (
9
+ <div className="fixed flex items-center justify-center inset-0 bg-background z-50 text-sm text-muted-foreground gap-1">
10
+ <Spinner />
11
+ Loading
12
+ </div>
13
+ );
14
+
15
+ if (!mounted) return fallback;
16
+
17
+ return (
18
+ <Suspense fallback={fallback} defer>
19
+ {children}
20
+ </Suspense>
21
+ );
22
+ }
@@ -0,0 +1,22 @@
1
+ import { ReactNode } from "react";
2
+ import { getCollectionItems, getDocumentItems } from "./actions";
3
+ import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
4
+ import { queryClient } from "./query";
5
+ import { BoundaryClient } from "./boundary.client";
6
+
7
+ export async function DataBoundary({ children }: { children: ReactNode }) {
8
+ await queryClient.prefetchQuery({
9
+ queryKey: ["collections"],
10
+ queryFn: () => getCollectionItems(),
11
+ });
12
+ await queryClient.prefetchQuery({
13
+ queryKey: ["documents"],
14
+ queryFn: () => getDocumentItems(),
15
+ });
16
+
17
+ return (
18
+ <BoundaryClient>
19
+ <HydrationBoundary state={dehydrate(queryClient)}>{children}</HydrationBoundary>
20
+ </BoundaryClient>
21
+ );
22
+ }
@@ -0,0 +1,9 @@
1
+ import { QueryClient } from "@tanstack/react-query";
2
+
3
+ export const queryClient = new QueryClient({
4
+ defaultOptions: {
5
+ queries: {
6
+ staleTime: 5 * 1000,
7
+ },
8
+ },
9
+ });
@@ -0,0 +1,52 @@
1
+ import { createCollection } from "@tanstack/react-db";
2
+ import { queryCollectionOptions } from "@tanstack/query-db-collection";
3
+ import { queryClient } from "./query";
4
+ import { deleteDocumentAction, getCollectionItems, getDocumentItems } from "./actions";
5
+
6
+ export interface CollectionItem {
7
+ id: string;
8
+ name: string;
9
+ badge?: string;
10
+ handlers?: Record<string, null>;
11
+ }
12
+
13
+ export interface DocumentItem {
14
+ id: string;
15
+ collectionId: string;
16
+ name: string;
17
+
18
+ permissions: {
19
+ delete: boolean;
20
+ };
21
+ }
22
+
23
+ export const collection = createCollection(
24
+ queryCollectionOptions({
25
+ queryClient,
26
+ queryKey: ["collections"],
27
+ queryFn: () => getCollectionItems(),
28
+ getKey: (item) => item.id,
29
+ }),
30
+ );
31
+
32
+ export const documentCollection = createCollection(
33
+ queryCollectionOptions({
34
+ queryClient,
35
+ queryKey: ["documents"],
36
+ queryFn: () => getDocumentItems(),
37
+ getKey: (item) => `${item.collectionId}-${item.id}`,
38
+ onDelete: async ({ transaction }) => {
39
+ const mutation = transaction.mutations[0];
40
+ const original = mutation.original;
41
+ // ignore if not supported
42
+ if (!original.permissions.delete) return;
43
+
44
+ try {
45
+ await deleteDocumentAction(original.id, original.collectionId);
46
+ } catch (error) {
47
+ console.error("Delete failed, rolling back:", error);
48
+ throw error;
49
+ }
50
+ },
51
+ }),
52
+ );
@@ -0,0 +1,3 @@
1
+ import { all, createLowlight } from "lowlight";
2
+
3
+ export const lowlight = createLowlight(all);