@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
@@ -0,0 +1,401 @@
1
+ // Launcher view — dashboard with drag-drop from tree + context switching
2
+
3
+ import { isRef, type NodeData, register, resolveExact } from '@treenity/core';
4
+ import { Render, RenderContext, type View } from '@treenity/react/context';
5
+ import { useChildren, useNavigate, usePath } from '@treenity/react/hooks';
6
+ import { cn } from '@treenity/react/lib/utils';
7
+ import { GenerateViewButton } from '@treenity/react/mods/editor-ui/default-view';
8
+ import { Button } from '@treenity/react/ui/button';
9
+ import {
10
+ DropdownMenu,
11
+ DropdownMenuContent,
12
+ DropdownMenuItem,
13
+ DropdownMenuTrigger,
14
+ } from '@treenity/react/ui/dropdown-menu';
15
+ import { GripVertical, Plus, X } from 'lucide-react';
16
+ import { useCallback, useEffect, useRef, useState } from 'react';
17
+ import GridLayout from 'react-grid-layout';
18
+ import 'react-grid-layout/css/styles.css';
19
+ import { Launcher } from './types';
20
+
21
+ type LauncherLayoutItem = { i: string; x: number; y: number; w: number; h: number; ctx?: string };
22
+
23
+ // Available rendering contexts for the selector
24
+ const CONTEXTS = ['auto', 'react', 'react:icon', 'react:widget', 'react:compact'] as const;
25
+
26
+ function contextLabel(ctx: string) {
27
+ if (ctx === 'auto') return 'Auto';
28
+ if (ctx === 'react') return 'Full';
29
+ if (ctx === 'react:icon') return 'Icon';
30
+ if (ctx === 'react:widget') return 'Widget';
31
+ if (ctx === 'react:compact') return 'Compact';
32
+ return ctx;
33
+ }
34
+
35
+ // Resolve which context to render based on layout item
36
+ function resolveContext(item: LauncherLayoutItem): string {
37
+ if (item.ctx && item.ctx !== 'auto') return item.ctx;
38
+ return item.w > 1 || item.h > 1 ? 'react:widget' : 'react:icon';
39
+ }
40
+
41
+ // ── Edit overlay — close + context dropdown menu ──
42
+
43
+ function EditOverlay({
44
+ ctx,
45
+ onRemove,
46
+ onContextChange,
47
+ }: {
48
+ ctx: string;
49
+ onRemove: () => void;
50
+ onContextChange: (ctx: string) => void;
51
+ }) {
52
+ return (
53
+ <>
54
+ <button
55
+ className="launcher-btn absolute -right-1 -top-1 z-10 flex h-5 w-5 items-center justify-center rounded-full bg-red-700 shadow-lg p-0"
56
+ onClick={(e) => { e.stopPropagation(); onRemove(); }}
57
+ onMouseDown={(e) => e.stopPropagation()}
58
+ >
59
+ <X className="h-3 w-3 text-white" />
60
+ </button>
61
+
62
+ <DropdownMenu>
63
+ <DropdownMenuTrigger asChild>
64
+ <button
65
+ className="launcher-btn absolute -left-1 -top-1 z-10 flex h-auto min-w-5 items-center justify-center rounded-full bg-blue-600 px-1.5 py-0.5 shadow-lg text-[9px] font-medium text-white"
66
+ onMouseDown={(e) => e.stopPropagation()}
67
+ >
68
+ {contextLabel(ctx)}
69
+ </button>
70
+ </DropdownMenuTrigger>
71
+ <DropdownMenuContent align="start" className="min-w-24">
72
+ {CONTEXTS.map((c) => (
73
+ <DropdownMenuItem
74
+ key={c}
75
+ className={cn('text-xs', c === ctx && 'font-bold')}
76
+ onSelect={() => onContextChange(c)}
77
+ >
78
+ {contextLabel(c)}
79
+ </DropdownMenuItem>
80
+ ))}
81
+ </DropdownMenuContent>
82
+ </DropdownMenu>
83
+ </>
84
+ );
85
+ }
86
+
87
+ // Stored ctx or 'auto' if none
88
+ function getStoredCtx(item: LauncherLayoutItem): string {
89
+ return item.ctx || 'auto';
90
+ }
91
+
92
+ // ── App item — resolves ref target and renders via context ──
93
+
94
+ function AppItem({
95
+ refNode,
96
+ context,
97
+ stored,
98
+ editing,
99
+ onRemove,
100
+ onContextChange,
101
+ }: {
102
+ refNode: NodeData;
103
+ context: string;
104
+ stored: string;
105
+ editing?: boolean;
106
+ onRemove?: () => void;
107
+ onContextChange?: (ctx: string) => void;
108
+ }) {
109
+ const targetPath = isRef(refNode) ? refNode.$ref : null;
110
+ const target = usePath(targetPath) as NodeData | undefined;
111
+ const navigate = useNavigate();
112
+
113
+ if (!target) {
114
+ return (
115
+ <div className="flex h-full w-full items-center justify-center rounded-2xl bg-zinc-700/50">
116
+ <span className="text-xs text-zinc-400">...</span>
117
+ </div>
118
+ );
119
+ }
120
+
121
+ const label = target.$path.split('/').at(-1) || '?';
122
+ const hasView = !!resolveExact(target.$type, context as 'react');
123
+
124
+ // No view registered for this context — offer AI generation
125
+ const renderContent = hasView
126
+ ? <RenderContext name={context}><Render value={target} /></RenderContext>
127
+ : <GenerateViewButton type={target.$type} sample={target} context={context} label="Make AI" />;
128
+
129
+ // Icon-style: centered icon + label
130
+ if (context === 'react:icon') {
131
+ return (
132
+ <div
133
+ className="relative flex h-full w-full cursor-pointer flex-col items-center justify-center gap-1 overflow-visible"
134
+ onClick={() => !editing && navigate(target.$path)}
135
+ >
136
+ {editing && onRemove && onContextChange && (
137
+ <EditOverlay ctx={stored} onRemove={onRemove} onContextChange={onContextChange} />
138
+ )}
139
+ <div className="h-14 w-14 shrink-0">
140
+ {renderContent}
141
+ </div>
142
+ <span className="max-w-16 truncate text-center text-[11px] font-medium text-white/90 drop-shadow-sm">
143
+ {label}
144
+ </span>
145
+ </div>
146
+ );
147
+ }
148
+
149
+ // Card-style: glass card with content
150
+ return (
151
+ <div
152
+ className="launcher-glass relative h-full w-full cursor-pointer overflow-visible rounded-2xl border border-white/10 bg-white/10 p-3"
153
+ onClick={() => !editing && navigate(target.$path)}
154
+ >
155
+ {editing && onRemove && onContextChange && (
156
+ <EditOverlay ctx={stored} onRemove={onRemove} onContextChange={onContextChange} />
157
+ )}
158
+ <div className="mb-1 text-[10px] font-semibold uppercase tracking-wider text-white/50">
159
+ {label}
160
+ </div>
161
+ <div className="h-[calc(100%-1.5rem)] overflow-hidden text-white">
162
+ {renderContent}
163
+ </div>
164
+ </div>
165
+ );
166
+ }
167
+
168
+ // ── Drop zone overlay ──
169
+
170
+ function DropZone({ visible }: { visible: boolean }) {
171
+ if (!visible) return null;
172
+ return (
173
+ <div className="pointer-events-none absolute inset-0 z-20 flex items-center justify-center rounded-2xl border-2 border-dashed border-blue-400/60 bg-blue-500/10 backdrop-blur-sm">
174
+ <div className="flex flex-col items-center gap-1 text-blue-300">
175
+ <Plus className="h-8 w-8" />
176
+ <span className="text-sm font-medium">Drop node here</span>
177
+ </div>
178
+ </div>
179
+ );
180
+ }
181
+
182
+ // ── Main Launcher View ──
183
+
184
+ const LauncherView: View<NodeData> = ({ value }) => {
185
+ const launcher = usePath(value.$path, Launcher);
186
+ const children = useChildren(value.$path, { watch: true, watchNew: true });
187
+
188
+ const [editing, setEditing] = useState(false);
189
+ const [dragOver, setDragOver] = useState(false);
190
+
191
+ // Measure container width for RGL
192
+ const containerRef = useRef<HTMLDivElement>(null);
193
+ const [gridWidth, setGridWidth] = useState(400);
194
+
195
+ useEffect(() => {
196
+ const el = containerRef.current;
197
+ if (!el) return;
198
+ const ro = new ResizeObserver((entries) => {
199
+ const w = entries[0]?.contentRect.width;
200
+ if (w && w > 0) setGridWidth(w);
201
+ });
202
+ ro.observe(el);
203
+ return () => ro.disconnect();
204
+ }, []);
205
+
206
+ // Parse layout
207
+ const columns = typeof launcher?.columns === 'number' ? launcher.columns : 4;
208
+ const wallpaper = typeof launcher?.wallpaper === 'string' ? launcher.wallpaper : '';
209
+ const layoutStr = typeof launcher?.layout === 'string' ? launcher.layout : '[]';
210
+
211
+ let layoutItems: LauncherLayoutItem[];
212
+ try {
213
+ layoutItems = JSON.parse(layoutStr);
214
+ } catch {
215
+ layoutItems = [];
216
+ }
217
+
218
+ // Build child map
219
+ const childMap = new Map<string, NodeData>();
220
+ for (const c of children) {
221
+ const id = c.$path.split('/').at(-1) || '';
222
+ childMap.set(id, c);
223
+ }
224
+
225
+ // Auto-generate layout for children without layout entries
226
+ const layoutIds = new Set(layoutItems.map((l) => l.i));
227
+ let nextY = layoutItems.reduce((m, l) => Math.max(m, l.y + l.h), 0);
228
+ let nextX = 0;
229
+
230
+ for (const c of children) {
231
+ const id = c.$path.split('/').at(-1) || '';
232
+ if (!layoutIds.has(id)) {
233
+ layoutItems.push({ i: id, x: nextX, y: nextY, w: 1, h: 1 });
234
+ nextX++;
235
+ if (nextX >= columns) {
236
+ nextX = 0;
237
+ nextY++;
238
+ }
239
+ }
240
+ }
241
+
242
+ const rowHeight = 90;
243
+
244
+ const persistLayout = useCallback(
245
+ (newLayout: readonly LauncherLayoutItem[]) => {
246
+ // Preserve ctx from current layoutItems
247
+ const ctxMap = new Map(layoutItems.map((l) => [l.i, l.ctx]));
248
+ const clean = newLayout.map(({ i, x, y, w, h }) => ({
249
+ i, x, y, w, h,
250
+ ...(ctxMap.get(i) ? { ctx: ctxMap.get(i) } : {}),
251
+ }));
252
+ launcher.updateLayout({ layout: JSON.stringify(clean) });
253
+ },
254
+ [launcher, layoutItems],
255
+ );
256
+
257
+ const handleRemove = (id: string) => {
258
+ launcher.removeApp({ id });
259
+ };
260
+
261
+ const handleContextChange = (id: string, ctx: string) => {
262
+ const updated = layoutItems.map((l) =>
263
+ l.i === id ? { ...l, ctx: ctx === 'auto' ? undefined : ctx } : l,
264
+ );
265
+ launcher.updateLayout({ layout: JSON.stringify(updated) });
266
+ };
267
+
268
+ // ── External drop from tree sidebar ──
269
+
270
+ const handleExternalDrop = useCallback(
271
+ (_layout: unknown, _item: unknown, e: Event) => {
272
+ const de = e as DragEvent;
273
+ const path = de.dataTransfer?.getData('application/treenity-path')
274
+ || de.dataTransfer?.getData('text/plain');
275
+ if (!path || !path.startsWith('/')) return;
276
+ launcher.addApp({ path });
277
+ setDragOver(false);
278
+ },
279
+ [value.$path],
280
+ );
281
+
282
+ // Container-level drop for when grid is empty or drop misses grid items
283
+ const handleContainerDragOver = useCallback((e: React.DragEvent) => {
284
+ if (e.dataTransfer.types.includes('application/treenity-path')) {
285
+ e.preventDefault();
286
+ setDragOver(true);
287
+ }
288
+ }, []);
289
+
290
+ const handleContainerDrop = useCallback(
291
+ (e: React.DragEvent) => {
292
+ e.preventDefault();
293
+ setDragOver(false);
294
+ const path = e.dataTransfer.getData('application/treenity-path')
295
+ || e.dataTransfer.getData('text/plain');
296
+ if (!path || !path.startsWith('/')) return;
297
+ launcher.addApp({ path });
298
+ },
299
+ [launcher],
300
+ );
301
+
302
+ return (
303
+ <div
304
+ className={cn(
305
+ 'view-full relative min-h-screen select-none overflow-auto',
306
+ editing && 'launcher-editing',
307
+ )}
308
+ style={{ background: wallpaper || undefined }}
309
+ onDragOver={handleContainerDragOver}
310
+ onDragLeave={() => setDragOver(false)}
311
+ onDrop={handleContainerDrop}
312
+ >
313
+ {/* Status bar */}
314
+ <div className="flex items-center justify-between px-5 pb-2 pt-3">
315
+ <span className="text-sm font-semibold text-white/80">Treenity</span>
316
+ <Button
317
+ size="sm"
318
+ variant="ghost"
319
+ className="rounded-full text-xs text-white/60"
320
+ onClick={(e) => {
321
+ e.stopPropagation();
322
+ setEditing(!editing);
323
+ }}
324
+ >
325
+ {editing ? 'Done' : 'Edit'}
326
+ </Button>
327
+ </div>
328
+
329
+ {/* Drop zone overlay */}
330
+ <DropZone visible={dragOver && children.length === 0} />
331
+
332
+ {/* Grid */}
333
+ <div className="launcher-grid px-3 relative" ref={containerRef}>
334
+ <GridLayout
335
+ layout={layoutItems}
336
+ width={gridWidth}
337
+ gridConfig={{
338
+ cols: columns,
339
+ rowHeight,
340
+ margin: [12, 12] as const,
341
+ containerPadding: null,
342
+ maxRows: Infinity,
343
+ }}
344
+ dragConfig={{ enabled: editing, handle: '.launcher-item', cancel: '.launcher-btn' }}
345
+ resizeConfig={{ enabled: editing }}
346
+ dropConfig={{
347
+ enabled: true,
348
+ defaultItem: { w: 1, h: 1 },
349
+ }}
350
+ onDragStop={(layout) => persistLayout(layout)}
351
+ onResizeStop={(layout) => persistLayout(layout)}
352
+ onDrop={handleExternalDrop}
353
+ >
354
+ {layoutItems.map((item) => {
355
+ const child = childMap.get(item.i);
356
+ if (!child) return <div key={item.i} />;
357
+
358
+ return (
359
+ <div key={item.i} className="launcher-item h-full overflow-visible">
360
+ <div
361
+ className={cn(
362
+ 'h-full overflow-visible transition-transform duration-300',
363
+ editing && 'launcher-edit-item',
364
+ )}
365
+ >
366
+ <AppItem
367
+ refNode={child}
368
+ context={resolveContext(item)}
369
+ stored={getStoredCtx(item)}
370
+ editing={editing}
371
+ onRemove={() => handleRemove(item.i)}
372
+ onContextChange={(ctx) => handleContextChange(item.i, ctx)}
373
+ />
374
+ </div>
375
+ </div>
376
+ );
377
+ })}
378
+ </GridLayout>
379
+
380
+ {/* Inline drop hint when grid has items */}
381
+ {dragOver && children.length > 0 && (
382
+ <div className="pointer-events-none absolute inset-x-0 bottom-0 flex justify-center py-4">
383
+ <div className="rounded-full bg-blue-500/30 border border-blue-400/50 px-4 py-2 text-sm text-blue-200 backdrop-blur-sm">
384
+ Drop to add to launcher
385
+ </div>
386
+ </div>
387
+ )}
388
+ </div>
389
+
390
+ {/* Empty state */}
391
+ {children.length === 0 && !dragOver && (
392
+ <div className="flex flex-col items-center justify-center gap-3 py-20 text-white/40">
393
+ <GripVertical className="h-12 w-12" />
394
+ <p className="text-sm">Drag nodes from the tree to add them here</p>
395
+ </div>
396
+ )}
397
+ </div>
398
+ );
399
+ };
400
+
401
+ register('launcher', 'react', LauncherView);
@@ -0,0 +1,171 @@
1
+ // Launcher widgets — react:widget context for compact live views on the home screen
2
+
3
+ import { type NodeData, register } from '@treenity/core';
4
+ import type { RenderProps } from '@treenity/react/context';
5
+ import { useChildren, usePath } from '@treenity/react/hooks';
6
+ import { cn } from '@treenity/react/lib/utils';
7
+ import type { FC } from 'react';
8
+ import { TodoItem } from '../todo/types';
9
+
10
+ const KanbanWidget: FC<RenderProps> = ({ value }) => {
11
+ const node = value as NodeData;
12
+ return <KanbanWidgetInner path={node.$path} />;
13
+ };
14
+
15
+ // Separate component to use hooks properly per column
16
+ function KanbanWidgetInner({ path }: { path: string }) {
17
+ const columns = useChildren(path, { watch: true, watchNew: true })
18
+ .filter(c => c.$type === 'board.column')
19
+ .sort((a, b) => ((a as any).order ?? 0) - ((b as any).order ?? 0));
20
+
21
+ return (
22
+ <div className="flex h-full flex-col justify-between gap-1">
23
+ {columns.map(col => (
24
+ <ColumnRow key={col.$path} col={col} />
25
+ ))}
26
+ </div>
27
+ );
28
+ }
29
+
30
+ function ColumnRow({ col }: { col: NodeData }) {
31
+ const tasks = useChildren(col.$path, { watch: true, watchNew: true });
32
+ const label = typeof col.label === 'string' ? col.label : col.$path.split('/').at(-1) || '?';
33
+ const color = typeof col.color === 'string' ? col.color : 'border-zinc-400';
34
+ // Extract bg color from border color class
35
+ const dotColor = color.replace('border-', 'bg-');
36
+
37
+ return (
38
+ <div className="flex items-center gap-2">
39
+ <span className={cn('h-2 w-2 shrink-0 rounded-full', dotColor)} />
40
+ <span className="flex-1 truncate text-[11px] text-white/70">{label}</span>
41
+ <span className="text-xs font-semibold text-white/90">{tasks.length}</span>
42
+ </div>
43
+ );
44
+ }
45
+
46
+ register('board.kanban', 'react:widget', KanbanWidget as any);
47
+
48
+ // ── todo.list widget — compact checklist ──
49
+
50
+ const TodoWidget: FC<RenderProps> = ({ value }) => {
51
+ const node = value as NodeData;
52
+ // value might be the todo dir — find the list child
53
+ const children = useChildren(node.$path, { watch: true, watchNew: true });
54
+ const list = children.find(c => c.$type === 'todo.list');
55
+
56
+ if (list) return <TodoListItems path={list.$path} />;
57
+
58
+ // value IS the list
59
+ return <TodoListItems path={node.$path} />;
60
+ };
61
+
62
+ function TodoListItems({ path }: { path: string }) {
63
+ const items = useChildren(path, { watch: true, watchNew: true });
64
+ const doneCount = items.filter(i => i.done).length;
65
+
66
+ return (
67
+ <div className="flex h-full flex-col">
68
+ <div className="mb-1 text-[10px] text-white/50">
69
+ {doneCount}/{items.length} done
70
+ </div>
71
+
72
+ <div className="flex flex-1 flex-col gap-0.5 overflow-hidden">
73
+ {items.slice(0, 5).map(item => (
74
+ <TodoItemCompact key={item.$path} value={item} />
75
+ ))}
76
+ {items.length > 5 && (
77
+ <span className="text-[10px] text-white/40">+{items.length - 5} more</span>
78
+ )}
79
+ </div>
80
+ </div>
81
+ );
82
+ }
83
+
84
+ function TodoItemCompact({ value }: { value: NodeData }) {
85
+ const item = usePath(value.$path, TodoItem);
86
+
87
+ return (
88
+ <div className="flex items-center gap-1.5">
89
+ <span className={cn(
90
+ 'flex h-3 w-3 shrink-0 items-center justify-center rounded-sm border text-[8px]',
91
+ item.done
92
+ ? 'border-green-400/50 bg-green-500/30 text-green-300'
93
+ : 'border-white/20',
94
+ )}>
95
+ {item.done ? '✓' : ''}
96
+ </span>
97
+ <span className={cn(
98
+ 'truncate text-[11px]',
99
+ item.done ? 'text-white/30 line-through' : 'text-white/80',
100
+ )}>
101
+ {item.title}
102
+ </span>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ register('todo.list', 'react:widget', TodoWidget as any);
108
+
109
+ // ── examples.demo.sensor widget — last value + mini bars ──
110
+
111
+ const SensorWidget: FC<RenderProps> = ({ value }) => {
112
+ const node = value as NodeData;
113
+ const children = useChildren(node.$path, { watch: true, watchNew: true });
114
+ const last5 = children.slice(-5);
115
+ const latest = last5[last5.length - 1];
116
+
117
+ const latestVal = typeof latest?.value === 'number' ? latest.value : null;
118
+ const latestTs = typeof latest?.ts === 'number' ? new Date(latest.ts).toLocaleTimeString() : '--:--';
119
+
120
+ return (
121
+ <div className="flex h-full items-center gap-3">
122
+ {/* Big number */}
123
+ <div className="flex flex-col">
124
+ <span className="text-2xl font-bold tabular-nums text-white/90">
125
+ {latestVal !== null ? `${latestVal}°` : '—'}
126
+ </span>
127
+ <span className="text-[9px] text-white/40">{latestTs}</span>
128
+ </div>
129
+
130
+ {/* Mini bars */}
131
+ <div className="flex flex-1 items-end gap-0.5 self-end pb-1">
132
+ {last5.map((n, i) => {
133
+ const val = typeof n.value === 'number' ? n.value : 20;
134
+ const h = Math.max(4, Math.round(((val - 15) / 15) * 32));
135
+ return (
136
+ <div
137
+ key={n.$path}
138
+ className={cn(
139
+ 'flex-1 rounded-sm',
140
+ i === last5.length - 1 ? 'bg-emerald-400' : 'bg-emerald-400/40',
141
+ )}
142
+ style={{ height: h }}
143
+ />
144
+ );
145
+ })}
146
+ {last5.length === 0 && (
147
+ <span className="text-[10px] text-white/30">No data</span>
148
+ )}
149
+ </div>
150
+ </div>
151
+ );
152
+ };
153
+
154
+ register('examples.demo.sensor', 'react:widget', SensorWidget as any);
155
+
156
+ // ── Default widget fallback — type + icon ──
157
+
158
+ const DefaultWidget: FC<RenderProps> = ({ value }) => {
159
+ const node = value as NodeData;
160
+ const typeParts = node.$type.split('.');
161
+ const name = typeParts[typeParts.length - 1] || node.$type;
162
+
163
+ return (
164
+ <div className="flex h-full flex-col items-center justify-center text-white/50">
165
+ <span className="text-lg font-bold capitalize">{name}</span>
166
+ <span className="text-[10px]">{node.$path}</span>
167
+ </div>
168
+ );
169
+ };
170
+
171
+ register('default', 'react:widget', DefaultWidget as any);