@treenity/react 3.0.0

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 (297) hide show
  1. package/dist/AclEditor.d.ts +11 -0
  2. package/dist/AclEditor.d.ts.map +1 -0
  3. package/dist/AclEditor.js +152 -0
  4. package/dist/AclEditor.js.map +1 -0
  5. package/dist/App.d.ts +2 -0
  6. package/dist/App.d.ts.map +1 -0
  7. package/dist/App.js +521 -0
  8. package/dist/App.js.map +1 -0
  9. package/dist/Inspector.d.ts +12 -0
  10. package/dist/Inspector.d.ts.map +1 -0
  11. package/dist/Inspector.js +360 -0
  12. package/dist/Inspector.js.map +1 -0
  13. package/dist/Tree.d.ts +16 -0
  14. package/dist/Tree.d.ts.map +1 -0
  15. package/dist/Tree.js +100 -0
  16. package/dist/Tree.js.map +1 -0
  17. package/dist/ViewPage.d.ts +5 -0
  18. package/dist/ViewPage.d.ts.map +1 -0
  19. package/dist/ViewPage.js +13 -0
  20. package/dist/ViewPage.js.map +1 -0
  21. package/dist/bind/computed.d.ts +9 -0
  22. package/dist/bind/computed.d.ts.map +1 -0
  23. package/dist/bind/computed.js +61 -0
  24. package/dist/bind/computed.js.map +1 -0
  25. package/dist/bind/engine.d.ts +3 -0
  26. package/dist/bind/engine.d.ts.map +1 -0
  27. package/dist/bind/engine.js +184 -0
  28. package/dist/bind/engine.js.map +1 -0
  29. package/dist/bind/eval.d.ts +13 -0
  30. package/dist/bind/eval.d.ts.map +1 -0
  31. package/dist/bind/eval.js +97 -0
  32. package/dist/bind/eval.js.map +1 -0
  33. package/dist/bind/hook.d.ts +8 -0
  34. package/dist/bind/hook.d.ts.map +1 -0
  35. package/dist/bind/hook.js +99 -0
  36. package/dist/bind/hook.js.map +1 -0
  37. package/dist/bind/parse.d.ts +19 -0
  38. package/dist/bind/parse.d.ts.map +1 -0
  39. package/dist/bind/parse.js +86 -0
  40. package/dist/bind/parse.js.map +1 -0
  41. package/dist/bind/pipes.d.ts +4 -0
  42. package/dist/bind/pipes.d.ts.map +1 -0
  43. package/dist/bind/pipes.js +43 -0
  44. package/dist/bind/pipes.js.map +1 -0
  45. package/dist/cache.d.ts +27 -0
  46. package/dist/cache.d.ts.map +1 -0
  47. package/dist/cache.js +236 -0
  48. package/dist/cache.js.map +1 -0
  49. package/dist/client-tree.d.ts +9 -0
  50. package/dist/client-tree.d.ts.map +1 -0
  51. package/dist/client-tree.js +14 -0
  52. package/dist/client-tree.js.map +1 -0
  53. package/dist/client.d.ts +2 -0
  54. package/dist/client.d.ts.map +1 -0
  55. package/dist/client.js +10 -0
  56. package/dist/client.js.map +1 -0
  57. package/dist/components/ui/accordion.d.ts +8 -0
  58. package/dist/components/ui/accordion.d.ts.map +1 -0
  59. package/dist/components/ui/accordion.js +18 -0
  60. package/dist/components/ui/accordion.js.map +1 -0
  61. package/dist/components/ui/badge.d.ts +10 -0
  62. package/dist/components/ui/badge.d.ts.map +1 -0
  63. package/dist/components/ui/badge.js +19 -0
  64. package/dist/components/ui/badge.js.map +1 -0
  65. package/dist/components/ui/button.d.ts +11 -0
  66. package/dist/components/ui/button.d.ts.map +1 -0
  67. package/dist/components/ui/button.js +31 -0
  68. package/dist/components/ui/button.js.map +1 -0
  69. package/dist/components/ui/checkbox.d.ts +4 -0
  70. package/dist/components/ui/checkbox.d.ts.map +1 -0
  71. package/dist/components/ui/checkbox.js +7 -0
  72. package/dist/components/ui/checkbox.js.map +1 -0
  73. package/dist/components/ui/dialog.d.ts +18 -0
  74. package/dist/components/ui/dialog.d.ts.map +1 -0
  75. package/dist/components/ui/dialog.js +37 -0
  76. package/dist/components/ui/dialog.js.map +1 -0
  77. package/dist/components/ui/drawer.d.ts +14 -0
  78. package/dist/components/ui/drawer.d.ts.map +1 -0
  79. package/dist/components/ui/drawer.js +35 -0
  80. package/dist/components/ui/drawer.js.map +1 -0
  81. package/dist/components/ui/input.d.ts +4 -0
  82. package/dist/components/ui/input.d.ts.map +1 -0
  83. package/dist/components/ui/input.js +7 -0
  84. package/dist/components/ui/input.js.map +1 -0
  85. package/dist/components/ui/label.d.ts +5 -0
  86. package/dist/components/ui/label.d.ts.map +1 -0
  87. package/dist/components/ui/label.js +8 -0
  88. package/dist/components/ui/label.js.map +1 -0
  89. package/dist/components/ui/popover.d.ts +11 -0
  90. package/dist/components/ui/popover.d.ts.map +1 -0
  91. package/dist/components/ui/popover.js +26 -0
  92. package/dist/components/ui/popover.js.map +1 -0
  93. package/dist/components/ui/progress.d.ts +5 -0
  94. package/dist/components/ui/progress.d.ts.map +1 -0
  95. package/dist/components/ui/progress.js +9 -0
  96. package/dist/components/ui/progress.js.map +1 -0
  97. package/dist/components/ui/select.d.ts +16 -0
  98. package/dist/components/ui/select.d.ts.map +1 -0
  99. package/dist/components/ui/select.js +39 -0
  100. package/dist/components/ui/select.js.map +1 -0
  101. package/dist/components/ui/slider.d.ts +5 -0
  102. package/dist/components/ui/slider.d.ts.map +1 -0
  103. package/dist/components/ui/slider.js +15 -0
  104. package/dist/components/ui/slider.js.map +1 -0
  105. package/dist/components/ui/sonner.d.ts +4 -0
  106. package/dist/components/ui/sonner.d.ts.map +1 -0
  107. package/dist/components/ui/sonner.js +21 -0
  108. package/dist/components/ui/sonner.js.map +1 -0
  109. package/dist/components/ui/switch.d.ts +7 -0
  110. package/dist/components/ui/switch.d.ts.map +1 -0
  111. package/dist/components/ui/switch.js +9 -0
  112. package/dist/components/ui/switch.js.map +1 -0
  113. package/dist/components/ui/textarea.d.ts +4 -0
  114. package/dist/components/ui/textarea.d.ts.map +1 -0
  115. package/dist/components/ui/textarea.js +7 -0
  116. package/dist/components/ui/textarea.js.map +1 -0
  117. package/dist/components/ui/tooltip.d.ts +8 -0
  118. package/dist/components/ui/tooltip.d.ts.map +1 -0
  119. package/dist/components/ui/tooltip.js +18 -0
  120. package/dist/components/ui/tooltip.js.map +1 -0
  121. package/dist/context/index.d.ts +31 -0
  122. package/dist/context/index.d.ts.map +1 -0
  123. package/dist/context/index.js +98 -0
  124. package/dist/context/index.js.map +1 -0
  125. package/dist/context.d.ts +2 -0
  126. package/dist/context.d.ts.map +1 -0
  127. package/dist/context.js +2 -0
  128. package/dist/context.js.map +1 -0
  129. package/dist/hooks.d.ts +21 -0
  130. package/dist/hooks.d.ts.map +1 -0
  131. package/dist/hooks.js +156 -0
  132. package/dist/hooks.js.map +1 -0
  133. package/dist/idb.d.ts +13 -0
  134. package/dist/idb.d.ts.map +1 -0
  135. package/dist/idb.js +67 -0
  136. package/dist/idb.js.map +1 -0
  137. package/dist/lib/minimd.d.ts +3 -0
  138. package/dist/lib/minimd.d.ts.map +1 -0
  139. package/dist/lib/minimd.js +97 -0
  140. package/dist/lib/minimd.js.map +1 -0
  141. package/dist/lib/utils.d.ts +3 -0
  142. package/dist/lib/utils.d.ts.map +1 -0
  143. package/dist/lib/utils.js +6 -0
  144. package/dist/lib/utils.js.map +1 -0
  145. package/dist/load-client.d.ts +2 -0
  146. package/dist/load-client.d.ts.map +1 -0
  147. package/dist/load-client.js +6 -0
  148. package/dist/load-client.js.map +1 -0
  149. package/dist/main.d.ts +4 -0
  150. package/dist/main.d.ts.map +1 -0
  151. package/dist/main.js +16 -0
  152. package/dist/main.js.map +1 -0
  153. package/dist/mods/editor-ui/client.d.ts +6 -0
  154. package/dist/mods/editor-ui/client.d.ts.map +1 -0
  155. package/dist/mods/editor-ui/client.js +8 -0
  156. package/dist/mods/editor-ui/client.js.map +1 -0
  157. package/dist/mods/editor-ui/default-view.d.ts +2 -0
  158. package/dist/mods/editor-ui/default-view.d.ts.map +1 -0
  159. package/dist/mods/editor-ui/default-view.js +71 -0
  160. package/dist/mods/editor-ui/default-view.js.map +1 -0
  161. package/dist/mods/editor-ui/dir-view.d.ts +2 -0
  162. package/dist/mods/editor-ui/dir-view.d.ts.map +1 -0
  163. package/dist/mods/editor-ui/dir-view.js +42 -0
  164. package/dist/mods/editor-ui/dir-view.js.map +1 -0
  165. package/dist/mods/editor-ui/form-fields.d.ts +6 -0
  166. package/dist/mods/editor-ui/form-fields.d.ts.map +1 -0
  167. package/dist/mods/editor-ui/form-fields.js +401 -0
  168. package/dist/mods/editor-ui/form-fields.js.map +1 -0
  169. package/dist/mods/editor-ui/layout-view.d.ts +2 -0
  170. package/dist/mods/editor-ui/layout-view.d.ts.map +1 -0
  171. package/dist/mods/editor-ui/layout-view.js +22 -0
  172. package/dist/mods/editor-ui/layout-view.js.map +1 -0
  173. package/dist/mods/editor-ui/list-items.d.ts +2 -0
  174. package/dist/mods/editor-ui/list-items.d.ts.map +1 -0
  175. package/dist/mods/editor-ui/list-items.js +38 -0
  176. package/dist/mods/editor-ui/list-items.js.map +1 -0
  177. package/dist/mods/editor-ui/node-utils.d.ts +10 -0
  178. package/dist/mods/editor-ui/node-utils.d.ts.map +1 -0
  179. package/dist/mods/editor-ui/node-utils.js +76 -0
  180. package/dist/mods/editor-ui/node-utils.js.map +1 -0
  181. package/dist/mods/editor-ui/user-view.d.ts +2 -0
  182. package/dist/mods/editor-ui/user-view.d.ts.map +1 -0
  183. package/dist/mods/editor-ui/user-view.js +47 -0
  184. package/dist/mods/editor-ui/user-view.js.map +1 -0
  185. package/dist/mods/treenity/client.d.ts +4 -0
  186. package/dist/mods/treenity/client.d.ts.map +1 -0
  187. package/dist/mods/treenity/client.js +6 -0
  188. package/dist/mods/treenity/client.js.map +1 -0
  189. package/dist/mods/treenity/groups/index.d.ts +2 -0
  190. package/dist/mods/treenity/groups/index.d.ts.map +1 -0
  191. package/dist/mods/treenity/groups/index.js +27 -0
  192. package/dist/mods/treenity/groups/index.js.map +1 -0
  193. package/dist/mods/treenity/preview.d.ts +6 -0
  194. package/dist/mods/treenity/preview.d.ts.map +1 -0
  195. package/dist/mods/treenity/preview.js +95 -0
  196. package/dist/mods/treenity/preview.js.map +1 -0
  197. package/dist/mods/treenity/ref-view.d.ts +2 -0
  198. package/dist/mods/treenity/ref-view.d.ts.map +1 -0
  199. package/dist/mods/treenity/ref-view.js +29 -0
  200. package/dist/mods/treenity/ref-view.js.map +1 -0
  201. package/dist/mods/treenity/schema-form.d.ts +2 -0
  202. package/dist/mods/treenity/schema-form.d.ts.map +1 -0
  203. package/dist/mods/treenity/schema-form.js +38 -0
  204. package/dist/mods/treenity/schema-form.js.map +1 -0
  205. package/dist/mods/treenity/seed.d.ts +2 -0
  206. package/dist/mods/treenity/seed.d.ts.map +1 -0
  207. package/dist/mods/treenity/seed.js +53 -0
  208. package/dist/mods/treenity/seed.js.map +1 -0
  209. package/dist/mods/treenity/server.d.ts +2 -0
  210. package/dist/mods/treenity/server.d.ts.map +1 -0
  211. package/dist/mods/treenity/server.js +2 -0
  212. package/dist/mods/treenity/server.js.map +1 -0
  213. package/dist/mods/treenity/type-view.d.ts +2 -0
  214. package/dist/mods/treenity/type-view.d.ts.map +1 -0
  215. package/dist/mods/treenity/type-view.js +36 -0
  216. package/dist/mods/treenity/type-view.js.map +1 -0
  217. package/dist/remote-tree.d.ts +6 -0
  218. package/dist/remote-tree.d.ts.map +1 -0
  219. package/dist/remote-tree.js +18 -0
  220. package/dist/remote-tree.js.map +1 -0
  221. package/dist/schema-loader.d.ts +19 -0
  222. package/dist/schema-loader.d.ts.map +1 -0
  223. package/dist/schema-loader.js +63 -0
  224. package/dist/schema-loader.js.map +1 -0
  225. package/dist/trpc.d.ts +187 -0
  226. package/dist/trpc.d.ts.map +1 -0
  227. package/dist/trpc.js +21 -0
  228. package/dist/trpc.js.map +1 -0
  229. package/package.json +88 -0
  230. package/src/AclEditor.tsx +330 -0
  231. package/src/App.tsx +775 -0
  232. package/src/CLAUDE.md +16 -0
  233. package/src/Inspector.tsx +857 -0
  234. package/src/Tree.tsx +237 -0
  235. package/src/ViewPage.tsx +45 -0
  236. package/src/bind/bind.test.ts +316 -0
  237. package/src/bind/computed.ts +64 -0
  238. package/src/bind/engine.ts +198 -0
  239. package/src/bind/eval.ts +108 -0
  240. package/src/bind/hook.ts +112 -0
  241. package/src/bind/parse.ts +104 -0
  242. package/src/bind/pipes.ts +71 -0
  243. package/src/cache.test.ts +139 -0
  244. package/src/cache.ts +244 -0
  245. package/src/client-tree.test.ts +116 -0
  246. package/src/client-tree.ts +24 -0
  247. package/src/client.ts +11 -0
  248. package/src/components/ui/accordion.tsx +63 -0
  249. package/src/components/ui/badge.tsx +27 -0
  250. package/src/components/ui/button.tsx +44 -0
  251. package/src/components/ui/checkbox.tsx +19 -0
  252. package/src/components/ui/dialog.tsx +156 -0
  253. package/src/components/ui/drawer.tsx +132 -0
  254. package/src/components/ui/input.tsx +19 -0
  255. package/src/components/ui/label.tsx +21 -0
  256. package/src/components/ui/popover.tsx +86 -0
  257. package/src/components/ui/progress.tsx +30 -0
  258. package/src/components/ui/select.tsx +189 -0
  259. package/src/components/ui/slider.tsx +62 -0
  260. package/src/components/ui/sonner.tsx +32 -0
  261. package/src/components/ui/switch.tsx +34 -0
  262. package/src/components/ui/textarea.tsx +17 -0
  263. package/src/components/ui/tooltip.tsx +56 -0
  264. package/src/context/index.tsx +131 -0
  265. package/src/context.ts +1 -0
  266. package/src/hooks.ts +208 -0
  267. package/src/idb.ts +80 -0
  268. package/src/index.html +14 -0
  269. package/src/lib/minimd.css +28 -0
  270. package/src/lib/minimd.ts +95 -0
  271. package/src/lib/utils.ts +6 -0
  272. package/src/load-client.ts +5 -0
  273. package/src/main.tsx +22 -0
  274. package/src/mods/editor-ui/CLAUDE.md +3 -0
  275. package/src/mods/editor-ui/client.ts +8 -0
  276. package/src/mods/editor-ui/default-view.tsx +148 -0
  277. package/src/mods/editor-ui/dir-view.tsx +91 -0
  278. package/src/mods/editor-ui/form-fields.tsx +861 -0
  279. package/src/mods/editor-ui/layout-view.tsx +62 -0
  280. package/src/mods/editor-ui/list-items.tsx +63 -0
  281. package/src/mods/editor-ui/node-utils.ts +84 -0
  282. package/src/mods/editor-ui/user-view.tsx +101 -0
  283. package/src/mods/treenity/CLAUDE.md +7 -0
  284. package/src/mods/treenity/client.ts +6 -0
  285. package/src/mods/treenity/groups/index.tsx +65 -0
  286. package/src/mods/treenity/preview.tsx +133 -0
  287. package/src/mods/treenity/ref-view.tsx +87 -0
  288. package/src/mods/treenity/schema-form.tsx +65 -0
  289. package/src/mods/treenity/seed.ts +56 -0
  290. package/src/mods/treenity/server.ts +1 -0
  291. package/src/mods/treenity/type-view.tsx +116 -0
  292. package/src/remote-tree.test.ts +142 -0
  293. package/src/remote-tree.ts +25 -0
  294. package/src/schema-loader.ts +84 -0
  295. package/src/style.css +1269 -0
  296. package/src/trpc.ts +27 -0
  297. package/src/vite-env.d.ts +3 -0
@@ -0,0 +1,95 @@
1
+ // Minimal markdown → HTML for chat bubbles (~50 lines)
2
+ // Covers: headers, bold, italic, inline code, code blocks, links, lists, blockquotes, hr
3
+ import './minimd.css';
4
+
5
+ const esc = (s: string) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
6
+
7
+ function inline(s: string): string {
8
+ return esc(s)
9
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
10
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
11
+ .replace(/__(.+?)__/g, '<strong>$1</strong>')
12
+ .replace(/\*(.+?)\*/g, '<em>$1</em>')
13
+ .replace(/_(.+?)_/g, '<em>$1</em>')
14
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
15
+ }
16
+
17
+ export function minimd(src: string): string {
18
+ const lines = src.split('\n');
19
+ const out: string[] = [];
20
+ let i = 0;
21
+
22
+ while (i < lines.length) {
23
+ const line = lines[i];
24
+
25
+ // Fenced code block
26
+ if (line.startsWith('```')) {
27
+ const lang = line.slice(3).trim();
28
+ const code: string[] = [];
29
+ i++;
30
+ while (i < lines.length && !lines[i].startsWith('```')) {
31
+ code.push(lines[i]);
32
+ i++;
33
+ }
34
+ i++; // skip closing ```
35
+ out.push(`<pre${lang ? ` data-lang="${esc(lang)}"` : ''}><code>${esc(code.join('\n'))}</code></pre>`);
36
+ continue;
37
+ }
38
+
39
+ // Header
40
+ const hm = line.match(/^(#{1,4})\s+(.+)/);
41
+ if (hm) { out.push(`<h${hm[1].length}>${inline(hm[2])}</h${hm[1].length}>`); i++; continue; }
42
+
43
+ // HR
44
+ if (/^[-*_]{3,}\s*$/.test(line)) { out.push('<hr>'); i++; continue; }
45
+
46
+ // Blockquote
47
+ if (line.startsWith('> ')) { out.push(`<blockquote>${inline(line.slice(2))}</blockquote>`); i++; continue; }
48
+
49
+ // Unordered list
50
+ if (/^[-*+]\s/.test(line)) {
51
+ out.push('<ul>');
52
+ while (i < lines.length && /^[-*+]\s/.test(lines[i])) {
53
+ out.push(`<li>${inline(lines[i].replace(/^[-*+]\s/, ''))}</li>`);
54
+ i++;
55
+ }
56
+ out.push('</ul>');
57
+ continue;
58
+ }
59
+
60
+ // Ordered list
61
+ if (/^\d+\.\s/.test(line)) {
62
+ out.push('<ol>');
63
+ while (i < lines.length && /^\d+\.\s/.test(lines[i])) {
64
+ out.push(`<li>${inline(lines[i].replace(/^\d+\.\s/, ''))}</li>`);
65
+ i++;
66
+ }
67
+ out.push('</ol>');
68
+ continue;
69
+ }
70
+
71
+ // Table
72
+ if (line.startsWith('|') && i + 1 < lines.length && /^\|[-\s:|]+\|/.test(lines[i + 1])) {
73
+ const parseRow = (r: string) => r.split('|').slice(1, -1).map(c => c.trim());
74
+ const headers = parseRow(line);
75
+ i += 2; // skip header + separator
76
+ out.push('<table><thead><tr>' + headers.map(h => `<th>${inline(h)}</th>`).join('') + '</tr></thead><tbody>');
77
+ while (i < lines.length && lines[i].startsWith('|')) {
78
+ const cells = parseRow(lines[i]);
79
+ out.push('<tr>' + cells.map(c => `<td>${inline(c)}</td>`).join('') + '</tr>');
80
+ i++;
81
+ }
82
+ out.push('</tbody></table>');
83
+ continue;
84
+ }
85
+
86
+ // Empty line
87
+ if (!line.trim()) { i++; continue; }
88
+
89
+ // Paragraph
90
+ out.push(`<p>${inline(line)}</p>`);
91
+ i++;
92
+ }
93
+
94
+ return out.join('\n');
95
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,5 @@
1
+ // Client-side mod auto-discovery.
2
+ // Internal mods (packages/react/src/mods/*) — vite glob.
3
+ // External mods (mods/*, core/src/mods/*) — vite-plugin-mods virtual module.
4
+ import.meta.glob('./mods/*/client.ts', { eager: true });
5
+ import 'virtual:mod-clients';
package/src/main.tsx ADDED
@@ -0,0 +1,22 @@
1
+ import 'reflect-metadata';
2
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
+ import { enablePatches } from 'immer';
4
+ import { StrictMode } from 'react';
5
+ import { createRoot } from 'react-dom/client';
6
+ import { App } from './App';
7
+ import './load-client';
8
+ import './style.css';
9
+
10
+ enablePatches();
11
+
12
+ const queryClient = new QueryClient();
13
+
14
+ const root = document.getElementById('root');
15
+ if (!root) throw new Error('No #root element');
16
+ createRoot(root).render(
17
+ <StrictMode>
18
+ <QueryClientProvider client={queryClient}>
19
+ <App />
20
+ </QueryClientProvider>
21
+ </StrictMode>,
22
+ );
@@ -0,0 +1,3 @@
1
+ # editor-ui
2
+
3
+ View-слой админки. Fallback `react`-рендерер для всех нод/компонентов, специализированные views (layout, page, dir, user, list), schema-driven form fields для NodeEditor.
@@ -0,0 +1,8 @@
1
+ import './default-view';
2
+ import './layout-view';
3
+ import './user-view';
4
+ import './dir-view';
5
+ import './list-items';
6
+ import { registerFormFields } from './form-fields';
7
+
8
+ registerFormFields();
@@ -0,0 +1,148 @@
1
+ import { Render, RenderContext } from '#context';
2
+ import { useChildren } from '#hooks';
3
+ import { trpc } from '#trpc';
4
+ import { type ComponentData, type NodeData, register } from '@treenity/core/core';
5
+ import { useCallback, useState } from 'react';
6
+ import { getComponents, getPlainFields, getSchema } from './node-utils';
7
+
8
+ /** Fallback for components without their own react handler */
9
+ function ComponentFieldsView({ value }: { value: ComponentData }) {
10
+ const s = getSchema(value.$type);
11
+ const fields = s ? Object.entries(s.properties) : [];
12
+
13
+ if (fields.length > 0) {
14
+ return (
15
+ <>
16
+ {fields.map(([k, prop]) => {
17
+ const val = (value as any)[k];
18
+ return (
19
+ <div key={k} className="comp-view-row">
20
+ <span className="comp-view-label">{(prop as any).title || k}</span>
21
+ <span className="comp-view-value">
22
+ {val === undefined || val === '' ? '—' : typeof val === 'object' ? JSON.stringify(val) : String(val)}
23
+ </span>
24
+ </div>
25
+ );
26
+ })}
27
+ </>
28
+ );
29
+ }
30
+
31
+ const entries = Object.entries(value).filter(([k]) => !k.startsWith('$'));
32
+ if (entries.length === 0) return null;
33
+
34
+ return (
35
+ <>
36
+ {entries.map(([k, v]) => (
37
+ <div key={k} className="comp-view-row">
38
+ <span className="comp-view-label">{k}</span>
39
+ <span className="comp-view-value">
40
+ {typeof v === 'string' ? v || '—' : typeof v === 'object' ? JSON.stringify(v) : String(v)}
41
+ </span>
42
+ </div>
43
+ ))}
44
+ </>
45
+ );
46
+ }
47
+
48
+ function GenerateViewButton({ type, sample }: { type: string; sample: NodeData }) {
49
+ const [status, setStatus] = useState<'idle' | 'generating' | 'done' | 'error'>('idle');
50
+ const [error, setError] = useState('');
51
+
52
+ const generate = useCallback(async () => {
53
+ setStatus('generating');
54
+ try {
55
+ const clean = Object.fromEntries(
56
+ Object.entries(sample).filter(([k]) => !['$rev', '$acl', '$owner'].includes(k)),
57
+ );
58
+ await trpc.execute.mutate({
59
+ path: '/metatron',
60
+ action: 'task',
61
+ data: { prompt: `Generate a React view for type "${type}". Sample data:\n${JSON.stringify(clean, null, 2)}` },
62
+ });
63
+ setStatus('done');
64
+ } catch (err: any) {
65
+ setError(err.message);
66
+ setStatus('error');
67
+ }
68
+ }, [type, sample]);
69
+
70
+ if (status === 'generating') {
71
+ return <div className="text-sm text-blue-400 animate-pulse py-2">Creating task...</div>;
72
+ }
73
+ if (status === 'error') {
74
+ return <div className="text-sm text-red-400 py-2">{error}</div>;
75
+ }
76
+ if (status === 'done') {
77
+ return <div className="text-sm text-green-400 py-2">Task created in /metatron — waiting for AI</div>;
78
+ }
79
+
80
+ return (
81
+ <button
82
+ onClick={generate}
83
+ className="text-sm text-blue-400 hover:text-blue-300 border border-blue-400/30 rounded px-3 py-1.5 my-2"
84
+ >
85
+ Generate AI View
86
+ </button>
87
+ );
88
+ }
89
+
90
+ function DefaultNodeView({ value }: { value: NodeData }) {
91
+ const children = useChildren(value.$path);
92
+ const plain = getPlainFields(value);
93
+ const components = getComponents(value);
94
+ const hasInfo = Object.keys(plain).length > 0 || components.length > 0;
95
+ const canGenerate = value.$type.includes('.');
96
+
97
+ return (
98
+ <div className="node-default-view">
99
+ {canGenerate && <GenerateViewButton type={value.$type} sample={value} />}
100
+
101
+ {components.map(([name, comp]) => {
102
+ const ctype = (comp as any).$type;
103
+ return (
104
+ <div key={name} className="comp-view-card">
105
+ <div className="comp-view-header">
106
+ {name}
107
+ {name !== ctype && <span className="comp-type">{ctype}</span>}
108
+ </div>
109
+ <Render value={comp as ComponentData} />
110
+ </div>
111
+ );
112
+ })}
113
+
114
+ {Object.keys(plain).length > 0 && (
115
+ <div className="node-info-bar">
116
+ {Object.entries(plain).map(([k, v]) => (
117
+ <span key={k} className="node-info-chip data">
118
+ <span className="node-info-chip-label">{k}</span>
119
+ <span className="node-info-chip-val">
120
+ {typeof v === 'string' ? v : JSON.stringify(v)}
121
+ </span>
122
+ </span>
123
+ ))}
124
+ </div>
125
+ )}
126
+
127
+ {children.length > 0 && (
128
+ <RenderContext name="react:list">
129
+ <div className="children-grid">
130
+ {children.map((child) => (
131
+ <Render key={child.$path} value={child} />
132
+ ))}
133
+ </div>
134
+ </RenderContext>
135
+ )}
136
+
137
+ {children.length === 0 && !hasInfo && <div className="node-empty">Empty node</div>}
138
+ </div>
139
+ );
140
+ }
141
+
142
+ /** Dispatch: node → DefaultNodeView, component → ComponentFieldsView */
143
+ function DefaultView({ value }: { value: ComponentData }) {
144
+ if ('$path' in value) return <DefaultNodeView value={value as NodeData} />;
145
+ return <ComponentFieldsView value={value} />;
146
+ }
147
+
148
+ register('default', 'react', DefaultView as any);
@@ -0,0 +1,91 @@
1
+ import { Render, RenderContext } from '#context';
2
+ import { useChildren } from '#hooks';
3
+ import { type NodeData, register } from '@treenity/core/core';
4
+
5
+ const STATUS_COLORS: Record<string, [string, string]> = {
6
+ draft: ['var(--accent-subtle, #1a2a3a)', 'var(--accent)'],
7
+ published: ['#1a2e1a', '#4c8'],
8
+ archived: ['#2e2a1a', '#ca4'],
9
+ };
10
+
11
+ function FolderView({ value }: { value: NodeData }) {
12
+ const children = useChildren(value.$path);
13
+ const meta = value.metadata as
14
+ | { $type: string; title?: string; description?: string }
15
+ | undefined;
16
+ const status = value.status as { $type: string; value?: string } | undefined;
17
+ const counter = value.counter as { $type: string; count?: number } | undefined;
18
+
19
+ return (
20
+ <div className="node-default-view">
21
+ {(meta || status || counter) && (
22
+ <div
23
+ style={{
24
+ padding: 16,
25
+ background: 'var(--surface)',
26
+ borderRadius: 'var(--radius)',
27
+ marginBottom: 12,
28
+ }}
29
+ >
30
+ {meta?.title && <div style={{ fontSize: 18, fontWeight: 600 }}>{meta.title}</div>}
31
+ {meta?.description && (
32
+ <div style={{ fontSize: 13, color: 'var(--text-2)', marginTop: 4 }}>
33
+ {meta.description}
34
+ </div>
35
+ )}
36
+ {(status || counter) && (
37
+ <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
38
+ {status?.value &&
39
+ (() => {
40
+ const [bg, fg] = STATUS_COLORS[status.value] ?? STATUS_COLORS.draft;
41
+ return (
42
+ <span
43
+ style={{
44
+ padding: '2px 10px',
45
+ borderRadius: 12,
46
+ fontSize: 11,
47
+ fontWeight: 600,
48
+ background: bg,
49
+ color: fg,
50
+ }}
51
+ >
52
+ {status.value}
53
+ </span>
54
+ );
55
+ })()}
56
+ {counter != null && (
57
+ <span
58
+ style={{
59
+ padding: '2px 10px',
60
+ borderRadius: 12,
61
+ fontSize: 11,
62
+ fontWeight: 600,
63
+ background: 'var(--surface)',
64
+ border: '1px solid var(--border)',
65
+ color: 'var(--text-2)',
66
+ }}
67
+ >
68
+ count: {counter.count ?? 0}
69
+ </span>
70
+ )}
71
+ </div>
72
+ )}
73
+ </div>
74
+ )}
75
+ {children.length > 0 && (
76
+ <RenderContext name="react:list">
77
+ <div className="children-grid">
78
+ {children.map((child) => (
79
+ <Render key={child.$path} value={child} />
80
+ ))}
81
+ </div>
82
+ </RenderContext>
83
+ )}
84
+ {children.length === 0 && !meta && !status && !counter && (
85
+ <div className="node-empty">Empty folder</div>
86
+ )}
87
+ </div>
88
+ );
89
+ }
90
+
91
+ register('dir', 'react', FolderView as any);