lightnet 3.6.0 → 3.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # lightnet
2
2
 
3
+ ## 3.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#288](https://github.com/LightNetDev/LightNet/pull/288) [`008553f`](https://github.com/LightNetDev/LightNet/commit/008553f7c016931b617ba2c632e0b473e32bad8d) Thanks [@smn-cds](https://github.com/smn-cds)! - Improves support for media items with more than one audio file
8
+ - Audio details page shows embedded player for each mp3 inside the media item's content array
9
+ - Audio player stops playing a mp3 when the next is started
10
+ - Audio player resumes with next mp3 when one mp3 ends
11
+
12
+ ### Patch Changes
13
+
14
+ - [#288](https://github.com/LightNetDev/LightNet/pull/288) [`008553f`](https://github.com/LightNetDev/LightNet/commit/008553f7c016931b617ba2c632e0b473e32bad8d) Thanks [@smn-cds](https://github.com/smn-cds)! - Reorganized dependencies
15
+
16
+ If you are using npm as the package manager, you can remove these
17
+ dependencies from your `package.json`.
18
+ - sharp
19
+ - @astrojs/react
20
+ - @astrojs/tailwind
21
+
22
+ - [#288](https://github.com/LightNetDev/LightNet/pull/288) [`008553f`](https://github.com/LightNetDev/LightNet/commit/008553f7c016931b617ba2c632e0b473e32bad8d) Thanks [@smn-cds](https://github.com/smn-cds)! - ‼️ Breaking changes on experimental custom details page components
23
+ - Design of `ShareButton` has changed from a gray button to a underlined text
24
+ - `MainDetailsSection` now already contains the `ShareButton`
25
+
26
+ ## 3.6.1
27
+
28
+ ### Patch Changes
29
+
30
+ - [#286](https://github.com/LightNetDev/LightNet/pull/286) [`4c3b137`](https://github.com/LightNetDev/LightNet/commit/4c3b13762357d63f486261ba8eb8202e666dfb55) Thanks [@NorthernMoodler](https://github.com/NorthernMoodler)! - Refine de, ru and uk language strings
31
+
3
32
  ## 3.6.0
4
33
 
5
34
  ### Minor Changes
@@ -2,7 +2,11 @@
2
2
  basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
3
 
4
4
  case `uname` in
5
- *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
6
10
  esac
7
11
 
8
12
  if [ -z "$NODE_PATH" ]; then
@@ -2,7 +2,11 @@
2
2
  basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
3
 
4
4
  case `uname` in
5
- *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
6
10
  esac
7
11
 
8
12
  if [ -z "$NODE_PATH" ]; then
@@ -2,7 +2,11 @@
2
2
  basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
3
 
4
4
  case `uname` in
5
- *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
6
10
  esac
7
11
 
8
12
  if [ -z "$NODE_PATH" ]; then
@@ -2,7 +2,11 @@
2
2
  basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
3
 
4
4
  case `uname` in
5
- *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
6
10
  esac
7
11
 
8
12
  if [ -z "$NODE_PATH" ]; then
@@ -2,7 +2,11 @@
2
2
  basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
3
 
4
4
  case `uname` in
5
- *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
6
10
  esac
7
11
 
8
12
  if [ -z "$NODE_PATH" ]; then
@@ -8,7 +8,7 @@
8
8
  "@astrojs/tailwind": "^6.0.2",
9
9
  "@lightnet/decap-admin": "^3.1.1",
10
10
  "astro": "^5.11.0",
11
- "lightnet": "^3.6.0",
11
+ "lightnet": "^3.7.0",
12
12
  "react": "^19.1.0",
13
13
  "react-dom": "^19.1.0",
14
14
  "sharp": "^0.33.5",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "lightnet",
3
3
  "type": "module",
4
4
  "license": "MIT",
5
- "version": "3.6.0",
5
+ "version": "3.7.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/LightNetDev/lightnet",
@@ -32,21 +32,18 @@
32
32
  "./pages/api/search.ts": "./src/pages/api/search.ts"
33
33
  },
34
34
  "peerDependencies": {
35
- "@astrojs/react": "^4.1.0",
36
- "@astrojs/tailwind": "^6.0.0",
37
35
  "astro": "^5.1.0",
38
36
  "react": "^19.0.0",
39
37
  "react-dom": "^19.0.0",
40
- "sharp": "^0.33.4",
41
- "tailwindcss": "^3.4.17",
42
- "typescript": "^5.5.3"
38
+ "tailwindcss": ">=3.4.0 <4.0.0"
43
39
  },
44
40
  "dependencies": {
45
41
  "@iconify-json/mdi": "^1.2.3",
46
42
  "@iconify/tailwind": "^1.2.0",
43
+ "@astrojs/react": "^4.1.0",
44
+ "@astrojs/tailwind": "^6.0.0",
47
45
  "@tailwindcss/typography": "^0.5.16",
48
46
  "@tanstack/react-virtual": "^3.13.12",
49
- "@types/react": "^19.1.8",
50
47
  "daisyui": "^4.12.24",
51
48
  "fuse.js": "^7.1.0",
52
49
  "i18next": "^25.3.2",
@@ -54,6 +51,8 @@
54
51
  "yaml": "^2.8.0"
55
52
  },
56
53
  "devDependencies": {
54
+ "typescript": "^5.5.3",
55
+ "@types/react": "^19.1.8",
57
56
  "@playwright/test": "^1.53.2",
58
57
  "@types/node": "^22.16.2",
59
58
  "vitest": "^3.2.4"
@@ -5,7 +5,7 @@ ln.category: Kategorie
5
5
  ln.language: Sprache
6
6
  ln.languages: Sprachen
7
7
  ln.type: Typ
8
- ln.external-link: Externer link
8
+ ln.external-link: Externer Link
9
9
  ln.details.open: Öffnen
10
10
  ln.details.part-of-collection: Teil der Sammlung
11
11
  ln.details.download: Download
@@ -17,6 +17,6 @@ ln.details.open: Открыть
17
17
  ln.details.share: Поделиться
18
18
  ln.details.part-of-collection: Часть коллекции
19
19
  ln.details.download: Скачать
20
- ln.share.url-copied-to-clipboard: ссылка скопированa в буфер обмена
20
+ ln.share.url-copied-to-clipboard: Ссылка скопированa в буфер
21
21
  ln.404.page-not-found: Страница не найдена
22
22
  ln.404.go-to-the-home-page: Перейти на главную страницу
@@ -17,6 +17,6 @@ ln.details.open: Відкрити
17
17
  ln.details.share: Поділитися
18
18
  ln.details.part-of-collection: Частина колекції
19
19
  ln.details.download: Завантажити
20
- ln.share.url-copied-to-clipboard: посилання скопійовано в буфер обміну
20
+ ln.share.url-copied-to-clipboard: Посилання скопійовано до буферу обміну
21
21
  ln.404.page-not-found: Сторінку не знайдено
22
22
  ln.404.go-to-the-home-page: Перейти на Головну сторінку
@@ -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,73 @@
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
+ document.addEventListener("astro:after-swap", () => {
46
+ initAudioPanel()
47
+ })
48
+ initAudioPanel()
49
+ function initAudioPanel() {
50
+ const audioPanel = document.getElementById("audio-panel")
51
+ if (!audioPanel) {
52
+ return
53
+ }
54
+ const audios = Array.from(audioPanel.querySelectorAll("audio"))
55
+
56
+ audios.forEach((audio, index) => {
57
+ audio.addEventListener("play", () => {
58
+ audios.forEach((otherAudio) => {
59
+ if (otherAudio !== audio) {
60
+ otherAudio.pause()
61
+ }
62
+ })
63
+ })
64
+
65
+ audio.addEventListener("ended", () => {
66
+ const nextAudio = audios[index + 1]
67
+ if (nextAudio) {
68
+ nextAudio.play()
69
+ }
70
+ })
71
+ })
72
+ }
73
+ </script>
@@ -6,7 +6,7 @@ type Props = {
6
6
  className?: string
7
7
  }
8
8
  const { src, className } = Astro.props
9
- if (!src.endsWith(".mp3")) {
9
+ if (!src.toLowerCase().endsWith(".mp3")) {
10
10
  throw new AstroError(
11
11
  `Unsupported audio file ${src}`,
12
12
  "To fix the issue, reference a mp3 file.",
@@ -8,11 +8,14 @@ import {
8
8
 
9
9
  interface Props {
10
10
  mediaId: string
11
+ minimumItems?: number
11
12
  }
12
13
 
13
- const item = await getMediaItem(Astro.props.mediaId)
14
+ const { mediaId, minimumItems = 2 } = Astro.props
14
15
 
15
- if (item.data.content.length < 2) {
16
+ const item = await getMediaItem(mediaId)
17
+
18
+ if (item.data.content.length < minimumItems) {
16
19
  return
17
20
  }
18
21
 
@@ -17,7 +17,7 @@ const { direction, code: lang } = resolveLanguage(language)
17
17
  description && (
18
18
  <div
19
19
  dir={direction}
20
- class="prose prose-gray mx-auto mt-16 max-w-screen-md px-4 md:mt-20 md:px-8"
20
+ class="prose prose-gray mx-auto mt-8 max-w-screen-md px-4 md:mt-12 md:px-8"
21
21
  set:html={await markdownToHtml(description)}
22
22
  lang={lang}
23
23
  id="description"
@@ -1,23 +1,26 @@
1
1
  ---
2
2
  import Authors from "./main-details/Authors.astro"
3
- import Cover from "./main-details/Cover.astro"
3
+ import ShareButton from "./main-details/ShareButton.astro"
4
+ import Thumbnail from "./main-details/Thumbnail.astro"
4
5
  import Title from "./main-details/Title.astro"
5
6
 
6
7
  export type Props = {
7
8
  mediaId: string
8
- coverStyle?: "default" | "book"
9
+ thumbnailStyle?: "default" | "book"
10
+ thumbnailSize?: "md" | "lg"
9
11
  }
10
12
 
11
- const { mediaId, coverStyle = "default" } = Astro.props
13
+ const { mediaId, thumbnailStyle, thumbnailSize } = Astro.props
12
14
  ---
13
15
 
14
16
  <div
15
17
  class="mx-auto mt-10 flex max-w-screen-md flex-col items-center gap-8 px-4 sm:mt-20 sm:flex-row sm:items-start sm:gap-14 md:px-8"
16
18
  >
17
- <Cover mediaId={mediaId} style={coverStyle} />
19
+ <Thumbnail size={thumbnailSize} mediaId={mediaId} style={thumbnailStyle} />
18
20
  <div class="flex w-full grow flex-col items-center sm:items-start">
19
21
  <Title className="text-center sm:text-start" mediaId={mediaId} />
20
- <Authors className="text-center sm:text-start" mediaId={mediaId} />
22
+ <Authors className="mt-2" mediaId={mediaId} />
23
+ <ShareButton className="mt-3" />
21
24
  <slot />
22
25
  </div>
23
26
  </div>
@@ -26,5 +26,5 @@ const item = await getMediaItem(mediaId)
26
26
  >
27
27
  <Title mediaId={mediaId} />
28
28
  <Authors mediaId={mediaId} />
29
- <ShareButton className="mt-6 md:mt-10" />
29
+ <ShareButton className="mt-3" />
30
30
  </div>
@@ -12,8 +12,8 @@ const authors = item.data.authors?.join(", ")
12
12
 
13
13
  {
14
14
  authors && (
15
- <p class="mt-2 text-xl sm:text-2xl" class:list={Astro.props.className}>
15
+ <div class="text-xl sm:text-2xl" class:list={Astro.props.className}>
16
16
  {authors}
17
- </p>
17
+ </div>
18
18
  )
19
19
  }
@@ -6,8 +6,9 @@ import { createContentMetadata } from "../../utils/create-content-metadata"
6
6
  interface Props {
7
7
  mediaId: string
8
8
  openActionLabel: string
9
+ className?: string
9
10
  }
10
- const { mediaId, openActionLabel } = Astro.props
11
+ const { mediaId, openActionLabel, className } = Astro.props
11
12
  const item = await getMediaItem(mediaId)
12
13
  const content = createContentMetadata(item.data.content[0])
13
14
  ---
@@ -17,6 +18,7 @@ const content = createContentMetadata(item.data.content[0])
17
18
  href={content.url}
18
19
  target={content.target}
19
20
  hreflang={item.data.language}
21
+ class:list={[className]}
20
22
  >
21
23
  {
22
24
  content.isExternal && (
@@ -7,7 +7,7 @@ interface Props {
7
7
  ---
8
8
 
9
9
  <button
10
- class="flex cursor-pointer items-center justify-center gap-2 rounded-2xl bg-gray-200 px-6 py-3 font-bold uppercase text-gray-600 shadow-sm hover:bg-gray-300"
10
+ class="flex cursor-pointer items-center gap-2 font-bold text-gray-700 underline"
11
11
  class:list={[Astro.props.className]}
12
12
  id="share-btn"
13
13
  ><Icon className="mdi--share" ariaLabel="" />
@@ -6,12 +6,23 @@ import { getMediaItem } from "../../../../content/get-media-items"
6
6
  interface Props {
7
7
  mediaId: string
8
8
  style?: "default" | "book"
9
+ size?: "md" | "lg"
9
10
  }
10
- const { mediaId, style = "default" } = Astro.props
11
+ const { mediaId, style = "default", size = "lg" } = Astro.props
11
12
 
12
13
  const item = await getMediaItem(mediaId)
13
14
  const image = item.data.image
14
15
  const isPortraitImage = image.height > image.width
16
+ const imageSizes = {
17
+ md: {
18
+ css: isPortraitImage ? "h-52 w-auto" : "h-auto w-52",
19
+ query: "13rem",
20
+ },
21
+ lg: {
22
+ css: isPortraitImage ? "h-52 w-auto sm:h-72" : "h-auto w-52 sm:w-72",
23
+ query: "(max-width: 640px) 13rem, 18rem",
24
+ },
25
+ }
15
26
  ---
16
27
 
17
28
  <div
@@ -19,10 +30,10 @@ const isPortraitImage = image.height > image.width
19
30
  class:list={style === "book" ? "rounded-sm" : "rounded-md"}
20
31
  >
21
32
  <Image
22
- class:list={isPortraitImage ? "h-52 w-auto sm:h-72" : "h-auto w-52 sm:w-72"}
33
+ class:list={imageSizes[size].css}
23
34
  alt=""
24
35
  widths={[256, 512, 768, 1024]}
25
- sizes="(max-width: 640px) 13rem, 18rem"
36
+ sizes={imageSizes[size].query}
26
37
  src={image}
27
38
  quality="high"
28
39
  loading="eager"