nodality 1.2.9 → 1.2.11
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/bin/mcp-server.mjs +86 -0
- package/bin/nodality.js +12 -0
- package/bin/schema-cli.mjs +72 -0
- 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 -28
- 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 -28
- package/dist/designer.esm.js +1 -1
- package/dist/designer.esm.js.LICENSE.txt +1 -28
- 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 -28
- 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 -28
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.LICENSE.txt +1 -28
- 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.LICENSE.txt +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 +1 -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/codegen.js +1 -1
- package/lib/data.js +1 -1
- package/lib/designer.js +21 -1
- package/lib/element-mapper.js +173 -16
- package/lib/element-params.generated.js +11 -0
- package/lib/keyframe-animation.js +1 -1
- package/lib/link-getter.js +1 -1
- package/lib/morph-node.js +1 -1
- package/lib/parse-html.js +151 -0
- package/lib/raster-inspect.js +1 -1
- package/lib/raster-ops.js +1 -1
- package/lib/raster-presets.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 +112 -2
- package/lib/webmcp-adapter.js +1 -1
- package/package.json +5 -2
- package/schema.json +7953 -0
- package/scripts/generate-schema.mjs +251 -0
- package/skills/nodality/SKILL.md +71 -1
package/lib/validate-nodes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.2.
|
|
2
|
+
* nodality v1.2.11
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -31,7 +31,8 @@ import {
|
|
|
31
31
|
} from "./raster-ops.js";
|
|
32
32
|
import { presetNames, presetInfo } from "./raster-presets.js";
|
|
33
33
|
import { ELEMENT_TYPES } from "./element-mapper.js";
|
|
34
|
-
import {
|
|
34
|
+
import { ELEMENT_PARAM_NAMES } from "./element-params.generated.js";
|
|
35
|
+
import { didYouMean, suggest, levenshtein } from "./suggest.js";
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Every key a morph node reads. Anything else on the node is ignored by
|
|
@@ -140,6 +141,26 @@ const familyOf = (node) => {
|
|
|
140
141
|
* which is the other half of the "silent no-op" problem: a correctly
|
|
141
142
|
* spelled op aimed at an element that is not there does nothing at all.
|
|
142
143
|
*/
|
|
144
|
+
// Which key actually carries a composite's content.
|
|
145
|
+
//
|
|
146
|
+
// GROUND TRUTH, DERIVED BY RENDERING, not by reading the source. A static scan
|
|
147
|
+
// got this wrong: `mapRow` appears to read `items` because a helper it calls
|
|
148
|
+
// does, and that helper's `items` belongs to a different element entirely. So
|
|
149
|
+
// each type was rendered with a distinctive payload in `items` and again in
|
|
150
|
+
// `children`, and only the slot the output actually followed is recorded here.
|
|
151
|
+
// `__tests__/unit/content-slots.test.mjs` re-derives it the same way and fails
|
|
152
|
+
// if this table drifts, which is the arrangement Stage 2 established for the
|
|
153
|
+
// schema.
|
|
154
|
+
//
|
|
155
|
+
// ONLY types with positive evidence appear. A type whose content slot could not
|
|
156
|
+
// be demonstrated is absent, and absent means silent: reporting a slot that
|
|
157
|
+
// might work would stop `preview` rendering a page that renders, which 1.2.7
|
|
158
|
+
// established is the costlier direction to be wrong in.
|
|
159
|
+
export const CONTENT_SLOT = {
|
|
160
|
+
cards: "items", nav: "items", sideNav: "items", table: "items", ulist: "items",
|
|
161
|
+
row: "children", form: "children", wrap: "children",
|
|
162
|
+
};
|
|
163
|
+
|
|
143
164
|
export function validateNodes(nodes, elements) {
|
|
144
165
|
const errors = [];
|
|
145
166
|
const push = (code, path, got, suggestions, valid) => {
|
|
@@ -204,6 +225,17 @@ export function validateNodes(nodes, elements) {
|
|
|
204
225
|
list.forEach((el, i) => {
|
|
205
226
|
const p = `${at}[${i}]`;
|
|
206
227
|
if (!el || typeof el !== "object" || Array.isArray(el)) {
|
|
228
|
+
// A bare string here is almost always the pre-S1 way of
|
|
229
|
+
// gesturing at a composite's shape —
|
|
230
|
+
// `children: ["image","text","link"]`. The mapper accepts
|
|
231
|
+
// it and ignores it, which is the silent no-op this
|
|
232
|
+
// validator exists to remove, so name the working form
|
|
233
|
+
// rather than emitting a generic shape error.
|
|
234
|
+
if (typeof el === "string") {
|
|
235
|
+
push("LEGACY_CHILD_STRING", p, el, [`items: [[{ type: "${el}", ... }]]`],
|
|
236
|
+
["an element descriptor, or a composite's `items` for card content"]);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
207
239
|
push("BAD_ELEMENT", p, el, [], ["{ type: \"<element type>\", ... }"]);
|
|
208
240
|
return;
|
|
209
241
|
}
|
|
@@ -214,7 +246,85 @@ export function validateNodes(nodes, elements) {
|
|
|
214
246
|
push("UNKNOWN_ELEMENT_TYPE", `${p}.type`, el.type,
|
|
215
247
|
suggest(el.type, ELEMENT_TYPES), ELEMENT_TYPES);
|
|
216
248
|
}
|
|
249
|
+
// Content declared in the slot this type does not read.
|
|
250
|
+
// `{type:"table", children:[...]}` validated clean and then
|
|
251
|
+
// rendered its placeholders — the exact silent no-op Stage 1
|
|
252
|
+
// removed for `cards`, surviving one slot over. It is not a
|
|
253
|
+
// typo, so near-miss detection cannot reach it.
|
|
254
|
+
const wants = CONTENT_SLOT[el.type];
|
|
255
|
+
if (wants) {
|
|
256
|
+
const other = wants === "items" ? "children" : "items";
|
|
257
|
+
const filled = (v) => Array.isArray(v) ? v.length > 0 : v != null;
|
|
258
|
+
// All-strings is the pre-S1 gesture and already gets its own
|
|
259
|
+
// diagnostic from the walk below; do not report it twice.
|
|
260
|
+
const allStrings = Array.isArray(el[other])
|
|
261
|
+
&& el[other].every((x) => typeof x === "string");
|
|
262
|
+
if (filled(el[other]) && !filled(el[wants]) && !allStrings) {
|
|
263
|
+
push("WRONG_CONTENT_SLOT", `${p}.${other}`, other, [wants],
|
|
264
|
+
[`"${el.type}" carries its content in \`${wants}\``]);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
217
268
|
walkEls(el.children, `${p}.children`);
|
|
269
|
+
|
|
270
|
+
// Phase S3: a misspelled element parameter. `{type:"cards",
|
|
271
|
+
// itms:[…]}` used to validate clean and then render a grid
|
|
272
|
+
// with no items — the silent no-op this validator exists to
|
|
273
|
+
// remove, one level up from the node vocabulary it already
|
|
274
|
+
// checked.
|
|
275
|
+
//
|
|
276
|
+
// Reported ONLY for a near miss of a known parameter name.
|
|
277
|
+
// An unrecognised name with no close match is left alone on
|
|
278
|
+
// purpose: several mappers spread the whole element into
|
|
279
|
+
// their component, so they accept names no static scan can
|
|
280
|
+
// enumerate, and rejecting one would stop `preview` from
|
|
281
|
+
// rendering a page that works. Detection here, full per-type
|
|
282
|
+
// vocabulary from `npx nodality schema <type>`.
|
|
283
|
+
if (typeof el.type === "string") {
|
|
284
|
+
for (const key in el) {
|
|
285
|
+
if (ELEMENT_PARAM_NAMES.includes(key)) continue;
|
|
286
|
+
// Ranked, closest first, capped at three. `suggest`
|
|
287
|
+
// returns everything within distance 2 in list order,
|
|
288
|
+
// so `txt` offered ["at","text","tint","top","x"] with
|
|
289
|
+
// the answer second — five guesses is not a one-turn
|
|
290
|
+
// repair, which is Stage 3's acceptance test.
|
|
291
|
+
// Ranked by a transposition-aware score. Plain
|
|
292
|
+
// Levenshtein calls `ulr`→`url` distance 2, the same
|
|
293
|
+
// as `ulr`→`mar`, so the alphabetical tiebreak put
|
|
294
|
+
// the wrong word first. A swapped pair of letters is
|
|
295
|
+
// the commonest typo there is, so a candidate built
|
|
296
|
+
// from exactly the same characters ranks ahead of an
|
|
297
|
+
// equal-distance one that is not.
|
|
298
|
+
const sorted = (w) => [...w].sort().join("");
|
|
299
|
+
const score = (c) => levenshtein(key, c)
|
|
300
|
+
- (sorted(c) === sorted(key) ? 1.5 : 0)
|
|
301
|
+
- (c[0] === key[0] ? 0.25 : 0);
|
|
302
|
+
const near = suggest(key, ELEMENT_PARAM_NAMES)
|
|
303
|
+
.sort((a, b) => score(a) - score(b)
|
|
304
|
+
|| a.length - b.length
|
|
305
|
+
|| a.localeCompare(b))
|
|
306
|
+
.slice(0, 3);
|
|
307
|
+
if (!near.length) continue;
|
|
308
|
+
push("UNKNOWN_ELEMENT_PARAM", `${p}.${key}`, key, near,
|
|
309
|
+
[`run \`npx nodality schema ${el.type}\` for this type's parameters`]);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Phase S1: a composite's `items` carries either element
|
|
314
|
+
// specs (a card's children) or shorthand data objects. Walk
|
|
315
|
+
// the spec form so a typo inside a card is reported at its
|
|
316
|
+
// real path; leave the shorthand alone, since it is data for
|
|
317
|
+
// the emitted template rather than elements to validate.
|
|
318
|
+
//
|
|
319
|
+
// Checked here rather than in a per-type table because
|
|
320
|
+
// `items` means the same thing wherever a composite accepts
|
|
321
|
+
// it, and a table would drift from the mapper the way the
|
|
322
|
+
// op registry did before 1.2.8.
|
|
323
|
+
if (Array.isArray(el.items)) {
|
|
324
|
+
el.items.forEach((entry, k) => {
|
|
325
|
+
if (Array.isArray(entry)) walkEls(entry, `${p}.items[${k}]`);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
218
328
|
});
|
|
219
329
|
};
|
|
220
330
|
walkEls(elements, "elements");
|
package/lib/webmcp-adapter.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodality",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.11",
|
|
4
4
|
"description": "A lightweight library for declarative UI elements.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"bin/",
|
|
15
15
|
"API.md",
|
|
16
16
|
"examples/",
|
|
17
|
-
"skills/"
|
|
17
|
+
"skills/",
|
|
18
|
+
"schema.json",
|
|
19
|
+
"scripts/generate-schema.mjs"
|
|
18
20
|
],
|
|
19
21
|
"exports": {
|
|
20
22
|
".": {
|
|
@@ -30,6 +32,7 @@
|
|
|
30
32
|
"./presets": "./lib/raster-presets.js",
|
|
31
33
|
"./raster": "./lib/raster-ops.js",
|
|
32
34
|
"./validate": "./lib/validate-nodes.js",
|
|
35
|
+
"./parse": "./lib/parse-html.js",
|
|
33
36
|
"./inspect": "./lib/raster-inspect.js",
|
|
34
37
|
"./examples/*": "./examples/*.js",
|
|
35
38
|
"./transition": "./lib/transition.js",
|