nodality 1.3.13 → 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 +1 -1
- package/dist/beta-desktop-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-desktop-bar.esm.js +1 -1
- package/dist/beta-desktop-bar.esm.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.cjs.js +1 -1
- package/dist/beta-mobile-bar.cjs.js.LICENSE.txt +1 -1
- package/dist/beta-mobile-bar.esm.js +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 +1 -1
- package/dist/multiswitcher.cjs.js.LICENSE.txt +1 -1
- package/dist/multiswitcher.esm.js +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 +4 -1
- package/layout/beta-mobile-bar.js +35 -13
- 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 +15 -2
- 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 +14 -9
- 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 +186 -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 +1028 -12
package/layout/shop.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* nodality v1.3.15
|
|
3
|
+
* (c) 2026 Filip Vabrousek
|
|
4
|
+
* License: MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// shop.js — commerce for a static page, on Shopify's Storefront Web Components.
|
|
8
|
+
//
|
|
9
|
+
// new Shop().set({ kind: "store", domain: "https://mock.shop" }).render("#mount");
|
|
10
|
+
// new Shop().set({ kind: "product", handle: "sweatpants" })
|
|
11
|
+
// .add([ new Shop().set({ kind: "price" }) ]) // live
|
|
12
|
+
// .fallback([ new Shop().set({ kind: "price", text: "€35", fallback: true }) ])
|
|
13
|
+
// .render("#mount");
|
|
14
|
+
//
|
|
15
|
+
// One class, one `kind` per element type: store, product, price, productData,
|
|
16
|
+
// productMedia, variantPicker, buy, cart. Each renders the matching Shopify
|
|
17
|
+
// custom element, so the cart, prices per market and checkout stay Shopify's
|
|
18
|
+
// and the page stays static.
|
|
19
|
+
//
|
|
20
|
+
// Every kind also has a STATIC form (`fallback: true`): plain text, an image
|
|
21
|
+
// or a link built from build-time data. A product's static children fill the
|
|
22
|
+
// placeholder Shopify shows until its data arrives — which is also what the
|
|
23
|
+
// prerenderer writes into the HTML, because jsdom never defines the custom
|
|
24
|
+
// elements. So crawlers and no-JS readers get the product, its price and a
|
|
25
|
+
// working link to buy it.
|
|
26
|
+
|
|
27
|
+
import { Animator } from "./animator.js";
|
|
28
|
+
import { toObjectSource } from "../lib/codegen.js";
|
|
29
|
+
|
|
30
|
+
const SCRIPT = "https://cdn.shopify.com/storefront/web-components.js";
|
|
31
|
+
|
|
32
|
+
// Ids end up inside inline handlers, so only id-shaped strings are accepted.
|
|
33
|
+
const safeId = (v, dflt) => {
|
|
34
|
+
const s = String(v ?? dflt).replace(/^#/, "");
|
|
35
|
+
return /^[A-Za-z][\w-]*$/.test(s) ? s : dflt;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const DEFAULT_QUERY = {
|
|
39
|
+
price: "product.selectedOrFirstAvailableVariant.price",
|
|
40
|
+
productData: "product.title",
|
|
41
|
+
productMedia: "product.selectedOrFirstAvailableVariant.image",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
class Shop extends Animator {
|
|
45
|
+
constructor() {
|
|
46
|
+
super();
|
|
47
|
+
this.items = [];
|
|
48
|
+
this.fallbackItems = [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
set(obj = {}) {
|
|
52
|
+
this.options = obj;
|
|
53
|
+
const kind = obj.kind;
|
|
54
|
+
const builder = this[`build_${kind}`];
|
|
55
|
+
if (typeof builder !== "function") {
|
|
56
|
+
throw new Error(`nodality: Shop has no kind "${kind}"`);
|
|
57
|
+
}
|
|
58
|
+
this.res = builder.call(this, obj);
|
|
59
|
+
|
|
60
|
+
if (kind === "product") {
|
|
61
|
+
// A product is a box on the page, styled like a wrap. The
|
|
62
|
+
// custom element is inline by default.
|
|
63
|
+
this.res.style.display = "block";
|
|
64
|
+
this.commonMethods(obj);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
obj.id && this.res.setAttribute("id", String(obj.id));
|
|
68
|
+
obj.font && (this.res.style.fontFamily = obj.font);
|
|
69
|
+
obj.color && (this.res.style.color = obj.color);
|
|
70
|
+
obj.keySet && this.keySet(obj.keySet);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── kinds ──────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
build_store(o) {
|
|
77
|
+
const el = document.createElement("shopify-store");
|
|
78
|
+
el.setAttribute("store-domain", o.domain);
|
|
79
|
+
o.token && el.setAttribute("public-access-token", o.token);
|
|
80
|
+
o.country && el.setAttribute("country", o.country);
|
|
81
|
+
o.language && el.setAttribute("language", o.language);
|
|
82
|
+
Shop.loadScript();
|
|
83
|
+
return el;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
build_product(o) {
|
|
87
|
+
const ctx = document.createElement("shopify-context");
|
|
88
|
+
ctx.setAttribute("type", "product");
|
|
89
|
+
ctx.setAttribute("handle", o.handle);
|
|
90
|
+
this.template = document.createElement("template");
|
|
91
|
+
this.placeholder = document.createElement("div");
|
|
92
|
+
this.placeholder.setAttribute("shopify-loading-placeholder", "");
|
|
93
|
+
ctx.appendChild(this.template);
|
|
94
|
+
ctx.appendChild(this.placeholder);
|
|
95
|
+
return ctx;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
build_price(o) {
|
|
99
|
+
if (o.fallback) return this.text(o.text);
|
|
100
|
+
const el = document.createElement("shopify-money");
|
|
101
|
+
el.setAttribute("query", o.query ?? DEFAULT_QUERY.price);
|
|
102
|
+
// The static value as initial content: shown until Shopify renders,
|
|
103
|
+
// so the price never flashes blank.
|
|
104
|
+
o.text != null && (el.textContent = o.text);
|
|
105
|
+
return el;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
build_productData(o) {
|
|
109
|
+
if (o.fallback) return this.text(o.text);
|
|
110
|
+
const el = document.createElement("shopify-data");
|
|
111
|
+
el.setAttribute("query", o.query ?? DEFAULT_QUERY.productData);
|
|
112
|
+
o.text != null && (el.textContent = o.text);
|
|
113
|
+
return el;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
build_productMedia(o) {
|
|
117
|
+
if (o.fallback) {
|
|
118
|
+
const img = document.createElement("img");
|
|
119
|
+
o.url && img.setAttribute("src", o.url);
|
|
120
|
+
img.setAttribute("alt", o.alt ?? "");
|
|
121
|
+
o.width && img.setAttribute("width", String(o.width));
|
|
122
|
+
o.height && img.setAttribute("height", String(o.height));
|
|
123
|
+
return img;
|
|
124
|
+
}
|
|
125
|
+
const el = document.createElement("shopify-media");
|
|
126
|
+
el.setAttribute("query", o.query ?? DEFAULT_QUERY.productMedia);
|
|
127
|
+
o.width && el.setAttribute("width", String(o.width));
|
|
128
|
+
o.height && el.setAttribute("height", String(o.height));
|
|
129
|
+
if (o.url) {
|
|
130
|
+
const img = document.createElement("img");
|
|
131
|
+
img.setAttribute("src", o.url);
|
|
132
|
+
img.setAttribute("alt", o.alt ?? "");
|
|
133
|
+
el.appendChild(img);
|
|
134
|
+
}
|
|
135
|
+
return el;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
build_variantPicker(o) {
|
|
139
|
+
if (o.fallback) return this.text(o.text);
|
|
140
|
+
const el = document.createElement("shopify-variant-selector");
|
|
141
|
+
o.text != null && (el.textContent = o.text);
|
|
142
|
+
return el;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
build_buy(o) {
|
|
146
|
+
if (o.fallback) {
|
|
147
|
+
// No JavaScript, or not yet: a real link to the product on the
|
|
148
|
+
// shop, so buying never depends on the script loading.
|
|
149
|
+
const a = document.createElement("a");
|
|
150
|
+
o.url && a.setAttribute("href", o.url);
|
|
151
|
+
a.textContent = o.text ?? "Buy";
|
|
152
|
+
return a;
|
|
153
|
+
}
|
|
154
|
+
const button = document.createElement("button");
|
|
155
|
+
button.setAttribute("type", "button");
|
|
156
|
+
button.textContent = o.text ?? "Add to cart";
|
|
157
|
+
const cart = safeId(o.cart, "cart");
|
|
158
|
+
const store = safeId(o.store, "store");
|
|
159
|
+
// An attribute, not a listener: a product's children are cloned out
|
|
160
|
+
// of its <template> by Shopify, and cloning drops listeners.
|
|
161
|
+
button.setAttribute("onclick", o.mode === "buyNow"
|
|
162
|
+
? `document.getElementById('${store}').buyNow(event)`
|
|
163
|
+
: `document.getElementById('${cart}').addLine(event).showModal()`);
|
|
164
|
+
return button;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
build_cart(o) {
|
|
168
|
+
const el = document.createElement("shopify-cart");
|
|
169
|
+
o.target && el.setAttribute("target", o.target);
|
|
170
|
+
o.discountCodes && el.setAttribute("discount-codes", o.discountCodes);
|
|
171
|
+
const id = safeId(o.id, "cart");
|
|
172
|
+
if (o.theme) Shop.themeCart(id, o.theme);
|
|
173
|
+
return el;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ── helpers ────────────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
text(value) {
|
|
179
|
+
const span = document.createElement("span");
|
|
180
|
+
span.textContent = value ?? "";
|
|
181
|
+
return span;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Shopify's script, once per document. */
|
|
185
|
+
static loadScript() {
|
|
186
|
+
if (typeof document === "undefined" || !document.head) return;
|
|
187
|
+
if (document.querySelector(`script[src="${SCRIPT}"]`)) return;
|
|
188
|
+
const s = document.createElement("script");
|
|
189
|
+
s.setAttribute("type", "module");
|
|
190
|
+
s.setAttribute("src", SCRIPT);
|
|
191
|
+
document.head.appendChild(s);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The cart draws in its own shadow DOM, styled from outside only through
|
|
196
|
+
* the parts Shopify exposes. The theme becomes ::part() rules in one
|
|
197
|
+
* <style> per cart, replaced rather than duplicated on a re-render.
|
|
198
|
+
*/
|
|
199
|
+
static themeCart(id, t) {
|
|
200
|
+
if (typeof document === "undefined" || !document.head) return;
|
|
201
|
+
const sel = `#${id}`;
|
|
202
|
+
const decl = (pairs) => Object.entries(pairs)
|
|
203
|
+
.filter(([, v]) => v != null && v !== "")
|
|
204
|
+
.map(([k, v]) => `${k}: ${String(v).replace(/[;{}<>]/g, "")}`).join("; ");
|
|
205
|
+
const css = [
|
|
206
|
+
`${sel} { ${decl({ "font-family": t.font })} }`,
|
|
207
|
+
`${sel}::part(dialog) { ${decl({ background: t.background, color: t.color, "font-family": t.font, "border-radius": t.radius })} }`,
|
|
208
|
+
`${sel}::part(primary-button) { ${decl({ background: t.accent, color: t.accentText, "font-family": t.font, "border-radius": t.buttonRadius ?? t.radius })} }`,
|
|
209
|
+
`${sel}::part(secondary-button) { ${decl({ color: t.color, "font-family": t.font })} }`,
|
|
210
|
+
`${sel}::part(line-heading) { ${decl({ "font-family": t.headingFont ?? t.font })} }`,
|
|
211
|
+
].join("\n");
|
|
212
|
+
const key = `nodality-cart-${id}`;
|
|
213
|
+
let style = document.head.querySelector(`style[data-shop="${key}"]`);
|
|
214
|
+
if (!style) {
|
|
215
|
+
style = document.createElement("style");
|
|
216
|
+
style.setAttribute("data-shop", key);
|
|
217
|
+
document.head.appendChild(style);
|
|
218
|
+
}
|
|
219
|
+
style.textContent = css;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── children ───────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
/** Live children. A product's go into its <template>. */
|
|
225
|
+
add(els = []) {
|
|
226
|
+
this.items = els;
|
|
227
|
+
const into = this.template ? this.template.content : this.res;
|
|
228
|
+
for (const el of els) if (el && typeof el.render === "function") into.appendChild(el.render());
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Static children, shown until the product's data arrives. */
|
|
233
|
+
fallback(els = []) {
|
|
234
|
+
this.fallbackItems = els;
|
|
235
|
+
const into = this.placeholder || this.res;
|
|
236
|
+
for (const el of els) if (el && typeof el.render === "function") into.appendChild(el.render());
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
toCode() {
|
|
241
|
+
const opts = Object.fromEntries(Object.entries(this.options || {}).filter(([, v]) => v != null));
|
|
242
|
+
const kids = (list) => list
|
|
243
|
+
.filter((e) => e && typeof e.toCode === "function")
|
|
244
|
+
.map((e) => [].concat(e.toCode()).join(""))
|
|
245
|
+
.join(",\n");
|
|
246
|
+
let code = `new Shop().set(${toObjectSource(opts, 4)})`;
|
|
247
|
+
if (this.items.length) code += `.add([${kids(this.items)}])`;
|
|
248
|
+
if (this.fallbackItems.length) code += `.fallback([${kids(this.fallbackItems)}])`;
|
|
249
|
+
return [code];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
render(selector) {
|
|
253
|
+
if (selector) {
|
|
254
|
+
const parent = document.querySelector(selector);
|
|
255
|
+
parent && parent.appendChild(this.res);
|
|
256
|
+
}
|
|
257
|
+
return this.res;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export { Shop, SCRIPT as SHOP_SCRIPT };
|
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
package/layout/video.js
CHANGED
package/layout/wrap.js
CHANGED
package/layout/zoom-card.js
CHANGED
package/lib/agent-surface.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
|
*/
|
|
@@ -315,6 +315,28 @@ export function deriveSurface(elements, nodes, { specDraft = null } = {}) {
|
|
|
315
315
|
});
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
// ── products, allow-listed by id ─────────────────────────────────
|
|
319
|
+
//
|
|
320
|
+
// Read-only. An agent can learn what is for sale, at what price and in
|
|
321
|
+
// which options; it cannot put anything in a cart. Paying stays a human
|
|
322
|
+
// act on the shop's own checkout.
|
|
323
|
+
const productIds = (Array.isArray(node.products) ? node.products : [])
|
|
324
|
+
.filter((s) => typeof s === "string").map((s) => s.replace(/^#/, ""));
|
|
325
|
+
for (const id of productIds) {
|
|
326
|
+
const product = byId.get(id) || byId.get(`#${id}`);
|
|
327
|
+
if (!product || product.type !== "product") continue;
|
|
328
|
+
tools.push({
|
|
329
|
+
name: named(`get_product_${id.replace(/[^a-zA-Z0-9_-]/g, "_")}`),
|
|
330
|
+
kind: "product",
|
|
331
|
+
productId: id,
|
|
332
|
+
description:
|
|
333
|
+
`Read the product "${product.handle}" as it is shown on this page: ` +
|
|
334
|
+
"name, price for the visitor's market, options and availability. " +
|
|
335
|
+
"Read-only; buying happens on the shop's checkout.",
|
|
336
|
+
inputSchema: { type: "object", properties: {} },
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
318
340
|
// ── what is on screen ────────────────────────────────────────────
|
|
319
341
|
tools.push({
|
|
320
342
|
name: named("read_view"),
|
|
@@ -331,7 +353,7 @@ export function deriveSurface(elements, nodes, { specDraft = null } = {}) {
|
|
|
331
353
|
// deciding whether to visit at all can actually read.
|
|
332
354
|
const manifest = {
|
|
333
355
|
spec: specDraft,
|
|
334
|
-
tools: tools.map(({ kind, formId, ...decl }) => decl),
|
|
356
|
+
tools: tools.map(({ kind, formId, productId, ...decl }) => decl),
|
|
335
357
|
...(graph
|
|
336
358
|
? {
|
|
337
359
|
views: {
|
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
|
@@ -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
|
*/
|
|
@@ -76,6 +76,7 @@ import { AreaSwitcher } from "../layout/grid-switcher.js";
|
|
|
76
76
|
import { Polygon } from "../layout/polygon.js";
|
|
77
77
|
import { Circle } from "../layout/circle.js";
|
|
78
78
|
import { GridOverlay } from "../layout/grid-overlay.js";
|
|
79
|
+
import { Shop } from "../layout/shop.js";
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
|
|
@@ -1446,7 +1447,7 @@ new Slider(texts, null, { tintColor: "#e74c3c", inactiveColor: "#ccc" })
|
|
|
1446
1447
|
Text, Image, Link, FlexRow, UINavBar, Free, Audio, Progress, Center, Code,
|
|
1447
1448
|
Stack, Wrapper, MetaAdder, Table, Dropdown, Modal, TextField, Card,
|
|
1448
1449
|
Wrap, FlexGrid, ZoomCard, Switcher, MobileBar, DesktopBar, SideNav, Spacer, HScroller, Polygon, Circle, UList,
|
|
1449
|
-
GridOverlay
|
|
1450
|
+
GridOverlay, Shop
|
|
1450
1451
|
};
|
|
1451
1452
|
|
|
1452
1453
|
const formComponents = {
|