([]);
- const [currentID, setCurrentID] = useState('overview');
- useEffect(() => {
- const getItemOffsets = () => {
- const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
- itemOffsets.current = Array.from(titles).map((title) => ({
- id: title.id,
- topOffset: title.getBoundingClientRect().top + window.scrollY,
- }));
- };
-
- getItemOffsets();
- window.addEventListener('resize', getItemOffsets);
-
- return () => {
- window.removeEventListener('resize', getItemOffsets);
- };
- }, []);
-
- useEffect(() => {
- if (!toc.current) return;
-
- const setCurrent: IntersectionObserverCallback = (entries) => {
- for (const entry of entries) {
- if (entry.isIntersecting) {
- const { id } = entry.target;
- if (id === onThisPageID) continue;
- setCurrentID(entry.target.id);
- break;
- }
- }
- };
-
- const observerOptions: IntersectionObserverInit = {
- // Negative top margin accounts for `scroll-margin`.
- // Negative bottom margin means heading needs to be towards top of viewport to trigger intersection.
- rootMargin: '-100px 0% -66%',
- threshold: 1,
- };
-
- const headingsObserver = new IntersectionObserver(setCurrent, observerOptions);
-
- // Observe all the headings in the main page content.
- document.querySelectorAll('article :is(h1,h2,h3)').forEach((h) => headingsObserver.observe(h));
-
- // Stop observing when the component is unmounted.
- return () => headingsObserver.disconnect();
- }, [toc.current]);
-
- const onLinkClick = (e) => {
- setCurrentID(e.target.getAttribute('href').replace('#', ''));
- };
-
- return (
- <>
-
- On this page
-
-
- >
- );
-};
-
-export default TableOfContents;
diff --git a/src/components/RightSidebar/ThemeToggleButton.css b/src/components/RightSidebar/ThemeToggleButton.css
deleted file mode 100644
index dc5ba46..0000000
--- a/src/components/RightSidebar/ThemeToggleButton.css
+++ /dev/null
@@ -1,37 +0,0 @@
-.theme-toggle {
- display: inline-flex;
- align-items: center;
- gap: 0.25em;
- padding: 0.33em 0.67em;
- border-radius: 99em;
- background-color: var(--theme-code-inline-bg);
-}
-
-.theme-toggle > label:focus-within {
- outline: 2px solid transparent;
- box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
-}
-
-.theme-toggle > label {
- color: var(--theme-code-inline-text);
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0.5;
-}
-
-.theme-toggle .checked {
- color: var(--theme-accent);
- opacity: 1;
-}
-
-input[name='theme-toggle'] {
- position: absolute;
- opacity: 0;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: -1;
-}
diff --git a/src/components/RightSidebar/ThemeToggleButton.tsx b/src/components/RightSidebar/ThemeToggleButton.tsx
deleted file mode 100644
index b9682aa..0000000
--- a/src/components/RightSidebar/ThemeToggleButton.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import type { FunctionalComponent } from 'preact';
-import { useState, useEffect } from 'preact/hooks';
-import './ThemeToggleButton.css';
-
-const themes = ['light', 'dark'];
-
-const icons = [
- ,
- ,
-];
-
-const ThemeToggle: FunctionalComponent = () => {
- const [theme, setTheme] = useState(() => {
- if (import.meta.env.SSR) {
- return undefined;
- }
- if (typeof localStorage !== undefined && localStorage.getItem('theme')) {
- return localStorage.getItem('theme');
- }
- if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
- return 'dark';
- }
- return 'light';
- });
-
- useEffect(() => {
- const root = document.documentElement;
- if (theme === 'light') {
- root.classList.remove('theme-dark');
- } else {
- root.classList.add('theme-dark');
- }
- }, [theme]);
-
- return (
-
- {themes.map((t, i) => {
- const icon = icons[i];
- const checked = t === theme;
- return (
-
- );
- })}
-
- );
-};
-
-export default ThemeToggle;
diff --git a/src/config.ts b/src/config.ts
deleted file mode 100644
index f085cea..0000000
--- a/src/config.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-export const SITE = {
- title: 'AM Docs',
- description: 'Documentation website of Astro-Metro',
- defaultLanguage: 'en_US',
- basePath: '/docs'
-};
-
-export const OPEN_GRAPH = {
- image: {
- src: 'https://github.com/astro-metro/docs/blob/main/public/banner.jpeg?raw=true',
- alt:
- 'astro logo on a starry expanse of space,' +
- ' with a purple saturn-like planet floating in the right foreground',
- },
- twitter: 'astrodotbuild',
-};
-
-// This is the type of the frontmatter you put in the docs markdown files.
-export type Frontmatter = {
- title: string;
- description: string;
- layout: string;
- image?: { src: string; alt: string };
- dir?: 'ltr' | 'rtl';
- ogLocale?: string;
- lang?: string;
-};
-
-export const KNOWN_LANGUAGES = {
- English: 'en',
-} as const;
-export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES);
-
-export const GITHUB_EDIT_URL = `https://github.com/astro-metro/docs/tree/main`;
-
-export const COMMUNITY_INVITE_URL = `https://github.com/astro-metro/docs/discussions`;
-
-// See "Algolia" section of the README for more information.
-export const ALGOLIA = {
- indexName: 'astro-metroio',
- appId: '93MWEFAEM9',
- apiKey: '7cf6e3455cd45792d6515b4890624339',
-};
-
-export type Sidebar = Record<
- typeof KNOWN_LANGUAGE_CODES[number],
- Record
->;
-export const SIDEBAR: Sidebar = {
- en: {
- 'About': [
- { text: 'Introduction', link: 'en/introduction' },
- ],
- 'Forms': [
- { text: 'Getting Started', link: 'en/forms/getting-started' },
- { text: 'Data binding', link: 'en/forms/data-binding' },
- { text: 'Components', link: 'en/forms/components' },
- { text: 'Session', link: 'en/forms/session' }
- ],
- 'Formidable': [
- { text: 'Getting Started', link: 'en/formidable' },
- ],
- 'Context': [
- { text: 'Getting Started', link: 'en/context' },
- ]
- },
-};
diff --git a/src/content/config.ts b/src/content/config.ts
new file mode 100644
index 0000000..9df91b6
--- /dev/null
+++ b/src/content/config.ts
@@ -0,0 +1,7 @@
+import { defineCollection } from 'astro:content';
+import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
+
+export const collections = {
+ docs: defineCollection({ schema: docsSchema() }),
+ i18n: defineCollection({ type: 'data', schema: i18nSchema() }),
+};
diff --git a/src/content/docs/guides/express-endpoints.mdx b/src/content/docs/guides/express-endpoints.mdx
new file mode 100644
index 0000000..7d6f605
--- /dev/null
+++ b/src/content/docs/guides/express-endpoints.mdx
@@ -0,0 +1,119 @@
+---
+title: Express Endpoints - Getting started
+description: How to use express endpoints
+---
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+# Introduction
+
+Express endpoints is a framework for astro.js that allows you to create express-like endpoints in astro.js.
+
+Some of the features:
+- β
Body, Query, Params, Headers parsers
+- β
Express Middleware
+- β
JWT session (if you use astro-utils/forms)
+- β
Body validator (via [`zod-express-middleware`](https://www.npmjs.com/package/expree))
+- β
Send JSON, files, and HTML easily
+
+## Installation
+
+
+
+
+ ```sh
+ npm install @astro-utils/express-endpoints
+ ```
+
+
+
+
+ ```sh
+ pnpm add @astro-utils/express-endpoints
+ ```
+
+
+
+
+ ```sh
+ yarn add @astro-utils/express-endpoints
+ ```
+
+
+
+
+
+## Usage
+
+```ts
+import { ExpressRoute } from "@astro-utils/express-endpoints";
+
+const router = new ExpressRoute();
+
+router.validate({
+ body: z.object({
+ name: z.string()
+ })
+});
+
+export const POST = router.route(async (req, res) => {
+ await new Promise(res => setTimeout(res, 1000));
+ res.json({
+ name: req.body.name,
+ url: 'This is a POST request'
+ });
+});
+```
+
+The validation options apply only to the next route.
+
+Meaning that you can use the same router for multiple methods.
+
+### Body options
+
+The default body-parser is `auto` meaning that it will parse the body no matter the type of it including `multipart/form-data`.
+
+You can configure the body parser by calling the `body` method.
+
+```ts
+const router = new ExpressRoute();
+
+router.body('multipart', {
+ maxFileSize: 10 * 1024 * 1024 // 10MB
+});
+
+export const POST = router.route((req, res) => {
+ const myFile = req.filesOne.myFile;
+
+ res.json({
+ name: myFile?.name || 'No file',
+ });
+});
+```
+
+The body parser options are the same as [`formidable`](https://www.npmjs.com/package/formidable#api).
+
+You can call the `body` method multiple times, only the last one will be used for the next route.
+
+```ts
+
+router.body('json');
+
+export const POST = router.route((req, res) => {
+ res.json({
+ name: req.body.name,
+ url: 'This is a POST request'
+ });
+});
+
+router.body('multipart', {
+ maxFileSize: 10 * 1024 * 1024 // 10MB
+});
+
+export const PUT = router.route((req, res) => {
+ const myFile = req.filesOne.myFile;
+
+ res.json({
+ name: myFile?.name || 'No file',
+ });
+});
+```
diff --git a/src/pages/en/forms/getting-started.md b/src/content/docs/guides/forms.mdx
similarity index 51%
rename from src/pages/en/forms/getting-started.md
rename to src/content/docs/guides/forms.mdx
index 2dccf9c..654a1f9 100644
--- a/src/pages/en/forms/getting-started.md
+++ b/src/content/docs/guides/forms.mdx
@@ -1,41 +1,75 @@
---
title: Forms - Getting started
-description: Getting started with astro-metro forms
-layout: ../../../layouts/MainLayout.astro
+description: How to use astro forms
---
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
+# Introduction
+
+Astro-Utils Forms is a set of tools to help you build forms in Astro.
+
+
+Some of the features:
+
+- β
Client & Server side form validation
+- β
Forms [CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF) protection
+- β
Data binding
+- β
File upload (support for multipart forms)
+- β
Web controls
+- β
JWT session
+
## Installation
First install the package
-```bash
-npm i @metro-astro/forms
+
+
+
+
+```sh
+npm install @astro-utils/forms
```
+
+
+
+```sh
+pnpm add @astro-utils/forms
+```
+
+
+
+
+```sh
+yarn add @astro-utils/forms
+```
+
+
+
+
+
## Adding integration & middleware
Edit your `astro.config.ts` to add the integration
```ts
import { defineConfig } from 'astro/config';
-import amDebug from "@astro-metro/forms/dist/integration.js";
+import formsDebug from "@astro-utils/forms/dist/integration.js";
export default defineConfig({
output: 'server',
- integrations: [amDebug],
- experimental: {
- middleware: true
- }
+ integrations: [formsDebug]
});
```
This integration will easy you debugging by avoiding the browser popups every vite reload.
-Edit
+Edit
`src/middleware.ts` to add the middleware
```ts
-import amMiddleware from "@astro-metro/forms";
+import astroFormsMiddleware from "@astro-utils/forms";
import {sequence} from "astro/middleware";
-export const onRequest = sequence(amMiddleware());
+export const onRequest = sequence(astroFormsMiddleware());
```
@@ -45,7 +79,7 @@ Add `WebForms` component to the main project layout
```astro
---
-import { WebForms } from "@astro-metro/forms/forms.js";
+import { WebForms } from "@astro-utils/forms/forms.js";
export interface Props {
title: string;
@@ -68,4 +102,4 @@ const { title } = Astro.props;