create-playcanvas 0.0.2-beta.2 → 0.0.2-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -41,7 +41,7 @@ ${C.gray(y)} ${e}
41
41
  yarn`,h+=`
42
42
  yarn dev`;break;default:h+=`
43
43
  ${F} install`,h+=`
44
- ${F} run dev`;break}au(h)}const{blue:Bu,cyan:mu}=C,ED=[{name:"react",display:"@playcanvas/react",description:"A @playcanvas/react project",color:mu,variants:[{name:"react-ts",display:"TypeScript",color:Bu}]}],gu=ED.flatMap(e=>e.variants.map(u=>u.name));async function pu({argTargetDir:e,defaultTargetDir:u,cancel:t}){let r=e?z(String(e)):void 0;if(!r){const i=await CD({message:"Project name:",defaultValue:u,placeholder:u});j(i)&&t(),r=z(i)}return r}async function fu({targetDir:e,argOverwrite:u,cancel:t}){if(m.existsSync(e)&&!Cu(e)){const r=u?"yes":await V({message:(e==="."?"Current directory":`Target directory "${e}"`)+" is not empty. Please choose how to proceed:",options:[{label:"Cancel operation",value:"no"},{label:"Remove existing files and continue",value:"yes"},{label:"Ignore files and continue",value:"ignore"}]});if(j(r))return t();switch(r){case"yes":ou(e);break;case"no":t();return}}}function lD(e){return/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e)}function cD(e){return e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-")}async function bu({targetDir:e,cancel:u}){let t=b.basename(b.resolve(e));if(!lD(t)){const r=await CD({message:"Package name:",defaultValue:cD(t),placeholder:cD(t),validate(i){if(!lD(i))return"Invalid package.json name"}});if(j(r))return u();t=r}return t}async function du({argTemplate:e,FRAMEWORKS:u,TEMPLATES:t,pkgInfo:r,getFullCustomCommand:i,cancel:s}){let D=e,n=!1;if(e&&!t.includes(e)&&(D=void 0,n=!0),!D){const F=await V({message:n?`"${e}" isn't a valid template. Please choose from below: `:"Select a framework:",options:u.map(a=>{const o=a.color;return{label:o(a.display||a.name),value:a,hint:a.description}})});if(j(F))return s();await V({message:"Select a variant:",options:F.variants.map(a=>{const o=a.color,E=a.customCommand?i(a.customCommand,r).replace(/ TARGET_DIR$/,""):void 0;return{label:o(a.display||a.name),value:a.name,hint:E}})}),D=F.variants[0].name}return D}const w=dD(process.argv.slice(2),{alias:{h:"help",t:"template",d:"delete"},boolean:["help","overwrite"],string:["template"]}),Au=process.cwd(),vu=`Usage: npm create playcanvas@latest [OPTION]...
44
+ ${F} run dev`;break}au(h)}const{blue:Bu,cyan:mu}=C,ED=[{name:"react",display:"React",description:"with @playcanvas/react",color:mu,variants:[{name:"react-ts",display:"TypeScript",color:Bu}]}],gu=ED.flatMap(e=>e.variants.map(u=>u.name));async function pu({argTargetDir:e,defaultTargetDir:u,cancel:t}){let r=e?z(String(e)):void 0;if(!r){const i=await CD({message:"Project name:",defaultValue:u,placeholder:u});j(i)&&t(),r=z(i)}return r}async function fu({targetDir:e,argOverwrite:u,cancel:t}){if(m.existsSync(e)&&!Cu(e)){const r=u?"yes":await V({message:(e==="."?"Current directory":`Target directory "${e}"`)+" is not empty. Please choose how to proceed:",options:[{label:"Cancel operation",value:"no"},{label:"Remove existing files and continue",value:"yes"},{label:"Ignore files and continue",value:"ignore"}]});if(j(r))return t();switch(r){case"yes":ou(e);break;case"no":t();return}}}function lD(e){return/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e)}function cD(e){return e.trim().toLowerCase().replace(/\s+/g,"-").replace(/^[._]/,"").replace(/[^a-z\d\-~]+/g,"-")}async function bu({targetDir:e,cancel:u}){let t=b.basename(b.resolve(e));if(!lD(t)){const r=await CD({message:"Package name:",defaultValue:cD(t),placeholder:cD(t),validate(i){if(!lD(i))return"Invalid package.json name"}});if(j(r))return u();t=r}return t}async function du({argTemplate:e,FRAMEWORKS:u,TEMPLATES:t,pkgInfo:r,getFullCustomCommand:i,cancel:s}){let D=e,n=!1;if(e&&!t.includes(e)&&(D=void 0,n=!0),!D){const F=await V({message:n?`"${e}" isn't a valid template. Please choose from below: `:"Select a framework:",options:u.map(a=>{const o=a.color;return{label:o(a.display||a.name),value:a,hint:a.description}})});if(j(F))return s();await V({message:"Select a variant:",options:F.variants.map(a=>{const o=a.color,E=a.customCommand?i(a.customCommand,r).replace(/ TARGET_DIR$/,""):void 0;return{label:o(a.display||a.name),value:a.name,hint:E}})}),D=F.variants[0].name}return D}const w=dD(process.argv.slice(2),{alias:{h:"help",t:"template",d:"delete"},boolean:["help","overwrite"],string:["template"]}),Au=process.cwd(),vu=`Usage: npm create playcanvas@latest [OPTION]...
45
45
 
46
46
  Create a new PlayCanvas project in JavaScript or TypeScript.
47
47
  With no arguments, start the CLI in interactive mode.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-playcanvas",
3
3
  "author": "Mark Lundin",
4
- "version": "0.0.2-beta.2",
4
+ "version": "0.0.2-beta.4",
5
5
  "description": "Scaffold a PlayCanvas project",
6
6
  "type": "module",
7
7
  "bin": {
@@ -0,0 +1,47 @@
1
+ import colors, { yellow } from 'picocolors'
2
+ const { blue, cyan } = colors
3
+
4
+ /**
5
+ * You can add new frameworks here.
6
+ *
7
+ * The framework name is used to create the directory name.
8
+ * The framework display name is used to display the framework name in the CLI.
9
+ * The framework description is used to display the framework description in the CLI.
10
+ * The framework color is used to display the framework color in the CLI.
11
+ * The framework variants are used to display the framework variants in the CLI.
12
+ *
13
+ */
14
+ export const FRAMEWORKS: Framework[] = [
15
+ {
16
+ name: 'react',
17
+ display: 'React',
18
+ description: 'with @playcanvas/react',
19
+ color: cyan,
20
+ variants: [
21
+ {
22
+ name: 'react-ts',
23
+ display: 'TypeScript',
24
+ color: blue,
25
+ },
26
+ ],
27
+ }
28
+ ]
29
+
30
+ export const TEMPLATES = FRAMEWORKS.flatMap((f) => f.variants.map((v) => v.name))
31
+
32
+ export type ColorFunc = (str: string | number) => string
33
+
34
+ export type FrameworkVariant = {
35
+ name: string
36
+ display: string
37
+ color: ColorFunc
38
+ customCommand?: string
39
+ }
40
+
41
+ export type Framework = {
42
+ name: string
43
+ display: string
44
+ color: ColorFunc
45
+ description?: string
46
+ variants: FrameworkVariant[]
47
+ }
@@ -0,0 +1,82 @@
1
+ # @playcanvas/react + TypeScript Starter
2
+
3
+ A modern, batteries-included starter for building PlayCanvas experiences with React and TypeScript, HMR and ESLint.
4
+
5
+ ## Prerequisites
6
+
7
+ * **Node.js ≥ 20.19.0** (or ≥ 22.12.0). Use [nvm](https://github.com/nvm-sh/nvm) to install/switch:
8
+
9
+ ```bash
10
+ nvm install 22 # or 20.19+
11
+ nvm use 22
12
+ ```
13
+
14
+ ---
15
+
16
+ ## Getting Started
17
+
18
+ Scaffold and install dependencies:
19
+
20
+ ```bash
21
+ npm create playcanvas@latest -t react-ts
22
+ cd playcanvas-project
23
+ npm install
24
+ ```
25
+
26
+ Start the dev server with hot-module reload:
27
+
28
+ ```bash
29
+ npm run dev
30
+ ```
31
+
32
+ Open <http://localhost:5173> to view the app. Any file changes trigger instant reloads.
33
+
34
+ ---
35
+
36
+ ## Scripts
37
+
38
+ | Command | Description |
39
+ |--------------------|--------------------------------------|
40
+ | `npm run dev` | Start Vite dev server with HMR |
41
+ | `npm run build` | Type-check & bundle for production |
42
+ | `npm run preview` | Preview the production build locally |
43
+ | `npm run lint` | Run ESLint over the codebase |
44
+
45
+ ---
46
+
47
+ ## Project Structure
48
+
49
+ ```
50
+ .
51
+ ├── public/ # Static assets copied as-is
52
+ ├── src/
53
+ │ ├── Scene.tsx # React wrapper around a PlayCanvas application
54
+ │ ├── App.tsx # Entry component
55
+ │ └── main.tsx # Vite/React bootstrap
56
+ ├── vite.config.ts # Vite + React plugin config
57
+ └── ...
58
+ ```
59
+
60
+ ## Linting & Formatting
61
+
62
+ ESLint is pre-configured with TypeScript, React hooks, React Refresh, and global browser/node globals. Adjust `eslint.config.js` as your codebase grows.
63
+
64
+ ---
65
+
66
+ ## Deployment
67
+
68
+ Run `npm run build` to generate a static production bundle in `dist/`. Deploy the contents of that folder to any static hosting provider (Netlify, Vercel, GitHub Pages, Cloudflare Pages, etc.).
69
+
70
+ ---
71
+
72
+ ## Further Reading
73
+
74
+ * [PlayCanvas Engine Docs](https://developer.playcanvas.com)
75
+ * [PlayCanvas React Docs](https://playcanvas-react.vercel.app)
76
+ * [React Docs](https://react.dev/)
77
+ * [Vite Docs](https://vitejs.dev/)
78
+ * [TypeScript Handbook](https://www.typescriptlang.org/docs/)
79
+
80
+ ---
81
+
82
+ Made with ❤️. PRs and issues welcome!
@@ -0,0 +1,24 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
@@ -0,0 +1,23 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+ import { globalIgnores } from 'eslint/config'
7
+
8
+ export default tseslint.config([
9
+ globalIgnores(['dist']),
10
+ {
11
+ files: ['**/*.{ts,tsx}'],
12
+ extends: [
13
+ js.configs.recommended,
14
+ tseslint.configs.recommended,
15
+ reactHooks.configs['recommended-latest'],
16
+ reactRefresh.configs.vite,
17
+ ],
18
+ languageOptions: {
19
+ ecmaVersion: 2020,
20
+ globals: globals.browser,
21
+ },
22
+ },
23
+ ])
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>PlayCanvas + React + TS</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "playcanvas-project",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": "^20.19.0 || >=22.12.0"
8
+ },
9
+ "scripts": {
10
+ "predev": "node ./scripts/ensure-node-version.cjs",
11
+ "dev": "vite",
12
+ "build": "tsc -b && vite build",
13
+ "lint": "eslint .",
14
+ "preview": "vite preview"
15
+ },
16
+ "dependencies": {
17
+ "@playcanvas/react": "^0.5.0",
18
+ "playcanvas": "^2.8.2",
19
+ "react": "^19.1.0",
20
+ "react-dom": "^19.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@eslint/js": "^9.29.0",
24
+ "@types/react": "^19.1.8",
25
+ "@types/react-dom": "^19.1.6",
26
+ "@vitejs/plugin-react": "^4.5.2",
27
+ "eslint": "^9.29.0",
28
+ "eslint-plugin-react-hooks": "^5.2.0",
29
+ "eslint-plugin-react-refresh": "^0.4.20",
30
+ "globals": "^16.2.0",
31
+ "semver": "^7.7.2",
32
+ "typescript": "~5.8.3",
33
+ "typescript-eslint": "^8.34.1",
34
+ "vite": "^7.0.0"
35
+ }
36
+ }
@@ -0,0 +1,12 @@
1
+ const semver = require('semver');
2
+ const pkg = require('../package.json');
3
+
4
+ const requiredVersion = pkg.engines.node;
5
+ const currentVersion = process.version;
6
+
7
+ if (!semver.satisfies(currentVersion, requiredVersion)) {
8
+ console.error(`\n❌ Unsupported Node.js version: ${currentVersion}`);
9
+ console.error(`Required: ${requiredVersion}`);
10
+ console.error('Please upgrade your Node.js to continue.\n');
11
+ process.exit(1);
12
+ }
@@ -0,0 +1,83 @@
1
+ #root {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100vh;
5
+ width: 100vw;
6
+ }
7
+
8
+ header {
9
+ display: flex;
10
+ flex-direction: row;
11
+ align-items: center;
12
+ padding: 2rem;
13
+ }
14
+
15
+ .absolute {
16
+ position: absolute;
17
+ top: 0;
18
+ left: 0;
19
+ }
20
+
21
+ .full-bleed {
22
+ width: 100vw;
23
+ height: 100vh;
24
+ overflow: hidden;
25
+ }
26
+
27
+ .logo {
28
+ height: 2em;
29
+ padding: .5em;
30
+ will-change: filter;
31
+ transition: filter 300ms;
32
+ }
33
+
34
+ .playcanvas-logo {
35
+ border-radius: 100%;
36
+ }
37
+
38
+ .logo:hover {
39
+ filter: drop-shadow(0 0 2em #646cffaa);
40
+ }
41
+
42
+ .logo.react:hover {
43
+ filter: drop-shadow(0 0 2em #61dafbaa);
44
+ }
45
+
46
+ .card {
47
+ padding: 2em;
48
+ }
49
+
50
+ .read-the-docs {
51
+ color: #888;
52
+ }
53
+
54
+ .container{
55
+ position: absolute;
56
+ top: 0;
57
+ left: 0;
58
+ display: flex;
59
+ }
60
+
61
+ .overlay {
62
+ margin: 0 auto;
63
+ text-align: center;
64
+ display: flex;
65
+ flex-direction: column;
66
+ align-items: center;
67
+ justify-content: stretch;
68
+ width: 100vw;
69
+ height: 100vh;
70
+ pointer-events: none;
71
+ }
72
+
73
+ .overlay > .grow {
74
+ flex-grow: 1;
75
+ }
76
+
77
+ .pill {
78
+ background-color: #000;
79
+ color: #fff;
80
+ padding: 0.5rem 1rem;
81
+ border-radius: 0.5rem;
82
+ }
83
+
@@ -0,0 +1,46 @@
1
+ import { Application } from "@playcanvas/react";
2
+ import { useState } from 'react'
3
+ import reactLogo from './assets/react.svg'
4
+ import playcanvasLogo from './assets/playcanvas.png'
5
+ import './App.css'
6
+ import Scene from "./Scene";
7
+
8
+ function App() {
9
+ const [count, setCount] = useState(0)
10
+
11
+ return (
12
+ <>
13
+ <div className="full-bleed">
14
+ <Application className="playcanvas-app">
15
+ <Scene onClick={() => setCount((count) => count + 1)}/>
16
+ </Application>
17
+ </div>
18
+ <div className="absolute overlay">
19
+ <div className="grow">
20
+ <header>
21
+ <h1>PlayCanvas + React</h1>
22
+ <a href="https://playcanvas-react.vercel.app/docs" target="_blank">
23
+ <img src={playcanvasLogo} className="playcanvas-logo logo" alt="PlayCanvas logo" />
24
+ </a>
25
+ <a href="https://react.dev" target="_blank">
26
+ <img src={reactLogo} className="logo react" alt="React logo" />
27
+ </a>
28
+ </header>
29
+ </div>
30
+ <div>
31
+ <span className="pill">
32
+ count is {count}
33
+ </span>
34
+ <p>
35
+ Edit <code>src/Scene.tsx</code> and save to test HMR
36
+ </p>
37
+ </div>
38
+ <p className="read-the-docs">
39
+ Click on the PlayCanvas and React logos to learn more
40
+ </p>
41
+ </div>
42
+ </>
43
+ )
44
+ }
45
+
46
+ export default App
@@ -0,0 +1,70 @@
1
+ // @ts-expect-error - ShadowCatcher is not typed
2
+ import { Grid } from "playcanvas/scripts/esm/grid.mjs";
3
+ import { useEffect, useState } from "react";
4
+ import { Entity } from "@playcanvas/react";
5
+ import { useEnvAtlas, useMaterial } from "@playcanvas/react/hooks";
6
+ import { Camera, EnvAtlas, Render, Script } from "@playcanvas/react/components";
7
+ import { OrbitControls } from "@playcanvas/react/scripts";
8
+
9
+ /**
10
+ * The Scene renders a sphere with a grid and camera controls
11
+ */
12
+ function Scene({ onClick }: SceneProps) {
13
+
14
+ // Track the hover state and set the color based on the hover state
15
+ const [hovering, setHovering] = useState(false);
16
+
17
+ // Set a material color based on the hover state
18
+ const diffuse = hovering ? '#ffffff' : '#dddddd';
19
+
20
+ // Create a material for the sphere
21
+ const material = useMaterial({ diffuse });
22
+
23
+ // Load the environment map
24
+ const { asset: envMap } = useEnvAtlas('/environment-map.png');
25
+
26
+ // change the mouse cursor based on the hover state
27
+ useEffect(() => {
28
+ document.body.style.cursor = hovering ? 'pointer' : 'default';
29
+ }, [hovering]);
30
+
31
+ // Don't render until the environment map is loaded
32
+ if (!envMap) return null;
33
+
34
+ return (
35
+ <>
36
+ {/* Render some environment lighting using the environment map */}
37
+ <EnvAtlas asset={envMap} showSkybox={false} />
38
+
39
+ {/* Render a background grid */}
40
+ <Entity scale={[1000, 1000, 1000]}>
41
+ <Script script={Grid} />
42
+ </Entity>
43
+
44
+ {/* Create a camera entity with orbit controls */}
45
+ <Entity name='camera' position={[4, 1, 4]}>
46
+ <Camera clearColor='#171717' />
47
+ <OrbitControls />
48
+ </Entity>
49
+
50
+ {/* Create and position entity with pointer events */}
51
+ <Entity
52
+ position={[0, 2, 0]}
53
+ scale={[4, 4, 4]}
54
+ onClick={onClick}
55
+ onPointerOver={() => setHovering(true)}
56
+ onPointerOut={() => setHovering(false)}
57
+ >
58
+
59
+ {/* Render a sphere with the material */}
60
+ <Render type="sphere" material={material} />
61
+ </Entity>
62
+ </>
63
+ )
64
+ }
65
+
66
+ type SceneProps = {
67
+ onClick: () => void;
68
+ }
69
+
70
+ export default Scene;
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1,73 @@
1
+ :root {
2
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ a {
17
+ font-weight: 500;
18
+ color: #646cff;
19
+ text-decoration: inherit;
20
+ }
21
+ a:hover {
22
+ color: #535bf2;
23
+ }
24
+
25
+ a > img {
26
+ vertical-align: middle;
27
+ }
28
+
29
+ body {
30
+ margin: 0;
31
+ display: flex;
32
+ place-items: center;
33
+ min-width: 320px;
34
+ min-height: 100vh;
35
+ }
36
+
37
+ h1 {
38
+ font-size: 2.4em;
39
+ line-height: 1.1;
40
+ opacity: 0.85;
41
+ }
42
+
43
+ button {
44
+ border-radius: 8px;
45
+ border: 1px solid transparent;
46
+ padding: 0.6em 1.2em;
47
+ font-size: 1em;
48
+ font-weight: 500;
49
+ font-family: inherit;
50
+ background-color: #1a1a1a;
51
+ cursor: pointer;
52
+ transition: border-color 0.25s;
53
+ }
54
+ button:hover {
55
+ border-color: #646cff;
56
+ }
57
+ button:focus,
58
+ button:focus-visible {
59
+ outline: 4px auto -webkit-focus-ring-color;
60
+ }
61
+
62
+ @media (prefers-color-scheme: light) {
63
+ :root {
64
+ color: #213547;
65
+ background-color: #ffffff;
66
+ }
67
+ a:hover {
68
+ color: #747bff;
69
+ }
70
+ button {
71
+ background-color: #f9f9f9;
72
+ }
73
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.tsx'
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ )
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2022",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+ "jsx": "react-jsx",
17
+
18
+ /* Linting */
19
+ "strict": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "erasableSyntaxOnly": true,
23
+ "noFallthroughCasesInSwitch": true,
24
+ "noUncheckedSideEffectImports": true
25
+ },
26
+ "include": ["src"]
27
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "verbatimModuleSyntax": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+
16
+ /* Linting */
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "erasableSyntaxOnly": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedSideEffectImports": true
23
+ },
24
+ "include": ["vite.config.ts"]
25
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ // https://vite.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ })
File without changes