@wix/himalaya-cli 0.1376.0 → 0.1378.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/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-contract.d.ts +5 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +1 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +1 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +1 -1
- package/dist/cli.mjs +39 -10
- package/guides/himi-json-output.md +2 -2
- package/guides/iap-products.md +6 -3
- package/guides/index.json +6 -0
- package/guides/sdk-reference.json +2 -2
- package/guides/wix-api-usage.md +5 -5
- package/guides/wix-pro-gallery.md +145 -0
- package/package.json +2 -2
|
@@ -56,9 +56,11 @@ export declare const STORES_APPLE_SALES_ENABLED = true;
|
|
|
56
56
|
/** StartItemPayment's CatalogReference: the purchases catalog sells a Stores product under its product
|
|
57
57
|
* id (eCom's `catalogItemId`). One-time unlocks only, whatever the backend also accepts. */
|
|
58
58
|
export declare const STORES_PRODUCT_SALE_RULE: AppleSaleRule;
|
|
59
|
-
/**
|
|
60
|
-
*
|
|
61
|
-
|
|
59
|
+
/** One-time unlimited plans sell through the App Store. Proven live in Sandbox on 2026-10-19: a member's
|
|
60
|
+
* online plan order is charged with one cashier item whose id is the plan id, so StartPayment maps it to the
|
|
61
|
+
* plan's Apple product, and the order reads ACTIVE and PAID. Recurring and time-limited plans stay outside
|
|
62
|
+
* this rule; their Billing lifecycle is unfinished. */
|
|
63
|
+
export declare const PRICING_PLAN_APPLE_SALES_ENABLED = true;
|
|
62
64
|
/**
|
|
63
65
|
* The one definition `himi store iap connect` writes mappings by and the mapping source reads them by,
|
|
64
66
|
* keyed by catalog kind. wix-iap StartPayment maps the cashier order's single item id, and Online
|
|
@@ -689,7 +689,7 @@ var STORES_ITEM_PAYMENT_ENABLED = false;
|
|
|
689
689
|
var STORES_APPLE_SALES_ENABLED = true;
|
|
690
690
|
var STORES_PRODUCT_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
691
691
|
var ONLINE_PROGRAM_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
692
|
-
var PRICING_PLAN_APPLE_SALES_ENABLED =
|
|
692
|
+
var PRICING_PLAN_APPLE_SALES_ENABLED = true;
|
|
693
693
|
var PRICING_PLAN_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
694
694
|
function appleSaleRules(options = {}) {
|
|
695
695
|
return {
|
|
@@ -667,7 +667,7 @@ var STORES_ITEM_PAYMENT_ENABLED = false;
|
|
|
667
667
|
var STORES_APPLE_SALES_ENABLED = true;
|
|
668
668
|
var STORES_PRODUCT_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
669
669
|
var ONLINE_PROGRAM_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
670
|
-
var PRICING_PLAN_APPLE_SALES_ENABLED =
|
|
670
|
+
var PRICING_PLAN_APPLE_SALES_ENABLED = true;
|
|
671
671
|
var PRICING_PLAN_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
672
672
|
function appleSaleRules(options = {}) {
|
|
673
673
|
return {
|
|
@@ -942,7 +942,7 @@ var STORES_ITEM_PAYMENT_ENABLED = false;
|
|
|
942
942
|
var STORES_APPLE_SALES_ENABLED = true;
|
|
943
943
|
var STORES_PRODUCT_SALE_RULE = { appProductId: (id2) => id2, productType: "NON_CONSUMABLE" };
|
|
944
944
|
var ONLINE_PROGRAM_SALE_RULE = { appProductId: (id2) => id2, productType: "NON_CONSUMABLE" };
|
|
945
|
-
var PRICING_PLAN_APPLE_SALES_ENABLED =
|
|
945
|
+
var PRICING_PLAN_APPLE_SALES_ENABLED = true;
|
|
946
946
|
var PRICING_PLAN_SALE_RULE = { appProductId: (id2) => id2, productType: "NON_CONSUMABLE" };
|
|
947
947
|
function appleSaleRules(options = {}) {
|
|
948
948
|
return {
|
package/dist/cli.mjs
CHANGED
|
@@ -159772,7 +159772,7 @@ var init_wix_payments_contract = __esm({
|
|
|
159772
159772
|
STORES_APPLE_SALES_ENABLED = true;
|
|
159773
159773
|
STORES_PRODUCT_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
159774
159774
|
ONLINE_PROGRAM_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
159775
|
-
PRICING_PLAN_APPLE_SALES_ENABLED =
|
|
159775
|
+
PRICING_PLAN_APPLE_SALES_ENABLED = true;
|
|
159776
159776
|
PRICING_PLAN_SALE_RULE = { appProductId: (id) => id, productType: "NON_CONSUMABLE" };
|
|
159777
159777
|
APPLE_SALE_RULES = appleSaleRules();
|
|
159778
159778
|
}
|
|
@@ -160506,6 +160506,13 @@ async function readPlans(wix, notes) {
|
|
|
160506
160506
|
throw e2;
|
|
160507
160507
|
}
|
|
160508
160508
|
}
|
|
160509
|
+
async function readOneTimeUnlimitedPlanIds(wix) {
|
|
160510
|
+
const rows = await allByOffset(async (offset) => {
|
|
160511
|
+
const res = await wix("GET", `/pricing-plans/v2/plans?limit=${PAGE2}&offset=${offset}`);
|
|
160512
|
+
return { rows: res.plans ?? [], total: res.pagingMetadata?.total };
|
|
160513
|
+
}, "/pricing-plans/v2/plans");
|
|
160514
|
+
return new Set(rows.filter((p2) => planKind(p2.pricing) === "digital" && p2.archived !== true && typeof p2.id === "string").map((p2) => p2.id));
|
|
160515
|
+
}
|
|
160509
160516
|
async function readWixCatalog(wix) {
|
|
160510
160517
|
const notes = [];
|
|
160511
160518
|
const items = [...await readStores(wix, notes), ...await readPrograms(wix, notes), ...await readPlans(wix, notes)];
|
|
@@ -160595,6 +160602,16 @@ var init_store_iap_purchases = __esm({
|
|
|
160595
160602
|
});
|
|
160596
160603
|
|
|
160597
160604
|
// src/store-iap-connect.ts
|
|
160605
|
+
async function planCheck(items, deps) {
|
|
160606
|
+
const rules = deps.saleRules ?? APPLE_SALE_RULES;
|
|
160607
|
+
if (!rules.pricingPlan || !items.some((item) => item.wix?.entityType === "pricingPlan")) return { ok: true, ids: /* @__PURE__ */ new Set() };
|
|
160608
|
+
if (!deps.oneTimePlans) return { ok: false, why: "the site's plans were not read" };
|
|
160609
|
+
try {
|
|
160610
|
+
return { ok: true, ids: await deps.oneTimePlans() };
|
|
160611
|
+
} catch (error) {
|
|
160612
|
+
return { ok: false, why: `the site's plans could not be read (${error instanceof Error ? error.message : String(error)})` };
|
|
160613
|
+
}
|
|
160614
|
+
}
|
|
160598
160615
|
async function connectIap(manifest, deps, options) {
|
|
160599
160616
|
const { target } = manifest;
|
|
160600
160617
|
const report3 = (app2, appId2, rest) => {
|
|
@@ -160609,19 +160626,20 @@ async function connectIap(manifest, deps, options) {
|
|
|
160609
160626
|
...rest
|
|
160610
160627
|
};
|
|
160611
160628
|
};
|
|
160629
|
+
const plans = await planCheck(manifest.items, deps);
|
|
160612
160630
|
const found = await findOrRegister(deps.api, manifest, options.apply);
|
|
160613
160631
|
if (!found.app) {
|
|
160614
160632
|
const skipped = found.step.state === "planned" ? planned : noApp;
|
|
160615
160633
|
return report3(found.step, null, {
|
|
160616
160634
|
credentials: found.step.state === "planned" ? credentialsPlan(deps.appleKey) : skipped("the app is not registered"),
|
|
160617
160635
|
provider: found.step.state === "planned" ? providerPlan(deps.provider) : skipped("the app is not registered"),
|
|
160618
|
-
mappings: manifest.items.map((item) => mappingWithoutApp(item, found.step.state === "planned", deps.saleRules))
|
|
160636
|
+
mappings: manifest.items.map((item) => mappingWithoutApp(item, found.step.state === "planned", plans, deps.saleRules))
|
|
160619
160637
|
});
|
|
160620
160638
|
}
|
|
160621
160639
|
const { app } = found;
|
|
160622
160640
|
const credentials = await storeKey(deps, app, options.apply);
|
|
160623
160641
|
const provider = await connectProvider(deps.provider, app.id, options.apply);
|
|
160624
|
-
const mappings = await mapItems(deps.api, app.id, manifest.items, options.apply, deps.saleRules);
|
|
160642
|
+
const mappings = await mapItems(deps.api, app.id, manifest.items, options.apply, plans, deps.saleRules);
|
|
160625
160643
|
return report3(found.step, app.id, { credentials, provider, mappings });
|
|
160626
160644
|
}
|
|
160627
160645
|
async function findOrRegister(api2, manifest, apply) {
|
|
@@ -160687,19 +160705,19 @@ function providerPlan(provider) {
|
|
|
160687
160705
|
detail: "the appleIAP provider is connected through Serve's IAP routes (wix-iap ConnectProviderAccount), and this Serve target has none."
|
|
160688
160706
|
};
|
|
160689
160707
|
}
|
|
160690
|
-
async function mapItems(api2, appId2, items, apply, rules = APPLE_SALE_RULES) {
|
|
160708
|
+
async function mapItems(api2, appId2, items, apply, plans, rules = APPLE_SALE_RULES) {
|
|
160691
160709
|
let catalog3;
|
|
160692
160710
|
try {
|
|
160693
160711
|
catalog3 = await api2.listProducts(appId2);
|
|
160694
160712
|
} catch (error) {
|
|
160695
160713
|
return items.map((item) => {
|
|
160696
|
-
const target = mappingTarget(item, rules);
|
|
160714
|
+
const target = mappingTarget(item, plans, rules);
|
|
160697
160715
|
return "skip" in target ? target.skip : { ...target, ...failed("read the app's catalog", error) };
|
|
160698
160716
|
});
|
|
160699
160717
|
}
|
|
160700
160718
|
const steps = [];
|
|
160701
160719
|
for (const item of items) {
|
|
160702
|
-
const target = mappingTarget(item, rules);
|
|
160720
|
+
const target = mappingTarget(item, plans, rules);
|
|
160703
160721
|
if ("skip" in target) {
|
|
160704
160722
|
steps.push(target.skip);
|
|
160705
160723
|
continue;
|
|
@@ -160719,7 +160737,7 @@ async function mapItems(api2, appId2, items, apply, rules = APPLE_SALE_RULES) {
|
|
|
160719
160737
|
}
|
|
160720
160738
|
return steps;
|
|
160721
160739
|
}
|
|
160722
|
-
function mappingTarget(item, rules = APPLE_SALE_RULES) {
|
|
160740
|
+
function mappingTarget(item, plans, rules = APPLE_SALE_RULES) {
|
|
160723
160741
|
const skip = (entity2, detail) => ({ skip: { entity: entity2, appProductId: null, storeProductId: item.productId, state: "skipped", code: "mapping_unsupported", detail } });
|
|
160724
160742
|
if (!item.wix || item.unlocks?.length) {
|
|
160725
160743
|
return skip(item.productId, "a consumable tier unlocks several Wix items, but a purchase pays one order item, so it cannot be mapped");
|
|
@@ -160730,6 +160748,12 @@ function mappingTarget(item, rules = APPLE_SALE_RULES) {
|
|
|
160730
160748
|
if (PRODUCT_TYPE[item.type] !== rule.productType) {
|
|
160731
160749
|
return skip(entity, `the App Store purchase flow sells ${item.wix.entityType} items only as ${rule.productType}, not ${PRODUCT_TYPE[item.type]}`);
|
|
160732
160750
|
}
|
|
160751
|
+
if (item.wix.entityType === "pricingPlan") {
|
|
160752
|
+
if (!plans.ok) return skip(entity, `only one-time unlimited plans sell through the App Store, and ${plans.why}, so this plan was not mapped`);
|
|
160753
|
+
if (!plans.ids.has(item.wix.entityId)) {
|
|
160754
|
+
return skip(entity, "only one-time unlimited plans (one payment, valid until cancelled) sell through the App Store; this plan is recurring, time-limited, archived or not on the site");
|
|
160755
|
+
}
|
|
160756
|
+
}
|
|
160733
160757
|
return { entity, appProductId: rule.appProductId(item.wix.entityId), storeProductId: item.productId, type: rule.productType };
|
|
160734
160758
|
}
|
|
160735
160759
|
function decide(catalog3, target) {
|
|
@@ -160752,8 +160776,8 @@ function decide(catalog3, target) {
|
|
|
160752
160776
|
}
|
|
160753
160777
|
return void 0;
|
|
160754
160778
|
}
|
|
160755
|
-
function mappingWithoutApp(item, planning, rules = APPLE_SALE_RULES) {
|
|
160756
|
-
const target = mappingTarget(item, rules);
|
|
160779
|
+
function mappingWithoutApp(item, planning, plans, rules = APPLE_SALE_RULES) {
|
|
160780
|
+
const target = mappingTarget(item, plans, rules);
|
|
160757
160781
|
if ("skip" in target) return target.skip;
|
|
160758
160782
|
return planning ? { ...target, state: "planned", detail: `map ${target.appProductId} \u2192 ${target.storeProductId}` } : { ...target, ...noApp("the app is not registered") };
|
|
160759
160783
|
}
|
|
@@ -161230,7 +161254,12 @@ async function connectWith(flags, deps, out2) {
|
|
|
161230
161254
|
const transport = deps.wixTransport(manifest.target.wixSiteId);
|
|
161231
161255
|
const route = deps.wixIapRoute();
|
|
161232
161256
|
const serve = route ? deps.serveTransport?.(manifest.target.wixSiteId, previewTag) ?? transport : transport;
|
|
161233
|
-
const connectDeps = {
|
|
161257
|
+
const connectDeps = {
|
|
161258
|
+
api: createPurchasesOwnerApi(transport),
|
|
161259
|
+
appleKey,
|
|
161260
|
+
provider: route ? wixIapProviderConnector(serve, route) : null,
|
|
161261
|
+
oneTimePlans: () => readOneTimeUnlimitedPlanIds(transport)
|
|
161262
|
+
};
|
|
161234
161263
|
let plan;
|
|
161235
161264
|
try {
|
|
161236
161265
|
plan = await connectIap(manifest, connectDeps, { apply: false });
|
|
@@ -117,9 +117,9 @@ exit statuses remain intact. Manifest parsing failures still use their legacy en
|
|
|
117
117
|
| `provider_not_connected` | wix-iap answered, but reported the provider account as not connected. |
|
|
118
118
|
| `mapping_conflict` | The Wix item is already mapped to a different Apple product; left as it is. |
|
|
119
119
|
| `mapping_inactive` | The mapping exists but is inactive; left as it is. |
|
|
120
|
-
| `mapping_type_mismatch` | The mapping exists as a product type the purchase flow cannot sell (programs sell only as `NON_CONSUMABLE`); left as it is. |
|
|
120
|
+
| `mapping_type_mismatch` | The mapping exists as a product type the purchase flow cannot sell (programs, Stores products and one-time plans sell only as `NON_CONSUMABLE`); left as it is. |
|
|
121
121
|
| `sku_in_use` | The Apple product is already mapped to another Wix item; left as it is. |
|
|
122
|
-
| `mapping_unsupported` | The purchase flow cannot map this item yet (a vertical other than Online Programs, a price tier, or
|
|
122
|
+
| `mapping_unsupported` | The purchase flow cannot map this item yet (a vertical other than Online Programs, Wix Stores or one-time Pricing Plans, a price tier, or an item declared as a consumable). |
|
|
123
123
|
| `no_purchases_app` | A step did not run because the site has no purchases app for the bundle. |
|
|
124
124
|
| `request_failed` | A purchases-service call failed for another reason; the original message is preserved. |
|
|
125
125
|
| `setup_incomplete` | Steps are only planned (a dry run) or otherwise not yet in place. |
|
package/guides/iap-products.md
CHANGED
|
@@ -280,9 +280,12 @@ called as usual. In order:
|
|
|
280
280
|
(`ConnectProviderAccount`). Reported as `provider_route_unavailable` only when the Serve target
|
|
281
281
|
has no IAP routes; a refusal from the service behind it is `request_failed`. There is no read
|
|
282
282
|
of the provider's state, so a rerun connects it again (the call is safe to repeat).
|
|
283
|
-
4. **One mapping per Wix item → Apple product.** Online Programs map by program id
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
4. **One mapping per Wix item → Apple product.** Online Programs map by program id, Wix Stores
|
|
284
|
+
digital products by product id, and one-time unlimited Pricing Plans by plan id (the ids the
|
|
285
|
+
purchase flow looks up), all only as non-consumables. A plan is mapped only when the site reads it
|
|
286
|
+
as one-time unlimited (one payment, valid until cancelled); a recurring, time-limited or archived
|
|
287
|
+
plan, or one connect cannot read, is skipped. Other verticals, price tiers and consumable items
|
|
288
|
+
are skipped with `mapping_unsupported`. An item already mapped to another
|
|
286
289
|
product (`mapping_conflict`), a mapping of another product type (`mapping_type_mismatch`), an
|
|
287
290
|
inactive mapping (`mapping_inactive`), or an Apple product mapped to another item
|
|
288
291
|
(`sku_in_use`) is reported and left exactly as it is.
|
package/guides/index.json
CHANGED
|
@@ -240,6 +240,12 @@
|
|
|
240
240
|
"category": "Wix SDK",
|
|
241
241
|
"file": "wix-sdk-overview.md"
|
|
242
242
|
},
|
|
243
|
+
{
|
|
244
|
+
"id": "wix-pro-gallery",
|
|
245
|
+
"title": "Build an app over Wix Pro Gallery",
|
|
246
|
+
"category": "Wix SDK",
|
|
247
|
+
"file": "wix-pro-gallery.md"
|
|
248
|
+
},
|
|
243
249
|
{
|
|
244
250
|
"id": "wix-login-integration",
|
|
245
251
|
"title": "Add a Wix login gate",
|
|
@@ -1979,8 +1979,8 @@
|
|
|
1979
1979
|
{
|
|
1980
1980
|
"name": "PRICING_PLAN_APPLE_SALES_ENABLED",
|
|
1981
1981
|
"kind": "const",
|
|
1982
|
-
"jsdoc": "/**
|
|
1983
|
-
"decl": "export const PRICING_PLAN_APPLE_SALES_ENABLED =
|
|
1982
|
+
"jsdoc": "/** One-time unlimited plans sell through the App Store. Proven live in Sandbox on 2026-10-19: a member's\n * online plan order is charged with one cashier item whose id is the plan id, so StartPayment maps it to the\n * plan's Apple product, and the order reads ACTIVE and PAID. Recurring and time-limited plans stay outside\n * this rule; their Billing lifecycle is unfinished. */",
|
|
1983
|
+
"decl": "export const PRICING_PLAN_APPLE_SALES_ENABLED = true;"
|
|
1984
1984
|
},
|
|
1985
1985
|
{
|
|
1986
1986
|
"name": "appleSaleRules",
|
package/guides/wix-api-usage.md
CHANGED
|
@@ -210,11 +210,11 @@ else the query 403s. In-repo, `core/dev-server/tier5-bundles-src/_shared/himi-cm
|
|
|
210
210
|
|
|
211
211
|
## Declared capability
|
|
212
212
|
|
|
213
|
-
**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
requires `"wix_platform"`.)
|
|
213
|
+
**Declare `wix_api`** for any worker that touches a vertical or raw `wixApi`. The web host's runtime denies a
|
|
214
|
+
raw `wixApi.call` from a worker that did not declare it: the call fails with `capability not declared: wix_api`.
|
|
215
|
+
Declare it in both places (the app-level `capabilities` in `config.ts` and the screen's `declaredCapabilities`) —
|
|
216
|
+
see [Native capabilities](./himi-capabilities.md#2-declare--in-two-places). Declaring it is also what the other
|
|
217
|
+
hosts will require. (Contrast: the cross-cutting facade `@himalaya/wix-platform` requires `"wix_platform"`.)
|
|
218
218
|
|
|
219
219
|
## Errors & best-effort
|
|
220
220
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Build an app over Wix Pro Gallery
|
|
2
|
+
|
|
3
|
+
How to build a Himalaya app that shows a Wix site's **Pro Gallery** data: the galleries a site owner made in the Wix Editor and the images, videos and text items inside them.
|
|
4
|
+
|
|
5
|
+
Read [the Wix API usage guide](./wix-api-usage.md) first for the `wixApi.call` bridge. This guide adds only what is specific to Pro Gallery.
|
|
6
|
+
|
|
7
|
+
Each fact below names its source:
|
|
8
|
+
|
|
9
|
+
- **[docs]** = a page on dev.wix.com (verified 2026-10-19).
|
|
10
|
+
- **[call]** = a live call on a test site (2026-10-14; the list-limit and `itemLimit` checks were run again on 2026-10-19).
|
|
11
|
+
- **[example]** = a Himalaya example app that renders all 13 Pro Gallery layouts and an expand-mode viewer.
|
|
12
|
+
- **[device]** = a test on an Android emulator (2026-10-15 to 2026-10-18).
|
|
13
|
+
|
|
14
|
+
## 1. What the API gives you
|
|
15
|
+
|
|
16
|
+
[docs] The Pro Gallery API has these methods and no others:
|
|
17
|
+
|
|
18
|
+
- Galleries: Create, Get, Update, Delete, List.
|
|
19
|
+
- Gallery items: Create, Get, List, Update, Delete, Bulk Delete.
|
|
20
|
+
- Webhooks: Gallery Created / Updated / Deleted and Gallery Item Created / Updated / Deleted.
|
|
21
|
+
|
|
22
|
+
[docs] A gallery item is an image, a video or a text item. It has a title, a description and a link.
|
|
23
|
+
|
|
24
|
+
[call] The gallery response holds items only. It has **no layout or style settings**. Your app picks a layout and stores its own settings (see section 6).
|
|
25
|
+
|
|
26
|
+
## 2. Read the galleries the site owner made
|
|
27
|
+
|
|
28
|
+
[docs] Wix does **not** recommend that you create galleries with the API. A gallery you create with the API exists only on the site's backend. It does not appear on the live site unless the owner connects it to a gallery component with code. Galleries that the owner adds in the Editor stay in sync both ways. Use the API to read and manage those galleries.
|
|
29
|
+
|
|
30
|
+
[docs] The Gallery Created and Gallery Updated webhooks do not return the gallery's items. Listen to the Gallery Item Created and Gallery Item Updated webhooks for items.
|
|
31
|
+
|
|
32
|
+
## 3. Read from a worker
|
|
33
|
+
|
|
34
|
+
[call] A **visitor** token can read two endpoints:
|
|
35
|
+
|
|
36
|
+
| Call | Use |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `GET /pro-gallery/v2/galleries?limit=10&offset=&itemLimit=` | List galleries. The response has `totalGalleries` and, for each gallery, `name`, `totalItems` and the first `itemLimit` items. |
|
|
39
|
+
| `GET /pro-gallery/v2/galleries/{id}?itemOffset=&itemLimit=` | One gallery. `gallery.totalItems` is the total number of items in the gallery (not a page count). Keep requesting with a growing `itemOffset` while it is below `totalItems`. |
|
|
40
|
+
|
|
41
|
+
[call] Writes need an owner token. A visitor app does not write.
|
|
42
|
+
|
|
43
|
+
[call] Limits you will hit:
|
|
44
|
+
|
|
45
|
+
- `limit` is at most **10**. `limit=11` returns `limit got 11, expected 10 or less`.
|
|
46
|
+
- `itemLimit` must be **1 or more**. `itemLimit=0` returns `itemLimit got 0, expected 1 or more`. To get only the gallery names and counts, ask for `itemLimit=1`.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const api = globalThis.himalaya?.wixApi;
|
|
50
|
+
// First page of galleries, 4 preview items each
|
|
51
|
+
const list = await api.call("GET", "/pro-gallery/v2/galleries?limit=10&offset=0&itemLimit=4");
|
|
52
|
+
// One gallery, items 0..99. Keep paging with itemOffset while it is below gallery.totalItems.
|
|
53
|
+
const one = await api.call("GET", `/pro-gallery/v2/galleries/${encodeURIComponent(id)}?itemOffset=0&itemLimit=100`);
|
|
54
|
+
const { totalItems, items } = one.gallery;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
[call] The same list call also returns 200 on `/progallery/v2/galleries` (checked 2026-10-19). The example app and this guide use `/pro-gallery/v2/...`.
|
|
58
|
+
|
|
59
|
+
[example] The example app loads a gallery 100 items at a time and lists galleries 10 at a time.
|
|
60
|
+
|
|
61
|
+
## 4. Item types
|
|
62
|
+
|
|
63
|
+
All fields below come from [call] responses unless marked.
|
|
64
|
+
|
|
65
|
+
| Item `type` | Where the media is | Notes |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `IMAGE` | `image.imageInfo`: `id`, `url`, `width`, `height`, `altText` | `url` is a `static.wixstatic.com` media URL. |
|
|
68
|
+
| `VIDEO`, `video.type` = `WIX_MEDIA` | `video.videoInfo`: `url`, `resolutions[]` (mp4 `url`, `width`, `height`, `quality`), `posters[]` | Wix-hosted. The type is `WIX_MEDIA`, not `WIX`. |
|
|
69
|
+
| `VIDEO`, `video.type` = `YOUTUBE` or `VIMEO` | `video.videoInfo.url` only | `resolutions` and `posters` are empty. There is no thumbnail in the response. |
|
|
70
|
+
| `TEXT` | `text.html`, `text.css.backgroundColor` | The text is **HTML**. |
|
|
71
|
+
|
|
72
|
+
Every item can also carry `title`, `description` and a `link` with `type`, `text`, `url` and `target`.
|
|
73
|
+
|
|
74
|
+
Thumbnails for external video:
|
|
75
|
+
|
|
76
|
+
- YouTube: build it from the video id, for example `https://img.youtube.com/vi/<id>/hqdefault.jpg` [example].
|
|
77
|
+
- Vimeo: the response has no thumbnail, and this guide has no source for another way to get one. Show a placeholder.
|
|
78
|
+
|
|
79
|
+
To create an image item you must pass the media id. Without it the call fails with `imageInfo … id must not be empty` [call].
|
|
80
|
+
|
|
81
|
+
## 5. Resize images
|
|
82
|
+
|
|
83
|
+
[docs] Add a transform to a Wix media URL:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
{url}/v1/fit/w_{w},h_{h}/file.{ext}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- `fit` keeps the whole image. `fill` fills the box and crops. `crop` cuts a region (`x_`, `y_`, `w_`, `h_`).
|
|
90
|
+
- `w` and `h` are 1 to 5000.
|
|
91
|
+
- It works only for public media on `static.wixstatic.com`.
|
|
92
|
+
- It never makes an image larger than the original.
|
|
93
|
+
|
|
94
|
+
A `q_` quality parameter (for example `q_85`) also works in practice [example], but the transform page does **not** document it. Leave it out, or treat it as undocumented.
|
|
95
|
+
|
|
96
|
+
Use a small size for tiles (the example app uses 800 px) and a large size for the full-screen viewer (2000 px).
|
|
97
|
+
|
|
98
|
+
## 6. What the app must decide for itself
|
|
99
|
+
|
|
100
|
+
- **Layout and display settings.** The API returns none, so choose a layout in the app and store the choice on the device [call].
|
|
101
|
+
- **Loves / likes.** No page in the Pro Gallery API index, the gallery object, Get Gallery Item or the Velo backend introduction mentions loves or likes [docs]. An app can keep loves only on the device, so a count is that device's count.
|
|
102
|
+
|
|
103
|
+
## 7. Layout recipes
|
|
104
|
+
|
|
105
|
+
[example] Himalaya has no masonry or collage component. The example app builds all 13 Pro Gallery layouts (Grid, Masonry, Collage, Thumbnails, Slider, Slideshow, Panorama, Columns, Magic, Bricks, Mix, Alternate, Blog) like this:
|
|
106
|
+
|
|
107
|
+
- **Masonry:** one single-column `Grid` per column. Split the items into the columns in the worker and bind each `Grid` to its own list.
|
|
108
|
+
- **Collage-type layouts:** one row template for each shape of item group, chosen with `FragmentSlot`.
|
|
109
|
+
- **Item ratio:** the example puts each item in one of six buckets by its width-to-height ratio, and picks row templates from the buckets.
|
|
110
|
+
|
|
111
|
+
[example] A screen descriptor over **256 KiB** is rejected on push (`413 item_too_large`). `himi validate` and `himi test` do not check this size. A descriptor that holds all 13 layouts is too big, so the example app has **three gallery screens, one for each layout family**. A layout change to another family replaces the screen with the same gallery and settings.
|
|
112
|
+
|
|
113
|
+
## 8. Viewer, slideshow, download and video on a device
|
|
114
|
+
|
|
115
|
+
These come from Android emulator tests [device].
|
|
116
|
+
|
|
117
|
+
- **Carousel.** A data-bound `Carousel` (`itemsPath`) is a plain scrolling row on native. It has no paging, no `onChange` and no autoplay. The example viewer and slideshow therefore show **one bound slide**, a swipe is `swipeDismiss`, and the example's slideshow autoplay is a `setTimeout` in the worker, guarded by `typeof setTimeout === "function"` [example]. Not every worker runtime has timers: the [Tier-5 guide](./tier-5-add-a-screen.md) says a worker has no clock and describes `refresh: { intervalMs, action }` and `_poll` for timed work. Autoplay was tested only with the `setTimeout` form, on Android; it was not tested with `refresh` or `_poll`.
|
|
118
|
+
- **Declare the capabilities.** The download and video recipes below call `media_playback` and `photos`, and the worker calls the API with `wix_api`, which the web host denies when undeclared. The example app lists `media_playback`, `photos` and `wix_api` in the app-level `capabilities` in `config.ts`, and in the `declaredCapabilities` of every screen that runs its worker [example]. A call a screen did not declare is denied. See [Native capabilities](./himi-capabilities.md#2-declare--in-two-places).
|
|
119
|
+
- **Download.** On Android, `photos.saveToLibrary` needs a file in the app's own store, not a URL. First call `media_playback.download` for the original image. Then pass the store-relative path to `photos.saveToLibrary`. The handle's `uri` is an absolute `file:` URI, and `saveToLibrary` rejects it.
|
|
120
|
+
- **Wix video.** A Wix MP4 plays in the `Video` component.
|
|
121
|
+
- **YouTube.** A YouTube embed in a `WebView` needs a `Referer` header. Without it the player shows "Error 153".
|
|
122
|
+
- **Vimeo.** A Vimeo embed needs protected-media (DRM) support in the app. This is fixed in the app shell. An app binary built before the fix shows "Rights issue" on Android, so **rebuild the binary** (`himi dist`) with a current CLI and shell (see section 9).
|
|
123
|
+
- **`WebView` URL from `$service:`.** On Android, a `WebView` whose `url` is a `$service:` binding never loads. Feed it from a one-row list and bind `$item.url`.
|
|
124
|
+
|
|
125
|
+
## 9. Known limitations
|
|
126
|
+
|
|
127
|
+
| Limitation | Status | Source |
|
|
128
|
+
| --- | --- | --- |
|
|
129
|
+
| No loves / likes in the public API. | Platform gap, no fix announced in the sources. Keep loves on the device. | [docs] |
|
|
130
|
+
| Galleries made with the API do not show on the live site. | Wix design, stated in the Pro Gallery introduction. Create galleries in the Editor. | [docs] |
|
|
131
|
+
| Vimeo shows "Rights issue" on Android. | Fixed in the app shell. Still present in binaries built before the fix: rebuild the binary. | [device] |
|
|
132
|
+
| A data-bound `Carousel` does not page. | Open. One-slide viewer is the workaround; autoplay timing needs a worker timer (see section 8). | [device] |
|
|
133
|
+
| A `WebView` bound to a `$service:` URL never loads on Android. | Open. Use the one-row list workaround. | [device] |
|
|
134
|
+
| `photos.saveToLibrary` rejects a URL or an absolute `file:` URI on Android. | Open. Use the `media_playback.download` path in section 8. | [device] |
|
|
135
|
+
| No thumbnail for Vimeo items. | Open. The response has none. | [call] |
|
|
136
|
+
| The `q_` image parameter is undocumented. | Works in practice. Not on the Wix transform page. | [docs], [example] |
|
|
137
|
+
|
|
138
|
+
## 10. Test data
|
|
139
|
+
|
|
140
|
+
[call] To get test galleries on a site you own:
|
|
141
|
+
|
|
142
|
+
1. Import media with `POST /site-media/v1/files/import`.
|
|
143
|
+
2. Create galleries and items with an **owner** token (`wix token --site <msid>`).
|
|
144
|
+
|
|
145
|
+
Remember section 2: a gallery created this way does not show on the live site, but a visitor read through the API returns it, so your app can show it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1378.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
|
|
6
6
|
"keywords": [
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"artifactId": "himalaya-cli"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
|
-
"falconPackageHash": "
|
|
74
|
+
"falconPackageHash": "797df247428aeadb6579acbf4bffa3856e28b369ea6eed3bc20ae80e"
|
|
75
75
|
}
|