nodality 1.3.4 → 1.3.6
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/dist/animator.cjs.js.LICENSE.txt +1 -1
- package/dist/animator.esm.js.LICENSE.txt +1 -1
- package/dist/audionew.cjs.js.LICENSE.txt +1 -1
- package/dist/audionew.esm.js.LICENSE.txt +1 -1
- package/dist/base.cjs.js.LICENSE.txt +1 -1
- package/dist/base.esm.js.LICENSE.txt +1 -1
- package/dist/beta-desktop-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-desktop-bar.esm.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.esm.js.LICENSE.txt +1 -1
- package/dist/bundle.umd.js +1 -1
- package/dist/bundle.umd.js.LICENSE.txt +1 -1
- package/dist/button.cjs.js.LICENSE.txt +1 -1
- package/dist/button.esm.js.LICENSE.txt +1 -1
- package/dist/card-getter.cjs.js.LICENSE.txt +1 -1
- package/dist/card-getter.esm.js.LICENSE.txt +1 -1
- package/dist/center.cjs.js.LICENSE.txt +1 -1
- package/dist/center.esm.js.LICENSE.txt +1 -1
- package/dist/checkbox.cjs.js.LICENSE.txt +1 -1
- package/dist/checkbox.esm.js.LICENSE.txt +1 -1
- package/dist/code.cjs.js.LICENSE.txt +1 -1
- package/dist/code.esm.js.LICENSE.txt +1 -1
- package/dist/container.cjs.js.LICENSE.txt +1 -1
- package/dist/container.esm.js.LICENSE.txt +1 -1
- package/dist/data-list.cjs.js.LICENSE.txt +1 -1
- package/dist/data-list.esm.js.LICENSE.txt +1 -1
- package/dist/designer.cjs.js +1 -1
- package/dist/designer.cjs.js.LICENSE.txt +1 -1
- package/dist/designer.esm.js +1 -1
- package/dist/designer.esm.js.LICENSE.txt +1 -1
- package/dist/element-mapper.cjs.js +1 -1
- package/dist/element-mapper.cjs.js.LICENSE.txt +1 -1
- package/dist/element-mapper.esm.js +1 -1
- package/dist/element-mapper.esm.js.LICENSE.txt +1 -1
- package/dist/finalresult.esm.js +1 -1
- package/dist/finalresult.esm.js.LICENSE.txt +1 -1
- package/dist/flex-card.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-card.esm.js.LICENSE.txt +1 -1
- package/dist/flex-grid.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-grid.esm.js.LICENSE.txt +1 -1
- package/dist/flex-row.cjs.js.LICENSE.txt +1 -1
- package/dist/flex-row.esm.js.LICENSE.txt +1 -1
- package/dist/floating-input.cjs.js.LICENSE.txt +1 -1
- package/dist/floating-input.esm.js.LICENSE.txt +1 -1
- package/dist/free.cjs.js.LICENSE.txt +1 -1
- package/dist/free.esm.js.LICENSE.txt +1 -1
- package/dist/horizontal-scroller.cjs.js.LICENSE.txt +1 -1
- package/dist/horizontal-scroller.esm.js.LICENSE.txt +1 -1
- package/dist/image-picker.cjs.js.LICENSE.txt +1 -1
- package/dist/image-picker.esm.js.LICENSE.txt +1 -1
- package/dist/image.cjs.js.LICENSE.txt +1 -1
- package/dist/image.esm.js.LICENSE.txt +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.LICENSE.txt +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.LICENSE.txt +1 -1
- package/dist/keyframe-animation.cjs.js.LICENSE.txt +1 -1
- package/dist/keyframe-animation.esm.js.LICENSE.txt +1 -1
- package/dist/link-getter.cjs.js.LICENSE.txt +1 -1
- package/dist/link-getter.esm.js.LICENSE.txt +1 -1
- package/dist/link.cjs.js.LICENSE.txt +1 -1
- package/dist/link.esm.js.LICENSE.txt +1 -1
- package/dist/meta-adder.cjs.js.LICENSE.txt +1 -1
- package/dist/meta-adder.esm.js.LICENSE.txt +1 -1
- package/dist/modal-2025.cjs.js.LICENSE.txt +1 -1
- package/dist/modal-2025.esm.js.LICENSE.txt +1 -1
- package/dist/multiswitcher.cjs.js.LICENSE.txt +1 -1
- package/dist/multiswitcher.esm.js.LICENSE.txt +1 -1
- package/dist/new-nav-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/new-nav-bar.esm.js.LICENSE.txt +1 -1
- package/dist/picker.cjs.js.LICENSE.txt +1 -1
- package/dist/picker.esm.js.LICENSE.txt +1 -1
- package/dist/progress.cjs.js.LICENSE.txt +1 -1
- package/dist/progress.esm.js.LICENSE.txt +1 -1
- package/dist/radio.cjs.js.LICENSE.txt +1 -1
- package/dist/radio.esm.js.LICENSE.txt +1 -1
- package/dist/range.cjs.js.LICENSE.txt +1 -1
- package/dist/range.esm.js.LICENSE.txt +1 -1
- package/dist/scroll-video.cjs.js.LICENSE.txt +1 -1
- package/dist/scroll-video.esm.js.LICENSE.txt +1 -1
- package/dist/side-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/side-bar.esm.js.LICENSE.txt +1 -1
- package/dist/side-nav-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/side-nav-bar.esm.js.LICENSE.txt +1 -1
- package/dist/simple-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/simple-bar.esm.js.LICENSE.txt +1 -1
- package/dist/slider-2025.cjs.js.LICENSE.txt +1 -1
- package/dist/slider-2025.esm.js.LICENSE.txt +1 -1
- package/dist/spacer.cjs.js.LICENSE.txt +1 -1
- package/dist/spacer.esm.js.LICENSE.txt +1 -1
- package/dist/stack.cjs.js.LICENSE.txt +1 -1
- package/dist/stack.esm.js.LICENSE.txt +1 -1
- package/dist/stacker.cjs.js.LICENSE.txt +1 -1
- package/dist/stacker.esm.js.LICENSE.txt +1 -1
- package/dist/table.cjs.js.LICENSE.txt +1 -1
- package/dist/table.esm.js.LICENSE.txt +1 -1
- package/dist/text-field.cjs.js.LICENSE.txt +1 -1
- package/dist/text-field.esm.js.LICENSE.txt +1 -1
- package/dist/text.cjs.js.LICENSE.txt +1 -1
- package/dist/text.esm.js.LICENSE.txt +1 -1
- package/dist/theme.cjs.js.LICENSE.txt +1 -1
- package/dist/theme.esm.js.LICENSE.txt +1 -1
- package/dist/transform-anim.cjs.js.LICENSE.txt +1 -1
- package/dist/transform-anim.esm.js.LICENSE.txt +1 -1
- package/dist/ulist.cjs.js +1 -1
- package/dist/ulist.cjs.js.LICENSE.txt +1 -1
- package/dist/ulist.esm.js +1 -1
- package/dist/ulist.esm.js.LICENSE.txt +1 -1
- package/dist/video.cjs.js.LICENSE.txt +1 -1
- package/dist/video.esm.js.LICENSE.txt +1 -1
- package/dist/wrap.cjs.js.LICENSE.txt +1 -1
- package/dist/wrap.esm.js.LICENSE.txt +1 -1
- package/dist/zoom-card.cjs.js.LICENSE.txt +1 -1
- package/dist/zoom-card.esm.js.LICENSE.txt +1 -1
- package/layout/animator.js +1 -1
- package/layout/audio.js +1 -1
- package/layout/audionew.js +1 -1
- package/layout/base.js +1 -1
- package/layout/beta-desktop-bar.js +1 -1
- package/layout/beta-mobile-bar.js +1 -1
- package/layout/button.js +1 -1
- package/layout/center.js +1 -1
- package/layout/checkbox.js +1 -1
- package/layout/circle.js +1 -1
- package/layout/code.js +1 -1
- package/layout/container.js +1 -1
- package/layout/dropdown-2025.js +1 -1
- package/layout/flex-card.js +1 -1
- package/layout/flex-grid.js +1 -1
- package/layout/flex-row.js +1 -1
- package/layout/form-components/custom.js +1 -1
- package/layout/form-components/data-list.js +1 -1
- package/layout/form-components/floating-input.js +1 -1
- package/layout/form-components/form-all.js +1 -1
- package/layout/form-components/form.js +1 -1
- package/layout/form-components/image-picker.js +1 -1
- package/layout/form-components/picker.js +1 -1
- package/layout/form-components/radio.js +1 -1
- package/layout/form-components/radiogroup.js +1 -1
- package/layout/form-components/range.js +1 -1
- package/layout/free.js +1 -1
- package/layout/grid-switcher.js +1 -1
- package/layout/grid.js +1 -1
- package/layout/horizontal-scroller.js +1 -1
- package/layout/image.js +1 -1
- package/layout/index.js +1 -1
- package/layout/link.js +1 -1
- package/layout/list.js +1 -1
- package/layout/meta-adder.js +1 -1
- package/layout/modal-2025.js +1 -1
- package/layout/morph.js +1 -1
- package/layout/multiswitcher.js +1 -1
- package/layout/nav-bar.js +1 -1
- package/layout/nav-factor/custom-div.js +1 -1
- package/layout/new-nav-bar.js +1 -1
- package/layout/polygon.js +1 -1
- package/layout/prerender-site.js +1 -1
- package/layout/prerender.js +1 -1
- package/layout/progress.js +1 -1
- package/layout/row.js +1 -1
- package/layout/scroll-video.js +1 -1
- package/layout/side-bar.js +1 -1
- package/layout/side-nav-bar.js +1 -1
- package/layout/simple-bar.js +1 -1
- package/layout/slider-2025.js +1 -1
- package/layout/spacer.js +1 -1
- package/layout/stack.js +1 -1
- package/layout/svg.js +1 -1
- package/layout/switcher.js +1 -1
- package/layout/table.js +1 -1
- package/layout/text-field.js +1 -1
- package/layout/text.js +1 -1
- package/layout/ulist.js +16 -1
- package/layout/video.js +1 -1
- package/layout/wrap.js +1 -1
- package/layout/zoom-card.js +1 -1
- package/lib/agent-surface.js +1 -1
- package/lib/card-getter.js +1 -1
- package/lib/check-page.js +1 -1
- package/lib/codegen.js +1 -1
- package/lib/data.js +1 -1
- package/lib/designer.js +1 -1
- package/lib/element-mapper.js +98 -5
- package/lib/element-params.generated.js +1 -1
- package/lib/keyframe-animation.js +1 -1
- package/lib/link-getter.js +1 -1
- package/lib/morph-node.js +1 -1
- package/lib/normalize-spec.js +1 -1
- package/lib/parse-html.js +1 -1
- package/lib/raster-inspect.js +1 -1
- package/lib/raster-ops.js +1 -1
- package/lib/raster-presets.js +1 -1
- package/lib/resolve-refs.js +1 -1
- package/lib/scroll-video.js +1 -1
- package/lib/seo.js +1 -1
- package/lib/stacker.js +1 -1
- package/lib/suggest.js +1 -1
- package/lib/theme.js +1 -1
- package/lib/transform-anim.js +1 -1
- package/lib/transition.js +1 -1
- package/lib/validate-nodes.js +83 -2
- package/lib/webmcp-adapter.js +1 -1
- package/package.json +1 -1
- package/schema.json +609 -3264
- package/scripts/generate-schema.mjs +102 -12
- package/skills/nodality/SKILL.md +20 -2
package/layout/prerender-site.js
CHANGED
package/layout/prerender.js
CHANGED
package/layout/progress.js
CHANGED
package/layout/row.js
CHANGED
package/layout/scroll-video.js
CHANGED
package/layout/side-bar.js
CHANGED
package/layout/side-nav-bar.js
CHANGED
package/layout/simple-bar.js
CHANGED
package/layout/slider-2025.js
CHANGED
package/layout/spacer.js
CHANGED
package/layout/stack.js
CHANGED
package/layout/svg.js
CHANGED
package/layout/switcher.js
CHANGED
package/layout/table.js
CHANGED
package/layout/text-field.js
CHANGED
package/layout/text.js
CHANGED
package/layout/ulist.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.6
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -176,6 +176,21 @@ toCode(indent = 0) {
|
|
|
176
176
|
const pad = " ".repeat(indent);
|
|
177
177
|
let code = `${pad}new UList()`;
|
|
178
178
|
|
|
179
|
+
// The options, so the emitted source reproduces this instance. Without
|
|
180
|
+
// this the generator wrote `new UList()` and dropped every option that
|
|
181
|
+
// had been set on it -- and since Des.set() EXECUTES the emitted
|
|
182
|
+
// source, the page rendered from code that no longer described the
|
|
183
|
+
// list. The mapper was forwarding correctly the whole time; the loss
|
|
184
|
+
// was here, at the boundary the artefact is supposed to cross.
|
|
185
|
+
const opts = Object.fromEntries(
|
|
186
|
+
Object.entries(this.options || {}).filter(([, v]) =>
|
|
187
|
+
v !== undefined && v !== null && v !== ""),
|
|
188
|
+
);
|
|
189
|
+
if (Object.keys(opts).length) {
|
|
190
|
+
code += `\n${pad} .set(${JSON.stringify(opts, null, 2)
|
|
191
|
+
.split("\n").join(`\n${pad} `)})`;
|
|
192
|
+
}
|
|
193
|
+
|
|
179
194
|
if (this.items && this.items.length) {
|
|
180
195
|
code += `\n${pad} .setItems([\n` +
|
|
181
196
|
this.items.map(c =>
|
package/layout/video.js
CHANGED
package/layout/wrap.js
CHANGED
package/layout/zoom-card.js
CHANGED
package/lib/agent-surface.js
CHANGED
package/lib/card-getter.js
CHANGED
package/lib/check-page.js
CHANGED
package/lib/codegen.js
CHANGED
package/lib/data.js
CHANGED
package/lib/designer.js
CHANGED
package/lib/element-mapper.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.6
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -125,9 +125,35 @@ const navEntry = (i) =>
|
|
|
125
125
|
// The failure this guards is silent: a composite handed content in the slot it
|
|
126
126
|
// does not read emits its placeholder template, so the page renders, nothing
|
|
127
127
|
// throws, and the content is simply gone.
|
|
128
|
+
// Kept byte-equal to `CONTENT_SLOT` in lib/validate-nodes.js, which cannot be
|
|
129
|
+
// imported here: this file pulls in every layout component and therefore a DOM,
|
|
130
|
+
// and the validator has to run in a bare Node process. Two copies is the price
|
|
131
|
+
// of that, and they had already drifted -- `stack` was declared here and not
|
|
132
|
+
// there -- so a unit test now pins them equal rather than trusting the comment.
|
|
133
|
+
//
|
|
134
|
+
// The per-type descriptions below are read by scripts/generate-schema.mjs. They
|
|
135
|
+
// live here because the mapper is what decides how a slot is read; before they
|
|
136
|
+
// existed the generator keyed descriptions by parameter NAME across every file
|
|
137
|
+
// it scanned, first writer winning, so the picker's `items` documentation was
|
|
138
|
+
// served for `cards`, `nav`, `sideNav`, `table` and `ulist` as well.
|
|
139
|
+
// Names whose meaning is decided by the element type, not by whichever file
|
|
140
|
+
// happened to document them first. See the SCOPED note in generate-schema.mjs.
|
|
141
|
+
//@scoped items
|
|
142
|
+
//@scoped type
|
|
143
|
+
//@scoped id
|
|
144
|
+
//@ type: The element type, which selects the mapper that builds it -- "h1", "cards", "form", and so on.
|
|
145
|
+
//@ id: This element's DOM id. Also what a design node's `target` names, and what `parse` recovers an element by.
|
|
146
|
+
//@ cards.items: The cards. Each entry is either a data object -- { title, link, img } -- or an ARRAY of element specs, which renders as one card containing them.
|
|
147
|
+
//@ nav.items: The links, each `{ title, link }` -- or a plain string.
|
|
148
|
+
//@ sideNav.items: The links, each `{ title, link }` -- or a plain string.
|
|
149
|
+
//@ table.items: The rows. Each entry is a `{ title, link }` or `{ text, url }` data object, a `[value, text]` pair, or an element spec.
|
|
150
|
+
//@ ulist.items: The list entries, each an element spec -- `{ type, ... }` -- or a plain string.
|
|
151
|
+
//@ dropdown.items: The choices, each a `[value, text]` pair -- or a plain string used as both.
|
|
152
|
+
//@ radio.items: The choices, each a plain string.
|
|
128
153
|
const CONTENT_SLOT = Object.freeze({
|
|
129
154
|
cards: "items", table: "items", ulist: "items",
|
|
130
155
|
nav: "items", sideNav: "items",
|
|
156
|
+
dropdown: "items", picker: "items", radio: "items",
|
|
131
157
|
row: "children", form: "children", stack: "children", wrap: "children",
|
|
132
158
|
});
|
|
133
159
|
|
|
@@ -295,6 +321,17 @@ class ElementMapper { // 22:09:58 04/11/2024
|
|
|
295
321
|
// return new Text("Hello").set({size: "S3"});
|
|
296
322
|
|
|
297
323
|
return new FlexRow().set({
|
|
324
|
+
// Everything the author declared. Without it a row's
|
|
325
|
+
// options were the node lookups alone, every one of
|
|
326
|
+
// them `undefined` when no node targets this element,
|
|
327
|
+
// so FlexRow.toCode() -- which correctly strips
|
|
328
|
+
// undefined -- emitted `.set({})` and the emitted
|
|
329
|
+
// source carried nothing. The empty object was the
|
|
330
|
+
// symptom; this is the cause.
|
|
331
|
+
...this.elOpts(el, [
|
|
332
|
+
"stroke", "gradient", "animation", "shadow", "span",
|
|
333
|
+
"backgroundOp", "marginOp", "transform", "filtera", "raster",
|
|
334
|
+
]),
|
|
298
335
|
stroke: this.filtero("blast", el.id, obj.customOptions),
|
|
299
336
|
gradient: this.filtero("gradient", el.id, obj.customOptions),
|
|
300
337
|
animation: this.filtero("animation", el.id, obj.customOptions),
|
|
@@ -1624,6 +1661,27 @@ let elo = new Polygon({ id: "hex" })
|
|
|
1624
1661
|
|
|
1625
1662
|
return new Text(el.text || el.value)
|
|
1626
1663
|
.set({
|
|
1664
|
+
// Everything the author declared, first, so that every option
|
|
1665
|
+
// Text reads is reachable from a spec. Hand-listing the
|
|
1666
|
+
// forwards left 36 of this type's 56 documented options
|
|
1667
|
+
// settable only on the emitted component -- `background`,
|
|
1668
|
+
// `align`, `width`, `opacity`, `radius` among them -- so a
|
|
1669
|
+
// page could declare one, validate clean, and render without
|
|
1670
|
+
// it. The note on `tag` below was one instance of that gap,
|
|
1671
|
+
// closed one option at a time; this closes the rest.
|
|
1672
|
+
//
|
|
1673
|
+
// The spread comes FIRST and every assignment below overrides
|
|
1674
|
+
// it, so nothing the mapper computes can be displaced by a
|
|
1675
|
+
// field of the same name. The drop list is the keys this
|
|
1676
|
+
// method derives from the NODE array: they belong to N, they
|
|
1677
|
+
// are resolved by `filtero` below, and letting E supply them
|
|
1678
|
+
// would both cross the two halves of the format and let an
|
|
1679
|
+
// empty lookup overwrite an author's value with `undefined`.
|
|
1680
|
+
...this.elOpts(el, [
|
|
1681
|
+
"value", // constructor argument, above
|
|
1682
|
+
"stroke", "gradient", "animation", "shadow", "span",
|
|
1683
|
+
"backgroundOp", "marginOp", "transform", "filtera", "raster",
|
|
1684
|
+
]),
|
|
1627
1685
|
id: el.id,
|
|
1628
1686
|
class: el.class,
|
|
1629
1687
|
color: el.color,
|
|
@@ -1662,7 +1720,14 @@ let elo = new Polygon({ id: "hex" })
|
|
|
1662
1720
|
|
|
1663
1721
|
return new Image(el.url)
|
|
1664
1722
|
.set({
|
|
1665
|
-
|
|
1723
|
+
// elOpts rather than a raw `...el`: the raw spread put `type`,
|
|
1724
|
+
// `children` and `items` into the component's OPTIONS, where
|
|
1725
|
+
// they mean nothing, and -- now that toCode() serialises
|
|
1726
|
+
// options -- writes them into the emitted source as well. The
|
|
1727
|
+
// artefact is meant to be code a developer reads and owns, so
|
|
1728
|
+
// it should not carry the specification's own bookkeeping.
|
|
1729
|
+
...this.elOpts(el),
|
|
1730
|
+
url: el.url, // skipped by elOpts; the raw spread carried it
|
|
1666
1731
|
isFull:el.isFull,
|
|
1667
1732
|
// index: obj.i + "",
|
|
1668
1733
|
animation: this.filtero("animation", el.id, obj.customOptions),
|
|
@@ -1857,12 +1922,28 @@ const blast = this.filtero("blast", el.id, obj.customOptions);
|
|
|
1857
1922
|
|
|
1858
1923
|
|
|
1859
1924
|
|
|
1925
|
+
// The author's own options, serialised into the emitted source. This
|
|
1926
|
+
// mapper returns SOURCE rather than an instance, so there is nothing to
|
|
1927
|
+
// spread onto -- the equivalent of `...elOpts(el)` here is to write the
|
|
1928
|
+
// entries into the `.set({...})` literal being generated.
|
|
1929
|
+
//
|
|
1930
|
+
// They come AFTER the four template defaults and BEFORE the node ops, and
|
|
1931
|
+
// that order is the contract: a default is a starting point an author may
|
|
1932
|
+
// overrule, so `{type:"cards", gap:"2rem"}` wins over `gap:"1rem"`; a node
|
|
1933
|
+
// is the N half of the format addressing this element by id, so it wins
|
|
1934
|
+
// over E. Before this, every one of these keys was accepted by the
|
|
1935
|
+
// validator, advertised by the schema, and silently dropped on the floor.
|
|
1936
|
+
const authored = Object.entries(this.elOpts(obj.el, [
|
|
1937
|
+
"backgroundCard", // consumed above, to pick the template
|
|
1938
|
+
"gradient", "animation", "shadow", "marginOp", "filtera", "blast", "raster",
|
|
1939
|
+
])).map(([k, v]) => `,\n ${JSON.stringify(k)}: ${JSON.stringify(v)}`).join("");
|
|
1940
|
+
|
|
1860
1941
|
return ` new FlexGrid()
|
|
1861
1942
|
.set({
|
|
1862
1943
|
colat: "700px",
|
|
1863
1944
|
wrap: true,
|
|
1864
1945
|
align: "center",
|
|
1865
|
-
gap: "1rem"
|
|
1946
|
+
gap: "1rem"${authored}
|
|
1866
1947
|
${
|
|
1867
1948
|
gradient !== undefined ? `,\n gradient: ${JSON.stringify(gradient)}` : ""
|
|
1868
1949
|
}${
|
|
@@ -2118,7 +2199,13 @@ static form(obj){
|
|
|
2118
2199
|
// slice on a word. A caller-supplied `size` still passes through.
|
|
2119
2200
|
const wrapper = new Wrapper(kind)
|
|
2120
2201
|
.set({
|
|
2121
|
-
|
|
2202
|
+
// elOpts rather than a raw `...el`: the raw spread put `type`,
|
|
2203
|
+
// `children` and `items` into the component's OPTIONS, where
|
|
2204
|
+
// they mean nothing, and -- now that toCode() serialises
|
|
2205
|
+
// options -- writes them into the emitted source as well. The
|
|
2206
|
+
// artefact is meant to be code a developer reads and owns, so
|
|
2207
|
+
// it should not carry the specification's own bookkeeping.
|
|
2208
|
+
...this.elOpts(el),
|
|
2122
2209
|
font: el.font ?? "Arial",
|
|
2123
2210
|
stroke: this.filtero("blast", el.id, obj.customOptions),
|
|
2124
2211
|
gradient: this.filtero("gradient", el.id, obj.customOptions),
|
|
@@ -2158,7 +2245,13 @@ static form(obj){
|
|
|
2158
2245
|
// output of getElType on a non-heading type.
|
|
2159
2246
|
const list = new UList(kind)
|
|
2160
2247
|
.set({
|
|
2161
|
-
|
|
2248
|
+
// elOpts rather than a raw `...el`: the raw spread put `type`,
|
|
2249
|
+
// `children` and `items` into the component's OPTIONS, where
|
|
2250
|
+
// they mean nothing, and -- now that toCode() serialises
|
|
2251
|
+
// options -- writes them into the emitted source as well. The
|
|
2252
|
+
// artefact is meant to be code a developer reads and owns, so
|
|
2253
|
+
// it should not carry the specification's own bookkeeping.
|
|
2254
|
+
...this.elOpts(el),
|
|
2162
2255
|
font: el.font ?? "Arial",
|
|
2163
2256
|
stroke: this.filtero("blast", el.id, obj.customOptions),
|
|
2164
2257
|
gradient: this.filtero("gradient", el.id, obj.customOptions),
|
package/lib/link-getter.js
CHANGED
package/lib/morph-node.js
CHANGED
package/lib/normalize-spec.js
CHANGED
package/lib/parse-html.js
CHANGED
package/lib/raster-inspect.js
CHANGED
package/lib/raster-ops.js
CHANGED
package/lib/raster-presets.js
CHANGED
package/lib/resolve-refs.js
CHANGED
package/lib/scroll-video.js
CHANGED
package/lib/seo.js
CHANGED
package/lib/stacker.js
CHANGED
package/lib/suggest.js
CHANGED
package/lib/theme.js
CHANGED
package/lib/transform-anim.js
CHANGED
package/lib/transition.js
CHANGED
package/lib/validate-nodes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.6
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -158,9 +158,18 @@ const familyOf = (node) => {
|
|
|
158
158
|
// be demonstrated is absent, and absent means silent: reporting a slot that
|
|
159
159
|
// might work would stop `preview` rendering a page that renders, which 1.2.7
|
|
160
160
|
// established is the costlier direction to be wrong in.
|
|
161
|
+
//
|
|
162
|
+
// Four types had that evidence and were missing anyway. `stack` was declared in
|
|
163
|
+
// the mapper's copy of this table and not in this one, so the two had already
|
|
164
|
+
// drifted -- which is what the comment on SPEC_ITEMS below warns about, one
|
|
165
|
+
// table over. `dropdown`, `picker` and `radio` read `items` as choices and were
|
|
166
|
+
// in neither. The omission was invisible to the drift test because the test
|
|
167
|
+
// iterated THIS map, so a type absent from it was a type it never checked; the
|
|
168
|
+
// test now iterates the element registry instead.
|
|
161
169
|
export const CONTENT_SLOT = {
|
|
162
170
|
cards: "items", nav: "items", sideNav: "items", table: "items", ulist: "items",
|
|
163
|
-
|
|
171
|
+
dropdown: "items", picker: "items", radio: "items",
|
|
172
|
+
row: "children", form: "children", stack: "children", wrap: "children",
|
|
164
173
|
};
|
|
165
174
|
|
|
166
175
|
// Of the types that read `items`, which read the entries as ELEMENT SPECS
|
|
@@ -172,6 +181,41 @@ export const CONTENT_SLOT = {
|
|
|
172
181
|
// drifted before 1.2.8.
|
|
173
182
|
export const SPEC_ITEMS = new Set(["ulist"]);
|
|
174
183
|
|
|
184
|
+
// Of the types that read `items` but NOT as flat element specs, what an entry
|
|
185
|
+
// may be. Measured by rendering every shape against every type rather than read
|
|
186
|
+
// off the mappers, because the mappers disagree: `table` takes an element spec,
|
|
187
|
+
// `cards` takes one only inside a nested array, and `nav` takes neither.
|
|
188
|
+
//
|
|
189
|
+
// This guards the expensive kind of failure rather than the cheap one. An entry
|
|
190
|
+
// carrying `type` IS a well-formed element spec, so nothing here is a typo and
|
|
191
|
+
// near-miss detection cannot reach it: `cards` renders an empty card, `nav`
|
|
192
|
+
// drops the entry, both pages render, and the content is simply gone. That is
|
|
193
|
+
// the "validates and is wrong" case, and it is the one shape a generator
|
|
194
|
+
// reaches for first, because every other slot in the format takes element
|
|
195
|
+
// specs.
|
|
196
|
+
//
|
|
197
|
+
// `keys` are the fields that prove an entry was MEANT as data. A report fires
|
|
198
|
+
// only when none of them is present -- i.e. only when the entry cannot render
|
|
199
|
+
// anything at all -- because a false report does not annotate the page, it
|
|
200
|
+
// prevents it.
|
|
201
|
+
export const ITEM_SHAPE = {
|
|
202
|
+
cards: {
|
|
203
|
+
nest: true, keys: ["title", "link", "img"],
|
|
204
|
+
valid: 'a data object -- { title, link, img } -- or an ARRAY of element specs, which renders as one card containing them',
|
|
205
|
+
},
|
|
206
|
+
nav: {
|
|
207
|
+
nest: false, keys: ["title", "link"],
|
|
208
|
+
valid: '{ title, link }, or a plain string',
|
|
209
|
+
},
|
|
210
|
+
sideNav: {
|
|
211
|
+
nest: false, keys: ["title", "link"],
|
|
212
|
+
valid: '{ title, link }, or a plain string',
|
|
213
|
+
},
|
|
214
|
+
dropdown: { nest: false, keys: [], valid: 'a plain string, or a [value, text] pair' },
|
|
215
|
+
picker: { nest: false, keys: [], valid: 'a plain string, or a [value, text] pair' },
|
|
216
|
+
radio: { nest: false, keys: [], valid: 'a plain string' },
|
|
217
|
+
};
|
|
218
|
+
|
|
175
219
|
export function validateNodes(nodes, elements, defs) {
|
|
176
220
|
const errors = [];
|
|
177
221
|
const push = (code, path, got, suggestions, valid) => {
|
|
@@ -433,6 +477,43 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
433
477
|
}
|
|
434
478
|
});
|
|
435
479
|
}
|
|
480
|
+
|
|
481
|
+
// Phase S8: an entry in the wrong SHAPE for a type that reads
|
|
482
|
+
// `items` as something other than element specs. The sibling
|
|
483
|
+
// check above walks entries that ARE specs; this one reports
|
|
484
|
+
// entries that are specs where a spec renders nothing.
|
|
485
|
+
//
|
|
486
|
+
// Fires only when the entry carries none of the fields the type
|
|
487
|
+
// reads, so an entry meant as data is never reported, and a
|
|
488
|
+
// `$ref` -- resolved later -- is left alone. The suggestion
|
|
489
|
+
// hands back the replacement rather than describing it, which
|
|
490
|
+
// 8.7.4 measured as the difference between a report a model can
|
|
491
|
+
// act on and one it cannot.
|
|
492
|
+
const shape = ITEM_SHAPE[el.type];
|
|
493
|
+
if (shape && Array.isArray(el.items)) {
|
|
494
|
+
el.items.forEach((entry, k) => {
|
|
495
|
+
const at = `${p}.items[${k}]`;
|
|
496
|
+
if (entry === null || typeof entry !== "object") return;
|
|
497
|
+
if (Array.isArray(entry)) return; // nested spec, or a [value, text] pair
|
|
498
|
+
if (isRef(entry)) return;
|
|
499
|
+
if (shape.keys.some((key) => entry[key] !== undefined)) return;
|
|
500
|
+
|
|
501
|
+
const text = ["text", "title", "label"]
|
|
502
|
+
.map((key) => entry[key]).find((v) => typeof v === "string");
|
|
503
|
+
const suggestions = [];
|
|
504
|
+
if (shape.nest && typeof entry.type === "string") {
|
|
505
|
+
suggestions.push(JSON.stringify([entry]));
|
|
506
|
+
} else if (shape.keys.length) {
|
|
507
|
+
suggestions.push(JSON.stringify({
|
|
508
|
+
title: text === undefined ? "..." : text,
|
|
509
|
+
link: entry.url || entry.link || "#",
|
|
510
|
+
}));
|
|
511
|
+
} else if (text !== undefined) {
|
|
512
|
+
suggestions.push(JSON.stringify(text));
|
|
513
|
+
}
|
|
514
|
+
push("WRONG_ITEM_SHAPE", at, entry, suggestions, [shape.valid]);
|
|
515
|
+
});
|
|
516
|
+
}
|
|
436
517
|
});
|
|
437
518
|
};
|
|
438
519
|
walkEls(toWalk, "elements");
|