diff --git a/.changeset/angry-dancers-juggle.md b/.changeset/angry-dancers-juggle.md
new file mode 100644
index 0000000000..f8d3ce2efd
--- /dev/null
+++ b/.changeset/angry-dancers-juggle.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+fix search overlay styles on mobile
diff --git a/.changeset/brave-snails-remember.md b/.changeset/brave-snails-remember.md
new file mode 100644
index 0000000000..7243a3ff8c
--- /dev/null
+++ b/.changeset/brave-snails-remember.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+split css to `hamburger`/`scrollbar`/`typesetting-article` css files
diff --git a/.changeset/chatty-beans-bake.md b/.changeset/chatty-beans-bake.md
new file mode 100644
index 0000000000..0d3c236f46
--- /dev/null
+++ b/.changeset/chatty-beans-bake.md
@@ -0,0 +1,6 @@
+---
+'nextra': patch
+'nextra-theme-docs': patch
+---
+
+fix edit on github button for cases when filename named as `index`
diff --git a/.changeset/cuddly-insects-scream.md b/.changeset/cuddly-insects-scream.md
new file mode 100644
index 0000000000..87b171e752
--- /dev/null
+++ b/.changeset/cuddly-insects-scream.md
@@ -0,0 +1,5 @@
+---
+'nextra': patch
+---
+
+fix `ReferenceError` when trying to access `__nextra_pageOpts__` inside MDX file
diff --git a/.changeset/curvy-tomatoes-worry.md b/.changeset/curvy-tomatoes-worry.md
new file mode 100644
index 0000000000..b5a121839c
--- /dev/null
+++ b/.changeset/curvy-tomatoes-worry.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+BREAKING! various theme config options was renamed, take a look of renamed options [here](https://github.com/shuding/nextra/blob/core/packages/nextra-theme-docs/src/constants.tsx)
diff --git a/.changeset/happy-crabs-hope.md b/.changeset/happy-crabs-hope.md
new file mode 100644
index 0000000000..d04a6d3d64
--- /dev/null
+++ b/.changeset/happy-crabs-hope.md
@@ -0,0 +1,7 @@
+---
+'nextra': patch
+'nextra-theme-blog': patch
+'nextra-theme-docs': patch
+---
+
+[Blog/Docs] Add copy to clipboard button for code-blocks. Add `NextraConfig.unstable_defaultShowCopyCode` option to show button by default, add `copy` and `copy=false` options for code-blocks
diff --git a/.changeset/olive-brooms-behave.md b/.changeset/olive-brooms-behave.md
new file mode 100644
index 0000000000..bea572c04f
--- /dev/null
+++ b/.changeset/olive-brooms-behave.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+hide search input in navbar on mobile
diff --git a/.changeset/orange-bugs-sell.md b/.changeset/orange-bugs-sell.md
new file mode 100644
index 0000000000..1556cf25ea
--- /dev/null
+++ b/.changeset/orange-bugs-sell.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+fix empty space in navbar when theme option `search.component: null`
diff --git a/.changeset/pink-chicken-exist.md b/.changeset/pink-chicken-exist.md
new file mode 100644
index 0000000000..19b33d3c38
--- /dev/null
+++ b/.changeset/pink-chicken-exist.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+add `editLink.component`
diff --git a/.changeset/poor-rivers-develop.md b/.changeset/poor-rivers-develop.md
new file mode 100644
index 0000000000..a737abfa8c
--- /dev/null
+++ b/.changeset/poor-rivers-develop.md
@@ -0,0 +1,6 @@
+---
+'nextra': patch
+'nextra-theme-docs': patch
+---
+
+rename `meta.json` to `_meta.json`
diff --git a/.changeset/proud-phones-dress.md b/.changeset/proud-phones-dress.md
new file mode 100644
index 0000000000..9e006d7696
--- /dev/null
+++ b/.changeset/proud-phones-dress.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+clicking on folder should navigate to first children if `index` page doesn't exist
diff --git a/.changeset/silly-apricots-help.md b/.changeset/silly-apricots-help.md
new file mode 100644
index 0000000000..e0d721ca6d
--- /dev/null
+++ b/.changeset/silly-apricots-help.md
@@ -0,0 +1,5 @@
+---
+'nextra': patch
+---
+
+better loader types, add `MetaJsonFile`, `MdxFile`, `Folder` types
diff --git a/.changeset/swift-drinks-relax.md b/.changeset/swift-drinks-relax.md
new file mode 100644
index 0000000000..f5daed83b6
--- /dev/null
+++ b/.changeset/swift-drinks-relax.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+fix callout shrinking from children content
diff --git a/.changeset/tall-cows-whisper.md b/.changeset/tall-cows-whisper.md
new file mode 100644
index 0000000000..57f09397a5
--- /dev/null
+++ b/.changeset/tall-cows-whisper.md
@@ -0,0 +1,7 @@
+---
+'nextra': patch
+'nextra-theme-blog': patch
+'nextra-theme-docs': patch
+---
+
+rename `PageOpts.meta` to `PageOpts.frontMatter`
diff --git a/.changeset/tough-turtles-scream.md b/.changeset/tough-turtles-scream.md
new file mode 100644
index 0000000000..55b22c51b9
--- /dev/null
+++ b/.changeset/tough-turtles-scream.md
@@ -0,0 +1,7 @@
+---
+'nextra': patch
+'nextra-theme-blog': patch
+'nextra-theme-docs': patch
+---
+
+move `withLayout` logic directly in nextra loader
diff --git a/.changeset/violet-pianos-know.md b/.changeset/violet-pianos-know.md
new file mode 100644
index 0000000000..65ae05dd01
--- /dev/null
+++ b/.changeset/violet-pianos-know.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+adjust active breadcrumb color
diff --git a/.changeset/young-cows-serve.md b/.changeset/young-cows-serve.md
new file mode 100644
index 0000000000..9a74de38b1
--- /dev/null
+++ b/.changeset/young-cows-serve.md
@@ -0,0 +1,5 @@
+---
+'nextra-theme-docs': patch
+---
+
+fix search input `ESC` icon vertical alignment
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 88c5bfd549..8767d37314 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -10,6 +10,7 @@ module.exports = {
{
// TODO: enable for `nextra-theme-blog` also
files: 'packages/nextra-theme-docs/**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}',
+ plugins: ['typescript-sort-keys'],
rules: {
'no-restricted-imports': [
'error',
diff --git a/examples/blog/next.config.mjs b/examples/blog/next.config.mjs
index 6c1f3b59a9..aecd923d57 100644
--- a/examples/blog/next.config.mjs
+++ b/examples/blog/next.config.mjs
@@ -3,7 +3,8 @@ import nextra from 'nextra'
const withNextra = nextra({
theme: 'nextra-theme-blog',
themeConfig: './theme.config.jsx',
- unstable_staticImage: true
+ unstable_staticImage: true,
+ unstable_defaultShowCopyCode: true
})
export default withNextra({
diff --git a/examples/blog/theme.config.jsx b/examples/blog/theme.config.jsx
index 311170472c..54ca3e84d7 100644
--- a/examples/blog/theme.config.jsx
+++ b/examples/blog/theme.config.jsx
@@ -1,4 +1,6 @@
+/* eslint sort-keys: error */
export default {
+ darkMode: true,
footer: (
),
- darkMode: true
}
diff --git a/examples/docs/src/pages/meta.json b/examples/docs/src/pages/_meta.json
similarity index 100%
rename from examples/docs/src/pages/meta.json
rename to examples/docs/src/pages/_meta.json
diff --git a/examples/docs/src/pages/advanced/meta.json b/examples/docs/src/pages/advanced/_meta.json
similarity index 100%
rename from examples/docs/src/pages/advanced/meta.json
rename to examples/docs/src/pages/advanced/_meta.json
diff --git a/examples/docs/src/pages/features/meta.json b/examples/docs/src/pages/features/_meta.json
similarity index 100%
rename from examples/docs/src/pages/features/meta.json
rename to examples/docs/src/pages/features/_meta.json
diff --git a/examples/docs/src/pages/themes/meta.json b/examples/docs/src/pages/themes/_meta.json
similarity index 100%
rename from examples/docs/src/pages/themes/meta.json
rename to examples/docs/src/pages/themes/_meta.json
diff --git a/examples/docs/src/pages/themes/blog/meta.json b/examples/docs/src/pages/themes/blog/_meta.json
similarity index 100%
rename from examples/docs/src/pages/themes/blog/meta.json
rename to examples/docs/src/pages/themes/blog/_meta.json
diff --git a/examples/docs/src/pages/themes/docs/meta.json b/examples/docs/src/pages/themes/docs/_meta.json
similarity index 100%
rename from examples/docs/src/pages/themes/docs/meta.json
rename to examples/docs/src/pages/themes/docs/_meta.json
diff --git a/examples/docs/src/pages/themes/docs/bleed.mdx b/examples/docs/src/pages/themes/docs/bleed.mdx
index 0f1e3abe29..dd79beae93 100644
--- a/examples/docs/src/pages/themes/docs/bleed.mdx
+++ b/examples/docs/src/pages/themes/docs/bleed.mdx
@@ -27,10 +27,9 @@ For example you can put text, image, video or any component inside:
diff --git a/examples/docs/src/theme.config.js b/examples/docs/src/theme.config.js
index a2f04560bd..bfe712c17c 100644
--- a/examples/docs/src/theme.config.js
+++ b/examples/docs/src/theme.config.js
@@ -1,19 +1,16 @@
+/* eslint sort-keys: error */
/**
* @type {import('nextra-theme-docs').DocsThemeConfig}
*/
export default {
- projectLink: 'https://github.com/shuding/nextra',
- docsRepositoryBase: 'https://github.com/shuding/nextra/blob/master',
- projectChatLink: 'https://discord.gg/hEM84NMkRv', // Next.js discord server,
- titleSuffix: ' – Nextra',
- logo: (
- <>
- Nextra
-
- The Next.js Static Site Generator
-
- >
- ),
+ banner: {
+ key: 'Nextra 2',
+ text: 'Nextra 2 Alpha',
+ },
+ docsRepositoryBase: 'https://github.com/shuding/nextra/blob/core/examples/docs',
+ editLink: {
+ text: 'Edit this page on GitHub'
+ },
head: () => (
<>
@@ -61,14 +58,8 @@ export default {
>
),
- search: true,
+ projectChat: {
+ link: 'https://discord.gg/hEM84NMkRv', // Next.js discord server,
+ },
unstable_faviconGlyph: '✦',
- prevLinks: true,
- nextLinks: true,
- footer: true,
- footerEditLink: 'Edit this page on GitHub',
- footerText: `MIT ${new Date().getFullYear()} © Nextra.`,
- darkMode: true,
- bannerKey: 'Nextra 2',
- banner: 'Nextra 2 Alpha'
}
diff --git a/examples/swr-site/next.config.js b/examples/swr-site/next.config.js
index 70cb70aee7..e08be49a31 100644
--- a/examples/swr-site/next.config.js
+++ b/examples/swr-site/next.config.js
@@ -5,6 +5,7 @@ const withNextra = require("nextra")({
codeblocks: true,
},
unstable_staticImage: true,
+ unstable_defaultShowCopyCode: true
});
module.exports = withNextra({
diff --git a/examples/swr-site/pages/meta.en-US.json b/examples/swr-site/pages/_meta.en-US.json
similarity index 100%
rename from examples/swr-site/pages/meta.en-US.json
rename to examples/swr-site/pages/_meta.en-US.json
diff --git a/examples/swr-site/pages/meta.es-ES.json b/examples/swr-site/pages/_meta.es-ES.json
similarity index 100%
rename from examples/swr-site/pages/meta.es-ES.json
rename to examples/swr-site/pages/_meta.es-ES.json
diff --git a/examples/swr-site/pages/meta.ja.json b/examples/swr-site/pages/_meta.ja.json
similarity index 100%
rename from examples/swr-site/pages/meta.ja.json
rename to examples/swr-site/pages/_meta.ja.json
diff --git a/examples/swr-site/pages/meta.ko.json b/examples/swr-site/pages/_meta.ko.json
similarity index 100%
rename from examples/swr-site/pages/meta.ko.json
rename to examples/swr-site/pages/_meta.ko.json
diff --git a/examples/swr-site/pages/meta.ru.json b/examples/swr-site/pages/_meta.ru.json
similarity index 100%
rename from examples/swr-site/pages/meta.ru.json
rename to examples/swr-site/pages/_meta.ru.json
diff --git a/examples/swr-site/pages/meta.zh-CN.json b/examples/swr-site/pages/_meta.zh-CN.json
similarity index 100%
rename from examples/swr-site/pages/meta.zh-CN.json
rename to examples/swr-site/pages/_meta.zh-CN.json
diff --git a/examples/swr-site/pages/about/meta.en-US.json b/examples/swr-site/pages/about/_meta.en-US.json
similarity index 100%
rename from examples/swr-site/pages/about/meta.en-US.json
rename to examples/swr-site/pages/about/_meta.en-US.json
diff --git a/examples/swr-site/pages/blog/meta.en-US.json b/examples/swr-site/pages/blog/_meta.en-US.json
similarity index 100%
rename from examples/swr-site/pages/blog/meta.en-US.json
rename to examples/swr-site/pages/blog/_meta.en-US.json
diff --git a/examples/swr-site/pages/blog/meta.es-ES.json b/examples/swr-site/pages/blog/_meta.es-ES.json
similarity index 100%
rename from examples/swr-site/pages/blog/meta.es-ES.json
rename to examples/swr-site/pages/blog/_meta.es-ES.json
diff --git a/examples/swr-site/pages/blog/meta.ja.json b/examples/swr-site/pages/blog/_meta.ja.json
similarity index 100%
rename from examples/swr-site/pages/blog/meta.ja.json
rename to examples/swr-site/pages/blog/_meta.ja.json
diff --git a/examples/swr-site/pages/blog/meta.ko.json b/examples/swr-site/pages/blog/_meta.ko.json
similarity index 100%
rename from examples/swr-site/pages/blog/meta.ko.json
rename to examples/swr-site/pages/blog/_meta.ko.json
diff --git a/examples/swr-site/pages/blog/meta.ru.json b/examples/swr-site/pages/blog/_meta.ru.json
similarity index 100%
rename from examples/swr-site/pages/blog/meta.ru.json
rename to examples/swr-site/pages/blog/_meta.ru.json
diff --git a/examples/swr-site/pages/blog/meta.zh-CN.json b/examples/swr-site/pages/blog/_meta.zh-CN.json
similarity index 100%
rename from examples/swr-site/pages/blog/meta.zh-CN.json
rename to examples/swr-site/pages/blog/_meta.zh-CN.json
diff --git a/examples/swr-site/pages/docs/meta.en-US.json b/examples/swr-site/pages/docs/_meta.en-US.json
similarity index 100%
rename from examples/swr-site/pages/docs/meta.en-US.json
rename to examples/swr-site/pages/docs/_meta.en-US.json
diff --git a/examples/swr-site/pages/docs/meta.es-ES.json b/examples/swr-site/pages/docs/_meta.es-ES.json
similarity index 100%
rename from examples/swr-site/pages/docs/meta.es-ES.json
rename to examples/swr-site/pages/docs/_meta.es-ES.json
diff --git a/examples/swr-site/pages/docs/meta.ja.json b/examples/swr-site/pages/docs/_meta.ja.json
similarity index 100%
rename from examples/swr-site/pages/docs/meta.ja.json
rename to examples/swr-site/pages/docs/_meta.ja.json
diff --git a/examples/swr-site/pages/docs/meta.ko.json b/examples/swr-site/pages/docs/_meta.ko.json
similarity index 100%
rename from examples/swr-site/pages/docs/meta.ko.json
rename to examples/swr-site/pages/docs/_meta.ko.json
diff --git a/examples/swr-site/pages/docs/meta.ru.json b/examples/swr-site/pages/docs/_meta.ru.json
similarity index 100%
rename from examples/swr-site/pages/docs/meta.ru.json
rename to examples/swr-site/pages/docs/_meta.ru.json
diff --git a/examples/swr-site/pages/docs/meta.zh-CN.json b/examples/swr-site/pages/docs/_meta.zh-CN.json
similarity index 100%
rename from examples/swr-site/pages/docs/meta.zh-CN.json
rename to examples/swr-site/pages/docs/_meta.zh-CN.json
diff --git a/examples/swr-site/pages/docs/advanced/meta.en-US.json b/examples/swr-site/pages/docs/advanced/_meta.en-US.json
similarity index 100%
rename from examples/swr-site/pages/docs/advanced/meta.en-US.json
rename to examples/swr-site/pages/docs/advanced/_meta.en-US.json
diff --git a/examples/swr-site/pages/docs/advanced/meta.es-ES.json b/examples/swr-site/pages/docs/advanced/_meta.es-ES.json
similarity index 100%
rename from examples/swr-site/pages/docs/advanced/meta.es-ES.json
rename to examples/swr-site/pages/docs/advanced/_meta.es-ES.json
diff --git a/examples/swr-site/pages/docs/advanced/meta.ja.json b/examples/swr-site/pages/docs/advanced/_meta.ja.json
similarity index 100%
rename from examples/swr-site/pages/docs/advanced/meta.ja.json
rename to examples/swr-site/pages/docs/advanced/_meta.ja.json
diff --git a/examples/swr-site/pages/docs/advanced/meta.ko.json b/examples/swr-site/pages/docs/advanced/_meta.ko.json
similarity index 100%
rename from examples/swr-site/pages/docs/advanced/meta.ko.json
rename to examples/swr-site/pages/docs/advanced/_meta.ko.json
diff --git a/examples/swr-site/pages/docs/advanced/meta.ru.json b/examples/swr-site/pages/docs/advanced/_meta.ru.json
similarity index 100%
rename from examples/swr-site/pages/docs/advanced/meta.ru.json
rename to examples/swr-site/pages/docs/advanced/_meta.ru.json
diff --git a/examples/swr-site/pages/docs/advanced/meta.zh-CN.json b/examples/swr-site/pages/docs/advanced/_meta.zh-CN.json
similarity index 100%
rename from examples/swr-site/pages/docs/advanced/meta.zh-CN.json
rename to examples/swr-site/pages/docs/advanced/_meta.zh-CN.json
diff --git a/examples/swr-site/pages/examples/meta.en-US.json b/examples/swr-site/pages/examples/_meta.en-US.json
similarity index 100%
rename from examples/swr-site/pages/examples/meta.en-US.json
rename to examples/swr-site/pages/examples/_meta.en-US.json
diff --git a/examples/swr-site/pages/examples/meta.es-ES.json b/examples/swr-site/pages/examples/_meta.es-ES.json
similarity index 100%
rename from examples/swr-site/pages/examples/meta.es-ES.json
rename to examples/swr-site/pages/examples/_meta.es-ES.json
diff --git a/examples/swr-site/pages/examples/meta.ja.json b/examples/swr-site/pages/examples/_meta.ja.json
similarity index 100%
rename from examples/swr-site/pages/examples/meta.ja.json
rename to examples/swr-site/pages/examples/_meta.ja.json
diff --git a/examples/swr-site/pages/examples/meta.ko.json b/examples/swr-site/pages/examples/_meta.ko.json
similarity index 100%
rename from examples/swr-site/pages/examples/meta.ko.json
rename to examples/swr-site/pages/examples/_meta.ko.json
diff --git a/examples/swr-site/pages/examples/meta.ru.json b/examples/swr-site/pages/examples/_meta.ru.json
similarity index 100%
rename from examples/swr-site/pages/examples/meta.ru.json
rename to examples/swr-site/pages/examples/_meta.ru.json
diff --git a/examples/swr-site/pages/examples/meta.zh-CN.json b/examples/swr-site/pages/examples/_meta.zh-CN.json
similarity index 100%
rename from examples/swr-site/pages/examples/meta.zh-CN.json
rename to examples/swr-site/pages/examples/_meta.zh-CN.json
diff --git a/examples/swr-site/theme.config.tsx b/examples/swr-site/theme.config.tsx
index e876d5ed25..9ea2a05d36 100644
--- a/examples/swr-site/theme.config.tsx
+++ b/examples/swr-site/theme.config.tsx
@@ -1,5 +1,5 @@
+/* eslint sort-keys: error */
import { useRouter } from "next/router";
-import NextHead from "next/head";
import { DocsThemeConfig, useConfig } from "nextra-theme-docs";
const Logo = ({ height }) => (
@@ -20,63 +20,123 @@ const Vercel = () => (
);
-const TITLE_WITH_TRANSLATIONS = {
+const TITLE = {
"en-US": "React Hooks for Data Fetching",
- "zh-CN": "用于数据请求的 React Hooks 库",
"es-ES": "Biblioteca React Hooks para la obtención de datos",
ja: "データ取得のための React Hooks ライブラリ",
ko: "데이터 가져오기를 위한 React Hooks",
ru: "React хуки для выборки данных",
+ "zh-CN": "用于数据请求的 React Hooks 库",
};
-const EDIT_LINK_WITH_TRANSLATIONS = {
- "zh-CN": "在 GitHub 上编辑本页",
+const EDIT_TEXT = {
+ "en-US": "Edit this page on GitHub →",
"es-ES": "Edite esta página en GitHub",
ja: "Github で編集する",
ko: "Github에서 이 페이지 편집하기",
ru: "Редактировать на GitHub",
+ "zh-CN": "在 GitHub 上编辑本页",
+};
+
+const FOOTER_LINK = {
+ "en-US": "https://vercel.com/?utm_source=swr",
+ "es-ES": "https://vercel.com/?utm_source=swr_es-es",
+ ja: "https://vercel.com/?utm_source=swr_ja",
+ ko: "https://vercel.com/?utm_source=swr_ko",
+ ru: "https://vercel.com/?utm_source=swr_ru",
+ "zh-CN": "https://vercel.com/?utm_source=swr_zh-cn",
+};
+
+const FOOTER_LINK_TEXT = {
+ "en-US": (
+ <>
+ Powered by
+
+ >
+ ),
+ "es-ES": (
+ <>
+ Desarrollado por
+
+ >
+ ),
+ ja: (
+ <>
+ 提供
+
+ >
+ ),
+ ko: (
+ <>
+ Powered by
+
+ >
+ ),
+ ru: (
+ <>
+ Работает на
+
+ >
+ ),
+ "zh-CN": (
+ <>
+ 由
+
+ 驱动
+ >
+ ),
};
const config: DocsThemeConfig = {
- github: "https://github.com/vercel/swr",
- docsRepositoryBase: "https://github.com/vercel/swr-site/blob/master/pages",
- titleSuffix() {
- const { locale } = useRouter();
- return ` – SWR (${locale})`;
+ banner: {
+ key: "swr-2",
+ text: "SWR 2.0 is out! Read more →",
+ },
+ bodyExtraContent() {
+ const router = useRouter();
+ return (
+ router.route.startsWith("/docs") && (
+ <>💪 content from `config.bodyExtraContent`>
+ )
+ );
},
- search: true,
- floatTOC: true,
darkMode: true,
- defaultMenuCollapsed: true,
- nextThemes: {
- defaultTheme: "dark",
+ docsRepositoryBase:
+ "https://github.com/shuding/nextra/blob/core/examples/swr-site",
+ editLink: {
+ text() {
+ const { locale } = useRouter();
+ return EDIT_TEXT[locale];
+ },
},
- feedbackLink: "Question? Give us feedback →",
- feedbackLabels: "feedback",
- bannerKey: "swr-2",
- banner: "SWR 2.0 is out! Read more →",
- tocExtraContent: ,
- logo() {
- const { locale } = useRouter();
- return (
- <>
-
-
- SWR
-
- >
- );
+ {FOOTER_LINK_TEXT[locale]}
+
+ );
+ },
},
+ gitTimestamp: "Last updated on",
+ github: "https://github.com/vercel/swr",
head() {
const config = useConfig();
const description =
- config.meta.description ||
+ config.frontMatter.description ||
"SWR is a React Hooks library for data fetching. SWR first returns the data from cache (stale), then sends the fetch request (revalidate), and finally comes with the up-to-date data again.";
const image =
- config.meta.image ||
+ config.frontMatter.image ||
"https://assets.vercel.com/image/upload/v1572282926/swr/twitter-card.jpg";
return (
<>
@@ -117,96 +177,48 @@ const config: DocsThemeConfig = {
>
);
},
- sidebarSubtitle: ({ title }) => (
-
-
- {title}
-
- ),
- footerEditLink() {
- const { locale } = useRouter();
- return EDIT_LINK_WITH_TRANSLATIONS[locale] || "Edit this page on GitHub →";
- },
- footerText() {
- const { locale } = useRouter();
-
- const linkProps = {
- target: "_blank",
- rel: "noopener",
- className: "inline-flex items-center font-semibold gap-2",
- href:
- {
- "zh-CN": "https://vercel.com/?utm_source=swr_zh-cn",
- "es-ES": "https://vercel.com/?utm_source=swr_es-es",
- ja: "https://vercel.com/?utm_source=swr_ja",
- ko: "https://vercel.com/?utm_source=swr_ko",
- ru: "https://vercel.com/?utm_source=swr_ru",
- }[locale] || "https://vercel.com/?utm_source=swr",
- };
-
- switch (locale) {
- case "zh-CN":
- return (
-
- 由
-
- 驱动
-
- );
- case "es-ES":
- return (
-
- Desarrollado por
-
-
- );
- case "ja":
- return (
-
- 提供
-
-
- );
- case "ko":
- return (
-
- Powered by
-
-
- );
- case "ru":
- return (
-
- Работает на
-
-
- );
- default:
- return (
-
- Powered by
-
-
- );
- }
- },
i18n: [
{ locale: "en-US", text: "English" },
- { locale: "es-ES", text: "Español RTL", direction: "rtl" },
+ { direction: "rtl", locale: "es-ES", text: "Español RTL" },
{ locale: "zh-CN", text: "简体中文" },
{ locale: "ja", text: "日本語" },
{ locale: "ko", text: "한국어" },
{ locale: "ru", text: "Русский" },
],
- gitTimestamp: "Last updated on",
- bodyExtraContent() {
- const router = useRouter();
+ logo() {
+ const { locale } = useRouter();
return (
- router.route.startsWith("/docs") && (
- <>💪 content from `config.bodyExtraContent`>
- )
+ <>
+
+
+ SWR
+
+ >
);
},
+ nextThemes: {
+ defaultTheme: "dark",
+ },
+ sidebar: {
+ defaultMenuCollapsed: true,
+ subtitle: ({ title }) => (
+
+
+ {title}
+
+ ),
+ },
+ titleSuffix() {
+ const { locale } = useRouter();
+ return ` – SWR (${locale})`;
+ },
+ toc: {
+ extraContent: ,
+ float: true,
+ },
};
export default config;
diff --git a/package.json b/package.json
index da7088c0dd..eae39b5038 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"@edge-runtime/vm": "1.1.0-beta.23",
"@typescript-eslint/parser": "^5.32.0",
"eslint": "^8.21.0",
+ "eslint-plugin-typescript-sort-keys": "^2.1.0",
"prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.13",
"rimraf": "^3.0.2",
diff --git a/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts b/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts
index c644f67be9..1fae0758e8 100644
--- a/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts
+++ b/packages/nextra-theme-blog/__test__/__fixture__/pageMap.ts
@@ -1,8 +1,8 @@
import { BlogPageOpts } from '../../src/types'
export const indexOpts: BlogPageOpts = {
- filename: 'index.mdx',
+ filePath: 'index.mdx',
route: '/',
- meta: {
+ frontMatter: {
type: 'page',
title: 'About',
date: '2020-01-01T00:00:00.000Z'
@@ -103,9 +103,9 @@ export const indexOpts: BlogPageOpts = {
}
export const postsOpts: BlogPageOpts = {
- filename: 'index.md',
+ filePath: 'index.md',
route: '/posts',
- meta: {
+ frontMatter: {
type: 'posts',
title: 'Random Thoughts',
date: '2020-01-03T00:00:00.000Z'
@@ -206,9 +206,9 @@ export const postsOpts: BlogPageOpts = {
}
export const articleOpts: BlogPageOpts = {
- filename: 'aaron-swartz-a-programmable-web.mdx',
+ filePath: 'aaron-swartz-a-programmable-web.mdx',
route: '/posts/aaron-swartz-a-programmable-web',
- meta: {
+ frontMatter: {
title: 'Notes on A Programmable Web by Aaron Swartz',
date: '2016/5/21',
description:
diff --git a/packages/nextra-theme-blog/src/article-layout.tsx b/packages/nextra-theme-blog/src/article-layout.tsx
index 881bf9de7a..66c267c4c1 100644
--- a/packages/nextra-theme-blog/src/article-layout.tsx
+++ b/packages/nextra-theme-blog/src/article-layout.tsx
@@ -10,7 +10,7 @@ export const ArticleLayout = ({ children }: { children: ReactNode }) => {
const { back } = getParent({ opts, config })
return (
-
+
{children}
{config.postFooter}
diff --git a/packages/nextra-theme-blog/src/basic-layout.tsx b/packages/nextra-theme-blog/src/basic-layout.tsx
index 317ce6710b..35ce18e32e 100644
--- a/packages/nextra-theme-blog/src/basic-layout.tsx
+++ b/packages/nextra-theme-blog/src/basic-layout.tsx
@@ -11,7 +11,7 @@ export const BasicLayout = ({ children }: { children: ReactNode }) => {
{title}
- {config.head?.({ title, meta: opts.meta })}
+ {config.head?.({ title, meta: opts.frontMatter })}
{opts.hasJsxInH1 ? : null}
diff --git a/packages/nextra-theme-blog/src/blog-context.tsx b/packages/nextra-theme-blog/src/blog-context.tsx
index d9a0c876f1..2623ff77fe 100644
--- a/packages/nextra-theme-blog/src/blog-context.tsx
+++ b/packages/nextra-theme-blog/src/blog-context.tsx
@@ -14,7 +14,7 @@ export const BlogProvider = ({
children,
opts
}: LayoutProps & { children: ReactNode }): ReactElement => {
- const { date } = opts.meta
+ const { date } = opts.frontMatter
if (date && !isValidDate(date)) {
throw new Error(
diff --git a/packages/nextra-theme-blog/src/constants.tsx b/packages/nextra-theme-blog/src/constants.tsx
index 97f771665b..e93951dd50 100644
--- a/packages/nextra-theme-blog/src/constants.tsx
+++ b/packages/nextra-theme-blog/src/constants.tsx
@@ -1,11 +1,12 @@
+/* eslint sort-keys: error */
import React from 'react'
import { NextraBlogTheme } from './types'
export const DEFAULT_THEME: NextraBlogTheme = {
- readMore: 'Read More →',
footer: (
CC BY-NC 4.0 {new Date().getFullYear()} © Shu Ding.
- )
+ ),
+ readMore: 'Read More →',
}
diff --git a/packages/nextra-theme-blog/src/env.d.ts b/packages/nextra-theme-blog/src/env.d.ts
new file mode 100644
index 0000000000..bdd77c14e8
--- /dev/null
+++ b/packages/nextra-theme-blog/src/env.d.ts
@@ -0,0 +1,13 @@
+declare module globalThis {
+ import { NextraBlogTheme } from './types'
+ import { FC } from 'react'
+ import { PageOpts } from 'nextra'
+
+ var __nextra_pageContext__: {
+ [route: string]: {
+ Content: FC
+ pageOpts: PageOpts
+ themeConfig: NextraBlogTheme
+ }
+ }
+}
diff --git a/packages/nextra-theme-blog/src/index.tsx b/packages/nextra-theme-blog/src/index.tsx
index 2edca5662d..0eb4e4eacb 100644
--- a/packages/nextra-theme-blog/src/index.tsx
+++ b/packages/nextra-theme-blog/src/index.tsx
@@ -21,7 +21,7 @@ const BlogLayout = ({
children,
opts
}: LayoutProps & { children: ReactNode }): ReactElement => {
- const type = opts.meta.type || 'post'
+ const type = opts.frontMatter.type || 'post'
const Layout = layoutMap[type]
if (!Layout) {
throw new Error(
@@ -35,17 +35,9 @@ const BlogLayout = ({
)
}
-const nextraPageContext: {
- [key: string]: {
- Content: FC
- pageOpts: PageOpts
- themeConfig: NextraBlogTheme
- }
-} = {}
-
-function Layout(props: any) {
+export default function Layout(props: any) {
const { route } = useRouter()
- const context = nextraPageContext[route]
+ const context = globalThis.__nextra_pageContext__[route]
if (!context) throw new Error(`No content found for ${route}.`)
const extendedConfig = { ...DEFAULT_THEME, ...context.themeConfig }
@@ -59,24 +51,6 @@ function Layout(props: any) {
)
}
-// Make sure the same component is always returned so Next.js will render the
-// stable layout. We then put the actual content into a global store and use
-// the route to identify it.
-export default function withLayout(
- route: string,
- Content: FC,
- pageOpts: PageOpts,
- themeConfig: NextraBlogTheme
-) {
- nextraPageContext[route] = {
- Content,
- pageOpts,
- themeConfig
- }
-
- return Layout
-}
-
export { useTheme } from 'next-themes'
export { useBlogContext } from './blog-context'
export * from './types'
diff --git a/packages/nextra-theme-blog/src/mdx-theme.tsx b/packages/nextra-theme-blog/src/mdx-theme.tsx
index a3652b1f64..62c83f3d06 100644
--- a/packages/nextra-theme-blog/src/mdx-theme.tsx
+++ b/packages/nextra-theme-blog/src/mdx-theme.tsx
@@ -10,6 +10,7 @@ import React, {
import { MDXProvider } from '@mdx-js/react'
import Link from 'next/link'
import ReactDOM from 'react-dom'
+import { Code, Pre } from 'nextra/components'
import { useBlogContext } from './blog-context'
export const HeadingContext = createContext<
@@ -71,13 +72,6 @@ const A = ({
)
}
-const Pre = ({ children }: { children?: ReactNode }): ReactElement => {
- return (
-
- )
-}
const components = {
h1: H1,
h2: createHeaderLink('h2'),
@@ -86,7 +80,14 @@ const components = {
h5: createHeaderLink('h5'),
h6: createHeaderLink('h6'),
a: A,
- pre: Pre
+ pre({ children, ...props }: ComponentProps<'pre'>) {
+ return (
+
+ )
+ },
+ code: Code
}
const MDXTheme = ({ children }: { children: ReactNode }): ReactElement => {
diff --git a/packages/nextra-theme-blog/src/posts-layout.tsx b/packages/nextra-theme-blog/src/posts-layout.tsx
index c869c996b6..486a42821f 100644
--- a/packages/nextra-theme-blog/src/posts-layout.tsx
+++ b/packages/nextra-theme-blog/src/posts-layout.tsx
@@ -12,9 +12,7 @@ export const PostsLayout = ({ children }: { children: ReactNode }) => {
const { config, opts } = useBlogContext()
const { posts } = collectPostsAndNavs({ config, opts })
const router = useRouter()
- const {
- meta: { type }
- } = opts
+ const { type } = opts.frontMatter
const tagName = type === 'tag' ? router.query.tag : null
const postList = posts.map(post => {
if (tagName) {
diff --git a/packages/nextra-theme-blog/src/types.ts b/packages/nextra-theme-blog/src/types.ts
index ba1d580b1f..f8b45da5f0 100644
--- a/packages/nextra-theme-blog/src/types.ts
+++ b/packages/nextra-theme-blog/src/types.ts
@@ -1,43 +1,44 @@
+/* eslint typescript-sort-keys/interface: error */
import { PageOpts } from 'nextra'
-import React from 'react'
+import { ReactNode } from 'react'
export interface NextraBlogTheme {
- readMore?: string
- footer?: React.ReactNode
- titleSuffix?: string
- postFooter?: string
- head?: ({
- title,
- meta
- }: {
- title: string
- meta: Record
- }) => React.ReactNode
+ comments?: ReactNode
cusdis?: {
appId: string
host?: string
lang: string
}
darkMode?: boolean
+ footer?: ReactNode
+ head?: ({
+ meta,
+ title,
+ }: {
+ meta: Record
+ title: string
+ }) => ReactNode
navs?: {
- url: string
name: string
+ url: string
}[]
- comments?: React.ReactNode
+ postFooter?: string
+ readMore?: string
+ titleSuffix?: string
}
export interface BlogPageOpts extends PageOpts {
- meta: Meta
+ frontMatter: Meta
}
type Meta = {
- title?: string
- type?: 'post' | 'page' | 'posts' | 'tag'
- tag?: string | string[]
+ author?: string
back?: string
date?: string
description?: string
- author?: string
+ tag?: string | string[]
+ title?: string
+ type?: 'post' | 'page' | 'posts' | 'tag'
}
export interface LayoutProps {
diff --git a/packages/nextra-theme-blog/src/utils/collect.ts b/packages/nextra-theme-blog/src/utils/collect.ts
index b6b79d0c19..125920cf98 100644
--- a/packages/nextra-theme-blog/src/utils/collect.ts
+++ b/packages/nextra-theme-blog/src/utils/collect.ts
@@ -1,20 +1,22 @@
-import { PageMapItem } from 'nextra'
+import { PageMapItem, MdxFile } from 'nextra'
import { LayoutProps } from '../types'
import { sortDate } from './date'
import traverse from './traverse'
-const isNav = (page: PageMapItem) => {
- return page.frontMatter && ['page', 'posts'].includes(page.frontMatter.type)
+const isNav = (page: PageMapItem): page is MdxFile => {
+ const type = 'frontMatter' in page && page.frontMatter?.type
+ return type && ['page', 'posts'].includes(type)
}
-const isPost = (page: PageMapItem) => {
- if (page.children) return false
+const isPost = (page: PageMapItem): page is MdxFile => {
+ if ('children' in page && page.children) return false
if (page.name.startsWith('_')) return false
- return !page.frontMatter?.type || page.frontMatter.type === 'post'
+ const type = 'frontMatter' in page && page.frontMatter?.type
+ return !type || type === 'post'
}
export const collectPostsAndNavs = ({ opts }: LayoutProps) => {
- const posts: PageMapItem[] = []
- const navPages: PageMapItem[] = []
+ const posts: MdxFile[] = []
+ const navPages: (MdxFile & { active: boolean })[] = []
const { route } = opts
traverse(opts.pageMap, page => {
if (isNav(page)) {
diff --git a/packages/nextra-theme-blog/src/utils/date.ts b/packages/nextra-theme-blog/src/utils/date.ts
index 674fa4f260..2f14ef7a1a 100644
--- a/packages/nextra-theme-blog/src/utils/date.ts
+++ b/packages/nextra-theme-blog/src/utils/date.ts
@@ -1,6 +1,6 @@
-import { PageMapItem } from 'nextra'
+import { MdxFile } from 'nextra'
-export const sortDate = (a: PageMapItem, b: PageMapItem): number => {
+export const sortDate = (a: MdxFile, b: MdxFile): number => {
if (!a.frontMatter?.date || !b.frontMatter?.date) return -1
return (
diff --git a/packages/nextra-theme-blog/src/utils/get-tags.ts b/packages/nextra-theme-blog/src/utils/get-tags.ts
index db6906994f..f305d47764 100644
--- a/packages/nextra-theme-blog/src/utils/get-tags.ts
+++ b/packages/nextra-theme-blog/src/utils/get-tags.ts
@@ -1,10 +1,10 @@
-import { PageMapItem } from 'nextra'
+import { MdxFile } from 'nextra'
export function split(tags: string | string[]): string[] {
return (Array.isArray(tags) ? tags : tags.split(',')).map(s => s.trim())
}
-export default function getTags(page: PageMapItem) {
+export default function getTags(page: MdxFile) {
if (!page.frontMatter) {
return []
}
diff --git a/packages/nextra-theme-blog/src/utils/parent.ts b/packages/nextra-theme-blog/src/utils/parent.ts
index 25475cfa51..dc3987090a 100644
--- a/packages/nextra-theme-blog/src/utils/parent.ts
+++ b/packages/nextra-theme-blog/src/utils/parent.ts
@@ -1,25 +1,30 @@
-import { PageMapItem } from 'nextra'
+import { MdxFile } from 'nextra'
import { LayoutProps } from '../types'
import traverse from './traverse'
export const getParent = ({ opts }: LayoutProps) => {
let back: string | null = null
- const parentPages: PageMapItem[] = []
- const route = opts.route
+ const parentPages: MdxFile[] = []
+ const { route } = opts
+
traverse(opts.pageMap, page => {
if (
+ 'route' in page &&
route !== page.route &&
(route + '/').startsWith(page.route === '/' ? '/' : page.route + '/')
) {
parentPages.push(page)
}
})
+
const parentPage = parentPages
.reverse()
.find(page => page.frontMatter && page.frontMatter.type === 'posts')
+
if (parentPage) {
back = parentPage.route
}
+
return {
parentPage,
back
diff --git a/packages/nextra-theme-blog/src/utils/traverse.ts b/packages/nextra-theme-blog/src/utils/traverse.ts
index 577442ae96..c90f8d2bab 100644
--- a/packages/nextra-theme-blog/src/utils/traverse.ts
+++ b/packages/nextra-theme-blog/src/utils/traverse.ts
@@ -11,10 +11,9 @@ export default function traverse(
}
}
- for (const pageMapItem of pageMap) {
- const { children } = pageMapItem
- if (children) {
- const matched = traverse(children, matcher)
+ for (const item of pageMap) {
+ if ('children' in item && item.children) {
+ const matched = traverse(item.children, matcher)
if (matched) {
return matched
}
diff --git a/packages/nextra-theme-blog/tailwind.config.js b/packages/nextra-theme-blog/tailwind.config.js
index 61480495ba..3b79e53cc1 100644
--- a/packages/nextra-theme-blog/tailwind.config.js
+++ b/packages/nextra-theme-blog/tailwind.config.js
@@ -1,6 +1,6 @@
const colors = require('tailwindcss/colors')
module.exports = {
- content: ['./src/**/*.{js,tsx,jsx}'],
+ content: ['./src/**/*.{js,tsx,jsx}', '../nextra/src/components/*.tsx'],
theme: {
colors: {
transparent: 'transparent',
diff --git a/packages/nextra-theme-docs/__test__/__fixture__/pageMap.ts b/packages/nextra-theme-docs/__test__/__fixture__/pageMap.ts
index a1c498f67b..aee529b325 100644
--- a/packages/nextra-theme-docs/__test__/__fixture__/pageMap.ts
+++ b/packages/nextra-theme-docs/__test__/__fixture__/pageMap.ts
@@ -3,42 +3,42 @@ export const cnPageMap: any[] = [
name: 'blog',
children: [
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Announcing SWR 1.0'
},
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Announcing SWR 1.0'
},
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'SWR 1.0 の発表'
},
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Announcing SWR 1.0'
},
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Представляем SWR 1.0'
},
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'SWR 1.0 发布'
},
@@ -70,7 +70,7 @@ export const cnPageMap: any[] = [
locale: 'zh-CN'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'Cache',
performance: 'Performance',
@@ -79,7 +79,7 @@ export const cnPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'Cache',
performance: 'Rendimiento',
@@ -88,7 +88,7 @@ export const cnPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'キャッシュ',
performance: 'パフォーマンス',
@@ -97,7 +97,7 @@ export const cnPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: '캐시',
performance: '성능',
@@ -106,7 +106,7 @@ export const cnPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'Кеш',
performance: 'Производительность',
@@ -115,7 +115,7 @@ export const cnPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: '缓存',
performance: '性能',
@@ -172,7 +172,7 @@ export const cnPageMap: any[] = [
locale: 'zh-CN'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'Getting Started',
options: 'Options',
@@ -195,7 +195,7 @@ export const cnPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'Comienza',
options: 'Opciones',
@@ -218,7 +218,7 @@ export const cnPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'はじめに',
options: 'オプション',
@@ -241,7 +241,7 @@ export const cnPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': '시작하기',
options: '옵션',
@@ -264,7 +264,7 @@ export const cnPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'Начало работы',
options: 'Опции',
@@ -287,7 +287,7 @@ export const cnPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': '入门',
options: '选项',
@@ -397,7 +397,7 @@ export const cnPageMap: any[] = [
locale: 'zh-CN'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: 'Basic Usage',
auth: 'Authentication',
@@ -408,7 +408,7 @@ export const cnPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: 'Uso Básico',
auth: 'Autenticación',
@@ -419,7 +419,7 @@ export const cnPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: '基本的な使用法',
auth: '認証',
@@ -430,7 +430,7 @@ export const cnPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: '기본 사용법',
auth: '인증',
@@ -441,7 +441,7 @@ export const cnPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: 'Основное использование',
auth: 'Аутентификация',
@@ -452,7 +452,7 @@ export const cnPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: '基本用法',
auth: '身份验证',
@@ -483,7 +483,7 @@ export const cnPageMap: any[] = [
locale: 'zh-CN'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: 'Introduction',
@@ -506,7 +506,7 @@ export const cnPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: 'Introducción',
@@ -529,7 +529,7 @@ export const cnPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: '前書き',
@@ -552,7 +552,7 @@ export const cnPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: '소개',
@@ -575,7 +575,7 @@ export const cnPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: 'Введение',
@@ -598,7 +598,7 @@ export const cnPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: '简介',
@@ -627,42 +627,42 @@ export const usPageMap: any[] = [
name: 'blog',
children: [
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Announcing SWR 1.0'
},
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Announcing SWR 1.0'
},
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'SWR 1.0 の発表'
},
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Announcing SWR 1.0'
},
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'Представляем SWR 1.0'
},
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'swr-v1': 'SWR 1.0 发布'
},
@@ -694,7 +694,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'Cache',
performance: 'Performance',
@@ -703,7 +703,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'Cache',
performance: 'Rendimiento',
@@ -712,7 +712,7 @@ export const usPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'キャッシュ',
performance: 'パフォーマンス',
@@ -721,7 +721,7 @@ export const usPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: '캐시',
performance: '성능',
@@ -730,7 +730,7 @@ export const usPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: 'Кеш',
performance: 'Производительность',
@@ -739,7 +739,7 @@ export const usPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
cache: '缓存',
performance: '性能',
@@ -796,7 +796,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'Getting Started',
options: 'Options',
@@ -819,7 +819,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'Comienza',
options: 'Opciones',
@@ -842,7 +842,7 @@ export const usPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'はじめに',
options: 'オプション',
@@ -865,7 +865,7 @@ export const usPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': '시작하기',
options: '옵션',
@@ -888,7 +888,7 @@ export const usPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': 'Начало работы',
options: 'Опции',
@@ -911,7 +911,7 @@ export const usPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
'getting-started': '入门',
options: '选项',
@@ -1021,7 +1021,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: 'Basic Usage',
auth: 'Authentication',
@@ -1032,7 +1032,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: 'Uso Básico',
auth: 'Autenticación',
@@ -1043,7 +1043,7 @@ export const usPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: '基本的な使用法',
auth: '認証',
@@ -1054,7 +1054,7 @@ export const usPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: '기본 사용법',
auth: '인증',
@@ -1065,7 +1065,7 @@ export const usPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: 'Основное использование',
auth: 'Аутентификация',
@@ -1076,7 +1076,7 @@ export const usPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
basic: '基本用法',
auth: '身份验证',
@@ -1107,7 +1107,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: 'Introduction',
@@ -1130,7 +1130,7 @@ export const usPageMap: any[] = [
locale: 'en-US'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: 'Introducción',
@@ -1153,7 +1153,7 @@ export const usPageMap: any[] = [
locale: 'es-ES'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: '前書き',
@@ -1176,7 +1176,7 @@ export const usPageMap: any[] = [
locale: 'ja'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: '소개',
@@ -1199,7 +1199,7 @@ export const usPageMap: any[] = [
locale: 'ko'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: 'Введение',
@@ -1222,7 +1222,7 @@ export const usPageMap: any[] = [
locale: 'ru'
},
{
- name: 'meta.json',
+ name: '_meta.json',
meta: {
index: {
title: '简介',
diff --git a/packages/nextra-theme-docs/__test__/normalize-page.spec.ts b/packages/nextra-theme-docs/__test__/normalize-page.spec.ts
index 0136ba7362..f8d96553ff 100644
--- a/packages/nextra-theme-docs/__test__/normalize-page.spec.ts
+++ b/packages/nextra-theme-docs/__test__/normalize-page.spec.ts
@@ -56,7 +56,7 @@ describe('normalize-page', () => {
{ name: 'get-started', route: '/get-started' },
{ name: 'index', route: '/' },
{
- name: 'meta.json',
+ name: '_meta.json',
route: '',
meta: {
'404': {
@@ -89,7 +89,7 @@ describe('normalize-page', () => {
{ name: 'get-started', route: '/get-started' },
{ name: 'index', route: '/' },
{
- name: 'meta.json',
+ name: '_meta.json',
route: '',
meta: {
'500': {
diff --git a/packages/nextra-theme-docs/css/hamburger.css b/packages/nextra-theme-docs/css/hamburger.css
new file mode 100644
index 0000000000..36edd926e2
--- /dev/null
+++ b/packages/nextra-theme-docs/css/hamburger.css
@@ -0,0 +1,39 @@
+.nextra-hamburger svg {
+ g {
+ transform-origin: center;
+ transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1);
+ }
+ path {
+ opacity: 1;
+ transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1) 0.2s,
+ opacity 0.2s ease 0.2s;
+ }
+
+ &.open {
+ path {
+ transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1),
+ opacity 0s ease 0.2s;
+ }
+ g {
+ transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1) 0.2s;
+ }
+ }
+
+ &.open > {
+ path {
+ opacity: 0;
+ }
+ g:nth-of-type(1) {
+ transform: rotate(45deg);
+ path {
+ transform: translate3d(0, 6px, 0);
+ }
+ }
+ g:nth-of-type(2) {
+ transform: rotate(-45deg);
+ path {
+ transform: translate3d(0, -6px, 0);
+ }
+ }
+ }
+}
diff --git a/packages/nextra-theme-docs/css/scrollbar.css b/packages/nextra-theme-docs/css/scrollbar.css
new file mode 100644
index 0000000000..25a426d740
--- /dev/null
+++ b/packages/nextra-theme-docs/css/scrollbar.css
@@ -0,0 +1,41 @@
+.nextra-scrollbar {
+ scrollbar-gutter: stable;
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+ &::-webkit-scrollbar-track {
+ margin-top: 20px;
+ margin-bottom: 16px;
+ background-color: transparent;
+ }
+ &::-webkit-scrollbar-thumb {
+ box-shadow: inset 0 0 0 5px rgba(128, 128, 128, 0);
+ border-radius: 20px;
+ }
+ &:hover::-webkit-scrollbar-thumb {
+ box-shadow: inset 0 0 0 5px rgba(128, 128, 128, 0.2);
+ &:hover {
+ box-shadow: inset 0 0 0 5px rgba(128, 128, 128, 0.4);
+ }
+ }
+}
+
+.nextra-sidebar-container.with-menu.nextra-scrollbar::-webkit-scrollbar-track {
+ margin-bottom: 76px;
+}
+
+@media (max-width: 767px) {
+ .nextra-container .nextra-scrollbar {
+ scrollbar-gutter: auto;
+ }
+}
+
+/* Hide scrollbar */
+.no-scrollbar {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+
+ &::-webkit-scrollbar {
+ display: none; /* Chrome, Safari and Opera */
+ }
+}
diff --git a/packages/nextra-theme-docs/src/styles.css b/packages/nextra-theme-docs/css/styles.css
similarity index 61%
rename from packages/nextra-theme-docs/src/styles.css
rename to packages/nextra-theme-docs/css/styles.css
index c74e35b0db..7ac7f6e44c 100644
--- a/packages/nextra-theme-docs/src/styles.css
+++ b/packages/nextra-theme-docs/css/styles.css
@@ -4,6 +4,9 @@
@import 'nextra/styles/variables.css';
@import 'nextra/styles/code-block.css';
@import 'nextra/styles/subheading-anchor.css';
+@import './hamburger.css';
+@import './scrollbar.css';
+@import './typesetting-article.css';
:root {
--nextra-primary-hue: 212deg;
@@ -42,67 +45,19 @@ body {
box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.1) inset;
}
}
- @supports (
- (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
- ) {
- .nextra-nav-container-blur {
- backdrop-filter: blur(12px);
- @apply bg-opacity-[.85] dark:bg-opacity-80;
- }
- }
.nextra-nav-link {
@apply text-sm;
}
.nextra-menu-icon {
- @apply select-none rounded outline-none;
- &:active {
- @apply bg-gray-400/20;
- }
- }
- .nextra-menu-icon svg {
- g {
- transform-origin: center;
- transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1);
- }
- path {
- opacity: 1;
- transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1) 0.2s,
- opacity 0.2s ease 0.2s;
- }
- &.open {
- path {
- transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1),
- opacity 0s ease 0.2s;
- }
- g {
- transition: transform 0.2s cubic-bezier(0.25, 1, 0.5, 1) 0.2s;
- }
- }
- &.open > {
- path {
- opacity: 0;
- }
- g:nth-of-type(1) {
- transform: rotate(45deg);
- path {
- transform: translate3d(0, 6px, 0);
- }
- }
- g:nth-of-type(2) {
- transform: rotate(-45deg);
- path {
- transform: translate3d(0, -6px, 0);
- }
- }
- }
+ @apply select-none rounded outline-none active:bg-gray-400/20;
}
}
/* Sidebar */
.nextra-sidebar {
-webkit-touch-callout: none;
- ul {
+ ul.nextra-sidebar-list {
a, button {
&:focus-visible {
@apply ring ring-primary-200;
@@ -146,48 +101,6 @@ body {
@apply ltr:md:border-l ltr:pl-3 ltr:ml-1 rtl:md:border-r rtl:pr-3 rtl:mr-1;
}
}
- .nextra-sidebar-search {
- @apply sticky top-0 -mt-4 bg-white pt-4 dark:bg-dark;
- box-shadow: 0 2px 14px 6px white;
- z-index: 1;
- .dark & {
- box-shadow: 0 2px 14px 6px #111;
- }
- }
-
- .nextra-sidebar-menu {
- @apply sticky bottom-0;
- @media (prefers-contrast: more) {
- & {
- box-shadow: none;
- border-top: 1px solid #999;
- }
- }
- }
- }
-
- .nextra-scrollbar {
- scrollbar-gutter: stable;
- &::-webkit-scrollbar {
- width: 6px;
- }
- &::-webkit-scrollbar-track {
- margin-top: 20px;
- margin-bottom: 16px;
- background-color: transparent;
- }
- &::-webkit-scrollbar-thumb {
- box-shadow: inset 0 0 0 5px rgba(128, 128, 128, 0);
- border-radius: 20px;
- }
- &:hover {
- &::-webkit-scrollbar-thumb {
- box-shadow: inset 0 0 0 5px rgba(128, 128, 128, 0.2);
- &:hover {
- box-shadow: inset 0 0 0 5px rgba(128, 128, 128, 0.4);
- }
- }
- }
}
}
@@ -244,11 +157,6 @@ body {
.nextra-banner-hidden div.nextra-nav-container {
@apply top-0;
}
- .nextra-container {
- .nextra-scrollbar {
- scrollbar-gutter: auto;
- }
- }
}
@media (prefers-reduced-motion: reduce) and (max-width: 767px) {
@@ -304,34 +212,6 @@ article {
vertical-align: -4px;
}
}
-
- /* Typesettings */
- &.nextra-body-typesetting-article {
- font-size: 17px;
- font-feature-settings: 'rlig' 1, 'calt' 1;
- h1 {
- @apply mt-6 mb-4 text-center;
- font-size: 2.5rem;
- }
- h2 {
- @apply border-none;
- }
- a {
- @apply no-underline hover:underline;
- }
- p {
- @apply leading-8;
- }
- code {
- @apply border-none dark:bg-neutral-700;
- }
- pre code {
- @apply dark:bg-transparent;
- }
- .subheading-anchor + a {
- @apply no-underline hover:no-underline after:hidden;
- }
- }
}
.nextra-toc, .nextra-sidebar-container {
@@ -339,37 +219,25 @@ article {
linear-gradient(to left, #000 10px, transparent 10px);
}
-.nextra-sidebar-container.with-menu.nextra-scrollbar::-webkit-scrollbar-track {
- margin-bottom: 76px;
- }
-
-/* Search */
-.nextra-search ul {
- max-height: min(calc(100vh - 5rem - env(safe-area-inset-bottom)), 400px);
- transition: max-height 0.2s ease;
- @supports (
- (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
- ) {
- & {
- backdrop-filter: blur(16px);
- @apply bg-opacity-70 dark:bg-opacity-80;
- }
+@supports (
+ (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
+) {
+ .nextra-search ul {
+ @apply backdrop-blur-lg bg-opacity-70 dark:bg-opacity-80;
+ }
+ .nextra-nav-container-blur {
+ @apply backdrop-blur-md bg-opacity-[.85] dark:bg-opacity-80;
}
}
@media screen and (max-width: 767px) {
- .nextra-search {
- .excerpt {
- overflow: hidden;
- text-overflow: ellipsis;
- display: -webkit-box;
- -webkit-line-clamp: 1;
- line-clamp: 1;
- -webkit-box-orient: vertical;
- }
- ul {
- max-height: min(calc(50vh - 11rem - env(safe-area-inset-bottom)), 400px);
- }
+ .nextra-search .excerpt {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ -webkit-box-orient: vertical;
}
}
@@ -403,17 +271,6 @@ input[type='search']::-webkit-search-results-decoration {
@apply mr-1;
}
-/* Hide scrollbar for Chrome, Safari and Opera */
-.no-scrollbar::-webkit-scrollbar {
- display: none;
-}
-
-/* Hide scrollbar for IE, Edge and Firefox */
-.no-scrollbar {
- -ms-overflow-style: none; /* IE and Edge */
- scrollbar-width: none; /* Firefox */
-}
-
.nextra-banner-hidden .nextra-banner-container {
display: none;
}
diff --git a/packages/nextra-theme-docs/css/typesetting-article.css b/packages/nextra-theme-docs/css/typesetting-article.css
new file mode 100644
index 0000000000..e50a3db17e
--- /dev/null
+++ b/packages/nextra-theme-docs/css/typesetting-article.css
@@ -0,0 +1,26 @@
+article.nextra-body-typesetting-article {
+ font-size: 17px;
+ font-feature-settings: 'rlig' 1, 'calt' 1;
+ h1 {
+ @apply mt-6 mb-4 text-center;
+ font-size: 2.5rem;
+ }
+ h2 {
+ @apply border-none;
+ }
+ a {
+ @apply no-underline hover:underline;
+ }
+ p {
+ @apply leading-8;
+ }
+ code {
+ @apply border-none dark:bg-neutral-700;
+ }
+ pre code {
+ @apply dark:bg-transparent;
+ }
+ .subheading-anchor + a {
+ @apply no-underline hover:no-underline after:hidden;
+ }
+}
diff --git a/packages/nextra-theme-docs/package.json b/packages/nextra-theme-docs/package.json
index 13f82411a9..133ce09bae 100644
--- a/packages/nextra-theme-docs/package.json
+++ b/packages/nextra-theme-docs/package.json
@@ -31,11 +31,11 @@
"dev": "concurrently \"pnpm dev:layout\" \"pnpm dev:tailwind\"",
"build": "tsup",
"build:all": "pnpm build && pnpm run build:tailwind",
- "build:tailwind": "cross-env NODE_ENV=production pnpm postcss src/styles.css -o style.css --verbose",
+ "build:tailwind": "cross-env NODE_ENV=production pnpm postcss css/styles.css -o style.css --verbose",
"types": "tsup --dts-only",
"types:check": "tsc --noEmit",
"dev:layout": "tsup --watch",
- "dev:tailwind": "cross-env TAILWIND_MODE=watch pnpm postcss src/styles.css -o style.css --watch",
+ "dev:tailwind": "cross-env TAILWIND_MODE=watch pnpm postcss css/styles.css -o style.css --watch",
"prepublishOnly": "pnpm build:all",
"test": "vitest --run",
"clean": "rimraf ./dist ./style.css",
diff --git a/packages/nextra-theme-docs/src/components/banner.tsx b/packages/nextra-theme-docs/src/components/banner.tsx
index 24c31be79e..2c1c4996d9 100644
--- a/packages/nextra-theme-docs/src/components/banner.tsx
+++ b/packages/nextra-theme-docs/src/components/banner.tsx
@@ -3,36 +3,36 @@ import { XIcon } from 'nextra/icons'
import { useConfig } from '../contexts'
import { renderComponent } from '../utils'
-export function Banner(): ReactElement {
- const { bannerKey, banner } = useConfig()
-
+export function Banner(): ReactElement | null {
+ const { banner } = useConfig()
+ if (!banner.text) {
+ return null
+ }
return (
<>
- {banner && bannerKey ? (
-
-
- {renderComponent(banner)}
-
-
+
+
+ {renderComponent(banner.text)}
- ) : null}
+
+
>
)
}
diff --git a/packages/nextra-theme-docs/src/components/breadcrumb.tsx b/packages/nextra-theme-docs/src/components/breadcrumb.tsx
index 227100f98b..480cc1d55e 100644
--- a/packages/nextra-theme-docs/src/components/breadcrumb.tsx
+++ b/packages/nextra-theme-docs/src/components/breadcrumb.tsx
@@ -10,19 +10,19 @@ export function Breadcrumb({
activePath: Item[]
}): ReactElement {
return (
-
+
{activePath.map((item, index) => {
const isLink = !item.children || item.withIndexPage
const isActive = index === activePath.length - 1
return (
- {index > 0 && }
-
+
)
}
diff --git a/packages/nextra-theme-docs/src/components/callout.tsx b/packages/nextra-theme-docs/src/components/callout.tsx
index c57a776c9c..9274b5b2cf 100644
--- a/packages/nextra-theme-docs/src/components/callout.tsx
+++ b/packages/nextra-theme-docs/src/components/callout.tsx
@@ -39,7 +39,7 @@ export function Callout({
>
{emoji}
-
+
{children}
diff --git a/packages/nextra-theme-docs/src/components/flexsearch.tsx b/packages/nextra-theme-docs/src/components/flexsearch.tsx
index 641cbcc66e..4096eaa5cb 100644
--- a/packages/nextra-theme-docs/src/components/flexsearch.tsx
+++ b/packages/nextra-theme-docs/src/components/flexsearch.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, ReactElement, ReactNode } from 'react'
+import React, { useState, ReactElement, ReactNode } from 'react'
import { useRouter } from 'next/router'
import FlexSearch from 'flexsearch'
import cn from 'clsx'
@@ -48,7 +48,11 @@ const indexes: {
[locale: string]: [PageIndex, SectionIndex]
} = {}
-export function Flexsearch(): ReactElement {
+export function Flexsearch({
+ className
+}: {
+ className?: string
+}): ReactElement {
const router = useRouter()
const { locale = DEFAULT_LOCALE } = router
const [loading, setLoading] = useState(false)
@@ -246,7 +250,8 @@ export function Flexsearch(): ReactElement {
)
diff --git a/packages/nextra-theme-docs/src/components/footer.tsx b/packages/nextra-theme-docs/src/components/footer.tsx
index 1172b08da9..450612fabd 100644
--- a/packages/nextra-theme-docs/src/components/footer.tsx
+++ b/packages/nextra-theme-docs/src/components/footer.tsx
@@ -7,7 +7,6 @@ import { renderComponent } from '../utils'
export function Footer({ menu }: { menu?: boolean }): ReactElement {
const config = useConfig()
-
return (