@cynodia/axiom-ui 0.7.0-alpha.1
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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/catalog.d.ts +32 -0
- package/dist/catalog.js +29 -0
- package/dist/example/app.d.ts +6 -0
- package/dist/example/app.js +365 -0
- package/dist/example/domain.d.ts +71 -0
- package/dist/example/domain.js +469 -0
- package/dist/example/index.d.ts +3 -0
- package/dist/example/index.js +2 -0
- package/dist/expand.d.ts +119 -0
- package/dist/expand.js +227 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +11 -0
- package/dist/inference.d.ts +55 -0
- package/dist/inference.js +113 -0
- package/dist/pattern.d.ts +192 -0
- package/dist/pattern.js +39 -0
- package/dist/patterns/action-bar.d.ts +24 -0
- package/dist/patterns/action-bar.js +94 -0
- package/dist/patterns/entity-form.d.ts +93 -0
- package/dist/patterns/entity-form.js +340 -0
- package/dist/patterns/entity-list.d.ts +45 -0
- package/dist/patterns/entity-list.js +236 -0
- package/dist/patterns/metric-grid.d.ts +31 -0
- package/dist/patterns/metric-grid.js +107 -0
- package/dist/patterns/page.d.ts +28 -0
- package/dist/patterns/page.js +104 -0
- package/dist/queries.d.ts +60 -0
- package/dist/queries.js +53 -0
- package/dist/toolkit.d.ts +8 -0
- package/dist/toolkit.js +19 -0
- package/docs/OWNERSHIP.md +75 -0
- package/docs/PATTERN_AUTHORING.md +71 -0
- package/docs/PATTERN_CATALOG.json +504 -0
- package/docs/PROVENANCE.md +55 -0
- package/docs/TOOLKIT_AGENT_REFERENCE.md +205 -0
- package/package.json +47 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Provenance
|
|
2
|
+
|
|
3
|
+
What a generated node remembers about where it came from.
|
|
4
|
+
|
|
5
|
+
## It is authoring metadata
|
|
6
|
+
|
|
7
|
+
Provenance lives under core's reserved authoring key, `AUTHORING_METADATA_KEY`, which means
|
|
8
|
+
**the compiler strips it from every artifact by default** — client IR, server IR, the generated
|
|
9
|
+
page. It exists in the graph, for tools; it does not exist in anything that ships.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
compileToIR(graph); // no provenance
|
|
13
|
+
compileToIR(graph, { includeAuthoringMetadata: true }); // provenance, for a tool that asked
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The mechanism is core's, not the toolkit's: anything under that key is authoring metadata,
|
|
17
|
+
whoever wrote it. Semantic metadata beside it is untouched.
|
|
18
|
+
|
|
19
|
+
## The record
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
{
|
|
23
|
+
toolkit: '@cynodia/axiom-ui',
|
|
24
|
+
pattern: 'entity-list',
|
|
25
|
+
patternVersion: '0.7.0',
|
|
26
|
+
instance: 'product_list',
|
|
27
|
+
part: 'row-action',
|
|
28
|
+
parent: 'products', // the nearest generating pattern
|
|
29
|
+
ancestry: ['products'], // outermost to nearest
|
|
30
|
+
ownership: 'declaration',
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Everything in it is a stable string. No source location, no line number, no AST pointer —
|
|
35
|
+
nothing that changes when a file is reformatted.
|
|
36
|
+
|
|
37
|
+
## What it is for
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
provenanceOf(node); // which pattern generated this node?
|
|
41
|
+
instancesOfPattern(graph, 'entity-list'); // which entity-lists exist?
|
|
42
|
+
nodesOfInstance(graph, 'product_list'); // which nodes belong to this one?
|
|
43
|
+
axiomUi.inspect(graph, 'product_list'); // declaration, nodes, explanations
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## What it is not for
|
|
47
|
+
|
|
48
|
+
Provenance records **origin, not ownership**, and it never affects execution. Removing it
|
|
49
|
+
changes node ids, semantic edges, validation, compiler output, presentation resolution and
|
|
50
|
+
runtime behaviour by exactly nothing — `expand(…, { model: 'macro' })` produces a
|
|
51
|
+
byte-identical graph.
|
|
52
|
+
|
|
53
|
+
**Nothing may branch on provenance at runtime.** A renderer, a constraint or an action that
|
|
54
|
+
behaved differently because a node was generated would make the toolkit part of the
|
|
55
|
+
application's semantics, which is the one thing this design exists to prevent.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Axiom UI Toolkit — Agent Reference
|
|
2
|
+
|
|
3
|
+
`@cynodia/axiom-ui` 0.7.0-alpha.1. Semantic UI patterns for Axiom.
|
|
4
|
+
|
|
5
|
+
Read this plus [`PATTERN_CATALOG.json`](PATTERN_CATALOG.json) and the `.d.ts` declarations.
|
|
6
|
+
Nothing else should be necessary.
|
|
7
|
+
|
|
8
|
+
## What a pattern is
|
|
9
|
+
|
|
10
|
+
A pattern is **not a component**. It has no runtime existence, renders nothing and owns no
|
|
11
|
+
state. It is a function from a declaration to ordinary Axiom UI nodes, run once at authoring
|
|
12
|
+
time.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
pattern declaration → expansion → canonical Axiom UI → presentation → renderer
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
After expansion the application is an ordinary Axiom application. `validateGraph`,
|
|
19
|
+
`compileToIR`, `AgentAPI` and the runtime see nodes, not patterns. **Do not look for a
|
|
20
|
+
pattern at runtime; there is none.**
|
|
21
|
+
|
|
22
|
+
## Using it
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { axiomUi } from '@cynodia/axiom-ui';
|
|
26
|
+
|
|
27
|
+
const rootId = axiomUi.expand(graph, {
|
|
28
|
+
pattern: 'entity-list',
|
|
29
|
+
instance: 'product_list', // stable, yours, and the root of every generated id
|
|
30
|
+
source: STATE_PRODUCTS,
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`expand` returns the id of the root node it created. Put that id in a `view`'s children, or in
|
|
35
|
+
another pattern's `content` slot.
|
|
36
|
+
|
|
37
|
+
A declaration is **plain data**: no functions, no callbacks, no JSX. Slots take node ids or
|
|
38
|
+
nested declarations, never markup.
|
|
39
|
+
|
|
40
|
+
## Discovering what exists
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { axiomUi, listPatterns, describePattern } from '@cynodia/axiom-ui';
|
|
44
|
+
|
|
45
|
+
listPatterns(axiomUi); // ['action-bar','entity-form','entity-list','metric-grid','page']
|
|
46
|
+
describePattern(axiomUi, 'entity-list'); // required, optional, inputs, slots, produces, inferred
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`describePattern(...).inferred` tells you what a pattern works out for itself, and from what.
|
|
50
|
+
**Do not restate anything listed there.** The same content is in `PATTERN_CATALOG.json` if you
|
|
51
|
+
prefer to read it without running code.
|
|
52
|
+
|
|
53
|
+
## What is inferred, and what is not
|
|
54
|
+
|
|
55
|
+
The rule: **the toolkit infers what the graph already says; you supply application-specific UX
|
|
56
|
+
choices.**
|
|
57
|
+
|
|
58
|
+
Inferred from the graph — do not repeat these:
|
|
59
|
+
|
|
60
|
+
| | from |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| the entity a collection holds | the state's `valueType` |
|
|
63
|
+
| a field's label | `FieldDef.name` |
|
|
64
|
+
| a field's control | `FieldDef.valueType` (`boolean` → checkbox, `number` → stepper, `enum` → select) |
|
|
65
|
+
| whether a control is required | `FieldDef.required` |
|
|
66
|
+
| field order | the entity's declaration order |
|
|
67
|
+
| number / boolean / date / datetime formats | `FieldDef.valueType` |
|
|
68
|
+
| destructive emphasis | `ActionDef.destructive` |
|
|
69
|
+
| the primary action of a form | its submit action |
|
|
70
|
+
| a metric's label | the `name` of the state its value reads |
|
|
71
|
+
| whether a form creates or edits | `draft` (create) or `target` (edit) — the two are different semantics, not a flag |
|
|
72
|
+
|
|
73
|
+
**Never inferred**, because nothing in the graph says it — supply these explicitly:
|
|
74
|
+
|
|
75
|
+
- **currency and percentage formats.** A `number` does not say which, and guessing from a field named `price` would be a heuristic you could not predict. Pass `formats`.
|
|
76
|
+
- **which fields matter in a list.** The default is every field but the identity; narrow it with `fields`.
|
|
77
|
+
- **where a form writes.** `draft: S` creates a record in a draft state; `target: { state, identity }` edits the member of a collection that `identity` selects. Exactly one is given, and the mode follows from which. A form that gave neither, or both, is refused at the declaration.
|
|
78
|
+
- **a choice drawn from application data.** A field whose value identifies another record takes an `options` source: `options: { [F_ORDER_PRODUCT]: { source: ref(STATE_PRODUCTS), scopeId, valueFieldId, labelFieldId } }`.
|
|
79
|
+
|
|
80
|
+
## Creating and editing
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// Create: controls write into a draft state, which is not checked per keystroke, because a
|
|
84
|
+
// half-filled new record is incomplete by definition.
|
|
85
|
+
{ pattern: 'entity-form', instance: 'new_product', draft: STATE_DRAFT, submit: ACTION_ADD }
|
|
86
|
+
|
|
87
|
+
// Edit: controls write into the addressed member of a collection. Every write is
|
|
88
|
+
// transactional against every hard invariant, and a value that breaks one is rolled back.
|
|
89
|
+
{
|
|
90
|
+
pattern: 'entity-form',
|
|
91
|
+
instance: 'edit_product',
|
|
92
|
+
target: { state: STATE_PRODUCTS, identity: ref(ROUTE_PARAM_CODE) },
|
|
93
|
+
submit: ACTION_SAVE,
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The identity field of the entity is what addresses the member; an entity without one cannot be
|
|
98
|
+
edited this way and says so (`NO_IDENTITY_FIELD`). An edit form omits the identity field from
|
|
99
|
+
its controls, because an identity is what addresses a record, not something to retype.
|
|
100
|
+
|
|
101
|
+
## User-visible text can be an expression
|
|
102
|
+
|
|
103
|
+
A pattern input carrying user-visible **value** text takes `string | Expression`:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
{ pattern: 'page', instance: 'detail', title: field(productInRoute(), F_PRODUCT_NAME) }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`page.title`, `page.description`, a metric's `label` and `description`, and a form's `title`,
|
|
110
|
+
`description` and `submitLabel` all accept either. A caption that is the same on every record
|
|
111
|
+
is a string; a title that names the record on screen is an expression.
|
|
112
|
+
|
|
113
|
+
## Composition
|
|
114
|
+
|
|
115
|
+
Patterns nest. `page` takes `content` and `actions`; `entity-list` takes `rowExtra`. A slot
|
|
116
|
+
accepts a node id or another declaration.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
axiomUi.expand(graph, {
|
|
120
|
+
pattern: 'page',
|
|
121
|
+
instance: 'products',
|
|
122
|
+
title: 'Products',
|
|
123
|
+
content: [
|
|
124
|
+
{ pattern: 'entity-list', instance: 'product_list', source: STATE_PRODUCTS },
|
|
125
|
+
{ pattern: 'entity-form', instance: 'new_product', draft: STATE_DRAFT, submit: ACTION_ADD },
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## When no pattern fits
|
|
131
|
+
|
|
132
|
+
**Compose. Do not wait for a pattern and do not reach for CSS.** Patterns emit ordinary Axiom
|
|
133
|
+
UI nodes, so ordinary nodes sit beside them. Build the part the toolkit does not know with
|
|
134
|
+
`container`, `text`, `conditional`, `field-display`, and put pattern expansions inside it.
|
|
135
|
+
|
|
136
|
+
The same applies to a requirement inside a pattern — a per-row badge, a warning marker. Put a
|
|
137
|
+
`conditional` node in the `rowExtra` slot. You do not need to reproduce the generated
|
|
138
|
+
structure, and there is no CSS or DOM escape hatch to reach for.
|
|
139
|
+
|
|
140
|
+
## Referring to the current row
|
|
141
|
+
|
|
142
|
+
Inside `entity-list`, a row's fields and a row action's arguments are expressions evaluated in
|
|
143
|
+
the row's scope. Build them with the helpers, which derive the repeat's id from the instance:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { rowField } from '@cynodia/axiom-ui';
|
|
147
|
+
|
|
148
|
+
rowActions: [ACTION_DELETE_PRODUCT],
|
|
149
|
+
rowArguments: { [ACTION_DELETE_PRODUCT]: { [PARAM_PRODUCT]: rowField('product_list', F_PRODUCT_ID) } },
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Diagnostics
|
|
153
|
+
|
|
154
|
+
A declaration is checked against the graph **before** any node is created, and a mistake points
|
|
155
|
+
at the declaration:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
[SOURCE_NOT_A_COLLECTION] product_list.source: Low stock is not a collection, so it has no rows to list.
|
|
159
|
+
[FIELD_NOT_ON_ENTITY] product_list.fields[1]: field_order_total is not a field of Product.
|
|
160
|
+
[MISSING_ACTION_ARGUMENT] bar.actions[0]: Delete product requires param_product; supply it under arguments.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
A refused expansion creates nothing. `validateGraph` still runs afterwards and is still final —
|
|
164
|
+
toolkit checks are an earlier, better-located diagnostic, never a replacement.
|
|
165
|
+
|
|
166
|
+
## Understanding what a pattern did
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
const expansion = axiomUi.inspect(graph, 'product_list');
|
|
170
|
+
expansion.declaration; // what you wrote
|
|
171
|
+
expansion.nodeIds; // what it generated
|
|
172
|
+
expansion.explanations; // why it chose what it chose
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`explanations` is prose written by the pattern — which field got which format and from what,
|
|
176
|
+
why an action landed in the row action group, why an empty state exists.
|
|
177
|
+
|
|
178
|
+
## Ownership
|
|
179
|
+
|
|
180
|
+
Generated nodes record who owns them. The default is `declaration`: **the declaration is the
|
|
181
|
+
source of truth, and editing a generated node is drift** that `detectDrift` reports and the
|
|
182
|
+
next build overwrites. To take ownership of the result instead, `materializePattern`. See
|
|
183
|
+
[`OWNERSHIP.md`](OWNERSHIP.md).
|
|
184
|
+
|
|
185
|
+
## When not to add a pattern
|
|
186
|
+
|
|
187
|
+
A new pattern is justified by **recurring semantic UX intent**, not by recurring visual
|
|
188
|
+
structure. Before adding one, require evidence that it recurs, that its inference is
|
|
189
|
+
meaningful, that expansion removes semantic restatement, that customization stays composable,
|
|
190
|
+
and that canonical primitives alone are unnecessarily repetitive. A missing composition is not
|
|
191
|
+
a missing pattern.
|
|
192
|
+
|
|
193
|
+
Two things are never patterns:
|
|
194
|
+
|
|
195
|
+
- **Interaction behaviour.** A pattern can only emit nodes that already exist, so focus movement, containment, `Escape`, typeahead and active descendant are unreachable from here. They are canonical semantics — `dialog` is one, and `combobox` is classified as the next.
|
|
196
|
+
- **Anything that generates behaviour.** No pattern creates state, an action, a constraint or an authority. A pattern that generated an action would be hiding the part of an application that decides what happens.
|
|
197
|
+
|
|
198
|
+
## Rules
|
|
199
|
+
|
|
200
|
+
- **MUST** give every declaration a stable, unique `instance`. Generated ids derive from it.
|
|
201
|
+
- **MUST NOT** put a function, closure or DOM node in a declaration.
|
|
202
|
+
- **MUST NOT** edit generated nodes under `declaration` ownership; use a slot, an option, or materialize.
|
|
203
|
+
- **MUST NOT** use `rendererOverrides` or any CSS mechanism to achieve a layout a pattern option or a semantic node can express.
|
|
204
|
+
- **SHOULD** compose patterns and canonical nodes when no pattern fits, rather than approximating with the wrong one.
|
|
205
|
+
- Patterns own UX structure — hierarchy, grouping, empty states, responsive intent. Themes own visual design. Neither owns behaviour: no pattern creates state, an action or a constraint.
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cynodia/axiom-ui",
|
|
3
|
+
"version": "0.7.0-alpha.1",
|
|
4
|
+
"description": "Semantic UI authoring for Axiom: patterns that expand, at build time, into ordinary Axiom UI nodes.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "AskTech AS",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./example": {
|
|
19
|
+
"types": "./dist/example/index.d.ts",
|
|
20
|
+
"import": "./dist/example/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./catalog": "./docs/PATTERN_CATALOG.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/**/*.js",
|
|
26
|
+
"dist/**/*.d.ts",
|
|
27
|
+
"docs/*.md",
|
|
28
|
+
"docs/PATTERN_CATALOG.json",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@cynodia/axiom-core": "0.7.0-alpha.1"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -b tsconfig.json tsconfig.test.json",
|
|
37
|
+
"test": "node --test dist-test/**/*.test.js"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/cynodia/axiom.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/cynodia/axiom",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/cynodia/axiom/issues"
|
|
46
|
+
}
|
|
47
|
+
}
|