@almadar/std 16.234.0 → 16.236.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 (245) hide show
  1. package/behaviors/behaviors-registry.json +4402 -1898
  2. package/behaviors/lolo/infra/atoms/std-approval-gate.lolo +25 -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-mod-queue.lolo +40 -18
  12. package/behaviors/lolo/infra/atoms/std-notification-preferences.lolo +20 -11
  13. package/behaviors/lolo/infra/atoms/std-notify-on-event.lolo +0 -5
  14. package/behaviors/lolo/infra/atoms/std-permission-matrix.lolo +23 -11
  15. package/behaviors/lolo/infra/atoms/std-rate-limiter.lolo +84 -22
  16. package/behaviors/lolo/infra/atoms/std-status-lifecycle.lolo +1 -6
  17. package/behaviors/lolo/ml/atoms/std-graph-builder.lolo +3 -1
  18. package/behaviors/lolo/ml/atoms/std-ml-classify.lolo +0 -4
  19. package/behaviors/lolo/ml/atoms/std-ml-similarity.lolo +0 -4
  20. package/behaviors/lolo/ui/avl/atoms/ui-avl-state-machine.lolo +12 -0
  21. package/behaviors/lolo/ui/core/atoms/std-app-layout.lolo +55 -9
  22. package/behaviors/lolo/ui/core/atoms/std-app-search.lolo +6 -2
  23. package/behaviors/lolo/ui/core/atoms/std-billable-hour.lolo +19 -14
  24. package/behaviors/lolo/ui/core/atoms/std-board.lolo +77 -27
  25. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +44 -28
  26. package/behaviors/lolo/ui/core/atoms/std-calendar.lolo +7 -3
  27. package/behaviors/lolo/ui/core/atoms/std-esign-flow.lolo +25 -13
  28. package/behaviors/lolo/ui/core/atoms/std-esign-request.lolo +5 -0
  29. package/behaviors/lolo/ui/core/atoms/std-event-log.lolo +0 -5
  30. package/behaviors/lolo/ui/core/atoms/std-export.lolo +0 -10
  31. package/behaviors/lolo/ui/core/atoms/std-file-store.lolo +7 -3
  32. package/behaviors/lolo/ui/core/atoms/std-form-advanced.lolo +1 -6
  33. package/behaviors/lolo/ui/core/atoms/std-geosearch.lolo +1 -0
  34. package/behaviors/lolo/ui/core/atoms/std-graphs.lolo +20 -4
  35. package/behaviors/lolo/ui/core/atoms/std-import.lolo +0 -5
  36. package/behaviors/lolo/ui/core/atoms/std-list.lolo +46 -32
  37. package/behaviors/lolo/ui/core/atoms/std-modal-editor.lolo +2 -2
  38. package/behaviors/lolo/ui/core/atoms/std-rating-review.lolo +12 -13
  39. package/behaviors/lolo/ui/core/atoms/std-record-detail.lolo +16 -12
  40. package/behaviors/lolo/ui/core/atoms/std-recurrence.lolo +10 -1
  41. package/behaviors/lolo/ui/core/atoms/std-role-gate.lolo +39 -0
  42. package/behaviors/lolo/ui/core/atoms/std-saved-search.lolo +78 -16
  43. package/behaviors/lolo/ui/core/atoms/std-selection.lolo +15 -10
  44. package/behaviors/lolo/ui/core/atoms/std-stats.lolo +5 -5
  45. package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +3 -0
  46. package/behaviors/lolo/ui/core/atoms/std-thread.lolo +41 -73
  47. package/behaviors/lolo/ui/core/atoms/std-version-history.lolo +20 -11
  48. package/behaviors/lolo/ui/core/atoms/std-vote.lolo +0 -4
  49. package/behaviors/lolo/ui/core/atoms/std-wizard.lolo +3 -3
  50. package/behaviors/lolo/ui/core/atoms/ui-presence.lolo +21 -0
  51. package/behaviors/lolo/ui/core/molecules/ui-breadcrumb.lolo +6 -1
  52. package/behaviors/lolo/ui/core/molecules/ui-chart.lolo +11 -1
  53. package/behaviors/lolo/ui/core/molecules/ui-code-block.lolo +13 -8
  54. package/behaviors/lolo/ui/core/molecules/ui-command-palette.lolo +7 -2
  55. package/behaviors/lolo/ui/core/molecules/ui-commit-row.lolo +2 -2
  56. package/behaviors/lolo/ui/core/molecules/ui-confirm-dialog.lolo +8 -0
  57. package/behaviors/lolo/ui/core/molecules/ui-data-grid.lolo +41 -3
  58. package/behaviors/lolo/ui/core/molecules/ui-data-list.lolo +41 -3
  59. package/behaviors/lolo/ui/core/molecules/ui-document-details.lolo +30 -9
  60. package/behaviors/lolo/ui/core/molecules/ui-document-panel.lolo +8 -3
  61. package/behaviors/lolo/ui/core/molecules/ui-document-viewer.lolo +6 -1
  62. package/behaviors/lolo/ui/core/molecules/ui-drawer.lolo +13 -0
  63. package/behaviors/lolo/ui/core/molecules/ui-floating-action-button.lolo +6 -1
  64. package/behaviors/lolo/ui/core/molecules/ui-floating-toolbar.lolo +6 -1
  65. package/behaviors/lolo/ui/core/molecules/ui-form-actions.lolo +6 -1
  66. package/behaviors/lolo/ui/core/molecules/ui-graph-canvas.lolo +6 -1
  67. package/behaviors/lolo/ui/core/molecules/ui-menu.lolo +6 -1
  68. package/behaviors/lolo/ui/core/molecules/ui-meter.lolo +6 -1
  69. package/behaviors/lolo/ui/core/molecules/ui-modal.lolo +5 -0
  70. package/behaviors/lolo/ui/core/molecules/ui-page-header.lolo +11 -1
  71. package/behaviors/lolo/ui/core/molecules/ui-page-transition.lolo +1 -1
  72. package/behaviors/lolo/ui/core/molecules/ui-swipeable-row.lolo +44 -15
  73. package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +41 -3
  74. package/behaviors/lolo/ui/core/molecules/ui-tabs.lolo +8 -2
  75. package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +66 -12
  76. package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +9 -3
  77. package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +9 -3
  78. package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +18 -9
  79. package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +55 -10
  80. package/behaviors/lolo/ui/core/organisms/ui-form.lolo +55 -10
  81. package/behaviors/lolo/ui/core/organisms/ui-media-gallery.lolo +8 -3
  82. package/behaviors/lolo/ui/core/organisms/ui-state-machine-view.lolo +12 -0
  83. package/behaviors/lolo/ui/core/organisms/ui-timeline.lolo +8 -3
  84. package/behaviors/lolo/ui/core/templates/ui-counter-template.lolo +2 -44
  85. package/behaviors/lolo/ui/core/templates/ui-dashboard-layout.lolo +6 -1
  86. package/behaviors/lolo/ui/core/templates/ui-generic-app-template.lolo +2 -37
  87. package/behaviors/lolo/ui/game/atoms/ui-game-menu.lolo +8 -2
  88. package/behaviors/lolo/ui/game/organisms/std-arcade-board-3d.lolo +2 -2
  89. package/behaviors/lolo/ui/game/organisms/std-crew-board-2d.lolo +0 -4
  90. package/behaviors/lolo/ui/game/organisms/std-platformer-puzzle.lolo +2 -66
  91. package/behaviors/lolo/ui/game/organisms/std-tactics-board-2d.lolo +0 -4
  92. package/behaviors/lolo/ui/game/organisms/std-worldmap-board-2d.lolo +0 -4
  93. package/behaviors/lolo/ui/learning/atoms/std-learn-stepper.lolo +6 -6
  94. package/behaviors/lolo-behavior-contracts.json +1 -1
  95. package/behaviors/registry/factory-signatures.json +1 -1
  96. package/behaviors/registry/infra/atoms/std-approval-gate.orb +124 -67
  97. package/behaviors/registry/infra/atoms/std-audit-capture.orb +0 -8
  98. package/behaviors/registry/infra/atoms/std-cache-aside.orb +0 -84
  99. package/behaviors/registry/infra/atoms/std-circuit-breaker.orb +1 -44
  100. package/behaviors/registry/infra/atoms/std-data-erasure.orb +124 -55
  101. package/behaviors/registry/infra/atoms/std-data-import.orb +12 -11
  102. package/behaviors/registry/infra/atoms/std-game-clock.orb +31 -0
  103. package/behaviors/registry/infra/atoms/std-knowledge-loop.orb +0 -57
  104. package/behaviors/registry/infra/atoms/std-lifecycle.orb +43 -0
  105. package/behaviors/registry/infra/atoms/std-mod-queue.orb +379 -103
  106. package/behaviors/registry/infra/atoms/std-notification-preferences.orb +126 -12
  107. package/behaviors/registry/infra/atoms/std-notify-on-event.orb +0 -11
  108. package/behaviors/registry/infra/atoms/std-permission-matrix.orb +128 -12
  109. package/behaviors/registry/infra/atoms/std-rate-limiter.orb +287 -72
  110. package/behaviors/registry/infra/atoms/std-status-lifecycle.orb +5 -8
  111. package/behaviors/registry/ml/atoms/std-graph-builder.orb +11 -0
  112. package/behaviors/registry/ml/atoms/std-ml-classify.orb +0 -7
  113. package/behaviors/registry/ml/atoms/std-ml-similarity.orb +0 -7
  114. package/behaviors/registry/ui/avl/atoms/ui-avl-state-machine.orb +82 -0
  115. package/behaviors/registry/ui/core/atoms/std-app-layout.orb +417 -9
  116. package/behaviors/registry/ui/core/atoms/std-app-search.orb +53 -1
  117. package/behaviors/registry/ui/core/atoms/std-billable-hour.orb +209 -19
  118. package/behaviors/registry/ui/core/atoms/std-board.orb +242 -61
  119. package/behaviors/registry/ui/core/atoms/std-browse.orb +295 -41
  120. package/behaviors/registry/ui/core/atoms/std-calendar.orb +96 -2
  121. package/behaviors/registry/ui/core/atoms/std-esign-flow.orb +122 -15
  122. package/behaviors/registry/ui/core/atoms/std-esign-request.orb +11 -0
  123. package/behaviors/registry/ui/core/atoms/std-event-log.orb +0 -8
  124. package/behaviors/registry/ui/core/atoms/std-export.orb +0 -16
  125. package/behaviors/registry/ui/core/atoms/std-file-store.orb +109 -2
  126. package/behaviors/registry/ui/core/atoms/std-form-advanced.orb +1 -43
  127. package/behaviors/registry/ui/core/atoms/std-geosearch.orb +5 -0
  128. package/behaviors/registry/ui/core/atoms/std-graphs.orb +173 -66
  129. package/behaviors/registry/ui/core/atoms/std-import.orb +0 -8
  130. package/behaviors/registry/ui/core/atoms/std-list.orb +226 -34
  131. package/behaviors/registry/ui/core/atoms/std-modal-editor.orb +10 -1
  132. package/behaviors/registry/ui/core/atoms/std-rating-review.orb +37 -19
  133. package/behaviors/registry/ui/core/atoms/std-record-detail.orb +204 -11
  134. package/behaviors/registry/ui/core/atoms/std-recurrence.orb +89 -4
  135. package/behaviors/registry/ui/core/atoms/std-role-gate.orb +217 -0
  136. package/behaviors/registry/ui/core/atoms/std-saved-search.orb +419 -19
  137. package/behaviors/registry/ui/core/atoms/std-selection.orb +140 -10
  138. package/behaviors/registry/ui/core/atoms/std-stats.orb +73 -1
  139. package/behaviors/registry/ui/core/atoms/std-tag-taxonomy.orb +3 -0
  140. package/behaviors/registry/ui/core/atoms/std-thread.orb +190 -115
  141. package/behaviors/registry/ui/core/atoms/std-version-history.orb +127 -13
  142. package/behaviors/registry/ui/core/atoms/std-vote.orb +0 -9
  143. package/behaviors/registry/ui/core/atoms/std-wizard.orb +33 -3
  144. package/behaviors/registry/ui/core/atoms/ui-presence.orb +59 -0
  145. package/behaviors/registry/ui/core/molecules/ui-breadcrumb.orb +53 -1
  146. package/behaviors/registry/ui/core/molecules/ui-chart.orb +68 -1
  147. package/behaviors/registry/ui/core/molecules/ui-code-block.orb +291 -8
  148. package/behaviors/registry/ui/core/molecules/ui-command-palette.orb +87 -2
  149. package/behaviors/registry/ui/core/molecules/ui-commit-row.orb +3 -2
  150. package/behaviors/registry/ui/core/molecules/ui-confirm-dialog.orb +32 -0
  151. package/behaviors/registry/ui/core/molecules/ui-data-grid.orb +189 -3
  152. package/behaviors/registry/ui/core/molecules/ui-data-list.orb +189 -3
  153. package/behaviors/registry/ui/core/molecules/ui-document-details.orb +81 -24
  154. package/behaviors/registry/ui/core/molecules/ui-document-panel.orb +121 -3
  155. package/behaviors/registry/ui/core/molecules/ui-document-viewer.orb +53 -1
  156. package/behaviors/registry/ui/core/molecules/ui-drawer.orb +40 -0
  157. package/behaviors/registry/ui/core/molecules/ui-floating-action-button.orb +53 -1
  158. package/behaviors/registry/ui/core/molecules/ui-floating-toolbar.orb +53 -1
  159. package/behaviors/registry/ui/core/molecules/ui-form-actions.orb +53 -1
  160. package/behaviors/registry/ui/core/molecules/ui-graph-canvas.orb +53 -1
  161. package/behaviors/registry/ui/core/molecules/ui-menu.orb +57 -1
  162. package/behaviors/registry/ui/core/molecules/ui-meter.orb +53 -1
  163. package/behaviors/registry/ui/core/molecules/ui-modal.orb +8 -0
  164. package/behaviors/registry/ui/core/molecules/ui-page-header.orb +60 -1
  165. package/behaviors/registry/ui/core/molecules/ui-page-transition.orb +1 -1
  166. package/behaviors/registry/ui/core/molecules/ui-swipeable-row.orb +393 -23
  167. package/behaviors/registry/ui/core/molecules/ui-table-view.orb +189 -3
  168. package/behaviors/registry/ui/core/molecules/ui-tabs.orb +99 -2
  169. package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +282 -20
  170. package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +130 -3
  171. package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +130 -3
  172. package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +367 -9
  173. package/behaviors/registry/ui/core/organisms/ui-form-section.orb +104 -13
  174. package/behaviors/registry/ui/core/organisms/ui-form.orb +104 -13
  175. package/behaviors/registry/ui/core/organisms/ui-media-gallery.orb +121 -3
  176. package/behaviors/registry/ui/core/organisms/ui-state-machine-view.orb +82 -0
  177. package/behaviors/registry/ui/core/organisms/ui-timeline.orb +121 -3
  178. package/behaviors/registry/ui/core/templates/ui-counter-template.orb +1 -172
  179. package/behaviors/registry/ui/core/templates/ui-dashboard-layout.orb +53 -1
  180. package/behaviors/registry/ui/core/templates/ui-generic-app-template.orb +1 -116
  181. package/behaviors/registry/ui/game/atoms/ui-game-menu.orb +99 -2
  182. package/behaviors/registry/ui/game/organisms/std-arcade-board-3d.orb +10 -0
  183. package/behaviors/registry/ui/game/organisms/std-crew-board-2d.orb +0 -7
  184. package/behaviors/registry/ui/game/organisms/std-platformer-puzzle.orb +1 -807
  185. package/behaviors/registry/ui/game/organisms/std-tactics-board-2d.orb +0 -7
  186. package/behaviors/registry/ui/game/organisms/std-worldmap-board-2d.orb +0 -7
  187. package/behaviors/registry/ui/learning/atoms/std-learn-stepper.orb +6 -0
  188. package/canonical-operators.json +18 -1
  189. package/dist/behaviors/exports-reader.js +7662 -3762
  190. package/dist/behaviors/functions/index.d.ts +309 -104
  191. package/dist/behaviors/functions/index.js +7651 -3762
  192. package/dist/behaviors/index.d.ts +1 -1
  193. package/dist/behaviors/index.js +7663 -3763
  194. package/dist/behaviors/query.js +7662 -3762
  195. package/dist/behaviors-embeddings.hash.json +3 -3
  196. package/dist/behaviors-embeddings.json +35 -27
  197. package/dist/behaviors-registry.json +4402 -1898
  198. package/dist/index.d.ts +3 -3
  199. package/dist/index.js +7697 -3763
  200. package/dist/knob-embeddings.hash.json +3 -3
  201. package/dist/knob-embeddings.json +612 -352
  202. package/dist/modules/anim.d.ts +1 -1
  203. package/dist/modules/array.d.ts +1 -1
  204. package/dist/modules/array.js +15 -0
  205. package/dist/modules/async.d.ts +1 -1
  206. package/dist/modules/async.js +9 -0
  207. package/dist/modules/behavior.d.ts +1 -1
  208. package/dist/modules/browser.d.ts +1 -1
  209. package/dist/modules/composition.d.ts +1 -1
  210. package/dist/modules/contract.d.ts +1 -1
  211. package/dist/modules/core.d.ts +1 -1
  212. package/dist/modules/core.js +10 -0
  213. package/dist/modules/data.d.ts +1 -1
  214. package/dist/modules/ease.d.ts +1 -1
  215. package/dist/modules/format.d.ts +1 -1
  216. package/dist/modules/geo.d.ts +1 -1
  217. package/dist/modules/graph.d.ts +1 -1
  218. package/dist/modules/grid.d.ts +1 -1
  219. package/dist/modules/index.d.ts +1 -1
  220. package/dist/modules/index.js +34 -0
  221. package/dist/modules/integration.d.ts +1 -1
  222. package/dist/modules/json.d.ts +1 -1
  223. package/dist/modules/llm.d.ts +1 -1
  224. package/dist/modules/math.d.ts +1 -1
  225. package/dist/modules/memory.d.ts +1 -1
  226. package/dist/modules/nn.d.ts +1 -1
  227. package/dist/modules/noise.d.ts +1 -1
  228. package/dist/modules/object.d.ts +1 -1
  229. package/dist/modules/os.d.ts +1 -1
  230. package/dist/modules/path.d.ts +1 -1
  231. package/dist/modules/prob.d.ts +1 -1
  232. package/dist/modules/session.d.ts +1 -1
  233. package/dist/modules/str.d.ts +1 -1
  234. package/dist/modules/tensor.d.ts +1 -1
  235. package/dist/modules/time.d.ts +1 -1
  236. package/dist/modules/trace.d.ts +1 -1
  237. package/dist/modules/train.d.ts +1 -1
  238. package/dist/modules/validate.d.ts +1 -1
  239. package/dist/modules/vector.d.ts +1 -1
  240. package/dist/modules/workspace.d.ts +1 -1
  241. package/dist/registry/factory-signatures.json +1 -1
  242. package/dist/registry.d.ts +5 -2
  243. package/dist/registry.js +55 -1
  244. package/dist/{types-DwAW4Nni.d.ts → types-DLx38UFZ.d.ts} +31 -4
  245. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  app std-graphs "2.0.0"
2
- "std-graphs — embeddable chart atom. ITEMS_LOADED groups @payload.data by @config.categoryField (or @config.timeField when timeAxis=true) and aggregates per bucket. Multi-series rendering via @config.series (preformatted ChartSeries shape). Histograms via chartType=histogram pass pre-binned @payload.data through unchanged. Stacked / 100% stacked bars via @config.stack. Post-process sort + limit. Clicking a data point emits UI:@config.drillEvent with { label, value, seriesLabel? }. Drop into a dashboard alongside std-stats / std-browse."
2
+ "std-graphs — embeddable chart atom. ITEMS_LOADED keeps the rows whose @config.filterField equals @config.filterValue (blank = all), groups them by @config.categoryField, or (timeAxis=true) by the @config.period bucket of @config.timeField — rows without a time value have no place on a time axis and are left out — and aggregates per bucket. Multi-series rendering via @config.series (preformatted ChartSeries shape). Histograms via chartType=histogram pass pre-binned @payload.data through unchanged. Stacked / 100% stacked bars via @config.stack. Post-process sort + limit. Clicking a data point emits UI:@config.drillEvent with { label, value, seriesLabel? }. Drop into a dashboard alongside std-stats / std-browse."
3
3
  type ObjectSpec = { id : string, label : string, value : string }
4
4
  orbital GraphItemOrbital {
5
5
  type ChartPoint = { label : string!, value : number! }
@@ -27,17 +27,22 @@ orbital GraphItemOrbital {
27
27
  stack: @config.stack
28
28
  subtitle: @config.subtitle
29
29
  timeAxis: @config.timeAxis
30
+ period: @config.period
30
31
  title: @config.title
31
32
  type: @config.viewPattern
32
33
  })
33
34
  ITEMS_LOADED -> idle
34
- (set @entity.chartData (if ["=", @config.chartType, histogram] (array/map ?data (fn p {
35
+ (let (
36
+ (rows (if ["=", @config.filterField, ""] ?data (array/filter ?data (fn row (= (object/get @row @config.filterField) @config.filterValue)))))
37
+ )
38
+ (set @entity.chartData (if ["=", @config.chartType, histogram] (array/map @rows (fn p {
35
39
  label: (object/get @p label)
36
40
  value: (object/get @p value)
37
- })) (array/map (object/entries (array/groupBy ?data (if @config.timeAxis @config.timeField @config.categoryField))) (fn entry {
41
+ })) (array/map (object/entries (if @config.timeAxis (array/groupBy (array/map (array/filter @rows (fn row (object/get @row @config.timeField))) (fn row (object/set @row "periodStart" (time/startOf (time/parse (object/get @row @config.timeField)) @config.period)))) "periodStart") (array/groupBy @rows @config.categoryField))) (fn entry {
38
42
  label: (object/get @entry "0")
39
43
  value: (if ["=", @config.aggregation, sum] (array/sum (object/get @entry "1") @config.valueField) (if ["=", @config.aggregation, avg] (array/avg (object/get @entry "1") @config.valueField) (if ["=", @config.aggregation, min] (array/min (object/get @entry "1") @config.valueField) (if ["=", @config.aggregation, max] (array/max (object/get @entry "1") @config.valueField) (array/len (object/get @entry "1"))))))
40
- }))))
44
+ })))))
45
+ (set @entity.chartData (if @config.timeAxis (array/sort @entity.chartData label asc) @entity.chartData))
41
46
  (set @entity.chartData (if ["!=", @config.sortField, none] (array/sort @entity.chartData @config.sortField @config.sortDir) @entity.chartData))
42
47
  (set @entity.chartData (if [">", @config.limit, 0] (array/take @entity.chartData @config.limit) @entity.chartData))
43
48
  (render-ui main {
@@ -51,6 +56,7 @@ orbital GraphItemOrbital {
51
56
  stack: @config.stack
52
57
  subtitle: @config.subtitle
53
58
  timeAxis: @config.timeAxis
59
+ period: @config.period
54
60
  title: @config.title
55
61
  type: @config.viewPattern
56
62
  })
@@ -92,6 +98,16 @@ orbital GraphItemOrbital {
92
98
  @description "Group by month, quarter, week, day, or year — the calendar bucket the chart groups data into. Set ALONGSIDE timeAxis=true whenever the user says 'group by <time-unit>' or 'by <time-unit>' or 'rebucket to <time-unit>' or 'instead of <other-time-unit>'."
93
99
  @synonyms "regroup / rebucket / change bucket / group differently. user phrases: 'group by month' / 'by month' / 'monthly' / 'monthly buckets' / 'switch to monthly' / 'instead of quarter' -> month; 'group by quarter' / 'by quarter' / 'quarterly' -> quarter; 'group by week' / 'by week' / 'weekly' / 'weekly buckets' -> week; 'group by day' / 'by day' / 'daily' / 'daily buckets' -> day; 'group by year' / 'by year' / 'yearly' / 'annually' -> year. Whenever the user voices 'group by <time-unit>' BOTH period AND timeAxis=true should be set."
94
100
  @tier "presentation"
101
+ filterField : string = ""
102
+ @label "Only count rows where field"
103
+ @description "Row field a row must match to be charted (blank = every row), compared with filterValue — e.g. status + paid charts paid invoices only"
104
+ @synonyms "only include, where, filter by, restrict to. user phrases: 'only paid invoices' -> filterField=status + filterValue=paid; 'just the open deals' -> filterField=status + filterValue=open"
105
+ @tier "presentation"
106
+ filterValue : string = ""
107
+ @label "Equals"
108
+ @description "The value filterField must equal for a row to be charted"
109
+ @synonyms "the kept value. user phrases: 'only paid' -> paid; 'just active ones' -> active"
110
+ @tier "presentation"
95
111
  valueField : string = ""
96
112
  @label "Value field"
97
113
  @description "Numeric field aggregated per bucket (blank = row count)"
@@ -107,11 +107,6 @@ orbital ImportOrbital {
107
107
  @description "Text shown on the toolbar button that opens the import source popover"
108
108
  @synonyms "the text on the import trigger button. user phrases: 'rename import button to X' / 'set the button label to X' -> X (e.g. 'Import Data', 'Add Sources')"
109
109
  @tier "presentation"
110
- event : string = IMPORT_SOURCE_PICKED
111
- @label "Source-picked event"
112
- @description "Event emitted when the user picks a source. Each entry in sources[] carries its own literal event field (no per-item templating in .lolo) — overriding this alone does not retarget the click; also set sources[].event to match."
113
- @synonyms "bus event name. internal wiring; users rarely target directly"
114
- @tier "internal"
115
110
  sources : [SourceOption] = [{
116
111
  event: IMPORT_SOURCE_PICKED
117
112
  id: paste
@@ -1,6 +1,7 @@
1
1
  app std-list "1.0.0"
2
2
  "std-list as a Function"
3
3
  type FilterSpec = { field : string!, label : string, options : [string] }
4
+ type ItemAction = { label : string!, event : event, variant : string, roles : [string], when : SExpr }
4
5
  ;;
5
6
  ;; Daisy-chain composition showcase: no `on EVENT { ... }` trait-ref overrides.
6
7
  ;; Modal + Confirmation trait refs are configured via `config: { ... }` at the
@@ -11,6 +12,10 @@ type FilterSpec = { field : string!, label : string, options : [string] }
11
12
  ;; old version had — form-section's mode set only supports "create" | "edit",
12
13
  ;; and a dedicated detail-view pattern is tracked separately (std-detail).
13
14
  orbital ListItemOrbital {
15
+ uses RoleGate from "std/behaviors/std-role-gate"
16
+
17
+ uses Button from "std/behaviors/ui-button"
18
+
14
19
  uses Confirmation from "std/behaviors/std-confirmation"
15
20
 
16
21
  uses Modal from "std/behaviors/std-modal"
@@ -61,13 +66,7 @@ orbital ListItemOrbital {
61
66
  direction: horizontal
62
67
  gap: md
63
68
  type: stack
64
- }, {
65
- action: CREATE
66
- icon: plus
67
- label: Create
68
- type: button
69
- variant: primary
70
- }]
69
+ }, <trait.ListItemCreateGate />]
71
70
  direction: horizontal
72
71
  gap: md
73
72
  justify: between
@@ -118,15 +117,7 @@ orbital ListItemOrbital {
118
117
  name: status
119
118
  variant: badge
120
119
  }]
121
- itemActions: [{
122
- event: EDIT
123
- label: Edit
124
- variant: ghost
125
- }, {
126
- event: DELETE
127
- label: Delete
128
- variant: danger
129
- }]
120
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
130
121
  pageSize: @config.displayPageSize
131
122
  type: data-grid
132
123
  }]
@@ -208,13 +199,7 @@ orbital ListItemOrbital {
208
199
  direction: horizontal
209
200
  gap: md
210
201
  type: stack
211
- }, {
212
- action: CREATE
213
- icon: plus
214
- label: Create
215
- type: button
216
- variant: primary
217
- }]
202
+ }, <trait.ListItemCreateGate />]
218
203
  direction: horizontal
219
204
  gap: md
220
205
  justify: between
@@ -265,15 +250,7 @@ orbital ListItemOrbital {
265
250
  name: status
266
251
  variant: badge
267
252
  }]
268
- itemActions: [{
269
- event: EDIT
270
- label: Edit
271
- variant: ghost
272
- }, {
273
- event: DELETE
274
- label: Delete
275
- variant: danger
276
- }]
253
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
277
254
  pageSize: @config.displayPageSize
278
255
  type: data-grid
279
256
  }]
@@ -392,6 +369,43 @@ orbital ListItemOrbital {
392
369
  @description "Rows visible per page before the data-grid's own pagination control appears — 20 matches the CRUD-list density sweet spot (Miller's law, 10-25 rows)."
393
370
  @synonyms "visible rows, items per page, display limit, show per page"
394
371
  @tier "presentation"
372
+ itemActions : [ItemAction] = [{
373
+ event: EDIT
374
+ label: Edit
375
+ variant: ghost
376
+ }, {
377
+ event: DELETE
378
+ label: Delete
379
+ variant: danger
380
+ }]
381
+ @label "Row actions"
382
+ @description "Actions offered on each row; give an item `roles` to show it only to those viewer roles."
383
+ @tier "presentation"
384
+ viewerRole : string = ""
385
+ @label "Viewer's role"
386
+ @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."
387
+ @tier "policy"
388
+ createRoles : [string] = []
389
+ @label "Roles that can create"
390
+ @description "Roles (matched against viewerRole) that see the Create button. Empty = everyone."
391
+ @tier "policy"
392
+ }
393
+ }
394
+
395
+ trait ListItemCreateButton = Button.traits.ButtonRender {
396
+ config {
397
+ action: CREATE
398
+ icon: plus
399
+ label: Create
400
+ variant: primary
401
+ }
402
+ }
403
+
404
+ trait ListItemCreateGate = RoleGate.traits.RoleGate {
405
+ config {
406
+ roles: @config.createRoles
407
+ viewerRole: @config.viewerRole
408
+ content: <trait.ListItemCreateButton />
395
409
  }
396
410
  }
397
411
 
@@ -293,7 +293,7 @@ orbital ModalEditorOrbital {
293
293
  (set @entity.cmdline "")
294
294
  (emit SET_MODE { editorId: ?editorId, mode: "NORMAL", caret: "block" })
295
295
 
296
- KEY -> COMMAND when (and ["!=", ?key, "Enter"] ["!=", ?key, "Escape"] ["!=", ?key, "Backspace"])
296
+ KEY -> COMMAND when (and (array/includes (object/get @config.keymap "COMMAND") ?key) ["!=", ?key, "Enter"] ["!=", ?key, "Escape"] ["!=", ?key, "Backspace"])
297
297
  (set @entity.cmdline (str/concat @entity.cmdline ?key))
298
298
 
299
299
  EDITOR_FOCUS -> COMMAND
@@ -312,7 +312,7 @@ orbital ModalEditorOrbital {
312
312
  meta : boolean
313
313
  repeat : boolean
314
314
  }
315
- @description "One physical keydown, delivered by the host's keyboard router. The host decides preventDefault from config.keymap; this trait only reads key + editorId."
315
+ @description "One physical keydown, delivered by the host's keyboard router. The host decides preventDefault from config.keymap; the command line accepts only keys config.keymap captures in COMMAND."
316
316
  EDITOR_FOCUS {
317
317
  editorId : string
318
318
  }
@@ -312,6 +312,7 @@ orbital RatingReviewOrbital {
312
312
  mode: edit
313
313
  submitEvent: SUBMIT_REVIEW
314
314
  submitLabel: @config.submitLabel
315
+ cancelLabel: @config.cancelLabel
315
316
  type: form-section
316
317
  }]
317
318
  direction: vertical
@@ -1114,6 +1115,7 @@ orbital RatingReviewOrbital {
1114
1115
  mode: edit
1115
1116
  submitEvent: SUBMIT_REVIEW
1116
1117
  submitLabel: @config.submitLabel
1118
+ cancelLabel: @config.cancelLabel
1117
1119
  type: form-section
1118
1120
  }]
1119
1121
  direction: vertical
@@ -1426,10 +1428,11 @@ orbital RatingReviewOrbital {
1426
1428
  }, {
1427
1429
  cancelEvent: CANCEL_REVIEW
1428
1430
  entity: @entity
1429
- fields: (draftComment)
1431
+ fields: (if @config.allowComment ["draftComment"] [])
1430
1432
  mode: edit
1431
1433
  submitEvent: SUBMIT_REVIEW
1432
1434
  submitLabel: @config.submitLabel
1435
+ cancelLabel: @config.cancelLabel
1433
1436
  type: form-section
1434
1437
  }]
1435
1438
  direction: vertical
@@ -1455,10 +1458,11 @@ orbital RatingReviewOrbital {
1455
1458
  }, {
1456
1459
  cancelEvent: CANCEL_REVIEW
1457
1460
  entity: @entity
1458
- fields: (draftComment)
1461
+ fields: (if @config.allowComment ["draftComment"] [])
1459
1462
  mode: edit
1460
1463
  submitEvent: SUBMIT_REVIEW
1461
1464
  submitLabel: @config.submitLabel
1465
+ cancelLabel: @config.cancelLabel
1462
1466
  type: form-section
1463
1467
  }]
1464
1468
  direction: vertical
@@ -1504,10 +1508,11 @@ orbital RatingReviewOrbital {
1504
1508
  }, {
1505
1509
  cancelEvent: CANCEL_REVIEW
1506
1510
  entity: @entity
1507
- fields: (draftComment)
1511
+ fields: (if @config.allowComment ["draftComment"] [])
1508
1512
  mode: edit
1509
1513
  submitEvent: SUBMIT_REVIEW
1510
1514
  submitLabel: @config.submitLabel
1515
+ cancelLabel: @config.cancelLabel
1511
1516
  type: form-section
1512
1517
  }]
1513
1518
  direction: vertical
@@ -1569,10 +1574,11 @@ orbital RatingReviewOrbital {
1569
1574
  }, {
1570
1575
  cancelEvent: CANCEL_REVIEW
1571
1576
  entity: @entity
1572
- fields: (draftComment)
1577
+ fields: (if @config.allowComment ["draftComment"] [])
1573
1578
  mode: edit
1574
1579
  submitEvent: SUBMIT_REVIEW
1575
1580
  submitLabel: @config.submitLabel
1581
+ cancelLabel: @config.cancelLabel
1576
1582
  type: form-section
1577
1583
  }]
1578
1584
  direction: vertical
@@ -1600,10 +1606,11 @@ orbital RatingReviewOrbital {
1600
1606
  }, {
1601
1607
  cancelEvent: CANCEL_REVIEW
1602
1608
  entity: @entity
1603
- fields: (draftComment)
1609
+ fields: (if @config.allowComment ["draftComment"] [])
1604
1610
  mode: edit
1605
1611
  submitEvent: SUBMIT_REVIEW
1606
1612
  submitLabel: @config.submitLabel
1613
+ cancelLabel: @config.cancelLabel
1607
1614
  type: form-section
1608
1615
  }]
1609
1616
  direction: vertical
@@ -1696,10 +1703,6 @@ orbital RatingReviewOrbital {
1696
1703
  @label "Cancel button"
1697
1704
  @description "Label on the cancel button"
1698
1705
  @tier "presentation"
1699
- submitEvent : string = SUBMIT_REVIEW
1700
- @label "Submit event"
1701
- @description "Event emitted when the user submits the form"
1702
- @tier "internal"
1703
1706
  maxStars : number = 5
1704
1707
  @label "Max stars"
1705
1708
  @description "Top of the rating scale (typically 5)"
@@ -1708,10 +1711,6 @@ orbital RatingReviewOrbital {
1708
1711
  @label "Allow comment"
1709
1712
  @description "Show a free-text comment field below the stars"
1710
1713
  @tier "presentation"
1711
- allowPhotos : boolean = false
1712
- @label "Allow photos"
1713
- @description "Let the user attach photos to the review"
1714
- @tier "presentation"
1715
1714
  cardLook : "elevated" | "flat-bordered" | "borderless-divider" | "ticket" | "invoice" | "chip" | "tile-image-first" = elevated
1716
1715
  @label "Card look"
1717
1716
  @description "Visual style of the wrapper card. Layer 2 cardLook knob — render-ui references @config.cardLook on the compose card."
@@ -2,7 +2,7 @@ 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 }
5
+ type RecordAction = { label : string!, event : event!, icon : string, variant : primary | secondary | ghost | danger, roles : [string], when : SExpr }
6
6
  type RecordFieldSpec = { key : string!, header : string, format : date | currency | number | boolean | percent }
7
7
  type MetaFieldSpec = { key : string!, label : string, kind : text | boolean | select | readonly, options : [string], icon : string, format : date | currency | number | boolean | percent }
8
8
  type RecordSection = { title : string!, fields : [string] }
@@ -481,6 +481,10 @@ orbital RecordItemOrbital {
481
481
  @description "Buttons on the panel header. One primary at most; the rest collapse past maxInlineActions."
482
482
  @synonyms "record actions, detail actions, panel buttons"
483
483
  @tier "presentation"
484
+ viewerRole : string = ""
485
+ @label "Viewer's role"
486
+ @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."
487
+ @tier "policy"
484
488
  maxInlineActions : number = 3
485
489
  @label "How many actions before collapsing?"
486
490
  @description "Actions beyond this count collapse into a '⋯' overflow menu."
@@ -560,10 +564,10 @@ orbital RecordItemOrbital {
560
564
  @tier "presentation"
561
565
  bodyContent : render-ui = {
562
566
  children: [{
563
- actions: @config.actions
564
- entity: ?data
567
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
568
+ entity: @entity.loadedRow
565
569
  fields: @config.fields
566
- initialData: ?data
570
+ initialData: @entity.loadedRow
567
571
  maxInlineActions: @config.maxInlineActions
568
572
  sections: @config.sections
569
573
  showActions: @config.showActions
@@ -580,10 +584,10 @@ orbital RecordItemOrbital {
580
584
  @tier "internal"
581
585
  slideOverBodyContent : render-ui = {
582
586
  children: [{
583
- actions: @config.actions
584
- entity: ?data
587
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
588
+ entity: @entity.loadedRow
585
589
  fields: @config.fields
586
- initialData: ?data
590
+ initialData: @entity.loadedRow
587
591
  maxInlineActions: @config.maxInlineActions
588
592
  sections: @config.sections
589
593
  showActions: @config.showActions
@@ -601,10 +605,10 @@ orbital RecordItemOrbital {
601
605
  @tier "internal"
602
606
  pageBodyContent : render-ui = {
603
607
  children: [{
604
- actions: @config.actions
605
- entity: ?data
608
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
609
+ entity: @entity.loadedRow
606
610
  fields: @config.fields
607
- initialData: ?data
611
+ initialData: @entity.loadedRow
608
612
  maxInlineActions: @config.maxInlineActions
609
613
  sections: @config.sections
610
614
  showActions: @config.showActions
@@ -621,7 +625,7 @@ orbital RecordItemOrbital {
621
625
  @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
626
  @tier "internal"
623
627
  documentBodyContent : render-ui = {
624
- actions: @config.actions
628
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
625
629
  coverImage: @entity.coverUrl
626
630
  icon: @entity.iconName
627
631
  editEvent: (if @config.allowEdit EDIT_CONTENT null)
@@ -640,7 +644,7 @@ orbital RecordItemOrbital {
640
644
  @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
645
  @tier "internal"
642
646
  documentEditingContent : render-ui = {
643
- actions: @config.actions
647
+ actions: (array/filter @config.actions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
644
648
  coverImage: @entity.coverUrl
645
649
  icon: @entity.iconName
646
650
  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,7 @@ orbital RecurrenceOrbital {
139
140
  }, {
140
141
  entity: @entity.occurrences
141
142
  fields: (title description date status)
142
- itemActions: [{ event: OPEN_EXCEPTION, label: Manage }]
143
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
143
144
  look: @config.timelineLook
144
145
  type: timeline
145
146
  }, {
@@ -543,6 +544,14 @@ orbital RecurrenceOrbital {
543
544
  @label "Timeline look"
544
545
  @description "Layer 2 visual treatment for the timeline."
545
546
  @tier "presentation"
547
+ itemActions : [ItemAction] = [{ event: OPEN_EXCEPTION, label: Manage }]
548
+ @label "Row actions"
549
+ @description "Actions offered on each row; give an item `roles` to show it only to those viewer roles."
550
+ @tier "presentation"
551
+ viewerRole : string = ""
552
+ @label "Viewer's role"
553
+ @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."
554
+ @tier "policy"
546
555
  }
547
556
  }
548
557
 
@@ -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,7 @@ 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
+ itemActions: (array/filter @config.itemActions (fn action (or (= (array/len (object/get @action roles [])) 0) (array/includes (object/get @action roles []) @config.viewerRole))))
93
81
  look: @config.tableLook
94
82
  type: data-grid
95
83
  }]
@@ -137,6 +125,24 @@ orbital SavedSearchOrbital {
137
125
  (persist delete SavedSearch ?id {
138
126
  emit: { failure: SavedSearchLoadFailed, success: SavedSearchDeleted }
139
127
  })
128
+ ;; The page's live search, fed by whichever search/filter traits the
129
+ ;; consumer wires in, so SAVE_CURRENT captures what the viewer sees.
130
+ TRACK_QUERY -> browsing
131
+ (set @entity.query ?query)
132
+ ;; One filter change at a time (std-filter's FILTER shape); an empty
133
+ ;; value clears that filter, matching std-browse's REFETCH_FILTER.
134
+ TRACK_FILTERS -> browsing
135
+ (set @entity.filters (if ["=", ?value, ""] (object/omit @entity.filters [?field]) (object/set @entity.filters ?field ?value)))
136
+ SAVE_CURRENT -> browsing
137
+ (persist create SavedSearch {
138
+ name: (if @entity.query @entity.query @config.defaultSearchName)
139
+ query: @entity.query
140
+ filters: @entity.filters
141
+ userId: @user.id
142
+ createdAt: @now
143
+ } {
144
+ emit: { failure: SavedSearchLoadFailed, success: SavedSearchCreated }
145
+ })
140
146
  EDIT -> editing
141
147
  (set @entity.id ?id)
142
148
  (render-ui main {
@@ -194,6 +200,10 @@ orbital SavedSearchOrbital {
194
200
  (fetch SavedSearch {
195
201
  emit: { failure: SavedSearchLoadFailed, success: SavedSearchLoaded }
196
202
  })
203
+ SavedSearchCreated -> loading
204
+ (fetch SavedSearch {
205
+ emit: { failure: SavedSearchLoadFailed, success: SavedSearchLoaded }
206
+ })
197
207
  ;; editing.SAVE -> browsing lands here before the persist resolves; a
198
208
  ;; failure must be caught here too, not just by loading's twin arm.
199
209
  SavedSearchLoadFailed -> error
@@ -280,6 +290,10 @@ orbital SavedSearchOrbital {
280
290
  @description "Indicates a failure to load saved search data."
281
291
  @synonyms "error, failure, load error"
282
292
  @tier "internal"
293
+ SavedSearchCreated
294
+ @description "Indicates the current search has been saved as a new row."
295
+ @synonyms "saved, created, bookmarked"
296
+ @tier "presentation"
283
297
  SavedSearchUpdated
284
298
  @description "Indicates a saved search has been modified and persisted."
285
299
  @synonyms "modified, changed, updated, persisted"
@@ -332,6 +346,27 @@ orbital SavedSearchOrbital {
332
346
  @description "Aborts the current operation or view."
333
347
  @synonyms "dismiss, close, undo"
334
348
  @tier "domain"
349
+ TRACK_QUERY {
350
+ query : string
351
+ }
352
+ @description "The page's current search text changed."
353
+ @synonyms "search changed, query changed, typed search"
354
+ @tier "domain"
355
+ TRACK_FILTERS {
356
+ field : string!
357
+ value : string!
358
+ }
359
+ @description "The page's current filters changed."
360
+ @synonyms "filters changed, criteria changed"
361
+ @tier "domain"
362
+ SAVE_CURRENT
363
+ @description "Save the page's current search and filters as a new saved search."
364
+ @synonyms "save search, bookmark search, remember search"
365
+ @tier "domain"
366
+ SavedSearchCreated
367
+ @description "The current search was saved as a new row."
368
+ @synonyms "saved, created, bookmarked"
369
+ @tier "presentation"
335
370
  SavedSearchLoaded {
336
371
  data : [SavedSearch]
337
372
  }
@@ -366,6 +401,33 @@ orbital SavedSearchOrbital {
366
401
  @label "Table look"
367
402
  @description "Layer 2 visual treatment for the data table rendered by this atom."
368
403
  @tier "presentation"
404
+ defaultSearchName : string = "Saved search"
405
+ @label "Default saved-search name"
406
+ @description "Name given to a saved search when the current search has no query text (filters only). Renamable afterwards."
407
+ @tier "presentation"
408
+ itemActions : [ItemAction] = [{
409
+ event: TOGGLE_ALERT
410
+ icon: bell
411
+ label: "Toggle alert"
412
+ variant: secondary
413
+ }, {
414
+ event: EDIT
415
+ icon: pencil
416
+ label: Edit
417
+ variant: secondary
418
+ }, {
419
+ event: DELETE
420
+ icon: trash-2
421
+ label: Delete
422
+ variant: danger
423
+ }]
424
+ @label "Row actions"
425
+ @description "Actions offered on each row; give an item `roles` to show it only to those viewer roles."
426
+ @tier "presentation"
427
+ viewerRole : string = ""
428
+ @label "Viewer's role"
429
+ @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."
430
+ @tier "policy"
369
431
  }
370
432
  }
371
433