sbuilder-mcp 0.11.1 → 0.11.3
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 +10 -0
- package/CHANGELOG.vi.md +10 -0
- package/dist/domains/site/builder.js +24 -0
- package/dist/domains/site/findings.js +3 -4
- package/dist/tools/page.js +19 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,16 @@ 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.11.3] - 2026-09-09
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- sb_set now says its hover-routing note once per element type per process instead of once per node, since repairing every button on a page in one batch returned ten copies of the same 300-character paragraph; a different element type still gets its own note, because it says something different.
|
|
13
|
+
|
|
14
|
+
## [0.11.2] - 2026-09-09
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- sb_set's routing of a hover write to an element's legacy home (a `button`, for instance) now also clears any leftover values in the unread `states.hover` slot in the same call, instead of leaving them behind for `sb_review` to keep reporting as `hover_dead`; the finding's own message previously named `unset` as the way to remove them, which could not reach the legacy home either, so the documented fix was unreachable.
|
|
18
|
+
|
|
9
19
|
## [0.11.1] - 2026-09-09
|
|
10
20
|
|
|
11
21
|
### Added
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,16 @@ 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.11.3] - 2026-09-09
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- sb_set giờ chỉ nói ghi chú định tuyến hover một lần cho mỗi loại element trong mỗi tiến trình thay vì một lần cho mỗi node, vì việc sửa mọi nút button trên một trang trong cùng một batch trước đây trả về mười bản sao của cùng một đoạn văn 300 ký tự; một loại element khác vẫn nhận được ghi chú riêng của nó, vì nó nói một điều khác.
|
|
13
|
+
|
|
14
|
+
## [0.11.2] - 2026-09-09
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Khi sb_set định tuyến một lần ghi hover tới nơi lưu cũ của element (ví dụ `button`), giờ nó cũng xoá luôn mọi giá trị còn sót trong slot `states.hover` không ai đọc, ngay trong cùng một lệnh — thay vì để lại chúng cho sb_review tiếp tục báo là `hover_dead`; thông báo của chính finding này trước đây nêu `unset` là cách xoá, nhưng `unset` cũng không thể chạm tới nơi lưu cũ đó, khiến cách sửa được ghi ra là không thể thực hiện được.
|
|
18
|
+
|
|
9
19
|
## [0.11.1] - 2026-09-09
|
|
10
20
|
|
|
11
21
|
### Added
|
|
@@ -264,6 +264,29 @@ export function setKeys(doc, id, keys, opts) {
|
|
|
264
264
|
// compiler reads. Routed rather than refused: the caller asked for a hover
|
|
265
265
|
// and there is a home that works; `hoverRoutingNote` says where it went.
|
|
266
266
|
if (opts.state === HOVER_STATE && namespace === 'style' && hoverHome(doc.node(id).data.type) === 'legacy') {
|
|
267
|
+
// …AND CLEAR THE SLOT NOBODY READS, in the same call.
|
|
268
|
+
//
|
|
269
|
+
// On an element with this home, `states.hover` has no legitimate use: the
|
|
270
|
+
// universal compiler stands aside for it and its own renderer looks in the
|
|
271
|
+
// flat map. Leaving values there is the exact defect `sb_review` reports as
|
|
272
|
+
// `hover_dead`, so a rewrite that repaired the look and left the dead copy
|
|
273
|
+
// behind would fix the page and keep the finding — and the fix that
|
|
274
|
+
// finding names would still be unreachable, which is the shape this repo
|
|
275
|
+
// has now closed twice.
|
|
276
|
+
//
|
|
277
|
+
// Safe against the platform changing its mind: if a button ever keeps its
|
|
278
|
+
// hover in the node, its meta's `storage` becomes 'node', HOVER_HOMES says
|
|
279
|
+
// 'state', and this branch does not run at all.
|
|
280
|
+
const node = doc.node(id);
|
|
281
|
+
const dead = [];
|
|
282
|
+
if (node.states?.[HOVER_STATE] !== undefined) {
|
|
283
|
+
dead.push({ op: 'unset', path: ['nodes', id, 'states', HOVER_STATE] });
|
|
284
|
+
}
|
|
285
|
+
for (const [bp, slot] of Object.entries(node.responsive ?? {})) {
|
|
286
|
+
if (slot?.states?.[HOVER_STATE] !== undefined) {
|
|
287
|
+
dead.push({ op: 'unset', path: ['nodes', id, 'responsive', bp, 'states', HOVER_STATE] });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
267
290
|
return [
|
|
268
291
|
...Object.entries(keys).map(([k, v]) => ({
|
|
269
292
|
op: 'set',
|
|
@@ -271,6 +294,7 @@ export function setKeys(doc, id, keys, opts) {
|
|
|
271
294
|
value: v,
|
|
272
295
|
})),
|
|
273
296
|
...(opts.unset ?? []).map((k) => ({ op: 'unset', path: legacyHoverPath(id, k) })),
|
|
297
|
+
...dead,
|
|
274
298
|
];
|
|
275
299
|
}
|
|
276
300
|
// Base state and per-breakpoint state are DIFFERENT PLACES in the document,
|
|
@@ -60,10 +60,9 @@ export const FIX = {
|
|
|
60
60
|
'{ "overflowX": "visible", "overflowY": "visible" } — or move the pinned node outside it. ' +
|
|
61
61
|
'Sticky resolves against its nearest SCROLLING ancestor, so a clipping one becomes that ' +
|
|
62
62
|
'ancestor and the node pins inside a box that never scrolls.',
|
|
63
|
-
hover_dead: 'Rewrite it: sb_set id "<id>", namespace style, state "hover",
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'once the working copy is in place.',
|
|
63
|
+
hover_dead: 'Rewrite it with the same values: sb_set id "<id>", namespace style, state "hover", ' +
|
|
64
|
+
'keys { … } — read them off the node first, because that one call both writes them where ' +
|
|
65
|
+
'the renderer looks AND clears the slot nobody reads. Nothing else to do afterwards.',
|
|
67
66
|
stuck_no_host: 'Pin something: sb_set id "<id>" (or the section it lives in), namespace style, keys ' +
|
|
68
67
|
'{ "position": "sticky" } — sb_set seeds the offset and the layer order with it. Until ' +
|
|
69
68
|
'then the platform compiles no rule for this state at all, so the override is stored, ' +
|
package/dist/tools/page.js
CHANGED
|
@@ -305,17 +305,26 @@ export function registerPageTools(server, ctx) {
|
|
|
305
305
|
}
|
|
306
306
|
const { patches, touched } = setMany(d, batch);
|
|
307
307
|
// WHERE A HOVER ACTUALLY WENT. Routing it silently would leave a caller
|
|
308
|
-
// who reads the node back looking for keys in a slot they never wrote to
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
|
|
308
|
+
// who reads the node back looking for keys in a slot they never wrote to.
|
|
309
|
+
//
|
|
310
|
+
// ONCE PER PROCESS PER ELEMENT TYPE, not once per node. A batch repairing
|
|
311
|
+
// every button on a page carried ten copies of the same 300-character
|
|
312
|
+
// paragraph — measured, on this repo's own storefront — which is the shape
|
|
313
|
+
// `ctx.notices` exists to prevent. The type is the whole content of the
|
|
314
|
+
// note, so a second copy tells the caller nothing they were not just told,
|
|
315
|
+
// and the ids it applies to are already in `set`.
|
|
316
|
+
const hoverTypes = new Set();
|
|
312
317
|
for (const e of batch) {
|
|
313
318
|
if (e.state !== HOVER_STATE || e.namespace !== 'style')
|
|
314
319
|
continue;
|
|
315
|
-
const
|
|
316
|
-
if (
|
|
317
|
-
|
|
320
|
+
const type = d.doc.nodes[e.id]?.data.type ?? '';
|
|
321
|
+
if (hoverRoutingNote(type))
|
|
322
|
+
hoverTypes.add(type);
|
|
318
323
|
}
|
|
324
|
+
const hoverNote = [...hoverTypes]
|
|
325
|
+
.map((t) => ctx.notices.once(`hover-home:${t}`, hoverRoutingNote(t)))
|
|
326
|
+
.filter((n) => !!n)
|
|
327
|
+
.join(' ');
|
|
319
328
|
// THE STICKY WARNING IS COMPUTED AGAINST THE DOCUMENT AS IT WILL BE, so
|
|
320
329
|
// the dry run and the real run say the same thing. A caller who is told
|
|
321
330
|
// only after committing has already shipped a header that does not move.
|
|
@@ -346,7 +355,7 @@ export function registerPageTools(server, ctx) {
|
|
|
346
355
|
dry_run: true,
|
|
347
356
|
patches,
|
|
348
357
|
...(Object.keys(sw).length ? { warnings: sw } : {}),
|
|
349
|
-
...(
|
|
358
|
+
...(hoverNote ? { hover: hoverNote } : {}),
|
|
350
359
|
...(note ? { note } : {}),
|
|
351
360
|
});
|
|
352
361
|
}
|
|
@@ -360,19 +369,18 @@ export function registerPageTools(server, ctx) {
|
|
|
360
369
|
}
|
|
361
370
|
if (!edits) {
|
|
362
371
|
const warn = warnings[batch[0].id];
|
|
363
|
-
const hv = hoverNotes[batch[0].id];
|
|
364
372
|
return text({
|
|
365
373
|
set: touched[0].keys,
|
|
366
374
|
rev: d.rev,
|
|
367
375
|
...(warn ? { warning: warn } : {}),
|
|
368
|
-
...(
|
|
376
|
+
...(hoverNote ? { hover: hoverNote } : {}),
|
|
369
377
|
});
|
|
370
378
|
}
|
|
371
379
|
return text({
|
|
372
380
|
set: touched,
|
|
373
381
|
rev: d.rev,
|
|
374
382
|
...(Object.keys(warnings).length ? { warnings } : {}),
|
|
375
|
-
...(
|
|
383
|
+
...(hoverNote ? { hover: hoverNote } : {}),
|
|
376
384
|
});
|
|
377
385
|
});
|
|
378
386
|
server.registerTool('sb_move', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
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",
|