sbuilder-mcp 0.22.0 → 0.23.0
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/CHANGELOG.md +11 -0
- package/CHANGELOG.vi.md +11 -0
- package/README.md +1 -1
- package/README.vi.md +1 -1
- package/dist/domains/site/importmap.js +30 -0
- package/dist/domains/site/patterns.js +130 -0
- package/dist/tools/page.js +71 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ All notable changes to this project are documented in this file.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.23.0] - 2026-09-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- sb_templates now lists six built-in layout patterns (two hero variants, a feature trio, a stats row, a CTA band, and an FAQ) under `built_in`, alongside the site's own saved section templates, so a page can start from a designed composition instead of an invented one.
|
|
13
|
+
- sb_template_use can now instantiate a built-in layout by id: the section is composed against the target page's own tokens (heading color, button fill, section padding) instead of being copied verbatim, and falls back to the site's theme tokens, carried as `var(--wb-color-…)`, when the page has no look of its own yet.
|
|
14
|
+
|
|
15
|
+
## [0.22.1] - 2026-09-10
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- sb_import and sb_import_site no longer size an imported row's stacked columns by height at mobile: each column's `flex: 1 1 280px` basis is reset to `0 1 auto` on the mobile breakpoint only, so a short paragraph no longer sits inside a fixed 280px-tall box while the rest of the page is empty space.
|
|
19
|
+
|
|
9
20
|
## [0.22.0] - 2026-09-10
|
|
10
21
|
|
|
11
22
|
### Added
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,17 @@ Mọi thay đổi đáng chú ý của dự án được ghi lại trong file n
|
|
|
6
6
|
Định dạng dựa trên [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
và dự án tuân theo [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.23.0] - 2026-09-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- sb_templates giờ liệt kê sáu layout dựng sẵn (hai kiểu hero, ba lợi ích, dải số liệu, dải kêu gọi hành động và FAQ) trong `built_in`, cùng với các section template đã lưu của chính site, để một trang có thể bắt đầu từ một bố cục được thiết kế thay vì tự bịa ra.
|
|
13
|
+
- sb_template_use giờ có thể thả một layout dựng sẵn theo id: section được soạn theo token của chính trang đích (màu chữ tiêu đề, nền nút, padding section) thay vì được copy nguyên văn, và lấy theo token theme của site, mang dạng `var(--wb-color-…)`, khi trang chưa có phong cách riêng.
|
|
14
|
+
|
|
15
|
+
## [0.22.1] - 2026-09-10
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- sb_import và sb_import_site giờ không còn định kích thước các cột đã xếp chồng của một row đã import theo chiều cao ở mobile: basis `flex: 1 1 280px` của mỗi cột được reset về `0 1 auto` chỉ ở breakpoint mobile, nên một đoạn văn ngắn không còn bị nằm trong một khung cao cố định 280px trong khi phần còn lại của trang là khoảng trống.
|
|
19
|
+
|
|
9
20
|
## [0.22.0] - 2026-09-10
|
|
10
21
|
|
|
11
22
|
### Added
|
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ make, because those mean "this person's account".
|
|
|
86
86
|
| `sb_move` | Move a node to another parent |
|
|
87
87
|
| `sb_remove` | Remove a node and its subtree |
|
|
88
88
|
| `sb_duplicate` | Copy a node and its subtree under fresh ids, right after the original |
|
|
89
|
-
| `sb_templates` | The store's saved section templates —
|
|
89
|
+
| `sb_templates` | The store's saved section templates, plus the BUILT-IN layouts — hero, feature trio, stats, FAQ, CTA band — composed against the page's own tokens |
|
|
90
90
|
| `sb_template_use` | Instantiate a template into a page |
|
|
91
91
|
| `sb_page_list` | Every page on the site |
|
|
92
92
|
| `sb_page_create` | Create a page — a store type arrives with the editor's own starting document; `type` is the route for checkout, product, category, post, course |
|
package/README.vi.md
CHANGED
|
@@ -83,7 +83,7 @@ là "tài khoản của người này".
|
|
|
83
83
|
| `sb_move` | Chuyển node sang cha khác |
|
|
84
84
|
| `sb_remove` | Xoá node và cả cây con |
|
|
85
85
|
| `sb_duplicate` | Nhân bản một node và cả cây con dưới id mới, ngay sau bản gốc |
|
|
86
|
-
| `sb_templates` | Section template đã lưu của cửa hàng —
|
|
86
|
+
| `sb_templates` | Section template đã lưu của cửa hàng, cộng bộ layout DỰNG SẴN — hero, ba lợi ích, số liệu, FAQ, dải kêu gọi — soạn theo token của chính trang |
|
|
87
87
|
| `sb_template_use` | Thả một template vào trang |
|
|
88
88
|
| `sb_page_list` | Mọi trang của site |
|
|
89
89
|
| `sb_page_create` | Tạo một trang — trang cửa hàng sinh ra đã có sẵn tài liệu như trong editor; `type` là đường đi cho checkout, product, category, post, course |
|
|
@@ -294,6 +294,23 @@ function one(c, t) {
|
|
|
294
294
|
const kids = (c.children ?? []).map((k) => one(k, t)).filter((n) => n !== null);
|
|
295
295
|
if (kids.length === 0)
|
|
296
296
|
return null;
|
|
297
|
+
// A COLUMN IS A STACK, NOT A ROW, and this was read by nothing.
|
|
298
|
+
//
|
|
299
|
+
// The capture never emits one — a column is what a page already is, so it
|
|
300
|
+
// is flattened in the browser — and `direction` sat in `Captured` unread
|
|
301
|
+
// for exactly that reason. The moment a caller composes a tree by hand
|
|
302
|
+
// (a layout pattern, a design ported out of Figma or Stitch) that stops
|
|
303
|
+
// being true, and every column it asked for came back as a row: a hero's
|
|
304
|
+
// heading, its sentence and its button side by side instead of stacked.
|
|
305
|
+
if (c.direction === 'column') {
|
|
306
|
+
if (kids.length === 1)
|
|
307
|
+
return kids[0];
|
|
308
|
+
return {
|
|
309
|
+
type: 'flex-block',
|
|
310
|
+
style: { width: '100%', display: 'flex', flexDirection: 'column', gap: '12px' },
|
|
311
|
+
children: kids,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
297
314
|
if (kids.length === 1)
|
|
298
315
|
return kids[0];
|
|
299
316
|
// A ROW OF TWO OR MORE COLUMNS NEEDS AN EXPLICIT STACK BREAKPOINT, and
|
|
@@ -317,9 +334,22 @@ function one(c, t) {
|
|
|
317
334
|
gap: '24px',
|
|
318
335
|
},
|
|
319
336
|
responsive: { mobile: { style: { flexDirection: 'column', gap: '16px' } } },
|
|
337
|
+
// A COLUMN'S BASIS BECOMES ITS HEIGHT THE MOMENT THE ROW STACKS, and that
|
|
338
|
+
// is the whole reason this needs a mobile answer of its own.
|
|
339
|
+
//
|
|
340
|
+
// `flex: 1 1 280px` sizes the MAIN axis; the row's own mobile override
|
|
341
|
+
// turns the main axis from width into height, so every stacked column
|
|
342
|
+
// came out 280px tall whatever was in it. Measured at 390 on a real
|
|
343
|
+
// import: a paragraph of two lines sat in a 280px box, and the page ran
|
|
344
|
+
// 6,009px with most of it empty — and NOTHING reported it, because no box
|
|
345
|
+
// overflowed and no two boxes overlapped. `measure` cannot see air.
|
|
346
|
+
//
|
|
347
|
+
// Mobile only, and base keeps the wide answer, which is the cascade's
|
|
348
|
+
// tail rule the right way round: the row is still a row at tablet.
|
|
320
349
|
children: kids.map((k) => ({
|
|
321
350
|
type: 'flex-block',
|
|
322
351
|
style: { flex: '1 1 280px', minWidth: '0', display: 'flex', flexDirection: 'column', gap: '12px' },
|
|
352
|
+
responsive: { mobile: { style: { flex: '0 1 auto' } } },
|
|
323
353
|
children: [k],
|
|
324
354
|
})),
|
|
325
355
|
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { toSpecs } from './importmap.js';
|
|
2
|
+
/**
|
|
3
|
+
* THE LOOK A PAGE HAS BEFORE IT HAS ANYTHING, and it is not nothing.
|
|
4
|
+
*
|
|
5
|
+
* Rule 0 says read the page's own pattern. A blank page has none — and the next
|
|
6
|
+
* authority is not invention, it is the SITE'S THEME, which is where every
|
|
7
|
+
* element's style preset resolves from anyway.
|
|
8
|
+
*
|
|
9
|
+
* Written as `var(--wb-color-…)` rather than as the hex those resolve to, which
|
|
10
|
+
* is the whole point: a literal on a node OUTRANKS the preset beneath it
|
|
11
|
+
* permanently, so a pattern that baked today's `#171717` in would stop following
|
|
12
|
+
* the theme the moment the merchant changed it — the exact detachment this repo
|
|
13
|
+
* already records for imported icons. The variable is what the theme's own
|
|
14
|
+
* scheme roles use.
|
|
15
|
+
*/
|
|
16
|
+
export const THEME_TOKENS = {
|
|
17
|
+
headingColor: 'var(--wb-color-heading)',
|
|
18
|
+
textColor: 'var(--wb-color-text)',
|
|
19
|
+
buttonBg: 'var(--wb-color-primary)',
|
|
20
|
+
buttonColor: '#ffffff',
|
|
21
|
+
};
|
|
22
|
+
/** One section, through the same mapper an import goes through. */
|
|
23
|
+
function section(children, t, style, innerStyle) {
|
|
24
|
+
const [spec] = toSpecs([{ kind: 'section', children }], t);
|
|
25
|
+
if (!spec)
|
|
26
|
+
return null;
|
|
27
|
+
const outer = style ? { ...spec, style: { ...spec.style, ...style } } : spec;
|
|
28
|
+
if (!innerStyle)
|
|
29
|
+
return outer;
|
|
30
|
+
// THE SECTION CENTRES ITS BLOCK; THE BLOCK CENTRES ITS CHILDREN. `textAlign`
|
|
31
|
+
// moves the words and leaves a `width: fit-content` button where it was, so a
|
|
32
|
+
// centred band came out with its call to action against the left margin.
|
|
33
|
+
const [inner, ...rest] = outer.children ?? [];
|
|
34
|
+
if (!inner)
|
|
35
|
+
return outer;
|
|
36
|
+
return {
|
|
37
|
+
...outer,
|
|
38
|
+
children: [{ ...inner, style: { ...inner.style, ...innerStyle } }, ...rest],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const row = (children, wrap = false) => ({
|
|
42
|
+
kind: 'group',
|
|
43
|
+
direction: 'row',
|
|
44
|
+
wrap,
|
|
45
|
+
children,
|
|
46
|
+
});
|
|
47
|
+
const h = (text, level = 2) => ({ kind: 'heading', level, text });
|
|
48
|
+
const p = (text) => ({ kind: 'text', text });
|
|
49
|
+
const cta = (text, href = '#') => ({ kind: 'button', variant: 'cta', text, href });
|
|
50
|
+
const img = (alt) => ({
|
|
51
|
+
// A PLACEHOLDER WITH NO SRC IS DROPPED by the mapper, which is right — an
|
|
52
|
+
// empty frame is not content. So a pattern that wants a picture says so in
|
|
53
|
+
// words and the caller points sb_set at it, rather than the page shipping a
|
|
54
|
+
// grey box nobody chose.
|
|
55
|
+
kind: 'image',
|
|
56
|
+
src: '',
|
|
57
|
+
alt,
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* The set. Deliberately small and deliberately ordinary: these are the bands
|
|
61
|
+
* every commercial page is built from, and a library that tried to be clever
|
|
62
|
+
* would be a library nobody could predict.
|
|
63
|
+
*/
|
|
64
|
+
export const LAYOUT_PATTERNS = [
|
|
65
|
+
{
|
|
66
|
+
id: 'sb_hero_split',
|
|
67
|
+
name: 'Hero — hai cột',
|
|
68
|
+
use: 'Mở đầu trang: tiêu đề, một câu, nút hành động bên trái; chỗ cho ảnh bên phải',
|
|
69
|
+
build: (t) => section([
|
|
70
|
+
row([
|
|
71
|
+
{ kind: 'group', direction: 'column', children: [h('Tiêu đề chính', 1), p('Một câu nói rõ bạn bán gì và cho ai.'), cta('Mua ngay')] },
|
|
72
|
+
{ kind: 'group', direction: 'column', children: [p('Đặt ảnh vào đây — sb_add một image, hoặc sb_set src lên khối này.')] },
|
|
73
|
+
]),
|
|
74
|
+
], t),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'sb_hero_centered',
|
|
78
|
+
name: 'Hero — canh giữa',
|
|
79
|
+
use: 'Mở đầu trang khi chưa có ảnh: tiêu đề lớn, một câu, một nút',
|
|
80
|
+
build: (t) => section([h('Tiêu đề chính', 1), p('Một câu nói rõ bạn bán gì và cho ai.'), cta('Mua ngay')], t, { alignItems: 'center', textAlign: 'center' }, { alignItems: 'center' }),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'sb_feature_trio',
|
|
84
|
+
name: 'Ba lợi ích',
|
|
85
|
+
use: 'Ba cột ngang nhau: mỗi cột một tiêu đề nhỏ và một đoạn — lý do nên mua',
|
|
86
|
+
build: (t) => section([
|
|
87
|
+
h('Vì sao chọn chúng tôi'),
|
|
88
|
+
row([
|
|
89
|
+
{ kind: 'group', direction: 'column', children: [h('Lợi ích một', 3), p('Một hoặc hai câu.')] },
|
|
90
|
+
{ kind: 'group', direction: 'column', children: [h('Lợi ích hai', 3), p('Một hoặc hai câu.')] },
|
|
91
|
+
{ kind: 'group', direction: 'column', children: [h('Lợi ích ba', 3), p('Một hoặc hai câu.')] },
|
|
92
|
+
]),
|
|
93
|
+
], t),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'sb_stats_row',
|
|
97
|
+
name: 'Dải số liệu',
|
|
98
|
+
use: 'Ba đến bốn con số lớn với nhãn bên dưới — bằng chứng, không phải lời hứa',
|
|
99
|
+
build: (t) => section([
|
|
100
|
+
row([
|
|
101
|
+
{ kind: 'group', direction: 'column', children: [h('1.000+', 2), p('Khách hàng')] },
|
|
102
|
+
{ kind: 'group', direction: 'column', children: [h('4,9/5', 2), p('Đánh giá trung bình')] },
|
|
103
|
+
{ kind: 'group', direction: 'column', children: [h('24h', 2), p('Giao trong nội thành')] },
|
|
104
|
+
]),
|
|
105
|
+
], t, { alignItems: 'center', textAlign: 'center' }, { alignItems: 'center' }),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'sb_cta_band',
|
|
109
|
+
name: 'Dải kêu gọi hành động',
|
|
110
|
+
use: 'Một câu và một nút, đặt cuối trang hoặc giữa hai band nội dung',
|
|
111
|
+
build: (t) => section([h('Sẵn sàng bắt đầu?'), p('Một câu nhắc lại lời hứa chính.'), cta('Mua ngay')], t, { alignItems: 'center', textAlign: 'center' }, { alignItems: 'center' }),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'sb_faq',
|
|
115
|
+
name: 'Câu hỏi thường gặp',
|
|
116
|
+
use: 'Ba câu hỏi mở/đóng — nơi trả lời những gì cản trở việc mua',
|
|
117
|
+
build: (t) => section([
|
|
118
|
+
h('Câu hỏi thường gặp'),
|
|
119
|
+
{
|
|
120
|
+
kind: 'accordion',
|
|
121
|
+
children: [
|
|
122
|
+
{ kind: 'accordion-item', text: 'Giao hàng mất bao lâu?', children: [p('Trả lời ngắn gọn.')] },
|
|
123
|
+
{ kind: 'accordion-item', text: 'Đổi trả thế nào?', children: [p('Trả lời ngắn gọn.')] },
|
|
124
|
+
{ kind: 'accordion-item', text: 'Thanh toán ra sao?', children: [p('Trả lời ngắn gọn.')] },
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
], t),
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
export const PATTERN_BY_ID = new Map(LAYOUT_PATTERNS.map((p2) => [p2.id, p2]));
|
package/dist/tools/page.js
CHANGED
|
@@ -20,6 +20,9 @@ import { readinessGaps, READINESS_NOTICE } from '../domains/site/readiness.js';
|
|
|
20
20
|
import { gatherReadiness } from '../domains/site/readiness-fetch.js';
|
|
21
21
|
import { globalWarning, restampPatches, RESPONSIVE_NOTICE } from '../domains/site/traps.js';
|
|
22
22
|
import { catalogMatches, traitsFor } from '../catalog/element-search.js';
|
|
23
|
+
import { LAYOUT_PATTERNS, PATTERN_BY_ID, THEME_TOKENS } from '../domains/site/patterns.js';
|
|
24
|
+
import { tokensFromPage } from '../domains/site/importmap.js';
|
|
25
|
+
import { middleEnd } from '../domains/site/traps.js';
|
|
23
26
|
import { applyPatches } from '../core/patch.js';
|
|
24
27
|
import { stickyWarning } from '../domains/site/sticky.js';
|
|
25
28
|
import { HOVER_STATE, PARENT_HOVER_STATE, hoverHostNote, hoverRoutingNote } from '../domains/site/hover.js';
|
|
@@ -654,16 +657,27 @@ export function registerPageTools(server, ctx) {
|
|
|
654
657
|
'than assembling one. Use sb_template_use to drop one into the open page.',
|
|
655
658
|
inputSchema: { site_id: z.string().optional() },
|
|
656
659
|
annotations: { readOnlyHint: true },
|
|
657
|
-
}, async ({ site_id: given }) =>
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
660
|
+
}, async ({ site_id: given }) => {
|
|
661
|
+
const listed = projectList(await request({
|
|
662
|
+
base: ctx.base,
|
|
663
|
+
method: 'GET',
|
|
664
|
+
path: `/api/sites/${encodeURIComponent(siteFor(ctx, given))}/section-templates`,
|
|
665
|
+
token: siteToken(ctx),
|
|
666
|
+
fetchImpl: ctx.fetchImpl,
|
|
667
|
+
}), 'sectionTemplates', TEMPLATE_FIELDS);
|
|
668
|
+
// THE SITE'S OWN FIRST, ALWAYS. A template a merchant designed is this
|
|
669
|
+
// site's answer; these are defaults for a page that has none. Measured on
|
|
670
|
+
// a live site, the platform's library held TWO — which is why an agent
|
|
671
|
+
// asked for "a hero" was inventing one from flex-blocks every time.
|
|
672
|
+
return text({
|
|
673
|
+
...listed,
|
|
674
|
+
built_in: LAYOUT_PATTERNS.map((p) => ({ id: p.id, name: p.name, use: p.use })),
|
|
675
|
+
});
|
|
676
|
+
});
|
|
664
677
|
server.registerTool('sb_template_use', {
|
|
665
|
-
description: 'Instantiate a
|
|
666
|
-
'
|
|
678
|
+
description: 'Instantiate a section template into a page — the site\'s own (the server copies it) or ' +
|
|
679
|
+
'one of the BUILT-IN layouts sb_templates lists, which are composed against this ' +
|
|
680
|
+
"page's own tokens rather than copied.",
|
|
667
681
|
inputSchema: {
|
|
668
682
|
site_id: z.string().optional(),
|
|
669
683
|
template_id: z.string(),
|
|
@@ -673,6 +687,54 @@ export function registerPageTools(server, ctx) {
|
|
|
673
687
|
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
674
688
|
}, async ({ site_id: given, template_id, page_id, dry_run }) => {
|
|
675
689
|
const site_id = siteFor(ctx, given);
|
|
690
|
+
// A BUILT-IN IS NOT A SERVER COPY. The platform's own templates are
|
|
691
|
+
// instantiated by the platform, which is why the section arrives exactly
|
|
692
|
+
// as designed; a built-in has no row on the server, so it is composed HERE
|
|
693
|
+
// — against the target page's own tokens, which is the whole point of it
|
|
694
|
+
// being a pattern rather than a snapshot.
|
|
695
|
+
const pattern = PATTERN_BY_ID.get(template_id);
|
|
696
|
+
if (pattern) {
|
|
697
|
+
await session.open(site_id, page_id);
|
|
698
|
+
const doc = session.current();
|
|
699
|
+
const read = tokensFromPage(doc.doc);
|
|
700
|
+
// A BLANK PAGE HAS NO PATTERN TO READ, and the next authority is the
|
|
701
|
+
// site's THEME rather than nothing — every element's style preset
|
|
702
|
+
// resolves from it anyway. Carried as `var(--wb-color-…)`, so the band
|
|
703
|
+
// goes on following the theme instead of freezing today's hex into it.
|
|
704
|
+
const fromTheme = Object.keys(read).length === 0;
|
|
705
|
+
const tokens = fromTheme ? THEME_TOKENS : read;
|
|
706
|
+
const spec = pattern.build(tokens);
|
|
707
|
+
if (!spec) {
|
|
708
|
+
throw new Error(`sbuilder: the built-in "${template_id}" produced nothing to add.`);
|
|
709
|
+
}
|
|
710
|
+
const { patches, ids } = addSubtree(doc, doc.doc.root_node_id, spec, middleEnd(doc.doc));
|
|
711
|
+
if (dry_run !== false) {
|
|
712
|
+
return text({
|
|
713
|
+
dry_run: true,
|
|
714
|
+
would_add: pattern.name,
|
|
715
|
+
nodes: ids.length,
|
|
716
|
+
into: page_id,
|
|
717
|
+
tokens_from: fromTheme ? "this site's theme — the page has no look of its own yet" : 'this page',
|
|
718
|
+
note: 'Composed against THIS page\'s tokens, not copied — the same heading ink, button ' +
|
|
719
|
+
'fill and section padding the page already uses. Pass dry_run:false to add it.',
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
await session.applyAndSave(patches);
|
|
723
|
+
return text({
|
|
724
|
+
added: pattern.name,
|
|
725
|
+
section: ids[0],
|
|
726
|
+
nodes: ids.length,
|
|
727
|
+
into: page_id,
|
|
728
|
+
rev: doc.rev,
|
|
729
|
+
...(fromTheme
|
|
730
|
+
? {
|
|
731
|
+
tokens_from: "this site's theme — the page had no heading, button or section to read a look " +
|
|
732
|
+
'off, so the band carries var(--wb-color-…) and follows the theme rather than a ' +
|
|
733
|
+
'frozen colour. Style this first band and every pattern after it reads THAT.',
|
|
734
|
+
}
|
|
735
|
+
: {}),
|
|
736
|
+
});
|
|
737
|
+
}
|
|
676
738
|
const path = `/api/sites/${encodeURIComponent(site_id)}/section-templates/${encodeURIComponent(template_id)}/instantiate`;
|
|
677
739
|
if (dry_run !== false) {
|
|
678
740
|
return text({ dry_run: true, would_post: path, body: { pageId: page_id } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "MCP server that designs and operates a Store Builder site — pages, data, theme and publish — through the platform's own API and live-edit protocol.",
|
|
5
5
|
"mcpName": "io.github.vuluu2k/sbuilder-mcp",
|
|
6
6
|
"type": "module",
|