@xapps-platform/marketplace-ui 0.1.8 → 0.1.10

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/index.js CHANGED
@@ -978,6 +978,14 @@ function getDefaultWidgetId(x) {
978
978
  const pick = def || widgets[0];
979
979
  return readFirstString(asRecord(pick).id) || null;
980
980
  }
981
+ function getDefaultWidgetName(x, locale) {
982
+ const rawWidgets = asRecord(x.manifest).widgets;
983
+ const widgets = Array.isArray(rawWidgets) ? rawWidgets : null;
984
+ if (!widgets || widgets.length === 0) return null;
985
+ const def = widgets.find((w) => asRecord(w).default === true);
986
+ const pick = asRecord(def || widgets[0]);
987
+ return resolveMarketplaceText(pick.title, locale) || readFirstString(pick.name, pick.id) || null;
988
+ }
981
989
  function buildCatalogFilterStorageKey(input) {
982
990
  const mode = input.isEmbedded ? "embed" : "portal";
983
991
  const scope = input.singleXappMode && input.xappId ? `xapp:${input.xappId}` : "catalog";
@@ -1011,6 +1019,11 @@ function CatalogPage() {
1011
1019
  const canMutate = host.canMutate ? host.canMutate() : true;
1012
1020
  const addAppLabel = env?.copy?.addAppLabel || "Add app";
1013
1021
  const removeAppLabel = env?.copy?.removeAppLabel || "Remove app";
1022
+ const openAppLabel = env?.copy?.openAppLabel || t("xapp.open_app", void 0, "Open app");
1023
+ const installationPolicyResolved = env?.installationPolicyResolved !== false;
1024
+ const mutationControlsReady = installationPolicyResolved || !host.subjectId || !canMutate;
1025
+ const autoUpdateMode = mutationControlsReady && (env?.installationPolicy ?? host.installationPolicy)?.update_mode === "auto_update_compatible" && Boolean(host.subjectId);
1026
+ const autoAvailableMode = mutationControlsReady && (env?.installationPolicy ?? host.installationPolicy)?.mode === "auto_available" && Boolean(host.subjectId);
1014
1027
  const loc = useLocation();
1015
1028
  const token = useQueryToken();
1016
1029
  const tokenSearch = buildTokenSearch(token, loc.search);
@@ -1075,6 +1088,14 @@ function CatalogPage() {
1075
1088
  setInstalledOnly(Boolean(stored?.addedOnly));
1076
1089
  setSelectedTag(typeof stored?.selectedTag === "string" ? stored.selectedTag : defaultTag);
1077
1090
  }, [defaultTag, storageKey]);
1091
+ useEffect3(() => {
1092
+ if (!autoAvailableMode || !installedOnly) return;
1093
+ setInstalledOnly(false);
1094
+ writeStoredCatalogFilters(storageKey, {
1095
+ addedOnly: false,
1096
+ selectedTag: selectedTag || ""
1097
+ });
1098
+ }, [autoAvailableMode, installedOnly, selectedTag, storageKey]);
1078
1099
  useEffect3(() => {
1079
1100
  const restrictedTags = env?.tags ?? [];
1080
1101
  if (selectedTag && restrictedTags.length > 0 && !restrictedTags.includes(selectedTag)) {
@@ -1192,7 +1213,7 @@ function CatalogPage() {
1192
1213
  }
1193
1214
  )
1194
1215
  ] }),
1195
- /* @__PURE__ */ jsxs3("label", { className: "mx-checkbox-label", children: [
1216
+ mutationControlsReady && !autoAvailableMode ? /* @__PURE__ */ jsxs3("label", { className: "mx-checkbox-label", children: [
1196
1217
  /* @__PURE__ */ jsx6(
1197
1218
  "input",
1198
1219
  {
@@ -1210,7 +1231,7 @@ function CatalogPage() {
1210
1231
  }
1211
1232
  ),
1212
1233
  /* @__PURE__ */ jsx6("span", { className: "mx-label mx-label-inline", children: t("common.added_only", void 0, "Added only") })
1213
- ] })
1234
+ ] }) : null
1214
1235
  ] }) }),
1215
1236
  busy && items.length === 0 ? /* @__PURE__ */ jsx6("div", { className: "mx-grid", "aria-busy": "true", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxs3("div", { className: "mx-skeleton-card", children: [
1216
1237
  /* @__PURE__ */ jsx6("div", { className: "mx-skeleton-card-image" }),
@@ -1240,7 +1261,7 @@ function CatalogPage() {
1240
1261
  /* @__PURE__ */ jsx6("div", { className: "mx-grid", children: filtered.length === 0 && !busy ? /* @__PURE__ */ jsxs3("div", { className: "mx-empty-catalog", children: [
1241
1262
  /* @__PURE__ */ jsx6("div", { className: "mx-empty-catalog-icon", children: "\u2315" }),
1242
1263
  /* @__PURE__ */ jsx6("div", { className: "mx-empty-catalog-title", children: t("catalog.no_apps_found", void 0, "No apps found") }),
1243
- /* @__PURE__ */ jsx6("div", { className: "mx-empty-catalog-desc", children: installedOnly && items.length > 0 ? t(
1264
+ /* @__PURE__ */ jsx6("div", { className: "mx-empty-catalog-desc", children: !autoAvailableMode && installedOnly && items.length > 0 ? t(
1244
1265
  "catalog.empty_added_only",
1245
1266
  void 0,
1246
1267
  "No apps are installed for the current subject yet. Turn off Added only to browse the full catalog."
@@ -1260,7 +1281,9 @@ function CatalogPage() {
1260
1281
  const image = typeof manifest.image === "string" ? manifest.image : "https://picsum.photos/seed/" + x.slug + "/560/320";
1261
1282
  const inst = installationsByXappId[String(x.id)];
1262
1283
  const installed = Boolean(inst);
1284
+ const updateAvailable = Boolean(inst?.updateAvailable);
1263
1285
  const defaultWidgetId = getDefaultWidgetId(x);
1286
+ const defaultWidgetName = getDefaultWidgetName(x, locale) || t("common.widget", void 0, "Widget");
1264
1287
  const xTerms = asRecord(manifest.terms);
1265
1288
  const requiresTerms = Boolean(
1266
1289
  Object.keys(xTerms).length > 0 || typeof xTerms.text === "string" && xTerms.text.trim() || typeof xTerms.url === "string" && xTerms.url.trim()
@@ -1293,7 +1316,7 @@ function CatalogPage() {
1293
1316
  )
1294
1317
  ] }) : /* @__PURE__ */ jsx6("div", { className: "mx-card-publisher", children: t("common.catalog_app", void 0, "Catalog app") }),
1295
1318
  /* @__PURE__ */ jsxs3("div", { className: "mx-card-badges", children: [
1296
- installed && /* @__PURE__ */ jsxs3("span", { className: "mx-card-installed-badge", children: [
1319
+ !autoAvailableMode && installed && /* @__PURE__ */ jsxs3("span", { className: "mx-card-installed-badge", children: [
1297
1320
  /* @__PURE__ */ jsx6("span", { className: "mx-card-installed-badge-dot" }),
1298
1321
  t("common.added", void 0, "Added")
1299
1322
  ] }),
@@ -1317,7 +1340,7 @@ function CatalogPage() {
1317
1340
  children: t("catalog.view_details", void 0, "View details")
1318
1341
  }
1319
1342
  ),
1320
- canMutate && (!installed ? /* @__PURE__ */ jsx6(
1343
+ canMutate && mutationControlsReady && (!installed ? /* @__PURE__ */ jsx6(
1321
1344
  "button",
1322
1345
  {
1323
1346
  className: "mx-btn mx-btn-primary mx-btn-sm",
@@ -1331,12 +1354,50 @@ function CatalogPage() {
1331
1354
  host.requestInstall({
1332
1355
  xappId: String(x.id),
1333
1356
  defaultWidgetId,
1357
+ xappTitle: title,
1358
+ widgetName: defaultWidgetName,
1359
+ openAfterInstall: autoAvailableMode && Boolean(defaultWidgetId),
1334
1360
  subjectId: host.subjectId ?? null
1335
1361
  });
1336
1362
  },
1337
- children: addAppLabel
1363
+ children: autoAvailableMode ? openAppLabel : addAppLabel
1364
+ }
1365
+ ) : autoAvailableMode ? /* @__PURE__ */ jsx6(
1366
+ "button",
1367
+ {
1368
+ className: "mx-btn mx-btn-primary mx-btn-sm",
1369
+ onClick: (e) => {
1370
+ e.preventDefault();
1371
+ e.stopPropagation();
1372
+ if (!defaultWidgetId || !inst?.installationId) {
1373
+ navigate(detailTo);
1374
+ return;
1375
+ }
1376
+ if (updateAvailable && host.requestUpdate && autoUpdateMode) {
1377
+ host.requestUpdate({
1378
+ installationId: inst.installationId,
1379
+ xappId: String(x.id),
1380
+ widgetId: defaultWidgetId,
1381
+ xappTitle: title,
1382
+ widgetName: defaultWidgetName
1383
+ });
1384
+ return;
1385
+ }
1386
+ if (updateAvailable) {
1387
+ navigate(detailTo);
1388
+ return;
1389
+ }
1390
+ host.openWidget({
1391
+ installationId: inst.installationId,
1392
+ widgetId: defaultWidgetId,
1393
+ xappId: String(x.id),
1394
+ xappTitle: title,
1395
+ widgetName: t("common.widget", void 0, "Widget")
1396
+ });
1397
+ },
1398
+ children: openAppLabel
1338
1399
  }
1339
- ) : /* @__PURE__ */ jsx6(
1400
+ ) : !autoAvailableMode ? /* @__PURE__ */ jsx6(
1340
1401
  "button",
1341
1402
  {
1342
1403
  className: "mx-btn mx-btn-outline mx-btn-sm mx-btn-danger-text",
@@ -1354,7 +1415,7 @@ function CatalogPage() {
1354
1415
  },
1355
1416
  children: removeAppLabel
1356
1417
  }
1357
- ))
1418
+ ) : null)
1358
1419
  ] })
1359
1420
  ] }, x.id);
1360
1421
  }) })
@@ -4242,6 +4303,365 @@ function RequestsPage() {
4242
4303
  import { useEffect as useEffect9, useMemo as useMemo8, useRef, useState as useState7 } from "react";
4243
4304
  import { Link as Link9, useLocation as useLocation7, useNavigate as useNavigate3, useParams as useParams2 } from "react-router-dom";
4244
4305
 
4306
+ // ../browser-host/src/xms.ts
4307
+ function readString4(value) {
4308
+ return String(value ?? "").trim();
4309
+ }
4310
+ function readNumber(value, fallback = 0) {
4311
+ const parsed = Number(value);
4312
+ return Number.isFinite(parsed) ? parsed : fallback;
4313
+ }
4314
+ function readLower(value) {
4315
+ return readString4(value).toLowerCase();
4316
+ }
4317
+ function readBoolean(value, fallback = false) {
4318
+ return typeof value === "boolean" ? value : fallback;
4319
+ }
4320
+ function readLocalizedText(value, fallback = "") {
4321
+ if (typeof value === "string") {
4322
+ return readString4(value) || fallback;
4323
+ }
4324
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4325
+ return fallback;
4326
+ }
4327
+ const record = value;
4328
+ return readString4(record.en) || readString4(record.ro) || Object.values(record).map((item) => readString4(item)).find(Boolean) || fallback;
4329
+ }
4330
+ function formatStateLabel(value, fallback = "\u2014") {
4331
+ const raw = readString4(value);
4332
+ if (!raw) return fallback;
4333
+ return raw.replace(/_/g, " ");
4334
+ }
4335
+ function listXappMonetizationPaywalls(items) {
4336
+ return (Array.isArray(items) ? items : []).filter((item) => item && typeof item === "object" && !Array.isArray(item)).map((item) => item);
4337
+ }
4338
+ function buildPaywallPlacementCandidates(value) {
4339
+ const raw = readLower(value);
4340
+ if (!raw) return [];
4341
+ const out = /* @__PURE__ */ new Set([raw]);
4342
+ if (raw === "paywall" || raw === "default_paywall") {
4343
+ out.add("paywall");
4344
+ out.add("default_paywall");
4345
+ }
4346
+ if (raw === "feature_paywall" || raw === "feature_gate") {
4347
+ out.add("feature_paywall");
4348
+ out.add("feature_gate");
4349
+ }
4350
+ if (raw === "upgrade" || raw === "upgrade_paywall") {
4351
+ out.add("upgrade");
4352
+ out.add("upgrade_paywall");
4353
+ }
4354
+ if (raw === "retention" || raw === "retention_paywall") {
4355
+ out.add("retention");
4356
+ out.add("retention_paywall");
4357
+ }
4358
+ if (raw === "checkout" || raw === "checkout_entry") {
4359
+ out.add("checkout");
4360
+ out.add("checkout_entry");
4361
+ }
4362
+ return Array.from(out);
4363
+ }
4364
+ function selectXappMonetizationPaywall(input) {
4365
+ const paywalls = listXappMonetizationPaywalls(input.paywalls);
4366
+ const slug = readLower(input.slug);
4367
+ const placement = readLower(input.placement);
4368
+ if (slug) {
4369
+ const matched = paywalls.find((item) => readLower(item.slug) === slug);
4370
+ if (matched) return matched;
4371
+ }
4372
+ if (placement) {
4373
+ const candidates = buildPaywallPlacementCandidates(placement);
4374
+ const matched = paywalls.find((item) => candidates.includes(readLower(item.placement)));
4375
+ if (matched) return matched;
4376
+ }
4377
+ return paywalls[0] || null;
4378
+ }
4379
+ function flattenXappMonetizationPaywallPackages(paywall) {
4380
+ const record = paywall && typeof paywall === "object" && !Array.isArray(paywall) ? paywall : {};
4381
+ const out = [];
4382
+ for (const pkg of Array.isArray(record.packages) ? record.packages : []) {
4383
+ const packageRecord = pkg && typeof pkg === "object" && !Array.isArray(pkg) ? pkg : {};
4384
+ const price = Array.isArray(packageRecord.prices) ? packageRecord.prices[0] || null : null;
4385
+ out.push({
4386
+ offeringId: readString4(packageRecord.offering_id),
4387
+ offeringSlug: readString4(packageRecord.offering_slug),
4388
+ offeringTitle: readString4(packageRecord.offering_slug) || "Offering",
4389
+ offeringPlacement: readString4(packageRecord.offering_placement) || null,
4390
+ packageId: readString4(packageRecord.id),
4391
+ packageSlug: readString4(packageRecord.slug),
4392
+ packageTitle: readString4(packageRecord.slug) || "Package",
4393
+ packageKind: readString4(packageRecord.package_kind) || "standard",
4394
+ productId: readString4(packageRecord.product?.id),
4395
+ productSlug: readString4(packageRecord.product?.slug),
4396
+ productFamily: readString4(
4397
+ packageRecord.product?.product_family
4398
+ ),
4399
+ priceId: readString4(price?.id),
4400
+ amount: readString4(price?.amount),
4401
+ currency: readString4(price?.currency),
4402
+ billingPeriod: readString4(price?.billing_period) || null,
4403
+ purchasePolicy: readProjectedPurchasePolicy(packageRecord.purchase_policy),
4404
+ metadata: packageRecord.metadata && typeof packageRecord.metadata === "object" ? packageRecord.metadata : {}
4405
+ });
4406
+ }
4407
+ return out.filter((item) => item.offeringId && item.packageId && item.priceId);
4408
+ }
4409
+ function formatPlacementLabel(value, fallback = "General placement") {
4410
+ const raw = readString4(value);
4411
+ if (!raw) return fallback;
4412
+ return raw.replace(/[_-]+/g, " ");
4413
+ }
4414
+ function readPackageCredits(item) {
4415
+ const metadata = item.metadata && typeof item.metadata === "object" && !Array.isArray(item.metadata) ? item.metadata : {};
4416
+ const direct = readNumber(item.credits ?? metadata.credits, -1);
4417
+ if (direct > 0) return direct;
4418
+ const included = readNumber(metadata.included_credits, -1);
4419
+ if (included > 0) return included;
4420
+ return 0;
4421
+ }
4422
+ function buildMonetizationOfferingPresentation(input) {
4423
+ const record = input && typeof input === "object" && !Array.isArray(input) ? input : {};
4424
+ const offeringLabel = readString4(record.offeringTitle) || formatStateLabel(record.offeringSlug, "Offering");
4425
+ const placementRaw = readLower(record.offeringPlacement);
4426
+ const placementLabel = formatPlacementLabel(record.offeringPlacement);
4427
+ let summary = "General offering surface for this xapp.";
4428
+ if (placementRaw.includes("feature") && placementRaw.includes("paywall")) {
4429
+ summary = "Feature-paywall placement for gated in-app flows.";
4430
+ } else if (placementRaw.includes("paywall")) {
4431
+ summary = "Default paywall placement for monetized upgrade prompts.";
4432
+ } else if (placementRaw.includes("checkout")) {
4433
+ summary = "Direct checkout placement for purchase-driven flows.";
4434
+ } else if (placementRaw.includes("upgrade")) {
4435
+ summary = "Upgrade-oriented placement for membership and package switching.";
4436
+ }
4437
+ return {
4438
+ offeringLabel,
4439
+ placementLabel,
4440
+ summary
4441
+ };
4442
+ }
4443
+ function buildMonetizationPaywallPresentation(input) {
4444
+ const record = input && typeof input === "object" && !Array.isArray(input) ? input : {};
4445
+ const paywallLabel = readLocalizedText(record.title, formatStateLabel(record.slug, "Paywall")) || "Paywall";
4446
+ const placementLabel = formatPlacementLabel(record.placement);
4447
+ const packages = flattenXappMonetizationPaywallPackages(record);
4448
+ const defaultPackageRef = readString4(record.default_package_ref);
4449
+ const defaultPackage = packages.find((item) => readString4(item.packageSlug) === defaultPackageRef) || packages[0] || null;
4450
+ const summary = readLocalizedText(record.description) || (placementLabel === "General placement" ? "Presentation surface for monetized package selection." : `${placementLabel} surface for monetized package selection.`);
4451
+ return {
4452
+ paywallLabel,
4453
+ placementLabel,
4454
+ summary,
4455
+ defaultPackageLabel: defaultPackage ? readString4(defaultPackage.packageTitle) : "",
4456
+ packageCountLabel: `${packages.length} package${packages.length === 1 ? "" : "s"}`
4457
+ };
4458
+ }
4459
+ function buildMonetizationPaywallRenderModel(input) {
4460
+ const record = input && typeof input === "object" && !Array.isArray(input) ? input : {};
4461
+ const presentation = buildMonetizationPaywallPresentation(record);
4462
+ const defaultPackageRef = readString4(record.default_package_ref);
4463
+ const packages = flattenXappMonetizationPaywallPackages(record).map((item) => {
4464
+ const packagePresentation = buildMonetizationPackagePresentation(item);
4465
+ return {
4466
+ packageId: readString4(item.packageId),
4467
+ packageSlug: readString4(item.packageSlug),
4468
+ packageTitle: readString4(item.packageTitle) || "Package",
4469
+ productId: readString4(item.productId),
4470
+ productSlug: readString4(item.productSlug),
4471
+ productFamily: readString4(item.productFamily),
4472
+ metadata: item.metadata && typeof item.metadata === "object" && !Array.isArray(item.metadata) ? item.metadata : {},
4473
+ description: readString4(item.description) || packagePresentation.summary,
4474
+ fitLabel: packagePresentation.fitLabel,
4475
+ moneyLabel: packagePresentation.moneyLabel,
4476
+ offeringLabel: packagePresentation.offeringLabel,
4477
+ placementLabel: packagePresentation.placementLabel,
4478
+ signals: packagePresentation.signals,
4479
+ isDefault: Boolean(defaultPackageRef) && readString4(item.packageSlug) === defaultPackageRef
4480
+ };
4481
+ });
4482
+ const badges = [presentation.placementLabel, presentation.packageCountLabel];
4483
+ if (presentation.defaultPackageLabel) {
4484
+ badges.push(`default ${presentation.defaultPackageLabel}`);
4485
+ }
4486
+ return {
4487
+ ...presentation,
4488
+ badges,
4489
+ packages
4490
+ };
4491
+ }
4492
+ function buildMonetizationPackagePresentation(input) {
4493
+ const item = input && typeof input === "object" && !Array.isArray(input) ? input : {};
4494
+ const packageKind = readString4(item.packageKind);
4495
+ const packageSlug = readLower(item.packageSlug);
4496
+ const metadata = item.metadata && typeof item.metadata === "object" && !Array.isArray(item.metadata) ? item.metadata : {};
4497
+ const credits = readPackageCredits(item);
4498
+ const moneyLabel = normalizeMoneyLabel(item);
4499
+ const offeringPresentation = buildMonetizationOfferingPresentation(item);
4500
+ let fitLabel = "General upgrade";
4501
+ let summary = "Useful as a general monetization upgrade for this creator scope.";
4502
+ if (packageKind === "one_time_unlock") {
4503
+ fitLabel = "Durable unlock";
4504
+ summary = "Best when the feature mainly needs current access without ongoing membership.";
4505
+ } else if (packageKind === "subscription") {
4506
+ fitLabel = "Recurring membership";
4507
+ summary = "Best when the feature depends on ongoing membership coverage.";
4508
+ } else if (packageKind === "credit_pack") {
4509
+ fitLabel = "Credit top-up";
4510
+ summary = "Best when the feature spends credits for each advanced action.";
4511
+ } else if (packageSlug.includes("hybrid")) {
4512
+ fitLabel = "Hybrid upgrade";
4513
+ summary = "Blends access coverage with bundled credits for mixed workflows.";
4514
+ }
4515
+ const signals = [];
4516
+ if (readString4(metadata.badge)) {
4517
+ signals.push(readString4(metadata.badge));
4518
+ }
4519
+ if (credits > 0) {
4520
+ signals.push(`${credits} credits`);
4521
+ }
4522
+ if (readString4(item.billingPeriod)) {
4523
+ signals.push(`billed ${readString4(item.billingPeriod)}`);
4524
+ }
4525
+ if (offeringPresentation.offeringLabel) {
4526
+ signals.push(offeringPresentation.offeringLabel);
4527
+ }
4528
+ if (offeringPresentation.placementLabel && offeringPresentation.placementLabel !== "General placement") {
4529
+ signals.push(offeringPresentation.placementLabel);
4530
+ }
4531
+ return {
4532
+ fitLabel,
4533
+ summary,
4534
+ signals,
4535
+ moneyLabel,
4536
+ offeringLabel: offeringPresentation.offeringLabel,
4537
+ placementLabel: offeringPresentation.placementLabel,
4538
+ offeringSummary: offeringPresentation.summary
4539
+ };
4540
+ }
4541
+ function collectPackageOwnershipCandidates(item) {
4542
+ const metadata = item.metadata && typeof item.metadata === "object" && !Array.isArray(item.metadata) ? item.metadata : {};
4543
+ return [
4544
+ readLower(item.productSlug),
4545
+ readLower(item.packageSlug),
4546
+ readLower(metadata.tier),
4547
+ readLower(metadata.access_tier)
4548
+ ].filter(Boolean);
4549
+ }
4550
+ function hasActiveRecurringSubscription(currentSubscription) {
4551
+ const record = currentSubscription && typeof currentSubscription === "object" && !Array.isArray(currentSubscription) ? currentSubscription : {};
4552
+ const status = readLower(record.status);
4553
+ if (!status || status === "expired") return false;
4554
+ const expiredAt = readString4(record.expired_at);
4555
+ if (!expiredAt) return true;
4556
+ const expiresAtMs = Date.parse(expiredAt);
4557
+ return Number.isNaN(expiresAtMs) || expiresAtMs > Date.now();
4558
+ }
4559
+ function hasCurrentOwnedEntitlement(entitlement) {
4560
+ const status = readLower(entitlement.status);
4561
+ if (status !== "active" && status !== "grace_period") return false;
4562
+ const expiresAt = readString4(entitlement.expires_at);
4563
+ if (!expiresAt) return true;
4564
+ const expiresAtMs = Date.parse(expiresAt);
4565
+ return Number.isNaN(expiresAtMs) || expiresAtMs > Date.now();
4566
+ }
4567
+ function readProjectedPurchasePolicy(value) {
4568
+ const record = value && typeof value === "object" && !Array.isArray(value) ? value : null;
4569
+ if (!record) return null;
4570
+ const status = readLower(record.status);
4571
+ const transitionKind = readLower(record.transition_kind ?? record.transitionKind);
4572
+ if (!status || !transitionKind) return null;
4573
+ return {
4574
+ canPurchase: readBoolean(record.can_purchase ?? record.canPurchase, true),
4575
+ status: status === "current_recurring_plan" || status === "owned_additive_unlock" ? status : "available",
4576
+ transitionKind: transitionKind === "start_recurring" || transitionKind === "replace_recurring" || transitionKind === "buy_additive_unlock" || transitionKind === "buy_credit_pack" || transitionKind === "activate_hybrid" ? transitionKind : "none",
4577
+ reason: readString4(record.reason) || null
4578
+ };
4579
+ }
4580
+ function resolveMonetizationPackagePurchasePolicy(input) {
4581
+ const item = input.item && typeof input.item === "object" ? input.item : {};
4582
+ const projectedPolicy = readProjectedPurchasePolicy(item.purchasePolicy ?? item.purchase_policy);
4583
+ if (projectedPolicy) return projectedPolicy;
4584
+ const currentSubscription = input.currentSubscription && typeof input.currentSubscription === "object" && !Array.isArray(input.currentSubscription) ? input.currentSubscription : {};
4585
+ const additiveEntitlements = Array.isArray(input.additiveEntitlements) ? input.additiveEntitlements : [];
4586
+ const productFamily = readLower(item.productFamily);
4587
+ const packageKind = readLower(item.packageKind);
4588
+ const productId = readLower(item.productId);
4589
+ const currentSubscriptionProductId = readLower(currentSubscription.product_id);
4590
+ const ownershipCandidates = new Set(collectPackageOwnershipCandidates(item));
4591
+ const ownedAdditiveUnlock = additiveEntitlements.some((entry) => {
4592
+ const entitlement = entry && typeof entry === "object" && !Array.isArray(entry) ? entry : {};
4593
+ if (!hasCurrentOwnedEntitlement(entitlement)) return false;
4594
+ const entitlementProductId = readLower(entitlement.product_id);
4595
+ if (productId && entitlementProductId && productId === entitlementProductId) return true;
4596
+ const candidateValues = [
4597
+ readLower(entitlement.product_slug),
4598
+ readLower(entitlement.tier)
4599
+ ].filter(Boolean);
4600
+ return candidateValues.some((value) => ownershipCandidates.has(value));
4601
+ });
4602
+ if (hasActiveRecurringSubscription(currentSubscription) && productId && currentSubscriptionProductId && productId === currentSubscriptionProductId) {
4603
+ return {
4604
+ canPurchase: false,
4605
+ status: "current_recurring_plan",
4606
+ transitionKind: "none",
4607
+ reason: "current_recurring_plan"
4608
+ };
4609
+ }
4610
+ if ((productFamily === "one_time_unlock" || packageKind === "one_time_unlock") && ownedAdditiveUnlock) {
4611
+ return {
4612
+ canPurchase: false,
4613
+ status: "owned_additive_unlock",
4614
+ transitionKind: "none",
4615
+ reason: "owned_additive_unlock"
4616
+ };
4617
+ }
4618
+ if (productFamily === "credit_pack" || packageKind === "credit_pack") {
4619
+ return {
4620
+ canPurchase: true,
4621
+ status: "available",
4622
+ transitionKind: "buy_credit_pack",
4623
+ reason: null
4624
+ };
4625
+ }
4626
+ if (productFamily === "one_time_unlock" || packageKind === "one_time_unlock") {
4627
+ return {
4628
+ canPurchase: true,
4629
+ status: "available",
4630
+ transitionKind: "buy_additive_unlock",
4631
+ reason: null
4632
+ };
4633
+ }
4634
+ if (productFamily === "subscription_plan" || packageKind === "subscription") {
4635
+ return {
4636
+ canPurchase: true,
4637
+ status: "available",
4638
+ transitionKind: hasActiveRecurringSubscription(currentSubscription) ? "replace_recurring" : "start_recurring",
4639
+ reason: null
4640
+ };
4641
+ }
4642
+ if (productFamily === "hybrid_plan") {
4643
+ return {
4644
+ canPurchase: true,
4645
+ status: "available",
4646
+ transitionKind: "activate_hybrid",
4647
+ reason: null
4648
+ };
4649
+ }
4650
+ return {
4651
+ canPurchase: true,
4652
+ status: "available",
4653
+ transitionKind: "none",
4654
+ reason: null
4655
+ };
4656
+ }
4657
+ function normalizeMoneyLabel(item) {
4658
+ const amount = readString4(item.amount);
4659
+ const currency = readString4(item.currency);
4660
+ const billingPeriod = readString4(item.billingPeriod);
4661
+ if (!amount && !currency) return "Price unavailable";
4662
+ return `${amount} ${currency}${billingPeriod ? ` / ${billingPeriod}` : ""}`.trim();
4663
+ }
4664
+
4245
4665
  // src/utils/monetizationAccess.ts
4246
4666
  function asRecord5(value) {
4247
4667
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -4310,17 +4730,49 @@ function asRecord6(value) {
4310
4730
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
4311
4731
  return value;
4312
4732
  }
4313
- function readString4(value) {
4733
+ function readString5(value) {
4314
4734
  return typeof value === "string" ? value.trim() : "";
4315
4735
  }
4316
- function readBoolean(value, fallback) {
4736
+ function readBoolean2(value, fallback) {
4317
4737
  return typeof value === "boolean" ? value : fallback;
4318
4738
  }
4319
- function readNumber(value) {
4739
+ function readNumber2(value) {
4320
4740
  return typeof value === "number" && Number.isFinite(value) ? value : null;
4321
4741
  }
4742
+ function buildMonetizationCheckoutReturnUrl(input) {
4743
+ const url = new URL(input.currentHref, window.location.href);
4744
+ url.searchParams.set("focus", "plans");
4745
+ url.searchParams.set("xapps_monetization_intent_id", input.intentId);
4746
+ if (input.packageSlug) {
4747
+ url.searchParams.set("paywallPackage", input.packageSlug);
4748
+ }
4749
+ return url.toString();
4750
+ }
4751
+ function navigateToHostedCheckout(url) {
4752
+ const target = String(url || "").trim();
4753
+ if (!target) return;
4754
+ try {
4755
+ if (typeof window !== "undefined" && window.top && window.top !== window) {
4756
+ window.top.location.assign(target);
4757
+ return;
4758
+ }
4759
+ } catch {
4760
+ }
4761
+ window.location.assign(target);
4762
+ }
4763
+ function stripMonetizationCheckoutReturnParams(search) {
4764
+ const next = new URLSearchParams(search);
4765
+ const keysToDelete = [];
4766
+ for (const key of next.keys()) {
4767
+ if (key.startsWith("xapps_payment_")) keysToDelete.push(key);
4768
+ }
4769
+ keysToDelete.push("xapps_monetization_intent_id");
4770
+ for (const key of keysToDelete) next.delete(key);
4771
+ const rendered = next.toString();
4772
+ return rendered ? `?${rendered}` : "";
4773
+ }
4322
4774
  function formatDateTime2(value, locale) {
4323
- const raw = readString4(value);
4775
+ const raw = readString5(value);
4324
4776
  if (!raw) return "";
4325
4777
  const parsed = new Date(raw);
4326
4778
  if (Number.isNaN(parsed.getTime())) return raw;
@@ -4339,28 +4791,28 @@ function formatDateTime2(value, locale) {
4339
4791
  function normalizeUsageCreditSummary(value) {
4340
4792
  const summary = asRecord6(value);
4341
4793
  if (!summary) return null;
4342
- const availableCount = readNumber(summary.available_count) ?? 0;
4343
- const availableSessionBackedCount = readNumber(summary.available_session_backed_count) ?? 0;
4344
- const availableLedgerOnlyCount = readNumber(summary.available_ledger_only_count) ?? 0;
4345
- const reservedCount = readNumber(summary.reserved_count) ?? 0;
4346
- const reservedActiveCount = readNumber(summary.reserved_active_count) ?? reservedCount;
4347
- const reservedStaleCount = readNumber(summary.reserved_stale_count) ?? 0;
4348
- const consumedCount = readNumber(summary.consumed_count) ?? 0;
4794
+ const availableCount = readNumber2(summary.available_count) ?? 0;
4795
+ const availableSessionBackedCount = readNumber2(summary.available_session_backed_count) ?? 0;
4796
+ const availableLedgerOnlyCount = readNumber2(summary.available_ledger_only_count) ?? 0;
4797
+ const reservedCount = readNumber2(summary.reserved_count) ?? 0;
4798
+ const reservedActiveCount = readNumber2(summary.reserved_active_count) ?? reservedCount;
4799
+ const reservedStaleCount = readNumber2(summary.reserved_stale_count) ?? 0;
4800
+ const consumedCount = readNumber2(summary.consumed_count) ?? 0;
4349
4801
  const byToolRaw = Array.isArray(summary.by_tool) ? summary.by_tool : [];
4350
4802
  const byTool = byToolRaw.map((entry) => {
4351
4803
  const record = asRecord6(entry);
4352
4804
  if (!record) return null;
4353
- const toolName = readString4(record.tool_name);
4805
+ const toolName = readString5(record.tool_name);
4354
4806
  if (!toolName) return null;
4355
4807
  return {
4356
4808
  tool_name: toolName,
4357
- available_count: readNumber(record.available_count) ?? 0,
4358
- available_session_backed_count: readNumber(record.available_session_backed_count) ?? 0,
4359
- available_ledger_only_count: readNumber(record.available_ledger_only_count) ?? 0,
4360
- reserved_count: readNumber(record.reserved_count) ?? 0,
4361
- reserved_active_count: readNumber(record.reserved_active_count) ?? readNumber(record.reserved_count) ?? 0,
4362
- reserved_stale_count: readNumber(record.reserved_stale_count) ?? 0,
4363
- consumed_count: readNumber(record.consumed_count) ?? 0
4809
+ available_count: readNumber2(record.available_count) ?? 0,
4810
+ available_session_backed_count: readNumber2(record.available_session_backed_count) ?? 0,
4811
+ available_ledger_only_count: readNumber2(record.available_ledger_only_count) ?? 0,
4812
+ reserved_count: readNumber2(record.reserved_count) ?? 0,
4813
+ reserved_active_count: readNumber2(record.reserved_active_count) ?? readNumber2(record.reserved_count) ?? 0,
4814
+ reserved_stale_count: readNumber2(record.reserved_stale_count) ?? 0,
4815
+ consumed_count: readNumber2(record.consumed_count) ?? 0
4364
4816
  };
4365
4817
  }).filter((entry) => Boolean(entry)).filter(
4366
4818
  (entry) => entry.available_count > 0 || entry.available_ledger_only_count > 0 || entry.reserved_active_count > 0 || entry.reserved_stale_count > 0
@@ -4376,14 +4828,14 @@ function normalizeUsageCreditSummary(value) {
4376
4828
  reserved_active_count: reservedActiveCount,
4377
4829
  reserved_stale_count: reservedStaleCount,
4378
4830
  consumed_count: consumedCount,
4379
- updated_at: readString4(summary.updated_at) || null,
4831
+ updated_at: readString5(summary.updated_at) || null,
4380
4832
  by_tool: byTool
4381
4833
  };
4382
4834
  }
4383
4835
  function normalizeGuardPolicyKind(policy) {
4384
4836
  if (policy === "all" || policy === "any") return policy;
4385
4837
  const policyObj = asRecord6(policy);
4386
- const mode = readString4(policyObj?.mode);
4838
+ const mode = readString5(policyObj?.mode);
4387
4839
  return mode === "any" ? "any" : "all";
4388
4840
  }
4389
4841
  function defaultBlockingForTrigger(trigger) {
@@ -4392,6 +4844,47 @@ function defaultBlockingForTrigger(trigger) {
4392
4844
  function humanizeSlug(slug) {
4393
4845
  return String(slug || "").trim().replace(/[_-]+/g, " ").replace(/\s+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
4394
4846
  }
4847
+ function buildMonetizationHookSummaryItems(manifest) {
4848
+ const monetization = asRecord6(manifest?.monetization);
4849
+ const hooks = asRecord6(monetization?.hooks);
4850
+ const afterPaymentCompleted = asRecord6(
4851
+ hooks?.after_payment_completed ?? hooks?.afterPaymentCompleted
4852
+ );
4853
+ if (!afterPaymentCompleted || readBoolean2(afterPaymentCompleted.enabled, true) === false) {
4854
+ return [];
4855
+ }
4856
+ const baseItems = [];
4857
+ const defaultInvoiceRef = readString5(
4858
+ afterPaymentCompleted.invoice_ref ?? afterPaymentCompleted.invoiceRef
4859
+ );
4860
+ baseItems.push({
4861
+ slug: "xms_after_payment_completed",
4862
+ label: defaultInvoiceRef ? `XMS payment completed \xB7 ${defaultInvoiceRef}` : "XMS payment completed",
4863
+ trigger: "after:payment_completed",
4864
+ headless: true,
4865
+ blocking: false,
4866
+ order: null,
4867
+ policyKind: "all"
4868
+ });
4869
+ const byPaymentGuardRef = asRecord6(
4870
+ afterPaymentCompleted.by_payment_guard_ref ?? afterPaymentCompleted.byPaymentGuardRef
4871
+ );
4872
+ for (const [paymentGuardRef, rawValue] of Object.entries(byPaymentGuardRef ?? {})) {
4873
+ const config = asRecord6(rawValue);
4874
+ if (!config || readBoolean2(config.enabled, true) === false) continue;
4875
+ const invoiceRef = readString5(config.invoice_ref ?? config.invoiceRef);
4876
+ baseItems.push({
4877
+ slug: `xms_after_payment_completed_${paymentGuardRef}`,
4878
+ label: invoiceRef ? `XMS payment completed \xB7 ${paymentGuardRef} \xB7 ${invoiceRef}` : `XMS payment completed \xB7 ${paymentGuardRef}`,
4879
+ trigger: "after:payment_completed",
4880
+ headless: true,
4881
+ blocking: false,
4882
+ order: null,
4883
+ policyKind: "all"
4884
+ });
4885
+ }
4886
+ return baseItems;
4887
+ }
4395
4888
  function humanizeTrigger(trigger, translate) {
4396
4889
  const normalized = String(trigger || "").trim().toLowerCase();
4397
4890
  if (normalized === "before:tool_run") {
@@ -4413,6 +4906,12 @@ function describeGuardChain(policyKind, translate) {
4413
4906
  return policyKind === "any" ? translate("xapp.policy_any", void 0, "Any one satisfies") : translate("xapp.policy_all", void 0, "All steps apply");
4414
4907
  }
4415
4908
  function XappDetailPage() {
4909
+ return /* @__PURE__ */ jsx14(XappDetailPageContent, {});
4910
+ }
4911
+ function XappPlansPage() {
4912
+ return /* @__PURE__ */ jsx14(XappDetailPageContent, { renderMode: "plans_only" });
4913
+ }
4914
+ function XappDetailPageContent(props) {
4416
4915
  const { client, host, env } = useMarketplace();
4417
4916
  const { locale, t } = useMarketplaceI18n();
4418
4917
  const { xappId } = useParams2();
@@ -4420,14 +4919,30 @@ function XappDetailPage() {
4420
4919
  const token = useQueryToken7();
4421
4920
  const action = useQueryAction();
4422
4921
  const queryToolName = useQueryToolName();
4922
+ const routeQuery = useMemo8(() => new URLSearchParams(loc.search), [loc.search]);
4923
+ const routeSource = String(routeQuery.get("from") || "").trim().toLowerCase();
4924
+ const focusedSection = String(routeQuery.get("focus") || "").trim().toLowerCase();
4925
+ const requestedPaywallSlug = String(routeQuery.get("paywall") || "").trim().toLowerCase();
4926
+ const selectedPaywallPackageSlug = String(routeQuery.get("paywallPackage") || "").trim().toLowerCase();
4927
+ const paymentReturnStatus = String(routeQuery.get("xapps_payment_status") || "").trim().toLowerCase();
4928
+ const paymentReturnSessionId = String(routeQuery.get("xapps_payment_session_id") || "").trim();
4929
+ const paymentReturnIntentId = String(routeQuery.get("xapps_monetization_intent_id") || "").trim();
4423
4930
  const tokenSearch = buildTokenSearch(token, loc.search);
4424
4931
  const navigate = useNavigate3();
4425
4932
  const autoOpenedWidgetKeyRef = useRef("");
4933
+ const plansSectionRef = useRef(null);
4426
4934
  const canMutate = host.canMutate ? host.canMutate() : true;
4935
+ const renderMode = props?.renderMode === "plans_only" ? "plans_only" : "full";
4936
+ const plansOnlyMode = renderMode === "plans_only";
4937
+ const widgetHostedPlansMode = plansOnlyMode && routeSource === "widget";
4427
4938
  const addAppLabel = env?.copy?.addAppLabel || t("xapp.add_to_workspace", void 0, "Add to workspace");
4428
4939
  const removeAppLabel = env?.copy?.removeAppLabel || t("xapp.remove_from_workspace", void 0, "Remove from workspace");
4429
4940
  const updateAppLabel = env?.copy?.updateAppLabel || t("xapp.update_app", void 0, "Update app");
4430
4941
  const openAppLabel = env?.copy?.openAppLabel || t("xapp.open_app", void 0, "Open app");
4942
+ const hasSubject = Boolean(host.subjectId);
4943
+ const installationPolicyResolved = env?.installationPolicyResolved !== false;
4944
+ const mutationControlsReady = installationPolicyResolved || !hasSubject || !canMutate;
4945
+ const installationPolicy = env?.installationPolicy ?? host.installationPolicy ?? null;
4431
4946
  const [data, setData] = useState7(null);
4432
4947
  const [monetization, setMonetization] = useState7(null);
4433
4948
  const [error, setError] = useState7(null);
@@ -4436,13 +4951,24 @@ function XappDetailPage() {
4436
4951
  const [termsOpen, setTermsOpen] = useState7(false);
4437
4952
  const [termsAccepted, setTermsAccepted] = useState7(false);
4438
4953
  const [termsAction, setTermsAction] = useState7("none");
4954
+ const [termsWidgetId, setTermsWidgetId] = useState7(null);
4955
+ const [termsWidgetName, setTermsWidgetName] = useState7(null);
4439
4956
  const [removeConfirmOpen, setRemoveConfirmOpen] = useState7(false);
4440
4957
  const [guardSummaryOpen, setGuardSummaryOpen] = useState7(false);
4441
- const hasSubject = Boolean(host.subjectId);
4958
+ const [checkoutBusyPackageSlug, setCheckoutBusyPackageSlug] = useState7("");
4959
+ const [checkoutError, setCheckoutError] = useState7(null);
4960
+ const [checkoutNotice, setCheckoutNotice] = useState7(null);
4961
+ const checkoutFinalizeKeyRef = useRef("");
4442
4962
  const installationsByXappId = hasSubject ? host.getInstallationsByXappId() : {};
4443
- const installation = xappId ? installationsByXappId[String(xappId)] : null;
4963
+ const routeInstallationId = String(routeQuery.get("installationId") || "").trim();
4964
+ const installation = xappId && installationsByXappId[String(xappId)] ? installationsByXappId[String(xappId)] : xappId && routeInstallationId ? {
4965
+ installationId: routeInstallationId,
4966
+ xappId: String(xappId)
4967
+ } : null;
4444
4968
  const updateAvailable = Boolean(installation?.updateAvailable);
4445
- const widgetsEnabled = Boolean(installation) && !updateAvailable && hasSubject;
4969
+ const autoAvailableMode = mutationControlsReady && installationPolicy?.mode === "auto_available" && Boolean(hasSubject) && canMutate;
4970
+ const autoUpdateMode = mutationControlsReady && installationPolicy?.update_mode === "auto_update_compatible" && Boolean(hasSubject) && canMutate;
4971
+ const widgetsEnabled = Boolean(hasSubject) && (Boolean(installation) || autoAvailableMode) && (!updateAvailable || autoUpdateMode);
4446
4972
  const isEmbedded = window.location.pathname.startsWith("/embed");
4447
4973
  const singleXappMode = env?.singleXappMode;
4448
4974
  async function refresh() {
@@ -4455,7 +4981,7 @@ function XappDetailPage() {
4455
4981
  });
4456
4982
  setData(asRecord6(res));
4457
4983
  } catch (e) {
4458
- setError(readString4(asRecord6(e)?.message) || String(e));
4984
+ setError(readString5(asRecord6(e)?.message) || String(e));
4459
4985
  } finally {
4460
4986
  setBusy(false);
4461
4987
  }
@@ -4499,7 +5025,10 @@ function XappDetailPage() {
4499
5025
  let cancelled = false;
4500
5026
  void (async () => {
4501
5027
  try {
4502
- const next = await client.getMyXappMonetization(currentXappId);
5028
+ const next = await client.getMyXappMonetization(currentXappId, {
5029
+ installationId: installation?.installationId ?? null,
5030
+ locale
5031
+ });
4503
5032
  if (!cancelled) {
4504
5033
  setMonetization(next);
4505
5034
  }
@@ -4512,23 +5041,23 @@ function XappDetailPage() {
4512
5041
  return () => {
4513
5042
  cancelled = true;
4514
5043
  };
4515
- }, [client, host.subjectId, xappId]);
5044
+ }, [client, host.subjectId, installation?.installationId, xappId]);
4516
5045
  const manifest = asRecord6(data?.manifest);
4517
5046
  const xappRecord = asRecord6(data?.xapp);
4518
5047
  const versionRecord = asRecord6(data?.version);
4519
5048
  const publisherRecord = asRecord6(xappRecord?.publisher);
4520
- const title = resolveMarketplaceText(manifest?.title, locale) || readString4(xappRecord?.name) || t("xapp.kicker_default", void 0, "Xapp");
4521
- const description = resolveMarketplaceText(manifest?.description, locale) || readString4(xappRecord?.description) || "";
4522
- const imageUrl = readString4(manifest?.image) || "https://picsum.photos/seed/xapps-detail/840/360";
5049
+ const title = resolveMarketplaceText(manifest?.title, locale) || readString5(xappRecord?.name) || t("xapp.kicker_default", void 0, "Xapp");
5050
+ const description = resolveMarketplaceText(manifest?.description, locale) || readString5(xappRecord?.description) || "";
5051
+ const imageUrl = readString5(manifest?.image) || "https://picsum.photos/seed/xapps-detail/840/360";
4523
5052
  const widgets = Array.isArray(data?.widgets) ? data.widgets.filter((widget) => Boolean(asRecord6(widget))) : [];
4524
5053
  const defaultWidget = useMemo8(() => {
4525
- const def = widgets.find((w) => readBoolean(w.default, false));
5054
+ const def = widgets.find((w) => readBoolean2(w.default, false));
4526
5055
  return def || widgets[0] || null;
4527
5056
  }, [widgets]);
4528
5057
  const requestedWidget = useMemo8(() => {
4529
- const desiredToolName = readString4(queryToolName);
5058
+ const desiredToolName = readString5(queryToolName);
4530
5059
  if (!desiredToolName) return defaultWidget;
4531
- return widgets.find((widget) => readString4(widget.bind_tool_name) === desiredToolName) || defaultWidget;
5060
+ return widgets.find((widget) => readString5(widget.bind_tool_name) === desiredToolName) || defaultWidget;
4532
5061
  }, [defaultWidget, queryToolName, widgets]);
4533
5062
  const backTo = {
4534
5063
  pathname: isEmbedded ? "/embed/catalog" : "..",
@@ -4536,55 +5065,126 @@ function XappDetailPage() {
4536
5065
  };
4537
5066
  const terms = asRecord6(manifest?.terms);
4538
5067
  const termsTitle = resolveMarketplaceText(terms?.title, locale) || t("xapp.terms_title", void 0, "Terms & Conditions");
4539
- const termsText = resolveMarketplaceText(terms?.text, locale) || readString4(terms?.text);
4540
- const termsUrl = readString4(terms?.url);
5068
+ const termsText = resolveMarketplaceText(terms?.text, locale) || readString5(terms?.text);
5069
+ const termsUrl = readString5(terms?.url);
4541
5070
  const hasTermsContent = Boolean(termsText || termsUrl);
4542
5071
  const requiresTerms = Boolean(terms || action === "install" || action === "update");
5072
+ function requestInstallForWidget(widgetId, widgetName = null, acceptedTerms = false, openAfterInstall = false) {
5073
+ host.requestInstall({
5074
+ xappId: String(xappId ?? ""),
5075
+ defaultWidgetId: widgetId,
5076
+ xappTitle: String(title),
5077
+ widgetName,
5078
+ openAfterInstall,
5079
+ subjectId: host.subjectId ?? null,
5080
+ ...acceptedTerms ? { termsAccepted: true } : {}
5081
+ });
5082
+ }
5083
+ function requestUpdateForWidget(widgetId, acceptedTerms = false) {
5084
+ if (!installation?.installationId || !host.requestUpdate) return;
5085
+ const activeWidget = widgetId && Array.isArray(widgets) ? widgets.find((entry) => readString5(entry.id) === widgetId) || null : null;
5086
+ host.requestUpdate({
5087
+ installationId: installation.installationId,
5088
+ xappId: String(xappId ?? ""),
5089
+ widgetId,
5090
+ xappTitle: String(title),
5091
+ widgetName: resolveMarketplaceText(activeWidget?.title, locale) || readString5(activeWidget?.name) || null,
5092
+ ...acceptedTerms ? { termsAccepted: true } : {}
5093
+ });
5094
+ }
5095
+ function openInstalledWidget(widgetId, widgetName, toolName) {
5096
+ if (!installation) return;
5097
+ if (env?.embedMode) {
5098
+ navigate({
5099
+ pathname: isEmbedded ? `/widget/${encodeURIComponent(installation.installationId)}/${encodeURIComponent(widgetId)}` : `/marketplace/widget/${encodeURIComponent(installation.installationId)}/${encodeURIComponent(widgetId)}`,
5100
+ search: tokenSearch
5101
+ });
5102
+ return;
5103
+ }
5104
+ host.openWidget({
5105
+ installationId: installation.installationId,
5106
+ widgetId,
5107
+ xappId: String(xappId ?? ""),
5108
+ xappTitle: String(title),
5109
+ widgetName,
5110
+ toolName
5111
+ });
5112
+ }
5113
+ function launchWidgetForSubject(widgetId, widgetName, toolName) {
5114
+ if (!hasSubject || !widgetId) return;
5115
+ if (!installation) {
5116
+ if (requiresTerms) {
5117
+ setTermsAccepted(false);
5118
+ setTermsAction("install");
5119
+ setTermsWidgetId(widgetId);
5120
+ setTermsWidgetName(widgetName);
5121
+ setTermsOpen(true);
5122
+ return;
5123
+ }
5124
+ requestInstallForWidget(widgetId, widgetName, false, true);
5125
+ return;
5126
+ }
5127
+ if (updateAvailable) {
5128
+ if (autoUpdateMode && host.requestUpdate) {
5129
+ if (requiresTerms) {
5130
+ setTermsAccepted(false);
5131
+ setTermsAction("update");
5132
+ setTermsWidgetId(widgetId);
5133
+ setTermsWidgetName(widgetName);
5134
+ setTermsOpen(true);
5135
+ return;
5136
+ }
5137
+ requestUpdateForWidget(widgetId);
5138
+ }
5139
+ return;
5140
+ }
5141
+ openInstalledWidget(widgetId, widgetName, toolName);
5142
+ }
4543
5143
  useEffect9(() => {
4544
5144
  if (action === "install") {
4545
5145
  setTermsAccepted(false);
4546
5146
  setTermsAction("install");
5147
+ setTermsWidgetId(readString5(defaultWidget?.id) || null);
5148
+ setTermsWidgetName(resolveMarketplaceText(defaultWidget?.title, locale) || null);
4547
5149
  setTermsOpen(true);
4548
5150
  return;
4549
5151
  }
4550
5152
  if (action === "update" && installation) {
4551
5153
  setTermsAccepted(false);
4552
5154
  setTermsAction("update");
5155
+ setTermsWidgetId(null);
5156
+ setTermsWidgetName(null);
4553
5157
  setTermsOpen(true);
4554
5158
  }
4555
- }, [action, installation]);
5159
+ }, [action, defaultWidget, installation, locale]);
4556
5160
  useEffect9(() => {
4557
5161
  if (action !== "open-widget") return;
4558
5162
  if (env?.embedMode) return;
4559
- if (!installation || !widgetsEnabled) return;
5163
+ if (!requestedWidget || !widgetsEnabled) return;
4560
5164
  const widget = requestedWidget;
4561
- const widgetId = readString4(widget?.id);
5165
+ const widgetId = readString5(widget?.id);
4562
5166
  if (!widgetId) return;
4563
5167
  const key = [
4564
5168
  String(xappId ?? ""),
4565
- installation.installationId,
5169
+ installation?.installationId || "auto",
4566
5170
  widgetId,
4567
- readString4(queryToolName)
5171
+ readString5(queryToolName)
4568
5172
  ].join(":");
4569
5173
  if (autoOpenedWidgetKeyRef.current === key) return;
4570
5174
  autoOpenedWidgetKeyRef.current = key;
4571
- host.openWidget({
4572
- installationId: installation.installationId,
5175
+ launchWidgetForSubject(
4573
5176
  widgetId,
4574
- xappId: String(xappId ?? ""),
4575
- xappTitle: String(title),
4576
- widgetName: resolveMarketplaceText(widget?.title, locale) || readString4(widget?.widget_name) || readString4(widget?.name) || t("common.widget", void 0, "Widget"),
4577
- toolName: readString4(widget?.bind_tool_name)
4578
- });
5177
+ resolveMarketplaceText(widget?.title, locale) || readString5(widget?.widget_name) || readString5(widget?.name) || t("common.widget", void 0, "Widget"),
5178
+ readString5(widget?.bind_tool_name)
5179
+ );
4579
5180
  }, [
4580
5181
  action,
4581
5182
  env?.embedMode,
4582
- host,
4583
5183
  installation,
5184
+ launchWidgetForSubject,
4584
5185
  locale,
4585
5186
  queryToolName,
4586
5187
  requestedWidget,
4587
- title,
4588
5188
  widgetsEnabled,
4589
5189
  xappId
4590
5190
  ]);
@@ -4618,8 +5218,8 @@ function XappDetailPage() {
4618
5218
  }
4619
5219
  return t("activity.requests_title", void 0, "Requests");
4620
5220
  }
4621
- const publisherSlug = readString4(publisherRecord?.slug);
4622
- const publisherName = readString4(publisherRecord?.name) || publisherSlug;
5221
+ const publisherSlug = readString5(publisherRecord?.slug);
5222
+ const publisherName = readString5(publisherRecord?.name) || publisherSlug;
4623
5223
  const publisherTo = publisherSlug ? {
4624
5224
  pathname: isEmbedded ? `/publishers/${encodeURIComponent(publisherSlug)}` : `/marketplace/publishers/${encodeURIComponent(publisherSlug)}`,
4625
5225
  search: tokenSearch
@@ -4627,28 +5227,31 @@ function XappDetailPage() {
4627
5227
  const guardSummary = useMemo8(() => {
4628
5228
  const guardsRaw = Array.isArray(manifest?.guards) ? manifest.guards : [];
4629
5229
  const policyMap = asRecord6(manifest?.guards_policy) ?? {};
4630
- const items = guardsRaw.map((raw) => {
5230
+ const guardItems = guardsRaw.map((raw) => {
4631
5231
  const guard = asRecord6(raw);
4632
5232
  if (!guard) return null;
4633
- const trigger = readString4(guard.trigger);
4634
- const slug = readString4(guard.slug);
5233
+ const trigger = readString5(guard.trigger);
5234
+ const slug = readString5(guard.slug);
4635
5235
  if (!trigger || !slug) return null;
4636
5236
  const policyKind = normalizeGuardPolicyKind(policyMap[trigger]);
4637
5237
  return {
4638
5238
  slug,
4639
5239
  trigger,
4640
- headless: readBoolean(guard.headless, true),
4641
- blocking: readBoolean(guard.blocking, defaultBlockingForTrigger(trigger)),
4642
- order: readNumber(guard.order),
5240
+ headless: readBoolean2(guard.headless, true),
5241
+ blocking: readBoolean2(guard.blocking, defaultBlockingForTrigger(trigger)),
5242
+ order: readNumber2(guard.order),
4643
5243
  policyKind
4644
5244
  };
4645
- }).filter((item) => Boolean(item)).sort((a, b) => {
4646
- const orderA = a.order ?? Number.MAX_SAFE_INTEGER;
4647
- const orderB = b.order ?? Number.MAX_SAFE_INTEGER;
4648
- if (orderA !== orderB) return orderA - orderB;
4649
- if (a.trigger !== b.trigger) return a.trigger.localeCompare(b.trigger);
4650
- return a.slug.localeCompare(b.slug);
4651
- });
5245
+ }).filter((item) => Boolean(item));
5246
+ const items = [...guardItems, ...buildMonetizationHookSummaryItems(manifest)].sort(
5247
+ (a, b) => {
5248
+ const orderA = a.order ?? Number.MAX_SAFE_INTEGER;
5249
+ const orderB = b.order ?? Number.MAX_SAFE_INTEGER;
5250
+ if (orderA !== orderB) return orderA - orderB;
5251
+ if (a.trigger !== b.trigger) return a.trigger.localeCompare(b.trigger);
5252
+ return a.slug.localeCompare(b.slug);
5253
+ }
5254
+ );
4652
5255
  const byTrigger = /* @__PURE__ */ new Map();
4653
5256
  for (const item of items) {
4654
5257
  const existing = byTrigger.get(item.trigger) ?? [];
@@ -4662,15 +5265,259 @@ function XappDetailPage() {
4662
5265
  [data?.usage_credit_summary]
4663
5266
  );
4664
5267
  const monetizationAccessProjection = monetization?.access_projection ?? null;
5268
+ const monetizationPaywalls = useMemo8(
5269
+ () => listXappMonetizationPaywalls(monetization?.paywalls),
5270
+ [monetization?.paywalls]
5271
+ );
5272
+ const selectedPaywall = useMemo8(
5273
+ () => monetizationPaywalls.find(
5274
+ (item) => readString5(asRecord6(item)?.slug).trim().toLowerCase() === requestedPaywallSlug
5275
+ ) || selectXappMonetizationPaywall({
5276
+ paywalls: monetizationPaywalls,
5277
+ placement: "default_paywall"
5278
+ }) || selectXappMonetizationPaywall({
5279
+ paywalls: monetizationPaywalls,
5280
+ placement: "paywall"
5281
+ }) || monetizationPaywalls[0] || null,
5282
+ [monetizationPaywalls, requestedPaywallSlug]
5283
+ );
5284
+ const selectedPaywallRenderModel = useMemo8(
5285
+ () => selectedPaywall ? buildMonetizationPaywallRenderModel(selectedPaywall) : null,
5286
+ [selectedPaywall]
5287
+ );
5288
+ const selectedPaywallPackageRecords = useMemo8(
5289
+ () => selectedPaywall ? flattenXappMonetizationPaywallPackages(selectedPaywall) : [],
5290
+ [selectedPaywall]
5291
+ );
5292
+ useEffect9(() => {
5293
+ if (focusedSection !== "plans" || !plansSectionRef.current) return;
5294
+ if (typeof plansSectionRef.current.scrollIntoView === "function") {
5295
+ plansSectionRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
5296
+ }
5297
+ }, [focusedSection]);
5298
+ useEffect9(() => {
5299
+ const currentXappId = String(xappId ?? "").trim();
5300
+ if (!currentXappId || !paymentReturnIntentId || !paymentReturnStatus) return;
5301
+ const handledKey = [
5302
+ currentXappId,
5303
+ paymentReturnIntentId,
5304
+ paymentReturnSessionId,
5305
+ paymentReturnStatus
5306
+ ].join(":");
5307
+ if (checkoutFinalizeKeyRef.current === handledKey) return;
5308
+ checkoutFinalizeKeyRef.current = handledKey;
5309
+ const clearReturnParams = () => {
5310
+ const nextSearch = stripMonetizationCheckoutReturnParams(loc.search);
5311
+ void navigate(
5312
+ {
5313
+ pathname: loc.pathname,
5314
+ search: nextSearch
5315
+ },
5316
+ { replace: true }
5317
+ );
5318
+ };
5319
+ if (paymentReturnStatus === "cancelled" || paymentReturnStatus === "canceled") {
5320
+ setCheckoutError(null);
5321
+ setCheckoutNotice(
5322
+ t("xapp.checkout_cancelled", void 0, "Checkout was cancelled before completion.")
5323
+ );
5324
+ clearReturnParams();
5325
+ return;
5326
+ }
5327
+ if (paymentReturnStatus === "failed") {
5328
+ setCheckoutNotice(null);
5329
+ setCheckoutError(
5330
+ t("xapp.checkout_failed", void 0, "Payment failed before access could be issued.")
5331
+ );
5332
+ clearReturnParams();
5333
+ return;
5334
+ }
5335
+ if (paymentReturnStatus !== "paid" || typeof client.finalizeMyXappPurchasePaymentSession !== "function") {
5336
+ return;
5337
+ }
5338
+ let cancelled = false;
5339
+ setCheckoutNotice(
5340
+ t("xapp.checkout_finalizing", void 0, "Finalizing payment and refreshing access...")
5341
+ );
5342
+ setCheckoutError(null);
5343
+ void (async () => {
5344
+ try {
5345
+ await client.finalizeMyXappPurchasePaymentSession({
5346
+ xappId: currentXappId,
5347
+ intentId: paymentReturnIntentId
5348
+ });
5349
+ if (typeof client.getMyXappMonetization === "function") {
5350
+ const next = await client.getMyXappMonetization(currentXappId, {
5351
+ installationId: installation?.installationId ?? null,
5352
+ locale
5353
+ });
5354
+ if (!cancelled) setMonetization(next);
5355
+ }
5356
+ if (!cancelled) {
5357
+ setCheckoutNotice(
5358
+ t("xapp.checkout_finalized", void 0, "Payment completed and access was refreshed.")
5359
+ );
5360
+ clearReturnParams();
5361
+ }
5362
+ } catch (error2) {
5363
+ if (cancelled) return;
5364
+ const message = readString5(asRecord6(error2)?.message) || (error2 instanceof Error ? error2.message : "") || t(
5365
+ "xapp.checkout_finalize_failed",
5366
+ void 0,
5367
+ "Payment returned, but access refresh did not complete yet."
5368
+ );
5369
+ setCheckoutNotice(null);
5370
+ setCheckoutError(message);
5371
+ }
5372
+ })();
5373
+ return () => {
5374
+ cancelled = true;
5375
+ };
5376
+ }, [
5377
+ client,
5378
+ installation?.installationId,
5379
+ locale,
5380
+ loc.pathname,
5381
+ loc.search,
5382
+ navigate,
5383
+ paymentReturnIntentId,
5384
+ paymentReturnSessionId,
5385
+ paymentReturnStatus,
5386
+ t,
5387
+ xappId
5388
+ ]);
5389
+ async function finalizeCurrentUserCheckoutIntent(currentXappId, intentId) {
5390
+ if (typeof client.finalizeMyXappPurchasePaymentSession !== "function") {
5391
+ throw new Error(
5392
+ t(
5393
+ "xapp.checkout_finalize_unavailable",
5394
+ void 0,
5395
+ "Access refresh is not available for this checkout flow."
5396
+ )
5397
+ );
5398
+ }
5399
+ setCheckoutNotice(
5400
+ t("xapp.checkout_finalizing", void 0, "Finalizing payment and refreshing access...")
5401
+ );
5402
+ setCheckoutError(null);
5403
+ await client.finalizeMyXappPurchasePaymentSession({
5404
+ xappId: currentXappId,
5405
+ intentId
5406
+ });
5407
+ if (typeof client.getMyXappMonetization === "function") {
5408
+ const next = await client.getMyXappMonetization(currentXappId, {
5409
+ installationId: installation?.installationId ?? null,
5410
+ locale
5411
+ });
5412
+ setMonetization(next);
5413
+ }
5414
+ setCheckoutNotice(
5415
+ t("xapp.checkout_finalized", void 0, "Payment completed and access was refreshed.")
5416
+ );
5417
+ }
5418
+ async function startPackageCheckout(packageSlug) {
5419
+ const normalizedSlug = packageSlug.trim().toLowerCase();
5420
+ if (!xappId || typeof client.prepareMyXappPurchaseIntent !== "function" || typeof client.createMyXappPurchasePaymentSession !== "function" || !normalizedSlug) {
5421
+ return;
5422
+ }
5423
+ const pkg = selectedPaywallPackageRecords.find(
5424
+ (item) => readString5(item.packageSlug).trim().toLowerCase() === normalizedSlug
5425
+ );
5426
+ if (pkg) {
5427
+ const purchasePolicy = getPackagePurchasePolicy(pkg);
5428
+ if (!purchasePolicy.canPurchase) {
5429
+ setCheckoutError(
5430
+ purchasePolicy.status === "owned_additive_unlock" ? t(
5431
+ "xapp.checkout_owned_unlock_blocked",
5432
+ void 0,
5433
+ "This add-on unlock is already owned for the current monetization scope."
5434
+ ) : t(
5435
+ "xapp.checkout_current_plan_blocked",
5436
+ void 0,
5437
+ "This plan is already active for the current monetization scope."
5438
+ )
5439
+ );
5440
+ return;
5441
+ }
5442
+ }
5443
+ const offeringId = readString5(pkg?.offeringId);
5444
+ const packageId = readString5(pkg?.packageId);
5445
+ const priceId = readString5(pkg?.priceId);
5446
+ if (!offeringId || !packageId || !priceId) {
5447
+ setCheckoutError(
5448
+ t(
5449
+ "xapp.checkout_package_missing",
5450
+ void 0,
5451
+ "This package is missing purchase metadata in the published paywall."
5452
+ )
5453
+ );
5454
+ return;
5455
+ }
5456
+ setCheckoutBusyPackageSlug(normalizedSlug);
5457
+ setCheckoutError(null);
5458
+ setCheckoutNotice(null);
5459
+ try {
5460
+ const prepared = await client.prepareMyXappPurchaseIntent({
5461
+ xappId: String(xappId),
5462
+ offeringId,
5463
+ packageId,
5464
+ priceId,
5465
+ installationId: installation?.installationId ?? null
5466
+ });
5467
+ const returnUrl = buildMonetizationCheckoutReturnUrl({
5468
+ currentHref: window.location.href,
5469
+ intentId: String(prepared.prepared_intent.purchase_intent_id || ""),
5470
+ packageSlug: normalizedSlug
5471
+ });
5472
+ const payment = await client.createMyXappPurchasePaymentSession({
5473
+ xappId: String(xappId),
5474
+ intentId: String(prepared.prepared_intent.purchase_intent_id || ""),
5475
+ returnUrl,
5476
+ cancelUrl: returnUrl,
5477
+ xappsResume: returnUrl,
5478
+ locale
5479
+ });
5480
+ const paymentPageUrl = readString5(payment.payment_page_url);
5481
+ const paymentStatus = readString5(payment.payment_session?.status).trim().toLowerCase();
5482
+ if (!paymentPageUrl && (paymentStatus === "paid" || paymentStatus === "completed")) {
5483
+ await finalizeCurrentUserCheckoutIntent(
5484
+ String(xappId),
5485
+ String(prepared.prepared_intent.purchase_intent_id || "")
5486
+ );
5487
+ return;
5488
+ }
5489
+ if (!paymentPageUrl) {
5490
+ throw new Error(
5491
+ t(
5492
+ "xapp.checkout_page_missing",
5493
+ void 0,
5494
+ "Payment page is not available for this package."
5495
+ )
5496
+ );
5497
+ }
5498
+ navigateToHostedCheckout(paymentPageUrl);
5499
+ } catch (error2) {
5500
+ const message = readString5(asRecord6(error2)?.message) || (error2 instanceof Error ? error2.message : "") || t("xapp.checkout_start_failed", void 0, "Unable to start checkout for this package.");
5501
+ setCheckoutError(message);
5502
+ } finally {
5503
+ setCheckoutBusyPackageSlug("");
5504
+ }
5505
+ }
4665
5506
  const hasCatalogMonetization = hasMarketplaceCatalogMonetization(manifest);
4666
5507
  const monetizationAccess = asRecord6(monetization?.access_projection);
4667
5508
  const monetizationSubscription = asRecord6(monetization?.current_subscription);
5509
+ const additiveEntitlements = Array.isArray(monetization?.additive_entitlements) ? monetization.additive_entitlements.map((item) => asRecord6(item)).filter((item) => item ? Object.keys(item).length > 0 : false) : [];
5510
+ const activeAdditiveEntitlements = additiveEntitlements.filter((item) => {
5511
+ const status = readString5(item.status).trim().toLowerCase();
5512
+ return status === "active" || status === "grace_period";
5513
+ });
5514
+ const additiveUnlockLabels = activeAdditiveEntitlements.map((item) => readString5(item.tier) || readString5(item.product_slug)).filter(Boolean);
4668
5515
  const overduePolicy = asRecord6(monetizationSubscription?.overdue_policy);
4669
- const currentTier = readString4(monetizationSubscription?.tier) || readString4(monetizationAccess?.tier);
4670
- const balanceState = readString4(monetizationAccess?.balance_state);
4671
- const subscriptionStatus = readString4(monetizationSubscription?.status);
5516
+ const currentTier = readString5(monetizationSubscription?.tier) || readString5(monetizationAccess?.tier);
5517
+ const balanceState = readString5(monetizationAccess?.balance_state);
5518
+ const subscriptionStatus = readString5(monetizationSubscription?.status);
4672
5519
  const subscriptionCoverage = typeof overduePolicy?.has_current_access === "boolean" ? overduePolicy.has_current_access ? t("xapp.subscription_coverage_active", void 0, "Still covered") : t("xapp.subscription_coverage_inactive", void 0, "Not covered") : null;
4673
- const subscriptionReasonKey = readString4(overduePolicy?.effective_status_reason);
5520
+ const subscriptionReasonKey = readString5(overduePolicy?.effective_status_reason);
4674
5521
  const subscriptionReason = subscriptionReasonKey === "grace_covered_past_due" ? t(
4675
5522
  "xapp.subscription_reason_grace_covered_past_due",
4676
5523
  void 0,
@@ -4688,17 +5535,154 @@ function XappDetailPage() {
4688
5535
  const expiryBoundaryAt = formatDateTime2(overduePolicy?.expiry_boundary_at, locale);
4689
5536
  const renewsAt = formatDateTime2(monetizationSubscription?.renews_at, locale);
4690
5537
  const expiresAt = formatDateTime2(monetizationSubscription?.expired_at, locale) || formatDateTime2(monetizationSubscription?.current_period_ends_at, locale);
4691
- const creditsRemaining = readString4(monetizationAccess?.credits_remaining);
5538
+ const creditsRemaining = readString5(monetizationAccess?.credits_remaining);
4692
5539
  const accessState = resolveMarketplaceDefaultAccessState({
4693
5540
  projection: monetizationAccessProjection,
4694
5541
  hasCatalogMonetization,
4695
5542
  availableLabel: t("xapp.access_state_available", void 0, "available")
4696
5543
  });
4697
5544
  const hasMonetizationState = Boolean(
4698
- currentTier || accessState || subscriptionStatus || subscriptionCoverage || subscriptionReason || overdueSince || expiryBoundaryAt || renewsAt || expiresAt || creditsRemaining
5545
+ currentTier || accessState || subscriptionStatus || subscriptionCoverage || subscriptionReason || overdueSince || expiryBoundaryAt || renewsAt || expiresAt || creditsRemaining || additiveUnlockLabels.length > 0
4699
5546
  );
4700
- const manifestScreenshots = Array.isArray(manifest?.screenshots) ? manifest.screenshots.map((shot) => readString4(shot)).filter(Boolean) : [];
4701
- const manifestTags = Array.isArray(manifest?.tags) ? manifest.tags.map((tag) => readString4(tag)).filter(Boolean) : [];
5547
+ const manifestScreenshots = Array.isArray(manifest?.screenshots) ? manifest.screenshots.map((shot) => readString5(shot)).filter(Boolean) : [];
5548
+ const manifestTags = Array.isArray(manifest?.tags) ? manifest.tags.map((tag) => readString5(tag)).filter(Boolean) : [];
5549
+ function getPackagePurchasePolicy(item) {
5550
+ return resolveMonetizationPackagePurchasePolicy({
5551
+ item,
5552
+ currentSubscription: monetizationSubscription,
5553
+ additiveEntitlements: activeAdditiveEntitlements
5554
+ });
5555
+ }
5556
+ const currentAccessCard = hasMonetizationState ? /* @__PURE__ */ jsxs10("div", { className: "mx-sidebar-card", children: [
5557
+ /* @__PURE__ */ jsx14("h3", { className: "mx-section-title mx-detail-sidebar-title", children: t("xapp.current_access_title", void 0, "Current Access") }),
5558
+ currentTier ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5559
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.current_plan_label", void 0, "Current plan") }),
5560
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: currentTier })
5561
+ ] }) : null,
5562
+ accessState ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5563
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.access_state_label", void 0, "Access state") }),
5564
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: accessState })
5565
+ ] }) : null,
5566
+ subscriptionStatus ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5567
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_status_label", void 0, "Subscription status") }),
5568
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: subscriptionStatus })
5569
+ ] }) : null,
5570
+ subscriptionCoverage ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5571
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_coverage_label", void 0, "Coverage") }),
5572
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: subscriptionCoverage })
5573
+ ] }) : null,
5574
+ subscriptionReason ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item mx-meta-item-top", children: [
5575
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_reason_label", void 0, "Status reason") }),
5576
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: subscriptionReason })
5577
+ ] }) : null,
5578
+ overdueSince ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5579
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_overdue_since_label", void 0, "Overdue since") }),
5580
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: overdueSince })
5581
+ ] }) : null,
5582
+ expiryBoundaryAt ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5583
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_expiry_boundary_label", void 0, "Expiry boundary") }),
5584
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: expiryBoundaryAt })
5585
+ ] }) : null,
5586
+ renewsAt ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5587
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.renews_at_label", void 0, "Renews at") }),
5588
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: renewsAt })
5589
+ ] }) : null,
5590
+ expiresAt ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5591
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.expires_at_label", void 0, "Expires at") }),
5592
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: expiresAt })
5593
+ ] }) : null,
5594
+ creditsRemaining ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5595
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.credits_remaining_label", void 0, "Credits remaining") }),
5596
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: creditsRemaining })
5597
+ ] }) : null,
5598
+ additiveUnlockLabels.length > 0 ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item mx-meta-item-top", children: [
5599
+ /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.add_on_unlocks_label", void 0, "Add-on unlocks") }),
5600
+ /* @__PURE__ */ jsx14("div", { className: "mx-meta-value mx-meta-stack-sm", children: additiveUnlockLabels.map((label) => /* @__PURE__ */ jsx14("div", { children: label }, label)) })
5601
+ ] }) : null
5602
+ ] }) : null;
5603
+ const plansCard = selectedPaywallRenderModel ? /* @__PURE__ */ jsxs10("div", { className: "mx-sidebar-card", ref: plansSectionRef, children: [
5604
+ /* @__PURE__ */ jsx14("h3", { className: "mx-section-title mx-detail-sidebar-title", children: t("xapp.plan_options_title", void 0, "Plans") }),
5605
+ /* @__PURE__ */ jsxs10("div", { className: "mx-paywall-card-head", children: [
5606
+ /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-title", children: selectedPaywallRenderModel.paywallLabel }),
5607
+ selectedPaywallRenderModel.summary ? /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-summary", children: selectedPaywallRenderModel.summary }) : null
5608
+ ] }),
5609
+ selectedPaywallRenderModel.badges.length > 0 ? /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-badges", children: selectedPaywallRenderModel.badges.map((badge) => /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-badge", children: badge }, badge)) }) : null,
5610
+ /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-packages", children: selectedPaywallRenderModel.packages.map((item) => {
5611
+ const normalizedPackageSlug = item.packageSlug.trim().toLowerCase();
5612
+ const purchasePolicy = getPackagePurchasePolicy(item);
5613
+ const isCurrentPackage = purchasePolicy.status === "current_recurring_plan";
5614
+ const isOwnedAdditive = purchasePolicy.status === "owned_additive_unlock";
5615
+ const isAdditiveCompanion = purchasePolicy.transitionKind === "buy_additive_unlock" && subscriptionStatus === "active";
5616
+ return /* @__PURE__ */ jsxs10(
5617
+ "div",
5618
+ {
5619
+ className: `mx-paywall-card-package ${item.isDefault ? "is-default" : ""} ${selectedPaywallPackageSlug && normalizedPackageSlug === selectedPaywallPackageSlug ? "is-selected" : ""}`,
5620
+ children: [
5621
+ /* @__PURE__ */ jsxs10("div", { className: "mx-paywall-card-package-head", children: [
5622
+ /* @__PURE__ */ jsxs10("div", { children: [
5623
+ /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-package-title", children: item.packageTitle }),
5624
+ item.description ? /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-package-description", children: item.description }) : null
5625
+ ] }),
5626
+ /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-money", children: item.moneyLabel })
5627
+ ] }),
5628
+ /* @__PURE__ */ jsxs10("div", { className: "mx-paywall-card-package-meta", children: [
5629
+ /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-package-fit", children: item.fitLabel }),
5630
+ selectedPaywallPackageSlug && normalizedPackageSlug === selectedPaywallPackageSlug ? /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-package-default", children: t("xapp.selected_label", void 0, "Selected") }) : null,
5631
+ isOwnedAdditive ? /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-package-default", children: t("xapp.owned_unlock_label", void 0, "Owned unlock") }) : null,
5632
+ isCurrentPackage && !isOwnedAdditive ? /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-package-default", children: t("xapp.current_plan_label", void 0, "Current plan") }) : null,
5633
+ isAdditiveCompanion ? /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-package-default", children: t("xapp.additive_unlock_label", void 0, "Add-on with membership") }) : null,
5634
+ item.isDefault ? /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-package-default", children: t("xapp.default_label", void 0, "Default") }) : null
5635
+ ] }),
5636
+ item.signals.length > 0 ? /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-signals", children: item.signals.map((signal) => /* @__PURE__ */ jsx14("span", { className: "mx-paywall-card-signal", children: signal }, signal)) }) : null,
5637
+ isAdditiveCompanion ? /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-summary", children: t(
5638
+ "xapp.additive_unlock_message",
5639
+ void 0,
5640
+ "This one-time unlock is additive. It adds access on top of the active recurring membership instead of replacing it."
5641
+ ) }) : null,
5642
+ typeof client.prepareMyXappPurchaseIntent === "function" && typeof client.createMyXappPurchasePaymentSession === "function" && canMutate ? /* @__PURE__ */ jsx14(
5643
+ "button",
5644
+ {
5645
+ className: "mx-btn mx-btn-secondary",
5646
+ disabled: !purchasePolicy.canPurchase || checkoutBusyPackageSlug === normalizedPackageSlug,
5647
+ onClick: () => void startPackageCheckout(item.packageSlug),
5648
+ children: isOwnedAdditive ? t("xapp.owned_unlock_active", void 0, "Owned unlock active") : isCurrentPackage ? t("xapp.current_plan_active", void 0, "Current plan active") : checkoutBusyPackageSlug === normalizedPackageSlug ? t("xapp.checkout_starting", void 0, "Starting checkout...") : isAdditiveCompanion ? t("xapp.additive_unlock_action", void 0, "Purchase add-on unlock") : t("xapp.checkout_action", void 0, "Continue to checkout")
5649
+ }
5650
+ ) : null
5651
+ ]
5652
+ },
5653
+ item.packageId || item.packageSlug
5654
+ );
5655
+ }) }),
5656
+ checkoutNotice ? /* @__PURE__ */ jsx14("div", { className: "mx-paywall-card-summary", children: checkoutNotice }) : null,
5657
+ checkoutError ? /* @__PURE__ */ jsx14("div", { className: "mx-payment-lock-error", children: checkoutError }) : null
5658
+ ] }) : null;
5659
+ if (plansOnlyMode) {
5660
+ return /* @__PURE__ */ jsxs10("div", { className: `mx-detail-container ${isEmbedded ? "is-embedded" : ""}`, children: [
5661
+ error && /* @__PURE__ */ jsx14("div", { className: "mx-detail-error", children: error }),
5662
+ busy && !data ? /* @__PURE__ */ jsx14("div", { className: "mx-sidebar-card mx-detail-empty", "aria-busy": "true", children: t("common.loading", void 0, "Loading...") }) : /* @__PURE__ */ jsxs10("div", { className: `mx-plans-route ${widgetHostedPlansMode ? "is-widget-hosted" : ""}`, children: [
5663
+ !widgetHostedPlansMode ? /* @__PURE__ */ jsxs10("div", { className: "mx-plans-route-header", children: [
5664
+ /* @__PURE__ */ jsx14("div", { className: "mx-plans-route-title", children: t("xapp.plan_options_title", void 0, "Plans") }),
5665
+ /* @__PURE__ */ jsx14("div", { className: "mx-plans-route-subtitle", children: title ? t(
5666
+ "xapp.plans_route_subtitle",
5667
+ { title },
5668
+ `Current access and published plans for ${title}.`
5669
+ ) : t(
5670
+ "xapp.plans_route_subtitle_default",
5671
+ void 0,
5672
+ "Current access and published plans for this app."
5673
+ ) })
5674
+ ] }) : null,
5675
+ /* @__PURE__ */ jsx14("div", { className: "mx-plans-route-grid", children: /* @__PURE__ */ jsxs10("div", { className: "mx-plans-route-main", children: [
5676
+ currentAccessCard,
5677
+ plansCard || /* @__PURE__ */ jsx14("div", { className: "mx-sidebar-card mx-detail-empty", children: t(
5678
+ "xapp.no_plans_available",
5679
+ void 0,
5680
+ "No published plans are currently available."
5681
+ ) })
5682
+ ] }) })
5683
+ ] })
5684
+ ] });
5685
+ }
4702
5686
  return /* @__PURE__ */ jsxs10("div", { className: `mx-detail-container ${isEmbedded ? "is-embedded" : ""}`, children: [
4703
5687
  /* @__PURE__ */ jsxs10("div", { className: "mx-detail-topbar", children: [
4704
5688
  /* @__PURE__ */ jsxs10("div", { className: "mx-detail-topbar-left", children: [
@@ -4778,15 +5762,15 @@ function XappDetailPage() {
4778
5762
  description ? /* @__PURE__ */ jsx14("p", { className: "mx-detail-subtitle", children: description }) : null,
4779
5763
  /* @__PURE__ */ jsxs10("div", { className: "mx-detail-meta-row", children: [
4780
5764
  /* @__PURE__ */ jsx14("div", { className: "mx-card-slug", children: xappId }),
4781
- readString4(versionRecord?.version) && /* @__PURE__ */ jsxs10("div", { className: "mx-card-version mx-detail-version-pill", children: [
5765
+ readString5(versionRecord?.version) && /* @__PURE__ */ jsxs10("div", { className: "mx-card-version mx-detail-version-pill", children: [
4782
5766
  "v",
4783
- readString4(versionRecord?.version)
5767
+ readString5(versionRecord?.version)
4784
5768
  ] }),
4785
5769
  updateAvailable ? /* @__PURE__ */ jsx14("div", { className: "mx-detail-update-pill", children: t("xapp.update_available", void 0, "Update available") }) : null
4786
5770
  ] })
4787
5771
  ] }),
4788
- /* @__PURE__ */ jsx14("div", { className: "mx-detail-actions", children: canMutate ? /* @__PURE__ */ jsxs10(Fragment8, { children: [
4789
- installation && updateAvailable && host.requestUpdate ? /* @__PURE__ */ jsx14(
5772
+ /* @__PURE__ */ jsx14("div", { className: "mx-detail-actions", children: canMutate && mutationControlsReady ? /* @__PURE__ */ jsxs10(Fragment8, { children: [
5773
+ installation && updateAvailable && host.requestUpdate && installationPolicy?.update_mode !== "auto_update_compatible" ? /* @__PURE__ */ jsx14(
4790
5774
  "button",
4791
5775
  {
4792
5776
  className: "mx-btn mx-btn-primary",
@@ -4795,14 +5779,13 @@ function XappDetailPage() {
4795
5779
  if (requiresTerms) {
4796
5780
  setTermsAccepted(false);
4797
5781
  setTermsAction("update");
5782
+ setTermsWidgetId(null);
5783
+ setTermsWidgetName(null);
4798
5784
  setTermsOpen(true);
4799
5785
  return;
4800
5786
  }
4801
5787
  if (!installation.installationId) return;
4802
- host.requestUpdate?.({
4803
- installationId: installation.installationId,
4804
- xappId: String(xappId ?? "")
4805
- });
5788
+ requestUpdateForWidget(null);
4806
5789
  },
4807
5790
  children: updateAppLabel
4808
5791
  }
@@ -4816,18 +5799,23 @@ function XappDetailPage() {
4816
5799
  if (requiresTerms) {
4817
5800
  setTermsAccepted(false);
4818
5801
  setTermsAction("install");
5802
+ setTermsWidgetId(readString5(defaultWidget?.id) || null);
5803
+ setTermsWidgetName(
5804
+ resolveMarketplaceText(defaultWidget?.title, locale) || null
5805
+ );
4819
5806
  setTermsOpen(true);
4820
5807
  return;
4821
5808
  }
4822
- host.requestInstall({
4823
- xappId: String(xappId ?? ""),
4824
- defaultWidgetId: readString4(defaultWidget?.id) || null,
4825
- subjectId: host.subjectId ?? null
4826
- });
5809
+ requestInstallForWidget(
5810
+ readString5(defaultWidget?.id) || null,
5811
+ resolveMarketplaceText(defaultWidget?.title, locale) || null,
5812
+ false,
5813
+ autoAvailableMode
5814
+ );
4827
5815
  },
4828
- children: addAppLabel
5816
+ children: autoAvailableMode ? openAppLabel : addAppLabel
4829
5817
  }
4830
- ) : /* @__PURE__ */ jsx14(
5818
+ ) : !autoAvailableMode ? /* @__PURE__ */ jsx14(
4831
5819
  "button",
4832
5820
  {
4833
5821
  className: "mx-btn mx-btn-outline",
@@ -4839,7 +5827,7 @@ function XappDetailPage() {
4839
5827
  },
4840
5828
  children: removeAppLabel
4841
5829
  }
4842
- )
5830
+ ) : null
4843
5831
  ] }) : null })
4844
5832
  ] }),
4845
5833
  /* @__PURE__ */ jsxs10("div", { className: "mx-detail-grid", children: [
@@ -4850,11 +5838,19 @@ function XappDetailPage() {
4850
5838
  ] }),
4851
5839
  /* @__PURE__ */ jsxs10("section", { className: "mx-detail-section mx-detail-section-widgets", children: [
4852
5840
  /* @__PURE__ */ jsx14("h2", { className: "mx-section-title", children: t("xapp.available_views_title", void 0, "Available Views") }),
4853
- !widgetsEnabled ? /* @__PURE__ */ jsx14("div", { className: "mx-sidebar-card mx-detail-empty", children: !installation ? t(
5841
+ !widgetsEnabled ? /* @__PURE__ */ jsx14("div", { className: "mx-sidebar-card mx-detail-empty", children: !installation ? autoAvailableMode ? t(
5842
+ "xapp.open_to_access_views",
5843
+ void 0,
5844
+ "Open this app to start and install it automatically for your workspace."
5845
+ ) : t(
4854
5846
  "xapp.add_to_access_views",
4855
5847
  void 0,
4856
5848
  "Add this app to your workspace to access its available views."
4857
- ) : updateAvailable ? t(
5849
+ ) : updateAvailable ? autoUpdateMode ? t(
5850
+ "xapp.update_to_access_views_auto",
5851
+ void 0,
5852
+ "Open a view to update the app automatically for your workspace."
5853
+ ) : t(
4858
5854
  "xapp.update_to_access_views",
4859
5855
  void 0,
4860
5856
  "An update is available. Please update the app to access its available views."
@@ -4867,9 +5863,9 @@ function XappDetailPage() {
4867
5863
  void 0,
4868
5864
  "No app views are currently available."
4869
5865
  ) }) : /* @__PURE__ */ jsx14("div", { className: "mx-detail-widget-list", children: widgets.map((w, idx) => {
4870
- const name = resolveMarketplaceText(w.title, locale) || readString4(w.widget_name) || readString4(w.name) || readString4(w.id) || t("common.widget", void 0, "Widget");
5866
+ const name = resolveMarketplaceText(w.title, locale) || readString5(w.widget_name) || readString5(w.name) || readString5(w.id) || t("common.widget", void 0, "Widget");
4871
5867
  const disabled = !widgetsEnabled;
4872
- const isDefault = readBoolean(w.default, false) || idx === 0 && !widgets.some((ww) => readBoolean(ww.default, false));
5868
+ const isDefault = readBoolean2(w.default, false) || idx === 0 && !widgets.some((ww) => readBoolean2(ww.default, false));
4873
5869
  const widgetType = String(w.type || "").toLowerCase();
4874
5870
  return /* @__PURE__ */ jsxs10(
4875
5871
  "button",
@@ -4877,24 +5873,10 @@ function XappDetailPage() {
4877
5873
  disabled,
4878
5874
  className: `mx-detail-widget-card ${disabled ? "is-disabled" : ""} ${isDefault ? "is-default" : ""}`,
4879
5875
  onClick: () => {
4880
- if (!installation || !widgetsEnabled) return;
4881
- const widgetId = readString4(w.id);
5876
+ if (!widgetsEnabled) return;
5877
+ const widgetId = readString5(w.id);
4882
5878
  if (!widgetId) return;
4883
- if (env?.embedMode && installation) {
4884
- navigate({
4885
- pathname: isEmbedded ? `/widget/${encodeURIComponent(installation.installationId)}/${encodeURIComponent(widgetId)}` : `/marketplace/widget/${encodeURIComponent(installation.installationId)}/${encodeURIComponent(widgetId)}`,
4886
- search: tokenSearch
4887
- });
4888
- return;
4889
- }
4890
- host.openWidget({
4891
- installationId: installation.installationId,
4892
- widgetId,
4893
- xappId: String(xappId ?? ""),
4894
- xappTitle: String(title),
4895
- widgetName: name,
4896
- toolName: readString4(w.bind_tool_name)
4897
- });
5879
+ launchWidgetForSubject(widgetId, name, readString5(w.bind_tool_name));
4898
5880
  },
4899
5881
  children: [
4900
5882
  /* @__PURE__ */ jsx14("div", { className: "mx-detail-widget-icon", children: widgetType === "read" ? /* @__PURE__ */ jsxs10(
@@ -4929,7 +5911,7 @@ function XappDetailPage() {
4929
5911
  ) }),
4930
5912
  /* @__PURE__ */ jsxs10("div", { className: "mx-detail-widget-body", children: [
4931
5913
  /* @__PURE__ */ jsx14("div", { className: "mx-detail-widget-name", children: name }),
4932
- readString4(w.bind_tool_name) && /* @__PURE__ */ jsx14("div", { className: "mx-detail-widget-tool", children: readString4(w.bind_tool_name) }),
5914
+ readString5(w.bind_tool_name) && /* @__PURE__ */ jsx14("div", { className: "mx-detail-widget-tool", children: readString5(w.bind_tool_name) }),
4933
5915
  /* @__PURE__ */ jsxs10("div", { className: "mx-detail-widget-badges", children: [
4934
5916
  isDefault && /* @__PURE__ */ jsx14("span", { className: "mx-detail-widget-badge is-primary", children: t("xapp.widget_primary", void 0, "Primary") }),
4935
5917
  widgetType && /* @__PURE__ */ jsx14("span", { className: "mx-detail-widget-badge", children: widgetType })
@@ -5062,7 +6044,14 @@ function XappDetailPage() {
5062
6044
  children: [
5063
6045
  /* @__PURE__ */ jsxs10("div", { className: "mx-guard-row-left", children: [
5064
6046
  guard.order !== null ? /* @__PURE__ */ jsx14("span", { className: "mx-guard-row-order", children: guard.order }) : /* @__PURE__ */ jsx14("span", { className: "mx-guard-row-order", children: guardIdx + 1 }),
5065
- /* @__PURE__ */ jsx14("span", { className: "mx-guard-row-name", title: guard.slug, children: humanizeSlug(guard.slug) })
6047
+ /* @__PURE__ */ jsx14(
6048
+ "span",
6049
+ {
6050
+ className: "mx-guard-row-name",
6051
+ title: guard.label || guard.slug,
6052
+ children: guard.label ? guard.label : humanizeSlug(guard.slug)
6053
+ }
6054
+ )
5066
6055
  ] }),
5067
6056
  /* @__PURE__ */ jsxs10("div", { className: "mx-guard-row-right", children: [
5068
6057
  /* @__PURE__ */ jsxs10(
@@ -5145,49 +6134,7 @@ function XappDetailPage() {
5145
6134
  ] })
5146
6135
  ] }),
5147
6136
  /* @__PURE__ */ jsxs10("aside", { className: "mx-detail-sidebar", children: [
5148
- hasMonetizationState ? /* @__PURE__ */ jsxs10("div", { className: "mx-sidebar-card", children: [
5149
- /* @__PURE__ */ jsx14("h3", { className: "mx-section-title mx-detail-sidebar-title", children: t("xapp.current_access_title", void 0, "Current Access") }),
5150
- currentTier ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5151
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.current_plan_label", void 0, "Current plan") }),
5152
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: currentTier })
5153
- ] }) : null,
5154
- accessState ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5155
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.access_state_label", void 0, "Access state") }),
5156
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: accessState })
5157
- ] }) : null,
5158
- subscriptionStatus ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5159
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_status_label", void 0, "Subscription status") }),
5160
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: subscriptionStatus })
5161
- ] }) : null,
5162
- subscriptionCoverage ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5163
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_coverage_label", void 0, "Coverage") }),
5164
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: subscriptionCoverage })
5165
- ] }) : null,
5166
- subscriptionReason ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item mx-meta-item-top", children: [
5167
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_reason_label", void 0, "Status reason") }),
5168
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: subscriptionReason })
5169
- ] }) : null,
5170
- overdueSince ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5171
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_overdue_since_label", void 0, "Overdue since") }),
5172
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: overdueSince })
5173
- ] }) : null,
5174
- expiryBoundaryAt ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5175
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.subscription_expiry_boundary_label", void 0, "Expiry boundary") }),
5176
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: expiryBoundaryAt })
5177
- ] }) : null,
5178
- renewsAt ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5179
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.renews_at_label", void 0, "Renews at") }),
5180
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: renewsAt })
5181
- ] }) : null,
5182
- expiresAt ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5183
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.expires_at_label", void 0, "Expires at") }),
5184
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: expiresAt })
5185
- ] }) : null,
5186
- creditsRemaining ? /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
5187
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-label", children: t("xapp.credits_remaining_label", void 0, "Credits remaining") }),
5188
- /* @__PURE__ */ jsx14("span", { className: "mx-meta-value", children: creditsRemaining })
5189
- ] }) : null
5190
- ] }) : null,
6137
+ currentAccessCard,
5191
6138
  usageCreditSummary ? /* @__PURE__ */ jsxs10("div", { className: "mx-sidebar-card", children: [
5192
6139
  /* @__PURE__ */ jsx14("h3", { className: "mx-section-title mx-detail-sidebar-title", children: t("xapp.usage_credits_title", void 0, "Usage Credits") }),
5193
6140
  /* @__PURE__ */ jsxs10("div", { className: "mx-meta-item", children: [
@@ -5248,26 +6195,19 @@ function XappDetailPage() {
5248
6195
  ] }, tool.tool_name)) })
5249
6196
  ] }) : null
5250
6197
  ] }) : null,
5251
- installation && defaultWidget && hasSubject && /* @__PURE__ */ jsx14(
6198
+ plansCard,
6199
+ defaultWidget && hasSubject && (installation || autoAvailableMode) && /* @__PURE__ */ jsx14(
5252
6200
  "button",
5253
6201
  {
5254
6202
  className: "mx-btn mx-btn-primary mx-detail-primary-cta",
5255
6203
  onClick: () => {
5256
- if (env?.embedMode) {
5257
- navigate({
5258
- pathname: isEmbedded ? `/widget/${encodeURIComponent(installation.installationId)}/${encodeURIComponent(String(defaultWidget.id))}` : `/marketplace/widget/${encodeURIComponent(installation.installationId)}/${encodeURIComponent(String(defaultWidget.id))}`,
5259
- search: tokenSearch
5260
- });
5261
- return;
5262
- }
5263
- host.openWidget({
5264
- installationId: installation.installationId,
5265
- widgetId: readString4(defaultWidget.id),
5266
- xappId: String(xappId ?? ""),
5267
- xappTitle: String(title),
5268
- widgetName: resolveMarketplaceText(defaultWidget?.title, locale) || readString4(defaultWidget?.widget_name) || readString4(defaultWidget?.name) || t("common.widget", void 0, "Widget"),
5269
- toolName: readString4(defaultWidget?.bind_tool_name)
5270
- });
6204
+ const widgetId = readString5(defaultWidget.id);
6205
+ if (!widgetId) return;
6206
+ launchWidgetForSubject(
6207
+ widgetId,
6208
+ resolveMarketplaceText(defaultWidget?.title, locale) || readString5(defaultWidget?.widget_name) || readString5(defaultWidget?.name) || t("common.widget", void 0, "Widget"),
6209
+ readString5(defaultWidget?.bind_tool_name)
6210
+ );
5271
6211
  },
5272
6212
  children: openAppLabel
5273
6213
  }
@@ -5448,21 +6388,35 @@ function XappDetailPage() {
5448
6388
  ] })
5449
6389
  ] }),
5450
6390
  /* @__PURE__ */ jsxs10("div", { className: "mx-detail-modal-actions", children: [
5451
- /* @__PURE__ */ jsx14("button", { className: "mx-btn mx-btn-outline", onClick: () => setTermsOpen(false), children: t("common.cancel", void 0, "Cancel") }),
6391
+ /* @__PURE__ */ jsx14(
6392
+ "button",
6393
+ {
6394
+ className: "mx-btn mx-btn-outline",
6395
+ onClick: () => {
6396
+ setTermsOpen(false);
6397
+ setTermsAction("none");
6398
+ setTermsWidgetId(null);
6399
+ setTermsWidgetName(null);
6400
+ },
6401
+ children: t("common.cancel", void 0, "Cancel")
6402
+ }
6403
+ ),
5452
6404
  termsAction === "install" ? /* @__PURE__ */ jsx14(
5453
6405
  "button",
5454
6406
  {
5455
6407
  className: "mx-btn mx-btn-primary",
5456
6408
  disabled: !termsAccepted,
5457
6409
  onClick: () => {
5458
- host.requestInstall({
5459
- xappId: String(xappId ?? ""),
5460
- defaultWidgetId: readString4(defaultWidget?.id) || null,
5461
- subjectId: host.subjectId ?? null,
5462
- termsAccepted: true
5463
- });
6410
+ requestInstallForWidget(
6411
+ termsWidgetId ?? readString5(defaultWidget?.id) ?? null,
6412
+ termsWidgetName || resolveMarketplaceText(defaultWidget?.title, locale) || null,
6413
+ true,
6414
+ autoAvailableMode
6415
+ );
5464
6416
  setTermsOpen(false);
5465
6417
  setTermsAction("none");
6418
+ setTermsWidgetId(null);
6419
+ setTermsWidgetName(null);
5466
6420
  },
5467
6421
  children: t("xapp.accept_add_app", void 0, "Accept & Add app")
5468
6422
  }
@@ -5473,13 +6427,11 @@ function XappDetailPage() {
5473
6427
  disabled: !termsAccepted || !installation,
5474
6428
  onClick: () => {
5475
6429
  if (!installation) return;
5476
- host.requestUpdate?.({
5477
- installationId: installation.installationId,
5478
- xappId: String(xappId ?? ""),
5479
- termsAccepted: true
5480
- });
6430
+ requestUpdateForWidget(termsWidgetId, true);
5481
6431
  setTermsOpen(false);
5482
6432
  setTermsAction("none");
6433
+ setTermsWidgetId(null);
6434
+ setTermsWidgetName(null);
5483
6435
  },
5484
6436
  children: t("xapp.accept_update", void 0, "Accept & Update")
5485
6437
  }
@@ -6741,6 +7693,7 @@ function MarketplaceApp() {
6741
7693
  /* @__PURE__ */ jsx18(Route, { path: "publishers", element: /* @__PURE__ */ jsx18(PublishersPage, {}) }),
6742
7694
  /* @__PURE__ */ jsx18(Route, { path: "publishers/:publisherSlug", element: /* @__PURE__ */ jsx18(PublisherDetailPage, {}) }),
6743
7695
  /* @__PURE__ */ jsx18(Route, { path: "xapps/:xappId", element: /* @__PURE__ */ jsx18(XappDetailPage, {}) }),
7696
+ /* @__PURE__ */ jsx18(Route, { path: "xapps/:xappId/plans", element: /* @__PURE__ */ jsx18(XappPlansPage, {}) }),
6744
7697
  /* @__PURE__ */ jsx18(Route, { path: "requests", element: /* @__PURE__ */ jsx18(RequestsPage, {}) }),
6745
7698
  /* @__PURE__ */ jsx18(Route, { path: "payments", element: /* @__PURE__ */ jsx18(PaymentsPage, {}) }),
6746
7699
  /* @__PURE__ */ jsx18(Route, { path: "invoices", element: /* @__PURE__ */ jsx18(InvoicesPage, {}) }),
@@ -6761,6 +7714,7 @@ export {
6761
7714
  RequestsPage,
6762
7715
  WidgetView,
6763
7716
  XappDetailPage,
7717
+ XappPlansPage,
6764
7718
  resolveMarketplaceText,
6765
7719
  resolvePaymentLockStateFromGuardSummary,
6766
7720
  useMarketplace,