sbuilder-mcp 0.14.1 → 0.16.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 CHANGED
@@ -6,6 +6,34 @@ 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.16.0] - 2026-09-10
10
+
11
+ ### Added
12
+ - sb_traits_for now returns a `translatable` block naming which of an element's specials a translation may safely rewrite, and which of its own specials must never be translated, since translating a non-content special (a lucide icon `name`, a `src` URL, a `filterSource` registry id) does not degrade the page, it breaks the render.
13
+ - sb_api_find and sb_api_call now attach a `translation_fields` call sheet to every `/translations` operation, listing the translatable columns for each entity type and pointing to sb_traits_for for the per-element `node` vocabulary, since every translations route was already reachable with no way to know which fields were safe to send.
14
+ - sb_set now warns once per node type when a field-skin config key (`payCardBg`, `choice*`, `slot*`, `file*`, and the rest of the 55-key vocabulary) is written on a form node whose renderer does not read it, naming the field node that actually renders it, since such a write is stored, saved and published but rendered nowhere.
15
+
16
+ ### Internal
17
+ - The generated catalog gained a translations table (`translations.generated.ts`, 141 translatable specials across 58 elements, 156 keys classified as never-translate, 12 entity types with their columns) and a field-skin table (`fieldskin.generated.ts`), both cross-checked at codegen time against the platform's own registries rather than hand-kept.
18
+
19
+ ## [0.15.0] - 2026-09-10
20
+
21
+ ### Added
22
+ - sb_set now writes a config key that a page's `html.go` renderer reads only at base (13 keys, including the repeater data axis `datasetSource`, `kind`, `collectionId`, `collectionType`) to base instead of the current breakpoint, and reports the move as `base_only`, since a per-breakpoint write to one of these keys updated the editor canvas and silently vanished on publish.
23
+ - sb_traits_for now returns `config_values` for the config keys whose platform renderer treats an unrecognized value as a silent alias rather than an error, listing the accepted values and aliases (`category` as a working spelling of `collection`, among them), since a repeater set to a plausible-sounding word like "bestseller" previously published and rendered the whole catalogue under the wrong heading with no error anywhere.
24
+ - sb_set now warns once per config key/value pair when a write sets one of these config values to something the renderer does not recognize.
25
+ - sb_node_read now returns a `preset` block naming the theme preset a node paints through, its resolved colors and other values with every var() chain flattened, and which keys the node has already overridden, closing nine element types (including icon, button, heading, text, image) whose default look now lives in a site theme preset rather than the node's own style, and which previously read back as no color at all on a page visibly painting one.
26
+ - sb_set now warns once per theme preset when a plain style write is about to permanently detach a node from that preset, since the node's own value then outranks the preset on every future palette change.
27
+ - sb_add now returns an `inert` note when a subtree includes a locale-switcher (which paints a fabricated locale chip and switches nothing below two configured site locales) or a breadcrumb (whose root "Home" label is authorable and defaults to English), since both elements render convincingly while wired to nothing, in a way neither sb_review nor sb_look can detect after the fact.
28
+ - sb_page_create now seeds a store page type (product, category, search, blog, post, complete) with the same document the platform's editor gives a merchant — a product page arrives with its whole buy box, including the `add_to_cart` binding — instead of an empty page; pass `seed:false` for a blank page, and `locale`/`headline` to pick the completion page's thank-you line.
29
+ - npm run codegen and npm run codegen:check now warn (without failing) when a route is annotated in the platform's server code but absent from swagger.json, naming the missing routes, since this gap does not get fixed by regenerating anything in this repo.
30
+
31
+ ### Fixed
32
+ - The element and operation catalog is refreshed to the platform's `8e40bbab`: five relation-slot operations (curated-shelf create/update/delete and both picks endpoints) that were reachable but shared one annotation with the list endpoint are now separately documented, and the AI chat assistant's whole configuration surface (`GET /api/chat-providers`, `GET/PUT/DELETE /api/sites/{siteId}/chat-settings`) is documented for the first time; the catalog now covers 495 operations, 166 of 216 write operations with a request body shape.
33
+
34
+ ### Internal
35
+ - The generated catalog gained a store-page-seed table (`storepages.generated.ts`) and a theme-preset table (`theme.generated.ts`), both produced by calling the platform's own seed and theme-preset builders rather than by copying their output, so the next platform change to either reaches this server's next codegen run automatically.
36
+
9
37
  ## [0.14.1] - 2026-09-09
10
38
 
11
39
  ### Added
package/CHANGELOG.vi.md CHANGED
@@ -6,6 +6,34 @@ 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.16.0] - 2026-09-10
10
+
11
+ ### Added
12
+ - sb_traits_for giờ trả về một khối `translatable` nêu tên những special nào của element có thể an toàn để một bản dịch ghi đè, và những special riêng của nó không bao giờ được dịch, vì dịch một special không phải nội dung (một `name` icon lucide, một URL `src`, một id registry `filterSource`) không làm trang xấu đi, mà làm hỏng hẳn phần render.
13
+ - sb_api_find và sb_api_call giờ đính kèm một call sheet `translation_fields` vào mọi operation `/translations`, liệt kê các cột có thể dịch cho từng loại entity và trỏ tới sb_traits_for để lấy vocabulary riêng theo từng element cho entity `node`, vì trước đây mọi route translations đều đã gọi được mà không có cách nào biết field nào an toàn để gửi.
14
+ - sb_set giờ cảnh báo một lần cho mỗi loại node khi một config key thuộc field-skin (`payCardBg`, `choice*`, `slot*`, `file*`, và phần còn lại trong vocabulary 55 key) được ghi trên một form node mà renderer của nó không đọc key đó, nêu tên field node thực sự render nó, vì việc ghi này vẫn được lưu, save và publish nhưng không được render ở đâu cả.
15
+
16
+ ### Internal
17
+ - Catalog được sinh ra giờ có thêm bảng translations (`translations.generated.ts`, 141 special có thể dịch trên 58 element, 156 key được phân loại không bao giờ dịch, 12 loại entity cùng các cột của chúng) và bảng field-skin (`fieldskin.generated.ts`), cả hai đều được đối chiếu chéo lúc codegen với chính các registry của nền tảng thay vì giữ thủ công.
18
+
19
+ ## [0.15.0] - 2026-09-10
20
+
21
+ ### Added
22
+ - sb_set giờ ghi một config key mà renderer `html.go` của trang chỉ đọc ở base (13 key, gồm cả trục dữ liệu của repeater `datasetSource`, `kind`, `collectionId`, `collectionType`) xuống base thay vì breakpoint hiện tại, và báo cáo việc chuyển này là `base_only`, vì trước đây ghi theo breakpoint lên một trong các key này chỉ cập nhật canvas của editor rồi âm thầm biến mất khi publish.
23
+ - sb_traits_for giờ trả về `config_values` cho các config key mà renderer của nền tảng coi một giá trị không nhận diện được là một cách viết khác (alias) thay vì báo lỗi, liệt kê các giá trị hợp lệ và alias của chúng (`category` là một cách viết khác của `collection`, chẳng hạn), vì trước đây một repeater đặt giá trị nghe hợp lý như "bestseller" sẽ publish và render toàn bộ catalogue dưới một tiêu đề sai mà không báo lỗi ở đâu cả.
24
+ - sb_set giờ cảnh báo một lần cho mỗi cặp config key/giá trị khi một lần ghi đặt một trong các giá trị config này thành thứ mà renderer không nhận diện được.
25
+ - sb_node_read giờ trả về một khối `preset` nêu tên theme preset mà một node dùng để vẽ, các giá trị màu và thuộc tính khác của nó với mọi chuỗi var() đã được làm phẳng, cùng những key mà node đã override, khép lại khoảng trống ở chín loại element (gồm icon, button, heading, text, image) mà giao diện mặc định của chúng giờ nằm trong một theme preset của site chứ không phải trong style riêng của node, và trước đây đọc lại không thấy màu nào trên một trang rõ ràng đang tô màu đó.
26
+ - sb_set giờ cảnh báo một lần cho mỗi theme preset khi một lần ghi style thuần sắp làm một node tách khỏi preset đó vĩnh viễn, vì giá trị riêng của node từ đó sẽ luôn thắng preset ở mọi lần đổi bảng màu sau này.
27
+ - sb_add giờ trả về một ghi chú `inert` khi một subtree chứa locale-switcher (vẽ ra một chip ngôn ngữ giả và không chuyển đổi gì cả khi site có dưới hai locale được cấu hình) hoặc breadcrumb (nhãn "Home" ở gốc có thể chỉnh sửa và mặc định là tiếng Anh), vì cả hai element đều hiển thị thuyết phục trong khi không được nối với gì cả, theo cách mà cả sb_review lẫn sb_look đều không thể phát hiện sau khi đã thêm.
28
+ - sb_page_create giờ gieo sẵn nội dung cho một loại trang store (product, category, search, blog, post, complete) bằng đúng document mà editor của nền tảng đưa cho merchant — một trang product sẽ có sẵn cả buy box, bao gồm cả binding `add_to_cart` — thay vì một trang trống; truyền `seed:false` để có trang trống, và `locale`/`headline` để chọn câu cảm ơn của trang hoàn tất đơn hàng.
29
+ - npm run codegen và npm run codegen:check giờ cảnh báo (không làm fail) khi một route được annotate trong code server của nền tảng nhưng lại vắng mặt trong swagger.json, nêu tên các route còn thiếu, vì khoảng trống này không thể khắc phục bằng cách regenerate bất cứ thứ gì trong repo này.
30
+
31
+ ### Fixed
32
+ - Catalog element và operation được làm mới theo đúng bản `8e40bbab` của nền tảng: năm relation-slot operation (tạo/sửa/xoá kệ hàng tuyển chọn và cả hai endpoint picks) trước đây có thể gọi được nhưng dùng chung một annotation với endpoint danh sách, giờ được ghi tài liệu riêng; và toàn bộ bề mặt cấu hình của trợ lý chat AI (`GET /api/chat-providers`, `GET/PUT/DELETE /api/sites/{siteId}/chat-settings`) lần đầu tiên được ghi tài liệu; catalog giờ bao phủ 495 operation, 166 trong số 216 write operation có shape body request.
33
+
34
+ ### Internal
35
+ - Catalog được sinh ra giờ có thêm bảng seed trang store (`storepages.generated.ts`) và bảng theme preset (`theme.generated.ts`), cả hai được tạo ra bằng cách gọi trực tiếp các hàm dựng seed và theme preset của chính nền tảng thay vì sao chép kết quả của chúng, để lần platform thay đổi tiếp theo ở một trong hai chỗ đó tự động được phản ánh vào lần codegen kế tiếp của server này.
36
+
9
37
  ## [0.14.1] - 2026-09-09
10
38
 
11
39
  ### Added
package/README.md CHANGED
@@ -89,7 +89,7 @@ make, because those mean "this person's account".
89
89
  | `sb_templates` | The store's saved section templates — designed sections to start from |
90
90
  | `sb_template_use` | Instantiate a template into a page |
91
91
  | `sb_page_list` | Every page on the site |
92
- | `sb_page_create` | Create a page; `type` is the route for checkout, product, category, post, course |
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 |
93
93
  | `sb_publish` | Compile the draft into the live page (cascades to shared globals) |
94
94
  | `sb_review` | Every defect a visitor would see, each with its fix, plus the five gaps between this store and a paid order |
95
95
  | `sb_media_list` | The site's media library |
@@ -102,8 +102,8 @@ make, because those mean "this person's account".
102
102
  | `sb_store` | Run a store flow that must happen in a fixed order — the four writes that make a working checkout, or any of the platform's 17 form templates (login, register, forgot, contact, subscribe …) with its own field document |
103
103
  | `sb_undo` | Put back what a PUT replaced — the platform has no page history or restore, so this is the only way back |
104
104
 
105
- Twenty-eight tools, **484 API operations** (158 of the 212 writes carrying a body shape read
106
- off the handler), 107 elements, 77 binding sources. `sb_api_find`
105
+ Twenty-eight tools, **495 API operations** (166 of the 216 writes carrying a body shape read
106
+ off the handler), 111 elements, 78 binding sources. `sb_api_find`
107
107
  is an index rather than a tool per endpoint, so the tool list stays short while everything
108
108
  the platform can do stays reachable — and operations added to the platform arrive with the
109
109
  next `npm run codegen`.
package/README.vi.md CHANGED
@@ -86,7 +86,7 @@ là "tài khoản của người này".
86
86
  | `sb_templates` | Section template đã lưu của cửa hàng — section thiết kế sẵn để bắt đầu |
87
87
  | `sb_template_use` | Thả một template vào trang |
88
88
  | `sb_page_list` | Mọi trang của site |
89
- | `sb_page_create` | Tạo một trang; `type` là đường đi cho checkout, product, category, post, course |
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 |
90
90
  | `sb_publish` | Biên dịch bản nháp thành trang live (lan sang global dùng chung) |
91
91
  | `sb_review` | Mọi khiếm khuyết người xem sẽ thấy, kèm lệnh sửa từng cái, và năm khoảng trống chắn giữa cửa hàng với một đơn đã thanh toán |
92
92
  | `sb_media_list` | Thư viện ảnh của site |
@@ -99,8 +99,8 @@ là "tài khoản của người này".
99
99
  | `sb_store` | Chạy một luồng cửa hàng bắt buộc đúng thứ tự — bốn lệnh ghi tạo nên trang thanh toán, hoặc gieo bất kỳ template nào trong 17 form của nền tảng (login, register, forgot, contact, subscribe …) kèm field document của nó |
100
100
  | `sb_undo` | Trả lại thứ mà một lệnh PUT đã ghi đè — nền tảng không có lịch sử trang hay restore, nên đây là đường về duy nhất |
101
101
 
102
- Hai mươi tám tool, **484 operation API** (158 trong 212 lệnh ghi có hình dạng body đọc thẳng
103
- từ handler), 107 element, 77 nguồn binding. `sb_api_find` là
102
+ Hai mươi tám tool, **495 operation API** (166 trong 216 lệnh ghi có hình dạng body đọc thẳng
103
+ từ handler), 111 element, 78 nguồn binding. `sb_api_find` là
104
104
  một chỉ mục chứ không phải mỗi endpoint một tool, nên danh sách tool vẫn ngắn trong khi mọi
105
105
  thứ nền tảng làm được vẫn với tới — và operation mới thêm bên nền tảng sẽ tự có sau lần
106
106
  `npm run codegen` kế tiếp.
@@ -1,7 +1,7 @@
1
1
  export const SWAGGER_SOURCE = {
2
- "operations": 486,
3
- "definitions": 102,
4
- "bodyCarrying": 172,
2
+ "operations": 495,
3
+ "definitions": 104,
4
+ "bodyCarrying": 177,
5
5
  "bodyUndescribed": 63,
6
6
  "generatedFrom": "server/docs/swagger.json"
7
7
  };
@@ -1513,6 +1513,19 @@ export const API_OPERATIONS = [
1513
1513
  "bodyRef": null,
1514
1514
  "credential": "none"
1515
1515
  },
1516
+ {
1517
+ "id": "get:/api/chat-providers",
1518
+ "method": "GET",
1519
+ "path": "/api/chat-providers",
1520
+ "tags": [
1521
+ "chatbot"
1522
+ ],
1523
+ "summary": "List the AI providers this server can talk to",
1524
+ "params": [],
1525
+ "bodyDescribed": false,
1526
+ "bodyRef": null,
1527
+ "credential": "siteScoped"
1528
+ },
1516
1529
  {
1517
1530
  "id": "post:/api/course-uploads/{siteId}",
1518
1531
  "method": "POST",
@@ -3939,6 +3952,69 @@ export const API_OPERATIONS = [
3939
3952
  "bodyRef": null,
3940
3953
  "credential": "siteScoped"
3941
3954
  },
3955
+ {
3956
+ "id": "get:/api/sites/{siteId}/chat-settings",
3957
+ "method": "GET",
3958
+ "path": "/api/sites/{siteId}/chat-settings",
3959
+ "tags": [
3960
+ "chatbot"
3961
+ ],
3962
+ "summary": "Get, save or clear a site's AI chat configuration",
3963
+ "params": [
3964
+ {
3965
+ "name": "siteId",
3966
+ "in": "path",
3967
+ "required": true,
3968
+ "type": "string",
3969
+ "description": "Site ID"
3970
+ }
3971
+ ],
3972
+ "bodyDescribed": false,
3973
+ "bodyRef": null,
3974
+ "credential": "siteScoped"
3975
+ },
3976
+ {
3977
+ "id": "put:/api/sites/{siteId}/chat-settings",
3978
+ "method": "PUT",
3979
+ "path": "/api/sites/{siteId}/chat-settings",
3980
+ "tags": [
3981
+ "chatbot"
3982
+ ],
3983
+ "summary": "Get, save or clear a site's AI chat configuration",
3984
+ "params": [
3985
+ {
3986
+ "name": "siteId",
3987
+ "in": "path",
3988
+ "required": true,
3989
+ "type": "string",
3990
+ "description": "Site ID"
3991
+ }
3992
+ ],
3993
+ "bodyDescribed": false,
3994
+ "bodyRef": null,
3995
+ "credential": "siteScoped"
3996
+ },
3997
+ {
3998
+ "id": "delete:/api/sites/{siteId}/chat-settings",
3999
+ "method": "DELETE",
4000
+ "path": "/api/sites/{siteId}/chat-settings",
4001
+ "tags": [
4002
+ "chatbot"
4003
+ ],
4004
+ "summary": "Get, save or clear a site's AI chat configuration",
4005
+ "params": [
4006
+ {
4007
+ "name": "siteId",
4008
+ "in": "path",
4009
+ "required": true,
4010
+ "type": "string",
4011
+ "description": "Site ID"
4012
+ }
4013
+ ],
4014
+ "bodyDescribed": false,
4015
+ "bodyRef": null,
4016
+ "credential": "siteScoped"
4017
+ },
3942
4018
  {
3943
4019
  "id": "post:/api/sites/{siteId}/clipboard/reconcile",
3944
4020
  "method": "POST",
@@ -10705,6 +10781,34 @@ export const API_OPERATIONS = [
10705
10781
  "bodyRef": null,
10706
10782
  "credential": "siteScoped"
10707
10783
  },
10784
+ {
10785
+ "id": "post:/api/sites/{siteId}/relation-slots",
10786
+ "method": "POST",
10787
+ "path": "/api/sites/{siteId}/relation-slots",
10788
+ "tags": [
10789
+ "relations"
10790
+ ],
10791
+ "summary": "Create a curated relation slot",
10792
+ "params": [
10793
+ {
10794
+ "name": "siteId",
10795
+ "in": "path",
10796
+ "required": true,
10797
+ "type": "string",
10798
+ "description": "Site ID"
10799
+ },
10800
+ {
10801
+ "name": "slot",
10802
+ "in": "body",
10803
+ "required": true,
10804
+ "type": "object",
10805
+ "description": "Slot"
10806
+ }
10807
+ ],
10808
+ "bodyDescribed": true,
10809
+ "bodyRef": "internal_relations_rest.slotBody",
10810
+ "credential": "siteScoped"
10811
+ },
10708
10812
  {
10709
10813
  "id": "get:/api/sites/{siteId}/relation-slots/usage",
10710
10814
  "method": "GET",
@@ -10733,6 +10837,160 @@ export const API_OPERATIONS = [
10733
10837
  "bodyRef": null,
10734
10838
  "credential": "siteScoped"
10735
10839
  },
10840
+ {
10841
+ "id": "put:/api/sites/{siteId}/relation-slots/{id}",
10842
+ "method": "PUT",
10843
+ "path": "/api/sites/{siteId}/relation-slots/{id}",
10844
+ "tags": [
10845
+ "relations"
10846
+ ],
10847
+ "summary": "Update or delete one curated relation slot",
10848
+ "params": [
10849
+ {
10850
+ "name": "siteId",
10851
+ "in": "path",
10852
+ "required": true,
10853
+ "type": "string",
10854
+ "description": "Site ID"
10855
+ },
10856
+ {
10857
+ "name": "id",
10858
+ "in": "path",
10859
+ "required": true,
10860
+ "type": "string",
10861
+ "description": "Slot ID"
10862
+ },
10863
+ {
10864
+ "name": "slot",
10865
+ "in": "body",
10866
+ "required": true,
10867
+ "type": "object",
10868
+ "description": "Slot"
10869
+ }
10870
+ ],
10871
+ "bodyDescribed": true,
10872
+ "bodyRef": "internal_relations_rest.slotBody",
10873
+ "credential": "siteScoped"
10874
+ },
10875
+ {
10876
+ "id": "delete:/api/sites/{siteId}/relation-slots/{id}",
10877
+ "method": "DELETE",
10878
+ "path": "/api/sites/{siteId}/relation-slots/{id}",
10879
+ "tags": [
10880
+ "relations"
10881
+ ],
10882
+ "summary": "Update or delete one curated relation slot",
10883
+ "params": [
10884
+ {
10885
+ "name": "siteId",
10886
+ "in": "path",
10887
+ "required": true,
10888
+ "type": "string",
10889
+ "description": "Site ID"
10890
+ },
10891
+ {
10892
+ "name": "id",
10893
+ "in": "path",
10894
+ "required": true,
10895
+ "type": "string",
10896
+ "description": "Slot ID"
10897
+ },
10898
+ {
10899
+ "name": "slot",
10900
+ "in": "body",
10901
+ "required": true,
10902
+ "type": "object",
10903
+ "description": "Slot"
10904
+ }
10905
+ ],
10906
+ "bodyDescribed": true,
10907
+ "bodyRef": "internal_relations_rest.slotBody",
10908
+ "credential": "siteScoped"
10909
+ },
10910
+ {
10911
+ "id": "get:/api/sites/{siteId}/relation-slots/{id}/picks",
10912
+ "method": "GET",
10913
+ "path": "/api/sites/{siteId}/relation-slots/{id}/picks",
10914
+ "tags": [
10915
+ "relations"
10916
+ ],
10917
+ "summary": "Read or replace one owner's picks in a curated slot",
10918
+ "params": [
10919
+ {
10920
+ "name": "siteId",
10921
+ "in": "path",
10922
+ "required": true,
10923
+ "type": "string",
10924
+ "description": "Site ID"
10925
+ },
10926
+ {
10927
+ "name": "id",
10928
+ "in": "path",
10929
+ "required": true,
10930
+ "type": "string",
10931
+ "description": "Slot ID"
10932
+ },
10933
+ {
10934
+ "name": "ownerId",
10935
+ "in": "query",
10936
+ "required": true,
10937
+ "type": "string",
10938
+ "description": "Product or article id whose picks these are"
10939
+ },
10940
+ {
10941
+ "name": "picks",
10942
+ "in": "body",
10943
+ "required": true,
10944
+ "type": "object",
10945
+ "description": "The WHOLE list, every time"
10946
+ }
10947
+ ],
10948
+ "bodyDescribed": true,
10949
+ "bodyRef": "internal_relations_rest.picksBody",
10950
+ "credential": "siteScoped"
10951
+ },
10952
+ {
10953
+ "id": "put:/api/sites/{siteId}/relation-slots/{id}/picks",
10954
+ "method": "PUT",
10955
+ "path": "/api/sites/{siteId}/relation-slots/{id}/picks",
10956
+ "tags": [
10957
+ "relations"
10958
+ ],
10959
+ "summary": "Read or replace one owner's picks in a curated slot",
10960
+ "params": [
10961
+ {
10962
+ "name": "siteId",
10963
+ "in": "path",
10964
+ "required": true,
10965
+ "type": "string",
10966
+ "description": "Site ID"
10967
+ },
10968
+ {
10969
+ "name": "id",
10970
+ "in": "path",
10971
+ "required": true,
10972
+ "type": "string",
10973
+ "description": "Slot ID"
10974
+ },
10975
+ {
10976
+ "name": "ownerId",
10977
+ "in": "query",
10978
+ "required": true,
10979
+ "type": "string",
10980
+ "description": "Product or article id whose picks these are"
10981
+ },
10982
+ {
10983
+ "name": "picks",
10984
+ "in": "body",
10985
+ "required": true,
10986
+ "type": "object",
10987
+ "description": "The WHOLE list, every time"
10988
+ }
10989
+ ],
10990
+ "bodyDescribed": true,
10991
+ "bodyRef": "internal_relations_rest.picksBody",
10992
+ "credential": "siteScoped"
10993
+ },
10736
10994
  {
10737
10995
  "id": "post:/api/sites/{siteId}/restore",
10738
10996
  "method": "POST",
@@ -17483,5 +17741,36 @@ export const API_DEFINITIONS = {
17483
17741
  "$ref": "#/definitions/internal_publicapi.Webhook"
17484
17742
  }
17485
17743
  }
17744
+ },
17745
+ "internal_relations_rest.picksBody": {
17746
+ "type": "object",
17747
+ "properties": {
17748
+ "itemIds": {
17749
+ "type": "array",
17750
+ "items": {
17751
+ "type": "string"
17752
+ }
17753
+ }
17754
+ }
17755
+ },
17756
+ "internal_relations_rest.slotBody": {
17757
+ "type": "object",
17758
+ "properties": {
17759
+ "itemType": {
17760
+ "type": "string"
17761
+ },
17762
+ "key": {
17763
+ "type": "string"
17764
+ },
17765
+ "name": {
17766
+ "type": "string"
17767
+ },
17768
+ "ownerType": {
17769
+ "type": "string"
17770
+ },
17771
+ "position": {
17772
+ "type": "integer"
17773
+ }
17774
+ }
17486
17775
  }
17487
17776
  };
@@ -1,4 +1,6 @@
1
1
  import { ELEMENTS, TRAIT_WRITES } from './elements.generated.js';
2
+ import { vocabulariesForWrites } from '../domains/site/vocabulary.js';
3
+ import { neverTranslatedOn, translatableSpecials } from '../domains/site/translate.js';
2
4
  /** Eight to choose from; the hints for the chosen one come with sb_traits_for. */
3
5
  export const DEFAULT_CATALOG_LIMIT = 8;
4
6
  /**
@@ -94,6 +96,35 @@ export function traitsFor(type, control) {
94
96
  // which, unlike `style`, are NOT open — this is the machine-readable
95
97
  // answer to "what does this element store", and often the only one.
96
98
  defaults: el.defaults,
99
+ // WHICH OF THIS ELEMENT'S STRINGS A TRANSLATION MAY REWRITE. Translating one
100
+ // of the others does not degrade the page, it BREAKS the render — `name` is
101
+ // a lucide icon id, `src` a URL, `filterSource` a registry id the renderer
102
+ // switches on. An empty list is the complete answer for an element whose
103
+ // only string is one of those, so it is reported rather than omitted.
104
+ ...(() => {
105
+ const may = translatableSpecials(el.type);
106
+ const own = Object.keys(el.defaults?.specials ?? {});
107
+ const never = neverTranslatedOn(el.type, own);
108
+ if (!may.length && !never.length)
109
+ return {};
110
+ return {
111
+ translatable: {
112
+ specials: may,
113
+ ...(never.length ? { never_translate: never } : {}),
114
+ },
115
+ };
116
+ })(),
117
+ // WHAT THOSE KEYS ARE ALLOWED TO HOLD, for the few where guessing wrong is
118
+ // silent. Attached to the ELEMENT rather than to a control, because the keys
119
+ // that most need it are exactly the UNDECLARED ones: `collectionType` is in
120
+ // this element's defaults and in its control list, and no TRAIT_WRITES entry
121
+ // names it — so a per-control attachment reaches none of them, which is how
122
+ // the first version of this was wrong.
123
+ ...(() => {
124
+ const keys = new Set([...Object.keys(el.defaults?.config ?? {}), ...el.controls]);
125
+ const vocab = vocabulariesForWrites([...keys]);
126
+ return Object.keys(vocab).length ? { config_values: vocab } : {};
127
+ })(),
97
128
  isContainer: el.isContainer,
98
129
  isRootOnly: el.isRootOnly,
99
130
  childAllows: el.childAllows,