@elaraai/east-claude-plugin 1.0.97 → 1.0.99
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/.build/daemon/lsp.js +40 -3
- package/.build/daemon/server.js +40 -3
- package/.claude-plugin/plugin.json +1 -1
- package/index.json +1829 -632
- package/package.json +1 -1
- package/skills/e3/SKILL.md +395 -90
- package/skills/e3-ui/SKILL.md +1248 -277
- package/skills/e3-ui-cli/SKILL.md +2 -2
- package/skills/east/SKILL.md +69 -2
- package/skills/east-contribute/SKILL.md +0 -1
- package/skills/east-py/SKILL.md +53 -11
- package/skills/east-ui/SKILL.md +20 -43
|
@@ -50,7 +50,7 @@ e3-ui https://e3.example.com # a bare origin → the repositories li
|
|
|
50
50
|
# Screenshots need the browser once per machine (add --with-deps on fresh Linux servers):
|
|
51
51
|
e3-ui install-browser
|
|
52
52
|
e3-ui shot --from-source ./dashboard.tsx -o dashboard.png
|
|
53
|
-
e3-ui shot --from-source test/collections/
|
|
53
|
+
e3-ui shot --from-source test/collections/table.examples.tsx -e tableBasic -o table.png
|
|
54
54
|
e3-ui shot --from-task main.dashboard --repo ./.repos -o dashboard.png
|
|
55
55
|
e3-ui shots src --out .shots --json # EVERYTHING renderable under src/
|
|
56
56
|
```
|
|
@@ -299,7 +299,7 @@ npm run shot # = e3-ui shot --from-source src/ui/index.tsx --export surfa
|
|
|
299
299
|
e3-ui shot --from-source test/display/combine.examples.tsx -e combineDensities -o combine.png
|
|
300
300
|
|
|
301
301
|
# Width-flexible component (Schematic/Plan/Table) — mount the frame at viewport width
|
|
302
|
-
e3-ui shot --from-source test/collections/
|
|
302
|
+
e3-ui shot --from-source test/collections/table.examples.tsx -e tableFrozen --frame-width full -o frozen.png
|
|
303
303
|
|
|
304
304
|
# Sweep a whole project (the --ui scaffold wires this as `npm run shots`):
|
|
305
305
|
e3-ui shots src --out .shots # PNGs, faithful width, typed skip reasons
|
package/skills/east/SKILL.md
CHANGED
|
@@ -120,7 +120,8 @@ Task → What do you need?
|
|
|
120
120
|
│ │ ├─ Convert → .stringJoin(), .toSet(), .toDict(), .flattenToSet(), .flattenToDict(), .encodeCsv()
|
|
121
121
|
│ │ ├─ Group → .groupReduce(), .groupSize(), .groupSum(), .groupMean(), .groupMinimum(), .groupMaximum(),
|
|
122
122
|
│ │ │ .groupToArrays(), .groupToSets(), .groupToDicts(), .groupEvery(), .groupSome()
|
|
123
|
-
│ │ └─ Tree → .toTree(Node, key, parent, build) — flat parent-keyed rows to nested nodes
|
|
123
|
+
│ │ └─ Tree → .toTree(Node, key, parent, build) — flat parent-keyed rows to nested nodes;
|
|
124
|
+
│ │ .flattenTree(children) — nested nodes back to { path, node } rows, depth first (below)
|
|
124
125
|
│ ├─ Set
|
|
125
126
|
│ │ ├─ Read and mutate → .size(), .has(), .insert(), .tryInsert(), .delete(), .tryDelete(), .clear(), .unionInPlace()
|
|
126
127
|
│ │ ├─ Set ops → .copy(), .union(), .intersection(), .difference(), .symmetricDifference(), .isSubsetOf(),
|
|
@@ -129,6 +130,7 @@ Task → What do you need?
|
|
|
129
130
|
│ │ reduce .reduce(), .scan(), .every(), .some(), .sum(), .mean();
|
|
130
131
|
│ │ convert .toArray(), .toSet(), .toDict(), .flattenToArray(), .flattenToSet(), .flattenToDict(),
|
|
131
132
|
│ │ .toTree(Node, parent, build) (an element / a key is its own key; Dict callbacks take (value, key));
|
|
133
|
+
│ │ Dict only: .flattenTree(children), the nodes back again (below);
|
|
132
134
|
│ │ group .groupReduce(), .groupSize(), .groupSum(), .groupMean(), .groupToArrays(), .groupToSets(),
|
|
133
135
|
│ │ .groupToDicts(), .groupEvery(), .groupSome()
|
|
134
136
|
│ ├─ Dict → .size(), .has(), .get(), .tryGet(), .keys(), .getKeys(), .insert(), .insertOrUpdate(), .update(),
|
|
@@ -164,6 +166,9 @@ Task → What do you need?
|
|
|
164
166
|
│ East.is() is identity for mutable collections — FROZEN ones (task inputs) compare by value
|
|
165
167
|
├─ Patches (East.*) → East.diff(before, after), East.applyPatch(value, patch),
|
|
166
168
|
│ East.composePatch(first, second, type), East.invertPatch(patch, type)
|
|
169
|
+
├─ Build East over types you are handed (a library, a generator) → East.field(v, name, T?), East.fields(v, names?),
|
|
170
|
+
│ East.struct(T, fields), East.withField(v, name, x), East.inCase(T, name, x), East.patchesFor(T): each name
|
|
171
|
+
│ checked as the program builds → "Names and types held as values"
|
|
167
172
|
│
|
|
168
173
|
├─ Ask a question of a value in jq → East.jq(input, program, resultType), checked when the program
|
|
169
174
|
│ builds → "Queries (typed jq)"
|
|
@@ -239,6 +244,42 @@ A variant is built with `variant()`, `some()` or `none` — never a
|
|
|
239
244
|
`{ type, value }` literal, which lacks the marker the encoders read. Variants are
|
|
240
245
|
encoded, compared and validated by case **name**.
|
|
241
246
|
|
|
247
|
+
### Names and types held as values
|
|
248
|
+
|
|
249
|
+
East names a struct's fields and a variant's cases where its code is written:
|
|
250
|
+
`row.site`, `variant("load", x)`. Code that builds programs over types it is
|
|
251
|
+
handed — a library generating a program for whatever record its caller
|
|
252
|
+
declares — holds the names, and the types, as values. These build the same
|
|
253
|
+
program pieces from them, and check each name against the type as the program
|
|
254
|
+
is built: a name the type lacks fails there, naming the name and the type
|
|
255
|
+
(`East.field: .Struct {name, site, status} has no field 'job'`). Nothing in the
|
|
256
|
+
program differs from what the written form builds, so every runtime runs it.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
const Machine = StructType({ name: StringType, site: StringType, status: StringType });
|
|
260
|
+
const Reading = VariantType({ load: FloatType, output: FloatType });
|
|
261
|
+
|
|
262
|
+
const site = "site"; // a name a caller hands you
|
|
263
|
+
const siteOf = East.function([Machine], StringType, ($, machine) =>
|
|
264
|
+
East.field(machine, site, StringType)); // typed by the type given, which is checked
|
|
265
|
+
|
|
266
|
+
const stopped = East.function([Machine], Machine, ($, machine) =>
|
|
267
|
+
East.withField(machine, "status", "down")); // the other fields as they are
|
|
268
|
+
|
|
269
|
+
const Place = StructType({ site: StringType, name: StringType });
|
|
270
|
+
const placeOf = East.function([Machine], Place, ($, machine) =>
|
|
271
|
+
East.struct(Place, East.fields(machine, ["site", "name"]))); // by name, or in the type's order
|
|
272
|
+
|
|
273
|
+
const readingOf = (place: string) => East.function([FloatType], Reading, ($, x) =>
|
|
274
|
+
East.inCase(Reading, place, x)); // a variant's case by name
|
|
275
|
+
|
|
276
|
+
const { diff, apply, compose, invert } = East.patchesFor(Machine); // East functions of a type held as a value
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
A recursive value is read, and rebuilt, through its node. `fields` and
|
|
280
|
+
`withField` read the value once a field: give them a variable, not a
|
|
281
|
+
computation. Code whose names are written keeps writing `row.site`.
|
|
282
|
+
|
|
242
283
|
### Error handling
|
|
243
284
|
|
|
244
285
|
```typescript
|
|
@@ -268,11 +309,37 @@ const nest = East.function([ArrayType(Row)], ArrayType(Node), ($, rows) =>
|
|
|
268
309
|
❗ `toTree: cycle through key <key>` — both before any `build`.
|
|
269
310
|
- A non-recursive `Node` folds bottom-up: `IntegerType` with
|
|
270
311
|
`children.sum().add(1n)` is each root's subtree size.
|
|
312
|
+
|
|
313
|
+
`flattenTree` is the way back: every node of nested data, each with its path
|
|
314
|
+
of keys (a Dict's) or indices (an Array's) from its root, depth first — each
|
|
315
|
+
node before its descendants, siblings in key or array order. `children` names
|
|
316
|
+
a node's children, of the collection's own type.
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
const Part = RecursiveType(self => StructType({ name: StringType, cost: FloatType, parts: DictType(StringType, self) }));
|
|
320
|
+
|
|
321
|
+
const flat = East.function([DictType(StringType, Part)], ArrayType(StructType({ path: ArrayType(StringType), name: StringType })), ($, parts) =>
|
|
322
|
+
parts.flattenTree(($, part) => part.unwrap().parts).map(($, e) => ({ path: e.path, name: e.node.unwrap().name })));
|
|
323
|
+
// [{ path: ["pump"], … }, { path: ["pump", "motor"], … }, { path: ["pump", "motor", "rotor"], … }, { path: ["pump", "seal"], … }]
|
|
324
|
+
|
|
325
|
+
// A rollup at every root: toTree over the paths, a node's parent its path less its last key
|
|
326
|
+
const totals = East.function([DictType(StringType, Part)], ArrayType(FloatType), ($, parts) =>
|
|
327
|
+
parts.flattenTree(($, part) => part.unwrap().parts).toTree(FloatType,
|
|
328
|
+
($, e) => e.path,
|
|
329
|
+
($, e) => East.greater(e.path.size(), 1n).ifElse(() => some(e.path.slice(0n, e.path.size().subtract(1n))), () => none),
|
|
330
|
+
($, e, _i, children) => children.sum().add(e.node.unwrap().cost)));
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
- The result is an Array of `{ path, node }`, not a Dict keyed by path: a Dict
|
|
334
|
+
key is immutable, and a path is an Array.
|
|
335
|
+
- `children` runs once per node, in the result's order. The walk is iterative;
|
|
336
|
+
each path is an array of its own, so a chain n deep holds n(n+1)/2 path
|
|
337
|
+
elements.
|
|
271
338
|
- **Recursion** — a function calling itself through a captured variable
|
|
272
339
|
(`$.let(fn, FunctionType(...))`, then `$.assign(fn, East.function(...))`) —
|
|
273
340
|
is for shallow depths, in the hundreds. Deeper, a call ❗
|
|
274
341
|
`call stack exhausted: East calls nested too deeply`, a catchable error on
|
|
275
|
-
every runtime. Walk deep data with `$.while` or `
|
|
342
|
+
every runtime. Walk deep data with `$.while`, `toTree` or `flattenTree`, which are iterative;
|
|
276
343
|
the C and Python runtimes still bound a recursive VALUE's depth to tens of
|
|
277
344
|
thousands of levels.
|
|
278
345
|
|
|
@@ -333,7 +333,6 @@ gh pr create --repo elaraai/east-workspace --title "fix(<lib>): …" --body "…
|
|
|
333
333
|
- `hooks/useDatasetValue.ts` — `[...StateImpl, ...BindPlatform, ...OverlayImpl]`.
|
|
334
334
|
- Import the impl from `@elaraai/east-ui-components/platform`. **Manifest-scoped** functions (data/func/record) go in via their `createScoped*(manifest…)` builder; **browser-local-state** functions (State, overlay, slice) go in **unscoped**, alongside `...StateImpl` / `...OverlayImpl`.
|
|
335
335
|
- Grep guard before you call a UI platform fn done: `rg -n "MyImpl" libs/east-ui/packages/e3-ui-components/src` must hit `UITaskPreview.tsx` **and** `useDatasetValue.ts`, not just the registering module.
|
|
336
|
-
- **Also rebuild + reinstall the `east-ui-preview` VS Code extension** — its webview bundles e3-ui-components, so a deployed package using the new fn renders blank / errors until the extension itself ships the array change. A version bump alone is not enough.
|
|
337
336
|
|
|
338
337
|
## Do NOT
|
|
339
338
|
|
package/skills/east-py/SKILL.md
CHANGED
|
@@ -132,6 +132,8 @@ Task → What do you need?
|
|
|
132
132
|
│ │ East.break_ / continue_ / label → "Control flow — the expression forms", "Sequential logic that stays in east-c"
|
|
133
133
|
│ ├─ Nest flat parent-keyed rows (any depth) → xs.to_tree(Node, key, parent, build) — not recursion, which is for
|
|
134
134
|
│ │ shallow depths → "Trees from flat rows, and recursion"
|
|
135
|
+
│ ├─ Flatten nested data back to {path, node} rows, depth first → xs.flatten_tree(children) (an Array's or a Dict's)
|
|
136
|
+
│ │ → "Trees from flat rows, and recursion"
|
|
135
137
|
│ ├─ Query a value with jq → East.jq(input, "jq", result_type) ❗ result_type is required, and types the result ·
|
|
136
138
|
│ │ a dict of inputs is read as an e3 root → "Queries — East.jq"
|
|
137
139
|
│ ├─ What a body may capture — and what it refuses, naming the binding → "What a body may reference"
|
|
@@ -325,13 +327,20 @@ or call it (a `Call`; in an `East.asyncFunction`, calling an async one is a
|
|
|
325
327
|
|
|
326
328
|
**Error locations.** A build records the python frames that built each node,
|
|
327
329
|
so a runtime error names its authoring site: `EastError.location` is the stack —
|
|
328
|
-
the lambda's `file:line:column` first, then
|
|
329
|
-
|
|
330
|
-
the same way. The
|
|
331
|
-
the
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
330
|
+
the lambda's `file:line:column` first, then any helper between, then the
|
|
331
|
+
`East.function(...)` call — and a platform-signature mismatch at `East.compile`
|
|
332
|
+
names the call the same way. The stack stops at that call: the code that
|
|
333
|
+
imported or called the module is not recorded. A recorded path is relative to
|
|
334
|
+
its file's project root, the nearest directory above it holding a
|
|
335
|
+
`pyproject.toml`, so a function's IR is the same whichever module imported it
|
|
336
|
+
and whatever directory the process ran in. A file in no project is relative to
|
|
337
|
+
the working directory, and `set_location_base_path(dir)` (from `east`) pins one
|
|
338
|
+
base for every path, for reproducible fixtures (`None` restores the default);
|
|
339
|
+
`east-py check` names a refusal's frames relative to the working directory, as
|
|
340
|
+
an editor opens them. The map rides the function's beast2 encoding, so the
|
|
341
|
+
error reads the same on east-c or east-node. An error inside a callback a
|
|
342
|
+
builtin invokes (`arr.map(...)` and friends) resolves to the builtin's call
|
|
343
|
+
site, on every runner.
|
|
335
344
|
`set_location_capture(False)` (from `east`) builds without recording any
|
|
336
345
|
locations: there is then no stack walk per node, and an error raised by such a
|
|
337
346
|
function carries no location. Binding names are read separately and keep
|
|
@@ -854,6 +863,13 @@ const score = East.importFunction("pricing", "score", FunctionType([RowType], Fl
|
|
|
854
863
|
await e3.export(pkg, "out.zip");
|
|
855
864
|
```
|
|
856
865
|
|
|
866
|
+
e3 exports each workspace package in one `east-py export-functions --batch`
|
|
867
|
+
process, however many tasks import from it, and keeps the manifests, so a
|
|
868
|
+
redeploy whose python has not changed runs no export (the **e3** skill). A
|
|
869
|
+
function's manifest entry is its own: the same bytes whatever else the process
|
|
870
|
+
built first and whatever directory it ran in, so a task embedding it keeps its
|
|
871
|
+
hash until the function, or a line it was written at, changes.
|
|
872
|
+
|
|
857
873
|
For a package built elsewhere (published, another repo), or to link
|
|
858
874
|
in-process, write the manifest where the package lives and pass it:
|
|
859
875
|
|
|
@@ -1117,7 +1133,7 @@ at the call. `.element_type` is the logical element type.
|
|
|
1117
1133
|
| Search | `find_first(target, key=None) -> some/none` · `find_all(value, by=None) -> Array<Integer>` · `find_maximum/find_minimum(by=None) -> some(index)/none` · `find_sorted_first/last(target, key=None) -> int` · `find_sorted_range(target, key=None) -> {start,end}` · `first_map(fn(el)->some/none, out=None)` · `is_sorted(key=None) -> bool` |
|
|
1118
1134
|
| Flatten | `flat_map(fn(el)->arr, out=None)` (`flatten_to_array` deprecated) · `flatten_to_set(fn(el)->set, out=None)` · `flatten_to_dict(fn(el)->dict, combine=None)` (a duplicate key errors without `combine`) |
|
|
1119
1135
|
| Columnar | `to_columns(fields=None) -> dict` (numpy per numeric/bool column, `Option<Float>`→NaN, interned strings) · `EastArray.from_columns(element_type, columns)` *(static)* (C-side fill needs numpy columns — float64/int64/bool, `Option<Float>` as float64+NaN; python lists convert per cell) · `map_batches(fn(cols)->cols, out=None, batch_size=100_000)` |
|
|
1120
|
-
| Convert | `to_tree(node, key(el[, i]), parent(el[, i])->some/none, build(el, i, children)) -> Array<node>` (flat parent-keyed rows → nested nodes of any depth, iterative — [Trees from flat rows](#trees-from-flat-rows-and-recursion)) · `string_join(sep) -> str` (String arrays) · `to_vector() -> EastVector` (Float/Integer/Boolean elements, in order — east-c VectorFromArray; the expression method emits the same builtin) · `encode_csv(config=None, **options) -> EastBlob` (Array of structs; east-c ArrayEncodeCsv) |
|
|
1136
|
+
| Convert | `to_tree(node, key(el[, i]), parent(el[, i])->some/none, build(el, i, children)) -> Array<node>` (flat parent-keyed rows → nested nodes of any depth, iterative — [Trees from flat rows](#trees-from-flat-rows-and-recursion)) · `flatten_tree(children(el[, i])) -> Array<{path, node}>` (nested nodes back: every node with its path of indices, depth first, iterative) · `string_join(sep) -> str` (String arrays) · `to_vector() -> EastVector` (Float/Integer/Boolean elements, in order — east-c VectorFromArray; the expression method emits the same builtin) · `encode_csv(config=None, **options) -> EastBlob` (Array of structs; east-c ArrayEncodeCsv) |
|
|
1121
1137
|
| Mutate (in place, the TS names) | `push_last(item)` · `push_first(item)` · `append(array)` (ArrayAppend — a whole array, NOT one element) · `prepend(array)` · `pop_last()` · `pop_first()` · `update(i, item)` ❗bounds · `merge(i, value, update_fn(existing, incoming[, i]))` · `merge_all(array, merge_fn(existing, incoming[, i]))` · `clear()` · `sort_in_place(by=None)` · `reverse_in_place()` — plus the python protocol: `extend(iterable)` (bulk: one crossing; C-to-C for same-type East arrays, raw buffers for numpy) · `insert(i, item)` · `pop(i=-1)` · `remove(item)` · `count(value) -> int` · `index(value) -> int` |
|
|
1122
1138
|
|
|
1123
1139
|
### EastSet — complete method surface
|
|
@@ -1159,7 +1175,7 @@ in `other`'s values; `merge` takes a single differently-typed value.
|
|
|
1159
1175
|
| Reduce | `reduce(fn(acc, value[, key]), init)` (TS order) · `scan(fn(acc, value[, key]), init) -> Array` (running fold in key order) · `map_reduce(map_fn(value[, key]), reduce_fn(a, b), out=None)` (raises on empty) · `sum(fn(value[, key])=None)` · `mean(fn(value[, key])=None) -> float` · `every(pred(value[, key])=None) -> bool` · `some(pred(value[, key])=None) -> bool` (native short-circuit) |
|
|
1160
1176
|
| Group | `group_reduce(key_fn(value[, key]), init_fn(gk), fold_fn(acc, value[, key]), key_out=None, acc_out=None) -> Dict` · `group_size(key_fn)` · `group_sum(key_fn, fn=None)` · `group_mean(key_fn, fn=None)` · `group_every/group_some(key_fn, pred(value[, key]))` · `group_to_arrays/group_to_sets(key_fn, value_fn=None)` · `group_to_dicts(key_fn, key2_fn, value_fn=None, combine=None)` · ⚠️ `group_fold(...)` is the DEPRECATED alias of `group_reduce` |
|
|
1161
1177
|
| Flatten | `flatten_to_array(fn(value[, key])->arr, out=None)` (the TS Dict name; `flat_map` is Array's and a deprecated spelling here) · `flatten_to_set(fn(value[, key])->set, out=None)` · `flatten_to_dict(fn(value[, key])->dict, combine=None)` (a duplicate key errors without `combine`) |
|
|
1162
|
-
| Convert | `keys() -> Set` · `to_array(fn(value[, key]), out=None)` · `to_set(fn(value[, key]), out=None)` · `to_dict(key_fn, value_fn=None, combine=None, key_out=None, value_out=None)` (the value itself when `value_fn` is omitted; a duplicate key errors without `combine`) · `to_tree(node, parent(value[, key])->some/none, build(value, key, children)) -> Array<node>` (each entry's key its key) · `copy()` |
|
|
1178
|
+
| Convert | `keys() -> Set` · `to_array(fn(value[, key]), out=None)` · `to_set(fn(value[, key]), out=None)` · `to_dict(key_fn, value_fn=None, combine=None, key_out=None, value_out=None)` (the value itself when `value_fn` is omitted; a duplicate key errors without `combine`) · `to_tree(node, parent(value[, key])->some/none, build(value, key, children)) -> Array<node>` (each entry's key its key) · `flatten_tree(children(value[, key])) -> Array<{path, node}>` (nested nodes back: every node with its path of keys, depth first, iterative) · `copy()` |
|
|
1163
1179
|
| Mutate (in place) | `d[k]=v` · `del d[k]` · `insert(k, v)` (errors if present) · `get_or_insert(k, fn(k))` · `insert_or_update(k, v, combine(existing, incoming, k))` · `update(k, v)` (TS; the read-modify-write `update(k, fn(current))` is deprecated) · `swap(k, v) -> prev` · `delete(k)` · `try_delete(k) -> bool` (`update`/`swap`/`delete` error on a missing key) · `pop(k, *default)` · `clear()` |
|
|
1164
1180
|
| Bulk (in place) | `update_many(keys, values, combine(existing, incoming)=None)` — the whole batch crosses once; a pure/precompiled `combine` resolves collisions C-to-C (dicts as hot-loop accumulators) |
|
|
1165
1181
|
|
|
@@ -1598,7 +1614,7 @@ east-py version -p east-py-std # the runner's version, and each
|
|
|
1598
1614
|
| `exec <unit> [-v] [--profile]` | The runner protocol: one unit file says what to run — a program over its inputs, read as its `decode` says (as `run --decode`), or a merge of an output's parts — and where; the output is written by its kind and a result recorded. Exit 0 when it succeeded, 1 when it failed (the message and locations also go to stderr), 2 when the unit cannot be read or the result written. Relative paths in the unit are relative to its directory |
|
|
1599
1615
|
| `convert <file> [-o FILE.east] [-v]` | Decodes a beast2 value (its header names the type) and prints it as East text, or writes it to a `.east` file |
|
|
1600
1616
|
| `transpile <ir> [-o FILE] [--name NAME] [-p PACKAGE]…` | IR as a python module rebuilding it (`--name` binds it, default `main`; each `-p` prints its calls as that package's own functions) — [IR ↔ python](#ir--python-east-py-transpile-and-the-east-c-ir-toolbox) |
|
|
1601
|
-
| `export-functions <module> -o FILE [-p PACKAGE]… [--name NAME] [--package-version V] [--only NAME]…` | A module's (a dotted name, or a `.py` path) `east_functions` as a function manifest; every platform call must be provided by a `-p` package; `--name` defaults to the module's top-level name, the version to the installed distribution's (else `0.0.0`); `--only` exports just those — [Cross-language functions](#cross-language-functions-east-py-export-functions-and-eastimport_function) |
|
|
1617
|
+
| `export-functions <module> -o FILE [-p PACKAGE]… [--name NAME] [--package-version V] [--only NAME]…` | A module's (a dotted name, or a `.py` path) `east_functions` as a function manifest; every platform call must be provided by a `-p` package; `--name` defaults to the module's top-level name, the version to the installed distribution's (else `0.0.0`); `--only` exports just those, building only them and what they call (a sibling that does not build fails nothing). `--batch FILE` in place of `-o`/`-p`/`--only` writes several manifests from one import, FILE a beast2 batch (`FunctionExportBatchType`: each entry's `output`, `packages` and `only`) — what `e3.export` runs per workspace package — [Cross-language functions](#cross-language-functions-east-py-export-functions-and-eastimport_function) |
|
|
1602
1618
|
| `lint [paths…] [--format text\|json] [--disable RULE]… [--exclude DIR]… [--list-rules]` | The rules (default path `.`); exit 1 on any finding, a warning included, 2 on a usage error — [Diagnostics](#diagnostics-at-edit-time--east-py-lint-east-py-check-flake8-east-py-lsp) |
|
|
1603
1619
|
| `check <targets…> [--format text\|json] [--only-if-enabled]` | The build's own errors; exit 1 on any |
|
|
1604
1620
|
| `lsp [--probe]` | The Language Server over stdio (needs `pygls`); `--probe` reports whether it can start here |
|
|
@@ -1899,10 +1915,30 @@ roots = rows.to_tree(Node,
|
|
|
1899
1915
|
- A non-recursive `Node` folds bottom-up: `IntegerType` with
|
|
1900
1916
|
`children.sum() + 1` is each root's subtree size. A Set's element and a
|
|
1901
1917
|
Dict's key are their own keys, so those take no `key`.
|
|
1918
|
+
|
|
1919
|
+
`flatten_tree` is the way back: every node of nested data, each with its path
|
|
1920
|
+
of keys (a Dict's) or indices (an Array's) from its root, depth first — each
|
|
1921
|
+
node before its descendants, siblings in key or array order. `children` names a
|
|
1922
|
+
node's children, of the collection's own type:
|
|
1923
|
+
|
|
1924
|
+
```python
|
|
1925
|
+
from east import DictType, FloatType, StringType, StructType, recursive_type
|
|
1926
|
+
|
|
1927
|
+
Part = recursive_type(lambda self: StructType([("name", StringType), ("cost", FloatType), ("parts", DictType(StringType, self))]))
|
|
1928
|
+
|
|
1929
|
+
nodes = parts.flatten_tree(lambda b, part: part.unwrap().parts)
|
|
1930
|
+
# an Array of {path, node}: ["pump"], ["pump", "motor"], ["pump", "motor", "rotor"], ["pump", "seal"]
|
|
1931
|
+
```
|
|
1932
|
+
|
|
1933
|
+
- An Array of `{path, node}`, not a Dict keyed by path: a Dict key is
|
|
1934
|
+
immutable, and a path is an Array. `to_tree` over it, keyed by `path` and
|
|
1935
|
+
parented by `path` less its last step, rolls the tree up at every root.
|
|
1936
|
+
- `children` runs once per node, in the result's order. Each path is an array
|
|
1937
|
+
of its own, so a chain n deep holds n(n+1)/2 path elements.
|
|
1902
1938
|
- **Recursion** — a function calling itself through a captured variable — is
|
|
1903
1939
|
for shallow depths, in the hundreds. Deeper, a call ❗ `call stack exhausted:
|
|
1904
1940
|
East calls nested too deeply`, a catchable `EastError` on every runtime. Walk
|
|
1905
|
-
deep data with `b.while_` or `
|
|
1941
|
+
deep data with `b.while_`, `to_tree` or `flatten_tree`; a recursive VALUE's depth is still
|
|
1906
1942
|
bounded, at tens of thousands of levels, by east-c's recursive collect and
|
|
1907
1943
|
free.
|
|
1908
1944
|
|
|
@@ -1997,6 +2033,12 @@ out = EastMatrix(FloatType, model(t).detach().cpu().numpy()) # bridge canonica
|
|
|
1997
2033
|
- **Read a captured East collection with `.get(expr)` /
|
|
1998
2034
|
`.get_or_default(expr, d)` / `.try_get(expr)`** — `[expr]` on a captured
|
|
1999
2035
|
constant does not build IR (python coerces the index via `__index__`).
|
|
2036
|
+
- **A python set has no order of its own.** Under a Set type
|
|
2037
|
+
(`b.const({"a", "b"}, SetType(StringType))`, `East.new_set(T, {…})`) its
|
|
2038
|
+
elements are built in East's order of `T`, the order an `EastSet` keeps, so
|
|
2039
|
+
the IR is the same in every process; a list keeps the order it is written in.
|
|
2040
|
+
A python set holding a traced value (`{some(x)}`) has nothing to order by and
|
|
2041
|
+
is refused — write a list.
|
|
2000
2042
|
- **Genuinely-python loops cross the boundary once** — `to_columns()` /
|
|
2001
2043
|
`EastArray.from_columns` / `map_batches`, never a platform call or a decode per
|
|
2002
2044
|
element.
|
package/skills/east-ui/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: east-ui
|
|
3
|
-
description: "Type-safe UI component library for the East language, authored as JSX tags. Use when writing East programs that define user interfaces. Triggers for: (1) Authoring `.tsx` component trees with `@elaraai/east-ui` tags, (2) Layout with <Box>, <Flex>, <Stack>/<VStack>/<HStack>, <Grid>, <SnapGrid> (a page of tiles held as data on the 12-column grid, and its wireframe thumbnail), <Splitter>, <ScrollArea>, <Sticky>, <Expandable>, <Dock>, <Configurator> (control table + live preview + spec readout), (3) Forms with <Input>, <Textarea>, <Select>, <Combobox>, <Checkbox>, <Switch>, <Slider>, <RadioGroup>, <RadioCardGroup>, <TagsInput>, <FileUpload>, <Field>, <DateRangeInput>, <TimeRangeInput>, (4) Data display with <Table>, <TreeView>, <ValueTree>, <DataList>, <Deck>, <Matrix>, <
|
|
3
|
+
description: "Type-safe UI component library for the East language, authored as JSX tags. Use when writing East programs that define user interfaces. Triggers for: (1) Authoring `.tsx` component trees with `@elaraai/east-ui` tags, (2) Layout with <Box>, <Flex>, <Stack>/<VStack>/<HStack>, <Grid>, <SnapGrid> (a page of tiles held as data on the 12-column grid, and its wireframe thumbnail), <Splitter>, <ScrollArea>, <Sticky>, <Expandable>, <Dock>, <Configurator> (control table + live preview + spec readout), (3) Forms with <Input>, <Textarea>, <Select>, <Combobox>, <Checkbox>, <Switch>, <Slider>, <RadioGroup>, <RadioCardGroup>, <TagsInput>, <FileUpload>, <Field>, <DateRangeInput>, <TimeRangeInput>, (4) Data display with <Table>, <TreeView>, <ValueTree>, <DataList>, <Deck>, <Matrix>, <Map>, <Library> (a drag palette, or a gallery of large cards whose media is any component), <Blend>, <Slice.Rail>, <Pagination>, <ChipRail>, <Trace>, (5) Charts with <Chart layers={Chart.Line/Column/Bar/Area/Scatter/Band(...)}/> (Column = vertical, Bar = horizontal) plus Chart.refLine/refBand/refDot, <Sparkline>, (6) Overlays with <Dialog>, <Drawer>, <Popover>, <Menu>, <Tooltip>, <HoverCard>, <ToggleTip>, <ActionBar>, <CommandPalette>, <Hotkey>, (7) Feedback with <Banner>, <Status>, <Progress>, <Skeleton>, <EmptyState>, (8) Disclosure with <Tabs>, <Accordion>, <Carousel>, <Collapsible>, <SegmentGroup>, <OptionList>, <Story>, (9) Navigation within a page with <Breadcrumb> and <NavList> (an app's own navigation is its host's), (10) Reactive UI via <Reactive>{$ => …}</Reactive> + State.bind, and conditional hosting of stateful components via <Match on cases> (remounts the active variant case on tag change), (11) Value formatting — Chart.format.* specs (chart axes, Slice fields, Deck metrics, e3-ui's Plan) and Format.* specs (<Numeric>, <Stat>, Table columns) through one interpreter, in the viewer's locale (react-aria's I18nProvider) with every date in UTC, (12) Status colour vocabulary — the five status tokens, the Deck.statuses registry, Library.status, rowStatus tints and tone props — and ColorTokenType, the typed colour an app gives for a data fill (a theme hue at a shade, Style.ColorToken(\"teal\", \"500\"))."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# East UI
|
|
@@ -138,10 +138,10 @@ Task → Which tag?
|
|
|
138
138
|
│ │ │ ├─ variant (optional) — "tiles" (default) | "wireframe": each tile an outline at its size with its content left out, its spans kept at any width and its declared height exact — a page's thumbnail. It draws no frame of its own: its host frames it (a Library gallery card's media) and it fills the host's height. With no rows it is the blank page — a band over the body, each dashed
|
|
139
139
|
│ │ │ ├─ width (optional) — the design width ("1440px"): a page's grid is that wide, scrolls inside a narrower host, and its spans answer to it; on the editing canvas it is the most the grid lays out at — a narrower column lays it out at the column's width, a wider one centres it — and the toolbar reads it out
|
|
140
140
|
│ │ │ ├─ height / maxHeight (optional) — pin or cap the SnapGrid; the grid scrolls within
|
|
141
|
-
│ │ │ ├─ edit + editing (optional, given together) — the builder's canvas: every tile keeps its declared span at any width, and each gesture is a DRAFT of the shared editing session — e3-ui's Sheet's and Plan's — each ONE undoable transaction (origin drop / move / resize / remove); the history item (Undo · Redo · Discard · Save) sits in the canvas's toolbar (see The frame), and ⌘Z / Ctrl+Z undo, ⌘⇧Z / Ctrl+Y redo. Editing takes an `Array` source — its order is the tiles' order, so a move is a new order and a new row key; a `Dict` is refused. `edit` names the row fields a gesture writes: { key: "id" (the String identity field), row: "row" (String), span: "span" (Integer), height?: "height" (Option<Integer>; omitted, heights are not edited), align?: "align" (SnapGrid.Types.Align; the field an align request writes — omitted, one is refused), create?: ($, card, at) => R — a dropped library card's new row: `card` the drag grammar's { library, key }, `at` { key (a fresh identity), row (the row it lands in) }; the canvas then writes `at.row` and fits the span }. `editing` is { onApply? | onUpdate?, onPatch?, onDrafted?, mode?, ready? } as on e3-ui's Plan: `onUpdate={handle.write}` with `data={handle}` is the inline adapter; `onPatch` hears SnapGrid.Types.PatchEvent(R); `onDrafted` fn(Array<R>) => Null hears the rows the canvas draws — the source with its drafts, exactly as Save would leave them — as it mounts and whenever they change (a gesture, an undo or redo, a Discard, a source that moved), so a pane beside it reads what it shows; `ready` fn(R) => Editing.Types.Readiness marks the tile it refuses and holds Save. Save leaves exactly the order the canvas shows
|
|
141
|
+
│ │ │ ├─ edit + editing (optional, given together) — the builder's canvas: every tile keeps its declared span at any width, and each gesture is a DRAFT of the shared editing session — e3-ui's Sheet's and Plan's — each ONE undoable transaction (origin drop / move / resize / remove); the history item (Undo · Redo · Discard · Save) sits in the canvas's toolbar (see The frame), and ⌘Z / Ctrl+Z undo, ⌘⇧Z / Ctrl+Y redo; the drafts are kept in the tab through a reload, and come back carried over to the source as it is then. Editing takes an `Array` source — its order is the tiles' order, so a move is a new order and a new row key; a `Dict` is refused. `edit` names the row fields a gesture writes: { key: "id" (the String identity field), row: "row" (String), span: "span" (Integer), height?: "height" (Option<Integer>; omitted, heights are not edited), align?: "align" (SnapGrid.Types.Align; the field an align request writes — omitted, one is refused), create?: ($, card, at) => R — a dropped library card's new row: `card` the drag grammar's { library, key }, `at` { key (a fresh identity), row (the row it lands in) }; the canvas then writes `at.row` and fits the span }. `editing` is { onApply? | onUpdate?, onPatch?, onDrafted?, mode?, ready? } as on e3-ui's Plan: `onUpdate={handle.write}` with `data={handle}` is the inline adapter; `onPatch` hears SnapGrid.Types.PatchEvent(R); `onDrafted` fn(Array<R>) => Null hears the rows the canvas draws — the source with its drafts, exactly as Save would leave them — as it mounts and whenever they change (a gesture, an undo or redo, a Discard, a source that moved), so a pane beside it reads what it shows; `ready` fn(R) => Editing.Types.Readiness marks the tile it refuses and holds Save. Save leaves exactly the order the canvas shows
|
|
142
142
|
│ │ │ ├─ ui (optional) — the bound selection: `State.bind([SnapGrid.Types.UiState], key, SnapGrid.uiState())` — the canvas draws the tile it holds and writes the one the user picks, so an inspector beside it reads and sets it; omitted, the canvas keeps its own. Its `request` asks the editing canvas for a change, as e3-ui's Plan's `focus` asks for a row: `ui.write(SnapGrid.uiState({ selected: key, request: { key, change: variant("span", 8n) } }))` — `span` (held to the row's room), `row` (counting from 1; a row already holding 6 tiles, or one past the last, is a new row after it), `height` (`none` for its content's), `align` (written to `edit.align`) — taken as ONE gesture of the session by the rules the handles and drags keep, undoable with the rest, and written back `none`
|
|
143
143
|
│ │ │ ├─ view (optional) — the bound design width and zoom: `State.bind([SnapGrid.Types.ViewState], key, SnapGrid.viewState())` under a key of its own, apart from `ui` — the editing canvas draws at them and writes the width the user presses and the zoom step they take; omitted, the canvas keeps them, starting from `width` and `zoom`
|
|
144
|
-
│ │ │ ├─ apply (optional) — the bound Apply request: `State.bind([SnapGrid.Types.ApplyState], key, variant("idle", null))` under a key of its own — a screen that must not leave drafts behind (a publish) writes `variant("asked", id)` with an id of its own, and the editing canvas saves its drafts as its history item does — waiting out a Save already running, retrying one of unknown outcome — and answers under the same id: `applied(id)` once the source confirms them, or at once with none; `refused({ id, reason })` when they cannot land, `reason` in the canvas's words (the conflict's, the rejection's, a check's or the
|
|
144
|
+
│ │ │ ├─ apply (optional) — the bound Apply request: `State.bind([SnapGrid.Types.ApplyState], key, variant("idle", null))` under a key of its own — a screen that must not leave drafts behind (a publish) writes `variant("asked", id)` with an id of its own, and the editing canvas saves its drafts as its history item does — waiting out a Save already running, retrying one of unknown outcome — and answers under the same id: `applied(id)` once the source confirms them, or at once with none; `refused({ id, reason })` when they cannot land, `reason` in the canvas's words (the conflict's, the rejection's, a check's, the drafts still being carried over to a source that moved, or the conflicts still to choose). The canvas stays the only writer of its drafts; omitted, only the history item saves
|
|
145
145
|
│ │ │ ├─ widths (optional) — [{ label, icon?, width }] the design widths the editing canvas's toolbar offers, a device each (`{ label: "Desktop", icon: "desktop", width: "1440px" }`, `{ label: "Tablet", icon: "tablet-screen-button", width: "1024px" }`): the one the canvas draws at is pressed, and pressing another draws it at that width
|
|
146
146
|
│ │ │ ├─ toolbar (optional) — { start?, end? } the host's items in the editing canvas's toolbar, each an array of UIComponents: `start` leads the row (a `<Status>`), `end` closes it (Preview, Publish buttons); they never fold
|
|
147
147
|
│ │ │ ├─ panes (optional) — { start?, end? } the panes beside the editing canvas, under its toolbar — a palette, an inspector: a `<Dock surface="shell">` pane collapses to its rail and the canvas column takes the room
|
|
@@ -529,35 +529,7 @@ Task → Which tag?
|
|
|
529
529
|
│ │ ├─ Matrix.cell({ segments?, markers?, orientation?, slot?, popover? }) — one cell (slot = arbitrary UIComponent; popover = click body)
|
|
530
530
|
│ │ ├─ Matrix.segment({ fill?, weight, label?, color?, min?, max?, step? }) — one segment of the cell bar
|
|
531
531
|
│ │ └─ Matrix.marker({ status?, message, at?, label? }) — corner status marker
|
|
532
|
-
│ ├─ <Schematic items
|
|
533
|
-
│ │ ├─ Props:
|
|
534
|
-
│ │ │ ├─ items + extent (required) — item rows + world bounds (canvas scales to fit)
|
|
535
|
-
│ │ │ ├─ item (optional) — row mapper to { key, x, y, label, sublabel?, icon?, status?, meter?{value,max}, metric?, width?, footprint?, tone?, color?, bg?, fillOpacity?, weight?, excluded?, layer? } (omit when already Schematic.Types.Item)
|
|
536
|
-
│ │ │ ├─ zones + zone (optional) — zone rows + mapper to { key, label, x, y, width, height, pattern?, geometry?, tone?, color?, bg?, fillOpacity?, weight?, layer? }
|
|
537
|
-
│ │ │ ├─ links + link (optional) — link rows + mapper to { key, from, to, label?, metric?, style?, route?, via?, layer? }
|
|
538
|
-
│ │ │ ├─ nets + net (optional) — manifold/bus rows (ONE row = many sources → many destinations) + mapper to { key, sources, destinations, label?, metric?, style?, route?, via?, layer? }; drawn P&ID-style — a header BAR spans each multi-endpoint side with a stub per endpoint, the trunk runs bar → via… → bar (junction dots ONLY where a tap 3-way joins a bar)
|
|
539
|
-
│ │ │ ├─ selectionMode (optional) — "single" (default) | "multiple" | "range"; multiple ⇒ marquee tool (drag-box multi-select w/ live preview + count; plain box/tap replaces, Shift extends)
|
|
540
|
-
│ │ │ ├─ onSelect / onSelectionChange (optional) — item click (key) / full selection-set events ({key?, selected, selectedKeys, additive, region?}); works in every tool (grab/zoom/marquee)
|
|
541
|
-
│ │ │ ├─ onSelectZone / onZoneSelectionChange (optional) — zone click-select (items win hit-test; innermost zone wins; Shift extends) reporting the zones AND their childItemKeys
|
|
542
|
-
│ │ │ ├─ selectZoomFocus (optional) — a canvas selection also moves the camera (tap flies, marquee fits)
|
|
543
|
-
│ │ │ ├─ onItemOpen (optional) — double-click drill-in (background double-click keeps Fit/reset)
|
|
544
|
-
│ │ │ ├─ onViewportChange (optional) — debounced viewport-settled reporting ({zoom, minX, minY, maxX, maxY}) for sync / lazy-load / persist
|
|
545
|
-
│ │ │ ├─ linkMode (optional) — connect-gesture mode: "draw" (adds locally, form-input style) | "connect" (event-only, repeatable: plan operations); Shift+drag ADDS to the session
|
|
546
|
-
│ │ │ ├─ onCreateLink / onSelectLink / onEditLink / onDeleteLink (optional) — link lifecycle ({link, links, additive, existing} on create; click-select key; endpoint re-target; Del delete)
|
|
547
|
-
│ │ │ ├─ canConnect (optional) — fn(from, to) => Bool vetoes pairs BEFORE they resolve (the draft never snaps; one rule covers links, Shift-session/net extensions, and re-targets; a throwing validator fails OPEN)
|
|
548
|
-
│ │ │ ├─ onEditNet (optional) — net membership edits: trunk/bar click selects the WHOLE net, a STUB click selects ONE leg (Del removes just that endpoint; onEditNet reports membership AFTER; a side emptying deletes the whole net via onDeleteLink). Shift-session gestures keep ONE bus (a member never flips sides); with a LINK selected, Shift-drag from its endpoint SEEDS the session with that link (onCreateLink.absorbed lists absorbed keys — delete those rows when upserting); with a NET selected, Shift-drag out of a member adds the target as a leg; a Shift connect-session commits as a net (onCreateLink.net = {key, sources, destinations}, stable session key — upsert by net.key)
|
|
549
|
-
│ │ │ ├─ readOnly / readOnlyLinks / readOnlyItems (optional) — flattened per-domain edit gates
|
|
550
|
-
│ │ │ ├─ onMoveItem (optional) — move tool (readOnlyItems off): drags items to new positions; a selected item moves the WHOLE selection rigidly, local-first; fires once per gesture ({key, x, y, keys, dx, dy})
|
|
551
|
-
│ │ │ ├─ itemHover / zoneHover / linkHover (optional) — East fn key => UIComponent lazy hover cards (charts in a card over a tank / pipe); any camera or edit gesture closes them; hover ignores readOnly + locked layers
|
|
552
|
-
│ │ │ ├─ slice + affordances (optional) — bound slice chrome (default ["search"]); flat effect props sliceHidden / sliceOpacity / sliceDesaturate / sliceDot / sliceEmphasis:"halo"|"pulse" / sliceFrame / sliceFrameFit keep filtered-OUT items as ghost/desaturate/dot context + emphasise the remainder instead of hiding — feed the FULL set (Slice.partition) and mark item.excluded (e.g. t.matched.not())
|
|
553
|
-
│ │ │ ├─ sliceSelectField (optional) — bound-slice fieldId a marquee/tap selection writes an `in` filter of selected item keys into (one-directional selection→slice) — pair with the ghost effect (not a Slice.rows feed on the same slice)
|
|
554
|
-
│ │ │ ├─ layers (optional) — [{ key, label, tone?, visible?, locked?, opacity? }] + tag items/zones/links with layer:"key"; a canvas layer button opens a show/hide/solo/lock panel (persists per panel); lock ⇒ non-selectable (click-through), opacity dims
|
|
555
|
-
│ │ │ ├─ scaleUnit / grid / navigator / minimap (optional) — scale bar unit, metric grid (default on), zones→items TOC (default when zones exist), minimap (default 25+ items)
|
|
556
|
-
│ │ │ └─ height / maxHeight (optional) — uniform sizing (#320); default aspect-driven, capped 75vh
|
|
557
|
-
│ │ └─ Factories:
|
|
558
|
-
│ │ ├─ Schematic.circle(r) / .polyline(verts, {width}) / .polygon(verts) / .rect() — item footprints + zone geometry
|
|
559
|
-
│ │ ├─ Schematic.outline() / .hatch() — zone boundary patterns
|
|
560
|
-
│ │ └─ Schematic.solid() / .dashed() — link / net stroke styles
|
|
532
|
+
│ ├─ <Schematic> — the 2D world-coordinate drawing of the app's own records (items, zones, links and nets, each a kind over a record, placed where its row says; a camera, layers and selection) is e3-ui's (#1384, #1386): `import { Schematic } from "@elaraai/e3-ui"` — its props, factories and patterns are in the e3-ui skill
|
|
561
533
|
│ ├─ <Flowchart> — the state-transition flowchart (states in ORDERED phase lanes, its layout derived — no coordinates; H/V-routed transitions; per-link decision triggers and evidence) is e3-ui's (#1243): `import { Flowchart } from "@elaraai/e3-ui"` — its props, factories and patterns are in the e3-ui skill
|
|
562
534
|
│ ├─ <Map markers={…} center={Map.at(lat,lng)} zoom={n} /> — interactive geographic basemap; read-only / selection-only
|
|
563
535
|
│ │ ├─ Props:
|
|
@@ -657,11 +629,11 @@ Task → Which tag?
|
|
|
657
629
|
│ │ │ ├─ slice (required) — the bound handle from Slice.bind
|
|
658
630
|
│ │ │ ├─ affordances (optional) — ["filter","search","range","breakdown","cohort","presets","brush","legend"]; legends are explicit-only (list "legend" or compose <Slice.Legend>)
|
|
659
631
|
│ │ │ ├─ persist (optional) — "local" | "session" | "url" opts the state into reload-surviving / shareable-link storage
|
|
660
|
-
│ │ │ └─ brush (optional) — the brush strip is rich by default (the range field's format drives the axis labels; a self-excluding count histogram shows the row distribution); brush={{ axis?, count?, buckets? }} opts down to the bare track. The applied window is a full brush selection: drag its body to slide (width preserved), an edge to resize, empty track to draw (also the Plan's horizon-brush gesture)
|
|
632
|
+
│ │ │ └─ brush (optional) — the brush strip is rich by default (the range field's format drives the axis labels; a self-excluding count histogram shows the row distribution); brush={{ axis?, count?, buckets? }} opts down to the bare track. The applied window is a full brush selection: drag its body to slide (width preserved), an edge to resize, empty track to draw (also the Plan's horizon-brush gesture). Its look is the horizon brush's (#1438): bars of one width toned on the heat ramp by count, whole inside the window's band and faded outside it, an outlined tab on each edge, and a hover's tooltip saying a bin's range and count; the start handle, the window and the end handle are three tab stops — ← → move a bin, Shift ten, Home / End the ends, Esc clears
|
|
661
633
|
│ │ ├─ Nested tags: <Slice.Filter/Search/Range/Breakdown/Legend/Cohort/Presets/Summary slice={slice} /> — per-affordance chrome; <Slice.Cohort mode="toggle"|"manage" allowCreate? group?> (cohorts toggle on chip click; cohorts sharing a `group` render as one captioned run — a FAMILY of alternatives: active members OR within a family, families AND with each other and with standalone cohorts; an empty family member hides on the preset bar unless on; `group="…"` shows one family alone; <Slice.Presets> = toggle-only preset bar); <Slice.Legend> = facet bar (click = in-set multi-select over self-excluding slice.facetGroups(); mode="visibility" = eye rail); Summary/Filter footers read "N of M"
|
|
662
634
|
│ │ └─ Factories:
|
|
663
635
|
│ │ ├─ Slice.bind(key, rows, options?) — local Array rows; infers the row type. Options: fields (label/hints/format), range, search, initial, presets, breakdown and match. Initial/preset clauses name typed fields; local operators include gte/contains. The positional form is removed.
|
|
664
|
-
│ │ ├─ Slice.bindRecord(key, indexed, options?) — Data.bindPaged(record, { indexes }) from e3-ui; fields/range/search/initial/presets come from declared field indexes. No browser predicate, local operators, match or breakdown. Several exact values then one range/prefix must follow one index; a Set field takes one exact member.
|
|
636
|
+
│ │ ├─ Slice.bindRecord(key, indexed, options?) — Data.bindPaged(record, { indexes }) from e3-ui; fields/range/search/initial/presets come from declared field indexes: a field is any row field an index is led by, whatever its name — `area` and `__tile` included, e3's parts of a generated key being its index's kind's (#1432). No browser predicate, local operators, match or breakdown. Several exact values then one range/prefix must follow one index; a Set field takes one exact member. A source's `pageWhere`/`countWhere` take a read, `{ where, area }`: the narrowing, and a read by area's window or unplaced clause beside it, none for any other read.
|
|
665
637
|
│ │ ├─ Slice.bindRecords(key, { name: { source: indexed, fields?, range?, search?, initial? }, … }, { presets? }) — several records narrowed from ONE rail: one Slice.bindRecord per named source under `${key}.${name}`; a choice in one source never changes another's rows or counts (no index relates two records). Presets narrow any sources at once (a source a preset doesn't name is cleared); slice.togglePreset(id) / slice.clearAll() act on every part. A component that narrows several kinds of row takes it as slice={{ slice }} with its sources named after them (the e3-ui Allocation's `lanes` and `resources`) and reads each source itself.
|
|
666
638
|
│ │ ├─ Slice.config(Row, { fields, rangeFieldId?, searchFieldIds?, breakdownFieldIds? }) / Slice.state({…}) — low-level config/state for pure Slice.apply and state operations; bind options infer and seed these automatically.
|
|
667
639
|
│ │ ├─ Slice.rows(slice) — the narrowed Array for local rows, or for records a live ordered source: one per binding whatever the narrowing, its windows read under the narrowing as it stands, its `query()` naming it (#1354); record entries retain original key beside tagged ik, covering value and optional joined row. Slice.rows(slice, source) reads one source of a Slice.bindRecords handle.
|
|
@@ -788,7 +760,7 @@ Task → Which tag?
|
|
|
788
760
|
│ └─ plotGutter (optional) — shared gutter (#147): pins the step lane so a Trace stacked under a Chart lines up (left supersedes labelWidth)
|
|
789
761
|
│
|
|
790
762
|
├─ Feedback (status & async signals)
|
|
791
|
-
│ ├─ <Banner> — page-spanning notice
|
|
763
|
+
│ ├─ <Banner> — page-spanning notice, the design system's Banner card: one row centred on itself — the status's mark (a triangle for warning/guard, a circle for error, else its 8px dot), the title as its message in the body face at 13, the description under it, the actions at its end (one Default button makes it 48 tall)
|
|
792
764
|
│ │ └─ Props:
|
|
793
765
|
│ │ ├─ status (required) — info | warning | success | error | neutral | change | guard | stale
|
|
794
766
|
│ │ ├─ title (required) — string or UIComponent
|
|
@@ -1017,12 +989,14 @@ Task → Which tag?
|
|
|
1017
989
|
│ ├─ Where a bare token / status value plugs in:
|
|
1018
990
|
│ │ ├─ Library.status(label, tone, ring?) — palette card status chip, a gallery card's dot (`ring` open) · Library.glyph(icon, label, tone?) — its trailing glyph
|
|
1019
991
|
│ │ ├─ Table rowStatus — fn(rowIndex) => StatusToken row tint
|
|
1020
|
-
│ │ ├─ Schematic
|
|
992
|
+
│ │ ├─ e3-ui's Schematic.items { status: { field, cases } } (a dot per case of a variant field) and { tone } (brand|ink|muted|success|warning|danger stroke override)
|
|
1021
993
|
│ │ ├─ e3-ui's Plan.run { status } (stuck ring) · Plan.marker { status } (cell rings, default danger) · Plan.event { tone } — tile tints, all ORTHOGONAL to `state`
|
|
1022
994
|
│ │ ├─ Matrix.marker { status } — corner markers
|
|
1023
995
|
│ │ ├─ <Status value> — dot + word (`ring` an open ring — not live yet); <Banner status> uses the wider notice set (info|warning|success|error|neutral|change|guard|stale)
|
|
1024
996
|
│ │ └─ <Meter tone> / BarStrip item { tone } / SegmentedMeter segment { tone } — bar fills
|
|
1025
997
|
│ ├─ Sentiment (value direction, NOT state): <MetricChip tone> positive|negative|neutral|info · <Numeric sentiment> positive|negative|neutral · <Stat indicator.sentiment> positive|negative|neutral · <Progress tone> brand|pos|neg
|
|
998
|
+
│ ├─ A colour the APP gives, not a state (#1406): ColorTokenType — a theme hue ("brand" | "gray" | "red" | "orange" | "yellow" | "green" | "teal" | "blue" | "cyan" | "purple" | "pink") holding its shade ColorShadeType ("50" … "900"); Style.ColorToken("teal", "500") is variant("teal", variant("500", null)). The typed value every colour accessor returns from here on (e3-ui's Roster.shifts `color`, Option<ColorToken>); a renderer resolves it as the theme's {colors.<hue>.<shade>}, the same in both colour modes, the words on it in the ink the shade takes (the foreground up to 400, the paper's from 500). `brand` and `gray` are the design system's scales, the rest Chakra's stock palettes
|
|
999
|
+
│ ├─ The Elara tokens (#1409): `elara` (the eye's iris and the banner's mark), `elara.ink`, `elara.edge` and `elara.soft` — the ONE accent for what the app's optimiser did, never a state and never an app's colour. Only east-ui-components' shared parts draw them: `OptimiseButton` (the Elara eye, `optimiseToolbarItem` in a builder's toolbar before the history item), `OptimiseBanner` (the `elara` banner tone), `OptimiseLegendItem` (`Elara change`), and the `optimise` recipe's landing over `data-op` / `data-fill` / `data-opn` — e3-ui's five builders list them when `optimise` is bound; an app writes none of them
|
|
1026
1000
|
│ ├─ state ≠ status: the AUDIT LIFECYCLE is orthogonal to the status tint. Blend allocations speak PlannerStateType ("committed"|"added"|"model"|"removed"|"rejected" — committed solid, proposals dashed/ghost/struck, rejected greyed; only proposed items drag); Plan elements speak the richer EventStateType ladder ("estimated"→"proposed"(added|recommended|removed)→"confirmed"→"in-progress"→"actual", plus "rejected"). A status token is the ORTHOGONAL semantic tint layered on top
|
|
1027
1001
|
│ └─ tone vs colorPalette: tone/status/sentiment = the semantic vocabulary above (meaning-bearing, theme-stable); colorPalette = decorative hue theming (Chakra palettes) for buttons/badges/tags where the colour carries no state meaning
|
|
1028
1002
|
│
|
|
@@ -1085,8 +1059,9 @@ pinned read the server refuses because the dataset has moved on (409
|
|
|
1085
1059
|
`dataset_hash_mismatch`) rediscovers the current snapshot instead of failing
|
|
1086
1060
|
its window. How a consumer bridges the move is its own: e3-ui's `<Plan>` and
|
|
1087
1061
|
`<Sheet>` keep the rows they show until the new snapshot's windows land; the
|
|
1088
|
-
Sheet clears a standing key search, and the Plan asks it again there
|
|
1089
|
-
|
|
1062
|
+
Sheet clears a standing key search, and the Plan asks it again there: a
|
|
1063
|
+
match its box shows stays while the new snapshot holds it in place, else the
|
|
1064
|
+
box shows its query again.
|
|
1090
1065
|
|
|
1091
1066
|
## Key Patterns
|
|
1092
1067
|
|
|
@@ -1218,7 +1193,7 @@ Non-UI sub-structures are never child sub-tags.
|
|
|
1218
1193
|
Every size prop is a **plain string** and every renderer parses it the same
|
|
1219
1194
|
way (`parseCssSize`). Four spellings, uniform across data components (`<Table>`,
|
|
1220
1195
|
e3-ui's `<Plan>`, `<Matrix>`,
|
|
1221
|
-
`<Library>`, `<
|
|
1196
|
+
`<Library>`, `<SnapGrid>`) and layout primitives (`<Box>` / `<Flex>` /
|
|
1222
1197
|
`<Stack>` / `<Grid>` / `<Card>`):
|
|
1223
1198
|
|
|
1224
1199
|
| Value | Means |
|
|
@@ -1510,11 +1485,13 @@ tier so they stay subordinate to tags at every density.
|
|
|
1510
1485
|
and written back (through an e3 record's patch, or `onUpdate` / `onApply`)
|
|
1511
1486
|
— the e3-ui skill documents it; for reading, sorting and reviewing a
|
|
1512
1487
|
dataset, `<Table>`.
|
|
1513
|
-
- **Flowchart vs Schematic** —
|
|
1514
|
-
|
|
1515
|
-
|
|
1488
|
+
- **Flowchart vs Schematic** — both are e3-ui's, and the e3-ui skill
|
|
1489
|
+
documents them: `<Schematic>` (#1384, #1386) is a world-coordinate 2D
|
|
1490
|
+
drawing of the app's records (each row carries its x/y; zones, footprints,
|
|
1491
|
+
camera); `<Flowchart>` (#1243)
|
|
1492
|
+
derives its whole layout from lanes + links (no coordinates) for
|
|
1516
1493
|
state-transition / process-flow reading, with decision triggers and
|
|
1517
|
-
evidence on the links
|
|
1494
|
+
evidence on the links.
|
|
1518
1495
|
- **Library vs Deck** — `<Library>` is the draggable palette (DnD source),
|
|
1519
1496
|
or with `variant="gallery"` a library a person browses — large cards, each
|
|
1520
1497
|
with its media, a byline and an action a click follows;
|