sbuilder-mcp 0.11.3 → 0.13.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 +14 -0
- package/CHANGELOG.vi.md +14 -0
- package/dist/catalog/elements.generated.js +215 -7
- package/dist/catalog/shapes.generated.js +146 -8
- package/dist/domains/site/hover.js +97 -14
- package/dist/tools/page.js +15 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@ 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.13.0] - 2026-09-09
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- sb_set now accepts `specials.hoverHostDepth` on a `state:"parentHover"` write, letting a rule hang off any ancestor box (1-based, nearest-first) instead of always the node's immediate parent, and reports which box it actually hung off, the wider ancestors on offer, and whether a depth past the end of the chain clamped to the outermost one.
|
|
13
|
+
- Element catalog refreshed to 109 elements, adding `cart-count` and a `hoverSwapImage` option on `product-image-feature` (shows the next gallery photo on pointer-hover, off by default).
|
|
14
|
+
|
|
15
|
+
## [0.12.0] - 2026-09-09
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- npm run codegen:check now also refuses a checkout whose HEAD carries commits touching the directories it reads (schema/src, editor/src, server/render, server/docs) that its upstream branch does not have, naming the commits; an uncommitted-only check had already been fooled once, by a feature committed locally and never pushed, so the tree read clean while the catalog it fed was ahead of every deployed platform. A detached worktree checked out at origin/main has no upstream and is therefore exempt, which is the shape the check itself recommends; `--dirty` still overrides it.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- The generated request-shape catalog now covers three write operations its handler parser was missing or mis-attributing: `roles/{roleId}` (a case arm listing several HTTP methods together), `products/{productId}/categories` (a dispatcher that routes by a literal path segment rather than by method), and `sites/{siteId}/org` (a doc comment block shared by two adjacent handlers declared in the opposite order, now attributed by the name it opens with). 162 of 276 write operations now carry a shape, up from 157, so sb_api_call and sb_undo can act on all three without guessing a body.
|
|
22
|
+
|
|
9
23
|
## [0.11.3] - 2026-09-09
|
|
10
24
|
|
|
11
25
|
### Fixed
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -6,6 +6,20 @@ 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.13.0] - 2026-09-09
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- sb_set giờ hỗ trợ `specials.hoverHostDepth` khi ghi `state:"parentHover"`, cho phép một rule hover-cha hangs off bất kỳ ancestor box nào (đánh số từ 1, gần nhất trước) thay vì luôn luôn là parent trực tiếp của node, và trả về box mà nó thực sự hangs off, các ancestor rộng hơn đang có sẵn, cùng thông báo khi một depth vượt quá độ dài chuỗi bị clamp về box ngoài cùng.
|
|
13
|
+
- Catalog element được làm mới lên 109 element, thêm `cart-count` và tùy chọn `hoverSwapImage` trên `product-image-feature` (hiện ảnh gallery kế tiếp khi trỏ chuột vào, mặc định tắt).
|
|
14
|
+
|
|
15
|
+
## [0.12.0] - 2026-09-09
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- npm run codegen:check giờ cũng từ chối một checkout mà HEAD của nó mang các commit chạm vào những thư mục mà nó đọc (schema/src, editor/src, server/render, server/docs) mà nhánh upstream không có, nêu tên các commit đó; việc chỉ kiểm tra thay đổi chưa commit từng bị qua mặt một lần, bởi một tính năng đã được commit ở local nhưng chưa từng push, khiến working tree trông sạch trong khi catalog mà nó tạo ra lại đi trước mọi nền tảng đã triển khai. Một worktree tách rời checkout tại origin/main không có upstream nên được miễn kiểm tra này, đúng như cách mà chính kiểm tra này khuyến nghị; `--dirty` vẫn cho phép ghi đè.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Catalog request-shape được sinh ra giờ bao phủ thêm ba write operation mà trình phân tích handler trước đây bỏ sót hoặc gán sai: `roles/{roleId}` (một case arm liệt kê nhiều phương thức HTTP cùng lúc), `products/{productId}/categories` (một dispatcher định tuyến theo một đoạn path cố định thay vì theo method), và `sites/{siteId}/org` (một khối doc comment dùng chung cho hai handler liền kề được khai báo theo thứ tự ngược lại, giờ được gán theo đúng tên mà nó mở đầu). 162 trong số 276 write operation giờ đã có shape, tăng từ 157, nhờ đó sb_api_call và sb_undo có thể thao tác trên cả ba mà không cần đoán body.
|
|
22
|
+
|
|
9
23
|
## [0.11.3] - 2026-09-09
|
|
10
24
|
|
|
11
25
|
### Fixed
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const ELEMENT_SOURCE = {
|
|
2
|
-
"count":
|
|
2
|
+
"count": 109,
|
|
3
3
|
"docSchemaVersion": 2
|
|
4
4
|
};
|
|
5
5
|
export const ELEMENTS = {
|
|
@@ -1803,7 +1803,8 @@ export const ELEMENTS = {
|
|
|
1803
1803
|
"key": "action",
|
|
1804
1804
|
"label": "Action",
|
|
1805
1805
|
"controls": [
|
|
1806
|
-
"action"
|
|
1806
|
+
"action",
|
|
1807
|
+
"icon_cart_badge"
|
|
1807
1808
|
]
|
|
1808
1809
|
},
|
|
1809
1810
|
{
|
|
@@ -1876,6 +1877,7 @@ export const ELEMENTS = {
|
|
|
1876
1877
|
"icon_color",
|
|
1877
1878
|
"icon_size",
|
|
1878
1879
|
"action",
|
|
1880
|
+
"icon_cart_badge",
|
|
1879
1881
|
"padding",
|
|
1880
1882
|
"margin",
|
|
1881
1883
|
"align_self",
|
|
@@ -13842,6 +13844,166 @@ export const ELEMENTS = {
|
|
|
13842
13844
|
"dynamic"
|
|
13843
13845
|
]
|
|
13844
13846
|
},
|
|
13847
|
+
"cart-count": {
|
|
13848
|
+
"type": "cart-count",
|
|
13849
|
+
"label": "Cart count",
|
|
13850
|
+
"category": "store",
|
|
13851
|
+
"isContainer": false,
|
|
13852
|
+
"isRootOnly": false,
|
|
13853
|
+
"locked": false,
|
|
13854
|
+
"hideInLayer": false,
|
|
13855
|
+
"childAllows": [],
|
|
13856
|
+
"defaults": {
|
|
13857
|
+
"style": {
|
|
13858
|
+
"display": "inline-flex",
|
|
13859
|
+
"alignItems": "center",
|
|
13860
|
+
"justifyContent": "center",
|
|
13861
|
+
"width": "fit-content",
|
|
13862
|
+
"minWidth": "20px",
|
|
13863
|
+
"height": "20px",
|
|
13864
|
+
"paddingLeft": "6px",
|
|
13865
|
+
"paddingRight": "6px",
|
|
13866
|
+
"borderRadius": "999px",
|
|
13867
|
+
"backgroundColor": "#171717",
|
|
13868
|
+
"color": "#ffffff",
|
|
13869
|
+
"fontSize": "12px",
|
|
13870
|
+
"fontWeight": "600",
|
|
13871
|
+
"lineHeight": "1"
|
|
13872
|
+
},
|
|
13873
|
+
"specials": {
|
|
13874
|
+
"placeholder": "0",
|
|
13875
|
+
"hideWhenEmpty": true
|
|
13876
|
+
}
|
|
13877
|
+
},
|
|
13878
|
+
"inspector": [
|
|
13879
|
+
{
|
|
13880
|
+
"tab": "general",
|
|
13881
|
+
"groups": [
|
|
13882
|
+
{
|
|
13883
|
+
"key": "content",
|
|
13884
|
+
"label": "Content",
|
|
13885
|
+
"controls": [
|
|
13886
|
+
"cart_count_placeholder",
|
|
13887
|
+
"cart_count_hide_empty"
|
|
13888
|
+
]
|
|
13889
|
+
},
|
|
13890
|
+
{
|
|
13891
|
+
"key": "size",
|
|
13892
|
+
"label": "Size",
|
|
13893
|
+
"controls": [
|
|
13894
|
+
"width_select",
|
|
13895
|
+
"height_select",
|
|
13896
|
+
"size_bounds"
|
|
13897
|
+
]
|
|
13898
|
+
},
|
|
13899
|
+
{
|
|
13900
|
+
"key": "layout",
|
|
13901
|
+
"label": "Layout",
|
|
13902
|
+
"controls": [
|
|
13903
|
+
"padding",
|
|
13904
|
+
"margin",
|
|
13905
|
+
"vertical",
|
|
13906
|
+
"horizontal"
|
|
13907
|
+
]
|
|
13908
|
+
},
|
|
13909
|
+
{
|
|
13910
|
+
"key": "typography",
|
|
13911
|
+
"label": "Typography",
|
|
13912
|
+
"controls": [
|
|
13913
|
+
"font_family",
|
|
13914
|
+
"font_size",
|
|
13915
|
+
"text_style",
|
|
13916
|
+
"text_color"
|
|
13917
|
+
]
|
|
13918
|
+
},
|
|
13919
|
+
{
|
|
13920
|
+
"key": "background",
|
|
13921
|
+
"label": "Background",
|
|
13922
|
+
"controls": [
|
|
13923
|
+
"bg_color"
|
|
13924
|
+
]
|
|
13925
|
+
},
|
|
13926
|
+
{
|
|
13927
|
+
"key": "shape",
|
|
13928
|
+
"label": "Shape",
|
|
13929
|
+
"controls": [
|
|
13930
|
+
"border",
|
|
13931
|
+
"corner"
|
|
13932
|
+
]
|
|
13933
|
+
}
|
|
13934
|
+
]
|
|
13935
|
+
},
|
|
13936
|
+
{
|
|
13937
|
+
"tab": "advanced",
|
|
13938
|
+
"groups": [
|
|
13939
|
+
{
|
|
13940
|
+
"key": "spacing",
|
|
13941
|
+
"label": "Spacing",
|
|
13942
|
+
"controls": [
|
|
13943
|
+
"padding_margin"
|
|
13944
|
+
]
|
|
13945
|
+
},
|
|
13946
|
+
{
|
|
13947
|
+
"key": "display",
|
|
13948
|
+
"label": "Display",
|
|
13949
|
+
"controls": [
|
|
13950
|
+
"display"
|
|
13951
|
+
]
|
|
13952
|
+
},
|
|
13953
|
+
{
|
|
13954
|
+
"key": "class_css",
|
|
13955
|
+
"label": "Class",
|
|
13956
|
+
"controls": [
|
|
13957
|
+
"class_css"
|
|
13958
|
+
]
|
|
13959
|
+
}
|
|
13960
|
+
]
|
|
13961
|
+
}
|
|
13962
|
+
],
|
|
13963
|
+
"controls": [
|
|
13964
|
+
"cart_count_placeholder",
|
|
13965
|
+
"cart_count_hide_empty",
|
|
13966
|
+
"width_select",
|
|
13967
|
+
"height_select",
|
|
13968
|
+
"size_bounds",
|
|
13969
|
+
"padding",
|
|
13970
|
+
"margin",
|
|
13971
|
+
"vertical",
|
|
13972
|
+
"horizontal",
|
|
13973
|
+
"font_family",
|
|
13974
|
+
"font_size",
|
|
13975
|
+
"text_style",
|
|
13976
|
+
"text_color",
|
|
13977
|
+
"bg_color",
|
|
13978
|
+
"border",
|
|
13979
|
+
"corner",
|
|
13980
|
+
"padding_margin",
|
|
13981
|
+
"display",
|
|
13982
|
+
"class_css"
|
|
13983
|
+
],
|
|
13984
|
+
"description": "The number of items in the shopper's cart, as a small badge. The figure comes from the shopper's own cart in their browser, so it changes the moment they add, remove or re-count a line. Its usual home is the cart icon in the site header, where it is the only evidence on the page that the basket holds anything.",
|
|
13985
|
+
"useWhen": [
|
|
13986
|
+
"a header or navigation bar carries a cart icon and a shopper needs to see the basket is not empty",
|
|
13987
|
+
"building a sticky mobile bar or a mini-cart trigger anywhere outside the cart drawer"
|
|
13988
|
+
],
|
|
13989
|
+
"avoidWhen": [
|
|
13990
|
+
"showing what the cart COSTS — that is a cart-total, which reads a money figure",
|
|
13991
|
+
"showing how many of ONE product a shopper picked — that is the quantity stepper on the product",
|
|
13992
|
+
"showing a past order’s item count; an order is a record, not the live cart"
|
|
13993
|
+
],
|
|
13994
|
+
"contentTips": [
|
|
13995
|
+
"the placeholder is what the badge reads before the shopper’s cart is known, so write it as a real empty-cart count",
|
|
13996
|
+
"leave “hide when empty” on unless the header’s layout must never shift when the first item lands",
|
|
13997
|
+
"to put it on a cart icon, turn the badge on from the icon’s own inspector rather than placing this element and positioning it by hand"
|
|
13998
|
+
],
|
|
13999
|
+
"semantics": [
|
|
14000
|
+
"cart",
|
|
14001
|
+
"count",
|
|
14002
|
+
"badge",
|
|
14003
|
+
"header",
|
|
14004
|
+
"commerce"
|
|
14005
|
+
]
|
|
14006
|
+
},
|
|
13845
14007
|
"cart-total": {
|
|
13846
14008
|
"type": "cart-total",
|
|
13847
14009
|
"label": "Cart total",
|
|
@@ -13909,7 +14071,7 @@ export const ELEMENTS = {
|
|
|
13909
14071
|
"controls": [
|
|
13910
14072
|
"font_family",
|
|
13911
14073
|
"font_size",
|
|
13912
|
-
"
|
|
14074
|
+
"text_style",
|
|
13913
14075
|
"text_color"
|
|
13914
14076
|
]
|
|
13915
14077
|
},
|
|
@@ -13972,7 +14134,7 @@ export const ELEMENTS = {
|
|
|
13972
14134
|
"horizontal",
|
|
13973
14135
|
"font_family",
|
|
13974
14136
|
"font_size",
|
|
13975
|
-
"
|
|
14137
|
+
"text_style",
|
|
13976
14138
|
"text_color",
|
|
13977
14139
|
"bg_color",
|
|
13978
14140
|
"border",
|
|
@@ -14330,7 +14492,8 @@ export const ELEMENTS = {
|
|
|
14330
14492
|
"paginationItemWidth": 8,
|
|
14331
14493
|
"paginationItemHeight": 8,
|
|
14332
14494
|
"paginationItemGap": 4,
|
|
14333
|
-
"featureClickAction": "open_gallery"
|
|
14495
|
+
"featureClickAction": "open_gallery",
|
|
14496
|
+
"hoverSwapImage": false
|
|
14334
14497
|
},
|
|
14335
14498
|
"specials": {
|
|
14336
14499
|
"alt": ""
|
|
@@ -16332,6 +16495,7 @@ export const ELEMENTS = {
|
|
|
16332
16495
|
"image_alt",
|
|
16333
16496
|
"main_image_source",
|
|
16334
16497
|
"feature_zoom",
|
|
16498
|
+
"feature_hover_swap",
|
|
16335
16499
|
"feature_click_action",
|
|
16336
16500
|
"feature_nav",
|
|
16337
16501
|
"feature_dots",
|
|
@@ -16354,6 +16518,7 @@ export const ELEMENTS = {
|
|
|
16354
16518
|
"listNavButtonBg",
|
|
16355
16519
|
"hoverZoomType",
|
|
16356
16520
|
"hoverZoomValue",
|
|
16521
|
+
"hoverSwapImage",
|
|
16357
16522
|
"paginationBg",
|
|
16358
16523
|
"paginationItemBg",
|
|
16359
16524
|
"paginationItemActiveBg",
|
|
@@ -16439,6 +16604,7 @@ export const ELEMENTS = {
|
|
|
16439
16604
|
"image_alt",
|
|
16440
16605
|
"main_image_source",
|
|
16441
16606
|
"feature_zoom",
|
|
16607
|
+
"feature_hover_swap",
|
|
16442
16608
|
"feature_click_action",
|
|
16443
16609
|
"feature_nav",
|
|
16444
16610
|
"feature_dots",
|
|
@@ -16461,6 +16627,7 @@ export const ELEMENTS = {
|
|
|
16461
16627
|
"listNavButtonBg",
|
|
16462
16628
|
"hoverZoomType",
|
|
16463
16629
|
"hoverZoomValue",
|
|
16630
|
+
"hoverSwapImage",
|
|
16464
16631
|
"paginationBg",
|
|
16465
16632
|
"paginationItemBg",
|
|
16466
16633
|
"paginationItemActiveBg",
|
|
@@ -16486,7 +16653,7 @@ export const ELEMENTS = {
|
|
|
16486
16653
|
"animation",
|
|
16487
16654
|
"class_css"
|
|
16488
16655
|
],
|
|
16489
|
-
"description": "A large product feature image carousel — stacks its images and shows the active one at full size, with optional prev/next navigation, pagination dots,
|
|
16656
|
+
"description": "A large product feature image carousel — stacks its images and shows the active one at full size, with optional prev/next navigation, pagination dots, hover-zoom and a hover image swap (config.hoverSwapImage) on published pages. When pinned to (or placed under) a product it carousels that product's full image gallery (config.mainImageSource picks which image leads), falling back to the seeded config.images when unbound. Owns no children.",
|
|
16490
16657
|
"useWhen": [
|
|
16491
16658
|
"Showing the primary/large product photo on a product detail layout.",
|
|
16492
16659
|
"You want visitors to page through several product images."
|
|
@@ -16499,7 +16666,8 @@ export const ELEMENTS = {
|
|
|
16499
16666
|
"Pair it with an Image list so shoppers can pick which photo shows large.",
|
|
16500
16667
|
"Use a consistent image ratio (config.featureImageRatio) so the layout does not jump between images — default is a uniform 1:1 square; \"auto\" uses each image's natural size.",
|
|
16501
16668
|
"Hover-zoom is on by default at 150% (config.hoverZoomValue); set it to 100 to disable, or raise it up to 300 for a closer zoom.",
|
|
16502
|
-
"Nav arrows sit \"inside\" (overlaid) or \"outside\" the image via config.listNavPosition."
|
|
16669
|
+
"Nav arrows sit \"inside\" (overlaid) or \"outside\" the image via config.listNavPosition.",
|
|
16670
|
+
"config.hoverSwapImage shows the NEXT gallery photo while the pointer is on the tile — the product-grid effect. It needs a gallery of 2+, is off by default, and does nothing on a touch screen (no pointer), so a phone keeps the first photo."
|
|
16503
16671
|
],
|
|
16504
16672
|
"semantics": [
|
|
16505
16673
|
"product-image",
|
|
@@ -33572,6 +33740,17 @@ export const TRAIT_WRITES = {
|
|
|
33572
33740
|
}
|
|
33573
33741
|
]
|
|
33574
33742
|
},
|
|
33743
|
+
"feature_hover_swap": {
|
|
33744
|
+
"key": "feature_hover_swap",
|
|
33745
|
+
"label": "Image swap",
|
|
33746
|
+
"writes": [
|
|
33747
|
+
{
|
|
33748
|
+
"target": "config",
|
|
33749
|
+
"writeKey": "hoverSwapImage",
|
|
33750
|
+
"type": "boolean"
|
|
33751
|
+
}
|
|
33752
|
+
]
|
|
33753
|
+
},
|
|
33575
33754
|
"overflow_x": {
|
|
33576
33755
|
"key": "overflow_x",
|
|
33577
33756
|
"label": "Overflow X",
|
|
@@ -33677,6 +33856,28 @@ export const TRAIT_WRITES = {
|
|
|
33677
33856
|
}
|
|
33678
33857
|
]
|
|
33679
33858
|
},
|
|
33859
|
+
"cart_count_placeholder": {
|
|
33860
|
+
"key": "cart_count_placeholder",
|
|
33861
|
+
"label": "Empty count",
|
|
33862
|
+
"writes": [
|
|
33863
|
+
{
|
|
33864
|
+
"target": "specials",
|
|
33865
|
+
"writeKey": "placeholder",
|
|
33866
|
+
"type": "string"
|
|
33867
|
+
}
|
|
33868
|
+
]
|
|
33869
|
+
},
|
|
33870
|
+
"cart_count_hide_empty": {
|
|
33871
|
+
"key": "cart_count_hide_empty",
|
|
33872
|
+
"label": "Hide when empty",
|
|
33873
|
+
"writes": [
|
|
33874
|
+
{
|
|
33875
|
+
"target": "specials",
|
|
33876
|
+
"writeKey": "hideWhenEmpty",
|
|
33877
|
+
"type": "boolean"
|
|
33878
|
+
}
|
|
33879
|
+
]
|
|
33880
|
+
},
|
|
33680
33881
|
"cart_total_placeholder": {
|
|
33681
33882
|
"key": "cart_total_placeholder",
|
|
33682
33883
|
"label": "Empty amount",
|
|
@@ -34037,6 +34238,13 @@ export const TRAIT_WRITES = {
|
|
|
34037
34238
|
}
|
|
34038
34239
|
};
|
|
34039
34240
|
export const SATELLITE_RULES = {
|
|
34241
|
+
"icon": [
|
|
34242
|
+
{
|
|
34243
|
+
"type": "cart-count",
|
|
34244
|
+
"configKey": "cartCountId",
|
|
34245
|
+
"optional": true
|
|
34246
|
+
}
|
|
34247
|
+
],
|
|
34040
34248
|
"tab": [
|
|
34041
34249
|
{
|
|
34042
34250
|
"type": "tab-item",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const SHAPE_SOURCE = {
|
|
2
2
|
"writeOperations": 212,
|
|
3
|
-
"shaped":
|
|
4
|
-
"fromHandlers":
|
|
3
|
+
"shaped": 162,
|
|
4
|
+
"fromHandlers": 162,
|
|
5
5
|
"fromSwaggerOnly": 0,
|
|
6
6
|
"withReadOnly": 26,
|
|
7
7
|
"structsRead": 1597
|
|
@@ -171,6 +171,110 @@ export const REQUEST_SHAPES = {
|
|
|
171
171
|
"source": "go",
|
|
172
172
|
"goType": "versionInput"
|
|
173
173
|
},
|
|
174
|
+
"post:/api/orgs/{orgId}/apps/{appId}/versions/{versionId}/withdraw": {
|
|
175
|
+
"fields": [
|
|
176
|
+
{
|
|
177
|
+
"name": "version",
|
|
178
|
+
"type": "string"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"name": "scopes",
|
|
182
|
+
"type": "string[]"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"name": "embedUrl",
|
|
186
|
+
"type": "string"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"name": "redirectUri",
|
|
190
|
+
"type": "string"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"name": "summary",
|
|
194
|
+
"type": "string"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "description",
|
|
198
|
+
"type": "string"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "category",
|
|
202
|
+
"type": "string",
|
|
203
|
+
"note": "Category is the marketplace shelf, from apps."
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"name": "pricingModel",
|
|
207
|
+
"type": "string",
|
|
208
|
+
"note": "PricingModel, PriceCents, PriceCurrency and BillingInterval are what this version costs a merchant. THIS ROUTE IS A FULL REPLACEMENT, and that includes the price: a client that omits `pricingModel` is saying free, exactly as one that…"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"name": "priceCents",
|
|
212
|
+
"type": "number"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"name": "priceCurrency",
|
|
216
|
+
"type": "string"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "billingInterval",
|
|
220
|
+
"type": "string"
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
"source": "go",
|
|
224
|
+
"goType": "versionInput"
|
|
225
|
+
},
|
|
226
|
+
"post:/api/orgs/{orgId}/apps/{appId}/versions/{versionId}/images": {
|
|
227
|
+
"fields": [
|
|
228
|
+
{
|
|
229
|
+
"name": "version",
|
|
230
|
+
"type": "string"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"name": "scopes",
|
|
234
|
+
"type": "string[]"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"name": "embedUrl",
|
|
238
|
+
"type": "string"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "redirectUri",
|
|
242
|
+
"type": "string"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"name": "summary",
|
|
246
|
+
"type": "string"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "description",
|
|
250
|
+
"type": "string"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"name": "category",
|
|
254
|
+
"type": "string",
|
|
255
|
+
"note": "Category is the marketplace shelf, from apps."
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"name": "pricingModel",
|
|
259
|
+
"type": "string",
|
|
260
|
+
"note": "PricingModel, PriceCents, PriceCurrency and BillingInterval are what this version costs a merchant. THIS ROUTE IS A FULL REPLACEMENT, and that includes the price: a client that omits `pricingModel` is saying free, exactly as one that…"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"name": "priceCents",
|
|
264
|
+
"type": "number"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"name": "priceCurrency",
|
|
268
|
+
"type": "string"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"name": "billingInterval",
|
|
272
|
+
"type": "string"
|
|
273
|
+
}
|
|
274
|
+
],
|
|
275
|
+
"source": "go",
|
|
276
|
+
"goType": "versionInput"
|
|
277
|
+
},
|
|
174
278
|
"post:/api/sites/{siteId}/quizzes": {
|
|
175
279
|
"fields": [
|
|
176
280
|
{
|
|
@@ -1790,7 +1894,7 @@ export const REQUEST_SHAPES = {
|
|
|
1790
1894
|
"source": "go",
|
|
1791
1895
|
"goType": "courses.Course"
|
|
1792
1896
|
},
|
|
1793
|
-
"
|
|
1897
|
+
"post:/api/sites/{siteId}/courses/{courseId}/sections": {
|
|
1794
1898
|
"fields": [
|
|
1795
1899
|
{
|
|
1796
1900
|
"name": "id",
|
|
@@ -1833,7 +1937,7 @@ export const REQUEST_SHAPES = {
|
|
|
1833
1937
|
"source": "go",
|
|
1834
1938
|
"goType": "courses.Section"
|
|
1835
1939
|
},
|
|
1836
|
-
"
|
|
1940
|
+
"put:/api/sites/{siteId}/courses/{courseId}/sections/{sectionId}": {
|
|
1837
1941
|
"fields": [
|
|
1838
1942
|
{
|
|
1839
1943
|
"name": "id",
|
|
@@ -1876,7 +1980,7 @@ export const REQUEST_SHAPES = {
|
|
|
1876
1980
|
"source": "go",
|
|
1877
1981
|
"goType": "courses.Section"
|
|
1878
1982
|
},
|
|
1879
|
-
"
|
|
1983
|
+
"post:/api/sites/{siteId}/courses/{courseId}/lessons": {
|
|
1880
1984
|
"fields": [
|
|
1881
1985
|
{
|
|
1882
1986
|
"name": "id",
|
|
@@ -1972,7 +2076,7 @@ export const REQUEST_SHAPES = {
|
|
|
1972
2076
|
"source": "go",
|
|
1973
2077
|
"goType": "courses.Lesson"
|
|
1974
2078
|
},
|
|
1975
|
-
"
|
|
2079
|
+
"put:/api/sites/{siteId}/courses/{courseId}/lessons/{lessonId}": {
|
|
1976
2080
|
"fields": [
|
|
1977
2081
|
{
|
|
1978
2082
|
"name": "id",
|
|
@@ -4762,10 +4866,10 @@ export const REQUEST_SHAPES = {
|
|
|
4762
4866
|
"updatedAt"
|
|
4763
4867
|
]
|
|
4764
4868
|
},
|
|
4765
|
-
"put:/api/sites/{siteId}/
|
|
4869
|
+
"put:/api/sites/{siteId}/products/{productId}/categories": {
|
|
4766
4870
|
"fields": [
|
|
4767
4871
|
{
|
|
4768
|
-
"name": "
|
|
4872
|
+
"name": "categoryIds",
|
|
4769
4873
|
"type": "string[]"
|
|
4770
4874
|
}
|
|
4771
4875
|
],
|
|
@@ -4782,6 +4886,16 @@ export const REQUEST_SHAPES = {
|
|
|
4782
4886
|
"source": "go",
|
|
4783
4887
|
"goType": "(inline)"
|
|
4784
4888
|
},
|
|
4889
|
+
"put:/api/sites/{siteId}/product-categories/{id}/products": {
|
|
4890
|
+
"fields": [
|
|
4891
|
+
{
|
|
4892
|
+
"name": "productIds",
|
|
4893
|
+
"type": "string[]"
|
|
4894
|
+
}
|
|
4895
|
+
],
|
|
4896
|
+
"source": "go",
|
|
4897
|
+
"goType": "(inline)"
|
|
4898
|
+
},
|
|
4785
4899
|
"post:/api/sites/{siteId}/product-categories": {
|
|
4786
4900
|
"fields": [
|
|
4787
4901
|
{
|
|
@@ -6337,6 +6451,30 @@ export const REQUEST_SHAPES = {
|
|
|
6337
6451
|
"source": "go",
|
|
6338
6452
|
"goType": "(inline)"
|
|
6339
6453
|
},
|
|
6454
|
+
"put:/api/sites/{siteId}/roles/{roleId}": {
|
|
6455
|
+
"fields": [
|
|
6456
|
+
{
|
|
6457
|
+
"name": "name",
|
|
6458
|
+
"type": "string"
|
|
6459
|
+
},
|
|
6460
|
+
{
|
|
6461
|
+
"name": "permissions",
|
|
6462
|
+
"type": "site.Permission[]"
|
|
6463
|
+
}
|
|
6464
|
+
],
|
|
6465
|
+
"source": "go",
|
|
6466
|
+
"goType": "(inline)"
|
|
6467
|
+
},
|
|
6468
|
+
"put:/api/sites/{siteId}/org": {
|
|
6469
|
+
"fields": [
|
|
6470
|
+
{
|
|
6471
|
+
"name": "orgId",
|
|
6472
|
+
"type": "string"
|
|
6473
|
+
}
|
|
6474
|
+
],
|
|
6475
|
+
"source": "go",
|
|
6476
|
+
"goType": "(inline)"
|
|
6477
|
+
},
|
|
6340
6478
|
"put:/api/sites/{id}/owner": {
|
|
6341
6479
|
"fields": [
|
|
6342
6480
|
{
|
|
@@ -72,11 +72,22 @@ export function hoverHome(type) {
|
|
|
72
72
|
*/
|
|
73
73
|
const HOVER_UNCOMPILED = new Set(['product-image-list']);
|
|
74
74
|
/**
|
|
75
|
-
* The
|
|
75
|
+
* The specials key naming WHICH ancestor a parent-hover rule hangs off, 1-based
|
|
76
|
+
* and nearest-first. Base-only: it is identity, not a quantity, and a host that
|
|
77
|
+
* differed per breakpoint would compile one state slot against different
|
|
78
|
+
* selectors at different widths.
|
|
79
|
+
*
|
|
80
|
+
* Absent means 1 — the node's own parent — which is what the seeded product card
|
|
81
|
+
* wants, because that template is flat and a child's parent IS the card. The key
|
|
82
|
+
* exists for the moment an author groups a few things inside it: the nearest box
|
|
83
|
+
* becomes the group, and "hover the whole card" stops being reachable without it.
|
|
84
|
+
*/
|
|
85
|
+
export const HOVER_HOST_DEPTH = 'hoverHostDepth';
|
|
86
|
+
/**
|
|
87
|
+
* Every ancestor a parent-hover state could hang off, NEAREST FIRST.
|
|
76
88
|
*
|
|
77
|
-
* Mirrors `
|
|
78
|
-
*
|
|
79
|
-
* nothing:
|
|
89
|
+
* Mirrors `hoverHostChain`. Empty for the three cases that can host nothing, and
|
|
90
|
+
* each is a rule that would compile to nothing:
|
|
80
91
|
*
|
|
81
92
|
* - a SATELLITE, which hangs off `config[key]` rather than `data.nodes` and
|
|
82
93
|
* renders no element the selector could name;
|
|
@@ -84,17 +95,57 @@ const HOVER_UNCOMPILED = new Set(['product-image-list']);
|
|
|
84
95
|
* whenever it is inside the window;
|
|
85
96
|
* - an orphan whose parent id points at nothing.
|
|
86
97
|
*/
|
|
87
|
-
export function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
export function hoverHostChain(doc, id) {
|
|
99
|
+
if (!hasHoverableBox(doc, id))
|
|
100
|
+
return [];
|
|
101
|
+
const out = [];
|
|
102
|
+
const seen = new Set([id]);
|
|
103
|
+
let cur = doc.nodes[id];
|
|
104
|
+
for (;;) {
|
|
105
|
+
const parentId = cur?.data.parent;
|
|
106
|
+
if (!parentId || seen.has(parentId))
|
|
107
|
+
break;
|
|
108
|
+
seen.add(parentId);
|
|
109
|
+
const parent = doc.nodes[parentId];
|
|
110
|
+
if (!parent || parent.data.type === 'root' || parentId === doc.root_node_id)
|
|
111
|
+
break;
|
|
112
|
+
out.push(parentId);
|
|
113
|
+
cur = parent;
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/** Does this node have a box of its own — a real child of a real parent? */
|
|
118
|
+
function hasHoverableBox(doc, id) {
|
|
119
|
+
const parentId = doc.nodes[id]?.data.parent;
|
|
120
|
+
if (!parentId)
|
|
121
|
+
return false;
|
|
92
122
|
const parent = doc.nodes[parentId];
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
123
|
+
return !!parent && (parent.data.nodes ?? []).includes(id);
|
|
124
|
+
}
|
|
125
|
+
/** The stored depth, coerced the way the platform coerces it: anything that is
|
|
126
|
+
* not a whole number ≥ 1 reads as 1, so a corrupt value costs the author the
|
|
127
|
+
* ancestor they picked rather than the whole state. */
|
|
128
|
+
export function hoverHostDepthOf(doc, id) {
|
|
129
|
+
const raw = doc.nodes[id]?.specials?.[HOVER_HOST_DEPTH];
|
|
130
|
+
const n = typeof raw === 'number' ? raw : Number(raw);
|
|
131
|
+
if (!Number.isFinite(n) || n < 1)
|
|
132
|
+
return 1;
|
|
133
|
+
return Math.floor(n);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The box whose `:hover` a parent-hover rule keys off, or null.
|
|
137
|
+
*
|
|
138
|
+
* The node's PARENT by default, or the ancestor `hoverHostDepth` levels up when
|
|
139
|
+
* the document names one. A depth past the end of the chain CLAMPS to the
|
|
140
|
+
* outermost real ancestor rather than answering null — the chain shortens
|
|
141
|
+
* whenever a wrapper is deleted, and a state pointing one level out is better
|
|
142
|
+
* than one that silently goes dead.
|
|
143
|
+
*/
|
|
144
|
+
export function hoverHostOf(doc, id) {
|
|
145
|
+
const chain = hoverHostChain(doc, id);
|
|
146
|
+
if (!chain.length)
|
|
96
147
|
return null;
|
|
97
|
-
return
|
|
148
|
+
return chain[Math.min(hoverHostDepthOf(doc, id), chain.length) - 1];
|
|
98
149
|
}
|
|
99
150
|
/** Is this node a satellite — referenced from its parent's config, not its children? */
|
|
100
151
|
export function isSatelliteNode(doc, id) {
|
|
@@ -122,11 +173,43 @@ export function requireHoverHost(doc, id) {
|
|
|
122
173
|
if (!parentId) {
|
|
123
174
|
throw new Error(`sbuilder: ${id} has no parent, so "${PARENT_HOVER_STATE}" has nothing to key off.`);
|
|
124
175
|
}
|
|
125
|
-
throw new Error(`sbuilder: "${PARENT_HOVER_STATE}"
|
|
176
|
+
throw new Error(`sbuilder: "${PARENT_HOVER_STATE}" hangs off an ANCESTOR BOX, and ${id}'s only one is ROOT — ` +
|
|
126
177
|
'the pointer is inside the page whenever it is inside the window, so the platform emits ' +
|
|
127
178
|
'no rule. Put this node inside a real box (a section\'s inner block, a card) and set it ' +
|
|
128
179
|
`there, or use state: "${HOVER_STATE}" for the node's own pointer state.`);
|
|
129
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* WHICH BOX A PARENT-HOVER RULE ACTUALLY HUNG OFF, said out loud.
|
|
183
|
+
*
|
|
184
|
+
* The inspector has a picker and a label that name it; an agent has neither, and
|
|
185
|
+
* the default — the NEAREST box — is the wrong one the moment somebody groups a
|
|
186
|
+
* few things inside a card. Writing the state and being told nothing is how a
|
|
187
|
+
* "hover the card" effect ends up triggered by an inner wrapper instead.
|
|
188
|
+
*
|
|
189
|
+
* Also reports a depth that CLAMPED, which the platform does silently: past the
|
|
190
|
+
* end of the chain it falls back to the outermost ancestor rather than going
|
|
191
|
+
* dead, so the caller's number and the box they got can differ with nothing on
|
|
192
|
+
* screen to say so.
|
|
193
|
+
*/
|
|
194
|
+
export function hoverHostNote(doc, id) {
|
|
195
|
+
const chain = hoverHostChain(doc, id);
|
|
196
|
+
if (!chain.length)
|
|
197
|
+
return null;
|
|
198
|
+
const want = hoverHostDepthOf(doc, id);
|
|
199
|
+
const got = Math.min(want, chain.length);
|
|
200
|
+
const host = chain[got - 1];
|
|
201
|
+
const named = (n) => `${doc.nodes[n]?.data.name ?? doc.nodes[n]?.data.type ?? '?'} ${n}`;
|
|
202
|
+
if (want > chain.length) {
|
|
203
|
+
return (`Hangs off ${named(host)} — specials.${HOVER_HOST_DEPTH} asks for level ${want} and the ` +
|
|
204
|
+
`chain is ${chain.length} long, so the platform CLAMPED to the outermost box. Set the ` +
|
|
205
|
+
'depth to a level that exists, or accept this one.');
|
|
206
|
+
}
|
|
207
|
+
if (chain.length === 1)
|
|
208
|
+
return null; // no choice to get wrong
|
|
209
|
+
return (`Hangs off ${named(host)} (level ${got} of ${chain.length}). To hang it off a wider box — ` +
|
|
210
|
+
`the whole card rather than the group inside it — set specials.${HOVER_HOST_DEPTH}, ` +
|
|
211
|
+
`1-based and nearest-first: ${chain.map((n, i) => `${i + 1}=${named(n)}`).join(', ')}.`);
|
|
212
|
+
}
|
|
130
213
|
/**
|
|
131
214
|
* The one config key a hover state translates into a declaration, and only
|
|
132
215
|
* `true` — the same contract `stuckDecls` has, for the same reason: `false`
|
package/dist/tools/page.js
CHANGED
|
@@ -15,7 +15,7 @@ import { globalWarning, restampPatches, RESPONSIVE_NOTICE } from '../domains/sit
|
|
|
15
15
|
import { catalogMatches, traitsFor } from '../catalog/element-search.js';
|
|
16
16
|
import { applyPatches } from '../core/patch.js';
|
|
17
17
|
import { stickyWarning } from '../domains/site/sticky.js';
|
|
18
|
-
import { HOVER_STATE, hoverRoutingNote } from '../domains/site/hover.js';
|
|
18
|
+
import { HOVER_STATE, PARENT_HOVER_STATE, hoverHostNote, hoverRoutingNote } from '../domains/site/hover.js';
|
|
19
19
|
import { siteFor } from './context.js';
|
|
20
20
|
import { projectList, PAGE_FIELDS, TEMPLATE_FIELDS } from './project.js';
|
|
21
21
|
/**
|
|
@@ -321,6 +321,17 @@ export function registerPageTools(server, ctx) {
|
|
|
321
321
|
if (hoverRoutingNote(type))
|
|
322
322
|
hoverTypes.add(type);
|
|
323
323
|
}
|
|
324
|
+
// WHICH BOX a parent-hover rule hung off. Per NODE rather than once per
|
|
325
|
+
// process: the answer is about this node's ancestry, so a second node's is
|
|
326
|
+
// genuinely different information.
|
|
327
|
+
const hostNotes = {};
|
|
328
|
+
for (const e of batch) {
|
|
329
|
+
if (e.state !== PARENT_HOVER_STATE)
|
|
330
|
+
continue;
|
|
331
|
+
const n = hoverHostNote(d.doc, e.id);
|
|
332
|
+
if (n)
|
|
333
|
+
hostNotes[e.id] = n;
|
|
334
|
+
}
|
|
324
335
|
const hoverNote = [...hoverTypes]
|
|
325
336
|
.map((t) => ctx.notices.once(`hover-home:${t}`, hoverRoutingNote(t)))
|
|
326
337
|
.filter((n) => !!n)
|
|
@@ -356,6 +367,7 @@ export function registerPageTools(server, ctx) {
|
|
|
356
367
|
patches,
|
|
357
368
|
...(Object.keys(sw).length ? { warnings: sw } : {}),
|
|
358
369
|
...(hoverNote ? { hover: hoverNote } : {}),
|
|
370
|
+
...(Object.keys(hostNotes).length ? { hover_host: hostNotes } : {}),
|
|
359
371
|
...(note ? { note } : {}),
|
|
360
372
|
});
|
|
361
373
|
}
|
|
@@ -374,6 +386,7 @@ export function registerPageTools(server, ctx) {
|
|
|
374
386
|
rev: d.rev,
|
|
375
387
|
...(warn ? { warning: warn } : {}),
|
|
376
388
|
...(hoverNote ? { hover: hoverNote } : {}),
|
|
389
|
+
...(hostNotes[batch[0].id] ? { hover_host: hostNotes[batch[0].id] } : {}),
|
|
377
390
|
});
|
|
378
391
|
}
|
|
379
392
|
return text({
|
|
@@ -381,6 +394,7 @@ export function registerPageTools(server, ctx) {
|
|
|
381
394
|
rev: d.rev,
|
|
382
395
|
...(Object.keys(warnings).length ? { warnings } : {}),
|
|
383
396
|
...(hoverNote ? { hover: hoverNote } : {}),
|
|
397
|
+
...(Object.keys(hostNotes).length ? { hover_host: hostNotes } : {}),
|
|
384
398
|
});
|
|
385
399
|
});
|
|
386
400
|
server.registerTool('sb_move', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sbuilder-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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",
|