webcake-landing-mcp 1.0.23 → 1.0.25
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/dist/changelog.json +14 -14
- package/dist/domains/landing/elements/form.js +4 -1
- package/dist/domains/landing/guide.js +4 -3
- package/dist/domains/landing/instructions.js +1 -1
- package/dist/domains/landing/page-schema.json +6 -2
- package/dist/domains/landing/page.js +17 -4
- package/dist/domains/landing/vocab.js +1 -1
- package/dist/web-guide.js +11 -5
- package/package.json +1 -1
package/dist/changelog.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"v": "1.0.25",
|
|
4
|
+
"d": "07/06/2026",
|
|
5
|
+
"type": "Changed",
|
|
6
|
+
"en": "currency has moved from options.currency to settings.currency in the page source model; new_page_skeleton now emits it in the correct location,…",
|
|
7
|
+
"vi": "currency đã được chuyển từ options.currency sang settings.currency trong mô hình nguồn trang; new_page_skeleton giờ xuất đúng vị trí,…"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"v": "1.0.24",
|
|
11
|
+
"d": "07/06/2026",
|
|
12
|
+
"type": "Changed",
|
|
13
|
+
"en": "The numbered installation steps on the GET / guide page now display a faint vertical connector line between step numbers, giving the list a clear…",
|
|
14
|
+
"vi": "Các bước cài đặt được đánh số trên trang hướng dẫn GET / nay hiển thị đường kết nối dọc mờ giữa các số thứ tự, tạo giao diện stepper rõ ràng."
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"v": "1.0.23",
|
|
4
18
|
"d": "07/06/2026",
|
|
@@ -26,19 +40,5 @@
|
|
|
26
40
|
"type": "Changed",
|
|
27
41
|
"en": "get_generation_guide now includes a Layout Archetypes block that maps seven page types (sales/COD, lead-gen/service, event/invitation, app/SaaS…",
|
|
28
42
|
"vi": "get_generation_guide now includes a Layout Archetypes block that maps seven page types (sales/COD, lead-gen/service, event/invitation, app/SaaS…"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"v": "1.0.19",
|
|
32
|
-
"d": "07/06/2026",
|
|
33
|
-
"type": "Changed",
|
|
34
|
-
"en": "get_generation_guide now includes a Section Playbook block that lists a common section menu for lead-gen and COD sales pages (header, hero,…",
|
|
35
|
-
"vi": "get_generation_guide nay có thêm khối \"Section Playbook\" liệt kê bộ section thường dùng cho trang thu lead và bán hàng COD (header, hero, tính…"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"v": "1.0.18",
|
|
39
|
-
"d": "07/06/2026",
|
|
40
|
-
"type": "Changed",
|
|
41
|
-
"en": "new_element for list-product now seeds a default styles.colorBtn (rgba(246,4,87,1)) so generated product-list button labels have a visible accent…",
|
|
42
|
-
"vi": "new_element cho list-product nay gán sẵn styles.colorBtn mặc định (rgba(246,4,87,1)) để nhãn nút của danh sách sản phẩm có màu nhấn nhìn thấy được…"
|
|
43
43
|
}
|
|
44
44
|
]
|
|
@@ -276,6 +276,7 @@ export const FORM = [
|
|
|
276
276
|
field_type: "'postal_code' | absent — 'postal_code' switches validation from phone OTP to a postal-code regex.",
|
|
277
277
|
condition: "'limit_5' | 'limit_6' | 'custom' — postal-code regex selector (active when field_type='postal_code').",
|
|
278
278
|
pattern: "string regex — custom postal-code pattern when condition='custom'.",
|
|
279
|
+
message_otp_wrong: "string — custom error message shown when the entered OTP is wrong (read by the form on submit-error). Optional.",
|
|
279
280
|
},
|
|
280
281
|
seed: (el) => {
|
|
281
282
|
seedPosition(el);
|
|
@@ -296,12 +297,14 @@ export const FORM = [
|
|
|
296
297
|
},
|
|
297
298
|
{
|
|
298
299
|
type: "group-select-item", category: "form", container: false, field: true, defaultName: "Group Select Item",
|
|
299
|
-
summary: "One attribute (or the quantity) inside group-select.
|
|
300
|
+
summary: "One attribute (or the quantity) inside group-select. Attribute items populate options from the product catalog (window.sync.products) at runtime based on attrName + the parent's sprod; the quantity item carries a STATIC options array.",
|
|
300
301
|
useWhen: "Child of group-select only.",
|
|
301
302
|
keySpecials: {
|
|
302
303
|
field_name: "REQUIRED unique data key (becomes 'quantity' when field_quantity=true).",
|
|
304
|
+
field_placeholder: "string — the item's visible label/placeholder (the editor seeds 'AttrName' for attribute items, 'Quantity' for the quantity item).",
|
|
303
305
|
field_quantity: "boolean — when true this item is the quantity selector (value goes to parent._setQuantity), not a product attribute. Only one item per group.",
|
|
304
306
|
attrName: "string — the product attribute name this item maps to (e.g. 'Color','Size'); numeric strings ('1','2') index product_attributes for custom products; 'sprod-name'/'sprod-sku' show the product name/SKU.",
|
|
307
|
+
options: "array [{id,name,value}] — STATIC option list used by the quantity item (field_quantity=true), e.g. 1..4; attribute items leave this empty and populate from the catalog at runtime.",
|
|
305
308
|
default_value: "string — pre-selected option value, or 'default-none'/empty for no default.",
|
|
306
309
|
required: "boolean — require selection when the item and its parent group are visible.",
|
|
307
310
|
},
|
|
@@ -8,12 +8,13 @@ export const GENERATION_GUIDE = `You are generating the JSON source of a Webcake
|
|
|
8
8
|
|
|
9
9
|
OUTPUT (top-level page source — matches the real editor shape)
|
|
10
10
|
- Return ONE JSON object:
|
|
11
|
-
{ "page": [<section>...], "popup": [<popup>...], "settings": {...},
|
|
12
|
-
"options": { "
|
|
11
|
+
{ "page": [<section>...], "popup": [<popup>...], "dynamic_pages": [], "settings": {...},
|
|
12
|
+
"options": { "mobileOnly":false, "versionID":null }, "cartConfigs": { "isActive":false }, "svariations": [] }
|
|
13
|
+
- "dynamic_pages" (dataset-bound sections) and "svariations" (product/variation data) stay [] for a static page — keep them when editing so commerce data isn't dropped. currency lives in settings.currency, NOT options.
|
|
13
14
|
- "page" is an array of SECTIONS stacked vertically (index 0 = top). Each item MUST be type "section" (or "dynamic_page").
|
|
14
15
|
- "popup" is a SEPARATE top-level array of popup elements — do NOT nest popups inside "page". A button opens one via a click event { action:"open_popup", target:"<popup id>" }.
|
|
15
16
|
- All other elements (text, image, button, form…) live inside a section's "children".
|
|
16
|
-
- "settings" carries SEO + page config: title, description, keywords, favicon, fontGeneral, width_section {desktop:960,mobile:420}, country, fb_tracking_code, tiktok_script, extra_css, extra_script (call new_page_skeleton for a ready default).
|
|
17
|
+
- "settings" carries SEO + page config: title, description, keywords, robots, canonical, favicon, fontGeneral, width_section {desktop:960,mobile:420}, country, currency, fb_tracking_code, tiktok_script, extra_css, extra_script, bhet (head code), bbet (body-end code) (call new_page_skeleton for a ready default).
|
|
17
18
|
|
|
18
19
|
ELEMENT NODE (every element)
|
|
19
20
|
{ "id": "<unique ~8-char [A-Za-z0-9_]>", "type": "<type>",
|
|
@@ -14,7 +14,7 @@ RULES (follow for every request):
|
|
|
14
14
|
- Organizations: call list_organizations and ask which to use; default to the is_default org. Endpoints are owner-scoped (only the account's own pages).
|
|
15
15
|
|
|
16
16
|
MODEL (essentials):
|
|
17
|
-
- Top-level: { page:[sections], popup:[popups], settings:{}, options:{
|
|
17
|
+
- Top-level: { page:[sections], popup:[popups], dynamic_pages:[], settings:{}, options:{mobileOnly,versionID}, cartConfigs:{isActive:false}, svariations:[] }. Popups are a SEPARATE top-level array, NOT inside page; currency lives in settings.currency (not options). Leave dynamic_pages/svariations as [] for a static page, but keep them on edit round-trips.
|
|
18
18
|
- Element: { id, type, properties, responsive:{desktop,mobile:{config,styles}}, specials, children, runtime, events }. Absolute canvas: children carry numeric top/left/width/height (px) per breakpoint (canvas width desktop=960, mobile=420); sections own a height.
|
|
19
19
|
- CENTERING (the #1 layout defect — do the math, don't eyeball): to center a box compute left = round((canvas - width)/2) — 960 desktop, 420 mobile. textAlign:center only centers text inside the box, not the box itself. For a row of N items, center the whole row block (startLeft = round((canvas - (N*item + (N-1)*gap))/2)). Keep 0 ≤ left and left+width ≤ canvas on each breakpoint.
|
|
20
20
|
- STICKY HEADER: a sticky/fixed header (config.sticky) OVERLAYS the page — it does NOT push sections below it down. Offset the first section's top content DOWN by the header height (~60–72px) so nothing hides behind it, and do NOT duplicate the shop name in both the header and the top of the hero. A non-sticky header stacks normally and needs no offset.
|
|
@@ -24,10 +24,9 @@
|
|
|
24
24
|
},
|
|
25
25
|
"options": {
|
|
26
26
|
"type": "object",
|
|
27
|
-
"description": "Editor options.",
|
|
27
|
+
"description": "Editor options: { mobileOnly, versionID }. NOTE: currency lives in settings.currency, not here.",
|
|
28
28
|
"additionalProperties": true,
|
|
29
29
|
"properties": {
|
|
30
|
-
"currency": { "type": "string", "examples": ["VND", "USD"] },
|
|
31
30
|
"mobileOnly": { "type": "boolean", "description": "true = page only renders the mobile breakpoint." },
|
|
32
31
|
"versionID": { "type": ["string", "null"] }
|
|
33
32
|
}
|
|
@@ -51,6 +50,11 @@
|
|
|
51
50
|
"properties": { "desktop": { "type": "number" }, "mobile": { "type": "number" } }
|
|
52
51
|
},
|
|
53
52
|
"country": { "type": "string", "description": "Dialing/locale code, e.g. \"84\"." },
|
|
53
|
+
"currency": { "type": "string", "description": "Page currency (the editor's canonical home for currency).", "examples": ["VND", "USD"] },
|
|
54
|
+
"robots": { "type": "string", "description": "SEO robots meta directive." },
|
|
55
|
+
"canonical": { "type": "string", "description": "SEO canonical URL." },
|
|
56
|
+
"bhet": { "type": "string", "description": "Custom code injected at the end of <head>." },
|
|
57
|
+
"bbet": { "type": "string", "description": "Custom code injected before </body>." },
|
|
54
58
|
"fb_tracking_code": { "type": "string", "description": "Facebook pixel id." },
|
|
55
59
|
"tiktok_script": { "type": "string" },
|
|
56
60
|
"global_track_ids": { "type": "array" },
|
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
/** Default page-level settings (subset of the ~
|
|
1
|
+
/** Default page-level settings (subset of the ~44 real keys; covers the essentials). */
|
|
2
2
|
export function defaultSettings(overrides = {}) {
|
|
3
3
|
return {
|
|
4
4
|
title: "",
|
|
5
5
|
description: "",
|
|
6
6
|
keywords: "",
|
|
7
|
+
robots: "",
|
|
8
|
+
canonical: "",
|
|
7
9
|
favicon: "",
|
|
8
10
|
thumbnail: "",
|
|
9
11
|
fontGeneral: "'Roboto', sans-serif",
|
|
10
12
|
width_section: { desktop: 960, mobile: 420 },
|
|
11
13
|
country: "84",
|
|
14
|
+
// currency lives in settings (the editor's canonical home), NOT in options.
|
|
15
|
+
currency: "VND",
|
|
12
16
|
fb_tracking_code: "",
|
|
13
17
|
tiktok_script: "",
|
|
14
18
|
global_track_ids: [],
|
|
19
|
+
global_tracks: {}, // the editor always re-writes this to {} on save.
|
|
15
20
|
extra_css: "",
|
|
16
21
|
extra_script: "",
|
|
22
|
+
bhet: "", // custom code injected at the end of <head>.
|
|
23
|
+
bbet: "", // custom code injected before </body>.
|
|
24
|
+
global_compress_image: { enable: true, option: 300, keep_solution: false },
|
|
17
25
|
auto_save_draft: true,
|
|
18
26
|
auto_save_info_user: false,
|
|
19
27
|
send_info_to_thank_page: true,
|
|
@@ -22,14 +30,19 @@ export function defaultSettings(overrides = {}) {
|
|
|
22
30
|
}
|
|
23
31
|
/**
|
|
24
32
|
* Build a complete, empty top-level page source matching the real editor shape:
|
|
25
|
-
* { page, popup, settings, options, cartConfigs }.
|
|
33
|
+
* { page, popup, dynamic_pages, settings, options, cartConfigs, svariations }.
|
|
34
|
+
* Fill `page` with sections. `dynamic_pages`/`svariations` stay empty for static,
|
|
35
|
+
* non-commerce pages but are emitted so edit round-trips don't drop them, and
|
|
36
|
+
* `options` carries only { mobileOnly, versionID } (currency lives in settings).
|
|
26
37
|
*/
|
|
27
38
|
export function createPageSource(opts = {}) {
|
|
28
39
|
return {
|
|
29
40
|
page: [],
|
|
30
41
|
popup: [],
|
|
42
|
+
dynamic_pages: [],
|
|
31
43
|
settings: defaultSettings(opts.settings ?? {}),
|
|
32
|
-
options: {
|
|
33
|
-
cartConfigs: {},
|
|
44
|
+
options: { mobileOnly: opts.mobileOnly ?? false, versionID: null },
|
|
45
|
+
cartConfigs: { isActive: false },
|
|
46
|
+
svariations: [],
|
|
34
47
|
};
|
|
35
48
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Landing-page vocabulary: the fixed canvas, the event triggers, and the
|
|
3
3
|
* per-trigger action maps. "Extra:" notes list the action-specific event-object
|
|
4
4
|
* fields the render_v4 dispatcher reads beyond { id, type, action, target }.
|
|
5
|
-
* Derived from assets/render_v4/
|
|
5
|
+
* Derived from assets/render_v4/event/index.js.
|
|
6
6
|
*/
|
|
7
7
|
export const CANVAS = { desktopWidth: 960, mobileWidth: 420, defaultSectionHeight: 800 };
|
|
8
8
|
export const EVENT_TRIGGERS = ["click", "hover", "success", "error", "unset", "delay"];
|
package/dist/web-guide.js
CHANGED
|
@@ -524,18 +524,24 @@ export function guideHtml(origin, lang = "vi") {
|
|
|
524
524
|
.method{margin-bottom:16px;padding:24px}
|
|
525
525
|
.method>.tag{margin-bottom:4px}
|
|
526
526
|
.msub{color:var(--mut);font-size:.92rem;margin:.5rem 0 1.2rem}
|
|
527
|
-
.steps{list-style:none;margin:0;padding:0;display:grid;gap:
|
|
528
|
-
.steps li{display:flex;gap:14px}
|
|
527
|
+
.steps{list-style:none;margin:0;padding:0;display:grid;gap:18px;position:relative}
|
|
528
|
+
.steps li{display:flex;gap:14px;align-items:flex-start;position:relative}
|
|
529
|
+
/* Faint connector between step numbers → reads as an intentional stepper */
|
|
530
|
+
.steps li:not(:last-child)::after{content:"";position:absolute;left:13px;top:30px;bottom:-18px;width:2px;background:var(--line)}
|
|
529
531
|
.steps .n{flex:0 0 auto;width:28px;height:28px;border-radius:50%;color:var(--ic-fg);
|
|
530
532
|
background:rgba(29,185,84,.12);border:1px solid var(--line);
|
|
531
533
|
font:800 .85rem/1 system-ui;display:flex;align-items:center;justify-content:center}
|
|
532
534
|
.steps .body{flex:1;min-width:0;font-size:.95rem}
|
|
533
535
|
.steps .body pre{margin-top:9px}
|
|
534
|
-
|
|
536
|
+
/* A button inside a step drops to its own left-aligned line (inline-flex would
|
|
537
|
+
sit beside the text and be shoved off-baseline by the top margin). */
|
|
538
|
+
.steps .body .btn{display:flex;width:fit-content;margin-top:10px}
|
|
535
539
|
code.inl{background:rgba(29,185,84,.13);color:var(--g7);padding:1px 6px;border-radius:6px;font-size:.85em;font-weight:600;
|
|
536
540
|
overflow-wrap:anywhere;word-break:break-word}
|
|
537
541
|
.note{font-size:.86rem;color:var(--mut);margin-top:10px}
|
|
538
542
|
.note + pre,.note + .codewrap{margin-top:9px}
|
|
543
|
+
.tip{margin-top:16px;background:rgba(29,185,84,.06);border:1px solid var(--line);border-radius:12px;padding:13px 15px}
|
|
544
|
+
.tip .note{margin:0}
|
|
539
545
|
details{padding:2px 18px;margin-bottom:11px}
|
|
540
546
|
details summary{cursor:pointer;font-weight:600;padding:15px 0;list-style:none;display:flex;align-items:center;gap:10px}
|
|
541
547
|
details summary::-webkit-details-marker{display:none}
|
|
@@ -584,6 +590,7 @@ export function guideHtml(origin, lang = "vi") {
|
|
|
584
590
|
.lead{font-size:1.05rem}
|
|
585
591
|
.method{padding:18px 15px}
|
|
586
592
|
.card{padding:18px}
|
|
593
|
+
.tip{padding:11px 12px}
|
|
587
594
|
.cl-wrap{padding:18px 16px 10px}
|
|
588
595
|
.langsw{padding:6px 10px}
|
|
589
596
|
.uses li,.feat li{padding:14px}
|
|
@@ -666,8 +673,7 @@ export function guideHtml(origin, lang = "vi") {
|
|
|
666
673
|
<ol class="steps">
|
|
667
674
|
${steps(t.m1Steps.map(fill))}
|
|
668
675
|
</ol>
|
|
669
|
-
<p class="note">${t.m1Note}</p>
|
|
670
|
-
<pre>${INSTALL_ALL_CMD}</pre>
|
|
676
|
+
<div class="tip"><p class="note">${t.m1Note}</p><pre>${INSTALL_ALL_CMD}</pre></div>
|
|
671
677
|
</div>
|
|
672
678
|
|
|
673
679
|
<div class="glass card method reveal">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webcake-landing-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "MCP server exposing Webcake landing-page element schemas + AI usage hints, and persisting LLM-generated page sources to a Webcake backend.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|