@treenity/mods 3.0.1

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 (486) hide show
  1. package/board/board.test.ts +212 -0
  2. package/board/client.ts +1 -0
  3. package/board/seed.ts +26 -0
  4. package/board/server.ts +5 -0
  5. package/board/types.ts +87 -0
  6. package/board/view.tsx +574 -0
  7. package/brahman/CLAUDE.md +18 -0
  8. package/brahman/brahman.test.ts +855 -0
  9. package/brahman/client.ts +2 -0
  10. package/brahman/helpers.ts +374 -0
  11. package/brahman/server.ts +2 -0
  12. package/brahman/service.ts +328 -0
  13. package/brahman/types.ts +727 -0
  14. package/brahman/view.tsx +73 -0
  15. package/brahman/views/action-cards.tsx +615 -0
  16. package/brahman/views/bot-view.tsx +76 -0
  17. package/brahman/views/chat-editor.tsx +782 -0
  18. package/brahman/views/chat-preview.tsx +266 -0
  19. package/brahman/views/menu-editor.tsx +451 -0
  20. package/brahman/views/page-layout.tsx +285 -0
  21. package/brahman/views/tstring-input.tsx +84 -0
  22. package/cafe/CLAUDE.md +7 -0
  23. package/cafe/seed.ts +8 -0
  24. package/cafe/server.ts +2 -0
  25. package/cafe/types.ts +32 -0
  26. package/canary/seed.ts +8 -0
  27. package/canary/server.ts +3 -0
  28. package/canary/service.ts +101 -0
  29. package/canary/types.ts +16 -0
  30. package/dist/board/client.d.ts +2 -0
  31. package/dist/board/client.d.ts.map +1 -0
  32. package/dist/board/client.js +2 -0
  33. package/dist/board/client.js.map +1 -0
  34. package/dist/board/seed.d.ts +2 -0
  35. package/dist/board/seed.d.ts.map +1 -0
  36. package/dist/board/seed.js +23 -0
  37. package/dist/board/seed.js.map +1 -0
  38. package/dist/board/server.d.ts +3 -0
  39. package/dist/board/server.d.ts.map +1 -0
  40. package/dist/board/server.js +5 -0
  41. package/dist/board/server.js.map +1 -0
  42. package/dist/board/types.d.ts +45 -0
  43. package/dist/board/types.d.ts.map +1 -0
  44. package/dist/board/types.js +82 -0
  45. package/dist/board/types.js.map +1 -0
  46. package/dist/board/view.d.ts +2 -0
  47. package/dist/board/view.d.ts.map +1 -0
  48. package/dist/board/view.js +254 -0
  49. package/dist/board/view.js.map +1 -0
  50. package/dist/brahman/client.d.ts +3 -0
  51. package/dist/brahman/client.d.ts.map +1 -0
  52. package/dist/brahman/client.js +3 -0
  53. package/dist/brahman/client.js.map +1 -0
  54. package/dist/brahman/helpers.d.ts +51 -0
  55. package/dist/brahman/helpers.d.ts.map +1 -0
  56. package/dist/brahman/helpers.js +321 -0
  57. package/dist/brahman/helpers.js.map +1 -0
  58. package/dist/brahman/server.d.ts +3 -0
  59. package/dist/brahman/server.d.ts.map +1 -0
  60. package/dist/brahman/server.js +3 -0
  61. package/dist/brahman/server.js.map +1 -0
  62. package/dist/brahman/service.d.ts +2 -0
  63. package/dist/brahman/service.d.ts.map +1 -0
  64. package/dist/brahman/service.js +310 -0
  65. package/dist/brahman/service.js.map +1 -0
  66. package/dist/brahman/types.d.ts +335 -0
  67. package/dist/brahman/types.d.ts.map +1 -0
  68. package/dist/brahman/types.js +633 -0
  69. package/dist/brahman/types.js.map +1 -0
  70. package/dist/brahman/view.d.ts +2 -0
  71. package/dist/brahman/view.d.ts.map +1 -0
  72. package/dist/brahman/view.js +47 -0
  73. package/dist/brahman/view.js.map +1 -0
  74. package/dist/brahman/views/action-cards.d.ts +60 -0
  75. package/dist/brahman/views/action-cards.d.ts.map +1 -0
  76. package/dist/brahman/views/action-cards.js +283 -0
  77. package/dist/brahman/views/action-cards.js.map +1 -0
  78. package/dist/brahman/views/bot-view.d.ts +5 -0
  79. package/dist/brahman/views/bot-view.d.ts.map +1 -0
  80. package/dist/brahman/views/bot-view.js +14 -0
  81. package/dist/brahman/views/bot-view.js.map +1 -0
  82. package/dist/brahman/views/chat-editor.d.ts +5 -0
  83. package/dist/brahman/views/chat-editor.d.ts.map +1 -0
  84. package/dist/brahman/views/chat-editor.js +306 -0
  85. package/dist/brahman/views/chat-editor.js.map +1 -0
  86. package/dist/brahman/views/chat-preview.d.ts +5 -0
  87. package/dist/brahman/views/chat-preview.d.ts.map +1 -0
  88. package/dist/brahman/views/chat-preview.js +145 -0
  89. package/dist/brahman/views/chat-preview.js.map +1 -0
  90. package/dist/brahman/views/menu-editor.d.ts +11 -0
  91. package/dist/brahman/views/menu-editor.d.ts.map +1 -0
  92. package/dist/brahman/views/menu-editor.js +171 -0
  93. package/dist/brahman/views/menu-editor.js.map +1 -0
  94. package/dist/brahman/views/page-layout.d.ts +5 -0
  95. package/dist/brahman/views/page-layout.d.ts.map +1 -0
  96. package/dist/brahman/views/page-layout.js +114 -0
  97. package/dist/brahman/views/page-layout.js.map +1 -0
  98. package/dist/brahman/views/tstring-input.d.ts +15 -0
  99. package/dist/brahman/views/tstring-input.d.ts.map +1 -0
  100. package/dist/brahman/views/tstring-input.js +23 -0
  101. package/dist/brahman/views/tstring-input.js.map +1 -0
  102. package/dist/cafe/seed.d.ts +2 -0
  103. package/dist/cafe/seed.d.ts.map +1 -0
  104. package/dist/cafe/seed.js +7 -0
  105. package/dist/cafe/seed.js.map +1 -0
  106. package/dist/cafe/server.d.ts +3 -0
  107. package/dist/cafe/server.d.ts.map +1 -0
  108. package/dist/cafe/server.js +3 -0
  109. package/dist/cafe/server.js.map +1 -0
  110. package/dist/cafe/types.d.ts +2 -0
  111. package/dist/cafe/types.d.ts.map +1 -0
  112. package/dist/cafe/types.js +31 -0
  113. package/dist/cafe/types.js.map +1 -0
  114. package/dist/canary/seed.d.ts +2 -0
  115. package/dist/canary/seed.d.ts.map +1 -0
  116. package/dist/canary/seed.js +7 -0
  117. package/dist/canary/seed.js.map +1 -0
  118. package/dist/canary/server.d.ts +4 -0
  119. package/dist/canary/server.d.ts.map +1 -0
  120. package/dist/canary/server.js +4 -0
  121. package/dist/canary/server.js.map +1 -0
  122. package/dist/canary/service.d.ts +2 -0
  123. package/dist/canary/service.d.ts.map +1 -0
  124. package/dist/canary/service.js +101 -0
  125. package/dist/canary/service.js.map +1 -0
  126. package/dist/canary/types.d.ts +9 -0
  127. package/dist/canary/types.d.ts.map +1 -0
  128. package/dist/canary/types.js +13 -0
  129. package/dist/canary/types.js.map +1 -0
  130. package/dist/doc/client.d.ts +3 -0
  131. package/dist/doc/client.d.ts.map +1 -0
  132. package/dist/doc/client.js +5 -0
  133. package/dist/doc/client.js.map +1 -0
  134. package/dist/doc/fs-codec.d.ts +2 -0
  135. package/dist/doc/fs-codec.d.ts.map +1 -0
  136. package/dist/doc/fs-codec.js +44 -0
  137. package/dist/doc/fs-codec.js.map +1 -0
  138. package/dist/doc/markdown.d.ts +13 -0
  139. package/dist/doc/markdown.d.ts.map +1 -0
  140. package/dist/doc/markdown.js +250 -0
  141. package/dist/doc/markdown.js.map +1 -0
  142. package/dist/doc/prefab.d.ts +2 -0
  143. package/dist/doc/prefab.d.ts.map +1 -0
  144. package/dist/doc/prefab.js +23 -0
  145. package/dist/doc/prefab.js.map +1 -0
  146. package/dist/doc/renderers.d.ts +2 -0
  147. package/dist/doc/renderers.d.ts.map +1 -0
  148. package/dist/doc/renderers.js +94 -0
  149. package/dist/doc/renderers.js.map +1 -0
  150. package/dist/doc/seed.d.ts +2 -0
  151. package/dist/doc/seed.d.ts.map +1 -0
  152. package/dist/doc/seed.js +9 -0
  153. package/dist/doc/seed.js.map +1 -0
  154. package/dist/doc/server.d.ts +6 -0
  155. package/dist/doc/server.d.ts.map +1 -0
  156. package/dist/doc/server.js +6 -0
  157. package/dist/doc/server.js.map +1 -0
  158. package/dist/doc/slash-command.d.ts +3 -0
  159. package/dist/doc/slash-command.d.ts.map +1 -0
  160. package/dist/doc/slash-command.js +123 -0
  161. package/dist/doc/slash-command.js.map +1 -0
  162. package/dist/doc/slash-menu.d.ts +15 -0
  163. package/dist/doc/slash-menu.d.ts.map +1 -0
  164. package/dist/doc/slash-menu.js +54 -0
  165. package/dist/doc/slash-menu.js.map +1 -0
  166. package/dist/doc/text.d.ts +2 -0
  167. package/dist/doc/text.d.ts.map +1 -0
  168. package/dist/doc/text.js +22 -0
  169. package/dist/doc/text.js.map +1 -0
  170. package/dist/doc/toolbar.d.ts +5 -0
  171. package/dist/doc/toolbar.d.ts.map +1 -0
  172. package/dist/doc/toolbar.js +15 -0
  173. package/dist/doc/toolbar.js.map +1 -0
  174. package/dist/doc/treenity-block-view.d.ts +2 -0
  175. package/dist/doc/treenity-block-view.d.ts.map +1 -0
  176. package/dist/doc/treenity-block-view.js +37 -0
  177. package/dist/doc/treenity-block-view.js.map +1 -0
  178. package/dist/doc/treenity-block.d.ts +3 -0
  179. package/dist/doc/treenity-block.d.ts.map +1 -0
  180. package/dist/doc/treenity-block.js +27 -0
  181. package/dist/doc/treenity-block.js.map +1 -0
  182. package/dist/doc/types.d.ts +2 -0
  183. package/dist/doc/types.d.ts.map +1 -0
  184. package/dist/doc/types.js +10 -0
  185. package/dist/doc/types.js.map +1 -0
  186. package/dist/launcher/client.d.ts +5 -0
  187. package/dist/launcher/client.d.ts.map +1 -0
  188. package/dist/launcher/client.js +5 -0
  189. package/dist/launcher/client.js.map +1 -0
  190. package/dist/launcher/icons.d.ts +2 -0
  191. package/dist/launcher/icons.d.ts.map +1 -0
  192. package/dist/launcher/icons.js +57 -0
  193. package/dist/launcher/icons.js.map +1 -0
  194. package/dist/launcher/seed.d.ts +2 -0
  195. package/dist/launcher/seed.d.ts.map +1 -0
  196. package/dist/launcher/seed.js +36 -0
  197. package/dist/launcher/seed.js.map +1 -0
  198. package/dist/launcher/server.d.ts +3 -0
  199. package/dist/launcher/server.d.ts.map +1 -0
  200. package/dist/launcher/server.js +3 -0
  201. package/dist/launcher/server.js.map +1 -0
  202. package/dist/launcher/types.d.ts +21 -0
  203. package/dist/launcher/types.d.ts.map +1 -0
  204. package/dist/launcher/types.js +47 -0
  205. package/dist/launcher/types.js.map +1 -0
  206. package/dist/launcher/view.d.ts +2 -0
  207. package/dist/launcher/view.d.ts.map +1 -0
  208. package/dist/launcher/view.js +187 -0
  209. package/dist/launcher/view.js.map +1 -0
  210. package/dist/launcher/widgets.d.ts +2 -0
  211. package/dist/launcher/widgets.d.ts.map +1 -0
  212. package/dist/launcher/widgets.js +73 -0
  213. package/dist/launcher/widgets.js.map +1 -0
  214. package/dist/mindmap/branch.d.ts +17 -0
  215. package/dist/mindmap/branch.d.ts.map +1 -0
  216. package/dist/mindmap/branch.js +47 -0
  217. package/dist/mindmap/branch.js.map +1 -0
  218. package/dist/mindmap/client.d.ts +3 -0
  219. package/dist/mindmap/client.d.ts.map +1 -0
  220. package/dist/mindmap/client.js +3 -0
  221. package/dist/mindmap/client.js.map +1 -0
  222. package/dist/mindmap/radial-tree.d.ts +14 -0
  223. package/dist/mindmap/radial-tree.d.ts.map +1 -0
  224. package/dist/mindmap/radial-tree.js +184 -0
  225. package/dist/mindmap/radial-tree.js.map +1 -0
  226. package/dist/mindmap/sidebar.d.ts +8 -0
  227. package/dist/mindmap/sidebar.d.ts.map +1 -0
  228. package/dist/mindmap/sidebar.js +24 -0
  229. package/dist/mindmap/sidebar.js.map +1 -0
  230. package/dist/mindmap/types.d.ts +8 -0
  231. package/dist/mindmap/types.d.ts.map +1 -0
  232. package/dist/mindmap/types.js +10 -0
  233. package/dist/mindmap/types.js.map +1 -0
  234. package/dist/mindmap/use-tree-data.d.ts +14 -0
  235. package/dist/mindmap/use-tree-data.d.ts.map +1 -0
  236. package/dist/mindmap/use-tree-data.js +95 -0
  237. package/dist/mindmap/use-tree-data.js.map +1 -0
  238. package/dist/mindmap/view.d.ts +3 -0
  239. package/dist/mindmap/view.d.ts.map +1 -0
  240. package/dist/mindmap/view.js +141 -0
  241. package/dist/mindmap/view.js.map +1 -0
  242. package/dist/sensor-demo/client.d.ts +2 -0
  243. package/dist/sensor-demo/client.d.ts.map +1 -0
  244. package/dist/sensor-demo/client.js +2 -0
  245. package/dist/sensor-demo/client.js.map +1 -0
  246. package/dist/sensor-demo/server.d.ts +2 -0
  247. package/dist/sensor-demo/server.d.ts.map +1 -0
  248. package/dist/sensor-demo/server.js +2 -0
  249. package/dist/sensor-demo/server.js.map +1 -0
  250. package/dist/sensor-demo/service.d.ts +2 -0
  251. package/dist/sensor-demo/service.d.ts.map +1 -0
  252. package/dist/sensor-demo/service.js +27 -0
  253. package/dist/sensor-demo/service.js.map +1 -0
  254. package/dist/sensor-demo/types.d.ts +15 -0
  255. package/dist/sensor-demo/types.d.ts.map +1 -0
  256. package/dist/sensor-demo/types.js +18 -0
  257. package/dist/sensor-demo/types.js.map +1 -0
  258. package/dist/sensor-demo/view.d.ts +2 -0
  259. package/dist/sensor-demo/view.d.ts.map +1 -0
  260. package/dist/sensor-demo/view.js +33 -0
  261. package/dist/sensor-demo/view.js.map +1 -0
  262. package/dist/sensor-generator/action.d.ts +2 -0
  263. package/dist/sensor-generator/action.d.ts.map +1 -0
  264. package/dist/sensor-generator/action.js +23 -0
  265. package/dist/sensor-generator/action.js.map +1 -0
  266. package/dist/sensor-generator/client.d.ts +2 -0
  267. package/dist/sensor-generator/client.d.ts.map +1 -0
  268. package/dist/sensor-generator/client.js +2 -0
  269. package/dist/sensor-generator/client.js.map +1 -0
  270. package/dist/sensor-generator/server.d.ts +2 -0
  271. package/dist/sensor-generator/server.d.ts.map +1 -0
  272. package/dist/sensor-generator/server.js +2 -0
  273. package/dist/sensor-generator/server.js.map +1 -0
  274. package/dist/sensor-generator/view.d.ts +2 -0
  275. package/dist/sensor-generator/view.d.ts.map +1 -0
  276. package/dist/sensor-generator/view.js +64 -0
  277. package/dist/sensor-generator/view.js.map +1 -0
  278. package/dist/sim/client.d.ts +2 -0
  279. package/dist/sim/client.d.ts.map +1 -0
  280. package/dist/sim/client.js +2 -0
  281. package/dist/sim/client.js.map +1 -0
  282. package/dist/sim/seed.d.ts +2 -0
  283. package/dist/sim/seed.d.ts.map +1 -0
  284. package/dist/sim/seed.js +50 -0
  285. package/dist/sim/seed.js.map +1 -0
  286. package/dist/sim/server.d.ts +3 -0
  287. package/dist/sim/server.d.ts.map +1 -0
  288. package/dist/sim/server.js +3 -0
  289. package/dist/sim/server.js.map +1 -0
  290. package/dist/sim/service.d.ts +4 -0
  291. package/dist/sim/service.d.ts.map +1 -0
  292. package/dist/sim/service.js +528 -0
  293. package/dist/sim/service.js.map +1 -0
  294. package/dist/sim/types.d.ts +63 -0
  295. package/dist/sim/types.d.ts.map +1 -0
  296. package/dist/sim/types.js +57 -0
  297. package/dist/sim/types.js.map +1 -0
  298. package/dist/sim/view.d.ts +2 -0
  299. package/dist/sim/view.d.ts.map +1 -0
  300. package/dist/sim/view.js +205 -0
  301. package/dist/sim/view.js.map +1 -0
  302. package/dist/table/client.d.ts +4 -0
  303. package/dist/table/client.d.ts.map +1 -0
  304. package/dist/table/client.js +4 -0
  305. package/dist/table/client.js.map +1 -0
  306. package/dist/table/edit.d.ts +2 -0
  307. package/dist/table/edit.d.ts.map +1 -0
  308. package/dist/table/edit.js +115 -0
  309. package/dist/table/edit.js.map +1 -0
  310. package/dist/table/server.d.ts +2 -0
  311. package/dist/table/server.d.ts.map +1 -0
  312. package/dist/table/server.js +2 -0
  313. package/dist/table/server.js.map +1 -0
  314. package/dist/table/types.d.ts +18 -0
  315. package/dist/table/types.d.ts.map +1 -0
  316. package/dist/table/types.js +13 -0
  317. package/dist/table/types.js.map +1 -0
  318. package/dist/table/use-debounced-sync.d.ts +8 -0
  319. package/dist/table/use-debounced-sync.d.ts.map +1 -0
  320. package/dist/table/use-debounced-sync.js +56 -0
  321. package/dist/table/use-debounced-sync.js.map +1 -0
  322. package/dist/table/view.d.ts +2 -0
  323. package/dist/table/view.d.ts.map +1 -0
  324. package/dist/table/view.js +199 -0
  325. package/dist/table/view.js.map +1 -0
  326. package/dist/tasks/server.d.ts +2 -0
  327. package/dist/tasks/server.d.ts.map +1 -0
  328. package/dist/tasks/server.js +2 -0
  329. package/dist/tasks/server.js.map +1 -0
  330. package/dist/tasks/types.d.ts +22 -0
  331. package/dist/tasks/types.d.ts.map +1 -0
  332. package/dist/tasks/types.js +34 -0
  333. package/dist/tasks/types.js.map +1 -0
  334. package/dist/three/client.d.ts +2 -0
  335. package/dist/three/client.d.ts.map +1 -0
  336. package/dist/three/client.js +2 -0
  337. package/dist/three/client.js.map +1 -0
  338. package/dist/three/seed.d.ts +2 -0
  339. package/dist/three/seed.d.ts.map +1 -0
  340. package/dist/three/seed.js +45 -0
  341. package/dist/three/seed.js.map +1 -0
  342. package/dist/three/server.d.ts +3 -0
  343. package/dist/three/server.d.ts.map +1 -0
  344. package/dist/three/server.js +3 -0
  345. package/dist/three/server.js.map +1 -0
  346. package/dist/three/types.d.ts +178 -0
  347. package/dist/three/types.d.ts.map +1 -0
  348. package/dist/three/types.js +209 -0
  349. package/dist/three/types.js.map +1 -0
  350. package/dist/three/view.d.ts +2 -0
  351. package/dist/three/view.d.ts.map +1 -0
  352. package/dist/three/view.js +307 -0
  353. package/dist/three/view.js.map +1 -0
  354. package/dist/todo/client.d.ts +3 -0
  355. package/dist/todo/client.d.ts.map +1 -0
  356. package/dist/todo/client.js +3 -0
  357. package/dist/todo/client.js.map +1 -0
  358. package/dist/todo/seed.d.ts +2 -0
  359. package/dist/todo/seed.d.ts.map +1 -0
  360. package/dist/todo/seed.js +8 -0
  361. package/dist/todo/seed.js.map +1 -0
  362. package/dist/todo/server.d.ts +3 -0
  363. package/dist/todo/server.d.ts.map +1 -0
  364. package/dist/todo/server.js +3 -0
  365. package/dist/todo/server.js.map +1 -0
  366. package/dist/todo/types.d.ts +15 -0
  367. package/dist/todo/types.d.ts.map +1 -0
  368. package/dist/todo/types.js +29 -0
  369. package/dist/todo/types.js.map +1 -0
  370. package/dist/todo/view.d.ts +2 -0
  371. package/dist/todo/view.d.ts.map +1 -0
  372. package/dist/todo/view.js +27 -0
  373. package/dist/todo/view.js.map +1 -0
  374. package/dist/whisper/client.d.ts +2 -0
  375. package/dist/whisper/client.d.ts.map +1 -0
  376. package/dist/whisper/client.js +2 -0
  377. package/dist/whisper/client.js.map +1 -0
  378. package/dist/whisper/inbox.d.ts +2 -0
  379. package/dist/whisper/inbox.d.ts.map +1 -0
  380. package/dist/whisper/inbox.js +50 -0
  381. package/dist/whisper/inbox.js.map +1 -0
  382. package/dist/whisper/route.d.ts +10 -0
  383. package/dist/whisper/route.d.ts.map +1 -0
  384. package/dist/whisper/route.js +154 -0
  385. package/dist/whisper/route.js.map +1 -0
  386. package/dist/whisper/seed.d.ts +2 -0
  387. package/dist/whisper/seed.d.ts.map +1 -0
  388. package/dist/whisper/seed.js +14 -0
  389. package/dist/whisper/seed.js.map +1 -0
  390. package/dist/whisper/server.d.ts +5 -0
  391. package/dist/whisper/server.d.ts.map +1 -0
  392. package/dist/whisper/server.js +5 -0
  393. package/dist/whisper/server.js.map +1 -0
  394. package/dist/whisper/service.d.ts +2 -0
  395. package/dist/whisper/service.d.ts.map +1 -0
  396. package/dist/whisper/service.js +26 -0
  397. package/dist/whisper/service.js.map +1 -0
  398. package/dist/whisper/types.d.ts +38 -0
  399. package/dist/whisper/types.d.ts.map +1 -0
  400. package/dist/whisper/types.js +45 -0
  401. package/dist/whisper/types.js.map +1 -0
  402. package/dist/whisper/view.d.ts +2 -0
  403. package/dist/whisper/view.d.ts.map +1 -0
  404. package/dist/whisper/view.js +40 -0
  405. package/dist/whisper/view.js.map +1 -0
  406. package/doc/CLAUDE.md +49 -0
  407. package/doc/client.ts +5 -0
  408. package/doc/editor.css +283 -0
  409. package/doc/fs-codec.test.ts +119 -0
  410. package/doc/fs-codec.ts +50 -0
  411. package/doc/markdown.test.ts +152 -0
  412. package/doc/markdown.ts +284 -0
  413. package/doc/prefab.ts +26 -0
  414. package/doc/renderers.tsx +126 -0
  415. package/doc/seed.ts +10 -0
  416. package/doc/server.ts +5 -0
  417. package/doc/slash-command.ts +136 -0
  418. package/doc/slash-menu.tsx +91 -0
  419. package/doc/text.ts +20 -0
  420. package/doc/toolbar.tsx +86 -0
  421. package/doc/treenity-block-view.tsx +116 -0
  422. package/doc/treenity-block.ts +31 -0
  423. package/doc/types.ts +10 -0
  424. package/launcher/client.ts +4 -0
  425. package/launcher/icons.tsx +234 -0
  426. package/launcher/launcher.css +64 -0
  427. package/launcher/seed.ts +41 -0
  428. package/launcher/server.ts +2 -0
  429. package/launcher/types.ts +53 -0
  430. package/launcher/view.tsx +401 -0
  431. package/launcher/widgets.tsx +171 -0
  432. package/mindmap/branch.tsx +163 -0
  433. package/mindmap/client.ts +2 -0
  434. package/mindmap/mindmap.css +243 -0
  435. package/mindmap/sidebar.tsx +127 -0
  436. package/mindmap/types.ts +10 -0
  437. package/mindmap/view.tsx +247 -0
  438. package/package.json +75 -0
  439. package/sensor-demo/CLAUDE.md +3 -0
  440. package/sensor-demo/client.ts +1 -0
  441. package/sensor-demo/server.ts +1 -0
  442. package/sensor-demo/service.ts +27 -0
  443. package/sensor-demo/types.ts +20 -0
  444. package/sensor-demo/view.tsx +64 -0
  445. package/sensor-generator/CLAUDE.md +3 -0
  446. package/sensor-generator/action.ts +28 -0
  447. package/sensor-generator/client.ts +1 -0
  448. package/sensor-generator/server.ts +1 -0
  449. package/sensor-generator/view.tsx +107 -0
  450. package/sim/CLAUDE.md +16 -0
  451. package/sim/client.ts +1 -0
  452. package/sim/seed.ts +55 -0
  453. package/sim/server.ts +2 -0
  454. package/sim/service.ts +594 -0
  455. package/sim/sim.test.ts +282 -0
  456. package/sim/types.ts +87 -0
  457. package/sim/view.tsx +446 -0
  458. package/table/client.ts +3 -0
  459. package/table/edit.tsx +241 -0
  460. package/table/server.ts +1 -0
  461. package/table/types.ts +22 -0
  462. package/table/use-debounced-sync.ts +67 -0
  463. package/table/view.tsx +339 -0
  464. package/tasks/CLAUDE.md +6 -0
  465. package/tasks/server.ts +1 -0
  466. package/tasks/types.ts +36 -0
  467. package/three/CLAUDE.md +6 -0
  468. package/three/client.ts +1 -0
  469. package/three/seed.ts +54 -0
  470. package/three/server.ts +2 -0
  471. package/three/types.ts +238 -0
  472. package/three/view.tsx +453 -0
  473. package/todo/client.ts +2 -0
  474. package/todo/seed.ts +9 -0
  475. package/todo/server.ts +2 -0
  476. package/todo/types.ts +32 -0
  477. package/todo/view.tsx +67 -0
  478. package/whisper/CLAUDE.md +16 -0
  479. package/whisper/client.ts +1 -0
  480. package/whisper/inbox.ts +54 -0
  481. package/whisper/route.ts +182 -0
  482. package/whisper/seed.ts +15 -0
  483. package/whisper/server.ts +4 -0
  484. package/whisper/service.ts +29 -0
  485. package/whisper/types.ts +51 -0
  486. package/whisper/view.tsx +81 -0
package/doc/CLAUDE.md ADDED
@@ -0,0 +1,49 @@
1
+ # doc mod
2
+
3
+ Rich document editing with Tiptap. Markdown files on disk become typed `doc.page` nodes.
4
+
5
+ ## Type
6
+
7
+ **`doc.page`** — title (string) + content (Tiptap JSON string)
8
+
9
+ ## FS Codec
10
+
11
+ Registered on `text/markdown` mime type:
12
+ - **decode:** `.md` file → `doc.page` node. First `# H1` extracted as `title`, rest → Tiptap JSON `content`
13
+ - **encode:** `doc.page` node → `.md` file. Title prepended as `# Title`, content converted via `tiptapToMd()`
14
+
15
+ ## Markdown ↔ Tiptap
16
+
17
+ `markdown.ts` — bidirectional converter. Supports: headings, paragraphs, bold/italic/code, bullet/ordered lists, code blocks, blockquotes, horizontal rules, treenityBlock references.
18
+
19
+ ## Editor (frontend)
20
+
21
+ Tiptap WYSIWYG with:
22
+ - Toolbar: bold, italic, code, H1-H3, lists, quote, code block, HR
23
+ - Slash commands: `/` opens menu for formatting + embedding Treenity nodes
24
+ - Drag-and-drop: drop tree nodes to embed as `treenityBlock`
25
+ - Embedded nodes rendered via `<Render>` with live data
26
+
27
+ ## Prefabs
28
+
29
+ - **`doc/library`** — FS mount-point for a docs directory. Setup param: `{ root: string }`
30
+ - **`doc/demo`** — sample `doc.page` node
31
+
32
+ ## Files
33
+
34
+ ```
35
+ types.ts — DocPage class + registerComp
36
+ fs-codec.ts — text/markdown decode/encode for FS store
37
+ markdown.ts — mdToTiptap / tiptapToMd converters
38
+ text.ts — text context (Tiptap → plain text/markdown)
39
+ renderers.tsx — Tiptap editor view
40
+ treenity-block.ts — custom Tiptap node type for embedded components
41
+ treenity-block-view.tsx — renders embedded Treenity nodes
42
+ toolbar.tsx — editor toolbar
43
+ slash-command.ts — Tiptap Suggestion extension
44
+ slash-menu.tsx — slash command popup
45
+ node-picker.tsx — tree browser for embedding
46
+ prefab.ts — prefab registrations
47
+ server.ts — server imports
48
+ client.ts — client imports + view registration
49
+ ```
package/doc/client.ts ADDED
@@ -0,0 +1,5 @@
1
+ import './types';
2
+ import './editor.css';
3
+ import { registerDocViews } from './renderers';
4
+
5
+ registerDocViews();
package/doc/editor.css ADDED
@@ -0,0 +1,283 @@
1
+ /* doc mod — ProseMirror content styles */
2
+
3
+ /* Toolbar icon fix — SVG collapses to 0 width in flex without explicit CSS size */
4
+ .doc-toolbar svg {
5
+ width: 14px;
6
+ height: 14px;
7
+ flex-shrink: 0;
8
+ display: block;
9
+ }
10
+
11
+
12
+ .ProseMirror {
13
+ outline: none;
14
+ color: var(--text-2);
15
+ font-size: 14px;
16
+ line-height: 1.65;
17
+ font-family: var(--font);
18
+ font-weight: 400;
19
+ letter-spacing: 0.01em;
20
+ max-width: 72ch;
21
+ }
22
+
23
+ /* Headings — proportional scale, tight letter-spacing */
24
+ .ProseMirror h1 {
25
+ font-size: 1.5em; /* 21px */
26
+ font-weight: 650;
27
+ letter-spacing: -0.02em;
28
+ line-height: 1.3;
29
+ color: var(--text);
30
+ margin: 1.6em 0 0.4em;
31
+ }
32
+ .ProseMirror h2 {
33
+ font-size: 1.2em; /* 16.8px */
34
+ font-weight: 600;
35
+ letter-spacing: -0.01em;
36
+ line-height: 1.35;
37
+ color: var(--text);
38
+ margin: 1.4em 0 0.35em;
39
+ }
40
+ .ProseMirror h3 {
41
+ font-size: 1.05em; /* 14.7px */
42
+ font-weight: 600;
43
+ letter-spacing: -0.005em;
44
+ line-height: 1.4;
45
+ color: var(--text);
46
+ margin: 1.2em 0 0.3em;
47
+ }
48
+ .ProseMirror h1:first-child,
49
+ .ProseMirror h2:first-child,
50
+ .ProseMirror h3:first-child {
51
+ margin-top: 0;
52
+ }
53
+
54
+ /* Paragraphs */
55
+ .ProseMirror p {
56
+ margin: 0 0 0.6em;
57
+ }
58
+ .ProseMirror p:last-child {
59
+ margin-bottom: 0;
60
+ }
61
+
62
+ /* Inline code */
63
+ .ProseMirror code {
64
+ font-family: 'SF Mono', SFMono-Regular, 'Cascadia Code', 'Fira Code', Consolas, monospace;
65
+ font-size: 0.84em;
66
+ padding: 0.15em 0.35em;
67
+ background: var(--surface-3);
68
+ border-radius: 3px;
69
+ color: var(--text);
70
+ letter-spacing: 0;
71
+ }
72
+
73
+ /* Code blocks */
74
+ .ProseMirror pre {
75
+ background: var(--surface);
76
+ border: 1px solid var(--border);
77
+ border-radius: 6px;
78
+ padding: 12px 14px;
79
+ overflow-x: auto;
80
+ margin: 0.75em 0;
81
+ }
82
+ .ProseMirror pre code {
83
+ background: none;
84
+ padding: 0;
85
+ border-radius: 0;
86
+ font-size: 12.5px;
87
+ line-height: 1.6;
88
+ color: var(--text);
89
+ }
90
+
91
+ /* Blockquote */
92
+ .ProseMirror blockquote {
93
+ border-left: 2px solid var(--border);
94
+ padding-left: 14px;
95
+ margin: 0.75em 0;
96
+ color: var(--text-3);
97
+ font-style: italic;
98
+ }
99
+
100
+ /* Lists */
101
+ .ProseMirror ul { list-style: disc; padding-left: 1.4em; margin: 0.4em 0 0.6em; }
102
+ .ProseMirror ol { list-style: decimal; padding-left: 1.4em; margin: 0.4em 0 0.6em; }
103
+ .ProseMirror li { margin: 0.2em 0; }
104
+ .ProseMirror li > p { margin: 0; }
105
+ .ProseMirror li::marker { color: var(--text-3); }
106
+ .ProseMirror ul ul, .ProseMirror ol ul { list-style: circle; margin: 0.15em 0; }
107
+ .ProseMirror ul ul ul { list-style: square; }
108
+
109
+ /* Horizontal rule */
110
+ .ProseMirror hr {
111
+ border: none;
112
+ border-top: 1px solid var(--border);
113
+ margin: 1.2em 0;
114
+ }
115
+
116
+ /* Links */
117
+ .ProseMirror a {
118
+ color: var(--accent-color);
119
+ text-decoration: underline;
120
+ text-underline-offset: 2px;
121
+ }
122
+
123
+ /* Bold — full text color for contrast */
124
+ .ProseMirror strong { font-weight: 600; color: var(--text); }
125
+ .ProseMirror em { font-style: italic; }
126
+
127
+ /* Selection */
128
+ .ProseMirror ::selection {
129
+ background: rgba(46, 204, 113, 0.2);
130
+ }
131
+
132
+ /* Placeholder */
133
+ .ProseMirror p.is-editor-empty:first-child::before {
134
+ content: attr(data-placeholder);
135
+ float: left;
136
+ color: var(--text-3);
137
+ pointer-events: none;
138
+ height: 0;
139
+ }
140
+
141
+ /* Slash command menu */
142
+ .slash-menu {
143
+ background: var(--surface);
144
+ border: 1px solid var(--border);
145
+ border-radius: 6px;
146
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
147
+ overflow: hidden;
148
+ width: 200px;
149
+ max-height: 280px;
150
+ overflow-y: auto;
151
+ }
152
+ .slash-menu-empty {
153
+ padding: 10px 12px;
154
+ font-size: 12px;
155
+ color: var(--text-3);
156
+ }
157
+ .slash-menu-item {
158
+ display: flex;
159
+ align-items: center;
160
+ justify-content: space-between;
161
+ width: 100%;
162
+ padding: 7px 12px;
163
+ background: none;
164
+ border: none;
165
+ cursor: pointer;
166
+ text-align: left;
167
+ color: var(--text-2);
168
+ transition: background 0.1s;
169
+ }
170
+ .slash-menu-item:hover,
171
+ .slash-menu-item.selected {
172
+ background: var(--surface-2);
173
+ color: var(--text);
174
+ }
175
+ .slash-menu-title {
176
+ font-size: 13px;
177
+ font-weight: 500;
178
+ }
179
+ .slash-menu-group {
180
+ font-size: 11px;
181
+ color: var(--text-3);
182
+ }
183
+
184
+ /* Syntax highlighting tokens (lowlight/highlight.js dark) */
185
+ .ProseMirror pre .hljs-comment,
186
+ .ProseMirror pre .hljs-quote { color: #6a737d; font-style: italic; }
187
+ .ProseMirror pre .hljs-keyword,
188
+ .ProseMirror pre .hljs-selector-tag,
189
+ .ProseMirror pre .hljs-built_in { color: #ff7b72; }
190
+ .ProseMirror pre .hljs-string,
191
+ .ProseMirror pre .hljs-attr,
192
+ .ProseMirror pre .hljs-addition { color: #a5d6ff; }
193
+ .ProseMirror pre .hljs-number,
194
+ .ProseMirror pre .hljs-literal { color: #79c0ff; }
195
+ .ProseMirror pre .hljs-title,
196
+ .ProseMirror pre .hljs-section,
197
+ .ProseMirror pre .hljs-name { color: #d2a8ff; }
198
+ .ProseMirror pre .hljs-variable,
199
+ .ProseMirror pre .hljs-template-variable { color: #ffa657; }
200
+ .ProseMirror pre .hljs-type { color: #79c0ff; }
201
+ .ProseMirror pre .hljs-tag { color: #7ee787; }
202
+ .ProseMirror pre .hljs-attribute { color: #ffa657; }
203
+ .ProseMirror pre .hljs-regexp { color: #a5d6ff; }
204
+ .ProseMirror pre .hljs-deletion { color: #ffa198; }
205
+ .ProseMirror pre .hljs-meta { color: #79c0ff; }
206
+ .ProseMirror pre .hljs-subst { color: #e6edf3; }
207
+ .ProseMirror pre .hljs-symbol { color: #79c0ff; }
208
+
209
+ /* Task list */
210
+ .ProseMirror ul[data-type="taskList"] {
211
+ list-style: none;
212
+ padding-left: 0.2em;
213
+ }
214
+ .ProseMirror ul[data-type="taskList"] li {
215
+ display: flex;
216
+ align-items: flex-start;
217
+ gap: 0.5em;
218
+ }
219
+ .ProseMirror ul[data-type="taskList"] li > label {
220
+ margin-top: 0.1em;
221
+ flex-shrink: 0;
222
+ }
223
+ .ProseMirror ul[data-type="taskList"] li > div {
224
+ flex: 1;
225
+ }
226
+ .ProseMirror ul[data-type="taskList"] input[type="checkbox"] {
227
+ accent-color: var(--accent-color);
228
+ cursor: pointer;
229
+ width: 14px;
230
+ height: 14px;
231
+ }
232
+
233
+ /* Tables */
234
+ .ProseMirror table {
235
+ border-collapse: collapse;
236
+ width: 100%;
237
+ margin: 0.75em 0;
238
+ font-size: 0.93em;
239
+ }
240
+ .ProseMirror th,
241
+ .ProseMirror td {
242
+ border: 1px solid var(--border);
243
+ padding: 5px 10px;
244
+ text-align: left;
245
+ vertical-align: top;
246
+ }
247
+ .ProseMirror th {
248
+ background: var(--surface-2);
249
+ font-weight: 600;
250
+ color: var(--text);
251
+ }
252
+ .ProseMirror td {
253
+ color: var(--text-2);
254
+ }
255
+ .ProseMirror tr:nth-child(even) td {
256
+ background: var(--surface);
257
+ }
258
+
259
+ /* Treenity embedded block */
260
+ .treenity-block-wrapper {
261
+ margin: 0.6em 0;
262
+ border: 1px solid var(--border);
263
+ border-radius: 6px;
264
+ overflow: hidden;
265
+ background: var(--surface);
266
+ transition: border-color 0.15s;
267
+ }
268
+ .treenity-block-wrapper:hover {
269
+ border-color: var(--accent-color);
270
+ }
271
+ .treenity-block-header {
272
+ display: flex;
273
+ align-items: center;
274
+ gap: 6px;
275
+ padding: 3px 8px;
276
+ background: var(--surface-2);
277
+ font-size: 11px;
278
+ color: var(--text-3);
279
+ border-bottom: 1px solid var(--border);
280
+ }
281
+ .treenity-block-content {
282
+ padding: 8px;
283
+ }
@@ -0,0 +1,119 @@
1
+ import type { Tree } from '@treenity/core/tree';
2
+ import { createFsTree } from '@treenity/core/tree/fs';
3
+ import assert from 'node:assert/strict';
4
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { afterEach, beforeEach, describe, it } from 'node:test';
8
+
9
+ // Register the codec — side effect import
10
+ import './fs-codec';
11
+
12
+ describe('doc fs-codec', () => {
13
+ let dir: string;
14
+ let store: Tree;
15
+
16
+ beforeEach(async () => {
17
+ dir = await mkdtemp(join(tmpdir(), 'treenity-doc-codec-'));
18
+ store = await createFsTree(dir);
19
+ });
20
+
21
+ afterEach(async () => {
22
+ if (dir) await rm(dir, { recursive: true, force: true });
23
+ });
24
+
25
+ it('decode: .md file → doc.page node', async () => {
26
+ await writeFile(join(dir, 'readme.md'), '# Hello World\n\nSome content here.\n');
27
+
28
+ const node = await store.get('/readme');
29
+ assert.equal(node?.$type, 'doc.page');
30
+ assert.equal((node as any).title, 'Hello World');
31
+
32
+ const content = JSON.parse((node as any).content);
33
+ assert.equal(content.type, 'doc');
34
+ assert.ok(content.content.length > 0);
35
+ });
36
+
37
+ it('decode: .md without H1 — empty title', async () => {
38
+ await writeFile(join(dir, 'notes.md'), 'Just a paragraph.\n\n## Second heading\n');
39
+
40
+ const node = await store.get('/notes');
41
+ assert.equal(node?.$type, 'doc.page');
42
+ assert.equal((node as any).title, '');
43
+
44
+ const content = JSON.parse((node as any).content);
45
+ assert.equal(content.content[0].type, 'paragraph');
46
+ assert.equal(content.content[1].type, 'heading');
47
+ });
48
+
49
+ it('encode: doc.page node → .md file', async () => {
50
+ const tiptapDoc = {
51
+ type: 'doc',
52
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello from Tiptap' }] }],
53
+ };
54
+
55
+ await store.set({
56
+ $path: '/output',
57
+ $type: 'doc.page',
58
+ title: 'My Doc',
59
+ content: JSON.stringify(tiptapDoc),
60
+ } as any);
61
+
62
+ const raw = await readFile(join(dir, 'output.md'), 'utf-8');
63
+ assert.ok(raw.startsWith('# My Doc'));
64
+ assert.ok(raw.includes('Hello from Tiptap'));
65
+ });
66
+
67
+ it('encode: no title — no H1 prefix', async () => {
68
+ const tiptapDoc = {
69
+ type: 'doc',
70
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Just text' }] }],
71
+ };
72
+
73
+ await store.set({
74
+ $path: '/bare',
75
+ $type: 'doc.page',
76
+ title: '',
77
+ content: JSON.stringify(tiptapDoc),
78
+ } as any);
79
+
80
+ const raw = await readFile(join(dir, 'bare.md'), 'utf-8');
81
+ assert.ok(!raw.startsWith('#'));
82
+ assert.ok(raw.includes('Just text'));
83
+ });
84
+
85
+ it('roundtrip: .md → node → .md preserves content', async () => {
86
+ const original = '# Project Notes\n\nThis is **bold** and *italic*.\n\n- item one\n- item two\n\n```ts\nconst x = 1;\n```\n';
87
+ await writeFile(join(dir, 'notes.md'), original);
88
+
89
+ // Decode
90
+ const node = await store.get('/notes');
91
+ assert.equal(node?.$type, 'doc.page');
92
+ assert.equal((node as any).title, 'Project Notes');
93
+
94
+ // Encode back
95
+ await store.set(node!);
96
+ const result = await readFile(join(dir, 'notes.md'), 'utf-8');
97
+
98
+ // Verify key elements survived roundtrip
99
+ assert.ok(result.includes('# Project Notes'));
100
+ assert.ok(result.includes('**bold**'));
101
+ assert.ok(result.includes('*italic*'));
102
+ assert.ok(result.includes('- item one'));
103
+ assert.ok(result.includes('```ts'));
104
+ assert.ok(result.includes('const x = 1;'));
105
+ });
106
+
107
+ it('getChildren lists .md files as doc.page', async () => {
108
+ await writeFile(join(dir, 'a.md'), '# Alpha\n\nContent A\n');
109
+ await writeFile(join(dir, 'b.md'), '# Beta\n\nContent B\n');
110
+ await writeFile(join(dir, 'c.txt'), 'plain text');
111
+
112
+ const { items } = await store.getChildren('/');
113
+ const types = Object.fromEntries(items.map(n => [n.$path, n.$type]));
114
+
115
+ assert.equal(types['/a'], 'doc.page');
116
+ assert.equal(types['/b'], 'doc.page');
117
+ assert.equal(types['/c'], 'text/plain');
118
+ });
119
+ });
@@ -0,0 +1,50 @@
1
+ // Markdown file ↔ doc.page node codec for FS store
2
+ // Decode: .md file → doc.page node (title from first H1, content as Tiptap JSON)
3
+ // Encode: doc.page node → .md file (Tiptap JSON → markdown)
4
+ // Paths are extensionless — encode appends .md
5
+
6
+ import type { NodeData } from '@treenity/core';
7
+ import { register } from '@treenity/core';
8
+ import { readFile, writeFile } from 'node:fs/promises';
9
+ import { mdToTiptap, type TiptapNode, tiptapToMd } from './markdown';
10
+
11
+ register('text/markdown', 'decode', async (filePath: string, nodePath: string) => {
12
+ const raw = await readFile(filePath, 'utf-8');
13
+ const tiptapDoc = mdToTiptap(raw);
14
+
15
+ // Extract title from first heading if present
16
+ let title = '';
17
+ const firstBlock = tiptapDoc.content?.[0];
18
+ if (firstBlock?.type === 'heading' && firstBlock.attrs?.level === 1) {
19
+ title = firstBlock.content?.map((n) => n.text ?? '').join('') ?? '';
20
+ // Remove H1 from content since it's in the title field
21
+ tiptapDoc.content = tiptapDoc.content!.slice(1);
22
+ }
23
+
24
+ return {
25
+ $path: nodePath,
26
+ $type: 'doc.page',
27
+ title,
28
+ content: JSON.stringify(tiptapDoc),
29
+ } as NodeData;
30
+ });
31
+
32
+ register('doc.page', 'encode', async (node: NodeData, filePath: string) => {
33
+ const { title, content } = node as any;
34
+ let md = '';
35
+
36
+ if (title) md += `# ${title}\n\n`;
37
+
38
+ if (content) {
39
+ try {
40
+ const doc = JSON.parse(content) as TiptapNode;
41
+ md += tiptapToMd(doc);
42
+ } catch {
43
+ md += content;
44
+ }
45
+ }
46
+
47
+ // Extensionless path → append .md
48
+ const actualPath = filePath.endsWith('.md') ? filePath : filePath + '.md';
49
+ await writeFile(actualPath, md.trimEnd() + '\n', 'utf-8');
50
+ });
@@ -0,0 +1,152 @@
1
+ import assert from 'node:assert/strict';
2
+ import { describe, it } from 'node:test';
3
+ import { mdToTiptap, type TiptapNode, tiptapToMd } from './markdown';
4
+
5
+ describe('mdToTiptap', () => {
6
+ it('parses heading', () => {
7
+ const doc = mdToTiptap('# Hello');
8
+ assert.equal(doc.type, 'doc');
9
+ assert.equal(doc.content?.[0].type, 'heading');
10
+ assert.equal(doc.content?.[0].attrs?.level, 1);
11
+ assert.equal(doc.content?.[0].content?.[0].text, 'Hello');
12
+ });
13
+
14
+ it('parses multiple heading levels', () => {
15
+ const doc = mdToTiptap('## Second\n### Third');
16
+ assert.equal(doc.content?.[0].attrs?.level, 2);
17
+ assert.equal(doc.content?.[1].attrs?.level, 3);
18
+ });
19
+
20
+ it('parses paragraph', () => {
21
+ const doc = mdToTiptap('Just some text');
22
+ assert.equal(doc.content?.[0].type, 'paragraph');
23
+ assert.equal(doc.content?.[0].content?.[0].text, 'Just some text');
24
+ });
25
+
26
+ it('parses bold and italic', () => {
27
+ const doc = mdToTiptap('This is **bold** and *italic*');
28
+ const nodes = doc.content?.[0].content ?? [];
29
+ assert.ok(nodes.some((n) => n.text === 'bold' && n.marks?.[0]?.type === 'bold'));
30
+ assert.ok(nodes.some((n) => n.text === 'italic' && n.marks?.[0]?.type === 'italic'));
31
+ });
32
+
33
+ it('parses inline code', () => {
34
+ const doc = mdToTiptap('Use `foo()` here');
35
+ const nodes = doc.content?.[0].content ?? [];
36
+ assert.ok(nodes.some((n) => n.text === 'foo()' && n.marks?.[0]?.type === 'code'));
37
+ });
38
+
39
+ it('parses bullet list', () => {
40
+ const doc = mdToTiptap('- one\n- two\n- three');
41
+ assert.equal(doc.content?.[0].type, 'bulletList');
42
+ assert.equal(doc.content?.[0].content?.length, 3);
43
+ });
44
+
45
+ it('parses ordered list', () => {
46
+ const doc = mdToTiptap('1. first\n2. second');
47
+ assert.equal(doc.content?.[0].type, 'orderedList');
48
+ assert.equal(doc.content?.[0].content?.length, 2);
49
+ });
50
+
51
+ it('parses code block', () => {
52
+ const doc = mdToTiptap('```ts\nconst x = 1;\n```');
53
+ assert.equal(doc.content?.[0].type, 'codeBlock');
54
+ assert.equal(doc.content?.[0].attrs?.language, 'ts');
55
+ assert.equal(doc.content?.[0].content?.[0].text, 'const x = 1;');
56
+ });
57
+
58
+ it('parses blockquote', () => {
59
+ const doc = mdToTiptap('> quoted text');
60
+ assert.equal(doc.content?.[0].type, 'blockquote');
61
+ });
62
+
63
+ it('parses horizontal rule', () => {
64
+ const doc = mdToTiptap('---');
65
+ assert.equal(doc.content?.[0].type, 'horizontalRule');
66
+ });
67
+
68
+ it('handles empty input', () => {
69
+ const doc = mdToTiptap('');
70
+ assert.equal(doc.type, 'doc');
71
+ assert.ok(doc.content?.length);
72
+ });
73
+
74
+ it('handles mixed content', () => {
75
+ const md = '# Title\n\nSome text\n\n- item 1\n- item 2\n\n```\ncode\n```';
76
+ const doc = mdToTiptap(md);
77
+ const types = doc.content?.map((c) => c.type);
78
+ assert.deepEqual(types, ['heading', 'paragraph', 'bulletList', 'codeBlock']);
79
+ });
80
+ });
81
+
82
+ describe('tiptapToMd', () => {
83
+ it('converts heading', () => {
84
+ const doc: TiptapNode = {
85
+ type: 'doc',
86
+ content: [{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Title' }] }],
87
+ };
88
+ assert.equal(tiptapToMd(doc), '## Title');
89
+ });
90
+
91
+ it('converts paragraph with marks', () => {
92
+ const doc: TiptapNode = {
93
+ type: 'doc',
94
+ content: [{
95
+ type: 'paragraph',
96
+ content: [
97
+ { type: 'text', text: 'Hello ' },
98
+ { type: 'text', text: 'world', marks: [{ type: 'bold' }] },
99
+ ],
100
+ }],
101
+ };
102
+ assert.equal(tiptapToMd(doc), 'Hello **world**');
103
+ });
104
+
105
+ it('converts bullet list', () => {
106
+ const doc: TiptapNode = {
107
+ type: 'doc',
108
+ content: [{
109
+ type: 'bulletList',
110
+ content: [
111
+ { type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'a' }] }] },
112
+ { type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'b' }] }] },
113
+ ],
114
+ }],
115
+ };
116
+ assert.equal(tiptapToMd(doc), '- a\n- b');
117
+ });
118
+
119
+ it('converts code block', () => {
120
+ const doc: TiptapNode = {
121
+ type: 'doc',
122
+ content: [{ type: 'codeBlock', attrs: { language: 'js' }, content: [{ type: 'text', text: 'x = 1' }] }],
123
+ };
124
+ assert.equal(tiptapToMd(doc), '```js\nx = 1\n```');
125
+ });
126
+
127
+ it('converts horizontal rule', () => {
128
+ const doc: TiptapNode = { type: 'doc', content: [{ type: 'horizontalRule' }] };
129
+ assert.equal(tiptapToMd(doc), '---');
130
+ });
131
+ });
132
+
133
+ describe('roundtrip md → tiptap → md', () => {
134
+ it('preserves basic structure', () => {
135
+ const original = '# Hello\n\nSome paragraph\n\n- one\n- two';
136
+ const tiptap = mdToTiptap(original);
137
+ const result = tiptapToMd(tiptap);
138
+ // Should contain the same elements (whitespace may differ)
139
+ assert.ok(result.includes('# Hello'));
140
+ assert.ok(result.includes('Some paragraph'));
141
+ assert.ok(result.includes('- one'));
142
+ assert.ok(result.includes('- two'));
143
+ });
144
+
145
+ it('preserves code block', () => {
146
+ const original = '```ts\nconst x = 1;\n```';
147
+ const tiptap = mdToTiptap(original);
148
+ const result = tiptapToMd(tiptap);
149
+ assert.ok(result.includes('```ts'));
150
+ assert.ok(result.includes('const x = 1;'));
151
+ });
152
+ });