@almadar/std 16.191.0 → 16.193.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 (113) hide show
  1. package/behaviors/lolo/infra/atoms/std-approval-gate.lolo +184 -18
  2. package/behaviors/lolo/infra/atoms/std-status-lifecycle.lolo +16 -3
  3. package/behaviors/lolo/ui/core/atoms/std-app-layout.lolo +6 -0
  4. package/behaviors/lolo/ui/core/atoms/std-billable-hour.lolo +1 -0
  5. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +3 -0
  6. package/behaviors/lolo/ui/core/atoms/std-image-upload-multi.lolo +37 -4
  7. package/behaviors/lolo/ui/core/atoms/std-record-detail.lolo +37 -0
  8. package/behaviors/lolo/ui/core/atoms/std-tag-strip.lolo +314 -0
  9. package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +195 -13
  10. package/behaviors/lolo/ui/core/atoms/std-tree.lolo +104 -12
  11. package/behaviors/lolo/ui/core/atoms/std-version-history.lolo +3 -1
  12. package/behaviors/lolo/ui/core/molecules/ui-calendar-grid.lolo +25 -1
  13. package/behaviors/lolo/ui/core/molecules/ui-data-grid.lolo +37 -1
  14. package/behaviors/lolo/ui/core/molecules/ui-data-list.lolo +46 -1
  15. package/behaviors/lolo/ui/core/molecules/ui-document-details.lolo +30 -2
  16. package/behaviors/lolo/ui/core/molecules/ui-document-panel.lolo +14 -0
  17. package/behaviors/lolo/ui/core/molecules/ui-file-tree.lolo +25 -0
  18. package/behaviors/lolo/ui/core/molecules/ui-positioned-canvas.lolo +19 -1
  19. package/behaviors/lolo/ui/core/molecules/ui-reply-tree.lolo +21 -1
  20. package/behaviors/lolo/ui/core/molecules/ui-sortable-list.lolo +15 -1
  21. package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +36 -1
  22. package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +41 -1
  23. package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +34 -1
  24. package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +30 -1
  25. package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +36 -1
  26. package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +39 -1
  27. package/behaviors/lolo/ui/core/organisms/ui-form.lolo +39 -1
  28. package/behaviors/lolo/ui/core/organisms/ui-master-detail.lolo +24 -1
  29. package/behaviors/lolo/ui/core/organisms/ui-media-gallery.lolo +30 -1
  30. package/behaviors/lolo/ui/core/organisms/ui-stat-card.lolo +37 -1
  31. package/behaviors/lolo/ui/core/organisms/ui-timeline.lolo +18 -1
  32. package/behaviors/lolo/ui/core/templates/ui-counter-template.lolo +21 -1
  33. package/behaviors/lolo/ui/core/templates/ui-dashboard-layout.lolo +5 -0
  34. package/behaviors/lolo/ui/core/templates/ui-generic-app-template.lolo +16 -1
  35. package/behaviors/lolo/ui/marketing/organisms/ui-book-chapter-view.lolo +12 -1
  36. package/behaviors/lolo/ui/marketing/organisms/ui-book-table-of-contents.lolo +13 -1
  37. package/behaviors/lolo/ui/marketing/organisms/ui-book-viewer.lolo +23 -1
  38. package/behaviors/lolo/ui/marketing/organisms/ui-case-study-organism.lolo +23 -1
  39. package/behaviors/lolo/ui/marketing/organisms/ui-feature-grid-organism.lolo +24 -1
  40. package/behaviors/lolo/ui/marketing/organisms/ui-hero-organism.lolo +22 -1
  41. package/behaviors/lolo/ui/marketing/organisms/ui-pricing-organism.lolo +23 -1
  42. package/behaviors/lolo/ui/marketing/organisms/ui-showcase-organism.lolo +24 -1
  43. package/behaviors/lolo/ui/marketing/organisms/ui-stats-organism.lolo +22 -1
  44. package/behaviors/lolo/ui/marketing/organisms/ui-step-flow-organism.lolo +25 -1
  45. package/behaviors/lolo/ui/marketing/organisms/ui-team-organism.lolo +23 -1
  46. package/behaviors/lolo/ui/marketing/templates/ui-about-page-template.lolo +11 -1
  47. package/behaviors/lolo/ui/marketing/templates/ui-feature-detail-page-template.lolo +11 -1
  48. package/behaviors/lolo/ui/marketing/templates/ui-landing-page-template.lolo +13 -1
  49. package/behaviors/lolo/ui/marketing/templates/ui-pricing-page-template.lolo +11 -1
  50. package/behaviors/registry/infra/atoms/std-approval-gate.orb +717 -45
  51. package/behaviors/registry/infra/atoms/std-status-lifecycle.orb +85 -10
  52. package/behaviors/registry/ui/core/atoms/std-app-layout.orb +8 -0
  53. package/behaviors/registry/ui/core/atoms/std-billable-hour.orb +5 -2
  54. package/behaviors/registry/ui/core/atoms/std-browse.orb +12 -0
  55. package/behaviors/registry/ui/core/atoms/std-image-upload-multi.orb +129 -7
  56. package/behaviors/registry/ui/core/atoms/std-record-detail.orb +316 -0
  57. package/behaviors/registry/ui/core/atoms/std-tag-strip.orb +1360 -0
  58. package/behaviors/registry/ui/core/atoms/std-tag-taxonomy.orb +564 -10
  59. package/behaviors/registry/ui/core/atoms/std-tree.orb +427 -66
  60. package/behaviors/registry/ui/core/atoms/std-version-history.orb +5 -1
  61. package/behaviors/registry/ui/core/molecules/ui-calendar-grid.orb +43 -0
  62. package/behaviors/registry/ui/core/molecules/ui-data-grid.orb +55 -0
  63. package/behaviors/registry/ui/core/molecules/ui-data-list.orb +64 -0
  64. package/behaviors/registry/ui/core/molecules/ui-document-details.orb +106 -1
  65. package/behaviors/registry/ui/core/molecules/ui-document-panel.orb +18 -0
  66. package/behaviors/registry/ui/core/molecules/ui-file-tree.orb +41 -0
  67. package/behaviors/registry/ui/core/molecules/ui-positioned-canvas.orb +37 -0
  68. package/behaviors/registry/ui/core/molecules/ui-reply-tree.orb +39 -0
  69. package/behaviors/registry/ui/core/molecules/ui-sortable-list.orb +45 -0
  70. package/behaviors/registry/ui/core/molecules/ui-table-view.orb +54 -0
  71. package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +59 -0
  72. package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +52 -0
  73. package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +48 -0
  74. package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +54 -0
  75. package/behaviors/registry/ui/core/organisms/ui-form-section.orb +57 -0
  76. package/behaviors/registry/ui/core/organisms/ui-form.orb +57 -0
  77. package/behaviors/registry/ui/core/organisms/ui-master-detail.orb +42 -0
  78. package/behaviors/registry/ui/core/organisms/ui-media-gallery.orb +48 -0
  79. package/behaviors/registry/ui/core/organisms/ui-stat-card.orb +55 -0
  80. package/behaviors/registry/ui/core/organisms/ui-timeline.orb +36 -0
  81. package/behaviors/registry/ui/core/templates/ui-counter-template.orb +39 -0
  82. package/behaviors/registry/ui/core/templates/ui-dashboard-layout.orb +7 -0
  83. package/behaviors/registry/ui/core/templates/ui-generic-app-template.orb +34 -0
  84. package/behaviors/registry/ui/marketing/organisms/ui-book-chapter-view.orb +30 -0
  85. package/behaviors/registry/ui/marketing/organisms/ui-book-table-of-contents.orb +31 -0
  86. package/behaviors/registry/ui/marketing/organisms/ui-book-viewer.orb +41 -0
  87. package/behaviors/registry/ui/marketing/organisms/ui-case-study-organism.orb +41 -0
  88. package/behaviors/registry/ui/marketing/organisms/ui-feature-grid-organism.orb +42 -0
  89. package/behaviors/registry/ui/marketing/organisms/ui-hero-organism.orb +40 -0
  90. package/behaviors/registry/ui/marketing/organisms/ui-pricing-organism.orb +41 -0
  91. package/behaviors/registry/ui/marketing/organisms/ui-showcase-organism.orb +42 -0
  92. package/behaviors/registry/ui/marketing/organisms/ui-stats-organism.orb +40 -0
  93. package/behaviors/registry/ui/marketing/organisms/ui-step-flow-organism.orb +43 -0
  94. package/behaviors/registry/ui/marketing/organisms/ui-team-organism.orb +41 -0
  95. package/behaviors/registry/ui/marketing/templates/ui-about-page-template.orb +29 -0
  96. package/behaviors/registry/ui/marketing/templates/ui-feature-detail-page-template.orb +29 -0
  97. package/behaviors/registry/ui/marketing/templates/ui-landing-page-template.orb +31 -0
  98. package/behaviors/registry/ui/marketing/templates/ui-pricing-page-template.orb +29 -0
  99. package/dist/behaviors/exports-reader.js +4884 -1967
  100. package/dist/behaviors/functions/index.d.ts +293 -13
  101. package/dist/behaviors/functions/index.js +4880 -1968
  102. package/dist/behaviors/index.d.ts +1 -1
  103. package/dist/behaviors/index.js +4885 -1968
  104. package/dist/behaviors/query.js +4884 -1967
  105. package/dist/behaviors-embeddings.hash.json +3 -3
  106. package/dist/behaviors-embeddings.json +21 -13
  107. package/dist/behaviors-registry.json +685 -60
  108. package/dist/index.d.ts +1 -1
  109. package/dist/index.js +4885 -1968
  110. package/dist/knob-embeddings.hash.json +3 -3
  111. package/dist/knob-embeddings.json +1 -1
  112. package/dist/registry/factory-signatures.json +1 -1
  113. package/package.json +2 -2
@@ -0,0 +1,314 @@
1
+ app std-tag-strip "1.0.0"
2
+ "std-tag-strip — inline tag editing ON the document surface: the record's tags render as removable chips with a type-to-add input (a typed name that matches an existing tag attaches it; an unknown name is created in the shared taxonomy first), plus one-click chips for every existing tag not yet attached. Emits TAGS_CHANGED { id, tagIds } — the host organism routes it into its persistor's update; the document's refetch re-scopes the strip. Binds any record carrying a tagIds relation array; the tag store is the shared `tags` collection (std-tag-taxonomy's)."
3
+ ;; @capabilities "inline tags, tag chips on a document, add tag from taxonomy, create tag by typing, remove tag chip, tag editing in place"
4
+ orbital TagStripOrbital {
5
+ type TagStripOptions = Event { data : [StripTag] } "The shared tag store loaded for the strip's display pass"
6
+
7
+ type TagStripAddOptions = Event { data : [StripTag] } "The shared tag store loaded to resolve a typed tag name on add"
8
+
9
+ type TagStripRemoveOptions = Event { data : [StripTag] } "The shared tag store loaded to resolve the remaining names on remove"
10
+
11
+ type TagStripTagCreated = Event { id : string, row : StripTag } "A typed tag name had no existing row; one was created in the shared taxonomy"
12
+
13
+ type TagStripLoadFailed = Event { error : string, code : string } "The tag store fetch or create failed"
14
+
15
+ entity StripTag [persistent: tags] {
16
+ id : string!
17
+ name : string! @description "The descriptive label for this tag." @synonyms "label, title"
18
+ parentId : StripTag @description "Reference to the parent tag in the hierarchy; empty marks a root." @synonyms "parent, ancestor"
19
+ createdAt : string = ""
20
+ pendingId : string = ""
21
+ docId : string @intrinsic @description "The open record's id, stashed at SHOW_TAG_STRIP so commits know which row to update." @synonyms "record id, scope id" = ""
22
+ docTagIds : [string] @intrinsic @description "The open record's current tag ids, stashed at SHOW_TAG_STRIP for membership tests." @synonyms "attached tags, current tags"
23
+ pendingTagName : string @intrinsic @description "The tag name typed or clicked on add, held across the resolve fetch." @synonyms "typed tag, adding tag" = ""
24
+ pendingNames : [string] @intrinsic @description "The chip names remaining after a remove, held across the resolve fetch." @synonyms "remaining tags, kept names"
25
+ }
26
+
27
+ trait TagStrip -> @rebindable StripTag [interaction, collection] {
28
+ initial: idle
29
+
30
+ state idle {
31
+ INIT -> idle
32
+ (fetch StripTag {
33
+ emit: { failure: TagStripLoadFailed, success: TagStripOptions }
34
+ })
35
+ ;; Pre-scope paint: the store is warm but no record is open yet.
36
+ TagStripOptions -> idle
37
+ TagStripLoadFailed -> idle
38
+ SHOW_TAG_STRIP -> loading
39
+ (set @entity.docId ?id)
40
+ (set @entity.docTagIds (array/map ?tagIds (fn t (str/default (object/get @t id) @t))))
41
+ (fetch StripTag {
42
+ emit: { failure: TagStripLoadFailed, success: TagStripOptions }
43
+ })
44
+ }
45
+
46
+ state loading {
47
+ SHOW_TAG_STRIP -> loading
48
+ (set @entity.docId ?id)
49
+ (set @entity.docTagIds (array/map ?tagIds (fn t (str/default (object/get @t id) @t))))
50
+ (fetch StripTag {
51
+ emit: { failure: TagStripLoadFailed, success: TagStripOptions }
52
+ })
53
+ TagStripOptions -> ready
54
+ (render-ui main {
55
+ children: [(if ["!=", @config.title, ""] {
56
+ color: muted
57
+ content: @config.title
58
+ type: typography
59
+ variant: caption
60
+ } null), {
61
+ addEvent: ADD_TAG
62
+ helperText: @config.helperText
63
+ placeholder: @config.placeholder
64
+ removeEvent: REMOVE_TAG
65
+ type: tag-input
66
+ value: (array/map (array/filter ?data (fn t (array/includes @entity.docTagIds (object/get @t id)))) (fn t (object/get @t name)))
67
+ variant: secondary
68
+ }, (if ["=", (array/len (array/filter ?data (fn t (if (array/includes @entity.docTagIds (object/get @t id)) false true)))), 0] null {
69
+ children: (array/map (array/filter ?data (fn t (if (array/includes @entity.docTagIds (object/get @t id)) false true))) (fn t {
70
+ action: ADD_TAG
71
+ actionPayload: { tag: @t.name }
72
+ icon: tag
73
+ label: @t.name
74
+ size: sm
75
+ type: button
76
+ variant: ghost
77
+ }))
78
+ className: flex-wrap
79
+ direction: horizontal
80
+ gap: xs
81
+ type: stack
82
+ })]
83
+ direction: vertical
84
+ gap: xs
85
+ type: stack
86
+ })
87
+ TagStripLoadFailed -> idle
88
+ (render-ui main {
89
+ message: ?error
90
+ type: alert
91
+ variant: error
92
+ })
93
+ }
94
+
95
+ state ready {
96
+ SHOW_TAG_STRIP -> loading
97
+ (set @entity.docId ?id)
98
+ (set @entity.docTagIds (array/map ?tagIds (fn t (str/default (object/get @t id) @t))))
99
+ (fetch StripTag {
100
+ emit: { failure: TagStripLoadFailed, success: TagStripOptions }
101
+ })
102
+ ADD_TAG -> adding
103
+ (set @entity.pendingTagName ?tag)
104
+ (fetch StripTag {
105
+ emit: { failure: TagStripLoadFailed, success: TagStripAddOptions }
106
+ })
107
+ (render-ui main {
108
+ align: center
109
+ children: [{ size: sm, type: spinner }]
110
+ direction: horizontal
111
+ gap: xs
112
+ type: stack
113
+ })
114
+ REMOVE_TAG -> removing
115
+ (set @entity.pendingNames ?value)
116
+ (fetch StripTag {
117
+ emit: { failure: TagStripLoadFailed, success: TagStripRemoveOptions }
118
+ })
119
+ (render-ui main {
120
+ align: center
121
+ children: [{ size: sm, type: spinner }]
122
+ direction: horizontal
123
+ gap: xs
124
+ type: stack
125
+ })
126
+ TagStripTagCreated -> ready
127
+ }
128
+
129
+ state adding {
130
+ SHOW_TAG_STRIP -> loading
131
+ (set @entity.docId ?id)
132
+ (set @entity.docTagIds (array/map ?tagIds (fn t (str/default (object/get @t id) @t))))
133
+ (fetch StripTag {
134
+ emit: { failure: TagStripLoadFailed, success: TagStripOptions }
135
+ })
136
+ ;; The typed name matches an existing tag — attach its id.
137
+ TagStripAddOptions -> ready when (array/includes (array/map ?data (fn t (object/get @t name))) @entity.pendingTagName)
138
+ (emit TAGS_CHANGED {
139
+ id: @entity.docId
140
+ tagIds: (array/append @entity.docTagIds (object/get (array/first (array/filter ?data (fn t ["=", (object/get @t name), @entity.pendingTagName]))) id))
141
+ })
142
+ ;; Unknown name — create it in the shared taxonomy, then attach.
143
+ TagStripAddOptions -> creating
144
+ (persist create StripTag { name: @entity.pendingTagName } {
145
+ emit: { failure: TagStripLoadFailed, success: TagStripTagCreated }
146
+ })
147
+ TagStripLoadFailed -> idle
148
+ (render-ui main {
149
+ message: ?error
150
+ type: alert
151
+ variant: error
152
+ })
153
+ }
154
+
155
+ state creating {
156
+ SHOW_TAG_STRIP -> loading
157
+ (set @entity.docId ?id)
158
+ (set @entity.docTagIds (array/map ?tagIds (fn t (str/default (object/get @t id) @t))))
159
+ (fetch StripTag {
160
+ emit: { failure: TagStripLoadFailed, success: TagStripOptions }
161
+ })
162
+ TagStripTagCreated -> ready when ["!=", (str/default ?id ""), ""]
163
+ (emit TAGS_CHANGED {
164
+ id: @entity.docId
165
+ tagIds: (array/append @entity.docTagIds ?id)
166
+ })
167
+ TagStripLoadFailed -> idle
168
+ (render-ui main {
169
+ message: ?error
170
+ type: alert
171
+ variant: error
172
+ })
173
+ }
174
+
175
+ state removing {
176
+ SHOW_TAG_STRIP -> loading
177
+ (set @entity.docId ?id)
178
+ (set @entity.docTagIds (array/map ?tagIds (fn t (str/default (object/get @t id) @t))))
179
+ (fetch StripTag {
180
+ emit: { failure: TagStripLoadFailed, success: TagStripOptions }
181
+ })
182
+ ;; The remaining chip names map back to ids — every kept name exists
183
+ ;; by construction (chips only render attached, existing tags).
184
+ TagStripRemoveOptions -> ready
185
+ (emit TAGS_CHANGED {
186
+ id: @entity.docId
187
+ tagIds: (array/map (array/filter ?data (fn t (array/includes @entity.pendingNames (object/get @t name)))) (fn t (object/get @t id)))
188
+ })
189
+ TagStripLoadFailed -> idle
190
+ (render-ui main {
191
+ message: ?error
192
+ type: alert
193
+ variant: error
194
+ })
195
+ }
196
+
197
+ emits {
198
+ TAGS_CHANGED -> external {
199
+ id : string
200
+ tagIds : [string]
201
+ }
202
+ @description "The record's tag set changed (chip added or removed); the host routes this into its persistor's update so the write and the refetch stay the organism's."
203
+ @synonyms "tags updated, tag set changed, retagged"
204
+ @tier "domain"
205
+ ADD_TAG {
206
+ tag : string!
207
+ }
208
+ @description "A tag name was typed into the strip or an existing tag's suggestion chip was clicked."
209
+ @synonyms "attach tag, add label"
210
+ @tier "presentation"
211
+ REMOVE_TAG {
212
+ tag : string
213
+ value : [string]
214
+ }
215
+ @description "A chip's remove affordance was clicked; value carries the names that remain."
216
+ @synonyms "detach tag, remove label"
217
+ @tier "presentation"
218
+ TagStripOptions {
219
+ data : [StripTag]
220
+ }
221
+ @description "The shared tag store loaded for the display pass."
222
+ @tier "internal"
223
+ TagStripAddOptions {
224
+ data : [StripTag]
225
+ }
226
+ @description "The shared tag store loaded to resolve a typed name on add."
227
+ @tier "internal"
228
+ TagStripRemoveOptions {
229
+ data : [StripTag]
230
+ }
231
+ @description "The shared tag store loaded to resolve the remaining names on remove."
232
+ @tier "internal"
233
+ TagStripTagCreated {
234
+ id : string
235
+ row : StripTag
236
+ }
237
+ @description "A typed name had no existing tag; a fresh row was created in the shared taxonomy."
238
+ @tier "internal"
239
+ TagStripLoadFailed {
240
+ error : string
241
+ code : string
242
+ }
243
+ @description "The tag store fetch or create failed."
244
+ @tier "internal"
245
+ }
246
+
247
+ listens {
248
+ SHOW_TAG_STRIP {
249
+ id : string!
250
+ tagIds : [string]
251
+ }
252
+ @description "Scope the strip to the open record — routed from the document's load events; carries the record's id and current tag ids (hydrated tag rows are normalized to their ids at stash time, so include-hydrated fetches route straight through)."
253
+ @synonyms "load tags, scope tag strip, show tags"
254
+ @tier "domain"
255
+ ADD_TAG {
256
+ tag : string!
257
+ }
258
+ @description "Attach a tag by name — typed into the input or clicked from the existing-tag chips."
259
+ @synonyms "attach tag, add label"
260
+ @tier "presentation"
261
+ REMOVE_TAG {
262
+ tag : string
263
+ value : [string]
264
+ }
265
+ @description "Detach a chip; value carries the names that remain."
266
+ @synonyms "detach tag, remove label"
267
+ @tier "presentation"
268
+ TagStripOptions {
269
+ data : [StripTag]
270
+ }
271
+ @description "Display-pass fetch resolved."
272
+ @tier "internal"
273
+ TagStripAddOptions {
274
+ data : [StripTag]
275
+ }
276
+ @description "Add-resolve fetch resolved."
277
+ @tier "internal"
278
+ TagStripRemoveOptions {
279
+ data : [StripTag]
280
+ }
281
+ @description "Remove-resolve fetch resolved."
282
+ @tier "internal"
283
+ TagStripTagCreated {
284
+ id : string
285
+ row : StripTag
286
+ }
287
+ @description "The taxonomy row for a typed name persisted; attach its id."
288
+ @tier "internal"
289
+ TagStripLoadFailed {
290
+ error : string
291
+ code : string
292
+ }
293
+ @description "The tag store fetch or create failed."
294
+ @tier "internal"
295
+ }
296
+
297
+ config {
298
+ title : string = Tags
299
+ @label "Strip caption"
300
+ @description "Small caption above the chips; empty hides it"
301
+ @tier "presentation"
302
+ placeholder : string = "Add a tag…"
303
+ @label "Input placeholder"
304
+ @description "Placeholder in the type-to-add input"
305
+ @tier "presentation"
306
+ helperText : string = ""
307
+ @label "Helper text"
308
+ @description "Optional helper line under the input"
309
+ @tier "presentation"
310
+ }
311
+ }
312
+
313
+ page "/tagstrip/demo" as TagStripDemoPage -> TagStrip
314
+ }
@@ -7,6 +7,8 @@ orbital TagTaxonomyOrbital {
7
7
 
8
8
  type TagSelected = Event { tagId : string! } "Fired when a tag is selected to drill into its children"
9
9
 
10
+ type TagCommit = { name : string!, parentId : string }
11
+
10
12
  entity Tag [persistent: tags] {
11
13
  id : string!
12
14
  name : string! @description "The descriptive label for this item." @synonyms "label, title, description"
@@ -21,6 +23,11 @@ orbital TagTaxonomyOrbital {
21
23
  initial: loading
22
24
 
23
25
  state loading {
26
+ LEVEL_RELOAD -> loading
27
+ (fetch Tag {
28
+ filter: ["=", (object/get @entity parentId), (str/default ?tagId "")]
29
+ emit: { failure: TagLoadFailed, success: TagLoaded }
30
+ })
24
31
  INIT -> loading
25
32
  (set @entity.id "")
26
33
  (set @entity.parentId "")
@@ -32,7 +39,7 @@ orbital TagTaxonomyOrbital {
32
39
  align: center
33
40
  children: [{ type: spinner }, {
34
41
  color: muted
35
- content: "Loading categories…"
42
+ content: "Loading…"
36
43
  type: typography
37
44
  variant: caption
38
45
  }]
@@ -47,7 +54,7 @@ orbital TagTaxonomyOrbital {
47
54
  action: SHOW_ROOT
48
55
  disabled: (if (!= @entity.id "") false true)
49
56
  icon: arrow-left
50
- label: "All categories"
57
+ label: @config.rootLabel
51
58
  size: sm
52
59
  type: button
53
60
  variant: ghost
@@ -55,7 +62,14 @@ orbital TagTaxonomyOrbital {
55
62
  content: @config.title
56
63
  type: typography
57
64
  variant: h3
58
- }, {
65
+ }, (if @config.allowEdit {
66
+ action: NEW_TAG
67
+ icon: plus
68
+ label: @config.createLabel
69
+ size: sm
70
+ type: button
71
+ variant: secondary
72
+ } null), {
59
73
  entity: ?data
60
74
  fields: []
61
75
  gap: sm
@@ -136,7 +150,7 @@ orbital TagTaxonomyOrbital {
136
150
  align: center
137
151
  children: [{ type: spinner }, {
138
152
  color: muted
139
- content: "Loading categories…"
153
+ content: "Loading…"
140
154
  type: typography
141
155
  variant: caption
142
156
  }]
@@ -157,7 +171,7 @@ orbital TagTaxonomyOrbital {
157
171
  align: center
158
172
  children: [{ type: spinner }, {
159
173
  color: muted
160
- content: "Loading children…"
174
+ content: "Loading…"
161
175
  type: typography
162
176
  variant: caption
163
177
  }]
@@ -177,7 +191,7 @@ orbital TagTaxonomyOrbital {
177
191
  align: center
178
192
  children: [{ type: spinner }, {
179
193
  color: muted
180
- content: "Loading categories…"
194
+ content: "Loading…"
181
195
  type: typography
182
196
  variant: caption
183
197
  }]
@@ -192,7 +206,7 @@ orbital TagTaxonomyOrbital {
192
206
  action: SHOW_ROOT
193
207
  disabled: (if (!= @entity.id "") false true)
194
208
  icon: arrow-left
195
- label: "All categories"
209
+ label: @config.rootLabel
196
210
  size: sm
197
211
  type: button
198
212
  variant: ghost
@@ -200,7 +214,14 @@ orbital TagTaxonomyOrbital {
200
214
  content: @config.title
201
215
  type: typography
202
216
  variant: h3
203
- }, {
217
+ }, (if @config.allowEdit {
218
+ action: NEW_TAG
219
+ icon: plus
220
+ label: @config.createLabel
221
+ size: sm
222
+ type: button
223
+ variant: secondary
224
+ } null), {
204
225
  entity: ?data
205
226
  fields: []
206
227
  gap: sm
@@ -261,6 +282,29 @@ orbital TagTaxonomyOrbital {
261
282
  gap: md
262
283
  type: stack
263
284
  })
285
+ NEW_TAG -> creating when @config.allowEdit
286
+ (render-ui modal {
287
+ children: [{
288
+ content: @config.createLabel
289
+ type: typography
290
+ variant: h3
291
+ }, {
292
+ color: muted
293
+ content: (if (!= @entity.id "") "Created inside the open category." "Created at the top level.")
294
+ type: typography
295
+ variant: caption
296
+ }, { type: divider }, {
297
+ cancelEvent: CANCEL_TAG
298
+ entity: { parentId: @entity.id }
299
+ fields: (name)
300
+ mode: create
301
+ submitEvent: COMMIT_TAG
302
+ type: form-section
303
+ }]
304
+ direction: vertical
305
+ gap: md
306
+ type: stack
307
+ })
264
308
  TagLoadFailed -> error
265
309
  (render-ui main {
266
310
  message: ?error
@@ -269,6 +313,61 @@ orbital TagTaxonomyOrbital {
269
313
  })
270
314
  }
271
315
 
316
+ state creating {
317
+ COMMIT_TAG -> creating
318
+ (persist create Tag {
319
+ name: (object/get ?data name)
320
+ parentId: (str/default (object/get ?data parentId) "")
321
+ } {
322
+ emit: { failure: TagCreateFailed, success: TagCreated }
323
+ })
324
+ ;; A filter expression binds @entity to the candidate ROW, so the open
325
+ ;; level must travel by payload: the arm emits LEVEL_RELOAD carrying the
326
+ ;; trait's own level id, and loading refetches from it.
327
+ CANCEL_TAG -> loading
328
+ (render-ui modal null)
329
+ (render-ui main {
330
+ align: center
331
+ children: [{ type: spinner }, {
332
+ color: muted
333
+ content: "Loading…"
334
+ type: typography
335
+ variant: caption
336
+ }]
337
+ className: py-12
338
+ direction: vertical
339
+ gap: md
340
+ type: stack
341
+ })
342
+ (emit LEVEL_RELOAD { tagId: @entity.id })
343
+ ;; Refetch the level the new tag landed on — its own parentId names it.
344
+ TagCreated -> loading
345
+ (render-ui modal null)
346
+ (render-ui main {
347
+ align: center
348
+ children: [{ type: spinner }, {
349
+ color: muted
350
+ content: "Loading…"
351
+ type: typography
352
+ variant: caption
353
+ }]
354
+ className: py-12
355
+ direction: vertical
356
+ gap: md
357
+ type: stack
358
+ })
359
+ (fetch Tag {
360
+ filter: ["=", (object/get @entity parentId), (str/default (object/get ?row parentId) "")]
361
+ emit: { failure: TagLoadFailed, success: TagLoaded }
362
+ })
363
+ TagCreateFailed -> creating
364
+ (render-ui modal {
365
+ message: ?error
366
+ type: alert
367
+ variant: error
368
+ })
369
+ }
370
+
272
371
  state editing {
273
372
  INIT -> loading
274
373
  (set @entity.id "")
@@ -281,7 +380,7 @@ orbital TagTaxonomyOrbital {
281
380
  align: center
282
381
  children: [{ type: spinner }, {
283
382
  color: muted
284
- content: "Loading categories…"
383
+ content: "Loading…"
285
384
  type: typography
286
385
  variant: caption
287
386
  }]
@@ -303,7 +402,7 @@ orbital TagTaxonomyOrbital {
303
402
  action: SHOW_ROOT
304
403
  disabled: (if (!= @entity.id "") false true)
305
404
  icon: arrow-left
306
- label: "All categories"
405
+ label: @config.rootLabel
307
406
  size: sm
308
407
  type: button
309
408
  variant: ghost
@@ -311,7 +410,14 @@ orbital TagTaxonomyOrbital {
311
410
  content: @config.title
312
411
  type: typography
313
412
  variant: h3
314
- }, {
413
+ }, (if @config.allowEdit {
414
+ action: NEW_TAG
415
+ icon: plus
416
+ label: @config.createLabel
417
+ size: sm
418
+ type: button
419
+ variant: secondary
420
+ } null), {
315
421
  entity: ?data
316
422
  fields: []
317
423
  gap: sm
@@ -392,7 +498,7 @@ orbital TagTaxonomyOrbital {
392
498
  align: center
393
499
  children: [{ type: spinner }, {
394
500
  color: muted
395
- content: "Loading categories…"
501
+ content: "Loading…"
396
502
  type: typography
397
503
  variant: caption
398
504
  }]
@@ -429,6 +535,39 @@ orbital TagTaxonomyOrbital {
429
535
  @description "Signals a tag has been chosen for further exploration."
430
536
  @synonyms "chosen, picked, activated, highlighted"
431
537
  @tier "domain"
538
+ NEW_TAG
539
+ @description "Opens the create-tag form (rendered only when allowEdit is on); the creation lands at the level currently open."
540
+ @synonyms "add tag, create category, new category"
541
+ @tier "domain"
542
+ COMMIT_TAG {
543
+ data : TagCommit
544
+ }
545
+ @description "The new-category form committed; data carries the name and the parent level."
546
+ @synonyms "save tag, create tag"
547
+ @tier "internal"
548
+ CANCEL_TAG
549
+ @description "The new-category form was dismissed without creating."
550
+ @synonyms "cancel, back"
551
+ @tier "internal"
552
+ LEVEL_RELOAD {
553
+ tagId : string
554
+ }
555
+ @description "Internal hop that refetches the open level after the new-category form closes (a fetch filter cannot read trait state, so the level travels by payload)."
556
+ @synonyms "reload level, refresh"
557
+ @tier "internal"
558
+ TagCreated {
559
+ row : Tag
560
+ }
561
+ @description "A new tag row persisted; the open level refetches to show it."
562
+ @synonyms "created, added"
563
+ @tier "internal"
564
+ TagCreateFailed {
565
+ error : string
566
+ code : string
567
+ }
568
+ @description "The tag create failed."
569
+ @synonyms "error, failure"
570
+ @tier "internal"
432
571
  }
433
572
 
434
573
  listens {
@@ -457,6 +596,39 @@ orbital TagTaxonomyOrbital {
457
596
  @description "A tag in the taxonomy has been chosen by the user."
458
597
  @synonyms "chosen, picked, activated, selected"
459
598
  @tier "domain"
599
+ NEW_TAG
600
+ @description "The New-category affordance was clicked."
601
+ @synonyms "add tag, create category"
602
+ @tier "domain"
603
+ COMMIT_TAG {
604
+ data : TagCommit
605
+ }
606
+ @description "The new-category form committed."
607
+ @synonyms "save tag, create tag"
608
+ @tier "internal"
609
+ CANCEL_TAG
610
+ @description "The new-category form was dismissed."
611
+ @synonyms "cancel, back"
612
+ @tier "internal"
613
+ LEVEL_RELOAD {
614
+ tagId : string
615
+ }
616
+ @description "Refetch the open level."
617
+ @synonyms "reload level, refresh"
618
+ @tier "internal"
619
+ TagCreated {
620
+ row : Tag
621
+ }
622
+ @description "A new tag row persisted."
623
+ @synonyms "created, added"
624
+ @tier "internal"
625
+ TagCreateFailed {
626
+ error : string
627
+ code : string
628
+ }
629
+ @description "The tag create failed."
630
+ @synonyms "error, failure"
631
+ @tier "internal"
460
632
  }
461
633
 
462
634
  config {
@@ -466,8 +638,18 @@ orbital TagTaxonomyOrbital {
466
638
  @tier "presentation"
467
639
  allowEdit : boolean = false
468
640
  @label "Allow editing"
469
- @description "Show inline controls to create or rename tags"
641
+ @description "Render the create-tag affordance: creations land at the level currently open (top level, or inside the drilled-into category). Rename is a follow-up (ledgered)."
470
642
  @tier "domain"
643
+ createLabel : string = "New tag"
644
+ @label "Create-button label"
645
+ @description "Label on the create affordance and its form heading — say what this taxonomy calls its entries (New tag, New category, New topic)."
646
+ @synonyms "add label, new tag label, create caption"
647
+ @tier "presentation"
648
+ rootLabel : string = "All tags"
649
+ @label "Back-to-root label"
650
+ @description "Label on the back affordance that returns from a drilled-in level to the root — phrase it in this taxonomy's vocabulary (All tags, All categories)."
651
+ @synonyms "back label, root caption, all label"
652
+ @tier "presentation"
471
653
  tableLook : "dense" | "spacious" | "striped" | "borderless" | "card-rows" = dense
472
654
  @label "Table look"
473
655
  @description "Layer 2 visual treatment for the data table rendered by this atom."