@almadar/std 16.183.0 → 16.185.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.
- package/behaviors/lolo/ui/core/.hand-authored.json +4 -0
- package/behaviors/lolo/ui/core/atoms/std-browse.lolo +11 -0
- package/behaviors/lolo/ui/core/atoms/std-related.lolo +86 -9
- package/behaviors/lolo/ui/core/atoms/std-rich-editor.lolo +368 -1
- package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +62 -2
- package/behaviors/lolo/ui/core/atoms/std-thread.lolo +43 -2
- package/behaviors/lolo/ui/core/atoms/std-tree.lolo +247 -0
- package/behaviors/lolo/ui/core/atoms/std-version-history.lolo +110 -2
- package/behaviors/lolo/ui/core/molecules/ui-file-tree.lolo +18 -2
- package/behaviors/lolo/ui/core/organisms/ui-detail-panel.lolo +12 -2
- package/behaviors/lolo/ui/core/organisms/ui-form-section.lolo +2 -2
- package/behaviors/lolo/ui/core/organisms/ui-form.lolo +2 -2
- package/behaviors/lolo/ui/game/atoms/std-tutorial-autodemo.lolo +136 -0
- package/behaviors/lolo/ui/game/atoms/std-tutorial-overlay.lolo +41 -8
- package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +3 -3
- package/behaviors/registry/ui/core/atoms/std-browse.orb +17 -0
- package/behaviors/registry/ui/core/atoms/std-related.orb +256 -3
- package/behaviors/registry/ui/core/atoms/std-rich-editor.orb +898 -1
- package/behaviors/registry/ui/core/atoms/std-tag-taxonomy.orb +240 -8
- package/behaviors/registry/ui/core/atoms/std-thread.orb +254 -4
- package/behaviors/registry/ui/core/atoms/std-tree.orb +656 -0
- package/behaviors/registry/ui/core/atoms/std-version-history.orb +479 -6
- package/behaviors/registry/ui/core/molecules/ui-file-tree.orb +43 -19
- package/behaviors/registry/ui/core/organisms/ui-detail-panel.orb +37 -3
- package/behaviors/registry/ui/core/organisms/ui-form-section.orb +8 -2
- package/behaviors/registry/ui/core/organisms/ui-form.orb +8 -2
- package/behaviors/registry/ui/game/atoms/std-tutorial-autodemo.orb +732 -0
- package/behaviors/registry/ui/game/atoms/std-tutorial-overlay.orb +305 -153
- package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +26 -0
- package/dist/behaviors/exports-reader.js +5132 -2398
- package/dist/behaviors/functions/index.d.ts +261 -44
- package/dist/behaviors/functions/index.js +5118 -2396
- package/dist/behaviors/index.d.ts +1 -1
- package/dist/behaviors/index.js +5133 -2399
- package/dist/behaviors/query.js +5132 -2398
- package/dist/behaviors-embeddings.hash.json +3 -3
- package/dist/behaviors-embeddings.json +310 -302
- package/dist/behaviors-registry.json +820 -35
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5133 -2399
- package/dist/knob-embeddings.hash.json +3 -3
- package/dist/knob-embeddings.json +1 -1
- package/dist/registry/factory-signatures.json +1 -1
- package/package.json +2 -2
|
@@ -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.
|
|
1
|
+
app std-related "1.1.0"
|
|
2
2
|
"std-related as a Function"
|
|
3
3
|
;;
|
|
4
|
-
;; Relation-rendering atom.
|
|
5
|
-
;;
|
|
6
|
-
;;
|
|
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 ].
|
|
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:
|
|
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:
|
|
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 IN-PLACE body editor: an organism embeds it where the content belongs, rebinds it to its own entity (WikiPage, Note, …), hands it the row's id + blocksJson via OPEN, and routes the emitted BLOCKS_SAVED into its own persistor — this atom never fetches or persists. The read-only body is itself the edit affordance; an empty row paints an empty state that starts the first block; and OPEN_DRAFT opens straight into the authoring editor for a row that was just minted blank (document-first creation)."
|
|
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,372 @@ orbital DocumentOrbital {
|
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
+
;; In-place body editor for one row. The composing organism embeds this
|
|
423
|
+
;; trait where the content belongs (`<trait.X />` inside its detail
|
|
424
|
+
;; render) and feeds it the loaded row through OPEN; the body paints
|
|
425
|
+
;; read-only AND is its own edit affordance — clicking it enters editing
|
|
426
|
+
;; with the authoring toolbar, and a row with no blocks yet paints an
|
|
427
|
+
;; empty state whose button starts the first one. OPEN_DRAFT is the
|
|
428
|
+
;; document-first entry: same payload, but it lands in the editor. SAVE
|
|
429
|
+
;; emits BLOCKS_SAVED for the host's persistor; this atom never fetches or
|
|
430
|
+
;; persists.
|
|
431
|
+
trait BlockEdit -> @rebindable Document [interaction, instance] {
|
|
432
|
+
initial: idle
|
|
433
|
+
|
|
434
|
+
state idle {
|
|
435
|
+
INIT -> idle
|
|
436
|
+
OPEN -> viewing when [">", (array/len ?blocksJson), 0]
|
|
437
|
+
(set @entity.id ?id)
|
|
438
|
+
(set @entity.blocksJson ?blocksJson)
|
|
439
|
+
(render-ui main {
|
|
440
|
+
children: [{
|
|
441
|
+
children: [{
|
|
442
|
+
action: EDIT_BLOCKS
|
|
443
|
+
icon: edit
|
|
444
|
+
label: @config.editLabel
|
|
445
|
+
type: button
|
|
446
|
+
variant: secondary
|
|
447
|
+
}]
|
|
448
|
+
direction: horizontal
|
|
449
|
+
gap: sm
|
|
450
|
+
justify: end
|
|
451
|
+
type: stack
|
|
452
|
+
}, {
|
|
453
|
+
action: EDIT_BLOCKS
|
|
454
|
+
children: [{
|
|
455
|
+
initialBlocks: @entity.blocksJson
|
|
456
|
+
readOnly: true
|
|
457
|
+
type: rich-block-editor
|
|
458
|
+
}]
|
|
459
|
+
className: "cursor-text rounded-md transition-colors hover:bg-muted/40"
|
|
460
|
+
type: box
|
|
461
|
+
}]
|
|
462
|
+
direction: vertical
|
|
463
|
+
gap: sm
|
|
464
|
+
type: stack
|
|
465
|
+
})
|
|
466
|
+
OPEN -> viewing when ["=", (array/len ?blocksJson), 0]
|
|
467
|
+
(set @entity.id ?id)
|
|
468
|
+
(set @entity.blocksJson ?blocksJson)
|
|
469
|
+
(render-ui main {
|
|
470
|
+
actionEvent: EDIT_BLOCKS
|
|
471
|
+
actionLabel: @config.emptyActionLabel
|
|
472
|
+
description: @config.emptyDescription
|
|
473
|
+
icon: file-text
|
|
474
|
+
title: @config.emptyTitle
|
|
475
|
+
type: empty-state
|
|
476
|
+
})
|
|
477
|
+
;; Document-first entry: the row was just minted as a blank draft, so
|
|
478
|
+
;; the body opens ALREADY authoring instead of painting an empty state
|
|
479
|
+
;; the author has to click through. A distinct trigger, not a flag on
|
|
480
|
+
;; OPEN — the guard surface stays decidable.
|
|
481
|
+
OPEN_DRAFT -> editing
|
|
482
|
+
(set @entity.id ?id)
|
|
483
|
+
(set @entity.blocksJson ?blocksJson)
|
|
484
|
+
(render-ui main {
|
|
485
|
+
children: [{
|
|
486
|
+
align: center
|
|
487
|
+
children: [{ name: file-text, type: icon }, {
|
|
488
|
+
content: @config.title
|
|
489
|
+
type: typography
|
|
490
|
+
variant: h4
|
|
491
|
+
}]
|
|
492
|
+
direction: horizontal
|
|
493
|
+
gap: sm
|
|
494
|
+
type: stack
|
|
495
|
+
}, {
|
|
496
|
+
changeEvent: BLOCKS_CHANGE
|
|
497
|
+
initialBlocks: @entity.blocksJson
|
|
498
|
+
placeholder: @config.placeholder
|
|
499
|
+
showToolbar: true
|
|
500
|
+
type: rich-block-editor
|
|
501
|
+
}, {
|
|
502
|
+
children: [{
|
|
503
|
+
action: SAVE_BLOCKS
|
|
504
|
+
label: Save
|
|
505
|
+
type: button
|
|
506
|
+
variant: primary
|
|
507
|
+
}, {
|
|
508
|
+
action: CLOSE
|
|
509
|
+
label: Cancel
|
|
510
|
+
type: button
|
|
511
|
+
variant: ghost
|
|
512
|
+
}]
|
|
513
|
+
direction: horizontal
|
|
514
|
+
gap: sm
|
|
515
|
+
justify: end
|
|
516
|
+
type: stack
|
|
517
|
+
}]
|
|
518
|
+
direction: vertical
|
|
519
|
+
gap: md
|
|
520
|
+
type: stack
|
|
521
|
+
})
|
|
522
|
+
;; self-loop on the external emit so the validator sees an in-atom
|
|
523
|
+
;; listener — the real consumer is the composing organism's persistor.
|
|
524
|
+
BLOCKS_SAVED -> idle
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
state viewing {
|
|
528
|
+
OPEN_DRAFT -> editing
|
|
529
|
+
(set @entity.id ?id)
|
|
530
|
+
(set @entity.blocksJson ?blocksJson)
|
|
531
|
+
(render-ui main {
|
|
532
|
+
children: [{
|
|
533
|
+
align: center
|
|
534
|
+
children: [{ name: file-text, type: icon }, {
|
|
535
|
+
content: @config.title
|
|
536
|
+
type: typography
|
|
537
|
+
variant: h4
|
|
538
|
+
}]
|
|
539
|
+
direction: horizontal
|
|
540
|
+
gap: sm
|
|
541
|
+
type: stack
|
|
542
|
+
}, {
|
|
543
|
+
changeEvent: BLOCKS_CHANGE
|
|
544
|
+
initialBlocks: @entity.blocksJson
|
|
545
|
+
placeholder: @config.placeholder
|
|
546
|
+
showToolbar: true
|
|
547
|
+
type: rich-block-editor
|
|
548
|
+
}, {
|
|
549
|
+
children: [{
|
|
550
|
+
action: SAVE_BLOCKS
|
|
551
|
+
label: Save
|
|
552
|
+
type: button
|
|
553
|
+
variant: primary
|
|
554
|
+
}, {
|
|
555
|
+
action: CLOSE
|
|
556
|
+
label: Cancel
|
|
557
|
+
type: button
|
|
558
|
+
variant: ghost
|
|
559
|
+
}]
|
|
560
|
+
direction: horizontal
|
|
561
|
+
gap: sm
|
|
562
|
+
justify: end
|
|
563
|
+
type: stack
|
|
564
|
+
}]
|
|
565
|
+
direction: vertical
|
|
566
|
+
gap: md
|
|
567
|
+
type: stack
|
|
568
|
+
})
|
|
569
|
+
OPEN -> viewing when [">", (array/len ?blocksJson), 0]
|
|
570
|
+
(set @entity.id ?id)
|
|
571
|
+
(set @entity.blocksJson ?blocksJson)
|
|
572
|
+
(render-ui main {
|
|
573
|
+
children: [{
|
|
574
|
+
children: [{
|
|
575
|
+
action: EDIT_BLOCKS
|
|
576
|
+
icon: edit
|
|
577
|
+
label: @config.editLabel
|
|
578
|
+
type: button
|
|
579
|
+
variant: secondary
|
|
580
|
+
}]
|
|
581
|
+
direction: horizontal
|
|
582
|
+
gap: sm
|
|
583
|
+
justify: end
|
|
584
|
+
type: stack
|
|
585
|
+
}, {
|
|
586
|
+
action: EDIT_BLOCKS
|
|
587
|
+
children: [{
|
|
588
|
+
initialBlocks: @entity.blocksJson
|
|
589
|
+
readOnly: true
|
|
590
|
+
type: rich-block-editor
|
|
591
|
+
}]
|
|
592
|
+
className: "cursor-text rounded-md transition-colors hover:bg-muted/40"
|
|
593
|
+
type: box
|
|
594
|
+
}]
|
|
595
|
+
direction: vertical
|
|
596
|
+
gap: sm
|
|
597
|
+
type: stack
|
|
598
|
+
})
|
|
599
|
+
OPEN -> viewing when ["=", (array/len ?blocksJson), 0]
|
|
600
|
+
(set @entity.id ?id)
|
|
601
|
+
(set @entity.blocksJson ?blocksJson)
|
|
602
|
+
(render-ui main {
|
|
603
|
+
actionEvent: EDIT_BLOCKS
|
|
604
|
+
actionLabel: @config.emptyActionLabel
|
|
605
|
+
description: @config.emptyDescription
|
|
606
|
+
icon: file-text
|
|
607
|
+
title: @config.emptyTitle
|
|
608
|
+
type: empty-state
|
|
609
|
+
})
|
|
610
|
+
EDIT_BLOCKS -> editing
|
|
611
|
+
(render-ui main {
|
|
612
|
+
children: [{
|
|
613
|
+
align: center
|
|
614
|
+
children: [{ name: file-text, type: icon }, {
|
|
615
|
+
content: @config.title
|
|
616
|
+
type: typography
|
|
617
|
+
variant: h4
|
|
618
|
+
}]
|
|
619
|
+
direction: horizontal
|
|
620
|
+
gap: sm
|
|
621
|
+
type: stack
|
|
622
|
+
}, {
|
|
623
|
+
changeEvent: BLOCKS_CHANGE
|
|
624
|
+
initialBlocks: @entity.blocksJson
|
|
625
|
+
placeholder: @config.placeholder
|
|
626
|
+
showToolbar: true
|
|
627
|
+
type: rich-block-editor
|
|
628
|
+
}, {
|
|
629
|
+
children: [{
|
|
630
|
+
action: SAVE_BLOCKS
|
|
631
|
+
label: Save
|
|
632
|
+
type: button
|
|
633
|
+
variant: primary
|
|
634
|
+
}, {
|
|
635
|
+
action: CLOSE
|
|
636
|
+
label: Cancel
|
|
637
|
+
type: button
|
|
638
|
+
variant: ghost
|
|
639
|
+
}]
|
|
640
|
+
direction: horizontal
|
|
641
|
+
gap: sm
|
|
642
|
+
justify: end
|
|
643
|
+
type: stack
|
|
644
|
+
}]
|
|
645
|
+
direction: vertical
|
|
646
|
+
gap: md
|
|
647
|
+
type: stack
|
|
648
|
+
})
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
state editing {
|
|
652
|
+
BLOCKS_CHANGE -> editing
|
|
653
|
+
(set @entity.blocksJson ?blocks)
|
|
654
|
+
SAVE_BLOCKS -> viewing
|
|
655
|
+
(emit BLOCKS_SAVED { id: @entity.id, blocksJson: @entity.blocksJson })
|
|
656
|
+
(render-ui main {
|
|
657
|
+
children: [{
|
|
658
|
+
children: [{
|
|
659
|
+
action: EDIT_BLOCKS
|
|
660
|
+
icon: edit
|
|
661
|
+
label: @config.editLabel
|
|
662
|
+
type: button
|
|
663
|
+
variant: secondary
|
|
664
|
+
}]
|
|
665
|
+
direction: horizontal
|
|
666
|
+
gap: sm
|
|
667
|
+
justify: end
|
|
668
|
+
type: stack
|
|
669
|
+
}, {
|
|
670
|
+
action: EDIT_BLOCKS
|
|
671
|
+
children: [{
|
|
672
|
+
initialBlocks: @entity.blocksJson
|
|
673
|
+
readOnly: true
|
|
674
|
+
type: rich-block-editor
|
|
675
|
+
}]
|
|
676
|
+
className: "cursor-text rounded-md transition-colors hover:bg-muted/40"
|
|
677
|
+
type: box
|
|
678
|
+
}]
|
|
679
|
+
direction: vertical
|
|
680
|
+
gap: sm
|
|
681
|
+
type: stack
|
|
682
|
+
})
|
|
683
|
+
CLOSE -> viewing
|
|
684
|
+
(render-ui main {
|
|
685
|
+
children: [{
|
|
686
|
+
children: [{
|
|
687
|
+
action: EDIT_BLOCKS
|
|
688
|
+
icon: edit
|
|
689
|
+
label: @config.editLabel
|
|
690
|
+
type: button
|
|
691
|
+
variant: secondary
|
|
692
|
+
}]
|
|
693
|
+
direction: horizontal
|
|
694
|
+
gap: sm
|
|
695
|
+
justify: end
|
|
696
|
+
type: stack
|
|
697
|
+
}, {
|
|
698
|
+
action: EDIT_BLOCKS
|
|
699
|
+
children: [{
|
|
700
|
+
initialBlocks: @entity.blocksJson
|
|
701
|
+
readOnly: true
|
|
702
|
+
type: rich-block-editor
|
|
703
|
+
}]
|
|
704
|
+
className: "cursor-text rounded-md transition-colors hover:bg-muted/40"
|
|
705
|
+
type: box
|
|
706
|
+
}]
|
|
707
|
+
direction: vertical
|
|
708
|
+
gap: sm
|
|
709
|
+
type: stack
|
|
710
|
+
})
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
emits {
|
|
714
|
+
BLOCKS_SAVED -> external {
|
|
715
|
+
id : string!
|
|
716
|
+
blocksJson : [BlockSpec]
|
|
717
|
+
}
|
|
718
|
+
@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."
|
|
719
|
+
@synonyms "saved, persist, update, commit"
|
|
720
|
+
@tier "domain"
|
|
721
|
+
CLOSE
|
|
722
|
+
@description "Leaves the editor without saving; the body returns to its read-only render."
|
|
723
|
+
@synonyms "dismiss, hide, cancel, close"
|
|
724
|
+
@tier "domain"
|
|
725
|
+
BLOCKS_CHANGE {
|
|
726
|
+
blocks : [BlockSpec]
|
|
727
|
+
}
|
|
728
|
+
@description "Signals that the content of a block has been modified while editing."
|
|
729
|
+
@synonyms "update, modify, changed, edit"
|
|
730
|
+
@tier "domain"
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
listens {
|
|
734
|
+
OPEN {
|
|
735
|
+
id : string!
|
|
736
|
+
blocksJson : [BlockSpec]
|
|
737
|
+
}
|
|
738
|
+
@description "Hands the editor the row whose body it renders — the caller supplies the row's id and current blocks, since this trait never fetches them itself. Re-firing it repaints the body (e.g. after the host refetches a saved row)."
|
|
739
|
+
@synonyms "show, launch, reveal, edit"
|
|
740
|
+
@tier "domain"
|
|
741
|
+
OPEN_DRAFT {
|
|
742
|
+
id : string!
|
|
743
|
+
blocksJson : [BlockSpec]
|
|
744
|
+
}
|
|
745
|
+
@description "Hands the editor a row that was just created as a blank draft: same payload as OPEN, but the body opens straight in the authoring editor instead of the read-only render, so a document-first 'New page' lands the author on a cursor rather than an empty state."
|
|
746
|
+
@synonyms "new document, start writing, blank draft, compose"
|
|
747
|
+
@tier "domain"
|
|
748
|
+
BLOCKS_CHANGE {
|
|
749
|
+
blocks : [BlockSpec]
|
|
750
|
+
}
|
|
751
|
+
@description "Indicates changes to the content of editable blocks while editing."
|
|
752
|
+
@synonyms "update, modify, edit"
|
|
753
|
+
@tier "domain"
|
|
754
|
+
CLOSE
|
|
755
|
+
@description "Leaves the editor without saving; the body returns to its read-only render."
|
|
756
|
+
@synonyms "dismiss, hide, cancel, close"
|
|
757
|
+
@tier "domain"
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
config {
|
|
761
|
+
title : string = "Edit content"
|
|
762
|
+
@label "Editor heading"
|
|
763
|
+
@description "Heading shown above the editor while the body is being edited."
|
|
764
|
+
@tier "presentation"
|
|
765
|
+
placeholder : string = "Start writing…"
|
|
766
|
+
@label "Placeholder text"
|
|
767
|
+
@description "Placeholder shown in the block editor when it is empty."
|
|
768
|
+
@tier "presentation"
|
|
769
|
+
editLabel : string = "Edit content"
|
|
770
|
+
@label "Edit button label"
|
|
771
|
+
@description "Label of the button above the read-only body that opens the editor."
|
|
772
|
+
@tier "presentation"
|
|
773
|
+
emptyTitle : string = "Nothing written yet"
|
|
774
|
+
@label "Empty heading"
|
|
775
|
+
@description "Heading shown in place of the body when the row has no blocks yet."
|
|
776
|
+
@tier "presentation"
|
|
777
|
+
emptyDescription : string = "This page has no content yet. Start writing to fill it in."
|
|
778
|
+
@label "Empty description"
|
|
779
|
+
@description "Supporting line shown with the empty body state."
|
|
780
|
+
@tier "presentation"
|
|
781
|
+
emptyActionLabel : string = "Start writing"
|
|
782
|
+
@label "Empty action label"
|
|
783
|
+
@description "Label of the button that opens the editor from the empty body state."
|
|
784
|
+
@tier "presentation"
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
422
788
|
page "/documents/edit" as DocumentEditPage -> DocumentEdit
|
|
789
|
+
page "/documents/block-edit" as BlockEditPage -> BlockEdit
|
|
423
790
|
}
|