@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/todo/seed.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { type NodeData } from '@treenity/core';
2
+ import { registerPrefab } from '@treenity/core/mod';
3
+
4
+ registerPrefab('todo', 'seed', [
5
+ { $path: 'todo', $type: 'dir' },
6
+ { $path: 'todo/list', $type: 'todo.list', title: 'My Todos' },
7
+ { $path: 'todo/list/1', $type: 'todo.item', title: 'Read the quickstart', done: true },
8
+ { $path: 'todo/list/2', $type: 'todo.item', title: 'Build something', done: false },
9
+ ] as NodeData[]);
package/todo/server.ts ADDED
@@ -0,0 +1,2 @@
1
+ import './types';
2
+ import './seed';
package/todo/types.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { getCtx, registerType } from '@treenity/core/comp';
2
+
3
+ class TodoItem {
4
+ title = '';
5
+ done = false;
6
+
7
+ /** @description Toggle done state */
8
+ toggle() {
9
+ this.done = !this.done;
10
+ }
11
+ }
12
+
13
+ class TodoList {
14
+ title = 'My Todos';
15
+
16
+ /** @description Add a new todo item */
17
+ async add(data: { title: string }) {
18
+ if (!data.title?.trim()) throw new Error('Title required');
19
+ const { node, store } = getCtx();
20
+ const id = Date.now().toString(36);
21
+ await store.set({
22
+ $path: `${node.$path}/${id}`,
23
+ $type: 'todo.item',
24
+ title: data.title.trim(),
25
+ done: false,
26
+ });
27
+ }
28
+ }
29
+
30
+ registerType('todo.item', TodoItem);
31
+ registerType('todo.list', TodoList);
32
+ export { TodoItem, TodoList };
package/todo/view.tsx ADDED
@@ -0,0 +1,67 @@
1
+ import { type NodeData, register } from '@treenity/core';
2
+ import { useChildren, usePath } from '@treenity/react/hooks';
3
+ import { useState } from 'react';
4
+ import { TodoItem, TodoList } from './types';
5
+
6
+ function TodoListView({ value }: { value: NodeData }) {
7
+ const list = usePath(value.$path, TodoList);
8
+ const children = useChildren(value.$path, { watch: true, watchNew: true });
9
+ const [draft, setDraft] = useState('');
10
+
11
+ const handleAdd = async () => {
12
+ if (!draft.trim()) return;
13
+ await list.add({ title: draft });
14
+ setDraft('');
15
+ };
16
+
17
+ return (
18
+ <div className="max-w-md mx-auto p-4">
19
+ <h2 className="text-xl font-bold mb-4">{list.title}</h2>
20
+
21
+ <div className="flex gap-2 mb-4">
22
+ <input
23
+ className="flex-1 border rounded px-3 py-1.5 text-sm"
24
+ placeholder="What needs to be done?"
25
+ value={draft}
26
+ onChange={e => setDraft(e.target.value)}
27
+ onKeyDown={e => e.key === 'Enter' && handleAdd()}
28
+ />
29
+ <button
30
+ className="bg-blue-600 text-white px-3 py-1.5 rounded text-sm"
31
+ onClick={handleAdd}
32
+ >Add</button>
33
+ </div>
34
+
35
+ <ul className="space-y-1">
36
+ {children.map(child => (
37
+ <TodoItemRow key={child.$path} value={child} />
38
+ ))}
39
+ </ul>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ function TodoItemRow({ value }: { value: NodeData }) {
45
+ const item = usePath(value.$path, TodoItem);
46
+
47
+ return (
48
+ <li
49
+ className="flex items-center gap-2 px-3 py-2 rounded
50
+ hover:bg-neutral-100 cursor-pointer"
51
+ onClick={() => item.toggle()}
52
+ >
53
+ <span className={`w-4 h-4 rounded border flex items-center
54
+ justify-center text-xs ${item.done
55
+ ? 'bg-blue-600 border-blue-600 text-white'
56
+ : 'border-neutral-300'}`}>
57
+ {item.done ? '✓' : ''}
58
+ </span>
59
+ <span className={item.done ? 'line-through text-neutral-400' : ''}>
60
+ {item.title}
61
+ </span>
62
+ </li>
63
+ );
64
+ }
65
+
66
+ register('todo.list', 'react', TodoListView as any);
67
+ register('todo.item', 'react', TodoItemRow as any);
@@ -0,0 +1,16 @@
1
+ ## mods/whisper
2
+ Audio transcription via @huggingface/transformers (Whisper). HTTP POST → node in tree immediately, transcribe in background.
3
+
4
+ ### Файлы
5
+ - route.ts — createWhisperHandler(cfg): POST audio → create node with status '...', respond 200, transcribe async via ffmpeg+transformers.js, update node
6
+ - types.ts — WhisperAudio, WhisperText, WhisperMeta, WhisperChecklist component classes
7
+ - service.ts — service registration
8
+ - server.ts — mounts HTTP handler, tRPC routes
9
+ - client.ts — frontend API
10
+ - view.tsx — React UI
11
+
12
+ ### Конвенции
13
+ - Pipeline: POST → ffmpeg → 16kHz mono WAV → Float32Array → Whisper pipeline
14
+ - Node appears in tree immediately (status '...'), updated when transcription finishes
15
+ - Models lazy-loaded and cached: `onnx-community/whisper-{model}` or full HF model id
16
+ - timeId() = YYYYMMDD-HHmmss-SSS for sortable node names
@@ -0,0 +1 @@
1
+ import './view';
@@ -0,0 +1,54 @@
1
+ // Whisper → Agent bridge service
2
+ // Watches a whisper channel for completed transcriptions, creates agent tasks
3
+
4
+ import { createNode, getComponent, type NodeData, register } from '@treenity/core';
5
+ import { WhisperInbox, WhisperText } from './types';
6
+
7
+ const log = (msg: string) => console.log(`[whisper.inbox] ${msg}`);
8
+
9
+ function getText(node: NodeData): string | undefined {
10
+ const comp = getComponent(node, WhisperText);
11
+ if (!comp) return undefined;
12
+ return comp.content && comp.content !== '...' ? comp.content : undefined;
13
+ }
14
+
15
+ register('whisper.inbox', 'service', async (node, ctx) => {
16
+ const config = getComponent(node, WhisperInbox);
17
+ if (!config) throw new Error(`missing config on ${node.$path}`);
18
+
19
+ const sent = new Set<string>();
20
+
21
+ // Mark existing COMPLETED transcriptions as already processed
22
+ const { items } = await ctx.store.getChildren(config.source);
23
+ for (const child of items) {
24
+ if (child.$type !== 'whisper.transcription') continue;
25
+ if (getText(child)) sent.add(child.$path);
26
+ }
27
+ log(`watching ${config.source} → ${config.target} (${sent.size} existing)`);
28
+
29
+ const unsub = ctx.subscribe(config.source, (event) => {
30
+ if (event.type !== 'set' && event.type !== 'patch') return;
31
+
32
+ ctx.store.get(event.path).then(async (n) => {
33
+ if (!n || n.$type !== 'whisper.transcription') return;
34
+ if (sent.has(n.$path)) return;
35
+
36
+ const text = getText(n);
37
+ if (!text) return;
38
+
39
+ sent.add(n.$path);
40
+
41
+ const taskId = `t-${Date.now()}`;
42
+ const taskPath = `${config.target}/tasks/${taskId}`;
43
+ await ctx.store.set(createNode(taskPath, 'agent.task', {
44
+ prompt: text,
45
+ status: 'pending',
46
+ createdAt: Date.now(),
47
+ }));
48
+
49
+ log(`${n.$path} → ${taskPath}`);
50
+ }).catch(err => console.error(`[whisper.inbox] error processing ${event.path}:`, err));
51
+ }, { children: true });
52
+
53
+ return { stop: async () => unsub() };
54
+ });
@@ -0,0 +1,182 @@
1
+ // Whisper audio transcription — HTTP handler
2
+ // POST ?id=channel-id with audio body → create node immediately, transcribe in background, update node
3
+
4
+ import { type AutomaticSpeechRecognitionPipeline, pipeline } from '@huggingface/transformers';
5
+ import { createNode } from '@treenity/core';
6
+ import { newComp } from '@treenity/core/comp';
7
+ import type { Tree } from '@treenity/core/tree';
8
+ import { execFile } from 'node:child_process';
9
+ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
10
+ import { type IncomingMessage, type ServerResponse } from 'node:http';
11
+ import { join, resolve } from 'node:path';
12
+ import { promisify } from 'node:util';
13
+ import WaveFile from 'wavefile';
14
+ import { WhisperAudio, WhisperChecklist, WhisperMeta, WhisperText } from './types';
15
+
16
+ const execFileAsync = promisify(execFile);
17
+
18
+ // Lazy-initialized pipelines keyed by model name
19
+ const pipelines = new Map<string, Promise<AutomaticSpeechRecognitionPipeline>>();
20
+
21
+ function getTranscriber(model: string): Promise<AutomaticSpeechRecognitionPipeline> {
22
+ let p = pipelines.get(model);
23
+ if (!p) {
24
+ const modelId = model.includes('/') ? model : `onnx-community/whisper-${model}`;
25
+ console.log(`[whisper] loading model ${modelId}...`);
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- pipeline() union type is too complex for TS
27
+ p = (pipeline as any)('automatic-speech-recognition', modelId, {
28
+ dtype: {
29
+ encoder_model: 'fp32',
30
+ decoder_model_merged: 'q4',
31
+ },
32
+ }) as Promise<AutomaticSpeechRecognitionPipeline>;
33
+ p.then(() => console.log(`[whisper] model ${modelId} ready`))
34
+ .catch((e: unknown) => {
35
+ console.error(`[whisper] model ${modelId} failed:`, e);
36
+ pipelines.delete(model); // allow retry on next request
37
+ });
38
+ pipelines.set(model, p);
39
+ }
40
+ return p;
41
+ }
42
+
43
+ function respond(res: ServerResponse, status: number, body: unknown) {
44
+ res.writeHead(status, { 'Content-Type': 'application/json' });
45
+ res.end(JSON.stringify(body));
46
+ }
47
+
48
+ // Sortable time-based id: YYYYMMDD-HHmmss-SSS
49
+ function timeId(): string {
50
+ const d = new Date();
51
+ const pad = (n: number, w = 2) => String(n).padStart(w, '0');
52
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}-${pad(d.getMilliseconds(), 3)}`;
53
+ }
54
+
55
+ /** Read 16kHz mono WAV file → Float32Array for transformers.js */
56
+ async function readWavAsFloat32(wavPath: string): Promise<Float32Array> {
57
+ const buf = await readFile(wavPath);
58
+ const wav = new WaveFile.WaveFile(buf);
59
+ wav.toBitDepth('32f');
60
+ wav.toSampleRate(16000);
61
+ let samples = wav.getSamples();
62
+ if (Array.isArray(samples)) samples = samples[0];
63
+ return new Float32Array(samples);
64
+ }
65
+
66
+ export type WhisperRouteConfig = {
67
+ nodePath: string;
68
+ model: string;
69
+ language: string;
70
+ audioDir: string;
71
+ };
72
+
73
+ export function createWhisperHandler(cfg: WhisperRouteConfig) {
74
+ const audioDir = resolve(cfg.audioDir);
75
+
76
+ // Pre-warm the pipeline
77
+ getTranscriber(cfg.model);
78
+
79
+ return async (req: IncomingMessage, res: ServerResponse, store: Tree) => {
80
+ if (req.method !== 'POST') {
81
+ return respond(res, 405, { error: 'Method not allowed' });
82
+ }
83
+
84
+ const url = new URL(req.url ?? '/', 'http://localhost');
85
+ const id = url.searchParams.get('id');
86
+ if (!id) {
87
+ return respond(res, 400, { error: 'Missing ?id= query parameter' });
88
+ }
89
+
90
+ const chunks: Buffer[] = [];
91
+ for await (const chunk of req) chunks.push(chunk as Buffer);
92
+ const body = Buffer.concat(chunks);
93
+
94
+ if (body.length === 0) {
95
+ return respond(res, 400, { error: 'Empty request body' });
96
+ }
97
+
98
+ const mime = req.headers['content-type'] || 'audio/wav';
99
+ const ext = mime.includes('mp3') ? 'mp3'
100
+ : mime.includes('ogg') ? 'ogg'
101
+ : mime.includes('webm') ? 'webm'
102
+ : mime.includes('m4a') ? 'm4a'
103
+ : 'wav';
104
+
105
+ const noteId = timeId();
106
+ const filename = `${id}-${noteId}.${ext}`;
107
+ const filePath = join(audioDir, filename);
108
+ const wavPath = join(audioDir, `${id}-${noteId}_16k.wav`);
109
+
110
+ try {
111
+ await mkdir(audioDir, { recursive: true });
112
+ await writeFile(filePath, body);
113
+
114
+ // Ensure {servicePath}/{id} dir exists
115
+ const idDirPath = `${cfg.nodePath}/${id}`;
116
+ if (!(await store.get(idDirPath))) {
117
+ await store.set(createNode(idDirPath, 'whisper.channel', {}, {
118
+ checklist: newComp(WhisperChecklist, {}),
119
+ }));
120
+ }
121
+
122
+ // 1. Create node immediately with audio — appears in tree right away
123
+ const nodePath = `${idDirPath}/${noteId}`;
124
+ const node = createNode(nodePath, 'whisper.transcription', {}, {
125
+ audio: newComp(WhisperAudio, { filename, size: body.length, mime }),
126
+ text: newComp(WhisperText, { content: '...' }),
127
+ meta: newComp(WhisperMeta, {
128
+ model: cfg.model,
129
+ language: cfg.language,
130
+ duration: 0,
131
+ segments: 0,
132
+ transcribedAt: Date.now(),
133
+ }),
134
+ });
135
+ await store.set(node);
136
+ console.log(`[whisper] ${filename} → ${nodePath} (processing...)`);
137
+
138
+ // 2. Respond immediately — client sees the node path
139
+ respond(res, 200, { path: nodePath, status: 'processing' });
140
+
141
+ // 3. Transcribe in background, update node when done
142
+ execFileAsync('ffmpeg', ['-i', filePath, '-ar', '16000', '-ac', '1', '-f', 'wav', '-y', wavPath])
143
+ .then(() => readWavAsFloat32(wavPath))
144
+ .then(async (audioData) => {
145
+ const transcriber = await getTranscriber(cfg.model);
146
+ const result = await transcriber(audioData, {
147
+ language: cfg.language,
148
+ return_timestamps: true,
149
+ });
150
+
151
+ const output = Array.isArray(result) ? result[0] : result;
152
+ const text = (output.text ?? '').trim();
153
+ const outputChunks = (output as any).chunks as Array<{ text: string; timestamp: [number, number | null] }> | undefined;
154
+
155
+ const duration = outputChunks?.length
156
+ ? (outputChunks[outputChunks.length - 1].timestamp[1] ?? 0)
157
+ : 0;
158
+
159
+ const updated = await store.get(nodePath);
160
+ if (!updated) return;
161
+ updated.text = newComp(WhisperText, { content: text });
162
+ updated.meta = newComp(WhisperMeta, {
163
+ model: cfg.model,
164
+ language: cfg.language,
165
+ duration,
166
+ segments: outputChunks?.length ?? 0,
167
+ transcribedAt: Date.now(),
168
+ });
169
+ await store.set(updated);
170
+ console.log(`[whisper] ${nodePath} done (${outputChunks?.length ?? 0} seg, ${duration}s)`);
171
+ })
172
+ .catch((err) => console.error(`[whisper] ${nodePath} transcription failed:`, err))
173
+ .finally(() => {
174
+ unlink(wavPath).catch(() => {});
175
+ });
176
+
177
+ } catch (err) {
178
+ console.error('[whisper] transcription error:', err);
179
+ respond(res, 500, { error: String(err) });
180
+ }
181
+ };
182
+ }
@@ -0,0 +1,15 @@
1
+ import { type NodeData } from '@treenity/core';
2
+ import { registerPrefab } from '@treenity/core/mod';
3
+
4
+ registerPrefab('whisper', 'seed', [
5
+ { $path: 'whisper', $type: 'whisper.service',
6
+ config: {
7
+ $type: 'whisper.config', model: 'small', language: 'ru',
8
+ audioDir: './data/audio', url: '/api/notice/audio',
9
+ },
10
+ },
11
+ { $path: '/sys/autostart/whisper', $type: 'ref', $ref: '/whisper' },
12
+ { $path: 'whisper/inbox', $type: 'whisper.inbox',
13
+ source: '/whisper/default', target: '/agent' },
14
+ { $path: '/sys/autostart/whisper-inbox', $type: 'ref', $ref: '/whisper/inbox' },
15
+ ] as NodeData[]);
@@ -0,0 +1,4 @@
1
+ import './types';
2
+ import './service';
3
+ import './inbox';
4
+ import './seed';
@@ -0,0 +1,29 @@
1
+ // Whisper service — autostart-compatible, registers HTTP route dynamically
2
+
3
+ import { getComponent, register } from '@treenity/core';
4
+ import { routeRegistry } from '@treenity/core/server/server';
5
+ import { createWhisperHandler } from './route';
6
+ import { WhisperConfig } from './types';
7
+
8
+ register('whisper.service', 'service', async (node, _ctx) => {
9
+ const config = getComponent(node, WhisperConfig);
10
+ if (!config) throw new Error(`[whisper] missing config on ${node.$path}`);
11
+
12
+ const routePath = config.url || node.$path;
13
+ const handler = createWhisperHandler({
14
+ nodePath: node.$path,
15
+ model: config.model,
16
+ language: config.language,
17
+ audioDir: config.audioDir,
18
+ });
19
+
20
+ routeRegistry.set(routePath, handler);
21
+ console.log(`[whisper] route ${routePath} (model: ${config.model})`);
22
+
23
+ return {
24
+ stop: async () => {
25
+ routeRegistry.delete(routePath);
26
+ console.log(`[whisper] unregistered ${routePath}`);
27
+ },
28
+ };
29
+ });
@@ -0,0 +1,51 @@
1
+ import { registerType } from '@treenity/core/comp';
2
+
3
+ /** Speech-to-text config — Whisper model, language, audio path */
4
+ export class WhisperConfig {
5
+ model = 'small';
6
+ language = 'ru';
7
+ audioDir = './data/audio';
8
+ url = ''; // override route path; empty = use node's $path
9
+ }
10
+
11
+ /** Audio file metadata — filename, size, MIME type */
12
+ export class WhisperAudio {
13
+ filename = '';
14
+ size = 0;
15
+ mime = 'audio/wav';
16
+ }
17
+
18
+ /** Transcription result — recognized text content */
19
+ export class WhisperText {
20
+ /** @format textarea */
21
+ content = '';
22
+ }
23
+
24
+ /** Transcription metadata — model, language, duration, segments */
25
+ export class WhisperMeta {
26
+ model = '';
27
+ language = '';
28
+ duration = 0;
29
+ segments = 0;
30
+ transcribedAt = 0;
31
+ }
32
+
33
+ /** Meeting checklist — action items from transcription */
34
+ export class WhisperChecklist {
35
+ checked: string[] = [];
36
+ }
37
+
38
+ /** Bridge: auto-send whisper transcriptions to a task inbox */
39
+ export class WhisperInbox {
40
+ /** @format path @description Whisper channel to watch, e.g. /whisper/kriz */
41
+ source = '';
42
+ /** @format path @description Target inbox, e.g. /agent */
43
+ target = '';
44
+ }
45
+
46
+ registerType('whisper.config', WhisperConfig);
47
+ registerType('whisper.audio', WhisperAudio);
48
+ registerType('whisper.text', WhisperText);
49
+ registerType('whisper.meta', WhisperMeta);
50
+ registerType('whisper.checklist', WhisperChecklist);
51
+ registerType('whisper.inbox', WhisperInbox);
@@ -0,0 +1,81 @@
1
+ // Whisper channel view — checklist of transcribed audio notes
2
+
3
+ import { type NodeData, register } from '@treenity/core';
4
+ import { set, useChildren, usePath } from '@treenity/react/hooks';
5
+
6
+ function ChannelView({ value }: { value: NodeData }) {
7
+ const children = useChildren(value.$path, { watchNew: true });
8
+ const node = usePath(value.$path);
9
+ const checklist = node?.checklist as { $type: string; checked?: string[] } | undefined;
10
+
11
+ const checked = new Set<string>(checklist?.checked ?? []);
12
+
13
+ const items = children.filter(c => {
14
+ const text = c.text as { $type: string; content: string } | undefined;
15
+ return text?.$type === 'whisper.text' && text.content && text.content !== '...';
16
+ });
17
+
18
+ const processing = children.filter(c => {
19
+ const text = c.text as { $type: string; content: string } | undefined;
20
+ return text?.$type === 'whisper.text' && text.content === '...';
21
+ });
22
+
23
+ const toggle = (path: string) => {
24
+ if (!node) return;
25
+ const next = new Set(checked);
26
+ if (next.has(path)) next.delete(path);
27
+ else next.add(path);
28
+ set({ ...node, checklist: { $type: 'whisper.checklist', checked: [...next] } });
29
+ };
30
+
31
+ return (
32
+ <div className="node-default-view">
33
+ {items.map(child => {
34
+ const text = (child.text as any).content as string;
35
+ const name = child.$path.slice(child.$path.lastIndexOf('/') + 1);
36
+ const meta = child.meta as { duration?: number } | undefined;
37
+ const done = checked.has(child.$path);
38
+
39
+ return (
40
+ <label
41
+ key={child.$path}
42
+ className={`flex gap-2.5 px-3 py-2.5 cursor-pointer border-b border-[var(--border)] ${done ? 'opacity-50' : ''}`}
43
+ >
44
+ <input
45
+ type="checkbox"
46
+ checked={done}
47
+ onChange={() => toggle(child.$path)}
48
+ className="mt-0.5 shrink-0 w-4 h-4 p-0"
49
+ />
50
+ <div className="flex-1">
51
+ <div className={`text-[13px] leading-snug ${done ? 'line-through' : ''}`}>
52
+ {text}
53
+ </div>
54
+ <div className="text-[11px] text-[var(--text-3)] mt-1">
55
+ {name}{meta?.duration ? ` · ${meta.duration}s` : ''}
56
+ </div>
57
+ </div>
58
+ </label>
59
+ );
60
+ })}
61
+
62
+ {processing.map(child => {
63
+ const name = child.$path.slice(child.$path.lastIndexOf('/') + 1);
64
+ return (
65
+ <div
66
+ key={child.$path}
67
+ className="px-3 py-2.5 border-b border-[var(--border)] text-[var(--text-3)] text-[13px] italic"
68
+ >
69
+ {name} — transcribing...
70
+ </div>
71
+ );
72
+ })}
73
+
74
+ {items.length === 0 && processing.length === 0 && (
75
+ <div className="node-empty">No transcriptions yet</div>
76
+ )}
77
+ </div>
78
+ );
79
+ }
80
+
81
+ register('whisper.channel', 'react', ChannelView as any);