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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # lightnet
2
2
 
3
+ ## 3.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#299](https://github.com/LightNetDev/LightNet/pull/299) [`ecaf1f6`](https://github.com/LightNetDev/LightNet/commit/ecaf1f698231c3e4eec700ca07cb78d1dbf4378c) Thanks [@smn-cds](https://github.com/smn-cds)! - Added a new `titleHref` prop to the `Section` component.
8
+
9
+ This enables section titles to act as anchors, allowing navigation to dedicated pages for expanded content. Useful for situations where content previews need to link to full listings.
10
+
11
+ - [#297](https://github.com/LightNetDev/LightNet/pull/297) [`537f6e5`](https://github.com/LightNetDev/LightNet/commit/537f6e50c8bbf7d9a22a17773acddf79fbd11c21) Thanks [@smn-cds](https://github.com/smn-cds)! - Add /api/versions.json endpoint to return the LightNet version.
12
+
13
+ - [#299](https://github.com/LightNetDev/LightNet/pull/299) [`ecaf1f6`](https://github.com/LightNetDev/LightNet/commit/ecaf1f698231c3e4eec700ca07cb78d1dbf4378c) Thanks [@smn-cds](https://github.com/smn-cds)! - Added filter parameters (`type`, `language`, `search`) to `searchPagePath` function.
14
+
15
+ - [#298](https://github.com/LightNetDev/LightNet/pull/298) [`b5c3c7a`](https://github.com/LightNetDev/LightNet/commit/b5c3c7a6637b9973acdbd34af3db1de5ed01c9f5) Thanks [@smn-cds](https://github.com/smn-cds)! - Remove client router
16
+
17
+ We have been relying on [Astro's ClientRouter](https://docs.astro.build/en/reference/modules/astro-transitions/#clientrouter-) for
18
+ view transitions between different pages.
19
+ With this release we remove the use of ClientRouter and switch to the browser built-in [ViewTransitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API).
20
+
21
+ - [#299](https://github.com/LightNetDev/LightNet/pull/299) [`ecaf1f6`](https://github.com/LightNetDev/LightNet/commit/ecaf1f698231c3e4eec700ca07cb78d1dbf4378c) Thanks [@smn-cds](https://github.com/smn-cds)! - ⚠️ Added new translations
22
+ - ln.previous: "Previous"
23
+ - ln.next: "Next"
24
+
25
+ - [#299](https://github.com/LightNetDev/LightNet/pull/299) [`ecaf1f6`](https://github.com/LightNetDev/LightNet/commit/ecaf1f698231c3e4eec700ca07cb78d1dbf4378c) Thanks [@smn-cds](https://github.com/smn-cds)! - Improved CategorySection
26
+ - added `carousel` layout
27
+ - replaced `image-grid` layout with `grid`
28
+ - ⚠️ removed `button-grid`
29
+ - ⚠️ changed the default layout from `button-grid` to `carousel`
30
+
31
+ ### Patch Changes
32
+
33
+ - [#299](https://github.com/LightNetDev/LightNet/pull/299) [`ecaf1f6`](https://github.com/LightNetDev/LightNet/commit/ecaf1f698231c3e4eec700ca07cb78d1dbf4378c) Thanks [@smn-cds](https://github.com/smn-cds)! - ⚠️ Removed `disableHorizontalPadding` property from Section component.
34
+
35
+ - [#299](https://github.com/LightNetDev/LightNet/pull/299) [`ecaf1f6`](https://github.com/LightNetDev/LightNet/commit/ecaf1f698231c3e4eec700ca07cb78d1dbf4378c) Thanks [@smn-cds](https://github.com/smn-cds)! - Accessibility improvements
36
+ - set `role=search` for SearchInput component
37
+ - set aria-label for Section component
38
+
39
+ ## 3.7.0
40
+
41
+ ### Minor Changes
42
+
43
+ - [#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
44
+ - Audio details page shows embedded player for each mp3 inside the media item's content array
45
+ - Audio player stops playing a mp3 when the next is started
46
+ - Audio player resumes with next mp3 when one mp3 ends
47
+
48
+ ### Patch Changes
49
+
50
+ - [#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
51
+
52
+ If you are using npm as the package manager, you can remove these
53
+ dependencies from your `package.json`.
54
+ - sharp
55
+ - @astrojs/react
56
+ - @astrojs/tailwind
57
+
58
+ - [#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
59
+ - Design of `ShareButton` has changed from a gray button to a underlined text
60
+ - `MainDetailsSection` now already contains the `ShareButton`
61
+
3
62
  ## 3.6.1
4
63
 
5
64
  ### Patch Changes
package/README.md CHANGED
@@ -1,25 +1,37 @@
1
- # LightNet
1
+ ![LightNet](https://github.com/LightNetDev/lightnet/blob/main/lightnet-banner.webp)
2
2
 
3
- Share the message of Jesus and strengthen believers worldwide.
3
+ Share the gospel and strengthen believers in your community.
4
4
 
5
- LightNet empowers ministries to run their own digital media libraries. They can easily share content in the heart language of the communities they serve - including videos, audio, images, and documents.
5
+ LightNet makes it easy to **run your own digital media library**, so more people can find what they need and grow in faith.
6
6
 
7
- It is built as an integration for the [Astro framework](https://astro.build), enabling the creation of statically generated sites that can be hosted on any file server. These sites are fast, easily extendable, and fully support internationalization.
7
+ Built as an integration for the [Astro framework](https://astro.build), LightNet enables the creation of fast, statically generated websites that can be easily hosted on any file server.
8
+
9
+ Learn more on the [LightNet homepage](https://lightnet.community).
10
+
11
+ ## Start Your Own Library
12
+
13
+ Get up and running quickly with LightNet by exploring its features and best practices. You can build your own media library by starting with the [LightNet example template](https://github.com/LightNetDev/example-template), which creates a local copy of a demo site for a fictional skateboard ministry. This beginner-friendly template serves as a great starting point for developers.
14
+
15
+ To get started, simply run the following command in your terminal:
16
+
17
+ ```bash
18
+ npm create astro@latest -- --template LightNetDev/example-template
19
+ ```
20
+
21
+ This will set up the demo site that you can customize and expand to meet your community’s needs.
8
22
 
9
23
  ## Documentation
10
24
 
11
- [Read the LightNet docs](https://docs.lightnet.community) to learn how to use LightNet.
25
+ Need help? [Explore the LightNet developer docs](https://docs.lightnet.community) for everything you need to get started and make the most of LightNet.
12
26
 
13
- ## Example site
27
+ ## Showcase
14
28
 
15
- [Check out the example site](/examples/sk8-ministries/) showcasing LightNet in action for a fictional skateboard ministry.
29
+ Want to see LightNet in action? [Visit the MediaWorks digital library](https://library.mediaworks.global) and see how it powers a real-world digital library.
16
30
 
17
31
  ## Contributing
18
32
 
19
- We would love to partner with you. [Visit the contribution guide](https://github.com/LightNetDev/lightnet/blob/main/CONTRIBUTING.md) to learn how to help with LightNet development.
33
+ Want to help improve LightNet? [Check out the contribution guide](https://github.com/LightNetDev/lightnet/blob/main/CONTRIBUTING.md) to learn how you can get involved and make a difference!
20
34
 
21
35
  ## License
22
36
 
23
- MIT
24
-
25
- Copyright (c) 2024–present [LightNet contributors](https://github.com/LightNetDev/LightNet/graphs/contributors)
37
+ LightNet is licensed under the MIT License. See the full details in the [LICENSE](https://github.com/LightNetDev/lightnet/blob/main/LICENSE) file.
@@ -2,13 +2,17 @@
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
9
- export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.11.0_@types+node@24.0.12_jiti@2.4.2_lightningcss@1.29.1_rollup@4.44.2_terser@5.39.0_typescript@5.8.3_yaml@2.8.0/node_modules/astro/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.11.0_@types+node@24.0.12_jiti@2.4.2_lightningcss@1.29.1_rollup@4.44.2_terser@5.39.0_typescript@5.8.3_yaml@2.8.0/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules"
13
+ export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.12.8_@types+node@24.2.0_jiti@2.4.2_lightningcss@1.29.1_rollup@4.46.2_terser@5.39.0_typescript@5.9.2_yaml@2.8.1/node_modules/astro/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.12.8_@types+node@24.2.0_jiti@2.4.2_lightningcss@1.29.1_rollup@4.46.2_terser@5.39.0_typescript@5.9.2_yaml@2.8.1/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules"
10
14
  else
11
- export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.11.0_@types+node@24.0.12_jiti@2.4.2_lightningcss@1.29.1_rollup@4.44.2_terser@5.39.0_typescript@5.8.3_yaml@2.8.0/node_modules/astro/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.11.0_@types+node@24.0.12_jiti@2.4.2_lightningcss@1.29.1_rollup@4.44.2_terser@5.39.0_typescript@5.8.3_yaml@2.8.0/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules:$NODE_PATH"
15
+ export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.12.8_@types+node@24.2.0_jiti@2.4.2_lightningcss@1.29.1_rollup@4.46.2_terser@5.39.0_typescript@5.9.2_yaml@2.8.1/node_modules/astro/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.12.8_@types+node@24.2.0_jiti@2.4.2_lightningcss@1.29.1_rollup@4.46.2_terser@5.39.0_typescript@5.9.2_yaml@2.8.1/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules:$NODE_PATH"
12
16
  fi
13
17
  if [ -x "$basedir/node" ]; then
14
18
  exec "$basedir/node" "$basedir/../astro/astro.js" "$@"
@@ -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,13 +2,17 @@
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
9
- export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules"
13
+ export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules"
10
14
  else
11
- export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules:$NODE_PATH"
15
+ export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules:$NODE_PATH"
12
16
  fi
13
17
  if [ -x "$basedir/node" ]; then
14
18
  exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
@@ -2,13 +2,17 @@
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
9
- export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules"
13
+ export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules"
10
14
  else
11
- export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules:$NODE_PATH"
15
+ export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/bin/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules/typescript/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/typescript@5.9.2/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules:$NODE_PATH"
12
16
  fi
13
17
  if [ -x "$basedir/node" ]; then
14
18
  exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
@@ -6,13 +6,16 @@
6
6
  "dependencies": {
7
7
  "@astrojs/react": "^4.3.0",
8
8
  "@astrojs/tailwind": "^6.0.2",
9
- "@lightnet/decap-admin": "^3.1.1",
10
- "astro": "^5.11.0",
11
- "lightnet": "^3.6.0",
12
- "react": "^19.1.0",
13
- "react-dom": "^19.1.0",
14
- "sharp": "^0.33.5",
9
+ "@lightnet/decap-admin": "^3.1.2",
10
+ "astro": "^5.12.8",
11
+ "lightnet": "^3.8.0",
12
+ "react": "^19.1.1",
13
+ "react-dom": "^19.1.1",
14
+ "sharp": "^0.34.3",
15
15
  "tailwindcss": "^3.4.17",
16
- "typescript": "^5.8.3"
16
+ "typescript": "^5.9.2"
17
+ },
18
+ "engines": {
19
+ "node": ">=22"
17
20
  }
18
21
  }
@@ -52,3 +52,7 @@ test("Should remove block quotes", () => {
52
52
  "block quote\nmore quote",
53
53
  )
54
54
  })
55
+
56
+ test("Should return undefined when markdown is undefined", () => {
57
+ expect(markdownToText(undefined)).toBeUndefined()
58
+ })
@@ -0,0 +1,27 @@
1
+ import config from "virtual:lightnet/config"
2
+ import projectContext from "virtual:lightnet/project-context"
3
+ import { expect, test } from "vitest"
4
+
5
+ import { isExternalUrl } from "../../src/utils/urls"
6
+
7
+ // relative path should be treated as internal
8
+ test("Should treat relative paths as internal", () => {
9
+ expect(isExternalUrl("/page")).toBe(false)
10
+ })
11
+
12
+ // absolute url that matches the configured site should be internal
13
+ test("Should treat URLs matching projectContext.site as internal", () => {
14
+ expect(isExternalUrl(`${projectContext.site}/page`)).toBe(false)
15
+ })
16
+
17
+ // domains listed in internalDomains should be treated as internal
18
+ test("Should treat configured internalDomains as internal", () => {
19
+ config.internalDomains.push("internal.test")
20
+ expect(isExternalUrl("https://internal.test/foo")).toBe(false)
21
+ config.internalDomains.pop()
22
+ })
23
+
24
+ // any other absolute url should be external
25
+ test("Should treat other absolute URLs as external", () => {
26
+ expect(isExternalUrl("https://example.com")).toBe(true)
27
+ })
@@ -0,0 +1 @@
1
+ export { default as CarouselSection } from "../src/components/CarouselSection.astro"
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "lightnet",
3
3
  "type": "module",
4
4
  "license": "MIT",
5
- "version": "3.6.1",
5
+ "version": "3.8.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/LightNetDev/lightnet",
@@ -20,6 +20,7 @@
20
20
  "./content": "./exports/content.ts",
21
21
  "./utils": "./exports/utils.ts",
22
22
  "./components": "./exports/components.ts",
23
+ "./experimental-components": "./exports/experimental-components.ts",
23
24
  "./experimental-details-page": "./exports/details-page.ts",
24
25
  "./i18n": "./exports/i18n.ts",
25
26
  "./locals": "./src/i18n/locals.ts",
@@ -29,35 +30,41 @@
29
30
  "./pages/RootRoute.astro": "./src/pages/RootRoute.astro",
30
31
  "./pages/SearchPageRoute.astro": "./src/pages/search-page/SearchPageRoute.astro",
31
32
  "./pages/DetailsPageRoute.astro": "./src/pages/details-page/DetailsPageRoute.astro",
32
- "./pages/api/search.ts": "./src/pages/api/search.ts"
33
+ "./pages/api/search.ts": "./src/pages/api/search.ts",
34
+ "./pages/api/versions.ts": "./src/pages/api/versions.ts"
33
35
  },
34
36
  "peerDependencies": {
35
- "@astrojs/react": "^4.1.0",
36
- "@astrojs/tailwind": "^6.0.0",
37
37
  "astro": "^5.1.0",
38
38
  "react": "^19.0.0",
39
39
  "react-dom": "^19.0.0",
40
- "sharp": "^0.33.4",
41
- "tailwindcss": "^3.4.17",
42
- "typescript": "^5.5.3"
40
+ "tailwindcss": ">=3.4.0 <4.0.0"
43
41
  },
44
42
  "dependencies": {
43
+ "@astrojs/react": "^4.3.0",
44
+ "@astrojs/tailwind": "^6.0.2",
45
45
  "@iconify-json/mdi": "^1.2.3",
46
46
  "@iconify/tailwind": "^1.2.0",
47
47
  "@tailwindcss/typography": "^0.5.16",
48
48
  "@tanstack/react-virtual": "^3.13.12",
49
- "@types/react": "^19.1.8",
50
49
  "daisyui": "^4.12.24",
50
+ "embla-carousel": "^8.6.0",
51
+ "embla-carousel-auto-height": "^8.6.0",
52
+ "embla-carousel-wheel-gestures": "^8.0.2",
51
53
  "fuse.js": "^7.1.0",
52
54
  "i18next": "^25.3.2",
53
- "marked": "^16.0.0",
54
- "yaml": "^2.8.0"
55
+ "marked": "^16.1.2",
56
+ "yaml": "^2.8.1"
55
57
  },
56
58
  "devDependencies": {
57
- "@playwright/test": "^1.53.2",
58
- "@types/node": "^22.16.2",
59
+ "@playwright/test": "^1.54.2",
60
+ "@types/node": "^22.17.0",
61
+ "@types/react": "^19.1.9",
62
+ "typescript": "^5.9.2",
59
63
  "vitest": "^3.2.4"
60
64
  },
65
+ "engines": {
66
+ "node": ">=22"
67
+ },
61
68
  "scripts": {
62
69
  "test": "vitest",
63
70
  "e2e": "playwright install --with-deps chromium && playwright test"
@@ -51,6 +51,12 @@ export function lightnet(lightnetConfig: LightnetConfig): AstroIntegration {
51
51
  prerender: true,
52
52
  })
53
53
 
54
+ injectRoute({
55
+ pattern: "/api/versions.json",
56
+ entrypoint: "lightnet/pages/api/versions.ts",
57
+ prerender: true,
58
+ })
59
+
54
60
  injectRoute({
55
61
  pattern: "/[locale]/media/[mediaId]",
56
62
  entrypoint: "lightnet/pages/DetailsPageRoute.astro",
@@ -0,0 +1,182 @@
1
+ ---
2
+ import Icon from "./Icon"
3
+ import Section, { type Props as SectionProps } from "./Section.astro"
4
+
5
+ type Props = SectionProps
6
+
7
+ const { titleClass = "", ...props } = Astro.props
8
+ const { t, direction } = Astro.locals.i18n
9
+ ---
10
+
11
+ <Section {...props} titleClass={"!mb-8 sm:!-mb-1 " + titleClass}>
12
+ <ln-carousel data-direction={direction} aria-roledescription="carousel">
13
+ <div class="mb-2 hidden justify-end gap-1 sm:flex">
14
+ <button
15
+ data-button-prev
16
+ aria-label={t("ln.previous")}
17
+ class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-gray-200 bg-gray-200 text-gray-600 transition-colors ease-in-out hover:border-gray-400 disabled:bg-transparent disabled:text-gray-300 disabled:hover:border-gray-200"
18
+ ><Icon
19
+ className="mdi--chevron-left"
20
+ ariaLabel=""
21
+ flipIcon={direction === "rtl"}
22
+ /></button
23
+ >
24
+ <button
25
+ data-button-next
26
+ aria-label={t("ln.next")}
27
+ class="flex h-9 w-9 items-center justify-center rounded-full border-2 border-gray-200 bg-gray-200 text-gray-600 transition-colors ease-in-out hover:border-gray-400 disabled:bg-transparent disabled:text-gray-300 disabled:hover:border-gray-200"
28
+ ><Icon
29
+ className="mdi--chevron-right"
30
+ ariaLabel=""
31
+ flipIcon={direction === "rtl"}
32
+ /></button
33
+ >
34
+ </div>
35
+
36
+ <div data-carousel class="-m-4 overflow-hidden p-4">
37
+ <ol
38
+ class="flex items-end gap-4 md:gap-8"
39
+ aria-atomic="false"
40
+ aria-live="polite"
41
+ data-carousel-container
42
+ >
43
+ <slot />
44
+ </ol>
45
+ </div>
46
+ </ln-carousel>
47
+
48
+ <script>
49
+ import EmblaCarousel from "embla-carousel"
50
+ import AutoHeightPlugin from "embla-carousel-auto-height"
51
+ import { WheelGesturesPlugin } from "embla-carousel-wheel-gestures"
52
+
53
+ class Carousel extends HTMLElement {
54
+ preloadIndex = 0
55
+
56
+ connectedCallback() {
57
+ const carouselNode = this.querySelector(
58
+ "[data-carousel]",
59
+ ) as HTMLElement
60
+ const direction = this.dataset.direction as "ltr" | "rtl"
61
+ const carousel = EmblaCarousel(
62
+ carouselNode,
63
+ {
64
+ slidesToScroll: "auto",
65
+ skipSnaps: true,
66
+ direction,
67
+ },
68
+ [AutoHeightPlugin(), WheelGesturesPlugin({ forceWheelAxis: "x" })],
69
+ )
70
+ const prevBtn = this.querySelector(
71
+ "[data-button-prev]",
72
+ ) as HTMLButtonElement
73
+ const nextBtn = this.querySelector(
74
+ "[data-button-next]",
75
+ ) as HTMLButtonElement
76
+
77
+ prevBtn.addEventListener("click", () => carousel.scrollPrev())
78
+ nextBtn.addEventListener("click", () => carousel.scrollNext())
79
+
80
+ const updateArrowButtons = () => {
81
+ if (carousel.canScrollPrev()) {
82
+ prevBtn.removeAttribute("disabled")
83
+ } else {
84
+ prevBtn.setAttribute("disabled", "disabled")
85
+ }
86
+
87
+ if (carousel.canScrollNext()) {
88
+ nextBtn.removeAttribute("disabled")
89
+ } else {
90
+ nextBtn.setAttribute("disabled", "disabled")
91
+ }
92
+ }
93
+ carousel
94
+ .on("init", updateArrowButtons)
95
+ .on("select", updateArrowButtons)
96
+ .on("reInit", updateArrowButtons)
97
+
98
+ // when images are set to loading=lazy, safari and
99
+ // firefox fail to preload them before they are
100
+ // visible. We improve the browser heuristics
101
+ // by setting the next chunk of images to load
102
+ // eagerly.
103
+ const slideNodes =
104
+ this.querySelector("[data-carousel-container]")?.children ?? []
105
+ const preloadNextSlides = () => {
106
+ const slidesInView = carousel.slidesInView()
107
+ slidesInView.forEach((slideIndex) => {
108
+ const preloadIndex = slideIndex + slidesInView.length
109
+ if (preloadIndex > this.preloadIndex) {
110
+ this.preloadIndex = preloadIndex
111
+ const node = slideNodes[preloadIndex]
112
+ node?.querySelectorAll("img").forEach((img) => {
113
+ img.loading = "eager"
114
+ })
115
+ }
116
+ })
117
+ }
118
+
119
+ // start preloading once the carousel enters the viewport
120
+ const observer = new IntersectionObserver(([target]) => {
121
+ if (!target.isIntersecting) {
122
+ return
123
+ }
124
+ preloadNextSlides()
125
+ carousel.on("slidesInView", () => preloadNextSlides())
126
+ observer.unobserve(target.target)
127
+ })
128
+ observer.observe(carouselNode)
129
+ }
130
+ }
131
+ customElements.define("ln-carousel", Carousel)
132
+ </script>
133
+ </Section>
134
+ <style is:global>
135
+ .carousel-item--wide {
136
+ flex: 0 0 auto;
137
+ /* 1 column + part of the next */
138
+ width: 85%;
139
+ }
140
+
141
+ .carousel-item--narrow {
142
+ flex: 0 0 auto;
143
+ /* 2 columns + part of the next */
144
+ width: calc((100% - 1rem) / 2.3);
145
+ }
146
+
147
+ /* sm */
148
+ @media (min-width: 640px) {
149
+ .carousel-item--wide {
150
+ /* 2 columns */
151
+ width: calc((100% - 1rem) / 2);
152
+ }
153
+ .carousel-item--narrow {
154
+ /* 3 columns */
155
+ width: calc((100% - 2rem) / 3);
156
+ }
157
+ }
158
+
159
+ /* md */
160
+ @media (min-width: 768px) {
161
+ .carousel-item--wide {
162
+ /* 3 columns */
163
+ width: calc((100% - 4rem) / 3);
164
+ }
165
+ .carousel-item--narrow {
166
+ /* 4 columns */
167
+ width: calc((100% - 6rem) / 4);
168
+ }
169
+ }
170
+
171
+ /* lg */
172
+ @media (min-width: 1024px) {
173
+ .carousel-item--wide {
174
+ /* 4 columns */
175
+ width: calc((100% - 6rem) / 4);
176
+ }
177
+ .carousel-item--narrow {
178
+ /* 5 columns */
179
+ width: calc((100% - 8rem) / 5);
180
+ }
181
+ }
182
+ </style>