@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/board/view.tsx ADDED
@@ -0,0 +1,574 @@
1
+ // Board views — kanban board + task detail (editable)
2
+
3
+ import {
4
+ closestCorners,
5
+ DndContext,
6
+ type DragEndEvent,
7
+ type DragOverEvent,
8
+ DragOverlay,
9
+ type DragStartEvent,
10
+ PointerSensor,
11
+ useDroppable,
12
+ useSensor,
13
+ useSensors,
14
+ } from '@dnd-kit/core';
15
+ import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
16
+ import { CSS } from '@dnd-kit/utilities';
17
+ import { type ComponentData, type NodeData, register } from '@treenity/core';
18
+ import { Render, RenderContext, type View } from '@treenity/react/context';
19
+ import { set, useChildren, useNavigate, usePath } from '@treenity/react/hooks';
20
+ import { minimd } from '@treenity/react/lib/minimd';
21
+ import { cn } from '@treenity/react/lib/utils';
22
+ import { trpc } from '@treenity/react/trpc';
23
+ import { Button } from '@treenity/react/ui/button';
24
+ import { Dialog, DialogContent, DialogTitle } from '@treenity/react/ui/dialog';
25
+ import { FormField } from '@treenity/react/ui/form-field';
26
+ import { Input } from '@treenity/react/ui/input';
27
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@treenity/react/ui/select';
28
+ import { Textarea } from '@treenity/react/ui/textarea';
29
+ import { useMemo, useRef, useState } from 'react';
30
+ import { BoardColumn, BoardKanban, BoardTask } from './types';
31
+
32
+ type TaskStatus = BoardTask['status'];
33
+
34
+ const PRIORITY_COLOR: Record<string, string> = {
35
+ urgent: 'bg-red-500',
36
+ high: 'bg-orange-400',
37
+ normal: 'bg-blue-400',
38
+ low: 'bg-zinc-400',
39
+ };
40
+
41
+ const PRIORITIES: { value: string; label: string }[] = [
42
+ { value: 'urgent', label: 'Urgent' },
43
+ { value: 'high', label: 'High' },
44
+ { value: 'normal', label: 'Normal' },
45
+ { value: 'low', label: 'Low' },
46
+ ];
47
+
48
+ function PriorityDot({ priority }: { priority: string }) {
49
+ return (
50
+ <span
51
+ className={cn('inline-block h-2 w-2 rounded-full', PRIORITY_COLOR[priority] ?? PRIORITY_COLOR.normal)}
52
+ title={priority}
53
+ />
54
+ );
55
+ }
56
+
57
+ function AiBadge() {
58
+ return (
59
+ <span className="rounded bg-violet-500/20 px-1.5 py-0.5 text-[10px] font-medium text-violet-400">
60
+ AI
61
+ </span>
62
+ );
63
+ }
64
+
65
+ // ── board.task view — editable task detail ──
66
+
67
+ const TaskView: View<BoardTask> = ({ ctx }) => {
68
+ const path = ctx?.path ?? '';
69
+ const node = usePath<NodeData>(path);
70
+ const proxy = usePath(path, BoardTask);
71
+ const [editingDesc, setEditingDesc] = useState(false);
72
+ if (!node || !proxy) return null;
73
+
74
+ const title = typeof proxy.title === 'string' ? proxy.title : '';
75
+ const description = typeof proxy.description === 'string' ? proxy.description : '';
76
+ const status = (typeof proxy.status === 'string' ? proxy.status : 'backlog') as TaskStatus;
77
+ const priority = typeof proxy.priority === 'string' ? proxy.priority : 'normal';
78
+ const assignee = typeof proxy.assignee === 'string' ? proxy.assignee : '';
79
+ const result = typeof proxy.result === 'string' ? proxy.result : '';
80
+
81
+ const save = (patch: Record<string, unknown>) => {
82
+ set({ ...node, ...patch, updatedAt: Date.now() });
83
+ };
84
+
85
+ return (
86
+ <div className="space-y-4">
87
+ <BlurInput
88
+ value={title}
89
+ placeholder="Task title..."
90
+ className="border-none bg-transparent p-0 text-lg font-semibold shadow-none focus-visible:ring-0"
91
+ onSave={v => save({ title: v })}
92
+ />
93
+
94
+ {editingDesc ? (
95
+ <Textarea
96
+ key={`desc-${node.$path}`}
97
+ defaultValue={description}
98
+ placeholder="Add a description..."
99
+ className="max-h-60 min-h-20 resize-none text-sm"
100
+ autoFocus
101
+ onBlur={e => {
102
+ if (e.target.value !== description) save({ description: e.target.value });
103
+ setEditingDesc(false);
104
+ }}
105
+ />
106
+ ) : description ? (
107
+ <div
108
+ className="cursor-pointer whitespace-pre-wrap rounded border border-transparent p-2 text-sm line-clamp-4 hover:border-border"
109
+ onClick={() => setEditingDesc(true)}
110
+ title="Click to edit"
111
+ >
112
+ {description}
113
+ </div>
114
+ ) : (
115
+ <div
116
+ className="cursor-pointer rounded border border-dashed border-border p-2 text-sm text-muted-foreground hover:border-foreground/30"
117
+ onClick={() => setEditingDesc(true)}
118
+ >
119
+ Add a description...
120
+ </div>
121
+ )}
122
+
123
+ <div className="space-y-2 text-sm">
124
+ <FormField label="Status">
125
+ <div className="flex items-center gap-2">
126
+ <span className="rounded bg-muted px-2 py-0.5 text-xs font-medium capitalize">{status}</span>
127
+ <TaskActions proxy={proxy} status={status} />
128
+ </div>
129
+ </FormField>
130
+
131
+ <FormField label="Priority">
132
+ <Select value={priority} onValueChange={v => save({ priority: v })}>
133
+ <SelectTrigger className="h-8 w-32">
134
+ <SelectValue />
135
+ </SelectTrigger>
136
+ <SelectContent>
137
+ {PRIORITIES.map(p => (
138
+ <SelectItem key={p.value} value={p.value}>
139
+ <span className="flex items-center gap-1.5">
140
+ <PriorityDot priority={p.value} /> {p.label}
141
+ </span>
142
+ </SelectItem>
143
+ ))}
144
+ </SelectContent>
145
+ </Select>
146
+ </FormField>
147
+
148
+ <FormField label="Assignee">
149
+ <BlurInput
150
+ value={assignee}
151
+ placeholder="Unassigned"
152
+ className="h-8 text-sm"
153
+ onSave={v => save({ assignee: v })}
154
+ />
155
+ </FormField>
156
+
157
+ {(status === 'review' || status === 'done' || result) && (
158
+ <FormField label="Result">
159
+ <ResultField path={node.$path} result={result} onSave={v => save({ result: v })} />
160
+ </FormField>
161
+ )}
162
+ </div>
163
+
164
+ {/* Named components (ai.plan, ai.thread, etc.) */}
165
+ <NamedComponents node={node} />
166
+
167
+ <EmbeddedTaskLog taskRef={typeof node.taskRef === 'string' ? node.taskRef : ''} />
168
+ </div>
169
+ );
170
+ };
171
+
172
+ function NamedComponents({ node }: { node: NodeData }) {
173
+ const keys = Object.keys(node).filter(k => {
174
+ if (k.startsWith('$') || k === 'taskRef') return false;
175
+ const v = node[k];
176
+ return v && typeof v === 'object' && '$type' in v;
177
+ });
178
+ if (!keys.length) return null;
179
+
180
+ return (
181
+ <div className="flex flex-col gap-3">
182
+ {keys.map(k => (
183
+ <Render key={k} value={node[k] as ComponentData} />
184
+ ))}
185
+ </div>
186
+ );
187
+ }
188
+
189
+ function ResultField({ path, result, onSave }: { path: string; result: string; onSave: (v: string) => void }) {
190
+ const [editing, setEditing] = useState(false);
191
+ const html = useMemo(() => result ? minimd(result) : '', [result]);
192
+
193
+ if (editing) {
194
+ return (
195
+ <Textarea
196
+ key={`res-${path}`}
197
+ defaultValue={result}
198
+ placeholder="Result notes..."
199
+ className="min-h-20 max-h-60 resize-none text-sm"
200
+ autoFocus
201
+ onBlur={e => {
202
+ if (e.target.value !== result) onSave(e.target.value);
203
+ setEditing(false);
204
+ }}
205
+ />
206
+ );
207
+ }
208
+
209
+ if (!result) {
210
+ return (
211
+ <div
212
+ className="cursor-pointer rounded border border-dashed border-border p-2 text-sm text-muted-foreground hover:border-foreground/30"
213
+ onClick={() => setEditing(true)}
214
+ >
215
+ Add result...
216
+ </div>
217
+ );
218
+ }
219
+
220
+ return (
221
+ <div
222
+ className="minimd cursor-pointer rounded border border-transparent p-2 text-sm max-h-60 overflow-y-auto hover:border-border"
223
+ onClick={() => setEditing(true)}
224
+ title="Click to edit"
225
+ dangerouslySetInnerHTML={{ __html: html }}
226
+ />
227
+ );
228
+ }
229
+
230
+ function EmbeddedTaskLog({ taskRef }: { taskRef: string }) {
231
+ const mtTask = usePath(taskRef || null) as NodeData | undefined;
232
+ if (!mtTask) return null;
233
+
234
+ return (
235
+ <div className="mt-2 rounded-md border border-border p-2">
236
+ <div className="mb-1 flex items-center gap-2">
237
+ <span className="text-[10px] font-medium uppercase text-muted-foreground">Agent Log</span>
238
+ <span className={cn(
239
+ 'rounded px-1.5 py-0.5 text-[10px] font-medium',
240
+ mtTask.status === 'running' ? 'bg-sky-500/20 text-sky-400' :
241
+ mtTask.status === 'done' ? 'bg-emerald-500/20 text-emerald-400' :
242
+ 'bg-red-500/20 text-red-400',
243
+ )}>
244
+ {String(mtTask.status)}
245
+ </span>
246
+ </div>
247
+ <Render value={mtTask} />
248
+ </div>
249
+ );
250
+ }
251
+
252
+ register('board.task', 'react', TaskView);
253
+
254
+ function TaskListItem({ value }: { value: NodeData }) {
255
+ const nav = useNavigate();
256
+ const priority = typeof value.priority === 'string' ? value.priority : 'normal';
257
+ const title = typeof value.title === 'string' && value.title
258
+ ? value.title
259
+ : value.$path.split('/').at(-1);
260
+ const aiStatus = typeof (value as any).aiStatus === 'string' ? (value as any).aiStatus : '';
261
+ const assignee = typeof value.assignee === 'string' ? value.assignee : '';
262
+
263
+ return (
264
+ <button
265
+ onClick={() => nav(value.$path)}
266
+ className="flex w-full items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-left transition-colors hover:bg-accent/50"
267
+ >
268
+ <PriorityDot priority={priority} />
269
+ <span className="flex-1 truncate text-sm font-medium">{title}</span>
270
+ {aiStatus && (
271
+ <span className="rounded bg-violet-500/20 px-1.5 py-0.5 text-[10px] font-medium text-violet-400">
272
+ {aiStatus}
273
+ </span>
274
+ )}
275
+ {assignee && !aiStatus && (
276
+ <span className="text-xs text-muted-foreground">{assignee}</span>
277
+ )}
278
+ </button>
279
+ );
280
+ }
281
+
282
+ register('board.task', 'react:list', TaskListItem as any);
283
+
284
+ // ── Helpers ──
285
+
286
+ function TaskActions({ proxy, status }: { proxy: ReturnType<typeof usePath<BoardTask>>; status: TaskStatus }) {
287
+ const btn = 'h-7 text-xs';
288
+ switch (status) {
289
+ case 'backlog':
290
+ return <Button variant="outline" size="sm" className={btn} onClick={() => proxy.start()}>Start</Button>;
291
+ case 'todo':
292
+ return <Button variant="outline" size="sm" className={btn} onClick={() => proxy.start()}>Begin</Button>;
293
+ case 'doing':
294
+ return <Button variant="outline" size="sm" className={btn} onClick={() => proxy.submit()}>Submit</Button>;
295
+ case 'review':
296
+ return (
297
+ <div className="flex gap-1">
298
+ <Button variant="outline" size="sm" className={btn} onClick={() => proxy.approve()}>Approve</Button>
299
+ <Button variant="ghost" size="sm" className={btn} onClick={() => proxy.reject()}>Reject</Button>
300
+ </div>
301
+ );
302
+ case 'done':
303
+ return <Button variant="ghost" size="sm" className={btn} onClick={() => proxy.reopen()}>Reopen</Button>;
304
+ default:
305
+ return null;
306
+ }
307
+ }
308
+
309
+ function BlurInput({ value, onSave, ...props }: {
310
+ value: string; onSave: (v: string) => void;
311
+ } & Omit<React.ComponentProps<typeof Input>, 'value' | 'onChange' | 'onBlur' | 'onKeyDown'>) {
312
+ const ref = useRef<HTMLInputElement>(null);
313
+ return (
314
+ <Input
315
+ ref={ref}
316
+ defaultValue={value}
317
+ onBlur={e => { if (e.target.value !== value) onSave(e.target.value); }}
318
+ onKeyDown={e => { if (e.key === 'Enter') ref.current?.blur(); }}
319
+ {...props}
320
+ />
321
+ );
322
+ }
323
+
324
+ // ── Task card (kanban) ──
325
+
326
+ function TaskCardContent({ task, isDragging }: { task: NodeData; isDragging?: boolean }) {
327
+ const proxy = usePath(task.$path, BoardTask);
328
+
329
+ const title = typeof proxy?.title === 'string' && proxy.title
330
+ ? proxy.title
331
+ : task.$path.split('/').at(-1);
332
+ const assignee = typeof proxy?.assignee === 'string' ? proxy.assignee : '';
333
+ const priority = typeof proxy?.priority === 'string' ? proxy.priority : 'normal';
334
+ const result = typeof proxy?.result === 'string' ? proxy.result : '';
335
+ const aiStatus = typeof task.aiStatus === 'string' ? task.aiStatus : '';
336
+ const isAi = assignee === 'metatron' || !!aiStatus;
337
+
338
+ return (
339
+ <div className={cn(
340
+ 'mb-2 rounded-md border border-border bg-card p-3 shadow-sm transition-colors',
341
+ isDragging ? 'opacity-50' : 'hover:bg-accent/50',
342
+ )}>
343
+ <div className="mb-1 flex items-center gap-2">
344
+ <PriorityDot priority={priority} />
345
+ <span className="flex-1 text-sm font-semibold leading-tight">{title}</span>
346
+ {aiStatus ? (
347
+ <span className="rounded bg-violet-500/20 px-1.5 py-0.5 text-[10px] font-medium text-violet-400">
348
+ {String(aiStatus)}
349
+ </span>
350
+ ) : isAi ? <AiBadge /> : null}
351
+ </div>
352
+
353
+ {assignee && !isAi && (
354
+ <div className="mb-1 text-xs text-muted-foreground">{assignee}</div>
355
+ )}
356
+
357
+ {result && (
358
+ <div className="line-clamp-2 text-xs text-muted-foreground">{result}</div>
359
+ )}
360
+ </div>
361
+ );
362
+ }
363
+
364
+ function TaskCard({ task, onSelect, colStatus }: { task: NodeData; onSelect: (path: string) => void; colStatus: string }) {
365
+ const proxy = usePath(task.$path, BoardTask);
366
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
367
+ id: task.$path,
368
+ data: { task, status: colStatus, move: proxy.move },
369
+ });
370
+
371
+ // dnd-kit requires inline style for runtime-computed transforms
372
+ const style = { transform: CSS.Transform.toString(transform), transition };
373
+
374
+ return (
375
+ <div
376
+ ref={setNodeRef}
377
+ {...listeners}
378
+ {...attributes}
379
+ className="cursor-grab active:cursor-grabbing"
380
+ style={style}
381
+ onClick={() => { if (!isDragging) onSelect(task.$path); }}
382
+ >
383
+ <TaskCardContent task={task} isDragging={isDragging} />
384
+ </div>
385
+ );
386
+ }
387
+
388
+ // ── Column ──
389
+
390
+ function Column({ col, onSelect, highlighted }: { col: NodeData; onSelect: (path: string) => void; highlighted?: boolean }) {
391
+ const proxy = usePath(col.$path, BoardColumn);
392
+ const tasks = useChildren(col.$path, { watch: true, watchNew: true });
393
+ const status = col.$path.split('/').at(-1) ?? '';
394
+ const { setNodeRef } = useDroppable({ id: `col:${status}`, data: { status } });
395
+
396
+ const label = typeof proxy?.label === 'string' ? proxy.label : status;
397
+ const color = typeof proxy?.color === 'string' ? proxy.color : 'border-zinc-400';
398
+ const taskIds = useMemo(() => tasks.map(t => t.$path), [tasks]);
399
+
400
+ return (
401
+ <div className="flex min-w-40 flex-1 flex-col">
402
+ <div className={cn('mb-2 flex items-center gap-2 border-b-2 pb-1.5', color)}>
403
+ <span className="text-sm font-bold">{label}</span>
404
+ <span className="text-xs text-muted-foreground">({tasks.length})</span>
405
+ </div>
406
+
407
+ <SortableContext items={taskIds} strategy={verticalListSortingStrategy}>
408
+ <div
409
+ ref={setNodeRef}
410
+ className={cn(
411
+ 'flex-1 rounded-md p-1 transition-colors min-h-16',
412
+ highlighted && 'bg-accent/30 ring-1 ring-accent',
413
+ )}
414
+ >
415
+ {tasks.map(task => (
416
+ <TaskCard key={task.$path} task={task} onSelect={onSelect} colStatus={status} />
417
+ ))}
418
+
419
+ {tasks.length === 0 && (
420
+ <div className="rounded-md border border-dashed border-border py-6 text-center text-xs text-muted-foreground">
421
+ {highlighted ? 'Drop here' : 'Empty'}
422
+ </div>
423
+ )}
424
+ </div>
425
+ </SortableContext>
426
+ </div>
427
+ );
428
+ }
429
+
430
+ // ── Kanban Board ──
431
+
432
+ const KanbanView: View<BoardKanban> = ({ value, ctx }) => {
433
+ const [selectedTask, setSelectedTask] = useState<string | null>(null);
434
+ const [activeTask, setActiveTask] = useState<NodeData | null>(null);
435
+ const [overColumn, setOverColumn] = useState<string | null>(null);
436
+ const selectedNode = usePath(selectedTask ?? '') as NodeData | undefined;
437
+ const basePath = ctx!.path;
438
+
439
+ const sensors = useSensors(
440
+ useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
441
+ );
442
+
443
+ const children = useChildren(basePath, { watch: true, watchNew: true });
444
+ const columns = children
445
+ .filter(c => c.$type === 'board.column')
446
+ .sort((a, b) => {
447
+ const oa = typeof a.order === 'number' ? a.order : 0;
448
+ const ob = typeof b.order === 'number' ? b.order : 0;
449
+ return oa - ob;
450
+ });
451
+
452
+ const createTask = async () => {
453
+ const id = Date.now().toString(36).toUpperCase();
454
+ await trpc.set.mutate({
455
+ node: {
456
+ $path: `${basePath}/data/${id}`,
457
+ $type: 'board.task',
458
+ title: `Task #${id}`,
459
+ status: 'backlog',
460
+ priority: 'normal',
461
+ assignee: '',
462
+ description: '',
463
+ result: '',
464
+ createdAt: Date.now(),
465
+ updatedAt: Date.now(),
466
+ } as NodeData,
467
+ });
468
+ };
469
+
470
+ const handleDragStart = (event: DragStartEvent) => {
471
+ const task = event.active.data.current?.task;
472
+ if (task && typeof task === 'object' && '$path' in task) {
473
+ setActiveTask(task as NodeData);
474
+ }
475
+ };
476
+
477
+ const handleDragOver = (event: DragOverEvent) => {
478
+ const over = event.over;
479
+ if (!over) { setOverColumn(null); return; }
480
+
481
+ // Over a column droppable
482
+ const overId = String(over.id);
483
+ if (overId.startsWith('col:')) { setOverColumn(overId.slice(4)); return; }
484
+
485
+ // Over a card — get its column status
486
+ const status = over.data.current?.status;
487
+ setOverColumn(typeof status === 'string' ? status : null);
488
+ };
489
+
490
+ const handleDragEnd = (event: DragEndEvent) => {
491
+ setActiveTask(null);
492
+ setOverColumn(null);
493
+ const { active, over } = event;
494
+ if (!over) return;
495
+
496
+ // Target is either a column droppable (col:status) or a card sortable (has data.status)
497
+ const overId = String(over.id);
498
+ const targetStatus = over.data.current?.status
499
+ ?? (overId.startsWith('col:') ? overId.slice(4) : undefined);
500
+ if (typeof targetStatus !== 'string') return;
501
+
502
+ const src = active.data.current;
503
+ if (!src?.task || typeof src.task !== 'object' || !('status' in src.task)) return;
504
+ if (src.task.status === targetStatus) return;
505
+
506
+ const move = src.move;
507
+ if (typeof move === 'function') move({ status: targetStatus });
508
+ };
509
+
510
+ return (
511
+ <div className="view-full px-4 py-3">
512
+ <div className="mb-3 flex items-center justify-between">
513
+ <h2 className="text-lg font-bold">Task Board</h2>
514
+ <Button onClick={createTask}>+ New Task</Button>
515
+ </div>
516
+
517
+ <DndContext sensors={sensors} collisionDetection={closestCorners} onDragStart={handleDragStart} onDragOver={handleDragOver} onDragEnd={handleDragEnd}>
518
+ <div className="flex gap-3 overflow-x-auto pb-2">
519
+ {columns.map(col => (
520
+ <Column key={col.$path} col={col} onSelect={setSelectedTask} highlighted={overColumn === (col.$path.split('/').at(-1) ?? '')} />
521
+ ))}
522
+ </div>
523
+
524
+ <DragOverlay>
525
+ {activeTask && (
526
+ <div className="w-60 rotate-2 opacity-90">
527
+ <TaskCardContent task={activeTask} />
528
+ </div>
529
+ )}
530
+ </DragOverlay>
531
+ </DndContext>
532
+
533
+ {selectedTask && selectedNode && (
534
+ <Dialog open onOpenChange={open => { if (!open) setSelectedTask(null); }}>
535
+ <DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-lg" aria-describedby={undefined}>
536
+ <DialogTitle className="sr-only">Task</DialogTitle>
537
+ <Render value={selectedNode} />
538
+ </DialogContent>
539
+ </Dialog>
540
+ )}
541
+ </div>
542
+ );
543
+ };
544
+
545
+ register('board.kanban', 'react', KanbanView);
546
+
547
+ // ── board.column view ──
548
+
549
+ function ColumnView({ value }: { value: NodeData & BoardColumn }) {
550
+ const tasks = useChildren(value.$path, { watch: true, watchNew: true });
551
+ const label = typeof value.label === 'string' ? value.label : value.$path.split('/').at(-1);
552
+ const color = typeof value.color === 'string' ? value.color : 'border-zinc-400';
553
+
554
+ return (
555
+ <div className="flex flex-col gap-2 p-3">
556
+ <div className={cn('flex items-center gap-2 border-b-2 pb-1.5', color)}>
557
+ <span className="text-sm font-bold">{label}</span>
558
+ <span className="text-xs text-muted-foreground">({tasks.length})</span>
559
+ </div>
560
+ <RenderContext name="react:list">
561
+ {tasks.map(task => (
562
+ <Render key={task.$path} value={task} />
563
+ ))}
564
+ {tasks.length === 0 && (
565
+ <div className="rounded-md border border-dashed border-border py-4 text-center text-xs text-muted-foreground">
566
+ Empty
567
+ </div>
568
+ )}
569
+ </RenderContext>
570
+ </div>
571
+ );
572
+ }
573
+
574
+ register('board.column', 'react', ColumnView as any);
@@ -0,0 +1,18 @@
1
+ ## brahman
2
+ Telegram bot constructor. Pages with ordered actions, DnD menu editor, session/user management.
3
+
4
+ ### Файлы
5
+ - types.ts — 24 component classes (BotConfig, PageConfig, 20 action types, User, Session)
6
+ - service.ts — Grammy bot runtime: middleware, template engine, 20 action handlers via register()
7
+ - action.ts — server-side action:run for brahman.page (tRPC execute)
8
+ - views/action-cards.tsx — icons, summaries, editors for all 20 action types
9
+ - views/page-layout.tsx — DnD sortable action list (@dnd-kit)
10
+ - views/menu-editor.tsx — DnD button/row editor for inline/reply keyboards
11
+ - views/bot-view.tsx — bot config view (token, langs, maintenance)
12
+ - views/tstring-input.tsx — TString multilingual text editor
13
+
14
+ ### Конвенции
15
+ - Action handlers registered via `register('brahman.action.X', 'brahman:run', handler)` — no switch/case
16
+ - TString = Record<string, string> for multilingual text (keyed by lang code)
17
+ - Template engine: Handlebars-like {var}, {{#ifEquals}}, {{#tag}}, {{eval expr}}
18
+ - Views go in views/, registered in view.ts for 'react' and 'react:list' contexts