@wix/himalaya-cli 0.1303.0 → 0.1304.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -16589,7 +16589,6 @@ function resolveIdiom(tokens2, ctx = {}) {
16589
16589
  var IDIOM_CONTEXTS, UNSAFE_KEYS;
16590
16590
  var init_idiom = __esm({
16591
16591
  "../../core/ts/src/idiom.ts"() {
16592
- "use strict";
16593
16592
  IDIOM_CONTEXTS = {
16594
16593
  ios: { native: ["ios"] },
16595
16594
  android: { native: ["material"] },
@@ -25183,7 +25182,7 @@ function serializeAt(node, src, open) {
25183
25182
  const indent = lineIndent(src, open);
25184
25183
  return JSON.stringify(node, null, 2).split("\n").map((line, i2) => i2 === 0 ? line : indent + line).join("\n");
25185
25184
  }
25186
- function patchGrids(src, installed, patch, keepFaq = false, dropFaq = false) {
25185
+ function patchGrids(src, installed, patch, keepFaq = false, dropFaq = false, dropGalleries = false) {
25187
25186
  const OPEN = /"id": "([A-Za-z_0-9]+(?:\.[A-Za-z_0-9]+)*\.verticals)"/g;
25188
25187
  let out2 = "";
25189
25188
  let cursor = 0;
@@ -25237,9 +25236,10 @@ function patchGrids(src, installed, patch, keepFaq = false, dropFaq = false) {
25237
25236
  faqCell = cloneCell(shape, prefix, "faq", FAQ_CARD);
25238
25237
  }
25239
25238
  if (!installed.size) {
25240
- if (dropFaq) {
25241
- const kept = items.filter((item) => claimOf(item) !== "faq");
25242
- if (kept.length !== items.length && kept.length > 0) {
25239
+ if (dropFaq || dropGalleries) {
25240
+ const kept = items.filter((item) => !(dropFaq && claimOf(item) === "faq") && !(dropGalleries && claimOf(item) === "galleries"));
25241
+ if (keepFaq && faqCell && !kept.some((item) => claimOf(item) === "faq") && kept.length < EXPLORE_MAX_CELLS) kept.push(faqCell);
25242
+ if (kept.length !== items.length && (kept.length > 0 || dropGalleries)) {
25243
25243
  node.body.props.items = kept;
25244
25244
  out2 += src.slice(cursor, open) + serializeAt(node, src, open);
25245
25245
  cursor = close;
@@ -25396,7 +25396,9 @@ function patchHomeForVerticals(src, verticals, homeActionId, opts = {}) {
25396
25396
  const installed = new Set(verticals.filter(isKnown));
25397
25397
  const patch = { src, cells: [], added: [], droppedModules: [], droppedCtas: [], unreachable: [] };
25398
25398
  if (!installed.size) {
25399
- if (opts.keepFaq || opts.dropFaq) patch.src = patchGrids(src, installed, patch, !!opts.keepFaq, !opts.keepFaq && !!opts.dropFaq);
25399
+ if (opts.keepFaq || opts.dropFaq || opts.dropGalleries) {
25400
+ patch.src = patchGrids(src, installed, patch, !!opts.keepFaq, !opts.keepFaq && !!opts.dropFaq, !!opts.dropGalleries);
25401
+ }
25400
25402
  return patch;
25401
25403
  }
25402
25404
  const ctas = patchStoreCtas(patchModules(patchGrids(src, installed, patch, opts.keepFaq), installed, patch), [...installed], homeActionId);
@@ -162746,6 +162748,7 @@ async function writeBusinessBook(result, opts) {
162746
162748
  screenshotUrl: p2.screenshotUrl,
162747
162749
  headings: p2.headings,
162748
162750
  heroCopy: p2.heroCopy,
162751
+ galleryImageUrls: p2.galleryImageUrls,
162749
162752
  sections: p2.sections,
162750
162753
  ctaLabels: p2.ctaLabels,
162751
162754
  faq: p2.faq,
@@ -166893,16 +166896,21 @@ async function createPlaywrightBrowser(load3 = () => import("playwright"), siteF
166893
166896
  let mainPageId;
166894
166897
  let seededFromMap = false;
166895
166898
  let sitePages = [];
166899
+ let skippedPage = false;
166896
166900
  while (queue.length && pages.length < budget) {
166897
166901
  const url = queue.shift();
166898
166902
  if (seen.has(url)) continue;
166899
166903
  seen.add(url);
166900
- if (!await navigated(page, url, url === siteUrl)) continue;
166904
+ if (!await navigated(page, url, url === siteUrl)) {
166905
+ skippedPage = true;
166906
+ continue;
166907
+ }
166901
166908
  const title = await page.title();
166902
166909
  let evaled;
166903
166910
  try {
166904
166911
  evaled = await page.evaluate(PAGE_EVAL);
166905
166912
  } catch {
166913
+ skippedPage = true;
166906
166914
  continue;
166907
166915
  }
166908
166916
  let pageId2 = evaled.velo?.pageId || void 0;
@@ -166942,6 +166950,7 @@ async function createPlaywrightBrowser(load3 = () => import("playwright"), siteF
166942
166950
  headings: evaled.headings,
166943
166951
  heroCopy: evaled.hero || void 0,
166944
166952
  imageUrls: evaled.imgs,
166953
+ ...evaled.galleryImgs?.length ? { galleryImageUrls: evaled.galleryImgs } : {},
166945
166954
  wst: evaled.wst,
166946
166955
  layout: {
166947
166956
  hero: !!evaled.hero,
@@ -166980,6 +166989,7 @@ async function createPlaywrightBrowser(load3 = () => import("playwright"), siteF
166980
166989
  const veloHint = mergeVeloHints(veloHints);
166981
166990
  return {
166982
166991
  pages,
166992
+ complete: !skippedPage && pages.length > 0 && pages.length < budget && !queue.some((url) => !seen.has(url)) && (!sitePages.length || sitePages.length <= pages.length),
166983
166993
  navLabels: navAll,
166984
166994
  ...navLinksAll.length ? { navLinks: navLinksAll } : {},
166985
166995
  theme: Object.keys(wst).length ? {
@@ -167144,6 +167154,12 @@ var init_crawl_look = __esm({
167144
167154
  ogDescription: attrOf('meta[property="og:description"]'),
167145
167155
  };
167146
167156
  const imgs = Array.from(document.querySelectorAll("img[src]")).map((i) => i.src).filter((s) => /^https?:/.test(s)).slice(0, 16);
167157
+ // A Gallery page can also contain its logo, header art and footer images. Only media
167158
+ // inside a gallery widget is safe to carry into a customer app as gallery content.
167159
+ const galleryImgs = Array.from(document.querySelectorAll(
167160
+ 'img.wixui-gallery__image,.wixui-gallery img,.pro-gallery img,[data-hook*="gallery"] img,[data-testid*="gallery"] img,img[class*="gallery__image"]'
167161
+ )).filter((i) => !i.closest('header,footer,nav,[role="banner"],[role="contentinfo"]'))
167162
+ .map((i) => i.src).filter((s) => /^https?:/.test(s)).slice(0, 100);
167147
167163
  const hrefs = Array.from(document.querySelectorAll("a[href]")).map((a) => a.href);
167148
167164
  const buttons = Array.from(document.querySelectorAll("a,button")).map((el) => (el.textContent || "").trim()).filter(Boolean).slice(0, 40);
167149
167165
  const html = document.documentElement.outerHTML.slice(0, 80000);
@@ -167199,7 +167215,7 @@ var init_crawl_look = __esm({
167199
167215
  if (pid) velo.pageId = pid[1];
167200
167216
  }
167201
167217
  } catch (e4) {}
167202
- return { wst, nav, navLinks, headings, hero, imgs, hrefs, buttons, html, htmlLang, velo, sections, ctaLabels, faq, testimonials, about, meta };
167218
+ return { wst, nav, navLinks, headings, hero, imgs, galleryImgs, hrefs, buttons, html, htmlLang, velo, sections, ctaLabels, faq, testimonials, about, meta };
167203
167219
  })()`;
167204
167220
  CAP2 = {
167205
167221
  heading: 120,
@@ -167601,7 +167617,37 @@ function deriveStaticFaq(faq) {
167601
167617
  }
167602
167618
  return out2;
167603
167619
  }
167604
- var clean, ID_MAX, NAME_MAX, TYPE_MAX, CONTROL_CHARS2, HAS_CONTROL_CHAR, plain, shown, STATIC_FAQ_LIMITS, oneLine;
167620
+ function deriveStaticGalleries(pages) {
167621
+ if (!Array.isArray(pages)) return [];
167622
+ const galleries = [];
167623
+ const seenPhotoSets = /* @__PURE__ */ new Set();
167624
+ let totalPhotos = 0;
167625
+ for (const raw of pages) {
167626
+ if (galleries.length >= STATIC_GALLERY_LIMITS.galleries || totalPhotos >= STATIC_GALLERY_LIMITS.totalPhotos) break;
167627
+ const page = raw && typeof raw === "object" ? raw : {};
167628
+ if (!Array.isArray(page.galleryImageUrls)) continue;
167629
+ const photos = [];
167630
+ for (const image of page.galleryImageUrls) {
167631
+ if (photos.length >= STATIC_GALLERY_LIMITS.photos || totalPhotos + photos.length >= STATIC_GALLERY_LIMITS.totalPhotos) break;
167632
+ if (typeof image !== "string") continue;
167633
+ const match = /^https:\/\/static\.wixstatic\.com\/media\/([A-Za-z0-9_~.-]+)(?:\/.*)?$/.exec(image);
167634
+ if (!match) continue;
167635
+ const url = `https://static.wixstatic.com/media/${match[1]}`;
167636
+ if (!photos.includes(url)) photos.push(url);
167637
+ }
167638
+ if (!photos.length) continue;
167639
+ const photoSet = photos.join("\n");
167640
+ if (seenPhotoSets.has(photoSet)) continue;
167641
+ seenPhotoSets.add(photoSet);
167642
+ totalPhotos += photos.length;
167643
+ const heading = Array.isArray(page.headings) ? page.headings.find((h2) => typeof h2 === "string" && !!h2.trim()) : void 0;
167644
+ const pageTitle = typeof page.title === "string" ? page.title.split(/\s+[|–—]\s+/)[0] : "";
167645
+ const title = bounded(oneLine(page.role === "gallery" ? pageTitle || heading || "Gallery" : "Gallery"), STATIC_GALLERY_LIMITS.title);
167646
+ galleries.push({ id: `site-gallery-${galleries.length + 1}`, title, photos });
167647
+ }
167648
+ return galleries;
167649
+ }
167650
+ var clean, ID_MAX, NAME_MAX, TYPE_MAX, CONTROL_CHARS2, HAS_CONTROL_CHAR, plain, shown, STATIC_FAQ_LIMITS, oneLine, STATIC_GALLERY_LIMITS;
167605
167651
  var init_derive = __esm({
167606
167652
  "src/site-analyze/derive.ts"() {
167607
167653
  init_types2();
@@ -167615,6 +167661,7 @@ var init_derive = __esm({
167615
167661
  shown = (value) => (typeof value === "string" ? JSON.stringify(value) : String(value)).slice(0, 64);
167616
167662
  STATIC_FAQ_LIMITS = { pairs: 20, question: 200, answer: 1500 };
167617
167663
  oneLine = (s2) => typeof s2 === "string" ? s2.replace(/[\u0000-\u001F\u007F\s]+/g, " ").trim() : "";
167664
+ STATIC_GALLERY_LIMITS = { galleries: 10, photos: 50, totalPhotos: 50, title: 120 };
167618
167665
  }
167619
167666
  });
167620
167667
 
@@ -168584,6 +168631,7 @@ function mergeCustomerOwned(committed, overlay) {
168584
168631
  "wix",
168585
168632
  "copy",
168586
168633
  "faq",
168634
+ "galleries",
168587
168635
  "site",
168588
168636
  "goals"
168589
168637
  ];
@@ -168592,6 +168640,9 @@ function mergeCustomerOwned(committed, overlay) {
168592
168640
  const value = overlay[key2];
168593
168641
  if (value !== void 0) customer[key2] = value;
168594
168642
  }
168643
+ if (customer.galleries === void 0 && !sameKnownSite(committed, overlay)) {
168644
+ customer.galleries = [];
168645
+ }
168595
168646
  if (committed.site === void 0) delete customer.site;
168596
168647
  customer.site = keepSiteLayout(committed.site, customer.site);
168597
168648
  const overlayBranding = { ...customer.branding ?? {} };
@@ -168623,6 +168674,10 @@ function siteKeyOf(overlay) {
168623
168674
  const url = overlay.wix?.siteUrl;
168624
168675
  return typeof url === "string" && url.trim() ? `url:${url.trim().replace(/\/+$/, "").toLowerCase()}` : "";
168625
168676
  }
168677
+ function sameKnownSite(previous, next) {
168678
+ const key2 = siteKeyOf(next);
168679
+ return key2 !== "" && key2 === siteKeyOf(previous);
168680
+ }
168626
168681
  function buildMergedContract(options) {
168627
168682
  const { appDir: appDir3, packageDir, overlay } = options;
168628
168683
  const committedPath = join78(appDir3, "provisioning.json");
@@ -168739,6 +168794,21 @@ function staticFaqFor(overlay, overlayPath) {
168739
168794
  return void 0;
168740
168795
  }
168741
168796
  }
168797
+ function staticGalleriesFor(overlay, overlayPath) {
168798
+ if (overlay.galleries !== void 0) return deriveStaticGalleries(
168799
+ overlay.galleries.map((gallery) => ({ role: "gallery", title: gallery.title, galleryImageUrls: gallery.photos }))
168800
+ );
168801
+ const bookPath = join78(dirname42(resolve58(overlayPath)), "book.json");
168802
+ if (!existsSync76(bookPath)) return void 0;
168803
+ try {
168804
+ const book = JSON.parse(readFileSync92(bookPath, "utf8"));
168805
+ if (!book.look || book.look.unavailable || !Array.isArray(book.look.pages)) return void 0;
168806
+ const galleries = deriveStaticGalleries(book.look.pages);
168807
+ return galleries.length || book.look.complete === true ? galleries : void 0;
168808
+ } catch {
168809
+ return void 0;
168810
+ }
168811
+ }
168742
168812
  async function applyOverlays(input) {
168743
168813
  const cwd = resolve58(input.cwd);
168744
168814
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
@@ -168747,6 +168817,11 @@ async function applyOverlays(input) {
168747
168817
  const tokens2 = JSON.parse(readFileSync92(input.tokensPath, "utf8"));
168748
168818
  const faq = staticFaqFor(overlay, input.overlayPath);
168749
168819
  if (faq !== void 0) overlay.faq = faq;
168820
+ const galleries = staticGalleriesFor(overlay, input.overlayPath);
168821
+ if (galleries !== void 0) {
168822
+ overlay.galleries = galleries;
168823
+ if (galleries.length && !overlay.installedVerticals.includes("galleries")) overlay.installedVerticals.push("galleries");
168824
+ }
168750
168825
  const wrote = [];
168751
168826
  const notes = [];
168752
168827
  const repoRoot2 = findRepoRoot(cwd);
@@ -168788,16 +168863,22 @@ async function applyOverlays(input) {
168788
168863
  const pkgProv = join78(cwd, "provisioning.json");
168789
168864
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
168790
168865
  let packageFaq = faq;
168866
+ let packageGalleries = galleries;
168791
168867
  if (existsSync76(pkgProv)) {
168792
168868
  const cur = JSON.parse(readFileSync92(pkgProv, "utf8"));
168869
+ if (galleries === void 0 && !sameKnownSite(cur, overlay)) overlay.galleries = [];
168793
168870
  const merged = deepMerge4(cur, overlay);
168794
168871
  packageFaq = merged.faq;
168872
+ packageGalleries = merged.galleries;
168795
168873
  writeFileSync35(pkgProv, JSON.stringify(merged, null, 2) + "\n");
168796
168874
  wrote.push(pkgProv);
168797
168875
  } else {
168798
168876
  writeFileSync35(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
168799
168877
  wrote.push(pkgProv);
168800
168878
  }
168879
+ if (Array.isArray(packageGalleries) && packageGalleries.length && !overlay.installedVerticals.includes("galleries")) {
168880
+ overlay.installedVerticals.push("galleries");
168881
+ }
168801
168882
  const pkgTokens = join78(cwd, "tokens.json");
168802
168883
  if (existsSync76(pkgTokens) && (tokens2.colors || tokens2.typography)) {
168803
168884
  const cur = JSON.parse(readFileSync92(pkgTokens, "utf8"));
@@ -168835,11 +168916,15 @@ async function applyOverlays(input) {
168835
168916
  "the site's FAQ pairs were saved to provisioning.json, but this package's FAQ screen predates the site-FAQ fallback and will not show them \u2014 re-scaffold from the current template to pick it up"
168836
168917
  );
168837
168918
  }
168919
+ if (!patched.foreign && galleries?.length && prev.includes('"tier5.himiBrandedGalleries.load"') && !prev.includes('"staticGalleries"')) {
168920
+ notes.push("the site's Gallery photos were saved to provisioning.json, but this package predates the page-gallery viewer \u2014 re-scaffold from the current template");
168921
+ }
168838
168922
  const keepFaq = Array.isArray(packageFaq) && packageFaq.length > 0;
168839
168923
  const dropFaq = Array.isArray(faq) && faq.length === 0;
168924
+ const dropGalleries = Array.isArray(packageGalleries) && packageGalleries.length === 0 && !overlay.installedVerticals.includes("galleries");
168840
168925
  const ownsHome = prev.includes(`"actionId": "${homeAction}"`);
168841
- if (ownsHome && !patched.foreign && (overlay.installedVerticals?.length || keepFaq || dropFaq)) {
168842
- const home = patchHomeForVerticals(next, overlay.installedVerticals ?? [], homeAction, { keepFaq, dropFaq });
168926
+ if (ownsHome && !patched.foreign && (overlay.installedVerticals?.length || keepFaq || dropFaq || dropGalleries)) {
168927
+ const home = patchHomeForVerticals(next, overlay.installedVerticals ?? [], homeAction, { keepFaq, dropFaq, dropGalleries });
168843
168928
  next = home.src;
168844
168929
  if (home.droppedCtas.length) {
168845
168930
  notes.push(`removed the home's store call(s) to action (${home.droppedCtas.join(", ")}) \u2014 this site has no store`);
@@ -168886,6 +168971,7 @@ function patchBrandParams(src, overlay, homeActionId) {
168886
168971
  const accent = overlay.branding?.primaryColor;
168887
168972
  const verts = overlay.installedVerticals;
168888
168973
  const faq = overlay.faq;
168974
+ const galleries = overlay.galleries;
168889
168975
  const edits = [
168890
168976
  ...layout ? [{ pattern: /"ownerDefaultLayout": "[^"]*"/g, replacement: `"ownerDefaultLayout": ${JSON.stringify(layout)}` }] : [],
168891
168977
  ...accent ? [{ pattern: /"brandAccent": "[^"]*"/g, replacement: `"brandAccent": ${JSON.stringify(accent)}` }] : [],
@@ -168896,7 +168982,8 @@ function patchBrandParams(src, overlay, homeActionId) {
168896
168982
  // replacement, because the pairs are customer text and may contain `$&`. Patched whenever
168897
168983
  // the analysis SAID something, `[]` included, so a re-apply clears pairs an earlier one
168898
168984
  // baked in rather than leaving the worker another site's FAQ.
168899
- ...faq ? [{ pattern: /"staticFaq": "(?:[^"\\]|\\.)*"/g, replacement: `"staticFaq": ${JSON.stringify(JSON.stringify(faq))}` }] : []
168985
+ ...faq ? [{ pattern: /"staticFaq": "(?:[^"\\]|\\.)*"/g, replacement: `"staticFaq": ${JSON.stringify(JSON.stringify(faq))}` }] : [],
168986
+ ...galleries ? [{ pattern: /"staticGalleries": "(?:[^"\\]|\\.)*"/g, replacement: `"staticGalleries": ${JSON.stringify(JSON.stringify(galleries))}` }] : []
168900
168987
  ];
168901
168988
  if (!edits.length) return { src, applied: 0, unparsed: 0, foreign: false };
168902
168989
  if (!src.includes(`"actionId": "${homeActionId}"`)) {
@@ -860,27 +860,26 @@
860
860
  "total": 2
861
861
  }
862
862
  },
863
- "GET https://www.wix.com/_api/photography-one-app-viewer/galleries": {
864
- "data": {
865
- "galleries": [
866
- {
867
- "id": "gal-0001",
868
- "name": "The Roastery",
869
- "totalItemsCount": 24,
870
- "coverImage": {
871
- "url": "https://static.wixstatic.com/media/example-roastery.jpg"
872
- }
873
- },
874
- {
875
- "id": "gal-0002",
876
- "name": "Harvest 2026",
877
- "totalItemsCount": 48,
878
- "coverImage": {
879
- "url": "https://static.wixstatic.com/media/example-harvest-gallery.jpg"
880
- }
863
+ "GET /progallery/v2/galleries": {
864
+ "totalGalleries": 2,
865
+ "galleries": [
866
+ {
867
+ "id": "gal-0001",
868
+ "name": "The Roastery",
869
+ "totalItems": 24,
870
+ "coverImage": {
871
+ "url": "https://static.wixstatic.com/media/example-roastery.jpg"
881
872
  }
882
- ]
883
- }
873
+ },
874
+ {
875
+ "id": "gal-0002",
876
+ "name": "Harvest 2026",
877
+ "totalItems": 48,
878
+ "coverImage": {
879
+ "url": "https://static.wixstatic.com/media/example-harvest-gallery.jpg"
880
+ }
881
+ }
882
+ ]
884
883
  },
885
884
  "GET /inbox/v2/conversations": {
886
885
  "conversations": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1303.0",
3
+ "version": "0.1304.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -70,5 +70,5 @@
70
70
  "artifactId": "himalaya-cli"
71
71
  }
72
72
  },
73
- "falconPackageHash": "4ef9467b4cf9f72c791b0e318db771b01a5f56cb44ff4f6e46dd0e22"
73
+ "falconPackageHash": "fd1140c9356d5a4d1715c4b39f8804830e3e4f36114d7a006cd4530e"
74
74
  }
@@ -639,9 +639,11 @@ export default defineApp({
639
639
  },
640
640
  {
641
641
  "screenId": "himi_photo_album",
642
- "isPublic": false,
642
+ "isPublic": true,
643
643
  "paramKeys": [
644
- "id"
644
+ "id",
645
+ "url",
646
+ "name"
645
647
  ],
646
648
  "parentScreenId": "himi_gallery",
647
649
  "tabId": ""
@@ -76632,7 +76632,10 @@ const SCREENS = runtimeSetting({
76632
76632
  "bottom": 28
76633
76633
  },
76634
76634
  "onAppear": {
76635
- "actionId": "tier5.himiBrandedGalleries.load"
76635
+ "actionId": "tier5.himiBrandedGalleries.load",
76636
+ "params": {
76637
+ "staticGalleries": "[]"
76638
+ }
76636
76639
  }
76637
76640
  }
76638
76641
  },
@@ -76641,7 +76644,8 @@ const SCREENS = runtimeSetting({
76641
76644
  "integrity": "",
76642
76645
  "contentType": "application/javascript+ts-bundle",
76643
76646
  "declaredCapabilities": [
76644
- "wix_api"
76647
+ "wix_api",
76648
+ "app_info"
76645
76649
  ],
76646
76650
  "minBinaryVersion": "0.1.0",
76647
76651
  "bundleVersion": "0.0.1"
@@ -76662,7 +76666,7 @@ const SCREENS = runtimeSetting({
76662
76666
  "package": "himalaya",
76663
76667
  "type": "VStack",
76664
76668
  "props": {
76665
- "spacing": 12,
76669
+ "spacing": 16,
76666
76670
  "alignment": "leading",
76667
76671
  "children": [
76668
76672
  {
@@ -76671,49 +76675,60 @@ const SCREENS = runtimeSetting({
76671
76675
  "package": "himalaya",
76672
76676
  "type": "VStack",
76673
76677
  "props": {
76674
- "spacing": 8,
76678
+ "spacing": 16,
76675
76679
  "alignment": "leading",
76676
76680
  "children": [
76677
76681
  {
76678
- "id": "himi_gallery.name",
76682
+ "id": "himi_gallery.title",
76679
76683
  "body": {
76680
76684
  "package": "himalaya",
76681
76685
  "type": "Text",
76682
76686
  "props": {
76683
- "text": "$service:himiBrandedSimpleDetail.name",
76687
+ "text": "$service:himiBrandedGalleries.galleryTitle",
76684
76688
  "style": "title1"
76685
76689
  }
76686
76690
  }
76687
76691
  },
76688
76692
  {
76689
- "id": "",
76693
+ "id": "himi_gallery.photos",
76690
76694
  "body": {
76691
76695
  "package": "himalaya",
76692
- "type": "Text",
76693
- "props": {
76694
- "text": "$service:himiBrandedSimpleDetail.subtitle",
76695
- "style": "body",
76696
- "color": "textSecondary",
76697
- "collapseWhenEmpty": true
76698
- }
76699
- }
76700
- },
76701
- {
76702
- "id": "himi_gallery.cta",
76703
- "body": {
76704
- "package": "himalaya",
76705
- "type": "Button",
76696
+ "type": "Grid",
76706
76697
  "props": {
76707
- "title": "Open album",
76708
- "style": "secondary",
76709
- "fullWidth": true,
76710
- "action": {
76711
- "actionId": "himi.open",
76712
- "params": {
76713
- "screenId": "himi_photo_album",
76714
- "id": "$service:himiBrandedSimpleDetail.id"
76698
+ "columns": 2,
76699
+ "columnsMedium": 3,
76700
+ "spacing": 10,
76701
+ "itemsPath": "$service:himiBrandedGalleries.photos",
76702
+ "items": [
76703
+ {
76704
+ "id": "",
76705
+ "body": {
76706
+ "package": "himalaya",
76707
+ "type": "Image",
76708
+ "props": {
76709
+ "url": "$item.url",
76710
+ "fillWidth": true,
76711
+ "height": 180,
76712
+ "contentMode": "fill",
76713
+ "contentDescription": "$item.title"
76714
+ }
76715
+ },
76716
+ "modifiers": {
76717
+ "cornerRadius": 8,
76718
+ "fillMaxWidth": true,
76719
+ "onTap": {
76720
+ "actionId": "himi.open",
76721
+ "params": {
76722
+ "screenId": "himi_photo_album",
76723
+ "id": "$item.id",
76724
+ "url": "$item.url",
76725
+ "name": "$item.title"
76726
+ }
76727
+ },
76728
+ "accessibilityLabel": "$item.title"
76729
+ }
76715
76730
  }
76716
- }
76731
+ ]
76717
76732
  }
76718
76733
  }
76719
76734
  },
@@ -76723,18 +76738,20 @@ const SCREENS = runtimeSetting({
76723
76738
  "package": "himalaya",
76724
76739
  "type": "Text",
76725
76740
  "props": {
76726
- "text": "$service:himiBrandedSimpleDetail.hint",
76727
- "style": "footnote",
76728
- "color": "textTertiary",
76729
- "collapseWhenEmpty": true
76741
+ "text": "Photos added to this album will appear here.",
76742
+ "style": "body",
76743
+ "color": "textSecondary"
76730
76744
  }
76745
+ },
76746
+ "modifiers": {
76747
+ "hiddenPath": "$service:himiBrandedGalleries.hasPhotos"
76731
76748
  }
76732
76749
  }
76733
76750
  ]
76734
76751
  }
76735
76752
  },
76736
76753
  "modifiers": {
76737
- "hiddenPath": "$service:himiBrandedSimpleDetail.noId"
76754
+ "hiddenPath": "$service:himiBrandedGalleries.noEntity"
76738
76755
  }
76739
76756
  },
76740
76757
  {
@@ -76758,7 +76775,7 @@ const SCREENS = runtimeSetting({
76758
76775
  }
76759
76776
  },
76760
76777
  "modifiers": {
76761
- "hiddenPath": "$service:himiBrandedSimpleDetail.hideLoading"
76778
+ "hiddenPath": "$service:himiBrandedGalleries.hideLoading"
76762
76779
  }
76763
76780
  },
76764
76781
  {
@@ -76773,7 +76790,7 @@ const SCREENS = runtimeSetting({
76773
76790
  }
76774
76791
  },
76775
76792
  "modifiers": {
76776
- "hiddenPath": "$service:himiBrandedSimpleDetail.hideLoadError"
76793
+ "hiddenPath": "$service:himiBrandedGalleries.hideLoadError"
76777
76794
  }
76778
76795
  },
76779
76796
  {
@@ -76791,7 +76808,7 @@ const SCREENS = runtimeSetting({
76791
76808
  "package": "himalaya",
76792
76809
  "type": "Icon",
76793
76810
  "props": {
76794
- "name": "search",
76811
+ "name": "photo",
76795
76812
  "size": 32,
76796
76813
  "tint": "textTertiary"
76797
76814
  }
@@ -76862,7 +76879,7 @@ const SCREENS = runtimeSetting({
76862
76879
  "bottom": 20
76863
76880
  },
76864
76881
  "fillMaxWidth": true,
76865
- "hiddenPath": "$service:himiBrandedSimpleDetail.hideNotFound"
76882
+ "hiddenPath": "$service:himiBrandedGalleries.hideNotFound"
76866
76883
  }
76867
76884
  }
76868
76885
  ]
@@ -76883,15 +76900,21 @@ const SCREENS = runtimeSetting({
76883
76900
  "bottom": 28
76884
76901
  },
76885
76902
  "onAppear": {
76886
- "actionId": "tier5.himiBrandedSimpleDetail.load"
76903
+ "actionId": "tier5.himiBrandedGalleries.open",
76904
+ "params": {
76905
+ "staticGalleries": "[]"
76906
+ }
76887
76907
  }
76888
76908
  }
76889
76909
  },
76890
76910
  "logicBundle": {
76891
- "url": "http://localhost:8190/v1/bundles/himi-branded-lite-simple-detail?integrity=",
76911
+ "url": "http://localhost:8190/v1/bundles/himi-branded-lite-galleries?integrity=",
76892
76912
  "integrity": "",
76893
76913
  "contentType": "application/javascript+ts-bundle",
76894
- "declaredCapabilities": [],
76914
+ "declaredCapabilities": [
76915
+ "wix_api",
76916
+ "app_info"
76917
+ ],
76895
76918
  "minBinaryVersion": "0.1.0",
76896
76919
  "bundleVersion": "0.0.1"
76897
76920
  }
@@ -76911,46 +76934,25 @@ const SCREENS = runtimeSetting({
76911
76934
  "package": "himalaya",
76912
76935
  "type": "VStack",
76913
76936
  "props": {
76914
- "spacing": 8,
76937
+ "spacing": 12,
76915
76938
  "alignment": "leading",
76916
76939
  "children": [
76917
76940
  {
76918
76941
  "id": "",
76919
76942
  "body": {
76920
76943
  "package": "himalaya",
76921
- "type": "VStack",
76944
+ "type": "Image",
76922
76945
  "props": {
76923
- "spacing": 8,
76924
- "alignment": "leading",
76925
- "children": [
76926
- {
76927
- "id": "himi_photo_album.title",
76928
- "body": {
76929
- "package": "himalaya",
76930
- "type": "Text",
76931
- "props": {
76932
- "text": "No photos yet",
76933
- "style": "title2"
76934
- }
76935
- }
76936
- },
76937
- {
76938
- "id": "",
76939
- "body": {
76940
- "package": "himalaya",
76941
- "type": "Text",
76942
- "props": {
76943
- "text": "Photos added to this album will appear here.",
76944
- "style": "body",
76945
- "color": "textSecondary"
76946
- }
76947
- }
76948
- }
76949
- ]
76946
+ "url": "$service:himiBrandedGalleries.photoUrl",
76947
+ "fillWidth": true,
76948
+ "height": 500,
76949
+ "contentMode": "fit",
76950
+ "contentDescription": "$service:himiBrandedGalleries.photoTitle"
76950
76951
  }
76951
76952
  },
76952
76953
  "modifiers": {
76953
- "hiddenPath": "$service:spacesPhotoAlbums.noEntity"
76954
+ "fillMaxWidth": true,
76955
+ "hiddenPath": "$service:himiBrandedGalleries.noEntity"
76954
76956
  }
76955
76957
  },
76956
76958
  {
@@ -76974,7 +76976,7 @@ const SCREENS = runtimeSetting({
76974
76976
  }
76975
76977
  },
76976
76978
  "modifiers": {
76977
- "hiddenPath": "$service:spacesPhotoAlbums.hideLoading"
76979
+ "hiddenPath": "$service:himiBrandedGalleries.hideLoading"
76978
76980
  }
76979
76981
  },
76980
76982
  {
@@ -76989,7 +76991,7 @@ const SCREENS = runtimeSetting({
76989
76991
  }
76990
76992
  },
76991
76993
  "modifiers": {
76992
- "hiddenPath": "$service:spacesPhotoAlbums.hideLoadError"
76994
+ "hiddenPath": "$service:himiBrandedGalleries.hideLoadError"
76993
76995
  }
76994
76996
  },
76995
76997
  {
@@ -77078,7 +77080,7 @@ const SCREENS = runtimeSetting({
77078
77080
  "bottom": 20
77079
77081
  },
77080
77082
  "fillMaxWidth": true,
77081
- "hiddenPath": "$service:spacesPhotoAlbums.hideNotFound"
77083
+ "hiddenPath": "$service:himiBrandedGalleries.hideNotFound"
77082
77084
  }
77083
77085
  }
77084
77086
  ]
@@ -77099,15 +77101,18 @@ const SCREENS = runtimeSetting({
77099
77101
  "bottom": 28
77100
77102
  },
77101
77103
  "onAppear": {
77102
- "actionId": "tier5.spacesPhotoAlbums.load"
77104
+ "actionId": "tier5.himiBrandedGalleries.view"
77103
77105
  }
77104
77106
  }
77105
77107
  },
77106
77108
  "logicBundle": {
77107
- "url": "http://localhost:8190/v1/bundles/wix-spaces-photo-albums?integrity=",
77109
+ "url": "http://localhost:8190/v1/bundles/himi-branded-lite-galleries?integrity=",
77108
77110
  "integrity": "",
77109
77111
  "contentType": "application/javascript+ts-bundle",
77110
- "declaredCapabilities": [],
77112
+ "declaredCapabilities": [
77113
+ "wix_api",
77114
+ "app_info"
77115
+ ],
77111
77116
  "minBinaryVersion": "0.1.0",
77112
77117
  "bundleVersion": "0.0.1"
77113
77118
  }
@@ -1,30 +1,32 @@
1
- // himi-branded-lite-galleries — branded-lite "Galleries" worker (Wix Pro Gallery).
1
+ // himi-branded-lite-galleries — branded-lite page galleries and Wix Pro Gallery.
2
2
  //
3
- // Like Videos, Wix Pro Gallery has NO platformized REST @wix/auto_sdk_* domain to
4
- // codegen — the real consumer app (wix-private/wix-one-app-pro-gallery) fetches it
5
- // from the photography one-app viewer REST endpoint. So this worker mirrors that: it
6
- // GETs the site's galleries from that endpoint through the raw wixApi bridge (absolute
7
- // URL + the app's WixApiCapability auth). a failed query is an error with
8
- // Retry (or Sign in), an empty one a real empty state; sample galleries appear only
9
- // in the host's explicit sample preview (_shared/branded-load-state.ts).
3
+ // Published Gallery page photos are carried by site analyze in the descriptor and
4
+ // displayed without a Pro Gallery request. Sites with no page gallery use the Wix
5
+ // API gateway. Failed reads remain errors; sample data is explicit preview only.
10
6
  //
11
- // Endpoint verbatim from wix-one-app-pro-gallery/src/constants (API_BASE_URL) +
12
- // api/photo-gallery-api.js (fetchGalleries).
13
7
  import { defineService } from "../_shared/collection-service.js";
8
+ import { get as getAppInfo } from "@himalaya/app-info";
14
9
  import { LOADING, loaded, loadFailed, type LoadCopy, type LoadFlags } from "../_shared/branded-load-state.js";
10
+ import { presence, type PresenceFlags } from "../_shared/detail-presence.js";
15
11
 
16
- const GALLERIES_URL =
12
+ const GALLERIES_PATH = "/progallery/v2/galleries";
13
+ const LEGACY_GALLERIES_URL =
17
14
  "https://www.wix.com/_api/photography-one-app-viewer/galleries?itemsPerGallery=1&offset=0&limit=30&withHidden=false&useNew=true";
15
+ const PAGE_SIZE = 10; // API maximum. Keep the prior screen's 30-gallery cap.
16
+ const MAX_GALLERIES = 30;
18
17
 
19
18
  type WixApi = { call: (m: string, p: string, b?: unknown, h?: Record<string, string>) => Promise<unknown> };
20
19
  function wixApi(): WixApi | null {
21
20
  return (globalThis as { himalaya?: { wixApi?: WixApi } }).himalaya?.wixApi ?? null;
22
21
  }
23
22
 
24
- interface RawGallery { id?: string; name?: string; totalItemsCount?: number }
25
- interface Row { id: string; title: string; subtitle: string }
26
- interface S extends LoadFlags {
23
+ interface RawGallery { id: string; name?: string; totalItems?: number; totalItemsCount?: number }
24
+ interface StaticGallery { id: string; title: string; photos: string[] }
25
+ interface Row { id: string; title: string; subtitle: string; name: string; description: string; image: string; noCover: boolean }
26
+ interface PhotoRow { id: string; url: string; title: string }
27
+ interface S extends LoadFlags, PresenceFlags {
27
28
  galleries: Row[]; emptyText: string;
29
+ galleryTitle: string; photos: PhotoRow[]; photoUrl: string; photoTitle: string; noGallery: boolean; hasPhotos: boolean; hasPhoto: boolean; noPhoto: boolean;
28
30
  isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
29
31
  * shows only when the site really has none, and the unavailable one only when the
30
32
  * query failed. A failed query must never render the sentence that claims the
@@ -32,17 +34,93 @@ interface S extends LoadFlags {
32
34
  notMockEmpty: boolean; notMockUnavailable: boolean;
33
35
  }
34
36
 
37
+ function input(key: string): string {
38
+ const value = (globalThis as { himalaya?: { input?: Record<string, unknown> } }).himalaya?.input?.[key];
39
+ return typeof value === "string" ? value : "";
40
+ }
41
+
42
+ function parseStaticGalleries(raw: unknown): StaticGallery[] {
43
+ const parsed: unknown = typeof raw === "string" ? JSON.parse(raw) : raw;
44
+ if (parsed == null) return [];
45
+ if (!Array.isArray(parsed)) throw new Error("Malformed imported galleries");
46
+ let remaining = 50;
47
+ return parsed.slice(0, 10).flatMap((item): StaticGallery[] => {
48
+ if (!remaining) return [];
49
+ if (!item || typeof item !== "object") return [];
50
+ const gallery = item as Partial<StaticGallery>;
51
+ if (typeof gallery.id !== "string" || typeof gallery.title !== "string" || !Array.isArray(gallery.photos)) return [];
52
+ const photos = gallery.photos.filter((url): url is string => typeof url === "string" && /^https:\/\/static\.wixstatic\.com\/media\/[A-Za-z0-9_~.-]+$/.test(url)).slice(0, remaining);
53
+ remaining -= photos.length;
54
+ return photos.length ? [{ id: gallery.id, title: gallery.title, photos }] : [];
55
+ });
56
+ }
57
+
58
+ let staticGalleries: StaticGallery[] = [];
59
+
35
60
  function norm(g: RawGallery, i: number): Row {
36
61
  const title = (g.name || "").trim() || `Gallery ${i + 1}`;
37
- const n = g.totalItemsCount ?? 0;
62
+ const n = g.totalItems ?? g.totalItemsCount ?? 0;
38
63
  const subtitle = n ? (n === 1 ? "1 photo" : `${n} photos`) : "";
39
- return { id: g.id || `gallery-${i}`, title, subtitle };
64
+ return { id: g.id, title, subtitle, name: title, description: subtitle, image: "", noCover: true };
65
+ }
66
+
67
+ async function readGalleries(api: WixApi): Promise<RawGallery[]> {
68
+ const all: RawGallery[] = [];
69
+ for (let offset = 0; offset < MAX_GALLERIES; offset += PAGE_SIZE) {
70
+ const res = await api.call("GET", `${GALLERIES_PATH}?itemLimit=1&offset=${offset}&limit=${PAGE_SIZE}`) as unknown;
71
+ const page = (res as { galleries?: unknown } | null)?.galleries;
72
+ if (!Array.isArray(page) || page.some((g) => !g || typeof g.id !== "string" || !g.id)) {
73
+ throw new Error("Malformed Pro Gallery list response");
74
+ }
75
+ all.push(...page as RawGallery[]);
76
+ const total = (res as { totalGalleries?: unknown }).totalGalleries;
77
+ if (page.length < PAGE_SIZE || (typeof total === "number" && all.length >= total)) break;
78
+ }
79
+ return all;
80
+ }
81
+
82
+ async function readLegacyGalleries(api: WixApi): Promise<RawGallery[]> {
83
+ const res = await api.call("GET", LEGACY_GALLERIES_URL) as unknown;
84
+ const raw = Array.isArray(res) ? res :
85
+ (res as { galleries?: unknown; data?: { galleries?: unknown } } | null)?.galleries ??
86
+ (res as { data?: { galleries?: unknown } } | null)?.data?.galleries;
87
+ if (!Array.isArray(raw)) throw new Error("Malformed legacy Pro Gallery list response");
88
+ return raw.map((gallery, index) => {
89
+ if (!gallery || typeof gallery !== "object") throw new Error("Malformed legacy Pro Gallery row");
90
+ const row = gallery as Partial<RawGallery>;
91
+ return { ...row, id: typeof row.id === "string" && row.id ? row.id : `gallery-${index}` };
92
+ });
93
+ }
94
+
95
+ async function readCompatibleGalleries(api: WixApi): Promise<RawGallery[]> {
96
+ // The old absolute host is supported by native Wix transports, but the
97
+ // published web bridge refuses it as webwix_owner_only.
98
+ const nativeLegacyAvailable = async (): Promise<boolean> => {
99
+ const info = await getAppInfo().catch(() => undefined);
100
+ return info?.platform === "ios" || info?.platform === "android" ||
101
+ info?.platform === "macos" || info?.platform === "react-native";
102
+ };
103
+ try {
104
+ const galleries = await readGalleries(api);
105
+ if (galleries.length) return galleries;
106
+ if (!(await nativeLegacyAvailable())) return galleries;
107
+ // A native site's older Pro Gallery app can have content even when the gateway
108
+ // reports none. Keep the gateway's honest empty result if the old route is blocked.
109
+ try {
110
+ const legacy = await readLegacyGalleries(api);
111
+ return legacy.length ? legacy : galleries;
112
+ } catch { return galleries; }
113
+ } catch (gatewayError) {
114
+ if (!(await nativeLegacyAvailable())) throw gatewayError;
115
+ try { return await readLegacyGalleries(api); }
116
+ catch { throw gatewayError; }
117
+ }
40
118
  }
41
119
 
42
120
  const MOCK: Row[] = [
43
- { id: "sample-work", title: "Our Work", subtitle: "24 photos" },
44
- { id: "sample-events", title: "Events", subtitle: "48 photos" },
45
- { id: "sample-team", title: "The Team", subtitle: "9 photos" },
121
+ { id: "sample-work", title: "Our Work", subtitle: "24 photos", name: "Our Work", description: "24 photos", image: "", noCover: true },
122
+ { id: "sample-events", title: "Events", subtitle: "48 photos", name: "Events", description: "48 photos", image: "", noCover: true },
123
+ { id: "sample-team", title: "The Team", subtitle: "9 photos", name: "The Team", description: "9 photos", image: "", noCover: true },
46
124
  ];
47
125
 
48
126
  /** sample rows reach the screen ONLY in the host's explicit sample preview;
@@ -55,27 +133,58 @@ const COPY: LoadCopy<Row> = {
55
133
  sample: () => MOCK.map((r) => ({ ...r })),
56
134
  };
57
135
 
58
- const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedGalleries", {
59
- state: { ...LOADING, galleries: [], emptyText: "" },
136
+ const svc = defineService<S, {
137
+ load: (s: S, arg?: { staticGalleries?: unknown }) => Promise<S>;
138
+ open: (s: S, arg?: { staticGalleries?: unknown }) => S;
139
+ view: (s: S) => S;
140
+ }>("himiBrandedGalleries", {
141
+ state: { ...LOADING, ...presence("loading"), galleries: [], emptyText: "", galleryTitle: "", photos: [], photoUrl: "", photoTitle: "", noGallery: true, hasPhotos: false, hasPhoto: false, noPhoto: true },
60
142
  actions: {
61
- load: async (s) => {
143
+ load: async (s, arg) => {
62
144
  try {
145
+ if (arg && "staticGalleries" in arg) {
146
+ staticGalleries = [];
147
+ staticGalleries = parseStaticGalleries(arg.staticGalleries);
148
+ }
149
+ if (staticGalleries.length) {
150
+ const rows = staticGalleries.map((gallery) => {
151
+ const subtitle = gallery.photos.length === 1 ? "1 photo" : `${gallery.photos.length} photos`;
152
+ return { id: gallery.id, title: gallery.title, subtitle, name: gallery.title, description: subtitle, image: gallery.photos[0], noCover: false };
153
+ });
154
+ return { ...s, ...loaded("galleries", rows, COPY) };
155
+ }
63
156
  const api = wixApi();
64
157
  if (!api) return { ...s, ...loadFailed("galleries", new Error("Wix bridge unavailable"), COPY) };
65
- const res = (await api.call("GET", GALLERIES_URL)) as
66
- { galleries?: RawGallery[] } | RawGallery[] | { data?: { galleries?: RawGallery[] } };
67
- // Accept {galleries:[...]}, a top-level array, or a {data:{galleries}} envelope.
68
- const raw: RawGallery[] =
69
- (res as { galleries?: RawGallery[] })?.galleries ??
70
- (Array.isArray(res) ? (res as RawGallery[]) : undefined) ??
71
- (res as { data?: { galleries?: RawGallery[] } })?.data?.galleries ?? [];
158
+ const raw = await readCompatibleGalleries(api);
72
159
  const galleries = raw.map(norm);
73
160
  return { ...s, ...loaded("galleries", galleries, COPY) };
74
161
  } catch (err) {
75
162
  return { ...s, ...loadFailed("galleries", err, COPY) };
76
163
  }
77
164
  },
165
+ open: (s, arg) => {
166
+ try {
167
+ if (arg && "staticGalleries" in arg) {
168
+ staticGalleries = [];
169
+ staticGalleries = parseStaticGalleries(arg.staticGalleries);
170
+ }
171
+ } catch (err) {
172
+ return { ...s, ...loadFailed("galleries", err, COPY), ...presence("failed"), galleryTitle: "", photos: [],
173
+ noGallery: true, hasPhotos: false, photoUrl: "", photoTitle: "", hasPhoto: false, noPhoto: true };
174
+ }
175
+ const id = input("id");
176
+ const gallery = staticGalleries.find((entry) => entry.id === id);
177
+ const missing = !id || (staticGalleries.length > 0 && !gallery);
178
+ return { ...s, ...loaded("galleries", s.galleries, COPY), ...presence(missing ? "missing" : "found"), galleryTitle: gallery?.title || input("name"),
179
+ photos: (gallery?.photos ?? []).map((url, index) => ({ id: `${id}-${index}`, url, title: `Photo ${index + 1}` })),
180
+ noGallery: missing, hasPhotos: !!gallery?.photos.length, noPhoto: true };
181
+ },
182
+ view: (s) => {
183
+ const photoUrl = input("url");
184
+ const valid = /^https:\/\/static\.wixstatic\.com\/media\/[A-Za-z0-9_~.-]+$/.test(photoUrl);
185
+ return { ...s, ...loaded("galleries", s.galleries, COPY), ...presence(valid ? "found" : "missing"),
186
+ photoUrl: valid ? photoUrl : "", photoTitle: valid ? input("name") || "Gallery photo" : "", hasPhoto: valid, noPhoto: !valid };
187
+ },
78
188
  },
79
189
  });
80
- svc.load();
81
190
  export { svc };
@@ -1,21 +0,0 @@
1
- // Tier-5 bundle: Wix Spaces clone — Photo Albums (ON-DEVICE, site-scoped).
2
- // No albums API wired yet → renders the real empty state, never fabricated rows.
3
- // The emptyTitle/emptyBody copy is the Spaces clone's LIST screen. An app that opens
4
- // this for ONE album (branded-lite's gallery "Open album", which passes the album id)
5
- // renders its own single-album copy and gates it on `noEntity`; opened with no id at
6
- // all it shows the shared not-found state.
7
- import { defineService } from "@himalaya/state";
8
- import { presence, detailInputId, type PresenceFlags } from "../_shared/detail-presence.js";
9
-
10
- interface S extends PresenceFlags { status: "ready"; emptyTitle: string; emptyBody: string }
11
-
12
- const svc = defineService<S, { load: (s: S) => S }>("spacesPhotoAlbums", {
13
- state: {
14
- status: "ready",
15
- emptyTitle: "No albums yet",
16
- emptyBody: "Photo albums shared on this site will appear here.",
17
- ...presence("loading"),
18
- },
19
- actions: { load: (s) => ({ ...s, ...presence(detailInputId() ? "found" : "missing") }) },
20
- });
21
- export { svc };