@almadar/std 16.182.0 → 16.184.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 (56) hide show
  1. package/behaviors/lolo/infra/atoms/std-migration-job.lolo +2 -2
  2. package/behaviors/lolo/ui/core/.hand-authored.json +4 -0
  3. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +11 -0
  4. package/behaviors/lolo/ui/core/atoms/std-related.lolo +86 -9
  5. package/behaviors/lolo/ui/core/atoms/std-rich-editor.lolo +115 -1
  6. package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +62 -2
  7. package/behaviors/lolo/ui/core/atoms/std-thread.lolo +43 -2
  8. package/behaviors/lolo/ui/core/atoms/std-tree.lolo +247 -0
  9. package/behaviors/lolo/ui/core/atoms/std-version-history.lolo +110 -2
  10. package/behaviors/lolo/ui/core/molecules/ui-file-tree.lolo +18 -2
  11. package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +6 -2
  12. package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +2 -2
  13. package/behaviors/lolo/ui/core/organisms/ui-form.lolo +2 -2
  14. package/behaviors/lolo/ui/game/atoms/std-fx-trail.lolo +63 -0
  15. package/behaviors/lolo/ui/game/atoms/std-gate-lock.lolo +96 -0
  16. package/behaviors/lolo/ui/game/atoms/std-lives.lolo +121 -0
  17. package/behaviors/lolo/ui/game/atoms/std-patrol-hazard.lolo +96 -0
  18. package/behaviors/lolo/ui/game/atoms/std-tutorial-autodemo.lolo +136 -0
  19. package/behaviors/lolo/ui/game/atoms/std-tutorial-overlay.lolo +197 -0
  20. package/behaviors/lolo/ui/learning/atoms/std-learn-slope-probe.lolo +117 -0
  21. package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +18 -0
  22. package/behaviors/registry/infra/atoms/std-migration-job.orb +2 -2
  23. package/behaviors/registry/ui/core/atoms/std-browse.orb +17 -0
  24. package/behaviors/registry/ui/core/atoms/std-related.orb +256 -3
  25. package/behaviors/registry/ui/core/atoms/std-rich-editor.orb +407 -1
  26. package/behaviors/registry/ui/core/atoms/std-tag-taxonomy.orb +240 -8
  27. package/behaviors/registry/ui/core/atoms/std-thread.orb +254 -4
  28. package/behaviors/registry/ui/core/atoms/std-tree.orb +656 -0
  29. package/behaviors/registry/ui/core/atoms/std-version-history.orb +479 -6
  30. package/behaviors/registry/ui/core/molecules/ui-file-tree.orb +43 -19
  31. package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +28 -3
  32. package/behaviors/registry/ui/core/organisms/ui-form-section.orb +8 -2
  33. package/behaviors/registry/ui/core/organisms/ui-form.orb +8 -2
  34. package/behaviors/registry/ui/game/atoms/std-fx-trail.orb +376 -0
  35. package/behaviors/registry/ui/game/atoms/std-gate-lock.orb +852 -0
  36. package/behaviors/registry/ui/game/atoms/std-lives.orb +739 -0
  37. package/behaviors/registry/ui/game/atoms/std-patrol-hazard.orb +768 -0
  38. package/behaviors/registry/ui/game/atoms/std-tutorial-autodemo.orb +732 -0
  39. package/behaviors/registry/ui/game/atoms/std-tutorial-overlay.orb +1212 -0
  40. package/behaviors/registry/ui/learning/atoms/std-learn-slope-probe.orb +908 -0
  41. package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +26 -0
  42. package/dist/behaviors/exports-reader.js +5478 -2954
  43. package/dist/behaviors/functions/index.d.ts +763 -15
  44. package/dist/behaviors/functions/index.js +5447 -2953
  45. package/dist/behaviors/index.d.ts +1 -1
  46. package/dist/behaviors/index.js +5479 -2955
  47. package/dist/behaviors/query.js +5478 -2954
  48. package/dist/behaviors-embeddings.hash.json +3 -3
  49. package/dist/behaviors-embeddings.json +362 -330
  50. package/dist/behaviors-registry.json +2812 -542
  51. package/dist/index.d.ts +1 -1
  52. package/dist/index.js +5479 -2955
  53. package/dist/knob-embeddings.hash.json +3 -3
  54. package/dist/knob-embeddings.json +1 -1
  55. package/dist/registry/factory-signatures.json +1 -1
  56. package/package.json +2 -2
@@ -297,9 +297,9 @@ orbital MigrationJobOrbital {
297
297
  @description "Name of the host entity this import writes into (e.g. the organism's own entity). The migration service derives the full target descriptor from the running app's schema."
298
298
  @synonyms "destination, import into, target type"
299
299
  @tier "domain"
300
- connectionRef : string = ""
300
+ connectionRef : secret = ""
301
301
  @label "Connection reference"
302
- @description "Secret-store / environment KEY holding the source credentials. The reference is stored; the secret itself never enters config, traces, or snapshots — the migration service resolves it at run time. (Typed `string` until the `secret` config-field type lands in the lolo language — tracked gap.)"
302
+ @description "Secret-store / environment KEY holding the source credentials. The reference is stored; the secret itself never enters config, traces, or snapshots — the migration service resolves it at run time. (Typed `secret` — the config-field type that marks a credential reference; UI password-renders it and traces mask it.)"
303
303
  @synonyms "credential key, connection string reference, secret name"
304
304
  @tier "internal"
305
305
  reviewSlot : slot = modal
@@ -151,6 +151,10 @@
151
151
  "signature": "a923f3042436663f",
152
152
  "board": "packages/almadar-std/behaviors/lolo/ui/core/atoms/std-thread.lolo"
153
153
  },
154
+ "std-tree": {
155
+ "signature": "",
156
+ "board": "packages/almadar-std/behaviors/lolo/ui/core/atoms/std-tree.lolo"
157
+ },
154
158
  "std-version-history": {
155
159
  "signature": "1f1eb4fcabd6cead",
156
160
  "board": "packages/almadar-std/behaviors/lolo/ui/core/atoms/std-version-history.lolo"
@@ -84,6 +84,7 @@ orbital BrowseItemOrbital {
84
84
  (fetch BrowseItem {
85
85
  emit: { failure: BrowseItemLoadFailed, success: BrowseItemLoaded }
86
86
  filter: (and (or ["=", @config.scopeField, ""] ["=", (object/get @entity @config.scopeField), @user.id]) (or ["=", @config.initialFilterField, ""] ["=", (object/get @entity @config.initialFilterField), @config.initialFilterValue]))
87
+ include: @config.include
87
88
  limit: @config.pageSize
88
89
  offset: 0
89
90
  })
@@ -113,6 +114,7 @@ orbital BrowseItemOrbital {
113
114
  (fetch BrowseItem {
114
115
  emit: { failure: BrowseItemLoadFailed, success: BrowseItemLoaded }
115
116
  filter: (and (or ["=", @config.scopeField, ""] ["=", (object/get @entity @config.scopeField), @user.id]) (or ["=", @config.initialFilterField, ""] ["=", (object/get @entity @config.initialFilterField), @config.initialFilterValue]))
117
+ include: @config.include
116
118
  limit: @config.pageSize
117
119
  offset: 0
118
120
  })
@@ -126,6 +128,7 @@ orbital BrowseItemOrbital {
126
128
  (fetch BrowseItem {
127
129
  emit: { failure: BrowseItemLoadFailed, success: BrowseItemLoaded }
128
130
  filter: (and (or ["=", @config.scopeField, ""] ["=", (object/get @entity @config.scopeField), @user.id]) (or ["=", ?searchTerm, ""] (str/includes (object/get @entity name) ?searchTerm)))
131
+ include: @config.include
129
132
  limit: @config.pageSize
130
133
  offset: 0
131
134
  })
@@ -133,6 +136,7 @@ orbital BrowseItemOrbital {
133
136
  (fetch BrowseItem {
134
137
  emit: { failure: BrowseItemLoadFailed, success: BrowseItemLoaded }
135
138
  filter: (and (or ["=", @config.scopeField, ""] ["=", (object/get @entity @config.scopeField), @user.id]) (or ["=", ?value, ""] ["=", (object/get @entity ?field), ?value]))
139
+ include: @config.include
136
140
  limit: @config.pageSize
137
141
  offset: 0
138
142
  })
@@ -140,6 +144,7 @@ orbital BrowseItemOrbital {
140
144
  (fetch BrowseItem {
141
145
  emit: { failure: BrowseItemLoadFailed, success: BrowseItemLoaded }
142
146
  filter: (or ["=", @config.scopeField, ""] ["=", (object/get @entity @config.scopeField), @user.id])
147
+ include: @config.include
143
148
  limit: @config.pageSize
144
149
  offset: ["*", ["-", ?page, 1], @config.pageSize]
145
150
  })
@@ -170,6 +175,7 @@ orbital BrowseItemOrbital {
170
175
  (fetch BrowseItem {
171
176
  emit: { failure: BrowseItemLoadFailed, success: BrowseItemLoaded }
172
177
  filter: (and (or ["=", @config.scopeField, ""] ["=", (object/get @entity @config.scopeField), @user.id]) (or ["=", @config.initialFilterField, ""] ["=", (object/get @entity @config.initialFilterField), @config.initialFilterValue]))
178
+ include: @config.include
173
179
  limit: @config.pageSize
174
180
  offset: 0
175
181
  })
@@ -346,6 +352,11 @@ orbital BrowseItemOrbital {
346
352
  @description "Rows visible per page before the pagination control appears."
347
353
  @synonyms "visible rows, items per page, display limit, show per page"
348
354
  @tier "presentation"
355
+ include : [string] = []
356
+ @label "Which relation fields to hydrate?"
357
+ @description "Entity relation field names to hydrate on fetch (e.g. a `tag : Category` field named `tag`), so grid columns can use dot-paths like tag.name. Empty = no hydration, the default."
358
+ @synonyms "relation hydration, populate relations, joined fields, hydrate relations"
359
+ @tier "internal"
349
360
  filters : [FilterSpec] = []
350
361
  @label "Which filters should appear above the list?"
351
362
  @description "Dropdown filter facets by field. Each fires a refetch when the user changes the selection. Empty = no filter bar."
@@ -1,18 +1,24 @@
1
- app std-related "1.0.0"
1
+ app std-related "1.1.0"
2
2
  "std-related as a Function"
3
3
  ;;
4
- ;; Relation-rendering atom. Assumes the linked entity's relation field has
5
- ;; already been hydrated by an upstream fetch with include; this atom only
6
- ;; owns the rendering of the related children.
4
+ ;; Relation-rendering atom. INIT loads the full RelatedItem collection
5
+ ;; unfiltered (unchanged default). A consumer that wants the list scoped to
6
+ ;; one parent document sets `relationField` (the RelatedItem column that
7
+ ;; holds the parent key) and redirects its own "document opened" signal to
8
+ ;; SHOW_RELATED { key } in a `listens` override — the atom then fetches only
9
+ ;; rows whose relationField equals key. Each row is clickable: it emits
10
+ ;; SELECT_RELATED { id, name } for a parent molecule (e.g.
11
+ ;; std-related-master-detail) to drive a detail panel.
7
12
  ;;
8
13
  ;; Paired with std-browse via @trait.X this gives a one-line master-detail
9
- ;; layout: hstack [ @trait.ParentList, @trait.ParentRelated ]. The molecule
10
- ;; std-related-master-detail is the canonical showcase.
14
+ ;; layout: hstack [ @trait.ParentList, @trait.ParentRelated ].
11
15
  orbital RelatedItemOrbital {
12
16
  type RelatedItemLoaded = Event { data : [RelatedItem] } "Fired when RelatedItem finishes loading; payload.data holds the list"
13
17
 
14
18
  type RelatedItemLoadFailed = Event { error : string, code : string } "Fired when RelatedItem fails to load"
15
19
 
20
+ type ShowRelated = Event { key : string! } "Fired to scope the related fetch to rows whose relationField equals key"
21
+
16
22
  entity RelatedItem [persistent: relateditems] {
17
23
  id : string!
18
24
  name : string @description "The identifier or designation for the related item." @synonyms "title, label, identifier"
@@ -39,12 +45,30 @@ orbital RelatedItemOrbital {
39
45
  gap: sm
40
46
  type: stack
41
47
  })
48
+ SHOW_RELATED -> loading
49
+ (fetch RelatedItem {
50
+ emit: { failure: RelatedItemLoadFailed, success: RelatedItemLoaded }
51
+ filter: (or ["=", @config.relationField, ""] ["=", (object/get @entity @config.relationField), ?key])
52
+ })
53
+ (render-ui main {
54
+ align: center
55
+ children: [{ type: spinner }, {
56
+ color: muted
57
+ content: "Loading related…"
58
+ type: typography
59
+ variant: caption
60
+ }]
61
+ className: py-8
62
+ direction: vertical
63
+ gap: sm
64
+ type: stack
65
+ })
42
66
  RelatedItemLoaded -> loaded
43
67
  (render-ui main {
44
68
  children: [{
45
69
  align: center
46
70
  children: [{ name: link, type: icon }, {
47
- content: Related
71
+ content: @config.title
48
72
  type: typography
49
73
  variant: h3
50
74
  }]
@@ -56,13 +80,23 @@ orbital RelatedItemOrbital {
56
80
  fields: []
57
81
  look: @config.tableLook
58
82
  renderItem: (fn item {
83
+ align: center
59
84
  children: [{
60
85
  content: @item.name
61
86
  type: typography
62
87
  variant: body
88
+ }, {
89
+ action: SELECT_RELATED
90
+ actionPayload: { id: @item.id, name: @item.name }
91
+ icon: chevron-right
92
+ label: Open
93
+ size: sm
94
+ type: button
95
+ variant: ghost
63
96
  }]
64
- direction: vertical
97
+ direction: horizontal
65
98
  gap: xs
99
+ justify: between
66
100
  type: stack
67
101
  })
68
102
  type: data-list
@@ -107,6 +141,14 @@ orbital RelatedItemOrbital {
107
141
  emit: { failure: RelatedItemLoadFailed, success: RelatedItemLoaded }
108
142
  })
109
143
  (render-ui main { type: spinner })
144
+ SELECT_RELATED -> loaded
145
+ (set @entity.id ?id)
146
+ SHOW_RELATED -> loading
147
+ (fetch RelatedItem {
148
+ emit: { failure: RelatedItemLoadFailed, success: RelatedItemLoaded }
149
+ filter: (or ["=", @config.relationField, ""] ["=", (object/get @entity @config.relationField), ?key])
150
+ })
151
+ (render-ui main { type: spinner })
110
152
  }
111
153
 
112
154
  state error {
@@ -122,6 +164,12 @@ orbital RelatedItemOrbital {
122
164
  emit: { failure: RelatedItemLoadFailed, success: RelatedItemLoaded }
123
165
  })
124
166
  (render-ui main { type: spinner })
167
+ SHOW_RELATED -> loading
168
+ (fetch RelatedItem {
169
+ emit: { failure: RelatedItemLoadFailed, success: RelatedItemLoaded }
170
+ filter: (or ["=", @config.relationField, ""] ["=", (object/get @entity @config.relationField), ?key])
171
+ })
172
+ (render-ui main { type: spinner })
125
173
  }
126
174
 
127
175
  emits {
@@ -133,9 +181,23 @@ orbital RelatedItemOrbital {
133
181
  @description "Indicates failure to load related items."
134
182
  @synonyms "error, failure, problem, unsuccessful"
135
183
  @tier "internal"
184
+ SELECT_RELATED -> external {
185
+ id : string!
186
+ name : string
187
+ }
188
+ @description "Signals a related item was selected to drive a detail view."
189
+ @synonyms "select, choose, open, view"
190
+ @tier "domain"
136
191
  }
137
192
 
138
193
  listens {
194
+ SELECT_RELATED {
195
+ id : string!
196
+ name : string
197
+ }
198
+ @description "A related item was selected; the atom records it as the active row."
199
+ @synonyms "select, choose, open, view"
200
+ @tier "domain"
139
201
  RelatedItemLoadFailed {
140
202
  error : string
141
203
  code : string
@@ -143,9 +205,23 @@ orbital RelatedItemOrbital {
143
205
  @description "Indicates failure to load related items."
144
206
  @synonyms "error, failure, problem, unsuccessful"
145
207
  @tier "internal"
208
+ SHOW_RELATED {
209
+ key : string!
210
+ }
211
+ @description "Scopes the related fetch to rows whose relationField equals key."
212
+ @synonyms "scope, filter, focus, target"
213
+ @tier "domain"
146
214
  }
147
215
 
148
216
  config {
217
+ title : string = Related
218
+ @label "Section title"
219
+ @description "Heading shown above the related list."
220
+ @tier "presentation"
221
+ relationField : string = ""
222
+ @label "Relation field"
223
+ @description "RelatedItem field compared against SHOW_RELATED's key to scope the fetch to one parent document; empty renders the full unfiltered collection (default)."
224
+ @tier "domain"
149
225
  tableLook : "dense" | "spacious" | "striped" | "borderless" | "card-rows" = dense
150
226
  @label "Table look"
151
227
  @description "Layer 2 visual treatment for the data table rendered by this atom."
@@ -153,6 +229,7 @@ orbital RelatedItemOrbital {
153
229
  }
154
230
  }
155
231
 
156
- ;; consumer: parent molecule (e.g. std-related-master-detail) listens for SELECT_RELATED to drive a detail panel
232
+ ;; consumer: parent molecule (e.g. std-related-master-detail) listens for SELECT_RELATED to drive a detail panel; a
233
+ ;; per-document scope forwards its "opened" event to SHOW_RELATED { key } via a listens override on relationField
157
234
  page "/related" as Related -> RelatedItemList
158
235
  }
@@ -1,5 +1,5 @@
1
1
  app std-rich-editor "1.0.0"
2
- "std-rich-editor — wiki/CMS Notion-style block editor. Browses documents in `browsing`; opens the rich-block-editor on a selected row; persists block edits + publish status."
2
+ "std-rich-editor — wiki/CMS Notion-style block editor. Browses documents in `browsing`; opens the rich-block-editor on a selected row; persists block edits + publish status. `BlockEdit` is a rebindable single-row overlay editor: an organism rebinds it to its own entity (WikiPage, Note, …), passes the row's id + blocksJson via OPEN, and routes the emitted BLOCKS_SAVED into its own persistor — this atom never fetches or persists."
3
3
  ;; @capabilities "notes app, note-taking, journal, personal journal entries, document editor, write and organize notes"
4
4
  type BlockSpec = { type : string!, content : string, id : string }
5
5
  type ObjectSpec = { id : string, label : string, value : string }
@@ -419,5 +419,119 @@ orbital DocumentOrbital {
419
419
  }
420
420
  }
421
421
 
422
+ trait BlockEdit -> @rebindable Document [interaction, instance] {
423
+ initial: idle
424
+
425
+ state idle {
426
+ INIT -> idle
427
+ OPEN -> editing
428
+ (set @entity.id ?id)
429
+ (set @entity.blocksJson ?blocksJson)
430
+ (render-ui modal {
431
+ children: [{
432
+ align: center
433
+ children: [{ name: file-text, type: icon }, {
434
+ content: @config.title
435
+ type: typography
436
+ variant: h3
437
+ }]
438
+ direction: horizontal
439
+ gap: sm
440
+ type: stack
441
+ }, { type: divider }, {
442
+ changeEvent: BLOCKS_CHANGE
443
+ initialBlocks: @entity.blocksJson
444
+ placeholder: @config.placeholder
445
+ showToolbar: true
446
+ type: rich-block-editor
447
+ }, {
448
+ align: center
449
+ children: [{
450
+ action: SAVE
451
+ label: Save
452
+ type: button
453
+ variant: primary
454
+ }, {
455
+ action: CLOSE
456
+ label: Cancel
457
+ type: button
458
+ variant: ghost
459
+ }]
460
+ direction: horizontal
461
+ gap: sm
462
+ type: stack
463
+ }]
464
+ direction: vertical
465
+ gap: md
466
+ type: stack
467
+ })
468
+ ;; self-loop on the external emit so the validator sees an in-atom
469
+ ;; listener — the real consumer is the composing organism's persistor.
470
+ BLOCKS_SAVED -> idle
471
+ }
472
+
473
+ state editing {
474
+ BLOCKS_CHANGE -> editing
475
+ (set @entity.blocksJson ?blocks)
476
+ SAVE -> idle
477
+ (render-ui modal null)
478
+ (emit BLOCKS_SAVED { id: @entity.id, blocksJson: @entity.blocksJson })
479
+ CLOSE -> idle
480
+ (render-ui modal null)
481
+ }
482
+
483
+ emits {
484
+ BLOCKS_SAVED -> external {
485
+ id : string!
486
+ blocksJson : [BlockSpec]
487
+ }
488
+ @description "Signals a row's block content is ready to persist. This atom never persists — the composing organism routes BLOCKS_SAVED into its own persistor's update event."
489
+ @synonyms "saved, persist, update, commit"
490
+ @tier "domain"
491
+ CLOSE
492
+ @description "Indicates the block editor overlay is being dismissed."
493
+ @synonyms "dismiss, hide, cancel, close"
494
+ @tier "domain"
495
+ BLOCKS_CHANGE {
496
+ blocks : [BlockSpec]
497
+ }
498
+ @description "Signals that the content of a block has been modified while editing."
499
+ @synonyms "update, modify, changed, edit"
500
+ @tier "domain"
501
+ }
502
+
503
+ listens {
504
+ OPEN {
505
+ id : string!
506
+ blocksJson : [BlockSpec]
507
+ }
508
+ @description "Requests the block editor overlay open for the given row. The caller supplies the row's id and current blocks — this trait never fetches them itself."
509
+ @synonyms "show, launch, reveal, edit"
510
+ @tier "domain"
511
+ BLOCKS_CHANGE {
512
+ blocks : [BlockSpec]
513
+ }
514
+ @description "Indicates changes to the content of editable blocks while editing."
515
+ @synonyms "update, modify, edit"
516
+ @tier "domain"
517
+ CLOSE
518
+ @description "Indicates the block editor overlay is being dismissed."
519
+ @synonyms "dismiss, hide, cancel, close"
520
+ @tier "domain"
521
+ }
522
+
523
+ config {
524
+ title : string = "Edit content"
525
+ @label "Editor heading"
526
+ @description "Heading shown at the top of the block editor overlay."
527
+ @tier "presentation"
528
+ placeholder : string = "Start writing…"
529
+ @label "Placeholder text"
530
+ @description "Placeholder shown in the block editor when it is empty."
531
+ @tier "presentation"
532
+ }
533
+ }
534
+
422
535
  page "/documents/edit" as DocumentEditPage -> DocumentEdit
536
+ page "/documents/block-edit" as BlockEditPage -> BlockEdit
423
537
  }
@@ -1,5 +1,5 @@
1
1
  app std-tag-taxonomy "1.1.0"
2
- "std-tag-taxonomy — hierarchical category tag tree (parent/child). TagBrowse fetches Tag rows on INIT, renders the active level via data-grid, and on SELECT_TAG drills into a tag's children by binding @entity.id to the chosen parent."
2
+ "std-tag-taxonomy — hierarchical category tag tree (parent/child). TagBrowse fetches root-level Tag rows (parentId = \"\") on INIT and renders the active level via data-grid; SELECT_TAG drills into the chosen tag's children by fetching rows whose parentId matches it, binding @entity.id to that parent so the level is remembered; SHOW_ROOT resets back to the root level, surfaced as a back affordance while drilled in. Flat taxonomies (all parentId \"\") render exactly one level, unchanged. TagSelected is emitted on selection for consumers to filter content by tag."
3
3
  orbital TagTaxonomyOrbital {
4
4
  type TagLoaded = Event { data : [Tag] } "Fired when the tag collection finishes loading"
5
5
 
@@ -10,7 +10,7 @@ orbital TagTaxonomyOrbital {
10
10
  entity Tag [persistent: tags] {
11
11
  id : string!
12
12
  name : string! @description "The descriptive label for this item." @synonyms "label, title, description"
13
- parentId : string @description "Reference to the parent tag in the hierarchy." @synonyms "parent, superior, ancestor"
13
+ parentId : Tag @description "Reference to the parent tag in the hierarchy; empty marks a root." @synonyms "parent, superior, ancestor"
14
14
  slug : string @description "A unique, URL-friendly identifier." @synonyms "identifier, key, code"
15
15
  color : string @description "A visual identifier, often used for categorization." @synonyms "hue, shade, tint, category color"
16
16
  count : number @description "The number of items associated with this entry." @synonyms "quantity, total, size, amount" = 0
@@ -22,7 +22,10 @@ orbital TagTaxonomyOrbital {
22
22
 
23
23
  state loading {
24
24
  INIT -> loading
25
+ (set @entity.id "")
26
+ (set @entity.parentId "")
25
27
  (fetch Tag {
28
+ filter: ["=", (object/get @entity parentId), ""]
26
29
  emit: { failure: TagLoadFailed, success: TagLoaded }
27
30
  })
28
31
  (render-ui main {
@@ -41,6 +44,14 @@ orbital TagTaxonomyOrbital {
41
44
  TagLoaded -> browsing
42
45
  (render-ui main {
43
46
  children: [{
47
+ action: SHOW_ROOT
48
+ disabled: (if (!= @entity.id "") false true)
49
+ icon: arrow-left
50
+ label: "All categories"
51
+ size: sm
52
+ type: button
53
+ variant: ghost
54
+ }, {
44
55
  content: @config.title
45
56
  type: typography
46
57
  variant: h3
@@ -115,7 +126,10 @@ orbital TagTaxonomyOrbital {
115
126
 
116
127
  state browsing {
117
128
  INIT -> loading
129
+ (set @entity.id "")
130
+ (set @entity.parentId "")
118
131
  (fetch Tag {
132
+ filter: ["=", (object/get @entity parentId), ""]
119
133
  emit: { failure: TagLoadFailed, success: TagLoaded }
120
134
  })
121
135
  (render-ui main {
@@ -133,8 +147,10 @@ orbital TagTaxonomyOrbital {
133
147
  })
134
148
  SELECT_TAG -> editing
135
149
  (set @entity.id ?tagId)
150
+ (set @entity.parentId ?tagId)
136
151
  (emit TagSelected { tagId: ?tagId })
137
152
  (fetch Tag {
153
+ filter: ["=", (object/get @entity parentId), ?tagId]
138
154
  emit: { failure: TagLoadFailed, success: TagLoaded }
139
155
  })
140
156
  (render-ui main {
@@ -150,9 +166,37 @@ orbital TagTaxonomyOrbital {
150
166
  gap: md
151
167
  type: stack
152
168
  })
169
+ SHOW_ROOT -> loading
170
+ (set @entity.id "")
171
+ (set @entity.parentId "")
172
+ (fetch Tag {
173
+ filter: ["=", (object/get @entity parentId), ""]
174
+ emit: { failure: TagLoadFailed, success: TagLoaded }
175
+ })
176
+ (render-ui main {
177
+ align: center
178
+ children: [{ type: spinner }, {
179
+ color: muted
180
+ content: "Loading categories…"
181
+ type: typography
182
+ variant: caption
183
+ }]
184
+ className: py-12
185
+ direction: vertical
186
+ gap: md
187
+ type: stack
188
+ })
153
189
  TagLoaded -> browsing
154
190
  (render-ui main {
155
191
  children: [{
192
+ action: SHOW_ROOT
193
+ disabled: (if (!= @entity.id "") false true)
194
+ icon: arrow-left
195
+ label: "All categories"
196
+ size: sm
197
+ type: button
198
+ variant: ghost
199
+ }, {
156
200
  content: @config.title
157
201
  type: typography
158
202
  variant: h3
@@ -227,7 +271,10 @@ orbital TagTaxonomyOrbital {
227
271
 
228
272
  state editing {
229
273
  INIT -> loading
274
+ (set @entity.id "")
275
+ (set @entity.parentId "")
230
276
  (fetch Tag {
277
+ filter: ["=", (object/get @entity parentId), ""]
231
278
  emit: { failure: TagLoadFailed, success: TagLoaded }
232
279
  })
233
280
  (render-ui main {
@@ -245,12 +292,22 @@ orbital TagTaxonomyOrbital {
245
292
  })
246
293
  SELECT_TAG -> editing
247
294
  (set @entity.id ?tagId)
295
+ (set @entity.parentId ?tagId)
248
296
  (emit TagSelected { tagId: ?tagId })
249
297
  TagSelected -> editing
250
298
  (set @entity.id ?tagId)
299
+ (set @entity.parentId ?tagId)
251
300
  TagLoaded -> browsing
252
301
  (render-ui main {
253
302
  children: [{
303
+ action: SHOW_ROOT
304
+ disabled: (if (!= @entity.id "") false true)
305
+ icon: arrow-left
306
+ label: "All categories"
307
+ size: sm
308
+ type: button
309
+ variant: ghost
310
+ }, {
254
311
  content: @config.title
255
312
  type: typography
256
313
  variant: h3
@@ -325,7 +382,10 @@ orbital TagTaxonomyOrbital {
325
382
 
326
383
  state error {
327
384
  INIT -> loading
385
+ (set @entity.id "")
386
+ (set @entity.parentId "")
328
387
  (fetch Tag {
388
+ filter: ["=", (object/get @entity parentId), ""]
329
389
  emit: { failure: TagLoadFailed, success: TagLoaded }
330
390
  })
331
391
  (render-ui main {
@@ -1,5 +1,5 @@
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. 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."
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 the frame and refetches filtered by the payload; the after-create and after-select refetch arms keep filtering by @config.threadRootId, so a mutation re-widens the panel until the consumer re-fires SET_THREAD_ROOT. 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."
3
3
  orbital ThreadPostOrbital {
4
4
  ;; Forward relation: array of child ThreadPost IDs whose parentId === this.id.
5
5
  ;; Lowers to `type: "relation", cardinality: "many"` (orbital-lolo lower.rs).
@@ -19,6 +19,8 @@ orbital ThreadPostOrbital {
19
19
 
20
20
  type ThreadPostCreateFailed = Event { error : string, code : string } "Fired when a new thread-post fails to persist"
21
21
 
22
+ type SetThreadRoot = Event { rootId : string! } "Fired to scope every ThreadPost fetch, and new-reply threadRootId stamping, to the descendants of one document root"
23
+
22
24
  entity ThreadPost [persistent: threadposts] {
23
25
  id : string!
24
26
  parentId : string @description "Identifier of the parent ThreadPost in a nested thread." @synonyms "parent, ancestor, source"
@@ -52,6 +54,14 @@ orbital ThreadPostOrbital {
52
54
  type: alert
53
55
  variant: error
54
56
  })
57
+ SET_THREAD_ROOT -> loading
58
+ (set @entity.threadRootId ?rootId)
59
+ (fetch ThreadPost {
60
+ emit: { failure: ThreadPostLoadFailed, success: ThreadPostLoaded }
61
+ filter: (or ["=", ?rootId, ""] ["=", (object/get @entity threadRootId), ?rootId])
62
+ include: (replies)
63
+ })
64
+ (render-ui main { size: sm, type: spinner })
55
65
  }
56
66
 
57
67
  state browsing {
@@ -73,13 +83,14 @@ orbital ThreadPostOrbital {
73
83
  authorName: @user.name
74
84
  content: ?content
75
85
  parentId: (str/default ?parentNodeId ?parentId)
76
- threadRootId: (str/default ?parentNodeId (str/default @config.threadRootId root))
86
+ threadRootId: (str/default ?parentNodeId (str/default @entity.threadRootId (str/default @config.threadRootId root)))
77
87
  } {
78
88
  emit: { failure: ThreadPostCreateFailed, success: ThreadPostCreated }
79
89
  })
80
90
  ;; Reply with no text yet (e.g. an email "Reply" button) opens the composer.
81
91
  REPLY -> replying when ["=", (str/default ?content ""), ""]
82
92
  (set @entity.parentId (str/default ?parentNodeId ?parentId))
93
+ (set @entity.threadRootId (str/default ?parentNodeId (str/default @entity.threadRootId (str/default @config.threadRootId root))))
83
94
  (render-ui main {
84
95
  children: [{
85
96
  content: "Write a reply"
@@ -139,6 +150,14 @@ orbital ThreadPostOrbital {
139
150
  type: alert
140
151
  variant: error
141
152
  })
153
+ SET_THREAD_ROOT -> loading
154
+ (set @entity.threadRootId ?rootId)
155
+ (fetch ThreadPost {
156
+ emit: { failure: ThreadPostLoadFailed, success: ThreadPostLoaded }
157
+ filter: (or ["=", ?rootId, ""] ["=", (object/get @entity threadRootId), ?rootId])
158
+ include: (replies)
159
+ })
160
+ (render-ui main { size: sm, type: spinner })
142
161
  }
143
162
 
144
163
  state replying {
@@ -165,6 +184,14 @@ orbital ThreadPostOrbital {
165
184
  type: alert
166
185
  variant: error
167
186
  })
187
+ SET_THREAD_ROOT -> loading
188
+ (set @entity.threadRootId ?rootId)
189
+ (fetch ThreadPost {
190
+ emit: { failure: ThreadPostLoadFailed, success: ThreadPostLoaded }
191
+ filter: (or ["=", ?rootId, ""] ["=", (object/get @entity threadRootId), ?rootId])
192
+ include: (replies)
193
+ })
194
+ (render-ui main { size: sm, type: spinner })
168
195
  }
169
196
 
170
197
  state error {
@@ -175,6 +202,14 @@ orbital ThreadPostOrbital {
175
202
  include: (replies)
176
203
  })
177
204
  (render-ui main { size: sm, type: spinner })
205
+ SET_THREAD_ROOT -> loading
206
+ (set @entity.threadRootId ?rootId)
207
+ (fetch ThreadPost {
208
+ emit: { failure: ThreadPostLoadFailed, success: ThreadPostLoaded }
209
+ filter: (or ["=", ?rootId, ""] ["=", (object/get @entity threadRootId), ?rootId])
210
+ include: (replies)
211
+ })
212
+ (render-ui main { size: sm, type: spinner })
178
213
  }
179
214
 
180
215
  emits {
@@ -295,6 +330,12 @@ orbital ThreadPostOrbital {
295
330
  @description "Indicates a failure to create a new thread post."
296
331
  @synonyms "error, failure, rejected, invalid"
297
332
  @tier "internal"
333
+ SET_THREAD_ROOT {
334
+ rootId : string!
335
+ }
336
+ @description "Scopes every fetch, and new-reply threadRootId stamping, to the descendants of one document root."
337
+ @synonyms "scope, focus, set root, document scope, thread scope"
338
+ @tier "domain"
298
339
  }
299
340
 
300
341
  config {