sbuilder-mcp 0.9.0 → 0.9.1

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 CHANGED
@@ -6,6 +6,11 @@ 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.9.1] - 2026-09-09
10
+
11
+ ### Fixed
12
+ - sb_set now refuses a `config.stuckAfter` write the renderer would drop on the floor — a negative, non-finite, or empty value, or one set on a node that cannot pin — instead of silently storing, saving, and publishing a threshold the runtime island never reads and quietly falling back to its automatic answer.
13
+
9
14
  ## [0.9.0] - 2026-09-08
10
15
 
11
16
  ### Added
package/CHANGELOG.vi.md CHANGED
@@ -6,6 +6,11 @@ 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.9.1] - 2026-09-09
10
+
11
+ ### Fixed
12
+ - sb_set giờ từ chối ghi `config.stuckAfter` nếu giá trị sẽ bị renderer âm thầm bỏ qua — số âm, không hữu hạn, rỗng, hoặc được đặt trên một node không thể ghim — thay vì lưu, save rồi publish một ngưỡng mà runtime island không bao giờ đọc và lặng lẽ quay về đáp án tự động của nó.
13
+
9
14
  ## [0.9.0] - 2026-09-08
10
15
 
11
16
  ### Added
@@ -2,7 +2,7 @@ import { isOverlay, subtreeIds, ancestors, appBlockRoot, SPEC_GLOBAL_ID, SPEC_GL
2
2
  import { ELEMENTS, ELEMENT_SEEDS, SATELLITE_RULES } from '../../catalog/elements.generated.js';
3
3
  import { bindingsForConfig, createNode, mintSatellites } from './node.js';
4
4
  import { refuseSecondTemplate } from './traps.js';
5
- import { STUCK_STATE, refuseStuckConfig, requireStuckHost, stickySeeds } from './sticky.js';
5
+ import { STUCK_STATE, refuseStuckAfter, refuseStuckConfig, requireStuckHost, stickySeeds, } from './sticky.js';
6
6
  import { genId } from './ids.js';
7
7
  /** Append sentinel: splice clamps a too-large index, and `isSyncablePatch`
8
8
  * deliberately allows one — an append is a legitimate thing to describe. */
@@ -182,6 +182,12 @@ export function setKeys(doc, id, keys, opts) {
182
182
  // the same silent shape as an unbound element, which cost a whole page of
183
183
  // "$0.00" cards to find once.
184
184
  const rebind = namespace === 'config' ? rebindPatch(doc, id, keys) : null;
185
+ // The scroll THRESHOLD is a plain config key, and the renderer drops it in two
186
+ // different ways without a word — see sticky.ts. Checked here rather than in
187
+ // the state branch because it is written on the node that PINS, at base or at
188
+ // a breakpoint, never inside a stuck slot.
189
+ if (namespace === 'config')
190
+ refuseStuckAfter(doc.doc, id, keys);
185
191
  if (namespace === 'specials') {
186
192
  refuseComposedStamp(keys);
187
193
  // `specials` is content and identity, base-only by definition, and states
@@ -36,6 +36,17 @@ import { ancestors } from '../../core/tree.js';
36
36
  */
37
37
  /** The state key, in `node.states` and `responsive[bp].states`. */
38
38
  export const STUCK_STATE = 'stuck';
39
+ /**
40
+ * The config key holding "pinned from WHEN?", in px of page scroll.
41
+ *
42
+ * It reaches the runtime island as a CSS custom property (`--wb-stuck-after`)
43
+ * rather than through `wb:props`, because it is per breakpoint and the cascade
44
+ * is what picks the right number at the right width. Unset is the common case:
45
+ * the island then decides from the element itself — a sticky one the moment it
46
+ * is held away from its place in the flow, a fixed one the moment the page
47
+ * scrolls past where it would have sat.
48
+ */
49
+ export const STUCK_AFTER = 'stuckAfter';
39
50
  /**
40
51
  * The positions that HAVE a pinned moment.
41
52
  *
@@ -206,3 +217,32 @@ export function stickyWarning(doc, id, bp) {
206
217
  `scrolls. The node will not move. Clear overflowX/overflowY on ${blocker}, or pin a node ` +
207
218
  'outside it.');
208
219
  }
220
+ /**
221
+ * Refuse a `stuckAfter` that the renderer would drop on the floor.
222
+ *
223
+ * `stuckAfterCss` emits the custom property only for a node that CAN pin and
224
+ * that carries a usable number, and returns an empty string otherwise — so both
225
+ * mistakes here are stored, saved, published and read by nothing. The value
226
+ * rules are the platform's own: zero is honoured ("as soon as the page moves at
227
+ * all" is a real answer, and dropping it would silently fall back to the
228
+ * automatic one); negative and non-finite are not, because no scroll position
229
+ * can satisfy them.
230
+ */
231
+ export function refuseStuckAfter(doc, id, keys) {
232
+ if (!(STUCK_AFTER in keys))
233
+ return;
234
+ const raw = keys[STUCK_AFTER];
235
+ const n = typeof raw === 'number' ? raw : Number(raw);
236
+ if (raw == null || raw === '' || !Number.isFinite(n) || n < 0) {
237
+ throw new Error(`sbuilder: config.${STUCK_AFTER} is px of page scroll — a finite number, zero or more ` +
238
+ `(0 means "as soon as the page moves at all"). ${JSON.stringify(raw)} is dropped by ` +
239
+ "the renderer, which then falls back to the island's automatic answer without saying " +
240
+ 'so. To go back to automatic, remove the key.');
241
+ }
242
+ if (!isPinnedNode(doc.nodes[id])) {
243
+ throw new Error(`sbuilder: config.${STUCK_AFTER} says WHEN a pinned element counts as stuck, and ${id} ` +
244
+ 'cannot pin — the renderer emits the threshold only for a node that can ' +
245
+ '(stickyState.ts stuckAfterCss), so this would be stored and read by nothing. ' +
246
+ `Pin it first: sb_set ${id} style { position: "sticky" }.`);
247
+ }
248
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sbuilder-mcp",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
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",