@almadar/std 16.235.0 → 16.237.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 (283) hide show
  1. package/behaviors/behaviors-registry.json +5621 -1993
  2. package/behaviors/lolo/infra/atoms/std-approval-gate.lolo +32 -41
  3. package/behaviors/lolo/infra/atoms/std-audit-capture.lolo +0 -5
  4. package/behaviors/lolo/infra/atoms/std-cache-aside.lolo +0 -24
  5. package/behaviors/lolo/infra/atoms/std-circuit-breaker.lolo +1 -17
  6. package/behaviors/lolo/infra/atoms/std-data-erasure.lolo +17 -24
  7. package/behaviors/lolo/infra/atoms/std-data-import.lolo +8 -11
  8. package/behaviors/lolo/infra/atoms/std-game-clock.lolo +2 -0
  9. package/behaviors/lolo/infra/atoms/std-knowledge-loop.lolo +0 -5
  10. package/behaviors/lolo/infra/atoms/std-lifecycle.lolo +3 -1
  11. package/behaviors/lolo/infra/atoms/std-migration-job.lolo +2 -2
  12. package/behaviors/lolo/infra/atoms/std-mod-queue.lolo +40 -18
  13. package/behaviors/lolo/infra/atoms/std-notification-preferences.lolo +25 -11
  14. package/behaviors/lolo/infra/atoms/std-notify-on-event.lolo +0 -5
  15. package/behaviors/lolo/infra/atoms/std-permission-matrix.lolo +28 -11
  16. package/behaviors/lolo/infra/atoms/std-rate-limiter.lolo +84 -22
  17. package/behaviors/lolo/infra/atoms/std-status-lifecycle.lolo +1 -6
  18. package/behaviors/lolo/ml/atoms/std-graph-builder.lolo +3 -1
  19. package/behaviors/lolo/ml/atoms/std-ml-classify.lolo +0 -4
  20. package/behaviors/lolo/ml/atoms/std-ml-similarity.lolo +0 -4
  21. package/behaviors/lolo/ui/avl/atoms/ui-avl-state-machine.lolo +12 -0
  22. package/behaviors/lolo/ui/core/atoms/std-app-layout.lolo +55 -9
  23. package/behaviors/lolo/ui/core/atoms/std-app-search.lolo +6 -2
  24. package/behaviors/lolo/ui/core/atoms/std-billable-hour.lolo +19 -14
  25. package/behaviors/lolo/ui/core/atoms/std-board.lolo +77 -27
  26. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +76 -33
  27. package/behaviors/lolo/ui/core/atoms/std-calendar.lolo +7 -3
  28. package/behaviors/lolo/ui/core/atoms/std-esign-flow.lolo +25 -13
  29. package/behaviors/lolo/ui/core/atoms/std-esign-request.lolo +5 -0
  30. package/behaviors/lolo/ui/core/atoms/std-event-log.lolo +32 -5
  31. package/behaviors/lolo/ui/core/atoms/std-export.lolo +0 -10
  32. package/behaviors/lolo/ui/core/atoms/std-file-store.lolo +15 -4
  33. package/behaviors/lolo/ui/core/atoms/std-form-advanced.lolo +1 -6
  34. package/behaviors/lolo/ui/core/atoms/std-gantt-schedule.lolo +8 -1
  35. package/behaviors/lolo/ui/core/atoms/std-geosearch.lolo +1 -0
  36. package/behaviors/lolo/ui/core/atoms/std-graphs.lolo +20 -4
  37. package/behaviors/lolo/ui/core/atoms/std-image-upload-multi.lolo +2 -2
  38. package/behaviors/lolo/ui/core/atoms/std-import.lolo +0 -5
  39. package/behaviors/lolo/ui/core/atoms/std-list.lolo +53 -32
  40. package/behaviors/lolo/ui/core/atoms/std-modal-editor.lolo +2 -2
  41. package/behaviors/lolo/ui/core/atoms/std-multi-party-flow.lolo +24 -0
  42. package/behaviors/lolo/ui/core/atoms/std-rating-review.lolo +12 -13
  43. package/behaviors/lolo/ui/core/atoms/std-record-detail.lolo +29 -13
  44. package/behaviors/lolo/ui/core/atoms/std-recurrence.lolo +14 -1
  45. package/behaviors/lolo/ui/core/atoms/std-role-gate.lolo +39 -0
  46. package/behaviors/lolo/ui/core/atoms/std-saved-search.lolo +84 -16
  47. package/behaviors/lolo/ui/core/atoms/std-selection.lolo +22 -10
  48. package/behaviors/lolo/ui/core/atoms/std-stats.lolo +5 -5
  49. package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +3 -0
  50. package/behaviors/lolo/ui/core/atoms/std-thread.lolo +41 -73
  51. package/behaviors/lolo/ui/core/atoms/std-version-history.lolo +27 -12
  52. package/behaviors/lolo/ui/core/atoms/std-vote.lolo +0 -4
  53. package/behaviors/lolo/ui/core/atoms/std-wizard.lolo +3 -3
  54. package/behaviors/lolo/ui/core/atoms/ui-presence.lolo +21 -0
  55. package/behaviors/lolo/ui/core/atoms/ui-time-slot-cell.lolo +36 -0
  56. package/behaviors/lolo/ui/core/atoms/ui-typography.lolo +5 -0
  57. package/behaviors/lolo/ui/core/molecules/ui-alert.lolo +5 -0
  58. package/behaviors/lolo/ui/core/molecules/ui-animated-counter.lolo +1 -1
  59. package/behaviors/lolo/ui/core/molecules/ui-breadcrumb.lolo +6 -1
  60. package/behaviors/lolo/ui/core/molecules/ui-chart.lolo +11 -1
  61. package/behaviors/lolo/ui/core/molecules/ui-code-block.lolo +13 -8
  62. package/behaviors/lolo/ui/core/molecules/ui-command-palette.lolo +11 -6
  63. package/behaviors/lolo/ui/core/molecules/ui-commit-row.lolo +2 -2
  64. package/behaviors/lolo/ui/core/molecules/ui-confirm-dialog.lolo +8 -0
  65. package/behaviors/lolo/ui/core/molecules/ui-data-grid.lolo +60 -5
  66. package/behaviors/lolo/ui/core/molecules/ui-data-list.lolo +47 -3
  67. package/behaviors/lolo/ui/core/molecules/ui-date-range-picker.lolo +3 -3
  68. package/behaviors/lolo/ui/core/molecules/ui-document-details.lolo +31 -9
  69. package/behaviors/lolo/ui/core/molecules/ui-document-panel.lolo +8 -3
  70. package/behaviors/lolo/ui/core/molecules/ui-document-viewer.lolo +6 -1
  71. package/behaviors/lolo/ui/core/molecules/ui-drawer.lolo +13 -0
  72. package/behaviors/lolo/ui/core/molecules/ui-emoji-picker.lolo +1 -1
  73. package/behaviors/lolo/ui/core/molecules/ui-feature-card.lolo +1 -1
  74. package/behaviors/lolo/ui/core/molecules/ui-floating-action-button.lolo +6 -1
  75. package/behaviors/lolo/ui/core/molecules/ui-floating-toolbar.lolo +6 -1
  76. package/behaviors/lolo/ui/core/molecules/ui-form-actions.lolo +6 -1
  77. package/behaviors/lolo/ui/core/molecules/ui-gantt.lolo +9 -1
  78. package/behaviors/lolo/ui/core/molecules/ui-graph-canvas.lolo +6 -1
  79. package/behaviors/lolo/ui/core/molecules/ui-import-preview-tree.lolo +3 -2
  80. package/behaviors/lolo/ui/core/molecules/ui-likert-scale.lolo +1 -1
  81. package/behaviors/lolo/ui/core/molecules/ui-menu.lolo +7 -1
  82. package/behaviors/lolo/ui/core/molecules/ui-meter.lolo +6 -1
  83. package/behaviors/lolo/ui/core/molecules/ui-modal.lolo +5 -0
  84. package/behaviors/lolo/ui/core/molecules/ui-page-header.lolo +11 -1
  85. package/behaviors/lolo/ui/core/molecules/ui-page-transition.lolo +1 -1
  86. package/behaviors/lolo/ui/core/molecules/ui-swipeable-row.lolo +44 -15
  87. package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +57 -5
  88. package/behaviors/lolo/ui/core/molecules/ui-tabs.lolo +8 -2
  89. package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +87 -16
  90. package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +37 -10
  91. package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +46 -8
  92. package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +36 -10
  93. package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +57 -10
  94. package/behaviors/lolo/ui/core/organisms/ui-form.lolo +57 -10
  95. package/behaviors/lolo/ui/core/organisms/ui-media-gallery.lolo +22 -3
  96. package/behaviors/lolo/ui/core/organisms/ui-stat-card.lolo +6 -0
  97. package/behaviors/lolo/ui/core/organisms/ui-state-machine-view.lolo +12 -0
  98. package/behaviors/lolo/ui/core/organisms/ui-timeline.lolo +38 -5
  99. package/behaviors/lolo/ui/core/templates/ui-auth-layout.lolo +1 -1
  100. package/behaviors/lolo/ui/core/templates/ui-counter-template.lolo +3 -45
  101. package/behaviors/lolo/ui/core/templates/ui-dashboard-layout.lolo +7 -2
  102. package/behaviors/lolo/ui/core/templates/ui-generic-app-template.lolo +2 -37
  103. package/behaviors/lolo/ui/game/.hand-authored.json +2 -2
  104. package/behaviors/lolo/ui/game/atoms/ui-choice-button.lolo +1 -1
  105. package/behaviors/lolo/ui/game/atoms/ui-dialogue-bubble.lolo +2 -2
  106. package/behaviors/lolo/ui/game/atoms/ui-game-menu.lolo +9 -3
  107. package/behaviors/lolo/ui/game/atoms/ui-score-display.lolo +1 -1
  108. package/behaviors/lolo/ui/game/organisms/std-arcade-board-3d.lolo +2 -2
  109. package/behaviors/lolo/ui/game/organisms/std-crew-board-2d.lolo +0 -4
  110. package/behaviors/lolo/ui/game/organisms/std-platformer-puzzle.lolo +2 -66
  111. package/behaviors/lolo/ui/game/organisms/std-tactics-board-2d.lolo +0 -4
  112. package/behaviors/lolo/ui/game/organisms/std-worldmap-board-2d.lolo +0 -4
  113. package/behaviors/lolo/ui/learning/atoms/std-learn-stepper.lolo +6 -6
  114. package/behaviors/lolo-behavior-contracts.json +1 -1
  115. package/behaviors/registry/factory-signatures.json +1 -1
  116. package/behaviors/registry/infra/atoms/std-approval-gate.orb +194 -67
  117. package/behaviors/registry/infra/atoms/std-audit-capture.orb +0 -8
  118. package/behaviors/registry/infra/atoms/std-cache-aside.orb +0 -84
  119. package/behaviors/registry/infra/atoms/std-circuit-breaker.orb +1 -44
  120. package/behaviors/registry/infra/atoms/std-data-erasure.orb +124 -55
  121. package/behaviors/registry/infra/atoms/std-data-import.orb +12 -11
  122. package/behaviors/registry/infra/atoms/std-game-clock.orb +31 -0
  123. package/behaviors/registry/infra/atoms/std-knowledge-loop.orb +0 -57
  124. package/behaviors/registry/infra/atoms/std-lifecycle.orb +43 -0
  125. package/behaviors/registry/infra/atoms/std-migration-job.orb +26 -1
  126. package/behaviors/registry/infra/atoms/std-mod-queue.orb +379 -103
  127. package/behaviors/registry/infra/atoms/std-notification-preferences.orb +135 -12
  128. package/behaviors/registry/infra/atoms/std-notify-on-event.orb +0 -11
  129. package/behaviors/registry/infra/atoms/std-permission-matrix.orb +137 -12
  130. package/behaviors/registry/infra/atoms/std-rate-limiter.orb +287 -72
  131. package/behaviors/registry/infra/atoms/std-status-lifecycle.orb +5 -8
  132. package/behaviors/registry/ml/atoms/std-graph-builder.orb +11 -0
  133. package/behaviors/registry/ml/atoms/std-ml-classify.orb +0 -7
  134. package/behaviors/registry/ml/atoms/std-ml-similarity.orb +0 -7
  135. package/behaviors/registry/ui/avl/atoms/ui-avl-state-machine.orb +82 -0
  136. package/behaviors/registry/ui/core/atoms/std-app-layout.orb +417 -9
  137. package/behaviors/registry/ui/core/atoms/std-app-search.orb +53 -1
  138. package/behaviors/registry/ui/core/atoms/std-billable-hour.orb +209 -19
  139. package/behaviors/registry/ui/core/atoms/std-board.orb +242 -61
  140. package/behaviors/registry/ui/core/atoms/std-browse.orb +499 -49
  141. package/behaviors/registry/ui/core/atoms/std-calendar.orb +96 -2
  142. package/behaviors/registry/ui/core/atoms/std-esign-flow.orb +122 -15
  143. package/behaviors/registry/ui/core/atoms/std-esign-request.orb +11 -0
  144. package/behaviors/registry/ui/core/atoms/std-event-log.orb +42 -6
  145. package/behaviors/registry/ui/core/atoms/std-export.orb +0 -16
  146. package/behaviors/registry/ui/core/atoms/std-file-store.orb +121 -3
  147. package/behaviors/registry/ui/core/atoms/std-form-advanced.orb +1 -43
  148. package/behaviors/registry/ui/core/atoms/std-gantt-schedule.orb +25 -1
  149. package/behaviors/registry/ui/core/atoms/std-geosearch.orb +5 -0
  150. package/behaviors/registry/ui/core/atoms/std-graphs.orb +173 -66
  151. package/behaviors/registry/ui/core/atoms/std-image-upload-multi.orb +4 -0
  152. package/behaviors/registry/ui/core/atoms/std-import.orb +0 -8
  153. package/behaviors/registry/ui/core/atoms/std-list.orb +237 -34
  154. package/behaviors/registry/ui/core/atoms/std-modal-editor.orb +10 -1
  155. package/behaviors/registry/ui/core/atoms/std-multi-party-flow.orb +24 -0
  156. package/behaviors/registry/ui/core/atoms/std-rating-review.orb +37 -19
  157. package/behaviors/registry/ui/core/atoms/std-record-detail.orb +275 -14
  158. package/behaviors/registry/ui/core/atoms/std-recurrence.orb +93 -4
  159. package/behaviors/registry/ui/core/atoms/std-role-gate.orb +217 -0
  160. package/behaviors/registry/ui/core/atoms/std-saved-search.orb +473 -19
  161. package/behaviors/registry/ui/core/atoms/std-selection.orb +179 -10
  162. package/behaviors/registry/ui/core/atoms/std-stats.orb +73 -1
  163. package/behaviors/registry/ui/core/atoms/std-tag-taxonomy.orb +3 -0
  164. package/behaviors/registry/ui/core/atoms/std-thread.orb +190 -115
  165. package/behaviors/registry/ui/core/atoms/std-version-history.orb +138 -14
  166. package/behaviors/registry/ui/core/atoms/std-vote.orb +0 -9
  167. package/behaviors/registry/ui/core/atoms/std-wizard.orb +33 -3
  168. package/behaviors/registry/ui/core/atoms/ui-presence.orb +59 -0
  169. package/behaviors/registry/ui/core/atoms/ui-time-slot-cell.orb +132 -0
  170. package/behaviors/registry/ui/core/atoms/ui-typography.orb +7 -0
  171. package/behaviors/registry/ui/core/molecules/ui-alert.orb +7 -0
  172. package/behaviors/registry/ui/core/molecules/ui-animated-counter.orb +1 -1
  173. package/behaviors/registry/ui/core/molecules/ui-breadcrumb.orb +53 -1
  174. package/behaviors/registry/ui/core/molecules/ui-chart.orb +68 -1
  175. package/behaviors/registry/ui/core/molecules/ui-code-block.orb +291 -8
  176. package/behaviors/registry/ui/core/molecules/ui-command-palette.orb +89 -6
  177. package/behaviors/registry/ui/core/molecules/ui-commit-row.orb +3 -2
  178. package/behaviors/registry/ui/core/molecules/ui-confirm-dialog.orb +32 -0
  179. package/behaviors/registry/ui/core/molecules/ui-data-grid.orb +297 -5
  180. package/behaviors/registry/ui/core/molecules/ui-data-list.orb +261 -3
  181. package/behaviors/registry/ui/core/molecules/ui-date-range-picker.orb +1 -3
  182. package/behaviors/registry/ui/core/molecules/ui-document-details.orb +93 -24
  183. package/behaviors/registry/ui/core/molecules/ui-document-panel.orb +121 -3
  184. package/behaviors/registry/ui/core/molecules/ui-document-viewer.orb +53 -1
  185. package/behaviors/registry/ui/core/molecules/ui-drawer.orb +40 -0
  186. package/behaviors/registry/ui/core/molecules/ui-emoji-picker.orb +0 -1
  187. package/behaviors/registry/ui/core/molecules/ui-feature-card.orb +0 -1
  188. package/behaviors/registry/ui/core/molecules/ui-floating-action-button.orb +53 -1
  189. package/behaviors/registry/ui/core/molecules/ui-floating-toolbar.orb +53 -1
  190. package/behaviors/registry/ui/core/molecules/ui-form-actions.orb +53 -1
  191. package/behaviors/registry/ui/core/molecules/ui-gantt.orb +28 -1
  192. package/behaviors/registry/ui/core/molecules/ui-graph-canvas.orb +53 -1
  193. package/behaviors/registry/ui/core/molecules/ui-import-preview-tree.orb +9 -2
  194. package/behaviors/registry/ui/core/molecules/ui-likert-scale.orb +1 -1
  195. package/behaviors/registry/ui/core/molecules/ui-menu.orb +74 -1
  196. package/behaviors/registry/ui/core/molecules/ui-meter.orb +53 -1
  197. package/behaviors/registry/ui/core/molecules/ui-modal.orb +8 -0
  198. package/behaviors/registry/ui/core/molecules/ui-page-header.orb +60 -1
  199. package/behaviors/registry/ui/core/molecules/ui-page-transition.orb +1 -1
  200. package/behaviors/registry/ui/core/molecules/ui-swipeable-row.orb +393 -23
  201. package/behaviors/registry/ui/core/molecules/ui-table-view.orb +337 -5
  202. package/behaviors/registry/ui/core/molecules/ui-tabs.orb +99 -2
  203. package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +415 -33
  204. package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +340 -29
  205. package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +296 -20
  206. package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +489 -9
  207. package/behaviors/registry/ui/core/organisms/ui-form-section.orb +132 -13
  208. package/behaviors/registry/ui/core/organisms/ui-form.orb +132 -13
  209. package/behaviors/registry/ui/core/organisms/ui-media-gallery.orb +141 -3
  210. package/behaviors/registry/ui/core/organisms/ui-stat-card.orb +58 -0
  211. package/behaviors/registry/ui/core/organisms/ui-state-machine-view.orb +82 -0
  212. package/behaviors/registry/ui/core/organisms/ui-timeline.orb +159 -8
  213. package/behaviors/registry/ui/core/templates/ui-auth-layout.orb +0 -1
  214. package/behaviors/registry/ui/core/templates/ui-counter-template.orb +1 -173
  215. package/behaviors/registry/ui/core/templates/ui-dashboard-layout.orb +53 -2
  216. package/behaviors/registry/ui/core/templates/ui-generic-app-template.orb +1 -116
  217. package/behaviors/registry/ui/game/atoms/ui-choice-button.orb +1 -1
  218. package/behaviors/registry/ui/game/atoms/ui-dialogue-bubble.orb +1 -2
  219. package/behaviors/registry/ui/game/atoms/ui-game-menu.orb +100 -3
  220. package/behaviors/registry/ui/game/atoms/ui-score-display.orb +0 -1
  221. package/behaviors/registry/ui/game/organisms/std-arcade-board-3d.orb +10 -0
  222. package/behaviors/registry/ui/game/organisms/std-crew-board-2d.orb +0 -7
  223. package/behaviors/registry/ui/game/organisms/std-platformer-puzzle.orb +1 -807
  224. package/behaviors/registry/ui/game/organisms/std-tactics-board-2d.orb +0 -7
  225. package/behaviors/registry/ui/game/organisms/std-worldmap-board-2d.orb +0 -7
  226. package/behaviors/registry/ui/learning/atoms/std-learn-stepper.orb +6 -0
  227. package/canonical-operators.json +18 -1
  228. package/dist/behaviors/exports-reader.js +8942 -3946
  229. package/dist/behaviors/functions/index.d.ts +419 -132
  230. package/dist/behaviors/functions/index.js +8931 -3946
  231. package/dist/behaviors/index.d.ts +1 -1
  232. package/dist/behaviors/index.js +8943 -3947
  233. package/dist/behaviors/query.js +8942 -3946
  234. package/dist/behaviors-embeddings.hash.json +3 -3
  235. package/dist/behaviors-embeddings.json +49 -41
  236. package/dist/behaviors-registry.json +5621 -1993
  237. package/dist/index.d.ts +3 -3
  238. package/dist/index.js +8960 -3947
  239. package/dist/knob-embeddings.hash.json +3 -3
  240. package/dist/knob-embeddings.json +774 -374
  241. package/dist/modules/anim.d.ts +1 -1
  242. package/dist/modules/array.d.ts +1 -1
  243. package/dist/modules/array.js +15 -0
  244. package/dist/modules/async.d.ts +1 -1
  245. package/dist/modules/behavior.d.ts +1 -1
  246. package/dist/modules/browser.d.ts +1 -1
  247. package/dist/modules/composition.d.ts +1 -1
  248. package/dist/modules/contract.d.ts +1 -1
  249. package/dist/modules/core.d.ts +1 -1
  250. package/dist/modules/core.js +2 -0
  251. package/dist/modules/data.d.ts +1 -1
  252. package/dist/modules/ease.d.ts +1 -1
  253. package/dist/modules/format.d.ts +1 -1
  254. package/dist/modules/geo.d.ts +1 -1
  255. package/dist/modules/graph.d.ts +1 -1
  256. package/dist/modules/grid.d.ts +1 -1
  257. package/dist/modules/index.d.ts +1 -1
  258. package/dist/modules/index.js +17 -0
  259. package/dist/modules/integration.d.ts +1 -1
  260. package/dist/modules/json.d.ts +1 -1
  261. package/dist/modules/llm.d.ts +1 -1
  262. package/dist/modules/math.d.ts +1 -1
  263. package/dist/modules/memory.d.ts +1 -1
  264. package/dist/modules/nn.d.ts +1 -1
  265. package/dist/modules/noise.d.ts +1 -1
  266. package/dist/modules/object.d.ts +1 -1
  267. package/dist/modules/os.d.ts +1 -1
  268. package/dist/modules/path.d.ts +1 -1
  269. package/dist/modules/prob.d.ts +1 -1
  270. package/dist/modules/session.d.ts +1 -1
  271. package/dist/modules/str.d.ts +1 -1
  272. package/dist/modules/tensor.d.ts +1 -1
  273. package/dist/modules/time.d.ts +1 -1
  274. package/dist/modules/trace.d.ts +1 -1
  275. package/dist/modules/train.d.ts +1 -1
  276. package/dist/modules/validate.d.ts +1 -1
  277. package/dist/modules/vector.d.ts +1 -1
  278. package/dist/modules/workspace.d.ts +1 -1
  279. package/dist/registry/factory-signatures.json +1 -1
  280. package/dist/registry.d.ts +2 -2
  281. package/dist/registry.js +17 -0
  282. package/dist/{types-Ggla7OK7.d.ts → types-DLx38UFZ.d.ts} +23 -3
  283. package/package.json +3 -3
@@ -2,8 +2,9 @@ app std-record-detail "1.0.0"
2
2
  "std-record-detail — single primitive: detail-panel. Owns the fetch-one-record state machine (loading → viewing → error, with RETRY), the record counterpart to std-browse's collection machine. Consumers configure fields/sections/actions and the title/subtitle/status bindings; the atom fetches by the id (or, for slug-routed records, the slug) carried on INIT and renders the panel. `detailLook` (panel default / slide-over / page / document) selects the render body, exactly as std-browse's browseLook selects a collection shape. The `document` look is for records whose shape IS a document (wiki page, note, article): the pure writing surface — a big inline-editable title with the rich-text body DIRECTLY beneath it, Edit/Done and the actions menu tucked into the header corner — where clicking the body drops a cursor in, every typing pause autosaves (CONTENT_SAVED, which the host persists QUIETLY — no refetch, or the editor unmounts mid-keystroke), and Done emits CONTENT_COMMITTED (the host refetches and snapshots version history on this one). Metadata never sits between the title and the words: compose the sibling RecordItemDetails trait beside the doc for a Gutenberg-style settings rail. A row minted blank by a document-first create arrives with draft=1 on INIT and opens straight in the editor. Pair with std-detail-layout for the back + breadcrumb chrome — that atom owns navigation, this one owns the record — exactly as a list page pairs std-browse with std-app-layout."
3
3
  ;; @capabilities "record detail, single record view, entity detail panel, drill-down record, show one record, record profile, detail page, view details, document editor, wiki page, note taking, article body, rich text editing, write and edit content"
4
4
  orbital RecordItemOrbital {
5
- type RecordAction = { label : string!, event : event!, icon : string, variant : primary | secondary | ghost | danger }
6
- type RecordFieldSpec = { key : string!, header : string, format : date | currency | number | boolean | percent }
5
+ type RecordAction = { label : string!, event : event!, icon : string, variant : primary | secondary | ghost | danger, roles : [string], when : SExpr }
6
+ type BadgeColor = "default" | "primary" | "secondary" | "success" | "warning" | "danger" | "error" | "info" | "neutral" | "destructive"
7
+ type RecordFieldSpec = { key : string!, header : string, format : date | currency | number | boolean | percent, variant : h3 | h4 | body | caption | badge | small | progress, colorMap : Map string BadgeColor, labels : Map string string }
7
8
  type MetaFieldSpec = { key : string!, label : string, kind : text | boolean | select | readonly, options : [string], icon : string, format : date | currency | number | boolean | percent }
8
9
  type RecordSection = { title : string!, fields : [string] }
9
10
 
@@ -448,12 +449,15 @@ orbital RecordItemOrbital {
448
449
  fields : [RecordFieldSpec] = [{
449
450
  header: Name
450
451
  key: name
452
+ variant: h4
451
453
  }, {
452
454
  header: Description
453
455
  key: description
456
+ variant: body
454
457
  }, {
455
458
  header: Status
456
459
  key: status
460
+ variant: badge
457
461
  }, {
458
462
  format: date
459
463
  header: Created
@@ -463,6 +467,11 @@ orbital RecordItemOrbital {
463
467
  @description "Each entry maps one entity field to a labelled row in the panel. Unlike a list row, a detail panel SHOULD label every field — the value stands alone here with no column header or repeated row position to name it."
464
468
  @synonyms "detail fields, record fields, panel fields, shown fields"
465
469
  @tier "presentation"
470
+ closeEvent : event = ""
471
+ @label "Which action closes the record?"
472
+ @description "The `actions` entry whose event matches renders as the panel's close (×) button. Empty = no close button (a routed detail page navigates back instead)."
473
+ @synonyms "close event, dismiss event, close button"
474
+ @tier "presentation"
466
475
  sections : [RecordSection] = []
467
476
  @label "How should the fields be grouped?"
468
477
  @description "Optional titled groups over `fields`, e.g. [{title: Contact, fields: [email, phone]}]. Group into sections of 3-5 once a record carries more than about seven fields — a flat list past that is the cognitive-load problem Almadar_UX.md §1.4 (Miller) describes. Empty = one ungrouped list, the default."
@@ -481,6 +490,10 @@ orbital RecordItemOrbital {
481
490
  @description "Buttons on the panel header. One primary at most; the rest collapse past maxInlineActions."
482
491
  @synonyms "record actions, detail actions, panel buttons"
483
492
  @tier "presentation"
493
+ viewerRole : string = ""
494
+ @label "Viewer's role"
495
+ @description "The signed-in viewer's role that each action's `roles` is checked against. Bind it explicitly at the call site (e.g. `viewerRole: @user.role`) in an app whose identity carries a role; left empty, only actions with no `roles` show."
496
+ @tier "policy"
484
497
  maxInlineActions : number = 3
485
498
  @label "How many actions before collapsing?"
486
499
  @description "Actions beyond this count collapse into a '⋯' overflow menu."
@@ -560,10 +573,11 @@ orbital RecordItemOrbital {
560
573
  @tier "presentation"
561
574
  bodyContent : render-ui = {
562
575
  children: [{
563
- actions: @config.actions
564
- entity: ?data
576
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
577
+ entity: @entity.loadedRow
565
578
  fields: @config.fields
566
- initialData: ?data
579
+ closeEvent: @config.closeEvent
580
+ initialData: @entity.loadedRow
567
581
  maxInlineActions: @config.maxInlineActions
568
582
  sections: @config.sections
569
583
  showActions: @config.showActions
@@ -580,10 +594,11 @@ orbital RecordItemOrbital {
580
594
  @tier "internal"
581
595
  slideOverBodyContent : render-ui = {
582
596
  children: [{
583
- actions: @config.actions
584
- entity: ?data
597
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
598
+ entity: @entity.loadedRow
585
599
  fields: @config.fields
586
- initialData: ?data
600
+ closeEvent: @config.closeEvent
601
+ initialData: @entity.loadedRow
587
602
  maxInlineActions: @config.maxInlineActions
588
603
  sections: @config.sections
589
604
  showActions: @config.showActions
@@ -601,10 +616,11 @@ orbital RecordItemOrbital {
601
616
  @tier "internal"
602
617
  pageBodyContent : render-ui = {
603
618
  children: [{
604
- actions: @config.actions
605
- entity: ?data
619
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
620
+ entity: @entity.loadedRow
606
621
  fields: @config.fields
607
- initialData: ?data
622
+ closeEvent: @config.closeEvent
623
+ initialData: @entity.loadedRow
608
624
  maxInlineActions: @config.maxInlineActions
609
625
  sections: @config.sections
610
626
  showActions: @config.showActions
@@ -621,7 +637,7 @@ orbital RecordItemOrbital {
621
637
  @description "Render-ui SExpr rendered after the record loads when detailLook = page: a full record page, width-capped so field rows do not stretch past a readable measure (Almadar_UI_Beauty.md §3)."
622
638
  @tier "internal"
623
639
  documentBodyContent : render-ui = {
624
- actions: @config.actions
640
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
625
641
  coverImage: @entity.coverUrl
626
642
  icon: @entity.iconName
627
643
  editEvent: (if @config.allowEdit EDIT_CONTENT null)
@@ -640,7 +656,7 @@ orbital RecordItemOrbital {
640
656
  @description "Render-ui SExpr for detailLook = document while reading: the pure writing surface — editable title with the rich-text body DIRECTLY beneath it, Edit and the actions menu tucked into the header corner. Metadata never sits between title and content; compose the sibling RecordItemDetails trait beside this one for the settings rail. Binds only @entity fields, never ?data, because it is also painted by arms whose payload is not the record."
641
657
  @tier "internal"
642
658
  documentEditingContent : render-ui = {
643
- actions: @config.actions
659
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
644
660
  coverImage: @entity.coverUrl
645
661
  icon: @entity.iconName
646
662
  autosaveHint: @config.autosaveHint
@@ -1,6 +1,7 @@
1
1
  app std-recurrence "2.1.0"
2
2
  "std-recurrence — recurring-rule definition + upcoming runs (Stripe Subscriptions / Cron Hub style). The viewing state shows a plain-English rule sentence and the next N dated runs as an agenda timeline. Per-run exception modal supports skip / reschedule. Topology = loading / defining_rule / viewing / awaiting_exception_decision / cancelled / error."
3
3
  type ObjectSpec = { id : string, label : string, value : string }
4
+ type ItemAction = { label : string!, event : event, roles : [string] }
4
5
  orbital RecurrenceOrbital {
5
6
  type OccurrenceItem = { id : string!, title : string!, description : string, date : string, status : "complete" | "active" | "pending" | "error", rawStatus : "scheduled" | "skipped" | "completed" | "rescheduled" }
6
7
 
@@ -139,7 +140,11 @@ orbital RecurrenceOrbital {
139
140
  }, {
140
141
  entity: @entity.occurrences
141
142
  fields: (title description date status)
142
- itemActions: [{ event: OPEN_EXCEPTION, label: Manage }]
143
+ titleField: "title"
144
+ descriptionField: "description"
145
+ dateField: "date"
146
+ statusField: "status"
147
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
143
148
  look: @config.timelineLook
144
149
  type: timeline
145
150
  }, {
@@ -543,6 +548,14 @@ orbital RecurrenceOrbital {
543
548
  @label "Timeline look"
544
549
  @description "Layer 2 visual treatment for the timeline."
545
550
  @tier "presentation"
551
+ itemActions : [ItemAction] = [{ event: OPEN_EXCEPTION, label: Manage }]
552
+ @label "Row actions"
553
+ @description "Actions offered on each row; give an item `roles` to show it only to those viewer roles."
554
+ @tier "presentation"
555
+ viewerRole : string = ""
556
+ @label "Viewer's role"
557
+ @description "The signed-in viewer's role that each action's `roles` is checked against. Bind it explicitly at the call site (e.g. `viewerRole: @user.role`) in an app whose identity carries a role; left empty, only actions with no `roles` show."
558
+ @tier "policy"
546
559
  }
547
560
  }
548
561
 
@@ -0,0 +1,39 @@
1
+ app std-role-gate "1.0.0"
2
+ "std-role-gate — explicit role gate around ONE piece of UI. Renders its `content` trait only when `roles` is empty or lists the viewer's role; any other viewer gets nothing rendered. Wrap a single button, FAB or panel in it, bind `viewerRole` at the call site (e.g. `viewerRole: @user.role`), and list the roles that may see it."
3
+ ;; @capabilities "role gate, only for roles, hide for role, permission wrapper, restrict button to role, show only to admins, role-based visibility"
4
+ ;; @synonyms "role guard, permission gate, access gate, visible to roles, hide unless role, admin only"
5
+ orbital RoleGateOrbital {
6
+ entity RoleGateData [runtime] {
7
+ id : string!
8
+ }
9
+
10
+ trait RoleGate -> @rebindable RoleGateData [interaction, instance] {
11
+ initial: idle
12
+
13
+ state idle {
14
+ INIT -> idle when (or (= (array/len @config.roles) 0) (array/includes @config.roles @config.viewerRole))
15
+ (render-ui main { children: [@config.content], type: box })
16
+ INIT -> idle when (not (or (= (array/len @config.roles) 0) (array/includes @config.roles @config.viewerRole)))
17
+ (render-ui main null)
18
+ }
19
+
20
+ config {
21
+ content : trait
22
+ @label "Gated content"
23
+ @description "The trait rendered when the viewer's role is allowed, e.g. a button or panel trait ref."
24
+ @synonyms "wrapped content, guarded element, protected button"
25
+ @tier "presentation"
26
+ roles : [string] = []
27
+ @label "Visible to roles"
28
+ @description "Roles (matched against viewerRole) that see the content. Empty = everyone; any other viewer sees nothing."
29
+ @synonyms "allowed roles, only for, who can see, permitted roles"
30
+ @tier "policy"
31
+ viewerRole : string = ""
32
+ @label "Viewer's role"
33
+ @description "The signed-in viewer's role that `roles` is checked against. Bind it explicitly at the call site (e.g. `viewerRole: @user.role`); left empty, only an ungated (empty `roles`) gate renders."
34
+ @tier "policy"
35
+ }
36
+ }
37
+
38
+ page "/role-gate" as RoleGatePage -> RoleGate
39
+ }
@@ -1,5 +1,6 @@
1
1
  app std-saved-search "1.0.0"
2
2
  "std-saved-search — persisted query + alert subscription. Owns a SavedSearch entity (one row per saved query) and a single manage trait that lists saved searches in a data-grid, lets users toggle their alert subscription / rename the search / delete it, and persists each change."
3
+ type ItemAction = { label : string!, event : event, variant : string, icon : string, roles : [string], when : SExpr }
3
4
  orbital SavedSearchOrbital {
4
5
  type SavedSearchLoaded = Event { data : [SavedSearch] } "Fired when the saved-search collection finishes loading"
5
6
 
@@ -9,6 +10,8 @@ orbital SavedSearchOrbital {
9
10
 
10
11
  type SavedSearchDeleted = Event { id : string! } "Fired after a saved-search row is deleted"
11
12
 
13
+ type SavedSearchCreated = Event SavedSearch "Fired after the current search is saved as a new row"
14
+
12
15
  entity SavedSearch [persistent: savedsearches] {
13
16
  id : string!
14
17
  name : string! @description "A user-defined label for the saved search." @synonyms "title, label, search name"
@@ -74,22 +77,8 @@ orbital SavedSearchOrbital {
74
77
  variant: badge
75
78
  }]
76
79
  gap: sm
77
- itemActions: [{
78
- event: TOGGLE_ALERT
79
- icon: bell
80
- label: "Toggle alert"
81
- variant: secondary
82
- }, {
83
- event: EDIT
84
- icon: pencil
85
- label: Edit
86
- variant: secondary
87
- }, {
88
- event: DELETE
89
- icon: trash-2
90
- label: Delete
91
- variant: danger
92
- }]
80
+ itemClickEvent: @config.itemClickEvent
81
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
93
82
  look: @config.tableLook
94
83
  type: data-grid
95
84
  }]
@@ -137,6 +126,24 @@ orbital SavedSearchOrbital {
137
126
  (persist delete SavedSearch ?id {
138
127
  emit: { failure: SavedSearchLoadFailed, success: SavedSearchDeleted }
139
128
  })
129
+ ;; The page's live search, fed by whichever search/filter traits the
130
+ ;; consumer wires in, so SAVE_CURRENT captures what the viewer sees.
131
+ TRACK_QUERY -> browsing
132
+ (set @entity.query ?query)
133
+ ;; One filter change at a time (std-filter's FILTER shape); an empty
134
+ ;; value clears that filter, matching std-browse's REFETCH_FILTER.
135
+ TRACK_FILTERS -> browsing
136
+ (set @entity.filters (if ["=", ?value, ""] (object/omit @entity.filters [?field]) (object/set @entity.filters ?field ?value)))
137
+ SAVE_CURRENT -> browsing
138
+ (persist create SavedSearch {
139
+ name: (if @entity.query @entity.query @config.defaultSearchName)
140
+ query: @entity.query
141
+ filters: @entity.filters
142
+ userId: @user.id
143
+ createdAt: @now
144
+ } {
145
+ emit: { failure: SavedSearchLoadFailed, success: SavedSearchCreated }
146
+ })
140
147
  EDIT -> editing
141
148
  (set @entity.id ?id)
142
149
  (render-ui main {
@@ -194,6 +201,10 @@ orbital SavedSearchOrbital {
194
201
  (fetch SavedSearch {
195
202
  emit: { failure: SavedSearchLoadFailed, success: SavedSearchLoaded }
196
203
  })
204
+ SavedSearchCreated -> loading
205
+ (fetch SavedSearch {
206
+ emit: { failure: SavedSearchLoadFailed, success: SavedSearchLoaded }
207
+ })
197
208
  ;; editing.SAVE -> browsing lands here before the persist resolves; a
198
209
  ;; failure must be caught here too, not just by loading's twin arm.
199
210
  SavedSearchLoadFailed -> error
@@ -243,6 +254,7 @@ orbital SavedSearchOrbital {
243
254
  @tier "presentation"
244
255
  EDIT {
245
256
  id : string!
257
+ row : SavedSearch
246
258
  }
247
259
  @description "Indicates a saved search is being modified."
248
260
  @synonyms "modify, update, change"
@@ -280,6 +292,10 @@ orbital SavedSearchOrbital {
280
292
  @description "Indicates a failure to load saved search data."
281
293
  @synonyms "error, failure, load error"
282
294
  @tier "internal"
295
+ SavedSearchCreated
296
+ @description "Indicates the current search has been saved as a new row."
297
+ @synonyms "saved, created, bookmarked"
298
+ @tier "presentation"
283
299
  SavedSearchUpdated
284
300
  @description "Indicates a saved search has been modified and persisted."
285
301
  @synonyms "modified, changed, updated, persisted"
@@ -332,6 +348,27 @@ orbital SavedSearchOrbital {
332
348
  @description "Aborts the current operation or view."
333
349
  @synonyms "dismiss, close, undo"
334
350
  @tier "domain"
351
+ TRACK_QUERY {
352
+ query : string
353
+ }
354
+ @description "The page's current search text changed."
355
+ @synonyms "search changed, query changed, typed search"
356
+ @tier "domain"
357
+ TRACK_FILTERS {
358
+ field : string!
359
+ value : string!
360
+ }
361
+ @description "The page's current filters changed."
362
+ @synonyms "filters changed, criteria changed"
363
+ @tier "domain"
364
+ SAVE_CURRENT
365
+ @description "Save the page's current search and filters as a new saved search."
366
+ @synonyms "save search, bookmark search, remember search"
367
+ @tier "domain"
368
+ SavedSearchCreated
369
+ @description "The current search was saved as a new row."
370
+ @synonyms "saved, created, bookmarked"
371
+ @tier "presentation"
335
372
  SavedSearchLoaded {
336
373
  data : [SavedSearch]
337
374
  }
@@ -366,6 +403,37 @@ orbital SavedSearchOrbital {
366
403
  @label "Table look"
367
404
  @description "Layer 2 visual treatment for the data table rendered by this atom."
368
405
  @tier "presentation"
406
+ defaultSearchName : string = "Saved search"
407
+ @label "Default saved-search name"
408
+ @description "Name given to a saved search when the current search has no query text (filters only). Renamable afterwards."
409
+ @tier "presentation"
410
+ itemClickEvent : event = EDIT
411
+ @label "Row click"
412
+ @description "Event emitted when a row is clicked; opens the saved search for editing. Empty disables the row click."
413
+ @tier "presentation"
414
+ itemActions : [ItemAction] = [{
415
+ event: TOGGLE_ALERT
416
+ icon: bell
417
+ label: "Toggle alert"
418
+ variant: secondary
419
+ }, {
420
+ event: EDIT
421
+ icon: pencil
422
+ label: Edit
423
+ variant: secondary
424
+ }, {
425
+ event: DELETE
426
+ icon: trash-2
427
+ label: Delete
428
+ variant: danger
429
+ }]
430
+ @label "Row actions"
431
+ @description "Actions offered on each row; give an item `roles` to show it only to those viewer roles."
432
+ @tier "presentation"
433
+ viewerRole : string = ""
434
+ @label "Viewer's role"
435
+ @description "The signed-in viewer's role that each action's `roles` is checked against. Bind it explicitly at the call site (e.g. `viewerRole: @user.role`) in an app whose identity carries a role; left empty, only actions with no `roles` show."
436
+ @tier "policy"
369
437
  }
370
438
  }
371
439
 
@@ -1,6 +1,7 @@
1
1
  app std-selection "1.0.0"
2
2
  "std-selection as a Function"
3
3
  type FilterSpec = { field : string!, label : string, options : [string] }
4
+ type ItemAction = { label : string!, event : event, icon : string, roles : [string], when : SExpr }
4
5
  orbital SelectableItemOrbital {
5
6
  type SelectableItemLoaded = Event { data : [SelectableItem] } "Fired when SelectableItem finishes loading; payload.data holds the list"
6
7
 
@@ -86,11 +87,8 @@ orbital SelectableItemOrbital {
86
87
  className: "transition-shadow hover:shadow-md cursor-pointer"
87
88
  entity: ?data
88
89
  fields: []
89
- itemActions: [{
90
- event: SELECT
91
- icon: check
92
- label: Select
93
- }]
90
+ itemClickEvent: @config.itemClickEvent
91
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
94
92
  look: @config.tableLook
95
93
  pageSize: @config.displayPageSize
96
94
  renderItem: (fn item {
@@ -220,11 +218,8 @@ orbital SelectableItemOrbital {
220
218
  className: "transition-shadow hover:shadow-md cursor-pointer"
221
219
  entity: ?data
222
220
  fields: []
223
- itemActions: [{
224
- event: SELECT
225
- icon: check
226
- label: Select
227
- }]
221
+ itemClickEvent: @config.itemClickEvent
222
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
228
223
  look: @config.tableLook
229
224
  pageSize: @config.displayPageSize
230
225
  renderItem: (fn item {
@@ -387,6 +382,7 @@ orbital SelectableItemOrbital {
387
382
  listens {
388
383
  SELECT {
389
384
  id : string!
385
+ row : SelectableItem
390
386
  }
391
387
  @description "Indicates an item has been chosen."
392
388
  @synonyms "chosen, picked, highlighted"
@@ -467,6 +463,22 @@ orbital SelectableItemOrbital {
467
463
  @description "Rows visible per page before the data-grid's own pagination control appears — 20 matches the pick-list density sweet spot (Miller's law, 10-25 rows)."
468
464
  @synonyms "visible rows, items per page, display limit, show per page"
469
465
  @tier "presentation"
466
+ itemClickEvent : event = SELECT
467
+ @label "Row click"
468
+ @description "Event emitted when a row is clicked; clicking a row selects it. Empty disables the row click."
469
+ @tier "presentation"
470
+ itemActions : [ItemAction] = [{
471
+ event: SELECT
472
+ icon: check
473
+ label: Select
474
+ }]
475
+ @label "Row actions"
476
+ @description "Actions offered on each row; give an item `roles` to show it only to those viewer roles."
477
+ @tier "presentation"
478
+ viewerRole : string = ""
479
+ @label "Viewer's role"
480
+ @description "The signed-in viewer's role that each action's `roles` is checked against. Bind it explicitly at the call site (e.g. `viewerRole: @user.role`) in an app whose identity carries a role; left empty, only actions with no `roles` show."
481
+ @tier "policy"
470
482
  }
471
483
  }
472
484
 
@@ -115,7 +115,7 @@ orbital StatsItemOrbital {
115
115
  @description "One stat card per entry; declares aggregation, format, and styling"
116
116
  @synonyms "list of KPI cards / stat tiles / metric cards. each entry has label / value / format / icon / variant / aggregation. user phrases: 'replace metrics with X, Y, Z' -> rewrite array; 'add Revenue tile' -> append entry; 'change KPIs to X' -> rewrite array"
117
117
  @tier "presentation"
118
- title : string = Summary
118
+ title : string = ""
119
119
  @label "Section title"
120
120
  @description "Heading shown above the stat-card grid"
121
121
  @synonyms "the section header text. user phrases: 'rename section to X' / 'title the KPIs X' / 'change section title to X' -> X"
@@ -137,7 +137,7 @@ orbital StatsItemOrbital {
137
137
  @label "Stats look"
138
138
  @description "Layer 3 body layout: default per-metric stat-card grid ('cards'), Stripe-dashboard KPI tile row ('kpi-tiles'), single oversize hero number ('hero-metric'), or chart-first sparkline tile grid ('sparkline-row')."
139
139
  @tier "presentation"
140
- bodyContent : render-ui = { data-theme: @config.theme, type: box, children: [{
140
+ bodyContent : render-ui = { data-theme: @config.theme, type: box, children: [(if ["!=", @config.title, ""] { content: @config.title, type: typography, variant: h3 } { children: [], gap: none, type: stack }), {
141
141
  entity: @entity.cards
142
142
  fields: []
143
143
  renderItem: (fn card {
@@ -163,7 +163,7 @@ orbital StatsItemOrbital {
163
163
  @label "Body content tree"
164
164
  @description "Render-ui SExpr rendered after metrics aggregate for statsLook='cards' — the canonical data-grid call mapping @entity.cards through stat-display. Other statsLook values render kpiTilesBodyContent / heroMetricBodyContent / sparklineRowBodyContent instead; all four share this trait's state machine, aggregation pipeline, and listens."
165
165
  @tier "internal"
166
- kpiTilesBodyContent : render-ui = { data-theme: @config.theme, type: box, children: [{
166
+ kpiTilesBodyContent : render-ui = { data-theme: @config.theme, type: box, children: [(if ["!=", @config.title, ""] { content: @config.title, type: typography, variant: h3 } { children: [], gap: none, type: stack }), {
167
167
  className: w-full
168
168
  entity: @entity.cards
169
169
  fields: []
@@ -239,7 +239,7 @@ orbital StatsItemOrbital {
239
239
  @label "KPI tiles body content tree"
240
240
  @description "Render-ui SExpr for statsLook='kpi-tiles' — Stripe-dashboard style tile grid: huge typography number, label, trend indicator, sparkline hint."
241
241
  @tier "internal"
242
- heroMetricBodyContent : render-ui = { data-theme: @config.theme, type: box, children: [{
242
+ heroMetricBodyContent : render-ui = { data-theme: @config.theme, type: box, children: [(if ["!=", @config.title, ""] { content: @config.title, type: typography, variant: h3 } { children: [], gap: none, type: stack }), {
243
243
  entity: @entity.cards
244
244
  fields: []
245
245
  gap: none
@@ -324,7 +324,7 @@ orbital StatsItemOrbital {
324
324
  @label "Hero metric body content tree"
325
325
  @description "Render-ui SExpr for statsLook='hero-metric' — one elevated card, one oversize number, background sparkline."
326
326
  @tier "internal"
327
- sparklineRowBodyContent : render-ui = { data-theme: @config.theme, type: box, children: [{
327
+ sparklineRowBodyContent : render-ui = { data-theme: @config.theme, type: box, children: [(if ["!=", @config.title, ""] { content: @config.title, type: typography, variant: h3 } { children: [], gap: none, type: stack }), {
328
328
  className: w-full
329
329
  entity: @entity.cards
330
330
  fields: []
@@ -126,6 +126,7 @@ orbital TagTaxonomyOrbital {
126
126
  padding: sm
127
127
  type: card
128
128
  })
129
+ look: @config.tableLook
129
130
  type: data-grid
130
131
  }]
131
132
  direction: vertical
@@ -278,6 +279,7 @@ orbital TagTaxonomyOrbital {
278
279
  padding: sm
279
280
  type: card
280
281
  })
282
+ look: @config.tableLook
281
283
  type: data-grid
282
284
  }]
283
285
  direction: vertical
@@ -490,6 +492,7 @@ orbital TagTaxonomyOrbital {
490
492
  padding: sm
491
493
  type: card
492
494
  })
495
+ look: @config.tableLook
493
496
  type: data-grid
494
497
  }]
495
498
  direction: vertical
@@ -1,6 +1,11 @@
1
1
  app std-thread "1.3.0"
2
- "std-thread — forum-style nested replies with collapse + flat-mode toggle. Owns ThreadPost (persistent). flat:true covers 1:1 linear inquiry threads; flat:false renders forum-style nested. INIT fetches, scoped by @config.threadRootId when set (blank = every root, the default). REPLY persists a child post under @payload.parentId, stamped with the real @user identity. A consumer that mounts this as page comments forwards its own document-opened signal to SET_THREAD_ROOT { rootId }, which stamps @entity.replyRootId (scratch — the persisted threadRootId column is written only by the reply persist create) and refetches filtered by the payload; a plain INIT re-entry now also falls back through @entity.replyRootId before @config.threadRootId, so it stays scoped to the last SET_THREAD_ROOT instead of re-widening — only the after-create/after-select refetches still scope directly off their own event payload. Layer 3 variants override `bodyContent` to deliver chat-bubble, email-threaded, or linear-comments UX while inheriting the same trait, state machine, emits, and listens. A non-empty sectionTitle switches the default bodyContent into titled-comments mode: header + post list + a standalone composer button (without it an empty thread has no REPLY emitter at all — the affordance only existed per-row)."
2
+ "std-thread — forum-style nested replies with collapse. Owns ThreadPost (persistent). INIT fetches, scoped by @config.threadRootId when set (blank = every root, the default). REPLY persists a child post under @payload.parentId, stamped with the real @user identity. A consumer that mounts this as page comments forwards its own document-opened signal to SET_THREAD_ROOT { rootId }, which stamps @entity.replyRootId (scratch — the persisted threadRootId column is written only by the reply persist create) and refetches filtered by the payload; a plain INIT re-entry now also falls back through @entity.replyRootId before @config.threadRootId, so it stays scoped to the last SET_THREAD_ROOT instead of re-widening — only the after-create/after-select refetches still scope directly off their own event payload. Layer 3 variants override `bodyContent` to deliver chat-bubble, email-threaded, or linear-comments UX while inheriting the same trait, state machine, emits, and listens. A non-empty sectionTitle switches the default bodyContent into titled-comments mode: header + post list + a standalone composer button (without it an empty thread has no REPLY emitter at all — the affordance only existed per-row)."
3
+ type ItemAction = { label : string!, event : event, icon : string, roles : [string], when : SExpr }
3
4
  orbital ThreadPostOrbital {
5
+ uses RoleGate from "std/behaviors/std-role-gate"
6
+
7
+ uses Button from "std/behaviors/ui-button"
8
+
4
9
  ;; Forward relation: array of child ThreadPost IDs whose parentId === this.id.
5
10
  ;; Lowers to `type: "relation", cardinality: "many"` (orbital-lolo lower.rs).
6
11
  ;; @rebindable rewrites the target to the rebound entity in variant atoms.
@@ -381,10 +386,6 @@ orbital ThreadPostOrbital {
381
386
  }
382
387
 
383
388
  config {
384
- flat : boolean = false
385
- @label "Flat thread"
386
- @description "Render replies as a linear list instead of nested tree"
387
- @tier "presentation"
388
389
  threadRootId : string = ""
389
390
  @label "Root post id"
390
391
  @description "Top-level post whose descendants are loaded, filters every fetch and defaults new top-level replies' threadRootId (blank = all roots, unfiltered)"
@@ -394,10 +395,6 @@ orbital ThreadPostOrbital {
394
395
  @label "Table look"
395
396
  @description "Layer 2 visual treatment for the data table rendered by this atom."
396
397
  @tier "presentation"
397
- recipientName : string = Conversation
398
- @label "recipientName"
399
- @description "Header/recipient label for chat-style threads."
400
- @tier "presentation"
401
398
  sectionTitle : string = ""
402
399
  @label "sectionTitle"
403
400
  @description "Section header rendered by the default bodyContent when non-empty, along with a standalone composer button — the only REPLY affordance an empty thread has. Blank (the default) keeps the bare post list for embedded threads that bring their own chrome (chat rails, custom comment sections)."
@@ -408,58 +405,6 @@ orbital ThreadPostOrbital {
408
405
  @description "Label of the standalone composer button rendered under the post list when sectionTitle is non-empty."
409
406
  @synonyms "reply label, comment button, add comment"
410
407
  @tier "presentation"
411
- emailAuthorBinding : string = @item.authorName
412
- @label "emailAuthorBinding"
413
- @description "List-pane binding for the message author."
414
- @tier "presentation"
415
- emailSubjectBinding : string = @item.subject
416
- @label "emailSubjectBinding"
417
- @description "List-pane binding for the message subject."
418
- @tier "presentation"
419
- emailPreviewBinding : string = @item.preview
420
- @label "emailPreviewBinding"
421
- @description "List-pane binding for the message preview text."
422
- @tier "presentation"
423
- detailSubjectBinding : string = @entity.focusedRow.subject
424
- @label "detailSubjectBinding"
425
- @description "Detail-pane binding for the focused message subject."
426
- @tier "presentation"
427
- detailAuthorBinding : string = @entity.focusedRow.authorName
428
- @label "detailAuthorBinding"
429
- @description "Detail-pane binding for the focused message author."
430
- @tier "presentation"
431
- detailFromBinding : string = @entity.focusedRow.fromAddress
432
- @label "detailFromBinding"
433
- @description "Detail-pane binding for the From address."
434
- @tier "presentation"
435
- detailToBinding : string = @entity.focusedRow.toAddress
436
- @label "detailToBinding"
437
- @description "Detail-pane binding for the To address."
438
- @tier "presentation"
439
- detailDateBinding : string = @entity.focusedRow.createdAt
440
- @label "detailDateBinding"
441
- @description "Detail-pane binding for the timestamp."
442
- @tier "presentation"
443
- detailContentBinding : string = @entity.focusedRow.content
444
- @label "detailContentBinding"
445
- @description "Detail-pane binding for the message body."
446
- @tier "presentation"
447
- replyAuthorBinding : string = @reply.authorName
448
- @label "replyAuthorBinding"
449
- @description "Reply binding for the reply author."
450
- @tier "presentation"
451
- replyFromBinding : string = @reply.fromAddress
452
- @label "replyFromBinding"
453
- @description "Reply binding for the reply From address."
454
- @tier "presentation"
455
- replyDateBinding : string = @reply.createdAt
456
- @label "replyDateBinding"
457
- @description "Reply binding for the reply timestamp."
458
- @tier "presentation"
459
- replyContentBinding : string = @reply.content
460
- @label "replyContentBinding"
461
- @description "Reply binding for the reply body."
462
- @tier "presentation"
463
408
  bodyContent : render-ui = {
464
409
  children: [(if ["!=", @config.sectionTitle, ""] {
465
410
  content: @config.sectionTitle
@@ -485,21 +430,11 @@ orbital ThreadPostOrbital {
485
430
  variant: caption
486
431
  }]
487
432
  gap: sm
488
- itemActions: [{
489
- event: REPLY
490
- icon: message-square
491
- label: Reply
492
- }]
433
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
493
434
  look: @config.tableLook
494
435
  type: data-list
495
436
  variant: card
496
- }, (if ["!=", @config.sectionTitle, ""] {
497
- action: REPLY
498
- icon: message-square
499
- label: @config.composerLabel
500
- type: button
501
- variant: secondary
502
- } {
437
+ }, (if ["!=", @config.sectionTitle, ""] <trait.ThreadReplyGate /> {
503
438
  children: []
504
439
  gap: none
505
440
  type: stack
@@ -511,6 +446,39 @@ orbital ThreadPostOrbital {
511
446
  @label "Body content tree"
512
447
  @description "Render-ui SExpr rendered after thread posts load. Default is the canonical forum-style data-list. Layer 3 variants override this with their own tree (chat bubbles, email-threaded cards, linear comment stacks, etc.) to deliver entirely different thread UX while inheriting the same trait, state machine, emits, and listens."
513
448
  @tier "internal"
449
+ itemActions : [ItemAction] = [{
450
+ event: REPLY
451
+ icon: message-square
452
+ label: Reply
453
+ }]
454
+ @label "Row actions"
455
+ @description "Actions offered on each row; give an item `roles` to show it only to those viewer roles."
456
+ @tier "presentation"
457
+ viewerRole : string = ""
458
+ @label "Viewer's role"
459
+ @description "The signed-in viewer's role that each action's `roles` is checked against. Bind it explicitly at the call site (e.g. `viewerRole: @user.role`) in an app whose identity carries a role; left empty, only actions with no `roles` show."
460
+ @tier "policy"
461
+ replyRoles : [string] = []
462
+ @label "Roles that can reply"
463
+ @description "Roles (matched against viewerRole) that see the thread's Reply button. Empty = everyone."
464
+ @tier "policy"
465
+ }
466
+ }
467
+
468
+ trait ThreadReplyButton = Button.traits.ButtonRender {
469
+ config {
470
+ action: REPLY
471
+ icon: message-square
472
+ label: @config.composerLabel
473
+ variant: secondary
474
+ }
475
+ }
476
+
477
+ trait ThreadReplyGate = RoleGate.traits.RoleGate {
478
+ config {
479
+ roles: @config.replyRoles
480
+ viewerRole: @config.viewerRole
481
+ content: <trait.ThreadReplyButton />
514
482
  }
515
483
  }
516
484