@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,82 @@
1
+ 'use client';
2
+
3
+ import { cn } from '#components/lib/utils';
4
+ import { toggleVariants } from '#components/ui/toggle';
5
+ import { type VariantProps } from 'class-variance-authority';
6
+ import { ToggleGroup as ToggleGroupPrimitive } from 'radix-ui';
7
+ import * as React from 'react';
8
+
9
+ const ToggleGroupContext = React.createContext<
10
+ VariantProps<typeof toggleVariants> & {
11
+ spacing?: number
12
+ }
13
+ >({
14
+ size: "default",
15
+ variant: "default",
16
+ spacing: 0,
17
+ })
18
+
19
+ function ToggleGroup({
20
+ className,
21
+ variant,
22
+ size,
23
+ spacing = 0,
24
+ children,
25
+ ...props
26
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
27
+ VariantProps<typeof toggleVariants> & {
28
+ spacing?: number
29
+ }) {
30
+ return (
31
+ <ToggleGroupPrimitive.Root
32
+ data-slot="toggle-group"
33
+ data-variant={variant}
34
+ data-size={size}
35
+ data-spacing={spacing}
36
+ style={{ "--gap": spacing } as React.CSSProperties}
37
+ className={cn(
38
+ "group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=default]:data-[variant=outline]:shadow-xs",
39
+ className
40
+ )}
41
+ {...props}
42
+ >
43
+ <ToggleGroupContext.Provider value={{ variant, size, spacing }}>
44
+ {children}
45
+ </ToggleGroupContext.Provider>
46
+ </ToggleGroupPrimitive.Root>
47
+ )
48
+ }
49
+
50
+ function ToggleGroupItem({
51
+ className,
52
+ children,
53
+ variant,
54
+ size,
55
+ ...props
56
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
57
+ VariantProps<typeof toggleVariants>) {
58
+ const context = React.useContext(ToggleGroupContext)
59
+
60
+ return (
61
+ <ToggleGroupPrimitive.Item
62
+ data-slot="toggle-group-item"
63
+ data-variant={context.variant || variant}
64
+ data-size={context.size || size}
65
+ data-spacing={context.spacing}
66
+ className={cn(
67
+ toggleVariants({
68
+ variant: context.variant || variant,
69
+ size: context.size || size,
70
+ }),
71
+ "w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10",
72
+ "data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
73
+ className
74
+ )}
75
+ {...props}
76
+ >
77
+ {children}
78
+ </ToggleGroupPrimitive.Item>
79
+ )
80
+ }
81
+
82
+ export { ToggleGroup, ToggleGroupItem }
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { cn } from '#components/lib/utils';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+ import { Toggle as TogglePrimitive } from 'radix-ui';
6
+ import * as React from 'react';
7
+
8
+ const toggleVariants = cva(
9
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: "bg-transparent",
14
+ outline:
15
+ "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
16
+ },
17
+ size: {
18
+ default: "h-9 min-w-9 px-2",
19
+ sm: "h-8 min-w-8 px-1.5",
20
+ lg: "h-10 min-w-10 px-2.5",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: "default",
25
+ size: "default",
26
+ },
27
+ }
28
+ )
29
+
30
+ function Toggle({
31
+ className,
32
+ variant,
33
+ size,
34
+ ...props
35
+ }: React.ComponentProps<typeof TogglePrimitive.Root> &
36
+ VariantProps<typeof toggleVariants>) {
37
+ return (
38
+ <TogglePrimitive.Root
39
+ data-slot="toggle"
40
+ className={cn(toggleVariants({ variant, size, className }))}
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ export { Toggle, toggleVariants }
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { cn } from '#lib/utils';
3
+ import { cn } from '#components/lib/utils';
4
4
  import { Tooltip as TooltipPrimitive } from 'radix-ui';
5
5
  import * as React from 'react';
6
6
 
@@ -2,6 +2,8 @@
2
2
  // <Render> + <RenderContext> + <NodeProvider>
3
3
  // Depends on: core (resolve), React
4
4
 
5
+ import { execute } from '#hooks';
6
+ import { $key, $node } from '#symbols';
5
7
  import {
6
8
  type ComponentData,
7
9
  hasMissResolver,
@@ -9,8 +11,8 @@ import {
9
11
  resolve,
10
12
  resolveExact,
11
13
  subscribeRegistry,
12
- } from '@treenity/core/core';
13
- import { createContext, createElement, type FC, type ReactNode, useContext, useEffect, useState } from 'react';
14
+ } from '@treenity/core';
15
+ import { createContext, createElement, type FC, type ReactNode, useContext, useEffect, useMemo, useState } from 'react';
14
16
 
15
17
  // ── Tree context (rendering context string) ──
16
18
 
@@ -28,7 +30,7 @@ export function RenderContext({ name, children }: { name: string; children: Reac
28
30
 
29
31
  const NodeCtx = createContext<NodeData | null>(null);
30
32
 
31
- export function NodeProvider({ value, children }: { value: NodeData | null; children: ReactNode }) {
33
+ export function NodeProvider({ value, children }: { value: NodeData | null; children?: ReactNode }) {
32
34
  if (!value?.$path) return null;
33
35
  return <NodeCtx.Provider value={value}>{children}</NodeCtx.Provider>;
34
36
  }
@@ -39,32 +41,67 @@ export function useCurrentNode(): NodeData {
39
41
  return n;
40
42
  }
41
43
 
44
+ // ── viewCtx — derive location context from value's symbol metadata ──
45
+
46
+ export type ViewCtx = {
47
+ node: NodeData;
48
+ path: string;
49
+ execute(action: string, data?: unknown): Promise<unknown>;
50
+ };
51
+
52
+ export function viewCtx(value: ComponentData): ViewCtx | null {
53
+ const node: NodeData | undefined = (value as any)[$node];
54
+ if (!node) return null;
55
+ const key: string = (value as any)[$key] ?? '';
56
+ const path = key ? `${node.$path}#${key}` : node.$path;
57
+ return { node, path, execute: (action, data?) => execute(path, action, data) };
58
+ }
59
+
42
60
  // ── Handler type for React context ──
43
61
  // value is ComponentData (base type). NodeData IS ComponentData.
44
- // Renderers that need $path use usePath().
45
62
 
46
- export type RenderProps = {
47
- value: ComponentData;
48
- onChange?: (next: ComponentData) => void;
63
+ export type RenderProps<T = ComponentData> = {
64
+ value: T;
65
+ onChange?: (next: T) => void;
66
+ ctx?: ViewCtx | null;
67
+ };
68
+
69
+ export type ReactHandler = FC<RenderProps<any>>;
70
+
71
+ /** Typed view component. Use: `const MyView: View<MyType> = ({ value, ctx }) => ...` */
72
+ export type View<T> = FC<RenderProps<T>>;
73
+
74
+ // ── useActions — proxy that turns value's symbol metadata into action calls ──
75
+
76
+ type Actions<T> = {
77
+ [K in keyof T as T[K] extends (...args: any[]) => any ? K : never]:
78
+ T[K] extends (data: infer D) => any
79
+ ? (data: D) => Promise<unknown>
80
+ : () => Promise<unknown>;
49
81
  };
50
82
 
51
- export type ReactHandler = FC<RenderProps>;
83
+ export function useActions<T extends ComponentData>(value: T): Actions<T> {
84
+ const ctx = viewCtx(value);
85
+ if (!ctx) throw new Error('useActions: value has no node context (missing $node symbol)');
86
+
87
+ return useMemo(() => new Proxy({} as Actions<T>, {
88
+ get: (_target, prop: string) => (data?: unknown) => ctx.execute(prop, data),
89
+ }), [ctx.path]);
90
+ }
52
91
 
53
92
  declare module '@treenity/core/core/context' {
54
- interface ContextHandlers {
55
- react: ReactHandler;
93
+ interface ContextHandlers<T> {
94
+ react: FC<RenderProps<T>>;
56
95
  }
57
96
  }
58
97
 
59
- // ── SystemFallbackView — registered by UIX when a type has no custom view ──
98
+ // ── UixNoView — registered by UIX when a type has no custom view yet ──
60
99
  // Renders default@context without going through type-specific resolve (avoids infinite loop).
61
- export const SystemFallbackView: FC<RenderProps> = ({ value, onChange }) => {
100
+ export const UixNoView: FC<RenderProps> = ({ value, onChange }) => {
62
101
  const context = useTreeContext();
63
102
  const def = resolve('default', context, false) as FC<RenderProps> | null;
64
103
  if (!def) return null;
65
- const el = createElement(def, { value, onChange });
66
- if ('$path' in value) return <NodeProvider value={value as NodeData}>{el}</NodeProvider>;
67
- return el;
104
+ return createElement(def, { value, onChange });
68
105
  };
69
106
 
70
107
  // ── <Render> — component/node-level rendering ──
@@ -73,40 +110,36 @@ export function Render({ value, onChange }: RenderProps) {
73
110
  const context = useTreeContext();
74
111
  const type = value.$type;
75
112
 
76
- // Tree actual handler in state so React Compiler can't optimize away the update
77
- // (a dummy tick counter gets eliminated because its value is never read in render output)
78
- const [Handler, setHandler] = useState<ReactHandler | null>(
79
- () => resolveExact(type, context) as ReactHandler | null,
80
- );
113
+ const ctx_ = context as 'react';
114
+ const sync = useMemo(() => resolveExact(type, ctx_), [type, ctx_]);
115
+ const [async_, setAsync] = useState<ReactHandler | null>(null);
81
116
 
82
- // Subscribe to registry bumps. When handler is registered async (UIX lazy load),
83
- // the callback fires and stores the resolved handler → triggers re-render.
84
117
  useEffect(() => {
85
- const found = resolveExact(type, context) as ReactHandler | null;
86
- if (found) { setHandler(() => found); return; }
87
- setHandler(null); // Clear stale handler when type/context changes
88
- if (hasMissResolver(context)) resolve(type, context);
89
- const unsub = subscribeRegistry(() => {
90
- const h = resolveExact(type, context) as ReactHandler | null;
91
- if (h) setHandler(() => h);
118
+ if (sync) return;
119
+ setAsync(null);
120
+ if (hasMissResolver(ctx_)) resolve(type, ctx_);
121
+
122
+ return subscribeRegistry(() => {
123
+ const h = resolveExact(type, ctx_);
124
+ if (h) setAsync(() => h);
92
125
  });
93
- return unsub;
94
- }, [type, context]);
95
-
96
- // Fallback: if no exact handler, try default/parent context resolution
97
- let Final = Handler;
98
- if (!Final) {
99
- if (hasMissResolver(context)) {
100
- resolve(type, context);
126
+ }, [type, ctx_, sync]);
127
+
128
+ let Handler = sync ?? async_;
129
+
130
+ if (!Handler) {
131
+ if (hasMissResolver(ctx_)) {
132
+ resolve(type, ctx_);
101
133
  return null;
102
134
  }
103
- Final = resolve(type, context, false) as ReactHandler | null;
135
+ Handler = resolve(type, ctx_, false);
104
136
  }
105
137
 
106
- if (!Final) return null;
107
- const el = createElement(Final, { value, onChange });
108
- if ('$path' in value) return <NodeProvider value={value as NodeData}>{el}</NodeProvider>;
109
- return el;
138
+ if (!Handler) return null;
139
+
140
+ const ctx = viewCtx(value);
141
+ const el = createElement(Handler, { value, onChange, ctx });
142
+ return ctx?.node ? createElement(NodeProvider, { value: ctx.node }, el) : el;
110
143
  }
111
144
 
112
145
  // ── <RenderField> — field-level rendering by type name ──
package/src/events.ts ADDED
@@ -0,0 +1,81 @@
1
+ // Server event subscription — module-level, not tied to any React component.
2
+ // Listens to trpc.events SSE and updates the cache.
3
+
4
+ import type { NodeData } from '@treenity/core';
5
+ import { applyPatch, type Operation } from 'fast-json-patch';
6
+ import * as cache from './cache';
7
+ import { trpc } from './trpc';
8
+
9
+ type LoadChildren = (path: string) => Promise<void>;
10
+
11
+ interface EventsConfig {
12
+ loadChildren: LoadChildren;
13
+ getExpanded: () => Set<string>;
14
+ getSelected: () => string | null;
15
+ }
16
+
17
+ let unsub: (() => void) | null = null;
18
+
19
+ export function startEvents(config: EventsConfig) {
20
+ stopEvents();
21
+
22
+ const { loadChildren, getExpanded, getSelected } = config;
23
+
24
+ const sub = trpc.events.subscribe(undefined as void, {
25
+ onData(event) {
26
+ if (event.type === 'reconnect') {
27
+ if (!event.preserved) {
28
+ cache.signalReconnect();
29
+ for (const path of getExpanded()) loadChildren(path);
30
+ const sel = getSelected();
31
+ if (sel) {
32
+ trpc.get.query({ path: sel, watch: true }).then(n => {
33
+ if (n) cache.put(n as NodeData);
34
+ });
35
+ }
36
+ }
37
+ return;
38
+ }
39
+
40
+ if (event.type === 'set') {
41
+ cache.put({ $path: event.path, ...event.node } as NodeData);
42
+ if (event.addVps) event.addVps.forEach((vp: string) => cache.addToParent(event.path, vp));
43
+ if (event.rmVps) event.rmVps.forEach((vp: string) => cache.removeFromParent(event.path, vp));
44
+ } else if (event.type === 'patch') {
45
+ const existing = cache.get(event.path);
46
+ if (existing && event.patches) {
47
+ try {
48
+ applyPatch(existing, event.patches as Operation[]);
49
+ cache.put(existing);
50
+ } catch (e) {
51
+ console.error('Failed to apply patches, fetching full node:', e);
52
+ trpc.get.query({ path: event.path }).then((n) => {
53
+ if (n) cache.put(n as NodeData);
54
+ });
55
+ }
56
+ } else {
57
+ trpc.get.query({ path: event.path }).then((n) => {
58
+ if (n) cache.put(n as NodeData);
59
+ });
60
+ }
61
+ if (event.addVps) event.addVps.forEach((vp: string) => cache.addToParent(event.path, vp));
62
+ if (event.rmVps) event.rmVps.forEach((vp: string) => cache.removeFromParent(event.path, vp));
63
+ } else if (event.type === 'remove') {
64
+ if (event.rmVps && event.rmVps.length > 0) {
65
+ event.rmVps.forEach((vp: string) => cache.removeFromParent(event.path, vp));
66
+ } else {
67
+ cache.remove(event.path);
68
+ }
69
+ }
70
+ },
71
+ });
72
+
73
+ unsub = () => sub.unsubscribe();
74
+ }
75
+
76
+ export function stopEvents() {
77
+ if (unsub) {
78
+ unsub();
79
+ unsub = null;
80
+ }
81
+ }
@@ -0,0 +1,112 @@
1
+ // Virtual read-only Tree over React Fiber.
2
+ // Walks the live Fiber tree on each query — no state, no tracking.
3
+ // Registered as t.mount.react — isomorphic mount, same as server-side mounts.
4
+
5
+ import type { NodeData } from '@treenity/core'
6
+ import { register } from '@treenity/core'
7
+ import type { Tree } from '@treenity/core/tree'
8
+
9
+ const PREFIX = '/local/react'
10
+
11
+ interface FiberNode {
12
+ $path: string
13
+ $type: string
14
+ sourcePath?: string
15
+ children?: FiberNode[]
16
+ }
17
+
18
+ // Detect <Render> by name + props shape (resilient to HMR wrappers)
19
+ function isRenderFiber(f: any): boolean {
20
+ if (!f.memoizedProps?.value?.$type) return false
21
+ const name = f.type?.name || f.type?.displayName || ''
22
+ return name === 'Render'
23
+ }
24
+
25
+ function getRootFiber() {
26
+ const el = document.getElementById('root')
27
+ if (!el) return null
28
+
29
+ // React 19 createRoot: __reactContainer$ on the container element
30
+ const containerKey = Object.keys(el).find(k => k.startsWith('__reactContainer$'))
31
+ if (containerKey) return (el as any)[containerKey]
32
+
33
+ // React 18 fallback: __reactFiber$ on first child
34
+ const fiberKey = Object.keys(el).find(k => k.startsWith('__reactFiber$'))
35
+ return fiberKey ? (el as any)[fiberKey] : null
36
+ }
37
+
38
+ function walk(fiber: any, parentPath: string, maxDepth: number, depth = 0): FiberNode[] {
39
+ if (depth >= maxDepth) return []
40
+
41
+ const result: FiberNode[] = []
42
+ let f = fiber?.child
43
+ let i = 0
44
+
45
+ while (f) {
46
+ if (isRenderFiber(f)) {
47
+ const v = f.memoizedProps.value
48
+ const path = `${parentPath}/${i}`
49
+ result.push({
50
+ $path: path,
51
+ $type: v.$type,
52
+ sourcePath: v.$path,
53
+ children: depth + 1 < maxDepth ? walk(f, path, maxDepth, depth + 1) : undefined,
54
+ })
55
+ i++
56
+ } else {
57
+ result.push(...walk(f, parentPath, maxDepth, depth))
58
+ }
59
+ f = f.sibling
60
+ }
61
+
62
+ return result
63
+ }
64
+
65
+ function scan(depth = 1): FiberNode[] {
66
+ const root = getRootFiber()
67
+ return root ? walk(root, PREFIX, depth) : []
68
+ }
69
+
70
+ function findByPath(nodes: FiberNode[], path: string): FiberNode | undefined {
71
+ for (const n of nodes) {
72
+ if (n.$path === path) return n
73
+ if (n.children) {
74
+ const found = findByPath(n.children, path)
75
+ if (found) return found
76
+ }
77
+ }
78
+ }
79
+
80
+ function toNode(n: FiberNode): NodeData {
81
+ return { $path: n.$path, $type: n.$type, sourcePath: n.sourcePath } as NodeData
82
+ }
83
+
84
+ export function createFiberTree(): Tree {
85
+ return {
86
+ async get(path) {
87
+ if (path === PREFIX) return { $path: PREFIX, $type: 'dir' } as NodeData
88
+ const node = findByPath(scan(100), path)
89
+ return node ? toNode(node) : undefined
90
+ },
91
+
92
+ async getChildren(path, opts?) {
93
+ const depth = opts?.depth ?? 1
94
+
95
+ if (path === PREFIX) {
96
+ const nodes = scan(depth)
97
+ return { items: nodes.map(toNode), total: nodes.length }
98
+ }
99
+
100
+ const parent = findByPath(scan(100), path)
101
+ const children = parent?.children ?? []
102
+ return { items: children.map(toNode), total: children.length }
103
+ },
104
+
105
+ async set() { throw new Error('fiber tree is read-only') },
106
+ async remove() { throw new Error('fiber tree is read-only') },
107
+ async patch() { throw new Error('fiber tree is read-only') },
108
+ }
109
+ }
110
+
111
+ // Mount adapter — same pattern as t.mount.fs, t.mount.mongo
112
+ register('t.mount.react', 'mount', () => createFiberTree())
package/src/hooks.ts CHANGED
@@ -5,10 +5,19 @@
5
5
  // execute: action caller
6
6
  // watch: universal async generator
7
7
 
8
- import { type Class, getComp, type TypeProxy } from '@treenity/core/comp';
9
- import { getComponent, type NodeData, normalizeType } from '@treenity/core/core';
8
+ import { getComponent, type NodeData, normalizeType, resolve } from '@treenity/core';
9
+ import { type Class, type TypeProxy } from '@treenity/core/comp';
10
10
  import { deriveURI, parseURI } from '@treenity/core/uri';
11
- import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
11
+ import {
12
+ createContext,
13
+ useCallback,
14
+ useContext,
15
+ useEffect,
16
+ useMemo,
17
+ useRef,
18
+ useState,
19
+ useSyncExternalStore,
20
+ } from 'react';
12
21
  import * as cache from './cache';
13
22
  import { tree } from './client';
14
23
  import { trpc } from './trpc';
@@ -50,7 +59,7 @@ export function usePath<T extends object>(
50
59
  const path = isTyped ? pathOrUri : (parsed?.path ?? null);
51
60
 
52
61
  const node = useSyncExternalStore(
53
- useCallback((cb: () => void) => (path ? cache.subscribePath(path, cb) : () => {}), [path]),
62
+ useCallback((cb: () => void) => (path ? cache.subscribePath(path, cb) : () => { }), [path]),
54
63
  useCallback(() => (path ? cache.get(path) : undefined), [path]),
55
64
  );
56
65
 
@@ -111,12 +120,78 @@ export async function set(next: NodeData) {
111
120
  // ── execute: action caller ──
112
121
 
113
122
  export const execute = (
114
- path: string, action: string, data?: unknown, type?: string, key?: string,
115
- ) => trpc.execute.mutate({ path, type, key, action, data });
123
+ pathOrUri: string, action: string, data?: unknown, type?: string, key?: string,
124
+ ) => {
125
+ let path = pathOrUri;
126
+ if (!key && pathOrUri.includes('#')) {
127
+ const parsed = parseURI(pathOrUri);
128
+ path = parsed.path;
129
+ key = parsed.key;
130
+ }
131
+
132
+ // Optimistic: resolve class from cache + registry, predict locally
133
+ const cached = cache.get(path);
134
+ if (cached) {
135
+ const compType = type ?? cached.$type;
136
+ const cls = resolve(compType, 'class');
137
+ if (cls) {
138
+ const fn = cls.prototype?.[action];
139
+ if (fn) predictOptimistic(path, cls, key, fn, data);
140
+ }
141
+ }
142
+
143
+ return trpc.execute.mutate({ path, type, key, action, data });
144
+ };
145
+
146
+ // ── useCanWrite: ACL-based write permission check ──
147
+
148
+ const W = 2;
149
+ const permCache = new Map<string, { perm: number; ts: number }>();
150
+ const PERM_TTL = 30_000; // 30s cache
151
+
152
+ export function useCanWrite(path: string | null): boolean {
153
+ const [perm, setPerm] = useState<number>(0);
154
+
155
+ useEffect(() => {
156
+ if (!path) return;
157
+ const cached = permCache.get(path);
158
+ if (cached && Date.now() - cached.ts < PERM_TTL) {
159
+ setPerm(cached.perm);
160
+ return;
161
+ }
162
+ trpc.getPerm.query({ path }).then((p) => {
163
+ permCache.set(path, { perm: p, ts: Date.now() });
164
+ setPerm(p);
165
+ }).catch(() => setPerm(0));
166
+ }, [path]);
167
+
168
+ return (perm & W) !== 0;
169
+ }
116
170
 
117
171
  // ── Internals ──
118
172
 
119
- const AsyncGenFn = Object.getPrototypeOf(async function* () {}).constructor;
173
+ const AsyncGenFn = Object.getPrototypeOf(async function* () { }).constructor;
174
+ const AsyncFn = Object.getPrototypeOf(async function () { }).constructor;
175
+
176
+ /** Optimistic prediction: run a sync method locally on a cloned cached node */
177
+ export function predictOptimistic<T extends object>(
178
+ path: string, cls: Class<T>, key: string | undefined,
179
+ fn: Function, data: unknown,
180
+ ): void {
181
+ if (fn instanceof AsyncFn) return;
182
+
183
+ const cached = cache.get(path);
184
+ if (!cached) return;
185
+
186
+ try {
187
+ const draft = structuredClone(cached);
188
+ const target = getComponent(draft, cls, key);
189
+ if (!target) return;
190
+
191
+ fn.call(target, data);
192
+ cache.put(draft);
193
+ } catch { /* prediction failed — server-only */ }
194
+ }
120
195
 
121
196
  function streamToAsyncIterable<T>(
122
197
  input: { path: string; type?: string; key?: string; action: string; data?: unknown },
@@ -157,7 +232,7 @@ function makeProxy<T extends object>(
157
232
  ): TypeProxy<T> {
158
233
  const type = normalizeType(cls);
159
234
  const comp = node
160
- ? (key ? getComponent(node, key) : getComp(node, cls))
235
+ ? (key ? getComponent(node, key) : getComponent(node, cls))
161
236
  : undefined;
162
237
 
163
238
  return new Proxy(comp ?? {}, {
@@ -166,7 +241,11 @@ function makeProxy<T extends object>(
166
241
  if (typeof fn === 'function') {
167
242
  if (fn instanceof AsyncGenFn)
168
243
  return (data?: unknown) => streamToAsyncIterable({ path, type, key, action: prop, data });
169
- return (data?: unknown) => trpc.execute.mutate({ path, type, key, action: prop, data });
244
+
245
+ return (data?: unknown) => {
246
+ predictOptimistic(path, cls, key, fn, data);
247
+ return trpc.execute.mutate({ path, type, key, action: prop, data });
248
+ };
170
249
  }
171
250
  return (comp as any)?.[prop];
172
251
  },
package/src/idb.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Raw IDB API, no dependencies. Fire-and-forget friendly.
3
3
  // Degrades silently if IDB unavailable (private browsing, SSR).
4
4
 
5
- import type { NodeData } from '@treenity/core/core';
5
+ import type { NodeData } from '@treenity/core';
6
6
 
7
7
  const DB_NAME = 'treenity';
8
8
  const DB_VERSION = 1;
@@ -0,0 +1,21 @@
1
+ // Deep-clone any value into a plain JSON-safe object.
2
+ // Strips valtio proxies, preserves Dates, drops functions/symbols.
3
+
4
+ export function toPlain<T>(value: T): T {
5
+ if (value === null || value === undefined) return value;
6
+
7
+ if (value instanceof Date) return new Date(value.getTime()) as T;
8
+
9
+ if (Array.isArray(value)) return value.map(toPlain) as T;
10
+
11
+ if (typeof value === 'object') {
12
+ const out: Record<string, unknown> = {};
13
+ for (const [k, v] of Object.entries(value)) {
14
+ if (typeof v === 'function' || typeof v === 'symbol') continue;
15
+ out[k] = toPlain(v);
16
+ }
17
+ return out as T;
18
+ }
19
+
20
+ return value;
21
+ }
package/src/main.tsx CHANGED
@@ -5,7 +5,8 @@ import { StrictMode } from 'react';
5
5
  import { createRoot } from 'react-dom/client';
6
6
  import { App } from './App';
7
7
  import './load-client';
8
- import './style.css';
8
+ import { Toaster } from './components/ui/sonner';
9
+ import './root.css';
9
10
 
10
11
  enablePatches();
11
12
 
@@ -17,6 +18,7 @@ createRoot(root).render(
17
18
  <StrictMode>
18
19
  <QueryClientProvider client={queryClient}>
19
20
  <App />
21
+ <Toaster />
20
22
  </QueryClientProvider>
21
23
  </StrictMode>,
22
24
  );