lightnet 3.6.1 → 3.8.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/CHANGELOG.md +59 -0
  2. package/README.md +23 -11
  3. package/__e2e__/fixtures/basics/node_modules/.bin/astro +7 -3
  4. package/__e2e__/fixtures/basics/node_modules/.bin/tailwind +5 -1
  5. package/__e2e__/fixtures/basics/node_modules/.bin/tailwindcss +5 -1
  6. package/__e2e__/fixtures/basics/node_modules/.bin/tsc +7 -3
  7. package/__e2e__/fixtures/basics/node_modules/.bin/tsserver +7 -3
  8. package/__e2e__/fixtures/basics/package.json +10 -7
  9. package/__tests__/utils/markdown.spec.ts +4 -0
  10. package/__tests__/utils/urls.spec.ts +27 -0
  11. package/exports/experimental-components.ts +1 -0
  12. package/package.json +19 -12
  13. package/src/astro-integration/integration.ts +6 -0
  14. package/src/components/CarouselSection.astro +182 -0
  15. package/src/components/CategoriesSection.astro +96 -47
  16. package/src/components/MediaGallery.astro +1 -1
  17. package/src/components/SearchInput.astro +1 -0
  18. package/src/components/SearchSection.astro +9 -4
  19. package/src/components/Section.astro +31 -17
  20. package/src/content/get-categories.ts +1 -1
  21. package/src/i18n/translations/README.md +1 -1
  22. package/src/i18n/translations/ar.yml +2 -0
  23. package/src/i18n/translations/bn.yml +2 -0
  24. package/src/i18n/translations/de.yml +2 -0
  25. package/src/i18n/translations/en.yml +12 -0
  26. package/src/i18n/translations/es.yml +2 -0
  27. package/src/i18n/translations/fi.yml +2 -0
  28. package/src/i18n/translations/fr.yml +2 -0
  29. package/src/i18n/translations/hi.yml +2 -0
  30. package/src/i18n/translations/pt.yml +2 -0
  31. package/src/i18n/translations/ru.yml +2 -0
  32. package/src/i18n/translations/uk.yml +2 -0
  33. package/src/i18n/translations/zh.yml +2 -0
  34. package/src/i18n/translations.ts +2 -0
  35. package/src/layouts/Page.astro +2 -3
  36. package/src/layouts/components/Header.astro +1 -4
  37. package/src/layouts/components/ViewTransition.astro +9 -0
  38. package/src/pages/api/versions.ts +7 -0
  39. package/src/pages/details-page/AudioDetailsPage.astro +4 -9
  40. package/src/pages/details-page/DefaultDetailsPage.astro +6 -6
  41. package/src/pages/details-page/components/AudioPanel.astro +64 -0
  42. package/src/pages/details-page/components/{main-details/AudioPlayer.astro → AudioPlayer.astro} +1 -1
  43. package/src/pages/details-page/components/ContentSection.astro +5 -2
  44. package/src/pages/details-page/components/DescriptionSection.astro +1 -1
  45. package/src/pages/details-page/components/MainDetailsSection.astro +8 -5
  46. package/src/pages/details-page/components/VideoDetailsSection.astro +1 -1
  47. package/src/pages/details-page/components/main-details/Authors.astro +2 -2
  48. package/src/pages/details-page/components/main-details/OpenButton.astro +3 -1
  49. package/src/pages/details-page/components/main-details/ShareButton.astro +21 -33
  50. package/src/pages/details-page/components/main-details/{Cover.astro → Thumbnail.astro} +14 -3
  51. package/src/pages/search-page/components/SearchList.tsx +0 -1
  52. package/src/pages/search-page/hooks/use-search.ts +2 -33
  53. package/src/utils/paths.ts +15 -1
@@ -4,15 +4,16 @@ import { Image } from "astro:assets"
4
4
 
5
5
  import { getUsedCategories } from "../content/get-categories"
6
6
  import { searchPagePath } from "../utils/paths"
7
- import Section from "./Section.astro"
7
+ import CarouselSection from "./CarouselSection.astro"
8
+ import Section, { type Props as SectionProps } from "./Section.astro"
8
9
 
9
- interface Props {
10
- title?: string
11
- layout?: "button-grid" | "image-grid"
10
+ type Props = SectionProps & {
11
+ layout?: "grid" | "carousel"
12
12
  }
13
13
 
14
- const { title, layout = "button-grid" } = Astro.props
14
+ const { title, layout = "carousel", ...props } = Astro.props
15
15
  const { t, currentLocale } = Astro.locals.i18n
16
+ const resolvedTitle = title ?? t("ln.categories")
16
17
 
17
18
  const categories = await getUsedCategories(currentLocale, t)
18
19
  type Category = (typeof categories)[number]
@@ -20,8 +21,8 @@ type Category = (typeof categories)[number]
20
21
  function getImage({ image, id }: Category) {
21
22
  if (!image) {
22
23
  throw new AstroError(
23
- `The CategorySection with layout="image-grid" requires an image for category "${id}".`,
24
- `To resolve this issue, either change the layout to "button-grid" or provide an image path in /src/content/categories/${id}.json.`,
24
+ `Expected an image for category "${id}".`,
25
+ `To resolve this issue, provide a valid image path in /src/content/categories/${id}.json.`,
25
26
  )
26
27
  }
27
28
  return image
@@ -29,40 +30,27 @@ function getImage({ image, id }: Category) {
29
30
  ---
30
31
 
31
32
  {
32
- categories.length && (
33
- <Section title={title ?? t("ln.categories")}>
34
- {layout === "button-grid" && (
35
- <ul class="grid w-full grid-cols-2 flex-wrap gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
36
- {categories.map((category) => (
37
- <li class="grow">
38
- <a
39
- class="flex h-20 w-full items-center justify-center rounded-2xl bg-gray-200 p-3 text-gray-600 shadow-sm transition-colors ease-in-out hover:bg-gray-300"
40
- href={searchPagePath(currentLocale, {
41
- category: category.id,
42
- })}
33
+ !!categories.length && layout === "grid" && (
34
+ <Section {...props} title={resolvedTitle}>
35
+ <ol class="grid grid-cols-2 items-end justify-between gap-4 sm:grid-cols-3 md:grid-cols-4 md:gap-8 lg:grid-cols-5">
36
+ {categories.map((category) => (
37
+ <li class="h-full">
38
+ <a
39
+ href={searchPagePath(currentLocale, { category: category.id })}
40
+ class="group flex h-full flex-col gap-3"
41
+ >
42
+ <div
43
+ class="relative overflow-hidden rounded-2xl shadow-md outline-2 outline-gray-400 transition-colors duration-75 ease-in-out sm:group-hover:outline"
44
+ class:list={[
45
+ !category.image && "h-full min-h-28 w-full bg-gray-300",
46
+ ]}
43
47
  >
44
- <div class="line-clamp-3 overflow-hidden text-balance text-center text-xs font-bold">
45
- {category.name}
46
- </div>
47
- </a>
48
- </li>
49
- ))}
50
- </ul>
51
- )}
52
- {layout === "image-grid" && (
53
- <ol class="grid grid-cols-2 items-end justify-between gap-4 sm:grid-cols-3 md:grid-cols-4 md:gap-8 lg:grid-cols-5">
54
- {categories.map((category) => (
55
- <li>
56
- <a
57
- href={searchPagePath(currentLocale, { category: category.id })}
58
- class="group flex flex-col gap-3"
59
- >
60
- <div class="relative overflow-hidden rounded-md shadow-md outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline">
48
+ {category.image && (
61
49
  <Image
62
- class="h-full w-full object-contain"
50
+ class="h-full w-full bg-gray-500 object-contain"
63
51
  src={getImage(category)}
64
52
  alt=""
65
- widths={[256, 512, 768, 1024]}
53
+ widths={[128, 256, 388, 512, 768]}
66
54
  sizes={
67
55
  "(max-width: 640px) calc(calc(100vw - 3.5rem ) / 2), " +
68
56
  "(max-width: 768px) calc(calc(100vw - 5rem ) / 3), " +
@@ -71,17 +59,78 @@ function getImage({ image, id }: Category) {
71
59
  "217px"
72
60
  }
73
61
  />
74
- <div class="absolute start-0 top-0 flex h-full w-full flex-col justify-end bg-gradient-to-t from-black/80 via-black/35 via-30% to-transparent to-65% p-4 text-gray-50">
75
- <span class="line-clamp-3 text-balance font-bold">
76
- {category.name}
77
- </span>
78
- </div>
62
+ )}
63
+
64
+ <div
65
+ class="absolute start-0 top-0 flex h-full w-full flex-col justify-end bg-gradient-to-t p-4 text-gray-50"
66
+ class:list={[
67
+ category.image
68
+ ? "from-black/80 via-black/30 via-50% to-transparent"
69
+ : "from-black/35 to-transparent",
70
+ ]}
71
+ >
72
+ <span class="line-clamp-3 select-none text-balance font-bold">
73
+ {category.name}
74
+ </span>
79
75
  </div>
80
- </a>
81
- </li>
82
- ))}
83
- </ol>
84
- )}
76
+ </div>
77
+ </a>
78
+ </li>
79
+ ))}
80
+ </ol>
85
81
  </Section>
86
82
  )
87
83
  }
84
+ {
85
+ !!categories.length && layout === "carousel" && (
86
+ <CarouselSection {...props} title={resolvedTitle}>
87
+ {categories.map((category) => (
88
+ <li
89
+ class="carousel-item--narrow h-full"
90
+ role="group"
91
+ aria-roledescription="slide"
92
+ >
93
+ <a
94
+ href={searchPagePath(currentLocale, { category: category.id })}
95
+ class="group flex h-full flex-col gap-3"
96
+ >
97
+ <div
98
+ class="relative overflow-hidden rounded-2xl shadow-md outline-2 outline-gray-400 transition-all duration-75 ease-in-out sm:group-hover:outline"
99
+ class:list={[
100
+ !category.image && "h-full min-h-28 w-full bg-gray-300",
101
+ ]}
102
+ >
103
+ {category.image && (
104
+ <Image
105
+ class="h-full w-full bg-gray-300 object-contain"
106
+ src={getImage(category)}
107
+ alt=""
108
+ widths={[128, 256, 388, 512, 768]}
109
+ sizes={
110
+ "(max-width: 640px) calc(calc(100vw - 3rem ) / 2.3), " +
111
+ "(max-width: 768px) calc(calc(100vw - 5rem ) / 3), " +
112
+ "(max-width: 1024px) calc(calc(100vw - 10rem ) / 4), " +
113
+ "(max-width: 1280px) calc(calc(100vw - 12rem ) / 5), " +
114
+ "217px"
115
+ }
116
+ />
117
+ )}
118
+ <div
119
+ class="absolute start-0 top-0 flex h-full w-full flex-col justify-end bg-gradient-to-t p-4 text-gray-50"
120
+ class:list={[
121
+ category.image
122
+ ? "from-black/80 via-black/30 via-50% to-transparent"
123
+ : "from-black/35 to-transparent",
124
+ ]}
125
+ >
126
+ <span class="line-clamp-3 select-none text-balance font-bold">
127
+ {category.name}
128
+ </span>
129
+ </div>
130
+ </div>
131
+ </a>
132
+ </li>
133
+ ))}
134
+ </CarouselSection>
135
+ )
136
+ }
@@ -81,7 +81,7 @@ const items = itemsInput.filter((item) => !!item)
81
81
  {
82
82
  (layout === "video" || layout === "landscape") && (
83
83
  <ol
84
- class="grid grid-cols-1 justify-between gap-x-7 gap-y-4 sm:grid-cols-2 md:grid-cols-3 md:gap-8 lg:grid-cols-4 xl:grid-cols-4"
84
+ class="grid grid-cols-1 justify-between gap-x-7 gap-y-4 sm:grid-cols-2 md:grid-cols-3 md:gap-8 lg:grid-cols-4"
85
85
  class:list={[layout === "landscape" && "items-end"]}
86
86
  >
87
87
  {items.map((item) => (
@@ -11,6 +11,7 @@ const { t } = Astro.locals.i18n
11
11
  <form
12
12
  action={`/${Astro.currentLocale}/media`}
13
13
  method="get"
14
+ role="search"
14
15
  class="dy-join group w-full rounded-2xl shadow-md outline-2 outline-offset-2 outline-gray-400 group-focus-within:outline"
15
16
  class:list={[Astro.props.className]}
16
17
  >
@@ -3,12 +3,17 @@ import SearchFilter from "../pages/search-page/components/SearchFilter.astro"
3
3
  import SearchList from "../pages/search-page/components/SearchList.astro"
4
4
  import Section, { type Props as SectionProps } from "./Section.astro"
5
5
 
6
- // we need the semi-colon for the astro compiler :(
7
- // prettier-ignore
8
- type Props = Omit<SectionProps, "disableHorizontalPadding" | "maxWidth">;
6
+ type Props = Omit<SectionProps, "maxWidth">
7
+
8
+ const { className = "", titleClass = "", ...props } = Astro.props
9
9
  ---
10
10
 
11
- <Section {...Astro.props} disableHorizontalPadding={true} maxWidth="narrow">
11
+ <Section
12
+ {...props}
13
+ className={"px-0 md:px-0 " + className}
14
+ titleClass={"px-4 md:px-8 " + titleClass}
15
+ maxWidth="narrow"
16
+ >
12
17
  <div class="px-4 md:px-8">
13
18
  <SearchFilter />
14
19
  </div>
@@ -1,4 +1,6 @@
1
1
  ---
2
+ import Icon from "./Icon"
3
+
2
4
  export interface Props {
3
5
  /**
4
6
  * Id to set to the section element.
@@ -21,30 +23,35 @@ export interface Props {
21
23
  * @default "lg"
22
24
  */
23
25
  marginTop?: "sm" | "lg" | "none"
24
- /**
25
- * Remove padding from the left and right side of the content.
26
- * This will not apply to the section title.
27
- *
28
- * @default false
29
- */
30
- disableHorizontalPadding?: boolean
31
26
  /**
32
27
  * Title on top of the section.
33
28
  */
34
29
  title?: string
30
+ /**
31
+ * A link to add to the section title. This link can be used to show
32
+ * more content related to the section.
33
+ *
34
+ * The link will not be transformed. You need to make sure to prepend the current locale.
35
+ */
36
+ titleHref?: string
35
37
  /**
36
38
  * Css classes to set to the section element.
37
39
  */
38
40
  className?: string
41
+ /**
42
+ * Css classes to set to the title element.
43
+ */
44
+ titleClass?: string
39
45
  }
40
46
 
41
47
  const {
42
48
  id,
43
49
  maxWidth = "wide",
44
50
  marginTop = "lg",
45
- disableHorizontalPadding = false,
51
+ titleHref,
46
52
  title,
47
53
  className,
54
+ titleClass,
48
55
  } = Astro.props
49
56
 
50
57
  const maxWidths = {
@@ -60,22 +67,29 @@ const marginTopValues = {
60
67
  ---
61
68
 
62
69
  <section
63
- class="mx-auto"
64
- class:list={[
65
- maxWidths[maxWidth],
66
- marginTopValues[marginTop],
67
- !disableHorizontalPadding && "px-4 md:px-8",
68
- className,
69
- ]}
70
+ class="mx-auto px-4 md:px-8"
71
+ class:list={[maxWidths[maxWidth], marginTopValues[marginTop], className]}
70
72
  id={id}
73
+ aria-label={title}
71
74
  >
72
75
  {
73
76
  title && (
74
77
  <h2
75
78
  class="mb-10 text-balance text-2xl font-bold text-gray-700 sm:mb-12 sm:text-3xl"
76
- class:list={[disableHorizontalPadding && "px-4 md:px-8"]}
79
+ class:list={[titleClass]}
77
80
  >
78
- {title}
81
+ {titleHref ? (
82
+ <a href={titleHref} class="group flex items-center">
83
+ {title}
84
+ <Icon
85
+ className="mdi--chevron-right group-hover:text-primary transition-colors ease-in-out duration-75 text-4xl sm:text-5xl -ms-2"
86
+ ariaLabel=""
87
+ flipIcon={Astro.locals.i18n.direction === "rtl"}
88
+ />
89
+ </a>
90
+ ) : (
91
+ <>{title}</>
92
+ )}
79
93
  </h2>
80
94
  )
81
95
  }
@@ -68,6 +68,6 @@ function parseCategory(item: unknown) {
68
68
  categoryEntrySchema,
69
69
  item,
70
70
  (id) => `Invalid category: ${id}`,
71
- (id) => `Fix these issues inside "src/content/category/${id}.json":`,
71
+ (id) => `Fix these issues inside "src/content/categories/${id}.json":`,
72
72
  )
73
73
  }
@@ -8,7 +8,7 @@ Check the [translation status](TRANSLATION-STATUS.md) for an overview of complet
8
8
  Have you translated LightNet into a new language? Have you fixed a incorrect translation? Great! How about sharing
9
9
  your work with others, by adding it to this folder?
10
10
 
11
- This are the ways how you can contribute your translations:
11
+ These are the ways you can contribute your translations:
12
12
 
13
13
  - [Open a GitHub pull-request](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project), if you are a git-native. 🤓
14
14
  - [Use the translation-update form](https://github.com/LightNetDev/LightNet/issues/new?template=---03-translations-update.yml) to share your work. ⭐️
@@ -6,6 +6,8 @@ ln.categories: الفئات
6
6
  ln.language: اللغة
7
7
  ln.languages: اللغات
8
8
  ln.type: النوع
9
+ ln.previous: السابق
10
+ ln.next: التالي
9
11
  ln.external-link: رابط خارجي
10
12
  ln.search.title: بحث
11
13
  ln.search.placeholder: ابحث في الوسائط
@@ -6,6 +6,8 @@ ln.categories: বিভাগসমূহ
6
6
  ln.language: ভাষা
7
7
  ln.languages: ভাষাসমূহ
8
8
  ln.type: ধরন
9
+ ln.previous: পূর্ববর্তী
10
+ ln.next: পরবর্তী
9
11
  ln.external-link: বহিরাগত লিঙ্ক
10
12
  ln.search.title: অনুসন্ধান
11
13
  ln.search.placeholder: মিডিয়া অনুসন্ধান করুন
@@ -5,6 +5,8 @@ ln.category: Kategorie
5
5
  ln.language: Sprache
6
6
  ln.languages: Sprachen
7
7
  ln.type: Typ
8
+ ln.previous: Zurück
9
+ ln.next: Weiter
8
10
  ln.external-link: Externer Link
9
11
  ln.details.open: Öffnen
10
12
  ln.details.part-of-collection: Teil der Sammlung
@@ -48,6 +48,18 @@ ln.languages: Languages
48
48
  # Used on: https://sk8-ministries.pages.dev/en/media
49
49
  ln.type: Type
50
50
 
51
+ # Accessibility label for buttons to show the previous items.
52
+ # This is used on the carousel arrow button.
53
+ #
54
+ # English: Previous
55
+ ln.previous: Previous
56
+
57
+ # Accessibility label for buttons to show the next items.
58
+ # This is used on the carousel arrow button.
59
+ #
60
+ # English: Next
61
+ ln.next: Next
62
+
51
63
  # Accessibility label for an external link.
52
64
  # This is only "visible" to a screen-reader.
53
65
  #
@@ -6,6 +6,8 @@ ln.categories: Categorías
6
6
  ln.language: Idioma
7
7
  ln.languages: Idiomas
8
8
  ln.type: Tipo
9
+ ln.previous: Anterior
10
+ ln.next: Siguiente
9
11
  ln.external-link: Enlace externo
10
12
  ln.search.title: Buscar
11
13
  ln.search.placeholder: Buscar medios
@@ -6,6 +6,8 @@ ln.categories: Kategoriat
6
6
  ln.language: Kieli
7
7
  ln.languages: Kielet
8
8
  ln.type: Mediatyyppi
9
+ ln.previous: Edellinen
10
+ ln.next: Seuraava
9
11
  ln.external-link: Ulkoinen linkki
10
12
  ln.search.title: Haku
11
13
  ln.search.placeholder: Hae mediaa
@@ -6,6 +6,8 @@ ln.categories: Catégories
6
6
  ln.language: Langue
7
7
  ln.languages: Langues
8
8
  ln.type: Type
9
+ ln.previous: Précédent
10
+ ln.next: Suivant
9
11
  ln.external-link: Lien externe
10
12
  ln.search.title: Recherche
11
13
  ln.search.placeholder: Rechercher des médias
@@ -6,6 +6,8 @@ ln.categories: श्रेणियाँ
6
6
  ln.language: भाषा
7
7
  ln.languages: भाषाएँ
8
8
  ln.type: प्रकार
9
+ ln.previous: पिछला
10
+ ln.next: अगला
9
11
  ln.external-link: बाहरी लिंक
10
12
  ln.search.title: खोज
11
13
  ln.search.placeholder: मीडिया खोजें
@@ -6,6 +6,8 @@ ln.categories: Categorias
6
6
  ln.language: Idioma
7
7
  ln.languages: Idiomas
8
8
  ln.type: Tipo
9
+ ln.previous: Anterior
10
+ ln.next: Seguinte
9
11
  ln.external-link: Ligação externa
10
12
  ln.search.title: Pesquisa
11
13
  ln.search.placeholder: Pesquisar conteúdos
@@ -6,6 +6,8 @@ ln.categories: Категории
6
6
  ln.language: Язык
7
7
  ln.languages: Языки
8
8
  ln.type: Тип
9
+ ln.previous: Предыдущий
10
+ ln.next: Следующий
9
11
  ln.external-link: Внешняя ссылка
10
12
  ln.search.title: Поиск
11
13
  ln.search.placeholder: Поиск медиа
@@ -6,6 +6,8 @@ ln.categories: Категорії
6
6
  ln.language: Мова
7
7
  ln.languages: Мови
8
8
  ln.type: Тип
9
+ ln.previous: Попередній
10
+ ln.next: Наступний
9
11
  ln.external-link: Зовнішнє посилання
10
12
  ln.search.title: Пошук
11
13
  ln.search.placeholder: Пошук медіа
@@ -6,6 +6,8 @@ ln.categories: 分类
6
6
  ln.language: 语言
7
7
  ln.languages: 语言
8
8
  ln.type: 类型
9
+ ln.previous: 上一个
10
+ ln.next: 下一个
9
11
  ln.external-link: 外部链接
10
12
  ln.search.title: 搜索
11
13
  ln.search.placeholder: 搜索媒体…
@@ -64,6 +64,8 @@ export type LightNetTranslationKey =
64
64
  | "ln.languages"
65
65
  | "ln.external-link"
66
66
  | "ln.type"
67
+ | "ln.previous"
68
+ | "ln.next"
67
69
  | "ln.details.open"
68
70
  | "ln.details.share"
69
71
  | "ln.details.part-of-collection"
@@ -1,5 +1,4 @@
1
1
  ---
2
- import { ClientRouter } from "astro:transitions"
3
2
  import CustomFooter from "virtual:lightnet/components/CustomFooter"
4
3
  import CustomHead from "virtual:lightnet/components/CustomHead"
5
4
  import config from "virtual:lightnet/config"
@@ -8,6 +7,7 @@ import { resolveLanguage } from "../i18n/resolve-language"
8
7
  import Favicon from "./components/Favicon.astro"
9
8
  import Header from "./components/Header.astro"
10
9
  import PreloadReact from "./components/PreloadReact"
10
+ import ViewTransition from "./components/ViewTransition.astro"
11
11
 
12
12
  interface Props {
13
13
  title?: string
@@ -32,10 +32,9 @@ const language = resolveLanguage(currentLocale)
32
32
  {config.manifest && <link rel="manifest" href={config.manifest} />}
33
33
  <link rel="prefetch" href="/api/search.json" />
34
34
  <Favicon />
35
- <ClientRouter />
35
+ <ViewTransition />
36
36
  </head>
37
37
  <body
38
- transition:animate="none"
39
38
  class="flex min-h-screen flex-col overflow-y-scroll bg-gray-50 text-gray-900"
40
39
  >
41
40
  <Header />
@@ -3,10 +3,7 @@ import PageNavigation from "./PageNavigation.astro"
3
3
  import PageTitle from "./PageTitle.astro"
4
4
  ---
5
5
 
6
- <header
7
- class="fixed top-0 z-50 h-14 w-full bg-white shadow-lg sm:h-20"
8
- transition:animate="none"
9
- >
6
+ <header class="fixed top-0 z-50 h-14 w-full bg-white shadow-lg sm:h-20">
10
7
  <div class="mx-auto flex h-full max-w-screen-xl justify-between px-4 md:px-8">
11
8
  <PageTitle />
12
9
  <PageNavigation />
@@ -0,0 +1,9 @@
1
+ ---
2
+
3
+ ---
4
+
5
+ <style is:global>
6
+ @view-transition {
7
+ navigation: auto;
8
+ }
9
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { APIRoute } from "astro"
2
+
3
+ import pkg from "../../../package.json" assert { type: "json" }
4
+
5
+ export const GET: APIRoute = () => {
6
+ return new Response(JSON.stringify({ lightnet: pkg.version }))
7
+ }
@@ -1,10 +1,8 @@
1
1
  ---
2
- import { getMediaItem } from "../../content/get-media-items"
2
+ import AudioPanel from "./components/AudioPanel.astro"
3
3
  import ContentSection from "./components/ContentSection.astro"
4
4
  import DescriptionSection from "./components/DescriptionSection.astro"
5
5
  import DetailsPage from "./components/DetailsPage.astro"
6
- import AudioPlayer from "./components/main-details/AudioPlayer.astro"
7
- import ShareButton from "./components/main-details/ShareButton.astro"
8
6
  import MainDetailsSection from "./components/MainDetailsSection.astro"
9
7
  import MediaCollectionsSection from "./components/MediaCollectionsSection.astro"
10
8
  import MoreDetailsSection from "./components/MoreDetailsSection.astro"
@@ -14,16 +12,13 @@ export type Props = {
14
12
  }
15
13
 
16
14
  const { mediaId } = Astro.props
17
- const audioSrc = (await getMediaItem(mediaId)).data.content[0].url
18
15
  ---
19
16
 
20
17
  <DetailsPage mediaId={mediaId}>
21
- <MainDetailsSection mediaId={mediaId}>
22
- <AudioPlayer src={audioSrc} className="mt-10 w-full" />
23
- <ShareButton className="mt-4 min-w-40" />
24
- </MainDetailsSection>
18
+ <MainDetailsSection thumbnailSize="md" mediaId={mediaId} />
19
+ <AudioPanel mediaId={mediaId} className="mt-10" />
25
20
  <DescriptionSection mediaId={mediaId} />
26
- <ContentSection mediaId={mediaId} />
21
+ <ContentSection mediaId={mediaId} minimumItems={1} />
27
22
  <MediaCollectionsSection mediaId={mediaId} />
28
23
  <MoreDetailsSection mediaId={mediaId} />
29
24
  </DetailsPage>
@@ -3,7 +3,6 @@ import ContentSection from "./components/ContentSection.astro"
3
3
  import DescriptionSection from "./components/DescriptionSection.astro"
4
4
  import DetailsPage from "./components/DetailsPage.astro"
5
5
  import OpenButton from "./components/main-details/OpenButton.astro"
6
- import ShareButton from "./components/main-details/ShareButton.astro"
7
6
  import MainDetailsSection from "./components/MainDetailsSection.astro"
8
7
  import MediaCollectionsSection from "./components/MediaCollectionsSection.astro"
9
8
  import MoreDetailsSection from "./components/MoreDetailsSection.astro"
@@ -18,11 +17,12 @@ const { mediaId, coverStyle, openActionLabel = "ln.details.open" } = Astro.props
18
17
  ---
19
18
 
20
19
  <DetailsPage mediaId={mediaId}>
21
- <MainDetailsSection mediaId={mediaId} coverStyle={coverStyle}>
22
- <div class="mt-10 flex flex-col justify-center gap-4 sm:justify-start">
23
- <OpenButton mediaId={mediaId} openActionLabel={openActionLabel} />
24
- <ShareButton />
25
- </div>
20
+ <MainDetailsSection mediaId={mediaId} thumbnailStyle={coverStyle}>
21
+ <OpenButton
22
+ className="mt-8"
23
+ mediaId={mediaId}
24
+ openActionLabel={openActionLabel}
25
+ />
26
26
  </MainDetailsSection>
27
27
  <DescriptionSection mediaId={mediaId} />
28
28
  <ContentSection mediaId={mediaId} />
@@ -0,0 +1,64 @@
1
+ ---
2
+ import { AstroError } from "astro/errors"
3
+
4
+ import { getMediaItem } from "../../../content/get-media-items"
5
+ import { createContentMetadata } from "../utils/create-content-metadata"
6
+ import AudioPlayer from "./AudioPlayer.astro"
7
+
8
+ interface Props {
9
+ mediaId: string
10
+ className?: string
11
+ }
12
+
13
+ const { mediaId, className } = Astro.props
14
+ const { t } = Astro.locals.i18n
15
+
16
+ const item = await getMediaItem(mediaId)
17
+
18
+ const content = item.data.content
19
+ .map((c) => createContentMetadata(c))
20
+ .filter((c) => c.extension === "mp3")
21
+
22
+ if (!content.length) {
23
+ throw new AstroError(
24
+ `Missing mp3 content for ${mediaId}`,
25
+ `Add at least one mp3 file to the content array of /src/media/${mediaId}.json`,
26
+ )
27
+ }
28
+ ---
29
+
30
+ <ol
31
+ class="mx-auto mt-10 flex max-w-screen-md flex-col gap-10 px-4 md:px-8"
32
+ class:list={[className]}
33
+ id="audio-panel"
34
+ >
35
+ {
36
+ content.map((c) => (
37
+ <li>
38
+ {content.length > 1 && <div class="mb-3 font-bold">{t(c.label)}</div>}
39
+ <AudioPlayer className="w-full" src={c.url} />
40
+ </li>
41
+ ))
42
+ }
43
+ </ol>
44
+ <script>
45
+ const audioPanel = document.getElementById("audio-panel")
46
+ const audios = Array.from(audioPanel?.querySelectorAll("audio") ?? [])
47
+
48
+ audios.forEach((audio, index) => {
49
+ audio.addEventListener("play", () => {
50
+ audios.forEach((otherAudio) => {
51
+ if (otherAudio !== audio) {
52
+ otherAudio.pause()
53
+ }
54
+ })
55
+ })
56
+
57
+ audio.addEventListener("ended", () => {
58
+ const nextAudio = audios[index + 1]
59
+ if (nextAudio) {
60
+ nextAudio.play()
61
+ }
62
+ })
63
+ })
64
+ </script>