nodality 1.3.14 → 1.3.15
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/nodality.mjs +1 -1
- 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 +2 -2
- 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 +20 -1
- package/dist/index.esm.js +2 -2
- 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.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-overlay.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 +5 -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/shop.js +261 -0
- 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 +24 -2
- 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 +3 -2
- package/lib/element-mapper.js +116 -1
- package/lib/element-params.generated.js +4 -4
- package/lib/grid-geometry.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 +42 -4
- package/lib/webmcp-adapter.js +36 -1
- package/package.json +1 -1
- package/schema.json +1000 -2
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.15
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -167,7 +167,7 @@ const EDGE_FIELDS = [
|
|
|
167
167
|
* what it exposes is DERIVED, so there is nothing here to describe the
|
|
168
168
|
* tools with — only which parts of the page may be reached.
|
|
169
169
|
*/
|
|
170
|
-
const AGENT_SURFACE_FIELDS = ["op", "name", "forms", "exclude"];
|
|
170
|
+
const AGENT_SURFACE_FIELDS = ["op", "name", "forms", "exclude", "products"];
|
|
171
171
|
|
|
172
172
|
/**
|
|
173
173
|
* Design ops that may be written as a BARE STRING, `{ op: "gradient" }`,
|
|
@@ -277,6 +277,7 @@ export const CONTENT_SLOT = {
|
|
|
277
277
|
cards: "items", nav: "items", sideNav: "items", table: "items", ulist: "items",
|
|
278
278
|
dropdown: "items", picker: "items", radio: "items",
|
|
279
279
|
row: "children", form: "children", stack: "children", wrap: "children",
|
|
280
|
+
product: "children",
|
|
280
281
|
};
|
|
281
282
|
|
|
282
283
|
// Of the types that read `items`, which read the entries as ELEMENT SPECS
|
|
@@ -417,6 +418,36 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
417
418
|
? resolveRefs(elements, defs)
|
|
418
419
|
: elements;
|
|
419
420
|
|
|
421
|
+
// Commerce order. A `product` looks for the page's `store` the moment
|
|
422
|
+
// it is attached; Des attaches in array order, so a product rendered
|
|
423
|
+
// before the store stays empty, and the only report is a console error
|
|
424
|
+
// in the browser. Found by building a product page with the store at the
|
|
425
|
+
// end, where it looked tidy.
|
|
426
|
+
if (Array.isArray(toWalk)) {
|
|
427
|
+
let storeAt = null;
|
|
428
|
+
const products = [];
|
|
429
|
+
const visit = (list, base) => {
|
|
430
|
+
if (!Array.isArray(list)) return;
|
|
431
|
+
list.forEach((el, i) => {
|
|
432
|
+
if (!el || typeof el !== "object") return;
|
|
433
|
+
const p = `${base}[${i}]`;
|
|
434
|
+
if (el.type === "store" && storeAt === null) storeAt = p;
|
|
435
|
+
if (el.type === "product") products.push({ p, before: storeAt === null });
|
|
436
|
+
visit(el.children, `${p}.children`);
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
visit(toWalk, "elements");
|
|
440
|
+
if (products.length && storeAt === null) {
|
|
441
|
+
warn("NO_STORE", products[0].p, "product", ["{ type: \"store\", domain: \"https://your-shop.example\" }"],
|
|
442
|
+
["a store element placed before the first product"]);
|
|
443
|
+
} else {
|
|
444
|
+
for (const { p } of products.filter((x) => x.before)) {
|
|
445
|
+
warn("STORE_AFTER_PRODUCT", p, "product", [`move ${storeAt} before this product`],
|
|
446
|
+
["the store element first: a product finds its store when it is attached"]);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
420
451
|
// An alias and its canonical name both present, disagreeing: one of them
|
|
421
452
|
// is going to be ignored. The author hears it here rather than finding it
|
|
422
453
|
// in the page.
|
|
@@ -728,11 +759,13 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
728
759
|
// form of the silent no-op, and an agent has no way to see
|
|
729
760
|
// that the tool it expected was never registered.
|
|
730
761
|
const formIds = [];
|
|
762
|
+
const productIds = [];
|
|
731
763
|
const walkForms = (list) => {
|
|
732
764
|
if (!Array.isArray(list)) return;
|
|
733
765
|
for (const el of list) {
|
|
734
766
|
if (!el || typeof el !== "object") continue;
|
|
735
767
|
if (el.type === "form" && typeof el.id === "string") formIds.push(el.id);
|
|
768
|
+
if (el.type === "product" && typeof el.id === "string") productIds.push(el.id.replace(/^#/, ""));
|
|
736
769
|
walkForms(el.children);
|
|
737
770
|
}
|
|
738
771
|
};
|
|
@@ -743,7 +776,7 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
743
776
|
["a string used to prefix every derived tool name"]);
|
|
744
777
|
}
|
|
745
778
|
|
|
746
|
-
for (const key of ["forms", "exclude"]) {
|
|
779
|
+
for (const key of ["forms", "exclude", "products"]) {
|
|
747
780
|
if (node[key] === undefined) continue;
|
|
748
781
|
if (!Array.isArray(node[key])) {
|
|
749
782
|
push("BAD_FIELD", `${path}.${key}`, node[key], [], ["an array of ids"]);
|
|
@@ -763,6 +796,10 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
763
796
|
if (idsKnown && !formIds.includes(bare)) {
|
|
764
797
|
push("UNKNOWN_FORM", at, v, suggest(bare, formIds), formIds);
|
|
765
798
|
}
|
|
799
|
+
} else if (key === "products") {
|
|
800
|
+
if (idsKnown && !productIds.includes(bare)) {
|
|
801
|
+
push("UNKNOWN_PRODUCT", at, v, suggest(bare, productIds), productIds);
|
|
802
|
+
}
|
|
766
803
|
} else if (idsKnown && !ids.includes(bare)) {
|
|
767
804
|
push("UNKNOWN_STATE", at, v, suggest(bare, ids), ids);
|
|
768
805
|
}
|
|
@@ -775,7 +812,8 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
775
812
|
// in the expectation of more says so.
|
|
776
813
|
const hasChain = nodes.some((n) => n && n.op === "morph");
|
|
777
814
|
const hasForms = Array.isArray(node.forms) && node.forms.length > 0;
|
|
778
|
-
|
|
815
|
+
const hasProducts = Array.isArray(node.products) && node.products.length > 0;
|
|
816
|
+
if (!hasChain && !hasForms && !hasProducts) {
|
|
779
817
|
push("EMPTY_SURFACE", path, node, [],
|
|
780
818
|
["add a { op: \"morph\" } node, or name a form in `forms`"]);
|
|
781
819
|
}
|
package/lib/webmcp-adapter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.15
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -265,6 +265,41 @@ export function installAgentSurface({ mount, elements, nodes, morphs }) {
|
|
|
265
265
|
|
|
266
266
|
read: async () => ({ ok: true, view: currentView(), ...readSubtree(currentSubtree()) }),
|
|
267
267
|
|
|
268
|
+
// What the product box shows now: the live rendering once Shopify has
|
|
269
|
+
// answered (its placeholder is hidden then), the static copy before.
|
|
270
|
+
product: async (args, tool) => {
|
|
271
|
+
const box = mount && mount.querySelector(`[id="${tool.productId}"]`);
|
|
272
|
+
if (!box) return refuse("NO_PRODUCT", tool.productId, []);
|
|
273
|
+
const ph = box.querySelector("[shopify-loading-placeholder]");
|
|
274
|
+
const live = ph && typeof window !== "undefined" && window.getComputedStyle(ph).display === "none";
|
|
275
|
+
// Its own reader, not readSubtree: a price and its options sit in
|
|
276
|
+
// inline elements and in Shopify's (open) shadow DOM, which a
|
|
277
|
+
// reader of headings and paragraphs never reaches. Visible text in
|
|
278
|
+
// document order; the template, scripts, styles and anything hidden
|
|
279
|
+
// — the placeholder once the live copy is up — are skipped.
|
|
280
|
+
const parts = [];
|
|
281
|
+
const actions = [];
|
|
282
|
+
const walk = (n) => {
|
|
283
|
+
for (const c of n.childNodes) {
|
|
284
|
+
if (c.nodeType === 3) {
|
|
285
|
+
const t = c.textContent.replace(/\s+/g, " ").trim();
|
|
286
|
+
if (t) parts.push(t);
|
|
287
|
+
} else if (c.nodeType === 1) {
|
|
288
|
+
if (/^(TEMPLATE|SCRIPT|STYLE)$/.test(c.tagName)) continue;
|
|
289
|
+
if (window.getComputedStyle(c).display === "none") continue;
|
|
290
|
+
if (/^(A|BUTTON)$/.test(c.tagName)) {
|
|
291
|
+
const t = c.textContent.replace(/\s+/g, " ").trim();
|
|
292
|
+
if (t && !actions.includes(t)) actions.push(t);
|
|
293
|
+
}
|
|
294
|
+
if (c.shadowRoot) walk(c.shadowRoot);
|
|
295
|
+
walk(c);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
walk(box);
|
|
300
|
+
return { ok: true, product: tool.productId, handle: box.getAttribute("handle"), live: !!live, text: parts.join("\n"), actions };
|
|
301
|
+
},
|
|
302
|
+
|
|
268
303
|
submit: async (args = {}, tool) => {
|
|
269
304
|
const form = findForm(tool.formId);
|
|
270
305
|
if (!form) return refuse("NO_FORM", tool.formId, []);
|