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