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.
Files changed (205) hide show
  1. package/bin/mcp-server.mjs +86 -0
  2. package/bin/nodality.js +12 -0
  3. package/bin/schema-cli.mjs +72 -0
  4. package/dist/animator.cjs.js.LICENSE.txt +1 -1
  5. package/dist/animator.esm.js.LICENSE.txt +1 -1
  6. package/dist/audionew.cjs.js.LICENSE.txt +1 -1
  7. package/dist/audionew.esm.js.LICENSE.txt +1 -1
  8. package/dist/base.cjs.js.LICENSE.txt +1 -1
  9. package/dist/base.esm.js.LICENSE.txt +1 -1
  10. package/dist/beta-desktop-bar.cjs.js.LICENSE.txt +1 -1
  11. package/dist/beta-desktop-bar.esm.js.LICENSE.txt +1 -1
  12. package/dist/beta-mobile-bar.cjs.js.LICENSE.txt +1 -1
  13. package/dist/beta-mobile-bar.esm.js.LICENSE.txt +1 -1
  14. package/dist/bundle.umd.js +1 -1
  15. package/dist/bundle.umd.js.LICENSE.txt +1 -28
  16. package/dist/button.cjs.js.LICENSE.txt +1 -1
  17. package/dist/button.esm.js.LICENSE.txt +1 -1
  18. package/dist/card-getter.cjs.js.LICENSE.txt +1 -1
  19. package/dist/card-getter.esm.js.LICENSE.txt +1 -1
  20. package/dist/center.cjs.js.LICENSE.txt +1 -1
  21. package/dist/center.esm.js.LICENSE.txt +1 -1
  22. package/dist/checkbox.cjs.js.LICENSE.txt +1 -1
  23. package/dist/checkbox.esm.js.LICENSE.txt +1 -1
  24. package/dist/code.cjs.js.LICENSE.txt +1 -1
  25. package/dist/code.esm.js.LICENSE.txt +1 -1
  26. package/dist/container.cjs.js.LICENSE.txt +1 -1
  27. package/dist/container.esm.js.LICENSE.txt +1 -1
  28. package/dist/data-list.cjs.js.LICENSE.txt +1 -1
  29. package/dist/data-list.esm.js.LICENSE.txt +1 -1
  30. package/dist/designer.cjs.js +1 -1
  31. package/dist/designer.cjs.js.LICENSE.txt +1 -28
  32. package/dist/designer.esm.js +1 -1
  33. package/dist/designer.esm.js.LICENSE.txt +1 -28
  34. package/dist/element-mapper.cjs.js +1 -1
  35. package/dist/element-mapper.cjs.js.LICENSE.txt +1 -1
  36. package/dist/element-mapper.esm.js +1 -1
  37. package/dist/element-mapper.esm.js.LICENSE.txt +1 -1
  38. package/dist/finalresult.esm.js +1 -1
  39. package/dist/finalresult.esm.js.LICENSE.txt +1 -28
  40. package/dist/flex-card.cjs.js.LICENSE.txt +1 -1
  41. package/dist/flex-card.esm.js.LICENSE.txt +1 -1
  42. package/dist/flex-grid.cjs.js.LICENSE.txt +1 -1
  43. package/dist/flex-grid.esm.js.LICENSE.txt +1 -1
  44. package/dist/flex-row.cjs.js.LICENSE.txt +1 -1
  45. package/dist/flex-row.esm.js.LICENSE.txt +1 -1
  46. package/dist/floating-input.cjs.js.LICENSE.txt +1 -1
  47. package/dist/floating-input.esm.js.LICENSE.txt +1 -1
  48. package/dist/free.cjs.js.LICENSE.txt +1 -1
  49. package/dist/free.esm.js.LICENSE.txt +1 -1
  50. package/dist/horizontal-scroller.cjs.js.LICENSE.txt +1 -1
  51. package/dist/horizontal-scroller.esm.js.LICENSE.txt +1 -1
  52. package/dist/image-picker.cjs.js.LICENSE.txt +1 -1
  53. package/dist/image-picker.esm.js.LICENSE.txt +1 -1
  54. package/dist/image.cjs.js.LICENSE.txt +1 -1
  55. package/dist/image.esm.js.LICENSE.txt +1 -1
  56. package/dist/index.cjs.js +1 -1
  57. package/dist/index.cjs.js.LICENSE.txt +1 -28
  58. package/dist/index.d.ts +1 -1
  59. package/dist/index.esm.js +1 -1
  60. package/dist/index.esm.js.LICENSE.txt +1 -28
  61. package/dist/keyframe-animation.cjs.js.LICENSE.txt +1 -1
  62. package/dist/keyframe-animation.esm.js.LICENSE.txt +1 -1
  63. package/dist/link-getter.cjs.js.LICENSE.txt +1 -1
  64. package/dist/link-getter.esm.js.LICENSE.txt +1 -1
  65. package/dist/link.cjs.js.LICENSE.txt +1 -1
  66. package/dist/link.esm.js.LICENSE.txt +1 -1
  67. package/dist/meta-adder.cjs.js.LICENSE.txt +1 -1
  68. package/dist/meta-adder.esm.js.LICENSE.txt +1 -1
  69. package/dist/modal-2025.cjs.js.LICENSE.txt +1 -1
  70. package/dist/modal-2025.esm.js.LICENSE.txt +1 -1
  71. package/dist/multiswitcher.cjs.js.LICENSE.txt +1 -1
  72. package/dist/multiswitcher.esm.js.LICENSE.txt +1 -1
  73. package/dist/new-nav-bar.cjs.js.LICENSE.txt +1 -1
  74. package/dist/new-nav-bar.esm.js.LICENSE.txt +1 -1
  75. package/dist/picker.cjs.js.LICENSE.txt +1 -1
  76. package/dist/picker.esm.js.LICENSE.txt +1 -1
  77. package/dist/progress.cjs.js.LICENSE.txt +1 -1
  78. package/dist/progress.esm.js.LICENSE.txt +1 -1
  79. package/dist/radio.cjs.js.LICENSE.txt +1 -1
  80. package/dist/radio.esm.js.LICENSE.txt +1 -1
  81. package/dist/range.cjs.js.LICENSE.txt +1 -1
  82. package/dist/range.esm.js.LICENSE.txt +1 -1
  83. package/dist/scroll-video.cjs.js.LICENSE.txt +1 -1
  84. package/dist/scroll-video.esm.js.LICENSE.txt +1 -1
  85. package/dist/side-bar.cjs.js.LICENSE.txt +1 -1
  86. package/dist/side-bar.esm.js.LICENSE.txt +1 -1
  87. package/dist/side-nav-bar.cjs.js.LICENSE.txt +1 -1
  88. package/dist/side-nav-bar.esm.js.LICENSE.txt +1 -1
  89. package/dist/simple-bar.cjs.js.LICENSE.txt +1 -1
  90. package/dist/simple-bar.esm.js.LICENSE.txt +1 -1
  91. package/dist/slider-2025.cjs.js.LICENSE.txt +1 -1
  92. package/dist/slider-2025.esm.js.LICENSE.txt +1 -1
  93. package/dist/spacer.cjs.js.LICENSE.txt +1 -1
  94. package/dist/spacer.esm.js.LICENSE.txt +1 -1
  95. package/dist/stack.cjs.js.LICENSE.txt +1 -1
  96. package/dist/stack.esm.js.LICENSE.txt +1 -1
  97. package/dist/stacker.cjs.js.LICENSE.txt +1 -1
  98. package/dist/stacker.esm.js.LICENSE.txt +1 -1
  99. package/dist/table.cjs.js.LICENSE.txt +1 -1
  100. package/dist/table.esm.js.LICENSE.txt +1 -1
  101. package/dist/text-field.cjs.js.LICENSE.txt +1 -1
  102. package/dist/text-field.esm.js.LICENSE.txt +1 -1
  103. package/dist/text.cjs.js.LICENSE.txt +1 -1
  104. package/dist/text.esm.js.LICENSE.txt +1 -1
  105. package/dist/theme.cjs.js.LICENSE.txt +1 -1
  106. package/dist/theme.esm.js.LICENSE.txt +1 -1
  107. package/dist/transform-anim.cjs.js.LICENSE.txt +1 -1
  108. package/dist/transform-anim.esm.js.LICENSE.txt +1 -1
  109. package/dist/ulist.cjs.js.LICENSE.txt +1 -1
  110. package/dist/ulist.esm.js.LICENSE.txt +1 -1
  111. package/dist/video.cjs.js.LICENSE.txt +1 -1
  112. package/dist/video.esm.js.LICENSE.txt +1 -1
  113. package/dist/wrap.cjs.js.LICENSE.txt +1 -1
  114. package/dist/wrap.esm.js.LICENSE.txt +1 -1
  115. package/dist/zoom-card.cjs.js.LICENSE.txt +1 -1
  116. package/dist/zoom-card.esm.js.LICENSE.txt +1 -1
  117. package/layout/animator.js +1 -1
  118. package/layout/audio.js +1 -1
  119. package/layout/audionew.js +1 -1
  120. package/layout/base.js +1 -1
  121. package/layout/beta-desktop-bar.js +1 -1
  122. package/layout/beta-mobile-bar.js +1 -1
  123. package/layout/button.js +1 -1
  124. package/layout/center.js +1 -1
  125. package/layout/checkbox.js +1 -1
  126. package/layout/circle.js +1 -1
  127. package/layout/code.js +1 -1
  128. package/layout/container.js +1 -1
  129. package/layout/dropdown-2025.js +1 -1
  130. package/layout/flex-card.js +1 -1
  131. package/layout/flex-grid.js +1 -1
  132. package/layout/flex-row.js +1 -1
  133. package/layout/form-components/custom.js +1 -1
  134. package/layout/form-components/data-list.js +1 -1
  135. package/layout/form-components/floating-input.js +1 -1
  136. package/layout/form-components/form-all.js +1 -1
  137. package/layout/form-components/form.js +1 -1
  138. package/layout/form-components/image-picker.js +1 -1
  139. package/layout/form-components/picker.js +1 -1
  140. package/layout/form-components/radio.js +1 -1
  141. package/layout/form-components/radiogroup.js +1 -1
  142. package/layout/form-components/range.js +1 -1
  143. package/layout/free.js +1 -1
  144. package/layout/grid-switcher.js +1 -1
  145. package/layout/grid.js +1 -1
  146. package/layout/horizontal-scroller.js +1 -1
  147. package/layout/image.js +1 -1
  148. package/layout/index.js +1 -1
  149. package/layout/link.js +1 -1
  150. package/layout/list.js +1 -1
  151. package/layout/meta-adder.js +1 -1
  152. package/layout/modal-2025.js +1 -1
  153. package/layout/morph.js +1 -1
  154. package/layout/multiswitcher.js +1 -1
  155. package/layout/nav-bar.js +1 -1
  156. package/layout/nav-factor/custom-div.js +1 -1
  157. package/layout/new-nav-bar.js +1 -1
  158. package/layout/polygon.js +1 -1
  159. package/layout/prerender-site.js +1 -1
  160. package/layout/prerender.js +1 -1
  161. package/layout/progress.js +1 -1
  162. package/layout/row.js +1 -1
  163. package/layout/scroll-video.js +1 -1
  164. package/layout/side-bar.js +1 -1
  165. package/layout/side-nav-bar.js +1 -1
  166. package/layout/simple-bar.js +1 -1
  167. package/layout/slider-2025.js +1 -1
  168. package/layout/spacer.js +1 -1
  169. package/layout/stack.js +1 -1
  170. package/layout/svg.js +1 -1
  171. package/layout/switcher.js +1 -1
  172. package/layout/table.js +1 -1
  173. package/layout/text-field.js +1 -1
  174. package/layout/text.js +1 -1
  175. package/layout/ulist.js +1 -1
  176. package/layout/video.js +1 -1
  177. package/layout/wrap.js +1 -1
  178. package/layout/zoom-card.js +1 -1
  179. package/lib/agent-surface.js +1 -1
  180. package/lib/card-getter.js +1 -1
  181. package/lib/codegen.js +1 -1
  182. package/lib/data.js +1 -1
  183. package/lib/designer.js +21 -1
  184. package/lib/element-mapper.js +173 -16
  185. package/lib/element-params.generated.js +11 -0
  186. package/lib/keyframe-animation.js +1 -1
  187. package/lib/link-getter.js +1 -1
  188. package/lib/morph-node.js +1 -1
  189. package/lib/parse-html.js +151 -0
  190. package/lib/raster-inspect.js +1 -1
  191. package/lib/raster-ops.js +1 -1
  192. package/lib/raster-presets.js +1 -1
  193. package/lib/scroll-video.js +1 -1
  194. package/lib/seo.js +1 -1
  195. package/lib/stacker.js +1 -1
  196. package/lib/suggest.js +1 -1
  197. package/lib/theme.js +1 -1
  198. package/lib/transform-anim.js +1 -1
  199. package/lib/transition.js +1 -1
  200. package/lib/validate-nodes.js +112 -2
  201. package/lib/webmcp-adapter.js +1 -1
  202. package/package.json +5 -2
  203. package/schema.json +7953 -0
  204. package/scripts/generate-schema.mjs +251 -0
  205. package/skills/nodality/SKILL.md +71 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.2.9
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 { didYouMean, suggest } from "./suggest.js";
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");
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * nodality v1.2.9
2
+ * nodality v1.2.11
3
3
  * (c) 2026 Filip Vabrousek
4
4
  * License: MIT
5
5
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodality",
3
- "version": "1.2.9",
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",