@treenity/react 3.0.0 → 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 (369) hide show
  1. package/README.md +91 -0
  2. package/dist/AclEditor.d.ts +1 -1
  3. package/dist/AclEditor.d.ts.map +1 -1
  4. package/dist/AclEditor.js +5 -5
  5. package/dist/AclEditor.js.map +1 -1
  6. package/dist/ActionCards.d.ts +9 -0
  7. package/dist/ActionCards.d.ts.map +1 -0
  8. package/dist/ActionCards.js +96 -0
  9. package/dist/ActionCards.js.map +1 -0
  10. package/dist/App.d.ts.map +1 -1
  11. package/dist/App.js +71 -185
  12. package/dist/App.js.map +1 -1
  13. package/dist/ComponentSection.d.ts +15 -0
  14. package/dist/ComponentSection.d.ts.map +1 -0
  15. package/dist/ComponentSection.js +25 -0
  16. package/dist/ComponentSection.js.map +1 -0
  17. package/dist/ErrorBoundary.d.ts +18 -0
  18. package/dist/ErrorBoundary.d.ts.map +1 -0
  19. package/dist/ErrorBoundary.js +17 -0
  20. package/dist/ErrorBoundary.js.map +1 -0
  21. package/dist/Inspector.d.ts +1 -0
  22. package/dist/Inspector.d.ts.map +1 -1
  23. package/dist/Inspector.js +22 -347
  24. package/dist/Inspector.js.map +1 -1
  25. package/dist/Login.d.ts +8 -0
  26. package/dist/Login.d.ts.map +1 -0
  27. package/dist/Login.js +45 -0
  28. package/dist/Login.js.map +1 -0
  29. package/dist/NodeEditor.d.ts +11 -0
  30. package/dist/NodeEditor.d.ts.map +1 -0
  31. package/dist/NodeEditor.js +157 -0
  32. package/dist/NodeEditor.js.map +1 -0
  33. package/dist/Tree.d.ts +1 -0
  34. package/dist/Tree.d.ts.map +1 -1
  35. package/dist/Tree.js +8 -27
  36. package/dist/Tree.js.map +1 -1
  37. package/dist/bind/engine.js +1 -1
  38. package/dist/bind/engine.js.map +1 -1
  39. package/dist/bind/eval.d.ts +1 -1
  40. package/dist/bind/eval.d.ts.map +1 -1
  41. package/dist/bind/hook.d.ts +1 -1
  42. package/dist/bind/hook.d.ts.map +1 -1
  43. package/dist/bind/hook.js +1 -1
  44. package/dist/bind/hook.js.map +1 -1
  45. package/dist/cache.d.ts +1 -1
  46. package/dist/cache.d.ts.map +1 -1
  47. package/dist/cache.js +4 -0
  48. package/dist/cache.js.map +1 -1
  49. package/dist/client-tree.d.ts +1 -2
  50. package/dist/client-tree.d.ts.map +1 -1
  51. package/dist/client-tree.js +12 -5
  52. package/dist/client-tree.js.map +1 -1
  53. package/dist/client.d.ts +1 -1
  54. package/dist/client.d.ts.map +1 -1
  55. package/dist/client.js +2 -4
  56. package/dist/client.js.map +1 -1
  57. package/dist/components/ConfirmDialog.d.ts +9 -0
  58. package/dist/components/ConfirmDialog.d.ts.map +1 -0
  59. package/dist/components/ConfirmDialog.js +6 -0
  60. package/dist/components/ConfirmDialog.js.map +1 -0
  61. package/dist/components/ConfirmPopover.d.ts +8 -0
  62. package/dist/components/ConfirmPopover.d.ts.map +1 -0
  63. package/dist/components/ConfirmPopover.js +9 -0
  64. package/dist/components/ConfirmPopover.js.map +1 -0
  65. package/dist/components/PathBreadcrumb.d.ts +5 -0
  66. package/dist/components/PathBreadcrumb.d.ts.map +1 -0
  67. package/dist/components/PathBreadcrumb.js +16 -0
  68. package/dist/components/PathBreadcrumb.js.map +1 -0
  69. package/dist/components/lib/utils.d.ts +3 -0
  70. package/dist/components/lib/utils.d.ts.map +1 -0
  71. package/dist/components/lib/utils.js +6 -0
  72. package/dist/components/lib/utils.js.map +1 -0
  73. package/dist/components/ui/accordion.js +1 -1
  74. package/dist/components/ui/accordion.js.map +1 -1
  75. package/dist/components/ui/alert-dialog.d.ts +19 -0
  76. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  77. package/dist/components/ui/alert-dialog.js +42 -0
  78. package/dist/components/ui/alert-dialog.js.map +1 -0
  79. package/dist/components/ui/badge.js +1 -1
  80. package/dist/components/ui/badge.js.map +1 -1
  81. package/dist/components/ui/breadcrumb.d.ts +12 -0
  82. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  83. package/dist/components/ui/breadcrumb.js +28 -0
  84. package/dist/components/ui/breadcrumb.js.map +1 -0
  85. package/dist/components/ui/button.d.ts +9 -8
  86. package/dist/components/ui/button.d.ts.map +1 -1
  87. package/dist/components/ui/button.js +26 -21
  88. package/dist/components/ui/button.js.map +1 -1
  89. package/dist/components/ui/card.d.ts +10 -0
  90. package/dist/components/ui/card.d.ts.map +1 -0
  91. package/dist/components/ui/card.js +25 -0
  92. package/dist/components/ui/card.js.map +1 -0
  93. package/dist/components/ui/checkbox.js +1 -1
  94. package/dist/components/ui/checkbox.js.map +1 -1
  95. package/dist/components/ui/collapsible.d.ts +6 -0
  96. package/dist/components/ui/collapsible.d.ts.map +1 -0
  97. package/dist/components/ui/collapsible.js +13 -0
  98. package/dist/components/ui/collapsible.js.map +1 -0
  99. package/dist/components/ui/command.d.ts +19 -0
  100. package/dist/components/ui/command.d.ts.map +1 -0
  101. package/dist/components/ui/command.js +35 -0
  102. package/dist/components/ui/command.js.map +1 -0
  103. package/dist/components/ui/dialog.d.ts.map +1 -1
  104. package/dist/components/ui/dialog.js +1 -1
  105. package/dist/components/ui/dialog.js.map +1 -1
  106. package/dist/components/ui/drawer.js +1 -1
  107. package/dist/components/ui/drawer.js.map +1 -1
  108. package/dist/components/ui/dropdown-menu.d.ts +26 -0
  109. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  110. package/dist/components/ui/dropdown-menu.js +52 -0
  111. package/dist/components/ui/dropdown-menu.js.map +1 -0
  112. package/dist/components/ui/form-field.d.ts +7 -0
  113. package/dist/components/ui/form-field.d.ts.map +1 -0
  114. package/dist/components/ui/form-field.js +17 -0
  115. package/dist/components/ui/form-field.js.map +1 -0
  116. package/dist/components/ui/input.js +1 -1
  117. package/dist/components/ui/input.js.map +1 -1
  118. package/dist/components/ui/label.js +1 -1
  119. package/dist/components/ui/label.js.map +1 -1
  120. package/dist/components/ui/pagination.d.ts +14 -0
  121. package/dist/components/ui/pagination.d.ts.map +1 -0
  122. package/dist/components/ui/pagination.js +30 -0
  123. package/dist/components/ui/pagination.js.map +1 -0
  124. package/dist/components/ui/popover.js +2 -2
  125. package/dist/components/ui/popover.js.map +1 -1
  126. package/dist/components/ui/progress.js +1 -1
  127. package/dist/components/ui/progress.js.map +1 -1
  128. package/dist/components/ui/resizable.d.ts +8 -0
  129. package/dist/components/ui/resizable.d.ts.map +1 -0
  130. package/dist/components/ui/resizable.js +14 -0
  131. package/dist/components/ui/resizable.js.map +1 -0
  132. package/dist/components/ui/scroll-area.d.ts +6 -0
  133. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  134. package/dist/components/ui/scroll-area.js +13 -0
  135. package/dist/components/ui/scroll-area.js.map +1 -0
  136. package/dist/components/ui/select.js +1 -1
  137. package/dist/components/ui/select.js.map +1 -1
  138. package/dist/components/ui/separator.d.ts +5 -0
  139. package/dist/components/ui/separator.d.ts.map +1 -0
  140. package/dist/components/ui/separator.js +9 -0
  141. package/dist/components/ui/separator.js.map +1 -0
  142. package/dist/components/ui/sheet.d.ts +15 -0
  143. package/dist/components/ui/sheet.d.ts.map +1 -0
  144. package/dist/components/ui/sheet.js +40 -0
  145. package/dist/components/ui/sheet.js.map +1 -0
  146. package/dist/components/ui/skeleton.d.ts +3 -0
  147. package/dist/components/ui/skeleton.d.ts.map +1 -0
  148. package/dist/components/ui/skeleton.js +7 -0
  149. package/dist/components/ui/skeleton.js.map +1 -0
  150. package/dist/components/ui/slider.js +1 -1
  151. package/dist/components/ui/slider.js.map +1 -1
  152. package/dist/components/ui/switch.js +1 -1
  153. package/dist/components/ui/switch.js.map +1 -1
  154. package/dist/components/ui/table.d.ts +11 -0
  155. package/dist/components/ui/table.d.ts.map +1 -0
  156. package/dist/components/ui/table.js +29 -0
  157. package/dist/components/ui/table.js.map +1 -0
  158. package/dist/components/ui/tabs.d.ts +12 -0
  159. package/dist/components/ui/tabs.d.ts.map +1 -0
  160. package/dist/components/ui/tabs.js +29 -0
  161. package/dist/components/ui/tabs.js.map +1 -0
  162. package/dist/components/ui/textarea.js +1 -1
  163. package/dist/components/ui/textarea.js.map +1 -1
  164. package/dist/components/ui/toggle-group.d.ts +10 -0
  165. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  166. package/dist/components/ui/toggle-group.js +23 -0
  167. package/dist/components/ui/toggle-group.js.map +1 -0
  168. package/dist/components/ui/toggle.d.ts +10 -0
  169. package/dist/components/ui/toggle.d.ts.map +1 -0
  170. package/dist/components/ui/toggle.js +27 -0
  171. package/dist/components/ui/toggle.js.map +1 -0
  172. package/dist/components/ui/tooltip.js +1 -1
  173. package/dist/components/ui/tooltip.js.map +1 -1
  174. package/dist/context/index.d.ts +27 -10
  175. package/dist/context/index.d.ts.map +1 -1
  176. package/dist/context/index.js +43 -36
  177. package/dist/context/index.js.map +1 -1
  178. package/dist/events.d.ts +10 -0
  179. package/dist/events.d.ts.map +1 -0
  180. package/dist/events.js +78 -0
  181. package/dist/events.js.map +1 -0
  182. package/dist/fiber-tree.d.ts +3 -0
  183. package/dist/fiber-tree.d.ts.map +1 -0
  184. package/dist/fiber-tree.js +93 -0
  185. package/dist/fiber-tree.js.map +1 -0
  186. package/dist/hooks.d.ts +5 -2
  187. package/dist/hooks.d.ts.map +1 -1
  188. package/dist/hooks.js +66 -6
  189. package/dist/hooks.js.map +1 -1
  190. package/dist/idb.d.ts +1 -1
  191. package/dist/idb.d.ts.map +1 -1
  192. package/dist/lib/to-plain.d.ts +2 -0
  193. package/dist/lib/to-plain.d.ts.map +1 -0
  194. package/dist/lib/to-plain.js +21 -0
  195. package/dist/lib/to-plain.js.map +1 -0
  196. package/dist/main.d.ts +1 -1
  197. package/dist/main.d.ts.map +1 -1
  198. package/dist/main.js +4 -3
  199. package/dist/main.js.map +1 -1
  200. package/dist/mods/clients.d.ts +3 -0
  201. package/dist/mods/clients.d.ts.map +1 -0
  202. package/dist/mods/clients.js +4 -0
  203. package/dist/mods/clients.js.map +1 -0
  204. package/dist/mods/editor-ui/FieldLabel.d.ts +15 -0
  205. package/dist/mods/editor-ui/FieldLabel.d.ts.map +1 -0
  206. package/dist/mods/editor-ui/FieldLabel.js +55 -0
  207. package/dist/mods/editor-ui/FieldLabel.js.map +1 -0
  208. package/dist/mods/editor-ui/client.d.ts +1 -1
  209. package/dist/mods/editor-ui/client.d.ts.map +1 -1
  210. package/dist/mods/editor-ui/client.js +1 -1
  211. package/dist/mods/editor-ui/client.js.map +1 -1
  212. package/dist/mods/editor-ui/default-edit.d.ts +2 -0
  213. package/dist/mods/editor-ui/default-edit.d.ts.map +1 -0
  214. package/dist/mods/editor-ui/default-edit.js +54 -0
  215. package/dist/mods/editor-ui/default-edit.js.map +1 -0
  216. package/dist/mods/editor-ui/default-view.d.ts +8 -1
  217. package/dist/mods/editor-ui/default-view.d.ts.map +1 -1
  218. package/dist/mods/editor-ui/default-view.js +8 -5
  219. package/dist/mods/editor-ui/default-view.js.map +1 -1
  220. package/dist/mods/editor-ui/dir-view.js +0 -2
  221. package/dist/mods/editor-ui/dir-view.js.map +1 -1
  222. package/dist/mods/editor-ui/empty-placeholder.d.ts +5 -0
  223. package/dist/mods/editor-ui/empty-placeholder.d.ts.map +1 -0
  224. package/dist/mods/editor-ui/empty-placeholder.js +14 -0
  225. package/dist/mods/editor-ui/empty-placeholder.js.map +1 -0
  226. package/dist/mods/editor-ui/form-field.d.ts +17 -0
  227. package/dist/mods/editor-ui/form-field.d.ts.map +1 -0
  228. package/dist/mods/editor-ui/form-field.js +68 -0
  229. package/dist/mods/editor-ui/form-field.js.map +1 -0
  230. package/dist/mods/editor-ui/form-fields.d.ts +1 -2
  231. package/dist/mods/editor-ui/form-fields.d.ts.map +1 -1
  232. package/dist/mods/editor-ui/form-fields.js +56 -60
  233. package/dist/mods/editor-ui/form-fields.js.map +1 -1
  234. package/dist/mods/editor-ui/layout-view.js +3 -2
  235. package/dist/mods/editor-ui/layout-view.js.map +1 -1
  236. package/dist/mods/editor-ui/list-items.js +1 -1
  237. package/dist/mods/editor-ui/list-items.js.map +1 -1
  238. package/dist/mods/editor-ui/node-utils.d.ts +2 -2
  239. package/dist/mods/editor-ui/node-utils.d.ts.map +1 -1
  240. package/dist/mods/editor-ui/node-utils.js +4 -5
  241. package/dist/mods/editor-ui/node-utils.js.map +1 -1
  242. package/dist/mods/editor-ui/type-picker.d.ts +15 -0
  243. package/dist/mods/editor-ui/type-picker.d.ts.map +1 -0
  244. package/dist/mods/editor-ui/type-picker.js +69 -0
  245. package/dist/mods/editor-ui/type-picker.js.map +1 -0
  246. package/dist/mods/editor-ui/user-view.js +1 -1
  247. package/dist/mods/editor-ui/user-view.js.map +1 -1
  248. package/dist/mods/servers.d.ts +1 -0
  249. package/dist/mods/servers.d.ts.map +1 -0
  250. package/dist/mods/servers.js +4 -0
  251. package/dist/mods/servers.js.map +1 -0
  252. package/dist/mods/treenity/groups/index.js +1 -1
  253. package/dist/mods/treenity/groups/index.js.map +1 -1
  254. package/dist/mods/treenity/preview.js +1 -1
  255. package/dist/mods/treenity/preview.js.map +1 -1
  256. package/dist/mods/treenity/ref-view.js +1 -1
  257. package/dist/mods/treenity/ref-view.js.map +1 -1
  258. package/dist/mods/treenity/schema-form.js +1 -1
  259. package/dist/mods/treenity/schema-form.js.map +1 -1
  260. package/dist/mods/treenity/seed.js +1 -1
  261. package/dist/mods/treenity/seed.js.map +1 -1
  262. package/dist/mods/treenity/type-view.js +1 -1
  263. package/dist/mods/treenity/type-view.js.map +1 -1
  264. package/dist/schema-loader.d.ts +1 -1
  265. package/dist/schema-loader.d.ts.map +1 -1
  266. package/dist/schema-loader.js +1 -1
  267. package/dist/schema-loader.js.map +1 -1
  268. package/dist/symbols.d.ts +5 -0
  269. package/dist/symbols.d.ts.map +1 -0
  270. package/dist/symbols.js +16 -0
  271. package/dist/symbols.js.map +1 -0
  272. package/dist/trpc.d.ts +10 -3
  273. package/dist/trpc.d.ts.map +1 -1
  274. package/package.json +74 -8
  275. package/src/AclEditor.tsx +11 -18
  276. package/src/ActionCards.tsx +224 -0
  277. package/src/App.tsx +204 -385
  278. package/src/ComponentSection.tsx +113 -0
  279. package/src/ErrorBoundary.tsx +37 -0
  280. package/src/Inspector.css +54 -0
  281. package/src/Inspector.tsx +73 -793
  282. package/src/Login.tsx +97 -0
  283. package/src/NodeEditor.tsx +300 -0
  284. package/src/Tree.css +91 -0
  285. package/src/Tree.tsx +40 -43
  286. package/src/bind/bind.test.ts +1 -1
  287. package/src/bind/engine.ts +1 -1
  288. package/src/bind/eval.ts +1 -1
  289. package/src/bind/hook.ts +1 -1
  290. package/src/bind/pipes.ts +1 -1
  291. package/src/cache.ts +5 -1
  292. package/src/client-tree.test.ts +1 -1
  293. package/src/client-tree.ts +22 -16
  294. package/src/client.ts +2 -4
  295. package/src/components/ConfirmDialog.tsx +34 -0
  296. package/src/components/ConfirmPopover.tsx +41 -0
  297. package/src/components/PathBreadcrumb.tsx +36 -0
  298. package/src/components/lib/utils.ts +6 -0
  299. package/src/components/lib/utils.ts.bak +6 -0
  300. package/src/components/ui/accordion.tsx +1 -1
  301. package/src/components/ui/alert-dialog.tsx +189 -0
  302. package/src/components/ui/badge.tsx +1 -1
  303. package/src/components/ui/breadcrumb.tsx +108 -0
  304. package/src/components/ui/button.tsx +53 -31
  305. package/src/components/ui/card.tsx +91 -0
  306. package/src/components/ui/checkbox.tsx +1 -1
  307. package/src/components/ui/collapsible.tsx +31 -0
  308. package/src/components/ui/command.tsx +177 -0
  309. package/src/components/ui/dialog.tsx +1 -2
  310. package/src/components/ui/drawer.tsx +1 -1
  311. package/src/components/ui/dropdown-menu.tsx +256 -0
  312. package/src/components/ui/form-field.tsx +37 -0
  313. package/src/components/ui/input.tsx +1 -1
  314. package/src/components/ui/label.tsx +1 -1
  315. package/src/components/ui/pagination.tsx +127 -0
  316. package/src/components/ui/popover.tsx +2 -2
  317. package/src/components/ui/progress.tsx +1 -1
  318. package/src/components/ui/resizable.tsx +47 -0
  319. package/src/components/ui/scroll-area.tsx +55 -0
  320. package/src/components/ui/select.tsx +1 -1
  321. package/src/components/ui/separator.tsx +27 -0
  322. package/src/components/ui/sheet.tsx +140 -0
  323. package/src/components/ui/skeleton.tsx +13 -0
  324. package/src/components/ui/slider.tsx +1 -1
  325. package/src/components/ui/switch.tsx +1 -1
  326. package/src/components/ui/table.tsx +115 -0
  327. package/src/components/ui/tabs.tsx +88 -0
  328. package/src/components/ui/textarea.tsx +1 -1
  329. package/src/components/ui/toggle-group.tsx +82 -0
  330. package/src/components/ui/toggle.tsx +46 -0
  331. package/src/components/ui/tooltip.tsx +1 -1
  332. package/src/context/index.tsx +75 -42
  333. package/src/events.ts +81 -0
  334. package/src/fiber-tree.ts +112 -0
  335. package/src/hooks.ts +88 -9
  336. package/src/idb.ts +1 -1
  337. package/src/lib/to-plain.ts +21 -0
  338. package/src/main.tsx +3 -1
  339. package/src/mods/clients.ts +3 -0
  340. package/src/mods/editor-ui/FieldLabel.tsx +124 -0
  341. package/src/mods/editor-ui/client.ts +1 -1
  342. package/src/mods/editor-ui/default-edit.tsx +99 -0
  343. package/src/mods/editor-ui/default-view.tsx +13 -8
  344. package/src/mods/editor-ui/dir-view.tsx +2 -2
  345. package/src/mods/editor-ui/editor-ui.css +174 -0
  346. package/src/mods/editor-ui/empty-placeholder.tsx +39 -0
  347. package/src/mods/editor-ui/form-field.tsx +144 -0
  348. package/src/mods/editor-ui/form-fields.tsx +132 -113
  349. package/src/mods/editor-ui/layout-view.tsx +4 -2
  350. package/src/mods/editor-ui/list-items.tsx +2 -2
  351. package/src/mods/editor-ui/node-utils.ts +4 -5
  352. package/src/mods/editor-ui/type-picker.tsx +147 -0
  353. package/src/mods/editor-ui/user-view.tsx +1 -1
  354. package/src/mods/servers.ts +2 -0
  355. package/src/mods/treenity/groups/index.tsx +1 -1
  356. package/src/mods/treenity/preview.tsx +1 -1
  357. package/src/mods/treenity/ref-view.tsx +1 -1
  358. package/src/mods/treenity/schema-form.tsx +1 -1
  359. package/src/mods/treenity/seed.ts +1 -1
  360. package/src/mods/treenity/type-view.tsx +1 -1
  361. package/src/optimistic.test.ts +111 -0
  362. package/src/remote-tree.test.ts +1 -1
  363. package/src/remote-tree.ts +1 -1
  364. package/src/root.css +117 -0
  365. package/src/schema-loader.ts +1 -1
  366. package/src/symbols.ts +18 -0
  367. package/src/index.html +0 -14
  368. package/src/style.css +0 -1269
  369. package/src/vite-env.d.ts +0 -3
@@ -0,0 +1,147 @@
1
+ import { Button } from '#components/ui/button';
2
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '#components/ui/command';
3
+ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '#components/ui/dialog';
4
+ import { trpc } from '#trpc';
5
+ import { isOfType, type NodeData } from '@treenity/core';
6
+ import { useEffect, useRef, useState } from 'react';
7
+
8
+ export type TypeInfo = { type: string; label: string; description: string };
9
+
10
+ export async function loadTypes(): Promise<TypeInfo[]> {
11
+ const { items } = (await trpc.getChildren.query({ path: '/sys/types', limit: 0, depth: 99 })) as {
12
+ items: NodeData[];
13
+ total: number;
14
+ };
15
+ return items
16
+ .filter((n) => isOfType(n, 'type'))
17
+ .map((n) => {
18
+ const schema = n.schema as { $type: string; title?: string; description?: string } | undefined;
19
+ const typeName = n.$path.slice('/sys/types/'.length).replace(/\//g, '.');
20
+ return {
21
+ type: typeName,
22
+ label: schema?.title ?? typeName,
23
+ description: schema?.description ?? '',
24
+ };
25
+ });
26
+ }
27
+
28
+ function groupByNamespace(types: TypeInfo[]): Map<string, TypeInfo[]> {
29
+ const groups = new Map<string, TypeInfo[]>();
30
+ for (const t of types) {
31
+ const dotIdx = t.type.indexOf('.');
32
+ const ns = dotIdx === -1 ? 'core' : t.type.slice(0, dotIdx);
33
+ if (!groups.has(ns)) groups.set(ns, []);
34
+ groups.get(ns)!.push(t);
35
+ }
36
+ return groups;
37
+ }
38
+
39
+ export function TypePicker({
40
+ onSelect,
41
+ onCancel,
42
+ title = 'Create Node',
43
+ nameLabel = 'Node name',
44
+ action = 'Create',
45
+ autoName = false,
46
+ }: {
47
+ onSelect: (name: string, type: string) => void;
48
+ onCancel: () => void;
49
+ title?: string;
50
+ nameLabel?: string;
51
+ action?: string;
52
+ autoName?: boolean;
53
+ }) {
54
+ const [types, setTypes] = useState<TypeInfo[]>([]);
55
+ const [loading, setLoading] = useState(true);
56
+ const [error, setError] = useState<string | null>(null);
57
+ const [name, setName] = useState('');
58
+ const [nameManual, setNameManual] = useState(false);
59
+ const [selectedType, setSelectedType] = useState<string | null>(null);
60
+ const nameRef = useRef<HTMLInputElement>(null);
61
+
62
+ useEffect(() => {
63
+ loadTypes()
64
+ .then(setTypes)
65
+ .catch((err) => {
66
+ console.error('Failed to load types:', err);
67
+ setError('Failed to load types');
68
+ })
69
+ .finally(() => setLoading(false));
70
+ }, []);
71
+
72
+ function handleSelectType(type: string) {
73
+ setSelectedType(type);
74
+ if (autoName && !nameManual) {
75
+ const lastSegment = type.includes('.') ? type.slice(type.lastIndexOf('.') + 1) : type;
76
+ setName(lastSegment);
77
+ }
78
+ requestAnimationFrame(() => nameRef.current?.focus());
79
+ }
80
+
81
+ function handleSubmit() {
82
+ if (name && selectedType) onSelect(name, selectedType);
83
+ }
84
+
85
+ const groups = groupByNamespace(types);
86
+
87
+ return (
88
+ <Dialog open onOpenChange={(open) => { if (!open) onCancel(); }}>
89
+ <DialogContent className="p-0 gap-0 max-w-[380px]" showCloseButton={false}>
90
+ <DialogHeader className="px-4 pt-4 pb-2">
91
+ <DialogTitle className="text-[15px]">{title}</DialogTitle>
92
+ </DialogHeader>
93
+
94
+ <Command className="rounded-none border-none" shouldFilter>
95
+ <CommandInput placeholder="Search types..." />
96
+ <CommandList className="max-h-[280px]">
97
+ {loading && <div className="p-3 text-muted-foreground text-[13px]">Loading types...</div>}
98
+ {error && <div className="p-3 text-destructive text-[13px]">{error}</div>}
99
+ <CommandEmpty>No types found</CommandEmpty>
100
+ {[...groups.entries()].map(([ns, items]) => (
101
+ <CommandGroup key={ns} heading={ns}>
102
+ {items.map((t) => (
103
+ <CommandItem
104
+ key={t.type}
105
+ value={`${t.type} ${t.label} ${t.description}`}
106
+ onSelect={() => handleSelectType(t.type)}
107
+ className={selectedType === t.type ? 'bg-accent text-accent-foreground' : ''}
108
+ >
109
+ <div className="flex flex-col gap-0.5">
110
+ <div className="flex items-center gap-2">
111
+ <span className="font-mono text-[13px]">{t.type}</span>
112
+ {t.label !== t.type && <span className="text-muted-foreground text-[12px]">{t.label}</span>}
113
+ </div>
114
+ {t.description && (
115
+ <span className="text-[11px] text-muted-foreground/60 leading-tight">{t.description}</span>
116
+ )}
117
+ </div>
118
+ </CommandItem>
119
+ ))}
120
+ </CommandGroup>
121
+ ))}
122
+ </CommandList>
123
+ </Command>
124
+
125
+ <div className="px-4 py-3 border-t border-border">
126
+ <input
127
+ ref={nameRef}
128
+ className="w-full"
129
+ placeholder={nameLabel}
130
+ value={name}
131
+ onChange={(e) => { setName(e.target.value); setNameManual(true); }}
132
+ onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleSubmit(); } }}
133
+ />
134
+ </div>
135
+
136
+ <DialogFooter className="px-4 pb-4">
137
+ <Button variant="outline" onClick={onCancel}>Cancel</Button>
138
+ <Button disabled={!name || !selectedType} onClick={handleSubmit}>
139
+ {action}
140
+ {name ? ` "${name}"` : ''}
141
+ {selectedType ? ` as ${selectedType}` : ''}
142
+ </Button>
143
+ </DialogFooter>
144
+ </DialogContent>
145
+ </Dialog>
146
+ );
147
+ }
@@ -1,4 +1,4 @@
1
- import { type NodeData, register } from '@treenity/core/core';
1
+ import { type NodeData, register } from '@treenity/core';
2
2
  import { pathName } from './list-items';
3
3
  import { getComponents, getSchema } from './node-utils';
4
4
 
@@ -0,0 +1,2 @@
1
+ // Barrel: all react mod server registrations
2
+ // (none currently — react mods are client-only)
@@ -3,7 +3,7 @@
3
3
  import { Badge } from '#components/ui/badge';
4
4
  import { Button } from '#components/ui/button';
5
5
  import { Input } from '#components/ui/input';
6
- import { register } from '@treenity/core/core';
6
+ import { register } from '@treenity/core';
7
7
  import { X } from 'lucide-react';
8
8
  import { useState } from 'react';
9
9
 
@@ -1,7 +1,7 @@
1
1
  // Reusable type preview — Storybook-like: context switcher + live render + schema form editor
2
2
 
3
3
  import { Render, RenderContext } from '#context';
4
- import { type ComponentData, getContextsForType, type NodeData } from '@treenity/core/core';
4
+ import { type ComponentData, getContextsForType, type NodeData } from '@treenity/core';
5
5
  import { useMemo, useState } from 'react';
6
6
 
7
7
  // ── Mock data generator (by JSON Schema field type) ──
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Render } from '#context';
4
4
  import { usePath } from '#hooks';
5
- import { type NodeData, register } from '@treenity/core/core';
5
+ import { type NodeData, register } from '@treenity/core';
6
6
  import { useState } from 'react';
7
7
 
8
8
  // ── Node-level view (for ref nodes like /sys/autostart/xxx) ──
@@ -2,7 +2,7 @@
2
2
  // Any type without a specific react:form handler falls here via resolve('default', 'react:form')
3
3
 
4
4
  import { type RenderProps } from '#context';
5
- import { register, resolve } from '@treenity/core/core';
5
+ import { register, resolve } from '@treenity/core';
6
6
  import { createElement } from 'react';
7
7
 
8
8
  function DefaultSchemaForm({ value, onChange }: RenderProps) {
@@ -1,4 +1,4 @@
1
- import { type NodeData, R, S } from '@treenity/core/core';
1
+ import { type NodeData, R, S } from '@treenity/core';
2
2
  import { registerPrefab } from '@treenity/core/mod';
3
3
 
4
4
  registerPrefab('core', 'seed', [
@@ -1,6 +1,6 @@
1
1
  // Type node views — react + react:list + live preview
2
2
 
3
- import { getContextsForType, type NodeData, register } from '@treenity/core/core';
3
+ import { getContextsForType, type NodeData, register } from '@treenity/core';
4
4
  import { TypePreview } from './preview';
5
5
 
6
6
  // ── Helpers ──
@@ -0,0 +1,111 @@
1
+ // Optimistic prediction tests — predictOptimistic updates cache for sync methods
2
+
3
+ import { registerType } from '@treenity/core/comp';
4
+ import assert from 'node:assert';
5
+ import { beforeEach, describe, it } from 'node:test';
6
+ import * as cache from './cache';
7
+ import { predictOptimistic } from './hooks';
8
+
9
+ class Counter {
10
+ count = 0;
11
+
12
+ increment() {
13
+ this.count++;
14
+ }
15
+
16
+ setCount(data: { count: number }) {
17
+ this.count = data.count;
18
+ }
19
+
20
+ async asyncAction() {
21
+ // Simulates server-only async method
22
+ await Promise.resolve();
23
+ this.count = 999;
24
+ }
25
+
26
+ broken() {
27
+ throw new Error('intentional failure');
28
+ }
29
+ }
30
+
31
+ registerType('test.counter', Counter);
32
+
33
+ describe('predictOptimistic', () => {
34
+ beforeEach(() => {
35
+ cache.clear();
36
+ });
37
+
38
+ it('sync method updates cache immediately', () => {
39
+ cache.put({ $path: '/c', $type: 'test.counter', count: 5 } as any);
40
+
41
+ predictOptimistic('/c', Counter, undefined, Counter.prototype.increment, undefined);
42
+
43
+ const node = cache.get('/c');
44
+ assert.strictEqual((node as any).count, 6);
45
+ });
46
+
47
+ it('sync method with data updates cache', () => {
48
+ cache.put({ $path: '/c', $type: 'test.counter', count: 0 } as any);
49
+
50
+ predictOptimistic('/c', Counter, undefined, Counter.prototype.setCount, { count: 42 });
51
+
52
+ const node = cache.get('/c');
53
+ assert.strictEqual((node as any).count, 42);
54
+ });
55
+
56
+ it('does not mutate original cached node', () => {
57
+ cache.put({ $path: '/c', $type: 'test.counter', count: 10 } as any);
58
+ const before = cache.get('/c');
59
+
60
+ predictOptimistic('/c', Counter, undefined, Counter.prototype.increment, undefined);
61
+
62
+ const after = cache.get('/c');
63
+ assert.notStrictEqual(before, after);
64
+ });
65
+
66
+ it('skips async methods', () => {
67
+ cache.put({ $path: '/c', $type: 'test.counter', count: 0 } as any);
68
+
69
+ predictOptimistic('/c', Counter, undefined, Counter.prototype.asyncAction, undefined);
70
+
71
+ const node = cache.get('/c');
72
+ assert.strictEqual((node as any).count, 0);
73
+ });
74
+
75
+ it('skips when path not in cache', () => {
76
+ predictOptimistic('/missing', Counter, undefined, Counter.prototype.increment, undefined);
77
+
78
+ assert.strictEqual(cache.get('/missing'), undefined);
79
+ });
80
+
81
+ it('swallows method errors without updating cache', () => {
82
+ cache.put({ $path: '/c', $type: 'test.counter', count: 5 } as any);
83
+
84
+ predictOptimistic('/c', Counter, undefined, Counter.prototype.broken, undefined);
85
+
86
+ const node = cache.get('/c');
87
+ assert.strictEqual((node as any).count, 5);
88
+ });
89
+
90
+ it('skips when component type not found on node', () => {
91
+ cache.put({ $path: '/c', $type: 'unknown.type', count: 5 } as any);
92
+
93
+ predictOptimistic('/c', Counter, undefined, Counter.prototype.increment, undefined);
94
+
95
+ const node = cache.get('/c');
96
+ assert.strictEqual((node as any).count, 5);
97
+ });
98
+
99
+ it('works with named component key', () => {
100
+ cache.put({
101
+ $path: '/n',
102
+ $type: 'dir',
103
+ stats: { $type: 'test.counter', count: 3 },
104
+ } as any);
105
+
106
+ predictOptimistic('/n', Counter, 'stats', Counter.prototype.increment, undefined);
107
+
108
+ const node = cache.get('/n') as any;
109
+ assert.strictEqual(node.stats.count, 4);
110
+ });
111
+ });
@@ -1,4 +1,4 @@
1
- import type { NodeData } from '@treenity/core/core';
1
+ import type { NodeData } from '@treenity/core';
2
2
  import { withCache } from '@treenity/core/tree/cache';
3
3
  import assert from 'node:assert/strict';
4
4
  import { describe, it } from 'node:test';
@@ -3,7 +3,7 @@
3
3
  // Enables client to use the same combinators as server:
4
4
  // withSubscriptions(withCache(createRemoteTree(trpc)))
5
5
 
6
- import type { NodeData } from '@treenity/core/core';
6
+ import type { NodeData } from '@treenity/core';
7
7
  import type { Tree } from '@treenity/core/tree';
8
8
  import { defaultPatch } from '@treenity/core/tree/patch';
9
9
  import type { trpc } from './trpc';
package/src/root.css ADDED
@@ -0,0 +1,117 @@
1
+ /* Tailwind disabled — using CDN in index.html to avoid cross-package conflict */
2
+ /* @import 'tailwindcss'; */
3
+ /* @source "."; */
4
+ /* @source "../../src/mods"; */
5
+
6
+ /* Local fonts */
7
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-Light.ttf'); font-weight: 300; font-display: swap; }
8
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-Regular.ttf'); font-weight: 400; font-display: swap; }
9
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-Medium.ttf'); font-weight: 500; font-display: swap; }
10
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-SemiBold.ttf'); font-weight: 600; font-display: swap; }
11
+
12
+ /* shadcn/ui theme tokens — now in CDN (index.html), kept here for reference */
13
+ /* @theme inline extend { ... } — see index.html <style type="text/tailwindcss"> */
14
+
15
+ /* Tailwind theme tokens — unlayered :root beats CDN's layered output */
16
+ :root {
17
+ --color-background: #0d1117;
18
+ --color-foreground: #e6edf3;
19
+ --color-card: #161b22;
20
+ --color-card-foreground: #e6edf3;
21
+ --color-popover: #161b22;
22
+ --color-popover-foreground: #e6edf3;
23
+ --color-primary: #2ecc71;
24
+ --color-primary-foreground: #ffffff;
25
+ --color-secondary: #21262d;
26
+ --color-secondary-foreground: #e6edf3;
27
+ --color-muted: #161b22;
28
+ --color-muted-foreground: #7d8590;
29
+ --color-accent: rgba(46, 204, 113, 0.15);
30
+ --color-accent-foreground: #2ecc71;
31
+ --color-destructive: #f85149;
32
+ --color-destructive-foreground: #e6edf3;
33
+ --color-border: #30363d;
34
+ --color-input: #30363d;
35
+ --color-ring: #2ecc71;
36
+ --radius-sm: 4px;
37
+ --radius-md: 6px;
38
+ --radius-lg: 8px;
39
+
40
+ /* Legacy aliases */
41
+ --bg: var(--color-background);
42
+ --surface: var(--color-card);
43
+ --surface-2: var(--color-secondary);
44
+ --surface-3: var(--color-border);
45
+ --border: var(--color-border);
46
+ --border-subtle: var(--color-border);
47
+ --text: var(--color-foreground);
48
+ --text-2: var(--color-muted-foreground);
49
+ --text-3: color-mix(in srgb, var(--color-muted-foreground) 60%, transparent);
50
+ --accent: var(--color-primary);
51
+ --accent-subtle: var(--color-accent);
52
+ --danger: var(--color-destructive);
53
+ --danger-subtle: color-mix(in srgb, var(--color-destructive) 15%, transparent);
54
+ --success: #3fb950;
55
+ --success-subtle: rgba(63, 185, 80, 0.15);
56
+ --warning: #d29922;
57
+ --radius: var(--radius-md);
58
+ --radius-lg: var(--radius-lg);
59
+ --font: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
60
+ --mono: 'SF Mono', SFMono-Regular, 'Cascadia Code', 'Fira Code', Consolas, monospace;
61
+ --transition: 0.15s ease;
62
+ }
63
+ @layer base {
64
+ *,
65
+ ::before,
66
+ ::after {
67
+ margin: 0;
68
+ padding: 0;
69
+ box-sizing: border-box;
70
+ border-color: var(--color-border);
71
+ }
72
+ }
73
+ html,
74
+ body {
75
+ height: 100%;
76
+ font-family: var(--font);
77
+ }
78
+ body {
79
+ background: var(--bg);
80
+ color: var(--text);
81
+ font-size: 14px;
82
+ line-height: 1.5;
83
+ }
84
+
85
+ @layer base {
86
+ input,
87
+ textarea,
88
+ select {
89
+ width: 100%;
90
+ padding: 4px 8px;
91
+ background: var(--bg);
92
+ border: 1px solid var(--border);
93
+ border-radius: var(--radius);
94
+ color: var(--text);
95
+ font-size: 12px;
96
+ font-family: var(--mono);
97
+ outline: none;
98
+ transition: border-color var(--transition);
99
+ }
100
+ input:focus,
101
+ textarea:focus,
102
+ select:focus {
103
+ border-color: var(--accent);
104
+ }
105
+ input:read-only {
106
+ color: var(--text-2);
107
+ cursor: default;
108
+ }
109
+ input:read-only:focus {
110
+ border-color: var(--border);
111
+ }
112
+ textarea {
113
+ min-height: 80px;
114
+ resize: vertical;
115
+ line-height: 1.5;
116
+ }
117
+ }
@@ -1,5 +1,5 @@
1
1
  // Lazy registry loader — fetches type nodes from /sys/types on demand
2
- import { type ContextHandlers, register, resolve } from '@treenity/core/core';
2
+ import { type ContextHandlers, register, resolve } from '@treenity/core';
3
3
  import type { TypeSchema } from '@treenity/core/schema/types';
4
4
  import { useEffect, useState } from 'react';
5
5
  import { trpc } from './trpc';
package/src/symbols.ts ADDED
@@ -0,0 +1,18 @@
1
+ // Symbol-based component location metadata.
2
+ // Stamped on deserialization (cache.put). Survive spread, invisible to JSON/keys/entries.
3
+
4
+ import { isComponent, type NodeData } from '@treenity/core';
5
+
6
+ export const $key = Symbol.for('treenity.$key');
7
+ export const $node = Symbol.for('treenity.$node');
8
+
9
+ export function stampNode(node: NodeData): void {
10
+ (node as any)[$key] = '';
11
+ (node as any)[$node] = node;
12
+
13
+ for (const [k, v] of Object.entries(node)) {
14
+ if (k.startsWith('$') || !isComponent(v)) continue;
15
+ (v as any)[$key] = k;
16
+ (v as any)[$node] = node;
17
+ }
18
+ }
package/src/index.html DELETED
@@ -1,14 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Treenity</title>
7
- <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
8
- <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
9
- </head>
10
- <body>
11
- <div id="root"></div>
12
- <script type="module" src="main.tsx"></script>
13
- </body>
14
- </html>