nodality 1.3.14 → 1.3.16
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/install-skill.mjs +3 -3
- package/bin/mcp-server.mjs +46 -1
- package/bin/nodality.mjs +104 -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 +1 -1
- package/dist/link-getter.cjs.js.LICENSE.txt +1 -1
- package/dist/link-getter.esm.js +1 -1
- package/dist/link-getter.esm.js.LICENSE.txt +1 -1
- package/dist/link.cjs.js +1 -1
- package/dist/link.cjs.js.LICENSE.txt +1 -1
- package/dist/link.esm.js +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 +1 -1
- package/dist/text-field.cjs.js.LICENSE.txt +1 -1
- package/dist/text-field.esm.js +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 +9 -2
- 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 +6 -3
- 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 +4 -2
- 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 +37 -5
- package/lib/codegen.js +1 -1
- package/lib/data.js +1 -1
- package/lib/designer.js +3 -2
- package/lib/element-mapper.js +118 -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/known-inert.js +85 -0
- 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 +55 -4
- package/lib/webmcp-adapter.js +36 -1
- package/package.json +1 -1
- package/schema.json +3477 -816
- package/scripts/generate-schema.mjs +7 -0
- package/skills/nodality/SKILL.md +11 -1
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.16
|
|
3
3
|
* (c) 2026 Filip Vabrousek
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -142,6 +142,7 @@ function shapeFor(unit) {
|
|
|
142
142
|
return SHAPES[unit] || null;
|
|
143
143
|
}
|
|
144
144
|
import { didYouMean, suggest, levenshtein } from "./suggest.js";
|
|
145
|
+
import { KNOWN_INERT } from "./known-inert.js";
|
|
145
146
|
|
|
146
147
|
/**
|
|
147
148
|
* Every key a morph node reads. Anything else on the node is ignored by
|
|
@@ -167,7 +168,7 @@ const EDGE_FIELDS = [
|
|
|
167
168
|
* what it exposes is DERIVED, so there is nothing here to describe the
|
|
168
169
|
* tools with — only which parts of the page may be reached.
|
|
169
170
|
*/
|
|
170
|
-
const AGENT_SURFACE_FIELDS = ["op", "name", "forms", "exclude"];
|
|
171
|
+
const AGENT_SURFACE_FIELDS = ["op", "name", "forms", "exclude", "products"];
|
|
171
172
|
|
|
172
173
|
/**
|
|
173
174
|
* Design ops that may be written as a BARE STRING, `{ op: "gradient" }`,
|
|
@@ -277,6 +278,7 @@ export const CONTENT_SLOT = {
|
|
|
277
278
|
cards: "items", nav: "items", sideNav: "items", table: "items", ulist: "items",
|
|
278
279
|
dropdown: "items", picker: "items", radio: "items",
|
|
279
280
|
row: "children", form: "children", stack: "children", wrap: "children",
|
|
281
|
+
product: "children",
|
|
280
282
|
};
|
|
281
283
|
|
|
282
284
|
// Of the types that read `items`, which read the entries as ELEMENT SPECS
|
|
@@ -417,6 +419,36 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
417
419
|
? resolveRefs(elements, defs)
|
|
418
420
|
: elements;
|
|
419
421
|
|
|
422
|
+
// Commerce order. A `product` looks for the page's `store` the moment
|
|
423
|
+
// it is attached; Des attaches in array order, so a product rendered
|
|
424
|
+
// before the store stays empty, and the only report is a console error
|
|
425
|
+
// in the browser. Found by building a product page with the store at the
|
|
426
|
+
// end, where it looked tidy.
|
|
427
|
+
if (Array.isArray(toWalk)) {
|
|
428
|
+
let storeAt = null;
|
|
429
|
+
const products = [];
|
|
430
|
+
const visit = (list, base) => {
|
|
431
|
+
if (!Array.isArray(list)) return;
|
|
432
|
+
list.forEach((el, i) => {
|
|
433
|
+
if (!el || typeof el !== "object") return;
|
|
434
|
+
const p = `${base}[${i}]`;
|
|
435
|
+
if (el.type === "store" && storeAt === null) storeAt = p;
|
|
436
|
+
if (el.type === "product") products.push({ p, before: storeAt === null });
|
|
437
|
+
visit(el.children, `${p}.children`);
|
|
438
|
+
});
|
|
439
|
+
};
|
|
440
|
+
visit(toWalk, "elements");
|
|
441
|
+
if (products.length && storeAt === null) {
|
|
442
|
+
warn("NO_STORE", products[0].p, "product", ["{ type: \"store\", domain: \"https://your-shop.example\" }"],
|
|
443
|
+
["a store element placed before the first product"]);
|
|
444
|
+
} else {
|
|
445
|
+
for (const { p } of products.filter((x) => x.before)) {
|
|
446
|
+
warn("STORE_AFTER_PRODUCT", p, "product", [`move ${storeAt} before this product`],
|
|
447
|
+
["the store element first: a product finds its store when it is attached"]);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
420
452
|
// An alias and its canonical name both present, disagreeing: one of them
|
|
421
453
|
// is going to be ignored. The author hears it here rather than finding it
|
|
422
454
|
// in the page.
|
|
@@ -524,6 +556,18 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
524
556
|
`"${el.type}" does not read "${key}" — it is accepted and ignored`);
|
|
525
557
|
}
|
|
526
558
|
|
|
559
|
+
// Advertised by the schema for this type, and known to
|
|
560
|
+
// do nothing (the conformance baseline). Same failure
|
|
561
|
+
// as above — accepted, ignored — but the name IS in
|
|
562
|
+
// the type's vocabulary, so only this list knows.
|
|
563
|
+
if (KNOWN_INERT.has(`${el.type}.${key}`)) {
|
|
564
|
+
warn("PARAM_INERT", `${p}.${key}`, key, [],
|
|
565
|
+
[key === "size" && /^(h[1-6]|p)$/.test(el.type)
|
|
566
|
+
? "the element type sets the scale step; use `tag` for the heading level and keySet fontSize for the size"
|
|
567
|
+
: `see \`npx nodality schema ${el.type}\`; wrap the element in a \`wrap\` to style or place it`],
|
|
568
|
+
`"${el.type}" lists "${key}" but ignores it — a known gap, recorded in lib/known-inert.js`);
|
|
569
|
+
}
|
|
570
|
+
|
|
527
571
|
// A value the parameter cannot use. Checked only where
|
|
528
572
|
// the source declares a shape, so this grows with the
|
|
529
573
|
// annotations and never guesses.
|
|
@@ -728,11 +772,13 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
728
772
|
// form of the silent no-op, and an agent has no way to see
|
|
729
773
|
// that the tool it expected was never registered.
|
|
730
774
|
const formIds = [];
|
|
775
|
+
const productIds = [];
|
|
731
776
|
const walkForms = (list) => {
|
|
732
777
|
if (!Array.isArray(list)) return;
|
|
733
778
|
for (const el of list) {
|
|
734
779
|
if (!el || typeof el !== "object") continue;
|
|
735
780
|
if (el.type === "form" && typeof el.id === "string") formIds.push(el.id);
|
|
781
|
+
if (el.type === "product" && typeof el.id === "string") productIds.push(el.id.replace(/^#/, ""));
|
|
736
782
|
walkForms(el.children);
|
|
737
783
|
}
|
|
738
784
|
};
|
|
@@ -743,7 +789,7 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
743
789
|
["a string used to prefix every derived tool name"]);
|
|
744
790
|
}
|
|
745
791
|
|
|
746
|
-
for (const key of ["forms", "exclude"]) {
|
|
792
|
+
for (const key of ["forms", "exclude", "products"]) {
|
|
747
793
|
if (node[key] === undefined) continue;
|
|
748
794
|
if (!Array.isArray(node[key])) {
|
|
749
795
|
push("BAD_FIELD", `${path}.${key}`, node[key], [], ["an array of ids"]);
|
|
@@ -763,6 +809,10 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
763
809
|
if (idsKnown && !formIds.includes(bare)) {
|
|
764
810
|
push("UNKNOWN_FORM", at, v, suggest(bare, formIds), formIds);
|
|
765
811
|
}
|
|
812
|
+
} else if (key === "products") {
|
|
813
|
+
if (idsKnown && !productIds.includes(bare)) {
|
|
814
|
+
push("UNKNOWN_PRODUCT", at, v, suggest(bare, productIds), productIds);
|
|
815
|
+
}
|
|
766
816
|
} else if (idsKnown && !ids.includes(bare)) {
|
|
767
817
|
push("UNKNOWN_STATE", at, v, suggest(bare, ids), ids);
|
|
768
818
|
}
|
|
@@ -775,7 +825,8 @@ export function validateNodes(nodes, elements, defs) {
|
|
|
775
825
|
// in the expectation of more says so.
|
|
776
826
|
const hasChain = nodes.some((n) => n && n.op === "morph");
|
|
777
827
|
const hasForms = Array.isArray(node.forms) && node.forms.length > 0;
|
|
778
|
-
|
|
828
|
+
const hasProducts = Array.isArray(node.products) && node.products.length > 0;
|
|
829
|
+
if (!hasChain && !hasForms && !hasProducts) {
|
|
779
830
|
push("EMPTY_SURFACE", path, node, [],
|
|
780
831
|
["add a { op: \"morph\" } node, or name a form in `forms`"]);
|
|
781
832
|
}
|
package/lib/webmcp-adapter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* nodality v1.3.
|
|
2
|
+
* nodality v1.3.16
|
|
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, []);
|