From 34f4fe4c7be5eae42dd8e37bd0eae467aa2098f0 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Tue, 10 Jun 2025 12:04:10 -0400 Subject: [PATCH] Revert "Use a custom documentation site (#197)" This reverts commit 56ac380e6cc6bbf936f93abe6f433449ada28301. --- client/package-lock.json | 1025 ----------------- docs/.eslintrc.json | 3 - docs/.gitignore | 36 - docs/.prettierignore | 1 - docs/README.md | 36 - .../building-projects/app_basics.md | 0 .../building-projects/build_steps.md | 0 .../building-projects/documenting.md | 0 .../building-projects/parameters.md | 0 .../building-projects/request_data.md | 0 .../building-projects/responses.md | 0 .../building-projects/routing.md | 0 .../building-projects/templating.md | 0 .../building-projects/websockets.md | 0 {docs_md => docs}/contributing.md | 0 .../getting-started/configuration.md | 0 .../getting-started/creating_a_project.md | 0 .../getting-started/installation.md | 0 {docs_md => docs}/index.md | 0 docs/mdx-components.tsx | 5 - docs/next.config.mjs | 15 - docs/package.json | 39 - docs/postcss.config.mjs | 8 - docs/public/logo-white.svg | 155 --- docs/public/logo.svg | 153 --- docs/public/space.png | Bin 11355 -> 0 bytes docs/{src/pages/docs => }/reference/app.md | 0 docs/{src/pages/docs => }/reference/build.md | 0 docs/{src/pages/docs => }/reference/config.md | 0 .../pages/docs => }/reference/exceptions.md | 0 .../pages/docs => }/reference/responses.md | 0 .../{src/pages/docs => }/reference/routing.md | 0 .../pages/docs => }/reference/templates.md | 0 docs/{src/pages/docs => }/reference/types.md | 0 docs/{src/pages/docs => }/reference/utils.md | 0 .../pages/docs => }/reference/websockets.md | 0 docs/src/components/Spotlight.tsx | 59 - docs/src/components/admonition.tsx | 155 --- docs/src/components/animated-modal.tsx | 243 ---- docs/src/components/aurora-background.tsx | 54 - docs/src/components/background-beams.tsx | 160 --- .../background-gradient-animation.tsx | 182 --- docs/src/components/background-gradient.tsx | 72 -- docs/src/components/codeblock.tsx | 86 -- docs/src/components/doc-nav.tsx | 65 -- docs/src/components/docs.module.css | 43 - docs/src/components/footer-content.tsx | 48 - docs/src/components/mdx-layout.tsx | 334 ------ docs/src/components/meteors.tsx | 35 - .../placeholders-and-vanish-input.tsx | 280 ----- docs/src/components/search-bar.tsx | 243 ---- docs/src/components/stars-background.tsx | 133 --- docs/src/pages/_app.tsx | 152 --- .../docs/building-projects/app-basics.mdx | 135 --- .../pages/docs/building-projects/builds.mdx | 249 ---- .../docs/building-projects/documenting.mdx | 134 --- .../docs/building-projects/parameters.mdx | 388 ------- .../docs/building-projects/request-data.mdx | 208 ---- .../docs/building-projects/responses.mdx | 334 ------ .../pages/docs/building-projects/routing.mdx | 296 ----- .../docs/building-projects/templating.mdx | 210 ---- .../docs/building-projects/websockets.mdx | 200 ---- .../docs/getting-started/configuration.mdx | 183 --- .../getting-started/creating-a-project.mdx | 93 -- .../docs/getting-started/installation.mdx | 72 -- docs/src/pages/docs/index.mdx | 44 - docs/src/pages/favicon.ico | Bin 25931 -> 0 bytes docs/src/pages/globals.css | 19 - docs/src/pages/index.tsx | 247 ---- docs/src/utils/cn.ts | 6 - docs/tailwind.config.ts | 151 --- docs/tsconfig.json | 26 - docs_md/reference/app.md | 3 - docs_md/reference/build.md | 3 - docs_md/reference/config.md | 3 - docs_md/reference/exceptions.md | 3 - docs_md/reference/responses.md | 3 - docs_md/reference/routing.md | 8 - docs_md/reference/templates.md | 3 - docs_md/reference/types.md | 3 - docs_md/reference/utils.md | 4 - docs_md/reference/websockets.md | 3 - 82 files changed, 6851 deletions(-) delete mode 100644 client/package-lock.json delete mode 100644 docs/.eslintrc.json delete mode 100644 docs/.gitignore delete mode 100644 docs/.prettierignore delete mode 100644 docs/README.md rename {docs_md => docs}/building-projects/app_basics.md (100%) rename {docs_md => docs}/building-projects/build_steps.md (100%) rename {docs_md => docs}/building-projects/documenting.md (100%) rename {docs_md => docs}/building-projects/parameters.md (100%) rename {docs_md => docs}/building-projects/request_data.md (100%) rename {docs_md => docs}/building-projects/responses.md (100%) rename {docs_md => docs}/building-projects/routing.md (100%) rename {docs_md => docs}/building-projects/templating.md (100%) rename {docs_md => docs}/building-projects/websockets.md (100%) rename {docs_md => docs}/contributing.md (100%) rename {docs_md => docs}/getting-started/configuration.md (100%) rename {docs_md => docs}/getting-started/creating_a_project.md (100%) rename {docs_md => docs}/getting-started/installation.md (100%) rename {docs_md => docs}/index.md (100%) delete mode 100644 docs/mdx-components.tsx delete mode 100644 docs/next.config.mjs delete mode 100644 docs/package.json delete mode 100644 docs/postcss.config.mjs delete mode 100644 docs/public/logo-white.svg delete mode 100644 docs/public/logo.svg delete mode 100644 docs/public/space.png rename docs/{src/pages/docs => }/reference/app.md (100%) rename docs/{src/pages/docs => }/reference/build.md (100%) rename docs/{src/pages/docs => }/reference/config.md (100%) rename docs/{src/pages/docs => }/reference/exceptions.md (100%) rename docs/{src/pages/docs => }/reference/responses.md (100%) rename docs/{src/pages/docs => }/reference/routing.md (100%) rename docs/{src/pages/docs => }/reference/templates.md (100%) rename docs/{src/pages/docs => }/reference/types.md (100%) rename docs/{src/pages/docs => }/reference/utils.md (100%) rename docs/{src/pages/docs => }/reference/websockets.md (100%) delete mode 100644 docs/src/components/Spotlight.tsx delete mode 100644 docs/src/components/admonition.tsx delete mode 100644 docs/src/components/animated-modal.tsx delete mode 100644 docs/src/components/aurora-background.tsx delete mode 100644 docs/src/components/background-beams.tsx delete mode 100644 docs/src/components/background-gradient-animation.tsx delete mode 100644 docs/src/components/background-gradient.tsx delete mode 100644 docs/src/components/codeblock.tsx delete mode 100644 docs/src/components/doc-nav.tsx delete mode 100644 docs/src/components/docs.module.css delete mode 100644 docs/src/components/footer-content.tsx delete mode 100644 docs/src/components/mdx-layout.tsx delete mode 100644 docs/src/components/meteors.tsx delete mode 100644 docs/src/components/placeholders-and-vanish-input.tsx delete mode 100644 docs/src/components/search-bar.tsx delete mode 100644 docs/src/components/stars-background.tsx delete mode 100644 docs/src/pages/_app.tsx delete mode 100644 docs/src/pages/docs/building-projects/app-basics.mdx delete mode 100644 docs/src/pages/docs/building-projects/builds.mdx delete mode 100644 docs/src/pages/docs/building-projects/documenting.mdx delete mode 100644 docs/src/pages/docs/building-projects/parameters.mdx delete mode 100644 docs/src/pages/docs/building-projects/request-data.mdx delete mode 100644 docs/src/pages/docs/building-projects/responses.mdx delete mode 100644 docs/src/pages/docs/building-projects/routing.mdx delete mode 100644 docs/src/pages/docs/building-projects/templating.mdx delete mode 100644 docs/src/pages/docs/building-projects/websockets.mdx delete mode 100644 docs/src/pages/docs/getting-started/configuration.mdx delete mode 100644 docs/src/pages/docs/getting-started/creating-a-project.mdx delete mode 100644 docs/src/pages/docs/getting-started/installation.mdx delete mode 100644 docs/src/pages/docs/index.mdx delete mode 100644 docs/src/pages/favicon.ico delete mode 100644 docs/src/pages/globals.css delete mode 100644 docs/src/pages/index.tsx delete mode 100644 docs/src/utils/cn.ts delete mode 100644 docs/tailwind.config.ts delete mode 100644 docs/tsconfig.json delete mode 100644 docs_md/reference/app.md delete mode 100644 docs_md/reference/build.md delete mode 100644 docs_md/reference/config.md delete mode 100644 docs_md/reference/exceptions.md delete mode 100644 docs_md/reference/responses.md delete mode 100644 docs_md/reference/routing.md delete mode 100644 docs_md/reference/templates.md delete mode 100644 docs_md/reference/types.md delete mode 100644 docs_md/reference/utils.md delete mode 100644 docs_md/reference/websockets.md diff --git a/client/package-lock.json b/client/package-lock.json deleted file mode 100644 index 3b75292a..00000000 --- a/client/package-lock.json +++ /dev/null @@ -1,1025 +0,0 @@ -{ - "name": "client", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@reactpy/client": "^0.3.1", - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "devDependencies": { - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "typescript": "^5.4.5", - "vite": "^5.2.0", - "vite-plugin-singlefile": "^2.0.1" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@reactpy/client": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@reactpy/client/-/client-0.3.1.tgz", - "integrity": "sha512-mvFwAvmRMgo7lTjkhkEJzBep6HX/wfm5BaNbtEMOUzto7G/h+z1AmqlOMXLH37DSI0iwfmCuNwy07EJM0JWZ0g==", - "dependencies": { - "event-to-object": "^0.1.2", - "json-pointer": "^0.6.2" - }, - "peerDependencies": { - "react": ">=16 <18", - "react-dom": ">=16 <18" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" - } - }, - "node_modules/event-to-object": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/event-to-object/-/event-to-object-0.1.2.tgz", - "integrity": "sha512-+fUmp1XOCZiYomwe5Zxp4IlchuZZfdVdjFUk5MbgRT4M+V2TEWKc0jJwKLCX/nxlJ6xM5VUb/ylzERh7YDCRrg==", - "dependencies": { - "json-pointer": "^0.6.2" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreach": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", - "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/json-pointer": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.2.tgz", - "integrity": "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==", - "dependencies": { - "foreach": "^2.0.4" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/vite": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", - "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", - "dev": true, - "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-plugin-singlefile": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.0.1.tgz", - "integrity": "sha512-J74tfN6TE4fz0Hp7E1+dmVTmCpyazv4yuIpR6jd22Kq76d2CQDSQx3wDiHX8LT02f922V+YrLhRq2VIk/UYrig==", - "dev": true, - "dependencies": { - "micromatch": "^4.0.5" - }, - "engines": { - "node": ">18.0.0" - }, - "peerDependencies": { - "rollup": "^4.12.0", - "vite": "^5.1.4" - } - } - } -} diff --git a/docs/.eslintrc.json b/docs/.eslintrc.json deleted file mode 100644 index bffb357a..00000000 --- a/docs/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index fd3dbb57..00000000 --- a/docs/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/docs/.prettierignore b/docs/.prettierignore deleted file mode 100644 index 6e1fb3a4..00000000 --- a/docs/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -**/*.mdx \ No newline at end of file diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index c4033664..00000000 --- a/docs/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/docs_md/building-projects/app_basics.md b/docs/building-projects/app_basics.md similarity index 100% rename from docs_md/building-projects/app_basics.md rename to docs/building-projects/app_basics.md diff --git a/docs_md/building-projects/build_steps.md b/docs/building-projects/build_steps.md similarity index 100% rename from docs_md/building-projects/build_steps.md rename to docs/building-projects/build_steps.md diff --git a/docs_md/building-projects/documenting.md b/docs/building-projects/documenting.md similarity index 100% rename from docs_md/building-projects/documenting.md rename to docs/building-projects/documenting.md diff --git a/docs_md/building-projects/parameters.md b/docs/building-projects/parameters.md similarity index 100% rename from docs_md/building-projects/parameters.md rename to docs/building-projects/parameters.md diff --git a/docs_md/building-projects/request_data.md b/docs/building-projects/request_data.md similarity index 100% rename from docs_md/building-projects/request_data.md rename to docs/building-projects/request_data.md diff --git a/docs_md/building-projects/responses.md b/docs/building-projects/responses.md similarity index 100% rename from docs_md/building-projects/responses.md rename to docs/building-projects/responses.md diff --git a/docs_md/building-projects/routing.md b/docs/building-projects/routing.md similarity index 100% rename from docs_md/building-projects/routing.md rename to docs/building-projects/routing.md diff --git a/docs_md/building-projects/templating.md b/docs/building-projects/templating.md similarity index 100% rename from docs_md/building-projects/templating.md rename to docs/building-projects/templating.md diff --git a/docs_md/building-projects/websockets.md b/docs/building-projects/websockets.md similarity index 100% rename from docs_md/building-projects/websockets.md rename to docs/building-projects/websockets.md diff --git a/docs_md/contributing.md b/docs/contributing.md similarity index 100% rename from docs_md/contributing.md rename to docs/contributing.md diff --git a/docs_md/getting-started/configuration.md b/docs/getting-started/configuration.md similarity index 100% rename from docs_md/getting-started/configuration.md rename to docs/getting-started/configuration.md diff --git a/docs_md/getting-started/creating_a_project.md b/docs/getting-started/creating_a_project.md similarity index 100% rename from docs_md/getting-started/creating_a_project.md rename to docs/getting-started/creating_a_project.md diff --git a/docs_md/getting-started/installation.md b/docs/getting-started/installation.md similarity index 100% rename from docs_md/getting-started/installation.md rename to docs/getting-started/installation.md diff --git a/docs_md/index.md b/docs/index.md similarity index 100% rename from docs_md/index.md rename to docs/index.md diff --git a/docs/mdx-components.tsx b/docs/mdx-components.tsx deleted file mode 100644 index fddbb6d0..00000000 --- a/docs/mdx-components.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from "react"; - -export function useMDXComponents(components: React.ReactNode[]) { - return { ...components }; -} diff --git a/docs/next.config.mjs b/docs/next.config.mjs deleted file mode 100644 index cea58fd4..00000000 --- a/docs/next.config.mjs +++ /dev/null @@ -1,15 +0,0 @@ -import withMDX from "@next/mdx"; -/** @type {import('next').NextConfig} */ -const nextConfig = withMDX()({ - pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"], - transpilePackages: - process.env.NODE_ENV !== "development" - ? ["geist"] - : ["geist", "hightlight.js"], -}); - -/* -https://github.com/vercel/geist-font/issues/59, -https://github.com/highlightjs/highlight.js/issues/3982 -*/ -export default nextConfig; diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index e46b56b4..00000000 --- a/docs/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "docs", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "postbuild": "pagefind --site .next --output-path .next/static/chunks/pages/pagefind" - }, - "dependencies": { - "@mdx-js/loader": "^3.0.1", - "@next/mdx": "^14.2.5", - "clsx": "^2.1.1", - "framer-motion": "^11.3.19", - "geist": "^1.3.1", - "highlight.js": "^11.10.0", - "mini-css-extract-plugin": "^2.9.0", - "mini-svg-data-uri": "^1.4.4", - "next": "14.2.5", - "react": "^18", - "react-dom": "^18", - "react-icons": "^5.2.1", - "tailwind-merge": "^2.4.0" - }, - "devDependencies": { - "@types/node": "^22.0.0", - "@types/react": "^18", - "@types/react-dom": "^18", - "eslint": "^9.8.0", - "eslint-config-next": "14.2.5", - "pagefind": "^1.1.0", - "postcss": "^8.4.40", - "tailwindcss": "^3.4.7", - "typescript": "^5.5.4" - }, - "type": "module" -} diff --git a/docs/postcss.config.mjs b/docs/postcss.config.mjs deleted file mode 100644 index 1a69fd2a..00000000 --- a/docs/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('postcss-load-config').Config} */ -const config = { - plugins: { - tailwindcss: {}, - }, -}; - -export default config; diff --git a/docs/public/logo-white.svg b/docs/public/logo-white.svg deleted file mode 100644 index 54ec7054..00000000 --- a/docs/public/logo-white.svg +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/public/logo.svg b/docs/public/logo.svg deleted file mode 100644 index f7bd0e4d..00000000 --- a/docs/public/logo.svg +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/public/space.png b/docs/public/space.png deleted file mode 100644 index 9b2794a0113783795d9a093d56f75a8e5a3add74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11355 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 E0e5`XPXGV_ diff --git a/docs/src/pages/docs/reference/app.md b/docs/reference/app.md similarity index 100% rename from docs/src/pages/docs/reference/app.md rename to docs/reference/app.md diff --git a/docs/src/pages/docs/reference/build.md b/docs/reference/build.md similarity index 100% rename from docs/src/pages/docs/reference/build.md rename to docs/reference/build.md diff --git a/docs/src/pages/docs/reference/config.md b/docs/reference/config.md similarity index 100% rename from docs/src/pages/docs/reference/config.md rename to docs/reference/config.md diff --git a/docs/src/pages/docs/reference/exceptions.md b/docs/reference/exceptions.md similarity index 100% rename from docs/src/pages/docs/reference/exceptions.md rename to docs/reference/exceptions.md diff --git a/docs/src/pages/docs/reference/responses.md b/docs/reference/responses.md similarity index 100% rename from docs/src/pages/docs/reference/responses.md rename to docs/reference/responses.md diff --git a/docs/src/pages/docs/reference/routing.md b/docs/reference/routing.md similarity index 100% rename from docs/src/pages/docs/reference/routing.md rename to docs/reference/routing.md diff --git a/docs/src/pages/docs/reference/templates.md b/docs/reference/templates.md similarity index 100% rename from docs/src/pages/docs/reference/templates.md rename to docs/reference/templates.md diff --git a/docs/src/pages/docs/reference/types.md b/docs/reference/types.md similarity index 100% rename from docs/src/pages/docs/reference/types.md rename to docs/reference/types.md diff --git a/docs/src/pages/docs/reference/utils.md b/docs/reference/utils.md similarity index 100% rename from docs/src/pages/docs/reference/utils.md rename to docs/reference/utils.md diff --git a/docs/src/pages/docs/reference/websockets.md b/docs/reference/websockets.md similarity index 100% rename from docs/src/pages/docs/reference/websockets.md rename to docs/reference/websockets.md diff --git a/docs/src/components/Spotlight.tsx b/docs/src/components/Spotlight.tsx deleted file mode 100644 index 33835a58..00000000 --- a/docs/src/components/Spotlight.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from "react"; -import { cn } from "@/utils/cn"; - -type SpotlightProps = { - className?: string; - fill?: string; -}; - -export const Spotlight = ({ className, fill }: SpotlightProps) => { - return ( - - - - - - - - - - - - - ); -}; diff --git a/docs/src/components/admonition.tsx b/docs/src/components/admonition.tsx deleted file mode 100644 index 8f04bdae..00000000 --- a/docs/src/components/admonition.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import React from "react"; - -type Level = "info" | "danger" | "warning" | "note" | "question"; -type AdmonitionProps = { - children: React.ReactNode; - title: string | undefined; - level: Level | undefined; -}; - -const LEVEL_BORDER_COLORS: Record = { - info: "border-l-sky-500", - warning: "border-l-amber-500", - danger: "border-l-rose-500", - note: "border-l-blue-500", - question: "border-l-green-500", -}; - -function LevelTitle({ - level, - title, -}: { - level: Level; - title: string | undefined; -}) { - switch (level) { - case "info": - return ( -
- - - - -
- {title || "Info"} -
-
- ); - case "warning": - return ( -
- - - -
- {title || "Warning"} -
-
- ); - case "danger": - return ( -
- - - - -
- {title || "Danger"} -
-
- ); - case "note": - return ( -
- - - - -
- {title || "Note"} -
-
- ); - case "question": - return ( -
- - - - -
- {title || "Question"} -
-
- ); - } -} - -export default function Admonition(props: AdmonitionProps) { - const level = props.level || "note"; - return ( -
-
- -
-
{props.children}
-
- ); -} diff --git a/docs/src/components/animated-modal.tsx b/docs/src/components/animated-modal.tsx deleted file mode 100644 index f93ea883..00000000 --- a/docs/src/components/animated-modal.tsx +++ /dev/null @@ -1,243 +0,0 @@ -"use client"; -import { cn } from "@/utils/cn"; -import { AnimatePresence, motion } from "framer-motion"; -import React, { - ReactNode, - createContext, - useContext, - useEffect, - useRef, - useState, -} from "react"; - -interface ModalContextType { - open: boolean; - setOpen: (open: boolean) => void; -} - -const ModalContext = createContext(undefined); - -export const ModalProvider = ({ children }: { children: ReactNode }) => { - const [open, setOpen] = useState(false); - - return ( - - {children} - - ); -}; - -export const useModal = () => { - const context = useContext(ModalContext); - if (!context) { - throw new Error("useModal must be used within a ModalProvider"); - } - return context; -}; - -export function Modal({ children }: { children: ReactNode }) { - return {children}; -} - -export const ModalTrigger = ({ - children, - className, -}: { - children: ReactNode; - className?: string; -}) => { - const { setOpen } = useModal(); - return ( - - ); -}; - -export const ModalBody = ({ - children, - className, -}: { - children: ReactNode; - className?: string; -}) => { - const { open } = useModal(); - - useEffect(() => { - if (open) { - document.body.style.overflow = "hidden"; - } else { - document.body.style.overflow = "auto"; - } - }, [open]); - - const modalRef = useRef(null); - const { setOpen } = useModal(); - useOutsideClick(modalRef, () => setOpen(false)); - - return ( - - {open && ( - - - - - - {children} - - - )} - - ); -}; - -export const ModalContent = ({ - children, - className, -}: { - children: ReactNode; - className?: string; -}) => { - return ( -
- {children} -
- ); -}; - -export const ModalFooter = ({ - children, - className, -}: { - children: ReactNode; - className?: string; -}) => { - return ( -
- {children} -
- ); -}; - -const Overlay = ({ className }: { className?: string }) => { - return ( - - ); -}; - -const CloseIcon = () => { - const { setOpen } = useModal(); - return ( - - ); -}; - -// Hook to detect clicks outside of a component. -// Add it in a separate file, I've added here for simplicity -export const useOutsideClick = ( - ref: React.RefObject, - callback: Function -) => { - useEffect(() => { - const listener = (event: any) => { - // DO NOTHING if the element being clicked is the target element or their children - if (!ref.current || ref.current.contains(event.target)) { - return; - } - callback(event); - }; - - document.addEventListener("mousedown", listener); - document.addEventListener("touchstart", listener); - - return () => { - document.removeEventListener("mousedown", listener); - document.removeEventListener("touchstart", listener); - }; - }, [ref, callback]); -}; diff --git a/docs/src/components/aurora-background.tsx b/docs/src/components/aurora-background.tsx deleted file mode 100644 index 209cb2b9..00000000 --- a/docs/src/components/aurora-background.tsx +++ /dev/null @@ -1,54 +0,0 @@ -"use client"; -import { cn } from "@/utils/cn"; -import React, { ReactNode } from "react"; - -interface AuroraBackgroundProps extends React.HTMLProps { - children: ReactNode; - showRadialGradient?: boolean; -} - -export const AuroraBackground = ({ - className, - children, - showRadialGradient = true, - ...props -}: AuroraBackgroundProps) => { - return ( -
-
-
-
-
- {children} -
-
- ); -}; diff --git a/docs/src/components/background-beams.tsx b/docs/src/components/background-beams.tsx deleted file mode 100644 index 02a5fffa..00000000 --- a/docs/src/components/background-beams.tsx +++ /dev/null @@ -1,160 +0,0 @@ -"use client"; -import React from "react"; -import { motion } from "framer-motion"; -import { cn } from "@/utils/cn"; - -export const BackgroundBeams = React.memo( - ({ className }: { className?: string }) => { - const paths = [ - "M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875", - "M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867", - "M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859", - "M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851", - "M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843", - "M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835", - "M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827", - "M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819", - "M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811", - "M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803", - "M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795", - "M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787", - "M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779", - "M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771", - "M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763", - "M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755", - "M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747", - "M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739", - "M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731", - "M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723", - "M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715", - "M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707", - "M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699", - "M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691", - "M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683", - "M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675", - "M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667", - "M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659", - "M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651", - "M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643", - "M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635", - "M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627", - "M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619", - "M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611", - "M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603", - "M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595", - "M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587", - "M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579", - "M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571", - "M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563", - "M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555", - "M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547", - "M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539", - "M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531", - "M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523", - "M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515", - "M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507", - "M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499", - "M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491", - "M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483", - ]; - return ( -
- - - - {paths.map((path, index) => ( - - ))} - - {paths.map((path, index) => ( - - - - - - - ))} - - - - - - - - - {/* Radial gradient for the container to give a faded look */} -
-
- ); - } -); - -BackgroundBeams.displayName = "BackgroundBeams"; diff --git a/docs/src/components/background-gradient-animation.tsx b/docs/src/components/background-gradient-animation.tsx deleted file mode 100644 index e0aa6bbd..00000000 --- a/docs/src/components/background-gradient-animation.tsx +++ /dev/null @@ -1,182 +0,0 @@ -"use client"; -import { cn } from "@/utils/cn"; -import { useEffect, useRef, useState } from "react"; - -export const BackgroundGradientAnimation = ({ - gradientBackgroundStart = "rgb(0, 0, 0)", - gradientBackgroundEnd = "rgb(1, 1, 1)", - firstColor = "76, 0, 255", - secondColor = "32, 94, 110", - thirdColor = "73, 87, 179", - fourthColor = "0, 87, 128", - fifthColor = "255, 255, 255", - pointerColor = "32, 44, 110", - size = "80%", - blendingValue = "hard-light", - children, - className, - interactive = true, - containerClassName, -}: { - gradientBackgroundStart?: string; - gradientBackgroundEnd?: string; - firstColor?: string; - secondColor?: string; - thirdColor?: string; - fourthColor?: string; - fifthColor?: string; - pointerColor?: string; - size?: string; - blendingValue?: string; - children?: React.ReactNode; - className?: string; - interactive?: boolean; - containerClassName?: string; -}) => { - const interactiveRef = useRef(null); - - const [curX, setCurX] = useState(0); - const [curY, setCurY] = useState(0); - const [tgX, setTgX] = useState(0); - const [tgY, setTgY] = useState(0); - useEffect(() => { - document.body.style.setProperty( - "--gradient-background-start", - gradientBackgroundStart - ); - document.body.style.setProperty( - "--gradient-background-end", - gradientBackgroundEnd - ); - document.body.style.setProperty("--first-color", firstColor); - document.body.style.setProperty("--second-color", secondColor); - document.body.style.setProperty("--third-color", thirdColor); - document.body.style.setProperty("--fourth-color", fourthColor); - document.body.style.setProperty("--fifth-color", fifthColor); - document.body.style.setProperty("--pointer-color", pointerColor); - document.body.style.setProperty("--size", size); - document.body.style.setProperty("--blending-value", blendingValue); - }, []); - - useEffect(() => { - function move() { - if (!interactiveRef.current) { - return; - } - setCurX(curX + (tgX - curX) / 20); - setCurY(curY + (tgY - curY) / 20); - interactiveRef.current.style.transform = `translate(${Math.round( - curX - )}px, ${Math.round(curY)}px)`; - } - - move(); - }, [tgX, tgY]); - - const handleMouseMove = (event: React.MouseEvent) => { - if (interactiveRef.current) { - const rect = interactiveRef.current.getBoundingClientRect(); - setTgX(event.clientX - rect.left); - setTgY(event.clientY - rect.top); - } - }; - - const [isSafari, setIsSafari] = useState(false); - useEffect(() => { - setIsSafari(/^((?!chrome|android).)*safari/i.test(navigator.userAgent)); - }, []); - - return ( -
- - - - - - - - - -
{children}
-
-
-
-
-
-
- {interactive && ( -
- )} -
- {/* Radial gradient for the container to give a faded look */} -
-
- ); -}; diff --git a/docs/src/components/background-gradient.tsx b/docs/src/components/background-gradient.tsx deleted file mode 100644 index f4e525dc..00000000 --- a/docs/src/components/background-gradient.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { cn } from "@/utils/cn"; -import React from "react"; -import { motion } from "framer-motion"; - -export const BackgroundGradient = ({ - children, - className, - containerClassName, - animate = true, -}: { - children?: React.ReactNode; - className?: string; - containerClassName?: string; - animate?: boolean; -}) => { - const variants = { - initial: { - backgroundPosition: "0 50%", - }, - animate: { - backgroundPosition: ["0, 50%", "100% 50%", "0 50%"], - }, - }; - return ( -
- - - -
{children}
-
- ); -}; diff --git a/docs/src/components/codeblock.tsx b/docs/src/components/codeblock.tsx deleted file mode 100644 index 295f0a61..00000000 --- a/docs/src/components/codeblock.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from "react"; -import { FaPython } from "react-icons/fa"; -import { TbToml } from "react-icons/tb"; -import { FaHtml5 } from "react-icons/fa"; -import { SiGnubash } from "react-icons/si"; -import { VscJson } from "react-icons/vsc"; - -interface CodeblockProps { - children: React.ReactNode; - fileName: string; -} - -const ICONS: Record = { - py: , - html: , - toml: , - bash: , - json: , -}; - -export default function Codeblock(props: CodeblockProps) { - const ext: string = props.fileName.split(".")[1]; - const [copied, setCopied] = React.useState(false); - const elementRef = React.useRef(null); - - return ( -
-
-
- {ICONS[ext] || ICONS[props.fileName]} - - {props.fileName} - -
- -
- {props.children} -
- ); -} diff --git a/docs/src/components/doc-nav.tsx b/docs/src/components/doc-nav.tsx deleted file mode 100644 index d43be465..00000000 --- a/docs/src/components/doc-nav.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import Link from "next/link"; - -export type DocNavPage = { - name: string; - url: string; -}; - -interface DocNavProps { - next: DocNavPage | null; - last: DocNavPage | null; -} - -export default function DocNav(props: DocNavProps) { - return ( -
- {props.last ? ( - - - - - -

{props.last.name}

- - ) : ( -
- )} - {props.next && ( - -

{props.next.name}

- - - - - )} -
- ); -} diff --git a/docs/src/components/docs.module.css b/docs/src/components/docs.module.css deleted file mode 100644 index 1adc4582..00000000 --- a/docs/src/components/docs.module.css +++ /dev/null @@ -1,43 +0,0 @@ -.docs { - @apply flex flex-col space-y-2 text-lg; -} - -.docs p { - @apply py-1; -} - -.docs h1 { - @apply text-5xl font-bold py-2; -} - -.docs h2 { - @apply text-3xl py-2 font-semibold; -} - -.docs h3 { - @apply text-xl py-2 font-semibold; -} - -.docs code { - @apply bg-transparent text-base; -} - -.docs p code { - @apply rounded-md bg-zinc-950 border border-zinc-900 p-1 text-zinc-300; -} - -.docs li code { - @apply rounded-md bg-zinc-950 border border-zinc-900 p-1 text-zinc-300; -} - -.docs li { - @apply py-0.5; -} - -.docs ul { - @apply text-white pl-6 list-disc; -} - -.docs a { - @apply text-sky-600 hover:text-sky-400 font-medium; -} diff --git a/docs/src/components/footer-content.tsx b/docs/src/components/footer-content.tsx deleted file mode 100644 index 95639ab7..00000000 --- a/docs/src/components/footer-content.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import Image from "next/image"; -import { FaRegCopyright, FaDiscord, FaGithub } from "react-icons/fa"; -import { SiGithubsponsors } from "react-icons/si"; - -export default function FooterContent() { - return ( -
- view.py -
- -
- -

- ZeroIntensity 2024 -

-
-
-
- ); -} diff --git a/docs/src/components/mdx-layout.tsx b/docs/src/components/mdx-layout.tsx deleted file mode 100644 index af613202..00000000 --- a/docs/src/components/mdx-layout.tsx +++ /dev/null @@ -1,334 +0,0 @@ -import { usePathname } from "next/navigation"; -import styles from "./docs.module.css"; -import DocNav, { DocNavPage } from "./doc-nav"; -import { useRef, useEffect, useState } from "react"; -import Link from "next/link"; -import FooterContent from "./footer-content"; - -type Section = Record; -type Nav = Record; -const NAV: Nav = { - "Getting Started": { - Introduction: "/docs", - Installation: "/docs/getting-started/installation", - "Creating a project": "/docs/getting-started/creating-a-project", - Configuration: "/docs/getting-started/configuration", - }, - "Building Projects": { - "App Basics": "/docs/building-projects/app-basics", - "URL Routing": "/docs/building-projects/routing", - "Returning Responses": "/docs/building-projects/responses", - "Taking Parameters": "/docs/building-projects/parameters", - "Getting Request Data": "/docs/building-projects/request-data", - "HTML Templating": "/docs/building-projects/templating", - "Runtime Builds": "/docs/building-projects/builds", - "Writing Documentation": "/docs/building-projects/documenting", - "Using WebSockets": "/docs/building-projects/websockets", - }, - "API Reference": { - Types: "/docs/reference/types", - Utilities: "/docs/reference/utils", - Exceptions: "/docs/reference/exceptions", - Apps: "/docs/reference/apps", - Configuration: "/docs/reference/config", - Routing: "/docs/reference/routing", - Templates: "/docs/reference/templates", - Build: "/docs/reference/build", - WebSockets: "/docs/reference/websockets", - }, -}; - -export default function MdxLayout({ children }: { children: React.ReactNode }) { - const path = usePathname(); - let [next, setNext] = useState(null); - let [last, setLast] = useState(null); - const mdRef = useRef(null); - - let [query, setQuery] = useState | null>(null); - useEffect(() => { - Object.entries(NAV).forEach((firstEntry, firstIndex) => { - const entries = Object.entries(firstEntry[1]); - entries.forEach((entry, index) => { - let [pageName, url] = entry; - if (url == path) { - // This is the page! - if (entries[index + 1] != undefined) { - let [pageName, url] = entries[index + 1]; - setNext({ - name: pageName, - url, - }); - } - - if (entries[index - 1] != undefined) { - let [pageName, url] = entries[index - 1]; - setLast({ - name: pageName, - url, - }); - } - } - }); - }); - setQuery(mdRef.current!.querySelectorAll("h2")); - }, [mdRef]); - - return ( - <> -
-
- -
-
-
{children}
-
-
-
- -
- -
-
-
- -
- - ); -} diff --git a/docs/src/components/meteors.tsx b/docs/src/components/meteors.tsx deleted file mode 100644 index 341539d0..00000000 --- a/docs/src/components/meteors.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { cn } from "@/utils/cn"; -import React from "react"; - -export const Meteors = ({ - number, - className, -}: { - number?: number; - className?: string; -}) => { - const meteors = new Array(number || 20).fill(true); - return ( - <> - {meteors.map((el, idx) => ( - - ))} - - ); -}; diff --git a/docs/src/components/placeholders-and-vanish-input.tsx b/docs/src/components/placeholders-and-vanish-input.tsx deleted file mode 100644 index b129fa9d..00000000 --- a/docs/src/components/placeholders-and-vanish-input.tsx +++ /dev/null @@ -1,280 +0,0 @@ -"use client"; - -import { AnimatePresence, motion } from "framer-motion"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { cn } from "@/utils/cn"; - -export function PlaceholdersAndVanishInput({ - placeholders, - onChange, - onSubmit, -}: { - placeholders: string[]; - onChange: (e: React.ChangeEvent) => void; - onSubmit: (e: React.FormEvent) => void; -}) { - const [currentPlaceholder, setCurrentPlaceholder] = useState(0); - - const intervalRef = useRef(null); - const startAnimation = () => { - intervalRef.current = setInterval(() => { - setCurrentPlaceholder(prev => (prev + 1) % placeholders.length); - }, 3000); - }; - const handleVisibilityChange = () => { - if (document.visibilityState !== "visible" && intervalRef.current) { - clearInterval(intervalRef.current); // Clear the interval when the tab is not visible - intervalRef.current = null; - } else if (document.visibilityState === "visible") { - startAnimation(); // Restart the interval when the tab becomes visible - } - }; - - useEffect(() => { - startAnimation(); - document.addEventListener("visibilitychange", handleVisibilityChange); - - return () => { - if (intervalRef.current) { - clearInterval(intervalRef.current); - } - document.removeEventListener( - "visibilitychange", - handleVisibilityChange - ); - }; - }, [placeholders]); - - const canvasRef = useRef(null); - const newDataRef = useRef([]); - const inputRef = useRef(null); - const [value, setValue] = useState(""); - const [animating, setAnimating] = useState(false); - - const draw = useCallback(() => { - if (!inputRef.current) return; - const canvas = canvasRef.current; - if (!canvas) return; - const ctx = canvas.getContext("2d"); - if (!ctx) return; - - canvas.width = 800; - canvas.height = 800; - ctx.clearRect(0, 0, 800, 800); - const computedStyles = getComputedStyle(inputRef.current); - - const fontSize = parseFloat( - computedStyles.getPropertyValue("font-size") - ); - ctx.font = `${fontSize * 2}px ${computedStyles.fontFamily}`; - ctx.fillStyle = "#FFF"; - ctx.fillText(value, 16, 40); - - const imageData = ctx.getImageData(0, 0, 800, 800); - const pixelData = imageData.data; - const newData: any[] = []; - - for (let t = 0; t < 800; t++) { - let i = 4 * t * 800; - for (let n = 0; n < 800; n++) { - let e = i + 4 * n; - if ( - pixelData[e] !== 0 && - pixelData[e + 1] !== 0 && - pixelData[e + 2] !== 0 - ) { - newData.push({ - x: n, - y: t, - color: [ - pixelData[e], - pixelData[e + 1], - pixelData[e + 2], - pixelData[e + 3], - ], - }); - } - } - } - - newDataRef.current = newData.map(({ x, y, color }) => ({ - x, - y, - r: 1, - color: `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`, - })); - }, [value]); - - useEffect(() => { - draw(); - }, [value, draw]); - - const animate = (start: number) => { - const animateFrame = (pos: number = 0) => { - requestAnimationFrame(() => { - const newArr = []; - for (let i = 0; i < newDataRef.current.length; i++) { - const current = newDataRef.current[i]; - if (current.x < pos) { - newArr.push(current); - } else { - if (current.r <= 0) { - current.r = 0; - continue; - } - current.x += Math.random() > 0.5 ? 1 : -1; - current.y += Math.random() > 0.5 ? 1 : -1; - current.r -= 0.05 * Math.random(); - newArr.push(current); - } - } - newDataRef.current = newArr; - const ctx = canvasRef.current?.getContext("2d"); - if (ctx) { - ctx.clearRect(pos, 0, 800, 800); - newDataRef.current.forEach(t => { - const { x: n, y: i, r: s, color: color } = t; - if (n > pos) { - ctx.beginPath(); - ctx.rect(n, i, s, s); - ctx.fillStyle = color; - ctx.strokeStyle = color; - ctx.stroke(); - } - }); - } - if (newDataRef.current.length > 0) { - animateFrame(pos - 8); - } else { - setValue(""); - setAnimating(false); - } - }); - }; - animateFrame(start); - }; - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter" && !animating) { - vanishAndSubmit(); - } - }; - - const vanishAndSubmit = () => { - setAnimating(true); - draw(); - - const value = inputRef.current?.value || ""; - if (value && inputRef.current) { - const maxX = newDataRef.current.reduce( - (prev, current) => (current.x > prev ? current.x : prev), - 0 - ); - animate(maxX); - } - }; - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - vanishAndSubmit(); - onSubmit && onSubmit(e); - }; - return ( -
- - { - if (!animating) { - setValue(e.target.value); - onChange && onChange(e); - } - }} - onKeyDown={handleKeyDown} - ref={inputRef} - value={value} - type="text" - className={cn( - "w-full relative text-sm sm:text-base z-50 border-none dark:text-white bg-transparent text-black h-full rounded-full focus:outline-none focus:ring-0 pl-4 sm:pl-10 pr-20", - animating && "text-transparent dark:text-transparent" - )} - /> - - - -
- - {!value && ( - - {placeholders[currentPlaceholder]} - - )} - -
- - ); -} diff --git a/docs/src/components/search-bar.tsx b/docs/src/components/search-bar.tsx deleted file mode 100644 index 2c943661..00000000 --- a/docs/src/components/search-bar.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import { useEffect, useState } from "react"; -import { - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalTrigger, -} from "./animated-modal"; -import { PlaceholdersAndVanishInput } from "./placeholders-and-vanish-input"; -import Link from "next/link"; - -/* Pagefind post-build types are not on NPM */ - -type PagefindSearchResult = { - id: string; - score: number; - words: number[]; - data: () => Promise; -}; - -type PagefindSearchFragment = { - url: string; - raw_url?: string; - content: string; - raw_content?: string; - excerpt: string; - sub_results: PagefindSubResult[]; - word_count: number; - locations: number[]; - weighted_locations: PagefindWordLocation[]; - filters: Record; - meta: Record; - anchors: PagefindSearchAnchor[]; -}; - -type PagefindSubResult = { - title: string; - url: string; - locations: number[]; - weighted_locations: PagefindWordLocation[]; - excerpt: string; - anchor?: PagefindSearchAnchor; -}; - -type PagefindWordLocation = { - weight: number; - balanced_score: number; - location: number; -}; - -type PagefindSearchAnchor = { - element: string; - id: string; - text?: string; - location: number; -}; - -interface WindowWithPagefind { - pagefind: { - search: (query: string) => Promise<{ results: PagefindSearchResult[] }>; - }; -} - -declare var window: Window & WindowWithPagefind; - -function Result({ result }: { result: PagefindSearchResult }) { - const [data, setData] = useState(null); - - useEffect(() => { - async function fetchData() { - const data = await result.data(); - setData(data); - } - fetchData(); - }, [result]); - - if (!data) return null; - - return ( - -
-

{data.meta.title}

-
- - ); -} - -export function SearchBar() { - const placeholders = [ - "Click to search...", - "What is the airspeed velocity of an unladen swallow?", - "Search the documentation...", - "My hovercraft is full of eels...", - ]; - - useEffect(() => { - async function loadPagefind() { - if (typeof window.pagefind === "undefined") { - try { - window.pagefind = await import( - // @ts-expect-error pagefind.js generated after build - /* webpackIgnore: true */ "./pagefind/pagefind.js" - ); - } catch (e) { - window.pagefind = { - search: async q => ({ results: [] }), - }; - } - } - } - loadPagefind(); - }, []); - - const [results, setResults] = useState([]); - - return ( - - -

- Search documentation... -

- - - -
- - -

- Search the documentation! -

-
- {results.map(result => ( - - ))} - -
- - - - -

Configuration

-
- - -
- - - - -

Configuration

-
- - -
- - - - -

Configuration

-
- -
-
- - { - const search = await window.pagefind.search( - e.currentTarget.value - ); - setResults(search.results); - }} - onSubmit={async e => { - const search = await window.pagefind.search( - ( - e.currentTarget - .elements[0] as HTMLInputElement - ).value - ); - setResults(search.results); - }} - /> - -
-
- ); -} diff --git a/docs/src/components/stars-background.tsx b/docs/src/components/stars-background.tsx deleted file mode 100644 index 400fa313..00000000 --- a/docs/src/components/stars-background.tsx +++ /dev/null @@ -1,133 +0,0 @@ -"use client"; -import { cn } from "@/utils/cn"; -import React, { - useState, - useEffect, - useRef, - RefObject, - useCallback, -} from "react"; - -interface StarProps { - x: number; - y: number; - radius: number; - opacity: number; - twinkleSpeed: number | null; -} - -export const Star: React.FC = ({ - x, - y, - radius, - opacity, - twinkleSpeed, -}) => ( - - {twinkleSpeed !== null && ( - - )} - -); - -interface StarsBackgroundProps { - starDensity?: number; - allStarsTwinkle?: boolean; - twinkleProbability?: number; - minTwinkleSpeed?: number; - maxTwinkleSpeed?: number; - className?: string; -} - -export const StarsBackground: React.FC = ({ - starDensity = 0.00015, - allStarsTwinkle = true, - twinkleProbability = 0.7, - minTwinkleSpeed = 0.5, - maxTwinkleSpeed = 1, - className, -}) => { - const [stars, setStars] = useState([]); - const containerRef: RefObject = - useRef(null); - - const generateStars = useCallback( - (width: number, height: number): StarProps[] => { - const area = width * height; - const numStars = Math.floor(area * starDensity); - return Array.from({ length: numStars }, () => { - const shouldTwinkle = - allStarsTwinkle || Math.random() < twinkleProbability; - return { - x: Math.random() * width, - y: Math.random() * height, - radius: Math.random() * 0.05 + 0.5, - opacity: Math.random() * 0.5 + 0.5, - twinkleSpeed: shouldTwinkle - ? minTwinkleSpeed + - Math.random() * (maxTwinkleSpeed - minTwinkleSpeed) - : null, - }; - }); - }, - [ - starDensity, - allStarsTwinkle, - twinkleProbability, - minTwinkleSpeed, - maxTwinkleSpeed, - ] - ); - - useEffect(() => { - const updateStars = () => { - if (containerRef.current) { - const { width, height } = - containerRef.current.getBoundingClientRect(); - setStars(generateStars(width, height)); - } - }; - - updateStars(); - - const resizeObserver = new ResizeObserver(updateStars); - if (containerRef.current) { - resizeObserver.observe(containerRef.current); - } - - return () => { - if (containerRef.current) { - resizeObserver.unobserve(containerRef.current); - } - }; - }, [ - starDensity, - allStarsTwinkle, - twinkleProbability, - minTwinkleSpeed, - maxTwinkleSpeed, - generateStars, - ]); - - return ( -
- - - {stars.map((star, index) => ( - - ))} - -
- ); -}; diff --git a/docs/src/pages/_app.tsx b/docs/src/pages/_app.tsx deleted file mode 100644 index ea073122..00000000 --- a/docs/src/pages/_app.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { GeistSans } from "geist/font/sans"; -import "./globals.css"; -import "highlight.js/styles/github-dark-dimmed.css"; -import type { AppProps } from "next/app"; -import hljs from "highlight.js"; -import python from "highlight.js/lib/languages/python"; -import bash from "highlight.js/lib/languages/bash"; -import { useEffect } from "react"; -import Image from "next/image"; -import { Spotlight } from "@/components/Spotlight"; -import Link from "next/link"; -import { SearchBar } from "@/components/search-bar"; - -export default function MyApp({ Component, pageProps }: AppProps) { - useEffect(() => { - hljs.registerLanguage("py", python); - hljs.registerLanguage("bash", bash); - hljs.highlightAll(); - }, [Component]); - return ( -
-
- -
- -
- -
-
- ); -} diff --git a/docs/src/pages/docs/building-projects/app-basics.mdx b/docs/src/pages/docs/building-projects/app-basics.mdx deleted file mode 100644 index 66431fb9..00000000 --- a/docs/src/pages/docs/building-projects/app-basics.mdx +++ /dev/null @@ -1,135 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# App Basics - -## New Applications - -Every view project will have a `new_app` call. The simplest app looks like this: - - -```py -from view import new_app - -app = new_app() -app.run() # You'll learn about the importance of this later -``` - - -`new_app` does a few important things: - -- Loads the configuration, regardless of whether a config file exists. -- Sets the `App` address for use by `get_app` (more on that later). -- Loads finalization code for when the app closes. - -While it's not required for every app, naming your app variable `app` is the proper convention for view, as that's the default variable searched for when using the `view serve` command, but more on that in a moment. - -For now, just try to stick with naming your app file `app.py` and your `view.App` instance `app`. - -## Launching Apps - -Python libraries generally have two ways to run a web server: - -- Running via the command line. -- Launching from Python itself (e.g. a `server.start(...)` function). - -Both have their benefits and downsides, so view.py supports both out of the box. `App` comes with its `run()` method, and the view CLI has the `view serve` command. - -Generally, you're going to want to add an `app.run()` to every view.py project, like so: - - -```py -from view import new_app - -app = new_app() -app.run() -``` - - -This way, if you (or someone else) want to run your code programmatically, they can run it via something like `python3 app.py`. It's also more semantically clear that an app is going to start when you run that file. - -If you prefer the CLI method, you can just run `view serve` and view.py will extract the app from the file itself, ignoring the `run()` call. - -Note that this behavior is a double-edged sword, so be careful. When calling with `run()`, the Python script will never get past that line because the server will run indefinitely, but when using `view serve` it proceeds past it just fine since all it's doing is extracting the `app`, skipping `run()`. For example, take a look at this code: - - -```py -from view import new_app - -app = new_app() -app.run() -print("You called the app with `view serve`!") # This only runs when `view serve` is used -``` - - -### Fancy Mode - -View comes with something called "fancy mode", which is a fancy UI that shows when you run the app. If you would like to disable this, you can do one of two things: - -- Disable the `fancy` setting in configuration. -- Pass `fancy=False` to `run()`. - -You should disable it in the configuration if you completely despise fancy mode and don't want to use it at all, but if you only want to temporarily turn it off (for example, if you're a view.py developer and need to see proper output) then pass `fancy=False`. - -## Getting the App - -### Circular Imports - -If you've worked with big Python projects before, there's a good chance you've run into a circular import error. A circular import error occurs when two modules try to import each other. A view.py example of this problem would most likely be the main app file trying to import a route, but then that route tries to import the app. - - - The example below uses routing, which if you're reading this for the first time you don't know how to use yet. Focus on the use of the `app` variable and not the routing itself. - - - -```py -from view import new_app -from routes import my_route - -app = new_app() -app.load([my_route]) -app.run() -``` - - - -```py -from view import get -from app import app - -@app.get("/something") -def something(): - return "something" - -@get("/") -def index(): - return "Hello, view.py" -``` - - -View gives you a solution to this problem: `get_app`. `get_app` uses some magic internally to get you your `App` instance right then and there, no import required. It works similar to how you would use `new_app`: - - -```py -from view import get_app - -app = get_app() - -@app.get("/") -def index(): - return "..." -``` - - -## Review - -Every view.py project should contain a call to `new_app`. `new_app` does important things like loading your configuration, set's up finalization code, and letting the `App` instance be used by `get_app`. - -Running an app can be done in two ways: programmatically via the `App.run` or through `view serve` command. However, every view.py app should contain an `App.run` to give the choice for running programmatically. By default, view.py has a fancy UI when running your app, which may be disabled via editing the config or passing `fancy=False` to `run()`. - -Finally, circular imports occur when two Python modules try to import each other, which can happen a lot in view when getting the app from the app file (especially in manual routing). To fix it, View provides a `get_app` function to get you your `App` instance without an import. diff --git a/docs/src/pages/docs/building-projects/builds.mdx b/docs/src/pages/docs/building-projects/builds.mdx deleted file mode 100644 index 719f6b7c..00000000 --- a/docs/src/pages/docs/building-projects/builds.mdx +++ /dev/null @@ -1,249 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Runtime Builds - -## Static Exports - -In some cases, you might want to export your application as [static HTML](https://en.wikipedia.org/wiki/Static_web_page). This makes it much easier to serve your app somewhere, at the limit of being able to perform actions server-side. You can export your app in view.py via the `view build` command, or by running the `build_app` function: - - -``` -$ view build -* Starting build process! -* Starting build steps -* Getting routes -* Calling GET /... -* Created ... -* Created index.html -* Successfully built app -``` - - -This will export your app into a static folder called `build`, which can then be served via something like [http.server](https://docs.python.org/3/library/http.server.html). An exported route cannot contain: - -- Route Inputs -- Path Parameters -- A method other than `GET` - -As stated above, you can also build your app programatically via `build_app`: - - -```py -from view import new_app -from view.build import build_app - -app = new_app() -app.load() # Call the loader manually, since we aren't calling run() - -build_app(app) -``` - - -## Build Steps - -Instead of exporting static HTML, you might just want to call some build script at runtime for your app to use. For example, this could be something like a [Next.js](https://nextjs.org) app, which you want to use as the UI for your website. Each different build is called a **build step** in View. - -View's build system does not aim to be a full fledged build system, but instead a bridge to use other package managers or tools to build requirements for your app. It tries to be _extendable_, instead of batteries-included. - -To specify a build step, add it under `build.steps` in your configuration. A build step should contain a list of requirements under `requires` and a `command`: - - -```toml -[build.steps.nextjs] -requires = ["npm"] -command = "npm run build" -``` - - -By default, this will only be run once the app is started. If you would like to run it every time a certain route is called, add the `steps` parameter to a router function. Note that this will make your route much slower (as a build process needs to be started for every request), so it's highly recommended that you [cache](https://view.zintensity.dev/building-projects/responses/#caching) the route. - -For example: - - -```py -from view import new_app - -app = new_app() - -@app.get("/", steps=["nextjs"], cache_rate=10000) # Reloads app every 10,000 requests -async def index(): - return await app.template("out/index.html") - -app.run() -``` - - - - Running build steps are expensive! When possible, try to make it a one-time cost by specify it in the default steps, or by setting the cache rate really high. - - -## Executing Build Scripts - -Instead of running a command, you can also run a Python script. To do this, simply specify a `script` value as a path to a file instead of a `command`: - - -```toml -[build.steps.foo] -requires = [] -script = "foo.py" -``` - - - - `__name__` is set to `__view_build__` when using a build script. If you want to use the file for other things, you can simply check `if __name__ == "__view_build__"`. - - -You can also specify a list of files or commands for both, to run multiple of either: - - -```toml -[build.steps.foo] -requires = ["gcc"] -script = ["foo.py", "bar.py"] -command = ["gcc -c -Wall -Werror -fpic foo.c", "gcc -shared -o libfoo.so foo.o"] -``` - - -If the script needs to run asynchronous code, export a `__view_build__` from the script: - - -```py -import aiofiles - -# This function will be run by the view.py build system -async def __view_build__(): - async with aiofiles.open("something.txt", "w") as f: - await f.write("...") -``` - - - -## Default Steps - -As said earlier, the default build steps are always run right before the app is started, and then never ran again (unless explicitly needed by a route). If you would like only certain steps to run, specify them with the `build.default_steps` value: - - -```toml -[build] -default_steps = ["nextjs"] -# Only NextJS will be built on startup - -[build.steps.nextjs] -requires = ["npm"] -command = "npm run build" - -[build.steps.php] -requires = ["php"] -command = "php -f payment.php" -``` - - -## Platform-Dependent Steps - -Many commands are different based on the platform used. For example, to read from a file on the Windows shell would be `type`, while on Linux and Mac it would be `cat`. If you add multiple step entries (in the form of an [array of tables](https://toml.io/en/v1.0.0-rc.2#array-of-tables)) with `platform` values, view.py will run the entry based on the platform the app was run on. - -For example, using the file reading example from above: - -Notice the double brackets next to `[[build.steps.read_from_file]]`, specifying an array of tables. - - -```toml -[[build.steps.read_from_file]] -platform = ["mac", "linux"] -command = "cat whatever.txt" - -[[build.steps.read_from_file]] -platform = "windows" -command = "type whatever.txt" -``` - - -The `platform` value can be one of three things per entry: - -- A list of platforms. -- A string containing a single platform. -- `None`, meaning to use this entry if no other platforms match. - -For example, with a `None` platform set (on multiple entries), the above could be rewritten as: - - -```toml -[[build.steps.read_from_file]] -# Windows ONLY runs this step -platform = "windows" -command = "type whatever.txt" - -[[build.steps.read_from_file]] -# All other platforms run this! -command = "cat whatever.txt" -``` - - -Note that only one step entry can have a `None` platform value, otherwise view.py will throw an error. - - - The only recognized operating systems for `platform` are the big three: Windows, Mac, and any Linux based system. If you want more fine-grained control (for example, using `pacman` or `apt` depending on the Linux distro), use a custom build script that knows how to read the Linux distribution. - - -## Build Requirements - -As you've seen above, build requirements are specified via the `requires` value. Out of the box, view.py supports a number of different build tools, compilers, and interpreters. To specify a requirement for one, simply add the name of their executable (_i.e._, how you access their CLI). For example, since `pip` is accessed via using the `pip` command in your terminal, `pip` is the name of the requirement. - -However, view.py might not support checking for a command by default (this is the case if you get a `Unknown build requirement` error). If so, you need a custom requirement. If you would like to, you can make an [issue](https://github.com/ZeroIntensity/view.py/issues) requesting support for it as well. - -### Custom Requirements - -There are four types of custom requirements, which are specified by adding a prefix to the requirement name: - -- Importing a Python module (`mod+`) -- Executing a Python script (`script+`) -- Checking if a path exists (`path+`) -- Checking if a command exists (`command+`) - -For example, the `command+gcc` would make sure that `gcc --version` return `0`: - - -```toml -[build.steps.c] -requires = ["command+gcc"] -command = "gcc *.c -o out" -``` - - -### The Requirement Protocol - -In a custom requirement specifying a module or script, view.py will attempt to call an asynchronous `__view_requirement__` function (similar to `__view_build__`). This function should return a `bool` value, with `True` indicating that the requirement exists, and `False` otherwise. - - - If no `__view_requirement__` function exists, then all view.py does it check that execution or import was successful, and marks the requirement as passing. - - -For example, if you were to write a requirement script that checks if the Python version is at least `3.10`, it could look like: - - -```py -import sys - -async def __view_requirement__() -> bool: - # Make sure we're running on at least Python 3.10 - return sys.version_info >= (3, 10) -``` - - -The above could actually be used via both `script+check_310.py` and `mod+check_310`. - - - Don't use the view.py build system to check the Python version or if a Python package is installed. Instead, use the `dependencies` section of a `pyproject.toml` file, or [PEP 723](https://peps.python.org/pep-0723/) script metadata. - - -## Review - -View can build static HTML with the `view build` command, or via `view.build.build_app`. Build steps in view.py are used to call external build systems, which can then in turn be used to build things your app needs at runtime (such as static HTML generated by [Next.js](https://nextjs.org)). Builds can run commands, Python scripts, or both. - -Each build step contains a list of build requirements. View provides several known requirements to specify out of the box, but you may also specify custom requirements, either via a Python script or module, checking a file path, or executing an arbitrary command. diff --git a/docs/src/pages/docs/building-projects/documenting.mdx b/docs/src/pages/docs/building-projects/documenting.mdx deleted file mode 100644 index 5c7b26ab..00000000 --- a/docs/src/pages/docs/building-projects/documenting.mdx +++ /dev/null @@ -1,134 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Documenting Applications - -## What is documenting? - -Writing documentation (or "documenting", as view.py calls it) can be an important task when it comes to writing API's, but it can be extremely tedious to do manually. Other frameworks, such as [FastAPI](https://fastapi.tiangolo.com), have their own approaches to generating API documentation, a common method is by using [OpenAPI](https://www.openapis.org/). - -OpenAPI is a good choice when it comes to this topic, but View does not support it. However, [support is planned](https://github.com/ZeroIntensity/view.py/issues/103). - -For now, View has it's own system internally that does not use OpenAPI. This means that **client generation is not yet supported.** If you would like to track this issue, see it [here](https://github.com/ZeroIntensity/view.py/issues/74). - -## Writing Documentation - -On a route, you may define a route's documentation in one of two ways: - -- Passing `doc` to the router function (e.g. `@get("/", doc="Homepage")`) -- More versatile, adding a docstring to the route (e.g. `"""Homepage"""`) - -Here's an example using both: - - -```py -from view import new_app - -app = new_app() - -@app.get("/", doc="The homepage") -async def index(): - ... - -@app.get("/hello") -async def hello(): - """A greeting to the user.""" - -app.docs("docs.md") # more on this function later -app.run() -``` - - -## Documenting Inputs - -For route inputs, it's almost idential, except that **you cannot** use a docstring, and instead must use the `doc` parameter. This syntax is the same across both `query` and `body` (including standard and direct). - - -```py -from view import new_app - -@app.get('/') -@app.query("greeting", str, doc="The greeting to be used by the server", default="hello") -async def index(greeting: str): - """The homepage that returns a greeting to the user.""" - return f"{greeting}, world!" -``` - - -However, you may want to define documentation for certain object keys when using object types (i.e. they support `__view_body__` or are handled internally). In this case, you can use `typing.Annotated` and a docstring again - -- The docstring defines a description for the overall class. -- `Annotated` can provide a description for a certain key. - - -```py -from view import new_app -from typing import Annotated, NamedTuple - -app = new_app() - -class Person(NamedTuple): - """A person in the world.""" - first: Annotated[str, "Their first name."] - last: Annotated[str, "Their last name."] - -@app.get("/") -@app.query("person", Person) -async def index(person: Person): - ... - -app.run() -``` - - -**Note:** If you are on Python 3.8, you will get an error complaining about `Annotated` not being a part of `typing`. In this case, you can import `Annotated` from `typing_extensions` instead. - -## Autogeneration - -View will generate your API documentation into a markdown document that you could render in something like [MkDocs](https://mkdocs.org). This can be done via `App.docs()`, which will generate the markdown and write it to a file for you. - -There are, roughly speaking, two ways to write to a file via `App.docs()`: - -- Passing it a `str` or `Path`. -- Passing it a `TextIO[str]` file wrapper. - - -```py -from view import new_app -from pathlib import Path - -app = new_app() - -app.docs("docs.md") -app.docs(Path("docs.md")) - -with open("docs.md", "w") as f: - app.docs(f) -``` - - -Alternatively, you can also use the `view docs` command to generate your documentation: - - -``` -$ view docs -- Created `docs.md` -``` - - -## Review - -"Documenting" in terms of View, is the act of writing documentation. Other frameworks use [OpenAPI](https://www.openapis.org/) as a versatile solution to doing this, but [view.py does not yet support this](https://github.com/ZeroIntensity/view.py/issues/103). - -To write a description for a route, you may pass a `doc` parameter to the router call, or instead add a docstring to the route function itself. In a route input, it's quite similar, where you pass `doc` to the input function, but **using a docstring is not allowed**. However, this rule is broken in the case of using an object as the type. When using an object, you must provide a docstring to define the class's description, and use `typing.Annotated` (or `typing_extensions.Annotated`) to set descriptions for object attributes. - -Finally, you can actually generate the markdown content via the `docs()` method on your `App`, or by the `view docs` command. `docs()` can take three types of parameters: - -- A `str`, in which it opens the file path and attempts to write to it. -- A `Path`, in which the same thing happens. -- A `TextIO[str]` (file wrapper), where the file is **not opened** by View, and is instead just written to via the wrapper. diff --git a/docs/src/pages/docs/building-projects/parameters.mdx b/docs/src/pages/docs/building-projects/parameters.mdx deleted file mode 100644 index 9afda6d6..00000000 --- a/docs/src/pages/docs/building-projects/parameters.mdx +++ /dev/null @@ -1,388 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Taking Parameters - -## Query Strings and Bodies - -If you're familiar with HTTP semantics, then you likely already know about query parameters and body parameters. If you have no idea what that is, that's okay. - -In HTTP, the query string is at the end of the URL and is prefixed with a `?`. For example, in a YouTube link (e.g. `youtube.com/watch?v=...`), the query string is `?v=...`. The query string contains key-value pairs in the form of strings. In Python, it's similiar to a `dict` that only contains strings. - -Bodies are a little bit more complicated. An HTTP body can be a number of different formats, but in a nutshell they are again, key-value pairs, but they can be a number of types. For now, JSON will be the main focus, which can have `str` keys, and any of the following as a value (in terms of Python types): - -- `str` -- `int` -- `bool` -- `dict[str, ]` - -The main similiarity here is that they are both key value pairs, which will make more sense in a moment. - -## Route Inputs - -In view.py, a route input is anything that feeds a parameter (or "input") to the route. This can be either a parameter received through the HTTP body, or something taken from the query string. View treats these two essentially the same on the user's end. Route inputs are similar to routes in the sense that there are standard and direct versions of the same thing: - -- `query` or `App.query` -- `body` or `App.body` - -There is little to no difference between the standard and direct variations, **including loading**. The direct versions are only to be used when the app is already available to **prevent extra imports**. - -## Defining Inputs - -For documentation purposes, only `query` variations will be used. However, **`body` works the exact same way**. A route input function (`query` in this case) takes one or more parameters: - -- The name of the parameter, should be a `str`. -- The type that it expects (optional). Note that this can be passed as many times as you want, and each type is just treated as a union. - -The below code would expect a parameter in the query string named `hello` of type `int`: - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -@app.query("hello", int) -async def index(hello: int): - print(hello) - return "hello" -``` - - -The `query()` call can actually come before `get` due to the nature of the routing system. In fact, anything you decorate a route with does not have a specific order needed. For example, the following is completely valid: - - -```py -from view import new_app - -app = new_app() - -@app.query("hello", int) # query comes before get() -@app.get("/") -async def index(hello: int): - ... - -app.run() -``` - - -### Automatically - -If you've used a library like [FastAPI](https://fastapi.tiangolo.com), then you're probably already familiar with the concept of automatic inputs. Automatic inputs in terms of view.py are when you define route inputs without using a `query` or `body` decorator, and instead, just get input definitions through the function signature. This is the most basic example: - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -async def index(hello: str): # no @query needed - return f"{hello}, world" - -app.run() -``` - - -Note that automatic inputs create inputs for **query parameters only**. - - -When mixing automatic route inputs with decorators (e.g. `query` and `body`), view.py assumes that decorator inputs have the same name as the parameter. For example, the following will **not** work: - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -@app.query("hello", str) -async def index(hello_param: str, test: str): - ... - -app.run() -``` - - - -## Cast Semantics - -In query strings, only a string can be sent, but these strings can represent other data types. This idea is called **casting**, and it's not at all specific to Python. If you're still confused, think of it as calling `int()` on the string `"1"` to convert it into an integer `1`. - -View has this exact same behavior when it comes to route inputs. If you tell your route to take an `int`, view.py will do all the necessary computing internally to make sure that you get an integer in your route. If a proper integer was not sent, then the server will automatically return an error `400` (Bad Request). There are a few things that should be noted for this behavior: - -- Every type can be casted to `str`. -- Every type can be casted to `Any`. -- `bool` expects `true` and `false` (instead of Python's `True` and `False`) to fit with JSON's types. -- `dict` expects valid JSON, **not** a valid Python dictionary. - -## Typing Inputs - -Typing route inputs is very simple if you're already familiar with Python's type annotation system. Again, unions can be formed via passing multiple types instead of one. However, direct union types provided by Python are supported too. This includes both `typing.Union` and the newer `|` syntax. - - -```py -from view import new_app -from typing import Union - -app = new_app() - -@app.get("/") -@app.query("name", str, int) -async def index(name: str | int): - ... - -@app.get("/hello") -@app.query("name", Union[str, int]) -async def hello(name: str | int): - ... - -@app.get("/world") -@app.query("name", str | int) -async def world(name: str | int): - ... - -app.run() -``` - - -The types supported are (all of which can be mixed and matched to your needs): - -- `str` -- `int` -- `bool` -- `list` (or `typing.List`) -- `dict` (or `typing.Dict`) -- `Any` (as in `typing.Any`) -- `None` -- `dataclasses.dataclass` -- `pydantic.BaseModel` -- Classes decorated with `attrs.define` -- `typing.NamedTuple` -- `typing.TypedDict` -- Any object supporting the `__view_body__` protocol. - -### Lists and Dictionaries - -You can use lists and dictionaries in a few ways, the most simple being just passing the raw type (`list` and `dict`). In typing terms, view.py will assume that these mean `dict[str, Any]` (as all JSON keys have to be strings) and `list[Any]`. If you would like to enforce a type, simply replace `Any` with an available type. The typing variations of these types (`typing.Dict` and `typing.List`) are supported as well. - - -```py -from view import new_app -from typing import Dict - -app = new_app() - -@app.get("/") -@app.query("name", Dict[str, str | int]) -async def index(name: Dict[str, str | int]): - ... - -@app.get("/hello") -@app.query("name", dict) -async def hello(name: dict): - ... - -app.run() -``` - - -Note that backport is **not possible** if you're using new typing features (such as the `dict[...]` or `list[...]`) as `from __future__ import annotations` does not affect parameters, meaning that the second value sent to the route input function (again, `query` or `body`) is not changed. - -## Using Objects - -As listed about earlier, view.py supports a few different objects to be used as types. All of these objects are meant for holding data to a specific model, which can be incredibly useful in developing web apps. Some things should be noted when using these types: - -- Any annotated value types must an available type already (i.e. `str | int` is supported, but `set | str` is not). Other objects are indeed supported. -- Respected modifiers are supported (such as `dataclasses.field` on `dataclass`). -- Methods are unrelated to the parsing, and may return any type and take any parameters. Methods are not accessible to the user (as JSON doesn't have methods). - -Here's an example using `dataclasses`: - - -```py -from view import new_app -from dataclasses import dataclass, field -from typing import List - -app = new_app() - -@dataclass -class Person: - first: str - last: str - favorite_foods: List[str] = field(default_factory=list) - -@app.get("/") -@app.query("me", Person) -async def index(me: Person): - return f"Hello, {me.first} {me.last}" -``` - - -If you would prefer to not use an object, View supports using a `TypedDict` to enforce parameters. It's subject to the same rules as normal objects, but is allowed to use `typing.NotRequired` to omit keys. Note that `TypedDict` **cannot** have default values. - - -```py -from view import new_app -from typing import TypedDict, NotRequired, List - -app = new_app() - -class Person(TypedDict): - first: str - last: str - favorite_foods: NotRequired[List[str]] - -@app.get("/") -@app.query("me", Person) -async def index(me: Person): - return f"Hello, {me['first']} {me['last']}" -``` - - -## Type Validation API - -You can use view.py's type validator on your own to do whatever you want. To create a validator for a type, use `compile_type`: - - -```py -from view import compile_type - -validator = compile_type(str | int) -``` - - - -The above code uses the `|` syntax, which is only available to Python 3.9+ - - -With a validator, you can do three things: - -- Cast an object to the type. -- Check if an object is compatible with the type. -- Check if an object is compatible, without the use of casting. - -`cast` will raise a `TypeValidationError` if the type is not compatible: - - -```py -from view import compile_type - -tp = compile_type(dict) -tp.cast("{}") -tp.cast("123") # TypeValidationError -``` - - -The difference between `check_type` and `is_compatible`, is that `check_type` is a [type guard](https://mypy.readthedocs.io/en/latest/type_narrowing.html), which `is_compatible` is not. - -This means that `check_type` will ensure that the object is _an instance_ of the type, while `is_compatible` checks whether it can be casted. For example: - - -```py -from view import compile_type - -tp = compile_type(dict) - -x: Any = {} -y: Any = {} # you could also use the string "{}" here - -if tp.check_type(x): - reveal_type(x) # dict - # to a type checker, x is now a dict - -if tp.is_compatible(y): - reveal_type(y) # Any - # a type checker doesn't know that y is a dict -``` - - -## Body Protocol - -If any of the above types do not support your needs, you may design your own type with the `__view_body__` protocol. On a type, `__view_body__` can be held in one of two things: - -- An attribute (e.g. `cls.__view_body__ = ...`) -- A property -- A static (or class) method. - -Whichever way you choose, the `__view_body__` data must be accessed statically, **not in an instance**. The data should be a dictionary (containing only `str` keys, once again), but the values should be types, not instances. These types outline how view.py should parse it at runtime. For example, a `__view_body__` to create an object that has a key called `a`, which a `str` value would look like so: - - -```py -class MyObject: - __view_body__ = {"a": str} -``` - - -View **does not** handle the initialization, so you must define a proper `__init__` for it. If you are already using the `__init__` for something else, you can define a `__view_construct__` class or static method and view.py will choose it over `__init__`. - - -```py -class MyObject: - __view_body__ = {"a": str} - - @classmethod - def __view_construct__(cls, **kwargs): - self = cls() - self.a: str = kwargs["a"] -``` - - -### Default Types and Unions - -`__view_body__` works the same as standard object types would work in the sense that types like `typing.Union` or the `|` syntax are supported, but you may also use a special value called `BodyParam`. `BodyParam` will allow you to pass union types in a tuple and set a default value. If you only want one type when using `BodyParam`, simply set `types` to a single value instead of a tuple. Here's an example of how it works, with the original object from above: - - -```py -class MyObject: - __view_body__ = { - "a": view.BodyParam(types=(str, int), default="hello"), - "b": view.BodyParam(types=str, default="world"), - } - - @classmethod - def __view_construct__(cls, **kwargs): - self = cls() - self.a: str | int = kwargs["a"] - self.a: str = kwargs["b"] -``` - - -## Client Semantics - -On the client side, sending data to view.py might be a bit unintuitive, depending on your background. For this part of the documentation, a JSON body will be used for simplicity. In the case of JSON, strings will be casted to a proper type if the route supports it. For example, if a route takes `a: str | int`, the following would be set to the integer `1`, not `"1"`. - - -```json -{ - "a": "1" -} -``` - - -Objects are simply formatted in JSON as well. If you had an object under the parameter name `test` and that object had the key `a: str`, it would be sent to the server like so: - - -```py -{ - "test": { - "a": "..." - } -} -``` - - -## Review - -View treats queries and bodies more or less equivalent, as they are both key value pairs. Strings can be casted to every other type assuming that it is in the proper format, and that's what makes it work. - -Any body or query parameter to a route is called a route input. There are standard and direct inputs (`body` and `query`, `App.body` and `App.query`), but they are not same in the way standard and direct routers work (direct inputs only exist to prevent extra imports). - -A route input function takes two parameters, the name (which is always a `str`), and the (optional) type(s). All the supported types are JSON types with the exception of some object structures (which are translated to a `dict`/JSON internally). `__view_body__` and `__view_construct__` can be used to implement special types that will be parsed by view. diff --git a/docs/src/pages/docs/building-projects/request-data.mdx b/docs/src/pages/docs/building-projects/request-data.mdx deleted file mode 100644 index 82babc68..00000000 --- a/docs/src/pages/docs/building-projects/request-data.mdx +++ /dev/null @@ -1,208 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Request Data - -## The Context - -If you've used a framework like [Django](https://djangoproject.com) or [FastAPI](https://fastapi.tiangolo.com), you've likely used a `request` parameter (or a `Request` type). View has something similiar, called `Context`. - -The `Context` instance contains information about the incoming request, including: - -- Headers. -- Cookies. -- HTTP version. -- Request method. -- URL path. -- Client and server IP address. - - -`Context` is an [extension type](https://docs.python.org/3/extending/newtypes_tutorial.html), and is defined in the `_view` module. It's Python signatures are defined in the `_view.pyi` type stub. - - -## Context Input - -The context can be added to a route via a route input, which is done through the `context` decorator. Note that `context` has a standard and direct variation (`App.context` is available to prevent imports). - -For example: - - -```py -from view import new_app, context, Context - -app = new_app() - -@app.get("/") -@context -async def index(ctx: Context): - print(ctx.headers["user-agent"]) - return "..." - -app.run() -``` - - -Since `context` is a route input, it can be used alongside other route inputs: - - -```py -from view import new_app, Context - -app = new_app() - -@app.get("/") -@app.query("greeting", str) -@app.context # direct variation -async def index(greeting: str, ctx: Context): - return f"{greeting}, {ctx.headers['place']}" - -app.run() -``` - - -### Automatic Input - -`Context` works well with the automatic inputs API (similar to how you would do it in [FastAPI](https://fastapi.tiangolo.com)), like so: - - -```py -from view import new_app, Context - -app = new_app() - -@app.get("/") -async def index(ctx: Context): # this is allowed - ... - -app.run() -``` - - -## Detecting Tests - -`Context` can also be used to detect whether the route is being used via `App.test`, through the `http_version` attribute. - - - - `App.test` is a more internal detail, but is available for public use. It looks like this: - - - ```py - from view import new_app - import asyncio - - app = new_app() - - @app.get("/") - async def index(): - return "hello, view.py" - - async def main(): - async with app.test() as test: - res = await test.get("/") - assert res.message == "hello, view.py" - - if __name__ == "__main__": - asyncio.run(main()) - ``` - - - -When a route is being used via `App.test`, `http_version` is set to `view_test`. For example: - - -```py -from view import new_app, Context -import asyncio - -app = new_app() - -@app.get("/") -@app.context -async def index(context: Context): - if context.http_version == "view_test": - return "this is a test!" - - return "hello, view.py" - -async def main(): - async with app.test() as test: - res = await test.get("/") - assert res.message == "this is a test!" - -if __name__ == "__main__": - asyncio.run(main()) -``` - - -## Cookies - -Technically speaking, cookies in HTTP are done via [headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie), but typically cookies in Python frameworks are done in a `dict` instead. View is no exception to this. - -Cookies can be viewed from the `cookies` attribute. However, since the `Context` is **not related** to the response, you must use `cookie` on a `Response` object to mutate a cookie. For example: - - -```py -from view import new_app, Context, Response - -app = new_app() - -@app.get("/") -async def index(ctx: Context): # automatic route input - count = int(ctx.cookies.get("count") or 0) - count += 1 - res = Response(f"you have been to this page {count} time(s)") - res.set_cookie("count", str(count)) - return res - -app.run() -``` - - -## Server and Client Address - -The `Context` also provides information about the servers binding address, as well as the address of the client. This information is provided in the form of an `ipaddress.IPv4Address` or `ipaddress.IPv6Address` (see the [ipaddress module](https://docs.python.org/3/library/ipaddress.html)), and then the port is in a seperate attribute. - -Both `Context.client` and `Context.client_port` may be `None`, but note that **if either of them is not `None`, the other one will be non `None`**. For example: - - -```py -from view import new_app, Context - -app = new_app() - -@app.get('/') -@app.context -async def index(ctx: Context): - if ctx.client: - port = ctx.client_port # this will always be an int in this case - ... - -app.run() -``` - - - -The above is **not** type safe. The type checker will still believe the `port` is `int | None`. - - -## Review - -`Context` is similiar to `Request` in other web frameworks, and is considered to be a route input in View, meaning you can add it to a route via the `context` decorator (or `App.context`, to prevent an extra import), or by the automatic route input system (i.e. adding a parameter annotated with type `Context`). - -`Context` contains nine attributes: - -- The `App` instance -- `headers`, of type `dict[str, str]`. -- `cookies`, of type `dict[str, str]`. -- `client`, of type `ipaddress.IPv4Address`, `ipaddress.IPv6Address`, or `None`. -- `server`, of type `ipaddress.IPv4Address`, `ipaddress.IPv6Address`, or `None`. -- `method`, of type `StrMethodASGI` (uppercase string containing the method, such as `"GET"`). -- `path`, of type `str`. -- `scheme`, which can be the string `"http"`, `"https"`. -- `http_version`, which can be the string `"1.0"`, `"1.1"`, `"2.0"`, `"view_test"`. diff --git a/docs/src/pages/docs/building-projects/responses.mdx b/docs/src/pages/docs/building-projects/responses.mdx deleted file mode 100644 index 3f66b62d..00000000 --- a/docs/src/pages/docs/building-projects/responses.mdx +++ /dev/null @@ -1,334 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Returning Responses - -## Basic Responses - -In any web framework, returning a response can be as simple as returning a string of text or quite complex with all sorts of things like server-side rendering. Right out of the box, View supports returning status codes, headers, and a response without any fancy tooling. A response **must** contain a body (this is a `str` or `bytes`), but may also contain a status (`int`) or headers (`dict[str, str]`). These may be in any order. - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -async def index(): - return "Hello, view.py", 201, {"x-my-header": "my_header"} - -app.run() -``` - - -## HTTP Errors - -Generally when returning a [client error](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses) or [server error](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#server_error_responses), you want to skip future execution. For example: - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -async def index(number: int): - if number == 1: - return "number cannot be one", 400 - - return f"your number is {number}" - -app.run() -``` - - -However, manually returning can be messy. For this, view.py provides you the `Error` class, which behaves like an `Exception`. It takes two parameters: - -- The status code, which is `400` by default. -- The message to send back to the user. If this is `None`, it uses the default error message (e.g. `Bad Request` for error `400`). - -Since `Error` works like a Python exception, you can `raise` it just fine: - - -```py -from view import new_app, Error - -app = new_app() - -@app.get("/") -async def index(number: int): - if number == 1: - raise Error(400) - - return f"your number is {number}" - -app.run() -``` - - - -`Error` can only be used to send back *error* responses. It can **not** be used to return status codes such as `200`. - - -## Caching - -Sometimes, computing the response for a route can be expensive or unnecessary. For this, view.py, along with many other web frameworks, provide the ability to cache responses. - -View lets you do this by using the `cache_rate` parameter on a router. - -For example: - - -```py -from view import new_app - -app = new_app() - -@app.get("/", cache_rate=10) # reload this route every 10 requests -async def index(): - return "..." - -app.run() -``` - - -You can see this in more detail by using a route that changes it's responses: - - -```py -from view import new_app - -app = new_app() -count = 1 - -@app.get("/", cache_rate=10) -async def index(): - global count - count += 1 - return str(count) - -app.run() -``` - - -In the above example, `index` is only called every 10 requests, so after 20 calls, `count` would be `2`. - -## Response Protocol - -If you have some sort of object that you want to wrap a response around, view.py gives you the `__view_result__` protocol. The only requirements are: - -- `__view_result__` is available on the returned object (doesn't matter if it's static or instance) -- `__view_result__` returns data that corresponds to the allowed return values. - -All types supporting `__view_result__` must take a `Context` parameter (see [this page](/docs/building-projects/request-data)). For now, ignore the context, and just focus on the overall `__view_result__`. - -With that in mind, a type `MyObject` defining `__view_result__` could look like: - - -```py -from view import new_app, Context - -app = new_app() - -class MyObject: - def __view_result__(self, ctx: Context): - return "Hello from MyObject!", {"x-www-myobject": "foo"} - -@app.get("/") -async def index(): - return MyObject() # this is ok - -app.run() -``` - - -## Response Objects - -Generally, you don't want to manually define a `__view_result__` method. Instead, you'll want to wrap the existing response implementation. - -To start, view.py comes with three built in response objects: - -- `Response` is simply a wrapper around other responses, but also acts as the base type for all response objects. -- `HTML` is for returning HTML content. -- `JSON` is for returning JSON content. - -A common use case for `Response`, apart from defining other response types, is wrapping an object that has a `__view_result__` and changing one of the values. For example: - - -```py -from view import new_app, Response - -app = new_app() - -class Test: - def __view_result__(self): - return "test", 201 - -@app.get("/") -async def index(): - return Response(Test(), status=200) # 200 is returned, not 201 - -app.run() -``` - - -Another common case for `Response` is using cookies. You can add a cookie to the response via the `cookie` method: - - -```py -@app.get("/") -async def index(): - res = Response(...) - res.cookie("hello", "world") - return res -``` - - -Note that **all built-in response classes inherit from `Response`**, meaning you can use this functionality anywhere. However, this is a convention as far as third party libraries go - it's not a requirement for a third party to inherit from `Response` (although, it's highly recommended that they do.) - - -A `Response` must be *returned* for things like `cookie` to take effect. For example: - - - -```py -from view import new_app, Response - -app = new_app() - -@app.get("/") -async def index(): - res = Response(...) - return "..." # res is not returned! - -app.run() -``` - - -### Body Translate Strategy - -The body translate strategy in the `__view_result__` protocol refers to how the `Response` class will translate the body into a `str`. There are four available strategies: - -- `str`, which uses the object's `__str__` method. -- `repr`, which uses the object's `__repr__` method. -- `result`, which calls the `__view_result__` protocol implemented on the object (assuming it exists). -- `custom`, uses the `Response` instance's `_custom` attribute (this only works on subclasses of `Response` that implement it). - -For example, the route below would return the string `"'hi'"`: - - -```py -from view import new_app, Response - -app = new_app() - -@app.get("/") -async def index(): - res = Response('hi', body_translate="repr") - return res - -app.run() -``` - - -### Implementing Responses - -`Response` is a [generic type](https://mypy.readthedocs.io/en/stable/generics.html), meaning you should supply it a type argument when writing a class that inherits from it. - -For example, if you wanted to write a type that takes a `str`: - - -```py -class MyResponse(Response[str]): - def __init__(self, body: str) -> None: - super().__init__(body) -``` - - -Generally, you'll want to use the `custom` translation strategy when writing custom `Response` objects. - -You must implement the `_custom` method (which takes in the `T` passed to `Response`, and returns a `str`) to use the `custom` strategy. For example, the code below would be for a `Response` type that formats a list: - - -```py -from view import Response - -class ListResponse(Response[list]): - def __init__(self, body: list) -> None: - super().__init__(body, body_translate="custom") - - def _custom(self, body: list) -> str: - return " ".join(body) -``` - - -## Middleware - -### The Middleware API - -`Route.middleware` is used to define a middleware function for a route. Like other web frameworks, middleware functions are given a `call_next`. Note that `call_next` is **always asynchronous**, regardless of whether the route is asynchronous. - - -```py -from view import new_app, CallNext - -app = new_app() - -@app.get("/") -def index(): - return "My response!" - -@index.middleware -async def index_middleware(call_next: CallNext): - print("This is called before index()!") - res = await call_next() - print("This is called after index()!") - return res - -app.run() -``` - - -### Response Parsing - -As shown above, `call_next` returns the result of the route. However, dealing with the raw response tuple might be a bit of a hassle. Instead, you can convert the response to a `Response` object using the `to_response` function: - - -```py -from view import new_app, CallNext, to_response -from time import perf_counter - -app = new_app() - -@app.get("/") -def index(): - return "my response!" - -@index.middleware -async def took_time_middleware(call_next: CallNext): - a = perf_counter() - res = to_response(await call_next()) - b = perf_counter() - res.headers["X-Time-Elapsed"] = str(b - a) - return res - -app.run() -``` - - -## Review - -Responses can be returned with a string, integer, and/or dictionary in any order. - -- The string represents the body of the response (e.g. HTML or JSON) -- The integer represents the status code (200 by default) -- The dictionary represents the headers (e.g. `{"x-www-my-header": "some value"}`) - -`Response` objects can also be returned, which implement the `__view_result__` protocol. All response classes inherit from `Response`, which supports operations like setting cookies. - -Finally, the `middleware` method on a `Route` can be used to implement middleware. diff --git a/docs/src/pages/docs/building-projects/routing.mdx b/docs/src/pages/docs/building-projects/routing.mdx deleted file mode 100644 index 2a9570b9..00000000 --- a/docs/src/pages/docs/building-projects/routing.mdx +++ /dev/null @@ -1,296 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Routing - -## Loaders - -Routing is a big part of any web library, and there are many ways to do it. View does it's best to support as many methods as possible to give you a well-rounded approach to routing. In view, your choice of routing is called the loader/loader strategy, and there are five of them: - -- `manual` -- `simple` -- `filesystem` -- `patterns` -- `custom` - -## Manually Routing - -If you're used to Python libraries like [Flask](https://flask.palletsprojects.com/en/3.0.x/) or [FastAPI](https://fastapi.tiangolo.com), then you're probably already familiar with manual routing. Manual routing is considered to be letting the user do all of the loading themself, and not do any automatic import or load mechanics. There are two ways to do manual routing, directly calling on your `App` being the most robust. Here's an example: - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -def index(): - return "Hello, view.py!" - -app.run() -``` - - -This type of function is called a **direct router**, and is what's recommended for small view.py projects. However, if you're more accustomed to JavaScript libraries, using the **standard routers** may be a good fit. When using manual routing, a standard router must be registered via a call to `App.load`. - - -view.py is aimed at people who love type hints, so what type should a route return? Well, all router functions will automatically tell the type checker what a route should return (specifically, [ViewResult](https://view.zintensity.dev/reference/types/#view.typing.ViewResult)), but if you *really* want to specify the return value, you have two options: - -- Use something route-specific, so something like `tuple[str, int]`, or just `str` -- More robust, using `ViewResult`, as mentioned above. - -The recommended way is to annotate the return value as `ViewResult`, but again, this is already known by the type checker: - - -```py -from view import new_app, ViewResult - -@app.get("/") -def index() -> ViewResult: - return "Hello, view.py!" - -app.run() -``` - - - -### Standard and Direct Routers - -Standard routers and direct routers have **the exact same** API (i.e. they are called the same way). The only difference is that direct routers automatically register a route onto the app, while standard routes do not. Direct routers tend to be used in small projects under manual loading, but standard routers are used in larger applications with one of the other loaders. - -Here are all the routers (standard on left, direct on right): - -- `view.get` and `App.get` -- `view.post` and `App.post` -- `view.put` and `App.put` -- `view.patch` and `App.patch` -- `view.delete` and `App.delete` -- `view.options` and `App.options` - - -```py -from view import new_app, get - -app = new_app() - -@get("/") -def index(): - return "Hello, view.py!" - -app.load([get]) -app.run() -``` - - -This method may be a bit more versatile if you plan on writing a larger project using manual routing, as you can import your routes from other files, but if that's the case it's recommended that you use one of the other loaders. - - -Use the direct variation if the `App` is already available, and use the standard version otherwise. It's discouraged to call `get_app()` and then use direct routing. - - -### Methodless Routing - -So far, only routers that allow a single method are allowed. If you're familiar with the [Flask](https://flask.palletsprojects.com) framework, you've likely tried the `route` method that lets any a route be accessed with any method. View supports the same thing, via the `route` router function, and the `App.route` direct variation. - -For example: - - -```py -from view import new_app, route - -app = new_app() - -@route("/") -async def index(): - return "This can be accessed with any method!" - -app.load([index]) -app.run() -``` - - -You can specify certain methods via the `methods` parameter: - - -```py -from view import new_app - -app = new_app() - -@app.route("/", methods=("GET", "POST")) # using the direct variation -async def index(): - return "This can be accessed with only GET and POST" - -app.run() -``` - - -## Simple Routing - -Simple routing is similar to manual routing, but you tend to not use direct routers and don't have any call to `load()`. In your routes directory (`routes/` by default, `loader_path` setting), your routes will be held in any number of files. Simple loading is recursive, so you may also use folders. View will automatically extract any route objects created in these files. - - -```py -from view import get - -@get("/foo") -def index(): - return "foo" - -@get("/bar") -def bar(): - return "bar" -``` - - -`/foo` and `/bar` will be loaded properly, no extra call to `App.load` is required. In fact, you don't even have to import these in your app file. **This is the recommended loader for larger view.py projects.** - -## URL Pattern Routing - -If you have ever used [Django](https://djangoproject.com), you already know how URL pattern routing works. Instead of defining your routes all over the place, all routes are defined and imported into one central file. Traditionally, this file is called `urls.py`, but you can play around with the name via the `loader_path` configuration option. - -Pattern loading looks like this in view.py: - - -```py -def my_route(hello: str): - return f"{hello}, world!" -``` - - - -```py -from view import path, query -from something import my_route - -patterns = ( - path("/", my_route, query("hello")), # this is a route input, you'll learn about this later - path("/another/thing", "/this/can/be/a/path/to/file.py") -) -``` - - -In the above example, we defined two routes via exporting a `tuple` of `Route` objects (generated by `path`). The name `patterns` was used as the variable name, but it may be any of the following: - -- `PATTERNS` -- `patterns` -- `URLPATTERNS` -- `URL_PATTERNS` -- `urlpatterns` -- `url_patterns` - - -Traditionally, Python constants are denoted via using the `SCREAMING_SNAKE_CASE` naming convention. -To follow Python convention, use `PATTERNS` or `URL_PATTERNS` when using the `patterns` loader. - - -## Filesystem Routing - -Finally, if you're familiar with JavaScript frameworks like [NextJS](https://nextjs.org), you're likely already familiar with filesystem routing. If that's the case, this may be the proper loader for you. The filesystem loader works by recursively searching your `loader_path` (again, `routes/` by default) and assigning each found file to a route. You do not have to pass an argument for the path when using filesystem routing. - -Filesystem routing comes with a few quirks. - -- There should only be one route per file. -- The upper directory structure is ignored, so `/home/user/app/routes/foo.py`, the assigned route would be `/foo`. -- If a file is named `index.py`, the route is not named `index`, but instead the parent (e.g. `foo/hello/index.py` would be assigned to `foo/hello`). -- If a file is prefixed with `_` (e.g. `_hello.py`), then it will be skipped entirely and not loaded. Files like this should be used for utilities and such. - -Here's an example of this in action: - - -```py -# This file is prefixed with _, so it will not be loaded as a route - -def do_something(): - ... -``` - - - -```py -# routes/index.py -from view import get -from _util import do_something - -@get() -def index(): - do_something() - return "Hello, view.py!" -``` - - -## Custom Routing - -The `custom` loader is, you guessed it, a user-defined loader. To start, decorate a function with `custom_loader`: - - -```py -from pathlib import Path -from typing import Iterable -from view import Route, new_app - -app = new_app() - -@app.custom_loader -def my_loader(app: App, path: Path) -> Iterable[Route]: - return [...] - -app.run() -``` - - -As shown above, there are two parameters to the `custom_loader` callback: - -- The `App` instance. -- The `Path` set by the `loader_path` config setting. - -The `custom_loader` callback is expected to return a list (or any iterable) of collected routes. - - -You might be confused about the `Route` constructor. That's because it's undocumented, and still technically a private API (meaning it can change at any time, for no reason). Don't try and instantiate a route yourself! Instead, let router functions do it (e.g. `get` or `query`), and collect the functions (or really, `Route` instances.) - - -For example, if you wanted to implement a loader that loaded routes from all files named `foo.py` from the `path`: - - -```py -from pathlib import Path -from typing import Iterable -from view import Route, new_app, get -import runpy - -app = new_app() - -@app.custom_loader -def my_loader(app: App, path: Path) -> Iterable[Route]: - routes: list[Route] = [] - - for i in path.iterdir(): - if str(i) != "foo.py": - continue - - globls = runpy.run_path(i.absolute()) - for name, value in globls.items(): - if isinstance(value, Route): - routes.append(name) - - return routes - -app.run() -``` - - -## Review - -In view, a loader is defined as the method of routing used. There are three loaders in view.py: `manual`, `simple`, and `filesystem`. - -- `manual` is good for small projects that are similar to Python libraries like [Flask](https://flask.palletsprojects.com/en/3.0.x/) or [FastAPI](https://fastapi.tiangolo.com). -- `simple` routing is the recommended loader for full-scale view.py applications. -- `filesystem` routing is similar to how JavaScript frameworks like [NextJS](https://nextjs.org) handle routing. -- `patterns` is similar to [Django](https://djangoproject.com/) routing. -- `custom` let's you decide - you can make your own loader and figure it out as you please. diff --git a/docs/src/pages/docs/building-projects/templating.mdx b/docs/src/pages/docs/building-projects/templating.mdx deleted file mode 100644 index c42ec28c..00000000 --- a/docs/src/pages/docs/building-projects/templating.mdx +++ /dev/null @@ -1,210 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# HTML Templating - -## What is templating? - -If you're building any sort of website, you likely don't want to write HTML from Python strings. Instead, you would rather just render HTML files and keep your Python code seperate. - -However, this has a drawback: **you can't put variables into your HTML.** Nearly all Python web frameworks use templating as a solution. - -Templating is the use of a template engine to put Python code in your HTML. For a more in-depth explanation, see the [Python Wiki](https://wiki.python.org/moin/Templating). - -## Templating API - -In View, the main template API entry point is the `template` function. Because this function performs I/O, it is asynchronous. - -The only required argument to `template` is the name or path of the template to be used. For example: - - -```py -from view import new_app, template - -app = new_app() - -@app.get("/") -async def index(): - return await template("index") # this refers to index.html - -@app.get("/other") -async def other(): - return await template("index.html") # works the same way - -app.run() -``` - - -The most notable difference about view.py's templating API is that parameters are automatically included from your scope (i.e. you don't have to pass them into the call to `template`). If you're against this behavior, you may disable it in the configuration via the `globals` and `locals` settings. - -You can override the template engine and settings via the `engine` and `directory` parameters. For example, if the engine was `view`, the below would use `mako`: - - -```py -from view import new_app, template - -app = new_app() - -@app.get('/') -async def index(): - return await template("index", engine="mako") - -app.run() -``` - - -There's also a direct variation of `template` on `App`. - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -async def index(): - return await app.template("index") - -app.run() -``` - - -The following template engines are supported: - -- View's built-in engine -- [Jinja](https://jinja.palletsprojects.com/en/3.1.x/) -- [Django Templates](https://docs.djangoproject.com/en/5.0/intro/tutorial03/) -- [Mako](https://www.makotemplates.org/) -- [Chameleon](https://chameleon.readthedocs.io/en/latest/) - -## The View Engine - -View has it's own built in template engine that is used by default. It's based around the usage of a `` tag, which is more limited, yet pretty to look at. - -A `` element can have any of the following attributes: - -- `ref`: Can be any Python expression (including variable references). -- `template`: Loads another template in place. -- `if`: Shows the element if the expression is truthy. -- `elif`: Shows the element if the expression is truthy and if the previous `if` or `elif` was falsy. -- `else`: Shows the element if all the previous `if` and `elif`'s were falsy. -- `iter`: May be any iterable expression. An `item` attribute must be present if this attribute is set. -- `item`: Specifies the name for the item in each iteration. Always present when `iter` is set. - -### Examples - -`ref` can be used to take variables, but may also be used to display any Python expression. For example, if you had defined `hello = "world"`: - - -```html -

Hello,

-

The length of hello is

-``` -
- -If you had declared `my_list = [1, 2, 3]`, you could iterate through it like so: - - -```html - - - -``` - - -The above would result in `123` - -`if`, `elif`, and `else` are only shown if their cases are met. So, for example: - - -```html - - - - - - - -

You must be an admin to use the admin panel!

-
-``` -
- -## Using Other Engines - -If you would like to use an unsupported engine (or use extra features of a supported engine), you can do one of two things: - -- Make a feature request on [GitHub](https://github.com/ZeroIntensity/view.py) requesting for support. -- Manually use it's API to return a response from a route. - -For example, if you wanted to customize [Jinja](https://jinja.palletsprojects.com/en/3.1.x/), you shouldn't use View's `template`, but instead just use it manually: - - -```py -from view import new_app -from jinja2 import Environment - -app = new_app() -env = Environment() - -@app.get('/') -async def index(): - return env.get_template("mytemplate.html").render() - -app.run() -``` - - -However, if you would like to access the engine instance (such as Jinja's `Environment`), you can get it from `app.templaters`, or set the value yourself. For example: - - -```py -from view import new_app -from jinja2 import Environment - -app = new_app() -env = Environment() -app.templaters["jinja"] = env - -@app.get('/') -async def index(): - return await app.template("index.html") - -app.run() -``` - - -## Markdown Rendering - -Many find writing raw HTML to be a hassle in many cases. For this, view.py provides the `markdown` function, which can turn markdown content into HTML, similar to how [MkDocs](https://mkdocs.org) does: - - -```py -from view import new_app, markdown - -app = new_app() - -@app.get("/blog") -async def index(): - return await markdown("blog.md") - -app.run() -``` - - -## Review - -Template engines are used to mix your Python code and HTML. You can use View's `template` or (`App.template`, if the `App` is available already) function to render a template with one of the supported engines, which are: - -- view.py's built-in engine -- [Jinja](https://jinja.palletsprojects.com/en/3.1.x/) -- [Django Templates](https://docs.djangoproject.com/en/5.0/intro/tutorial03/) -- [Mako](https://www.makotemplates.org/) -- [Chameleon](https://chameleon.readthedocs.io/en/latest/) - -If you would like to use an unsupported engine, you can make a feature request on [GitHub](https://github.com/ZeroIntensity/view.py/issues), or use it's API manually. diff --git a/docs/src/pages/docs/building-projects/websockets.mdx b/docs/src/pages/docs/building-projects/websockets.mdx deleted file mode 100644 index 42da9d5c..00000000 --- a/docs/src/pages/docs/building-projects/websockets.mdx +++ /dev/null @@ -1,200 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# WebSockets - - -In web development, a WebSocket is a two-way communication channel used on websites. Read more about them [here](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). - - -## WebSocket Routers - -Like other routers, the `websocket` router has both a standard and direct variation, with the same API. Unlike other routers, a WebSocket comes with one input out of the box, that being the actual WebSocket object. - - -You can add `query` inputs and path parameters to a `websocket` route, but not a `body` input. This isn't a limitation with view.py, but it's not supported by the protocol itself. - - -A WebSocket route does also not care what you return. In fact, a type checker expects that routes decorated with `websocket` return `None`. - -For example, a WebSocket that does nothing is as follows: - - -```py -from view import new_app, WebSocket - -app = new_app() - -@app.websocket("/") -async def websocket_route(ws: WebSocket): - ... - -app.run() -``` - - - -If you installed `uvicorn` manually, make sure to install `websockets` or `wsproto` if you plan on using WebSockets: - - -``` -$ pip install websockets -``` - - - -## Handshakes - -If you used the above code, it wouldn't actually work as a WebSocket from the client, since we don't accept the connection. - -Like other libraries, view.py does not automatically decide the lifecycle of your WebSocket handshake, meaning you have to manually `accept` and `close` it. For example, adding on to our above example that does nothing: - - -```py -from view import new_app, WebSocket - -app = new_app() - -@app.websocket("/") -async def websocket_route(ws: WebSocket): - await ws.accept() - await ws.close() - -app.run() -``` - - -Now, we could actually use a WebSocket client to access this route. However, you should use a context manager instead of manually calling lifecycle methods: - - -```py -from view import new_app, WebSocket - -app = new_app() - -@app.websocket("/") -async def websocket_route(ws: WebSocket): - async with ws: - ... - -app.run() -``` - - - -A `WebSocketHandshakeError` is raised if the client disconnects before the server calls `close`. - - -## Sending and Receiving - -Now, let's make our WebSocket do something! We can use `send` and `receive` to send and receive data. - -The best way to understand these methods is visually, so a simple chat application could look like: - - -```py -from view import new_app, WebSocket -import aiofiles # For asynchronous input() - -app = new_app() - -@app.websocket("/") -async def websocket_route(ws: WebSocket): - await ws.accept() # We shouldn't ever need to exit, so no need for a context manager - while True: - await ws.send(await aiofiles.stdin.readline()) - print("Them:", await ws.receive()) - -app.run() -``` - - -### Receiving Types - -Using view.py's type-casting system, you can specify a type to receive from the client by passing `tp` to `receive`. The supported types are: - -- `str` -- `int` -- `bool` -- `bytes` -- `dict` - -For example, if you wanted to receive JSON data: - - -```py -from view import new_app, WebSocket - -app = new_app() - -@app.websocket("/") -async def websocket_route(ws: WebSocket): - async with ws: - json = await ws.receive(tp=dict) - -app.run() -``` - - -## Message Pairs - -In many cases, such as with our chat app from above, we want a 1:1 ratio of messages from the server to the client. view.py gives you the `pair` method, to remove some boilerplate. It simply sends a message, then returns a received message. For example, with our chat app from above: - - -```py -from view import new_app, WebSocket -import aiofiles - -app = new_app() - -@app.websocket("/") -async def websocket_route(ws: WebSocket): - await ws.accept() - while True: - print("Them:", await ws.pair(await aiofiles.stdin.readline())) - -app.run() -``` - - -As stated above, `pair` sends the message before receiving, but you can reverse this by passing `recv_first=True`: - - -```py -print("Them:", await ws.pair(await aiofiles.stdin.readline(), recv_first=True)) -``` - - -This would receive from the client, _then_ send a message, and then return that received message. - -## Expecting Messages - -In some cases, you might just want the client to send some data to ensure compliance with a protocol, or perhaps for a [ping-pong](https://en.wikipedia.org/wiki/Ping-pong_scheme). You can use the `expect` method, which ensures that the client send some data, and then discards the message. For example: - - -```py -from view import new_app, WebSocket - -app = new_app() - -@app.websocket("/") -async def websocket_route(ws: WebSocket): - async with ws: - await ws.expect("MYPROTOCOL V1.1") - await ws.send("ACK") - # ... - -app.run() -``` - - -## Review - -WebSocket routes always have at least one route input, that being a `WebSocket` object representing the connection. view.py does not handle the connection lifetime, so calling `accept`, `close`, or using the context manager is up to the user. - -Data can be sent and received via `send` and `receive` (who would have guessed!), and certain types can be expected from the client via the `tp` parameter. You can also use the `pair` method to eliminate some boilerplate when it comes to 1:1 message correspondence, as well as use the `expect` method to expect that the client sends some data. diff --git a/docs/src/pages/docs/getting-started/configuration.mdx b/docs/src/pages/docs/getting-started/configuration.mdx deleted file mode 100644 index 58f33edd..00000000 --- a/docs/src/pages/docs/getting-started/configuration.mdx +++ /dev/null @@ -1,183 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Configuration - -## Introduction - -Before you can make any projects with view.py, you should learn about how it handles configuration. Configuration is handled by the [configzen](https://github.com/bswck/configzen) library under the hood, so most questions about configuration will be answered there. - -## The Config File - -When creating your app, view will search for one of the following configuration files: - -- `view.toml` -- `view.json` -- `view.ini` -- `view_config.py` - -Note that while all of these are different formats, they can all evaluate to the same thing internally. If you have any questions on these semantics, once again see [configzen](https://github.com/bswck/configzen). - -## Programatically - -Many Python users aren't fond of the configuration file strategy, and that's okay. View supports editing the config at runtime just fine through the `config` property. The `config` property stores a `Config` object, which holds more subcategories. - - -```py -from view import new_app - -app = new_app() -app.config.foo.bar = "..." -``` - - -Configurations are loaded at runtime by the `load_config` function. If you would like to use View's configuration file without creating an `App`, you may use it like so: - - -```py -from view import load_config - -config = load_config() -``` - - - - While it's possible to use `load_config` directly, it's not at all recommended. Always prefer `new_app` over trying to manually instantiate the `App` object. - - -## Settings - -View has several different configuration settings. For documentation purposes, values will be talked about in terms of Python (i.e. `null` values will be regarded as `None`). - -At the top level, there's one real setting: `dev`. - -`dev` is `True` by default, and is what tells view.py whether you're running in a production server setting or just running on your local machine. - -### Environment Variables - -If you would like to set a configuration setting via an [environment variable](https://en.wikipedia.org/wiki/Environment_variable), you must account for the setting's environment prefix. - -All environment prefixes look like `view__`. For example, the `loader` setting is under the `app` section, so to set `loader` you would use the following command: - - -```bash -$ export view_app_loader=filesystem -``` - - -Environment variables can also be set via the `env` config setting, or by adding a `.env` file to the project: - - -```toml -[env] -TEST = "hello" -``` - - -You can access environment variables via the `view.env` utility: - - -```py -from view import env - -test = env("TEST", tp=int) -# test will be an integer. if environment variable "TEST" does not exist, an exception is thrown. -# if environment variable "TEST" is not an integer, an exception is thrown. -``` - - -## App Settings - -**Environment Prefix: `view_app_`** - -| Key | Description | Default | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------ | -| `loader` | This is the strategy that will be used to load routes. Can be `manual`, `simple`, or `filesystem`. | `manual` | -| `app_path` | A string defining the location of the app, as well as the variable name. Should be in the format of `file_path:variable_name`. | `app.py:app` | -| `uvloop` | Whether or not to use `uvloop` as a means of event loop. Can be `decide` or a `bool` value. | `decide` | -| `loader_path` | When the loader is `simple` or `filesystem`, this is the path that it searches for routes. | `routes/` | - -Example with TOML: - -```toml -[app] -loader = "filesystem" -loader_path = "./app" -``` - -## Server Settings - -**Environment Prefix:** `view_server_` - -| Key | Description | Default | -| ------------ | -------------------------------------------------------------------------------------------------------------------- | --------- | -| `host` | IPv4 address specifying what address to bind the server to. `0.0.0.0` by default. | `0.0.0.0` | -| `port` | Integer defining what port to bind the server to. | `5000` | -| `backend` | ASGI backend to use. Can be `uvicorn`, `daphne`, or `hypercorn`. | `uvicorn` | -| `extra_args` | Dictionary containing extra parameters for the ASGI backend. This parameter is specific to the backend and not View. | `{}` | - -Example with TOML: - -```toml -[server] -host = "localhost" -port = 8080 -``` - -## Log Settings - -**Environment Prefix:** `view_log_` - -| Key | Description | Default | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -| `level` | Log level. May be `debug`, `info`, `warning`, `error`, `critical`, or an `int`. This is based on Python's built-in [logging module](https://docs.python.org/3/library/logging.html). | `info` | -| `server_logger` | This is a `bool` determining whether the ASGI backend's logger should be displayed. | `False` | -| `fancy` | Whether to use View's fancy output mode. | `True` | -| `pretty_tracebacks` | Whether to use [Rich Exceptions](https://rich.readthedocs.io/en/stable/logging.html?highlight=exceptions#handle-exceptions). | `True` | -| `startup_message` | Whether to show the view.py welcome message on server startup. | `True` | - -## User Logging Settings - -_Environment Prefix:_ `view_user_log_` - -- `urgency`: The log level for user logging. `info` by default. -- `log_file`: The target file for outputting log messages. `None` by default. -- `show_time`: Whether to show the time in each message. `True` by default. -- `show_caller`: Whether to show the caller function in each message. `True` by default. -- `show_color`: Whether to enable colorization for messages. `True` by default. -- `show_urgency`: Whether to show the urgency for messages. `True` by default. -- `file_write`: The preference for writing to an output file, if set. May be `both`, to write to both the terminal and the output file, `only`, to write to just the output file, or `never`, to not write anything. -- `strftime`: The time format used if `show_time` is set to `True`. `%H:%M:%S` by default. - -Example with TOML: - -```toml -[log] -level = "warning" -fancy = false - -[log.user] -log_file = "app.log" -``` - -## Template Settings - -_Environment Prefix:_ `view_templates_` - -- `directory`: The path to search for templates. `./templates` by default. -- `locals`: Whether to include local variables in the rendering parameters (i.e. local variables can be used inside templates). `True` by default -- `globals`: The same as `locals`, but for global variables instead. `True` by default. -- `engine`: The default template engine to use for rendering. Can be `view`, `jinja`, `django`, `mako`, or `chameleon`. `view` by default. - -Example with TOML: - -```toml -[templates] -directory = "./pages" -engine = "jinja" -``` diff --git a/docs/src/pages/docs/getting-started/creating-a-project.mdx b/docs/src/pages/docs/getting-started/creating-a-project.mdx deleted file mode 100644 index 93b29b48..00000000 --- a/docs/src/pages/docs/getting-started/creating-a-project.mdx +++ /dev/null @@ -1,93 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Creating a Project - -## Automatic - -The View CLI supports automatically creating a project via the `view init` command. - - -```bash -$ view init -``` - - -However, you probably don't want to create an entire new virtual environment (assuming you're using one, at least) just for running `view init`, since it will generate a second environment for your project. The easiest solution is to use `pipx`: - - -```bash -$ pipx run view-py init -``` - - - -This is out of view.py's control - pipx does not support `.` in a project name, so we have to use `view-py` instead of `view.py`. - - -## Manually - -view.py doesn't actually need any big project structure. In fact, you can run an app in just a single Python file, but larger structures like this might be more convenient for big projects. The only real requirement for something to be a view app is that it calls `new_app`, but again, more on that later. - -Some "hello world" code for manually starting a view project would look like this: - - -```py -from view import new_app - -app = new_app() - -@app.get("/") -def index(): - return "..." - -app.run() -``` - - -## Structure - -First, in any view project, you need a file to contain your app. By default, view expects it to be in `app.py` under a variable called `app`. Again, you can change this via the `app_path` setting. You're also going to want an `app.run()` (assuming you named your `App` instance `app`), but more on that later. - - -```py -from view import new_app - -app = new_app() -app.run() -``` - - -Generally, you're going to want one of the configuration files talked about earlier, but if you're against configuration files that's OK, view.py will work just fine without it. If you choose to use something other than manual routing, you want a `routes` directory (unless you changed the `loader_path` setting). - - -```toml -dev = true - -[app] -loader_path = "./my_custom_loader_path" -``` - - -For mobility purposes, you may want to add a `pyproject.toml` that contains the dependencies for your project, in case you need to run your project on a different system. - - -```toml -[build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" - -[project] -name = "your_view_app" -requires-python = ">=3.8" -authors = [ - { name = "Your Name", email = "your@email.com" }, -] -dependencies = ["view.py"] -``` - \ No newline at end of file diff --git a/docs/src/pages/docs/getting-started/installation.mdx b/docs/src/pages/docs/getting-started/installation.mdx deleted file mode 100644 index 827a44ea..00000000 --- a/docs/src/pages/docs/getting-started/installation.mdx +++ /dev/null @@ -1,72 +0,0 @@ -import MdxLayout from "../../../components/mdx-layout"; -import Codeblock from "../../../components/codeblock"; -import Admonition from "../../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Installation - -## System Requirements - -view.py requires [CPython](https://python.org/downloads/) 3.8 or above. - - - CPython is the reference/official implementation of Python. If you downloaded Python through [python.org](https://python.org) or some sort of system package manager (e.g. `apt`, `pacman`, `brew`), it's probably CPython. - - -view.py does **not** officially support PyPy, nor any other implementation of Python. If you're looking for speed, rely on view.py's C extension to do the heavy lifting! - -## Installing with Pipx (Recommended) - -[pipx](https://pipx.pypa.io/stable/) can install CLIs into isolated environments. view.py recommends using `pipx` for installation, and then using `view init` to initialize a virtual environment in projects. For example: - - -```bash -$ pipx install view.py -... pipx output -$ view init -``` - - -## Installing via Pip - - -```bash -$ pip install view.py -``` - - - -## Development Version - - -```bash -$ pip install git+https://github.com/ZeroIntensity/view.py -``` - - -## Finalizing - -To ensure you've installed view.py correctly, run the `view` command: - - -```bash -$ view -``` - - - - On Linux, `view` is already a command! Read about it [here](https://www.ibm.com/docs/zh/aix/7.2?topic=v-view-command), but in short, it opens `vi` in read only mode. You can either shadow this command with view.py's CLI, or use the `view-admin` command instead, which is an alias. This documentation will assume you use `view` instead of `view-admin`, but note that they do the exact same thing. - - -If this doesn't work properly, try executing via Python: - - -```bash -$ python3 -m view -# OR, view-admin, as shown above -$ python3 -m view-admin -``` - diff --git a/docs/src/pages/docs/index.mdx b/docs/src/pages/docs/index.mdx deleted file mode 100644 index 0dd62233..00000000 --- a/docs/src/pages/docs/index.mdx +++ /dev/null @@ -1,44 +0,0 @@ -import MdxLayout from "../../components/mdx-layout"; -import Codeblock from "../../components/codeblock"; -import Admonition from "../../components/admonition" - -export default function MDXPage({ children }) { - return {children}; -} - -# Introduction - -Welcome to view.py's documentation! - -- [Source](https://github.com/ZeroIntensity/view.py) -- [PyPI](https://pypi.org/project/view.py) -- [Discord](https://discord.gg/tZAfuWAbm2) - -Here, you'll learn all about view.py and how to use it. - -## Quickstart - - -``` -$ pipx run view-py init -# OR -$ python3 -m pip install view.py -$ python3 -m view init -``` - - -## Batteries Detachability - -So, what does "batteries-detachable" actually mean? It means that we give you everything out of the box, but don't make you use it - instead, we actively support other libraries. **This is what makes view.py unique among other frameworks.** - -For example, with our approach to [template engines](/docs/building-projects/templating). We've developed our own flavor of template engine, but include native support for lots of others, including Jinja and Django's internal template engine (which we used some magic to access). - -Basically, if you don't want to relearn anything, you don't really have to; you can just keep using what you have been, straight inside of view.py - -## This Documentation - -Throughout this documentation, it's assumed that you've read the previous pages, like a book. If you're really in a hurry, you can read the "review" section at the bottom of each page. - -## Support - -You can directly get support from the developers in the [view.py discord server](https://discord.gg/tZAfuWAbm2). Hope to see you there! \ No newline at end of file diff --git a/docs/src/pages/favicon.ico b/docs/src/pages/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 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 LrJugUA?W`A8`#=m diff --git a/docs/src/pages/globals.css b/docs/src/pages/globals.css deleted file mode 100644 index 56fb03e9..00000000 --- a/docs/src/pages/globals.css +++ /dev/null @@ -1,19 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - color-scheme: dark; -} - -body { - @apply bg-black text-white; -} - -a { - @apply text-zinc-500 hover:text-white transition-all; -} - -h1 { - @apply text-4xl; -} diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx deleted file mode 100644 index 85f37a0b..00000000 --- a/docs/src/pages/index.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { BackgroundBeams } from "@/components/background-beams"; -import React from "react"; -import { cn } from "@/utils/cn"; -import { AuroraBackground } from "@/components/aurora-background"; -import { motion } from "framer-motion"; -import { StarsBackground } from "@/components/stars-background"; -import { Meteors } from "@/components/meteors"; -import FooterContent from "@/components/footer-content"; - -export const Highlight = ({ - children, - className, -}: { - children: React.ReactNode; - className?: string; -}) => { - return ( - - {children} - - ); -}; - -function Card({ - title, - children, - icon, -}: { - title: string; - children: React.ReactNode; - icon: React.ReactNode; -}) { - return ( -
-
-
- - -
- {icon} -

- {title} -

-
- -

- {children} -

- - {/* Meaty part - Meteor effect */} - -
-
- ); -} - -export default function Home() { - const [copied, setCopied] = React.useState(false); - - return ( - <> -
-
-
-

- Alpha -

-

- view.py is currently in alpha! -

-
-

- The Batteries-Detachable Web - Framework -

- - -
- - -
-
- - -
-

- What's view.py? -

-

- Not your ordinary web framework -

-
-
- - - - } - > - We provide everything you need, right out of the - box, while including native support your - favorite third-party libraries. Don't want - to relearn anything? No problem. - - - - - } - > - Powered by our own{" "} - - PyAwaitable - - , view.py is the world's first web - framework to implement ASGI in pure C, without - the use of transpilers. - - - - - } - > - view.py is written by developers, for - developers, under the MIT license. - -
-
-
- -
-
-
- - ); -} diff --git a/docs/src/utils/cn.ts b/docs/src/utils/cn.ts deleted file mode 100644 index d83ac213..00000000 --- a/docs/src/utils/cn.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} diff --git a/docs/tailwind.config.ts b/docs/tailwind.config.ts deleted file mode 100644 index 6ad77b6e..00000000 --- a/docs/tailwind.config.ts +++ /dev/null @@ -1,151 +0,0 @@ -import type { Config } from "tailwindcss"; -const defaultTheme = require("tailwindcss/defaultTheme"); -const svgToDataUri = require("mini-svg-data-uri"); - -const colors = require("tailwindcss/colors"); -const { - default: flattenColorPalette, -} = require("tailwindcss/lib/util/flattenColorPalette"); - -const config: Config = { - content: [ - "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", - "./src/components/**/*.{js,ts,jsx,tsx,mdx}", - "./src/app/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: { - backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - "gradient-conic": - "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", - }, - animation: { - spotlight: "spotlight 2s ease .75s 1 forwards", - shimmer: "shimmer 2s linear infinite", - first: "moveVertical 30s ease infinite", - second: "moveInCircle 20s reverse infinite", - third: "moveInCircle 40s linear infinite", - fourth: "moveHorizontal 40s ease infinite", - fifth: "moveInCircle 20s ease infinite", - aurora: "aurora 60s linear infinite", - "meteor-effect": "meteor 5s linear infinite", - }, - - keyframes: { - spotlight: { - "0%": { - opacity: "0", - transform: "translate(-72%, -62%) scale(0.5)", - }, - "100%": { - opacity: "1", - transform: "translate(-50%,-40%) scale(1)", - }, - }, - shimmer: { - from: { - backgroundPosition: "0 0", - }, - to: { - backgroundPosition: "-200% 0", - }, - }, - moveHorizontal: { - "0%": { - transform: "translateX(-50%) translateY(-10%)", - }, - "50%": { - transform: "translateX(50%) translateY(10%)", - }, - "100%": { - transform: "translateX(-50%) translateY(-10%)", - }, - }, - moveInCircle: { - "0%": { - transform: "rotate(0deg)", - }, - "50%": { - transform: "rotate(180deg)", - }, - "100%": { - transform: "rotate(360deg)", - }, - }, - moveVertical: { - "0%": { - transform: "translateY(-50%)", - }, - "50%": { - transform: "translateY(50%)", - }, - "100%": { - transform: "translateY(-50%)", - }, - }, - aurora: { - from: { - backgroundPosition: "50% 50%, 50% 50%", - }, - to: { - backgroundPosition: "350% 50%, 350% 50%", - }, - }, - meteor: { - "0%": { - transform: "rotate(215deg) translateX(0)", - opacity: "1", - }, - "70%": { opacity: "1" }, - "100%": { - transform: "rotate(215deg) translateX(-500px)", - opacity: "0", - }, - }, - }, - }, - }, - darkMode: "class", - plugins: [ - addVariablesForColors, - function ({ matchUtilities, theme }: any) { - matchUtilities( - { - "bg-grid": (value: any) => ({ - backgroundImage: `url("${svgToDataUri( - `` - )}")`, - }), - "bg-grid-small": (value: any) => ({ - backgroundImage: `url("${svgToDataUri( - `` - )}")`, - }), - "bg-dot": (value: any) => ({ - backgroundImage: `url("${svgToDataUri( - `` - )}")`, - }), - }, - { - values: flattenColorPalette(theme("backgroundColor")), - type: "color", - } - ); - }, - ], -}; - -function addVariablesForColors({ addBase, theme }: any) { - let allColors = flattenColorPalette(theme("colors")); - let newVars = Object.fromEntries( - Object.entries(allColors).map(([key, val]) => [`--${key}`, val]) - ); - - addBase({ - ":root": newVars, - }); -} - -export default config; diff --git a/docs/tsconfig.json b/docs/tsconfig.json deleted file mode 100644 index 7b285893..00000000 --- a/docs/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/docs_md/reference/app.md b/docs_md/reference/app.md deleted file mode 100644 index 3a616d2a..00000000 --- a/docs_md/reference/app.md +++ /dev/null @@ -1,3 +0,0 @@ -# App Reference - -::: view.app diff --git a/docs_md/reference/build.md b/docs_md/reference/build.md deleted file mode 100644 index 527d3e68..00000000 --- a/docs_md/reference/build.md +++ /dev/null @@ -1,3 +0,0 @@ -# Build Reference - -::: view.build diff --git a/docs_md/reference/config.md b/docs_md/reference/config.md deleted file mode 100644 index 3769d154..00000000 --- a/docs_md/reference/config.md +++ /dev/null @@ -1,3 +0,0 @@ -# Configuration Reference - -::: view.config diff --git a/docs_md/reference/exceptions.md b/docs_md/reference/exceptions.md deleted file mode 100644 index bbf90b80..00000000 --- a/docs_md/reference/exceptions.md +++ /dev/null @@ -1,3 +0,0 @@ -# Exceptions Reference - -::: view.exceptions diff --git a/docs_md/reference/responses.md b/docs_md/reference/responses.md deleted file mode 100644 index d4970701..00000000 --- a/docs_md/reference/responses.md +++ /dev/null @@ -1,3 +0,0 @@ -# Responses Reference - -::: view.response diff --git a/docs_md/reference/routing.md b/docs_md/reference/routing.md deleted file mode 100644 index 245ee8c1..00000000 --- a/docs_md/reference/routing.md +++ /dev/null @@ -1,8 +0,0 @@ -# Routing Reference - -::: view.routing - - -::: view._loader - -::: view.patterns diff --git a/docs_md/reference/templates.md b/docs_md/reference/templates.md deleted file mode 100644 index 209591cc..00000000 --- a/docs_md/reference/templates.md +++ /dev/null @@ -1,3 +0,0 @@ -# Templating Reference - -::: view.templates diff --git a/docs_md/reference/types.md b/docs_md/reference/types.md deleted file mode 100644 index 64e8d587..00000000 --- a/docs_md/reference/types.md +++ /dev/null @@ -1,3 +0,0 @@ -# Types Reference - -::: view.typing diff --git a/docs_md/reference/utils.md b/docs_md/reference/utils.md deleted file mode 100644 index b802b630..00000000 --- a/docs_md/reference/utils.md +++ /dev/null @@ -1,4 +0,0 @@ -# Utilities Reference - -::: view.util -::: view.typecodes diff --git a/docs_md/reference/websockets.md b/docs_md/reference/websockets.md deleted file mode 100644 index 4ebe02e6..00000000 --- a/docs_md/reference/websockets.md +++ /dev/null @@ -1,3 +0,0 @@ -# WebSockets Reference - -::: view.ws