webcake-landing-mcp 1.0.30 → 1.0.31
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
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"v": "1.0.31",
|
|
4
|
+
"d": "08/06/2026",
|
|
5
|
+
"type": "Fixed",
|
|
6
|
+
"en": "get_element for spin_wheel now correctly documents specials.code as a newline-delimited string (one line per segment in couponCode|Prize…",
|
|
7
|
+
"vi": "get_element cho spin_wheel nay ghi lại đúng specials.code là chuỗi phân cách bằng dấu xuống dòng (mỗi dòng một segment theo định dạng couponCode|Tên…"
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"v": "1.0.30",
|
|
4
11
|
"d": "08/06/2026",
|
|
@@ -33,12 +40,5 @@
|
|
|
33
40
|
"type": "Added",
|
|
34
41
|
"en": "The HTTP server now serves a pre-rendered 1200×630 PNG social card at GET /og.png; the guide page's og:image and twitter:image meta tags now point…",
|
|
35
42
|
"vi": "HTTP server nay phục vụ ảnh social card PNG được render sẵn 1200×630 tại GET /og.png; các meta tag og:image và twitter:image của trang hướng dẫn nay…"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"v": "1.0.25",
|
|
39
|
-
"d": "07/06/2026",
|
|
40
|
-
"type": "Changed",
|
|
41
|
-
"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,…",
|
|
42
|
-
"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í,…"
|
|
43
43
|
}
|
|
44
44
|
]
|
|
@@ -152,9 +152,9 @@ export const CONTENT = [
|
|
|
152
152
|
{
|
|
153
153
|
type: "gallery", category: "content", container: false, defaultName: "Gallery",
|
|
154
154
|
summary: "Multi-image/video gallery with thumbnail strip. Content comes entirely from specials.media — this is NOT a container and has no children.",
|
|
155
|
-
useWhen: "Photo grids/galleries with several images or videos. No image API — fill specials.media with placeholder
|
|
155
|
+
useWhen: "Photo grids/galleries with several images or videos. No image API — fill specials.media with placeholder image objects (see media). NEVER use plain URL strings — the gallery reads item.link and renders blank for a string.",
|
|
156
156
|
keySpecials: {
|
|
157
|
-
media: "array of
|
|
157
|
+
media: "array of media OBJECTS (NOT plain URLs — the gallery reads item.link). Image item: {type:'image', link:'<url>', linkVideo:'', typeVideo:'youtube', imageCompression:true} — use a https://placehold.co/WxH URL for link if no real image. Video item: {type:'image', link:'<poster-url>', linkVideo:'<video-url>', typeVideo:'youtube'|'upload', imageCompression:true}.",
|
|
158
158
|
allowZoom: "'off' | 'carousel' | 'lightbox' — (config) zoom/lightbox mode when clicking an image.",
|
|
159
159
|
showNavigation: "boolean — (config) show prev/next navigation arrows.",
|
|
160
160
|
thumbnailAutoplay: "number (ms) | 'off' — (config) auto-advance thumbnails every N ms, or 'off' to disable.",
|
|
@@ -167,7 +167,15 @@ export const CONTENT = [
|
|
|
167
167
|
seed: (el) => {
|
|
168
168
|
seedPosition(el);
|
|
169
169
|
setBox(el, 350, 400);
|
|
170
|
-
|
|
170
|
+
// gallery media items are OBJECTS, not strings — the renderer reads item.link
|
|
171
|
+
// (a plain URL string renders blank). Shape mirrors the editor's seed.
|
|
172
|
+
el.specials.media = [1, 2, 3].map((n) => ({
|
|
173
|
+
type: "image",
|
|
174
|
+
link: imgPlaceholder(600, 400, String(n)),
|
|
175
|
+
linkVideo: "",
|
|
176
|
+
typeVideo: "youtube",
|
|
177
|
+
imageCompression: true,
|
|
178
|
+
}));
|
|
171
179
|
// gallery has NO children — content comes entirely from specials.media (gallery.js never reads vm.children)
|
|
172
180
|
},
|
|
173
181
|
},
|
|
@@ -105,9 +105,9 @@ export const MARKETING = [
|
|
|
105
105
|
summary: "Lucky-spin wheel with configurable prize segments and coupon codes. Can open a result popup after spinning and supports dataset-driven coupon lists.",
|
|
106
106
|
useWhen: "Gamified lead capture / promos. Users spin to win a coupon or prize.",
|
|
107
107
|
keySpecials: {
|
|
108
|
-
message: "
|
|
108
|
+
message: "string — result-popup message template shown after a spin (NOT segment labels). Supports placeholders {{coupon_text}}, {{coupon_code}}, {{spin_turn_left}}, {{coupon_codes}}.",
|
|
109
109
|
spin: "object — spin configuration (segment colors, angles, etc.).",
|
|
110
|
-
code: "
|
|
110
|
+
code: "string (NOT an array) — the segments. ONE LINE PER SEGMENT, each line `couponCode|Prize Name|percent`, lines joined by \\n. The visible label on each wheel slice is the middle field (Prize Name); percent is the win weight. e.g. 'SALE10|Giảm 10%|40\\nSALE50|Giảm 50%|10\\nMISS|Chúc may mắn|50'.",
|
|
111
111
|
dataType: "0 | 1 — 0=static codes, 1=dataset-driven codes.",
|
|
112
112
|
datasetId: "string — webcake dataset ID for coupon codes.",
|
|
113
113
|
codeDataset: "string — dataset column key for the coupon code.",
|
|
@@ -123,6 +123,16 @@ export const MARKETING = [
|
|
|
123
123
|
seedPosition(el);
|
|
124
124
|
setBox(el, 400, 400);
|
|
125
125
|
setStyle(el, "color", "rgba(255, 255, 255, 1)");
|
|
126
|
+
// `code` is a newline-delimited string, ONE segment per line `couponCode|Prize Name|percent`
|
|
127
|
+
// (the editor preview does code.split("\n") and crashes/renders blank if it is missing).
|
|
128
|
+
el.specials.code = [
|
|
129
|
+
"PRIZE1|Giải 1|20",
|
|
130
|
+
"PRIZE2|Giải 2|20",
|
|
131
|
+
"PRIZE3|Giải 3|20",
|
|
132
|
+
"MISS|Chúc may mắn|40",
|
|
133
|
+
].join("\n");
|
|
134
|
+
// `message` is the result-popup template (a string), NOT segment labels.
|
|
135
|
+
el.specials.message = "Chúc mừng! Bạn nhận được {{coupon_text}} (mã: {{coupon_code}}).";
|
|
126
136
|
},
|
|
127
137
|
},
|
|
128
138
|
{
|
|
@@ -78,7 +78,7 @@ SECTION BUILD HINTS (apply to whichever sections the chosen archetype uses)
|
|
|
78
78
|
RULES
|
|
79
79
|
- Visible content goes in "specials" (text-block.specials.text, image-block.specials.src…), NEVER in "styles".
|
|
80
80
|
- Colors as rgba(r,g,b,a). fontSize/borderWidth/top/left/width/height are NUMBERS (px).
|
|
81
|
-
- IMAGES: a real landing page has images (hero/product shot, feature icons, about photo). There is NO image API yet, so set image-block specials.src to a PLACEHOLDER URL sized to the box: "https://placehold.co/<width>x<height>". NEVER leave src empty — it renders blank and the page looks broken. gallery.media = array of
|
|
81
|
+
- IMAGES: a real landing page has images (hero/product shot, feature icons, about photo). There is NO image API yet, so set image-block specials.src to a PLACEHOLDER URL sized to the box: "https://placehold.co/<width>x<height>". NEVER leave src empty — it renders blank and the page looks broken. gallery.media = array of OBJECTS {type:'image', link:'<placeholder-url>', linkVideo:'', typeVideo:'youtube', imageCompression:true} (NOT plain URL strings — the gallery reads item.link); video.specials.img = a poster placeholder. The user replaces these later.
|
|
82
82
|
- CONTRAST: text must contrast with the section background (dark text on light sections, light text on dark sections). Don't put light-gray text on white or faint text on a dark background.
|
|
83
83
|
- movable:false for section/slide/grid-item/popup; otherwise true. runtime is always {}.
|
|
84
84
|
- Every form input MUST have a unique specials.field_name.
|
|
@@ -20,6 +20,6 @@ MODEL (essentials):
|
|
|
20
20
|
- 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.
|
|
21
21
|
- 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.
|
|
22
22
|
- Visible content lives in specials (text, src, field_name…), never in styles. Colors as rgba(). Animation in config.animation={name,delay,duration,repeat}. Form inputs need a unique specials.field_name (use canonical keys: full_name, phone_number, email, address, quantity).
|
|
23
|
-
- IMAGES: include them (hero/product, feature icons, about photo). No image API yet → set image-block specials.src to a PLACEHOLDER sized to the box: https://placehold.co/<width>x<height> (gallery.media = array of
|
|
23
|
+
- IMAGES: include them (hero/product, feature icons, about photo). No image API yet → set image-block specials.src to a PLACEHOLDER sized to the box: https://placehold.co/<width>x<height> (gallery.media = array of OBJECTS {type:'image',link:'<url>',linkVideo:'',typeVideo:'youtube',imageCompression:true} — NOT plain strings, the gallery reads item.link; video.specials.img = poster). NEVER leave src empty (renders blank). Ensure text contrasts with its section background.
|
|
24
24
|
|
|
25
25
|
Start by calling get_generation_guide. Tools: get_generation_guide, list_elements, get_element, new_element, new_page_skeleton, get_page_schema, validate_page, list_organizations, create_page, list_pages, get_page, update_page.`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webcake-landing-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
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
|
"license": "MIT",
|