@wix/himalaya-cli 0.1556.0 → 0.1558.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.
Files changed (53) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +6 -4
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/media/src/index.d.ts +1 -1
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +2 -2
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +1 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-media.mjs +1 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-rich-content.mjs +1 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores-collections.mjs +1 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores.mjs +1 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-video.mjs +1 -0
  10. package/dist/cli.mjs +22575 -22024
  11. package/dist/preview-assets.generated.bin +0 -0
  12. package/guides/getting-started.md +4 -1
  13. package/guides/himi-json-output.md +13 -0
  14. package/guides/himi-skill.md +4 -1
  15. package/guides/one-app-to-himi-app.md +25 -6
  16. package/guides/sdk-reference.json +2 -2
  17. package/guides/skill.md +4 -1
  18. package/guides/splash-screens.md +22 -1
  19. package/package.json +2 -2
  20. package/templates/beauty-salon/tier5-src/_shared/himi-media.ts +29 -0
  21. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +3 -4
  22. package/templates/branded-lite/tier5-src/_shared/himi-media.ts +29 -0
  23. package/templates/branded-lite/tier5-src/himi-branded-lite-group/index.ts +3 -6
  24. package/templates/branded-lite/tier5-src/himi-branded-lite-groups/index.ts +2 -15
  25. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +3 -4
  26. package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +3 -9
  27. package/templates/branded-lite/tier5-src/himi-branded-lite-programs/index.ts +1 -9
  28. package/templates/community-club/tier5-src/_shared/himi-media.ts +29 -0
  29. package/templates/community-club/tier5-src/himi-branded-lite-group/index.ts +3 -6
  30. package/templates/community-club/tier5-src/himi-branded-lite-groups/index.ts +2 -15
  31. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +3 -4
  32. package/templates/events-meetups/tier5-src/_shared/himi-media.ts +29 -0
  33. package/templates/events-meetups/tier5-src/himi-branded-lite-group/index.ts +3 -6
  34. package/templates/events-meetups/tier5-src/himi-branded-lite-groups/index.ts +2 -15
  35. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +3 -4
  36. package/templates/gym-fitness/tier5-src/_shared/himi-media.ts +29 -0
  37. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +3 -4
  38. package/templates/nonprofit-cause/tier5-src/_shared/himi-media.ts +29 -0
  39. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group/index.ts +3 -6
  40. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-groups/index.ts +2 -15
  41. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +3 -4
  42. package/templates/podcast-creator/tier5-src/_shared/himi-media.ts +29 -0
  43. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +3 -4
  44. package/templates/portfolio-creative/tier5-src/_shared/himi-media.ts +29 -0
  45. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +3 -4
  46. package/templates/restaurant/tier5-src/_shared/himi-media.ts +29 -0
  47. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +3 -4
  48. package/templates/retail-store/tier5-src/_shared/himi-media.ts +29 -0
  49. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +3 -4
  50. package/templates/yoga-studio/tier5-src/_shared/himi-media.ts +29 -0
  51. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +3 -4
  52. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +3 -9
  53. package/templates/yoga-studio/tier5-src/himi-branded-lite-programs/index.ts +1 -9
@@ -1,6 +1,6 @@
1
1
  import { defineService } from "../_shared/collection-service.js";
2
2
  import { queryGroups, type WixGroup } from "@himalaya/wix-groups";
3
- import { wixMediaUrl } from "@himalaya/wix-media";
3
+ import { groupImage } from "../_shared/himi-media.js";
4
4
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
5
5
  import { getAll, upsert, type DbEntity } from "@himalaya/local-db";
6
6
 
@@ -45,23 +45,10 @@ interface State {
45
45
  noEmpty: boolean;
46
46
  }
47
47
 
48
- /** Social Groups stores a cover as `coverImage.image.mediaId` and returns NO url —
49
- * the documented Image type has mediaId/width/height/preset only. Probing just
50
- * `.url` therefore left every real cover unrendered (accent band + initials).
51
- * Keep the url probes for other shapes, then derive from the media id. */
52
- function coverImage(group: WixGroup): string {
53
- const cover = group.coverImage as
54
- | { url?: string; image?: { url?: string; mediaId?: string; id?: string } }
55
- | undefined;
56
- return wixMediaUrl(
57
- cover?.image?.url ?? cover?.url ?? cover?.image?.mediaId ?? cover?.image?.id,
58
- );
59
- }
60
-
61
48
  function groupRow(group: WixGroup): GroupRow {
62
49
  const title = (group.name || copyParam("groups.groupFallbackName", "Group")).trim();
63
50
  const count = group.membersCount ?? group.memberCount;
64
- const image = coverImage(group);
51
+ const image = groupImage(group);
65
52
  return {
66
53
  id: group.id || title,
67
54
  title,
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -0,0 +1,29 @@
1
+ // Cover selection is shared by list, detail and home so the same Wix object never
2
+ // loses its image on navigation. Relative SDK imports are rewritten by the template generator.
3
+ import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
4
+ import type { WixGroup } from "@himalaya/wix-groups";
5
+ import type { WixProgram } from "@himalaya/wix-online-programs";
6
+
7
+ function firstImage(references: unknown[], size: WixImageSize): string {
8
+ for (const reference of references) {
9
+ const image = wixMediaUrl(reference, size);
10
+ if (image) return image;
11
+ }
12
+ return "";
13
+ }
14
+
15
+ export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
16
+ const cover = group.coverImage as {
17
+ url?: unknown;
18
+ image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
19
+ } | undefined;
20
+ return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
21
+ }
22
+
23
+ export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
24
+ const cover = (program as { coverImage?: unknown }).coverImage;
25
+ const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
26
+ const description = (program as { description?: unknown }).description;
27
+ const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
28
+ return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
29
+ }
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -0,0 +1,29 @@
1
+ // Cover selection is shared by list, detail and home so the same Wix object never
2
+ // loses its image on navigation. Relative SDK imports are rewritten by the template generator.
3
+ import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
4
+ import type { WixGroup } from "@himalaya/wix-groups";
5
+ import type { WixProgram } from "@himalaya/wix-online-programs";
6
+
7
+ function firstImage(references: unknown[], size: WixImageSize): string {
8
+ for (const reference of references) {
9
+ const image = wixMediaUrl(reference, size);
10
+ if (image) return image;
11
+ }
12
+ return "";
13
+ }
14
+
15
+ export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
16
+ const cover = group.coverImage as {
17
+ url?: unknown;
18
+ image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
19
+ } | undefined;
20
+ return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
21
+ }
22
+
23
+ export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
24
+ const cover = (program as { coverImage?: unknown }).coverImage;
25
+ const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
26
+ const description = (program as { description?: unknown }).description;
27
+ const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
28
+ return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
29
+ }
@@ -4,6 +4,8 @@ import { getGroup, joinGroup, leaveGroup, queryMembers as queryGroupMembers, typ
4
4
  import { getMember } from "@himalaya/wix-members";
5
5
  import { signedInEmail } from "../_shared/himi-account";
6
6
  import { initials } from "../_shared/himi-text.js";
7
+ import { groupImage } from "../_shared/himi-media.js";
8
+ import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
7
9
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
8
10
  import { get as dbGet, upsert as dbUpsert, type DbEntity } from "@himalaya/local-db";
9
11
  import { isOfflineFailure, writeMayHaveLanded } from "../_shared/owner-load-error";
@@ -87,11 +89,6 @@ function inputId(): string {
87
89
  return input?.screenId?.startsWith("himi_group_") ? input.screenId.slice("himi_group_".length) : "";
88
90
  }
89
91
 
90
- function coverImage(group: WixGroup): string {
91
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
92
- return cover?.image?.url ?? cover?.url ?? "";
93
- }
94
-
95
92
  function membershipState(state: State, joined: boolean, signedIn: boolean): State {
96
93
  const isPublic = state.privacy === "Public" || state.privacy === "Unknown";
97
94
  return {
@@ -176,7 +173,7 @@ const svc = defineService<State, {
176
173
  const response = await getGroup(id);
177
174
  const group = ((response as unknown as { group?: WixGroup }).group ?? response) as WixGroup;
178
175
  const name = (group.name || copyParam("group.groupFallbackName", "Group")).trim();
179
- const image = coverImage(group);
176
+ const image = groupImage(group, WIX_DETAIL_IMAGE);
180
177
  const count = group.membersCount ?? group.memberCount;
181
178
  const privacy = (group.privacyStatus || "UNKNOWN").toLowerCase();
182
179
  const privacyLabel = privacy[0].toUpperCase() + privacy.slice(1);
@@ -1,6 +1,6 @@
1
1
  import { defineService } from "../_shared/collection-service.js";
2
2
  import { queryGroups, type WixGroup } from "@himalaya/wix-groups";
3
- import { wixMediaUrl } from "@himalaya/wix-media";
3
+ import { groupImage } from "../_shared/himi-media.js";
4
4
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
5
5
  import { getAll, upsert, type DbEntity } from "@himalaya/local-db";
6
6
 
@@ -45,23 +45,10 @@ interface State {
45
45
  noEmpty: boolean;
46
46
  }
47
47
 
48
- /** Social Groups stores a cover as `coverImage.image.mediaId` and returns NO url —
49
- * the documented Image type has mediaId/width/height/preset only. Probing just
50
- * `.url` therefore left every real cover unrendered (accent band + initials).
51
- * Keep the url probes for other shapes, then derive from the media id. */
52
- function coverImage(group: WixGroup): string {
53
- const cover = group.coverImage as
54
- | { url?: string; image?: { url?: string; mediaId?: string; id?: string } }
55
- | undefined;
56
- return wixMediaUrl(
57
- cover?.image?.url ?? cover?.url ?? cover?.image?.mediaId ?? cover?.image?.id,
58
- );
59
- }
60
-
61
48
  function groupRow(group: WixGroup): GroupRow {
62
49
  const title = (group.name || copyParam("groups.groupFallbackName", "Group")).trim();
63
50
  const count = group.membersCount ?? group.memberCount;
64
- const image = coverImage(group);
51
+ const image = groupImage(group);
65
52
  return {
66
53
  id: group.id || title,
67
54
  title,
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -0,0 +1,29 @@
1
+ // Cover selection is shared by list, detail and home so the same Wix object never
2
+ // loses its image on navigation. Relative SDK imports are rewritten by the template generator.
3
+ import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
4
+ import type { WixGroup } from "@himalaya/wix-groups";
5
+ import type { WixProgram } from "@himalaya/wix-online-programs";
6
+
7
+ function firstImage(references: unknown[], size: WixImageSize): string {
8
+ for (const reference of references) {
9
+ const image = wixMediaUrl(reference, size);
10
+ if (image) return image;
11
+ }
12
+ return "";
13
+ }
14
+
15
+ export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
16
+ const cover = group.coverImage as {
17
+ url?: unknown;
18
+ image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
19
+ } | undefined;
20
+ return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
21
+ }
22
+
23
+ export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
24
+ const cover = (program as { coverImage?: unknown }).coverImage;
25
+ const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
26
+ const description = (program as { description?: unknown }).description;
27
+ const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
28
+ return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
29
+ }
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -0,0 +1,29 @@
1
+ // Cover selection is shared by list, detail and home so the same Wix object never
2
+ // loses its image on navigation. Relative SDK imports are rewritten by the template generator.
3
+ import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
4
+ import type { WixGroup } from "@himalaya/wix-groups";
5
+ import type { WixProgram } from "@himalaya/wix-online-programs";
6
+
7
+ function firstImage(references: unknown[], size: WixImageSize): string {
8
+ for (const reference of references) {
9
+ const image = wixMediaUrl(reference, size);
10
+ if (image) return image;
11
+ }
12
+ return "";
13
+ }
14
+
15
+ export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
16
+ const cover = group.coverImage as {
17
+ url?: unknown;
18
+ image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
19
+ } | undefined;
20
+ return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
21
+ }
22
+
23
+ export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
24
+ const cover = (program as { coverImage?: unknown }).coverImage;
25
+ const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
26
+ const description = (program as { description?: unknown }).description;
27
+ const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
28
+ return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
29
+ }
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -0,0 +1,29 @@
1
+ // Cover selection is shared by list, detail and home so the same Wix object never
2
+ // loses its image on navigation. Relative SDK imports are rewritten by the template generator.
3
+ import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
4
+ import type { WixGroup } from "@himalaya/wix-groups";
5
+ import type { WixProgram } from "@himalaya/wix-online-programs";
6
+
7
+ function firstImage(references: unknown[], size: WixImageSize): string {
8
+ for (const reference of references) {
9
+ const image = wixMediaUrl(reference, size);
10
+ if (image) return image;
11
+ }
12
+ return "";
13
+ }
14
+
15
+ export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
16
+ const cover = group.coverImage as {
17
+ url?: unknown;
18
+ image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
19
+ } | undefined;
20
+ return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
21
+ }
22
+
23
+ export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
24
+ const cover = (program as { coverImage?: unknown }).coverImage;
25
+ const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
26
+ const description = (program as { description?: unknown }).description;
27
+ const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
28
+ return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
29
+ }
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -0,0 +1,29 @@
1
+ // Cover selection is shared by list, detail and home so the same Wix object never
2
+ // loses its image on navigation. Relative SDK imports are rewritten by the template generator.
3
+ import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
4
+ import type { WixGroup } from "@himalaya/wix-groups";
5
+ import type { WixProgram } from "@himalaya/wix-online-programs";
6
+
7
+ function firstImage(references: unknown[], size: WixImageSize): string {
8
+ for (const reference of references) {
9
+ const image = wixMediaUrl(reference, size);
10
+ if (image) return image;
11
+ }
12
+ return "";
13
+ }
14
+
15
+ export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
16
+ const cover = group.coverImage as {
17
+ url?: unknown;
18
+ image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
19
+ } | undefined;
20
+ return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
21
+ }
22
+
23
+ export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
24
+ const cover = (program as { coverImage?: unknown }).coverImage;
25
+ const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
26
+ const description = (program as { description?: unknown }).description;
27
+ const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
28
+ return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
29
+ }
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -0,0 +1,29 @@
1
+ // Cover selection is shared by list, detail and home so the same Wix object never
2
+ // loses its image on navigation. Relative SDK imports are rewritten by the template generator.
3
+ import { wixMediaUrl, WIX_LIST_IMAGE, type WixImageSize } from "@himalaya/wix-media";
4
+ import type { WixGroup } from "@himalaya/wix-groups";
5
+ import type { WixProgram } from "@himalaya/wix-online-programs";
6
+
7
+ function firstImage(references: unknown[], size: WixImageSize): string {
8
+ for (const reference of references) {
9
+ const image = wixMediaUrl(reference, size);
10
+ if (image) return image;
11
+ }
12
+ return "";
13
+ }
14
+
15
+ export function groupImage(group: WixGroup, size: WixImageSize = WIX_LIST_IMAGE): string {
16
+ const cover = group.coverImage as {
17
+ url?: unknown;
18
+ image?: { url?: unknown; fileUrl?: unknown; mediaId?: unknown; id?: unknown };
19
+ } | undefined;
20
+ return firstImage([cover?.image?.url, cover?.image?.fileUrl, cover?.url, cover?.image?.mediaId, cover?.image?.id], size);
21
+ }
22
+
23
+ export function programImage(program: WixProgram, size: WixImageSize = WIX_LIST_IMAGE): string {
24
+ const cover = (program as { coverImage?: unknown }).coverImage;
25
+ const coverUrl = typeof cover === "string" ? cover : (cover as { url?: unknown } | undefined)?.url;
26
+ const description = (program as { description?: unknown }).description;
27
+ const nested = description && typeof description === "object" ? (description as { image?: { url?: unknown; id?: unknown } }).image : undefined;
28
+ return firstImage([coverUrl, program.image, nested?.url, nested?.id], size);
29
+ }
@@ -24,6 +24,7 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
24
24
  import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
25
25
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
26
26
  import { wixMediaUrl } from "@himalaya/wix-media";
27
+ import { groupImage, programImage } from "../_shared/himi-media.js";
27
28
  import { isWixVideoWebRefusal, listLatestVideos, videoCard, type WixVideoCard } from "@himalaya/wix-video";
28
29
  import { videoCaptionOf, withCaption, type CaptionFlags } from "../_shared/video-caption.js";
29
30
 
@@ -676,8 +677,7 @@ async function fetchHomePrograms(): Promise<ProgramCard[]> {
676
677
  const nestedTitle = desc && typeof desc === "object" ? (desc as { title?: string }).title : undefined;
677
678
  const nestedDetails = desc && typeof desc === "object" ? (desc as { details?: string }).details : undefined;
678
679
  const name = cleanTitle(p.settings?.title ?? nestedTitle ?? p.title) || copyParam("home.programFallbackName", "Program");
679
- const cover = (p as { coverImage?: unknown }).coverImage;
680
- const image = (typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url) || p.image || "";
680
+ const image = programImage(p);
681
681
  const details = (p.settings?.description ?? nestedDetails ?? (typeof desc === "string" ? desc : "") ?? "")
682
682
  .replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
683
683
  return {
@@ -732,8 +732,7 @@ function homeTimeLabel(value?: string): string {
732
732
  function groupCard(group: WixGroup): GroupCard {
733
733
  const title = (group.name || copyParam("home.groupFallbackTitle", "Group")).trim();
734
734
  const count = group.membersCount ?? group.memberCount;
735
- const cover = group.coverImage as { url?: string; image?: { url?: string } } | undefined;
736
- const image = cover?.image?.url ?? cover?.url ?? "";
735
+ const image = groupImage(group);
737
736
  return {
738
737
  id: group.id || title,
739
738
  title,
@@ -21,6 +21,8 @@ import { withOnlineWrites } from "../_shared/online-actions.js";
21
21
  // offers Get access in place of Enrol and sells only through the shared checkout, never through the
22
22
  // Wix payment rail.
23
23
  import { defineService as registerService } from "@himalaya/state";
24
+ import { programImage } from "../_shared/himi-media.js";
25
+ import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
24
26
  import {
25
27
  getProgram,
26
28
  joinProgram,
@@ -122,14 +124,6 @@ function detailsOf(p: WixProgram): string {
122
124
  .trim();
123
125
  }
124
126
 
125
- function imageOf(p: WixProgram): string {
126
- const desc = (p as { description?: unknown }).description;
127
- const nested = desc && typeof desc === "object" ? (desc as { image?: { url?: string } }).image?.url : undefined;
128
- const cover = (p as { coverImage?: unknown }).coverImage;
129
- const coverUrl = typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url;
130
- return (coverUrl || p.image || nested || "").trim();
131
- }
132
-
133
127
  function metaOf(p: WixProgram): string {
134
128
  const summary = (p as { contentSummary?: { stepCount?: number; sectionCount?: number } }).contentSummary;
135
129
  const bits: string[] = [];
@@ -160,7 +154,7 @@ function ready(state: State, p: WixProgram | undefined): State {
160
154
  return derive({ ...state, status: "ready", name: "", description: "", meta: "", priceLine: "", image: "", initial: "", emptyText: copyParam("program.emptyText", "This program is no longer available.") });
161
155
  }
162
156
  const name = titleOf(p);
163
- const image = imageOf(p);
157
+ const image = programImage(p, WIX_DETAIL_IMAGE);
164
158
  return derive({
165
159
  ...state,
166
160
  status: "ready",
@@ -14,7 +14,7 @@ import { decorateCollection } from "../_shared/collection-service.js";
14
14
  import { queryPrograms, type WixProgram } from "@himalaya/wix-online-programs";
15
15
  import { iapPriceFields, onlineProgramItem, type IapPollDirective } from "@himalaya/iap";
16
16
  import { createIapCommerce } from "@himalaya/iap-checkout";
17
- import { wixMediaUrl } from "@himalaya/wix-media";
17
+ import { programImage } from "../_shared/himi-media.js";
18
18
  import { cleanTitle, initials } from "../_shared/himi-text.js";
19
19
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
20
20
  import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
@@ -86,14 +86,6 @@ function programDetails(p: WixProgram): string {
86
86
  return raw.length > 140 ? `${raw.slice(0, 139).trimEnd()}…` : raw;
87
87
  }
88
88
 
89
- function programImage(p: WixProgram): string {
90
- const desc = (p as { description?: unknown }).description;
91
- const nested = desc && typeof desc === "object" ? (desc as { image?: { url?: string } }).image?.url : undefined;
92
- const cover = (p as { coverImage?: unknown }).coverImage;
93
- const coverUrl = typeof cover === "string" ? cover : (cover as { url?: string } | undefined)?.url;
94
- return wixMediaUrl(coverUrl || p.image || nested);
95
- }
96
-
97
89
  function programMeta(p: WixProgram): string {
98
90
  const summary = (p as { contentSummary?: { stepCount?: number; sectionCount?: number } }).contentSummary;
99
91
  const bits: string[] = [];