diff --git a/README.md b/README.md
index a1061d2..50cdbf3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
diff --git a/cli/server/public/apple-touch-icon.png b/cli/server/public/apple-touch-icon.png
new file mode 100644
index 0000000..29a9ac9
Binary files /dev/null and b/cli/server/public/apple-touch-icon.png differ
diff --git a/cli/server/public/favicon-16.png b/cli/server/public/favicon-16.png
new file mode 100644
index 0000000..9d8cd78
Binary files /dev/null and b/cli/server/public/favicon-16.png differ
diff --git a/cli/server/public/favicon-32.png b/cli/server/public/favicon-32.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/cli/server/public/favicon-32.png differ
diff --git a/cli/server/public/favicon-48.png b/cli/server/public/favicon-48.png
new file mode 100644
index 0000000..b09a2a0
Binary files /dev/null and b/cli/server/public/favicon-48.png differ
diff --git a/cli/server/public/favicon.ico b/cli/server/public/favicon.ico
new file mode 100644
index 0000000..5cca6cb
Binary files /dev/null and b/cli/server/public/favicon.ico differ
diff --git a/cli/server/public/favicon.png b/cli/server/public/favicon.png
index 521edf8..f455437 100644
Binary files a/cli/server/public/favicon.png and b/cli/server/public/favicon.png differ
diff --git a/cli/server/public/icon-192.png b/cli/server/public/icon-192.png
new file mode 100644
index 0000000..c158d9e
Binary files /dev/null and b/cli/server/public/icon-192.png differ
diff --git a/public/icon-512.png b/cli/server/public/icon-512.png
similarity index 100%
rename from public/icon-512.png
rename to cli/server/public/icon-512.png
diff --git a/cli/server/public/logo-200.png b/cli/server/public/logo-200.png
new file mode 100644
index 0000000..d6aa943
Binary files /dev/null and b/cli/server/public/logo-200.png differ
diff --git a/cli/server/public/logo-400.png b/cli/server/public/logo-400.png
new file mode 100644
index 0000000..7633523
Binary files /dev/null and b/cli/server/public/logo-400.png differ
diff --git a/public/logo.png b/cli/server/public/logo.png
similarity index 100%
rename from public/logo.png
rename to cli/server/public/logo.png
diff --git a/cli/server/public/logo.svg b/cli/server/public/logo.svg
new file mode 100644
index 0000000..7d83e68
--- /dev/null
+++ b/cli/server/public/logo.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/docs/blog/changelog.md b/docs/blog/changelog.md
index f301d45..446c395 100644
--- a/docs/blog/changelog.md
+++ b/docs/blog/changelog.md
@@ -1,13 +1,24 @@
---
slug: changelog
title: Changelog
-date: 2026-05-17
+date: 2026-06-07
authors: [fecommunity]
tags: [reactpress, release]
---
+## [3.1.0](https://github.com/fecommunity/reactpress/compare/v3.0.0...v3.1.0) (2026-06-07)
+
+**Toolkit theme refactor** — modular exports, first-class theme development.
+
+- **Toolkit 3.1**: split into `theme` / `ui` / `app` / `plugin` submodules; theme manifest, appearance config (Formily), SSR bootstrap, site settings & preview
+- **CLI 3.0.3**: `reactpress nginx` reverse proxy; Docker `mysqldump` in `db backup`; build target selection & step logging; improved UX
+- **Brand assets**: centralized `public/brand/` + `pnpm export:brand` to sync logos/favicons across server, web, cli, and themes
+- **Docs**: official theme section, Lighthouse scores; theme manifest schema URL updated
+
+---
+
## [3.0.0](https://github.com/fecommunity/reactpress/compare/v2.0.2...v3.0.0) (2026-05-17)
**ReactPress 3.0 Platform** — one package, one command, your CMS in about a minute.
diff --git a/docs/i18n/zh/docusaurus-plugin-content-blog/changelog.md b/docs/i18n/zh/docusaurus-plugin-content-blog/changelog.md
index 4aa6c24..1d5de02 100644
--- a/docs/i18n/zh/docusaurus-plugin-content-blog/changelog.md
+++ b/docs/i18n/zh/docusaurus-plugin-content-blog/changelog.md
@@ -1,13 +1,30 @@
---
slug: changelog
title: 更新日志
-date: 2026-05-17
+date: 2026-06-07
authors: [fecommunity]
tags: [reactpress, release]
---
+## [3.1.0](https://github.com/fecommunity/reactpress/compare/v3.0.0...v3.1.0) (2026-06-07)
+
+**Toolkit 主题化重构** — 模块化导出,主题开发一等公民。
+
+### 新特性
+
+- **Toolkit 3.1**:拆分为 `theme` / `ui` / `app` / `plugin` 子模块;主题 manifest 解析、外观配置(Formily)、SSR bootstrap、站点设置与预览
+- **CLI 3.0.3**:`reactpress nginx` 反向代理;`db backup` 支持 Docker 内 `mysqldump`;`build` 目标选择与分步日志;交互体验优化
+- **品牌资产**:`public/brand/` 集中管理 + `pnpm export:brand` 一键同步至各目录
+- **文档**:README 新增官方主题章节与 Lighthouse 性能指标
+
+### Bug Fixes
+
+- 增强 monorepo 根目录检测;主题 manifest schema URL 更新为 `reactpress.surge.sh`
+
+---
+
## [3.0.0](https://github.com/fecommunity/reactpress/compare/v2.0.2...v3.0.0) (2026-05-17)
**ReactPress 3.0 平台版** — 装一个包,敲一条命令,一分钟拥有自己的 CMS。
diff --git a/docs/src/components/Home/Logo.tsx b/docs/src/components/Home/Logo.tsx
index bc80612..10bb69a 100644
--- a/docs/src/components/Home/Logo.tsx
+++ b/docs/src/components/Home/Logo.tsx
@@ -1,40 +1,18 @@
-/**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
+import useBaseUrl from '@docusaurus/useBaseUrl';
import React from 'react';
function Logo({ className }) {
+ const src = useBaseUrl('/img/logo.svg');
+
return (
-
+ alt="ReactPress"
+ className={className}
+ decoding="async"
+ />
);
}
diff --git a/docs/static/img/favicon.ico b/docs/static/img/favicon.ico
index 5c125de..5cca6cb 100644
Binary files a/docs/static/img/favicon.ico and b/docs/static/img/favicon.ico differ
diff --git a/docs/static/img/logo.svg b/docs/static/img/logo.svg
index 2694bf8..7d83e68 100644
--- a/docs/static/img/logo.svg
+++ b/docs/static/img/logo.svg
@@ -1 +1,7 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/package.json b/package.json
index abcd70a..6a7e351 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,8 @@
"test": "pnpm --dir cli test",
"test:cli": "pnpm --dir cli test",
"test:smoke": "node scripts/smoke-api-health.mjs",
- "test:benchmark": "node scripts/benchmark-cold-start.mjs"
+ "test:benchmark": "node scripts/benchmark-cold-start.mjs",
+ "export:brand": "node scripts/export-brand-assets.mjs"
},
"dependencies": {
"@fecommunity/reactpress": "workspace:*"
@@ -48,6 +49,7 @@
"husky": "^7.0.4",
"lint-staged": "^12.4.1",
"prettier": "^2.3.2",
+ "to-ico": "^1.1.5",
"typescript": "~4.1.6"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6633ad5..3f46ed1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -21,6 +21,9 @@ importers:
prettier:
specifier: ^2.3.2
version: 2.8.8
+ to-ico:
+ specifier: ^1.1.5
+ version: 1.1.5
typescript:
specifier: ~4.1.6
version: 4.1.6
@@ -51,14 +54,14 @@ importers:
ora:
specifier: ^5.4.1
version: 5.4.1
- devDependencies:
- '@fecommunity/reactpress-cli-core':
- specifier: npm:@fecommunity/reactpress-cli@0.1.0
- version: '@fecommunity/reactpress-cli@0.1.0(@types/node@24.5.2)'
optionalDependencies:
mysql2:
specifier: ^3.12.0
version: 3.22.3(@types/node@24.5.2)
+ devDependencies:
+ '@fecommunity/reactpress-cli-core':
+ specifier: npm:@fecommunity/reactpress-cli@0.1.0
+ version: '@fecommunity/reactpress-cli@0.1.0(@types/node@24.5.2)'
client:
dependencies:
@@ -130,7 +133,7 @@ importers:
version: 2.1.35
next:
specifier: ^12.3.4
- version: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ version: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
next-compose-plugins:
specifier: ^2.2.1
version: 2.2.1
@@ -142,19 +145,19 @@ importers:
version: 1.8.5(webpack@5.98.0)
next-intl:
specifier: ^1.5.1
- version: 1.5.1(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(react@17.0.2)
+ version: 1.5.1(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(react@17.0.2)
next-page-transitions:
specifier: ^1.0.0-beta.2
version: 1.0.0-beta.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
next-pwa:
specifier: ^5.5.2
- version: 5.6.0(@babel/core@7.26.9)(@types/babel__core@7.20.5)(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(webpack@5.98.0)
+ version: 5.6.0(@babel/core@7.25.2)(@types/babel__core@7.20.5)(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(webpack@5.98.0)
next-sitemap:
specifier: ^1.6.102
- version: 1.9.12(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))
+ version: 1.9.12(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))
next-with-less:
specifier: ^2.0.5
- version: 2.0.5(less-loader@10.2.0(less@4.2.0)(webpack@5.98.0))(less@4.2.0)(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))
+ version: 2.0.5(less-loader@10.2.0(less@4.2.0)(webpack@5.98.0))(less@4.2.0)(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))
nprogress:
specifier: ^0.2.0
version: 0.2.0
@@ -184,7 +187,7 @@ importers:
version: 2.0.0(prop-types@15.8.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
react-spring:
specifier: ^9.1.2
- version: 9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react-konva@18.2.10(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react-zdog@1.2.2)(react@17.0.2)(three@0.168.0)(zdog@1.1.3)
+ version: 9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react-konva@18.2.10(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react-zdog@1.2.2)(react@17.0.2)(three@0.168.0)(zdog@1.1.3)
react-text-loop:
specifier: 2.3.0
version: 2.3.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
@@ -224,7 +227,7 @@ importers:
version: 8.11.0
eslint-config-next:
specifier: 12.1.0
- version: 12.1.0(eslint@8.11.0)(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(typescript@4.6.2)
+ version: 12.1.0(eslint@8.11.0)(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(typescript@4.6.2)
eslint-config-prettier:
specifier: ^8.5.0
version: 8.10.0(eslint@8.11.0)
@@ -263,19 +266,19 @@ importers:
dependencies:
'@docusaurus/core':
specifier: 3.7.0
- version: 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ version: 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
'@docusaurus/preset-classic':
specifier: 3.7.0
- version: 3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(@types/react@17.0.42)(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)
+ version: 3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(@types/react@18.3.31)(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)
'@mdx-js/react':
specifier: ^3.0.0
- version: 3.1.0(@types/react@17.0.42)(react@19.0.0)
+ version: 3.1.0(@types/react@18.3.31)(react@19.0.0)
clsx:
specifier: ^2.0.0
version: 2.1.1
docusaurus-plugin-sass:
specifier: ^0.2.5
- version: 0.2.6(@docusaurus/core@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(sass@1.79.3)(webpack@5.98.0)
+ version: 0.2.6(@docusaurus/core@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(sass@1.79.3)(webpack@5.98.0)
prism-react-renderer:
specifier: ^2.3.0
version: 2.4.1(react@19.0.0)
@@ -291,13 +294,13 @@ importers:
devDependencies:
'@docusaurus/module-type-aliases':
specifier: 3.7.0
- version: 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ version: 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@docusaurus/tsconfig':
specifier: 3.7.0
version: 3.7.0
'@docusaurus/types':
specifier: 3.7.0
- version: 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ version: 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
typescript:
specifier: ~5.6.2
version: 5.6.3
@@ -451,7 +454,7 @@ importers:
devDependencies:
'@nestjs/schematics':
specifier: ^6.7.0
- version: 6.9.4(typescript@4.1.6)
+ version: 6.9.4(typescript@4.6.2)
'@nestjs/testing':
specifier: ^6.7.1
version: 6.11.11(@nestjs/common@6.11.11(reflect-metadata@0.1.14)(rxjs@6.6.7))(@nestjs/core@6.11.11(@nestjs/common@6.11.11(reflect-metadata@0.1.14)(rxjs@6.6.7))(encoding@0.1.13)(reflect-metadata@0.1.14)(rxjs@6.6.7))
@@ -469,10 +472,10 @@ importers:
version: 2.0.16
'@typescript-eslint/eslint-plugin':
specifier: ^5.21.0
- version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.1.6))(eslint@8.57.1)(typescript@4.1.6)
+ version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.6.2))(eslint@8.57.1)(typescript@4.6.2)
'@typescript-eslint/parser':
specifier: ^5.21.0
- version: 5.62.0(eslint@8.57.1)(typescript@4.1.6)
+ version: 5.62.0(eslint@8.57.1)(typescript@4.6.2)
eslint:
specifier: ^8.15.0
version: 8.57.1
@@ -481,7 +484,7 @@ importers:
version: 8.10.0(eslint@8.57.1)
eslint-plugin-import:
specifier: ^2.26.0
- version: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.1.6))(eslint@8.57.1)
+ version: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.6.2))(eslint@8.57.1)
eslint-plugin-prettier:
specifier: ^4.0.0
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.1))(eslint@8.57.1)(prettier@1.19.1)
@@ -505,19 +508,19 @@ importers:
version: 24.3.0(jest@24.9.0)
ts-loader:
specifier: ^6.1.1
- version: 6.2.2(typescript@4.1.6)
+ version: 6.2.2(typescript@4.6.2)
ts-node:
specifier: ^8.4.1
- version: 8.10.2(typescript@4.1.6)
+ version: 8.10.2(typescript@4.6.2)
tsconfig-paths:
specifier: ^3.9.0
version: 3.15.0
tslint:
specifier: ^5.20.0
- version: 5.20.1(typescript@4.1.6)
+ version: 5.20.1(typescript@4.6.2)
typescript:
- specifier: ~4.1.6
- version: 4.1.6
+ specifier: 4.6.2
+ version: 4.6.2
templates/hello-world:
dependencies:
@@ -529,7 +532,7 @@ importers:
version: 10.1.0
next:
specifier: ^12.3.4
- version: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ version: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
react:
specifier: 17.0.2
version: 17.0.2
@@ -554,7 +557,7 @@ importers:
version: link:../../toolkit
next:
specifier: ^12.3.4
- version: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ version: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
react:
specifier: 17.0.2
version: 17.0.2
@@ -567,7 +570,7 @@ importers:
version: 6.8.0
'@testing-library/react':
specifier: ^16.3.0
- version: 16.3.0(@testing-library/dom@10.4.1)(@types/react@17.0.42)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@17.0.42))(@types/react@17.0.42)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
'@types/babel__traverse':
specifier: ^7.28.0
version: 7.28.0
@@ -595,6 +598,12 @@ importers:
toolkit:
dependencies:
+ '@tanstack/react-query':
+ specifier: '>=5'
+ version: 5.101.0(react@18.3.1)
+ ajv:
+ specifier: ^8.17.1
+ version: 8.17.1
axios:
specifier: ^1.12.2
version: 1.12.2
@@ -604,19 +613,49 @@ importers:
fs-extra:
specifier: ^10.0.0
version: 10.1.0
+ highlight.js:
+ specifier: '>=9'
+ version: 9.18.5
lodash:
specifier: ^4.17.21
version: 4.17.21
+ next:
+ specifier: '>=12'
+ version: 12.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.3)
+ nprogress:
+ specifier: '>=0.2'
+ version: 0.2.0
swagger-typescript-api:
specifier: ^12.0.4
version: 12.0.4(encoding@0.1.13)
+ viewerjs:
+ specifier: '>=1'
+ version: 1.11.6
devDependencies:
+ '@ant-design/cssinjs':
+ specifier: ^1.22.0
+ version: 1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/fs-extra':
specifier: ^9.0.13
version: 9.0.13
'@types/node':
specifier: ^17.0.22
version: 17.0.22
+ '@types/react':
+ specifier: ^18.3.18
+ version: 18.3.31
+ '@types/react-dom':
+ specifier: ^18.3.5
+ version: 18.3.7(@types/react@18.3.31)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ typescript:
+ specifier: ^5.7.3
+ version: 5.9.3
packages:
@@ -3360,6 +3399,14 @@ packages:
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
engines: {node: '>=14.16'}
+ '@tanstack/query-core@5.101.0':
+ resolution: {integrity: sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==}
+
+ '@tanstack/react-query@5.101.0':
+ resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==}
+ peerDependencies:
+ react: ^18 || ^19
+
'@testing-library/dom@10.4.1':
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
engines: {node: '>=18'}
@@ -3550,6 +3597,11 @@ packages:
'@types/range-parser@1.2.7':
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
+ '@types/react-dom@18.3.7':
+ resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==}
+ peerDependencies:
+ '@types/react': ^18.0.0
+
'@types/react-infinite-scroller@1.2.5':
resolution: {integrity: sha512-fJU1jhMgoL6NJFrqTM0Ob7tnd2sQWGxe2ESwiU6FZWbJK/VO/Er5+AOhc+e2zbT0dk5pLygqctsulOLJ8xnSzw==}
@@ -3571,6 +3623,9 @@ packages:
'@types/react@17.0.42':
resolution: {integrity: sha512-nuab3x3CpJ7VFeNA+3HTUuEkvClYHXqWtWd7Ud6AZYW7Z3NH9WKtgU+tFB0ZLcHq+niB/HnzLcaZPqMJ95+k5Q==}
+ '@types/react@18.3.31':
+ resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==}
+
'@types/resolve@1.17.1':
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
@@ -4143,6 +4198,10 @@ packages:
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
engines: {node: '>= 0.4'}
+ arrify@1.0.1:
+ resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
+ engines: {node: '>=0.10.0'}
+
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
@@ -4357,6 +4416,9 @@ packages:
big.js@5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
+ bignumber.js@2.4.0:
+ resolution: {integrity: sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==}
+
binary-extensions@1.13.1:
resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==}
engines: {node: '>=0.10.0'}
@@ -4379,6 +4441,12 @@ packages:
bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
+ bmp-js@0.0.1:
+ resolution: {integrity: sha512-OS74Rlt0Aynu2mTPmY9RZOUOXlqWecFIILFXr70vv16/xCZnFxvri9IKkF1IGxQ8r9dOE62qGNpKxXx8Lko8bg==}
+
+ bmp-js@0.0.3:
+ resolution: {integrity: sha512-epsm3Z92j5xwek9p97pVw3KbsNc0F4QnbYh+N93SpbJYuHFQQ/UAh6K+bKFGyLePH3Hudtl/Sa95Quqp0gX8IQ==}
+
bn.js@4.12.3:
resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==}
@@ -4485,9 +4553,22 @@ packages:
bser@2.1.1:
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+ buffer-alloc-unsafe@1.1.0:
+ resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==}
+
+ buffer-alloc@1.2.0:
+ resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==}
+
buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
+ buffer-equal@0.0.1:
+ resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==}
+ engines: {node: '>=0.4.0'}
+
+ buffer-fill@1.0.0:
+ resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==}
+
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
@@ -4621,6 +4702,9 @@ packages:
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+ centra@2.7.0:
+ resolution: {integrity: sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==}
+
chalk@1.1.3:
resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
engines: {node: '>=0.10.0'}
@@ -5283,6 +5367,9 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
culvert@0.1.2:
resolution: {integrity: sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==}
@@ -5590,6 +5677,9 @@ packages:
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+ dom-walk@0.1.2:
+ resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
+
domain-browser@1.2.0:
resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
engines: {node: '>=0.4', npm: '>=1.2'}
@@ -6153,6 +6243,9 @@ packages:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'}
+ exif-parser@0.1.12:
+ resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==}
+
exit@0.1.2:
resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
engines: {node: '>= 0.8.0'}
@@ -6304,6 +6397,10 @@ packages:
peerDependencies:
webpack: ^4.0.0 || ^5.0.0
+ file-type@3.9.0:
+ resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==}
+ engines: {node: '>=0.10.0'}
+
file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
@@ -6566,6 +6663,10 @@ packages:
get-ready@1.0.0:
resolution: {integrity: sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw==}
+ get-stream@2.3.1:
+ resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==}
+ engines: {node: '>=0.10.0'}
+
get-stream@4.1.0:
resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
engines: {node: '>=6'}
@@ -6640,6 +6741,9 @@ packages:
resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
engines: {node: '>=6'}
+ global@4.4.0:
+ resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==}
+
globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
@@ -7118,6 +7222,10 @@ packages:
resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==}
engines: {node: '>= 12'}
+ ip-regex@1.0.3:
+ resolution: {integrity: sha512-HjpCHTuxbR/6jWJroc/VN+npo5j0T4Vv2TAI5qdEHQx7hsL767MeccGFSsLtF694EiZKTSEqgoeU6DtGFCcuqQ==}
+ engines: {node: '>=0.10.0'}
+
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@@ -7247,6 +7355,9 @@ packages:
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
engines: {node: '>=12'}
+ is-function@1.0.2:
+ resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==}
+
is-generator-fn@2.1.0:
resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
engines: {node: '>=6'}
@@ -7658,6 +7769,9 @@ packages:
engines: {node: '>= 6'}
hasBin: true
+ jimp@0.2.28:
+ resolution: {integrity: sha512-9HT7DA279xkTlry2oG30s6AtOUglNiY2UdyYpj0yNI4/NBv8PmdNC0gcldgMU4HqvbUlrM3+v+6GaHnTkH23JQ==}
+
jiti@1.21.7:
resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
hasBin: true
@@ -7665,6 +7779,12 @@ packages:
joi@17.13.3:
resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==}
+ jpeg-js@0.1.2:
+ resolution: {integrity: sha512-CiRVjMKBUp6VYtGwzRjrdnro41yMwKGOWdP9ATXqmixdz2n7KHNwdqthTYSSbOKj/Ha79Gct1sA8ZQpse55TYA==}
+
+ jpeg-js@0.2.0:
+ resolution: {integrity: sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==}
+
js-base64@2.6.4:
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
@@ -7906,6 +8026,9 @@ packages:
enquirer:
optional: true
+ load-bmfont@1.4.2:
+ resolution: {integrity: sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==}
+
load-json-file@4.0.0:
resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
engines: {node: '>=4'}
@@ -8475,6 +8598,9 @@ packages:
resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ min-document@2.19.2:
+ resolution: {integrity: sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==}
+
min-indent@1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'}
@@ -8501,6 +8627,9 @@ packages:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
+ minimist@0.0.8:
+ resolution: {integrity: sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==}
+
minimist@1.2.0:
resolution: {integrity: sha512-7Wl+Jz+IGWuSdgsQEJ4JunV0si/iMhg42MnQQG6h1R6TNeVenp4U9x5CC5v/gYqz/fENLQITAWXidNtVL0NNbw==}
@@ -8515,6 +8644,11 @@ packages:
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
engines: {node: '>=0.10.0'}
+ mkdirp@0.5.1:
+ resolution: {integrity: sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==}
+ deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
+ hasBin: true
+
mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true
@@ -9122,9 +9256,21 @@ packages:
resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==}
engines: {node: '>= 0.10'}
+ parse-bmfont-ascii@1.0.6:
+ resolution: {integrity: sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==}
+
+ parse-bmfont-binary@1.0.6:
+ resolution: {integrity: sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==}
+
+ parse-bmfont-xml@1.1.6:
+ resolution: {integrity: sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==}
+
parse-entities@4.0.2:
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
+ parse-headers@2.0.6:
+ resolution: {integrity: sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==}
+
parse-json@4.0.0:
resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
engines: {node: '>=4'}
@@ -9140,6 +9286,10 @@ packages:
parse-numeric-range@1.3.0:
resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==}
+ parse-png@1.1.2:
+ resolution: {integrity: sha512-Ge6gDV9T5zhkWHmjvnNiyhPTCIoY7W+FC7qWPtuL2lIGZAFxxqTRG/ouEXsH9qkw+HzYiPEU/tFcxOCEDTP1Yw==}
+ engines: {node: '>=4'}
+
parse5-htmlparser2-tree-adapter@6.0.1:
resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
@@ -9258,6 +9408,11 @@ packages:
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
+ phin@3.7.1:
+ resolution: {integrity: sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==}
+ engines: {node: '>= 8'}
+ deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -9302,6 +9457,10 @@ packages:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
+ pixelmatch@4.0.2:
+ resolution: {integrity: sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==}
+ hasBin: true
+
pkg-dir@3.0.0:
resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
engines: {node: '>=6'}
@@ -9347,6 +9506,10 @@ packages:
pn@1.1.0:
resolution: {integrity: sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==}
+ pngjs@3.4.0:
+ resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
+ engines: {node: '>=4.0.0'}
+
posix-character-classes@0.1.1:
resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==}
engines: {node: '>=0.10.0'}
@@ -10416,6 +10579,10 @@ packages:
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
engines: {node: '>=0.10.0'}
+ read-chunk@1.0.1:
+ resolution: {integrity: sha512-5NLTTdX45dKFtG8CX5pKmvS9V5u9wBE+gkklN7xhDuhq3pA2I4O7ALfKxosCMcLHOhkxj6GNacZhfXtp5nlCdg==}
+ engines: {node: '>=0.10.0'}
+
read-pkg-up@4.0.0:
resolution: {integrity: sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==}
engines: {node: '>=6'}
@@ -10651,6 +10818,10 @@ packages:
requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+ resize-img@1.1.2:
+ resolution: {integrity: sha512-/4nKUmuNPuM6gYTWad136ica81baOVjpesgv8FGaIvP0KWcbCWahOWBKaM4tFoM+aVcSA+qQDg28pcnIzFRpJw==}
+ engines: {node: '>=4'}
+
resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
@@ -11316,6 +11487,14 @@ packages:
stream-shift@1.0.3:
resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==}
+ stream-to-buffer@0.1.0:
+ resolution: {integrity: sha512-Da4WoKaZyu3nf+bIdIifh7IPkFjARBnBK+pYqn0EUJqksjV9afojjaCCHUemH30Jmu7T2qcKvlZm2ykN38uzaw==}
+ engines: {node: '>= 0.8'}
+
+ stream-to@0.2.2:
+ resolution: {integrity: sha512-Kg1BSDTwgGiVMtTCJNlo7kk/xzL33ZuZveEBRt6rXw+f1WLK/8kmz2NVCT/Qnv0JkV85JOHcLhD82mnXsR3kPw==}
+ engines: {node: '>= 0.10.0'}
+
stream-wormhole@1.1.0:
resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==}
engines: {node: '>=4.0.0'}
@@ -11697,6 +11876,9 @@ packages:
tiny-warning@1.0.3:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
+ tinycolor2@1.6.0:
+ resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
+
tmp@0.0.33:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
@@ -11711,6 +11893,10 @@ packages:
resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==}
engines: {node: '>= 0.4'}
+ to-ico@1.1.5:
+ resolution: {integrity: sha512-5kIh7m7bkIlqIESEZkL8gAMMzucXKfPe3hX2FoDY5HEAfD9OJU+Qh9b6Enp74w0qRcxVT5ejss66PHKqc3AVkg==}
+ engines: {node: '>=4'}
+
to-object-path@0.3.0:
resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==}
engines: {node: '>=0.10.0'}
@@ -11991,6 +12177,11 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
ua-parser-js@0.7.39:
resolution: {integrity: sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==}
hasBin: true
@@ -12123,6 +12314,10 @@ packages:
file-loader:
optional: true
+ url-regex@3.2.0:
+ resolution: {integrity: sha512-dQ9cJzMou5OKr6ZzfvwJkCq3rC72PNXhqz0v3EIhF4a3Np+ujr100AhUx2cKx5ei3iymoJpJrPB3sVSEMdqAeg==}
+ engines: {node: '>=0.10.0'}
+
url@0.11.4:
resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==}
engines: {node: '>= 0.4'}
@@ -12578,6 +12773,9 @@ packages:
resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==}
engines: {node: '>=12'}
+ xhr@2.6.0:
+ resolution: {integrity: sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==}
+
xml-js@1.6.11:
resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==}
hasBin: true
@@ -12585,10 +12783,17 @@ packages:
xml-name-validator@3.0.0:
resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
+ xml-parse-from-string@1.0.1:
+ resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==}
+
xml2js@0.4.23:
resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==}
engines: {node: '>=4.0.0'}
+ xml2js@0.5.0:
+ resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}
+ engines: {node: '>=4.0.0'}
+
xml2js@0.6.2:
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
engines: {node: '>=4.0.0'}
@@ -12907,6 +13112,18 @@ snapshots:
react-dom: 17.0.2(react@17.0.2)
stylis: 4.3.4
+ '@ant-design/cssinjs@1.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@emotion/hash': 0.8.0
+ '@emotion/unitless': 0.7.5
+ classnames: 2.5.1
+ csstype: 3.1.3
+ rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ stylis: 4.3.4
+
'@ant-design/fast-color@2.0.6':
dependencies:
'@babel/runtime': 7.26.0
@@ -12966,7 +13183,7 @@ snapshots:
'@ant-design/pro-provider@2.14.9(antd@5.24.4(date-fns@2.30.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
dependencies:
- '@ant-design/cssinjs': 1.22.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
+ '@ant-design/cssinjs': 1.23.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
'@babel/runtime': 7.26.0
'@ctrl/tinycolor': 3.6.1
antd: 5.24.4(date-fns@2.30.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
@@ -13115,6 +13332,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/helper-create-class-features-plugin@7.26.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-member-expression-to-functions': 7.25.9
+ '@babel/helper-optimise-call-expression': 7.25.9
+ '@babel/helper-replace-supers': 7.26.5(@babel/core@7.25.2)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ '@babel/traverse': 7.26.9
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-create-class-features-plugin@7.26.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13142,6 +13372,13 @@ snapshots:
regexpu-core: 5.3.2
semver: 6.3.1
+ '@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-annotate-as-pure': 7.25.9
+ regexpu-core: 6.2.0
+ semver: 6.3.1
+
'@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13171,6 +13408,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-plugin-utils': 7.26.5
+ debug: 4.4.3
+ lodash.debounce: 4.0.8
+ resolve: 1.22.8
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13220,6 +13468,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/helper-module-transforms@7.26.0(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13250,6 +13507,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-wrap-function': 7.25.9
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13268,6 +13534,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/helper-replace-supers@7.26.5(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-member-expression-to-functions': 7.25.9
+ '@babel/helper-optimise-call-expression': 7.25.9
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-replace-supers@7.26.5(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13348,6 +13623,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13361,6 +13644,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13371,6 +13659,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13385,6 +13678,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.25.2)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13402,6 +13704,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13418,11 +13728,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/plugin-proposal-export-default-from@7.24.7(@babel/core@7.26.9)':
+ '@babel/plugin-proposal-export-default-from@7.24.7(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.26.9
+ '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.26.5
- '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.26.9)
+ '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.25.2)
'@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.26.9)':
dependencies:
@@ -13472,9 +13782,9 @@ snapshots:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-export-default-from@7.24.7(@babel/core@7.26.9)':
+ '@babel/plugin-syntax-export-default-from@7.24.7(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.26.9
+ '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.25.2)':
@@ -13482,6 +13792,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13492,6 +13807,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13502,6 +13822,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13592,6 +13917,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13614,6 +13944,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13629,6 +13964,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-async-generator-functions@7.26.8(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.25.2)
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-async-generator-functions@7.26.8(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13647,6 +13991,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.25.2)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13661,6 +14014,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-block-scoped-functions@7.26.5(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-block-scoped-functions@7.26.5(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13671,6 +14029,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13684,6 +14047,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13701,6 +14072,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13721,6 +14100,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-classes@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-replace-supers': 7.26.5(@babel/core@7.25.2)
+ '@babel/traverse': 7.26.9
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13739,7 +14130,13 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/template': 7.25.0
- '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.9)':
+ '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/template': 7.26.9
+
+ '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.26.5
@@ -13750,6 +14147,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13761,6 +14163,12 @@ snapshots:
'@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2)
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13772,6 +14180,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13783,6 +14196,12 @@ snapshots:
'@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2)
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13795,6 +14214,11 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13808,6 +14232,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13819,11 +14248,22 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.26.5
+ '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.25.2)
+
'@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13838,6 +14278,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-for-of@7.26.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-for-of@7.26.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13855,6 +14303,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13870,6 +14327,11 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13880,6 +14342,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-literals@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13891,6 +14358,11 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.25.2)
+ '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13901,6 +14373,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13914,6 +14391,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13931,6 +14416,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13949,6 +14442,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.26.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13967,6 +14470,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13981,6 +14492,12 @@ snapshots:
'@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2)
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -13992,6 +14509,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14003,6 +14525,11 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-nullish-coalescing-operator@7.26.6(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-nullish-coalescing-operator@7.26.6(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14014,6 +14541,11 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.25.2)
+ '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14027,6 +14559,13 @@ snapshots:
'@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.25.2)
'@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.25.2)
+ '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.25.2)
+
'@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14042,6 +14581,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-replace-supers': 7.26.5(@babel/core@7.25.2)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14056,6 +14603,11 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
'@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14070,6 +14622,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14083,6 +14643,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14096,6 +14661,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14114,6 +14687,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14128,6 +14710,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14162,14 +14749,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.26.9)':
+ '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.26.9
+ '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.26.5
- '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.26.9)':
+ '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.26.9
+ '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.25.2)':
@@ -14212,12 +14799,24 @@ snapshots:
'@babel/helper-plugin-utils': 7.24.8
regenerator-transform: 0.15.2
+ '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ regenerator-transform: 0.15.2
+
'@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.26.5
regenerator-transform: 0.15.2
+ '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14229,11 +14828,28 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.26.5
+ '@babel/plugin-transform-runtime@7.26.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-plugin-utils': 7.26.5
+ babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.2)
+ babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.25.2)
+ babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.2)
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-runtime@7.26.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14251,6 +14867,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14264,6 +14885,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-spread@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14277,6 +14906,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14287,6 +14921,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-template-literals@7.26.8(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-template-literals@7.26.8(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14297,6 +14936,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-typeof-symbol@7.26.7(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-typeof-symbol@7.26.7(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14313,6 +14957,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/plugin-transform-typescript@7.26.8(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-annotate-as-pure': 7.25.9
+ '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.25.9
+ '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.25.2)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/plugin-transform-typescript@7.26.8(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14329,6 +14984,11 @@ snapshots:
'@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14340,6 +15000,12 @@ snapshots:
'@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2)
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14352,6 +15018,12 @@ snapshots:
'@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2)
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14364,6 +15036,12 @@ snapshots:
'@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.25.2)
'@babel/helper-plugin-utils': 7.24.8
+ '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.25.2)
+ '@babel/helper-plugin-utils': 7.26.5
+
'@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
@@ -14459,6 +15137,81 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/preset-env@7.26.9(@babel/core@7.25.2)':
+ dependencies:
+ '@babel/compat-data': 7.26.8
+ '@babel/core': 7.25.2
+ '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-plugin-utils': 7.26.5
+ '@babel/helper-validator-option': 7.25.9
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.25.2)
+ '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.25.2)
+ '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.25.2)
+ '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.25.2)
+ '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.25.2)
+ '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-block-scoped-functions': 7.26.5(@babel/core@7.25.2)
+ '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.25.2)
+ '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-exponentiation-operator': 7.26.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-for-of': 7.26.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.26.6(@babel/core@7.25.2)
+ '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.25.2)
+ '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-template-literals': 7.26.8(@babel/core@7.25.2)
+ '@babel/plugin-transform-typeof-symbol': 7.26.7(@babel/core@7.25.2)
+ '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.25.2)
+ babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.25.2)
+ babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.25.2)
+ babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.25.2)
+ core-js-compat: 3.41.0
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/preset-env@7.26.9(@babel/core@7.26.9)':
dependencies:
'@babel/compat-data': 7.26.8
@@ -14991,21 +15744,21 @@ snapshots:
'@docsearch/css@3.9.0': {}
- '@docsearch/react@3.9.0(@algolia/client-search@5.20.3)(@types/react@17.0.42)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)':
+ '@docsearch/react@3.9.0(@algolia/client-search@5.20.3)(@types/react@18.3.31)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)':
dependencies:
'@algolia/autocomplete-core': 1.17.9(@algolia/client-search@5.20.3)(algoliasearch@5.20.3)(search-insights@2.17.3)
'@algolia/autocomplete-preset-algolia': 1.17.9(@algolia/client-search@5.20.3)(algoliasearch@5.20.3)
'@docsearch/css': 3.9.0
algoliasearch: 5.20.3
optionalDependencies:
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
search-insights: 2.17.3
transitivePeerDependencies:
- '@algolia/client-search'
- '@docusaurus/babel@3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/babel@3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@babel/core': 7.26.9
'@babel/generator': 7.26.9
@@ -15018,7 +15771,7 @@ snapshots:
'@babel/runtime-corejs3': 7.26.9
'@babel/traverse': 7.26.9
'@docusaurus/logger': 3.7.0
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
babel-plugin-dynamic-import-node: 2.3.3
fs-extra: 11.3.0
tslib: 2.8.1
@@ -15032,14 +15785,14 @@ snapshots:
- uglify-js
- webpack-cli
- '@docusaurus/bundler@3.7.0(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/bundler@3.7.0(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
'@babel/core': 7.26.9
- '@docusaurus/babel': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/babel': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@docusaurus/cssnano-preset': 3.7.0
'@docusaurus/logger': 3.7.0
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0)
clean-css: 5.3.3
copy-webpack-plugin: 11.0.0(webpack@5.98.0)
@@ -15077,16 +15830,16 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/core@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/core@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/babel': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/bundler': 3.7.0(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/babel': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/bundler': 3.7.0(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
'@docusaurus/logger': 3.7.0
- '@docusaurus/mdx-loader': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@mdx-js/react': 3.1.0(@types/react@17.0.42)(react@19.0.0)
+ '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@mdx-js/react': 3.1.0(@types/react@18.3.31)(react@19.0.0)
boxen: 6.2.1
chalk: 4.1.2
chokidar: 3.6.0
@@ -15156,12 +15909,12 @@ snapshots:
chalk: 4.1.2
tslib: 2.8.1
- '@docusaurus/mdx-loader@3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/mdx-loader@3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@docusaurus/logger': 3.7.0
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@mdx-js/mdx': 3.1.0(acorn@8.15.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@mdx-js/mdx': 3.1.0(acorn@8.14.0)
'@slorber/remark-comment': 1.0.0
escape-html: 1.0.3
estree-util-value-to-estree: 3.3.2
@@ -15192,11 +15945,11 @@ snapshots:
- uglify-js
- webpack-cli
- '@docusaurus/module-type-aliases@3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/module-type-aliases@3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@types/history': 4.7.11
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
'@types/react-router-config': 5.0.11
'@types/react-router-dom': 5.3.3
react: 19.0.0
@@ -15211,17 +15964,17 @@ snapshots:
- uglify-js
- webpack-cli
- '@docusaurus/plugin-content-blog@3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-content-blog@3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
'@docusaurus/logger': 3.7.0
- '@docusaurus/mdx-loader': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
cheerio: 1.0.0-rc.12
feed: 4.2.2
fs-extra: 11.3.0
@@ -15255,17 +16008,17 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
'@docusaurus/logger': 3.7.0
- '@docusaurus/mdx-loader': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/module-type-aliases': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/module-type-aliases': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@types/react-router-config': 5.0.11
combine-promises: 1.2.0
fs-extra: 11.3.0
@@ -15297,13 +16050,13 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-content-pages@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-content-pages@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/mdx-loader': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
fs-extra: 11.3.0
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -15330,11 +16083,11 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-debug@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-debug@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
fs-extra: 11.3.0
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -15361,11 +16114,11 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-google-analytics@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-google-analytics@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
tslib: 2.7.0
@@ -15390,11 +16143,11 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-google-gtag@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-google-gtag@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@types/gtag.js': 0.0.12
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -15420,11 +16173,11 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-google-tag-manager@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-google-tag-manager@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
tslib: 2.7.0
@@ -15449,14 +16202,14 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-sitemap@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-sitemap@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
'@docusaurus/logger': 3.7.0
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
fs-extra: 11.3.0
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -15483,12 +16236,12 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/plugin-svgr@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/plugin-svgr@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@svgr/core': 8.1.0(typescript@5.6.3)
'@svgr/webpack': 8.1.0(typescript@5.6.3)
react: 19.0.0
@@ -15516,22 +16269,22 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/preset-classic@3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(@types/react@17.0.42)(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)':
- dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-content-blog': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-content-pages': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-debug': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-google-analytics': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-google-gtag': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-google-tag-manager': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-sitemap': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-svgr': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/theme-classic': 3.7.0(@types/react@17.0.42)(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/theme-search-algolia': 3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(@types/react@17.0.42)(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/preset-classic@3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(@types/react@18.3.31)(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)':
+ dependencies:
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-content-blog': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-content-pages': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-debug': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-google-analytics': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-google-gtag': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-google-tag-manager': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-sitemap': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-svgr': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/theme-classic': 3.7.0(@types/react@18.3.31)(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/theme-search-algolia': 3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(@types/react@18.3.31)(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
transitivePeerDependencies:
@@ -15560,25 +16313,25 @@ snapshots:
'@docusaurus/react-loadable@6.0.0(react@19.0.0)':
dependencies:
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
react: 19.0.0
- '@docusaurus/theme-classic@3.7.0(@types/react@17.0.42)(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
+ '@docusaurus/theme-classic@3.7.0(@types/react@18.3.31)(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)':
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
'@docusaurus/logger': 3.7.0
- '@docusaurus/mdx-loader': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/module-type-aliases': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/plugin-content-blog': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/plugin-content-pages': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/module-type-aliases': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/plugin-content-blog': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/plugin-content-pages': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@docusaurus/theme-translations': 3.7.0
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@mdx-js/react': 3.1.0(@types/react@17.0.42)(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@mdx-js/react': 3.1.0(@types/react@18.3.31)(react@19.0.0)
clsx: 2.1.1
copy-text-to-clipboard: 3.2.0
infima: 0.2.0-alpha.45
@@ -15614,15 +16367,15 @@ snapshots:
- vue-template-compiler
- webpack-cli
- '@docusaurus/theme-common@3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/theme-common@3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@docusaurus/mdx-loader': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/module-type-aliases': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/mdx-loader': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/module-type-aliases': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@types/history': 4.7.11
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
'@types/react-router-config': 5.0.11
clsx: 2.1.1
parse-numeric-range: 1.3.0
@@ -15639,16 +16392,16 @@ snapshots:
- uglify-js
- webpack-cli
- '@docusaurus/theme-search-algolia@3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(@types/react@17.0.42)(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)':
+ '@docusaurus/theme-search-algolia@3.7.0(@algolia/client-search@5.20.3)(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(@types/react@18.3.31)(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)(typescript@5.6.3)':
dependencies:
- '@docsearch/react': 3.9.0(@algolia/client-search@5.20.3)(@types/react@17.0.42)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docsearch/react': 3.9.0(@algolia/client-search@5.20.3)(@types/react@18.3.31)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
'@docusaurus/logger': 3.7.0
- '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
- '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/plugin-content-docs': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/theme-common': 3.7.0(@docusaurus/plugin-content-docs@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@docusaurus/theme-translations': 3.7.0
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-validation': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-validation': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
algoliasearch: 5.20.3
algoliasearch-helper: 3.24.1(algoliasearch@5.20.3)
clsx: 2.1.1
@@ -15690,11 +16443,11 @@ snapshots:
'@docusaurus/tsconfig@3.7.0': {}
- '@docusaurus/types@3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/types@3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@mdx-js/mdx': 3.1.0(acorn@8.15.0)
+ '@mdx-js/mdx': 3.1.0(acorn@8.14.0)
'@types/history': 4.7.11
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
commander: 5.1.0
joi: 17.13.3
react: 19.0.0
@@ -15711,9 +16464,9 @@ snapshots:
- uglify-js
- webpack-cli
- '@docusaurus/utils-common@3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/utils-common@3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
tslib: 2.8.1
transitivePeerDependencies:
- '@swc/core'
@@ -15725,11 +16478,11 @@ snapshots:
- uglify-js
- webpack-cli
- '@docusaurus/utils-validation@3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/utils-validation@3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@docusaurus/logger': 3.7.0
- '@docusaurus/utils': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
fs-extra: 11.3.0
joi: 17.13.3
js-yaml: 4.1.0
@@ -15745,11 +16498,11 @@ snapshots:
- uglify-js
- webpack-cli
- '@docusaurus/utils@3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
+ '@docusaurus/utils@3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@docusaurus/logger': 3.7.0
- '@docusaurus/types': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
- '@docusaurus/utils-common': 3.7.0(acorn@8.15.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/types': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ '@docusaurus/utils-common': 3.7.0(acorn@8.14.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
escape-string-regexp: 4.0.0
file-loader: 6.2.0(webpack@5.98.0)
fs-extra: 11.3.0
@@ -16065,9 +16818,7 @@ snapshots:
source-map: 0.6.1
string-length: 2.0.0
transitivePeerDependencies:
- - bufferutil
- supports-color
- - utf-8-validate
'@jest/schemas@29.6.3':
dependencies:
@@ -16164,7 +16915,7 @@ snapshots:
'@leichtgewicht/ip-codec@2.0.5': {}
- '@mdx-js/mdx@3.1.0(acorn@8.15.0)':
+ '@mdx-js/mdx@3.1.0(acorn@8.14.0)':
dependencies:
'@types/estree': 1.0.6
'@types/estree-jsx': 1.0.5
@@ -16178,7 +16929,7 @@ snapshots:
hast-util-to-jsx-runtime: 2.3.5
markdown-extensions: 2.0.0
recma-build-jsx: 1.0.0
- recma-jsx: 1.0.0(acorn@8.15.0)
+ recma-jsx: 1.0.0(acorn@8.14.0)
recma-stringify: 1.0.0
rehype-recma: 1.0.0
remark-mdx: 3.1.0
@@ -16194,10 +16945,10 @@ snapshots:
- acorn
- supports-color
- '@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0)':
+ '@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0)':
dependencies:
'@types/mdx': 2.0.13
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
react: 19.0.0
'@monaco-editor/loader@1.4.0(monaco-editor@0.52.0)':
@@ -16319,12 +17070,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@nestjs/schematics@6.9.4(typescript@4.1.6)':
+ '@nestjs/schematics@6.9.4(typescript@4.6.2)':
dependencies:
'@angular-devkit/core': 7.3.8
'@angular-devkit/schematics': 7.3.8
fs-extra: 8.1.0
- typescript: 4.1.6
+ typescript: 4.6.2
transitivePeerDependencies:
- supports-color
@@ -16657,110 +17408,110 @@ snapshots:
dependencies:
joi: 17.13.3
- '@react-native-community/cli@14.1.0(typescript@4.6.2)':
- dependencies:
- '@react-native-community/cli-clean': 14.1.0
- '@react-native-community/cli-config': 14.1.0(typescript@4.6.2)
- '@react-native-community/cli-debugger-ui': 14.1.0
- '@react-native-community/cli-doctor': 14.1.0(typescript@4.6.2)
- '@react-native-community/cli-server-api': 14.1.0
- '@react-native-community/cli-tools': 14.1.0
- '@react-native-community/cli-types': 14.1.0
- chalk: 4.1.2
- commander: 9.5.0
- deepmerge: 4.3.1
- execa: 5.1.1
- find-up: 5.0.0
- fs-extra: 8.1.0
- graceful-fs: 4.2.11
- prompts: 2.4.2
- semver: 7.6.3
- transitivePeerDependencies:
- - bufferutil
- - supports-color
- - typescript
- - utf-8-validate
-
- '@react-native/assets-registry@0.75.3': {}
-
- '@react-native/babel-plugin-codegen@0.75.3(@babel/preset-env@7.26.9(@babel/core@7.26.9))':
- dependencies:
- '@react-native/codegen': 0.75.3(@babel/preset-env@7.26.9(@babel/core@7.26.9))
- transitivePeerDependencies:
- - '@babel/preset-env'
- - supports-color
-
- '@react-native/babel-preset@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))':
- dependencies:
- '@babel/core': 7.26.9
- '@babel/plugin-proposal-export-default-from': 7.24.7(@babel/core@7.26.9)
- '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.26.9)
- '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.26.9)
- '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.26.9)
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.9)
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.9)
- '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.26.9)
- '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.26.9)
- '@babel/plugin-transform-for-of': 7.26.9(@babel/core@7.26.9)
- '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.9)
- '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-nullish-coalescing-operator': 7.26.6(@babel/core@7.26.9)
- '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.26.9)
- '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.26.9)
- '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-runtime': 7.26.9(@babel/core@7.26.9)
- '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.9)
- '@babel/plugin-transform-typescript': 7.26.8(@babel/core@7.26.9)
- '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.9)
+ '@react-native-community/cli@14.1.0(typescript@4.6.2)':
+ dependencies:
+ '@react-native-community/cli-clean': 14.1.0
+ '@react-native-community/cli-config': 14.1.0(typescript@4.6.2)
+ '@react-native-community/cli-debugger-ui': 14.1.0
+ '@react-native-community/cli-doctor': 14.1.0(typescript@4.6.2)
+ '@react-native-community/cli-server-api': 14.1.0
+ '@react-native-community/cli-tools': 14.1.0
+ '@react-native-community/cli-types': 14.1.0
+ chalk: 4.1.2
+ commander: 9.5.0
+ deepmerge: 4.3.1
+ execa: 5.1.1
+ find-up: 5.0.0
+ fs-extra: 8.1.0
+ graceful-fs: 4.2.11
+ prompts: 2.4.2
+ semver: 7.6.3
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - typescript
+ - utf-8-validate
+
+ '@react-native/assets-registry@0.75.3': {}
+
+ '@react-native/babel-plugin-codegen@0.75.3(@babel/preset-env@7.26.9(@babel/core@7.25.2))':
+ dependencies:
+ '@react-native/codegen': 0.75.3(@babel/preset-env@7.26.9(@babel/core@7.25.2))
+ transitivePeerDependencies:
+ - '@babel/preset-env'
+ - supports-color
+
+ '@react-native/babel-preset@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))':
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/plugin-proposal-export-default-from': 7.24.7(@babel/core@7.25.2)
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.25.2)
+ '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.25.2)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.25.2)
+ '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.25.2)
+ '@babel/plugin-transform-for-of': 7.26.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.25.2)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.26.6(@babel/core@7.25.2)
+ '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2)
+ '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2)
+ '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-runtime': 7.26.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.25.2)
+ '@babel/plugin-transform-typescript': 7.26.8(@babel/core@7.25.2)
+ '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.25.2)
'@babel/template': 7.26.9
- '@react-native/babel-plugin-codegen': 0.75.3(@babel/preset-env@7.26.9(@babel/core@7.26.9))
- babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.26.9)
+ '@react-native/babel-plugin-codegen': 0.75.3(@babel/preset-env@7.26.9(@babel/core@7.25.2))
+ babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.25.2)
react-refresh: 0.14.2
transitivePeerDependencies:
- '@babel/preset-env'
- supports-color
- '@react-native/codegen@0.75.3(@babel/preset-env@7.26.9(@babel/core@7.26.9))':
+ '@react-native/codegen@0.75.3(@babel/preset-env@7.26.9(@babel/core@7.25.2))':
dependencies:
'@babel/parser': 7.26.9
- '@babel/preset-env': 7.26.9(@babel/core@7.26.9)
+ '@babel/preset-env': 7.26.9(@babel/core@7.25.2)
glob: 7.2.3
hermes-parser: 0.22.0
invariant: 2.2.4
- jscodeshift: 0.14.0(@babel/preset-env@7.26.9(@babel/core@7.26.9))
+ jscodeshift: 0.14.0(@babel/preset-env@7.26.9(@babel/core@7.25.2))
mkdirp: 0.5.6
nullthrows: 1.1.1
yargs: 17.7.2
transitivePeerDependencies:
- supports-color
- '@react-native/community-cli-plugin@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(encoding@0.1.13)':
+ '@react-native/community-cli-plugin@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(encoding@0.1.13)':
dependencies:
'@react-native-community/cli-server-api': 14.1.0
'@react-native-community/cli-tools': 14.1.0
'@react-native/dev-middleware': 0.75.3(encoding@0.1.13)
- '@react-native/metro-babel-transformer': 0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))
+ '@react-native/metro-babel-transformer': 0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))
chalk: 4.1.2
execa: 5.1.1
metro: 0.80.12
@@ -16802,10 +17553,10 @@ snapshots:
'@react-native/js-polyfills@0.75.3': {}
- '@react-native/metro-babel-transformer@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))':
+ '@react-native/metro-babel-transformer@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))':
dependencies:
- '@babel/core': 7.26.9
- '@react-native/babel-preset': 0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))
+ '@babel/core': 7.25.2
+ '@react-native/babel-preset': 0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))
hermes-parser: 0.22.0
nullthrows: 1.1.1
transitivePeerDependencies:
@@ -16814,12 +17565,12 @@ snapshots:
'@react-native/normalize-colors@0.75.3': {}
- '@react-native/virtualized-lists@0.75.3(@types/react@17.0.42)(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)':
+ '@react-native/virtualized-lists@0.75.3(@types/react@17.0.42)(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)':
dependencies:
invariant: 2.2.4
nullthrows: 1.1.1
react: 17.0.2
- react-native: 0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2)
+ react-native: 0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2)
optionalDependencies:
'@types/react': 17.0.42
@@ -16846,14 +17597,14 @@ snapshots:
react: 17.0.2
react-konva: 18.2.10(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
- '@react-spring/native@9.7.4(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)':
+ '@react-spring/native@9.7.4(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)':
dependencies:
'@react-spring/animated': 9.7.4(react@17.0.2)
'@react-spring/core': 9.7.4(react@17.0.2)
'@react-spring/shared': 9.7.4(react@17.0.2)
'@react-spring/types': 9.7.4
react: 17.0.2
- react-native: 0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2)
+ react-native: 0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2)
'@react-spring/rafz@9.7.4': {}
@@ -16863,13 +17614,13 @@ snapshots:
'@react-spring/types': 9.7.4
react: 17.0.2
- '@react-spring/three@9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(react@17.0.2)(three@0.168.0)':
+ '@react-spring/three@9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(react@17.0.2)(three@0.168.0)':
dependencies:
'@react-spring/animated': 9.7.4(react@17.0.2)
'@react-spring/core': 9.7.4(react@17.0.2)
'@react-spring/shared': 9.7.4(react@17.0.2)
'@react-spring/types': 9.7.4
- '@react-three/fiber': 8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0)
+ '@react-three/fiber': 8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0)
react: 17.0.2
three: 0.168.0
@@ -16895,7 +17646,7 @@ snapshots:
react-zdog: 1.2.2
zdog: 1.1.3
- '@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0)':
+ '@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0)':
dependencies:
'@babel/runtime': 7.26.0
'@types/debounce': 1.2.4
@@ -16913,7 +17664,7 @@ snapshots:
zustand: 3.7.2(react@17.0.2)
optionalDependencies:
react-dom: 17.0.2(react@17.0.2)
- react-native: 0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2)
+ react-native: 0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2)
'@rollup/plugin-babel@5.3.1(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@2.79.1)':
dependencies:
@@ -17110,6 +17861,13 @@ snapshots:
dependencies:
defer-to-connect: 2.0.1
+ '@tanstack/query-core@5.101.0': {}
+
+ '@tanstack/react-query@5.101.0(react@18.3.1)':
+ dependencies:
+ '@tanstack/query-core': 5.101.0
+ react: 18.3.1
+
'@testing-library/dom@10.4.1':
dependencies:
'@babel/code-frame': 7.26.2
@@ -17130,7 +17888,7 @@ snapshots:
picocolors: 1.1.1
redent: 3.0.0
- '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react@17.0.42)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
+ '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@17.0.42))(@types/react@17.0.42)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
dependencies:
'@babel/runtime': 7.26.0
'@testing-library/dom': 10.4.1
@@ -17138,6 +17896,7 @@ snapshots:
react-dom: 17.0.2(react@17.0.2)
optionalDependencies:
'@types/react': 17.0.42
+ '@types/react-dom': 18.3.7(@types/react@17.0.42)
'@tootallnate/quickjs-emscripten@0.23.0': {}
@@ -17322,34 +18081,43 @@ snapshots:
'@types/range-parser@1.2.7': {}
- '@types/react-infinite-scroller@1.2.5':
+ '@types/react-dom@18.3.7(@types/react@17.0.42)':
dependencies:
'@types/react': 17.0.42
+ optional: true
+
+ '@types/react-dom@18.3.7(@types/react@18.3.31)':
+ dependencies:
+ '@types/react': 18.3.31
+
+ '@types/react-infinite-scroller@1.2.5':
+ dependencies:
+ '@types/react': 18.3.31
'@types/react-reconciler@0.26.7':
dependencies:
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
'@types/react-reconciler@0.28.8':
dependencies:
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
'@types/react-router-config@5.0.11':
dependencies:
'@types/history': 4.7.11
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
'@types/react-router': 5.1.20
'@types/react-router-dom@5.3.3':
dependencies:
'@types/history': 4.7.11
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
'@types/react-router': 5.1.20
'@types/react-router@5.1.20':
dependencies:
'@types/history': 4.7.11
- '@types/react': 17.0.42
+ '@types/react': 18.3.31
'@types/react@17.0.42':
dependencies:
@@ -17357,6 +18125,11 @@ snapshots:
'@types/scheduler': 0.23.0
csstype: 3.1.3
+ '@types/react@18.3.31':
+ dependencies:
+ '@types/prop-types': 15.7.13
+ csstype: 3.2.3
+
'@types/resolve@1.17.1':
dependencies:
'@types/node': 17.0.22
@@ -17479,22 +18252,22 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.1.6))(eslint@8.57.1)(typescript@4.1.6)':
+ '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.6.2))(eslint@8.57.1)(typescript@4.6.2)':
dependencies:
'@eslint-community/regexpp': 4.11.1
- '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.1.6)
+ '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.6.2)
'@typescript-eslint/scope-manager': 5.62.0
- '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.1)(typescript@4.1.6)
- '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@4.1.6)
+ '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.1)(typescript@4.6.2)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@4.6.2)
debug: 4.3.7(supports-color@9.4.0)
eslint: 8.57.1
graphemer: 1.4.0
ignore: 5.3.2
natural-compare-lite: 1.4.0
semver: 7.6.3
- tsutils: 3.21.0(typescript@4.1.6)
+ tsutils: 3.21.0(typescript@4.6.2)
optionalDependencies:
- typescript: 4.1.6
+ typescript: 4.6.2
transitivePeerDependencies:
- supports-color
@@ -17510,15 +18283,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.1.6)':
+ '@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.6.2)':
dependencies:
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
- '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.1.6)
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.6.2)
debug: 4.3.7(supports-color@9.4.0)
eslint: 8.57.1
optionalDependencies:
- typescript: 4.1.6
+ typescript: 4.6.2
transitivePeerDependencies:
- supports-color
@@ -17539,34 +18312,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/type-utils@5.62.0(eslint@8.57.1)(typescript@4.1.6)':
+ '@typescript-eslint/type-utils@5.62.0(eslint@8.57.1)(typescript@4.6.2)':
dependencies:
- '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.1.6)
- '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@4.1.6)
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.6.2)
+ '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@4.6.2)
debug: 4.4.3
eslint: 8.57.1
- tsutils: 3.21.0(typescript@4.1.6)
+ tsutils: 3.21.0(typescript@4.6.2)
optionalDependencies:
- typescript: 4.1.6
+ typescript: 4.6.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@5.62.0': {}
- '@typescript-eslint/typescript-estree@5.62.0(typescript@4.1.6)':
- dependencies:
- '@typescript-eslint/types': 5.62.0
- '@typescript-eslint/visitor-keys': 5.62.0
- debug: 4.4.3
- globby: 11.1.0
- is-glob: 4.0.3
- semver: 7.6.3
- tsutils: 3.21.0(typescript@4.1.6)
- optionalDependencies:
- typescript: 4.1.6
- transitivePeerDependencies:
- - supports-color
-
'@typescript-eslint/typescript-estree@5.62.0(typescript@4.6.2)':
dependencies:
'@typescript-eslint/types': 5.62.0
@@ -17596,14 +18355,14 @@ snapshots:
- supports-color
- typescript
- '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@4.1.6)':
+ '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@4.6.2)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
'@types/json-schema': 7.0.15
'@types/semver': 7.5.8
'@typescript-eslint/scope-manager': 5.62.0
'@typescript-eslint/types': 5.62.0
- '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.1.6)
+ '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.6.2)
eslint: 8.57.1
eslint-scope: 5.1.1
semver: 7.6.3
@@ -18194,6 +18953,8 @@ snapshots:
is-array-buffer: 3.0.4
is-shared-array-buffer: 1.0.3
+ arrify@1.0.1: {}
+
asap@2.0.6: {}
asn1.js@4.10.1:
@@ -18315,9 +19076,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- babel-loader@8.4.1(@babel/core@7.26.9)(webpack@5.98.0):
+ babel-loader@8.4.1(@babel/core@7.25.2)(webpack@5.98.0):
dependencies:
- '@babel/core': 7.26.9
+ '@babel/core': 7.25.2
find-cache-dir: 3.3.2
loader-utils: 2.0.4
make-dir: 3.1.0
@@ -18382,6 +19143,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.25.2):
+ dependencies:
+ '@babel/core': 7.25.2
+ '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.25.2)
+ core-js-compat: 3.41.0
+ transitivePeerDependencies:
+ - supports-color
+
babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.26.9):
dependencies:
'@babel/core': 7.26.9
@@ -18404,9 +19173,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.26.9):
+ babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.25.2):
dependencies:
- '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.26.9)
+ '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.25.2)
transitivePeerDependencies:
- '@babel/core'
@@ -18451,6 +19220,8 @@ snapshots:
big.js@5.2.2: {}
+ bignumber.js@2.4.0: {}
+
binary-extensions@1.13.1: {}
binary-extensions@2.3.0: {}
@@ -18470,6 +19241,10 @@ snapshots:
bluebird@3.7.2: {}
+ bmp-js@0.0.1: {}
+
+ bmp-js@0.0.3: {}
+
bn.js@4.12.3: {}
bn.js@5.2.3: {}
@@ -18668,8 +19443,19 @@ snapshots:
dependencies:
node-int64: 0.4.0
+ buffer-alloc-unsafe@1.1.0: {}
+
+ buffer-alloc@1.2.0:
+ dependencies:
+ buffer-alloc-unsafe: 1.1.0
+ buffer-fill: 1.0.0
+
buffer-equal-constant-time@1.0.1: {}
+ buffer-equal@0.0.1: {}
+
+ buffer-fill@1.0.0: {}
+
buffer-from@1.1.2: {}
buffer-xor@1.0.3: {}
@@ -18822,6 +19608,12 @@ snapshots:
ccount@2.0.1: {}
+ centra@2.7.0:
+ dependencies:
+ follow-redirects: 1.15.9(debug@4.3.7)
+ transitivePeerDependencies:
+ - debug
+
chalk@1.1.3:
dependencies:
ansi-styles: 2.2.1
@@ -19607,6 +20399,8 @@ snapshots:
csstype@3.1.3: {}
+ csstype@3.2.3: {}
+
culvert@0.1.2: {}
cxs@6.2.0: {}
@@ -19875,9 +20669,9 @@ snapshots:
dependencies:
esutils: 2.0.3
- docusaurus-plugin-sass@0.2.6(@docusaurus/core@3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(sass@1.79.3)(webpack@5.98.0):
+ docusaurus-plugin-sass@0.2.6(@docusaurus/core@3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3))(sass@1.79.3)(webpack@5.98.0):
dependencies:
- '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@17.0.42)(react@19.0.0))(acorn@8.15.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
+ '@docusaurus/core': 3.7.0(@mdx-js/react@3.1.0(@types/react@18.3.31)(react@19.0.0))(acorn@8.14.0)(eslint@9.36.0(jiti@1.21.7))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.6.3)
sass: 1.79.3
sass-loader: 16.0.5(sass@1.79.3)(webpack@5.98.0)
transitivePeerDependencies:
@@ -19912,6 +20706,8 @@ snapshots:
domhandler: 5.0.3
entities: 4.5.0
+ dom-walk@0.1.2: {}
+
domain-browser@1.2.0: {}
domelementtype@2.3.0: {}
@@ -20290,7 +21086,7 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
- eslint-config-next@12.1.0(eslint@8.11.0)(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(typescript@4.6.2):
+ eslint-config-next@12.1.0(eslint@8.11.0)(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(typescript@4.6.2):
dependencies:
'@next/eslint-plugin-next': 12.1.0
'@rushstack/eslint-patch': 1.10.4
@@ -20302,7 +21098,7 @@ snapshots:
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.11.0)
eslint-plugin-react: 7.36.1(eslint@8.11.0)
eslint-plugin-react-hooks: 4.6.2(eslint@8.11.0)
- next: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ next: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
optionalDependencies:
typescript: 4.6.2
transitivePeerDependencies:
@@ -20329,7 +21125,7 @@ snapshots:
dependencies:
debug: 4.4.3
eslint: 8.11.0
- eslint-plugin-import: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.11.0)(typescript@4.6.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.11.0)
+ eslint-plugin-import: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.11.0)(typescript@4.6.2))(eslint@8.11.0)
glob: 7.2.3
is-glob: 4.0.3
resolve: 1.22.8
@@ -20348,11 +21144,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.1.6))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1):
+ eslint-module-utils@2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.6.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
- '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.1.6)
+ '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.6.2)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
@@ -20386,7 +21182,35 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-import@2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.1.6))(eslint@8.57.1):
+ eslint-plugin-import@2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.11.0)(typescript@4.6.2))(eslint@8.11.0):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.8
+ array.prototype.findlastindex: 1.2.5
+ array.prototype.flat: 1.3.2
+ array.prototype.flatmap: 1.3.2
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 8.11.0
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.11.0)(typescript@4.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@2.7.1)(eslint@8.11.0)
+ hasown: 2.0.2
+ is-core-module: 2.15.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.0
+ semver: 6.3.1
+ tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 5.62.0(eslint@8.11.0)(typescript@4.6.2)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-import@2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.6.2))(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -20397,7 +21221,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.1.6))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1)
+ eslint-module-utils: 2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.6.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@@ -20408,7 +21232,7 @@ snapshots:
semver: 6.3.1
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.1.6)
+ '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.6.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@@ -20769,6 +21593,8 @@ snapshots:
signal-exit: 3.0.7
strip-final-newline: 2.0.0
+ exif-parser@0.1.12: {}
+
exit@0.1.2: {}
expand-brackets@2.1.4:
@@ -21040,6 +21866,8 @@ snapshots:
schema-utils: 3.3.0
webpack: 5.98.0
+ file-type@3.9.0: {}
+
file-uri-to-path@1.0.0:
optional: true
@@ -21346,6 +22174,11 @@ snapshots:
get-ready@1.0.0: {}
+ get-stream@2.3.1:
+ dependencies:
+ object-assign: 4.1.1
+ pinkie-promise: 2.0.1
+
get-stream@4.1.0:
dependencies:
pump: 3.0.2
@@ -21429,6 +22262,11 @@ snapshots:
kind-of: 6.0.3
which: 1.3.1
+ global@4.4.0:
+ dependencies:
+ min-document: 2.19.2
+ process: 0.11.10
+
globals@11.12.0: {}
globals@13.24.0:
@@ -21934,8 +22772,7 @@ snapshots:
ignore@5.3.2: {}
- image-size@0.5.5:
- optional: true
+ image-size@0.5.5: {}
image-size@1.1.1:
dependencies:
@@ -22080,6 +22917,8 @@ snapshots:
ip-address@10.2.0: {}
+ ip-regex@1.0.3: {}
+
ipaddr.js@1.9.1: {}
ipaddr.js@2.2.0: {}
@@ -22192,6 +23031,8 @@ snapshots:
is-fullwidth-code-point@4.0.0: {}
+ is-function@1.0.2: {}
+
is-generator-fn@2.1.0: {}
is-generator-function@1.0.10:
@@ -22556,9 +23397,7 @@ snapshots:
pretty-format: 24.9.0
throat: 4.1.0
transitivePeerDependencies:
- - bufferutil
- supports-color
- - utf-8-validate
jest-leak-detector@24.9.0:
dependencies:
@@ -22794,6 +23633,27 @@ snapshots:
- supports-color
- utf-8-validate
+ jimp@0.2.28:
+ dependencies:
+ bignumber.js: 2.4.0
+ bmp-js: 0.0.3
+ es6-promise: 3.3.1
+ exif-parser: 0.1.12
+ file-type: 3.9.0
+ jpeg-js: 0.2.0
+ load-bmfont: 1.4.2
+ mime: 1.6.0
+ mkdirp: 0.5.1
+ pixelmatch: 4.0.2
+ pngjs: 3.4.0
+ read-chunk: 1.0.1
+ request: 2.88.2
+ stream-to-buffer: 0.1.0
+ tinycolor2: 1.6.0
+ url-regex: 3.2.0
+ transitivePeerDependencies:
+ - debug
+
jiti@1.21.7: {}
joi@17.13.3:
@@ -22804,6 +23664,10 @@ snapshots:
'@sideway/formula': 3.0.1
'@sideway/pinpoint': 2.0.0
+ jpeg-js@0.1.2: {}
+
+ jpeg-js@0.2.0: {}
+
js-base64@2.6.4: {}
js-git@0.7.8:
@@ -22832,7 +23696,7 @@ snapshots:
jsc-safe-url@0.2.4: {}
- jscodeshift@0.14.0(@babel/preset-env@7.26.9(@babel/core@7.26.9)):
+ jscodeshift@0.14.0(@babel/preset-env@7.26.9(@babel/core@7.25.2)):
dependencies:
'@babel/core': 7.26.9
'@babel/parser': 7.26.9
@@ -22840,7 +23704,7 @@ snapshots:
'@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.26.9)
'@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.26.9)
'@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.9)
- '@babel/preset-env': 7.26.9(@babel/core@7.26.9)
+ '@babel/preset-env': 7.26.9(@babel/core@7.25.2)
'@babel/preset-flow': 7.24.7(@babel/core@7.26.9)
'@babel/preset-typescript': 7.26.0(@babel/core@7.26.9)
'@babel/register': 7.24.6(@babel/core@7.26.9)
@@ -23121,6 +23985,19 @@ snapshots:
optionalDependencies:
enquirer: 2.3.6
+ load-bmfont@1.4.2:
+ dependencies:
+ buffer-equal: 0.0.1
+ mime: 1.6.0
+ parse-bmfont-ascii: 1.0.6
+ parse-bmfont-binary: 1.0.6
+ parse-bmfont-xml: 1.1.6
+ phin: 3.7.1
+ xhr: 2.6.0
+ xtend: 4.0.2
+ transitivePeerDependencies:
+ - debug
+
load-json-file@4.0.0:
dependencies:
graceful-fs: 4.2.11
@@ -24087,6 +24964,10 @@ snapshots:
mimic-response@4.0.0: {}
+ min-document@2.19.2:
+ dependencies:
+ dom-walk: 0.1.2
+
min-indent@1.0.1: {}
mini-css-extract-plugin@2.9.2(webpack@5.98.0):
@@ -24111,6 +24992,8 @@ snapshots:
dependencies:
brace-expansion: 2.0.1
+ minimist@0.0.8: {}
+
minimist@1.2.0: {}
minimist@1.2.8: {}
@@ -24133,6 +25016,10 @@ snapshots:
for-in: 1.0.2
is-extendable: 1.0.1
+ mkdirp@0.5.1:
+ dependencies:
+ minimist: 0.0.8
+
mkdirp@0.5.6:
dependencies:
minimist: 1.2.8
@@ -24279,9 +25166,9 @@ snapshots:
url-loader: 4.1.1(file-loader@6.2.0(webpack@5.98.0))(webpack@5.98.0)
webpack: 5.98.0
- next-intl@1.5.1(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(react@17.0.2):
+ next-intl@1.5.1(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(react@17.0.2):
dependencies:
- next: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ next: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
react: 17.0.2
use-intl: 1.5.1(react@17.0.2)
@@ -24292,12 +25179,12 @@ snapshots:
react-dom: 17.0.2(react@17.0.2)
react-transition-group: 2.9.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
- next-pwa@5.6.0(@babel/core@7.26.9)(@types/babel__core@7.20.5)(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(webpack@5.98.0):
+ next-pwa@5.6.0(@babel/core@7.25.2)(@types/babel__core@7.20.5)(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3))(webpack@5.98.0):
dependencies:
- babel-loader: 8.4.1(@babel/core@7.26.9)(webpack@5.98.0)
+ babel-loader: 8.4.1(@babel/core@7.25.2)(webpack@5.98.0)
clean-webpack-plugin: 4.0.0(webpack@5.98.0)
globby: 11.1.0
- next: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ next: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
terser-webpack-plugin: 5.3.10(webpack@5.98.0)
workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.98.0)
workbox-window: 6.6.0
@@ -24310,11 +25197,11 @@ snapshots:
- uglify-js
- webpack
- next-sitemap@1.9.12(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)):
+ next-sitemap@1.9.12(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)):
dependencies:
'@corex/deepmerge': 2.6.148
minimist: 1.2.8
- next: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ next: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
next-tick@1.1.0: {}
@@ -24323,14 +25210,14 @@ snapshots:
enhanced-resolve: 5.17.1
escalade: 3.2.0
- next-with-less@2.0.5(less-loader@10.2.0(less@4.2.0)(webpack@5.98.0))(less@4.2.0)(next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)):
+ next-with-less@2.0.5(less-loader@10.2.0(less@4.2.0)(webpack@5.98.0))(less@4.2.0)(next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)):
dependencies:
clone-deep: 4.0.1
less: 4.2.0
less-loader: 10.2.0(less@4.2.0)(webpack@5.98.0)
- next: 12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
+ next: 12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3)
- next@12.3.4(@babel/core@7.26.9)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3):
+ next@12.3.4(@babel/core@7.25.2)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(sass@1.79.3):
dependencies:
'@next/env': 12.3.4
'@swc/helpers': 0.4.11
@@ -24338,7 +25225,7 @@ snapshots:
postcss: 8.4.14
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- styled-jsx: 5.0.7(@babel/core@7.26.9)(react@17.0.2)
+ styled-jsx: 5.0.7(@babel/core@7.25.2)(react@17.0.2)
use-sync-external-store: 1.2.0(react@17.0.2)
optionalDependencies:
'@next/swc-android-arm-eabi': 12.3.4
@@ -24359,6 +25246,35 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
+ next@12.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.3):
+ dependencies:
+ '@next/env': 12.3.4
+ '@swc/helpers': 0.4.11
+ caniuse-lite: 1.0.30001701
+ postcss: 8.4.14
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ styled-jsx: 5.0.7(react@18.3.1)
+ use-sync-external-store: 1.2.0(react@18.3.1)
+ optionalDependencies:
+ '@next/swc-android-arm-eabi': 12.3.4
+ '@next/swc-android-arm64': 12.3.4
+ '@next/swc-darwin-arm64': 12.3.4
+ '@next/swc-darwin-x64': 12.3.4
+ '@next/swc-freebsd-x64': 12.3.4
+ '@next/swc-linux-arm-gnueabihf': 12.3.4
+ '@next/swc-linux-arm64-gnu': 12.3.4
+ '@next/swc-linux-arm64-musl': 12.3.4
+ '@next/swc-linux-x64-gnu': 12.3.4
+ '@next/swc-linux-x64-musl': 12.3.4
+ '@next/swc-win32-arm64-msvc': 12.3.4
+ '@next/swc-win32-ia32-msvc': 12.3.4
+ '@next/swc-win32-x64-msvc': 12.3.4
+ sass: 1.79.3
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
nice-try@1.0.5: {}
no-case@3.0.4:
@@ -24858,6 +25774,15 @@ snapshots:
pbkdf2: 3.1.5
safe-buffer: 5.2.1
+ parse-bmfont-ascii@1.0.6: {}
+
+ parse-bmfont-binary@1.0.6: {}
+
+ parse-bmfont-xml@1.1.6:
+ dependencies:
+ xml-parse-from-string: 1.0.1
+ xml2js: 0.5.0
+
parse-entities@4.0.2:
dependencies:
'@types/unist': 2.0.11
@@ -24868,6 +25793,8 @@ snapshots:
is-decimal: 2.0.1
is-hexadecimal: 2.0.1
+ parse-headers@2.0.6: {}
+
parse-json@4.0.0:
dependencies:
error-ex: 1.3.2
@@ -24884,6 +25811,10 @@ snapshots:
parse-numeric-range@1.3.0: {}
+ parse-png@1.1.2:
+ dependencies:
+ pngjs: 3.4.0
+
parse5-htmlparser2-tree-adapter@6.0.1:
dependencies:
parse5: 6.0.1
@@ -24983,6 +25914,12 @@ snapshots:
performance-now@2.1.0: {}
+ phin@3.7.1:
+ dependencies:
+ centra: 2.7.0
+ transitivePeerDependencies:
+ - debug
+
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -25012,6 +25949,10 @@ snapshots:
pirates@4.0.6: {}
+ pixelmatch@4.0.2:
+ dependencies:
+ pngjs: 3.4.0
+
pkg-dir@3.0.0:
dependencies:
find-up: 3.0.0
@@ -25105,6 +26046,8 @@ snapshots:
pn@1.1.0: {}
+ pngjs@3.4.0: {}
+
posix-character-classes@0.1.1: {}
possible-typed-array-names@1.0.0: {}
@@ -26166,6 +27109,13 @@ snapshots:
react-dom: 17.0.2(react@17.0.2)
react-is: 18.3.1
+ rc-util@5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@babel/runtime': 7.26.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-is: 18.3.1
+
rc-virtual-list@3.14.7(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
dependencies:
'@babel/runtime': 7.26.0
@@ -26298,19 +27248,19 @@ snapshots:
raf: 3.4.1
react: 17.0.2
- react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2):
+ react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2):
dependencies:
'@jest/create-cache-key-function': 29.7.0
'@react-native-community/cli': 14.1.0(typescript@4.6.2)
'@react-native-community/cli-platform-android': 14.1.0
'@react-native-community/cli-platform-ios': 14.1.0
'@react-native/assets-registry': 0.75.3
- '@react-native/codegen': 0.75.3(@babel/preset-env@7.26.9(@babel/core@7.26.9))
- '@react-native/community-cli-plugin': 0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(encoding@0.1.13)
+ '@react-native/codegen': 0.75.3(@babel/preset-env@7.26.9(@babel/core@7.25.2))
+ '@react-native/community-cli-plugin': 0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(encoding@0.1.13)
'@react-native/gradle-plugin': 0.75.3
'@react-native/js-polyfills': 0.75.3
'@react-native/normalize-colors': 0.75.3
- '@react-native/virtualized-lists': 0.75.3(@types/react@17.0.42)(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)
+ '@react-native/virtualized-lists': 0.75.3(@types/react@17.0.42)(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)
abort-controller: 3.0.0
anser: 1.4.10
ansi-regex: 5.0.1
@@ -26403,12 +27353,12 @@ snapshots:
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
- react-spring@9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react-konva@18.2.10(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react-zdog@1.2.2)(react@17.0.2)(three@0.168.0)(zdog@1.1.3):
+ react-spring@9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react-konva@18.2.10(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react-zdog@1.2.2)(react@17.0.2)(three@0.168.0)(zdog@1.1.3):
dependencies:
'@react-spring/core': 9.7.4(react@17.0.2)
'@react-spring/konva': 9.7.4(konva@9.3.15)(react-konva@18.2.10(konva@9.3.15)(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2)
- '@react-spring/native': 9.7.4(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)
- '@react-spring/three': 9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.26.9)(@babel/preset-env@7.26.9(@babel/core@7.26.9))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(react@17.0.2)(three@0.168.0)
+ '@react-spring/native': 9.7.4(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)
+ '@react-spring/three': 9.7.4(@react-three/fiber@8.17.7(react-dom@17.0.2(react@17.0.2))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.26.9(@babel/core@7.25.2))(@types/react@17.0.42)(encoding@0.1.13)(react@17.0.2)(typescript@4.6.2))(react@17.0.2)(three@0.168.0))(react@17.0.2)(three@0.168.0)
'@react-spring/web': 9.7.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
'@react-spring/zdog': 9.7.4(react-dom@17.0.2(react@17.0.2))(react-zdog@1.2.2)(react@17.0.2)(zdog@1.1.3)
react: 17.0.2
@@ -26462,6 +27412,8 @@ snapshots:
react@19.0.0: {}
+ read-chunk@1.0.1: {}
+
read-pkg-up@4.0.0:
dependencies:
find-up: 3.0.0
@@ -26546,9 +27498,9 @@ snapshots:
estree-util-build-jsx: 3.0.1
vfile: 6.0.3
- recma-jsx@1.0.0(acorn@8.15.0):
+ recma-jsx@1.0.0(acorn@8.14.0):
dependencies:
- acorn-jsx: 5.3.2(acorn@8.15.0)
+ acorn-jsx: 5.3.2(acorn@8.14.0)
estree-util-to-js: 2.0.0
recma-parse: 1.0.0
recma-stringify: 1.0.0
@@ -26811,6 +27763,17 @@ snapshots:
requires-port@1.0.0: {}
+ resize-img@1.1.2:
+ dependencies:
+ bmp-js: 0.0.1
+ file-type: 3.9.0
+ get-stream: 2.3.1
+ jimp: 0.2.28
+ jpeg-js: 0.1.2
+ parse-png: 1.1.2
+ transitivePeerDependencies:
+ - debug
+
resize-observer-polyfill@1.5.1: {}
resolve-alpn@1.2.1: {}
@@ -27566,6 +28529,12 @@ snapshots:
stream-shift@1.0.3: {}
+ stream-to-buffer@0.1.0:
+ dependencies:
+ stream-to: 0.2.2
+
+ stream-to@0.2.2: {}
+
stream-wormhole@1.1.0: {}
streamroller@3.1.5:
@@ -27729,11 +28698,15 @@ snapshots:
dependencies:
inline-style-parser: 0.2.4
- styled-jsx@5.0.7(@babel/core@7.26.9)(react@17.0.2):
+ styled-jsx@5.0.7(@babel/core@7.25.2)(react@17.0.2):
dependencies:
react: 17.0.2
optionalDependencies:
- '@babel/core': 7.26.9
+ '@babel/core': 7.25.2
+
+ styled-jsx@5.0.7(react@18.3.1):
+ dependencies:
+ react: 18.3.1
stylehacks@6.1.1(postcss@8.5.14):
dependencies:
@@ -27977,6 +28950,8 @@ snapshots:
tiny-warning@1.0.3: {}
+ tinycolor2@1.6.0: {}
+
tmp@0.0.33:
dependencies:
os-tmpdir: 1.0.2
@@ -27991,6 +28966,16 @@ snapshots:
safe-buffer: 5.2.1
typed-array-buffer: 1.0.3
+ to-ico@1.1.5:
+ dependencies:
+ arrify: 1.0.1
+ buffer-alloc: 1.2.0
+ image-size: 0.5.5
+ parse-png: 1.1.2
+ resize-img: 1.1.2
+ transitivePeerDependencies:
+ - debug
+
to-object-path@0.3.0:
dependencies:
kind-of: 3.2.2
@@ -28050,22 +29035,22 @@ snapshots:
semver: 5.7.2
yargs-parser: 10.1.0
- ts-loader@6.2.2(typescript@4.1.6):
+ ts-loader@6.2.2(typescript@4.6.2):
dependencies:
chalk: 2.4.2
enhanced-resolve: 4.5.0
loader-utils: 1.4.2
micromatch: 4.0.8
semver: 6.3.1
- typescript: 4.1.6
+ typescript: 4.6.2
- ts-node@8.10.2(typescript@4.1.6):
+ ts-node@8.10.2(typescript@4.6.2):
dependencies:
arg: 4.1.3
diff: 4.0.4
make-error: 1.3.6
source-map-support: 0.5.21
- typescript: 4.1.6
+ typescript: 4.6.2
yn: 3.1.1
tsconfig-paths-webpack-plugin@3.2.0:
@@ -28106,7 +29091,7 @@ snapshots:
tslib@2.8.1: {}
- tslint@5.20.1(typescript@4.1.6):
+ tslint@5.20.1(typescript@4.6.2):
dependencies:
'@babel/code-frame': 7.26.2
builtin-modules: 1.1.1
@@ -28120,18 +29105,13 @@ snapshots:
resolve: 1.22.8
semver: 5.7.2
tslib: 1.14.1
- tsutils: 2.29.0(typescript@4.1.6)
- typescript: 4.1.6
-
- tsutils@2.29.0(typescript@4.1.6):
- dependencies:
- tslib: 1.14.1
- typescript: 4.1.6
+ tsutils: 2.29.0(typescript@4.6.2)
+ typescript: 4.6.2
- tsutils@3.21.0(typescript@4.1.6):
+ tsutils@2.29.0(typescript@4.6.2):
dependencies:
tslib: 1.14.1
- typescript: 4.1.6
+ typescript: 4.6.2
tsutils@3.21.0(typescript@4.6.2):
dependencies:
@@ -28260,6 +29240,8 @@ snapshots:
typescript@5.6.3: {}
+ typescript@5.9.3: {}
+
ua-parser-js@0.7.39: {}
unbox-primitive@1.0.2:
@@ -28411,6 +29393,10 @@ snapshots:
optionalDependencies:
file-loader: 6.2.0(webpack@5.98.0)
+ url-regex@3.2.0:
+ dependencies:
+ ip-regex: 1.0.3
+
url@0.11.4:
dependencies:
punycode: 1.4.1
@@ -28440,6 +29426,10 @@ snapshots:
dependencies:
react: 17.0.2
+ use-sync-external-store@1.2.0(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+
use-sync-external-store@1.2.2(react@17.0.2):
dependencies:
react: 17.0.2
@@ -29044,17 +30034,31 @@ snapshots:
xdg-basedir@5.1.0: {}
+ xhr@2.6.0:
+ dependencies:
+ global: 4.4.0
+ is-function: 1.0.2
+ parse-headers: 2.0.6
+ xtend: 4.0.2
+
xml-js@1.6.11:
dependencies:
sax: 1.4.1
xml-name-validator@3.0.0: {}
+ xml-parse-from-string@1.0.1: {}
+
xml2js@0.4.23:
dependencies:
sax: 1.4.1
xmlbuilder: 11.0.1
+ xml2js@0.5.0:
+ dependencies:
+ sax: 1.4.1
+ xmlbuilder: 11.0.1
+
xml2js@0.6.2:
dependencies:
sax: 1.4.1
diff --git a/public/README.md b/public/README.md
new file mode 100644
index 0000000..80ad119
--- /dev/null
+++ b/public/README.md
@@ -0,0 +1,13 @@
+# 仓库根目录静态资源
+
+营销与演示素材保留在本目录根级(如 `usage.gif`、`poster.png`)。
+
+品牌与站点图标由 `pnpm export:brand` 生成,按类型分子目录:
+
+| 目录 | 内容 |
+| :--- | :--- |
+| `brand/` | 字标与图标源:`logo.svg`、`wordmark.svg`、`logo.png` 等 |
+| `favicon/` | 浏览器 favicon:`favicon.ico`、`favicon-16/32/48.png` |
+| `icons/` | PWA / Apple Touch:`apple-touch-icon.png`、`icon-192/512.png` |
+
+运行时应用(`web/public`、`server/public` 等)仍使用扁平路径,便于部署引用。
diff --git a/public/brand/logo-200.png b/public/brand/logo-200.png
new file mode 100644
index 0000000..d6aa943
Binary files /dev/null and b/public/brand/logo-200.png differ
diff --git a/public/brand/logo-400.png b/public/brand/logo-400.png
new file mode 100644
index 0000000..7633523
Binary files /dev/null and b/public/brand/logo-400.png differ
diff --git a/public/brand/logo.png b/public/brand/logo.png
new file mode 100644
index 0000000..f5064b6
Binary files /dev/null and b/public/brand/logo.png differ
diff --git a/public/brand/logo.svg b/public/brand/logo.svg
new file mode 100644
index 0000000..7d83e68
--- /dev/null
+++ b/public/brand/logo.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/public/brand/wordmark.svg b/public/brand/wordmark.svg
new file mode 100644
index 0000000..611d8ae
--- /dev/null
+++ b/public/brand/wordmark.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/public/favicon/favicon-16.png b/public/favicon/favicon-16.png
new file mode 100644
index 0000000..9d8cd78
Binary files /dev/null and b/public/favicon/favicon-16.png differ
diff --git a/public/favicon/favicon-32.png b/public/favicon/favicon-32.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/public/favicon/favicon-32.png differ
diff --git a/public/favicon/favicon-48.png b/public/favicon/favicon-48.png
new file mode 100644
index 0000000..b09a2a0
Binary files /dev/null and b/public/favicon/favicon-48.png differ
diff --git a/public/favicon/favicon.ico b/public/favicon/favicon.ico
new file mode 100644
index 0000000..5cca6cb
Binary files /dev/null and b/public/favicon/favicon.ico differ
diff --git a/public/favicon/favicon.png b/public/favicon/favicon.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/public/favicon/favicon.png differ
diff --git a/public/icons/apple-touch-icon.png b/public/icons/apple-touch-icon.png
new file mode 100644
index 0000000..29a9ac9
Binary files /dev/null and b/public/icons/apple-touch-icon.png differ
diff --git a/public/icons/icon-192.png b/public/icons/icon-192.png
new file mode 100644
index 0000000..c158d9e
Binary files /dev/null and b/public/icons/icon-192.png differ
diff --git a/public/icons/icon-512.png b/public/icons/icon-512.png
new file mode 100644
index 0000000..6b91775
Binary files /dev/null and b/public/icons/icon-512.png differ
diff --git a/scripts/brand-assets.mjs b/scripts/brand-assets.mjs
new file mode 100644
index 0000000..d177212
--- /dev/null
+++ b/scripts/brand-assets.mjs
@@ -0,0 +1,142 @@
+/**
+ * ReactPress brand SVG sources + export manifest.
+ * Layout numbers: keep in sync with `web/src/shared/brand/reactPressLogoPaths.ts`
+ * and `scripts/brand-wordmark-layout.mjs`.
+ */
+import {
+ REACT_PRESS_BRAND_FONT,
+ REACT_PRESS_ICON_P,
+ REACT_PRESS_WORDMARK_LAYOUT,
+ REACT_PRESS_WORDMARK_VIEWBOX,
+ wordmarkTextX,
+} from "./brand-wordmark-layout.mjs";
+
+export const BRAND_FILL = "#087ea4";
+
+export const ICON_VIEWBOX = { width: 112, height: 102 };
+
+/** Root `public/` layout (marketing files stay at `public/` root). */
+export const ROOT_PUBLIC_DIRS = {
+ brand: "public/brand",
+ favicon: "public/favicon",
+ icons: "public/icons",
+};
+
+const ORBITS = [
+ "M56 75.165c29.455 0 53.333-10.745 53.333-24s-23.878-24-53.333-24-53.334 10.745-53.334 24 23.879 24 53.334 24Z",
+ "M35.215 63.165c14.728 25.509 35.972 40.815 47.451 34.188 11.48-6.628 8.846-32.68-5.882-58.188-14.727-25.51-35.972-40.816-47.45-34.188-11.48 6.627-8.846 32.679 5.881 58.188Z",
+ "M35.215 39.165c-14.727 25.509-17.36 51.56-5.882 58.188 11.48 6.627 32.724-8.68 47.451-34.188 14.728-25.51 17.362-51.56 5.883-58.188-11.48-6.628-32.724 8.679-47.452 34.188Z",
+];
+
+function orbitPaths(fill = BRAND_FILL) {
+ return ORBITS.map(
+ (d) =>
+ ``,
+ );
+}
+
+/** Icon-only SVG (112×102) — favicon source. */
+export function buildIconSvg(fill = BRAND_FILL) {
+ const paths = orbitPaths(fill).join("\n ");
+ const { fontSize, fontWeight, letterSpacing } = REACT_PRESS_ICON_P;
+ return ``;
+}
+
+/** Horizontal wordmark SVG — logo.png source. */
+export function buildWordmarkSvg(fill = BRAND_FILL) {
+ const { padX, padY, iconScale, textY, textSize, textWeight, textLetterSpacing } =
+ REACT_PRESS_WORDMARK_LAYOUT;
+ const { width, height } = REACT_PRESS_WORDMARK_VIEWBOX;
+ const paths = orbitPaths(fill).join("\n ");
+ const { fontSize, fontWeight, letterSpacing } = REACT_PRESS_ICON_P;
+
+ return ``;
+}
+
+/** Flat runtime dirs (web / server / theme — unchanged deploy paths). */
+const RUNTIME_PUBLIC_DIRS = [
+ "web/public",
+ "themes/twentytwentyfive/public",
+ "themes/my-blog/public",
+ "cli/server/public",
+ "server/public",
+];
+
+/** @type {readonly string[]} Legacy flat paths under `public/` (removed after export). */
+export const ROOT_PUBLIC_LEGACY_BRAND_FILES = [
+ "public/logo.svg",
+ "public/logo.png",
+ "public/logo-400.png",
+ "public/logo-200.png",
+ "public/favicon.ico",
+ "public/favicon.png",
+ "public/favicon-16.png",
+ "public/favicon-32.png",
+ "public/favicon-48.png",
+ "public/apple-touch-icon.png",
+ "public/icon-192.png",
+ "public/icon-512.png",
+];
+
+export const BRAND_EXPORT_MANIFEST = {
+ iconSvg: [
+ "web/public/logo.svg",
+ `${ROOT_PUBLIC_DIRS.brand}/logo.svg`,
+ "docs/static/img/logo.svg",
+ "themes/twentytwentyfive/public/logo.svg",
+ "themes/my-blog/public/logo.svg",
+ "cli/server/public/logo.svg",
+ "server/public/logo.svg",
+ ],
+
+ wordmarkSvg: "web/public/logo-wordmark.svg",
+
+ wordmarkSvgCopies: [`${ROOT_PUBLIC_DIRS.brand}/wordmark.svg`],
+
+ faviconPng: [
+ { name: "favicon-16.png", width: 16 },
+ { name: "favicon-32.png", width: 32 },
+ { name: "favicon-48.png", width: 48 },
+ { name: "favicon.png", width: 32 },
+ ],
+
+ pwaPng: [
+ { name: "apple-touch-icon.png", width: 180 },
+ { name: "icon-192.png", width: 192 },
+ { name: "icon-512.png", width: 512 },
+ ],
+
+ runtimePublicDirs: RUNTIME_PUBLIC_DIRS,
+
+ wordmarkPng: [
+ { name: "logo.png", width: 800 },
+ { name: "logo-400.png", width: 400 },
+ { name: "logo-200.png", width: 200 },
+ ],
+
+ wordmarkRuntimeDirs: RUNTIME_PUBLIC_DIRS,
+
+ wordmarkRootDir: ROOT_PUBLIC_DIRS.brand,
+
+ faviconIcoSizes: [16, 32, 48],
+ faviconIco: [
+ "web/public/favicon.ico",
+ `${ROOT_PUBLIC_DIRS.favicon}/favicon.ico`,
+ "docs/static/img/favicon.ico",
+ "themes/twentytwentyfive/public/favicon.ico",
+ "themes/my-blog/public/favicon.ico",
+ "cli/server/public/favicon.ico",
+ "server/public/favicon.ico",
+ ],
+};
diff --git a/scripts/brand-wordmark-layout.mjs b/scripts/brand-wordmark-layout.mjs
new file mode 100644
index 0000000..dba66e6
--- /dev/null
+++ b/scripts/brand-wordmark-layout.mjs
@@ -0,0 +1,51 @@
+/**
+ * Wordmark layout — keep in sync with `web/src/shared/brand/reactPressLogoPaths.ts`.
+ * Used by `export-brand-assets.mjs` (no TS runtime).
+ */
+
+/** Brand sans — icon “P” and “ReactPress” wordmark. */
+export const REACT_PRESS_BRAND_FONT =
+ "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif";
+
+export const REACT_PRESS_WORDMARK_FONT = REACT_PRESS_BRAND_FONT;
+export const REACT_PRESS_P_FONT = REACT_PRESS_BRAND_FONT;
+
+export const REACT_PRESS_ICON_P = {
+ fontSize: 33,
+ fontWeight: 600,
+ letterSpacing: "-0.03em",
+};
+
+export const REACT_PRESS_WORDMARK_LAYOUT = {
+ padX: 8,
+ padY: 4,
+ iconScale: 0.86,
+ iconGap: 12,
+ textY: 53.5,
+ textSize: 35,
+ textWeight: 600,
+ textLetterSpacing: "-0.03em",
+ textWidth: 180,
+ textPadEnd: 14,
+};
+
+const ICON_W = 112;
+
+const wordmarkIconWidth = ICON_W * REACT_PRESS_WORDMARK_LAYOUT.iconScale;
+
+export const REACT_PRESS_WORDMARK_VIEWBOX = {
+ width: Math.round(
+ REACT_PRESS_WORDMARK_LAYOUT.padX * 2 +
+ wordmarkIconWidth +
+ REACT_PRESS_WORDMARK_LAYOUT.iconGap +
+ REACT_PRESS_WORDMARK_LAYOUT.textWidth +
+ REACT_PRESS_WORDMARK_LAYOUT.textPadEnd,
+ ),
+ height: 96,
+};
+
+export const wordmarkTextX = Math.round(
+ REACT_PRESS_WORDMARK_LAYOUT.padX +
+ wordmarkIconWidth +
+ REACT_PRESS_WORDMARK_LAYOUT.iconGap,
+);
diff --git a/scripts/export-brand-assets.mjs b/scripts/export-brand-assets.mjs
new file mode 100644
index 0000000..6c6a6f8
--- /dev/null
+++ b/scripts/export-brand-assets.mjs
@@ -0,0 +1,183 @@
+#!/usr/bin/env node
+/**
+ * Export ReactPress brand assets (SVG, PNG, ICO) from canonical SVG sources.
+ *
+ * pnpm export:brand
+ *
+ * Root `public/` layout: `brand/`, `favicon/`, `icons/` (see `public/README.md`).
+ */
+import { execSync } from "node:child_process";
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+import {
+ BRAND_EXPORT_MANIFEST,
+ ROOT_PUBLIC_DIRS,
+ ROOT_PUBLIC_LEGACY_BRAND_FILES,
+ buildIconSvg,
+ buildWordmarkSvg,
+} from "./brand-assets.mjs";
+import { REACT_PRESS_WORDMARK_VIEWBOX } from "./brand-wordmark-layout.mjs";
+
+const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
+const tmpDir = path.join(root, ".brand-export");
+
+function ensureDir(filePath) {
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
+}
+
+function writeText(relPath, content) {
+ const dest = path.join(root, relPath);
+ ensureDir(dest);
+ fs.writeFileSync(dest, content);
+}
+
+function resvgPng(svgPath, pngPath, width) {
+ ensureDir(pngPath);
+ execSync(
+ `npx --yes @resvg/resvg-js-cli "${svgPath}" "${pngPath}" --fit-width ${width}`,
+ { stdio: "pipe", cwd: root },
+ );
+}
+
+function copyToDirs(tmpFile, dirs, name) {
+ const written = [];
+ for (const dir of dirs) {
+ const dest = path.join(root, dir, name);
+ ensureDir(dest);
+ fs.copyFileSync(tmpFile, dest);
+ written.push(`${dir}/${name}`);
+ }
+ return written;
+}
+
+function mirrorPngGroup(svgPath, items, dirs, label) {
+ const written = [];
+ for (const { name, width } of items) {
+ const tmpPng = path.join(tmpDir, name);
+ resvgPng(svgPath, tmpPng, width);
+ for (const rel of copyToDirs(tmpPng, dirs, name)) {
+ written.push(`${rel} (${width}px, ${label})`);
+ }
+ }
+ return written;
+}
+
+function removeLegacyRootPublicBrand() {
+ for (const rel of ROOT_PUBLIC_LEGACY_BRAND_FILES) {
+ const dest = path.join(root, rel);
+ if (fs.existsSync(dest)) {
+ fs.unlinkSync(dest);
+ }
+ }
+}
+
+async function writeFaviconIco() {
+ const { faviconIcoSizes, faviconIco } = BRAND_EXPORT_MANIFEST;
+ const pngPaths = faviconIcoSizes.map((size) =>
+ path.join(tmpDir, `favicon-${size}.png`),
+ );
+
+ let toIco;
+ try {
+ ({ default: toIco } = await import("to-ico"));
+ } catch {
+ console.warn(
+ "Skip favicon.ico: install devDependency `to-ico` (pnpm add -D to-ico).",
+ );
+ return [];
+ }
+
+ const buf = await toIco(pngPaths.map((p) => fs.readFileSync(p)));
+ const written = [];
+ for (const rel of faviconIco) {
+ const dest = path.join(root, rel);
+ ensureDir(dest);
+ fs.writeFileSync(dest, buf);
+ written.push(rel);
+ }
+ return written;
+}
+
+function cleanup() {
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+}
+
+async function main() {
+ fs.mkdirSync(tmpDir, { recursive: true });
+
+ const iconSvg = buildIconSvg();
+ const wordmarkSvg = buildWordmarkSvg();
+ const {
+ runtimePublicDirs,
+ wordmarkRuntimeDirs,
+ wordmarkRootDir,
+ faviconPng,
+ pwaPng,
+ } = BRAND_EXPORT_MANIFEST;
+
+ const faviconDirs = [...runtimePublicDirs, ROOT_PUBLIC_DIRS.favicon];
+ const pwaDirs = [...runtimePublicDirs, ROOT_PUBLIC_DIRS.icons];
+
+ for (const rel of BRAND_EXPORT_MANIFEST.iconSvg) {
+ writeText(rel, iconSvg);
+ }
+ writeText(BRAND_EXPORT_MANIFEST.wordmarkSvg, wordmarkSvg);
+ for (const rel of BRAND_EXPORT_MANIFEST.wordmarkSvgCopies) {
+ writeText(rel, wordmarkSvg);
+ }
+
+ const iconSvgTmp = path.join(tmpDir, "icon.svg");
+ const wordmarkSvgTmp = path.join(tmpDir, "wordmark.svg");
+ fs.writeFileSync(iconSvgTmp, iconSvg);
+ fs.writeFileSync(wordmarkSvgTmp, wordmarkSvg);
+
+ console.log("SVG");
+ console.log(" icon:", BRAND_EXPORT_MANIFEST.iconSvg.join("\n "));
+ console.log(" wordmark:", BRAND_EXPORT_MANIFEST.wordmarkSvg);
+ console.log(
+ " copies:",
+ BRAND_EXPORT_MANIFEST.wordmarkSvgCopies.join(", "),
+ );
+ console.log(
+ ` viewBox: icon 112×102, wordmark ${REACT_PRESS_WORDMARK_VIEWBOX.width}×${REACT_PRESS_WORDMARK_VIEWBOX.height}`,
+ );
+
+ console.log("\nPNG → public/favicon + runtime");
+ for (const line of mirrorPngGroup(iconSvgTmp, faviconPng, faviconDirs, "favicon")) {
+ console.log(` ${line}`);
+ }
+
+ console.log("\nPNG → public/icons + runtime");
+ for (const line of mirrorPngGroup(iconSvgTmp, pwaPng, pwaDirs, "pwa")) {
+ console.log(` ${line}`);
+ }
+
+ console.log("\nPNG → public/brand + runtime (wordmark)");
+ for (const line of mirrorPngGroup(
+ wordmarkSvgTmp,
+ BRAND_EXPORT_MANIFEST.wordmarkPng,
+ [...wordmarkRuntimeDirs, wordmarkRootDir],
+ "wordmark",
+ )) {
+ console.log(` ${line}`);
+ }
+
+ console.log("\nICO");
+ for (const rel of await writeFaviconIco()) {
+ console.log(` ${rel}`);
+ }
+
+ removeLegacyRootPublicBrand();
+ console.log("\nRemoved legacy flat brand files under public/ (root).");
+
+ cleanup();
+ console.log("\nDone. Root layout:", Object.values(ROOT_PUBLIC_DIRS).join(", "));
+}
+
+main().catch((err) => {
+ cleanup();
+ console.error(err);
+ process.exit(1);
+});
diff --git a/server/package.json b/server/package.json
index d4856fe..2a728eb 100644
--- a/server/package.json
+++ b/server/package.json
@@ -131,7 +131,7 @@
"ts-node": "^8.4.1",
"tsconfig-paths": "^3.9.0",
"tslint": "^5.20.0",
- "typescript": "~4.1.6"
+ "typescript": "4.6.2"
},
"jest": {
"moduleFileExtensions": [
diff --git a/server/public/apple-touch-icon.png b/server/public/apple-touch-icon.png
new file mode 100644
index 0000000..29a9ac9
Binary files /dev/null and b/server/public/apple-touch-icon.png differ
diff --git a/server/public/favicon-16.png b/server/public/favicon-16.png
new file mode 100644
index 0000000..9d8cd78
Binary files /dev/null and b/server/public/favicon-16.png differ
diff --git a/server/public/favicon-32.png b/server/public/favicon-32.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/server/public/favicon-32.png differ
diff --git a/server/public/favicon-48.png b/server/public/favicon-48.png
new file mode 100644
index 0000000..b09a2a0
Binary files /dev/null and b/server/public/favicon-48.png differ
diff --git a/server/public/favicon.ico b/server/public/favicon.ico
new file mode 100644
index 0000000..5cca6cb
Binary files /dev/null and b/server/public/favicon.ico differ
diff --git a/server/public/favicon.png b/server/public/favicon.png
index 521edf8..f455437 100644
Binary files a/server/public/favicon.png and b/server/public/favicon.png differ
diff --git a/server/public/icon-192.png b/server/public/icon-192.png
new file mode 100644
index 0000000..c158d9e
Binary files /dev/null and b/server/public/icon-192.png differ
diff --git a/server/public/icon-512.png b/server/public/icon-512.png
new file mode 100644
index 0000000..6b91775
Binary files /dev/null and b/server/public/icon-512.png differ
diff --git a/server/public/logo-200.png b/server/public/logo-200.png
new file mode 100644
index 0000000..d6aa943
Binary files /dev/null and b/server/public/logo-200.png differ
diff --git a/server/public/logo-400.png b/server/public/logo-400.png
new file mode 100644
index 0000000..7633523
Binary files /dev/null and b/server/public/logo-400.png differ
diff --git a/server/public/logo.png b/server/public/logo.png
new file mode 100644
index 0000000..f5064b6
Binary files /dev/null and b/server/public/logo.png differ
diff --git a/server/public/logo.svg b/server/public/logo.svg
new file mode 100644
index 0000000..7d83e68
--- /dev/null
+++ b/server/public/logo.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/themes/my-blog/public/apple-touch-icon.png b/themes/my-blog/public/apple-touch-icon.png
new file mode 100644
index 0000000..29a9ac9
Binary files /dev/null and b/themes/my-blog/public/apple-touch-icon.png differ
diff --git a/themes/my-blog/public/favicon-16.png b/themes/my-blog/public/favicon-16.png
new file mode 100644
index 0000000..9d8cd78
Binary files /dev/null and b/themes/my-blog/public/favicon-16.png differ
diff --git a/themes/my-blog/public/favicon-32.png b/themes/my-blog/public/favicon-32.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/themes/my-blog/public/favicon-32.png differ
diff --git a/themes/my-blog/public/favicon-48.png b/themes/my-blog/public/favicon-48.png
new file mode 100644
index 0000000..b09a2a0
Binary files /dev/null and b/themes/my-blog/public/favicon-48.png differ
diff --git a/themes/my-blog/public/favicon.ico b/themes/my-blog/public/favicon.ico
new file mode 100644
index 0000000..5cca6cb
Binary files /dev/null and b/themes/my-blog/public/favicon.ico differ
diff --git a/themes/my-blog/public/favicon.png b/themes/my-blog/public/favicon.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/themes/my-blog/public/favicon.png differ
diff --git a/themes/my-blog/public/icon-192.png b/themes/my-blog/public/icon-192.png
new file mode 100644
index 0000000..c158d9e
Binary files /dev/null and b/themes/my-blog/public/icon-192.png differ
diff --git a/themes/my-blog/public/icon-512.png b/themes/my-blog/public/icon-512.png
new file mode 100644
index 0000000..6b91775
Binary files /dev/null and b/themes/my-blog/public/icon-512.png differ
diff --git a/themes/my-blog/public/logo-200.png b/themes/my-blog/public/logo-200.png
new file mode 100644
index 0000000..d6aa943
Binary files /dev/null and b/themes/my-blog/public/logo-200.png differ
diff --git a/themes/my-blog/public/logo-400.png b/themes/my-blog/public/logo-400.png
new file mode 100644
index 0000000..7633523
Binary files /dev/null and b/themes/my-blog/public/logo-400.png differ
diff --git a/themes/my-blog/public/logo.png b/themes/my-blog/public/logo.png
new file mode 100644
index 0000000..f5064b6
Binary files /dev/null and b/themes/my-blog/public/logo.png differ
diff --git a/themes/my-blog/public/logo.svg b/themes/my-blog/public/logo.svg
new file mode 100644
index 0000000..7d83e68
--- /dev/null
+++ b/themes/my-blog/public/logo.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/themes/theme.manifest.schema.json b/themes/theme.manifest.schema.json
index bc15bce..08d0ae7 100644
--- a/themes/theme.manifest.schema.json
+++ b/themes/theme.manifest.schema.json
@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
- "$id": "https://reactpress.dev/schemas/theme.manifest.json",
+ "$id": "https://github.com/fecommunity/reactpress/blob/master/themes/theme.manifest.schema.json",
"title": "Theme Manifest",
"description": "Portable theme manifest for ReactPress and compatible platforms. Parsed by `parseThemeManifest()`; stored at `themes/{id}/theme.json`. Platform fields are top-level (no vendor namespace). Appearance controls map to WordPress theme_mod storage.",
"type": "object",
diff --git a/themes/twentytwentyfive/public/apple-touch-icon.png b/themes/twentytwentyfive/public/apple-touch-icon.png
new file mode 100644
index 0000000..29a9ac9
Binary files /dev/null and b/themes/twentytwentyfive/public/apple-touch-icon.png differ
diff --git a/themes/twentytwentyfive/public/favicon-16.png b/themes/twentytwentyfive/public/favicon-16.png
new file mode 100644
index 0000000..9d8cd78
Binary files /dev/null and b/themes/twentytwentyfive/public/favicon-16.png differ
diff --git a/themes/twentytwentyfive/public/favicon-32.png b/themes/twentytwentyfive/public/favicon-32.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/themes/twentytwentyfive/public/favicon-32.png differ
diff --git a/themes/twentytwentyfive/public/favicon-48.png b/themes/twentytwentyfive/public/favicon-48.png
new file mode 100644
index 0000000..b09a2a0
Binary files /dev/null and b/themes/twentytwentyfive/public/favicon-48.png differ
diff --git a/themes/twentytwentyfive/public/favicon.ico b/themes/twentytwentyfive/public/favicon.ico
new file mode 100644
index 0000000..5cca6cb
Binary files /dev/null and b/themes/twentytwentyfive/public/favicon.ico differ
diff --git a/themes/twentytwentyfive/public/favicon.png b/themes/twentytwentyfive/public/favicon.png
new file mode 100644
index 0000000..f455437
Binary files /dev/null and b/themes/twentytwentyfive/public/favicon.png differ
diff --git a/themes/twentytwentyfive/public/icon-192.png b/themes/twentytwentyfive/public/icon-192.png
new file mode 100644
index 0000000..c158d9e
Binary files /dev/null and b/themes/twentytwentyfive/public/icon-192.png differ
diff --git a/themes/twentytwentyfive/public/icon-512.png b/themes/twentytwentyfive/public/icon-512.png
new file mode 100644
index 0000000..6b91775
Binary files /dev/null and b/themes/twentytwentyfive/public/icon-512.png differ
diff --git a/themes/twentytwentyfive/public/logo-200.png b/themes/twentytwentyfive/public/logo-200.png
new file mode 100644
index 0000000..d6aa943
Binary files /dev/null and b/themes/twentytwentyfive/public/logo-200.png differ
diff --git a/themes/twentytwentyfive/public/logo-400.png b/themes/twentytwentyfive/public/logo-400.png
new file mode 100644
index 0000000..7633523
Binary files /dev/null and b/themes/twentytwentyfive/public/logo-400.png differ
diff --git a/themes/twentytwentyfive/public/logo.png b/themes/twentytwentyfive/public/logo.png
new file mode 100644
index 0000000..f5064b6
Binary files /dev/null and b/themes/twentytwentyfive/public/logo.png differ
diff --git a/themes/twentytwentyfive/public/logo.svg b/themes/twentytwentyfive/public/logo.svg
new file mode 100644
index 0000000..7d83e68
--- /dev/null
+++ b/themes/twentytwentyfive/public/logo.svg
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/toolkit/README.md b/toolkit/README.md
index 6c81e18..aaef922 100644
--- a/toolkit/README.md
+++ b/toolkit/README.md
@@ -1,387 +1,272 @@
# @fecommunity/reactpress-toolkit
-TypeScript-based API client toolkit for ReactPress, automatically generated from OpenAPI/Swagger specifications.
+ReactPress 的 TypeScript 工具包:从 OpenAPI/Swagger 生成的 API 客户端、站点配置类型、Next.js 主题 SSR 辅助,以及管理后台插件注册。
+
+## 包导出(`package.json` → `exports`)
+
+| 子路径 | 用途 |
+|--------|------|
+| `.` | 根入口:`api`、`config`、`theme`、`ui`、`plugin` 命名空间及常用 re-export |
+| `./api` | 各资源 HTTP 客户端类 |
+| `./api/instance` | 默认 `api` 实例与 `createApiInstance` |
+| `./types` | Swagger 生成的请求/响应类型 |
+| `./utils` | 与业务无关的纯工具(见下方模块表) |
+| `./config` | 环境变量、全局默认、`i18n` 文案(`config/locales/*.json`) |
+| `./theme` | 访客主题:SSR 拉数、运行时、扩展配置类型,并 re-export `./ui` |
+| `./theme/next-config` | `createReactPressNextConfig()` |
+| `./theme/node` | Node 专用(如读取主题管理端 locale 文件) |
+| `./ui` | 无样式主题组件与 hooks |
+| `./plugin` | 管理端插件聚合入口 |
+| `./plugin/admin` | 菜单注册、权限常量 |
+| `./plugin/client` | Web/Electron API 客户端工厂(推荐) |
+| `./plugin/react` | 与 `./plugin/client` 相同(兼容旧路径) |
+| `./plugin/dev` | Vite 开发端口重定向等 |
+
+**约定:** `src/` 下仅保留与上述导出对应的一级目录;实现细节放在子目录中,根目录用薄 shim 保持对外路径稳定(例如 `theme/node.ts` → `theme/build/node.ts`)。
+
+## 源码结构
-[](https://www.npmjs.com/package/@fecommunity/reactpress-toolkit)
-[](https://github.com/fecommunity/reactpress/blob/master/toolkit/LICENSE)
-[](http://www.typescriptlang.org/)
-
-## Overview
-
-The ReactPress Toolkit is an API client library that provides strongly-typed interfaces for interacting with the ReactPress backend services. It includes:
-
-- **Auto-generated API clients** for all ReactPress modules
-- **TypeScript definitions** for all data contracts
-- **Utility functions** for common operations
-- **HTTP client** with built-in authentication and error handling
-- **Automatic retry mechanisms** for failed requests
-- **Request/response interceptors** for logging
-
-The toolkit is automatically generated from the ReactPress backend's OpenAPI specification, ensuring that it's always in sync with the latest API changes. This eliminates manual API client maintenance and reduces the likelihood of API integration errors.
-
-## Installation
-
-```bash
-npm install @fecommunity/reactpress-toolkit
```
-
-or
-
-```bash
-yarn add @fecommunity/reactpress-toolkit
+src/
+├── api/ # OpenAPI 生成(勿手改业务逻辑)
+├── config/ # env、global、i18n + locales/
+├── types/ # Swagger 生成类型
+├── utils/ # 纯函数工具(theme/plugin 共用)
+│ ├── api-envelope.ts # unpackList / unpackOne / unpackPaginated
+│ ├── cookie.ts # readRequestCookie
+│ ├── date.ts # formatDate、formatPublishDate*
+│ ├── email.ts # 评论邮箱校验
+│ ├── error.ts # ApiError、isApiError
+│ ├── json.ts # safeJsonParse
+│ ├── jsonp.ts # 浏览器 JSONP
+│ ├── object.ts # deepMerge、getByPath、deepClone
+│ ├── setting.ts # pickSiteSettings
+│ └── string.ts # stripHtml、truncateWords
+├── plugin/
+│ ├── admin/ # AdminModule、菜单注册、权限
+│ ├── client/ # createClient、resolveApiBaseUrl
+│ ├── dev/ # devPortRedirectPlugin 等
+│ └── react/ # 仅 re-export client(兼容)
+├── theme/
+│ ├── api/ # themeApi、axios 封装(JSON/解包 re-export utils)
+│ ├── ssr/ # fetch、static props、站点 meta
+│ ├── visitor/ # 语言、会话、配色、运行时 mods
+│ ├── content/ # SEO、导航路径、摘要、静态资源 URL
+│ ├── preview/ # 预览 token / mods / config 查询参数
+│ ├── createCatalogApp.js # 完整主题 _app 工厂(无 UI 框架依赖)
+│ ├── createApp.js # Next `_app` 工厂(allowJs,随 tsc 输出到 dist)
+│ ├── extension/ # theme.json schema、站点配置、与服务端共享的类型
+│ ├── node.ts # → build/node
+│ ├── next-config.ts
+│ └── index.ts # 对外 barrel
+└── ui/
+ ├── components/ # 无样式 React 组件
+ ├── context/ # ReactPressProvider、locale、runtime
+ └── hooks/ # 通用 React hooks(与具体页面无关)
```
-or
+## 安装
```bash
pnpm add @fecommunity/reactpress-toolkit
```
-## Quick Start
+## 快速开始
-### Using the Default API Instance
+### API 客户端
```typescript
import { api } from '@fecommunity/reactpress-toolkit';
+// 或
+import { api, createApiInstance } from '@fecommunity/reactpress-toolkit/api/instance';
-// Get all articles
const articles = await api.article.findAll();
-
-// Get a specific article by ID
-const article = await api.article.findById('article-id');
-
-// Create a new article
-const newArticle = await api.article.create({
- title: 'My New Article',
- content: 'Article content here...',
- // ... other properties
-});
```
-### Using Named Exports
+自定义 baseURL:
```typescript
-import { api, types, utils } from '@fecommunity/reactpress-toolkit';
-
-// Working with typed data
-const article: types.IArticle = {
- id: '1',
- title: 'Sample Article',
- // ... other properties
-};
+import { createApiInstance } from '@fecommunity/reactpress-toolkit/api/instance';
-// Using utility functions
-const formattedDate = utils.formatDate(new Date());
+const customApi = createApiInstance({ baseURL: 'https://api.example.com/api' });
```
-## Configuration
+### 通用工具(`./utils`)
-You can create a custom API instance with specific configuration:
+与 Next/主题无关的逻辑应放在 `utils`,`theme` 仅 re-export 以保持 `@fecommunity/reactpress-toolkit/theme` 路径兼容:
```typescript
-import { http } from '@fecommunity/reactpress-toolkit';
-
-const customApi = http.createApiInstance({
- baseURL: 'https://api.yourdomain.com',
- timeout: 10000,
- // ... other axios configuration options
-});
-
-// Use the custom instance
-const articles = await customApi.article.findAll();
+import {
+ safeJsonParse,
+ unpackList,
+ formatPublishDate,
+ stripHtml,
+ deepMerge,
+ getByPath,
+ pickSiteSettings,
+} from '@fecommunity/reactpress-toolkit/utils';
```
-## Enterprise Features
+| 模块 | 典型用途 |
+|------|----------|
+| `json` | 解析 setting 里的 JSON 字符串 |
+| `api-envelope` | 解包 Nest `TransformInterceptor` 的 `{ data }` |
+| `date` | 文章发布日期展示 |
+| `string` | 归档摘要 HTML 剥离、截断 |
+| `object` | theme.json 配置合并、点路径读写 |
+| `setting` | 把 settings 行映射为 props |
+| `cookie` | SSR 读取 `Cookie` 头 |
+| `email` | 访客评论邮箱格式 |
-### Automatic Retry Mechanisms
-```typescript
-import { http } from '@fecommunity/reactpress-toolkit';
-
-// Configure retry settings
-const customApi = http.createApiInstance({
- baseURL: 'https://api.yourdomain.com',
- retry: {
- retries: 3,
- retryDelay: 1000,
- retryCondition: (error) => {
- return error.response?.status === 503 || error.code === 'ECONNABORTED';
- }
- }
-});
-```
+### 管理后台插件
-### Request/Response Interceptors
```typescript
-import { http } from '@fecommunity/reactpress-toolkit';
-
-const monitoredApi = http.createApiInstance({
- baseURL: 'https://api.yourdomain.com',
- interceptors: {
- request: (config) => {
- // Add logging, metrics, etc.
- console.log(`Request: ${config.method?.toUpperCase()} ${config.url}`);
- return config;
- },
- response: (response) => {
- // Add logging, metrics, etc.
- console.log(`Response: ${response.status} ${response.config.url}`);
- return response;
- }
- }
-});
+import type { AdminModule } from '@fecommunity/reactpress-toolkit/plugin/admin';
+import { permissionsForRole } from '@fecommunity/reactpress-toolkit/plugin/admin';
```
-### Authentication Handling
-```typescript
-import { http, api, utils } from '@fecommunity/reactpress-toolkit';
-
-// Automatic token refresh
-const secureApi = http.createApiInstance({
- baseURL: 'https://api.yourdomain.com',
- auth: {
- tokenRefresh: async (refreshToken) => {
- const response = await api.auth.refresh({ refreshToken });
- return response.data.accessToken;
- }
- }
-});
-```
-
-## Type Definitions
-
-All data models are strongly typed and available through the `types` export:
+### Web / Electron 客户端
```typescript
-import { types } from '@fecommunity/reactpress-toolkit';
-
-const user: types.IUser = {
- name: 'John Doe',
- email: 'john@example.com',
- // ... other properties
-};
+import { createClient, resolveApiBaseUrl } from '@fecommunity/reactpress-toolkit/plugin/client';
```
-Available type definitions include:
-- `types.IUser`
-- `types.IArticle`
-- `types.ICategory`
-- `types.ITag`
-- `types.IComment`
-- `types.IFile`
-- `types.ISetting`
-- `types.IPage`
-- `types.IKnowledge`
-- `types.IView`
-- `types.I_SMTP`
+## App 工厂(`./app`)
-## Utility Functions
+主题 `_app.tsx` 的入口工厂,与 UI 框架无关:
-The toolkit includes helpful utility functions:
+| 工厂 | 用途 |
+|------|------|
+| `createThemeApp` | 极简主题(hello-world):`ReactPressProvider` + `ThemeCssVars` |
+| `createReactPressApp` | 完整主题:SSR catalog、i18n、统计、PV;通过 `wrapContent` 注入 Ant Design 等 |
```typescript
-import { utils } from '@fecommunity/reactpress-toolkit';
-
-// Date formatting
-const formattedDate = utils.formatDate(new Date(), 'YYYY-MM-DD');
+import { createThemeApp } from '@fecommunity/reactpress-toolkit/app';
+import { createReactPressApp } from '@fecommunity/reactpress-toolkit/app';
+```
-// Deep cloning
-const clonedObject = utils.deepClone(originalObject);
+也可从 `./theme` re-export 导入(兼容旧路径)。
-// Error handling
-if (utils.ApiError.isInstance(error)) {
- console.log(`API Error: ${error.code} - ${error.message}`);
-}
+## 主题开发(`./theme`)
-// Data validation
-const isValidEmail = utils.validateEmail('user@example.com');
+Next.js 访客主题应使用 toolkit 提供的 API 与 SSR 辅助,避免在每个主题里复制 `lib/api.ts`。
-// String manipulation
-const slug = utils.createSlug('My Article Title');
+```typescript
+import {
+ themeApi,
+ fetchVisitorContext,
+ fetchThemeCatalog,
+ themeStaticProps,
+ unpackList,
+} from '@fecommunity/reactpress-toolkit/theme';
+import { createThemeApp } from '@fecommunity/reactpress-toolkit/app';
```
-## Development
-
-### Generating API Definitions
+```javascript
+// next.config.js
+const { createReactPressNextConfig } = require('@fecommunity/reactpress-toolkit/theme/next-config');
+module.exports = createReactPressNextConfig();
+```
-The toolkit is automatically generated from the ReactPress backend's OpenAPI specification:
+```typescript
+// pages/_app.tsx
+import themeManifest from '../theme.json';
-```bash
-npm run generate
+export default createThemeApp(themeManifest);
```
-This command will:
-1. Generate a new Swagger JSON from the ReactPress server
-2. Create TypeScript definitions from the specification
-3. Organize the generated files into appropriate directories
-4. Create API clients for each module
+**完整功能主题** — 使用 `createReactPressApp`(UI 框架无关),Ant Design 等重型依赖留在主题内:
-### Building
-
-```bash
-npm run build
+```typescript
+import { createReactPressApp } from '@fecommunity/reactpress-toolkit/app';
+import { ConfigProvider } from 'antd';
+import { NextIntlProvider } from 'next-intl';
+
+export default createReactPressApp(themeManifest, {
+ Layout: AppLayout,
+ IntlProvider: NextIntlProvider,
+ wrapContent: (content, { locale, isDark, colorPrimary }) => (
+
+ {content}
+
+ ),
+});
```
-This will compile the TypeScript code to JavaScript in the `dist` directory.
+`createReactPressApp` 内置:SSR 站点数据、预览 mods、SiteCatalogProvider、路由进度条、统计脚本、PV 上报。**不含** Ant Design / cssinjs。
-## Testing
+**REST Provider** — 无需在每个主题复制 14 个 Provider 文件:
-```bash
-# Run unit tests
-npm run test
+```typescript
+import { createThemeAxiosClient, createThemeProviders } from '@fecommunity/reactpress-toolkit/theme';
-# Run tests with coverage
-npm run test:cov
+export const httpProvider = createThemeAxiosClient({ onError, onUnauthorized });
+export const { ArticleProvider, CategoryProvider, TagProvider } = createThemeProviders(httpProvider);
+```
-# Run linting
-npm run lint
+环境变量:`REACTPRESS_API_URL`(SSR)、`NEXT_PUBLIC_REACTPRESS_API_URL`(浏览器),由 `reactpress theme dev` 注入。
-# Run formatting
-npm run format
-```
+### 无样式 UI(`./ui`)
-## Integration Examples
+也可从 `./theme` 一并导入:
-### React Integration
```typescript
-import { useState, useEffect } from 'react';
-import { api, types } from '@fecommunity/reactpress-toolkit';
-
-const ArticleList = () => {
- const [articles, setArticles] = useState([]);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- const fetchArticles = async () => {
- try {
- const response = await api.article.findAll();
- setArticles(response.data);
- } catch (error) {
- console.error('Failed to fetch articles:', error);
- } finally {
- setLoading(false);
- }
- };
-
- fetchArticles();
- }, []);
-
- if (loading) return Loading...
;
-
- return (
-
- {articles.map(article => (
-
-
{article.title}
-
{article.content}
-
- ))}
-
- );
-};
+import { NavMenu, ArticleList, ReactPressProvider, useLocale } from '@fecommunity/reactpress-toolkit/ui';
```
-### Node.js Integration
+| 符号 | 说明 |
+|------|------|
+| `ReactPressProvider` | 在 `_app` 注入站点上下文 |
+| `useLocale` / `useThemeRuntime` / `useThemeMod` | 语言与主题 mods |
+| `useToggle` / `useAsyncLoading` | 布尔切换、防抖 loading(评论/搜索等) |
+| `usePagination` | `[items, total]` 分页列表 |
+| `useReportArticleView` / `useReportPageView` | 文章阅读数、全站 PV 上报 |
+| `useRouteParam` / `useNavActive` | 动态路由参数、导航高亮 |
+| `useWarningOnExit` | 离开页面前确认(Next Router) |
+| `NavMenu` | 配置驱动导航,通过 `renderLink` 接 Next `Link` |
+| `ArticleList` | 列表渲染,通过 `renderArticle` 自定义卡片 |
+| `SiteCatalogProvider` / `useSiteSetting` / `useSiteUser` | 全站 catalog + 用户会话 |
+| `SiteSeo` / `RouteProgress` / `SiteAnalytics` | SEO meta、路由进度条、GA/百度统计 |
+| `ArticleReader` / `HtmlContent` / `ArticleToc` | 文章阅读、HTML 渲染、目录 |
+| `ImageViewer` / `LocaleTime` | 图片预览、本地化时间 |
+| `createReactPressApp` | 完整主题 `_app` 工厂(`@fecommunity/reactpress-toolkit/app`) |
+| `fetchAppBootstrap` | `_app.getInitialProps` SSR 数据(setting / taxonomy / preview) |
+
+### 扩展配置类型
+
+`theme/extension` 中的类型与校验逻辑与 **server**、**web 外观设置** 共用,例如 `ThemeConfigurationSchema`、`resolveSiteConfig`、`PUBLIC_SETTING_KEYS`。服务端可:
+
```typescript
-import { api, types } from '@fecommunity/reactpress-toolkit';
-
-async function syncArticles() {
- try {
- // Fetch articles from ReactPress
- const response = await api.article.findAll({
- limit: 100,
- offset: 0
- });
-
- // Process articles
- const articles: types.IArticle[] = response.data;
-
- // Sync with another system
- for (const article of articles) {
- // Your sync logic here
- console.log(`Synced article: ${article.title}`);
- }
- } catch (error) {
- console.error('Sync failed:', error);
- }
-}
-
-syncArticles();
+import { PUBLIC_SETTING_KEYS, systemGlobalSettingDefaults } from '@fecommunity/reactpress-toolkit/theme';
+import { readThemeAdminLocaleFile } from '@fecommunity/reactpress-toolkit/theme/node';
```
-## Best Practices
+## 从 Swagger 重新生成 API
-### Error Handling
-```typescript
-import { api, utils } from '@fecommunity/reactpress-toolkit';
-
-try {
- const articles = await api.article.findAll();
- // Process articles
-} catch (error) {
- if (utils.ApiError.isInstance(error)) {
- switch (error.code) {
- case 401:
- // Handle unauthorized access
- redirectToLogin();
- break;
- case 403:
- // Handle forbidden access
- showPermissionError();
- break;
- case 500:
- // Handle server errors
- showServerError();
- break;
- default:
- // Handle other API errors
- showGenericError(error.message);
- }
- } else {
- // Handle network errors
- showNetworkError();
- }
-}
+在 monorepo 根目录确保 server 可构建后:
+
+```bash
+cd toolkit && pnpm run generate
```
-### Pagination
-```typescript
-import { api, types } from '@fecommunity/reactpress-toolkit';
-
-async function fetchAllArticles(): Promise {
- const allArticles: types.IArticle[] = [];
- let offset = 0;
- const limit = 50;
-
- while (true) {
- const response = await api.article.findAll({ limit, offset });
- allArticles.push(...response.data);
-
- if (response.data.length < limit) {
- // No more articles to fetch
- break;
- }
-
- offset += limit;
- }
-
- return allArticles;
-}
+将更新 `src/api/*` 与 `src/types/*`。
+
+## 构建
+
+```bash
+cd toolkit && pnpm run build
```
-## Contributing
+输出到 `dist/`;`createApp.js` 随 `tsc`(`allowJs`)写入 `dist/theme/createApp.js`。
-1. Fork the repository
-2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
-3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
-4. Push to the branch (`git push origin feature/AmazingFeature`)
-5. Open a pull request
+## 开发脚本
+
+| 命令 | 说明 |
+|------|------|
+| `pnpm run generate` | 从 server Swagger 生成 API |
+| `pnpm run build` | `tsc` + 复制 locales |
+| `pnpm run typecheck` | 仅类型检查 |
## License
ISC
-
-## Related Projects
-
-- [ReactPress](https://github.com/fecommunity/reactpress) - The main ReactPress platform
-- [ReactPress Server](https://github.com/fecommunity/reactpress/server) - Backend server
-- [ReactPress Client](https://github.com/fecommunity/reactpress/client) - Frontend client
\ No newline at end of file
diff --git a/toolkit/package.json b/toolkit/package.json
index 037b5ce..2d84163 100644
--- a/toolkit/package.json
+++ b/toolkit/package.json
@@ -4,16 +4,107 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
- ".": "./dist/index.js",
- "./api": "./dist/api/index.js",
- "./api/instance": "./dist/api/instance.js",
- "./types": "./dist/types/index.js",
- "./utils": "./dist/utils/index.js",
- "./config": "./dist/config/index.js"
+ ".": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ },
+ "./api": {
+ "types": "./dist/api/index.d.ts",
+ "default": "./dist/api/index.js"
+ },
+ "./api/instance": {
+ "types": "./dist/api/instance.d.ts",
+ "default": "./dist/api/instance.js"
+ },
+ "./types": {
+ "types": "./dist/types/index.d.ts",
+ "default": "./dist/types/index.js"
+ },
+ "./utils": {
+ "types": "./dist/utils/index.d.ts",
+ "default": "./dist/utils/index.js"
+ },
+ "./config": {
+ "types": "./dist/config/index.d.ts",
+ "default": "./dist/config/index.js"
+ },
+ "./theme": {
+ "types": "./dist/theme/index.d.ts",
+ "default": "./dist/theme/index.js"
+ },
+ "./theme/server": {
+ "types": "./dist/theme/server.d.ts",
+ "default": "./dist/theme/server.js"
+ },
+ "./theme/next-config": {
+ "types": "./dist/theme/next-config.d.ts",
+ "default": "./dist/theme/next-config.js"
+ },
+ "./theme/node": {
+ "types": "./dist/theme/node.d.ts",
+ "default": "./dist/theme/node.js"
+ },
+ "./theme/providers": {
+ "types": "./dist/theme/providers/index.d.ts",
+ "default": "./dist/theme/providers/index.js"
+ },
+ "./app": {
+ "types": "./dist/app/index.d.ts",
+ "default": "./dist/app/index.js"
+ },
+ "./ui": {
+ "types": "./dist/ui/index.d.ts",
+ "default": "./dist/ui/index.js"
+ },
+ "./ui/content": {
+ "types": "./dist/ui/components/content/index.d.ts",
+ "default": "./dist/ui/components/content/index.js"
+ },
+ "./plugin": {
+ "types": "./dist/plugin/index.d.ts",
+ "default": "./dist/plugin/index.js"
+ },
+ "./plugin/admin": {
+ "types": "./dist/plugin/admin/index.d.ts",
+ "default": "./dist/plugin/admin/index.js"
+ },
+ "./plugin/client": {
+ "types": "./dist/plugin/client/index.d.ts",
+ "default": "./dist/plugin/client/index.js"
+ },
+ "./plugin/react": {
+ "types": "./dist/plugin/react/index.d.ts",
+ "default": "./dist/plugin/react/index.js"
+ },
+ "./plugin/dev": {
+ "types": "./dist/plugin/dev/index.d.ts",
+ "default": "./dist/plugin/dev/index.js"
+ }
+ },
+ "peerDependencies": {
+ "@ant-design/cssinjs": ">=1",
+ "@tanstack/react-query": ">=5",
+ "highlight.js": ">=9",
+ "next": ">=12",
+ "nprogress": ">=0.2",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "viewerjs": ">=1"
+ },
+ "peerDependenciesMeta": {
+ "@tanstack/react-query": { "optional": true },
+ "@ant-design/cssinjs": { "optional": true },
+ "highlight.js": { "optional": true },
+ "next": { "optional": true },
+ "nprogress": { "optional": true },
+ "react": { "optional": true },
+ "react-dom": { "optional": true },
+ "viewerjs": { "optional": true }
},
"scripts": {
"generate": "node scripts/generate-swagger.js",
- "build": "tsc && cp -r src/locales dist/"
+ "build": "tsc && rm -rf dist/config/locales && cp -r src/config/locales dist/config/locales",
+ "typecheck": "tsc --noEmit"
},
"files": [
"dist"
@@ -38,6 +129,7 @@
},
"description": "ReactPress TypeScript toolkit: OpenAPI clients, theme SSR helpers, headless UI, and admin plugin registry.",
"dependencies": {
+ "ajv": "^8.17.1",
"axios": "^1.12.2",
"dotenv": "^8.6.0",
"fs-extra": "^10.0.0",
@@ -45,7 +137,13 @@
"swagger-typescript-api": "^12.0.4"
},
"devDependencies": {
+ "@ant-design/cssinjs": "^1.22.0",
"@types/fs-extra": "^9.0.13",
- "@types/node": "^17.0.22"
+ "@types/node": "^17.0.22",
+ "@types/react": "^18.3.18",
+ "@types/react-dom": "^18.3.5",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "typescript": "^5.7.3"
}
}
diff --git a/toolkit/scripts/resolve-swagger-input.js b/toolkit/scripts/resolve-swagger-input.js
index 545f971..1f8a64f 100644
--- a/toolkit/scripts/resolve-swagger-input.js
+++ b/toolkit/scripts/resolve-swagger-input.js
@@ -1,7 +1,9 @@
-const { getBundledSwaggerPath, getBundledServerDir } = require('../../scripts/bundled-server-path');
+const path = require('path');
+const { getBundledServerDir, getSwaggerPath } = require('../../cli/lib/paths');
function getSwaggerInputPath() {
- return getBundledSwaggerPath();
+ const root = path.resolve(__dirname, '../..');
+ return getSwaggerPath(root);
}
function getBundledServerPathForGenerate() {
diff --git a/toolkit/src/api/Smtp.ts b/toolkit/src/api/Smtp.ts
index a76b4b0..b5b5b1b 100644
--- a/toolkit/src/api/Smtp.ts
+++ b/toolkit/src/api/Smtp.ts
@@ -10,7 +10,7 @@
*/
import { ISmtpControllerCreateData, ISmtpControllerDeleteByIdData, ISmtpControllerFindAllData } from '../types';
-import { HttpClient, RequestParams } from './HttpClient';
+import { HttpClient, ContentType, RequestParams } from './HttpClient';
export class Smtp {
http: HttpClient;
@@ -61,4 +61,29 @@ export class Smtp {
method: 'DELETE',
...params,
});
+ /**
+ * 发送测试邮件(验证 SMTP 配置)
+ *
+ * @tags Smtp
+ * @name SmtpControllerTestSend
+ * @request POST:/smtp/test
+ */
+ testSend = (
+ data: {
+ to: string;
+ smtpHost?: string;
+ smtpPort?: string;
+ smtpUser?: string;
+ smtpPass?: string;
+ smtpFromUser?: string;
+ },
+ params: RequestParams = {},
+ ) =>
+ this.http.request<{ ok: true }, any>({
+ path: `/smtp/test`,
+ method: 'POST',
+ body: data,
+ type: ContentType.Json,
+ ...params,
+ });
}
diff --git a/toolkit/src/api/instance.ts b/toolkit/src/api/instance.ts
index 1895fd2..0ff66e9 100644
--- a/toolkit/src/api/instance.ts
+++ b/toolkit/src/api/instance.ts
@@ -1,12 +1,11 @@
-import { HttpClient } from './HttpClient';
// Auto-generated API instance
-
import { Article } from './Article';
import { Auth } from './Auth';
import { Category } from './Category';
import { Comment } from './Comment';
import { File } from './File';
+import { HttpClient } from './HttpClient';
import { Knowledge } from './Knowledge';
import { Page } from './Page';
import { Search } from './Search';
diff --git a/toolkit/src/app/createReactPressApp.d.ts b/toolkit/src/app/createReactPressApp.d.ts
new file mode 100644
index 0000000..6a19ec0
--- /dev/null
+++ b/toolkit/src/app/createReactPressApp.d.ts
@@ -0,0 +1,8 @@
+/**
+ * Full-featured theme `_app` factory (UI-framework agnostic).
+ * Alias: `createCatalogThemeApp` (deprecated name).
+ */
+export function createReactPressApp(
+ manifest: Record,
+ options?: Record,
+): unknown;
diff --git a/toolkit/src/app/createReactPressApp.js b/toolkit/src/app/createReactPressApp.js
new file mode 100644
index 0000000..0406efc
--- /dev/null
+++ b/toolkit/src/app/createReactPressApp.js
@@ -0,0 +1,352 @@
+const React = require('react');
+const App = require('next/app').default;
+const Router = require('next/router').default;
+
+const { fetchAppBootstrap, createDefaultAppBootstrap } = require('../theme/ssr/bootstrap');
+const { slimAppBootstrapForRoute } = require('../theme/ssr/slimBootstrap');
+const { createThemeAxiosClient } = require('../theme/api/httpClient');
+const { createThemeProviders } = require('../theme/providers');
+const {
+ applyColorModeClass,
+ resolveInitialColorModeState,
+ persistColorMode,
+} = require('../theme/visitor/colorMode');
+const {
+ persistVisitorLocale,
+ readBrowserCookie,
+ VISITOR_LOCALE_COOKIE,
+ LEGACY_LOCALE_STORAGE_KEY,
+} = require('../theme/visitor/visitorLocale');
+const {
+ clearThemeSession,
+ persistThemeSession,
+ resolveStoredUser,
+} = require('../theme/visitor/authSession');
+const { RouteProgress } = require('../ui/components/RouteProgress');
+const { SiteAnalytics } = require('../ui/components/SiteAnalytics');
+const { SiteCatalogProvider } = require('../ui/context/SiteCatalogContext');
+const { useReportPageView } = require('../ui/hooks/useReportPageView');
+const { safeJsonParse } = require('../theme/api/json');
+const { mergeVisitorI18n } = require('../theme/visitor/i18n');
+
+function resolveGlobalSettingForLocale(setting, locale, fallback) {
+ if (!setting?.globalSetting) return fallback;
+ const raw = setting.globalSetting;
+ const parsed =
+ typeof raw === 'string' ? safeJsonParse(raw, {}) : raw && typeof raw === 'object' ? raw : {};
+ return parsed[locale] ?? fallback;
+}
+
+function readStoredVisitorLocale(locales) {
+ if (typeof window === 'undefined') return null;
+ try {
+ const supported = Array.isArray(locales) ? locales : [];
+ const stored =
+ window.localStorage.getItem('reactpress-locale') ||
+ window.localStorage.getItem('locale');
+ if (stored && supported.includes(stored)) return stored;
+ } catch {
+ // ignore
+ }
+ return null;
+}
+
+function ViewStatisticsBridge() {
+ useReportPageView();
+ return null;
+}
+
+/**
+ * Full-featured theme `_app` factory (UI-framework agnostic).
+ * Alias: `createCatalogThemeApp` (deprecated name).
+ * @param {Record} manifest
+ * @param {Record} [options]
+ * @returns {unknown}
+ */
+function createReactPressApp(manifest, options = {}) {
+ const {
+ Layout,
+ buildAppearanceCss,
+ httpClientOptions = {},
+ scrollToTopOnRouteChange = true,
+ IntlProvider,
+ wrapContent,
+ transformBootstrap,
+ /** When true, apply default catalog-theme SSR payload slimming (nav routes keep full siteConfig). */
+ slimBootstrap = false,
+ } = options;
+
+ if (!Layout) {
+ throw new Error('createReactPressApp: options.Layout is required');
+ }
+ if (!IntlProvider) {
+ throw new Error(
+ 'createReactPressApp: options.IntlProvider is required — import { NextIntlProvider } from "next-intl" in your theme',
+ );
+ }
+
+ if (scrollToTopOnRouteChange) {
+ Router.events.on('routeChangeComplete', () => {
+ setTimeout(() => {
+ if (document.documentElement.scrollTop > 0) {
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }
+ }, 0);
+ });
+ }
+
+ class ReactPressThemeApp extends App {
+ state = {
+ locale: '',
+ user: null,
+ /** Keep SSR and the first client render identical; sync from storage in componentDidMount. */
+ theme: 'light',
+ collapsed: false,
+ setting: null,
+ /** Full locale catalog — SSR may ship only the active locale to shrink `__NEXT_DATA__`. */
+ i18n: null,
+ };
+
+ prefetchRemainingI18nLocales = () => {
+ const locales = this.props.locales;
+ if (!Array.isArray(locales) || locales.length <= 1) return;
+
+ const current = this.state.i18n ?? this.props.i18n ?? {};
+ if (locales.every((key) => current[key])) return;
+
+ const http = createThemeAxiosClient(httpClientOptions);
+ const { SettingProvider } = createThemeProviders(http);
+ SettingProvider.getSetting()
+ .then((res) => {
+ const full = mergeVisitorI18n(safeJsonParse(res.i18n, {}));
+ this.setState((prev) => ({
+ i18n: { ...(prev.i18n ?? this.props.i18n ?? {}), ...full },
+ }));
+ })
+ .catch(() => {
+ // ignore — active locale messages already available from SSR
+ });
+ };
+
+ static getInitialProps = async (appContext) => {
+ const appProps = await App.getInitialProps(appContext);
+ let bootstrap;
+ try {
+ bootstrap = await fetchAppBootstrap({
+ manifest,
+ ctx: appContext.ctx,
+ });
+ } catch (error) {
+ console.error('[reactpress] fetchAppBootstrap failed, using defaults', error);
+ bootstrap = createDefaultAppBootstrap();
+ }
+
+ if (typeof transformBootstrap === 'function') {
+ bootstrap = transformBootstrap(bootstrap, appContext.ctx);
+ } else if (slimBootstrap) {
+ bootstrap = slimAppBootstrapForRoute(
+ bootstrap,
+ appContext.ctx.pathname ?? '/',
+ typeof slimBootstrap === 'object' ? slimBootstrap : undefined,
+ );
+ }
+
+ return {
+ ...appProps,
+ ...bootstrap,
+ pageProps: appProps.pageProps,
+ };
+ };
+
+ changeLocale = (key) => {
+ if (!key) return;
+ const active =
+ this.state.locale ||
+ this.props.initialLocale ||
+ (Array.isArray(this.props.locales) && this.props.locales[0]) ||
+ 'zh';
+ if (key === active) return;
+ persistVisitorLocale(key);
+
+ const catalog = this.state.i18n ?? this.props.i18n ?? {};
+ if (!catalog[key]) {
+ this.prefetchRemainingI18nLocales();
+ }
+
+ this.setState({ locale: key });
+ };
+
+ setUser = (user) => {
+ persistThemeSession(user);
+ this.setState({ user });
+ };
+
+ removeUser = () => {
+ clearThemeSession();
+ this.setState({ user: null });
+ window.location.reload();
+ };
+
+ changeTheme = (nextTheme) => {
+ const isDark = nextTheme === 'dark';
+ applyColorModeClass(isDark);
+ persistColorMode(isDark);
+ this.setState({ theme: nextTheme });
+ };
+
+ getSetting = () => {
+ const http = createThemeAxiosClient(httpClientOptions);
+ const { SettingProvider } = createThemeProviders(http);
+ SettingProvider.getSetting().then((res) => {
+ this.setState({ setting: res });
+ });
+ };
+
+ toggleCollapse = () => {
+ this.setState({ collapsed: !this.state.collapsed });
+ };
+
+ componentDidMount() {
+ const user = resolveStoredUser();
+ if (user) {
+ persistThemeSession(user);
+ this.setState({ user });
+ }
+
+ const preferred = resolveInitialColorModeState() ?? 'light';
+ applyColorModeClass(preferred === 'dark');
+
+ const patches = {};
+ if (this.state.theme !== preferred) {
+ patches.theme = preferred;
+ }
+
+ const ssrLocale =
+ this.props.initialLocale ||
+ (Array.isArray(this.props.locales) && this.props.locales.length > 0
+ ? this.props.locales[0]
+ : 'zh');
+ const cookieLocale =
+ readBrowserCookie(VISITOR_LOCALE_COOKIE) ||
+ readBrowserCookie(LEGACY_LOCALE_STORAGE_KEY);
+ const storedLocale = readStoredVisitorLocale(this.props.locales);
+
+ if (this.state.locale === '') {
+ if (
+ cookieLocale &&
+ Array.isArray(this.props.locales) &&
+ this.props.locales.includes(cookieLocale) &&
+ cookieLocale !== ssrLocale
+ ) {
+ // Cookie is the source of truth for an explicit prior choice.
+ patches.locale = cookieLocale;
+ } else if (!cookieLocale) {
+ // First visit or stale storage — align cookie/storage with SSR locale.
+ persistVisitorLocale(ssrLocale);
+ } else if (storedLocale && storedLocale !== ssrLocale) {
+ persistVisitorLocale(ssrLocale);
+ }
+ }
+
+ if (Object.keys(patches).length > 0) {
+ this.setState(patches);
+ }
+
+ this.prefetchRemainingI18nLocales();
+ }
+
+ componentDidUpdate(_prevProps, prevState) {
+ if (prevState.theme !== this.state.theme) {
+ applyColorModeClass(this.state.theme === 'dark');
+ }
+ }
+
+ render() {
+ const {
+ Component,
+ pageProps,
+ i18n: i18nFromProps,
+ globalSetting,
+ siteConfig,
+ locales,
+ initialLocale,
+ tags,
+ categories,
+ pages,
+ colorPrimary,
+ themeMods = {},
+ ...rest
+ } = this.props;
+
+ const i18n = this.state.i18n ?? i18nFromProps;
+
+ const locale =
+ this.state.locale ||
+ initialLocale ||
+ (Array.isArray(locales) && locales.length > 0 ? locales[0] : null) ||
+ 'zh';
+
+ const { needLayoutFooter = true, hasBg = false } = pageProps;
+ const message = i18n?.[locale] || {};
+ const isDark = this.state.theme === 'dark';
+ const appearanceCss = buildAppearanceCss?.(themeMods);
+ const setting = this.state.setting ?? rest.setting;
+
+ const catalogValue = {
+ setting,
+ i18n,
+ locale,
+ locales,
+ globalSetting,
+ siteConfig,
+ tags,
+ categories,
+ pages,
+ theme: this.state.theme,
+ collapsed: this.state.collapsed,
+ changeLocale: this.changeLocale,
+ user: this.state.user,
+ setUser: this.setUser,
+ removeUser: this.removeUser,
+ changeTheme: this.changeTheme,
+ getSetting: this.getSetting,
+ toggleCollapse: this.toggleCollapse,
+ };
+
+ const layout = React.createElement(
+ Layout,
+ {
+ needHeader: true,
+ needFooter: needLayoutFooter,
+ hasBg,
+ },
+ React.createElement(RouteProgress, null),
+ React.createElement(Component, pageProps),
+ );
+
+ const runtime = { locale, isDark, colorPrimary, themeMods, colorMode: this.state.theme };
+ const content = wrapContent ? wrapContent(layout, runtime) : layout;
+
+ return React.createElement(
+ SiteCatalogProvider,
+ { value: catalogValue },
+ React.createElement(
+ IntlProvider,
+ { messages: message, locale },
+ appearanceCss
+ ? React.createElement('style', {
+ dangerouslySetInnerHTML: { __html: appearanceCss },
+ })
+ : null,
+ React.createElement(ViewStatisticsBridge, null),
+ React.createElement(SiteAnalytics, null),
+ content,
+ ),
+ );
+ }
+ }
+
+ return ReactPressThemeApp;
+}
+
+module.exports = { createReactPressApp };
diff --git a/toolkit/src/app/createThemeApp.d.ts b/toolkit/src/app/createThemeApp.d.ts
new file mode 100644
index 0000000..f66482c
--- /dev/null
+++ b/toolkit/src/app/createThemeApp.d.ts
@@ -0,0 +1,2 @@
+/** Minimal theme `_app` — hello-world style, ReactPressProvider + ThemeCssVars only. */
+export function createThemeApp(manifest: { id: string }): unknown;
diff --git a/toolkit/src/app/createThemeApp.js b/toolkit/src/app/createThemeApp.js
new file mode 100644
index 0000000..bb71f22
--- /dev/null
+++ b/toolkit/src/app/createThemeApp.js
@@ -0,0 +1,61 @@
+const React = require('react');
+const { createDefaultVisitorContext } = require('../theme/ssr/fetch');
+const { ReactPressProvider } = require('../ui/context/ReactPressProvider');
+const { ThemeCssVars } = require('../ui/components/ThemeCssVars');
+const { PREVIEW_TOKEN_QUERY_KEY } = require('../theme/preview/preview-draft');
+const { fetchPreviewDraft } = require('../theme/extension/preview');
+const { mergePreviewMods } = require('../theme/preview/preview-mods');
+const { DevChunkRecovery } = require('./devChunkRecovery');
+
+function readPreviewTokenFromBrowser() {
+ if (typeof window === 'undefined') return '';
+ return new URLSearchParams(window.location.search).get(PREVIEW_TOKEN_QUERY_KEY)?.trim() ?? '';
+}
+
+/**
+ * Minimal theme `_app` — hello-world style, ReactPressProvider + ThemeCssVars only.
+ * @param {{ id: string }} manifest
+ * @returns {unknown}
+ */
+function createThemeApp(manifest) {
+ function ThemeApp({ Component, pageProps }) {
+ const { reactPress, ...rest } = pageProps;
+ const visitorContext = reactPress ?? createDefaultVisitorContext(manifest.id);
+ const [mods, setMods] = React.useState(() => visitorContext.mods ?? {});
+
+ React.useEffect(() => {
+ const base = visitorContext.mods ?? {};
+ const token = readPreviewTokenFromBrowser();
+ if (!token) {
+ setMods(base);
+ return undefined;
+ }
+ let cancelled = false;
+ void fetchPreviewDraft(token).then((draft) => {
+ if (cancelled) return;
+ setMods(mergePreviewMods(base, draft.mods ?? {}));
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, [reactPress, visitorContext.mods]);
+
+ const providerProps = {
+ ...visitorContext,
+ mods,
+ isPreview: visitorContext.isPreview || Boolean(readPreviewTokenFromBrowser()),
+ };
+
+ return React.createElement(
+ ReactPressProvider,
+ providerProps,
+ React.createElement(DevChunkRecovery, null),
+ React.createElement(ThemeCssVars, null),
+ React.createElement(Component, rest),
+ );
+ }
+
+ return ThemeApp;
+}
+
+module.exports = { createThemeApp };
diff --git a/toolkit/src/app/devChunkRecovery.d.ts b/toolkit/src/app/devChunkRecovery.d.ts
new file mode 100644
index 0000000..3d480fb
--- /dev/null
+++ b/toolkit/src/app/devChunkRecovery.d.ts
@@ -0,0 +1,2 @@
+/** Dev-only: recover from stale webpack chunks after on-demand page compilation. */
+export function DevChunkRecovery(): null;
diff --git a/toolkit/src/app/devChunkRecovery.js b/toolkit/src/app/devChunkRecovery.js
new file mode 100644
index 0000000..d6676a0
--- /dev/null
+++ b/toolkit/src/app/devChunkRecovery.js
@@ -0,0 +1,73 @@
+const React = require('react');
+
+/** Dev-only: recover from stale webpack chunks after on-demand page compilation. */
+function DevChunkRecovery() {
+ React.useEffect(() => {
+ if (process.env.NODE_ENV !== 'development') return undefined;
+
+ const storageKey = '__reactpress_dev_chunk_recover';
+ const maxAttempts = 2;
+
+ const isChunkMismatch = (message) => {
+ const msg = String(message || '');
+ return (
+ msg.includes("reading 'call'") ||
+ msg.includes('ChunkLoadError') ||
+ msg.includes('Loading chunk') ||
+ msg.includes('__webpack_modules__') ||
+ msg.includes('is not a function') ||
+ msg.includes('/_next/undefined')
+ );
+ };
+
+ const recover = () => {
+ const attempts = Number(window.sessionStorage.getItem(storageKey) || 0);
+ if (attempts >= maxAttempts) return;
+ window.sessionStorage.setItem(storageKey, String(attempts + 1));
+ window.location.reload();
+ };
+
+ const onError = (event) => {
+ const msg = event?.message || event?.reason?.message || event?.reason || '';
+ if (!isChunkMismatch(msg)) return;
+ if (typeof event.preventDefault === 'function') event.preventDefault();
+ recover();
+ };
+
+ window.addEventListener('error', onError);
+ window.addEventListener('unhandledrejection', onError);
+
+ const clearRecoveries = () => window.sessionStorage.removeItem(storageKey);
+ window.addEventListener('load', clearRecoveries);
+
+ let routerCleanup = () => {};
+ try {
+ const Router = require('next/router').default;
+ if (Router?.events?.on) {
+ const onRouteError = (err) => {
+ if (err?.cancelled) return;
+ if (isChunkMismatch(err?.message)) recover();
+ };
+ Router.events.on('routeChangeComplete', clearRecoveries);
+ Router.events.on('routeChangeError', onRouteError);
+ routerCleanup = () => {
+ Router.events.off('routeChangeComplete', clearRecoveries);
+ Router.events.off('routeChangeError', onRouteError);
+ };
+ }
+ } catch {
+ // App Router themes rely on window error listeners only.
+ }
+
+ return () => {
+ window.removeEventListener('error', onError);
+ window.removeEventListener('unhandledrejection', onError);
+ window.removeEventListener('load', clearRecoveries);
+ routerCleanup();
+ };
+ }, []);
+
+ return null;
+}
+
+module.exports = { DevChunkRecovery };
diff --git a/toolkit/src/app/index.ts b/toolkit/src/app/index.ts
new file mode 100644
index 0000000..103d586
--- /dev/null
+++ b/toolkit/src/app/index.ts
@@ -0,0 +1,47 @@
+/** Type declarations for `@fecommunity/reactpress-toolkit/app` JS factories. */
+import type { ComponentType, ReactNode } from 'react';
+import type { ThemeMods } from '../theme/extension/branding-mods';
+
+export type CreateThemeApp = (manifest: { id: string }) => unknown;
+
+export type CreateReactPressAppOptions = {
+ Layout: ComponentType<{
+ children?: ReactNode;
+ needFooter?: boolean;
+ hasBg?: boolean;
+ needHeader?: boolean;
+ }>;
+ /** i18n root provider (e.g. next-intl NextIntlProvider). */
+ IntlProvider: ComponentType>;
+ buildAppearanceCss?: (mods: ThemeMods) => string;
+ httpClientOptions?: Record;
+ wrapContent?: (
+ content: ReactNode,
+ runtime: {
+ locale: string;
+ isDark: boolean;
+ colorPrimary?: string;
+ themeMods: ThemeMods;
+ colorMode: string;
+ },
+ ) => unknown;
+ scrollToTopOnRouteChange?: boolean;
+ transformBootstrap?: (
+ bootstrap: Record,
+ ctx: { pathname?: string; asPath?: string },
+ ) => Record;
+};
+
+export type CreateReactPressApp = (
+ manifest: { id: string; options?: unknown },
+ options: CreateReactPressAppOptions,
+) => unknown;
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+export const createThemeApp = require('./createThemeApp').createThemeApp as CreateThemeApp;
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+export const DevChunkRecovery = require('./devChunkRecovery').DevChunkRecovery as ComponentType;
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+export const createReactPressApp = require('./createReactPressApp').createReactPressApp as CreateReactPressApp;
+/** @deprecated Use `createReactPressApp`. */
+export const createCatalogThemeApp = createReactPressApp;
diff --git a/toolkit/src/config/env.ts b/toolkit/src/config/env.ts
index 9faa9ff..5a04ed0 100644
--- a/toolkit/src/config/env.ts
+++ b/toolkit/src/config/env.ts
@@ -1,6 +1,6 @@
+import * as dotenv from 'dotenv';
import * as fs from 'fs-extra';
import * as path from 'path';
-import * as dotenv from 'dotenv';
const isProd = process.env.NODE_ENV === 'production';
diff --git a/toolkit/src/config/global.ts b/toolkit/src/config/global.ts
index 1c4ad33..7cf0229 100644
--- a/toolkit/src/config/global.ts
+++ b/toolkit/src/config/global.ts
@@ -1,3 +1,6 @@
+import type { SiteThemeState } from '../theme/extension/theme';
+import { defaultSiteThemeState } from '../theme/extension/theme';
+
interface NavItem {
label: string;
key: string;
@@ -423,12 +426,19 @@ const en: LanguageConfig = {
interface GlobalSetting {
zh: LanguageConfig;
en: LanguageConfig;
+ theme: SiteThemeState;
}
const globalSetting: GlobalSetting = {
zh,
en,
+ theme: defaultSiteThemeState,
+};
+
+/** Persisted in DB `Setting.globalSetting` — theme runtime only (nav/content lives in `config[themeId]`). */
+export const systemGlobalSettingDefaults = {
+ theme: defaultSiteThemeState,
};
-export type { GlobalSetting, LanguageConfig, GlobalConfig, NavConfig, NavItem, UrlCategory };
+export type { GlobalConfig, GlobalSetting, LanguageConfig, NavConfig, NavItem, SiteThemeState,UrlCategory };
export { globalSetting };
\ No newline at end of file
diff --git a/toolkit/src/config/i18n.ts b/toolkit/src/config/i18n.ts
index 46359b6..facb542 100644
--- a/toolkit/src/config/i18n.ts
+++ b/toolkit/src/config/i18n.ts
@@ -12,14 +12,16 @@ interface I18nResult {
}
function parseI18n(): I18nResult {
- // Locales are now in the dist directory, one level up from config
- const localesDir = path.join(__dirname, '../locales');
+ const localesDir = path.join(__dirname, 'locales');
if (!fs.existsSync(localesDir)) {
return { messages: {}, locales: [], defaultLocale: '' };
}
- const files = fs.readdirSync(localesDir);
+ const files = fs.readdirSync(localesDir).filter((file) => {
+ const filePath = path.join(localesDir, file);
+ return file.endsWith('.json') && fs.statSync(filePath).isFile();
+ });
const messages: I18nMessages = files.reduce((i18n: I18nMessages, file: string) => {
const language = file.replace(path.extname(file), '');
const json = fs.readJsonSync(path.join(localesDir, file));
diff --git a/toolkit/src/config/index.ts b/toolkit/src/config/index.ts
index 6b67286..1d6a48e 100644
--- a/toolkit/src/config/index.ts
+++ b/toolkit/src/config/index.ts
@@ -2,8 +2,8 @@
* 环境变量配置
*/
import { config as envConfig, file } from './env';
-import { messages, locales, defaultLocale } from './i18n';
-import { globalSetting } from './global';
+import { globalSetting, systemGlobalSettingDefaults } from './global';
+import { defaultLocale,locales, messages } from './i18n';
// Create a unified config object that includes all configuration properties
const config = {
@@ -15,6 +15,6 @@ const config = {
file
};
-export { config, file, messages, locales, defaultLocale, globalSetting };
+export { config, defaultLocale, file, globalSetting, locales, messages, systemGlobalSettingDefaults };
export default config;
\ No newline at end of file
diff --git a/toolkit/src/locales/en.json b/toolkit/src/config/locales/en.json
similarity index 73%
rename from toolkit/src/locales/en.json
rename to toolkit/src/config/locales/en.json
index a2240f0..2bf78cf 100644
--- a/toolkit/src/locales/en.json
+++ b/toolkit/src/config/locales/en.json
@@ -69,12 +69,17 @@
"openAuth": "Use third-party login",
"areYouHasAccount": "Do you already have an account?",
"loginNow": "Login now",
- "toggleNeedSetInfo": "Incomplete email information, please complete personal information."
+ "toggleNeedSetInfo": "Incomplete email information, please complete personal information.",
+ "guestCommentLead": "Enter your name and email to comment — no login required.",
+ "commentingAs": "Commenting as {name}"
},
"loading": "Loading",
- "copySuccess": "Copy successfully",
+ "copySuccess": "Copied",
+ "shareLinkCopied": "Share link copied",
"copy": "copy",
"article": "Articles",
+ "skipToContent": "Skip to content",
+ "search": "Search",
"searchArticle": "Search Articles",
"searchArticlePlaceholder": "Enter keywords, search articles",
"shareNamespace": {
@@ -93,5 +98,28 @@
"rssSubscribe": "RSS Subscription",
"aboutUs": "About Us",
"nav": "Navigation",
- "suggestions": "Suggestions"
+ "suggestions": "Suggestions",
+ "theme": {
+ "toggle": "Toggle theme"
+ },
+ "locale": {
+ "switch": "Switch language",
+ "switchToEn": "Switch to English",
+ "switchToZh": "Switch to Chinese"
+ },
+ "pageTitleNavSite": "Site Navigation",
+ "pageTitleLogin": "Login",
+ "pageTitleTag": "Tag: {label}",
+ "pageTitleCategory": "Category: {label}",
+ "pageTitleNavDetail": "Navigation: {label}",
+ "navPreviewDisclaimer": "This site only provides content previews and does not store any information. Please protect your account and property. If content cannot be previewed, click \"Open site\" to view it externally.",
+ "navOpenSite": "Open site ›",
+ "commentShowMore": "Show more ↓",
+ "commentShowLess": "Collapse ↑",
+ "navExpandMenu": "Expand menu",
+ "navCollapseMenu": "Collapse menu",
+ "navCategoryMenu": "Navigation categories",
+ "carouselPrev": "Previous slide, {current} of {total}",
+ "carouselNext": "Next slide, {current} of {total}",
+ "aboutUsFallback": "ReactPress — Modern blog publishing platform"
}
\ No newline at end of file
diff --git a/toolkit/src/locales/zh.json b/toolkit/src/config/locales/zh.json
similarity index 72%
rename from toolkit/src/locales/zh.json
rename to toolkit/src/config/locales/zh.json
index 3aa6278..923ab0c 100644
--- a/toolkit/src/locales/zh.json
+++ b/toolkit/src/config/locales/zh.json
@@ -67,14 +67,19 @@
"openAuth": "第三方登录",
"areYouHasAccount": "已有账号?",
"loginNow": "立即登录",
- "toggleNeedSetInfo": "邮箱信息不完整,请完善个人资料。"
+ "toggleNeedSetInfo": "邮箱信息不完整,请完善个人资料。",
+ "guestCommentLead": "填写昵称与邮箱即可评论,无需登录。",
+ "commentingAs": "以 {name} 的身份发表评论"
},
"loading": "加载中",
- "copySuccess": "复制成功",
+ "copySuccess": "已复制",
+ "shareLinkCopied": "分享链接复制成功",
"copy": "复制",
"article": "文章",
"home": "首页",
+ "skipToContent": "跳到正文",
"knowledge": "专辑",
+ "search": "搜索",
"searchArticle": "文章搜索",
"searchArticlePlaceholder": "输入关键字,搜索文章",
"shareNamespace": {
@@ -93,5 +98,28 @@
"rssSubscribe": "RSS 订阅",
"aboutUs": "关于我们",
"nav": "导航",
- "suggestions": "建议"
+ "suggestions": "建议",
+ "theme": {
+ "toggle": "切换主题"
+ },
+ "locale": {
+ "switch": "切换语言",
+ "switchToEn": "切换到 English",
+ "switchToZh": "切换到中文"
+ },
+ "pageTitleNavSite": "网址导航",
+ "pageTitleLogin": "登录",
+ "pageTitleTag": "标签:{label}",
+ "pageTitleCategory": "分类:{label}",
+ "pageTitleNavDetail": "导航:{label}",
+ "navPreviewDisclaimer": "本站只做内容预览,不做任何信息存储,请注意您的账号和财产安全。如遇内容无法预览,请点击「打开网站」按钮预览。",
+ "navOpenSite": "打开网站 ›",
+ "commentShowMore": "查看更多 ↓",
+ "commentShowLess": "收起 ↑",
+ "navExpandMenu": "展开目录",
+ "navCollapseMenu": "收起目录",
+ "navCategoryMenu": "导航分类",
+ "carouselPrev": "上一张,第 {current} 张,共 {total} 张",
+ "carouselNext": "下一张,第 {current} 张,共 {total} 张",
+ "aboutUsFallback": "ReactPress — 现代化博客发布平台"
}
\ No newline at end of file
diff --git a/toolkit/src/index.ts b/toolkit/src/index.ts
index 56f1ea8..ffdb3cd 100644
--- a/toolkit/src/index.ts
+++ b/toolkit/src/index.ts
@@ -1,15 +1,22 @@
-// Automatically generated API toolkit for ReactPress
-// Do not manually modify this file
-// Generated at: 9/25/2025, 9:47:27 PM
+// Public subpath modules: api, types, ui, utils, theme, plugin, config
-import { http as httpInstance, api, createApiInstance } from './api/instance';
-import * as types from './types';
-import * as utils from './utils';
-import * as config from './config';
+export * as config from './config';
+export * as plugin from './plugin';
+export * as theme from './theme';
+export * as types from './types';
+export * as ui from './ui';
+export * as utils from './utils';
-const http = {
+/** @deprecated Prefer `@fecommunity/reactpress-toolkit/api` subpath imports. */
+export { api, createApiInstance, http as httpInstance } from './api/instance';
+export * from './config';
+export * from './types';
+export * from './utils';
+
+import { createApiInstance, http as httpInstance } from './api/instance';
+
+/** @deprecated Prefer `createApiInstance` from `@fecommunity/reactpress-toolkit/api/instance`. */
+export const http = {
...httpInstance,
createApiInstance,
};
-
-export { api, types, utils, config, http };
diff --git a/toolkit/src/plugin/admin/index.ts b/toolkit/src/plugin/admin/index.ts
new file mode 100644
index 0000000..d15903b
--- /dev/null
+++ b/toolkit/src/plugin/admin/index.ts
@@ -0,0 +1,2 @@
+export * from './permissions';
+export * from './registry';
diff --git a/toolkit/src/plugin/admin/permissions.ts b/toolkit/src/plugin/admin/permissions.ts
new file mode 100644
index 0000000..ea2aab3
--- /dev/null
+++ b/toolkit/src/plugin/admin/permissions.ts
@@ -0,0 +1,42 @@
+/** Admin capability strings — keep in sync with server guards. */
+export type Permission =
+ | 'article:read'
+ | 'article:write'
+ | 'article:publish'
+ | 'media:manage'
+ | 'page:manage'
+ | 'user:manage'
+ | 'setting:manage'
+ | 'extension:manage'
+ | 'comment:manage'
+ | 'view:read';
+
+export const ADMIN_PERMISSIONS: Permission[] = [
+ 'article:read',
+ 'article:write',
+ 'article:publish',
+ 'media:manage',
+ 'page:manage',
+ 'user:manage',
+ 'setting:manage',
+ 'extension:manage',
+ 'comment:manage',
+ 'view:read',
+];
+
+/** Map legacy server roles to capability strings. */
+export function permissionsForRole(role: string | undefined): Permission[] {
+ if (role === 'admin') return [...ADMIN_PERMISSIONS];
+ if (role === 'editor') {
+ return [
+ 'article:read',
+ 'article:write',
+ 'article:publish',
+ 'media:manage',
+ 'page:manage',
+ 'comment:manage',
+ 'view:read',
+ ];
+ }
+ return ['article:read', 'view:read'];
+}
diff --git a/toolkit/src/plugin/admin/registry.ts b/toolkit/src/plugin/admin/registry.ts
new file mode 100644
index 0000000..be97d6e
--- /dev/null
+++ b/toolkit/src/plugin/admin/registry.ts
@@ -0,0 +1,156 @@
+import type { Permission } from './permissions';
+
+export type MenuNodeKind = 'group' | 'item';
+
+export interface AdminMenuItem {
+ id: string;
+ kind: MenuNodeKind;
+ title: string;
+ path: string | null;
+ icon?: string | null;
+ sort?: number;
+ hidden?: boolean;
+ permissions?: Permission[] | null;
+ children?: AdminMenuItem[];
+}
+
+export interface MenuRegisterInput {
+ id: string;
+ title: string;
+ path?: string | null;
+ icon?: string | null;
+ sort?: number;
+ hidden?: boolean;
+ permissions?: Permission[] | null;
+ children?: MenuRegisterInput[];
+}
+
+export interface SettingsTabInput {
+ id: string;
+ title: string;
+ path: string;
+ permission?: Permission;
+ sort?: number;
+}
+
+export interface RoutePermissionInput {
+ path: string;
+ permission: Permission | null;
+}
+
+export interface MenuRegistry {
+ register(input: MenuRegisterInput): void;
+ getTree(): AdminMenuItem[];
+}
+
+export interface SettingsRegistry {
+ registerTab(input: SettingsTabInput): void;
+ getTabs(): SettingsTabInput[];
+}
+
+export interface PermissionRegistry {
+ register(perms: Permission[]): void;
+ getAll(): Permission[];
+}
+
+export interface RouteRegistry {
+ registerRoute(input: RoutePermissionInput): void;
+ getRoutePermissions(): RoutePermissionInput[];
+}
+
+export interface AdminContext {
+ menu: MenuRegistry;
+ settings: SettingsRegistry;
+ permissions: PermissionRegistry;
+ routes: RouteRegistry;
+}
+
+export interface AdminModule {
+ id: string;
+ register(ctx: AdminContext): void;
+}
+
+function normalizeMenuInput(input: MenuRegisterInput): AdminMenuItem {
+ const hasChildren = Boolean(input.children?.length);
+ return {
+ id: input.id,
+ kind: hasChildren && !input.path ? 'group' : 'item',
+ title: input.title,
+ path: input.path ?? (hasChildren ? null : '/'),
+ icon: input.icon ?? null,
+ sort: input.sort ?? 0,
+ hidden: input.hidden ?? false,
+ permissions: input.permissions ?? null,
+ children: input.children?.map(normalizeMenuInput),
+ };
+}
+
+export function createAdminRegistry(): AdminContext {
+ const menuItems: AdminMenuItem[] = [];
+ const settingsTabs: SettingsTabInput[] = [];
+ const permissions = new Set();
+ const routePermissions: RoutePermissionInput[] = [];
+
+ const menu: MenuRegistry = {
+ register(input) {
+ menuItems.push(normalizeMenuInput(input));
+ },
+ getTree() {
+ return [...menuItems].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
+ },
+ };
+
+ const settings: SettingsRegistry = {
+ registerTab(input) {
+ settingsTabs.push(input);
+ },
+ getTabs() {
+ return [...settingsTabs].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
+ },
+ };
+
+ const permissionRegistry: PermissionRegistry = {
+ register(perms) {
+ for (const p of perms) permissions.add(p);
+ },
+ getAll() {
+ return [...permissions];
+ },
+ };
+
+ const routes: RouteRegistry = {
+ registerRoute(input) {
+ routePermissions.push(input);
+ },
+ getRoutePermissions() {
+ return routePermissions;
+ },
+ };
+
+ return { menu, settings, permissions: permissionRegistry, routes };
+}
+
+export function filterMenuByPermissions(
+ nodes: AdminMenuItem[],
+ granted: Set,
+): AdminMenuItem[] {
+ const can = (required: Permission[] | null | undefined) => {
+ if (!required?.length) return true;
+ return required.every((p) => granted.has(p));
+ };
+
+ const walk = (list: AdminMenuItem[]): AdminMenuItem[] =>
+ list
+ .map((node) => {
+ if (!can(node.permissions)) return null;
+ if (node.children?.length) {
+ const children = walk(node.children);
+ if (children.length === 0) return null;
+ return { ...node, children };
+ }
+ return node;
+ })
+ .filter((n): n is AdminMenuItem => n != null);
+
+ return walk(nodes);
+}
diff --git a/toolkit/src/plugin/client/client.ts b/toolkit/src/plugin/client/client.ts
new file mode 100644
index 0000000..e5cac23
--- /dev/null
+++ b/toolkit/src/plugin/client/client.ts
@@ -0,0 +1,125 @@
+import type { HttpClient } from '../../api/HttpClient';
+import { createApiInstance } from '../../api/instance';
+
+export class ApiError extends Error {
+ code: number;
+ constructor(code: number, message: string) {
+ super(message);
+ this.name = 'ApiError';
+ this.code = code;
+ }
+}
+
+export interface ClientOptions {
+ baseURL?: string;
+ getAccessToken?: () => string | null | undefined;
+ onUnauthorized?: () => void;
+}
+
+export type ReactPressClient = ReturnType;
+
+function readApiMessage(value: unknown): string | null {
+ if (typeof value === 'string') {
+ const trimmed = value.trim();
+ return trimmed ? trimmed : null;
+ }
+
+ if (Array.isArray(value)) {
+ const parts = value
+ .map((item) => readApiMessage(item))
+ .filter((item): item is string => Boolean(item));
+ return parts.length ? parts.join(', ') : null;
+ }
+
+ if (value && typeof value === 'object') {
+ const payload = value as Record;
+ return readApiMessage(payload.message) ?? readApiMessage(payload.msg);
+ }
+
+ return null;
+}
+
+function rejectApiError(status: number | undefined, payload: Record) {
+ if (typeof payload.code === 'number' && payload.code !== 0) {
+ return Promise.reject(
+ new ApiError(payload.code, readApiMessage(payload.message) ?? 'Request failed'),
+ );
+ }
+
+ const message = readApiMessage(payload.msg) ?? readApiMessage(payload.message);
+ if (message) {
+ return Promise.reject(new ApiError(Number(payload.statusCode ?? status ?? 500), message));
+ }
+
+ return null;
+}
+
+function attachInterceptors(http: HttpClient, options: ClientOptions) {
+ http.instance.interceptors.request.use((config) => {
+ const token = options.getAccessToken?.();
+ if (token && config.headers) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+ });
+
+ http.instance.interceptors.response.use(
+ (response) => {
+ const body = response.data as Record | null;
+ if (!body || typeof body !== 'object') return response;
+
+ if (typeof body.code === 'number') {
+ if (body.code !== 0) {
+ throw new ApiError(
+ body.code as number,
+ readApiMessage(body.message) ?? 'Request failed',
+ );
+ }
+ response.data = body.data;
+ return response;
+ }
+
+ if (typeof body.success === 'boolean') {
+ if (!body.success) {
+ throw new ApiError(
+ Number(body.statusCode ?? 500),
+ readApiMessage(body.msg) ?? 'Request failed',
+ );
+ }
+ response.data = body.data;
+ return response;
+ }
+
+ return response;
+ },
+ (error) => {
+ const status = error?.response?.status;
+ if (status === 401) {
+ options.onUnauthorized?.();
+ }
+
+ const body = error?.response?.data;
+ if (body && typeof body === 'object') {
+ const rejected = rejectApiError(status, body as Record);
+ if (rejected) return rejected;
+ }
+
+ return Promise.reject(error);
+ },
+ );
+}
+
+/** Single factory for web / theme / plugins — wraps generated API modules. */
+export function createClient(options: ClientOptions = {}): ReactPressClient {
+ const baseURL = options.baseURL ?? 'http://localhost:3002/api';
+ const client = createApiInstance({ baseURL });
+ attachInterceptors(client.article.http, options);
+ return client;
+}
+
+export function getDefaultApiBaseUrl(): string {
+ if (typeof process !== 'undefined' && process.env?.SERVER_API_URL) {
+ return process.env.SERVER_API_URL;
+ }
+ return 'http://localhost:3002/api';
+}
diff --git a/toolkit/src/plugin/client/index.ts b/toolkit/src/plugin/client/index.ts
new file mode 100644
index 0000000..16d273d
--- /dev/null
+++ b/toolkit/src/plugin/client/index.ts
@@ -0,0 +1,8 @@
+export type { ClientOptions, ReactPressClient } from './client';
+export { ApiError, createClient, getDefaultApiBaseUrl } from './client';
+export type { AppRuntime, DesktopApi } from './runtime';
+export {
+ getDesktopApi,
+ getRuntime,
+ resolveApiBaseUrl,
+} from './runtime';
diff --git a/toolkit/src/plugin/client/runtime.ts b/toolkit/src/plugin/client/runtime.ts
new file mode 100644
index 0000000..f1e1e59
--- /dev/null
+++ b/toolkit/src/plugin/client/runtime.ts
@@ -0,0 +1,40 @@
+export type AppRuntime = 'web' | 'electron';
+
+export interface DesktopApi {
+ getApiBaseUrl: () => Promise;
+ setApiBaseUrl: (url: string) => Promise;
+ showSaveDialog: (opts: { defaultPath?: string }) => Promise;
+ openExternal: (url: string) => Promise;
+ platform: NodeJS.Platform;
+}
+
+declare global {
+ interface Window {
+ reactpressDesktop?: DesktopApi;
+ }
+}
+
+export function getRuntime(): AppRuntime {
+ if (typeof window !== 'undefined' && window.reactpressDesktop) {
+ return 'electron';
+ }
+ return 'web';
+}
+
+export function getDesktopApi(): DesktopApi | undefined {
+ if (typeof window === 'undefined') return undefined;
+ return window.reactpressDesktop;
+}
+
+export async function resolveApiBaseUrl(fallback: string): Promise {
+ const desktop = getDesktopApi();
+ if (desktop) {
+ try {
+ const url = await desktop.getApiBaseUrl();
+ if (url) return url;
+ } catch {
+ // fall through
+ }
+ }
+ return fallback;
+}
diff --git a/toolkit/src/plugin/dev/index.ts b/toolkit/src/plugin/dev/index.ts
new file mode 100644
index 0000000..d831338
--- /dev/null
+++ b/toolkit/src/plugin/dev/index.ts
@@ -0,0 +1,13 @@
+export type { DevPortRedirectOptions, DevPortRole } from './portRedirect';
+export { isUnifiedNginxEntryEnabled } from './portRedirect';
+export {
+ buildDevPortRedirectUrl,
+ isDirectDevPortAccess,
+ isNginxDevRedirectEnabled,
+ isThemePreviewDevPort,
+ resolveDevPortRole,
+ resolveNginxEntryUrl,
+ shouldRedirectDevPortToNginx,
+ THEME_PREVIEW_DEV_PORTS,
+} from './portRedirect';
+export { devPortRedirectPlugin } from './vitePortRedirectPlugin';
diff --git a/toolkit/src/plugin/dev/portRedirect.ts b/toolkit/src/plugin/dev/portRedirect.ts
new file mode 100644
index 0000000..a8e870a
--- /dev/null
+++ b/toolkit/src/plugin/dev/portRedirect.ts
@@ -0,0 +1,127 @@
+const DEFAULT_NGINX_PORT = 80;
+
+/** Admin theme preview pool — must not redirect to nginx visitor (active theme). */
+export const THEME_PREVIEW_DEV_PORTS = [3003, 3004, 3005, 3006, 3007, 3008];
+
+export type DevPortRole = 'visitor' | 'admin' | 'api' | 'preview';
+
+export function isThemePreviewDevPort(port: number): boolean {
+ return Number.isInteger(port) && THEME_PREVIEW_DEV_PORTS.includes(port);
+}
+
+function readPortEnv(key: string, fallback: number): number {
+ const raw = process.env[key]?.trim();
+ if (!raw) return fallback;
+ const parsed = parseInt(raw, 10);
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
+}
+
+export function resolveDevPortRole(directPort: number): DevPortRole {
+ if (isThemePreviewDevPort(directPort)) return 'preview';
+ const apiPort = readPortEnv('SERVER_PORT', 3002);
+ const adminPort = readPortEnv('WEB_ADMIN_PORT', 3000);
+ if (directPort === apiPort) return 'api';
+ if (directPort === adminPort) return 'admin';
+ return 'visitor';
+}
+
+/** Unified nginx entry (:80) — dev (`REACTPRESS_NGINX_ENTRY_URL`) or prod (`NGINX_ENTRY_URL`). */
+export function isUnifiedNginxEntryEnabled(): boolean {
+ return Boolean(
+ process.env.REACTPRESS_NGINX_ENTRY_URL?.trim() || process.env.NGINX_ENTRY_URL?.trim(),
+ );
+}
+
+/** @deprecated Use {@link isUnifiedNginxEntryEnabled} */
+export function isNginxDevRedirectEnabled(): boolean {
+ return isUnifiedNginxEntryEnabled();
+}
+
+export function resolveNginxEntryUrl(): string {
+ const override = process.env.REACTPRESS_NGINX_ENTRY_URL?.trim();
+ if (override) {
+ return override.replace(/\/$/, '');
+ }
+
+ const rawPort = process.env.NGINX_PORT?.trim();
+ const port = rawPort ? parseInt(rawPort, 10) : DEFAULT_NGINX_PORT;
+ if (Number.isFinite(port) && port > 0 && port !== DEFAULT_NGINX_PORT) {
+ return `http://localhost:${port}`;
+ }
+ return 'http://localhost';
+}
+
+export function isDirectDevPortAccess(host: string | null | undefined, port: number): boolean {
+ if (!host || !Number.isFinite(port) || port <= 0) return false;
+ const normalized = host.toLowerCase();
+ return (
+ normalized === `localhost:${port}` ||
+ normalized === `127.0.0.1:${port}` ||
+ normalized === `[::1]:${port}`
+ );
+}
+
+function acceptsHtmlNavigation(accept: string | null | undefined): boolean {
+ if (!accept) return false;
+ return accept.includes('text/html');
+}
+
+export interface DevPortRedirectOptions {
+ host?: string | null;
+ method?: string;
+ accept?: string | null;
+ directPort: number;
+ pathname?: string;
+ skipPathPrefixes?: string[];
+}
+
+export function shouldRedirectDevPortToNginx(options: DevPortRedirectOptions): boolean {
+ if (process.env.REACTPRESS_SKIP_DEV_PORT_REDIRECT === '1') return false;
+ if (isThemePreviewDevPort(options.directPort)) return false;
+ if (!isUnifiedNginxEntryEnabled()) return false;
+
+ const method = (options.method || 'GET').toUpperCase();
+ if (method !== 'GET' && method !== 'HEAD') return false;
+ if (!isDirectDevPortAccess(options.host, options.directPort)) return false;
+ if (!acceptsHtmlNavigation(options.accept)) return false;
+
+ const pathname = options.pathname || '/';
+ if (options.skipPathPrefixes?.some((prefix) => pathname.startsWith(prefix))) {
+ return false;
+ }
+
+ return true;
+}
+
+export function buildDevPortRedirectUrl(options: {
+ directPort: number;
+ pathname?: string;
+ search?: string;
+ nginxEntryUrl?: string;
+}): string {
+ const entry = (options.nginxEntryUrl || resolveNginxEntryUrl()).replace(/\/$/, '');
+ const pathname = options.pathname || '/';
+ const search = options.search || '';
+ const role = resolveDevPortRole(options.directPort);
+
+ if (role === 'api') {
+ if (pathname === '/' || pathname === '') {
+ return `${entry}/api${search}`;
+ }
+ return `${entry}${pathname}${search}`;
+ }
+
+ if (role === 'admin') {
+ let adminPath = pathname;
+ if (!adminPath.startsWith('/admin')) {
+ adminPath = adminPath === '/' ? '/admin/' : `/admin${adminPath.startsWith('/') ? adminPath : `/${adminPath}`}`;
+ }
+ return `${entry}${adminPath}${search}`;
+ }
+
+ if (role === 'preview') {
+ return `${entry}${pathname}${search}`;
+ }
+
+ return `${entry}${pathname}${search}`;
+}
diff --git a/toolkit/src/plugin/dev/vitePortRedirectPlugin.ts b/toolkit/src/plugin/dev/vitePortRedirectPlugin.ts
new file mode 100644
index 0000000..4618753
--- /dev/null
+++ b/toolkit/src/plugin/dev/vitePortRedirectPlugin.ts
@@ -0,0 +1,65 @@
+import { buildDevPortRedirectUrl, shouldRedirectDevPortToNginx } from './portRedirect';
+
+const VITE_DEV_SKIP_PREFIXES = ['/@vite', '/@fs', '/@id', '/node_modules'];
+
+type DevServer = {
+ middlewares: {
+ use: (handler: (req: DevRequest, res: DevResponse, next: () => void) => void) => void;
+ };
+};
+
+type DevRequest = {
+ url?: string;
+ method?: string;
+ headers: {
+ host?: string;
+ accept?: string;
+ };
+};
+
+type DevResponse = {
+ statusCode: number;
+ setHeader: (name: string, value: string) => void;
+ end: () => void;
+};
+
+function createRedirectMiddleware(port: number) {
+ return (req: DevRequest, res: DevResponse, next: () => void) => {
+ const url = req.url || '/';
+ const pathname = url.split('?')[0] || '/';
+ const search = url.includes('?') ? url.slice(url.indexOf('?')) : '';
+
+ if (
+ !shouldRedirectDevPortToNginx({
+ host: req.headers.host,
+ method: req.method,
+ accept: req.headers.accept,
+ directPort: port,
+ pathname,
+ skipPathPrefixes: VITE_DEV_SKIP_PREFIXES,
+ })
+ ) {
+ next();
+ return;
+ }
+
+ const target = buildDevPortRedirectUrl({
+ directPort: port,
+ pathname,
+ search,
+ });
+ res.statusCode = 302;
+ res.setHeader('Location', target);
+ res.end();
+ };
+}
+
+/** Redirect direct :3000 browser hits to nginx entry when REACTPRESS_NGINX_ENTRY_URL is set. */
+export function devPortRedirectPlugin(port: number) {
+ return {
+ name: 'reactpress-dev-port-redirect',
+ configureServer(server: DevServer) {
+ server.middlewares.use(createRedirectMiddleware(port));
+ },
+ };
+}
diff --git a/toolkit/src/plugin/index.ts b/toolkit/src/plugin/index.ts
new file mode 100644
index 0000000..4061cc6
--- /dev/null
+++ b/toolkit/src/plugin/index.ts
@@ -0,0 +1,10 @@
+export * as admin from './admin';
+export * as client from './client';
+export * as dev from './dev';
+/** @deprecated Use `plugin/client`; path kept for compatibility. */
+export * as react from './react';
+
+export * from './admin';
+export * from './client';
+export * from './dev';
+export * from './react';
diff --git a/toolkit/src/plugin/react/index.ts b/toolkit/src/plugin/react/index.ts
new file mode 100644
index 0000000..6dd8dbc
--- /dev/null
+++ b/toolkit/src/plugin/react/index.ts
@@ -0,0 +1,2 @@
+/** Stable export path: `@fecommunity/reactpress-toolkit/plugin/react`. */
+export * from '../client';
diff --git a/toolkit/src/theme/api/api-data.ts b/toolkit/src/theme/api/api-data.ts
new file mode 100644
index 0000000..f81e52b
--- /dev/null
+++ b/toolkit/src/theme/api/api-data.ts
@@ -0,0 +1,2 @@
+export type { ApiEnvelope } from '../../utils/api-envelope';
+export { unpackList, unpackOne, unpackPaginated, unpackPaginatedPair } from '../../utils/api-envelope';
diff --git a/toolkit/src/theme/api/api.ts b/toolkit/src/theme/api/api.ts
new file mode 100644
index 0000000..50646de
--- /dev/null
+++ b/toolkit/src/theme/api/api.ts
@@ -0,0 +1,29 @@
+import { createApiInstance } from '../../api/instance';
+
+export type ThemeApi = ReturnType;
+
+/**
+ * Base URL for theme API calls.
+ * SSR uses REACTPRESS_API_URL (direct :3002); browser uses NEXT_PUBLIC_* (often nginx /api).
+ */
+export function resolveThemeApiBaseUrl(): string {
+ const serverBaseURL =
+ process.env.REACTPRESS_API_URL || 'http://localhost:3002/api';
+ const browserBaseURL =
+ process.env.NEXT_PUBLIC_REACTPRESS_API_URL || serverBaseURL;
+ return typeof window === 'undefined' ? serverBaseURL : browserBaseURL;
+}
+
+/** Create a theme-scoped API client (override baseURL for tests or multi-tenant). */
+export function createThemeApi(config?: { baseURL?: string }): ThemeApi {
+ return createApiInstance({
+ baseURL: config?.baseURL ?? resolveThemeApiBaseUrl(),
+ });
+}
+
+/** Default theme API client — same env contract as `reactpress theme dev`. */
+export const themeApi = createThemeApi();
+
+export function getThemeApiBaseUrl(): string {
+ return resolveThemeApiBaseUrl();
+}
diff --git a/toolkit/src/theme/api/httpClient.ts b/toolkit/src/theme/api/httpClient.ts
new file mode 100644
index 0000000..15f2354
--- /dev/null
+++ b/toolkit/src/theme/api/httpClient.ts
@@ -0,0 +1,114 @@
+import axios, { type AxiosInstance, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios';
+
+import { getStoredAccessToken } from '../visitor/authSession';
+import { resolveThemeApiBaseUrl } from './api';
+
+export interface ThemeApiEnvelope {
+ statusCode: number;
+ success: boolean;
+ msg: string | null;
+ data: T;
+}
+
+export interface CreateThemeAxiosClientOptions {
+ baseURL?: string;
+ timeout?: number;
+ getAccessToken?: () => string | null;
+ onError?: (message: string, status?: number) => void;
+ onUnauthorized?: () => void;
+ unwrapEnvelope?: boolean;
+}
+
+/**
+ * Theme axios base URL (SSR + client).
+ * Do not read `process.env.SERVER_API_URL` here — Next inlines it from next.config at compile time
+ * and stale values (e.g. localhost:3002) break `--remote-origin` dev.
+ */
+export function resolveThemeAxiosBaseUrl(): string {
+ return resolveThemeApiBaseUrl();
+}
+
+/** Encode dynamic path segments (e.g. Chinese tag names) for Node fetch / axios. */
+export function encodeAxiosUrlPath(url: string): string {
+ const qIndex = url.indexOf('?');
+ const pathname = qIndex === -1 ? url : url.slice(0, qIndex);
+ const search = qIndex === -1 ? '' : url.slice(qIndex);
+
+ const encodedPath = pathname
+ .split('/')
+ .map((segment) => {
+ if (!segment) return segment;
+ try {
+ return encodeURIComponent(decodeURIComponent(segment));
+ } catch {
+ return encodeURIComponent(segment);
+ }
+ })
+ .join('/');
+
+ return encodedPath + search;
+}
+
+export function createThemeAxiosClient(options: CreateThemeAxiosClientOptions = {}): AxiosInstance {
+ const isBrowser = typeof window !== 'undefined';
+ const {
+ baseURL = resolveThemeAxiosBaseUrl(),
+ timeout = 60000,
+ getAccessToken = getStoredAccessToken,
+ onError,
+ onUnauthorized,
+ unwrapEnvelope = true,
+ } = options;
+
+ const client = axios.create({ baseURL, timeout });
+
+ client.interceptors.request.use(
+ (config: InternalAxiosRequestConfig) => {
+ if (config.url) {
+ config.url = encodeAxiosUrlPath(config.url);
+ }
+ if (isBrowser) {
+ const token = getAccessToken();
+ if (config.headers && token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ }
+ return config;
+ },
+ () => {
+ throw new Error('Failed to send request');
+ },
+ );
+
+ client.interceptors.response.use(
+ (response: AxiosResponse) => {
+ if (!unwrapEnvelope) {
+ return response;
+ }
+
+ const payload = response.data;
+ if (!payload?.success) {
+ onError?.(payload?.msg || 'Request failed');
+ const message = payload?.msg || `Request failed (${payload?.statusCode ?? 'unknown'})`;
+ return Promise.reject(new Error(message));
+ }
+ return payload.data as never;
+ },
+ (err) => {
+ const status = err?.response?.status as number | undefined;
+ const msg = err?.response?.data?.msg as string | undefined;
+
+ if (status === 401) {
+ onUnauthorized?.();
+ onError?.(msg || 'Unauthorized');
+ } else if (status) {
+ onError?.(msg || 'Server error', status);
+ }
+
+ const message = msg || err?.message || `HTTP ${status ?? 'error'}`;
+ return Promise.reject(new Error(message));
+ },
+ );
+
+ return client;
+}
diff --git a/toolkit/src/theme/api/json.ts b/toolkit/src/theme/api/json.ts
new file mode 100644
index 0000000..cc57972
--- /dev/null
+++ b/toolkit/src/theme/api/json.ts
@@ -0,0 +1 @@
+export { safeJsonParse } from '../../utils/json';
diff --git a/toolkit/src/theme/api/jsonp.ts b/toolkit/src/theme/api/jsonp.ts
new file mode 100644
index 0000000..d357e5d
--- /dev/null
+++ b/toolkit/src/theme/api/jsonp.ts
@@ -0,0 +1 @@
+export { jsonp } from '../../utils/jsonp';
diff --git a/toolkit/src/theme/build/next-config.ts b/toolkit/src/theme/build/next-config.ts
new file mode 100644
index 0000000..dddb0a0
--- /dev/null
+++ b/toolkit/src/theme/build/next-config.ts
@@ -0,0 +1,80 @@
+/** Shared Next.js defaults for ReactPress theme packages (Pages Router). */
+
+import path from 'path';
+
+export interface ReactPressNextConfig {
+ reactStrictMode?: boolean;
+ eslint?: { ignoreDuringBuilds?: boolean };
+ transpilePackages?: string[];
+ webpack?: (config: Record, context: unknown) => Record;
+ [key: string]: unknown;
+}
+
+function resolveFromApp(appDir: string, spec: string): string {
+ return require.resolve(spec, { paths: [appDir] });
+}
+
+/** Pin React to one copy so lazy-loaded page chunks share the same runtime (pnpm monorepo). */
+function applyThemeWebpackResolve(
+ config: Record,
+ appDir: string,
+): Record {
+ const nextConfig = config as {
+ resolve?: {
+ alias?: Record;
+ modules?: string[] | string;
+ };
+ };
+ nextConfig.resolve = nextConfig.resolve || {};
+ const reactDir = path.dirname(resolveFromApp(appDir, 'react/package.json'));
+ const reactDomDir = path.dirname(resolveFromApp(appDir, 'react-dom/package.json'));
+ const themeNodeModules = path.join(appDir, 'node_modules');
+ nextConfig.resolve.modules = [
+ themeNodeModules,
+ ...(Array.isArray(nextConfig.resolve.modules)
+ ? nextConfig.resolve.modules
+ : nextConfig.resolve.modules
+ ? [nextConfig.resolve.modules]
+ : []),
+ 'node_modules',
+ ];
+ nextConfig.resolve.alias = {
+ ...nextConfig.resolve.alias,
+ // axios → follow-redirects → debug optional peer supports-color@9 (ESM-only)
+ 'supports-color': false,
+ react: reactDir,
+ 'react-dom': reactDomDir,
+ 'react/jsx-runtime': resolveFromApp(appDir, 'react/jsx-runtime'),
+ 'react/jsx-dev-runtime': resolveFromApp(appDir, 'react/jsx-dev-runtime'),
+ };
+ return config;
+}
+
+/**
+ * Baseline Next config for themes: strict mode, eslint relaxed in CI, axios webpack alias.
+ * Pass overrides to extend (your `webpack` runs after the supports-color alias).
+ */
+export function createReactPressNextConfig(
+ overrides: ReactPressNextConfig = {},
+): ReactPressNextConfig {
+ const { webpack: userWebpack, eslint, transpilePackages, ...rest } = overrides;
+
+ return {
+ reactStrictMode: true,
+ transpilePackages: ['@fecommunity/reactpress-toolkit', ...(transpilePackages ?? [])],
+ eslint: {
+ ignoreDuringBuilds: true,
+ ...(eslint || {}),
+ },
+ ...rest,
+ webpack: (config, context) => {
+ const ctx = context as { dir?: string; dev?: boolean; isServer?: boolean };
+ const appDir = ctx.dir ?? process.cwd();
+ applyThemeWebpackResolve(config, appDir);
+ if (typeof userWebpack === 'function') {
+ return userWebpack(config, context);
+ }
+ return config;
+ },
+ };
+}
diff --git a/toolkit/src/theme/build/node.ts b/toolkit/src/theme/build/node.ts
new file mode 100644
index 0000000..5487d2d
--- /dev/null
+++ b/toolkit/src/theme/build/node.ts
@@ -0,0 +1,2 @@
+/** Node-only theme helpers (filesystem I/O). */
+export { readThemeAdminLocaleFile } from '../extension/theme-admin-locale.io';
diff --git a/toolkit/src/theme/content/archiveSlim.ts b/toolkit/src/theme/content/archiveSlim.ts
new file mode 100644
index 0000000..872bac8
--- /dev/null
+++ b/toolkit/src/theme/content/archiveSlim.ts
@@ -0,0 +1,41 @@
+import type { IArticle } from '../../types';
+
+export type ArchiveArticle = Pick;
+
+export type ArchiveTree = Record>;
+
+export function slimArchiveTree(raw: Record>): ArchiveTree {
+ const out: ArchiveTree = {};
+ for (const year of Object.keys(raw)) {
+ out[year] = {};
+ for (const month of Object.keys(raw[year])) {
+ out[year][month] = raw[year][month].map(({ id, title, publishAt }) => ({
+ id,
+ title,
+ publishAt,
+ }));
+ }
+ }
+ return out;
+}
+
+export function countArchiveArticles(articles: ArchiveTree): number {
+ let total = 0;
+ for (const year of Object.keys(articles)) {
+ for (const month of Object.keys(articles[year])) {
+ total += articles[year][month].length;
+ }
+ }
+ return total;
+}
+
+export function formatArchiveDay(publishAt: string): string {
+ const date = new Date(publishAt);
+ const month = String(date.getMonth() + 1).padStart(2, '0');
+ const day = String(date.getDate()).padStart(2, '0');
+ return `${month}-${day}`;
+}
+
+export function sortedArchiveYears(articles: ArchiveTree): string[] {
+ return Object.keys(articles).sort((a, b) => +b - +a);
+}
diff --git a/toolkit/src/theme/content/articleSlim.ts b/toolkit/src/theme/content/articleSlim.ts
new file mode 100644
index 0000000..c43dd65
--- /dev/null
+++ b/toolkit/src/theme/content/articleSlim.ts
@@ -0,0 +1,44 @@
+import type { IArticle } from '../../types';
+
+/** Fields required by article list cards — omit content/html/toc from SSR payload. */
+export type ListArticle = Pick<
+ IArticle,
+ 'id' | 'title' | 'cover' | 'summary' | 'likes' | 'views' | 'publishAt'
+> & {
+ category?: Pick, 'value' | 'label'>;
+};
+
+export type CarouselArticle = Pick;
+
+export function slimArticleForList(article: IArticle): ListArticle {
+ return {
+ id: article.id,
+ title: article.title,
+ cover: article.cover,
+ summary: article.summary,
+ likes: article.likes,
+ views: article.views,
+ publishAt: article.publishAt,
+ category: article.category
+ ? { value: article.category.value, label: article.category.label }
+ : undefined,
+ };
+}
+
+export function slimArticlesForList(articles: IArticle[]): ListArticle[] {
+ return articles.map(slimArticleForList);
+}
+
+export function slimArticleForCarousel(article: IArticle): CarouselArticle {
+ return {
+ id: article.id,
+ title: article.title,
+ cover: article.cover,
+ publishAt: article.publishAt,
+ views: article.views,
+ };
+}
+
+export function slimArticlesForCarousel(articles: IArticle[]): CarouselArticle[] {
+ return articles.map(slimArticleForCarousel);
+}
diff --git a/toolkit/src/theme/content/assets.ts b/toolkit/src/theme/content/assets.ts
new file mode 100644
index 0000000..1451952
--- /dev/null
+++ b/toolkit/src/theme/content/assets.ts
@@ -0,0 +1,76 @@
+/** Legacy root paths that live under API `/public` on the server. */
+const PUBLIC_PATH_ALIASES: Record = {
+ '/logo.png': '/public/logo.png',
+ '/favicon.png': '/public/favicon.png',
+ '/favicon.ico': '/public/favicon.ico',
+};
+
+function normalizePublicAssetPath(path: string): string {
+ return PUBLIC_PATH_ALIASES[path] ?? path;
+}
+
+/** Whether a configured asset path is likely to resolve (skip broken defaults like `/logo.png`). */
+export function isLikelyValidAssetPath(url: string | undefined | null): boolean {
+ const trimmed = (url ?? '').trim();
+ if (!trimmed) return false;
+ if (/^https?:\/\//i.test(trimmed) || trimmed.startsWith('/api/') || trimmed.startsWith('/uploads/')) {
+ return true;
+ }
+ if (trimmed.startsWith('/public/') && !PUBLIC_PATH_ALIASES[trimmed.replace('/public', '')]) {
+ return true;
+ }
+ return false;
+}
+
+/** Header logo — uploads only; default wordmark `/logo.png` is shown via `SiteBranding` text instead. */
+export function isLikelyValidHeaderLogoPath(url: string | undefined | null): boolean {
+ return isLikelyValidAssetPath(url);
+}
+
+/** Stable public site origin for asset URLs — matches on SSR and client when `NEXT_PUBLIC_*` is set. */
+function resolvePublicSiteOrigin(): string {
+ const publicApi =
+ (typeof process !== 'undefined' && process.env.NEXT_PUBLIC_REACTPRESS_API_URL?.trim()) || '';
+ if (publicApi) {
+ return publicApi.replace(/\/api\/?$/, '').replace(/\/$/, '');
+ }
+ if (typeof window !== 'undefined') {
+ return window.location.origin;
+ }
+ const serverApi =
+ process.env.REACTPRESS_API_URL?.trim() ||
+ process.env.SERVER_API_URL?.trim() ||
+ 'http://localhost:3002/api';
+ return serverApi.replace(/\/api\/?$/, '').replace(/\/$/, '');
+}
+
+/** Resolve media / logo URLs for theme pages. */
+export function resolvePublicAssetUrl(url: string | undefined | null): string {
+ const trimmed = (url ?? '').trim();
+ if (!trimmed) return '';
+ if (/^https?:\/\//i.test(trimmed) || trimmed.startsWith('data:')) return trimmed;
+ if (trimmed.startsWith('//')) {
+ const protocol = typeof window !== 'undefined' ? window.location.protocol : 'http:';
+ return `${protocol}${trimmed}`;
+ }
+ if (trimmed.startsWith('/')) {
+ const normalized = normalizePublicAssetPath(trimmed);
+ const origin = resolvePublicSiteOrigin();
+ const themeOrigin = typeof window !== 'undefined' ? window.location.origin : origin;
+ if (origin === themeOrigin) {
+ return normalized;
+ }
+ return `${origin}${normalized}`;
+ }
+ return trimmed;
+}
+
+/** Rewrite relative asset URLs inside rendered article HTML (`src`, `href`). */
+export function rewriteArticleHtmlAssets(html: string | undefined | null): string {
+ if (!html?.trim()) return html ?? '';
+ return html.replace(
+ /(\b(?:src|href)\s*=\s*)(["'])([^"']+)\2/gi,
+ (_, prefix: string, quote: string, url: string) =>
+ `${prefix}${quote}${resolvePublicAssetUrl(url)}${quote}`,
+ );
+}
diff --git a/toolkit/src/theme/content/carouselArticles.ts b/toolkit/src/theme/content/carouselArticles.ts
new file mode 100644
index 0000000..7c28945
--- /dev/null
+++ b/toolkit/src/theme/content/carouselArticles.ts
@@ -0,0 +1,59 @@
+import type { IArticle } from '../../types';
+
+import { slimArticlesForCarousel, type CarouselArticle } from './articleSlim';
+
+export const CAROUSEL_MAX_SLIDES = 6;
+
+export type ResolveCarouselArticlesOptions = {
+ /** Fetch recommended articles when the first page has fewer than {@link CAROUSEL_MAX_SLIDES} marked recommended. */
+ fetchRecommended?: () => Promise;
+ maxSlides?: number;
+};
+
+function mergeCarouselArticles(
+ primary: IArticle[],
+ extra: IArticle[],
+ maxSlides: number,
+): IArticle[] {
+ const merged: IArticle[] = [];
+ const seen = new Set();
+
+ for (const article of [...primary, ...extra]) {
+ if (!article.cover || seen.has(article.id)) continue;
+ seen.add(article.id);
+ merged.push(article);
+ if (merged.length >= maxSlides) break;
+ }
+
+ return merged;
+}
+
+/** Resolve homepage carousel data with at most one recommend API call. */
+export async function resolveCarouselArticles(
+ rawArticles: IArticle[],
+ options: ResolveCarouselArticlesOptions = {},
+): Promise {
+ const maxSlides = options.maxSlides ?? CAROUSEL_MAX_SLIDES;
+ const fromFirstPage = rawArticles.filter((article) => article.isRecommended && article.cover);
+
+ if (fromFirstPage.length >= maxSlides) {
+ return slimArticlesForCarousel(fromFirstPage.slice(0, maxSlides));
+ }
+
+ if (!rawArticles.some((article) => article.cover)) {
+ return [];
+ }
+
+ let merged = fromFirstPage;
+
+ if (merged.length < maxSlides && options.fetchRecommended) {
+ const recommended = await options.fetchRecommended().catch(() => []);
+ merged = mergeCarouselArticles(merged, recommended, maxSlides);
+ }
+
+ if (merged.length === 0) {
+ return [];
+ }
+
+ return slimArticlesForCarousel(merged.slice(0, maxSlides));
+}
diff --git a/toolkit/src/theme/content/excerpt.ts b/toolkit/src/theme/content/excerpt.ts
new file mode 100644
index 0000000..2f69152
--- /dev/null
+++ b/toolkit/src/theme/content/excerpt.ts
@@ -0,0 +1,56 @@
+import { stripHtml, truncateWords } from '../../utils/string';
+
+export type ArchiveExcerptMode = 'excerpt' | 'full' | 'none';
+
+const DEFAULT_EXCERPT_WORD_LIMIT = 55;
+
+export { stripHtml, truncateWords };
+
+export interface ResolveArchiveExcerptOptions {
+ mode?: ArchiveExcerptMode;
+ content?: string | null;
+ html?: string | null;
+ summary?: string | null;
+ wordLimit?: number;
+}
+
+/** Plain text from Markdown when only `content` is available (no rendered `html`). */
+function stripMarkdown(markdown: string): string {
+ return markdown
+ .replace(/^$/gm, '')
+ .replace(/^#+\s+.*$/gm, '')
+ .replace(/\*\*([^*]+)\*\*/g, '$1')
+ .replace(/\*([^*]+)\*/g, '$1')
+ .replace(/~~([^~]+)~~/g, '$1')
+ .replace(/`([^`]+)`/g, '$1')
+ .replace(/^\d+\.\s+/gm, '')
+ .replace(/^>\s+.*/gm, '')
+ .replace(/^---$/gm, '')
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
+ .replace(/!\[[^\]]*\]\([^)]+\)/g, '')
+ .replace(/\s+/g, ' ')
+ .trim();
+}
+
+/**
+ * Resolve archive list excerpt text from customizer `archiveExcerpt` mode.
+ * Returns `null` when the excerpt should be hidden.
+ */
+export function resolveArchiveExcerpt({
+ mode = 'excerpt',
+ content,
+ html,
+ summary,
+ wordLimit = DEFAULT_EXCERPT_WORD_LIMIT,
+}: ResolveArchiveExcerptOptions): string | null {
+ if (mode === 'none') return null;
+
+ const raw =
+ summary?.trim() ||
+ stripHtml(html ?? '') ||
+ stripMarkdown(content ?? '') ||
+ '';
+ if (!raw) return null;
+ if (mode === 'full') return raw;
+ return truncateWords(raw, wordLimit);
+}
diff --git a/toolkit/src/theme/content/nav.ts b/toolkit/src/theme/content/nav.ts
new file mode 100644
index 0000000..4a40ae0
--- /dev/null
+++ b/toolkit/src/theme/content/nav.ts
@@ -0,0 +1,34 @@
+export interface NavItem {
+ id: string;
+ href: string;
+ label: string;
+}
+
+/** Resolve active nav item id from pathname (exact match, then longest prefix). */
+export function getNavActiveId(pathname: string, items: NavItem[]): string | undefined {
+ const path = pathname.split('?')[0].replace(/\/$/, '') || '/';
+ const normalized = items.map((item) => ({
+ ...item,
+ href: item.href.replace(/\/$/, '') || '/',
+ }));
+
+ const exact = normalized.find((item) => item.href === path);
+ if (exact) return exact.id;
+
+ const prefixMatches = normalized
+ .filter((item) => item.href !== '/' && path.startsWith(item.href))
+ .sort((a, b) => b.href.length - a.href.length);
+ return prefixMatches[0]?.id;
+}
+
+export function articlePath(id: string): string {
+ return `/article/${id}`;
+}
+
+export function categoryPath(value: string): string {
+ return `/category/${value}`;
+}
+
+export function tagPath(value: string): string {
+ return `/tag/${value}`;
+}
diff --git a/toolkit/src/theme/content/seo.ts b/toolkit/src/theme/content/seo.ts
new file mode 100644
index 0000000..d1c1745
--- /dev/null
+++ b/toolkit/src/theme/content/seo.ts
@@ -0,0 +1,18 @@
+export interface SiteTitleSource {
+ systemTitle?: string;
+ systemSubTitle?: string;
+}
+
+export function getSiteTitle(setting: SiteTitleSource, fallback = 'ReactPress'): string {
+ const main = setting.systemTitle || fallback;
+ return setting.systemSubTitle ? `${main} - ${setting.systemSubTitle}` : main;
+}
+
+export function getPageTitle(
+ pageTitle: string,
+ setting: Pick,
+ fallback = 'ReactPress',
+): string {
+ const site = setting.systemTitle || fallback;
+ return `${pageTitle} - ${site}`;
+}
diff --git a/toolkit/src/theme/extension/branding-mods.ts b/toolkit/src/theme/extension/branding-mods.ts
new file mode 100644
index 0000000..0c793f0
--- /dev/null
+++ b/toolkit/src/theme/extension/branding-mods.ts
@@ -0,0 +1,69 @@
+export type ThemeMods = Record;
+
+/** Customizer mod id → legacy Setting column (runtime overlay for themes). */
+export const THEME_BRANDING_MOD_TO_SETTING: Record = {
+ displayTitle: 'systemTitle',
+ displayTagline: 'systemSubTitle',
+ siteLogo: 'systemLogo',
+ backgroundImage: 'systemBg',
+ siteFavicon: 'systemFavicon',
+ siteNotice: 'systemNoticeInfo',
+ seoKeyword: 'seoKeyword',
+ seoDesc: 'seoDesc',
+ baiduAnalyticsId: 'baiduAnalyticsId',
+ googleAnalyticsId: 'googleAnalyticsId',
+};
+
+/** Mod ids stored under the same key in mods and Setting overlay. */
+export const THEME_BRANDING_DIRECT_MODS = [
+ 'systemFooterInfo',
+ 'aboutUsGithubUrl',
+ 'aboutUsCommentQr',
+ 'aboutUsWechatQr',
+] as const;
+
+function modValue(mods: ThemeMods, modId: string): string | undefined {
+ const v = mods[modId];
+ if (v == null || String(v).trim() === '') return undefined;
+ return String(v);
+}
+
+/** Build customizer form seed from legacy Setting columns (one-time / fallback). */
+export function seedThemeModsFromLegacySetting(
+ setting: Record | null | undefined,
+ mods: ThemeMods = {},
+): ThemeMods {
+ const merged = { ...mods };
+ for (const [modId, settingKey] of Object.entries(THEME_BRANDING_MOD_TO_SETTING)) {
+ if (!modValue(merged, modId) && setting?.[settingKey] != null) {
+ merged[modId] = String(setting[settingKey]);
+ }
+ }
+ for (const key of THEME_BRANDING_DIRECT_MODS) {
+ if (!modValue(merged, key) && setting?.[key] != null) {
+ merged[key] = String(setting[key]);
+ }
+ }
+ return merged;
+}
+
+/** Overlay theme mods onto a Setting-shaped object for theme runtime & mail templates. */
+export function applyThemeModsToSiteSetting>(
+ setting: T,
+ mods: ThemeMods,
+): T {
+ const next = { ...setting };
+ for (const [modId, settingKey] of Object.entries(THEME_BRANDING_MOD_TO_SETTING)) {
+ const v = modValue(mods, modId);
+ if (v) (next as Record)[settingKey] = v;
+ }
+ for (const key of THEME_BRANDING_DIRECT_MODS) {
+ const v = modValue(mods, key);
+ if (v) (next as Record)[key] = v;
+ }
+ return next;
+}
+
+export function brandingModValue(mods: ThemeMods, modId: string): string | undefined {
+ return modValue(mods, modId);
+}
diff --git a/toolkit/src/theme/extension/configuration/defaults.ts b/toolkit/src/theme/extension/configuration/defaults.ts
new file mode 100644
index 0000000..b1c7790
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/defaults.ts
@@ -0,0 +1,28 @@
+import type { ThemeConfigurationPropertySchema, ThemeConfigurationSchema } from './types';
+
+/** Collect `default` values from a JSON Schema object tree. */
+export function defaultsFromSchema(schema: ThemeConfigurationSchema | null | undefined): Record {
+ if (!schema?.properties) return {};
+ const out: Record = {};
+ for (const [key, prop] of Object.entries(schema.properties)) {
+ const value = defaultFromProperty(prop);
+ if (value !== undefined) {
+ out[key] = value;
+ }
+ }
+ return out;
+}
+
+function defaultFromProperty(prop: ThemeConfigurationPropertySchema | undefined): unknown {
+ if (!prop) return undefined;
+ if (prop.default !== undefined) return prop.default;
+ if (prop.type === 'object' && prop.properties) {
+ const nested: Record = {};
+ for (const [k, p] of Object.entries(prop.properties)) {
+ const v = defaultFromProperty(p);
+ if (v !== undefined) nested[k] = v;
+ }
+ return Object.keys(nested).length ? nested : undefined;
+ }
+ return undefined;
+}
diff --git a/toolkit/src/theme/extension/configuration/formily.ts b/toolkit/src/theme/extension/configuration/formily.ts
new file mode 100644
index 0000000..44889bc
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/formily.ts
@@ -0,0 +1,199 @@
+import type { ConfigurationUiMeta, ThemeConfigurationPropertySchema, ThemeConfigurationSchema } from './types';
+
+/** Formily `ISchema` node (JSON Schema + Formily x-* extensions). */
+export type FormilySchemaNode = Record;
+
+const VOID_COMPONENTS = [
+ 'FormLayout',
+ 'FormGrid',
+ 'FormGrid.GridColumn',
+ 'SectionCard',
+ 'FormCollapse',
+ 'FormCollapse.CollapsePanel',
+ 'CollapsePanel',
+ 'FormTab',
+ 'FormTab.TabPane',
+];
+
+/** Map `x-ui.widget` → Formily `x-component` (extend via registerFormilyWidget). */
+const WIDGET_COMPONENT_MAP: Record = {
+ navLinkList: 'NavLinkListField',
+ urlConfigEditor: 'JsonConfigEditor',
+ navSearchEditor: 'JsonConfigEditor',
+};
+
+const registeredWidgets: Record = { ...WIDGET_COMPONENT_MAP };
+
+export function registerFormilyWidget(widgetId: string, componentName: string): void {
+ registeredWidgets[widgetId] = componentName;
+}
+
+/**
+ * Convert theme `reactpress.configuration` JSON Schema to Formily render schema.
+ * Adds `x-decorator` / `x-component` from types and `x-ui.widget`.
+ */
+export function toFormilyJsonSchema(
+ schema: ThemeConfigurationSchema | null | undefined,
+): FormilySchemaNode | null {
+ if (!schema || schema.type !== 'object') return null;
+ const root = convertProperty(
+ { ...schema, type: 'object' } as ThemeConfigurationPropertySchema,
+ 0,
+ true,
+ );
+ const sectionProps = (root.properties ?? {}) as Record;
+ return {
+ type: 'object',
+ 'x-component': 'FormLayout',
+ 'x-component-props': { layout: 'vertical', style: { width: '100%' } },
+ properties: wrapRootSections(sectionProps),
+ };
+}
+
+/** Full-width section cards; field paths stay `header.*` / `nav.*`. */
+function wrapRootSections(properties: Record): Record {
+ return Object.fromEntries(
+ Object.entries(properties).map(([key, child]) => {
+ const title = typeof child.title === 'string' ? child.title : key;
+ const inner = withObjectLayout(child);
+ return [
+ key,
+ {
+ ...inner,
+ 'x-decorator': 'SectionCard',
+ 'x-decorator-props': {
+ title,
+ sectionId: `theme-section-${key}`,
+ feedbackLayout: 'none',
+ layout: 'vertical',
+ fullness: true,
+ },
+ },
+ ];
+ }),
+ );
+}
+
+/** Ensure nested object fields render their properties in Formily. */
+function withObjectLayout(node: FormilySchemaNode, title?: string): FormilySchemaNode {
+ if (node.type !== 'object' || !node.properties) return node;
+ const props = node.properties as Record;
+ const keys = Object.keys(props);
+ return {
+ ...node,
+ ...(keys.length > 1 ? { title } : {}),
+ 'x-component': 'FormLayout',
+ 'x-component-props': { layout: 'vertical' },
+ };
+}
+
+function convertProperty(
+ node: ThemeConfigurationPropertySchema,
+ depth: number,
+ isRoot = false,
+): FormilySchemaNode {
+ const ui = node['x-ui'] as ConfigurationUiMeta | undefined;
+ if (ui?.hidden) {
+ return { ...node, 'x-display': 'hidden' };
+ }
+
+ const type = typeof node.type === 'string' ? node.type : undefined;
+
+ if (type === 'object') {
+ const widget = ui?.widget ? registeredWidgets[ui.widget] : undefined;
+ if (widget === 'JsonConfigEditor') {
+ return {
+ ...stripMeta(node),
+ type: 'object',
+ 'x-decorator': 'FormItem',
+ 'x-decorator-props': {
+ feedbackLayout: 'none',
+ layout: 'vertical',
+ fullness: true,
+ wrapperCol: 24,
+ },
+ 'x-component': 'JsonConfigEditor',
+ 'x-component-props': { height: 360 },
+ };
+ }
+ if (node.properties) {
+ const properties = Object.fromEntries(
+ Object.entries(node.properties).map(([key, child]) => [
+ key,
+ convertProperty(child, depth + 1),
+ ]),
+ );
+ return {
+ ...stripMeta(node),
+ type: 'object',
+ properties,
+ };
+ }
+ }
+
+ if (type === 'array') {
+ const widget = ui?.widget ? registeredWidgets[ui.widget] : undefined;
+ const out: FormilySchemaNode = {
+ ...stripMeta(node),
+ type: 'array',
+ 'x-decorator': 'FormItem',
+ 'x-decorator-props': {
+ feedbackLayout: 'none',
+ layout: 'vertical',
+ colon: false,
+ fullness: true,
+ wrapperCol: 24,
+ },
+ 'x-component': widget ?? 'ArrayCards',
+ 'x-component-props':
+ widget === 'NavLinkListField'
+ ? {}
+ : widget === 'ArrayCards'
+ ? { title: node.title ?? 'Item' }
+ : {},
+ };
+ if (widget !== 'NavLinkListField' && node.items && typeof node.items === 'object' && !Array.isArray(node.items)) {
+ out.items = convertProperty(node.items as ThemeConfigurationPropertySchema, depth + 1);
+ }
+ return out;
+ }
+
+ if (type === 'boolean') {
+ return {
+ ...stripMeta(node),
+ type: 'boolean',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Switch',
+ };
+ }
+
+ if (type === 'number' || type === 'integer') {
+ return {
+ ...stripMeta(node),
+ type: 'number',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'NumberPicker',
+ };
+ }
+
+ if (type === 'string') {
+ const component = node.enum ? 'Select' : 'Input';
+ return {
+ ...stripMeta(node),
+ type: 'string',
+ 'x-decorator': 'FormItem',
+ 'x-component': component,
+ };
+ }
+
+ return { ...stripMeta(node) };
+}
+
+function stripMeta(node: ThemeConfigurationPropertySchema): FormilySchemaNode {
+ const { ['x-ui']: _ui, ...rest } = node;
+ return { ...rest };
+}
+
+export function getFormilyVoidComponents(): string[] {
+ return [...VOID_COMPONENTS];
+}
diff --git a/toolkit/src/theme/extension/configuration/index.ts b/toolkit/src/theme/extension/configuration/index.ts
new file mode 100644
index 0000000..06084d8
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/index.ts
@@ -0,0 +1,12 @@
+export * from './defaults';
+export * from './formily';
+export * from './paths';
+export {
+ getThemeConfigurationSchema,
+ getThemeConfigurationSeed,
+ TWENTYTWENTYFIVE_CONFIGURATION_SCHEMA,
+ twentytwentyfiveConfigSeed,
+} from './presets/twentytwentyfive';
+export * from './resolve';
+export * from './types';
+export * from './validate';
diff --git a/toolkit/src/theme/extension/configuration/paths.ts b/toolkit/src/theme/extension/configuration/paths.ts
new file mode 100644
index 0000000..ef625ff
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/paths.ts
@@ -0,0 +1,5 @@
+export {
+ deepMerge,
+ getByPath,
+ setByPath,
+} from '../../../utils/object';
diff --git a/toolkit/src/theme/extension/configuration/presets/twentytwentyfive.ts b/toolkit/src/theme/extension/configuration/presets/twentytwentyfive.ts
new file mode 100644
index 0000000..08a1455
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/presets/twentytwentyfive.ts
@@ -0,0 +1,97 @@
+import { globalSetting } from '../../../../config/global';
+import { defaultsFromSchema } from '../defaults';
+import { deepMerge } from '../paths';
+import type { ThemeConfigurationSchema } from '../types';
+
+/** Declarative schema shipped with the default blog theme (also embedded in `theme.json`). */
+export const TWENTYTWENTYFIVE_CONFIGURATION_SCHEMA: ThemeConfigurationSchema = {
+ $schema: 'http://json-schema.org/draft-07/schema#',
+ title: 'Twenty Twenty-Five',
+ description: '顶栏导航与 /nav 网址导航、聚合搜索',
+ type: 'object',
+ additionalProperties: false,
+ properties: {
+ header: {
+ type: 'object',
+ title: '顶栏',
+ properties: {
+ navLinks: {
+ type: 'array',
+ title: '固定导航链接',
+ description: '显示在「页面」模块发布的自定义页面之前',
+ default: [
+ { path: '/', locale: 'home', icon: 'HomeOutlined', visible: true },
+ { path: '/nav', locale: 'nav', icon: 'GlobalOutlined', visible: true },
+ { path: '/knowledge', locale: 'knowledge', icon: 'BookOutlined', visible: false },
+ { path: '/archives', locale: 'archives', icon: 'HistoryOutlined', visible: true },
+ ],
+ items: {
+ type: 'object',
+ required: ['path'],
+ properties: {
+ path: { type: 'string', title: '路径' },
+ locale: { type: 'string', title: 'i18n 键' },
+ label: { type: 'string', title: '覆盖文案' },
+ icon: { type: 'string', title: 'Ant Design 图标名' },
+ visible: { type: 'boolean', title: '显示', default: true },
+ },
+ },
+ 'x-ui': { widget: 'navLinkList', section: 'header', order: 10 },
+ },
+ },
+ },
+ nav: {
+ type: 'object',
+ title: '网址导航',
+ properties: {
+ urlConfig: {
+ type: 'array',
+ title: '导航卡片分组',
+ 'x-ui': { widget: 'urlConfigEditor', section: 'nav', order: 20 },
+ items: { type: 'object' },
+ },
+ searchCategories: {
+ type: 'object',
+ title: '聚合搜索分类',
+ 'x-ui': { widget: 'navSearchEditor', section: 'nav', order: 30 },
+ properties: {
+ categories: { type: 'array', items: { type: 'object' } },
+ subCategories: { type: 'object' },
+ },
+ },
+ },
+ },
+ },
+};
+
+/** Seed nav blocks from toolkit `globalSetting` locale bundles (too large for inline schema defaults). */
+export function twentytwentyfiveConfigSeed(locale = 'zh'): Record {
+ const gs = globalSetting as unknown as {
+ en?: { globalConfig?: { urlConfig?: unknown[]; navConfig?: unknown } };
+ zh?: { globalConfig?: { urlConfig?: unknown[]; navConfig?: unknown } };
+ };
+ const bundle = locale === 'en' ? gs.en : gs.zh;
+ const gc = bundle?.globalConfig ?? {};
+ return {
+ nav: {
+ urlConfig: gc.urlConfig ?? [],
+ searchCategories: gc.navConfig ?? { categories: [], subCategories: {} },
+ },
+ };
+}
+
+export function getThemeConfigurationSchema(themeId: string): ThemeConfigurationSchema | null {
+ if (themeId === 'twentytwentyfive') return TWENTYTWENTYFIVE_CONFIGURATION_SCHEMA;
+ return null;
+}
+
+export function getThemeConfigurationSeed(
+ themeId: string,
+ locale = 'zh',
+): Record | null {
+ const schema = getThemeConfigurationSchema(themeId);
+ if (!schema) return null;
+ const schemaDefaults = defaultsFromSchema(schema);
+ const navSeed = themeId === 'twentytwentyfive' ? twentytwentyfiveConfigSeed(locale) : {};
+ return deepMerge(schemaDefaults, navSeed);
+}
diff --git a/toolkit/src/theme/extension/configuration/resolve.ts b/toolkit/src/theme/extension/configuration/resolve.ts
new file mode 100644
index 0000000..c74bca4
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/resolve.ts
@@ -0,0 +1,145 @@
+import { defaultsFromSchema } from './defaults';
+import { deepMerge, getByPath } from './paths';
+import { getThemeConfigurationSchema, getThemeConfigurationSeed } from './presets/twentytwentyfive';
+import type {
+ GlobalSettingWithConfig,
+ HeaderNavLinkConfig,
+ ResolvedSiteConfig,
+ ThemeConfigStore,
+ ThemeConfigurationSchema,
+} from './types';
+import { validateThemeConfiguration } from './validate';
+
+const FALLBACK_HEADER_NAV: HeaderNavLinkConfig[] = [
+ { path: '/', locale: 'home', icon: 'HomeOutlined', visible: true },
+ { path: '/nav', locale: 'nav', icon: 'GlobalOutlined', visible: true },
+ { path: '/archives', locale: 'archives', icon: 'HistoryOutlined', visible: true },
+];
+
+export function parseGlobalSettingRaw(raw: unknown): GlobalSettingWithConfig {
+ if (!raw) return {};
+ if (typeof raw === 'string') {
+ try {
+ return JSON.parse(raw) as GlobalSettingWithConfig;
+ } catch {
+ return {};
+ }
+ }
+ if (typeof raw === 'object') return raw as GlobalSettingWithConfig;
+ return {};
+}
+
+type ManifestWithOptions = {
+ options?: ThemeConfigurationSchema;
+};
+
+export function getConfigurationSchemaFromManifest(
+ manifest: ManifestWithOptions | null | undefined,
+ themeId: string,
+): ThemeConfigurationSchema | null {
+ const fromManifest = manifest?.options;
+ if (fromManifest && fromManifest.type === 'object') return fromManifest;
+ return getThemeConfigurationSchema(themeId);
+}
+
+export function getMergedThemeConfiguration(
+ globalRaw: unknown,
+ themeId: string,
+ options?: { locale?: string; manifest?: ManifestWithOptions },
+): Record {
+ const gs = parseGlobalSettingRaw(globalRaw);
+ const schema =
+ getConfigurationSchemaFromManifest(options?.manifest ?? null, themeId) ??
+ getThemeConfigurationSchema(themeId);
+ const seed =
+ getThemeConfigurationSeed(themeId, options?.locale ?? 'zh') ??
+ (schema ? defaultsFromSchema(schema) : {});
+ const store: ThemeConfigStore =
+ gs.config && typeof gs.config === 'object' ? (gs.config as ThemeConfigStore) : {};
+ const stored = store[themeId];
+ const storedObj =
+ stored && typeof stored === 'object' && !Array.isArray(stored)
+ ? (stored as Record)
+ : {};
+ return deepMerge(deepMerge({}, seed), storedObj);
+}
+
+export function getConfig(
+ configuration: Record,
+ path: string,
+): T | undefined {
+ return getByPath(configuration, path) as T | undefined;
+}
+
+export function patchConfiguration(
+ current: Record,
+ patch: Record,
+): Record {
+ return deepMerge({ ...current }, patch);
+}
+
+export function mergeConfigIntoGlobalSetting(
+ globalRaw: unknown,
+ themeId: string,
+ themeConfig: Record,
+): GlobalSettingWithConfig {
+ const gs = parseGlobalSettingRaw(globalRaw);
+ const config: ThemeConfigStore =
+ gs.config && typeof gs.config === 'object' ? { ...(gs.config as ThemeConfigStore) } : {};
+ config[themeId] = themeConfig;
+ return { ...gs, config };
+}
+
+export function resolveSiteConfig(
+ globalRaw: unknown,
+ themeId: string,
+ locale = 'zh',
+ manifest?: ManifestWithOptions,
+ /** Draft theme config from appearance preview query (not persisted). */
+ configOverride?: Record,
+): ResolvedSiteConfig {
+ const merged = getMergedThemeConfiguration(globalRaw, themeId, { locale, manifest });
+ const effective =
+ configOverride && Object.keys(configOverride).length > 0
+ ? patchConfiguration(merged, configOverride)
+ : merged;
+ const gs = parseGlobalSettingRaw(globalRaw);
+ const legacyBundle = (gs[locale as 'zh' | 'en'] ?? gs.zh) as
+ | { globalConfig?: { urlConfig?: unknown[]; navConfig?: ResolvedSiteConfig['nav']['searchCategories'] } }
+ | undefined;
+ const legacy = legacyBundle?.globalConfig;
+
+ const headerNav = getConfig(effective, 'header.navLinks');
+ const urlConfig = getConfig(effective, 'nav.urlConfig');
+ const searchCategories = getConfig(
+ effective,
+ 'nav.searchCategories',
+ );
+
+ const navLinks = (headerNav ?? FALLBACK_HEADER_NAV).filter((item) => item.visible !== false);
+
+ return {
+ header: { navLinks },
+ nav: {
+ urlConfig: urlConfig ?? legacy?.urlConfig ?? [],
+ searchCategories: searchCategories ??
+ legacy?.navConfig ?? { categories: [], subCategories: {} },
+ },
+ };
+}
+
+export function validateAndMergeThemeConfiguration(
+ themeId: string,
+ globalRaw: unknown,
+ patch: Record,
+ manifest?: ManifestWithOptions,
+): { ok: true; config: Record } | { ok: false; message: string } {
+ const schema = getConfigurationSchemaFromManifest(manifest ?? null, themeId);
+ const current = getMergedThemeConfiguration(globalRaw, themeId, { manifest });
+ const next = patchConfiguration(current, patch);
+ const result = validateThemeConfiguration(schema, next);
+ if (!result.valid) {
+ return { ok: false, message: result.message ?? 'Invalid configuration' };
+ }
+ return { ok: true, config: next };
+}
diff --git a/toolkit/src/theme/extension/configuration/types.ts b/toolkit/src/theme/extension/configuration/types.ts
new file mode 100644
index 0000000..a190d08
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/types.ts
@@ -0,0 +1,69 @@
+/** JSON Schema–driven theme/site configuration (VS Code `contributes.configuration` style). */
+
+/** UI hints for admin settings editor (P2); stored in schema only. */
+export interface ConfigurationUiMeta {
+ widget?: string;
+ section?: string;
+ order?: number;
+ scope?: 'site' | 'locale';
+ hidden?: boolean;
+}
+
+/** JSON Schema 7 object schema for theme `reactpress.configuration`. */
+export interface ThemeConfigurationSchema {
+ $schema?: string;
+ title?: string;
+ description?: string;
+ type: 'object';
+ properties?: Record;
+ required?: string[];
+ additionalProperties?: boolean;
+}
+
+export type ThemeConfigurationPropertySchema = {
+ type?: string | string[];
+ title?: string;
+ description?: string;
+ default?: unknown;
+ enum?: unknown[];
+ items?: ThemeConfigurationPropertySchema;
+ properties?: Record;
+ required?: string[];
+ additionalProperties?: boolean;
+ /** Extension: admin widget id */
+ 'x-ui'?: ConfigurationUiMeta;
+ [key: string]: unknown;
+};
+
+export interface HeaderNavLinkConfig {
+ path: string;
+ locale?: string;
+ label?: string;
+ icon?: string;
+ visible?: boolean;
+}
+
+export interface ThemeConfigStore {
+ [themeId: string]: Record;
+}
+
+export interface GlobalSettingWithConfig {
+ zh?: { globalConfig?: unknown; [key: string]: unknown };
+ en?: { globalConfig?: unknown; [key: string]: unknown };
+ theme?: unknown;
+ config?: ThemeConfigStore;
+ [key: string]: unknown;
+}
+
+export interface ResolvedSiteConfig {
+ header: {
+ navLinks: HeaderNavLinkConfig[];
+ };
+ nav: {
+ urlConfig: unknown[];
+ searchCategories: {
+ categories?: unknown[];
+ subCategories?: Record;
+ };
+ };
+}
diff --git a/toolkit/src/theme/extension/configuration/validate.ts b/toolkit/src/theme/extension/configuration/validate.ts
new file mode 100644
index 0000000..9857fd2
--- /dev/null
+++ b/toolkit/src/theme/extension/configuration/validate.ts
@@ -0,0 +1,28 @@
+import Ajv, { type ErrorObject } from 'ajv';
+
+import type { ThemeConfigurationSchema } from './types';
+
+const ajv = new Ajv({ allErrors: true, strict: false, coerceTypes: false });
+
+export interface ConfigurationValidationResult {
+ valid: boolean;
+ errors: ErrorObject[] | null;
+ message?: string;
+}
+
+export function validateThemeConfiguration(
+ schema: ThemeConfigurationSchema | null | undefined,
+ data: unknown,
+): ConfigurationValidationResult {
+ if (!schema) {
+ return { valid: true, errors: null };
+ }
+ const validate = ajv.compile(schema);
+ const valid = validate(data) === true;
+ if (valid) {
+ return { valid: true, errors: null };
+ }
+ const errors = validate.errors ?? [];
+ const message = errors.map((e) => `${e.instancePath || '/'} ${e.message ?? ''}`.trim()).join('; ');
+ return { valid: false, errors, message: message || 'Invalid configuration' };
+}
diff --git a/toolkit/src/theme/extension/index.ts b/toolkit/src/theme/extension/index.ts
new file mode 100644
index 0000000..c6fe6c3
--- /dev/null
+++ b/toolkit/src/theme/extension/index.ts
@@ -0,0 +1,22 @@
+export { systemGlobalSettingDefaults } from '../../config/global';
+export * from './configuration';
+export {
+ TWENTYTWENTYFIVE_APPEARANCE,
+ TWENTYTWENTYFIVE_APPEARANCE_PANELS,
+ TWENTYTWENTYFIVE_APPEARANCE_SECTIONS,
+} from './presets/twentytwentyfive-appearance';
+export * from './preview';
+export * from './site-notices';
+export * from './site-settings';
+export * from './theme';
+export * from './theme-admin-locale';
+export {
+ appearanceBackgroundColor,
+ appearanceLinkColor,
+ appearancePrimaryColorForMode,
+ appearanceSecondaryBackgroundColor,
+ buildBrandingAppearanceCss,
+ buildTwentyTwentyFiveAppearanceCss,
+} from './twentytwentyfive-vars';
+export type { FetchSiteNavConfigOptions, SiteNavConfig } from './siteNav';
+export { fetchSiteNavConfig } from './siteNav';
diff --git a/toolkit/src/theme/extension/manifest-normalize.ts b/toolkit/src/theme/extension/manifest-normalize.ts
new file mode 100644
index 0000000..6ad505f
--- /dev/null
+++ b/toolkit/src/theme/extension/manifest-normalize.ts
@@ -0,0 +1,94 @@
+import type { ThemeConfigurationSchema } from './configuration/types';
+import type {
+ ThemeAppearanceGroup,
+ ThemeAppearanceManifest,
+ ThemeAppearancePanel,
+ ThemeAppearanceSection,
+ ThemeAppearanceSetting,
+ ThemeManifest,
+} from './theme';
+
+type RawRecord = Record;
+
+function asRecord(value: unknown): RawRecord | undefined {
+ return value && typeof value === 'object' && !Array.isArray(value) ? (value as RawRecord) : undefined;
+}
+
+function normalizeSetting(raw: RawRecord): ThemeAppearanceSetting {
+ return {
+ id: String(raw.id),
+ type: raw.type as ThemeAppearanceSetting['type'],
+ label: String(raw.label),
+ default: typeof raw.default === 'string' ? raw.default : undefined,
+ description: typeof raw.description === 'string' ? raw.description : undefined,
+ choices: Array.isArray(raw.choices)
+ ? (raw.choices as ThemeAppearanceSetting['choices'])
+ : undefined,
+ group: typeof raw.group === 'string' ? raw.group : undefined,
+ };
+}
+
+function normalizeGroup(raw: RawRecord): ThemeAppearanceGroup {
+ return {
+ id: String(raw.id),
+ title: String(raw.title),
+ description: typeof raw.description === 'string' ? raw.description : undefined,
+ };
+}
+
+function normalizePanel(raw: RawRecord): ThemeAppearancePanel {
+ return {
+ id: String(raw.id),
+ title: String(raw.title),
+ description: typeof raw.description === 'string' ? raw.description : undefined,
+ };
+}
+
+function normalizeSection(raw: RawRecord): ThemeAppearanceSection {
+ return {
+ id: String(raw.id),
+ title: String(raw.title),
+ panel: typeof raw.panel === 'string' ? raw.panel : undefined,
+ embed: raw.embed === 'options' ? 'options' : undefined,
+ description: typeof raw.description === 'string' ? raw.description : undefined,
+ groups: Array.isArray(raw.groups)
+ ? raw.groups.map((item) => normalizeGroup(asRecord(item) ?? {}))
+ : undefined,
+ settings: Array.isArray(raw.settings)
+ ? raw.settings.map((item) => normalizeSetting(asRecord(item) ?? {}))
+ : undefined,
+ };
+}
+
+export function normalizeAppearance(raw: unknown): ThemeAppearanceManifest | undefined {
+ const source = asRecord(raw);
+ if (!source) return undefined;
+
+ const panels = Array.isArray(source.panels)
+ ? source.panels.map((item) => normalizePanel(asRecord(item) ?? {}))
+ : undefined;
+
+ const sections = Array.isArray(source.sections)
+ ? source.sections.map((item) => normalizeSection(asRecord(item) ?? {}))
+ : [];
+
+ if (panels === undefined && sections.length === 0) return undefined;
+ return { panels, sections };
+}
+
+export function normalizePlatformFields(o: RawRecord): Pick<
+ ThemeManifest,
+ 'requires' | 'supports' | 'templates' | 'options'
+> {
+ const requires = typeof o.requires === 'string' ? o.requires : undefined;
+ const supports = o.supports as ThemeManifest['supports'];
+ const templates = o.templates as ThemeManifest['templates'];
+ const options = o.options as ThemeConfigurationSchema | undefined;
+
+ return {
+ requires,
+ supports: supports && typeof supports === 'object' ? supports : undefined,
+ templates: templates && typeof templates === 'object' ? templates : undefined,
+ options: options?.type === 'object' ? options : undefined,
+ };
+}
diff --git a/toolkit/src/theme/extension/presets/twentytwentyfive-appearance.ts b/toolkit/src/theme/extension/presets/twentytwentyfive-appearance.ts
new file mode 100644
index 0000000..183b712
--- /dev/null
+++ b/toolkit/src/theme/extension/presets/twentytwentyfive-appearance.ts
@@ -0,0 +1,194 @@
+import type { ThemeAppearancePanel, ThemeAppearanceSection } from '../theme';
+
+/** Sidebar nav panels for Twenty Twenty-Five appearance editor. */
+export const TWENTYTWENTYFIVE_APPEARANCE_PANELS: ThemeAppearancePanel[] = [
+ {
+ id: 'basic',
+ title: '基础配置',
+ description: '站点信息与页脚',
+ },
+ {
+ id: 'style',
+ title: '样式配置',
+ description: '颜色与背景',
+ },
+ {
+ id: 'layout',
+ title: '布局配置',
+ description: '导航与页面结构',
+ },
+];
+
+/** Default appearance sections for Twenty Twenty-Five (also in `themes/.../theme.json`). */
+export const TWENTYTWENTYFIVE_APPEARANCE_SECTIONS: ThemeAppearanceSection[] = [
+ {
+ id: 'identity',
+ panel: 'basic',
+ title: '站点身份',
+ settings: [
+ {
+ id: 'displayTitle',
+ type: 'text',
+ label: '站点标题',
+ description: '留空则继承「设置 → 常规」中的站点标题',
+ },
+ {
+ id: 'displayTagline',
+ type: 'text',
+ label: '站点副标题',
+ description: '留空则继承「设置 → 常规」中的副标题',
+ },
+ {
+ id: 'siteLogo',
+ type: 'image',
+ label: '站点 Logo',
+ description: '留空则继承「设置 → 常规」中的站点 Logo',
+ },
+ {
+ id: 'siteFavicon',
+ type: 'image',
+ label: '站点图标',
+ description: '留空则继承「设置 → 常规」中的站点图标',
+ },
+ {
+ id: 'siteNotice',
+ type: 'noticeList',
+ label: '站点公告',
+ description: '未自定义时继承「设置 → 常规」;可为本主题单独配置多条公告与顺序',
+ },
+ ],
+ },
+ {
+ id: 'about',
+ panel: 'basic',
+ title: '关于我们与页脚',
+ settings: [
+ {
+ id: 'systemFooterInfo',
+ type: 'textarea',
+ label: '页脚说明',
+ description: '支持 HTML;仅本主题生效',
+ },
+ {
+ id: 'aboutUsGithubUrl',
+ type: 'text',
+ label: 'GitHub 链接',
+ description: '页头与页脚 GitHub 图标;留空不显示',
+ },
+ {
+ id: 'aboutUsCommentQr',
+ type: 'image',
+ label: '社群/评论二维码',
+ description: '悬停展示的图片地址',
+ },
+ {
+ id: 'aboutUsWechatQr',
+ type: 'image',
+ label: '微信二维码',
+ description: '悬停展示的图片地址',
+ },
+ ],
+ },
+ {
+ id: 'colors',
+ panel: 'style',
+ title: '颜色',
+ description: '与访客站顶栏「日 / 夜」切换对应,可分别配置浅色与深色模式',
+ groups: [
+ {
+ id: 'light',
+ title: '浅色模式',
+ description: '访客关闭深色模式时生效',
+ },
+ {
+ id: 'dark',
+ title: '深色模式',
+ description: '访客开启深色模式时生效',
+ },
+ ],
+ settings: [
+ { id: 'primaryColor', group: 'light', type: 'color', label: '主色', default: '#f44336' },
+ {
+ id: 'backgroundColor',
+ group: 'light',
+ type: 'color',
+ label: '背景色',
+ default: '#ffffff',
+ description: '页面与内容区背景;设置背景图后主区域会透明以露出图片',
+ },
+ {
+ id: 'secondaryBackgroundColor',
+ group: 'light',
+ type: 'color',
+ label: '次要背景色',
+ default: '#f3f5f7',
+ description: '卡片间隔、侧栏等次要区域',
+ },
+ {
+ id: 'linkColor',
+ group: 'light',
+ type: 'color',
+ label: '链接色',
+ default: '#4299e1',
+ description: '正文 Markdown 中的链接颜色',
+ },
+ {
+ id: 'darkPrimaryColor',
+ group: 'dark',
+ type: 'color',
+ label: '主色',
+ default: '#f44336',
+ },
+ {
+ id: 'darkBackgroundColor',
+ group: 'dark',
+ type: 'color',
+ label: '背景色',
+ default: '#1e2a36',
+ description: '页面与主内容区背景',
+ },
+ {
+ id: 'darkSecondaryBackgroundColor',
+ group: 'dark',
+ type: 'color',
+ label: '次要背景色',
+ default: '#1a242f',
+ description: '卡片、顶栏、侧栏等次要区域',
+ },
+ {
+ id: 'darkLinkColor',
+ group: 'dark',
+ type: 'color',
+ label: '链接色',
+ default: '#4299e1',
+ description: '正文 Markdown 中的链接颜色',
+ },
+ ],
+ },
+ {
+ id: 'background',
+ panel: 'style',
+ title: '背景图片',
+ settings: [
+ {
+ id: 'backgroundImage',
+ type: 'image',
+ label: '背景图',
+ description: '设置后将覆盖纯色背景(宽屏可见);仅本主题生效',
+ },
+ ],
+ },
+ {
+ id: 'themeConfiguration',
+ panel: 'layout',
+ title: '顶栏与网址导航',
+ embed: 'options',
+ description: '固定导航、/nav 卡片与聚合搜索',
+ },
+];
+
+/** Full `theme.json` → `appearance` block for Twenty Twenty-Five. */
+export const TWENTYTWENTYFIVE_APPEARANCE = {
+ panels: TWENTYTWENTYFIVE_APPEARANCE_PANELS,
+ sections: TWENTYTWENTYFIVE_APPEARANCE_SECTIONS,
+};
diff --git a/toolkit/src/theme/extension/preview.ts b/toolkit/src/theme/extension/preview.ts
new file mode 100644
index 0000000..e8d2953
--- /dev/null
+++ b/toolkit/src/theme/extension/preview.ts
@@ -0,0 +1,143 @@
+import axios from 'axios';
+
+import { resolveThemeApiBaseUrl } from '../api/api';
+import {
+ parsePreviewTokenFromNextCtx,
+ type PreviewDraftPayload,
+} from '../preview/preview-draft';
+import { mergePreviewMods } from '../preview/preview-mods';
+import { resolveSiteConfig } from './configuration/resolve';
+import type { ResolvedSiteConfig, ThemeConfigurationSchema } from './configuration/types';
+import {
+ appearancePrimaryColor,
+ applyThemeModsToSiteSetting,
+ getThemeStateFromGlobalSetting,
+ type ThemeMods,
+} from './theme';
+
+/** Draft payload returned by `GET /extension/themes/preview-draft/:token`. */
+export type PreviewDraftResponse = PreviewDraftPayload;
+
+export type NextPreviewCtx = Parameters[0];
+
+export type PreviewDraftFetcher = (token: string) => Promise;
+
+/** Relative API path for a preview draft token. */
+export function previewDraftApiPath(token: string): string {
+ return `/extension/themes/preview-draft/${encodeURIComponent(token.trim())}`;
+}
+
+/**
+ * Normalize API or axios-interceptor output into a preview draft.
+ * Accepts either `{ success, data }` envelopes or plain draft objects.
+ */
+export function normalizePreviewDraftData(raw: unknown): PreviewDraftResponse {
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
+ return {};
+ }
+ const envelope = raw as { success?: boolean; data?: unknown };
+ if ('success' in envelope && 'data' in envelope) {
+ if (envelope.success === false) return {};
+ return normalizePreviewDraftData(envelope.data);
+ }
+ const draft = raw as PreviewDraftResponse;
+ return {
+ themeId: typeof draft.themeId === 'string' ? draft.themeId : undefined,
+ mods: draft.mods,
+ configuration: draft.configuration,
+ };
+}
+
+/** Load short-lived customizer preview payload (public GET by token). */
+export async function fetchPreviewDraft(
+ token: string,
+ options?: {
+ baseURL?: string;
+ fetcher?: PreviewDraftFetcher;
+ },
+): Promise {
+ const trimmed = token.trim();
+ if (!trimmed) return {};
+ if (options?.fetcher) {
+ return normalizePreviewDraftData(await options.fetcher(trimmed));
+ }
+
+ const base = (options?.baseURL ?? resolveThemeApiBaseUrl()).replace(/\/$/, '');
+ try {
+ const { data } = await axios.get(`${base}${previewDraftApiPath(trimmed)}`, {
+ timeout: 60_000,
+ });
+ return normalizePreviewDraftData(data);
+ } catch {
+ return {};
+ }
+}
+
+export type ResolveThemePreviewContextInput = {
+ globalSettingRaw: unknown;
+ setting: Record;
+ locale?: string;
+ ctx?: NextPreviewCtx;
+ manifest?: { options?: ThemeConfigurationSchema };
+ /** Override draft loader (e.g. theme-local axios with auth interceptors). */
+ fetchDraft?: PreviewDraftFetcher;
+ apiBaseURL?: string;
+};
+
+/** Fully resolved visitor runtime after applying optional preview draft. */
+export type ResolvedThemePreviewContext = {
+ siteConfig: ResolvedSiteConfig;
+ setting: Record;
+ colorPrimary: string;
+ effectiveMods: ThemeMods;
+ configThemeId: string;
+ isPreview: boolean;
+ draft: PreviewDraftResponse | null;
+};
+
+/**
+ * Resolve site config, setting overlays, and customizer mods for a Next.js `_app` request.
+ * Themes should call this once in `getInitialProps` and pass results into their context/provider.
+ */
+export async function resolveThemePreviewContext(
+ input: ResolveThemePreviewContextInput,
+): Promise {
+ const locale = input.locale ?? 'zh';
+ const themeState = getThemeStateFromGlobalSetting(input.globalSettingRaw);
+ const previewToken = input.ctx ? parsePreviewTokenFromNextCtx(input.ctx) : '';
+
+ let draft: PreviewDraftResponse | null = null;
+ if (previewToken) {
+ draft = await fetchPreviewDraft(previewToken, {
+ baseURL: input.apiBaseURL,
+ fetcher: input.fetchDraft,
+ });
+ }
+
+ const previewConfig = draft?.configuration ?? {};
+ const previewMods = draft?.mods ?? {};
+ const configThemeId = draft?.themeId ?? themeState.activeTheme;
+
+ const siteConfig = resolveSiteConfig(
+ input.globalSettingRaw,
+ configThemeId,
+ locale,
+ input.manifest,
+ Object.keys(previewConfig).length > 0 ? previewConfig : undefined,
+ );
+
+ const savedMods = themeState.mods[configThemeId] ?? {};
+ const effectiveMods = mergePreviewMods(savedMods, previewMods);
+ const setting = applyThemeModsToSiteSetting(input.setting, effectiveMods);
+ const colorPrimary = appearancePrimaryColor(effectiveMods);
+
+ return {
+ siteConfig,
+ setting,
+ colorPrimary,
+ effectiveMods,
+ configThemeId,
+ isPreview: Boolean(previewToken),
+ draft,
+ };
+}
diff --git a/toolkit/src/theme/extension/site-notices.ts b/toolkit/src/theme/extension/site-notices.ts
new file mode 100644
index 0000000..fa7729b
--- /dev/null
+++ b/toolkit/src/theme/extension/site-notices.ts
@@ -0,0 +1,95 @@
+/** Single site-wide notice (stored in `Setting.systemNoticeInfo` as JSON). */
+export type SiteNoticeItem = {
+ id: string;
+ content: string;
+ /** Default true when omitted. */
+ enabled?: boolean;
+};
+
+function newNoticeId(): string {
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
+ return crypto.randomUUID();
+ }
+ return `notice-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
+}
+
+function normalizeItem(raw: unknown, index: number): SiteNoticeItem | null {
+ if (typeof raw === 'string') {
+ const content = raw.trim();
+ if (!content) return null;
+ return { id: `legacy-${index}`, content, enabled: true };
+ }
+ if (!raw || typeof raw !== 'object') return null;
+ const o = raw as Record;
+ const content = typeof o.content === 'string' ? o.content : typeof o.text === 'string' ? o.text : '';
+ if (!content.trim()) return null;
+ const id = typeof o.id === 'string' && o.id.trim() ? o.id.trim() : newNoticeId();
+ const enabled = o.enabled === false ? false : true;
+ return { id, content, enabled };
+}
+
+/**
+ * Parse `systemNoticeInfo` from DB / API.
+ * Supports JSON array, legacy newline-separated text, or plain string.
+ */
+export function parseSiteNotices(raw: unknown): SiteNoticeItem[] {
+ if (raw == null) return [];
+
+ if (Array.isArray(raw)) {
+ return raw.map((item, i) => normalizeItem(item, i)).filter((x): x is SiteNoticeItem => x != null);
+ }
+
+ const text = String(raw).trim();
+ if (!text) return [];
+
+ if (text.startsWith('[')) {
+ try {
+ const parsed = JSON.parse(text) as unknown;
+ if (Array.isArray(parsed)) {
+ return parsed.map((item, i) => normalizeItem(item, i)).filter((x): x is SiteNoticeItem => x != null);
+ }
+ } catch {
+ /* fall through */
+ }
+ }
+
+ return text
+ .split(/\r?\n/)
+ .map((line) => line.trim())
+ .filter(Boolean)
+ .map((content, i) => ({ id: `legacy-${i}`, content, enabled: true }));
+}
+
+/** Persist notices for `Setting.systemNoticeInfo`. */
+export function serializeSiteNotices(items: SiteNoticeItem[]): string {
+ const cleaned = items
+ .map((item) => ({
+ id: item.id?.trim() || newNoticeId(),
+ content: item.content?.trim() ?? '',
+ enabled: item.enabled !== false,
+ }))
+ .filter((item) => item.content.length > 0);
+ return JSON.stringify(cleaned);
+}
+
+/** Ordered notice HTML/text lines for the visitor banner (enabled only). */
+export function siteNoticeDisplayLines(raw: unknown): string[] {
+ return parseSiteNotices(raw)
+ .filter((n) => n.enabled !== false)
+ .map((n) => n.content);
+}
+
+export function createEmptySiteNotice(): SiteNoticeItem {
+ return { id: newNoticeId(), content: '', enabled: true };
+}
+
+/** Whether theme mod `siteNotice` is empty or identical to system `systemNoticeInfo`. */
+export function siteNoticeModInheritsSystem(
+ modRaw: string | undefined,
+ systemRaw: unknown,
+): boolean {
+ if (!modRaw?.trim()) return true;
+ const modSerialized = serializeSiteNotices(parseSiteNotices(modRaw));
+ const sysSerialized = serializeSiteNotices(parseSiteNotices(systemRaw));
+ return modSerialized === sysSerialized;
+}
diff --git a/toolkit/src/theme/extension/site-settings.ts b/toolkit/src/theme/extension/site-settings.ts
new file mode 100644
index 0000000..27282d0
--- /dev/null
+++ b/toolkit/src/theme/extension/site-settings.ts
@@ -0,0 +1,174 @@
+/**
+ * Site-wide vs theme-owned settings.
+ *
+ * - **System settings** (`SYSTEM_SETTING_FIELD_KEYS`): shared defaults (URLs, site identity,
+ * SEO, i18n, mail). Edited under admin → 设置.
+ * - **Theme mods** (`THEME_BRANDING_*`): per-theme overrides in `globalSetting.theme.mods[themeId]`.
+ * Empty mod → inherit the matching system setting at runtime.
+ * - **Theme-only** (footer / 关于我们): appearance only, no system setting column.
+ */
+
+import {
+ applyThemeModsToSiteSetting,
+ seedThemeModsFromLegacySetting,
+ THEME_BRANDING_DIRECT_MODS,
+ THEME_BRANDING_MOD_TO_SETTING,
+} from './branding-mods';
+import {
+ getThemeStateFromGlobalSetting,
+ type GlobalSettingWithTheme,
+ type SiteThemeState,
+ type ThemeMods,
+} from './theme';
+
+export {
+ applyThemeModsToSiteSetting,
+ seedThemeModsFromLegacySetting,
+ THEME_BRANDING_DIRECT_MODS,
+ THEME_BRANDING_MOD_TO_SETTING,
+} from './branding-mods';
+
+/** Site identity defaults (admin → 设置 → 常规). Themes may override via customizer mods. */
+export const SITE_DEFAULT_SETTING_KEYS = [
+ 'systemTitle',
+ 'systemSubTitle',
+ 'systemLogo',
+ 'systemFavicon',
+ 'systemNoticeInfo',
+] as const;
+
+/** SEO defaults (admin → 设置 → SEO). Themes may override via customizer mods. */
+export const SEO_DEFAULT_SETTING_KEYS = [
+ 'seoKeyword',
+ 'seoDesc',
+ 'baiduAnalyticsId',
+ 'googleAnalyticsId',
+] as const;
+
+/** Theme-only appearance (no system setting column). */
+export const THEME_ONLY_APPEARANCE_KEYS = [
+ 'systemFooterInfo',
+ 'aboutUsGithubUrl',
+ 'aboutUsCommentQr',
+ 'aboutUsWechatQr',
+] as const;
+
+/** DB columns managed as system configuration (admin → 设置). */
+export const SYSTEM_SETTING_FIELD_KEYS = [
+ 'i18n',
+ 'systemUrl',
+ 'adminSystemUrl',
+ ...SITE_DEFAULT_SETTING_KEYS,
+ ...SEO_DEFAULT_SETTING_KEYS,
+ 'smtpHost',
+ 'smtpPort',
+ 'smtpUser',
+ 'smtpPass',
+ 'smtpFromUser',
+] as const;
+
+export type SystemSettingFieldKey = (typeof SYSTEM_SETTING_FIELD_KEYS)[number];
+
+/** @deprecated Use SITE_DEFAULT_SETTING_KEYS + SEO_DEFAULT_SETTING_KEYS */
+export const LEGACY_APPEARANCE_SETTING_KEYS = [
+ ...SITE_DEFAULT_SETTING_KEYS,
+ ...SEO_DEFAULT_SETTING_KEYS,
+ ...THEME_ONLY_APPEARANCE_KEYS,
+] as const;
+
+export type ThemeBrandingDirectMod = (typeof THEME_BRANDING_DIRECT_MODS)[number];
+
+/** Keys exposed to anonymous theme `setting/get` (plus computed appearance overlay). */
+export const PUBLIC_SETTING_KEYS = [
+ 'i18n',
+ 'systemUrl',
+ 'adminSystemUrl',
+ 'globalSetting',
+ ...LEGACY_APPEARANCE_SETTING_KEYS,
+] as const;
+
+function modHasValue(mods: ThemeMods, modId: string): boolean {
+ const v = mods[modId];
+ return v != null && String(v).trim() !== '';
+}
+
+/** Copy legacy appearance columns into theme mods when mods are empty. */
+export function migrateLegacyAppearanceToThemeMods(
+ globalRaw: unknown,
+ settingRow: Record,
+): { global: GlobalSettingWithTheme; changed: boolean } {
+ const base =
+ globalRaw && typeof globalRaw === 'object'
+ ? ({ ...(globalRaw as GlobalSettingWithTheme) } as GlobalSettingWithTheme)
+ : ({} as GlobalSettingWithTheme);
+ const themeState = getThemeStateFromGlobalSetting(base);
+ const themeIds = new Set([
+ themeState.activeTheme,
+ ...themeState.installedThemes,
+ ]);
+ const mods: Record =
+ themeState.mods && typeof themeState.mods === 'object'
+ ? { ...(themeState.mods as Record) }
+ : {};
+
+ let changed = false;
+ for (const themeId of themeIds) {
+ if (!themeId) continue;
+ const current = mods[themeId] ?? {};
+ const seeded = seedThemeModsFromLegacySetting(settingRow, current);
+ const currentHasValues = Object.keys(current).some((k) => modHasValue(current, k));
+ const seededHasValues = Object.keys(seeded).some((k) => modHasValue(seeded, k));
+ if (!currentHasValues && seededHasValues) {
+ mods[themeId] = seeded;
+ changed = true;
+ }
+ }
+
+ if (!changed) {
+ return { global: base, changed: false };
+ }
+ const next: SiteThemeState = { ...themeState, mods };
+ return { global: { ...base, theme: next }, changed: true };
+}
+
+/** Strip non-system fields from admin PATCH payloads. */
+export function pickSystemSettingPatch(patch: Record): Record {
+ const out: Record = {};
+ for (const key of SYSTEM_SETTING_FIELD_KEYS) {
+ if (key in patch) out[key] = patch[key];
+ }
+ return out;
+}
+
+/** Overlay active theme mods onto a Setting row for runtime (themes, mail templates). */
+export function resolveEffectiveSettingRow>(
+ row: T,
+ globalRaw?: unknown,
+ themeId?: string,
+): T {
+ const gs = globalRaw ?? row.globalSetting;
+ const themeState = getThemeStateFromGlobalSetting(
+ typeof gs === 'string'
+ ? (() => {
+ try {
+ return JSON.parse(gs) as unknown;
+ } catch {
+ return {};
+ }
+ })()
+ : gs,
+ );
+ const id = themeId ?? themeState.activeTheme;
+ const mods = themeState.mods[id] ?? {};
+ return applyThemeModsToSiteSetting(row, mods);
+}
+
+/** Public API view: system keys + branding overlay + globalSetting. */
+export function buildPublicSettingView(row: Record): Record {
+ const effective = resolveEffectiveSettingRow(row);
+ const out: Record = {};
+ for (const key of PUBLIC_SETTING_KEYS) {
+ if (effective[key] !== undefined) out[key] = effective[key];
+ }
+ return out;
+}
diff --git a/toolkit/src/theme/extension/siteNav.ts b/toolkit/src/theme/extension/siteNav.ts
new file mode 100644
index 0000000..e2f4343
--- /dev/null
+++ b/toolkit/src/theme/extension/siteNav.ts
@@ -0,0 +1,34 @@
+import type { ThemeConfigurationSchema } from './configuration/types';
+import type { ResolvedSiteConfig } from './configuration/types';
+import { resolveSiteConfig } from './configuration/resolve';
+import { getThemeStateFromGlobalSetting } from './theme';
+import { safeJsonParse } from '../api/json';
+
+export type SiteNavConfig = {
+ urlConfig: NonNullable['urlConfig']>;
+ searchCategories: NonNullable['searchCategories']>;
+};
+
+export type FetchSiteNavConfigOptions = {
+ locale: string;
+ manifest: { id: string; options?: ThemeConfigurationSchema };
+ getSetting: () => Promise<{ globalSetting?: unknown }>;
+};
+
+/** Load nav blocks for `/nav` pages (client transitions may skip slimmed `_app` nav). */
+export async function fetchSiteNavConfig(options: FetchSiteNavConfigOptions): Promise {
+ const setting = await options.getSetting();
+ const globalSettingRaw = safeJsonParse>(setting.globalSetting, {});
+ const themeState = getThemeStateFromGlobalSetting(globalSettingRaw);
+ const siteConfig = resolveSiteConfig(
+ globalSettingRaw,
+ themeState.activeTheme,
+ options.locale,
+ options.manifest,
+ );
+
+ return {
+ urlConfig: siteConfig.nav?.urlConfig ?? [],
+ searchCategories: siteConfig.nav?.searchCategories ?? { categories: [], subCategories: {} },
+ };
+}
diff --git a/toolkit/src/theme/extension/theme-admin-locale.io.ts b/toolkit/src/theme/extension/theme-admin-locale.io.ts
new file mode 100644
index 0000000..1039bf3
--- /dev/null
+++ b/toolkit/src/theme/extension/theme-admin-locale.io.ts
@@ -0,0 +1,21 @@
+import * as fs from 'fs';
+import * as path from 'path';
+
+import { THEME_LOCALE_DIR, type ThemeAdminLocaleMessages } from './theme-admin-locale';
+
+export function readThemeAdminLocaleFile(
+ themeDir: string,
+ locale: string,
+): ThemeAdminLocaleMessages | null {
+ const safeLocale = /^[a-z]{2}(?:-[a-z]{2})?$/i.test(locale) ? locale : 'en';
+ const filePath = path.join(themeDir, THEME_LOCALE_DIR, `${safeLocale}.json`);
+ if (!fs.existsSync(filePath)) return null;
+ try {
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
+ return raw && typeof raw === 'object' && !Array.isArray(raw)
+ ? (raw as ThemeAdminLocaleMessages)
+ : null;
+ } catch {
+ return null;
+ }
+}
diff --git a/toolkit/src/theme/extension/theme-admin-locale.ts b/toolkit/src/theme/extension/theme-admin-locale.ts
new file mode 100644
index 0000000..6645ceb
--- /dev/null
+++ b/toolkit/src/theme/extension/theme-admin-locale.ts
@@ -0,0 +1,95 @@
+import {
+ getNestedLocaleString,
+ getNestedLocaleValue,
+} from '../../utils/object';
+import type {
+ ThemeConfigurationPropertySchema,
+ ThemeConfigurationSchema,
+} from './configuration/types';
+
+/** Customizer copy shipped with the theme (`locales/{locale}.json`). */
+export type ThemeAdminLocaleMessages = Record;
+
+export const THEME_LOCALE_DIR = 'locales';
+
+export { getNestedLocaleString, getNestedLocaleValue };
+
+/** Resolve admin copy from theme locale bundle; falls back to manifest text. */
+export function resolveThemeAdminLocaleText(
+ messages: ThemeAdminLocaleMessages | null | undefined,
+ dotPath: string,
+ fallback?: string,
+): string {
+ const fromLocale = messages ? getNestedLocaleString(messages, dotPath) : undefined;
+ if (fromLocale) return fromLocale;
+ return fallback?.trim() ?? '';
+}
+
+export function resolveThemeAdminLocaleTags(
+ messages: ThemeAdminLocaleMessages | null | undefined,
+ fallback?: string[],
+): string[] {
+ const raw = messages ? getNestedLocaleValue(messages, 'meta.tags') : undefined;
+ if (Array.isArray(raw) && raw.every((item) => typeof item === 'string')) {
+ return raw as string[];
+ }
+ return fallback ?? [];
+}
+
+function localizeSchemaField(
+ field: ThemeConfigurationPropertySchema,
+ pathParts: string[],
+ resolve: (path: string, fallback?: string) => string,
+): void {
+ if (typeof field.title === 'string') {
+ field.title = resolve([...pathParts, 'title'].join('.'), field.title);
+ }
+ if (typeof field.description === 'string') {
+ field.description = resolve([...pathParts, 'description'].join('.'), field.description);
+ }
+
+ if (field.properties) {
+ for (const [key, child] of Object.entries(field.properties)) {
+ localizeSchemaField(child, [...pathParts, key], resolve);
+ }
+ }
+
+ const items = field.items;
+ if (items && typeof items === 'object' && !Array.isArray(items)) {
+ const itemSchema = items as ThemeConfigurationPropertySchema;
+ if (itemSchema.properties) {
+ for (const [key, child] of Object.entries(itemSchema.properties)) {
+ localizeSchemaField(child, [...pathParts, 'items', key], resolve);
+ }
+ } else {
+ localizeSchemaField(itemSchema, [...pathParts, 'items'], resolve);
+ }
+ }
+}
+
+/** Apply theme admin locale messages to options JSON Schema titles/descriptions. */
+export function localizeThemeConfigurationSchemaWithLocale(
+ schema: ThemeConfigurationSchema | null | undefined,
+ messages: ThemeAdminLocaleMessages | null | undefined,
+): ThemeConfigurationSchema | null {
+ if (!schema) return null;
+
+ const out = structuredClone(schema) as ThemeConfigurationSchema;
+ const resolve = (dotPath: string, fallback?: string) =>
+ resolveThemeAdminLocaleText(messages, `options.${dotPath}`, fallback);
+
+ if (typeof out.title === 'string') {
+ out.title = resolve('title', out.title);
+ }
+ if (typeof out.description === 'string') {
+ out.description = resolve('description', out.description);
+ }
+
+ if (out.properties) {
+ for (const [key, field] of Object.entries(out.properties)) {
+ localizeSchemaField(field, [key], resolve);
+ }
+ }
+
+ return out;
+}
diff --git a/toolkit/src/theme/extension/theme.ts b/toolkit/src/theme/extension/theme.ts
new file mode 100644
index 0000000..4d7a771
--- /dev/null
+++ b/toolkit/src/theme/extension/theme.ts
@@ -0,0 +1,189 @@
+/** WordPress-style theme manifest and site theme state (shared by server / web / themes). */
+
+import type { ThemeConfigurationSchema } from './configuration/types';
+import { normalizeAppearance, normalizePlatformFields } from './manifest-normalize';
+
+export interface ThemeAppearanceChoice {
+ value: string;
+ label: string;
+}
+
+/** Appearance control (declared in `theme.json` → `appearance.sections`). */
+export interface ThemeAppearanceSetting {
+ id: string;
+ type: 'color' | 'text' | 'image' | 'textarea' | 'checkbox' | 'select' | 'noticeList';
+ label: string;
+ default?: string;
+ description?: string;
+ choices?: ThemeAppearanceChoice[];
+ /** Sub-group within a section (e.g. light vs dark colors). */
+ group?: string;
+}
+
+/** Sidebar nav panel (Site Identity, Colors, …). */
+export interface ThemeAppearancePanel {
+ id: string;
+ title: string;
+ description?: string;
+}
+
+/** Control group inside a section panel (e.g. Light / Dark). */
+export interface ThemeAppearanceGroup {
+ id: string;
+ title: string;
+ description?: string;
+}
+
+export interface ThemeAppearanceSection {
+ id: string;
+ title: string;
+ /** Nav panel id (`appearance.panels[].id`). */
+ panel?: string;
+ settings?: ThemeAppearanceSetting[];
+ groups?: ThemeAppearanceGroup[];
+ /** Embed the theme `options` schema form instead of inline controls. */
+ embed?: 'options';
+ description?: string;
+}
+
+export interface ThemeAppearanceManifest {
+ panels?: ThemeAppearancePanel[];
+ sections: ThemeAppearanceSection[];
+}
+
+export type { ThemeMods } from './branding-mods';
+export {
+ applyThemeModsToSiteSetting,
+ seedThemeModsFromLegacySetting,
+ THEME_BRANDING_DIRECT_MODS,
+ THEME_BRANDING_MOD_TO_SETTING,
+} from './branding-mods';
+
+import type { ThemeMods } from './branding-mods';
+import { appearancePrimaryColorForMode } from './twentytwentyfive-vars';
+
+export { appearancePrimaryColorForMode } from './twentytwentyfive-vars';
+
+/** Primary color from appearance mods (Ant Design / CSS), light mode. */
+export function appearancePrimaryColor(mods: ThemeMods, fallback = '#f44336'): string {
+ return appearancePrimaryColorForMode(mods, false, fallback);
+}
+
+export interface ThemeManifest {
+ id: string;
+ name: string;
+ version: string;
+ description?: string;
+ author?: string;
+ authorUri?: string;
+ themeUri?: string;
+ tags?: string[];
+ /** Theme cover image path (relative to theme root). Shown in admin theme list / preview. */
+ cover?: string;
+ requires?: string;
+ supports?: Record;
+ templates?: Record;
+ options?: ThemeConfigurationSchema;
+ appearance?: ThemeAppearanceManifest;
+}
+
+export interface SiteThemeState {
+ activeTheme: string;
+ installedThemes: string[];
+ mods: Record;
+ previewThemeId?: string;
+}
+
+export const DEFAULT_ACTIVE_THEME = 'twentytwentyfive';
+
+export const defaultSiteThemeState: SiteThemeState = {
+ activeTheme: DEFAULT_ACTIVE_THEME,
+ installedThemes: [DEFAULT_ACTIVE_THEME],
+ mods: {},
+};
+
+export interface GlobalSettingWithTheme {
+ zh?: unknown;
+ en?: unknown;
+ theme?: SiteThemeState;
+ config?: Record>;
+ [key: string]: unknown;
+}
+
+export function parseThemeManifest(raw: unknown): ThemeManifest | null {
+ if (!raw || typeof raw !== 'object') return null;
+ const o = raw as Record;
+ if (typeof o.id !== 'string' || typeof o.name !== 'string') return null;
+
+ const platform = normalizePlatformFields(o);
+ const appearance = normalizeAppearance(o.appearance);
+
+ return {
+ id: o.id,
+ name: o.name,
+ version: typeof o.version === 'string' ? o.version : '1.0.0',
+ description: typeof o.description === 'string' ? o.description : undefined,
+ author: typeof o.author === 'string' ? o.author : undefined,
+ authorUri: typeof o.authorUri === 'string' ? o.authorUri : undefined,
+ themeUri: typeof o.themeUri === 'string' ? o.themeUri : undefined,
+ tags: Array.isArray(o.tags) ? o.tags.filter((t): t is string => typeof t === 'string') : undefined,
+ cover: typeof o.cover === 'string' ? o.cover : undefined,
+ ...platform,
+ appearance,
+ };
+}
+
+export function getThemeStateFromGlobalSetting(raw: unknown): SiteThemeState {
+ if (!raw || typeof raw !== 'object') return { ...defaultSiteThemeState };
+ const gs = raw as GlobalSettingWithTheme;
+ const theme = gs.theme;
+ if (!theme || typeof theme !== 'object') return { ...defaultSiteThemeState };
+ return {
+ activeTheme:
+ typeof theme.activeTheme === 'string' ? theme.activeTheme : defaultSiteThemeState.activeTheme,
+ installedThemes: Array.isArray(theme.installedThemes)
+ ? theme.installedThemes.filter((id): id is string => typeof id === 'string')
+ : [...defaultSiteThemeState.installedThemes],
+ mods:
+ theme.mods && typeof theme.mods === 'object'
+ ? (theme.mods as Record)
+ : {},
+ previewThemeId:
+ typeof theme.previewThemeId === 'string' ? theme.previewThemeId : undefined,
+ };
+}
+
+export { resolvePublicAssetUrl } from '../content/assets';
+export {
+ appendPreviewConfigToUrl,
+ parsePreviewConfigFromNextCtx,
+ parsePreviewConfigFromRequestUrl,
+ parsePreviewConfigParam,
+ PREVIEW_CONFIG_QUERY_KEY,
+} from '../preview/preview-config';
+export {
+ appendPreviewTokenToUrl,
+ parsePreviewTokenFromNextCtx,
+ parsePreviewTokenFromRequestUrl,
+ PREVIEW_TOKEN_QUERY_KEY,
+ type PreviewDraftPayload,
+} from '../preview/preview-draft';
+export {
+ appendPreviewModsToUrl,
+ mergePreviewMods,
+ parsePreviewModsFromNextCtx,
+ parsePreviewModsFromRequestUrl,
+ parsePreviewModsParam,
+ PREVIEW_MODS_QUERY_KEY,
+} from '../preview/preview-mods';
+
+export function mergeThemeStateIntoGlobalSetting(
+ raw: unknown,
+ patch: Partial,
+): GlobalSettingWithTheme {
+ const base =
+ raw && typeof raw === 'object' ? ({ ...(raw as GlobalSettingWithTheme) } as GlobalSettingWithTheme) : {};
+ const current = getThemeStateFromGlobalSetting(base);
+ base.theme = { ...current, ...patch };
+ return base;
+}
diff --git a/toolkit/src/theme/extension/twentytwentyfive-vars.ts b/toolkit/src/theme/extension/twentytwentyfive-vars.ts
new file mode 100644
index 0000000..dc1b6d2
--- /dev/null
+++ b/toolkit/src/theme/extension/twentytwentyfive-vars.ts
@@ -0,0 +1,114 @@
+import { brandingModValue, type ThemeMods } from './branding-mods';
+
+const HEX_COLOR = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
+
+function normalizeHexColor(value: string | undefined): string | undefined {
+ if (!value) return undefined;
+ const trimmed = value.trim();
+ if (!HEX_COLOR.test(trimmed)) return undefined;
+ if (trimmed.length === 4) {
+ const [, r, g, b] = trimmed;
+ return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
+ }
+ return trimmed.toLowerCase();
+}
+
+function hexToRgbChannels(hex: string): string | null {
+ const normalized = normalizeHexColor(hex);
+ if (!normalized) return null;
+ const raw = normalized.slice(1);
+ const r = parseInt(raw.slice(0, 2), 16);
+ const g = parseInt(raw.slice(2, 4), 16);
+ const b = parseInt(raw.slice(4, 6), 16);
+ if ([r, g, b].some((n) => Number.isNaN(n))) return null;
+ return `${r}, ${g}, ${b}`;
+}
+
+type ColorModKeys = {
+ primary: string;
+ background: string;
+ secondaryBackground: string;
+ link: string;
+};
+
+const LIGHT_COLOR_KEYS: ColorModKeys = {
+ primary: 'primaryColor',
+ background: 'backgroundColor',
+ secondaryBackground: 'secondaryBackgroundColor',
+ link: 'linkColor',
+};
+
+const DARK_COLOR_KEYS: ColorModKeys = {
+ primary: 'darkPrimaryColor',
+ background: 'darkBackgroundColor',
+ secondaryBackground: 'darkSecondaryBackgroundColor',
+ link: 'darkLinkColor',
+};
+
+function buildModeColorDeclarations(mods: ThemeMods, keys: ColorModKeys): string[] {
+ const primary = normalizeHexColor(brandingModValue(mods, keys.primary));
+ const background = normalizeHexColor(brandingModValue(mods, keys.background));
+ const secondaryBg = normalizeHexColor(brandingModValue(mods, keys.secondaryBackground));
+ const link = normalizeHexColor(brandingModValue(mods, keys.link));
+
+ const declarations: string[] = [];
+ if (primary) declarations.push(`--primary-color: ${primary};`);
+ if (background) {
+ declarations.push(`--bg: ${background};`);
+ declarations.push(`--bg-body: ${background};`);
+ }
+ if (secondaryBg) {
+ declarations.push(`--bg-second: ${secondaryBg};`);
+ declarations.push(`--bg-box: ${secondaryBg};`);
+ const rgb = hexToRgbChannels(secondaryBg);
+ if (rgb) declarations.push(`--rgb-bg-second: ${rgb};`);
+ } else if (background) {
+ declarations.push(`--bg-box: ${background};`);
+ }
+ if (link) declarations.push(`--link-color: ${link};`);
+
+ return declarations;
+}
+
+export function appearanceBackgroundColor(mods: ThemeMods, fallback?: string): string | undefined {
+ const v = brandingModValue(mods, LIGHT_COLOR_KEYS.background);
+ return normalizeHexColor(v) ?? (fallback ? normalizeHexColor(fallback) : undefined);
+}
+
+export function appearanceSecondaryBackgroundColor(mods: ThemeMods): string | undefined {
+ return normalizeHexColor(brandingModValue(mods, LIGHT_COLOR_KEYS.secondaryBackground));
+}
+
+export function appearanceLinkColor(mods: ThemeMods): string | undefined {
+ return normalizeHexColor(brandingModValue(mods, LIGHT_COLOR_KEYS.link));
+}
+
+export function appearancePrimaryColorForMode(
+ mods: ThemeMods,
+ dark: boolean,
+ fallback = '#f44336',
+): string {
+ const keys = dark ? DARK_COLOR_KEYS : LIGHT_COLOR_KEYS;
+ return (
+ normalizeHexColor(brandingModValue(mods, keys.primary)) ??
+ normalizeHexColor(brandingModValue(mods, LIGHT_COLOR_KEYS.primary)) ??
+ fallback
+ );
+}
+
+/** Maps theme appearance color mods to standard CSS variables (`--primary-color`, `--bg`, …). */
+export function buildBrandingAppearanceCss(mods: ThemeMods): string {
+ const lightDecl = buildModeColorDeclarations(mods, LIGHT_COLOR_KEYS);
+ const darkDecl = buildModeColorDeclarations(mods, DARK_COLOR_KEYS);
+ const blocks: string[] = [];
+ if (lightDecl.length > 0) {
+ blocks.push(`body:not(.dark) { ${lightDecl.join(' ')} }`);
+ }
+ if (darkDecl.length > 0) {
+ blocks.push(`body.dark { ${darkDecl.join(' ')} }`);
+ }
+ return blocks.join('\n');
+}
+
+/** @deprecated Use `buildBrandingAppearanceCss`. */
+export const buildTwentyTwentyFiveAppearanceCss = buildBrandingAppearanceCss;
diff --git a/toolkit/src/theme/index.ts b/toolkit/src/theme/index.ts
new file mode 100644
index 0000000..a1d6a6d
--- /dev/null
+++ b/toolkit/src/theme/index.ts
@@ -0,0 +1,281 @@
+export type { ThemeApi } from './api/api';
+export {
+ createThemeApi,
+ getThemeApiBaseUrl,
+ resolveThemeApiBaseUrl,
+ themeApi,
+} from './api/api';
+export type { ApiEnvelope } from '../utils/api-envelope';
+export {
+ unpackList,
+ unpackOne,
+ unpackPaginated,
+ unpackPaginatedPair,
+} from '../utils/api-envelope';
+export { isLikelyValidAssetPath, isLikelyValidHeaderLogoPath, resolvePublicAssetUrl, rewriteArticleHtmlAssets } from './content/assets';
+export type { ArchiveExcerptMode, ResolveArchiveExcerptOptions } from './content/excerpt';
+export {
+ resolveArchiveExcerpt,
+ stripHtml,
+ truncateWords,
+} from './content/excerpt';
+export type { AppBootstrapResult, FetchAppBootstrapOptions } from './ssr/bootstrap';
+export {
+ createDefaultAppBootstrap,
+ fetchAppBootstrap,
+ fetchArchives,
+ fetchCmsPage,
+ fetchKnowledgeDetail,
+ fetchKnowledgeList,
+ fetchPublishedPages,
+ fetchRecommendArticles,
+} from './ssr/bootstrap';
+export type { SlimBootstrapOptions } from './ssr/slimBootstrap';
+export {
+ createBootstrapSlimmer,
+ slimAppBootstrapForRoute,
+} from './ssr/slimBootstrap';
+export type {
+ CarouselArticle,
+ ListArticle,
+} from './content/articleSlim';
+export {
+ slimArticleForCarousel,
+ slimArticleForList,
+ slimArticlesForCarousel,
+ slimArticlesForList,
+} from './content/articleSlim';
+export type { ArchiveArticle, ArchiveTree } from './content/archiveSlim';
+export {
+ countArchiveArticles,
+ formatArchiveDay,
+ slimArchiveTree,
+ sortedArchiveYears,
+} from './content/archiveSlim';
+export type { ResolveCarouselArticlesOptions } from './content/carouselArticles';
+export { resolveCarouselArticles } from './content/carouselArticles';
+export type {
+ CategoryArchivePageProps,
+ HomePageProps,
+ KnowledgeIndexPageProps,
+ SearchPageProps,
+ TagArchivePageProps,
+} from './ssr/pageProps';
+export {
+ fetchArchivesPageProps,
+ fetchArticleDetailProps,
+ fetchCategoryArchivePageProps,
+ fetchCmsPageProps,
+ fetchHomePageProps,
+ fetchKnowledgeBookPageProps,
+ fetchKnowledgeChapterPageProps,
+ fetchKnowledgeIndexPageProps,
+ fetchSearchPageProps,
+ fetchTagArchivePageProps,
+} from './ssr/pageProps';
+export type { FetchVisitorContextOptions, ThemeArchiveKind, VisitorContextProps } from './ssr/fetch';
+export {
+ createArchiveGetStaticProps,
+ createDefaultVisitorContext,
+ fetchArticlePageProps,
+ fetchCategoryArchive,
+ fetchCategoryIndex,
+ fetchSearchArticles,
+ fetchSingleArticle,
+ fetchSiteMeta,
+ fetchSiteSettings,
+ fetchTagArchive,
+ fetchTagIndex,
+ fetchThemeCatalog,
+ fetchVisitorContext,
+ resolveStaticVisitorContext,
+ sanitizeNextProps,
+ THEME_ISR_REVALIDATE_SECONDS,
+ themeStaticProps,
+ withApiRetry,
+ withThemeStaticProps,
+} from './ssr/fetch';
+export type { SettingRow } from '../utils/setting';
+export {
+ formatPublishDate,
+ formatPublishDateShort,
+} from '../utils/date';
+export { pickSiteSettings } from '../utils/setting';
+export { safeJsonParse } from '../utils/json';
+export type { ImageVariant } from '../utils/image';
+export { resolveImageUrl } from '../utils/image';
+export type {
+ ThemeSessionUser,
+} from './visitor/authSession';
+export {
+ ADMIN_AUTH_STORAGE_KEY,
+ clearThemeSession,
+ getStoredAccessToken,
+ persistThemeSession,
+ resolveStoredUser,
+ THEME_TOKEN_STORAGE_KEY,
+ THEME_USER_STORAGE_KEY,
+} from './visitor/authSession';
+export type { ThemeColorMode } from './visitor/colorMode';
+export {
+ applyColorModeClass,
+ buildColorModeInitScript,
+ COLOR_MODE_STORAGE_KEY,
+ colorModeInitScript,
+ persistColorMode,
+ resolveClientThemeMode,
+ resolveInitialColorModeState,
+ resolvePreferredColorMode,
+} from './visitor/colorMode';
+export {
+ DEFAULT_VISITOR_LOCALES,
+ LEGACY_LOCALE_STORAGE_KEY,
+ persistVisitorLocale,
+ readBrowserCookie,
+ readRequestCookie,
+ resolveVisitorLocale,
+ VISITOR_LOCALE_COOKIE,
+} from './visitor/visitorLocale';
+export type { CommentAuthor } from './visitor/commentAuthor';
+export {
+ COMMENT_AUTHOR_STORAGE_KEY,
+ COMMENT_EMAIL_REGEXP,
+ getCommentEmailError,
+ isValidCommentAuthor,
+ isValidCommentEmail,
+ persistCommentAuthor,
+ readCommentAuthor,
+} from './visitor/commentAuthor';
+export type { SiteTitleSource } from './content/seo';
+export { getPageTitle, getSiteTitle } from './content/seo';
+export { jsonp } from '../utils/jsonp';
+export type {
+ CreateThemeAxiosClientOptions,
+ ThemeApiEnvelope,
+} from './api/httpClient';
+export {
+ createThemeAxiosClient,
+ encodeAxiosUrlPath,
+ resolveThemeAxiosBaseUrl,
+} from './api/httpClient';
+export type {
+ LocaleCatalog,
+ LocaleMessages,
+ ParseSiteLocaleOptions,
+ SiteLocaleState,
+} from './visitor/locale';
+export {
+ createTranslator,
+ parseSiteLocale,
+ resolveRequestLocale,
+} from './visitor/locale';
+export { mergeVisitorI18n, slimVisitorI18nForSsr } from './visitor/i18n';
+export type { NavItem } from './content/nav';
+export {
+ articlePath,
+ categoryPath,
+ getNavActiveId,
+ tagPath,
+} from './content/nav';
+export type { ResolveThemeRuntimeOptions, ThemeRuntime } from './visitor/runtime';
+export { resolveThemeRuntime } from './visitor/runtime';
+export type { SiteMeta } from './ssr/setting';
+export { DEFAULT_SITE_META, parseSiteMeta, unwrapSetting } from './ssr/setting';
+
+export { createThemeProviders } from './providers';
+export type { ThemeProviders } from './providers';
+export type { CreateThemeHttpStackOptions } from './providers/setup';
+export { createThemeHttpStack } from './providers/setup';
+export { defaultModsFromManifest } from './visitor/appearance';
+export type ThemeManifestRef = { id: string };
+
+export * from './extension';
+
+export { themeNotFound, themeOnDemandPaths, themeStaticNotFound } from './ssr/static';
+export type { ThemeManifestLike, ThemeTemplateSlug } from './ssr/templates';
+export { DEFAULT_TEMPLATE_FILES, resolveTemplateFiles, ThemeTemplate } from './ssr/templates';
+
+/** Headless theme UI — also available from `@fecommunity/reactpress-toolkit/ui`. */
+export type {
+ ArchiveEmptyStateProps,
+ ArchivePageLayoutProps,
+ ArticleCardArticle,
+ ArticleCardLinkProps,
+ ArticleCardProps,
+ ArticleListProps,
+ BaseGlobalStylesProps,
+ LocaleContextValue,
+ LocaleProviderProps,
+ LocaleSwitcherProps,
+ NavMenuProps,
+ NavMenuRenderLinkProps,
+ NotFoundPanelProps,
+ PageHeaderProps,
+ ReactPressProviderProps,
+ SiteBrandingProps,
+ SiteDocumentFallbackProps,
+ SiteDocumentProps,
+ SiteLogoProps,
+ SiteTaglineProps,
+ TaxonomyItem,
+ TaxonomyListProps,
+ TaxonomyListRenderLinkProps,
+ TaxonomyListVariant,
+ ThemeLayoutProps,
+ ThemeRuntimeContextValue,
+ ThemeRuntimeProviderProps,
+ SiteCatalogContextValue,
+ SiteCatalogSiteConfig,
+ SiteConfigNav,
+ SiteSeoProps,
+ SiteAnalyticsProps,
+} from '../ui';
+export {
+ ArchiveEmptyState,
+ ArchivePageLayout,
+ ArticleCard,
+ ArticleList,
+ BaseGlobalStyles,
+ LocaleProvider,
+ LocaleSwitcher,
+ NavMenu,
+ NotFoundPanel,
+ PageHeader,
+ ReactPressProvider,
+ readPersistedLocale,
+ SiteBranding,
+ SiteDocument,
+ SiteDocumentFallback,
+ SiteLogo,
+ SiteTagline,
+ TaxonomyList,
+ ThemeCssVars,
+ ThemeLayout,
+ ThemeRuntimeProvider,
+ useActiveThemeId,
+ useIsThemePreview,
+ useLocale,
+ useAsyncLoading,
+ useForceUpdate,
+ useNavActive,
+ usePagination,
+ useReportArticleView,
+ useReportPageView,
+ useRouteParam,
+ useToggle,
+ useWarningOnExit,
+ useSiteMeta,
+ useThemeId,
+ useThemeMod,
+ useThemeModBool,
+ useThemeRuntime,
+ SiteCatalogContext,
+ SiteCatalogProvider,
+ SiteSeo,
+ SiteAnalytics,
+ useSiteCatalog,
+ useSiteSetting,
+ useSiteUser,
+ useColorMode,
+ useSiteConfig,
+} from '../ui';
diff --git a/toolkit/src/theme/next-config.ts b/toolkit/src/theme/next-config.ts
new file mode 100644
index 0000000..be10341
--- /dev/null
+++ b/toolkit/src/theme/next-config.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import path stable for `require('@fecommunity/reactpress-toolkit/theme/next-config')`. */
+export * from './build/next-config';
diff --git a/toolkit/src/theme/node.ts b/toolkit/src/theme/node.ts
new file mode 100644
index 0000000..d9ae5fd
--- /dev/null
+++ b/toolkit/src/theme/node.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import path stable for `require('@fecommunity/reactpress-toolkit/theme/node')`. */
+export * from './build/node';
diff --git a/toolkit/src/theme/preview/preview-config.ts b/toolkit/src/theme/preview/preview-config.ts
new file mode 100644
index 0000000..487a768
--- /dev/null
+++ b/toolkit/src/theme/preview/preview-config.ts
@@ -0,0 +1,66 @@
+/** Query key for draft theme configuration in live preview (`?reactpress_preview_config=…`). */
+export const PREVIEW_CONFIG_QUERY_KEY = 'reactpress_preview_config';
+
+export function parsePreviewConfigParam(raw: string | null | undefined): Record {
+ if (!raw?.trim()) return {};
+ try {
+ const parsed = JSON.parse(decodeURIComponent(raw.trim())) as unknown;
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
+ return parsed as Record;
+ } catch {
+ return {};
+ }
+}
+
+export function parsePreviewConfigFromRequestUrl(url: string | undefined): Record {
+ if (!url?.trim()) return {};
+ try {
+ const parsed = new URL(url, 'http://reactpress.local');
+ return parsePreviewConfigParam(parsed.searchParams.get(PREVIEW_CONFIG_QUERY_KEY));
+ } catch {
+ return {};
+ }
+}
+
+export function appendPreviewConfigToUrl(
+ baseUrl: string,
+ configuration: Record,
+): string {
+ if (!configuration || Object.keys(configuration).length === 0) return baseUrl;
+
+ try {
+ const url = baseUrl.includes('://')
+ ? new URL(baseUrl)
+ : new URL(baseUrl, 'http://localhost');
+ url.searchParams.set(PREVIEW_CONFIG_QUERY_KEY, JSON.stringify(configuration));
+ return url.toString();
+ } catch {
+ return baseUrl;
+ }
+}
+
+/** Next.js Pages `ctx.query` / `asPath` / `req.url`. */
+export function parsePreviewConfigFromNextCtx(ctx: {
+ query?: Record;
+ asPath?: string;
+ req?: { url?: string };
+}): Record {
+ const raw = ctx.query?.[PREVIEW_CONFIG_QUERY_KEY];
+ if (typeof raw === 'string') return parsePreviewConfigParam(raw);
+ if (Array.isArray(raw) && typeof raw[0] === 'string') return parsePreviewConfigParam(raw[0]);
+
+ const asPath = typeof ctx.asPath === 'string' ? ctx.asPath : '';
+ if (asPath.includes('?')) {
+ return parsePreviewConfigFromRequestUrl(
+ `http://reactpress.local${asPath.startsWith('/') ? asPath : `/${asPath}`}`,
+ );
+ }
+
+ const reqUrl = ctx.req?.url;
+ if (reqUrl && String(reqUrl).includes('?')) {
+ const path = String(reqUrl).startsWith('/') ? reqUrl : `/${reqUrl}`;
+ return parsePreviewConfigFromRequestUrl(`http://reactpress.local${path}`);
+ }
+
+ return {};
+}
diff --git a/toolkit/src/theme/preview/preview-draft.ts b/toolkit/src/theme/preview/preview-draft.ts
new file mode 100644
index 0000000..d01c5d2
--- /dev/null
+++ b/toolkit/src/theme/preview/preview-draft.ts
@@ -0,0 +1,60 @@
+import type { ThemeMods } from '../extension/theme';
+
+/** Short-lived preview payload id (avoids 414 from huge query strings). */
+export const PREVIEW_TOKEN_QUERY_KEY = 'reactpress_preview_token';
+
+export type PreviewDraftPayload = {
+ /** Theme whose `configuration` / mods apply (defaults to active theme when omitted). */
+ themeId?: string;
+ mods?: ThemeMods;
+ configuration?: Record;
+};
+
+export function appendPreviewTokenToUrl(baseUrl: string, token: string): string {
+ if (!token.trim()) return baseUrl;
+ try {
+ const url = baseUrl.includes('://')
+ ? new URL(baseUrl)
+ : new URL(baseUrl, 'http://localhost');
+ url.searchParams.set(PREVIEW_TOKEN_QUERY_KEY, token);
+ return url.toString();
+ } catch {
+ return baseUrl;
+ }
+}
+
+export function parsePreviewTokenFromRequestUrl(url: string | undefined): string {
+ if (!url?.trim()) return '';
+ try {
+ const parsed = new URL(url, 'http://reactpress.local');
+ return parsed.searchParams.get(PREVIEW_TOKEN_QUERY_KEY)?.trim() ?? '';
+ } catch {
+ return '';
+ }
+}
+
+/** Next.js Pages `ctx.query` / `asPath` / `req.url`. */
+export function parsePreviewTokenFromNextCtx(ctx: {
+ query?: Record;
+ asPath?: string;
+ req?: { url?: string };
+}): string {
+ const raw = ctx.query?.[PREVIEW_TOKEN_QUERY_KEY];
+ if (typeof raw === 'string') return raw.trim();
+ if (Array.isArray(raw) && typeof raw[0] === 'string') return raw[0].trim();
+
+ const asPath = typeof ctx.asPath === 'string' ? ctx.asPath : '';
+ if (asPath.includes('?')) {
+ return parsePreviewTokenFromRequestUrl(
+ `http://reactpress.local${asPath.startsWith('/') ? asPath : `/${asPath}`}`,
+ );
+ }
+
+ const reqUrl = ctx.req?.url;
+ if (reqUrl && String(reqUrl).includes('?')) {
+ const path = String(reqUrl).startsWith('/') ? reqUrl : `/${reqUrl}`;
+ return parsePreviewTokenFromRequestUrl(`http://reactpress.local${path}`);
+ }
+
+ return '';
+}
diff --git a/toolkit/src/theme/preview/preview-mods.ts b/toolkit/src/theme/preview/preview-mods.ts
new file mode 100644
index 0000000..5f868e3
--- /dev/null
+++ b/toolkit/src/theme/preview/preview-mods.ts
@@ -0,0 +1,74 @@
+import type { ThemeMods } from '../extension/theme';
+
+/** Query key for draft customizer values in live preview iframe (`?reactpress_preview_mods=…`). */
+export const PREVIEW_MODS_QUERY_KEY = 'reactpress_preview_mods';
+
+export function parsePreviewModsParam(raw: string | null | undefined): ThemeMods {
+ if (!raw?.trim()) return {};
+ try {
+ const parsed = JSON.parse(decodeURIComponent(raw.trim())) as unknown;
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return {};
+ const out: ThemeMods = {};
+ for (const [key, value] of Object.entries(parsed as Record)) {
+ if (value == null || value === '') continue;
+ out[key] = String(value);
+ }
+ return out;
+ } catch {
+ return {};
+ }
+}
+
+/** Read draft mods from a request URL or path+query string. */
+export function parsePreviewModsFromRequestUrl(url: string | undefined): ThemeMods {
+ if (!url?.trim()) return {};
+ try {
+ const parsed = new URL(url, 'http://reactpress.local');
+ return parsePreviewModsParam(parsed.searchParams.get(PREVIEW_MODS_QUERY_KEY));
+ } catch {
+ return {};
+ }
+}
+
+export function appendPreviewModsToUrl(baseUrl: string, mods: ThemeMods): string {
+ if (Object.keys(mods).length === 0) return baseUrl;
+
+ try {
+ const url = baseUrl.includes('://')
+ ? new URL(baseUrl)
+ : new URL(baseUrl, 'http://localhost');
+ url.searchParams.set(PREVIEW_MODS_QUERY_KEY, JSON.stringify(mods));
+ return url.toString();
+ } catch {
+ return baseUrl;
+ }
+}
+
+export function mergePreviewMods(base: ThemeMods, override: ThemeMods): ThemeMods {
+ if (!Object.keys(override).length) return base;
+ return { ...base, ...override };
+}
+
+/** Next.js Pages `ctx.query` / `asPath` / `req.url` — used in `createThemeApp.getInitialProps`. */
+export function parsePreviewModsFromNextCtx(ctx: {
+ query?: Record;
+ asPath?: string;
+ req?: { url?: string };
+}): ThemeMods {
+ const raw = ctx.query?.[PREVIEW_MODS_QUERY_KEY];
+ if (typeof raw === 'string') return parsePreviewModsParam(raw);
+ if (Array.isArray(raw) && typeof raw[0] === 'string') return parsePreviewModsParam(raw[0]);
+
+ const asPath = typeof ctx.asPath === 'string' ? ctx.asPath : '';
+ if (asPath.includes('?')) {
+ return parsePreviewModsFromRequestUrl(`http://reactpress.local${asPath.startsWith('/') ? asPath : `/${asPath}`}`);
+ }
+
+ const reqUrl = ctx.req?.url;
+ if (reqUrl && String(reqUrl).includes('?')) {
+ const path = String(reqUrl).startsWith('/') ? reqUrl : `/${reqUrl}`;
+ return parsePreviewModsFromRequestUrl(`http://reactpress.local${path}`);
+ }
+
+ return {};
+}
diff --git a/toolkit/src/theme/providers/index.ts b/toolkit/src/theme/providers/index.ts
new file mode 100644
index 0000000..0482be8
--- /dev/null
+++ b/toolkit/src/theme/providers/index.ts
@@ -0,0 +1,345 @@
+import type { AxiosInstance } from 'axios';
+
+import type {
+ IArticle,
+ ICategory,
+ IComment,
+ IFile,
+ IKnowledge,
+ IPage,
+ ISetting,
+ ITag,
+ IUser,
+ IView,
+} from '../../types';
+
+/** Create REST provider classes bound to a theme HTTP client (axios + envelope unwrap). */
+export function createThemeProviders(http: AxiosInstance) {
+ class SettingProvider {
+ static async getSetting(): Promise {
+ return http.post('/setting/get');
+ }
+
+ static async updateSetting(data: Partial): Promise {
+ return http.post('/setting', data);
+ }
+ }
+
+ class ArticleProvider {
+ static async getArticles(params: Record): Promise<[IArticle[], number]> {
+ return http.get('/article', { params });
+ }
+
+ static async getAllRecommendArticles(): Promise {
+ return http.get('/article/all/recommend');
+ }
+
+ static async getArticlesByCategory(
+ category: string,
+ params: Record,
+ ): Promise<[IArticle[], number]> {
+ return http.get(`/article/category/${category}`, { params });
+ }
+
+ static async getArticlesByTag(
+ tag: string,
+ params: Record,
+ ): Promise<[IArticle[], number]> {
+ return http.get(`/article/tag/${tag}`, { params });
+ }
+
+ static async getRecommend(articleId: string | null = null, pageSize = 6): Promise {
+ return http.get('/article/recommend', { params: { articleId, pageSize } });
+ }
+
+ static async getArchives(): Promise>> {
+ return http.get('/article/archives');
+ }
+
+ static async getArticle(id: string): Promise {
+ return http.get(`/article/${id}`);
+ }
+
+ static async addArticle(data: Partial): Promise {
+ return http.post('/article', data);
+ }
+
+ static async updateArticle(id: string, data: Partial): Promise {
+ return http.patch(`/article/${id}`, data);
+ }
+
+ static async updateArticleViews(id: string): Promise {
+ return http.post(`/article/${id}/views`);
+ }
+
+ static async updateArticleLikes(id: string, type: string): Promise {
+ return http.post(`/article/${id}/likes`, { type });
+ }
+
+ static async checkPassword(
+ id: string,
+ password: string,
+ ): Promise<{ pass: boolean } & IArticle> {
+ return http.post(`/article/${id}/checkPassword`, { password });
+ }
+
+ static async deleteArticle(id: string): Promise {
+ return http.delete(`/article/${id}`);
+ }
+ }
+
+ class CategoryProvider {
+ static async getCategory(params?: Record): Promise {
+ return http.get('/category', { params });
+ }
+
+ static async add(data: Partial): Promise {
+ return http.post('/category', data);
+ }
+
+ static async getCategoryById(id: string): Promise {
+ return http.get(`/category/${id}`);
+ }
+
+ static async update(id: string, data: Partial): Promise {
+ return http.patch(`/category/${id}`, data);
+ }
+
+ static async delete(id: string): Promise {
+ return http.delete(`/category/${id}`);
+ }
+ }
+
+ class TagProvider {
+ static async getTags(params?: Record): Promise {
+ return http.get('/tag', { params });
+ }
+
+ static async getTagWithArticles(id: string, needFilter = false): Promise {
+ return http.get(
+ `/tag/${id}/article`,
+ needFilter ? { params: { status: 'publish' } } : {},
+ );
+ }
+
+ static async addTag(data: Partial): Promise {
+ return http.post('/tag', data);
+ }
+
+ static async getTagById(id: string): Promise {
+ return http.get(`/tag/${id}`);
+ }
+
+ static async updateTag(id: string, data: Partial): Promise {
+ return http.patch(`/tag/${id}`, data);
+ }
+
+ static async deleteTag(id: string): Promise {
+ return http.delete(`/tag/${id}`);
+ }
+ }
+
+ class PageProvider {
+ static async getPages(params: Record): Promise<[IPage[], number]> {
+ return http.get('/page', { params });
+ }
+
+ static async getAllPublisedPages(): Promise<[IPage[], number]> {
+ return http.get('/page', { params: { status: 'publish' } }).then((res) => {
+ const [pages, total] = res as unknown as [IPage[], number];
+ return [pages.sort((a, b) => -a.order + b.order), total];
+ });
+ }
+
+ static async getPage(id: string): Promise {
+ return http.get(`/page/${id}`);
+ }
+
+ static async addPage(data: Partial): Promise {
+ return http.post('/page', data);
+ }
+
+ static async updatePage(id: string, data: Partial): Promise {
+ return http.patch(`/page/${id}`, data);
+ }
+
+ static async updatePageViews(id: string): Promise {
+ return http.post(`/page/${id}/views`);
+ }
+
+ static async deletePage(id: string): Promise {
+ return http.delete(`/page/${id}`);
+ }
+ }
+
+ class CommentProvider {
+ static async getComments(params: Record): Promise<[IComment[], number]> {
+ return http.get('/comment', { params });
+ }
+
+ static async getComment(id: string): Promise {
+ return http.get(`/comment/${id}`);
+ }
+
+ static async getArticleComments(
+ hostId: string,
+ params: Record,
+ ): Promise<[IComment[], number]> {
+ return http.get(`/comment/host/${hostId}`, { params });
+ }
+
+ static async addComment(data: Partial): Promise {
+ return http.post('/comment', data);
+ }
+
+ static async updateComment(id: string, data: Partial): Promise {
+ return http.patch(`/comment/${id}`, data);
+ }
+
+ static async deleteComment(id: string): Promise {
+ return http.delete(`/comment/${id}`);
+ }
+ }
+
+ class UserProvider {
+ static async login(data: Record): Promise {
+ return http.post('/auth/login', data);
+ }
+
+ static async loginWithGithub(code: string): Promise {
+ return http.post('/auth/github', { code });
+ }
+
+ static async register(data: Record): Promise {
+ return http.post('/user/register', data);
+ }
+
+ static getUsers(params: Record): Promise<[IUser[], number]> {
+ return http.get('/user', { params });
+ }
+
+ static async update(data: Record): Promise {
+ return http.post('/user/update', data);
+ }
+
+ static async updatePassword(data: Record): Promise {
+ return http.post('/user/password', data);
+ }
+ }
+
+ class KnowledgeProvider {
+ static createBook(data: Partial): Promise {
+ return http.post('/knowledge/book', data);
+ }
+
+ static createChapters(data: Partial[]): Promise {
+ return http.post('/knowledge/chapter', data);
+ }
+
+ static async deleteKnowledge(id: string): Promise {
+ return http.delete(`/knowledge/${id}`);
+ }
+
+ static async updateKnowledge(id: string, data: Partial): Promise {
+ return http.patch(`/knowledge/${id}`, data);
+ }
+
+ static async getKnowledges(params: Record = {}): Promise<[IKnowledge[], number]> {
+ return http.get('/knowledge', { params });
+ }
+
+ static async getKnowledge(id: string): Promise {
+ return http.get(`/knowledge/${id}`);
+ }
+
+ static async updateKnowledgeViews(id: string): Promise {
+ return http.post(`/knowledge/${id}/views`);
+ }
+
+ static async updateKnowledgeLikes(id: string, type: string): Promise {
+ return http.post(`/knowledge/${id}/likes`, { type });
+ }
+ }
+
+ class SearchProvider {
+ static async searchArticles(keyword: string): Promise {
+ return http.get('/search/article', { params: { keyword } });
+ }
+ }
+
+ class FileProvider {
+ static async uploadFile(file: FormData, unique = 0): Promise {
+ return http.post('/file/upload', file, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ params: { unique },
+ });
+ }
+
+ static async getFiles(params: Record): Promise<[IFile[], number]> {
+ return http.get('/file', { params });
+ }
+
+ static async deleteFile(id: string): Promise {
+ return http.delete(`/file/${id}`);
+ }
+ }
+
+ class ViewProvider {
+ static async getViews(params: Record): Promise<[IView[], number]> {
+ return http.get('/view', { params });
+ }
+
+ static async addView(data: Record): Promise {
+ return http.post('/view', data);
+ }
+
+ static async getViewsByUrl(url: string): Promise {
+ return http.get('/view/url', { params: { url } });
+ }
+
+ static async deleteView(id: string): Promise {
+ return http.delete(`/view/${id}`);
+ }
+ }
+
+ class MailProvider {
+ static async getMails(params: Record): Promise<[Record[], number]> {
+ return http.get('/mail', { params });
+ }
+
+ static async deleteMail(id: string): Promise> {
+ return http.delete(`/mail/${id}`);
+ }
+ }
+
+ class SmtpProvider {
+ static async testSendMail(user: Record): Promise {
+ return http.post('/smtp/test', user);
+ }
+ }
+
+ class PosterProvider {
+ static async createPoster(data: Record): Promise<{ name: string; url: string }> {
+ return http.post('/poster', data);
+ }
+ }
+
+ return {
+ SettingProvider,
+ ArticleProvider,
+ CategoryProvider,
+ TagProvider,
+ PageProvider,
+ CommentProvider,
+ UserProvider,
+ KnowledgeProvider,
+ SearchProvider,
+ FileProvider,
+ ViewProvider,
+ MailProvider,
+ SmtpProvider,
+ PosterProvider,
+ };
+}
+
+export type ThemeProviders = ReturnType;
diff --git a/toolkit/src/theme/providers/setup.ts b/toolkit/src/theme/providers/setup.ts
new file mode 100644
index 0000000..f929aaa
--- /dev/null
+++ b/toolkit/src/theme/providers/setup.ts
@@ -0,0 +1,37 @@
+import { clearThemeSession } from '../visitor/authSession';
+import type { CreateThemeAxiosClientOptions } from '../api/httpClient';
+import { createThemeAxiosClient } from '../api/httpClient';
+import { createThemeProviders } from './index';
+
+export type CreateThemeHttpStackOptions = CreateThemeAxiosClientOptions & {
+ onError?: (message: string) => void;
+ onUnauthorized?: () => void;
+};
+
+/**
+ * One-call HTTP client + REST providers for theme pages.
+ * Mirrors the stack used inside `createReactPressApp`, with optional toast hooks.
+ */
+export function createThemeHttpStack(options: CreateThemeHttpStackOptions = {}) {
+ const { onError, onUnauthorized, ...httpOptions } = options;
+
+ const httpProvider = createThemeAxiosClient({
+ ...httpOptions,
+ onError,
+ onUnauthorized:
+ onUnauthorized ??
+ (() => {
+ clearThemeSession();
+ if (typeof window !== 'undefined') {
+ window.location.reload();
+ }
+ }),
+ });
+
+ const providers = createThemeProviders(httpProvider);
+
+ return {
+ httpProvider,
+ ...providers,
+ };
+}
diff --git a/toolkit/src/theme/server.ts b/toolkit/src/theme/server.ts
new file mode 100644
index 0000000..12552d5
--- /dev/null
+++ b/toolkit/src/theme/server.ts
@@ -0,0 +1,201 @@
+/**
+ * SSR-safe theme exports for App Router / server components.
+ * Does not re-export `@fecommunity/reactpress-toolkit/ui` (React context / hooks).
+ * Client components should import UI from `@fecommunity/reactpress-toolkit/ui` directly.
+ */
+export type { ThemeApi } from './api/api';
+export {
+ createThemeApi,
+ getThemeApiBaseUrl,
+ resolveThemeApiBaseUrl,
+ themeApi,
+} from './api/api';
+export type { ApiEnvelope } from '../utils/api-envelope';
+export {
+ unpackList,
+ unpackOne,
+ unpackPaginated,
+ unpackPaginatedPair,
+} from '../utils/api-envelope';
+export { isLikelyValidAssetPath, isLikelyValidHeaderLogoPath, resolvePublicAssetUrl, rewriteArticleHtmlAssets } from './content/assets';
+export type { ArchiveExcerptMode, ResolveArchiveExcerptOptions } from './content/excerpt';
+export {
+ resolveArchiveExcerpt,
+ stripHtml,
+ truncateWords,
+} from './content/excerpt';
+export type { AppBootstrapResult, FetchAppBootstrapOptions } from './ssr/bootstrap';
+export {
+ createDefaultAppBootstrap,
+ fetchAppBootstrap,
+ fetchArchives,
+ fetchCmsPage,
+ fetchKnowledgeDetail,
+ fetchKnowledgeList,
+ fetchPublishedPages,
+ fetchRecommendArticles,
+} from './ssr/bootstrap';
+export type { SlimBootstrapOptions } from './ssr/slimBootstrap';
+export {
+ createBootstrapSlimmer,
+ slimAppBootstrapForRoute,
+} from './ssr/slimBootstrap';
+export type {
+ CarouselArticle,
+ ListArticle,
+} from './content/articleSlim';
+export {
+ slimArticleForCarousel,
+ slimArticleForList,
+ slimArticlesForCarousel,
+ slimArticlesForList,
+} from './content/articleSlim';
+export type { ArchiveArticle, ArchiveTree } from './content/archiveSlim';
+export {
+ countArchiveArticles,
+ formatArchiveDay,
+ slimArchiveTree,
+ sortedArchiveYears,
+} from './content/archiveSlim';
+export type { ResolveCarouselArticlesOptions } from './content/carouselArticles';
+export { resolveCarouselArticles } from './content/carouselArticles';
+export type {
+ CategoryArchivePageProps,
+ HomePageProps,
+ KnowledgeIndexPageProps,
+ SearchPageProps,
+ TagArchivePageProps,
+} from './ssr/pageProps';
+export {
+ fetchArchivesPageProps,
+ fetchArticleDetailProps,
+ fetchCategoryArchivePageProps,
+ fetchCmsPageProps,
+ fetchHomePageProps,
+ fetchKnowledgeBookPageProps,
+ fetchKnowledgeChapterPageProps,
+ fetchKnowledgeIndexPageProps,
+ fetchSearchPageProps,
+ fetchTagArchivePageProps,
+} from './ssr/pageProps';
+export type { FetchVisitorContextOptions, ThemeArchiveKind, VisitorContextProps } from './ssr/fetch';
+export {
+ createArchiveGetStaticProps,
+ createDefaultVisitorContext,
+ fetchArticlePageProps,
+ fetchCategoryArchive,
+ fetchCategoryIndex,
+ fetchSearchArticles,
+ fetchSingleArticle,
+ fetchSiteMeta,
+ fetchSiteSettings,
+ fetchTagArchive,
+ fetchTagIndex,
+ fetchThemeCatalog,
+ fetchVisitorContext,
+ resolveStaticVisitorContext,
+ sanitizeNextProps,
+ THEME_ISR_REVALIDATE_SECONDS,
+ themeStaticProps,
+ withApiRetry,
+ withThemeStaticProps,
+} from './ssr/fetch';
+export type { SettingRow } from '../utils/setting';
+export {
+ formatPublishDate,
+ formatPublishDateShort,
+} from '../utils/date';
+export { pickSiteSettings } from '../utils/setting';
+export { safeJsonParse } from '../utils/json';
+export type { ImageVariant } from '../utils/image';
+export { resolveImageUrl } from '../utils/image';
+export type {
+ ThemeSessionUser,
+} from './visitor/authSession';
+export {
+ ADMIN_AUTH_STORAGE_KEY,
+ clearThemeSession,
+ getStoredAccessToken,
+ persistThemeSession,
+ resolveStoredUser,
+ THEME_TOKEN_STORAGE_KEY,
+ THEME_USER_STORAGE_KEY,
+} from './visitor/authSession';
+export type { ThemeColorMode } from './visitor/colorMode';
+export {
+ applyColorModeClass,
+ buildColorModeInitScript,
+ COLOR_MODE_STORAGE_KEY,
+ colorModeInitScript,
+ persistColorMode,
+ resolveClientThemeMode,
+ resolveInitialColorModeState,
+ resolvePreferredColorMode,
+} from './visitor/colorMode';
+export {
+ DEFAULT_VISITOR_LOCALES,
+ LEGACY_LOCALE_STORAGE_KEY,
+ persistVisitorLocale,
+ readBrowserCookie,
+ readRequestCookie,
+ resolveVisitorLocale,
+ VISITOR_LOCALE_COOKIE,
+} from './visitor/visitorLocale';
+export type { CommentAuthor } from './visitor/commentAuthor';
+export {
+ COMMENT_AUTHOR_STORAGE_KEY,
+ COMMENT_EMAIL_REGEXP,
+ getCommentEmailError,
+ isValidCommentAuthor,
+ isValidCommentEmail,
+ persistCommentAuthor,
+ readCommentAuthor,
+} from './visitor/commentAuthor';
+export type { SiteTitleSource } from './content/seo';
+export { getPageTitle, getSiteTitle } from './content/seo';
+export { jsonp } from '../utils/jsonp';
+export type {
+ CreateThemeAxiosClientOptions,
+ ThemeApiEnvelope,
+} from './api/httpClient';
+export {
+ createThemeAxiosClient,
+ encodeAxiosUrlPath,
+ resolveThemeAxiosBaseUrl,
+} from './api/httpClient';
+export type {
+ LocaleCatalog,
+ LocaleMessages,
+ ParseSiteLocaleOptions,
+ SiteLocaleState,
+} from './visitor/locale';
+export {
+ createTranslator,
+ parseSiteLocale,
+ resolveRequestLocale,
+} from './visitor/locale';
+export { mergeVisitorI18n, slimVisitorI18nForSsr } from './visitor/i18n';
+export type { NavItem } from './content/nav';
+export {
+ articlePath,
+ categoryPath,
+ getNavActiveId,
+ tagPath,
+} from './content/nav';
+export type { ResolveThemeRuntimeOptions, ThemeRuntime } from './visitor/runtime';
+export { resolveThemeRuntime } from './visitor/runtime';
+export type { SiteMeta } from './ssr/setting';
+export { DEFAULT_SITE_META, parseSiteMeta, unwrapSetting } from './ssr/setting';
+
+export { createThemeProviders } from './providers';
+export type { ThemeProviders } from './providers';
+export type { CreateThemeHttpStackOptions } from './providers/setup';
+export { createThemeHttpStack } from './providers/setup';
+export { defaultModsFromManifest } from './visitor/appearance';
+export type ThemeManifestRef = { id: string };
+
+export * from './extension';
+
+export { themeNotFound, themeOnDemandPaths, themeStaticNotFound } from './ssr/static';
+export type { ThemeManifestLike, ThemeTemplateSlug } from './ssr/templates';
+export { DEFAULT_TEMPLATE_FILES, resolveTemplateFiles, ThemeTemplate } from './ssr/templates';
diff --git a/toolkit/src/theme/ssr/bootstrap.ts b/toolkit/src/theme/ssr/bootstrap.ts
new file mode 100644
index 0000000..670677e
--- /dev/null
+++ b/toolkit/src/theme/ssr/bootstrap.ts
@@ -0,0 +1,166 @@
+import type { IncomingMessage } from 'http';
+
+import type { ThemeApi } from '../api/api';
+import { themeApi } from '../api/api';
+import { unpackList, unpackPaginated } from '../api/api-data';
+import { safeJsonParse } from '../api/json';
+import type { ThemeConfigurationSchema } from '../extension/configuration/types';
+import type { NextPreviewCtx } from '../extension/preview';
+import {
+ normalizePreviewDraftData,
+ previewDraftApiPath,
+ resolveThemePreviewContext,
+} from '../extension/preview';
+import type { ThemeMods } from '../extension/theme';
+import { createThemeAxiosClient } from '../api/httpClient';
+import { createThemeProviders } from '../providers';
+import { resolveVisitorLocale } from '../visitor/visitorLocale';
+import { mergeVisitorI18n } from '../visitor/i18n';
+import { DEFAULT_VISITOR_LOCALES } from '../visitor/visitorLocale';
+import { withApiRetry } from './fetch';
+import { unwrapSetting } from './setting';
+
+export interface AppBootstrapResult {
+ setting: Record;
+ tags: unknown[];
+ categories: unknown[];
+ pages: unknown[];
+ i18n: Record;
+ globalSetting: Record | undefined;
+ siteConfig: Record;
+ locales: string[];
+ initialLocale: string;
+ colorPrimary: string;
+ themeMods: ThemeMods;
+}
+
+export interface FetchAppBootstrapOptions {
+ api?: ThemeApi;
+ manifest?: { id: string; options?: ThemeConfigurationSchema };
+ ctx?: NextPreviewCtx;
+ /** Explicit request (e.g. App Router `cookies()` / `headers()` shim) for locale resolution. */
+ req?: IncomingMessage;
+}
+
+/** Safe defaults when the API is unreachable during dev startup or SSR. */
+export function createDefaultAppBootstrap(
+ partial: Partial = {},
+): AppBootstrapResult {
+ const i18n = mergeVisitorI18n({});
+ const locales = partial.locales ?? [...DEFAULT_VISITOR_LOCALES];
+ return {
+ setting: {},
+ tags: [],
+ categories: [],
+ pages: [],
+ i18n,
+ globalSetting: undefined,
+ siteConfig: {},
+ locales,
+ initialLocale: partial.initialLocale ?? locales[0] ?? 'zh',
+ colorPrimary: '#f44336',
+ themeMods: {},
+ ...partial,
+ };
+}
+
+/**
+ * SSR bootstrap for full-featured themes — setting, taxonomy, pages, preview mods.
+ * Call once from `_app.getInitialProps` or `createReactPressApp`.
+ */
+export async function fetchAppBootstrap(
+ options: FetchAppBootstrapOptions = {},
+): Promise {
+ const api = options.api ?? themeApi;
+ const http = createThemeAxiosClient({ unwrapEnvelope: true });
+ const { SettingProvider, TagProvider, CategoryProvider, PageProvider } =
+ createThemeProviders(http);
+
+ const [setting, tags, categories, pagesResult] = await withApiRetry(() =>
+ Promise.all([
+ SettingProvider.getSetting(),
+ TagProvider.getTags({ articleStatus: 'publish' }),
+ CategoryProvider.getCategory({ articleStatus: 'publish' }),
+ PageProvider.getAllPublisedPages(),
+ ]),
+ );
+
+ const i18n = mergeVisitorI18n(safeJsonParse>(setting.i18n, {}));
+ const globalSettingRaw = safeJsonParse>(setting.globalSetting, {});
+ const localeKeys = Object.keys(i18n);
+ const locale = resolveVisitorLocale(
+ localeKeys,
+ (options.req ?? options.ctx?.req) as IncomingMessage | undefined,
+ );
+ const globalSetting = (globalSettingRaw?.[locale] ?? undefined) as
+ | Record
+ | undefined;
+
+ const preview = await resolveThemePreviewContext({
+ globalSettingRaw,
+ setting: setting as unknown as Record,
+ locale,
+ ctx: options.ctx,
+ manifest: options.manifest,
+ fetchDraft: async (token) =>
+ normalizePreviewDraftData(await http.get(previewDraftApiPath(token))),
+ });
+
+ return {
+ setting: preview.setting,
+ tags,
+ categories,
+ pages: pagesResult[0] || [],
+ i18n,
+ globalSetting,
+ siteConfig: preview.siteConfig as unknown as Record,
+ locales: localeKeys,
+ initialLocale: locale,
+ colorPrimary: preview.colorPrimary,
+ themeMods: preview.effectiveMods,
+ };
+}
+
+/** Fetch helpers for pages not covered by `fetchThemeCatalog`. */
+export async function fetchArchives(api: ThemeApi = themeApi) {
+ return api.article.getArchives();
+}
+
+export async function fetchKnowledgeList(
+ api: ThemeApi = themeApi,
+ params: Record = {},
+) {
+ return unpackPaginated(await api.knowledge.findAll({ query: params } as never));
+}
+
+export async function fetchPublishedPages(api: ThemeApi = themeApi) {
+ const response = await api.page.findAll({ query: { status: 'publish' } } as never);
+ const [pages, total] = unpackPaginated(response) as [unknown[], number];
+ return {
+ pages: [...pages].sort(
+ (a, b) => -((a as { order?: number }).order ?? 0) + ((b as { order?: number }).order ?? 0),
+ ),
+ total,
+ };
+}
+
+export async function fetchCmsPage(api: ThemeApi = themeApi, id: string) {
+ const row = unwrapSetting(await api.setting.findAll());
+ const page = await api.page.findById(id);
+ return {
+ setting: row,
+ page: page as unknown,
+ };
+}
+
+export async function fetchKnowledgeDetail(api: ThemeApi = themeApi, id: string) {
+ return api.knowledge.findById(id);
+}
+
+export async function fetchRecommendArticles(api: ThemeApi = themeApi, articleId?: string) {
+ return unpackList(
+ await api.article.getRecommendArticles({
+ query: articleId ? { articleId } : {},
+ } as never),
+ );
+}
diff --git a/toolkit/src/theme/ssr/fetch.ts b/toolkit/src/theme/ssr/fetch.ts
new file mode 100644
index 0000000..d2e1aa6
--- /dev/null
+++ b/toolkit/src/theme/ssr/fetch.ts
@@ -0,0 +1,357 @@
+import type { ThemeApi } from '../api/api';
+import { themeApi } from '../api/api';
+import { unpackList, unpackOne, unpackPaginated } from '../api/api-data';
+import { resolvePublicAssetUrl, rewriteArticleHtmlAssets } from '../content/assets';
+import type { SettingRow } from './format';
+import { pickSiteSettings } from './format';
+import { safeJsonParse } from '../api/json';
+import type { ParseSiteLocaleOptions } from '../visitor/locale';
+import { parseSiteLocale } from '../visitor/locale';
+import type { ThemeRuntime } from '../visitor/runtime';
+import { resolveThemeRuntime } from '../visitor/runtime';
+import type { SiteMeta } from './setting';
+import { DEFAULT_SITE_META, parseSiteMeta, unwrapSetting } from './setting';
+import { themeNotFound } from './static';
+
+/** Default ISR interval for theme list/home pages. */
+export const THEME_ISR_REVALIDATE_SECONDS = 60;
+
+function isConnectionRefused(error: unknown): boolean {
+ const err = error as { code?: string; cause?: { code?: string } };
+ return err?.code === 'ECONNREFUSED' || err?.cause?.code === 'ECONNREFUSED';
+}
+
+function isRetryableNetworkError(error: unknown): boolean {
+ if (isConnectionRefused(error)) return true;
+ const err = error as {
+ code?: string;
+ message?: string;
+ cause?: { code?: string; message?: string };
+ };
+ const code = err?.code || err?.cause?.code;
+ const message = `${err?.message || ''} ${err?.cause?.message || ''}`.toLowerCase();
+ return (
+ code === 'ECONNRESET' ||
+ code === 'ETIMEDOUT' ||
+ code === 'EPIPE' ||
+ message.includes('socket hang up') ||
+ message.includes('network error') ||
+ message.includes('timeout')
+ );
+}
+
+/** Retry SSR API calls during dev startup or flaky remote connections. */
+export async function withApiRetry(fn: () => Promise, attempts = 8, delayMs = 400): Promise {
+ let lastError: unknown;
+ for (let i = 0; i < attempts; i += 1) {
+ try {
+ return await fn();
+ } catch (error) {
+ lastError = error;
+ if (!isRetryableNetworkError(error) || i >= attempts - 1) {
+ throw error;
+ }
+ await new Promise((resolve) => {
+ setTimeout(resolve, delayMs);
+ });
+ }
+ }
+ throw lastError;
+}
+
+/** Articles, categories, and tags for home / toolkit demo pages. */
+export async function fetchThemeCatalog(api: ThemeApi) {
+ return withApiRetry(async () => {
+ const [articlesResponse, categoriesResponse, tagsResponse] = await Promise.all([
+ api.article.findAll(),
+ api.category.findAll(),
+ api.tag.findAll(),
+ ]);
+
+ return {
+ articles: unpackPaginated(articlesResponse),
+ categories: unpackList(categoriesResponse),
+ tags: unpackList(tagsResponse),
+ };
+ });
+}
+
+/** Strip `undefined` values so Next.js can serialize `getStaticProps` results. */
+export function sanitizeNextProps(props: T): T {
+ return JSON.parse(JSON.stringify(props)) as T;
+}
+
+export function themeStaticProps>(
+ props: T,
+ revalidate: number = THEME_ISR_REVALIDATE_SECONDS,
+) {
+ return { props: sanitizeNextProps(props), revalidate };
+}
+
+let staticVisitorContextPromise: Promise | null = null;
+let staticVisitorContextKey = '';
+
+/** Visitor context for `getStaticProps` — `_app.getInitialProps` does not run on SSR for static pages. */
+export async function resolveStaticVisitorContext(): Promise {
+ const themeId = process.env.REACTPRESS_THEME_ID?.trim() || 'starter-theme';
+ const honorPreview = process.env.REACTPRESS_HONOR_PREVIEW === '1';
+ const cacheKey = `${themeId}:${honorPreview ? '1' : '0'}`;
+
+ if (staticVisitorContextPromise && staticVisitorContextKey === cacheKey) {
+ return staticVisitorContextPromise;
+ }
+
+ staticVisitorContextKey = cacheKey;
+ staticVisitorContextPromise = (async () => {
+ try {
+ return await fetchVisitorContext(themeApi, { themeId, honorPreview });
+ } catch (error) {
+ const code = (error as { code?: string; cause?: { code?: string } })?.code
+ ?? (error as { cause?: { code?: string } })?.cause?.code;
+ if (code !== 'ECONNREFUSED') {
+ console.error('[reactpress] resolveStaticVisitorContext failed', error);
+ }
+ return createDefaultVisitorContext(themeId);
+ }
+ })();
+
+ return staticVisitorContextPromise;
+}
+
+/** All categories — `getStaticProps` for `pages/category/index.tsx`. */
+export async function fetchCategoryIndex(api: ThemeApi) {
+ return { categories: unpackList(await api.category.findAll()) };
+}
+
+/** All tags — `getStaticProps` for `pages/tag/index.tsx`. */
+export async function fetchTagIndex(api: ThemeApi) {
+ return { tags: unpackList(await api.tag.findAll()) };
+}
+
+/** Site settings for about/footer pages (`getStaticProps`). */
+export async function fetchSiteSettings<
+ T extends Record,
+>(api: ThemeApi, schema: T): Promise<{ [K in keyof T]: string }> {
+ const rows = unpackList(await api.setting.findAll()) as SettingRow[];
+ return pickSiteSettings(rows, schema);
+}
+
+export interface FetchVisitorContextOptions extends ParseSiteLocaleOptions {
+ themeId: string;
+ honorPreview?: boolean;
+}
+
+/** Props for `ReactPressProvider` — fetch once in `_app.getInitialProps` or `getStaticProps`. */
+export interface VisitorContextProps {
+ locale: string;
+ locales: string[];
+ messages: Record;
+ catalog: Record>;
+ themeId: string;
+ activeThemeId: string;
+ mods: ThemeRuntime['mods'];
+ isPreview: boolean;
+ /** Site settings (`systemTitle`, `systemSubTitle`, …) for branding fallbacks. */
+ siteMeta: SiteMeta;
+}
+
+/** Safe defaults when API is unreachable (still wraps pages in Provider). */
+export function createDefaultVisitorContext(
+ themeId: string,
+ partial: Partial = {},
+): VisitorContextProps {
+ return {
+ locale: 'zh',
+ locales: ['zh', 'en'],
+ messages: {},
+ catalog: {},
+ themeId,
+ activeThemeId: themeId,
+ mods: {},
+ isPreview: false,
+ siteMeta: DEFAULT_SITE_META,
+ ...partial,
+ };
+}
+
+export async function fetchVisitorContext(
+ api: ThemeApi,
+ options: FetchVisitorContextOptions,
+): Promise {
+ if (!api?.setting?.findAll) {
+ throw new Error('fetchVisitorContext: invalid ThemeApi instance');
+ }
+ return withApiRetry(async () => {
+ const row = unwrapSetting(await api.setting.findAll());
+ const localeState = parseSiteLocale(row?.i18n, {
+ locale: options.locale,
+ fallbackLocale: options.fallbackLocale,
+ preferredLocale: options.preferredLocale,
+ acceptLanguage: options.acceptLanguage,
+ });
+ const globalSetting = safeJsonParse>(row?.globalSetting, {});
+ const runtime = resolveThemeRuntime(globalSetting, {
+ themeId: options.themeId,
+ honorPreview: options.honorPreview,
+ });
+ const siteMeta = parseSiteMeta(row);
+
+ return {
+ locale: localeState.locale,
+ locales: localeState.locales,
+ messages: localeState.messages,
+ catalog: localeState.catalog,
+ themeId: runtime.themeId,
+ activeThemeId: runtime.activeThemeId,
+ mods: runtime.mods,
+ isPreview: runtime.isPreview,
+ siteMeta,
+ };
+ });
+}
+
+/** Common site meta from the Setting row (`systemTitle`, `systemSubTitle`, …). */
+export async function fetchSiteMeta(api: ThemeApi) {
+ return withApiRetry(async () => {
+ const row = unwrapSetting(await api.setting.findAll());
+ return parseSiteMeta(row);
+ });
+}
+
+/** Category archive — `getStaticProps` for `pages/category/[category].tsx`. */
+export async function fetchCategoryArchive(api: ThemeApi, category: string) {
+ const [articlesResponse, categoriesResponse] = await Promise.all([
+ api.article.findArticlesByCategory(category),
+ api.category.findAll(),
+ ]);
+ return {
+ category,
+ articles: unpackPaginated(articlesResponse),
+ categories: unpackList(categoriesResponse),
+ };
+}
+
+/** Tag archive — `getStaticProps` for `pages/tag/[tag].tsx`. */
+export async function fetchTagArchive(api: ThemeApi, tag: string) {
+ const [articlesResponse, tagsResponse] = await Promise.all([
+ api.article.findArticlesByTag(tag),
+ api.tag.findAll(),
+ ]);
+ return {
+ tag,
+ articles: unpackPaginated(articlesResponse),
+ tags: unpackList(tagsResponse),
+ };
+}
+
+/** Article shape returned by theme article detail pages. */
+export type ThemeArticlePage = {
+ id: string;
+ title: string;
+ summary?: string;
+ html?: string;
+ cover?: string;
+ publishAt?: string;
+ views?: number;
+ category?: { label: string; value: string };
+ tags?: Array<{ label: string; value: string }>;
+};
+
+/** Single article — `getStaticProps` for `pages/article/[id].tsx`. */
+export async function fetchSingleArticle(api: ThemeApi, id: string) {
+ const articleResponse = await api.article.findById(id);
+ return { article: unpackOne(articleResponse) };
+}
+
+/** Site search — `getServerSideProps` / `getStaticProps` helper. */
+export async function fetchSearchArticles(
+ api: ThemeApi,
+ keyword: string,
+): Promise<{
+ query: string;
+ articles: Array<{
+ id: string;
+ title: string;
+ summary?: string;
+ publishAt?: string;
+ category?: { label: string; value: string };
+ }>;
+}> {
+ if (!keyword.trim()) {
+ return { query: '', articles: [] };
+ }
+ const searchResponse = await api.search.searchArticle({
+ query: { keyword: keyword.trim() },
+ } as never);
+ return {
+ query: keyword.trim(),
+ articles: unpackList(searchResponse) as Array<{
+ id: string;
+ title: string;
+ summary?: string;
+ publishAt?: string;
+ category?: { label: string; value: string };
+ }>,
+ };
+}
+
+export type ThemeArchiveKind = 'category' | 'tag';
+
+type StaticPropsContext = {
+ params?: Record;
+};
+
+/**
+ * Wrap `getStaticProps` fetchers with consistent logging and ISR fallback props.
+ */
+export async function withThemeStaticProps>(
+ label: string,
+ fetchFn: () => Promise,
+ fallback: T | ((error: unknown) => T),
+) {
+ const reactPress = await resolveStaticVisitorContext();
+ try {
+ const data = await withApiRetry(fetchFn);
+ return themeStaticProps({ ...data, reactPress });
+ } catch (error) {
+ console.error(`[reactpress] ${label}`, error);
+ const props = typeof fallback === 'function' ? fallback(error) : fallback;
+ return themeStaticProps({ ...props, reactPress });
+ }
+}
+
+/** Factory for category/tag archive pages — removes duplicated `getStaticProps` boilerplate. */
+export function createArchiveGetStaticProps>(
+ kind: ThemeArchiveKind,
+ fetchArchive: (api: ThemeApi, slug: string) => Promise,
+ emptyFallback: (slug: string) => T,
+) {
+ return async (ctx: StaticPropsContext) => {
+ const slug = ctx.params?.[kind];
+ if (typeof slug !== 'string' || !slug) return themeNotFound();
+
+ return withThemeStaticProps(
+ `fetch ${kind} archive failed`,
+ () => fetchArchive(themeApi, slug),
+ () => emptyFallback(slug),
+ );
+ };
+}
+
+/** Standard on-demand article page props. */
+export async function fetchArticlePageProps(
+ api: ThemeApi,
+ id: string | undefined,
+): Promise<{ article: ThemeArticlePage | null }> {
+ if (!id) return { article: null };
+ const data = await fetchSingleArticle(api, id);
+ const article = data.article ?? null;
+ if (!article) return { article: null };
+ return {
+ article: {
+ ...article,
+ html: article.html ? rewriteArticleHtmlAssets(article.html) : article.html,
+ cover: article.cover ? resolvePublicAssetUrl(article.cover) : article.cover,
+ },
+ };
+}
diff --git a/toolkit/src/theme/ssr/format.ts b/toolkit/src/theme/ssr/format.ts
new file mode 100644
index 0000000..a54f1b6
--- /dev/null
+++ b/toolkit/src/theme/ssr/format.ts
@@ -0,0 +1,6 @@
+export {
+ formatPublishDate,
+ formatPublishDateShort,
+} from '../../utils/date';
+export type { SettingRow } from '../../utils/setting';
+export { pickSiteSettings } from '../../utils/setting';
diff --git a/toolkit/src/theme/ssr/pageProps.ts b/toolkit/src/theme/ssr/pageProps.ts
new file mode 100644
index 0000000..ddb27d1
--- /dev/null
+++ b/toolkit/src/theme/ssr/pageProps.ts
@@ -0,0 +1,194 @@
+import type { ThemeApi } from '../api/api';
+import { themeApi } from '../api/api';
+import { unpackList, unpackOne, unpackPaginatedPair } from '../api/api-data';
+import {
+ slimArticlesForList,
+ type CarouselArticle,
+ type ListArticle,
+} from '../content/articleSlim';
+import { slimArchiveTree, type ArchiveTree } from '../content/archiveSlim';
+import { resolveCarouselArticles } from '../content/carouselArticles';
+import { withApiRetry } from './fetch';
+import type { IArticle, ICategory, IKnowledge, ITag } from '../../types';
+
+export type HomePageProps = {
+ articles: ListArticle[];
+ total: number;
+ recommendedArticles: CarouselArticle[];
+};
+
+export type CategoryArchivePageProps = {
+ articles: ListArticle[];
+ total: number;
+ category: Pick & Partial;
+};
+
+export type TagArchivePageProps = {
+ articles: ListArticle[];
+ total: number;
+ tag: Pick & Partial;
+};
+
+export type KnowledgeIndexPageProps = {
+ books: IKnowledge[];
+ total: number;
+};
+
+export type SearchPageProps = {
+ keyword: string;
+ articles: IArticle[];
+};
+
+const DEFAULT_PAGE_SIZE = 12;
+
+function fallbackCategory(value: string): CategoryArchivePageProps['category'] {
+ return { value, label: value };
+}
+
+function fallbackTag(value: string): TagArchivePageProps['tag'] {
+ return { value, label: value };
+}
+
+/** Home / blog index — first page of articles + carousel recommendations. */
+export async function fetchHomePageProps(
+ api: ThemeApi = themeApi,
+ pageSize = DEFAULT_PAGE_SIZE,
+): Promise {
+ return withApiRetry(async () => {
+ const response = await api.article.findAll({
+ query: { page: 1, pageSize, status: 'publish' },
+ } as never);
+ const [rawArticles, total] = unpackPaginatedPair(response);
+ const recommendedArticles = await resolveCarouselArticles(rawArticles, {
+ fetchRecommended: async () =>
+ unpackList(await api.article.getRecommendArticles({} as never)) as IArticle[],
+ });
+ return {
+ articles: slimArticlesForList(rawArticles),
+ total,
+ recommendedArticles,
+ };
+ });
+}
+
+/** Category archive — paginated list with taxonomy metadata. */
+export async function fetchCategoryArchivePageProps(
+ api: ThemeApi,
+ categoryValue: string,
+ pageSize = DEFAULT_PAGE_SIZE,
+): Promise {
+ const [articlesResponse, categoryResponse] = await Promise.all([
+ api.article.findArticlesByCategory(categoryValue, {
+ query: { page: 1, pageSize, status: 'publish' },
+ } as never),
+ api.category.findById(categoryValue),
+ ]);
+ const [rawArticles, total] = unpackPaginatedPair(articlesResponse);
+ const category = unpackOne(categoryResponse) ?? fallbackCategory(categoryValue);
+ return {
+ articles: slimArticlesForList(rawArticles),
+ total,
+ category,
+ };
+}
+
+/** Tag archive — paginated list with taxonomy metadata. */
+export async function fetchTagArchivePageProps(
+ api: ThemeApi,
+ tagValue: string,
+ pageSize = DEFAULT_PAGE_SIZE,
+): Promise {
+ const [articlesResponse, tagResponse] = await Promise.all([
+ api.article.findArticlesByTag(tagValue, {
+ query: { page: 1, pageSize, status: 'publish' },
+ } as never),
+ api.tag.findById(tagValue),
+ ]);
+ const [rawArticles, total] = unpackPaginatedPair(articlesResponse);
+ const tag = unpackOne(tagResponse) ?? fallbackTag(tagValue);
+ return {
+ articles: slimArticlesForList(rawArticles),
+ total,
+ tag,
+ };
+}
+
+/** Archives tree — year/month grouped article index. */
+export async function fetchArchivesPageProps(api: ThemeApi = themeApi): Promise<{ articles: ArchiveTree }> {
+ const response = await api.article.getArchives({} as never);
+ const raw =
+ unpackOne>>(response as never) ??
+ (response as unknown as Record