Refactor routing in App component to enhance navigation and improve error handling by integrating dynamic routes and updating the NotFound route.

This commit is contained in:
becarta
2025-05-23 12:43:00 +02:00
parent f40db0f5c9
commit a544759a3b
11127 changed files with 1647032 additions and 0 deletions

126
node_modules/astro/components/Code.astro generated vendored Normal file
View File

@@ -0,0 +1,126 @@
---
import type { ThemePresets } from '@astrojs/markdown-remark';
import type {
BuiltinLanguage,
LanguageRegistration,
ShikiTransformer,
SpecialLanguage,
ThemeRegistration,
ThemeRegistrationRaw,
} from 'shiki';
import { bundledLanguages } from 'shiki/langs';
import { getCachedHighlighter } from '../dist/core/shiki.js';
import type { HTMLAttributes } from '../types';
interface Props extends Omit<HTMLAttributes<'pre'>, 'lang'> {
/** The code to highlight. Required. */
code: string;
/**
* The language of your code.
* Supports all languages listed here: https://shiki.style/languages
* Instructions for loading a custom language: https://shiki.style/guide/load-lang
*
* @default "plaintext"
*/
lang?: BuiltinLanguage | SpecialLanguage | LanguageRegistration;
/**
* A metastring to pass to the highlighter.
* Allows passing information to transformers: https://shiki.style/guide/transformers#meta
*
* @default undefined
*/
meta?: string;
/**
* The styling theme.
* Supports all themes listed here: https://shiki.style/themes
* Instructions for loading a custom theme: https://shiki.style/guide/load-theme
*
* @default "github-dark"
*/
theme?: ThemePresets | ThemeRegistration | ThemeRegistrationRaw;
/**
* Multiple themes to style with -- alternative to "theme" option.
* Supports all themes found above; see https://shiki.style/guide/dual-themes for more information.
*/
themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw>;
/**
* Chooses a theme from the "themes" option that you've defined as the default styling theme.
* - <string>: one of the keys defined in the "themes" option. Will throw an error if the key is not defined.
* - false: disabled. You'll have to apply the styling theme yourself. No effect if the "themes" option is not set.
*
* See https://shiki.style/guide/dual-themes#without-default-color for more information.
*
* @default "light"
*/
defaultColor?: 'light' | 'dark' | string | false;
/**
* Enable word wrapping.
* - true: enabled.
* - false: disabled.
* - null: All overflow styling removed. Code will overflow the element by default.
*
* @default false
*/
wrap?: boolean | null;
/**
* Generate inline code element only, without the pre element wrapper.
*
* @default false
*/
inline?: boolean;
/**
* Shiki transformers to customize the generated HTML by manipulating the hast tree.
* Supports all transformers listed here: https://shiki.style/packages/transformers#transformers
* Instructions for custom transformers: https://shiki.style/guide/transformers
*/
transformers?: ShikiTransformer[];
}
const {
code,
lang = 'plaintext',
meta,
theme = 'github-dark',
themes = {},
defaultColor = 'light',
wrap = false,
inline = false,
transformers = [],
...rest
} = Astro.props;
// shiki 1.0 compat
if (typeof lang === 'object') {
// `id` renamed to `name` (always override)
if ((lang as any).id) {
lang.name = (lang as any).id;
}
// `grammar` flattened to lang itself
if ((lang as any).grammar) {
Object.assign(lang, (lang as any).grammar);
}
}
const highlighter = await getCachedHighlighter({
langs: [
typeof lang === 'string'
? Object.keys(bundledLanguages).includes(lang)
? lang
: 'plaintext'
: (lang as any),
],
theme,
themes,
defaultColor,
wrap,
transformers,
});
const html = await highlighter.highlight(code, typeof lang === 'string' ? lang : lang.name, {
inline,
meta,
attributes: rest as any,
});
---
<Fragment set:html={html} />

61
node_modules/astro/components/Debug.astro generated vendored Normal file
View File

@@ -0,0 +1,61 @@
---
import Code from './Code.astro';
const key = Object.keys(Astro.props)[0];
const value = Astro.props[key];
---
<div class="astro-debug">
<div class="astro-debug-header">
<h2 class="astro-debug-title">
<span class="astro-debug-label">Debug</span>
<span class="astro-debug-name">"{key}"</span>
</h2>
</div>
<Code code={JSON.stringify(value, null, 2)} />
</div>
<style is:inline>
.astro-debug {
font-size: 14px;
padding: 1rem 1.5rem;
background: white;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
'Open Sans', 'Helvetica Neue', sans-serif;
}
.astro-debug-header,
pre.astro-code {
margin: -1rem -1.5rem 1rem;
padding: 0.25rem 0.75rem;
}
.astro-debug-header {
background: #ff1639;
border-radius: 4px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.astro-debug-title {
font-size: 1em;
color: white;
margin: 0.5em 0;
}
.astro-debug-label {
font-weight: bold;
text-transform: uppercase;
margin-right: 0.75em;
}
pre.astro-code {
border: 1px solid #eee;
padding: 1rem 0.75rem;
border-radius: 4px;
border-top-left-radius: 0;
border-top-right-radius: 0;
font-size: 14px;
}
</style>

38
node_modules/astro/components/Image.astro generated vendored Normal file
View File

@@ -0,0 +1,38 @@
---
import { type LocalImageProps, type RemoteImageProps, getImage } from 'astro:assets';
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
import type { HTMLAttributes } from '../types';
// The TypeScript diagnostic for JSX props uses the last member of the union to suggest props, so it would be better for
// LocalImageProps to be last. Unfortunately, when we do this the error messages that remote images get are complete nonsense
// Not 100% sure how to fix this, seems to be a TypeScript issue. Unfortunate.
type Props = LocalImageProps | RemoteImageProps;
const props = Astro.props;
if (props.alt === undefined || props.alt === null) {
throw new AstroError(AstroErrorData.ImageMissingAlt);
}
// As a convenience, allow width and height to be string with a number in them, to match HTML's native `img`.
if (typeof props.width === 'string') {
props.width = parseInt(props.width);
}
if (typeof props.height === 'string') {
props.height = parseInt(props.height);
}
const image = await getImage(props);
const additionalAttributes: HTMLAttributes<'img'> = {};
if (image.srcSet.values.length > 0) {
additionalAttributes.srcset = image.srcSet.attribute;
}
if (import.meta.env.DEV) {
additionalAttributes['data-image-component'] = 'true';
}
---
<img src={image.src} {...additionalAttributes} {...image.attributes} />

110
node_modules/astro/components/Picture.astro generated vendored Normal file
View File

@@ -0,0 +1,110 @@
---
import { type LocalImageProps, type RemoteImageProps, getImage } from 'astro:assets';
import * as mime from 'mrmime';
import type { GetImageResult, ImageOutputFormat } from '../dist/@types/astro';
import { isESMImportedImage, resolveSrc } from '../dist/assets/utils/imageKind';
import { AstroError, AstroErrorData } from '../dist/core/errors/index.js';
import type { HTMLAttributes } from '../types';
type Props = (LocalImageProps | RemoteImageProps) & {
formats?: ImageOutputFormat[];
fallbackFormat?: ImageOutputFormat;
pictureAttributes?: HTMLAttributes<'picture'>;
};
const defaultFormats = ['webp'] as const;
const defaultFallbackFormat = 'png' as const;
// Certain formats don't want PNG fallbacks:
// - GIF will typically want to stay as a gif, either for animation or for the lower amount of colors
// - SVGs can't be converted to raster formats in most cases
// - JPEGs compress photographs and high-noise images better than PNG in most cases
// For those, we'll use the original format as the fallback instead.
const specialFormatsFallback = ['gif', 'svg', 'jpg', 'jpeg'] as const;
const { formats = defaultFormats, pictureAttributes = {}, fallbackFormat, ...props } = Astro.props;
if (props.alt === undefined || props.alt === null) {
throw new AstroError(AstroErrorData.ImageMissingAlt);
}
// Picture attribute inherit scoped styles from class and attributes
const scopedStyleClass = props.class?.match(/\bastro-\w{8}\b/)?.[0];
if (scopedStyleClass) {
if (pictureAttributes.class) {
pictureAttributes.class = `${pictureAttributes.class} ${scopedStyleClass}`;
} else {
pictureAttributes.class = scopedStyleClass;
}
}
for (const key in props) {
if (key.startsWith('data-astro-cid')) {
pictureAttributes[key] = props[key];
}
}
const originalSrc = await resolveSrc(props.src);
const optimizedImages: GetImageResult[] = await Promise.all(
formats.map(
async (format) =>
await getImage({
...props,
src: originalSrc,
format: format,
widths: props.widths,
densities: props.densities,
}),
),
);
let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
if (
!fallbackFormat &&
isESMImportedImage(originalSrc) &&
(specialFormatsFallback as ReadonlyArray<string>).includes(originalSrc.format)
) {
resultFallbackFormat = originalSrc.format;
}
const fallbackImage = await getImage({
...props,
format: resultFallbackFormat,
widths: props.widths,
densities: props.densities,
});
const imgAdditionalAttributes: HTMLAttributes<'img'> = {};
const sourceAdditionalAttributes: HTMLAttributes<'source'> = {};
// Propagate the `sizes` attribute to the `source` elements
if (props.sizes) {
sourceAdditionalAttributes.sizes = props.sizes;
}
if (fallbackImage.srcSet.values.length > 0) {
imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
}
if (import.meta.env.DEV) {
imgAdditionalAttributes['data-image-component'] = 'true';
}
---
<picture {...pictureAttributes}>
{
Object.entries(optimizedImages).map(([_, image]) => {
const srcsetAttribute =
props.densities || (!props.densities && !props.widths)
? `${image.src}${image.srcSet.values.length > 0 ? ', ' + image.srcSet.attribute : ''}`
: image.srcSet.attribute;
return (
<source
srcset={srcsetAttribute}
type={mime.lookup(image.options.format ?? image.src) ?? `image/${image.options.format}`}
{...sourceAdditionalAttributes}
/>
);
})
}
<img src={fallbackImage.src} {...imgAdditionalAttributes} {...fallbackImage.attributes} />
</picture>

149
node_modules/astro/components/ViewTransitions.astro generated vendored Normal file
View File

@@ -0,0 +1,149 @@
---
type Fallback = 'none' | 'animate' | 'swap';
export interface Props {
fallback?: Fallback;
/** @deprecated handleForms is enabled by default in Astro 4.0
*
* Set `data-astro-reload` on your form to opt-out of the default behavior.
*/
handleForms?: boolean;
}
const { fallback = 'animate' } = Astro.props;
---
<style is:global>
/* Route announcer */
.astro-route-announcer {
position: absolute;
left: 0;
top: 0;
clip: rect(0 0 0 0);
clip-path: inset(50%);
overflow: hidden;
white-space: nowrap;
width: 1px;
height: 1px;
}
</style>
<meta name="astro-view-transitions-enabled" content="true" />
<meta name="astro-view-transitions-fallback" content={fallback} />
<script>
import type { Options } from 'astro:transitions/client';
import { supportsViewTransitions, navigate } from 'astro:transitions/client';
// NOTE: import from `astro/virtual-modules/prefetch.js` as `astro:prefetch` requires the `prefetch` config to be enabled
// @ts-ignore
import { init } from 'astro/virtual-modules/prefetch.js';
type Fallback = 'none' | 'animate' | 'swap';
function getFallback(): Fallback {
const el = document.querySelector('[name="astro-view-transitions-fallback"]');
if (el) {
return el.getAttribute('content') as Fallback;
}
return 'animate';
}
function isReloadEl(el: HTMLElement | SVGAElement): boolean {
return el.dataset.astroReload !== undefined;
}
if (supportsViewTransitions || getFallback() !== 'none') {
if (import.meta.env.DEV && window.matchMedia('(prefers-reduced-motion)').matches) {
console.warn(
`[transitions]: all view transition animations, including fallback animation, are disabled as this device has the prefer-reduced-motion setting enabled.`,
);
}
document.addEventListener('click', (ev) => {
let link = ev.target;
if (ev.composed) {
link = ev.composedPath()[0];
}
if (link instanceof Element) {
link = link.closest('a, area');
}
if (
!(link instanceof HTMLAnchorElement) &&
!(link instanceof SVGAElement) &&
!(link instanceof HTMLAreaElement)
)
return;
// This check verifies that the click is happening on an anchor
// that is going to another page within the same origin. Basically it determines
// same-origin navigation, but omits special key combos for new tabs, etc.
const linkTarget = link instanceof HTMLElement ? link.target : link.target.baseVal;
const href = link instanceof HTMLElement ? link.href : link.href.baseVal;
const origin = new URL(href, location.href).origin;
if (
isReloadEl(link) ||
link.hasAttribute('download') ||
!link.href ||
(linkTarget && linkTarget !== '_self') ||
origin !== location.origin ||
ev.button !== 0 || // left clicks only
ev.metaKey || // new tab (mac)
ev.ctrlKey || // new tab (windows)
ev.altKey || // download
ev.shiftKey || // new window
ev.defaultPrevented
) {
// No page transitions in these cases,
// Let the browser standard action handle this
return;
}
ev.preventDefault();
navigate(href, {
history: link.dataset.astroHistory === 'replace' ? 'replace' : 'auto',
sourceElement: link,
});
});
document.addEventListener('submit', (ev) => {
let el = ev.target as HTMLElement;
if (el.tagName !== 'FORM' || ev.defaultPrevented || isReloadEl(el)) {
return;
}
const form = el as HTMLFormElement;
const submitter = ev.submitter;
const formData = new FormData(form, submitter);
// form.action and form.method can point to an <input name="action"> or <input name="method">
// in which case should fallback to the form attribute
const formAction =
typeof form.action === 'string' ? form.action : form.getAttribute('action');
const formMethod =
typeof form.method === 'string' ? form.method : form.getAttribute('method');
// Use the form action, if defined, otherwise fallback to current path.
let action = submitter?.getAttribute('formaction') ?? formAction ?? location.pathname;
// Use the form method, if defined, otherwise fallback to "get"
const method = submitter?.getAttribute('formmethod') ?? formMethod ?? 'get';
// the "dialog" method is a special keyword used within <dialog> elements
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fs-method
if (method === 'dialog' || location.origin !== new URL(action, location.href).origin) {
// No page transitions in these cases,
// Let the browser standard action handle this
return;
}
const options: Options = { sourceElement: submitter ?? form };
if (method === 'get') {
const params = new URLSearchParams(formData as any);
const url = new URL(action);
url.search = params.toString();
action = url.toString();
} else {
options.formData = formData;
}
ev.preventDefault();
navigate(action, options);
});
// @ts-expect-error injected by vite-plugin-transitions for treeshaking
if (!__PREFETCH_DISABLED__) {
init({ prefetchAll: true });
}
}
</script>

6
node_modules/astro/components/index.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
// The `ts-ignore` comments here are necessary because we're importing this file inside the `astro:components`
// virtual module's types, which means that `tsc` will try to resolve these imports. Don't mind the editor errors.
// @ts-ignore
export { default as Code } from './Code.astro';
// @ts-ignore
export { default as Debug } from './Debug.astro';

66
node_modules/astro/components/viewtransitions.css generated vendored Normal file
View File

@@ -0,0 +1,66 @@
@keyframes astroFadeInOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes astroFadeIn {
from {
opacity: 0;
mix-blend-mode: plus-lighter;
}
to {
opacity: 1;
mix-blend-mode: plus-lighter;
}
}
@keyframes astroFadeOut {
from {
opacity: 1;
mix-blend-mode: plus-lighter;
}
to {
opacity: 0;
mix-blend-mode: plus-lighter;
}
}
@keyframes astroSlideFromRight {
from {
transform: translateX(100%);
}
}
@keyframes astroSlideFromLeft {
from {
transform: translateX(-100%);
}
}
@keyframes astroSlideToRight {
to {
transform: translateX(100%);
}
}
@keyframes astroSlideToLeft {
to {
transform: translateX(-100%);
}
}
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
[data-astro-transition-scope] {
animation: none !important;
}
}