Merge branch 'main' into feat/picture-component

This commit is contained in:
Erika 2023-10-06 19:09:36 +02:00 committed by GitHub
commit 73a06292bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
402 changed files with 88373 additions and 6541 deletions

View file

@ -113,7 +113,7 @@ jobs:
- os: macos-latest
NODE_VERSION: 18
- os: windows-latest
NODE_VERSION: 18
NODE_VERSION: 18.17.1
fail-fast: false
env:
NODE_VERSION: ${{ matrix.NODE_VERSION }}
@ -155,7 +155,7 @@ jobs:
strategy:
matrix:
OS: [ubuntu-latest, windows-latest]
NODE_VERSION: [18]
NODE_VERSION: [18.17.1]
fail-fast: false
env:
NODE_VERSION: ${{ matrix.NODE_VERSION }}

View file

@ -22,6 +22,9 @@ jobs:
name: Changelog PR or Release
if: ${{ github.repository_owner == 'withastro' }}
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v3

View file

@ -81,6 +81,8 @@ jobs:
id: publish
run: |
pnpm run release --tag next--${{ steps.getSnapshotName.outputs.result }} > publish.output.txt 2>&1
echo "Release complete"
cat publish.output.txt
echo ::set-output name=result::`cat publish.output.txt`
env:
# Needs access to publish to npm

View file

@ -11,6 +11,6 @@
"astro": "astro"
},
"dependencies": {
"astro": "^3.1.2"
"astro": "^3.2.3"
}
}

View file

@ -11,9 +11,9 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/mdx": "^1.1.0",
"@astrojs/mdx": "^1.1.1",
"@astrojs/rss": "^3.0.0",
"@astrojs/sitemap": "^3.0.0",
"astro": "^3.1.2"
"@astrojs/sitemap": "^3.0.1",
"astro": "^3.2.3"
}
}

View file

@ -15,7 +15,7 @@
],
"scripts": {},
"devDependencies": {
"astro": "^3.1.2"
"astro": "^3.2.3"
},
"peerDependencies": {
"astro": "^2.0.0-beta.0"

View file

@ -1 +0,0 @@
FROM node:18-bullseye

View file

@ -1,4 +0,0 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

View file

@ -1,11 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View file

@ -1,54 +0,0 @@
# Astro Starter Kit: Deno
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/deno)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/deno)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/deno/devcontainer.json)
```sh
npm create astro@latest -- --template deno
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
![basics](https://user-images.githubusercontent.com/4677417/186188965-73453154-fdec-4d6b-9c34-cb35c248ae5b.png)
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
│ └── favicon.svg
├── src/
│ ├── components/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
├── package.json
└── tsconfig.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| | (preview uses Deno CLI) |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View file

@ -1,9 +0,0 @@
import { defineConfig } from 'astro/config';
import deno from '@astrojs/deno';
// https://astro.build/config
export default defineConfig({
output: 'server',
adapter: deno(),
});

View file

@ -1,18 +0,0 @@
{
"name": "@example/deno",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs",
"astro": "astro"
},
"dependencies": {
"astro": "^3.1.2"
},
"devDependencies": {
"@astrojs/deno": "^5.0.0"
}
}

View file

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

View file

@ -1,61 +0,0 @@
---
interface Props {
title: string;
}
const { title } = Astro.props as Props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>{title}</title>
</head>
<body>
<slot />
<style>
:root {
--font-size-base: clamp(1rem, 0.34vw + 0.91rem, 1.19rem);
--font-size-lg: clamp(1.2rem, 0.7vw + 1.2rem, 1.5rem);
--font-size-xl: clamp(2.44rem, 2.38vw + 1.85rem, 3rem);
--color-text: hsl(12, 5%, 4%);
--color-bg: hsl(10, 21%, 95%);
}
html {
font-family: system-ui, sans-serif;
font-size: var(--font-size-base);
color: var(--color-text);
background-color: var(--color-bg);
}
body {
margin: 0;
}
:global(h1) {
font-size: var(--font-size-xl);
}
:global(h2) {
font-size: var(--font-size-lg);
}
:global(code) {
font-family:
Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
}
</style>
</body>
</html>

View file

@ -1,188 +0,0 @@
---
import Layout from '../components/Layout.astro';
export const prerender = true;
---
<Layout title="Welcome to Astro (on Deno).">
<main>
<h1>Welcome to <span class="text-gradient">Astro</span> on Deno</h1>
<p class="instructions">
<strong>Your first mission:</strong> tweak this message to try our hot module reloading. Check
the <code>src/pages</code> directory!
</p>
<ul role="list" class="link-card-grid">
<li class="link-card">
<a href="https://astro.build/integrations/">
<h2>Integrations <span>&rarr;</span></h2>
<p>Add component frameworks, Tailwind, Partytown, and more!</p>
</a>
</li>
<li class="link-card">
<a href="https://astro.build/themes/">
<h2>Themes <span>&rarr;</span></h2>
<p>Explore a galaxy of community-built starters.</p>
</a>
</li>
<li class="link-card">
<a href="https://docs.astro.build/">
<h2>Docs <span>&rarr;</span></h2>
<p>Learn our complete feature set and explore the API.</p>
</a>
</li>
<li class="link-card">
<a href="https://astro.build/chat/">
<h2>Chat <span>&rarr;</span></h2>
<p>
Ask, contribute, and have fun on our community Discord
<svg
class="heart"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
width="16"
height="16"
fill="currentColor"
>
<title>heart</title>
<path
d="M256 448l-30.164-27.211C118.718 322.442 48 258.61 48 179.095 48 114.221 97.918 64 162.4 64c36.399 0 70.717 16.742 93.6 43.947C278.882 80.742 313.199 64 349.6 64 414.082 64 464 114.221 464 179.095c0 79.516-70.719 143.348-177.836 241.694L256 448z"
></path>
</svg>
</p>
</a>
</li>
</ul>
</main>
</Layout>
<style>
:root {
--color-border: hsl(17, 24%, 90%);
--astro-gradient: linear-gradient(0deg, #4f39fa, #da62c4);
--link-gradient: linear-gradient(45deg, #4f39fa, #da62c4 30%, var(--color-border) 60%);
--night-sky-gradient: linear-gradient(
0deg,
#392362 -33%,
#431f69 10%,
#30216b 50%,
#1f1638 100%
);
}
h2 {
margin: 0;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
h2 span {
display: inline-block;
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
code {
font-size: 0.875em;
border: 0.1em solid var(--color-border);
border-radius: 4px;
padding: 0.15em 0.25em;
}
main {
margin: auto;
padding: 1em;
max-width: 60ch;
}
.text-gradient {
font-weight: 900;
background-image: var(--astro-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 100% 200%;
background-position-y: 100%;
border-radius: 0.4rem;
animation: pulse 4s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
background-position-y: 0%;
}
50% {
background-position-y: 80%;
}
}
.instructions {
line-height: 1.8;
margin-bottom: 2rem;
background-image: var(--night-sky-gradient);
padding: 1.5rem;
border-radius: 0.4rem;
color: var(--color-bg);
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 1rem;
padding: 0;
}
.link-card {
list-style: none;
display: flex;
padding: 0.15rem;
background-image: var(--link-gradient);
background-size: 400%;
border-radius: 0.5rem;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: 1em 1.3em;
border-radius: 0.35rem;
color: var(--text-color);
background-color: white;
opacity: 0.8;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
}
.link-card:is(:hover, :focus-within) h2 {
color: #4f39fa;
}
.link-card:is(:hover, :focus-within) h2 span {
transform: translateX(2px);
}
.heart {
display: inline-block;
color: #da62c4;
animation: heartbeat 3s ease-in-out infinite;
}
@keyframes heartbeat {
0%,
50%,
100% {
transform: scale(1);
}
5% {
transform: scale(1.125);
}
10% {
transform: scale(1.05);
}
15% {
transform: scale(1.25);
}
}
</style>

View file

@ -1,3 +0,0 @@
{
"extends": "astro/tsconfigs/base"
}

View file

@ -11,9 +11,9 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/alpinejs": "^0.3.0",
"@astrojs/alpinejs": "^0.3.1",
"@types/alpinejs": "^3.7.2",
"alpinejs": "^3.12.3",
"astro": "^3.1.2"
"astro": "^3.2.3"
}
}

View file

@ -11,9 +11,9 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/lit": "^3.0.0",
"@astrojs/lit": "^3.0.1",
"@webcomponents/template-shadowroot": "^0.2.1",
"astro": "^3.1.2",
"astro": "^3.2.3",
"lit": "^2.8.0"
}
}

View file

@ -11,12 +11,12 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/preact": "^3.0.0",
"@astrojs/react": "^3.0.2",
"@astrojs/solid-js": "^3.0.1",
"@astrojs/svelte": "^4.0.2",
"@astrojs/vue": "^3.0.0",
"astro": "^3.1.2",
"@astrojs/preact": "^3.0.1",
"@astrojs/react": "^3.0.3",
"@astrojs/solid-js": "^3.0.2",
"@astrojs/svelte": "^4.0.3",
"@astrojs/vue": "^3.0.1",
"astro": "^3.2.3",
"preact": "^10.17.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View file

@ -11,9 +11,9 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/preact": "^3.0.0",
"@astrojs/preact": "^3.0.1",
"@preact/signals": "^1.2.1",
"astro": "^3.1.2",
"astro": "^3.2.3",
"preact": "^10.17.1"
}
}

View file

@ -11,10 +11,10 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/react": "^3.0.2",
"@astrojs/react": "^3.0.3",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"astro": "^3.1.2",
"astro": "^3.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}

View file

@ -11,8 +11,8 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/solid-js": "^3.0.1",
"astro": "^3.1.2",
"@astrojs/solid-js": "^3.0.2",
"astro": "^3.2.3",
"solid-js": "^1.7.11"
}
}

View file

@ -11,8 +11,8 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/svelte": "^4.0.2",
"astro": "^3.1.2",
"@astrojs/svelte": "^4.0.3",
"astro": "^3.2.3",
"svelte": "^4.2.0"
}
}

View file

@ -11,8 +11,8 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/vue": "^3.0.0",
"astro": "^3.1.2",
"@astrojs/vue": "^3.0.1",
"astro": "^3.2.3",
"vue": "^3.3.4"
}
}

View file

@ -11,7 +11,7 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/node": "^6.0.1",
"astro": "^3.1.2"
"@astrojs/node": "^6.0.3",
"astro": "^3.2.3"
}
}

View file

@ -15,7 +15,7 @@
],
"scripts": {},
"devDependencies": {
"astro": "^3.1.2"
"astro": "^3.2.3"
},
"peerDependencies": {
"astro": "^2.0.0-beta.0"

View file

@ -12,8 +12,8 @@
"server": "node dist/server/entry.mjs"
},
"dependencies": {
"@astrojs/node": "^6.0.1",
"astro": "^3.1.2",
"@astrojs/node": "^6.0.3",
"astro": "^3.2.3",
"html-minifier": "^4.0.0"
}
}

View file

@ -11,6 +11,6 @@
"astro": "astro"
},
"dependencies": {
"astro": "^3.1.2"
"astro": "^3.2.3"
}
}

View file

@ -11,6 +11,6 @@
"astro": "astro"
},
"dependencies": {
"astro": "^3.1.2"
"astro": "^3.2.3"
}
}

View file

@ -11,6 +11,6 @@
"astro": "astro"
},
"dependencies": {
"astro": "^3.1.2"
"astro": "^3.2.3"
}
}

View file

@ -12,9 +12,9 @@
"server": "node dist/server/entry.mjs"
},
"dependencies": {
"@astrojs/node": "^6.0.1",
"@astrojs/svelte": "^4.0.2",
"astro": "^3.1.2",
"@astrojs/node": "^6.0.3",
"@astrojs/svelte": "^4.0.3",
"astro": "^3.2.3",
"svelte": "^4.2.0"
}
}

View file

@ -0,0 +1,30 @@
# Astro Movies View Transitions Demo
### 👉🏽 [Live Demo](https://astro-movies.pages.dev/)
![Screenshot](./screenshot.png)
## 🚀 Getting Started
1. Clone this repository and install dependencies with `npm install`.
2. Start the project locally with npm run dev, or deploy it to your favorite server.
3. Have fun! ✨
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :--------------------- | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Check out [Astro's documentation](https://docs.astro.build) or jump into their [Discord server](https://astro.build/chat).
You can also reach out to [Maxi on Twitter](https://twitter.com/charca).

View file

@ -0,0 +1,15 @@
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import nodejs from '@astrojs/node';
// https://astro.build/config
export default defineConfig({
integrations: [tailwind()],
output: 'server',
adapter: nodejs({ mode: 'standalone' }),
vite: {
define: {
'process.env.TMDB_API_KEY': JSON.stringify(process.env.TMDB_API_KEY),
},
},
});

View file

@ -0,0 +1,17 @@
{
"name": "@example/view-transitions",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"devDependencies": {
"@astrojs/tailwind": "^5.0.1",
"@astrojs/node": "^6.0.3",
"astro": "^3.2.3"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -0,0 +1,17 @@
<footer class="border border-t border-gray-800">
<div class="container mx-auto text-sm px-4 py-6">
Made with ❤️ by <a
href="https://www.twitter.com/charca"
target="_blank"
class="underline hover:text-gray-300">Maxi Ferreira</a
> — Powered by <a
href="https://astro.build"
target="_blank"
class="underline hover:text-gray-300">Astro</a
> and <a
href="https://www.themoviedb.org/documentation/api"
target="_blank"
class="underline hover:text-gray-300">TMDb API</a
>.
</div>
</footer>

View file

@ -0,0 +1,31 @@
---
const { movie } = Astro.props;
---
<div class="mt-8">
<a href={`/movies/${movie.id}`}>
<img
src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
alt={`${movie.title} Poster`}
class="thumbnail hover:opacity-75 transition ease-in-out duration-150"
id={`movie-poster-${movie.id}`}
transition:name={`poster-${movie.id}`}
/>
</a>
<div class="mt-2">
<a href={`/movies/${movie.id}`} class="text-lg mt-2 hover:text-gray-300">{movie.title}</a>
<div class="flex items-center text-gray-400 text-sm mt-1">
<svg class="fill-current text-orange-500 w-4" viewBox="0 0 24 24"
><g data-name="Layer 2"
><path
d="M17.56 21a1 1 0 01-.46-.11L12 18.22l-5.1 2.67a1 1 0 01-1.45-1.06l1-5.63-4.12-4a1 1 0 01-.25-1 1 1 0 01.81-.68l5.7-.83 2.51-5.13a1 1 0 011.8 0l2.54 5.12 5.7.83a1 1 0 01.81.68 1 1 0 01-.25 1l-4.12 4 1 5.63a1 1 0 01-.4 1 1 1 0 01-.62.18z"
data-name="star"></path></g
></svg
>
<span class="ml-1">{movie.vote_average}</span>
<span class="mx-2">|</span>
<span>{movie.release_date}</span>
</div>
<div class="text-gray-400 text-sm">{movie.genres}</div>
</div>
</div>

View file

@ -0,0 +1,125 @@
---
const { data } = Astro.props;
const movie = {
...data,
poster_path: data.poster_path
? 'https://image.tmdb.org/t/p/w500/' + data.poster_path
: 'https://via.placeholder.com/500x750',
vote_average: (data.vote_average * 10).toFixed(2) + '%',
release_date: new Date(data.release_date).toLocaleDateString('en-us', {
year: 'numeric',
month: 'long',
day: 'numeric',
}),
genres: data.genres.map((g: any) => g.name).join(', '),
crew: data.credits.crew.slice(0, 3),
cast: data.credits.cast.slice(0, 5).map((c: any) => ({
...c,
profile_path: c.profile_path
? 'https://image.tmdb.org/t/p/w300/' + c.profile_path
: 'https://via.placeholder.com/300x450',
})),
images: data.images.backdrops.slice(0, 9),
};
---
<div class="movie-info border-b border-gray-800">
<div class="container mx-auto px-4 py-16 flex flex-col md:flex-row">
<div class="flex-none">
<img
src={movie.poster_path}
alt={`${movie.title} Poster`}
class="movie-poster w-64 lg:w-96"
id="movie-poster"
transition:name={`poster-${movie.id}`}
/>
</div>
<div class="md:ml-24">
<h2 class="text-4xl mt-4 md:mt-0 mb-2 font-semibold">{movie.title}</h2>
<div class="flex flex-wrap items-center text-gray-400 text-sm">
<svg class="fill-current text-orange-500 w-4" viewBox="0 0 24 24"
><g data-name="Layer 2"
><path
d="M17.56 21a1 1 0 01-.46-.11L12 18.22l-5.1 2.67a1 1 0 01-1.45-1.06l1-5.63-4.12-4a1 1 0 01-.25-1 1 1 0 01.81-.68l5.7-.83 2.51-5.13a1 1 0 011.8 0l2.54 5.12 5.7.83a1 1 0 01.81.68 1 1 0 01-.25 1l-4.12 4 1 5.63a1 1 0 01-.4 1 1 1 0 01-.62.18z"
data-name="star"></path></g
></svg
>
<span class="ml-1">{movie.vote_average}</span>
<span class="mx-2">|</span>
<span>{movie.release_date}</span>
<span class="mx-2">|</span>
<span>{movie.genres}</span>
</div>
<p class="text-gray-300 mt-8">
{movie.overview}
</p>
<div class="mt-12">
<h4 class="text-white font-semibold">Featured Crew</h4>
<div class="flex mt-4">
{
movie.crew.map((crew: any) => (
<div class="mr-8">
<div>{crew.name}</div>
<div class="text-gray-400 text-sm">{crew.job}</div>
</div>
))
}
</div>
</div>
</div>
</div>
</div>
<!-- end movie-info -->
<div class="movie-cast border-b border-gray-800">
<div class="container mx-auto px-4 py-16">
<h2 class="text-4xl font-semibold">Cast</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-8">
{
movie.cast.map((cast: any) => (
<div class="mt-8">
<span>
<img
id={`person-photo-${cast.id}`}
src={cast.profile_path}
alt={cast.name}
class="thumbnail hover:opacity-75 transition ease-in-out duration-150"
/>
</span>
<div class="mt-2">
<span class="text-lg mt-2 hover:text-gray:300">{cast.name}</span>
<div class="text-sm text-gray-400">{cast.character}</div>
</div>
</div>
))
}
</div>
</div>
</div>
<!-- end movie-cast -->
<div class="movie-images">
<div class="container mx-auto px-4 py-16">
<h2 class="text-4xl font-semibold">Images</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8">
{
movie.images.map((image: any) => (
<div class="mt-8">
<span>
<img
src={`https://image.tmdb.org/t/p/w500${image.file_path}`}
loading="lazy"
alt={movie.name}
class="hover:opacity-75 transition ease-in-out duration-150"
/>
</span>
</div>
))
}
</div>
</div>
</div>
<!-- end movie-images -->

View file

@ -0,0 +1,15 @@
---
import MovieCard from './MovieCard.astro';
import movies from '../popular-movies.json';
const popularMovies = movies.results;
---
<div class="container mx-auto px-4 pt-16 mb-16">
<div class="popular-movies">
<h2 class="uppercase tracking-wider text-orange-500 text-lg font-semibold">Popular Movies</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-8">
{popularMovies.map((movie) => <MovieCard movie={movie} />)}
</div>
</div>
<!-- end pouplar-movies -->
</div>

View file

@ -0,0 +1,18 @@
<nav class="nav border-b border-gray-800 sticky top-0 z-30 bg-gray-900">
<div
class="container mx-auto px-4 flex flex-col md:flex-row items-center justify-between px-4 py-6"
>
<ul class="flex flex-col md:flex-row items-center">
<li>
<a href="/" class="flex items-center font-bold text-xl">
<span>Movies</span>
&nbsp;
<span class="text-orange-500">List</span>
</a>
</li>
<li class="md:ml-16 mt-3 md:mt-0">
<a href="/" class="hover:text-gray-300">Movies</a>
</li>
</ul>
</div>
</nav>

View file

@ -0,0 +1,12 @@
import { z, defineCollection } from 'astro:content';
const movies = defineCollection({
type: 'data',
schema: z.object({
data: z.any(),
}),
});
// Expose your defined collection to Astro
// with the `collections` export
export const collections = { movies };

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,345 @@
{
"data": {
"adult": false,
"backdrop_path": "/e8FyMnifoN5BMuRFE97fS1lJZ6S.jpg",
"belongs_to_collection": {
"id": 1153632,
"name": "Darkland Collection",
"poster_path": null,
"backdrop_path": null
},
"budget": 0,
"genres": [
{ "id": 28, "name": "Action" },
{ "id": 53, "name": "Thriller" }
],
"homepage": "",
"id": 1085218,
"imdb_id": "tt20204996",
"original_language": "da",
"original_title": "Underverden 2",
"overview": "Seven years ago, Zaid went to war against the Copenhagen underworld to avenge his dead brother. His identity as a respected doctor of cardiology and life as a family man is but a fading dream, and in prison Zaid suffers the loss of his son Noah, whom he barely knows. When a police agent approaches Zaid and offers him a deal to be released in exchange for infiltrating the Copenhagen underworld, he sees his chance to reclaim the remnants of the family life he left behind. But everything has a price, and Zaid realizes that he has now seriously endangered his son's life. After all, once you become part of the underworld, is there any way out?",
"popularity": 524.476,
"poster_path": "/c8B4DsVcFVDLVmbpHMHU3RjLNAV.jpg",
"production_companies": [
{
"id": 64293,
"logo_path": "/lbYGnir9KeY8NyQZrUFZbf3QDej.png",
"name": "Profile Pictures",
"origin_country": "DK"
}
],
"production_countries": [{ "iso_3166_1": "DK", "name": "Denmark" }],
"release_date": "2023-04-13",
"revenue": 0,
"runtime": 110,
"spoken_languages": [
{ "english_name": "Arabic", "iso_639_1": "ar", "name": "العربية" },
{ "english_name": "Danish", "iso_639_1": "da", "name": "Dansk" }
],
"status": "Released",
"tagline": "",
"title": "Darkland: The Return",
"video": false,
"vote_average": 6.32,
"vote_count": 64,
"credits": {
"cast": [
{
"adult": false,
"gender": 2,
"id": 234907,
"known_for_department": "Acting",
"name": "Dar Salim",
"original_name": "Dar Salim",
"popularity": 16.442,
"profile_path": "/oTYCqdF6nfZTsvt1fbypi54ydI.jpg",
"cast_id": 1,
"character": "Zaid",
"credit_id": "6429b39701b1ca0113cc2840",
"order": 0
},
{
"adult": false,
"gender": 1,
"id": 90514,
"known_for_department": "Acting",
"name": "Birgitte Hjort Sørensen",
"original_name": "Birgitte Hjort Sørensen",
"popularity": 9.741,
"profile_path": "/uPak0FlCPdsc9B1pw4bBkuaMLXT.jpg",
"cast_id": 2,
"character": "Helle",
"credit_id": "6429b3d101b1ca00d5e8507e",
"order": 1
},
{
"adult": false,
"gender": 1,
"id": 32682,
"known_for_department": "Acting",
"name": "Stine Fischer Christensen",
"original_name": "Stine Fischer Christensen",
"popularity": 3.87,
"profile_path": "/6MYODCa28L1IzfhcYTUZUYThoF2.jpg",
"cast_id": 3,
"character": "Stine",
"credit_id": "6429b42c9cc67b05796bcdcc",
"order": 2
},
{
"adult": false,
"gender": 0,
"id": 1178394,
"known_for_department": "Acting",
"name": "Henrik Vestergaard",
"original_name": "Henrik Vestergaard",
"popularity": 0.766,
"profile_path": "/1Opgkbdkyt1x5iEOLgV3VJ29ZpA.jpg",
"cast_id": 4,
"character": "Lars",
"credit_id": "6429b45401b1ca0097fdfb0f",
"order": 3
},
{
"adult": false,
"gender": 0,
"id": 3236472,
"known_for_department": "Acting",
"name": "Soheil Bavi",
"original_name": "Soheil Bavi",
"popularity": 0.6,
"profile_path": null,
"cast_id": 5,
"character": "Muhdir",
"credit_id": "6429b4828de0ae00b65514de",
"order": 4
},
{
"adult": false,
"gender": 0,
"id": 3991209,
"known_for_department": "Acting",
"name": "Jack Pedersen",
"original_name": "Jack Pedersen",
"popularity": 0.648,
"profile_path": null,
"cast_id": 6,
"character": "Shahin",
"credit_id": "6429b4f4ac8e6b00d32aa4eb",
"order": 5
},
{
"adult": false,
"gender": 0,
"id": 2595279,
"known_for_department": "Acting",
"name": "Mohamed Djeziri",
"original_name": "Mohamed Djeziri",
"popularity": 0.84,
"profile_path": null,
"cast_id": 11,
"character": "",
"credit_id": "64c14243871b340101072cf6",
"order": 6
},
{
"adult": false,
"gender": 0,
"id": 4188787,
"known_for_department": "Acting",
"name": "Abud Mustafa",
"original_name": "Abud Mustafa",
"popularity": 0.98,
"profile_path": null,
"cast_id": 12,
"character": "",
"credit_id": "64c6c269db8a0000e3286594",
"order": 7
},
{
"adult": false,
"gender": 0,
"id": 4188788,
"known_for_department": "Acting",
"name": "Ahmad Ayman",
"original_name": "Ahmad Ayman",
"popularity": 0.6,
"profile_path": null,
"cast_id": 13,
"character": "",
"credit_id": "64c6c27f63aad20209a58505",
"order": 8
},
{
"adult": false,
"gender": 0,
"id": 4188789,
"known_for_department": "Acting",
"name": "Sebastian Nørgaard",
"original_name": "Sebastian Nørgaard",
"popularity": 0.6,
"profile_path": null,
"cast_id": 14,
"character": "",
"credit_id": "64c6c298db8a0000e32865ab",
"order": 9
},
{
"adult": false,
"gender": 0,
"id": 4188791,
"known_for_department": "Acting",
"name": "Hamed \"Baloosh\" Balosha",
"original_name": "Hamed \"Baloosh\" Balosha",
"popularity": 0.6,
"profile_path": null,
"cast_id": 15,
"character": "",
"credit_id": "64c6c2a3eec5b500ff5262cb",
"order": 10
},
{
"adult": false,
"gender": 0,
"id": 3549264,
"known_for_department": "Acting",
"name": "Noah Carter",
"original_name": "Noah Carter",
"popularity": 0.6,
"profile_path": null,
"cast_id": 16,
"character": "",
"credit_id": "64c6c2b0cadb6b00c82a2404",
"order": 11
},
{
"adult": false,
"gender": 0,
"id": 4188792,
"known_for_department": "Acting",
"name": "Asgar Hansen",
"original_name": "Asgar Hansen",
"popularity": 0.6,
"profile_path": null,
"cast_id": 17,
"character": "",
"credit_id": "64c6c2bc30f79c00c781a97c",
"order": 12
}
],
"crew": [
{
"adult": false,
"gender": 0,
"id": 1183636,
"known_for_department": "Directing",
"name": "Fenar Ahmad",
"original_name": "Fenar Ahmad",
"popularity": 1.646,
"profile_path": null,
"credit_id": "6429b673c04429026b13a94a",
"department": "Directing",
"job": "Director"
},
{
"adult": false,
"gender": 0,
"id": 1183636,
"known_for_department": "Directing",
"name": "Fenar Ahmad",
"original_name": "Fenar Ahmad",
"popularity": 1.646,
"profile_path": null,
"credit_id": "642ed070158c8501263ad755",
"department": "Writing",
"job": "Writer"
},
{
"adult": false,
"gender": 0,
"id": 2405913,
"known_for_department": "Directing",
"name": "Behrouz Bigdeli",
"original_name": "Behrouz Bigdeli",
"popularity": 0.6,
"profile_path": null,
"credit_id": "642ed08058361b00f2f13338",
"department": "Writing",
"job": "Writer"
}
]
},
"videos": { "results": [] },
"images": {
"backdrops": [
{
"aspect_ratio": 1.778,
"height": 1080,
"iso_639_1": "da",
"file_path": "/4wVFtesa5YEWuAUHRcxoCN1Y1uN.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 1920
},
{
"aspect_ratio": 1.778,
"height": 720,
"iso_639_1": null,
"file_path": "/e8FyMnifoN5BMuRFE97fS1lJZ6S.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1280
},
{
"aspect_ratio": 1.778,
"height": 1116,
"iso_639_1": null,
"file_path": "/tDT465D3JZiABgz2uug9jCgOUlw.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1984
}
],
"logos": [],
"posters": [
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "da",
"file_path": "/A8EPXv3SV9qiNCIttIM4ezJRmhW.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 1200,
"iso_639_1": "da",
"file_path": "/A0cxUcMWBruPknr5ZSePIYFfe7z.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 800
},
{
"aspect_ratio": 0.7,
"height": 2834,
"iso_639_1": "en",
"file_path": "/c8B4DsVcFVDLVmbpHMHU3RjLNAV.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1984
},
{
"aspect_ratio": 0.7,
"height": 2834,
"iso_639_1": "en",
"file_path": "/my1ve6HKygOVNYJBi3A0pRpPm2l.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1984
}
]
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,856 @@
{
"data": {
"adult": false,
"backdrop_path": "/jkKVLzLWjSvTnc84VzeljhSy6j8.jpg",
"belongs_to_collection": {
"id": 702624,
"name": "After Collection",
"poster_path": "/250ewaLutOJXqBBqMKxCHD1KpCL.jpg",
"backdrop_path": "/zZTy8G3sEVZNv0yGssgc7DvPUQJ.jpg"
},
"budget": 14000000,
"genres": [
{ "id": 10749, "name": "Romance" },
{ "id": 18, "name": "Drama" }
],
"homepage": "",
"id": 820525,
"imdb_id": "tt15334488",
"original_language": "en",
"original_title": "After Everything",
"overview": "Besieged by writers block and the crushing breakup with Tessa, Hardin travels to Portugal in search of a woman he wronged in the past and to find himself. Hoping to win back Tessa, he realizes he needs to change his ways before he can make the ultimate commitment.",
"popularity": 650.272,
"poster_path": "/gZLGCibvFY4zmt8sWUZcbBTHRtk.jpg",
"production_companies": [
{
"id": 6626,
"logo_path": "/A1BnMoWjzjOrjzpWimyBQkf84mS.png",
"name": "Voltage Pictures",
"origin_country": "US"
},
{
"id": 107108,
"logo_path": "/5mxc7uNtFOZm2ly0BxixxGPvPlb.png",
"name": "Wattpad",
"origin_country": "US"
}
],
"production_countries": [{ "iso_3166_1": "US", "name": "United States of America" }],
"release_date": "2023-09-13",
"revenue": 962741,
"runtime": 93,
"spoken_languages": [
{ "english_name": "English", "iso_639_1": "en", "name": "English" },
{ "english_name": "Portuguese", "iso_639_1": "pt", "name": "Português" }
],
"status": "Released",
"tagline": "",
"title": "After Everything",
"video": false,
"vote_average": 6.407,
"vote_count": 27,
"credits": {
"cast": [
{
"adult": false,
"gender": 2,
"id": 1114487,
"known_for_department": "Acting",
"name": "Hero Fiennes Tiffin",
"original_name": "Hero Fiennes Tiffin",
"popularity": 15.891,
"profile_path": "/6zMrrZvOMH6uGwEFoK0Uo8sZvxL.jpg",
"cast_id": 5,
"character": "Hardin Scott",
"credit_id": "60d374f40e64af0046370008",
"order": 0
},
{
"adult": false,
"gender": 1,
"id": 1694711,
"known_for_department": "Acting",
"name": "Mimi Keene",
"original_name": "Mimi Keene",
"popularity": 7.403,
"profile_path": "/7bUgfaycm0f6PgEn3eLo8oANByO.jpg",
"cast_id": 24,
"character": "Nathalie",
"credit_id": "6441b4e7651fcf02fb9c2851",
"order": 1
},
{
"adult": false,
"gender": 2,
"id": 2849483,
"known_for_department": "Acting",
"name": "Benjamin Mascolo",
"original_name": "Benjamin Mascolo",
"popularity": 4.235,
"profile_path": "/zkKiB05KRFCIQdMVnseQlatMLRq.jpg",
"cast_id": 23,
"character": "Sebastian",
"credit_id": "6441b4cab3f6f5055a9d8f4a",
"order": 2
},
{
"adult": false,
"gender": 1,
"id": 20805,
"known_for_department": "Acting",
"name": "Louise Lombard",
"original_name": "Louise Lombard",
"popularity": 17.846,
"profile_path": "/6znYbOI2Z8PfzZ6p9jHG5QdAeb2.jpg",
"cast_id": 22,
"character": "Trish Daniels",
"credit_id": "63a8788e0f21c6007871434e",
"order": 3
},
{
"adult": false,
"gender": 2,
"id": 32203,
"known_for_department": "Acting",
"name": "Stephen Moyer",
"original_name": "Stephen Moyer",
"popularity": 12.55,
"profile_path": "/xprwOCXa2cNkjhrGtcJp0VfJMlZ.jpg",
"cast_id": 7,
"character": "Christian Vance",
"credit_id": "63a638291f748b008a0a1614",
"order": 4
},
{
"adult": false,
"gender": 1,
"id": 20373,
"known_for_department": "Acting",
"name": "Arielle Kebbel",
"original_name": "Arielle Kebbel",
"popularity": 23.827,
"profile_path": "/dmYiAeWoeVsRdT4UBBGuW9gBfjQ.jpg",
"cast_id": 6,
"character": "Kim Vance",
"credit_id": "63a638191f748b007cc00f2f",
"order": 5
},
{
"adult": false,
"gender": 1,
"id": 3009693,
"known_for_department": "Acting",
"name": "Jessica Webber",
"original_name": "Jessica Webber",
"popularity": 1.15,
"profile_path": null,
"cast_id": 20,
"character": "Maddy",
"credit_id": "63a64fa98a84d200b4ce9858",
"order": 6
},
{
"adult": false,
"gender": 1,
"id": 3069742,
"known_for_department": "Acting",
"name": "Cora Kirk",
"original_name": "Cora Kirk",
"popularity": 1.404,
"profile_path": "/4av80vVFZSBuKJyzXqEXLE8Vftc.jpg",
"cast_id": 21,
"character": "Freya",
"credit_id": "63a64fb1eb093200a0c1594c",
"order": 7
},
{
"adult": false,
"gender": 1,
"id": 2173697,
"known_for_department": "Acting",
"name": "Rosa Escoda",
"original_name": "Rosa Escoda",
"popularity": 4.382,
"profile_path": "/bnGlICypCtpuiKe5mHCnijESI3W.jpg",
"cast_id": 27,
"character": "Kat",
"credit_id": "6503cbc1efea7a00e036e0f1",
"order": 8
},
{
"adult": false,
"gender": 1,
"id": 2677100,
"known_for_department": "Acting",
"name": "Ella Martine",
"original_name": "Ella Martine",
"popularity": 0.6,
"profile_path": "/d9uZ2tqibHTno0ZKPUc9FZNXhTn.jpg",
"cast_id": 28,
"character": "Naomi",
"credit_id": "6503cbd7e0ca7f010deb819c",
"order": 9
},
{
"adult": false,
"gender": 1,
"id": 2026999,
"known_for_department": "Acting",
"name": "Laura Dutra",
"original_name": "Laura Dutra",
"popularity": 1.8,
"profile_path": "/ipSilYFln0N3UdiL3eboTv8XWS1.jpg",
"cast_id": 29,
"character": "Paloma",
"credit_id": "6503cbedefea7a00aad915a0",
"order": 10
},
{
"adult": false,
"gender": 2,
"id": 2035329,
"known_for_department": "Acting",
"name": "Chance Perdomo",
"original_name": "Chance Perdomo",
"popularity": 9.723,
"profile_path": "/xRRDtdHhTewrKMj5cpcmEkPNmuP.jpg",
"cast_id": 30,
"character": "Landon Scott",
"credit_id": "6503cbfed7dcd20139cc9553",
"order": 11
},
{
"adult": false,
"gender": 1,
"id": 142115,
"known_for_department": "Acting",
"name": "Kiana Madeira",
"original_name": "Kiana Madeira",
"popularity": 19.805,
"profile_path": "/HgCI95xnSZJaE18d15n7PykOU7.jpg",
"cast_id": 31,
"character": "Nora",
"credit_id": "6503cc206a2227011a7c6353",
"order": 12
},
{
"adult": false,
"gender": 2,
"id": 20960,
"known_for_department": "Acting",
"name": "Rob Estes",
"original_name": "Rob Estes",
"popularity": 9.434,
"profile_path": "/b24y5Tv4A8DUjkfYpJ1l1qpkhyv.jpg",
"cast_id": 32,
"character": "Ken Scott",
"credit_id": "6503cc31efea7a0137d3de3b",
"order": 13
},
{
"adult": false,
"gender": 1,
"id": 20373,
"known_for_department": "Acting",
"name": "Arielle Kebbel",
"original_name": "Arielle Kebbel",
"popularity": 23.827,
"profile_path": "/dmYiAeWoeVsRdT4UBBGuW9gBfjQ.jpg",
"cast_id": 33,
"character": "Kimberley",
"credit_id": "6503cc596a222700abaa07d9",
"order": 14
},
{
"adult": false,
"gender": 2,
"id": 86653,
"known_for_department": "Acting",
"name": "Carter Jenkins",
"original_name": "Carter Jenkins",
"popularity": 5.084,
"profile_path": "/fGVndWC3hgwK1uVrhiyzTUyIaxW.jpg",
"cast_id": 34,
"character": "Robert Freeman",
"credit_id": "6503cc65ffc9de0edf625db1",
"order": 15
},
{
"adult": false,
"gender": 0,
"id": 1910292,
"known_for_department": "Acting",
"name": "Ana Ivanova",
"original_name": "Ana Ivanova",
"popularity": 1.307,
"profile_path": "/kT9AL9N1zwi4gksDPdLXGAThbpx.jpg",
"cast_id": 35,
"character": "Emery",
"credit_id": "6503cc95efea7a00c3986356",
"order": 16
},
{
"adult": false,
"gender": 2,
"id": 2916706,
"known_for_department": "Acting",
"name": "Anton Kottas",
"original_name": "Anton Kottas",
"popularity": 3.44,
"profile_path": "/trj5wj5Y6Tvj649tbn1a4IG6YfG.jpg",
"cast_id": 36,
"character": "Smith",
"credit_id": "6503cca5ffc9de0ee20aaf80",
"order": 17
},
{
"adult": false,
"gender": 1,
"id": 1753914,
"known_for_department": "Acting",
"name": "Josephine Langford",
"original_name": "Josephine Langford",
"popularity": 27.865,
"profile_path": "/8Fj1UIFRJA0B5Zo22KwML5d3Mr3.jpg",
"cast_id": 39,
"character": "Tessa Young",
"credit_id": "650a7334cadb6b00e11f6961",
"order": 18
}
],
"crew": [
{
"adult": false,
"gender": 2,
"id": 20739,
"known_for_department": "Directing",
"name": "Adam Shankman",
"original_name": "Adam Shankman",
"popularity": 8.011,
"profile_path": "/zZmZgVp5OTU2eSMCDuOXGlQ4fBR.jpg",
"credit_id": "65072f3b42d8a5011bd6f839",
"department": "Directing",
"job": "Director"
},
{
"adult": false,
"gender": 1,
"id": 20742,
"known_for_department": "Production",
"name": "Jennifer Gibgot",
"original_name": "Jennifer Gibgot",
"popularity": 1.159,
"profile_path": null,
"credit_id": "63a64f171108a800bab73fcd",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 2,
"id": 46088,
"known_for_department": "Production",
"name": "Mark Canton",
"original_name": "Mark Canton",
"popularity": 2.299,
"profile_path": "/7OiY4eak3DztX0lSj4cbmKLQuaj.jpg",
"credit_id": "63a64f03907f2600aa7262a1",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 2,
"id": 61921,
"known_for_department": "Production",
"name": "Courtney Solomon",
"original_name": "Courtney Solomon",
"popularity": 2.006,
"profile_path": "/xujLAPgpM1JXLqE0X4mWVrPTmqd.jpg",
"credit_id": "63a64f35907f2600da9a63bf",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 2,
"id": 1031200,
"known_for_department": "Production",
"name": "Nicolas Chartier",
"original_name": "Nicolas Chartier",
"popularity": 1.901,
"profile_path": "/59ptYCph60CtAtLmWuC9dASHhsp.jpg",
"credit_id": "63a64f0d1f748b007cc01cfd",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 0,
"id": 1179667,
"known_for_department": "Lighting",
"name": "Diego Moyano",
"original_name": "Diego Moyano",
"popularity": 0.6,
"profile_path": null,
"credit_id": "63a64f908a84d200d9c3b03a",
"department": "Lighting",
"job": "Gaffer"
},
{
"adult": false,
"gender": 1,
"id": 1193192,
"known_for_department": "Production",
"name": "Carolyn McLeod",
"original_name": "Carolyn McLeod",
"popularity": 0.612,
"profile_path": null,
"credit_id": "63a64f7404b596007d427d31",
"department": "Production",
"job": "Casting"
},
{
"adult": false,
"gender": 1,
"id": 1448492,
"known_for_department": "Directing",
"name": "Castille Landon",
"original_name": "Castille Landon",
"popularity": 5.18,
"profile_path": "/s1oXtmqyG9X0uDfiU5HidPB6BJx.jpg",
"credit_id": "645009ff52dc7f02dddb5ed5",
"department": "Writing",
"job": "Screenplay"
},
{
"adult": false,
"gender": 0,
"id": 1540619,
"known_for_department": "Camera",
"name": "Joshua Reis",
"original_name": "Joshua Reis",
"popularity": 0.984,
"profile_path": null,
"credit_id": "63a64f681108a800c60a4d19",
"department": "Camera",
"job": "Director of Photography"
},
{
"adult": false,
"gender": 2,
"id": 1551998,
"known_for_department": "Production",
"name": "Brian Pitt",
"original_name": "Brian Pitt",
"popularity": 2.997,
"profile_path": null,
"credit_id": "63a64f2a04b59600918423ea",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 0,
"id": 1552003,
"known_for_department": "Production",
"name": "David Shojai",
"original_name": "David Shojai",
"popularity": 0.6,
"profile_path": null,
"credit_id": "63a64f592b8a43007dddfc26",
"department": "Production",
"job": "Co-Producer"
},
{
"adult": false,
"gender": 1,
"id": 1664826,
"known_for_department": "Writing",
"name": "Anna Todd",
"original_name": "Anna Todd",
"popularity": 2.633,
"profile_path": "/b0L0Hicos8ZeEXt96Jf9wDed8FS.jpg",
"credit_id": "6450089f2fccee02e4cebc26",
"department": "Writing",
"job": "Novel"
},
{
"adult": false,
"gender": 0,
"id": 3032242,
"known_for_department": "Art",
"name": "Alexandra Tibbe",
"original_name": "Alexandra Tibbe",
"popularity": 0.6,
"profile_path": null,
"credit_id": "63a64f812b8a4300b60617fd",
"department": "Art",
"job": "Set Decoration"
},
{
"adult": false,
"gender": 2,
"id": 3055946,
"known_for_department": "Production",
"name": "Aron Levitz",
"original_name": "Aron Levitz",
"popularity": 0.6,
"profile_path": null,
"credit_id": "63a64f211f748b00826608cc",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 0,
"id": 3216486,
"known_for_department": "Acting",
"name": "Taylor Conrod",
"original_name": "Taylor Conrod",
"popularity": 0.6,
"profile_path": null,
"credit_id": "63a64f4c04b596007d427cfc",
"department": "Production",
"job": "Co-Producer"
}
]
},
"videos": {
"results": [
{
"iso_639_1": "en",
"iso_3166_1": "US",
"name": "Official Trailer",
"key": "NsmopvKNSE4",
"site": "YouTube",
"size": 1080,
"type": "Trailer",
"official": true,
"published_at": "2023-05-01T12:00:05.000Z",
"id": "64bd958d0ed2ab00c5e3af6a"
},
{
"iso_639_1": "en",
"iso_3166_1": "US",
"name": "Hessa (Official Clip)",
"key": "iVg7CDtG3MQ",
"site": "YouTube",
"size": 1080,
"type": "Clip",
"official": true,
"published_at": "2023-02-14T13:00:28.000Z",
"id": "63ebc244813cb60096aa356a"
},
{
"iso_639_1": "en",
"iso_3166_1": "US",
"name": "Official Teaser",
"key": "zmgbPv7lGrk",
"site": "YouTube",
"size": 1080,
"type": "Teaser",
"official": true,
"published_at": "2022-12-23T17:34:00.000Z",
"id": "63c169ee23be4600d960ae6d"
}
]
},
"images": {
"backdrops": [
{
"aspect_ratio": 1.778,
"height": 720,
"iso_639_1": null,
"file_path": "/jkKVLzLWjSvTnc84VzeljhSy6j8.jpg",
"vote_average": 5.384,
"vote_count": 2,
"width": 1280
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/x6y6AHY3UoCeOP6kwZain7WNLPN.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/ol32sTlLzayf7y3KbJrfuQkLmT8.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/jL9pvfEep0houuNFMSEjinBm6Jh.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/xZuhDaKDpWDG0sE3Dx6CIR8DeaN.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/zg5rp4R3RkGApxWM03jUWJQzXGg.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 3840
}
],
"logos": [
{
"aspect_ratio": 1.81,
"height": 749,
"iso_639_1": "en",
"file_path": "/r7eYlJVm3OSVBA78Wg3kSUmCKQ7.png",
"vote_average": 0,
"vote_count": 0,
"width": 1356
},
{
"aspect_ratio": 1.81,
"height": 749,
"iso_639_1": "en",
"file_path": "/t3wmFD8ghWeP1EEjahgD0MtcPM9.png",
"vote_average": 0,
"vote_count": 0,
"width": 1356
}
],
"posters": [
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "en",
"file_path": "/gZLGCibvFY4zmt8sWUZcbBTHRtk.jpg",
"vote_average": 5.318,
"vote_count": 3,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "ru",
"file_path": "/qMPcBNHcgclpl4IUiriSVcyt2Xr.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "es",
"file_path": "/jO3VGQi5sHIj2BGS963g1F74yCq.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 1020,
"iso_639_1": "sk",
"file_path": "/7q9Q3OFu4E2UwTUnwGTZx79fqgj.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 680
},
{
"aspect_ratio": 0.671,
"height": 1176,
"iso_639_1": "ar",
"file_path": "/d0qw9hiNwhRmfw4yAc44pep6vhz.jpg",
"vote_average": 5.252,
"vote_count": 4,
"width": 789
},
{
"aspect_ratio": 0.667,
"height": 2466,
"iso_639_1": "en",
"file_path": "/aJbrGWjOMwguiCSAGBcpoWGgpXf.jpg",
"vote_average": 5.246,
"vote_count": 2,
"width": 1644
},
{
"aspect_ratio": 0.667,
"height": 2466,
"iso_639_1": "en",
"file_path": "/uQxjZGU6rxSPSMeAJPJQlmfV3ys.jpg",
"vote_average": 5.246,
"vote_count": 2,
"width": 1644
},
{
"aspect_ratio": 0.667,
"height": 1500,
"iso_639_1": "en",
"file_path": "/8oibiRBkwSHFDamazdrEB5fWvXN.jpg",
"vote_average": 5.18,
"vote_count": 3,
"width": 1000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "en",
"file_path": "/3PqW0elNnj5tk4XD9o82djTknGd.jpg",
"vote_average": 5.18,
"vote_count": 3,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "es",
"file_path": "/zXDnVeyWwvhLYbaQjnei0A6vohx.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "en",
"file_path": "/6bPnDIGG8RBncFPIUNxM6GAW0Ox.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "uk",
"file_path": "/v9qWcfkC3nJOASotuFdJ5huqg6l.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 1714,
"iso_639_1": "es",
"file_path": "/nm7rgAYCdATbHgm6CjtoSNvON9k.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 1143
},
{
"aspect_ratio": 0.666,
"height": 2000,
"iso_639_1": "fr",
"file_path": "/brIDXQSf02a09i13xvMDTkRlO7z.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1333
},
{
"aspect_ratio": 0.665,
"height": 1351,
"iso_639_1": "pt",
"file_path": "/tRjzdNiFHda6lrXySOQPyY3OtCA.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 899
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "en",
"file_path": "/mhjf22NsRXY4HKRbaUlfZAt7Swb.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "en",
"file_path": "/gh7JdLfrjewoA00xeMr0ju0PMwd.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": null,
"file_path": "/moIOvJIdvvwriBiaKTehcmIeOf9.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "he",
"file_path": "/ao0uyI21djx9uLv6voAjoaetX9O.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "nl",
"file_path": "/z7Yq7vkVts9yeHCTFqIOEJkhJbi.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 1350,
"iso_639_1": "hr",
"file_path": "/fZog1nBWjNPQtPtoR1AFrmdbuG5.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 900
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "bg",
"file_path": "/naMHX4mvqEAM4UbMox0MYYoFaPd.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "sv",
"file_path": "/afPrxdEVh9uoSSIc0RVKMXPRHWm.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "sk",
"file_path": "/stF5ysfzd1X5xMuvOiv5k8rRiX3.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
}
]
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,927 @@
{
"data": {
"adult": false,
"backdrop_path": "/rV56FkcHkzHJcBOOqoCeSDnoBff.jpg",
"belongs_to_collection": null,
"budget": 0,
"genres": [
{ "id": 28, "name": "Action" },
{ "id": 18, "name": "Drama" }
],
"homepage": "",
"id": 990140,
"imdb_id": "tt22488024",
"original_language": "cn",
"original_title": "天龍八部之喬峰傳",
"overview": "Qiao Feng is the respected leader of a roving band of martial artists. After he is wrongfully accused of murder and subsequently exiled, Qiao Feng goes on the run in search of answers about his own mysterious origin story—and the unknown enemies working to destroy him from the shadows.",
"popularity": 522.83,
"poster_path": "/jGKCpt3zzbGZbgoza6HCvecqElM.jpg",
"production_companies": [
{ "id": 155372, "logo_path": null, "name": "Wishart Media Co., Ltd.", "origin_country": "" },
{ "id": 160005, "logo_path": null, "name": "Super Bullet Pictures", "origin_country": "HK" }
],
"production_countries": [
{ "iso_3166_1": "CN", "name": "China" },
{ "iso_3166_1": "HK", "name": "Hong Kong" }
],
"release_date": "2023-01-16",
"revenue": 159959,
"runtime": 130,
"spoken_languages": [
{ "english_name": "Cantonese", "iso_639_1": "cn", "name": "广州话 / 廣州話" },
{ "english_name": "Mandarin", "iso_639_1": "zh", "name": "普通话" }
],
"status": "Released",
"tagline": "When a warrior belongs nowhere, the enemy is everywhere.",
"title": "Sakra",
"video": false,
"vote_average": 6.552,
"vote_count": 87,
"credits": {
"cast": [
{
"adult": false,
"gender": 2,
"id": 1341,
"known_for_department": "Acting",
"name": "Donnie Yen",
"original_name": "Donnie Yen",
"popularity": 32.998,
"profile_path": "/hTlhrrZMj8hZVvD17j4KyAFWBHc.jpg",
"cast_id": 1,
"character": "Qiao Feng / Xiao Feng",
"credit_id": "62b0cf55ecc7e800f0fe0311",
"order": 0
},
{
"adult": false,
"gender": 1,
"id": 2085448,
"known_for_department": "Acting",
"name": "Yukee Chen",
"original_name": "Yukee Chen",
"popularity": 7.917,
"profile_path": "/3E8eXz1CJsE34LZsmQt1V3jH2Y5.jpg",
"cast_id": 4,
"character": "A Zhu",
"credit_id": "639572ec2cefc2007c1eb3c0",
"order": 1
},
{
"adult": false,
"gender": 1,
"id": 1397017,
"known_for_department": "Acting",
"name": "Liu Yase",
"original_name": "Liu Yase",
"popularity": 3.823,
"profile_path": "/2UBsASN6G37pjZtjl0Zse6S74y9.jpg",
"cast_id": 8,
"character": "A Zi",
"credit_id": "63980c59f5c8240083a9e015",
"order": 2
},
{
"adult": false,
"gender": 1,
"id": 84205,
"known_for_department": "Acting",
"name": "Kara Hui Ying-Hung",
"original_name": "Kara Hui Ying-Hung",
"popularity": 17.533,
"profile_path": "/6OV9kM62Y7M7EswtkpCThs0QAxg.jpg",
"cast_id": 9,
"character": "Ruan Xingzhu",
"credit_id": "63980c70f5c824007b890cd2",
"order": 3
},
{
"adult": false,
"gender": 2,
"id": 1800792,
"known_for_department": "Acting",
"name": "Wu Yue",
"original_name": "Wu Yue",
"popularity": 8.983,
"profile_path": "/hRePA3mh9mfAqX7SJ4rGxrNrxT7.jpg",
"cast_id": 7,
"character": "Murong Fu",
"credit_id": "63957308a0f1a200c880a34b",
"order": 4
},
{
"adult": false,
"gender": 2,
"id": 72732,
"known_for_department": "Acting",
"name": "Eddie Cheung",
"original_name": "Eddie Cheung",
"popularity": 5.704,
"profile_path": "/jTrBt3y43LsO8XYsQnWg8bdwsP2.jpg",
"cast_id": 14,
"character": "Duan Zhengchun",
"credit_id": "63980e36d05a0300cf2afd10",
"order": 5
},
{
"adult": false,
"gender": 1,
"id": 1255467,
"known_for_department": "Acting",
"name": "Grace Wong",
"original_name": "Grace Wong",
"popularity": 2.312,
"profile_path": "/Ab9GO6Awb183qaSP9VxlVNWZpFg.jpg",
"cast_id": 12,
"character": "Mrs. Ma",
"credit_id": "63980cd8d05a030094f1d7d3",
"order": 6
},
{
"adult": false,
"gender": 2,
"id": 1571926,
"known_for_department": "Acting",
"name": "Do Yuming",
"original_name": "Do Yuming",
"popularity": 3.349,
"profile_path": "/aqxa9aRrGOIiIcia0Vb3ErvXz4e.jpg",
"cast_id": 17,
"character": "Bai Shijing",
"credit_id": "63980fd28a88b20091ab7208",
"order": 7
},
{
"adult": false,
"gender": 2,
"id": 78877,
"known_for_department": "Acting",
"name": "Ray Lui",
"original_name": "Ray Lui",
"popularity": 5.367,
"profile_path": "/v6KV1Ib7ck9noPdwL6L2lAozeak.jpg",
"cast_id": 16,
"character": "Murong Bo",
"credit_id": "63980e6df5c824008bc95f42",
"order": 8
},
{
"adult": false,
"gender": 2,
"id": 1145614,
"known_for_department": "Directing",
"name": "Tsui Siu-Ming",
"original_name": "Tsui Siu-Ming",
"popularity": 1.31,
"profile_path": "/aY9b2ucIGylE3d7a1GNALk8qwH.jpg",
"cast_id": 15,
"character": "Jiu Mozhi",
"credit_id": "63980e5da1a9ba00a0003400",
"order": 9
},
{
"adult": false,
"gender": 1,
"id": 2961684,
"known_for_department": "Acting",
"name": "Cai Xiangyu",
"original_name": "Cai Xiangyu",
"popularity": 1.631,
"profile_path": "/9P9916rJ9SkiYBqrAckRsuO48FA.jpg",
"cast_id": 21,
"character": "A Bi",
"credit_id": "63cb7e60ea394900c827848a",
"order": 10
},
{
"adult": false,
"gender": 1,
"id": 1209782,
"known_for_department": "Acting",
"name": "Michelle Hu",
"original_name": "Michelle Hu",
"popularity": 8.5,
"profile_path": "/oNwNoLJg707G0HSIDnNU4mpaoIu.jpg",
"cast_id": 22,
"character": "Mrs. Xiao",
"credit_id": "63cb7e739a6435008def2fc7",
"order": 11
},
{
"adult": false,
"gender": 0,
"id": 3260046,
"known_for_department": "Acting",
"name": "Zhao Huawei",
"original_name": "Zhao Huawei",
"popularity": 0.6,
"profile_path": null,
"cast_id": 23,
"character": "Duan Yu",
"credit_id": "63cb7ea46d97e6007c9d47f2",
"order": 12
},
{
"adult": false,
"gender": 2,
"id": 1089434,
"known_for_department": "Acting",
"name": "Yu Kang",
"original_name": "Yu Kang",
"popularity": 4.673,
"profile_path": "/aga0SZgIsntO4ZNsOF28HBLMx3i.jpg",
"cast_id": 24,
"character": "You Ju",
"credit_id": "63cb7eba9a643500872aa4a4",
"order": 13
},
{
"adult": false,
"gender": 2,
"id": 1174692,
"known_for_department": "Acting",
"name": "Xu Xiangdong",
"original_name": "Xu Xiangdong",
"popularity": 2.684,
"profile_path": null,
"cast_id": 25,
"character": "Xuannan",
"credit_id": "63cb7ed0d363e500ba783604",
"order": 14
},
{
"adult": false,
"gender": 2,
"id": 65975,
"known_for_department": "Acting",
"name": "Yuen Cheung-Yan",
"original_name": "Yuen Cheung-Yan",
"popularity": 3.233,
"profile_path": "/oI1Q07u74Yod4t8K06aSvfEJIbH.jpg",
"cast_id": 26,
"character": "Xue Muhua",
"credit_id": "63cf408c0d2f5301d1ef7d31",
"order": 15
},
{
"adult": false,
"gender": 0,
"id": 3998591,
"known_for_department": "Acting",
"name": "Cheung Siu Fai",
"original_name": "Cheung Siu Fai",
"popularity": 0.6,
"profile_path": null,
"cast_id": 34,
"character": "",
"credit_id": "642fe90e9a643506f1afb11f",
"order": 16
},
{
"adult": false,
"gender": 0,
"id": 3998593,
"known_for_department": "Acting",
"name": "Cya Liu",
"original_name": "Cya Liu",
"popularity": 0.6,
"profile_path": null,
"cast_id": 35,
"character": "",
"credit_id": "642fe92531032500bd55bed7",
"order": 17
},
{
"adult": false,
"gender": 2,
"id": 1113443,
"known_for_department": "Acting",
"name": "Kenji Tanigaki",
"original_name": "Kenji Tanigaki",
"popularity": 3.134,
"profile_path": "/4WKEFXTK233yvrRvjA6TLIQiCn0.jpg",
"cast_id": 37,
"character": "",
"credit_id": "642fe970e92d83011306e9c7",
"order": 18
},
{
"adult": false,
"gender": 0,
"id": 3183831,
"known_for_department": "Acting",
"name": "Hua Yan",
"original_name": "Hua Yan",
"popularity": 0.6,
"profile_path": null,
"cast_id": 38,
"character": "Bandit",
"credit_id": "642fe9bae92d8300b6e41abd",
"order": 19
}
],
"crew": [
{
"adult": false,
"gender": 2,
"id": 1341,
"known_for_department": "Acting",
"name": "Donnie Yen",
"original_name": "Donnie Yen",
"popularity": 32.998,
"profile_path": "/hTlhrrZMj8hZVvD17j4KyAFWBHc.jpg",
"credit_id": "639572d2a1a9ba00a0fef209",
"department": "Directing",
"job": "Director"
},
{
"adult": false,
"gender": 2,
"id": 1341,
"known_for_department": "Acting",
"name": "Donnie Yen",
"original_name": "Donnie Yen",
"popularity": 32.998,
"profile_path": "/hTlhrrZMj8hZVvD17j4KyAFWBHc.jpg",
"credit_id": "63980c9679b3d400a0bc13f1",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 2,
"id": 64430,
"known_for_department": "Camera",
"name": "Chi Ying Chan",
"original_name": "Chi Ying Chan",
"popularity": 1.204,
"profile_path": "/yDChYg2NSVBYAmGdoyTDznaIfSv.jpg",
"credit_id": "63981028f5c82400b783039a",
"department": "Camera",
"job": "Director of Photography"
},
{
"adult": false,
"gender": 2,
"id": 548474,
"known_for_department": "Writing",
"name": "Wong Jing",
"original_name": "Wong Jing",
"popularity": 4.649,
"profile_path": "/gMmaDRst3OwnY1wClKt541AmslD.jpg",
"credit_id": "63980c892cefc20084b6093a",
"department": "Production",
"job": "Producer"
},
{
"adult": false,
"gender": 0,
"id": 551565,
"known_for_department": "Art",
"name": "Lau Sai-Wan",
"original_name": "Lau Sai-Wan",
"popularity": 0.6,
"profile_path": null,
"credit_id": "639810a5a1a9ba009430b5f2",
"department": "Art",
"job": "Art Direction"
},
{
"adult": false,
"gender": 1,
"id": 1006504,
"known_for_department": "Writing",
"name": "Chen Li",
"original_name": "Chen Li",
"popularity": 1.183,
"profile_path": "/uIkAFpDNsAL5sYmlartPNoV2BWn.jpg",
"credit_id": "64288650c5840d00d58e3c34",
"department": "Writing",
"job": "Writer"
},
{
"adult": false,
"gender": 2,
"id": 1113443,
"known_for_department": "Acting",
"name": "Kenji Tanigaki",
"original_name": "Kenji Tanigaki",
"popularity": 3.134,
"profile_path": "/4WKEFXTK233yvrRvjA6TLIQiCn0.jpg",
"credit_id": "62b0cfb19c24fc0061b28020",
"department": "Directing",
"job": "Action Director"
},
{
"adult": false,
"gender": 2,
"id": 1415282,
"known_for_department": "Directing",
"name": "Kam Ka-Wai",
"original_name": "Kam Ka-Wai",
"popularity": 1.821,
"profile_path": "/gsPOd7NYzugZJwnEI5DokM6plQC.jpg",
"credit_id": "64288624c0442901f0024683",
"department": "Directing",
"job": "Director"
},
{
"adult": false,
"gender": 2,
"id": 2095747,
"known_for_department": "Acting",
"name": "Andrew Yan Hua",
"original_name": "Andrew Yan Hua",
"popularity": 1.052,
"profile_path": null,
"credit_id": "6398107bd05a0300ae5105b3",
"department": "Directing",
"job": "Action Director"
},
{
"adult": false,
"gender": 2,
"id": 2195223,
"known_for_department": "Directing",
"name": "Leping Shen",
"original_name": "Leping Shen",
"popularity": 0.6,
"profile_path": "/ocdkTboTAdk6BkJKO2Wrq4yLoto.jpg",
"credit_id": "6428866a960cde0103a4b5be",
"department": "Writing",
"job": "Writer"
},
{
"adult": false,
"gender": 0,
"id": 2552279,
"known_for_department": "Directing",
"name": "Wei Zhu",
"original_name": "Wei Zhu",
"popularity": 0.98,
"profile_path": "/1hcEJ4rXam8i74RjTjfn6JKcSIo.jpg",
"credit_id": "642886880f3655011078cc3e",
"department": "Writing",
"job": "Writer"
},
{
"adult": false,
"gender": 0,
"id": 3113929,
"known_for_department": "Production",
"name": "Zhu Weijie",
"original_name": "Zhu Weijie",
"popularity": 0.6,
"profile_path": null,
"credit_id": "63980d8ed05a0300ae5103a0",
"department": "Production",
"job": "Executive Producer"
},
{
"adult": false,
"gender": 0,
"id": 3391253,
"known_for_department": "Directing",
"name": "Sheng Lingzhi",
"original_name": "Sheng Lingzhi",
"popularity": 0.6,
"profile_path": null,
"credit_id": "6428865e01b1ca0097fd97c6",
"department": "Writing",
"job": "Writer"
},
{
"adult": false,
"gender": 0,
"id": 3989726,
"known_for_department": "Writing",
"name": "He Ben",
"original_name": "He Ben",
"popularity": 0.668,
"profile_path": null,
"credit_id": "64288644c04429026b13431a",
"department": "Writing",
"job": "Writer"
},
{
"adult": false,
"gender": 0,
"id": 3989729,
"known_for_department": "Writing",
"name": "Xu Yifan",
"original_name": "Xu Yifan",
"popularity": 0.6,
"profile_path": null,
"credit_id": "6428867a960cde00e0bcdb7d",
"department": "Writing",
"job": "Writer"
}
]
},
"videos": {
"results": [
{
"iso_639_1": "en",
"iso_3166_1": "US",
"name": "Official US Trailer",
"key": "aHP_pyGtFIk",
"site": "YouTube",
"size": 1080,
"type": "Trailer",
"official": true,
"published_at": "2023-03-23T17:09:04.000Z",
"id": "641e49dcc613ce01031f8b85"
},
{
"iso_639_1": "en",
"iso_3166_1": "US",
"name": "Main Trailer",
"key": "es5NAYPdcdI",
"site": "YouTube",
"size": 1080,
"type": "Trailer",
"official": true,
"published_at": "2023-01-12T10:00:30.000Z",
"id": "640a298154f6eb00993d3ebf"
}
]
},
"images": {
"backdrops": [
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/rV56FkcHkzHJcBOOqoCeSDnoBff.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 1080,
"iso_639_1": null,
"file_path": "/skY1yDFcLF4h6CYDxndr5jHJNof.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 1920
},
{
"aspect_ratio": 1.778,
"height": 1080,
"iso_639_1": null,
"file_path": "/zS3ngMx0IJGzSUlAJ5u40utdCFK.jpg",
"vote_average": 5.172,
"vote_count": 1,
"width": 1920
},
{
"aspect_ratio": 1.778,
"height": 1080,
"iso_639_1": null,
"file_path": "/oREkJ5uPIryJz0zWMASkGVLzeIc.jpg",
"vote_average": 5.106,
"vote_count": 2,
"width": 1920
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": "en",
"file_path": "/qAL0fwjZCQ41TwybgERAAUp4kWA.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": null,
"file_path": "/bBERGgCqgUPzriFxsVrXccINprn.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 3840
},
{
"aspect_ratio": 1.778,
"height": 2160,
"iso_639_1": "fr",
"file_path": "/v4E3Gb7QikastXcvKzvn519bM1m.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 3840
}
],
"logos": [
{
"aspect_ratio": 4.046,
"height": 194,
"iso_639_1": "en",
"file_path": "/pqu3S5zlYRt9oPXmC4OI1r4dml5.png",
"vote_average": 5.312,
"vote_count": 1,
"width": 785
},
{
"aspect_ratio": 1.733,
"height": 288,
"iso_639_1": "en",
"file_path": "/jWexdriLJE0kFXG4Nct7C0tSnfJ.png",
"vote_average": 5.106,
"vote_count": 2,
"width": 499
},
{
"aspect_ratio": 1.691,
"height": 615,
"iso_639_1": "zh",
"file_path": "/slL3b5hNslfrjdM4qlXn3XhJcak.png",
"vote_average": 0,
"vote_count": 0,
"width": 1040
},
{
"aspect_ratio": 1.878,
"height": 147,
"iso_639_1": "zh",
"file_path": "/cd24lYjAb7dQRS7l4UrIs9D81go.png",
"vote_average": 0,
"vote_count": 0,
"width": 276
},
{
"aspect_ratio": 2.555,
"height": 1286,
"iso_639_1": "fr",
"file_path": "/4heIx9YFADRbnfkSzlFS6LVGTfT.png",
"vote_average": 0,
"vote_count": 0,
"width": 3286
},
{
"aspect_ratio": 2.497,
"height": 1273,
"iso_639_1": "fr",
"file_path": "/fn2o38gQPPqqmlH2qLyk4wP9GJT.png",
"vote_average": 0,
"vote_count": 0,
"width": 3179
}
],
"posters": [
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "cn",
"file_path": "/vvWyOCmBAND2p9UwaXZdALIDd2W.jpg",
"vote_average": 5.384,
"vote_count": 2,
"width": 2000
},
{
"aspect_ratio": 0.672,
"height": 2048,
"iso_639_1": "en",
"file_path": "/jGKCpt3zzbGZbgoza6HCvecqElM.jpg",
"vote_average": 5.318,
"vote_count": 3,
"width": 1376
},
{
"aspect_ratio": 0.71,
"height": 1600,
"iso_639_1": "cn",
"file_path": "/vksLBSmHahKD9F30vIrm0S7JsSO.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 1136
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "zh",
"file_path": "/zr28ZaTpZeB7ACFVIoa4YuxFHgh.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 2340,
"iso_639_1": "zh",
"file_path": "/k20PICIsiYMkD425UASSXs12M3.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 1560
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "cn",
"file_path": "/rafCyitNRMQUHDIVh3abWZRWofO.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 2100,
"iso_639_1": "de",
"file_path": "/b4A9L61eJfLL2eWPjsfwhDuzDsP.jpg",
"vote_average": 5.312,
"vote_count": 1,
"width": 1400
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "de",
"file_path": "/lEWeNdbPuJIUON6qUwwvRcYNgSU.jpg",
"vote_average": 5.246,
"vote_count": 2,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "fr",
"file_path": "/qGhdGdocth1csUlEnPEmYDfEmwY.jpg",
"vote_average": 5.238,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 1600,
"iso_639_1": "de",
"file_path": "/zFRCKPsJxuQqdtysUD7UaySnxKC.jpg",
"vote_average": 5.106,
"vote_count": 2,
"width": 1067
},
{
"aspect_ratio": 0.705,
"height": 1064,
"iso_639_1": "cn",
"file_path": "/vPRZUtW2tQZ0JzwqUcS739qDfOv.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 750
},
{
"aspect_ratio": 0.667,
"height": 2815,
"iso_639_1": "ko",
"file_path": "/rl6lMKv6XRPPOgMTcTmZWSsEsLH.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1877
},
{
"aspect_ratio": 0.666,
"height": 2067,
"iso_639_1": "zh",
"file_path": "/dvlaQWBoZYmgHrG9r4V5DWn7e4N.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1377
},
{
"aspect_ratio": 0.705,
"height": 1532,
"iso_639_1": "zh",
"file_path": "/vNpAREC0oLWUx1F7NWSdGO89Qaf.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1080
},
{
"aspect_ratio": 0.667,
"height": 1711,
"iso_639_1": "ko",
"file_path": "/fmcRd957yQnlyEFltODkScqldKr.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1141
},
{
"aspect_ratio": 0.672,
"height": 2048,
"iso_639_1": "fr",
"file_path": "/1d4Iru6pLYESE1DsE8BVxK5OwRm.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1376
},
{
"aspect_ratio": 0.667,
"height": 2400,
"iso_639_1": "zh",
"file_path": "/lI3gD04BXUnB9ZhPmceeAVmnwHp.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1600
},
{
"aspect_ratio": 0.667,
"height": 1600,
"iso_639_1": "en",
"file_path": "/2vA3CqwZ01LUsEaYRz2ZY6Om9xi.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1067
},
{
"aspect_ratio": 0.667,
"height": 1500,
"iso_639_1": "en",
"file_path": "/uSmqyGyrjP1FF5UpxryUsSdQ4w0.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1000
},
{
"aspect_ratio": 0.667,
"height": 1500,
"iso_639_1": "en",
"file_path": "/7JzNqNnisJnKH69mYuXNedznAAH.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1000
},
{
"aspect_ratio": 0.667,
"height": 1500,
"iso_639_1": "en",
"file_path": "/o6229QNb9FHwWBCemZxM8mCBDgV.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1000
},
{
"aspect_ratio": 0.667,
"height": 1500,
"iso_639_1": "en",
"file_path": "/7UyeEpMQXp5zlzSVHif8ZWDE2tA.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1000
},
{
"aspect_ratio": 0.666,
"height": 1367,
"iso_639_1": "en",
"file_path": "/xoznyg9SNXjHPwT9kMMrPr6vW34.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 911
},
{
"aspect_ratio": 0.667,
"height": 2100,
"iso_639_1": "en",
"file_path": "/aS3mnTkt6cnPcndb0l3F0J7GTDK.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1400
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "tr",
"file_path": "/zP9mNbn55fNGdbHOAqzMt07aN4s.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0.667,
"height": 1500,
"iso_639_1": "fr",
"file_path": "/tObrbSUlTGurumll1SaIeVf14TT.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1000
},
{
"aspect_ratio": 0.667,
"height": 1500,
"iso_639_1": "fr",
"file_path": "/rvy5WHZMY8YEaCmJjNvD9DW73AR.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1000
},
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "fr",
"file_path": "/hCsXU4l8NdhmQTaLknzg9em73tF.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
}
]
}
}
}

View file

@ -0,0 +1,36 @@
---
import '../styles/styles.css';
import { ViewTransitions } from 'astro:transitions';
import Footer from '../components/Footer.astro';
import Nav from '../components/Nav.astro';
export interface Props {
title: string;
}
const { title } = Astro.props as Props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} />
<meta name="view-transition" content="same-origin" />
<title>{title}</title>
<ViewTransitions />
</head>
<body class="font-sans bg-gray-900 text-white">
<div class="h-screen overflow-hidden flex flex-col">
<Nav />
<div id="container" class="h-full flex-1 overflow-y-auto">
<div id="content">
<slot />
</div>
<Footer />
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,8 @@
---
import Layout from '../layouts/Layout.astro';
import MovieList from '../components/MovieList.astro';
---
<Layout title="Movies List">
<MovieList />
</Layout>

View file

@ -0,0 +1,14 @@
---
import Layout from '../../layouts/Layout.astro';
import MovieDetails from '../../components/MovieDetails.astro';
import { getDataEntryById } from 'astro:content';
// Data collection bug?
const id: any = '/src/content/movies/' + Astro.params.id;
const result = await getDataEntryById('movies', id);
const data = result.data.data;
---
<Layout title={`${data.title} on Movies List`}>
<MovieDetails data={data} />
</Layout>

View file

@ -0,0 +1,327 @@
{
"page": 1,
"results": [
{
"adult": false,
"backdrop_path": "/iIvQnZyzgx9TkbrOgcXx0p7aLiq.jpg",
"genre_ids": [27, 53],
"id": 1008042,
"original_language": "en",
"original_title": "Talk to Me",
"overview": "When a group of friends discover how to conjure spirits using an embalmed hand, they become hooked on the new thrill, until one of them goes too far and unleashes terrifying supernatural forces.",
"popularity": 2292.177,
"poster_path": "/kdPMUMJzyYAc4roD52qavX0nLIC.jpg",
"release_date": "2023-07-26",
"title": "Talk to Me",
"video": false,
"vote_average": 7.3,
"vote_count": 686
},
{
"adult": false,
"backdrop_path": "/ctMserH8g2SeOAnCw5gFjdQF8mo.jpg",
"genre_ids": [35, 12, 14],
"id": 346698,
"original_language": "en",
"original_title": "Barbie",
"overview": "Barbie and Ken are having the time of their lives in the colorful and seemingly perfect world of Barbie Land. However, when they get a chance to go to the real world, they soon discover the joys and perils of living among humans.",
"popularity": 1899.184,
"poster_path": "/iuFNMS8U5cb6xfzi51Dbkovj7vM.jpg",
"release_date": "2023-07-19",
"title": "Barbie",
"video": false,
"vote_average": 7.3,
"vote_count": 4757
},
{
"adult": false,
"backdrop_path": "/4XM8DUTQb3lhLemJC51Jx4a2EuA.jpg",
"genre_ids": [28, 80, 53],
"id": 385687,
"original_language": "en",
"original_title": "Fast X",
"overview": "Over many missions and against impossible odds, Dom Toretto and his family have outsmarted, out-nerved and outdriven every foe in their path. Now, they confront the most lethal opponent they've ever faced: A terrifying threat emerging from the shadows of the past who's fueled by blood revenge, and who is determined to shatter this family and destroy everything—and everyone—that Dom loves, forever.",
"popularity": 1973.052,
"poster_path": "/fiVW06jE7z9YnO4trhaMEdclSiC.jpg",
"release_date": "2023-05-17",
"title": "Fast X",
"video": false,
"vote_average": 7.3,
"vote_count": 3749
},
{
"adult": false,
"backdrop_path": "/8pjWz2lt29KyVGoq1mXYu6Br7dE.jpg",
"genre_ids": [28, 878, 27],
"id": 615656,
"original_language": "en",
"original_title": "Meg 2: The Trench",
"overview": "An exploratory dive into the deepest depths of the ocean of a daring research team spirals into chaos when a malevolent mining operation threatens their mission and forces them into a high-stakes battle for survival.",
"popularity": 1804.581,
"poster_path": "/4m1Au3YkjqsxF8iwQy0fPYSxE0h.jpg",
"release_date": "2023-08-02",
"title": "Meg 2: The Trench",
"video": false,
"vote_average": 7,
"vote_count": 1823
},
{
"adult": false,
"backdrop_path": "/c6Splshb8lb2Q9OvUfhpqXl7uP0.jpg",
"genre_ids": [28, 53],
"id": 717930,
"original_language": "en",
"original_title": "Kandahar",
"overview": "After his mission is exposed, an undercover CIA operative stuck deep in hostile territory in Afghanistan must fight his way out, alongside his Afghan translator, to an extraction point in Kandahar, all whilst avoiding elite enemy forces and foreign spies tasked with hunting them down.",
"popularity": 1206.966,
"poster_path": "/lCanGgsqF4xD2WA5NF8PWeT3IXd.jpg",
"release_date": "2023-05-25",
"title": "Kandahar",
"video": false,
"vote_average": 6.8,
"vote_count": 507
},
{
"adult": false,
"backdrop_path": "/53z2fXEKfnNg2uSOPss2unPBGX1.jpg",
"genre_ids": [27, 9648, 53],
"id": 968051,
"original_language": "en",
"original_title": "The Nun II",
"overview": "In 1956 France, a priest is violently murdered, and Sister Irene begins to investigate. She once again comes face-to-face with a powerful evil.",
"popularity": 1296.77,
"poster_path": "/5gzzkR7y3hnY8AD1wXjCnVlHba5.jpg",
"release_date": "2023-09-06",
"title": "The Nun II",
"video": false,
"vote_average": 6.7,
"vote_count": 229
},
{
"adult": false,
"backdrop_path": "/4fLZUr1e65hKPPVw0R3PmKFKxj1.jpg",
"genre_ids": [16, 35, 10751, 14, 10749],
"id": 976573,
"original_language": "en",
"original_title": "Elemental",
"overview": "In a city where fire, water, land and air residents live together, a fiery young woman and a go-with-the-flow guy will discover something elemental: how much they have in common.",
"popularity": 953.333,
"poster_path": "/4Y1WNkd88JXmGfhtWR7dmDAo1T2.jpg",
"release_date": "2023-06-14",
"title": "Elemental",
"video": false,
"vote_average": 7.8,
"vote_count": 2182
},
{
"adult": false,
"backdrop_path": "/H6j5smdpRqP9a8UnhWp6zfl0SC.jpg",
"genre_ids": [28, 878, 12],
"id": 565770,
"original_language": "en",
"original_title": "Blue Beetle",
"overview": "Recent college grad Jaime Reyes returns home full of aspirations for his future, only to find that home is not quite as he left it. As he searches to find his purpose in the world, fate intervenes when Jaime unexpectedly finds himself in possession of an ancient relic of alien biotechnology: the Scarab.",
"popularity": 1007.105,
"poster_path": "/vNfL4DYnonltukBrrgMmw94zMYL.jpg",
"release_date": "2023-08-16",
"title": "Blue Beetle",
"video": false,
"vote_average": 7.2,
"vote_count": 541
},
{
"adult": false,
"backdrop_path": "/9m161GawbY3cWxe6txd1NOHTjd0.jpg",
"genre_ids": [878, 12, 28, 14],
"id": 335977,
"original_language": "en",
"original_title": "Indiana Jones and the Dial of Destiny",
"overview": "Finding himself in a new era, and approaching retirement, Indy wrestles with fitting into a world that seems to have outgrown him. But as the tentacles of an all-too-familiar evil return in the form of an old rival, Indy must don his hat and pick up his whip once more to make sure an ancient and powerful artifact doesn't fall into the wrong hands.",
"popularity": 804.982,
"poster_path": "/Af4bXE63pVsb2FtbW8uYIyPBadD.jpg",
"release_date": "2023-06-28",
"title": "Indiana Jones and the Dial of Destiny",
"video": false,
"vote_average": 6.7,
"vote_count": 1650
},
{
"adult": false,
"backdrop_path": "/jkKVLzLWjSvTnc84VzeljhSy6j8.jpg",
"genre_ids": [10749, 18],
"id": 820525,
"original_language": "en",
"original_title": "After Everything",
"overview": "Besieged by writers block and the crushing breakup with Tessa, Hardin travels to Portugal in search of a woman he wronged in the past and to find himself. Hoping to win back Tessa, he realizes he needs to change his ways before he can make the ultimate commitment.",
"popularity": 650.272,
"poster_path": "/gZLGCibvFY4zmt8sWUZcbBTHRtk.jpg",
"release_date": "2023-09-13",
"title": "After Everything",
"video": false,
"vote_average": 6.4,
"vote_count": 27
},
{
"adult": false,
"backdrop_path": "/waBWlJlMpyFb7STkFHfFvJKgwww.jpg",
"genre_ids": [28, 18],
"id": 678512,
"original_language": "en",
"original_title": "Sound of Freedom",
"overview": "The story of Tim Ballard, a former US government agent, who quits his job in order to devote his life to rescuing children from global sex traffickers.",
"popularity": 668.456,
"poster_path": "/kSf9svfL2WrKeuK8W08xeR5lTn8.jpg",
"release_date": "2023-07-03",
"title": "Sound of Freedom",
"video": false,
"vote_average": 8,
"vote_count": 458
},
{
"adult": false,
"backdrop_path": "/iiXliCeykkzmJ0Eg9RYJ7F2CWSz.jpg",
"genre_ids": [28, 9648, 53, 80],
"id": 762430,
"original_language": "en",
"original_title": "Retribution",
"overview": "When a mysterious caller puts a bomb under his car seat, Matt Turner begins a high-speed chase across the city to complete a specific series of tasks. With his kids trapped in the back seat and a bomb that will explode if they get out of the car, a normal commute becomes a twisted game of life or death as Matt follows the stranger's increasingly dangerous instructions in a race against time to save his family.",
"popularity": 702.724,
"poster_path": "/oUmmY7QWWn7OhKlcPOnirHJpP1F.jpg",
"release_date": "2023-08-23",
"title": "Retribution",
"video": false,
"vote_average": 6.6,
"vote_count": 130
},
{
"adult": false,
"backdrop_path": "/2vFuG6bWGyQUzYS9d69E5l85nIz.jpg",
"genre_ids": [28, 12, 878],
"id": 667538,
"original_language": "en",
"original_title": "Transformers: Rise of the Beasts",
"overview": "When a new threat capable of destroying the entire planet emerges, Optimus Prime and the Autobots must team up with a powerful faction known as the Maximals. With the fate of humanity hanging in the balance, humans Noah and Elena will do whatever it takes to help the Transformers as they engage in the ultimate battle to save Earth.",
"popularity": 650.789,
"poster_path": "/gPbM0MK8CP8A174rmUwGsADNYKD.jpg",
"release_date": "2023-06-06",
"title": "Transformers: Rise of the Beasts",
"video": false,
"vote_average": 7.5,
"vote_count": 3189
},
{
"adult": false,
"backdrop_path": "/w2nFc2Rsm93PDkvjY4LTn17ePO0.jpg",
"genre_ids": [16, 35, 28],
"id": 614930,
"original_language": "en",
"original_title": "Teenage Mutant Ninja Turtles: Mutant Mayhem",
"overview": "After years of being sheltered from the human world, the Turtle brothers set out to win the hearts of New Yorkers and be accepted as normal teenagers through heroic acts. Their new friend April O'Neil helps them take on a mysterious crime syndicate, but they soon get in over their heads when an army of mutants is unleashed upon them.",
"popularity": 644.874,
"poster_path": "/oupWWrVuCgNEa5GcjdkpjCYbx2X.jpg",
"release_date": "2023-07-31",
"title": "Teenage Mutant Ninja Turtles: Mutant Mayhem",
"video": false,
"vote_average": 7.3,
"vote_count": 541
},
{
"adult": false,
"backdrop_path": "/yF1eOkaYvwiORauRCPWznV9xVvi.jpg",
"genre_ids": [28, 12, 878],
"id": 298618,
"original_language": "en",
"original_title": "The Flash",
"overview": "When his attempt to save his family inadvertently alters the future, Barry Allen becomes trapped in a reality in which General Zod has returned and there are no Super Heroes to turn to. In order to save the world that he is in and return to the future that he knows, Barry's only hope is to race for his life. But will making the ultimate sacrifice be enough to reset the universe?",
"popularity": 561.181,
"poster_path": "/rktDFPbfHfUbArZ6OOOKsXcv0Bm.jpg",
"release_date": "2023-06-13",
"title": "The Flash",
"video": false,
"vote_average": 6.9,
"vote_count": 2873
},
{
"adult": false,
"backdrop_path": "/4HodYYKEIsGOdinkGi2Ucz6X9i0.jpg",
"genre_ids": [16, 28, 12],
"id": 569094,
"original_language": "en",
"original_title": "Spider-Man: Across the Spider-Verse",
"overview": "After reuniting with Gwen Stacy, Brooklyns full-time, friendly neighborhood Spider-Man is catapulted across the Multiverse, where he encounters the Spider Society, a team of Spider-People charged with protecting the Multiverses very existence. But when the heroes clash on how to handle a new threat, Miles finds himself pitted against the other Spiders and must set out on his own to save those he loves most.",
"popularity": 842.076,
"poster_path": "/8Vt6mWEReuy4Of61Lnj5Xj704m8.jpg",
"release_date": "2023-05-31",
"title": "Spider-Man: Across the Spider-Verse",
"video": false,
"vote_average": 8.5,
"vote_count": 4261
},
{
"adult": false,
"backdrop_path": "/9fOfsVHZHig6MHPHczv0zMY6cKc.jpg",
"genre_ids": [28, 53, 10752, 18],
"id": 1880,
"original_language": "en",
"original_title": "Red Dawn",
"overview": "It is the dawn of World War III. In mid-western America, a group of teenagers band together to defend their town—and their country—from invading Soviet forces.",
"popularity": 579.024,
"poster_path": "/a2GkHcioc2QEFJbQk1NTB85u3vD.jpg",
"release_date": "1984-08-10",
"title": "Red Dawn",
"video": false,
"vote_average": 6.3,
"vote_count": 670
},
{
"adult": false,
"backdrop_path": "/3mYCjwll5RG342Dz1f8HcnT8tV.jpg",
"genre_ids": [28, 80],
"id": 606403,
"original_language": "ko",
"original_title": "특송",
"overview": "Eun-ha, who is a normal junkyard employee, secretly works as a delivery clerk that deals with unusual delivery requests. One day, Eun-ha heads to Seoul to pick up a client who is involved in a gambling crime that wants to flee overseas. However, Eun-ha meets the client's young son at the pick-up point, instead of the client himself. Kyeong-pil, a current police officer who is actually masterminding the whole gambling crime, chases after the missing child who has the security key to the bank account that holds 30 million dollars.",
"popularity": 525.582,
"poster_path": "/fYT7JB4sU1XXeawEXOdQ3TtkFB2.jpg",
"release_date": "2022-01-12",
"title": "Special Delivery",
"video": false,
"vote_average": 6.9,
"vote_count": 105
},
{
"adult": false,
"backdrop_path": "/rV56FkcHkzHJcBOOqoCeSDnoBff.jpg",
"genre_ids": [28, 18],
"id": 990140,
"original_language": "cn",
"original_title": "天龍八部之喬峰傳",
"overview": "Qiao Feng is the respected leader of a roving band of martial artists. After he is wrongfully accused of murder and subsequently exiled, Qiao Feng goes on the run in search of answers about his own mysterious origin story—and the unknown enemies working to destroy him from the shadows.",
"popularity": 522.83,
"poster_path": "/jGKCpt3zzbGZbgoza6HCvecqElM.jpg",
"release_date": "2023-01-16",
"title": "Sakra",
"video": false,
"vote_average": 6.6,
"vote_count": 86
},
{
"adult": false,
"backdrop_path": "/4wVFtesa5YEWuAUHRcxoCN1Y1uN.jpg",
"genre_ids": [28, 53],
"id": 1085218,
"original_language": "da",
"original_title": "Underverden 2",
"overview": "Seven years ago, Zaid went to war against the Copenhagen underworld to avenge his dead brother. His identity as a respected doctor of cardiology and life as a family man is but a fading dream, and in prison Zaid suffers the loss of his son Noah, whom he barely knows. When a police agent approaches Zaid and offers him a deal to be released in exchange for infiltrating the Copenhagen underworld, he sees his chance to reclaim the remnants of the family life he left behind. But everything has a price, and Zaid realizes that he has now seriously endangered his son's life. After all, once you become part of the underworld, is there any way out?",
"popularity": 524.476,
"poster_path": "/c8B4DsVcFVDLVmbpHMHU3RjLNAV.jpg",
"release_date": "2023-04-13",
"title": "Darkland: The Return",
"video": false,
"vote_average": 6.2,
"vote_count": 59
}
],
"total_pages": 40154,
"total_results": 803062
}

View file

@ -0,0 +1,254 @@
import {
getNavigationType,
getPathId,
isBackNavigation,
shouldNotIntercept,
updateTheDOMSomehow,
useTvFragment,
} from './utils';
// View Transitions support cross-document navigations.
// Should compare performace.
// https://github.com/WICG/view-transitions/blob/main/explainer.md#cross-document-same-origin-transitions
// https://github.com/WICG/view-transitions/blob/main/explainer.md#script-events
function shouldDisableSpa() {
return false;
}
navigation.addEventListener('navigate', (navigateEvent) => {
if (shouldDisableSpa()) return;
if (shouldNotIntercept(navigateEvent)) return;
const toUrl = new URL(navigateEvent.destination.url);
const toPath = toUrl.pathname;
const fromPath = location.pathname;
const navigationType = getNavigationType(fromPath, toPath);
if (location.origin !== toUrl.origin) return;
switch (navigationType) {
case 'home-to-movie':
case 'tv-to-show':
handleHomeToMovieTransition(navigateEvent, getPathId(toPath));
break;
case 'movie-to-home':
case 'show-to-tv':
handleMovieToHomeTransition(navigateEvent, getPathId(fromPath));
break;
case 'movie-to-person':
handleMovieToPersonTransition(navigateEvent, getPathId(fromPath), getPathId(toPath));
break;
case 'person-to-movie':
case 'person-to-show':
handlePersonToMovieTransition(navigateEvent, getPathId(fromPath), getPathId(toPath));
break;
default:
return;
}
});
// TODO: https://developer.chrome.com/docs/web-platform/view-transitions/#transitions-as-an-enhancement
function handleHomeToMovieTransition(navigateEvent, movieId) {
navigateEvent.intercept({
async handler() {
const fragmentUrl = useTvFragment(navigateEvent)
? '/fragments/TvDetails'
: '/fragments/MovieDetails';
const response = await fetch(`${fragmentUrl}/${movieId}`);
const data = await response.text();
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
const thumbnail = document.getElementById(`movie-poster-${movieId}`);
if (thumbnail) {
thumbnail.style.viewTransitionName = 'movie-poster';
}
const transition = document.startViewTransition(() => {
if (thumbnail) {
thumbnail.style.viewTransitionName = '';
}
document.getElementById('container').scrollTop = 0;
updateTheDOMSomehow(data);
});
await transition.finished;
},
});
}
function handleMovieToHomeTransition(navigateEvent, movieId) {
navigateEvent.intercept({
scroll: 'manual',
async handler() {
const fragmentUrl = useTvFragment(navigateEvent)
? '/fragments/TvList'
: '/fragments/MovieList';
const response = await fetch(fragmentUrl);
const data = await response.text();
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
const tempHomePage = document.createElement('div');
const moviePoster = document.getElementById(`movie-poster`);
let thumbnail;
// If the movie poster is not in the home page, removes the transition style so that
// the poster doesn't stay on the page while transitioning
tempHomePage.innerHTML = data;
if (!tempHomePage.querySelector(`#movie-poster-${movieId}`)) {
moviePoster?.classList.remove('movie-poster');
}
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
thumbnail = document.getElementById(`movie-poster-${movieId}`);
if (thumbnail) {
thumbnail.scrollIntoViewIfNeeded();
thumbnail.style.viewTransitionName = 'movie-poster';
}
});
await transition.finished;
if (thumbnail) {
thumbnail.style.viewTransitionName = '';
}
},
});
}
function handleMovieToPersonTransition(navigateEvent, movieId, personId) {
// TODO: https://developer.chrome.com/docs/web-platform/view-transitions/#not-a-polyfill
// ...has example of `back-transition` class applied to document
const isBack = isBackNavigation(navigateEvent);
navigateEvent.intercept({
async handler() {
const response = await fetch('/fragments/PersonDetails/' + personId);
const data = await response.text();
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
let personThumbnail;
let moviePoster;
let movieThumbnail;
if (!isBack) {
// We're transitioning the person photo; we need to remove the transition of the poster
// so that it doesn't stay on the page while transitioning
moviePoster = document.getElementById(`movie-poster`);
if (moviePoster) {
moviePoster.classList.remove('movie-poster');
}
personThumbnail = document.getElementById(`person-photo-${personId}`);
if (personThumbnail) {
personThumbnail.style.viewTransitionName = 'person-photo';
}
}
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
if (personThumbnail) {
personThumbnail.style.viewTransitionName = '';
}
if (isBack) {
// If we're coming back to the person page, we're transitioning
// into the movie poster thumbnail, so we need to add the tag to it
movieThumbnail = document.getElementById(`movie-poster-${movieId}`);
if (movieThumbnail) {
movieThumbnail.scrollIntoViewIfNeeded();
movieThumbnail.style.viewTransitionName = 'movie-poster';
}
}
document.getElementById('container').scrollTop = 0;
});
await transition.finished;
if (movieThumbnail) {
movieThumbnail.style.viewTransitionName = '';
}
},
});
}
function handlePersonToMovieTransition(navigateEvent, personId, movieId) {
const isBack = isBackNavigation(navigateEvent);
navigateEvent.intercept({
scroll: 'manual',
async handler() {
const fragmentUrl = useTvFragment(navigateEvent)
? '/fragments/TvDetails'
: '/fragments/MovieDetails';
const response = await fetch(`${fragmentUrl}/${movieId}`);
const data = await response.text();
if (!document.startViewTransition) {
updateTheDOMSomehow(data);
return;
}
let thumbnail;
let moviePoster;
let movieThumbnail;
if (!isBack) {
movieThumbnail = document.getElementById(`movie-poster-${movieId}`);
if (movieThumbnail) {
movieThumbnail.style.viewTransitionName = 'movie-poster';
}
}
const transition = document.startViewTransition(() => {
updateTheDOMSomehow(data);
if (isBack) {
moviePoster = document.getElementById(`movie-poster`);
if (moviePoster) {
moviePoster.classList.remove('movie-poster');
}
if (personId) {
thumbnail = document.getElementById(`person-photo-${personId}`);
if (thumbnail) {
thumbnail.scrollIntoViewIfNeeded();
thumbnail.style.viewTransitionName = 'person-photo';
}
}
} else {
document.getElementById('container').scrollTop = 0;
if (movieThumbnail) {
movieThumbnail.style.viewTransitionName = '';
}
}
});
await transition.finished;
if (thumbnail) {
thumbnail.style.viewTransitionName = '';
}
if (moviePoster) {
moviePoster.classList.add('movie-poster');
}
},
});
}

View file

@ -0,0 +1,76 @@
export function getNavigationType(fromPath, toPath) {
if (fromPath.startsWith('/movies') && toPath === '/') {
return 'movie-to-home';
}
if (fromPath === '/tv' && toPath.startsWith('/tv/')) {
return 'tv-to-show';
}
if (fromPath === '/' && toPath.startsWith('/movies')) {
return 'home-to-movie';
}
if (fromPath.startsWith('/tv/') && toPath === '/tv') {
return 'show-to-tv';
}
if (
(fromPath.startsWith('/movies') || fromPath.startsWith('/tv')) &&
toPath.startsWith('/people')
) {
return 'movie-to-person';
}
if (
fromPath.startsWith('/people') &&
(toPath.startsWith('/movies') || toPath.startsWith('/tv/'))
) {
return 'person-to-movie';
}
return 'other';
}
export function isBackNavigation(navigateEvent) {
if (navigateEvent.navigationType === 'push' || navigateEvent.navigationType === 'replace') {
return false;
}
if (
navigateEvent.destination.index !== -1 &&
navigateEvent.destination.index < navigation.currentEntry.index
) {
return true;
}
return false;
}
export function shouldNotIntercept(navigationEvent) {
return (
navigationEvent.canIntercept === false ||
// If this is just a hashChange,
// just let the browser handle scrolling to the content.
navigationEvent.hashChange ||
// If this is a download,
// let the browser perform the download.
navigationEvent.downloadRequest ||
// If this is a form submission,
// let that go to the server.
navigationEvent.formData
);
}
export function useTvFragment(navigateEvent) {
const toUrl = new URL(navigateEvent.destination.url);
const toPath = toUrl.pathname;
return toPath.startsWith('/tv');
}
export function getPathId(path) {
return path.split('/')[2];
}
export function updateTheDOMSomehow(data) {
document.getElementById('content').innerHTML = data;
}

View file

@ -0,0 +1,63 @@
@keyframes fade-in {
from {
opacity: 0;
}
}
@keyframes fade-out {
to {
opacity: 0;
}
}
@keyframes slide-from-right {
from {
transform: translateX(30px);
}
}
@keyframes slide-to-left {
to {
transform: translateX(-30px);
}
}
::view-transition-old(root) {
animation:
90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}
::view-transition-new(root) {
animation:
210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
::view-transition-old(movie-poster),
::view-transition-new(movie-poster) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-image-pair(movie-poster) {
isolation: none;
}
.nav {
view-transition-name: main-header;
contain: paint;
}
.movie-poster {
contain: paint;
}
.person-photo {
view-transition-name: person-photo;
contain: paint;
}
.thumbnail {
contain: paint;
}

View file

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
width: {
96: '24rem',
},
},
},
plugins: [],
};

View file

@ -0,0 +1,6 @@
{
"extends": "astro/tsconfigs/base",
"compilerOptions": {
"resolveJsonModule": true
}
}

View file

@ -11,7 +11,7 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/markdoc": "^0.5.0",
"astro": "^3.1.2"
"@astrojs/markdoc": "^0.5.2",
"astro": "^3.2.3"
}
}

View file

@ -11,8 +11,8 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/markdown-remark": "^3.2.0",
"astro": "^3.1.2",
"@astrojs/markdown-remark": "^3.2.1",
"astro": "^3.2.3",
"hast-util-select": "^5.0.5",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.1.0",

View file

@ -11,6 +11,6 @@
"astro": "astro"
},
"dependencies": {
"astro": "^3.1.2"
"astro": "^3.2.3"
}
}

View file

@ -11,9 +11,9 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/mdx": "^1.1.0",
"@astrojs/preact": "^3.0.0",
"astro": "^3.1.2",
"@astrojs/mdx": "^1.1.1",
"@astrojs/preact": "^3.0.1",
"astro": "^3.2.3",
"preact": "^10.17.1"
}
}

View file

@ -11,9 +11,9 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/preact": "^3.0.0",
"@astrojs/preact": "^3.0.1",
"@nanostores/preact": "^0.5.0",
"astro": "^3.1.2",
"astro": "^3.2.3",
"nanostores": "^0.9.3",
"preact": "^10.17.1"
}

View file

@ -11,10 +11,10 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/mdx": "^1.1.0",
"@astrojs/tailwind": "^5.0.0",
"@astrojs/mdx": "^1.1.1",
"@astrojs/tailwind": "^5.0.1",
"@types/canvas-confetti": "^1.6.0",
"astro": "^3.1.2",
"astro": "^3.2.3",
"autoprefixer": "^10.4.15",
"canvas-confetti": "^1.6.0",
"postcss": "^8.4.28",

View file

@ -11,7 +11,7 @@
"astro": "astro"
},
"dependencies": {
"astro": "^3.1.2",
"astro": "^3.2.3",
"vite-plugin-pwa": "0.16.4",
"workbox-window": "^7.0.0"
}

View file

@ -12,7 +12,7 @@
"test": "vitest"
},
"dependencies": {
"astro": "^3.1.2",
"astro": "^3.2.3",
"vitest": "^0.34.2"
}
}

View file

@ -1,5 +1,139 @@
# astro
## 3.2.3
### Patch Changes
- [#8737](https://github.com/withastro/astro/pull/8737) [`6f60da805`](https://github.com/withastro/astro/commit/6f60da805e0014bc50dd07bef972e91c73560c3c) Thanks [@ematipico](https://github.com/ematipico)! - Add provenance statement when publishing the library from CI
- [#8747](https://github.com/withastro/astro/pull/8747) [`d78806dfe`](https://github.com/withastro/astro/commit/d78806dfe0301ea7ffe6c7c1f783bd415ac7cda9) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Improve error message when user attempts to render a dynamic component reference
- [#8736](https://github.com/withastro/astro/pull/8736) [`d1c75fe15`](https://github.com/withastro/astro/commit/d1c75fe158839699c59728cf3a83888e8c72a459) Thanks [@bluwy](https://github.com/bluwy)! - Fix `tsconfig.json` update causing the server to crash
- [#8743](https://github.com/withastro/astro/pull/8743) [`aa265d730`](https://github.com/withastro/astro/commit/aa265d73024422967c1b1c68ad268c419c6c798f) Thanks [@bluwy](https://github.com/bluwy)! - Remove unused CSS output files when inlined
- [#8700](https://github.com/withastro/astro/pull/8700) [`78adbc443`](https://github.com/withastro/astro/commit/78adbc4433208458291e36713909762e148e1e5d) Thanks [@jacobthesheep](https://github.com/jacobthesheep)! - Update link for Netlify SSR
- [#8729](https://github.com/withastro/astro/pull/8729) [`21e0757ea`](https://github.com/withastro/astro/commit/21e0757ea22a57d344c934045ca19db93b684436) Thanks [@lilnasy](https://github.com/lilnasy)! - Node-based adapters now create less server-side javascript
- [#8730](https://github.com/withastro/astro/pull/8730) [`357270f2a`](https://github.com/withastro/astro/commit/357270f2a3d0bf2aa634ba7e52e9d17618eff4a7) Thanks [@natemoo-re](https://github.com/natemoo-re)! - Improve `astro info` copy to clipboard compatability
- Updated dependencies [[`21f482657`](https://github.com/withastro/astro/commit/21f4826576c2c812a1604e18717799da3470decd), [`6f60da805`](https://github.com/withastro/astro/commit/6f60da805e0014bc50dd07bef972e91c73560c3c), [`21e0757ea`](https://github.com/withastro/astro/commit/21e0757ea22a57d344c934045ca19db93b684436)]:
- @astrojs/markdown-remark@3.2.1
- @astrojs/internal-helpers@0.2.1
- @astrojs/telemetry@3.0.3
## 3.2.2
### Patch Changes
- [#8724](https://github.com/withastro/astro/pull/8724) [`455af3235`](https://github.com/withastro/astro/commit/455af3235b3268852e6988accecc796f03f6d16e) Thanks [@bluwy](https://github.com/bluwy)! - Fix CSS styles on Windows
- [#8710](https://github.com/withastro/astro/pull/8710) [`4c2bec681`](https://github.com/withastro/astro/commit/4c2bec681b0752e7215b8a32bd2d44bf477adac1) Thanks [@matthewp](https://github.com/matthewp)! - Fixes View transition styles being missing when component used multiple times
## 3.2.1
### Patch Changes
- [#8680](https://github.com/withastro/astro/pull/8680) [`31c59ad8b`](https://github.com/withastro/astro/commit/31c59ad8b6a72f95c98a306ecf92d198c03110b4) Thanks [@bluwy](https://github.com/bluwy)! - Fix hydration on slow connection
- [#8698](https://github.com/withastro/astro/pull/8698) [`47ea310f0`](https://github.com/withastro/astro/commit/47ea310f01d06ed1562c790bec348718a2fa8277) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Use a Node-specific image endpoint to resolve images in dev and Node SSR. This should fix many issues related to getting 404 from the \_image endpoint under certain configurations
- [#8706](https://github.com/withastro/astro/pull/8706) [`345808170`](https://github.com/withastro/astro/commit/345808170fce783ddd3c9a4035a91fa64dcc5f46) Thanks [@bluwy](https://github.com/bluwy)! - Fix duplicated Astro and Vite injected styles
## 3.2.0
### Minor Changes
- [#8696](https://github.com/withastro/astro/pull/8696) [`2167ffd72`](https://github.com/withastro/astro/commit/2167ffd72f58904f449ffc6e53581a2d8faf7317) Thanks [@matthewp](https://github.com/matthewp)! - Support adding integrations dynamically
Astro integrations can now themselves dynamically add and configure additional integrations during set-up. This makes it possible for integration authors to bundle integrations more intelligently for their users.
In the following example, a custom integration checks whether `@astrojs/sitemap` is already configured. If not, the integration adds Astros sitemap integration, passing any desired configuration options:
```ts
import sitemap from '@astrojs/sitemap';
import type { AstroIntegration } from 'astro';
const MyIntegration = (): AstroIntegration => {
return {
name: 'my-integration',
'astro:config:setup': ({ config, updateConfig }) => {
// Look for sitemap in user-configured integrations.
const userSitemap = config.integrations.find(
({ name }) => name === '@astrojs/sitemap'
);
if (!userSitemap) {
// If sitemap wasnt found, add it.
updateConfig({
integrations: [sitemap({ /* opts */ }],
});
}
},
};
};
```
- [#8696](https://github.com/withastro/astro/pull/8696) [`2167ffd72`](https://github.com/withastro/astro/commit/2167ffd72f58904f449ffc6e53581a2d8faf7317) Thanks [@matthewp](https://github.com/matthewp)! - View transitions can now be triggered from JavaScript!
Import the client-side router from "astro:transitions/client" and enjoy your new remote control for navigation:
```js
import { navigate } from 'astro:transitions/client';
// Navigate to the selected option automatically.
document.querySelector('select').onchange = (ev) => {
let href = ev.target.value;
navigate(href);
};
```
- [#8696](https://github.com/withastro/astro/pull/8696) [`2167ffd72`](https://github.com/withastro/astro/commit/2167ffd72f58904f449ffc6e53581a2d8faf7317) Thanks [@matthewp](https://github.com/matthewp)! - Route Announcer in `<ViewTransitions />`
The View Transitions router now does route announcement. When transitioning between pages with a traditional MPA approach, assistive technologies will announce the page title when the page finishes loading. This does not automatically happen during client-side routing, so visitors relying on these technologies to announce routes are not aware when a page has changed.
The view transitions route announcer runs after the `astro:page-load` event, looking for the page `<title>` to announce. If one cannot be found, the announcer falls back to the first `<h1>` it finds, or otherwise announces the pathname. We recommend you always include a `<title>` in each page for accessibility.
See the [View Transitions docs](https://docs.astro.build/en/guides/view-transitions/) for more on how accessibility is handled.
### Patch Changes
- [#8647](https://github.com/withastro/astro/pull/8647) [`408b50c5e`](https://github.com/withastro/astro/commit/408b50c5ea5aba66252424f54788557274a58571) Thanks [@lilnasy](https://github.com/lilnasy)! - Fixed an issue where configured redirects with dynamic routes did not work in dev mode.
- [#8696](https://github.com/withastro/astro/pull/8696) [`2167ffd72`](https://github.com/withastro/astro/commit/2167ffd72f58904f449ffc6e53581a2d8faf7317) Thanks [@matthewp](https://github.com/matthewp)! - Fix logLevel passed to Vite build
- [#8696](https://github.com/withastro/astro/pull/8696) [`2167ffd72`](https://github.com/withastro/astro/commit/2167ffd72f58904f449ffc6e53581a2d8faf7317) Thanks [@matthewp](https://github.com/matthewp)! - Fix NoImageMetadata image path error message
- [#8670](https://github.com/withastro/astro/pull/8670) [`e797b6816`](https://github.com/withastro/astro/commit/e797b6816072f63f38d9a91dd2a66765c558d46c) Thanks [@MichailiK](https://github.com/MichailiK)! - Fix asset optimization failing when outDir is outside the project directory
- [#8684](https://github.com/withastro/astro/pull/8684) [`824dd4670`](https://github.com/withastro/astro/commit/824dd4670a145c47337eff84a5ae412bf7443117) Thanks [@matthewp](https://github.com/matthewp)! - Support content collections with % in filename
- [#8648](https://github.com/withastro/astro/pull/8648) [`cfd895d87`](https://github.com/withastro/astro/commit/cfd895d877fdb7fc69e745665a374fc32cb3ef7d) Thanks [@lilnasy](https://github.com/lilnasy)! - Fixed an issue where a response with status code 404 led to an endless loop of implicit rerouting in dev mode.
## 3.1.4
### Patch Changes
- [#8646](https://github.com/withastro/astro/pull/8646) [`69fbf95b2`](https://github.com/withastro/astro/commit/69fbf95b22c0fb0d8e7e5fef9ec61e26cac9767f) Thanks [@matthewp](https://github.com/matthewp)! - Fix cases of head propagation not occuring in dev server
## 3.1.3
### Patch Changes
- [#8591](https://github.com/withastro/astro/pull/8591) [`863f5171e`](https://github.com/withastro/astro/commit/863f5171e8e7516c9d72f2e48ea7db1dea71c4f5) Thanks [@rishi-raj-jain](https://github.com/rishi-raj-jain)! - add site url to the location of redirect
- [#8633](https://github.com/withastro/astro/pull/8633) [`63141f3f3`](https://github.com/withastro/astro/commit/63141f3f3e4a57d2f55ccfebd7e506ea1033a1ab) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Fix build not working when having multiple images in the same Markdown file
- [#8636](https://github.com/withastro/astro/pull/8636) [`974d5117a`](https://github.com/withastro/astro/commit/974d5117abc8b47f8225e455b9285c88e305272f) Thanks [@martrapp](https://github.com/martrapp)! - fix: no deletion of scripts during view transition
- [#8645](https://github.com/withastro/astro/pull/8645) [`cb838b84b`](https://github.com/withastro/astro/commit/cb838b84b457041b0442996f7611b04aa940a620) Thanks [@matthewp](https://github.com/matthewp)! - Fix getDataEntryById to lookup by basename
- [#8640](https://github.com/withastro/astro/pull/8640) [`f36c4295b`](https://github.com/withastro/astro/commit/f36c4295be1ef2bcfa4aecb3c59551388419c53d) Thanks [@matthewp](https://github.com/matthewp)! - Warn on empty content collections
- [#8615](https://github.com/withastro/astro/pull/8615) [`4c4ad9d16`](https://github.com/withastro/astro/commit/4c4ad9d167e8d15ff2c15e3336ede8ca22f646b2) Thanks [@alexanderniebuhr](https://github.com/alexanderniebuhr)! - Improve the logging of assets for adapters that do not support image optimization
## 3.1.2
### Patch Changes

View file

@ -3,7 +3,6 @@
// ISOMORPHIC FILE: NO TOP-LEVEL IMPORT/REQUIRE() ALLOWED
// This file has to run as both ESM and CJS on older Node.js versions
// Needed for Stackblitz: https://github.com/stackblitz/webcontainer-core/issues/281
const CI_INSTRUCTIONS = {
NETLIFY: 'https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript',
@ -16,15 +15,11 @@ const CI_INSTRUCTIONS = {
const engines = '>=18.14.1';
const skipSemverCheckIfAbove = 19;
// HACK (2023-08-18) Stackblitz does not support Node 18 yet, so we'll fake Node 16 support for some time until it's supported
// TODO: Remove when Node 18 is supported on Stackblitz
const isStackblitz = process.env.SHELL === '/bin/jsh' && process.versions.webcontainer != null;
/** `astro *` */
async function main() {
const version = process.versions.node;
// Fast-path for higher Node.js versions
if (!isStackblitz && (parseInt(version) || 0) <= skipSemverCheckIfAbove) {
if ((parseInt(version) || 0) <= skipSemverCheckIfAbove) {
try {
const semver = await import('semver');
if (!semver.satisfies(version, engines)) {

View file

@ -114,6 +114,13 @@ declare module 'astro:transitions' {
export const ViewTransitions: ViewTransitionsModule['default'];
}
declare module 'astro:transitions/client' {
type TransitionRouterModule = typeof import('./dist/transitions/router.js');
export const supportsViewTransitions: TransitionRouterModule['supportsViewTransitions'];
export const transitionEnabledOnThisPage: TransitionRouterModule['transitionEnabledOnThisPage'];
export const navigate: TransitionRouterModule['navigate'];
}
declare module 'astro:middleware' {
export * from 'astro/middleware/namespace';
}

View file

@ -11,91 +11,12 @@ const { fallback = 'animate' } = Astro.props as Props;
<meta name="astro-view-transitions-enabled" content="true" />
<meta name="astro-view-transitions-fallback" content={fallback} />
<script>
type Fallback = 'none' | 'animate' | 'swap';
type Direction = 'forward' | 'back';
type State = {
index: number;
scrollX: number;
scrollY: number;
intraPage?: boolean;
};
type Events = 'astro:page-load' | 'astro:after-swap';
// only update history entries that are managed by us
// leave other entries alone and do not accidently add state.
const persistState = (state: State) => history.state && history.replaceState(state, '');
// @ts-expect-error: startViewTransition might exist
const supportsViewTransitions = !!document.startViewTransition;
const transitionEnabledOnThisPage = () =>
!!document.querySelector('[name="astro-view-transitions-enabled"]');
const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name));
const onPageLoad = () => triggerEvent('astro:page-load');
const PERSIST_ATTR = 'data-astro-transition-persist';
const parser = new DOMParser();
// explained at its usage
let noopEl: HTMLDivElement;
if (import.meta.env.DEV) {
noopEl = document.createElement('div');
}
// The History API does not tell you if navigation is forward or back, so
// you can figure it using an index. On pushState the index is incremented so you
// can use that to determine popstate if going forward or back.
let currentHistoryIndex = 0;
if (history.state) {
// we reloaded a page with history state
// (e.g. history navigation from non-transition page or browser reload)
currentHistoryIndex = history.state.index;
scrollTo({ left: history.state.scrollX, top: history.state.scrollY });
} else if (transitionEnabledOnThisPage()) {
history.replaceState({ index: currentHistoryIndex, scrollX, scrollY, intraPage: false }, '');
}
const throttle = (cb: (...args: any[]) => any, delay: number) => {
let wait = false;
// During the waiting time additional events are lost.
// So repeat the callback at the end if we have swallowed events.
let onceMore = false;
return (...args: any[]) => {
if (wait) {
onceMore = true;
return;
}
cb(...args);
wait = true;
setTimeout(() => {
if (onceMore) {
onceMore = false;
cb(...args);
}
wait = false;
}, delay);
};
};
// returns the contents of the page or null if the router can't deal with it.
async function fetchHTML(
href: string
): Promise<null | { html: string; redirected?: string; mediaType: DOMParserSupportedType }> {
try {
const res = await fetch(href);
// drop potential charset (+ other name/value pairs) as parser needs the mediaType
const mediaType = res.headers.get('content-type')?.replace(/;.*$/, '');
// the DOMParser can handle two types of HTML
if (mediaType !== 'text/html' && mediaType !== 'application/xhtml+xml') {
// everything else (e.g. audio/mp3) will be handled by the browser but not by us
return null;
}
const html = await res.text();
return {
html,
redirected: res.redirected ? res.url : undefined,
mediaType,
};
} catch (err) {
// can't fetch, let someone else deal with it.
return null;
}
}
import {
supportsViewTransitions,
transitionEnabledOnThisPage,
navigate,
} from 'astro:transitions/client';
export type Fallback = 'none' | 'animate' | 'swap';
function getFallback(): Fallback {
const el = document.querySelector('[name="astro-view-transitions-fallback"]');
@ -105,263 +26,6 @@ const { fallback = 'animate' } = Astro.props as Props;
return 'animate';
}
function markScriptsExec() {
for (const script of document.scripts) {
script.dataset.astroExec = '';
}
}
function runScripts() {
let wait = Promise.resolve();
for (const script of Array.from(document.scripts)) {
if (script.dataset.astroExec === '') continue;
const newScript = document.createElement('script');
newScript.innerHTML = script.innerHTML;
for (const attr of script.attributes) {
if (attr.name === 'src') {
const p = new Promise((r) => {
newScript.onload = r;
});
wait = wait.then(() => p as any);
}
newScript.setAttribute(attr.name, attr.value);
}
newScript.dataset.astroExec = '';
script.replaceWith(newScript);
}
return wait;
}
function isInfinite(animation: Animation) {
const effect = animation.effect;
if (!effect || !(effect instanceof KeyframeEffect) || !effect.target) return false;
const style = window.getComputedStyle(effect.target, effect.pseudoElement);
return style.animationIterationCount === 'infinite';
}
const updateHistoryAndScrollPosition = (toLocation) => {
if (toLocation.href !== location.href) {
history.pushState(
{ index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 },
'',
toLocation.href
);
// now we are on the new page for non-history navigations!
// (with history navigation page change happens before popstate is fired)
}
// freshly loaded pages start from the top
scrollTo({ left: 0, top: 0, behavior: 'instant' });
if (toLocation.hash) {
// because we are already on the target page ...
// ... what comes next is a intra-page navigation
// that won't reload the page but instead scroll to the fragment
location.href = toLocation.href;
}
};
// replace head and body of the windows document with contents from newDocument
// if !popstate, update the history entry and scroll position according to toLocation
// if popState is given, this holds the scroll position for history navigation
// if fallback === "animate" then simulate view transitions
async function updateDOM(
newDocument: Document,
toLocation: URL,
popState?: State,
fallback?: Fallback
) {
// Check for a head element that should persist and returns it,
// either because it has the data attribute or is a link el.
const persistedHeadElement = (el: HTMLElement): Element | null => {
const id = el.getAttribute(PERSIST_ATTR);
const newEl = id && newDocument.head.querySelector(`[${PERSIST_ATTR}="${id}"]`);
if (newEl) {
return newEl;
}
if (el.matches('link[rel=stylesheet]')) {
const href = el.getAttribute('href');
return newDocument.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
}
// What follows is a fix for an issue (#8472) with missing client:only styles after transition.
// That problem exists only in dev mode where styles are injected into the page by Vite.
// Returning a noop element ensures that the styles are not removed from the old document.
// Guarding the code below with the dev mode check
// allows tree shaking to remove this code in production.
if (import.meta.env.DEV) {
if (el.tagName === 'STYLE' && el.dataset.viteDevId) {
const devId = el.dataset.viteDevId;
// If this same style tag exists, remove it from the new page
return (
newDocument.querySelector(`style[data-astro-dev-id="${devId}"]`) ||
// Otherwise, keep it anyways. This is client:only styles.
noopEl
);
}
}
return null;
};
const swap = () => {
// swap attributes of the html element
// - delete all attributes from the current document
// - insert all attributes from doc
// - reinsert all original attributes that are named 'data-astro-*'
const html = document.documentElement;
const astro = [...html.attributes].filter(
({ name }) => (html.removeAttribute(name), name.startsWith('data-astro-'))
);
[...newDocument.documentElement.attributes, ...astro].forEach(({ name, value }) =>
html.setAttribute(name, value)
);
// Replace scripts in both the head and body.
for (const s1 of document.scripts) {
for (const s2 of newDocument.scripts) {
if (
// Inline
(s1.textContent && s1.textContent === s2.textContent) ||
// External
(s1.type === s2.type && s1.src === s2.src)
) {
s2.remove();
} else {
s1.remove();
}
}
}
// Swap head
for (const el of Array.from(document.head.children)) {
const newEl = persistedHeadElement(el as HTMLElement);
// If the element exists in the document already, remove it
// from the new document and leave the current node alone
if (newEl) {
newEl.remove();
} else {
// Otherwise remove the element in the head. It doesn't exist in the new page.
el.remove();
}
}
// Everything left in the new head is new, append it all.
document.head.append(...newDocument.head.children);
// Persist elements in the existing body
const oldBody = document.body;
// this will reset scroll Position
document.body.replaceWith(newDocument.body);
for (const el of oldBody.querySelectorAll(`[${PERSIST_ATTR}]`)) {
const id = el.getAttribute(PERSIST_ATTR);
const newEl = document.querySelector(`[${PERSIST_ATTR}="${id}"]`);
if (newEl) {
// The element exists in the new page, replace it with the element
// from the old page so that state is preserved.
newEl.replaceWith(el);
}
}
if (popState) {
scrollTo(popState.scrollX, popState.scrollY); // usings 'auto' scrollBehavior
} else {
updateHistoryAndScrollPosition(toLocation);
}
triggerEvent('astro:after-swap');
};
// Wait on links to finish, to prevent FOUC
const links: Promise<any>[] = [];
for (const el of newDocument.querySelectorAll('head link[rel=stylesheet]')) {
// Do not preload links that are already on the page.
if (
!document.querySelector(
`[${PERSIST_ATTR}="${el.getAttribute(PERSIST_ATTR)}"], link[rel=stylesheet]`
)
) {
const c = document.createElement('link');
c.setAttribute('rel', 'preload');
c.setAttribute('as', 'style');
c.setAttribute('href', el.getAttribute('href')!);
links.push(
new Promise<any>((resolve) => {
['load', 'error'].forEach((evName) => c.addEventListener(evName, resolve));
document.head.append(c);
})
);
}
}
links.length && (await Promise.all(links));
if (fallback === 'animate') {
// Trigger the animations
const currentAnimations = document.getAnimations();
document.documentElement.dataset.astroTransitionFallback = 'old';
const newAnimations = document
.getAnimations()
.filter((a) => !currentAnimations.includes(a) && !isInfinite(a));
const finished = Promise.all(newAnimations.map((a) => a.finished));
const fallbackSwap = () => {
swap();
document.documentElement.dataset.astroTransitionFallback = 'new';
};
await finished;
fallbackSwap();
} else {
swap();
}
}
async function transition(direction: Direction, toLocation: URL, popState?: State) {
let finished: Promise<void>;
const href = toLocation.href;
const response = await fetchHTML(href);
// If there is a problem fetching the new page, just do an MPA navigation to it.
if (response === null) {
location.href = href;
return;
}
// if there was a redirection, show the final URL in the browser's address bar
if (response.redirected) {
toLocation = new URL(response.redirected);
}
const newDocument = parser.parseFromString(response.html, response.mediaType);
// The next line might look like a hack,
// but it is actually necessary as noscript elements
// and their contents are returned as markup by the parser,
// see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString
newDocument.querySelectorAll('noscript').forEach((el) => el.remove());
if (!newDocument.querySelector('[name="astro-view-transitions-enabled"]')) {
location.href = href;
return;
}
if (!popState) {
// save the current scroll position before we change the DOM and transition to the new page
history.replaceState({ ...history.state, scrollX, scrollY }, '');
}
document.documentElement.dataset.astroTransition = direction;
if (supportsViewTransitions) {
// @ts-expect-error: startViewTransition exist
finished = document.startViewTransition(() =>
updateDOM(newDocument, toLocation, popState)
).finished;
} else {
finished = updateDOM(newDocument, toLocation, popState, getFallback());
}
try {
await finished;
} finally {
// skip this for the moment as it tends to stop fallback animations
// document.documentElement.removeAttribute('data-astro-transition');
await runScripts();
markScriptsExec();
onPageLoad();
}
}
// Prefetching
function maybePrefetch(pathname: string) {
if (document.querySelector(`link[rel=prefetch][href="${pathname}"]`)) return;
@ -406,83 +70,9 @@ const { fallback = 'animate' } = Astro.props as Props;
return;
}
ev.preventDefault();
navigate(link.href);
});
function navigate(href) {
// not ours
if (!transitionEnabledOnThisPage()) {
location.href = href;
return;
}
const toLocation = new URL(href, location.href);
// We do not have page transitions on navigations to the same page (intra-page navigation)
// but we want to handle prevent reload on navigation to the same page
// Same page means same origin, path and query params (but maybe different hash)
if (
location.origin === toLocation.origin &&
location.pathname === toLocation.pathname &&
location.search === toLocation.search
) {
// mark current position as non transition intra-page scrolling
if (location.href !== toLocation.href) {
history.replaceState({ ...history.state, intraPage: true }, '');
history.pushState(
{ index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 },
'',
toLocation.href
);
}
if (toLocation.hash) {
location.href = toLocation.href;
} else {
scrollTo({ left: 0, top: 0, behavior: 'instant' });
}
} else {
transition('forward', toLocation);
}
}
addEventListener('popstate', (ev) => {
if (!transitionEnabledOnThisPage() && ev.state) {
// The current page doesn't have View Transitions enabled
// but the page we navigate to does (because it set the state).
// Do a full page refresh to reload the client-side router from the new page.
// Scroll restauration will then happen during the reload when the router's code is re-executed
if (history.scrollRestoration) {
history.scrollRestoration = 'manual';
}
location.reload();
return;
}
// History entries without state are created by the browser (e.g. for hash links)
// Our view transition entries always have state.
// Just ignore stateless entries.
// The browser will handle navigation fine without our help
if (ev.state === null) {
if (history.scrollRestoration) {
history.scrollRestoration = 'auto';
}
return;
}
// With the default "auto", the browser will jump to the old scroll position
// before the ViewTransition is complete.
if (history.scrollRestoration) {
history.scrollRestoration = 'manual';
}
const state: State = history.state;
if (state.intraPage) {
// this is non transition intra-page scrolling
scrollTo(state.scrollX, state.scrollY);
} else {
const nextIndex = state.index;
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
currentHistoryIndex = nextIndex;
transition(direction, new URL(location.href), state);
}
navigate(link.href, {
history: link.dataset.astroHistory === 'replace' ? 'replace' : 'auto',
});
});
['mouseenter', 'touchstart', 'focus'].forEach((evName) => {
@ -503,17 +93,5 @@ const { fallback = 'animate' } = Astro.props as Props;
{ passive: true, capture: true }
);
});
addEventListener('load', onPageLoad);
// There's not a good way to record scroll position before a back button.
// So the way we do it is by listening to scrollend if supported, and if not continuously record the scroll position.
const updateState = () => {
persistState({ ...history.state, scrollX, scrollY });
};
if ('onscrollend' in window) addEventListener('scrollend', updateState);
else addEventListener('scroll', throttle(updateState, 300));
markScriptsExec();
}
</script>

View file

@ -70,7 +70,7 @@ export const getEntryBySlug = createGetEntryBySlug({
});
export const getDataEntryById = createGetDataEntryById({
dataCollectionToEntryMap,
getEntryImport: createGlobLookup(dataCollectionToEntryMap),
});
export const getEntry = createGetEntry({

View file

@ -1,36 +0,0 @@
import { expect } from '@playwright/test';
import { testFactory } from './test-utils.js';
const test = testFactory({
root: './fixtures/css/',
});
let devServer;
test.beforeAll(async ({ astro }) => {
devServer = await astro.startDevServer();
});
test.afterAll(async () => {
await devServer.stop();
});
test.describe('CSS Sourcemap HMR', () => {
test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => {
const html = await astro.fetch('/').then((res) => res.text());
// style[data-astro-dev-id] should exist in initial SSR'd markup
expect(html).toMatch('data-astro-dev-id');
await page.goto(astro.resolveUrl('/'));
// Ensure JS has initialized
await page.waitForTimeout(500);
// style[data-astro-dev-id] should NOT exist once JS runs
expect(await page.locator('style[data-astro-dev-id]').count()).toEqual(0);
// style[data-vite-dev-id] should exist now
expect(await page.locator('style[data-vite-dev-id]').count()).toBeGreaterThan(0);
});
});

View file

@ -29,21 +29,9 @@ test.describe('CSS HMR', () => {
await expect(h).toHaveCSS('color', 'rgb(0, 128, 0)');
});
test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => {
test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ astro }) => {
const html = await astro.fetch('/').then((res) => res.text());
// style[data-astro-dev-id] should exist in initial SSR'd markup
expect(html).toMatch('data-astro-dev-id');
await page.goto(astro.resolveUrl('/'));
// Ensure JS has initialized
await page.waitForTimeout(500);
// style[data-astro-dev-id] should NOT exist once JS runs
expect(await page.locator('style[data-astro-dev-id]').count()).toEqual(0);
// style[data-vite-dev-id] should exist now
expect(await page.locator('style[data-vite-dev-id]').count()).toBeGreaterThan(0);
// style[data-vite-dev-id] should exist in initial SSR'd markup
expect(html).toMatch('data-vite-dev-id');
});
});

View file

@ -1,7 +0,0 @@
export default {
vite: {
css: {
devSourcemap: true,
}
}
};

View file

@ -1 +0,0 @@
/// <reference types="astro/client" />

View file

@ -1,9 +0,0 @@
<h1>hello world</h1>
<style>
@import "../styles/main.css";
h1 {
color: var(--h1-color);
}
</style>

View file

@ -1,3 +0,0 @@
:root {
--h1-color: red;
}

View file

@ -0,0 +1,3 @@
#one {
background-color: whitesmoke;
}

View file

@ -0,0 +1,3 @@
h1 {
color: blue
}

View file

@ -18,6 +18,7 @@ const { link } = Astro.props as Props;
margin: auto;
}
</style>
<link rel="stylesheet" href="/style.css">
<ViewTransitions />
<DarkMode />
<meta name="script-executions" content="0">

View file

@ -1,7 +1,7 @@
---
import Layout from '../components/Layout.astro';
---
<Layout>
<Layout link="/one.css">
<p id="one">Page 1</p>
<a id="click-one" href="#test">test</a>
<a id="click-two" href="/two">go to 2</a>

View file

@ -0,0 +1,20 @@
---
import Layout from '../components/Layout.astro';
---
<Layout>
<p id="six">Page 6</p>
<a id="click-one" href="/one">test</a>
<div id="test">test content</div>
<script>
import { navigate } from "astro:transitions/client";
// this is to simulate client side use, will be triggered from test
window.addEventListener('jumpToTwo', ()=>navigate('/two'));
// this is a holder to pick up the router in additional tests
window.clientSideRouterForTestsParkedHere = navigate
// this is the direct use of the router in this page, redirecting to page one
navigate('/one');
</script>
</Layout>

View file

@ -4,6 +4,7 @@ import Layout from '../components/Layout.astro';
<Layout link="/two.css">
<p id="two">Page 2</p>
<article id="twoarticle"></article>
<a id="click-longpage" data-astro-history="replace" href="/long-page">go to long page</a>
</Layout>
<script>
document.addEventListener('astro:page-load', () => {

View file

@ -20,6 +20,20 @@ function scrollToBottom(page) {
});
}
function collectPreloads(page) {
return page.evaluate(() => {
window.preloads = [];
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) =>
mutation.addedNodes.forEach((node) => {
if (node.nodeName === 'LINK' && node.rel === 'preload') preloads.push(node.href);
})
);
});
observer.observe(document.head, { childList: true });
});
}
test.describe('View Transitions', () => {
test('Moving from page 1 to page 2', async ({ page, astro }) => {
const loads = [];
@ -170,11 +184,15 @@ test.describe('View Transitions', () => {
let p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
await collectPreloads(page);
// Go to page 2
await page.click('#click-two');
p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
await expect(p, 'imported CSS updated').toHaveCSS('font-size', '24px');
const preloads = await page.evaluate(() => window.preloads);
expect(preloads.length === 1 && preloads[0].endsWith('/two.css')).toBeTruthy();
});
test('astro:page-load event fires when navigating to new page', async ({ page, astro }) => {
@ -631,7 +649,7 @@ test.describe('View Transitions', () => {
});
test('client:only styles are retained on transition', async ({ page, astro }) => {
const totalExpectedStyles = 8;
const totalExpectedStyles = 7;
// Go to page 1
await page.goto(astro.resolveUrl('/client-only-one'));
@ -680,6 +698,39 @@ test.describe('View Transitions', () => {
await expect(locator).not.toBeInViewport();
});
test('Use the client side router', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/six'));
// page six loads the router and automatically uses the router to navigate to page 1
let p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
// nudge to jump to page 2
await page.evaluate(() => {
window.dispatchEvent(new Event('jumpToTwo'));
});
p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
// jump to page 3
await page.evaluate(() => {
// get the router from its fixture park position
const navigate = window.clientSideRouterForTestsParkedHere;
navigate('/three');
});
p = page.locator('#three');
await expect(p, 'should have content').toHaveText('Page 3');
// go back
await page.goBack();
p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
// no bad things happen when we revisit redirecting to page 6
await page.goto(astro.resolveUrl('/six'));
p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
});
test('body inline scripts do not re-execute on navigation', async ({ page, astro }) => {
const errors = [];
page.addListener('pageerror', (err) => {
@ -697,4 +748,52 @@ test.describe('View Transitions', () => {
expect(errors).toHaveLength(0);
});
test('replace history', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/one'));
// page six loads the router and automatically uses the router to navigate to page 1
let p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
// go to page 2
await page.click('#click-two');
p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
// replace with long page
await page.click('#click-longpage');
let article = page.locator('#longpage');
await expect(article, 'should have script content').toBeVisible('exists');
// one step back == #1
await page.goBack();
p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
});
test('CSR replace history', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/six'));
// page six loads the router and automatically uses the router to navigate to page 1
let p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
// goto #2
await page.evaluate(() => {
window.clientSideRouterForTestsParkedHere('/two', { history: 'auto' });
});
p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
// replace with long page
await page.evaluate(() => {
window.clientSideRouterForTestsParkedHere('/long-page', { history: 'replace' });
});
let article = page.locator('#longpage');
await expect(article, 'should have script content').toBeVisible('exists');
// one step back == #1
await page.goBack();
p = page.locator('#one');
await expect(p, 'should have content').toHaveText('Page 1');
});
});

View file

@ -1,6 +1,6 @@
{
"name": "astro",
"version": "3.1.2",
"version": "3.2.3",
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
"type": "module",
"author": "withastro",
@ -56,7 +56,7 @@
"./components/*": "./components/*",
"./assets": "./dist/assets/index.js",
"./assets/utils": "./dist/assets/utils/index.js",
"./assets/image-endpoint": "./dist/assets/image-endpoint.js",
"./assets/endpoint/*": "./dist/assets/endpoint/*.js",
"./assets/services/sharp": "./dist/assets/services/sharp.js",
"./assets/services/squoosh": "./dist/assets/services/squoosh.js",
"./assets/services/noop": "./dist/assets/services/noop.js",
@ -77,7 +77,8 @@
"types": "./dist/core/middleware/namespace.d.ts",
"default": "./dist/core/middleware/namespace.js"
},
"./transitions": "./dist/transitions/index.js"
"./transitions": "./dist/transitions/index.js",
"./transitions/router": "./dist/transitions/router.js"
},
"imports": {
"#astro/*": "./dist/*.js"
@ -167,7 +168,6 @@
"string-width": "^6.1.0",
"strip-ansi": "^7.1.0",
"tsconfig-resolver": "^3.0.1",
"undici": "^5.23.0",
"unist-util-visit": "^4.1.2",
"vfile": "^5.3.7",
"vite": "^4.4.9",
@ -226,5 +226,8 @@
"engines": {
"node": ">=18.14.1",
"npm": ">=6.14.0"
},
"publishConfig": {
"provenance": true
}
}

View file

@ -636,7 +636,7 @@ export interface AstroUserConfig {
* @see output
* @description
*
* Deploy to your favorite server, serverless, or edge host with build adapters. Import one of our first-party adapters for [Netlify](https://docs.astro.build/en/guides/deploy/netlify/#adapter-for-ssredge), [Vercel](https://docs.astro.build/en/guides/deploy/vercel/#adapter-for-ssr), and more to engage Astro SSR.
* Deploy to your favorite server, serverless, or edge host with build adapters. Import one of our first-party adapters for [Netlify](https://docs.astro.build/en/guides/deploy/netlify/#adapter-for-ssr), [Vercel](https://docs.astro.build/en/guides/deploy/vercel/#adapter-for-ssr), and more to engage Astro SSR.
*
* [See our Server-side Rendering guide](https://docs.astro.build/en/guides/server-side-rendering/) for more on SSR, and [our deployment guides](https://docs.astro.build/en/guides/deploy/) for a complete list of hosts.
*
@ -1168,10 +1168,10 @@ export interface AstroUserConfig {
* Pass [rehype plugins](https://github.com/remarkjs/remark-rehype) to customize how your Markdown's output HTML is processed. You can import and apply the plugin function (recommended), or pass the plugin name as a string.
*
* ```js
* import rehypeMinifyHtml from 'rehype-minify';
* import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis';
* {
* markdown: {
* rehypePlugins: [rehypeMinifyHtml]
* rehypePlugins: [rehypeAccessibleEmojis]
* }
* }
* ```
@ -2209,7 +2209,7 @@ export interface SSRMetadata {
hasRenderedHead: boolean;
headInTree: boolean;
extraHead: string[];
propagators: Map<AstroComponentFactory, AstroComponentInstance>;
propagators: Set<AstroComponentInstance>;
}
/* Preview server stuff */

View file

@ -1,6 +1,7 @@
import fs, { readFileSync } from 'node:fs';
import { basename, join } from 'node:path/posix';
import type { BuildPipeline } from '../../core/build/buildPipeline.js';
import { getOutDirWithinCwd } from '../../core/build/common.js';
import { prependForwardSlash } from '../../core/path.js';
import { isServerLikeOutput } from '../../prerender/utils.js';
import { getConfiguredImageService, isESMImportedImage } from '../internal.js';
@ -48,7 +49,7 @@ export async function generateImage(
serverRoot = config.build.server;
clientRoot = config.build.client;
} else {
serverRoot = config.outDir;
serverRoot = getOutDirWithinCwd(config.outDir);
clientRoot = config.outDir;
}

View file

@ -1,8 +1,8 @@
import { isRemotePath } from '@astrojs/internal-helpers/path';
import mime from 'mime/lite.js';
import type { APIRoute } from '../@types/astro.js';
import { getConfiguredImageService, isRemoteAllowed } from './internal.js';
import { etag } from './utils/etag.js';
import type { APIRoute } from '../../@types/astro.js';
import { getConfiguredImageService, isRemoteAllowed } from '../internal.js';
import { etag } from '../utils/etag.js';
// @ts-expect-error
import { imageConfig } from 'astro:assets';
@ -40,7 +40,6 @@ export const GET: APIRoute = async ({ request }) => {
let inputBuffer: Buffer | undefined = undefined;
// TODO: handle config subpaths?
const sourceUrl = isRemotePath(transform.src)
? new URL(transform.src)
: new URL(transform.src, url.origin);

Some files were not shown because too many files have changed in this diff Show more